ElementUI Tree组件的父子节点联动实现

实现效果分析

该权限树组件实现了以下的关键功能

  • 从后端获取角色权限并初始化树的选择状态
  • 父子节点联动选择
    • 父节点全选子节点也全选,父节点不选子节点也全不选,
    • 子节点选中一个父节点就要选中,子节点全都不选父节点可以单独选中
  • 获取全选和半选节点的权限码集合

核心代码解析

1.Tree组件模版
html 复制代码
<el-tree
  ref="preTreeRef"
  :data="permissionTree"
  show-checkbox
  node-key="code"
  highlight-current
  :expand-on-click-node="false"
  :props="defaultPermissionProps"
  :check-strictly="checkStrictly"
  @check="handleCheckChange"
>
</el-tree>

关键属性解析

  • node-key="code":使用权限码作为节点的唯一标识
  • :check-strictly="checkStrictly":禁用Element UI自带的父子关联,改为手动控制
  • @check="handleCheckChange":节点选择状态变化时触发自定义事件
2.初始化角色权限
js 复制代码
async getQueryRolePermission(id) {
  const roleId = id || this.addForm.roleId;
  const res = await queryRolePermissionCodes({ roleId });
  
  if (+res.code !== 200) {
    this.$message.error(res.msg || '请求失败');
    return;
  }
  
  const { permissionCodes } = res.data || [];
  const permissionCodesList = permissionCodes?.split(',') || [];
  
  this.$nextTick(() => {
    this.$refs.preTreeRef.setCheckedKeys(permissionCodesList, false);
    this.permissionCodes = permissionCodesList;
  });
}

关键属性解析

  • 使用$nextTick确保DOM更新完成后再操作树组件
  • setCheckedKeys第二个参数设为false避免触发check事件
  • 直接将权限码数组设置为选中状态
3.节点选择事件处理
js 复制代码
handleCheckChange(node, checkedStatus) {
  if (this.isHandling) return;
  this.isHandling = true;
  
  const checked = checkedStatus.checkedKeys.includes(node.code);
  
  if (checked) {
    // 选中节点时处理子节点和父节点
    if (node.children?.length) {
      this.checkAllChildren(node, true);
    }
    this.checkAllParents(node);
  } else {
    // 取消节点时处理子节点
    if (node.children?.length) {
      this.checkAllChildren(node, false);
    }
  }
  
  this.$nextTick(() => (this.isHandling = false));
  
  // 收集权限码
  const childCode = this.$refs?.preTreeRef?.getCheckedKeys() || [];
  const parentCode = this.$refs?.preTreeRef?.getHalfCheckedKeys() || [];
  this.permissionCodes = childCode.concat(parentCode);
}

逻辑解析

  • 使用isHandling标志位防止递归操作导致的死循环
  • 当节点被选中时:
    • 递归选中所有子节点
    • 递归向上选中所有父节点
  • 当节点被取消选中时:
    • 递归取消所有子节点
  • 收集全选和半选节点的权限码
4.递归操作子节点
js 复制代码
checkAllChildren(node, checked) {
  node.children.forEach(child => {
    this.$refs.preTreeRef.setChecked(child.code, checked);
    if (child.children) this.checkAllChildren(child, checked);
  });
}

逻辑解析

  • 递归操作子节点,使用setChecked方法设置每个节点的选中状态
5.递归操作父节点
js 复制代码
checkAllParents(node) {
  let parent = this.getParentNode(node);
  while (parent) {
    this.$refs.preTreeRef.setChecked(parent.code, true);
    parent = this.getParentNode(parent);
  }
}

逻辑解析

  • 从当前节点开始向上遍历父节点链
  • 将路径上的所有父节点设置为选中状态
  • 使用while循环代替递归,避免栈溢出风险
6. 父节点查找算法,返回目标节点的直接父节点
js 复制代码
getParentNode(node) {
  const findParent = (data, targetCode, parent) => {
    for (const item of data) {
      if (item.code === targetCode) return parent;
      if (item.children) {
        const found = findParent(item.children, targetCode, item);
        if (found) return found;
      }
    }
    return null;
  }
  return findParent(this.permissionTree, node.code, null);
}
相关推荐
bestlanzi9 小时前
使用nvm管理node环境
前端·vue.js·npm
weixin_4713830311 小时前
由浅入深递归练习
前端·javascript·vue.js
喵个咪13 小时前
Headless 后端实践:基于Go的企业级多栈管理系统脚手架
前端·vue.js·react.js
柚子科技16 小时前
Vue3 响应式原理:我被 ref 和 reactive 坑了3次后终于搞懂了
前端·javascript·vue.js
大鱼前端16 小时前
Veaury:让Vue和React组件在同一应用中共存的神器
前端·vue.js·react.js
五月君_16 小时前
继 React、Vue 之后,Three.js 也有 Skills 了!AI 写 3D 终于不“晕”了
javascript·vue.js·人工智能·react.js·3d
蜡台18 小时前
Vue2 使用 typescript 教程
前端·vue.js·typescript
i220818 Faiz Ul19 小时前
在线预约导游|基于SSM+vue的在线预约导游系统(源码+数据库+文档)
java·前端·数据库·vue.js·spring boot·毕设·在线预约导游系统
MaCa .BaKa20 小时前
56-非遗手工艺品定制平台系统
java·vue.js·spring boot·mysql·maven·非遗手工制作平台系统·非遗制作