feat(platform): recommend vehicle detail actions
This commit is contained in:
@@ -56,6 +56,14 @@ function sourceServiceRole(source: VehicleSourceStatus, primaryProtocol?: string
|
||||
return { label: '离线证据', color: 'grey' as const };
|
||||
}
|
||||
|
||||
type VehicleServiceAction = {
|
||||
key: string;
|
||||
label: string;
|
||||
description: string;
|
||||
color: 'green' | 'orange' | 'red' | 'blue';
|
||||
onClick: () => void;
|
||||
};
|
||||
|
||||
async function copyVehicleServiceURL() {
|
||||
try {
|
||||
await navigator.clipboard.writeText(`${window.location.origin}${window.location.pathname}${window.location.hash}`);
|
||||
@@ -214,6 +222,56 @@ export function VehicleDetail({
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
const serviceActions = useMemo<VehicleServiceAction[]>(() => {
|
||||
const actions: VehicleServiceAction[] = [];
|
||||
if (!hasResolvedVIN) {
|
||||
actions.push({
|
||||
key: 'identity',
|
||||
label: '维护身份绑定',
|
||||
description: `关键词 ${displayLookupKey} 暂未解析到 VIN,先补齐绑定后再看跨来源服务。`,
|
||||
color: 'orange',
|
||||
onClick: () => onOpenVehicles?.({ bindingStatus: 'unbound' })
|
||||
});
|
||||
}
|
||||
for (const item of missingProtocols) {
|
||||
actions.push({
|
||||
key: `missing-${item}`,
|
||||
label: `补齐 ${item} 来源`,
|
||||
description: `${item} 当前没有形成有效来源证据,会影响这辆车的统一服务可信度。`,
|
||||
color: 'orange',
|
||||
onClick: () => onOpenVehicles?.({ serviceStatus: 'degraded', missingProtocol: item })
|
||||
});
|
||||
}
|
||||
for (const source of detail?.sourceStatus ?? []) {
|
||||
if (source.online) continue;
|
||||
actions.push({
|
||||
key: `offline-${source.protocol}`,
|
||||
label: `排查 ${source.protocol} 离线`,
|
||||
description: `${source.protocol} 最后上报时间:${source.lastSeen || '无上报'}。`,
|
||||
color: 'orange',
|
||||
onClick: () => switchSource(source.protocol)
|
||||
});
|
||||
}
|
||||
if (qualityCount > 0) {
|
||||
actions.push({
|
||||
key: 'quality',
|
||||
label: `处理质量问题 ${qualityCount} 项`,
|
||||
description: '质量问题会影响车辆服务的定位、里程和实时状态可信度。',
|
||||
color: 'orange',
|
||||
onClick: () => onOpenQuality?.(qualityFiltersForCurrentVehicle())
|
||||
});
|
||||
}
|
||||
if (actions.length === 0 && hasResolvedVIN) {
|
||||
actions.push({
|
||||
key: 'healthy',
|
||||
label: '查看实时服务',
|
||||
description: '当前没有明确处置项,可直接查看该车统一实时状态。',
|
||||
color: 'green',
|
||||
onClick: () => onOpenRealtime(resolvedVIN, activeProtocol)
|
||||
});
|
||||
}
|
||||
return actions;
|
||||
}, [activeProtocol, detail?.sourceStatus, displayLookupKey, hasResolvedVIN, missingProtocols, onOpenQuality, onOpenRealtime, onOpenVehicles, qualityCount, resolvedVIN]);
|
||||
const qualityTable = (
|
||||
<Table
|
||||
loading={loading}
|
||||
@@ -315,6 +373,22 @@ export function VehicleDetail({
|
||||
</Card>
|
||||
) : null}
|
||||
|
||||
<Card bordered title="服务处置建议" style={{ marginTop: 16 }}>
|
||||
<div className="vp-action-grid">
|
||||
{serviceActions.map((item) => (
|
||||
<div key={item.key} className="vp-action-item">
|
||||
<div>
|
||||
<Tag color={item.color}>{item.label}</Tag>
|
||||
<Typography.Text type="secondary" style={{ display: 'block', marginTop: 8 }}>{item.description}</Typography.Text>
|
||||
</div>
|
||||
<Button size="small" theme="light" type={item.color === 'red' ? 'danger' : item.color === 'green' ? 'primary' : 'warning'} onClick={item.onClick}>
|
||||
{item.label}
|
||||
</Button>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
<Card bordered style={{ marginTop: 16 }}>
|
||||
<div className="vp-vehicle-summary">
|
||||
<Descriptions
|
||||
|
||||
Reference in New Issue
Block a user