无人机管理后台前端(已迁走)
shuishen
2025-09-12 a2bb326dfbc6024916d360b89d021243f8502e33
src/views/airspace/components/AirspaceMap.vue
@@ -5,16 +5,35 @@
         <div class="form-container">
            <avue-form ref="formEle" :option="option" v-model="form"></avue-form>
         </div>
         <div class="map-container">
            <div class="tool-tip warning" v-show="isShowWaringTip">
               <span class="icon">
                  <el-icon>
                     <WarningFilled />
                  </el-icon>
               </span>
               <span>空域不支持交叉面,无法生成空域</span>
            </div>
            <div id="AirspaceMap" class="ztzf-cesium"></div>
         </div>
         <div class="btn-container">
            <el-button icon="el-icon-check" type="primary" @click="handleSubmit">确定</el-button>
            <el-button icon="el-icon-close" @click="dialogShow = false">取消</el-button>
         </div>
      </div>
   </el-dialog>
</template>
<script setup>
import _, { cloneDeep, throttle } from 'lodash'
import * as Cesium from 'cesium'
import * as turf from '@turf/turf'
import { DrawPolygon } from '@/utils/drawPolygon/drawPolygon'
import { PublicCesium } from '@/utils/cesium/publicCesium'
import { ElMessageBox, ElMessage } from 'element-plus'
const props = defineProps({
   title: {
@@ -23,6 +42,8 @@
   },
})
const emit = defineEmits(['submitClick'])
const dialogShow = defineModel('show')
const formEle = ref(null)
@@ -30,10 +51,11 @@
const option = ref({
   submitBtn: false,
   emptyBtn: false,
   menuBtn: false,
   column: [
      {
         label: '名称',
         prop: 'input',
         prop: 'name',
         type: 'input',
         rules: [
            {
@@ -46,7 +68,7 @@
      {
         label: '高度',
         prop: 'input',
         prop: 'height',
         type: 'input',
         rules: [
            {
@@ -59,18 +81,13 @@
      {
         label: '类型',
         prop: 'select',
         prop: 'type',
         type: 'select',
         dicData: [
            {
               label: '字典1',
               value: 0,
            },
            {
               label: '字典2',
               value: 1,
            },
         ],
         dicUrl: `/blade-system/dict/dictionary?code=flow`,
         props: {
            label: 'dictValue',
            value: 'dictKey',
         },
         rules: [
            {
               required: true,
@@ -97,21 +114,26 @@
      },
      {
         label: '备注',
         prop: 'input',
         label: '管控原因',
         prop: 'remark',
         type: 'textarea',
         span: 24,
         rows: 1,
         minRows: 1,
         maxRows: 1,
         placeholder: '请输入备注',
         placeholder: '请输入管控原因',
      },
   ],
})
let drawPolygonExample = null
let publicCesiumInstance = null
let viewer = null
// 地图
const isShowWaringTip = ref(false)
const curDrawPolygonData = ref([])
const initMap = () => {
   if (!document.getElementById('AirspaceMap')) {
      return
@@ -121,37 +143,155 @@
      flatMode: false,
      terrain: true,
      layerMode: 4,
      contour: false,
      contour: true,
      flyToContour: true,
   })
   viewer = publicCesiumInstance.getViewer()
   drawPolygonExample = new DrawPolygon()
   drawPolygonExample.initHandler(viewer)
   drawPolygonExample?.subscribe('getShowWaringTip', data => {
      isShowWaringTip.value = data
   })
   drawPolygonExample?.subscribe('getPolygonPositions', data => {
      curDrawPolygonData.value = data.map(item => {
         let cartographic = Cesium.Cartographic.fromCartesian(item)
         let lng = Cesium.Math.toDegrees(cartographic.longitude) // 经度
         let lat = Cesium.Math.toDegrees(cartographic.latitude) // 纬度
         return {
            lng: _.round(lng, 6),
            lat: _.round(lat, 6),
         }
      })
   })
}
watch(dialogShow, async (show) => {
   if (show) {
      await nextTick()
      cesiumContextMenu()
      initMap()
   }
})
const handleSubmit = () => {
   if (ElMessage.value) {
      ElMessage.warning('空域不支持交叉,请重新绘制或调整')
      return
   }
   if (curDrawPolygonData.value.length === 0) {
      ElMessage.warning('请绘制空域区域')
      return
   }
   if (formEle.value) {
      formEle.value.validate((valid, done, msg) => {
         if (valid) {
            let disposePolygon = curDrawPolygonData.value.map(i => [i.lng, i.lat])
            let polygon = turf.polygon([
               [
                  ...disposePolygon,
                  disposePolygon[0]
               ]
            ])
            let area = turf.area(polygon)
            emit('submitClick')
            done()
         } else {
            return false
         }
      })
   }
}
const handleClose = () => {
   isShowWaringTip.value = false
   curDrawPolygonData.value = []
   cesiumContextMenu(false)
   drawPolygonExample?.destroy()
   publicCesiumInstance?.viewerDestroy()
   publicCesiumInstance = null
   viewer = null
   drawPolygonExample = null
   form.value = {}
   formEle.value.resetForm()
   dialogShow.value = false
}
// 阻止右键默认行为
const preventDefault = event => {
   event.preventDefault()
   return
}
const cesiumContextMenu = (isAdd = true) => {
   let cesium = document.getElementById('AirspaceMap')
   if (!cesium) return
   if (isAdd) {
      cesium.addEventListener('contextmenu', preventDefault)
   } else {
      cesium.removeEventListener('contextmenu', preventDefault)
   }
}
</script>
<style scoped lang="scss">
.custom-container {
   .map-container {
      height: 440px;
      height: 620px;
      #AirspaceMap {
         position: relative;
         width: 100%;
         height: 100%;
         overflow: hidden;
      }
   }
   .btn-container {
      margin-top: 16px;
      display: flex;
      align-items: center;
      justify-content: center;
   }
}
.tool-tip {
   display: flex;
   justify-content: center;
   align-items: center;
   position: absolute;
   width: 800px;
   height: 64px;
   font-family: Source Han Sans CN, Source Han Sans CN;
   font-size: 18px;
   color: #ffffff;
   background: rgba(11, 31, 58, 0.7);
   -moz-user-select: none;
   -webkit-user-select: none;
   -ms-user-select: none;
   user-select: none;
   z-index: 9;
}
.warning {
   top: 234px;
   left: 50%;
   transform: translateX(-50%);
   color: #fff;
   background: rgba(140, 0, 0, 0.4);
   .icon {
      display: flex;
      align-items: center;
      color: #ff1f1f;
   }
}
</style>