From e4e5ef04d4991a22a175482893f7441d7c7dfe89 Mon Sep 17 00:00:00 2001 From: iven Date: Wed, 13 May 2026 23:28:22 +0800 Subject: [PATCH] =?UTF-8?q?feat(web):=20Web=20=E5=89=8D=E7=AB=AF=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E5=AE=8C=E5=96=84=20=E2=80=94=20API=20=E6=89=A9?= =?UTF-8?q?=E5=B1=95=20+=20=E7=BB=84=E4=BB=B6=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 AI 透析分析 API + 药物提醒 API - MediaPicker/ThemeSwitcher/usePaginatedData 优化 - 健康管理页面组件增强(Banner/Consultation/Doctor/MediaLibrary 等) - PluginCRUDPage 导入优化 --- apps/web/src/api/ai/dialysis.ts | 23 ++++++ apps/web/src/api/ai/suggestions.ts | 4 + apps/web/src/api/ai/usage.ts | 56 ++++++++++++++ apps/web/src/api/health/alerts.ts | 29 +++++++ apps/web/src/api/health/articles.ts | 12 ++- apps/web/src/api/health/consultations.ts | 32 ++++++++ apps/web/src/api/health/dialysis.ts | 8 ++ apps/web/src/api/health/doctors.ts | 4 +- apps/web/src/api/health/familyProxy.ts | 6 +- apps/web/src/api/health/healthData.ts | 8 ++ .../web/src/api/health/medicationReminders.ts | 75 +++++++++++++++++++ apps/web/src/api/health/points.ts | 4 +- apps/web/src/components/MediaPicker/index.tsx | 2 +- apps/web/src/components/ThemeSwitcher.tsx | 2 +- apps/web/src/hooks/usePaginatedData.ts | 7 +- .../src/pages/PluginCRUDPage/ImportModal.tsx | 2 +- .../PluginCRUDPage/PluginCRUDPageInner.tsx | 2 +- apps/web/src/pages/health/AiPromptList.tsx | 2 +- .../src/pages/health/ArticleManageList.tsx | 6 +- apps/web/src/pages/health/BannerManage.tsx | 2 +- .../src/pages/health/ConsultationDetail.tsx | 48 +++++------- .../web/src/pages/health/ConsultationList.tsx | 2 +- .../src/pages/health/DialysisManageList.tsx | 2 +- apps/web/src/pages/health/DoctorList.tsx | 2 +- apps/web/src/pages/health/DoctorSchedule.tsx | 2 +- .../web/src/pages/health/FollowUpTaskList.tsx | 4 +- .../src/pages/health/FollowUpTemplateList.tsx | 2 +- apps/web/src/pages/health/MediaLibrary.tsx | 8 +- .../web/src/pages/health/OfflineEventList.tsx | 2 +- apps/web/src/pages/health/PointsOrderList.tsx | 2 +- apps/web/src/pages/health/PointsRuleList.tsx | 2 +- .../health/components/DailyMonitoringTab.tsx | 2 +- .../health/components/HealthRecordsTab.tsx | 2 +- .../pages/health/components/LabReportsTab.tsx | 31 +++++++- .../pages/health/components/VitalSignsTab.tsx | 2 +- .../src/pages/workflow/ProcessDefinitions.tsx | 2 +- 36 files changed, 332 insertions(+), 69 deletions(-) create mode 100644 apps/web/src/api/ai/dialysis.ts create mode 100644 apps/web/src/api/health/medicationReminders.ts diff --git a/apps/web/src/api/ai/dialysis.ts b/apps/web/src/api/ai/dialysis.ts new file mode 100644 index 0000000..70268ab --- /dev/null +++ b/apps/web/src/api/ai/dialysis.ts @@ -0,0 +1,23 @@ +import client from '../client'; + +export interface DialysisRiskRequest { + patient_id: string; + dialysis_session_id?: string; +} + +export interface DialysisRiskAssessment { + id: string; + patient_id: string; + risk_level: string; + risk_factors: string[]; + recommendations: string[]; + kdigo_stage?: string; + created_at: string; +} + +export const dialysisRiskApi = { + assess: async (data: DialysisRiskRequest) => { + const resp = await client.post('/ai/dialysis/risk-assessment', data); + return resp.data.data as DialysisRiskAssessment; + }, +}; diff --git a/apps/web/src/api/ai/suggestions.ts b/apps/web/src/api/ai/suggestions.ts index bbfaacd..5e351fc 100644 --- a/apps/web/src/api/ai/suggestions.ts +++ b/apps/web/src/api/ai/suggestions.ts @@ -27,6 +27,10 @@ export const suggestionApi = { const resp = await client.post(`/ai/suggestions/${id}/approve`, { action }); return resp.data.data as { id: string; status: string }; }, + execute: async (id: string) => { + const resp = await client.post(`/ai/suggestions/${id}/execute`); + return resp.data.data as { id: string; status: string }; + }, getComparison: async (id: string) => { const resp = await client.get(`/ai/suggestions/${id}/comparison`); return resp.data.data as ComparisonReport; diff --git a/apps/web/src/api/ai/usage.ts b/apps/web/src/api/ai/usage.ts index 7a104c5..3815942 100644 --- a/apps/web/src/api/ai/usage.ts +++ b/apps/web/src/api/ai/usage.ts @@ -9,6 +9,42 @@ export interface TypeDistribution { count: number; } +export interface ProviderInfo { + id: string; + name: string; + provider_type: string; + is_active: boolean; + model_name?: string; +} + +export interface ProviderHealth { + provider_id: string; + status: string; + latency_ms?: number; + last_checked_at?: string; +} + +export interface QuotaSummary { + provider_id: string; + quota_limit: number; + quota_used: number; + quota_remaining: number; + period: string; +} + +export interface BudgetStatus { + total_budget: number; + spent: number; + remaining: number; + period: string; +} + +export interface CostEstimate { + analysis_type: string; + estimated_cost: number; + currency: string; +} + export const usageApi = { overview: async () => { const resp = await client.get('/ai/usage/overview'); @@ -18,4 +54,24 @@ export const usageApi = { const resp = await client.get('/ai/usage/by-type'); return resp.data.data as TypeDistribution[]; }, + listProviders: async () => { + const resp = await client.get('/ai/providers'); + return resp.data.data as ProviderInfo[]; + }, + getProvidersHealth: async () => { + const resp = await client.get('/ai/providers/health'); + return resp.data.data as ProviderHealth[]; + }, + getQuotaSummary: async () => { + const resp = await client.get('/ai/quota/summary'); + return resp.data.data as QuotaSummary[]; + }, + getBudgetStatus: async () => { + const resp = await client.get('/ai/budget/status'); + return resp.data.data as BudgetStatus; + }, + getCostEstimate: async (params: { analysis_type: string }) => { + const resp = await client.get('/ai/cost/estimate', { params }); + return resp.data.data as CostEstimate; + }, }; diff --git a/apps/web/src/api/health/alerts.ts b/apps/web/src/api/health/alerts.ts index 7c00b2d..005db29 100644 --- a/apps/web/src/api/health/alerts.ts +++ b/apps/web/src/api/health/alerts.ts @@ -86,3 +86,32 @@ export const alertRuleApi = { deactivate: (id: string, version: number) => client.put(`/health/alert-rules/${id}/deactivate`, { version }).then((r) => r.data.data as AlertRule), }; + +// --- Critical Alerts API --- + +export interface CriticalAlert { + id: string; + patient_id: string; + patient_name?: string; + alert_type: string; + severity: string; + title: string; + detail?: Record; + status: string; + acknowledged_by?: string; + acknowledged_at?: string; + notes?: string; + created_at: string; + version: number; +} + +export const criticalAlertApi = { + list: (params?: { page?: number; page_size?: number }) => + client.get('/health/critical-alerts', { params }).then((r) => r.data.data as PaginatedResponse), + + get: (id: string) => + client.get(`/health/critical-alerts/${id}`).then((r) => r.data.data as CriticalAlert), + + acknowledge: (id: string, req: { notes?: string }) => + client.post(`/health/critical-alerts/${id}/acknowledge`, req).then((r) => r.data), +}; diff --git a/apps/web/src/api/health/articles.ts b/apps/web/src/api/health/articles.ts index 27ef507..06b8e95 100644 --- a/apps/web/src/api/health/articles.ts +++ b/apps/web/src/api/health/articles.ts @@ -149,11 +149,11 @@ export const articleApi = { return data.data; }, - delete: async (id: string) => { + delete: async (id: string, version: number) => { const { data } = await client.delete<{ success: boolean; data: null; - }>(`/health/articles/${id}`); + }>(`/health/articles/${id}`, { data: { version } }); return data.data; }, @@ -196,6 +196,14 @@ export const articleApi = { }>(`/health/articles/${id}/view`); return data.data; }, + + listRevisions: async (id: string, params?: { page?: number; page_size?: number }) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse>; + }>(`/health/articles/${id}/revisions`, { params }); + return data.data; + }, }; // --- Category API --- diff --git a/apps/web/src/api/health/consultations.ts b/apps/web/src/api/health/consultations.ts index d48f139..0a4eb08 100644 --- a/apps/web/src/api/health/consultations.ts +++ b/apps/web/src/api/health/consultations.ts @@ -102,4 +102,36 @@ export const consultationApi = { }>('/health/consultation-messages', req); return data.data; }, + + pollMessages: async ( + sessionId: string, + afterId?: string, + ) => { + const { data } = await client.get<{ + success: boolean; + data: Message[]; + }>(`/health/consultation-sessions/${sessionId}/messages/poll`, { + params: { after_id: afterId, timeout: 25 }, + timeout: 30000, + }); + return data.data; + }, + + markSessionRead: async (id: string) => { + await client.put(`/health/consultation-sessions/${id}/read`); + }, + + exportSessions: async (params?: { + status?: string; + patient_id?: string; + doctor_id?: string; + page?: number; + page_size?: number; + }) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>('/health/consultation-sessions/export', { params }); + return data.data; + }, }; diff --git a/apps/web/src/api/health/dialysis.ts b/apps/web/src/api/health/dialysis.ts index 1c04097..582941e 100644 --- a/apps/web/src/api/health/dialysis.ts +++ b/apps/web/src/api/health/dialysis.ts @@ -105,4 +105,12 @@ export const dialysisApi = { }>(`/health/dialysis-records/${id}/review`, req); return data.data; }, + + completeRecord: async (id: string, version: number) => { + const { data } = await client.put<{ + success: boolean; + data: DialysisRecord; + }>(`/health/dialysis-records/${id}/complete`, { version }); + return data.data; + }, }; diff --git a/apps/web/src/api/health/doctors.ts b/apps/web/src/api/health/doctors.ts index 582e9d2..3dcfedb 100644 --- a/apps/web/src/api/health/doctors.ts +++ b/apps/web/src/api/health/doctors.ts @@ -77,7 +77,7 @@ export const doctorApi = { return data.data; }, - delete: async (id: string) => { - await client.delete(`/health/doctors/${id}`); + delete: async (id: string, version: number) => { + await client.delete(`/health/doctors/${id}`, { data: { version } }); }, }; diff --git a/apps/web/src/api/health/familyProxy.ts b/apps/web/src/api/health/familyProxy.ts index b9e3a39..d584c2a 100644 --- a/apps/web/src/api/health/familyProxy.ts +++ b/apps/web/src/api/health/familyProxy.ts @@ -79,7 +79,7 @@ export const familyProxyApi = { const { data } = await client.post<{ success: boolean; data: FamilyMember; - }>(`/health/patients/${patientId}/family-members/${familyMemberId}/grant-access?version=${version}`, req); + }>(`/health/patients/${patientId}/family-members/${familyMemberId}/grant-access`, { ...req, version }); return data.data; }, @@ -87,7 +87,7 @@ export const familyProxyApi = { const { data } = await client.put<{ success: boolean; data: FamilyMember; - }>(`/health/patients/${patientId}/family-members/${familyMemberId}/revoke-access?version=${version}`); + }>(`/health/patients/${patientId}/family-members/${familyMemberId}/revoke-access`, { version }); return data.data; }, @@ -95,7 +95,7 @@ export const familyProxyApi = { const { data } = await client.get<{ success: boolean; data: FamilyPatientSummary[]; - }>('/health/family/my-patients'); + }>('/health/family/patients'); return data.data; }, diff --git a/apps/web/src/api/health/healthData.ts b/apps/web/src/api/health/healthData.ts index 61f6d04..c4333b2 100644 --- a/apps/web/src/api/health/healthData.ts +++ b/apps/web/src/api/health/healthData.ts @@ -249,6 +249,14 @@ export const healthDataApi = { return data.data; }, + generateTrend: async (patientId: string, req: { indicator: string; start_date?: string; end_date?: string }) => { + const { data } = await client.post<{ + success: boolean; + data: TrendData; + }>(`/health/patients/${patientId}/trends/generate`, req); + return data.data; + }, + getIndicatorTimeseries: async (patientId: string, indicator: string) => { const { data } = await client.get<{ success: boolean; diff --git a/apps/web/src/api/health/medicationReminders.ts b/apps/web/src/api/health/medicationReminders.ts new file mode 100644 index 0000000..f84d05e --- /dev/null +++ b/apps/web/src/api/health/medicationReminders.ts @@ -0,0 +1,75 @@ +import client from '../client'; +import type { PaginatedResponse } from '../types'; + +// --- Types --- + +export interface MedicationReminder { + id: string; + patient_id: string; + medication_name: string; + dosage?: string; + frequency: string; + time_slots: string[]; + start_date?: string; + end_date?: string; + is_active: boolean; + notes?: string; + created_at: string; + updated_at: string; + version: number; +} + +export interface CreateMedicationReminderReq { + patient_id: string; + medication_name: string; + dosage?: string; + frequency?: string; + time_slots?: string[]; + start_date?: string; + end_date?: string; + is_active?: boolean; + notes?: string; +} + +export interface UpdateMedicationReminderReq { + medication_name?: string; + dosage?: string; + frequency?: string; + time_slots?: string[]; + start_date?: string; + end_date?: string; + is_active?: boolean; + notes?: string; +} + +// --- API --- + +export const medicationReminderApi = { + list: async (patientId: string, params?: { page?: number; page_size?: number }) => { + const { data } = await client.get<{ + success: boolean; + data: PaginatedResponse; + }>(`/health/patients/${patientId}/medication-reminders`, { params }); + return data.data; + }, + + create: async (req: CreateMedicationReminderReq) => { + const { data } = await client.post<{ + success: boolean; + data: MedicationReminder; + }>('/health/medication-reminders', req); + return data.data; + }, + + update: async (id: string, req: UpdateMedicationReminderReq & { version: number }) => { + const { data } = await client.put<{ + success: boolean; + data: MedicationReminder; + }>(`/health/medication-reminders/${id}`, req); + return data.data; + }, + + delete: async (id: string, version: number) => { + await client.delete(`/health/medication-reminders/${id}`, { data: { version } }); + }, +}; diff --git a/apps/web/src/api/health/points.ts b/apps/web/src/api/health/points.ts index 8985190..2689d47 100644 --- a/apps/web/src/api/health/points.ts +++ b/apps/web/src/api/health/points.ts @@ -294,7 +294,7 @@ export const pointsApi = { const { data } = await client.put<{ success: boolean; data: PointsRule; - }>(`/health/admin/points/rules/${id}`, { data: req, version: req.version }); + }>(`/health/admin/points/rules/${id}`, req); return data.data; }, @@ -325,7 +325,7 @@ export const pointsApi = { const { data } = await client.put<{ success: boolean; data: PointsProduct; - }>(`/health/admin/points/products/${id}`, { data: req, version: req.version }); + }>(`/health/admin/points/products/${id}`, req); return data.data; }, diff --git a/apps/web/src/components/MediaPicker/index.tsx b/apps/web/src/components/MediaPicker/index.tsx index ead5718..e347ddd 100644 --- a/apps/web/src/components/MediaPicker/index.tsx +++ b/apps/web/src/components/MediaPicker/index.tsx @@ -73,7 +73,7 @@ export default function MediaPicker({ open, onClose, onSelect, accept = 'image/* onCancel={onClose} footer={null} width={720} - destroyOnClose + destroyOnHidden >
content} trigger={['click']} placement="bottomRight"> + content} trigger={['click']} placement="bottomRight">
diff --git a/apps/web/src/hooks/usePaginatedData.ts b/apps/web/src/hooks/usePaginatedData.ts index efec654..c6efc09 100644 --- a/apps/web/src/hooks/usePaginatedData.ts +++ b/apps/web/src/hooks/usePaginatedData.ts @@ -59,9 +59,12 @@ export function usePaginatedData( const filtersRef = useRef(filters); filtersRef.current = filters; + const stateRef = useRef(state); + stateRef.current = state; + const refresh = useCallback( async (p?: number) => { - const targetPage = p ?? state.page; + const targetPage = p ?? stateRef.current.page; setState((s) => ({ ...s, loading: true })); try { const result = await (fetchFnRef.current as any)( @@ -75,7 +78,7 @@ export function usePaginatedData( setState((s) => ({ ...s, loading: false })); } }, - [pageSize, state.page], + [pageSize], ); useEffect(() => { diff --git a/apps/web/src/pages/PluginCRUDPage/ImportModal.tsx b/apps/web/src/pages/PluginCRUDPage/ImportModal.tsx index 88c0c82..d7ff577 100644 --- a/apps/web/src/pages/PluginCRUDPage/ImportModal.tsx +++ b/apps/web/src/pages/PluginCRUDPage/ImportModal.tsx @@ -27,7 +27,7 @@ export default function ImportModal({ open, pluginId, entityName, onClose, onSuc footer={importResult ? ( ) : null} - destroyOnClose + destroyOnHidden > {importResult ? (
diff --git a/apps/web/src/pages/PluginCRUDPage/PluginCRUDPageInner.tsx b/apps/web/src/pages/PluginCRUDPage/PluginCRUDPageInner.tsx index 48cba1e..848292c 100644 --- a/apps/web/src/pages/PluginCRUDPage/PluginCRUDPageInner.tsx +++ b/apps/web/src/pages/PluginCRUDPage/PluginCRUDPageInner.tsx @@ -440,7 +440,7 @@ export default function PluginCRUDPageInner({ open={modalOpen} onCancel={() => { setModalOpen(false); setEditRecord(null); setFormValues({}); }} onOk={() => form.submit()} - destroyOnClose + destroyOnHidden >
setFormValues(allValues)}> {fields.map((field) => { diff --git a/apps/web/src/pages/health/AiPromptList.tsx b/apps/web/src/pages/health/AiPromptList.tsx index 56097eb..fd292a7 100644 --- a/apps/web/src/pages/health/AiPromptList.tsx +++ b/apps/web/src/pages/health/AiPromptList.tsx @@ -230,7 +230,7 @@ export default function AiPromptList() { onCancel={() => setModalOpen(false)} onOk={() => form.submit()} width={600} - destroyOnClose + destroyOnHidden > { + const handleDelete = async (id: string, version: number) => { try { - await articleApi.delete(id); + await articleApi.delete(id, version); message.success('文章已删除'); refresh(); } catch { @@ -231,7 +231,7 @@ export default function ArticleManageList() { handleDelete(record.id)} + onConfirm={() => handleDelete(record.id, record.version)} >
{/* 上传弹窗 */} - setUploadOpen(false)} footer={null} destroyOnClose> + setUploadOpen(false)} footer={null} destroyOnHidden> @@ -279,7 +279,7 @@ export default function MediaLibrary() { {/* 编辑弹窗 */} - setEditOpen(false)} onOk={() => editForm.submit()} confirmLoading={submitting} destroyOnClose> + setEditOpen(false)} onOk={() => editForm.submit()} confirmLoading={submitting} destroyOnHidden> @@ -288,7 +288,7 @@ export default function MediaLibrary() { {/* 移动弹窗 */} - setMoveOpen(false)} footer={null} destroyOnClose> + setMoveOpen(false)} footer={null} destroyOnHidden>
选择目标文件夹:
@@ -299,7 +299,7 @@ export default function MediaLibrary() { {/* 文件夹创建/重命名弹窗 */} - setFolderOpen(false)} onOk={() => folderForm.submit()} confirmLoading={submitting} destroyOnClose> + setFolderOpen(false)} onOk={() => folderForm.submit()} confirmLoading={submitting} destroyOnHidden> diff --git a/apps/web/src/pages/health/OfflineEventList.tsx b/apps/web/src/pages/health/OfflineEventList.tsx index 36f2831..03cffc6 100644 --- a/apps/web/src/pages/health/OfflineEventList.tsx +++ b/apps/web/src/pages/health/OfflineEventList.tsx @@ -345,7 +345,7 @@ export default function OfflineEventList() { form.resetFields(); }} onOk={() => form.submit()} - destroyOnClose + destroyOnHidden width={620} >
diff --git a/apps/web/src/pages/health/PointsOrderList.tsx b/apps/web/src/pages/health/PointsOrderList.tsx index d1e6ae9..70f6be1 100644 --- a/apps/web/src/pages/health/PointsOrderList.tsx +++ b/apps/web/src/pages/health/PointsOrderList.tsx @@ -264,7 +264,7 @@ export default function PointsOrderList() { }} onOk={() => verifyForm.submit()} confirmLoading={verifying} - destroyOnClose + destroyOnHidden width={440} > diff --git a/apps/web/src/pages/health/PointsRuleList.tsx b/apps/web/src/pages/health/PointsRuleList.tsx index 970d0af..ad213b0 100644 --- a/apps/web/src/pages/health/PointsRuleList.tsx +++ b/apps/web/src/pages/health/PointsRuleList.tsx @@ -342,7 +342,7 @@ export default function PointsRuleList() { form.resetFields(); }} onOk={() => form.submit()} - destroyOnClose + destroyOnHidden width={560} > diff --git a/apps/web/src/pages/health/components/DailyMonitoringTab.tsx b/apps/web/src/pages/health/components/DailyMonitoringTab.tsx index b71a0fd..e959d54 100644 --- a/apps/web/src/pages/health/components/DailyMonitoringTab.tsx +++ b/apps/web/src/pages/health/components/DailyMonitoringTab.tsx @@ -199,7 +199,7 @@ export function DailyMonitoringTab({ patientId }: Props) { }} onOk={() => form.submit()} confirmLoading={submitting} - destroyOnClose + destroyOnHidden width={560} > diff --git a/apps/web/src/pages/health/components/HealthRecordsTab.tsx b/apps/web/src/pages/health/components/HealthRecordsTab.tsx index f300f6d..9f6adcf 100644 --- a/apps/web/src/pages/health/components/HealthRecordsTab.tsx +++ b/apps/web/src/pages/health/components/HealthRecordsTab.tsx @@ -167,7 +167,7 @@ export function HealthRecordsTab({ patientId }: Props) { onCancel={() => { setModalOpen(false); setEditingRecord(null); }} onOk={() => form.submit()} confirmLoading={submitting} - destroyOnClose + destroyOnHidden width={520} > diff --git a/apps/web/src/pages/health/components/LabReportsTab.tsx b/apps/web/src/pages/health/components/LabReportsTab.tsx index 95fb470..ef9abaf 100644 --- a/apps/web/src/pages/health/components/LabReportsTab.tsx +++ b/apps/web/src/pages/health/components/LabReportsTab.tsx @@ -1,6 +1,6 @@ import { useCallback, useState, useMemo } from 'react'; import { Table, Tag, Button, Modal, Form, Input, DatePicker, message, Popconfirm, Space, Card } from 'antd'; -import { PlusOutlined, EditOutlined, DeleteOutlined, AuditOutlined, ThunderboltOutlined } from '@ant-design/icons'; +import { PlusOutlined, EditOutlined, DeleteOutlined, AuditOutlined, ThunderboltOutlined, FileTextOutlined } from '@ant-design/icons'; import { dayjs } from '../../../utils/dayjs'; import type { Dayjs } from 'dayjs'; import { healthDataApi } from '../../../api/health/healthData'; @@ -30,6 +30,8 @@ export function LabReportsTab({ patientId }: Props) { const [reviewSubmitting, setReviewSubmitting] = useState(false); const [analyzingReportId, setAnalyzingReportId] = useState(null); const [analysisContent, setAnalysisContent] = useState(''); + const [summaryReportId, setSummaryReportId] = useState(null); + const [summaryContent, setSummaryContent] = useState(''); const handleAiAnalysis = async (reportId: string) => { setAnalyzingReportId(reportId); @@ -44,6 +46,19 @@ export function LabReportsTab({ patientId }: Props) { }); }; + const handleReportSummary = async (reportId: string) => { + setSummaryReportId(reportId); + setSummaryContent(''); + await startAnalysis('report-summary', { report_id: reportId }, { + onChunk: (content) => setSummaryContent(prev => prev + content), + onError: (msg) => { message.error(msg); setSummaryReportId(null); }, + onDone: () => { + message.success('报告摘要生成完成'); + setSummaryReportId(null); + }, + }); + }; + const fetcher = useCallback( async (page: number, pageSize: number) => { return healthDataApi.listLabReports(patientId, { page, page_size: pageSize }); @@ -163,6 +178,11 @@ export function LabReportsTab({ patientId }: Props) { AI 解读 + + + {record.status === 'pending' && (
}>