2026-06-12-menu-restructuring.md 15 KB

菜单结构调整实现计划

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: 验证菜单显示
  1. 登录管理端
  2. 检查顶部是否显示 5 个一级菜单
  3. 点击一级菜单,检查左侧是否显示对应的二级菜单
  4. 验证权限控制:没有 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?