element-plus el-tree 多选框回显问题修复

使用若依框架时发现了该bug,在菜单回显的时候,子节点没有全部选中,父节点也被默认全选了

/** 打开弹窗 */
const open = async (row: RoleApi.RoleVO) => {
  dialogVisible.value = true
  resetForm()
  // 加载 Menu 列表。注意,必须放在前面,不然下面 setChecked 没数据节点
  menuOptions.value = handleTree(await MenuApi.getSimpleMenusList())
  // 设置数据
  formData.id = row.id
  formData.name = row.name
  formData.code = row.code
  formLoading.value = true
  try {
    formData.value.menuIds = await PermissionApi.getRoleMenuList(row.id)
    // 设置选中
    formData.value.menuIds.forEach((menuId: number) => {
      treeRef.value.setChecked(menuId, true, false)
    })
  } finally {
    formLoading.value = false
  }
}

因为在保存时,会将父节点id子节点id一起保存,导致回显 都被默认选中了,

解决方案:

在加载时,从回显数据区去掉没有全选的父节点id,菜单并且存在多层级的情况,孙节点没有全部选中,则子节点、父节点都需要剔除;

/**
 * 清洗选中 ID,移除不合法的父节点
 * @param {Array} tree - 树结构 
 * @param {Array} selected - 后端返回的一维 ID 列表
 */
function cleanSelected(tree, selected) {
  const selectedSet = new Set(selected);
  const validSet = new Set();

  function dfs(node) {
    const hasChildren = node.children && node.children.length > 0;

    if (!hasChildren) {
      // 叶子节点
      if (selectedSet.has(node.id)) validSet.add(node.id);
      return { selected: selectedSet.has(node.id), allSelected: selectedSet.has(node.id) };
    }

    let childSelected = false;
    let childAllSelected = true;

    for (const child of node.children) {
      const res = dfs(child);
      childSelected = childSelected || res.selected;
      childAllSelected = childAllSelected && res.allSelected;
    }

    const selfSelected = selectedSet.has(node.id);

    // 情况 1:父节点选中但子节点没有全选 → 父节点无效
    if (selfSelected && !childAllSelected) {
      // 不加入 validSet
      return { selected: true, allSelected: false };
    }

    // 情况 2:父节点选中并且子节点全部选中 → 保留父节点,移除子节点
    if (selfSelected && childAllSelected) {
      validSet.add(node.id);
      // 删除所有子节点
      node.children.forEach(c => validSet.delete(c.id));
      return { selected: true, allSelected: true };
    }

    // 情况 3:父节点未选中,但子节点选中了 → 保留子节点
    return { selected: childSelected, allSelected: false };
  }

  tree.forEach(root => dfs(root));
  return [...validSet];
}
/** 打开弹窗 */
const open = async (row: RoleApi.RoleVO) => {
  dialogVisible.value = true
  resetForm()
  // 加载 Menu 列表。注意,必须放在前面,不然下面 setChecked 没数据节点
  menuOptions.value = handleTree(await MenuApi.getSimpleMenusList())
  // 设置数据
  formData.id = row.id
  formData.name = row.name
  formData.code = row.code
  formLoading.value = true
  try {
    formData.menuIds = await PermissionApi.getRoleMenuList(row.id)
    // 设置选中
    // 去掉所有半选节点ID
    const cleanMenuIds = cleanSelected(menuOptions.value, formData.menuIds)
    treeRef.value.setCheckedKeys(cleanMenuIds, false) // setCheckedNodes()
  } finally {
    formLoading.value = false
  }
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值