refine Semi UI vehicle workspace

This commit is contained in:
lingniu
2026-07-18 20:04:46 +08:00
parent e95fbe5c19
commit b5b235e394
4 changed files with 588 additions and 9 deletions

View File

@@ -79,6 +79,7 @@ test('polls lightweight single-vehicle realtime data and passes its report inter
]));
const commandCard = view.container.querySelector('.v2-identity-band');
expect(commandCard).toHaveClass('semi-card', 'v2-vehicle-command-card', 'v2-live-overview');
expect(screen.getByLabelText('粤A12345 车辆档案')).toHaveClass('v2-vehicle-record-v3');
expect(screen.getByLabelText('单车任务栏')).toHaveClass('v2-workspace-command-bar', 'v2-vehicle-record-command');
expect(screen.getByLabelText('单车任务栏')).toHaveTextContent('粤A12345');
expect(screen.getByLabelText('单车任务栏')).toHaveTextContent('LTEST000000000001');
@@ -181,6 +182,7 @@ test('shows deduplicated Semi vehicle candidates and opens the selected VIN', as
expect(screen.getByText('本页在线 1')).toBeInTheDocument();
expect(screen.getByText('多源车辆 0')).toBeInTheDocument();
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-discovery-shell')).toContainElement(screen.getByLabelText('查车操作'));
expect(screen.getByRole('button', { name: `打开 ${initialRealtime.vin} 车辆档案` })).toBeInTheDocument();
@@ -485,11 +487,14 @@ test('uses compact Semi telemetry evidence cards on mobile', async () => {
expect(document.querySelector('.v2-telemetry-table')).not.toBeInTheDocument();
expect(document.querySelector('.v2-telemetry-mobile-list.semi-list')).toBeInTheDocument();
expect(document.querySelector('.v2-telemetry-mobile-item.semi-list-item')).toHaveTextContent('36km/h正常');
expect(document.querySelector('.v2-vehicle-record-page')).toHaveClass('is-mobile-layout');
expect(document.querySelector('.v2-vehicle-record-page')).toHaveClass('v2-vehicle-record-v3', 'is-mobile-layout');
expect(screen.getByRole('group', { name: '车辆快捷操作' }).querySelectorAll('.semi-button')).toHaveLength(5);
expect(screen.getByRole('group', { name: '车辆快捷操作' })).toHaveTextContent('切换车辆轨迹回放历史数据里程查询告警事件');
expect(document.querySelectorAll('.v2-identity-sources .semi-tag')).toHaveLength(3);
expect(document.querySelector('.v2-identity-sources')).toHaveTextContent('GB/T 32960JT/T 808宇通 MQTT');
expect(document.querySelector('.v2-vehicle-command-card .v2-live-grid')).toHaveAttribute('aria-label', '车辆实时指标');
expect(document.querySelectorAll('.v2-vehicle-command-card .v2-live-grid > [role="listitem"]')).toHaveLength(6);
expect(document.querySelector('.v2-live-metric.is-speed')).toHaveTextContent('速度');
expect(document.querySelector('.v2-live-metric.is-today-mileage')).toHaveTextContent('当日里程');
expect(document.querySelector('.v2-live-metric.is-source-status')).toHaveTextContent('来源状态');
});

View File

@@ -454,7 +454,7 @@ function VehicleRecord({ detail, liveRealtime, telemetry, telemetryPending, tele
for (const eventName of interactionEvents) window.removeEventListener(eventName, cancelEntryReset, true);
};
}, [detail.vin]);
return <div ref={recordRootRef} className={`v2-vehicle-record-page${mobileLayout ? ' is-mobile-layout' : ''}`} tabIndex={-1} aria-label={`${fmt(identity?.plate || realtime?.plate)} 车辆档案`}>
return <div ref={recordRootRef} className={`v2-vehicle-record-page v2-vehicle-record-v3${mobileLayout ? ' is-mobile-layout' : ''}`} tabIndex={-1} aria-label={`${fmt(identity?.plate || realtime?.plate)} 车辆档案`}>
<MonitorReturnBar />
<Card className="v2-identity-band v2-record-card v2-live-card v2-live-overview v2-vehicle-command-card" bodyStyle={{ padding: 0 }}>
<WorkspaceCommandBar
@@ -480,12 +480,12 @@ function VehicleRecord({ detail, liveRealtime, telemetry, telemetryPending, tele
meta={<span className="v2-live-report-meta"><i className={realtime?.online ? 'is-online' : ''} />{realtime?.online ? '实时在线' : '当前离线'} · <IconClock /><PlatformTime value={realtime?.lastSeen || identity?.lastSeen} /></span>}
/>
<div className="v2-live-grid" role="list" aria-label="车辆实时指标">
<div role="listitem"><small></small><strong>{availableMetric(realtime?.speedKmh, realtime?.speedAvailable)}<em>km/h</em></strong><span></span></div>
<div role="listitem"><small>SOC</small><strong>{availableMetric(realtime?.socPercent, realtime?.socAvailable)}<em>%</em></strong><span></span></div>
<div role="listitem"><small></small><Button theme="borderless" type="tertiary" className="v2-live-source-link" title="展开全部里程来源" aria-label="查看总里程全部来源" onClick={() => setSourceEvidenceOpen(true)}><span>{availableMetric(realtime?.totalMileageKm, realtime?.mileageAvailable)}<em>km</em></span><IconChevronRight /></Button><span></span></div>
<div role="listitem"><small></small><Button theme="borderless" type="tertiary" className="v2-live-source-link" title="展开全部里程来源" aria-label="查看当日里程全部来源" onClick={() => setSourceEvidenceOpen(true)}><span>{availableMetric(realtime?.todayMileageKm, realtime?.todayMileageAvailable)}<em>km</em></span><IconChevronRight /></Button><span></span></div>
<div role="listitem"><small></small><strong className="is-text" title={realtime?.primaryProtocol}>{protocolDisplayLabel(realtime?.primaryProtocol)}</strong><span title={realtime?.locationSource}>{protocolSourceLabel(realtime?.locationSource) || '当前最优来源'}</span></div>
<div role="listitem"><small></small><strong>{realtime?.onlineSourceCount ?? 0}<em> / {detail.sourceStatus.length}</em></strong><span>线 / </span></div>
<div className="v2-live-metric is-speed" role="listitem"><small></small><strong>{availableMetric(realtime?.speedKmh, realtime?.speedAvailable)}<em>km/h</em></strong><span></span></div>
<div className="v2-live-metric is-soc" role="listitem"><small>SOC</small><strong>{availableMetric(realtime?.socPercent, realtime?.socAvailable)}<em>%</em></strong><span></span></div>
<div className="v2-live-metric is-mileage" role="listitem"><small></small><Button theme="borderless" type="tertiary" className="v2-live-source-link" title="展开全部里程来源" aria-label="查看总里程全部来源" onClick={() => setSourceEvidenceOpen(true)}><span>{availableMetric(realtime?.totalMileageKm, realtime?.mileageAvailable)}<em>km</em></span><IconChevronRight /></Button><span></span></div>
<div className="v2-live-metric is-today-mileage" role="listitem"><small></small><Button theme="borderless" type="tertiary" className="v2-live-source-link" title="展开全部里程来源" aria-label="查看当日里程全部来源" onClick={() => setSourceEvidenceOpen(true)}><span>{availableMetric(realtime?.todayMileageKm, realtime?.todayMileageAvailable)}<em>km</em></span><IconChevronRight /></Button><span></span></div>
<div className="v2-live-metric is-source" role="listitem"><small></small><strong className="is-text" title={realtime?.primaryProtocol}>{protocolDisplayLabel(realtime?.primaryProtocol)}</strong><span title={realtime?.locationSource}>{protocolSourceLabel(realtime?.locationSource) || '当前最优来源'}</span></div>
<div className="v2-live-metric is-source-status" role="listitem"><small></small><strong>{realtime?.onlineSourceCount ?? 0}<em> / {detail.sourceStatus.length}</em></strong><span>线 / </span></div>
</div>
<CurrentVehicleAddress vehicle={realtime} fallback={identity?.locationText} />
</section>

View File

@@ -105,7 +105,7 @@ export default function VehicleSearchWorkspace() {
if (document.activeElement instanceof HTMLElement) document.activeElement.blur();
};
const vehicleScope = candidates.data ? `${candidates.data.total.toLocaleString('zh-CN')} 辆授权` : '授权范围';
return <section className={`v2-vehicle-search-page${syncOpen ? ' has-sync-panel' : ''}${candidatesOpen ? ' has-candidates' : ''}`}>
return <section className={`v2-vehicle-search-page v2-vehicle-directory-v3${syncOpen ? ' has-sync-panel' : ''}${candidatesOpen ? ' has-candidates' : ''}`}>
<div className="v2-vehicle-discovery-shell">
{!mobileLayout || administrator ? <WorkspaceCommandBar
className={`v2-vehicle-command-bar${mobileLayout ? ' is-mobile-admin' : ''}`}