1. 菜单类型默认值改为 TOP_LEFT(顶部一级+左侧二级) 2. 二级菜单 component 设为空字符串避免嵌套布局 3. 前端和后端菜单数据同步更新 Co-authored-by: Cursor <cursoragent@cursor.com>
15 KiB
菜单结构调整实现计划
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: 将 14 个一级菜单重组为 5 个一级菜单(市场、生产、质检、运营支撑、系统管理),新增菜单记录到 sys_menu 表并支持角色权限控制。
Architecture: 采用方案 A(一级菜单独立权限),每个新一级菜单设置独立 perms(如 menu:market),前端改造菜单组件支持两级菜单结构(顶部一级 + 左侧二级),后端同步改造菜单接口。
Tech Stack: Vue 3 + Element Plus + Pinia + FastAPI + SQLAlchemy
变更范围总览
| 层级 | 文件 | 变更类型 |
|---|---|---|
| 前端路由 | web-admin/src/router/modules/sqxp.ts |
结构调整 |
| 前端菜单组件 | web-admin/src/components/core/layouts/art-menus/* |
支持两级结构 |
| 前端状态 | web-admin/src/store/modules/menu.ts, setting.ts |
新增一级菜单状态 |
| 后端菜单接口 | backend/app/api/menu.py |
新增一级菜单数据 |
| 后端权限服务 | backend/app/services/web_auth_roles.py |
支持一级菜单权限过滤 |
| 数据库 | sys_menu 表 |
新增 5 个一级菜单记录 |
Task 1: 后端 - 更新菜单接口数据
Files:
-
Modify:
backend/app/api/menu.py:10-773 -
Step 1: 添加新一级菜单定义
在 STATIC_MENU_TREE 开头添加 5 个新一级菜单:
# 新增一级菜单定义(放在 STATIC_MENU_TREE 最前面)
TOP_MENU_MARKET = {
"id": "TOP00000000000000000000000000001",
"name": "MarketTop",
"path": "/market",
"component": "/index/index",
"meta": {
"title": "市场",
"icon": "ri:shopping-cart-line",
"keepAlive": False,
"isTopMenu": True,
"menuCode": "menu:market",
},
"children": [
# 细胞存储、科研捐赠、中试服务、基础实验、市场基础信息、临床应用
],
}
TOP_MENU_PRODUCTION = {
"id": "TOP00000000000000000000000000002",
"name": "ProductionTop",
"path": "/production-group",
"component": "/index/index",
"meta": {
"title": "生产",
"icon": "ri:factory-line",
"keepAlive": False,
"isTopMenu": True,
"menuCode": "menu:production",
},
"children": [
# 生产管理、生产基础信息
],
}
TOP_MENU_QUALITY = {
"id": "TOP00000000000000000000000000003",
"name": "QualityTop",
"path": "/quality-group",
"component": "/index/index",
"meta": {
"title": "质检",
"icon": "ri:shield-check-line",
"keepAlive": False,
"isTopMenu": True,
"menuCode": "menu:quality",
},
"children": [
# 质检管理、质检基础信息
],
}
TOP_MENU_OPERATIONS = {
"id": "TOP00000000000000000000000000004",
"name": "OperationsTop",
"path": "/operations-group",
"component": "/index/index",
"meta": {
"title": "运营支撑",
"icon": "ri:settings-3-line",
"keepAlive": False,
"isTopMenu": True,
"menuCode": "menu:operations",
},
"children": [
# 系统基础信息、运营支撑
],
}
TOP_MENU_SYSTEM = {
"id": "TOP00000000000000000000000000005",
"name": "SystemTop",
"path": "/system-manage",
"component": "/index/index",
"meta": {
"title": "系统管理",
"icon": "ri:server-line",
"keepAlive": False,
"isTopMenu": True,
"menuCode": "menu:system-manage",
},
"children": [
# 系统管理、工作流管理
],
}
- Step 2: 调整原有菜单到新父级下
将现有 14 个一级菜单作为二级菜单,重新分配 parent_id 和 path:
| 原菜单 | 新父级 | 新 path |
|---|---|---|
| 细胞存储 | TOP00000000000000000000000000001 | /market/cell-storage |
| 科研捐赠 | TOP00000000000000000000000000001 | /market/research-donation |
| 中试服务 | TOP00000000000000000000000000001 | /market/pilot-service |
| 基础实验 | TOP00000000000000000000000000001 | /market/basic-experiment |
| 市场基础信息 | TOP00000000000000000000000000001 | /market/market-info |
| 临床应用 | TOP00000000000000000000000000002 | /production-group/clinical-application |
| 生产管理 | TOP00000000000000000000000000002 | /production-group/production |
| 生产基础信息 | TOP00000000000000000000000000002 | /production-group/production-base |
| 质检管理 | TOP00000000000000000000000000003 | /quality-group/quality-manage |
| 质检基础信息 | TOP00000000000000000000000000003 | /quality-group/quality-base |
| 系统基础信息 | TOP00000000000000000000000000004 | /operations-group/system-base |
| 运营支撑 | TOP00000000000000000000000000004 | /operations-group/operations |
| 系统管理 | TOP00000000000000000000000000005 | /system-manage/system |
| 工作流管理 | TOP00000000000000000000000000005 | /system-manage/workflow |
- Step 3: 更新 STATIC_MENU_TREE 结构
将 STATIC_MENU_TREE 改为只包含 5 个新一级菜单,每个一级菜单的 children 包含对应的原菜单作为二级菜单。
- Step 4: Commit
git add backend/app/api/menu.py
git commit -m "feat(menu): 重构菜单为5个一级分组菜单结构"
Task 2: 后端 - 更新权限服务支持一级菜单过滤
Files:
-
Modify:
backend/app/services/web_auth_roles.py -
Step 1: 添加一级菜单权限码常量
# 一级菜单权限码
TOP_MENU_PERMS = {"menu:market", "menu:production", "menu:quality", "menu:operations", "menu:system-manage"}
- Step 2: 确保 iter_user_menu_perm_codes 返回一级菜单 perms
当前 iter_user_menu_perm_codes 通过 sys_role_menu → sys_menu 查询用户拥有的菜单权限。一级菜单的 perms(如 menu:market)已经在 sys_menu 表中定义,因此无需修改查询逻辑。只需确保 sys_menu 表中有这 5 个一级菜单的记录(Task 3 处理)。
- Step 3: Commit
git add backend/app/services/web_auth_roles.py
git commit -m "feat(auth): 支持一级菜单权限码过滤"
Task 3: 数据库 - 插入一级菜单数据到 sys_menu
Files:
-
Create:
docs/sql/menu_restructuring.sql -
Step 1: 编写 SQL 插入脚本
创建 SQL 脚本,包含 5 个一级菜单的 INSERT 语句(menu_type = 'M' 表示目录):
-- 一级菜单
INSERT INTO sys_menu (id, menu_name, parent_id, menu_type, path, component, perms, is_frame, visible, status, tenant_no, order_num, create_by, create_dept_id, create_time, update_by, update_time, del_flag) VALUES
('TOP00000000000000000000000000001', '市场', '0', 'M', '/market', '/index/index', 'menu:market', 1, '1', '1', 'zkxw', 1, 'system', '', NOW(), 'system', NOW(), 0),
('TOP00000000000000000000000000002', '生产', '0', 'M', '/production-group', '/index/index', 'menu:production', 1, '1', '1', 'zkxw', 2, 'system', '', NOW(), 'system', NOW(), 0),
('TOP00000000000000000000000000003', '质检', '0', 'M', '/quality-group', '/index/index', 'menu:quality', 1, '1', '1', 'zkxw', 3, 'system', '', NOW(), 'system', NOW(), 0),
('TOP00000000000000000000000000004', '运营支撑', '0', 'M', '/operations-group', '/index/index', 'menu:operations', 1, '1', '1', 'zkxw', 4, 'system', '', NOW(), 'system', NOW(), 0),
('TOP00000000000000000000000000005', '系统管理', '0', 'M', '/system-manage', '/index/index', 'menu:system-manage', 1, '1', '1', 'zkxw', 5, 'system', '', NOW(), 'system', NOW(), 0);
- Step 2: Commit
git add docs/sql/menu_restructuring.sql
git commit -m "feat(db): 添加5个一级菜单到sys_menu表"
Task 4: 前端 - 改造 sqxp.ts 路由结构
Files:
-
Modify:
web-admin/src/router/modules/sqxp.ts:1-1264 -
Step 1: 添加新一级菜单定义
在文件开头添加 5 个新一级菜单(与后端对应):
// 新一级菜单
const MARKET_TOP_MENU: AppRouteRecord = {
id: 'TOP00000000000000000000000000001',
name: 'MarketTop',
path: '/market',
component: '/index/index',
meta: {
title: '市场',
icon: 'ri:shopping-cart-line',
keepAlive: false,
isTopMenu: true,
roles: ['admin'],
menuCode: 'menu:market'
},
children: [
// 细胞存储、科研捐赠、中试服务、基础实验、市场基础信息、临床应用
]
}
const PRODUCTION_TOP_MENU: AppRouteRecord = {
id: 'TOP00000000000000000000000000002',
name: 'ProductionTop',
path: '/production-group',
component: '/index/index',
meta: {
title: '生产',
icon: 'ri:factory-line',
keepAlive: false,
isTopMenu: true,
roles: ['admin'],
menuCode: 'menu:production'
},
children: [
// 临床应用、生产管理、生产基础信息
]
}
const QUALITY_TOP_MENU: AppRouteRecord = {
id: 'TOP00000000000000000000000000003',
name: 'QualityTop',
path: '/quality-group',
component: '/index/index',
meta: {
title: '质检',
icon: 'ri:shield-check-line',
keepAlive: false,
isTopMenu: true,
roles: ['admin'],
menuCode: 'menu:quality'
},
children: [
// 质检管理、质检基础信息
]
}
const OPERATIONS_TOP_MENU: AppRouteRecord = {
id: 'TOP00000000000000000000000000004',
name: 'OperationsTop',
path: '/operations-group',
component: '/index/index',
meta: {
title: '运营支撑',
icon: 'ri:settings-3-line',
keepAlive: false,
isTopMenu: true,
roles: ['admin'],
menuCode: 'menu:operations'
},
children: [
// 系统基础信息、运营支撑
]
}
const SYSTEM_TOP_MENU: AppRouteRecord = {
id: 'TOP00000000000000000000000000005',
name: 'SystemTop',
path: '/system-manage',
component: '/index/index',
meta: {
title: '系统管理',
icon: 'ri:server-line',
keepAlive: false,
isTopMenu: true,
roles: ['admin'],
menuCode: 'menu:system-manage'
},
children: [
// 系统管理、工作流管理
]
}
- Step 2: 将原有 14 个菜单作为二级菜单移动到对应一级菜单下
将细胞存储、科研捐赠、中试服务、基础实验、市场基础信息移到 MARKET_TOP_MENU.children 下; 将临床应用移到 PRODUCTION_TOP_MENU.children 下; 以此类推。
- Step 3: 更新 export 导出
将 sqxpAdminRoutes 改为只包含 5 个新一级菜单。
- Step 4: Commit
git add web-admin/src/router/modules/sqxp.ts
git commit -m "feat(router): 重构路由为5个一级菜单分组"
Task 5: 前端 - 改造菜单组件支持两级结构
Files:
-
Modify:
web-admin/src/components/core/layouts/art-menus/art-horizontal-menu/index.vue -
Modify:
web-admin/src/components/core/layouts/art-menus/art-mixed-menu/index.vue -
Modify:
web-admin/src/components/core/layouts/art-menus/art-sidebar-menu/index.vue -
Step 1: 识别当前活跃的一级菜单
新增逻辑:根据当前路由路径判断所属的一级菜单,提取出 activeTopMenuId。
// 在组件中添加 computed
const activeTopMenuId = computed(() => {
const path = route.path
// 从路径中提取一级菜单 path,如 /market/cell-storage → /market
const topPath = '/' + path.split('/')[1]
return topPath
})
- Step 2: 分离一级菜单和二级菜单数据
// 过滤出一级菜单(isTopMenu: true)
const topMenus = computed(() => {
return props.list.filter(item => item.meta?.isTopMenu)
})
// 根据活跃的一级菜单获取二级菜单
const subMenus = computed(() => {
const active = topMenus.value.find(item => item.path === activeTopMenuId.value)
return active?.children || []
})
- Step 3: 修改模板
水平菜单只显示一级菜单,点击后更新 activeTopMenuId,并触发侧边栏显示对应的二级菜单。
- Step 4: Commit
git add web-admin/src/components/core/layouts/art-menus/art-horizontal-menu/index.vue
git add web-admin/src/components/core/layouts/art-menus/art-mixed-menu/index.vue
git commit -m "feat(menu): 改造顶部菜单支持两级结构"
Task 6: 前端 - 改造侧边栏组件
Files:
-
Modify:
web-admin/src/components/core/layouts/art-menus/art-sidebar-menu/index.vue -
Step 1: 修改菜单列表计算逻辑
当前 menuList computed 根据 isTopLeftMenu 或 isDualMenu 返回对应子菜单。改为:
- 当存在活跃一级菜单时,返回该一级菜单的 children
- 否则返回完整菜单列表(兼容一级菜单页面)
const menuList = computed(() => {
const menuStore = useMenuStore()
const allMenus = menuStore.menuList
if (!isTopLeftMenu.value && !isDualMenu.value) {
return allMenus
}
// 处理 iframe 路径
if (isIframe(route.path)) {
return findIframeMenuList(route.path, allMenus)
}
// 一级菜单页面直接返回空
if (route.meta.isFirstLevel) {
return []
}
// 返回当前一级菜单的子菜单
const topPath = '/' + route.path.split('/')[1]
const currentTopMenu = allMenus.find(menu =>
menu.meta?.isTopMenu && menu.path === topPath
)
if (currentTopMenu?.children) {
return currentTopMenu.children.filter((child: AppRouteRecord) => !child.meta?.isHide)
}
return []
})
- Step 2: Commit
git add web-admin/src/components/core/layouts/art-menus/art-sidebar-menu/index.vue
git commit -m "feat(menu): 改造侧边栏显示二级菜单"
Task 7: 前端 - 更新菜单状态管理
Files:
-
Modify:
web-admin/src/store/modules/menu.ts -
Modify:
web-admin/src/store/modules/setting.ts -
Step 1: 在 menu.ts 中添加活跃一级菜单状态
/** 活跃的一级菜单路径 */
const activeTopMenuPath = ref('')
/** 设置活跃一级菜单 */
const setActiveTopMenuPath = (path: string) => {
activeTopMenuPath.value = path
}
- Step 2: Commit
git add web-admin/src/store/modules/menu.ts
git commit -m "feat(store): 添加一级菜单活跃状态管理"
Task 8: 测试验证
- Step 1: 启动后端服务
cd backend && ./start-local.sh
- Step 2: 启动前端服务
cd web-admin && pnpm dev
- Step 3: 验证菜单显示
- 登录管理端
- 检查顶部是否显示 5 个一级菜单
- 点击一级菜单,检查左侧是否显示对应的二级菜单
- 验证权限控制:没有
menu:market权限的角色不应看到「市场」菜单
- Step 4: Commit
git add -A
git commit -m "test: 验证菜单结构调整功能"
执行方式
Two execution options:
1. Subagent-Driven (recommended) - I dispatch a fresh subagent per task, review between tasks, fast iteration
2. Inline Execution - Execute tasks in this session using executing-plans, batch execution with checkpoints
Which approach?