perf(monitor): skip metadata-only map work
This commit is contained in:
@@ -248,6 +248,33 @@ test('skips unchanged refresh redraws and replaces only a moved vehicle label',
|
||||
expect((replacementLabels[0].options.text as { content: string }).content).toBe('粤A12345');
|
||||
});
|
||||
|
||||
test('does not traverse unchanged point arrays for an asOf-only refresh', async () => {
|
||||
window.__LINGNIU_APP_CONFIG__ = { amapWebJsKey: 'amap-web-key' };
|
||||
window.AMapLoader = { load: vi.fn(async () => amapMock()) };
|
||||
let pointReads = 0;
|
||||
const observedPoints = new Proxy(pointMap.points, {
|
||||
get(target, property, receiver) {
|
||||
if (typeof property === 'string' && /^\d+$/.test(property)) pointReads += 1;
|
||||
return Reflect.get(target, property, receiver);
|
||||
}
|
||||
});
|
||||
const observedMap = { ...pointMap, points: observedPoints };
|
||||
const view = render(<FleetMap vehicles={[]} monitorMap={observedMap} onSelect={() => undefined} />);
|
||||
await waitFor(() => expect(addLabels).toHaveBeenCalled());
|
||||
pointReads = 0;
|
||||
|
||||
await act(async () => {
|
||||
view.rerender(<FleetMap
|
||||
vehicles={[]}
|
||||
monitorMap={{ ...observedMap, asOf: '2026-07-14T01:00:15Z' }}
|
||||
onSelect={() => undefined}
|
||||
/>);
|
||||
await Promise.resolve();
|
||||
});
|
||||
|
||||
expect(pointReads).toBe(0);
|
||||
});
|
||||
|
||||
test('converts AMap GCJ-02 bounds back to WGS-84 before requesting monitor data', async () => {
|
||||
const [west, south] = wgs84ToGcj02(113, 22);
|
||||
const [east, north] = wgs84ToGcj02(114, 24);
|
||||
|
||||
@@ -90,11 +90,18 @@ type LabelMarkerEntry = {
|
||||
signature: string;
|
||||
};
|
||||
|
||||
function massPointSignature(monitorMap: MonitorMapResponse | undefined, points: VehicleRealtimeRow[]) {
|
||||
if (monitorMap) return [
|
||||
`mode:${monitorMap.mode}`,
|
||||
...monitorMap.clusters.map((cluster) => `c:${cluster.id}:${cluster.longitude}:${cluster.latitude}:${cluster.count}`),
|
||||
...monitorMap.points.map((point) => `p:${point.vin}:${point.longitude}:${point.latitude}:${point.status}:${point.plate || ''}`)
|
||||
const NO_VEHICLE_POINTS: VehicleRealtimeRow[] = [];
|
||||
|
||||
function massPointSignature(
|
||||
mode: MonitorMapResponse['mode'] | undefined,
|
||||
clusters: MonitorMapResponse['clusters'] | undefined,
|
||||
mapPoints: MonitorMapResponse['points'] | undefined,
|
||||
points: VehicleRealtimeRow[]
|
||||
) {
|
||||
if (mode) return [
|
||||
`mode:${mode}`,
|
||||
...(clusters ?? []).map((cluster) => `c:${cluster.id}:${cluster.longitude}:${cluster.latitude}:${cluster.count}`),
|
||||
...(mapPoints ?? []).map((point) => `p:${point.vin}:${point.longitude}:${point.latitude}:${point.status}:${point.plate || ''}`)
|
||||
].join('|');
|
||||
return ['vehicles', ...points.map((point) => `p:${point.vin}:${point.longitude}:${point.latitude}:${vehicleStatus(point)}:${point.plate || ''}`)].join('|');
|
||||
}
|
||||
@@ -166,11 +173,15 @@ export function FleetMap({ vehicles, selectedVin, onSelect, monitorMap, onSelect
|
||||
const [followSelected, setFollowSelected] = useState(true);
|
||||
const [mapZoom, setMapZoom] = useState(5);
|
||||
const points = useMemo(() => vehicles.filter((vehicle) => isValidAMapCoordinate(vehicle.longitude, vehicle.latitude)), [vehicles]);
|
||||
const monitorMode = monitorMap?.mode;
|
||||
const monitorPoints = monitorMap?.points;
|
||||
const monitorClusters = monitorMap?.clusters;
|
||||
const fallbackPoints = monitorMode ? NO_VEHICLE_POINTS : points;
|
||||
const selectedTarget = useMemo(() => selectedVin
|
||||
? monitorMap?.points.find((item) => item.vin === selectedVin) ?? points.find((item) => item.vin === selectedVin)
|
||||
: undefined, [monitorMap, points, selectedVin]);
|
||||
const renderedPointCount = monitorMap ? monitorMap.points.length : points.length;
|
||||
const renderedClusterCount = monitorMap?.clusters.length ?? 0;
|
||||
? monitorPoints?.find((item) => item.vin === selectedVin) ?? points.find((item) => item.vin === selectedVin)
|
||||
: undefined, [monitorPoints, points, selectedVin]);
|
||||
const renderedPointCount = monitorPoints?.length ?? fallbackPoints.length;
|
||||
const renderedClusterCount = monitorClusters?.length ?? 0;
|
||||
const mapComposition = monitorMap && renderedClusterCount > 0
|
||||
? `${renderedClusterCount} 个聚合 · ${renderedPointCount} 个车辆点 · ${monitorMap.total} 辆`
|
||||
: `${renderedPointCount} 个有效点位`;
|
||||
@@ -309,8 +320,10 @@ export function FleetMap({ vehicles, selectedVin, onSelect, monitorMap, onSelect
|
||||
const mass = massRef.current;
|
||||
const AMap = amapRef.current;
|
||||
if (!mass || !AMap) return;
|
||||
clustersRef.current = new Map((monitorMap?.clusters ?? []).map((cluster) => [cluster.id, cluster]));
|
||||
const clusterCounts = [...new Set((monitorMap?.clusters ?? []).map((cluster) => cluster.count))].sort((a, b) => a - b);
|
||||
const clusters = monitorClusters ?? [];
|
||||
const mapPoints = monitorPoints ?? [];
|
||||
clustersRef.current = new Map(clusters.map((cluster) => [cluster.id, cluster]));
|
||||
const clusterCounts = [...new Set(clusters.map((cluster) => cluster.count))].sort((a, b) => a - b);
|
||||
const styleSignature = clusterCounts.join(',');
|
||||
if (massStyleSignatureRef.current !== styleSignature) {
|
||||
const baseStyles = COLORS.map((color) => ({ url: dotDataUrl(color), anchor: new AMap.Pixel(9, 9), size: new AMap.Size(18, 18) }));
|
||||
@@ -321,18 +334,18 @@ export function FleetMap({ vehicles, selectedVin, onSelect, monitorMap, onSelect
|
||||
mass.setStyle?.([...baseStyles, ...clusterStyles]);
|
||||
massStyleSignatureRef.current = styleSignature;
|
||||
}
|
||||
const dataSignature = massPointSignature(monitorMap, points);
|
||||
const dataSignature = massPointSignature(monitorMode, monitorClusters, monitorPoints, fallbackPoints);
|
||||
if (massDataSignatureRef.current === dataSignature) return;
|
||||
const clusterStyleIndexes = new Map(clusterCounts.map((count, index) => [count, COLORS.length + index]));
|
||||
const data: AMapMassPoint[] = monitorMap ? [
|
||||
...monitorMap.clusters.map((cluster) => ({ lnglat: wgs84ToGcj02(cluster.longitude, cluster.latitude), style: clusterStyleIndexes.get(cluster.count) ?? COLORS.length, id: cluster.id, label: `${cluster.count} 辆` })),
|
||||
...monitorMap.points.map((point) => ({ lnglat: wgs84ToGcj02(point.longitude, point.latitude), style: point.status === 'driving' ? 2 : point.status === 'idle' ? 0 : point.status === 'offline' ? 1 : 3, id: point.vin, label: point.plate || point.vin }))
|
||||
] : points.map((vehicle) => ({
|
||||
const data: AMapMassPoint[] = monitorMode ? [
|
||||
...clusters.map((cluster) => ({ lnglat: wgs84ToGcj02(cluster.longitude, cluster.latitude), style: clusterStyleIndexes.get(cluster.count) ?? COLORS.length, id: cluster.id, label: `${cluster.count} 辆` })),
|
||||
...mapPoints.map((point) => ({ lnglat: wgs84ToGcj02(point.longitude, point.latitude), style: point.status === 'driving' ? 2 : point.status === 'idle' ? 0 : point.status === 'offline' ? 1 : 3, id: point.vin, label: point.plate || point.vin }))
|
||||
] : fallbackPoints.map((vehicle) => ({
|
||||
lnglat: wgs84ToGcj02(vehicle.longitude, vehicle.latitude), style: styleIndex(vehicle), id: vehicle.vin, label: vehicle.plate || vehicle.vin
|
||||
}));
|
||||
mass.setData(data);
|
||||
massDataSignatureRef.current = dataSignature;
|
||||
}, [monitorMap, points, state]);
|
||||
}, [fallbackPoints, monitorClusters, monitorMode, monitorPoints, state]);
|
||||
|
||||
useEffect(() => {
|
||||
const labels = labelsRef.current;
|
||||
@@ -340,9 +353,9 @@ export function FleetMap({ vehicles, selectedVin, onSelect, monitorMap, onSelect
|
||||
const AMap = amapRef.current;
|
||||
const map = mapRef.current;
|
||||
if (!labels || !denseLabels || !AMap?.LabelMarker || !map) return;
|
||||
const mapLabelPoints = (monitorMap
|
||||
? monitorMap.points
|
||||
: points.map((vehicle) => ({ ...vehicle, status: vehicleStatus(vehicle) })));
|
||||
const mapLabelPoints = (monitorPoints
|
||||
? monitorPoints
|
||||
: fallbackPoints.map((vehicle) => ({ ...vehicle, status: vehicleStatus(vehicle) })));
|
||||
const allLabelPoints = selectedTarget && !mapLabelPoints.some((point) => point.vin === selectedTarget.vin)
|
||||
? [...mapLabelPoints, selectedTarget]
|
||||
: mapLabelPoints;
|
||||
@@ -407,7 +420,7 @@ export function FleetMap({ vehicles, selectedVin, onSelect, monitorMap, onSelect
|
||||
if (markersToRemove.length) activeLabels.remove(markersToRemove);
|
||||
if (markersToAdd.length) activeLabels.add(markersToAdd);
|
||||
labelMarkersRef.current = nextMarkers;
|
||||
}, [mapZoom, monitorMap, points, selectedTarget, selectedVin, showLabels, state]);
|
||||
}, [fallbackPoints, mapZoom, monitorPoints, selectedTarget, selectedVin, showLabels, state]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!selectedVin || !mapRef.current || !amapRef.current) {
|
||||
|
||||
Reference in New Issue
Block a user