Files
hms/docs/archive/superpowers-completed/2026-04-26-frontend-engineering.md
iven 18fa6ce6d4 docs: 全局文档梳理归档 — 删除过期文件 + 归档 V1/早期设计 + wiki 数据校正 + CLAUDE.md 规则优化
**根目录清理:**
- 删除 CLAUDE-1.md(ZCLAW 旧项目配置,HMS 已完全脱离)
- 移动 DESIGN.md → docs/archive/(ERP 旧设计系统)
- 删除 plans/ 98 个临时会话计划文件

**归档重组:**
- V1 审计(12 文件)→ docs/archive/audits-v1/
- 早期 CRM/插件迭代设计(13 文件)→ docs/archive/superpowers-early/
- 已完成/已取代设计(28 文件)→ docs/archive/superpowers-completed/
- 早期讨论/测试报告 → docs/archive/discussions-early/ + test-reports-early/
- QA 重复文件清理(3 个旧版 result 文件)

**wiki 数据校正:**
- 迁移数 137→145,源文件 599→649,提交数 720→800+
- 小程序文件 124→163,Web 前端 297→332
- 后端测试 999→943(实际统计),权限码 75+→128
- 文档索引新增归档目录说明

**CLAUDE.md 规则优化:**
- §2.5 闭环工作法:提交+文档+推送三合一 + wiki 更新触发条件
- §2.6 Feature DoD:新增文档一致性检查项
- §6 反模式:新增 wiki 更新滞后/推送不及时警告
2026-05-15 09:29:04 +08:00

