feat(platform): summarize vehicle source coverage
This commit is contained in:
@@ -11,6 +11,13 @@ type VehicleQuery = {
|
|||||||
protocol?: string;
|
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({
|
export function VehicleDetail({
|
||||||
vin,
|
vin,
|
||||||
onOpenHistory,
|
onOpenHistory,
|
||||||
@@ -66,7 +73,7 @@ export function VehicleDetail({
|
|||||||
load(nextQuery);
|
load(nextQuery);
|
||||||
}}>
|
}}>
|
||||||
<Form.Input field="vin" label="VIN" placeholder="输入 VIN" style={{ width: 280 }} />
|
<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="GB32960">GB32960</Select.Option>
|
||||||
<Select.Option value="JT808">JT808</Select.Option>
|
<Select.Option value="JT808">JT808</Select.Option>
|
||||||
<Select.Option value="YUTONG_MQTT">YUTONG_MQTT</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: 'OEM', value: summary?.oem || identity?.oem || '-' },
|
||||||
{ key: '在线', value: <StatusTag status={online ? 'ok' : 'offline'} /> },
|
{ 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> : '-'
|
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}` : '-' },
|
{ key: '在线来源', value: summary ? `${summary.onlineSourceCount}/${summary.sourceCount}` : '-' },
|
||||||
@@ -103,7 +110,34 @@ export function VehicleDetail({
|
|||||||
</div>
|
</div>
|
||||||
</Card>
|
</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
|
<Table
|
||||||
loading={loading}
|
loading={loading}
|
||||||
pagination={false}
|
pagination={false}
|
||||||
|
|||||||
@@ -167,6 +167,28 @@ body {
|
|||||||
align-items: center;
|
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 {
|
.vp-realtime-strip {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(6, minmax(0, 1fr));
|
grid-template-columns: repeat(6, minmax(0, 1fr));
|
||||||
|
|||||||
Reference in New Issue
Block a user