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)
This commit is contained in:
81
frontend-doctor/src/pages/consultations/ChatPage.tsx
Normal file
81
frontend-doctor/src/pages/consultations/ChatPage.tsx
Normal file
@@ -0,0 +1,81 @@
|
||||
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>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user