feat(platform): summarize vehicle service status
This commit is contained in:
@@ -70,6 +70,7 @@ export interface VehicleDetail {
|
||||
resolution?: VehicleIdentityResolution;
|
||||
identity?: VehicleRow;
|
||||
realtimeSummary?: VehicleRealtimeRow;
|
||||
serviceStatus?: VehicleServiceStatus;
|
||||
sources: string[];
|
||||
sourceStatus: VehicleSourceStatus[];
|
||||
realtime: RealtimeLocationRow[];
|
||||
@@ -79,6 +80,15 @@ export interface VehicleDetail {
|
||||
quality: Page<QualityIssueRow>;
|
||||
}
|
||||
|
||||
export interface VehicleServiceStatus {
|
||||
status: string;
|
||||
severity: string;
|
||||
title: string;
|
||||
detail: string;
|
||||
sourceCount: number;
|
||||
onlineSourceCount: number;
|
||||
}
|
||||
|
||||
export interface VehicleSourceStatus {
|
||||
protocol: string;
|
||||
online: boolean;
|
||||
|
||||
@@ -87,6 +87,7 @@ export function VehicleDetail({
|
||||
const qualityCount = detail?.quality?.total ?? 0;
|
||||
const online = resolution?.online || summary?.online || identity?.online || false;
|
||||
const lastSeen = resolution?.lastSeen || summary?.lastSeen || latest?.lastSeen || '-';
|
||||
const serviceStatus = detail?.serviceStatus;
|
||||
const activeProtocol = query.protocol?.trim() ?? '';
|
||||
const scopeText = activeProtocol ? `单一来源:${activeProtocol}` : '全部来源聚合';
|
||||
const formKey = `${query.keyword}-${query.protocol ?? ''}`;
|
||||
@@ -145,6 +146,16 @@ export function VehicleDetail({
|
||||
</Typography.Text>
|
||||
</div>
|
||||
|
||||
{serviceStatus ? (
|
||||
<div className={`vp-service-status vp-service-status-${serviceStatus.severity}`}>
|
||||
<span className="vp-scope-label">车辆服务状态</span>
|
||||
<Tag color={serviceStatus.severity === 'ok' ? 'green' : serviceStatus.severity === 'error' ? 'red' : 'orange'}>
|
||||
{serviceStatus.title}
|
||||
</Tag>
|
||||
<Typography.Text type="tertiary">{serviceStatus.detail}</Typography.Text>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<Card bordered style={{ marginTop: 16 }}>
|
||||
<div className="vp-vehicle-summary">
|
||||
<Descriptions
|
||||
|
||||
@@ -115,6 +115,33 @@ body {
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.vp-service-status {
|
||||
min-height: 40px;
|
||||
margin-top: 8px;
|
||||
padding: 8px 12px;
|
||||
border: 1px solid var(--vp-border);
|
||||
border-radius: var(--vp-radius);
|
||||
background: var(--vp-surface);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.vp-service-status-ok {
|
||||
border-color: rgba(18, 183, 106, 0.35);
|
||||
background: rgba(18, 183, 106, 0.05);
|
||||
}
|
||||
|
||||
.vp-service-status-warning {
|
||||
border-color: rgba(247, 144, 9, 0.35);
|
||||
background: rgba(247, 144, 9, 0.06);
|
||||
}
|
||||
|
||||
.vp-service-status-error {
|
||||
border-color: rgba(240, 68, 56, 0.35);
|
||||
background: rgba(240, 68, 56, 0.05);
|
||||
}
|
||||
|
||||
.vp-section {
|
||||
background: var(--vp-surface);
|
||||
border: 1px solid var(--vp-border);
|
||||
|
||||
@@ -489,6 +489,14 @@ test('shows selected source scope on vehicle detail', async () => {
|
||||
{ protocol: 'GB32960', online: false, lastSeen: '2026-07-03 20:11:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true },
|
||||
{ protocol: 'JT808', online: true, lastSeen: '2026-07-03 20:12:10', hasRealtime: true, hasHistory: true, hasRaw: true, hasMileage: true }
|
||||
],
|
||||
serviceStatus: {
|
||||
status: 'degraded',
|
||||
severity: 'warning',
|
||||
title: '部分来源离线',
|
||||
detail: '1/2 个来源在线,车辆服务可用但需要关注离线来源。',
|
||||
sourceCount: 2,
|
||||
onlineSourceCount: 1
|
||||
},
|
||||
realtime: [],
|
||||
history: { items: [], total: 0, limit: 20, offset: 0 },
|
||||
raw: { items: [], total: 0, limit: 10, offset: 0 },
|
||||
@@ -516,5 +524,7 @@ test('shows selected source scope on vehicle detail', async () => {
|
||||
|
||||
expect(await screen.findByText('当前查看范围')).toBeInTheDocument();
|
||||
expect(screen.getByText('单一来源:JT808')).toBeInTheDocument();
|
||||
expect(screen.getByText('车辆服务状态')).toBeInTheDocument();
|
||||
expect(screen.getByText('部分来源离线')).toBeInTheDocument();
|
||||
expect(fetchMock).toHaveBeenCalledWith(expect.stringContaining('/api/vehicle-service?keyword=VIN001&protocol=JT808'), undefined);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user