feat: improve asset inventory statistics and responsive UX (v1.1.16)
This commit is contained in:
@@ -1,105 +1,57 @@
|
||||
import { Activity, TriangleAlert, PlusCircle, Truck, Warehouse } from 'lucide-react';
|
||||
import { Activity, TriangleAlert, PlusCircle, Truck, Warehouse, ChevronRight } from 'lucide-react';
|
||||
import type { SummaryMetricsProps } from './types';
|
||||
|
||||
export function SummaryMetrics({
|
||||
summary: SUMMARY,
|
||||
operatingRate,
|
||||
inventoryRate,
|
||||
pendingRate,
|
||||
setVehicleSelection: setShowPlateNumbers,
|
||||
}: SummaryMetricsProps) {
|
||||
return (
|
||||
<>
|
||||
{/* Header Summary - Ultra Compact */}
|
||||
<div className="grid grid-cols-2 md:grid-cols-5 gap-2 mb-2">
|
||||
{/* Total Assets */}
|
||||
<div className="rounded-2xl border border-slate-100 bg-white p-3 shadow-sm flex items-center gap-2 cursor-pointer transition-all hover:-translate-y-0.5 hover:shadow-md"
|
||||
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', source: 'asset', title: '资产概览' })}>
|
||||
<div className="w-8 h-8 rounded-xl bg-slate-50 flex items-center justify-center text-slate-500">
|
||||
<Truck size={14} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-[9px] text-gray-400 font-medium uppercase leading-none mb-0.5">资产总数</div>
|
||||
<div className="text-base font-bold text-gray-800 leading-none">{SUMMARY.totalAssets.toLocaleString()}</div>
|
||||
</div>
|
||||
</div>
|
||||
export function SummaryMetrics({ summary, operatingRate, inventoryRate, setVehicleSelection }: SummaryMetricsProps) {
|
||||
const open = (category: 'Operating' | 'Inventory' | 'Abnormal' | 'Pending' | undefined, title: string) =>
|
||||
setVehicleSelection({ batch: 'All', model: 'All', location: 'All', category, source: 'asset', title });
|
||||
|
||||
{/* Operating */}
|
||||
<div className="rounded-2xl border border-slate-100 bg-white p-3 shadow-sm flex items-center gap-2 cursor-pointer transition-all hover:-translate-y-0.5 hover:shadow-md"
|
||||
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', category: 'Operating', source: 'asset', title: '正在运营' })}>
|
||||
<div className="w-8 h-8 rounded-xl bg-blue-50 flex items-center justify-center text-blue-500">
|
||||
<Activity size={14} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-[9px] text-gray-400 font-medium uppercase leading-none mb-0.5">总运营</div>
|
||||
<div className="flex items-baseline gap-1">
|
||||
<span className="text-base font-bold text-blue-600 leading-none">{SUMMARY.operating.total}</span>
|
||||
<span className="text-[8px] text-gray-400 leading-none">自{SUMMARY.operating.self} 租{SUMMARY.operating.leased}{SUMMARY.operating.hanging > 0 && ` 挂${SUMMARY.operating.hanging}`}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Inventory */}
|
||||
<div className="rounded-2xl border border-slate-100 bg-white p-3 shadow-sm flex items-center gap-2 cursor-pointer transition-all hover:-translate-y-0.5 hover:shadow-md"
|
||||
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', category: 'Inventory', source: 'asset', title: '库存总数' })}>
|
||||
<div className="w-8 h-8 rounded-xl bg-slate-50 flex items-center justify-center text-slate-500">
|
||||
<Warehouse size={14} />
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-[9px] text-gray-400 font-medium uppercase leading-none mb-0.5">总库存</div>
|
||||
<div className="flex items-baseline gap-1">
|
||||
<span className="text-base font-bold text-gray-800 leading-none">{SUMMARY.inventory.total}</span>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button type="button" data-testid="asset-abnormal-card"
|
||||
className="rounded-2xl border border-amber-100 bg-white p-3 shadow-sm flex items-center gap-2 text-left transition-all hover:-translate-y-0.5 hover:shadow-md"
|
||||
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', category: 'Abnormal', source: 'asset', title: '异动车辆' })}>
|
||||
<div className="w-8 h-8 rounded-xl bg-amber-50 flex items-center justify-center text-amber-600"><TriangleAlert size={14} /></div>
|
||||
<div>
|
||||
<div className="text-[9px] text-amber-600 font-bold leading-none mb-0.5">异动</div>
|
||||
<div className="text-base font-bold text-amber-600 leading-none">{SUMMARY.inventory.abnormal}</div>
|
||||
return <div className="mb-3 space-y-2">
|
||||
<button type="button" onClick={() => open(undefined, '资产概览')}
|
||||
className="flex items-center gap-2 px-1 py-2 text-xs text-slate-500 hover:text-blue-600">
|
||||
<Truck size={15} /><span>资产总数</span><strong className="text-base text-slate-800">{summary.totalAssets.toLocaleString()}</strong><span>台</span><ChevronRight size={14} />
|
||||
</button>
|
||||
<div className="grid grid-cols-1 gap-3 md:grid-cols-2">
|
||||
<section data-testid="asset-operating-group" className="overflow-hidden rounded-2xl border border-blue-100 bg-white shadow-sm">
|
||||
<button type="button" onClick={() => open('Operating', '总运营车辆')}
|
||||
className="flex w-full items-center justify-between gap-3 p-4 text-left hover:bg-blue-50/50">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="rounded-xl bg-blue-50 p-2.5 text-blue-600"><Activity size={20} /></span>
|
||||
<div><h3 className="text-sm font-bold text-slate-800">总运营</h3><p className="mt-1 text-xs text-slate-500">租赁 + 自营</p></div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2"><strong className="text-2xl font-bold text-blue-600">{summary.operating.total}</strong><span className="text-xs text-slate-400">台</span><ChevronRight size={16} className="text-slate-400" /></div>
|
||||
</button>
|
||||
|
||||
{/* Pending */}
|
||||
<div className="rounded-2xl border border-slate-100 bg-white p-3 shadow-sm flex items-center gap-2 cursor-pointer transition-all hover:-translate-y-0.5 hover:shadow-md"
|
||||
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', category: 'Pending', source: 'asset', title: '待交车' })}>
|
||||
<div className="w-8 h-8 rounded-xl bg-blue-50 flex items-center justify-center text-blue-500">
|
||||
<PlusCircle size={14} />
|
||||
<div className="grid grid-cols-2 divide-x divide-blue-100 border-t border-blue-50 bg-blue-50/30 px-4 py-3 text-sm">
|
||||
<div className="flex items-center justify-between gap-2 pr-4"><span className="text-slate-500">租赁</span><strong className="text-slate-800">{summary.operating.leased}</strong></div>
|
||||
<div className="flex items-center justify-between gap-2 pl-4"><span className="text-slate-500">自营</span><strong className="text-slate-800">{summary.operating.self}</strong></div>
|
||||
</div>
|
||||
</section>
|
||||
<section data-testid="asset-inventory-group" className="overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm">
|
||||
<button type="button" onClick={() => open('Inventory', '总库存车辆')}
|
||||
className="flex w-full items-center justify-between gap-3 p-4 text-left hover:bg-slate-50">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="rounded-xl bg-slate-100 p-2.5 text-slate-600"><Warehouse size={20} /></span>
|
||||
<div><h3 className="text-sm font-bold text-slate-800">总库存</h3><p className="mt-1 text-xs text-slate-500">待运营 + 可运营</p></div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-[9px] text-blue-500 font-bold uppercase leading-none mb-0.5">待交车</div>
|
||||
<div className="text-base font-bold text-blue-600 leading-none">{SUMMARY.pendingDelivery}</div>
|
||||
<div className="flex items-center gap-2"><strong className="text-2xl font-bold text-slate-800">{summary.inventory.total}</strong><span className="text-xs text-slate-400">台</span><ChevronRight size={16} className="text-slate-400" /></div>
|
||||
</button>
|
||||
<div className="border-t border-slate-100 bg-slate-50/60 px-4 py-3">
|
||||
<p className="mb-2 text-[11px] text-slate-500">以下车辆已包含在总库存中</p>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<button type="button" data-testid="asset-abnormal-card" onClick={() => open('Abnormal', '库存异动车辆')}
|
||||
className="flex items-center justify-between gap-2 rounded-xl border border-amber-100 bg-amber-50 px-3 py-2 text-sm text-amber-700 hover:bg-amber-100">
|
||||
<span className="flex items-center gap-1.5"><TriangleAlert size={14} />异动</span><span className="flex items-center gap-1 font-bold">{summary.inventory.abnormal}<ChevronRight size={14} /></span>
|
||||
</button>
|
||||
<button type="button" data-testid="asset-pending-card" onClick={() => open('Pending', '库存待交车')}
|
||||
className="flex items-center justify-between gap-2 rounded-xl border border-blue-100 bg-blue-50 px-3 py-2 text-sm text-blue-700 hover:bg-blue-100">
|
||||
<span className="flex items-center gap-1.5"><PlusCircle size={14} />待交车</span><span className="flex items-center gap-1 font-bold">{summary.pendingDelivery}<ChevronRight size={14} /></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div data-testid="asset-operation-ratio-strip" className="mb-3 rounded-2xl border border-slate-100 bg-white/85 px-4 py-3 shadow-sm">
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div className="shrink-0 text-[11px] font-black text-slate-400">运营概览</div>
|
||||
<div className="grid min-w-0 flex-1 grid-cols-4 divide-x divide-slate-100 text-center">
|
||||
<div className="px-2">
|
||||
<div className="text-[10px] font-black text-slate-400">运营率</div>
|
||||
<div className="mt-0.5 text-base font-black text-blue-600">{operatingRate.toFixed(1)}%</div>
|
||||
</div>
|
||||
<div className="px-2">
|
||||
<div className="text-[10px] font-black text-slate-400">库存率</div>
|
||||
<div className="mt-0.5 text-base font-black text-slate-700">{inventoryRate.toFixed(1)}%</div>
|
||||
</div>
|
||||
<div className="px-2">
|
||||
<div className="text-[10px] font-black text-slate-400">异动率</div>
|
||||
<div className="mt-0.5 text-base font-black text-amber-600">{(SUMMARY.totalAssets > 0 ? SUMMARY.inventory.abnormal / SUMMARY.totalAssets * 100 : 0).toFixed(1)}%</div>
|
||||
</div>
|
||||
<div className="px-2">
|
||||
<div className="text-[10px] font-black text-slate-400">待交率</div>
|
||||
<div className="mt-0.5 text-base font-black text-amber-600">{pendingRate.toFixed(1)}%</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
</section>
|
||||
</div>
|
||||
<div data-testid="asset-operation-ratio-strip" className="flex flex-wrap items-center gap-x-5 gap-y-1 px-1 py-1 text-xs text-slate-500">
|
||||
<span>运营率 <strong className="text-blue-600">{operatingRate.toFixed(1)}%</strong></span>
|
||||
<span>库存率 <strong className="text-slate-700">{inventoryRate.toFixed(1)}%</strong></span>
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user