import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react'; import { Truck, Warehouse, Activity, PlusCircle, MinusCircle, History, ChevronDown, ChevronRight, Info, Loader2, Search, Filter, ArrowRightLeft, Users, MapPin, Building2, } from 'lucide-react'; import { motion, AnimatePresence } from 'motion/react'; import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, PieChart, Pie, Cell, LabelList, } from 'recharts'; import type { SummaryData, TypeSummary, VehicleListItem, DeptGroup, RegionGroup, CustomerStats, RegionalInventoryStats } from './types'; import { fetchSummary, fetchByType, fetchVehicleList, fetchWeeklyDetail, fetchDeptStats, fetchRegionStats, fetchCustomerStats, fetchInventoryStats, fetchRegionChart } from './api'; import type { WeeklyDetailItem } from './api'; // --- SearchSelect Component --- function SearchSelect({ value, onChange, options, placeholder, className }: { value: string; onChange: (v: string) => void; options: string[]; placeholder: string; className?: string; }) { const [open, setOpen] = useState(false); const [query, setQuery] = useState(''); const ref = useRef(null); useEffect(() => { const handler = (e: MouseEvent) => { if (ref.current && !ref.current.contains(e.target as Node)) setOpen(false); }; document.addEventListener('mousedown', handler); return () => document.removeEventListener('mousedown', handler); }, []); const filtered = useMemo(() => { if (!query) return options; const q = query.toLowerCase(); return options.filter((o) => o.toLowerCase().includes(q)); }, [options, query]); const displayValue = value || ''; return (
setOpen(!open)} > { setQuery(e.target.value); if (!open) setOpen(true); }} onFocus={() => { setOpen(true); setQuery(''); }} />
{open && (
{ onChange(''); setQuery(''); setOpen(false); }} > {placeholder}
{filtered.map((o) => (
{ onChange(o); setQuery(''); setOpen(false); }} > {o}
))} {filtered.length === 0 && (
无匹配项
)}
)}
); } // --- Constants --- const TABS = [ { id: 'overview', label: '总览' }, { id: 'department', label: '按部门' }, { id: 'region', label: '按区域' }, { id: 'customer', label: '按客户' }, ]; export default function App() { const [activeTab, setActiveTab] = useState<'overview' | 'department' | 'region' | 'customer'>('overview'); const [tabReady, setTabReady] = useState(true); const prevTabRef = useRef(activeTab); useEffect(() => { if (prevTabRef.current !== activeTab) { setTabReady(false); prevTabRef.current = activeTab; const id = requestAnimationFrame(() => { setTabReady(true); }); return () => cancelAnimationFrame(id); } }, [activeTab]); const [theme, setTheme] = useState<'soft' | 'minimal' | 'vibrant'>('soft'); const [expandedModels, setExpandedModels] = useState>(new Set()); const [expandedAssetTypes, setExpandedAssetTypes] = useState>(new Set()); const [showPlateNumbers, setShowPlateNumbers] = useState<{ batch: string; model: string; location: string; category?: 'Inventory' | 'Pending' | 'Delivered' | 'Returned' | 'Replaced' | 'Operating'; vehicleType?: string; manager?: string; customer?: string; department?: string; attendance?: 'active' | 'idle'; isColdChain?: boolean; isTrailer?: boolean; type?: string; source?: string; title?: string; } | null>(null); // Data state const [summary, setSummary] = useState(null); const [processedData, setProcessedData] = useState([]); const [modalVehicles, setModalVehicles] = useState([]); const [modalWeeklyDetail, setModalWeeklyDetail] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [lastUpdate, setLastUpdate] = useState(''); const [modalLoading, setModalLoading] = useState(false); // Dept/Region/Customer data const [deptData, setDeptData] = useState([]); const [regionData, setRegionData] = useState([]); const [customerData, setCustomerData] = useState([]); // Dept section state const [deptViewMode, setDeptViewMode] = useState<'department' | 'manager'>('department'); const [expandedDepts, setExpandedDepts] = useState>(new Set()); const [expandedManagerDetails, setExpandedManagerDetails] = useState>(new Set()); const [selectedManager, setSelectedManager] = useState('All'); // Region section state const [expandedRegions, setExpandedRegions] = useState>(new Set()); const [expandedRegionCities, setExpandedRegionCities] = useState>(new Set()); const [regionFilters, setRegionFilters] = useState({ region: '', city: '', customer: '' }); const [isRegionFilterOpen, setIsRegionFilterOpen] = useState(false); const [draftRegionFilters, setDraftRegionFilters] = useState({ region: '', city: '', customer: '' }); // Customer section state const [expandedCustomers, setExpandedCustomers] = useState>(new Set()); const [customerFilters, setCustomerFilters] = useState({ customer: '', brand: '', department: '', manager: '', region: '' }); const [isCustomerFilterOpen, setIsCustomerFilterOpen] = useState(false); const [draftCustomerFilters, setDraftCustomerFilters] = useState({ customer: '', brand: '', department: '', manager: '', region: '' }); // Inventory statistics section state const [inventoryData, setInventoryData] = useState([]); const [inventoryTab, setInventoryTab] = useState<'region' | 'model'>('region'); const [expandedInventoryRegions, setExpandedInventoryRegions] = useState>(new Set()); const [expandedInventoryTypes, setExpandedInventoryTypes] = useState>(new Set(['4.5T普货'])); const [inventoryFilters, setInventoryFilters] = useState({ region: '', city: '', brand: '', type: '', model: '' }); const [isInventoryFilterOpen, setIsInventoryFilterOpen] = useState(false); const [draftInventoryFilters, setDraftInventoryFilters] = useState({ region: '', city: '', brand: '', type: '', model: '' }); // Chart view states const [customerChartView, setCustomerChartView] = useState<'region' | 'province'>('region'); const [regionChartView, setRegionChartView] = useState<'region' | 'province'>('region'); const [regionChartData, setRegionChartData] = useState<{ name: string; value: number }[]>([]); // Modal filter state const [modalFilters, setModalFilters] = useState({ plateNumber: '', model: '', brand: '', location: '' }); const [isModalFilterExpanded, setIsModalFilterExpanded] = useState(false); // Reset modal filters when modal opens useEffect(() => { if (showPlateNumbers) { setModalFilters({ plateNumber: '', model: '', brand: '', location: '' }); } }, [showPlateNumbers]); const loadData = useCallback(async () => { try { setLoading(true); setError(null); const [s, byType, dept, region, cust, inv] = await Promise.all([ fetchSummary(), fetchByType(), fetchDeptStats(), fetchRegionStats(), fetchCustomerStats(), fetchInventoryStats(), ]); setSummary(s); setProcessedData(byType); setDeptData(dept); setRegionData(region); setCustomerData(cust); setInventoryData(inv); setLastUpdate(new Date().toLocaleString('zh-CN')); } catch (e) { setError(e instanceof Error ? e.message : '数据加载失败'); } finally { setLoading(false); } }, []); useEffect(() => { loadData(); const interval = setInterval(loadData, 60 * 1000); return () => clearInterval(interval); }, [loadData]); // Re-fetch region data when filters change useEffect(() => { const hasFilter = regionFilters.customer || regionFilters.city || regionFilters.region; if (hasFilter) { fetchRegionStats({ customer: regionFilters.customer || undefined, city: regionFilters.city || undefined, region: regionFilters.region || undefined }) .then(setRegionData).catch(() => {}); } else { // No filters: use data from the main loadData cycle fetchRegionStats().then(setRegionData).catch(() => {}); } }, [regionFilters]); // Fetch region chart data when view changes useEffect(() => { fetchRegionChart(regionChartView, regionChartView === 'province' ? 5 : 8).then(setRegionChartData).catch(() => setRegionChartData([])); }, [regionChartView]); // Load modal vehicles useEffect(() => { if (!showPlateNumbers) { setModalVehicles([]); setModalWeeklyDetail([]); return; } setModalLoading(true); const cat = showPlateNumbers.category; // Weekly categories use the dedicated weekly-detail endpoint const weeklyTypes: Record = { Delivered: 'delivered', Returned: 'returned', Replaced: 'replaced', Pending: 'pending' }; if (cat && weeklyTypes[cat]) { setModalVehicles([]); fetchWeeklyDetail(weeklyTypes[cat]) .then(setModalWeeklyDetail) .catch(() => setModalWeeklyDetail([])) .finally(() => setModalLoading(false)); return; } // Normal vehicle list setModalWeeklyDetail([]); const params: Record = {}; if (showPlateNumbers.vehicleType) params.vehicleType = showPlateNumbers.vehicleType; if (showPlateNumbers.batch !== 'All') params.batch = showPlateNumbers.batch; if (showPlateNumbers.model !== 'All') params.model = showPlateNumbers.model; if (showPlateNumbers.location !== 'All') params.location = showPlateNumbers.location; if (cat === 'Inventory') params.category = 'Inventory'; if (cat === 'Operating') params.category = 'Operating'; if (showPlateNumbers.manager) params.manager = showPlateNumbers.manager; if (showPlateNumbers.customer) params.customer = showPlateNumbers.customer; if (showPlateNumbers.department) params.department = showPlateNumbers.department; if (showPlateNumbers.attendance) params.attendance = showPlateNumbers.attendance; if (!showPlateNumbers.type) { if (showPlateNumbers.isColdChain !== undefined) params.isColdChain = String(showPlateNumbers.isColdChain); if (showPlateNumbers.isTrailer !== undefined) params.isTrailer = String(showPlateNumbers.isTrailer); } // Map type field to backend vehicleType if (showPlateNumbers.type) { const t = showPlateNumbers.type; if (t === '4.5T') { if (showPlateNumbers.isColdChain === true) params.vehicleType = '4.5T冷链'; else if (showPlateNumbers.isColdChain === false) params.vehicleType = '4.5T普货'; } else if (t === '4.5T普货' || t === '4.5T冷链' || t === '18T' || t === '49T' || t === '挂车' || t === '其他') { params.vehicleType = t; } else if (t === '其他车型') { if (showPlateNumbers.isTrailer === true) params.isTrailer = 'true'; else if (showPlateNumbers.isTrailer === false) params.vehicleType = '其他'; } } fetchVehicleList(params) .then(setModalVehicles) .catch(() => setModalVehicles([])) .finally(() => setModalLoading(false)); }, [showPlateNumbers]); const allTypesExpanded = processedData.length > 0 && processedData.every((t) => expandedAssetTypes.has(t.type)); const toggleAllAssetTypes = () => { if (allTypesExpanded) { setExpandedAssetTypes(new Set()); } else { setExpandedAssetTypes(new Set(processedData.map((t) => t.type))); } }; const toggleAssetType = (type: string) => { const newSet = new Set(expandedAssetTypes); if (newSet.has(type)) newSet.delete(type); else newSet.add(type); setExpandedAssetTypes(newSet); }; const toggleModel = (model: string) => { const newSet = new Set(expandedModels); if (newSet.has(model)) newSet.delete(model); else newSet.add(model); setExpandedModels(newSet); }; const toggleDept = (dept: string) => { const newSet = new Set(expandedDepts); if (newSet.has(dept)) newSet.delete(dept); else newSet.add(dept); setExpandedDepts(newSet); }; const toggleManagerDetails = (manager: string) => { const newSet = new Set(expandedManagerDetails); if (newSet.has(manager)) newSet.delete(manager); else newSet.add(manager); setExpandedManagerDetails(newSet); }; const toggleRegion = (region: string) => { const newSet = new Set(expandedRegions); if (newSet.has(region)) newSet.delete(region); else newSet.add(region); setExpandedRegions(newSet); }; const toggleRegionCity = (key: string) => { const newSet = new Set(expandedRegionCities); if (newSet.has(key)) newSet.delete(key); else newSet.add(key); setExpandedRegionCities(newSet); }; const toggleCustomer = (customer: string) => { const newSet = new Set(expandedCustomers); if (newSet.has(customer)) newSet.delete(customer); else newSet.add(customer); setExpandedCustomers(newSet); }; const toggleInventoryRegion = (region: string) => { const newSet = new Set(expandedInventoryRegions); if (newSet.has(region)) newSet.delete(region); else newSet.add(region); setExpandedInventoryRegions(newSet); }; const toggleInventoryType = (type: string) => { const newSet = new Set(expandedInventoryTypes); if (newSet.has(type)) newSet.delete(type); else newSet.add(type); setExpandedInventoryTypes(newSet); }; // Derived data for inventory section const filteredInventoryStats = useMemo(() => inventoryData.filter((s) => { const mr = !inventoryFilters.region || s.region === inventoryFilters.region; const mc = !inventoryFilters.city || s.city === inventoryFilters.city; const mb = !inventoryFilters.brand || s.brand === inventoryFilters.brand; const mt = !inventoryFilters.type || s.type === inventoryFilters.type; const mm = !inventoryFilters.model || s.model === inventoryFilters.model; return mr && mc && mb && mt && mm; }), [inventoryData, inventoryFilters]); const uniqueInventoryBrands = useMemo(() => Array.from(new Set(inventoryData.map((s) => s.brand).filter(Boolean))), [inventoryData]); const uniqueInventoryRegions = useMemo(() => Array.from(new Set(inventoryData.map((s) => s.region))), [inventoryData]); const uniqueInventoryCities = useMemo(() => Array.from(new Set(inventoryData.map((s) => s.city).filter(Boolean))), [inventoryData]); const INVENTORY_TYPE_ORDER_LIST = ['4.5T普货', '4.5T冷链', '18T', '49T', '挂车', '其他']; const uniqueInventoryTypes = useMemo(() => { const types = Array.from(new Set(inventoryData.map((s) => s.type).filter(Boolean))); return types.sort((a, b) => INVENTORY_TYPE_ORDER_LIST.indexOf(a) - INVENTORY_TYPE_ORDER_LIST.indexOf(b)); }, [inventoryData]); const uniqueInventoryModelsForType = useMemo(() => { const typeFilter = isInventoryFilterOpen ? draftInventoryFilters.type : inventoryFilters.type; const source = typeFilter ? inventoryData.filter((s) => s.type === typeFilter) : inventoryData; return Array.from(new Set(source.map((s) => s.model).filter(Boolean))); }, [inventoryData, inventoryFilters.type, draftInventoryFilters.type, isInventoryFilterOpen]); const inventoryByRegion = useMemo(() => { const result: Record> = {}; for (const s of filteredInventoryStats) { if (!result[s.region]) result[s.region] = {}; if (!result[s.region][s.city]) result[s.region][s.city] = []; result[s.region][s.city].push(s); } return result; }, [filteredInventoryStats]); const inventoryByModel = useMemo(() => { const INVENTORY_TYPE_ORDER = ['4.5T普货', '4.5T冷链', '18T', '49T', '挂车', '其他']; const raw: Record> = {}; for (const s of filteredInventoryStats) { if (!raw[s.type]) raw[s.type] = {}; if (!raw[s.type][s.model]) raw[s.type][s.model] = []; raw[s.type][s.model].push(s); } const result: Record> = {}; for (const t of INVENTORY_TYPE_ORDER) { if (raw[t]) result[t] = raw[t]; } for (const t of Object.keys(raw)) { if (!result[t]) result[t] = raw[t]; } return result; }, [filteredInventoryStats]); // Derived data for dept section const allManagersList = useMemo(() => deptData.flatMap((d) => d.managers.map((m) => m.manager)).filter((v, i, a) => a.indexOf(v) === i).sort(), [deptData]); const managersGroupedByDept = useMemo(() => deptData.map((d) => ({ department: d.department, managers: d.managers.map((m) => m.manager) })), [deptData]); const managerStats = useMemo(() => deptData .flatMap((d) => d.managers) .filter((m) => selectedManager === 'All' || m.manager === selectedManager) .sort((a, b) => b.total - a.total), [deptData, selectedManager]); // Derived data for customer section const filteredCustomerStats = useMemo(() => customerData.filter((s) => { const mc = !customerFilters.customer || s.customer === customerFilters.customer; const mb = !customerFilters.brand || s.brand === customerFilters.brand; const md = !customerFilters.department || s.department === customerFilters.department; const mm = !customerFilters.manager || s.manager === customerFilters.manager; const mr = !customerFilters.region || s.region === customerFilters.region; return mc && mb && md && mm && mr; }), [customerData, customerFilters]); const uniqueBrands = useMemo(() => Array.from(new Set(customerData.map((s) => s.brand).filter(Boolean))), [customerData]); const uniqueDepts = useMemo(() => { const numMap: Record = { '一': 1, '二': 2, '三': 3, '四': 4, '五': 5, '六': 6, '七': 7, '八': 8, '九': 9, '十': 10 }; const getOrder = (name: string) => { const m = name.match(/[一二三四五六七八九十]/); return m ? (numMap[m[0]] || 99) : 99; }; return Array.from(new Set(customerData.map((s) => s.department).filter(Boolean))).sort((a, b) => getOrder(a) - getOrder(b)); }, [customerData]); const uniqueRegions = useMemo(() => Array.from(new Set(customerData.map((s) => s.region))), [customerData]); const uniqueCities = useMemo(() => Array.from(new Set(customerData.map((s) => s.city).filter(Boolean))), [customerData]); const uniqueCustomerNames = useMemo(() => Array.from(new Set(customerData.map((s) => s.customer).filter(Boolean))), [customerData]); const uniqueCustomerManagers = useMemo(() => Array.from(new Set(customerData.map((s) => s.manager).filter(Boolean))), [customerData]); const customerManagersGroupedByDept = useMemo(() => { const numMap: Record = { '一': 1, '二': 2, '三': 3, '四': 4, '五': 5, '六': 6, '七': 7, '八': 8, '九': 9, '十': 10 }; const getDeptOrder = (name: string) => { if (name === '公务车') return 100; const m = name.match(/[一二三四五六七八九十]/); return m ? (numMap[m[0]] || 99) : 99; }; // Use deptData order as primary source (already sorted), fallback to customerData const deptMap = new Map>(); // First add from deptData to get correct dept order and all managers for (const d of deptData) { if (!deptMap.has(d.department)) deptMap.set(d.department, new Set()); for (const m of d.managers) deptMap.get(d.department)!.add(m.manager); } // Then add any extra from customerData for (const s of customerData) { if (!s.manager || !s.department) continue; if (!deptMap.has(s.department)) deptMap.set(s.department, new Set()); deptMap.get(s.department)!.add(s.manager); } return Array.from(deptMap.entries()) .sort((a, b) => getDeptOrder(a[0]) - getDeptOrder(b[0])) .map(([dept, mgrs]) => ({ department: dept, managers: Array.from(mgrs) })); }, [customerData, deptData]); const uniqueInventoryModels = useMemo(() => Array.from(new Set(inventoryData.map((s) => s.model).filter(Boolean))), [inventoryData]); const uniqueModalPlates = useMemo(() => Array.from(new Set(modalVehicles.map(v => v.plateNumber || v.vin).filter(Boolean))), [modalVehicles]); const uniqueModalModels = useMemo(() => Array.from(new Set(modalVehicles.map(v => v.model).filter(Boolean))), [modalVehicles]); const uniqueModalBrands = useMemo(() => Array.from(new Set(modalVehicles.map(v => v.brandLabel).filter((x): x is string => Boolean(x)))), [modalVehicles]); const uniqueModalLocations = useMemo(() => Array.from(new Set(modalVehicles.map(v => v.location).filter(Boolean))), [modalVehicles]); // Derived data for region section // regionData is already filtered by backend based on regionFilters // Filtered modal vehicles based on modal filters const filteredModalVehicles = useMemo(() => modalVehicles.filter((v) => { const mp = !modalFilters.plateNumber || (v.plateNumber || v.vin) === modalFilters.plateNumber; const mm = !modalFilters.model || v.model === modalFilters.model; const mb = !modalFilters.brand || v.brandLabel === modalFilters.brand; const ml = !modalFilters.location || v.location === modalFilters.location; return mp && mm && mb && ml; }), [modalVehicles, modalFilters]); const filteredModalWeeklyDetail = useMemo(() => modalWeeklyDetail.filter((v) => { const mp = !modalFilters.plateNumber || v.plate_number === modalFilters.plateNumber; return mp; }), [modalWeeklyDetail, modalFilters.plateNumber]); const [customerProvinceData, setCustomerProvinceData] = useState<{ name: string; value: number }[]>([]); useEffect(() => { if (customerChartView === 'province') { fetchRegionChart('province', 5).then(setCustomerProvinceData).catch(() => setCustomerProvinceData([])); } }, [customerChartView]); const customerPieData = useMemo(() => { if (customerChartView === 'region') { const map: Record = {}; customerData.forEach(item => { map[item.region] = (map[item.region] || 0) + item.total; }); return Object.entries(map).map(([name, value]) => ({ name, value })).sort((a, b) => b.value - a.value); } else { return customerProvinceData; } }, [customerData, customerChartView, customerProvinceData]); const watermarkText = useMemo(() => `羚牛氢能-${new Date().toLocaleString('zh-CN', { year: 'numeric', month: '2-digit', day: '2-digit', hour: '2-digit', minute: '2-digit', second: '2-digit', hour12: false }).replace(/\//g, '-')}`, [lastUpdate]); if (loading && !summary) { return (
正在加载数据...
); } if (error && !summary) { return (
加载失败
{error}
); } const SUMMARY = summary!; return (
{/* Watermark */}
${watermarkText}`)}")`, backgroundRepeat: 'repeat', }} />
{/* Compact Header Bar */}
{/* Title row */}

