Files
hms/docs/superpowers/specs/2026-04-28-ui-ux-overhaul-design.md
iven ca32be59be
Some checks failed
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
docs: UI/UX 设计规格二轮修订 — 填充表单分组表、修正 any→unknown
2026-04-28 01:34:16 +08:00

22 KiB
Raw Blame History

HMS 前端 UI/UX 全面重构设计规格

日期: 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 弹窗无法承载复杂录入,小程序快捷服务用纯文字图标辨识度低。

本次重构目标:对标国内外一流医疗/SaaS 产品MyChart、丁香医生、Linear、Stripe按角色自适应原则重新设计信息架构统一 CRUD 页面模式,建立表单三级容器策略。


一、Web 仪表盘重构

1.1 角色自适应策略

仪表盘根据当前用户角色显示不同视图:

角色 视图名称 核心原则 主要内容
医生 今日工作台 行动优先 日程时间线 + 紧急提醒 + 化验审核 + 咨询消息
护士 随访监控台 监控优先 体征异常列表 + 随访队列 + 上报率 + 今日预约
管理员 管理中心 数据优先 5 KPI + 趋势图 + 医护工作量 + 透析/化验/预约 Tab
运营 运营中心 转化优先 积分发放/消费 + 热门文章 + 活动报名 + 排行

角色判定逻辑:从 UserInfo.roles: RoleInfo[] 中提取角色 coderoles.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 adminsuper_admin
operator operatorcontent_manager

1.2 医生视图布局

┌─────────────────────────────────────────────────┐
│ 问候语 + 今日摘要X个预约 · X条待处理  🔍 🔔 │
├─────────────────────────────────────────────────┤
│ [紧急] 张明华 血压 180/110  │ [注意] 2份化验待审 │
├─────────────────────┬───────────────────────────┤
│                     │  化验审核 (2待审)          │
│  今日日程           │  ├ 张明华 — 血常规  3项异常│
│  ├ 09:30 张明华 ←当前│  └ 陈建国 — 肝功能  正常  │
│  ├ 10:00 李秀英     ├───────────────────────────┤
│  ├ 11:00 陈建国     │  咨询消息 (5未读)          │
│  └ 14:00 赵丽芳     │  ├ 王小明 10分钟前         │
│                     │  └ 赵丽芳 1小时前          │
├─────────┬──────┬──────────┬────────────────────┤
│ 我的患者│随访率│ 本月咨询 │ 体征上报率          │
│ 186     │ 92%  │ 47       │ 78%                │
│ ↑12新增 │ ↑5%  │ 5待回复  │ ↓3%                │
└─────────┴──────┴──────────┴────────────────────┘

1.3 护士视图布局

┌─────────────────────────────────────────────────┐
│ 问候 + 今日待随访12人 · 体征异常5人              │
├─────────────────────────────────────────────────┤
│ 体征异常提醒 (5人异常)              查看全部 →   │
│  ├ 张明华 血压 180/110 · 上报08:30  [高危] 通知医生→│
│  └ 刘美华 血糖 12.5 · 上报07:45     [偏高] 通知医生→│
├─────────────────────┬───────────────────────────┤
│  今日随访队列 (12人) │  今日体征上报              │
│  [逾期] 王小明 血压  │       78%                 │
│  [逾期] 陈小红 用药  │  142已上报 │ 40未上报      │
│  [今日] 刘美华 用药  │       催报未上报患者 →     │
│  [今日] 赵丽芳 透析  │                            │
├─────────┬──────────────┬────────────────────────┤
│ 今日预约│ 随访完成率   │ 逾期随访               │
│ 6       │ 85%          │ 8                      │
└─────────┴──────────────┴────────────────────────┘

1.4 管理员视图布局

┌─────────────────────────────────────────────────┐
│ 管理中心 · 数据概览          [📅本月▾] [⬇️导出]  │
├───────┬───────┬───────┬───────┬─────────────────┤
│患者总数│本月预约│随访完成│体征上报│医护人数         │
│ 1,284 │ 356   │ 86%   │ 72%   │ 24              │
│ ↑8.2% │ ↓3.1% │ ↑5%   │ ↓2%   │ 3医21护         │
├─────────────────────────┬───────────────────────┤
│ 患者增长 & 预约趋势图   │ 医护工作量             │
│ [双线图: 新增+预约]     │ 王主任 186 ████████ 92%│
│                         │ 陈医生 142 ██████   70%│
│                         │ 李护士 28 随访 █████ 85%│
├─────────────────────────┴───────────────────────┤
│ [透析管理] 化验报告 预约分析 体征数据             │
│ 总记录 423 │ 并发症 2.3% │ 均超滤 2100ml │ 均时长245min │
└─────────────────────────────────────────────────┘

