فهرست منبع

[Wip 0000] 首页地图从leaflet迁移至vjmap,可配置首页合并

houzekong 2 ماه پیش
والد
کامیت
acf76bdcdf

+ 13 - 13
public/js/global.js

@@ -8,7 +8,7 @@ const __STATIC_ROUTES__ = [
     children: [
       {
         path: '/dashboard/sealedgoaf',
-        component: '/dashboard/SealedGoafMine',
+        component: '/dashboard/SealedGoaf',
         meta: {
           keepAlive: true,
           internalOrExternal: false,
@@ -18,18 +18,18 @@ const __STATIC_ROUTES__ = [
         },
         name: 'dashboard-sealedgoaf',
       },
-      {
-        path: '/dashboard/sealedgoaf',
-        component: '/dashboard/SealedGoafMine',
-        meta: {
-          keepAlive: true,
-          internalOrExternal: false,
-          icon: '',
-          componentName: 'SealedGoafMine',
-          title: '老空区永久密闭首页',
-        },
-        name: 'dashboard-sealedgoaf-mine',
-      },
+      // {
+      //   path: '/dashboard/sealedgoaf',
+      //   component: '/dashboard/SealedGoafMine',
+      //   meta: {
+      //     keepAlive: true,
+      //     internalOrExternal: false,
+      //     icon: '',
+      //     componentName: 'SealedGoafMine',
+      //     title: '老空区永久密闭首页',
+      //   },
+      //   name: 'dashboard-sealedgoaf-mine',
+      // },
 
       // {
       //     path: '/dashboard/analysis',

+ 123 - 42
src/api/sys/map.ts

@@ -1,7 +1,8 @@
-import { useMineDepartmentStore } from '/@/store/modules/mine';
-import { findNode } from '/@/utils/helper/treeHelper';
 import { defHttp } from '/@/utils/http/axios';
-import { omitBy, isNil } from 'lodash-es';
+import { omitBy, isNil, get } from 'lodash-es';
+import { useMineDepartmentStore } from '/@/store/modules/mine';
+import { StatusColorEnum } from '/@/enums/jeecgEnum';
+// import { findNode } from '/@/utils/helper/treeHelper';
 
 enum Api {
   list = '/province/mineData/getMineData',
@@ -12,47 +13,127 @@ enum Api {
  * @param params
  */
 export function getMarkers(params: any): Promise<any> {
-  const mineStore = useMineDepartmentStore();
-
-  return defHttp.post({ url: Api.list, params: omitBy(params, isNil) }, { joinParamsToUrl: true }).then(({ records }) => {
-    return mineStore.getDepartTree.reduce((array: any[], root) => {
-      // return array;
-      const children = records.filter((e) => {
-        return Boolean(
-          findNode([root], (node) => node.rawid === e.parentId, {
-            children: 'childDepart',
-          })
-        );
-      });
-      if (!children.length) return array;
-
-      const { latitude, longitude } = children[0];
-
-      const ele = {
-        id: root.rawid,
-        name: root.departName,
-        radius: 10,
-        mineCode: children.map((c) => c.mineCode).join(','),
-        lat: latitude,
-        lng: longitude,
-        children: children.map((e) => {
-          return {
-            ...e,
-            name: root.departName,
-            rootId: root.rawid,
-            lat: e.latitude,
-            lng: e.longitude,
-          };
-        }),
-      };
-
-      array.push(ele);
-
-      return array;
-    }, []);
-  });
+  return defHttp.post({ url: Api.list, params: omitBy(params, isNil) }, { joinParamsToUrl: true });
 }
 
 export function getGoafAlarmLevel(params: any): Promise<any> {
   return defHttp.post({ url: Api.getGoafAlarmLevel, params: omitBy(params, isNil) }, { joinParamsToUrl: true });
 }
+
+export function getShanxiGeoJSON() {
+  return fetch('/js/shanxi.geo.json').then((r) => r.json());
+}
+
+export function getGeoJSON(params: any) {
+  const getFeatColor = (gb) => {
+    const colorMap = {
+      // 邮编前7位:颜色
+      // '1566101': '#14baff',
+      // '1566102': '#14baff',
+      // '1566103': '#ffff12',
+      // '1566104': '#ffff12',
+      // '1566105': '#ffab12',
+      // '1566106': '#ffab12',
+      // '1566107': '#fe1414',
+      // '1566108': '#fe1414',
+      // '1566109': '#fe1414',
+      // '1566111': '#fe1414',
+      '1566101': StatusColorEnum.blue,
+      '1566102': StatusColorEnum.blue,
+      '1566103': StatusColorEnum.yellow,
+      '1566104': StatusColorEnum.yellow,
+      '1566105': StatusColorEnum.gold,
+      '1566106': StatusColorEnum.gold,
+      '1566107': StatusColorEnum.red,
+      '1566108': StatusColorEnum.red,
+      '1566109': StatusColorEnum.green,
+      '1566110': StatusColorEnum.green,
+    };
+    return get(colorMap, gb.slice(0, -2), StatusColorEnum.blue);
+  };
+  const getAlarmColor = (lv) => {
+    const colorMap = {
+      4: StatusColorEnum.red,
+      3: StatusColorEnum.gold,
+      2: StatusColorEnum.yellow,
+      1: StatusColorEnum.blue,
+      0: StatusColorEnum.blue,
+    };
+
+    return get(colorMap, lv, StatusColorEnum.blue);
+  };
+
+  return Promise.all([getShanxiGeoJSON(), getMarkers(params)]).then(([geojson, { records }]) => {
+    const mineStore = useMineDepartmentStore();
+
+    const map = new Map<string, any>();
+
+    // 根据树信息将煤矿信息分类并整合为一个数组
+    for (const element of records) {
+      // 为初始的数据赋值
+      if (!element.dynamic) {
+        // 子节点计数器
+        element.count = 1;
+        element.isLeaf = true;
+        element.dynamic = false;
+        element.alarmLevel = parseInt(element.alarmLevel) || 0;
+        element.longitude = parseFloat(element.longitude);
+        element.latitude = parseFloat(element.latitude);
+        element.fillColor = getAlarmColor(element.alarmLevel);
+        // 地图缩放等级
+        element.zoom = 1;
+
+        map.set(element.deptId, element);
+      }
+
+      if (map.has(element.parentId)) {
+        const node = map.get(element.parentId);
+        node.count += element.count;
+        node.alarmLevel = Math.max(node.alarmLevel, element.alarmLevel);
+        node.fillColor = getAlarmColor(node.alarmLevel);
+        continue;
+      }
+
+      const ref = mineStore.findDepartById(element.parentId, mineStore.departTree);
+      if (!ref) continue;
+
+      const node = {
+        id: ref.id,
+        deptId: ref.id,
+        parentId: ref.parentId,
+        name: ref.departName,
+        count: 1,
+        isLeaf: false,
+        dynamic: true,
+        alarmLevel: element.alarmLevel,
+        longitude: element.longitude,
+        latitude: element.latitude,
+        fillColor: getAlarmColor(element.alarmLevel),
+        zoom: 5 + parseInt(ref.orgType),
+      };
+      map.set(element.parentId, node);
+      records.push(node);
+    }
+
+    map.clear();
+
+    geojson.features.forEach(({ properties }) => {
+      properties.fillColor = getFeatColor(properties.gb);
+    });
+
+    const markers = {
+      type: 'FeatureCollection',
+      features: records.map((item) => ({
+        type: 'Feature',
+        id: item.deptId,
+        geometry: {
+          type: 'Point',
+          coordinates: [item.longitude, item.latitude],
+        },
+        properties: item,
+      })),
+    };
+
+    return [geojson, records, markers];
+  });
+}

+ 3 - 1
src/design/var/index.less

@@ -39,7 +39,9 @@
 
 @page-footer-z-index: 99;
 
-@layout-basic-z-index: 2;
+@layout-basic-z-index: 3;
+
+@cad-map-z-index: 2;
 
 @simple-map-z-index: 1;
 

+ 256 - 159
src/layouts/default/feature/SimpleMap.vue

@@ -1,185 +1,210 @@
 <template>
   <div ref="mapContainer" class="map-container"></div>
+  <div class="map-container cad-container" id="map2dContainer"></div>
   <div v-if="!isTopLevel" class="map-reset-btn">
-    <Button type="primary" @click="reset">重置</Button>
+    <Button type="primary" @click="revertStack">返回上级</Button>
+  </div>
+  <div
+    id="map-copyright"
+    style="
+      position: absolute;
+      bottom: 10px;
+      left: 500px;
+      z-index: 1000;
+      background: rgba(0, 0, 0, 0.2);
+      color: white;
+      padding: 2px 5px;
+      font-size: 12px;
+      border-radius: 3px;
+    "
+  >
+    © 高德地图
   </div>
 </template>
 
 <script lang="ts" setup>
+  import 'vjmap/dist/vjmap.min.css';
   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';
+  // import { useRoute } from 'vue-router';
+  import vjmap from 'vjmap';
+  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';
 
   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,
+  const mineStore = useMineDepartmentStore();
+  appStore.setSimpleMapParams({ deptId: mineStore.getRootId });
+  // const route = useRoute();
+
+  const mapContainer = ref<HTMLElement>();
+  let map: vjmap.Map;
+  let app: vjmap3d.App;
+  let cadOpened = false;
+
+  const GEO_LAYER_ID = 'geojson-layer';
+  const CIRCLE_LAYER_ID = 'circle-layer';
+  const SYMBOL_LAYER_ID = 'symbol-layer';
+  const DEFAULT_NODE = {
+    id: mineStore.getRootId!,
+    name: '根节点',
+    parentId: null,
+    longitude: 108.367283,
+    latitude: 36.024691,
+    zoom: 6,
+  };
+
+  const historyStack = ref([DEFAULT_NODE]); // 用于存储历史节点的栈,实现返回上一级功能
+  const isTopLevel = computed(() => historyStack.value.length === 1);
+
+  function initMapGeoJSON(map: vjmap.Map, geojson) {
+    map.addSource('geojson-data', {
+      type: 'geojson',
+      data: geojson,
+      generateId: true,
     });
+    map.addFillLayer(GEO_LAYER_ID, 'geojson-data', {
+      fillColor: ['get', 'fillColor'],
+      fillOpacity: ['case', ['to-boolean', ['feature-state', 'hover']], 0.6, 0.2],
+      fillOutlineColor: '#ff9100', // 边线颜色,没错,确实没有边线宽度这个选项,所以有了下面的LineLayer做边线
+      // filter: ['in', last(historyStack.value) as string, ['get', 'children']],
+    });
+    map.addLineLayer('geoline-layer', 'geojson-data', {
+      lineColor: '#ff9100',
+      lineWidth: 2,
+    });
+    // map.hoverPointer([GEO_LAYER_ID]);
+    map.hoverFeatureState(GEO_LAYER_ID);
+  }
 
-    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!);
+  function initMapMarker(map: vjmap.Map, markers) {
+    // 绘制背景圆圈
+    const circles = new vjmap.Circle({
+      layerId: CIRCLE_LAYER_ID,
+      data: markers,
+      // 根据hover不同的状态设置不同的颜色值
+      circleColor: ['case', ['to-boolean', ['feature-state', 'hover']], StatusColorEnum.gray, ['get', 'fillColor']],
+      // 根据hover不同的状态设置不同的宽度值
+      circleStrokeWidth: 1,
+      circleRadius: ['case', ['to-boolean', ['get', 'isLeaf']], 8, 16],
+      // 根据用户所属部门进行初步筛选
+      filter: ['==', last(historyStack.value)!.id, ['get', 'parentId']],
+    });
+    circles.addTo(map);
+    // 设置 hover 鼠标指针样式和状态更新
+    circles.hoverPointer();
+    circles.hoverFeatureState();
+    circles.hoverPopup((f) => {
+      if (f.properties.isLeaf) {
+        return generatePopupContent(f.properties);
+      } else {
+        return generateSimplePopup(f.properties);
       }
     });
-  }
 
-  /** 加载 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);
-          },
-        });
-      },
+    const symbols = new vjmap.Symbol({
+      layerId: SYMBOL_LAYER_ID,
+      data: markers,
+      textField: ['case', ['to-boolean', ['get', 'isLeaf']], '', ['get', 'count']],
+      textFont: ['Arial Unicode MS Regular'],
+      textSize: 16,
+      textAllowOverlap: true,
+      textColor: '#ffffff',
+      filter: ['==', last(historyStack.value)!.id, ['get', 'parentId']],
     });
+    symbols.addTo(map);
 
-    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);
-    //   });
-    // });
+    circles.clickLayer((e) => {
+      if (e.defaultPrevented) return; //  如果事件之前阻止了,则不再执行了
+      historyStack.value.push(get(e, 'features[0].properties', DEFAULT_NODE));
+      markerClickHandler();
+      e.preventDefault(); // 阻止之后的事件执行
+    });
   }
 
-  /** 初始化所有标记点 */
-  async function initMarkers() {
-    const records = await getMarkers({ pageNo: 0, pageSize: 9999 });
+  function markerClickHandler() {
+    const node: any = last(historyStack.value);
+    if (!node) return;
 
-    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 });
+    appStore.setSimpleMapParams({ deptId: node.id });
+
+    if (node.isLeaf) {
+      document.getElementById('map2dContainer')!.style.display = 'block';
+      initMap2d('map2dContainer').then((r) => {
+        app = r!;
+        cadOpened = true;
       });
-      // 将该标记点添加到默认地图上
-      marker.addTo(layerMap.get(DEFAULT_LAYER_ID)!);
+      return;
+    }
 
-      // 创建子标记点
-      if (!item.children.length) 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']]);
 
-      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)!);
-      });
+    map.flyTo({
+      center: [node.longitude, node.latitude],
+      zoom: node.zoom,
     });
   }
 
