- T40 UI 审计计划和结果文档(docs/qa/) - wiki 更新:miniprogram 设计系统合规审计记录 + index 关键数字更新 - 审计 V2 完整报告(docs/audits/v2/) - 讨论记录文档(docs/discussions/) - 设计规格和实施计划(docs/superpowers/) - 角色测试计划和结果(docs/qa/role-test-*) - Docker 生产部署配置
295 lines
15 KiB
Markdown
295 lines
15 KiB
Markdown
---
|
||
title: Web 前端
|
||
updated: 2026-05-10
|
||
status: stable
|
||
tags: [frontend, react, antd, vite, spa]
|
||
---
|
||
|
||
# Web 前端
|
||
|
||
> 从 [[index]] 导航。关联: [[erp-server]] [[infrastructure]] [[erp-health]]
|
||
|
||
## 1. 设计决策
|
||
|
||
- **组件库优先** — Ant Design 6,不自造轮子
|
||
- **状态集中** — Zustand 管理全局状态(6 个 store)
|
||
- **API 层分离** — HTTP 调用封装到 `src/api/`(含 health/ 和 ai/ 子目录),组件不直接 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 后台管理布局(响应式,支持移动端):
|
||
|
||
```text
|
||
┌─────────────────────────────────────────────┐
|
||
│ LOGO 搜索... 🔔 5 👤 Admin ▾ │ ← 顶部导航栏
|
||
├─────────┬───────────────────────────────────┤
|
||
│ 📊 首页 │ │
|
||
│ 👥 用户 │ 主内容区域 │
|
||
│ 🔐 权限 │ (多标签页切换) │
|
||
│ 📋 流程 │ │
|
||
│ 💬 消息 │ │
|
||
│ ⚙️ 设置 │ │
|
||
│─────────│ │
|
||
│ 📦 进销存│ │
|
||
│ 🏭 生产 │ │
|
||
│ 💰 财务 │ │
|
||
│─────────│ │
|
||
│ ▸ 更多 │ │
|
||
└─────────┴───────────────────────────────────┘
|
||
```
|
||
|
||
### UI 规则
|
||
|
||
- 使用 Ant Design 组件库,不自造轮子
|
||
- 中文优先,所有文案通过 i18n key 引用
|
||
- 支持 4 套主题切换:信任蓝 / 温润东方 / 深邃夜色 / 翡翠清雅
|
||
- 侧边栏按模块分组:基础模块 / 行业模块
|
||
- 表单验证使用 Ant Design Form 的 validateRules
|
||
|
||
### 2.1 多主题系统(4 套内置主题)
|
||
|
||
> 自 2026-04-28 起采用多主题架构。用户可在顶栏主题切换器中选择偏好主题,选择持久化到 localStorage。
|
||
|
||
#### 4 套主题视觉人格
|
||
|
||
| 主题 | 主色 | 背景 | 圆角 | 性格 |
|
||
|------|------|------|------|------|
|
||
| **信任蓝** (blue) | `#2563EB` | `#F8FAFC` 冷灰 | 10/12/6px | 专业·企业 |
|
||
| **温润东方** (warm) | `#C4623A` | `#F5F0EB` 暖米 | 12/16/8px | 温润·人文 |
|
||
| **深邃夜色** (dark) | `#60A5FA` | `#0F172A` 深蓝黑 | 10/12/6px | 护眼·专注 |
|
||
| **翡翠清雅** (emerald) | `#5B7A5E` | `#F4F7F4` 浅绿灰 | 10/14/8px | 清新·健康 |
|
||
|
||
#### 技术架构
|
||
|
||
- **CSS 变量层** — `:root` 默认为 blue,`[data-theme='xxx']` 覆盖全部视觉 token(`apps/web/src/index.css`)
|
||
- **Ant Design 动态主题** — `ConfigProvider` 的 `theme` prop 按 ThemeName 选择不同配置(`apps/web/src/App.tsx`)
|
||
- **Zustand 持久化** — `useAppStore().theme` + `localStorage('hms-theme')`(`apps/web/src/stores/app.ts`)
|
||
- **暗色检测** — `useThemeMode()` hook 从 store 读取,不再比对色值(`apps/web/src/hooks/useThemeMode.ts`)
|
||
|
||
#### 温润东方风详细 Token(与小程序端共享)
|
||
|
||
> 小程序端源文件:`apps/miniprogram/src/styles/variables.scss` + `mixins.scss`
|
||
|
||
| 角色 | CSS 变量 | 色值 |
|
||
|------|----------|------|
|
||
| 主色 | `--erp-primary` | `#C4623A` |
|
||
| 背景 | `--erp-bg-page` | `#F5F0EB` |
|
||
| 容器 | `--erp-bg-container` | `#FFFFFF` |
|
||
| 主文字 | `--erp-text-primary` | `#2D2A26` |
|
||
| 次文字 | `--erp-text-secondary` | `#7A756E` |
|
||
| 边框 | `--erp-border` | `#E8E2DC` |
|
||
| 成功 | `--erp-success` | `#5B7A5E` |
|
||
| 警告 | `--erp-warning` | `#C4873A` |
|
||
| 错误 | `--erp-error` | `#B54A4A` |
|
||
|
||
#### 禁止事项
|
||
|
||
- 禁止紫色渐变、禁止 emoji 作图标
|
||
- 禁止左侧彩色边框卡片标示状态(改用 tag 标签)
|
||
- 禁止无意义的渐变背景
|
||
- 禁止装饰性 icon 遍地配
|
||
|
||
## 3. 关键文件 + 数据流
|
||
|
||
### 核心文件
|
||
|
||
| 文件 | 职责 |
|
||
|------|------|
|
||
| `apps/web/src/main.tsx` | React 入口 |
|
||
| `apps/web/src/App.tsx` | 路由定义 + Ant Design 动态主题(4 套) |
|
||
| `apps/web/src/layouts/MainLayout.tsx` | SaaS 后台管理布局 + ThemeSwitcher 集成 |
|
||
| `apps/web/src/stores/` | 4 个 Zustand store |
|
||
| `apps/web/src/components/ThemeSwitcher.tsx` | 主题选择下拉面板 |
|
||
| `apps/web/src/hooks/useThemeMode.ts` | 暗色模式检测(从 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|graph|dashboard|kanban/:name` | 插件多视图页面 |
|
||
|
||
**健康管理路由(25 条)**:
|
||
|
||
| 路径 | 页面 |
|
||
|------|------|
|
||
| `/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` | 咨询详情(含消息 + 导出) |
|
||
| `/health/articles` | 文章管理列表 |
|
||
| `/health/article-editor` | 文章编辑器(富文本) |
|
||
| `/health/article-categories` | 文章分类管理 |
|
||
| `/health/article-tags` | 文章标签管理 |
|
||
| `/health/points-rules` | 积分规则管理 |
|
||
| `/health/points-products` | 积分商品管理 |
|
||
| `/health/points-orders` | 积分订单列表 |
|
||
| `/health/statistics` | 统计概览(透析/化验/预约/体征上报率) |
|
||
| `/health/offline-events` | 线下活动管理 |
|
||
| `/health/ai-analysis` | AI 分析历史 |
|
||
| `/health/ai-prompts` | AI Prompt 管理 |
|
||
| `/health/ai-usage` | AI 用量统计 |
|
||
| `/health/media-library` | 媒体库管理(上传/文件夹/网格浏览) |
|
||
| `/health/banners` | 轮播图管理(表格+Drawer 表单+排序) |
|
||
|
||
### 健康模块共享组件(13 个)
|
||
|
||
| 组件 | 用途 |
|
||
|------|------|
|
||
| `StatusTag` | 预约/随访/咨询状态标签(含单元测试) |
|
||
| `PatientSelect` | 患者搜索选择器(远程搜索) |
|
||
| `DoctorSelect` | 医护搜索选择器(远程搜索) |
|
||
| `CalendarView` | 排班日历视图 |
|
||
| `ExportButton` | 咨询记录导出按钮 |
|
||
| `VitalSignsChart` | 多指标趋势图:概览卡片条(5 指标 sparkline) + 点击展开详情折线图 |
|
||
| `VitalSignsTab` | 患者详情-体征标签页 |
|
||
| `LabReportsTab` | 患者详情-化验报告标签页 |
|
||
| `HealthRecordsTab` | 患者详情-健康档案标签页 |
|
||
| `FollowUpTab` | 患者详情-随访标签页 |
|
||
| `ImagePreview` | 图片预览组件 |
|
||
| `MediaPicker` | 媒体库选图组件(复用于轮播图选图、文章封面选图) |
|
||
| `resolveMediaUrl()` | 媒体 URL 工具函数(`src/utils/media.ts`):自动处理路径前缀 + JWT token 拼接 |
|
||
|
||
### 集成契约
|
||
|
||
| 方向 | 模块 | 接口 | 触发时机 |
|
||
|------|------|------|---------|
|
||
| 调用 → | [[erp-server]] | `/api/v1/*` REST | 所有数据操作 |
|
||
| 调用 → | [[erp-server]] | `ws://localhost:3000/ws/*` | WebSocket |
|
||
| 消费 ← | 插件系统 | `plugin.toml` schema | 动态生成插件页面 |
|
||
| 调用 → | [[erp-health]] | `/api/v1/health/*` | 健康模块所有数据操作 |
|
||
|
||
## 4. 代码逻辑
|
||
|
||
### 状态管理(5 个 Zustand Store)
|
||
|
||
| Store | 状态 |
|
||
|-------|------|
|
||
| `app.ts` | theme(blue/warm/dark/emerald), sidebarCollapsed, localStorage 持久化 |
|
||
| `auth.ts` | user, isAuthenticated, localStorage 持久化 |
|
||
| `health.ts` | 患者/医生姓名缓存与批量解析 |
|
||
| `message.ts` | unreadCount, recentMessages, SSE 实时推送连接, 请求去重 |
|
||
| `plugin.ts` | plugins 列表, 动态菜单, schema 缓存, 请求去重 |
|
||
|
||
### 健康模块 API 文件(12 个)
|
||
|
||
| 文件 | 覆盖端点 |
|
||
|------|---------|
|
||
| `patients.ts` | 患者 CRUD + 标签 + 健康摘要 + 家庭成员 |
|
||
| `doctors.ts` | 医护档案 CRUD |
|
||
| `appointments.ts` | 预约 CRUD + 状态流转 |
|
||
| `healthData.ts` | 体征/化验/健康档案/趋势 |
|
||
| `followUp.ts` | 随访任务 + 记录 |
|
||
| `consultations.ts` | 咨询会话 + 消息 + 导出 |
|
||
| `articles.ts` | 健康文章 |
|
||
| `media.ts` | 媒体库 CRUD + 文件夹管理 + 批量操作 |
|
||
| `banners.ts` | 轮播图 CRUD + 排序 |
|
||
| `points.ts` | 积分系统 |
|
||
| `deviceReadings.ts` | 设备数据采集 |
|
||
| `alerts.ts` | 健康预警 |
|
||
|
||
### 前端单元测试(36 个测试文件)
|
||
|
||
| 类别 | 覆盖范围 |
|
||
|------|---------|
|
||
| Store 测试 | auth, health, plugin, workbench, app, message |
|
||
| Hook 测试 | useThemeMode, useDebouncedValue, useCountUp 等 |
|
||
| API 契约测试 | 25+ API 模块 URL/Method/参数验证 |
|
||
| 组件测试 | StatusTag, 健康常量, 表达式求值 |
|
||
|
||
> ⚠️ **审计发现**:Web 前端 225 个文件有 36 个测试文件 + 5 E2E spec,测试覆盖率仍需持续提升。小程序完全无测试。详见 `docs/audits/07-test-coverage.md`。
|
||
|
||
### 2026-04-30 审计发现
|
||
|
||
| 发现 | 严重性 | 说明 |
|
||
|------|--------|------|
|
||
| 告警管理按钮永远不显示 | CRITICAL | AlertList.tsx 使用 `health.alert.manage`(单数),后端声明 `health.alerts.manage`(复数),AuthButton 隐藏 |
|
||
| AI 分析 SSE 无 UI 入口 | MEDIUM | 4 个 SSE 端点(vital-signs/lab-report/health-trend/health-summary)前端未调用 |
|
||
| SSE 重连无指数退避 | MEDIUM | 依赖浏览器原生 EventSource,固定 3 秒间隔 |
|
||
| 前端测试极低 | MEDIUM | 163 文件仅 10 个测试 |
|
||
| 前端路由级权限控制缺失 | LOW | 健康模块路由无前端权限守卫,依赖后端 403 |
|
||
| AuthButton 覆盖率 26% | LOW | 13/50 声明权限码有 AuthButton,其余依赖 API 403 |
|
||
|
||
### 插件页面系统
|
||
|
||
插件通过 `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)
|
||
http://localhost:5174/uploads/* → http://localhost:3000/* (媒体文件,需 JWT)
|
||
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-05-10 | **媒体库 + 轮播图管理**:新增 MediaLibrary 页面(上传/文件夹/网格浏览)、BannerManage 页面(表格+Drawer+排序)、MediaPicker 组件(复用于轮播图和文章封面选图);新增 `resolveMediaUrl()` 工具函数统一处理媒体 URL;Vite 代理新增 `/uploads`;健康路由 22→25 条,共享组件 11→13 个,API 文件 10→12 个 |
|
||
| 2026-05-01 | 审计发现更新:CRITICAL 权限码拼写错误(alert→alerts)、前端测试极低、AI SSE 无入口 |
|
||
| 2026-04-28 | UI/UX 重构 Phase 5(小程序端 8 项优化):首页健康资讯+空状态引导、Hub sparkline bar+打卡合并、日常监测 3 分组折叠+异常高亮、预约时段灰显、咨询消息日期分组+图片预览、医护异常横幅+搜索、趋势图骨架屏 |
|
||
| 2026-04-28 | UI/UX 重构 Phase 4:4 个表单 Modal→DrawerForm(患者 4 分组/预约 3 分组+排班校验/随访 2 分组/积分商品 2 分组) |
|
||
| 2026-04-28 | UI/UX 重构 Phase 3:10 个列表页统一迁移至 PageContainer + usePaginatedData + EntityName + 共享格式化工具,移除手动 isDark 处理 |
|
||
| 2026-04-28 | UI/UX 重构 Phase 2:仪表盘角色自适应(useDashboardRole + DoctorDashboard/NurseDashboard/AdminDashboard/OperatorDashboard),删除快捷入口/积分排行/最近活动旧区块 |
|
||
| 2026-04-28 | UI/UX 重构 Phase 1:提取 6 个共享组件(PageContainer/EntityName/FilterBar/DrawerForm/FilterBar + dayjs/format 工具),12 个文件 dayjs 导入统一 |
|
||
| 2026-04-28 | 多主题系统:4 套主题(blue/warm/dark/emerald)+ CSS 变量 + Ant Design 动态主题 + ThemeSwitcher + useThemeMode 修复 |
|
||
| 2026-04-26 | 全面更新:22 条健康路由(+12 内容/积分/统计/活动/AI)、11 个共享组件、77 个 TSX 文件 |
|
||
| 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 节结构,更新为当前完整前端状态 |
|