diff --git a/src/App.tsx b/src/App.tsx index 735f430..8e1d9f4 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -57,6 +57,7 @@ export default function App() { manager?: string; customer?: string; department?: string; + attendance?: 'active' | 'idle'; isColdChain?: boolean; isTrailer?: boolean; type?: string; @@ -190,6 +191,7 @@ export default function App() { 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); @@ -1274,14 +1276,14 @@ export default function App() { {deptData.reduce((s, d) => s + d.totalAssets, 0)}
setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', category: 'Operating', source: 'department', title: '部门运营统计 - 出勤车辆' })}> + onClick={() => 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', category: 'Inventory', source: 'department', title: '部门运营统计 - 闲置车辆' })}> + onClick={() => setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', attendance: 'idle', source: 'department', title: '部门运营统计 - 闲置车辆' })}> 闲置车辆 {deptData.reduce((acc, d) => acc + d.idleCount, 0)} @@ -1396,7 +1398,7 @@ export default function App() {
{ e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', department: dept.department, category: 'Operating', source: 'department', title: `部门运营统计 - ${dept.department} - 出勤车辆` }); }}> + onClick={(e) => { 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, category: 'Inventory', source: 'department', title: `部门运营统计 - ${dept.department} - 闲置车辆` }); }}> + onClick={(e) => { e.stopPropagation(); setShowPlateNumbers({ batch: 'All', model: 'All', location: 'All', department: dept.department, attendance: 'idle', source: 'department', title: `部门运营统计 - ${dept.department} - 闲置车辆` }); }}>
闲置
{dept.idleCount}
diff --git a/src/api.ts b/src/api.ts index 4617962..e6e51b6 100644 --- a/src/api.ts +++ b/src/api.ts @@ -36,6 +36,7 @@ export async function fetchVehicleList(params: { isColdChain?: string; isTrailer?: string; department?: string; + attendance?: string; }): Promise { const query = new URLSearchParams(); if (params.batch) query.set('batch', params.batch); @@ -49,6 +50,7 @@ export async function fetchVehicleList(params: { if (params.isColdChain) query.set('isColdChain', params.isColdChain); if (params.isTrailer) query.set('isTrailer', params.isTrailer); if (params.department) query.set('department', params.department); + if (params.attendance) query.set('attendance', params.attendance); return fetchJson(`${BASE}/list?${query.toString()}`); } diff --git a/src/server/routes/vehicles.ts b/src/server/routes/vehicles.ts index c104040..04c9d24 100644 --- a/src/server/routes/vehicles.ts +++ b/src/server/routes/vehicles.ts @@ -858,9 +858,22 @@ const VEHICLE_TYPE_FILTERS: Record boolean> = { // GET /api/vehicles/list — flat list with optional filters app.get('/list', async (c) => { const vehicles = await getVehicles(); - const { batch, model, location, status, category, vehicleType, manager, customer, isColdChain, isTrailer, department } = c.req.query(); + const { batch, model, location, status, category, vehicleType, manager, customer, isColdChain, isTrailer, department, attendance } = c.req.query(); let filtered = vehicles; + + // attendance filter: active = today mileage > 0, idle = today mileage = 0 (only for Operating vehicles) + if (attendance === 'active' || attendance === 'idle') { + const [todayRows] = await pool.query(`SELECT plateNumber, dayMileage FROM ln_vehicle_day_mileage WHERE dates = CURDATE()`); + const todayMap = new Map(); + for (const row of todayRows as any[]) todayMap.set(row.plateNumber, Number(row.dayMileage)); + filtered = filtered.filter((v) => v.status === 'Operating'); + if (attendance === 'active') { + filtered = filtered.filter((v) => (todayMap.get(v.plateNumber) || 0) > 0); + } else { + filtered = filtered.filter((v) => (todayMap.get(v.plateNumber) || 0) === 0); + } + } if (vehicleType) { if (VEHICLE_TYPE_FILTERS[vehicleType]) { filtered = filtered.filter(VEHICLE_TYPE_FILTERS[vehicleType]); @@ -904,7 +917,7 @@ app.get('/list', async (c) => { filtered = filtered.filter((v) => customer === '未分配客户' ? !v.customerName : v.customerName === customer); } if (department) { - filtered = filtered.filter((v) => v.departmentName === department); + filtered = filtered.filter((v) => department === '公务车' ? !v.departmentName : v.departmentName === department); } if (isColdChain !== undefined) { const wantCold = isColdChain === 'true';