diff --git a/apps/miniprogram/src/pages/consultation/index.tsx b/apps/miniprogram/src/pages/consultation/index.tsx index 4562b95..b54d155 100644 --- a/apps/miniprogram/src/pages/consultation/index.tsx +++ b/apps/miniprogram/src/pages/consultation/index.tsx @@ -3,6 +3,7 @@ import { View, Text } from '@tarojs/components'; import Taro, { useDidShow, usePullDownRefresh, useReachBottom } from '@tarojs/taro'; import { listConsultations, ConsultationSession } from '@/services/consultation'; import Loading from '../../components/Loading'; +import { useUIStore } from '../../stores/ui'; import './index.scss'; function getStatusTag(status: string) { @@ -33,6 +34,8 @@ export default function Consultation() { const [sessions, setSessions] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(''); + const { mode } = useUIStore(); + const modeClass = mode === 'elder' ? 'elder-mode' : ''; const [page, setPage] = useState(1); const [total, setTotal] = useState(0); const loadingRef = useRef(false); @@ -85,68 +88,84 @@ export default function Consultation() { }; return ( - - {/* 页头 */} - - 在线咨询 + + + {/* 副标题 */} 随时随地,连接专业医生 - - {/* 内容区 */} - {loading ? ( - - + {/* 发起咨询按钮 — 实心主色 */} + Taro.navigateTo({ url: '/pages/consultation/create/index' })} + > + 发起咨询 - ) : error ? ( - - {error} - - ) : sessions.length === 0 ? ( - - - + + {/* 内容区 */} + {loading ? ( + + - 暂无咨询记录 - 发起咨询后即可在这里与医生交流 - - ) : ( - - {sessions.map((session) => { - const tag = getStatusTag(session.status); - return ( - handleTapSession(session)} - > - - - - {session.subject || '在线咨询'} - - {tag.label} + ) : error ? ( + + {error} + + ) : sessions.length === 0 ? ( + + + + + 暂无咨询记录 + 发起咨询后即可在这里与医生交流 + + ) : ( + + {sessions.map((session) => { + const tag = getStatusTag(session.status); + const initial = (session.subject || '咨').charAt(0); + const isClosed = session.status === 'closed' || session.status === 'cancelled'; + return ( + handleTapSession(session)} + > + + {initial} + + + + + {session.subject || '在线咨询'} + + + {session.last_message_at + ? formatTime(session.last_message_at) + : formatTime(session.created_at)} + + + + {tag.label} + + + + {session.last_message || '暂无消息'} + + {session.unread_count_patient > 0 && ( + + + {session.unread_count_patient > 99 ? '99+' : session.unread_count_patient} + + + )} + - - {session.last_message || '暂无消息'} - - - {session.last_message_at - ? formatTime(session.last_message_at) - : formatTime(session.created_at)} - - {session.unread_count_patient > 0 && ( - - - {session.unread_count_patient > 99 ? '99+' : session.unread_count_patient} - - - )} - - ); - })} - - )} + ); + })} + + )} + ); } diff --git a/apps/miniprogram/src/pages/health/index.tsx b/apps/miniprogram/src/pages/health/index.tsx index 63c5f46..11047d9 100644 --- a/apps/miniprogram/src/pages/health/index.tsx +++ b/apps/miniprogram/src/pages/health/index.tsx @@ -3,6 +3,7 @@ import { View, Text, Input } from '@tarojs/components'; import Taro, { useDidShow, usePullDownRefresh } from '@tarojs/taro'; import { useHealthStore } from '../../stores/health'; import { useAuthStore } from '../../stores/auth'; +import { useUIStore } from '../../stores/ui'; import { inputVitalSign, getTrend, getHealthThresholds, findThreshold, DEFAULT_THRESHOLDS, type HealthThreshold } from '../../services/health'; import { listPendingSuggestions, type AiSuggestionItem } from '../../services/ai-analysis'; import Loading from '../../components/Loading'; @@ -42,6 +43,8 @@ interface TrendPoint { export default function Health() { const { todaySummary, loading, refreshToday, getTrend: fetchTrend } = useHealthStore(); const { user, currentPatient } = useAuthStore(); + const { mode } = useUIStore(); + const modeClass = mode === 'elder' ? 'elder-mode' : ''; const [activeTab, setActiveTab] = useState('blood_pressure'); const [systolic, setSystolic] = useState(''); const [diastolic, setDiastolic] = useState(''); @@ -211,7 +214,7 @@ export default function Health() { const dayLabels = ['日', '一', '二', '三', '四', '五', '六']; return ( - + {/* 页头 */} 健康数据 diff --git a/apps/miniprogram/src/pages/messages/index.tsx b/apps/miniprogram/src/pages/messages/index.tsx index f0af42b..d097b34 100644 --- a/apps/miniprogram/src/pages/messages/index.tsx +++ b/apps/miniprogram/src/pages/messages/index.tsx @@ -6,6 +6,7 @@ import { notificationService } from '../../services/notification'; import Loading from '../../components/Loading'; import GuestGuard from '../../components/GuestGuard'; import { useAuthStore } from '../../stores/auth'; +import { useUIStore } from '../../stores/ui'; import './index.scss'; type MsgTab = 'consultation' | 'notification'; @@ -29,6 +30,8 @@ const NOTIFY_ICONS: Record export default function Messages() { const user = useAuthStore((s) => s.user); + const { mode } = useUIStore(); + const modeClass = mode === 'elder' ? 'elder-mode' : ''; const [activeTab, setActiveTab] = useState('consultation'); const [sessions, setSessions] = useState([]); const [notifications, setNotifications] = useState([]); @@ -108,7 +111,7 @@ export default function Messages() { const unreadConsultCount = sessions.filter((s) => s.unread_count_patient > 0).length; return ( - + {/* 页头 */} 消息 diff --git a/apps/miniprogram/src/styles/elder-mode.scss b/apps/miniprogram/src/styles/elder-mode.scss index 254b25f..0528800 100644 --- a/apps/miniprogram/src/styles/elder-mode.scss +++ b/apps/miniprogram/src/styles/elder-mode.scss @@ -1,173 +1,445 @@ -// 长辈模式 CSS 覆写 -// 字号 ×1.3 / 间距 ×1.2 / 按钮 48→60px -// 通过页面根 View 添加 .elder-mode class 激活 +// 关怀模式 CSS 覆写(Phase 1 非线性放大策略) +// 策略:标题 ×1.15 / 正文 ×1.35 / 辅助 ×1.55 / 间距 ×1.4 +// 布局:体征网格 1 列 / 行高 1.7 / 触控 56px+ / 对比度增强 .elder-mode { - font-size: 36px; // 28 × 1.3 + font-size: 34px; // 28 × 1.2(根字号适度放大) + line-height: 1.7; // 从 1.5 提升到 1.7 // ─── 全局触控放大 ─── .vital-card, .checkin-card, .reminder-item, .menu-item, - .action-btn { - min-height: 60px; + .session-card, + .consult-card, + .notify-card, + .msg-segment-tab, + .vital-tab, + .period-btn, + .device-card, + .article-entry { + min-height: 56px; } - .action-btn { - height: 64px; + .action-btn, + .save-btn, + .consultation-create-btn, + .guest-login-btn { + height: 60px; } - .action-btn-text { - font-size: 22px; // 17 × 1.3 + .action-btn-text, + .save-btn-text, + .consultation-create-btn-text { + font-size: 21px; // 17 × 1.24 } - // ─── 首页 ─── + // ─── 对比度增强($tx3 → $tx2 等效) ─── + .greeting-date, + .vital-unit, + .reminder-count, + .reminder-arrow, + .reminder-tag, + .capsule.capsule-pending, + .vital-tag.tag-empty, + .input-label, + .input-ref, + .trend-bar-label, + .device-arrow, + .session-time, + .consult-time, + .notify-time, + .msg-segment-text, + .msg-segment-badge-text, + .consultation-subtitle, + .empty-hint, + .msg-empty-text, + .trend-empty-text, + .guest-empty-text, + .guest-article-summary, + .session-tag, + .consult-badge-text, + .session-badge-text { + color: #5A554F; // $tx2 覆盖 $tx3,提升对比度 + } + + // ═══════════════════════════════════════ + // 首页(标题 ×1.15 / 正文 ×1.35 / 辅助 ×1.55) + // ═══════════════════════════════════════ + .greeting-text { - font-size: 34px; // 26 × 1.3 + font-size: 30px; // 26 × 1.15 标题微增 + } + + .section-title { + font-size: 30px; // 26 × 1.15 } .checkin-title { - font-size: 21px; // 16 × 1.3 + font-size: 22px; // 16 × 1.35 正文显增 } .vital-label { - font-size: 17px; // 13 × 1.3 + font-size: 18px; // 13 × 1.38 } .vital-value { - font-size: 39px; // 30 × 1.3 + font-size: 34px; // 30 × 1.13(数值型标题,适度放大) } .vital-tag { - font-size: 14px; // 11 × 1.3 + font-size: 17px; // 11 × 1.55 辅助强增 padding: 3px 10px; } .capsule { - font-size: 14px; // 11 × 1.3 + font-size: 17px; // 11 × 1.55 padding: 4px 10px; } .reminder-title { - font-size: 20px; // 15 × 1.3 + font-size: 21px; // 15 × 1.4 } .reminder-text { - font-size: 17px; // 13 × 1.3 + font-size: 18px; // 13 × 1.38 } - .section-title { - font-size: 34px; // 26 × 1.3 + // 体征网格:2 列 → 1 列(解决溢出核心改动) + .vitals-grid { + grid-template-columns: 1fr; + gap: 14px; } - // ─── 个人页 ─── + // ═══════════════════════════════════════ + // 个人页 + // ═══════════════════════════════════════ + .profile-name { - font-size: 29px; // 22 × 1.3 + font-size: 26px; // 22 × 1.18 标题微增 } .stat-value { - font-size: 36px; // 28 × 1.3 + font-size: 34px; // 28 × 1.21 } .stat-label { - font-size: 17px; // 13 × 1.3 + font-size: 18px; // 13 × 1.38 } .menu-group-title { - font-size: 18px; // 14 × 1.3 + font-size: 20px; // 14 × 1.43 } .menu-label { - font-size: 20px; // 15 × 1.3 + font-size: 21px; // 15 × 1.4 } .menu-icon { - width: 44px; - height: 44px; + width: 48px; + height: 48px; border-radius: 14px; } .menu-icon-text { - font-size: 21px; // 16 × 1.3 + font-size: 22px; // 16 × 1.38 } .logout-text { - font-size: 18px; // 14 × 1.3 + font-size: 20px; // 14 × 1.43 } - // ─── 访客首页 ─── + // ═══════════════════════════════════════ + // 健康页 + // ═══════════════════════════════════════ + + .health-title { + font-size: 30px; // 26 × 1.15 + } + + .vital-tab-text, + .period-btn-text { + font-size: 20px; // 15 × 1.33 + } + + .input-field { + height: 64px; + font-size: 34px; // 28 × 1.21 + } + + .input-label, + .input-ref { + font-size: 18px; // 13 × 1.38 + } + + .device-name, + .article-entry-text { + font-size: 20px; // 15 × 1.33 + } + + .device-desc { + font-size: 18px; // 13 × 1.38 + } + + .ai-card-title { + font-size: 19px; // 14 × 1.36 + } + + .ai-suggestion-text { + font-size: 18px; // 13 × 1.38 + line-height: 1.7; + } + + .trend-bar-label { + font-size: 15px; // 11 × 1.36 + } + + // ═══════════════════════════════════════ + // 消息页 + // ═══════════════════════════════════════ + + .messages-title { + font-size: 30px; // 26 × 1.15 + } + + .consult-doctor, + .notify-title { + font-size: 20px; // 15 × 1.33 + } + + .consult-preview, + .notify-desc, + .session-message { + font-size: 18px; // 13 × 1.38 + line-height: 1.7; + } + + .consult-avatar-char { + font-size: 22px; // 18 × 1.22 + } + + .consult-badge-text, + .session-badge-text, + .msg-segment-badge-text { + font-size: 14px; // 10-11 × 1.3+ + } + + .msg-segment-badge { + min-width: 20px; + height: 20px; + } + + .notify-icon-char { + font-size: 20px; // 16 × 1.25 + } + + // ═══════════════════════════════════════ + // 咨询页 + // ═══════════════════════════════════════ + + .consultation-subtitle { + font-size: 19px; // 14 × 1.36 + } + + .empty-char { + font-size: 40px; // 32 × 1.25 + } + + .empty-title { + font-size: 22px; // 16 × 1.38 + } + + .session-subject { + font-size: 20px; // 15 × 1.33 + } + + .session-avatar-char { + font-size: 20px; // 16 × 1.25 + } + + .session-tag { + font-size: 14px; // 10 × 1.4 + padding: 3px 8px; + } + + .session-badge { + min-width: 22px; + height: 22px; + } + + // ═══════════════════════════════════════ + // 访客首页 + // ═══════════════════════════════════════ + .guest-slide-title { - font-size: 34px; // 26 × 1.3 + font-size: 30px; // 26 × 1.15 } .guest-slide-desc { - font-size: 21px; // 16 × 1.3 + font-size: 22px; // 16 × 1.38 } .guest-article-title { - font-size: 21px; // 16 × 1.3 - } - - .guest-article-summary { - font-size: 17px; // 13 × 1.3 + font-size: 22px; // 16 × 1.38 } .guest-login-btn { - height: 72px; // 56 × 1.3 + height: 64px; font-size: 26px; // 20 × 1.3 } + .guest-login-text { + font-size: 18px; // 13 × 1.38 + } + .guest-institution-name { - font-size: 21px; // 16 × 1.3 + font-size: 22px; // 16 × 1.38 } .guest-institution-desc { - font-size: 17px; // 13 × 1.3 + font-size: 18px; // 13 × 1.38 } - // ─── 登录页 ─── + // ═══════════════════════════════════════ + // 登录页 + // ═══════════════════════════════════════ + .login-title { - font-size: 62px; // 48 × 1.3 + font-size: 56px; // 48 × 1.17 标题微增 } .login-subtitle { - font-size: 34px; // 26 × 1.3 + font-size: 30px; // 26 × 1.15 } .login-btn { - height: 108px; // 96 × 1.13 - font-size: 36px; // 32 × 1.13 + height: 96px; + font-size: 34px; // 32 × 1.06 } .skip-btn { - font-size: 26px; // 20 × 1.3 - height: 64px; + font-size: 24px; // 20 × 1.2 + height: 60px; } - // ─── 间距放大 ×1.2 ─── + // ═══════════════════════════════════════ + // 间距放大 ×1.4(大于字号放大倍率,增加呼吸空间) + // ═══════════════════════════════════════ + .vitals-grid { gap: 14px; } .checkin-card { - padding: 24px; + padding: 28px; } .reminder-card { - padding: 22px; + padding: 24px; } .home-page, - .guest-page { - padding: 24px 28px 120px; + .guest-page, + .health-page, + .messages-page, + .consultation-body { + padding: 28px 32px 120px; } .profile-page { - padding: 24px 28px 120px; + padding: 28px 32px 120px; } .menu-item { - padding: 17px 18px; + padding: 18px 22px; + } + + .session-list, + .msg-list { + gap: 12px; + } + + .session-card, + .consult-card, + .notify-card { + padding: 20px; + } + + .vital-tabs, + .period-group { + gap: 10px; + } + + // ═══════════════════════════════════════ + // 共享组件 + // ═══════════════════════════════════════ + + // EmptyState + .empty-state-icon-char { + font-size: 56px; // 48 × 1.17 + color: #5A554F; // $tx2 + } + + .empty-state-text { + font-size: 34px; // 30 × 1.13 + } + + .empty-state-hint { + font-size: 28px; // 24 × 1.17 + color: #5A554F; // $tx2 + } + + .empty-state-action { + padding: 20px 56px; + } + + .empty-state-action-text { + font-size: 32px; // 28 × 1.14 + } + + // ErrorState + .error-state-icon { + font-size: 96px; // 80 × 1.2 + } + + .error-state-text { + font-size: 32px; // 28 × 1.14 + } + + .error-state-retry { + padding: 20px 56px; + } + + .error-state-retry-text { + font-size: 32px; // 28 × 1.14 + } + + // Loading + .loading-spinner { + width: 56px; + height: 56px; + } + + .loading-state-text { + font-size: 30px; // 26 × 1.15 + color: #5A554F; // $tx2 + } + + // StepIndicator + .step-dot { + width: 56px; + height: 56px; + font-size: 28px; // 24 × 1.17 + } + + .step-label { + font-size: 26px; // 22 × 1.18 + color: #5A554F; // $tx2 + } + + .step-line { + height: 4px; + top: 28px; } } diff --git a/docs/discussions/2026-05-09-miniprogram-elder-mode-ui-optimization.md b/docs/discussions/2026-05-09-miniprogram-elder-mode-ui-optimization.md new file mode 100644 index 0000000..125f8e4 --- /dev/null +++ b/docs/discussions/2026-05-09-miniprogram-elder-mode-ui-optimization.md @@ -0,0 +1,187 @@ +# 小程序关怀模式(长辈模式)UI 优化 — 多专家组头脑风暴 + +> 日期: 2026-05-09 | 参与专家: UX 设计师、无障碍专家、视觉设计师、交互设计师、前端工程师 + +## 背景 + +小程序关怀模式当前采用 CSS class 覆写方案(`.elder-mode`),通过字号 ×1.3、间距 ×1.2 的粗暴放大策略实现。实际效果:放大后文字截断、布局溢出、视觉混乱,仅覆盖 4/59 页面。需要重新思考整体方案。 + +--- + +## 专家组分析 + +### 专家 1:UX 设计师 — 信息架构与用户流程视角 + +**核心诊断:问题不在"放大",在"信息架构"** + +当前方案的本质是"把正常模式的东西做大",这从根本上就错了。关怀模式应该是一种**不同的信息呈现策略**,而不是简单的缩放。 + +**具体问题:** + +1. **信息密度不降反升** — 字号放大 1.3x 后,相同面积内可见信息量减少 ~40%,但页面没有重新组织内容层级,导致用户需要更多滚动才能看到同样多的信息 +2. **关键操作被挤出视口** — 首页的签到、体征卡片、提醒列表在放大后,CTA 按钮可能在首屏不可见 +3. **TabBar 交互模式不变** — 5 个 Tab(首页/健康/咨询/商城/我的)在关怀模式下仍然是 5 个,但每个 Tab 的可触控面积缩小了(因为文字更大,挤占空间) +4. **导航深度未简化** — 患者需要经过多层页面才能完成核心操作(如查看报告),关怀模式下滚动成本更高 + +**建议方向:** + +- **信息分层策略**:关怀模式只展示每个功能最核心的 2-3 个操作,次要功能收纳到"更多" +- **单屏聚焦**:每个屏幕只做一件事,减少认知负荷 +- **简化导航**:关怀模式下 TabBar 只保留 3 个核心 Tab(首页/健康/我的) +- **渐进式披露**:默认展示结论性信息("血压正常"),点击才看详情 + +--- + +### 专家 2:无障碍专家 — WCAG 与适老化标准视角 + +**核心诊断:当前方案远未达到 WCAG 2.1 AA 标准** + +**具体问题:** + +1. **最小字号严重违规** — 正常模式就有大量 10-13px 的文字(tag、标签、时间戳),关怀模式放大后仍有 14px 的文字(如 `.vital-tag`、`.capsule`),低于国标 GB/T 37668.1-2019 适老化要求的 ≥16px(等效值) +2. **触控区域不足** — 正常模式定义了 `$touch-min: 48px`,但关怀模式下很多元素的触控区域并未真正达到推荐值。WCAG 2.5.8 (Target Size Minimum) 要求 ≥24×24 CSS px(AA)或 ≥44×44 CSS px(AAA),微信小程序 rpx 换算后部分元素仍不达标 +3. **对比度问题** — `$tx3: #78716C` 在 `$bg: #F5F0EB` 上的对比度约 4.6:1,仅对 ≥24px 文字满足 AA 标准。关怀模式下仍使用了此色值的场景(如跳过链接 20px→26px),刚好达标但不舒适 +4. **缺少非视觉反馈** — 没有触觉反馈(haptic)、没有焦点指示器、没有 ARIA 属性 +5. **行间距不足** — 当前行高 `line-height: 1.5`,大字号下需要至少 1.6-1.8 的行高才能保证可读性 + +**建议方向:** + +- **建立最小字号底线**:关怀模式正文 ≥16px,辅助文字 ≥14px,禁用 10-13px +- **触控区域全局 44px+**:通过 mixin 强制所有可点击元素的最小尺寸 +- **提高对比度**:关怀模式下将 `$tx3` 替换为 `$tx2`,将 `$tx2` 替换为 `$tx` +- **增加行高**:关怀模式 `line-height` 从 1.5 提升到 1.7 +- **添加 focus/hover 状态**:所有可交互元素需要明确的视觉反馈 + +--- + +### 专家 3:视觉设计师 — 品牌一致性与美学视角 + +**核心诊断:放大破坏了视觉韵律和品牌调性** + +"温润东方风"的设计系统在正常模式下建立了清晰的视觉层级:标题 26px、正文 15-16px、辅助 11-13px、按钮 48px。这个层级通过"赤土橙 + 米白底"传达温润感。但粗暴放大后: + +**具体问题:** + +1. **层级坍塌** — 标题 26→34px、正文 16→21px、辅助 11→14px,三层之间的比例关系从 2.36:1.45:1 变成 2.43:1.5:1,差异微乎其微。**视觉层级消失了**,所有文字看起来一样大 +2. **空间失控** — 卡片内 padding 放大 ×1.2(20→24px),但字号放大 ×1.3(16→21px),内容增长速度 > 容器增长速度,导致文字挤压 +3. **卡片内容溢出** — 体征网格 `.vitals-grid` 的 gap 从 12→14px(+2px),但每张卡片内的数值从 30→39px(+9px),2 列布局在小屏上必然溢出 +4. **品牌色比例失衡** — 按钮从 56→64px 高度增加,但整个页面的留白区域(米白底)没有相应增加,橙色占比过高,失去了"温润"的视觉平衡 +5. **图标/文字比例失调** — `.menu-icon` 从 40→44px,但 `.menu-icon-text` 从 16→21px,图标内文字显得拥挤 + +**建议方向:** + +- **非线性放大**:标题 ×1.2、正文 ×1.35、辅助文字 ×1.5(拉大层级差距,而非等比缩小) +- **布局重排优于尺寸放大**:体征网格从 2 列改为 1 列,列表项从紧凑改为宽松 +- **增加呼吸空间**:关怀模式的间距放大倍率应 > 字号放大倍率(如间距 ×1.5,字号 ×1.3) +- **重新平衡色彩占比**:关怀模式下减少装饰性色彩元素,增加留白 + +--- + +### 专家 4:交互设计师 — 手势与反馈视角 + +**核心诊断:交互模式未适配老年用户认知特点** + +**具体问题:** + +1. **手势操作没有简化** — 仍依赖精准点击(如小型标签切换、下拉刷新、滑动删除),老年用户手部震颤/灵活度下降,精细操作困难 +2. **反馈延迟不可感知** — 点击后没有即时的视觉/触觉反馈,老年用户可能重复点击导致意外操作 +3. **返回导航不明确** — 小程序左上角返回按钮很小,老年用户习惯"返回"操作但可能找不到入口 +4. **错误状态不够明确** — toast 提示 1.5 秒消失太快,错误信息字号不放大 +5. **表单输入体验差** — 输入框字号放大但键盘不变,导致输入区域和键盘之间的视觉关联断裂 + +**建议方向:** + +- **点击反馈强化**:所有可点击元素添加 `active` 状态(缩放/变色)+ 可选的 `Taro.vibrateShort()` +- **增大返回按钮**:导航栏返回按钮触控区域从 44px 扩大到 56px+ +- **Toast 延长到 3 秒**:关怀模式下 toast 持续时间翻倍 +- **简化表单**:用选择器替代自由输入(日期选择器代替日期输入、下拉选择代替文本输入) +- **防重复点击**:按钮点击后 500ms 内禁用二次点击 + +--- + +### 专家 5:前端工程师 — 技术架构与实施方案视角 + +**核心诊断:CSS 覆写方案不可持续,需要架构升级** + +**具体问题:** + +1. **维护噩梦** — `elder-mode.scss` 已有 173 行,每新增一个组件都要记得加覆写。59 个页面 × 平均 5 个样式属性 = ~300 条覆写规则,这个文件会膨胀到无法维护 +2. **覆盖不全** — 当前只覆盖 4 个页面,8 个共享组件完全未适配 +3. **px 硬编码** — 正常模式大量使用 px 硬编码值,SCSS 变量未统一管理,关怀模式无法通过变量层级覆盖 +4. **无响应式** — 没有考虑不同屏幕尺寸下关怀模式的表现(iPhone SE vs iPhone 15 Pro Max) +5. **性能隐患** — 大量嵌套 CSS 选择器(`.elder-mode .page .component .element`)增加样式计算成本 + +**建议方向(三个方案递进):** + +#### 方案 A:SCSS 变量驱动(最小改动,短期) +- 所有字号/间距通过 SCSS 变量定义 +- 关怀模式通过覆盖变量集合实现 +- 估算工作量:3-5 人天 +- 缺点:仍需手动覆盖,不解决维护性问题 + +#### 方案 B:Design Token 系统(推荐,中期) +- 引入 design token(JSON/YAML 定义),编译为 SCSS 变量 + CSS 自定义属性 +- 关怀模式 = 一套不同的 token 值 +- 组件通过 token 引用样式,自动适配 +- 估算工作量:8-12 人天 +- 优点:可扩展、可维护、组件自动适配 + +#### 方案 C:布局重排系统(最优,长期) +- 不仅覆盖样式值,还允许组件切换布局模板 +- 例如:体征网格正常模式 2 列 → 关怀模式 1 列 +- 通过 React props 或 context 驱动 +- 估算工作量:15-20 人天 +- 优点:彻底解决布局溢出问题 + +**推荐路径:先 A 止血,再 B 建基,最后 C 完善** + +--- + +## 共识与分歧 + +### 专家组共识 + +1. **简单缩放是错误方向** — 所有专家一致认为"字号 ×1.3"的策略需要被替代 +2. **布局重排 > 尺寸放大** — 信息架构调整比样式缩放更重要 +3. **覆盖范围必须全局** — 不能只有 4 个页面适配 +4. **触控区域必须放大** — 44px 是底线,推荐 48-56px +5. **视觉层级需要强化** — 非线性放大,拉大标题/正文/辅助的差距 + +### 专家组分歧 + +| 议题 | UX 设计师 | 无障碍专家 | 视觉设计师 | 前端工程师 | +|------|----------|----------|----------|----------| +| TabBar 数量 | 减到 3 个 | 保持 5 个,加大间距 | 保持 5 个,调整视觉权重 | 保持 5 个,技术成本最低 | +| 放大策略 | 布局重排 | WCAG 为准 | 非线性放大 | Design Token | +| 优先级 | 先简化信息 | 先修复对比度 | 先修视觉层级 | 先建基础设施 | +| 页面内容量 | 大幅精简 | 确保可读 | 增加留白 | 不改变数据,只改变渲染 | + +### 建议综合方案 + +**Phase 1:止血(1-2 天)** +- 修复当前 4 个已适配页面的溢出问题 +- 调整放大倍率为非线性:标题 ×1.15、正文 ×1.3、辅助 ×1.5、间距 ×1.4 +- 将体征网格从 2 列改为 1 列 +- 提高对比度($tx3 → $tx2) + +**Phase 2:建基(3-5 天)** +- 建立 Design Token 系统(至少覆盖字号、间距、圆角、颜色) +- 创建 `useElderMode` React Context,组件自动读取 token +- 扩展覆盖到全部 59 个页面和 8 个共享组件 +- 添加触控反馈和 ARIA 属性 + +**Phase 3:优化(5-8 天)** +- 布局重排系统:允许组件在关怀模式下切换布局模板 +- 简化信息架构:关怀模式下隐藏次要信息 +- 优化表单交互:选择器替代自由输入 +- 全面 WCAG 审计 + +--- + +## 待定事项 + +1. **TabBar 方案**:保持 5 个还是减少到 3 个?需要用户调研数据支撑 +2. **Design Token 方案选型**:JSON 编译 vs CSS 自定义属性 vs SCSS 变量?需要技术验证 +3. **是否引入「超级关怀模式」**:比当前更激进的简化(如只有大字号列表 + 大按钮),作为第二级选项 +4. **与 Web 端适老化方案的关系**:Web 端是否也需要同步适配?如果需要,Design Token 系统需要跨端共享 +5. **测试验证方式**:是否需要找老年用户做可用性测试?