3
0

4 Revīzijas b17c3b2e5a ... cd86e8b4a5

Autors SHA1 Ziņojums Datums
  houzekong cd86e8b4a5 [Feat 0000] 首页地图实现改进及CAD初始化代码优化 3 nedēļas atpakaļ
  houzekong f58152168a [Mod 0000] 地图标记点信息更新 3 nedēļas atpakaļ
  houzekong 158d0cb36e [Feat 0000] 缓存刷新功能对接 3 nedēļas atpakaļ
  houzekong 79b5d23f4c [Fix 0000] 修复问题反馈页面的接口对接问题及数据质量跳转问题 3 nedēļas atpakaļ

+ 3 - 2
src/api/common/api.ts

@@ -14,7 +14,8 @@ enum Api {
   getDictItems = '/sys/dict/getDictItems/',
   getTableList = '/sys/user/queryUserComponentData',
   getCategoryData = '/sys/category/loadAllData',
-  refreshDragCache = '/drag/page/refreshCache',
+  refreshDragCache = '/province/device/refreshProvince',
+  // refreshDragCache = '/drag/page/refreshCache',
   refreshDefaultIndexCache = '/sys/sysRoleIndex/cleanDefaultIndexCache',
   //异步获取部门和岗位
   queryDepartAndPostTreeSync = '/sys/sysDepart/queryDepartAndPostTreeSync',
@@ -182,7 +183,7 @@ export const uploadMyFile = (url, data) => {
  * 刷新仪表盘缓存
  * @param params
  */
-export const refreshDragCache = () => defHttp.get({ url: Api.refreshDragCache }, { isTransformResponse: false });
+export const refreshDragCache = () => defHttp.post({ url: Api.refreshDragCache }, { isTransformResponse: false });
 /**
  * 刷新默认首页缓存
  * @param params

+ 1 - 1
src/api/sys/map.ts

@@ -103,7 +103,7 @@ export function getGeoJSON(params: any) {
         deptId: ref.id,
         parentId: ref.parentId,
         name: ref.departName,
-        count: 1,
+        count: element.count,
         isLeaf: false,
         dynamic: true,
         alarmLevel: element.alarmLevel,

+ 73 - 107
src/layouts/default/feature/SimpleMap.vue

@@ -25,18 +25,19 @@
 <script lang="ts" setup>
   import 'vjmap/dist/vjmap.min.css';
   import { ref, onMounted, onUnmounted, computed } from 'vue';
-  import { Button } from 'ant-design-vue';
+  import { Button, message } from 'ant-design-vue';
   import { useAppStore } from '/@/store/modules/app';
   // import { useRoute } from 'vue-router';
   import vjmap from 'vjmap';
-  import type vjmap3d from 'vjmap3d';
+  // import type vjmap3d from 'vjmap3d';
   import { getGeoJSON } from '/@/api/sys/map';
   import { useMineDepartmentStore } from '/@/store/modules/mine';
   import { get, last } from 'lodash-es';
   import { env } from '/@/views/system/cadFile/env';
   import { StatusColorEnum } from '/@/enums/jeecgEnum';
-  import { generatePopupContent, generateSimplePopup } from './SimpleMap/simpleMap.data';
-  import { initMap2d } from '/@/views/system/cadFile/app';
+  import { generatePopupContent, generateSimplePopup } from './hooks/popup';
+  import { initMap2d, renderGoafMarkers } from '/@/views/system/cadFile/app';
+  import { getGoafList } from '/@/views/system/cadFile/cad.api';
 
   const appStore = useAppStore();
   const mineStore = useMineDepartmentStore();
@@ -44,9 +45,9 @@
   // const route = useRoute();
 
   const mapContainer = ref<HTMLElement>();
+  let svc = new vjmap.Service(env.serviceUrl, env.accessToken);
   let map: vjmap.Map;
-  let app: vjmap3d.App;
-  let cadOpened = false;
+  // let app: vjmap3d.App;
 
   const GEO_LAYER_ID = 'geojson-layer';
   const CIRCLE_LAYER_ID = 'circle-layer';
@@ -64,6 +65,7 @@
   const historyStack = ref([DEFAULT_NODE]); // 用于存储历史节点的栈,实现返回上一级功能
   const isTopLevel = computed(() => historyStack.value.length === 1);
 
+  /** 根据GeoJSON文件绘制省市边界和填充色到地图上,其将作为一个新图层 */
   function initMapGeoJSON(map: vjmap.Map, geojson) {
     map.addSource('geojson-data', {
       type: 'geojson',
@@ -84,6 +86,7 @@
     map.hoverFeatureState(GEO_LAYER_ID);
   }
 
+  /** 根据标记点信息绘制两个图层,一个圆标点图层和一个文本标点图层 */
   function initMapMarker(map: vjmap.Map, markers) {
     // 绘制背景圆圈
     const circles = new vjmap.Circle({
@@ -129,82 +132,102 @@
     });
   }
 
+  /** 标记点点击后,如果不是叶节点那么聚焦到下一级,如果已经是叶节点了则显示该节点的CAD地图 */
   function markerClickHandler() {
     const node: any = last(historyStack.value);
     if (!node) return;
 
     appStore.setSimpleMapParams({ deptId: node.id, isLeaf: node.isLeaf });
 
+    // 标点点击后,如果是叶节点需要显示CAD图
     if (node.isLeaf) {
-      document.getElementById('map2dContainer')!.style.display = 'block';
-      initMap2d('map2dContainer').then((r) => {
-        app = r!;
-        cadOpened = true;
+      toggleCADMap(true);
+    } else {
+      map.setFilter(CIRCLE_LAYER_ID, ['==', node.id, ['get', 'parentId']]);
+      map.setFilter(SYMBOL_LAYER_ID, ['==', node.id, ['get', 'parentId']]);
+      // map.setFilter(GEO_LAYER_ID, ['in', node.id, ['get', 'children']]);
+
+      map.flyTo({
+        center: [node.longitude, node.latitude],
+        zoom: node.zoom,
       });
-      return;
     }
-
-    map.setFilter(CIRCLE_LAYER_ID, ['==', node.id, ['get', 'parentId']]);
-    map.setFilter(SYMBOL_LAYER_ID, ['==', node.id, ['get', 'parentId']]);
-    // map.setFilter(GEO_LAYER_ID, ['in', node.id, ['get', 'children']]);
-
-    map.flyTo({
-      center: [node.longitude, node.latitude],
-      zoom: node.zoom,
-    });
   }
 
-  function revertStack() {
-    if (cadOpened) {
-      document.getElementById('map2dContainer')!.style.display = 'none';
-      app.destroy();
-    }
-    if (historyStack.value.length > 1) {
-      historyStack.value.pop();
+  /** 返回上一级操作,如果存在CAD地图,则切换至底图地图,否则回退历史记录栈 */
+  async function revertStack() {
+    if (historyStack.value.length === 1) return;
+    historyStack.value.pop()!;
+
+    toggleCADMap(false).then(() => {
       markerClickHandler();
-    }
+    });
   }
 
+  /** 初始化地图对象,仅负责初始化地图及其瓦片底图,待map加载完成后返回 */
   async function initMap(HTMLElement) {
-    let svc = new vjmap.Service(env.serviceUrl, env.accessToken);
-    map = new vjmap.Map({
+    const map = new vjmap.Map({
       container: HTMLElement,
       // 这个zoom配合了getGeoJSON,要改两边都要改
       maxZoom: 20,
       minZoom: 1,
       center: [DEFAULT_NODE.longitude, DEFAULT_NODE.latitude],
       zoom: DEFAULT_NODE.zoom,
-      // style: {
-      //   version: 8,
-      //   name: 'my-style',
-      //   // 关键:配置字体服务地址
-      //   glyphs: 'https://fonts.vjmap.com/{fontstack}/{range}.pbf',
-      //   sources: {},
-      //   layers: [],
-      // },
       style: svc.rasterStyle('https://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}'),
     });
-    // const [geojson, __, markers] = await getGeoJSON({ deptId: mineStore.getRoot?.id, pageSize: 9999 });
-    const [___, [geojson, __, markers]] = await Promise.all([map.onLoad(), getGeoJSON({ deptId: mineStore.getRootId, pageSize: 9999 })]);
+    await map.onLoad();
 
-    // await map.onLoad();
-    // initMapWMS(map);
-    initMapGeoJSON(map, geojson);
-    initMapMarker(map, markers);
+    return map;
   }
 
-  onMounted(() => {
-    if (!mineStore.getRoot?.isLeaf) {
-      initMap(mapContainer.value!);
+  let cadOpened: boolean;
+  /** 切换CAD地图和瓦片地图的显示,通过重新初始化进行切换,避免出现动画异常和多个DOM节点 */
+  async function toggleCADMap(visiable: boolean) {
+    if (cadOpened === visiable) return;
+
+    cadOpened = visiable;
+    map?.destory();
+    if (visiable) {
+      const hide = message.loading('CAD图加载中...', 0);
+      const [m, res] = await Promise.all([
+        initMap2d(mapContainer.value!, {
+          // fileUrl: node.url
+          style: { backcolor: 0xe6f3ff },
+        }),
+        getGoafList({
+          mineCode: mineStore.getRoot?.fax,
+          deptId: mineStore.getRootId,
+        }),
+      ]);
+      hide();
+      map = m;
+
+      renderGoafMarkers(res); // 渲染标记
     } else {
-      document.getElementById('map2dContainer')!.style.display = 'block';
-      initMap2d('map2dContainer')!.then((r) => (app = r!));
+      const hide = message.loading('地图加载中...', 0);
+      const [m, res] = await Promise.all([
+        initMap(mapContainer.value!),
+        getGeoJSON({
+          deptId: mineStore.getRootId,
+          pageSize: 9999,
+        }),
+      ]);
+      const [geojson, __, markers] = res;
+      hide();
+      map = m;
+
+      initMapGeoJSON(map, geojson);
+      initMapMarker(map, markers);
     }
+  }
+
+  onMounted(() => {
+    toggleCADMap(mineStore.getRoot?.isLeaf || false);
   });
 
   onUnmounted(() => {
     map?.remove();
-    app?.destroy();
+    // app?.destroy();
   });
 </script>
 
@@ -232,63 +255,6 @@
     // filter: sepia(0.3) hue-rotate(180deg) saturate(1.8);
   }
 
-  // .leaflet-popup {
-  //   color: @white;
-  //   &-content-wrapper {
-  //     background-color: @map-popup-bg;
-  //   }
-
-  //   &-tip {
-  //     background-color: @map-popup-bg;
-  //   }
-
-  //   .leaflet-popup-content {
-  //     margin: 13px 20px;
-  //     min-width: 280px;
-  //     // &__popup {
-  //     // }
-
-  //     &__divider {
-  //       height: 1px;
-  //       background-color: @white;
-  //       margin: 10px 5px;
-  //     }
-
-  //     &__title {
-  //       color: @white;
-  //       text-align: center;
-  //     }
-
-  //     &__board {
-  //       padding: 10px;
-  //       border-radius: 5px;
-  //       background-color: @map-popup-board-bg;
-  //       display: flex;
-  //       justify-content: space-between;
-  //     }
-
-  //     &__table {
-  //       color: @white;
-  //       text-align: center;
-
-  //       thead {
-  //         height: 20px;
-  //       }
-  //       tbody {
-  //         tr {
-  //           background-color: @map-popup-table-th-bg;
-  //         }
-  //         tr:nth-child(even) {
-  //           background-color: @map-popup-table-theven-bg;
-  //         }
-  //         td {
-  //           padding: 5px;
-  //         }
-  //       }
-  //     }
-  //   }
-  // }
-
   .vjmapgis-popup {
     color: @white;
     // &-content-wrapper {

+ 0 - 267
src/layouts/default/feature/SimpleMap/SimpleMap copy.vue

@@ -1,267 +0,0 @@
-<template>
-  <div ref="mapContainer" class="map-container"></div>
-  <div v-if="!isTopLevel" class="map-reset-btn">
-    <Button type="primary" @click="reset">重置</Button>
-  </div>
-</template>
-
-<script lang="ts" setup>
-  import { ref, onMounted, onUnmounted, computed } from 'vue';
-  import L from 'leaflet';
-  import 'leaflet/dist/leaflet.css';
-  import { Button } from 'ant-design-vue';
-  import { getMarkers } from '/@/api/sys/map';
-  import { DEFAULT_LAYER_ID, regionColorMap, tileLayerConfigs, generatePopupContent, getRootMarkerIcon, GEOJSON_LAYER_ID } from './simpleMap.data';
-  import { useAppStore } from '/@/store/modules/app';
-  import { isEmpty } from '/@/utils/is';
-  import { useRoute } from 'vue-router';
-  import { PageEnum } from '/@/enums/pageEnum';
-
-  const appStore = useAppStore();
-  const route = useRoute();
-  const mapContainer = ref(null);
-
-  const isTopLevel = computed(() => {
-    return isEmpty(appStore.getSimpleMapParams);
-  });
-
-  let map: L.Map | null = null;
-
-  // 图层组 Map(基于所有区域编码初始化)
-  const layerMap = new Map<string, L.LayerGroup>([
-    [DEFAULT_LAYER_ID, L.featureGroup()],
-    [GEOJSON_LAYER_ID, L.featureGroup()],
-    // // 西安
-    // ['1566101', L.featureGroup()],
-    // // 铜川
-    // ['1566102', L.featureGroup()],
-    // // 宝鸡
-    // ['1566103', L.featureGroup()],
-    // // 咸阳
-    // ['1566104', L.featureGroup()],
-    // // 渭南
-    // ['1566105', L.featureGroup()],
-    // // 延安
-    // ['1566106', L.featureGroup()],
-    // // 汉中
-    // ['1566107', L.featureGroup()],
-    // // 榆林
-    // ['1566108', L.featureGroup()],
-    // // 安康
-    // ['1566109', L.featureGroup()],
-    // // 商洛
-    // ['1566111', L.featureGroup()],
-  ]);
-
-  onMounted(async () => {
-    if (!mapContainer.value) return;
-
-    reset();
-
-    map = L.map(mapContainer.value, {
-      center: [35.841, 108.94],
-      zoom: 7,
-      zoomControl: false,
-      attributionControl: false,
-    });
-
-    initTileLayers();
-
-    if (route.path === PageEnum.BASE_HOME) {
-      await initMarkers();
-      await initGeoJSON();
-    }
-
-    layerMap.get(DEFAULT_LAYER_ID)?.addTo(map);
-  });
-
-  onUnmounted(() => {
-    map?.remove();
-  });
-
-  /** 初始化瓦片图层 */
-  function initTileLayers() {
-    tileLayerConfigs.forEach((config) => {
-      const layer = L.tileLayer(config.url, config.options);
-      if (config.addByDefault) {
-        layer.addTo(map!);
-      }
-    });
-  }
-
-  /** 加载 GeoJSON 并添加到对应图层组 */
-  async function initGeoJSON() {
-    const response = await fetch('/js/shanxi.geo.json');
-    const ShanXiGeoJSON = await response.json();
-
-    const geoJsonLayer = L.geoJSON(ShanXiGeoJSON, {
-      style(feature) {
-        const gb = feature.properties.gb.slice(0, -2);
-        return {
-          fillColor: regionColorMap[gb] || '#14baff',
-          fillOpacity: 0.2,
-          weight: 2,
-          opacity: 1,
-          color: '#ff9100',
-        };
-      },
-      onEachFeature(_, layer) {
-        layer.on({
-          mouseover: (e) => {
-            e.target.setStyle({ weight: 2, fillOpacity: 0.6 });
-            e.target.bringToFront();
-          },
-          mouseout: (e) => {
-            geoJsonLayer.resetStyle(e.target);
-          },
-        });
-      },
-    });
-
-    map.addLayer(geoJsonLayer);
-    // geoJsonLayer.addTo(layerMap.get(GEOJSON_LAYER_ID));
-    // layerMap.forEach((group, code) => {
-    //   const features = ShanXiGeoJSON.features.filter((feature: any) => {
-    //     if (code === DEFAULT_LAYER_ID) return true;
-    //     return feature.properties.gb.startsWith(code);
-    //   });
-    // });
-  }
-
-  /** 初始化所有标记点 */
-  async function initMarkers() {
-    const records = await getMarkers({ pageNo: 0, pageSize: 9999 });
-
-    records.forEach((item, index) => {
-      if (!layerMap.has(item.id)) {
-        layerMap.set(item.id, L.featureGroup());
-      }
-      // 创建根标记点
-      const iconConfig = getRootMarkerIcon(40, item.children.length, Object.values(regionColorMap)[index]);
-
-      const marker = L.marker([item.lat, item.lng], {
-        icon: L.divIcon(iconConfig),
-        riseOnHover: true,
-      }).on('click', () => {
-        // 将绑定到该标记点的所有子标记点添加到地图上
-        layerMap.forEach((group, code) => {
-          map?.removeLayer(group);
-          if (code === item.id) {
-            map?.addLayer(group);
-            map?.flyToBounds(group.getBounds());
-            // map?.flyToBounds(layerMap.get(item.id).getBounds());
-          }
-        });
-        appStore.setSimpleMapParams({ mineCode: item.mineCode });
-      });
-      // 将该标记点添加到默认地图上
-      marker.addTo(layerMap.get(DEFAULT_LAYER_ID)!);
-
-      // 创建子标记点
-      if (!item.children.length) return;
-
-      item.children.forEach((child: any) => {
-        const popupContent = generatePopupContent(child);
-        const childMarker = L.marker([child.lat, child.lng]).bindPopup(popupContent);
-        childMarker.addTo(layerMap.get(item.id)!);
-      });
-    });
-  }
-
-  /** 重置视图到默认图层 */
-  function reset() {
-    layerMap.forEach((group, code) => {
-      map?.removeLayer(group);
-      if (code === DEFAULT_LAYER_ID) {
-        map?.addLayer(group);
-        map?.flyToBounds(group.getBounds());
-      }
-    });
-
-    appStore.setSimpleMapParams({});
-  }
-</script>
-
-<style lang="less">
-  .map-container {
-    flex: 1;
-    height: 100%;
-    width: 100%;
-    position: fixed;
-    top: 0;
-    left: 0;
-    z-index: @simple-map-z-index;
-    // filter: grayscale(80%) invert(0%) sepia(10%) hue-rotate(150deg) saturate(200%) brightness(100%) contrast(100%);
-  }
-
-  .light-blue-theme {
-    filter: invert(0.1) hue-rotate(190deg) saturate(2);
-    // filter: sepia(0.3) hue-rotate(180deg) saturate(1.8);
-  }
-
-  .leaflet-popup {
-    color: @white;
-    &-content-wrapper {
-      background-color: @map-popup-bg;
-    }
-
-    &-tip {
-      background-color: @map-popup-bg;
-    }
-
-    .leaflet-popup-content {
-      margin: 13px 20px;
-      min-width: 280px;
-      // &__popup {
-      // }
-
-      &__divider {
-        height: 1px;
-        background-color: @white;
-        margin: 10px 5px;
-      }
-
-      &__title {
-        color: @white;
-        text-align: center;
-      }
-
-      &__board {
-        padding: 10px;
-        border-radius: 5px;
-        background-color: @map-popup-board-bg;
-        display: flex;
-        justify-content: space-between;
-      }
-
-      &__table {
-        color: @white;
-        text-align: center;
-
-        thead {
-          height: 20px;
-        }
-        tbody {
-          tr {
-            background-color: @map-popup-table-th-bg;
-          }
-          tr:nth-child(even) {
-            background-color: @map-popup-table-theven-bg;
-          }
-          td {
-            padding: 5px;
-          }
-        }
-      }
-    }
-  }
-
-  .map-reset-btn {
-    position: absolute;
-    right: 30%; /* 距离右边 20px */
-    z-index: 2; /* 确保在地图控件之上 */
-    top: @header-height;
-    // padding: 10px 15px;
-    margin: 10px 0;
-  }
-</style>

+ 0 - 147
src/layouts/default/feature/SimpleMap/simpleMap.data.ts

@@ -1,147 +0,0 @@
-import { get } from 'lodash-es';
-import { StatusColorEnum } from '/@/enums/jeecgEnum';
-
-// 默认图层ID
-export const DEFAULT_LAYER_ID = 'default';
-export const GEOJSON_LAYER_ID = 'GEO';
-
-// 区域颜色映射(包含所有需要用到的编码)
-export const regionColorMap: Record<string, string> = {
-  DEFAULT_LAYER_ID: '#14baff',
-  '1566101': '#14baff',
-  '1566102': '#14baff',
-  '1566103': '#ffff12',
-  '1566104': '#ffff12',
-  '1566105': '#ffab12',
-  '1566106': '#ffab12',
-  '1566107': '#fe1414',
-  '1566108': '#fe1414',
-  '1566109': '#fe1414',
-  '1566111': '#fe1414',
-  //   '810321182c6a40fa92d5acccdba34ce3': '#ffff12',
-  //   '63afd272d0e24c7e9c1475c6498f0c80': '#ffab12',
-};
-
-// 瓦片图层配置(不含运行时的 layer 属性)
-export const tileLayerConfigs = [
-  {
-    name: '基准瓦片',
-    url: 'https://shaanxizhxx.chinamine-safety.gov.cn/zh1/8/33/17.png',
-    addByDefault: false,
-    options: {
-      maxZoom: 18,
-      attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
-    },
-  },
-  {
-    name: '卫星图',
-    url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
-    addByDefault: false,
-    options: {
-      maxZoom: 18,
-      attribution: '© Esri',
-    },
-  },
-  {
-    name: '高德地图',
-    url: 'https://webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
-    // url: 'https://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
-    addByDefault: true,
-    options: {
-      className: 'light-blue-theme',
-      maxZoom: 18,
-      subdomains: ['1', '2', '3', '4'],
-      attribution: '© 高德地图',
-    },
-  },
-];
-
-// 状态文本与颜色映射(直接使用枚举值)
-export const linkStatusText = ['断线', '正常', '标校'];
-export const alarmLevelText = ['暂无信息', '低风险', '一般风险', '较高风险', '高风险'];
-export const alarmLevelColor = [StatusColorEnum.red, StatusColorEnum.blue, StatusColorEnum.gold, StatusColorEnum.purple, StatusColorEnum.red];
-
-/**
- * 生成标准标记点(子节点)的弹出框 HTML
- */
-export function generatePopupContent(data: any): string {
-  const { goafDataList = '[]', id, mineName, parentArea, gasLevelName, gjMineStatus } = data;
-  const list = JSON.parse(goafDataList);
-  const tbodyInnerHtml = list.reduce((html, { devicePos, alarmLevel, linkStatus }, index) => {
-    html += `
-      <tr>
-        <td>${index + 1}</td>
-        <td>${devicePos}</td>
-        <td style="color: ${get(alarmLevelColor, alarmLevel, alarmLevelColor[0])}">${get(alarmLevelText, alarmLevel, alarmLevelText[0])}</td>
-        <td style="color: ${get(alarmLevelColor, linkStatus, alarmLevelColor[0])}">${get(linkStatusText, linkStatus, linkStatusText[0])}</td>
-      </tr>
-    `;
-    return html;
-  }, '');
-
-  return `
-    <div class="vjmapgis-popup-content__title">${mineName}</div>
-    <div class="vjmapgis-popup-content__divider"></div>
-    <div class="vjmapgis-popup-content__board mb-1">
-      <div class="mr-5px">所属上级</div>
-      <div>${parentArea}</div>
-    </div>
-    <div class="vjmapgis-popup-content__board mb-1">
-      <div class="mr-5px">瓦斯等级</div>
-      <div>${gasLevelName}</div>
-    </div>
-    <div class="vjmapgis-popup-content__board">
-      <div class="mr-5px">生产状态</div>
-      <div>${gjMineStatus}</div>
-    </div>
-    <div class="vjmapgis-popup-content__divider"></div>
-    <table id="${id}" class="vjmapgis-popup-content__table w-full mt-1">
-      <thead>
-        <tr>
-          <th>序号</th>
-          <th>采空区名称</th>
-          <th>风险等级</th>
-          <th>通讯状态</th>
-        </tr>
-      </thead>
-      <tbody>
-        ${tbodyInnerHtml.length ? tbodyInnerHtml : '<tr><td colspan="4">暂无数据</td></tr>'}
-      </tbody>
-    </table>
-  `;
-}
-
-export function generateSimplePopup(data: any): string {
-  const { name } = data;
-
-  return `
-    <div class="vjmapgis-popup-content__strong">${name}</div>
-  `;
-}
-
-/**
- * 生成根标记点的图标配置
- */
-export function getRootMarkerIcon(size: number, count: number, color: string) {
-  return {
-    className: 'numbered-marker',
-    html: `
-      <svg width="${size}" height="${size}" viewBox="0 0 ${size} ${size}" style="overflow: visible;">
-        <circle cx="${size / 2}" cy="${size / 2}" r="${size / 2 - 2}" fill="${color}" />
-        <text
-          x="${size / 2}"
-          y="${size / 2}"
-          text-anchor="middle"
-          dominant-baseline="middle"
-          fill="#ddd"
-          font-size="16"
-          font-weight="bold"
-          font-family="Arial, sans-serif"
-        >${count}</text>
-      </svg>
-    `,
-    iconSize: [size, size],
-    iconAnchor: [size / 2, size / 2],
-    popupAnchor: [0, -size / 2],
-  };
-}

+ 65 - 0
src/layouts/default/feature/hooks/popup.ts

@@ -0,0 +1,65 @@
+import { get } from 'lodash-es';
+import { StatusColorEnum } from '/@/enums/jeecgEnum';
+
+// 状态文本与颜色映射(直接使用枚举值)
+export const linkStatusText = ['断线', '正常', '标校'];
+export const alarmLevelText = ['暂无信息', '低风险', '一般风险', '较高风险', '高风险'];
+export const alarmLevelColor = [StatusColorEnum.red, StatusColorEnum.blue, StatusColorEnum.gold, StatusColorEnum.purple, StatusColorEnum.red];
+
+/**
+ * 生成标准标记点(子节点)的弹出框 HTML
+ */
+export function generatePopupContent(data: any): string {
+  const { goafDataList = '[]', id, mineName, parentArea, gasLevelName, gjMineStatus } = data;
+  const list = JSON.parse(goafDataList);
+  const tbodyInnerHtml = list.reduce((html, { devicePos, alarmLevel, linkStatus }, index) => {
+    html += `
+      <tr>
+        <td>${index + 1}</td>
+        <td>${devicePos}</td>
+        <td style="color: ${get(alarmLevelColor, alarmLevel, alarmLevelColor[0])}">${get(alarmLevelText, alarmLevel, alarmLevelText[0])}</td>
+        <td style="color: ${get(alarmLevelColor, linkStatus, alarmLevelColor[0])}">${get(linkStatusText, linkStatus, linkStatusText[0])}</td>
+      </tr>
+    `;
+    return html;
+  }, '');
+
+  return `
+    <div class="vjmapgis-popup-content__title">${mineName}</div>
+    <div class="vjmapgis-popup-content__divider"></div>
+    <div class="vjmapgis-popup-content__board mb-1">
+      <div class="mr-5px">所属上级</div>
+      <div>${parentArea}</div>
+    </div>
+    <div class="vjmapgis-popup-content__board mb-1">
+      <div class="mr-5px">瓦斯等级</div>
+      <div>${gasLevelName}</div>
+    </div>
+    <div class="vjmapgis-popup-content__board">
+      <div class="mr-5px">生产状态</div>
+      <div>${gjMineStatus}</div>
+    </div>
+    <div class="vjmapgis-popup-content__divider"></div>
+    <table id="${id}" class="vjmapgis-popup-content__table w-full mt-1">
+      <thead>
+        <tr>
+          <th>序号</th>
+          <th>采空区名称</th>
+          <th>风险等级</th>
+          <th>通讯状态</th>
+        </tr>
+      </thead>
+      <tbody>
+        ${tbodyInnerHtml.length ? tbodyInnerHtml : '<tr><td colspan="4">暂无数据</td></tr>'}
+      </tbody>
+    </table>
+  `;
+}
+
+export function generateSimplePopup(data: any): string {
+  const { name } = data;
+
+  return `
+    <div class="vjmapgis-popup-content__strong">${name}</div>
+  `;
+}

+ 1 - 2
src/layouts/default/header/components/user-dropdown/index.vue

@@ -122,8 +122,7 @@
       // 清除缓存
       async function clearCache() {
         const result = await refreshCache();
-        const dragRes = await refreshDragCache();
-        console.log('dragRes', dragRes);
+        await refreshDragCache();
         if (result.success) {
           const res = await queryAllDictItems();
           removeAuthCache(DB_DICT_DATA_KEY);

+ 10 - 5
src/views/dashboard/basicInfo/basicInfo.api.ts

@@ -97,12 +97,17 @@ export const addGoafQuestionReport = (params) =>
     params,
   });
 export const deleteGoafQuestionReport = (params) =>
-  defHttp.delete({
-    url: Api.deleteGoafQuestionReport,
-    params,
-  });
+  defHttp.delete(
+    {
+      url: Api.deleteGoafQuestionReport,
+      params,
+    },
+    {
+      joinParamsToUrl: true,
+    }
+  );
 export const editGoafQuestionReport = (params) =>
-  defHttp.get({
+  defHttp.post({
     url: Api.editGoafQuestionReport,
     params,
   });

+ 2 - 2
src/views/dashboard/basicInfo/dataQuality/index.vue

@@ -416,7 +416,7 @@
   async function handleGoToPage(record: any) {
     try {
       const mineCode = record.mineCode;
-      const targetNode = findNode(mineStore.getDepartTree, (item) => item.id === mineCode, { id: 'id', pid: 'parentId', children: 'childDepart' });
+      const targetNode = findNode(mineStore.getDepartTree, (item) => item.fax === mineCode, { id: 'id', pid: 'parentId', children: 'childDepart' });
 
       let minePath = '';
       if (targetNode) {
@@ -427,7 +427,7 @@
       }
 
       // 跳转页面(可携带拼接后的矿名/路径等参数)
-      router.push({ path: `/sealed/${minePath}`, query: { mineCode } });
+      router.push({ path: `/sealed/${minePath}`, query: { id: targetNode.id } });
     } catch (error) {
       console.error('矿节点定位失败:', error);
       message.error('矿节点定位失败,请稍后重试');

+ 21 - 14
src/views/dashboard/basicInfo/problemReport/components/ProblemReportModal.vue

@@ -26,7 +26,7 @@
         <div class="que-item" v-for="(item, index) in queList" :key="index">
           <div class="que-details">
             <div class="que-field">
-              <span class="que-value que-goafName">{{ item.goafName || '-' }}</span>
+              <span class="que-value que-goafName">{{ item.queTitle || '-' }}</span>
             </div>
             <div class="que-field time-field">
               <span class="que-label">时间:</span>
@@ -48,19 +48,20 @@
 
       <!-- 编辑/新增模式 -->
       <div v-else class="edit-container">
-        <!-- 动态渲染topFormSchema字段(编辑/新增模式) -->
-        <!-- <div class="mine-base-info" v-if="mode === 'add'">
-          <a-form-item v-for="schema in topFormSchema" :key="schema.field" :name="schema.field" :label="schema.label" :rules="schema.rules">
-            <component
-              :is="getComponent(schema.component)"
-              v-model:value="currentRecord[schema.field]"
-              v-bind="schema.componentProps"
-              :placeholder="`请输入${schema.label}`"
-              style="width: 100%"
-              disabled
+        <div class="mine-base-info" v-if="mode === 'add'">
+          <a-form-item name="deptId" label="煤矿名称">
+            <MineCascader
+              v-model:value="innerValue"
+              :sync-to-store="false"
+              :init-from-store="false"
+              :change-on-select="false"
+              @change="changeCascader"
             />
           </a-form-item>
-        </div> -->
+          <a-form-item name="goafId" label="密闭名称">
+            <a-select v-model:value="goafId" :options="goafOptions" placeholder="请选择" />
+          </a-form-item>
+        </div>
 
         <!-- 问题项编辑区 -->
         <div class="que-item" v-for="(item, index) in queList" :key="item.orderNum || index">
@@ -96,13 +97,14 @@
 <script setup lang="ts">
   import { ref, computed, reactive, watch } from 'vue';
   import { BasicModal, useModalInner } from '/@/components/Modal';
-  import { formSchema, topFormSchema } from '../problemReport.data';
+  import { formSchema } from '../problemReport.data';
   import { Select, Input, DatePicker, message } from 'ant-design-vue';
   import { PlusOutlined } from '@ant-design/icons-vue';
   import dayjs, { Dayjs } from 'dayjs';
   import MineCascader from '/@/components/Form/src/jeecg/components/MineCascader/MineCascader.vue';
   import type { FormInstance, RuleObject } from 'ant-design-vue/es/form';
   import { JEditor, JImageUpload } from '/@/components/Form';
+  import { useInitForm } from '/@/views/analysis/warningAnalysis/connectAnalysis/hooks/form';
 
   // 组件映射表
   const componentMap = {
@@ -117,6 +119,7 @@
 
   // 定义事件发射
   const emit = defineEmits(['success']);
+  const { goafOptions, goafId, innerValue, initGoafOptions } = useInitForm();
 
   // 模态框模式:查看/编辑/新增
   const mode = ref<'view' | 'edit' | 'add'>('view');
@@ -142,7 +145,6 @@
 
   // 合并表单规则(从schema中提取)
   const formRules = reactive({
-    mineCode: topFormSchema.find((item) => item.field === 'mineCode')?.rules || [],
     queList: {
       type: 'array',
       required: true,
@@ -372,6 +374,11 @@
       setModalProps({ confirmLoading: false });
     }
   }
+
+  function changeCascader(val) {
+    innerValue.value = val;
+    initGoafOptions(val);
+  }
 </script>
 
 <style scoped>

+ 18 - 6
src/views/dashboard/basicInfo/problemReport/index.vue

@@ -5,7 +5,15 @@
     <TabPane key="unresolved" tab="未解决">
       <BasicTable style="padding: 0" @register="registerUnresolvedTable">
         <template #resetBefore>
-          <a-button type="primary" class="ml-8px" preIcon="mdi:page-next-outline" @click="handleOpenModal({}, 'add')"> 新增问题 </a-button>
+          <a-button
+            v-if="mineStore.getRoot?.isLeaf"
+            type="primary"
+            class="ml-8px"
+            preIcon="mdi:page-next-outline"
+            @click="handleOpenModal({}, 'add')"
+          >
+            新增问题
+          </a-button>
           <a-button type="default" class="ml-8px" preIcon="mdi:download" @click="handleOpenExportModal('unresolved')"> 导出 </a-button>
         </template>
         <template #queJson="{ record }">
@@ -220,7 +228,7 @@
           ...params,
           isOk: false,
           goafId: goafId.value,
-          deptId: innerValue.value,
+          deptId: mineStore.getRootId,
         });
       },
       columns: columns, // 绑定动态列
@@ -258,7 +266,7 @@
           ...params,
           isOk: true,
           goafId: goafId.value,
-          deptId: innerValue.value,
+          deptId: mineStore.getRootId,
         });
       },
       columns: columns,
@@ -451,7 +459,11 @@
   async function handleModalSuccess(result: any) {
     try {
       if (pageMode.value === 'add') {
-        await addGoafQuestionReport(result);
+        await addGoafQuestionReport({
+          ...result,
+          deptId: mineStore.getRootId,
+          mineCode: mineStore.getRoot?.fax,
+        });
       } else if (pageMode.value === 'edit') {
         await editGoafQuestionReport(result);
       }
@@ -468,7 +480,7 @@
   async function handleGoToPage(record: any) {
     try {
       const mineCode = record.mineCode;
-      const targetNode = findNode(mineStore.getDepartTree, (item) => item.id === mineCode, { id: 'id', pid: 'parentId', children: 'childDepart' });
+      const targetNode = findNode(mineStore.getDepartTree, (item) => item.fax === mineCode, { id: 'id', pid: 'parentId', children: 'childDepart' });
 
       let minePath = '';
       if (targetNode) {
@@ -479,7 +491,7 @@
       }
 
       // 跳转页面(可携带拼接后的矿名/路径等参数)
-      router.push({ path: `/sealed/${minePath}`, query: { mineCode } });
+      router.push({ path: `/sealed/${minePath}`, query: { id: targetNode.id } });
     } catch (error) {
       console.error('矿节点定位失败:', error);
       message.error('矿节点定位失败,请稍后重试');

+ 6 - 0
src/views/dashboard/basicInfo/problemReport/problemReport.data.ts

@@ -224,6 +224,9 @@ export const formSchema: FormSchema[] = [
       //最大上传数量
       fileMax: 9,
     },
+    colProps: {
+      span: 6,
+    },
   },
   {
     label: '密闭墙确认照片',
@@ -241,6 +244,9 @@ export const formSchema: FormSchema[] = [
       //最大上传数量
       fileMax: 9,
     },
+    colProps: {
+      span: 6,
+    },
   },
   // {
   //   field: 'param',

+ 41 - 42
src/views/system/cadFile/app.ts

@@ -1,12 +1,13 @@
 import vjmap, { Map, GeoBounds, GeoProjection, Service } from 'vjmap';
 import { env } from './env';
-import { App, MapThreeLayer } from 'vjmap3d';
+// import { App, MapThreeLayer } from 'vjmap3d';
 import 'vjmap/dist/vjmap.min.css';
 import { useAppStore } from '/@/store/modules/app';
 import { message, Modal } from 'ant-design-vue';
 import { updatateGoaf } from './cad.api';
 import { createApp, h } from 'vue';
 import GoafPopup from './components/GoafPopup.vue';
+import { get } from 'lodash-es';
 
 // ===================== 全局状态 =====================
 let map: Map | null = null;
@@ -18,34 +19,35 @@ let currentPopup: any = null;
 
 // ===================== 地图初始化部分 =====================
 // 初始化地图
-const createMapApp = async (containerId = 'map3dContainer') => {
-  const appStore = useAppStore();
-  const containerDiv = document.getElementById(containerId);
-
-  if (!containerDiv) {
+export const initMap2d = async (container: HTMLElement, config: { fileUrl?: string; style?: vjmap.IMapStyleParam } = {}) => {
+  if (!container) {
     message.error('地图容器不存在!');
-    return null;
+    throw new Error('地图容器不存在!');
   }
+  const appStore = useAppStore();
+  // const httpDwgUrl = '/resource/test.dwg';
+  const { fileUrl = 'https://vjmap.com/static/assets/data/gym.dwg', style = env.darkTheme ? vjmap.openMapDarkStyle() : vjmap.openMapLightStyle() } =
+    config;
 
-  containerDiv.style.background = 'transparent';
-  containerDiv.style.width = `${containerDiv?.parentElement?.clientWidth / appStore.widthScale}px`;
-  containerDiv.style.height = `${containerDiv?.parentElement?.clientHeight / appStore.heightScale}px`;
+  container.style.background = 'transparent';
+  container.style.width = `${get(container, 'parentElement.clientWidth', 0) / appStore.widthScale}px`;
+  container.style.height = `${get(container, 'parentElement.clientHeight', 0) / appStore.heightScale}px`;
 
   svc = new vjmap.Service(env.serviceUrl, env.accessToken);
   if (env.workspace) svc.switchWorkspace(env.workspace);
 
-  const style = env.darkTheme ? vjmap.openMapDarkStyle() : vjmap.openMapLightStyle();
   style.clipbounds = Math.pow(2, 6);
-  // const httpDwgUrl = 'https://vjmap.com/static/assets/data/gym.dwg';
-  const httpDwgUrl = './test.dwg';
 
   const res = await svc.openMap({
-    // mapid: env.mapId, // 地图ID,上传文件后获得的mapid
+    mapid: 'mapId', // 地图ID,上传文件后获得的mapid
     version: env.version,
-    fileid: httpDwgUrl,
+    fileid: fileUrl,
     // @ts-ignore
     mapopenway: env.mapopenway || vjmap.MapOpenWay.GeomRender,
-    style,
+    style: {
+      clipbounds: Math.pow(2, 6),
+      ...style,
+    },
   });
 
   if (res.error) {
@@ -59,7 +61,7 @@ const createMapApp = async (containerId = 'map3dContainer') => {
   prj = new GeoProjection(mapExtent);
   // 地图对象
   map = new vjmap.Map({
-    container: containerId, // DIV容器ID
+    container, // DIV容器ID
     style: svc.rasterStyle(), // 样式,这里是栅格样式
     center: prj.toLngLat(mapExtent.center()), // 设置地图中心点
     zoom: 8, // 设置地图缩放级别
@@ -72,33 +74,30 @@ const createMapApp = async (containerId = 'map3dContainer') => {
   map.attach(svc, prj);
   await map.onLoad();
 
-  const mapLayer = new MapThreeLayer(map, {
-    stat: { show: false, left: '0' },
-    scene: {
-      showAxesHelper: false,
-      axesHelperSize: 1,
-      defaultLights: false,
-      gridHelper: {
-        visible: env.gridHelper ?? true,
-        args: [1000, 1000],
-        cellSize: 10,
-        sectionSize: 100,
-      },
-    },
-  });
-  const app: App = mapLayer.app;
-  map.addLayer(new vjmap.ThreeLayer({ context: mapLayer as any }));
-  map.doubleClickZoom.disable();
-
-  return app;
+  return map;
+  // const mapLayer = new MapThreeLayer(map, {
+  //   stat: { show: false, left: '0' },
+  //   scene: {
+  //     showAxesHelper: false,
+  //     axesHelperSize: 1,
+  //     defaultLights: false,
+  //     gridHelper: {
+  //       visible: env.gridHelper ?? true,
+  //       args: [1000, 1000],
+  //       cellSize: 10,
+  //       sectionSize: 100,
+  //     },
+  //   },
+  // });
+  // const app: App = mapLayer.app;
+  // map.addLayer(new vjmap.ThreeLayer({ context: mapLayer as any }));
+  // map.doubleClickZoom.disable();
+
+  // return app;
 };
 
 // 对外导出地图初始化方法
-export const initMap2d = async (id?) => {
-  const app = await createMapApp(id);
-  console.info('地图初始化完成!');
-  return app;
-};
+// export const initMap2d = (id?) => createMapApp(id);
 
 // ===================== 地图操作处理部分 =====================
 // 拾取点位
@@ -313,7 +312,7 @@ export const bindPosition = async (props?: any) => {
 };
 
 // 渲染密闭列表标记
-export const renderGoafMarkers = async (goafList: any[]) => {
+export const renderGoafMarkers = (goafList: any[]) => {
   if (!map || !prj) {
     message.error('地图或投影坐标系未初始化完成!');
     return;

+ 1 - 1
src/views/system/cadFile/components/SideDrawer.vue

@@ -154,7 +154,7 @@
       });
       goafList.value = res || [];
       pagination.value.current = 1; // 搜索后重置页码到第一页
-      await renderGoafMarkers(goafList.value); // 渲染标记
+      renderGoafMarkers(goafList.value); // 渲染标记
     } catch (error) {
       goafList.value = [];
       message.error(`查询失败:${(error as Error).message}`);

+ 49 - 50
src/views/system/cadFile/mapView/index.vue

@@ -1,68 +1,67 @@
 <template>
-  <div id="map3dContainer"></div>
+  <div ref="map3dContainer"></div>
   <!-- 操作按钮面板 -->
   <div class="map-operation-panel">
     <button @click="drawerVisible = true" class="btn">密闭列表</button>
   </div>
 
-    <SideDrawer v-model:visible="drawerVisible" :mineCode="mineCode"/>
+  <SideDrawer v-model:visible="drawerVisible" :mineCode="mineCode" />
 </template>
 
 <script lang="ts" setup>
-import { nextTick, onMounted,ref } from 'vue';
-// 直接导入app.ts中导出的方法
-import { initMap2d } from '../app';
-import { message } from 'ant-design-vue';
-// 导入侧边弹框组件
-import SideDrawer from '../components/SideDrawer.vue';
+  import { nextTick, onMounted, ref } from 'vue';
+  // 直接导入app.ts中导出的方法
+  import { initMap2d } from '../app';
+  // import { message } from 'ant-design-vue';
+  // 导入侧边弹框组件
+  import SideDrawer from '../components/SideDrawer.vue';
 
-// 定义弹框显隐状态
-const drawerVisible = ref(false);
-const mineCode = ref('')
+  // 定义弹框显隐状态
+  const drawerVisible = ref(false);
+  const mineCode = ref('');
+  const map3dContainer = ref<HTMLElement>();
 
-// 初始化地图(确保先加载地图,再调用其他方法)
-onMounted(() => {
-  mineCode.value = history.state?.mineCode;
-  console.log('当前地图编号:', mineCode.value);
-  nextTick(async () => {
-    await initMap2d();
+  // 初始化地图(确保先加载地图,再调用其他方法)
+  onMounted(() => {
+    mineCode.value = history.state?.mineCode;
+    console.log('当前地图编号:', mineCode.value);
+    nextTick(async () => {
+      await initMap2d(map3dContainer.value!);
+    });
   });
-});
-
-
 </script>
 
 <style scoped>
-#map3dContainer {
-  position: absolute;
-  background: transparent;
-  width: 100%;
-  height: 100%;
-  top: 0;
-  left: 0;
-}
+  #map3dContainer {
+    position: absolute;
+    background: transparent;
+    width: 100%;
+    height: 100%;
+    top: 0;
+    left: 0;
+  }
 
-/* 操作面板样式 */
-.map-operation-panel {
-  position: absolute;
-  top: 20px;
-  left: 20px;
-  z-index: 999;
-  display: flex;
-  gap: 8px;
-}
+  /* 操作面板样式 */
+  .map-operation-panel {
+    position: absolute;
+    top: 20px;
+    left: 20px;
+    z-index: 999;
+    display: flex;
+    gap: 8px;
+  }
 
-.btn {
-  padding: 8px 12px;
-  background: #409eff;
-  color: #fff;
-  border: none;
-  border-radius: 4px;
-  cursor: pointer;
-  transition: background 0.2s;
-}
+  .btn {
+    padding: 8px 12px;
+    background: #409eff;
+    color: #fff;
+    border: none;
+    border-radius: 4px;
+    cursor: pointer;
+    transition: background 0.2s;
+  }
 
-.btn:hover {
-  background: #66b1ff;
-}
-</style>
+  .btn:hover {
+    background: #66b1ff;
+  }
+</style>

BIN
src/views/system/cadFile/test.dwg