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 3439a04a..f312b3e8 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 @@ -208,17 +208,22 @@ test('shows deduplicated Semi vehicle candidates and opens the selected VIN', as expect(screen.getByRole('heading', { name: '授权车辆目录', level: 5 })).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(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.getByText('第 1 / 1 页').closest('.semi-tag')).toBeInTheDocument(); 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'); expect(view.container.querySelector('.v2-vehicle-directory-protocols .v2-protocol-tag')).toHaveClass('semi-tag-cyan-light'); expect(view.container.querySelector('.v2-vehicle-discovery-shell')).toContainElement(screen.getByLabelText('查车操作')); - expect(screen.getByRole('button', { name: `打开 ${initialRealtime.vin} 车辆档案` })).toBeInTheDocument(); + expect(screen.getByTestId(`vehicle-directory-row-${initialRealtime.vin}`)).toHaveAttribute('role', 'button'); + expect(screen.getByTestId(`vehicle-directory-row-${initialRealtime.vin}`)).toHaveClass('is-online'); + expect(screen.getByTestId(`vehicle-directory-row-${initialRealtime.vin}`)).toHaveAttribute('aria-label', `打开 ${initialRealtime.plate} 车辆档案`); + expect(screen.getByTestId(`vehicle-directory-row-${initialRealtime.vin}`).querySelector('button')).not.toBeInTheDocument(); + expect(screen.getByTestId(`vehicle-directory-row-${initialRealtime.vin}`)).toHaveTextContent('查看档案'); fireEvent.focus(input); expect(input).toHaveAttribute('aria-expanded', 'false'); expect(view.container.querySelector('.v2-vehicle-search-options')).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 2822fb2a..9554356c 100644 --- a/vehicle-data-platform/apps/web/src/v2/pages/VehicleSearchWorkspace.tsx +++ b/vehicle-data-platform/apps/web/src/v2/pages/VehicleSearchWorkspace.tsx @@ -171,13 +171,6 @@ export default function VehicleSearchWorkspace() { note: '2+ 协议来源', tone: 'primary', emphasis: 'secondary' - }, - { - label: '当前页', - value: candidates.data ? `${page} / ${totalPages}` : '—', - note: `每页 ${pageSize} 辆`, - tone: 'neutral', - emphasis: 'secondary' } ]; const directoryTitle = deferredKeyword ? '匹配车辆' : mobileLayout ? '授权车辆' : '授权车辆目录'; @@ -306,7 +299,10 @@ export default function VehicleSearchWorkspace() { items={directoryMetrics} context={
{directoryTitle}
{directoryDescription}
- + + 第 {page} / {totalPages} 页 + +
} /> {candidates.isFetching && !candidates.data @@ -315,7 +311,7 @@ export default function VehicleSearchWorkspace() { ?
{candidates.error instanceof Error ? candidates.error.message : '授权车辆加载失败'}
: pageVehicles.length ? mobileLayout ?
- {pageVehicles.map((vehicle) =>
+ {pageVehicles.map((vehicle) =>
+ render: () => } ]} dataSource={pageVehicles} rowKey="vin" onRow={(vehicle) => vehicle ? ({ + className: vehicle.online ? 'is-online' : 'is-offline', + role: 'button', tabIndex: 0, + 'aria-label': `打开 ${vehicle.plate || vehicle.vin} 车辆档案`, + 'data-testid': `vehicle-directory-row-${vehicle.vin}`, onClick: () => openVehicle(vehicle.vin), onKeyDown: (event) => { if (event.key !== 'Enter' && event.key !== ' ') return; diff --git a/vehicle-data-platform/apps/web/src/v2/styles/workspace.css b/vehicle-data-platform/apps/web/src/v2/styles/workspace.css index 141a8ded..00d228f3 100644 --- a/vehicle-data-platform/apps/web/src/v2/styles/workspace.css +++ b/vehicle-data-platform/apps/web/src/v2/styles/workspace.css @@ -24318,3 +24318,148 @@ grid-template-columns: repeat(2, minmax(0, 1fr)); } } + +/* + * Semi UI vehicle directory convergence. + * The directory remains dense, but uses one white evidence surface, explicit + * row affordances and a quieter three-signal summary. + */ +.v2-vehicle-directory-v3 > .v2-vehicle-recent-card > .semi-card-body { + background: #fff; +} + +.v2-vehicle-directory-metric-rail .v2-workspace-metric-list { + grid-template-columns: 1.08fr .92fr 1fr; +} + +.v2-vehicle-directory-metric-rail.is-queue.has-context > .semi-card-body { + grid-template-columns: minmax(0, 1fr) minmax(340px, .96fr); +} + +.v2-vehicle-directory-context { + grid-template-columns: minmax(0, 1fr) auto; +} + +.v2-vehicle-directory-context > .v2-vehicle-directory-context-actions { + display: inline-flex; + min-width: 0; + align-items: center; + justify-content: flex-end; + gap: 6px; +} + +.v2-vehicle-directory-context-actions > .semi-tag { + min-height: 22px; + margin: 0; + border-radius: 999px; + padding-inline: 8px; + font-size: 9px; + font-variant-numeric: tabular-nums; + font-weight: 700; +} + +.v2-vehicle-directory-context-actions > .semi-button { + min-width: 58px; + height: 34px; + border-radius: 8px; + color: #45627f; + font-size: 9px; + font-weight: 650; +} + +.v2-vehicle-directory-v3 .v2-vehicle-directory-table.semi-table-wrapper, +.v2-vehicle-directory-v3 .v2-vehicle-recent-grid { + background: #fff; +} + +.v2-vehicle-directory-table .semi-table-thead > .semi-table-row > .semi-table-row-head { + height: 40px; + background: #f8fafc; + color: #66778e; + font-size: 10px; + letter-spacing: .015em; +} + +.v2-vehicle-directory-table .semi-table-tbody > .semi-table-row > .semi-table-row-cell { + height: 52px; + background: #fff; + transition: background-color .16s ease, box-shadow .16s ease; +} + +.v2-vehicle-directory-table .semi-table-tbody > .semi-table-row:hover > .semi-table-row-cell, +.v2-vehicle-directory-table .semi-table-tbody > .semi-table-row:focus-visible > .semi-table-row-cell { + background: #f6f9fe; +} + +.v2-vehicle-directory-table .semi-table-tbody > .semi-table-row.is-online > .semi-table-row-cell:first-child { + box-shadow: inset 3px 0 #23a779; +} + +.v2-vehicle-directory-table .semi-table-tbody > .semi-table-row.is-offline > .semi-table-row-cell:first-child { + box-shadow: inset 3px 0 #c9d2de; +} + +.v2-vehicle-directory-table .semi-table-tbody > .semi-table-row:hover .v2-vehicle-directory-open { + background: #eaf3ff; +} + +.v2-vehicle-directory-open { + display: inline-flex; + height: 28px; + align-items: center; + gap: 3px; + border-radius: 999px; + padding-inline: 10px; + color: #155fc5; + font-size: 10px; + font-weight: 700; + transition: background-color .16s ease; +} + +.v2-vehicle-recent-card > .semi-card-body > .v2-vehicle-directory-pagination { + background: #fbfcfe; + box-shadow: 0 -8px 22px rgba(43, 62, 86, .025); +} + +@media (max-width: 980px) and (min-width: 681px) { + .v2-vehicle-directory-metric-rail.is-queue.has-context > .semi-card-body { + grid-template-columns: minmax(0, 1fr) 270px; + } +} + +@media (max-width: 680px) { + .v2-vehicle-directory-metric-rail.is-queue.has-context > .semi-card-body { + grid-template-columns: minmax(0, 1fr); + } + + .v2-vehicle-directory-v3 .v2-vehicle-recent-grid { + padding: 0 10px 5px; + } + + .v2-vehicle-directory-v3 .v2-vehicle-recent-item-shell { + border-bottom: 1px solid #e9eef5; + } + + .v2-vehicle-directory-v3 .v2-vehicle-recent-item-shell:last-child { + border-bottom: 0; + } + + .v2-vehicle-directory-v3 .v2-vehicle-recent-item.semi-button { + min-height: 64px; + border-radius: 0; + background: #fff; + transition: background-color .16s ease; + } + + .v2-vehicle-directory-v3 .v2-vehicle-recent-item.semi-button:active { + background: #eef5ff; + } + + .v2-vehicle-directory-v3 .v2-vehicle-recent-item-shell.is-online { + box-shadow: inset 2px 0 #23a779; + } + + .v2-vehicle-directory-v3 .v2-vehicle-recent-item-shell.is-offline { + box-shadow: inset 2px 0 #c9d2de; + } +}