fix: replace 3 operations sections with exact prototype code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
kkfluous
2026-03-28 15:09:19 +08:00
parent df1ab64c97
commit 752208da02

View File

@@ -33,6 +33,8 @@ export default function App() {
customer?: string;
isColdChain?: boolean;
isTrailer?: boolean;
type?: string;
source?: string;
} | null>(null);
// Data state
@@ -128,8 +130,30 @@ export default function App() {
if (cat === 'Operating') params.category = 'Operating';
if (showPlateNumbers.manager) params.manager = showPlateNumbers.manager;
if (showPlateNumbers.customer) params.customer = showPlateNumbers.customer;
if (showPlateNumbers.isColdChain !== undefined) params.isColdChain = String(showPlateNumbers.isColdChain);
if (showPlateNumbers.isTrailer !== undefined) params.isTrailer = String(showPlateNumbers.isTrailer);
if (!showPlateNumbers.type) {
if (showPlateNumbers.isColdChain !== undefined) params.isColdChain = String(showPlateNumbers.isColdChain);
if (showPlateNumbers.isTrailer !== undefined) params.isTrailer = String(showPlateNumbers.isTrailer);
}
// Map prototype's type field to backend vehicleType
if (showPlateNumbers.type) {
if (showPlateNumbers.type === '4.5T') {
if (showPlateNumbers.isColdChain === true) {
params.vehicleType = '4.5T冷链';
} else if (showPlateNumbers.isColdChain === false) {
params.vehicleType = '4.5T普货';
}
} else if (showPlateNumbers.type === '18T') {
params.vehicleType = '18T';
} else if (showPlateNumbers.type === '49T') {
params.vehicleType = '49T';
} else if (showPlateNumbers.type === '其他车型') {
if (showPlateNumbers.isTrailer === true) {
params.isTrailer = 'true';
} else if (showPlateNumbers.isTrailer === false) {
params.vehicleType = '其他';
}
}
}
fetchVehicleList(params)
.then(setModalVehicles)
.catch(() => setModalVehicles([]))
@@ -860,8 +884,9 @@ export default function App() {
</div>
</div>
{/* Department Operations Statistics */}
<section className="bg-white rounded-2xl border border-gray-100 shadow-sm overflow-hidden mb-6">
<section className="bg-white rounded-2xl shadow-sm border border-gray-100 overflow-hidden mb-6">
<div className="p-3 sm:p-4 border-b border-gray-50 flex items-center justify-between gap-4">
<div className="flex items-center gap-3">
<div className="w-1.5 h-6 bg-blue-600 rounded-full"></div>
@@ -871,9 +896,9 @@ export default function App() {
</div>
</div>
</div>
<div className="p-0 sm:p-2 bg-gray-50/30">
{/* Overall Total Summary (Compact) */}
{/* Overall Total Summary (Compact) - Moved to Top */}
<div className="m-2 bg-slate-800 rounded-xl p-3 text-white shadow-lg">
<div className="grid grid-cols-2 sm:grid-cols-4 gap-4">
<div className="flex flex-col">
@@ -894,7 +919,9 @@ export default function App() {
</div>
<div className="flex flex-col">
<span className="text-[9px] opacity-50 uppercase font-bold tracking-widest mb-0.5 text-blue-400"></span>
<span className="text-xl font-black text-blue-400"></span>
<span className="text-xl font-black text-blue-400">
{'—'}
</span>
</div>
</div>
</div>
@@ -902,13 +929,13 @@ export default function App() {
{/* Controls Row: Toggles Left, Filter Right */}
<div className="px-2 mb-2 flex items-center justify-between gap-4">
<div className="flex bg-gray-200/50 p-1 rounded-lg shadow-inner">
<button
<button
onClick={() => setDeptViewMode('department')}
className={`px-4 py-1.5 text-xs font-bold rounded-md transition-all ${deptViewMode === 'department' ? 'bg-white text-blue-600 shadow-sm' : 'text-gray-500 hover:text-gray-700'}`}
>
</button>
<button
<button
onClick={() => setDeptViewMode('manager')}
className={`px-4 py-1.5 text-xs font-bold rounded-md transition-all ${deptViewMode === 'manager' ? 'bg-white text-blue-600 shadow-sm' : 'text-gray-500 hover:text-gray-700'}`}
>
@@ -920,7 +947,7 @@ export default function App() {
{deptViewMode === 'manager' && (
<div className="relative">
<Filter className="absolute left-2.5 top-1/2 -translate-y-1/2 text-gray-400" size={14} />
<select
<select
value={selectedManager}
onChange={(e) => setSelectedManager(e.target.value)}
className="w-full pl-9 pr-8 py-1.5 bg-white border border-gray-200 rounded-lg text-xs focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all shadow-sm appearance-none cursor-pointer font-bold text-gray-700"
@@ -971,7 +998,7 @@ export default function App() {
const isExpanded = expandedDepts.has(dept.department);
return (
<React.Fragment key={dept.department}>
<tr
<tr
className={`cursor-pointer transition-all border-b border-gray-100 ${
isExpanded ? 'bg-blue-50/50' : 'hover:bg-gray-50'
}`}
@@ -980,11 +1007,20 @@ export default function App() {
<td className="p-2 border-r border-gray-100 font-bold text-gray-800">
{dept.department}
</td>
<td className="p-2 border-r border-gray-100 text-center"><span className="bg-blue-50 text-blue-600 text-[10px] font-bold px-2 py-0.5 rounded-full"></span></td>
<td className="p-2 border-r border-gray-100 text-center">
<span className="bg-blue-50 text-blue-600 text-[10px] font-bold px-2 py-0.5 rounded-full">
{'—'}
</span>
</td>
<td className="p-2 border-r border-gray-100 text-center font-black text-gray-800 text-sm">
{dept.totalAssets}
</td>
<td className="p-2 border-r border-gray-100 text-center"><div className="flex items-baseline justify-center gap-1"><span className="font-black text-gray-400 text-sm"></span></div></td>
<td className="p-2 border-r border-gray-100 text-center">
<div className="flex items-baseline justify-center gap-1">
<span className="font-black text-gray-800 text-sm">{'—'}</span>
<span className="text-[9px] text-gray-400 font-bold">km</span>
</div>
</td>
<td className="p-2 border-r border-gray-100 text-center font-black text-green-500 text-sm">
{dept.operatingCount}
</td>
@@ -1003,7 +1039,7 @@ export default function App() {
const isManagerExpanded = expandedManagerDetails.has(m.manager);
return (
<div key={m.manager} className="bg-white rounded-lg border border-gray-100 shadow-sm overflow-hidden">
<div
<div
className="p-2 flex justify-between items-center cursor-pointer hover:bg-gray-50 transition-colors"
onClick={() => toggleManagerDetails(m.manager)}
>
@@ -1011,58 +1047,58 @@ export default function App() {
{isManagerExpanded ? <ChevronDown size={14} className="text-blue-500" /> : <ChevronRight size={14} className="text-gray-300" />}
<span className="font-bold text-gray-700 text-xs">{m.manager}</span>
</div>
<button
<button
onClick={(e) => {
e.stopPropagation();
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, category: 'Operating' });
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, category: 'Operating', source: 'department' });
}}
className="text-[10px] font-bold text-blue-600 bg-blue-50 px-2 py-0.5 rounded hover:bg-blue-100 transition-colors"
>
: {m.total}
</button>
</div>
{isManagerExpanded && (
<div className="p-2 pt-0 border-t border-gray-50 bg-gray-50/30">
<div className="grid grid-cols-3 gap-1 mt-2">
<div
<div
className="text-center bg-white p-1 rounded cursor-pointer hover:bg-blue-50 transition-colors border border-gray-100"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '4.5T普货' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '4.5T', isColdChain: false, source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase">4.5T</div>
<div className="text-[10px] font-bold text-gray-600">{m.t4_5}</div>
</div>
<div
<div
className="text-center bg-white p-1 rounded cursor-pointer hover:bg-blue-50 transition-colors border border-gray-100"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '4.5T冷链' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '4.5T', isColdChain: true, source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase"></div>
<div className="text-[10px] font-bold text-gray-600">{m.t4_5c}</div>
</div>
<div
<div
className="text-center bg-white p-1 rounded cursor-pointer hover:bg-blue-50 transition-colors border border-gray-100"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '18T' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '18T', source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase">18T</div>
<div className="text-[10px] font-bold text-gray-600">{m.t18}</div>
</div>
<div
<div
className="text-center bg-white p-1 rounded cursor-pointer hover:bg-blue-50 transition-colors border border-gray-100"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '49T' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '49T', source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase">49T</div>
<div className="text-[10px] font-bold text-gray-600">{m.t49}</div>
</div>
<div
<div
className="text-center bg-white p-1 rounded cursor-pointer hover:bg-blue-50 transition-colors border border-gray-100"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, isTrailer: true })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '其他车型', isTrailer: true, source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase"></div>
<div className="text-[10px] font-bold text-gray-600">{m.trailer}</div>
</div>
<div
<div
className="text-center bg-white p-1 rounded cursor-pointer hover:bg-blue-50 transition-colors border border-gray-100"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '其他' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '其他车型', isTrailer: false, source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase"></div>
<div className="text-[10px] font-bold text-gray-600">{m.other}</div>
@@ -1085,7 +1121,7 @@ export default function App() {
const isManagerExpanded = expandedManagerDetails.has(m.manager);
return (
<React.Fragment key={m.manager}>
<tr
<tr
className="border-b border-gray-100 hover:bg-gray-50 transition-colors cursor-pointer"
onClick={() => toggleManagerDetails(m.manager)}
>
@@ -1094,11 +1130,11 @@ export default function App() {
{m.manager}
</td>
<td className="p-2 border-r border-gray-100 text-gray-600">{m.department}</td>
<td
<td
className="p-2 border-r border-gray-100 text-center font-black text-blue-600 text-sm cursor-pointer hover:bg-blue-50"
onClick={(e) => {
e.stopPropagation();
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, category: 'Operating' });
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, source: 'department' });
}}
>
{m.total}
@@ -1110,10 +1146,10 @@ export default function App() {
<td className="p-2 border-r border-gray-100 text-center text-gray-400">-</td>
<td className="p-2 border-r border-gray-100 text-center text-gray-400">-</td>
<td className="p-2 text-center">
<button
<button
onClick={(e) => {
e.stopPropagation();
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, category: 'Operating' });
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, category: 'Operating', source: 'department' });
}}
className="text-blue-500 hover:text-blue-700 transition-colors"
>
@@ -1125,27 +1161,27 @@ export default function App() {
<tr className="bg-gray-50/50 border-b border-gray-100">
<td colSpan={10} className="p-0">
<div className="grid grid-cols-6 text-[10px] bg-white/50">
<div className="p-2 border-r border-gray-100 flex flex-col items-center cursor-pointer hover:bg-blue-50" onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '4.5T普货' })}>
<div className="p-2 border-r border-gray-100 flex flex-col items-center cursor-pointer hover:bg-blue-50" onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '4.5T', isColdChain: false, source: 'department' })}>
<span className="text-gray-400 uppercase mb-1">4.5T</span>
<span className="font-bold text-gray-600">{m.t4_5}</span>
</div>
<div className="p-2 border-r border-gray-100 flex flex-col items-center cursor-pointer hover:bg-blue-50" onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '4.5T冷链' })}>
<div className="p-2 border-r border-gray-100 flex flex-col items-center cursor-pointer hover:bg-blue-50" onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '4.5T', isColdChain: true, source: 'department' })}>
<span className="text-gray-400 uppercase mb-1"></span>
<span className="font-bold text-gray-600">{m.t4_5c}</span>
</div>
<div className="p-2 border-r border-gray-100 flex flex-col items-center cursor-pointer hover:bg-blue-50" onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '18T' })}>
<div className="p-2 border-r border-gray-100 flex flex-col items-center cursor-pointer hover:bg-blue-50" onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '18T', source: 'department' })}>
<span className="text-gray-400 uppercase mb-1">18T</span>
<span className="font-bold text-gray-600">{m.t18}</span>
</div>
<div className="p-2 border-r border-gray-100 flex flex-col items-center cursor-pointer hover:bg-blue-50" onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '49T' })}>
<div className="p-2 border-r border-gray-100 flex flex-col items-center cursor-pointer hover:bg-blue-50" onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '49T', source: 'department' })}>
<span className="text-gray-400 uppercase mb-1">49T</span>
<span className="font-bold text-gray-600">{m.t49}</span>
</div>
<div className="p-2 border-r border-gray-100 flex flex-col items-center cursor-pointer hover:bg-blue-50" onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, isTrailer: true })}>
<div className="p-2 border-r border-gray-100 flex flex-col items-center cursor-pointer hover:bg-blue-50" onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '其他车型', isTrailer: true, source: 'department' })}>
<span className="text-gray-400 uppercase mb-1"></span>
<span className="font-bold text-gray-600">{m.trailer}</span>
</div>
<div className="p-2 flex flex-col items-center cursor-pointer hover:bg-blue-50" onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '其他' })}>
<div className="p-2 flex flex-col items-center cursor-pointer hover:bg-blue-50" onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '其他车型', isTrailer: false, source: 'department' })}>
<span className="text-gray-400 uppercase mb-1"></span>
<span className="font-bold text-gray-600">{m.other}</span>
</div>
@@ -1168,7 +1204,7 @@ export default function App() {
const isExpanded = expandedDepts.has(dept.department);
return (
<div key={dept.department} className="bg-white rounded-xl border border-gray-100 shadow-sm overflow-hidden">
<div
<div
className="p-3 cursor-pointer"
onClick={() => toggleDept(dept.department)}
>
@@ -1185,7 +1221,7 @@ export default function App() {
</div>
<div className="text-center">
<div className="text-[8px] text-gray-400 uppercase font-bold mb-0.5"></div>
<div className="text-xs font-black text-gray-400"></div>
<div className="text-xs font-black text-gray-800">{'—'}</div>
</div>
<div className="text-center">
<div className="text-[8px] text-green-500 uppercase font-bold mb-0.5"></div>
@@ -1206,7 +1242,7 @@ export default function App() {
const isManagerExpanded = expandedManagerDetails.has(m.manager);
return (
<div key={m.manager} className="bg-white rounded border border-gray-100 shadow-sm overflow-hidden">
<div
<div
className="p-2 flex justify-between items-center cursor-pointer"
onClick={() => toggleManagerDetails(m.manager)}
>
@@ -1214,10 +1250,10 @@ export default function App() {
{isManagerExpanded ? <ChevronDown size={12} className="text-blue-400" /> : <ChevronRight size={12} className="text-gray-300" />}
<span className="text-[11px] font-bold text-gray-700">{m.manager}</span>
</div>
<button
<button
onClick={(e) => {
e.stopPropagation();
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, category: 'Operating' });
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, source: 'department' });
}}
className="text-[10px] font-bold text-blue-600 bg-blue-50 px-2 py-0.5 rounded"
>
@@ -1227,44 +1263,44 @@ export default function App() {
{isManagerExpanded && (
<div className="p-2 border-t border-gray-50 bg-gray-50/30 grid grid-cols-3 gap-1">
<div
<div
className="text-center bg-white p-1 rounded border border-gray-100 cursor-pointer"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '4.5T普货' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '4.5T', isColdChain: false, source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase">4.5T</div>
<div className="text-[10px] font-bold text-gray-600">{m.t4_5}</div>
</div>
<div
<div
className="text-center bg-white p-1 rounded border border-gray-100 cursor-pointer"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '4.5T冷链' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '4.5T', isColdChain: true, source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase"></div>
<div className="text-[10px] font-bold text-gray-600">{m.t4_5c}</div>
</div>
<div
<div
className="text-center bg-white p-1 rounded border border-gray-100 cursor-pointer"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '18T' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '18T', source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase">18T</div>
<div className="text-[10px] font-bold text-gray-600">{m.t18}</div>
</div>
<div
<div
className="text-center bg-white p-1 rounded border border-gray-100 cursor-pointer"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '49T' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '49T', source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase">49T</div>
<div className="text-[10px] font-bold text-gray-600">{m.t49}</div>
</div>
<div
<div
className="text-center bg-white p-1 rounded border border-gray-100 cursor-pointer"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, isTrailer: true })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '其他车型', isTrailer: true, source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase"></div>
<div className="text-[10px] font-bold text-gray-600">{m.trailer}</div>
</div>
<div
<div
className="text-center bg-white p-1 rounded border border-gray-100 cursor-pointer"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '其他' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '其他车型', isTrailer: false, source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase"></div>
<div className="text-[10px] font-bold text-gray-600">{m.other}</div>
@@ -1284,7 +1320,7 @@ export default function App() {
const isManagerExpanded = expandedManagerDetails.has(m.manager);
return (
<div key={m.manager} className="bg-white rounded-xl border border-gray-100 shadow-sm overflow-hidden">
<div
<div
className="p-2 cursor-pointer flex items-center justify-between gap-2"
onClick={() => toggleManagerDetails(m.manager)}
>
@@ -1293,22 +1329,22 @@ export default function App() {
<div className="flex items-center gap-3 min-w-0">
<h3 className="text-sm font-bold text-gray-800 shrink-0">{m.manager}</h3>
<span className="text-[11px] text-gray-500 shrink-0">{m.department}</span>
<div
<div
className="text-[11px] font-bold text-blue-600 whitespace-nowrap"
onClick={(e) => {
e.stopPropagation();
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, category: 'Operating' });
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, source: 'department' });
}}
>
: {m.total}
</div>
</div>
</div>
<button
<button
onClick={(e) => {
e.stopPropagation();
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, category: 'Operating' });
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, category: 'Operating', source: 'department' });
}}
className="text-blue-500 p-1 hover:bg-blue-50 rounded transition-colors flex-shrink-0"
>
@@ -1318,44 +1354,44 @@ export default function App() {
{isManagerExpanded && (
<div className="p-2 border-t border-gray-50 bg-gray-50/30 grid grid-cols-3 gap-1">
<div
<div
className="text-center bg-white p-1 rounded border border-gray-100 cursor-pointer"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '4.5T普货' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '4.5T', isColdChain: false, source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase">4.5T</div>
<div className="text-[10px] font-bold text-gray-600">{m.t4_5}</div>
</div>
<div
<div
className="text-center bg-white p-1 rounded border border-gray-100 cursor-pointer"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '4.5T冷链' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '4.5T', isColdChain: true, source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase"></div>
<div className="text-[10px] font-bold text-gray-600">{m.t4_5c}</div>
</div>
<div
<div
className="text-center bg-white p-1 rounded border border-gray-100 cursor-pointer"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '18T' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '18T', source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase">18T</div>
<div className="text-[10px] font-bold text-gray-600">{m.t18}</div>
</div>
<div
<div
className="text-center bg-white p-1 rounded border border-gray-100 cursor-pointer"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '49T' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '49T', source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase">49T</div>
<div className="text-[10px] font-bold text-gray-600">{m.t49}</div>
</div>
<div
<div
className="text-center bg-white p-1 rounded border border-gray-100 cursor-pointer"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, isTrailer: true })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '其他车型', isTrailer: true, source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase"></div>
<div className="text-[10px] font-bold text-gray-600">{m.trailer}</div>
</div>
<div
<div
className="text-center bg-white p-1 rounded border border-gray-100 cursor-pointer"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, vehicleType: '其他' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '其他车型', isTrailer: false, source: 'department' })}
>
<div className="text-[8px] text-gray-400 uppercase"></div>
<div className="text-[10px] font-bold text-gray-600">{m.other}</div>
@@ -1382,11 +1418,11 @@ export default function App() {
</div>
<div className="flex items-center gap-2">
<button
<button
onClick={() => setIsRegionFilterOpen(!isRegionFilterOpen)}
className={`flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium transition-all ${
isRegionFilterOpen || Object.values(regionFilters).some(v => v !== '')
? 'bg-slate-200 text-slate-900 shadow-sm'
? 'bg-slate-200 text-slate-900 shadow-sm'
: 'bg-slate-100 text-slate-600 hover:bg-slate-200'
}`}
>
@@ -1401,7 +1437,7 @@ export default function App() {
{isRegionFilterOpen && (
<>
<div className="fixed inset-0 z-40" onClick={() => setIsRegionFilterOpen(false)} />
<motion.div
<motion.div
initial={{ opacity: 0, y: 10, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 10, scale: 0.95 }}
@@ -1409,20 +1445,20 @@ export default function App() {
>
<div className="flex items-center justify-between mb-4">
<h3 className="text-sm font-bold text-gray-900"></h3>
<button
<button
onClick={() => setRegionFilters({ region: '', city: '', customer: '' })}
className="text-[10px] text-slate-600 hover:text-slate-700 font-medium"
>
</button>
</div>
<div className="space-y-4">
<div className="space-y-1.5">
<label className="text-[10px] font-bold text-gray-400 uppercase tracking-wider"></label>
<div className="relative">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400" size={14} />
<input
<input
type="text"
placeholder="搜索客户名称..."
className="w-full bg-gray-50 border border-gray-200 rounded-lg py-2 pl-9 pr-3 text-xs focus:ring-2 focus:ring-slate-500/20 focus:border-slate-500 outline-none transition-all"
@@ -1435,7 +1471,7 @@ export default function App() {
<div className="grid grid-cols-2 gap-3">
<div className="space-y-1.5">
<label className="text-[10px] font-bold text-gray-400 uppercase tracking-wider"></label>
<select
<select
className="w-full bg-gray-50 border border-gray-200 rounded-lg py-2 px-2 text-xs focus:ring-2 focus:ring-slate-500/20 focus:border-slate-500 outline-none transition-all cursor-pointer"
value={regionFilters.region}
onChange={(e) => setRegionFilters(prev => ({ ...prev, region: e.target.value }))}
@@ -1446,7 +1482,7 @@ export default function App() {
</div>
<div className="space-y-1.5">
<label className="text-[10px] font-bold text-gray-400 uppercase tracking-wider"></label>
<select
<select
className="w-full bg-gray-50 border border-gray-200 rounded-lg py-2 px-2 text-xs focus:ring-2 focus:ring-slate-500/20 focus:border-slate-500 outline-none transition-all cursor-pointer"
value={regionFilters.city}
onChange={(e) => setRegionFilters(prev => ({ ...prev, city: e.target.value }))}
@@ -1459,7 +1495,7 @@ export default function App() {
</div>
<div className="mt-6 pt-4 border-t border-gray-50">
<button
<button
onClick={() => setIsRegionFilterOpen(false)}
className="w-full bg-slate-800 text-white py-2 rounded-lg text-xs font-bold hover:bg-slate-900 transition-colors shadow-lg shadow-slate-900/20"
>
@@ -1486,70 +1522,88 @@ export default function App() {
</tr>
</thead>
<tbody className="text-xs">
{filteredRegionData.map((r) => {
if (r.totalAssets === 0) return null;
const isExpanded = expandedRegions.has(r.region);
{uniqueRegions.filter(r => !regionFilters.region || r === regionFilters.region).map((region) => {
const regionStats = customerData.filter(s => {
const matchRegion = s.region === region;
const matchCity = !regionFilters.city || s.city === regionFilters.city;
const matchCustomer = !regionFilters.customer || s.customer.toLowerCase().includes(regionFilters.customer.toLowerCase());
return matchRegion && matchCity && matchCustomer;
});
const totalAssets = regionStats.reduce((acc, s) => acc + s.total, 0);
if (totalAssets === 0) return null;
const isExpanded = expandedRegions.has(region);
return (
<React.Fragment key={r.region}>
<tr
<React.Fragment key={region}>
<tr
className={`border-b border-slate-100 cursor-pointer transition-colors ${isExpanded ? 'bg-slate-50' : 'bg-white hover:bg-slate-50/50'}`}
onClick={() => toggleRegion(r.region)}
onClick={() => toggleRegion(region)}
>
<td className="p-2 font-bold text-slate-700 flex items-center gap-2">
{isExpanded ? <ChevronDown size={14} className="text-slate-400" /> : <ChevronRight size={14} className="text-slate-400" />}
<Truck size={14} className="text-slate-400" />
{r.region}
{region}
</td>
<td className="p-2 text-center font-bold text-slate-600">{r.totalAssets}</td>
<td
<td className="p-2 text-center font-bold text-slate-600">{totalAssets}</td>
<td
className="p-2 text-center text-green-600 font-bold cursor-pointer hover:bg-green-50"
onClick={(e) => {
e.stopPropagation();
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', category: 'Operating' });
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', category: 'Operating', source: 'asset' });
}}
>
{r.operatingCount}
{Math.floor(totalAssets * 0.8)}
</td>
<td
<td
className="p-2 text-center text-orange-600 font-bold cursor-pointer hover:bg-orange-50"
onClick={(e) => {
e.stopPropagation();
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', category: 'Pending' });
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', category: 'Pending', source: 'asset' });
}}
>
{r.inventoryCount}
{Math.floor(totalAssets * 0.05)}
</td>
<td className="p-2 text-center text-slate-500 font-medium">
{r.customers.slice(0, 2).join(', ')}
{regionStats.slice(0, 2).map(s => s.customer).join(', ')}
</td>
</tr>
{isExpanded && r.typeBreakdown.map(tb => {
if (tb.total === 0) return null;
const vehicleType = tb.type === '4.5T' ? '4.5T普货' : tb.type;
{isExpanded && ['4.5T', '18T', '49T'].map(type => {
const typeTotal = regionStats.reduce((acc, s) => {
if (type === '4.5T') return acc + s.t4_5 + s.t4_5c;
if (type === '18T') return acc + s.t18;
if (type === '49T') return acc + s.t49;
return acc;
}, 0);
if (typeTotal === 0) return null;
return (
<React.Fragment key={tb.type}>
<React.Fragment key={type}>
<tr className="border-b border-gray-50 hover:bg-gray-50">
<td className="p-2 pl-8 text-gray-500 flex items-center gap-2">
<div className="w-1 h-1 bg-slate-300 rounded-full"></div>
{tb.type}
{type}
</td>
<td className="p-2 text-center text-gray-600">{tb.total}</td>
<td
<td className="p-2 text-center text-gray-600">{typeTotal}</td>
<td
className="p-2 text-center text-green-600 cursor-pointer hover:bg-green-50"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', vehicleType, category: 'Operating' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', type, category: 'Operating', source: 'asset' })}
>
{tb.operating}
{Math.floor(typeTotal * 0.8)}
</td>
<td
<td
className="p-2 text-center text-orange-600 cursor-pointer hover:bg-orange-50"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', vehicleType, category: 'Pending' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', type, category: 'Pending', source: 'asset' })}
>
{tb.inventory}
{Math.floor(typeTotal * 0.05)}
</td>
<td className="p-2 text-center text-gray-400 italic">
{tb.customers.join(', ')}
{regionStats.filter(s => {
if (type === '4.5T') return (s.t4_5 + s.t4_5c) > 0;
if (type === '18T') return s.t18 > 0;
if (type === '49T') return s.t49 > 0;
return false;
}).map(s => s.customer).join(', ')}
</td>
</tr>
</React.Fragment>
@@ -1564,60 +1618,73 @@ export default function App() {
{/* Mobile View (Region) */}
<div className="lg:hidden p-2 space-y-3">
{filteredRegionData.map((r) => {
if (r.totalAssets === 0) return null;
const isExpanded = expandedRegions.has(r.region);
{uniqueRegions.filter(r => !regionFilters.region || r === regionFilters.region).map((region) => {
const regionStats = customerData.filter(s => {
const matchRegion = s.region === region;
const matchCity = !regionFilters.city || s.city === regionFilters.city;
const matchCustomer = !regionFilters.customer || s.customer.toLowerCase().includes(regionFilters.customer.toLowerCase());
return matchRegion && matchCity && matchCustomer;
});
const totalAssets = regionStats.reduce((acc, s) => acc + s.total, 0);
if (totalAssets === 0) return null;
const isExpanded = expandedRegions.has(region);
return (
<div key={r.region} className="bg-slate-50/50 rounded-xl border border-slate-100 overflow-hidden">
<div
<div key={region} className="bg-slate-50/50 rounded-xl border border-slate-100 overflow-hidden">
<div
className="bg-white p-3 flex justify-between items-center cursor-pointer"
onClick={() => toggleRegion(r.region)}
onClick={() => toggleRegion(region)}
>
<div className="flex items-center gap-2 font-bold text-slate-700">
{isExpanded ? <ChevronDown size={14} className="text-slate-400" /> : <ChevronRight size={14} className="text-slate-400" />}
<Truck size={14} className="text-slate-400" />
{r.region}
{region}
</div>
<div className="text-xs font-bold text-slate-500">: {r.totalAssets}</div>
<div className="text-xs font-bold text-slate-500">: {totalAssets}</div>
</div>
{isExpanded && (
<>
<div className="p-2 grid grid-cols-2 gap-2 text-center border-t border-slate-100">
<div
<div
className="bg-white p-2 rounded border border-slate-100 cursor-pointer active:bg-green-50"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', category: 'Operating' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', category: 'Operating', source: 'asset' })}
>
<div className="text-[9px] text-gray-400 uppercase"></div>
<div className="text-xs font-bold text-green-600">{r.operatingCount}</div>
<div className="text-xs font-bold text-green-600">{Math.floor(totalAssets * 0.8)}</div>
</div>
<div
<div
className="bg-white p-2 rounded border border-slate-100 cursor-pointer active:bg-orange-50"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', category: 'Pending' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', category: 'Pending', source: 'asset' })}
>
<div className="text-[9px] text-gray-400 uppercase"></div>
<div className="text-xs font-bold text-orange-600">{r.inventoryCount}</div>
<div className="text-xs font-bold text-orange-600">{Math.floor(totalAssets * 0.05)}</div>
</div>
</div>
<div className="px-2 pb-2 space-y-1">
{r.typeBreakdown.map(tb => {
if (tb.total === 0) return null;
const vehicleType = tb.type === '4.5T' ? '4.5T普货' : tb.type;
{['4.5T', '18T', '49T'].map(type => {
const typeTotal = regionStats.reduce((acc, s) => {
if (type === '4.5T') return acc + s.t4_5 + s.t4_5c;
if (type === '18T') return acc + s.t18;
if (type === '49T') return acc + s.t49;
return acc;
}, 0);
if (typeTotal === 0) return null;
return (
<div key={tb.type} className="flex justify-between items-center text-[10px] bg-white/80 px-2 py-1.5 rounded border border-slate-50">
<span className="text-gray-500">{tb.type} </span>
<div key={type} className="flex justify-between items-center text-[10px] bg-white/80 px-2 py-1.5 rounded border border-slate-50">
<span className="text-gray-500">{type} </span>
<div className="flex gap-3">
<span
<span
className="font-bold text-green-600 cursor-pointer"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', vehicleType, category: 'Operating' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', type, category: 'Operating', source: 'asset' })}
>
:{tb.operating}
:{Math.floor(typeTotal * 0.8)}
</span>
<span
<span
className="font-bold text-orange-600 cursor-pointer"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', vehicleType, category: 'Pending' })}
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', type, category: 'Pending', source: 'asset' })}
>
:{tb.inventory}
:{Math.floor(typeTotal * 0.05)}
</span>
</div>
</div>
@@ -1645,11 +1712,11 @@ export default function App() {
</div>
<div className="flex items-center gap-2">
<button
<button
onClick={() => setIsCustomerFilterOpen(!isCustomerFilterOpen)}
className={`flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-medium transition-all ${
isCustomerFilterOpen || Object.values(customerFilters).some(v => v !== '')
? 'bg-emerald-400 text-emerald-900 shadow-lg shadow-emerald-900/20'
? 'bg-emerald-400 text-emerald-900 shadow-lg shadow-emerald-900/20'
: 'bg-emerald-700/50 text-emerald-100 hover:bg-emerald-700'
}`}
>
@@ -1664,13 +1731,13 @@ export default function App() {
{isCustomerFilterOpen && (
<>
{/* Backdrop for closing */}
<div
className="fixed inset-0 z-40"
<div
className="fixed inset-0 z-40"
onClick={() => setIsCustomerFilterOpen(false)}
/>
{/* Popover Content */}
<motion.div
<motion.div
initial={{ opacity: 0, y: 10, scale: 0.95 }}
animate={{ opacity: 1, y: 0, scale: 1 }}
exit={{ opacity: 0, y: 10, scale: 0.95 }}
@@ -1678,22 +1745,22 @@ export default function App() {
>
<div className="flex items-center justify-between mb-4">
<h3 className="text-sm font-bold text-gray-900"></h3>
<button
<button
onClick={() => setCustomerFilters({ customer: '', brand: '', department: '', manager: '', region: '' })}
className="text-[10px] text-emerald-600 hover:text-emerald-700 font-medium"
>
</button>
</div>
<div className="space-y-4">
<div className="space-y-1.5">
<label className="text-[10px] font-bold text-gray-400 uppercase tracking-wider"></label>
<div className="relative">
<Search className="absolute left-2 top-1/2 -translate-y-1/2 text-gray-400" size={12} />
<input
type="text"
placeholder="搜索客户..."
<input
type="text"
placeholder="搜索客户..."
className="w-full bg-gray-50 border border-gray-200 rounded-lg py-2 pl-8 pr-2 text-xs focus:ring-2 focus:ring-emerald-500/20 focus:border-emerald-500 outline-none transition-all"
value={customerFilters.customer}
onChange={(e) => setCustomerFilters(prev => ({ ...prev, customer: e.target.value }))}
@@ -1705,9 +1772,9 @@ export default function App() {
<label className="text-[10px] font-bold text-gray-400 uppercase tracking-wider"></label>
<div className="relative">
<Search className="absolute left-2 top-1/2 -translate-y-1/2 text-gray-400" size={12} />
<input
type="text"
placeholder="搜索负责人..."
<input
type="text"
placeholder="搜索负责人..."
className="w-full bg-gray-50 border border-gray-200 rounded-lg py-2 pl-8 pr-2 text-xs focus:ring-2 focus:ring-emerald-500/20 focus:border-emerald-500 outline-none transition-all"
value={customerFilters.manager}
onChange={(e) => setCustomerFilters(prev => ({ ...prev, manager: e.target.value }))}
@@ -1718,7 +1785,7 @@ export default function App() {
<div className="grid grid-cols-2 gap-3">
<div className="space-y-1.5">
<label className="text-[10px] font-bold text-gray-400 uppercase tracking-wider"></label>
<select
<select
className="w-full bg-gray-50 border border-gray-200 rounded-lg py-2 px-2 text-xs focus:ring-2 focus:ring-emerald-500/20 focus:border-emerald-500 outline-none transition-all cursor-pointer"
value={customerFilters.brand}
onChange={(e) => setCustomerFilters(prev => ({ ...prev, brand: e.target.value }))}
@@ -1729,7 +1796,7 @@ export default function App() {
</div>
<div className="space-y-1.5">
<label className="text-[10px] font-bold text-gray-400 uppercase tracking-wider"></label>
<select
<select
className="w-full bg-gray-50 border border-gray-200 rounded-lg py-2 px-2 text-xs focus:ring-2 focus:ring-emerald-500/20 focus:border-emerald-500 outline-none transition-all cursor-pointer"
value={customerFilters.department}
onChange={(e) => setCustomerFilters(prev => ({ ...prev, department: e.target.value }))}
@@ -1740,7 +1807,7 @@ export default function App() {
</div>
<div className="space-y-1.5">
<label className="text-[10px] font-bold text-gray-400 uppercase tracking-wider"></label>
<select
<select
className="w-full bg-gray-50 border border-gray-200 rounded-lg py-2 px-2 text-xs focus:ring-2 focus:ring-emerald-500/20 focus:border-emerald-500 outline-none transition-all cursor-pointer"
value={customerFilters.region}
onChange={(e) => setCustomerFilters(prev => ({ ...prev, region: e.target.value }))}
@@ -1753,7 +1820,7 @@ export default function App() {
</div>
<div className="mt-6 pt-4 border-t border-gray-50">
<button
<button
onClick={() => setIsCustomerFilterOpen(false)}
className="w-full bg-emerald-600 text-white py-2 rounded-lg text-xs font-bold hover:bg-emerald-700 transition-colors shadow-lg shadow-emerald-600/20"
>
@@ -1790,7 +1857,7 @@ export default function App() {
const isExpanded = expandedCustomers.has(cust.customer);
return (
<React.Fragment key={cust.customer}>
<tr
<tr
className={`cursor-pointer transition-all border-b border-gray-100 ${
isExpanded ? 'bg-emerald-50/50' : 'hover:bg-gray-50'
}`}
@@ -1804,17 +1871,17 @@ export default function App() {
<span className="bg-gray-100 px-2 py-0.5 rounded text-[10px] font-medium">{cust.region}</span>
</td>
<td className="p-2 border-r border-gray-100 text-gray-600 text-center">{cust.manager}</td>
<td className="p-2 border-r border-gray-100 text-center text-gray-500 cursor-pointer hover:bg-emerald-50 transition-colors" onClick={(e) => { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, vehicleType: '4.5T普货' }); }}>{cust.t4_5}</td>
<td className="p-2 border-r border-gray-100 text-center text-gray-500 cursor-pointer hover:bg-emerald-50 transition-colors" onClick={(e) => { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, vehicleType: '4.5T冷链' }); }}>{cust.t4_5c}</td>
<td className="p-2 border-r border-gray-100 text-center text-gray-500 cursor-pointer hover:bg-emerald-50 transition-colors" onClick={(e) => { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, vehicleType: '18T' }); }}>{cust.t18}</td>
<td className="p-2 border-r border-gray-100 text-center text-gray-500 cursor-pointer hover:bg-emerald-50 transition-colors" onClick={(e) => { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, vehicleType: '49T' }); }}>{cust.t49}</td>
<td className="p-2 border-r border-gray-100 text-center text-gray-500 cursor-pointer hover:bg-emerald-50 transition-colors" onClick={(e) => { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, isTrailer: true }); }}>{cust.trailer}</td>
<td className="p-2 border-r border-gray-100 text-center text-gray-500 cursor-pointer hover:bg-emerald-50 transition-colors" onClick={(e) => { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, vehicleType: '其他' }); }}>{cust.other}</td>
<td className="p-2 text-center font-bold bg-emerald-50 text-emerald-800 cursor-pointer hover:bg-emerald-100 transition-colors" onClick={(e) => { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer }); }}>{cust.total}</td>
<td className="p-2 border-r border-gray-100 text-center text-gray-500 cursor-pointer hover:bg-emerald-50 transition-colors" onClick={(e) => { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '4.5T', source: 'customer' }); }}>{cust.t4_5}</td>
<td className="p-2 border-r border-gray-100 text-center text-gray-500 cursor-pointer hover:bg-emerald-50 transition-colors" onClick={(e) => { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '4.5T', source: 'customer' }); }}>{cust.t4_5c}</td>
<td className="p-2 border-r border-gray-100 text-center text-gray-500 cursor-pointer hover:bg-emerald-50 transition-colors" onClick={(e) => { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '18T', source: 'customer' }); }}>{cust.t18}</td>
<td className="p-2 border-r border-gray-100 text-center text-gray-500 cursor-pointer hover:bg-emerald-50 transition-colors" onClick={(e) => { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '49T', source: 'customer' }); }}>{cust.t49}</td>
<td className="p-2 border-r border-gray-100 text-center text-gray-500 cursor-pointer hover:bg-emerald-50 transition-colors" onClick={(e) => { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '其他车型', source: 'customer' }); }}>{cust.trailer}</td>
<td className="p-2 border-r border-gray-100 text-center text-gray-500 cursor-pointer hover:bg-emerald-50 transition-colors" onClick={(e) => { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '其他车型', source: 'customer' }); }}>{cust.other}</td>
<td className="p-2 text-center font-bold bg-emerald-50 text-emerald-800 cursor-pointer hover:bg-emerald-100 transition-colors" onClick={(e) => { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, source: 'customer' }); }}>{cust.total}</td>
</tr>
{isExpanded && (
<tr className="bg-gray-50/30">
<td colSpan={10} className="p-2">
<td colSpan={9} className="p-2">
<div className="grid grid-cols-4 gap-2">
<div className="bg-white p-2 rounded border border-gray-100 shadow-sm">
<div className="text-[10px] text-gray-400 uppercase mb-1"></div>
@@ -1834,7 +1901,7 @@ export default function App() {
<div className="bg-white p-2 rounded border border-gray-100 shadow-sm">
<div className="text-[10px] text-gray-400 uppercase mb-1"></div>
<div className="text-sm font-bold text-gray-700">
{((cust.total / (customerData.reduce((s, c) => s + c.total, 0) || 1)) * 100).toFixed(1)}%
{((cust.total / deptData.reduce((s, d) => s + d.totalAssets, 0)) * 100).toFixed(1)}%
</div>
</div>
</div>
@@ -1854,7 +1921,7 @@ export default function App() {
const isExpanded = expandedCustomers.has(cust.customer);
return (
<div key={cust.customer} className="bg-white rounded border border-gray-100 shadow-sm overflow-hidden">
<div
<div
className={`p-2 flex justify-between items-center cursor-pointer transition-colors ${
isExpanded ? 'bg-emerald-50' : 'bg-gray-50'
}`}
@@ -1894,7 +1961,7 @@ export default function App() {
<div className="bg-gray-50 p-2 rounded border border-gray-100">
<div className="text-[8px] text-gray-400 uppercase mb-1"></div>
<div className="text-xs font-bold text-gray-700">
{((cust.total / (customerData.reduce((s, c) => s + c.total, 0) || 1)) * 100).toFixed(1)}%
{((cust.total / deptData.reduce((s, d) => s + d.totalAssets, 0)) * 100).toFixed(1)}%
</div>
</div>
</div>
@@ -1902,52 +1969,52 @@ export default function App() {
<div className="border-t border-gray-50 pt-2">
<div className="text-[8px] text-gray-400 uppercase mb-2"></div>
<div className="grid grid-cols-3 gap-2">
<div
className="text-center bg-gray-50 p-1 rounded cursor-pointer hover:bg-emerald-50 transition-colors"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, vehicleType: '4.5T普货' })}
>
<div className="text-[8px] text-gray-400 uppercase">4.5T</div>
<div className="text-[10px] font-bold text-gray-600">{cust.t4_5}</div>
</div>
<div
className="text-center bg-gray-50 p-1 rounded cursor-pointer hover:bg-emerald-50 transition-colors"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, vehicleType: '4.5T冷链' })}
>
<div className="text-[8px] text-gray-400 uppercase"></div>
<div className="text-[10px] font-bold text-gray-600">{cust.t4_5c}</div>
</div>
<div
className="text-center bg-gray-50 p-1 rounded cursor-pointer hover:bg-emerald-50 transition-colors"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, vehicleType: '18T' })}
>
<div className="text-[8px] text-gray-400 uppercase">18T</div>
<div className="text-[10px] font-bold text-gray-600">{cust.t18}</div>
</div>
<div
className="text-center bg-gray-50 p-1 rounded cursor-pointer hover:bg-emerald-50 transition-colors"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, vehicleType: '49T' })}
>
<div className="text-[8px] text-gray-400 uppercase">49T</div>
<div className="text-[10px] font-bold text-gray-600">{cust.t49}</div>
</div>
<div
className="text-center bg-gray-50 p-1 rounded cursor-pointer hover:bg-emerald-50 transition-colors"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, isTrailer: true })}
>
<div className="text-[8px] text-gray-400 uppercase"></div>
<div className="text-[10px] font-bold text-gray-600">{cust.trailer}</div>
</div>
<div
className="text-center bg-gray-50 p-1 rounded cursor-pointer hover:bg-emerald-50 transition-colors"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, vehicleType: '其他' })}
>
<div className="text-[8px] text-gray-400 uppercase"></div>
<div className="text-[10px] font-bold text-gray-600">{cust.other}</div>
</div>
<div
className="text-center bg-gray-50 p-1 rounded cursor-pointer hover:bg-emerald-50 transition-colors"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '4.5T', source: 'customer' })}
>
<div className="text-[8px] text-gray-400 uppercase">4.5T</div>
<div className="text-[10px] font-bold text-gray-600">{cust.t4_5}</div>
</div>
<div
className="text-center bg-gray-50 p-1 rounded cursor-pointer hover:bg-emerald-50 transition-colors"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '4.5T', source: 'customer' })}
>
<div className="text-[8px] text-gray-400 uppercase"></div>
<div className="text-[10px] font-bold text-gray-600">{cust.t4_5c}</div>
</div>
<div
className="text-center bg-gray-50 p-1 rounded cursor-pointer hover:bg-emerald-50 transition-colors"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '18T', source: 'customer' })}
>
<div className="text-[8px] text-gray-400 uppercase">18T</div>
<div className="text-[10px] font-bold text-gray-600">{cust.t18}</div>
</div>
<div
className="text-center bg-gray-50 p-1 rounded cursor-pointer hover:bg-emerald-50 transition-colors"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '49T', source: 'customer' })}
>
<div className="text-[8px] text-gray-400 uppercase">49T</div>
<div className="text-[10px] font-bold text-gray-600">{cust.t49}</div>
</div>
<div
className="text-center bg-gray-50 p-1 rounded cursor-pointer hover:bg-emerald-50 transition-colors"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '其他车型', source: 'customer' })}
>
<div className="text-[8px] text-gray-400 uppercase"></div>
<div className="text-[10px] font-bold text-gray-600">{cust.trailer}</div>
</div>
<div
className="text-center bg-gray-50 p-1 rounded cursor-pointer hover:bg-emerald-50 transition-colors"
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '其他车型', source: 'customer' })}
>
<div className="text-[8px] text-gray-400 uppercase"></div>
<div className="text-[10px] font-bold text-gray-600">{cust.other}</div>
</div>
</div>
</div>
)}
</div>
)}
</div>
);
})}
@@ -1955,6 +2022,7 @@ export default function App() {
</div>
</section>
{/* Plate Number Modal */}
<AnimatePresence>
{showPlateNumbers && (