element目录树组件el-tree使用相关笔记

默认配置

            <el-tree
              ref="tree"
              highlight-current // 高亮当前选中节点
              accordion // 只打开一个同级树节点展开
              :data="treeData" 
              :expand-on-click-node="false" // 单击节点是否展开(懒加载模式下无效)
              :props="treeDefaultProps" // 映射属性值
              node-key="id" // 标识属性
              :default-expanded-keys="['-1']"  // 默认展开的节点
              @node-click="handleNodeClick" 
              :filter-node-method="filterNode" // 对树节点进行筛选时执行的方法,返回 true则显示
              :check-on-click-node="true" // 点击节点时选中
            >
data() {
    return { 
	   	treeData:[
                id: '-1',
                label: '全部',
                leaf: false,
                children: [
				{
				...
				},
				{
				...
				}
			],
		],
		treeDefaultProps:{
        children: 'children',
        label: 'label',
        isLeaf: 'leaf',
      },
    }        
}

懒加载

   <el-tree
     :load="queryTreeData"
     lazy
   >
    // 获取树目录
    queryTreeData(node, resolve) {
      const { level } = node
      // nodeId 为 -1代表第一次请求
      let nodeId = level == 0 ? '-1' : node.data.id
      const params = {
        displayTable: true,
        parentId: nodeId,
      }
      api
        .queryDirectoryLazyLoad(params)
        .then((res) => {
          const nodes = res.map((item) => ({
            id: item.id,
            label: item.name,
            leaf: !item.hasChildren,
          }))
          // 初始化请求
          if (level == 0) {
            resolve([
              {
                id: '-1',
                label: '全部',
                leaf: false,
                children: nodes, // 非必要
              },
            ])
          } else {
            resolve(nodes)
          }
        })
        .then(() => {
          this.$refs.tree.setCurrentKey(this.directoryId)
        })
        .catch((error) => {
          console.log(error)
        })
    },

递归处理数据

递归遍历树级结构,进行字段映射

    mapTree(data) {
      const haveChildren =
        Array.isArray(data.childNode) && data.childNode.length
      return {
        label: data.nodeName,
        id: data.nodeId,
        children: haveChildren
          ? data.childNode.map((i) => this.mapTree(i))
          : [],
      }
    },

一维数组处理为树结构

思路:
ant-design-vue

/**
 * 数组转树形结构
 * @param list 源数组
 * @param tree 树
 * @param parentId 父ID
 */
const listToTree = (list, tree, parentId) => {
  list.forEach(item => {
    // 判断是否为父级菜单
    if (item.parentId === parentId) {
      const child = {
        ...item,
        key: item.key || item.name,
        children: []
      }
      //  找到当前菜单相符合的所有子菜单
      listToTree(list, child.children, item.id)
      // 删掉不存在 children 值的属性
      if (child.children.length <= 0) delete child.children
      // 加入到树中
      tree.push(child)
    }
  })
}
// 在idx=0的该次循环中,找到最高层级pid为-1的元素101。调用listToTree,把list、当前层级的信息、当前id(101)作为参数传入,去寻找pid为101的元素。当pid为101的遍历结束后,id为102和104的child信息被推入children的字段中,idx=0的此次遍历结束。以此迭代遍历,直至找到所有的子节点。
const list = [
  {'id':101,'name':'语文','parentId': -1},
  {'id':102,'name':'语文知识点1','parentId': 101},
  {'id':103,'name':'语文知识点11','parentId': 102},
  {'id':104,'name':'语文知识点2','parentId': 101},
]
let resTree = []
listToTree(list, resTree, -1)
console.log(resTree)

在这里插入图片描述

默认选中并展开特定节点

初始化的需求场景

// 默认展开
            <el-tree
              ref="tree"
              :default-expanded-keys="expandedKeys" 
              lazy
            >
  computed: {
    // 树目录默认展开的节点
    expandedKeys() {
      let directoryLink = this.$route.query.directoryLink?.split(',')
      const directoryId = this.$route.query.directoryId
      if (_.isEmpty(directoryId) || _.isEmpty(directoryLink)) return '-1'
      return ['-1'].concat(...directoryLink, directoryId)
    },
  },
// 初始化选中
    queryTreeData() {
    ...
      api.queryDirectory(params).then((res) => {
        this.treeData = this.convertTreeData(res.list)
        this.$nextTick(() => {
          // nextTick 确保组件渲染后能set得到key
          this.$refs.tree.setCurrentKey(this.directoryId)
        })
        ...
      })
    },

切换tab后的需求场景

思路: 切换tab, 会引起树组件的数据变动, 仅需监听数据, 在回调里设置选中第一个节点即可.

在这里插入图片描述

注: this.$nextTick 很关键,确保在组件渲染后才执行选中节点代码;

  watch: {
    treeData: {
      handler: function (newVal, oldVal) {
        // 只有切换tab页才会导致树组件重新渲染, 因此不用设置首次渲染的标识
        // 当数据改变时, 组件元素还未更新, 用nextTick等组件更新完再执行
        this.$nextTick(() => {
          newVal[0] && this.$refs.treeComp.setCurrentKey(newVal[0].id)
        })
      },
    },
  },

