docs: UI/UX 设计规格修订 — 补充 API 契约、组件接口、技术前置条件
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled

- 新增范围与前置条件(antd 6.x、dayjs 初始化、目标目录)
- 定义角色 code 映射表和判定逻辑
- 补充个人工作量 API 契约(GET /personal-stats)
- 新增 DrawerForm/FilterBar 组件接口定义
- 补充 dayjs 集中初始化方案
- 明确数据层策略(统一 usePaginatedData)
- 修正小程序:SVG 图标替代 emoji、sparkline 用 CSS/SVG
- 标记输入指示器为后续迭代
- 明确预约日历为现有组件增量优化
This commit is contained in:
iven
2026-04-28 01:31:42 +08:00
parent a66d59e86b
commit 1404cc8f1a

View File

@@ -2,6 +2,14 @@
> **日期**: 2026-04-28 | **状态**: 待审核 | **范围**: Web 管理后台 + 微信小程序
## 范围与前置条件
- **Web 端改动目标**: `apps/web/`React 19 SPA不涉及 `apps/desktop/`Tauri 桌面壳)
- **小程序改动目标**: `apps/miniprogram/`Taro 4.2 + React 18
- **UI 框架**: Ant Design 6.x已确认 `antd ^6.3.5`),使用 `styles.body` 替代已弃用的 `bodyStyle`
- **状态管理**: Zustand storesauth、theme 等)
- **日期库**: dayjs ^1.11.20,需集中初始化 `relativeTime` 插件 + `zh-cn` locale
## Context
HMS 健康管理平台前端当前存在"大而全"问题:仪表盘堆砌了 7 个信息区块但缺乏行动导向22 个列表页面各自为政3 种容器样式、3 种日期格式、暗色模式覆盖不完整),表单全用 Modal 弹窗无法承载复杂录入,小程序快捷服务用纯文字图标辨识度低。
@@ -23,7 +31,14 @@ HMS 健康管理平台前端当前存在"大而全"问题:仪表盘堆砌了 7
| 管理员 | 管理中心 | 数据优先 | 5 KPI + 趋势图 + 医护工作量 + 透析/化验/预约 Tab |
| 运营 | 运营中心 | 转化优先 | 积分发放/消费 + 热门文章 + 活动报名 + 排行 |
角色判定逻辑:从 JWT 中提取用户角色(`user.roles`),按优先级 doctor > nurse > admin > operator 匹配。
角色判定逻辑:从 `UserInfo.roles: RoleInfo[]` 中提取角色 code`roles.map(r => r.code)`),按优先级 `doctor > nurse > admin > operator` 匹配第一个命中。多角色用户取最高优先级角色对应的仪表盘。`RoleInfo` 结构:`{ id, name, code, description?, is_system }`
| 仪表盘角色 code | 匹配的 RBAC 角色 code |
|----------------|---------------------|
| `doctor` | `doctor` 或任何以 `doctor` 为前缀的 code |
| `nurse` | `nurse` 或任何以 `nurse` 为前缀的 code |
| `admin` | `admin``super_admin` |
| `operator` | `operator``content_manager` |
### 1.2 医生视图布局
@@ -120,8 +135,39 @@ HMS 健康管理平台前端当前存在"大而全"问题:仪表盘堆砌了 7
### 1.7 技术实现
- **组件**: 新建 `StatisticsDashboard/` 目录下 4 个角色组件 + 1 个路由组件
- **API**: 复用现有 `useStatsData` hook,按角色选择性请求
- **后端**: 新增"当前用户工作量"接口(我的患者数、随访完成率等个人维度数据)
- **API**: 复用现有 `useStatsData` hook 获取全局统计,新增个人维度请求
- **后端**: 新增个人工作量 API
#### 1.7.1 个人工作量 API 契约
```
GET /api/v1/health/dashboard/personal-stats
Authorization: Bearer <jwt>
Response 200:
{
"success": true,
"data": {
"my_patients": 186, // 我的患者数
"new_patients_this_month": 12, // 本月新增患者
"follow_up_rate": 0.92, // 随访完成率
"consultations_this_month": 47, // 本月咨询数
"pending_consultations": 5, // 待回复咨询
"vital_signs_report_rate": 0.78, // 体征上报率(患者维度)
"today_appointments": 6, // 今日预约数
"overdue_follow_ups": 8, // 逾期随访数
"today_follow_ups": 12, // 今日待随访数
"abnormal_vital_signs": 5, // 体征异常患者数
"vital_signs_reported": 142, // 已上报人数
"vital_signs_total": 182, // 应上报总人数
"pending_lab_reviews": 2 // 待审核化验数
}
}
```
#### 1.7.2 数据层策略
所有列表页必须统一使用 `usePaginatedData` hook 作为数据层(已有 `apps/web/src/hooks/usePaginatedData.ts`)。`PageContainer` 接收 `usePaginatedData` 返回值作为 props避免两套分页/筛选系统并存。目前仅 `VitalSignsTab``LabReportsTab``HealthRecordsTab``FollowUpTab` 使用了该 hook其余页面需迁移。
---
@@ -189,8 +235,19 @@ interface PageContainerProps {
### 2.5 共享工具
提取到 `utils/format.ts`
集中初始化 dayjs`utils/dayjs.ts`
```typescript
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import 'dayjs/locale/zh-cn';
dayjs.extend(relativeTime);
dayjs.locale('zh-cn');
export { dayjs };
```
提取到 `utils/format.ts`(依赖 `utils/dayjs.ts`
```typescript
import { dayjs } from './dayjs';
export const formatDate = (v: string) => dayjs(v).format('YYYY-MM-DD');
export const formatDateTime = (v: string) => dayjs(v).format('YYYY-MM-DD HH:mm');
export const formatRelative = (v: string) => dayjs(v).fromNow();
@@ -228,17 +285,52 @@ export const calcAge = (birthDate: string) => dayjs().diff(dayjs(birthDate), 'ye
| 积分商品 | Modal → **Drawer** | 7→8 | 双列 + 图片上传 |
| 文章编辑 | 独立页面(保持) | 富文本 | 全屏编辑器 |
### 3.3 表单分组规范
### 3.3 DrawerForm 组件接口
中等/复杂表单必须分组,每组有标题:
```typescript
interface FormSection {
title: string; // 分组标题
fields: React.ReactNode; // 该分组内的表单项
defaultCollapsed?: boolean; // 默认是否折叠
}
**患者表单分组:**
- 基本信息:姓名*、性别、出生日期、血型
- 联系方式:手机号、身份证号、地址
- 医疗信息:过敏史、备注
- 紧急联系人:姓名、电话、关系(新增)
interface DrawerFormProps {
title: string; // 抽屉标题
open: boolean;
onClose: () => void;
onSubmit: (values: Record<string, any>) => Promise<void>;
initialValues?: Record<string, any>;
loading?: boolean;
width?: number | string; // 默认 640
sections?: FormSection[]; // 分组模式
children?: React.ReactNode; // 非分组模式,直接传入表单项
columns?: 1 | 2; // 布局列数,默认 2
}
```
### 3.4 表单交互规范
### 3.4 FilterBar 组件接口
```typescript
// 组合模式FilterBar 提供布局容器,筛选控件由各页面自行组合
interface FilterBarProps {
children: React.ReactNode; // 筛选控件Input/Select/DatePicker 等)
onReset?: () => void; // 重置按钮回调
extra?: React.ReactNode; // 右侧额外操作(如导出按钮)
}
```
使用示例:
```tsx
<FilterBar onReset={handleReset}>
<Input placeholder="搜索姓名/手机号" />
<Select placeholder="状态" options={statusOptions} />
<DatePicker.RangePicker />
</FilterBar>
```
### 3.5 表单分组规范
### 3.6 表单交互规范
- 所有表单字段编辑时必须完整回填(当前患者编辑缺失 allergy_history/notes
- 双列布局中相关字段同行(姓名+性别、出生日期+血型)
@@ -254,14 +346,14 @@ export const calcAge = (birthDate: string) => dayjs().diff(dayjs(birthDate), 'ye
**改动点:**
- 顶部区域从浅色 → 深色渐变卡片,集成今日健康摘要(血压/心率/血糖三格)
- 无数据时显示引导文案:"今天还没录入数据,点击开始" + 录入按钮
- 快捷服务从纯文字图标 → emoji + 白色卡片日常上报、预约挂号、在线咨询、AI报告
- 快捷服务从纯文字图标 → SVG 图标 + 白色卡片日常上报、预约挂号、在线咨询、AI报告。使用微信小程序内置 icon 组件或项目内 SVG 资源,不使用 emoji跨设备渲染不一致
- 待办事项增加日期卡片视觉(左侧显示日期,右侧显示内容)
- 新增"健康资讯"推荐区块1-2 篇文章)
- 提醒角标:顶部右侧显示未读消息/异常提醒数
### 4.2 健康数据 Hub
- 体征卡片增加参考范围文字和微型趋势 sparkline
- 体征卡片增加参考范围文字和微型趋势 sparkline(使用纯 CSS/SVG 实现,不加载 ECharts 实例,避免小卡片内运行完整图表库)
- 打卡入口合并到顶部(当前独立卡片)
- 趋势链接优化为横向滚动卡片
@@ -274,13 +366,13 @@ export const calcAge = (birthDate: string) => dayjs().diff(dayjs(birthDate), 'ye
### 4.4 预约流程
- 创建页增加医生排班实时日历视图
- 选择日期后高亮可约时段,不可约时段灰显
- 选择日期后高亮可约时段,不可约时段灰显(增量优化现有 `WeekCalendar` 组件:为 `available_count === 0` 的时段添加灰显样式,不足时显示"当日无空位"提示)
- 预约详情页增加"修改预约"和"取消预约"操作
### 4.5 咨询聊天
- 消息按日期分组显示("今天"、"昨天"、具体日期)
- 增加"对方正在输入"状态提示
- 增加"对方正在输入"状态提示(需后端支持 WebSocket typing 事件Phase 5 暂不实现,移至后续迭代)
- 图片消息支持点击预览大图
### 4.6 积分商城