- 新增 CollapsibleSubGroup 组件,支持子分组的展开/折叠渲染 - DynamicMenuSection 改为递归检测子目录,支持多级嵌套 - 当前路径所在子分组自动展开 - 折叠侧边栏时子分组显示为图标 + Tooltip - 兼容 menu_type='page' 类型 - 数据库插入 6 个子分组(患者医护/预约排班/随访咨询/积分运营/内容运营/AI 分析)
176 lines
6.8 KiB
Markdown
176 lines
6.8 KiB
Markdown
# 三级可折叠侧边栏菜单 — 论证与实施计划
|
||
|
||
## 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 级可折叠目录。**
|
||
|
||
核心理由:
|
||
1. **数据库零改动** — `menus` 表的 `parent_id` 自引用和 `build_tree()` 递归构建已支持 N 级嵌套
|
||
2. **前端改动小** — `DynamicMenuSection` 只需加递归渲染,`SidebarSubMenu` 的展开/折叠逻辑可直接复用
|
||
3. **向后兼容** — 没有子分组的 directory 仍按原有方式渲染(2 级),有子分组的自动变为 3 级
|
||
4. **可扩展** — 未来第 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()` 递归构建已生成完整嵌套 JSON
|
||
- `MenuInfo.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](apps/web/src/layouts/MainLayout.tsx#L199-L256)):
|
||
- 遍历 `menus`,directory → 渲染标题 + 遍历 children
|
||
- children 只渲染为 `SidebarMenuItem`(叶子节点),不再检查嵌套
|
||
|
||
**改造目标**:
|
||
- directory 有 children → 检查 children 中是否有 sub-directory
|
||
- 如果有 sub-directory → 用 `SidebarSubMenu` 的展开/折叠模式渲染
|
||
- 递归调用自身,支持任意深度
|
||
|
||
**复用的模式**:`SidebarSubMenu`([MainLayout.tsx:136-196](apps/web/src/layouts/MainLayout.tsx#L136-L196))的展开/折叠逻辑:
|
||
- `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 变更):
|
||
|
||
1. 查出"健康管理"目录的 `id`
|
||
2. INSERT 6 个 sub-directory 记录(`menu_type = 'directory'`,`parent_id` 指向健康管理)
|
||
3. UPDATE 18 个菜单项的 `parent_id` 指向对应 sub-directory
|
||
|
||
### Step 2: 前端 — DynamicMenuSection 递归化
|
||
|
||
将 `DynamicMenuSection` 改为支持递归渲染:
|
||
|
||
1. 提取一个通用 `MenuNode` 组件,接收 `MenuInfo` 递归渲染
|
||
2. `menu_type === 'directory'` → 渲染分组标题 + 递归渲染 children
|
||
3. children 中如果是 directory → 用展开/折叠样式(复用 SidebarSubMenu 的箭头模式)
|
||
4. children 中如果是 menu → 渲染 `SidebarMenuItem`
|
||
5. 折叠侧边栏时:sub-directory 显示 Tooltip,首项可点击
|
||
|
||
### Step 3: 样式微调
|
||
|
||
- sub-directory 标题增加左侧缩进(`padding-left: 24px`)
|
||
- 展开动画过渡
|
||
- 活跃路径自动展开父级目录
|
||
|
||
### Step 4: 验证
|
||
|
||
1. `pnpm build` 前端编译通过
|
||
2. 浏览器验证:侧边栏显示 3 级结构
|
||
3. 子分组可展开/折叠
|
||
4. 折叠侧边栏时 Tooltip 正确显示
|
||
5. 当前页面所在分组自动展开
|
||
6. 其他顶级分组(系统管理、工作流等)不受影响
|
||
|
||
## 4. 工作量估计
|
||
|
||
| 步骤 | 预计时间 |
|
||
|------|---------|
|
||
| Step 1 数据重组 | 30 分钟 |
|
||
| Step 2 前端递归化 | 1-2 小时 |
|
||
| Step 3 样式微调 | 30 分钟 |
|
||
| Step 4 验证 | 30 分钟 |
|
||
| **总计** | **3-4 小时** |
|
||
|
||
## 5. 验证方式
|
||
|
||
- `pnpm build` 编译无错误
|
||
- 浏览器实际操作:展开/折叠/导航/折叠侧边栏
|
||
- 确认所有原有路由可正常访问
|
||
- 确认插件菜单不受影响
|