【类 型】:fix

【原  因】:1修正 地图组件载入 内部函数调用过早 导致报错  2.完善地图源地址 从 后端数据库拿数据
【过  程】:
【影  响】:

# 类型 包含:
# feat:新功能(feature)
# fix:修补bug
# docs:文档(documentation)
# style: 格式(不影响代码运行的变动)
# refactor:重构(即不是新增功能,也不是修改bug的代码变动)
# test:增加测试
# chore:构建过程或辅助工具的变动
This commit is contained in:
szdot 2025-06-22 06:06:18 +08:00
parent 03ee669804
commit bc6bf29da2
6 changed files with 147 additions and 81 deletions

View File

@ -23,8 +23,7 @@ export default {
takeoffLonLats: [], // 线
isflow: false, //
currentStyleIndex: 0, //
guidedMarker: null, //
mapStyles: this.$store.state.mapStyleList//
guidedMarker: null //
}
},
props: {
@ -79,53 +78,63 @@ export default {
},
defaultZoom () {
return this.$store.getters['app/getDefaultZoom']
},
mapStyleList () {
return this.$store.state.mapStyleList
}
},
mounted () {
this.init().then(() => {
//
this.map.on('load', () => {
this.$emit('map-ready') //
/* 更新样式,添加自定义 sprite */
watch: {
},
async mounted () {
//
await this.$store.dispatch('fetchMapStyleList', this.$store.state.settings.host)
//
await this.init()
//
this.map.setFog({
range: [1.0, 8.0],
color: 'white',
'horizon-blend': 0.01
})
// // 线
// this.map.addSource('contours', {
// type: 'vector',
// url: 'mapbox://mapbox.mapbox-terrain-v2'
// })
// this.map.addLayer({
// id: 'contours',
// type: 'line',
// source: 'contours',
// 'source-layer': 'contour',
// layout: {
// 'line-join': 'round',
// 'line-cap': 'round'
// },
// paint: {
// 'line-color': '#ff69b4',// 线
// 'line-width': 1 // 线
// }
// })
// // 3D
// this.map.addSource('mapbox-dem', {
// type: 'raster-dem',
// url: 'mapbox://mapbox.mapbox-terrain-dem-v1',
// tileSize: 512,
// maxzoom: 14
// })
// this.map.setTerrain({ source: 'mapbox-dem', exaggeration: 1.5 })
//
this.map.on('load', () => {
/* 更新样式,添加自定义 sprite */
//
this.map.setFog({
range: [1.0, 8.0],
color: 'white',
'horizon-blend': 0.01
})
// // 线
// this.map.addSource('contours', {
// type: 'vector',
// url: 'mapbox://mapbox.mapbox-terrain-v2'
// })
// this.map.addLayer({
// id: 'contours',
// type: 'line',
// source: 'contours',
// 'source-layer': 'contour',
// layout: {
// 'line-join': 'round',
// 'line-cap': 'round'
// },
// paint: {
// 'line-color': '#ff69b4',// 线
// 'line-width': 1 // 线
// }
// })
// // 3D
// this.map.addSource('mapbox-dem', {
// type: 'raster-dem',
// url: 'mapbox://mapbox.mapbox-terrain-dem-v1',
// tileSize: 512,
// maxzoom: 14
// })
// this.map.setTerrain({ source: 'mapbox-dem', exaggeration: 1.5 })
// (on 'load')
this.$emit('map-ready')
//
if (this.enableGuided) {
// longPress
// longPress
let pressTimer = null
let isLongPress = false //
let startPoint = null //
@ -186,7 +195,7 @@ export default {
// map
this.map = new mapboxgl.Map({
container: 'map',
style: this.mapStyles[0],
style: this.mapStyleList[0],
center: this.defaultLonlat,
zoom: this.defaultZoom,
pitch: 0,
@ -211,7 +220,7 @@ export default {
//
//
if (this.enableSwitch) {
const styleSwitcherControl = new MapboxStyleSwitcherControl(this.mapStyles, 'iconfont icon-duozhang f-s-20', this.currentStyleIndex)
const styleSwitcherControl = new MapboxStyleSwitcherControl(this.mapStyleList, 'iconfont icon-duozhang f-s-20', this.currentStyleIndex)
this.map.addControl(styleSwitcherControl, 'top-right')
}
@ -871,8 +880,6 @@ export default {
this.$store.commit('app/setDefaultLonLat', this.map.getCenter())
this.$store.commit('app/setDefaultZoom', this.map.getZoom())
}
},
watch: {
}
}
@ -899,5 +906,4 @@ export default {
.adsb-icon {
will-change: transform;
}
</style>

View File

@ -26,7 +26,6 @@ const store = new Vuex.Store({
logList: [], // 操作日志列表
messageList: [], // 管理员公告列表
mapStyleList: [], // 地图样式列表
mapIsStyleReady: false, // 地图样式是否加载完成 在全局入口判断设置 子页面调用地图组件时直接判断该值
crosFrequency: null, // 对频macadd
ADSBList: [] // 存放当前活跃的 ADSB 飞机数据
},
@ -155,13 +154,6 @@ const store = new Vuex.Store({
setMapStyleList (state, list) {
state.mapStyleList = list
},
/**
* @description: 确认地图源地址 加载完成
* @param {bool} status 是否完成
*/
setMapIsStyleReady (state, status) {
state.mapIsStyleReady = status
},
/**
* @description: 清除过期的 ADSB 数据
*/
@ -1209,7 +1201,6 @@ const store = new Vuex.Store({
}
})
commit('setMapStyleList', list)
commit('setMapIsStyleReady', true)
} else {
commit('setMapStyleList', [])
Message.error(res.data.msg || '地图样式获取失败')

View File

@ -158,3 +158,37 @@ export function formatPrice (value) {
}
return formattedPrice
}
/**
* 等待 Mapbox 地图的画布容器Canvas Container准备就绪
* 因为 Mapbox 初始化是异步的某些操作需要等待画布容器加载完成才能执行
*
* @param {Object} map - Mapbox GL JS 地图实例对象
* @param {number} maxRetry - 最大重试次数防止无限等待默认5次
* @returns {Promise<HTMLElement>} 返回一个 Promise成功时返回画布容器元素失败时抛出错误
*/
export function waitForMapCanvasReady (map, maxRetry = 5) {
return new Promise((resolve, reject) => {
/**
* 递归检查函数使用 requestAnimationFrame 尽可能快地检测画布容器是否可用
* @param {number} retry 剩余重试次数
*/
const check = (retry) => {
// 尝试获取画布容器元素
const container = map?.getCanvasContainer?.()
// 如果获取成功,表示地图画布已准备好
if (container) {
resolve(container) // Promise 成功返回画布容器
} else if (retry > 0) { // 如果还没准备好且重试次数未用尽,继续异步下一帧检测
// requestAnimationFrame 在浏览器每一帧时执行回调,比 setTimeout 更高效和精准
requestAnimationFrame(() => check(retry - 1))
} else { // 超过最大重试次数,拒绝 Promise避免无限等待
reject(new Error('Map canvas container is not ready after multiple retries'))
}
}
// 启动首次检测,带上最大重试次数参数
check(maxRetry)
})
}

View File

@ -1,21 +1,25 @@
<template>
<div class="h-100">
<map-box ref="mapbox" v-if="$store.state.mapIsStyleReady">
<!-- <template #content>
<Statistics :plane="plane" />
</template> -->
<map-box ref="mapbox" @map-ready="onMapReady">
<template #content>
<div v-show="mapReady">
<!-- <Statistics :plane="plane" /> -->
</div>
</template>
</map-box>
</div>
</template>
<script>
import MapBox from '@/components/MapBox'
import { waitForMapCanvasReady } from '@/utils'
// import Statistics from '@/components/Statistics'
export default {
name: 'Home',
data () {
return {
mapReady: false//
}
},
components: {
@ -34,6 +38,11 @@ export default {
}
},
methods: {
//
onMapReady () {
this.mapReady = true //
this.makePlanes(this.airList)
},
/**
* @description: 创建飞机图标
*/
@ -46,8 +55,9 @@ export default {
plane.lon = planeDefaultLonLat.lon
plane.lat = planeDefaultLonLat.lat
} else {
plane.lon = 0
plane.lat = 0
// 10
plane.lon = 100 + Number((Math.random() * 0.01).toFixed(5))
plane.lat = 40 + Number((Math.random() * 0.01).toFixed(5))
}
this.$refs.mapbox.makePlane(plane, index)
})
@ -56,13 +66,21 @@ export default {
mounted () {
},
watch: {
/**
* @description: 飞机列表更新时候 更新地图
*/
airList: {
handler (val) {
if (val && val.length > 0) {
this.makePlanes(val)
async handler () {
try {
//
await waitForMapCanvasReady(this.map)
//
this.onMapReady()
} catch (err) {
console.debug('等待地图画布准备超时', err)
}
},
immediate: true //
immediate: true
},
/**
* @description: 侧边栏显隐

View File

@ -1,12 +1,14 @@
<template>
<div class="h-100">
<!-- 地图组件 -->
<map-box ref="mapbox" :enableShowNofly="true" :enableGuided="true" :enableFollow="true" :enblueScale="!$store.state.app.isWideScreen"
@longPress="handleLongPress">
<map-box ref="mapbox" :enableShowNofly="true" :enableGuided="true" :enableFollow="true"
:enblueScale="!$store.state.app.isWideScreen" @longPress="handleLongPress" @map-ready="onMapReady">
<template #content>
<BatteryStatus :plane="plane" />
<PlaneStatus :plane="plane" />
<ControllerTabs :plane="plane" @mapXOffset="mapXOffset" @makeRoute="makeRoute" @clearRoute="clearRoute" />
<div v-show="mapReady">
<BatteryStatus :plane="plane" />
<PlaneStatus :plane="plane" />
<ControllerTabs :plane="plane" @mapXOffset="mapXOffset" @makeRoute="makeRoute" @clearRoute="clearRoute" />
</div>
</template>
</map-box>
<!-- 弹出框 -->
@ -42,6 +44,7 @@ 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',
@ -54,7 +57,8 @@ export default {
guidedAlt: '', //
isReserveGuidedMaker: false, //
planesId: this.$route.params.id,
localCount: 0 //
localCount: 0, //
mapReady: false//
}
},
components: {
@ -116,6 +120,11 @@ export default {
const posLen = this.plane.planeState.position.length
this.guidedAlt = this.plane.planeState.position[posLen - 1][2]//
},
//
onMapReady () {
this.mapReady = true//
this.makePlane(this.plane)
},
/**
* @description: 创建飞机图标
*/
@ -126,8 +135,9 @@ export default {
plane.lon = planeDefaultLonLat.lon
plane.lat = planeDefaultLonLat.lat
} else {
plane.lon = 100
plane.lat = 40
// 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)//
@ -166,13 +176,20 @@ export default {
}
},
mounted () {
if (this.plane) {
this.makePlane(this.plane) //
}
},
watch: {
plane (val) {
if (val) this.makePlane(val)//
plane: {
async handler () {
try {
//
await waitForMapCanvasReady(this.map)
//
this.onMapReady()
} catch (err) {
console.debug('等待地图画布准备超时', err)
}
},
immediate: true
},
/**
* @description: 更新飞机位置 并画出轨迹 跟随飞机

View File

@ -122,13 +122,13 @@ export default {
this.$store.dispatch('fetchSpuList'),
this.$store.dispatch('fetchSkuList'),
this.$store.dispatch('fetchPaidOrderList'),
this.$store.dispatch('fetchMessageList'),
this.$store.dispatch('fetchMapStyleList', this.$store.state.settings.host)
this.$store.dispatch('fetchMessageList')
]).then(() => {
return this.$store.dispatch('fetchNoflyData', this.shop_id)
})
},
watch: {
/**
* @description: 异步拿到飞机列表之后 再进行一些初始化操作
* @param {*} res 飞机列表