feat(platform): summarize vehicle source coverage

This commit is contained in:
lingniu
2026-07-03 23:51:40 +08:00
parent 6c53172ba7
commit d4fdd7527b
2 changed files with 59 additions and 3 deletions

View File

@@ -11,6 +11,13 @@ type VehicleQuery = {
protocol?: string;
};
const sourceCapabilities: Array<{ key: keyof Pick<VehicleSourceStatus, 'hasRealtime' | 'hasHistory' | 'hasRaw' | 'hasMileage'>; label: string }> = [
{ key: 'hasRealtime', label: '实时' },
{ key: 'hasHistory', label: '历史' },
{ key: 'hasRaw', label: 'RAW' },
{ key: 'hasMileage', label: '里程' }
];
export function VehicleDetail({
vin,
onOpenHistory,
@@ -66,7 +73,7 @@ export function VehicleDetail({
load(nextQuery);
}}>
<Form.Input field="vin" label="VIN" placeholder="输入 VIN" style={{ width: 280 }} />
<Form.Select field="protocol" label="来源协议" placeholder="全部来源" style={{ width: 180 }}>
<Form.Select field="protocol" label="数据来源" placeholder="全部来源" style={{ width: 180 }}>
<Select.Option value="GB32960">GB32960</Select.Option>
<Select.Option value="JT808">JT808</Select.Option>
<Select.Option value="YUTONG_MQTT">YUTONG_MQTT</Select.Option>
@@ -91,7 +98,7 @@ export function VehicleDetail({
{ key: 'OEM', value: summary?.oem || identity?.oem || '-' },
{ key: '在线', value: <StatusTag status={online ? 'ok' : 'offline'} /> },
{
key: '来源协议',
key: '数据来源',
value: protocols.length > 0 ? <Space>{protocols.map((item) => <Tag key={item} color={item === summary?.primaryProtocol ? 'blue' : 'grey'}>{item}</Tag>)}</Space> : '-'
},
{ key: '在线来源', value: summary ? `${summary.onlineSourceCount}/${summary.sourceCount}` : '-' },
@@ -103,7 +110,34 @@ export function VehicleDetail({
</div>
</Card>
<Card bordered title="来源状态" style={{ marginTop: 16 }}>
<Card bordered title="数据来源覆盖" style={{ marginTop: 16 }}>
{detail?.sourceStatus?.length ? (
<div className="vp-source-grid">
{detail.sourceStatus.map((source) => (
<div key={source.protocol} className="vp-source-card">
<div className="vp-source-card-head">
<Space>
<Tag color={source.protocol === summary?.primaryProtocol ? 'blue' : 'grey'}>{source.protocol}</Tag>
<StatusTag status={source.online ? 'ok' : 'offline'} />
</Space>
<Typography.Text type="tertiary">{source.lastSeen || '无上报'}</Typography.Text>
</div>
<Space spacing={4} wrap>
{sourceCapabilities.map((item) => (
<Tag key={item.key} color={source[item.key] ? 'green' : 'grey'}>
{item.label}{source[item.key] ? '有' : '无'}
</Tag>
))}
</Space>
</div>
))}
</div>
) : (
<Typography.Text type="secondary"></Typography.Text>
)}
</Card>
<Card bordered title="来源诊断明细" style={{ marginTop: 16 }}>
<Table
loading={loading}
pagination={false}

View File

@@ -167,6 +167,28 @@ body {
align-items: center;
}
.vp-source-grid {
display: grid;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 12px;
}
.vp-source-card {
min-height: 86px;
padding: 12px;
border: 1px solid var(--vp-border);
border-radius: var(--vp-radius);
background: #fbfcff;
}
.vp-source-card-head {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
margin-bottom: 12px;
}
.vp-realtime-strip {
display: grid;
grid-template-columns: repeat(6, minmax(0, 1fr));