Browse Source

feat: 驿站站点数据可视化地图

zhangying 9 months ago
parent
commit
f58e2e7b14

BIN
vue/src/assets/images/redTh.png


+ 31 - 1
vue/src/utils/position.ts

@@ -1,4 +1,3 @@
-// 获取经纬度
 export function getPosition() {
   return new Promise((resolve, reject) => {
     if (navigator.geolocation) {
@@ -22,3 +21,34 @@ export function getPosition() {
     }
   })
 }
+
+/**
+ * 按给定的地图数据绘制城市边境
+ * @param map 地图实例
+ * @param T 地图绘制对象
+ * @param geoJson 城市数据
+ */
+export function setBoundary(map: any, T: any, geoJson: any) {
+  if (map != null) {
+    const points = new Array<any>();
+    geoJson.geometry.coordinates.forEach(subset => {
+      subset.forEach((item: any) => {
+        item.forEach((coord: any) => {
+          const point = new T.LngLat(coord[0], coord[1]);
+
+          points.push(point);
+        })
+      })
+    });
+    const polygon = new T.Polygon(points, {
+      color: "blue",
+      weight: 3,
+      opacity: 1,
+      fillColor: "DodgerBlue",
+      fillOpacity: 0.5
+    });
+
+    // 添加多边形到地图
+    (map as any).addOverLay(polygon);
+  }
+}

+ 16 - 63
vue/src/views/dataMap/companyDataMap.vue

@@ -209,7 +209,8 @@
 <script setup lang="ts">
 import {onMounted, reactive, ref} from "vue";
 import huiZhouGeoJSON from "./geo"
-import {getPosition} from "@/utils/position";
+import {getPosition, setBoundary} from "@/utils/position";
+import redThIcon from "@/assets/images/redTh.png"
 import thIcon from "@/assets/images/th.jpg"
 import {message, type SelectProps} from "ant-design-vue";
 import {getSysDictionaryList} from "@/api/system/dictionary";
@@ -362,41 +363,14 @@ const initMap = () => {
   }
 
   // 设置城市边界
-  setBoundary()
+  setBoundary(map, T, huiZhouGeoJSON);
 
   // 设置登录用户的定位
   setLoginLocation();
 };
 
