import { useState, useEffect } 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 { 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'; 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 getCurrentDateLabel(): string { const now = new Date(); return `${now.getFullYear()}.${now.getMonth() + 1}.${now.getDate()}`; } 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 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}`; } export default function StatisticsView() { const currentDateLabel = getCurrentDateLabel(); const [targets, setTargets] = useState([]); const [trendData, setTrendData] = useState([]); const [targetVehiclesMap, setTargetVehiclesMap] = useState>({}); const [selectedTargetId, setSelectedTargetId] = useState(null); 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(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 selectedTarget = targets.find(t => t.id === selectedTargetId); const selectedAssessment = selectedTarget ? getTargetAssessment(selectedTarget, assessmentYearMap[selectedTarget.id]) : null; const selectedCompletion = selectedAssessment?.completionRate ?? selectedTarget?.avgCompletion ?? 0; // Load targets on mount useEffect(() => { fetchTargets().then(data => { 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); if (ordered.length > 0 && !selectedTargetId) { setSelectedTargetId(focused.id); setExpandedTargetId(focused.id); setAssessmentYearMap(Object.fromEntries(ordered.map(item => [item.id, item.yearlyAssessments[0]?.yearNumber || 1]))); fetchTargetVehicles(focused.id).then(vehicles => { setTargetVehiclesMap(prev => ({ ...prev, [focused.id]: vehicles })); }).catch(() => {}); } }).catch(() => {}); }, []); // Load trend when selectedTargetId changes useEffect(() => { if (selectedTargetId === null) return; fetchTrend(selectedTargetId).then(setTrendData).catch(() => setTrendData([])); }, [selectedTargetId]); // Re-fetch target vehicles when viewAllDate changes useEffect(() => { if (viewAllTargetId === null) return; setViewAllLoading(true); fetchTargetVehicles(viewAllTargetId, viewAllDate).then(data => { setTargetVehiclesMap(prev => ({ ...prev, [viewAllTargetId]: data })); }).catch(() => {}).finally(() => setViewAllLoading(false)); }, [viewAllTargetId, viewAllDate]); return (
{/* Project Selector */}
{targets.map(target => ( ))}
{/* 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 => ( ))}
{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 */}

车型考核里程汇总

{targets.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(expandedTargetId === target.id ? null : target.id); if (!targetVehiclesMap[target.id]) { fetchTargetVehicles(target.id).then(data => { setTargetVehiclesMap(prev => ({ ...prev, [target.id]: data })); }).catch(() => {}); } }} >
{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

数据截至 {currentDateLabel}

{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)} | 所选年度完成率 {targets.length > 0 ? (targets.reduce((sum, t) => sum + (getTargetAssessment(t, assessmentYearMap[t.id])?.completionRate ?? t.avgCompletion), 0) / targets.length).toFixed(1) : '0.0'} %
{/* 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 && ( <> setViewAllTargetId(null)} className="fixed inset-0 z-[110] bg-slate-950/60 backdrop-blur-sm" />

{viewAllTargetName}

车辆实时明细清单

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 filtered = (viewAllTargetId !== null ? (targetVehiclesMap[viewAllTargetId] || []) : []).filter(tv => tv.plateNumber.toLowerCase().includes(viewAllSearch.toLowerCase())); const totalKm = filtered.reduce((sum, tv) => sum + (tv.todayMileage || 0), 0); return `${filtered.length} 辆 · 合计 ${fmtKm(totalKm)} km`; })()}
{(viewAllTargetId !== null ? (targetVehiclesMap[viewAllTargetId] || []) : []).filter(tv => tv.plateNumber.toLowerCase().includes(viewAllSearch.toLowerCase()) ).sort((a, b) => { const valA = a.todayMileage || 0; const valB = b.todayMileage || 0; return viewAllSort === 'desc' ? valB - valA : valA - valB; }).map(tv => (
{tv.plateNumber} {tv.isOnline ? '在线' : '离线'}
{tv.rentStatus || ''}{tv.department ? ` · ${tv.department.replace('业务', '')}` : ''} {tv.customer || '-'}
{tv.todayMileage.toLocaleString()} KM
累计: {fmtKm(tv.totalMileage || 0)} km
))}
)}
); }