diff --git a/src/App.tsx b/src/App.tsx
index f231d90..691ac95 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -1357,17 +1357,25 @@ export default function App() {
总运营车辆
{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}%
+
@@ -1453,7 +1461,9 @@ export default function App() {
{dept.department}
- -
+
+ {dept.attendanceRate}%
+
|
|
- - |
- - |
+
+
+ |
+
+
+ |
{isExpanded ? : }
|
@@ -1651,8 +1665,8 @@ export default function App() {
>
{dept.department}
-
- 出勤率: -
+
+ 出勤率: {dept.attendanceRate}%
@@ -1661,13 +1675,15 @@ export default function App() {
总运营
{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}
diff --git a/src/server/routes/vehicles.ts b/src/server/routes/vehicles.ts
index 36a3a00..61ac0fd 100644
--- a/src/server/routes/vehicles.ts
+++ b/src/server/routes/vehicles.ts
@@ -652,35 +652,16 @@ app.get('/dept-stats', async (c) => {
const vehicles = await getVehicles();
const withManager = vehicles.filter((v) => v.status === 'Operating');
- // Query mileage data: last 30 days attendance & avg mileage per plate
- // + today's mileage for idle detection
- const [[mileageRows], [todayRows]] = await Promise.all([
- pool.query
(`
- SELECT plateNumber,
- COUNT(CASE WHEN dayMileage > 0 THEN 1 END) AS activeDays,
- COUNT(*) AS totalDays,
- AVG(dayMileage) AS avgMileage
- FROM ln_vehicle_day_mileage
- WHERE dates >= DATE_SUB(CURDATE(), INTERVAL 30 DAY)
- GROUP BY plateNumber
- `),
- pool.query(`
- SELECT plateNumber, dayMileage
- FROM ln_vehicle_day_mileage
- WHERE dates = CURDATE()
- `),
- ]);
- const mileageMap = new Map();
- for (const row of mileageRows as any[]) {
- mileageMap.set(row.plateNumber, {
- activeDays: Number(row.activeDays),
- totalDays: Number(row.totalDays),
- avgMileage: Number(row.avgMileage),
- });
- }
+ // Query realtime day_mileage from tab_truck_remote_sync_realtime_info
+ const [realtimeRows] = await pool.query(`
+ SELECT plate_number, day_mileage
+ FROM tab_truck_remote_sync_realtime_info
+ WHERE is_deleted = 0 AND plate_number IS NOT NULL
+ `);
const todayMileageMap = new Map();
- for (const row of todayRows as any[]) {
- todayMileageMap.set(row.plateNumber, Number(row.dayMileage));
+ for (const row of realtimeRows as any[]) {
+ const plate = (row.plate_number || '').trim();
+ if (plate) todayMileageMap.set(plate, Number(row.day_mileage) || 0);
}
const deptMap = new Map>();
@@ -693,15 +674,15 @@ app.get('/dept-stats', async (c) => {
mgrMap.get(mgr)!.push(v);
}
- // Compute attendance (today) & avg mileage (30d) for a set of vehicles
+ // Compute attendance & avg mileage from realtime data
const getMileageStats = (vList: Vehicle[]) => {
const todayActive = vList.filter((v) => (todayMileageMap.get(v.plateNumber) || 0) > 0).length;
let totalMileage = 0;
let count = 0;
for (const v of vList) {
- const m = mileageMap.get(v.plateNumber);
- if (m) {
- totalMileage += m.avgMileage;
+ const m = todayMileageMap.get(v.plateNumber);
+ if (m !== undefined && m > 0) {
+ totalMileage += m;
count++;
}
}
@@ -859,11 +840,11 @@ app.get('/list', async (c) => {
let filtered = vehicles;
- // attendance filter: active = today mileage > 0, idle = today mileage = 0 (only for Operating vehicles)
+ // attendance filter: active = day_mileage > 0, idle = day_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 [realtimeRows] = await pool.query(`SELECT plate_number, day_mileage FROM tab_truck_remote_sync_realtime_info WHERE is_deleted = 0 AND plate_number IS NOT NULL`);
const todayMap = new Map();
- for (const row of todayRows as any[]) todayMap.set(row.plateNumber, Number(row.dayMileage));
+ for (const row of realtimeRows as any[]) todayMap.set((row.plate_number || '').trim(), Number(row.day_mileage) || 0);
filtered = filtered.filter((v) => v.status === 'Operating');
if (attendance === 'active') {
filtered = filtered.filter((v) => (todayMap.get(v.plateNumber) || 0) > 0);