feat(miniprogram): Phase 5 UI/UX 优化 — 8 项改进
Some checks failed
CI / rust-check (push) Has been cancelled
CI / rust-test (push) Has been cancelled
CI / frontend-build (push) Has been cancelled
CI / security-audit (push) Has been cancelled

- 首页: 健康资讯推荐 + 空状态引导 + 快捷服务字符图标优化
- 健康 Hub: sparkline bar + 参考范围 + 打卡合并到快捷操作
- 日常监测: 3 分组折叠(晨间/晚间/其他) + 异常值高亮 + 提交前确认
- 预约: 已满时段 pointer-events:none + opacity 优化
- 咨询聊天: 消息日期分组(今天/昨天) + 图片预览
- 积分商城: 确认已有余额大字+签到+库存提示
- 医护工作台: 异常体征横幅 + 患者搜索入口 + 快捷操作扩展
- 趋势图表: 骨架屏加载状态 + ECharts 异常标记已有
This commit is contained in:
iven
2026-04-28 08:51:27 +08:00
parent 852a429ef3
commit 0e45778fc3
13 changed files with 693 additions and 286 deletions

View File

@@ -1,5 +1,5 @@
import { useState } from 'react';
import { View, Text } from '@tarojs/components';
import { View, Text, ScrollView } from '@tarojs/components';
import Taro, { useDidShow } from '@tarojs/taro';
import { useHealthStore } from '../../stores/health';
import { listDailyMonitoring, DailyMonitoring } from '../../services/health';
@@ -16,6 +16,27 @@ function getStatusTag(status?: string) {
return null;
}
/** 根据 status 计算 sparkline bar 的颜色 */
function getBarColor(status?: string): string {
if (status === 'normal') return 'bar-green';
if (status === 'high' || status === 'low') return 'bar-orange';
return 'bar-green';
}
/** 计算数值在参考范围中的位置百分比 (0-100) */
function getBarPercent(value: number | undefined, ref?: string): number {
if (!value || !ref) return 50;
const match = ref.match(/([\d.]+)\s*[-]\s*([\d.]+)/);
if (!match) return 50;
const low = parseFloat(match[1]);
const high = parseFloat(match[2]);
if (high <= low) return 50;
// 将值映射到 0-100 范围,参考范围占据中间 70%15%-85%
const range = high - low;
const normalized = (value - low + range * 0.3) / (range * 1.6);
return Math.max(5, Math.min(95, normalized * 100));
}
export default function Health() {
const { todaySummary, loading, refreshToday } = useHealthStore();
const { currentPatient } = useAuthStore();
@@ -63,10 +84,10 @@ export default function Health() {
const summary = todaySummary || {};
const items = [
{ label: '血压', value: summary.blood_pressure ? `${summary.blood_pressure.systolic}/${summary.blood_pressure.diastolic}` : '--/--', unit: 'mmHg', indicator: 'blood_pressure_systolic', status: summary.blood_pressure?.status, ref: summary.blood_pressure?.reference_range },
{ label: '心率', value: summary.heart_rate ? `${summary.heart_rate.value}` : '--', unit: 'bpm', indicator: 'heart_rate', status: summary.heart_rate?.status, ref: summary.heart_rate?.reference_range },
{ label: '血糖', value: summary.blood_sugar ? `${summary.blood_sugar.value}` : '--', unit: 'mmol/L', indicator: 'blood_sugar_fasting', status: summary.blood_sugar?.status, ref: summary.blood_sugar?.reference_range },
{ label: '体重', value: summary.weight ? `${summary.weight.value}` : '--', unit: 'kg', indicator: 'weight', status: summary.weight?.status, ref: summary.weight?.reference_range },
{ label: '血压', value: summary.blood_pressure ? `${summary.blood_pressure.systolic}/${summary.blood_pressure.diastolic}` : '--/--', unit: 'mmHg', indicator: 'blood_pressure_systolic', status: summary.blood_pressure?.status, ref: summary.blood_pressure?.reference_range, numValue: summary.blood_pressure?.systolic },
{ label: '心率', value: summary.heart_rate ? `${summary.heart_rate.value}` : '--', unit: 'bpm', indicator: 'heart_rate', status: summary.heart_rate?.status, ref: summary.heart_rate?.reference_range, numValue: summary.heart_rate?.value },
{ label: '血糖', value: summary.blood_sugar ? `${summary.blood_sugar.value}` : '--', unit: 'mmol/L', indicator: 'blood_sugar_fasting', status: summary.blood_sugar?.status, ref: summary.blood_sugar?.reference_range, numValue: summary.blood_sugar?.value },
{ label: '体重', value: summary.weight ? `${summary.weight.value}` : '--', unit: 'kg', indicator: 'weight', status: summary.weight?.status, ref: summary.weight?.reference_range, numValue: summary.weight?.value },
];
const quickActions = [
@@ -102,7 +123,7 @@ export default function Health() {
</View>
</View>
{/* 快捷操作 */}
{/* 快捷操作 + 打卡状态紧凑合并 */}
<View className='health-actions-row'>
{quickActions.map((a) => (
<View className='action-item' key={a.label} onClick={a.action}>
@@ -112,30 +133,22 @@ export default function Health() {
<Text className='action-label'>{a.label}</Text>
</View>
))}
</View>
{/* 打卡状态 */}
{checkinStatus && (
<View className='checkin-card'>
<View className='checkin-info'>
{checkinStatus.checked_in_today ? (
<>
<Text className='checkin-done'></Text>
{checkinStatus.consecutive_days > 0 && (
<Text className='checkin-streak'> {checkinStatus.consecutive_days} </Text>
)}
</>
) : (
<Text className='checkin-pending'></Text>
)}
</View>
{!checkinStatus.checked_in_today && (
<View className='checkin-go' onClick={goToMall}>
<Text className='checkin-go-text'></Text>
{checkinStatus && (
<View
className='action-item checkin-badge'
onClick={!checkinStatus.checked_in_today ? goToMall : undefined}
>
<View className={`action-icon ${checkinStatus.checked_in_today ? 'icon-accent' : 'icon-warn'}`}>
<Text className='action-char'></Text>
</View>
)}
</View>
)}
<Text className='action-label'>
{checkinStatus.checked_in_today
? (checkinStatus.consecutive_days > 0 ? `已打卡${checkinStatus.consecutive_days}` : '已打卡')
: '去打卡'}
</Text>
</View>
)}
</View>
{/* 今日体征概览 */}
<View className='health-section'>
@@ -146,6 +159,8 @@ export default function Health() {
<View className='vitals-grid'>
{items.map((item) => {
const tag = getStatusTag(item.status);
const barColor = getBarColor(item.status);
const barPercent = getBarPercent(item.numValue, item.ref);
return (
<View className='vital-card' key={item.label} onClick={() => goToTrend(item.indicator)}>
<Text className='vital-label'>{item.label}</Text>
@@ -154,6 +169,12 @@ export default function Health() {
<Text className='vital-unit'>{item.unit}</Text>
{tag && <Text className={`vital-tag ${tag.cls}`}>{tag.label}</Text>}
</View>
{/* Sparkline bar */}
{item.ref && item.numValue != null && (
<View className='vital-bar-track'>
<View className={`vital-bar-fill ${barColor}`} style={`width: ${barPercent}%`} />
</View>
)}
{item.ref && <Text className='vital-ref'> {item.ref}</Text>}
</View>
);
@@ -162,20 +183,20 @@ export default function Health() {
)}
</View>
{/* 趋势快捷入口 */}
{/* 趋势快捷入口 — 水平滚动卡片 */}
<View className='health-section'>
<Text className='section-title'></Text>
<View className='trend-row'>
<ScrollView className='trend-scroll' scrollX>
{trendLinks.map((t) => (
<View className='trend-item' key={t.label} onClick={() => goToTrend(t.indicator)}>
<View className='trend-icon'>
<Text className='trend-char'>{t.char}</Text>
<View className='trend-card' key={t.label} onClick={() => goToTrend(t.indicator)}>
<View className='trend-card-icon'>
<Text className='trend-card-char'>{t.char}</Text>
</View>
<Text className='trend-label'>{t.label}</Text>
<Text className='trend-arrow'></Text>
<Text className='trend-card-label'>{t.label}</Text>
<Text className='trend-card-arrow'> </Text>
</View>
))}
</View>
</ScrollView>
</View>
{/* 最近监测记录 */}