Vue+Element UI 树形控件整合下拉功能菜单(tree + dropdown +input)

这篇博客主要介绍树形控件的两个小小的功能:

  • 下拉菜单
  • 输入过滤框

以CSS样式为主,也会涉及到Vue组件和element组件的使用。

对于没有层级的数据,我们可以使用表格或卡片来展示。要展示或建立层级关系,就一定会用到树形组件了。
使用Vue + Element UI,构建出最基本的树如下图所示:


最简单的树结构

现在我们就要在这个基础上进行改造,使页面更加符合我们的交互场景。

一、下拉菜单

将下拉菜单嵌到树节点中,使操作更加简便、紧凑。

效果演示

效果如图:

  • 图示1:悬浮在树节点状态


    悬浮在树节点状态
  • 图示2:点击三个点图标状态


    点击三个点图标状态
  • 图示3: 选中并选择菜单


    下拉菜单效果

如上,当鼠标悬浮在树节点上时,出现竖着的三个小点,点击时弹出下拉菜单,显示可以对树节点进行的操作。

实现步骤

1、使用插槽(slot) + 子组件

  • 父组件(含有树形控件)模板代码
 <el-tree :data="resourceTree" :ref="tree" node-key="id"  size="small"
                :highlight-current="true" :check-on-click-node="true" >
                <span class="custom-tree-node" slot-scope="{ node, data }">
                    <div class="custom-tree-node-wrapper">
                        <span class="custom-tree-node-label">
                            {{ node.label }}
                        </span>
                        <span class="operate-btns">                
                            <dot-dropdown  :events="dropMenuEvents" :data="{node,data}" @addNode="addNode" />
                        </span>
                    </div>
                </span>
            </el-tree>

2、 DotDropdown 下拉框代码

很多树形结构都会使用该下拉框,所以定义组件,方便复用。

<template>
    <el-dropdown trigger="click" class="custom-tree-menu" size="small">
        <i class="el-icon-more rotate " />
        <el-dropdown-menu slot="dropdown">
            <el-dropdown-item v-for='(item,index) in events' :key="index" :divided="index >0" @click.native="clickMenu(item)">
                {{item.label}}
            </el-dropdown-item>
        </el-dropdown-menu>
    </el-dropdown>
</template>
<script>
export default {
  props: {
    events: {
      type: Array,
      default: function() {
        return [
          {
            label: '新建同级',
            funcName: 'addNode'
          },
          {
            label: '编辑',
            funcName: 'editNode'
          },
          {
            label: '删除',
            funcName: 'deleteNode'
          }
        ]
      }
    },
    // 注入数据
    data: {
      type: Object
    }
  },
  methods: {
    clickMenu(item) {
      this.$emit(item.funcName, this.data)
    }
  }
}

模板代码很简单,是一个点击触发的下拉菜单组件(trigger="click"),菜单循环props中传入的events属性,data为从父组件拿到的数据,定义了菜单和菜单的事件(方法名称),当点击菜单(@click.native)时,触发:

this.$emit(item.funcName, this.data)

容易看出,数据和实现方法都是父组件的,该子组件只做了转发。

3、 父组件使用子组件

引入和注册子组件,并定义好对应的方法即可。下面给出使用示例:

<span class="operate-btns">
          <dot-dropdown v-if="data.type === 1" :events="dropMenuEvents" :data="{node,data}"/>
          <dot-dropdown v-if="data.type === 2" :events="sysDropMenuEvents" :data="{node,data}" @addNode="addResource" />
 </span>

根据数据节点的类型,注入不同的events属性,显示不同的下拉框菜单。(常用场景:根节点不可删除、不可编辑,只能新增子级,叶子节点可以新增同级和子级)。
在父组件中的data中定义:

sysDropMenuEvents: [{ label: '新增资源', funcName: 'addNode' }],

dropMenuEvents: [
      { label: '新建同级', funcName: 'addPeerNode' },
      { label: '新建子级', funcName: 'addNode' },
      { label: '分配操作', funcName: 'distributeAction' },
      { label: '编辑', funcName: 'editNode' },
      { label: '删除', funcName: 'removeNode' }
 ]

父组件编写实际功能方法:

// 打开新增资源弹窗
    addResource({ node, data }) {
      ...
    }

父组件注入data时,将树节点插槽中的node和data都注入了进去(:data="{node,data}"),在使用时也可以用过同样的大括号+属性名的方式拿到对应的属性,这里体现了ES6解构赋值的特性。

4、父组件样式

父组件中,树节点的样式:

 .el-tree-node__content {
      position: relative;
      .operate-btns {
          position: absolute;
          right: 2px;
          display: none;
      }
      // 鼠标悬停时,展示
      &:hover,
      :focus-within {
          .operate-btns {
              display: inline;
          }
      }
  }
 }
  • 子绝对,父相对,使操作按钮靠右,贴边显示
  • 无状态时不显示,hover或内部元素被激活时显示(:hover :focus-within

5、子组件样式

最终效果
  • 旋转图标
    原本的图标使用的是element UI提供的 <i class="el-icon-more" />,是横着的点点点↓
    原本的图标

图标有点小,颜色也不喜欢。改下字体让它变大一点。这里注意需要修改的是元素的before伪类:

 .el-icon-more:before {
      content: "\E794";
      color: #c0c4cc;
      font-size: 20px;
}

加一个transform将它旋转90°,悬停时鼠标样式为pointer:

.rotate {
      cursor: pointer;
      margin-left: 5px;
      transform: rotate(90deg);
 }

点击时,增加圆形半透明的灰色背景:

.rotate:focus {
      width: 20px;
      height: 20px;
      border-radius: 4em;
      background-color: rgba(130, 132, 138, 0.2);
}

至此,下拉全部完成。
除了用在树节点中,也可以用在表格中。

输入过滤框

el-tree提供了过滤方法,使用:filter-node-method="filterNode"属性即可。这里主要分享样式:
效果:

非激活状态

激活输入框状态

模板代码:

<div class="filter-input">
    <el-input placeholder="输入资源名称进行过滤" v-model="filterText" size="small" prefix-icon="el-icon-search">
       </el-input>
</div>
  • 去掉输入框上、左右边框和圆角,并两侧留出10px边距
 .el-input__inner,.el-input-group__prepend{
      width: calc(100% - 20px);
      margin:0 10px;
      height: 40px;
      border-top:none;
      border-width: 0 0 1px;
      border-radius:0;
    }
  • 调整搜索图标大小、颜色和粗细,并稍微调整位置:
    .el-input__prefix{
      .el-input__icon{
        margin-right: 15px;
        display: inline-block;
      }
      font-size:18px;
    }

此时点击输入框,只有下边变蓝色,希望图标的样式也随之更改。
只有input被触发了focus事件,icon感知不到,:focus伪类不满足需求了。我们可以使用:focus-within伪类,加在iconinput共同的父类上。

.el-input:focus-within{
      .el-icon-search:before {
         color: #3c6eff;
         font-weight: bold;
      }
    }

至此完成。

总结

没写前端之前以为前端只是展示从后端拿到的数据,但现在觉得,前端作为面向用户的直接门面,承担了绝大部分交互体验优化的任务。
合理的布局和样式能避免用户的无效操作,体验的优化是一个漫长而细致的过程,可能需要仔细打磨,才能做出好用的产品。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342