zhaobao 2 жил өмнө
parent
commit
cfbe38f1b1
46 өөрчлөгдсөн 1375 нэмэгдсэн , 86 устгасан
  1. 4 0
      package.json
  2. 6 0
      src/App.vue
  3. 72 0
      src/api/goaf/check.js
  4. 49 0
      src/api/goaf/info.js
  5. 48 0
      src/api/goaf/layer.js
  6. 40 0
      src/api/goaf/sensor.js
  7. 36 0
      src/api/goaf/sensorCatApi.js
  8. 44 0
      src/api/system/index.js
  9. 78 0
      src/api/system/mapApi.js
  10. BIN
      src/assets/HBuilderX.dmp
  11. BIN
      src/assets/icon/1.png
  12. BIN
      src/assets/icon/1hover.png
  13. BIN
      src/assets/icon/2.png
  14. BIN
      src/assets/icon/2hover.png
  15. BIN
      src/assets/icon/3.png
  16. BIN
      src/assets/icon/3hover.png
  17. BIN
      src/assets/icon/4.png
  18. BIN
      src/assets/icon/4hover.png
  19. BIN
      src/assets/icon/5.png
  20. BIN
      src/assets/icon/5hover.png
  21. BIN
      src/assets/icon/6.png
  22. BIN
      src/assets/icon/6hover.png
  23. BIN
      src/assets/icon/7.png
  24. BIN
      src/assets/icon/7hover.png
  25. BIN
      src/assets/icon/8.png
  26. BIN
      src/assets/icon/8hover.png
  27. BIN
      src/assets/icon/camera.png
  28. BIN
      src/assets/icon/cgq.png
  29. BIN
      src/assets/icon/icon_chuanganqi.png
  30. BIN
      src/assets/icon/icon_shexiangtou.png
  31. 1 1
      src/components/Header.vue
  32. 82 0
      src/components/MapSelector/index.vue
  33. 25 0
      src/config/index.js
  34. 2 0
      src/main.js
  35. 52 0
      src/utils/auth.js
  36. 34 1
      src/utils/index.js
  37. 76 0
      src/utils/request.js
  38. 310 0
      src/views/home/components/Map/index.vue
  39. BIN
      src/views/home/components/Map/tip.png
  40. 1 1
      src/views/home/components/SensorInfoStatistics.vue
  41. 0 1
      src/views/home/components/SensorStatus.vue
  42. 3 1
      src/views/home/components/index.js
  43. 28 12
      src/views/home/index.vue
  44. 72 36
      src/views/particulars/goaf_info.vue
  45. 57 30
      src/views/particulars/sensor.vue
  46. 255 3
      yarn.lock

+ 4 - 0
package.json

