Files
hms/apps/miniprogram/src/pages/consultation/index.tsx
iven 5bb6105127
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
feat: 咨询消息轮询优化 — Web 自动刷新 + 患者端聊天详情页
Web 端:
- ConsultationDetail 添加 10s 自动轮询新消息(after_id 增量拉取)
- consultations API 补充 after_id 参数

小程序患者端:
- 新增 consultation service 消息 API(listMessages/sendMessage/markSessionRead)
- 新增聊天详情页(8s 轮询 + 发送消息 + 自动标记已读)
- 咨询列表页点击跳转详情页(替换"即将上线"占位)
2026-04-26 14:40:46 +08:00

138 lines
4.5 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 } from 'react';
import { View, Text } from '@tarojs/components';
import Taro, { useDidShow, usePullDownRefresh } from '@tarojs/taro';
import { listConsultations, ConsultationSession } from '@/services/consultation';
import Loading from '../../components/Loading';
import './index.scss';
function getStatusLabel(status: string): string {
const map: Record<string, string> = {
pending: '等待接诊',
active: '进行中',
closed: '已结束',
cancelled: '已取消',
};
return map[status] || status;
}
function getStatusClass(status: string): string {
if (status === 'active') return 'session-status-active';
if (status === 'pending') return 'session-status-pending';
return 'session-status-closed';
}
function formatTime(iso: string): string {
if (!iso) return '';
const d = new Date(iso);
const now = new Date();
const diffMs = now.getTime() - d.getTime();
const diffMin = Math.floor(diffMs / 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 loadSessions = async () => {
setLoading(true);
setError('');
try {
const resp = await listConsultations({ page: 1, page_size: 20 });
setSessions(resp.data || []);
} catch (e: unknown) {
const msg = e instanceof Error ? e.message : '加载失败';
setError(msg);
} finally {
setLoading(false);
}
};
useDidShow(() => {
Taro.setNavigationBarTitle({ title: '在线咨询' });
loadSessions();
});
usePullDownRefresh(() => {
loadSessions().finally(() => {
Taro.stopPullDownRefresh();
});
});
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-header-title'>线</Text>
<Text className='consultation-header-desc'></Text>
</View>
{loading ? (
<View className='consultation-loading'>
<Loading text='加载中...' />
</View>
) : error ? (
<View className='consultation-error'>
<Text className='consultation-error-text'>{error}</Text>
</View>
) : sessions.length === 0 ? (
<View className='consultation-empty'>
<Text className='consultation-empty-icon'>💬</Text>
<Text className='consultation-empty-text'></Text>
<Text className='consultation-empty-hint'></Text>
</View>
) : (
<View className='consultation-list'>
{sessions.map((session) => (
<View
key={session.id}
className='consultation-session'
onClick={() => handleTapSession(session)}
>
<View className='session-left'>
<View className='session-top'>
<Text className='session-subject'>
{session.subject || '在线咨询'}
</Text>
<Text className={getStatusClass(session.status)}>
{getStatusLabel(session.status)}
</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 > 0 && (
<View className='session-badge'>
<Text className='session-badge-text'>
{session.unread_count > 99 ? '99+' : session.unread_count}
</Text>
</View>
)}
</View>
))}
</View>
)}
</View>
);
}