food/src/views/layout/components/main/planes/index.vue
oldHome bb032a81c2 fix 1解决地图组件因为menu缩进按钮 造成的空白问题 包括所有使用地图组件的模块
2打印日志前 带上飞机名字前缀
3集群指点飞行 判断最矮飞机目标高度是不是小于0 即地面以下 打断 不发送mqtt命令
2025-06-28 16:48:48 +08:00

308 lines
9.9 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>