import { AlertTriangle, Building2, ChevronRight, Users } from 'lucide-react'; import type { TargetVehicle } from './types'; import { buildMileageAttribution, reconcileMileageAttribution, type AttributionDimension, } from './attribution'; interface Props { vehicles: TargetVehicle[]; dimension: AttributionDimension; selectedValue?: string; loading?: boolean; targetMileagePerVehicle: number; expectedShortfallMileage?: number; assessmentLabel?: string; onDimensionChange: (dimension: AttributionDimension) => void; onSelect: (value: string) => void; } function fmtKm(value: number): string { return value >= 10000 ? `${(value / 10000).toFixed(2)}万` : value.toLocaleString(undefined, { maximumFractionDigits: 1 }); } export default function MileageAttributionView({ vehicles, dimension, selectedValue, loading = false, targetMileagePerVehicle, expectedShortfallMileage, assessmentLabel, onDimensionChange, onSelect, }: Props) { const groups = buildMileageAttribution(vehicles, dimension, targetMileagePerVehicle); const totalShortfall = groups.reduce((sum, group) => sum + group.shortfallMileage, 0); const totalBehind = groups.reduce((sum, group) => sum + group.behindCount, 0); const reconciliation = expectedShortfallMileage == null ? null : reconcileMileageAttribution(groups, expectedShortfallMileage); return (

考核缺口归因

{assessmentLabel ? `${assessmentLabel} · ` : ''}{totalBehind} 台未达标,累计缺口 {fmtKm(totalShortfall)} km

{!loading && reconciliation && (

{reconciliation.matches ? '与考核目标汇总一致' : `与考核目标汇总相差 ${reconciliation.difference > 0 ? '+' : ''}${fmtKm(reconciliation.difference)} km`}

)}
{([ ['department', '部门', Building2], ['customer', '客户', Users], ] as const).map(([value, label, Icon]) => ( ))}
{loading ? (
正在计算归因...
) : groups.length === 0 ? (
暂无可归因车辆
) : (
{groups.slice(0, 8).map(group => ( ))}
)}
); }