Files
ln-bi/src/modules/energy/hydrogen-daily/components/DailyKpiGrid.tsx
T
kkfluous 62efef0ab9
ci/woodpecker/push/woodpecker Pipeline was successful
feat(energy): rebuild hydrogen BI board and drill-through
2026-08-20 13:59:03 +08:00

124 lines
3.6 KiB
TypeScript

import { Fuel, TrendingUp, Truck, Zap } from 'lucide-react';
import type { HydrogenDailyVehicleScope } from '../model';
interface DailyKpiGridProps {
rangeLabel: string;
rangeText: string;
totalKg: number;
activeDays: number;
dayCount: number;
averageKg: number;
stationCount: number;
vehicleScope: HydrogenDailyVehicleScope;
lingniuKg: number;
externalKg: number;
selectedStationName?: string;
}
const VEHICLE_LABEL: Record<HydrogenDailyVehicleScope, string> = {
all: '全部车辆',
lingniu: '羚牛车辆',
external: '外部车辆',
};
export function DailyKpiGrid({
rangeLabel,
rangeText,
totalKg,
activeDays,
dayCount,
averageKg,
stationCount,
vehicleScope,
lingniuKg,
externalKg,
selectedStationName,
}: DailyKpiGridProps) {
return (
<section className="grid grid-cols-2 gap-2.5 md:grid-cols-4 md:gap-3">
<KpiCard
icon={Fuel}
title={`${rangeLabel}加氢量`}
value={totalKg.toLocaleString('zh-CN', { maximumFractionDigits: 1 })}
unit="Kg"
helper={rangeText}
tone="blue"
/>
<KpiCard
icon={Truck}
title="车辆结构"
value={VEHICLE_LABEL[vehicleScope]}
helper={vehicleScope === 'all'
? `羚牛 ${formatKg(lingniuKg)} · 外部 ${formatKg(externalKg)}`
: `仅统计${VEHICLE_LABEL[vehicleScope]}`}
tone="green"
smallValue
/>
<KpiCard
icon={TrendingUp}
title="有效天数"
value={activeDays}
unit="天"
helper={`区间 ${dayCount} 天 · 日均 ${averageKg.toLocaleString('zh-CN', { maximumFractionDigits: 1 })} Kg`}
tone="amber"
/>
<KpiCard
icon={Zap}
title={selectedStationName ? '当前加氢站' : '涉及加氢站'}
value={selectedStationName ?? stationCount}
unit={selectedStationName ? undefined : '站'}
helper={selectedStationName ?? '按区间明细站点去重'}
tone="purple"
smallValue={Boolean(selectedStationName)}
/>
</section>
);
}
function formatKg(value: number): string {
return `${value.toLocaleString('zh-CN', { maximumFractionDigits: 1 })} Kg`;
}
function KpiCard({
icon: Icon,
title,
value,
unit,
helper,
tone,
smallValue = false,
}: {
icon: typeof Fuel;
title: string;
value: string | number;
unit?: string;
helper: string;
tone: 'blue' | 'green' | 'amber' | 'purple';
smallValue?: boolean;
}) {
const toneClass = {
blue: 'bg-blue-50 text-blue-600',
green: 'bg-emerald-50 text-emerald-600',
amber: 'bg-amber-50 text-amber-600',
purple: 'bg-violet-50 text-violet-600',
}[tone];
return (
<article className="min-w-0 rounded-[10px] border border-slate-200/70 bg-white px-3 py-3 shadow-sm md:px-4">
<div className="flex items-center justify-between gap-2">
<span className="truncate text-[12px] font-medium text-slate-500">{title}</span>
<span className={`inline-flex h-7 w-7 shrink-0 items-center justify-center rounded-lg ${toneClass}`}>
<Icon size={14} />
</span>
</div>
<div className="mt-1.5 flex min-w-0 items-baseline gap-1 font-mono tabular-nums text-slate-900">
<strong className={`${smallValue ? 'truncate text-[17px] md:text-[19px]' : 'text-[23px] md:text-[25px]'} leading-tight font-extrabold`}>
{value}
</strong>
{unit ? <span className="shrink-0 text-[11px] font-semibold text-slate-400">{unit}</span> : null}
</div>
<p className="mt-1 truncate font-mono text-[10px] font-medium text-slate-400" title={helper}>{helper}</p>
</article>
);
}