feat(platform): add filtered vehicle coverage summary

This commit is contained in:
lingniu
2026-07-04 03:42:19 +08:00
parent 6683ce4df7
commit b7e8aae317
12 changed files with 228 additions and 14 deletions

View File

@@ -12,6 +12,7 @@ import type {
RealtimeLocationRow,
VehicleRealtimeRow,
VehicleCoverageRow,
VehicleCoverageSummary,
VehicleDetail,
VehicleIdentityResolution,
VehicleServiceOverview,
@@ -83,6 +84,7 @@ export const api = {
vehicles: (params = new URLSearchParams()) => request<Page<VehicleRow>>(`/api/vehicles?${params.toString()}`),
vehicleResolve: (params = new URLSearchParams()) => request<VehicleIdentityResolution>(`/api/vehicles/resolve?${params.toString()}`),
vehicleCoverage: (params = new URLSearchParams()) => request<Page<VehicleCoverageRow>>(`/api/vehicles/coverage?${params.toString()}`),
vehicleCoverageSummary: (params = new URLSearchParams()) => request<VehicleCoverageSummary>(`/api/vehicles/coverage/summary?${params.toString()}`),
vehicleDetail: (params = new URLSearchParams()) => request<VehicleDetail>(`/api/vehicle-service?${params.toString()}`),
vehicleServiceSummary: () => request<VehicleServiceSummary>('/api/vehicle-service/summary'),
vehicleServiceOverview: (params = new URLSearchParams()) => request<VehicleServiceOverview>(`/api/vehicle-service/overview?${params.toString()}`),

View File

@@ -60,6 +60,14 @@ export interface VehicleCoverageRow {
serviceStatus?: VehicleServiceStatus;
}
export interface VehicleCoverageSummary {
totalVehicles: number;
onlineVehicles: number;
singleSourceVehicles: number;
multiSourceVehicles: number;
unboundVehicles: number;
}
export interface VehicleIdentityResolution {
lookupKey: string;
resolved: boolean;

View File

@@ -1,7 +1,7 @@
import { Button, Card, Form, Select, Space, Table, Tag, Toast } from '@douyinfe/semi-ui';
import { useEffect, useMemo, useState } from 'react';
import { api } from '../api/client';
import type { VehicleCoverageRow } from '../api/types';
import type { VehicleCoverageRow, VehicleCoverageSummary } from '../api/types';
import { DataEmpty } from '../components/DataEmpty';
import { PageHeader } from '../components/PageHeader';
import { StatusTag } from '../components/StatusTag';
@@ -30,20 +30,18 @@ function vehicleServiceStatus(row: VehicleCoverageRow) {
export function Vehicles({ onOpenVehicle, initialFilters = {} }: { onOpenVehicle: (vin: string) => void; initialFilters?: Record<string, string> }) {
const [rows, setRows] = useState<VehicleCoverageRow[]>([]);
const [summary, setSummary] = useState<VehicleCoverageSummary | null>(null);
const [loading, setLoading] = useState(true);
const [filters, setFilters] = useState<Record<string, string>>(initialFilters);
const [pagination, setPagination] = useState({ currentPage: 1, pageSize: 20, total: 0 });
const resultSummary = useMemo(() => {
const onlineRows = rows.filter((row) => row.online).length;
const multiSourceRows = rows.filter((row) => row.sourceCount > 1).length;
const unboundRows = rows.filter((row) => row.bindingStatus !== 'bound').length;
return [
{ label: '过滤车辆', value: pagination.total.toLocaleString() },
{ label: '本页在线', value: onlineRows.toLocaleString() },
{ label: '本页多源', value: multiSourceRows.toLocaleString() },
{ label: '本页待绑定', value: unboundRows.toLocaleString() }
{ label: '过滤车辆', value: (summary?.totalVehicles ?? pagination.total).toLocaleString() },
{ label: '在线车辆', value: (summary?.onlineVehicles ?? 0).toLocaleString() },
{ label: '多源车辆', value: (summary?.multiSourceVehicles ?? 0).toLocaleString() },
{ label: '待绑定', value: (summary?.unboundVehicles ?? 0).toLocaleString() }
];
}, [pagination.total, rows]);
}, [pagination.total, summary]);
const load = (values: Record<string, string> = filters, page = pagination.currentPage, pageSize = pagination.pageSize) => {
setLoading(true);
@@ -54,9 +52,13 @@ export function Vehicles({ onOpenVehicle, initialFilters = {} }: { onOpenVehicle
if (values?.online) params.set('online', values.online);
if (values?.bindingStatus) params.set('bindingStatus', values.bindingStatus);
if (values?.serviceStatus) params.set('serviceStatus', values.serviceStatus);
api.vehicleCoverage(params)
.then((nextPage) => {
const summaryParams = new URLSearchParams(params);
summaryParams.delete('limit');
summaryParams.delete('offset');
Promise.all([api.vehicleCoverage(params), api.vehicleCoverageSummary(summaryParams)])
.then(([nextPage, nextSummary]) => {
setRows(nextPage.items);
setSummary(nextSummary);
setPagination({ currentPage: page, pageSize, total: nextPage.total });
})
.catch((error: Error) => Toast.error(error.message))

View File

@@ -191,6 +191,22 @@ test('shows vehicle service result summary on vehicle list filters', async () =>
})
} as Response;
}
if (path.includes('/api/vehicles/coverage/summary')) {
return {
ok: true,
json: async () => ({
data: {
totalVehicles: 181,
onlineVehicles: 73,
singleSourceVehicles: 0,
multiSourceVehicles: 181,
unboundVehicles: 9
},
traceId: 'trace-test',
timestamp: 1783094400000
})
} as Response;
}
if (path.includes('/api/vehicles/coverage')) {
return {
ok: true,
@@ -238,10 +254,11 @@ test('shows vehicle service result summary on vehicle list filters', async () =>
render(<App />);
expect(await screen.findByText('当前车辆结果')).toBeInTheDocument();
expect(screen.getByText('181')).toBeInTheDocument();
expect(screen.getAllByText('181').length).toBeGreaterThanOrEqual(2);
expect(screen.getByText('过滤车辆')).toBeInTheDocument();
expect(screen.getByText('本页在线')).toBeInTheDocument();
expect(screen.getByText('本页多源')).toBeInTheDocument();
expect(screen.getByText('73')).toBeInTheDocument();
expect(screen.getByText('在线车辆')).toBeInTheDocument();
expect(screen.getByText('待绑定')).toBeInTheDocument();
expect(screen.getByText('VIN-MULTI-001')).toBeInTheDocument();
});