diff --git a/vehicle-data-platform/apps/web/src/v2/pages/TrackPage.test.tsx b/vehicle-data-platform/apps/web/src/v2/pages/TrackPage.test.tsx
index 9f838f47..8fa77675 100644
--- a/vehicle-data-platform/apps/web/src/v2/pages/TrackPage.test.tsx
+++ b/vehicle-data-platform/apps/web/src/v2/pages/TrackPage.test.tsx
@@ -212,6 +212,7 @@ test('renders a map-first replay workspace and connects stop, event, and panel i
expect(screen.getByRole('slider', { name: '轨迹播放进度' })).toBeInTheDocument();
expect(view.container.querySelector('.v2-track-dock-summary')).not.toBeInTheDocument();
expect(view.container.querySelector('.v2-track-progress-meta')).toHaveTextContent('已暂停 · 数据点 1 / 3');
+ expect(screen.getByRole('button', { name: '开始轨迹播放' })).toHaveTextContent('播放');
expect(screen.queryByRole('button', { name: '暂停轨迹播放' })).not.toBeInTheDocument();
await waitFor(() => expect(mocks.reverseGeocode).toHaveBeenCalledTimes(1));
expect(client.getQueryCache().findAll({ queryKey: ['track-address'] })).toHaveLength(1);
@@ -239,6 +240,7 @@ test('renders a map-first replay workspace and connects stop, event, and panel i
fireEvent.change(screen.getByRole('combobox', { name: '速度' }), { target: { value: '4' } });
fireEvent.click(screen.getByRole('button', { name: '开始轨迹播放' }));
expect(view.container.querySelector('.v2-track-progress-meta')).toHaveTextContent(/播放中 · 数据点 [12] \/ 3/);
+ expect(screen.getByRole('button', { name: '暂停轨迹播放' })).toHaveTextContent('暂停');
expect(screen.getByTestId('track-map')).toHaveAttribute('data-follow-duration', '65');
fireEvent.click(screen.getByRole('button', { name: '暂停轨迹播放' }));
expect(screen.getByTestId('track-map')).toHaveAttribute('data-follow-duration', '180');
diff --git a/vehicle-data-platform/apps/web/src/v2/pages/TrackPage.tsx b/vehicle-data-platform/apps/web/src/v2/pages/TrackPage.tsx
index 7d371a41..51dfca86 100644
--- a/vehicle-data-platform/apps/web/src/v2/pages/TrackPage.tsx
+++ b/vehicle-data-platform/apps/web/src/v2/pages/TrackPage.tsx
@@ -545,11 +545,13 @@ export default function TrackPage() {
{track ?
:
}
selectIndex(Number(value))} disabled={!points.length} showBoundary={false} tipFormatter={(value) => `第 ${Number(value) + 1} / ${points.length} 个数据点`} />
- {playing ? '播放中' : '已暂停'} · 数据点 {points.length ? boundedIndex + 1 : 0} / {points.length}
+ {playing ? '播放中' : '已暂停'} · 数据点 {points.length ? boundedIndex + 1 : 0} / {points.length}
} onClick={() => selectIndex(boundedIndex - 1)} disabled={!boundedIndex} />
- : } onClick={togglePlayback} disabled={points.length < 2} />
+ : } onClick={togglePlayback} disabled={points.length < 2}>
+ {playing ? '暂停' : '播放'}
+
} onClick={() => selectIndex(boundedIndex + 1)} disabled={!points.length || boundedIndex >= points.length - 1} />
} onClick={() => selectIndex(0)} disabled={!boundedIndex} />
diff --git a/vehicle-data-platform/apps/web/src/v2/styles/workspace.css b/vehicle-data-platform/apps/web/src/v2/styles/workspace.css
index 1598d08a..01561b60 100644
--- a/vehicle-data-platform/apps/web/src/v2/styles/workspace.css
+++ b/vehicle-data-platform/apps/web/src/v2/styles/workspace.css
@@ -28053,3 +28053,211 @@
min-height: 320px;
}
}
+
+/*
+ * Semi UI trajectory evidence polish.
+ * Preserve the dense map-first workspace while raising the active vehicle,
+ * playback state and evidence labels above the microcopy layer. The transport
+ * control now has one explicit primary action instead of relying on an icon.
+ */
+.v2-track-query-header.has-identity .v2-workspace-command-eyebrow {
+ font-size: 9px;
+ line-height: 11px;
+}
+
+.v2-track-query-header.has-identity .v2-workspace-command-title {
+ font-size: 16px;
+ line-height: 20px;
+}
+
+.v2-track-query-header.has-identity .v2-workspace-command-description {
+ font-size: 10px;
+ line-height: 13px;
+}
+
+.v2-track-result-metric-rail .v2-workspace-metric-list > span > small {
+ font-size: 9px;
+ line-height: 13px;
+}
+
+.v2-track-result-metric-rail .v2-workspace-metric-list > span > strong,
+.v2-track-result-metric-rail .v2-workspace-metric-list > span.is-secondary > strong {
+ font-weight: 760;
+}
+
+.v2-track-list-action.semi-button {
+ min-height: 56px;
+ border-radius: 9px;
+}
+
+.v2-track-list-action .semi-button-content > span > strong {
+ font-size: 12px;
+ line-height: 17px;
+}
+
+.v2-track-list-action .semi-button-content > span > small,
+.v2-track-list-action .semi-button-content > em {
+ font-size: 9px;
+ line-height: 14px;
+}
+
+.v2-track-current-card .v2-workspace-panel-copy > h5.semi-typography {
+ font-size: 15px;
+ line-height: 20px;
+}
+
+.v2-track-current-card .v2-workspace-panel-copy > .semi-typography {
+ font-size: 10px;
+ line-height: 14px;
+}
+
+.v2-track-current-card > .semi-card-body > .v2-track-current-metrics small {
+ font-size: 11px;
+ line-height: 15px;
+}
+
+.v2-track-current-card > .semi-card-body > .v2-track-current-metrics strong {
+ font-weight: 740;
+}
+
+.v2-track-current-evidence,
+.v2-track-current-evidence .semi-tag,
+.v2-track-current-evidence strong {
+ font-size: 10px;
+}
+
+.v2-track-progress-meta {
+ min-height: 18px;
+}
+
+.v2-track-progress-meta > time {
+ color: #718197;
+ font-size: 10px;
+ font-variant-numeric: tabular-nums;
+}
+
+.v2-track-progress-status {
+ display: inline-flex;
+ min-height: 22px;
+ align-items: center;
+ border: 1px solid #e0e8f2;
+ border-radius: 999px;
+ background: #f7faff;
+ padding: 2px 9px;
+ color: #455d78;
+ font-size: 10px;
+ font-weight: 700;
+ line-height: 16px;
+}
+
+.v2-track-dock-controls > .semi-button.is-primary {
+ width: auto;
+ min-width: 76px;
+ gap: 5px;
+ padding-inline: 13px;
+}
+
+.v2-track-playback-primary-label {
+ font-size: 11px;
+ font-weight: 750;
+ line-height: 16px;
+}
+
+@media (max-width: 700px) {
+ .v2-track-detail-sidesheet .v2-workspace-config-title strong {
+ font-size: 16px;
+ line-height: 21px;
+ }
+
+ .v2-track-detail-sidesheet .v2-workspace-config-title small {
+ font-size: 10px;
+ line-height: 14px;
+ }
+
+ .v2-track-detail-sidesheet .v2-workspace-config-summary > span {
+ min-height: 68px;
+ padding: 8px 9px;
+ }
+
+ .v2-track-detail-sidesheet .v2-workspace-config-summary small {
+ font-size: 9px;
+ line-height: 13px;
+ }
+
+ .v2-track-detail-sidesheet .v2-workspace-config-summary strong {
+ font-size: 14px;
+ line-height: 18px;
+ }
+
+ .v2-track-detail-sidesheet .v2-workspace-config-summary em {
+ font-size: 8px;
+ line-height: 11px;
+ }
+
+ .v2-track-detail-sidesheet .v2-track-rail-tabs > .semi-button {
+ font-size: 12px;
+ font-weight: 680;
+ }
+
+ .v2-track-detail-sidesheet .v2-track-list-action.semi-button {
+ min-height: 62px;
+ padding: 8px 10px;
+ }
+
+ .v2-track-detail-sidesheet .v2-track-list-action .semi-button-content > span > strong {
+ font-size: 13px;
+ line-height: 18px;
+ }
+
+ .v2-track-detail-sidesheet .v2-track-list-action .semi-button-content > span > small,
+ .v2-track-detail-sidesheet .v2-track-list-action .semi-button-content > em {
+ font-size: 10px;
+ line-height: 15px;
+ }
+
+ .v2-track-current-card > .semi-card-body > .v2-track-current-metrics small {
+ font-size: 9px;
+ }
+
+ .v2-track-current-card > .semi-card-body > .v2-track-current-metrics strong {
+ font-size: 14px;
+ }
+
+ .v2-track-playback-dock > .semi-card-body {
+ grid-template-columns: minmax(0, 1fr) auto;
+ }
+
+ .v2-track-progress-status {
+ max-width: 142px;
+ min-height: 20px;
+ overflow: hidden;
+ padding-inline: 7px;
+ font-size: 9px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+
+ .v2-track-dock-controls > .semi-button.is-primary {
+ min-width: 72px;
+ height: 44px;
+ padding-inline: 11px;
+ }
+
+ .v2-track-playback-primary-label {
+ font-size: 11px;
+ }
+}
+
+@media (max-width: 460px) {
+ .v2-track-dock-controls > .semi-button[aria-label="回到起点"] {
+ display: none;
+ }
+
+ .v2-track-dock-controls {
+ gap: 3px;
+ }
+
+ .v2-track-dock-controls > label {
+ min-width: 70px;
+ }
+}