羚牛氢能车辆资产

{/* Right: status + theme */}
{lastUpdate}
{loading && (
)}
{(['soft','minimal','vibrant'] as const).map((t) => ( ))}
{/* Tab row */}
{TABS.map(tab => ( ))}
{/* Status row */}
最后更新: {lastUpdate}
每分钟更新一次
{/* Main Content Area */}
{!tabReady && (
)} {tabReady && activeTab === 'overview' && ( <> {/* Header Summary - Ultra Compact */}
{/* Total Assets */}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', source: 'asset', title: '资产概览' })}>
资产总数
{SUMMARY.totalAssets.toLocaleString()}
{/* Operating */}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', category: 'Operating', source: 'asset', title: '正在运营' })}>
总运营
{SUMMARY.operating.total} 自{SUMMARY.operating.self} 租{SUMMARY.operating.leased}{SUMMARY.operating.hanging > 0 && ` 挂${SUMMARY.operating.hanging}`}
{/* Inventory */}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', category: 'Inventory', source: 'asset', title: '库存总数' })}>
总库存
{SUMMARY.inventory.total} 库{SUMMARY.inventory.inStock} 异{SUMMARY.inventory.abnormal}
{/* Pending */}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', category: 'Pending', source: 'asset', title: '待交车' })}>
待交车
{SUMMARY.pendingDelivery}
{/* Dynamics */}
本周动态
上周六-本周五
{SUMMARY.weeklyNew} 新增
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', category: 'Delivered', source: 'asset', title: '本周交车' })}> {SUMMARY.weeklyDelivered} 交车
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', category: 'Returned', source: 'asset', title: '本周还车' })}> {SUMMARY.weeklyReturned} 还车
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', category: 'Replaced', source: 'asset', title: '本周替换' })}> {SUMMARY.weeklyReplaced} 替换
{/* Asset Summary Table */}

资产数据实时汇总

点击型号展开详情
{/* Desktop View Table */}
{processedData.map((typeGroup) => ( {/* Category Header Row */} toggleAssetType(typeGroup.type)}> {expandedAssetTypes.has(typeGroup.type) && typeGroup.models.map((model) => ( toggleModel(model.model)} > {['嘉兴', '广东', '北京', '新疆', '其他'].map(reg => ( ))} ))} ))}
车型 品牌型号 车辆总资产 库存总数 库存-江浙沪 库存-广东 库存-北京 库存-新疆 库存-其他 待交车 当前在运营 本周交车 本周还车 本周替换
{expandedAssetTypes.has(typeGroup.type) ? : } {typeGroup.type}
资产 {typeGroup.totalAssets} 库存 {typeGroup.totalInventory} 运营 {typeGroup.totalOperating}
{typeGroup.type}
{expandedModels.has(model.model) ? : } {model.model}
资产 {model.total} 库存 {model.inventory} 运营 {model.operating}
{model.inventoryRegions[reg] > 0 ? ( ) : ''} {model.pending > 0 ? ( ) : model.pending} {model.weeklyDelivered > 0 ? ( ) : model.weeklyDelivered} {model.weeklyReturned > 0 ? ( ) : model.weeklyReturned} {model.weeklyReplaced > 0 ? ( ) : model.weeklyReplaced}
{/* Mobile View Cards for Asset Summary */}
{processedData.map((typeGroup) => (
toggleAssetType(typeGroup.type)} >
{expandedAssetTypes.has(typeGroup.type) ? : } {typeGroup.type}
资产 {typeGroup.totalAssets} 库存 {typeGroup.totalInventory} 运营 {typeGroup.totalOperating}
{expandedAssetTypes.has(typeGroup.type) && ( {typeGroup.models.map((model) => (
toggleModel(model.model)} >
{expandedModels.has(model.model) ? : } {model.model}
{ e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: model.model, location: 'All', source: 'asset', title: model.model }); }}>资产 {model.total} { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: model.model, location: 'All', category: 'Inventory', source: 'asset', title: `${model.model} - 库存` }); }}>库存 {model.inventory} { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: model.model, location: 'All', category: 'Operating', source: 'asset', title: `${model.model} - 在运营` }); }}>运营 {model.operating}
{expandedModels.has(model.model) && (
setShowPlateNumbers({ batch: 'All', model: model.model, location: 'All', category: 'Inventory', source: 'asset', title: `${model.model} - 库存` })}> 总库存 {model.inventory}
setShowPlateNumbers({ batch: 'All', model: model.model, location: 'All', category: 'Pending', source: 'asset', title: `${model.model} - 待交车` })}> 待交车 {model.pending}
{['嘉兴', '广东', '北京', '新疆', '其他'].map(reg => (
{reg === '嘉兴' ? '浙' : reg === '广东' ? '粤' : reg === '北京' ? '京' : reg === '新疆' ? '新' : '其'}
{model.inventoryRegions[reg] > 0 ? ( ) : (
-
)}
))}
setShowPlateNumbers({ batch: 'All', model: model.model, location: 'All', category: 'Delivered', source: 'asset', title: `${model.model} - 本周交车` })}> 本周已交车 {model.weeklyDelivered}
setShowPlateNumbers({ batch: 'All', model: model.model, location: 'All', category: 'Returned', source: 'asset', title: `${model.model} - 本周还车` })}> 已还车 {model.weeklyReturned}
setShowPlateNumbers({ batch: 'All', model: model.model, location: 'All', category: 'Replaced', source: 'asset', title: `${model.model} - 本周替换` })}> 已替换 {model.weeklyReplaced}
)}
))}
)}
))}
{/* Inventory Statistics */}

