feat(platform): show active vehicle filters
This commit is contained in:
@@ -34,6 +34,29 @@ function sourceEvidenceText(row: VehicleCoverageRow) {
|
|||||||
return `${row.onlineSourceCount}/${row.sourceCount} 来源在线`;
|
return `${row.onlineSourceCount}/${row.sourceCount} 来源在线`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const coverageLabel: Record<string, string> = {
|
||||||
|
single: '单源',
|
||||||
|
multi: '多源'
|
||||||
|
};
|
||||||
|
|
||||||
|
const serviceStatusLabel: Record<string, string> = {
|
||||||
|
healthy: '服务正常',
|
||||||
|
degraded: '来源不完整',
|
||||||
|
offline: '车辆离线',
|
||||||
|
no_data: '暂无数据来源',
|
||||||
|
identity_required: '身份未绑定'
|
||||||
|
};
|
||||||
|
|
||||||
|
const onlineLabel: Record<string, string> = {
|
||||||
|
online: '在线',
|
||||||
|
offline: '离线'
|
||||||
|
};
|
||||||
|
|
||||||
|
const bindingStatusLabel: Record<string, string> = {
|
||||||
|
bound: '已绑定',
|
||||||
|
unbound: '未绑定'
|
||||||
|
};
|
||||||
|
|
||||||
function sourceDiagnosisText(row: VehicleCoverageRow) {
|
function sourceDiagnosisText(row: VehicleCoverageRow) {
|
||||||
const parts = [sourceEvidenceText(row)];
|
const parts = [sourceEvidenceText(row)];
|
||||||
if ((row.missingProtocols ?? []).length > 0) {
|
if ((row.missingProtocols ?? []).length > 0) {
|
||||||
@@ -110,6 +133,15 @@ export function Vehicles({
|
|||||||
}
|
}
|
||||||
return items;
|
return items;
|
||||||
}, [pagination.total, summary]);
|
}, [pagination.total, summary]);
|
||||||
|
const filterSummary = [
|
||||||
|
filters.keyword ? `关键词:${filters.keyword}` : '',
|
||||||
|
filters.protocol ? `数据来源:${filters.protocol}` : '',
|
||||||
|
filters.coverage ? `来源覆盖:${coverageLabel[filters.coverage] ?? filters.coverage}` : '',
|
||||||
|
filters.missingProtocol ? `缺失来源:${filters.missingProtocol}` : '',
|
||||||
|
filters.serviceStatus ? `服务状态:${serviceStatusLabel[filters.serviceStatus] ?? filters.serviceStatus}` : '',
|
||||||
|
filters.online ? `在线:${onlineLabel[filters.online] ?? filters.online}` : '',
|
||||||
|
filters.bindingStatus ? `绑定:${bindingStatusLabel[filters.bindingStatus] ?? filters.bindingStatus}` : ''
|
||||||
|
].filter(Boolean);
|
||||||
|
|
||||||
const load = (values: Record<string, string> = filters, page = pagination.currentPage, pageSize = pagination.pageSize) => {
|
const load = (values: Record<string, string> = filters, page = pagination.currentPage, pageSize = pagination.pageSize) => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
@@ -249,6 +281,16 @@ export function Vehicles({
|
|||||||
</Space>
|
</Space>
|
||||||
</Form>
|
</Form>
|
||||||
</Card>
|
</Card>
|
||||||
|
{filterSummary.length > 0 ? (
|
||||||
|
<Card bordered title="当前车辆筛选" style={{ marginTop: 16 }}>
|
||||||
|
<Space wrap>
|
||||||
|
{filterSummary.map((item) => (
|
||||||
|
<Tag key={item} color="blue">{item}</Tag>
|
||||||
|
))}
|
||||||
|
<Button size="small" onClick={() => applyFilters({})}>清空筛选</Button>
|
||||||
|
</Space>
|
||||||
|
</Card>
|
||||||
|
) : null}
|
||||||
<Card bordered title="当前车辆结果" style={{ marginTop: 16 }}>
|
<Card bordered title="当前车辆结果" style={{ marginTop: 16 }}>
|
||||||
<div className="vp-result-summary-grid">
|
<div className="vp-result-summary-grid">
|
||||||
{resultSummary.map((item) => (
|
{resultSummary.map((item) => (
|
||||||
|
|||||||
@@ -354,6 +354,75 @@ test('shows vehicle service result summary on vehicle list filters', async () =>
|
|||||||
expect(screen.getByText('1/2 来源在线,缺 YUTONG_MQTT')).toBeInTheDocument();
|
expect(screen.getByText('1/2 来源在线,缺 YUTONG_MQTT')).toBeInTheDocument();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('shows and clears current vehicle service filters', async () => {
|
||||||
|
window.history.replaceState(null, '', '/#/vehicles?keyword=%E7%B2%A4A&protocol=JT808&coverage=multi&missingProtocol=YUTONG_MQTT&serviceStatus=degraded&online=online&bindingStatus=bound');
|
||||||
|
const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
|
||||||
|
const path = String(input);
|
||||||
|
if (path.includes('/api/ops/health')) {
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
json: async () => ({
|
||||||
|
data: { linkHealth: [], kafkaLag: 0, redisOnlineKeys: 0, tdengineWritable: true, mysqlWritable: true, runtime: { requestTimeoutMs: 5000 } },
|
||||||
|
traceId: 'trace-test',
|
||||||
|
timestamp: 1783094400000
|
||||||
|
})
|
||||||
|
} as Response;
|
||||||
|
}
|
||||||
|
if (path.includes('/api/vehicles/coverage/summary')) {
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
json: async () => ({
|
||||||
|
data: {
|
||||||
|
totalVehicles: 1,
|
||||||
|
onlineVehicles: 1,
|
||||||
|
singleSourceVehicles: 0,
|
||||||
|
multiSourceVehicles: 1,
|
||||||
|
noDataVehicles: 0,
|
||||||
|
unboundVehicles: 0
|
||||||
|
},
|
||||||
|
traceId: 'trace-test',
|
||||||
|
timestamp: 1783094400000
|
||||||
|
})
|
||||||
|
} as Response;
|
||||||
|
}
|
||||||
|
if (path.includes('/api/vehicles/coverage')) {
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
json: async () => ({
|
||||||
|
data: { items: [], total: 1, limit: 20, offset: 0 },
|
||||||
|
traceId: 'trace-test',
|
||||||
|
timestamp: 1783094400000
|
||||||
|
})
|
||||||
|
} as Response;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
json: async () => ({
|
||||||
|
data: { items: [], total: 0, limit: 20, offset: 0 },
|
||||||
|
traceId: 'trace-test',
|
||||||
|
timestamp: 1783094400000
|
||||||
|
})
|
||||||
|
} as Response;
|
||||||
|
});
|
||||||
|
|
||||||
|
render(<App />);
|
||||||
|
|
||||||
|
expect(await screen.findByText('当前车辆筛选')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('关键词:粤A')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('数据来源:JT808')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('来源覆盖:多源')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('缺失来源:YUTONG_MQTT')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('服务状态:来源不完整')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('在线:在线')).toBeInTheDocument();
|
||||||
|
expect(screen.getByText('绑定:已绑定')).toBeInTheDocument();
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: '清空筛选' }));
|
||||||
|
|
||||||
|
await waitFor(() => {
|
||||||
|
expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/vehicles/coverage?limit=20&offset=0'), undefined);
|
||||||
|
});
|
||||||
|
expect(window.location.hash).toBe('#/vehicles');
|
||||||
|
});
|
||||||
|
|
||||||
test('filters vehicle list from result summary actions', async () => {
|
test('filters vehicle list from result summary actions', async () => {
|
||||||
window.history.replaceState(null, '', '/#/vehicles');
|
window.history.replaceState(null, '', '/#/vehicles');
|
||||||
const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
|
const fetchMock = vi.spyOn(globalThis, 'fetch').mockImplementation(async (input) => {
|
||||||
|
|||||||
Reference in New Issue
Block a user