@@ -9,11 +9,15 @@
   },
   "dependencies": {
     "@element-plus/icons-vue": "^2.1.0",
+    "@geoman-io/leaflet-geoman-free": "^2.11.3",
+    "axios": "^1.4.0",
     "babel-eslint": "^10.1.0",
     "core-js": "^3.8.3",
     "echarts": "^5.4.2",
     "element-plus": "^2.3.3",
     "eslint-plugin-import": "^2.27.5",
+    "js-cookie": "^3.0.5",
+    "leaflet": "^1.7.1",
     "pinia": "^2.0.34",
     "sass": "^1.61.0",
     "sass-loader": "^13.2.2",

+ 6 - 0
src/App.vue

@@ -6,12 +6,18 @@
 </template>
 
 <script>
+import {getQueryObject} from '@/utils'
+import { setToken ,getToken} from '@/utils/auth'
 import appHeader from '@/components/Header.vue'
 export default {
   components:{
     appHeader
   }
 }
+if(!getToken()){
+  setToken(getQueryObject().token)
+}
+
 </script>
 <style lang="scss" scoped>
 #app-main{

+ 72 - 0
src/api/goaf/check.js

@@ -0,0 +1,72 @@
+
+import request from '@/utils/request'
+
+// 获取所有检查表及其内容
+export function getChecklist(params) {
+  return request({
+    url: '/goaf/goafChecklist/list',
+    params
+  })
+}
+// 分页获取所有检查表及其内容
+export function getChecklistByPage(params) {
+  return request({
+    url: '/goaf/goafChecklist/page',
+    params
+  })
+}
+
+// 获取指定的检查表及其内容
+export function getChecklistById(goafchecklistId) {
+  return request({
+    url: `/goaf/goafChecklist/${goafchecklistId}`
+  })
+}
+// 增加检查表及其内容
+export function createChecklist(data) {
+  return request({
+    url: '/goaf/goafChecklist/add',
+    method: 'POST',
+    data
+  })
+}
+// 更新
+export function updateChecklist(data) {
+  return request({
+    url: '/goaf/goafChecklist/update',
+    method: 'PUT',
+    data
+  })
+}
+
+// 删除指定检查表
+export function deleteChecklistById(goafchecklistId) {
+  return request({
+    url: `/goaf/goafChecklist/deletechecklist/${goafchecklistId}`,
+    method: 'DELETE'
+  })
+}
+// .删除指定检查表的某项内容
+export function deleteChecklistItemById(goafchecklistitemid) {
+  return request({
+    url: `/goaf/goafChecklist/deletecheckitemlist/${goafchecklistitemid}`,
+    method: 'DELETE'
+  })
+}
+
+// 增加检查表及其内容
+export function createChecklistItem(data) {
+  return request({
+    url: '/goaf/goafChecklist/add',
+    method: 'POST',
+    data
+  })
+}
+// 更新
+export function updateChecklistItem(data) {
+  return request({
+    url: '/goaf/goafChecklist/update',
+    method: 'PUT',
+    data
+  })
+}

+ 49 - 0
src/api/goaf/info.js

@@ -0,0 +1,49 @@
+
+import request from '@/utils/request'
+
+// 获取所有采空区信息
+export function getGoafBaseInfo(params) {
+  return request({
+    url: '/goaf/goafbaseinfo/list',
+    params
+  })
+}
+// 获取所有采空区信息分页
+export function getGoafBaseInfoByPage(params) {
+  return request({
+    url: '/goaf/goafbaseinfo/page',
+    params
+  })
+}
+
+// 根据采空区id获取信息
+export function getGoafInfoById(id) {
+  return request({
+    url: `/goaf/goafbaseinfo/${id}`
+  })
+}
+// 新增
+export function createGoaf(data) {
+  return request({
+    url: '/goaf/goafbaseinfo/add',
+    method: 'POST',
+    data
+  })
+}
+// 更新
+export function updateGoaf(data) {
+  return request({
+    url: '/goaf/goafbaseinfo/update',
+    method: 'PUT',
+    data
+  })
+}
+
+// 删除
+export function delGoaf(goafId) {
+  return request({
+    url: `/goaf/goafbaseinfo/delete/${goafId}`,
+    method: 'DELETE'
+  })
+}
+

+ 48 - 0
src/api/goaf/layer.js

@@ -0,0 +1,48 @@
+
+import request from '@/utils/request'
+
+// 获取指定地图上的所有采空区坐标信息
+export function getMapLayer(params) {
+  return request({
+    url: '/goaf/goafmapflayer/list',
+    params
+  })
+}
+// 分页获取指定地图上的所有采空区坐标信息
+export function getMapLayerByPage(params) {
+  return request({
+    url: '/goaf/goafmapflayer/page',
+    params
+  })
+}
+// 根据采空区id获取坐标信息
+export function getMapLayerById(goaflayerId) {
+  return request({
+    url: `/goaf/goafmapflayer/${goaflayerId}`
+  })
+}
+// 增加
+export function createMapLayer(data) {
+  return request({
+    url: '/goaf/goafmapflayer/add',
+    method: 'POST',
+    data
+  })
+}
+
+// 删除
+export function delMapLayer(goaflayerId, mapId, layerId) {
+  return request({
+    url: `/goaf/goafmapflayer/delete/${goaflayerId}/${mapId}/${layerId}`
+  })
+}
+
+// 修改
+export function updateMapLayer(data) {
+  return request({
+    url: '/goaf/goafmapflayer/update',
+    method: 'PUT',
+    data
+  })
+}
+

+ 40 - 0
src/api/goaf/sensor.js

@@ -0,0 +1,40 @@
+
+import request from '@/utils/request'
+// 获取所有传感器类型信息
+export function getSensor(params) {
+  return request({
+    url: '/goaf/sensortypecfg/list',
+    params
+  })
+}
+// 根据类型获取传感器类型信息
+export function getSensorByType(typeId) {
+  return request({
+    url: `/goaf/sensortypecfg/${typeId}`
+  })
+}
+// 增加
+export function createSensor(data) {
+  return request({
+    url: '/goaf/sensortypecfg/add',
+    method: 'POST',
+    data
+  })
+}
+
+// 删除
+export function delSensor(typeId) {
+  return request({
+    url: `/goaf/sensortypecfg/delete/${typeId}`
+  })
+}
+
+// 修改
+export function updateSensor(data) {
+  return request({
+    url: '/goaf/sensortypecfg/update',
+    method: 'PUT',
+    data
+  })
+}
+

+ 36 - 0
src/api/goaf/sensorCatApi.js

@@ -0,0 +1,36 @@
+
+import request from '@/utils/request'
+
+// 获取所有传感器类型
+export function getSensorCat(params) {
+  return request({
+    url: '/goaf/sensortypecfg/list',
+    params
+  })
+}
+
+// 增加传感器类型
+export function createSensorCat(data) {
+  return request({
+    url: '/goaf/sensortypecfg/add',
+    method: 'POST',
+    data
+  })
+}
+// 更新
+export function updateSensorCat(data) {
+  return request({
+    url: 'goaf/goafbaseinfo/update',
+    method: 'PUT',
+    data
+  })
+}
+
+// 删除指定检查表的某项内容
+export function deleteSensorCatById(goafId) {
+  return request({
+    url: `/goaf/goafbaseinfo/delete/${goafId}`,
+    method: 'DELETE'
+  })
+}
+

+ 44 - 0
src/api/system/index.js

@@ -0,0 +1,44 @@
+import request from '@/utils/request'
+
+// 登入
+export function login(data) {
+  return request({
+    url: '/login',
+    method: 'POST',
+    data
+  })
+}
+
+// 获取授权
+export function getPermit() {
+  return request({
+    url: '/user/permit',
+    method: 'GET'
+  })
+}
+
+// 获取用户基本信息
+export function getUserProfile() {
+  return request({
+    url: '/user/profile',
+    method: 'GET'
+  })
+}
+
+// 更新用户基本信息
+export function updateUserProfile(data) {
+  return request({
+    url: '/user/profile/update',
+    method: 'PUT',
+    data
+  })
+}
+
+// 退出登陆
+export function logout() {
+  return request({
+    url: '/logout',
+    method: 'GET'
+  })
+}
+

+ 78 - 0
src/api/system/mapApi.js

@@ -0,0 +1,78 @@
+import request from '@/utils/request'
+
+/**
+ * 列表查询
+ * @param data
+ * @returns
+ */
+export function getMapByList(data) {
+  return request({
+    url: '/ent/map',
+    method: 'GET',
+    params: data
+  })
+}
+
+/**
+ * 分页查询
+ * @param data
+ * @returns
+ */
+export function getMapByPage(data) {
+  return request({
+    url: '/ent/map/page',
+    method: 'GET',
+    params: data
+  })
+}
+
+/**
+ * 基于ID查询
+ * @param mapId
+ * @returns
+ */
+export function getMapById(mapId) {
+  return request({
+    url: `/ent/map/${mapId}`,
+    method: 'GET'
+  })
+}
+
+/**
+ * 新增
+ * @param data
+ * @returns
+ */
+export function createMap(data) {
+  return request({
+    url: `/ent/map/add`,
+    method: 'POST',
+    data
+  })
+}
+
+/**
+ * 更新
+ * @param data
+ * @returns
+ */
+export function updateMap(data) {
+  return request({
+    url: `/ent/map/update`,
+    method: 'PUT',
+    data
+  })
+}
+
+/**
+ * 删除
+ * @param mapId
+ * @returns
+ */
+export function deleteMapById(mapId) {
+  return request({
+    url: `/ent/map/${mapId}`,
+    method: 'DELETE'
+  })
+}
+

BIN
src/assets/HBuilderX.dmp


BIN
src/assets/icon/1.png


BIN
src/assets/icon/1hover.png


BIN
src/assets/icon/2.png


BIN
src/assets/icon/2hover.png


BIN
src/assets/icon/3.png


BIN
src/assets/icon/3hover.png


BIN
src/assets/icon/4.png


BIN
src/assets/icon/4hover.png


BIN
src/assets/icon/5.png


BIN
src/assets/icon/5hover.png


BIN
src/assets/icon/6.png


BIN
src/assets/icon/6hover.png


BIN
src/assets/icon/7.png


BIN
src/assets/icon/7hover.png


BIN
src/assets/icon/8.png


BIN
src/assets/icon/8hover.png


BIN
src/assets/icon/camera.png


BIN
src/assets/icon/cgq.png


BIN
src/assets/icon/icon_chuanganqi.png


BIN
src/assets/icon/icon_shexiangtou.png


+ 1 - 1
src/components/Header.vue

@@ -2,7 +2,7 @@
     <div id="app-header">
         <span class="title">采空区信息化管理系统</span>
         <div class="time">{{ time }}</div>
-        <div class="user"><el-icon><User /></el-icon><span class="welcome">欢迎您,{{ user?.name||'***' }}</span></div>
+        <div class="user"><el-icon><User /></el-icon><span class="welcome">欢迎您,{{ user?.accountName||'***' }}</span></div>
     </div>
 </template>
 <script>

+ 82 - 0
src/components/MapSelector/index.vue

@@ -0,0 +1,82 @@
+<template>
+  <el-select
+    v-model="mapId"
+    value-key="mapId"
+    filterable
+    placeholder="请选择地图"
+    :popper-append-to-body="false"
+    @change="handleSelectMap"
+  >
+      <el-option
+        v-for="item in mapList"
+        :key="item.mapId"
+        :label="item.mapTitle"
+        :value="item.mapId"
+      />
+  </el-select>
+  
+</template>
+<script>
+import { getMapByList } from '@/api/system/mapApi'
+export default {
+  name: 'MapSelector',
+  components: { },
+  props: {
+    defaultVal: {
+      type: Number,
+      default: undefined
+    }
+  },
+  data() {
+    return {
+      mapId: undefined,
+      mapList: []
+    }
+  },
+  watch: {
+    defaultVal(val) {
+      this.mapId = val
+    }
+  },
+  created() {
+    this.initMapData()
+  },
+  methods: {
+    // 检查表清单
+    initMapData() {
+      getMapByList({}).then((resp) => {
+        const { code, data, msg } = resp
+        if (code === 0) {
+          this.mapList = data
+          if (data && data.length > 0) {
+            const node = data[0]
+            this.mapId = node.mapId
+            this.setVal(node)
+          }
+        } else {
+          this.$message.error(msg)
+        }
+      }).catch((error) => {
+        console.log(error)
+      })
+    },
+
+    // 选择地图
+    handleSelectMap(val) {
+      const size = this.mapList.length
+      for (let idx = 0; idx < size; idx++) {
+        const mp = this.mapList[idx]
+        if (mp.mapId === val) {
+          this.setVal(mp)
+          return
+        }
+      }
+    },
+
+    // 返回值
+    setVal(obj) {
+      this.$emit('setMapInfo', obj)
+    }
+  }
+}
+</script>

+ 25 - 0
src/config/index.js

@@ -0,0 +1,25 @@
+module.exports = {
+    title: '采空区信息化管理系统',
+    /**
+    * 服务API地址
+    */
+    serverUrl: 'http://192.168.1.214:8080/', // 正式环境
+    // devServerUrl: 'http://1.15.92.205:8080/', // 开发环境
+    devServerUrl: 'http://192.168.3.5:8080/', // 开发环境 qu
+    // devServerUrl: 'http://192.168.3.201:8080/', // 开发环境  test
+    mqttUrl: 'ws://1.15.92.205:8083/mqtt', // 水泥厂mqtt地址
+    /**
+    * websocket 地址
+    * dev 开发环境
+    * pro正式环境
+    */
+    appID: '10001',
+    appKey: '82a8bf439373e305',
+    appSecret: '82a8bf439373e305',
+    permitTplId: 7,
+    wsServerUrl: {
+      dev: 'ws://ws.58yunkang.com/websocket/link',
+      pro: 'ws://ws.58yunkang.com/websocket/link'
+    }
+  }
+  

+ 2 - 0
src/main.js

@@ -6,6 +6,8 @@ import ElementPlus from 'element-plus'
 import * as ElementPlusIconsVue from '@element-plus/icons-vue'
 import pinia from '@/store'
 import 'element-plus/dist/index.css'
+import 'leaflet/dist/leaflet.css'
+import '@geoman-io/leaflet-geoman-free/dist/leaflet-geoman.css'
 import './style.css'
 
 

+ 52 - 0
src/utils/auth.js

@@ -0,0 +1,52 @@
+import Cookies from 'js-cookie'
+
+// Token
+const TokenKey = 'User-Token'
+
+export function getToken() {
+  return Cookies.get(TokenKey)
+}
+
+export function setToken(token) {
+  return Cookies.set(TokenKey, token)
+}
+
+export function removeToken() {
+  return Cookies.remove(TokenKey)
+}
+
+// 用户基本信息
+const UserData = 'User-Data'
+
+export function getUserData() {
+  const userDataStr = Cookies.get(UserData)
+  if (userDataStr) {
+    return JSON.parse(userDataStr)
+  }
+}
+
+export function setUserData(userData) {
+  return Cookies.set(UserData, JSON.stringify(userData))
+}
+
+export function removeUserData() {
+  return Cookies.remove(UserData)
+}
+
+// 用户权限
+const UserPermit = 'User-Permit'
+
+export function getUserPermit() {
+  const userPermitStr = Cookies.get(UserPermit)
+  if (userPermitStr) {
+    return JSON.parse(userPermitStr)
+  }
+}
+
+export function setUserPermit(userPermit) {
+  return Cookies.set(UserPermit, JSON.stringify(userPermit))
+}
+
+export function removeUserPermit() {
+  return Cookies.remove(UserPermit)
+}

+ 34 - 1
src/utils/index.js

@@ -39,4 +39,37 @@ export function parseTime(time, cFormat) {
       return value.toString().padStart(2, '0')
     })
     return time_str
