diff --git a/src/modules/mileage/StatisticsView.tsx b/src/modules/mileage/StatisticsView.tsx
index 1a1c178..d494ea8 100644
--- a/src/modules/mileage/StatisticsView.tsx
+++ b/src/modules/mileage/StatisticsView.tsx
@@ -1,3 +1,554 @@
+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';
+
export default function StatisticsView() {
- return
StatisticsView placeholder
;
+ 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'}
+ %
+
+
+
+
+
+
+
+
+ {(['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}%
+
+
+ 达标:
+ {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()}
+
+
+
+
+
+
+ | 车型 |
+ 车辆数 |
+ 总考核里程 |
+ 已行驶总里程 |
+ 总完成率 |
+ 考核区间 |
+ 年考核任务/辆 |
+ 达标车辆数 |
+ 50%达标数 |
+ 今日总里程 |
+ 本年需完成 |
+ 已完成(截止3.31) |
+ 未完成总数 |
+ 剩余天数 |
+ 日均需完成 |
+
+
+
+ {targets.map((target, idx) => (
+
+ | {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}%
+
+ |
+ {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()}
+
+
+ ))}
+
+
+
+
+
+
+ >
+ )}
+
+
+ );
}