diff --git a/src/modules/mileage/MonitoringView.tsx b/src/modules/mileage/MonitoringView.tsx index 84e692c..f9e9e4d 100644 --- a/src/modules/mileage/MonitoringView.tsx +++ b/src/modules/mileage/MonitoringView.tsx @@ -98,7 +98,6 @@ export default function MonitoringView() { // New filters from image const [filterPlate, setFilterPlate] = useState('All'); - const [filterYear, setFilterYear] = useState('All'); const [filterDateRange, setFilterDateRange] = useState({ start: '', end: '' }); const [filterDate, setFilterDate] = useState('2026-04-01'); const [filterProject, setFilterProject] = useState('All'); @@ -108,7 +107,7 @@ export default function MonitoringView() { const [vehicles, setVehicles] = useState([]); const [stats, setStats] = useState({ totalToday: 0, totalAll: 0, onlineCount: 0, vehicleCount: 0 }); - const [filterOptions, setFilterOptions] = useState({ departments: [], customers: [], plates: [] }); + const [filterOptions, setFilterOptions] = useState({ departments: [], customers: [], plates: [], projects: [], entities: [] }); const [total, setTotal] = useState(0); const [page, setPage] = useState(1); const [hasMore, setHasMore] = useState(true); @@ -131,7 +130,8 @@ export default function MonitoringView() { page: 1, search: searchTerm || undefined, dept: filterDept !== 'All' ? filterDept : undefined, - customer: filterProject !== 'All' ? filterProject : undefined, + project: filterProject !== 'All' ? filterProject : undefined, + entity: filterEntity !== 'All' ? filterEntity : undefined, }).then(d => { setVehicles(d.vehicles); setStats(d.stats); @@ -140,7 +140,7 @@ export default function MonitoringView() { setPage(1); setHasMore(d.page < d.totalPages); }).catch(() => {}); - }, [sortBy, sortOrder, searchTerm, filterDept, filterProject]); + }, [sortBy, sortOrder, searchTerm, filterDept, filterProject, filterEntity]); // 加载更多 const loadMore = useCallback(() => { @@ -154,13 +154,14 @@ export default function MonitoringView() { page: nextPage, search: searchTerm || undefined, dept: filterDept !== 'All' ? filterDept : undefined, - customer: filterProject !== 'All' ? filterProject : undefined, + project: filterProject !== 'All' ? filterProject : undefined, + entity: filterEntity !== 'All' ? filterEntity : undefined, }).then(d => { setVehicles(prev => [...prev, ...d.vehicles]); setPage(nextPage); setHasMore(nextPage < d.totalPages); }).catch(() => {}).finally(() => setLoadingMore(false)); - }, [sortBy, sortOrder, searchTerm, filterDept, filterProject, page, loadingMore, hasMore]); + }, [sortBy, sortOrder, searchTerm, filterDept, filterProject, filterEntity, page, loadingMore, hasMore]); // 筛选/排序变化时重新加载 useEffect(() => { @@ -550,20 +551,6 @@ export default function MonitoringView() { - {/* Year */} -
- - -
{/* Date Range */} @@ -614,7 +601,7 @@ export default function MonitoringView() { onChange={(e) => setFilterProject(e.target.value)} > - {projects.map(p => )} + {filterOptions.projects.map(p => )} @@ -641,8 +628,7 @@ export default function MonitoringView() { onChange={(e) => setFilterEntity(e.target.value)} > - - + {filterOptions.entities.map(e => )} @@ -663,21 +649,23 @@ export default function MonitoringView() { {/* Mileage Range */} -
- -
+
+
+ setFilterMileageRange(prev => ({ ...prev, min: e.target.value }))} /> - {'\u2264'} 值 {'\u2264'} +
+
+ setFilterMileageRange(prev => ({ ...prev, max: e.target.value }))} /> @@ -690,7 +678,6 @@ export default function MonitoringView() { setSearchTerm(''); setFilterDept('All'); setFilterPlate('All'); - setFilterYear('All'); setFilterDateRange({ start: '', end: '' }); setFilterDate('2026-04-01'); setFilterProject('All'); @@ -772,7 +759,7 @@ export default function MonitoringView() {
{v.department ? v.department.replace('业务', '') : v.rentStatus || ''} - {v.customer || '未分配'} + {v.customer || '-'}
diff --git a/src/modules/mileage/StatisticsView.tsx b/src/modules/mileage/StatisticsView.tsx index 1d129f7..72021b2 100644 --- a/src/modules/mileage/StatisticsView.tsx +++ b/src/modules/mileage/StatisticsView.tsx @@ -141,7 +141,7 @@ export default function StatisticsView() { {chartType === 'bar' ? ( - val.split('-')[1]} /> + { const [m, d] = val.split('-'); return `${parseInt(m)}.${parseInt(d)}`; }} /> @@ -154,7 +154,7 @@ export default function StatisticsView() { ) : chartType === 'line' ? ( - val.split('-')[1]} /> + { const [m, d] = val.split('-'); return `${parseInt(m)}.${parseInt(d)}`; }} /> @@ -170,7 +170,7 @@ export default function StatisticsView() { - val.split('-')[1]} /> + { const [m, d] = val.split('-'); return `${parseInt(m)}.${parseInt(d)}`; }} /> diff --git a/src/modules/mileage/api.ts b/src/modules/mileage/api.ts index 558331b..2a0ef30 100644 --- a/src/modules/mileage/api.ts +++ b/src/modules/mileage/api.ts @@ -16,6 +16,8 @@ export async function fetchMonitoring(params?: { search?: string; dept?: string; customer?: string; + project?: string; + entity?: string; }): Promise { const query = new URLSearchParams(); if (params?.sortBy) query.set('sortBy', params.sortBy); @@ -25,6 +27,8 @@ export async function fetchMonitoring(params?: { if (params?.search) query.set('search', params.search); if (params?.dept) query.set('dept', params.dept); if (params?.customer) query.set('customer', params.customer); + if (params?.project) query.set('project', params.project); + if (params?.entity) query.set('entity', params.entity); const qs = query.toString(); return fetchJson(`${BASE}/monitoring${qs ? `?${qs}` : ''}`); } diff --git a/src/modules/mileage/types.ts b/src/modules/mileage/types.ts index 57f955a..7aa656c 100644 --- a/src/modules/mileage/types.ts +++ b/src/modules/mileage/types.ts @@ -10,6 +10,8 @@ export interface MonitoringVehicle { department: string | null; manager: string | null; rentStatus: string | null; + entity: string | null; + project: string | null; } export interface MonitoringStats { @@ -23,6 +25,8 @@ export interface MonitoringFilters { departments: string[]; customers: string[]; plates: string[]; + projects: string[]; + entities: string[]; } export interface MonitoringData { diff --git a/src/server/routes/mileage.ts b/src/server/routes/mileage.ts index 89f2709..79a2e9d 100644 --- a/src/server/routes/mileage.ts +++ b/src/server/routes/mileage.ts @@ -10,7 +10,9 @@ const VEHICLE_INFO_SQL = `SELECT cus.customer_name AS customer, dep.dep_name AS department, u.user_name AS manager, - dic_status.dic_name AS rent_status + dic_status.dic_name AS rent_status, + org_truck.org_name AS entity, + c.project_name AS project FROM tab_truck truck LEFT JOIN tab_truck_status_info si ON si.truck_id = truck.id AND si.is_deleted = 0 LEFT JOIN tab_contract c ON c.id = si.contract_id AND c.is_deleted = 0 @@ -19,6 +21,7 @@ LEFT JOIN tab_user u ON u.id = c.bd AND u.is_deleted = 0 LEFT JOIN tab_department dep ON dep.id = u.dep_id AND dep.is_deleted = 0 LEFT JOIN tab_dic dic_status ON dic_status.parent_code = 'dic_truck_rent_status' AND dic_status.dic_code = truck.truck_rent_status AND dic_status.is_deleted = 0 +LEFT JOIN tab_org org_truck ON org_truck.id = truck.org_id AND org_truck.is_deleted = 0 WHERE truck.is_deleted = 0 AND truck.is_operation = 1`; // ========== 实时监控缓存(每2分钟刷新) ========== @@ -34,12 +37,14 @@ interface CachedVehicle { department: string | null; manager: string | null; rentStatus: string | null; + entity: string | null; + project: string | null; } interface MonitoringCache { vehicles: CachedVehicle[]; stats: { totalToday: number; totalAll: number; onlineCount: number; vehicleCount: number }; - filters: { departments: string[]; customers: string[]; plates: string[] }; + filters: { departments: string[]; customers: string[]; plates: string[]; projects: string[]; entities: string[] }; updatedAt: string; } @@ -100,6 +105,8 @@ async function refreshMonitoringCache() { department: info?.department || null, manager: info?.manager || null, rentStatus: info?.rent_status || null, + entity: info?.entity || null, + project: info?.project || null, }; }); @@ -112,11 +119,13 @@ async function refreshMonitoringCache() { const departments = Array.from(new Set(vehicles.map(v => v.department).filter(Boolean))) as string[]; const customers = Array.from(new Set(vehicles.map(v => v.customer).filter(Boolean))) as string[]; const plates = vehicles.map(v => v.plate); + const projects = Array.from(new Set(vehicles.map(v => v.project).filter(Boolean))) as string[]; + const entities = Array.from(new Set(vehicles.map(v => v.entity).filter(Boolean))) as string[]; monitoringCache = { vehicles, stats: { totalToday, totalAll, onlineCount, vehicleCount: vehicles.length }, - filters: { departments, customers, plates }, + filters: { departments, customers, plates, projects, entities }, updatedAt: new Date().toISOString(), }; @@ -133,7 +142,7 @@ setInterval(refreshMonitoringCache, 2 * 60 * 1000); // GET /monitoring — 从缓存取数据,支持筛选/排序/分页 app.get('/monitoring', (c) => { if (!monitoringCache) { - return c.json({ vehicles: [], stats: { totalToday: 0, totalAll: 0, onlineCount: 0, vehicleCount: 0 }, filters: { departments: [], customers: [], plates: [] }, total: 0, updatedAt: new Date().toISOString() }); + return c.json({ vehicles: [], stats: { totalToday: 0, totalAll: 0, onlineCount: 0, vehicleCount: 0 }, filters: { departments: [], customers: [], plates: [], projects: [], entities: [] }, total: 0, page: 1, totalPages: 1, updatedAt: new Date().toISOString() }); } const sortBy = c.req.query('sortBy') || 'today'; @@ -143,6 +152,8 @@ app.get('/monitoring', (c) => { const search = c.req.query('search') || ''; const dept = c.req.query('dept') || ''; const customer = c.req.query('customer') || ''; + const project = c.req.query('project') || ''; + const entity = c.req.query('entity') || ''; let vehicles = monitoringCache.vehicles; @@ -151,11 +162,14 @@ app.get('/monitoring', (c) => { const q = search.toLowerCase(); vehicles = vehicles.filter(v => v.plate.toLowerCase().includes(q) || - (v.customer || '').toLowerCase().includes(q) + (v.customer || '').toLowerCase().includes(q) || + (v.project || '').toLowerCase().includes(q) ); } if (dept) vehicles = vehicles.filter(v => v.department === dept); if (customer) vehicles = vehicles.filter(v => v.customer === customer); + if (project) vehicles = vehicles.filter(v => v.project === project); + if (entity) vehicles = vehicles.filter(v => v.entity === entity); const total = vehicles.length;