-  }
+  }
+  /**
+ * @param {string} url
+ * @returns {Object}
+ */
+export function getQueryObject(url) {
+  url = url == null ? window.location.href : url
+  const search = url.substring(url.lastIndexOf('?') + 1)
+  const obj = {}
+  const reg = /([^?&=]+)=([^?&=]*)/g
+  search.replace(reg, (rs, $1, $2) => {
+    const name = decodeURIComponent($1)
+    let val = decodeURIComponent($2)
+    val = String(val)
+    obj[name] = val
+    return rs
+  })
+  return obj
+}
+// 只能转换 4000 以下的正整数阿拉伯数字
+export function NumConvertLM(num) {
+  var Roman = [['', 'I', 'Ⅱ', 'Ⅲ', 'Ⅳ', 'Ⅴ', 'Ⅵ', 'Ⅶ', 'Ⅷ', 'Ⅸ'],
+    ['', 'X', 'XX', 'XXX', 'XL', 'L', 'LX', 'LXX', 'LXXX', 'XC'],
+    ['', 'C', 'CC', 'CCC', 'CD', 'D', 'DC', 'DCC', 'DCCC', 'CM'],
+    ['', 'M', 'MM', 'MMM', '  ', ' ', '  ', '   ', '    ', '  ']]
+
+  if (isNaN(num)) return num
+  var ReverseArr = num.toString().split('').reverse()
+  var CorrectArr = []
+  for (var i = 0; i < ReverseArr.length; i++) {
+    CorrectArr.unshift(Roman[i][ReverseArr[i]])
+  }
+  return CorrectArr.join('')
+}

+ 76 - 0
src/utils/request.js

@@ -0,0 +1,76 @@
+import axios from 'axios'
+import { ElMessageBox, ElMessage, ElLoading } from 'element-plus'
+import store from '@/store'
+import { getToken } from '@/utils/auth'
+import { appID, appKey, appSecret, serverUrl, devServerUrl } from '@/config'
+const baseURL = process.env.NODE_ENV === 'development' ? devServerUrl : serverUrl
+// create an axios instance
+const service = axios.create({
+  baseURL: baseURL, // url = base url + request url
+  // withCredentials: true, // send cookies when cross-domain requests
+  timeout: 1000 * 60 * 3 // request timeout
+})
+
+let loadingInstance = null
+
+// request interceptor
+service.interceptors.request.use(
+  config => {
+    // do something before request is sent
+    config.headers['AppID'] = appID
+    config.headers['AppKey'] = appKey
+    config.headers['AppSecret'] = appSecret
+    loadingInstance = ElLoading.service({ fullscreen: true })
+    if (getToken()) {
+      config.headers['Authorization'] = 'Bearer ' + getToken()
+    }
+    return config
+  },
+  error => {
+    // do something with request error
+    return Promise.reject(error)
+  }
+)
+
+// response interceptor
+service.interceptors.response.use(
+  response => {
+    loadingInstance.close()
+    const res = response.data
+    if (res.code !== 0) {
+      // 50008: Illegal token; 50012: Other clients logged in; 50014: Token expired;
+      if (res.code === 401) {
+        // to re-login
+        ElMessageBox.confirm('登录失效,请重新登录!', '温馨提示!', {
+          confirmButtonText: '确认',
+          cancelButtonText: '取消',
+          type: 'warning'
+        }).then(() => {
+          store.dispatch('user/resetToken').then(() => {
+            location.reload()
+          })
+        })
+      } else {
+        ElMessage({
+          message: res.msg || 'Error',
+          type: 'error',
+          duration: 3 * 1000
+        })
+      }
+      return Promise.reject(new Error(res.message || 'Error'))
+    } else {
+      return res
+    }
+  },
+  error => {
+    loadingInstance.close()
+    ElMessage({
+      message: error.message || 'Error',
+      type: 'error',
+      duration: 3 * 1000
+    })
+    return Promise.reject(error)
+  }
+)
+
+export default service

+ 310 - 0
src/views/home/components/Map/index.vue

