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, } from 'lucide-react'; import type { TargetSummary, TargetVehicle, TrendPoint } from './types'; import { fetchTargets, fetchTargetVehicles, fetchTrend } from './api'; 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 [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 [expandedModel, setExpandedModel] = useState(null); const [viewAllTargetId, setViewAllTargetId] = useState(null); const [viewAllTargetName, setViewAllTargetName] = useState(''); const [viewAllSearch, setViewAllSearch] = useState(''); const [viewAllSort, setViewAllSort] = useState<'asc' | 'desc'>('desc'); // Load targets on mount useEffect(() => { fetchTargets().then(data => { setTargets(data); if (data.length > 0 && !selectedTargetId) { setSelectedTargetId(data[0].id); } }).catch(() => {}); }, []); // Load trend when selectedTargetId changes useEffect(() => { if (selectedTargetId === null) return; fetchTrend(selectedTargetId).then(setTrendData).catch(() => setTrendData([])); }, [selectedTargetId]); return (
{/* Project Selector - Full width even in landscape */}
{targets.map(target => ( ))}
{/* Left Side: Trend Chart / Dashboard Sidebar */}
{/* KPI Cards in Landscape */}
今日总里程
{targets.reduce((sum, t) => sum + t.todayTotal, 0).toLocaleString()} KM
累计总里程
{targets.reduce((sum, t) => sum + t.cumulativeTotal, 0).toLocaleString()} KM
总考核车辆
{targets.reduce((sum, t) => sum + t.vehicleCount, 0)}
平均完成率
{targets.length > 0 ? (targets.reduce((sum, t) => sum + t.avgCompletion, 0) / targets.length).toFixed(1) : '0.0'} %

7天里程趋势

{(['bar', 'line', 'area'] as const).map(type => ( ))}
{chartType === 'bar' ? ( val.split('-')[1]} /> {trendData.map((_entry: TrendPoint, index: number) => ( ))} ) : chartType === 'line' ? ( val.split('-')[1]} /> ) : ( val.split('-')[1]} /> )}
{/* Right Side: Summary Section */}

车型考核里程汇总

{targets.map((target, idx) => (
{ const name = target.targetName; setExpandedModel(expandedModel === name ? null : name); if (!targetVehiclesMap[target.id]) { fetchTargetVehicles(target.id).then(data => { setTargetVehiclesMap(prev => ({ ...prev, [target.id]: data })); }).catch(() => {}); } }} >
{target.targetName} {target.vehicleCount}台
完成率: = 90 ? 'text-emerald-500' : 'text-blue-500'}`}>{target.avgCompletion.toFixed(1)}%
达标: {target.yearQualifiedCount}台
{target.todayTotal.toLocaleString()} KM
累计: {target.cumulativeTotal.toLocaleString()} KM
{expandedModel === target.targetName && (

考核区间

{target.period}

总考核里程

{(target.totalMileagePerVehicle * target.vehicleCount).toLocaleString()} KM

年考核任务/辆

{target.annualMileagePerVehicle.toLocaleString()} KM

50%达标数

{target.halfQualifiedCount} 台

本年需完成

{target.currentYearTarget.toLocaleString()} KM

已完成(截止3.31)

{target.currentYearCompleted.toLocaleString()} KM

未完成总数

{target.remaining.toLocaleString()} KM

日均需完成

{target.dailyTarget} KM

剩余考核天数 {target.daysLeft} 天
{/* Vehicle List Detail */}
车辆明细 (前5台)
{(targetVehiclesMap[target.id] || []).slice(0, 5).map(tv => (
{tv.plateNumber} 在线
{tv.todayMileage} KM
))}
)}
))}
{/* Fullscreen Table Overlay */} {isTableFullscreen && ( {/* Sidebar with KPI Cards */}

车型考核汇总

今日总里程
{targets.reduce((sum, t) => sum + t.todayTotal, 0).toLocaleString()} KM
累计总里程
{targets.reduce((sum, t) => sum + t.cumulativeTotal, 0).toLocaleString()} KM
总考核车辆
{targets.reduce((sum, t) => sum + t.vehicleCount, 0)}
平均完成率
{targets.length > 0 ? (targets.reduce((sum, t) => sum + t.avgCompletion, 0) / targets.length).toFixed(1) : '0.0'} %
车型考核明细数据 最后更新: {new Date().toLocaleTimeString()}
{targets.map((target, idx) => ( ))}
车型 车辆数 总考核里程 已行驶总里程 总完成率 考核区间 年考核任务/辆 达标车辆数 50%达标数 今日总里程 本年需完成 已完成(截止3.31) 未完成总数 剩余天数 日均需完成
{target.targetName} {target.vehicleCount} {(target.totalMileagePerVehicle * target.vehicleCount).toLocaleString()} {target.cumulativeTotal.toLocaleString()}
= 90 ? 'bg-emerald-500' : target.avgCompletion >= 80 ? 'bg-blue-500' : 'bg-amber-500'}`} style={{ width: `${target.avgCompletion}%` }} />
{target.avgCompletion.toFixed(1)}%
{target.period} {target.annualMileagePerVehicle} {target.yearQualifiedCount} {target.halfQualifiedCount} {target.todayTotal.toLocaleString()} {target.currentYearTarget.toLocaleString()} {target.currentYearCompleted.toLocaleString()} {target.remaining.toLocaleString()} {target.daysLeft} {target.dailyTarget}
)}
{/* 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-4 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" />
排序方式: 今日里程
{(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.todayMileage} KM
累计: {tv.totalMileage?.toLocaleString()}
))}
)}
); }