• el-tree 懒加载数据,展开的节点与查询条件联动


    效果描述

    项目某模块左侧为el-tree树形结构地市数据,右侧有地市查询条件及结果展示
    要求:

    1. 点击左侧某地市进行查询时,右侧的地市查询输入框内同步进行赋值(如左侧点击成都,右侧地市查询输入框内,应该自动选中成都)
    2. 同理,右侧地市查询输入框 选中成都时,左侧树形结构成都节点自动展开

    实现原理

    步骤1:el-tree设置node-key

    在这里插入图片描述
    首先要知道node-key 是设置展开和选中的必要设置,其绑定的值是整个树唯一的(一般绑定 id 等),如果出现重复,会报错。同时设置展开某个节点时,如果这个节点的 id 存在重复,则会出现展开混乱的现象

    备注:这里注意,可能在某些特殊时候,后台返回的树形数据,并不存在唯一值(比如我这里,绑定的字段 id ,但在数据里,存在部分数据 id 会一致的情况),这个时候要么是让后端将 id 处理成唯一值,要么就是我们自己对拿到的数据进行处理了(这里我是自己处理的)

    步骤2:懒加载时对数据进行处理,给整个树形数据添加唯一值

    因为数据量过大,我这里的el-tree用的是懒加载获取数据的方法(:load=“loadNode”)

    <el-tree
      ref="tree"
      node-key="key"
      accordion
      @node-expand="nodeExpandFn"
      :filter-node-method="filterNode"
      :data="treeData"
      :default-expanded-keys="expanded"
      :props="defaultProps"
      :indent="0"
      lazy
      auto-expand-parent
      highlight-current
      :load="loadNode"
      @node-click="handleNodeClick"
    >
    el-tree>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    可以看到,node-key 我绑定的是 key 字段,这是我给数据中每个对象添加的唯一值:
    loadNode方法中,在获取数据时添加唯一值,这里不用关注其他代码,只需关注我根据每个节点的特性,给每个节点对象都增加了一个 key 字段 (我这里第三级的节点数据,每个对象的id是相同的,这也是我选择增加 key 字段的原因)

    loadNode(node, resolve) {
     if (node.level === 0) {
       this.resolve = resolve
       this.chooseNode = node
       queryKSHDeviceTree({ type: "0" }).then(res => {
         const treeData = [];
         res.resultValue.forEach(e => {
           e.key = e.id; // key ----------------------
           treeData.push(e);
         });
         resolve(treeData);
       });
     }
     else if (node.level === 1) {
       queryKSHDeviceTree({ type: '1' }).then(res => {
         let treeData = [];
         res.resultValue.forEach(e => {
           e.key = e.id; // key ------------------------
           treeData.push(e);
         });
         resolve(treeData);
       });
     } else if (node.level === 2) {
       queryKSHDeviceTree({ type: '2',pId: node.data.id }).then(res => {
         let treeData = [];
         res.resultValue.forEach(e => {
           // 这里根据当前对象自己的数据,拼出一个唯一值
           e.key = node.data.id + e.pId; // key --------------------
           treeData.push(e);
         });
         resolve(treeData);
       });
     }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    步骤3:(联动) 点击左侧树形结构,右侧对应查询框自动赋值

    这一步相对来说简单一些,在el-tree的点击事件 handleNodeClick 中进行操作

    // 如果右侧查询框绑定的字段为 queryForm.cityId
    // 点击第二级节点,对其他查询框赋值也同理
    handleNodeClick(data, node) {
      if (data.label === "city") {
        this.queryForm.cityId = data.id;
      }
      else if ...
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    步骤4:(联动) 右侧查询条件选择好后,点击查询,左侧树形结构自动展开对应节点

    这里 主要用到了 :default-expanded-keys=“expanded” (默认展开节点)
    expanded 为 存放 key 值的数组

    其实当整个树形数据完整,并且设置了 auto-expand-parent 
    (展开子节点的时候自动展开父节点)的情况下,
    这个时候expanded只用保存最后一级的key值即可
    
    但是,由于我这里的数据是懒加载的,意味着虽然你右侧选了最后一级的数据,点击查询,
    但我左侧的树形数据里,还并未加载子级数据,所以这个时候,
    即时你将key存入expanded里,它找不到,所以也是没有用的
    
    ***所以这个时候,我们需要把每一级要展开的节点key字段都拿到,并存入expanded中
    ***这样,el-tree在自动展开某一节点时,它会根据懒加载方法,自动获取子节点的数据
    ***当子节点数据获取到后,它也会将每个节点的key自动去匹配expanded中的值,若存在相同的,
    就会接着展开
    !!!(简单的说,就是把每一级要展开的节点key字段都拿到,并存入expanded中,
    同时懒加载方法写好,其他的交给el-tree自己就行)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    经过测试,在展开节点后,对expanded重新赋值,已展开的节点不会自动关闭,所以这里也写了方法,先将el-tree所有的节点进行关闭

    // 查询方法
    queryClick() {
      let obj = {
        refName: 'tree2',
        cityId: this.queryForm.cityId,
        twoId: this.queryForm.twoId,
        threeId: this.queryForm.threeId || ''
      }
      this.treeNodeLinkage(obj);
    }
    
    // 查询条件和el-tree显示联动
    // cityId 为第一级id,twoId 为第二级,threeId 为第三级
    treeNodeLinkage({refName, cityId, twoId , threeId}) {
      // 先将el-tree所有节点关闭
      let nodesMap = this.$refs[refName].store.nodesMap; // 拿到全部的node
      for(let key in nodesMap) {
        nodesMap[key].expanded = false;
      }
      // 这里的第二级id,对应懒加载方法里 node.level === 2时候
      // 所以这里的newId拼接方法,按照当时key的拼接方法来
      let newId = cityId + twoId;
      this.expanded = [cityId, newId];
      if(threeId) {
        this.expanded.push(threeId);
      }
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    步骤5:定位到查询的节点

    添加定位方法

    思路:通过当前选中的节点样式   '.is-current' 进行定位,这里通过this.$refs.tree.setCurrentKey(key)方法,
    对查询的节点进行选中。
    
    • 1
    • 2

    下面的’.treeDome’ 为滚动的父元素 的 class名

    // 首先在上面的treeNodeLinkage方法里进行一些操作
    /**  this.$refs.tree.setCurrentKey(null); // 清除上次的选中
    	document.querySelector('.treeDome').scrollTop = 0; // 父盒子滚动条回到顶部
    	this.$refs.tree.setCurrentKey(threeId); // 设置选中
    	setTimeout(() => {
            this.toPositionTree();
        }, 500)
    */ 
    treeNodeLinkage({refName, cityId, twoId , threeId}) {
      // 先将el-tree所有节点关闭
      let nodesMap = this.$refs[refName].store.nodesMap; // 拿到全部的node
      for(let key in nodesMap) {
        nodesMap[key].expanded = false;
      }
      // 这里的第二级id,对应懒加载方法里 node.level === 2时候
      // 所以这里的newId拼接方法,按照当时key的拼接方法来
      let newId = cityId + twoId;
      this.expanded = [cityId, newId];
      this.$refs.tree2.setCurrentKey(null); // 清除上次的选中
      document.querySelector('.treeDome').scrollTop = 0; // 父盒子滚动条回到顶部
      if(threeId) {
        this.expanded.push(threeId);
        /*
        	因为数据已加载,则可直接设置选中,如果未加载,可能会出现执行这段代码时,el-tree里数据还未拿到,
        	会导致下面的toPositionTree方法,一直拿不到'.is-current'元素,
        	所以在 :load="loadNode" 拿到数据的地方加上
    	 	setTimeout(() => {
    	      this.$refs.tree.setCurrentKey(this.filterText2);
    	    }, 500)
    	   执行完这个后,元素就能成功被选中,toPositionTree方法也能成功执行
    	 */ 
        this.$refs.tree.setCurrentKey(threeId); 
      }
    },
    
    toPositionTree() {
     // 这里因为数据是懒加载,可能一次找不到当前的节点,所以进行重复查询,查到为止
    
     if(document.querySelector('.is-current')) {
       let nodeOffsetTop = document.querySelector('.is-current').offsetTop;
       // console.log('.is-current', nodeOffsetTop);
       let tempH = document.querySelector('.treeDome').clientHeight / 2;
       if (nodeOffsetTop > tempH) {
         document.querySelector('.treeDome').scrollTop = nodeOffsetTop - tempH + 150;
         // console.log('.treeDome', document.querySelector('.treeDome').scrollTop);
       }
     } else {
       setTimeout(() => {
         this.toPositionTree();
       },500)
     }
    },
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52

    总结

    其实这个联动,主要是搞懂el-tree的 node-keydefault-expanded-keys 属性就行。
    简单来说,就是你想展开哪个节点,就把该节点node-key对应的字段放入default-expanded-keys对应的数组中。

    其他的就是对数据的处理
    把接口获取到的数据处理成你想要的样子,这是前端在很多时候都要做的事情

  • 相关阅读:
    .NET深入了解哈希表和Dictionary
    密码技术 (3) - 单向散列函数
    Pytorch框架详解
    The system is running in low-graphics mode解决方法
    两千字讲明白java中instanceof关键字的使用!
    思维意识转变是施工企业数字化转型成败的关键
    PostgreSQL数据库缓冲区管理器——本地缓冲区管理
    界面组件DevExpress WPF v23.2 - 全新升级的数据编辑器、流程图组件
    3-MySQL常用数据类型及表管理
    开发工程师必备————【Day15】python操作Mysql及SQL语法补充
  • 原文地址:https://blog.csdn.net/IT_dabai/article/details/132731569