308 lines
9.9 KiB
Vue
308 lines
9.9 KiB
Vue
<template>
|
||
<div class="h-100">
|
||
<!-- 地图组件 -->
|
||
<map-box ref="mapbox" :enableShowNofly="true" :enableGuided="true" :enableFollow="true"
|
||
:enblueScale="!$store.state.app.isWideScreen" @longPress="handleLongPress" @map-ready="onMapReady">
|
||
<template #content>
|
||
<div v-show="mapReady">
|
||
<BatteryStatus :plane="plane" />
|
||
<PlaneStatus :plane="plane" />
|
||
<ControllerTabs :plane="plane" @mapXOffset="mapXOffset" @makeRoute="makeRoute" @clearRoute="clearRoute" />
|
||
</div>
|
||
</template>
|
||
</map-box>
|
||
<!-- 弹出框 -->
|
||
<el-dialog :title="dialogTitle" :visible.sync="dialogVisible" width="320px" top="30vh" @close="closeCallback"
|
||
@open="openCallback">
|
||
<!-- 点飞设置弹出框 -->
|
||
<template v-if="dialogItem == 'guidedBox'">
|
||
<el-form label-position="left">
|
||
<el-form-item label="纬度" label-width="80px">
|
||
<el-input v-model="guidedLonLat.lat" placeholder="请输维度" label="纬度"></el-input>
|
||
</el-form-item>
|
||
<el-form-item label="经度" label-width="80px">
|
||
<el-input v-model="guidedLonLat.lon" placeholder="请输经度" label="经度"></el-input>
|
||
</el-form-item>
|
||
<el-form-item label="高度" label-width="80px">
|
||
<el-input-number v-model="guidedAlt" label="高度设置"></el-input-number>
|
||
<font class="m-l-5">米</font>
|
||
</el-form-item>
|
||
</el-form>
|
||
<span slot="footer" class="dialog-footer">
|
||
<el-button size="medium" @click="dialogVisible = false">关闭</el-button>
|
||
<el-button size="medium" type="primary" @click="() => {
|
||
if (flyToSinglePlane(guidedLonLat.lon, guidedLonLat.lat, guidedAlt)) {
|
||
isReserveGuidedMaker = true;
|
||
dialogVisible = false;
|
||
}
|
||
}">
|
||
飞至
|
||
</el-button>
|
||
</span>
|
||
</template>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import MapBox from '@/components/MapBox'
|
||
import ControllerTabs from '@/components/ControllerTabs'
|
||
import BatteryStatus from '@/components/BatteryStatus'
|
||
import PlaneStatus from '@/components/PlaneStatus'
|
||
import mqtt from '@/utils/mqtt'
|
||
import { waitForMapCanvasReady } from '@/utils'
|
||
|
||
export default {
|
||
name: 'Planes',
|
||
data () {
|
||
return {
|
||
dialogTitle: '', // 弹出框 标题
|
||
dialogItem: '', // 弹出框 项目类型判断
|
||
dialogVisible: false, // 弹出框 显隐
|
||
guidedLonLat: {}, // 点飞 的经纬度
|
||
guidedAlt: '', // 点飞的高度
|
||
isReserveGuidedMaker: false, // 关闭指点飞行操作窗口时 标记是否删除图标
|
||
planesId: this.$route.params.id,
|
||
localCount: 0, // 本地存储计数器
|
||
mapReady: false// 地图加载完成后 回调时 设置此值 让地图组件插槽内容 滞后显示
|
||
}
|
||
},
|
||
components: {
|
||
MapBox,
|
||
ControllerTabs,
|
||
BatteryStatus,
|
||
PlaneStatus
|
||
},
|
||
computed: {
|
||
plane () {
|
||
if (this.$store.state.airList.length > 0) {
|
||
return this.$store.state.airList.find(plane => plane.id === this.planesId)
|
||
}
|
||
return null
|
||
},
|
||
planeState () {
|
||
if (this.plane && this.plane.planeState) {
|
||
return this.plane.planeState
|
||
} else {
|
||
return {}
|
||
}
|
||
},
|
||
position () {
|
||
if (this.plane) {
|
||
if (this.plane.planeState.position.length > 0) {
|
||
return this.plane.planeState.position
|
||
} else {
|
||
return []
|
||
}
|
||
} else {
|
||
return []
|
||
}
|
||
},
|
||
noflyData () {
|
||
return this.$store.state.noflyData
|
||
},
|
||
ADSBList () {
|
||
return this.$store.state.ADSBList
|
||
},
|
||
/**
|
||
* @description: 侧边栏显隐
|
||
*/
|
||
isCollapse () {
|
||
return this.$store.state.app.isCollapse
|
||
}
|
||
},
|
||
methods: {
|
||
/** 弹出框 关闭事件回调 */
|
||
closeCallback () {
|
||
if (this.dialogItem === 'guidedBox' && this.isReserveGuidedMaker === false) { // 关闭点飞窗口时
|
||
this.$refs.mapbox.delGuidedMarker()// 删除所有点飞的地图标记
|
||
this.$refs.mapbox.clearMapElements(['guided-lines-layer', 'guided-center-point-layer'], ['guided-lines-source'])// 删除引导线
|
||
}
|
||
this.dialogVisible = false
|
||
this.dialogItem = ''
|
||
},
|
||
/** 弹出框 打开事件回调 */
|
||
openCallback () {
|
||
},
|
||
// 地图长按事件 记录地图经纬度
|
||
handleLongPress (lonLat) {
|
||
this.isReserveGuidedMaker = false
|
||
this.dialogTitle = '指点飞行'
|
||
this.dialogVisible = true
|
||
this.dialogItem = 'guidedBox'
|
||
this.guidedLonLat = lonLat // 设置点击的经纬度
|
||
|
||
// 安全获取当前飞机位置和高度
|
||
let center = { lon: 0, lat: 0 }
|
||
let height = 0
|
||
if (this.plane?.planeState?.position?.length > 0) {
|
||
const lastPos = this.plane.planeState.position.at(-1)
|
||
if (Array.isArray(lastPos)) {
|
||
center = { lon: lastPos[0], lat: lastPos[1] }
|
||
height = lastPos[2] || 0
|
||
}
|
||
}
|
||
this.guidedAlt = height
|
||
|
||
// ✅ 构造单架飞机的位置数组
|
||
const planeCoords = [[center.lon, center.lat]]
|
||
|
||
// ✅ 复用现有方法绘制引导线
|
||
this.$refs.mapbox.drawGuidedLines({
|
||
centerPoint: { lng: center.lon, lat: center.lat },
|
||
endPoint: { lng: lonLat.lon, lat: lonLat.lat },
|
||
planeCoords
|
||
})
|
||
},
|
||
// 单架飞机点飞指令,参数:lon, lat, alt
|
||
flyToSinglePlane (lon, lat, alt) {
|
||
const lonNum = Number(lon)
|
||
const latNum = Number(lat)
|
||
const altNum = Number(alt)
|
||
|
||
if (
|
||
isNaN(lonNum) || isNaN(latNum) || isNaN(altNum) ||
|
||
lonNum < -180 || lonNum > 180 ||
|
||
latNum < -90 || latNum > 90
|
||
) {
|
||
this.$message.warning('请输入有效的经纬度(经度-180~180,纬度-90~90)和高度')
|
||
return false
|
||
}
|
||
|
||
const cmd = `{guidedMode:{lon:${lonNum.toFixed(7)},lat:${latNum.toFixed(7)},alt:${altNum.toFixed(1)}}}`
|
||
this.publishFun(cmd)
|
||
return true
|
||
},
|
||
// 地图组件回调地图加载完成后 执行
|
||
onMapReady () {
|
||
this.mapReady = true// 标记地图加载完成
|
||
this.makePlane(this.plane)
|
||
},
|
||
/**
|
||
* @description: 创建飞机图标
|
||
*/
|
||
makePlane (plane) {
|
||
let planeDefaultLonLat
|
||
if (localStorage.getItem(plane.name)) { // 从本地缓存 拿飞机得初始位置
|
||
planeDefaultLonLat = JSON.parse(localStorage.getItem(plane.name))
|
||
plane.lon = planeDefaultLonLat.lon
|
||
plane.lat = planeDefaultLonLat.lat
|
||
} else {
|
||
// 缺省 给经纬度 小数点后随机第五位 即有个10米左右的随机距离
|
||
plane.lon = 100 + Number((Math.random() * 0.01).toFixed(5))
|
||
plane.lat = 40 + Number((Math.random() * 0.01).toFixed(5))
|
||
}
|
||
this.$refs.mapbox.removePlanes()// 先清除画布上现有的飞机
|
||
this.$refs.mapbox.makePlane(plane)// 创建飞机
|
||
this.$refs.mapbox.goto({ lon: plane.lon, lat: plane.lat })// 跳转到飞机位置
|
||
},
|
||
/**
|
||
* @description: 创建航线
|
||
*/
|
||
makeRoute (routeData) {
|
||
this.$refs.mapbox.makeRoute(routeData)
|
||
},
|
||
/**
|
||
* @description: 清楚航线
|
||
*/
|
||
clearRoute () {
|
||
this.$refs.mapbox.clearRoute()
|
||
},
|
||
/**
|
||
* @description: 屏幕横移
|
||
* @param {*} val 正数向左移动 负数向右移动
|
||
* @param {*} y 正数向上移动 负数向下移动
|
||
*/
|
||
mapXOffset (x, y) {
|
||
this.$refs.mapbox.mapXOffset(x, y)
|
||
},
|
||
/**
|
||
* @description: 发布 mqtt 信息
|
||
* @param {*} jsonData {'item':val} // item: questAss飞行航点任务 setQuestState 设置飞机状态 resetQuestState设置飞机初始状态 chan1油门通道1 chan2油门通道2 chan3油门通道3 chan4油门通道4 hookConteroller钩子控制 cameraController云台相机控制
|
||
*/
|
||
publishFun (jsonData) {
|
||
if (this.plane) {
|
||
mqtt.publishFun(`cmd/${this.plane.macadd}`, jsonData)
|
||
} else {
|
||
this.$message.warning('与飞机通信未接通,请稍后')
|
||
}
|
||
}
|
||
},
|
||
mounted () {
|
||
},
|
||
watch: {
|
||
// 这里不要deep:true 会一直新创飞机图标
|
||
plane: {
|
||
async handler () {
|
||
try {
|
||
// 等待地图画布准备好
|
||
await waitForMapCanvasReady(this.$refs.mapbox.map)
|
||
// 画布准备好后执行你自己的逻辑
|
||
this.onMapReady()
|
||
console.warn('飞机数据更新', this.plane)
|
||
} catch (err) {
|
||
console.debug('等待地图画布准备超时', err)
|
||
}
|
||
},
|
||
immediate: true
|
||
},
|
||
// 实时更新飞机状态 到 到飞机弹出框
|
||
planeState: {
|
||
handler (val) {
|
||
if (val) {
|
||
val.name = this.plane.name // 保留飞机名称
|
||
// 调用更新 popup 内容方法
|
||
this.$refs.mapbox.updatePopupContent(val)
|
||
}
|
||
},
|
||
deep: true
|
||
},
|
||
/**
|
||
* @description: 更新飞机位置 并画出轨迹 跟随飞机
|
||
*/
|
||
position: {
|
||
handler (val) {
|
||
const len = val.length
|
||
if (len > 2) {
|
||
const lon = val[len - 1][0]
|
||
const lat = val[len - 1][1]
|
||
this.localCount++// 计数器 叠加
|
||
if (this.localCount % 100 === 1) {
|
||
localStorage.setItem(this.plane.name, `{ "lon": ${lon}, "lat": ${lat} }`)
|
||
}
|
||
this.$refs.mapbox.setPlaneLonLat({ lon: lon, lat: lat }, 0, val)// 更新飞机位置 并画出轨迹 跟随飞机
|
||
}
|
||
},
|
||
deep: true
|
||
},
|
||
ADSBList: {
|
||
handler (newList) {
|
||
if (this.$refs.mapbox && typeof this.$refs.mapbox.makeADSBPlanes === 'function') {
|
||
this.$refs.mapbox.makeADSBPlanes(newList)
|
||
}
|
||
},
|
||
immediate: true,
|
||
deep: true
|
||
},
|
||
/**
|
||
* @description: 侧边栏缩进有变化时 地图重新自适应
|
||
*/
|
||
isCollapse: {
|
||
handler (val) {
|
||
if (val) {
|
||
setTimeout(() => {
|
||
this.$nextTick(() => {
|
||
this.$refs.mapbox.handleResize()
|
||
})
|
||
}, 500)
|
||
}
|
||
},
|
||
deep: true
|
||
}
|
||
}
|
||
}
|
||
|
||
</script>
|
||
|
||
<style lang="scss" scoped></style>
|