198 lines
4.3 KiB
Markdown
198 lines
4.3 KiB
Markdown
![]() |
# 动态菜单路由系统
|
|||
|
|
|||
|
## 概述
|
|||
|
|
|||
|
本系统实现了基于后端接口的动态菜单路由加载功能,支持从后端获取菜单配置并自动生成前端路由。
|
|||
|
|
|||
|
## 接口说明
|
|||
|
|
|||
|
### 获取首页菜单
|
|||
|
- **接口**: `/aiol/aiolMenu/getIndexMenus`
|
|||
|
- **方法**: GET
|
|||
|
- **返回格式**:
|
|||
|
```json
|
|||
|
{
|
|||
|
"success": true,
|
|||
|
"message": "",
|
|||
|
"code": 200,
|
|||
|
"result": [
|
|||
|
{
|
|||
|
"id": "1",
|
|||
|
"name": "首页",
|
|||
|
"path": "/",
|
|||
|
"type": "index",
|
|||
|
"icon": null,
|
|||
|
"parentId": null,
|
|||
|
"sortOrder": 99,
|
|||
|
"izVisible": 1,
|
|||
|
"permissionKey": null
|
|||
|
}
|
|||
|
],
|
|||
|
"timestamp": 1759134778382
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
### 获取学生菜单
|
|||
|
- **接口**: `/aiol/aiolMenu/getStudentMenus`
|
|||
|
- **方法**: GET
|
|||
|
- **返回格式**: 同上,type为"student"
|
|||
|
|
|||
|
## 核心文件
|
|||
|
|
|||
|
### 1. API模块 (`src/api/modules/menu.ts`)
|
|||
|
```typescript
|
|||
|
export interface MenuItem {
|
|||
|
id: string
|
|||
|
name: string
|
|||
|
path: string
|
|||
|
type: string
|
|||
|
icon: string | null
|
|||
|
parentId: string | null
|
|||
|
sortOrder: number
|
|||
|
izVisible: number
|
|||
|
permissionKey: string | null
|
|||
|
}
|
|||
|
|
|||
|
export class MenuApi {
|
|||
|
static async getIndexMenus(): Promise<ApiResponse<MenuItem[]>>
|
|||
|
static async getStudentMenus(): Promise<ApiResponse<MenuItem[]>>
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
### 2. 路由工具 (`src/utils/routeUtils.ts`)
|
|||
|
```typescript
|
|||
|
// 根据菜单数据生成路由配置
|
|||
|
export function generateRoutesFromMenus(menus: MenuItem[]): RouteRecordRaw[]
|
|||
|
|
|||
|
// 生成导航菜单数据
|
|||
|
export function generateNavMenus(menus: MenuItem[])
|
|||
|
|
|||
|
// 检查路由是否存在于菜单中
|
|||
|
export function isRouteInMenus(path: string, menus: MenuItem[]): boolean
|
|||
|
```
|
|||
|
|
|||
|
### 3. 状态管理 (`src/stores/menu.ts`)
|
|||
|
```typescript
|
|||
|
export const useMenuStore = defineStore('menu', () => {
|
|||
|
// 状态
|
|||
|
const indexMenus = ref<MenuItem[]>([])
|
|||
|
const studentMenus = ref<MenuItem[]>([])
|
|||
|
const loading = ref(false)
|
|||
|
const error = ref<string | null>(null)
|
|||
|
|
|||
|
// 计算属性
|
|||
|
const navMenus = computed(() => generateNavMenus(indexMenus.value))
|
|||
|
const visibleIndexMenus = computed(() => ...)
|
|||
|
|
|||
|
// 方法
|
|||
|
const fetchIndexMenus = async () => { ... }
|
|||
|
const fetchStudentMenus = async () => { ... }
|
|||
|
})
|
|||
|
```
|
|||
|
|
|||
|
### 4. 路由配置 (`src/router/index.ts`)
|
|||
|
在路由守卫中自动加载动态菜单:
|
|||
|
```typescript
|
|||
|
router.beforeEach(async (to, from, next) => {
|
|||
|
// 加载动态菜单路由(仅在首次访问时)
|
|||
|
if (!isMenuLoaded) {
|
|||
|
await loadMenuRoutes()
|
|||
|
}
|
|||
|
// ...
|
|||
|
})
|
|||
|
```
|
|||
|
|
|||
|
## 使用方法
|
|||
|
|
|||
|
### 1. 在组件中使用菜单状态
|
|||
|
```vue
|
|||
|
<script setup lang="ts">
|
|||
|
import { useMenuStore } from '@/stores/menu'
|
|||
|
|
|||
|
const menuStore = useMenuStore()
|
|||
|
|
|||
|
// 获取菜单数据
|
|||
|
await menuStore.fetchIndexMenus()
|
|||
|
|
|||
|
// 使用菜单数据
|
|||
|
console.log(menuStore.navMenus)
|
|||
|
</script>
|
|||
|
```
|
|||
|
|
|||
|
### 2. 使用动态导航组件
|
|||
|
```vue
|
|||
|
<template>
|
|||
|
<DynamicNavigation />
|
|||
|
</template>
|
|||
|
|
|||
|
<script setup lang="ts">
|
|||
|
import DynamicNavigation from '@/components/layout/DynamicNavigation.vue'
|
|||
|
</script>
|
|||
|
```
|
|||
|
|
|||
|
### 3. 检查路由权限
|
|||
|
```typescript
|
|||
|
import { useMenuStore } from '@/stores/menu'
|
|||
|
|
|||
|
const menuStore = useMenuStore()
|
|||
|
|
|||
|
// 检查路由是否可见
|
|||
|
const isVisible = menuStore.isRouteVisible('/courses', 'index')
|
|||
|
```
|
|||
|
|
|||
|
## 路径映射
|
|||
|
|
|||
|
当前支持的路径映射:
|
|||
|
|
|||
|
| 菜单名称 | 路径 | 组件 |
|
|||
|
|---------|------|------|
|
|||
|
| 首页 | / | Home |
|
|||
|
| 热门好课 | /courses | Courses |
|
|||
|
| 专题训练 | /special-training | SpecialTraining |
|
|||
|
| 师资力量 | /faculty | Faculty |
|
|||
|
| 精选资源 | /resources | Resources |
|
|||
|
| 活动 | /activities | Activities |
|
|||
|
| AI体验 | /ai/app | AiAppList |
|
|||
|
|
|||
|
## 测试页面
|
|||
|
|
|||
|
访问 `/menu-test` 可以查看动态菜单系统的测试页面,包含:
|
|||
|
- 菜单状态监控
|
|||
|
- 菜单数据展示
|
|||
|
- 路由跳转测试
|
|||
|
- 动态导航组件演示
|
|||
|
|
|||
|
## 响应式设计
|
|||
|
|
|||
|
动态导航组件支持响应式设计:
|
|||
|
- 桌面端:垂直菜单布局
|
|||
|
- 移动端:水平滚动菜单布局
|
|||
|
|
|||
|
## 错误处理
|
|||
|
|
|||
|
系统包含完整的错误处理机制:
|
|||
|
- 网络请求失败重试
|
|||
|
- 加载状态显示
|
|||
|
- 错误信息提示
|
|||
|
- 手动重试功能
|
|||
|
|
|||
|
## 扩展说明
|
|||
|
|
|||
|
### 添加新的路径映射
|
|||
|
在 `src/utils/routeUtils.ts` 中的 `componentMap` 添加新的路径映射:
|
|||
|
```typescript
|
|||
|
const componentMap: Record<string, () => Promise<any>> = {
|
|||
|
'/new-path': () => import('@/views/NewComponent.vue'),
|
|||
|
// ...
|
|||
|
}
|
|||
|
```
|
|||
|
|
|||
|
### 自定义菜单图标
|
|||
|
在 `DynamicNavigation.vue` 中的 `iconMap` 添加新的图标映射:
|
|||
|
```typescript
|
|||
|
const iconMap: Record<string, any> = {
|
|||
|
'new-icon': NewIconComponent,
|
|||
|
// ...
|
|||
|
}
|
|||
|
```
|