fix(miniprogram): 关怀模式非线性放大重构 + 3 页面接入
- elder-mode.scss: 等比×1.3改为非线性放大(标题×1.15/正文×1.35/辅助×1.55) - 体征网格从2列改为1列,解决放大后溢出问题 - 行高从1.5提升到1.7,对比度$tx3→$tx2增强可读性 - 健康页/消息页/咨询页接入useUIStore关怀模式 - 共享组件(EmptyState/ErrorState/Loading/StepIndicator)适配关怀模式 - 触控区域统一提升到56px+ Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -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<ConsultationSession[]>([]);
|
||||
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,11 +88,17 @@ export default function Consultation() {
|
||||
};
|
||||
|
||||
return (
|
||||
<View className='consultation-page'>
|
||||
{/* 页头 */}
|
||||
<View className='consultation-header'>
|
||||
<Text className='consultation-title'>在线咨询</Text>
|
||||
<View className={`consultation-page ${modeClass}`}>
|
||||
<View className='consultation-body'>
|
||||
{/* 副标题 */}
|
||||
<Text className='consultation-subtitle'>随时随地,连接专业医生</Text>
|
||||
|
||||
{/* 发起咨询按钮 — 实心主色 */}
|
||||
<View
|
||||
className='consultation-create-btn'
|
||||
onClick={() => Taro.navigateTo({ url: '/pages/consultation/create/index' })}
|
||||
>
|
||||
<Text className='consultation-create-btn-text'>发起咨询</Text>
|
||||
</View>
|
||||
|
||||
{/* 内容区 */}
|
||||
@@ -113,28 +122,35 @@ export default function Consultation() {
|
||||
<View className='session-list'>
|
||||
{sessions.map((session) => {
|
||||
const tag = getStatusTag(session.status);
|
||||
const initial = (session.subject || '咨').charAt(0);
|
||||
const isClosed = session.status === 'closed' || session.status === 'cancelled';
|
||||
return (
|
||||
<View
|
||||
key={session.id}
|
||||
className='session-card'
|
||||
className={`session-card ${isClosed ? 'session-card-closed' : ''}`}
|
||||
onClick={() => handleTapSession(session)}
|
||||
>
|
||||
<View className='session-main'>
|
||||
<View className='session-avatar'>
|
||||
<Text className='session-avatar-char'>{initial}</Text>
|
||||
</View>
|
||||
<View className='session-body'>
|
||||
<View className='session-top'>
|
||||
<Text className='session-subject'>
|
||||
{session.subject || '在线咨询'}
|
||||
</Text>
|
||||
<Text className={`session-tag ${tag.cls}`}>{tag.label}</Text>
|
||||
</View>
|
||||
<Text className='session-message'>
|
||||
{session.last_message || '暂无消息'}
|
||||
</Text>
|
||||
<Text className='session-time'>
|
||||
{session.last_message_at
|
||||
? formatTime(session.last_message_at)
|
||||
: formatTime(session.created_at)}
|
||||
</Text>
|
||||
</View>
|
||||
<View className='session-meta'>
|
||||
<Text className={`session-tag ${tag.cls}`}>{tag.label}</Text>
|
||||
</View>
|
||||
<View className='session-message-row'>
|
||||
<Text className='session-message'>
|
||||
{session.last_message || '暂无消息'}
|
||||
</Text>
|
||||
{session.unread_count_patient > 0 && (
|
||||
<View className='session-badge'>
|
||||
<Text className='session-badge-text'>
|
||||
@@ -143,10 +159,13 @@ export default function Consultation() {
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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<VitalType>('blood_pressure');
|
||||
const [systolic, setSystolic] = useState('');
|
||||
const [diastolic, setDiastolic] = useState('');
|
||||
@@ -211,7 +214,7 @@ export default function Health() {
|
||||
const dayLabels = ['日', '一', '二', '三', '四', '五', '六'];
|
||||
|
||||
return (
|
||||
<View className='health-page'>
|
||||
<View className={`health-page ${modeClass}`}>
|
||||
{/* 页头 */}
|
||||
<View className='health-header'>
|
||||
<Text className='health-title'>健康数据</Text>
|
||||
|
||||
@@ -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<string, { icon: string; bg: string; color: string }>
|
||||
|
||||
export default function Messages() {
|
||||
const user = useAuthStore((s) => s.user);
|
||||
const { mode } = useUIStore();
|
||||
const modeClass = mode === 'elder' ? 'elder-mode' : '';
|
||||
const [activeTab, setActiveTab] = useState<MsgTab>('consultation');
|
||||
const [sessions, setSessions] = useState<ConsultationSession[]>([]);
|
||||
const [notifications, setNotifications] = useState<NotificationItem[]>([]);
|
||||
@@ -108,7 +111,7 @@ export default function Messages() {
|
||||
const unreadConsultCount = sessions.filter((s) => s.unread_count_patient > 0).length;
|
||||
|
||||
return (
|
||||
<View className='messages-page'>
|
||||
<View className={`messages-page ${modeClass}`}>
|
||||
{/* 页头 */}
|
||||
<View className='messages-header'>
|
||||
<Text className='messages-title'>消息</Text>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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. **测试验证方式**:是否需要找老年用户做可用性测试?
|
||||
Reference in New Issue
Block a user