From 1be76c1dfa949e899cbc9f37655c2f128d8d5253 Mon Sep 17 00:00:00 2001 From: lingniu Date: Sun, 19 Jul 2026 11:04:34 +0800 Subject: [PATCH] feat: unify vehicle directory summary --- .../web/src/v2/pages/VehiclePage.test.tsx | 15 +- .../src/v2/pages/VehicleSearchWorkspace.tsx | 54 +++++-- .../apps/web/src/v2/productionEntry.test.ts | 4 + .../apps/web/src/v2/styles/workspace.css | 153 ++++++++++++++++++ 4 files changed, 211 insertions(+), 15 deletions(-) diff --git a/vehicle-data-platform/apps/web/src/v2/pages/VehiclePage.test.tsx b/vehicle-data-platform/apps/web/src/v2/pages/VehiclePage.test.tsx index 748b23e2..3439a04a 100644 --- a/vehicle-data-platform/apps/web/src/v2/pages/VehiclePage.test.tsx +++ b/vehicle-data-platform/apps/web/src/v2/pages/VehiclePage.test.tsx @@ -206,9 +206,13 @@ test('shows deduplicated Semi vehicle candidates and opens the selected VIN', as expect(initialQuery).toBeDefined(); expect(initialQuery!.get('limit')).toBe('10'); expect(screen.getByRole('heading', { name: '授权车辆目录', level: 5 })).toBeInTheDocument(); - expect(await screen.findByText('本页 1 辆')).toBeInTheDocument(); - expect(screen.getByText('本页在线 1')).toBeInTheDocument(); - expect(screen.getByText('多源车辆 0')).toBeInTheDocument(); + const directoryMetrics = screen.getByRole('list', { name: '车辆目录摘要' }); + expect(directoryMetrics.closest('.v2-vehicle-directory-metric-rail')).toHaveClass('semi-card', 'v2-workspace-metric-rail', 'is-queue'); + expect(Array.from(directoryMetrics.querySelectorAll(':scope > [role="listitem"] > small')).map((item) => item.textContent)).toEqual(['授权车辆', '本页在线', '多源车辆', '当前页']); + expect(directoryMetrics.querySelector(':scope > [role="listitem"]:nth-child(1)')).toHaveTextContent('授权车辆2辆账号范围'); + expect(directoryMetrics.querySelector(':scope > [role="listitem"]:nth-child(2)')).toHaveTextContent('本页在线1辆本页 1 辆'); + expect(directoryMetrics.querySelector(':scope > [role="listitem"]:nth-child(3)')).toHaveTextContent('多源车辆0辆2+ 协议来源'); + expect(screen.getByRole('button', { name: '刷新车辆目录' })).toHaveClass('semi-button'); expect(view.container.querySelector('.v2-vehicle-directory-table.semi-table-wrapper')).toBeInTheDocument(); expect(view.container.querySelector('.v2-vehicle-search-page')).toHaveClass('v2-vehicle-directory-v3'); expect(view.container.querySelector('.v2-vehicle-directory-profile')).toHaveTextContent('测试品牌13800000000'); @@ -261,8 +265,9 @@ test('keeps the mobile vehicle directory primary and applies search from a Semi const view = render(} />); - expect(await screen.findByText('本页 8 辆')).toBeInTheDocument(); - expect(screen.getAllByRole('listitem')).toHaveLength(8); + const mobileDirectoryMetrics = await screen.findByRole('list', { name: '车辆目录摘要' }); + await waitFor(() => expect(mobileDirectoryMetrics).toHaveTextContent('本页在线6辆本页 8 辆')); + expect(view.container.querySelectorAll('.v2-vehicle-recent-grid [role="listitem"]')).toHaveLength(8); expect(screen.getAllByRole('button', { name: /打开 .* 车辆档案/ })).toHaveLength(8); expect(screen.getAllByText(/GB\/T 32960|JT\/T 808/).length).toBeGreaterThan(0); expect(view.container.querySelector('.v2-vehicle-directory-table')).not.toBeInTheDocument(); diff --git a/vehicle-data-platform/apps/web/src/v2/pages/VehicleSearchWorkspace.tsx b/vehicle-data-platform/apps/web/src/v2/pages/VehicleSearchWorkspace.tsx index c3cf425a..2822fb2a 100644 --- a/vehicle-data-platform/apps/web/src/v2/pages/VehicleSearchWorkspace.tsx +++ b/vehicle-data-platform/apps/web/src/v2/pages/VehicleSearchWorkspace.tsx @@ -18,7 +18,7 @@ import { VehicleCandidateList } from '../shared/VehicleCandidateList'; import { mergeVehicleCandidates } from '../shared/vehicleCandidates'; import { WorkspaceCommandBar } from '../shared/WorkspaceCommandBar'; import { WorkspaceFilterPanel } from '../shared/WorkspaceFilterPanel'; -import { WorkspacePanelHeader } from '../shared/WorkspacePanelHeader'; +import { WorkspaceMetricRail, type WorkspaceQueueMetricRailItem } from '../shared/WorkspaceMetricRail'; const VehicleProfileSyncPanel = lazy(() => import('./VehicleProfileSyncPanel')); @@ -150,6 +150,40 @@ export default function VehicleSearchWorkspace() { const mobileFilterSummary = deferredKeyword ? `${deferredKeyword} · ${candidates.data ? `${candidates.data.total.toLocaleString('zh-CN')} 辆匹配` : '正在查找'}` : candidates.data ? `${candidates.data.total.toLocaleString('zh-CN')} 辆授权车辆` : '正在读取授权范围'; + const directoryMetrics: WorkspaceQueueMetricRailItem[] = [ + { + label: deferredKeyword ? '匹配车辆' : '授权车辆', + value: <>{candidates.data ? formatZhNumber(total) : '—'}, + note: deferredKeyword ? '当前条件' : '账号范围', + tone: 'primary', + emphasis: 'primary' + }, + { + label: '本页在线', + value: <>{candidates.data ? formatZhNumber(onlineOnPage) : '—'}, + note: candidates.data ? `本页 ${formatZhNumber(pageVehicles.length)} 辆` : '正在读取', + tone: 'success', + emphasis: 'primary' + }, + { + label: '多源车辆', + value: <>{candidates.data ? formatZhNumber(multiSourceOnPage) : '—'}, + note: '2+ 协议来源', + tone: 'primary', + emphasis: 'secondary' + }, + { + label: '当前页', + value: candidates.data ? `${page} / ${totalPages}` : '—', + note: `每页 ${pageSize} 辆`, + tone: 'neutral', + emphasis: 'secondary' + } + ]; + const directoryTitle = deferredKeyword ? '匹配车辆' : mobileLayout ? '授权车辆' : '授权车辆目录'; + const directoryDescription = deferredKeyword + ? '按当前搜索条件展示,可直接进入车辆数字档案' + : '按照最新上报时间排列,分页浏览全部授权车辆'; return
{!mobileLayout || administrator ? }
- - {candidates.data ? `本页 ${formatZhNumber(pageVehicles.length)} 辆` : '授权范围'} - {candidates.data ? 本页在线 {formatZhNumber(onlineOnPage)} : null} - {candidates.data ? 多源车辆 {formatZhNumber(multiSourceOnPage)} : null} - } - actions={} + +
{directoryTitle}
{directoryDescription}
+ + } /> {candidates.isFetching && !candidates.data ?
正在读取授权车辆…
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 2f5cc343..4c804c51 100644 --- a/vehicle-data-platform/apps/web/src/v2/productionEntry.test.ts +++ b/vehicle-data-platform/apps/web/src/v2/productionEntry.test.ts @@ -76,6 +76,10 @@ describe('V2 production entry', () => { expect(corePageSources.VehiclePage).toContain("lazy(() => import('./VehicleSearchWorkspace'))"); expect(vehicleSearchSource).toContain("from '../shared/WorkspaceCommandBar'"); expect(vehicleSearchSource).toContain(' .semi-card-body { + min-height: 62px; + grid-template-columns: minmax(0, 1fr) minmax(280px, .82fr); +} + +.v2-vehicle-directory-metric-rail .v2-workspace-metric-list { + grid-template-columns: 1.15fr 1.08fr .92fr .85fr; +} + +.v2-vehicle-directory-metric-rail .v2-workspace-metric-list > span { + min-height: 62px; + padding-block: 8px; +} + +.v2-vehicle-directory-metric-rail .v2-workspace-metric-list > span > strong { + font-size: clamp(20px, 1.55vw, 25px); + font-weight: 740; + letter-spacing: -.02em; +} + +.v2-vehicle-directory-metric-rail .v2-workspace-metric-list > span.is-secondary > strong { + font-size: clamp(17px, 1.25vw, 20px); +} + +.v2-vehicle-directory-unit { + margin-left: 3px; + color: #8493a5; + font-size: 7px; + font-style: normal; + font-weight: 550; + letter-spacing: 0; +} + +.v2-vehicle-directory-metric-rail .v2-workspace-metric-list > span.is-primary .v2-vehicle-directory-unit { + color: #6f90bb; +} + +.v2-vehicle-directory-metric-rail .v2-workspace-metric-context { + padding: 7px 10px 7px 13px; +} + +.v2-vehicle-directory-context { + display: grid; + width: 100%; + min-width: 0; + grid-template-columns: minmax(0, 1fr) auto; + align-items: center; + gap: 9px; +} + +.v2-vehicle-directory-context > span { + display: grid; + min-width: 0; + gap: 2px; +} + +.v2-vehicle-directory-context h5 { + overflow: hidden; + margin: 0; + color: #263a51; + font-size: 12px; + font-weight: 740; + line-height: 17px; + text-overflow: ellipsis; + white-space: nowrap; +} + +.v2-vehicle-directory-context small { + overflow: hidden; + color: #8794a5; + font-size: 8px; + line-height: 12px; + text-overflow: ellipsis; + white-space: nowrap; +} + +.v2-vehicle-directory-context > .semi-button { + min-width: 58px; + height: 34px; + border-radius: 8px; + color: #45627f; + font-size: 9px; + font-weight: 650; +} + +@media (max-width: 980px) { + .v2-vehicle-directory-metric-rail.is-queue > .semi-card-body { + grid-template-columns: minmax(0, 1fr) 226px; + } + + .v2-vehicle-directory-metric-rail .v2-workspace-metric-list > span > em { + display: none; + } +} + +@media (max-width: 680px) { + .v2-vehicle-directory-metric-rail.is-queue > .semi-card-body { + min-height: 56px; + grid-template-columns: minmax(0, 1fr); + } + + .v2-vehicle-directory-metric-rail .v2-workspace-metric-list > span { + min-height: 56px; + padding: 6px 11px; + } + + .v2-vehicle-directory-metric-rail .v2-workspace-metric-list > span.is-secondary { + display: none; + } + + .v2-vehicle-directory-metric-rail .v2-workspace-metric-list > span > strong { + font-size: 19px; + line-height: 22px; + } + + .v2-vehicle-directory-metric-rail .v2-workspace-metric-list > span > em { + display: block; + font-size: 7px; + } + + .v2-vehicle-directory-metric-rail .v2-workspace-metric-context { + display: none; + } +} + +@media (max-width: 900px) and (max-height: 480px) and (orientation: landscape) { + .v2-vehicle-directory-metric-rail.is-queue > .semi-card-body, + .v2-vehicle-directory-metric-rail .v2-workspace-metric-list > span { + min-height: 42px; + } + + .v2-vehicle-directory-metric-rail .v2-workspace-metric-list > span { + padding-block: 3px; + } + + .v2-vehicle-directory-metric-rail .v2-workspace-metric-list > span > strong { + font-size: 15px; + line-height: 17px; + } +} + /* * Monitor fleet summary. * Operational state remains prominent; high-volume ingest evidence is kept