feat(platform): add filtered vehicle coverage summary
This commit is contained in:
@@ -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()}`),
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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();
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user