From d610e4b841b980c6e12932fb79a6e5701254160e Mon Sep 17 00:00:00 2001 From: kkfluous Date: Wed, 12 Aug 2026 23:17:06 +0800 Subject: [PATCH] feat(mileage): add report loading overlay --- src/modules/mileage/DailyReportView.tsx | 82 +++++++++++++++++++------ 1 file changed, 62 insertions(+), 20 deletions(-) diff --git a/src/modules/mileage/DailyReportView.tsx b/src/modules/mileage/DailyReportView.tsx index 89a6846..a9a4f1d 100644 --- a/src/modules/mileage/DailyReportView.tsx +++ b/src/modules/mileage/DailyReportView.tsx @@ -41,7 +41,6 @@ import { import Blur from '../../components/Blur'; import { ErrorState, - LoadingState, MetricTile, SurfaceCard, } from '../../components/ui/surface'; @@ -57,6 +56,7 @@ type VehicleSortKey = 'dailyMileage' | 'sevenDayMileage' | 'completionRate'; type SortDirection = 'asc' | 'desc'; const VEHICLE_PAGE_SIZE = 20; +const MIN_REPORT_LOADING_MS = 280; const VEHICLE_FILTERS: { id: VehicleFilter; label: string }[] = [ { id: 'ALL', label: '全部' }, @@ -209,6 +209,48 @@ function ReportDatePicker({ ); } +function DailyReportLoadingOverlay({ reportDate }: { reportDate: string }) { + return ( + + +
+ + + + +
+
正在加载日报
+
正在汇总 {fmtDate(reportDate)} 的里程数据
+
+ +
+
+
+ ); +} + function updateReportDateInUrl(date: string): void { const url = new URL(window.location.href); url.searchParams.set('mileageReportDate', date); @@ -831,7 +873,7 @@ function roundMileageForDisplay(value: number): number { } export default function DailyReportView() { - const maxDate = shanghaiYmd(); + const maxDate = shanghaiYmd(-1); const [selectedDate, setSelectedDate] = useState(initialReportDate); const [report, setReport] = useState(null); const [loading, setLoading] = useState(true); @@ -841,6 +883,8 @@ export default function DailyReportView() { useEffect(() => { let active = true; + let finishTimer: number | undefined; + const loadingStartedAt = performance.now(); setLoading(true); setError(''); updateReportDateInUrl(selectedDate); @@ -855,8 +899,17 @@ export default function DailyReportView() { if (!active) return; setError(cause instanceof Error ? cause.message : '日报加载失败'); }) - .finally(() => { if (active) setLoading(false); }); - return () => { active = false; }; + .finally(() => { + if (!active) return; + const delay = Math.max(0, MIN_REPORT_LOADING_MS - (performance.now() - loadingStartedAt)); + finishTimer = window.setTimeout(() => { + if (active) setLoading(false); + }, delay); + }); + return () => { + active = false; + if (finishTimer != null) window.clearTimeout(finishTimer); + }; }, [selectedDate]); const highMileageRate = report && report.totals.operatingCount > 0 @@ -871,7 +924,7 @@ export default function DailyReportView() { note => note.message !== '日行驶总里程延续人工汇报口径,包含库存车辆产生的里程。', ) || []; - if (loading && !report) return ; + if (loading && !report) return ; if (!report) return ; const trendGroup = trendTargetId == null ? null : report.groups.find(group => group.targetId === trendTargetId) || null; @@ -881,7 +934,7 @@ export default function DailyReportView() { const trendColor = trendGroup ? '#0f766e' : '#2563eb'; return ( -
+
@@ -917,15 +970,6 @@ export default function DailyReportView() { -
);