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-01 08:42:06 +08:00
|
|
|
|
import { omit } from 'lodash';
|
2026-06-03 15:03:31 +08:00
|
|
|
|
import moment from 'moment';
|
2026-06-01 08:42:06 +08:00
|
|
|
|
import { MapClass } from '@/components/gis/map.class';
|
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';
|
|
|
|
|
|
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';
|
|
|
|
|
|
import { urlList } from './GisUrlList';
|
|
|
|
|
|
const mapClass = MapClass.getInstance();
|
2026-06-03 15:03:31 +08:00
|
|
|
|
const DEFAULT_LAYER_REQUEST_CONCURRENCY = 4;
|
|
|
|
|
|
|
|
|
|
|
|
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 buildUrlListIndex = (items: any[] = []) => {
|
|
|
|
|
|
const index: Record<string, any[]> = {};
|
|
|
|
|
|
|
|
|
|
|
|
items.forEach(item => {
|
|
|
|
|
|
[item.url, item.title, item.keyType].forEach(key => {
|
|
|
|
|
|
if (!key) return;
|
|
|
|
|
|
if (!index[key]) {
|
|
|
|
|
|
index[key] = [];
|
|
|
|
|
|
}
|
|
|
|
|
|
index[key].push(item);
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
return index;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const urlListIndex = buildUrlListIndex(urlList);
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
const { getPointLayerData, hasPointLayerData, hasValidPointLayerCache } =
|
|
|
|
|
|
mapDataStore;
|
|
|
|
|
|
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>();
|
|
|
|
|
|
let loadSessionSeed = 0;
|
|
|
|
|
|
|
|
|
|
|
|
const normalizeLegendNameEn = mapConfigStore.normalizeLegendNameEn;
|
|
|
|
|
|
|
|
|
|
|
|
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 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
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 设置图层数据
|
|
|
|
|
|
*/
|
|
|
|
|
|
const setLayerData = (data: any[]) => {
|
2026-06-03 15:03:31 +08:00
|
|
|
|
mapConfigStore.setLayerConfigTree(data);
|
|
|
|
|
|
mapViewStore.setCheckedLayerKeys(
|
|
|
|
|
|
mapConfigStore.extractCheckedLayerKeys(data)
|
|
|
|
|
|
);
|
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-06-03 15:03:31 +08:00
|
|
|
|
* 启动新的图层加载会话,并取消上一轮仍在进行中的旧请求,避免菜单切换后旧数据回写。
|
2026-06-01 08:42:06 +08:00
|
|
|
|
*/
|
2026-06-03 15:03:31 +08:00
|
|
|
|
const startLoadSession = () => {
|
|
|
|
|
|
loadSessionSeed += 1;
|
|
|
|
|
|
const currentSessionId = loadSessionSeed;
|
|
|
|
|
|
|
|
|
|
|
|
inFlightRequestMap.forEach(entry => {
|
|
|
|
|
|
if (entry.sessionId !== currentSessionId) {
|
|
|
|
|
|
entry.controller.abort();
|
|
|
|
|
|
mapDataStore.clearLayerLoadState(entry.layerKey);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2026-06-01 08:42:06 +08:00
|
|
|
|
|
2026-06-03 15:03:31 +08:00
|
|
|
|
return currentSessionId;
|
|
|
|
|
|
};
|
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-06-03 15:03:31 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 按固定并发数执行图层加载任务,避免瞬时请求过多,同时让单个失败不影响其他任务。
|
|
|
|
|
|
*/
|
|
|
|
|
|
const runLayerLoadQueue = async (
|
|
|
|
|
|
tasks: Array<() => Promise<any>>,
|
|
|
|
|
|
concurrency: number = DEFAULT_LAYER_REQUEST_CONCURRENCY
|
|
|
|
|
|
) => {
|
|
|
|
|
|
const results: PromiseSettledResult<any>[] = [];
|
|
|
|
|
|
const safeConcurrency = Math.max(1, concurrency);
|
|
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < tasks.length; i += safeConcurrency) {
|
|
|
|
|
|
const batch = tasks.slice(i, i + safeConcurrency).map(task => task());
|
|
|
|
|
|
const batchResults = await Promise.allSettled(batch);
|
|
|
|
|
|
results.push(...batchResults);
|
|
|
|
|
|
}
|
2026-06-01 08:42:06 +08:00
|
|
|
|
|
2026-06-03 15:03:31 +08:00
|
|
|
|
return results;
|
|
|
|
|
|
};
|
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
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 图例数据转对象
|
|
|
|
|
|
*/
|
|
|
|
|
|
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-06-03 15:03:31 +08:00
|
|
|
|
const cachedData = layer?.data || getPointLayerData(key);
|
2026-06-01 08:42:06 +08:00
|
|
|
|
if (cachedData && cachedData.length > 0) {
|
|
|
|
|
|
console.log(`图层 ${key} 使用缓存数据,共 ${cachedData.length} 条`);
|
|
|
|
|
|
layer.checked = 1;
|
|
|
|
|
|
if (!mapClass.hasLayer(key)) {
|
|
|
|
|
|
console.log(`向地图添加图层 ${key}`);
|
|
|
|
|
|
mapClass.addInitDataLayer(cachedData, 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-06-03 15:03:31 +08:00
|
|
|
|
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) {
|
|
|
|
|
|
if (legendDataOriginal.value.length > 0) {
|
|
|
|
|
|
// 先将该图层所有锚点隐藏
|
|
|
|
|
|
mapClass.setLegendPointVisible(key, '', false);
|
2026-06-03 15:03:31 +08:00
|
|
|
|
getLegendItemsByLayerCode(key).forEach((legendItem: any) => {
|
|
|
|
|
|
if (getLegendChecked(legendItem.nameEn) === 1) {
|
|
|
|
|
|
applyLegendItemVisibility(legendItem, 1);
|
2026-06-01 08:42:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
// 图例状态设置完成后再显示图层
|
|
|
|
|
|
mapClass.mdLayerTreeShowOrHidden(key, true);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 没有图例数据:直接显示图层
|
|
|
|
|
|
mapClass.mdLayerTreeShowOrHidden(key, true);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
} else if (layerItem && layerItem.type === 'GISMap') {
|
|
|
|
|
|
// 处理 GISMap 类型的图层(基础图层已在 LayerController 中加载,这里只需控制显示隐藏)
|
|
|
|
|
|
if (key && key !== '-' && key !== 'powerBaseStation') {
|
2026-06-03 15:03:31 +08:00
|
|
|
|
console.log('layerItem 接口', key);
|
2026-06-01 08:42:06 +08:00
|
|
|
|
const shouldBeVisible = checkKeys.includes(key);
|
|
|
|
|
|
mapClass.controlBaseLayerTreeShowAndHidden(key, key, shouldBeVisible);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 加载所有包含URL的图层数据
|
|
|
|
|
|
* @param items 图层数据
|
|
|
|
|
|
* @param checkedKeys 选中的图层 keys(用于优先加载)
|
|
|
|
|
|
*/
|
|
|
|
|
|
const loadAllLayerData = async (items: any[], checkedKeys: string[] = []) => {
|
|
|
|
|
|
console.log('开始加载所有图层数据:', items);
|
2026-06-03 15:03:31 +08:00
|
|
|
|
mapDataStore.setLoading(true);
|
|
|
|
|
|
const currentSessionId = startLoadSession();
|
|
|
|
|
|
const checkedTasks: Array<() => Promise<any>> = [];
|
|
|
|
|
|
const uncheckedTasks: Array<() => Promise<any>> = [];
|
2026-06-01 08:42:06 +08:00
|
|
|
|
const processedKeys = new Set<string>();
|
|
|
|
|
|
|
|
|
|
|
|
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-06-03 15:03:31 +08:00
|
|
|
|
const task = () => loadLayerData(item, currentSessionId);
|
|
|
|
|
|
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 {
|
|
|
|
|
|
const checkedResults = await runLayerLoadQueue(checkedTasks);
|
|
|
|
|
|
const uncheckedResults = await runLayerLoadQueue(uncheckedTasks);
|
|
|
|
|
|
const failedResults = [...checkedResults, ...uncheckedResults].filter(
|
|
|
|
|
|
result => result.status === 'rejected'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if (failedResults.length > 0) {
|
|
|
|
|
|
console.warn(
|
|
|
|
|
|
'部分图层数据加载失败,但不会阻断其他图层:',
|
|
|
|
|
|
failedResults
|
|
|
|
|
|
);
|
2026-06-01 08:42:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-03 15:03:31 +08:00
|
|
|
|
if (!isCurrentLoadSession(currentSessionId)) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-06-01 08:42:06 +08:00
|
|
|
|
|
2026-06-03 15:03:31 +08:00
|
|
|
|
console.log('所有图层数据加载完成');
|
2026-06-01 08:42:06 +08:00
|
|
|
|
|
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);
|
|
|
|
|
|
if (layer && hasPointLayerData(key)) {
|
|
|
|
|
|
layer.data = getPointLayerData(key);
|
|
|
|
|
|
console.log(
|
|
|
|
|
|
`确认图层 ${key} 数据已准备好,共 ${layer.data.length} 条`
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const allPointData = mapDataStore.rebuildPointDataFromCache(allLayerKeys);
|
|
|
|
|
|
console.log('所有锚点数据已设置,共', allPointData.length, '条');
|
|
|
|
|
|
|
|
|
|
|
|
// 设置选中的图例数据(确保图例在所有锚点加载完成后才显示)
|
|
|
|
|
|
setSelectedLegendData();
|
|
|
|
|
|
|
|
|
|
|
|
// 调用 updateLayerData 显示选中图层的锚点
|
|
|
|
|
|
if (checkedKeys.length > 0) {
|
|
|
|
|
|
await updateLayerData(checkedKeys, true);
|
|
|
|
|
|
}
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
mapDataStore.setLoading(false);
|
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-06-03 15:03:31 +08:00
|
|
|
|
const getRuntimeLegendName = (nameEn: string): string => {
|
|
|
|
|
|
const normalizedNameEn = normalizeLegendNameEn(nameEn);
|
|
|
|
|
|
return normalizedNameEn.includes('alarm_range_')
|
|
|
|
|
|
? `large_eng_built_${normalizedNameEn}`
|
|
|
|
|
|
: 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 runtimeLegendName = getRuntimeLegendName(legendItem.nameEn);
|
|
|
|
|
|
|
|
|
|
|
|
if (layerItem?.type === 'GISMap') {
|
|
|
|
|
|
mapClass.controlBaseLayerTreeShowAndHidden(
|
|
|
|
|
|
layerKey,
|
|
|
|
|
|
layerItem.config?.id || layerKey,
|
|
|
|
|
|
shouldBeVisible
|
|
|
|
|
|
);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
mapClass.setLegendPointVisible(
|
|
|
|
|
|
layerKey,
|
|
|
|
|
|
runtimeLegendName,
|
|
|
|
|
|
shouldBeVisible
|
|
|
|
|
|
);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
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,
|
|
|
|
|
|
sessionId: number = loadSessionSeed
|
|
|
|
|
|
) => {
|
2026-06-01 08:42:06 +08:00
|
|
|
|
const { key, url, params = {}, paramJson } = layer;
|
|
|
|
|
|
|
|
|
|
|
|
// 没有URL,跳过
|
|
|
|
|
|
if (!url) {
|
2026-06-03 15:03:31 +08:00
|
|
|
|
return [];
|
2026-06-01 08:42:06 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 参考 React 版本:使用 urlList 匹配图层配置
|
|
|
|
|
|
let requestUrl = url;
|
|
|
|
|
|
let requestParams: any = params;
|
|
|
|
|
|
let requestOrders: any = null;
|
|
|
|
|
|
|
|
|
|
|
|
// 根据图层的 key(keyType)、title、url 查找匹配的 urlList 项
|
|
|
|
|
|
const layerKey = layer?.key || '';
|
|
|
|
|
|
const layerTitle = layer?.title || '';
|
|
|
|
|
|
const matchedList =
|
2026-06-03 15:03:31 +08:00
|
|
|
|
urlListIndex[layerKey] || urlListIndex[layerTitle] || urlListIndex[url];
|
2026-06-01 08:42:06 +08:00
|
|
|
|
|
|
|
|
|
|
if (matchedList && matchedList.length > 0) {
|
|
|
|
|
|
let matchedItem: any = null;
|
|
|
|
|
|
if (key == 'va_built_point ' || key == 'vp_built_point') {
|
|
|
|
|
|
matchedList.forEach(item => {
|
|
|
|
|
|
if (item.title === layerTitle) {
|
|
|
|
|
|
matchedItem = item;
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
} else {
|
|
|
|
|
|
matchedItem = matchedList[0];
|
|
|
|
|
|
}
|
|
|
|
|
|
requestUrl = matchedItem.url;
|
|
|
|
|
|
|
|
|
|
|
|
// 转换 params 为 filter 格式
|
|
|
|
|
|
if (matchedItem.params) {
|
|
|
|
|
|
const filtersArray: any[] = [];
|
|
|
|
|
|
for (const key in matchedItem.params) {
|
|
|
|
|
|
const value = matchedItem.params[key];
|
|
|
|
|
|
if (Array.isArray(value)) {
|
|
|
|
|
|
// 处理数组格式: { field: 'sttpCode', operator: 'eq', value: 'WQ' }
|
|
|
|
|
|
value.forEach((filter: any) => {
|
|
|
|
|
|
filtersArray.push({
|
|
|
|
|
|
...filter,
|
|
|
|
|
|
dataType: filter.dataType || 'string'
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 处理简单格式: sttpCode: 'VP'
|
|
|
|
|
|
filtersArray.push({
|
|
|
|
|
|
field: key,
|
|
|
|
|
|
operator: 'eq',
|
|
|
|
|
|
dataType: 'string',
|
|
|
|
|
|
value: value
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
requestParams = {
|
|
|
|
|
|
logic: 'and',
|
|
|
|
|
|
filters: filtersArray
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
let voidSttp = 'all';
|
|
|
|
|
|
const ylfbKeys = ['ylfb_point']; // 需要时间搜索的图层key
|
|
|
|
|
|
const timeRangeLayerKeys = ['ef_point']; // 需要时间搜索的图层key
|
|
|
|
|
|
const spjkz = ['stinfo_video_point'];
|
|
|
|
|
|
const lineTimeLayerKeys = [
|
|
|
|
|
|
'wt_rive_gradient_line',
|
|
|
|
|
|
'wq_rive_gradient_line'
|
|
|
|
|
|
]; // 需要时间搜索的图层key
|
|
|
|
|
|
|
|
|
|
|
|
let yearTime = moment().subtract(1, 'years'); // 鱼类分布年份
|
|
|
|
|
|
if (timeRangeLayerKeys.includes(layerKey)) {
|
|
|
|
|
|
requestParams.filters.push({
|
|
|
|
|
|
field: 'tm',
|
|
|
|
|
|
operator: 'gte',
|
|
|
|
|
|
dataType: 'date',
|
2026-06-03 15:03:31 +08:00
|
|
|
|
value: moment(searchTimeRange.value[0]).format('YYYY-MM-DD HH:mm:ss')
|
2026-06-01 08:42:06 +08:00
|
|
|
|
});
|
|
|
|
|
|
requestParams.filters.push({
|
|
|
|
|
|
field: 'tm',
|
|
|
|
|
|
operator: 'lte',
|
|
|
|
|
|
dataType: 'date',
|
2026-06-03 15:03:31 +08:00
|
|
|
|
value: moment(searchTimeRange.value[1]).format('YYYY-MM-DD 23:59:59')
|
2026-06-01 08:42:06 +08:00
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
if (spjkz.includes(layerKey)) {
|
|
|
|
|
|
if (voidSttp !== 'all') {
|
|
|
|
|
|
requestParams.filters.push({
|
|
|
|
|
|
field: 'sttp',
|
|
|
|
|
|
operator: 'in',
|
|
|
|
|
|
value: voidSttp == 'VD_EQ,VD_EQS' ? ['VD_EQ', 'VD_EQS'] : [voidSttp]
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (ylfbKeys.includes(layerKey)) {
|
|
|
|
|
|
if (yearTime) {
|
|
|
|
|
|
requestParams.filters.push({
|
2026-06-15 09:18:00 +08:00
|
|
|
|
field: 'startTime',
|
2026-06-01 08:42:06 +08:00
|
|
|
|
operator: 'eq',
|
|
|
|
|
|
dataType: 'date',
|
|
|
|
|
|
value: moment(yearTime)
|
|
|
|
|
|
.startOf('year')
|
|
|
|
|
|
.format('YYYY-MM-DD 00:00:00')
|
|
|
|
|
|
});
|
|
|
|
|
|
requestParams.filters.push({
|
2026-06-15 09:18:00 +08:00
|
|
|
|
field: 'endTime',
|
2026-06-01 08:42:06 +08:00
|
|
|
|
operator: 'eq',
|
|
|
|
|
|
dataType: 'date',
|
|
|
|
|
|
value: moment(yearTime).endOf('year').format('YYYY-MM-DD 23:59:59')
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 转换 orders 格式:从 JSON 字符串转换为数组格式
|
|
|
|
|
|
if (matchedItem.orders) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const ordersObj = JSON.parse(matchedItem.orders);
|
|
|
|
|
|
const ordersArray = Object.entries(ordersObj).map(([field, dir]) => ({
|
|
|
|
|
|
field,
|
|
|
|
|
|
dir
|
|
|
|
|
|
}));
|
|
|
|
|
|
requestOrders = ordersArray;
|
|
|
|
|
|
} catch (e) {
|
|
|
|
|
|
console.error('解析 orders 失败:', e);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果没有匹配到 urlList,尝试解析 paramJson
|
|
|
|
|
|
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-06-03 15:03:31 +08:00
|
|
|
|
if (hasValidPointLayerCache(key, requestIdentifier)) {
|
|
|
|
|
|
console.log(`图层 ${key} 命中有效缓存,跳过请求`);
|
|
|
|
|
|
layer.data = getPointLayerData(key);
|
|
|
|
|
|
layer.checked = 0;
|
|
|
|
|
|
if (!mapClass.hasLayer(key)) {
|
|
|
|
|
|
console.log(`图层 ${key} 未在地图中,添加到地图(隐藏状态)`);
|
|
|
|
|
|
mapClass.addInitDataLayer(getPointLayerData(key), key);
|
|
|
|
|
|
mapClass.mdLayerTreeShowOrHidden(key, false);
|
|
|
|
|
|
}
|
|
|
|
|
|
return layer.data;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const previousRequestKey = activeLayerRequestKeyMap.get(key);
|
|
|
|
|
|
if (previousRequestKey && previousRequestKey !== requestIdentifier) {
|
|
|
|
|
|
const previousRequest = inFlightRequestMap.get(previousRequestKey);
|
|
|
|
|
|
previousRequest?.controller.abort();
|
|
|
|
|
|
mapDataStore.clearLayerLoadState(key);
|
|
|
|
|
|
inFlightRequestMap.delete(previousRequestKey);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const existingRequest = inFlightRequestMap.get(requestIdentifier);
|
|
|
|
|
|
if (existingRequest) {
|
|
|
|
|
|
return existingRequest.promise;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
mapDataStore.setLayerLoading(key);
|
|
|
|
|
|
|
|
|
|
|
|
const controller = new AbortController();
|
|
|
|
|
|
activeLayerRequestKeyMap.set(key, requestIdentifier);
|
|
|
|
|
|
|
|
|
|
|
|
const requestPromise = (async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const response = await request({
|
|
|
|
|
|
url: '/api' + requestUrl,
|
|
|
|
|
|
method: 'post',
|
|
|
|
|
|
data: requestData,
|
|
|
|
|
|
signal: controller.signal
|
|
|
|
|
|
});
|
|
|
|
|
|
const resData = response?.data || response?.data?.data || [];
|
|
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
|
controller.signal.aborted ||
|
|
|
|
|
|
!isCurrentLoadSession(sessionId) ||
|
|
|
|
|
|
activeLayerRequestKeyMap.get(key) !== requestIdentifier
|
|
|
|
|
|
) {
|
|
|
|
|
|
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-06-03 15:03:31 +08:00
|
|
|
|
list.forEach((item: any) => {
|
|
|
|
|
|
const iconType = item.anchoPointState;
|
|
|
|
|
|
const legendConfig = getLegendConfigByNameEn(iconType);
|
|
|
|
|
|
item.iconCode = legendConfig?.icon || '';
|
|
|
|
|
|
item.code = legendConfig?.code || '';
|
|
|
|
|
|
item.tm =
|
|
|
|
|
|
item.sttpMap === 'WQ_ALARM'
|
|
|
|
|
|
? item?.warnDataList?.[0]?.tm
|
|
|
|
|
|
: item?.tm;
|
|
|
|
|
|
item._id = item.sttpMap + '_' + item.stcd;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
list = list.filter(
|
|
|
|
|
|
(l: any) =>
|
|
|
|
|
|
(l.iconCode || l.code == 'colorLayer') &&
|
|
|
|
|
|
!(l?.baseId == 'all' && l?.anchoPointState?.endsWith('_nbuilt'))
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const isLayerChecked = layer.checked === 1;
|
|
|
|
|
|
mapDataStore.setPointLayerCache(key, {
|
|
|
|
|
|
checked: isLayerChecked,
|
|
|
|
|
|
data: list,
|
|
|
|
|
|
cacheKey: requestIdentifier
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
layer.data = list;
|
|
|
|
|
|
|
|
|
|
|
|
if (list.length > 0) {
|
|
|
|
|
|
console.log(
|
|
|
|
|
|
`向地图添加图层 ${key}(${
|
|
|
|
|
|
isLayerChecked ? '显示状态' : '隐藏状态'
|
|
|
|
|
|
})`
|
2026-06-01 08:42:06 +08:00
|
|
|
|
);
|
2026-06-03 15:03:31 +08:00
|
|
|
|
mapClass.addInitDataLayer(list, key);
|
|
|
|
|
|
mapClass.mdLayerTreeShowOrHidden(key, isLayerChecked);
|
|
|
|
|
|
}
|
2026-06-01 08:42:06 +08:00
|
|
|
|
|
2026-06-03 15:03:31 +08:00
|
|
|
|
console.log(`图层 ${key} 数据加载完成,共 ${list.length} 条记录`);
|
|
|
|
|
|
mapDataStore.setLayerLoaded(key);
|
|
|
|
|
|
return list;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
if (
|
|
|
|
|
|
controller.signal.aborted ||
|
|
|
|
|
|
isCanceledRequestError(error) ||
|
|
|
|
|
|
!isCurrentLoadSession(sessionId) ||
|
|
|
|
|
|
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 () => {
|
|
|
|
|
|
console.log('searchTimeRange updated:', searchTimeRange.value);
|
|
|
|
|
|
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);
|
|
|
|
|
|
console.log(`已清空图层 ${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)) {
|
|
|
|
|
|
console.log(`重新加载图层 ${key}`);
|
|
|
|
|
|
await loadLayerData(layerItem, currentSessionId);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!isCurrentLoadSession(currentSessionId)) {
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const allPointData = mapDataStore.rebuildPointDataFromCache();
|
|
|
|
|
|
console.log('锚点数据已更新,共', allPointData.length, '条');
|
|
|
|
|
|
|
|
|
|
|
|
// 更新地图锚点显示
|
|
|
|
|
|
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,
|
|
|
|
|
|
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-06-01 08:42:06 +08:00
|
|
|
|
getCheckedKeys,
|
|
|
|
|
|
legendData2Obj,
|
|
|
|
|
|
loading,
|
|
|
|
|
|
pointDataCache,
|
2026-06-03 15:03:31 +08:00
|
|
|
|
layerLoadState,
|
|
|
|
|
|
findLayerByKey,
|
|
|
|
|
|
getLegendItemsByLayerCode,
|
|
|
|
|
|
getLayerBranchKeys,
|
|
|
|
|
|
normalizeCheckedLayerKeys,
|
|
|
|
|
|
searchTimeRange,
|
|
|
|
|
|
reloadBySearchTimeRange,
|
|
|
|
|
|
updateSearchTimeRange
|
2026-04-03 16:04:16 +08:00
|
|
|
|
};
|
|
|
|
|
|
});
|