-// 设置惠州市边界
-function setBoundary() {
-  if (map != null) {
-    const points = new Array<any>();
-    huiZhouGeoJSON.geometry.coordinates.forEach(subset => {
-      subset.forEach((item: any) => {
-        item.forEach((coord: any) => {
-          const point = new T.LngLat(coord[0], coord[1]);
-
-          points.push(point);
-        })
-      })
-    });
-    const polygon = new T.Polygon(points, {
-      color: "deepskyblue",
-      weight: 3,
-      opacity: 0.8,
-      fillColor: "LightCyan",
-      fillOpacity: 0.65
-    });
-
-    // 添加多边形到地图
-    (map as any).addOverLay(polygon);
-  }
-}
-
 // 获取当前登录用户定位
 function setLoginLocation() {
-  let labelText = "定位位置"
   getPosition().then((data: any) => {
     if (data.longitude && data.latitude) {
       companySearchParam.longitude = data.longitude;
@@ -411,22 +385,16 @@ function setLoginLocation() {
     // 设置中心点
     (map as any).centerAndZoom(new T.LngLat(companySearchParam.longitude, companySearchParam.latitude), 12);
     const icon = new T.Icon({
-      iconUrl: thIcon,
-      iconSize: new T.Point(30, 30),
-      iconAnchor: new T.Point(10, 56)
-    })
-    const label = new T.Label({
-      text: labelText, //文本标注的内容
-      position: new T.LngLat(companySearchParam.longitude, companySearchParam.latitude), //文本标注的地理位置
-      offset: new T.Point(-45, 20) //文本标注的位置偏移值
-    })
-    const point = new T.LngLat(companySearchParam.longitude, companySearchParam.latitude, {
-      icon: icon
+      iconUrl: redThIcon,
+      iconSize: new T.Point(50, 50),
+      iconAnchor: new T.Point(25, 50)
     })
+    const point = new T.LngLat(companySearchParam.longitude, companySearchParam.latitude)
     // 创建标注
-    const marker = new T.Marker(point);
+    const marker = new T.Marker(point, {
+      icon: icon
+    });
     //向地图上标记
-    (map as any).addOverLay(label);
     (map as any).addOverLay(marker);
     onSearch();
   })
@@ -440,37 +408,22 @@ function setCompanyMarker() {
       (map as any).removeOverLay(markerList[i]);
     }
   }
-  if (labelList.length > 0) {
-    for (let i = 0; i < labelList.length; i++) {
-      (map as any).removeOverLay(labelList[i]);
-    }
-  }
-
-  // 设置中心点
-  (map as any).centerAndZoom(new T.LngLat(companySearchParam.longitude, companySearchParam.latitude), 12);
 
   if (companyList.value.length > 0) {
     // 设置图标
     const icon = new T.Icon({
-      iconUrl: thIcon,
-      iconSize: new T.Point(20, 20),
-      iconAnchor: new T.Point(10, 56)
+      iconUrl: redThIcon,
+      iconSize: new T.Point(30, 30),
+      iconAnchor: new T.Point(15, 30)
     })
     // 解析企业数据,在地图中标记
     companyList.value.forEach((item: any) => {
       if (item.longitude && item.latitude) {
-        const label = new T.Label({
-          text: item.companyName, //文本标注的内容
-          position: new T.LngLat(item.longitude, item.latitude), //文本标注的地理位置
-          offset: new T.Point(-70, 20) //文本标注的位置偏移值
-        })
-        const point = new T.LngLat(item.longitude, item.latitude, {
+        const point = new T.LngLat(item.longitude, item.latitude)
+        const marker = new T.Marker(point, {
           icon: icon
-        })
-        const marker = new T.Marker(point); // 创建标注
-        (map as any).addOverLay(label); // 将标注添加到地图中
+        }); // 创建标注
         (map as any).addOverLay(marker);// 将标注添加到地图中
-        labelList.push(label);
         markerList.push(marker);
       }
     })

+ 279 - 3
vue/src/views/dataMap/siteDataMap.vue

@@ -1,13 +1,289 @@
 <template>
-  <div>
-
+  <div class="card-search">
+    <div class="map-box">
+      <!-- 地图 -->
+      <div
+        id="mapDiv"
+      ></div>
+      <!-- 驿站查询与展示 -->
+      <div class="site-search-data-box">
+        <!-- 查询表单区域 -->
+        <div class="search-box">
+          <a-input-search
+            v-model:value="siteSearchParam.siteName"
+            placeholder="请输入站点名称"
+            enter-button="查询"
+            :loading="searchLoading"
+            style="width: 100%"
+            @search="onSearch"
+          />
+          <div style="width: 100%">
+            <a-select
+              style="width: 36%;"
+              v-model:value="siteSearchParam.regionCode"
+              :allow-clear="true"
+              :options="regionList"
+              :field-names="{ label: 'name', value: 'code' }"
+              placeholder="所属县区"
+              @change="changeRegion"
+            >
+            </a-select>
+            <a-select
+              style="width: 36%;"
+              v-model:value="siteSearchParam.streetCode"
+              :options="streetList"
+              :field-names="{ label: 'name', value: 'code' }"
+              :allow-clear="true"
+              placeholder="所属街道"
+            >
+            </a-select>
+            <a-button style="width: 28%" @click="searchAll">全部</a-button>
+          </div>
+        </div>
+        <!-- 数据列表 -->
+        <div class="list-box">
+          <div v-if="siteList.length > 0" class="site-data"
+               :class="{'check-site':nowCheckSite.siteID == site.siteID}" v-for="(site,siteIndex) in siteList"
+               :key="siteIndex"
+               @click="checkSite(site)">
+            <p class="site-name">
+              {{ site.siteName }}
+            </p>
+            <p class="label-text">
+              驿站编号{{ site.siteCode }}
+            </p>
+            <p class="label-text">
+              地点:{{ site.detailAddress }}
+            </p>
+            <p class="label-text">
+              站点工作人员:{{ site.siteUsers.length }}
+            </p>
+          </div>
+          <div v-else class="empty-box">
+            <a-empty/>
+          </div>
+        </div>
+        <!-- 分页控件 -->
+        <div class="pagination-box">
+          <a-pagination v-model:current="siteSearchParam.pageIndex" :total="siteTotal"
+                        v-model:pageSize="siteSearchParam.pageSize"
+                        show-less-items @change="onSearch" simple :show-size-changer="false"/>
+        </div>
+      </div>
+    </div>
   </div>
 </template>
 
 <script setup lang="ts">
+import huiZhouGeoJSON from "./geo"
+import {setBoundary} from "@/utils/position";
+import {onMounted, reactive, ref} from "vue";
+import type {SelectProps} from "ant-design-vue";
+import {getRegionCodeList, getStreetCodeList} from "@/api/system/area/index";
+import {getSiteList} from "@/api/baseSettings/siteInfo";
+import thIcon from "@/assets/images/redTh.png"
+
+const T = (window as any).T;
+const zoom = 11;
+let map = null;
+// 地图标记点
+let markerList = new Array<any>([]);
+
+// 驿站查询数据
+const siteSearchParam = reactive({
+  pageIndex: 1,
+  pageSize: 6,
+  siteName: "",
+  regionCode: null,
+  streetCode: null,
+});
+// 驿站数据
+const siteList = ref<Array<any>>([])
+// 查询动画
+const searchLoading = ref(false);
+// 驿站总条数
+const siteTotal = ref(0)
+// 县区数据
+const regionList = ref<SelectProps['options']>();
+// 街道数据
+const streetList = ref<SelectProps['options']>();
+// 选中的站点
+const nowCheckSite = ref({})
+
+// 地图初始化
+const initMap = () => {
+  // 初始化容器
+  map = new T.Map('mapDiv');
+  if (map != null) {
+    // 设置地图显示中心点为惠州市人民政府
+    (map as any).centerAndZoom(new T.LngLat(114.416110, 23.111582), zoom);
+  }
+  // 设置城市边界
+  setBoundary(map, T, huiZhouGeoJSON);
+};
+
+// 查询驿站
+function onSearch() {
+  searchLoading.value = true;
+  getSiteList(siteSearchParam).then((result: any) => {
+    siteList.value = result.list;
+    siteList.value.forEach((site: any) => {
+      if (site.siteUsers) {
+        site.siteUsers = site.siteUsers.split(',');
+      } else {
+        site.siteUsers = []
+      }
+    })
+    siteTotal.value = result.total;
+    // 地图绘制
+    setSiteMarker()
+  }).finally(() => {
+    searchLoading.value = false;
+  })
+}
+
+// 查询全部
+function searchAll() {
+  siteSearchParam.pageIndex = 1
+  siteSearchParam.pageSize = 5
+  siteSearchParam.siteName = ""
+  siteSearchParam.regionCode = null
+  siteSearchParam.streetCode = null
+  onSearch();
+}
+
+// 查询县区
+const getRegionList = async function () {
+  regionList.value = await getRegionCodeList();
+}
+// 县区变更事件-查询街道数据
+const changeRegion = async function () {
+  if (siteSearchParam.regionCode) {
+    streetList.value = await getStreetCodeList(siteSearchParam.regionCode);
+  }
+}
+
+function setSiteMarker() {
+  // 删除已有标点
+  if (markerList.length > 0) {
+    for (let i = 0; i < markerList.length; i++) {
+      (map as any).removeOverLay(markerList[i]);
+    }
+  }
+
+  // 设置中心点
+  (map as any).centerAndZoom(new T.LngLat(114.416110, 23.111582), 10);
+
+  if (siteList.value.length > 0) {
+    // 设置图标
+    const icon = new T.Icon({
+      iconUrl: thIcon,
+      iconSize: new T.Point(30, 30),
+      iconAnchor: new T.Point(15, 30)
+    })
+    // 解析企业数据,在地图中标记
+    siteList.value.forEach((item: any) => {
+      if (item.siteLongitude && item.siteLatitude) {
+        const point = new T.LngLat(item.siteLongitude, item.siteLatitude)
+        const marker = new T.Marker(point, {icon: icon}); // 创建标注
+        (map as any).addOverLay(marker);// 将标注添加到地图中
+        markerList.push(marker);
+      }
+    })
+  }
+}
+
+// 选择站点
+function checkSite(site: any) {
+  nowCheckSite.value = JSON.parse(JSON.stringify(site))
+  if (site.siteLongitude && site.siteLatitude) {
+    // 设置地图中心点
+    (map as any).centerAndZoom(new T.LngLat(site.siteLongitude, site.siteLatitude), 14);
+  }
+}
 
+onMounted(() => {
+  initMap();
+  getRegionList();
+  onSearch();
+})
 </script>
 
-<style scoped>
+<script lang="ts">
+// 设置页面名称进行组件缓存
+export default {
+  name: "SiteDataMap"
+}
+</script>
+
+<style lang="less" scoped>
+.map-box {
+  width: 100%;
+  height: calc(100vh - 235px);
+  position: relative;
+  border: 1px rgba(173, 173, 173, 0.8) solid;
+  box-sizing: border-box;
+
+  #mapDiv {
+    position: absolute;
+    width: 100%;
+    height: 100%;
+    z-index: 100;
+  }
+
+  .site-search-data-box {
+    width: 300px;
+    height: 100%;
+    position: absolute;
+    top: 0;
+    bottom: 0;
+    left: 0;
+    right: 330px;
+    background-color: white;
+    border-right: 1px solid rgba(173, 173, 173, 0.8);
+    z-index: 110;
+
+    .list-box {
+      max-height: calc(100% - 110px);
+      margin-bottom: 10px;
+      overflow: hidden;
+      overflow-y: auto;
+
+      .site-data {
+        padding: 8px;
+        border-bottom: 1px solid rgba(173, 173, 173, 0.8);
+        cursor: pointer;
+
+        .site-name {
+          font-size: 14px;
+          font-weight: 700;
+        }
+
+        .label-text {
+          font-size: 12px;
+          color: #737373;
+          margin-bottom: 3px;
+        }
+      }
+    }
+  }
+
+  .pagination-box {
+    display: flex;
+    justify-content: center;
+  }
+
+  .empty-box {
+    width: 100%;
+    height: 450px;
+    display: flex;
+    justify-content: center;
+    align-items: center;
+    border-right: 1px solid rgba(173, 173, 173, 0.8);
+  }
 
+  .check-site {
+    background-color: #dadada;
+  }
+}
 </style>