import { useState, useEffect, useMemo, useCallback, useRef } from 'react'; import { motion, AnimatePresence } from 'motion/react'; import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, LineChart, Line, AreaChart, Area, Cell, LabelList, } from 'recharts'; import { AlertTriangle, Truck, ChevronDown, Maximize2, Minimize2, Search, ArrowUpDown, X, RotateCcw, Calendar, } from 'lucide-react'; import type { TargetSummary, TargetVehicle, TargetYearlyAssessment, TrendPoint } from './types'; import { fetchTargets, fetchTargetVehicles, fetchTrend } from './api'; import Blur from '../../components/Blur'; import { EmptyState, ErrorState, LoadingState } from '../../components/ui/surface'; import { weightedCompletionRate } from '../../shared/analytics/metrics'; import { formatMileageSnapshotDateTime } from './data-freshness'; import { assessVehicleCoverage } from './assessment-coverage'; import MileageAttributionView from './MileageAttributionView'; import { filterAttributionVehicles, type AttributionDimension, } from './attribution'; import { buildMileageDrillUrl, parseMileageDrillContext, type MileageDrillContext, } from './drill-context'; function getDefaultDate(): string { const now = new Date(); if (now.getHours() < 5) now.setDate(now.getDate() - 1); return `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')}`; } function fmtKm(value: number): string { if (value >= 10000) return (value / 10000).toFixed(2) + '万'; return value.toLocaleString(); } function fmtPercent(value: number): string { return `${value.toFixed(1)}%`; } function getTargetAssessment(target: TargetSummary, selectedYear?: number): TargetYearlyAssessment | null { if (target.yearlyAssessments.length === 0) return null; return target.yearlyAssessments.find(item => item.yearNumber === selectedYear) || target.yearlyAssessments[0]; } function resolveAssessmentYear(target: TargetSummary, requestedYear?: number): number | undefined { return getTargetAssessment(target, requestedYear)?.yearNumber; } function shortTargetName(name: string): string { // Extract the number and a short description const match = name.match(/(\d+)[辆台](.+)/); if (!match) return name; const count = match[1]; let desc = match[2]; // Simplify common patterns desc = desc.replace('4.5T普货', '普货'); desc = desc.replace('4.5T冷链车', '冷藏车'); desc = desc.replace('4.5T冷链', '冷藏车'); desc = desc.replace('18T', '18T'); return `${count}台${desc}`; } function getRequestErrorMessage(error: unknown, fallback: string): string { if (error instanceof Error && error.message && error.message !== 'Failed to fetch') { return error.message; } return fallback; } export default function StatisticsView() { const [drillContext, setDrillContext] = useState(() => ( parseMileageDrillContext(window.location.search) )); const [targets, setTargets] = useState([]); const [targetsLoading, setTargetsLoading] = useState(true); const [targetsError, setTargetsError] = useState(null); const [targetsRequestVersion, setTargetsRequestVersion] = useState(0); const [trendData, setTrendData] = useState([]); const [trendLoading, setTrendLoading] = useState(false); const [trendError, setTrendError] = useState(null); const [trendRequestVersion, setTrendRequestVersion] = useState(0); const [targetVehiclesMap, setTargetVehiclesMap] = useState>({}); const [targetVehicleLoadingMap, setTargetVehicleLoadingMap] = useState>({}); const [targetVehicleErrorMap, setTargetVehicleErrorMap] = useState>({}); const [selectedTargetId, setSelectedTargetId] = useState(drillContext.targetId || null); const [attributionDimension, setAttributionDimension] = useState(() => ( drillContext.level === 'breakdown' && drillContext.dimension === 'customer' ? 'customer' : 'department' )); const [chartType, setChartType] = useState<'bar' | 'line' | 'area'>('bar'); const [isTableFullscreen, setIsTableFullscreen] = useState(false); const [expandedTargetId, setExpandedTargetId] = useState(null); const [assessmentYearMap, setAssessmentYearMap] = useState>({}); const [viewAllTargetId, setViewAllTargetId] = useState(() => ( drillContext.level === 'breakdown' ? drillContext.targetId || null : null )); const [viewAllTargetName, setViewAllTargetName] = useState(''); const [viewAllSearch, setViewAllSearch] = useState(''); const [viewAllSort, setViewAllSort] = useState<'asc' | 'desc'>('desc'); const [viewAllDate, setViewAllDate] = useState(getDefaultDate); const [viewAllLoading, setViewAllLoading] = useState(false); const [viewAllHistoricalVehicles, setViewAllHistoricalVehicles] = useState(null); const targetVehicleRequestsRef = useRef(new Set()); const initialDrillContextRef = useRef(drillContext); const selectedTarget = targets.find(t => t.id === selectedTargetId); const selectedTargetVehicles = selectedTargetId === null ? [] : targetVehiclesMap[selectedTargetId] || []; const selectedAssessment = selectedTarget ? getTargetAssessment(selectedTarget, assessmentYearMap[selectedTarget.id]) : null; const selectedCoverage = selectedTarget ? assessVehicleCoverage(selectedTarget.declaredVehicleCount, selectedTarget.vehicleCount) : null; const selectedAssessmentYear = selectedAssessment?.yearNumber; const selectedCompletion = selectedAssessment?.completionRate ?? selectedTarget?.avgCompletion ?? 0; const selectedRemaining = selectedAssessment?.remaining ?? 0; const selectedDaysLeft = selectedAssessment?.daysLeft ?? selectedTarget?.daysLeft ?? 0; const selectedDailyTarget = selectedAssessment?.dailyTarget ?? (selectedDaysLeft > 0 ? selectedRemaining / selectedDaysLeft : 0); const selectedQualifiedRate = selectedAssessment?.qualifiedRate ?? (selectedTarget?.vehicleCount ? selectedTarget.yearQualifiedCount / selectedTarget.vehicleCount * 100 : 0); const selectedYearCompletionRate = weightedCompletionRate( targets.flatMap(target => { const assessment = getTargetAssessment(target, assessmentYearMap[target.id]); return assessment ? [{ completed: assessment.completed, target: assessment.target }] : []; }), ); const latestTrend = trendData[trendData.length - 1]; const previousTrend = trendData[trendData.length - 2]; const trendDelta = latestTrend && previousTrend ? latestTrend.mileage - previousTrend.mileage : 0; const pressureLevel = selectedCompletion >= 90 ? '健康' : selectedCompletion >= 70 ? '关注' : '高压'; const selectedBreakdownValue = drillContext.level === 'breakdown' ? drillContext.dimensionValue : undefined; const commitDrillContext = useCallback((next: MileageDrillContext, mode: 'push' | 'replace') => { const url = buildMileageDrillUrl(window.location, next); window.history[mode === 'push' ? 'pushState' : 'replaceState'](null, '', url); setDrillContext(next); }, []); const retryTargets = useCallback(() => { setTargetsRequestVersion(version => version + 1); }, []); const retryTrend = useCallback(() => { setTrendRequestVersion(version => version + 1); }, []); const loadCurrentTargetVehicles = useCallback((targetId: number) => { if (targetVehicleRequestsRef.current.has(targetId)) return; targetVehicleRequestsRef.current.add(targetId); setTargetVehicleLoadingMap(prev => ({ ...prev, [targetId]: true })); setTargetVehicleErrorMap(prev => { const next = { ...prev }; delete next[targetId]; return next; }); fetchTargetVehicles(targetId).then(vehicles => { setTargetVehiclesMap(prev => ({ ...prev, [targetId]: vehicles })); }).catch(error => { setTargetVehicleErrorMap(prev => ({ ...prev, [targetId]: getRequestErrorMessage(error, '无法获取考核车辆数据,请稍后重试。'), })); }).finally(() => { setTargetVehicleLoadingMap(prev => ({ ...prev, [targetId]: false })); targetVehicleRequestsRef.current.delete(targetId); }); }, []); const selectTarget = useCallback((targetId: number) => { const target = targets.find(item => item.id === targetId); const assessmentYear = target ? resolveAssessmentYear(target, assessmentYearMap[targetId]) : undefined; setSelectedTargetId(targetId); setExpandedTargetId(targetId); setViewAllTargetId(null); setViewAllSearch(''); commitDrillContext({ level: 'overview', targetId, assessmentYear, sourcePriority: ['instrument'], }, 'replace'); }, [assessmentYearMap, commitDrillContext, targets]); const openAttributionGroup = useCallback((value: string) => { if (selectedTargetId === null) return; setViewAllTargetId(selectedTargetId); setViewAllTargetName(selectedTarget?.targetName || '考核车辆'); setViewAllSearch(''); setViewAllDate(getDefaultDate()); setViewAllHistoricalVehicles(null); commitDrillContext({ level: 'breakdown', targetId: selectedTargetId, assessmentYear: selectedAssessmentYear, dimension: attributionDimension, dimensionValue: value, sourcePriority: ['instrument'], }, 'push'); }, [attributionDimension, commitDrillContext, selectedAssessmentYear, selectedTarget?.targetName, selectedTargetId]); const openTargetVehicles = useCallback((target: TargetSummary) => { const assessmentYear = resolveAssessmentYear(target, assessmentYearMap[target.id]); setViewAllTargetId(target.id); setViewAllTargetName(target.targetName); setViewAllSearch(''); setViewAllDate(getDefaultDate()); setViewAllHistoricalVehicles(null); commitDrillContext({ level: 'breakdown', targetId: target.id, assessmentYear, dimension: 'assessment-target', dimensionValue: target.targetName, sourcePriority: ['instrument'], }, 'push'); }, [assessmentYearMap, commitDrillContext]); const closeVehiclePanel = useCallback(() => { setViewAllTargetId(null); setViewAllSearch(''); if (drillContext.level === 'breakdown') { commitDrillContext({ level: 'overview', targetId: selectedTargetId || undefined, assessmentYear: selectedAssessmentYear, sourcePriority: ['instrument'], }, 'replace'); } }, [commitDrillContext, drillContext.level, selectedAssessmentYear, selectedTargetId]); const openVehicleDiagnostic = useCallback((vehicle: TargetVehicle) => { const url = buildMileageDrillUrl( { pathname: window.location.pathname, search: window.location.search, hash: '#mileage' }, { level: 'vehicle', targetId: viewAllTargetId || selectedTargetId || undefined, assessmentYear: selectedAssessmentYear, plate: vehicle.plateNumber, startDate: viewAllDate, endDate: viewAllDate, sourcePriority: ['instrument'], }, ); window.history.pushState(null, '', url); window.dispatchEvent(new HashChangeEvent('hashchange')); }, [selectedAssessmentYear, selectedTargetId, viewAllDate, viewAllTargetId]); // Load targets on mount and through the shared retry path. useEffect(() => { let cancelled = false; setTargetsLoading(true); setTargetsError(null); fetchTargets().then(data => { if (cancelled) return; const initialContext = initialDrillContextRef.current; const focused = data.find(item => item.targetName.includes('羚牛136')) || data[0]; const ordered = focused ? [focused, ...data.filter(item => item.id !== focused.id)] : data; setTargets(ordered); const initialTarget = ordered.find(item => item.id === selectedTargetId) || focused; if (initialTarget) { setSelectedTargetId(initialTarget.id); setExpandedTargetId(initialTarget.id); const initialYears = Object.fromEntries(ordered.map(item => [ item.id, resolveAssessmentYear(item, item.id === initialContext.targetId ? initialContext.assessmentYear : undefined) || 1, ])); setAssessmentYearMap(initialYears); if (initialContext.targetId === initialTarget.id) { const resolvedYear = initialYears[initialTarget.id]; if (resolvedYear && resolvedYear !== initialContext.assessmentYear) { const normalizedContext = { ...initialContext, assessmentYear: resolvedYear }; window.history.replaceState(null, '', buildMileageDrillUrl(window.location, normalizedContext)); setDrillContext(normalizedContext); } } } else { setSelectedTargetId(null); setExpandedTargetId(null); setAssessmentYearMap({}); } }).catch(error => { if (!cancelled) { setTargetsError(getRequestErrorMessage(error, '无法获取考核目标数据,请检查服务连接后重试。')); } }).finally(() => { if (!cancelled) setTargetsLoading(false); }); return () => { cancelled = true; }; }, [targetsRequestVersion]); // Load trend when selectedTargetId changes useEffect(() => { if (selectedTargetId === null) { setTrendData([]); setTrendError(null); setTrendLoading(false); return; } let cancelled = false; setExpandedTargetId(selectedTargetId); if (!targetVehiclesMap[selectedTargetId]) { loadCurrentTargetVehicles(selectedTargetId); } setTrendLoading(true); setTrendError(null); fetchTrend(selectedTargetId).then(data => { if (!cancelled) setTrendData(data); }).catch(error => { if (!cancelled) { setTrendData([]); setTrendError(getRequestErrorMessage(error, '无法获取里程趋势,请稍后重试。')); } }).finally(() => { if (!cancelled) setTrendLoading(false); }); return () => { cancelled = true; }; }, [selectedTargetId, trendRequestVersion]); // Re-fetch target vehicles when viewAllDate changes useEffect(() => { if (viewAllTargetId === null) return; setViewAllLoading(true); fetchTargetVehicles(viewAllTargetId, viewAllDate).then(data => { setViewAllHistoricalVehicles(data); }).catch(() => {}).finally(() => setViewAllLoading(false)); }, [viewAllTargetId, viewAllDate]); useEffect(() => { const handlePopState = () => { const next = parseMileageDrillContext(window.location.search); setDrillContext(next); if (next.targetId) { setSelectedTargetId(next.targetId); const target = targets.find(item => item.id === next.targetId); const resolvedYear = target ? resolveAssessmentYear(target, next.assessmentYear) : undefined; if (resolvedYear) { setAssessmentYearMap(prev => ({ ...prev, [next.targetId!]: resolvedYear })); } } if (next.level === 'breakdown' && next.targetId && next.dimensionValue) { setAttributionDimension(next.dimension === 'customer' ? 'customer' : 'department'); setViewAllTargetId(next.targetId); } else { setViewAllTargetId(null); } }; window.addEventListener('popstate', handlePopState); return () => window.removeEventListener('popstate', handlePopState); }, [targets]); const viewAllVehicles = useMemo(() => { const vehicles = viewAllHistoricalVehicles || (viewAllTargetId === null ? [] : targetVehiclesMap[viewAllTargetId] || []); if ( drillContext.level === 'breakdown' && drillContext.dimensionValue && (drillContext.dimension === 'department' || drillContext.dimension === 'customer') ) { return filterAttributionVehicles(vehicles, drillContext.dimension, drillContext.dimensionValue); } return vehicles; }, [drillContext, targetVehiclesMap, viewAllHistoricalVehicles, viewAllTargetId]); const searchedViewAllVehicles = useMemo(() => ( viewAllVehicles.filter(vehicle => ( vehicle.plateNumber.toLowerCase().includes(viewAllSearch.toLowerCase()) )) ), [viewAllSearch, viewAllVehicles]); if (targetsLoading && targets.length === 0) { return ; } if (targetsError && targets.length === 0) { return ( ); } if (!targetsLoading && targets.length === 0) { return ( ); } return (
{/* Project Selector */}
{targets.map(target => ( ))}
{targetsError && ( )} {selectedTarget && (
考核压力
{pressureLevel}
完成率 {fmtPercent(selectedCompletion)}
剩余缺口
{fmtKm(Math.max(0, selectedRemaining))}km
剩余 {selectedDaysLeft} 天
日均需完成
{selectedDaysLeft > 0 ? ( <> {fmtKm(selectedDailyTarget)}km ) : '已到期'}
按当前考核口径
最新日变化
= 0 ? 'text-emerald-600' : 'text-rose-600'}`}> {trendLoading ? '加载中' : trendError ? '暂不可用' : `${trendDelta >= 0 ? '+' : ''}${fmtKm(trendDelta)}`}
达标率 {fmtPercent(selectedQualifiedRate)}
)} {selectedTarget && selectedCoverage && selectedCoverage.status !== 'matched' && (
考核车辆配置不一致: 目标 {selectedCoverage.declaredCount} 台,当前有效 {selectedCoverage.activeCount} 台, {selectedCoverage.status === 'missing' ? `缺少 ${Math.abs(selectedCoverage.difference)} 台` : `超出 ${selectedCoverage.difference} 台`}。 完成率、缺口和归因按当前有效车辆计算。
)} {selectedTarget && ( { setAttributionDimension(dimension); if (drillContext.level === 'breakdown') closeVehiclePanel(); }} onSelect={openAttributionGroup} onRetry={() => loadCurrentTargetVehicles(selectedTarget.id)} /> )}
{/* Left Side: Trend Chart / Dashboard Sidebar */}
{/* KPI Cards in Landscape — linked to selected target */} {(() => { const sel = selectedTarget; return (
今日总里程
{fmtKm(sel?.todayTotal ?? 0)} KM
累计总里程
{fmtKm(sel?.cumulativeTotal ?? 0)} KM
考核车辆
{sel?.vehicleCount ?? 0}
完成率
{selectedCompletion.toFixed(1)} %
); })()}

7天里程趋势

{(['bar', 'line', 'area'] as const).map(type => ( ))}
{trendLoading ? ( ) : trendError ? ( ) : trendData.length === 0 ? ( ) : ( {chartType === 'bar' ? ( { const [m, d] = val.split('-'); return `${parseInt(m)}.${parseInt(d)}`; }} /> {trendData.map((_entry: TrendPoint, index: number) => ( ))} ) : chartType === 'line' ? ( { const [m, d] = val.split('-'); return `${parseInt(m)}.${parseInt(d)}`; }} /> ) : ( { const [m, d] = val.split('-'); return `${parseInt(m)}.${parseInt(d)}`; }} /> )} )}
{/* Right Side: Summary Section */}

当前车型考核详情

{selectedTarget?.targetName || '请选择车型'}

{(selectedTarget ? [selectedTarget] : []).map((target, idx) => ( (() => { const assessment = getTargetAssessment(target, assessmentYearMap[target.id]); const primaryCompletion = assessment?.completionRate ?? target.avgCompletion; const primaryQualified = assessment?.qualifiedCount ?? target.yearQualifiedCount; const primaryQualifiedLabel = assessment ? `${assessment.label}达标:` : '达标:'; return (
{ setExpandedTargetId(target.id); }} >
{target.targetName} {target.vehicleCount}台
{assessment ? `${assessment.label}完成:` : '完成率:'} = 90 ? 'text-emerald-500' : 'text-blue-500'}`}>{fmtPercent(primaryCompletion)}
{primaryQualifiedLabel} {primaryQualified}台
{fmtKm(target.todayTotal)} KM
累计: {fmtKm(target.cumulativeTotal)} KM
{expandedTargetId === target.id && (
考核年度

总考核区间

{target.periods.map((p, i) => (

{p}

))}

总考核里程

{fmtKm(target.totalMileagePerVehicle * target.vehicleCount)} km

{assessment ? ( <>

{assessment.label}区间

{(assessment.periods.length > 0 ? assessment.periods : [`${assessment.startDate} ~ ${assessment.endDate}`]).map((period, i) => (

{period}

))}

{assessment.label}任务/辆

{fmtKm(target.annualMileagePerVehicle * assessment.yearNumber)} km

{assessment.label}已进入车辆

{assessment.vehicleCount} 台

{assessment.vehicleCount < target.vehicleCount && (

其余 {target.vehicleCount - assessment.vehicleCount} 台尚未进入{assessment.label}

)}

{assessment.label}需完成

{fmtKm(assessment.target)} km

{assessment.label}已完成

{fmtKm(assessment.completed)} km

数据截至 {formatMileageSnapshotDateTime(target.snapshotUpdatedAt)}

{assessment.label}完成率

{fmtPercent(assessment.completionRate)}

{assessment.label}达标率

{fmtPercent(assessment.qualifiedRate)} ({assessment.qualifiedCount}台)

{assessment.label}剩余里程

{fmtKm(assessment.remaining)} km

{assessment.label}日均需完成

{assessment.daysLeft > 0 ? `${fmtKm(assessment.dailyTarget)} km` : '考核已到期'}

) : (
暂无第一年度车辆,当前车型车辆已进入后续考核年度。
)}
{assessment ? `${assessment.label}剩余考核天数` : '剩余考核天数'} {assessment?.daysLeft ?? target.daysLeft} 天
{/* Vehicle List Detail */}
车辆明细 (前5台)
{(targetVehiclesMap[target.id] || []).slice(0, 5).map(tv => (
{tv.plateNumber} 在线
{tv.todayMileage} KM
))}
)}
); })() ))}
{/* Fullscreen Table Overlay */} {isTableFullscreen && ( {/* Top bar: compact inline KPI */}

车型考核汇总

今日 {fmtKm(targets.reduce((sum, t) => sum + t.todayTotal, 0))} km | 累计 {fmtKm(targets.reduce((sum, t) => sum + t.cumulativeTotal, 0))} km | 车辆 {targets.reduce((sum, t) => sum + t.vehicleCount, 0)} | 所选年度完成率 {selectedYearCompletionRate.toFixed(1)} %
{targetsError && (
{targetsError} 当前仍展示上一次成功加载的数据。
)} {/* Table Area */}
{targets.map((target, idx) => { const assessment = getTargetAssessment(target, assessmentYearMap[target.id]); const completion = assessment?.completionRate ?? target.avgCompletion; const qualified = assessment?.qualifiedCount ?? target.yearQualifiedCount; const halfQualified = assessment?.halfQualifiedCount ?? target.halfQualifiedCount; const goal = assessment?.target ?? target.currentYearTarget; const completed = assessment?.completed ?? target.currentYearCompleted; const remainingMileage = assessment?.remaining ?? target.remaining; const days = assessment?.daysLeft ?? target.daysLeft; const daily = assessment?.dailyTarget ?? target.dailyTarget; const taskPerVehicle = target.annualMileagePerVehicle * (assessment?.yearNumber || 1); return ( ); })}
车型 台数 年度进度 年度任务/辆 年度达标 50%达标 今日里程 年度目标 已完成 未完成 余天 日均需完成
{target.targetName}
{assessment?.label || '当前年度'}
{target.periods.map((p, i) => {p})}
{target.vehicleCount}
= 90 ? 'bg-emerald-500' : completion >= 50 ? 'bg-amber-500' : 'bg-amber-500/60'}`} style={{ width: `${Math.min(completion, 100)}%` }} />
{completion.toFixed(1)}%
{fmtKm(completed)} / {fmtKm(goal)} km
{fmtKm(taskPerVehicle)} km {qualified} {halfQualified} {fmtKm(target.todayTotal)} km {fmtKm(goal)} km {fmtKm(completed)} km {fmtKm(remainingMileage)} km {days} {assessment && days === 0 ? '考核已到期' : `${fmtKm(daily)} km`}
)}
{/* View All Vehicles Side Panel */} {viewAllTargetId !== null && ( <>

{viewAllTargetName || selectedTarget?.targetName || '考核车辆'}

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

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

{selectedBreakdownValue}

)}
setViewAllSearch(e.target.value)} className="w-full pl-9 pr-3 py-2 bg-slate-50 border border-slate-100 rounded-xl text-xs font-bold focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all" />
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]" />
{viewAllLoading ? '加载中...' : (() => { const totalKm = searchedViewAllVehicles.reduce((sum, vehicle) => sum + (vehicle.todayMileage || 0), 0); return `${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 => ( ))}
)}
); }