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);
}
相关推荐
sunbyte1 小时前
50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | ThemeClock(主题时钟)
前端·javascript·css·vue.js·前端框架·tailwindcss
浏览器API调用工程师_Taylor1 小时前
AOP魔法:一招实现登录弹窗的全局拦截与动态处理
前端·javascript·vue.js
一个水瓶座程序猿.2 小时前
Vue3 中使用 Vueuse
前端·javascript·vue.js
网络点点滴2 小时前
Vue如何处理数据、v-HTML的使用及总结
前端·vue.js·html
paopaokaka_luck3 小时前
基于SpringBoot+Vue的酒类仓储管理系统
数据库·vue.js·spring boot·后端·小程序
雲墨款哥4 小时前
Vue3.0 项目初始化及 Element Plus 配置实战
前端·vue.js
前端小饭桌4 小时前
关于 Vue 3 的 ref,这些细节你了解了多少?
前端·vue.js
光影少年4 小时前
vue3.0性能提升主要通过那几方面体现的?
前端·vue.js
江城开朗的豌豆4 小时前
路由守卫:你的Vue路由‘保安’,全局把关还是局部盯梢?
前端·javascript·vue.js
Jinxiansen02114 小时前
Vue 3 响应式核心源码详解(基于 @vue/reactivity)
前端·javascript·vue.js