feat(platform): integrate runtime amap vehicle maps

This commit is contained in:
lingniu
2026-07-04 10:52:11 +08:00
parent bb1b2f1c46
commit adb9d146d9
12 changed files with 402 additions and 51 deletions

View File

@@ -3,7 +3,9 @@ import { IconRefresh, IconSearch } from '@douyinfe/semi-icons';
import { useEffect, useMemo, useState } from 'react';
import { api, type RawFrameQuery } from '../api/client';
import type { HistoryLocationRow, Page, RawFrameRow } from '../api/types';
import { VehicleMap, type VehicleMapPoint } from '../components/VehicleMap';
import { PageHeader } from '../components/PageHeader';
import { isAMapConfigured } from '../config/appConfig';
type HistoryFilters = {
keyword?: string;
@@ -51,15 +53,6 @@ function hasValidCoordinate(row: HistoryLocationRow) {
return isFiniteNumber(row.longitude) && isFiniteNumber(row.latitude) && row.longitude !== 0 && row.latitude !== 0;
}
function mapPointStyle(row: HistoryLocationRow, index: number) {
if (!hasValidCoordinate(row)) {
return { left: `${18 + (index % 4) * 18}%`, top: `${28 + (index % 3) * 17}%` };
}
const left = Math.min(88, Math.max(10, ((row.longitude + 180) / 360) * 100));
const top = Math.min(82, Math.max(12, ((90 - row.latitude) / 180) * 100));
return { left: `${left}%`, top: `${top}%` };
}
function formatNumber(value?: number, suffix = '') {
if (!isFiniteNumber(value)) return '-';
return `${value.toLocaleString(undefined, { maximumFractionDigits: 1 })}${suffix}`;
@@ -197,7 +190,16 @@ export function History({
const lastLocation = locationItems[locationItems.length - 1];
const currentVehicleKeyword = filters.keyword?.trim() ?? '';
const currentProtocol = filters.protocol?.trim() ?? '';
const amapConfigured = isAMapConfigured();
const selectedFieldCount = splitFields(filters.fields).length;
const playbackPoints: VehicleMapPoint[] = validLocations.map((row, index) => ({
id: `${row.vin}-${row.deviceTime || row.serverTime || index}`,
label: row.plate || row.vin || `${index + 1}`,
longitude: row.longitude,
latitude: row.latitude,
online: true,
title: `${row.deviceTime || row.serverTime || '-'} ${row.speedKmh ?? '-'} km/h`
}));
const filterSummary = [
currentVehicleKeyword ? `车辆:${currentVehicleKeyword}` : '',
currentProtocol ? `数据来源:${currentProtocol}` : '',
@@ -270,18 +272,14 @@ export function History({
<Tag color="blue">{validLocations.length.toLocaleString()} </Tag>
<Tag color={currentProtocol ? 'blue' : 'green'}>{currentProtocol || '全部来源'}</Tag>
<Tag color="green">{formatNumber(mileageDelta, ' km')}</Tag>
<Tag color={amapConfigured ? 'green' : 'orange'}>{amapConfigured ? '高德地图配置就绪' : '高德地图待配置'}</Tag>
</Space>
</div>
<div className="vp-map vp-map-monitor">
{validLocations.slice(0, 120).map((row, index) => (
<span
key={`${row.vin}-${row.deviceTime}-${index}`}
className={`vp-map-dot ${index === 0 ? 'vp-map-dot-start' : index === validLocations.length - 1 ? 'vp-map-dot-end' : 'vp-map-dot-online'}`}
title={`${row.deviceTime} ${row.speedKmh ?? '-'} km/h`}
style={mapPointStyle(row, index)}
/>
))}
</div>
<VehicleMap
points={playbackPoints}
mode="track"
fallbackLabel="高德地图未配置,显示轨迹坐标预览"
/>
</div>
<div className="vp-playback-side">
{[
@@ -316,7 +314,7 @@ export function History({
<Space wrap style={{ marginTop: 12 }}>
<Tag color="grey">{firstLocation?.deviceTime || firstLocation?.serverTime || '-'}</Tag>
<Tag color="grey">{lastLocation?.deviceTime || lastLocation?.serverTime || '-'}</Tag>
<Tag color="grey"> JS API </Tag>
<Tag color="grey"> JS API </Tag>
</Space>
</Card>
<Card bordered style={{ marginTop: 16 }}>