@@ -0,0 +1,310 @@
+<template>
+    <div class="map-container">
+      <nav>
+        <map-selector :default-val="formData.mapId" @setMapInfo="handleSelectMap" />
+      </nav>
+      <div class="layout-map">
+        <div id="vmap" />
+        <div id="marker-tip" :style="marker_tip_style">
+          <div class="head">基本信息</div>
+          <div class="item">
+            <p class="title">巡检任务统计(20)</p>
+            <p><span>未完成:8</span><span>已完成:8</span></p>          
+          </div>
+          <div class="item">
+            <p class="title">传感器统计(21)</p>
+            <p><span>在线:8</span><span>离线:8</span></p>
+            <p><span class="warn">预警:8</span></p>          
+          </div>
+        </div>
+      </div>
+    </div>
+</template>
+  
+  <script>
+  import * as L from 'leaflet'
+  import '@geoman-io/leaflet-geoman-free'
+  import MapSelector from '@/components/MapSelector'
+  import { getMapLayerById, createMapLayer, delMapLayer, updateMapLayer, getMapLayer } from '@/api/goaf/layer'
+  const markerIcon = L.icon({
+    iconUrl: require('@/assets/icon/4.png'),
+    shadowUrl: require('@/assets/icon/4hover.png'),
+    iconSize: [60, 70],
+    shadowSize: [0, 0],
+    iconAnchor: [16, 52],
+    popupAnchor: [1, -38]
+  })
+  L.Marker.prototype.options.icon = markerIcon
+  export default {
+    name: 'GoafVisualEditor',
+    components: {
+      MapSelector
+    },
+    data() {
+      return {
+        viewData: {},
+        formData: {
+          mapId: undefined,
+          layerId: undefined,
+          layerVector: ''
+        },
+        map: undefined,
+        geoLayer: undefined,
+        conditions: {
+          layerId: undefined
+        },
+        layer: {
+          containerPoint:{
+            x:"",
+            y:""
+          }
+        },
+        marker_tip_style:"",
+        layers: [],
+        layerGroup: [],
+        imageOverlay: undefined
+      }
+    },
+    mounted() {
+      this.init()
+    },
+    methods: {
+      init() {
+        this.initMap()
+      },
+      // 初始化
+      getData() {
+        getMapLayerById(this.conditions.layerId).then((resp) => {
+          const { data } = resp
+            this.viewData = data
+            this.setRasterLayer()
+        })
+      },
+  
+      // 初始化地图
+      initMap() {
+        var yx = L.latLng
+        var xy = function xy(x, y) {
+          if (L.Util.isArray(x)) {
+            return yx(x[1], x[0])
+          }
+          return yx(y, x)
+        }
+        var bounds = [xy(-600, -600), xy(300, 300)]
+        this.map = L.map('vmap', {
+          crs: L.CRS.Simple, // 一个简单的CRS,可将经度和纬度直接映射到x其中y。可用于平面地图(例如游戏地图)
+          minZoom: 0,
+          maxZoom: 1,
+          zoom: 1,
+          maxBounds: bounds, // 地图拖动到边界后自动弹回
+          maxBoundsViscosity: 0.2,
+          attributionControl: true,
+          zoomControl: true // zoomControl(缩放控制):确定缩放控制是否默认加载在地图上。
+        })
+        this.layerGroup = L.layerGroup().addTo(this.map)
+        this.geoLayer = L.geoJson(null, { pmIgnore: false })
+        this.geoLayer.addTo(this.map)
+        if (!this.viewData.layerVector) return
+        this.geoLayer.addData(this.viewData.layerVector)
+      },
+  
+      // 设置地图背景图
+      setRasterLayer() {
+        var yx = L.latLng
+        var xy = function xy(x, y) {
+          if (L.Util.isArray(x)) {
+            return yx(x[1], x[0])
+          }
+          return yx(y, x)
+        }
+        var bounds = [xy(-600, -600), xy(300, 300)]
+        if (this.imageOverlay) {
+          this.imageOverlay.setUrl(this.viewData.mapRasterLayer)
+        } else {
+          this.imageOverlay = L.imageOverlay(this.viewData.mapRasterLayer, bounds).addTo(this.map)
+        }
+  
+        this.map.fitBounds(bounds)
+        this.map.scrollWheelZoom.disable()
+        this.getMapLayers()
+      },
+  
+      // 选择地图
+      handleSelectMap(obj) {
+        this.viewData.mapRasterLayer = obj.mapRasterLayer
+        this.viewData.mapId = obj.mapId
+        this.formData.mapId = obj.mapId
+        this.setRasterLayer()
+        this.clearLayers()
+      },
+      formSuccess() {
+        this.$emit('formSuccess')
+      },
+      // 获取地图图层数据
+      getMapLayers() {
+        getMapLayer({
+          mapId: this.formData.mapId
+        }).then((resp) => {
+          const { code, data, msg } = resp
+          if (code === 0) {
+            this.mapLayerList = data
+            this.initMapLayers()
+          } else {
+            this.$message.error(msg)
+          }
+        })
+      },
+  
+      // 初始化地图图层
+      initMapLayers() {
+        const self = this
+        const layerList = this.mapLayerList
+        const feature = []
+        if (layerList != null && layerList.length > 0) {
+          layerList.forEach((item) => {
+            const layerMarker = item.layerMarker && JSON.parse(item.layerMarker)
+            const layerVector = item.layerMarker && JSON.parse(item.layerVector)
+            if (layerMarker && layerMarker.properties) {
+              layerMarker.properties.layerId = item.layerId
+              feature.push(layerMarker)
+            }
+            if (layerVector && layerVector.properties) {
+              layerVector.properties.layerId = item.layerId
+              feature.push(layerVector)
+            }
+          })
+          L.geoJSON(feature, {
+            // pointToLayer: function(_feature, latlng) {
+            //   // var smallIcon = L.Icon({
+            //   //   options: {
+            //   //     iconSize: [27, 27],
+            //   //     iconAnchor: [13, 27],
+            //   //     popupAnchor: [1, -24],
+            //   //     iconUrl: 'icone/chapel-2.png'
+            //   //   }
+            //   // })
+            //   // return L.marker(latlng, { icon: smallIcon })
+            // },
+            onEachFeature: (_feature, layer) => {
+              this.layerGroup.addLayer(layer)
+              // layer.on('click', (ev) => {
+              //   self.layer=ev
+              //   self.marker_tip_style=`left:${ev.containerPoint.x+24}px;top:${ev.containerPoint.y-20}px`
+              //   console.log({
+              //       layer:ev
+              //   })
+              // })
+              // layer.on('mouseover', (ev) => {
+              //   self.layer=ev
+              //   self.marker_tip_style=`left:${ev.containerPoint.x+30}px;top:${ev.containerPoint.y-10}px;display:block;`
+              //   console.log({
+              //       layer:ev
+              //   })
+              // })
+              // layer.on('mouseout', (ev) => {
+              //   self.layer=ev
+              //   self.marker_tip_style="left:-1000px;top:0;display:none;"
+              // })
+            }
+          }).addTo(this.layerGroup)
+        }
+      },
+      clearLayers() {
+        if (this.layerGroup && this.layerGroup.clearLayers) {
+          this.layerGroup.clearLayers()
+        }
+      },
+      createGeoJson() {
+        var layerArray = []
+        this.map.eachLayer(function(layer) {
+          if (layer.pm !== 'undefined' && layer.pm != null && layer.pm !== '') {
+            if (layer.pm._enabled === false && layer.pm.options.draggable === true) {
+              layerArray.push(layer)
+            }
+          }
+        })
+        var geojson = L.layerGroup(layerArray).toGeoJSON()
+        for (var n = 0; n < geojson.features.length; n++) {
+          var nowJson = JSON.stringify(geojson.features[n])
+          for (var m = n + 1; m < geojson.features.length; m++) {
+            var nextJson = JSON.stringify(geojson.features[m])
+            if (nowJson === nextJson) {
+              geojson.features.splice(n, 1)
+            }
+          }
+        }
+        return geojson
+      },
+      unique(arr = [], name = 'name') {
+        const res = new Map()
+        return arr.filter((item) => !res.has(item[name]) && res.set(item[name], 1))
+      },
+      deeepClone(params) {
+        return JSON.parse(JSON.stringify(params))
+      }
+    }
+  }
+  </script>
+      <style lang="scss" scoped>
+      .map-container{
+          nav{
+              display: flex;
+              justify-content: space-between;
+              align-items: center;
+              padding: 10px 16px;
+              box-sizing: border-box;
+              background-color: #193142;
+          }
+          .layout-map{
+              display: flex;
+              justify-content: space-between;
+              height: calc(100vh - 190px);
+              position: relative;
+              #vmap {
+                  height: 100%;
+                  flex: 1;
+                  background: #8c939d;
+                  box-sizing: border-box;
+              }
+              #marker-tip{
+                width: 200px;
+                height: 200px;
+                background-image: url(./tip.png);
+                background-size: 100% 100%;
+                background-repeat: no-repeat;
+                position: absolute;
+                z-index: 99999;
+                padding: 15px 20px 0 20px;
+                box-sizing: border-box;
+                display: none;
+                .head{
+                  font-size: 24px;
+                  color: #fff;
+                }
+                .item{
+                  padding-top: 15px;
+                  padding-bottom: 5px;
+                  &.item{
+                    padding-top: 8px;
+                  }
+                  .title{
+                    font-size: 16px;
+                    color: rgba(230, 247, 255, 0.6);
+                    padding-bottom: 5px;
+                  }
+                  span{
+                    font-size: 14px;
+                    padding-right: 15px;
+                    color: #fff;
+                    .warn{
+                      color: red;
+                    }
+                  }
+                }
+              }
+          }
+  
+      }
+      </style>
+  
+  

BIN
src/views/home/components/Map/tip.png


+ 1 - 1
src/views/home/components/SensorInfoStatistics.vue

