refine Semi UI vehicle evidence hierarchy

This commit is contained in:
lingniu
2026-07-18 03:52:05 +08:00
parent 3cebfe1982
commit 8291f5ea69
5 changed files with 128 additions and 75 deletions

View File

@@ -149,8 +149,11 @@ test('starts without a selection and supports expand, collapse, reselection, and
for (const name of ['最新上报', '实时状态', '车辆信息']) {
expect(screen.getByRole('heading', { name, level: 5 })).toBeInTheDocument();
}
expect(screen.getByText('车辆最近一次有效实时数据')).toBeInTheDocument();
expect(screen.getByText('身份、车型与来源覆盖')).toBeInTheDocument();
expect(view.container.querySelector('.v2-report-summary')).toHaveTextContent('平台接收时间');
expect(view.container.querySelector('.v2-report-summary')).toHaveTextContent('主协议JT808');
expect(view.container.querySelector('.v2-metric-grid > div:first-child')).toHaveTextContent('速度42');
expect(view.container.querySelector('.v2-metric-grid > div:nth-child(4)')).toHaveTextContent('今日里程18.6km');
expect(view.container.querySelector('.v2-status-text')).toHaveClass('semi-tag');
expect(screen.getByRole('button', { name: '取消选择车辆' })).toHaveClass('semi-button');
expect(screen.getByRole('button', { name: '查看总里程全部来源' })).toHaveClass('semi-button', 'v2-metric-source-link');

View File

@@ -268,11 +268,13 @@ function VehicleDetailCard({
<WorkspacePanelHeader
variant="compact"
title="最新上报"
description="车辆最近一次有效实时数据"
meta={<Tag color={vehicle.online ? 'green' : 'grey'} type="light" size="small">{relativeFreshness(vehicle.lastSeen)}</Tag>}
/>
<div className="v2-detail-report-time"><small></small><strong>{vehicle.lastSeen || '暂无上报'}</strong></div>
<div className="v2-detail-report-source"><span><small></small><b>{vehicle.primaryProtocol || '未知'}</b></span><span><small></small><b>{accessSource || '待补充'}</b></span></div>
<div className="v2-report-summary">
<span className="is-time"><small></small><strong>{vehicle.lastSeen || '暂无上报'}</strong></span>
<span><small></small><b>{vehicle.primaryProtocol || '未知'}</b></span>
<span><small></small><b>{accessSource || '待补充'}</b></span>
</div>
<dl className="v2-detail-list">
<div><dt></dt><dd><Button theme="borderless" type="tertiary" className="v2-detail-source-link" aria-label="查看全部位置来源" title="展开全部位置来源" onClick={() => setSourceEvidenceOpen(true)}>{hasRealtimeLocation(vehicle) ? `${vehicle.longitude.toFixed(6)}, ${vehicle.latitude.toFixed(6)}` : '—'}</Button></dd></div>
<div><dt></dt><dd>{hasRealtimeLocation(vehicle) ? address?.formattedAddress || '位置解析中' : '暂无实时位置'}</dd></div>
@@ -291,7 +293,7 @@ function VehicleDetailCard({
<div><small>{mileageLabel}</small><Button theme="borderless" type="tertiary" className="v2-metric-source-link" aria-label="查看总里程全部来源" title="展开全部里程来源" onClick={() => setSourceEvidenceOpen(true)}><span>{hasRealtimeMileage(vehicle) ? formatNumber(vehicle.totalMileageKm, 1) : '—'}<em>{hasRealtimeMileage(vehicle) ? 'km' : ''}</em></span><IconChevronRight /></Button></div>
<div><small></small><Button theme="borderless" type="tertiary" className="v2-metric-source-link" aria-label="查看今日里程全部来源" title="展开全部里程来源" onClick={() => setSourceEvidenceOpen(true)}><span>{dailyMileage == null ? '—' : formatNumber(dailyMileage, 1)}<em>{dailyMileage == null ? '' : 'km'}</em></span><IconChevronRight /></Button></div>
<div><small></small><strong>{statusLabel(status)}</strong></div>
<div><small></small><strong>{formatNumber(activeAlerts?.total ?? 0)}<em></em></strong></div>
<div className={(activeAlerts?.total ?? 0) > 0 ? 'has-alerts' : ''}><small></small><strong>{formatNumber(activeAlerts?.total ?? 0)}<em></em></strong></div>
</div>
{gbHighlights.length ? <div className="v2-gb-highlight-grid">{gbHighlights.map((item) => <div key={`${item.key}-${item.sourceField}`} title={item.description}><small>{item.label}</small><strong>{formatTelemetryValue(item.value, item.displayValue)}<em>{item.unit}</em></strong></div>)}</div> : null}
</section>

View File

@@ -81,6 +81,9 @@ test('polls lightweight single-vehicle realtime data and passes its report inter
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(commandCard?.querySelector('.v2-live-grid > div:first-child')).toHaveTextContent('速度');
expect(commandCard?.querySelector('.v2-live-grid > div:nth-child(2)')).toHaveTextContent('SOC');
expect(commandCard?.querySelector('.v2-live-grid > div:nth-child(4)')).toHaveTextContent('当日里程');
expect(screen.getByRole('heading', { name: '最新上报', level: 5 })).toBeInTheDocument();
expect(screen.getByRole('heading', { name: '实时位置', level: 5 })).toBeInTheDocument();
expect(screen.getByRole('heading', { name: '最近事件', level: 5 })).toBeInTheDocument();

View File

@@ -8816,31 +8816,37 @@ button, a { -webkit-tap-highlight-color: transparent; }
border-bottom-color: #dce9f8;
background: #f5f9ff;
}
.v2-detail-report-time {
.v2-report-summary {
display: grid;
grid-template-columns: minmax(0,1.45fr) minmax(0,.8fr) minmax(0,1fr);
gap: 0;
overflow: hidden;
margin: 9px 10px 0;
border: 1px solid #e2ebf6;
border-radius: 8px;
background: #fff;
border: 1px solid #dce7f4;
border-radius: 9px;
background: #f8fafd;
}
.v2-report-summary > span {
min-width: 0;
padding: 8px 9px;
}
.v2-detail-report-time small,
.v2-detail-report-time strong { display: block; }
.v2-detail-report-time small { color: #7186a1; font-size: 8px; }
.v2-detail-report-time strong {
margin-top: 4px;
.v2-report-summary > span + span { border-left: 1px solid #e3eaf3; }
.v2-report-summary small,
.v2-report-summary strong,
.v2-report-summary b {
display: block;
overflow: hidden;
color: #153f73;
font-size: 12px;
font-variant-numeric: tabular-nums;
text-overflow: ellipsis;
white-space: nowrap;
}
.v2-detail-report-source { display: grid; grid-template-columns: .8fr 1.2fr; gap: 6px; margin: 6px 10px 0; }
.v2-detail-report-source > span { min-width: 0; border-radius: 7px; background: #f5f8fc; padding: 6px 8px; }
.v2-detail-report-source small,
.v2-detail-report-source b { display: block; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.v2-detail-report-source small { color: #7c8da3; font-size: 8px; }
.v2-detail-report-source b { margin-top: 3px; color: #345679; font-size: 10px; }
.v2-report-summary small { color: #7186a1; font-size: 8px; }
.v2-report-summary strong {
margin-top: 4px;
color: #153f73;
font-size: 12px;
font-variant-numeric: tabular-nums;
}
.v2-report-summary b { margin-top: 4px; color: #345679; font-size: 10px; }
.v2-detail-report .v2-detail-list { margin: 5px 10px 8px; }
.v2-detail-report .v2-detail-list > div { min-height: 24px; grid-template-columns: 44px minmax(0,1fr); align-items: center; padding-block: 3px; }
.v2-detail-report .v2-detail-list dd { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@@ -8851,6 +8857,30 @@ button, a { -webkit-tap-highlight-color: transparent; }
border-color: #e0e7f0;
border-radius: 8px;
}
.v2-detail-section > .v2-metric-grid > div {
position: relative;
min-height: 68px;
padding: 10px 11px 9px;
}
.v2-detail-section > .v2-metric-grid > div::after {
position: absolute;
right: 11px;
bottom: 0;
left: 11px;
height: 2px;
border-radius: 999px 999px 0 0;
background: #dce5ef;
content: "";
}
.v2-detail-section > .v2-metric-grid > div:nth-child(1)::after { background: #78a9ef; }
.v2-detail-section > .v2-metric-grid > div:nth-child(2)::after { background: #65bf94; }
.v2-detail-section > .v2-metric-grid > div:nth-child(3)::after { background: #79a7ea; }
.v2-detail-section > .v2-metric-grid > div:nth-child(4)::after { background: #2f7de9; }
.v2-detail-section > .v2-metric-grid > div:nth-child(5)::after { background: #43b77d; }
.v2-detail-section > .v2-metric-grid > .has-alerts::after { background: #eb6d70; }
.v2-detail-section > .v2-metric-grid > div:nth-child(4) strong,
.v2-detail-section > .v2-metric-grid > div:nth-child(4) .v2-metric-source-link { color: #1268df; }
.v2-detail-section > .v2-metric-grid > .has-alerts strong { color: #c84349; }
.v2-detail-section > .v2-gb-highlight-grid {
margin: 0 10px 10px;
}
@@ -8899,7 +8929,8 @@ button, a { -webkit-tap-highlight-color: transparent; }
.v2-detail-section > .v2-workspace-panel-header .v2-workspace-panel-meta {
font-size: 10px;
}
.v2-detail-report-time strong { font-size: 13px; }
.v2-report-summary { grid-template-columns: minmax(0,1.35fr) minmax(0,.8fr) minmax(0,1fr); }
.v2-report-summary strong { font-size: 13px; }
.v2-detail-list > div { font-size: 10px; }
.v2-metric-grid small { font-size: 9px; }
.v2-metric-grid strong,
@@ -9022,8 +9053,11 @@ button, a { -webkit-tap-highlight-color: transparent; }
.v2-monitor-mobile-cards dd.is-today,
.v2-monitor-mobile-protocol { color: #1268df; font-weight: 750; }
.v2-monitor-mobile-cards > .v2-monitor-mobile-card.semi-card { contain-intrinsic-size: auto 190px; }
.v2-detail-report-time strong { font-size: 13px; }
.v2-detail-report-source b { font-size: 11px; }
.v2-report-summary {
grid-template-columns: minmax(0,1.3fr) minmax(0,.85fr) minmax(0,1fr);
}
.v2-report-summary strong { font-size: 13px; }
.v2-report-summary b { font-size: 11px; }
.v2-mileage-page { height: 100%; min-height: 0; gap: 6px; overflow: hidden; padding: 7px; }
.v2-mileage-query-panel { flex: 0 0 auto; padding: 0; }

View File

@@ -4845,7 +4845,7 @@
.v2-vehicle-command-header {
display: grid;
grid-template-columns: minmax(310px, .9fr) minmax(360px, 1.15fr) minmax(360px, auto);
grid-template-columns: minmax(270px, .78fr) minmax(320px, 1.08fr) minmax(300px, .9fr);
align-items: stretch;
background: #fff;
}
@@ -4857,18 +4857,18 @@
align-content: center;
align-items: center;
justify-content: start;
gap: 8px 10px;
padding: 18px 20px;
gap: 7px 10px;
padding: 14px 16px;
}
.v2-vehicle-command-header .v2-plate {
min-height: 42px;
min-height: 40px;
border-color: #b9d2fb;
border-radius: 9px;
background: #edf4ff;
padding-inline: 14px;
padding-inline: 13px;
color: var(--v2-blue);
font-size: 18px;
font-size: 17px;
letter-spacing: .01em;
box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .65);
}
@@ -4931,19 +4931,19 @@
gap: 0;
border: 0;
border-left: 1px solid #e4eaf2;
padding: 16px 0;
padding: 12px 0;
}
.v2-vehicle-command-header .v2-identity-meta > div {
min-width: 0;
padding: 0 20px;
padding: 0 16px;
}
.v2-vehicle-command-header .v2-identity-meta > div + div {
margin: 0;
border: 0;
border-left: 1px solid #e7edf4;
padding: 0 20px;
padding: 0 16px;
}
.v2-vehicle-command-header .v2-identity-meta small {
@@ -4955,7 +4955,7 @@
.v2-vehicle-command-header .v2-identity-meta strong {
display: block;
margin-top: 7px;
margin-top: 6px;
overflow: hidden;
color: #2c4057;
font-size: 12px;
@@ -4969,7 +4969,7 @@
min-width: 0;
flex-wrap: wrap;
gap: 5px;
margin: 7px 0 0;
margin: 6px 0 0;
}
.v2-vehicle-command-header .v2-identity-meta .semi-tag {
@@ -4984,21 +4984,23 @@
.v2-vehicle-command-header .v2-identity-actions {
display: flex;
min-width: 0;
grid-column: auto;
flex-wrap: wrap;
align-content: center;
justify-content: flex-end;
gap: 7px;
gap: 6px;
border-left: 1px solid #e4eaf2;
padding: 15px 18px;
padding: 12px 14px;
}
.v2-vehicle-command-header .v2-identity-actions > .semi-button {
min-width: 100px;
min-height: 38px;
width: auto;
min-width: 88px;
min-height: 34px;
flex: 0 1 auto;
border-radius: 8px;
padding-inline: 12px;
font-size: 11px;
padding-inline: 10px;
font-size: 10px;
font-weight: 650;
}
@@ -5014,10 +5016,10 @@
}
.v2-vehicle-live-section > .v2-workspace-panel-header {
min-height: 58px;
min-height: 48px;
border-bottom: 1px solid #e6ecf3;
background: #f8fafd;
padding: 10px 16px;
padding: 8px 14px;
}
.v2-vehicle-live-section .v2-live-report-meta {
@@ -5036,10 +5038,10 @@
.v2-vehicle-command-card .v2-live-grid > div {
position: relative;
min-width: 0;
min-height: 104px;
min-height: 88px;
border-top: 0;
border-left: 1px solid #e7edf4;
padding: 15px 16px 14px;
padding: 12px 14px 11px;
}
.v2-vehicle-command-card .v2-live-grid > div:first-child {
@@ -5056,18 +5058,20 @@
.v2-vehicle-command-card .v2-live-grid > div::after {
position: absolute;
right: 16px;
right: 14px;
bottom: 0;
left: 16px;
height: 3px;
left: 14px;
height: 2px;
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 > div:nth-child(1)::after { background: #78a9ef; }
.v2-vehicle-command-card .v2-live-grid > div:nth-child(2)::after { background: #65bf94; }
.v2-vehicle-command-card .v2-live-grid > div:nth-child(3)::after { background: #79a7ea; }
.v2-vehicle-command-card .v2-live-grid > div:nth-child(4)::after { background: #2f7de9; }
.v2-vehicle-command-card .v2-live-grid > div:nth-child(6)::after { background: #43b77d; }
.v2-vehicle-command-card .v2-live-grid small {
color: #6c7d92;
@@ -5077,9 +5081,9 @@
.v2-vehicle-command-card .v2-live-grid strong,
.v2-vehicle-command-card .v2-live-source-link {
margin-top: 9px;
margin-top: 7px;
color: #213950;
font-size: 22px;
font-size: 20px;
font-weight: 750;
letter-spacing: -.025em;
line-height: 1;
@@ -5107,7 +5111,7 @@
.v2-vehicle-command-card .v2-live-grid > div > span:last-child {
display: block;
margin-top: 8px;
margin-top: 6px;
overflow: hidden;
color: #8996a7;
font-size: 9px;
@@ -5117,14 +5121,14 @@
}
.v2-vehicle-command-card .v2-current-location {
min-height: 58px;
min-height: 48px;
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;
padding: 7px 14px;
}
.v2-vehicle-command-card .v2-current-location small {
@@ -5160,7 +5164,7 @@
font-size: 9px;
}
@media (max-width: 1400px) {
@media (max-width: 1180px) {
.v2-vehicle-command-header {
grid-template-columns: minmax(300px, .85fr) minmax(0, 1.15fr);
}
@@ -5172,12 +5176,12 @@
overflow-x: auto;
border-top: 1px solid #e4eaf2;
border-left: 0;
padding: 9px 14px;
padding: 8px 12px;
scrollbar-width: thin;
}
.v2-vehicle-command-header .v2-identity-actions > .semi-button {
min-width: 118px;
min-width: 112px;
flex: 0 0 auto;
}
}
@@ -5276,20 +5280,20 @@
}
.v2-vehicle-command-header .v2-identity-primary {
padding: 13px 13px 11px;
padding: 10px 12px 8px;
}
.v2-vehicle-command-header .v2-plate {
min-height: 38px;
min-height: 36px;
padding-inline: 12px;
font-size: 16px;
font-size: 15px;
}
.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;
padding: 8px 0;
}
.v2-vehicle-command-header .v2-identity-meta > div {
@@ -5320,7 +5324,7 @@
display: flex;
grid-template-columns: none;
gap: 6px;
padding: 7px 10px;
padding: 6px 10px;
scrollbar-width: none;
}
@@ -5337,7 +5341,7 @@
}
.v2-vehicle-live-section > .v2-workspace-panel-header {
min-height: 52px;
min-height: 46px;
padding: 8px 11px;
}
@@ -5351,16 +5355,16 @@
}
.v2-vehicle-command-card .v2-live-grid > div {
min-width: 138px;
min-height: 94px;
flex-basis: 138px;
padding: 13px 12px 12px;
min-width: 134px;
min-height: 88px;
flex-basis: 134px;
padding: 11px 12px 10px;
}
.v2-vehicle-command-card .v2-live-grid strong,
.v2-vehicle-command-card .v2-live-source-link {
margin-top: 8px;
font-size: 20px;
margin-top: 7px;
font-size: 19px;
}
.v2-vehicle-command-card .v2-current-location {
@@ -5368,8 +5372,8 @@
}
.v2-vehicle-command-card .v2-current-location > span {
min-height: 46px;
padding: 8px 11px;
min-height: 38px;
padding: 5px 11px;
}
.v2-vehicle-command-card .v2-current-location > span + span {
@@ -5378,7 +5382,9 @@
}
.v2-vehicle-record-nav .v2-workspace-panel-header {
min-height: 50px;
grid-template-columns: auto minmax(0, 1fr);
padding-block: 5px;
}
.v2-vehicle-record-nav .v2-workspace-panel-copy > h5.semi-typography {
@@ -5396,6 +5402,11 @@
.v2-vehicle-record-page .v2-single-map-card {
min-height: 330px;
}
.v2-vehicle-record-page > .v2-source-evidence .v2-source-evidence-trigger {
min-height: 54px;
padding-block: 7px;
}
}
/*