一、超管端设计优化 - 文档管理SOP体系建立,docs目录重组 - 统一用户管理:跨租户全局视角,合并用户管理+公众用户 - 活动监管全模块重构:全部活动(统计卡片+阶段筛选+SuperDetail详情页)、报名数据/作品数据/评审进度(两层合一扁平列表)、成果发布(去Tab+统计+隐藏写操作) - 菜单精简:移除评委管理/评审规则/通知管理 - Bug修复:租户编辑丢失隐藏菜单、pageSize限制、主色统一 二、UGC绘本创作社区P0 - 数据库:10张新表(user_works/user_work_pages/work_tags等) - 子女账号独立化:Child升级为独立User,家长切换+独立登录 - 用户作品库:CRUD+发布审核,8个API - AI创作流程:提交→生成→保存到作品库,4个API - 作品广场:首页改造为推荐流,标签+搜索+排序 - 内容审核(超管端):作品审核+作品管理+标签管理 - 活动联动:WorkSelector作品选择器 - 布局改造:底部5Tab(发现/创作/活动/作品库/我的) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
263 lines
8.3 KiB
Markdown
263 lines
8.3 KiB
Markdown
# 新增前端页面路由指南
|
||
|
||
本文档介绍如何在活动管理系统中新增前端页面路由。
|
||
|
||
## 概述
|
||
|
||
系统采用**动态路由**机制,路由配置存储在数据库中,前端根据用户权限动态加载。新增页面需要完成以下 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` | 主布局、侧边栏菜单渲染 |
|