library-picturebook-activity/lesingle-creation-frontend/docs/add-new-route.md

263 lines
8.3 KiB
Markdown
Raw Permalink Normal View History

# 新增前端页面路由指南
本文档介绍如何在活动管理系统中新增前端页面路由。
## 概述
系统采用**动态路由**机制,路由配置存储在数据库中,前端根据用户权限动态加载。新增页面需要完成以下 4 个步骤:
```
┌──────────────────────────────────────────────────────────────┐
│ Step 1: 创建 Vue 组件文件 │
│ frontend/src/views/xxx/Index.vue │
├──────────────────────────────────────────────────────────────┤
│ Step 2: 在 componentMap 中注册组件 │
│ frontend/src/utils/menu.ts │
├──────────────────────────────────────────────────────────────┤
│ Step 3: 在数据库 menus 表中添加菜单记录 │
├──────────────────────────────────────────────────────────────┤
│ Step 4: 在数据库 tenant_menus 表中关联租户 │
└──────────────────────────────────────────────────────────────┘
```
## 详细步骤
### Step 1: 创建 Vue 组件文件
`frontend/src/views/` 目录下创建对应的 Vue 组件。
**目录结构规范:**
```
frontend/src/views/
├── workbench/ # 工作台模块
│ └── Index.vue
├── contests/ # 活动管理模块
│ ├── Index.vue # 活动列表
│ ├── Create.vue # 创建活动
│ ├── Detail.vue # 活动详情
│ ├── registrations/ # 报名管理
│ │ └── Index.vue
│ ├── works/ # 作品管理
│ │ └── Index.vue
│ └── ...
├── system/ # 系统管理模块
│ ├── users/
│ │ └── Index.vue
│ ├── roles/
│ │ └── Index.vue
│ └── ...
└── your-module/ # 你的新模块
└── Index.vue
```
**组件模板示例:**
```vue
<template>
<div class="page-container">
<h1>页面标题</h1>
<!-- 页面内容 -->
</div>
</template>
<script setup lang="ts">
// 组件逻辑
</script>
<style scoped lang="scss">
.page-container {
// 样式
}
</style>
```
### Step 2: 注册组件映射
`frontend/src/utils/menu.ts` 文件中的 `componentMap` 对象中添加组件映射。
**文件位置:** `frontend/src/utils/menu.ts`
```typescript
const componentMap: Record<string, () => Promise<any>> = {
// 工作台
"workbench/Index": () => import("@/views/workbench/Index.vue"),
// 活动管理模块
"contests/Index": () => import("@/views/contests/Index.vue"),
"contests/Create": () => import("@/views/contests/Create.vue"),
// ...
// 系统管理模块
"system/users/Index": () => import("@/views/system/users/Index.vue"),
// ...
// ========== 新增组件映射 ==========
"your-module/Index": () => import("@/views/your-module/Index.vue"),
"your-module/Detail": () => import("@/views/your-module/Detail.vue"),
};
```
**注意事项:**
- Key 格式:`模块名/组件名`,不需要 `@/views/` 前缀和 `.vue` 后缀
- Value 格式:使用动态 import 函数
- 如果未注册,控制台会输出警告信息
### Step 3: 添加数据库菜单记录
在数据库的 `menus` 表中插入菜单记录。
**menus 表结构:**
| 字段 | 类型 | 说明 |
|------|------|------|
| id | int | 主键,自增 |
| name | varchar | 菜单名称(显示文本) |
| path | varchar | 路由路径,如 `/contests` |
| component | varchar | 组件路径,对应 componentMap 的 key |
| icon | varchar | 图标名称,使用 Ant Design 图标 |
| parent_id | int | 父菜单ID顶级菜单为 NULL |
| sort | int | 排序序号 |
| permission | varchar | 权限标识,如 `contest:read` |
| tenant_id | int | 租户ID |
**SQL 示例:**
```sql
-- 添加顶级菜单
INSERT INTO menus (name, path, component, icon, parent_id, sort, tenant_id)
VALUES ('新模块', '/your-module', 'your-module/Index', 'AppstoreOutlined', NULL, 10, 1);
-- 获取刚插入的菜单ID
SET @parent_id = LAST_INSERT_ID();
-- 添加子菜单
INSERT INTO menus (name, path, component, icon, parent_id, sort, tenant_id)
VALUES ('子页面1', '/your-module/sub1', 'your-module/Sub1', NULL, @parent_id, 1, 1);
INSERT INTO menus (name, path, component, icon, parent_id, sort, tenant_id)
VALUES ('子页面2', '/your-module/sub2', 'your-module/Sub2', NULL, @parent_id, 2, 1);
```
**常用图标名称:**
- `HomeOutlined` - 首页
- `AppstoreOutlined` - 应用
- `TrophyOutlined` - 奖杯/活动
- `TeamOutlined` - 团队
- `UserOutlined` - 用户
- `SettingOutlined` - 设置
- `FileOutlined` - 文件
- `FolderOutlined` - 文件夹
更多图标请参考:[Ant Design Icons](https://ant.design/components/icon-cn)
### Step 4: 关联租户菜单
`tenant_menus` 表中添加租户与菜单的关联关系。
**SQL 示例:**
```sql
-- 假设菜单ID为 30租户ID为 1
INSERT INTO tenant_menus (tenant_id, menu_id) VALUES (1, 30);
-- 如果刚插入菜单,可以使用 LAST_INSERT_ID()
INSERT INTO tenant_menus (tenant_id, menu_id) VALUES (1, LAST_INSERT_ID());
-- 批量关联(关联多个租户)
INSERT INTO tenant_menus (tenant_id, menu_id) VALUES
(1, 30),
(2, 30),
(3, 30);
```
## 完整示例
假设要新增一个「公告管理」页面:
### 1. 创建组件文件
**文件:** `frontend/src/views/announcements/Index.vue`
```vue
<template>
<div>
<a-card title="公告管理">
<a-table :columns="columns" :data-source="data" />
</a-card>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const columns = [
{ title: '标题', dataIndex: 'title' },
{ title: '发布时间', dataIndex: 'createdAt' },
{ title: '操作', key: 'action' },
]
const data = ref([])
</script>
```
### 2. 注册组件
**文件:** `frontend/src/utils/menu.ts`
```typescript
const componentMap: Record<string, () => Promise<any>> = {
// ... 其他组件
"announcements/Index": () => import("@/views/announcements/Index.vue"),
};
```
### 3. 添加菜单记录
```sql
INSERT INTO menus (name, path, component, icon, parent_id, sort, permission, tenant_id)
VALUES ('公告管理', '/announcements', 'announcements/Index', 'NotificationOutlined', NULL, 5, 'announcement:read', 1);
```
### 4. 关联租户
```sql
INSERT INTO tenant_menus (tenant_id, menu_id) VALUES (1, LAST_INSERT_ID());
```
### 5. 验证
1. 重新登录系统(清除缓存)
2. 检查侧边栏是否显示新菜单
3. 点击菜单验证页面是否正常加载
## 常见问题
### Q1: 菜单不显示?
检查以下几点:
1. `tenant_menus` 表是否已关联当前租户
2. 用户角色是否有该菜单的权限
3. 清除浏览器缓存后重新登录
### Q2: 点击菜单报 404
检查以下几点:
1. `componentMap` 是否已注册该组件
2. 组件文件路径是否正确
3. 查看控制台是否有警告信息
### Q3: 控制台警告「组件路径未在 componentMap 中定义」?
`menu.ts``componentMap` 中添加对应的组件映射。
### Q4: 如何添加需要权限的页面?
`menus` 表的 `permission` 字段设置权限标识,如 `announcement:read`,然后确保用户角色拥有该权限。
## 相关文件
| 文件 | 说明 |
|------|------|
| `frontend/src/utils/menu.ts` | 组件映射、菜单转换工具 |
| `frontend/src/router/index.ts` | 路由配置、动态路由加载 |
| `frontend/src/stores/auth.ts` | 用户认证、菜单数据存储 |
| `frontend/src/layouts/BasicLayout.vue` | 主布局、侧边栏菜单渲染 |