<template>
|
<div class="drone-pilot-details" :style="{ width: screenWidth,height: screenHeight}">
|
<van-nav-bar
|
title="详情"
|
left-arrow
|
@click-left="onClickLogin"
|
/>
|
<div id="landscapeBox" class="landscape-box" :style="{ width: boxWidth + 'px', height: boxHeight + 'px'}">
|
<div id="videoModule" class="l-video" :style="{ width: screenWidthVideo + 'px', height: (screenHeightVideo) + 'px'}">
|
<video v-show="videoUrl" ref="videoPlayerBig" :style="{ width: screenWidthVideo + 'px', height: screenHeightVideo + 'px'}" controls autoplay muted playsinline style="text-align: left; object-fit: fill">
|
Your browser is too old which doesn't support HTML5 video.
|
</video>
|
<el-empty v-show="videoUrl == ''" description="当前设备已关机,无法进行直播" :image="imageUrl"></el-empty>
|
<div v-if="videoUrl" class="center-point"></div>
|
<div class="right-box">
|
<!-- <ptzControl :sn="sn" :osdVisible="sbInfo"/> -->
|
<!-- <controlConsole :sn="sn" :osdVisible="sbInfo" :cesiumViewe="globalViewer" /> -->
|
</div>
|
</div>
|
<div class="l-map" :style="{ width: screenWidthVideo + 'px', height: (screenHeightVideo) + 'px'}">
|
<div id="cesiumContainerBigMap"></div>
|
<div class="l-zp" id="lZp">
|
<comPass :cesiumViewe="globalViewer"/>
|
</div>
|
<div class="l-zoom" id="lZoom">
|
<div class="zoom">
|
<img src="@/assets/images/me.png" @touchstart="clickPhoneLocation" />
|
</div>
|
<div class="zoom" @touchstart="onChangeD">
|
<img v-if="is2d" src="@/assets/images/stand.png" />
|
<img v-else src="@/assets/images/satellite.png" />
|
</div>
|
<div class="zoom">
|
<img v-if="!isBigMap" src="@/assets/images/fang.png" @touchstart="changeModelMap" />
|
<img v-else src="@/assets/images/suo.png" @touchstart="changeModelMap" />
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
</template>
|
|
<script lang="ts" setup>
|
import { ref, onMounted, onBeforeUnmount, nextTick, reactive, computed, watch } from 'vue';
|
import { EDeviceTypeName, EHmsLevel } from '@/types/enums'
|
import { getRoot } from '../../root'
|
import { getWaylineFile, getLiveVideoUrl,getTokenByCode } from '@/api/manage'
|
import { CURRENT_CONFIG as config } from '@/api/http/config';
|
import { getLnglatDist, cartesian3Convert, getLnglatAltitude, createCircleBillboard } from '@/utils/cesium/mapUtil';
|
import cesiumOperation from '@/utils/cesium-tsa.js';
|
const { _init, viewerDestory } = cesiumOperation();
|
import { useMyStore } from '@/store'
|
import comPass from './components/comPass.vue'
|
import ptzControl from './components/ptzControl.vue'
|
import controlConsole from './components/controlConsole.vue'
|
import { useConnectWebSocket } from '@/utils/websocket/connect-websocket'
|
import { getWebsocketUrl } from '@/websocket/util/config'
|
import { EBizCode, ELocalStorageKey, ERouterName } from '@/types'
|
// import VConsole from 'vconsole';
|
import { analyzeKmzFile, XMLToJSON } from '@/utils/cesium/kmz.js'
|
import { initPointWayLine } from './hooks/initPointWayline'
|
import { initPlanarWayline } from './hooks/initPlanarWayline'
|
import { droneFly } from './hooks/droneFly'
|
const { pointWayline } = initPointWayLine()
|
const { planarWayline } = initPlanarWayline()
|
const { initDock, getPhoneLocation, clickPhoneLocation } = droneFly()
|
|
const {
|
removeAllPoint,
|
removeAllDataSource,
|
globalCesium,
|
getEntityById,
|
removeById,
|
loadLAYER,
|
} = cesiumOperation()
|
|
// const vConsole = new VConsole();
|
|
let globalViewer = null
|
|
// 使用 ref 来存储屏幕宽度和高度
|
const screenWidth = ref(window.innerWidth);
|
const screenHeight = ref(window.innerHeight);
|
// 加了头部
|
const boxWidth = ref(window.innerWidth);
|
const boxHeight = ref(window.innerHeight - 46);
|
// 地图切换
|
const screenWidthMap = ref(boxWidth.value)// ref(((boxHeight.value)/2)*2)
|
const screenHeightMap = ref(((boxHeight.value)/2));
|
// 视频切换
|
const screenWidthVideo = ref(boxWidth.value);
|
const screenHeightVideo = ref(boxHeight.value/2);
|
const isBigMap = ref(false)
|
|
// 显示高德地图矢量还是影像(2D/3D)
|
let is2d = ref(true)
|
|
const onChangeD = () => {
|
is2d.value = !is2d.value
|
store.commit('SET_MAP_SETTING_MODE', is2d.value?2:3);
|
loadLAYER();
|
}
|
|
const store = useMyStore()
|
// 获取路由
|
const root = getRoot()
|
|
interface deviceInfo {
|
dockSn: string;
|
latitude?:number;// 机场经纬度
|
longitude?:number;
|
height?:number; // 机场高度
|
isOnline: boolean;
|
name: string; // 可选字段
|
sn: string;
|
workspaceId:string;
|
children?: { };
|
}
|
// 取值
|
let sbInfo: deviceInfo = {} as deviceInfo;
|
// 无人机sn
|
let sn = ref<String>('')
|
// 机场sn
|
let dockSn = ref<String>('')
|
// workspaceId
|
let workspaceId = ref<String>('')
|
// 获取实时航线
|
const getWrjSsLx = async () => {
|
if (window.cesiumViewer== null) return
|
console.log('哒哒哒1111',sbInfo)
|
await getWaylineFile(sbInfo.sn).then((res:any) => {
|
if (res.code != 200) return
|
ssLinePath.value = res.data
|
// globalViewer = window.cesiumViewer
|
// 先有航线,再飞行
|
generateCourse()
|
})
|
}
|
const getTokenAndTbInfo = () => {
|
getTokenByCode(root.$route.query.code).then((res:any) => {
|
let tokenAndInfo = JSON.parse(res.data);
|
sbInfo = JSON.parse(tokenAndInfo.sbInfo);
|
store.commit('SET_TOKEN', tokenAndInfo.token);
|
sn.value = sbInfo.sn;
|
dockSn.value = sbInfo.dockSn;
|
workspaceId.value = sbInfo.workspaceId
|
// 监听ws
|
const messageHandler = async (payload: any) => {
|
if (!payload) return
|
switch (payload.biz_code) {
|
case EBizCode.GatewayOsd: { // 遥控器
|
store.commit('SET_GATEWAY_INFO', payload.data)
|
break
|
}
|
case EBizCode.DeviceOsd: { // 无人机的
|
store.commit('SET_DEVICE_INFO', payload.data)
|
store.commit('SET_WS_MESSAGE', payload)
|
getviewDrone(payload)
|
break
|
}
|
case EBizCode.DockOsd: { // 机场
|
store.commit('SET_DOCK_INFO', payload.data)
|
break
|
}
|
case EBizCode.DeviceOffline: {
|
store.commit('SET_DEVICE_OFFLINE', payload.data)
|
if (!payload.data.online_status) {
|
videoUrl.value = ''
|
}
|
break
|
}
|
case EBizCode.FlightTaskProgress: { // 获取进度
|
// 如果点击列表进入没有获取到航线文件,那么这里重新请求一次
|
if (!ssLinePath || !ssLinePath.value) {
|
getWrjSsLx()
|
}
|
break
|
}
|
}
|
}
|
|
const webSorketUrl = getWebsocketUrl() + '&workspace-id=' + workspaceId.value
|
// 监听ws 消息
|
useConnectWebSocket(messageHandler, webSorketUrl)
|
// 获取视频信息
|
loadVideo();
|
// 获取航线信息
|
getWrjSsLx();
|
})
|
}
|
getTokenAndTbInfo()
|
console.log('哒哒哒',sbInfo)
|
|
|
// 记录横竖屏模式
|
const isLandscape = ref(false);
|
|
// 记录视频地址
|
const videoUrl = ref<String>('');
|
|
// 设置图片地址
|
const imageUrl = new URL('@/assets/images/norecord.png', import.meta.url).href
|
|
const imageMapSrc = new URL('@/assets/images/mapdock.png', import.meta.url).href
|
|
const jgbMapSrc = new URL('@/assets/images/jgb.png', import.meta.url).href
|
|
const mapswitching = new URL('@/assets/images/mapswitching.png', import.meta.url).href
|
|
const videoWH = ref<any>(null)
|
|
// 视频宽高
|
const drawCanvasWidth = ref<Number>(0)
|
const drawCanvasHeight = ref<Number>(0)
|
|
const videoPlayerBig = ref<any>(null); // 视频播放器实例
|
let webrtcPlayerBig = null
|
|
let player = null; // video.js 播放器
|
|
// 无人机实时飞行链接
|
let ssLinePath = ref<String>(null)
|
|
// 实时定位
|
let viewDroneInfo = {}
|
// 机场高度
|
let droneHeight = ref(0)
|
|
// 监听
|
let connectWs = ref<any>(null)
|
|
// RTCWEB 加载视频
|
const playBig = () => {
|
webrtcPlayerBig = new window.ZLMRTCClient.Endpoint({
|
element: videoPlayerBig.value, // video 标签
|
debug: true, // 是否打印日志
|
zlmsdpUrl: videoUrl.value, //流地址
|
simulecast: false,
|
useCamera: false,
|
audioEnable: true,
|
videoEnable: true,
|
recvOnly: true,
|
usedatachannel: false,
|
})
|
webrtcPlayerBig.on(
|
window.ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR,
|
(e) => {
|
// ICE 协商出错
|
console.error('ICE 协商出错')
|
// this.eventcallbacK('ICE ERROR', 'ICE 协商出错')
|
},
|
)
|
|
webrtcPlayerBig.on(
|
window.ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS,
|
(e) => {
|
//获取到了远端流,可以播放
|
console.log('播放成功', e.streams)
|
// this.eventcallbacK('playing', '播放成功')
|
},
|
)
|
|
webrtcPlayerBig.on(
|
window.ZLMRTCClient.Events.WEBRTC_OFFER_ANWSER_EXCHANGE_FAILED,
|
(e) => {
|
// offer anwser 交换失败
|
console.error('offer anwser 交换失败', e)
|
// this.eventcallbacK('OFFER ANSWER ERROR ', 'offer anwser 交换失败')
|
if (e.code == -400 && e.msg == '流不存在') {
|
console.log('流不存在')
|
timerBig = setTimeout(() => {
|
webrtcPlayerBig.close()
|
playBig()
|
}, 100)
|
}
|
},
|
)
|
|
webrtcPlayerBig.on(
|
window.ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM,
|
(s) => {
|
// 获取到了本地流
|
|
// document.getElementById('selfVideo').srcObject=s;
|
// this.eventcallbacK('LOCAL STREAM', '获取到了本地流')
|
},
|
)
|
}
|
|
|
// 加载该设备的视频信息
|
const loadVideo = async () => {
|
await getLiveVideoUrl(sn.value).then((res) => {
|
if (res.data.code != 0) return
|
// 获取高度
|
if (videoWH.value) {
|
drawCanvasWidth.value = videoWH.value.offsetWidth;
|
drawCanvasHeight.value = videoWH.value.offsetHeight;
|
}
|
videoUrl.value = res.data.data.rtcs_url
|
// 播放
|
playBig()
|
})
|
}
|
|
|
// 生成航线轨迹
|
const generateCourse = async () => {
|
if (!ssLinePath.value) return
|
const { fileInfoObj } = await analyzeKmzFile(`${ssLinePath.value}?_t=${new Date().getTime()}`)
|
const xmlStr = await fileInfoObj['wpmz/template.kml']
|
const xmlJson = XMLToJSON(xmlStr)?.['Document']
|
const placemark = xmlJson.Folder.Placemark
|
if (placemark?.Polygon) {
|
// return 'planar'
|
planarWayline(ssLinePath.value, sbInfo.longitude, sbInfo.latitude)
|
} else {
|
// return 'point'
|
pointWayline(ssLinePath.value, sbInfo.longitude, sbInfo.latitude)
|
}
|
}
|
|
// 定义方法直接监听机场消息
|
const getviewDrone = (newValue:any) => {
|
if (newValue.data.host) {
|
viewDroneInfo.longitude = newValue.data.host?.longitude
|
viewDroneInfo.latitude = newValue.data.host?.latitude
|
viewDroneInfo.elevation = Number(newValue.data.host?.elevation.toFixed(2)) + droneHeight.value
|
}
|
}
|
|
|
// 地图全屏切换
|
const changeModelMap = () => {
|
isBigMap.value = !isBigMap.value
|
const cesiumContainer = document.getElementById('cesiumContainerBigMap');
|
const lZp = document.getElementById('lZp');
|
// const lZoom = document.getElementById('lZoom');
|
if (isLandscape.value) { // 横屏
|
if (isBigMap.value) { // 全屏
|
cesiumContainer.style.position = 'fixed';
|
cesiumContainer.style.top = '46px';
|
cesiumContainer.style.left = '0';
|
cesiumContainer.style.width = `${boxWidth.value}px`
|
// cesiumContainer.style.height = `${screenHeightMap.value}px`
|
lZp.style.right = '40%';
|
// lZoom.style.left = '0';
|
} else { // 缩放
|
cesiumContainer.style.width = `${boxWidth.value/2}px`
|
// cesiumContainer.style.height = `${screenHeightMap.value}px`
|
cesiumContainer.style.position = 'static';
|
lZp.style.right = '16%';
|
// lZoom.style.left = '50%';
|
|
}
|
} else {
|
if (isBigMap.value) { // 竖-全屏
|
cesiumContainer.style.position = 'fixed';
|
cesiumContainer.style.top = '46px';
|
cesiumContainer.style.left = '0';
|
// cesiumContainer.style.width = `${boxHeight.value}px`
|
cesiumContainer.style.height = `${boxHeight.value}px`
|
lZp.style.right = '30%';
|
// lZoom.style.right = '0';
|
} else { // 缩放
|
cesiumContainer.style.width = `${boxHeight.value}px`
|
cesiumContainer.style.height = `${boxHeight.value/2}px`
|
cesiumContainer.style.position = 'static';
|
lZp.style.right = '30%';
|
// lZoom.style.right = '0';
|
|
}
|
}
|
}
|
|
const checkOrientation = async () => {
|
boxWidth.value = window.innerWidth;
|
boxHeight.value = window.innerHeight - 46;
|
const landscapeBox = document.getElementById('landscapeBox');
|
let cesiumContainer = document.getElementById('cesiumContainerBigMap');
|
const lZp = document.getElementById('lZp');
|
const lZoom = document.getElementById('lZoom');
|
|
if (window.innerWidth > window.innerHeight) {//横屏
|
screenWidthVideo.value = boxWidth.value/2
|
screenHeightVideo.value = boxHeight.value
|
isLandscape.value = true;
|
// updateScreenSize()
|
landscapeBox.style.display = 'flex';
|
lZp.style.right = '16%';
|
lZoom.style.position = 'fixed';
|
cesiumContainer.style.width = `${boxWidth.value/2}px`
|
cesiumContainer.style.height = `${boxHeight.value}px`
|
cesiumContainer.style.position = 'static'
|
cesiumContainer.style.right = '0'
|
cesiumContainer.style.marginLeft = '0%'
|
} else { //竖屏
|
isLandscape.value = false;
|
screenWidthVideo.value = boxWidth.value
|
screenHeightVideo.value = boxHeight.value/2
|
// updateScreenSize()
|
landscapeBox.style.display = 'inherit';
|
landscapeBox.style.overflow = 'hidden';
|
lZp.style.right = '32%';
|
cesiumContainer.style.width = `${boxHeight.value}px`
|
cesiumContainer.style.height = `${boxHeight.value/2}px`
|
// cesiumContainer.style.position = 'absolute'
|
cesiumContainer.style.marginLeft = '-32%'
|
}
|
};
|
|
// 切换航线轨迹
|
const courseTrack = (data:any) => {
|
if (window.cesiumViewer== null) return
|
const currentSn = dockSn.value
|
const longitude = data.dockInfo[currentSn]?.basic_osd?.longitude || null
|
const latitude = data.dockInfo[currentSn]?.basic_osd?.latitude || null
|
const height = data.dockInfo[currentSn]?.basic_osd?.height || null
|
let getLongOk = ref(0)
|
if (!getEntityById('drone_dock')) {
|
getLnglatAltitude(longitude, latitude, window.cesiumViewer).then(res => {
|
removeById('drone_dock')
|
window.cesiumViewer.entities.add({
|
position: globalCesium.Cartesian3.fromDegrees(
|
longitude,
|
latitude,
|
res.height,
|
),
|
id: 'drone_dock',
|
billboard: {
|
image: imageMapSrc,
|
outlineWidth: 0,
|
width: 36,
|
height: 36,
|
scale: 1.0,
|
}
|
})
|
window.cesiumViewer.scene.camera.setView({
|
destination: globalCesium.Cartesian3.fromDegrees(
|
Number(longitude),
|
Number(latitude),
|
10000.0,
|
),
|
})
|
droneHeight.value = res.height
|
})
|
} else {
|
// 当机场坐标存在时,判断获取的位置与机场坐标位置是否一致,若不是,重新更新位置
|
let dornePoint = cartesian3Convert(getEntityById('drone_dock')._position._value, window.cesiumViewer,)
|
getLongOk = getLnglatDist(
|
dornePoint.longitude,
|
dornePoint.latitude,
|
longitude,
|
latitude,
|
)
|
if (getLongOk.value > 100) {
|
removeById('drone_dock')
|
}
|
}
|
if (getLongOk.value > 100) return
|
initDock(data, sn.value, dockSn.value, workspaceId.value, ssLinePath.value, viewDroneInfo)
|
}
|
|
// 监听
|
watch(() => store.state.deviceState, (newValue, oldValue) => {
|
if (newValue) {
|
nextTick()
|
courseTrack(newValue)
|
}
|
}, {deep: true})
|
|
// watch(() => store.state.wsMessage, (newValue, oldValue) => {
|
// // 控制台报 Expected longitude to be typeof number, actual typeof was undefined 是因为这里没有及时拿到longitude数据
|
// if (newValue) {
|
// viewDroneInfo.longitude = newValue?.longitude
|
// viewDroneInfo.latitude = newValue?.latitude
|
// viewDroneInfo.elevation = Number(newValue?.elevation.toFixed(2)) + droneHeight.value
|
// }
|
// }, {deep: true})
|
|
|
let intervalId = null;
|
|
const onClickLogin = () => {
|
history.back();
|
}
|
|
// 为组件赋值
|
const getGlobalViewer = () => {
|
globalViewer = window.cesiumViewer;
|
}
|
|
onMounted(async () => {
|
window.addEventListener('orientationchange', checkOrientation);
|
window.addEventListener('resize', checkOrientation); // 兼容某些设备
|
checkOrientation(); // 初始化时检查一次
|
// loadVideo();
|
await nextTick();
|
await _init('cesiumContainerBigMap');
|
await getGlobalViewer()
|
// await getWrjSsLx();
|
// 判断是遥控器还是机场
|
let domain = sessionStorage.getItem('domain')
|
if (domain !== '0') {
|
await getPhoneLocation(workspaceId.value)
|
// // // 设置定时器,每隔 5 秒刷新一次数据
|
// intervalId = setInterval(()=> {
|
// getPhoneLocation(workspaceId.value)
|
// }, 5000);
|
}
|
});
|
|
onBeforeUnmount(() => {
|
window.removeEventListener('orientationchange', checkOrientation);
|
window.removeEventListener('resize', checkOrientation);
|
|
// removeAllPoint()
|
// removeAllDataSource()
|
// 销毁地图实例
|
if (window.cesiumViewer && !window.cesiumViewer.isDestroyed()) {
|
window.cesiumViewer.destroy();
|
}
|
if (globalViewer && !globalViewer.isDestroyed()) {
|
globalViewer.destroy();
|
}
|
if (player) {
|
player.dispose();
|
}
|
// 组件卸载时清除定时器
|
if (intervalId) {
|
clearInterval(intervalId);
|
}
|
});
|
|
|
|
</script>
|
|
<style lang="scss" scoped>
|
.drone-pilot-details {
|
.landscape-box {
|
overflow: hidden;
|
// position: relative;
|
// display: flex;
|
.l-map {
|
.l-zp {
|
position: fixed;
|
right: 16%;
|
bottom: 0;
|
}
|
.l-zoom {
|
position: fixed;
|
bottom: 0;
|
right: 0;
|
.zoom {
|
width: 2rem;
|
height: 2rem;
|
margin: 0.2rem 0.4rem;
|
border-radius: 3px;
|
background-color: rgba(0, 0, 0, 0.8);
|
display: flex;
|
align-items: center;
|
justify-content: center;
|
overflow: hidden;
|
cursor: pointer;
|
pointer-events: all;
|
img {
|
width: 1.6rem;
|
height: 1.6rem;
|
}
|
}
|
}
|
}
|
}
|
.l-video {
|
position: relative;
|
.el-empty {
|
width: 100%;
|
height: 100%;
|
padding: 0;
|
::v-deep(.el-empty__image) {
|
width: 50%;
|
height: 50%;
|
}
|
::v-deep(.el-empty__description) {
|
margin: 0;
|
}
|
}
|
.center-point {
|
position: absolute;
|
top: 50%;
|
left: 50%;
|
width: 0.4rem;
|
height: 0.4rem;
|
opacity: 0.5;
|
border-radius: 50%;
|
background-color: yellow;
|
transform: translate(-50%, -50%);
|
justify-content: center;
|
}
|
.right-box {}
|
}
|
#cesiumContainerBigMap {
|
width: 100%;
|
height: 100%;
|
margin: 0;
|
padding: 0;
|
overflow: hidden;
|
transform-origin: top left;
|
transition: none; // 防止切换时的动画效果
|
backface-visibility: hidden; // 提高性能
|
-webkit-backface-visibility: hidden; // 提高性能
|
|
:deep() {
|
.cesium-viewer {
|
width: 100%;
|
height: 100%;
|
|
.cesium-viewer-cesiumWidgetContainer {
|
width: 100%;
|
height: 100%;
|
|
.cesium-widget {
|
width: 100%;
|
height: 100%;
|
|
canvas {
|
width: 100%;
|
height: 100%;
|
}
|
}
|
}
|
}
|
}
|
}
|
|
}
|
</style>
|