库存统计

实时更新库存数据
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', category: 'Inventory', source: 'asset', title: '库存总数' })}> 总库存数据 {filteredInventoryStats.reduce((acc, s) => acc + s.quantity, 0)}
{isInventoryFilterOpen && ( <>

库存统计 - 数据筛选

)}
{/* Active Filters Bar */} {(inventoryFilters.region || inventoryFilters.city || inventoryFilters.brand || inventoryFilters.type || inventoryFilters.model) && (
{inventoryFilters.region && ( 区域: {inventoryFilters.region} )} {inventoryFilters.city && ( 城市: {inventoryFilters.city} )} {inventoryFilters.brand && ( 品牌: {inventoryFilters.brand} )} {inventoryFilters.type && ( 车型: {inventoryFilters.type} )} {inventoryFilters.model && ( 批次: {inventoryFilters.model} )}
)} {/* Desktop View Table */}
{inventoryTab === 'region' ? ( Object.entries(inventoryByRegion).map(([region, cities]) => ( toggleInventoryRegion(region)} > {expandedInventoryRegions.has(region) && Object.entries(cities).map(([city, stats]) => ( {stats.map((stat, idx) => ( ))} ))} )) ) : ( Object.entries(inventoryByModel).map(([type, models]) => ( toggleInventoryType(type)} > {expandedInventoryTypes.has(type) && Object.entries(models).map(([model, stats]) => ( {stats.map((stat, idx) => ( ))} ))} )) )}
{inventoryTab === 'region' ? '区域 / 城市' : '车型分类 / 型号'} {inventoryTab === 'region' ? '品牌' : '品牌'} {inventoryTab === 'region' ? '车型' : '所在区域/城市'} 库存数量
{expandedInventoryRegions.has(region) ? : } {region} { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: region, category: 'Inventory', source: 'asset', title: `库存统计 - ${region}` }); }}> (共 {Object.values(cities).flat().reduce((acc, s) => acc + s.quantity, 0)} 台)
{idx === 0 ? {city} : ''} {stat.brand} {stat.model}
{expandedInventoryTypes.has(type) ? : } {type} { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', type: type, category: 'Inventory', source: 'asset', title: `库存统计 - ${type}` }); }}> (共 {Object.values(models).flat().reduce((acc, s) => acc + s.quantity, 0)} 台)
{idx === 0 ? {model} : ''} {stat.brand} {stat.region} / {stat.city}
{/* Mobile View */}
{inventoryTab === 'region' ? ( Object.entries(inventoryByRegion).map(([region, cities]) => (
toggleInventoryRegion(region)} >
{expandedInventoryRegions.has(region) ? : } {region}
{Object.values(cities).flat().reduce((acc, s) => acc + s.quantity, 0)} 台
{expandedInventoryRegions.has(region) && (
{Object.entries(cities).map(([city, stats]) => (
{city}
{stats.map((stat, idx) => (
{stat.brand} {stat.model}
))}
))}
)}
)) ) : ( Object.entries(inventoryByModel).map(([type, models]) => (
toggleInventoryType(type)} >
{expandedInventoryTypes.has(type) ? : } {type}
{Object.values(models).flat().reduce((acc, s) => acc + s.quantity, 0)} 台
{expandedInventoryTypes.has(type) && (
{Object.entries(models).map(([model, stats]) => (
{model}
{stats.map((stat, idx) => (
{stat.brand} {stat.region} / {stat.city}
))}
))}
)}
)) )}
)} {tabReady && activeTab === 'department' && (
{/* Overall Total Summary (Compact) - Moved to Top */}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', source: 'department', title: '部门运营统计' })}> 总运营车辆 {deptData.reduce((s, d) => s + d.totalAssets, 0)}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', attendance: 'active', source: 'department', title: '部门运营统计 - 出勤车辆' })}> 出勤车辆 {deptData.reduce((acc, d) => acc + d.operatingCount, 0)}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', attendance: 'idle', source: 'department', title: '部门运营统计 - 闲置车辆' })}> 闲置车辆 {deptData.reduce((acc, d) => acc + d.idleCount, 0)}
平均出勤 {deptData.length > 0 ? (deptData.reduce((acc, d) => acc + d.attendanceRate, 0) / deptData.length).toFixed(1) : 0}%
{/* Controls Row: Toggles Left, Filter Right */}
{deptViewMode === 'manager' && (
)}
{/* Desktop Table View */}
{deptViewMode === 'manager' && } {deptViewMode === 'department' && ( <> )} {deptViewMode === 'manager' && ( <> )} {deptViewMode === 'department' ? ( deptData.map((dept) => { const isExpanded = expandedDepts.has(dept.department); return ( toggleDept(dept.department)} > {isExpanded && ( )} ); }) ) : ( managerStats.map((m) => { const isManagerExpanded = expandedManagerDetails.has(m.manager); return ( toggleManagerDetails(m.manager)} > {isManagerExpanded && ( )} ); }) )}
{deptViewMode === 'department' ? '部门名称' : '业务负责人'}所属部门{deptViewMode === 'department' ? '出勤率' : '合计资产'}总运营车辆 出勤车辆 闲置车辆4.5T 冷链 18T 49T 挂车 其他详情
{dept.department} {dept.attendanceRate}% {isExpanded ? : }
{dept.managers.map(m => { const isManagerExpanded = expandedManagerDetails.has(m.manager); return (
toggleManagerDetails(m.manager)} >
{isManagerExpanded ? : } {m.manager}
{isManagerExpanded && (
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '4.5T', isColdChain: false, category: 'Operating', source: 'department', title: `部门运营统计 - ${m.manager} - 4.5T` })} >
4.5T
{m.t4_5}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '4.5T', isColdChain: true, category: 'Operating', source: 'department', title: `部门运营统计 - ${m.manager} - 4.5T冷链` })} >
冷链
{m.t4_5c}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '18T', category: 'Operating', source: 'department', title: `部门运营统计 - ${m.manager} - 18T` })} >
18T
{m.t18}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '49T', category: 'Operating', source: 'department', title: `部门运营统计 - ${m.manager} - 49T` })} >
49T
{m.t49}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '其他车型', isTrailer: true, category: 'Operating', source: 'department', title: `部门运营统计 - ${m.manager} - 挂车` })} >
挂车
{m.trailer}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '其他车型', isTrailer: false, category: 'Operating', source: 'department', title: `部门运营统计 - ${m.manager} - 其他` })} >
其他
{m.other}
)}
); })}
{isManagerExpanded ? : } {m.manager} {m.department} { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, category: 'Operating', source: 'department', title: `部门运营统计 - ${m.manager} - 正在运营` }); }} > {m.total} - - - - - -
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '4.5T', isColdChain: false, category: 'Operating', source: 'department', title: `部门运营统计 - ${m.manager} - 4.5T` })}> 4.5T {m.t4_5}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '4.5T', isColdChain: true, category: 'Operating', source: 'department', title: `部门运营统计 - ${m.manager} - 4.5T冷链` })}> 冷链 {m.t4_5c}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '18T', category: 'Operating', source: 'department', title: `部门运营统计 - ${m.manager} - 18T` })}> 18T {m.t18}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '49T', category: 'Operating', source: 'department', title: `部门运营统计 - ${m.manager} - 49T` })}> 49T {m.t49}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '其他车型', isTrailer: true, category: 'Operating', source: 'department', title: `部门运营统计 - ${m.manager} - 挂车` })}> 挂车 {m.trailer}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '其他车型', isTrailer: false, category: 'Operating', source: 'department', title: `部门运营统计 - ${m.manager} - 其他` })}> 其他 {m.other}
{/* Mobile Card View */}
{deptViewMode === 'department' ? ( deptData.map((dept) => { const isExpanded = expandedDepts.has(dept.department); return (
toggleDept(dept.department)} >

{dept.department}

出勤率: {dept.attendanceRate}%
{ e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', department: dept.department, source: 'department', title: `部门运营统计 - ${dept.department}` }); }}>
总运营
{dept.totalAssets}
{ e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', department: dept.department, attendance: 'active', source: 'department', title: `部门运营统计 - ${dept.department} - 出勤车辆` }); }}>
出勤
{dept.operatingCount}
{ e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', department: dept.department, attendance: 'idle', source: 'department', title: `部门运营统计 - ${dept.department} - 闲置车辆` }); }}>
闲置
{dept.idleCount}
{isExpanded ? : }
{isExpanded && (
{dept.managers.map(m => { const isManagerExpanded = expandedManagerDetails.has(m.manager); return (
toggleManagerDetails(m.manager)} >
{isManagerExpanded ? : } {m.manager}
{isManagerExpanded && (
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '4.5T', isColdChain: false, category: 'Operating', source: 'department', title: `部门运营统计 - ${m.manager} - 4.5T` })} >
4.5T
{m.t4_5}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '4.5T', isColdChain: true, category: 'Operating', source: 'department', title: `部门运营统计 - ${m.manager} - 4.5T冷链` })} >
冷链
{m.t4_5c}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '18T', category: 'Operating', source: 'department', title: `部门运营统计 - ${m.manager} - 18T` })} >
18T
{m.t18}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '49T', category: 'Operating', source: 'department', title: `部门运营统计 - ${m.manager} - 49T` })} >
49T
{m.t49}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '其他车型', isTrailer: true, category: 'Operating', source: 'department', title: `部门运营统计 - ${m.manager} - 挂车` })} >
挂车
{m.trailer}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '其他车型', isTrailer: false, category: 'Operating', source: 'department', title: `部门运营统计 - ${m.manager} - 其他` })} >
其他
{m.other}
)}
); })}
)}
); }) ) : ( managerStats.map((m) => { const isManagerExpanded = expandedManagerDetails.has(m.manager); return (
toggleManagerDetails(m.manager)} >
{isManagerExpanded ? : }

{m.manager}

{m.department}
{ e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, category: 'Operating', source: 'department', title: `部门运营统计 - ${m.manager} - 正在运营` }); }} > 资产: {m.total}
{isManagerExpanded && (
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '4.5T', isColdChain: false, category: 'Operating', source: 'department', title: `部门运营统计 - ${m.manager} - 4.5T` })} >
4.5T
{m.t4_5}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '4.5T', isColdChain: true, category: 'Operating', source: 'department', title: `部门运营统计 - ${m.manager} - 4.5T冷链` })} >
冷链
{m.t4_5c}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '18T', category: 'Operating', source: 'department', title: `部门运营统计 - ${m.manager} - 18T` })} >
18T
{m.t18}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '49T', category: 'Operating', source: 'department', title: `部门运营统计 - ${m.manager} - 49T` })} >
49T
{m.t49}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '其他车型', isTrailer: true, category: 'Operating', source: 'department', title: `部门运营统计 - ${m.manager} - 挂车` })} >
挂车
{m.trailer}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', manager: m.manager, type: '其他车型', isTrailer: false, category: 'Operating', source: 'department', title: `部门运营统计 - ${m.manager} - 其他` })} >
其他
{m.other}
)}
); }) )}
)} {tabReady && activeTab === 'region' && (
{/* Region Distribution Chart */}

区域资产分布概览

{/* Region - Vehicle - Customer Section */}

区域运营统计

*按区域—车型—客户维度统计

{isRegionFilterOpen && ( <>

区域筛选

setDraftRegionFilters(prev => ({ ...prev, customer: v }))} options={uniqueCustomerNames} placeholder="所有客户" className="text-xs py-2 px-2" />
)}
{Object.values(regionFilters).some(v => v !== '') && (
{regionFilters.customer && ( 客户: {regionFilters.customer} )} {regionFilters.region && ( 区域: {regionFilters.region} )} {regionFilters.city && ( 城市: {regionFilters.city} )}
)}
{regionData.map((r) => { const isExpanded = expandedRegions.has(r.region); return ( toggleRegion(r.region)} > {isExpanded && r.cities.map((city) => { const cityKey = `${r.region}-${city.city}`; const isCityExpanded = expandedRegionCities.has(cityKey); return ( toggleRegionCity(cityKey)} > {isCityExpanded && city.typeBreakdown.map(tb => ( ))} ); })} ); })}
区域 / 车型 / 客户 资产总数 运营中 待交车 主要客户
{isExpanded ? : } {r.region}区域 { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: r.region, source: 'region', title: `区域运营统计 - ${r.region}` }); }}>{r.totalAssets} { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: r.region, category: 'Operating', source: 'region', title: `区域运营统计 - ${r.region} - 正在运营` }); }}>{r.operatingCount} { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: r.region, category: 'Pending', source: 'region', title: `区域运营统计 - ${r.region} - 待交车` }); }}>{r.pendingCount} {r.customers.slice(0, 2).join(', ')}
{isCityExpanded ? : } {city.city} { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: city.city, source: 'region', title: `区域运营统计 - ${city.city}` }); }}>{city.totalAssets} { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: city.city, category: 'Operating', source: 'region', title: `区域运营统计 - ${city.city} - 正在运营` }); }}>{city.operatingCount} { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: city.city, category: 'Pending', source: 'region', title: `区域运营统计 - ${city.city} - 待交车` }); }}>{city.pendingCount} {city.customers.slice(0, 2).join(', ')}
{tb.type} 车型
setShowPlateNumbers({ batch: 'All', model: 'All', location: city.city, vehicleType: tb.type, source: 'region', title: `区域运营统计 - ${city.city} - ${tb.type}` })}>{tb.total} setShowPlateNumbers({ batch: 'All', model: 'All', location: city.city, vehicleType: tb.type, category: 'Operating', source: 'region', title: `区域运营统计 - ${city.city} - ${tb.type} - 正在运营` })}>{tb.operating} { setShowPlateNumbers({ batch: 'All', model: 'All', location: city.city, vehicleType: tb.type, category: 'Inventory', source: 'region', title: `区域运营统计 - ${city.city} - ${tb.type} - 库存` }); }}>{tb.inventory} {tb.customers.slice(0, 2).join(', ')}
{/* Mobile View (Region) */}
{regionData.map((r) => { const isExpanded = expandedRegions.has(r.region); return (
toggleRegion(r.region)} >
{isExpanded ? : } {r.region}区域
资产: {r.totalAssets}
{isExpanded && ( <>
setShowPlateNumbers({ batch: 'All', model: 'All', location: r.region, category: 'Operating', source: 'region', title: `区域运营统计 - ${r.region} - 正在运营` })} >
运营中
{r.operatingCount}
setShowPlateNumbers({ batch: 'All', model: 'All', location: r.region, category: 'Pending', source: 'region', title: `区域运营统计 - ${r.region} - 待交车` })} >
待交车
{r.pendingCount}
{r.typeBreakdown.map(tb => (
{tb.type} 车型
setShowPlateNumbers({ batch: 'All', model: 'All', location: r.region, vehicleType: tb.type, category: 'Operating', source: 'region', title: `区域运营统计 - ${r.region} - ${tb.type} - 正在运营` })} > 运:{tb.operating} setShowPlateNumbers({ batch: 'All', model: 'All', location: r.region, vehicleType: tb.type, category: 'Inventory', source: 'region', title: `区域运营统计 - ${r.region} - ${tb.type} - 库存` })} > 待:{tb.inventory}
))}
)}
); })}
)} {tabReady && activeTab === 'customer' && (
{/* Customer Region Distribution Chart */}

客户运营地区占比

{(() => { const PIE_COLORS = ['#6366f1','#06b6d4','#f59e0b','#f43f5e','#10b981','#a855f7','#94a3b8']; const pieData = customerPieData; const grandTotal = pieData.reduce((s,d) => s + d.value, 0); return (
{/* Donut chart */}
{pieData.map((_, i) => ( ))} [`${value} 辆`, '']} contentStyle={{ borderRadius: '10px', border: 'none', boxShadow: '0 8px 24px -4px rgba(0,0,0,0.12)', fontSize: 12 }} /> {/* Center label */}
{grandTotal}
{/* Custom legend */}
{pieData.map((item, i) => { const pct = grandTotal > 0 ? (item.value / grandTotal * 100) : 0; const color = PIE_COLORS[i % PIE_COLORS.length]; return (
{item.name}
{item.value} {pct.toFixed(1)}%
); })}
); })()}
{/* Customer Operations Statistics Section */}

客户运营统计

*按客户维度统计资产分布

{isCustomerFilterOpen && ( <> {/* Backdrop */}
{/* Popover Content */}

数据筛选

setDraftCustomerFilters(prev => ({ ...prev, customer: v }))} options={uniqueCustomerNames} placeholder="所有客户" className="text-xs py-2 px-2" />
)}
{Object.values(customerFilters).some(v => v !== '') && (
{customerFilters.customer && ( 客户: {customerFilters.customer} )} {customerFilters.manager && ( 负责人: {customerFilters.manager} )} {customerFilters.brand && ( 品牌: {customerFilters.brand} )} {customerFilters.department && ( 部门: {customerFilters.department} )} {customerFilters.region && ( 区域: {customerFilters.region} )}
)}
{/* Desktop Table View (Customer) */}
{filteredCustomerStats.map((cust) => { const isExpanded = expandedCustomers.has(cust.customer); return ( toggleCustomer(cust.customer)} > {isExpanded && ( )} ); })}
客户名称 所在区域 关联业务负责人 4.5T 4.5T冷链 18T 49T 挂车 其他 合计
{isExpanded ? : } {cust.customer} {cust.region} {cust.manager} { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '4.5T', isColdChain: false, source: 'customer', title: `客户运营统计 - ${cust.customer} - 4.5T` }); }}>{cust.t4_5} { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '4.5T', isColdChain: true, source: 'customer', title: `客户运营统计 - ${cust.customer} - 4.5T冷链` }); }}>{cust.t4_5c} { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '18T', source: 'customer', title: `客户运营统计 - ${cust.customer} - 18T` }); }}>{cust.t18} { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '49T', source: 'customer', title: `客户运营统计 - ${cust.customer} - 49T` }); }}>{cust.t49} { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '其他车型', source: 'customer', title: `客户运营统计 - ${cust.customer} - 挂车` }); }}>{cust.trailer} { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '其他车型', source: 'customer', title: `客户运营统计 - ${cust.customer} - 其他` }); }}>{cust.other} { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, source: 'customer', title: `客户运营统计 - ${cust.customer}` }); }}>{cust.total}
客户详情
{cust.customer}
主要车型
{cust.t49 > cust.t18 ? '49T 重卡' : (cust.t18 > cust.t4_5c ? '18T 货车' : '4.5T 轻卡')}
业务经理
{cust.manager}
资产占比
{((cust.total / deptData.reduce((s, d) => s + d.totalAssets, 0)) * 100).toFixed(1)}%
{/* Mobile Card View (Customer) */}
{filteredCustomerStats.map((cust) => { const isExpanded = expandedCustomers.has(cust.customer); return (
toggleCustomer(cust.customer)} >
{isExpanded ? : }
{cust.customer} {cust.region}区域
{ e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, source: 'customer', title: `客户运营统计 - ${cust.customer}` }); }} > 合计: {cust.total}
{isExpanded && (
{/* Details Cards for Mobile */}
客户详情
{cust.customer}
主要车型
{cust.t49 > cust.t18 ? '49T 重卡' : (cust.t18 > cust.t4_5c ? '18T 货车' : '4.5T 轻卡')}
业务经理
{cust.manager}
资产占比
{((cust.total / deptData.reduce((s, d) => s + d.totalAssets, 0)) * 100).toFixed(1)}%
车型分布
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '4.5T', isColdChain: false, source: 'customer', title: `客户运营统计 - ${cust.customer} - 4.5T` })} >
4.5T
{cust.t4_5}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '4.5T', isColdChain: true, source: 'customer', title: `客户运营统计 - ${cust.customer} - 4.5T冷链` })} >
冷链
{cust.t4_5c}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '18T', source: 'customer', title: `客户运营统计 - ${cust.customer} - 18T` })} >
18T
{cust.t18}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '49T', source: 'customer', title: `客户运营统计 - ${cust.customer} - 49T` })} >
49T
{cust.t49}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '其他车型', source: 'customer', title: `客户运营统计 - ${cust.customer} - 挂车` })} >
挂车
{cust.trailer}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', customer: cust.customer, type: '其他车型', source: 'customer', title: `客户运营统计 - ${cust.customer} - 其他` })} >
其他
{cust.other}
)}
); })}
)} {/* Vehicle Detail Modal */} {showPlateNumbers && (

{showPlateNumbers.title || ( (showPlateNumbers.manager ? `${showPlateNumbers.manager} 的${showPlateNumbers.type || ''}车辆` : showPlateNumbers.customer ? `${showPlateNumbers.customer} 的${showPlateNumbers.type || ''}车辆` : showPlateNumbers.batch === 'All' ? '全量批次' : `${showPlateNumbers.batch} 批次`) + ' - 运营明细' )}

{showPlateNumbers.model === 'All' ? '全量型号' : showPlateNumbers.model} | {showPlateNumbers.category === 'Pending' ? '待交车' : showPlateNumbers.category === 'Delivered' ? '本周已交车' : showPlateNumbers.category === 'Returned' ? '已还车' : showPlateNumbers.category === 'Replaced' ? '已替换' : showPlateNumbers.category === 'Inventory' ? `${showPlateNumbers.location}库存` : showPlateNumbers.category === 'Operating' ? '正在运营' : '全部状态'}

{/* Modal Filters */}
setIsModalFilterExpanded(!isModalFilterExpanded)} >
筛选条件
{/* Quick Search always visible when collapsed */} {!isModalFilterExpanded && (
e.stopPropagation()}> setModalFilters({...modalFilters, plateNumber: v})} options={uniqueModalPlates} placeholder="快速搜索车牌..." className="text-[11px] py-1 px-2" />
)}
{isModalFilterExpanded && (
setModalFilters({...modalFilters, plateNumber: v})} options={uniqueModalPlates} placeholder="全部车牌" className="text-[11px] py-1.5 px-2" />
)}
{modalLoading ? (
) : modalWeeklyDetail.length > 0 ? (
{filteredModalWeeklyDetail.map((v, i) => ( ))}
车牌 客户名称 日期
{v.plate_number} {v.customer_name || '—'} {v.handover_date || '—'}
) : (
{showPlateNumbers.source === 'customer' ? ( <> ) : ( <> {showPlateNumbers.source !== 'asset' && showPlateNumbers.category !== 'Inventory' && ( )} )} {filteredModalVehicles.map((v, idx) => ( {showPlateNumbers.source === 'customer' ? ( <> ) : ( <> {showPlateNumbers.source !== 'asset' && showPlateNumbers.category !== 'Inventory' && ( )} )} ))} {filteredModalVehicles.length === 0 && ( )}
业务部门 业务负责人 品牌 车型 资产归属 客户名称 车牌 状态 提车时间 合同到期时间 运营区域 离到期 签约公司车牌客户名称品牌 车型 所在地
{v.departmentName || '—'} {v.customerManager || '—'} {v.brandLabel || '—'} {v.type} {v.subjectOrg || '—'} {v.customerName || '—'} {v.plateNumber || v.vin || '—'} {v.status === 'Operating' ? '在租' : v.status === 'Inventory' ? '库存' : '异常'} {'—'} {'—'} {v.location === '其他' ? '对接中' : v.location} {'—'} {v.orgName || '—'}{v.plateNumber || v.vin || '—'}{v.customerName || '—'}{v.brandLabel || '—'} {v.type} {v.location === '其他' ? '对接中' : v.location}
暂无符合条件的车辆数据
)}
共计 {filteredModalWeeklyDetail.length > 0 ? filteredModalWeeklyDetail.length : filteredModalVehicles.length} 台车辆
)}
{/* Footer / Navigation */}
); }