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';