From 017001e889b35b60f32936494d8f76754b301982 Mon Sep 17 00:00:00 2001 From: kkfluous Date: Fri, 7 Aug 2026 18:09:52 +0800 Subject: [PATCH] fix: preserve dated vehicle panel snapshots --- docs/bi-refactor-roadmap.md | 1 + src/modules/mileage/StatisticsView.tsx | 164 ++++++++++++++++++------- 2 files changed, 118 insertions(+), 47 deletions(-) diff --git a/docs/bi-refactor-roadmap.md b/docs/bi-refactor-roadmap.md index b9f02d5..fced716 100644 --- a/docs/bi-refactor-roadmap.md +++ b/docs/bi-refactor-roadmap.md @@ -174,6 +174,7 @@ SUM(fee) / NULLIF(SUM(kwh), 0) - 里程考核接口区分目标声明台数与当前有效车辆数;两者不一致时页面明确标注缺少或超配数量,并说明完成率、缺口和归因的实际计算范围。 - 里程考核统计主数据已区分加载、真实空数据、首次请求故障和保留旧数据的刷新故障,并提供统一重试入口,接口失败不再显示为零值或空页面。 - 里程考核归因车辆与 7 天趋势已提供独立加载、故障、空数据和重试状态;子请求失败时停止展示“0 台未达标”和“最新日变化 +0”等伪业务结果。 +- 里程考核车辆日期侧栏仅展示带目标和日期标识的成功快照;切换日期失败时保留并标注上一成功日期,首次失败不再回退为当前日数据或伪造“0 辆”。 - 已完成电能数据截至时间和实际趋势月展示。 - 电能日期下钻自动核对日汇总与订单全量合计的日期、车辆范围、电量和费用,并显式展示通过或差异状态。 - 已完成氢能结构化故障状态与重试体验。 diff --git a/src/modules/mileage/StatisticsView.tsx b/src/modules/mileage/StatisticsView.tsx index 2cd84b5..2a1b724 100644 --- a/src/modules/mileage/StatisticsView.tsx +++ b/src/modules/mileage/StatisticsView.tsx @@ -104,7 +104,11 @@ export default function StatisticsView() { const [viewAllSort, setViewAllSort] = useState<'asc' | 'desc'>('desc'); const [viewAllDate, setViewAllDate] = useState(getDefaultDate); const [viewAllLoading, setViewAllLoading] = useState(false); + const [viewAllError, setViewAllError] = useState(null); + const [viewAllRequestVersion, setViewAllRequestVersion] = useState(0); const [viewAllHistoricalVehicles, setViewAllHistoricalVehicles] = useState(null); + const [viewAllLoadedDate, setViewAllLoadedDate] = useState(null); + const [viewAllLoadedTargetId, setViewAllLoadedTargetId] = useState(null); const targetVehicleRequestsRef = useRef(new Set()); const initialDrillContextRef = useRef(drillContext); @@ -148,6 +152,10 @@ export default function StatisticsView() { setTrendRequestVersion(version => version + 1); }, []); + const retryViewAllVehicles = useCallback(() => { + setViewAllRequestVersion(version => version + 1); + }, []); + const loadCurrentTargetVehicles = useCallback((targetId: number) => { if (targetVehicleRequestsRef.current.has(targetId)) return; targetVehicleRequestsRef.current.add(targetId); @@ -194,6 +202,9 @@ export default function StatisticsView() { setViewAllSearch(''); setViewAllDate(getDefaultDate()); setViewAllHistoricalVehicles(null); + setViewAllLoadedDate(null); + setViewAllLoadedTargetId(null); + setViewAllError(null); commitDrillContext({ level: 'breakdown', targetId: selectedTargetId, @@ -211,6 +222,9 @@ export default function StatisticsView() { setViewAllSearch(''); setViewAllDate(getDefaultDate()); setViewAllHistoricalVehicles(null); + setViewAllLoadedDate(null); + setViewAllLoadedTargetId(null); + setViewAllError(null); commitDrillContext({ level: 'breakdown', targetId: target.id, @@ -332,11 +346,25 @@ export default function StatisticsView() { // Re-fetch target vehicles when viewAllDate changes useEffect(() => { if (viewAllTargetId === null) return; + let cancelled = false; setViewAllLoading(true); + setViewAllError(null); fetchTargetVehicles(viewAllTargetId, viewAllDate).then(data => { + if (cancelled) return; setViewAllHistoricalVehicles(data); - }).catch(() => {}).finally(() => setViewAllLoading(false)); - }, [viewAllTargetId, viewAllDate]); + setViewAllLoadedDate(viewAllDate); + setViewAllLoadedTargetId(viewAllTargetId); + }).catch(error => { + if (!cancelled) { + setViewAllError(getRequestErrorMessage(error, '无法获取所选日期的车辆明细,请稍后重试。')); + } + }).finally(() => { + if (!cancelled) setViewAllLoading(false); + }); + return () => { + cancelled = true; + }; + }, [viewAllTargetId, viewAllDate, viewAllRequestVersion]); useEffect(() => { const handlePopState = () => { @@ -361,9 +389,10 @@ export default function StatisticsView() { return () => window.removeEventListener('popstate', handlePopState); }, [targets]); + const hasViewAllSnapshot = viewAllHistoricalVehicles !== null + && viewAllLoadedTargetId === viewAllTargetId; const viewAllVehicles = useMemo(() => { - const vehicles = viewAllHistoricalVehicles - || (viewAllTargetId === null ? [] : targetVehiclesMap[viewAllTargetId] || []); + const vehicles = hasViewAllSnapshot ? viewAllHistoricalVehicles : []; if ( drillContext.level === 'breakdown' && drillContext.dimensionValue @@ -372,7 +401,7 @@ export default function StatisticsView() { return filterAttributionVehicles(vehicles, drillContext.dimension, drillContext.dimensionValue); } return vehicles; - }, [drillContext, targetVehiclesMap, viewAllHistoricalVehicles, viewAllTargetId]); + }, [drillContext, hasViewAllSnapshot, viewAllHistoricalVehicles]); const searchedViewAllVehicles = useMemo(() => ( viewAllVehicles.filter(vehicle => ( @@ -996,7 +1025,7 @@ export default function StatisticsView() { {viewAllTargetName || selectedTarget?.targetName || '考核车辆'}

- {selectedAssessment ? `${selectedAssessment.label} · ` : ''}车辆实时明细清单 + {selectedAssessment ? `${selectedAssessment.label} · ` : ''}车辆日期明细清单

{selectedBreakdownValue && drillContext.dimension !== 'assessment-target' && (

{selectedBreakdownValue}

@@ -1010,7 +1039,7 @@ export default function StatisticsView() { -
+
@@ -1028,16 +1057,27 @@ export default function StatisticsView() { type="date" value={viewAllDate} onChange={(e) => setViewAllDate(e.target.value)} - className="pl-8 pr-2 py-2 bg-slate-50 border border-slate-100 rounded-xl text-xs font-bold text-slate-700 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all w-[130px]" + className="w-[145px] bg-slate-50 py-2 pl-8 pr-2 text-xs font-bold text-slate-700 border border-slate-100 rounded-xl focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all" />
- {viewAllLoading ? '加载中...' : (() => { - const totalKm = searchedViewAllVehicles.reduce((sum, vehicle) => sum + (vehicle.todayMileage || 0), 0); - return `${searchedViewAllVehicles.length} 辆 · 合计 ${fmtKm(totalKm)} km`; - })()} + {viewAllLoading + ? hasViewAllSnapshot + ? `正在加载 ${viewAllDate} · 当前显示 ${viewAllLoadedDate}` + : `正在加载 ${viewAllDate}` + : viewAllError + ? hasViewAllSnapshot + ? `当前显示 ${viewAllLoadedDate} 已成功数据` + : `${viewAllDate} 暂不可用` + : (() => { + const totalKm = searchedViewAllVehicles.reduce( + (sum, vehicle) => sum + (vehicle.todayMileage || 0), + 0, + ); + return `${viewAllLoadedDate || viewAllDate} · ${searchedViewAllVehicles.length} 辆 · 合计 ${fmtKm(totalKm)} km`; + })()}
- {[...searchedViewAllVehicles].sort((a, b) => { - const valA = a.todayMileage || 0; - const valB = b.todayMileage || 0; - return viewAllSort === 'desc' ? valB - valA : valA - valB; - }).map(tv => ( - - ))} + + ))} + + )}