1.5 运营视图布局

┌─────────────────────────────────────────────────┐
│ 运营中心 · 积分、内容、活动        [📅本月▾]     │
├──────────┬──────────┬──────────┬────────────────┤
│ 积分发放 │ 积分消费 │ 文章发布 │ 活动报名        │
│ 12,450   │ 8,320   │ 18      │ 86              │
│ ↑15%     │ 消费率66.8%│ 阅读4520│ 3场进行中      │
├─────────────────────┬───────────────────────────┤
│ 积分消费排行        │ 热门文章                   │
│ 1 赵丽芳 3,200分    │ 高血压管理指南 阅读1,230   │
│ 2 王小明 2,800分    │ 透析饮食注意 阅读980       │
│ 3 刘美华 2,150分    │ 春季过敏预防 阅读756       │
├─────────────────────┴───────────────────────────┤
│ 线下活动                                         │
│ [高血压讲座 5/15 30/50人] [透析培训 5/22 18/30]  │
└─────────────────────────────────────────────────┘

1.6 砍掉的内容

当前区块 处置 原因
快捷入口8个图标 删除 与左侧导航完全重复
积分排行 Top10 移至运营视图 医护不需要看积分排行
最近活动 删除 复用了排行 columns未展示真正活动
健康数据中心(嵌套 Card>Card 拆分到管理员视图 4 面板数据医护无需关注

1.7 技术实现

  • 组件: 新建 StatisticsDashboard/ 目录下 4 个角色组件 + 1 个路由组件
  • 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避免两套分页/筛选系统并存。目前仅 VitalSignsTabLabReportsTabHealthRecordsTabFollowUpTab 使用了该 hook其余页面需迁移。


二、列表页统一规范

2.1 统一 PageContainer 组件

提取共享组件 components/PageContainer.tsx,解决 3 种容器样式 + 暗色模式缺失:

interface PageContainerProps {
  title: string;
  subtitle?: string;
  filters?: React.ReactNode;       // 筛选栏
  actions?: React.ReactNode;       // 右侧操作按钮
  batchActions?: React.ReactNode;  // 批量操作(选中时显示)
  selectedCount?: number;          // 选中数量
  onSelectAll?: () => void;
  onClearSelection?: () => void;
}

2.2 统一筛选栏规范

每个列表页至少 3 个筛选维度:

页面 筛选维度
患者管理 搜索(姓名/手机/身份证) + 状态 + 性别 + 注册日期范围
医生管理 搜索(姓名) + 科室 + 职称 + 在线状态
预约管理 状态 + 日期范围 + 患者搜索 + 预约类型
随访任务 状态 + 计划日期范围 + 随访类型 + 执行人
咨询管理 状态 + 日期范围
积分规则 类型 + 状态
积分商品 搜索(名称) + 类型 + 上架状态
积分订单 状态 + 日期范围
告警列表 状态 + 严重程度 + 规则名搜索 + 日期范围
文章管理 搜索(标题) + 分类 + 状态

2.3 统一表格列设计原则

  • UUID 永远不暴露给用户:后端必须返回 name 字段,前端兜底显示"未知患者"
  • 日期格式统一:全部使用 dayjs(v).format('YYYY-MM-DD HH:mm'),告警等时间敏感页面用 fromNow() + title 悬停绝对时间
  • 实体列合并:姓名列集成副标题(手机号/来源),状态列合并认证状态
  • 出生日期 → 年龄:列表页显示"42岁"比"1984-03-15"更实用
  • 增加 Checkbox 列:支持批量选择和操作(批量导出/删除)

2.4 各页面列调整

患者管理 (9列 → 8列)

  • 合并:姓名 + 来源 → 姓名列(副标题显示手机号 + 来源)
  • 合并:认证状态 → 状态列
  • 变更:出生日期 → 年龄
  • 变更:创建时间 → 最近就诊时间
  • 新增Checkbox 列

告警列表 (7列 → 6列)

  • 变更Patient ID → 患者姓名(可点击跳转)
  • 新增:严重程度筛选 + 日期范围筛选

随访任务 (7列 → 7列)

  • 变更:患者 ID → 患者姓名
  • 变更:执行人 ID → 执行人姓名
  • 新增:日期范围 + 类型 + 执行人筛选

2.5 共享工具

集中初始化 dayjsutils/dayjs.ts

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

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();
export const calcAge = (birthDate: string) => dayjs().diff(dayjs(birthDate), 'year');

提取到 components/EntityName.tsx

// 统一处理 name 缺失的兜底显示
<EntityName name={record.patient_name} id={record.patient_id} />
// name 存在 → 显示 name
// name 缺失 → 显示 "未知患者" + tooltip 显示 ID

三、表单设计规范

3.1 三级容器策略

复杂度 容器 布局 适用场景
简单 (≤6字段) Modal 弹窗 垂直单列 状态变更、简单编辑、分配
中等 (7-12字段) Drawer 抽屉 分组 + 双列网格 创建/编辑主实体
复杂 (>12字段) 独立页面 分步/分组 + 双列 完整建档、文章编辑

3.2 各页面表单容器决策

页面 当前 → 改进 字段数 布局
患者管理 Modal → Drawer 7→12 分组双列(基本/联系/医疗/紧急联系人)
医生管理 Modal保持 6 双列网格
预约管理 Modal → Drawer + 支持编辑 6→7 双列 + 排班校验反馈
随访填写 3 Modal → Modal + Drawer 5 填写记录用 Drawer
积分商品 Modal → Drawer 7→8 双列 + 图片上传
文章编辑 独立页面(保持) 富文本 全屏编辑器

3.3 DrawerForm 组件接口

interface FormSection {
  title: string;              // 分组标题
  fields: React.ReactNode;    // 该分组内的表单项
  defaultCollapsed?: boolean; // 默认是否折叠
}

interface DrawerFormProps {
  title: string;              // 抽屉标题
  open: boolean;
  onClose: () => void;
  onSubmit: (values: Record<string, unknown>) => Promise<void>;
  initialValues?: Record<string, any>;
  loading?: boolean;
  width?: number | string;    // 默认 640
  sections?: FormSection[];   // 分组模式
  children?: React.ReactNode; // 非分组模式,直接传入表单项
  columns?: 1 | 2;           // 布局列数,默认 2
}

3.4 FilterBar 组件接口

// 组合模式FilterBar 提供布局容器,筛选控件由各页面自行组合
interface FilterBarProps {
  children: React.ReactNode;   // 筛选控件Input/Select/DatePicker 等)
  onReset?: () => void;        // 重置按钮回调
  extra?: React.ReactNode;     // 右侧额外操作(如导出按钮)
}

使用示例:

<FilterBar onReset={handleReset}>
  <Input placeholder="搜索姓名/手机号" />
  <Select placeholder="状态" options={statusOptions} />
  <DatePicker.RangePicker />
</FilterBar>

3.5 表单分组规范

中等/复杂表单必须分组,每组有标题:

表单 分组 1 分组 2 分组 3 分组 4
患者 基本信息(姓名/性别/出生日期/血型) 联系方式(手机/身份证/地址) 医疗信息(过敏史/备注) 紧急联系人(姓名/电话/关系)
预约 患者信息(患者/类型/日期) 医生信息(医生/时段/排班状态)
随访 基本信息(患者/计划日期/类型) 填写内容(执行人/结果/备注)
积分商品 基本信息(名称/类型/积分/库存) 展示(图片/描述/上架状态)

3.6 表单交互规范

  • 所有表单字段编辑时必须完整回填(当前患者编辑缺失 allergy_history/notes
  • 双列布局中相关字段同行(姓名+性别、出生日期+血型)
  • 预约表单选择医生+日期后实时显示排班状态
  • 取消预约的取消原因用独立 Drawer 而非嵌套 Modal.confirm

四、小程序端重构

4.1 患者首页

改动点:

  • 顶部区域从浅色 → 深色渐变卡片,集成今日健康摘要(血压/心率/血糖三格)
  • 无数据时显示引导文案:"今天还没录入数据,点击开始" + 录入按钮
  • 快捷服务从纯文字图标 → SVG 图标 + 白色卡片日常上报、预约挂号、在线咨询、AI报告。使用微信小程序内置 icon 组件或项目内 SVG 资源,不使用 emoji跨设备渲染不一致
  • 待办事项增加日期卡片视觉(左侧显示日期,右侧显示内容)
  • 新增"健康资讯"推荐区块1-2 篇文章)
  • 提醒角标:顶部右侧显示未读消息/异常提醒数

4.2 健康数据 Hub

  • 体征卡片增加参考范围文字和微型趋势 sparkline使用纯 CSS/SVG 实现,不加载 ECharts 实例,避免小卡片内运行完整图表库)
  • 打卡入口合并到顶部(当前独立卡片)
  • 趋势链接优化为横向滚动卡片

