fix: distinguish track mileage evidence
This commit is contained in:
@@ -50,9 +50,9 @@ vi.mock('@douyinfe/semi-ui', async (importOriginal) => {
|
||||
const track = {
|
||||
vin: 'LTEST000000000001', plate: '粤A12345', total: 3, truncated: false, sampled: false, asOf: '2026-07-15T08:00:00Z',
|
||||
points: [
|
||||
{ vin: 'LTEST000000000001', plate: '粤A12345', protocol: 'JT808', deviceTime: '2026-07-15T00:00:00Z', serverTime: '2026-07-15T00:00:01Z', longitude: 113.1, latitude: 23.1, speedKmh: 0, totalMileageKm: 100, socPercent: 80, socAvailable: true },
|
||||
{ vin: 'LTEST000000000001', plate: '粤A12345', protocol: 'JT808', deviceTime: '2026-07-15T00:10:00Z', serverTime: '2026-07-15T00:10:01Z', longitude: 113.2, latitude: 23.2, speedKmh: 35, totalMileageKm: 104, socPercent: 79, socAvailable: true },
|
||||
{ vin: 'LTEST000000000001', plate: '粤A12345', protocol: 'JT808', deviceTime: '2026-07-15T00:20:00Z', serverTime: '2026-07-15T00:20:01Z', longitude: 113.3, latitude: 23.3, speedKmh: 10, totalMileageKm: 108, socPercent: 78, socAvailable: true }
|
||||
{ vin: 'LTEST000000000001', plate: '粤A12345', protocol: 'JT808', deviceTime: '2026-07-15T00:00:00Z', serverTime: '2026-07-15T00:00:01Z', longitude: 113.1, latitude: 23.1, speedKmh: 0, totalMileageKm: 100, mileageAvailable: true, socPercent: 80, socAvailable: true },
|
||||
{ vin: 'LTEST000000000001', plate: '粤A12345', protocol: 'JT808', deviceTime: '2026-07-15T00:10:00Z', serverTime: '2026-07-15T00:10:01Z', longitude: 113.2, latitude: 23.2, speedKmh: 35, totalMileageKm: 104, mileageAvailable: true, socPercent: 79, socAvailable: true },
|
||||
{ vin: 'LTEST000000000001', plate: '粤A12345', protocol: 'JT808', deviceTime: '2026-07-15T00:20:00Z', serverTime: '2026-07-15T00:20:01Z', longitude: 113.3, latitude: 23.3, speedKmh: 10, totalMileageKm: 108, mileageAvailable: true, socPercent: 78, socAvailable: true }
|
||||
],
|
||||
events: [
|
||||
{ index: 0, sampledIndex: 0, type: 'start', title: '开始行驶', time: '2026-07-15T00:00:00Z', speedKmh: 0, longitude: 113.1, latitude: 23.1 },
|
||||
@@ -62,7 +62,7 @@ const track = {
|
||||
sources: [{ protocol: 'JT808', pointCount: 3, startTime: '2026-07-15T00:00:00Z', endTime: '2026-07-15T00:20:00Z' }],
|
||||
segments: [{ index: 0, type: 'moving', title: '行驶', startTime: '2026-07-15T00:00:00Z', endTime: '2026-07-15T00:20:00Z', durationSeconds: 1200, distanceKm: 8, pointCount: 3, startIndex: 0, endIndex: 2, sampledStartIndex: 0, sampledEndIndex: 2 }],
|
||||
stops: [{ index: 0, startTime: '2026-07-15T00:00:00Z', endTime: '2026-07-15T00:05:00Z', durationSeconds: 300, pointCount: 1, longitude: 113.1, latitude: 23.1, sampledIndex: 0, evidence: 'GPS 推断' }],
|
||||
summary: { startTime: '2026-07-15T00:00:00Z', endTime: '2026-07-15T00:20:00Z', distanceKm: 8, durationSeconds: 1200, averageSpeedKmh: 24, maximumSpeedKmh: 35, pointCount: 3, movingSeconds: 900, stoppedSeconds: 300, stopCount: 1, segmentCount: 1 },
|
||||
summary: { startTime: '2026-07-15T00:00:00Z', endTime: '2026-07-15T00:20:00Z', distanceKm: 8, distanceMethod: 'odometer', durationSeconds: 1200, averageSpeedKmh: 24, maximumSpeedKmh: 35, pointCount: 3, movingSeconds: 900, stoppedSeconds: 300, stopCount: 1, segmentCount: 1 },
|
||||
coverage: { requestedStart: '', requestedEnd: '', actualStart: '', actualEnd: '', totalPoints: 3, fetchedPoints: 3, processedPoints: 3, returnedPoints: 3, complete: true, limitReasons: [], evidence: '时间窗完整' },
|
||||
quality: { status: 'good', selectedProtocol: 'JT808', rawPoints: 3, validPoints: 3, alternateSourcePoints: 0, invalidCoordinatePoints: 0, duplicatePoints: 0, driftPoints: 0, sourceSwitches: 0, largeGapCount: 0, maximumGapSeconds: 0, evidence: '轨迹质量正常' }
|
||||
} as unknown as TrackPlaybackResponse;
|
||||
@@ -181,8 +181,8 @@ test('renders a map-first replay workspace and connects stop, event, and panel i
|
||||
expect(view.container.querySelector('.v2-track-rail-result')).toBeInTheDocument();
|
||||
const tripSummary = screen.getByRole('list', { name: '轨迹行程摘要' });
|
||||
expect(tripSummary.closest('.v2-track-result-metric-rail')).toHaveClass('semi-card', 'v2-workspace-metric-rail', 'is-queue');
|
||||
expect(Array.from(tripSummary.querySelectorAll(':scope > [role="listitem"] > small')).map((item) => item.textContent)).toEqual(['行程里程', '停留点', '事件点', '数据点']);
|
||||
expect(tripSummary).toHaveTextContent('行程里程8 km00:20:00');
|
||||
expect(Array.from(tripSummary.querySelectorAll(':scope > [role="listitem"] > small')).map((item) => item.textContent)).toEqual(['协议总里程差', '停留点', '事件点', '数据点']);
|
||||
expect(tripSummary).toHaveTextContent('协议总里程差8 km00:20:00');
|
||||
expect(tripSummary).toHaveTextContent('停留点1超过 3 分钟');
|
||||
expect(tripSummary).toHaveTextContent('事件点3轨迹事件');
|
||||
expect(tripSummary).toHaveTextContent('数据点3时间窗完整');
|
||||
@@ -285,7 +285,7 @@ test('uses the shared Semi editor SideSheet for mobile track criteria and eviden
|
||||
expect(screen.queryByRole('textbox', { name: '搜索轨迹车辆' })).not.toBeInTheDocument();
|
||||
expect(screen.getAllByText('07-15 00:00 – 07-15 23:59 · 自动来源')).toHaveLength(2);
|
||||
const mobileSummary = screen.getByRole('list', { name: '轨迹查询与明细摘要' });
|
||||
expect(mobileSummary).toHaveTextContent('行程里程8 km00:20:00');
|
||||
expect(mobileSummary).toHaveTextContent('协议总里程差8 km00:20:00');
|
||||
expect(mobileSummary).toHaveTextContent('停留点1超过 3 分钟');
|
||||
expect(mobileSummary).toHaveTextContent('事件点3轨迹事件');
|
||||
expect(mobileSummary).toHaveTextContent('数据点3时间窗完整');
|
||||
|
||||
@@ -9,7 +9,7 @@ import { useSearchParams } from 'react-router-dom';
|
||||
import { api } from '../../api/client';
|
||||
import type { TrackPlaybackEvent, TrackPlaybackResponse, VehicleRow } from '../../api/types';
|
||||
import { protocolDisplayLabel } from '../domain/protocols';
|
||||
import { buildTrackRailSelection, downloadTrackCsv, formatDuration, sampledEventIndex, TRACK_ADDRESS_SETTLE_MS, trackAddressCoordinate, trackPlaybackInterval, type TrackAddressCoordinate } from '../domain/track';
|
||||
import { buildTrackRailSelection, downloadTrackCsv, formatDuration, sampledEventIndex, TRACK_ADDRESS_SETTLE_MS, trackAddressCoordinate, trackPlaybackInterval, trackPointMileageAvailable, type TrackAddressCoordinate } from '../domain/track';
|
||||
import { TrackMap } from '../map/TrackMap';
|
||||
import { InlineError } from '../shared/AsyncState';
|
||||
import { MonitorReturnBar } from '../shared/MonitorReturnBar';
|
||||
@@ -129,9 +129,10 @@ function mobileTrackSheetHeight(track: TrackPlaybackResponse | undefined, queryC
|
||||
}
|
||||
|
||||
function trackSummaryItems(track?: TrackPlaybackResponse): WorkspaceQueueMetricRailItem[] {
|
||||
const distanceLabel = track?.summary.distanceMethod === 'coordinates' ? 'GPS 轨迹累计' : track?.summary.distanceMethod === 'odometer' ? '协议总里程差' : '行程里程';
|
||||
return [
|
||||
{
|
||||
label: '行程里程',
|
||||
label: distanceLabel,
|
||||
value: track ? `${number(track.summary.distanceKm)} km` : '—',
|
||||
note: track ? formatDuration(track.summary.durationSeconds) : '等待查询',
|
||||
tone: 'primary',
|
||||
@@ -206,13 +207,14 @@ function VehiclePicker({ value, onChange, onSelect }: { value: string; onChange:
|
||||
}
|
||||
|
||||
function OverviewPanel({ track }: { track: TrackPlaybackResponse }) {
|
||||
const distanceLabel = track.summary.distanceMethod === 'coordinates' ? 'GPS 轨迹累计' : track.summary.distanceMethod === 'odometer' ? '协议总里程差' : '行驶里程';
|
||||
return <div className="v2-track-overview-panel">
|
||||
<section className="v2-track-overview-section">
|
||||
<WorkspacePanelHeader variant="compact" title="行程概览" meta={<Tag color={track.sampled ? 'orange' : 'green'} type="light" size="small">{track.sampled ? '地图已抽稀' : '完整点集'}</Tag>} />
|
||||
<div className="v2-track-overview-card-body"><Descriptions className="v2-track-overview-descriptions" align="left" size="small" data={[
|
||||
{ key: '开始时间', value: dateTime(track.summary.startTime) },
|
||||
{ key: '结束时间', value: dateTime(track.summary.endTime) },
|
||||
{ key: '行驶里程', value: `${number(track.summary.distanceKm)} km` },
|
||||
{ key: distanceLabel, value: `${number(track.summary.distanceKm)} km` },
|
||||
{ key: '行驶 / 停车', value: `${formatDuration(track.summary.movingSeconds)} / ${formatDuration(track.summary.stoppedSeconds)}` },
|
||||
{ key: '平均 / 最高速度', value: `${number(track.summary.averageSpeedKmh)} / ${number(track.summary.maximumSpeedKmh)} km/h` },
|
||||
{ key: '停车 / 分段', value: `${track.summary.stopCount} / ${track.summary.segmentCount}` }
|
||||
@@ -501,7 +503,7 @@ export default function TrackPage() {
|
||||
<span><small>速度</small><strong>{number(current?.speedKmh ?? 0)}<em> km/h</em></strong></span>
|
||||
<span><small>方向</small><strong>{direction(current?.directionDeg)}</strong></span>
|
||||
<span><small>SOC</small><strong>{current?.socAvailable ? `${number(current.socPercent)}%` : '—'}</strong></span>
|
||||
<span><small>总里程</small><strong>{number(current?.totalMileageKm ?? 0)}<em> km</em></strong></span>
|
||||
<span><small>节点累计总里程</small><strong>{trackPointMileageAvailable(current) ? number(current?.totalMileageKm ?? 0) : '—'}{trackPointMileageAvailable(current) ? <em> km</em> : null}</strong></span>
|
||||
</div>
|
||||
<div className={`v2-track-current-evidence v2-track-current-coverage${track.coverage.complete ? '' : ' is-warning'}`}>
|
||||
<ProtocolTag className="v2-track-protocol-tag" protocol={current?.protocol} compact />
|
||||
|
||||
Reference in New Issue
Block a user