# 动态菜单系统实现指南

## 📋 概述

已成功将菜单管理模块扩展为**完全动态化的菜单生成系统**。系统现在支持：

- ✅ 从数据库动态加载菜单
- ✅ 根据用户权限自动过滤菜单
- ✅ 动态生成路由配置
- ✅ 动态生成侧边栏菜单
- ✅ 支持多级菜单结构
- ✅ 菜单级别的权限控制

## 🏗️ 架构设计

### 后端架构

```
数据库 (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. **菜单拖拽排序**: 在管理界面支持拖拽排序
