Files
soft/frontend-doctor/src/pages/consultations/ChatPage.tsx
MingNian 435af55c4a Initial commit: HealthManager full-stack health management platform
Backend: .NET 10 + PostgreSQL + EF Core + JWT + SignalR
Frontend patient: React 19 + TypeScript + Vite (mobile H5)
Frontend doctor: React 19 + TypeScript + Vite (desktop web)
2026-05-20 16:18:56 +08:00

82 lines
2.9 KiB
TypeScript

import { useEffect, useState, useRef } from 'react';
import { useParams } from 'react-router-dom';
import { api } from '../../services/api-client';
interface Message {
id: string; senderId: string; senderRole: string;
content: string; contentType: string; createdAt: string;
}
export function ChatPage() {
const { id } = useParams<{ id: string }>();
const [messages, setMessages] = useState<Message[]>([]);
const [input, setInput] = useState('');
const bottomRef = useRef<HTMLDivElement>(null);
useEffect(() => {
if (!id) return;
api.get<Message[]>(`/api/consultations/${id}/messages`)
.then((r) => setMessages(r.data))
.catch(() => {});
}, [id]);
useEffect(() => {
bottomRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]);
const handleSend = async () => {
if (!input.trim() || !id) return;
try {
const res = await api.post<Message>(`/api/consultations/${id}/messages`, { content: input });
setMessages((prev) => [...prev, res.data]);
setInput('');
} catch { /* ignore */ }
};
return (
<div style={{ display: 'flex', flexDirection: 'column', height: 'calc(100vh - 0px)' }}>
<div style={{ padding: '14px 20px', background: '#fff', borderBottom: '1px solid #eee', fontSize: 15, fontWeight: 500 }}>
线
</div>
<div style={{ flex: 1, overflow: 'auto', padding: 20, background: '#fafafa' }}>
{messages.map((msg) => (
<div key={msg.id} style={{
display: 'flex', justifyContent: msg.senderRole === 'doctor' ? 'flex-end' : 'flex-start',
marginBottom: 12,
}}>
<div style={{
maxWidth: '70%', padding: '10px 14px', borderRadius: 12, fontSize: 14,
background: msg.senderRole === 'doctor' ? '#1976d2' : '#fff',
color: msg.senderRole === 'doctor' ? '#fff' : '#333',
boxShadow: '0 1px 3px rgba(0,0,0,0.08)',
}}>
<div>{msg.content}</div>
<div style={{
fontSize: 10, marginTop: 4, textAlign: 'right',
opacity: 0.7,
}}>
{msg.createdAt?.split('T')[1]?.slice(0, 5)}
</div>
</div>
</div>
))}
<div ref={bottomRef} />
</div>
<div style={{ padding: '12px 20px', background: '#fff', borderTop: '1px solid #eee', display: 'flex', gap: 12 }}>
<input value={input} onChange={(e) => setInput(e.target.value)}
onKeyDown={(e) => e.key === 'Enter' && handleSend()}
placeholder="输入回复..."
style={{ flex: 1, padding: '10px 14px', border: '1px solid #ddd', borderRadius: 20, fontSize: 14 }} />
<button onClick={handleSend} style={{
padding: '10px 24px', background: '#1976d2', color: '#fff',
border: 'none', borderRadius: 20, fontSize: 14,
}}>
</button>
</div>
</div>
);
}