feat(monitor): aggregate nationwide fleet by province

This commit is contained in:
lingniu
2026-07-16 18:36:31 +08:00
parent ea4d576793
commit 21d1baada5
9 changed files with 598 additions and 32 deletions

View File

@@ -22,8 +22,8 @@ export type AMapMap = {
export type AMapOverlay = {
setMap?: (map: AMapMap | null) => void;
on?: (eventName: string, handler: () => void) => void;
off?: (eventName: string, handler: () => void) => void;
on?: (eventName: string, handler: (event?: unknown) => void) => void;
off?: (eventName: string, handler: (event?: unknown) => void) => void;
setPosition?: (position: [number, number]) => void;
setPath?: (path: [number, number][]) => void;
};
@@ -77,6 +77,32 @@ export type AMapAddress = {
adcode?: string;
};
export type AMapDistrictFeature = {
properties: {
adcode: number;
name: string;
center?: [number, number];
};
};
export type AMapDistrictGroup<T> = {
subFeatureIndex: number;
subFeature?: AMapDistrictFeature | null;
pointsIndexes: number[];
points: T[];
};
export type AMapAreaNode = {
getSubFeatures: () => AMapDistrictFeature[];
groupByPosition: <T>(points: T[], getPosition: (point: T, index: number) => [number, number]) => AMapDistrictGroup<T>[];
};
export type AMapDistrictExplorer = {
loadAreaNode: (adcode: number, callback: (error: Error | null, areaNode?: AMapAreaNode) => void) => void;
};
export type AMapDistrictExplorerConstructor = new (options?: Record<string, unknown>) => AMapDistrictExplorer;
export type AMapLike = {
Map: new (container: HTMLDivElement, options: Record<string, unknown>) => AMapMap;
Marker: new (options: Record<string, unknown>) => AMapOverlay;
@@ -135,6 +161,43 @@ function loadScript(src: string) {
});
}
function loadAMapUIScript() {
const src = 'https://webapi.amap.com/ui/1.1/main.js?v=1.1.1';
return new Promise<void>((resolve, reject) => {
if (window.AMapUI) {
resolve();
return;
}
const existing = document.querySelector<HTMLScriptElement>(`script[src="${src}"]`);
if (existing) {
if (existing.dataset.loadState === 'loaded') {
existing.remove();
loadAMapUIScript().then(resolve, reject);
return;
}
existing.addEventListener('load', () => resolve(), { once: true });
existing.addEventListener('error', () => {
existing.remove();
reject(new Error('高德行政区组件加载失败'));
}, { once: true });
return;
}
const script = document.createElement('script');
script.src = src;
script.async = true;
script.dataset.loadState = 'loading';
script.onload = () => {
script.dataset.loadState = 'loaded';
resolve();
};
script.onerror = () => {
script.remove();
reject(new Error('高德行政区组件加载失败'));
};
document.head.appendChild(script);
});
}
function pluginKey(plugins: AMapPlugin[]) {
return plugins.slice().sort().join('|') || 'base';
}
@@ -257,6 +320,24 @@ export function loadAMap(plugins: AMapPlugin[] = ['AMap.Scale']) {
return promise;
}
export async function loadAMapDistrictExplorer(): Promise<AMapDistrictExplorerConstructor> {
await loadAMapUIScript();
if (!window.AMapUI) {
throw new Error('高德行政区组件不可用');
}
return new Promise((resolve, reject) => {
const timeout = window.setTimeout(() => reject(new Error('高德行政区组件加载超时')), 10_000);
window.AMapUI!.loadUI(['geo/DistrictExplorer'], (constructor) => {
window.clearTimeout(timeout);
if (typeof constructor !== 'function') {
reject(new Error('高德行政区组件返回异常'));
return;
}
resolve(constructor as AMapDistrictExplorerConstructor);
});
});
}
export async function reverseGeocodeWithAMap(longitude: number, latitude: number): Promise<AMapAddress> {
if (!isValidAMapCoordinate(longitude, latitude)) {
throw new Error('坐标不可用');