浏览代码

大风监测、空间分析调整

Hwf 1 月之前
父节点
当前提交
1ec6fbe2a2

+ 6 - 1
src/views/globalMap/RightMenu/DrawTools.vue

@@ -509,9 +509,14 @@ onBeforeUnmount(() => {
       }
     });
   }
-  if (!mapStore.isAMap) {
+  const drawTool = getDrawTool();
+  if (mapStore.isAMap) {
+    drawTool.getMouseTool().off('draw', onDraw);
+  } else {
+    drawTool.getMouseTool().un('drawend', onDraw2);
     map.un('click', onMapClick);
   }
+  drawTool.closeDraw();
 });
 </script>
 

+ 246 - 114
src/views/globalMap/RightMenu/WindMonitor/index.vue

@@ -7,7 +7,7 @@
       </div>
 
       <div style="height: 1200px; overflow: auto">
-        <div style="margin-left: 30px;">
+        <div style="margin-left: 30px">
           <span>风流场</span>
           <img :src="switchStatus ? switchOn : switchOff" style="margin-left: 30px; cursor: pointer;" @click="switchControl" />
         </div>
@@ -91,9 +91,9 @@
             <div class="gradient-text">空间分析</div>
           </div>
           <div class="btn-box">
-            <div class="btn" @click="handleShowMore">
+            <div class="btn" @click="handleToShowDrawTools">
               <i class="more-icon"></i>
-              <div class="btn-text">开启圈选</div>
+              <div class="btn-text">{{ !openSelect ? '开启圈选' : '结束圈选' }}</div>
             </div>
             <div class="btn" @click="exportData">
               <i class="export-icon"></i>
@@ -153,52 +153,52 @@
             </div>
           </div>
         </div>
-        <div class="data-box2">
-          <div class="data-item">
-            <div class="icon1"></div>
-            <div class="item-right">
-              <div class="text-box">
-                <div class="text1">行政镇</div>
-                <div class="text2">(个)</div>
-              </div>
-              <div class="text3">{{ analyzeData.town_num }}</div>
-            </div>
+        <div class="analyze-data-container">
+          <div class="item" @click="handleShowDetail">
+            <div class="item-label">行政镇(个)</div>
+            <div class="item-value">{{ validateNum(analysisData.townCount) }}</div>
           </div>
-          <div class="data-item">
-            <div class="icon2"></div>
-            <div class="item-right">
-              <div class="text-box">
-                <div class="text1">人口</div>
-                <div class="text2">(万)</div>
-              </div>
-              <div class="text4">{{ analyzeData.populationSize }}</div>
-            </div>
+          <div class="item" @click="handleShowDetail">
+            <div class="item-label">行政村(个)</div>
+            <div class="item-value">{{ validateNum(analysisData.villageCount) }}</div>
           </div>
-          <div class="data-item">
-            <div class="icon3"></div>
-            <div class="item-right">
-              <div class="text-box">
-                <div class="text1">面积</div>
-                <div class="text2">(km²)</div>
-              </div>
-              <div class="text5">{{ analyzeData.areaSize }}</div>
-            </div>
+          <div class="item" @click="handleShowDetail">
+            <div class="item-label">面积(km²)</div>
+            <div class="item-value">{{ validateNum(analysisData.areaSize) }}</div>
           </div>
-          <div class="data-item">
-            <div class="icon4"></div>
-            <div class="item-right">
-              <div class="text-box">
-                <div class="text1">GDP</div>
-                <div class="text2">(亿)</div>
-              </div>
-              <div class="text6">{{ analyzeData.GDP }}</div>
-            </div>
+          <div class="item" @click="handleShowDetail">
+            <div class="item-label">常住人口(万)</div>
+            <div class="item-value">{{ validateNum(analysisData.populationSize) }}</div>
+          </div>
+          <div class="item" @click="handleShowDetail">
+            <div class="item-label">GDP(万元)</div>
+            <div class="item-value">{{ validateNum(analysisData.GDP) }}</div>
           </div>
         </div>
       </div>
     </div>
   </div>
   <WindSpeedRank v-if="showMore" v-model="showMore" />
