Files
hms/apps/miniprogram/src/pages/consultation/index.tsx
iven 81c174a902
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
fix(miniprogram): 修复多角色找茬测试 V3 发现的 8 个问题
1. EmptyState 默认 emoji 📭 → serif 首字圆形图标(影响 23 处使用)
2. 预约页英文副标题 "Appointment" 移除
3. consultation 页技术错误信息直接渲染到 UI → 用户友好提示
4. auth store restore() 增加 fallback:secureGet 失败时读 wx.getStorageSync
5. request.ts 新增 safeGet():token/tenantId 读取容错
6. doctor/consultation useMemo 自引用死循环 → Math.ceil(total/20)
7. doctor/alerts 同样自引用 bug 修复
8. doctor/patients 死代码 totalPages + useMemo import 清理
2026-05-08 17:34:42 +08:00

150 lines
5.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { useState, useRef } from 'react';
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 './index.scss';
function getStatusTag(status: string) {
if (status === 'active') return { label: '进行中', cls: 'tag-ok' };
if (status === 'pending') return { label: '等待接诊', cls: 'tag-warn' };
return { label: { closed: '已结束', cancelled: '已取消' }[status] || status, cls: 'tag-default' };
}
function formatTime(iso: string): string {
if (!iso) return '';
const d = new Date(iso);
const now = new Date();
const diffMin = Math.floor((now.getTime() - d.getTime()) / 60000);
if (diffMin < 1) return '刚刚';
if (diffMin < 60) return `${diffMin}分钟前`;
const diffHour = Math.floor(diffMin / 60);
if (diffHour < 24) return `${diffHour}小时前`;
const diffDay = Math.floor(diffHour / 24);
if (diffDay < 7) return `${diffDay}天前`;
const m = String(d.getMonth() + 1).padStart(2, '0');
const day = String(d.getDate()).padStart(2, '0');
return `${m}-${day}`;
}
export default function Consultation() {
const [sessions, setSessions] = useState<ConsultationSession[]>([]);
const [loading, setLoading] = useState(true);
const [error, setError] = useState('');
const [page, setPage] = useState(1);
const [total, setTotal] = useState(0);
const loadingRef = useRef(false);
const loadSessions = async (pageNum: number, isRefresh = false) => {
if (loadingRef.current) return;
loadingRef.current = true;
if (isRefresh) setLoading(true);
setError('');
try {
const resp = await listConsultations({ page: pageNum, page_size: 20 });
const list = resp.data || [];
if (isRefresh) {
setSessions(list);
} else {
setSessions((prev) => [...prev, ...list]);
}
setTotal(resp.total || 0);
setPage(pageNum);
} catch {
setError('加载失败,请稍后重试');
} finally {
setLoading(false);
loadingRef.current = false;
}
};
useDidShow(() => {
Taro.setNavigationBarTitle({ title: '在线咨询' });
loadSessions(1, true);
});
usePullDownRefresh(() => {
loadSessions(1, true).finally(() => {
Taro.stopPullDownRefresh();
});
});
useReachBottom(() => {
if (!loading && sessions.length < total) {
loadSessions(page + 1);
}
});
const handleTapSession = (session: ConsultationSession) => {
Taro.navigateTo({ url: `/pages/consultation/detail/index?id=${session.id}` });
};
return (
<View className='consultation-page'>
{/* 页头 */}
<View className='consultation-header'>
<Text className='consultation-title'>线</Text>
<Text className='consultation-subtitle'></Text>
</View>
{/* 内容区 */}
{loading ? (
<View className='consultation-center'>
<Loading text='加载中...' />
</View>
) : error ? (
<View className='consultation-center'>
<Text className='consultation-error'>{error}</Text>
</View>
) : sessions.length === 0 ? (
<View className='consultation-empty'>
<View className='empty-icon'>
<Text className='empty-char'></Text>
</View>
<Text className='empty-title'></Text>
<Text className='empty-hint'></Text>
</View>
) : (
<View className='session-list'>
{sessions.map((session) => {
const tag = getStatusTag(session.status);
return (
<View
key={session.id}
className='session-card'
onClick={() => handleTapSession(session)}
>
<View className='session-main'>
<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>
{session.unread_count_patient > 0 && (
<View className='session-badge'>
<Text className='session-badge-text'>
{session.unread_count_patient > 99 ? '99+' : session.unread_count_patient}
</Text>
</View>
)}
</View>
);
})}
</View>
)}
</View>
);
}