基础信息后台对接,
This commit is contained in:
parent
ddaa39c811
commit
90bd2bac1f
417
frontend/docs/地图模块-3D Cesium实施计划.md
Normal file
417
frontend/docs/地图模块-3D Cesium实施计划.md
Normal file
@ -0,0 +1,417 @@
|
|||||||
|
# 地图模块 3D Cesium 实施计划
|
||||||
|
|
||||||
|
## 1. 目标
|
||||||
|
|
||||||
|
在现有地图体系下补齐 `3D(Cesium)` 视图能力,使其尽量复用 `2D(OpenLayers)` 的业务编排结果,满足以下要求:
|
||||||
|
|
||||||
|
- 3D 默认进入中国视角,不再落到美国区域。
|
||||||
|
- 3D 初始化时显示过渡动画,再定位到中国。
|
||||||
|
- 3D 保留基础底图加载能力,但不提供底图切换面板。
|
||||||
|
- 3D 支持锚点展示。
|
||||||
|
- 3D 支持与 2D 一致的鼠标悬停 popup,样式复用现有 popup。
|
||||||
|
- 3D 支持与 2D 一致的锚点点击行为,点击后打开同一套详情弹框逻辑。
|
||||||
|
- 菜单切换、图层树勾选、图例勾选后,3D 锚点与 2D 一样联动变化。
|
||||||
|
- 3D 必须复用 `eng_point` 的层级显示规则,中型水电站仅在 `ENG_POINT_MEDIUM_VISIBLE_ZOOM = 7.5` 以上显示。
|
||||||
|
|
||||||
|
## 2. 2D 现状梳理
|
||||||
|
|
||||||
|
当前 `2D` 的能力并不是散落在页面里,而是已经形成了比较清晰的分层:
|
||||||
|
|
||||||
|
### 2.1 页面入口
|
||||||
|
|
||||||
|
- `src/components/gis/GisView.vue`
|
||||||
|
- 负责挂载地图容器、popup 容器、地图控制器、图例、图层树。
|
||||||
|
- 页面只调用 `mapOrchestrator.mountView()` 和 `handlePageChange()`,不直接拼地图业务。
|
||||||
|
|
||||||
|
### 2.2 地图编排层
|
||||||
|
|
||||||
|
- `src/modules/map/application/map-orchestrator.ts`
|
||||||
|
- 负责页面初始化、菜单切换、图层配置加载、图例配置加载、基地切换、缩放联动。
|
||||||
|
- 2D/3D 都应尽量复用这一层,避免在 Cesium 侧另起一套业务流程。
|
||||||
|
|
||||||
|
### 2.3 地图统一门面
|
||||||
|
|
||||||
|
- `src/components/gis/map.class.ts`
|
||||||
|
- 对外暴露统一接口,如:
|
||||||
|
- `addBaseDataLayer`
|
||||||
|
- `addInitDataLayer`
|
||||||
|
- `mdLayerTreeShowOrHidden`
|
||||||
|
- `setLegendPointVisible`
|
||||||
|
- `controlBaseLayerTreeShowAndHidden`
|
||||||
|
- `initPopupOverlay`
|
||||||
|
- `flyTopanto`
|
||||||
|
- 业务层只认这套接口,不应感知底层是 `OpenLayers` 还是 `Cesium`。
|
||||||
|
|
||||||
|
### 2.4 2D 地图能力落点
|
||||||
|
|
||||||
|
- `src/components/gis/map.ol.ts`
|
||||||
|
- 已实现的关键能力:
|
||||||
|
- 默认中国视角初始化
|
||||||
|
- 基础底图加载与切换
|
||||||
|
- 点位图层注册和显隐
|
||||||
|
- 图例控制单类锚点显隐
|
||||||
|
- 鼠标 hover popup
|
||||||
|
- click 打开详情弹窗
|
||||||
|
- 基地裁切与底图遮罩
|
||||||
|
- 缩放/移动后的批量 popup 和标签刷新
|
||||||
|
|
||||||
|
### 2.5 2D 锚点与 popup 支撑
|
||||||
|
|
||||||
|
- `src/components/gis/ol/point-layer-manager.ts`
|
||||||
|
- 负责将点位数据转成地图要素、分层、索引、图例显隐。
|
||||||
|
- `src/components/gis/ol/popup-manager.ts`
|
||||||
|
- 负责 hover 命中、popup DOM 内容复用、批量 popup。
|
||||||
|
|
||||||
|
### 2.6 图层树和图例交互
|
||||||
|
|
||||||
|
- `src/components/mapController/LayerController.vue`
|
||||||
|
- 图层树勾选后走 `mapOrchestrator.applyLayerSelection()`
|
||||||
|
- `src/components/mapLegend/index.vue`
|
||||||
|
- 图例点击后走 `mapOrchestrator.toggleLegend()` / `toggleLegendBatch()`
|
||||||
|
- `src/store/modules/map.ts`
|
||||||
|
- 真正把缓存数据、图层状态、图例状态应用到地图实例
|
||||||
|
- 也就是说,`3D` 只要把统一接口补齐,就能直接接住现有业务流
|
||||||
|
|
||||||
|
### 2.7 2D 已有特殊业务规则
|
||||||
|
|
||||||
|
- 锚点点击逻辑已在 `src/components/gis/map.ol.ts` 实现:
|
||||||
|
- 命中锚点后,`ylfb` 走 `ylfbModalVisible`
|
||||||
|
- 其他锚点走 `modalVisible`
|
||||||
|
- 标题和参数都直接取当前锚点数据
|
||||||
|
- `eng_point` 的中型水电站显示阈值已在现有链路中实现:
|
||||||
|
- 阈值常量位于 `src/modules/map/application/map-orchestrator.ts`
|
||||||
|
- 过滤逻辑位于 `src/store/modules/map.ts` 的 `filterPointLayerDataForDisplay()`
|
||||||
|
- `3D` 不应重新发明一套规则,而应复用这套现有过滤结果
|
||||||
|
- 部分点位会随着缩放级别提升显示得更多,不是简单的“图层开/关”:
|
||||||
|
- `eng_point` 的中型电站要到 `ENG_POINT_MEDIUM_VISIBLE_ZOOM = 7.5` 以上才显示
|
||||||
|
- 水电开发页在缩放跨过 `12` 级时会自动补开一批动态图层
|
||||||
|
- `2D` 内部还存在点位密度阈值、近邻点展开、批量 popup 阈值等缩放联动规则
|
||||||
|
- `2D` 对锚点存在碰撞检测与密度控制,不是所有点始终全量显示:
|
||||||
|
- 图标和标签会分别经过碰撞计算
|
||||||
|
- 低缩放下会因为密度、碰撞、区域裁切、图例状态而隐藏部分点
|
||||||
|
- 高缩放下会逐步放开限制,显示更多锚点和标签
|
||||||
|
|
||||||
|
## 3. 3D 当前现状与缺口
|
||||||
|
|
||||||
|
### 3.1 已有内容
|
||||||
|
|
||||||
|
- `src/components/gis/map.cesium.ts`
|
||||||
|
- 当前只完成了最小化 `Cesium.Viewer` 初始化。
|
||||||
|
- 已有基础缩放、截图、`flyTopanto()` 雏形。
|
||||||
|
|
||||||
|
### 3.2 当前主要缺口
|
||||||
|
|
||||||
|
- `init()` 里虽然定义了 `flyToChina()`,但没有真正执行,也没有 `resolve(viewer)`,初始化流程不完整。
|
||||||
|
- 默认底图仍是 ArcGIS 全球影像,因此启动时容易落到美国默认视角。
|
||||||
|
- `addBaseDataLayer()` 为空,无法接住当前 `GISMap/customBaseLayer` 逻辑。
|
||||||
|
- `addInitDataLayer()` 为空,无法展示锚点。
|
||||||
|
- `mdLayerTreeShowOrHidden()` 为空,菜单切换时 3D 锚点不会同步显隐。
|
||||||
|
- `setLegendPointVisible()` 为空,图例勾选不会影响 3D 锚点。
|
||||||
|
- `initPopupOverlay()` 为空,无法复用 2D popup 容器。
|
||||||
|
- 缺少点击命中与详情弹框联动,3D 当前无法做到“点击锚点与 2D 同行为”。
|
||||||
|
- `controlBaseLayerTreeShowAndHidden()` 为空,3D 基础底图和叠加层无法受现有图层树控制。
|
||||||
|
- 尚未验证 `eng_point` 的缩放阈值规则在 3D 下是否能完全复用,存在实现时遗漏中型站点显隐的风险。
|
||||||
|
- 目前没有承接 `2D` 中“放大到具体层级显示更多锚点”的缩放联动显示机制。
|
||||||
|
- 目前没有承接 `2D` 中图标/标签的碰撞检测逻辑,后续若直接全量渲染,视觉效果会与 `2D` 明显不一致。
|
||||||
|
- 当前没有建立 `Cesium entity / datasource / imageryLayer` 的注册表,后续联动无从下手。
|
||||||
|
|
||||||
|
## 4. 实施原则
|
||||||
|
|
||||||
|
- 不重做业务编排,优先复用 `map-orchestrator.ts` 和 `src/store/modules/map.ts`。
|
||||||
|
- 不额外引入一套 3D 专用图例和图层状态,仍以现有 store 为唯一运行态来源。
|
||||||
|
- 不单独复制 `eng_point` 的中型站显示规则,优先复用现有 `mapStore` 过滤结果。
|
||||||
|
- 不单独新建一套点击弹框协议,优先保持与 `map.ol.ts` 相同的命中后行为。
|
||||||
|
- 对“缩放越大显示越多锚点”这类规则,优先复用现有缩放阈值与显示门闩,不做拍脑袋的新规则。
|
||||||
|
- 对碰撞检测,优先与 `2D` 保持同方向行为:低缩放控量,高缩放放开,而不是 3D 全量堆点。
|
||||||
|
- 先做最小闭环,再逐步补能力:
|
||||||
|
- 第一步保证 3D 能正确初始化并定位中国。
|
||||||
|
- 第二步保证基础底图能显示。
|
||||||
|
- 第三步保证锚点能显示。
|
||||||
|
- 第四步保证 popup、图层树、图例联动打通。
|
||||||
|
- 3D 不做“基础地图切换 UI”,但底层仍要支持 `customBaseLayer + addBaseDataLayer(..., true)` 这一逻辑。
|
||||||
|
|
||||||
|
## 5. 分步骤实施计划
|
||||||
|
|
||||||
|
### Step 1:修正 3D 初始化并默认定位中国
|
||||||
|
|
||||||
|
目标:
|
||||||
|
|
||||||
|
- 切到 3D 后稳定初始化 viewer。
|
||||||
|
- 首屏出现一个轻量加载动画。
|
||||||
|
- 初始化完成后飞到中国默认视角。
|
||||||
|
|
||||||
|
计划:
|
||||||
|
|
||||||
|
- 修复 `map.cesium.ts` 的 `init()`:
|
||||||
|
- 保证初始化后一定 `resolve(this.viewer)`。
|
||||||
|
- 执行 `flyToChina()`。
|
||||||
|
- 补一个可控的启动过渡动画。
|
||||||
|
- 统一中国默认参数:
|
||||||
|
- 中心经纬度建议复用 `104.5, 36.5`
|
||||||
|
- 相机高度先按全国视角收口,后续再微调
|
||||||
|
- 如果切回 2D 再切到 3D,仍然要能正确重建 viewer。
|
||||||
|
|
||||||
|
验收:
|
||||||
|
|
||||||
|
- 点击 `3D` 按钮后,不再出现美国默认视角。
|
||||||
|
- 首屏有加载过渡。
|
||||||
|
- 进入后稳定落在中国。
|
||||||
|
|
||||||
|
### Step 2:展示 3D 基础底图
|
||||||
|
|
||||||
|
目标:
|
||||||
|
|
||||||
|
- 3D 不显示空球。
|
||||||
|
- 保留“基础底图 + 3D 专用底图地址”能力。
|
||||||
|
- 不提供底图切换面板,但底层能吃现有配置。
|
||||||
|
|
||||||
|
计划:
|
||||||
|
|
||||||
|
- 在 `MapCesium` 内建立基础图层注册表。
|
||||||
|
- 实现 `addBaseDataLayer(layer, isShow)`:
|
||||||
|
- 对 `customBaseLayer` 使用 `url_3d`
|
||||||
|
- 对 `BASEMAP-img`、`BASEMAP-white` 预留兼容能力
|
||||||
|
- 实现 `controlBaseLayerTreeShowAndHidden()`:
|
||||||
|
- 让图层树勾选能控制 3D 底图显隐
|
||||||
|
- 根据你的要求,UI 层先不开放 `BaseLayerSwitcher` 的 3D 切换行为,只保留默认基础底图加载。
|
||||||
|
|
||||||
|
验收:
|
||||||
|
|
||||||
|
- 切到 3D 后可稳定看到基础底图。
|
||||||
|
- 图层初始化时 `addBaseDataLayer(this.baseLayerConfig, checked, true)` 的思路在 3D 可落地。
|
||||||
|
|
||||||
|
### Step 3:打通 3D 锚点展示
|
||||||
|
|
||||||
|
目标:
|
||||||
|
|
||||||
|
- 3D 能展示和 2D 同源的锚点数据。
|
||||||
|
- 菜单切换后,3D 能跟随页面数据切换。
|
||||||
|
|
||||||
|
计划:
|
||||||
|
|
||||||
|
- 在 `MapCesium` 内建立点位图层注册结构,建议按 `layerKey -> entity[]` 或 `CustomDataSource` 管理。
|
||||||
|
- 实现 `addInitDataLayer(pointData, layerType)`:
|
||||||
|
- 复用当前点位数据结构
|
||||||
|
- 解析 `lgtd/lttd/iconCode/titleName/stnm/anchoPointState/popupHtml`
|
||||||
|
- 复用现有图标资源路径
|
||||||
|
- 确保 `eng_point` 仍按现有 `mapStore` 过滤后的数据展示,中型站点只在 `7.5` 以上层级出现
|
||||||
|
- 为后续缩放驱动的增量显示预留运行态字段,不把所有点简单一次性硬渲染
|
||||||
|
- 实现 `mdLayerTreeShowOrHidden(layerType, checked)`:
|
||||||
|
- 图层树控制整个点图层显隐
|
||||||
|
- 实现 `removePointLayer()` / `hasLayer()`
|
||||||
|
- 便于后续页面切换和重载
|
||||||
|
|
||||||
|
验收:
|
||||||
|
|
||||||
|
- 切到 3D 后当前页默认勾选图层的锚点能显示。
|
||||||
|
- 切换菜单后,3D 锚点跟随变化。
|
||||||
|
- 取消勾选图层后,对应锚点消失。
|
||||||
|
- `eng_point` 在低于 `7.5` 时不显示中型水电站,高于 `7.5` 时正常显示。
|
||||||
|
|
||||||
|
### Step 4:打通 3D popup 与 hover
|
||||||
|
|
||||||
|
目标:
|
||||||
|
|
||||||
|
- 3D 鼠标悬停时显示与 2D 一样的 popup。
|
||||||
|
- popup 样式复用现有 DOM 和样式,不再单独做一版。
|
||||||
|
|
||||||
|
计划:
|
||||||
|
|
||||||
|
- 实现 `initPopupOverlay(popupContainer)`
|
||||||
|
- 继续复用 `GisView.vue` 中现有 popup 容器
|
||||||
|
- 在 Cesium 中增加鼠标移动命中:
|
||||||
|
- 基于 `ScreenSpaceEventHandler`
|
||||||
|
- hover 到锚点时读取 entity 附带属性
|
||||||
|
- popup 内容生成逻辑复用 2D 现有字段:
|
||||||
|
- 优先使用 `popupHtml`
|
||||||
|
- 否则走通用字段渲染
|
||||||
|
- 处理离开锚点时隐藏 popup。
|
||||||
|
|
||||||
|
验收:
|
||||||
|
|
||||||
|
- 鼠标移入锚点出现 popup。
|
||||||
|
- 样式和 2D 保持一致。
|
||||||
|
- 移出后 popup 正常消失。
|
||||||
|
|
||||||
|
### Step 5:补齐缩放驱动显示与碰撞检测
|
||||||
|
|
||||||
|
目标:
|
||||||
|
|
||||||
|
- 3D 在低缩放下控制锚点数量,避免全量堆叠。
|
||||||
|
- 3D 在放大到具体层级后,逐步显示更多锚点。
|
||||||
|
- 3D 的图标/标签显示效果尽量接近 2D,不出现严重遮挡。
|
||||||
|
|
||||||
|
计划:
|
||||||
|
|
||||||
|
- 先承接现有明确可复用的缩放规则:
|
||||||
|
- `eng_point` 的 `7.5` 阈值
|
||||||
|
- 水电开发页的 `12` 级动态图层联动
|
||||||
|
- 再补 3D 侧的显示门闩:
|
||||||
|
- 低缩放下根据缩放级别、图层状态、图例状态控制 entity 显隐
|
||||||
|
- 高缩放下逐步放开限制,显示更多锚点
|
||||||
|
- 为 3D 补一版轻量碰撞控制:
|
||||||
|
- 至少先控制标签碰撞,避免名称完全叠在一起
|
||||||
|
- 如有必要,再补图标级碰撞或屏幕网格抽稀
|
||||||
|
- 明确与 2D 对齐的目标不是“算法完全一样”,而是“视觉行为一致”:
|
||||||
|
- 低缩放控量
|
||||||
|
- 高缩放增量显示
|
||||||
|
- 关键锚点不被完全淹没
|
||||||
|
|
||||||
|
验收:
|
||||||
|
|
||||||
|
- 3D 在全国视角下不会一次性挤满所有锚点。
|
||||||
|
- 放大到具体层级后会看到更多锚点逐步出现。
|
||||||
|
- 标签和锚点不会大面积重叠到不可读。
|
||||||
|
|
||||||
|
### Step 6:打通 3D 锚点点击详情
|
||||||
|
|
||||||
|
目标:
|
||||||
|
|
||||||
|
- 3D 点击锚点后,行为与 2D 一致。
|
||||||
|
- 继续复用现有详情弹框状态和参数结构。
|
||||||
|
|
||||||
|
计划:
|
||||||
|
|
||||||
|
- 在 Cesium 中增加点击命中:
|
||||||
|
- 基于 `ScreenSpaceEventHandler`
|
||||||
|
- 命中锚点后读取 entity 绑定的原始业务数据
|
||||||
|
- 复用 `2D` 当前点击行为:
|
||||||
|
- `sttpMap === 'ylfb'` 时打开 `ylfbModalVisible`
|
||||||
|
- 其他锚点打开 `modalVisible`
|
||||||
|
- `params`、`title` 赋值逻辑与 `map.ol.ts` 保持一致
|
||||||
|
- 避免 hover popup 与 click 弹框互相干扰。
|
||||||
|
|
||||||
|
验收:
|
||||||
|
|
||||||
|
- 点击 3D 锚点后可打开与 2D 一致的详情弹框。
|
||||||
|
- `ylfb` 和普通锚点分支行为一致。
|
||||||
|
- 点击空白区域不会误触发弹框。
|
||||||
|
|
||||||
|
### Step 7:打通图例联动
|
||||||
|
|
||||||
|
目标:
|
||||||
|
|
||||||
|
- 图例勾选后,3D 只显示对应状态的锚点。
|
||||||
|
|
||||||
|
计划:
|
||||||
|
|
||||||
|
- 实现 `setLegendPointVisible(layerKey, anchoPointState, checked)`:
|
||||||
|
- 复用 `anchoPointState` 作为图例匹配字段
|
||||||
|
- entity 级别维护显示状态
|
||||||
|
- 如果后续需要,也可补一个 3D 版本的图例索引表,避免每次全量遍历。
|
||||||
|
|
||||||
|
验收:
|
||||||
|
|
||||||
|
- 图例单项勾选/取消时,3D 对应类别锚点同步显隐。
|
||||||
|
- 图例分组勾选/取消时,3D 行为与 2D 保持一致。
|
||||||
|
|
||||||
|
### Step 8:补足收口与稳定性
|
||||||
|
|
||||||
|
目标:
|
||||||
|
|
||||||
|
- 让 3D 能稳定参与现有地图生命周期。
|
||||||
|
|
||||||
|
计划:
|
||||||
|
|
||||||
|
- 清理 Cesium 事件监听、viewer、entity、datasource,避免重复切换内存泄漏。
|
||||||
|
- 检查 `switchView('2D' | '3D')` 前后 popup、底图、图层注册是否被正确重置。
|
||||||
|
- 检查 hover、click、图例、图层树、缩放阈值这几条联动链路在 3D 是否完全闭环。
|
||||||
|
- 视情况补最小诊断日志,确认菜单切换与图层联动没有漏项。
|
||||||
|
|
||||||
|
验收:
|
||||||
|
|
||||||
|
- 2D/3D 多次来回切换不报错。
|
||||||
|
- 菜单切换、图层切换、图例切换行为稳定。
|
||||||
|
|
||||||
|
## 6. 推荐落地顺序
|
||||||
|
|
||||||
|
按你的要求,建议严格按下面顺序推进:
|
||||||
|
|
||||||
|
1. 默认定位到中国 + 启动动画
|
||||||
|
2. 展示基础底图
|
||||||
|
3. 展示锚点
|
||||||
|
4. 补 hover popup
|
||||||
|
5. 补缩放驱动显示与碰撞检测
|
||||||
|
6. 补锚点点击详情
|
||||||
|
7. 补图层树联动
|
||||||
|
8. 补图例联动
|
||||||
|
9. 做切换稳定性收口
|
||||||
|
|
||||||
|
## 9. 必须补齐的隐藏逻辑清单
|
||||||
|
|
||||||
|
下面这些逻辑不是表面接口能直接看出来的,但如果 `3D` 不补,最终效果就会和 `2D` 明显不一致。
|
||||||
|
|
||||||
|
### 9.1 图层与菜单切换
|
||||||
|
|
||||||
|
- 菜单切换时,只隐藏旧页点图层,不隐藏底图。
|
||||||
|
- 图层树勾选前要先经过互斥规则归一化:
|
||||||
|
- `rare_fish_point` 与 `fish_along_point` 互斥
|
||||||
|
- `stinfo/stinfo_video_point` 与 `stinfo_ai_video_point` 互斥
|
||||||
|
- `facilities` 与 `facilities_built` 两个分支互斥
|
||||||
|
- 点图层取消勾选时只隐藏,不删除缓存;重新勾选时优先从缓存恢复。
|
||||||
|
|
||||||
|
### 9.2 图例与点内显隐
|
||||||
|
|
||||||
|
- 图例控制的是图层内部锚点是否可见,不是图层本身是否可见。
|
||||||
|
- 图层恢复显示时,图例要先整层全关,再按当前 checked 状态逐项恢复。
|
||||||
|
- 环保设施图例组当前是只展示不交互。
|
||||||
|
|
||||||
|
### 9.3 缩放驱动显示
|
||||||
|
|
||||||
|
- `eng_point` 的中型水电站只在 `ENG_POINT_MEDIUM_VISIBLE_ZOOM = 7.5` 以上显示。
|
||||||
|
- 水电开发页缩放跨过 `12` 级时,会自动补开一批动态图层;缩回去时再自动收口。
|
||||||
|
- `2D` 存在“缩放越大显示越多”的行为,不是所有锚点在所有层级都等量显示。
|
||||||
|
- `2D` 还存在 hover、批量 popup、密度控制等缩放阈值:
|
||||||
|
- hover 低于一定缩放不触发
|
||||||
|
- 高缩放会进入批量 popup 模式
|
||||||
|
- 点位会随着缩放提升逐步放开显示限制
|
||||||
|
|
||||||
|
### 9.4 锚点碰撞与控量
|
||||||
|
|
||||||
|
- `2D` 中锚点是否可见,不只受图层开关控制,还受以下门闩共同影响:
|
||||||
|
- 图例可见性
|
||||||
|
- 区域裁切可见性
|
||||||
|
- 密度阈值
|
||||||
|
- 图标碰撞结果
|
||||||
|
- 标签碰撞结果
|
||||||
|
- 低缩放下,为避免堆叠,部分锚点或标签会被主动隐藏。
|
||||||
|
- 高缩放下,会逐步放开图标/标签限制,显示更多内容。
|
||||||
|
- 因此 `3D` 不能简单把所有 entity 永久全开,否则视觉密度会明显失控。
|
||||||
|
|
||||||
|
### 9.5 Popup 与点击
|
||||||
|
|
||||||
|
- hover popup 不是全时开启的,有缩放门槛。
|
||||||
|
- 点击和 hover 的命中对象是图标本体,不是任意文字区域。
|
||||||
|
- `ylfb` 与普通锚点的点击详情弹框走不同业务分支。
|
||||||
|
|
||||||
|
### 9.6 基地与区域裁切
|
||||||
|
|
||||||
|
- 基地切换不仅影响搜索候选,还会影响地图上的点位可见范围。
|
||||||
|
- 区域裁切会直接把区域外点位隐藏,不是只改底图遮罩。
|
||||||
|
|
||||||
|
### 9.7 筛选器特殊逻辑
|
||||||
|
|
||||||
|
- 装机容量筛选本质是批量切 `eng_point` 图例状态,不是简单切图层。
|
||||||
|
- 鱼类分布模式会接管 `ylfb_point` 的数据、缓存、图例与显隐。
|
||||||
|
- 时间筛选当前只重载指定时间相关图层,不是全量重载。
|
||||||
|
|
||||||
|
## 7. 预计修改文件
|
||||||
|
|
||||||
|
- `src/components/gis/map.cesium.ts`
|
||||||
|
- 3D 地图主体实现,主改动文件
|
||||||
|
- `src/components/gis/map.class.ts`
|
||||||
|
- 视情况补齐 2D/3D 切换后 popup 初始化和服务切换细节
|
||||||
|
- `src/components/gis/GisView.vue`
|
||||||
|
- 视情况控制 3D 下的底图切换器显示策略、加载态挂载位置
|
||||||
|
- `src/components/mapController/index.vue`
|
||||||
|
- 若需要补 3D 初始化后的交互控制
|
||||||
|
|
||||||
|
## 8. 当前建议
|
||||||
|
|
||||||
|
建议先只做 `Step 1`,也就是:
|
||||||
|
|
||||||
|
- 修正 Cesium 初始化完成逻辑
|
||||||
|
- 增加 3D 启动加载动画
|
||||||
|
- 默认飞到中国
|
||||||
|
|
||||||
|
这一步最小、最容易验证,也能先把“打开 3D 看起来不对”的核心问题解决掉。
|
||||||
@ -1,43 +0,0 @@
|
|||||||
# 问题汇总
|
|
||||||
|
|
||||||
> 本记录用于跟踪项目开发过程中发现的问题,按日期倒序排列,并标注每项问题的发现人及处理状态。
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 问题列表(2026-06-26)
|
|
||||||
|
|
||||||
| 发现日期 | 解决人 | 编号 | 问题描述 | 状态 | 备注 |
|
|
||||||
| ---------- | ------ | ---- | ---------------------------------------------------------------------- | ------ | -------- |
|
|
||||||
| 2026-06-26 | 扈 | 1 | 基础信息图片展示是什么逻辑 | ✅ | <br /> |
|
|
||||||
| 2026-06-26 | 扈 | 2 | 电站专题展示逻辑 | ✅ | <br /> |
|
|
||||||
| 2026-06-26 | <br /> | 3 | 实时视频回放 少接口 | ⚠ | 缺少接口 |
|
|
||||||
| 2026-06-26 | <br /> | 4 | 预警提示 少接口 | ⚠ | 缺少接口 |
|
|
||||||
| 2026-06-26 | 扈 | 5 | 生态流量 达标率查询不对 | ✅ | <br /> |
|
|
||||||
| 2026-06-26 | 王 | 6 | 鱼类适应性繁殖同期对比 NAN | ⚠ | <br /> |
|
|
||||||
| 2026-06-26 | 扈 | 7 | 出库水温 综合分析 导出没做 | ✅ | <br /> |
|
|
||||||
| 2026-06-26 | <br /> | 8 | 栖息地-流量监测 没有水位视频和流量视频 字段没数据 | ⚠ | <br /> |
|
|
||||||
| 2026-06-26 | <br /> | 9 | 栖息地 水温、水质、流量没有数据没法测 | ⚠ | <br /> |
|
|
||||||
| 2026-06-26 | 扈 | 10 | 鱼类增殖站 - 运行数据 要添加字典 | ✅ | <br /> |
|
|
||||||
| 2026-06-26 | 扈 | 11 | 珍惜植物园 - 种植要求字段不知道 | ✅ | <br /> |
|
|
||||||
| 2026-06-26 | 扈 | 12 | 水生调查断面 - 监测数据 不知道电导率,性腺发育期,早期资源量和种类 | ✅ | <br /> |
|
|
||||||
| 2026-06-26 | 扈 | 13 | 水电告警情况 生态流量 不知道字段 接口传参有问题 | ✅ | <br /> |
|
|
||||||
| 2026-06-26 | <br /> | 14 | 地图抽吸 碰撞检测 放大到具体层级锚点抽吸了但是 popup 弹框还在显示 | ⚠ | <br /> |
|
|
||||||
| 2026-06-26 | <br /> | 15 | 地图比如积石峡 放大到层级被公伯峡隐藏掉了,然后在放大才显示 抽吸的问题 | ⚠ | <br /> |
|
|
||||||
| 2026-06-26 | <br /> | 16 | 运行情况 计划开始运行时间 接口没接 | ⚠ | <br /> |
|
|
||||||
| 2026-07-01 | 王 | 17 | 所有弹框加拖动 | ✅ | <br /> |
|
|
||||||
| 2026-07-01 | 王 | 18 | 所有弹框检查宽度有的是固定的 | ✅ | <br /> |
|
|
||||||
| 2026-07-01 | 王 | 18 | 所有导出的添加 | <br /> | <br /> |
|
|
||||||
| 2026-07-02 | 扈 | 19 | 地图点击基地切换偶发性会全部展示出来所有锚点 | <br /> | <br /> |
|
|
||||||
| 2026-07-03 | 汤 | 20 | 图例数据关了选中配置需要添加一个字段是默认不显示 | <br /> | <br /> |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 状态说明
|
|
||||||
|
|
||||||
- ✅ – 已解决 / 已确认
|
|
||||||
- ⚠ – 处理中 / 待解决
|
|
||||||
- ❌ – 阻塞 / 未开始
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
、
|
|
||||||
@ -62,7 +62,7 @@ export function checkAnalysisShow(data: any) {
|
|||||||
// 查询站点的关联子站点 (垂向水温、AI 识别)
|
// 查询站点的关联子站点 (垂向水温、AI 识别)
|
||||||
export function getStInfoByStcd(params: any) {
|
export function getStInfoByStcd(params: any) {
|
||||||
return request({
|
return request({
|
||||||
url: '/api/dec-lygk-base-server/base/msstbprpt/getStInfoByStcd',
|
url: '/eng/base/msstbprpt/getStInfoByStcd',
|
||||||
method: 'get',
|
method: 'get',
|
||||||
params
|
params
|
||||||
});
|
});
|
||||||
|
|||||||
@ -115,7 +115,7 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import { ref, onMounted, nextTick, watch, computed } from 'vue';
|
import { ref, nextTick, watch, computed } from 'vue';
|
||||||
import {
|
import {
|
||||||
BasicColumns,
|
BasicColumns,
|
||||||
wtPointColumns,
|
wtPointColumns,
|
||||||
@ -359,8 +359,7 @@ const getData = async () => {
|
|||||||
try {
|
try {
|
||||||
if (props.url && modelStore.params.stcd) {
|
if (props.url && modelStore.params.stcd) {
|
||||||
let res: any = await getStcdDetail(props.url, modelStore.params.stcd);
|
let res: any = await getStcdDetail(props.url, modelStore.params.stcd);
|
||||||
console.log(res);
|
if (res && res.code) {
|
||||||
if (res && res.success) {
|
|
||||||
let data = res.data;
|
let data = res.data;
|
||||||
data2.value = data;
|
data2.value = data;
|
||||||
|
|
||||||
|
|||||||
@ -17,7 +17,7 @@ const ENGTabs: Array<any> = [
|
|||||||
name: '基础信息',
|
name: '基础信息',
|
||||||
key: 'basicInfo',
|
key: 'basicInfo',
|
||||||
type: 'basic',
|
type: 'basic',
|
||||||
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo',
|
url: '/eng/base/msstbprpt/getStcdInfo',
|
||||||
default: true // 默认显示
|
default: true // 默认显示
|
||||||
},
|
},
|
||||||
// {
|
// {
|
||||||
@ -113,7 +113,7 @@ const ENGEQTabs: Array<any> = [
|
|||||||
name: '基础信息',
|
name: '基础信息',
|
||||||
key: 'basicInfo',
|
key: 'basicInfo',
|
||||||
type: 'basic',
|
type: 'basic',
|
||||||
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo',
|
url: '/eng/base/msstbprpt/getStcdInfo',
|
||||||
default: true // 默认显示
|
default: true // 默认显示
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -213,7 +213,7 @@ const EQTabs: Array<any> = [
|
|||||||
name: '基础信息',
|
name: '基础信息',
|
||||||
key: 'basicInfo',
|
key: 'basicInfo',
|
||||||
type: 'basic',
|
type: 'basic',
|
||||||
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo',
|
url: '/eng/base/msstbprpt/getStcdInfo',
|
||||||
default: true // 默认显示
|
default: true // 默认显示
|
||||||
},
|
},
|
||||||
// {
|
// {
|
||||||
@ -239,7 +239,7 @@ const FPTabs: Array<any> = [
|
|||||||
name: '基础信息',
|
name: '基础信息',
|
||||||
key: 'basicInfo',
|
key: 'basicInfo',
|
||||||
type: 'basic',
|
type: 'basic',
|
||||||
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo',
|
url: '/eng/base/msstbprpt/getStcdInfo',
|
||||||
code: 'gyss.tabs.jcxx'
|
code: 'gyss.tabs.jcxx'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -311,7 +311,7 @@ const FPTabs1: Array<any> = [
|
|||||||
key: 'basicInfo',
|
key: 'basicInfo',
|
||||||
type: 'basic',
|
type: 'basic',
|
||||||
default: true, // 默认显示
|
default: true, // 默认显示
|
||||||
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo'
|
url: '/eng/base/msstbprpt/getStcdInfo'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '实时视频',
|
name: '实时视频',
|
||||||
@ -333,7 +333,7 @@ const FBTabs: Array<any> = [
|
|||||||
name: '基础信息',
|
name: '基础信息',
|
||||||
key: 'basicInfo',
|
key: 'basicInfo',
|
||||||
type: 'basic',
|
type: 'basic',
|
||||||
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo'
|
url: '/eng/base/msstbprpt/getStcdInfo'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '运行数据',
|
name: '运行数据',
|
||||||
@ -442,7 +442,7 @@ const FHTabs: Array<any> = [
|
|||||||
name: '基础信息',
|
name: '基础信息',
|
||||||
key: 'basicInfo',
|
key: 'basicInfo',
|
||||||
type: 'basic',
|
type: 'basic',
|
||||||
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo',
|
url: '/eng/base/msstbprpt/getStcdInfo',
|
||||||
default: true // 默认显示
|
default: true // 默认显示
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -476,7 +476,7 @@ const ZQTabs: Array<any> = [
|
|||||||
name: '基础信息',
|
name: '基础信息',
|
||||||
key: 'basicInfo',
|
key: 'basicInfo',
|
||||||
type: 'basic',
|
type: 'basic',
|
||||||
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo',
|
url: '/eng/base/msstbprpt/getStcdInfo',
|
||||||
default: true // 默认显示
|
default: true // 默认显示
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -493,7 +493,7 @@ const WQFBTabs: Array<any> = [
|
|||||||
name: '基础信息',
|
name: '基础信息',
|
||||||
key: 'basicInfo',
|
key: 'basicInfo',
|
||||||
type: 'basic',
|
type: 'basic',
|
||||||
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo'
|
url: '/eng/base/msstbprpt/getStcdInfo'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '监测数据',
|
name: '监测数据',
|
||||||
@ -547,7 +547,7 @@ const FLOWTabs: Array<any> = [
|
|||||||
name: '基础信息',
|
name: '基础信息',
|
||||||
key: 'basicInfo',
|
key: 'basicInfo',
|
||||||
type: 'basic',
|
type: 'basic',
|
||||||
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo',
|
url: '/eng/base/msstbprpt/getStcdInfo',
|
||||||
default: true // 默认显示
|
default: true // 默认显示
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -562,7 +562,7 @@ const WTTabs: Array<any> = [
|
|||||||
name: '基础信息',
|
name: '基础信息',
|
||||||
key: 'basicInfo',
|
key: 'basicInfo',
|
||||||
type: 'basic',
|
type: 'basic',
|
||||||
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo'
|
url: '/eng/base/msstbprpt/getStcdInfo'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '监测数据',
|
name: '监测数据',
|
||||||
@ -590,7 +590,7 @@ const WTTabs1: Array<any> = [
|
|||||||
name: '基础信息',
|
name: '基础信息',
|
||||||
key: 'basicInfo',
|
key: 'basicInfo',
|
||||||
type: 'basic',
|
type: 'basic',
|
||||||
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo'
|
url: '/eng/base/msstbprpt/getStcdInfo'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '监测数据',
|
name: '监测数据',
|
||||||
@ -608,7 +608,7 @@ const DWTabs: Array<any> = [
|
|||||||
key: 'basicInfo',
|
key: 'basicInfo',
|
||||||
type: 'basic',
|
type: 'basic',
|
||||||
default: true, // 默认显示
|
default: true, // 默认显示
|
||||||
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo'
|
url: '/eng/base/msstbprpt/getStcdInfo'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '实时视频',
|
name: '实时视频',
|
||||||
@ -668,7 +668,7 @@ const WETabs: Array<any> = [
|
|||||||
name: '基础信息',
|
name: '基础信息',
|
||||||
key: 'basicInfo',
|
key: 'basicInfo',
|
||||||
type: 'basic',
|
type: 'basic',
|
||||||
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo',
|
url: '/eng/base/msstbprpt/getStcdInfo',
|
||||||
default: true // 默认显示
|
default: true // 默认显示
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -729,7 +729,7 @@ const DEVICETABS: Array<any> = [
|
|||||||
name: '基础信息',
|
name: '基础信息',
|
||||||
key: 'basicInfo',
|
key: 'basicInfo',
|
||||||
type: 'basic',
|
type: 'basic',
|
||||||
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo',
|
url: '/eng/base/msstbprpt/getStcdInfo',
|
||||||
default: true // 默认显示
|
default: true // 默认显示
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
@ -739,7 +739,7 @@ const DZGJ: Array<any> = [
|
|||||||
name: '基础信息',
|
name: '基础信息',
|
||||||
key: 'basicInfo',
|
key: 'basicInfo',
|
||||||
type: 'basic',
|
type: 'basic',
|
||||||
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo',
|
url: '/eng/base/msstbprpt/getStcdInfo',
|
||||||
default: true // 默认显示
|
default: true // 默认显示
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -801,7 +801,7 @@ const VPTabs: Array<any> = [
|
|||||||
name: '基础信息',
|
name: '基础信息',
|
||||||
key: 'basicInfo',
|
key: 'basicInfo',
|
||||||
type: 'basic',
|
type: 'basic',
|
||||||
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo',
|
url: '/eng/base/msstbprpt/getStcdInfo',
|
||||||
default: true // 默认显示
|
default: true // 默认显示
|
||||||
},
|
},
|
||||||
// {
|
// {
|
||||||
@ -853,7 +853,7 @@ const videoTabs: Array<any> = [
|
|||||||
name: '基础信息',
|
name: '基础信息',
|
||||||
key: 'basicInfo',
|
key: 'basicInfo',
|
||||||
type: 'basic',
|
type: 'basic',
|
||||||
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo',
|
url: '/eng/base/msstbprpt/getStcdInfo',
|
||||||
default: true // 默认显示
|
default: true // 默认显示
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -869,7 +869,7 @@ const noLiveVideoTabs: Array<any> = [
|
|||||||
name: '基础信息',
|
name: '基础信息',
|
||||||
key: 'basicInfo',
|
key: 'basicInfo',
|
||||||
type: 'basic',
|
type: 'basic',
|
||||||
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo',
|
url: '/eng/base/msstbprpt/getStcdInfo',
|
||||||
default: true // 默认显示
|
default: true // 默认显示
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -885,7 +885,7 @@ const VATabs: Array<any> = [
|
|||||||
name: '基础信息',
|
name: '基础信息',
|
||||||
key: 'basicInfo',
|
key: 'basicInfo',
|
||||||
type: 'basic',
|
type: 'basic',
|
||||||
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo',
|
url: '/eng/base/msstbprpt/getStcdInfo',
|
||||||
default: true // 默认显示
|
default: true // 默认显示
|
||||||
},
|
},
|
||||||
// {
|
// {
|
||||||
@ -961,7 +961,7 @@ const WeatherTabs: any = [
|
|||||||
name: '基础信息',
|
name: '基础信息',
|
||||||
key: 'basicInfo',
|
key: 'basicInfo',
|
||||||
type: 'basic',
|
type: 'basic',
|
||||||
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo',
|
url: '/eng/base/msstbprpt/getStcdInfo',
|
||||||
default: true // 默认显示
|
default: true // 默认显示
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -969,9 +969,8 @@ const WeatherTabs: any = [
|
|||||||
key: '',
|
key: '',
|
||||||
type: '',
|
type: '',
|
||||||
url: ''
|
url: ''
|
||||||
},
|
}
|
||||||
|
];
|
||||||
]
|
|
||||||
|
|
||||||
// const WaterQualityMonitoring: any = [
|
// const WaterQualityMonitoring: any = [
|
||||||
// {
|
// {
|
||||||
@ -1020,7 +1019,7 @@ const WATabs: Array<any> = [
|
|||||||
name: '基础信息',
|
name: '基础信息',
|
||||||
key: 'basicInfo',
|
key: 'basicInfo',
|
||||||
type: 'basic',
|
type: 'basic',
|
||||||
url: '/api/dec-lygk-base-server/base/msstbprpt/getStcdInfo',
|
url: '/eng/base/msstbprpt/getStcdInfo',
|
||||||
default: true // 默认显示
|
default: true // 默认显示
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@ -1233,7 +1232,7 @@ const handleTabs = (modaldata: any) => {
|
|||||||
// case 'WE':
|
// case 'WE':
|
||||||
// return WETabs
|
// return WETabs
|
||||||
case 'MM':
|
case 'MM':
|
||||||
return WeatherTabs
|
return WeatherTabs;
|
||||||
// case 'WARN':
|
// case 'WARN':
|
||||||
// return WaterQualityMonitoring
|
// return WaterQualityMonitoring
|
||||||
// case 'AI':
|
// case 'AI':
|
||||||
|
|||||||
@ -9,33 +9,46 @@ import type { MapInterface, layer } from './map.d';
|
|||||||
export class MapCesium implements MapInterface {
|
export class MapCesium implements MapInterface {
|
||||||
private viewer: Cesium.Viewer | null = null;
|
private viewer: Cesium.Viewer | null = null;
|
||||||
private containerId = '';
|
private containerId = '';
|
||||||
|
private containerElement: HTMLElement | null = null;
|
||||||
|
private loadingOverlayElement: HTMLDivElement | null = null;
|
||||||
|
private loadingSpinnerAnimation: Animation | null = null;
|
||||||
|
private flightFallbackTimer: number | null = null;
|
||||||
|
|
||||||
init(container: HTMLElement, rectangle?: any): Promise<any> {
|
private readonly CHINA_CENTER = {
|
||||||
return new Promise(async (resolve, reject) => {
|
lng: 114.5,
|
||||||
|
lat: 36.5
|
||||||
|
};
|
||||||
|
private readonly CHINA_BOOT_HEIGHT = 20000000;
|
||||||
|
private readonly CHINA_DEFAULT_HEIGHT = 12000000;
|
||||||
|
private readonly CHINA_VIEW_ORIENTATION = {
|
||||||
|
heading: Cesium.Math.toRadians(0),
|
||||||
|
pitch: Cesium.Math.toRadians(-90),
|
||||||
|
roll: 0
|
||||||
|
};
|
||||||
|
|
||||||
|
async init(container: HTMLElement, _rectangle?: any): Promise<any> {
|
||||||
try {
|
try {
|
||||||
this.containerId = container.id;
|
this.containerId = container.id;
|
||||||
// ✅ 2. 初始化 Viewer
|
this.containerElement = container;
|
||||||
// 在 1.141.0 中,建议先不传 imageryProvider,或者使用默认的 Ion Imagery
|
this.showLoadingOverlay(container);
|
||||||
// 如果不想用 Ion,可以传一个空的 ImageryProviderCollection 或者稍后移除
|
|
||||||
this.viewer = new Cesium.Viewer(this.containerId, {
|
this.viewer = new Cesium.Viewer(container, {
|
||||||
animation: false, // 隐藏动画
|
animation: false,
|
||||||
timeline: false, // 隐藏时间线
|
timeline: false,
|
||||||
baseLayerPicker: false, // 隐藏底图选择器
|
baseLayerPicker: false,
|
||||||
fullscreenButton: false, // 隐藏全屏按钮
|
fullscreenButton: false,
|
||||||
vrButton: false, // 隐藏VR按钮
|
vrButton: false,
|
||||||
geocoder: false, // 隐藏地址搜索框
|
geocoder: false,
|
||||||
homeButton: false, // 隐藏首页按钮
|
homeButton: false,
|
||||||
sceneModePicker: false, // 隐藏场景模式选择器
|
sceneModePicker: false,
|
||||||
navigationHelpButton: false, // 隐藏导航帮助按钮
|
navigationHelpButton: false,
|
||||||
infoBox: false, // 隐藏信息框
|
infoBox: false,
|
||||||
selectionIndicator: false, // 隐藏选择指示器
|
selectionIndicator: false,
|
||||||
shouldAnimate: true // 开启动画
|
shouldAnimate: true
|
||||||
});
|
});
|
||||||
|
|
||||||
// // 清空默认图层
|
|
||||||
const layers = this.viewer.imageryLayers;
|
const layers = this.viewer.imageryLayers;
|
||||||
layers.removeAll();
|
layers.removeAll();
|
||||||
// // 添加 ArcGIS 底图(正确 URL)
|
|
||||||
const arcgisUrl =
|
const arcgisUrl =
|
||||||
'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/';
|
'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/';
|
||||||
|
|
||||||
@ -43,57 +56,160 @@ export class MapCesium implements MapInterface {
|
|||||||
arcgisUrl,
|
arcgisUrl,
|
||||||
{
|
{
|
||||||
enablePickFeatures: false,
|
enablePickFeatures: false,
|
||||||
// 可选:设置最大层级,避免请求过细的瓦片
|
|
||||||
maximumLevel: 19
|
maximumLevel: 19
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
// ✅ 6. 【关键】将 Provider 添加到图层集合中
|
|
||||||
layers.addImageryProvider(provider);
|
layers.addImageryProvider(provider);
|
||||||
|
|
||||||
// layers.addImageryProvider(provider);
|
|
||||||
|
|
||||||
// // ✅ 5. 场景优化
|
|
||||||
this.viewer.scene.globe.depthTestAgainstTerrain = false;
|
this.viewer.scene.globe.depthTestAgainstTerrain = false;
|
||||||
// // 启用高 DPI 支持(可选,根据性能需求)
|
|
||||||
this.viewer.useBrowserRecommendedResolution = true;
|
this.viewer.useBrowserRecommendedResolution = true;
|
||||||
|
|
||||||
// ✅ 6. 飞行到中国视角
|
// 先把相机直接摆到中国上空,避免初始化阶段短暂闪到美国默认视角。
|
||||||
// 使用 setTimeout 确保 DOM 和 WebGL 上下文完全就绪
|
this.setBootChinaView();
|
||||||
// setTimeout(() => {
|
await this.waitForViewerReady();
|
||||||
// this.flyToChina();
|
await this.flyToChina();
|
||||||
// resolve(this.viewer);
|
|
||||||
// }, 100);
|
return this.viewer;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
this.hideLoadingOverlay();
|
||||||
console.error('Cesium Init Critical Error:', error);
|
console.error('Cesium Init Critical Error:', error);
|
||||||
reject(error);
|
throw error;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private flyToChina() {
|
private setBootChinaView() {
|
||||||
if (!this.viewer) return;
|
if (!this.viewer) return;
|
||||||
|
|
||||||
// 取消之前的飞行动画(如果有)
|
|
||||||
this.viewer.camera.cancelFlight();
|
this.viewer.camera.cancelFlight();
|
||||||
|
this.viewer.camera.setView({
|
||||||
|
destination: Cesium.Cartesian3.fromDegrees(
|
||||||
|
this.CHINA_CENTER.lng,
|
||||||
|
this.CHINA_CENTER.lat,
|
||||||
|
this.CHINA_BOOT_HEIGHT
|
||||||
|
),
|
||||||
|
orientation: this.CHINA_VIEW_ORIENTATION
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private waitForViewerReady(): Promise<void> {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
requestAnimationFrame(() => {
|
||||||
|
this.viewer?.scene.requestRender();
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private flyToChina(): Promise<void> {
|
||||||
|
return new Promise(resolve => {
|
||||||
|
if (!this.viewer) {
|
||||||
|
this.hideLoadingOverlay();
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const finish = () => {
|
||||||
|
if (this.flightFallbackTimer !== null) {
|
||||||
|
window.clearTimeout(this.flightFallbackTimer);
|
||||||
|
this.flightFallbackTimer = null;
|
||||||
|
}
|
||||||
|
this.hideLoadingOverlay();
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
this.viewer.camera.cancelFlight();
|
||||||
|
this.flightFallbackTimer = window.setTimeout(() => {
|
||||||
|
finish();
|
||||||
|
}, 2600);
|
||||||
|
|
||||||
this.viewer.camera.flyTo({
|
this.viewer.camera.flyTo({
|
||||||
destination: Cesium.Cartesian3.fromDegrees(104.5, 36.5, 4000000),
|
destination: Cesium.Cartesian3.fromDegrees(
|
||||||
orientation: {
|
this.CHINA_CENTER.lng,
|
||||||
heading: Cesium.Math.toRadians(0),
|
this.CHINA_CENTER.lat,
|
||||||
pitch: Cesium.Math.toRadians(-90),
|
this.CHINA_DEFAULT_HEIGHT
|
||||||
roll: 0.0
|
),
|
||||||
},
|
orientation: this.CHINA_VIEW_ORIENTATION,
|
||||||
duration: 2
|
duration: 1.8,
|
||||||
|
complete: finish,
|
||||||
|
cancel: finish
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private showLoadingOverlay(container: HTMLElement) {
|
||||||
|
this.hideLoadingOverlay();
|
||||||
|
|
||||||
|
const overlay = document.createElement('div');
|
||||||
|
overlay.setAttribute('data-map-cesium-loading', 'true');
|
||||||
|
overlay.style.position = 'absolute';
|
||||||
|
overlay.style.inset = '0';
|
||||||
|
overlay.style.display = 'flex';
|
||||||
|
overlay.style.flexDirection = 'column';
|
||||||
|
overlay.style.alignItems = 'center';
|
||||||
|
overlay.style.justifyContent = 'center';
|
||||||
|
overlay.style.gap = '12px';
|
||||||
|
overlay.style.background =
|
||||||
|
'linear-gradient(180deg, rgba(7, 20, 36, 0.72) 0%, rgba(7, 20, 36, 0.46) 100%)';
|
||||||
|
overlay.style.backdropFilter = 'blur(2px)';
|
||||||
|
overlay.style.zIndex = '30';
|
||||||
|
overlay.style.pointerEvents = 'auto';
|
||||||
|
|
||||||
|
const spinner = document.createElement('div');
|
||||||
|
spinner.style.width = '36px';
|
||||||
|
spinner.style.height = '36px';
|
||||||
|
spinner.style.borderRadius = '50%';
|
||||||
|
spinner.style.border = '3px solid rgba(255, 255, 255, 0.25)';
|
||||||
|
spinner.style.borderTopColor = '#ffffff';
|
||||||
|
this.loadingSpinnerAnimation = spinner.animate(
|
||||||
|
[{ transform: 'rotate(0deg)' }, { transform: 'rotate(360deg)' }],
|
||||||
|
{
|
||||||
|
duration: 900,
|
||||||
|
iterations: Infinity,
|
||||||
|
easing: 'linear'
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const label = document.createElement('div');
|
||||||
|
label.textContent = '3D场景加载中...';
|
||||||
|
label.style.color = '#ffffff';
|
||||||
|
label.style.fontSize = '14px';
|
||||||
|
label.style.lineHeight = '20px';
|
||||||
|
label.style.letterSpacing = '0.5px';
|
||||||
|
label.style.textShadow = '0 1px 2px rgba(0, 0, 0, 0.35)';
|
||||||
|
|
||||||
|
overlay.appendChild(spinner);
|
||||||
|
overlay.appendChild(label);
|
||||||
|
container.appendChild(overlay);
|
||||||
|
this.loadingOverlayElement = overlay;
|
||||||
|
}
|
||||||
|
|
||||||
|
private hideLoadingOverlay() {
|
||||||
|
if (this.loadingSpinnerAnimation) {
|
||||||
|
this.loadingSpinnerAnimation.cancel();
|
||||||
|
this.loadingSpinnerAnimation = null;
|
||||||
|
}
|
||||||
|
if (this.loadingOverlayElement?.parentNode) {
|
||||||
|
this.loadingOverlayElement.parentNode.removeChild(
|
||||||
|
this.loadingOverlayElement
|
||||||
|
);
|
||||||
|
}
|
||||||
|
this.loadingOverlayElement = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
destroy(): void {
|
destroy(): void {
|
||||||
|
if (this.flightFallbackTimer !== null) {
|
||||||
|
window.clearTimeout(this.flightFallbackTimer);
|
||||||
|
this.flightFallbackTimer = null;
|
||||||
|
}
|
||||||
|
this.hideLoadingOverlay();
|
||||||
if (this.viewer) {
|
if (this.viewer) {
|
||||||
// ✅ 关键:销毁前取消所有动画和事件
|
|
||||||
this.viewer.camera.cancelFlight();
|
this.viewer.camera.cancelFlight();
|
||||||
this.viewer.destroy();
|
this.viewer.destroy();
|
||||||
this.viewer = null;
|
this.viewer = null;
|
||||||
}
|
}
|
||||||
|
this.containerElement = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
flyTopanto(position: number[], zoom: number): void {
|
flyTopanto(position: number[], zoom: number): void {
|
||||||
@ -113,37 +229,41 @@ export class MapCesium implements MapInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ... 其他空实现方法保持不变 ...
|
// ... 其他空实现方法保持不变 ...
|
||||||
jdPanelControlShowAndHidden(baseid: string, isAll: boolean): void {}
|
jdPanelControlShowAndHidden(_baseid: string, _isAll: boolean): void {}
|
||||||
mdLayerShowOrHidden(): void {}
|
mdLayerShowOrHidden(): void {}
|
||||||
addBaseDataLayer(layer: any): void {}
|
addBaseDataLayer(_layer: any): void {}
|
||||||
controlBaseLayerTreeShowAndHidden(
|
controlBaseLayerTreeShowAndHidden(
|
||||||
layerType: string,
|
_layerType: string,
|
||||||
key: string,
|
_key: string,
|
||||||
checked: boolean
|
_checked: boolean
|
||||||
): void {}
|
): void {}
|
||||||
mdLayerTreeShowOrHidden(layerType: string, checked?: boolean): void {}
|
mdLayerTreeShowOrHidden(_layerType: string, _checked?: boolean): void {}
|
||||||
hasLayer(layerKey: string): boolean {
|
hasLayer(_layerKey: string): boolean {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
hasBaseLayer(layerKey: string): boolean {
|
hasBaseLayer(_layerKey: string): boolean {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
setLegendPointVisible(
|
setLegendPointVisible(
|
||||||
layerKey: string,
|
_layerKey: string,
|
||||||
anchoPointState: string,
|
_anchoPointState: string,
|
||||||
checked: boolean
|
_checked: boolean
|
||||||
): void {}
|
): void {}
|
||||||
addInitDataLayer(pointData: any[], layerType: any, mdoptions?: any): void {}
|
addInitDataLayer(
|
||||||
switchView(type: any): void {}
|
_pointData: any[],
|
||||||
fitBounds(bounds: any): void {}
|
_layerType: any,
|
||||||
baseLayerSwitcher(key: string): void {}
|
_mdoptions?: any
|
||||||
|
): void {}
|
||||||
|
switchView(_type: any): void {}
|
||||||
|
fitBounds(_bounds: any): void {}
|
||||||
|
baseLayerSwitcher(_key: string): void {}
|
||||||
addTertiarybasinLayer(
|
addTertiarybasinLayer(
|
||||||
layer: layer,
|
_layer: layer,
|
||||||
fillcolor: any,
|
_fillcolor: any,
|
||||||
outlineColor: any,
|
_outlineColor: any,
|
||||||
datas: any
|
_datas: any
|
||||||
): void {}
|
): void {}
|
||||||
hideTertiarybasinLayer(layer: layer): void {}
|
hideTertiarybasinLayer(_layer: layer): void {}
|
||||||
zoomToggle(type: 'out' | 'in'): void {
|
zoomToggle(type: 'out' | 'in'): void {
|
||||||
if (!this.viewer) return;
|
if (!this.viewer) return;
|
||||||
const factor = 1.5;
|
const factor = 1.5;
|
||||||
@ -195,7 +315,6 @@ export class MapCesium implements MapInterface {
|
|||||||
|
|
||||||
// 清理临时元素
|
// 清理临时元素
|
||||||
document.body.removeChild(link);
|
document.body.removeChild(link);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to export map image:', error);
|
console.error('Failed to export map image:', error);
|
||||||
if (error instanceof DOMException && error.name === 'SecurityError') {
|
if (error instanceof DOMException && error.name === 'SecurityError') {
|
||||||
@ -207,5 +326,5 @@ export class MapCesium implements MapInterface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ... 其他代码 ...
|
// ... 其他代码 ...
|
||||||
initPopupOverlay(popupContainer: HTMLDivElement): void {}
|
initPopupOverlay(_popupContainer: HTMLDivElement): void {}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -54,7 +54,6 @@ service.interceptors.response.use(
|
|||||||
(response: any) => {
|
(response: any) => {
|
||||||
const { status, msg } = response;
|
const { status, msg } = response;
|
||||||
if (status === 200) {
|
if (status === 200) {
|
||||||
console.log(response.data);
|
|
||||||
if (response.data.code == 401) {
|
if (response.data.code == 401) {
|
||||||
message.error(response.data.msg || '请求失败');
|
message.error(response.data.msg || '请求失败');
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user