Files
ln-bi/src/modules/mileage/daily-report/ReportOverview.tsx
T

142 lines
5.7 KiB
TypeScript

import { CircleGauge, Route, TrendingUp, Truck, Warehouse } from 'lucide-react';
import { MetricTile } from '../../../components/ui/surface';
import type { DailyMileageReport } from '../api';
import { deltaLabel, fmtKm } from './utils';
interface ReportOverviewProps {
report: DailyMileageReport;
highMileageRate: number;
}
function MobileDailyOverview({ report, highMileageRate }: ReportOverviewProps) {
const operatingRate = (report.totals.operatingCount / Math.max(1, report.totals.vehicleCount)) * 100;
const stats = [
{
label: '运营车辆',
value: report.totals.operatingCount,
unit: '台',
detail: `${report.totals.vehicleCount} 台考核车辆 · ${operatingRate.toFixed(1)}%`,
icon: Truck,
tone: 'text-emerald-600 bg-emerald-50',
},
{
label: '库存车辆',
value: report.totals.inventoryCount,
unit: '台',
detail: `产生 ${fmtKm(report.totals.inventoryMileage)} km`,
icon: Warehouse,
tone: 'text-rose-600 bg-rose-50',
},
{
label: '运营单车均值',
value: fmtKm(report.totals.averageOperatingMileage),
unit: 'km/台',
detail: `运营里程 ${fmtKm(report.totals.operatingMileage)} km`,
icon: CircleGauge,
tone: 'text-slate-600 bg-slate-100',
},
{
label: '高里程车辆',
value: report.totals.highMileageCount,
unit: '台',
detail: `${highMileageRate.toFixed(1)}% · 低里程 ${report.totals.lowMileageCount} 台`,
icon: TrendingUp,
tone: 'text-blue-600 bg-blue-50',
},
];
return (
<section className="overflow-hidden rounded-xl border border-slate-200 bg-white shadow-sm md:hidden">
<div className="flex items-start justify-between gap-3 px-4 py-3.5">
<div>
<div className="text-[11px] font-black text-slate-400">当日总里程</div>
<div className="mt-1 flex items-end gap-1">
<span className="whitespace-nowrap text-[clamp(2rem,10vw,2.45rem)] font-black leading-none tabular-nums text-slate-950">{fmtKm(report.totals.dailyMileage)}</span>
<span className="mb-0.5 text-xs font-black text-slate-400">km</span>
</div>
</div>
<div className={`mt-1 rounded-lg px-2.5 py-1.5 text-right ${report.totals.dayOverDayDelta >= 0 ? 'bg-emerald-50' : 'bg-rose-50'}`}>
<div className="text-[9px] font-black text-slate-400">较前一日</div>
<div className={`mt-0.5 text-[11px] font-black tabular-nums ${report.totals.dayOverDayDelta >= 0 ? 'text-emerald-600' : 'text-rose-600'}`}>
{deltaLabel(report.totals.dayOverDayDelta, report.totals.dayOverDayRate)}
</div>
</div>
</div>
<div className="grid grid-cols-2 border-t border-slate-100">
{stats.map((stat, index) => {
const Icon = stat.icon;
return (
<div key={stat.label} className={`min-w-0 px-3 py-2.5 ${index < 2 ? 'border-b border-slate-100' : ''} ${index % 2 === 0 ? 'border-r border-slate-100' : ''}`}>
<div className="flex items-center justify-between gap-2">
<span className="truncate text-[10px] font-black text-slate-400">{stat.label}</span>
<span className={`flex h-6 w-6 shrink-0 items-center justify-center rounded-md ${stat.tone}`}><Icon size={13} /></span>
</div>
<div className="mt-1.5 flex items-end gap-1">
<span className="min-w-0 truncate text-xl font-black leading-none tabular-nums text-slate-900">{stat.value}</span>
<span className="mb-px shrink-0 text-[10px] font-black text-slate-400">{stat.unit}</span>
</div>
<div className="mt-1 truncate text-[9px] font-bold text-slate-500">{stat.detail}</div>
</div>
);
})}
</div>
</section>
);
}
export default function ReportOverview({ report, highMileageRate }: ReportOverviewProps) {
return (
<>
<MobileDailyOverview report={report} highMileageRate={highMileageRate} />
<div className="hidden grid-cols-2 gap-2 md:grid xl:grid-cols-5">
<MetricTile
label="当日总里程"
value={fmtKm(report.totals.dailyMileage)}
unit="km"
helper={deltaLabel(report.totals.dayOverDayDelta, report.totals.dayOverDayRate)}
icon={Route}
tone="blue"
className="col-span-2 xl:col-span-1"
/>
<MetricTile
label="运营车辆"
value={report.totals.operatingCount}
unit="台"
helper={`${report.totals.vehicleCount} 台考核车辆 · 运营率 ${((report.totals.operatingCount / Math.max(1, report.totals.vehicleCount)) * 100).toFixed(1)}%`}
icon={Truck}
tone="emerald"
compact
/>
<MetricTile
label="库存车辆"
value={report.totals.inventoryCount}
unit="台"
helper={`库存产生 ${fmtKm(report.totals.inventoryMileage)} km`}
icon={Warehouse}
tone={report.totals.inventoryMileage > 0 ? 'rose' : 'amber'}
compact
/>
<MetricTile
label="运营单车均值"
value={fmtKm(report.totals.averageOperatingMileage)}
unit="km/台"
helper={`运营里程 ${fmtKm(report.totals.operatingMileage)} km`}
icon={CircleGauge}
tone="slate"
compact
/>
<MetricTile
label="高里程车辆"
value={report.totals.highMileageCount}
unit="台"
helper={`占运营车辆 ${highMileageRate.toFixed(1)}% · ≤100km ${report.totals.lowMileageCount} 台`}
icon={TrendingUp}
tone="blue"
compact
/>
</div>
</>
);
}