三维修改
This commit is contained in:
parent
90bd2bac1f
commit
8d17118b40
@ -32,8 +32,10 @@ import { useMapOrchestrator } from '@/modules/map/application/map-orchestrator';
|
||||
import { MapClass } from './map.class';
|
||||
import { getQgcRvcd } from '@/api/map';
|
||||
import { servers } from './mapurlManage';
|
||||
import { useUiStore } from '@/store/modules/ui';
|
||||
|
||||
const mapOrchestrator = useMapOrchestrator();
|
||||
const uiStore = useUiStore();
|
||||
|
||||
const route = useRoute();
|
||||
const mapClass = MapClass.getInstance();
|
||||
@ -82,7 +84,11 @@ const init = async () => {
|
||||
const handleMapController = async (e: any, mapType: any) => {
|
||||
switch (e) {
|
||||
case 'dim':
|
||||
await mapClass.switchView(mapType);
|
||||
await mapOrchestrator.switchMapType(mapType, {
|
||||
popupContainer: popupRef.value,
|
||||
getIsHydroMenu: () => isShuiDianKaiFaMenu.value
|
||||
});
|
||||
uiStore.markMapSwitchCompleted();
|
||||
break;
|
||||
case 4: // 梯级流域显示
|
||||
tlyLayerVisible.value = !tlyLayerVisible.value;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -272,4 +272,8 @@ export class MapClass implements MapClassInterface {
|
||||
flyTopanto(position: number[], zoom: number): void {
|
||||
this.service.flyTopanto(position, zoom);
|
||||
}
|
||||
|
||||
getCurrentZoom(): number | undefined {
|
||||
return this.service.getCurrentZoom();
|
||||
}
|
||||
}
|
||||
|
||||
6
frontend/src/components/gis/map.d.ts
vendored
6
frontend/src/components/gis/map.d.ts
vendored
@ -124,6 +124,12 @@ export interface MapInterface {
|
||||
fitBounds(bbox, bearing): void;
|
||||
|
||||
flyTopanto(positon, zoom): void;
|
||||
|
||||
/**
|
||||
* 获取当前地图缩放级别语义
|
||||
* 2D 直接返回原生 zoom,3D 由相机高度换算后返回
|
||||
*/
|
||||
getCurrentZoom(): number | undefined;
|
||||
// /**
|
||||
// * 加载倾斜摄影数据
|
||||
// * @param HH3DUrlArray
|
||||
|
||||
@ -49,7 +49,7 @@ const MIN_ZOOM = 4.23;
|
||||
const MAX_ZOOM = 22;
|
||||
const INITIAL_ZOOM = 4.5;
|
||||
const BATCH_POPUP_MODE_ZOOM = 15;
|
||||
const FULL_DISPLAY_NO_COLLISION_ZOOM = 15;
|
||||
const FULL_DISPLAY_NO_COLLISION_ZOOM = Number.POSITIVE_INFINITY;
|
||||
|
||||
// 定义边界 [minX, minY, maxX, maxY] (Web Mercator 坐标)
|
||||
const BOUNDS_SW = [26.5, -9.99999999999929];
|
||||
@ -80,6 +80,8 @@ export class MapOl implements MapInterface {
|
||||
private regionMaskManager: RegionMaskManager;
|
||||
private isBatchPopupMode = false;
|
||||
private batchPopupRefreshFrameId: number | null = null;
|
||||
private batchPopupPendingRebuild = false;
|
||||
private batchPopupPostRenderListenerKey: any = null;
|
||||
private pointLayerStyleRefreshFrameId: number | null = null;
|
||||
private labelVisibilityRefreshFrameId: number | null = null;
|
||||
private labelVisibilityDebounceTimerId: number | null = null;
|
||||
@ -200,13 +202,14 @@ export class MapOl implements MapInterface {
|
||||
|
||||
this.map.on('pointerdrag', () => {
|
||||
if (this.isBatchPopupMode) {
|
||||
this.requestUpdateBatchPopups();
|
||||
this.ensureBatchPopupPostRenderSync();
|
||||
}
|
||||
});
|
||||
this.map.on('moveend', () => {
|
||||
this.clearBatchPopupPostRenderSync();
|
||||
this.requestRefreshPointLabelVisibility(false, 60);
|
||||
if (this.isBatchPopupMode) {
|
||||
this.requestUpdateBatchPopups();
|
||||
this.requestUpdateBatchPopups(false, true);
|
||||
}
|
||||
});
|
||||
|
||||
@ -291,8 +294,9 @@ export class MapOl implements MapInterface {
|
||||
*/
|
||||
private enableBatchPopupMode() {
|
||||
this.isBatchPopupMode = true;
|
||||
this.clearBatchPopupPostRenderSync();
|
||||
this.popupManager.clearBatchPopups();
|
||||
this.requestUpdateBatchPopups(true);
|
||||
this.requestUpdateBatchPopups(true, true);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -300,6 +304,8 @@ export class MapOl implements MapInterface {
|
||||
*/
|
||||
private disableBatchPopupMode() {
|
||||
this.isBatchPopupMode = false;
|
||||
this.batchPopupPendingRebuild = false;
|
||||
this.clearBatchPopupPostRenderSync();
|
||||
if (this.batchPopupRefreshFrameId !== null) {
|
||||
cancelAnimationFrame(this.batchPopupRefreshFrameId);
|
||||
this.batchPopupRefreshFrameId = null;
|
||||
@ -314,11 +320,40 @@ export class MapOl implements MapInterface {
|
||||
private updateBatchPopups() {
|
||||
const features = this.pointLayerManager.getFeaturesInViewport();
|
||||
// 传入样式检查器,过滤掉 declutter/距离过滤等隐藏的锚点
|
||||
this.popupManager.showPopupsForFeatures(features, feature => {
|
||||
const style = this.createPointStyle(feature);
|
||||
return style !== null;
|
||||
this.popupManager.showPopupsForFeatures(
|
||||
features,
|
||||
feature => {
|
||||
const style = this.createPointStyle(feature);
|
||||
return style !== null;
|
||||
},
|
||||
{
|
||||
rebuild: this.batchPopupPendingRebuild
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
private ensureBatchPopupPostRenderSync() {
|
||||
if (!this.map || this.batchPopupPostRenderListenerKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.batchPopupPostRenderListenerKey = this.map.on('postrender', () => {
|
||||
if (!this.isBatchPopupMode) {
|
||||
return;
|
||||
}
|
||||
this.popupManager.updateBatchPopupPositions(feature => {
|
||||
const style = this.createPointStyle(feature);
|
||||
return style !== null;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private clearBatchPopupPostRenderSync() {
|
||||
if (this.batchPopupPostRenderListenerKey) {
|
||||
unByKey(this.batchPopupPostRenderListenerKey);
|
||||
this.batchPopupPostRenderListenerKey = null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* 创建点样式 (模拟 Leaflet 的 DivIcon 效果,支持随缩放动态调整大小)
|
||||
*/
|
||||
@ -350,6 +385,7 @@ export class MapOl implements MapInterface {
|
||||
|
||||
const dynamicScale = this.getDynamicPointScale(currentZoom);
|
||||
const fontSize = this.getPointFontSize(dynamicScale);
|
||||
const engPriority = this.getFeatureEngRenderPriority(feature);
|
||||
const formattedLabelText = this.formatPointLabelText(labelText);
|
||||
const labelLineCount = formattedLabelText
|
||||
? formattedLabelText.split('\n').length
|
||||
@ -363,7 +399,10 @@ export class MapOl implements MapInterface {
|
||||
feature.get('_labelCollisionVisible') === true;
|
||||
const finalIconVisible = iconTargetVisible && iconCollisionVisible;
|
||||
const labelTargetVisible =
|
||||
finalIconVisible && !!formattedLabelText && labelCollisionVisible;
|
||||
!this.isBatchPopupMode &&
|
||||
finalIconVisible &&
|
||||
!!formattedLabelText &&
|
||||
labelCollisionVisible;
|
||||
|
||||
if (!finalIconVisible) {
|
||||
return null;
|
||||
@ -373,6 +412,7 @@ export class MapOl implements MapInterface {
|
||||
iconUrl,
|
||||
dynamicScale.toFixed(2),
|
||||
fontSize,
|
||||
engPriority,
|
||||
labelOffsetY,
|
||||
labelTargetVisible ? formattedLabelText : '',
|
||||
labelTargetVisible ? 1 : 0
|
||||
@ -384,6 +424,7 @@ export class MapOl implements MapInterface {
|
||||
|
||||
const styles: Style[] = [
|
||||
new Style({
|
||||
zIndex: engPriority,
|
||||
image: new Icon({
|
||||
src: iconUrl,
|
||||
scale: dynamicScale,
|
||||
@ -397,7 +438,7 @@ export class MapOl implements MapInterface {
|
||||
if (formattedLabelText && labelTargetVisible) {
|
||||
styles.push(
|
||||
new Style({
|
||||
zIndex: 101,
|
||||
zIndex: engPriority + 1,
|
||||
text: new Text({
|
||||
text: formattedLabelText,
|
||||
offsetY: labelOffsetY,
|
||||
@ -598,6 +639,7 @@ export class MapOl implements MapInterface {
|
||||
top: number;
|
||||
bottom: number;
|
||||
priority: number;
|
||||
engPriority: number;
|
||||
densityPriority: number;
|
||||
id: string;
|
||||
pixelX: number;
|
||||
@ -616,6 +658,7 @@ export class MapOl implements MapInterface {
|
||||
top: number;
|
||||
bottom: number;
|
||||
priority: number;
|
||||
engPriority: number;
|
||||
densityPriority: number;
|
||||
id: string;
|
||||
pixelX: number;
|
||||
@ -808,7 +851,7 @@ export class MapOl implements MapInterface {
|
||||
const dynamicScale = this.getDynamicPointScale(currentZoom);
|
||||
// 备注:图标碰撞盒适当小于视觉图标尺寸,允许相邻站点轻微贴近显示,
|
||||
// 避免像“小浪底/三门峡”这类近点被过早裁掉成只剩一个点。
|
||||
const iconCollisionSize = Math.max(12, 16 * dynamicScale);
|
||||
const iconCollisionSize = Math.max(14, 24 * dynamicScale);
|
||||
const iconPadding = 0;
|
||||
const iconLeft = entry.pixelX - iconCollisionSize / 2 - iconPadding;
|
||||
const iconRight = entry.pixelX + iconCollisionSize / 2 + iconPadding;
|
||||
@ -851,6 +894,7 @@ export class MapOl implements MapInterface {
|
||||
top: centerY - estimatedHeight / 2,
|
||||
bottom: centerY + estimatedHeight / 2,
|
||||
priority: Number(entry.feature.get('_nearbyPriority') || 9999),
|
||||
engPriority: this.getFeatureEngRenderPriority(entry.feature),
|
||||
densityPriority: entry.densityPriority,
|
||||
id: candidateId,
|
||||
pixelX: entry.pixelX,
|
||||
@ -863,6 +907,9 @@ export class MapOl implements MapInterface {
|
||||
});
|
||||
|
||||
candidates.sort((left, right) => {
|
||||
if (left.engPriority !== right.engPriority) {
|
||||
return right.engPriority - left.engPriority;
|
||||
}
|
||||
if (left.priority !== right.priority) {
|
||||
return left.priority - right.priority;
|
||||
}
|
||||
@ -951,6 +998,9 @@ export class MapOl implements MapInterface {
|
||||
});
|
||||
|
||||
const labelCandidates = [...candidates].sort((left, right) => {
|
||||
if (left.engPriority !== right.engPriority) {
|
||||
return right.engPriority - left.engPriority;
|
||||
}
|
||||
if (left.priority !== right.priority) {
|
||||
return left.priority - right.priority;
|
||||
}
|
||||
@ -1149,10 +1199,11 @@ export class MapOl implements MapInterface {
|
||||
}
|
||||
}
|
||||
|
||||
private requestUpdateBatchPopups(immediate = false) {
|
||||
private requestUpdateBatchPopups(immediate = false, rebuild = true) {
|
||||
if (!this.isBatchPopupMode) {
|
||||
return;
|
||||
}
|
||||
this.batchPopupPendingRebuild = this.batchPopupPendingRebuild || rebuild;
|
||||
|
||||
if (immediate) {
|
||||
if (this.batchPopupRefreshFrameId !== null) {
|
||||
@ -1160,6 +1211,7 @@ export class MapOl implements MapInterface {
|
||||
this.batchPopupRefreshFrameId = null;
|
||||
}
|
||||
this.updateBatchPopups();
|
||||
this.batchPopupPendingRebuild = false;
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1173,6 +1225,7 @@ export class MapOl implements MapInterface {
|
||||
return;
|
||||
}
|
||||
this.updateBatchPopups();
|
||||
this.batchPopupPendingRebuild = false;
|
||||
});
|
||||
}
|
||||
|
||||
@ -1326,6 +1379,27 @@ export class MapOl implements MapInterface {
|
||||
return densityDisplayRules.at(-1)?.minZoom ?? 0;
|
||||
}
|
||||
|
||||
private isFeatureEng(feature: Feature): boolean {
|
||||
const layerKey = String(feature.get('_layerKey') || '').toLowerCase();
|
||||
const sttpMap = String(
|
||||
feature.get('_sttpMap') || feature.get('sttpMap') || ''
|
||||
).toUpperCase();
|
||||
const sttpCode = String(
|
||||
feature.get('sttpCode') || feature.get('sttp') || ''
|
||||
).toUpperCase();
|
||||
|
||||
return (
|
||||
layerKey.includes('eng_point') ||
|
||||
sttpMap === 'ENG' ||
|
||||
sttpMap === 'ENG2' ||
|
||||
sttpCode === 'ENG'
|
||||
);
|
||||
}
|
||||
|
||||
private getFeatureEngRenderPriority(feature: Feature): number {
|
||||
return this.isFeatureEng(feature) ? 200 : 100;
|
||||
}
|
||||
|
||||
/**
|
||||
* 如果点位在当前视图中没有近邻点,则允许跳过静态密度门槛直接参与显示。
|
||||
* 这样像“戈兰滩”这类 distance 档位偏密、但当前周围实际很空的点也能出现。
|
||||
@ -1407,10 +1481,9 @@ export class MapOl implements MapInterface {
|
||||
const nearbyGroupId = feature.get('_nearbyGroupId');
|
||||
const groupSize = Number(feature.get('_nearbyGroupSize'));
|
||||
const priority = Number(feature.get('_nearbyPriority'));
|
||||
const replaceAtZoom = Number(feature.get('_nearbyReplaceAtZoom'));
|
||||
const expandAtZoom = Number(feature.get('_nearbyExpandAtZoom'));
|
||||
const showZoomMin = feature.get('_nearbyShowZoomMin');
|
||||
const showZoomMax = feature.get('_nearbyShowZoomMax');
|
||||
const expandAtZoom = feature.get('_nearbyExpandAtZoom');
|
||||
|
||||
if (!nearbyGroupId || !Number.isFinite(groupSize) || groupSize < 2) {
|
||||
return true;
|
||||
@ -1436,14 +1509,14 @@ export class MapOl implements MapInterface {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (Number.isFinite(expandAtZoom) && currentZoom >= expandAtZoom) {
|
||||
if (
|
||||
expandAtZoom !== null &&
|
||||
expandAtZoom !== undefined &&
|
||||
currentZoom >= Number(expandAtZoom)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (Number.isFinite(replaceAtZoom) && currentZoom >= replaceAtZoom) {
|
||||
return priority <= Math.min(2, groupSize);
|
||||
}
|
||||
|
||||
return priority === 1;
|
||||
}
|
||||
/**
|
||||
@ -2436,6 +2509,10 @@ export class MapOl implements MapInterface {
|
||||
);
|
||||
}
|
||||
|
||||
getCurrentZoom(): number | undefined {
|
||||
return this.view?.getZoom();
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成带完整圆边框的上半圆 Canvas
|
||||
* @param radius 半径(像素)
|
||||
@ -2496,6 +2573,7 @@ export class MapOl implements MapInterface {
|
||||
window.clearTimeout(this.labelVisibilityDebounceTimerId);
|
||||
this.labelVisibilityDebounceTimerId = null;
|
||||
}
|
||||
this.clearBatchPopupPostRenderSync();
|
||||
this.removeQueryLayer();
|
||||
this.regionMaskManager.destroy();
|
||||
|
||||
|
||||
@ -318,7 +318,7 @@ export class PointLayerManager {
|
||||
layerKey: string,
|
||||
matchValue: string,
|
||||
checked: boolean,
|
||||
fieldKey: string = 'anchoPointState'
|
||||
fieldKey = 'anchoPointState'
|
||||
): void {
|
||||
const vectorLayer = this.layerRegistry.get(layerKey);
|
||||
if (!vectorLayer) return;
|
||||
|
||||
@ -4,7 +4,10 @@ import OlMap from 'ol/Map';
|
||||
import Overlay from 'ol/Overlay';
|
||||
import VectorLayer from 'ol/layer/Vector';
|
||||
import VectorSource from 'ol/source/Vector';
|
||||
import { generatePopupHtml } from '@/utils/popupHtmlGenerator';
|
||||
import {
|
||||
generatePopupHtml,
|
||||
shouldPreferEng2Popup
|
||||
} from '@/utils/popupHtmlGenerator';
|
||||
|
||||
type PopupManagerOptions = {
|
||||
map: OlMap | null;
|
||||
@ -27,6 +30,11 @@ export class PopupManager {
|
||||
private map: OlMap | null;
|
||||
private popupOverlay: Overlay | null = null;
|
||||
private popupElement: HTMLElement | null = null;
|
||||
private batchPopupContainer: HTMLDivElement | null = null;
|
||||
private batchPopupItems: Array<{
|
||||
feature: Feature;
|
||||
element: HTMLDivElement;
|
||||
}> = [];
|
||||
private lastHoveredId: string | number | null = null;
|
||||
private animationFrameId: number | null = null;
|
||||
private getPointLayers: () => VectorLayer<VectorSource>[];
|
||||
@ -49,6 +57,11 @@ export class PopupManager {
|
||||
// 备注:初始化 Popup Overlay 并挂载到地图实例。
|
||||
initPopupOverlay(container: HTMLElement) {
|
||||
this.popupElement = container;
|
||||
this.popupElement.style.display = 'none';
|
||||
this.popupElement.style.removeProperty('position');
|
||||
this.popupElement.style.removeProperty('transform');
|
||||
this.popupElement.style.removeProperty('left');
|
||||
this.popupElement.style.removeProperty('top');
|
||||
this.popupElement.style.setProperty('pointer-events', 'none', 'important');
|
||||
this.popupMouseEnterHandler = () => {
|
||||
this.forceHidePopup();
|
||||
@ -190,7 +203,7 @@ export class PopupManager {
|
||||
|
||||
if (feature && coordinate) {
|
||||
const props = feature.getProperties();
|
||||
const popupHtml = props.popupHtml || generatePopupHtml(props);
|
||||
const popupHtml = this.getPopupHtml(props);
|
||||
|
||||
if (popupHtml) {
|
||||
this.popupElement.innerHTML = popupHtml;
|
||||
@ -204,22 +217,37 @@ export class PopupManager {
|
||||
this.popupElement.style.display = 'none';
|
||||
}
|
||||
|
||||
private getPopupHtml(props: Record<string, any>) {
|
||||
return props.popupHtml || generatePopupHtml(props);
|
||||
}
|
||||
|
||||
// 备注:批量显示多个要素的 Popup,使用绝对定位的 div 而非 Overlay。
|
||||
showPopupsForFeatures(
|
||||
features: Feature[],
|
||||
styleChecker?: (feature: Feature) => boolean
|
||||
styleChecker?: (feature: Feature) => boolean,
|
||||
options?: {
|
||||
rebuild?: boolean;
|
||||
}
|
||||
) {
|
||||
if (!this.map || !this.popupElement) return;
|
||||
|
||||
// 清除之前批量显示的 popups
|
||||
this.clearBatchPopups();
|
||||
const shouldRebuild = options?.rebuild !== false;
|
||||
|
||||
// 过滤:只显示图例可见的要素
|
||||
const visibleFeatures = features.filter(
|
||||
f => f.get('_legendVisible') !== false
|
||||
);
|
||||
|
||||
if (visibleFeatures.length === 0) return;
|
||||
if (visibleFeatures.length === 0) {
|
||||
this.clearBatchPopups();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!shouldRebuild && this.batchPopupItems.length > 0) {
|
||||
this.updateBatchPopupPositions(styleChecker);
|
||||
return;
|
||||
}
|
||||
|
||||
this.clearBatchPopups();
|
||||
|
||||
const mapElement = this.map.getTargetElement();
|
||||
const batchContainer = document.createElement('div');
|
||||
@ -234,6 +262,7 @@ export class PopupManager {
|
||||
z-index: 1000;
|
||||
`;
|
||||
batchContainer.id = 'batch-popup-container';
|
||||
this.batchPopupContainer = batchContainer;
|
||||
|
||||
// 先添加容器到 DOM,以便后续元素能正确测量尺寸
|
||||
mapElement.appendChild(batchContainer);
|
||||
@ -245,12 +274,6 @@ export class PopupManager {
|
||||
top: number;
|
||||
bottom: number;
|
||||
}> = [];
|
||||
let styleHiddenCount = 0;
|
||||
let blockedCount = 0;
|
||||
let renderedCount = 0;
|
||||
const renderedIds: Array<string | number | null> = [];
|
||||
const blockedIds: Array<string | number | null> = [];
|
||||
|
||||
visibleFeatures.forEach(feature => {
|
||||
const geom = feature.getGeometry();
|
||||
if (!geom || geom.getType() !== 'Point') return;
|
||||
@ -261,12 +284,15 @@ export class PopupManager {
|
||||
|
||||
// 检查样式是否隐藏(declutter、距离过滤等)
|
||||
if (styleChecker && !styleChecker(feature)) {
|
||||
styleHiddenCount += 1;
|
||||
return;
|
||||
}
|
||||
|
||||
const props = feature.getProperties();
|
||||
const popupHtml = props.popupHtml || generatePopupHtml(props);
|
||||
const popupHtml = shouldPreferEng2Popup(props)
|
||||
? generatePopupHtml(props, { forceEng2: true }) ||
|
||||
props.popupHtml ||
|
||||
generatePopupHtml(props)
|
||||
: this.getPopupHtml(props);
|
||||
if (!popupHtml) return;
|
||||
|
||||
// 创建与原始 popupElement 完全相同的样式
|
||||
@ -314,8 +340,10 @@ export class PopupManager {
|
||||
popupEl.style.visibility = 'visible';
|
||||
popupEl.style.left = `${popupX}px`;
|
||||
popupEl.style.top = `${popupY}px`;
|
||||
renderedCount += 1;
|
||||
renderedIds.push((feature.getId?.() as string | number | null) ?? null);
|
||||
this.batchPopupItems.push({
|
||||
feature,
|
||||
element: popupEl
|
||||
});
|
||||
|
||||
placedPopups.push({
|
||||
left: popupRect.left,
|
||||
@ -325,15 +353,76 @@ export class PopupManager {
|
||||
});
|
||||
} else {
|
||||
// 有碰撞,移除该 popup
|
||||
blockedCount += 1;
|
||||
blockedIds.push((feature.getId?.() as string | number | null) ?? null);
|
||||
batchContainer.removeChild(popupEl);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateBatchPopupPositions(styleChecker?: (feature: Feature) => boolean) {
|
||||
if (
|
||||
!this.map ||
|
||||
!this.batchPopupContainer ||
|
||||
this.batchPopupItems.length === 0
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const mapSize = this.map.getSize();
|
||||
const viewportWidth = mapSize?.[0] ?? 0;
|
||||
const viewportHeight = mapSize?.[1] ?? 0;
|
||||
const viewportPadding = 48;
|
||||
|
||||
this.batchPopupItems.forEach(({ feature, element }) => {
|
||||
if (feature.get('_legendVisible') === false) {
|
||||
element.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
if (styleChecker && !styleChecker(feature)) {
|
||||
element.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
const geom = feature.getGeometry();
|
||||
if (!geom || geom.getType() !== 'Point') {
|
||||
element.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
const coords = (geom as Point).getCoordinates();
|
||||
const pixel = this.map?.getPixelFromCoordinate(coords);
|
||||
if (!pixel) {
|
||||
element.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
const popupX = pixel[0];
|
||||
const popupY = pixel[1] - 10;
|
||||
if (
|
||||
popupX < -viewportPadding ||
|
||||
popupY < -viewportPadding ||
|
||||
popupX > viewportWidth + viewportPadding ||
|
||||
popupY > viewportHeight + viewportPadding
|
||||
) {
|
||||
element.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
|
||||
element.style.left = `${popupX}px`;
|
||||
element.style.top = `${popupY}px`;
|
||||
element.style.display = 'block';
|
||||
});
|
||||
}
|
||||
|
||||
// 备注:清除批量显示的 popups。
|
||||
clearBatchPopups() {
|
||||
this.batchPopupItems = [];
|
||||
if (this.batchPopupContainer) {
|
||||
this.batchPopupContainer.remove();
|
||||
this.batchPopupContainer = null;
|
||||
return;
|
||||
}
|
||||
|
||||
const existing = document.getElementById('batch-popup-container');
|
||||
if (existing) {
|
||||
existing.remove();
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
<template>
|
||||
<div class="map-filter-container">
|
||||
<div
|
||||
class="map-filter-container"
|
||||
:style="{ left: uiStore.mapType == '2D' ? '220px' : '20px' }"
|
||||
>
|
||||
<div class="toolbar">
|
||||
<a-form :model="formModel">
|
||||
<a-row :gutter="10">
|
||||
@ -110,7 +113,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, watch } from 'vue';
|
||||
import { ref, computed, watch, nextTick } from 'vue';
|
||||
import dayjs from 'dayjs';
|
||||
import { useRoute } from 'vue-router';
|
||||
import { MapClass } from '@/components/gis/map.class';
|
||||
@ -118,6 +121,7 @@ import { useMapOrchestrator } from '@/modules/map/application/map-orchestrator';
|
||||
import { useMapDataStore } from '@/modules/map/stores/map-data.store';
|
||||
import { useMapViewStore } from '@/modules/map/stores/map-view.store';
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
import { useUiStore } from '@/store/modules/ui';
|
||||
import { useMapStore } from '@/store/modules/map';
|
||||
import { DateSetting } from '@/utils/enumeration';
|
||||
import { getFishList, getFishPointList } from '@/api/map';
|
||||
@ -126,6 +130,7 @@ const route = useRoute();
|
||||
const mapOrchestrator = useMapOrchestrator();
|
||||
const mapDataStore = useMapDataStore();
|
||||
const mapViewStore = useMapViewStore();
|
||||
const uiStore = useUiStore();
|
||||
const jidiSelectEventStore = useJidiSelectEventStore();
|
||||
const mapStore = useMapStore();
|
||||
const mapClass = MapClass.getInstance();
|
||||
@ -156,6 +161,7 @@ const fishPointOptions = ref<any[]>([]);
|
||||
const fishSpeciesOptions = ref<{ label: string; value: string }[]>([]);
|
||||
const fishSpeciesLoading = ref(false);
|
||||
const fishPointLoading = ref(false);
|
||||
const refocusTimer = ref<ReturnType<typeof setTimeout> | null>(null);
|
||||
|
||||
const searchTimeRange = ref([
|
||||
dayjs(mapViewStore.searchTimeRange[0]).format('YYYY-MM-DD HH:mm:ss'),
|
||||
@ -623,15 +629,48 @@ const handleCapacityChange = (value: string) => {
|
||||
mapOrchestrator.changeEngPointCapacity(value);
|
||||
};
|
||||
|
||||
// 备注:搜索定位只分发定位命令,不在筛选组件里直接操作地图实例。
|
||||
const handleAnchorPointChange = (value: string) => {
|
||||
if (!value) return;
|
||||
const clearRefocusTimer = () => {
|
||||
if (!refocusTimer.value) {
|
||||
return;
|
||||
}
|
||||
clearTimeout(refocusTimer.value);
|
||||
refocusTimer.value = null;
|
||||
};
|
||||
|
||||
const focusSelectedAnchorPoint = () => {
|
||||
const selectedValue = anchorPointSelectValue.value;
|
||||
if (!selectedValue) {
|
||||
return;
|
||||
}
|
||||
|
||||
mapOrchestrator.focusPoint(
|
||||
value,
|
||||
selectedValue,
|
||||
14,
|
||||
isFishSurveyMode.value ? anchorPointOptions.value : undefined
|
||||
);
|
||||
};
|
||||
|
||||
// 备注:搜索定位只分发定位命令,不在筛选组件里直接操作地图实例。
|
||||
const handleAnchorPointChange = (value: string) => {
|
||||
if (!value) return;
|
||||
focusSelectedAnchorPoint();
|
||||
};
|
||||
|
||||
watch(
|
||||
() => uiStore.mapSwitchCompletedTick,
|
||||
async (newVal, oldVal) => {
|
||||
if (newVal === oldVal || !anchorPointSelectValue.value) {
|
||||
return;
|
||||
}
|
||||
|
||||
await nextTick();
|
||||
clearRefocusTimer();
|
||||
refocusTimer.value = setTimeout(() => {
|
||||
refocusTimer.value = null;
|
||||
focusSelectedAnchorPoint();
|
||||
}, 120);
|
||||
}
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref } from 'vue';
|
||||
import { useUiStore } from '@/store/modules/ui';
|
||||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||||
const loading = ref(false);
|
||||
const isOpen = ref(true);
|
||||
const JidiSelectEventStore = useJidiSelectEventStore();
|
||||
const uiStore = useUiStore();
|
||||
|
||||
const jidiDataNum = ref(9);
|
||||
const itemClick = (index: any) => {
|
||||
@ -13,7 +15,7 @@ onMounted(() => {});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="jidiSelectorMod">
|
||||
<div class="jidiSelectorMod" v-if="uiStore.mapType == '2D'">
|
||||
<a-spin :spinning="JidiSelectEventStore.loading">
|
||||
<div
|
||||
class="qgc-dropdown-select"
|
||||
|
||||
@ -43,6 +43,7 @@ const HYDRO_DYNAMIC_LAYER_KEYS = [
|
||||
];
|
||||
const ENG_POINT_LAYER_KEY = 'eng_point';
|
||||
const ENG_POINT_MEDIUM_VISIBLE_ZOOM = 7.5;
|
||||
const CESIUM_ZOOM_SYNC_DEBOUNCE_MS = 80;
|
||||
const LARGE_ENG_LEGEND_PREFIX = 'large_eng_';
|
||||
const MID_ENG_LEGEND_PREFIX = 'mid_eng_';
|
||||
|
||||
@ -55,12 +56,20 @@ export const useMapOrchestrator = () => {
|
||||
const mapViewStore = useMapViewStore();
|
||||
const jidiSelectEventStore = useJidiSelectEventStore();
|
||||
let activePageLoadRequestId = 0;
|
||||
let zoomListenerKey: any = null;
|
||||
let removeZoomListener: (() => void) | null = null;
|
||||
let stopBaseSelectionWatch: WatchStopHandle | null = null;
|
||||
const initializedBaseLayerKeys = new Set<string>();
|
||||
const baseSelectionDebounceTimer = ref<ReturnType<typeof setTimeout> | null>(
|
||||
null
|
||||
);
|
||||
const zoomSyncDebounceTimer = ref<ReturnType<typeof setTimeout> | null>(null);
|
||||
let latestHydroMenuGetter: (() => boolean) | null = null;
|
||||
let currentMapType: '2D' | '3D' = '2D';
|
||||
let hydroMenuDefaultCheckedKeys = new Set<string>();
|
||||
let hydroDynamicLayerSyncTask: {
|
||||
visible: boolean;
|
||||
promise: Promise<string[] | void>;
|
||||
} | null = null;
|
||||
|
||||
// 备注:统一获取当前地图视图对象,避免在多个组件里重复兼容 2D/3D view 访问方式。
|
||||
const getCurrentView = () => {
|
||||
@ -69,7 +78,14 @@ export const useMapOrchestrator = () => {
|
||||
|
||||
// 备注:统一获取当前地图缩放级别,供页面切换和缩放监听复用。
|
||||
const getCurrentZoom = (): number | undefined => {
|
||||
return getCurrentView()?.getZoom?.();
|
||||
return mapClass.getCurrentZoom();
|
||||
};
|
||||
|
||||
const clearZoomSyncDebounceTimer = () => {
|
||||
if (zoomSyncDebounceTimer.value) {
|
||||
clearTimeout(zoomSyncDebounceTimer.value);
|
||||
zoomSyncDebounceTimer.value = null;
|
||||
}
|
||||
};
|
||||
|
||||
// 备注:统一把当前页面中的 GIS 基础图层初始化到地图实例,避免在图层树组件里做配置解析和加层编排。
|
||||
@ -95,7 +111,20 @@ export const useMapOrchestrator = () => {
|
||||
}
|
||||
|
||||
if (item.key === 'customBaseLayer') {
|
||||
sessionStorage.setItem('customBaseLayer', JSON.stringify(item.config));
|
||||
const serializableBaseLayerConfig = {
|
||||
key: item.config?.key,
|
||||
id: item.config?.id,
|
||||
name: item.config?.name,
|
||||
type: item.config?.type,
|
||||
url: item.config?.url,
|
||||
url_3d: item.config?.url_3d,
|
||||
layers: item.config?.layers,
|
||||
rasteropacity: item.config?.rasteropacity
|
||||
};
|
||||
sessionStorage.setItem(
|
||||
'customBaseLayer',
|
||||
JSON.stringify(serializableBaseLayerConfig)
|
||||
);
|
||||
}
|
||||
|
||||
if (!initializedBaseLayerKeys.has(item.key)) {
|
||||
@ -154,6 +183,9 @@ export const useMapOrchestrator = () => {
|
||||
|
||||
// 先设置图层数据(更新 checkedLayerKeys),再设置图例数据
|
||||
if (layerConfig.length > 0) {
|
||||
hydroMenuDefaultCheckedKeys = new Set(
|
||||
mapConfigStore.extractCheckedLayerKeys(layerConfig)
|
||||
);
|
||||
mapStore.setLayerData(layerConfig);
|
||||
ensureBaseLayersInitialized(layerConfig);
|
||||
|
||||
@ -229,10 +261,58 @@ export const useMapOrchestrator = () => {
|
||||
await mapClass.init(container);
|
||||
|
||||
if (popupContainer) {
|
||||
const popupHost = container.parentElement;
|
||||
if (popupHost && popupContainer.parentElement !== popupHost) {
|
||||
popupHost.appendChild(popupContainer);
|
||||
}
|
||||
popupContainer.style.display = 'none';
|
||||
mapClass.initPopupOverlay(popupContainer);
|
||||
}
|
||||
};
|
||||
|
||||
const replayCurrentMapState = async (
|
||||
options: Pick<InitializeOptions, 'popupContainer'> & {
|
||||
getIsHydroMenu: () => boolean;
|
||||
}
|
||||
) => {
|
||||
initializedBaseLayerKeys.clear();
|
||||
|
||||
const currentZoom = getCurrentZoom();
|
||||
if (currentZoom !== undefined) {
|
||||
mapViewStore.setCurrentZoomLevel(currentZoom);
|
||||
}
|
||||
|
||||
if (options.popupContainer) {
|
||||
const container = document.getElementById('mapContainer');
|
||||
const popupHost = container?.parentElement || null;
|
||||
if (popupHost && options.popupContainer.parentElement !== popupHost) {
|
||||
popupHost.appendChild(options.popupContainer);
|
||||
}
|
||||
options.popupContainer.style.display = 'none';
|
||||
mapClass.initPopupOverlay(options.popupContainer);
|
||||
}
|
||||
|
||||
ensureBaseLayersInitialized(mapStore.layerData || []);
|
||||
|
||||
const checkedKeys = mapViewStore.getCheckedLayerKeys();
|
||||
await mapStore.updateLayerData(checkedKeys, true);
|
||||
mapStore.setSelectedLegendData();
|
||||
await syncZoomSensitiveState({
|
||||
isHydroMenu: options.getIsHydroMenu(),
|
||||
refreshEngPoint: true
|
||||
});
|
||||
|
||||
if (currentMapType === '2D') {
|
||||
const selectedBaseId = mapViewStore.selectedBaseId;
|
||||
mapClass.jdPanelControlShowAndHidden(
|
||||
selectedBaseId || 'all',
|
||||
!!selectedBaseId
|
||||
);
|
||||
}
|
||||
|
||||
bindZoomListener(options.getIsHydroMenu);
|
||||
};
|
||||
|
||||
// 备注:统一完成首屏页面配置加载,供挂载阶段和后续页面重载复用。
|
||||
const initialize = async ({
|
||||
container,
|
||||
@ -249,20 +329,18 @@ export const useMapOrchestrator = () => {
|
||||
|
||||
// 备注:统一绑定地图缩放监听,让页面组件只负责触发初始化,不再直接处理缩放联动细节。
|
||||
const bindZoomListener = (getIsHydroMenu: () => boolean) => {
|
||||
if (zoomListenerKey) {
|
||||
unByKey(zoomListenerKey);
|
||||
zoomListenerKey = null;
|
||||
if (removeZoomListener) {
|
||||
removeZoomListener();
|
||||
removeZoomListener = null;
|
||||
}
|
||||
|
||||
const view = getCurrentView();
|
||||
if (!view?.on) return;
|
||||
clearZoomSyncDebounceTimer();
|
||||
|
||||
const currentZoom = getCurrentZoom();
|
||||
if (currentZoom !== undefined) {
|
||||
mapViewStore.setCurrentZoomLevel(currentZoom);
|
||||
}
|
||||
|
||||
zoomListenerKey = view.on('change:resolution', async () => {
|
||||
const emitZoomChange = async () => {
|
||||
const zoom = getCurrentZoom();
|
||||
if (zoom === undefined) return;
|
||||
|
||||
@ -271,7 +349,36 @@ export const useMapOrchestrator = () => {
|
||||
zoom,
|
||||
getIsHydroMenu()
|
||||
);
|
||||
};
|
||||
|
||||
if (currentMapType === '3D') {
|
||||
const removeCesiumListener =
|
||||
mapClass.view?.camera?.changed?.addEventListener(() => {
|
||||
clearZoomSyncDebounceTimer();
|
||||
zoomSyncDebounceTimer.value = setTimeout(() => {
|
||||
zoomSyncDebounceTimer.value = null;
|
||||
void emitZoomChange();
|
||||
}, CESIUM_ZOOM_SYNC_DEBOUNCE_MS);
|
||||
});
|
||||
|
||||
if (typeof removeCesiumListener === 'function') {
|
||||
removeZoomListener = () => {
|
||||
clearZoomSyncDebounceTimer();
|
||||
removeCesiumListener();
|
||||
};
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const view = getCurrentView();
|
||||
if (!view?.on) return;
|
||||
|
||||
const zoomListenerKey = view.on('change:resolution', () => {
|
||||
void emitZoomChange();
|
||||
});
|
||||
removeZoomListener = () => {
|
||||
unByKey(zoomListenerKey);
|
||||
};
|
||||
};
|
||||
|
||||
// 备注:统一绑定基地切换事件,让页面组件不再直接 watch 外部基地选择源。
|
||||
@ -308,10 +415,34 @@ export const useMapOrchestrator = () => {
|
||||
getIsHydroMenu: () => boolean;
|
||||
}
|
||||
) => {
|
||||
currentMapType = '2D';
|
||||
latestHydroMenuGetter = options.getIsHydroMenu;
|
||||
await initializeMapShell(options);
|
||||
bindBaseSelection();
|
||||
bindZoomListener(options.getIsHydroMenu);
|
||||
await loadPage({ pageKey: options.pageKey, isInitialLoad: true });
|
||||
await syncZoomSensitiveState({
|
||||
isHydroMenu: options.getIsHydroMenu(),
|
||||
refreshEngPoint: true
|
||||
});
|
||||
};
|
||||
|
||||
const switchMapType = async (
|
||||
type: '2D' | '3D',
|
||||
options: Pick<InitializeOptions, 'popupContainer'> & {
|
||||
getIsHydroMenu?: () => boolean;
|
||||
} = {}
|
||||
) => {
|
||||
const hydroMenuGetter =
|
||||
options.getIsHydroMenu || latestHydroMenuGetter || (() => false);
|
||||
latestHydroMenuGetter = hydroMenuGetter;
|
||||
currentMapType = type;
|
||||
|
||||
await mapClass.switchView(type);
|
||||
await replayCurrentMapState({
|
||||
popupContainer: options.popupContainer,
|
||||
getIsHydroMenu: hydroMenuGetter
|
||||
});
|
||||
};
|
||||
|
||||
// 备注:统一处理菜单切换后的页面配置重载,供页面 watch 直接复用。
|
||||
@ -361,47 +492,91 @@ export const useMapOrchestrator = () => {
|
||||
(layer: any) => layer?.url && !mapDataStore.hasPointLayerData(layer.key)
|
||||
);
|
||||
|
||||
for (const layer of layersToLoad) {
|
||||
await mapStore.loadLayerData(layer);
|
||||
if (layersToLoad.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
await Promise.all(layersToLoad.map(layer => mapStore.loadLayerData(layer)));
|
||||
};
|
||||
|
||||
// 备注:统一控制水电开发菜单下的动态图层增删,避免页面组件直接操作加载和勾选细节。
|
||||
const syncHydroDynamicLayers = async (visible: boolean) => {
|
||||
const currentCheckedKeys = mapViewStore.getCheckedLayerKeys();
|
||||
const pageDefaultCheckedKeys = new Set(
|
||||
mapConfigStore.extractCheckedLayerKeys(mapStore.layerData || [])
|
||||
);
|
||||
if (
|
||||
hydroDynamicLayerSyncTask &&
|
||||
hydroDynamicLayerSyncTask.visible === visible
|
||||
) {
|
||||
return hydroDynamicLayerSyncTask.promise;
|
||||
}
|
||||
|
||||
if (visible) {
|
||||
const layersToAdd = HYDRO_DYNAMIC_LAYER_KEYS.filter(
|
||||
key => !currentCheckedKeys.includes(key)
|
||||
);
|
||||
const task = (async () => {
|
||||
const currentCheckedKeys = mapViewStore.getCheckedLayerKeys();
|
||||
|
||||
if (layersToAdd.length === 0) {
|
||||
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 hydroMenuDefaultCheckedKeys.has(key);
|
||||
});
|
||||
|
||||
if (nextCheckedKeys.length === currentCheckedKeys.length) {
|
||||
return currentCheckedKeys;
|
||||
}
|
||||
|
||||
await ensureDynamicLayersLoaded(layersToAdd);
|
||||
return applyLayerSelection({
|
||||
checkedKeys: [...currentCheckedKeys, ...layersToAdd]
|
||||
checkedKeys: nextCheckedKeys
|
||||
});
|
||||
}
|
||||
|
||||
const nextCheckedKeys = currentCheckedKeys.filter(key => {
|
||||
if (!HYDRO_DYNAMIC_LAYER_KEYS.includes(key)) {
|
||||
return true;
|
||||
})().finally(() => {
|
||||
if (hydroDynamicLayerSyncTask?.promise === task) {
|
||||
hydroDynamicLayerSyncTask = null;
|
||||
}
|
||||
return pageDefaultCheckedKeys.has(key);
|
||||
});
|
||||
|
||||
if (nextCheckedKeys.length === currentCheckedKeys.length) {
|
||||
return currentCheckedKeys;
|
||||
hydroDynamicLayerSyncTask = {
|
||||
visible,
|
||||
promise: task
|
||||
};
|
||||
return task;
|
||||
};
|
||||
|
||||
// 备注:统一按当前缩放对齐首页菜单的中型站过滤和 12 级动态图层状态。
|
||||
const syncZoomSensitiveState = async ({
|
||||
isHydroMenu,
|
||||
refreshEngPoint = false
|
||||
}: {
|
||||
isHydroMenu: boolean;
|
||||
refreshEngPoint?: boolean;
|
||||
}) => {
|
||||
const currentZoom = getCurrentZoom();
|
||||
if (currentZoom !== undefined) {
|
||||
mapViewStore.setCurrentZoomLevel(currentZoom);
|
||||
}
|
||||
|
||||
return applyLayerSelection({
|
||||
checkedKeys: nextCheckedKeys
|
||||
});
|
||||
if (
|
||||
isHydroMenu &&
|
||||
refreshEngPoint &&
|
||||
mapViewStore.getCheckedLayerKeys().includes(ENG_POINT_LAYER_KEY)
|
||||
) {
|
||||
mapStore.refreshPointLayerDisplayData(ENG_POINT_LAYER_KEY);
|
||||
}
|
||||
|
||||
await syncHydroDynamicLayers(
|
||||
!!isHydroMenu && currentZoom !== undefined && currentZoom >= 12
|
||||
);
|
||||
};
|
||||
|
||||
// 备注:统一处理地图缩放联动,收口水电开发菜单下的动态图层阈值切换逻辑。
|
||||
@ -419,6 +594,7 @@ export const useMapOrchestrator = () => {
|
||||
currentZoom <= ENG_POINT_MEDIUM_VISIBLE_ZOOM);
|
||||
|
||||
if (
|
||||
isHydroMenu &&
|
||||
crossedEngPointMediumThreshold &&
|
||||
mapViewStore.getCheckedLayerKeys().includes(ENG_POINT_LAYER_KEY)
|
||||
) {
|
||||
@ -454,13 +630,10 @@ export const useMapOrchestrator = () => {
|
||||
}
|
||||
|
||||
return reloadPage(pageKey).then(async () => {
|
||||
const currentZoom = getCurrentZoom();
|
||||
if (isHydroMenu && currentZoom !== undefined && currentZoom >= 12) {
|
||||
await syncHydroDynamicLayers(true);
|
||||
return;
|
||||
}
|
||||
|
||||
await syncHydroDynamicLayers(false);
|
||||
await syncZoomSensitiveState({
|
||||
isHydroMenu,
|
||||
refreshEngPoint: true
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
@ -507,10 +680,12 @@ export const useMapOrchestrator = () => {
|
||||
const changeBaseId = (baseId: string) => {
|
||||
const nextBaseId = !baseId || baseId === 'all' ? '' : baseId;
|
||||
mapViewStore.setSelectedBaseId(nextBaseId);
|
||||
mapClass.jdPanelControlShowAndHidden(
|
||||
baseId || 'all',
|
||||
baseId !== 'all' && !!baseId
|
||||
);
|
||||
if (currentMapType === '2D') {
|
||||
mapClass.jdPanelControlShowAndHidden(
|
||||
baseId || 'all',
|
||||
baseId !== 'all' && !!baseId
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// 备注:统一处理时间范围变更,并触发相关图层数据重载。
|
||||
@ -570,30 +745,47 @@ export const useMapOrchestrator = () => {
|
||||
mapDataStore.pointData.find(pointMatcher) ||
|
||||
fallbackPoints.find(pointMatcher);
|
||||
if (!targetPoint?.lgtd || !targetPoint?.lttd) return;
|
||||
|
||||
const legendState = String(targetPoint?.anchoPointState || '');
|
||||
if (legendState && mapViewStore.getLegendChecked(legendState) !== 1) {
|
||||
mapStore.updateLegendChecked(legendState, 1);
|
||||
}
|
||||
|
||||
const layerKey = String(targetPoint?.layerKey || '');
|
||||
if (layerKey && mapClass.hasLayer(layerKey)) {
|
||||
mapClass.mdLayerTreeShowOrHidden(layerKey, true);
|
||||
}
|
||||
|
||||
mapClass.flyTopanto([targetPoint.lgtd, targetPoint.lttd], zoom);
|
||||
};
|
||||
|
||||
// 备注:统一释放地图页面生命周期里注册的监听,避免页面卸载后残留联动。
|
||||
const unmountView = () => {
|
||||
if (zoomListenerKey) {
|
||||
unByKey(zoomListenerKey);
|
||||
zoomListenerKey = null;
|
||||
if (removeZoomListener) {
|
||||
removeZoomListener();
|
||||
removeZoomListener = null;
|
||||
}
|
||||
clearZoomSyncDebounceTimer();
|
||||
|
||||
if (stopBaseSelectionWatch) {
|
||||
stopBaseSelectionWatch();
|
||||
stopBaseSelectionWatch = null;
|
||||
}
|
||||
|
||||
if (baseSelectionDebounceTimer.value) {
|
||||
clearTimeout(baseSelectionDebounceTimer.value);
|
||||
baseSelectionDebounceTimer.value = null;
|
||||
}
|
||||
|
||||
latestHydroMenuGetter = null;
|
||||
currentMapType = '2D';
|
||||
initializedBaseLayerKeys.clear();
|
||||
hydroMenuDefaultCheckedKeys.clear();
|
||||
};
|
||||
|
||||
return {
|
||||
initialize,
|
||||
mountView,
|
||||
switchMapType,
|
||||
unmountView,
|
||||
loadPage,
|
||||
reloadPage,
|
||||
|
||||
@ -5,6 +5,7 @@ export const useUiStore = defineStore('ui', () => {
|
||||
// 右侧抽屉状态
|
||||
const drawerOpen = ref(true);
|
||||
const mapType = ref('2D');
|
||||
const mapSwitchCompletedTick = ref(0);
|
||||
|
||||
// 切换抽屉状态
|
||||
const toggleDrawer = () => {
|
||||
@ -16,10 +17,16 @@ export const useUiStore = defineStore('ui', () => {
|
||||
drawerOpen.value = open;
|
||||
};
|
||||
|
||||
const markMapSwitchCompleted = () => {
|
||||
mapSwitchCompletedTick.value += 1;
|
||||
};
|
||||
|
||||
return {
|
||||
drawerOpen,
|
||||
toggleDrawer,
|
||||
setDrawerOpen,
|
||||
mapType
|
||||
mapType,
|
||||
mapSwitchCompletedTick,
|
||||
markMapSwitchCompleted
|
||||
};
|
||||
});
|
||||
|
||||
@ -2642,16 +2642,19 @@ const timegj = [
|
||||
* @param props - 锚点数据对象
|
||||
* @returns HTML 字符串
|
||||
*/
|
||||
export function generatePopupHtml(props: any): string {
|
||||
export function generatePopupHtml(
|
||||
props: any,
|
||||
options?: {
|
||||
forceEng2?: boolean;
|
||||
}
|
||||
): string {
|
||||
const p = props;
|
||||
const dzFdl = getUnitConfigByCode('Other', 'FDLYKW')?.unit;
|
||||
const dzZjrl = getUnitConfigByCode('Other', 'ZJRL')?.unit;
|
||||
const title = p?.ennm || p?.stnm;
|
||||
const titleClass = getTitleClass(title);
|
||||
|
||||
let newsttpMap = p.sttpMap;
|
||||
console.log('newsttpMap', newsttpMap);
|
||||
if (p.qecInterval) {
|
||||
if (options?.forceEng2) {
|
||||
newsttpMap = 'eng2';
|
||||
}
|
||||
let html = '';
|
||||
@ -2714,7 +2717,7 @@ export function generatePopupHtml(props: any): string {
|
||||
} else {
|
||||
lastTmEngEqDataVo = p.lastTmEngEqDataVo;
|
||||
}
|
||||
if (!lastTmEngEqDataVo?.qo && !lastTmEngEqDataVo?.qi) return '';
|
||||
// if (!lastTmEngEqDataVo?.qo && !lastTmEngEqDataVo?.qi) return '';
|
||||
|
||||
const qecLimitVal = lastTmEngEqDataVo?.qecLimit;
|
||||
let qecLimitDisplay = '-';
|
||||
@ -3587,3 +3590,29 @@ export function generatePopupHtml(props: any): string {
|
||||
|
||||
return html ? `<div>${html}</div>` : '';
|
||||
}
|
||||
|
||||
export function getNormalizedPopupSttpMap(props: any): string {
|
||||
return String(props?._sttpMap || props?.sttpMap || props?.popupSttpMap || '')
|
||||
.trim()
|
||||
.toUpperCase();
|
||||
}
|
||||
|
||||
export function isEngPopupFamily(props: any): boolean {
|
||||
return getNormalizedPopupSttpMap(props).startsWith('ENG');
|
||||
}
|
||||
|
||||
export function shouldPreferEng2Popup(
|
||||
props: any,
|
||||
options?: {
|
||||
forceEng2?: boolean;
|
||||
}
|
||||
): boolean {
|
||||
if (options?.forceEng2) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (
|
||||
isEngPopupFamily(props) &&
|
||||
(!!props?.lastTmEngEqDataVo || !!props?.qecInterval)
|
||||
);
|
||||
}
|
||||
|
||||
@ -58,7 +58,6 @@ service.interceptors.response.use(
|
||||
message.error(response.data.msg || '请求失败');
|
||||
setTimeout(() => {
|
||||
localStorage.clear();
|
||||
router.push('/login');
|
||||
window.location.href = '/';
|
||||
}, 1000);
|
||||
return;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user