feat(platform): link trajectory playback selection

This commit is contained in:
lingniu
2026-07-04 18:04:45 +08:00
parent 89e954cad9
commit 483d92719b
3 changed files with 69 additions and 4 deletions

View File

@@ -109,6 +109,10 @@ function timeValue(row?: HistoryLocationRow) {
return Number.isFinite(ms) ? ms : undefined;
}
function playbackPointId(row: HistoryLocationRow, index: number) {
return `${row.vin || 'unknown'}-${row.deviceTime || row.serverTime || row.lastSeen || index}-${index}`;
}
function formatDurationMinutes(value?: number, suffix = '') {
if (!isFiniteNumber(value)) return '-';
if (value < 1) return `${Math.round(value * 60)}${suffix}`;
@@ -444,9 +448,10 @@ export function History({
const playbackRows = validLocations.length > 0 ? validLocations : locationItems;
const currentPlaybackIndex = playbackRows.length === 0 ? -1 : Math.min(playbackIndex, playbackRows.length - 1);
const currentPlayback = currentPlaybackIndex >= 0 ? playbackRows[currentPlaybackIndex] : undefined;
const currentPlaybackPointId = currentPlayback ? playbackPointId(currentPlayback, currentPlaybackIndex) : undefined;
const playbackAtEnd = playbackRows.length > 0 && currentPlaybackIndex >= playbackRows.length - 1;
const playbackPoints: VehicleMapPoint[] = validLocations.map((row, index) => ({
id: `${row.vin}-${row.deviceTime || row.serverTime || index}`,
id: playbackPointId(row, index),
label: row.plate || row.vin || `${index + 1}`,
longitude: row.longitude,
latitude: row.latitude,
@@ -539,6 +544,17 @@ export function History({
setPlaybackPlaying(false);
setPlaybackIndex((current) => Math.min(Math.max(current + delta, 0), playbackRows.length - 1));
};
const selectPlaybackPoint = (nextIndex: number) => {
if (playbackRows.length === 0) return;
setPlaybackPlaying(false);
setPlaybackIndex(Math.min(Math.max(nextIndex, 0), playbackRows.length - 1));
};
const selectPlaybackMapPoint = (point: VehicleMapPoint) => {
const index = playbackRows.findIndex((row, rowIndex) => playbackPointId(row, rowIndex) === point.id);
if (index >= 0) {
selectPlaybackPoint(index);
}
};
const openPlaybackVehicle = () => {
if (!currentPlayback || !canOpenVehicle(currentPlayback.vin)) return;
onOpenVehicle(currentPlayback.vin, currentPlayback.protocol);
@@ -648,6 +664,8 @@ export function History({
<VehicleMap
points={playbackPoints}
mode="track"
selectedId={currentPlaybackPointId}
onPointSelect={selectPlaybackMapPoint}
fallbackLabel="高德地图未配置,显示轨迹坐标预览"
/>
</div>
@@ -663,7 +681,7 @@ export function History({
<div className="vp-monitor-metric-value">{item.value}</div>
</div>
))}
<div className="vp-playback-current">
<div className="vp-playback-current">
<div className="vp-map-service-queue-title"></div>
{currentPlayback ? (
<>
@@ -703,6 +721,16 @@ export function History({
<Button size="small" disabled={currentPlaybackIndex >= playbackRows.length - 1} onClick={() => movePlayback(1)}></Button>
<Button size="small" disabled={!canOpenVehicle(currentPlayback.vin)} onClick={openPlaybackVehicle}></Button>
</Space>
<input
className="vp-playback-range"
aria-label="轨迹回放进度"
type="range"
min={0}
max={Math.max(playbackRows.length - 1, 0)}
step={1}
value={currentPlaybackIndex}
onChange={(event) => selectPlaybackPoint(Number(event.currentTarget.value))}
/>
</>
) : (
<Typography.Text type="tertiary"></Typography.Text>
@@ -712,7 +740,13 @@ export function History({
</div>
<div className="vp-playback-timeline">
{(validLocations.length > 0 ? validLocations : locationItems).slice(0, 8).map((row, index) => (
<div key={`${row.deviceTime}-${index}`} className="vp-playback-step">
<button
key={`${row.deviceTime}-${index}`}
type="button"
className={`vp-playback-step ${index === currentPlaybackIndex ? 'vp-playback-step-active' : ''}`}
aria-label={`选择轨迹点 ${index + 1}`}
onClick={() => selectPlaybackPoint(index)}
>
<Tag color={index === 0 ? 'green' : index === Math.min((validLocations.length || locationItems.length), 8) - 1 ? 'orange' : 'blue'}>
{index === 0 ? '起点' : index === Math.min((validLocations.length || locationItems.length), 8) - 1 ? '终点' : `${index + 1}`}
</Tag>
@@ -720,7 +754,7 @@ export function History({
<Typography.Text type="secondary">
{formatNumber(row.speedKmh, ' km/h')} / {formatNumber(row.totalMileageKm, ' km')}
</Typography.Text>
</div>
</button>
))}
{locationItems.length === 0 ? (
<Typography.Text type="secondary"></Typography.Text>