feat: refine responsive vehicle record

This commit is contained in:
lingniu
2026-07-19 03:08:27 +08:00
parent c695d6e431
commit bbacf3b913
3 changed files with 295 additions and 4 deletions

View File

@@ -231,7 +231,11 @@ function TelemetrySourceCell({ item }: { item: LatestTelemetryValue }) {
} }
function telemetryRowKey(item?: LatestTelemetryValue) { function telemetryRowKey(item?: LatestTelemetryValue) {
return `${item?.protocol ?? ''}-${item?.category ?? ''}-${item?.sourceField ?? ''}-${item?.frameId ?? ''}`; return [
item?.protocol ?? '',
item?.sourceEndpoint ?? '',
item?.key || `${item?.category ?? ''}:${item?.sourceField ?? ''}`
].join('\u0000');
} }
function TelemetryPanel({ data, pending, error }: { data?: LatestTelemetryResponse; pending: boolean; error?: string }) { function TelemetryPanel({ data, pending, error }: { data?: LatestTelemetryResponse; pending: boolean; error?: string }) {
@@ -265,13 +269,13 @@ function TelemetryPanel({ data, pending, error }: { data?: LatestTelemetryRespon
.map((key) => ({ key, label: categoryLabels.get(key) ?? key, count: indexed.valuesByProtocolCategory.get(`${activeProtocol}\u0000${key}`)?.length ?? 0 })); .map((key) => ({ key, label: categoryLabels.get(key) ?? key, count: indexed.valuesByProtocolCategory.get(`${activeProtocol}\u0000${key}`)?.length ?? 0 }));
const activeCategory = indexed.valuesByProtocolCategory.has(`${activeProtocol}\u0000${selectedCategory}`) ? selectedCategory : categories[0]?.key ?? ''; const activeCategory = indexed.valuesByProtocolCategory.has(`${activeProtocol}\u0000${selectedCategory}`) ? selectedCategory : categories[0]?.key ?? '';
const visibleMetrics = indexed.valuesByProtocolCategory.get(`${activeProtocol}\u0000${activeCategory}`) ?? []; const visibleMetrics = indexed.valuesByProtocolCategory.get(`${activeProtocol}\u0000${activeCategory}`) ?? [];
const columns = [ const columns = useMemo(() => [
{ title: '字段 / 协议映射', dataIndex: 'label', width: 260, render: (_: unknown, item: LatestTelemetryValue) => <TelemetryFieldCell item={item} /> }, { title: '字段 / 协议映射', dataIndex: 'label', width: 260, render: (_: unknown, item: LatestTelemetryValue) => <TelemetryFieldCell item={item} /> },
{ title: '当前值', dataIndex: 'value', width: 170, render: (_: unknown, item: LatestTelemetryValue) => <TelemetryValueCell item={item} onInspect={setInspectedValue} /> }, { title: '当前值', dataIndex: 'value', width: 170, render: (_: unknown, item: LatestTelemetryValue) => <TelemetryValueCell item={item} onInspect={setInspectedValue} /> },
{ title: '质量 / 新鲜度', dataIndex: 'quality', width: 170, render: (_: unknown, item: LatestTelemetryValue) => <TelemetryQualityCell item={item} /> }, { title: '质量 / 新鲜度', dataIndex: 'quality', width: 170, render: (_: unknown, item: LatestTelemetryValue) => <TelemetryQualityCell item={item} /> },
{ title: '设备 / 接收时间', dataIndex: 'deviceTime', width: 230, render: (_: unknown, item: LatestTelemetryValue) => <TelemetryTimeCell item={item} /> }, { title: '设备 / 接收时间', dataIndex: 'deviceTime', width: 230, render: (_: unknown, item: LatestTelemetryValue) => <TelemetryTimeCell item={item} /> },
{ title: '数据来源', dataIndex: 'protocol', width: 180, render: (_: unknown, item: LatestTelemetryValue) => <TelemetrySourceCell item={item} /> } { title: '数据来源', dataIndex: 'protocol', width: 180, render: (_: unknown, item: LatestTelemetryValue) => <TelemetrySourceCell item={item} /> }
]; ], []);
const state = pending const state = pending
? <div className="v2-telemetry-state" role="status"><strong></strong><span></span></div> ? <div className="v2-telemetry-state" role="status"><strong></strong><span></span></div>
: error : error

View File

@@ -92,6 +92,8 @@ describe('V2 production entry', () => {
expect(corePageSources.VehiclePage).toContain('<List.Item className="v2-telemetry-mobile-item"'); expect(corePageSources.VehiclePage).toContain('<List.Item className="v2-telemetry-mobile-item"');
expect(corePageSources.VehiclePage).toContain('className="v2-telemetry-mobile-action"'); expect(corePageSources.VehiclePage).toContain('className="v2-telemetry-mobile-action"');
expect(corePageSources.VehiclePage).toContain("placement={mobileLayout ? 'bottom' : 'right'}"); expect(corePageSources.VehiclePage).toContain("placement={mobileLayout ? 'bottom' : 'right'}");
expect(corePageSources.VehiclePage).toContain("item?.sourceEndpoint ?? ''");
expect(corePageSources.VehiclePage).toContain('const columns = useMemo(() => [');
expect(corePageSources.VehiclePage).toContain('<List'); expect(corePageSources.VehiclePage).toContain('<List');
expect(corePageSources.VehiclePage).toContain('className="v2-event-list"'); expect(corePageSources.VehiclePage).toContain('className="v2-event-list"');
expect(corePageSources.VehiclePage).toContain('useMobileLayout'); expect(corePageSources.VehiclePage).toContain('useMobileLayout');
@@ -394,6 +396,10 @@ describe('V2 production entry', () => {
expect(workspaceStyles).toContain('.v2-vehicle-directory-v3 .v2-mobile-filter-toggle-copy small {'); expect(workspaceStyles).toContain('.v2-vehicle-directory-v3 .v2-mobile-filter-toggle-copy small {');
expect(workspaceStyles).toContain('.v2-vehicle-mobile-filter-sidesheet.semi-sidesheet-bottom .semi-sidesheet-inner {'); expect(workspaceStyles).toContain('.v2-vehicle-mobile-filter-sidesheet.semi-sidesheet-bottom .semi-sidesheet-inner {');
expect(workspaceStyles).toContain('grid-template-columns: repeat(2, minmax(0, 1fr));'); expect(workspaceStyles).toContain('grid-template-columns: repeat(2, minmax(0, 1fr));');
expect(workspaceStyles).toContain('.v2-vehicle-record-v3 .v2-vehicle-command-card > .semi-card-body {');
expect(workspaceStyles).toContain('grid-template-columns: minmax(210px, .62fr) minmax(0, 1.38fr);');
expect(workspaceStyles).toContain('.v2-vehicle-record-v3 .v2-vehicle-command-card .v2-live-grid {');
expect(workspaceStyles).toContain('.v2-vehicle-mobile-actions-sidesheet .v2-vehicle-mobile-actions-list {');
expect(corePageSources.MonitorPage).not.toContain('<select'); expect(corePageSources.MonitorPage).not.toContain('<select');
expect(corePageSources.MonitorPage).not.toContain('<textarea'); expect(corePageSources.MonitorPage).not.toContain('<textarea');
expect(corePageSources.MonitorPage).not.toContain('<button'); expect(corePageSources.MonitorPage).not.toContain('<button');

View File

@@ -16818,7 +16818,7 @@
.v2-vehicle-record-v3.v2-vehicle-record-page { .v2-vehicle-record-v3.v2-vehicle-record-page {
gap: 7px; gap: 7px;
padding: 7px 7px 88px; padding: 7px;
} }
.v2-vehicle-record-v3 .v2-vehicle-command-card.semi-card { .v2-vehicle-record-v3 .v2-vehicle-command-card.semi-card {
@@ -18263,3 +18263,284 @@
max-height: min(42dvh, 180px); max-height: min(42dvh, 180px);
} }
} }
/*
* Short landscape vehicle record.
* The shell already reserves the fixed mobile navigation. Split the command
* card into an identity/action rail and a live evidence canvas so the latest
* report remains visible before the user scrolls.
*/
@media (max-width: 900px) and (max-height: 480px) and (orientation: landscape) {
.v2-vehicle-record-v3.v2-vehicle-record-page {
gap: 7px;
padding: 6px 7px;
}
.v2-vehicle-record-v3 .v2-vehicle-command-card > .semi-card-body {
display: grid;
grid-template-columns: minmax(210px, .62fr) minmax(0, 1.38fr);
align-items: stretch;
}
.v2-vehicle-record-v3 .v2-vehicle-record-command.v2-workspace-command-bar {
min-height: 0;
align-content: start;
border-right: 1px solid #dfe7f0;
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-workspace-command-copy {
min-height: 57px;
gap: 2px;
border-right: 0;
border-bottom: 1px solid #e4eaf2;
padding: 5px 8px;
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-plate {
min-height: 29px;
padding-inline: 7px;
font-size: 12px;
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-online-label.semi-tag {
min-height: 20px;
padding-inline: 6px;
font-size: 8px;
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-identity-vin {
gap: 3px;
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-identity-vin small {
font-size: 8px;
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-identity-vin b {
font-size: 8px;
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-identity-vin .semi-button {
width: 22px;
min-width: 22px;
height: 22px;
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-workspace-command-actions {
grid-template-columns: minmax(0, 1fr);
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-workspace-command-meta {
border-right: 0;
border-bottom: 1px solid #e7edf4;
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-identity-meta {
grid-template-columns: minmax(0, 1fr);
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-identity-meta > span {
min-height: 35px;
padding: 4px 8px;
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-identity-meta > span + span {
border-top: 1px solid #edf1f6;
border-left: 0;
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-identity-meta small {
font-size: 8px;
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-identity-meta strong {
margin-top: 1px;
font-size: 9px;
line-height: 1.2;
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-identity-sources {
flex-wrap: nowrap;
gap: 3px;
margin-top: 2px;
overflow: hidden;
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-identity-sources .semi-tag {
min-height: 18px;
padding-inline: 4px;
font-size: 7px;
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-identity-actions {
min-height: 43px;
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 4px;
padding: 5px 6px;
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-identity-actions > .semi-button {
min-height: 33px;
border-radius: 7px;
padding: 2px 4px;
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-identity-actions > .semi-button .semi-button-content {
gap: 3px;
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-identity-actions > .semi-button .semi-button-content-left {
font-size: 12px;
}
.v2-vehicle-record-v3 .v2-vehicle-command-header.v2-vehicle-record-command .v2-identity-actions > .semi-button .semi-button-content-right {
font-size: 8px;
}
.v2-vehicle-record-v3 .v2-vehicle-live-section {
min-width: 0;
border-top: 0;
}
.v2-vehicle-record-v3 .v2-vehicle-live-section > .v2-workspace-panel-header {
min-height: 36px;
padding: 4px 7px;
}
.v2-vehicle-record-v3 .v2-vehicle-live-section .v2-workspace-panel-copy > h5.semi-typography {
font-size: 10px;
}
.v2-vehicle-record-v3 .v2-vehicle-live-section .v2-live-report-meta {
max-width: 64%;
font-size: 7px;
}
.v2-vehicle-record-v3 .v2-vehicle-command-card .v2-live-grid {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
.v2-vehicle-record-v3 .v2-vehicle-command-card .v2-live-grid > .v2-live-metric {
min-height: 58px;
border-top: 0;
border-left: 1px solid #e7edf4;
padding: 6px 7px 5px;
}
.v2-vehicle-record-v3 .v2-vehicle-command-card .v2-live-grid > .v2-live-metric:nth-child(3n + 1) {
border-left: 0;
}
.v2-vehicle-record-v3 .v2-vehicle-command-card .v2-live-grid > .v2-live-metric:nth-child(n + 4) {
border-top: 1px solid #e7edf4;
}
.v2-vehicle-record-v3 .v2-vehicle-command-card .v2-live-grid > .v2-live-metric::after {
right: 7px;
left: 7px;
}
.v2-vehicle-record-v3 .v2-vehicle-command-card .v2-live-grid small {
font-size: 8px;
}
.v2-vehicle-record-v3 .v2-vehicle-command-card .v2-live-grid strong,
.v2-vehicle-record-v3 .v2-vehicle-command-card .v2-live-source-link {
margin-top: 3px;
font-size: 13px;
}
.v2-vehicle-record-v3 .v2-vehicle-command-card .v2-live-grid strong.is-text {
font-size: 9px;
}
.v2-vehicle-record-v3 .v2-vehicle-command-card .v2-live-primary-protocol.semi-tag {
min-height: 19px;
margin-top: 3px;
padding-inline: 5px;
font-size: 7px;
}
.v2-vehicle-record-v3 .v2-vehicle-command-card .v2-live-source-link.semi-button {
height: 19px;
}
.v2-vehicle-record-v3 .v2-vehicle-command-card .v2-live-grid > .v2-live-metric > span:last-child {
margin-top: 3px;
font-size: 7px;
}
.v2-vehicle-record-v3 .v2-vehicle-command-card .v2-current-location {
min-height: 37px;
grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}
.v2-vehicle-record-v3 .v2-vehicle-command-card .v2-current-location > span {
gap: 4px;
padding: 4px 7px;
}
.v2-vehicle-record-v3 .v2-vehicle-command-card .v2-current-location > span + span {
border-top: 0;
border-left: 1px solid #e4eaf2;
}
.v2-vehicle-record-v3 .v2-vehicle-command-card .v2-current-location small,
.v2-vehicle-record-v3 .v2-vehicle-command-card .v2-current-location strong {
font-size: 8px;
}
.v2-vehicle-record-v3 .v2-vehicle-command-card .v2-current-address-action.semi-button {
min-height: 25px;
padding-inline: 6px;
font-size: 8px;
}
.v2-vehicle-mobile-actions-sidesheet.semi-sidesheet-bottom .semi-sidesheet-inner {
height: 100dvh !important;
max-height: 100dvh;
border-radius: 0;
}
.v2-vehicle-mobile-actions-sidesheet.semi-sidesheet-bottom .semi-sidesheet-header {
min-height: 56px;
padding: 7px 12px;
}
.v2-vehicle-mobile-actions-sidesheet.semi-sidesheet-bottom .semi-sidesheet-body {
padding: 9px 12px;
}
.v2-vehicle-mobile-actions-sidesheet.semi-sidesheet-bottom .semi-sidesheet-footer {
padding: 7px 12px;
}
.v2-vehicle-mobile-actions-sidesheet .v2-vehicle-mobile-actions-title {
gap: 1px;
}
.v2-vehicle-mobile-actions-sidesheet .v2-vehicle-mobile-actions-title > span {
font-size: 8px;
}
.v2-vehicle-mobile-actions-sidesheet .v2-vehicle-mobile-actions-list {
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 8px;
}
.v2-vehicle-mobile-actions-sidesheet .v2-vehicle-mobile-actions-list > .semi-button {
min-height: 72px;
justify-content: center;
padding-inline: 10px;
}
.v2-vehicle-mobile-actions-sidesheet .v2-vehicle-mobile-actions-list > .semi-button .semi-button-content {
justify-content: center;
}
.v2-vehicle-mobile-actions-sidesheet .v2-vehicle-mobile-actions-list > .semi-button .semi-button-content-right {
max-width: 100%;
flex: 0 1 auto;
gap: 7px;
}
}