feat(web): refine track and mileage workspaces

This commit is contained in:
lingniu
2026-07-20 05:12:12 +08:00
parent 9eb77a2d57
commit c150e45f11
6 changed files with 47 additions and 30 deletions

View File

@@ -11,7 +11,7 @@ import type { TrackPlaybackEvent, TrackPlaybackResponse, VehicleRow } from '../.
import { protocolDisplayLabel } from '../domain/protocols';
import { buildTrackRailSelection, compactTrackWindowLabel, downloadTrackCsv, formatDuration, sampledEventIndex, TRACK_ADDRESS_SETTLE_MS, trackAddressCoordinate, trackPlaybackInterval, trackPointMileageAvailable, type TrackAddressCoordinate } from '../domain/track';
import { TrackMap } from '../map/TrackMap';
import { InlineError, PanelEmpty, WorkspaceEmptyGuide } from '../shared/AsyncState';
import { InlineError, PanelEmpty, PanelLoading, WorkspaceEmptyGuide } from '../shared/AsyncState';
import { MonitorReturnBar } from '../shared/MonitorReturnBar';
import { ProtocolTag } from '../shared/ProtocolTag';
import { SegmentedTabs } from '../shared/SegmentedTabs';
@@ -210,7 +210,7 @@ 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">
<Card className="v2-track-overview-section v2-track-overview-card" bodyStyle={{ padding: 0 }}>
<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) },
@@ -220,15 +220,15 @@ function OverviewPanel({ track }: { track: TrackPlaybackResponse }) {
{ key: '平均 / 最高速度', value: `${number(track.summary.averageSpeedKmh)} / ${number(track.summary.maximumSpeedKmh)} km/h` },
{ key: '停车 / 分段', value: `${track.summary.stopCount} / ${track.summary.segmentCount}` }
]} /></div>
</section>
<section className="v2-track-overview-section v2-track-source-card">
</Card>
<Card className="v2-track-overview-section v2-track-overview-card v2-track-source-card" bodyStyle={{ padding: 0 }}>
<WorkspacePanelHeader variant="compact" title="数据来源" meta={<Tag color="blue" type="light" size="small">{track.coverage.totalPoints.toLocaleString('zh-CN')} </Tag>} />
<div className="v2-track-overview-card-body"><List className="v2-track-source-list">{track.sources.map((source) => <List.Item className="v2-track-source-item" key={source.protocol}><ProtocolTag className="v2-track-protocol-tag" protocol={source.protocol} compact /><strong>{source.pointCount.toLocaleString('zh-CN')} </strong><small>{time(source.startTime)}{time(source.endTime)}</small></List.Item>)}</List></div>
</section>
<section className={`v2-track-overview-section v2-track-quality-card is-${track.quality.status}`}>
</Card>
<Card className={`v2-track-overview-section v2-track-overview-card v2-track-quality-card is-${track.quality.status}`} bodyStyle={{ padding: 0 }}>
<WorkspacePanelHeader variant="compact" title="轨迹质量" meta={<Tag color={track.quality.status === 'good' ? 'green' : 'orange'} type="light" size="small">{track.quality.status === 'good' ? '通过' : '需关注'}</Tag>} />
<div className="v2-track-overview-card-body"><p>{track.quality.evidence}</p><small> {track.quality.invalidCoordinatePoints} · {track.quality.duplicatePoints} · {track.quality.driftPoints} · {track.quality.largeGapCount}</small></div>
</section>
</Card>
</div>;
}
@@ -538,7 +538,7 @@ export default function TrackPage() {
action={<Button theme="solid" icon={<IconSearch />} onClick={() => setRailCollapsed(false)}></Button>}
/></Card>}
{query.isFetching ? <div className="v2-track-loading"><span className="v2-spinner" /></div> : null}
{query.isFetching ? <PanelLoading className="v2-track-loading" compact title="正在生成轨迹" description="正在校验来源覆盖并整理停留与事件证据。" /> : null}
{query.isError ? <div className="v2-track-error"><InlineError message={query.error instanceof Error ? query.error.message : '轨迹查询失败'} onRetry={() => query.refetch()} /></div> : null}
<Card className="v2-track-playback-dock" bodyStyle={{ padding: 0 }}>