diff --git a/vehicle-data-platform/apps/web/src/v2/pages/OperationsPage.test.tsx b/vehicle-data-platform/apps/web/src/v2/pages/OperationsPage.test.tsx index 644f74bc..7b44aa54 100644 --- a/vehicle-data-platform/apps/web/src/v2/pages/OperationsPage.test.tsx +++ b/vehicle-data-platform/apps/web/src/v2/pages/OperationsPage.test.tsx @@ -356,13 +356,14 @@ test('fuzzy searches a vehicle and renders all source diagnosis evidence', async expect(await screen.findByText('当前推荐 G7')).toBeInTheDocument(); expect(screen.getByText('1 个来源')).toBeInTheDocument(); expect(screen.getByRole('group', { name: '车辆来源诊断概览' })).toHaveClass('v2-source-summary-region'); - expect(document.querySelector('.v2-source-summary-region > .v2-source-summary.semi-card-group')).toBeInTheDocument(); - expect(document.querySelectorAll('.v2-source-summary-card.semi-card')).toHaveLength(4); - expect(document.querySelector('.v2-source-summary-card.is-vehicle')).toHaveTextContent('粤A00001'); - expect(document.querySelector('.v2-source-summary-card.is-recommended')).toHaveTextContent('G7'); - expect(document.querySelector('.v2-source-summary-card.is-source-health')).toHaveClass('is-warning'); - expect(document.querySelector('.v2-source-summary-card.is-policy')).toHaveTextContent('v1'); - expect(document.querySelectorAll('.v2-source-summary-card')[1]?.querySelector('.semi-tag')).toHaveTextContent('JT808'); + const sourceSummary = screen.getByRole('list', { name: '车辆来源诊断概览' }); + expect(sourceSummary.closest('.v2-source-summary-rail')).toHaveClass('v2-workspace-metric-rail', 'is-queue'); + expect(sourceSummary.querySelectorAll(':scope > [role="listitem"]')).toHaveLength(4); + expect(sourceSummary).toHaveTextContent('来源状态1 / 1位置冲突 233m'); + expect(sourceSummary).toHaveTextContent('当前推荐G7JT808'); + expect(sourceSummary).toHaveTextContent('车辆粤A00001VIN001'); + expect(sourceSummary).toHaveTextContent('策略版本v1尚无人工调整'); + expect(sourceSummary.querySelector(':scope > [role="listitem"]')).toHaveClass('is-warning', 'is-primary'); expect(screen.getByText('推荐说明').closest('.semi-card')).toHaveClass('v2-source-recommendation'); expect(screen.getByText('最近策略审计').closest('.semi-card')).toHaveClass('v2-source-audit'); expect(screen.getByText('终端 133****0001')).toBeInTheDocument(); @@ -455,8 +456,8 @@ test('allows provider maintenance but keeps canonical source policy read only', expect(document.querySelector('.v2-source-mobile-card.semi-card')).toBeInTheDocument(); expect(document.querySelector('.v2-source-mobile-descriptions.semi-descriptions')).toBeInTheDocument(); expect(screen.getByRole('group', { name: '车辆来源诊断概览' })).toHaveClass('v2-source-summary-region'); - expect(document.querySelector('.v2-source-summary-region > .v2-source-summary.semi-card-group')).toBeInTheDocument(); - expect(document.querySelectorAll('.v2-source-summary-card.semi-card')).toHaveLength(4); + expect(screen.getByRole('list', { name: '车辆来源诊断概览' }).closest('.v2-source-summary-rail')).toHaveClass('v2-workspace-metric-rail', 'is-queue'); + expect(screen.getByRole('list', { name: '车辆来源诊断概览' }).querySelectorAll(':scope > [role="listitem"]')).toHaveLength(4); expect(screen.getByText('优先级 100')).toBeInTheDocument(); expect(screen.queryByLabelText('JT808 优先级')).not.toBeInTheDocument(); fireEvent.click(screen.getByRole('button', { name: '维护提供方' })); diff --git a/vehicle-data-platform/apps/web/src/v2/pages/OperationsPage.tsx b/vehicle-data-platform/apps/web/src/v2/pages/OperationsPage.tsx index 526a8f7f..d69c3554 100644 --- a/vehicle-data-platform/apps/web/src/v2/pages/OperationsPage.tsx +++ b/vehicle-data-platform/apps/web/src/v2/pages/OperationsPage.tsx @@ -315,6 +315,36 @@ function SourceDiagnosticWorkspace() { onSelect={choose} footer={(candidates.data?.total ?? 0) > 20 ? <>第 {Math.floor(candidateOffset / 20) + 1} / {Math.ceil((candidates.data?.total ?? 0) / 20)} 页
: undefined} /> : null; + const sourceSummaryItems: WorkspaceQueueMetricRailItem[] = data ? [ + { + label: '来源状态', + value: `${data.evidence.locationSources.filter((item) => item.online).length} / ${data.evidence.locationSources.length}`, + note: data.evidence.locationConflict ? `位置冲突 ${number(data.evidence.conflictDistanceM)}m` : '未发现实时位置冲突', + tone: data.evidence.locationConflict ? 'warning' : 'success', + emphasis: 'primary' + }, + { + label: '当前推荐', + value: data.evidence.recommendedLocationLabel || '暂无推荐', + note: data.evidence.recommendedLocationProtocol || '尚无推荐协议', + tone: 'primary', + emphasis: 'primary' + }, + { + label: '车辆', + value: data.evidence.plate || selected?.plate || '未绑定车牌', + note: data.evidence.vin, + tone: 'neutral', + emphasis: 'secondary' + }, + { + label: '策略版本', + value: `v${data.policy.version}`, + note: data.policy.updatedAt ? <>{data.policy.updatedBy} · : '尚无人工调整', + tone: 'neutral', + emphasis: 'secondary' + } + ] : []; return <> {mobileLayout ?
正在读取来源证据只查询当前车辆,不会扫描整车队。
: null} {data ? <>
- - 车辆{data.evidence.plate || selected?.plate || '未绑定车牌'}{data.evidence.vin} - 当前推荐{data.evidence.recommendedLocationLabel || '暂无推荐'}{data.evidence.recommendedLocationProtocol || '—'} - 来源状态{data.evidence.locationSources.filter((item) => item.online).length} / {data.evidence.locationSources.length} 在线{data.evidence.locationConflict ? `位置冲突 ${number(data.evidence.conflictDistanceM)}m` : '未发现实时位置冲突'} - 策略版本v{data.policy.version}{data.policy.updatedAt ? <>{data.policy.updatedBy} · : '尚无人工调整'} - +
推荐说明{data.refreshHint}

{data.recommendationReason}

{!editable ?
只读权限当前账号可查看诊断证据;只有管理员可以调整启停和优先级。
: null} diff --git a/vehicle-data-platform/apps/web/src/v2/productionEntry.test.ts b/vehicle-data-platform/apps/web/src/v2/productionEntry.test.ts index 1d24d486..304b8f5e 100644 --- a/vehicle-data-platform/apps/web/src/v2/productionEntry.test.ts +++ b/vehicle-data-platform/apps/web/src/v2/productionEntry.test.ts @@ -356,10 +356,10 @@ describe('V2 production entry', () => { expect(corePageSources.OperationsPage).not.toContain('
生产数据模式
'); expect(corePageSources.OperationsPage).toContain(' span.is-primary.is-warning { + background: linear-gradient(145deg, #fff7ea 0%, #fffdf8 100%); + box-shadow: inset 0 -3px #e59a2f; +} + +.v2-source-summary-rail .v2-workspace-metric-list > span.is-primary.is-success { + background: linear-gradient(145deg, #eefaf5 0%, #fbfefd 100%); + box-shadow: inset 0 -3px #20a678; +} + +@media (max-width: 680px) { + .v2-source-summary-rail .v2-workspace-metric-list { + grid-template-columns: repeat(2, minmax(0, 1fr)); + } +}