feat: unify monitor vehicle metric rail
This commit is contained in:
@@ -170,11 +170,15 @@ test('starts without a selection and supports expand, collapse, reselection, and
|
||||
'单车详情', '轨迹回放', '历史数据', '里程查询'
|
||||
]);
|
||||
expect(view.container.querySelector('.v2-detail-protocols .v2-protocol-tag')).toHaveTextContent('JT/T 808');
|
||||
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(screen.getByRole('list', { name: '车辆实时状态指标' })).toHaveClass('v2-workspace-metric-list');
|
||||
expect(view.container.querySelector('.v2-monitor-detail-metric-rail')).toHaveClass('semi-card', 'v2-workspace-metric-rail', 'is-queue');
|
||||
expect(view.container.querySelectorAll('.v2-monitor-detail-metric-rail .v2-workspace-metric-list > [role="listitem"]')).toHaveLength(4);
|
||||
expect(view.container.querySelector('.v2-monitor-detail-metric-rail .v2-workspace-metric-list > span:first-child')).toHaveTextContent('速度42');
|
||||
expect(view.container.querySelector('.v2-monitor-detail-metric-rail .v2-workspace-metric-list > span:nth-child(2)')).toHaveTextContent('今日里程18.6km');
|
||||
expect(view.container.querySelector('.v2-monitor-detail-state')).toHaveTextContent('车辆状态行驶当前告警0条');
|
||||
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');
|
||||
expect(screen.getByRole('button', { name: '查看总里程全部来源' })).toHaveClass('semi-button', 'v2-workspace-metric-action');
|
||||
expect(screen.getByRole('button', { name: '查看全部位置来源' })).toHaveClass('semi-button', 'v2-detail-source-link');
|
||||
expect(screen.getByTestId('fleet-map')).toHaveAttribute('data-selected-vin', 'LTEST000000000001');
|
||||
expect(vehicleCardArgsSpy).toHaveBeenLastCalledWith('LTEST000000000001', vehicles[0], true);
|
||||
|
||||
@@ -10,6 +10,7 @@ import { useMonitorVehicleCard } from '../hooks/useMonitorData';
|
||||
import { withMonitorReturn } from '../routing/monitorContext';
|
||||
import { ProtocolTag } from '../shared/ProtocolTag';
|
||||
import { VehicleSourceEvidencePanel } from '../shared/VehicleSourceEvidencePanel';
|
||||
import { WorkspaceMetricRail, type WorkspaceQueueMetricRailItem } from '../shared/WorkspaceMetricRail';
|
||||
import { WorkspacePanelHeader } from '../shared/WorkspacePanelHeader';
|
||||
|
||||
function hasRealtimeLocation(vehicle: VehicleRealtimeRow) {
|
||||
@@ -70,6 +71,40 @@ export default function MonitorVehicleDetailCard({
|
||||
'hydrogen_concentration_percent', 'hydrogen_pressure_mpa', 'hydrogen_temperature_c',
|
||||
'engine_speed_rpm', 'total_voltage_v', 'total_current_a'
|
||||
].includes(item.key)).slice(0, 9);
|
||||
const alertCount = activeAlerts?.total ?? 0;
|
||||
const metricItems: WorkspaceQueueMetricRailItem[] = [
|
||||
{
|
||||
label: '速度',
|
||||
value: <>{hasRealtimeSpeed(vehicle) ? formatNumber(vehicle.speedKmh, 1) : '—'}<em className="v2-monitor-detail-unit">{hasRealtimeSpeed(vehicle) ? 'km/h' : ''}</em></>,
|
||||
note: '实时',
|
||||
tone: 'primary',
|
||||
emphasis: 'primary'
|
||||
},
|
||||
{
|
||||
label: '今日里程',
|
||||
value: <>{dailyMileage == null ? '—' : formatNumber(dailyMileage, 1)}<em className="v2-monitor-detail-unit">{dailyMileage == null ? '' : 'km'}</em></>,
|
||||
note: <span className="v2-monitor-detail-action-note">今日累计<IconChevronRight /></span>,
|
||||
tone: 'primary',
|
||||
emphasis: 'primary',
|
||||
ariaLabel: '查看今日里程全部来源',
|
||||
onClick: () => setSourceEvidenceOpen(true)
|
||||
},
|
||||
{
|
||||
label: '总里程',
|
||||
value: <>{hasRealtimeMileage(vehicle) ? formatNumber(vehicle.totalMileageKm, 1) : '—'}<em className="v2-monitor-detail-unit">{hasRealtimeMileage(vehicle) ? 'km' : ''}</em></>,
|
||||
note: <span className="v2-monitor-detail-action-note" title={mileageLabel}>{mileageLabel}<IconChevronRight /></span>,
|
||||
emphasis: 'secondary',
|
||||
ariaLabel: '查看总里程全部来源',
|
||||
onClick: () => setSourceEvidenceOpen(true)
|
||||
},
|
||||
{
|
||||
label: 'SOC',
|
||||
value: <>{hasRealtimeSOC(vehicle) ? formatNumber(vehicle.socPercent, 1) : '—'}<em className="v2-monitor-detail-unit">{hasRealtimeSOC(vehicle) ? '%' : ''}</em></>,
|
||||
note: '剩余电量',
|
||||
tone: 'success',
|
||||
emphasis: 'secondary'
|
||||
}
|
||||
];
|
||||
|
||||
return <Card className="v2-vehicle-detail" bodyStyle={{ padding: 0 }}>
|
||||
<div className="v2-detail-body" role="region" aria-label={`${vehicle.plate || vehicle.vin}车辆详情`}>
|
||||
@@ -110,14 +145,22 @@ export default function MonitorVehicleDetailCard({
|
||||
description={accessSourceLabel}
|
||||
meta={<ProtocolTag protocol={vehicle.primaryProtocol} compact unknownLabel="未知协议" />}
|
||||
/>
|
||||
<div className="v2-metric-grid">
|
||||
<div className="is-speed"><small>速度</small><strong>{hasRealtimeSpeed(vehicle) ? formatNumber(vehicle.speedKmh, 1) : '—'}<em>{hasRealtimeSpeed(vehicle) ? 'km/h' : ''}</em></strong></div>
|
||||
<div className="is-soc"><small>SOC</small><strong>{hasRealtimeSOC(vehicle) ? formatNumber(vehicle.socPercent, 1) : '—'}<em>{hasRealtimeSOC(vehicle) ? '%' : ''}</em></strong></div>
|
||||
<div className="is-mileage"><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 className="is-today"><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 className="is-status"><small>状态</small><strong>{statusLabel(status)}</strong></div>
|
||||
<div className={`is-alert${(activeAlerts?.total ?? 0) > 0 ? ' has-alerts' : ''}`}><small>当前告警</small><strong>{formatNumber(activeAlerts?.total ?? 0)}<em>条</em></strong></div>
|
||||
</div>
|
||||
<WorkspaceMetricRail
|
||||
variant="queue"
|
||||
className="v2-monitor-detail-metric-rail"
|
||||
ariaLabel="车辆实时状态指标"
|
||||
items={metricItems}
|
||||
context={<div className="v2-monitor-detail-state">
|
||||
<span>
|
||||
<small>车辆状态</small>
|
||||
<Tag color={status === 'driving' ? 'blue' : status === 'idle' || status === 'online' ? 'green' : status === 'alert' ? 'red' : 'grey'} type="light" size="small">{statusLabel(status)}</Tag>
|
||||
</span>
|
||||
<span className={alertCount > 0 ? 'has-alerts' : ''}>
|
||||
<small>当前告警</small>
|
||||
<strong>{formatNumber(alertCount)}<em>条</em></strong>
|
||||
</span>
|
||||
</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>
|
||||
<section className="v2-detail-section">
|
||||
|
||||
Reference in New Issue
Block a user