element el-tree复选框父子节点不关联实现联动回显

3,391 阅读3分钟

前言

树形复选框在实际业务开发中会遇到一些特殊需求,如点击父节点全选时需要跳过某些子节点不需要选中,让用户自己手动勾选的情况,或者其他需要自定义选中逻辑的需求。

功能介绍

  1. 点击根(父)节点,子节点全部勾选;
  2. 点击子节点父节点勾选上(嵌套多层父节点自动递归往上查找);
  3. 已勾选父节点下的子节点全部取消勾选,父节点就取消勾选状态;

注意事项

  • parentId和children根据你自己获取到的参数修改
  • 如要跳过某个子节点,在uniteChildSame函数中添加如下if判断:
if (treeListData[i].label !== '二级 1-1') {
  this.uniteChildSame(treeListData[i], isSelected, ref);
}
  • 代码实现的是点击父节点子节点全选中,具体逻辑实现按你的项目需求来修改代码

源码

获取最终选中id值this.$refs.tree.getCheckedKeys().concat(this.$refs.tree.getHalfCheckedKeys())返回数组
<template>
  <div>
    <el-tree ref="tree"
             :data="treeMenus"
             :props="multiProps"
             :show-checkbox="true"
             node-key="id"
             highlight-current
             :expand-on-click-node="true"
             :default-checked-keys="checkedId"
             :check-strictly="true"
             @check="clickDeal">
    </el-tree>
  </div>
</template>

<script>
export default {
  name: "tree",
  data() {
    return {
      checkedId: [],
      treeMenus: [{
        id: 1,
        parentId: -1,
        label: '一级 1',
        children: [{
          id: 4,
          parent: 1,
          label: '二级 1-1',
          children: [{
            id: 9,
            parentId: 4,
            label: '三级 1-1-1',
            children: [{
              id: 1000,
              parentId: 9,
              label: '三级 1000-1-1',
              children: []
            }, {
              id: 1001,
              parentId: 9,
              label: '三级 1001-1-1',
              children: [{
                id: 2000,
                parentId: 1001,
                label: '三级 2000-1-1',
                children: []
              }, {
                id: 2001,
                parentId: 1001,
                label: '三级 2001-1-1',
                children: []
              }]
            }]
          }, {
            id: 10,
            parentId: 4,
            label: '三级 1-1-2',
            children: []
          }]
        }, {
          id: 20,
          parentId: 1,
          label: '123',
          children: []
        }, {
          id: 25,
          parentId: 1,
          label: '12456',
          children: []
        }]
      }, {
        parentId: -1,
        id: 2,
        label: '一级 2',
        children: [{
          parentId: 2,
          id: 5,
          label: '二级 2-1',
          children: []
        }, {
          parentId: 2,
          id: 6,
          label: '二级 2-2',
          children: []
        }]
      }, {
        parentId: -1,
        id: 3,
        label: '一级 3',
        children: [{
          parentId: 3,
          id: 7,
          label: '二级 3-1',
          children: []
        }, {
          parentId: 3,
          id: 8,
          label: '二级 3-2',
          children: []
        }]
      }],
      multiProps: {
        children: 'children',
        label: 'label'
      }
    }
  },
  methods: {
    clickDeal(currentObj, treeStatus) {
      this.clickCheck(currentObj, treeStatus, this.$refs.tree)
    },

    /**
     * 树形菜单复选框父子节点不关联实现父子节点联动回显
     *
     * @see selectedParent - 处理父节点为选中
     * @see uniteChildSame - 处理子节点为相同的勾选状态
     * @see removeParent   - 子节点全没选中取消父级的选中状态
     *
     * @param {Object} currentObj - 当前勾选节点的对象
     * @param {Object} treeStatus - 树目前的选中状态对象
     * @param {Object} ref - this.$refs.xxx
     **/
    clickCheck(currentObj, treeStatus, ref) {
      // 用于:父子节点严格互不关联时,父节点勾选变化时通知子节点同步变化,实现单向关联。
      let selected = treeStatus.checkedKeys.indexOf(currentObj.id); // -1未选中

      // 选中
      if (selected !== -1) {
        // 子节点只要被选中父节点就被选中
        this.selectedParent(currentObj, ref);
        // 统一处理子节点为相同的勾选状态
        this.uniteChildSame(currentObj, true, ref);
      } else {
        // 取消子节点的选中状态触发
        if (currentObj.parentId !== -1) {
          this.removeParent(currentObj, ref);
        }

        // 未选中 处理子节点全部未选中
        if (currentObj.children.length !== 0) {
          this.uniteChildSame(currentObj, false, ref);
        }
      }
    },

    /**   统一处理子节点为相同的勾选状态  **/
    uniteChildSame(treeList, isSelected, ref) {
      let treeListData = treeList.children;
      let len = treeListData.length;

      ref.setChecked(treeList.id, isSelected);

      for (let i = 0; i < len; i++) {
        this.uniteChildSame(treeListData[i], isSelected, ref);
      }
    },

    /**    统一处理父节点为选中    **/
    selectedParent(currentObj, ref) {
      let currentNode = ref.getNode(currentObj);
      if (currentNode.parent.key !== undefined) {
        ref.setChecked(currentNode.parent, true);
        return this.selectedParent(currentNode.parent, ref);
      }
    },

    /**    子节点全没选中取消父级的选中状态   **/
    removeParent(currentObj, ref) {
      let a = 0;
      let b = 0;
      let i = 0;
      let j = 0
      let currentNode = ref.getNode(currentObj);
      if (currentNode.parent !== null) {
        if (currentNode.parent.key !== undefined) {
          ref.setChecked(currentNode.parent, true); //根节点
          this.removeParent(currentNode.parent, ref); //递归判断子节点
        }
      }

      //不为0表示为父节点
      if (currentNode.childNodes.length !== 0) {

        //循环判断父节点下的子节点
        for (i; i < currentNode.childNodes.length; i++) {

          //判断父节点下的子节点是否全为false
          if (currentNode.childNodes[i].checked === false) {
            ++a;
          }

          //a === currentNode.childNodes.length 表明子节点全为false
          if (a === currentNode.childNodes.length) {

            //等于 undefined 跳过,不等于继续执行
            if (currentNode.childNodes[i].parent.key !== undefined) {
              ref.setChecked(currentNode.childNodes[i].parent, false); //父元素设置为false
              //循环上级父节点下的子节点
              for (j = 0; j < currentNode.parent.childNodes.length; j++) {

                //判断父节点下的子节点是否全为false
                if (currentNode.parent.childNodes[j].checked === false) {
                  ++b;
                }

                //b === currentNode.parent.childNodes.length 表明子节点全为false
                if (b === currentNode.parent.childNodes.length) {
                  ref.setChecked(currentNode.parent.key, false); //父元素设置为false
                  return this.removeParent(currentNode.parent, ref); //继续递归循环判断
                }
              }
            }
          }
        }
      }
    },
  }
}
</script>

本代码修改自:blog.csdn.net/Beam007/art…