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);
}
相关推荐
LuckySusu2 小时前
【vue篇】Vue 性能优化全景图:从编码到部署的优化策略
前端·vue.js
LuckySusu2 小时前
【vue篇】SSR 深度解析:服务端渲染的“利”与“弊”
前端·vue.js
LuckySusu2 小时前
【vue篇】SPA 单页面应用:现代 Web 的革命与挑战
前端·vue.js
LuckySusu2 小时前
【vue篇】Vue 初始化页面闪动(FOUC)问题终极解决方案
前端·vue.js
LuckySusu2 小时前
【vue篇】技术分析:Template 与 JSX 的本质区别与选型指南
前端·vue.js
今天头发还在吗3 小时前
【框架演进】Vue与React的跨越性变革:从Vue2到Vue3,从Class到Hooks
javascript·vue.js·react.js
铅笔侠_小龙虾3 小时前
深入理解 Vue.js 原理
前端·javascript·vue.js
你的眼睛會笑3 小时前
vue3 使用html2canvas实现网页截图并下载功能 以及问题处理
前端·javascript·vue.js
无光末阳4 小时前
vue 环境下多个定时器的创建与暂停的统一封装
前端·vue.js
慧一居士4 小时前
src/App.vue 和 public/index.html 关系和区别
前端·vue.js