99 lines
3.4 KiB
TypeScript
99 lines
3.4 KiB
TypeScript
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>
|
||
);
|
||
}
|