204 lines
12 KiB
TypeScript
204 lines
12 KiB
TypeScript
import { Filter } from 'lucide-react';
|
||
import { AnimatePresence, motion } from 'motion/react';
|
||
import { InventoryDesktopTable } from './InventoryDesktopTable';
|
||
import { InventoryMobileList } from './InventoryMobileList';
|
||
import type { InventoryStatisticsSectionProps } from './types';
|
||
|
||
export function InventoryStatisticsSection({
|
||
inventoryFilters,
|
||
setInventoryFilters,
|
||
draftInventoryFilters,
|
||
setDraftInventoryFilters,
|
||
isInventoryFilterOpen,
|
||
setIsInventoryFilterOpen,
|
||
uniqueInventoryRegions,
|
||
uniqueInventoryCities,
|
||
uniqueInventoryBrands,
|
||
uniqueInventoryTypes,
|
||
uniqueInventoryModelsForType,
|
||
filteredInventoryStats,
|
||
inventoryTab,
|
||
setInventoryTab,
|
||
inventoryByRegion,
|
||
expandedInventoryRegions,
|
||
toggleInventoryRegion,
|
||
inventoryByModel,
|
||
expandedInventoryTypes,
|
||
toggleInventoryType,
|
||
setVehicleSelection: setShowPlateNumbers,
|
||
}: InventoryStatisticsSectionProps) {
|
||
const listProps = {
|
||
inventoryTab,
|
||
inventoryByRegion,
|
||
expandedInventoryRegions,
|
||
toggleInventoryRegion,
|
||
inventoryByModel,
|
||
expandedInventoryTypes,
|
||
toggleInventoryType,
|
||
setVehicleSelection: setShowPlateNumbers,
|
||
};
|
||
|
||
return (
|
||
<div className="bg-white rounded-2xl border border-gray-100 shadow-sm mb-6 overflow-hidden min-h-[420px]">
|
||
<div className="p-3 sm:p-4 border-b border-gray-50 bg-white flex items-center justify-between relative">
|
||
<div className="flex items-center gap-3">
|
||
<div className="w-1.5 h-6 bg-blue-600 rounded-full"></div>
|
||
<div className="flex flex-col">
|
||
<h2 className="text-lg font-bold text-gray-800 leading-tight">库存统计</h2>
|
||
<span className="text-[10px] text-gray-400 font-medium">实时更新库存数据</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="flex items-center gap-6 ml-auto pr-2">
|
||
<div className="flex flex-col items-end cursor-pointer hover:opacity-80 transition-opacity"
|
||
onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', category: 'Inventory', source: 'asset', title: '库存总数' })}>
|
||
<span className="text-[10px] text-gray-400 font-bold tracking-wider uppercase mb-0.5">总库存数据</span>
|
||
<span className="text-2xl font-black text-gray-900 leading-none">
|
||
{filteredInventoryStats.reduce((acc, s) => acc + s.quantity, 0)}
|
||
<span className="text-xs font-bold text-gray-400 ml-1">台</span>
|
||
</span>
|
||
</div>
|
||
|
||
<div className="relative">
|
||
<button
|
||
onClick={() => { if (!isInventoryFilterOpen) setDraftInventoryFilters({...inventoryFilters}); setIsInventoryFilterOpen(!isInventoryFilterOpen); }}
|
||
className={`flex items-center gap-1.5 px-3 py-1.5 rounded-lg text-xs font-bold transition-all ${
|
||
isInventoryFilterOpen || (inventoryFilters.region || inventoryFilters.city || inventoryFilters.brand || inventoryFilters.type || inventoryFilters.model)
|
||
? 'bg-blue-600 text-white shadow-md'
|
||
: 'bg-gray-100 text-gray-600 hover:bg-gray-200'
|
||
}`}
|
||
>
|
||
<Filter size={14} />
|
||
<span>筛选</span>
|
||
{(inventoryFilters.region || inventoryFilters.city || inventoryFilters.brand || inventoryFilters.type || inventoryFilters.model) && (
|
||
<span className="w-2 h-2 bg-white rounded-full animate-pulse"></span>
|
||
)}
|
||
</button>
|
||
|
||
<AnimatePresence>
|
||
{isInventoryFilterOpen && (
|
||
<>
|
||
<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 }}
|
||
className="fixed inset-x-4 top-20 max-h-[80vh] overflow-auto sm:inset-auto sm:top-20 sm:right-4 sm:w-72 bg-white rounded-xl shadow-2xl border border-slate-100 z-50 p-4"
|
||
>
|
||
<div className="flex justify-between items-center mb-4">
|
||
<h3 className="text-xs font-bold text-slate-800">库存统计 - 数据筛选</h3>
|
||
<button onClick={() => setDraftInventoryFilters({ region: '', city: '', brand: '', type: '', model: '' })} className="text-[10px] text-blue-500 hover:underline">重置所有</button>
|
||
</div>
|
||
|
||
<div className="space-y-3 text-left">
|
||
<div>
|
||
<label className="text-[10px] text-slate-400 block mb-1">区域</label>
|
||
<select value={draftInventoryFilters.region} onChange={(e) => setDraftInventoryFilters({...draftInventoryFilters, region: e.target.value})} className="w-full text-xs bg-white border border-slate-200 rounded-lg px-2 py-1.5 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all shadow-sm cursor-pointer">
|
||
<option value="">全部区域</option>
|
||
{uniqueInventoryRegions.map(r => <option key={r} value={r}>{r}</option>)}
|
||
</select>
|
||
</div>
|
||
<div>
|
||
<label className="text-[10px] text-slate-400 block mb-1">城市</label>
|
||
<select value={draftInventoryFilters.city} onChange={(e) => setDraftInventoryFilters({...draftInventoryFilters, city: e.target.value})} className="w-full text-xs bg-white border border-slate-200 rounded-lg px-2 py-1.5 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all shadow-sm cursor-pointer">
|
||
<option value="">全部城市</option>
|
||
{uniqueInventoryCities.map(c => <option key={c} value={c}>{c}</option>)}
|
||
</select>
|
||
</div>
|
||
<div>
|
||
<label className="text-[10px] text-slate-400 block mb-1">品牌</label>
|
||
<select value={draftInventoryFilters.brand} onChange={(e) => setDraftInventoryFilters({...draftInventoryFilters, brand: e.target.value})} className="w-full text-xs bg-white border border-slate-200 rounded-lg px-2 py-1.5 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all shadow-sm cursor-pointer">
|
||
<option value="">全部品牌</option>
|
||
{uniqueInventoryBrands.map(b => <option key={b} value={b}>{b}</option>)}
|
||
</select>
|
||
</div>
|
||
<div>
|
||
<label className="text-[10px] text-slate-400 block mb-1">车型</label>
|
||
<select value={draftInventoryFilters.type} onChange={(e) => setDraftInventoryFilters({...draftInventoryFilters, type: e.target.value, model: ''})} className="w-full text-xs bg-white border border-slate-200 rounded-lg px-2 py-1.5 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all shadow-sm cursor-pointer">
|
||
<option value="">全部车型</option>
|
||
{uniqueInventoryTypes.map(t => <option key={t} value={t}>{t}</option>)}
|
||
</select>
|
||
</div>
|
||
<div>
|
||
<label className="text-[10px] text-slate-400 block mb-1">批次</label>
|
||
<select value={draftInventoryFilters.model} onChange={(e) => setDraftInventoryFilters({...draftInventoryFilters, model: e.target.value})} className="w-full text-xs bg-white border border-slate-200 rounded-lg px-2 py-1.5 focus:outline-none focus:ring-2 focus:ring-blue-500/20 focus:border-blue-500 transition-all shadow-sm cursor-pointer">
|
||
<option value="">全部批次</option>
|
||
{uniqueInventoryModelsForType.map(m => <option key={m} value={m}>{m}</option>)}
|
||
</select>
|
||
</div>
|
||
</div>
|
||
<button onClick={() => { setInventoryFilters({...draftInventoryFilters}); setIsInventoryFilterOpen(false); }} className="w-full mt-4 py-2 bg-blue-600 text-white rounded-lg text-xs font-bold hover:bg-blue-700 transition-colors">确认</button>
|
||
</motion.div>
|
||
</>
|
||
)}
|
||
</AnimatePresence>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="px-4 py-3 bg-gray-50/50 border-b border-gray-100">
|
||
<div className="flex bg-gray-200/50 p-1 rounded-lg w-fit shadow-inner">
|
||
<button
|
||
onClick={() => setInventoryTab('region')}
|
||
className={`px-6 py-1.5 rounded-md text-xs font-bold transition-all ${
|
||
inventoryTab === 'region' ? 'bg-white text-blue-600 shadow-sm' : 'text-gray-500 hover:text-gray-700'
|
||
}`}
|
||
>
|
||
按区域
|
||
</button>
|
||
<button
|
||
onClick={() => setInventoryTab('model')}
|
||
className={`px-6 py-1.5 rounded-md text-xs font-bold transition-all ${
|
||
inventoryTab === 'model' ? 'bg-white text-blue-600 shadow-sm' : 'text-gray-500 hover:text-gray-700'
|
||
}`}
|
||
>
|
||
按车型
|
||
</button>
|
||
</div>
|
||
</div>
|
||
|
||
{/* Active Filters Bar */}
|
||
{(inventoryFilters.region || inventoryFilters.city || inventoryFilters.brand || inventoryFilters.type || inventoryFilters.model) && (
|
||
<div className="px-4 py-2 border-b border-gray-100 flex flex-wrap gap-2 items-center">
|
||
{inventoryFilters.region && (
|
||
<span className="px-2 py-0.5 bg-blue-50 text-blue-700 rounded text-[10px] flex items-center gap-1">
|
||
区域: {inventoryFilters.region}
|
||
<button onClick={() => setInventoryFilters({...inventoryFilters, region: ''})} className="hover:text-red-500 ml-0.5">×</button>
|
||
</span>
|
||
)}
|
||
{inventoryFilters.city && (
|
||
<span className="px-2 py-0.5 bg-blue-50 text-blue-700 rounded text-[10px] flex items-center gap-1">
|
||
城市: {inventoryFilters.city}
|
||
<button onClick={() => setInventoryFilters({...inventoryFilters, city: ''})} className="hover:text-red-500 ml-0.5">×</button>
|
||
</span>
|
||
)}
|
||
{inventoryFilters.brand && (
|
||
<span className="px-2 py-0.5 bg-blue-50 text-blue-700 rounded text-[10px] flex items-center gap-1">
|
||
品牌: {inventoryFilters.brand}
|
||
<button onClick={() => setInventoryFilters({...inventoryFilters, brand: ''})} className="hover:text-red-500 ml-0.5">×</button>
|
||
</span>
|
||
)}
|
||
{inventoryFilters.type && (
|
||
<span className="px-2 py-0.5 bg-blue-50 text-blue-700 rounded text-[10px] flex items-center gap-1">
|
||
车型: {inventoryFilters.type}
|
||
<button onClick={() => setInventoryFilters({...inventoryFilters, type: '', model: ''})} className="hover:text-red-500 ml-0.5">×</button>
|
||
</span>
|
||
)}
|
||
{inventoryFilters.model && (
|
||
<span className="px-2 py-0.5 bg-blue-50 text-blue-700 rounded text-[10px] flex items-center gap-1">
|
||
批次: {inventoryFilters.model}
|
||
<button onClick={() => setInventoryFilters({...inventoryFilters, model: ''})} className="hover:text-red-500 ml-0.5">×</button>
|
||
</span>
|
||
)}
|
||
<button onClick={() => setInventoryFilters({ region: '', city: '', brand: '', type: '', model: '' })} className="text-[11px] text-red-500 font-bold ml-auto hover:text-red-600">清除</button>
|
||
</div>
|
||
)}
|
||
|
||
{/* Desktop View Table */}
|
||
<InventoryDesktopTable {...listProps} />
|
||
|
||
{/* Mobile View */}
|
||
<InventoryMobileList {...listProps} />
|
||
</div>
|
||
);
|
||
}
|