禁止点击事件

<el-tree
    ref="tree"
    :class="defaultSelectedKey ? 'no-event' : ''"
>
  .no-event {
    pointer-events: none;
  }

注:不可加在 el-tree 的父级元素上, 否则会把滚动事件也禁止掉, 导致无法正常滚动.

搜索

本地搜索

思路:el-input 的筛选回调为 filterTreeNode,该方法会调用树的 filter方法,然后在 el-tree 绑定一个动态属性 filter-node-method,在相应的值 filterNode方法中,会挨个遍历每个节点的 data,并返回匹配结果。

注:此方法的应用前提为树目录非懒加载

        <!-- 左侧栏 -->
        <div class="data-application-sidebar l-box-col" v-loading="treeLoading">
          <el-input
            class="search-input l-mb-10"
            placeholder="请输入资产目录查询"
            size="medium"
            v-model="filterTreeVal"
            @keyup.enter.native="filterTreeNode"
            @clear="filterTreeNode"
            clearable
          >
            <div slot="prefix" class="search-icon" @click="queryData">
              <i class="el-icon-search"></i>
            </div>
          </el-input>
          <div class="data-application-sidebar-tree l-flex">
            <el-tree
              ref="tree"
              highlight-current
              accordion
              :data="treeData"
              :expand-on-click-node="false"
              :props="treeDefaultProps"
              :node-key="treeDefaultProps.id"
              :default-expanded-keys="['-1']"
              @node-click="handleNodeClick"
              :filter-node-method="filterNode"
            >
              <span class="custom-tree-node" slot-scope="{ node, data }">
                <span v-if="data.isFolder" class="l-mr-10">
                  <i class="el-icon-folder"></i>
                </span>
                <span v-else class="l-mr-10">
                  <i class="el-icon-document"></i>
                </span>
                <span class="custom-tree-node-label">{{ node.label }}</span>
              </span>
            </el-tree>
          </div>
        </div>

    /**
     * 过滤树目录
     */
    filterTreeNode() {
      this.$refs.tree.filter(this.filterTreeVal)
    },
    filterNode(value, data) {
      if (!value) return true
      return data.label.indexOf(value) !== -1
    },

搜索后滚动定位结果

思路: 计算出选中节点的上边框距离父容器的上边框的距离 (offsetTop), 若此距离 h > 父容器高度, 则设置父容器滚动距离 (scrollTop) 为 h

 nodeScroll() {
      setTimeout(() => {
        let liList = this.$refs.tree.$el.getElementsByClassName('is-current')
        let offsetTop = liList[0].offsetTop
        let listWrapper = document.querySelector('.market-input-directory-tree')
        if (offsetTop + 30 > listWrapper.clientHeight) {
          listWrapper.scrollTop = offsetTop
        }
      }, 0)
    },

注:

  1. nodeScroll中要使用异步函数,先切换了选中的节点,再滚动
  2. 千万不可以使用 nextTick。如果在 nodeScroll中使用nextTick,offsetTop获取的值会是上一选中节点的位置
  3. 当树组件节点较多时, 搜索节点需要一定的时间, 此时 nodeScroll 可能会在setCurrentKey 前执行, 可设置定时器来延迟 nodeScroll 的执行. (使用第三方组件的弊端: 由于搜索执行的过程被封装了, 所以不能严格控制代码执行的先后顺序)

添加图标

在这里插入图片描述

方法一: 通过伪类的 background 属性

                  <el-tree
                  class="flow-chart-tree-content-item"
                  ref="treeInFlowChart"
                  node-key="id"
                  :default-expand-all="false"
                  :data="treeData"
                  :expand-on-click-node="true"
                  :filter-node-method="filterNode"
                  :class="workflowDisabled ? 'flow-chart-no-event' : ''"
                >
                  <span
                    class="custom-tree-node"
                    slot-scope="{ node, data }"
                    @mousedown="addNodeToGraph($event, data)"
                  >
                    <span>
                      <span class="l-mr-5">
                        <i class="icon-sql"></i>
                      </span>
                      <span>{{ node.label }}</span>
                    </span>
                  </span>
                </el-tree>
    .icon-sql:before {
      content: '';
      background: url(~@/assets/agileDevTools/sql.png) no-repeat center center;
      background-size: contain;
      width: 20px;
      height: 20px;
      display: block;
      position: relative;
      top: 3px;
    }

方法二: 通过 img 标签引入图片

          <div class="data-application-sidebar-tree l-flex">
            <el-tree
              ref="tree"
              highlight-current
              accordion
              :data="treeData"
              :expand-on-click-node="false"
              :props="treeDefaultProps"
              node-key="id"
              :default-expanded-keys="[directoryId]"
              @node-click="handleNodeClick"
              :filter-node-method="filterNode"
            >
              <span class="custom-tree-node" slot-scope="{ node, data }">
                <span class="l-mr-10">
                  <img :src="require(`@/assets/workbench/folder.png`)" />
                </span>
                <span class="custom-tree-node-label">{{ node.label }}</span>
              </span>
            </el-tree>
          </div>