-  /** 重置视图到默认图层 */
-  function reset() {
-    layerMap.forEach((group, code) => {
-      map?.removeLayer(group);
-      if (code === DEFAULT_LAYER_ID) {
-        map?.addLayer(group);
-        map?.flyToBounds(group.getBounds());
-      }
+  function revertStack() {
+    if (cadOpened) {
+      document.getElementById('map2dContainer')!.style.display = 'none';
+      app.destroy();
+    }
+    if (historyStack.value.length > 1) {
+      historyStack.value.pop();
+      markerClickHandler();
+    }
+  }
+
+  async function initMap(HTMLElement) {
+    let svc = new vjmap.Service(env.serviceUrl, env.accessToken);
+    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 })]);
 
-    appStore.setSimpleMapParams({});
+    // await map.onLoad();
+    // initMapWMS(map);
+    initMapGeoJSON(map, geojson);
+    initMapMarker(map, markers);
   }
+
+  onMounted(() => {
+    if (!mineStore.getRoot?.isLeaf) {
+      initMap(mapContainer.value!);
+    } else {
+      document.getElementById('map2dContainer')!.style.display = 'block';
+      initMap2d('map2dContainer')!.then((r) => (app = r!));
+    }
+  });
+
+  onUnmounted(() => {
+    map?.remove();
+    app?.destroy();
+  });
 </script>
 
 <style lang="less">