+  <Dialog v-model="showDetail" title="空间分析" hide-footer>
+    <div class="common-table">
+      <div class="table-header">
+        <div class="td">序号</div>
+        <div class="td">行政镇</div>
+        <div class="td">行政村</div>
+        <div class="td">常住人口(万)</div>
+        <div class="td">面积(km2)</div>
+        <div class="td">GDP(亿元)</div>
+      </div>
+      <div v-for="(item, index) in analysisData.townData" :key="index" class="tr">
+        <div class="td">{{ index + 1 }}</div>
+        <div class="td">{{ item.townName }}</div>
+        <div class="td">{{ item.villageName ? item.villageName : '-' }}</div>
+        <div class="td">{{ item.populationSize }}</div>
+        <div class="td">{{ item.areaSize }}</div>
+        <div class="td">{{ item.GDP }}</div>
+      </div>
+    </div>
+  </Dialog>
 </template>
 
 <script setup lang="ts">
@@ -208,7 +208,12 @@ import switchOn from '@/assets/images/map/rightMenu/windMonitor/switch-on.png';
 import WindSpeedRank from '@/views/globalMap/RightMenu/WindMonitor/windSpeedRank.vue';
 import { getRainfallTownNumCount } from '@/api/globalMap/rainMonitor';
 import BigNumber from 'bignumber.js';
-import { debounce } from '@/utils';
+import { debounce, deepClone } from '@/utils';
+import useMapStore from '@/store/modules/map';
+import { nanoid } from 'nanoid';
+import { getSpatialAnalysis } from '@/api/globalMap';
+import { validateNum } from '@/utils/ruoyi';
+import * as turf from '@turf/turf';
 
 const { proxy } = getCurrentInstance() as ComponentInternalInstance;
 const { wind_time } = toRefs<any>(proxy?.useDict('wind_time'));
@@ -347,7 +352,6 @@ watch(endLeft, () => {
   getRainRange();
 });
 const getNewRange = (index, value) => {
-
   if (value >= 0 && value < 193) {
     const data = new BigNumber(value).dividedBy(new BigNumber(49.4)).toFixed(1.6);
     rainRange[index] = data;
@@ -399,10 +403,186 @@ const initData = () => {
   });
 };
 