修改选中的高亮 (图标和颜色)

在这里插入图片描述

选中时图标切换

思路: 利用选中节点时带有的 focus 伪类, 去设置相应的子元素样式和图标

文字和背景的高亮

   <el-tree
   >
     <span
       class="custom-tree-node"
       slot-scope="{ node, data }"
     >
       <span>
         <span v-show="data.icon" class="l-mr-5">
         // 修改特定节点的高亮, 需要在动态类属性做一个是否添加 directory 类名的判断
           <i
             :class="[data.icon, data.notNode ? 'directory' : '']"
           ></i>
         </span>
         <span>{{ node.label }}</span>
       </span>
     </span>
   </el-tree>
  ::v-deep .el-tree-node__content:hover,
  ::v-deep .el-tree-node:focus > .el-tree-node__content {
    background: #f2f9ff;
  }

  ::v-deep .el-tree-node:focus > .el-tree-node__content {
    color: #2590ff;
  }

  // 修改图标
  ::v-deep .el-tree-node:focus > .el-tree-node__content .directory:before {
    background: url(~@/assets/agileDevTools/data-directory-active.png) no-repeat
      center center;
  }

可编辑树

可编辑树目录

点击展开后回调

 @node-expand="handleNodeExpand"

注: node-expand 只针对人为点击引起的展开事件, 由 default-expanded-keys 属性值变动而展开的事件, 不会触发 node-expand 的事件.

点击节点图标切换显示(包含一键切换全部图标)

请添加图片描述

思路: 点击图标(文字), 切换 data.showField的值, 即用节点数据的字段 showField 来控制切换


<div class="popover-tree-el-content l-mb-12" :style="setContentStyle">
  <el-tree
    ref="treeInPopoverEl"
    node-key="id"
    show-checkbox
    default-expand-all
    :data="treeData"
    :expand-on-click-node="false"
    :check-on-click-node="true"
    :filter-node-method="filterNode"
  >
    <div class="custom-tree-node" slot-scope="{ node, data }">
      <span>
        <span>{{ node.label }}</span>
      </span>

      <span class="l-mr-30">
        <span
          v-if="data.showField"
          class="custom-tree-node-remove"
          @click.stop="() => toggleField(node, data)"
          >{{ node.label == '全选' ? '全部移除' : '移除' }}</span
        >
        <span
          v-else
          class="custom-tree-node-recover"
          @click.stop="() => toggleField(node, data)"
          >{{ node.label == '全选' ? '全部恢复' : '恢复' }}</span
        >
      </span>
    </div>
  </el-tree>
</div>

toggleField(node, data) {
  // 1. 更新被点击节点的数据字段 showField, 以此切换图标显示
  data.showField = !data.showField
  // 2. 全选有两种情况需处理: 点击全部处理子节点, 当所有子节点状态一致, 则处理全部 
  if (data.label == '全选') {
  // 若点击全部, 则遍历赋值, 更新所有子节点
    this.treeData[0].children.forEach((item) => {
      item.showField = data.showField
    })
  } else {
  // 若点击的是子节点, 则处理当所有子节点一致时, 改变全部的图标的情况
    const treeChildData = this.treeData?.[0].children
    const allChange = treeChildData.every(
      (item) => item.showField == data.showField
    )
    // 若所有字段都改变,则改变全选按钮
    if (allChange) this.treeData[0].showField = data.showField
  }
},
.custom-tree-node {
  width: 100%;
  display: flex;
  justify-content: space-between;

  &-recover {
    color: #358df6;
  }
}
  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
element-plus 是一个基于 Vue.js 的 UI 组件库,el-tree-select 是其中的一个组件,用于实现形结构的下拉选择。 el-tree-select 组件是在 el-select 组件的基础上进行扩展,使其支持形结构的数据。使用 el-tree-select,我们可以在下拉列表中展示状数据,并支持选择节点的功能。 el-tree-select 的使用方式大致分为以下几步: 1. 引入 element-plus 库,确保已安装并正确引入相关 CSS 和 JS 文件。 2. 在组件使用 el-tree-select 标签,通过 v-model 绑定选中的节点,将选中的节点值赋给 data 中的一个变量。 3. 设置 el-tree-select 的配置项,包括数据源、显示字段、样式等。 4. 响应选择事件,在 el-tree-select 标签上绑定 change 事件,根据选中的节点进行相应操作。 配置项中常用的属性有: - data:形数据源,可以是一个数组或者通过异步加载数据。 - label-prop:用于显示节点文本的属性名。 - value-prop:用于取值的属性名。 - default-expand-all:是否默认展开全部节点。 - filterable:是否支持输入框搜索。 el-tree-select 还提供了其他的配置项和方法,可以根据具体需求进行调整和使用。 总之,element-plus 的 el-tree-select 组件提供了一种简单易用的方式来展示和选择形数据,在Vue.js项目中非常实用。通过合理配置,我们可以灵活定制形下拉选择框的功能和样式,使其符合项目的需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值