Vue3+Ts+Element Plus 权限菜单控制节点

实现了完整的父子节点联动逻辑功能:

  1. 选择父节点时自动选中所有子节点
  2. 取消父节点时自动取消所有子节点
  3. 选择子节点时自动选中所有父节点
  4. 取消子节点时保留父节点的勾选状态

如图效果:

代码部分:

复制代码
<template>
  <div>
    <el-drawer
      v-model="dialogVisible"
      title="菜单权限设置 - 静态案例"
      size="400"
      @close="handleClose"
      class="pl-8 pr-8"
    >
      <div class="mb-4 text-sm text-gray-500">
        这是一个静态案例,展示父子节点联动选择功能
      </div>
      <el-tree
        ref="treeRef"
        :data="menuData"
        highlight-current
        show-checkbox
        :check-strictly="true"
        node-key="id"
        :default-checked-keys="defaultCheckedKeys"
        :props="defaultProps"
        :default-expand-all="true"
        @check="hanleCheck"
        @check-change="checkChange"
      />
      <template #footer>
        <div class="dialog-footer">
          <el-button @click="handleClose">取消</el-button>
          <el-button type="primary" @click="submitForm">确认</el-button>
        </div>
      </template>
    </el-drawer>
  </div>
</template>
逻辑部分:
复制代码
<script lang="ts" setup>
import { ElMessage } from "element-plus";
import { ref } from "vue";

// 类型定义
interface MenuItem {
  id: number;
  title: string;
  children?: MenuItem[];
}

interface FormInfo {
  menuIds: number[];
}

interface RowList {
  id?: number;
  menus_ids?: number[];
}

// 静态菜单数据
const menuData: MenuItem[] = [
  {
    id: 1,
    title: "系统管理",
    children: [
      { id: 11, title: "用户管理" },
      { id: 12, title: "角色管理" },
      { id: 13, title: "权限设置" }
    ]
  },
  {
    id: 2,
    title: "商品管理",
    children: [
      { id: 21, title: "商品列表" },
      { id: 22, title: "商品分类" },
      { id: 23, title: "库存管理" }
    ]
  },
  {
    id: 3,
    title: "订单管理",
    children: [
      { id: 31, title: "订单列表" },
      { id: 32, title: "退款管理" },
      { id: 33, title: "物流跟踪" }
    ]
  },
  {
    id: 4,
    title: "数据统计",
    children: [
      { id: 41, title: "销售统计" },
      { id: 42, title: "用户分析" },
      { id: 43, title: "商品分析" }
    ]
  }
];

// 默认选中的菜单ID
const defaultCheckedKeys = [11, 21, 31];

// 响应式数据
const dialogVisible = ref<boolean>(true);
const treeRef = ref();
const selectedMenuIds = ref<number[]>(defaultCheckedKeys);

const defaultProps = {
  children: "children",
  label: "title",
};

// 递归设置子节点的选择状态
const setChildrenChecked = (children: any[], isChecked: boolean) => {
  children.forEach((child: any) => {
    treeRef.value!.setChecked(child.id, isChecked, false);
    if (child.children && child.children.length > 0) {
      setChildrenChecked(child.children, isChecked);
    }
  });
};

// 递归选中所有父节点
const setParentChecked = (node: any) => {
  if (!node.parent) return;
  
  // 选中父节点
  treeRef.value!.setChecked(node.parent, true, false);
  
  // 继续递归选中父节点的父节点
  setParentChecked(node.parent);
};

// 处理节点选择
const hanleCheck = (data: MenuItem) => {
  const isChecked = treeRef.value!.getNode(data).checked;
  const currentNode = treeRef.value!.getNode(data);
  
  if (isChecked) {
    setParentChecked(currentNode);
    if (data.children?.length) {
      setChildrenChecked(data.children, true);
    }
  } else {
    if (data.children?.length && !currentNode.checked) {
      setChildrenChecked(data.children, false);
    }
  }
  
  updateSelectedMenuIds();
};

const checkChange = () => {
  updateSelectedMenuIds();
};

const updateSelectedMenuIds = () => {
  const checkedNodes = treeRef.value!.getCheckedNodes(false, false);
  selectedMenuIds.value = checkedNodes.map(node => node.id);
};

const handleClose = () => {
  dialogVisible.value = false;
  ElMessage.info("已关闭菜单权限设置");
};

const submitForm = () => {
  updateSelectedMenuIds();
  ElMessage.success(`权限设置成功!选中的菜单ID: [${selectedMenuIds.value.join(', ')}]`);
  console.log("选中的菜单ID:", selectedMenuIds.value);
};
</script>

<style scoped>
.dialog-footer {
  display: flex;
  justify-content: flex-end;
  gap: 12px;
  margin-top: 16px;
}
</style>
相关推荐
全栈前端老曹5 分钟前
【前端路由】React Router 权限路由控制 - 登录验证、私有路由封装、高阶组件实现路由守卫
前端·javascript·react.js·前端框架·react-router·前端路由·权限路由
zhuà!26 分钟前
uv-picker在页面初始化时,设置初始值无效
前端·javascript·uv
Amumu1213827 分钟前
React应用
前端·react.js·前端框架
摸鱼的春哥30 分钟前
实战:在 Docker (Windows) 中构建集成 yt-dlp 的“满血版” n8n 自动化工作流
前端·javascript·后端
_Rookie._32 分钟前
关于迭代协议:可迭代协议和迭代器协议,生成器函数 生成器对象的理解
javascript·python
幽络源小助理35 分钟前
Springboot机场乘客服务系统源码 – SpringBoot+Vue项目免费下载 | 幽络源
vue.js·spring boot·后端
小酒星小杜36 分钟前
在AI时代,技术人应该每天都要花两小时来构建一个自身的构建系统
前端·vue.js·架构
测试游记37 分钟前
基于 FastGPT 的 LangChain.js + RAG 系统实现
开发语言·前端·javascript·langchain·ecmascript
阿奇__38 分钟前
elementUI table 多列排序并保持状态样式显示正确(无需修改源码)
前端·vue.js·elementui
Van_captain41 分钟前
rn_for_openharmony常用组件_Empty空状态
javascript·开源·harmonyos