+const getDrawTool = inject('getDrawTool');
+const getMap = inject('getMap');
+const getMapUtils = inject('getMapUtils');
+const mapStore = useMapStore();
+let map, mapUtils, overlay;
+let showDetail = ref(false);
+const handleShowDetail = () => {
+  showDetail.value = true;
+};
+watch(
+  () => mapStore.mapLoaded,
+  (loaded) => {
+    if (loaded) {
+      map = getMap();
+      mapUtils = getMapUtils();
+    }
+  },
+  {
+    immediate: true
+  }
+);
+let openSelect = ref(false);
+const drawOptions = {
+  color: '#f80102',
+  drawType: '1',
+  graphicsType: 'circle'
+};
+let analysisData = ref({
+  townCount: '',
+  villageCount: '',
+  areaSize: '',
+  populationSize: '',
+  GDP: '',
+  townData: [],
+  list: []
+});
+let locationData = ref([]);
+// 开始圈选
+const handleToShowDrawTools = () => {
+  openSelect.value = !openSelect.value;
+  const drawTool = getDrawTool();
+  if (openSelect.value) {
+    removeOverlays();
+    analysisData.value = {
+      townCount: '',
+      villageCount: '',
+      areaSize: '',
+      populationSize: '',
+      GDP: '',
+      townData: [],
+      list: []
+    };
+    drawTool.drawGraphics2(drawOptions);
+    if (mapStore.isAMap) {
+      drawTool.getMouseTool().on('draw', onDraw);
+    } else {
+      drawTool.getMouseTool().on('drawend', onDraw2);
+    }
+  } else {
+    drawTool.closeDraw();
+  }
+};
+// 高德地图绘制结束
+const onDraw = (event) => {
+  const obj = event.obj;
+  const id = nanoid();
+  obj._opts.extData = {
+    id: id
+  };
+  const data: any = {
+    id: id,
+    type: drawOptions.graphicsType,
+    color: obj._opts.strokeColor,
+    drawType: obj._opts.fillOpacity === 0 ? '1' : '2'
+  };
+  if (data.type === 'circle') {
+    data.center = [obj.getCenter().lng, obj.getCenter().lat];
+    data.radius = obj.getRadius();
+  }
+  const path = obj.getPath();
+  // 将AMap.LngLat对象数组转换为经纬度数组
+  const pathArr = path.map((lngLat) => {
+    // 返回经度和纬度的数组
+    return [lngLat.lng, lngLat.lat];
+  });
+  data.path = pathArr;
+  overlay = obj;
+  analysisSpatial(data);
+};
+// 粤政图绘制结束
+const onDraw2 = (event) => {
+  const feature = event.feature;
+  const geometry = feature.getGeometry();
+  const geometryType = geometry.getType();
+  const id = nanoid();
+  feature.set('id', id);
+  feature.set('dotType', 'analysisSpatial');
+  const data: any = {
+    id: id,
+    type: drawOptions.graphicsType,
+    color: drawOptions.color,
+    drawType: drawOptions.drawType
+  };
+  feature.set('extraData', data);
+  if (geometryType === 'Circle') {
+    data.center = geometry.getCenter();
+    data.radius = geometry.getRadius();
+    const data2 = turf.sector(data.center, data.radius, 0, 360);
+    const pathArr = data2.geometry.coordinates[0];
+    data.path = pathArr;
+  } else if (geometryType === 'Polygon') {
+    const coordinates = geometry.getCoordinates();
+    data.path = coordinates[0];
+  }
+  overlay = feature;
+  analysisSpatial(data);
+};
+// 空间分析数据
+const analysisSpatial = (data) => {
+  openSelect.value = false;
+  let location = [];
+  let itemLocation = [];
+  data.path.forEach((item) => {
+    itemLocation.push({
+      x: item[0],
+      y: item[1]
+    });
+  });
+  itemLocation.push(itemLocation[0]);
+  location.push(itemLocation);
+  locationData.value = location;
+  getSpatialAnalysis(locationData.value).then((res) => {
+    if (res.data && res.data.list) {
+      const list = [];
+      res.data.list.forEach((item) => {
+        if (item.num > 0) {
+          list.push(item);
+        }
+      });
+      res.data.list = list;
+    }
+    if (res.data && res.data.townData) {
+      const data = [];
+      res.data.townData.forEach((item) => {
+        data.push(item);
+        item.children.forEach((item2) => {
+          const obj = deepClone(item2);
+          obj.townName = item.townName;
+          data.push(obj);
+        });
+        delete item.children;
+      });
+      res.data.townData = data;
+    }
+    analysisData.value = res.data;
+  });
+};
+const removeOverlays = () => {
+  if (!overlay) return;
+  if (mapStore.isAMap) {
+    overlay.setMap(null);
+  } else {
+    mapUtils.getDrawVector().getSource().removeFeature(overlay);
+  }
+  overlay = null;
+};
 //调用函数
 onMounted(() => {
   initData();
 });
+onBeforeUnmount(() => {
+  removeOverlays();
+  const drawTool = getDrawTool();
+  if (mapStore.isAMap) {
+    drawTool.getMouseTool().off('draw', onDraw);
+  } else {
+    drawTool.getMouseTool().un('drawend', onDraw2);
+  }
+  drawTool.closeDraw();
+});
 </script>
 
 <style lang="scss" scoped>
