1459 lines
42 KiB
TypeScript
1459 lines
42 KiB
TypeScript
import { defineStore, storeToRefs } from 'pinia';
|
||
import { ref } from 'vue';
|
||
import dayjs from 'dayjs';
|
||
import { MapClass } from '@/components/gis/map.class';
|
||
import { getMapConfig } from '@/components/gis/gisUtils';
|
||
import { applyLayerMutualExclusionRules } from '@/modules/map/domain/map-layer-rules';
|
||
import {
|
||
applyEnvFacilityLegendRule,
|
||
buildLegendCheckedState,
|
||
buildLegendTree
|
||
} from '@/modules/map/domain/legend-deriver';
|
||
import { attachNearbyPointRuntimeMeta } from '@/modules/map/domain/nearby-point-rules';
|
||
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';
|
||
import request from '@/utils/request';
|
||
const mapClass = MapClass.getInstance();
|
||
const ENG_POINT_LAYER_KEY = 'eng_point';
|
||
const ENG_ALARM_POINT_LAYER_KEY = 'eng_alarm_point';
|
||
const YLFB_POINT_LAYER_KEY = 'ylfb_point';
|
||
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'
|
||
];
|
||
|
||
const isEngPointRuntimeItem = (item: any): boolean => {
|
||
const runtimeLayerKey = String(
|
||
item?.layerKey || item?.type || item?._layerKey || ''
|
||
).toLowerCase();
|
||
const sttpMap = String(
|
||
item?._sttpMap || item?.sttpMap || item?.popupSttpMap || ''
|
||
).toUpperCase();
|
||
const sttpCode = String(item?.sttpCode || item?.sttp || '').toUpperCase();
|
||
|
||
if (runtimeLayerKey) {
|
||
return runtimeLayerKey.includes(ENG_POINT_LAYER_KEY);
|
||
}
|
||
|
||
return sttpMap === 'ENG' || sttpMap === 'ENG2' || sttpCode === 'ENG';
|
||
};
|
||
|
||
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')
|
||
);
|
||
};
|
||
|
||
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 [];
|
||
}
|
||
};
|
||
|
||
export const useMapStore = defineStore('map', () => {
|
||
const mapConfigStore = useMapConfigStore();
|
||
const mapDataStore = useMapDataStore();
|
||
const mapViewStore = useMapViewStore();
|
||
const {
|
||
getLayerConfigByKey,
|
||
getLegendConfigByLayerCode,
|
||
getLegendConfigByNameEn
|
||
} = mapConfigStore;
|
||
const { hasPointLayerData, getPointLayerData } = mapDataStore;
|
||
const {
|
||
layerConfigTree: layerData,
|
||
legendConfigOriginal: legendDataOriginal,
|
||
pageLegendConfig
|
||
} = storeToRefs(mapConfigStore);
|
||
const { checkedLayerKeys, legendCheckedState, searchTimeRange } =
|
||
storeToRefs(mapViewStore);
|
||
const { pointData, pointDataCache, layerLoadState, loading } =
|
||
storeToRefs(mapDataStore);
|
||
|
||
// 图例数据(经过筛选和排序的)
|
||
const legendData = ref<any[]>([]);
|
||
// 选中的图例数据
|
||
const legendDataSelected = ref<any[]>([]);
|
||
const inFlightRequestMap = new Map<
|
||
string,
|
||
{
|
||
promise: Promise<any[]>;
|
||
controller: AbortController;
|
||
layerKey: string;
|
||
sessionId: number;
|
||
}
|
||
>();
|
||
const activeLayerRequestKeyMap = new Map<string, string>();
|
||
const backgroundPointLayerCache = new Map<
|
||
string,
|
||
{
|
||
checked: boolean;
|
||
data: any[];
|
||
cacheKey?: string;
|
||
}
|
||
>();
|
||
let loadSessionSeed = 0;
|
||
let activePageRenderToken = 0;
|
||
let activePageKey = '';
|
||
let activePageLayerKeys = new Set<string>();
|
||
let activeLoadingPageToken: number | null = null;
|
||
let pendingPageNavigationKey = '';
|
||
|
||
const normalizeLegendNameEn = mapConfigStore.normalizeLegendNameEn;
|
||
|
||
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) => {
|
||
if (!isEngPointRuntimeItem(item)) {
|
||
return true;
|
||
}
|
||
|
||
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);
|
||
syncBackgroundCacheToReactiveStore(layerKey);
|
||
const rawData =
|
||
Array.isArray(layerItem?.data) && layerItem.data.length > 0
|
||
? layerItem.data
|
||
: getEffectivePointLayerData(layerKey);
|
||
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);
|
||
};
|
||
|
||
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);
|
||
};
|
||
|
||
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;
|
||
};
|
||
|
||
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
|
||
});
|
||
};
|
||
|
||
const getRuntimeCheckedLayerKeys = (): string[] => {
|
||
return mapViewStore.getCheckedLayerKeys();
|
||
};
|
||
|
||
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);
|
||
};
|
||
|
||
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'))
|
||
);
|
||
};
|
||
|
||
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;
|
||
};
|
||
|
||
/**
|
||
* 设置图层数据
|
||
*/
|
||
const setLayerData = (data: any[]) => {
|
||
mapConfigStore.setLayerConfigTree(data);
|
||
const nextCheckedLayerKeys = mapConfigStore.extractCheckedLayerKeys(data);
|
||
mapViewStore.setCheckedLayerKeys(nextCheckedLayerKeys);
|
||
rebuildLegendRuntimeData(nextCheckedLayerKeys);
|
||
};
|
||
|
||
/**
|
||
* 按照 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));
|
||
};
|
||
|
||
/**
|
||
* 启动新的图层加载会话,供需要严格按最新查询条件收口的场景复用。
|
||
*/
|
||
const startLoadSession = () => {
|
||
loadSessionSeed += 1;
|
||
return loadSessionSeed;
|
||
};
|
||
|
||
/**
|
||
* 判断当前请求是否仍属于最新加载会话,避免旧请求结果回写到最新页面。
|
||
*/
|
||
const isCurrentLoadSession = (sessionId: number) => {
|
||
return loadSessionSeed === sessionId;
|
||
};
|
||
|
||
const activatePageContext = (pageKey: string, items: any[] = []) => {
|
||
activePageKey = pageKey;
|
||
pendingPageNavigationKey = '';
|
||
activePageRenderToken += 1;
|
||
activePageLayerKeys = new Set(getAllLayerKeys(items));
|
||
return activePageRenderToken;
|
||
};
|
||
|
||
const markPendingPageNavigation = (targetPageKey: string) => {
|
||
pendingPageNavigationKey = targetPageKey || '__route-change__';
|
||
};
|
||
|
||
const hasPendingPageNavigationAwayFrom = (pageKey: string) => {
|
||
return !!pendingPageNavigationKey && pendingPageNavigationKey !== pageKey;
|
||
};
|
||
|
||
const shouldApplyToActivePage = (layerKey: string, pageToken?: number) => {
|
||
if (!layerKey) return false;
|
||
if (activePageKey && hasPendingPageNavigationAwayFrom(activePageKey)) {
|
||
return false;
|
||
}
|
||
if (pageToken === undefined) return true;
|
||
return (
|
||
activePageRenderToken === pageToken && activePageLayerKeys.has(layerKey)
|
||
);
|
||
};
|
||
|
||
const isActivePageToken = (pageToken?: number) => {
|
||
if (pageToken === undefined) return true;
|
||
return activePageRenderToken === pageToken;
|
||
};
|
||
|
||
const beginPageLoading = (pageToken?: number) => {
|
||
if (typeof pageToken === 'number') {
|
||
activeLoadingPageToken = pageToken;
|
||
}
|
||
mapDataStore.setLoading(true);
|
||
};
|
||
|
||
const finishPageLoading = (pageToken?: number) => {
|
||
if (typeof pageToken !== 'number') {
|
||
mapDataStore.setLoading(false);
|
||
return;
|
||
}
|
||
|
||
if (
|
||
activeLoadingPageToken === pageToken &&
|
||
activePageRenderToken === pageToken
|
||
) {
|
||
activeLoadingPageToken = null;
|
||
mapDataStore.setLoading(false);
|
||
}
|
||
};
|
||
|
||
/**
|
||
* 设置图例数据(过滤掉 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);
|
||
};
|
||
|
||
/**
|
||
* 设置选中的图例数据(在图层加载完成后调用)
|
||
*/
|
||
const setSelectedLegendData = () => {
|
||
rebuildLegendRuntimeData(checkedLayerKeys.value);
|
||
};
|
||
|
||
/**
|
||
* 设置描点数据
|
||
*/
|
||
const setPointData = (data: any[]) => {
|
||
mapDataStore.setPointData(data);
|
||
};
|
||
|
||
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);
|
||
};
|
||
|
||
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);
|
||
};
|
||
|
||
/**
|
||
* 图例数据转对象
|
||
*/
|
||
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 {
|
||
const normalizedNameEn = normalizeLegendNameEn(item.nameEn);
|
||
if (normalizedNameEn) {
|
||
_tempData[normalizedNameEn] = item;
|
||
}
|
||
}
|
||
});
|
||
};
|
||
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);
|
||
}
|
||
if (mapDataStore.getPointLayerCache(key)) {
|
||
mapDataStore.setPointLayerCacheChecked(key, false);
|
||
}
|
||
if (layer) {
|
||
layer.checked = 0;
|
||
}
|
||
return;
|
||
}
|
||
|
||
// 使用初始化时已加载的数据(存入 layer.data)
|
||
const cachedData =
|
||
Array.isArray(layer?.data) && layer.data.length > 0
|
||
? layer.data
|
||
: getPointLayerData(key);
|
||
if (cachedData && cachedData.length > 0) {
|
||
layer.checked = 1;
|
||
const displayData = filterPointLayerDataForDisplay(key, cachedData);
|
||
mapClass.addInitDataLayer(displayData, key);
|
||
restoreLayerLegendVisibility(key);
|
||
if (mapDataStore.getPointLayerCache(key)) {
|
||
mapDataStore.setPointLayerCacheChecked(key, true);
|
||
}
|
||
} else {
|
||
console.warn(`图层 ${key} 没有缓存数据`);
|
||
}
|
||
};
|
||
|
||
/**
|
||
* 更新图层数据(联动图例和描点)
|
||
* @param checkKeys - 选中的图层 key 列表
|
||
* @param isInit - 是否是初始化阶段(初始化时遍历所有图层)
|
||
*/
|
||
const updateLayerData = async (checkKeys: string[], isInit = false) => {
|
||
if (!isInit) {
|
||
checkKeys = normalizeCheckedLayerKeys(checkKeys);
|
||
}
|
||
|
||
mapViewStore.setCheckedLayerKeys(checkKeys);
|
||
|
||
// 收集变化的图层 key(新增选中 / 取消选中)
|
||
const newlyChecked: string[] = [];
|
||
const newlyUnchecked: string[] = [];
|
||
|
||
// 更新图层的 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);
|
||
|
||
rebuildLegendRuntimeData(checkKeys);
|
||
|
||
// 控制锚点显示隐藏:初始化时处理全部,否则只处理变化的图层
|
||
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) {
|
||
restoreLayerLegendVisibility(key);
|
||
mapClass.mdLayerTreeShowOrHidden(key, true);
|
||
}
|
||
} else if (layerItem && layerItem.type === 'GISMap') {
|
||
// 处理 GISMap 类型的图层:勾选时若地图实例中不存在,则先重新加载再控制显隐。
|
||
if (key && key !== '-' && key !== 'powerBaseStation') {
|
||
const shouldBeVisible = checkKeys.includes(key);
|
||
const layerConfig = ensureGISLayerConfig(layerItem);
|
||
if (
|
||
shouldBeVisible &&
|
||
layerConfig &&
|
||
!mapClass.hasBaseLayer(layerItem.key)
|
||
) {
|
||
mapClass.addBaseDataLayer(layerConfig, true);
|
||
}
|
||
mapClass.controlBaseLayerTreeShowAndHidden(
|
||
key,
|
||
layerItem.key,
|
||
shouldBeVisible
|
||
);
|
||
}
|
||
}
|
||
}
|
||
};
|
||
|
||
/**
|
||
* 加载所有包含URL的图层数据
|
||
* @param items 图层数据
|
||
* @param checkedKeys 选中的图层 keys(用于优先加载)
|
||
*/
|
||
const loadAllLayerData = async (
|
||
items: any[],
|
||
checkedKeys: string[] = [],
|
||
options: {
|
||
pageToken?: number;
|
||
skipSessionCheck?: boolean;
|
||
pageKey?: string;
|
||
} = {}
|
||
) => {
|
||
beginPageLoading(options.pageToken);
|
||
const currentSessionId = options.skipSessionCheck
|
||
? undefined
|
||
: startLoadSession();
|
||
const checkedTasks: Array<() => Promise<any>> = [];
|
||
const uncheckedTasks: Array<() => Promise<any>> = [];
|
||
const processedKeys = new Set<string>();
|
||
const debugStart = Date.now();
|
||
|
||
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);
|
||
const task = () =>
|
||
loadLayerData(item, {
|
||
sessionId: currentSessionId,
|
||
pageToken: options.pageToken,
|
||
skipSessionCheck: options.skipSessionCheck,
|
||
pageKey: options.pageKey
|
||
});
|
||
if (checkedKeys.includes(item.key)) {
|
||
checkedTasks.push(task);
|
||
} else {
|
||
uncheckedTasks.push(task);
|
||
}
|
||
}
|
||
|
||
if (item.children && item.children.length > 0) {
|
||
processItems(item.children);
|
||
}
|
||
});
|
||
};
|
||
|
||
processItems(items);
|
||
|
||
try {
|
||
// 首页初始化:所有图层接口一次性并发下发,避免分批串行等待。
|
||
const loadResults = await Promise.allSettled(
|
||
[...checkedTasks, ...uncheckedTasks].map(task => task())
|
||
);
|
||
const failedResults = loadResults.filter(
|
||
result => result.status === 'rejected'
|
||
);
|
||
|
||
if (failedResults.length > 0) {
|
||
console.warn(
|
||
'部分图层数据加载失败,但不会阻断其他图层:',
|
||
failedResults
|
||
);
|
||
}
|
||
|
||
if (
|
||
(typeof currentSessionId === 'number' &&
|
||
!isCurrentLoadSession(currentSessionId)) ||
|
||
!isActivePageToken(options.pageToken)
|
||
) {
|
||
return;
|
||
}
|
||
|
||
if (
|
||
options.pageKey &&
|
||
hasPendingPageNavigationAwayFrom(options.pageKey)
|
||
) {
|
||
return;
|
||
}
|
||
|
||
// 确保所有图层的数据都已经缓存到 pointDataCache 和 layer.data
|
||
const allLayerKeys = getAllLayerKeys(items);
|
||
for (const key of allLayerKeys) {
|
||
const layer = findLayerByKey(items, key);
|
||
if (layer && getEffectivePointLayerData(key).length > 0) {
|
||
syncBackgroundCacheToReactiveStore(key);
|
||
layer.data = getEffectivePointLayerData(key);
|
||
}
|
||
}
|
||
|
||
const allPointData = mapDataStore.rebuildPointDataFromCache(allLayerKeys);
|
||
attachNearbyPointRuntimeMeta(allPointData);
|
||
const runtimeCheckedKeys = getRuntimeCheckedLayerKeys();
|
||
const treeCheckedKeys = mapConfigStore.extractCheckedLayerKeys(
|
||
layerData.value.length > 0 ? layerData.value : items
|
||
);
|
||
const finalCheckedKeys =
|
||
runtimeCheckedKeys.length > 0 ? runtimeCheckedKeys : treeCheckedKeys;
|
||
|
||
// 备注:近邻点元数据需要基于全量点位统一识别,识别完成后同步刷新各图层 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
|
||
: layer.checked === 1 || finalCheckedKeys.includes(key);
|
||
layer.data = layerPoints;
|
||
mapClass.addInitDataLayer(displayData, key);
|
||
if (shouldRestoreVisible) {
|
||
restoreLayerLegendVisibility(key);
|
||
}
|
||
mapClass.mdLayerTreeShowOrHidden(key, shouldRestoreVisible);
|
||
}
|
||
|
||
// 设置选中的图例数据(确保图例在所有锚点加载完成后才显示)
|
||
setSelectedLegendData();
|
||
|
||
// 备注:初始化 loading 期间用户可能已经手动改过图层勾选,这里必须以最新运行态收尾,
|
||
// 不能再回放 load 启动瞬间的默认 checked 快照。
|
||
await updateLayerData(finalCheckedKeys, true);
|
||
} finally {
|
||
const shouldFinishLoading =
|
||
(typeof currentSessionId !== 'number' ||
|
||
isCurrentLoadSession(currentSessionId)) &&
|
||
(options.pageToken === undefined ||
|
||
activePageRenderToken === options.pageToken);
|
||
if (shouldFinishLoading) {
|
||
finishPageLoading(options.pageToken);
|
||
}
|
||
}
|
||
};
|
||
|
||
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) {
|
||
finishPageLoading(options.pageToken);
|
||
return;
|
||
}
|
||
|
||
beginPageLoading(options.pageToken);
|
||
|
||
try {
|
||
// 菜单切换:仅当前页的勾选图层一次性并发下发,旧请求在后台继续运行。
|
||
await Promise.allSettled(tasks.map(task => task()));
|
||
} finally {
|
||
if (isActivePageToken(options.pageToken)) {
|
||
finishPageLoading(options.pageToken);
|
||
}
|
||
}
|
||
};
|
||
|
||
/**
|
||
* 根据 key 查找图层
|
||
*/
|
||
const findLayerByKey = (items: any[], key: string): any | null => {
|
||
void items;
|
||
if (!key) return null;
|
||
return getLayerConfigByKey(key) || null;
|
||
};
|
||
|
||
/**
|
||
* 获取所有图层 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;
|
||
};
|
||
|
||
const getRuntimeLegendNames = (
|
||
layerKey: string,
|
||
nameEn: string
|
||
): string[] => {
|
||
const normalizedNameEn = normalizeLegendNameEn(nameEn);
|
||
if (!normalizedNameEn) {
|
||
return [];
|
||
}
|
||
|
||
if (normalizedNameEn.includes('alarm_range_')) {
|
||
const candidates: string[] = [];
|
||
if (layerKey === ENG_ALARM_POINT_LAYER_KEY) {
|
||
candidates.push(`mid_eng_built_${normalizedNameEn}`);
|
||
candidates.push(`large_eng_built_${normalizedNameEn}`);
|
||
} else {
|
||
candidates.push(`large_eng_built_${normalizedNameEn}`);
|
||
candidates.push(`mid_eng_built_${normalizedNameEn}`);
|
||
}
|
||
return candidates;
|
||
}
|
||
|
||
return [normalizedNameEn];
|
||
};
|
||
|
||
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;
|
||
const runtimeLegendNames = getRuntimeLegendNames(
|
||
layerKey,
|
||
legendItem.nameEn
|
||
);
|
||
|
||
if (layerItem?.type === 'GISMap') {
|
||
mapClass.controlBaseLayerTreeShowAndHidden(
|
||
layerKey,
|
||
layerItem.config?.id || layerKey,
|
||
shouldBeVisible
|
||
);
|
||
return;
|
||
}
|
||
|
||
runtimeLegendNames.forEach(runtimeLegendName => {
|
||
mapClass.setLegendPointVisible(
|
||
layerKey,
|
||
runtimeLegendName,
|
||
shouldBeVisible
|
||
);
|
||
});
|
||
};
|
||
|
||
/**
|
||
* 更新图例选中状态
|
||
*/
|
||
const updateLegendChecked = (nameEn: string, checked: number) => {
|
||
const normalizedNameEn = normalizeLegendNameEn(nameEn);
|
||
const legendItem = getLegendConfigByNameEn(normalizedNameEn);
|
||
if (!legendItem) {
|
||
return;
|
||
}
|
||
|
||
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)
|
||
);
|
||
};
|
||
|
||
/**
|
||
* 生成请求标识符,用于避免重复请求
|
||
*/
|
||
const getRequestIdentifier = (url: string, params: any): string => {
|
||
const paramString = JSON.stringify(normalizeCachePayload(params));
|
||
return `${url}?${encodeURIComponent(paramString)}`;
|
||
};
|
||
|
||
/**
|
||
* 加载单个图层数据
|
||
*/
|
||
const loadLayerData = async (
|
||
layer: any,
|
||
options: {
|
||
sessionId?: number;
|
||
pageToken?: number;
|
||
skipSessionCheck?: boolean;
|
||
pageKey?: string;
|
||
} = {}
|
||
) => {
|
||
const {
|
||
sessionId = loadSessionSeed,
|
||
pageToken,
|
||
skipSessionCheck = false,
|
||
pageKey
|
||
} = options;
|
||
const { key, url, params = {}, paramJson, anchorParamJson } = layer;
|
||
|
||
// 没有URL,跳过
|
||
if (!url) {
|
||
return [];
|
||
}
|
||
const requestUrl = url;
|
||
let requestParams: any = params;
|
||
let requestOrders: any = null;
|
||
|
||
const layerKey = layer?.key || '';
|
||
|
||
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) {
|
||
requestParams.filters.push({
|
||
field: 'startTime',
|
||
operator: 'eq',
|
||
dataType: 'date',
|
||
value: dayjs(yearTime).startOf('year').format('YYYY-MM-DD 00:00:00')
|
||
});
|
||
requestParams.filters.push({
|
||
field: 'endTime',
|
||
operator: 'eq',
|
||
dataType: 'date',
|
||
value: dayjs(yearTime).endOf('year').format('YYYY-MM-DD 23:59:59')
|
||
});
|
||
}
|
||
}
|
||
|
||
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);
|
||
}
|
||
}
|
||
|
||
if (!Object.keys(requestParams).length && paramJson) {
|
||
try {
|
||
const jsonObj = JSON.parse(paramJson);
|
||
if (jsonObj.params) {
|
||
requestParams = jsonObj.params;
|
||
}
|
||
} catch (e) {
|
||
console.error('解析 paramJson 失败:', e);
|
||
}
|
||
}
|
||
const requestData: any = {
|
||
filter: requestParams
|
||
};
|
||
if (requestOrders) {
|
||
requestData.sort = requestOrders;
|
||
}
|
||
|
||
const requestIdentifier = getRequestIdentifier(requestUrl, {
|
||
layerKey: key,
|
||
requestData
|
||
});
|
||
|
||
if (hasEffectivePointLayerCache(key, requestIdentifier)) {
|
||
syncBackgroundCacheToReactiveStore(key);
|
||
const cachedList = getEffectivePointLayerData(key);
|
||
layer.data = cachedList;
|
||
if (shouldSyncMergedPointData(key, pageToken)) {
|
||
syncPointDataForFilter(getRuntimeCheckedLayerKeys());
|
||
}
|
||
applyLoadedPointLayerToMap(layer, key, cachedList, pageToken);
|
||
return cachedList;
|
||
}
|
||
|
||
activeLayerRequestKeyMap.set(key, requestIdentifier);
|
||
const existingRequest = inFlightRequestMap.get(requestIdentifier);
|
||
if (existingRequest) {
|
||
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;
|
||
});
|
||
}
|
||
|
||
mapDataStore.setLayerLoading(key);
|
||
|
||
const controller = new AbortController();
|
||
|
||
const requestPromise = (async () => {
|
||
try {
|
||
const response = await request({
|
||
url: requestUrl,
|
||
method: 'post',
|
||
data: requestData,
|
||
signal: controller.signal
|
||
});
|
||
const resData = response?.data || response?.data?.data || [];
|
||
|
||
if (
|
||
controller.signal.aborted ||
|
||
(!skipSessionCheck && !isCurrentLoadSession(sessionId))
|
||
) {
|
||
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;
|
||
}
|
||
}
|
||
|
||
list = normalizePointLayerItems(
|
||
list.map((item: any) => ({
|
||
...item,
|
||
layerKey: item.layerKey || key
|
||
}))
|
||
);
|
||
|
||
if (activeLayerRequestKeyMap.get(key) !== requestIdentifier) {
|
||
return [];
|
||
}
|
||
|
||
const isLayerChecked = layer.checked === 1;
|
||
const cachePayload = {
|
||
checked: isLayerChecked,
|
||
data: list,
|
||
cacheKey: requestIdentifier
|
||
};
|
||
const shouldDeferToBackgroundCache =
|
||
!!pageKey && hasPendingPageNavigationAwayFrom(pageKey);
|
||
|
||
if (shouldDeferToBackgroundCache) {
|
||
setBackgroundPointLayerCache(key, cachePayload);
|
||
layer.data = list;
|
||
return list;
|
||
}
|
||
|
||
mapDataStore.setPointLayerCache(key, cachePayload);
|
||
if (shouldSyncMergedPointData(key, pageToken)) {
|
||
syncPointDataForFilter(getRuntimeCheckedLayerKeys());
|
||
}
|
||
applyLoadedPointLayerToMap(layer, key, list, pageToken);
|
||
|
||
mapDataStore.setLayerLoaded(key);
|
||
return list;
|
||
} catch (error) {
|
||
if (
|
||
controller.signal.aborted ||
|
||
isCanceledRequestError(error) ||
|
||
(!skipSessionCheck && !isCurrentLoadSession(sessionId)) ||
|
||
activeLayerRequestKeyMap.get(key) !== requestIdentifier
|
||
) {
|
||
return [];
|
||
}
|
||
|
||
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);
|
||
}
|
||
}
|
||
})();
|
||
|
||
inFlightRequestMap.set(requestIdentifier, {
|
||
promise: requestPromise,
|
||
controller,
|
||
layerKey: key,
|
||
sessionId
|
||
});
|
||
|
||
return requestPromise;
|
||
};
|
||
|
||
/**
|
||
* 获取选中的图层 key
|
||
*/
|
||
const getCheckedKeys = (): string[] => {
|
||
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)) {
|
||
await loadLayerData(layerItem, { sessionId: currentSessionId });
|
||
}
|
||
}
|
||
|
||
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();
|
||
};
|
||
|
||
return {
|
||
layerData,
|
||
setLayerData,
|
||
loadAllLayerData,
|
||
loadCurrentPageLayerData,
|
||
loadLayerData,
|
||
setSelectedLegendData,
|
||
legendData,
|
||
setLegendData,
|
||
legendDataSelected,
|
||
pointData,
|
||
setPointData,
|
||
checkedLayerKeys,
|
||
updateLayerData,
|
||
updateLegendChecked,
|
||
updateLegendCheckedBatch,
|
||
replacePointLayerData,
|
||
getCheckedKeys,
|
||
legendData2Obj,
|
||
loading,
|
||
pointDataCache,
|
||
layerLoadState,
|
||
findLayerByKey,
|
||
getLegendItemsByLayerCode,
|
||
getLayerBranchKeys,
|
||
normalizeCheckedLayerKeys,
|
||
refreshPointLayerDisplayData,
|
||
activatePageContext,
|
||
markPendingPageNavigation,
|
||
searchTimeRange,
|
||
reloadBySearchTimeRange,
|
||
updateSearchTimeRange
|
||
};
|
||
});
|