2026-06-03 15:03:31 +08:00
|
|
|
|
import { defineStore, storeToRefs } from 'pinia';
|
2026-04-03 16:04:16 +08:00
|
|
|
|
import { ref } from 'vue';
|
2026-06-26 18:00:09 +08:00
|
|
|
|
import dayjs from 'dayjs';
|
2026-06-01 08:42:06 +08:00
|
|
|
|
import { MapClass } from '@/components/gis/map.class';
|
2026-07-01 08:48:39 +08:00
|
|
|
|
import { getMapConfig } from '@/components/gis/gisUtils';
|
2026-06-03 15:03:31 +08:00
|
|
|
|
import { applyLayerMutualExclusionRules } from '@/modules/map/domain/map-layer-rules';
|
|
|
|
|
|
import {
|
|
|
|
|
|
applyEnvFacilityLegendRule,
|
|
|
|
|
|
buildLegendCheckedState,
|
|
|
|
|
|
buildLegendTree
|
|
|
|
|
|
} from '@/modules/map/domain/legend-deriver';
|
2026-07-01 08:48:39 +08:00
|
|
|
|
import { attachNearbyPointRuntimeMeta } from '@/modules/map/domain/nearby-point-rules';
|
2026-06-03 15:03:31 +08:00
|
|
|
|
import { useMapConfigStore } from '@/modules/map/stores/map-config.store';
|
|
|
|
|
|
import { useMapDataStore } from '@/modules/map/stores/map-data.store';
|
|
|
|
|
|
import { useMapViewStore } from '@/modules/map/stores/map-view.store';
|
2026-06-01 08:42:06 +08:00
|
|
|
|
import request from '@/utils/request';
|
|
|
|
|
|
const mapClass = MapClass.getInstance();
|
2026-07-01 08:48:39 +08:00
|
|
|
|
const ENG_POINT_LAYER_KEY = 'eng_point';
|
2026-07-08 08:33:08 +08:00
|
|
|
|
const YLFB_POINT_LAYER_KEY = 'ylfb_point';
|
2026-07-01 08:48:39 +08:00
|
|
|
|
const ENG_POINT_MEDIUM_VISIBLE_ZOOM = 7.5;
|
|
|
|
|
|
const ENG_POINT_LARGE_STATES = [
|
|
|
|
|
|
'large_eng_built',
|
|
|
|
|
|
'large_eng_ubuilt',
|
|
|
|
|
|
'large_eng_nbuilt'
|
|
|
|
|
|
];
|
|
|
|
|
|
const ENG_POINT_MEDIUM_STATES = [
|
|
|
|
|
|
'mid_eng_built',
|
|
|
|
|
|
'mid_eng_ubuilt',
|
|
|
|
|
|
'mid_eng_nbuilt'
|
|
|
|
|
|
];
|
2026-06-03 15:03:31 +08:00
|
|
|
|
|
|
|
|
|
|
const normalizeCachePayload = (value: any): any => {
|
|
|
|
|
|
if (Array.isArray(value)) {
|
|
|
|
|
|
return value.map(item => normalizeCachePayload(item));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (value && typeof value === 'object') {
|
|
|
|
|
|
return Object.keys(value)
|
|
|
|
|
|
.sort()
|
|
|
|
|
|
.reduce<Record<string, any>>((acc, key) => {
|
|
|
|
|
|
acc[key] = normalizeCachePayload(value[key]);
|
|
|
|
|
|
return acc;
|
|
|
|
|
|
}, {});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return value;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const isCanceledRequestError = (error: unknown) => {
|
|
|
|
|
|
const message = error instanceof Error ? error.message : String(error || '');
|
|
|
|
|
|
return (
|
|
|
|
|
|
message.includes('canceled') ||
|
|
|
|
|
|
message.includes('aborted') ||
|
|
|
|
|
|
message.includes('AbortError')
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
2026-04-03 16:04:16 +08:00
|
|
|
|
|
2026-07-03 15:59:05 +08:00
|
|
|
|
const buildFiltersFromParamsObject = (
|
|
|
|
|
|
paramsObject: Record<string, any> = {}
|
|
|
|
|
|
) => {
|
|
|
|
|
|
const filtersArray: any[] = [];
|
|
|
|
|
|
|
|
|
|
|
|
Object.keys(paramsObject).forEach(paramKey => {
|
|
|
|
|
|
const value = paramsObject[paramKey];
|
|
|
|
|
|
|
|
|
|
|
|
if (Array.isArray(value)) {
|
|
|
|
|
|
value.forEach((filter: any) => {
|
|
|
|
|
|
filtersArray.push({
|
|
|
|
|
|
...filter,
|
|
|
|
|
|
dataType: filter?.dataType || 'string'
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
filtersArray.push({
|
|
|
|
|
|
field: paramKey,
|
|
|
|
|
|
operator: 'eq',
|
|
|
|
|
|
dataType: 'string',
|
|
|
|
|
|
value
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return filtersArray;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const normalizeRequestParams = (rawParams: any) => {
|
|
|
|
|
|
if (!rawParams || !Object.keys(rawParams).length) {
|
|
|
|
|
|
return { logic: 'and', filters: [] };
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (Array.isArray(rawParams.filters)) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
logic: rawParams.logic || 'and',
|
|
|
|
|
|
filters: rawParams.filters.map((filter: any) => ({
|
|
|
|
|
|
...filter,
|
|
|
|
|
|
dataType: filter?.dataType || 'string'
|
|
|
|
|
|
}))
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
logic: 'and',
|
|
|
|
|
|
filters: buildFiltersFromParamsObject(rawParams)
|
|
|
|
|
|
};
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const parseAnchorParamFilters = (anchorParamJson?: string) => {
|
|
|
|
|
|
if (!anchorParamJson) return [];
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
const parsedParams = JSON.parse(anchorParamJson);
|
|
|
|
|
|
if (!parsedParams || typeof parsedParams !== 'object') {
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return buildFiltersFromParamsObject(parsedParams);
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('解析 anchorParamJson 失败:', error);
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-04-03 16:04:16 +08:00
|
|
|
|
export const useMapStore = defineStore('map', () => {
|
2026-06-03 15:03:31 +08:00
|
|
|
|
const mapConfigStore = useMapConfigStore();
|
|
|
|
|
|
const mapDataStore = useMapDataStore();
|
|
|
|
|
|
const mapViewStore = useMapViewStore();
|
|
|
|
|
|
const {
|
|
|
|
|
|
getLayerConfigByKey,
|
|
|
|
|
|
getLegendConfigByLayerCode,
|
|
|
|
|
|
getLegendConfigByNameEn
|
|
|
|
|
|
} = mapConfigStore;
|
2026-07-09 11:33:04 +08:00
|
|
|
|
const { hasPointLayerData, getPointLayerData } = mapDataStore;
|
2026-06-03 15:03:31 +08:00
|
|
|
|
const {
|
|
|
|
|
|
layerConfigTree: layerData,
|
|
|
|
|
|
legendConfigOriginal: legendDataOriginal,
|
|
|
|
|
|
pageLegendConfig
|
|
|
|
|
|
} = storeToRefs(mapConfigStore);
|
|
|
|
|
|
const { checkedLayerKeys, legendCheckedState, searchTimeRange } =
|
|
|
|
|
|
storeToRefs(mapViewStore);
|
|
|
|
|
|
const { pointData, pointDataCache, layerLoadState, loading } =
|
|
|
|
|
|
storeToRefs(mapDataStore);
|
|
|
|
|
|
|
2026-06-01 08:42:06 +08:00
|
|
|
|
// 图例数据(经过筛选和排序的)
|
|
|
|
|
|
const legendData = ref<any[]>([]);
|
|
|
|
|
|
// 选中的图例数据
|
|
|
|
|
|
const legendDataSelected = ref<any[]>([]);
|
2026-06-03 15:03:31 +08:00
|
|
|
|
const inFlightRequestMap = new Map<
|
|
|
|
|
|
string,
|
|
|
|
|
|
{
|
|
|
|
|
|
promise: Promise<any[]>;
|
|
|
|
|
|
controller: AbortController;
|
|
|
|
|
|
layerKey: string;
|
|
|
|
|
|
sessionId: number;
|
|
|
|
|
|
}
|
|
|
|
|
|
>();
|
|
|
|
|
|
const activeLayerRequestKeyMap = new Map<string, string>();
|
2026-07-09 11:33:04 +08:00
|
|
|
|
const backgroundPointLayerCache = new Map<
|
|
|
|
|
|
string,
|
|
|
|
|
|
{
|
|
|
|
|
|
checked: boolean;
|
|
|
|
|
|
data: any[];
|
|
|
|
|
|
cacheKey?: string;
|
|
|
|
|
|
}
|
|
|
|
|
|
>();
|
2026-06-03 15:03:31 +08:00
|
|
|
|
let loadSessionSeed = 0;
|
2026-07-08 08:33:08 +08:00
|
|
|
|
let activePageRenderToken = 0;
|
2026-07-09 11:33:04 +08:00
|
|
|
|
let activePageKey = '';
|
2026-07-08 08:33:08 +08:00
|
|
|
|
let activePageLayerKeys = new Set<string>();
|
2026-07-09 11:33:04 +08:00
|
|
|
|
let activeLoadingPageToken: number | null = null;
|
|
|
|
|
|
let pendingPageNavigationKey = '';
|
2026-06-03 15:03:31 +08:00
|
|
|
|
|
|
|
|
|
|
const normalizeLegendNameEn = mapConfigStore.normalizeLegendNameEn;
|
|
|
|
|
|
|
2026-07-01 08:48:39 +08:00
|
|
|
|
const filterPointLayerDataForDisplay = (
|
|
|
|
|
|
layerKey: string,
|
|
|
|
|
|
sourceData: any[] = [],
|
|
|
|
|
|
currentZoom: number = mapViewStore.currentZoomLevel
|
|
|
|
|
|
): any[] => {
|
|
|
|
|
|
if (layerKey !== ENG_POINT_LAYER_KEY || !Array.isArray(sourceData)) {
|
|
|
|
|
|
return sourceData;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const allowMedium = currentZoom > ENG_POINT_MEDIUM_VISIBLE_ZOOM;
|
|
|
|
|
|
return sourceData.filter((item: any) => {
|
|
|
|
|
|
const state = String(item?.anchoPointState || '');
|
|
|
|
|
|
if (ENG_POINT_LARGE_STATES.includes(state)) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (ENG_POINT_MEDIUM_STATES.includes(state)) {
|
|
|
|
|
|
return allowMedium;
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const refreshPointLayerDisplayData = (layerKey: string) => {
|
|
|
|
|
|
if (!layerKey) return;
|
|
|
|
|
|
|
|
|
|
|
|
const layerItem = findLayerByKey(layerData.value, layerKey);
|
2026-07-09 11:33:04 +08:00
|
|
|
|
syncBackgroundCacheToReactiveStore(layerKey);
|
|
|
|
|
|
const rawData =
|
|
|
|
|
|
Array.isArray(layerItem?.data) && layerItem.data.length > 0
|
|
|
|
|
|
? layerItem.data
|
|
|
|
|
|
: getEffectivePointLayerData(layerKey);
|
2026-07-01 08:48:39 +08:00
|
|
|
|
const displayData = filterPointLayerDataForDisplay(layerKey, rawData);
|
|
|
|
|
|
const shouldRestoreVisible =
|
|
|
|
|
|
layerItem?.checked === 1 || checkedLayerKeys.value.includes(layerKey);
|
|
|
|
|
|
|
|
|
|
|
|
mapClass.addInitDataLayer(displayData, layerKey);
|
|
|
|
|
|
if (!shouldRestoreVisible) {
|
|
|
|
|
|
mapClass.mdLayerTreeShowOrHidden(layerKey, false);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
restoreLayerLegendVisibility(layerKey);
|
|
|
|
|
|
mapClass.mdLayerTreeShowOrHidden(layerKey, true);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-06-03 15:03:31 +08:00
|
|
|
|
const getLegendItemsByLayerCode = (layerCode: string): any[] => {
|
|
|
|
|
|
return getLegendConfigByLayerCode(layerCode);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const getLegendChecked = (nameEn?: string): number => {
|
|
|
|
|
|
const normalizedNameEn = normalizeLegendNameEn(nameEn);
|
|
|
|
|
|
if (!normalizedNameEn) return 0;
|
|
|
|
|
|
return mapViewStore.getLegendChecked(normalizedNameEn);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-07-01 08:48:39 +08:00
|
|
|
|
const restoreLayerLegendVisibility = (layerKey: string): boolean => {
|
|
|
|
|
|
const layerLegendItems = getLegendItemsByLayerCode(layerKey);
|
|
|
|
|
|
if (
|
|
|
|
|
|
legendDataOriginal.value.length === 0 ||
|
|
|
|
|
|
layerLegendItems.length === 0
|
|
|
|
|
|
) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
mapClass.setLegendPointVisible(layerKey, '', false);
|
|
|
|
|
|
layerLegendItems.forEach((legendItem: any) => {
|
|
|
|
|
|
if (getLegendChecked(legendItem.nameEn) === 1) {
|
|
|
|
|
|
applyLegendItemVisibility(legendItem, 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-06-03 15:03:31 +08:00
|
|
|
|
const buildRuntimeLegendTree = (
|
|
|
|
|
|
items: any[] = [],
|
|
|
|
|
|
selectedLayerCodes?: Set<string>
|
|
|
|
|
|
): any[] => {
|
|
|
|
|
|
return buildLegendTree({
|
|
|
|
|
|
items,
|
|
|
|
|
|
selectedLayerCodes,
|
|
|
|
|
|
getLegendChecked,
|
|
|
|
|
|
normalizeLegendNameEn
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const deriveSelectedLegendData = (layerKeys: string[]): any[] => {
|
|
|
|
|
|
const selectedLayerCodes = new Set(
|
|
|
|
|
|
layerKeys.filter(
|
|
|
|
|
|
key =>
|
|
|
|
|
|
key &&
|
|
|
|
|
|
key !== '-' &&
|
|
|
|
|
|
key !== 'customBaseLayer' &&
|
|
|
|
|
|
key !== 'powerBaseStation'
|
|
|
|
|
|
)
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
let derivedLegend = buildRuntimeLegendTree(
|
|
|
|
|
|
legendDataOriginal.value,
|
|
|
|
|
|
selectedLayerCodes
|
|
|
|
|
|
);
|
|
|
|
|
|
derivedLegend = applyEnvFacilityLegendRule(
|
|
|
|
|
|
derivedLegend,
|
|
|
|
|
|
Array.from(selectedLayerCodes),
|
|
|
|
|
|
legendDataOriginal.value,
|
|
|
|
|
|
items => buildRuntimeLegendTree(items)
|
|
|
|
|
|
);
|
|
|
|
|
|
return sortByOrderIndex(derivedLegend);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const rebuildLegendRuntimeData = (
|
|
|
|
|
|
layerKeys: string[] = checkedLayerKeys.value
|
|
|
|
|
|
) => {
|
|
|
|
|
|
legendData.value = sortByOrderIndex(
|
|
|
|
|
|
buildRuntimeLegendTree(legendDataOriginal.value)
|
|
|
|
|
|
);
|
|
|
|
|
|
legendDataSelected.value = deriveSelectedLegendData(layerKeys);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const getLayerBranchKeys = (rootKey: string): string[] => {
|
|
|
|
|
|
const rootLayer = getLayerConfigByKey(rootKey);
|
|
|
|
|
|
if (!rootLayer) return [];
|
|
|
|
|
|
|
|
|
|
|
|
const branchKeys: string[] = [];
|
|
|
|
|
|
const walk = (node: any) => {
|
|
|
|
|
|
if (node?.key) {
|
|
|
|
|
|
branchKeys.push(node.key);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (node?.children?.length > 0) {
|
|
|
|
|
|
node.children.forEach((child: any) => walk(child));
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
walk(rootLayer);
|
|
|
|
|
|
return branchKeys;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const normalizeCheckedLayerKeys = (
|
|
|
|
|
|
rawKeys: string[] = [],
|
|
|
|
|
|
triggerKey?: string,
|
|
|
|
|
|
isChecked?: boolean
|
|
|
|
|
|
): string[] => {
|
|
|
|
|
|
return applyLayerMutualExclusionRules({
|
|
|
|
|
|
rawKeys,
|
|
|
|
|
|
previousKeys: checkedLayerKeys.value,
|
|
|
|
|
|
triggerKey,
|
|
|
|
|
|
checked: isChecked,
|
|
|
|
|
|
getLayerBranchKeys
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
2026-06-01 08:42:06 +08:00
|
|
|
|
|
2026-07-01 08:48:39 +08:00
|
|
|
|
const getRuntimeCheckedLayerKeys = (): string[] => {
|
2026-07-03 15:59:05 +08:00
|
|
|
|
return mapViewStore.getCheckedLayerKeys();
|
2026-07-01 08:48:39 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2026-07-09 11:33:04 +08:00
|
|
|
|
const shouldSyncMergedPointData = (layerKey: string, pageToken?: number) => {
|
|
|
|
|
|
if (!layerKey) return false;
|
|
|
|
|
|
return (
|
|
|
|
|
|
shouldApplyToActivePage(layerKey, pageToken) ||
|
|
|
|
|
|
getRuntimeCheckedLayerKeys().includes(layerKey)
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const getEffectivePointLayerCache = (layerKey: string) => {
|
|
|
|
|
|
const reactiveCache = mapDataStore.getPointLayerCache(layerKey);
|
|
|
|
|
|
if (reactiveCache) {
|
|
|
|
|
|
return reactiveCache;
|
|
|
|
|
|
}
|
|
|
|
|
|
return backgroundPointLayerCache.get(layerKey);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const getEffectivePointLayerData = (layerKey: string): any[] => {
|
|
|
|
|
|
const reactiveCache = mapDataStore.getPointLayerCache(layerKey);
|
|
|
|
|
|
if (reactiveCache?.data) {
|
|
|
|
|
|
return reactiveCache.data;
|
|
|
|
|
|
}
|
|
|
|
|
|
return getEffectivePointLayerCache(layerKey)?.data || [];
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const hasEffectivePointLayerCache = (layerKey: string, cacheKey: string) => {
|
|
|
|
|
|
const cache = getEffectivePointLayerCache(layerKey);
|
|
|
|
|
|
if (!cache || !cacheKey) return false;
|
|
|
|
|
|
return cache.cacheKey === cacheKey && Array.isArray(cache.data);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const setBackgroundPointLayerCache = (
|
|
|
|
|
|
layerKey: string,
|
|
|
|
|
|
cache: {
|
|
|
|
|
|
checked: boolean;
|
|
|
|
|
|
data: any[];
|
|
|
|
|
|
cacheKey?: string;
|
|
|
|
|
|
}
|
|
|
|
|
|
) => {
|
|
|
|
|
|
if (!layerKey) return;
|
|
|
|
|
|
backgroundPointLayerCache.set(layerKey, cache);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const syncBackgroundCacheToReactiveStore = (layerKey: string) => {
|
|
|
|
|
|
const cache = backgroundPointLayerCache.get(layerKey);
|
|
|
|
|
|
if (!cache) return;
|
|
|
|
|
|
mapDataStore.setPointLayerCache(layerKey, cache);
|
|
|
|
|
|
backgroundPointLayerCache.delete(layerKey);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const syncPointDataForFilter = (layerKeys: string[] = []) => {
|
|
|
|
|
|
mapDataStore.rebuildPointDataFromCache(layerKeys);
|
2026-07-01 08:48:39 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2026-07-08 08:33:08 +08:00
|
|
|
|
const normalizePointLayerItems = (list: any[] = []) => {
|
|
|
|
|
|
return list
|
|
|
|
|
|
.map((item: any) => {
|
|
|
|
|
|
const iconType = item.anchoPointState;
|
|
|
|
|
|
const legendConfig = getLegendConfigByNameEn(iconType);
|
|
|
|
|
|
return {
|
|
|
|
|
|
...item,
|
|
|
|
|
|
iconCode: item.iconCode || legendConfig?.icon || '',
|
|
|
|
|
|
code: item.code || legendConfig?.code || '',
|
|
|
|
|
|
tm:
|
|
|
|
|
|
item.sttpMap === 'WQ_ALARM'
|
|
|
|
|
|
? item?.warnDataList?.[0]?.tm
|
|
|
|
|
|
: item?.tm,
|
|
|
|
|
|
_id:
|
|
|
|
|
|
item._id || `${item.sttpMap || iconType || 'point'}_${item.stcd}`,
|
|
|
|
|
|
layerKey: item.layerKey || YLFB_POINT_LAYER_KEY
|
|
|
|
|
|
};
|
|
|
|
|
|
})
|
|
|
|
|
|
.filter(
|
|
|
|
|
|
(item: any) =>
|
|
|
|
|
|
(item.iconCode || item.code == 'colorLayer') &&
|
|
|
|
|
|
!(item?.baseId == 'all' && item?.anchoPointState?.endsWith('_nbuilt'))
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-07-01 08:48:39 +08:00
|
|
|
|
const ensureGISLayerConfig = (layerItem: any) => {
|
|
|
|
|
|
if (!layerItem) return null;
|
|
|
|
|
|
|
|
|
|
|
|
if (!layerItem.config && layerItem.paramJson) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const jsonObj = JSON.parse(layerItem.paramJson);
|
|
|
|
|
|
layerItem.config = getMapConfig(jsonObj);
|
|
|
|
|
|
} catch {
|
|
|
|
|
|
layerItem.config = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!layerItem.config) {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (layerItem.config.key !== layerItem.key) {
|
|
|
|
|
|
layerItem.config = {
|
|
|
|
|
|
...layerItem.config,
|
|
|
|
|
|
key: layerItem.key
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return layerItem.config;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-06-01 08:42:06 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 设置图层数据
|
|
|
|
|
|
*/
|
|
|
|
|
|
const setLayerData = (data: any[]) => {
|
2026-06-03 15:03:31 +08:00
|
|
|
|
mapConfigStore.setLayerConfigTree(data);
|
2026-07-01 08:48:39 +08:00
|
|
|
|
const nextCheckedLayerKeys = mapConfigStore.extractCheckedLayerKeys(data);
|
|
|
|
|
|
mapViewStore.setCheckedLayerKeys(nextCheckedLayerKeys);
|
|
|
|
|
|
rebuildLegendRuntimeData(nextCheckedLayerKeys);
|
2026-04-22 17:53:20 +08:00
|
|
|
|
};
|
2026-06-01 08:42:06 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 按照 orderIndex 排序图例数据(递归)
|
|
|
|
|
|
*/
|
|
|
|
|
|
const sortByOrderIndex = (items: any[]): any[] => {
|
|
|
|
|
|
return items
|
|
|
|
|
|
.map(item => {
|
|
|
|
|
|
if (item.childrenList && item.childrenList.length > 0) {
|
|
|
|
|
|
return {
|
|
|
|
|
|
...item,
|
|
|
|
|
|
childrenList: sortByOrderIndex(item.childrenList)
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
return item;
|
|
|
|
|
|
})
|
|
|
|
|
|
.sort((a, b) => (a.orderIndex || 0) - (b.orderIndex || 0));
|
2026-04-22 17:53:20 +08:00
|
|
|
|
};
|
2026-06-01 08:42:06 +08:00
|
|
|
|
|
|
|
|
|
|
/**
|
2026-07-08 08:33:08 +08:00
|
|
|
|
* 启动新的图层加载会话,供需要严格按最新查询条件收口的场景复用。
|
2026-06-01 08:42:06 +08:00
|
|
|
|
*/
|
2026-06-03 15:03:31 +08:00
|
|
|
|
const startLoadSession = () => {
|
|
|
|
|
|
loadSessionSeed += 1;
|
2026-07-08 08:33:08 +08:00
|
|
|
|
return loadSessionSeed;
|
2026-06-03 15:03:31 +08:00
|
|
|
|
};
|
2026-06-01 08:42:06 +08:00
|
|
|
|
|
2026-06-03 15:03:31 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 判断当前请求是否仍属于最新加载会话,避免旧请求结果回写到最新页面。
|
|
|
|
|
|
*/
|
|
|
|
|
|
const isCurrentLoadSession = (sessionId: number) => {
|
|
|
|
|
|
return loadSessionSeed === sessionId;
|
|
|
|
|
|
};
|
2026-06-01 08:42:06 +08:00
|
|
|
|
|
2026-07-08 08:33:08 +08:00
|
|
|
|
const activatePageContext = (pageKey: string, items: any[] = []) => {
|
2026-07-09 11:33:04 +08:00
|
|
|
|
activePageKey = pageKey;
|
|
|
|
|
|
pendingPageNavigationKey = '';
|
2026-07-08 08:33:08 +08:00
|
|
|
|
activePageRenderToken += 1;
|
|
|
|
|
|
activePageLayerKeys = new Set(getAllLayerKeys(items));
|
|
|
|
|
|
return activePageRenderToken;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-07-09 11:33:04 +08:00
|
|
|
|
const markPendingPageNavigation = (targetPageKey: string) => {
|
|
|
|
|
|
pendingPageNavigationKey = targetPageKey || '__route-change__';
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const hasPendingPageNavigationAwayFrom = (pageKey: string) => {
|
|
|
|
|
|
return !!pendingPageNavigationKey && pendingPageNavigationKey !== pageKey;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-07-08 08:33:08 +08:00
|
|
|
|
const shouldApplyToActivePage = (layerKey: string, pageToken?: number) => {
|
|
|
|
|
|
if (!layerKey) return false;
|
2026-07-09 11:33:04 +08:00
|
|
|
|
if (activePageKey && hasPendingPageNavigationAwayFrom(activePageKey)) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2026-07-08 08:33:08 +08:00
|
|
|
|
if (pageToken === undefined) return true;
|
|
|
|
|
|
return (
|
|
|
|
|
|
activePageRenderToken === pageToken && activePageLayerKeys.has(layerKey)
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const isActivePageToken = (pageToken?: number) => {
|
|
|
|
|
|
if (pageToken === undefined) return true;
|
|
|
|
|
|
return activePageRenderToken === pageToken;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-07-09 11:33:04 +08:00
|
|
|
|
const beginPageLoading = (pageToken?: number) => {
|
|
|
|
|
|
if (typeof pageToken === 'number') {
|
|
|
|
|
|
activeLoadingPageToken = pageToken;
|
2026-07-08 08:33:08 +08:00
|
|
|
|
}
|
2026-07-09 11:33:04 +08:00
|
|
|
|
mapDataStore.setLoading(true);
|
|
|
|
|
|
};
|
2026-07-08 08:33:08 +08:00
|
|
|
|
|
2026-07-09 11:33:04 +08:00
|
|
|
|
const finishPageLoading = (pageToken?: number) => {
|
|
|
|
|
|
if (typeof pageToken !== 'number') {
|
|
|
|
|
|
mapDataStore.setLoading(false);
|
|
|
|
|
|
return;
|
2026-06-03 15:03:31 +08:00
|
|
|
|
}
|
2026-06-01 08:42:06 +08:00
|
|
|
|
|
2026-07-09 11:33:04 +08:00
|
|
|
|
if (
|
|
|
|
|
|
activeLoadingPageToken === pageToken &&
|
|
|
|
|
|
activePageRenderToken === pageToken
|
|
|
|
|
|
) {
|
|
|
|
|
|
activeLoadingPageToken = null;
|
|
|
|
|
|
mapDataStore.setLoading(false);
|
|
|
|
|
|
}
|
2026-06-03 15:03:31 +08:00
|
|
|
|
};
|
2026-06-01 08:42:06 +08:00
|
|
|
|
|
2026-06-03 15:03:31 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 设置图例数据(过滤掉 name == '地图' 的项,ifShow == 0 时不显示)
|
|
|
|
|
|
* @param data - 全部图例数据
|
|
|
|
|
|
* @param selectedData - 当前页面选中的图例数据(用于标记选中状态)
|
|
|
|
|
|
*/
|
|
|
|
|
|
const setLegendData = (data: any[], selectedData: any[] = []) => {
|
|
|
|
|
|
mapConfigStore.setLegendConfigOriginal(data);
|
|
|
|
|
|
mapConfigStore.setPageLegendConfig(selectedData);
|
|
|
|
|
|
mapViewStore.setLegendCheckedState(
|
|
|
|
|
|
buildLegendCheckedState(legendDataOriginal.value, normalizeLegendNameEn)
|
|
|
|
|
|
);
|
|
|
|
|
|
rebuildLegendRuntimeData(checkedLayerKeys.value);
|
2026-06-01 08:42:06 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 设置选中的图例数据(在图层加载完成后调用)
|
|
|
|
|
|
*/
|
|
|
|
|
|
const setSelectedLegendData = () => {
|
2026-06-03 15:03:31 +08:00
|
|
|
|
rebuildLegendRuntimeData(checkedLayerKeys.value);
|
2026-06-01 08:42:06 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 设置描点数据
|
|
|
|
|
|
*/
|
|
|
|
|
|
const setPointData = (data: any[]) => {
|
2026-06-03 15:03:31 +08:00
|
|
|
|
mapDataStore.setPointData(data);
|
2026-06-01 08:42:06 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2026-07-08 08:33:08 +08:00
|
|
|
|
const replacePointLayerData = (
|
|
|
|
|
|
layerKey: string,
|
|
|
|
|
|
data: any[] = [],
|
|
|
|
|
|
visible = true
|
|
|
|
|
|
) => {
|
|
|
|
|
|
if (!layerKey) return;
|
|
|
|
|
|
|
|
|
|
|
|
const layerItem = findLayerByKey(layerData.value, layerKey);
|
|
|
|
|
|
const normalizedData = normalizePointLayerItems(
|
|
|
|
|
|
data.map((item: any) => ({
|
|
|
|
|
|
...item,
|
|
|
|
|
|
layerKey
|
|
|
|
|
|
}))
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
mapDataStore.setPointLayerCache(layerKey, {
|
|
|
|
|
|
checked: visible,
|
|
|
|
|
|
data: normalizedData,
|
|
|
|
|
|
cacheKey: `custom:${layerKey}:${normalizedData.length}`
|
|
|
|
|
|
});
|
|
|
|
|
|
mapDataStore.rebuildPointDataFromCache();
|
|
|
|
|
|
|
|
|
|
|
|
if (layerItem) {
|
|
|
|
|
|
layerItem.data = normalizedData;
|
|
|
|
|
|
layerItem.checked = visible ? 1 : 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!visible || normalizedData.length === 0) {
|
|
|
|
|
|
if (mapClass.hasLayer(layerKey)) {
|
|
|
|
|
|
mapClass.mdLayerTreeShowOrHidden(layerKey, false);
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
refreshPointLayerDisplayData(layerKey);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-07-09 11:33:04 +08:00
|
|
|
|
const applyLoadedPointLayerToMap = (
|
|
|
|
|
|
layer: any,
|
|
|
|
|
|
layerKey: string,
|
|
|
|
|
|
list: any[] = [],
|
|
|
|
|
|
pageToken?: number
|
|
|
|
|
|
) => {
|
|
|
|
|
|
if (!Array.isArray(list)) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
layer.data = list;
|
|
|
|
|
|
const isLayerChecked = layer.checked === 1;
|
|
|
|
|
|
|
|
|
|
|
|
if (list.length === 0 || !shouldApplyToActivePage(layerKey, pageToken)) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const displayData = filterPointLayerDataForDisplay(layerKey, list);
|
|
|
|
|
|
mapClass.addInitDataLayer(displayData, layerKey);
|
|
|
|
|
|
if (isLayerChecked) {
|
|
|
|
|
|
restoreLayerLegendVisibility(layerKey);
|
|
|
|
|
|
}
|
|
|
|
|
|
mapClass.mdLayerTreeShowOrHidden(layerKey, isLayerChecked);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-06-01 08:42:06 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 图例数据转对象
|
|
|
|
|
|
*/
|
|
|
|
|
|
const legendData2Obj = (data: any[]): Record<string, any> => {
|
|
|
|
|
|
const _tempData: Record<string, any> = {};
|
|
|
|
|
|
const f = (_data: any[]) => {
|
|
|
|
|
|
_data.forEach(item => {
|
|
|
|
|
|
if (item?.childrenList && item.childrenList?.length > 0) {
|
|
|
|
|
|
f(item.childrenList);
|
|
|
|
|
|
} else {
|
2026-06-03 15:03:31 +08:00
|
|
|
|
const normalizedNameEn = normalizeLegendNameEn(item.nameEn);
|
|
|
|
|
|
if (normalizedNameEn) {
|
|
|
|
|
|
_tempData[normalizedNameEn] = item;
|
2026-06-01 08:42:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
f(data);
|
|
|
|
|
|
return _tempData;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 处理单个 pointMap 图层的显示/隐藏
|
|
|
|
|
|
* 数据已在初始化时通过 loadLayerData 加载并存入 layer.data
|
|
|
|
|
|
*/
|
|
|
|
|
|
const handlePointMapLayer = async (layer: any, isChecked: boolean) => {
|
|
|
|
|
|
const { key } = layer;
|
|
|
|
|
|
|
|
|
|
|
|
// 如果没有勾选,隐藏图层
|
|
|
|
|
|
if (!isChecked) {
|
|
|
|
|
|
if (mapClass.hasLayer(key)) {
|
|
|
|
|
|
mapClass.mdLayerTreeShowOrHidden(key, false);
|
|
|
|
|
|
}
|
2026-06-03 15:03:31 +08:00
|
|
|
|
if (mapDataStore.getPointLayerCache(key)) {
|
|
|
|
|
|
mapDataStore.setPointLayerCacheChecked(key, false);
|
2026-06-01 08:42:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
if (layer) {
|
|
|
|
|
|
layer.checked = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 使用初始化时已加载的数据(存入 layer.data)
|
2026-07-09 11:33:04 +08:00
|
|
|
|
const cachedData =
|
|
|
|
|
|
Array.isArray(layer?.data) && layer.data.length > 0
|
|
|
|
|
|
? layer.data
|
|
|
|
|
|
: getPointLayerData(key);
|
2026-06-01 08:42:06 +08:00
|
|
|
|
if (cachedData && cachedData.length > 0) {
|
|
|
|
|
|
layer.checked = 1;
|
2026-07-01 08:48:39 +08:00
|
|
|
|
const displayData = filterPointLayerDataForDisplay(key, cachedData);
|
2026-07-09 11:33:04 +08:00
|
|
|
|
mapClass.addInitDataLayer(displayData, key);
|
2026-07-01 08:48:39 +08:00
|
|
|
|
restoreLayerLegendVisibility(key);
|
2026-06-03 15:03:31 +08:00
|
|
|
|
if (mapDataStore.getPointLayerCache(key)) {
|
|
|
|
|
|
mapDataStore.setPointLayerCacheChecked(key, true);
|
2026-06-01 08:42:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.warn(`图层 ${key} 没有缓存数据`);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 更新图层数据(联动图例和描点)
|
|
|
|
|
|
* @param checkKeys - 选中的图层 key 列表
|
|
|
|
|
|
* @param isInit - 是否是初始化阶段(初始化时遍历所有图层)
|
|
|
|
|
|
*/
|
|
|
|
|
|
const updateLayerData = async (checkKeys: string[], isInit = false) => {
|
2026-07-03 15:59:05 +08:00
|
|
|
|
if (!isInit) {
|
|
|
|
|
|
checkKeys = normalizeCheckedLayerKeys(checkKeys);
|
|
|
|
|
|
}
|
2026-04-03 16:04:16 +08:00
|
|
|
|
|
2026-06-03 15:03:31 +08:00
|
|
|
|
mapViewStore.setCheckedLayerKeys(checkKeys);
|
2026-06-01 08:42:06 +08:00
|
|
|
|
|
|
|
|
|
|
// 收集变化的图层 key(新增选中 / 取消选中)
|
|
|
|
|
|
const newlyChecked: string[] = [];
|
|
|
|
|
|
const newlyUnchecked: string[] = [];
|
2026-04-03 16:04:16 +08:00
|
|
|
|
|
2026-06-01 08:42:06 +08:00
|
|
|
|
// 更新图层的 checked 状态,并记录变化
|
|
|
|
|
|
const updateCheckedStatus = (items: any[]) => {
|
|
|
|
|
|
items.forEach(item => {
|
|
|
|
|
|
if (item.key) {
|
|
|
|
|
|
const wasChecked = item.checked === 1;
|
|
|
|
|
|
const isChecked = checkKeys.includes(item.key);
|
|
|
|
|
|
item.checked = isChecked ? 1 : 0;
|
|
|
|
|
|
if (wasChecked !== isChecked) {
|
|
|
|
|
|
if (isChecked) {
|
|
|
|
|
|
newlyChecked.push(item.key);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
newlyUnchecked.push(item.key);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (item.children && item.children.length > 0) {
|
|
|
|
|
|
updateCheckedStatus(item.children);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
updateCheckedStatus(layerData.value);
|
|
|
|
|
|
|
2026-06-03 15:03:31 +08:00
|
|
|
|
rebuildLegendRuntimeData(checkKeys);
|
2026-06-01 08:42:06 +08:00
|
|
|
|
|
|
|
|
|
|
// 控制锚点显示隐藏:初始化时处理全部,否则只处理变化的图层
|
|
|
|
|
|
const keysToProcess = isInit
|
|
|
|
|
|
? getAllLayerKeys(layerData.value)
|
|
|
|
|
|
: [...newlyChecked, ...newlyUnchecked];
|
|
|
|
|
|
|
|
|
|
|
|
for (const key of keysToProcess) {
|
|
|
|
|
|
const layerItem = findLayerByKey(layerData.value, key);
|
|
|
|
|
|
if (layerItem && layerItem.type === 'pointMap' && layerItem.url) {
|
|
|
|
|
|
const shouldBeVisible = checkKeys.includes(key);
|
|
|
|
|
|
await handlePointMapLayer(layerItem, shouldBeVisible);
|
|
|
|
|
|
if (shouldBeVisible) {
|
2026-07-01 08:48:39 +08:00
|
|
|
|
restoreLayerLegendVisibility(key);
|
|
|
|
|
|
mapClass.mdLayerTreeShowOrHidden(key, true);
|
2026-06-01 08:42:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
} else if (layerItem && layerItem.type === 'GISMap') {
|
2026-07-01 08:48:39 +08:00
|
|
|
|
// 处理 GISMap 类型的图层:勾选时若地图实例中不存在,则先重新加载再控制显隐。
|
2026-06-01 08:42:06 +08:00
|
|
|
|
if (key && key !== '-' && key !== 'powerBaseStation') {
|
|
|
|
|
|
const shouldBeVisible = checkKeys.includes(key);
|
2026-07-01 08:48:39 +08:00
|
|
|
|
const layerConfig = ensureGISLayerConfig(layerItem);
|
|
|
|
|
|
if (
|
|
|
|
|
|
shouldBeVisible &&
|
|
|
|
|
|
layerConfig &&
|
|
|
|
|
|
!mapClass.hasBaseLayer(layerItem.key)
|
|
|
|
|
|
) {
|
|
|
|
|
|
mapClass.addBaseDataLayer(layerConfig, true);
|
|
|
|
|
|
}
|
|
|
|
|
|
mapClass.controlBaseLayerTreeShowAndHidden(
|
|
|
|
|
|
key,
|
|
|
|
|
|
layerItem.key,
|
|
|
|
|
|
shouldBeVisible
|
|
|
|
|
|
);
|
2026-06-01 08:42:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 加载所有包含URL的图层数据
|
|
|
|
|
|
* @param items 图层数据
|
|
|
|
|
|
* @param checkedKeys 选中的图层 keys(用于优先加载)
|
|
|
|
|
|
*/
|
2026-07-08 08:33:08 +08:00
|
|
|
|
const loadAllLayerData = async (
|
|
|
|
|
|
items: any[],
|
|
|
|
|
|
checkedKeys: string[] = [],
|
|
|
|
|
|
options: {
|
|
|
|
|
|
pageToken?: number;
|
|
|
|
|
|
skipSessionCheck?: boolean;
|
2026-07-09 11:33:04 +08:00
|
|
|
|
pageKey?: string;
|
2026-07-08 08:33:08 +08:00
|
|
|
|
} = {}
|
|
|
|
|
|
) => {
|
2026-07-09 11:33:04 +08:00
|
|
|
|
beginPageLoading(options.pageToken);
|
2026-07-08 08:33:08 +08:00
|
|
|
|
const currentSessionId = options.skipSessionCheck
|
|
|
|
|
|
? undefined
|
|
|
|
|
|
: startLoadSession();
|
2026-06-03 15:03:31 +08:00
|
|
|
|
const checkedTasks: Array<() => Promise<any>> = [];
|
|
|
|
|
|
const uncheckedTasks: Array<() => Promise<any>> = [];
|
2026-06-01 08:42:06 +08:00
|
|
|
|
const processedKeys = new Set<string>();
|
2026-07-09 11:33:04 +08:00
|
|
|
|
const debugStart = Date.now();
|
2026-06-01 08:42:06 +08:00
|
|
|
|
|
|
|
|
|
|
const processItems = (itemList: any[]) => {
|
|
|
|
|
|
itemList.forEach(item => {
|
|
|
|
|
|
if (
|
|
|
|
|
|
(item.type === 'pointMap' || item.url) &&
|
|
|
|
|
|
item.key &&
|
|
|
|
|
|
item.url &&
|
|
|
|
|
|
!processedKeys.has(item.key)
|
|
|
|
|
|
) {
|
|
|
|
|
|
processedKeys.add(item.key);
|
2026-07-08 08:33:08 +08:00
|
|
|
|
const task = () =>
|
|
|
|
|
|
loadLayerData(item, {
|
|
|
|
|
|
sessionId: currentSessionId,
|
|
|
|
|
|
pageToken: options.pageToken,
|
2026-07-09 11:33:04 +08:00
|
|
|
|
skipSessionCheck: options.skipSessionCheck,
|
|
|
|
|
|
pageKey: options.pageKey
|
2026-07-08 08:33:08 +08:00
|
|
|
|
});
|
2026-06-03 15:03:31 +08:00
|
|
|
|
if (checkedKeys.includes(item.key)) {
|
|
|
|
|
|
checkedTasks.push(task);
|
2026-06-01 08:42:06 +08:00
|
|
|
|
} else {
|
2026-06-03 15:03:31 +08:00
|
|
|
|
uncheckedTasks.push(task);
|
2026-06-01 08:42:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (item.children && item.children.length > 0) {
|
|
|
|
|
|
processItems(item.children);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
processItems(items);
|
|
|
|
|
|
|
2026-06-03 15:03:31 +08:00
|
|
|
|
try {
|
2026-07-09 11:33:04 +08:00
|
|
|
|
// 首页初始化:所有图层接口一次性并发下发,避免分批串行等待。
|
|
|
|
|
|
const loadResults = await Promise.allSettled(
|
|
|
|
|
|
[...checkedTasks, ...uncheckedTasks].map(task => task())
|
2026-07-08 08:33:08 +08:00
|
|
|
|
);
|
|
|
|
|
|
const failedResults = loadResults.filter(
|
2026-06-03 15:03:31 +08:00
|
|
|
|
result => result.status === 'rejected'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if (failedResults.length > 0) {
|
|
|
|
|
|
console.warn(
|
|
|
|
|
|
'部分图层数据加载失败,但不会阻断其他图层:',
|
|
|
|
|
|
failedResults
|
|
|
|
|
|
);
|
2026-06-01 08:42:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-08 08:33:08 +08:00
|
|
|
|
if (
|
|
|
|
|
|
(typeof currentSessionId === 'number' &&
|
|
|
|
|
|
!isCurrentLoadSession(currentSessionId)) ||
|
|
|
|
|
|
!isActivePageToken(options.pageToken)
|
|
|
|
|
|
) {
|
2026-06-03 15:03:31 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-06-01 08:42:06 +08:00
|
|
|
|
|
2026-07-09 11:33:04 +08:00
|
|
|
|
if (
|
|
|
|
|
|
options.pageKey &&
|
|
|
|
|
|
hasPendingPageNavigationAwayFrom(options.pageKey)
|
|
|
|
|
|
) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-03 15:03:31 +08:00
|
|
|
|
// 确保所有图层的数据都已经缓存到 pointDataCache 和 layer.data
|
|
|
|
|
|
const allLayerKeys = getAllLayerKeys(items);
|
|
|
|
|
|
for (const key of allLayerKeys) {
|
|
|
|
|
|
const layer = findLayerByKey(items, key);
|
2026-07-09 11:33:04 +08:00
|
|
|
|
if (layer && getEffectivePointLayerData(key).length > 0) {
|
|
|
|
|
|
syncBackgroundCacheToReactiveStore(key);
|
|
|
|
|
|
layer.data = getEffectivePointLayerData(key);
|
2026-06-03 15:03:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const allPointData = mapDataStore.rebuildPointDataFromCache(allLayerKeys);
|
2026-07-01 08:48:39 +08:00
|
|
|
|
attachNearbyPointRuntimeMeta(allPointData);
|
|
|
|
|
|
const runtimeCheckedKeys = getRuntimeCheckedLayerKeys();
|
2026-07-03 15:59:05 +08:00
|
|
|
|
const treeCheckedKeys = mapConfigStore.extractCheckedLayerKeys(
|
|
|
|
|
|
layerData.value.length > 0 ? layerData.value : items
|
|
|
|
|
|
);
|
|
|
|
|
|
const finalCheckedKeys =
|
|
|
|
|
|
runtimeCheckedKeys.length > 0 ? runtimeCheckedKeys : treeCheckedKeys;
|
2026-07-01 08:48:39 +08:00
|
|
|
|
|
|
|
|
|
|
// 备注:近邻点元数据需要基于全量点位统一识别,识别完成后同步刷新各图层 Feature。
|
|
|
|
|
|
for (const key of allLayerKeys) {
|
|
|
|
|
|
const layer = findLayerByKey(items, key);
|
|
|
|
|
|
if (!layer || !hasPointLayerData(key)) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const layerPoints = getPointLayerData(key);
|
|
|
|
|
|
const displayData = filterPointLayerDataForDisplay(key, layerPoints);
|
|
|
|
|
|
const cacheChecked = mapDataStore.getPointLayerCache(key)?.checked;
|
|
|
|
|
|
const shouldRestoreVisible =
|
|
|
|
|
|
typeof cacheChecked === 'boolean'
|
|
|
|
|
|
? cacheChecked
|
2026-07-03 15:59:05 +08:00
|
|
|
|
: layer.checked === 1 || finalCheckedKeys.includes(key);
|
2026-07-01 08:48:39 +08:00
|
|
|
|
layer.data = layerPoints;
|
|
|
|
|
|
mapClass.addInitDataLayer(displayData, key);
|
|
|
|
|
|
if (shouldRestoreVisible) {
|
|
|
|
|
|
restoreLayerLegendVisibility(key);
|
|
|
|
|
|
}
|
|
|
|
|
|
mapClass.mdLayerTreeShowOrHidden(key, shouldRestoreVisible);
|
|
|
|
|
|
}
|
2026-06-03 15:03:31 +08:00
|
|
|
|
|
|
|
|
|
|
// 设置选中的图例数据(确保图例在所有锚点加载完成后才显示)
|
|
|
|
|
|
setSelectedLegendData();
|
|
|
|
|
|
|
2026-07-01 08:48:39 +08:00
|
|
|
|
// 备注:初始化 loading 期间用户可能已经手动改过图层勾选,这里必须以最新运行态收尾,
|
|
|
|
|
|
// 不能再回放 load 启动瞬间的默认 checked 快照。
|
2026-07-03 15:59:05 +08:00
|
|
|
|
await updateLayerData(finalCheckedKeys, true);
|
2026-06-03 15:03:31 +08:00
|
|
|
|
} finally {
|
2026-07-08 08:33:08 +08:00
|
|
|
|
const shouldFinishLoading =
|
|
|
|
|
|
(typeof currentSessionId !== 'number' ||
|
|
|
|
|
|
isCurrentLoadSession(currentSessionId)) &&
|
|
|
|
|
|
(options.pageToken === undefined ||
|
|
|
|
|
|
activePageRenderToken === options.pageToken);
|
|
|
|
|
|
if (shouldFinishLoading) {
|
2026-07-09 11:33:04 +08:00
|
|
|
|
finishPageLoading(options.pageToken);
|
2026-07-08 08:33:08 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const loadCurrentPageLayerData = async (
|
|
|
|
|
|
items: any[],
|
|
|
|
|
|
checkedKeys: string[] = [],
|
|
|
|
|
|
options: {
|
|
|
|
|
|
pageToken?: number;
|
|
|
|
|
|
skipSessionCheck?: boolean;
|
|
|
|
|
|
} = {}
|
|
|
|
|
|
) => {
|
|
|
|
|
|
const checkedLayerSet = new Set(checkedKeys);
|
|
|
|
|
|
const tasks: Array<() => Promise<any>> = [];
|
|
|
|
|
|
const processedKeys = new Set<string>();
|
|
|
|
|
|
|
|
|
|
|
|
const processItems = (itemList: any[]) => {
|
|
|
|
|
|
itemList.forEach(item => {
|
|
|
|
|
|
if (
|
|
|
|
|
|
item.type === 'pointMap' &&
|
|
|
|
|
|
item.key &&
|
|
|
|
|
|
item.url &&
|
|
|
|
|
|
checkedLayerSet.has(item.key) &&
|
|
|
|
|
|
!processedKeys.has(item.key)
|
|
|
|
|
|
) {
|
|
|
|
|
|
processedKeys.add(item.key);
|
|
|
|
|
|
tasks.push(() =>
|
|
|
|
|
|
loadLayerData(item, {
|
|
|
|
|
|
sessionId: options.skipSessionCheck ? undefined : loadSessionSeed,
|
|
|
|
|
|
pageToken: options.pageToken,
|
|
|
|
|
|
skipSessionCheck: options.skipSessionCheck ?? true
|
|
|
|
|
|
})
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (item.children && item.children.length > 0) {
|
|
|
|
|
|
processItems(item.children);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
processItems(items);
|
|
|
|
|
|
|
|
|
|
|
|
if (tasks.length === 0) {
|
2026-07-09 11:33:04 +08:00
|
|
|
|
finishPageLoading(options.pageToken);
|
2026-07-08 08:33:08 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-09 11:33:04 +08:00
|
|
|
|
beginPageLoading(options.pageToken);
|
2026-07-08 08:33:08 +08:00
|
|
|
|
|
|
|
|
|
|
try {
|
2026-07-09 11:33:04 +08:00
|
|
|
|
// 菜单切换:仅当前页的勾选图层一次性并发下发,旧请求在后台继续运行。
|
|
|
|
|
|
await Promise.allSettled(tasks.map(task => task()));
|
2026-07-08 08:33:08 +08:00
|
|
|
|
} finally {
|
|
|
|
|
|
if (isActivePageToken(options.pageToken)) {
|
2026-07-09 11:33:04 +08:00
|
|
|
|
finishPageLoading(options.pageToken);
|
2026-07-08 08:33:08 +08:00
|
|
|
|
}
|
2026-06-01 08:42:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 根据 key 查找图层
|
|
|
|
|
|
*/
|
|
|
|
|
|
const findLayerByKey = (items: any[], key: string): any | null => {
|
2026-06-03 15:03:31 +08:00
|
|
|
|
void items;
|
|
|
|
|
|
if (!key) return null;
|
|
|
|
|
|
return getLayerConfigByKey(key) || null;
|
2026-06-01 08:42:06 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取所有图层 keys
|
|
|
|
|
|
*/
|
|
|
|
|
|
const getAllLayerKeys = (data: any[]): string[] => {
|
|
|
|
|
|
const keys: string[] = [];
|
|
|
|
|
|
const f = (arr: any[] = []) => {
|
|
|
|
|
|
arr.forEach((item: any) => {
|
|
|
|
|
|
if (item.key) {
|
|
|
|
|
|
keys.push(item.key);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (item.children && item.children.length > 0) {
|
|
|
|
|
|
f(item.children);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
f(data);
|
|
|
|
|
|
return keys;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2026-07-01 08:48:39 +08:00
|
|
|
|
const getRuntimeLegendNames = (
|
|
|
|
|
|
layerKey: string,
|
|
|
|
|
|
nameEn: string
|
|
|
|
|
|
): string[] => {
|
|
|
|
|
|
void layerKey;
|
2026-06-03 15:03:31 +08:00
|
|
|
|
const normalizedNameEn = normalizeLegendNameEn(nameEn);
|
2026-07-01 08:48:39 +08:00
|
|
|
|
if (!normalizedNameEn) {
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (normalizedNameEn.includes('alarm_range_')) {
|
|
|
|
|
|
return [`large_eng_built_${normalizedNameEn}`];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return [normalizedNameEn];
|
2026-06-03 15:03:31 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const syncLegendCheckedInTree = (
|
|
|
|
|
|
items: any[],
|
|
|
|
|
|
nameEn: string,
|
|
|
|
|
|
checked: number
|
|
|
|
|
|
) => {
|
|
|
|
|
|
items.forEach((item: any) => {
|
|
|
|
|
|
if (item.nameEn === nameEn) {
|
|
|
|
|
|
item.checked = checked;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (item.childrenList && item.childrenList.length > 0) {
|
|
|
|
|
|
syncLegendCheckedInTree(item.childrenList, nameEn, checked);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const applyLegendItemVisibility = (legendItem: any, checked: number) => {
|
|
|
|
|
|
if (!legendItem?.nameEn || !legendItem?.layerCode) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const layerKey = legendItem.layerCode;
|
|
|
|
|
|
const layerItem = getLayerConfigByKey(layerKey);
|
|
|
|
|
|
const shouldBeVisible = checked === 1;
|
2026-07-01 08:48:39 +08:00
|
|
|
|
const runtimeLegendNames = getRuntimeLegendNames(
|
|
|
|
|
|
layerKey,
|
|
|
|
|
|
legendItem.nameEn
|
|
|
|
|
|
);
|
2026-06-03 15:03:31 +08:00
|
|
|
|
|
|
|
|
|
|
if (layerItem?.type === 'GISMap') {
|
|
|
|
|
|
mapClass.controlBaseLayerTreeShowAndHidden(
|
|
|
|
|
|
layerKey,
|
|
|
|
|
|
layerItem.config?.id || layerKey,
|
|
|
|
|
|
shouldBeVisible
|
|
|
|
|
|
);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-01 08:48:39 +08:00
|
|
|
|
runtimeLegendNames.forEach(runtimeLegendName => {
|
|
|
|
|
|
mapClass.setLegendPointVisible(
|
|
|
|
|
|
layerKey,
|
|
|
|
|
|
runtimeLegendName,
|
|
|
|
|
|
shouldBeVisible
|
|
|
|
|
|
);
|
|
|
|
|
|
});
|
2026-06-03 15:03:31 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2026-06-01 08:42:06 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 更新图例选中状态
|
|
|
|
|
|
*/
|
|
|
|
|
|
const updateLegendChecked = (nameEn: string, checked: number) => {
|
2026-06-03 15:03:31 +08:00
|
|
|
|
const normalizedNameEn = normalizeLegendNameEn(nameEn);
|
|
|
|
|
|
const legendItem = getLegendConfigByNameEn(normalizedNameEn);
|
|
|
|
|
|
if (!legendItem) {
|
|
|
|
|
|
return;
|
2026-06-01 08:42:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-03 15:03:31 +08:00
|
|
|
|
mapViewStore.setLegendChecked(normalizedNameEn, checked);
|
|
|
|
|
|
syncLegendCheckedInTree(legendData.value, normalizedNameEn, checked);
|
|
|
|
|
|
syncLegendCheckedInTree(
|
|
|
|
|
|
legendDataSelected.value,
|
|
|
|
|
|
normalizedNameEn,
|
|
|
|
|
|
checked
|
|
|
|
|
|
);
|
|
|
|
|
|
rebuildLegendRuntimeData(checkedLayerKeys.value);
|
|
|
|
|
|
applyLegendItemVisibility(legendItem, checked);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const updateLegendCheckedBatch = (nameEns: string[], checked: number) => {
|
|
|
|
|
|
const nextState = { ...legendCheckedState.value };
|
|
|
|
|
|
const changedLegendItems: any[] = [];
|
|
|
|
|
|
|
|
|
|
|
|
nameEns.forEach(nameEn => {
|
|
|
|
|
|
const normalizedNameEn = normalizeLegendNameEn(nameEn);
|
|
|
|
|
|
const legendItem = getLegendConfigByNameEn(normalizedNameEn);
|
|
|
|
|
|
if (!legendItem) return;
|
|
|
|
|
|
|
|
|
|
|
|
nextState[normalizedNameEn] = checked;
|
|
|
|
|
|
changedLegendItems.push(legendItem);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
mapViewStore.setLegendCheckedState(nextState);
|
|
|
|
|
|
rebuildLegendRuntimeData(checkedLayerKeys.value);
|
|
|
|
|
|
changedLegendItems.forEach(legendItem =>
|
|
|
|
|
|
applyLegendItemVisibility(legendItem, checked)
|
|
|
|
|
|
);
|
2026-06-01 08:42:06 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 生成请求标识符,用于避免重复请求
|
|
|
|
|
|
*/
|
|
|
|
|
|
const getRequestIdentifier = (url: string, params: any): string => {
|
2026-06-03 15:03:31 +08:00
|
|
|
|
const paramString = JSON.stringify(normalizeCachePayload(params));
|
2026-06-01 08:42:06 +08:00
|
|
|
|
return `${url}?${encodeURIComponent(paramString)}`;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 加载单个图层数据
|
|
|
|
|
|
*/
|
2026-06-03 15:03:31 +08:00
|
|
|
|
const loadLayerData = async (
|
|
|
|
|
|
layer: any,
|
2026-07-08 08:33:08 +08:00
|
|
|
|
options: {
|
|
|
|
|
|
sessionId?: number;
|
|
|
|
|
|
pageToken?: number;
|
|
|
|
|
|
skipSessionCheck?: boolean;
|
2026-07-09 11:33:04 +08:00
|
|
|
|
pageKey?: string;
|
2026-07-08 08:33:08 +08:00
|
|
|
|
} = {}
|
2026-06-03 15:03:31 +08:00
|
|
|
|
) => {
|
2026-07-08 08:33:08 +08:00
|
|
|
|
const {
|
|
|
|
|
|
sessionId = loadSessionSeed,
|
|
|
|
|
|
pageToken,
|
2026-07-09 11:33:04 +08:00
|
|
|
|
skipSessionCheck = false,
|
|
|
|
|
|
pageKey
|
2026-07-08 08:33:08 +08:00
|
|
|
|
} = options;
|
2026-07-03 15:59:05 +08:00
|
|
|
|
const { key, url, params = {}, paramJson, anchorParamJson } = layer;
|
2026-06-01 08:42:06 +08:00
|
|
|
|
|
|
|
|
|
|
// 没有URL,跳过
|
|
|
|
|
|
if (!url) {
|
2026-06-03 15:03:31 +08:00
|
|
|
|
return [];
|
2026-06-01 08:42:06 +08:00
|
|
|
|
}
|
2026-07-03 15:59:05 +08:00
|
|
|
|
const requestUrl = url;
|
2026-06-01 08:42:06 +08:00
|
|
|
|
let requestParams: any = params;
|
|
|
|
|
|
let requestOrders: any = null;
|
|
|
|
|
|
|
|
|
|
|
|
const layerKey = layer?.key || '';
|
2026-07-03 15:59:05 +08:00
|
|
|
|
|
|
|
|
|
|
const ylfbKeys = ['ylfb_point'];
|
|
|
|
|
|
const timeRangeLayerKeys = ['ef_point'];
|
|
|
|
|
|
|
|
|
|
|
|
const yearTime = dayjs().subtract(1, 'years');
|
|
|
|
|
|
|
|
|
|
|
|
requestParams = normalizeRequestParams(requestParams);
|
|
|
|
|
|
|
|
|
|
|
|
const anchorParamFilters = parseAnchorParamFilters(anchorParamJson);
|
|
|
|
|
|
if (anchorParamFilters.length) {
|
|
|
|
|
|
requestParams.filters.push(...anchorParamFilters);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (timeRangeLayerKeys.includes(layerKey)) {
|
|
|
|
|
|
requestParams.filters.push({
|
|
|
|
|
|
field: 'tm',
|
|
|
|
|
|
operator: 'gte',
|
|
|
|
|
|
dataType: 'date',
|
|
|
|
|
|
value: dayjs(searchTimeRange.value[0]).format('YYYY-MM-DD HH:mm:ss')
|
|
|
|
|
|
});
|
|
|
|
|
|
requestParams.filters.push({
|
|
|
|
|
|
field: 'tm',
|
|
|
|
|
|
operator: 'lte',
|
|
|
|
|
|
dataType: 'date',
|
|
|
|
|
|
value: dayjs(searchTimeRange.value[1]).format('YYYY-MM-DD 23:59:59')
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
if (ylfbKeys.includes(layerKey)) {
|
|
|
|
|
|
if (yearTime) {
|
2026-06-01 08:42:06 +08:00
|
|
|
|
requestParams.filters.push({
|
2026-07-03 15:59:05 +08:00
|
|
|
|
field: 'startTime',
|
|
|
|
|
|
operator: 'eq',
|
2026-06-01 08:42:06 +08:00
|
|
|
|
dataType: 'date',
|
2026-07-03 15:59:05 +08:00
|
|
|
|
value: dayjs(yearTime).startOf('year').format('YYYY-MM-DD 00:00:00')
|
2026-06-01 08:42:06 +08:00
|
|
|
|
});
|
|
|
|
|
|
requestParams.filters.push({
|
2026-07-03 15:59:05 +08:00
|
|
|
|
field: 'endTime',
|
|
|
|
|
|
operator: 'eq',
|
2026-06-01 08:42:06 +08:00
|
|
|
|
dataType: 'date',
|
2026-07-03 15:59:05 +08:00
|
|
|
|
value: dayjs(yearTime).endOf('year').format('YYYY-MM-DD 23:59:59')
|
2026-06-01 08:42:06 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
2026-07-03 15:59:05 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (layer?.orders) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const ordersObj = JSON.parse(layer.orders);
|
|
|
|
|
|
const ordersArray = Object.entries(ordersObj).map(([field, dir]) => ({
|
|
|
|
|
|
field,
|
|
|
|
|
|
dir
|
|
|
|
|
|
}));
|
|
|
|
|
|
requestOrders = ordersArray;
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.error('解析 orders 失败:', e);
|
2026-06-01 08:42:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!Object.keys(requestParams).length && paramJson) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const jsonObj = JSON.parse(paramJson);
|
|
|
|
|
|
if (jsonObj.params) {
|
|
|
|
|
|
requestParams = jsonObj.params;
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.error('解析 paramJson 失败:', e);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-06-03 15:03:31 +08:00
|
|
|
|
const requestData: any = {
|
|
|
|
|
|
filter: requestParams
|
2026-06-01 08:42:06 +08:00
|
|
|
|
};
|
2026-06-03 15:03:31 +08:00
|
|
|
|
if (requestOrders) {
|
|
|
|
|
|
requestData.sort = requestOrders;
|
|
|
|
|
|
}
|
2026-06-01 08:42:06 +08:00
|
|
|
|
|
2026-06-03 15:03:31 +08:00
|
|
|
|
const requestIdentifier = getRequestIdentifier(requestUrl, {
|
|
|
|
|
|
layerKey: key,
|
|
|
|
|
|
requestData
|
|
|
|
|
|
});
|
2026-06-01 08:42:06 +08:00
|
|
|
|
|
2026-07-09 11:33:04 +08:00
|
|
|
|
if (hasEffectivePointLayerCache(key, requestIdentifier)) {
|
|
|
|
|
|
syncBackgroundCacheToReactiveStore(key);
|
|
|
|
|
|
const cachedList = getEffectivePointLayerData(key);
|
|
|
|
|
|
layer.data = cachedList;
|
|
|
|
|
|
if (shouldSyncMergedPointData(key, pageToken)) {
|
|
|
|
|
|
syncPointDataForFilter(getRuntimeCheckedLayerKeys());
|
2026-06-03 15:03:31 +08:00
|
|
|
|
}
|
2026-07-09 11:33:04 +08:00
|
|
|
|
applyLoadedPointLayerToMap(layer, key, cachedList, pageToken);
|
|
|
|
|
|
return cachedList;
|
2026-06-03 15:03:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-07-09 11:33:04 +08:00
|
|
|
|
activeLayerRequestKeyMap.set(key, requestIdentifier);
|
2026-06-03 15:03:31 +08:00
|
|
|
|
const existingRequest = inFlightRequestMap.get(requestIdentifier);
|
|
|
|
|
|
if (existingRequest) {
|
2026-07-09 11:33:04 +08:00
|
|
|
|
mapDataStore.setLayerLoading(key);
|
|
|
|
|
|
return existingRequest.promise.then(list => {
|
|
|
|
|
|
if (Array.isArray(list) && list.length > 0) {
|
|
|
|
|
|
if (shouldSyncMergedPointData(key, pageToken)) {
|
|
|
|
|
|
syncPointDataForFilter(getRuntimeCheckedLayerKeys());
|
|
|
|
|
|
}
|
|
|
|
|
|
applyLoadedPointLayerToMap(layer, key, list, pageToken);
|
|
|
|
|
|
}
|
|
|
|
|
|
return list;
|
|
|
|
|
|
});
|
2026-06-03 15:03:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
mapDataStore.setLayerLoading(key);
|
|
|
|
|
|
|
|
|
|
|
|
const controller = new AbortController();
|
|
|
|
|
|
|
|
|
|
|
|
const requestPromise = (async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await request({
|
2026-07-09 11:33:04 +08:00
|
|
|
|
url: requestUrl,
|
2026-06-03 15:03:31 +08:00
|
|
|
|
method: 'post',
|
|
|
|
|
|
data: requestData,
|
|
|
|
|
|
signal: controller.signal
|
|
|
|
|
|
});
|
|
|
|
|
|
const resData = response?.data || response?.data?.data || [];
|
|
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
|
controller.signal.aborted ||
|
2026-07-09 11:33:04 +08:00
|
|
|
|
(!skipSessionCheck && !isCurrentLoadSession(sessionId))
|
2026-06-03 15:03:31 +08:00
|
|
|
|
) {
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let list: any[] = [];
|
|
|
|
|
|
if (resData) {
|
|
|
|
|
|
if (Array.isArray(resData)) {
|
|
|
|
|
|
list = resData;
|
|
|
|
|
|
} else if (Array.isArray(resData.data)) {
|
|
|
|
|
|
list = resData.data;
|
|
|
|
|
|
} else if (resData.data && Array.isArray(resData.data.data)) {
|
|
|
|
|
|
list = resData.data.data;
|
|
|
|
|
|
} else if (
|
|
|
|
|
|
resData.data &&
|
|
|
|
|
|
resData.data.data &&
|
|
|
|
|
|
Array.isArray(resData.data.data.data)
|
|
|
|
|
|
) {
|
|
|
|
|
|
list = resData.data.data.data;
|
2026-06-01 08:42:06 +08:00
|
|
|
|
}
|
2026-06-03 15:03:31 +08:00
|
|
|
|
}
|
2026-06-01 08:42:06 +08:00
|
|
|
|
|
2026-07-08 08:33:08 +08:00
|
|
|
|
list = normalizePointLayerItems(
|
|
|
|
|
|
list.map((item: any) => ({
|
|
|
|
|
|
...item,
|
|
|
|
|
|
layerKey: item.layerKey || key
|
|
|
|
|
|
}))
|
2026-06-03 15:03:31 +08:00
|
|
|
|
);
|
|
|
|
|
|
|
2026-07-09 11:33:04 +08:00
|
|
|
|
if (activeLayerRequestKeyMap.get(key) !== requestIdentifier) {
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-03 15:03:31 +08:00
|
|
|
|
const isLayerChecked = layer.checked === 1;
|
2026-07-09 11:33:04 +08:00
|
|
|
|
const cachePayload = {
|
2026-06-03 15:03:31 +08:00
|
|
|
|
checked: isLayerChecked,
|
|
|
|
|
|
data: list,
|
|
|
|
|
|
cacheKey: requestIdentifier
|
2026-07-09 11:33:04 +08:00
|
|
|
|
};
|
|
|
|
|
|
const shouldDeferToBackgroundCache =
|
|
|
|
|
|
!!pageKey && hasPendingPageNavigationAwayFrom(pageKey);
|
2026-06-03 15:03:31 +08:00
|
|
|
|
|
2026-07-09 11:33:04 +08:00
|
|
|
|
if (shouldDeferToBackgroundCache) {
|
|
|
|
|
|
setBackgroundPointLayerCache(key, cachePayload);
|
|
|
|
|
|
layer.data = list;
|
|
|
|
|
|
return list;
|
|
|
|
|
|
}
|
2026-06-03 15:03:31 +08:00
|
|
|
|
|
2026-07-09 11:33:04 +08:00
|
|
|
|
mapDataStore.setPointLayerCache(key, cachePayload);
|
|
|
|
|
|
if (shouldSyncMergedPointData(key, pageToken)) {
|
|
|
|
|
|
syncPointDataForFilter(getRuntimeCheckedLayerKeys());
|
2026-06-03 15:03:31 +08:00
|
|
|
|
}
|
2026-07-09 11:33:04 +08:00
|
|
|
|
applyLoadedPointLayerToMap(layer, key, list, pageToken);
|
2026-06-01 08:42:06 +08:00
|
|
|
|
|
2026-06-03 15:03:31 +08:00
|
|
|
|
mapDataStore.setLayerLoaded(key);
|
|
|
|
|
|
return list;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
if (
|
|
|
|
|
|
controller.signal.aborted ||
|
|
|
|
|
|
isCanceledRequestError(error) ||
|
2026-07-08 08:33:08 +08:00
|
|
|
|
(!skipSessionCheck && !isCurrentLoadSession(sessionId)) ||
|
2026-06-03 15:03:31 +08:00
|
|
|
|
activeLayerRequestKeyMap.get(key) !== requestIdentifier
|
|
|
|
|
|
) {
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
2026-06-01 08:42:06 +08:00
|
|
|
|
|
2026-06-03 15:03:31 +08:00
|
|
|
|
console.error(`加载描点数据失败 [${key}]:`, error);
|
|
|
|
|
|
mapDataStore.setLayerLoadError(key, error);
|
|
|
|
|
|
throw error;
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
const activeRequest = inFlightRequestMap.get(requestIdentifier);
|
|
|
|
|
|
if (activeRequest?.controller === controller) {
|
|
|
|
|
|
inFlightRequestMap.delete(requestIdentifier);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (activeLayerRequestKeyMap.get(key) === requestIdentifier) {
|
|
|
|
|
|
activeLayerRequestKeyMap.delete(key);
|
2026-06-01 08:42:06 +08:00
|
|
|
|
}
|
2026-06-03 15:03:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
})();
|
|
|
|
|
|
|
|
|
|
|
|
inFlightRequestMap.set(requestIdentifier, {
|
|
|
|
|
|
promise: requestPromise,
|
|
|
|
|
|
controller,
|
|
|
|
|
|
layerKey: key,
|
|
|
|
|
|
sessionId
|
2026-06-01 08:42:06 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
2026-06-03 15:03:31 +08:00
|
|
|
|
return requestPromise;
|
2026-06-01 08:42:06 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取选中的图层 key
|
|
|
|
|
|
*/
|
|
|
|
|
|
const getCheckedKeys = (): string[] => {
|
2026-06-03 15:03:31 +08:00
|
|
|
|
return mapViewStore.getCheckedLayerKeys();
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 根据当前搜索时间范围重新加载描点数据
|
|
|
|
|
|
*/
|
|
|
|
|
|
const reloadBySearchTimeRange = async () => {
|
|
|
|
|
|
mapDataStore.setLoading(true);
|
|
|
|
|
|
const currentSessionId = startLoadSession();
|
|
|
|
|
|
|
|
|
|
|
|
// 只处理和 searchTimeRange 相关的图层
|
|
|
|
|
|
const timeRangeLayerKeys = ['ef_point'];
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
for (const key of timeRangeLayerKeys) {
|
|
|
|
|
|
// 如果描点图层存在于地图中,先删除旧图层
|
|
|
|
|
|
mapClass.removePointLayer(key);
|
|
|
|
|
|
|
|
|
|
|
|
// 清空缓存
|
|
|
|
|
|
if (mapDataStore.getPointLayerCache(key)) {
|
|
|
|
|
|
mapDataStore.removePointLayerCache(key);
|
|
|
|
|
|
mapDataStore.clearLayerLoadState(key);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 重新加载需要时间搜索的图层数据
|
|
|
|
|
|
if (layerData.value.length > 0) {
|
|
|
|
|
|
const currentCheckedKeys = getCheckedKeys();
|
|
|
|
|
|
|
|
|
|
|
|
for (const key of timeRangeLayerKeys) {
|
|
|
|
|
|
const layerItem = findLayerByKey(layerData.value, key);
|
|
|
|
|
|
if (layerItem && currentCheckedKeys.includes(key)) {
|
2026-07-08 08:33:08 +08:00
|
|
|
|
await loadLayerData(layerItem, { sessionId: currentSessionId });
|
2026-06-03 15:03:31 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!isCurrentLoadSession(currentSessionId)) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const allPointData = mapDataStore.rebuildPointDataFromCache();
|
|
|
|
|
|
// 更新地图锚点显示
|
|
|
|
|
|
await updateLayerData(currentCheckedKeys, false);
|
|
|
|
|
|
}
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
mapDataStore.setLoading(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 更新搜索时间范围并重新加载描点数据
|
|
|
|
|
|
*/
|
|
|
|
|
|
const updateSearchTimeRange = async (newRange: [any, any]) => {
|
|
|
|
|
|
mapViewStore.setSearchTimeRange(newRange);
|
|
|
|
|
|
await reloadBySearchTimeRange();
|
2026-04-03 17:04:34 +08:00
|
|
|
|
};
|
2026-04-03 16:04:16 +08:00
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
|
layerData,
|
2026-04-22 17:53:20 +08:00
|
|
|
|
setLayerData,
|
2026-06-01 08:42:06 +08:00
|
|
|
|
loadAllLayerData,
|
2026-07-08 08:33:08 +08:00
|
|
|
|
loadCurrentPageLayerData,
|
2026-06-01 08:42:06 +08:00
|
|
|
|
loadLayerData,
|
|
|
|
|
|
setSelectedLegendData,
|
2026-04-03 16:04:16 +08:00
|
|
|
|
legendData,
|
2026-04-22 17:53:20 +08:00
|
|
|
|
setLegendData,
|
2026-04-03 16:04:16 +08:00
|
|
|
|
legendDataSelected,
|
2026-06-01 08:42:06 +08:00
|
|
|
|
pointData,
|
|
|
|
|
|
setPointData,
|
|
|
|
|
|
checkedLayerKeys,
|
|
|
|
|
|
updateLayerData,
|
|
|
|
|
|
updateLegendChecked,
|
2026-06-03 15:03:31 +08:00
|
|
|
|
updateLegendCheckedBatch,
|
2026-07-08 08:33:08 +08:00
|
|
|
|
replacePointLayerData,
|
2026-06-01 08:42:06 +08:00
|
|
|
|
getCheckedKeys,
|
|
|
|
|
|
legendData2Obj,
|
|
|
|
|
|
loading,
|
|
|
|
|
|
pointDataCache,
|
2026-06-03 15:03:31 +08:00
|
|
|
|
layerLoadState,
|
|
|
|
|
|
findLayerByKey,
|
|
|
|
|
|
getLegendItemsByLayerCode,
|
|
|
|
|
|
getLayerBranchKeys,
|
|
|
|
|
|
normalizeCheckedLayerKeys,
|
2026-07-01 08:48:39 +08:00
|
|
|
|
refreshPointLayerDisplayData,
|
2026-07-08 08:33:08 +08:00
|
|
|
|
activatePageContext,
|
2026-07-09 11:33:04 +08:00
|
|
|
|
markPendingPageNavigation,
|
2026-06-03 15:03:31 +08:00
|
|
|
|
searchTimeRange,
|
|
|
|
|
|
reloadBySearchTimeRange,
|
|
|
|
|
|
updateSearchTimeRange
|
2026-04-03 16:04:16 +08:00
|
|
|
|
};
|
|
|
|
|
|
});
|