@@ -652,82 +832,34 @@ onMounted(() => {
 .rank-other {
   background: url('@/assets/images/map/rightMenu/rainMonitor/other.png') no-repeat;
 }
-.data-box2 {
-  width: 1492px;
-  height: 200px;
-  background: url('@/assets/images/map/rightMenu/rainMonitor/dataBox.png') no-repeat;
-  background-size: 1492px 123px;
-  background-position: bottom;
-  margin-left: 28px;
+.analyze-data-container {
+  width: 100%;
+  font-size: 16px;
+  padding: 15px;
   display: flex;
-  padding: 0 70px;
-  align-items: center;
-  .data-item {
+  flex-wrap: wrap;
+  margin-top: 20px;
+  .item {
+    width: 342px;
+    height: 160px;
+    background: url('@/assets/images/map/rightMenu/ironTower/boxBg.png') no-repeat;
+    background-size: 100% 100%;
+    color: #fff;
     display: flex;
-    flex: 1;
-    .item-right {
-      margin-left: 20px;
-      .text-box {
-        display: flex;
-        margin-bottom: 20px;
-        .text1 {
-          font-size: 32px;
-          color: #ffffff;
-        }
-        .text2 {
-          font-size: 32px;
-          color: #a7ccdf;
-          margin-left: 5px;
-        }
-      }
-      .text3,
-      .text4,
-      .text5,
-      .text6 {
-        font-size: 38px;
-        font-family: BEBAS-1;
-        /* 设置字体透明 */
-        color: transparent;
-        /* 使用 -webkit-background-clip 属性将背景剪裁至文本形状 */
-        -webkit-background-clip: text;
-        /* 非Webkit内核浏览器需要使用标准前缀 */
-        background-clip: text;
-        /* 把当前元素设置为行内块,以便能够应用背景 */
-        display: inline-block;
-      }
-      .text3 {
-        background-image: linear-gradient(to bottom, #ffffff 25%, #e58400 100%);
-      }
-      .text4 {
-        background-image: linear-gradient(to bottom, #ffffff 25%, #6fe695 100%);
-      }
-      .text5 {
-        background-image: linear-gradient(to bottom, #ffffff 25%, #00fde8 100%);
-      }
-      .text6 {
-        background-image: linear-gradient(to bottom, #ffffff 25%, #2b72d6 100%);
-      }
-    }
-    .icon1,
-    .icon2,
-    .icon3,
-    .icon4 {
-      width: 152px;
-      height: 138px;
-    }
-    .icon1 {
-      width: 141px;
-      height: 126px;
-      background: url('@/assets/images/map/rightMenu/rainMonitor/icon1.png') no-repeat;
-    }
-    .icon2 {
-      background: url('@/assets/images/map/rightMenu/rainMonitor/icon2.png') no-repeat;
+    flex-direction: column;
+    justify-content: center;
+    align-items: center;
+    font-size: 38px;
+    margin-right: 42px;
+    cursor: pointer;
+    &:nth-child(4) {
+      margin-right: 0;
     }
-    .icon3 {
-      background: url('@/assets/images/map/rightMenu/rainMonitor/icon3.png') no-repeat;
+    &:nth-child(5) {
+      margin-top: 20px;
     }
-    .icon4 {
-      background: url('@/assets/images/map/rightMenu/rainMonitor/icon4.png') no-repeat;
+    .item-label {
+      margin-bottom: 18px;
     }
   }
 }

+ 4 - 0
src/views/globalMap/index.vue

@@ -491,6 +491,9 @@ const mapMoveEnd = () => {
   if (!mapStore.pointParams.dict_value && !mapStore.pointParams.option) return;
   addMarkersMethod();
 };
+const handleShowDrawTools = (name) => {
+  showDrawTools.value = true;
+};
 watch(
   () => mapStore.mapLoaded,
   (loaded) => {
@@ -593,6 +596,7 @@ provide('showDetail', showDetail);
 provide('getDrawTool', getDrawTool);
 provide('initDataToPlay', initDataToPlay);
 provide('handleShowVideo2', handleShowVideo2);
+provide('handleShowDrawTools', handleShowDrawTools);
 </script>
 
 <style lang="scss" scoped>