feat(platform): open vehicle list from dashboard coverage

This commit is contained in:
lingniu
2026-07-04 07:25:13 +08:00
parent 86b65f682d
commit 2cfb9e551f
2 changed files with 133 additions and 10 deletions

View File

@@ -94,21 +94,24 @@ export function Dashboard({ onOpenVehicle, onOpenQuality, onOpenVehicles }: { on
const [loading, setLoading] = useState(true);
const [coverageLoading, setCoverageLoading] = useState(false);
const [coverageServiceStatusTitle, setCoverageServiceStatusTitle] = useState('');
const [coverageFilters, setCoverageFilters] = useState<Record<string, string>>({});
const loadCoverage = (values?: Record<string, string>) => {
const nextValues = values ?? {};
setCoverageLoading(true);
setCoverageFilters(nextValues);
const scopeTitle = [
values?.serviceStatus ? serviceStatusTitle[values.serviceStatus] ?? values.serviceStatus : '',
values?.missingProtocol ? missingProtocolTitle[values.missingProtocol] ?? `${values.missingProtocol}` : ''
nextValues.serviceStatus ? serviceStatusTitle[nextValues.serviceStatus] ?? nextValues.serviceStatus : '',
nextValues.missingProtocol ? missingProtocolTitle[nextValues.missingProtocol] ?? `${nextValues.missingProtocol}` : ''
].filter(Boolean).join(' / ');
setCoverageServiceStatusTitle(scopeTitle);
const params = new URLSearchParams({ limit: '8' });
if (values?.keyword) params.set('keyword', values.keyword);
if (values?.coverage) params.set('coverage', values.coverage);
if (values?.missingProtocol) params.set('missingProtocol', values.missingProtocol);
if (values?.online) params.set('online', values.online);
if (values?.bindingStatus) params.set('bindingStatus', values.bindingStatus);
if (values?.serviceStatus) params.set('serviceStatus', values.serviceStatus);
if (nextValues.keyword) params.set('keyword', nextValues.keyword);
if (nextValues.coverage) params.set('coverage', nextValues.coverage);
if (nextValues.missingProtocol) params.set('missingProtocol', nextValues.missingProtocol);
if (nextValues.online) params.set('online', nextValues.online);
if (nextValues.bindingStatus) params.set('bindingStatus', nextValues.bindingStatus);
if (nextValues.serviceStatus) params.set('serviceStatus', nextValues.serviceStatus);
api.vehicleCoverage(params)
.then((page) => setCoverage(page.items))
.catch((error: Error) => Toast.error(error.message))
@@ -270,7 +273,11 @@ export function Dashboard({ onOpenVehicle, onOpenQuality, onOpenVehicles }: { on
]}
/>
</Card>
<Card title="车辆服务覆盖" bordered style={{ marginTop: 16 }}>
<Card
title={<Space><span></span><Button size="small" onClick={() => onOpenVehicles(coverageFilters)}></Button></Space>}
bordered
style={{ marginTop: 16 }}
>
{coverageServiceStatusTitle ? (
<div className="vp-scope-bar" style={{ marginBottom: 12 }}>
<Tag color="blue">{coverageServiceStatusTitle}</Tag>
@@ -304,7 +311,7 @@ export function Dashboard({ onOpenVehicle, onOpenQuality, onOpenVehicles }: { on
</Form.Select>
<Space>
<Button htmlType="submit" theme="solid" type="primary"></Button>
<Button onClick={() => loadCoverage()}></Button>
<Button onClick={() => loadCoverage({})}></Button>
</Space>
</Form>
<Table

View File

@@ -897,6 +897,122 @@ test('filters dashboard coverage from source consistency diagnosis', async () =>
expect(screen.getByText('当前筛选:来源异常 / 缺 JT808')).toBeInTheDocument();
});
test('opens full vehicle service list from dashboard coverage filters', async () => {
window.history.replaceState(null, '', '/#/dashboard');
const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
const path = String(input);
if (path.includes('/api/dashboard/summary')) {
return {
ok: true,
json: async () => ({
data: {
onlineVehicles: 3,
activeToday: 4,
frameToday: 1286320,
issueVehicles: 7,
kafkaLag: 0,
protocols: [],
serviceStatuses: [],
linkHealth: []
},
traceId: 'trace-test',
timestamp: 1783094400000
})
} as Response;
}
if (path.includes('/api/vehicle-service/summary')) {
return {
ok: true,
json: async () => ({
data: {
totalVehicles: 1,
boundVehicles: 1,
onlineVehicles: 1,
singleSourceVehicles: 1,
multiSourceVehicles: 0,
noDataVehicles: 0,
identityRequiredVehicles: 0,
serviceStatuses: [],
protocols: [],
missingSources: []
},
traceId: 'trace-test',
timestamp: 1783094400000
})
} as Response;
}
if (path.includes('/api/vehicles/coverage')) {
return {
ok: true,
json: async () => ({
data: {
items: [{
vin: 'VIN-DASH-FULL-LIST',
plate: '粤A全量',
phone: '',
oem: '',
protocols: ['GB32960'],
missingProtocols: ['JT808', 'YUTONG_MQTT'],
sourceCount: 1,
onlineSourceCount: 1,
online: true,
lastSeen: '2026-07-04 07:20:14',
bindingStatus: 'bound',
sourceConsistency: {
sourceCount: 1,
onlineSourceCount: 1,
locatedSourceCount: 0,
missingProtocols: ['JT808', 'YUTONG_MQTT'],
mileageDeltaKm: 0,
sourceTimeDeltaSeconds: 0,
scope: 'coverage',
status: 'single_source',
severity: 'warning',
title: '单一来源',
detail: '当前车辆只有一个数据来源,无法做跨来源一致性校验。'
}
}],
total: 1,
limit: path.includes('limit=20') ? 20 : 8,
offset: 0
},
traceId: 'trace-test',
timestamp: 1783094400000
})
} as Response;
}
return {
ok: true,
json: async () => ({
data: path.includes('/api/ops/health')
? { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } }
: { items: [], total: 0, limit: 8, offset: 0 },
traceId: 'trace-test',
timestamp: 1783094400000
})
} as Response;
});
render(<App />);
expect(await screen.findByText('VIN-DASH-FULL-LIST')).toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: '单一来源' }));
await waitFor(() => {
expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('missingProtocol=JT808'), undefined);
});
fireEvent.click(screen.getByRole('button', { name: '查看全部车辆' }));
await waitFor(() => {
expect(window.location.hash).toBe('#/vehicles?serviceStatus=degraded&missingProtocol=JT808');
});
const vehicleCoverageCall = fetchMock.mock.calls.find(([input]) => String(input).startsWith('/api/vehicles/coverage?limit=20&offset=0'));
expect(vehicleCoverageCall).toBeTruthy();
const vehicleCoverageParams = new URLSearchParams(String(vehicleCoverageCall?.[0]).split('?')[1]);
expect(vehicleCoverageParams.get('serviceStatus')).toBe('degraded');
expect(vehicleCoverageParams.get('missingProtocol')).toBe('JT808');
});
test('opens vehicle service from dashboard realtime preview with source evidence', async () => {
window.history.replaceState(null, '', '/#/dashboard');
vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {