diff --git a/backend/src/HealthManager.Application/Services/ReportService.cs b/backend/src/HealthManager.Application/Services/ReportService.cs index cbff934..2904337 100644 --- a/backend/src/HealthManager.Application/Services/ReportService.cs +++ b/backend/src/HealthManager.Application/Services/ReportService.cs @@ -14,6 +14,14 @@ public class ReportService(AppDbContext db) .OrderByDescending(r => r.UploadedAt) .ToListAsync(); + public async Task> GetAllReportsAsync() + => await db.Reports + .Include(r => r.Patient) + .Include(r => r.Doctor) + .Include(r => r.Items) + .OrderByDescending(r => r.UploadedAt) + .ToListAsync(); + public async Task GetByIdAsync(Guid id) => await db.Reports .Include(r => r.Patient) diff --git a/backend/src/HealthManager.WebApi/Controllers/ReportController.cs b/backend/src/HealthManager.WebApi/Controllers/ReportController.cs index fd0cd7c..d5b35ac 100644 --- a/backend/src/HealthManager.WebApi/Controllers/ReportController.cs +++ b/backend/src/HealthManager.WebApi/Controllers/ReportController.cs @@ -16,12 +16,33 @@ public class ReportController(ReportService reportService) : ControllerBase [HttpGet] public async Task GetReports() { - var targetUserId = UserId; - if (Role == "doctor" && Request.Query.ContainsKey("patientId")) - targetUserId = Guid.Parse(Request.Query["patientId"]!); + // Patients: see own reports. Doctors: see all reports (or filter by patientId) + if (Role == "doctor") + { + if (Request.Query.ContainsKey("patientId")) + { + var targetUserId = Guid.Parse(Request.Query["patientId"]!); + var reports = await reportService.GetPatientReportsAsync(targetUserId); + return Ok(reports.Select(r => new + { + r.Id, r.PatientId, r.Title, r.Category, r.ImageUrls, r.Status, + r.RiskLevel, r.UploadedAt, r.CompletedAt, + PatientName = r.Patient?.Name, + DoctorName = r.Doctor?.Name, + })); + } + var allReports = await reportService.GetAllReportsAsync(); + return Ok(allReports.Select(r => new + { + r.Id, r.PatientId, r.Title, r.Category, r.ImageUrls, r.Status, + r.RiskLevel, r.UploadedAt, r.CompletedAt, + PatientName = r.Patient?.Name, + DoctorName = r.Doctor?.Name, + })); + } - var reports = await reportService.GetPatientReportsAsync(targetUserId); - return Ok(reports.Select(r => new + var myReports = await reportService.GetPatientReportsAsync(UserId); + return Ok(myReports.Select(r => new { r.Id, r.PatientId, r.Title, r.Category, r.ImageUrls, r.Status, r.RiskLevel, r.UploadedAt, r.CompletedAt, diff --git a/frontend-patient/src/pages/services/ReportUploadPage.module.css b/frontend-patient/src/pages/services/ReportUploadPage.module.css index f16eb8a..789641d 100644 --- a/frontend-patient/src/pages/services/ReportUploadPage.module.css +++ b/frontend-patient/src/pages/services/ReportUploadPage.module.css @@ -3,4 +3,9 @@ .catGrid { display: flex; flex-wrap: wrap; gap: 8px; } .catChip { padding: 6px 14px; border-radius: var(--radius-full); font-size: var(--font-size-sm); background: var(--color-bg-secondary); color: var(--color-text-secondary); } .catActive { background: var(--color-primary-bg); color: var(--color-primary); } -.uploadArea { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 32px; background: var(--color-bg); border: 2px dashed var(--color-border); border-radius: var(--radius-lg); cursor: pointer; } +.uploadArea { display: flex; flex-direction: column; align-items: center; gap: 8px; padding: 32px; background: var(--color-bg); border: 2px dashed var(--color-border); border-radius: var(--radius-lg); cursor: pointer; transition: border-color 0.15s; } +.uploadArea:hover { border-color: var(--color-primary); background: var(--color-primary-bg); } +.fileList { display: flex; flex-direction: column; gap: 6px; } +.fileItem { display: flex; align-items: center; justify-content: space-between; padding: 8px 12px; background: var(--color-bg); border-radius: var(--radius-sm); font-size: var(--font-size-sm); } +.fileName { color: var(--color-text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.fileRemove { background: none; border: none; color: var(--color-danger); cursor: pointer; font-size: 14px; padding: 4px; } diff --git a/frontend-patient/src/pages/services/ReportUploadPage.tsx b/frontend-patient/src/pages/services/ReportUploadPage.tsx index ebce0e6..d878988 100644 --- a/frontend-patient/src/pages/services/ReportUploadPage.tsx +++ b/frontend-patient/src/pages/services/ReportUploadPage.tsx @@ -1,4 +1,4 @@ -import { useState } from 'react'; +import { useState, useRef } from 'react'; import { useNavigate } from 'react-router-dom'; import { Button } from '@/components/common/Button'; import { Input } from '@/components/common/Input'; @@ -9,18 +9,38 @@ import styles from './ReportUploadPage.module.css'; export function ReportUploadPage() { const navigate = useNavigate(); + const fileRef = useRef(null); const [title, setTitle] = useState(''); const [category, setCategory] = useState('血液检查'); + const [files, setFiles] = useState([]); const [loading, setLoading] = useState(false); const categories = ['血液检查', '心电图', '影像学', '尿液检查', '其他']; + const handleFileChange = (e: React.ChangeEvent) => { + if (e.target.files) { + setFiles((prev) => [...prev, ...Array.from(e.target.files!)]); + } + // Reset so the same file can be selected again + if (fileRef.current) fileRef.current.value = ''; + }; + + const removeFile = (index: number) => { + setFiles((prev) => prev.filter((_, i) => i !== index)); + }; + const handleSubmit = async () => { if (!title.trim()) { toast('请输入报告名称', 'error'); return; } setLoading(true); - await reportService.uploadReport({ title, category }); - toast('上传成功,正在解读中...'); - setTimeout(() => navigate(-1), 800); + try { + await reportService.uploadReport({ title, category }); + toast('上传成功'); + setTimeout(() => navigate('/services/reports'), 800); + } catch { + toast('上传失败,请检查后端是否运行', 'error'); + } finally { + setLoading(false); + } }; return ( @@ -36,11 +56,30 @@ export function ReportUploadPage() { ))} -
- 📸 - 点击拍照或选择图片上传 - 模拟上传,直接保存即可 +
fileRef.current?.click()}> + 📷 + 点击选择报告图片 + 支持 jpg、png,可多选
+ + + {files.length > 0 && ( +
+ {files.map((file, i) => ( +
+ 📎 {file.name} + +
+ ))} +
+ )}