diff --git a/apps/web/src/api/client.ts b/apps/web/src/api/client.ts index 3554193..b4f1260 100644 --- a/apps/web/src/api/client.ts +++ b/apps/web/src/api/client.ts @@ -104,7 +104,7 @@ client.interceptors.response.use( }, async (error) => { const originalRequest = error.config; - if (error.response?.status === 401 && !originalRequest._retry) { + if (error.response?.status === 401 && !originalRequest._retry && !originalRequest.url?.includes('/auth/login')) { if (isRefreshing) { return new Promise((resolve, reject) => { failedQueue.push({ resolve, reject }); diff --git a/apps/web/src/pages/Home.tsx b/apps/web/src/pages/Home.tsx index eae9d1b..ad73694 100644 --- a/apps/web/src/pages/Home.tsx +++ b/apps/web/src/pages/Home.tsx @@ -186,7 +186,7 @@ export default function Home() { setActivitiesLoading(true); try { const result = await listAuditLogs({ page: 1, page_size: 5 }); - if (!cancelled) setRecentActivities(result.data); + if (!cancelled) setRecentActivities(result.data.filter(a => a.action !== 'login_failed')); } catch { // 静默处理 } finally { diff --git a/apps/web/src/pages/health/FollowUpTaskList.tsx b/apps/web/src/pages/health/FollowUpTaskList.tsx index bb17439..ce6e779 100644 --- a/apps/web/src/pages/health/FollowUpTaskList.tsx +++ b/apps/web/src/pages/health/FollowUpTaskList.tsx @@ -16,6 +16,7 @@ import type { ColumnsType, TablePaginationConfig } from 'antd/es/table'; import dayjs from 'dayjs'; import { followUpApi, type FollowUpTask, type CreateFollowUpTaskReq, type UpdateFollowUpTaskReq } from '../../api/health/followUp'; import { patientApi } from '../../api/health/patients'; +import { getUser } from '../../api/users'; import { StatusTag } from './components/StatusTag'; import { PatientSelect } from './components/PatientSelect'; import { DoctorSelect } from './components/DoctorSelect'; @@ -120,6 +121,21 @@ export default function FollowUpTaskList() { if (Object.keys(newLabels).length > 0) { setPatientLabels((prev) => ({ ...prev, ...newLabels })); } + + // Batch resolve assignee names + const assigneeIds = [...new Set(result.data.map((t: FollowUpTask) => t.assigned_to).filter(Boolean))]; + const newDoctorLabels: Record = {}; + await Promise.allSettled( + assigneeIds.map(async (id: string) => { + try { + const u = await getUser(id); + newDoctorLabels[id] = u.display_name || u.username; + } catch { /* skip */ } + }), + ); + if (Object.keys(newDoctorLabels).length > 0) { + setDoctorLabels((prev) => ({ ...prev, ...newDoctorLabels })); + } } catch { message.error('加载随访任务失败'); } finally { diff --git a/apps/web/src/pages/health/StatisticsDashboard.tsx b/apps/web/src/pages/health/StatisticsDashboard.tsx index 94856af..f75e8d2 100644 --- a/apps/web/src/pages/health/StatisticsDashboard.tsx +++ b/apps/web/src/pages/health/StatisticsDashboard.tsx @@ -92,25 +92,33 @@ export default function StatisticsDashboard() { const fetchAllStats = useCallback(async () => { setLoading(true); setError(null); - try { - const [patients, consultations, followUps, points, healthData] = await Promise.all([ - pointsApi.getPatientStats(), - pointsApi.getConsultationStats(), - pointsApi.getFollowUpStats(), - pointsApi.getStatistics(), - pointsApi.getHealthDataStats(), - ]); - setPatientStats(patients); - setConsultationStats(consultations); - setFollowUpStats(followUps); - setPointsStats(points); - setHealthDataStats(healthData); - } catch (err: unknown) { - const message = err instanceof Error ? err.message : '加载统计数据失败'; - setError(message); - } finally { - setLoading(false); + + let hasAnyError = false; + const errors: string[] = []; + + const tryFetch = async (fn: () => Promise, setter: (v: T) => void, label: string) => { + try { + const data = await fn(); + setter(data); + } catch { + hasAnyError = true; + errors.push(label); + } + }; + + await Promise.all([ + tryFetch(pointsApi.getPatientStats, setPatientStats, '患者'), + tryFetch(pointsApi.getConsultationStats, setConsultationStats, '咨询'), + tryFetch(pointsApi.getFollowUpStats, setFollowUpStats, '随访'), + tryFetch(pointsApi.getStatistics, setPointsStats, '积分'), + tryFetch(pointsApi.getHealthDataStats, setHealthDataStats, '健康数据'), + ]); + + if (hasAnyError && errors.length === 5) { + setError('加载统计数据失败'); } + + setLoading(false); }, []); useEffect(() => { diff --git a/apps/web/src/pages/messages/NotificationList.tsx b/apps/web/src/pages/messages/NotificationList.tsx index cf28687..49681a7 100644 --- a/apps/web/src/pages/messages/NotificationList.tsx +++ b/apps/web/src/pages/messages/NotificationList.tsx @@ -7,6 +7,16 @@ import { useThemeMode } from '../../hooks/useThemeMode'; const { Paragraph } = Typography; +function formatDateTime(value: string): string { + return new Date(value).toLocaleString('zh-CN', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + }); +} + interface Props { queryFilter?: MessageQuery; } @@ -84,7 +94,7 @@ export default function NotificationList({ queryFilter }: Props) {
{record.body}
- {record.created_at} + {formatDateTime(record.created_at)}
), @@ -170,7 +180,7 @@ export default function NotificationList({ queryFilter }: Props) { key: 'created_at', width: 180, render: (v: string) => ( - {v} + {formatDateTime(v)} ), }, { diff --git a/crates/erp-health/src/service/stats_service.rs b/crates/erp-health/src/service/stats_service.rs index eae3ea6..f6460a8 100644 --- a/crates/erp-health/src/service/stats_service.rs +++ b/crates/erp-health/src/service/stats_service.rs @@ -426,6 +426,8 @@ async fn compute_avg_field( field: &str, ) -> AppResult> { const ALLOWED_FIELDS: &[&str] = &[ + "ultrafiltration_volume", + "dialysis_duration", "uf_volume", "uf_rate", "blood_flow_rate",