feat(platform): align vehicle detail with statistics query

This commit is contained in:
lingniu
2026-07-05 14:50:54 +08:00
parent 597395852c
commit 5c73cea263
2 changed files with 25 additions and 25 deletions

View File

@@ -431,7 +431,7 @@ export function VehicleDetail({
`实时:${vehicleServiceURL(vehicleServiceHash('realtime'))}`, `实时:${vehicleServiceURL(vehicleServiceHash('realtime'))}`,
`轨迹:${vehicleServiceURL(vehicleServiceHash('history'))}`, `轨迹:${vehicleServiceURL(vehicleServiceHash('history'))}`,
`历史数据:${vehicleServiceURL(vehicleServiceHash('history-query', { tab: 'raw', includeFields: 'true' }))}`, `历史数据:${vehicleServiceURL(vehicleServiceHash('history-query', { tab: 'raw', includeFields: 'true' }))}`,
`里程${vehicleServiceURL(vehicleServiceHash('mileage'))}` `统计查询${vehicleServiceURL(vehicleServiceHash('mileage'))}`
]; ];
copyText(lines.join('\n'), '诊断摘要'); copyText(lines.join('\n'), '诊断摘要');
}; };
@@ -463,7 +463,7 @@ export function VehicleDetail({
`实时监控:${vehicleServiceURL(vehicleServiceHash('realtime'))}`, `实时监控:${vehicleServiceURL(vehicleServiceHash('realtime'))}`,
`轨迹回放:${vehicleServiceURL(vehicleServiceHash('history'))}`, `轨迹回放:${vehicleServiceURL(vehicleServiceHash('history'))}`,
`历史数据:${vehicleServiceURL(vehicleServiceHash('history-query', { tab: 'raw', includeFields: 'true' }))}`, `历史数据:${vehicleServiceURL(vehicleServiceHash('history-query', { tab: 'raw', includeFields: 'true' }))}`,
`里程统计:${vehicleServiceURL(vehicleServiceHash('mileage'))}` `统计查询${vehicleServiceURL(vehicleServiceHash('mileage'))}`
]; ];
copyText(lines.join('\n'), '数据通道归并矩阵'); copyText(lines.join('\n'), '数据通道归并矩阵');
}; };
@@ -612,7 +612,7 @@ export function VehicleDetail({
`实时监控:${vehicleServiceURL(vehicleServiceHash('realtime'))}`, `实时监控:${vehicleServiceURL(vehicleServiceHash('realtime'))}`,
`轨迹回放:${vehicleServiceURL(vehicleServiceHash('history'))}`, `轨迹回放:${vehicleServiceURL(vehicleServiceHash('history'))}`,
`历史数据:${vehicleServiceURL(vehicleServiceHash('history-query', { tab: 'raw', includeFields: 'true' }))}`, `历史数据:${vehicleServiceURL(vehicleServiceHash('history-query', { tab: 'raw', includeFields: 'true' }))}`,
`里程统计:${vehicleServiceURL(vehicleServiceHash('mileage'))}` `统计查询${vehicleServiceURL(vehicleServiceHash('mileage'))}`
]; ];
copyText(lines.join('\n'), '统一车辆服务摘要'); copyText(lines.join('\n'), '统一车辆服务摘要');
}; };
@@ -639,7 +639,7 @@ export function VehicleDetail({
`实时监控:${vehicleServiceURL(vehicleServiceHash('realtime'))}`, `实时监控:${vehicleServiceURL(vehicleServiceHash('realtime'))}`,
`轨迹回放:${vehicleServiceURL(vehicleServiceHash('history'))}`, `轨迹回放:${vehicleServiceURL(vehicleServiceHash('history'))}`,
`历史数据:${vehicleServiceURL(vehicleServiceHash('history-query', { tab: 'raw', includeFields: 'true' }))}`, `历史数据:${vehicleServiceURL(vehicleServiceHash('history-query', { tab: 'raw', includeFields: 'true' }))}`,
`里程统计:${vehicleServiceURL(vehicleServiceHash('mileage'))}` `统计查询${vehicleServiceURL(vehicleServiceHash('mileage'))}`
]; ];
copyText(lines.join('\n'), '车辆服务档案'); copyText(lines.join('\n'), '车辆服务档案');
}; };
@@ -667,7 +667,7 @@ export function VehicleDetail({
`实时监控:${vehicleServiceURL(vehicleServiceHash('realtime'))}`, `实时监控:${vehicleServiceURL(vehicleServiceHash('realtime'))}`,
`轨迹回放:${vehicleServiceURL(vehicleServiceHash('history'))}`, `轨迹回放:${vehicleServiceURL(vehicleServiceHash('history'))}`,
`历史数据:${vehicleServiceURL(vehicleServiceHash('history-query', { tab: 'raw', includeFields: 'true' }))}`, `历史数据:${vehicleServiceURL(vehicleServiceHash('history-query', { tab: 'raw', includeFields: 'true' }))}`,
`里程统计:${vehicleServiceURL(vehicleServiceHash('mileage'))}` `统计查询${vehicleServiceURL(vehicleServiceHash('mileage'))}`
]; ];
copyText(lines.join('\n'), '运营摘要'); copyText(lines.join('\n'), '运营摘要');
}; };
@@ -717,14 +717,14 @@ export function VehicleDetail({
] ]
}, },
{ {
title: '里程复核', title: '统计复核',
value: `${formatCompactNumber(overview?.mileageCount ?? detail?.mileage?.total ?? 0)} 条统计`, value: `${formatCompactNumber(overview?.mileageCount ?? detail?.mileage?.total ?? 0)} 条统计`,
meta: formatCompactNumber(evidenceMileageDelta, ' km 差异'), meta: formatCompactNumber(evidenceMileageDelta, ' km 差异'),
color: isFiniteNumber(evidenceMileageDelta) && Math.abs(evidenceMileageDelta) > 1 ? 'orange' as const : 'green' as const, color: isFiniteNumber(evidenceMileageDelta) && Math.abs(evidenceMileageDelta) > 1 ? 'orange' as const : 'green' as const,
detail: '核对统计口径,确认区间总值、每日里程与数据通道是否闭合。', detail: '核对统计口径,确认区间总值、每日里程与数据通道是否闭合。',
disabled: !hasResolvedVIN, disabled: !hasResolvedVIN,
actions: [ actions: [
{ label: '查看里程', onClick: () => onOpenMileage(resolvedVIN, activeProtocol) } { label: '查看统计', onClick: () => onOpenMileage(resolvedVIN, activeProtocol) }
] ]
} }
]; ];
@@ -765,7 +765,7 @@ export function VehicleDetail({
title: '复核统计口径', title: '复核统计口径',
evidence: `${formatCompactNumber(overview?.mileageCount ?? detail?.mileage?.total ?? 0)} 条统计,证据里程差 ${formatCompactNumber(evidenceMileageDelta, ' km')}`, evidence: `${formatCompactNumber(overview?.mileageCount ?? detail?.mileage?.total ?? 0)} 条统计,证据里程差 ${formatCompactNumber(evidenceMileageDelta, ' km')}`,
acceptance: '区间里程、每日里程和轨迹总里程可以互相解释。', acceptance: '区间里程、每日里程和轨迹总里程可以互相解释。',
action: '里程统计', action: '统计查询',
disabled: !hasResolvedVIN, disabled: !hasResolvedVIN,
onClick: () => onOpenMileage(resolvedVIN, activeProtocol) onClick: () => onOpenMileage(resolvedVIN, activeProtocol)
} }
@@ -791,7 +791,7 @@ export function VehicleDetail({
`轨迹回放:${vehicleServiceURL(vehicleServiceHash('history'))}`, `轨迹回放:${vehicleServiceURL(vehicleServiceHash('history'))}`,
`历史数据:${vehicleServiceURL(vehicleServiceHash('history-query', { tab: 'raw', includeFields: 'true' }))}`, `历史数据:${vehicleServiceURL(vehicleServiceHash('history-query', { tab: 'raw', includeFields: 'true' }))}`,
`告警事件:${vehicleServiceURL(buildAppHash({ page: 'alert-events', keyword: resolvedVIN, protocol: activeProtocol }))}`, `告警事件:${vehicleServiceURL(buildAppHash({ page: 'alert-events', keyword: resolvedVIN, protocol: activeProtocol }))}`,
`里程统计:${vehicleServiceURL(vehicleServiceHash('mileage'))}`, `统计查询${vehicleServiceURL(vehicleServiceHash('mileage'))}`,
`车辆服务:${vehicleServiceURL(vehicleServiceHash('detail'))}` `车辆服务:${vehicleServiceURL(vehicleServiceHash('detail'))}`
]; ];
copyText(lines.join('\n'), '单车处理清单'); copyText(lines.join('\n'), '单车处理清单');
@@ -831,11 +831,11 @@ export function VehicleDetail({
onSecondary: () => exportVehicleReport() onSecondary: () => exportVehicleReport()
}, },
{ {
title: '复核里程统计', title: '复核统计查询',
value: `${formatCompactNumber(overview?.mileageCount ?? detail?.mileage?.total ?? 0)} 条统计`, value: `${formatCompactNumber(overview?.mileageCount ?? detail?.mileage?.total ?? 0)} 条统计`,
detail: `证据里程差 ${formatCompactNumber(evidenceMileageDelta, ' km')},确认区间和日统计能闭合。`, detail: `证据里程差 ${formatCompactNumber(evidenceMileageDelta, ' km')},确认区间和日统计能闭合。`,
color: isFiniteNumber(evidenceMileageDelta) && Math.abs(evidenceMileageDelta) > 1 ? 'orange' as const : 'green' as const, color: isFiniteNumber(evidenceMileageDelta) && Math.abs(evidenceMileageDelta) > 1 ? 'orange' as const : 'green' as const,
primaryAction: '里程统计', primaryAction: '统计查询',
secondaryAction: '诊断摘要', secondaryAction: '诊断摘要',
disabled: !hasResolvedVIN, disabled: !hasResolvedVIN,
onPrimary: () => onOpenMileage(resolvedVIN, activeProtocol), onPrimary: () => onOpenMileage(resolvedVIN, activeProtocol),
@@ -920,10 +920,10 @@ export function VehicleDetail({
onClick: () => onOpenHistory(resolvedVIN, activeProtocol) onClick: () => onOpenHistory(resolvedVIN, activeProtocol)
}, },
{ {
title: '里程能源', title: '里程能源',
value: `${formatCompactNumber(latest?.totalMileageKm, ' km')}`, value: `${formatCompactNumber(latest?.totalMileageKm, ' km')}`,
detail: `SOC ${formatCompactNumber(latest?.socPercent, '%')} / 速度 ${formatCompactNumber(latest?.speedKmh, ' km/h')}`, detail: `SOC ${formatCompactNumber(latest?.socPercent, '%')} / 速度 ${formatCompactNumber(latest?.speedKmh, ' km/h')}`,
action: '里程统计', action: '统计查询',
color: isFiniteNumber(latest?.totalMileageKm) ? 'green' as const : 'orange' as const, color: isFiniteNumber(latest?.totalMileageKm) ? 'green' as const : 'orange' as const,
disabled: !hasResolvedVIN, disabled: !hasResolvedVIN,
onClick: () => onOpenMileage(resolvedVIN, activeProtocol) onClick: () => onOpenMileage(resolvedVIN, activeProtocol)
@@ -964,7 +964,7 @@ export function VehicleDetail({
`最新上报:${lastSeen}`, `最新上报:${lastSeen}`,
`轨迹证据:${overview?.historyCount ?? detail.history.total ?? 0}`, `轨迹证据:${overview?.historyCount ?? detail.history.total ?? 0}`,
`历史数据:${overview?.rawCount ?? detail.raw.total ?? 0}`, `历史数据:${overview?.rawCount ?? detail.raw.total ?? 0}`,
`里程统计:${overview?.mileageCount ?? detail.mileage.total ?? 0} 条,证据差异 ${formatCompactNumber(evidenceMileageDelta, ' km')}`, `统计查询${overview?.mileageCount ?? detail.mileage.total ?? 0} 条,证据差异 ${formatCompactNumber(evidenceMileageDelta, ' km')}`,
`告警事件:${qualityCount.toLocaleString()}`, `告警事件:${qualityCount.toLocaleString()}`,
`车辆服务:${vehicleServiceURL(vehicleServiceHash('detail'))}`, `车辆服务:${vehicleServiceURL(vehicleServiceHash('detail'))}`,
`实时监控:${vehicleServiceURL(vehicleServiceHash('realtime'))}`, `实时监控:${vehicleServiceURL(vehicleServiceHash('realtime'))}`,
@@ -1030,7 +1030,7 @@ export function VehicleDetail({
return ( return (
<div className="vp-page"> <div className="vp-page">
<PageHeader title="车辆服务" description="以车辆为主对象查看实时位置、轨迹回放、里程统计、告警通知和历史数据" /> <PageHeader title="车辆服务" description="以车辆为主对象查看实时位置、轨迹回放、统计查询、告警通知和历史数据" />
<Card bordered> <Card bordered>
<Form key={formKey} initValues={query} layout="horizontal" onSubmit={(values) => { <Form key={formKey} initValues={query} layout="horizontal" onSubmit={(values) => {
const nextQuery = { keyword: String(values.keyword ?? ''), protocol: String(values.protocol ?? '') }; const nextQuery = { keyword: String(values.keyword ?? ''), protocol: String(values.protocol ?? '') };
@@ -1054,7 +1054,7 @@ export function VehicleDetail({
<Button disabled={!hasResolvedVIN} onClick={() => onOpenRealtime(resolvedVIN, activeProtocol)}></Button> <Button disabled={!hasResolvedVIN} onClick={() => onOpenRealtime(resolvedVIN, activeProtocol)}></Button>
<Button disabled={!hasResolvedVIN} onClick={() => onOpenHistory(resolvedVIN, activeProtocol)}></Button> <Button disabled={!hasResolvedVIN} onClick={() => onOpenHistory(resolvedVIN, activeProtocol)}></Button>
<Button disabled={!hasResolvedVIN} onClick={() => onOpenRaw(resolvedVIN, activeProtocol)}></Button> <Button disabled={!hasResolvedVIN} onClick={() => onOpenRaw(resolvedVIN, activeProtocol)}></Button>
<Button disabled={!hasResolvedVIN} onClick={() => onOpenMileage(resolvedVIN, activeProtocol)}></Button> <Button disabled={!hasResolvedVIN} onClick={() => onOpenMileage(resolvedVIN, activeProtocol)}></Button>
</Space> </Space>
</Form> </Form>
</Card> </Card>
@@ -1089,7 +1089,7 @@ export function VehicleDetail({
{[archivePlate, displayVIN].filter((item) => item && item !== '-').join(' / ') || displayLookupKey} {[archivePlate, displayVIN].filter((item) => item && item !== '-').join(' / ') || displayLookupKey}
</Typography.Title> </Typography.Title>
<Typography.Text type="secondary"> <Typography.Text type="secondary">
SOC SOC
</Typography.Text> </Typography.Text>
<div className="vp-vehicle-live-facts"> <div className="vp-vehicle-live-facts">
{[ {[
@@ -1159,7 +1159,7 @@ export function VehicleDetail({
<Button size="small" theme="solid" type="primary" disabled={!hasResolvedVIN} onClick={() => onOpenRealtime(resolvedVIN, activeProtocol)}></Button> <Button size="small" theme="solid" type="primary" disabled={!hasResolvedVIN} onClick={() => onOpenRealtime(resolvedVIN, activeProtocol)}></Button>
<Button size="small" theme="light" disabled={!hasResolvedVIN} onClick={() => onOpenHistory(resolvedVIN, activeProtocol)}></Button> <Button size="small" theme="light" disabled={!hasResolvedVIN} onClick={() => onOpenHistory(resolvedVIN, activeProtocol)}></Button>
<Button size="small" theme="light" disabled={!hasResolvedVIN} onClick={() => onOpenRaw(resolvedVIN, activeProtocol)}></Button> <Button size="small" theme="light" disabled={!hasResolvedVIN} onClick={() => onOpenRaw(resolvedVIN, activeProtocol)}></Button>
<Button size="small" theme="light" disabled={!hasResolvedVIN} onClick={() => onOpenMileage(resolvedVIN, activeProtocol)}></Button> <Button size="small" theme="light" disabled={!hasResolvedVIN} onClick={() => onOpenMileage(resolvedVIN, activeProtocol)}></Button>
</div> </div>
</div> </div>
<div className="vp-unified-service-grid"> <div className="vp-unified-service-grid">

View File

@@ -11340,7 +11340,7 @@ test('shows unified service overview on vehicle detail', async () => {
expect(screen.getByText('看实时位置')).toBeInTheDocument(); expect(screen.getByText('看实时位置')).toBeInTheDocument();
expect(screen.getByText('回放轨迹证据')).toBeInTheDocument(); expect(screen.getByText('回放轨迹证据')).toBeInTheDocument();
expect(screen.getAllByText('查询历史数据').length).toBeGreaterThanOrEqual(1); expect(screen.getAllByText('查询历史数据').length).toBeGreaterThanOrEqual(1);
expect(screen.getByText('复核里程统计')).toBeInTheDocument(); expect(screen.getByText('复核统计查询')).toBeInTheDocument();
expect(screen.getByText('闭环告警事件')).toBeInTheDocument(); expect(screen.getByText('闭环告警事件')).toBeInTheDocument();
expect(screen.getByText('单车运营工作台')).toBeInTheDocument(); expect(screen.getByText('单车运营工作台')).toBeInTheDocument();
expect(screen.getByText('单车服务处理清单')).toBeInTheDocument(); expect(screen.getByText('单车服务处理清单')).toBeInTheDocument();
@@ -11354,7 +11354,7 @@ test('shows unified service overview on vehicle detail', async () => {
expect(screen.getAllByText('轨迹回放').length).toBeGreaterThanOrEqual(1); expect(screen.getAllByText('轨迹回放').length).toBeGreaterThanOrEqual(1);
expect(screen.getAllByText('历史数据').length).toBeGreaterThan(0); expect(screen.getAllByText('历史数据').length).toBeGreaterThan(0);
expect(screen.getByText('告警处置')).toBeInTheDocument(); expect(screen.getByText('告警处置')).toBeInTheDocument();
expect(screen.getByText('里程复核')).toBeInTheDocument(); expect(screen.getAllByText('统计复核').length).toBeGreaterThan(0);
expect(screen.getByText('确认实时状态')).toBeInTheDocument(); expect(screen.getByText('确认实时状态')).toBeInTheDocument();
expect(screen.getByText('回放轨迹断点')).toBeInTheDocument(); expect(screen.getByText('回放轨迹断点')).toBeInTheDocument();
expect(screen.getAllByText('查询历史数据').length).toBeGreaterThanOrEqual(1); expect(screen.getAllByText('查询历史数据').length).toBeGreaterThanOrEqual(1);
@@ -11371,7 +11371,7 @@ test('shows unified service overview on vehicle detail', async () => {
expect(screen.getByRole('button', { name: '单车任务 看实时位置 实时监控' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: '单车任务 看实时位置 实时监控' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: '单车任务 回放轨迹证据 轨迹回放' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: '单车任务 回放轨迹证据 轨迹回放' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: '单车任务 查询历史数据 历史数据' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: '单车任务 查询历史数据 历史数据' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: '单车任务 复核里程统计 里程统计' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: '单车任务 复核统计查询 统计查询' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: '单车任务 闭环告警事件 告警事件' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: '单车任务 闭环告警事件 告警事件' })).toBeInTheDocument();
fireEvent.click(screen.getByRole('button', { name: '单车运营工作台 历史数据 查看历史数据' })); fireEvent.click(screen.getByRole('button', { name: '单车运营工作台 历史数据 查看历史数据' }));
expect(window.location.hash).toBe('#/history-query?keyword=VIN001&tab=raw'); expect(window.location.hash).toBe('#/history-query?keyword=VIN001&tab=raw');
@@ -11864,7 +11864,7 @@ test('copies vehicle service diagnostic summary', async () => {
expect(screen.getByText('业务可用性')).toBeInTheDocument(); expect(screen.getByText('业务可用性')).toBeInTheDocument();
expect(await screen.findByText('车辆服务结论')).toBeInTheDocument(); expect(await screen.findByText('车辆服务结论')).toBeInTheDocument();
expect(screen.getByText('单车 Live View')).toBeInTheDocument(); expect(screen.getByText('单车 Live View')).toBeInTheDocument();
expect(screen.getByText('客户打开一辆车时先看当前位置、最后上报、速度、SOC、总里程和告警再进入轨迹回放、里程统计、数据导出和告警闭环。')).toBeInTheDocument(); expect(screen.getByText('客户打开一辆车时先看当前位置、最后上报、速度、SOC、总里程和告警再进入轨迹回放、统计查询、数据导出和告警闭环。')).toBeInTheDocument();
expect(screen.getByText('当前位置')).toBeInTheDocument(); expect(screen.getByText('当前位置')).toBeInTheDocument();
expect(screen.getByRole('button', { name: '单车实时视图 实时位置 查看实时' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: '单车实时视图 实时位置 查看实时' })).toBeInTheDocument();
expect(screen.getByRole('button', { name: '单车实时视图 数据导出 历史导出' })).toBeInTheDocument(); expect(screen.getByRole('button', { name: '单车实时视图 数据导出 历史导出' })).toBeInTheDocument();
@@ -11939,7 +11939,7 @@ test('copies vehicle service diagnostic summary', async () => {
expect(copied).toContain('实时http://localhost:3000/#/realtime?keyword=VIN-SUMMARY-001&protocol=JT808'); expect(copied).toContain('实时http://localhost:3000/#/realtime?keyword=VIN-SUMMARY-001&protocol=JT808');
expect(copied).toContain('轨迹http://localhost:3000/#/history?keyword=VIN-SUMMARY-001&protocol=JT808'); expect(copied).toContain('轨迹http://localhost:3000/#/history?keyword=VIN-SUMMARY-001&protocol=JT808');
expect(copied).toContain('历史数据http://localhost:3000/#/history-query?keyword=VIN-SUMMARY-001&protocol=JT808&tab=raw&includeFields=true'); expect(copied).toContain('历史数据http://localhost:3000/#/history-query?keyword=VIN-SUMMARY-001&protocol=JT808&tab=raw&includeFields=true');
expect(copied).toContain('里程http://localhost:3000/#/mileage?keyword=VIN-SUMMARY-001&protocol=JT808'); expect(copied).toContain('统计查询http://localhost:3000/#/mileage?keyword=VIN-SUMMARY-001&protocol=JT808');
fireEvent.click(screen.getByRole('button', { name: /复制运营摘要/ })); fireEvent.click(screen.getByRole('button', { name: /复制运营摘要/ }));
await waitFor(() => { await waitFor(() => {
@@ -11961,7 +11961,7 @@ test('copies vehicle service diagnostic summary', async () => {
expect(operationsCopied).toContain('实时监控http://localhost:3000/#/realtime?keyword=VIN-SUMMARY-001&protocol=JT808'); expect(operationsCopied).toContain('实时监控http://localhost:3000/#/realtime?keyword=VIN-SUMMARY-001&protocol=JT808');
expect(operationsCopied).toContain('轨迹回放http://localhost:3000/#/history?keyword=VIN-SUMMARY-001&protocol=JT808'); expect(operationsCopied).toContain('轨迹回放http://localhost:3000/#/history?keyword=VIN-SUMMARY-001&protocol=JT808');
expect(operationsCopied).toContain('历史数据http://localhost:3000/#/history-query?keyword=VIN-SUMMARY-001&protocol=JT808&tab=raw&includeFields=true'); expect(operationsCopied).toContain('历史数据http://localhost:3000/#/history-query?keyword=VIN-SUMMARY-001&protocol=JT808&tab=raw&includeFields=true');
expect(operationsCopied).toContain('里程统计http://localhost:3000/#/mileage?keyword=VIN-SUMMARY-001&protocol=JT808'); expect(operationsCopied).toContain('统计查询http://localhost:3000/#/mileage?keyword=VIN-SUMMARY-001&protocol=JT808');
fireEvent.click(screen.getByRole('button', { name: '复制处理清单' })); fireEvent.click(screen.getByRole('button', { name: '复制处理清单' }));
await waitFor(() => { await waitFor(() => {
@@ -11978,7 +11978,7 @@ test('copies vehicle service diagnostic summary', async () => {
expect(runbookCopied).toContain('5. 复核统计口径'); expect(runbookCopied).toContain('5. 复核统计口径');
expect(runbookCopied).toContain('验收:车辆在线状态、最新时间、有效坐标均可解释。'); expect(runbookCopied).toContain('验收:车辆在线状态、最新时间、有效坐标均可解释。');
expect(runbookCopied).toContain('告警事件http://localhost:3000/#/alert-events?keyword=VIN-SUMMARY-001&protocol=JT808'); expect(runbookCopied).toContain('告警事件http://localhost:3000/#/alert-events?keyword=VIN-SUMMARY-001&protocol=JT808');
expect(runbookCopied).toContain('里程统计http://localhost:3000/#/mileage?keyword=VIN-SUMMARY-001&protocol=JT808'); expect(runbookCopied).toContain('统计查询http://localhost:3000/#/mileage?keyword=VIN-SUMMARY-001&protocol=JT808');
}); });
test('exports vehicle detail service dossier as csv', async () => { test('exports vehicle detail service dossier as csv', async () => {