@@ -160,7 +160,7 @@
                 var myChart = echarts.init(chartDom);
                 this.option && myChart.setOption(this.option);      
                 myChart.on('click', (params)=> {
-                    console.log("SensorInfoStatistics",params)
+                    // console.log("SensorInfoStatistics",params)
                     this.$router.push({
                         path: '/particulars/sensor',
                         query: {

+ 0 - 1
src/views/home/components/SensorStatus.vue

@@ -83,7 +83,6 @@
         // this.$nextTick(()=>{
         //     this.init()
         // })
-        console.log('sensor-status-chart',this.myChart)
     },
     methods:{
         init(){

+ 3 - 1
src/views/home/components/index.js

@@ -7,6 +7,7 @@ import SensorWarningStatistics from './SensorWarningStatistics.vue'
 import SensorWarningInfo from './SensorWarningInfo.vue'
 import SensorInfoStatistics from './SensorInfoStatistics.vue'
 
+import Map from './Map/index.vue'
 
 export  {
     WarningStatistics,
@@ -15,5 +16,6 @@ export  {
     SensorStatus,
     SensorWarningStatistics,
     SensorWarningInfo,
-    SensorInfoStatistics
+    SensorInfoStatistics,
+    Map
 }

+ 28 - 12
src/views/home/index.vue

@@ -6,7 +6,7 @@
         <warn-report :data="reportData" /> 
     </div>
     <div class="main">
-        <div id="map"></div>
+        <Map ref="map" />
     </div>
     <div class="side-right">
         <sensor-status ref="sensor-status" />
@@ -17,7 +17,8 @@
 </div>
 </template>
 <script>
-import {parseTime} from '@/utils'
+import {parseTime,getQueryObject} from '@/utils'
+import {getUserProfile} from '@/api/system'
 import { useStore } from '@/store/modules/user'
 const store=useStore()
 import {   
@@ -27,7 +28,8 @@ import {
     SensorStatus,
     SensorWarningStatistics,
     SensorWarningInfo,
-    SensorInfoStatistics
+    SensorInfoStatistics,
+    Map
 } from './components'
  export default {
     name:'Home',
@@ -38,7 +40,8 @@ import {
         SensorStatus,
         SensorWarningStatistics,
         SensorWarningInfo,
-        SensorInfoStatistics
+        SensorInfoStatistics,
+        Map
     },
     data(){
         return{
@@ -50,16 +53,13 @@ import {
     },
     created(){
         this.init()
-        store.$patch({
-            user:{
-               name:"张三" 
-            },
-            counter: store.counter + 1,
-            name: 'Abalam',
-        })
     }, 
     methods:{
         init(){
+            this.saveUserInfo()
+            this.initChart()
+        },
+        initChart(){
             let reportData=[]
             for(let i=0;i<20;i++){
                 reportData.push({
@@ -75,7 +75,23 @@ import {
             this.$nextTick(()=>{
                 this.$refs['sensor-status'].init();
                 this.$refs['sensor-info-statistics'].init();
-            })
+            })            
+        },
+        saveUserInfo(){
+            if(localStorage.getItem('user')){
+                let user=localStorage.getItem('user');
+                user=JSON.parse(user)
+                store.$patch({
+                    user
+                })
+            }else{
+                getUserProfile().then((res)=>{     
+                    store.$patch({
+                        user:res.data
+                    })                               
+                    localStorage.setItem('user',JSON.stringify(res.data))
+                })
+            }
         }
     }
  }

+ 72 - 36
src/views/particulars/goaf_info.vue

@@ -7,51 +7,48 @@
         <div class="container">
             <div class="lf">
                 <div class="search-container">
-                    <el-input v-model="search.code"  class="input" placeholder="请输入采空区编号" />
-                    <div class="bt search" @click="searchSubmit">查询</div>
+                    <el-input v-model="conditions.goafName"  class="input" placeholder="请输入采空区编号" />
+                    <div class="bt search" @click="getData">查询</div>
                     <div class="bt" @click="resetSubmit">重置</div>
                 </div>
                 <div class="lf-container"> 
                     <table>
                         <tr>
                             <td>矿带</td>
+                            <td>矿体</td>
                             <td>中段</td>
                             <td>采空编号</td>
-                            <td>水平断面均暴露面积(㎡)</td>
-                            <td>平均倾向宽度(m)</td>
-                            <td>平均暴露高度(m)</td>
-                            <td>体积(m³)</td>
-                            <td>倾向°</td>
-                            <td>倾角°</td>
                             <td>顶板矿柱厚度(m)</td>
                             <td>保安间柱平均厚度(m)</td>
                             <td>勘探位置</td>
-                            <td>位于矿体编号</td>
                             <td>围岩岩性</td>
                             <td>围岩稳定性</td>
+                            <td>可填充体积</td>
+                            <td>剩余可填充体积</td>
+                            <td>是否填充</td>
                             <td>形成时间</td>
                         </tr>
-                        <tr v-for="(item,index) in 11" :key="index">
-                            <td v-if="index<1" rowspan="11" >Ⅲ</td>
-                            <td v-if="index<1" rowspan="11">770</td>
-                            <td>32003</td>
-                            <td>444</td>
-                            <td>5.1</td>
-                            <td>5.1</td>
-                            <td>245</td>
-                            <td>45</td>
-                            <td>36</td>
-                            <td>82.1</td>
-                            <td>8.3</td>
-                            <td>0-1</td>
-                            <td>Ⅲ3</td>
-                            <td>岩屑晶屑凝灰岩</td>
-                            <td>稳定</td>
-                            <td>2023-04-12</td>
+                        <tr v-for="(item,index) in dataList" :key="index">
+                            <td >{{NumConvertLM(item.goafOrebelt)}}</td>
+                            <td >{{item.goafOrebody}}</td>
+                            <td>{{item.goafOreheight}}</td>
+                            <td>{{item.goafName}}</td>
+                            <td>{{item.goafRoofpillarThickness}}</td>
+                            <td>{{item.goafIncoavThickness}}</td>
+                            <td>{{item.goafExpLocation}}</td>
+                            <td>{{item.goafRockLithology}}</td>
+                            <td>{{item.goafRockStability}}</td>
+                            <td>{{item.goafCanfillVolume}}</td>
+                            <td>{{item.goafRemainVolume}}</td>
+                            <td>{{item.goafIsFill===0?'是':'否'}}</td>
+                            <td>{{parseTime(item.goafFormationTime,'{y}:{m}:{d}')}}</td>
                         </tr>
                     </table>
                     <div class="el-pagination-wrap">
-                        <el-pagination small layout="prev, pager, next" :total="50" />
+                        <el-pagination small layout="prev, pager, next" 
+                        :total="total"
+                        @size-change="handleSizeChange"
+                        @current-change="handleCurrentChange" />
                      </div>                                   
                 </div>
             </div>
@@ -61,6 +58,8 @@
 <script>
 import AlertChart from './components/AlertChart.vue'
 import WarnChart from './components/WarnChart.vue'
+import { getGoafBaseInfoByPage } from '@/api/goaf/info'
+import { NumConvertLM ,parseTime} from '@/utils'
  export default {
     name:"goaf_info",
     components:{
@@ -71,37 +70,59 @@ import WarnChart from './components/WarnChart.vue'
         return{
             activeId:'1',
             activetab:'1',
+            conditions: {
+                page: 1,
+                limit: 10,
+                goafName:""
+            },           
             tabs:[
                 {name:"Ⅲ号",id:"1"},
                 {name:"770",id:"2"},
                 {name:"32001",id:"3"},
             ],
-            search:{
-                code:""
-            }                     
+            dataList:[],
+            total:0                 
         }
     },
     created(){
         this.init()
     },
     methods:{
+        NumConvertLM,
+        parseTime,
         init(){
-
+            this.getData()
         },
+        getData() {
+            getGoafBaseInfoByPage(this.conditions).then((resp) => {
+                this.listLoading = false
+                const {data, total } = resp
+                this.dataList = data
+                this.total = total
+            })
+        },        
         changeHead(item){
             this.activeId=item.id
         },
         changeTab(item){
             this.activetab=item.id
         },
-        searchSubmit(){
-
-        },
         resetSubmit(){
-            this.search={
-                code:""           
+            this.conditions={
+                page: 1,
+                limit: 10,
+                goafName:""      
             }
+            this.getData()
+        },
+        handleSizeChange(limit){
+            this.conditions.limit=limit
+            this.getData()
         },
+        handleCurrentChange(pageNumber){
+            this.conditions.page=pageNumber
+            this.getData()
+        }
     }
  }
 </script>
@@ -143,6 +164,18 @@ import WarnChart from './components/WarnChart.vue'
                 box-sizing: border-box;
                 display: flex;
                 flex-wrap: wrap;
+                .search-item{
+                    display: flex;
+                    justify-content: center;
+                    align-items: center;
+                    margin-bottom: 14px;
+                    span{
+                        font-family: 'Ping Hei';
+                        font-size: 14px;
+                        line-height: 1;
+                        color: rgba(255, 255, 255, 0.7);
+                    }
+                }
                 .input,.select,.el-date-editor{
                     width: 220px;
                     height: 36px;
@@ -192,6 +225,9 @@ import WarnChart from './components/WarnChart.vue'
                         padding: 8px;
                         line-height: 1;
                         border-bottom: 1px solid rgba(255, 255, 255, 0.15);
+                        overflow: hidden; 
+                        white-space: nowrap; 
+                        text-overflow: ellipsis;
                     }
                 }
                 .el-pagination-wrap{

+ 57 - 30
src/views/particulars/sensor.vue

@@ -9,34 +9,48 @@
         <div class="container">
             <div class="lf">
                 <div class="search-container">
-                    <el-input v-model="search.name"  class="input" placeholder="请输入传感器名称" />
-                    <el-select v-model="search.level" class="select" placeholder="全部">
-                        <el-option
-                        v-for="item in options1"
-                        :key="item.value"
-                        :label="item.label"
-                        :value="item.value"
-                        />
-                    </el-select>
-                    <el-select v-model="search.value" class="select" placeholder="全部">
-                        <el-option
-                        v-for="item in options"
-                        :key="item.value"
-                        :label="item.label"
-                        :value="item.value"
-                        />
-                    </el-select>
-                    <el-date-picker
-                        v-model="search.time"
-                        type="date"
-                        class="date11"
-                        placeholder="请选择预警时间"
-                        style="width:200px;margin-right: 14px;"
-                        :default-value="new Date(2010, 9, 1)"
-                    />
-                    <el-input-number v-model="search.range" class="input" placeholder="Please input" :controls="false" style="margin-top: 14px;"/>
+                    <div class="search-item">
+                        <el-input v-model="search.name"  class="input" placeholder="请输入传感器名称" />
+                    </div>
+                    <div class="search-item">
+                        <span>预警等级:</span>
+                        <el-select v-model="search.level" class="select" placeholder="全部">
+                            <el-option
+                            v-for="item in options1"
+                            :key="item.value"
+                            :label="item.label"
+                            :value="item.value"
+                            />
+                        </el-select>                    
+                    </div>
+                    <div class="search-item">
+                        <span>状态:</span>
+                        <el-select v-model="search.value" class="select" placeholder="全部">
+                            <el-option
+                            v-for="item in options"
+                            :key="item.value"
+                            :label="item.label"
+                            :value="item.value"
+                            />
+                        </el-select>                    
+                    </div>
+                    <div class="search-item">
+                        <span>预警时间:</span>
+                        <el-date-picker
+                            v-model="search.time"
+                            type="date"
+                            class="date11"
+                            placeholder="请选择预警时间"
+                            style="width:200px;margin-right: 14px;"
+                            :default-value="new Date(2010, 9, 1)"
+                        />                        
+                    </div>
+                    <div class="search-item">
+                        <span>数值范围:</span>
+                        <el-input-number v-model="search.range" class="input" placeholder="Please input" :controls="false" />                        
+                    </div>
                     <div class="bt search" @click="searchSubmit">查询</div>
-                    <div class="bt" @click="resetSubmit">重置</div>
+                    <div class="bt" @click="resetSubmit">重置</div>    
                 </div>
                 <div class="lf-container">
                     <div class="card-container">
@@ -219,8 +233,20 @@ import WarnChart from './components/WarnChart.vue'
                 box-sizing: border-box;
                 display: flex;
                 flex-wrap: wrap;
+                .search-item{
+                    display: flex;
+                    justify-content: center;
+                    align-items: center;
+                    margin-bottom: 14px;
+                    span{
+                        font-family: 'Ping Hei';
+                        font-size: 14px;
+                        line-height: 1;
+                        color: rgba(255, 255, 255, 0.7);
+                    }
+                }
                 .input,.select,.el-date-editor{
-                    width: 220px;
+                    width: 180px;
                     margin-right:16px;
                     color: #fff;
                 }
@@ -244,10 +270,11 @@ import WarnChart from './components/WarnChart.vue'
                     background: rgba(8, 128, 255, 0.2);
                     border-radius: 4px;  
                     margin-top: 14px;    
-                    cursor: pointer;              
+                    cursor: pointer;   
+                    margin-top: 0;           
                     &.search{
                         background: linear-gradient(rgba(94, 226, 255, 1),rgba(79, 175, 255, 1));
-                        margin:14px 10px 0 0;
+                        margin:0 10px 0 0;
                     }
                 }       
             }

+ 255 - 3
yarn.lock

@@ -1024,6 +1024,18 @@
   dependencies:
     "@floating-ui/core" "^1.2.6"
 
+"@geoman-io/leaflet-geoman-free@^2.11.3":
+  version "2.14.2"
+  resolved "https://registry.yarnpkg.com/@geoman-io/leaflet-geoman-free/-/leaflet-geoman-free-2.14.2.tgz#c84c2115c263f34d11dc0b43859551639fe3d56b"
+  integrity sha512-6lIyG8RvSVdFjVjiQgBPyNASjymSyqzsiUeBW0pA+q41lB5fAg4SDC6SfJvWdEyDHa81Jb5FWjUkCc9O+u0gbg==
+  dependencies:
+    "@turf/boolean-contains" "^6.5.0"
+    "@turf/kinks" "^6.5.0"
+    "@turf/line-intersect" "^6.5.0"
+    "@turf/line-split" "^6.5.0"
+    lodash "4.17.21"
+    polygon-clipping "0.15.3"
+
 "@hapi/hoek@^9.0.0":
   version "9.3.0"
   resolved "https://registry.npmmirror.com/@hapi/hoek/-/hoek-9.3.0.tgz#8368869dcb735be2e7f5cb7647de78e167a251fb"
@@ -1187,6 +1199,156 @@
   resolved "https://registry.npmmirror.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
   integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==
 
+"@turf/bbox@*", "@turf/bbox@^6.5.0":
+  version "6.5.0"
+  resolved "https://registry.yarnpkg.com/@turf/bbox/-/bbox-6.5.0.tgz#bec30a744019eae420dac9ea46fb75caa44d8dc5"
+  integrity sha512-RBbLaao5hXTYyyg577iuMtDB8ehxMlUqHEJiMs8jT1GHkFhr6sYre3lmLsPeYEi/ZKj5TP5tt7fkzNdJ4GIVyw==
+  dependencies:
+    "@turf/helpers" "^6.5.0"
+    "@turf/meta" "^6.5.0"
+
+"@turf/bearing@^6.5.0":
+  version "6.5.0"
+  resolved "https://registry.yarnpkg.com/@turf/bearing/-/bearing-6.5.0.tgz#462a053c6c644434bdb636b39f8f43fb0cd857b0"
+  integrity sha512-dxINYhIEMzgDOztyMZc20I7ssYVNEpSv04VbMo5YPQsqa80KO3TFvbuCahMsCAW5z8Tncc8dwBlEFrmRjJG33A==
+  dependencies:
+    "@turf/helpers" "^6.5.0"
+    "@turf/invariant" "^6.5.0"
+
+"@turf/boolean-contains@^6.5.0":
+  version "6.5.0"
+  resolved "https://registry.yarnpkg.com/@turf/boolean-contains/-/boolean-contains-6.5.0.tgz#f802e7432fb53109242d5bf57393ef2f53849bbf"
+  integrity sha512-4m8cJpbw+YQcKVGi8y0cHhBUnYT+QRfx6wzM4GI1IdtYH3p4oh/DOBJKrepQyiDzFDaNIjxuWXBh0ai1zVwOQQ==
+  dependencies:
+    "@turf/bbox" "^6.5.0"
+    "@turf/boolean-point-in-polygon" "^6.5.0"
+    "@turf/boolean-point-on-line" "^6.5.0"
+    "@turf/helpers" "^6.5.0"
+    "@turf/invariant" "^6.5.0"
+
+"@turf/boolean-point-in-polygon@^6.5.0":
+  version "6.5.0"
+  resolved "https://registry.yarnpkg.com/@turf/boolean-point-in-polygon/-/boolean-point-in-polygon-6.5.0.tgz#6d2e9c89de4cd2e4365004c1e51490b7795a63cf"
+  integrity sha512-DtSuVFB26SI+hj0SjrvXowGTUCHlgevPAIsukssW6BG5MlNSBQAo70wpICBNJL6RjukXg8d2eXaAWuD/CqL00A==
+  dependencies:
+    "@turf/helpers" "^6.5.0"
+    "@turf/invariant" "^6.5.0"
+
+"@turf/boolean-point-on-line@^6.5.0":
+  version "6.5.0"
+  resolved "https://registry.yarnpkg.com/@turf/boolean-point-on-line/-/boolean-point-on-line-6.5.0.tgz#a8efa7bad88760676f395afb9980746bc5b376e9"
+  integrity sha512-A1BbuQ0LceLHvq7F/P7w3QvfpmZqbmViIUPHdNLvZimFNLo4e6IQunmzbe+8aSStH9QRZm3VOflyvNeXvvpZEQ==
+  dependencies:
+    "@turf/helpers" "^6.5.0"
+    "@turf/invariant" "^6.5.0"
+
+"@turf/destination@^6.5.0":
+  version "6.5.0"
+  resolved "https://registry.yarnpkg.com/@turf/destination/-/destination-6.5.0.tgz#30a84702f9677d076130e0440d3223ae503fdae1"
+  integrity sha512-4cnWQlNC8d1tItOz9B4pmJdWpXqS0vEvv65bI/Pj/genJnsL7evI0/Xw42RvEGROS481MPiU80xzvwxEvhQiMQ==
+  dependencies:
+    "@turf/helpers" "^6.5.0"
+    "@turf/invariant" "^6.5.0"
+
+"@turf/distance@^6.5.0":
+  version "6.5.0"
+  resolved "https://registry.yarnpkg.com/@turf/distance/-/distance-6.5.0.tgz#21f04d5f86e864d54e2abde16f35c15b4f36149a"
+  integrity sha512-xzykSLfoURec5qvQJcfifw/1mJa+5UwByZZ5TZ8iaqjGYN0vomhV9aiSLeYdUGtYRESZ+DYC/OzY+4RclZYgMg==
+  dependencies:
+    "@turf/helpers" "^6.5.0"
+    "@turf/invariant" "^6.5.0"
+
+"@turf/helpers@6.x", "@turf/helpers@^6.5.0":
+  version "6.5.0"
+  resolved "https://registry.yarnpkg.com/@turf/helpers/-/helpers-6.5.0.tgz#f79af094bd6b8ce7ed2bd3e089a8493ee6cae82e"
+  integrity sha512-VbI1dV5bLFzohYYdgqwikdMVpe7pJ9X3E+dlr425wa2/sMJqYDhTO++ec38/pcPvPE6oD9WEEeU3Xu3gza+VPw==
+
+"@turf/invariant@^6.5.0":
+  version "6.5.0"
+  resolved "https://registry.yarnpkg.com/@turf/invariant/-/invariant-6.5.0.tgz#970afc988023e39c7ccab2341bd06979ddc7463f"
+  integrity sha512-Wv8PRNCtPD31UVbdJE/KVAWKe7l6US+lJItRR/HOEW3eh+U/JwRCSUl/KZ7bmjM/C+zLNoreM2TU6OoLACs4eg==
+  dependencies:
+    "@turf/helpers" "^6.5.0"
+
+"@turf/kinks@^6.5.0":
+  version "6.5.0"
+  resolved "https://registry.yarnpkg.com/@turf/kinks/-/kinks-6.5.0.tgz#80e7456367535365012f658cf1a988b39a2c920b"
+  integrity sha512-ViCngdPt1eEL7hYUHR2eHR662GvCgTc35ZJFaNR6kRtr6D8plLaDju0FILeFFWSc+o8e3fwxZEJKmFj9IzPiIQ==
+  dependencies:
+    "@turf/helpers" "^6.5.0"
+
+"@turf/line-intersect@^6.5.0":
+  version "6.5.0"
+  resolved "https://registry.yarnpkg.com/@turf/line-intersect/-/line-intersect-6.5.0.tgz#dea48348b30c093715d2195d2dd7524aee4cf020"
+  integrity sha512-CS6R1tZvVQD390G9Ea4pmpM6mJGPWoL82jD46y0q1KSor9s6HupMIo1kY4Ny+AEYQl9jd21V3Scz20eldpbTVA==
+  dependencies:
+    "@turf/helpers" "^6.5.0"
+    "@turf/invariant" "^6.5.0"
+    "@turf/line-segment" "^6.5.0"
+    "@turf/meta" "^6.5.0"
+    geojson-rbush "3.x"
+
+"@turf/line-segment@^6.5.0":
+  version "6.5.0"
+  resolved "https://registry.yarnpkg.com/@turf/line-segment/-/line-segment-6.5.0.tgz#ee73f3ffcb7c956203b64ed966d96af380a4dd65"
+  integrity sha512-jI625Ho4jSuJESNq66Mmi290ZJ5pPZiQZruPVpmHkUw257Pew0alMmb6YrqYNnLUuiVVONxAAKXUVeeUGtycfw==
+  dependencies:
+    "@turf/helpers" "^6.5.0"
+    "@turf/invariant" "^6.5.0"
+    "@turf/meta" "^6.5.0"
+
+"@turf/line-split@^6.5.0":
+  version "6.5.0"
+  resolved "https://registry.yarnpkg.com/@turf/line-split/-/line-split-6.5.0.tgz#116d7fbf714457878225187f5820ef98db7b02c2"
+  integrity sha512-/rwUMVr9OI2ccJjw7/6eTN53URtGThNSD5I0GgxyFXMtxWiloRJ9MTff8jBbtPWrRka/Sh2GkwucVRAEakx9Sw==
+  dependencies:
+    "@turf/bbox" "^6.5.0"
+    "@turf/helpers" "^6.5.0"
+    "@turf/invariant" "^6.5.0"
+    "@turf/line-intersect" "^6.5.0"
+    "@turf/line-segment" "^6.5.0"
+    "@turf/meta" "^6.5.0"
+    "@turf/nearest-point-on-line" "^6.5.0"
+    "@turf/square" "^6.5.0"
+    "@turf/truncate" "^6.5.0"
+    geojson-rbush "3.x"
+
+"@turf/meta@6.x", "@turf/meta@^6.5.0":
+  version "6.5.0"
+  resolved "https://registry.yarnpkg.com/@turf/meta/-/meta-6.5.0.tgz#b725c3653c9f432133eaa04d3421f7e51e0418ca"
+  integrity sha512-RrArvtsV0vdsCBegoBtOalgdSOfkBrTJ07VkpiCnq/491W67hnMWmDu7e6Ztw0C3WldRYTXkg3SumfdzZxLBHA==
+  dependencies:
+    "@turf/helpers" "^6.5.0"
+
+"@turf/nearest-point-on-line@^6.5.0":
+  version "6.5.0"
+  resolved "https://registry.yarnpkg.com/@turf/nearest-point-on-line/-/nearest-point-on-line-6.5.0.tgz#8e1cd2cdc0b5acaf4c8d8b3b33bb008d3cb99e7b"
+  integrity sha512-WthrvddddvmymnC+Vf7BrkHGbDOUu6Z3/6bFYUGv1kxw8tiZ6n83/VG6kHz4poHOfS0RaNflzXSkmCi64fLBlg==
+  dependencies:
+    "@turf/bearing" "^6.5.0"
+    "@turf/destination" "^6.5.0"
+    "@turf/distance" "^6.5.0"
+    "@turf/helpers" "^6.5.0"
+    "@turf/invariant" "^6.5.0"
+    "@turf/line-intersect" "^6.5.0"
+    "@turf/meta" "^6.5.0"
+
+"@turf/square@^6.5.0":
+  version "6.5.0"
+  resolved "https://registry.yarnpkg.com/@turf/square/-/square-6.5.0.tgz#ab43eef99d39c36157ab5b80416bbeba1f6b2122"
+  integrity sha512-BM2UyWDmiuHCadVhHXKIx5CQQbNCpOxB6S/aCNOCLbhCeypKX5Q0Aosc5YcmCJgkwO5BERCC6Ee7NMbNB2vHmQ==
+  dependencies:
+    "@turf/distance" "^6.5.0"
+    "@turf/helpers" "^6.5.0"
+
+"@turf/truncate@^6.5.0":
+  version "6.5.0"
+  resolved "https://registry.yarnpkg.com/@turf/truncate/-/truncate-6.5.0.tgz#c3a16cad959f1be1c5156157d5555c64b19185d8"
+  integrity sha512-pFxg71pLk+eJj134Z9yUoRhIi8vqnnKvCYwdT4x/DQl/19RVdq1tV3yqOT3gcTQNfniteylL5qV1uTBDV5sgrg==
+  dependencies:
+    "@turf/helpers" "^6.5.0"
+    "@turf/meta" "^6.5.0"
+
 "@types/body-parser@*":
   version "1.19.2"
   resolved "https://registry.npmmirror.com/@types/body-parser/-/body-parser-1.19.2.tgz#aea2059e28b7658639081347ac4fab3de166e6f0"
@@ -1262,6 +1424,11 @@
     "@types/qs" "*"
     "@types/serve-static" "*"
 
+"@types/geojson@7946.0.8":
+  version "7946.0.8"
+  resolved "https://registry.yarnpkg.com/@types/geojson/-/geojson-7946.0.8.tgz#30744afdb385e2945e22f3b033f897f76b1f12ca"
+  integrity sha512-1rkryxURpr6aWP7R786/UQOkJ3PcpQiWkAXBmdWc7ryFWqN6a4xfK7BtjXvFBKO9LjQ+MWQSWxYeZX1OApnArA==
+
 "@types/html-minifier-terser@^6.0.0":
   version "6.1.0"
   resolved "https://registry.npmmirror.com/@types/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz#4fc33a00c1d0c16987b1a20cf92d20614c55ac35"
@@ -2086,6 +2253,11 @@ async@^2.6.4:
   dependencies:
     lodash "^4.17.14"
 
+asynckit@^0.4.0:
+  version "0.4.0"
+  resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79"
+  integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
+
 at-least-node@^1.0.0:
   version "1.0.0"
   resolved "https://registry.npmmirror.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
@@ -2108,6 +2280,15 @@ available-typed-arrays@^1.0.5:
   resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7"
   integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==
 
+axios@^1.4.0:
+  version "1.4.0"
+  resolved "https://registry.yarnpkg.com/axios/-/axios-1.4.0.tgz#38a7bf1224cd308de271146038b551d725f0be1f"
+  integrity sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==
+  dependencies:
+    follow-redirects "^1.15.0"
+    form-data "^4.0.0"
+    proxy-from-env "^1.1.0"
+
 babel-eslint@^10.1.0:
   version "10.1.0"
   resolved "https://registry.yarnpkg.com/babel-eslint/-/babel-eslint-10.1.0.tgz#6968e568a910b78fb3779cdd8b6ac2f479943232"
@@ -2486,6 +2667,13 @@ colorette@^2.0.10:
   resolved "https://registry.npmmirror.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798"
   integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==
 
+combined-stream@^1.0.8:
+  version "1.0.8"
+  resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f"
+  integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==
+  dependencies:
+    delayed-stream "~1.0.0"
+
 commander@^2.20.0:
   version "2.20.3"
   resolved "https://registry.npmmirror.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33"
@@ -2823,6 +3011,11 @@ define-properties@^1.1.3, define-properties@^1.1.4:
     has-property-descriptors "^1.0.0"
     object-keys "^1.1.1"
 
+delayed-stream@~1.0.0:
+  version "1.0.0"
+  resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619"
+  integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==
+
 depd@2.0.0:
   version "2.0.0"
   resolved "https://registry.npmmirror.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
@@ -3516,7 +3709,7 @@ flatted@^3.1.0:
   resolved "https://registry.npmmirror.com/flatted/-/flatted-3.2.7.tgz#609f39207cb614b89d0765b477cb2d437fbf9787"
   integrity sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==
 
-follow-redirects@^1.0.0:
+follow-redirects@^1.0.0, follow-redirects@^1.15.0:
   version "1.15.2"
   resolved "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.2.tgz#b460864144ba63f2681096f274c4e57026da2c13"
   integrity sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==
@@ -3528,6 +3721,15 @@ for-each@^0.3.3:
   dependencies:
     is-callable "^1.1.3"
 
+form-data@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.0.tgz#93919daeaf361ee529584b9b31664dc12c9fa452"
+  integrity sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==
+  dependencies:
+    asynckit "^0.4.0"
+    combined-stream "^1.0.8"
+    mime-types "^2.1.12"
+
 forwarded@0.2.0:
   version "0.2.0"
   resolved "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
@@ -3593,6 +3795,17 @@ gensync@^1.0.0-beta.2:
   resolved "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz#32a6ee76c3d7f52d46b2b1ae5d93fea8580a25e0"
   integrity sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==
 
+geojson-rbush@3.x:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/geojson-rbush/-/geojson-rbush-3.2.0.tgz#8b543cf0d56f99b78faf1da52bb66acad6dfc290"
+  integrity sha512-oVltQTXolxvsz1sZnutlSuLDEcQAKYC/uXt9zDzJJ6bu0W+baTI8LZBaTup5afzibEH4N3jlq2p+a152wlBJ7w==
+  dependencies:
+    "@turf/bbox" "*"
+    "@turf/helpers" "6.x"
+    "@turf/meta" "6.x"
+    "@types/geojson" "7946.0.8"
+    rbush "^3.0.1"
+
 get-caller-file@^2.0.5:
   version "2.0.5"
   resolved "https://registry.npmmirror.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
@@ -4239,6 +4452,11 @@ joi@^17.4.0:
     "@sideway/formula" "^3.0.1"
     "@sideway/pinpoint" "^2.0.0"
 
+js-cookie@^3.0.5:
+  version "3.0.5"
+  resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-3.0.5.tgz#0b7e2fd0c01552c58ba86e0841f94dc2557dcdbc"
+  integrity sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==
+
 js-message@1.0.7:
   version "1.0.7"
   resolved "https://registry.npmmirror.com/js-message/-/js-message-1.0.7.tgz#fbddd053c7a47021871bb8b2c95397cc17c20e47"
@@ -4342,6 +4560,11 @@ launch-editor@^2.2.1, launch-editor@^2.6.0:
     picocolors "^1.0.0"
     shell-quote "^1.7.3"
 
+leaflet@^1.7.1:
+  version "1.9.3"
+  resolved "https://registry.yarnpkg.com/leaflet/-/leaflet-1.9.3.tgz#52ec436954964e2d3d39e0d433da4b2500d74414"
+  integrity sha512-iB2cR9vAkDOu5l3HAay2obcUHZ7xwUBBjph8+PGtmW/2lYhbLizWtG7nTeYht36WfOslixQF9D/uSIzhZgGMfQ==
+
 levn@^0.4.1:
   version "0.4.1"
   resolved "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade"
@@ -4442,7 +4665,7 @@ lodash.uniq@^4.5.0:
   resolved "https://registry.npmmirror.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
   integrity sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==
 
-lodash@^4.17.14, lodash@^4.17.20, lodash@^4.17.21:
+lodash@4.17.21, lodash@^4.17.14, lodash@^4.17.20, lodash@^4.17.21:
   version "4.17.21"
   resolved "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
   integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@@ -4569,7 +4792,7 @@ mime-db@1.52.0, "mime-db@>= 1.43.0 < 2":
   resolved "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
   integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
 
-mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34:
+mime-types@^2.1.12, mime-types@^2.1.27, mime-types@^2.1.31, mime-types@~2.1.17, mime-types@~2.1.24, mime-types@~2.1.34:
   version "2.1.35"
   resolved "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
   integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
@@ -5056,6 +5279,13 @@ pkg-dir@^4.1.0:
   dependencies:
     find-up "^4.0.0"
 
+polygon-clipping@0.15.3:
+  version "0.15.3"
+  resolved "https://registry.yarnpkg.com/polygon-clipping/-/polygon-clipping-0.15.3.tgz#0215840438470ba2e9e6593625e4ea5c1087b4b7"
+  integrity sha512-ho0Xx5DLkgxRx/+n4O74XyJ67DcyN3Tu9bGYKsnTukGAW6ssnuak6Mwcyb1wHy9MZc9xsUWqIoiazkZB5weECg==
+  dependencies:
+    splaytree "^3.1.0"
+
 portfinder@^1.0.26:
   version "1.0.32"
   resolved "https://registry.npmmirror.com/portfinder/-/portfinder-1.0.32.tgz#2fe1b9e58389712429dc2bea5beb2146146c7f81"
@@ -5369,6 +5599,11 @@ proxy-addr@~2.0.7:
     forwarded "0.2.0"
     ipaddr.js "1.9.1"
 
+proxy-from-env@^1.1.0:
+  version "1.1.0"
+  resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2"
+  integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==
+
 pseudomap@^1.0.2:
   version "1.0.2"
   resolved "https://registry.npmmirror.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3"
@@ -5399,6 +5634,11 @@ queue-microtask@^1.2.2:
   resolved "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243"
   integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==
 
+quickselect@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/quickselect/-/quickselect-2.0.0.tgz#f19680a486a5eefb581303e023e98faaf25dd018"
+  integrity sha512-RKJ22hX8mHe3Y6wH/N3wCM6BWtjaxIyyUIkpHOvfFnxdI4yD4tBXEBKSbriGujF6jnSVkJrffuo6vxACiSSxIw==
+
 randombytes@^2.1.0:
   version "2.1.0"
   resolved "https://registry.npmmirror.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
@@ -5421,6 +5661,13 @@ raw-body@2.5.1:
     iconv-lite "0.4.24"
     unpipe "1.0.0"
 
+rbush@^3.0.1:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/rbush/-/rbush-3.0.1.tgz#5fafa8a79b3b9afdfe5008403a720cc1de882ecf"
+  integrity sha512-XRaVO0YecOpEuIvbhbpTrZgoiI6xBlz6hnlr6EHhd+0x9ase6EmeN+hdwwUaJvLcsFFQ8iWVF1GAK1yB0BWi0w==
+  dependencies:
+    quickselect "^2.0.0"
+
 read-pkg-up@^7.0.1:
   version "7.0.1"
   resolved "https://registry.npmmirror.com/read-pkg-up/-/read-pkg-up-7.0.1.tgz#f3a6135758459733ae2b95638056e1854e7ef507"
@@ -5908,6 +6155,11 @@ spdy@^4.0.2:
     select-hose "^2.0.0"
     spdy-transport "^3.0.0"
 
+splaytree@^3.1.0:
+  version "3.1.2"
+  resolved "https://registry.yarnpkg.com/splaytree/-/splaytree-3.1.2.tgz#d1db2691665a3c69d630de98d55145a6546dc166"
+  integrity sha512-4OM2BJgC5UzrhVnnJA4BkHKGtjXNzzUfpQjCO8I05xYPsfS/VuQDwjCGGMi8rYQilHEV4j8NBqTFbls/PZEE7A==
+
 ssri@^8.0.1:
   version "8.0.1"
   resolved "https://registry.npmmirror.com/ssri/-/ssri-8.0.1.tgz#638e4e439e2ffbd2cd289776d5ca457c4f51a2af"