vue el-tree 递归实现子节点未全部勾选时,父节点半选,子节点全部勾选时,父节点勾选

el-tree checkStrictly 设置 true, 勾选树节点时,实现子节点全勾选,父节点勾选;子节点未全勾选,父节点半选;子节点全取消勾选,父节点取消勾选;父节点勾选,子节点全部勾选;父节点取消勾选,子节点全部取消勾选。checkChange事件中提供 currentList(当前已勾选节点的数组), currentNode(当前操作的节点对象)
如何实现 ?

在父节点获取是否点选,然后再遍历

这个你得自己修改css,增加部分选择的样式功能

<el-tree :data="treeData" node-key="label" :props="defaultProps">
  <template slot-scope="{ node, data }">
    <span class="custom-node">
      <span>{{ node.label }}</span>
      <span v-if="node.children && node.children.length !== 0">
        <i v-if="data.state === 'checked'" class="el-icon-check custom-icon-checked"></i>
        <i v-else-if="data.state === 'unchecked'" class="el-icon-close custom-icon-unchecked"></i>
        <i v-else-if="data.state === 'indeterminate'" class="el-icon-minus custom-icon-indeterminate"></i>
      </span>
    </span>
  </template>
</el-tree>



.custom-icon-checked {
  color: gray; /* 设置图标为灰色 */
  font-size: 16px; /* 设置图标大小 */
}

.custom-icon-unchecked {
  color: gray;
  font-size: 16px;
}

.custom-icon-indeterminate {
  color: gray;
  font-size: 16px;
}


<template>
  <el-tree
    :data="treeData"
    :props="treeProps"
    :default-expanded-keys="[1]"
    :check-strictly="true"
    @check-change="handleCheckChange"
  ></el-tree>
</template>

<script>
export default {
  data() {
    return {
      treeData: [
        {
          id: 1,
          label: 'Parent 1',
          children: [
            {
              id: 11,
              label: 'Child 1-1'
            },
            {
              id: 12,
              label: 'Child 1-2'
            }
          ]
        },
        {
          id: 2,
          label: 'Parent 2',
          children: [
            {
              id: 21,
              label: 'Child 2-1'
            },
            {
              id: 22,
              label: 'Child 2-2'
            }
          ]
        }
      ],
      treeProps: {
        children: 'children',
        label: 'label'
      }
    };
  },
  methods: {
    handleCheckChange(data) {
      const currentNode = data.node;
      const currentList = data.currentList;

      // 更新当前节点的子节点勾选状态
      const updateChildNodes = (node, checked) => {
        if (node.children && node.children.length > 0) {
          node.children.forEach(childNode => {
            childNode.checked = checked;
            updateChildNodes(childNode, checked);
          });
        }
      };

      // 更新当前节点的父节点勾选状态
      const updateParentNodes = node => {
        const parent = this.$refs.tree.getNode(node.data.parent.id);
        if (parent) {
          let allChecked = true;
          let allUnchecked = true;
          parent.data.children.forEach(childNode => {
            if (childNode.checked !== true) {
              allChecked = false;
            }
            if (childNode.checked !== false) {
              allUnchecked = false;
            }
          });
          parent.setChecked(allChecked, allUnchecked);
          updateParentNodes(parent);
        }
      };

      if (currentNode) {
        const checked = currentNode.checked;
        updateChildNodes(currentNode.data, checked);
        updateParentNodes(currentNode);
      }
    }
  }
};
</script>

您可以使用以下代码来实现在checkChange事件中实现上述功能:

javascript
// 初始化树数据
var treeData = [
  {
    label: '父节点1',
    children: [
      {
        label: '子节点1',
        children: [
          {
            label: '子节点1-1'
          },
          {
            label: '子节点1-2'
          }
        ]
      },
      {
        label: '子节点2'
      }
    ]
  },
  {
    label: '父节点2',
    children: [
      {
        label: '子节点3'
      },
      {
        label: '子节点4'
      }
    ]
  }
];

// 初始化Vue实例
new Vue({
  el: '#app',
  data: {
    treeData: treeData
  },
  methods: {
    checkChange: function(currentNode, checkedNodes) {
      if (checkedNodes.length > 0) {
        // 当前节点被勾选
        if (currentNode.checked) {
          // 勾选当前节点,递归勾选所有子节点
          this.checkAllChildren(currentNode, true);
          // 递归勾选所有父节点
          this.checkAllParent(currentNode, true);
        } else {
          // 取消勾选当前节点,递归取消勾选所有子节点
          this.checkAllChildren(currentNode, false);
          // 递归取消勾选所有父节点
          this.checkAllParent(currentNode, false);
        }
      }
    },
    checkAllChildren: function(node, checked) {
      // 递归勾选/取消勾选所有子节点
      if (node.children && node.children.length > 0) {
        node.children.forEach(function(child) {
          child.checked = checked;
          this.checkAllChildren(child, checked);
        }, this);
      }
    },
    checkAllParent: function(node, checked) {
      // 递归勾选/取消勾选所有父节点
      var parent = node.parent;
      if (parent && parent.level > 0) {
        var siblings = parent.children;
        var allChecked = siblings.every(function(child) {
          return child.checked === true;
        });
        var halfChecked = siblings.some(function(child) {
          return child.checked === true || child.indeterminate === true;
        });

        parent.checked = allChecked;
        parent.indeterminate = halfChecked;

        this.checkAllParent(parent, checked);
      }
    }
  }
});

