feat(platform): expose single-source vehicles
This commit is contained in:
@@ -102,6 +102,7 @@ export function Dashboard({ onOpenVehicle, onOpenQuality, onOpenVehicles }: { on
|
||||
const kpis: Array<{ label: string; value: string; filters: Record<string, string> }> = [
|
||||
{ label: '总车辆', value: formatCount(serviceSummary?.totalVehicles), filters: {} },
|
||||
{ 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?.identityRequiredVehicles), filters: { serviceStatus: 'identity_required' } }
|
||||
];
|
||||
|
||||
@@ -50,6 +50,7 @@ export function Vehicles({
|
||||
const items: Array<{ label: string; value: string; filters: Record<string, string> }> = [
|
||||
{ label: '过滤车辆', value: (summary?.totalVehicles ?? pagination.total).toLocaleString(), filters: {} },
|
||||
{ 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?.unboundVehicles ?? 0).toLocaleString(), filters: { bindingStatus: 'unbound' } }
|
||||
];
|
||||
|
||||
@@ -117,6 +117,8 @@ test('dashboard renders vehicle service summary metrics', async () => {
|
||||
|
||||
expect(await screen.findByText('总车辆')).toBeInTheDocument();
|
||||
expect(screen.getByText('1,033')).toBeInTheDocument();
|
||||
expect(screen.getByText('单源车辆')).toBeInTheDocument();
|
||||
expect(screen.getByText('391')).toBeInTheDocument();
|
||||
expect(screen.getByText('多源车辆')).toBeInTheDocument();
|
||||
expect(screen.getByText('181')).toBeInTheDocument();
|
||||
expect(screen.getByText('身份未绑定')).toBeInTheDocument();
|
||||
@@ -169,12 +171,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=multi'), undefined);
|
||||
expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/vehicles/coverage?limit=20&offset=0&coverage=single'), undefined);
|
||||
});
|
||||
expect(window.location.hash).toBe('#/vehicles?coverage=multi');
|
||||
expect(window.location.hash).toBe('#/vehicles?coverage=single');
|
||||
});
|
||||
|
||||
test('shows vehicle service result summary on vehicle list filters', async () => {
|
||||
@@ -258,6 +260,7 @@ test('shows vehicle service result summary on vehicle list filters', async () =>
|
||||
expect(screen.getByText('过滤车辆')).toBeInTheDocument();
|
||||
expect(screen.getByText('73')).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);
|
||||
|
||||
@@ -26,6 +26,8 @@ Vehicle detail and top search context should expose one source-consistency concl
|
||||
|
||||
Vehicle service lists and dashboard previews should label protocol coverage as `来源证据`. The row should make the vehicle service coverage explicit, for example `2/3 来源在线`, while protocol tags remain compact evidence labels underneath the same vehicle row.
|
||||
|
||||
Dashboard and vehicle-list summary cards should expose both `单源车辆` and `多源车辆`. Single-source vehicles are operationally important because they cannot be cross-checked across sources, so they must be one-click filter targets rather than hidden behind the generic coverage filter.
|
||||
|
||||
## Interaction Rules
|
||||
|
||||
- Tables are the default data surface.
|
||||
|
||||
Reference in New Issue
Block a user