<template>
|
<el-dialog class="airspace-dialog" :title="props.title" v-model="dialogShow" width="60%" align-center
|
@close="handleClose" :close-on-click-modal="false" :close-on-press-escape="false">
|
<div class="custom-container">
|
<div class="form-container">
|
<avue-form ref="formEle" :option="option" v-model="form"></avue-form>
|
</div>
|
|
<div class="map-container" v-loading="mapLoading" element-loading-text="地形初始化中,请稍后..."
|
element-loading-background="rgba(0, 0, 0, 0.7)">
|
<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 { getPointPositionsHeight } from '@/utils/cesium/mapUtil'
|
import { DrawPolygon } from '@/utils/drawPolygon/drawPolygon'
|
import { PublicCesium } from '@/utils/cesium/publicCesium'
|
import { ElMessageBox, ElMessage } from 'element-plus'
|
|
import {
|
airspaceEnteringAdd,
|
airspaceEnteringUpdate
|
} from '@/api/airspace/airspace'
|
|
|
const props = defineProps({
|
title: {
|
type: String,
|
default: '新增空域',
|
},
|
|
type: {
|
type: String,
|
default: 'add',
|
},
|
|
curEditRow: {
|
type: Object,
|
default: () => { },
|
}
|
})
|
|
const emit = defineEmits(['submitClick'])
|
|
const dialogShow = defineModel('show')
|
|
const formEle = ref(null)
|
const form = ref({
|
height: 120,
|
})
|
const option = ref({
|
submitBtn: false,
|
emptyBtn: false,
|
menuBtn: false,
|
column: [
|
{
|
label: '名称',
|
prop: 'name',
|
type: 'input',
|
rules: [
|
{
|
required: true,
|
message: '请输入名称',
|
trigger: 'blur'
|
}
|
]
|
},
|
|
{
|
label: '高度',
|
prop: 'height',
|
type: 'input',
|
value: 120,
|
min: 5,
|
max: 500,
|
type: 'number',
|
change ({ column, value }) {
|
drawPolygonExample?.setPolygonHeight?.(value)
|
},
|
rules: [
|
{
|
required: true,
|
message: '请输入高度',
|
trigger: 'blur'
|
},
|
{
|
pattern: /^(?:[5-9]|[1-9]\d|[1-4]\d{2}|500)$/, // 5-500
|
message: '高度需在5-500之间',
|
trigger: ['blur', 'change']
|
}
|
]
|
},
|
|
{
|
label: '类型',
|
prop: 'flight_type',
|
type: 'select',
|
dicUrl: `/drone-device-core/airspace/page?current=1&size=10000`,
|
props: {
|
label: 'type_name',
|
value: 'id',
|
},
|
dicFormatter (res) {
|
return res.data.records
|
},
|
rules: [
|
{
|
required: true,
|
message: '请选择类型',
|
trigger: 'blur'
|
}
|
]
|
},
|
{
|
label: '管控时段',
|
prop: 'time_period',
|
type: 'daterange',
|
format: 'YYYY-MM-DD',
|
valueFormat: 'YYYY-MM-DD',
|
startPlaceholder: '开始日期',
|
endPlaceholder: '结束日期',
|
rules: [
|
{
|
required: true,
|
message: '请选择管控时段',
|
trigger: 'blur'
|
}
|
]
|
},
|
|
{
|
label: '管控原因',
|
prop: 'remark',
|
type: 'textarea',
|
span: 24,
|
rows: 1,
|
minRows: 1,
|
maxRows: 1,
|
placeholder: '请输入管控原因',
|
},
|
],
|
})
|
|
let drawPolygonExample = null
|
let publicCesiumInstance = null
|
let viewer = null
|
// 地图
|
const mapLoading = ref(true)
|
|
const isShowWaringTip = ref(false)
|
|
const curDrawPolygonData = ref([])
|
|
const initMap = async () => {
|
if (!document.getElementById('AirspaceMap')) {
|
return
|
}
|
publicCesiumInstance = new PublicCesium({
|
dom: 'AirspaceMap',
|
flatMode: false,
|
terrain: true,
|
layerMode: 4,
|
contour: true,
|
flyToContour: true,
|
terrainInit () {
|
setTimeout(async () => {
|
initDrawPolygon()
|
mapLoading.value = false
|
}, 1500)
|
}
|
})
|
|
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) // 纬度
|
let height = cartographic.height
|
|
return {
|
lng: _.round(lng, 6),
|
lat: _.round(lat, 6),
|
height
|
}
|
})
|
})
|
}
|
|
watch(dialogShow, async (show) => {
|
if (show) {
|
if (props.type === 'edit') {
|
form.value = { ...props.curEditRow }
|
}
|
|
initDrawPolygon()
|
|
if (viewer) return
|
|
await nextTick()
|
cesiumContextMenu()
|
initMap()
|
}
|
})
|
|
const initDrawPolygon = async () => {
|
if (props.type === 'edit' && drawPolygonExample) {
|
drawPolygonExample?.setPolygonHeight?.(props.curEditRow.height)
|
|
let polygon = JSON.parse(props.curEditRow.flight_range)
|
|
if (polygon.length > 0) {
|
const aslData = await getPointPositionsHeight(polygon, viewer)
|
drawPolygonExample?.initPolygon(viewer, aslData.map(item => ({ ...item, height: item.ASL })))
|
}
|
}
|
}
|
|
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)
|
|
if (props.type === 'edit') {
|
airspaceEnteringUpdate({
|
id: props.curEditRow.id,
|
name: form.value.name,
|
flight_range: JSON.stringify(curDrawPolygonData.value),
|
height: form.value.height,
|
time_period: form.value.time_period.join('~'),
|
flight_type: form.value.flight_type,
|
remark: form.value.remark,
|
area: area
|
}).then(res => {
|
ElMessage.success('更新成功')
|
dialogShow.value = false
|
emit('submitClick')
|
done()
|
})
|
} else {
|
airspaceEnteringAdd({
|
name: form.value.name,
|
flight_range: JSON.stringify(curDrawPolygonData.value),
|
height: form.value.height,
|
time_period: form.value.time_period.join('~'),
|
flight_type: form.value.flight_type,
|
remark: form.value.remark,
|
area: area
|
}).then(res => {
|
ElMessage.success('新增成功')
|
dialogShow.value = false
|
emit('submitClick')
|
done()
|
})
|
}
|
} else {
|
return false
|
}
|
})
|
}
|
}
|
|
const handleClose = () => {
|
drawPolygonExample?.delPolygon()
|
isShowWaringTip.value = false
|
curDrawPolygonData.value = []
|
form.value = {
|
height: 120
|
}
|
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)
|
}
|
}
|
|
onBeforeUnmount(() => {
|
drawPolygonExample?.destroy()
|
drawPolygonExample = null
|
cesiumContextMenu(false)
|
publicCesiumInstance?.viewerDestroy()
|
publicCesiumInstance = null
|
viewer = null
|
})
|
</script>
|
|
<style scoped lang="scss">
|
.custom-container {
|
.map-container {
|
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>
|