refine Semi UI vehicle detail workspace

This commit is contained in:
lingniu
2026-07-18 02:24:21 +08:00
parent 5a8eec16f3
commit c29d41fd86
4 changed files with 626 additions and 31 deletions

View File

@@ -77,7 +77,10 @@ test('polls lightweight single-vehicle realtime data and passes its report inter
await waitFor(() => expect(fleetMapVehicles).toHaveBeenLastCalledWith([ await waitFor(() => expect(fleetMapVehicles).toHaveBeenLastCalledWith([
expect.objectContaining({ longitude: 113.28, latitude: 23.15, reportIntervalMs: 30_000 }) expect.objectContaining({ longitude: 113.28, latitude: 23.15, reportIntervalMs: 30_000 })
])); ]));
expect(view.container.querySelector('.v2-identity-band')).toHaveClass('semi-card'); const commandCard = view.container.querySelector('.v2-identity-band');
expect(commandCard).toHaveClass('semi-card', 'v2-vehicle-command-card', 'v2-live-overview');
expect(screen.getByRole('list', { name: '车辆实时指标' })).toHaveClass('v2-live-grid');
expect(commandCard?.querySelectorAll('.v2-live-grid > [role="listitem"]')).toHaveLength(6);
expect(screen.getByRole('heading', { name: '最新上报', level: 5 })).toBeInTheDocument(); expect(screen.getByRole('heading', { name: '最新上报', level: 5 })).toBeInTheDocument();
expect(screen.getByRole('heading', { name: '实时位置', level: 5 })).toBeInTheDocument(); expect(screen.getByRole('heading', { name: '实时位置', level: 5 })).toBeInTheDocument();
expect(screen.getByRole('heading', { name: '最近事件', level: 5 })).toBeInTheDocument(); expect(screen.getByRole('heading', { name: '最近事件', level: 5 })).toBeInTheDocument();
@@ -99,6 +102,7 @@ test('polls lightweight single-vehicle realtime data and passes its report inter
const totalMileageSource = screen.getByRole('button', { name: '查看总里程全部来源' }); const totalMileageSource = screen.getByRole('button', { name: '查看总里程全部来源' });
expect(totalMileageSource).toHaveClass('semi-button', 'v2-live-source-link'); expect(totalMileageSource).toHaveClass('semi-button', 'v2-live-source-link');
expect(totalMileageSource.closest('.semi-card')).toHaveClass('v2-live-overview'); expect(totalMileageSource.closest('.semi-card')).toHaveClass('v2-live-overview');
expect(totalMileageSource.closest('.semi-card')).toBe(commandCard);
fireEvent.click(locationSource); fireEvent.click(locationSource);
await waitFor(() => expect(sourceEvidence).toHaveBeenCalledTimes(1)); await waitFor(() => expect(sourceEvidence).toHaveBeenCalledTimes(1));
}); });
@@ -347,4 +351,5 @@ test('uses compact Semi telemetry evidence cards on mobile', async () => {
expect(document.querySelector('.v2-telemetry-table')).not.toBeInTheDocument(); expect(document.querySelector('.v2-telemetry-table')).not.toBeInTheDocument();
expect(document.querySelector('.v2-telemetry-mobile-list.semi-list')).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-telemetry-mobile-item.semi-list-item')).toHaveTextContent('36km/h正常');
expect(document.querySelector('.v2-vehicle-command-card .v2-live-grid')).toHaveAttribute('aria-label', '车辆实时指标');
}); });

View File

