WholeProcessPlatform/frontend/docs/地图模块现状与改造方案.md

1159 lines
33 KiB
Markdown
Raw Normal View History

# 地图模块现状与改造方案
## 1. 文档目的
本文档用于完整梳理当前地图模块的技术实现、业务能力、数据流转、关键规则、已知问题与性能瓶颈,并给出可分阶段落地的重构方案。目标是:
- 让地图模块职责边界清晰,便于后续新增功能;
- 降低组件、Store、地图引擎之间的耦合
- 优化基础图层、锚点、图例、筛选、基地裁切等功能的响应速度;
- 为后续 2D/3D 扩展、复杂专题图层、更多筛选维度、更多交互能力预留稳定结构。
---
## 2. 当前模块范围
当前地图模块覆盖以下业务能力:
- 地图初始化与 2D/3D 切换;
- GeoServer 基础底图、叠加图层、区域矢量图层加载;
- Java 后端锚点数据请求、缓存、渲染;
- 右侧图层树勾选控制图层显示与隐藏;
- 左侧图例基于图层选中结果动态生成;
- 图例项点击控制锚点显隐;
- 基地选择后底图裁切与区域外锚点隐藏;
- 地图悬停 Popup、点击详情弹窗
- 搜索、时间筛选、容量筛选、基地筛选;
- 某些业务图层的互斥显示与特殊联动;
- 局部专题菜单下的缩放级别联动动态图层。
---
## 3. 当前技术实现概览
## 3.1 技术栈
- 地图引擎OpenLayers 作为 2D 主引擎Cesium 作为 3D 引擎入口;
- 状态管理Pinia
- UI 框架Vue 3 + `script setup` + Ant Design Vue
- 数据来源:
- GeoServer基础底图、WMTS、XYZ、GeoJSON 边界;
- Java 后端接口:图层配置、图例配置、锚点数据;
- 业务配置:
- 图层配置与图例配置来自后端;
- 锚点接口映射规则由 `src/store/modules/GisUrlList.ts` 维护。
## 3.2 核心文件职责
- `src/components/gis/GisView.vue`
- 地图容器入口;
- 地图初始化;
- 拉取图层配置与图例配置;
- 监听菜单切换、基地切换、缩放联动;
- 挂载 `MapLegend`、`MapFilter`、`MapController`、`BaseLayerSwitcher`。
- `src/components/gis/map.class.ts`
- 地图能力门面类;
- 对外屏蔽 OpenLayers / Cesium 差异;
- 当前主要起到转发作用。
- `src/components/gis/map.ol.ts`
- OpenLayers 主实现;
- 负责底图、锚点图层、Popup、图层显隐、区域裁切、量算、定位等
- 当前承担了过多业务规则与渲染控制逻辑。
- `src/store/modules/map.ts`
- 地图图层树、图例、锚点、缓存的核心 Store
- 负责图层选中状态、图例选中状态、锚点请求、缓存、图例联动、图层联动;
- 当前是业务与渲染耦合最重的文件之一。
- `src/components/mapController/LayerController.vue`
- 图层树 UI
- 负责初始基础图层加载;
- 勾选事件直接调用 Store 更新图层与图例状态;
- 包含多个互斥业务规则。
- `src/components/mapLegend/index.vue`
- 图例展示;
- 基于当前选中图层动态显示;
- 图例点击后直接驱动地图锚点显隐。
- `src/components/mapFilter/index.vue`
- 搜索、基地筛选、容量筛选、时间筛选;
- 直接依赖 `mapStore` 和地图实例;
- 部分筛选逻辑通过直接修改图例和锚点实现。
- `src/components/BaseLayerSwitcher/index.vue`
- 底图模式切换器;
- 切换矢量 / 地形 / 影像底图。
- `src/components/gis/gisUtils.ts`
- 图层结构拍平;
- 基础图层 URL 处理;
- 图例数据转对象;
- 若干地图布局相关工具。
- `src/api/map.ts`
- 拉取地图配置和图例配置。
---
## 4. 当前业务能力与逻辑链路
## 4.1 初始化流程
当前初始化主要发生在 `GisView.vue`
1. 调用 `MapClass.init()` 初始化 OpenLayers
2. 调用 `initPopupOverlay()` 挂载悬停 Popup
3. 调用 `fetchMapConfigs()` 并行请求:
- 地图图层配置;
- 全量图例配置;
- 当前页面图例配置;
4. `mapStore.setLegendData()` 初始化图例原始数据与映射;
5. `mapStore.setLayerData()` 初始化图层树和选中图层 key
6. `mapStore.loadAllLayerData()` 递归加载所有有 URL 的图层数据;
7. `loadLayerData()` 将锚点数据写入缓存并直接调用 `mapClass.addInitDataLayer()` 预创建锚点图层;
8. 全部锚点加载完成后,再调用 `updateLayerData()` 统一控制初始显示状态;
9. 图例根据当前选中的图层动态生成并展示。
### 现状特点
- 当前是“初始化阶段一次性预加载大多数锚点图层,再通过显隐控制”的策略;
- 图例显示被设计为“锚点完成后再显示”;
- 地图实例、Store、UI 组件三方互相直接调用。
## 4.2 基础图层加载与切换
基础底图主要由 `LayerController.vue``map.ol.ts` 协同完成:
- `LayerController.vue` 中监听图层树配置;
- 对类型为 `GISMap` 的节点解析 `paramJson`,通过 `getMapConfig()` 补齐 GeoServer 地址;
- 调用 `mapClass.addBaseDataLayer()` 初始化基础图层;
- 同时调用 `controlBaseLayerTreeShowAndHidden()` 控制显隐;
- `BaseLayerSwitcher` 再通过 `baseLayerSwitcher()` 在矢量 / 地形 / 影像底图之间切换。
当前基础图层类型主要包括:
- WMTSGeoServer WMTS 瓦片;
- XYZ例如 DEM 或其他切片图层;
- vector部分矢量底图或区域配置。
### 自然保护区图层特点
- 属于叠加在底图之上的 GIS 图层;
- 显隐仍通过基础图层控制逻辑处理;
- 与区域裁切场景存在较强耦合。
## 4.3 图层树勾选控制
右侧图层树由 `LayerController.vue` 驱动:
- 树节点勾选结果通过 `broadcast()` 调用 `mapStore.updateLayerData()`
- 图层树中包含多种特殊规则:
- 视频监控站与 AI 视频监控站互斥;
- 环保设施和环保设施在建互斥;
- 珍稀鱼类与沿程鱼类互斥;
- 初始选中状态通过递归遍历图层树生成。
### 现状问题
- 互斥逻辑分散在 UI 组件和 Store 两处;
- “图层树状态”与“地图真实状态”并非单向流;
- 图层节点既承担展示,又携带业务配置与运行态字段。
## 4.4 锚点数据加载
锚点数据由 `mapStore.loadAllLayerData()``loadLayerData()` 负责:
- 遍历图层树中全部带 URL 的图层;
- 使用 `GisUrlList.ts` 将图层 key / title / url 映射成真实请求地址、筛选条件、排序规则;
- 请求 Java 接口;
- 将返回记录补充:
- `iconCode`
- `code`
- `_id`
- `tm`
- 然后缓存到:
- `pointDataCache[layerKey]`
- `layer.data`
- 合并后的 `pointData`
- 若地图上还不存在该图层,则直接调用 `mapClass.addInitDataLayer()` 创建 `VectorLayer`
### 当前锚点渲染方式
- 每个 `pointMap` 图层对应一个 `VectorLayer`
- 每个点被转为 `Feature<Point>`
- Feature 上维护多个运行态字段:
- `_iconUrl`
- `_labelText`
- `_legendVisible`
- `_regionVisible`
- `_layerKey`
- 样式函数 `createPointStyle()` 在渲染时综合判断:
- 图例是否显示;
- 是否在基地区域内;
- 当前缩放级别;
- 点之间距离阈值;
- 是否悬停。
## 4.5 图例动态生成与点击联动
图例数据来源于两部分:
- `legendDataOriginal`:后端返回的全量图例;
- `legendDataSelected`:根据当前选中的图层动态筛选后的图例结构。
当前机制:
1. 初始化时拉取全部图例;
2. 通过 `layerCode` 与图层树选中结果建立关联;
3. `mapStore.updateLayerData()` 重新计算当前选中的图例树;
4. `MapLegend` 监听 `legendDataSelected` 进行展示;
5. 点击图例项后:
- 更新 Store 中图例 checked 状态;
- 若是锚点图层,则调用 `mapClass.setLegendPointVisible()`
- 若是 GIS 图层,则调用 `controlBaseLayerTreeShowAndHidden()`
### 环保设施特殊逻辑
- 勾选 `fp_point`、`eq_point`、`fb_point`、`vp_point`、`va_point`、`sg_point`、`dw_point` 中任意图层时,图例自动补充“环保设施”分组;
- 全部取消时自动移除该图例分组。
## 4.6 筛选与搜索
`MapFilter` 负责以下功能:
- 装机容量筛选;
- 锚点关键字搜索;
- 时间筛选;
- 基地筛选;
- 个别专题下的鱼类分布筛选。
当前实现方式:
- 搜索下拉列表从 `pointData` 里计算;
- 只展示当前已选中图层中的锚点;
- 容量筛选直接修改图例状态,并调用 `map.mdLayerShowOrHidden()` 控制 feature 显隐;
- 时间筛选通过 `mapStore.updateSearchTimeRange()` 清缓存、删图层、重新请求;
- 基地切换通过监听 `JidiSelectEventStore.selectedItem` 联动地图区域裁切。
## 4.7 基地选择与区域裁切
基地逻辑入口在 `GisView.vue` 监听器中:
- 当基地变化时,调用 `mapClass.jdPanelControlShowAndHidden()`
- `map.ol.ts` 内会:
- 更新当前 `BASEID`
- 请求基地 GeoJSON 边界;
- 隐藏所有锚点;
- 通过射线法判断锚点是否在区域内;
- 标记 `_regionVisible`
- 调整视角;
- 对底图应用遮罩。
### 该能力的业务效果
- 只显示当前基地区域内的锚点;
- 底图裁切到当前基地范围;
- 全选时取消遮罩并显示全部锚点。
## 4.8 Popup 与点击详情
当前交互如下:
- `pointermove` 时调用 `detectFeatureAtPixel()` 检查命中点要素;
- 若命中图标区域,则:
- 更新鼠标指针;
- 更新 `hoveredFeatureId`
- 调用 `showPopup()` 展示悬停浮窗;
- 重绘所有点图层;
- `click` 命中点要素时,打开详情弹窗并将 feature 属性写入 `modelStore`
### 当前特点
- Popup 内容由 `popupHtml``generatePopupHtml(props)` 生成;
- 命中检测采用逐像素回调加距离二次判断;
- 所有点图层在 hover 状态切换时都会 `layer.changed()`
## 4.9 菜单与缩放级别联动
在水电开发状况菜单下:
- 地图缩放级别大于等于 12 时自动添加一组动态图层;
- 缩回 12 以下时自动移除;
- 菜单切换时会根据当前层级重新判断是否添加。
当前动态图层 key 包括:
- `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`
---
## 5. 当前数据流与调用关系
当前主数据流可概括为:
1. `GisView.vue` 拉取配置;
2. `mapStore` 保存图层树、图例、缓存;
3. `LayerController.vue` / `MapLegend.vue` / `MapFilter.vue` 直接操作 `mapStore`
4. `mapStore` 又直接调用 `mapClass`
5. `mapClass` 继续转发到 `map.ol.ts`
6. `map.ol.ts` 内部维护图层注册表和 Feature 运行态;
7. 地图状态变化后,组件又通过监听 Store 再次更新 UI。
### 现有结构本质
当前是“多入口、多方向写状态”的结构:
- 组件可以改 Store
- 组件可以直接调地图;
- Store 可以直接调地图;
- 地图内部又维护一套运行态;
- 图例、图层树、地图显隐存在多份状态镜像。
这会导致:
- 状态同步成本高;
- 某些场景下容易出现 UI 勾选状态与实际地图状态不一致;
- 新增规则时只能继续在现有逻辑上叠加 `if/else`
---
## 6. 当前存在的主要问题
## 6.1 架构层面问题
### 1. UI、业务编排、地图渲染耦合过深
- `LayerController.vue`、`MapLegend.vue`、`MapFilter.vue` 都直接调 Store 和地图实例;
- `map.ts` 同时负责:
- 请求;
- 缓存;
- 图例结构整理;
- 图层勾选;
- 地图显隐;
- 业务互斥;
- `map.ol.ts` 同时负责:
- OpenLayers 适配;
- 业务过滤;
- 区域裁切;
- 弹窗;
- 图例控制;
- 锚点显示策略。
### 2. 缺乏清晰的单向数据流
- 图层树 checked、图例 checked、Feature `_legendVisible`、Layer visible 都是状态源;
- 任何一个点修改后都可能触发联动,难以推断最终状态;
- 某些流程依赖顺序正确才能表现正常。
### 3. 业务规则分散
- 互斥规则分散在 `LayerController.vue``map.ts`
- 环保设施图例补充逻辑在 `map.ts`
- 装机容量筛选逻辑在 `MapFilter.vue`
- 基地区域过滤逻辑在 `map.ol.ts`
- 这些规则没有统一归口,新增业务会继续扩大分散度。
## 6.2 性能问题
### 1. 初始化阶段预加载过重
- `loadAllLayerData()` 会递归处理大量图层;
- 即使图层未勾选,也会提前请求并建图层;
- 页面首次进入时网络压力和首屏渲染压力较大。
### 2. 递归遍历频繁
- 图层树查找 `findLayerByKey()` 多次递归;
- 图例生成 `getlegendData()` 递归;
- `updateLayerData()` 每次变化都要递归更新 checked 状态;
- 搜索项计算 `anchorPointOptions` 也会递归收集图层 key。
### 3. 锚点显隐是全量 feature 遍历
- 图例点击时逐个遍历 feature
- 基地裁切时逐个 feature 做点在面内判断;
- hover 时所有点图层重绘;
- 缩放变化会重复执行样式计算。
### 4. 底图切换会 remove / add 图层
- `baseLayerSwitcher()` 每次切换都移除旧底图再新增;
- 对 WMTS / XYZ 来说会造成重复创建图层对象和重复请求开销。
## 6.3 一致性与可靠性问题
### 1. 存在明显临时代码与未收敛逻辑
- `GisView.vue``fetchMapConfigs()` 里对图层做了 `splice(0, 2)` 临时截断;
- 日志仍显示“临时限制”,说明线上逻辑可能仍处于测试状态。
### 2. 存在未定义引用
- `GisView.vue` 切换地图后调用 `fetchPointData()`,但项目中未找到定义;
- `map.ts` 中使用了 `wq_lineTime`、`wt_lineTime`,但项目中未找到定义。
### 3. 请求去重逻辑未真正落地
- `map.ts` 中生成了 `requestIdentifier`,但未实际用于请求去重;
- `requestParamsCopy` 也未参与有效逻辑。
### 4. 图层状态与渲染状态容易失同步
- `layer.checked`、`checkedLayerKeys`、图例 checked、Feature 可见状态是多份数据;
- 其中任意一份被跳过更新,就可能出现视觉不一致。
### 5. 部分代码存在明显遗留痕迹
- `addInitDataLayer()` 中存在无意义语句 `3;`
- 有重复日志;
- 有注释掉的大量旧逻辑;
- 说明代码迁移过程中缺乏结构性收口。
## 6.4 可维护性问题
### 1. Store 过大
- `map.ts` 既是数据仓库,又是调度器,又是业务规则中心;
- 后续新增图层类型、筛选类型、专题逻辑时,文件会持续膨胀。
### 2. MapOl 职责过多
- 当前 `map.ol.ts` 既是 OpenLayers 适配器又是图层经理、锚点经理、Popup 经理、区域裁切经理;
- 单文件理解和修改成本已经偏高。
### 3. 缺少规范化类型系统
- 大量 `any`
- 图层配置、图例配置、锚点数据、请求映射结构都没有稳定类型;
- 特殊图层规则依赖字符串常量散落在各处。
---
## 7. 改造目标
本次改造建议达成以下目标:
### 7.1 结构目标
- 建立清晰的四层结构:视图层、应用编排层、领域状态层、地图渲染层;
- 收拢业务规则,不再散落到多个 UI 组件和地图引擎实现中;
- 建立稳定的数据模型与类型定义。
### 7.2 性能目标
- 首屏只加载必要图层;
- 选中图层优先加载,未选中图层延后或懒加载;(待考虑)
- 图例切换、基地切换、搜索筛选尽量使用索引和差量更新;
- 减少重复递归、重复建图层、重复样式重算。
### 7.3 维护目标
- 新增图层类型、专题规则、图例规则时有固定扩展点;
- 2D/3D 共享统一业务层,底层只替换渲染适配器;
- 可以较容易增加埋点、日志、调试工具和单元测试。
---
## 8. 目标架构设计
建议将地图模块拆分为四层:
## 8.1 视图层
只负责展示和用户交互,不直接编写复杂业务逻辑。
建议保留的组件:
- `GisView.vue`
- `LayerController.vue`
- `MapLegend.vue`
- `MapFilter.vue`
- `BaseLayerSwitcher.vue`
视图层职责:
- 展示树、图例、筛选项、底图切换器;
- 分发用户动作;
- 订阅 Store 派生结果;
- 不直接调用 `map.ol.ts` 里的业务方法。
## 8.2 应用编排层
新增地图编排层,建议命名为:
- `src/modules/map/application/map-orchestrator.ts`
-`src/components/gis/core/orchestrator.ts`
职责:
- 承接 UI 动作;
- 统一处理“图层勾选、图例勾选、基地切换、时间筛选、搜索定位、底图切换”等命令;
- 控制加载顺序;
- 根据状态差异驱动地图渲染层;
- 管理互斥规则和特殊规则。
### 推荐模式
所有 UI 动作统一转换成 Command
- `toggleLayer(layerKey)`
- `toggleLegend(layerKey, legendKey)`
- `switchBaseMap(baseMapKey)`
- `changeBaseRegion(baseId)`
- `changeTimeRange(range)`
- `focusPoint(pointId)`
## 8.3 领域状态层
保留 Pinia但将当前大 Store 拆分为多个职责明确的 Store / Service
- `mapConfigStore`
- 保存图层树、图例原始配置、页面配置;
- `mapViewStateStore`
- 保存当前选中图层、选中图例、当前底图、当前基地、当前时间范围、当前缩放级别;
- `mapDataStore`
- 保存锚点缓存、请求状态、加载状态、错误状态;
- `mapRuleEngine`
- 保存互斥规则、派生规则、特殊专题规则。
## 8.4 地图渲染层
保留 `MapClass` 作为统一门面,但将 `MapOl` 拆分成多个 Manager
- `ol-map-adapter.ts`
- 只负责地图实例生命周期;
- `ol-base-layer-manager.ts`
- 负责 WMTS / XYZ / GeoJSON / 底图切换;
- `ol-point-layer-manager.ts`
- 负责锚点图层创建、更新、索引、显隐;
- `ol-popup-manager.ts`
- 负责 hover / click / popup
- `ol-region-mask-manager.ts`
- 负责基地裁切、遮罩、点在面内过滤;
- `ol-interaction-manager.ts`
- 负责缩放监听、量算、定位等。
这样做后,`MapOl` 只保留聚合职责,不再直接承载全部细节。
---
## 9. 详细改造方案
## 9.1 统一领域模型
首先建立类型定义,避免继续依赖 `any`
建议新增:
- `types/map-layer.ts`
- `types/map-legend.ts`
- `types/map-point.ts`
- `types/map-rule.ts`
### 图层类型建议
- `BaseMapLayer`
- `OverlayLayer`
- `PointLayer`
- `RegionMaskLayer`
- `DynamicLayer`
### 图层运行态建议(待考虑)
将运行态从后端配置中剥离,不再直接把运行态写回原始配置:
- `config`: 后端返回配置,只读;
- `runtime`:
- `visible`
- `loaded`
- `loading`
- `error`
- `featureCount`
- `lastRequestKey`
- `lastUpdatedAt`
## 9.2 统一图层树、图例、地图显隐状态源
建议明确一个唯一状态源:
- 图层是否勾选:只认 `selectedLayerKeys`
- 图例是否勾选:只认 `selectedLegendKeys`
- 地图渲染层不再存业务真相,只接收派生后的可见性差量。
### 派生关系建议
- `selectedLayerKeys` -> 派生当前显示的图例组;
- `selectedLayerKeys + selectedLegendKeys + currentBaseId` -> 派生 Feature 可见性;
- `selectedBaseMapKey` -> 派生当前底图组合;
- `timeRange + filterConditions` -> 派生图层请求签名。
## 9.3 建立图层索引与图例索引
为提升速度,初始化配置后立即建立索引:
- `layerByKey`
- `legendByNameEn`
- `legendByLayerCode`
- `childrenByParentKey`
- `layerDescendantsByKey`
- `featureIndexByLayerAndLegendKey`
### 优化收益
- 避免每次 `findLayerByKey()` 递归;
- 图例切换时直接定位对应图层和对应 feature 集合;
- 图层树勾选联动时只处理差异节点。
## 9.4 抽离请求解析器
当前 `GisUrlList.ts` 的匹配逻辑散落在 `loadLayerData()` 中,建议拆出:
- `map-layer-request-resolver.ts`
职责:
- 根据 `layer.key` / `layer.title` / `layer.url` 解析真实请求;
- 生成标准请求结构:
- `url`
- `method`
- `filter`
- `sort`
- `cacheKey`
### 进一步建议
- 在应用启动时将 `GisUrlList.ts` 预编译成多索引字典;
- 不要在每次 `loadLayerData()` 内重新构建 `urlListDict`
- 请求签名应真正参与去重和缓存。
## 9.5 重新设计锚点加载策略(待考虑)
建议从“全量预加载”改为“分层加载”:
### 第一阶段
- 首屏只加载默认勾选图层;
- 图例也只基于已加载的默认勾选图层生成;
### 第二阶段(待考虑)
- 对常用图层做后台低优先级预取;
- 非常规图层在首次勾选时再加载;
### 第三阶段(待考虑)
- 对时间敏感或高频筛选图层使用缓存失效策略:
- `cacheKey = layerKey + requestSignature`
- 基于时间范围、基地、专题条件生成。
### 配套措施(待考虑)
- 增加并发控制,例如同时最多 4 到 6 个接口请求;
- 增加可取消请求,避免菜单切换时旧请求回写新页面;
- 记录每个图层单独 loading 和 error 状态。
## 9.6 重构点图层渲染管理
建议将点图层改造成“图层级管理 + 索引级更新”模式。
### 当前问题
- 图例点击靠遍历全量 feature
- 基地裁切靠遍历全量 feature
- hover 时所有点图层都重绘。
### 改造建议
`PointLayerManager` 中维护以下索引:
- `featuresByLayerKey`
- `featuresByLegendKey`
- `featuresByBaseId`
- `featureById`
### 改造后的显隐策略
- 图例点击时,只处理该图例对应 feature 集合;
- 基地切换时,优先按 `baseId` 做粗筛,再对边界交叉区域做点在面内判断;
- 图层勾选时只切换对应 `VectorLayer.visible`
- 非必要不重新 `addFeatures()`
### 样式策略建议
- 保留 style function但减少在 style function 内做复杂业务判断;
-`_legendVisible`、`_regionVisible`、`_layerVisible` 预先计算为单个 `renderVisible` 标记;
- 样式函数只做:
- 是否可见;
- 当前缩放级别;
- 当前 hover 状态;
- 图标与文本样式计算。
## 9.7 底图与叠加层管理重构(待考虑)
建议明确区分三类图层:
- 主底图:矢量 / 地形 / 影像三选一;
- 基础叠加层:自然保护区、行政边界等可附加;
- 专题锚点层:水电工程、生态流量、水质站、视频站等。
### 当前问题
- 底图切换会 remove / add `customBaseLayer`
- 主底图和叠加底图概念混杂;
- `controlBaseLayerTreeShowAndHidden()` 实际控制的不全是底图。
### 改造建议
- 维护 `currentBaseMapKey`
- 主底图预创建,切换时只改 visible
- 叠加层作为独立 overlay 管理;
- 自然保护区等图层明确归类为 overlay不与主底图复用同一逻辑。
## 9.8 图例模块重构
建议把“图例结构”和“图例选中态”彻底分离:
- `legendSchema`: 后端原始图例树;
- `legendVisibleTree`: 根据当前图层选择派生;
- `legendCheckedSet`: 当前选中的图例 key 集。
### 这样做的好处
- 后端原始图例树不再被运行态污染;
- 图例点击只更新 `legendCheckedSet`
- 图层变化只重算当前显示哪些图例,不直接修改原始结构;
- 更容易实现“重置图例”、“记忆用户图例偏好”。
## 9.9 基地裁切与区域过滤重构
建议将基地逻辑完全收口到 `RegionMaskManager`
### 改造重点
- 缓存基地 GeoJSON
- 一次解析边界,多次复用;
- 对点位先按 `baseId` 粗筛,再按 polygon 精筛;
- 避免每次基地切换都重新请求已缓存边界;
- 底图遮罩和锚点区域过滤使用同一份区域上下文。
### 进一步优化
- 若后端能返回基地与锚点的归属关系,可优先直接按 `baseId` 过滤;
- 点在面内判断只作为兜底校验;
- 对非常大的多边形可预处理 bbox先做 bbox 命中,再做射线法。 -
- 有一个 baseID 来传值 直接请求接口数据加载
- const url = this.hydropBaseConfig.geojson_url + `&cql_filter=BASEID='${this.BASEID}'`; console.log('正在请求裁切数据:', url);
// 等待数据加载
const geoJsonData = await this.loadGeoJsonData(url);
## 9.10 Popup 与交互重构
建议将 Popup 逻辑从 `MapOl` 中抽离:
- `PopupManager` 负责:
- hover 命中检测;
- popup 位置;
- popup 内容渲染;
- click 详情回调;
### 优化方向
- 命中检测尽量只针对当前可见的点图层;
- 对 hover 更新增加最小移动阈值;
- 只重绘当前命中 feature 所在图层,而不是所有点图层;
- Popup 内容生成应支持模板注册,不直接在地图层拼接业务 HTML。
## 9.11 业务规则引擎化
建议把分散的特殊规则抽到统一规则层。
### 当前应纳入规则引擎的规则
- 视频监控站与 AI 视频监控站互斥;
- 环保设施与环保设施在建互斥;
- 珍稀鱼类与沿程鱼类互斥;
- 环保设施图例自动补充;
- 水电开发状况菜单的缩放级别动态图层;
- 水质断面多接口拼接;
- 生态流量三级数据结构处理。
### 建议实现方式
定义规则接口:
```ts
interface MapBusinessRule {
id: string;
when(context: RuleContext): boolean;
apply(context: RuleContext): RulePatch;
}
```
这样新增业务时只加规则文件,不再继续往组件和 Store 塞判断。
## 9.12 组件层改造建议
### `GisView.vue`
- 只保留地图容器、初始化、模块挂载;
- 配置请求交给 `mapOrchestrator.initialize(pageKey)`
- 菜单切换、基地切换也只发送命令。
### `LayerController.vue`
- 只处理树控件展示与事件抛出;
- 互斥逻辑不要写在组件中;
- 初始底图加载不要放在组件 watch 里,改到编排层统一处理。
### `MapLegend.vue`
- 只负责展示图例树和发出切换事件;
- 不直接调用 `mapClass`
### `MapFilter.vue`
- 搜索、容量、时间、基地变化全部改为发送标准命令;
- 过滤后的锚点候选列表由 Store 派生,不在组件里自行拼装业务逻辑。
### `BaseLayerSwitcher.vue`
- 只负责切换主底图;
- 不关心图层树中 `customBaseLayer` 的内部结构。
## 9.13 日志、监控与调试能力(待考虑)
建议补充以下调试能力:
- 图层加载耗时;
- 单图层接口耗时;
- 首屏加载耗时;
- 当前可见图层列表;
- 当前可见锚点数量;
- 当前选中图例数量;
- 当前缓存命中率;
- 当前基地裁切耗时。
可以在开发模式下提供一个简易的地图调试面板,便于排查性能与状态同步问题。
---
## 10. 分阶段落地方案
建议采用“四阶段渐进式改造”,避免一次性大改导致风险过高。
## 第 1 阶段:收口现状与止血
目标:不改变功能表现,先把明显风险收口。
建议内容:
- 去掉测试遗留逻辑,如 `splice(0, 2)`
- 修复未定义方法和未定义变量;
- 清理重复日志、无效代码、注释垃圾;
- 补充类型定义;
- 建立 `layerByKey`、`legendByNameEn` 等基础索引;
- 将互斥规则从组件内搬到 Store 或规则层统一处理;
- 实现真实请求去重和请求取消。
## 第 2 阶段:拆 Store 与编排层
目标:建立单向数据流。
建议内容:
- 新增 `mapOrchestrator`
-`map.ts` 为配置、状态、数据三个模块;
- UI 组件只发送命令;
- 图例派生、图层派生、筛选派生全部收口到编排层。
## 第 3 阶段:拆地图渲染层
目标:把 `MapOl` 大文件拆开。
建议内容:
-`BaseLayerManager`
-`PointLayerManager`
-`PopupManager`
-`RegionMaskManager`
- 明确地图引擎层只负责“渲染执行”,不处理业务真相。
## 第 4 阶段:性能强化与能力扩展
目标:让模块适合长期迭代。
建议内容:
- 实现懒加载与后台预取;
- feature 索引化;
- 地图交互性能优化;
- 支持规则插件化;
- 补充测试与性能指标看板;
- 为 3D 共用业务层做适配。
---
## 11. 推荐目录结构
建议重构后目录大致如下:
```text
src/
modules/
map/
application/
map-orchestrator.ts
map-commands.ts
map-rule-engine.ts
domain/
types/
map-layer.ts
map-legend.ts
map-point.ts
map-filter.ts
services/
layer-request-resolver.ts
legend-deriver.ts
layer-deriver.ts
infrastructure/
stores/
map-config.store.ts
map-view-state.store.ts
map-data.store.ts
repositories/
map-config.repository.ts
map-point.repository.ts
render/
map.class.ts
ol/
ol-map-adapter.ts
ol-base-layer-manager.ts
ol-point-layer-manager.ts
ol-popup-manager.ts
ol-region-mask-manager.ts
ol-interaction-manager.ts
components/
gis/
GisView.vue
mapController/
LayerController.vue
mapLegend/
index.vue
mapFilter/
index.vue
BaseLayerSwitcher/
index.vue
```
---
## 12. 重点业务的改造策略
## 12.1 基础图层
策略:
- 单独定义主底图与叠加图层;
- 预创建主底图;
- 切换时仅改 visible
- `customBaseLayer` 不再作为特殊魔法 key 到处透传。
## 12.2 水电工程锚点
策略:
- 作为标准 `PointLayer` 处理;
- 保留当前图标、标签、距离阈值、Popup 规则;
-`PointLayerManager` 中建立 feature 索引。
## 12.3 生态流量三级数据
策略:
- 在领域层定义树结构转换器;
- Store 不直接关心层级深浅;
- 图层树组件只拿已经标准化后的树。
## 12.4 水质监测断面三接口拼接
策略:
- 不在 UI 或 Store 中散写拼接逻辑;
- 建立专题数据聚合器,例如 `water-quality-point-aggregator.ts`
- 对外输出统一的 `PointLayerData`
## 12.5 视频监控站与 AI 视频监控站互斥
策略:
- 写成标准业务规则;
- 由规则引擎统一裁剪勾选结果;
- UI 不再内嵌业务互斥判断。
## 12.6 自然保护区与区域底图
策略:
- 统一归类为 `OverlayLayer`
- 与主底图切换分离;
- 与基地裁切共享区域上下文。
---
## 13. 测试与验收建议
## 13.1 功能验收清单
- 页面首次进入,默认勾选图层正确显示;
- 图层树勾选与取消勾选,地图表现与图例表现一致;
- 图例点击后,仅影响对应锚点;
- 基地切换后,区域外锚点正确隐藏;
- 底图切换不影响锚点和图例状态;
- 时间筛选后相关图层正确重载;
- 视频监控站与 AI 视频监控站互斥稳定;
- 菜单切换后图层、图例、筛选项正确重置;
- 悬停 Popup 与点击详情正常。
## 13.2 性能验收指标
建议至少跟踪:
- 首屏地图可交互时间;
- 默认勾选图层加载总耗时;
- 单图层首次加载耗时;
- 图层勾选后锚点出现时间;
- 图例点击后的显隐响应时间;
- 基地切换后的裁切完成时间;
- 缓存命中率;
- 页面切换时旧请求是否被取消。
## 13.3 自动化测试建议
建议补充三类测试:
- 单元测试:
- 图例派生;
- 图层互斥规则;
- 请求解析器;
- 缓存 key 生成;
- 集成测试:
- 图层勾选 -> 图例变化 -> 地图显隐;
- 基地切换 -> 区域裁切 -> 搜索列表更新;
- 端到端测试:
- 地图初始化;
- 图层切换;
- 图例切换;
- 筛选联动;
- Popup 和详情弹窗。
---
## 14. 优先级最高的首批改造项
如果本轮改造只做最有价值的一批,建议优先按以下顺序处理:
1. 修复明显问题:
- 去掉 `splice(0, 2)`
- 修复 `fetchPointData()` 未定义;
- 修复 `wq_lineTime` / `wt_lineTime` 未定义;
- 清理无效代码与重复日志。
2. 建立统一状态源:
- `selectedLayerKeys`
- `selectedLegendKeys`
- `currentBaseId`
- `currentBaseMapKey`
3. 抽离请求解析器和规则引擎:
- URL 映射规则统一;
- 互斥规则统一;
- 特殊图例补充规则统一。
4.`map.ts`
- 配置状态;
- 运行状态;
- 数据缓存;
- 行为编排。
5.`map.ol.ts`
- 底图管理;
- 锚点管理;
- Popup 管理;
- 区域裁切管理。
---
## 15. 改造后的预期收益
完成改造后,地图模块应达到以下效果:
- 新增图层和专题能力时,不需要修改多个组件和多个文件;
- 图层树、图例、地图显隐的状态不再互相打架;
- 首屏加载更快,图层切换更轻;
- 地图代码从“堆逻辑”变成“可扩展的稳定结构”;
- 后续增加 3D 共用逻辑、专题聚合图层、更多筛选条件时,改造成本明显下降。
---
## 16. 结论
当前地图模块功能已经较完整,但代码结构处于“功能可用、结构偏重、规则分散、性能隐患较多”的阶段。继续在现有结构上叠加新需求,后续维护成本会快速上升。
建议本次改造不要只做局部优化,而应围绕以下核心原则整体推进:
- 单向数据流;
- 业务规则收口;
- 渲染层职责下沉;
- 类型与索引先行;
- 先止血,再拆层,再提速。
按照本文档的分阶段方案推进,可以在不一次性推翻现有功能的前提下,逐步完成地图模块的结构升级。