4.3 日常监测表单

  • 8 个区块改为分组折叠:晨间体征(血压/心率)→ 晚间体征 → 其他(体重/血糖/出入量/备注)
  • 异常值实时提示:输入超出参考范围时红框高亮 + 文字提醒
  • 提交前校验摘要弹窗:"血压偏高 (160/95),确认提交?"

4.4 预约流程

  • 创建页增加医生排班实时日历视图
  • 选择日期后高亮可约时段,不可约时段灰显(增量优化现有 WeekCalendar 组件:为 available_count === 0 的时段添加灰显样式,不足时显示"当日无空位"提示)
  • 预约详情页增加"修改预约"和"取消预约"操作

4.5 咨询聊天

  • 消息按日期分组显示("今天"、"昨天"、具体日期)
  • 增加"对方正在输入"状态提示(需后端支持 WebSocket typing 事件Phase 5 暂不实现,移至后续迭代)
  • 图片消息支持点击预览大图

4.6 积分商城

  • 顶部积分余额大字显示 + 签到按钮
  • 商品卡片增加库存提示和兑换按钮
  • 分类 Tab 保留但优化视觉

4.7 医护工作台

  • 顶部增加异常体征提醒横幅(与 Web 医生视图紧急提醒联动)
  • 工作概览卡片改为图标 + 数字 + 标签三层结构
  • 新增患者搜索入口
  • 快捷操作增加"随访记录"、"排班查看"