@@ -194,24 +219,89 @@
     // filter: grayscale(80%) invert(0%) sepia(10%) hue-rotate(150deg) saturate(200%) brightness(100%) contrast(100%);
   }
 
+  .cad-container {
+    display: none;
+    z-index: @cad-map-z-index;
+    background: @map-bg !important;
+  }
+
+  // .map-container canvas {
   .light-blue-theme {
     filter: invert(0.1) hue-rotate(190deg) saturate(2);
     // filter: sepia(0.3) hue-rotate(180deg) saturate(1.8);
   }
 
-  .leaflet-popup {
+  // .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 {
-      background-color: @map-popup-bg;
-    }
+    // &-content-wrapper {
+    //   background-color: @map-popup-bg;
+    // }
 
     &-tip {
       background-color: @map-popup-bg;
     }
 
-    .leaflet-popup-content {
-      margin: 13px 20px;
-      min-width: 280px;
+    .vjmapgis-popup-content {
+      // margin: 13px 20px;
+      // min-width: 300px;
+      background-color: @map-popup-bg;
       // &__popup {
       // }
 
@@ -226,10 +316,18 @@
         text-align: center;
       }
 
+      &__strong {
+        color: @white;
+        text-align: center;
+        font-weight: bold;
+        font-size: 20px;
+      }
+
       &__board {
         padding: 10px;
         border-radius: 5px;
         background-color: @map-popup-board-bg;
+        color: @text-color-base;
         display: flex;
         justify-content: space-between;
       }
@@ -255,7 +353,6 @@
       }
     }
   }
