import type { TargetSummary, TargetVehicle, TargetYearlyAssessment, TrendPoint } from '../types'; export type ChartType = 'bar' | 'line' | 'area'; export type VehicleSort = 'asc' | 'desc'; export type PressureLevel = '健康' | '关注' | '高压'; export interface SelectedTargetMetrics { assessment: TargetYearlyAssessment | null; completion: number; remaining: number; daysLeft: number; dailyTarget: number; qualifiedRate: number; trendDelta: number; pressureLevel: PressureLevel; } export interface TargetTableRow { assessment: TargetYearlyAssessment | null; completion: number; qualified: number; halfQualified: number; goal: number; completed: number; remainingMileage: number; days: number; daily: number; taskPerVehicle: number; } export interface TargetTotals { today: number; cumulative: number; vehicleCount: number; averageCompletion: number; } export function getDefaultDate(now = new Date()): string { const date = new Date(now); if (date.getHours() < 5) date.setDate(date.getDate() - 1); return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`; } export function getCurrentDateLabel(now = new Date()): string { return `${now.getFullYear()}.${now.getMonth() + 1}.${now.getDate()}`; } export function fmtKm(value: number): string { if (value >= 10000) return (value / 10000).toFixed(2) + '万'; return value.toLocaleString(); } export function fmtPercent(value: number): string { return `${value.toFixed(1)}%`; } export 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]; } export function fmtDateLabel(date: string | null): string { if (!date) return ''; const [year, month, day] = date.split('-'); return `${year}.${Number(month)}.${Number(day)}`; } export function shortTargetName(name: string): string { const match = name.match(/(\d+)[辆台](.+)/); if (!match) return name; const count = match[1]; let desc = match[2]; desc = desc.replace('4.5T普货', '普货'); desc = desc.replace('4.5T冷链车', '冷藏车'); desc = desc.replace('4.5T冷链', '冷藏车'); desc = desc.replace('18T', '18T'); return `${count}台${desc}`; } export function trendDateLabel(value: string): string { const [month, day] = value.split('-'); return `${parseInt(month)}.${parseInt(day)}`; } export function orderTargets(targets: TargetSummary[]): TargetSummary[] { const focused = targets.find(item => item.targetName.includes('羚牛136')) || targets[0]; return focused ? [focused, ...targets.filter(item => item.id !== focused.id)] : targets; } export function initialAssessmentYears(targets: TargetSummary[]): Record { return Object.fromEntries( targets.map(item => [item.id, item.yearlyAssessments[0]?.yearNumber || 1]), ); } export function deriveSelectedTargetMetrics( target: TargetSummary | undefined, selectedYear: number | undefined, trendData: TrendPoint[], ): SelectedTargetMetrics { const assessment = target ? getTargetAssessment(target, selectedYear) : null; const completion = assessment?.completionRate ?? target?.avgCompletion ?? 0; const remaining = assessment?.remaining ?? 0; const daysLeft = assessment?.daysLeft ?? target?.daysLeft ?? 0; const dailyTarget = assessment?.dailyTarget ?? (daysLeft > 0 ? remaining / daysLeft : 0); const qualifiedRate = assessment?.qualifiedRate ?? (target?.vehicleCount ? target.yearQualifiedCount / target.vehicleCount * 100 : 0); const latestTrend = trendData[trendData.length - 1]; const previousTrend = trendData[trendData.length - 2]; const trendDelta = latestTrend && previousTrend ? latestTrend.mileage - previousTrend.mileage : 0; const pressureLevel = completion >= 90 ? '健康' : completion >= 70 ? '关注' : '高压'; return { assessment, completion, remaining, daysLeft, dailyTarget, qualifiedRate, trendDelta, pressureLevel, }; } export function deriveTargetTableRow( target: TargetSummary, selectedYear?: number, ): TargetTableRow { const assessment = getTargetAssessment(target, selectedYear); return { assessment, completion: assessment?.completionRate ?? target.avgCompletion, qualified: assessment?.qualifiedCount ?? target.yearQualifiedCount, halfQualified: assessment?.halfQualifiedCount ?? target.halfQualifiedCount, goal: assessment?.target ?? target.currentYearTarget, completed: assessment?.completed ?? target.currentYearCompleted, remainingMileage: assessment?.remaining ?? target.remaining, days: assessment?.daysLeft ?? target.daysLeft, daily: assessment?.dailyTarget ?? target.dailyTarget, taskPerVehicle: target.annualMileagePerVehicle * (assessment?.yearNumber || 1), }; } export function summarizeTargets( targets: TargetSummary[], assessmentYearMap: Record, ): TargetTotals { const totals = targets.reduce((result, target) => ({ today: result.today + target.todayTotal, cumulative: result.cumulative + target.cumulativeTotal, vehicleCount: result.vehicleCount + target.vehicleCount, completion: result.completion + (getTargetAssessment(target, assessmentYearMap[target.id])?.completionRate ?? target.avgCompletion), }), { today: 0, cumulative: 0, vehicleCount: 0, completion: 0 }); return { today: totals.today, cumulative: totals.cumulative, vehicleCount: totals.vehicleCount, averageCompletion: targets.length > 0 ? totals.completion / targets.length : 0, }; } export function filterAndSortTargetVehicles( vehicles: TargetVehicle[], search: string, sort: VehicleSort, ): TargetVehicle[] { return vehicles .filter(vehicle => vehicle.plateNumber.toLowerCase().includes(search.toLowerCase())) .sort((a, b) => { const valueA = a.todayMileage || 0; const valueB = b.todayMileage || 0; return sort === 'desc' ? valueB - valueA : valueA - valueB; }); } export function totalTodayMileage(vehicles: TargetVehicle[]): number { return vehicles.reduce((sum, vehicle) => sum + (vehicle.todayMileage || 0), 0); }