feat(platform): summarize vehicle service verdict
This commit is contained in:
@@ -69,6 +69,13 @@ type VehicleServiceAction = {
|
||||
onClick: () => void;
|
||||
};
|
||||
|
||||
type VehicleServiceConclusion = {
|
||||
status: string;
|
||||
color: 'green' | 'orange' | 'red';
|
||||
risk: string;
|
||||
nextStep: string;
|
||||
};
|
||||
|
||||
async function copyVehicleServiceURL() {
|
||||
try {
|
||||
await navigator.clipboard.writeText(`${window.location.origin}${window.location.pathname}${window.location.hash}`);
|
||||
@@ -230,6 +237,62 @@ export function VehicleDetail({
|
||||
onQueryChange?.(nextQuery.keyword, nextQuery.protocol);
|
||||
load(nextQuery);
|
||||
};
|
||||
const serviceConclusion = useMemo<VehicleServiceConclusion>(() => {
|
||||
if (!hasResolvedVIN) {
|
||||
return {
|
||||
status: '不可服务',
|
||||
color: 'red',
|
||||
risk: '身份未归并到 VIN',
|
||||
nextStep: '先维护车辆身份绑定,再使用实时、历史和里程服务。'
|
||||
};
|
||||
}
|
||||
if (sourceCount <= 0) {
|
||||
return {
|
||||
status: '不可服务',
|
||||
color: 'red',
|
||||
risk: '没有任何来源证据',
|
||||
nextStep: '优先确认平台转发、端口、订阅和绑定配置。'
|
||||
};
|
||||
}
|
||||
if (onlineSourceCount <= 0) {
|
||||
return {
|
||||
status: '不可服务',
|
||||
color: 'red',
|
||||
risk: '全部来源离线',
|
||||
nextStep: '先恢复至少一个实时来源,再判断车辆状态。'
|
||||
};
|
||||
}
|
||||
if (qualityCount > 0) {
|
||||
return {
|
||||
status: '降级可服务',
|
||||
color: 'orange',
|
||||
risk: `${qualityCount} 项质量问题影响可信度`,
|
||||
nextStep: '先处理质量问题,再将该车用于 BI、告警或对外查询。'
|
||||
};
|
||||
}
|
||||
if (missingProtocols.length > 0) {
|
||||
return {
|
||||
status: '降级可服务',
|
||||
color: 'orange',
|
||||
risk: `缺少 ${missingProtocols.join('、')} 来源`,
|
||||
nextStep: '车辆仍可查询,但需要补齐缺失来源后再做跨来源校验。'
|
||||
};
|
||||
}
|
||||
if (isFiniteNumber(consistencyMileageDelta) && consistencyMileageDelta > 1) {
|
||||
return {
|
||||
status: '降级可服务',
|
||||
color: 'orange',
|
||||
risk: `跨来源里程差异 ${formatCompactNumber(consistencyMileageDelta, ' km')}`,
|
||||
nextStep: '核对各来源总里程口径,确认统计使用的主来源。'
|
||||
};
|
||||
}
|
||||
return {
|
||||
status: '可服务',
|
||||
color: 'green',
|
||||
risk: '身份、来源和核心实时证据可用',
|
||||
nextStep: '可进入实时、历史、RAW 和里程查询继续分析。'
|
||||
};
|
||||
}, [consistencyMileageDelta, hasResolvedVIN, missingProtocols, onlineSourceCount, qualityCount, sourceCount]);
|
||||
const qualityFiltersForCurrentVehicle = () => ({
|
||||
keyword: resolvedVIN,
|
||||
...(activeProtocol ? { protocol: activeProtocol } : {})
|
||||
@@ -379,6 +442,21 @@ export function VehicleDetail({
|
||||
</Card>
|
||||
) : null}
|
||||
|
||||
<Card bordered title="车辆服务结论" style={{ marginTop: 16 }}>
|
||||
<div className="vp-conclusion-grid">
|
||||
{[
|
||||
{ label: '服务判断', value: <Tag color={serviceConclusion.color}>{serviceConclusion.status}</Tag> },
|
||||
{ label: '主要风险', value: serviceConclusion.risk },
|
||||
{ label: '下一步', value: serviceConclusion.nextStep }
|
||||
].map((item) => (
|
||||
<div key={item.label} className="vp-conclusion-item">
|
||||
<div className="vp-evidence-label">{item.label}</div>
|
||||
<div className="vp-evidence-value">{item.value}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</Card>
|
||||
|
||||
{overview || consistency ? (
|
||||
<Card bordered title="车辆服务证据链" style={{ marginTop: 16 }}>
|
||||
<div className="vp-evidence-grid">
|
||||
|
||||
@@ -295,6 +295,20 @@ body {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.vp-conclusion-grid {
|
||||
display: grid;
|
||||
grid-template-columns: 160px minmax(180px, 1fr) minmax(260px, 1.4fr);
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.vp-conclusion-item {
|
||||
min-height: 78px;
|
||||
padding: 12px;
|
||||
border: 1px solid var(--vp-border);
|
||||
border-radius: var(--vp-radius);
|
||||
background: #fbfcff;
|
||||
}
|
||||
|
||||
.vp-evidence-item {
|
||||
min-height: 70px;
|
||||
padding: 12px;
|
||||
@@ -377,3 +391,11 @@ body {
|
||||
.semi-navigation-item-text {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.vp-evidence-grid,
|
||||
.vp-action-grid,
|
||||
.vp-conclusion-grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5534,6 +5534,10 @@ test('shows actionable vehicle service recommendations on detail', async () => {
|
||||
|
||||
render(<App />);
|
||||
|
||||
expect(await screen.findByText('车辆服务结论')).toBeInTheDocument();
|
||||
expect(screen.getByText('降级可服务')).toBeInTheDocument();
|
||||
expect(screen.getByText('3 项质量问题影响可信度')).toBeInTheDocument();
|
||||
expect(screen.getByText('先处理质量问题,再将该车用于 BI、告警或对外查询。')).toBeInTheDocument();
|
||||
expect(await screen.findByText('服务处置建议')).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: '补齐 YUTONG_MQTT 来源' })).toBeInTheDocument();
|
||||
expect(screen.getByRole('button', { name: '处理质量问题 3 项' })).toBeInTheDocument();
|
||||
|
||||
Reference in New Issue
Block a user