上述代码中,我们通过Vue实例的数据属性treeData来存储树的数据。在checkChange方法中,我们根据当前节点的勾选状态来递归勾选/取消勾选所有子节点,并递归勾选/取消勾选所有父节点。checkAllChildren方法用于递归勾选/取消勾选所有子节点,checkAllParent方法用于递归勾选/取消勾选所有父节点。
请注意,以上代码是基于Vue框架的实现,您需要引入Vue库,并在HTML中定义一个id为'app'的元素作为Vue实例的挂载点。同时,您需要根据您的实际情况进行适当的调整和修改。

参考gpt:
结合自己分析给你如下建议:
一种方法是使用el-tree的check-change事件,获取当前操作的节点对象和树目前的选中状态对象,然后根据这些信息来更新父子节点的勾选状态。
另一种方法是使用el-tree的getCheckedNodes和setCheckedNodes方法,获取所有勾选的节点数组,然后根据这些节点的父子关系来判断是否需要全选、反选或半选。
还有一种方法是使用递归函数,遍历树形数据,根据节点的id和checked属性来设置父子节点的勾选状态。


el-tree 树结构实现父子不关联(check-strictly) 且能全选、反选、半选_el-tree check-strictly_橙色日落的博客-CSDN博客 解决:单独把全部抽离出来,拉平树结构数据,根据选中长度同拉平后的数据长度进行对比实现全选、反选、半选。_el-tree check-strictly https://blog.csdn.net/misschengispink/article/details/127388564

要实现在 el-tree 中勾选状态的递归关联,您可以利用 checkChange 事件来监听节点的勾选状态变化,并在该事件中递归更新父节点和子节点的勾选状态。以下是一个可能的实现方式:

<template>
  <div>
    <el-tree
      :data="treeData"
      :props="treeProps"
      :check-strictly="true"
      @check-change="handleCheckChange"
    ></el-tree>
  </div>
</template>

<script>
export default {
  data() {
    return {
      treeData: [
        // Your tree data here
      ],
      treeProps: {
        children: 'children', // Adjust this based on your data structure
        label: 'label', // Adjust this based on your data structure
      },
    };
  },
  methods: {
    handleCheckChange(currentList, currentNode) {
      // Update parent node based on child nodes' status
      const updateParent = (node) => {
        const parentNode = this.$refs.tree.getNode(node.data.parent);
        if (parentNode) {
          const childNodes = parentNode.childNodes;
          const allChecked = childNodes.every((child) => child.checked);
          const someChecked = childNodes.some((child) => child.checked || child.indeterminate);

          parentNode.setChecked(allChecked, someChecked);
          updateParent(parentNode); // Recursive call for parent nodes
        }
      };

      // Update child nodes based on parent node's status
      const updateChildren = (node) => {
        const childNodes = node.childNodes;
        childNodes.forEach((child) => {
          child.setChecked(node.checked);
          updateChildren(child); // Recursive call for child nodes
        });
      };

      // Handle check change for the current node
      if (currentNode) {
        updateParent(currentNode);
        updateChildren(currentNode);
      }
    },
  },
};
</script>

在这个示例中,handleCheckChange 方法会被调用当任何节点的勾选状态发生变化时。在这个方法中,我们递归地更新父节点和子节点的勾选状态,以实现您所描述的逻辑。

请注意,上述代码只是一个示例,您需要根据您的具体数据结构和组件配置进行调整。同时,这个示例假设您使用的是 Element UI 的 el-tree 组件。如果您使用的是其他库或组件,可能需要稍作修改。

试试这个


 <el-tree :data="data" show-checkbox accordion node-key="id" :default-checked-keys="userPrivileges" @check="nodeClick" :props="defaultProps"> </el-tree>

data(){
  return{
          data: [], //树形数组
      defaultProps: {
        children: 'children',
        label: 'moduleName',
      },
      userPrivileges: [], //回显
      checkedKeys: [], //选中的节点
  }
}



methods:{
      // 获取选中的节点
    nodeClick(a, b) {
      this.checkedKeys = []
      for (var i = 0; i < b.checkedKeys.length; i++) {
        if (b.checkedKeys[i]) {
          this.checkedKeys.push(b.checkedKeys[i])
        }
      }
    },
}

【以下回答由 GPT 生成】

