WholeProcessPlatform/frontend/src/modules/map/application/map-orchestrator.ts
2026-07-03 15:59:05 +08:00

583 lines
18 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { watch, type WatchStopHandle, ref } from 'vue';
import { useRoute } from 'vue-router';
import { unByKey } from 'ol/Observable';
import dayjs from 'dayjs';
import { MapClass } from '@/components/gis/map.class';
import { getMapConfig, layerConfig2Flat } from '@/components/gis/gisUtils';
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 { useMapStore } from '@/store/modules/map';
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
type InitializeOptions = {
container: HTMLElement;
popupContainer?: HTMLDivElement | null;
pageKey: string;
};
type LoadPageOptions = {
pageKey: string;
isInitialLoad?: boolean;
};
type ApplyLayerSelectionOptions = {
checkedKeys: string[];
triggerKey?: string;
checked?: boolean;
};
const SYSTEM_ID = 'qgc';
const HYDRO_DYNAMIC_LAYER_KEYS = [
'dw_point',
'stinfo_video_point',
'stinfo_gjllz_point',
'wt_point',
'wq_ownWq_point',
'wq_countryWq_point',
'fp_point',
'eq_point',
'fb_point',
'vp_point',
'va_point',
'sg_point'
];
const ENG_POINT_LAYER_KEY = 'eng_point';
const ENG_POINT_MEDIUM_VISIBLE_ZOOM = 7.5;
const LARGE_ENG_LEGEND_PREFIX = 'large_eng_';
const MID_ENG_LEGEND_PREFIX = 'mid_eng_';
export const useMapOrchestrator = () => {
const route = useRoute();
const mapClass = MapClass.getInstance();
const mapStore = useMapStore();
const mapConfigStore = useMapConfigStore();
const mapDataStore = useMapDataStore();
const mapViewStore = useMapViewStore();
const jidiSelectEventStore = useJidiSelectEventStore();
let zoomListenerKey: any = null;
let stopBaseSelectionWatch: WatchStopHandle | null = null;
const initializedBaseLayerKeys = new Set<string>();
const baseSelectionDebounceTimer = ref<ReturnType<typeof setTimeout> | null>(
null
);
// 备注:统一获取当前地图视图对象,避免在多个组件里重复兼容 2D/3D view 访问方式。
const getCurrentView = () => {
return mapClass.view?.getView ? mapClass.view.getView() : mapClass.view;
};
// 备注:统一获取当前地图缩放级别,供页面切换和缩放监听复用。
const getCurrentZoom = (): number | undefined => {
return getCurrentView()?.getZoom?.();
};
// 备注:统一把当前页面中的 GIS 基础图层初始化到地图实例,避免在图层树组件里做配置解析和加层编排。
const ensureBaseLayersInitialized = (configs: any[] = []) => {
const flatLayerConfigs = layerConfig2Flat(configs);
flatLayerConfigs.forEach((item: any) => {
if (item.type !== 'GISMap' || !item.key) {
return;
}
if (!item.config && item.paramJson) {
try {
const jsonObj = JSON.parse(item.paramJson);
item.config = getMapConfig(jsonObj);
} catch {
item.config = null;
}
}
if (!item.config) {
return;
}
if (item.key === 'customBaseLayer') {
sessionStorage.setItem('customBaseLayer', JSON.stringify(item.config));
}
if (!initializedBaseLayerKeys.has(item.key)) {
mapClass.addBaseDataLayer(item.config, item.checked === 1);
initializedBaseLayerKeys.add(item.key);
}
mapClass.controlBaseLayerTreeShowAndHidden(
item.key,
item.config?.id || item.key,
item.checked === 1
);
});
};
// 备注:统一加载当前页面所需的图层配置、图例配置以及首批锚点数据。
const loadPage = async ({
pageKey,
isInitialLoad = false
}: LoadPageOptions) => {
mapDataStore.setLoading(true);
try {
const hasGlobalLegendConfig =
Array.isArray(mapConfigStore.legendConfigOriginal) &&
mapConfigStore.legendConfigOriginal.length > 0;
const hasPointLayerCache =
Object.keys(mapDataStore.pointDataCache || {}).length > 0;
const shouldLoadGlobalLegend = isInitialLoad && !hasGlobalLegendConfig;
const shouldPreloadLayerData = isInitialLoad && !hasPointLayerCache;
const previousPageKey = mapConfigStore.lastLoadOptions?.pageKey || '';
const previousCheckedKeys = mapViewStore.getCheckedLayerKeys();
const moduleId = (route.meta?.moduleId as string) || '';
const loadOptions = {
systemId: SYSTEM_ID,
moduleId,
pageKey,
description: 'true'
};
const layerConfigPromise =
mapConfigStore.loadPageLayerConfig(loadOptions);
const legendConfigPromise = mapConfigStore.loadPageLegendConfig(
moduleId,
{
includeGlobal: shouldLoadGlobalLegend
}
);
// 同时等待两个 promise
const [{ layerConfig }, { legendOriginal, pageLegend }] =
await Promise.all([layerConfigPromise, legendConfigPromise]);
// 先设置图层数据(更新 checkedLayerKeys再设置图例数据
if (layerConfig.length > 0) {
mapStore.setLayerData(layerConfig);
ensureBaseLayersInitialized(layerConfig);
let checkedKeys = mapViewStore.getCheckedLayerKeys();
if (previousPageKey === pageKey && previousCheckedKeys.length > 0) {
const getAllLayerKeys = (items: any[]): string[] => {
const keys: string[] = [];
const walk = (nodes: any[]) => {
nodes.forEach(item => {
if (item?.key) {
keys.push(item.key);
}
if (item?.children?.length > 0) {
walk(item.children);
}
});
};
walk(items);
return keys;
};
const currentLayerKeys = new Set(getAllLayerKeys(layerConfig));
const runtimeCheckedKeys = previousCheckedKeys.filter(key =>
currentLayerKeys.has(key)
);
if (runtimeCheckedKeys.length > 0) {
mapViewStore.setCheckedLayerKeys(runtimeCheckedKeys);
checkedKeys = runtimeCheckedKeys;
}
}
// 设置图例数据(此时 checkedLayerKeys 已正确设置)
if (legendOriginal.length > 0) {
mapStore.setLegendData(legendOriginal, pageLegend);
}
if (shouldPreloadLayerData) {
mapStore.setSelectedLegendData();
await mapStore.loadAllLayerData(layerConfig, checkedKeys);
} else {
await mapStore.updateLayerData(checkedKeys, true);
mapStore.setSelectedLegendData();
}
}
} finally {
// mapDataStore.setLoading(false);
}
};
// 备注:统一完成地图实例初始化和弹窗挂载,确保后续事件监听可以在图层数据加载前生效。
const initializeMapShell = async ({
container,
popupContainer
}: InitializeOptions) => {
await mapClass.init(container);
if (popupContainer) {
mapClass.initPopupOverlay(popupContainer);
}
};
// 备注:统一完成首屏页面配置加载,供挂载阶段和后续页面重载复用。
const initialize = async ({
container,
popupContainer,
pageKey
}: InitializeOptions) => {
await initializeMapShell({
container,
popupContainer,
pageKey
});
await loadPage({ pageKey, isInitialLoad: true });
};
// 备注:统一绑定地图缩放监听,让页面组件只负责触发初始化,不再直接处理缩放联动细节。
const bindZoomListener = (getIsHydroMenu: () => boolean) => {
if (zoomListenerKey) {
unByKey(zoomListenerKey);
zoomListenerKey = null;
}
const view = getCurrentView();
if (!view?.on) return;
const currentZoom = getCurrentZoom();
if (currentZoom !== undefined) {
mapViewStore.setCurrentZoomLevel(currentZoom);
}
zoomListenerKey = view.on('change:resolution', async () => {
const zoom = getCurrentZoom();
if (zoom === undefined) return;
await handleZoomLevelChange(
mapViewStore.currentZoomLevel,
zoom,
getIsHydroMenu()
);
});
};
// 备注:统一绑定基地切换事件,让页面组件不再直接 watch 外部基地选择源。
const bindBaseSelection = () => {
if (stopBaseSelectionWatch) {
stopBaseSelectionWatch();
stopBaseSelectionWatch = null;
}
stopBaseSelectionWatch = watch(
() => jidiSelectEventStore.selectedItem?.wbsCode,
newWbsCode => {
if (!newWbsCode) {
changeBaseId('');
return;
}
if (baseSelectionDebounceTimer.value) {
clearTimeout(baseSelectionDebounceTimer.value);
}
baseSelectionDebounceTimer.value = setTimeout(() => {
changeBaseId(newWbsCode);
baseSelectionDebounceTimer.value = null;
}, 100);
},
{ immediate: true }
);
};
// 备注:统一挂载地图页面生命周期,初始化后自动接管缩放监听和基地切换监听。
const mountView = async (
options: InitializeOptions & {
getIsHydroMenu: () => boolean;
}
) => {
await initializeMapShell(options);
bindBaseSelection();
bindZoomListener(options.getIsHydroMenu);
await loadPage({ pageKey: options.pageKey, isInitialLoad: true });
};
// 备注:统一处理菜单切换后的页面配置重载,供页面 watch 直接复用。
const reloadPage = async (pageKey: string) => {
if (!pageKey) return;
await loadPage({ pageKey, isInitialLoad: false });
};
// 备注:菜单切换前先隐藏旧页面当前可见的点图层,避免旧锚点残留到新页面接口返回之后才消失。
const hideCurrentVisiblePointLayers = () => {
const currentCheckedKeys = mapViewStore.getCheckedLayerKeys();
currentCheckedKeys.forEach(layerKey => {
const layerItem = mapStore.findLayerByKey(mapStore.layerData, layerKey);
if (
!layerItem?.key ||
layerItem.type !== 'pointMap' ||
!mapClass.hasLayer(layerItem.key)
) {
return;
}
mapClass.mdLayerTreeShowOrHidden(layerItem.key, false);
});
};
// 备注:统一处理图层树勾选结果归一化和地图联动,供图层树组件复用。
const applyLayerSelection = async ({
checkedKeys,
triggerKey,
checked
}: ApplyLayerSelectionOptions) => {
const normalizedKeys = mapStore.normalizeCheckedLayerKeys(
checkedKeys,
triggerKey,
checked
);
await mapStore.updateLayerData(normalizedKeys);
return normalizedKeys;
};
// 备注:统一确保指定动态图层完成数据预加载,避免缩放切换时重复在页面组件里拼装加载逻辑。
const ensureDynamicLayersLoaded = async (layerKeys: string[] = []) => {
const layersToLoad = layerKeys
.map(layerKey => mapStore.findLayerByKey(mapStore.layerData, layerKey))
.filter(
(layer: any) => layer?.url && !mapDataStore.hasPointLayerData(layer.key)
);
for (const layer of layersToLoad) {
await mapStore.loadLayerData(layer);
}
};
// 备注:统一控制水电开发菜单下的动态图层增删,避免页面组件直接操作加载和勾选细节。
const syncHydroDynamicLayers = async (visible: boolean) => {
const currentCheckedKeys = mapViewStore.getCheckedLayerKeys();
const pageDefaultCheckedKeys = new Set(
mapConfigStore.extractCheckedLayerKeys(mapStore.layerData || [])
);
if (visible) {
const layersToAdd = HYDRO_DYNAMIC_LAYER_KEYS.filter(
key => !currentCheckedKeys.includes(key)
);
if (layersToAdd.length === 0) {
return currentCheckedKeys;
}
await ensureDynamicLayersLoaded(layersToAdd);
return applyLayerSelection({
checkedKeys: [...currentCheckedKeys, ...layersToAdd]
});
}
const nextCheckedKeys = currentCheckedKeys.filter(key => {
if (!HYDRO_DYNAMIC_LAYER_KEYS.includes(key)) {
return true;
}
return pageDefaultCheckedKeys.has(key);
});
if (nextCheckedKeys.length === currentCheckedKeys.length) {
return currentCheckedKeys;
}
return applyLayerSelection({
checkedKeys: nextCheckedKeys
});
};
// 备注:统一处理地图缩放联动,收口水电开发菜单下的动态图层阈值切换逻辑。
const handleZoomLevelChange = async (
previousZoom: number,
currentZoom: number,
isHydroMenu: boolean
) => {
mapViewStore.setCurrentZoomLevel(currentZoom);
const crossedEngPointMediumThreshold =
(previousZoom <= ENG_POINT_MEDIUM_VISIBLE_ZOOM &&
currentZoom > ENG_POINT_MEDIUM_VISIBLE_ZOOM) ||
(previousZoom > ENG_POINT_MEDIUM_VISIBLE_ZOOM &&
currentZoom <= ENG_POINT_MEDIUM_VISIBLE_ZOOM);
if (
crossedEngPointMediumThreshold &&
mapViewStore.getCheckedLayerKeys().includes(ENG_POINT_LAYER_KEY)
) {
mapStore.refreshPointLayerDisplayData(ENG_POINT_LAYER_KEY);
}
if (!isHydroMenu) {
return;
}
const oldZoomLevel = Math.floor(previousZoom);
const newZoomLevel = Math.floor(currentZoom);
if (oldZoomLevel < 12 && newZoomLevel >= 12) {
await syncHydroDynamicLayers(true);
return;
}
if (oldZoomLevel >= 12 && newZoomLevel < 12) {
await syncHydroDynamicLayers(false);
}
};
// 备注:统一处理菜单切换后的页面重载和动态图层收口,减少页面组件里的分支编排。
const handlePageChange = async (pageKey: string, isHydroMenu: boolean) => {
if (!pageKey) return;
const previousPageKey = mapConfigStore.lastLoadOptions?.pageKey || '';
if (previousPageKey && previousPageKey !== pageKey) {
hideCurrentVisiblePointLayers();
}
await reloadPage(pageKey);
const currentZoom = getCurrentZoom();
if (isHydroMenu && currentZoom !== undefined && currentZoom >= 12) {
await syncHydroDynamicLayers(true);
return;
}
await syncHydroDynamicLayers(false);
};
// 备注:统一处理单个图层切换命令,供后续其他入口复用。
const toggleLayer = async (layerKey: string, checked?: boolean) => {
const currentKeys = mapViewStore.getCheckedLayerKeys();
const currentSet = new Set(currentKeys);
const nextChecked =
checked === undefined ? !currentSet.has(layerKey) : checked;
if (nextChecked) {
currentSet.add(layerKey);
} else {
currentSet.delete(layerKey);
}
return applyLayerSelection({
checkedKeys: Array.from(currentSet),
triggerKey: layerKey,
checked: nextChecked
});
};
// 备注:统一处理单个图例切换命令,供图例组件复用。
const toggleLegend = (nameEn: string, checked?: number) => {
if (!nameEn) return;
const nextChecked =
checked === undefined
? mapViewStore.getLegendChecked(nameEn) === 1
? 0
: 1
: checked;
mapStore.updateLegendChecked(nameEn, nextChecked);
};
// 备注:统一处理批量图例切换命令,供容量筛选和分组图例复用。
const toggleLegendBatch = (nameEns: string[] = [], checked: number) => {
const targetNameEns = nameEns.filter(Boolean);
if (targetNameEns.length === 0) return;
mapStore.updateLegendCheckedBatch(targetNameEns, checked);
};
// 备注:统一处理基地切换命令,同时驱动地图基地裁切联动。
const changeBaseId = (baseId: string) => {
const nextBaseId = !baseId || baseId === 'all' ? '' : baseId;
mapViewStore.setSelectedBaseId(nextBaseId);
mapClass.jdPanelControlShowAndHidden(
baseId || 'all',
baseId !== 'all' && !!baseId
);
};
// 备注:统一处理时间范围变更,并触发相关图层数据重载。
const changeTimeRange = async (range: [any, any]) => {
mapViewStore.setSearchTimeRange(range);
await mapStore.reloadBySearchTimeRange();
};
// 备注:统一处理装机容量筛选,让筛选器切换时同步更新 eng_point 图例运行态和点位显示。
const changeEngPointCapacity = (capacityType?: string | null) => {
const legendItems = mapStore.getLegendItemsByLayerCode(ENG_POINT_LAYER_KEY);
if (!legendItems.length) return;
const allEngLegendNames = legendItems
.map((item: any) => item?.nameEn)
.filter(Boolean);
if (!allEngLegendNames.length) return;
const normalizedCapacityType = capacityType || 'all';
let activePrefixes = [LARGE_ENG_LEGEND_PREFIX, MID_ENG_LEGEND_PREFIX];
if (normalizedCapacityType === 'large_eng_built') {
activePrefixes = [LARGE_ENG_LEGEND_PREFIX];
} else if (normalizedCapacityType === 'mid_eng_built') {
activePrefixes = [MID_ENG_LEGEND_PREFIX];
}
const visibleLegendNames = allEngLegendNames.filter((nameEn: string) =>
activePrefixes.some(prefix => nameEn.startsWith(prefix))
);
const hiddenLegendNames = allEngLegendNames.filter(
(nameEn: string) => !visibleLegendNames.includes(nameEn)
);
mapStore.updateLegendCheckedBatch(hiddenLegendNames, 0);
mapStore.updateLegendCheckedBatch(visibleLegendNames, 1);
mapStore.refreshPointLayerDisplayData(ENG_POINT_LAYER_KEY);
};
// 备注:统一重置地图筛选表单依赖的时间范围,供筛选组件在切页时复位默认输入。
const resetFilterState = () => {
mapViewStore.setSearchTimeRange([dayjs().subtract(1, 'M'), dayjs()]);
};
// 备注:统一处理搜索定位,按点位编码从缓存数据中查找并飞行到目标位置。
const focusPoint = (pointId: string, zoom = 15) => {
if (!pointId) return;
const targetPoint = mapDataStore.pointData.find((item: any) => {
return item.stcd === pointId || item._id === pointId;
});
if (!targetPoint?.lgtd || !targetPoint?.lttd) return;
mapClass.flyTopanto([targetPoint.lgtd, targetPoint.lttd], zoom);
};
// 备注:统一释放地图页面生命周期里注册的监听,避免页面卸载后残留联动。
const unmountView = () => {
if (zoomListenerKey) {
unByKey(zoomListenerKey);
zoomListenerKey = null;
}
if (stopBaseSelectionWatch) {
stopBaseSelectionWatch();
stopBaseSelectionWatch = null;
}
if (baseSelectionDebounceTimer.value) {
clearTimeout(baseSelectionDebounceTimer.value);
baseSelectionDebounceTimer.value = null;
}
};
return {
initialize,
mountView,
unmountView,
loadPage,
reloadPage,
applyLayerSelection,
ensureDynamicLayersLoaded,
syncHydroDynamicLayers,
getCurrentZoom,
handleZoomLevelChange,
handlePageChange,
toggleLayer,
toggleLegend,
toggleLegendBatch,
changeBaseId,
changeTimeRange,
changeEngPointCapacity,
resetFilterState,
focusPoint
};
};