tree回显问题

问题描述:

1、el-tree 勾选反显,原本整个父子节点都勾选状态,新增一个子节点,因为父节点是全选状态,所以新增的子节点也呈勾选状态,实际是未勾选状态。

2、用的是default-checked-keys 回显

数据更新后,必须在 $nextTick 中调用 setCheckedKeys,这样才能确保树组件正确回显勾选状态。

解决思路:

问题 原因 解决方案
回显不生效 default-checked-keys 非响应式 使用 setCheckedKeys
设置时机不对 DOM 未渲染完成 包裹在 $nextTick
传了父节点 id 父节点会强制全选,并级联勾选所有子节点 只传叶子节点 id 给 setCheckedKeys
  • 核心逻辑
  • 先处理子节点,再根据子节点状态回推父节点
  • 只传叶子节点 id 给 setCheckedKeys,父节点状态由组件自动计算
  • markTreeChecked 返回的是树数据本身,不会影响 setCheckedKeys 的行为
    这样即使后端返回的数据中新增了子节点,父节点也能正确呈现半选状态。

html

javascript 复制代码
<el-tree
	:data="list"
	show-checkbox
	node-key="id"
	ref="tree"
	default-expand-all
	:props="{label: 'name',value: 'id'}"
></el-tree>

获取数据及回显调用(created 或 mounted)

javascript 复制代码
// 获取树的数据
getMenuTree()
  .then(res => {
    this.list = res.data.data;
    // 获取已勾选的数据id
    return getEchoData();
  })
  .then(res => {
    // 处理回显
    let menuIds = [];
    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,避免父节点强制全选
        const leafIds = this.filterLeafIds(this.list);
        this.$refs.tree.setCheckedKeys(leafIds);
      }
    });
  });

methods

javascript 复制代码
// 递归标记树节点的选中状态
markTreeChecked(treeData, checkedIds) {
  treeData.forEach(node => {
    if (node.children && node.children.length > 0) {
      // 先处理子节点
      const childCheckedCount = this.markTreeChecked(node.children, checkedIds);
      
      // 根据子节点选中数量,计算当前父节点状态
      const allChecked = childCheckedCount === node.children.length;
      const noneChecked = childCheckedCount === 0;
      
      node.checked = allChecked;
      node.halfChecked = !allChecked && !noneChecked;
    } else {
      // 叶子节点:直接判断是否在选中列表中
      node.checked = checkedIds.includes(node.id);
    }
  });
}

// 收集所有选中的叶子节点 id
filterLeafIds(treeData) {
  const leafIds = [];
  
  const traverse = (nodes) => {
    nodes.forEach(node => {
      if (node.children && node.children.length > 0) {
        traverse(node.children);
      } else if (node.checked) {
        leafIds.push(node.id);
      }
    });
  };
  
  traverse(treeData);
  return leafIds;
}
相关推荐
kyriewen2 小时前
面试官让我用 AI 重构一个 8 年陈的 React 组件——他说他不看代码,只看我会不会拆
前端·javascript·面试
Jacky19993 小时前
Electron + Vue 3 桌面打字游戏实战:从 VSCode 扩展到独立应用的架构改造
javascript
用户50093768390393 小时前
热敏小票 Web 打印:我在 58/80mm 上折腾的两周
前端·vue.js
柚yuzumi4 小时前
别再猜 this:先看它属于谁,再看它指向谁
前端·javascript
汉堡大王95274 小时前
面试必考:手写代码 new 做了什么?从原理到实现全解析
前端·javascript·面试
BillKu5 小时前
TypeScript中,字符串字面量联合类型(Union Type)、enum的用法说明
前端·javascript·typescript
顶级自由人7 小时前
本地正常、线上正常,为什么一个 Hook 仍会报错?
前端·javascript·程序员
lerhxx7 小时前
R3F 第一人称漫游与碰撞检测:Pointer Lock + 不穿墙的滑墙秘诀(中)
前端·javascript·three.js
labixiong7 小时前
button按钮原生开关弹窗,零 JS 搞定80%交互场景
前端·javascript·html
隔岸观火烧连营7 小时前
如何用 WebCodecs 在浏览器里实现高清录屏 —— 无插件、无水印、直接导出 MP4
前端·javascript