业务需求:

树形组件实现二级、三级的联动,即选择某一节点时,其他节点也要同时选中。

发现的坑

虽然el-tree提供了setCheckedKeys方法,但假设node-key属性里有重复值的话,这个方法并不能批量勾选所有的node-key。因此当两个人的value值完全相等的情况下,value值便无法作为node-key使用。

解决关键点

首先需要el-tree提供的getCheckedKeys方法来获取所有已选择的节点;

通过setCheckedKeyssetChecked方法来设置节点的是否选中;

需要关闭render-after-expand属性,保证页面加载时全部渲染,才能实时操作节点的选择;

保证node-key必须不能重复,即使是同一个节点内容;

完整代码如下:

<template>
  <div>
    <el-tree ref="elTree" :data="data" :render-after-expand="false" node-key="treeId" show-checkbox @check-change="checkChange" @check="check"></el-tree>
  </div>
</template>
<script>
export default {
  data() {
    return {
      data: [],
      resList: [],
      resData: [{
        label: '一年级',
        children: [{
          label: '一班',
          children: [{
            label: '丛思萱',
            value: 1,
          }, {
            label: '俞长逸',
            value: 2,
          }, {
            label: '茅诗筠',
            value: 3,
          }, {
            label: '定茹雪',
            value: 4,
          }, {
            label: '程华池',
            value: 5,
          }, {
            label: '释晓曼',
            value: 6,
          }]
        }]
      }, {
        label: '二年级',
        children: [{
          label: '一班',
          children: [{
            label: '丛思萱',
            value: 1,
          }, {
            label: '伏和惬',
            value: 7,
          }, {
            label: '茅诗筠',
            value: 3,
          }, {
            label: '么孤云',
            value: 9,
          }, {
            label: '淦灵松',
            value: 10,
          }, {
            label: '慕学名',
            value: 11,
          }]
        }, {
          label: '二班',
          children: [{
            label: '程华池',
            value: 5,
          }, {
            label: '后静秀',
            value: 12,
          }, {
            label: '巴问芙',
            value: 13,
          }, {
            label: '盖亦云',
            value: 14,
          }, {
            label: '伏和惬',
            value: 7,
          }]
        }]
      }]
    };
  },
  mounted() {
    // 设置tree数据
    this.setData();
  },
  methods: {
    // 设置tree数据
    setData() {
      // 加载来自接口的数据, 本例为 resData
      let resData = this.resData;
      const resList = []

      // 给每个子节点增加treeId
      let treeId = 1
      resData.forEach(item => {
        if (item.children && item.children.length > 0) {
          item.children.forEach(item2 => {
            if (item2.children && item2.children.length > 0) {
              item2.children.forEach(item3 => {
                // 增加treeId
                item3.treeId = treeId;
                treeId ++;
                // 保存现有列表
                resList.push(item3)
              })
            }
          })
        }
      });

      this.data = resData;
      this.resList = resList
    },
    // 节点选中状态发生变化时的回调
    checkChange(data, state) {
      // 获取已选择的节点
      const isSelect = this.$refs.elTree.getCheckedKeys().filter(item => item);
      const treeIds = []; // 用于设置要选中的节点

      if (state) {
        // 遍历已选择的节点
        isSelect.forEach(item => {
          treeIds.push(item);
          // 通过现有列表查询相同节点
          this.resList.forEach(item2 => {
            // 获取已选择的节点对应的对象
            if (item === item2.treeId) {
              // 根据对象的value值再查询相同的人, 获取对应的treeId
              this.resList.forEach(item3 => {
                if (item2.value === item3.value && item3.treeId !== item) {
                  treeIds.push(item3.treeId)
                }
              });
            }
          });
        });
        // 统一设置选中的节点
        this.$refs.elTree.setCheckedKeys(treeIds);
      } else {
        // 单节点判断 - 因为这里如果更改了节点选中状态时会反复触发checkChange方法,因此这里只做单体判断即可
        if (data.value) {
          this.resList.forEach(item => {
            if (item.value === data.value) {
              // 设置为不选中
              this.$refs.elTree.setChecked(item.treeId, false);
            }
          })
        }
      }
    },
    // 当复选框被点击的时候触发 - 获取最终数据
    check(data, select) {
      const isSelect = select.checkedKeys.filter(item => item);
      let selected = []

      isSelect.forEach(item => {
        if (item) {
          this.resList.forEach(item2 => {
            if (item === item2.treeId) {
              selected.push(item2)
            }
          })
        }
      });

      // 去重
      const unmap = new Map()
      selected = selected.filter((item) => {
        return !unmap.has(item['value']) && unmap.set(item['value'], 1)
      });
      
      console.log('当前选择的最终数据:', selected);
    }
  }
}
</script>