Files
soft/frontend-patient/src/pages/services/ChatPage.tsx

99 lines
3.4 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 { useEffect, useState, useRef } from 'react';
import { PageHeader } from '@/components/layout/PageHeader';
import * as consultationService from '@/services/consultation.service';
import type { Consultation, ConsultationMessage, Doctor } from '@/types';
import { formatRelative } from '@/utils/format';
import styles from './ChatPage.module.css';
export function ChatPage() {
const [doctor, setDoctor] = useState<Doctor | null>(null);
const [consultation, setConsultation] = useState<Consultation | null>(null);
const [messages, setMessages] = useState<ConsultationMessage[]>([]);
const [text, setText] = useState('');
const [sending, setSending] = useState(false);
const bottomRef = useRef<HTMLDivElement>(null);
useEffect(() => {
// Get the first available doctor
consultationService.getDoctors().then((docs) => {
if (docs.length > 0) {
const doc = docs[0];
setDoctor(doc);
// Find or create consultation
consultationService.getConsultation(doc.id).then(async (c) => {
if (c) {
setConsultation(c);
loadMessages(c.id);
} else {
const newC = await consultationService.startConsultation(doc.id, '在线咨询');
setConsultation(newC);
}
});
}
});
}, []);
const loadMessages = (cid: string) => {
import('@/services/api-client').then(({ api }) => {
api.get<ConsultationMessage[]>(`/api/consultations/${cid}/messages`)
.then((res) => setMessages(res.data));
});
};
useEffect(() => {
bottomRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]);
useEffect(() => {
// Poll for new messages every 3 seconds
if (!consultation?.id) return;
const timer = setInterval(() => loadMessages(consultation.id), 3000);
return () => clearInterval(timer);
}, [consultation?.id]);
const handleSend = async () => {
if (!text.trim() || !consultation || sending) return;
setSending(true);
const msgText = text;
setText('');
const sent = await consultationService.sendMessage(consultation.id, msgText);
setMessages((prev) => [...prev, sent]);
setSending(false);
};
return (
<div className={styles.page}>
<PageHeader title={doctor?.name || '在线问诊'} />
<div className={styles.messages}>
{messages.length === 0 && (
<div style={{ textAlign: 'center', color: '#9CA3AF', marginTop: 40, fontSize: 14 }}>
{doctor?.name || '医生'}
</div>
)}
{messages.map((msg) => (
<div
key={msg.id}
className={`${styles.bubble} ${msg.senderRole === 'patient' ? styles.patient : styles.doctor}`}
>
<div className={styles.bubbleContent}>{msg.content}</div>
<div className={styles.bubbleTime}>{formatRelative(msg.createdAt)}</div>
</div>
))}
<div ref={bottomRef} />
</div>
<div className={styles.inputBar}>
<input
className={styles.input}
value={text}
onChange={(e) => setText(e.target.value)}
placeholder="输入消息..."
onKeyDown={(e) => e.key === 'Enter' && handleSend()}
/>
<button className={styles.sendBtn} onClick={handleSend} disabled={sending}>
</button>
</div>
</div>
);
}