142 lines
5.7 KiB
TypeScript
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>
|
|
</>
|
|
);
|
|
}
|