feat: 多项优化 - 全屏加载全部数据、无值筛选、刷新按钮、加载动画、负值显示为0
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
All checks were successful
ci/woodpecker/push/woodpecker Pipeline was successful
- 全屏监控一次加载全部车辆数据,支持完整滚动和筛选 - 客户/部门筛选增加"无值"选项筛选空数据 - 全屏刷新按钮实际触发数据重新加载,带旋转动画 - 全屏筛选时显示加载遮罩 - 负值里程前端显示为0 - 未对接车机显示"未对接"替代"-" - 删除"未同步"标签 - 统计报表配色统一为白色主题、KPI联动选中项目 - 统计报表全屏表格列合并优化 - 车辆明细面板增加日期选择、租赁状态/部门/客户信息、里程合计 - 每分钟自动刷新数据 - 清除按钮修复租赁状态重置 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -159,7 +159,7 @@ async function refreshMonitoringCache() {
|
||||
|
||||
// 启动时立即刷新,之后每2分钟刷新
|
||||
refreshMonitoringCache();
|
||||
setInterval(refreshMonitoringCache, 2 * 60 * 1000);
|
||||
setInterval(refreshMonitoringCache, 60 * 1000);
|
||||
|
||||
// 查询指定日期的里程数据(非缓存)
|
||||
async function queryDateMileage(dateStr: string): Promise<{ vehicles: CachedVehicle[] }> {
|
||||
@@ -258,8 +258,8 @@ app.get('/monitoring', async (c) => {
|
||||
(v.project || '').toLowerCase().includes(q)
|
||||
);
|
||||
}
|
||||
if (dept) vehicles = vehicles.filter(v => v.department === dept);
|
||||
if (customer) vehicles = vehicles.filter(v => v.customer === customer);
|
||||
if (dept) vehicles = vehicles.filter(v => dept === '__EMPTY__' ? !v.department : v.department === dept);
|
||||
if (customer) vehicles = vehicles.filter(v => customer === '__EMPTY__' ? !v.customer : v.customer === customer);
|
||||
if (project) vehicles = vehicles.filter(v => v.project === project);
|
||||
if (entity) vehicles = vehicles.filter(v => v.entity === entity);
|
||||
if (rentStatus) vehicles = vehicles.filter(v => v.rentStatus === rentStatus);
|
||||
@@ -398,10 +398,12 @@ app.get('/targets', async (c) => {
|
||||
}
|
||||
});
|
||||
|
||||
// GET /target/:id/vehicles — 某项目的车辆明细
|
||||
// GET /target/:id/vehicles — 某项目的车辆明细(支持 ?date= 查询指定日期里程)
|
||||
app.get('/target/:id/vehicles', async (c) => {
|
||||
const targetId = c.req.param('id');
|
||||
const date = c.req.query('date') || '';
|
||||
try {
|
||||
// 获取考核车辆基本信息
|
||||
const [rows] = await pool.execute(
|
||||
`SELECT plate_number, today_mileage, vehicle_total_mileage,
|
||||
completion_rate, is_qualified, current_year_is_qualified,
|
||||
@@ -412,15 +414,58 @@ app.get('/target/:id/vehicles', async (c) => {
|
||||
[targetId]
|
||||
) as any;
|
||||
|
||||
const result = rows.map((r: any) => ({
|
||||
plateNumber: r.plate_number,
|
||||
todayMileage: Number(r.today_mileage) || 0,
|
||||
totalMileage: Number(r.vehicle_total_mileage) || 0,
|
||||
completionRate: Number(r.completion_rate) || 0,
|
||||
isQualified: r.is_qualified === 1,
|
||||
currentYearIsQualified: r.current_year_is_qualified === 1,
|
||||
dailyRequiredMileage: Number(r.daily_required_mileage) || 0,
|
||||
}));
|
||||
// 获取车辆关联信息(租赁状态、部门、客户)
|
||||
const plates = rows.map((r: any) => r.plate_number);
|
||||
const infoMap = new Map<string, any>();
|
||||
if (plates.length > 0) {
|
||||
const [infoRows] = await pool.execute(
|
||||
`${VEHICLE_INFO_SQL} AND truck.plate_number IN (${plates.map(() => '?').join(',')})`,
|
||||
plates
|
||||
) as any;
|
||||
for (const row of infoRows) {
|
||||
infoMap.set(row.plate, row);
|
||||
}
|
||||
}
|
||||
|
||||
// 如果指定了日期,从里程数据库查询该日期的里程
|
||||
const dateMileageMap = new Map<string, { dailyKm: number; totalKm: number | null; isOnline: boolean }>();
|
||||
if (date && plates.length > 0) {
|
||||
const [mileageRows] = await mileagePool.execute(
|
||||
`SELECT plate, daily_km, total_km, source FROM v_vehicle_daily_stats
|
||||
WHERE stat_date = ? AND plate IN (${plates.map(() => '?').join(',')})`,
|
||||
[date, ...plates]
|
||||
) as any;
|
||||
for (const m of mileageRows) {
|
||||
const existing = dateMileageMap.get(m.plate);
|
||||
const dailyKm = Number(m.daily_km) || 0;
|
||||
if (!existing || dailyKm > existing.dailyKm) {
|
||||
const source = m.source || 'NONE';
|
||||
dateMileageMap.set(m.plate, {
|
||||
dailyKm,
|
||||
totalKm: m.total_km !== null ? Number(m.total_km) : null,
|
||||
isOnline: source !== 'NONE' && dailyKm > 0,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = rows.map((r: any) => {
|
||||
const info = infoMap.get(r.plate_number);
|
||||
const dateMileage = date ? dateMileageMap.get(r.plate_number) : null;
|
||||
return {
|
||||
plateNumber: r.plate_number,
|
||||
todayMileage: dateMileage ? dateMileage.dailyKm : (Number(r.today_mileage) || 0),
|
||||
totalMileage: dateMileage?.totalKm ?? (Number(r.vehicle_total_mileage) || 0),
|
||||
completionRate: Number(r.completion_rate) || 0,
|
||||
isQualified: r.is_qualified === 1,
|
||||
currentYearIsQualified: r.current_year_is_qualified === 1,
|
||||
dailyRequiredMileage: Number(r.daily_required_mileage) || 0,
|
||||
rentStatus: info?.rent_status || null,
|
||||
department: info?.department || null,
|
||||
customer: info?.customer || null,
|
||||
isOnline: dateMileage ? dateMileage.isOnline : true,
|
||||
};
|
||||
});
|
||||
|
||||
return c.json(result);
|
||||
} catch (e) {
|
||||
|
||||
Reference in New Issue
Block a user