library-picturebook-activity/frontend/docs/add-new-route.md
aid 418aa57ea8 Day4: 超管端设计优化 + UGC绘本创作社区P0实现
一、超管端设计优化
- 文档管理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>
2026-03-27 22:20:25 +08:00

8.3 KiB
Raw Blame 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

组件模板示例:

<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

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 示例:

-- 添加顶级菜单
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

Step 4: 关联租户菜单

tenant_menus 表中添加租户与菜单的关联关系。

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

<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

const componentMap: Record<string, () => Promise<any>> = {
  // ... 其他组件
  "announcements/Index": () => import("@/views/announcements/Index.vue"),
};

3. 添加菜单记录

INSERT INTO menus (name, path, component, icon, parent_id, sort, permission, tenant_id)
VALUES ('公告管理', '/announcements', 'announcements/Index', 'NotificationOutlined', NULL, 5, 'announcement:read', 1);

4. 关联租户

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.tscomponentMap 中添加对应的组件映射。

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 主布局、侧边栏菜单渲染