一种实现以上功能的方法是通过递归遍历树的节点,判断子节点的勾选状态来更新父节点的勾选状态。

具体的步骤如下:

  1. 首先,在el-tree的checkChange事件中,获取到当前已勾选节点的数组currentList和当前操作的节点对象currentNode。

  2. 接下来,我们需要遍历整个树的节点,首先可以写一个递归函数来实现:

function updateParentCheckedStatus(node) {
  // 如果节点没有子节点,直接返回
  if (!node.children) {
    return;
  }

  // 遍历该节点的子节点
  node.children.forEach(child => {
    // 如果子节点是勾选状态,则将父节点设置为勾选状态
    if (child.checked) {
      node.checked = true;
    }
    // 如果子节点是未勾选状态,则将父节点设置为未勾选状态
    if (!child.checked) {
      node.checked = false;
    }
    // 递归调用函数,继续遍历子节点
    updateParentCheckedStatus(child);
  });
}

// 遍历整个树,更新每个节点的勾选状态
updateParentCheckedStatus(treeData);
  1. 在上述代码中,我们首先判断节点是否有子节点,如果没有子节点,直接返回。然后遍历子节点,根据子节点的勾选状态来更新父节点的勾选状态。最后,递归调用函数,继续遍历子节点的子节点,以此类推。

  2. 最后,将更新后的树数据重新赋值给el-tree的data属性即可。

以上是一种实现思路,具体的实现代码可能需要根据你的具体业务逻辑和数据结构进行调整。如果有需要,请提供你的具体代码,我可以给出更为详细的解答。


如果你已经解决了该问题, 非常希望你能够分享一下解决方案, 写成博客, 将相关链接放在评论区, 以帮助更多的人 ^-^

<template>
  <el-tree
    :data="treeData"
    :props="treeProps"
    :check-strictly="true"
    @check-change="handleCheckChange"
  />
</template>

<script>
export default {
  data() {
    return {
      treeData: [
        {
          label: 'Node 1',
          children: [
            { label: 'Node 1-1' },
            { label: 'Node 1-2' }
          ]
        },
        {
          label: 'Node 2',
          children: [
            { label: 'Node 2-1' },
            { label: 'Node 2-2' }
          ]
        }
      ],
      treeProps: {
        children: 'children',
        label: 'label'
      }
    };
  },
  methods: {
    handleCheckChange(currentNode, checkedNodes) {
      // Update parent node's check state based on child nodes
      if (currentNode.children && currentNode.children.length > 0) {
        if (checkedNodes.includes(currentNode)) {
          this.checkNodeAndChildren(currentNode);
        } else {
          this.uncheckNodeAndChildren(currentNode);
        }
      }
      // Update child nodes based on parent node's check state
      else {
        if (currentNode.checked) {
          this.checkChildren(currentNode);
        } else {
          this.uncheckChildren(currentNode);
        }
      }
    },
    checkNodeAndChildren(node) {
      this.$refs.tree.setCheckedKeys([node.id], true);
      if (node.children) {
        node.children.forEach(child => this.checkNodeAndChildren(child));
      }
    },
    uncheckNodeAndChildren(node) {
      this.$refs.tree.setCheckedKeys([node.id], false);
      if (node.children) {
        node.children.forEach(child => this.uncheckNodeAndChildren(child));
      }
    },
    checkChildren(node) {
      if (node.children) {
        node.children.forEach(child => {
          this.$refs.tree.setChecked(child.id, true);
          this.checkChildren(child);
        });
      }
    },
    uncheckChildren(node) {
      if (node.children) {
        node.children.forEach(child => {
          this.$refs.tree.setChecked(child.id, false);
          this.uncheckChildren(child);
        });
      }
    }
  }
};
</script>

element-ui el-tree 子节点全不选时,设置父节点半选状态
角色分配菜单完成后,菜单管理将某父节点下的子节点全部删除,数据库中仅删除对应的子节点,而父节点没有删除,此时后台数据回填树会导致该父节点没有选中状态。

// data 为后台获取到的 key 数组
// 遍历data,筛选子节点和父节点
let checkTreeNode = []
let parentNodes = []
for (let item of data) {
    let node = this.$refs.tree.getNode(item)
    if (node && node.isLeaf) {
         checkTreeNode.push(item)
    } else if (node) {
        parentNodes.push(node)
    }
}
// 设置所有子节点选中,自动回填父节点
this.$refs.tree.setCheckedKeys(checkTreeNode) 
// 未回填的父节点单独设置
for (let node of parentNodes) {
    do {
        //应当有状态的父节点在未选中时设置为半选中状态 
        if (!node.checked && !node.indeterminate) {
            node.indeterminate = true
        }
        // node.indeterminate = true 仅对一个节点进行半选中状态设置,其父节点不能自动级联设置,所以这里循环设置级联父节点状态
        node = node.parent
    } while (node)
}