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

@@ -73,6 +73,26 @@
}
}
.msg-date-divider {
display: flex;
justify-content: center;
padding: 16px 0 12px;
&__text {
font-size: 22px;
color: #94A3B8;
background: #F1F5F9;
padding: 4px 16px;
border-radius: 8px;
}
}
.msg-image {
width: 320px;
border-radius: 12px;
margin-top: 4px;
}
.msg-time {
font-size: 20px;
color: $tx3;

View File

@@ -1,5 +1,5 @@
import { useState, useEffect, useRef } from 'react';
import { View, Text, Input, ScrollView } from '@tarojs/components';
import { View, Text, Input, Image, ScrollView } from '@tarojs/components';
import Taro, { useRouter } from '@tarojs/taro';
import {
getSession,
@@ -116,6 +116,24 @@ export default function ConsultationDetail() {
return d.toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' });
};
const getDateLabel = (dateStr: string): string => {
const d = new Date(dateStr);
const today = new Date();
const yesterday = new Date();
yesterday.setDate(yesterday.getDate() - 1);
const dStr = d.toDateString();
if (dStr === today.toDateString()) return '今天';
if (dStr === yesterday.toDateString()) return '昨天';
return d.toLocaleDateString('zh-CN', { month: 'long', day: 'numeric' });
};
const isDifferentDay = (a: string, b: string): boolean => {
return new Date(a).toDateString() !== new Date(b).toDateString();
};
const isImageUrl = (url: string) => /\.(jpg|jpeg|png|gif|webp)(\?.*)?$/i.test(url);
if (loading) return <Loading />;
const isOpen = session?.status !== 'closed';
@@ -137,11 +155,28 @@ export default function ConsultationDetail() {
>
{messages.map((msg, idx) => {
const isSelf = msg.sender_role === 'patient';
const showDateDivider = idx === 0 || isDifferentDay(msg.created_at, messages[idx - 1].created_at);
return (
<View key={msg.id} id={`msg-${idx + 1}`} className={`msg-row ${isSelf ? 'msg-row--self' : ''}`}>
<View className={`msg-bubble ${isSelf ? 'msg-bubble--self' : 'msg-bubble--other'}`}>
<Text className='msg-text'>{msg.content}</Text>
<Text className='msg-time'>{formatTime(msg.created_at)}</Text>
<View key={msg.id}>
{showDateDivider && (
<View className='msg-date-divider'>
<Text className='msg-date-divider__text'>{getDateLabel(msg.created_at)}</Text>
</View>
)}
<View id={`msg-${idx + 1}`} className={`msg-row ${isSelf ? 'msg-row--self' : ''}`}>
<View className={`msg-bubble ${isSelf ? 'msg-bubble--self' : 'msg-bubble--other'}`}>
{isImageUrl(msg.content) ? (
<Image
className='msg-image'
src={msg.content}
mode='widthFix'
onClick={() => Taro.previewImage({ urls: [msg.content], current: msg.content })}
/>
) : (
<Text className='msg-text'>{msg.content}</Text>
)}
<Text className='msg-time'>{formatTime(msg.created_at)}</Text>
</View>
</View>
</View>
);