feat(platform): customerize realtime monitoring view

This commit is contained in:
lingniu
2026-07-05 00:58:16 +08:00
parent 245af2c293
commit b824289cf6
2 changed files with 44 additions and 44 deletions

View File

@@ -34,7 +34,7 @@ function vehicleServiceStatus(row: VehicleRealtimeRow) {
}
function sourceEvidenceText(row: VehicleRealtimeRow) {
return `${row.onlineSourceCount}/${row.sourceCount} 来源在线`;
return `${row.onlineSourceCount}/${row.sourceCount} 通道在线`;
}
function formatPercent(value: number) {
@@ -154,11 +154,11 @@ const realtimeExportColumns: CsvColumn<VehicleRealtimeRow>[] = [
{ title: '车牌', value: (row) => row.plate },
{ title: '手机号', value: (row) => row.phone },
{ title: 'OEM', value: (row) => row.oem },
{ title: '主来源', value: (row) => row.primaryProtocol },
{ title: '来源列表', value: (row) => row.protocols?.join('|') },
{ title: '主通道', value: (row) => row.primaryProtocol },
{ title: '数据通道', value: (row) => row.protocols?.join('|') },
{ title: '在线', value: (row) => row.online ? '在线' : '离线' },
{ title: '车辆服务状态', value: (row) => vehicleServiceStatus(row).label },
{ title: '来源在线', value: (row) => sourceEvidenceText(row) },
{ title: '通道在线', value: (row) => sourceEvidenceText(row) },
{ title: '经度', value: (row) => row.longitude },
{ title: '纬度', value: (row) => row.latitude },
{ title: '速度km/h', value: (row) => row.speedKmh },
@@ -187,7 +187,7 @@ function realtimeExportFileName(filters: Record<string, string>) {
function realtimeFilterSummary(filters: Record<string, string>) {
return [
filters.keyword ? `关键词:${filters.keyword}` : '',
filters.protocol ? `数据来源${filters.protocol}` : '',
filters.protocol ? `数据通道${filters.protocol}` : '',
filters.online ? `在线:${onlineLabel[filters.online] ?? filters.online}` : '',
filters.serviceStatus ? `服务状态:${serviceStatusLabel[filters.serviceStatus] ?? filters.serviceStatus}` : ''
].filter(Boolean);
@@ -265,13 +265,13 @@ function realtimeIssueLabels(row: VehicleRealtimeRow) {
function realtimeIssueAction(row: VehicleRealtimeRow) {
const issues = realtimeIssueLabels(row).join('');
if (!isValidCoordinate(row)) {
return `核对${row.primaryProtocol || '实时来源'}定位字段解析和平台转发`;
return `核对${row.primaryProtocol || '实时数据'}定位字段解析和平台转发`;
}
if (dataFreshness(row).stale) {
return `确认${row.primaryProtocol || '实时来源'}是否持续上报,必要时检查接入链路`;
return `确认${row.primaryProtocol || '实时数据'}是否持续上报,必要时查看告警事件`;
}
if (hasSourceIssue(row)) {
return '补齐离线来源,确认多来源实时状态一致';
return '处理离线数据通道,确认车辆实时状态一致';
}
return issues === '暂无异常' ? '持续观察' : '结合车辆服务详情继续排查';
}
@@ -385,12 +385,12 @@ function realtimeDutyHandoffText({
lines.push(
`${index + 1}. ${row.plate || '-'} / ${row.vin} / ${protocol || '-'} / ${status.label}`,
` 最后上报:${row.lastSeen || '-'};新鲜度:${freshness.label};位置:${isValidCoordinate(row) ? `${row.longitude},${row.latitude}` : '无有效坐标'};速度:${row.speedKmh ?? '-'} km/hSOC${row.socPercent ?? '-'}%`,
` 来源${sourceEvidenceText(row)};问题:${realtimeIssueLabels(row).join('')}`,
` 数据覆盖${sourceEvidenceText(row)};问题:${realtimeIssueLabels(row).join('')}`,
` 车辆服务:${appURL(buildAppHash({ page: 'detail', keyword: row.vin, protocol }))}`,
` 实时监控:${appURL(buildAppHash({ page: 'realtime', keyword: row.vin, protocol }))}`,
` 轨迹回放:${appURL(buildAppHash({ page: 'history', keyword: row.vin, protocol }))}`,
` 里程统计:${appURL(buildAppHash({ page: 'mileage', keyword: row.vin, protocol }))}`,
` 历史RAW${protocol ? rawFrameAPIURL(row, protocol) : '-'}`
` 原始记录${protocol ? rawFrameAPIURL(row, protocol) : '-'}`
);
});
return lines.join('\n');
@@ -431,7 +431,7 @@ function realtimeImpactReportText({
`定位影响:${locatedCount.toLocaleString()} 辆有有效坐标`,
`服务影响:${degradedCount.toLocaleString()} 辆降级 / ${staleCount.toLocaleString()} 辆超时`,
`地图能力:${amapConfigured ? '高德地图可用' : '高德地图未配置,使用坐标预览'}`,
`建议动作:${impactLevel === '实时稳定' ? '保持监控并关注告警队列' : '优先处理离线、超时和来源不完整车辆,并回到轨迹/RAW证据复核'}`,
`建议动作:${impactLevel === '实时稳定' ? '保持监控并关注告警队列' : '优先处理离线、超时和来源不完整车辆,并回到轨迹和原始记录复核'}`,
`实时监控:${appURL(buildAppHash({ page: 'realtime', protocol: filters.protocol, filters }))}`
].join('\n');
}
@@ -728,7 +728,7 @@ export function Realtime({
applyFilters(nextFilters);
}} style={{ marginBottom: 12 }}>
<Form.Input field="keyword" label="车辆关键词" placeholder="VIN / 车牌 / 手机号 / OEM" style={{ width: 260 }} />
<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>
@@ -815,7 +815,7 @@ export function Realtime({
{staleCount > 0 ? <Tag color="red"> {staleCount.toLocaleString()}</Tag> : <Tag color="green"></Tag>}
{sourceIssueRows.length > 0 && onOpenQuality ? (
<Button size="small" theme="light" type="warning" onClick={() => onOpenQuality({ serviceStatus: 'degraded' })}>
{sourceIssueRows.length.toLocaleString()}
{sourceIssueRows.length.toLocaleString()}
</Button>
) : null}
</div>
@@ -825,7 +825,7 @@ export function Realtime({
<div className="vp-current-service-title"></div>
<div className="vp-source-coverage-grid">
{sourceCoverageRows.length === 0 ? (
<Typography.Text type="tertiary"></Typography.Text>
<Typography.Text type="tertiary"></Typography.Text>
) : sourceCoverageRows.map((item) => (
<button
key={item.protocol}
@@ -894,7 +894,7 @@ export function Realtime({
<Tag color={vehicleServiceStatus(selectedMapRow).color}>{vehicleServiceStatus(selectedMapRow).label}</Tag>
<Tag color={dataFreshness(selectedMapRow).color}>{dataFreshness(selectedMapRow).label}</Tag>
<Tag color={selectedMapRow.online ? 'green' : 'orange'}>{selectedMapRow.online ? '在线' : '离线'}</Tag>
<Tag color="blue">{selectedMapRow.primaryProtocol || '未知来源'}</Tag>
<Tag color="blue">{selectedMapRow.primaryProtocol || '未知通道'}</Tag>
</Space>
<Typography.Title heading={6} style={{ margin: '8px 0 0' }}>
{selectedMapRow.plate || selectedMapRow.vin}
@@ -915,7 +915,7 @@ export function Realtime({
</div>
) : null}
<div className="vp-map-integration-panel">
<div className="vp-map-service-queue-title"></div>
<div className="vp-map-service-queue-title"></div>
{mapIntegrationRows.map((item) => (
<div key={item.label} className="vp-map-integration-row">
<div>
@@ -927,9 +927,9 @@ export function Realtime({
))}
</div>
<div className="vp-source-consistency-panel">
<div className="vp-map-service-queue-title"></div>
<div className="vp-map-service-queue-title"></div>
{sourceIssueRows.length === 0 ? (
<Typography.Text type="tertiary"></Typography.Text>
<Typography.Text type="tertiary"></Typography.Text>
) : (
sourceIssueRows.map((row) => {
const status = vehicleServiceStatus(row);
@@ -946,16 +946,16 @@ export function Realtime({
<Tag color="blue">{sourceEvidenceText(row)}</Tag>
<Tag color={dataFreshness(row).color}>{dataFreshness(row).label}</Tag>
{sourceIssueTags(row).map((item) => (
<Tag key={item} color="orange">{item}</Tag>
<Tag key={item} color="orange">{item}</Tag>
))}
</Space>
<Typography.Text type="secondary" size="small">
{row.serviceStatus?.detail || sourceEvidenceText(row)}
{row.serviceStatus?.detail || sourceEvidenceText(row)}
</Typography.Text>
<div className="vp-map-service-item-footer">
<Typography.Text type="tertiary" size="small">{row.lastSeen || '-'}</Typography.Text>
<Space spacing={4} wrap>
<Button size="small" disabled={!onOpenQuality} onClick={() => openQualityEvidence(row)}></Button>
<Button size="small" disabled={!onOpenQuality} onClick={() => openQualityEvidence(row)}></Button>
<Button size="small" onClick={() => onOpenVehicle(row.vin, filters.protocol || row.primaryProtocol)}></Button>
</Space>
</div>
@@ -965,7 +965,7 @@ export function Realtime({
)}
</div>
<div className="vp-map-service-queue">
<div className="vp-map-service-queue-title"></div>
<div className="vp-map-service-queue-title"></div>
{mapServiceRows.length === 0 ? (
<Typography.Text type="tertiary"></Typography.Text>
) : (
@@ -1006,8 +1006,8 @@ export function Realtime({
<Space spacing={4} wrap>
<Button size="small" onClick={() => window.open(amapMarkerURL(row), '_blank', 'noopener,noreferrer')}></Button>
<Button size="small" onClick={() => onOpenHistory?.(row.vin, filters.protocol || row.primaryProtocol)}></Button>
<Button size="small" disabled={!onOpenQuality} onClick={() => openQualityEvidence(row)}></Button>
<Button size="small" onClick={() => onOpenVehicle(row.vin, filters.protocol || row.primaryProtocol)}></Button>
<Button size="small" disabled={!onOpenQuality} onClick={() => openQualityEvidence(row)}></Button>
<Button size="small" onClick={() => onOpenVehicle(row.vin, filters.protocol || row.primaryProtocol)}></Button>
</Space>
</div>
</div>
@@ -1117,13 +1117,13 @@ export function Realtime({
)
},
{
title: '来源证据',
title: '实时通道',
width: 260,
render: (_: unknown, row: VehicleRealtimeRow) => (
<SourceStatusTags sourceStatus={row.sourceStatus} protocols={row.protocols} lastSeen={row.lastSeen} />
)
},
{ title: '据覆盖', width: 120, render: (_: unknown, row: VehicleRealtimeRow) => sourceEvidenceText(row) },
{ title: '据覆盖', width: 120, render: (_: unknown, row: VehicleRealtimeRow) => sourceEvidenceText(row) },
{ title: '在线', width: 90, render: (_: unknown, row: VehicleRealtimeRow) => <StatusTag status={row.online ? 'ok' : 'offline'} /> },
{ title: '最后时间', dataIndex: 'lastSeen', width: 170 },
{
@@ -1133,7 +1133,7 @@ export function Realtime({
<Space spacing={4} wrap>
<Button disabled={!isValidCoordinate(row)} onClick={() => selectRealtimeRow(row)}></Button>
<Button disabled={!canOpenVehicle(row.vin)} onClick={() => onOpenVehicle(row.vin, filters.protocol || row.primaryProtocol)}></Button>
<Button disabled={!canOpenVehicle(row.vin) || !onOpenQuality} onClick={() => openQualityEvidence(row)}></Button>
<Button disabled={!canOpenVehicle(row.vin) || !onOpenQuality} onClick={() => openQualityEvidence(row)}></Button>
</Space>
)
}