-
   .map-reset-btn {
     position: absolute;
     right: 30%; /* 距离右边 20px */

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

@@ -0,0 +1,267 @@
+<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>

+ 20 - 10
src/layouts/default/feature/simpleMap.data.ts → src/layouts/default/feature/SimpleMap/simpleMap.data.ts

@@ -45,6 +45,7 @@ export const tileLayerConfigs = [
   {
     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',
@@ -64,8 +65,9 @@ export const alarmLevelColor = [StatusColorEnum.red, StatusColorEnum.blue, Statu
  * 生成标准标记点(子节点)的弹出框 HTML
  */
 export function generatePopupContent(data: any): string {
-  const { goafDataList = [], id, mineName, name, gasLevelName, gjMineStatus } = data;
-  const tbodyInnerHtml = goafDataList.reduce((html, { devicePos, alarmLevel, linkStatus }, index) => {
+  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>
@@ -78,22 +80,22 @@ export function generatePopupContent(data: any): string {
   }, '');
 
   return `
-    <div class="leaflet-popup-content__title">${mineName}</div>
-    <div class="leaflet-popup-content__divider"></div>
-    <div class="leaflet-popup-content__board mb-1">
+    <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>${name}</div>
+      <div>${parentArea}</div>
     </div>
-    <div class="leaflet-popup-content__board mb-1">
+    <div class="vjmapgis-popup-content__board mb-1">
       <div class="mr-5px">瓦斯等级</div>
       <div>${gasLevelName}</div>
     </div>
-    <div class="leaflet-popup-content__board">
+    <div class="vjmapgis-popup-content__board">
       <div class="mr-5px">生产状态</div>
       <div>${gjMineStatus}</div>
     </div>
-    <div class="leaflet-popup-content__divider"></div>
-    <table id="${id}" class="leaflet-popup-content__table w-full mt-1">
+    <div class="vjmapgis-popup-content__divider"></div>
+    <table id="${id}" class="vjmapgis-popup-content__table w-full mt-1">
       <thead>
         <tr>
           <th>序号</th>
@@ -109,6 +111,14 @@ export function generatePopupContent(data: any): string {
   `;
 }
 
+export function generateSimplePopup(data: any): string {
+  const { name } = data;
+
+  return `
+    <div class="vjmapgis-popup-content__strong">${name}</div>
+  `;
+}
+
 /**
  * 生成根标记点的图标配置
  */

+ 8 - 7
src/views/dashboard/SealedGoaf/index.vue

@@ -22,10 +22,12 @@
   import ModulePrimary from '/@/components/Configurable/ModulePrimary.vue';
   import { useGlobSetting } from '/@/hooks/setting';
   import { useAppStore } from '/@/store/modules/app';
+  import { useMineDepartmentStore } from '/@/store/modules/mine';
 
   const { title = '老空区永久密闭监测与分析系统' } = useGlobSetting();
   const { data, updateData } = useInitPage(title);
   const appStore = useAppStore();
+  const mineStore = useMineDepartmentStore();
 
   const cfgs = computed(() => configs.value);
   const { configs, fetchConfigs } = useInitConfigs();
@@ -47,13 +49,12 @@
     },
     goafAccessCount: {
       accessDetails: null,
-      totalNum:  {
+      totalNum: {
         yjTotal: 0, // 应接入总计
         zxTotal: 0, // 在线总计
         lxTotal: 0, // 离线总计
         wjTotal: 0, // 未接入总计
-
-      }
+      },
     },
   }); // 所有数据汇总
 
@@ -100,7 +101,7 @@
       let wjTotal = 0;
 
       if (Array.isArray(accessDetails)) {
-        accessDetails.forEach(item => {
+        accessDetails.forEach((item) => {
           yjTotal += Number(item.yjNum) || 0;
           zxTotal += Number(item.zxNum) || 0;
           lxTotal += Number(item.lxNum) || 0;
@@ -109,10 +110,10 @@
       }
       const goafAccessCount = {
         accessDetails,
-        totalNum: { yjTotal, zxTotal, lxTotal, wjTotal }
+        totalNum: { yjTotal, zxTotal, lxTotal, wjTotal },
       };
       // 3. 把接口数据赋值给响应式变量(备用)
-      mineData.value = { coalSeamFireData, productionStatusData, overLimitData, goafAlarmData, totalNum, goafAccessCount};
+      mineData.value = { coalSeamFireData, productionStatusData, overLimitData, goafAlarmData, totalNum, goafAccessCount };
       // 5. 更新页面数据
       updateData(mineData.value);
     } catch (error) {
@@ -126,7 +127,7 @@
     fetchHomeData(); // 4. 赋值更新后的配置到configs(触发组件重新渲染)
 
     // 1. 先获取基础配置(若有接口获取配置则保留,否则直接用本地testConfigSealedGoaf)
-    fetchConfigs('sealed_goaf').then(() => {
+    fetchConfigs(mineStore.getRoot?.isLeaf ? 'sealed_goaf_mine' : 'sealed_goaf').then(() => {
       if (configs.value.length === 0) {
         configs.value = [...testConfigSealedGoaf]; // 解构触发响应式更新
       }

+ 55 - 26
src/views/dashboard/SealedGoaf/sealedGoaf.api.ts

@@ -11,43 +11,72 @@ enum Api {
 
 // 查询煤层自燃倾向数量统计
 export const getCoalSeamFireNum = (params) =>
-  defHttp.post({
-    url: Api.getCoalSeamFireNum,
-    params,
-  });
+  defHttp.post(
+    {
+      url: Api.getCoalSeamFireNum,
+      params,
+    },
+    {
+      joinParamsToUrl: true,
+    }
+  );
 
 // 查询矿井生成状态统计
 export const getMineProductionStatusNum = (params) =>
-  defHttp.post({
-    url: Api.getMineProductionStatusNum,
-    params,
-  });
+  defHttp.post(
+    {
+      url: Api.getMineProductionStatusNum,
+      params,
+    },
+    {
+      joinParamsToUrl: true,
+    }
+  );
 
 // 查询采空区超限数量统计
 export const getOverLimitNum = (params) =>
-  defHttp.post({
-    url: Api.getOverLimitNum,
-    params,
-  });
+  defHttp.post(
+    {
+      url: Api.getOverLimitNum,
+      params,
+    },
+    {
+      joinParamsToUrl: true,
+    }
+  );
 
 // 执法处风险统计
 export const getGoafAlarmNum = (params) =>
-  defHttp.post({
-    url: Api.getGoafAlarmNum,
-    params,
-  });
+  defHttp.post(
+    {
+      url: Api.getGoafAlarmNum,
+      params,
+    },
+    {
+      joinParamsToUrl: true,
+    }
+  );
 
 // 根据矿编码查询矿下采空区风险等级
 export const getGoafAlarmLevel = (params) =>
-  defHttp.post({
-    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
-    url: Api.getGoafAlarmLevel,
-    params,
-  });
+  defHttp.post(
+    {
+      headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
+      url: Api.getGoafAlarmLevel,
+      params,
+    },
+    {
+      joinParamsToUrl: true,
+    }
+  );
 
-  // 接入统计
-  export const getGoafAccessCount = (params) =>
-    defHttp.get({
+// 接入统计
+export const getGoafAccessCount = (params) =>
+  defHttp.get(
+    {
       url: Api.getGoafAccessCount,
-    });
-  
+    },
+    {
+      joinParamsToUrl: true,
+    }
+  );

+ 83 - 74
src/views/system/cadFile/app.ts

@@ -18,13 +18,12 @@ let currentPopup: any = null;
 
 // ===================== 地图初始化部分 =====================
 // 初始化地图
-const createMapApp = async () => {
+const createMapApp = async (containerId = 'map3dContainer') => {
   const appStore = useAppStore();
-  const containerId = 'map3dContainer';
   const containerDiv = document.getElementById(containerId);
-  
+
   if (!containerDiv) {
-    message.error("地图容器不存在!");
+    message.error('地图容器不存在!');
     return null;
   }
 
@@ -39,7 +38,7 @@ const createMapApp = async () => {
   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
     version: env.version,
@@ -59,7 +58,7 @@ const createMapApp = async () => {
   // 根据地图范围建立几何投影坐标系
   prj = new GeoProjection(mapExtent);
   // 地图对象
-   map = new vjmap.Map({
+  map = new vjmap.Map({
     container: containerId, // DIV容器ID
     style: svc.rasterStyle(), // 样式,这里是栅格样式
     center: prj.toLngLat(mapExtent.center()), // 设置地图中心点
@@ -95,9 +94,9 @@ const createMapApp = async () => {
 };
 
 // 对外导出地图初始化方法
-export const initMap2d = async () => {
-  const app = await createMapApp();
-  console.info("地图初始化完成!");
+export const initMap2d = async (id?) => {
+  const app = await createMapApp(id);
+  console.info('地图初始化完成!');
   return app;
 };
 
@@ -120,16 +119,16 @@ const pickPoint = async (markerOptions: any) => {
     contextMenu: (e: any) => {
       new vjmap.ContextMenu({
         event: e.event.originalEvent,
-        theme: "dark",
-        width: "250px",
+        theme: 'dark',
+        width: '250px',
         items: [
           {
             label: '取消',
-            onClick: () => map?.fire("keyup", { keyCode: 27 })
-          }
-        ]
+            onClick: () => map?.fire('keyup', { keyCode: 27 }),
+          },
+        ],
       });
-    }
+    },
   });
   if (actionPoint.cancel) {
     marker?.remove();
@@ -150,64 +149,67 @@ export const getGoafIsPositioned = (goafItem: any): boolean => {
 // 获取所有标记状态列表
 export const getMarkerStatusList = () => {
   if (!map) return [];
-  return allMarkers.map(marker => ({
+  return allMarkers.map((marker) => ({
     sensorId: marker.data?.sensorId,
     id: marker.data?.id,
     data: marker.data,
     lnglat: marker.getLngLat(),
-    position: map?.fromLngLat(marker.getLngLat())
+    position: map?.fromLngLat(marker.getLngLat()),
   }));
 };
 
 // 移除指定传感器标记
 export const removePosition = async (sensorId: number | string, id: string | number) => {
   if (!map) {
-    message.error("地图未初始化完成!");
+    message.error('地图未初始化完成!');
     return false;
   }
 
-  const targetIndex = allMarkers.findIndex(marker => marker.data?.sensorId === sensorId || marker.data?.id === id);
+  const targetIndex = allMarkers.findIndex((marker) => marker.data?.sensorId === sensorId || marker.data?.id === id);
   if (targetIndex === -1) {
-    message.warning("未找到该传感器标记!");
+    message.warning('未找到该传感器标记!');
     return false;
   }
 
   try {
     await updatateGoaf({
       id: id,
-      xcoordinate: "",
-      ycoordinate: ""
+      xcoordinate: '',
+      ycoordinate: '',
     });
-    console.info("移除布点成功!");
+    console.info('移除布点成功!');
   } catch (apiError) {
     message.error(`同步移除信息失败:${(apiError as Error).message}`);
-    console.error("移除接口调用失败:", apiError);
+    console.error('移除接口调用失败:', apiError);
   }
 
   allMarkers[targetIndex].remove();
   allMarkers.splice(targetIndex, 1);
   syncLocalStorage();
-  
+
   return true;
 };
 
 // 布点方法
 export const bindPosition = async (props?: any) => {
   if (!map || !svc) {
-    message.error("地图未初始化完成,请先加载地图!");
+    message.error('地图未初始化完成,请先加载地图!');
     return null;
   }
 
-  map.fire("keyup", { keyCode: 27 });
+  map.fire('keyup', { keyCode: 27 });
 
-  const el = createMarkerElement({ 
-    alarmLevel: props?.alarmLevel, 
-    devicePos: props?.devicePos 
-  }, false);
-  const markerOptions = { 
+  const el = createMarkerElement(
+    {
+      alarmLevel: props?.alarmLevel,
+      devicePos: props?.devicePos,
+    },
+    false
+  );
+  const markerOptions = {
     element: el,
     anchor: 'bottom',
-    offset: [0, 0]
+    offset: [0, 0],
   };
 
   let marker: any = null;
@@ -224,7 +226,7 @@ export const bindPosition = async (props?: any) => {
         maskClosable: false,
         centered: true,
         onOk: () => resolve(true),
-        onCancel: () => resolve(false)
+        onCancel: () => resolve(false),
       });
     });
 
@@ -235,18 +237,18 @@ export const bindPosition = async (props?: any) => {
 
     const lnglat = marker.getLngLat();
     const id = props?.id;
-    
+
     if (!id) {
       marker.remove();
-      message.error("缺少密闭ID,无法同步布点信息到服务器");
-      throw new Error("缺少密闭ID,无法同步布点信息到服务器");
+      message.error('缺少密闭ID,无法同步布点信息到服务器');
+      throw new Error('缺少密闭ID,无法同步布点信息到服务器');
     }
 
     try {
       await updatateGoaf({
         id: id,
         xcoordinate: lnglat.lng.toString(),
-        ycoordinate: lnglat.lat.toString()
+        ycoordinate: lnglat.lat.toString(),
       });
     } catch (apiError) {
       marker.remove();
@@ -256,40 +258,43 @@ export const bindPosition = async (props?: any) => {
 
     const currentLngLat = marker.getLngLat();
     marker.remove();
-    
-    const finalEl = createMarkerElement({ 
-      alarmLevel: props?.alarmLevel, 
-      devicePos: props?.devicePos 
-    }, true);
-    
-    marker = vjmap.createMarker({ 
+
+    const finalEl = createMarkerElement(
+      {
+        alarmLevel: props?.alarmLevel,
+        devicePos: props?.devicePos,
+      },
+      true
+    );
+
+    marker = vjmap.createMarker({
       element: finalEl,
       anchor: 'bottom',
-      offset: [0, 0]
+      offset: [0, 0],
     });
     marker.setLngLat(currentLngLat);
     marker.addTo(map);
-    marker.data = { 
+    marker.data = {
       sensorId: sensorId++,
       id: id,
-      ...(props || {})
+      ...(props || {}),
     };
   } else {
     if (!Array.isArray(props.position)) {
-      throw new Error("预设坐标position格式错误,必须是[x, y]数组");
+      throw new Error('预设坐标position格式错误,必须是[x, y]数组');
     }
     const lngLat = map.toLngLat(props.position);
     if (!lngLat) {
-      throw new Error("转换坐标失败,无效的position");
+      throw new Error('转换坐标失败,无效的position');
     }
-    
+
     marker = vjmap.createMarker(markerOptions);
     marker.setLngLat(lngLat);
-    marker.data = { 
+    marker.data = {
       ...props.data,
       sensorId: props.data?.sensorId || sensorId++,
       id: props.data?.id || props.id,
-      ...(props || {})
+      ...(props || {}),
     };
     marker.addTo(map);
   }
@@ -303,24 +308,24 @@ export const bindPosition = async (props?: any) => {
     id: marker.data.id,
     lnglat: marker.getLngLat(),
     xcoordinate: marker.getLngLat().lng.toString(),
-    ycoordinate: marker.getLngLat().lat.toString()
+    ycoordinate: marker.getLngLat().lat.toString(),
   };
 };
 
 // 渲染密闭列表标记
 export const renderGoafMarkers = async (goafList: any[]) => {
   if (!map || !prj) {
-    message.error("地图或投影坐标系未初始化完成!");
+    message.error('地图或投影坐标系未初始化完成!');
     return;
   }
 
-  allMarkers.forEach(marker => marker.remove());
+  allMarkers.forEach((marker) => marker.remove());
   allMarkers = [];
   sensorId = 1;
 
-  const validGoafList = goafList.filter(item => getGoafIsPositioned(item));
+  const validGoafList = goafList.filter((item) => getGoafIsPositioned(item));
   if (validGoafList.length === 0) {
-    console.info("当前密闭列表无有效布点坐标,暂无标记可渲染");
+    console.info('当前密闭列表无有效布点坐标,暂无标记可渲染');
     return;
   }
 
@@ -328,17 +333,17 @@ export const renderGoafMarkers = async (goafList: any[]) => {
     try {
       const lnglat = new vjmap.LngLat(Number(goafItem.xcoordinate), Number(goafItem.ycoordinate));
       const el = createMarkerElement(goafItem, true);
-      
-      const marker = vjmap.createMarker({ 
-        element: el, 
+
+      const marker = vjmap.createMarker({
+        element: el,
         anchor: 'bottom',
-        offset: [0, 0]
+        offset: [0, 0],
       });
       marker.setLngLat(lnglat);
       marker.data = {
         sensorId: sensorId++,
         id: goafItem.id,
-        ...goafItem
+        ...goafItem,
       };
       marker.addTo(map);
 
@@ -355,7 +360,7 @@ export const renderGoafMarkers = async (goafList: any[]) => {
  * 校验单个坐标是否有效
  */
 const isValidSingleCoord = (coord: any): boolean => {
-  return coord && coord.trim() !== "" && !isNaN(Number(coord));
+  return coord && coord.trim() !== '' && !isNaN(Number(coord));
 };
 
 /**
@@ -365,9 +370,9 @@ const syncLocalStorage = (): void => {
   if (!map) return;
   const markerData = allMarkers.map((m) => ({
     position: map?.fromLngLat(m.getLngLat()),
-    data: m.data
+    data: m.data,
   }));
-  localStorage.setItem("marker_sensor", JSON.stringify(markerData));
+  localStorage.setItem('marker_sensor', JSON.stringify(markerData));
 };
 
 /**
@@ -386,16 +391,20 @@ const closeAndCleanPopup = (): void => {
 const bindMarkerEvents = (marker: any, id: string | number): void => {
   marker.setDraggable(false);
   let isDragging = false;
-  
-  marker.on("dragstart", () => { isDragging = false; });
-  marker.on("dragend", () => {
-    setTimeout(() => { isDragging = false; }, 150);
+
+  marker.on('dragstart', () => {
+    isDragging = false;
+  });
+  marker.on('dragend', () => {
+    setTimeout(() => {
+      isDragging = false;
+    }, 150);
     const lnglat = marker.getLngLat();
     if (id) {
       updatateGoaf({
         id,
         xcoordinate: lnglat.lng.toString(),
-        ycoordinate: lnglat.lat.toString()
+        ycoordinate: lnglat.lat.toString(),
       }).catch((err: Error) => {
         message.error(`拖拽后同步坐标失败:${err.message}`);
       });
@@ -468,7 +477,7 @@ const createMarkerElement = (goafItem: any, showInfo: boolean = false): HTMLDivE
 // 显示标记信息弹窗
 const showMarkerPopup = (marker: any) => {
   if (!map) return;
-  
+
   closeAndCleanPopup();
 
   const lnglat = marker.getLngLat();
@@ -476,7 +485,7 @@ const showMarkerPopup = (marker: any) => {
 
   const tempContainer = document.createElement('div');
   const popupApp = createApp({
-    render: () => h(GoafPopup, { data })
+    render: () => h(GoafPopup, { data }),
   });
   popupApp.mount(tempContainer);
 
@@ -495,4 +504,4 @@ const showMarkerPopup = (marker: any) => {
     tempContainer.remove();
     currentPopup = null;
   });
-};
+};