291 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 前端工程化改进实施计划
> 设计规格: `docs/superpowers/specs/2026-04-26-frontend-engineering-design.md`
> 日期: 2026-04-26 | 状态: draft | 总周期: 7 天
---
## Phase 1: 重复模式统一Day 1-2
### Task 1: 增强 useApiRequest hook统一错误处理
**目标**: 补齐 loading 状态,消除组件内联 `catch (err) { message.error(...) }` 模式。
**涉及文件**:
- 修改: `apps/web/src/hooks/useApiRequest.ts`
**详细步骤**:
1.`useApiRequest` 返回值中新增 `loading: boolean` 状态:
```typescript
interface UseApiRequestReturn {
execute: <T>(fn: () => Promise<T>, successMsg?: string) => Promise<T | null>;
loading: boolean;
}
```
2. `execute` 内部在调用前 `setLoading(true)`finally 中 `setLoading(false)`
3. 保持现有调用点无需修改 — 返回值是对象解构,新增字段不影响旧代码
4. 选取 3 个健康模块页面PatientList、AppointmentList、FollowUpTaskList迁移为使用 `execute` + `loading`
**验收标准**:
- `pnpm build` 通过
- 3 个迁移页面的 catch 块不再有内联 `message.error`,统一走 `handleApiError`
- loading 状态正确绑定到页面按钮/Spin 组件
---
### Task 2: 增强 usePaginatedData hook健康模块页面迁移
**目标**: 支持泛型筛选参数,迁移 6 个健康列表页使用统一 hook。
**涉及文件**:
- 修改: `apps/web/src/hooks/usePaginatedData.ts`
- 修改: `apps/web/src/pages/health/PatientList.tsx`
- 修改: `apps/web/src/pages/health/OfflineEventList.tsx`
- 修改: `apps/web/src/pages/health/PointsProductList.tsx`
**详细步骤**:
1. 增强 hook 签名为泛型筛选:
```typescript
function usePaginatedData<T, F = string>(
fetchFn: (page: number, pageSize: number, filters: F) => Promise<{ data: T[]; total: number }>,
options?: { pageSize?: number; defaultFilters: F; autoFetch?: boolean }
): { data, total, page, loading, filters, setFilters, refresh }
```
2. 函数重载保持旧 `(fetchFn, pageSize?)` 签名兼容
3. 新增 `filters` / `setFilters` 状态,`fetchFn` 调用时传入当前 filters
4. 迁移 PatientList按 status/name/gender 筛选)和 OfflineEventList按 status/dateRange 筛选)
**验收标准**:
- 旧调用点(不传 filters行为不变
- PatientList 和 OfflineEventList 筛选功能正常,代码行数各减少 15-25 行
- `pnpm build` 通过
---
### Task 3: 移除 nameCache统一用 useHealthStore
**目标**: 消除 AppointmentList 和 PointsOrderList 自建的 `useState<Record<string, string>>` nameCache。
**涉及文件**:
- 修改: `apps/web/src/stores/health.ts`
- 修改: `apps/web/src/pages/health/AppointmentList.tsx`
- 修改: `apps/web/src/pages/health/PointsOrderList.tsx`
**详细步骤**:
1.`useHealthStore` 新增批量解析方法:
- `batchResolvePatientNames(ids: string[]): Promise<Record<string, string>>`
- `batchResolveDoctorNames(ids: string[]): Promise<Record<string, string>>`
2. 内部实现:去重 → 过滤已缓存 → 并发加载(限制 5 并发)→ 写入缓存并返回
3. 在 AppointmentList 中移除 nameCache state改用 store 方法
4. 在 PointsOrderList 中同样迁移
**验收标准**:
- 两个页面无 `useState<Record<string, string>>` nameCache 代码
- 患者姓名/医生姓名在列表中正确显示
- `pnpm build` 通过
---
## Phase 2: 大组件拆分Day 3-5
### Task 4: PluginCRUDPage 拆分为 CRUDTable/CRUDForm/DetailDrawer/ImportExport
**目标**: 将 872 行的 PluginCRUDPage.tsx 拆为容器 + 展示组件。
**涉及文件**:
- 新增: `apps/web/src/pages/plugins/components/CRUDTable.tsx` (~150 行)
- 新增: `apps/web/src/pages/plugins/components/CRUDForm.tsx` (~180 行)
- 新增: `apps/web/src/pages/plugins/components/DetailDrawer.tsx` (~80 行)
- 新增: `apps/web/src/pages/plugins/components/ImportExport.tsx` (~100 行)
- 新增: `apps/web/src/pages/plugins/hooks/usePluginData.ts` (~120 行)
- 修改: `apps/web/src/pages/plugins/PluginCRUDPage.tsx` (缩减至 ~80 行)
**详细步骤**:
1. 创建 `hooks/usePluginData.ts`:提取 CRUD 操作、导入导出逻辑、Drawer 可见性状态
2. 创建 `CRUDTable.tsx`:表格列定义 + 行操作按钮props 接收 data/onDelete/onEdit/onDetail
3. 创建 `CRUDForm.tsx`:新增/编辑表单 + Drawer包含校验规则
4. 创建 `DetailDrawer.tsx`:详情展示 + 操作历史 Timeline
5. 创建 `ImportExport.tsx`:导入面板 + 导出按钮
6. 改写 `PluginCRUDPage.tsx` 为容器组件:调用 usePluginData hook组装子组件
**验收标准**:
- `pnpm build` 通过
- 插件 CRUD 所有功能正常(新增、编辑、删除、详情、导入、导出)
- PluginCRUDPage.tsx <= 100 行,无子组件超过 200 行
---
### Task 5: PluginGraphPage 抽取 useGraphCanvas hook
**目标**: 将 759 行的 PluginGraphPage.tsx 拆为 hook + 展示组件。
**涉及文件**:
- 新增: `apps/web/src/pages/plugins/hooks/useGraphLayout.ts` (~100 行)
- 新增: `apps/web/src/pages/plugins/hooks/useGraphData.ts` (~80 行)
- 新增: `apps/web/src/pages/plugins/components/GraphCanvas.tsx` (~200 行)
- 新增: `apps/web/src/pages/plugins/components/GraphToolbar.tsx` (~60 行)
- 修改: `apps/web/src/pages/plugins/PluginGraphPage.tsx` (缩减至 ~60 行)
**详细步骤**:
1. `useGraphData.ts`:数据加载、边/节点格式转换、字段映射
2. `useGraphLayout.ts`Dagre/elkjs 布局算法、节点位置计算、自动布局触发
3. `GraphCanvas.tsx`ReactFlow 渲染、自定义节点样式、拖拽交互
4. `GraphToolbar.tsx`:缩放控制、自动布局、布局方向切换
5. 容器组件组装以上模块
**验收标准**:
- 插件关系图页面正常渲染和交互
- 拖拽节点、自动布局、缩放功能正常
- `pnpm build` 通过
---
### Task 6: Organizations.tsx 抽象 TreeEntityManager
**目标**: 将 622 行的 Organizations.tsx 按三层模式拆分。
**涉及文件**:
- 新增: `apps/web/src/pages/system/hooks/useOrgTree.ts` (~80 行)
- 新增: `apps/web/src/pages/system/components/OrgTree.tsx` (~120 行)
- 新增: `apps/web/src/pages/system/components/OrgDetail.tsx` (~150 行)
- 新增: `apps/web/src/pages/system/components/DeptMemberList.tsx` (~100 行)
- 修改: `apps/web/src/pages/system/Organizations.tsx` (缩减至 ~60 行)
**详细步骤**:
1. `useOrgTree.ts`树数据加载、CRUD 操作、选中节点状态
2. `OrgTree.tsx`左侧树形选择DirectoryTree + 搜索 + 右键菜单)
3. `OrgDetail.tsx`:右侧组织详情/编辑表单
4. `DeptMemberList.tsx`:部门成员列表 + 人员分配 Modal
5. 容器组件三栏布局组装
**验收标准**:
- 组织管理 CRUD 功能正常(新增/编辑/删除组织、部门、人员分配)
- 树形选择、搜索过滤正常
- `pnpm build` 通过
---
### Task 7: StatisticsDashboard 拆分为独立卡片组件
**目标**: 将 580 行的 StatisticsDashboard.tsx 拆为 hook + 独立图表卡片。
**涉及文件**:
- 新增: `apps/web/src/pages/health/hooks/useStatsData.ts` (~100 行)
- 新增: `apps/web/src/pages/health/components/PatientTrendChart.tsx` (~80 行)
- 新增: `apps/web/src/pages/health/components/AppointmentStats.tsx` (~80 行)
- 新增: `apps/web/src/pages/health/components/OverviewCards.tsx` (~60 行)
- 新增: `apps/web/src/pages/health/components/TimeRangeSelector.tsx` (~40 行)
- 修改: `apps/web/src/pages/health/StatisticsDashboard.tsx` (缩减至 ~50 行)
**详细步骤**:
1. `useStatsData.ts`:五个统计 API 并行加载、loading/error 状态、时间范围变更触发刷新
2. `PatientTrendChart.tsx`:患者趋势折线图(@ant-design/charts Line
3. `AppointmentStats.tsx`:预约统计饼图/柱状图
4. `OverviewCards.tsx`概览数字卡片组Statistic + Card
5. `TimeRangeSelector.tsx`:日期范围选择 + 快捷选项近7天/近30天/近90天
6. 容器组件组装,布局使用 Row + Col
**验收标准**:
- 统计仪表板页面渲染正常,图表数据正确
- 时间范围切换触发数据刷新
- `pnpm build` 通过
---
## Phase 3: Bundle 优化Day 6-7
### Task 8: vite.config.ts manualChunks 拆分重型依赖
**目标**: 将 @ant-design/charts、@xyflow/react@wangeditor/editor 拆为独立 chunk降低主 chunk 体积。
**涉及文件**:
- 修改: `apps/web/vite.config.ts`
**详细步骤**:
1.`manualChunks` 配置中新增三条规则:
```typescript
if (id.includes('@ant-design/charts') || id.includes('@antv/')) return 'vendor-charts';
if (id.includes('@xyflow/react') || id.includes('@reactflow/')) return 'vendor-flow';
if (id.includes('@wangeditor/')) return 'vendor-editor';
```
2. 对应页面添加路由级 `React.lazy()`
- `StatisticsDashboard``lazy(() => import('./health/StatisticsDashboard'))`
- `PluginGraphPage``lazy(() => import('./plugins/PluginGraphPage'))`
- `ArticleEditor``lazy(() => import('./health/ArticleEditor'))`
3.`chunkSizeWarningLimit` 从 600 降至 500
4. 运行 `pnpm build` 对比拆分前后各 chunk 大小
**验收标准**:
- 主 chunk 体积 < 400KBgzip 前约 600KB 以内)
- `vendor-charts``vendor-flow``vendor-editor` 独立生成
- `pnpm build` 无警告
- 统计仪表板、插件关系图、文章编辑器页面功能正常(懒加载无闪烁)
---
### Task 9: columns 配置 useMemo 化
**目标**: 消除 PluginCRUDPage 和健康模块列表页的 columns 重复创建,减少不必要的 re-render。
**涉及文件**:
- 修改: `apps/web/src/pages/plugins/components/CRUDTable.tsx`Phase 2 Task 4 产物)
- 修改: `apps/web/src/pages/health/PatientList.tsx`
- 修改: `apps/web/src/pages/health/AppointmentList.tsx`
- 修改: `apps/web/src/pages/health/FollowUpTaskList.tsx`
**详细步骤**:
1. 在每个列表页中,将 `columns` 数组定义包裹在 `useMemo`
2. 依赖项包含 columns 中引用的回调函数(如 onDelete、onEdit
3. 确保回调函数通过 `useCallback` 缓存,避免 useMemo 失效
4. 使用 React DevTools Profiler 验证翻页/筛选时减少不必要渲染
**验收标准**:
- 列表翻页时 Table 组件不因 columns 引用变化触发全量渲染
- 所有列表页功能正常(排序、筛选、操作按钮)
- `pnpm build` 通过
---
### Task 10: API 层新代码统一为对象风格
**目标**: 确认新增 API 文件采用对象风格(`xxxApi.list()` 而非 `listXxx()`),修改已有文件时顺手迁移。
**涉及文件**:
- 修改: `apps/web/src/api/health/` 下近期新增的 API 文件(如 `alerts.ts``deviceReadings.ts`
**详细步骤**:
1. 审计 `apps/web/src/api/` 下所有文件,标记函数风格的文件清单
2. 近期新增的文件alerts、deviceReadings 等)统一改为对象风格:
```typescript
export const alertApi = {
list: (params) => client.get('/alerts', { params }),
acknowledge: (id) => client.post(`/alerts/${id}/acknowledge`),
};
```
3. 更新引用处的 import页面组件中的调用方式
4. 旧文件不强制迁移,仅记录待迁移清单
**验收标准**:
- `alerts.ts``deviceReadings.ts` 为对象风格导出
- 对应页面功能正常
- `pnpm build` 通过
---
## 执行原则
1. **每 Task 完成后立即提交** — 不积压,保持可追溯
2. **先基础设施后拆分** — Phase 1 的 hook 增强完成后再做 Phase 2 组件拆分
3. **每步验证** — 每个 Task 完成后 `pnpm build` 验证,拆分任务额外验证页面功能
4. **渐进迁移** — 重复模式统一采用渐进策略,不一次性全量迁移