import { useCallback, useEffect, useState } from 'react'; import { motion } from 'motion/react'; import { Activity, AlertTriangle, CalendarDays, ChevronRight, CircleGauge, Database, RefreshCw, Route, Truck, } from 'lucide-react'; import { Bar, BarChart, CartesianGrid, ResponsiveContainer, Tooltip, XAxis, YAxis, } from 'recharts'; import Blur from '../../components/Blur'; import { MetricTile, SurfaceCard } from '../../components/ui/surface'; import { fetchDailyReport, fetchDailyReportTrend, type DailyReportData, type DailyReportVehicle, } from './api'; import { buildMileageDrillUrl } from './drill-context'; function fmtKm(value: number): string { if (value >= 10000) return `${(value / 10000).toFixed(2)}万`; return value.toLocaleString(undefined, { maximumFractionDigits: 1 }); } function fmtTime(value: string): string { const date = new Date(value); if (Number.isNaN(date.getTime())) return value; return date.toLocaleString('zh-CN', { month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', hour12: false, }); } function VehicleRow({ vehicle, mode, onClick, }: { vehicle: DailyReportVehicle; mode: 'top' | 'risk'; onClick: () => void; }) { return ( ); } export default function DailyReportView() { const [report, setReport] = useState(null); const [loading, setLoading] = useState(true); const [trendLoading, setTrendLoading] = useState(true); const [error, setError] = useState(''); const load = useCallback(async (force = false) => { setLoading(true); setTrendLoading(true); setError(''); const trendRequest = fetchDailyReportTrend(force); try { const nextReport = await fetchDailyReport(force); setReport(nextReport); setLoading(false); try { const trend = await trendRequest; setReport(current => current ? { ...current, trend: trend.map(item => ({ date: item.date, value: item.mileage })), } : current); } catch { setError('7 日趋势加载失败'); } finally { setTrendLoading(false); } } catch (cause) { void trendRequest.catch(() => {}); setError(cause instanceof Error ? cause.message : '日报加载失败'); setLoading(false); setTrendLoading(false); } }, []); useEffect(() => { void load(); }, [load]); const openTarget = useCallback((targetId: number) => { const url = buildMileageDrillUrl( { pathname: window.location.pathname, search: window.location.search, hash: '#mileage/statistics' }, { level: 'overview', targetId, sourcePriority: ['instrument'] }, ); window.history.pushState(null, '', url); window.dispatchEvent(new HashChangeEvent('hashchange')); }, []); const openVehicle = useCallback((vehicle: DailyReportVehicle) => { if (!report) return; const url = buildMileageDrillUrl( { pathname: window.location.pathname, search: window.location.search, hash: '#mileage' }, { level: 'vehicle', targetId: vehicle.targetId, plate: vehicle.plate, startDate: report.reportDate, endDate: report.reportDate, sourcePriority: ['instrument'], }, ); window.history.pushState(null, '', url); window.dispatchEvent(new HashChangeEvent('hashchange')); }, [report]); if (loading && !report) { return (
正在生成里程日报
); } if (!report) { return (
日报暂不可用
{error || '数据接口未返回有效结果'}
); } return (

{report.reportDate} 里程日报

统计时间 {fmtTime(report.updatedAt)} · 仪表数据

0 ? 'rose' : 'emerald'} /> 0 ? 'amber' : 'emerald'} />
{report.trend.length === 0 && trendLoading ? (
正在加载趋势
) : report.trend.length === 0 ? (
暂无趋势数据
) : ( [`${fmtKm(Number(value))} km`, '总里程']} contentStyle={{ borderRadius: 8, border: '1px solid #e2e8f0', fontSize: 11 }} /> )}
{report.models.map((model, index) => ( openTarget(model.id)} className="grid w-full grid-cols-[minmax(0,1fr)_72px_78px_20px] items-center gap-2 px-4 py-3 text-left transition-colors hover:bg-slate-50" > {model.name} {model.active}/{model.count} 台有里程 · {model.zero} 台零里程 {model.missingDailyTaskCount > 0 ? ` · ${model.missingDailyTaskCount} 台缺任务值` : ''} {fmtKm(model.today)} 当日 km 0 ? 'text-rose-600' : 'text-emerald-600'}`}> {fmtKm(model.shortfall)} 缺口 km ))}
{report.topVehicles.map(vehicle => ( openVehicle(vehicle)} /> ))}
{report.riskVehicles.map(vehicle => ( openVehicle(vehicle)} /> ))}
考核车辆 {report.assessmentVehicleCount} 台 · 监控车辆 {report.monitoringVehicleCount} 台
重复考核归属 {report.duplicateAssignmentCount} 条
0 ? 'mt-1 text-amber-600' : 'mt-1'}> 缺少车辆级任务值 {report.missingDailyTaskCount} 台
{error &&
最近刷新失败:{error}
}
); }