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 注入修复条目
184 lines
18 KiB
Markdown
184 lines
18 KiB
Markdown
# 多端体验统一 — 主题综合
|
||
|
||
> 日期: 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-06;error_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 0(2 周):** 修 6 条死链 navigate + 清理 `AdminDashboard.tsx:88 value={healthDataStats ? 0 : 0}` 僵尸写法;`useAlertPolling.ts` 改 `resolveTabBarIndex('/pages/messages/index')` 动态查找。
|
||
- **Phase 1(1 个月):** 在 `routeConfig.ts` 扩展 `dashboardNavMap`(业务意图键 → path + permissions + 后端统计字段);抽 `useDashboardCard(intent)` hook 按权限过滤卡片;CI 加 navigate 目标存在性单测(遍历 map 断言每个 path 在 routeConfig 存在)。
|
||
- **Phase 2(3 个月):** `packages/shared-contracts/navigation.ts` 跨端 NavigationContract,Web 侧边栏 + 小程序 TabBar 都从契约读取;权限字段统一从契约派生。
|
||
|
||
**工作量估算:** Phase 0 约 2-3 人日;Phase 1 约 5-7 人日;Phase 2 约 10-15 人日(跨端 + 类型系统差异)。
|
||
**预期影响:** 消灭医疗管理后台 403 死胡同与假数据展示,根除"TabBar 顺序一变就复发"的角标 bug 类。**高 / 中量**
|
||
**KPI:** navigate 死链数 = 0(CI 门禁);TabBar 角标错位 bug 复发次数 = 0;权限过滤后无权卡片不再渲染(点击前拦截率 100%)。
|
||
**依赖:** 后端菜单 API(Phase 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 1(1 个月):** 新建 `packages/design-tokens/`(monorepo 首个共享包),DTCG JSON 为唯一源;Style Dictionary 生成三端产物(小程序 variables.scss + tokens.scss + token-values.ts;Web index.css :root + antd themeConfigs.ts);CI 加产物与源同步 diff 检查。**保留双命名空间**(--erp-* / --tk-*),只统一源不强行改名。
|
||
- **Phase 2(3 个月):** 新增四态语义 token(`--tk-state-error/empty/loading/critical`) + Web 适老化 variant(controlHeight 40→52, fontSize 14→18)+ motion token(duration/easing 自动尊重 prefers-reduced-motion) + a11y token(focus-ring);Web 用户菜单增加"适老化"开关。
|
||
- **Phase 3(6 个月):** 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 → 1(CI 门禁);Web 适老化主题覆盖 0 → ≥30 核心页面;WCAG 对比度门禁通过率 100%;token 变更影响面自动标注覆盖率 100%。
|
||
**依赖:** pnpm workspace 接入;Antd ConfigProvider 对自定义 variant 的支持;团队 DTCG 命名培训。
|
||
|
||
### 举措 3:跨端语义组件契约层 — AlertCard / EmptyState / VitalCard
|
||
|
||
**理由:** 不追求像素级跨端一致(Ant Design vs 微信原生组件库是硬约束),而是为高频业务语义组件规定统一 TS 接口,让后端 DTO 字段名对齐组件 prop 名,减少 transform 层。优先 AlertCard(PP-06 危急值可见性)与 EmptyState(积分商城 Tab 空白 bug wiki 已记)。
|
||
|
||
**分阶段落地点:**
|
||
- **Phase 1(1 个月):** 定义 `packages/shared-contracts/components.ts` 中 AlertCardProps(severity: critical|warning|info, title, value, action) + EmptyStateProps(icon, title, action);Web 与小程序各自实现但 satisfy interface(TS implements)。
|
||
- **Phase 2(3 个月):** 后端 `alert_dto.rs` 等响应字段与契约对齐;Storybook(Web) + 组件 demo 页(小程序) 按契约生成 demo。
|
||
- **Phase 3(6 个月):** 扩展到 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 0(2 周):** 修 `useAlertPolling.ts:64` 角标 index bug(举措 1 联动);接入 hapticHeavy/medium。
|
||
- **Phase 1(1 个月):** 多通道触达:(a) 触觉 haptic;(b) elder-mode 下 TTS 语音播报/预录提示音;(c) 页面顶部 sticky 红色横幅(不依赖 Tab 切换);elder-mode 自动缩短轮询间隔 10s → 5s + 放大横幅字号。
|
||
- **Phase 2(3 个月):** Web 端补齐 aria-live:所有数据加载/错误/告警区域加 role=status aria-live=polite(critical 用 assertive);e2e 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 2(3 个月):** 键盘快捷键层(J/K 选择、Enter 详情、C 确认告警、/ 聚焦搜索,参照 Linear/GitHub);危险操作改"软删除 + 5 秒撤销"替代二次确认 modal;告警/随访批量操作;小程序医生端左滑快速完成/右滑推迟。
|
||
- **Phase 3(6 个月):** `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 Wins,1-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 ~1KB,main.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-3(PP-09 死链 + PP-06 角标 + haptic 接入);Web Vitals MVP |
|
||
| **Phase 1** | 1-3 个月 | 举措 1 Phase 1(dashboardNavMap + CI 门禁);举措 2 Phase 1(DTCG 单源 + 生成器 + CI diff 检查);举措 3 Phase 1(AlertCard + EmptyState 契约);举措 4 Phase 1(多通道触达) |
|
||
| **Phase 2** | 3-6 个月 | 举措 1 Phase 2(跨端 NavigationContract);举措 2 Phase 2(四态语义 token + Web 适老化 variant + motion/a11y token);举措 3 Phase 2(DTO 对齐);举措 5 Phase 2(键盘快捷键 + 撤销模式 + 批量操作) |
|
||
| **Phase 3** | 6-12 个月 | 举措 2 Phase 3(Figma 双向 sync + 视觉回归 + WCAG 门禁);举措 3 Phase 3(6 类契约全覆盖);举措 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 死链)强关联,是这些上线前就绪项向"长期治理体制"的升格。
|