4.8 趋势图表

  • ECharts 加载优化:懒加载 + 骨架屏
  • 异常值红点标记 + 点击显示详情
  • 时间范围选择器7天/30天/90天 按钮组

五、共享组件抽取

5.1 Web 端新增组件

组件 位置 用途
PageContainer components/PageContainer.tsx 统一页面容器(标题+筛选+表格+暗色模式)
EntityName components/EntityName.tsx 统一实体名称显示(兜底"未知"
RoleDashboard pages/health/StatisticsDashboard/ 角色自适应仪表盘路由
FilterBar components/FilterBar.tsx 统一筛选栏组件
DrawerForm components/DrawerForm.tsx 抽屉式表单容器(分组+双列)

5.2 小程序端新增组件

组件 位置 用途
HealthSummaryCard components/HealthSummaryCard/ 健康摘要卡片(带状态色)
DateCard components/DateCard/ 日期卡片(用于待办/日程)
AlertBanner components/AlertBanner/ 紧急提醒横幅
GroupedFormSection components/GroupedFormSection/ 分组折叠式表单区块

5.3 共享工具

工具 位置 用途
formatDate/formatDateTime/formatRelative/calcAge utils/format.ts 统一日期格式化

六、实施优先级

Phase 1: 基础组件1-2天

  1. 提取 PageContainer 组件
  2. 提取 EntityName 组件
  3. 提取 formatDate/formatDateTime/formatRelative/calcAge 工具函数
  4. 统一暗色模式处理

Phase 2: 仪表盘重构2-3天

  1. 实现 4 角色仪表盘组件
  2. 对接现有 API + 新增个人工作量 API
  3. 删除快捷入口/积分排行/最近活动区块

Phase 3: 列表页统一3-4天

  1. 各页面迁移到 PageContainer
  2. 补充筛选器(每页至少 3 个维度)
  3. 修复 UUID 暴露(后端 + 前端兜底)
  4. 统一日期格式 + 增加 Checkbox 列
  5. 调整表格列(合并/变更/新增)

Phase 4: 表单升级2-3天

  1. 实现 DrawerForm 组件
  2. 患者表单升级Drawer + 分组 + 新增字段
  3. 预约表单升级Drawer + 排班校验
  4. 随访填写升级Drawer + 完整回填
  5. 积分商品升级Drawer + 图片上传

Phase 5: 小程序重构3-4天

  1. 患者首页重设计(深色顶部 + 健康摘要)
  2. 健康数据 Hub 优化(参考范围 + sparkline
  3. 日常监测表单分组折叠
  4. 预约流程优化(排班日历)
  5. 咨询聊天优化(消息分组)
  6. 积分商城优化(余额+签到)
  7. 医护工作台重设计
  8. 趋势图表性能优化

Phase 6: 验收1天

  1. 全页面暗色模式测试
  2. 响应式布局测试
  3. 各角色权限测试

七、验证方式

  1. pnpm build — 前端生产构建通过
  2. 启动后端 + 前端服务,逐角色登录验证仪表盘
  3. 逐页面对比截图,确认每个列表页筛选/列/表单改进生效
  4. 小程序编译后真机预览,验证各页面改动
  5. 暗色模式全量截图对比