8.3 KiB
8.3 KiB
title, updated, status, tags
| title | updated | status | tags | |||||
|---|---|---|---|---|---|---|---|---|
| Web 前端 | 2026-04-25 | stable |
|
Web 前端
从 index 导航。关联: erp-server infrastructure erp-health
1. 设计决策
- 组件库优先 — Ant Design 6,不自造轮子
- 状态集中 — Zustand 管理全局状态(4 个 store)
- API 层分离 — HTTP 调用封装到
src/api/(28 个文件),组件不直接 fetch - 代理开发 — Vite 代理
/api到后端 3000 端口 - HashRouter — 不需要服务端 fallback 配置,部署更稳健
- 懒加载 — 除 Login 外所有页面使用
lazy()按需加载
版本(以实际 package.json 为准)
React 19.2.4 / Ant Design 6.3.5 / React Router 7.14.0 / Zustand 5.0.12 / Vite 8.0.4 / TypeScript 6.0.2
2. UI 规范
SaaS 后台布局
经典 SaaS 后台管理布局(响应式,支持移动端):
┌─────────────────────────────────────────────┐
│ LOGO 搜索... 🔔 5 👤 Admin ▾ │ ← 顶部导航栏
├─────────┬───────────────────────────────────┤
│ 📊 首页 │ │
│ 👥 用户 │ 主内容区域 │
│ 🔐 权限 │ (多标签页切换) │
│ 📋 流程 │ │
│ 💬 消息 │ │
│ ⚙️ 设置 │ │
│─────────│ │
│ 📦 进销存│ │
│ 🏭 生产 │ │
│ 💰 财务 │ │
│─────────│ │
│ ▸ 更多 │ │
└─────────┴───────────────────────────────────┘
UI 规则
- 使用 Ant Design 组件库,不自造轮子
- 中文优先,所有文案通过 i18n key 引用
- 支持暗色/亮色主题切换
- 侧边栏按模块分组:基础模块 / 行业模块
- 表单验证使用 Ant Design Form 的 validateRules
3. 关键文件 + 数据流
核心文件
| 文件 | 职责 |
|---|---|
apps/web/src/main.tsx |
React 入口 |
apps/web/src/App.tsx |
路由定义(公开 + 受保护) |
apps/web/src/layouts/MainLayout.tsx |
SaaS 后台管理布局 |
apps/web/src/stores/ |
4 个 Zustand store |
apps/web/src/api/ |
28 个 API 服务文件(含 7 个健康模块 API) |
apps/web/vite.config.ts |
Vite 配置 + API 代理 |
微信小程序(患者端)是独立前端项目,详见 miniprogram
路由结构
公开: /login
受保护(MainLayout 包裹):
| 路径 | 页面 |
|---|---|
/ |
首页 |
/users, /roles, /organizations |
用户/角色/组织管理 |
/workflow |
工作流 |
/messages |
消息中心 |
/settings |
系统设置 |
/plugins/admin, /plugins/market |
插件管理/市场 |
/plugins/:pluginId/:entityName |
插件 CRUD(动态生成) |
| `/plugins/:pluginId/tabs | tree |
健康管理路由(10 条):
| 路径 | 页面 |
|---|---|
/health/patients |
患者列表 |
/health/patients/:id |
患者详情(5 个标签页:基本信息/体征/化验/健康档案/随访) |
/health/patient-tags |
患者标签管理 |
/health/doctors |
医护管理 |
/health/schedules |
排班管理(含日历视图) |
/health/appointments |
预约管理(含状态流转) |
/health/follow-up-tasks |
随访任务列表 |
/health/follow-up-records |
随访记录 |
/health/consultations |
咨询会话列表 |
/health/consultations/:id |
咨询详情(含消息 + 导出) |
健康模块共享组件(12 个)
| 组件 | 用途 |
|---|---|
StatusTag |
预约/随访/咨询状态标签(含单元测试) |
PatientSelect |
患者搜索选择器(远程搜索) |
DoctorSelect |
医护搜索选择器(远程搜索) |
CalendarView |
排班日历视图 |
ExportButton |
咨询记录导出按钮 |
VitalSignsChart |
多指标趋势图:概览卡片条(5 指标 sparkline) + 点击展开详情折线图 |
VitalSignsTab |
患者详情-体征标签页 |
LabReportsTab |
患者详情-化验报告标签页 |
HealthRecordsTab |
患者详情-健康档案标签页 |
FollowUpTab |
患者详情-随访标签页 |
ImagePreview |
图片预览组件 |
集成契约
| 方向 | 模块 | 接口 | 触发时机 |
|---|---|---|---|
| 调用 → | erp-server | /api/v1/* REST |
所有数据操作 |
| 调用 → | erp-server | ws://localhost:3000/ws/* |
WebSocket |
| 消费 ← | 插件系统 | plugin.toml schema |
动态生成插件页面 |
| 调用 → | erp-health | /api/v1/health/* |
健康模块所有数据操作 |
4. 代码逻辑
状态管理(4 个 Zustand Store)
| Store | 状态 |
|---|---|
app.ts |
theme(light/dark), sidebarCollapsed |
auth.ts |
user, isAuthenticated, localStorage 持久化 |
message.ts |
unreadCount, recentMessages, 请求去重 |
plugin.ts |
plugins 列表, 动态菜单, schema 缓存, 请求去重 |
健康模块 API 文件(7 个)
| 文件 | 覆盖端点 |
|---|---|
patients.ts |
患者 CRUD + 标签 + 健康摘要 + 家庭成员 |
doctors.ts |
医护档案 CRUD |
appointments.ts |
预约 CRUD + 状态流转 |
healthData.ts |
体征/化验/健康档案/趋势 |
followUp.ts |
随访任务 + 记录 |
consultations.ts |
咨询会话 + 消息 + 导出 |
articles.ts |
健康文章 |
前端单元测试(3 个)
| 文件 | 测试内容 |
|---|---|
constants/health.test.ts |
健康常量定义验证 |
hooks/useThemeMode.test.ts |
暗色模式 hook |
pages/health/components/StatusTag.test.tsx |
状态标签渲染 |
插件页面系统
插件通过 plugin.toml schema 声明页面,前端根据 schema 动态生成:
PluginCRUDPage— 标准列表+表单PluginTabsPage— 标签页切换PluginTreePage— 树形展示PluginGraphPage— 关系图谱PluginKanbanPage— 看板视图PluginDashboardPage— 仪表盘
⚡ 不变量: 插件菜单由 plugin.ts store 从 API 动态获取,不硬编码
⚡ 不变量: API client 在请求前 30s 检查 token 过期,提前刷新避免 401
⚡ 不变量: DatePicker 返回 dayjs 对象,提交前必须 .format('YYYY-MM-DD') 转字符串
代理配置
http://localhost:5174/api/* → http://localhost:3000/* (API)
ws://localhost:5174/ws/* → ws://localhost:3000/* (WebSocket)
5. 活跃问题 + 陷阱
⚠️ Ant Design 6 废弃 destroyOnClose,应使用 destroyOnHidden
⚠️ Ant Design 6 废弃 API 警告(valueStyle/Spin tip/trailColor)已在历史版本中修复
⚠️ antd.setScaleParam 强制回流 64ms — antd 内部问题,无法直接修复
历史教训
- DatePicker 提交 dayjs 对象而非字符串 → 后端 422
birth_date trailing input— 必须调用.format('YYYY-MM-DD') - 预约表单医护字段标为可选但后端必填 → 400
doctor_id is required— 医护为必填(CAS 排班需要) - 趋势图后端 DTO
Vec<(NaiveDate, f64)>序列化为 JSON 数组[[date, value]]而非对象[{date, value}]→ 改用Vec<DataPoint>修复;前端extractData需同时处理ApiResponse包装和裸数组两种响应格式 IndicatorTimeseriesResp返回{ indicator, data: [...] }包装对象,不是裸数组 —Array.isArray(res)为 false,必须从res.data取值
6. 变更记录
| 日期 | 变更 |
|---|---|
| 2026-04-26 | 从 CLAUDE.md 迁移:UI 布局规范(§8) |
| 2026-04-26 | VitalSignsChart 重设计:概览卡片条 + 点击展开详情图,5 指标独立 Y 轴 |
| 2026-04-25 | 全面更新:10 条健康路由、12 个共享组件、7 个健康 API 文件、3 个单元测试 |
| 2026-04-24 | 添加小程序交叉引用 |
| 2026-04-23 | 重构为 5 节结构,更新为当前完整前端状态 |