dromara/plus-ui菜单配置系统深度解析
前言:企业级权限管理的核心枢纽
在企业级应用开发中,菜单权限管理往往是系统架构的核心难点。你是否曾遇到过这样的困境:
- 菜单层级复杂,难以维护清晰的权限结构
- 动态路由加载性能瓶颈,影响用户体验
- 多类型菜单(目录、菜单、按钮)统一管理困难
- 权限控制与菜单显示逻辑耦合度过高
dromara/plus-ui 的菜单配置系统正是为解决这些痛点而生,它提供了一套完整、灵活且高性能的菜单管理解决方案。本文将深入解析这套系统的设计理念、核心实现和最佳实践。
系统架构概览
菜单类型体系
dromara/plus-ui 采用三级菜单类型体系,每种类型都有明确的职责划分:
export enum MenuTypeEnum {
/**
* 目录 - 用于组织菜单的容器
*/
M = 'M',
/**
* 菜单 - 具体的功能页面
*/
C = 'C',
/**
* 按钮 - 页面内的操作权限
*/
F = 'F'
}
核心组件关系图
核心实现机制
1. 动态路由加载系统
系统采用异步路由加载机制,根据用户权限动态生成路由结构:
// 权限存储管理
export const usePermissionStore = defineStore('permission', () => {
const routes = ref<RouteRecordRaw[]>([]);
const addRoutes = ref<RouteRecordRaw[]>([]);
const defaultRoutes = ref<RouteRecordRaw[]>([]);
const topbarRouters = ref<RouteRecordRaw[]>([]);
const sidebarRouters = ref<RouteRecordRaw[]>([]);
// 生成动态路由
const generateRoutes = async (): Promise<RouteRecordRaw[]> => {
const res = await getRouters();
const { data } = res;
const sdata = JSON.parse(JSON.stringify(data));
const rdata = JSON.parse(JSON.stringify(data));
const defaultData = JSON.parse(JSON.stringify(data));
const sidebarRoutes = filterAsyncRouter(sdata);
const rewriteRoutes = filterAsyncRouter(rdata, undefined, true);
const defaultRoutes = filterAsyncRouter(defaultData);
setRoutes(rewriteRoutes);
setSidebarRouters(constantRoutes.concat(sidebarRoutes));
setDefaultRoutes(sidebarRoutes);
setTopbarRoutes(defaultRoutes);
return new Promise<RouteRecordRaw[]>((resolve) => resolve(rewriteRoutes));
};
});
2. 路由过滤与组件映射
系统通过智能的路由过滤机制,将后端返回的菜单数据转换为前端可用的路由配置:
const filterAsyncRouter = (asyncRouterMap: RouteRecordRaw[], lastRouter?: RouteRecordRaw, type = false): RouteRecordRaw[] => {
return asyncRouterMap.filter((route) => {
// Layout组件特殊处理
if (route.component?.toString() === 'Layout') {
route.component = Layout;
} else if (route.component?.toString() === 'ParentView') {
route.component = ParentView;
} else if (route.component?.toString() === 'InnerLink') {
route.component = InnerLink;
} else {
route.component = loadView(route.component, route.name as string);
}
// 递归处理子路由
if (route.children != null && route.children && route.children.length) {
route.children = filterAsyncRouter(route.children, route, type);
} else {
delete route.children;
delete route.redirect;
}
return true;
});
};
3. 视图组件动态加载
系统支持按需加载视图组件,显著提升应用性能:
export const loadView = (view: any, name: string) => {
let res;
for (const path in modules) {
const viewsIndex = path.indexOf('/views/');
let dir = path.substring(viewsIndex + 7);
dir = dir.substring(0, dir.lastIndexOf('.vue'));
if (dir === view) {
res = createCustomNameComponent(modules[path], { name });
return res;
}
}
return res;
};
菜单配置详解
菜单属性配置表
| 属性名 | 类型 | 必填 | 说明 | 示例 |
|---|---|---|---|---|
| menuName | string | 是 | 菜单显示名称 | 用户管理 |
| menuType | MenuTypeEnum | 是 | 菜单类型(M/C/F) | C |
| parentId | number | 是 | 父菜单ID | 0 |
| path | string | 是 | 路由路径 | system/user |
| component | string | 条件必填 | 组件路径 | system/user/index |
| icon | string | 否 | 图标名称 | user |
| orderNum | number | 是 | 显示顺序 | 1 |
| isFrame | string | 否 | 是否外链(0/1) | 1 |
| isCache | string | 否 | 是否缓存(0/1) | 0 |
| visible | string | 否 | 显示状态(0/1) | 0 |
| status | string | 否 | 启用状态(0/1) | 0 |
| perms | string | 否 | 权限标识 | system:user:list |
路由配置规则说明
权限控制机制
1. 路由级别权限控制
系统支持基于角色和权限的双重控制机制:
export const filterDynamicRoutes = (routes: RouteRecordRaw[]) => {
const res: RouteRecordRaw[] = [];
routes.forEach((route) => {
if (route.permissions) {
if (auth.hasPermiOr(route.permissions)) {
res.push(route);
}
} else if (route.roles) {
if (auth.hasRoleOr(route.roles)) {
res.push(route);
}
}
});
return res;
};
2. 组件级别权限控制
通过自定义指令实现细粒度的权限控制:
<template>
<el-button v-hasPermi="['system:user:add']" @click="handleAdd">
新增用户
</el-button>
</template>
高级功能特性
1. 级联删除机制
系统提供安全的级联删除功能,防止误操作导致的数据不一致:
const handleCascadeDelete = () => {
menuTreeRef.value?.setCheckedKeys([]);
getTreeselect();
deleteDialog.visible = true;
};
const submitDeleteForm = async () => {
const menuIds = menuTreeRef.value?.getCheckedKeys();
if (menuIds.length < 0) {
proxy?.$modal.msgWarning('请选择要删除的菜单');
return;
}
await cascadeDelMenu(menuIds);
};
2. 懒加载树形结构
优化大型菜单树的性能表现:
const getChildrenList = async (row: any, treeNode: unknown, resolve: (data: any[]) => void) => {
menuExpandMap.value[row.menuId] = { row, treeNode, resolve };
const children = menuChildrenListMap.value[row.menuId] || [];
resolve(children);
};
3. 路由重复检查
防止路由配置冲突导致的404问题:
function duplicateRouteChecker(localRoutes: Route[], routes: Route[]) {
const allRoutes = flatRoutes([...localRoutes, ...routes]);
const nameList: string[] = [];
allRoutes.forEach((route) => {
const name = route.name.toString();
if (name && nameList.includes(name)) {
console.error(`路由名称: [${name}] 重复, 会造成 404`);
return;
}
nameList.push(route.name.toString());
});
}
最佳实践指南
1. 菜单命名规范
| 菜单类型 | 命名规范 | 示例 |
|---|---|---|
| 目录 | 使用名词,描述功能模块 | 系统管理 |
| 菜单 | 动词+名词,描述具体操作 | 用户列表 |
| 按钮 | 动词,描述操作动作 | 新增、编辑、删除 |
2. 权限标识设计原则
3. 性能优化建议
- 分页加载:当菜单数量超过1000时,建议启用分页加载
- 图标优化:使用SVG图标,避免字体图标带来的性能开销
- 缓存策略:合理使用路由缓存,提升重复访问性能
- 懒加载:对深层级菜单使用懒加载机制
常见问题解决方案
问题1:菜单显示异常
症状:菜单在侧边栏不显示或显示错乱
解决方案:
// 检查菜单配置
const checkMenuConfig = (menu: MenuForm) => {
if (menu.menuType === 'C' && !menu.component) {
throw new Error('菜单类型必须配置组件路径');
}
if (menu.visible === '1' && menu.status === '0') {
console.warn('菜单已隐藏但状态为启用,可能无法正常显示');
}
};
问题2:权限控制失效
症状:用户可以看到但没有权限操作的菜单
解决方案:
// 双重权限验证
const validatePermission = (route: RouteRecordRaw) => {
const hasRoutePermission = !route.permissions || auth.hasPermiOr(route.permissions);
const hasRolePermission = !route.roles || auth.hasRoleOr(route.roles);
return hasRoutePermission && hasRolePermission;
};
问题3:路由重复冲突
症状:页面出现404错误或路由跳转异常
解决方案:
// 路由名称唯一性检查
const ensureUniqueRouteNames = (routes: RouteRecordRaw[]) => {
const nameSet = new Set();
routes.forEach(route => {
if (route.name && nameSet.has(route.name)) {
console.error(`路由名称重复: ${route.name}`);
}
nameSet.add(route.name);
});
};
总结与展望
dromara/plus-ui 的菜单配置系统通过精心的架构设计和丰富的功能特性,为企业级应用提供了强大的权限管理能力。系统的主要优势包括:
- 灵活的权限控制:支持角色和权限双重验证机制
- 高性能动态加载:异步路由加载,优化用户体验
- 完整的类型支持:目录、菜单、按钮三级权限体系
- 安全的数据管理:级联删除和重复检查机制
- 易用的管理界面:直观的可视化配置界面
未来,该系统还可以进一步扩展以下功能:
- 菜单版本管理和回滚功能
- 菜单使用情况统计分析
- 多语言菜单支持
- 菜单导入导出模板
通过深入理解和合理运用这套菜单配置系统,开发者可以构建出更加安全、稳定、易用的企业级应用系统。
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



