feat(platform): add focus vehicle service card

This commit is contained in:
lingniu
2026-07-04 21:42:04 +08:00
parent 5d57efabea
commit f0f7bc546e
3 changed files with 179 additions and 3 deletions

View File

@@ -48,6 +48,20 @@ type DashboardSnapshotRow = {
detail: string;
};
type FocusVehicleService = {
label: string;
lookupKey: string;
protocol?: string;
reason: string;
statusLabel: string;
statusColor: 'green' | 'orange' | 'red' | 'grey' | 'blue';
realtimeEvidence: string;
historyEvidence: string;
alertEvidence: string;
statisticEvidence: string;
issueType?: string;
};
const dashboardSnapshotColumns: CsvColumn<DashboardSnapshotRow>[] = [
{ title: '模块', value: (row) => row.section },
{ title: '指标', value: (row) => row.item },
@@ -163,6 +177,43 @@ function priorityIssueNotificationText(row: QualityIssueRow) {
].join('\n');
}
function focusVehicleFromIssue(row: QualityIssueRow): FocusVehicleService {
const lookup = qualityIssueVehicleLookup(row);
const filters = priorityIssueEvidenceFilters(row);
const label = priorityIssueVehicleLabel(row);
return {
label,
lookupKey: lookup.key,
protocol: row.protocol,
reason: `${row.severity === 'error' ? 'P0' : 'P1'} ${qualityIssueLabel(row.issueType)}`,
statusLabel: row.severity === 'error' ? '优先处置' : '持续跟踪',
statusColor: row.severity === 'error' ? 'red' : 'orange',
realtimeEvidence: `来源 ${qualityProtocolLabel(row.protocol)} / 最后 ${row.lastSeen || '-'}`,
historyEvidence: filters.dateFrom ? `${filters.dateFrom}${filters.dateTo}` : '待确认时间窗',
alertEvidence: row.detail || qualityIssueLabel(row.issueType),
statisticEvidence: '统计查询需回溯车辆口径',
issueType: row.issueType
};
}
function focusVehicleFromRealtime(row: VehicleRealtimeRow): FocusVehicleService {
const status = rowServiceStatus(row);
const label = [row.plate?.trim(), row.vin?.trim()].filter(Boolean).join(' / ') || row.phone || '-';
const location = hasValidCoordinate(row) ? `${row.longitude.toFixed(6)}, ${row.latitude.toFixed(6)}` : '无有效坐标';
return {
label,
lookupKey: row.vin || row.phone || row.plate,
protocol: row.primaryProtocol,
reason: row.online ? '最新在线车辆' : '最新车辆',
statusLabel: status.label,
statusColor: status.color,
realtimeEvidence: `${row.onlineSourceCount}/${row.sourceCount} 来源在线 / ${row.lastSeen || '-'}`,
historyEvidence: location,
alertEvidence: row.onlineSourceCount < row.sourceCount ? '来源不完整,建议检查缺失协议' : '暂无高优先级告警',
statisticEvidence: `速度 ${formatCount(row.speedKmh)} km/h / 里程 ${formatCount(row.totalMileageKm)} km`
};
}
function sourceConsistencyAction(row: VehicleCoverageRow, onFilter: (filters: Record<string, string>) => void) {
const consistency = row.sourceConsistency;
if (!consistency) {
@@ -316,6 +367,7 @@ export function Dashboard({
const highPriorityIssue = qualityIssues.find((item) => item.severity === 'error') ?? qualityIssues[0];
const highPriorityLookup = highPriorityIssue ? qualityIssueVehicleLookup(highPriorityIssue) : undefined;
const highPriorityEvidenceFilters = highPriorityIssue ? priorityIssueEvidenceFilters(highPriorityIssue) : undefined;
const focusVehicle = highPriorityIssue ? focusVehicleFromIssue(highPriorityIssue) : locations[0] ? focusVehicleFromRealtime(locations[0]) : undefined;
const unhealthyLinkCount = (summary?.linkHealth ?? []).filter((item) => item.status !== 'ok').length;
const commandMapPoints: VehicleMapPoint[] = locations.map((row, index) => ({
id: row.vin || `${row.primaryProtocol || 'source'}-${index}`,
@@ -602,6 +654,30 @@ export function Dashboard({
];
copyText(lines.join('\n'), '功能蓝图');
};
const copyFocusVehicleService = () => {
if (!focusVehicle) {
Toast.warning('当前没有重点车辆服务可复制');
return;
}
const commonFilters = { keyword: focusVehicle.lookupKey, protocol: focusVehicle.protocol ?? '' };
const lines = [
'【重点车辆服务处置卡】',
`车辆:${focusVehicle.label}`,
`原因:${focusVehicle.reason}`,
`服务状态:${focusVehicle.statusLabel}`,
`实时证据:${focusVehicle.realtimeEvidence}`,
`轨迹证据:${focusVehicle.historyEvidence}`,
`告警证据:${focusVehicle.alertEvidence}`,
`统计证据:${focusVehicle.statisticEvidence}`,
`车辆服务:${appURL(buildAppHash({ page: 'detail', keyword: focusVehicle.lookupKey, protocol: focusVehicle.protocol }))}`,
`实时监控:${appURL(buildAppHash({ page: 'realtime', keyword: focusVehicle.lookupKey, protocol: focusVehicle.protocol }))}`,
`轨迹回放:${appURL(buildAppHash({ page: 'history', keyword: focusVehicle.lookupKey, protocol: focusVehicle.protocol }))}`,
`RAW证据${appURL(buildAppHash({ page: 'history-query', keyword: focusVehicle.lookupKey, protocol: focusVehicle.protocol, filters: { ...commonFilters, tab: 'raw', includeFields: 'true' } }))}`,
`统计查询:${appURL(buildAppHash({ page: 'mileage', keyword: focusVehicle.lookupKey, protocol: focusVehicle.protocol }))}`,
`告警事件:${appURL(buildAppHash({ page: 'alert-events', keyword: focusVehicle.lookupKey, protocol: focusVehicle.protocol, filters: focusVehicle.issueType ? { issueType: focusVehicle.issueType } : {} }))}`
];
copyText(lines.join('\n'), '重点车辆服务处置卡');
};
const buildDashboardSnapshotRows = () => {
const unhealthyLinks = (summary?.linkHealth ?? []).filter((item) => item.status !== 'ok');
const priorityAction = serviceActionQueue[0];
@@ -738,6 +814,42 @@ export function Dashboard({
<Button size="small" theme="light" type="primary" onClick={exportDashboardSnapshot}> CSV</Button>
</Space>
</Card>
{focusVehicle ? (
<Card
bordered
title={<Space><span></span><Tag color={focusVehicle.statusColor}>{focusVehicle.statusLabel}</Tag><Button size="small" onClick={copyFocusVehicleService}></Button></Space>}
style={{ marginBottom: 16 }}
>
<div className="vp-focus-service">
<div className="vp-focus-service-main">
<Tag color="blue">{focusVehicle.reason}</Tag>
<Typography.Title heading={5} style={{ margin: '8px 0 4px' }}>{focusVehicle.label}</Typography.Title>
<Typography.Text type="secondary">RAW</Typography.Text>
</div>
<div className="vp-focus-service-evidence">
{[
{ label: '实时证据', value: focusVehicle.realtimeEvidence },
{ label: '轨迹证据', value: focusVehicle.historyEvidence },
{ label: '告警证据', value: focusVehicle.alertEvidence },
{ label: '统计证据', value: focusVehicle.statisticEvidence }
].map((item) => (
<div key={item.label} className="vp-focus-service-evidence-item">
<span>{item.label}</span>
<strong>{item.value}</strong>
</div>
))}
</div>
<Space wrap>
<Button size="small" theme="solid" type="primary" aria-label="重点车辆 车辆服务" disabled={!focusVehicle.lookupKey} onClick={() => focusVehicle.lookupKey && onOpenVehicle(focusVehicle.lookupKey, focusVehicle.protocol)}></Button>
<Button size="small" theme="light" aria-label="重点车辆 实时监控" disabled={!focusVehicle.lookupKey} onClick={() => onOpenRealtime({ keyword: focusVehicle.lookupKey, protocol: focusVehicle.protocol ?? '' })}></Button>
<Button size="small" theme="light" aria-label="重点车辆 轨迹回放" disabled={!focusVehicle.lookupKey} onClick={() => onOpenHistory({ keyword: focusVehicle.lookupKey, protocol: focusVehicle.protocol ?? '' })}></Button>
<Button size="small" theme="light" aria-label="重点车辆 RAW证据" disabled={!focusVehicle.lookupKey} onClick={() => onOpenHistory({ keyword: focusVehicle.lookupKey, protocol: focusVehicle.protocol ?? '', tab: 'raw', includeFields: 'true' })}>RAW证据</Button>
<Button size="small" theme="light" aria-label="重点车辆 统计查询" disabled={!focusVehicle.lookupKey} onClick={() => onOpenMileage({ keyword: focusVehicle.lookupKey, protocol: focusVehicle.protocol ?? '' })}></Button>
<Button size="small" theme="light" type="warning" aria-label="重点车辆 告警事件" onClick={() => onOpenQuality({ keyword: focusVehicle.lookupKey, protocol: focusVehicle.protocol ?? '', ...(focusVehicle.issueType ? { issueType: focusVehicle.issueType } : {}) })}></Button>
</Space>
</div>
</Card>
) : null}
<Card bordered title="车辆服务作业台" style={{ marginBottom: 16 }}>
<div className="vp-workbench-grid">
{operationWorkbench.map((item) => (