import { useEffect, useRef, useState } from 'react'; import { useParams } from 'react-router-dom'; import { api } from '../../services/api-client'; import { getConnection, startConnection } from '../../services/signalr'; import type { ConsultationMessage } from '../../types'; export default function ChatPage() { const { id } = useParams<{ id: string }>(); const [messages, setMessages] = useState([]); const [status, setStatus] = useState(''); const [text, setText] = useState(''); const [connected, setConnected] = useState(false); const bottomRef = useRef(null); // 加载历史消息 useEffect(() => { if (!id) return; api.get<{ status: string; messages: ConsultationMessage[] }>(`/api/doctor/consultations/${id}/messages`) .then(data => { setMessages(data.messages); setStatus(data.status); }) .catch(() => {}); }, [id]); // 建立 SignalR 连接 useEffect(() => { if (!id) return; let disposed = false; (async () => { try { const conn = getConnection(); await startConnection(); if (disposed) return; // 加入问诊房间 await conn.invoke('JoinConsultation', id); setConnected(true); // 注册消息接收 const handler = (msg: ConsultationMessage & { consultationId: string }) => { if (msg.consultationId !== id) return; setMessages(prev => { if (prev.some(m => m.id === msg.id)) return prev; // 去重 return [...prev, msg]; }); }; conn.on('ReceiveMessage', handler); // 重连时重新加入房间 conn.onreconnected(() => { conn.invoke('JoinConsultation', id); }); } catch { /* ignore */ } })(); return () => { disposed = true; const conn = getConnection(); if (conn.state === 'Connected') { conn.invoke('LeaveConsultation', id).catch(() => {}); conn.off('ReceiveMessage'); } }; }, [id]); // 自动滚到底部 useEffect(() => { bottomRef.current?.scrollIntoView({ behavior: 'smooth' }); }, [messages]); async function sendMessage() { if (!text.trim() || !id) return; const content = text.trim(); setText(''); // 通过 HTTP 发送(后端会通过 SignalR 广播) try { await api.post(`/api/doctor/consultations/${id}/messages`, { content }); } catch { // fallback: 直接通过 SignalR 发送 try { await getConnection().invoke('SendMessage', id, 'Doctor', '医生 · 王建国', content); } catch { /* ignore */ } } } return (
{/* 顶栏 */}
问诊对话
{status === 'AiTalking' ? 'AI 分身对话中' : status === 'WaitingDoctor' ? '等待医生回复' : status === 'DoctorReplied' ? '医生已回复' : status === 'Closed' ? '已结束' : status}
{connected ? '已连接' : '未连接'}
{/* 消息列表 */}
{messages.map((msg, i) => { const isDoctor = msg.senderType === 'Doctor'; const isAi = msg.senderType === 'Ai'; return (
{/* 发送者名称 */} {!isDoctor && msg.senderName && (
{msg.senderName}
)} {/* 气泡 */}
{msg.content}
{/* AI 标记 */} {isAi && (
AI 分析,仅供参考
)} {/* 时间 */}
{new Date(msg.createdAt).toLocaleTimeString('zh-CN', { hour: '2-digit', minute: '2-digit' })}
); })}
{/* 输入栏 */}
setText(e.target.value)} onKeyDown={e => e.key === 'Enter' && sendMessage()} placeholder="输入回复..." style={{ flex: 1, padding: '12px 18px', border: '1px solid #E1E5ED', borderRadius: 12, fontSize: 15, outline: 'none' }} />
); }