- 新增 CollapsibleSubGroup 组件,支持子分组的展开/折叠渲染 - DynamicMenuSection 改为递归检测子目录,支持多级嵌套 - 当前路径所在子分组自动展开 - 折叠侧边栏时子分组显示为图标 + Tooltip - 兼容 menu_type='page' 类型 - 数据库插入 6 个子分组(患者医护/预约排班/随访咨询/积分运营/内容运营/AI 分析)
6.8 KiB
6.8 KiB
三级可折叠侧边栏菜单 — 论证与实施计划
Context
HMS 健康管理平台的侧边栏菜单在"健康管理"分组下已有 18 个平铺菜单项。随着功能继续增长(AI 模块、专科模块等),这个问题会持续恶化。用户提出将菜单改为可折叠的 3 级目录结构。
1. 论证分析
1.1 当前问题
| 指标 | 现状 | 趋势 |
|---|---|---|
| 健康管理下菜单项 | 18 个 | 还会增加(AI、血透、OCR) |
| 侧边栏可视区域 | ~600px 高度 | 固定 |
| 单项高度 | ~40px | 固定 |
| 满屏可显示项数 | ~15 个 | — |
| 溢出 | 是,需滚动 | 严重恶化 |
结论:18 个平铺项已超出可视区域,必须滚动才能看到底部菜单。 这违反了信息架构的基本原则——用户应在首屏看到完整的导航结构。
1.2 方案对比
| 方案 | 描述 | 优点 | 缺点 |
|---|---|---|---|
| A. 维持现状 | 平铺 2 级菜单 | 简单、无改动 | 菜单项越多越难用,不可接受 |
| B. 3 级可折叠 | 在 directory 下再嵌套 directory | 归类清晰、按需展开、数据库已支持 | 前端需改造渲染逻辑 |
| C. Tab 切换 | 顶部 Tab 分域(患者/预约/管理) | 分区明确 | 破坏侧边栏导航范式,改动大 |
| D. 搜索导航 | 搜索框替代层级导航 | 适合超多菜单 | 学习成本高,不适合当前规模 |
推荐方案 B:3 级可折叠目录。
核心理由:
- 数据库零改动 —
menus表的parent_id自引用和build_tree()递归构建已支持 N 级嵌套 - 前端改动小 —
DynamicMenuSection只需加递归渲染,SidebarSubMenu的展开/折叠逻辑可直接复用 - 向后兼容 — 没有子分组的 directory 仍按原有方式渲染(2 级),有子分组的自动变为 3 级
- 可扩展 — 未来第 4 级也自然支持
1.3 提出的分组结构
将"健康管理"下的 18 个菜单项按业务域归入 5 个子分组:
健康管理
├── 患者管理 (icon: TeamOutlined)
│ ├── 患者列表
│ ├── 医护档案
│ └── 健康档案
├── 预约排班 (icon: CalendarOutlined)
│ ├── 预约管理
│ └── 排班管理
├── 随访咨询 (icon: PhoneOutlined)
│ ├── 随访管理
│ └── 咨询管理
├── 健康数据 (icon: HeartOutlined)
│ ├── 体征监测
│ ├── 化验报告
│ ├── 健康趋势
│ ├── 诊断记录
│ ├── 过敏管理
│ └── 血透记录
├── 内容运营 (icon: FileTextOutlined)
│ ├── 文章管理
│ ├── 分类管理
│ └── 标签管理
└── 综合管理 (icon: TrophyOutlined)
├── 积分管理
├── 线下活动
└── 统计分析
其他顶级分组(仪表盘、系统管理、工作流、消息中心、AI 智能分析)保持不变。
1.4 反对意见与回应
反对:增加一级嵌套增加点击次数。 回应:折叠状态下子分组只占一行(~40px),18 项从 ~720px 压缩到 ~200px。用户只需展开自己关注的子域,实际点击次数不会增加,反而因为分类清晰减少了寻找时间。
反对:3 级菜单对用户认知负担更重。 回应:当前 18 个平铺项的认知负担远大于 5 个分类名称。分组名称("患者管理"、"预约排班")本身是业务语义,不需要额外学习。
2. 技术分析
2.1 后端:零改动
menus表parent_id自引用已支持任意层级build_tree()递归构建已生成完整嵌套 JSONMenuInfo.children前端类型已是递归结构- 不需要修改任何 Rust 代码
2.2 数据层:插入子分组记录
在 menus 表中为"健康管理"目录插入 6 个子 directory,然后将现有菜单项的 parent_id 指向对应子 directory。
INSERT 新记录: 6 个 sub-directory (parent_id = 健康管理目录的 id)
UPDATE 现有记录: 18 个菜单项的 parent_id → 对应子 directory 的 id
可通过 SQL 迁移或直接 UPDATE 实现。
2.3 前端改造:DynamicMenuSection 递归化
当前代码(MainLayout.tsx:199-256):
- 遍历
menus,directory → 渲染标题 + 遍历 children - children 只渲染为
SidebarMenuItem(叶子节点),不再检查嵌套
改造目标:
- directory 有 children → 检查 children 中是否有 sub-directory
- 如果有 sub-directory → 用
SidebarSubMenu的展开/折叠模式渲染 - 递归调用自身,支持任意深度
复用的模式:SidebarSubMenu(MainLayout.tsx:136-196)的展开/折叠逻辑:
useState(true)管理展开状态RightOutlined箭头旋转动画- 折叠状态下 Tooltip 显示子菜单列表
hasActive检测高亮
2.4 关键文件
| 文件 | 改动 |
|---|---|
apps/web/src/layouts/MainLayout.tsx |
DynamicMenuSection 改为递归渲染 |
| 数据库(SQL 迁移或直接 UPDATE) | 插入 6 个子 directory + 更新 18 项 parent_id |
3. 实施步骤
Step 1: 数据层 — 菜单重组
直接用 SQL 更新现有菜单数据(不需要新建迁移文件,因为这是数据变更不是 schema 变更):
- 查出"健康管理"目录的
id - INSERT 6 个 sub-directory 记录(
menu_type = 'directory',parent_id指向健康管理) - UPDATE 18 个菜单项的
parent_id指向对应 sub-directory
Step 2: 前端 — DynamicMenuSection 递归化
将 DynamicMenuSection 改为支持递归渲染:
- 提取一个通用
MenuNode组件,接收MenuInfo递归渲染 menu_type === 'directory'→ 渲染分组标题 + 递归渲染 children- children 中如果是 directory → 用展开/折叠样式(复用 SidebarSubMenu 的箭头模式)
- children 中如果是 menu → 渲染
SidebarMenuItem - 折叠侧边栏时:sub-directory 显示 Tooltip,首项可点击
Step 3: 样式微调
- sub-directory 标题增加左侧缩进(
padding-left: 24px) - 展开动画过渡
- 活跃路径自动展开父级目录
Step 4: 验证
pnpm build前端编译通过- 浏览器验证:侧边栏显示 3 级结构
- 子分组可展开/折叠
- 折叠侧边栏时 Tooltip 正确显示
- 当前页面所在分组自动展开
- 其他顶级分组(系统管理、工作流等)不受影响
4. 工作量估计
| 步骤 | 预计时间 |
|---|---|
| Step 1 数据重组 | 30 分钟 |
| Step 2 前端递归化 | 1-2 小时 |
| Step 3 样式微调 | 30 分钟 |
| Step 4 验证 | 30 分钟 |
| 总计 | 3-4 小时 |
5. 验证方式
pnpm build编译无错误- 浏览器实际操作:展开/折叠/导航/折叠侧边栏
- 确认所有原有路由可正常访问
- 确认插件菜单不受影响