From 204bc19ce5c7b03c71d4704005d29c5ffb35b6f7 Mon Sep 17 00:00:00 2001 From: MingNian <1281442923@qq.com> Date: Thu, 21 May 2026 15:40:17 +0800 Subject: [PATCH] fix: consultation notifications, profile edit hint, doctor report in Chinese, chat UI polish, tabbar badge --- .../Services/ConsultationService.cs | 27 +++ .../src/pages/reports/ReportDetailPage.tsx | 174 ++++++++---------- .../src/components/layout/TabBar.module.css | 22 +++ .../src/components/layout/TabBar.tsx | 9 +- .../src/pages/profile/ProfilePage.tsx | 2 +- .../src/pages/services/ChatPage.module.css | 66 ++++--- 6 files changed, 180 insertions(+), 120 deletions(-) diff --git a/backend/src/HealthManager.Application/Services/ConsultationService.cs b/backend/src/HealthManager.Application/Services/ConsultationService.cs index d8fcc17..2996d19 100644 --- a/backend/src/HealthManager.Application/Services/ConsultationService.cs +++ b/backend/src/HealthManager.Application/Services/ConsultationService.cs @@ -59,6 +59,30 @@ public class ConsultationService(AppDbContext db) ImageUrl = imageUrl, }; db.ConsultationMessages.Add(message); + + // Create notification for the recipient + var consultation = await db.Consultations + .Include(c => c.Patient) + .Include(c => c.Doctor) + .FirstOrDefaultAsync(c => c.Id == consultationId); + + if (consultation != null) + { + var targetUserId = senderRole == "patient" ? consultation.DoctorId : consultation.PatientId; + var senderName = senderRole == "patient" ? consultation.Patient?.Name : consultation.Doctor?.Name; + var notifyTitle = senderRole == "patient" ? "新患者消息" : "医生已回复"; + var notifyContent = $"{senderName ?? "对方"}:{TruncateContent(content)}"; + + db.Notifications.Add(new Notification + { + UserId = targetUserId, + Type = "consultation", + Title = notifyTitle, + Content = notifyContent, + RelatedId = consultationId, + }); + } + await db.SaveChangesAsync(); return message; } @@ -70,4 +94,7 @@ public class ConsultationService(AppDbContext db) query = query.Where(u => u.Department == department); return await query.ToListAsync(); } + + private static string TruncateContent(string content) => + content.Length > 50 ? content[..50] + "..." : content; } diff --git a/frontend-doctor/src/pages/reports/ReportDetailPage.tsx b/frontend-doctor/src/pages/reports/ReportDetailPage.tsx index 1690a24..21c71e4 100644 --- a/frontend-doctor/src/pages/reports/ReportDetailPage.tsx +++ b/frontend-doctor/src/pages/reports/ReportDetailPage.tsx @@ -16,18 +16,20 @@ interface RawItem { unit?: string; referenceRange?: string; isAbnormal: boolean; } +const categoryMap: Record = { + '血液检查': '血液检查', '心电图': '心电图', '影像学': '影像学', '尿液检查': '尿液检查', '其他': '其他', + 'Blood Test': '血液检查', 'ECG': '心电图', 'Imaging': '影像学', +}; + export function ReportDetailPage() { const { id } = useParams<{ id: string }>(); const [report, setReport] = useState(null); const [lightbox, setLightbox] = useState(null); - // Interpretation form const [summary, setSummary] = useState(''); const [riskLevel, setRiskLevel] = useState('normal'); const [suggestions, setSuggestions] = useState(''); - const [items, setItems] = useState<{ itemName: string; resultValue: string; unit: string; referenceRange: string; isAbnormal: boolean }[]>([ - { itemName: '', resultValue: '', unit: '', referenceRange: '', isAbnormal: false }, - ]); + const [items, setItems] = useState([{ itemName: '', resultValue: '', unit: '', referenceRange: '', isAbnormal: false }]); const [submitting, setSubmitting] = useState(false); useEffect(() => { @@ -35,51 +37,46 @@ export function ReportDetailPage() { api.get(`/api/reports/${id}`).then((r) => setReport(r.data)).catch(() => {}); }, [id]); - const addItem = () => { - setItems((prev) => [...prev, { itemName: '', resultValue: '', unit: '', referenceRange: '', isAbnormal: false }]); - }; - - const updateItem = (index: number, field: string, value: string | boolean) => { - setItems((prev) => prev.map((item, i) => i === index ? { ...item, [field]: value } : item)); - }; - - const removeItem = (index: number) => { - if (items.length <= 1) return; - setItems((prev) => prev.filter((_, i) => i !== index)); - }; + const addItem = () => setItems((prev) => [...prev, { itemName: '', resultValue: '', unit: '', referenceRange: '', isAbnormal: false }]); + const updateItem = (i: number, field: string, value: string | boolean) => + setItems((prev) => prev.map((it, idx) => idx === i ? { ...it, [field]: value } : it)); + const removeItem = (i: number) => { if (items.length > 1) setItems((prev) => prev.filter((_, idx) => idx !== i)); }; const handleInterpret = async () => { if (!summary.trim() || !id) return; setSubmitting(true); try { await api.post(`/api/reports/${id}/interpret`, { - summary, - items: items.filter((it) => it.itemName.trim()), - riskLevel, + summary, items: items.filter((it) => it.itemName.trim()), riskLevel, suggestions: suggestions || null, }); const updated = await api.get(`/api/reports/${id}`); setReport(updated.data); - } catch { alert('submit failed'); } + } catch { alert('提交失败'); } finally { setSubmitting(false); } }; - if (!report) return
Loading...
; + if (!report) return
加载中...
; const isCompleted = report.status === 'completed'; + const riskMap: Record = { + normal: { text: '正常', color: '#2e7d32' }, + attention: { text: '关注', color: '#f57c00' }, + abnormal: { text: '异常', color: '#c62828' }, + }; return (
- ← Back to Reports + ← 返回报告列表
-
+

{report.title}

- Patient: {report.patientName || 'unknown'}  |  - Category: {report.category}  |  - Date: {report.createdAt?.split('T')[0]} + 患者:{report.patientName || '未知'}  |  + 分类:{categoryMap[report.category] || report.category}  |  + 日期:{report.createdAt?.split('T')[0]}
- {isCompleted ? 'Completed' : 'Pending Review'} + {isCompleted ? '已完成' : '待审核'}
- {/* Image gallery */} + {/* 图片 */} {report.imageUrls && report.imageUrls.length > 0 && (
-

Uploaded Images ({report.imageUrls.length})

+

上传图片({report.imageUrls.length}张)

{report.imageUrls.map((url, i) => ( -
setLightbox(url)} - style={{ - width: 120, height: 120, borderRadius: 8, overflow: 'hidden', - cursor: 'pointer', border: '2px solid #eee', - background: '#f5f5f5', display: 'flex', alignItems: 'center', justifyContent: 'center', - }} - > - {`report-${i}`} setLightbox(url)} style={{ + width: 120, height: 120, borderRadius: 8, overflow: 'hidden', + cursor: 'pointer', border: '2px solid #eee', background: '#f5f5f5', + display: 'flex', alignItems: 'center', justifyContent: 'center', + }}> + {`图片${i}`} { (e.target as HTMLImageElement).style.display = 'none'; }} - /> + onError={(e) => { (e.target as HTMLImageElement).style.display = 'none'; }} />
))}
)} - {/* Lightbox */} + {/* 灯箱 */} {lightbox && (
setLightbox(null)} style={{ position: 'fixed', inset: 0, background: 'rgba(0,0,0,0.85)', - display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 999, - cursor: 'pointer', + display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 999, cursor: 'pointer', }}> - preview + 预览
)} - {/* Completed interpretation display */} + {/* 已完成解读 */} {isCompleted && (
-

Interpretation Result

+

解读结果

-

Risk Level: {report.riskLevel || '-'}

-

Summary: {report.summary || '-'}

- {report.suggestions &&

Suggestions: {report.suggestions}

} +

风险等级: + + {riskMap[report.riskLevel || '']?.text || report.riskLevel || '-'} + +

+

总结:{report.summary || '-'}

+ {report.suggestions &&

建议:{report.suggestions}

}
{report.items && report.items.length > 0 && ( - - - - + + + + {report.items.map((item) => ( @@ -157,7 +147,7 @@ export function ReportDetailPage() { ))} @@ -167,66 +157,62 @@ export function ReportDetailPage() { )} - {/* Interpretation form (only for pending reports) */} + {/* 解读表单 */} {!isCompleted && (
-

Doctor Interpretation

+

医生解读

- -
ItemResultReferenceAbnormal检查项目结果参考范围是否异常
{item.resultValue} {item.unit || ''} {item.referenceRange || '-'} - {item.isAbnormal ? 'Yes' : 'No'} + {item.isAbnormal ? '是' : '否'}