问题描述:
1、el-tree 勾选反显,原本整个父子节点都勾选状态,新增一个子节点,因为父节点是全选状态,所以新增的子节点也呈勾选状态,实际是未勾选状态。
2、用的是default-checked-keys 回显
数据更新后,必须在 $nextTick 中调用 setCheckedKeys,这样才能确保树组件正确回显勾选状态。
解决思路:
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 回显不生效 | default-checked-keys 非响应式 | 使用 setCheckedKeys |
| 设置时机不对 | DOM 未渲染完成 | 包裹在$nextTick中 |
| 传了父节点 id | 父节点会强制全选,并级联勾选所有子节点 | 只传叶子节点 id 给 setCheckedKeys |
- 核心逻辑
- 先处理子节点,再根据子节点状态回推父节点
- 只传叶子节点 id 给 setCheckedKeys,父节点状态由组件自动计算
markTreeChecked返回的是树数据本身,不会影响setCheckedKeys的行为
这样即使后端返回的数据中新增了子节点,父节点也能正确呈现半选状态。
html
<el-tree:data="list"show-checkbox node-key="id"ref="tree"default-expand-all:props="{label: 'name',value: 'id'}"></el-tree>获取数据及回显调用(created 或 mounted)
// 获取树的数据getMenuTree().then(res=>{this.list=res.data.data;// 获取已勾选的数据idreturngetEchoData();}).then(res=>{// 处理回显letmenuIds=[];if(res.data.data.menuIds&&res.data.data.menuIds.length>0){menuIds=res.data.data.menuIds.split(',');}// 根据返回的 menuIds 处理树数据,标记每个节点的选中状态(用于界面展示)this.markTreeChecked(this.list,menuIds);this.$nextTick(()=>{if(this.$refs.tree){// 只传叶子节点 id,避免父节点强制全选constleafIds=this.filterLeafIds(this.list);this.$refs.tree.setCheckedKeys(leafIds);}});});methods
// 递归标记树节点的选中状态markTreeChecked(treeData,checkedIds){treeData.forEach(node=>{if(node.children&&node.children.length>0){// 先处理子节点constchildCheckedCount=this.markTreeChecked(node.children,checkedIds);// 根据子节点选中数量,计算当前父节点状态constallChecked=childCheckedCount===node.children.length;constnoneChecked=childCheckedCount===0;node.checked=allChecked;node.halfChecked=!allChecked&&!noneChecked;}else{// 叶子节点:直接判断是否在选中列表中node.checked=checkedIds.includes(node.id);}});}// 收集所有选中的叶子节点 idfilterLeafIds(treeData){constleafIds=[];consttraverse=(nodes)=>{nodes.forEach(node=>{if(node.children&&node.children.length>0){traverse(node.children);}elseif(node.checked){leafIds.push(node.id);}});};traverse(treeData);returnleafIds;}