feat(platform): expose no-data vehicles

This commit is contained in:
lingniu
2026-07-04 04:25:29 +08:00
parent 7f9d327f38
commit 40397efa4a
13 changed files with 79 additions and 19 deletions

View File

@@ -65,6 +65,7 @@ export interface VehicleCoverageSummary {
onlineVehicles: number;
singleSourceVehicles: number;
multiSourceVehicles: number;
noDataVehicles: number;
unboundVehicles: number;
}

View File

@@ -17,6 +17,7 @@ const serviceStatusColor: Record<string, 'green' | 'orange' | 'red' | 'grey'> =
healthy: 'green',
degraded: 'orange',
offline: 'red',
no_data: 'orange',
identity_required: 'orange'
};
@@ -24,6 +25,7 @@ const serviceStatusTitle: Record<string, string> = {
healthy: '服务正常',
degraded: '部分来源离线',
offline: '车辆离线',
no_data: '暂无数据来源',
identity_required: '身份未绑定'
};
@@ -104,6 +106,7 @@ export function Dashboard({ onOpenVehicle, onOpenQuality, onOpenVehicles }: { on
{ label: '在线车辆', value: formatCount(serviceSummary?.onlineVehicles ?? summary?.onlineVehicles), filters: { online: 'online' } },
{ label: '单源车辆', value: formatCount(serviceSummary?.singleSourceVehicles), filters: { coverage: 'single' } },
{ label: '多源车辆', value: formatCount(serviceSummary?.multiSourceVehicles), filters: { coverage: 'multi' } },
{ label: '暂无来源车辆', value: formatCount(serviceSummary?.noDataVehicles), filters: { serviceStatus: 'no_data' } },
{ label: '身份未绑定', value: formatCount(serviceSummary?.identityRequiredVehicles), filters: { serviceStatus: 'identity_required' } }
];
@@ -239,6 +242,7 @@ export function Dashboard({ onOpenVehicle, onOpenQuality, onOpenVehicles }: { on
<Select.Option value="healthy"></Select.Option>
<Select.Option value="degraded">线</Select.Option>
<Select.Option value="offline">线</Select.Option>
<Select.Option value="no_data"></Select.Option>
<Select.Option value="identity_required"></Select.Option>
</Form.Select>
<Space>

View File

@@ -52,6 +52,7 @@ export function Vehicles({
{ label: '在线车辆', value: (summary?.onlineVehicles ?? 0).toLocaleString(), filters: { online: 'online' } },
{ label: '单源车辆', value: (summary?.singleSourceVehicles ?? 0).toLocaleString(), filters: { coverage: 'single' } },
{ label: '多源车辆', value: (summary?.multiSourceVehicles ?? 0).toLocaleString(), filters: { coverage: 'multi' } },
{ label: '暂无来源车辆', value: (summary?.noDataVehicles ?? 0).toLocaleString(), filters: { serviceStatus: 'no_data' } },
{ label: '待绑定', value: (summary?.unboundVehicles ?? 0).toLocaleString(), filters: { bindingStatus: 'unbound' } }
];
return items;
@@ -144,6 +145,7 @@ export function Vehicles({
<Select.Option value="healthy"></Select.Option>
<Select.Option value="degraded">线</Select.Option>
<Select.Option value="offline">线</Select.Option>
<Select.Option value="no_data"></Select.Option>
<Select.Option value="identity_required"></Select.Option>
</Form.Select>
<Form.Select field="online" label="在线" placeholder="全部" style={{ width: 130 }}>

View File

@@ -121,6 +121,8 @@ test('dashboard renders vehicle service summary metrics', async () => {
expect(screen.getByText('391')).toBeInTheDocument();
expect(screen.getByText('多源车辆')).toBeInTheDocument();
expect(screen.getByText('181')).toBeInTheDocument();
expect(screen.getByText('暂无来源车辆')).toBeInTheDocument();
expect(screen.getByText('461')).toBeInTheDocument();
expect(screen.getByText('身份未绑定')).toBeInTheDocument();
expect(fetchMock).toHaveBeenCalledWith('/api/vehicle-service/summary', undefined);
});
@@ -171,12 +173,12 @@ test('opens vehicle list filtered by service summary KPI', async () => {
render(<App />);
fireEvent.click(await screen.findByRole('button', { name: /源车辆/ }));
fireEvent.click(await screen.findByRole('button', { name: /暂无来源车辆/ }));
await waitFor(() => {
expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/vehicles/coverage?limit=20&offset=0&coverage=single'), undefined);
expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/vehicles/coverage?limit=20&offset=0&serviceStatus=no_data'), undefined);
});
expect(window.location.hash).toBe('#/vehicles?coverage=single');
expect(window.location.hash).toBe('#/vehicles?serviceStatus=no_data');
});
test('shows vehicle service result summary on vehicle list filters', async () => {
@@ -261,6 +263,7 @@ test('shows vehicle service result summary on vehicle list filters', async () =>
expect(screen.getByText('73')).toBeInTheDocument();
expect(screen.getByText('在线车辆')).toBeInTheDocument();
expect(screen.getByText('单源车辆')).toBeInTheDocument();
expect(screen.getByText('暂无来源车辆')).toBeInTheDocument();
expect(screen.getByText('待绑定')).toBeInTheDocument();
expect(screen.getByText('VIN-MULTI-001')).toBeInTheDocument();
expect(screen.getAllByText('来源证据').length).toBeGreaterThanOrEqual(1);