Files
hms/docs/discussions/2026-06-25-analysis/04-multidevice-ux.md
iven 3351c68d10 docs: redact Redis 凭据明文 + 系统分析报告 + wiki 关键数字校正
PP-03 凭据泄露处置:
- 清除 wiki + 2 份历史文档中的 Redis 明文密码与公网 IP(4 文件 5 处)
- wiki 新增安全告警 + 症状导航条目
- 核实降级:泄露旧密码已失效,HMS 连本地 Redis,云端闲置;公网已关闭

系统深度分析(9 维度 + 6 主题多专家组):
- docs/discussions/2026-06-25-analysis/ 新增 7 文件
- 综合 6.8/10,4 CRITICAL,TOP 12 痛点,4 阶段路线图

wiki 关键数字校正(PP-02/05a fix 触发):
- 迁移数 175→176(m20260626_000170)
- 症状导航新增 device_readings 分区硬截止 + claim_next 注入修复条目
2026-06-26 09:07:35 +08:00

184 lines
18 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.
# 多端体验统一 — 主题综合
> 日期: 2026-06-25 | 分支: feat/media-library-banner | 主题负责人: 综合主持
> 范围: V1 上线后 6-12 个月,跨 Web / 小程序 / 适老化 / 无障碍的多端体验治理
> 证据基线: 所有论断附文件路径:行号,已逐项核验(见文末附录 A
---
## 一、主题愿景 (Vision)
**以"行为契约一致"取代"像素级对齐"作为多端统一的度量:** 同一份交互契约、同一份设计 Token 源、同一类用户在任何端完成同等质量的操作。视觉层尊重三端语境差异PC 鼠标精确点击 vs 手机触控 vs 老人手抖),但**状态语义层(错误/空/加载/危急)必须跨端一致**——同一类危急值告警在三端都拥有图标、朗读、重试与多通道触达。
当前 HMS 的多端体验是"4 处 token 副本 + 2 套请求层 + 隐式导航结构 + Web 无障碍盲区"的离散态:色值在 `variables.scss:5`(#C4623A 橙)、`App.tsx:153/175/194/218`(4 套硬编码 themeConfigs)、`index.css:12`(#2563EB 蓝)、`token-values.ts` 四处平行复制且已发生漂移Web 与小程序的缓存 TTL(5s vs 60s)、错误映射(无 vs 有)、Token 刷新(预检 vs 响应式)三处配置已发散。本主题的目标是**把"统一=改一处生效全端"从口号变为 CI 可校验的硬约束**,让设计系统从"审美问题"升级为"医疗可见性错误的根因防线"。
---
## 二、专家提案摘要
| 专家 | 核心切入点 | 关键提案 |
|------|-----------|---------|
| **设计系统架构师** | DTCG JSON 单一真相源 + 代码生成器分发三端 | DTCG tokens.json 单源 → Style Dictionary 生成 SCSS/CSS/antd themeConfigs语义化导航契约根治 TabBar 硬编码索引;跨端语义组件契约(AlertCard/EmptyState)Web 适老化主题补盲Figma-代码双向 sync |
| **前端架构师** | @hms/shared 跨端内核 + 契约驱动 + 性能预算 | 抽错误码/缓存策略/Token 状态机三大不变量为共享包token 单源不统一命名空间(--erp-* / --tk-* 双轨)Web Vitals 采集 + 性能预算门禁;路由表契约收敛 PP-09/PP-06error_code enum + codegen |
| **UX 研究员 / 无障碍专家** | 真实使用情境(医护效率 + 老年降级 + WCAG | dashboardNavMap 意图层契约;危急值多通道触达(震动/语音/横幅)Design Token v2 含四态语义 + WCAG 对比度门禁;情境感知适老化(行为信号推断);键盘快捷键 + 撤销模式 |
### 三专家共识点(已收敛为举措基础)
1. **路由/导航契约化**是 PP-06 + PP-09 共同根因解三方都提出措辞不同dashboardNavMap / NavigationContract / routeConfig 扩展)。
2. **设计 Token 单源 + 生成器**取代手工多副本(三方一致,分歧仅在命名是否统一)。
3. **危急值可达性 + WCAG 对比度门禁**是无障碍从主观转客观的唯一路径。
4. **Web 适老化主题是系统性盲区**,需补齐(小程序 58/58 覆盖Web 0
### 主要分歧(已在第四节调和)
- **token 命名空间**:设计系统架构师倾向 DTCG 语义命名重整,前端架构师主张保留双命名空间(--erp-* / --tk-*)只统一源。→ **采纳前端方案**(改名风险大,生成器桥接更安全)。
- **共享包粒度**:前端架构师主张 packages/shared 全量抽象,设计系统架构师主张 packages/design-tokens 独立。→ **分两包但同 monorepo**design-tokens 先行shared 内核第二轮。
- **token 跨端范围**UX 专家主张视觉 token 各端独立 + 语义 token 共享,设计系统架构师倾向全量统一。→ **采纳 UX 方案**(蓝橙调色板本质不同不强行统一,只统一语义层)。
---
## 三、战略举措 (Initiatives)
### 举措 1路由与导航契约层 — 根治 PP-09 死链与 PP-06 角标错位
**理由:** PP-09(4 Dashboard 6 条死链 navigate`AdminDashboard.tsx:51/69` 指向 `/health/follow-ups`/`/health/vital-signs`,实际路由是 `follow-up-tasks` / 体征路由不存在)与 PP-06(`useAlertPolling.ts:64` 写死 `setTabBarBadge({index:2})`,但 `app.config.ts:80` index 2=商城,告警应到 index 3=助手)的共同根因是**路由/Tab 配置为运行时字符串字面量与裸数字索引,无单一契约源**。影响面 100% 角色、100% 用户、上线即暴露。
**分阶段落地点:**
- **Phase 02 周):** 修 6 条死链 navigate + 清理 `AdminDashboard.tsx:88 value={healthDataStats ? 0 : 0}` 僵尸写法;`useAlertPolling.ts``resolveTabBarIndex('/pages/messages/index')` 动态查找。
- **Phase 11 个月):** 在 `routeConfig.ts` 扩展 `dashboardNavMap`(业务意图键 → path + permissions + 后端统计字段);抽 `useDashboardCard(intent)` hook 按权限过滤卡片CI 加 navigate 目标存在性单测(遍历 map 断言每个 path 在 routeConfig 存在)。
- **Phase 23 个月):** `packages/shared-contracts/navigation.ts` 跨端 NavigationContractWeb 侧边栏 + 小程序 TabBar 都从契约读取;权限字段统一从契约派生。
**工作量估算:** Phase 0 约 2-3 人日Phase 1 约 5-7 人日Phase 2 约 10-15 人日(跨端 + 类型系统差异)。
**预期影响:** 消灭医疗管理后台 403 死胡同与假数据展示,根除"TabBar 顺序一变就复发"的角标 bug 类。**高 / 中量**
**KPI** navigate 死链数 = 0CI 门禁TabBar 角标错位 bug 复发次数 = 0权限过滤后无权卡片不再渲染点击前拦截率 100%)。
**依赖:** 后端菜单 APIPhase 2 才需扩展 path 字段routeConfig 现有结构。
### 举措 2设计 Token 单源 + 代码生成器DTCG JSON
**理由:** 当前色值/字号在 4 处平行复制且已漂移:`variables.scss:5`(#C4623A 橙)、`App.tsx:153-218`(4 套硬编码 colorPrimary)、`index.css:12`(#2563EB 蓝)、`token-values.ts`。Web 完全缺失适老化主题与 motion/a11y token。纯 SCSS 无法跨仓库收敛,已发生手工漂移。
**分阶段落地点:**
- **Phase 11 个月):** 新建 `packages/design-tokens/`monorepo 首个共享包DTCG JSON 为唯一源Style Dictionary 生成三端产物(小程序 variables.scss + tokens.scss + token-values.tsWeb index.css :root + antd themeConfigs.tsCI 加产物与源同步 diff 检查。**保留双命名空间**(--erp-* / --tk-*),只统一源不强行改名。
- **Phase 23 个月):** 新增四态语义 token(`--tk-state-error/empty/loading/critical`) + Web 适老化 variantcontrolHeight 40→52, fontSize 14→18+ motion token(duration/easing 自动尊重 prefers-reduced-motion) + a11y token(focus-ring)Web 用户菜单增加"适老化"开关。
- **Phase 36 个月):** Figma Tokens 插件双向 sync + 视觉回归(Playwright + miniprogram-automator 核心页面截图比对) + WCAG 对比度自动化门禁(正文/背景 ≥4.5:1关怀模式 ≥7:1
**工作量估算:** Phase 1 约 8-10 人日(含 Style Dictionary 选型与构建集成Phase 2 约 12-15 人日Phase 3 约 15-20 人日(含视觉回归基线调优)。
**预期影响:** 消灭 token 漂移CI 硬约束),补齐 Web 适老化盲区,无障碍从主观评价转为 CI 红绿灯。**中 / 大量**
**KPI** token 副本数从 4 → 1CI 门禁Web 适老化主题覆盖 0 → ≥30 核心页面WCAG 对比度门禁通过率 100%token 变更影响面自动标注覆盖率 100%。
**依赖:** pnpm workspace 接入Antd ConfigProvider 对自定义 variant 的支持;团队 DTCG 命名培训。
### 举措 3跨端语义组件契约层 — AlertCard / EmptyState / VitalCard
**理由:** 不追求像素级跨端一致Ant Design vs 微信原生组件库是硬约束),而是为高频业务语义组件规定统一 TS 接口,让后端 DTO 字段名对齐组件 prop 名,减少 transform 层。优先 AlertCardPP-06 危急值可见性)与 EmptyState积分商城 Tab 空白 bug wiki 已记)。
**分阶段落地点:**
- **Phase 11 个月):** 定义 `packages/shared-contracts/components.ts` 中 AlertCardProps(severity: critical|warning|info, title, value, action) + EmptyStateProps(icon, title, action)Web 与小程序各自实现但 satisfy interfaceTS implements
- **Phase 23 个月):** 后端 `alert_dto.rs` 等响应字段与契约对齐Storybook(Web) + 组件 demo 页(小程序) 按契约生成 demo。
- **Phase 36 个月):** 扩展到 VitalCard / ErrorBoundaryFallback / LoadingState / StatusTag 共 6 类CI 跑视觉回归保证 prop 兼容。
**工作量估算:** Phase 1 约 5-7 人日Phase 2 约 8-10 人日Phase 3 约 12-15 人日。
**预期影响:** 消灭两端各自重复造轮子40+ 小程序组件与 Web Antd 隐式重复),减少前后端 DTO 不同步症状。**中 / 大量**
**KPI** 高频业务组件契约覆盖率AlertCard/EmptyState 优先);前端 transform 层代码行数下降DTO ↔ prop 字段对齐率。
**依赖:** 举措 2 的 token 层;两端 StatusTag severity 枚举映射层(小程序 abnormal/critical vs antd error/warning
### 举措 4危急值可达性 + 多通道触达(无障碍优先)
**理由:** 医疗安全信息必须多通道冗余WCAG 2.2 Status Updates + IEC 62366 alarms 原则)。当前危急值仅靠单一角标(且写错 Tab老年患者对延迟和字号容忍度更低。`hapticHeavy()` 已实现于 utils/haptic.ts 但未接入告警。
**分阶段落地点:**
- **Phase 02 周):** 修 `useAlertPolling.ts:64` 角标 index bug举措 1 联动);接入 hapticHeavy/medium。
- **Phase 11 个月):** 多通道触达:(a) 触觉 haptic(b) elder-mode 下 TTS 语音播报/预录提示音;(c) 页面顶部 sticky 红色横幅(不依赖 Tab 切换elder-mode 自动缩短轮询间隔 10s → 5s + 放大横幅字号。
- **Phase 23 个月):** Web 端补齐 aria-live所有数据加载/错误/告警区域加 role=status aria-live=politecritical 用 assertivee2e mock 验证角标出现在正确 Tab。
**工作量估算:** Phase 0 约 1 人日Phase 1 约 5-7 人日Phase 2 约 4-5 人日。
**预期影响:** 危急值不再被单一视觉通道埋没,视障医护可朗读工作台状态。**高 / 小量**
**KPI** Web aria-live 覆盖区域数(当前仅 1 处 PluginDashboardPage:448 → 目标 ≥20危急值多通道触达覆盖率 100%elder-mode 下告警延迟 ≤5s。
**依赖:** 举措 1 的 TabBar 契约;微信小程序 TTS 插件能力(可能降级预录音频)。
### 举措 5医护效率层 + 情境感知适老化(差异化体验)
**理由:** 医护 8 小时班次高频重复操作(确认告警/回复咨询/标记随访),每次点击 + modal + 确认造成腕劳损elder-mode 完全依赖用户主动开启,真正需要它的老年患者往往不知道。
**分阶段落地点:**
- **Phase 23 个月):** 键盘快捷键层J/K 选择、Enter 详情、C 确认告警、/ 聚焦搜索,参照 Linear/GitHub危险操作改"软删除 + 5 秒撤销"替代二次确认 modal告警/随访批量操作;小程序医生端左滑快速完成/右滑推迟。
- **Phase 36 个月):** `useAdaptiveMode()` hook 被动采集信号(连续校验失败 ≥2 次 / 触摸偏离中心 >40% / 停留时长超中位数 2 倍),顶部柔和提示邀请开启长辈模式(不静默改设置,信号只本地不上传);情感化关怀文案(异常体征不再冷冰冰,配合 AI 主动关怀)。
**工作量估算:** Phase 2 约 10-12 人日Phase 3 约 12-15 人日。
**预期影响:** 降低医护重复劳损与误操作,把适老化从"用户主动开启"升级为"系统主动识别并邀请"。**中 / 大量**
**KPI** 医护高频操作点击数下降 ≥30%elder-mode 主动邀请转化率;撤销模式误操作回退成功率。
**依赖:** 后端撤销 API先纯前端 5 秒延迟窗口,后端分批补);隐私政策声明(行为信号采集);法务审核情感文案。
---
## 四、专家分歧调和Dissenting Views → 最终取舍)
| 分歧 | 设计系统架构师 | 前端架构师 | UX/无障碍专家 | **最终取舍** |
|------|--------------|-----------|--------------|------------|
| token 命名空间 | DTCG 语义命名重整 | 保留双命名空间只统一源 | 视觉各端独立 + 语义共享 | **保留双命名 + 源统一 + 语义层共享**(改名风险大,语义层价值高) |
| 共享包结构 | packages/design-tokens 独立 | packages/shared 全量抽象 | 跨端共享 token v2 | **design-tokens 先行 + shared 内核第二轮**(降低首轮风险) |
| 跨端统一范围 | 全量统一 | 统一源不统一命名 | 视觉独立语义统一 | **视觉层各端独立,状态语义层跨端共享**(蓝橙调色板本质不同) |
| 优先级之争 | — | 与 DevOps 争优先级PP-01/02/04 vs 前端) | — | **PP-01/PP-02/PP-03 硬截止让路PP-09/PP-06 并行**(影响面 100% 用户,修复成本低) |
| 传输层抽象时机 | — | 先抽纯逻辑后抽传输层 | — | **采纳:第一轮只抽错误/缓存/状态机axios vs Taro 延迟** |
---
## 五、速赢Quick Wins1-2 周可落地)
1. **修 PP-09 工作台 6 条死链 navigate + value={0} 僵尸写法**`AdminDashboard.tsx:51``/health/follow-ups``/health/follow-up-tasks``:69` 删除或改真实体征路由;`:88` 删除 `value={healthDataStats ? 0 : 0}`)— 约 0.5 人日,影响面 100% 角色。
2. **修 PP-06 告警角标写错 Tab**`useAlertPolling.ts:64` index:2 → 按 `app.config.ts:81` pagePath `pages/messages/index` 动态查找)— 约 0.5 人日,根除"危急值被埋没在商城 Tab"。
3. **接入 hapticHeavy/medium 到告警**utils/haptic.ts 已实现未接线)— 约 0.5 人日,危急值触觉反馈。
4. **Web Vitals 采集 MVP**(引入 web-vitals ~1KBmain.tsx 采集 LCP/CLS/INP走现有 /analytics/batch 上报)— 约 1 人日,唯一不依赖后端可观测性落地的性能观测手段。
---
## 六、主题级风险Risks
1. **monorepo 引入增加构建复杂度**packages/ 目录首次创建pnpm workspace 接入需同步改 apps/web 与 apps/miniprogram 的 tsconfig paths小程序对依赖体积敏感shared 包须支持 tree-shaking 且零浏览器 API 依赖。**缓解:** 第一轮只抽纯逻辑层传输层延迟shared 包严格 side-effect free。
2. **DTCG 标准对多主题支持尚在草案**:项目 Web 有 4 套主题,需用 $extensions 字段扩展,可能偏离标准。**缓解:** Phase 1 先用单主题 + variant 覆盖,多主题 Phase 2 再处理。
3. **视觉回归基线维护成本高**:初始会有大量误报需调阈值。**缓解:** 先只对 5 个核心页面(首页/工作台/告警详情/患者详情/咨询)跑,逐步扩展。
4. **Web 适老化业务价值需产品确认**医疗管理后台多为医护使用elder 模式优先级可能低于小程序适老化。**缓解:** 先做 motion/a11y token医护通用受益elder variant 待产品确认。
5. **情境感知适老化涉及行为数据采集**:与 PP-12 合规通道缺失形成张力。**缓解:** 信号只在端内计算、不落库、不上传,隐私政策声明;保留手动开关且不静默改设置。
6. **跨端契约抽象过早绑定错误 shape**6 类语义组件契约若设计不当会绑定错误 API surface。**缓解:** 先落地 2 类AlertCard + EmptyState验证后再扩。
7. **DTCG/Figma 双向 sync 多人协作冲突**Figma 同时被多人改易冲突。**缓解:** 引入 lock 机制或约定"每周一次设计 sync 日"。
8. **键盘快捷键与浏览器/输入法冲突**:需可配置。**缓解:** 参照 Linear/GitHub 键位规范,提供设置页自定义。
---
## 七、路线Roadmap对齐总览 Phase 0-3
| 阶段 | 时间 | 本主题交付物 |
|------|------|------------|
| **Phase 0** | 0-2 周 | Quick Wins 1-3PP-09 死链 + PP-06 角标 + haptic 接入Web Vitals MVP |
| **Phase 1** | 1-3 个月 | 举措 1 Phase 1dashboardNavMap + CI 门禁);举措 2 Phase 1DTCG 单源 + 生成器 + CI diff 检查);举措 3 Phase 1AlertCard + EmptyState 契约);举措 4 Phase 1多通道触达 |
| **Phase 2** | 3-6 个月 | 举措 1 Phase 2跨端 NavigationContract举措 2 Phase 2四态语义 token + Web 适老化 variant + motion/a11y token举措 3 Phase 2DTO 对齐);举措 5 Phase 2键盘快捷键 + 撤销模式 + 批量操作) |
| **Phase 3** | 6-12 个月 | 举措 2 Phase 3Figma 双向 sync + 视觉回归 + WCAG 门禁);举措 3 Phase 36 类契约全覆盖);举措 5 Phase 3情境感知适老化 + 情感文案) |
---
## 附录 A证据核验清单已逐项验证
| 论断 | 证据 | 核验结果 |
|------|------|---------|
| Web 色值 #2563EB | `apps/web/src/index.css:12` `--erp-primary: #2563eb` | ✓ |
| 小程序色值 #C4623A | `apps/miniprogram/src/styles/variables.scss:5` `$pri: #C4623A` | ✓ |
| App.tsx 4 套硬编码 themeConfigs | `apps/web/src/App.tsx:153/175/194/218` colorPrimary | ✓ |
| PP-09 死链 follow-ups | `AdminDashboard.tsx:51` navigate('/health/follow-ups') vs `routeConfig.ts:60` 实际 /health/follow-up-tasks | ✓ |
| PP-09 死链 vital-signs | `AdminDashboard.tsx:69` navigate('/health/vital-signs')routeConfig 无此路由) | ✓ |
| 僵尸写法 value=0 | `AdminDashboard.tsx:88` `value={healthDataStats ? 0 : 0}` | ✓ |
| PP-06 角标 index:2 | `useAlertPolling.ts:64` setTabBarBadge({index:2}) vs `app.config.ts:80` index 2=商城 | ✓ |
| 告警应到 index 3 | `app.config.ts:81` index 3=助手(messages) | ✓ |
| Web CACHE_TTL 5s | `apps/web/src/api/client.ts:11` `CACHE_TTL = 5000` | ✓ |
| Web 预检 token 刷新 | `client.ts:63/74` isTokenExpiringSoon | ✓ |
| 小程序 ResponseCache 60s | `apps/miniprogram/src/services/request.ts:3/66` | ✓ |
| 小程序 ERROR_CODE_MAP | `request.ts:15/246` | ✓ |
| Web 无 error_code 映射 | client.ts grep 无 ERROR_CODE_MAP | ✓ |
| Web aria-live 仅 1 处 | `PluginDashboardPage.tsx:448` role="alert" | ✓ |
| Web 无 web-vitals | grep web-vitals/onLCP/onCLS/onINP 全 0 命中 | ✓ |
| Web 无 i18n | grep useTranslation/FormattedMessage 全 0 命中 | ✓ |
| Web 无 elder-mode | grep elder-mode/elderMode 全 0 命中 | ✓ |
| 无 packages 目录 | `ls packages` → NO packages dir | ✓ |
---
> 本主题与 00-INDEX.md 的 T4(僵尸 UI 清理)、PP-06(告警角标)、PP-09(Dashboard 死链)强关联,是这些上线前就绪项向"长期治理体制"的升格。