6.5 KiB
6.5 KiB
动态菜单系统实现指南
📋 概述
已成功将菜单管理模块扩展为完全动态化的菜单生成系统。系统现在支持:
- ✅ 从数据库动态加载菜单
- ✅ 根据用户权限自动过滤菜单
- ✅ 动态生成路由配置
- ✅ 动态生成侧边栏菜单
- ✅ 支持多级菜单结构
- ✅ 菜单级别的权限控制
🏗️ 架构设计
后端架构
数据库 (Menu表)
↓
MenusService.findUserMenus()
↓
根据用户权限过滤菜单
↓
返回树形菜单结构
前端架构
用户登录
↓
获取用户菜单 (API)
↓
存储到 Auth Store
↓
转换为路由配置 (convertMenusToRoutes)
↓
动态注册路由 (router.addRoute)
↓
转换为菜单项 (convertMenusToMenuItems)
↓
渲染到侧边栏
🔧 实现细节
1. 数据库 Schema 更新
在 Menu 模型中添加了 permission 字段:
model Menu {
// ... 其他字段
permission String? /// 权限编码(用于控制菜单显示,如:menu:read)
// ... 其他字段
}
需要运行数据库迁移:
cd backend
npx prisma migrate dev --name add_menu_permission
2. 后端 API
新增接口:GET /menus/user-menus
获取当前用户的菜单(根据权限自动过滤)
响应示例:
[
{
"id": 1,
"name": "系统管理",
"path": "/system",
"icon": "SettingOutlined",
"permission": null,
"children": [
{
"id": 2,
"name": "用户管理",
"path": "/system/users",
"component": "system/users/Index",
"permission": "user:read"
}
]
}
]
3. 前端工具函数
创建了 frontend/src/utils/menu.ts,包含:
convertMenusToMenuItems(): 将数据库菜单转换为 Ant Design Vue Menu 格式convertMenusToRoutes(): 将数据库菜单转换为 Vue Router 路由配置getIconComponent(): 动态加载图标组件flattenMenus(): 扁平化菜单树
4. Auth Store 扩展
在 auth.ts 中添加了:
menus: 存储用户菜单数据fetchUserMenus(): 获取用户菜单- 登录时自动获取菜单
- 登出时清空菜单
5. 路由动态注册
在 router/index.ts 中:
- 基础路由(登录页、404等)保持静态
- 业务路由从数据库动态加载
- 登录后自动注册动态路由
- 支持路由权限检查
6. 布局组件更新
BasicLayout.vue 现在:
- 从
authStore.menus读取菜单数据 - 使用
convertMenusToMenuItems()转换菜单 - 自动展开包含当前路径的父菜单
📝 使用方法
1. 创建菜单
在菜单管理界面创建菜单时,需要填写:
- 菜单名称: 显示名称
- 路由路径: 如
/system/users - 图标: Ant Design Icons 名称,如
UserOutlined - 组件路径: Vue 组件路径,如
system/users/Index(相对于@/views/) - 权限编码: 可选,如
user:read(留空则所有用户可见) - 父菜单: 可选,用于创建多级菜单
- 排序: 数字,控制显示顺序
2. 权限控制
菜单级别权限
在菜单的"权限编码"字段设置权限码,如:
user:read- 需要用户查看权限role:create- 需要角色创建权限- 留空 - 所有用户可见
路由级别权限
菜单的权限会自动添加到路由的 meta.permissions 中,路由守卫会自动检查。
3. 组件路径规则
组件路径应该相对于 frontend/src/views/ 目录:
- ✅
system/users/Index→@/views/system/users/Index.vue - ✅
dashboard/Index→@/views/dashboard/Index.vue - ❌
@/views/system/users/Index(不需要@/views/前缀)
🔄 工作流程
用户登录流程
1. 用户输入账号密码
↓
2. 调用 login API
↓
3. 获取 token 和用户信息
↓
4. 调用 fetchUserMenus() 获取菜单
↓
5. 菜单数据存储到 authStore.menus
↓
6. 动态路由注册 (addDynamicRoutes)
↓
7. 跳转到首页
页面访问流程
1. 用户访问 /system/users
↓
2. 路由守卫检查认证
↓
3. 检查路由权限 (meta.permissions)
↓
4. 如果通过,渲染组件
↓
5. BasicLayout 显示侧边栏菜单
🎯 示例场景
场景1: 创建带权限的菜单
- 进入"菜单管理"
- 点击"新增菜单"
- 填写:
- 名称: 用户管理
- 路径:
/system/users - 图标:
UserOutlined - 组件路径:
system/users/Index - 权限编码:
user:read
- 保存
结果:只有拥有 user:read 权限的用户才能看到此菜单。
场景2: 创建多级菜单
-
创建父菜单:
- 名称: 系统管理
- 路径:
/system - 图标:
SettingOutlined - (不填组件路径和权限编码)
-
创建子菜单:
- 名称: 用户管理
- 路径:
/system/users - 父菜单: 选择"系统管理"
- 组件路径:
system/users/Index
结果:侧边栏显示为:
系统管理
└─ 用户管理
⚠️ 注意事项
- 数据库迁移: 添加
permission字段后,需要运行 Prisma 迁移 - 组件路径: 确保组件文件存在,否则路由会失败
- 权限编码: 必须与权限系统中定义的权限码一致
- 路由名称: 自动生成,基于路径(如
/system/users→SystemUsers) - 动态路由: 只在登录后添加一次,刷新页面不会重复添加
🐛 故障排除
菜单不显示
- 检查菜单的
validState是否为 1(有效) - 检查用户是否有菜单对应的权限
- 检查浏览器控制台是否有错误
路由404
- 检查组件路径是否正确
- 检查组件文件是否存在
- 检查路由是否已动态注册(查看 Vue DevTools)
图标不显示
- 检查图标名称是否正确(Ant Design Icons)
- 检查图标是否已导入到项目中
📚 相关文件
- 后端菜单服务:
backend/src/menus/menus.service.ts - 后端菜单控制器:
backend/src/menus/menus.controller.ts - 前端菜单工具:
frontend/src/utils/menu.ts - 前端路由配置:
frontend/src/router/index.ts - 前端布局组件:
frontend/src/layouts/BasicLayout.vue - 前端菜单管理:
frontend/src/views/system/menus/Index.vue
🚀 下一步优化建议
- 菜单缓存: 可以缓存菜单数据,减少API调用
- 菜单刷新: 提供手动刷新菜单的功能
- 菜单搜索: 在侧边栏添加菜单搜索功能
- 菜单收藏: 允许用户收藏常用菜单
- 菜单拖拽排序: 在管理界面支持拖拽排序