@@ -51,6 +51,12 @@ function vehicleLastSeenLabel(value?: string) {
return normalized.length >= 16 ? `${normalized.slice(5, 16)} 更新` : normalized; return normalized.length >= 16 ? `${normalized.slice(5, 16)} 更新` : normalized;
} }
function eventTimeLabel(value?: string) {
if (!value) return '—';
const normalized = value.replace('T', ' ');
return normalized.length >= 16 ? normalized.slice(5, 16) : normalized;
}
function VehicleSearch() { function VehicleSearch() {
const navigate = useNavigate(); const navigate = useNavigate();
const { session } = usePlatformSession(); const { session } = usePlatformSession();
@@ -255,7 +261,7 @@ function Events({ detail }: { detail: VehicleDetail }) {
emptyContent={<Empty className="v2-event-empty" title="暂无可用事件证据" description="车辆产生质量提醒或来源状态变化后会显示在这里。" />} emptyContent={<Empty className="v2-event-empty" title="暂无可用事件证据" description="车辆产生质量提醒或来源状态变化后会显示在这里。" />}
renderItem={(event, index) => <List.Item className={`v2-event-row is-${event.tone}`} key={`${event.title}-${event.time}-${index}`}> renderItem={(event, index) => <List.Item className={`v2-event-row is-${event.tone}`} key={`${event.title}-${event.time}-${index}`}>
<span className="v2-event-icon">{event.tone === 'success' ? <IconTickCircle /> : <IconAlarm />}</span> <span className="v2-event-icon">{event.tone === 'success' ? <IconTickCircle /> : <IconAlarm />}</span>
<div><strong>{event.title}</strong><p>{event.detail}</p></div><time>{fmt(event.time)}</time> <div><strong>{event.title}</strong><p>{event.detail}</p></div><time title={fmt(event.time)}>{eventTimeLabel(event.time)}</time>
</List.Item>} </List.Item>}
/> />
</Card>; </Card>;
@@ -409,16 +415,19 @@ function VehicleRecordNavigation() {
target.scrollIntoView({ behavior: 'smooth', block: 'start' }); target.scrollIntoView({ behavior: 'smooth', block: 'start' });
target.focus({ preventScroll: true }); target.focus({ preventScroll: true });
}; };
const navigationItems = [
['location', '实时位置', <IconMapPin />],
['events', '最近事件', <IconAlarm />],
['telemetry', '实时遥测', <IconClock />],
['archive', '车辆主档', <IconBox />]
] as const;
return <Card className="v2-record-card v2-vehicle-record-nav" bodyStyle={{ padding: 0 }}> return <Card className="v2-record-card v2-vehicle-record-nav" bodyStyle={{ padding: 0 }}>
<WorkspacePanelHeader <WorkspacePanelHeader
variant="compact" variant="compact"
title="详情导航" title="车辆视图"
description="快速定位当前车辆的数据区域" description="位置、事件、遥测与主档"
actions={<nav className="v2-vehicle-section-nav" aria-label="单车详情导航"> actions={<nav className="v2-vehicle-section-nav" aria-label="单车详情导航">
<Button size="small" theme="borderless" type="tertiary" icon={<IconMapPin />} aria-label="跳转到实时位置" onClick={() => jumpToSection('location')}></Button> {navigationItems.map(([key, label, icon]) => <Button key={key} size="small" theme="borderless" type="tertiary" icon={icon} aria-label={`跳转到${label}`} onClick={() => jumpToSection(key)}>{label}</Button>)}
<Button size="small" theme="borderless" type="tertiary" icon={<IconAlarm />} aria-label="跳转到最近事件" onClick={() => jumpToSection('events')}></Button>
<Button size="small" theme="borderless" type="tertiary" icon={<IconClock />} aria-label="跳转到实时遥测" onClick={() => jumpToSection('telemetry')}></Button>
<Button size="small" theme="borderless" type="tertiary" icon={<IconBox />} aria-label="跳转到车辆主档" onClick={() => jumpToSection('archive')}></Button>
</nav>} </nav>}
/> />
</Card>; </Card>;
@@ -441,28 +450,37 @@ function VehicleRecord({ detail, liveRealtime, telemetry, telemetryPending, tele
]; ];
return <div className="v2-vehicle-record-page"> return <div className="v2-vehicle-record-page">
<MonitorReturnBar /> <MonitorReturnBar />
<Card className="v2-identity-band" bodyStyle={{ padding: 0 }}> <Card className="v2-identity-band v2-record-card v2-live-card v2-live-overview v2-vehicle-command-card" bodyStyle={{ padding: 0 }}>
<div className="v2-identity-primary"><span className="v2-plate"><IconBox />{fmt(identity?.plate || realtime?.plate)}</span><Tag className={`v2-online-label ${realtime?.online ? 'is-online' : ''}`} color={realtime?.online ? 'green' : 'grey'} size="small">{realtime?.online ? '在线' : '离线'}</Tag><small>VIN</small><b>{detail.vin}</b><Button theme="borderless" aria-label="复制 VIN" title="复制 VIN" icon={<IconCopy />} onClick={() => navigator.clipboard?.writeText(detail.vin)} /></div> <div className="v2-vehicle-command-header">
<div className="v2-identity-meta"><div><small> / </small><strong>{[detail.profile?.brandName, detail.profile?.modelName].filter(Boolean).join(' / ') || fmt(identity?.oem || realtime?.oem)}</strong></div><div><small></small><p>{detail.sources.length ? detail.sources.map((source) => <span key={source}>{source}</span>) : <span></span>}</p></div></div> <div className="v2-identity-primary">
<span className="v2-plate"><IconBox />{fmt(identity?.plate || realtime?.plate)}</span>
<Tag className={`v2-online-label ${realtime?.online ? 'is-online' : ''}`} color={realtime?.online ? 'green' : 'grey'} size="small">{realtime?.online ? '在线' : '离线'}</Tag>
<span className="v2-identity-vin"><small>VIN</small><b>{detail.vin}</b><Button theme="borderless" aria-label="复制 VIN" title="复制 VIN" icon={<IconCopy />} onClick={() => navigator.clipboard?.writeText(detail.vin)} /></span>
</div>
<div className="v2-identity-meta">
<div><small> / </small><strong>{[detail.profile?.brandName, detail.profile?.modelName].filter(Boolean).join(' / ') || fmt(identity?.oem || realtime?.oem)}</strong></div>
<div><small></small><div className="v2-identity-sources">{detail.sources.length ? detail.sources.map((source) => <Tag key={source} color="blue" type="light" size="small">{source}</Tag>) : <Tag color="grey" type="light" size="small"></Tag>}</div></div>
</div>
<div className="v2-identity-actions" role="group" aria-label="车辆快捷操作"> <div className="v2-identity-actions" role="group" aria-label="车辆快捷操作">
{actions.map((action) => <Button key={action.key} theme="light" type={action.type} icon={action.icon} aria-label={action.label} onClick={() => navigate(action.to)}>{action.label}</Button>)} {actions.map((action) => <Button key={action.key} theme="light" type={action.type} icon={action.icon} aria-label={action.label} onClick={() => navigate(action.to)}>{action.label}</Button>)}
</div> </div>
</Card> </div>
<section className="v2-vehicle-live-section" aria-labelledby="vehicle-live-heading">
<Card className="v2-record-card v2-live-card v2-live-overview" bodyStyle={{ padding: 0 }}>
<WorkspacePanelHeader <WorkspacePanelHeader
title="最新上报" title={<span id="vehicle-live-heading"></span>}
description="关键运行指标与推荐数据来源"
meta={<span className="v2-live-report-meta"><i className={realtime?.online ? 'is-online' : ''} />{realtime?.online ? '实时在线' : '当前离线'} · <IconClock />{fmt(realtime?.lastSeen || identity?.lastSeen)}</span>} meta={<span className="v2-live-report-meta"><i className={realtime?.online ? 'is-online' : ''} />{realtime?.online ? '实时在线' : '当前离线'} · <IconClock />{fmt(realtime?.lastSeen || identity?.lastSeen)}</span>}
/> />
<div className="v2-live-grid"> <div className="v2-live-grid" role="list" aria-label="车辆实时指标">
<div><small></small><strong>{availableMetric(realtime?.speedKmh, realtime?.speedAvailable)}<em>km/h</em></strong></div> <div role="listitem"><small></small><strong>{availableMetric(realtime?.speedKmh, realtime?.speedAvailable)}<em>km/h</em></strong><span></span></div>
<div><small>SOC</small><strong>{availableMetric(realtime?.socPercent, realtime?.socAvailable)}<em>%</em></strong></div> <div role="listitem"><small>SOC</small><strong>{availableMetric(realtime?.socPercent, realtime?.socAvailable)}<em>%</em></strong><span></span></div>
<div><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></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><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></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><small></small><strong className="is-text">{fmt(realtime?.primaryProtocol)}<em>{realtime?.locationSource ? ` · ${realtime.locationSource}` : ''}</em></strong></div> <div role="listitem"><small></small><strong className="is-text">{fmt(realtime?.primaryProtocol)}</strong><span title={realtime?.locationSource}>{realtime?.locationSource || '当前最优来源'}</span></div>
<div><small></small><strong>{realtime?.onlineSourceCount ?? 0}<em> 线 / {detail.sourceStatus.length} </em></strong></div> <div role="listitem"><small></small><strong>{realtime?.onlineSourceCount ?? 0}<em> / {detail.sourceStatus.length}</em></strong><span>线 / </span></div>
</div> </div>
<CurrentVehicleAddress vehicle={realtime} fallback={identity?.locationText} /> <CurrentVehicleAddress vehicle={realtime} fallback={identity?.locationText} />
</section>
</Card> </Card>
<VehicleSourceEvidencePanel vin={detail.vin} open={sourceEvidenceOpen} onOpenChange={setSourceEvidenceOpen} /> <VehicleSourceEvidencePanel vin={detail.vin} open={sourceEvidenceOpen} onOpenChange={setSourceEvidenceOpen} />

View File

@@ -62,10 +62,11 @@ describe('V2 production entry', () => {
expect(corePageSources.StatisticsPage).not.toContain("from '../shared/PageHeader'"); expect(corePageSources.StatisticsPage).not.toContain("from '../shared/PageHeader'");
expect(corePageSources.StatisticsPage).not.toContain('<PageHeader'); expect(corePageSources.StatisticsPage).not.toContain('<PageHeader');
expect(corePageSources.VehiclePage).toContain('<Select'); expect(corePageSources.VehiclePage).toContain('<Select');
expect(corePageSources.VehiclePage).toContain('<Card className="v2-record-card v2-live-card v2-live-overview"'); 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('className="v2-live-grid" role="list" aria-label="车辆实时指标"');
expect(corePageSources.VehiclePage).toContain('<Card className="v2-single-map-card"'); expect(corePageSources.VehiclePage).toContain('<Card className="v2-single-map-card"');
expect(corePageSources.VehiclePage).toContain('className="v2-record-section-anchor v2-record-location-anchor"'); expect(corePageSources.VehiclePage).toContain('className="v2-record-section-anchor v2-record-location-anchor"');
expect(corePageSources.VehiclePage).toContain('<Card className="v2-identity-band"'); expect(corePageSources.VehiclePage).toContain('v2-identity-band');
expect(corePageSources.VehiclePage).not.toContain('<section className="v2-record-card'); expect(corePageSources.VehiclePage).not.toContain('<section className="v2-record-card');
expect(corePageSources.VehiclePage).not.toContain('<section className="v2-single-map-card'); expect(corePageSources.VehiclePage).not.toContain('<section className="v2-single-map-card');
expect(corePageSources.VehiclePage).not.toContain('<section className="v2-identity-band"'); expect(corePageSources.VehiclePage).not.toContain('<section className="v2-identity-band"');

View File

@@ -4260,3 +4260,574 @@
min-width: 62px; min-width: 62px;
} }
} }
/* Semi UI single-vehicle command workspace. */
.v2-vehicle-command-card.semi-card {
min-height: 0;
overflow: hidden;
border: 1px solid #d7e2ee;
border-radius: 13px;
background: #fff;
box-shadow: 0 10px 30px rgba(31, 53, 80, .065);
content-visibility: visible;
}
.v2-vehicle-command-card > .semi-card-body {
display: block;
min-height: 0;
}
.v2-vehicle-command-header {
display: grid;
grid-template-columns: minmax(310px, .9fr) minmax(360px, 1.15fr) minmax(360px, auto);
align-items: stretch;
background: #fff;
}
.v2-vehicle-command-header .v2-identity-primary {
display: grid;
grid-template-columns: auto auto;
grid-template-rows: auto auto;
align-content: center;
align-items: center;
justify-content: start;
gap: 8px 10px;
padding: 18px 20px;
}
.v2-vehicle-command-header .v2-plate {
min-height: 42px;
border-color: #b9d2fb;
border-radius: 9px;
background: #edf4ff;
padding-inline: 14px;
color: var(--v2-blue);
font-size: 18px;
letter-spacing: .01em;
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .65);
}
.v2-vehicle-command-header .v2-online-label.semi-tag {
min-height: 25px;
margin: 0;
border-radius: 999px;
padding-inline: 9px;
font-size: 10px;
font-weight: 700;
}
.v2-identity-vin {
display: flex;
min-width: 0;
grid-column: 1 / -1;
align-items: center;
gap: 7px;
}
.v2-vehicle-command-header .v2-identity-vin small,
.v2-vehicle-command-header .v2-identity-vin b,
.v2-vehicle-command-header .v2-identity-vin .semi-button {
grid-row: auto;
}
.v2-vehicle-command-header .v2-identity-vin small {
flex: 0 0 auto;
color: #8290a2;
font-size: 10px;
font-weight: 600;
}
.v2-vehicle-command-header .v2-identity-vin b {
min-width: 0;
overflow: hidden;
color: #41536a;
font-size: 11px;
font-variant-numeric: tabular-nums;
letter-spacing: .015em;
text-overflow: ellipsis;
white-space: nowrap;
}
.v2-vehicle-command-header .v2-identity-vin .semi-button {
width: 28px;
min-width: 28px;
height: 28px;
flex: 0 0 auto;
border-radius: 7px;
color: #73849a;
}
.v2-vehicle-command-header .v2-identity-meta {
display: grid;
min-width: 0;
grid-template-columns: minmax(180px, 1.15fr) minmax(160px, .85fr);
align-items: center;
gap: 0;
border: 0;
border-left: 1px solid #e4eaf2;
padding: 16px 0;
}
.v2-vehicle-command-header .v2-identity-meta > div {
min-width: 0;
padding: 0 20px;
}
.v2-vehicle-command-header .v2-identity-meta > div + div {
margin: 0;
border: 0;
border-left: 1px solid #e7edf4;
padding: 0 20px;
}
.v2-vehicle-command-header .v2-identity-meta small {
display: block;
color: #7b899b;
font-size: 10px;
font-weight: 600;
}
.v2-vehicle-command-header .v2-identity-meta strong {
display: block;
margin-top: 7px;
overflow: hidden;
color: #2c4057;
font-size: 12px;
line-height: 1.45;
text-overflow: ellipsis;
white-space: nowrap;
}
.v2-vehicle-command-header .v2-identity-sources {
display: flex;
min-width: 0;
flex-wrap: wrap;
gap: 5px;
margin: 7px 0 0;
}
.v2-vehicle-command-header .v2-identity-meta .semi-tag {
min-height: 22px;
margin: 0;
border-radius: 6px;
padding-inline: 7px;
font-size: 9px;
font-weight: 650;
}
.v2-vehicle-command-header .v2-identity-actions {
display: flex;
min-width: 0;
flex-wrap: wrap;
align-content: center;
justify-content: flex-end;
gap: 7px;
border-left: 1px solid #e4eaf2;
padding: 15px 18px;
}
.v2-vehicle-command-header .v2-identity-actions > .semi-button {
min-width: 100px;
min-height: 38px;
flex: 0 1 auto;
border-radius: 8px;
padding-inline: 12px;
font-size: 11px;
font-weight: 650;
}
.v2-vehicle-command-header .v2-identity-actions > .semi-button-primary {
border-color: #bcd3f8;
background: #edf4ff;
color: var(--v2-blue);
}
.v2-vehicle-live-section {
border-top: 1px solid #dfe7f0;
background: #fbfcfe;
}
.v2-vehicle-live-section > .v2-workspace-panel-header {
min-height: 58px;
border-bottom: 1px solid #e6ecf3;
background: #f8fafd;
padding: 10px 16px;
}
.v2-vehicle-live-section .v2-live-report-meta {
max-width: min(520px, 42vw);
color: #65778d;
font-size: 10px;
font-variant-numeric: tabular-nums;
}
.v2-vehicle-command-card .v2-live-grid {
display: grid;
grid-template-columns: repeat(6, minmax(0, 1fr));
background: #fff;
}
.v2-vehicle-command-card .v2-live-grid > div {
position: relative;
min-width: 0;
min-height: 104px;
border-top: 0;
border-left: 1px solid #e7edf4;
padding: 15px 16px 14px;
}
.v2-vehicle-command-card .v2-live-grid > div:first-child {
border-left: 0;
}
.v2-vehicle-command-card .v2-live-grid > div:nth-child(4) {
border-left: 1px solid #e7edf4;
}
.v2-vehicle-command-card .v2-live-grid > div:nth-child(n + 4) {
border-top: 0;
}
.v2-vehicle-command-card .v2-live-grid > div::after {
position: absolute;
right: 16px;
bottom: 0;
left: 16px;
height: 3px;
border-radius: 999px 999px 0 0;
background: #dce5ef;
content: "";
}
.v2-vehicle-command-card .v2-live-grid > div:nth-child(-n + 4)::after {
background: #8eb6f3;
}
.v2-vehicle-command-card .v2-live-grid small {
color: #6c7d92;
font-size: 10px;
font-weight: 600;
}
.v2-vehicle-command-card .v2-live-grid strong,
.v2-vehicle-command-card .v2-live-source-link {
margin-top: 9px;
color: #213950;
font-size: 22px;
font-weight: 750;
letter-spacing: -.025em;
line-height: 1;
}
.v2-vehicle-command-card .v2-live-grid strong.is-text {
font-size: 15px;
letter-spacing: 0;
}
.v2-vehicle-command-card .v2-live-source-link .semi-button-content {
display: flex;
min-width: 0;
align-items: center;
justify-content: space-between;
gap: 6px;
}
.v2-vehicle-command-card .v2-live-grid em {
margin-left: 4px;
color: #8290a3;
font-size: 9px;
letter-spacing: 0;
}
.v2-vehicle-command-card .v2-live-grid > div > span:last-child {
display: block;
margin-top: 8px;
overflow: hidden;
color: #8996a7;
font-size: 9px;
line-height: 1.35;
text-overflow: ellipsis;
white-space: nowrap;
}
.v2-vehicle-command-card .v2-current-location {
min-height: 58px;
grid-template-columns: minmax(310px, .8fr) minmax(420px, 1.2fr);
border-top: 1px solid #e4eaf2;
background: #f8fafd;
}
.v2-vehicle-command-card .v2-current-location > span {
padding: 9px 16px;
}
.v2-vehicle-command-card .v2-current-location small {
font-size: 10px;
font-weight: 600;
}
.v2-vehicle-command-card .v2-current-location strong {
color: #405269;
font-size: 11px;
}
.v2-vehicle-command-card .v2-current-address-action.semi-button {
min-height: 30px;
border-radius: 7px;
font-size: 10px;
}
.v2-vehicle-record-page .v2-record-grid {
gap: 14px;
}
.v2-vehicle-record-page .v2-events-card .v2-event-list .v2-event-row {
min-height: 48px;
}
.v2-vehicle-record-page .v2-events-card .v2-event-row strong {
font-size: 11px;
}
.v2-vehicle-record-page .v2-events-card .v2-event-row p,
.v2-vehicle-record-page .v2-events-card .v2-event-row time {
font-size: 9px;
}
@media (max-width: 1400px) {
.v2-vehicle-command-header {
grid-template-columns: minmax(300px, .85fr) minmax(0, 1.15fr);
}
.v2-vehicle-command-header .v2-identity-actions {
grid-column: 1 / -1;
flex-wrap: nowrap;
justify-content: flex-start;
overflow-x: auto;
border-top: 1px solid #e4eaf2;
border-left: 0;
padding: 9px 14px;
scrollbar-width: thin;
}
.v2-vehicle-command-header .v2-identity-actions > .semi-button {
min-width: 118px;
flex: 0 0 auto;
}
}
@media (max-width: 900px) {
.v2-vehicle-record-page {
gap: 10px;
padding: 10px 12px 14px;
}
.v2-vehicle-command-header {
grid-template-columns: minmax(250px, .8fr) minmax(0, 1.2fr);
}
.v2-vehicle-command-header .v2-identity-primary {
padding: 14px 16px;
}
.v2-vehicle-command-header .v2-identity-meta {
grid-template-columns: minmax(0, 1.1fr) minmax(130px, .9fr);
border-top: 0;
border-bottom: 0;
border-left: 1px solid #e4eaf2;
padding: 13px 0;
}
.v2-vehicle-command-header .v2-identity-meta > div {
padding-inline: 15px;
}
.v2-vehicle-command-header .v2-identity-meta > div + div {
margin: 0;
border-top: 0;
border-left: 1px solid #e7edf4;
padding: 0 15px;
}
.v2-vehicle-command-header .v2-identity-actions {
display: flex;
grid-template-columns: none;
gap: 7px;
}
.v2-vehicle-command-card .v2-live-grid {
display: flex;
overflow-x: auto;
overscroll-behavior-x: contain;
scroll-snap-type: x proximity;
scrollbar-width: thin;
}
.v2-vehicle-command-card .v2-live-grid > div {
min-width: 148px;
min-height: 100px;
flex: 0 0 148px;
border-top: 0;
border-left: 1px solid #e7edf4;
scroll-snap-align: start;
}
.v2-vehicle-command-card .v2-live-grid > div:first-child {
border-left: 0;
}
.v2-vehicle-command-card .v2-current-location {
grid-template-columns: minmax(210px, .78fr) minmax(0, 1.22fr);
}
.v2-vehicle-command-card .v2-current-location > span + span {
border-top: 0;
border-left: 1px solid #e4eaf2;
}
.v2-vehicle-record-nav .v2-workspace-panel-header {
min-height: 50px;
padding: 7px 9px;
}
.v2-vehicle-record-nav .v2-workspace-panel-copy > .semi-typography {
display: none;
}
}
@media (max-width: 680px) {
.v2-vehicle-record-page {
gap: 8px;
padding: 8px;
}
.v2-vehicle-command-card.semi-card {
border-radius: 11px;
}
.v2-vehicle-command-header {
grid-template-columns: 1fr;
}
.v2-vehicle-command-header .v2-identity-primary {
padding: 13px 13px 11px;
}
.v2-vehicle-command-header .v2-plate {
min-height: 38px;
padding-inline: 12px;
font-size: 16px;
}
.v2-vehicle-command-header .v2-identity-meta {
grid-template-columns: minmax(0, 1.15fr) minmax(120px, .85fr);
border-top: 1px solid #e7edf4;
border-left: 0;
padding: 10px 0;
}
.v2-vehicle-command-header .v2-identity-meta > div {
padding-inline: 12px;
}
.v2-vehicle-command-header .v2-identity-meta > div + div {
padding-inline: 12px;
}
.v2-vehicle-command-header .v2-identity-meta strong {
margin-top: 5px;
font-size: 11px;
}
.v2-vehicle-command-header .v2-identity-sources {
flex-wrap: nowrap;
overflow-x: auto;
margin-top: 5px;
scrollbar-width: none;
}
.v2-vehicle-command-header .v2-identity-meta .semi-tag {
flex: 0 0 auto;
}
.v2-vehicle-command-header .v2-identity-actions {
display: flex;
grid-template-columns: none;
gap: 6px;
padding: 7px 10px;
scrollbar-width: none;
}
.v2-vehicle-command-header .v2-identity-actions::-webkit-scrollbar,
.v2-vehicle-command-header .v2-identity-sources::-webkit-scrollbar {
display: none;
}
.v2-vehicle-command-header .v2-identity-actions > .semi-button {
width: auto;
min-width: 112px;
min-height: 38px;
flex: 0 0 auto;
}
.v2-vehicle-live-section > .v2-workspace-panel-header {
min-height: 52px;
padding: 8px 11px;
}
.v2-vehicle-live-section .v2-workspace-panel-copy > .semi-typography {
display: none;
}
.v2-vehicle-live-section .v2-live-report-meta {
max-width: 48vw;
font-size: 9px;
}
.v2-vehicle-command-card .v2-live-grid > div {
min-width: 138px;
min-height: 94px;
flex-basis: 138px;
padding: 13px 12px 12px;
}
.v2-vehicle-command-card .v2-live-grid strong,
.v2-vehicle-command-card .v2-live-source-link {
margin-top: 8px;
font-size: 20px;
}
.v2-vehicle-command-card .v2-current-location {
grid-template-columns: 1fr;
}
.v2-vehicle-command-card .v2-current-location > span {
min-height: 46px;
padding: 8px 11px;
}
.v2-vehicle-command-card .v2-current-location > span + span {
border-top: 1px solid #e4eaf2;
border-left: 0;
}
.v2-vehicle-record-nav .v2-workspace-panel-header {
grid-template-columns: auto minmax(0, 1fr);
}
.v2-vehicle-record-nav .v2-workspace-panel-copy > h5.semi-typography {
font-size: 11px;
}
.v2-vehicle-section-nav > .semi-button {
min-height: 32px;
}
.v2-vehicle-record-page .v2-record-grid {
gap: 10px;
}
.v2-vehicle-record-page .v2-single-map-card {
min-height: 330px;
}
}