library-picturebook-activity/DYNAMIC_MENU_GUIDE.md

281 lines
6.5 KiB
Markdown
Raw Normal View History

2025-11-23 14:04:20 +08:00
# 动态菜单系统实现指南
## 📋 概述
已成功将菜单管理模块扩展为**完全动态化的菜单生成系统**。系统现在支持:
- ✅ 从数据库动态加载菜单
- ✅ 根据用户权限自动过滤菜单
- ✅ 动态生成路由配置
- ✅ 动态生成侧边栏菜单
- ✅ 支持多级菜单结构
- ✅ 菜单级别的权限控制
## 🏗️ 架构设计
### 后端架构
```
数据库 (Menu表)
MenusService.findUserMenus()
根据用户权限过滤菜单
返回树形菜单结构
```
### 前端架构
```
用户登录
获取用户菜单 (API)
存储到 Auth Store
转换为路由配置 (convertMenusToRoutes)
动态注册路由 (router.addRoute)
转换为菜单项 (convertMenusToMenuItems)
渲染到侧边栏
```
## 🔧 实现细节
### 1. 数据库 Schema 更新
`Menu` 模型中添加了 `permission` 字段:
```prisma
model Menu {
// ... 其他字段
permission String? /// 权限编码用于控制菜单显示menu:read
// ... 其他字段
}
```
**需要运行数据库迁移:**
```bash
cd backend
npx prisma migrate dev --name add_menu_permission
```
### 2. 后端 API
#### 新增接口:`GET /menus/user-menus`
获取当前用户的菜单(根据权限自动过滤)
**响应示例:**
```json
[
{
"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: 创建带权限的菜单
1. 进入"菜单管理"
2. 点击"新增菜单"
3. 填写:
- 名称: 用户管理
- 路径: `/system/users`
- 图标: `UserOutlined`
- 组件路径: `system/users/Index`
- 权限编码: `user:read`
4. 保存
结果:只有拥有 `user:read` 权限的用户才能看到此菜单。
### 场景2: 创建多级菜单
1. 创建父菜单:
- 名称: 系统管理
- 路径: `/system`
- 图标: `SettingOutlined`
- (不填组件路径和权限编码)
2. 创建子菜单:
- 名称: 用户管理
- 路径: `/system/users`
- 父菜单: 选择"系统管理"
- 组件路径: `system/users/Index`
结果:侧边栏显示为:
```
系统管理
└─ 用户管理
```
## ⚠️ 注意事项
1. **数据库迁移**: 添加 `permission` 字段后,需要运行 Prisma 迁移
2. **组件路径**: 确保组件文件存在,否则路由会失败
3. **权限编码**: 必须与权限系统中定义的权限码一致
4. **路由名称**: 自动生成,基于路径(如 `/system/users``SystemUsers`
5. **动态路由**: 只在登录后添加一次,刷新页面不会重复添加
## 🐛 故障排除
### 菜单不显示
1. 检查菜单的 `validState` 是否为 1有效
2. 检查用户是否有菜单对应的权限
3. 检查浏览器控制台是否有错误
### 路由404
1. 检查组件路径是否正确
2. 检查组件文件是否存在
3. 检查路由是否已动态注册(查看 Vue DevTools
### 图标不显示
1. 检查图标名称是否正确Ant Design Icons
2. 检查图标是否已导入到项目中
## 📚 相关文件
- 后端菜单服务: `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`
## 🚀 下一步优化建议
1. **菜单缓存**: 可以缓存菜单数据减少API调用
2. **菜单刷新**: 提供手动刷新菜单的功能
3. **菜单搜索**: 在侧边栏添加菜单搜索功能
4. **菜单收藏**: 允许用户收藏常用菜单
5. **菜单拖拽排序**: 在管理界面支持拖拽排序