feat: unify vehicle directory summary
This commit is contained in:
@@ -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(<QueryClientProvider client={client}><MemoryRouter initialEntries={['/vehicles']} future={ROUTER_FUTURE}><Routes><Route path="/vehicles" element={<VehiclePage />} /></Routes></MemoryRouter></QueryClientProvider>);
|
||||
|
||||
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();
|
||||
|
||||
@@ -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) : '—'}<i className="v2-vehicle-directory-unit">辆</i></>,
|
||||
note: deferredKeyword ? '当前条件' : '账号范围',
|
||||
tone: 'primary',
|
||||
emphasis: 'primary'
|
||||
},
|
||||
{
|
||||
label: '本页在线',
|
||||
value: <>{candidates.data ? formatZhNumber(onlineOnPage) : '—'}<i className="v2-vehicle-directory-unit">辆</i></>,
|
||||
note: candidates.data ? `本页 ${formatZhNumber(pageVehicles.length)} 辆` : '正在读取',
|
||||
tone: 'success',
|
||||
emphasis: 'primary'
|
||||
},
|
||||
{
|
||||
label: '多源车辆',
|
||||
value: <>{candidates.data ? formatZhNumber(multiSourceOnPage) : '—'}<i className="v2-vehicle-directory-unit">辆</i></>,
|
||||
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 <section className={`v2-vehicle-search-page v2-vehicle-directory-v3${syncOpen ? ' has-sync-panel' : ''}${!mobileLayout && candidatesOpen ? ' has-candidates' : ''}`}>
|
||||
<div className="v2-vehicle-discovery-shell">
|
||||
{!mobileLayout || administrator ? <WorkspaceCommandBar
|
||||
@@ -265,15 +299,15 @@ export default function VehicleSearchWorkspace() {
|
||||
</WorkspaceFilterPanel>}
|
||||
</div>
|
||||
<Card className="v2-vehicle-recent-card" bodyStyle={{ padding: 0 }}>
|
||||
<WorkspacePanelHeader
|
||||
title={deferredKeyword ? '匹配车辆' : mobileLayout ? '授权车辆' : '授权车辆目录'}
|
||||
description={deferredKeyword ? '按当前搜索条件展示,可直接进入车辆数字档案' : '按照最新上报时间排列,分页浏览全部授权车辆'}
|
||||
meta={<span className="v2-vehicle-directory-summary">
|
||||
<Tag color={deferredKeyword ? 'blue' : 'grey'} type="light" size="small">{candidates.data ? `本页 ${formatZhNumber(pageVehicles.length)} 辆` : '授权范围'}</Tag>
|
||||
{candidates.data ? <Tag className="is-secondary" color="green" type="light" size="small">本页在线 {formatZhNumber(onlineOnPage)}</Tag> : null}
|
||||
{candidates.data ? <Tag className="is-secondary" color="blue" type="light" size="small">多源车辆 {formatZhNumber(multiSourceOnPage)}</Tag> : null}
|
||||
</span>}
|
||||
actions={<Button theme="borderless" type="tertiary" size="small" icon={<IconRefresh />} loading={candidates.isFetching} onClick={() => candidates.refetch()}>刷新</Button>}
|
||||
<WorkspaceMetricRail
|
||||
variant="queue"
|
||||
ariaLabel="车辆目录摘要"
|
||||
className="v2-vehicle-directory-metric-rail"
|
||||
items={directoryMetrics}
|
||||
context={<div className="v2-vehicle-directory-context">
|
||||
<span><h5>{directoryTitle}</h5><small>{directoryDescription}</small></span>
|
||||
<Button theme="borderless" type="tertiary" size="small" icon={<IconRefresh />} loading={candidates.isFetching} aria-label="刷新车辆目录" onClick={() => candidates.refetch()}>刷新</Button>
|
||||
</div>}
|
||||
/>
|
||||
{candidates.isFetching && !candidates.data
|
||||
? <div className="v2-vehicle-recent-state" role="status"><span className="v2-spinner" /><span>正在读取授权车辆…</span></div>
|
||||
|
||||
@@ -76,6 +76,10 @@ describe('V2 production entry', () => {
|
||||
expect(corePageSources.VehiclePage).toContain("lazy(() => import('./VehicleSearchWorkspace'))");
|
||||
expect(vehicleSearchSource).toContain("from '../shared/WorkspaceCommandBar'");
|
||||
expect(vehicleSearchSource).toContain('<WorkspaceCommandBar');
|
||||
expect(vehicleSearchSource).toContain("from '../shared/WorkspaceMetricRail'");
|
||||
expect(vehicleSearchSource).toContain('<WorkspaceMetricRail');
|
||||
expect(vehicleSearchSource).toContain('className="v2-vehicle-directory-metric-rail"');
|
||||
expect(vehicleSearchSource).not.toContain('className="v2-vehicle-directory-summary"');
|
||||
expect(corePageSources.VehiclePage).toContain('<Select');
|
||||
expect(corePageSources.VehiclePage).toContain('<Card className="v2-identity-band v2-record-card v2-live-card v2-live-overview v2-vehicle-command-card"');
|
||||
expect(corePageSources.VehiclePage).toContain("from '../shared/WorkspaceMetricRail'");
|
||||
|
||||
@@ -23009,6 +23009,159 @@
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Vehicle directory summary.
|
||||
* A single Semi metric rail replaces the mixed heading/tag row so directory
|
||||
* scale and current-page health can be scanned before opening a vehicle.
|
||||
*/
|
||||
.v2-vehicle-directory-metric-rail.is-queue.semi-card {
|
||||
flex: 0 0 auto;
|
||||
border: 0;
|
||||
border-bottom: 1px solid #e1e8f0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.v2-vehicle-directory-metric-rail.is-queue > .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
|
||||
|
||||
Reference in New Issue
Block a user