From a0f008e1b176ab0c844bc4721f282e12d18f855c Mon Sep 17 00:00:00 2001
From: chenyao <1219716595@qq.com>
Date: Mon, 24 Feb 2025 18:20:11 +0800
Subject: [PATCH] 调整详情和修改地图渲染功能
---
src/views/DronePilotDetails-copy1.vue | 754 ++++++++++
src/views/DronePilotDetails-copy3.vue | 675 +++++++++
src/views/DronePilotDetails.vue | 170 +
src/main.ts | 3
src/api/http/request.ts | 54
src/views/DronePilotDetails-copy2.vue | 708 +++++++++
src/views/components/ptzControl.vue | 76
src/views/DronePilotDetails-copy.vue | 2
src/views/hooks/droneFly-copy.ts | 664 +++++++++
src/views/hooks/droneFly.ts | 393 +---
src/api/http/request-copy.ts | 212 ++
src/views/components/ptzControl-copy.vue | 564 +++++++
src/router/index.ts | 4
src/views/DronePilotList.vue | 11
src/views/components/controlConsole.vue | 6
15 files changed, 3,858 insertions(+), 438 deletions(-)
diff --git a/src/api/http/request-copy.ts b/src/api/http/request-copy.ts
new file mode 100644
index 0000000..5186bda
--- /dev/null
+++ b/src/api/http/request-copy.ts
@@ -0,0 +1,212 @@
+/**
+ * 全站http配置
+ *
+ * axios参数说明
+ * isSerialize是否开启form表单提交
+ * isToken是否需要token
+ */
+import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'
+import store from '@/store/user'
+import router from '@/router';
+import { serialize } from '@/utils/common'
+import { getToken } from '@/utils/auth';
+import { ElMessage } from 'element-plus';
+import website from '@/config/website';
+import { Base64 } from 'js-base64'
+// import NProgress from 'nprogress';
+// import 'nprogress/nprogress.css';
+import { ELocalStorageKey } from '@/utils/enums';
+export * from './type'
+
+// 自定义响应类型
+interface CustomAxiosResponse<T = any> extends AxiosResponse<T> {
+ data: T
+}
+
+// 定义你的请求类型
+interface CustomAxiosRequestConfig extends AxiosRequestConfig {
+ // 是否开启鉴权
+ authorization?: boolean
+}
+
+type CustomAxiosConfig = {
+ [x: string]: any
+ <T = any, R = CustomAxiosResponse<T>, D = CustomAxiosRequestConfig>(config: D): Promise<R>
+}
+
+const REQUEST_ID = 'X-Request-Id'
+
+const getAuthToken = () => {
+ return localStorage.getItem(ELocalStorageKey.Token)
+}
+
+const instance: CustomAxiosConfig = axios.create({
+ // withCredentials: true,
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ baseURL: '/api',
+ // timeout: 12000,
+ withCredentials: true,
+})
+
+instance.interceptors.request.use(
+ (config: {
+ data: any
+ method: string
+ token: any
+ text: boolean
+ json: boolean
+ meta: {
+ isSerialize: boolean
+ isToken: boolean
+ noCookie: boolean
+ }
+ headers: { [x: string]: string | null }
+ url: string
+ authorization: boolean
+ }) => {
+ if(config.url.includes('h5')) {
+ config.url = '/drone-h5' + config.url
+ } else if (!config.url.includes('/blade-') && !config.url.includes('https://') && !config.url.includes('http://')) {
+ config.url = '/drone-yw' + config.url
+ }
+ const authorization = config.authorization === false
+ if (!authorization) {
+ config.headers.Authorization = `Basic ${Base64.encode(`${website.clientId}:${website.clientSecret}`)}`
+ }
+ // 让每个请求携带token
+ const meta = config.meta || {}
+ const isToken = meta.isToken === false
+ if (getToken() && !isToken) {
+ config.headers[website.tokenHeader] = 'bearer ' + getToken()
+ }
+ if (meta.noCookie) {
+ delete config.headers.cookie
+ }
+ // headers中配置text请求
+ if (config.text === true) {
+ config.headers['Content-Type'] = 'text/plain'
+ }
+ // headers配置JSON请求
+ if (config.json === true) {
+ config.headers['Content-Type'] = 'application/json'
+ }
+ // 水情预报接口调用
+ if (config.token) {
+ config.headers.Authorization = config.token
+ }
+ // headers中配置serialize为true开启序列化
+ if (config.method === 'post' && meta.isSerialize === true) {
+ config.data = serialize(config.data)
+ }
+ // config.headers[ELocalStorageKey.Token] = localStorage.getItem(ELocalStorageKey.Token)
+ // config.headers[REQUEST_ID] = uuidv4()
+ // config.baseURL = CURRENT_CONFIG.baseURL
+ return config
+ },
+ (error: any) => {
+ return Promise.reject(error)
+ },
+)
+
+instance.interceptors.response.use(
+ (res: { data: { code: any; msg: any; message: any; error_description: any }; status: any; config: { url: string; meta: { noCookie: boolean } } }) => {
+ const status = res.data.code || res.status
+ const message = res.data.msg || res.data.message || res.data.error_description || '未知错误'
+ // 如果是401则跳转到登录页面
+ // const store = useMyStore()
+ if (status === 401) {
+ store.dispatch('FedLogOut').then(() => router.push({ path: '/login' }))
+ // return
+ }
+ // 如果请求为非200否者默认统一处理
+ if (status !== 200) {
+ // 排除海康 code 为 0 情况
+ if (status === 0) {
+ return res
+ }
+ if ('meta' in res.config && 'noCookie' in res.config.meta && res.config.meta.noCookie === true) {
+ return Promise.reject(new Error(message))
+ }
+ if (message === '缺失令牌,鉴权失败') {
+ // message({
+ // message: '登录信息已过期,请重新登录',
+ // type: 'warning',
+ // })
+ ElMessage.warning('登录信息已过期,请重新登录')
+ }
+ if (
+ ![
+ '没有收到消息回复。',
+ '未知消息',
+ '设备端退出drc模式',
+ '未能恢复航路作业。错误码:: 319042',
+ 'success',
+ '缺失令牌,鉴权失败',
+ ].includes(message)
+ ) {
+ ElMessage.warning(message)
+ }
+ return Promise.reject(new Error(message))
+ }
+ // console.info('URL: ' + response.config.baseURL + response.config.url, '\nData: ', response.data, '\nResponse:', response)
+ // const whiteList = ['/manage/api/v1/live/streams/stop']
+ // if (whiteList.includes(response.config.url)) {
+ // return Promise.reject(response.data)
+ // }
+ // 处理消息返回
+ // if (response.data.code && !response.data.success) {
+ // message.error(response.data.message)
+ // }
+ // if (response.data.code && response.data.code !== 0) {
+ // message.error(response.data.message)
+ // }
+ return res
+ },
+ (err: {
+ config: { headers: { [x: string]: any }; url: any; method: any }
+ response: { data: { message: string; result: { message: string } }; status: number }
+ }) => {
+ const requestId = err?.config?.headers && err?.config?.headers[REQUEST_ID]
+ if (requestId) {
+ console.info(REQUEST_ID, ':', requestId)
+ }
+ console.info('url: ', err?.config?.url, `【${err?.config?.method}】 \n>>>> err: `, err)
+
+ let description = '-'
+ if (err.response?.data && err.response.data.message) {
+ description = err.response.data.message
+ }
+ if (err.response?.data && err.response.data.result) {
+ description = err.response.data.result.message
+ }
+ // @See: https://github.com/axios/axios/issues/383
+ if (!err.response || !err.response.status) {
+ ElMessage.error('网络异常,请检查后端服务后重试')
+ return
+ }
+ if (err.response?.status !== 200) {
+ ElMessage.error(`错误码: ${err.response?.status}`)
+ }
+ // if (err.response?.status === 403) {
+ // // window.location.href = '/'
+ // }
+ if (err.response?.status === 401) {
+ console.error(err.response)
+ store.dispatch('FedLogOut').then(() => router.push({ path: '/login' }))
+ // const flag: number = Number(localStorage.getItem(ELocalStorageKey.Flag))
+ // switch (flag) {
+ // case EUserType.Web:
+ // router.push(ERouterName.PROJECT_LIST)
+ // break
+ // case EUserType.Pilot:
+ // router.push(ERouterName.PILOT)
+ // break
+ // }
+ }
+ return Promise.reject(err)
+ },
+)
+
+export default instance
diff --git a/src/api/http/request.ts b/src/api/http/request.ts
index 5186bda..6488409 100644
--- a/src/api/http/request.ts
+++ b/src/api/http/request.ts
@@ -6,7 +6,8 @@
* isToken是否需要token
*/
import axios, { AxiosRequestConfig, AxiosResponse } from 'axios'
-import store from '@/store/user'
+// import store from '@/store/user'
+import { useMyStore } from '@/store'
import router from '@/router';
import { serialize } from '@/utils/common'
import { getToken } from '@/utils/auth';
@@ -100,9 +101,6 @@
if (config.method === 'post' && meta.isSerialize === true) {
config.data = serialize(config.data)
}
- // config.headers[ELocalStorageKey.Token] = localStorage.getItem(ELocalStorageKey.Token)
- // config.headers[REQUEST_ID] = uuidv4()
- // config.baseURL = CURRENT_CONFIG.baseURL
return config
},
(error: any) => {
@@ -115,10 +113,8 @@
const status = res.data.code || res.status
const message = res.data.msg || res.data.message || res.data.error_description || '未知错误'
// 如果是401则跳转到登录页面
- // const store = useMyStore()
- if (status === 401) {
- store.dispatch('FedLogOut').then(() => router.push({ path: '/login' }))
- // return
+ if (status == 401) {
+ router.push({ path: '/login' })
}
// 如果请求为非200否者默认统一处理
if (status !== 200) {
@@ -130,10 +126,6 @@
return Promise.reject(new Error(message))
}
if (message === '缺失令牌,鉴权失败') {
- // message({
- // message: '登录信息已过期,请重新登录',
- // type: 'warning',
- // })
ElMessage.warning('登录信息已过期,请重新登录')
}
if (
@@ -150,18 +142,6 @@
}
return Promise.reject(new Error(message))
}
- // console.info('URL: ' + response.config.baseURL + response.config.url, '\nData: ', response.data, '\nResponse:', response)
- // const whiteList = ['/manage/api/v1/live/streams/stop']
- // if (whiteList.includes(response.config.url)) {
- // return Promise.reject(response.data)
- // }
- // 处理消息返回
- // if (response.data.code && !response.data.success) {
- // message.error(response.data.message)
- // }
- // if (response.data.code && response.data.code !== 0) {
- // message.error(response.data.message)
- // }
return res
},
(err: {
@@ -181,29 +161,21 @@
if (err.response?.data && err.response.data.result) {
description = err.response.data.result.message
}
- // @See: https://github.com/axios/axios/issues/383
if (!err.response || !err.response.status) {
ElMessage.error('网络异常,请检查后端服务后重试')
return
}
if (err.response?.status !== 200) {
- ElMessage.error(`错误码: ${err.response?.status}`)
+ if (err.response.data?.msg) {
+ ElMessage.error(err.response.data?.msg)
+ } else if (err.response.data?.error_description) {
+ ElMessage.error(err.response.data?.error_description)
+ } else {
+ ElMessage.error(`错误码: ${err.response?.status}`)
+ }
}
- // if (err.response?.status === 403) {
- // // window.location.href = '/'
- // }
- if (err.response?.status === 401) {
- console.error(err.response)
- store.dispatch('FedLogOut').then(() => router.push({ path: '/login' }))
- // const flag: number = Number(localStorage.getItem(ELocalStorageKey.Flag))
- // switch (flag) {
- // case EUserType.Web:
- // router.push(ERouterName.PROJECT_LIST)
- // break
- // case EUserType.Pilot:
- // router.push(ERouterName.PILOT)
- // break
- // }
+ if (err.response?.status == 401) {
+ router.push({ path: '/login' })
}
return Promise.reject(err)
},
diff --git a/src/main.ts b/src/main.ts
index 44a7ddd..84dd574 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -8,6 +8,8 @@
import store, { storeKey } from './store'
import { createInstance } from '../root'
import * as Cesium from 'cesium';
+import Vant from 'vant';
+import 'vant/lib/index.css'; // 引入样式文件
// 初始化 Cesium Viewer
window.cesiumViewer = null
@@ -15,6 +17,7 @@
const app = createInstance(App) // 引入css
app.use(ElementPlus);
+app.use(Vant);
app.use(store, storeKey)
app.use(router)
diff --git a/src/router/index.ts b/src/router/index.ts
index 922833c..ee65479 100644
--- a/src/router/index.ts
+++ b/src/router/index.ts
@@ -26,6 +26,10 @@
// },
{
path: '/',
+ redirect: '/login',
+ },
+ {
+ path: '/login',
name: ERouterName.LOGIN,
component: () => import('@/views/Login.vue')
},
diff --git a/src/views/DronePilotDetails-copy.vue b/src/views/DronePilotDetails-copy.vue
index 160a8e6..50fb190 100644
--- a/src/views/DronePilotDetails-copy.vue
+++ b/src/views/DronePilotDetails-copy.vue
@@ -120,7 +120,7 @@
// 获取机场/遥控器sn
let localData = ref<any>(window.localStorage.getItem('sbInfo'))
const sbInfo: deviceInfo = JSON.parse(localData.value) as deviceInfo;
-// console.log('哒哒哒', sbInfo)
+
// 无人机sn
const sn = ref<String>(sbInfo.sn)
// 机场sn
diff --git a/src/views/DronePilotDetails-copy1.vue b/src/views/DronePilotDetails-copy1.vue
new file mode 100644
index 0000000..6104fed
--- /dev/null
+++ b/src/views/DronePilotDetails-copy1.vue
@@ -0,0 +1,754 @@
+<template>
+ <div class="drone-pilot-details" :style="{ width: screenWidth,height: screenHeight}">
+ <!-- 横屏模式 -->
+ <div id="landscapeBox" class="landscape-box" :style="{ width: screenWidth + 'px', height: (screenHeight) + '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>
+ <div class="l-map" :style="{ width: screenWidthVideo + 'px', height: (screenHeightVideo) + 'px'}">
+ <div id="cesiumContainerBigMap" :style="{ width: screenWidthMap + 'px', height: (screenHeightMap) + 'px'}"></div>
+ <div class="l-zp" id="lZp">
+ <comPass :cesiumViewe="globalViewer"/>
+ </div>
+ <div class="l-zoom" id="lZoom">
+ <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 class="right-box">
+ <ptzControl :sn="sn" :osdVisible="sbInfo" />
+ <controlConsole :sn="sn" :osdVisible="sbInfo" :cesiumViewe="globalViewer" />
+ </div>
+ </div>
+ <!-- 竖屏 -->
+ <!-- <div class="portrait-screen" v-else>
+ <div class="l-video" :style="{ width: screenWidth + 'px', height: (screenHeight/2)+40 + 'px'}">
+ <video v-show="videoUrl" ref="videoPlayerBig" :style="{ width: screenWidth + 'px', height: (screenHeight/2)+40 + '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>
+ <div class="l-map" :style="{ width: screenWidth + 'px', height: (screenHeight/2)-40 + 'px'}">
+ <div id="cesiumContainerMap"></div>
+ <div class="l-zp" id="lZp">
+ <comPass :cesiumViewe="globalViewer"/>
+ </div>
+ </div>
+ <div class="right-box">
+ <ptzControl :sn="sn" :osdVisible="sbInfo" />
+ <controlConsole :sn="sn" :osdVisible="sbInfo" />
+ </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 { startLivestream, getLiveVideoUrl } from '@/api/manage'
+import { CURRENT_CONFIG as config } from '@/api/http/config'
+// import videojs from 'video.js'
+// import 'video.js/dist/video-js.css';
+import { getLnglatDist, cartesian3Convert, getLnglatAltitude, createCircleBillboard } from '@/utils/cesium/mapUtil'
+import cesiumOperation from '@/utils/cesium-tsa.js';
+const { _init, viewerDestory } = cesiumOperation();
+import * as Cesium from 'cesium';
+
+// import { cesiumOperation } from '@/hooks/use-cesium-tsa'
+// const { _init } = cesiumOperation();
+import { useMyStore } from '@/store'
+import { getWaylineFile } from '@/api/manage'
+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 { any } from 'video.js/dist/types/utils/events';
+import { analyzeKmzFile, XMLToJSON } from '@/utils/cesium/kmz.js'
+import { initPointWayLine } from './hooks/initPointWayline'
+import { initPlanarWayline } from './hooks/initPlanarWayline'
+import { droneFly } from './hooks/droneFly'
+import flvjs from 'flv.js';
+const { pointWayline } = initPointWayLine()
+const { planarWayline } = initPlanarWayline()
+const { initDock } = 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 screenWidthMap = ref((window.innerHeight/2)*2)// ref(window.innerWidth);
+const screenHeightMap = ref((window.innerHeight/2));
+// 视频切换
+const screenWidthVideo = ref(window.innerWidth);
+const screenHeightVideo = ref(window.innerHeight/2);
+const isBigMap = ref(false)
+
+// 更新屏幕尺寸的函数
+const updateScreenSize = () => {
+ screenWidth.value = window.innerWidth;
+ screenHeight.value = window.innerHeight;
+ if (isLandscape.value) { //横屏
+ screenWidthMap.value = window.innerWidth/2
+ screenHeightMap.value = window.innerHeight
+ screenWidthVideo.value = window.innerWidth/2
+ screenHeightVideo.value = window.innerHeight
+ } else {//竖屏
+ screenWidthMap.value = (window.innerHeight/2)*2
+ screenHeightMap.value = (window.innerHeight/2)
+ screenWidthVideo.value = window.innerWidth
+ screenHeightVideo.value = (window.innerHeight/2)
+ }
+};
+
+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?: { };
+}
+// 获取机场/遥控器sn
+let localData = ref<any>(window.localStorage.getItem('sbInfo'))
+const sbInfo: deviceInfo = JSON.parse(localData.value) as deviceInfo;
+
+// 无人机sn
+const sn = ref<String>(sbInfo.sn)
+// 机场sn
+const dockSn = ref<String>(sbInfo.dockSn)
+
+// 记录横竖屏模式
+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 meMapSrc = new URL('@/assets/images/me.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 timer = null;
+const videoPlayer = ref<any>(null); // 视频播放器实例
+let webrtcPlayer = null; // video.js 播放器
+
+// 大屏幕视频
+const timerBig = null;
+const videoPlayerBig = ref<any>(null); // 视频播放器实例
+let webrtcPlayerBig = null
+
+let player = null; // video.js 播放器
+
+// 无人机实时飞行链接
+let ssLinePath = ref(null)
+
+// 实时定位
+let viewDroneInfo = {}
+// 机场高度
+let droneHeight = ref(0)
+
+// 监听
+let connectWs = ref<any>(null)
+
+let workspaceId = ref<String>(sbInfo.workspaceId)
+localStorage.setItem('bs_workspace_id', workspaceId.value)
+// store.commit('SET_SELECTED_WORKSPACE_ID', workspaceId.value);
+
+// 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 play = () => {
+// webrtcPlayer = new window.ZLMRTCClient.Endpoint({
+// element: videoPlayer.value, // video 标签
+// debug: true, // 是否打印日志
+// zlmsdpUrl: videoUrl.value, //Rtc地址
+// simulecast: false,
+// useCamera: false,
+// audioEnable: true,
+// videoEnable: true,
+// recvOnly: true,
+// usedatachannel: false,
+// })
+// webrtcPlayer.on(
+// window.ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR,
+// (e) => {
+// // ICE 协商出错
+// console.error('ICE 协商出错')
+// // this.eventcallbacK('ICE ERROR', 'ICE 协商出错')
+// },
+// )
+
+// webrtcPlayer.on(
+// window.ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS,
+// (e) => {
+// //获取到了远端流,可以播放
+// console.log('播放成功', e.streams)
+// // this.eventcallbacK('playing', '播放成功')
+// },
+// )
+
+// webrtcPlayer.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('流不存在')
+// timer = setTimeout(() => {
+// webrtcPlayer.close()
+// play()
+// }, 100)
+// }
+// },
+// )
+
+// webrtcPlayer.on(
+// window.ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM,
+// (s) => {
+// },
+// )
+// }
+const videoUrlFlv = ref<String>('')
+// 加载该设备的视频信息
+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
+ videoUrlFlv.value = res.data.data.url
+ // 播放
+ // play()
+ playBig()
+ // flv
+ // initFlvPlayer()
+ })
+}
+
+// flv
+const videoPlayerFlv = ref(null); // 引用 video 元素
+let flvPlayer = null; // 用于存储 flv.js 播放器实例
+ // 初始化 flv.js 播放器
+
+const initFlvPlayer = () => {
+ if (flvjs.isSupported()) {
+ const videoElement = videoPlayerFlv.value; // 获取 video 元素
+ flvPlayer = flvjs.createPlayer({
+ type: 'flv', // 视频格式
+ url: videoUrlFlv.value, // FLV 视频地址
+ });
+ flvPlayer.attachMediaElement(videoElement); // 绑定 video 元素
+ flvPlayer.load(); // 加载视频
+ flvPlayer.play(); // 播放视频
+ } else {
+ console.error('Your browser does not support flv.js.');
+ }
+};
+
+//===============================
+
+// 监听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)
+ break
+ }
+ case EBizCode.DockOsd: { // 机场
+ store.commit('SET_DOCK_INFO', payload.data)
+ break
+ }
+ }
+
+}
+
+const webSorketUrl = getWebsocketUrl() + '&workspace-id=' + workspaceId.value
+// 监听ws 消息
+useConnectWebSocket(messageHandler, webSorketUrl)
+
+
+let savedMapState = null; // 保存地图状态
+// 销毁之前先保存状态
+const destroyViewer = () => {
+ if ( window.cesiumViewer) {
+ // 保存当前地图状态(如相机位置)
+ // savedMapState = window.cesiumViewer.camera.view;
+ window.cesiumViewer.destroy();
+ window.cesiumViewer = null;
+ }
+}
+
+// 地图全屏切换
+const changeModelMap = () => {
+ isBigMap.value = !isBigMap.value
+ const cesiumContainer = document.getElementById('cesiumContainerBigMap');
+ const lZp = document.getElementById('lZp');
+ const lZoom = document.getElementById('lZoom');
+
+ if (isBigMap.value) { // 全屏
+ cesiumContainer.style.position = 'fixed';
+ cesiumContainer.style.top = '0';
+ cesiumContainer.style.left = '0';
+ cesiumContainer.style.width = '100%';
+ cesiumContainer.style.height = '100%';
+ lZp.style.right = '40%';
+ lZoom.style.left = '0';
+ } else { // 缩放
+ cesiumContainer.style.position = 'static';
+ cesiumContainer.style.width = `${screenWidthMap.value}`
+ cesiumContainer.style.height = `${screenHeight.value}`
+ lZp.style.right = '16%';
+ lZoom.style.left = '50%';
+
+ }
+}
+
+const checkOrientation = async () => {
+ const landscapeBox = document.getElementById('landscapeBox');
+ const cesiumContainer = document.getElementById('cesiumContainerBigMap');
+ const videoModule = document.getElementById('videoModule');
+ const lZp = document.getElementById('lZp');
+ const lZoom = document.getElementById('lZoom');
+ // 横屏再加载视频请求
+ // destroyViewer()
+
+ // loadVideo()
+ if (window.innerWidth > window.innerHeight) {
+ isLandscape.value = true;
+ updateScreenSize()
+ landscapeBox.style.display = 'flex';
+ lZp.style.right = '16%';
+ lZoom.style.display = 'block';
+ lZoom.style.position = 'fixed';
+ // await nextTick();
+ // await _init('cesiumContainerBigMap');
+ // await getWrjSsLx();
+ } else {
+ isLandscape.value = false;
+ updateScreenSize()
+ landscapeBox.style.display = 'inherit';
+ lZp.style.right = '32%';
+ lZoom.style.display = 'none';
+ // landscapeBox.style.width = `${screenWidth}`
+ // landscapeBox.style.height = `${screenHeight}`
+ // videoModule.style.width = `${screenWidth}`
+ // videoModule.style.height = `${screenHeight}`
+ // await nextTick();
+ // await _init('cesiumContainerMap');
+ // await getWrjSsLx();
+ }
+};
+
+// 获取实时航线
+const getWrjSsLx = () => {
+ getWaylineFile(sbInfo.sn).then((res:any) => {
+ if (res.code != 200) return
+ ssLinePath.value = res.data
+ globalViewer = window.cesiumViewer
+ // 先有航线,再飞行
+ generateCourse()
+ })
+}
+
+// 切换航线轨迹
+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})
+
+
+
+// 生成航线轨迹
+const generateCourse = async () => {
+ console.log('有木有航线值', ssLinePath.value)
+ 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
+ console.log('看看航线值', placemark.Polygon)
+ if (placemark?.Polygon) {
+ // return 'planar'
+ planarWayline(ssLinePath.value, sbInfo.longitude, sbInfo.latitude)
+ } else {
+ // return 'point'
+ pointWayline(ssLinePath.value, sbInfo.longitude, sbInfo.latitude)
+ }
+}
+
+const me_latitude = ref(0);
+const me_longitude = ref(0);
+// 获取自身定位
+const getLocation = () => {
+ if (window.cesiumViewer == null) return
+ if (navigator.geolocation) {
+ navigator.geolocation.getCurrentPosition(
+ (position) => {
+ me_latitude.value = position.coords.latitude;
+ me_longitude.value = position.coords.longitude;
+ // 开始给自己定位
+ getLnglatAltitude(me_longitude.value, me_latitude.value, window.cesiumViewer).then(res => {
+ removeById('me_coordinate')
+ window.cesiumViewer.entities.add({
+ position: globalCesium.Cartesian3.fromDegrees(
+ me_longitude.value,
+ me_latitude.value,
+ res.height || 0,
+ ),
+ id: 'me_coordinate',
+ billboard: {
+ image: meMapSrc,
+ outlineWidth: 0,
+ width: 36,
+ height: 36,
+ scale: 1.0,
+ }
+ })
+ })
+ },
+ (err) => {
+ console.log('定位有误')
+ }
+ );
+ } else {
+ console.log('您的浏览器不支持地理定位')
+ }
+};
+
+onMounted(async () => {
+ window.addEventListener('orientationchange', checkOrientation);
+ window.addEventListener('resize', checkOrientation); // 兼容某些设备
+ checkOrientation(); // 初始化时检查一次
+ loadVideo()
+ await nextTick();
+ await _init('cesiumContainerBigMap');
+ await getWrjSsLx();
+
+ await getLocation()
+
+});
+
+onBeforeUnmount(() => {
+ window.removeEventListener('orientationchange', checkOrientation);
+ window.removeEventListener('resize', checkOrientation);
+ if (player) {
+ player.dispose();
+ }
+ if (flvPlayer) {
+ flvPlayer.pause(); // 暂停播放
+ flvPlayer.unload(); // 卸载视频
+ flvPlayer.detachMediaElement(); // 解绑 video 元素
+ flvPlayer.destroy(); // 销毁播放器
+ flvPlayer = null;
+ }
+});
+
+
+
+</script>
+
+<style lang="scss" scoped>
+.drone-pilot-details {
+ .landscape-box {
+ position: relative;
+ display: flex;
+ .l-map {
+ .l-zp {
+ position: fixed;
+ right: 16%;
+ bottom: 0;
+ }
+ .l-zoom {
+ position: fixed;
+ bottom: 0;
+ left: 50%;
+ .zoom {
+ width: 2rem;
+ height: 2rem;
+ 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 {
+ .el-empty {
+ width: 100%;
+ height: 100%;
+ padding: 0;
+ ::v-deep(.el-empty__image) {
+ width: 50%;
+ height: 50%;
+ }
+ ::v-deep(.el-empty__description) {
+ margin: 0;
+ }
+ }
+ }
+ #cesiumContainerBigMap {
+ width: 100%;
+ height: 100%;
+ overflow: 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%;
+ }
+ }
+ }
+ }
+ }
+ }
+ #cesiumContainerMap {
+ width: 100%;
+ height: 100%;
+ overflow: 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%;
+ }
+ }
+ }
+ }
+ }
+ }
+ .portrait-screen {
+ .l-map {
+ .l-zp {
+ position: fixed;
+ right: 28%;
+ bottom: 0;
+ }
+ .l-zoom {
+ position: fixed;
+ bottom: 0;
+ .zoom {
+ width: 2rem;
+ height: 2rem;
+ 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;
+ }
+ }
+ }
+ }
+ }
+}
+</style>
\ No newline at end of file
diff --git a/src/views/DronePilotDetails-copy2.vue b/src/views/DronePilotDetails-copy2.vue
new file mode 100644
index 0000000..17d0c63
--- /dev/null
+++ b/src/views/DronePilotDetails-copy2.vue
@@ -0,0 +1,708 @@
+<template>
+ <div class="drone-pilot-details" :style="{ width: screenWidth,height: screenHeight}">
+ <!-- 横屏模式 -->
+ <div id="landscapeBox" class="landscape-box" :style="{ width: screenWidth + 'px', height: (screenHeight) + '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>
+ <div class="l-map" :style="{ width: screenWidthMap + 'px', height: (screenHeightMap) + '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 v-if="!isBigMap" src="@/assets/images/fang.png" @touchstart="changeModelMap" />
+ <img v-else src="@/assets/images/suo.png" @touchstart="changeModelMap" />
+ </div>
+ </div>
+ </div>
+ <div class="right-box">
+ <ptzControl :sn="sn" :osdVisible="sbInfo" />
+ <controlConsole :sn="sn" :osdVisible="sbInfo" :cesiumViewe="globalViewer" />
+ </div>
+ </div>
+ <!-- 竖屏 -->
+ <!-- <div class="portrait-screen" v-else>
+ <div class="l-video" :style="{ width: screenWidth + 'px', height: (screenHeight/2)+40 + 'px'}">
+ <video v-show="videoUrl" ref="videoPlayerBig" :style="{ width: screenWidth + 'px', height: (screenHeight/2)+40 + '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>
+ <div class="l-map" :style="{ width: screenWidth + 'px', height: (screenHeight/2)-40 + 'px'}">
+ <div id="cesiumContainerMap"></div>
+ <div class="l-zp" id="lZp">
+ <comPass :cesiumViewe="globalViewer"/>
+ </div>
+ </div>
+ <div class="right-box">
+ <ptzControl :sn="sn" :osdVisible="sbInfo" />
+ <controlConsole :sn="sn" :osdVisible="sbInfo" />
+ </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 { startLivestream, getLiveVideoUrl } from '@/api/manage'
+import { CURRENT_CONFIG as config } from '@/api/http/config'
+// import videojs from 'video.js'
+// import 'video.js/dist/video-js.css';
+import { getLnglatDist, cartesian3Convert, getLnglatAltitude, createCircleBillboard } from '@/utils/cesium/mapUtil'
+import cesiumOperation from '@/utils/cesium-tsa.js';
+const { _init, viewerDestory } = cesiumOperation();
+import * as Cesium from 'cesium';
+
+// import { cesiumOperation } from '@/hooks/use-cesium-tsa'
+// const { _init } = cesiumOperation();
+import { useMyStore } from '@/store'
+import { getWaylineFile } from '@/api/manage'
+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 { any } from 'video.js/dist/types/utils/events';
+import { analyzeKmzFile, XMLToJSON } from '@/utils/cesium/kmz.js'
+import { initPointWayLine } from './hooks/initPointWayline'
+import { initPlanarWayline } from './hooks/initPlanarWayline'
+import { droneFly } from './hooks/droneFly'
+import flvjs from 'flv.js';
+const { pointWayline } = initPointWayLine()
+const { planarWayline } = initPlanarWayline()
+const { initDock } = 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 screenWidthMap = ref(window.innerWidth);
+const screenHeightMap = ref((window.innerHeight/2)+40);
+// 视频切换
+const screenWidthVideo = ref(window.innerWidth);
+const screenHeightVideo = ref(window.innerHeight/2);
+const isBigMap = ref(false)
+
+// 更新屏幕尺寸的函数
+const updateScreenSize = () => {
+ screenWidth.value = window.innerWidth;
+ screenHeight.value = window.innerHeight;
+ if (isLandscape.value) { //横屏
+ screenWidthMap.value = window.innerWidth/2
+ screenHeightMap.value = window.innerHeight
+ screenWidthVideo.value = window.innerWidth/2
+ screenHeightVideo.value = window.innerHeight
+ } else {//竖屏
+ screenWidthMap.value = window.innerWidth
+ screenHeightMap.value = (window.innerHeight/2)-40
+ screenWidthVideo.value = window.innerWidth
+ screenHeightVideo.value = (window.innerHeight/2)+40
+ }
+};
+
+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?: { };
+}
+// 获取机场/遥控器sn
+let localData = ref<any>(window.localStorage.getItem('sbInfo'))
+const sbInfo: deviceInfo = JSON.parse(localData.value) as deviceInfo;
+// console.log('哒哒哒', sbInfo)
+// 无人机sn
+const sn = ref<String>(sbInfo.sn)
+// 机场sn
+const dockSn = ref<String>(sbInfo.dockSn)
+
+// 记录横竖屏模式
+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 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 timer = null;
+const videoPlayer = ref<any>(null); // 视频播放器实例
+let webrtcPlayer = null; // video.js 播放器
+
+// 大屏幕视频
+const timerBig = null;
+const videoPlayerBig = ref<any>(null); // 视频播放器实例
+let webrtcPlayerBig = null
+
+let player = null; // video.js 播放器
+
+// 无人机实时飞行链接
+let ssLinePath = ref(null)
+
+// 实时定位
+let viewDroneInfo = {}
+// 机场高度
+let droneHeight = ref(0)
+
+// 监听
+let connectWs = ref<any>(null)
+
+let workspaceId = ref<String>(sbInfo.workspaceId)
+localStorage.setItem('bs_workspace_id', workspaceId.value)
+// store.commit('SET_SELECTED_WORKSPACE_ID', workspaceId.value);
+
+// 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 play = () => {
+// webrtcPlayer = new window.ZLMRTCClient.Endpoint({
+// element: videoPlayer.value, // video 标签
+// debug: true, // 是否打印日志
+// zlmsdpUrl: videoUrl.value, //Rtc地址
+// simulecast: false,
+// useCamera: false,
+// audioEnable: true,
+// videoEnable: true,
+// recvOnly: true,
+// usedatachannel: false,
+// })
+// webrtcPlayer.on(
+// window.ZLMRTCClient.Events.WEBRTC_ICE_CANDIDATE_ERROR,
+// (e) => {
+// // ICE 协商出错
+// console.error('ICE 协商出错')
+// // this.eventcallbacK('ICE ERROR', 'ICE 协商出错')
+// },
+// )
+
+// webrtcPlayer.on(
+// window.ZLMRTCClient.Events.WEBRTC_ON_REMOTE_STREAMS,
+// (e) => {
+// //获取到了远端流,可以播放
+// console.log('播放成功', e.streams)
+// // this.eventcallbacK('playing', '播放成功')
+// },
+// )
+
+// webrtcPlayer.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('流不存在')
+// timer = setTimeout(() => {
+// webrtcPlayer.close()
+// play()
+// }, 100)
+// }
+// },
+// )
+
+// webrtcPlayer.on(
+// window.ZLMRTCClient.Events.WEBRTC_ON_LOCAL_STREAM,
+// (s) => {
+// },
+// )
+// }
+const videoUrlFlv = ref<String>('')
+// 加载该设备的视频信息
+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
+ videoUrlFlv.value = res.data.data.url
+ // 播放
+ // play()
+ playBig()
+ // flv
+ // initFlvPlayer()
+ })
+}
+
+// flv
+const videoPlayerFlv = ref(null); // 引用 video 元素
+let flvPlayer = null; // 用于存储 flv.js 播放器实例
+ // 初始化 flv.js 播放器
+
+const initFlvPlayer = () => {
+ if (flvjs.isSupported()) {
+ const videoElement = videoPlayerFlv.value; // 获取 video 元素
+ flvPlayer = flvjs.createPlayer({
+ type: 'flv', // 视频格式
+ url: videoUrlFlv.value, // FLV 视频地址
+ });
+ flvPlayer.attachMediaElement(videoElement); // 绑定 video 元素
+ flvPlayer.load(); // 加载视频
+ flvPlayer.play(); // 播放视频
+ } else {
+ console.error('Your browser does not support flv.js.');
+ }
+};
+
+//===============================
+
+// 监听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: {
+ // console.log('低调点',payload)
+ store.commit('SET_DEVICE_INFO', payload.data)
+ store.commit('SET_WS_MESSAGE', payload)
+ break
+ }
+ case EBizCode.DockOsd: { // 机场
+ store.commit('SET_DOCK_INFO', payload.data)
+ break
+ }
+ }
+
+}
+
+const webSorketUrl = getWebsocketUrl() + '&workspace-id=' + workspaceId.value
+// 监听ws 消息
+useConnectWebSocket(messageHandler, webSorketUrl)
+
+
+let savedMapState = null; // 保存地图状态
+// 销毁之前先保存状态
+const destroyViewer = () => {
+ if ( window.cesiumViewer) {
+ // 保存当前地图状态(如相机位置)
+ // savedMapState = window.cesiumViewer.camera.view;
+ window.cesiumViewer.destroy();
+ window.cesiumViewer = null;
+ }
+}
+
+// 地图全屏切换
+const changeModelMap = () => {
+ isBigMap.value = !isBigMap.value
+ const cesiumContainer = document.getElementById('cesiumContainerBigMap');
+ const lZp = document.getElementById('lZp');
+ const lZoom = document.getElementById('lZoom');
+
+ if (isBigMap.value) { // 全屏
+ cesiumContainer.style.position = 'fixed';
+ cesiumContainer.style.top = '0';
+ cesiumContainer.style.left = '0';
+ cesiumContainer.style.width = '100%';
+ cesiumContainer.style.height = '100%';
+ lZp.style.right = '40%';
+ lZoom.style.left = '0';
+ } else { // 缩放
+ cesiumContainer.style.position = 'static';
+ cesiumContainer.style.width = `${screenWidthMap.value}`
+ cesiumContainer.style.height = `${screenHeight.value}`
+ lZp.style.right = '16%';
+ lZoom.style.left = 'auto';
+
+ }
+}
+
+const checkOrientation = async () => {
+ const landscapeBox = document.getElementById('landscapeBox');
+ const cesiumContainer = document.getElementById('cesiumContainerBigMap');
+ const videoModule = document.getElementById('videoModule');
+ const lZp = document.getElementById('lZp');
+ const lZoom = document.getElementById('lZoom');
+ // 横屏再加载视频请求
+ // destroyViewer()
+
+ // loadVideo()
+ if (window.innerWidth > window.innerHeight) {
+ isLandscape.value = true;
+ updateScreenSize()
+ landscapeBox.style.display = 'flex';
+ lZp.style.right = '16%';
+ lZoom.style.display = 'block';
+ lZoom.style.position = 'fixed';
+ // await nextTick();
+ // await _init('cesiumContainerBigMap');
+ // await getWrjSsLx();
+ } else {
+ console.log('是否进来',landscapeBox)
+ isLandscape.value = false;
+ updateScreenSize()
+ landscapeBox.style.display = 'inherit';
+ lZp.style.right = '28%';
+ lZoom.style.display = 'none';
+ // landscapeBox.style.width = `${screenWidth}`
+ // landscapeBox.style.height = `${screenHeight}`
+ // videoModule.style.width = `${screenWidth}`
+ // videoModule.style.height = `${screenHeight}`
+ // await nextTick();
+ // await _init('cesiumContainerMap');
+ // await getWrjSsLx();
+ }
+};
+
+// 获取实时航线
+const getWrjSsLx = () => {
+ getWaylineFile(sbInfo.sn).then((res:any) => {
+ if (res.code != 200) return
+ ssLinePath.value = res.data
+ globalViewer = window.cesiumViewer
+ // 先有航线,再飞行
+ generateCourse()
+ })
+}
+
+// 切换航线轨迹
+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) => {
+ viewDroneInfo.longitude = newValue?.longitude
+ viewDroneInfo.latitude = newValue?.latitude
+ viewDroneInfo.elevation = Number(newValue?.elevation.toFixed(2)) + droneHeight.value
+}, {deep: true})
+
+
+
+// 生成航线轨迹
+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)
+ }
+}
+
+onMounted(async () => {
+ window.addEventListener('orientationchange', checkOrientation);
+ window.addEventListener('resize', checkOrientation); // 兼容某些设备
+ checkOrientation(); // 初始化时检查一次
+ loadVideo()
+ await nextTick();
+ await _init('cesiumContainerBigMap');
+ await getWrjSsLx();
+
+});
+
+onBeforeUnmount(() => {
+ window.removeEventListener('orientationchange', checkOrientation);
+ window.removeEventListener('resize', checkOrientation);
+ if (player) {
+ player.dispose();
+ }
+ if (flvPlayer) {
+ flvPlayer.pause(); // 暂停播放
+ flvPlayer.unload(); // 卸载视频
+ flvPlayer.detachMediaElement(); // 解绑 video 元素
+ flvPlayer.destroy(); // 销毁播放器
+ flvPlayer = null;
+ }
+});
+
+
+
+</script>
+
+<style lang="scss" scoped>
+.drone-pilot-details {
+ .landscape-box {
+ position: relative;
+ display: flex;
+ .l-map {
+ .l-zp {
+ position: fixed;
+ right: 16%;
+ bottom: 0;
+ }
+ .l-zoom {
+ position: fixed;
+ bottom: 0;
+ .zoom {
+ width: 2rem;
+ height: 2rem;
+ 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 {
+ .el-empty {
+ width: 100%;
+ height: 100%;
+ padding: 0;
+ ::v-deep(.el-empty__image) {
+ width: 50%;
+ height: 50%;
+ }
+ ::v-deep(.el-empty__description) {
+ margin: 0;
+ }
+ }
+ }
+ #cesiumContainerBigMap {
+ width: 100%;
+ height: 100%;
+ overflow: 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%;
+ }
+ }
+ }
+ }
+ }
+ }
+ #cesiumContainerMap {
+ width: 100%;
+ height: 100%;
+ overflow: 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%;
+ }
+ }
+ }
+ }
+ }
+ }
+ .portrait-screen {
+ .l-map {
+ .l-zp {
+ position: fixed;
+ right: 28%;
+ bottom: 0;
+ }
+ .l-zoom {
+ position: fixed;
+ bottom: 0;
+ .zoom {
+ width: 2rem;
+ height: 2rem;
+ 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;
+ }
+ }
+ }
+ }
+ }
+}
+</style>
\ No newline at end of file
diff --git a/src/views/DronePilotDetails-copy3.vue b/src/views/DronePilotDetails-copy3.vue
new file mode 100644
index 0000000..82f8ac0
--- /dev/null
+++ b/src/views/DronePilotDetails-copy3.vue
@@ -0,0 +1,675 @@
+<template>
+ <div class="drone-pilot-details" :style="{ width: screenWidth,height: screenHeight}">
+ <div id="landscapeBox" class="landscape-box" :style="{ width: screenWidth + 'px', height: (screenHeight) + '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>
+ <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="getLocation" />
+ </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 class="right-box">
+ <ptzControl :sn="sn" :osdVisible="sbInfo" />
+ <controlConsole :sn="sn" :osdVisible="sbInfo" :cesiumViewe="globalViewer" />
+ </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 { startLivestream, getLiveVideoUrl } from '@/api/manage'
+import { CURRENT_CONFIG as config } from '@/api/http/config'
+// import videojs from 'video.js'
+// import 'video.js/dist/video-js.css';
+import { getLnglatDist, cartesian3Convert, getLnglatAltitude, createCircleBillboard } from '@/utils/cesium/mapUtil'
+import cesiumOperation from '@/utils/cesium-tsa.js';
+const { _init, viewerDestory } = cesiumOperation();
+import * as Cesium from 'cesium';
+
+// import { cesiumOperation } from '@/hooks/use-cesium-tsa'
+// const { _init } = cesiumOperation();
+import { useMyStore } from '@/store'
+import { getWaylineFile } from '@/api/manage'
+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 { any } from 'video.js/dist/types/utils/events';
+import { analyzeKmzFile, XMLToJSON } from '@/utils/cesium/kmz.js'
+import { initPointWayLine } from './hooks/initPointWayline'
+import { initPlanarWayline } from './hooks/initPlanarWayline'
+import { droneFly } from './hooks/droneFly'
+import flvjs from 'flv.js';
+const { pointWayline } = initPointWayLine()
+const { planarWayline } = initPlanarWayline()
+const { initDock } = 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 screenWidthMap = ref((window.innerHeight/2)*2)// ref(window.innerWidth);
+const screenHeightMap = ref((window.innerHeight/2));
+// 视频切换
+const screenWidthVideo = ref(window.innerWidth);
+const screenHeightVideo = ref(window.innerHeight/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?: { };
+}
+// 获取机场/遥控器sn
+let localData = ref<any>(window.localStorage.getItem('sbInfo'))
+const sbInfo: deviceInfo = JSON.parse(localData.value) as deviceInfo;
+
+// 无人机sn
+const sn = ref<String>(sbInfo.sn)
+// 机场sn
+const dockSn = ref<String>(sbInfo.dockSn)
+
+// 记录横竖屏模式
+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 meMapSrc = new URL('@/assets/images/me.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 timer = null;
+const videoPlayer = ref<any>(null); // 视频播放器实例
+let webrtcPlayer = null; // video.js 播放器
+
+// 大屏幕视频
+const timerBig = null;
+const videoPlayerBig = ref<any>(null); // 视频播放器实例
+let webrtcPlayerBig = null
+
+let player = null; // video.js 播放器
+
+// 无人机实时飞行链接
+let ssLinePath = ref(null)
+
+// 实时定位
+let viewDroneInfo = {}
+// 机场高度
+let droneHeight = ref(0)
+
+// 监听
+let connectWs = ref<any>(null)
+
+let workspaceId = ref<String>(sbInfo.workspaceId)
+localStorage.setItem('bs_workspace_id', workspaceId.value)
+// store.commit('SET_SELECTED_WORKSPACE_ID', workspaceId.value);
+
+// 自身定位
+let me_latitude = 0;
+let me_longitude = 0;
+
+// 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()
+ })
+}
+
+//===============================
+
+// 监听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)
+ break
+ }
+ case EBizCode.DockOsd: { // 机场
+ store.commit('SET_DOCK_INFO', payload.data)
+ break
+ }
+ }
+
+}
+
+const webSorketUrl = getWebsocketUrl() + '&workspace-id=' + workspaceId.value
+// 监听ws 消息
+useConnectWebSocket(messageHandler, webSorketUrl)
+
+
+let savedMapState = null; // 保存地图状态
+// 销毁之前先保存状态
+const destroyViewer = () => {
+ if ( window.cesiumViewer) {
+ // 保存当前地图状态(如相机位置)
+ // savedMapState = window.cesiumViewer.camera.view;
+ window.cesiumViewer.destroy();
+ window.cesiumViewer = null;
+ }
+}
+
+// 地图全屏切换
+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 = '0';
+ cesiumContainer.style.left = '0';
+ cesiumContainer.style.width = `${screenWidthMap.value * 2}px`
+ cesiumContainer.style.height = `${screenHeightMap.value}px`
+ lZp.style.right = '40%';
+ // lZoom.style.left = '0';
+ } else { // 缩放
+ cesiumContainer.style.width = `${screenWidthMap.value}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 = '0';
+ cesiumContainer.style.left = '0';
+ cesiumContainer.style.width = `${screenWidthMap.value}px`
+ cesiumContainer.style.height = `${screenHeightMap.value * 2}px`
+ lZp.style.right = '30%';
+ // lZoom.style.right = '0';
+ } else { // 缩放
+ console.log('竖屏宽度',screenWidthMap.value)
+ console.log('竖屏高度度',screenHeightMap.value)
+ cesiumContainer.style.width = `${screenWidthMap.value}px`
+ cesiumContainer.style.height = `${screenHeightMap.value}px`
+ cesiumContainer.style.position = 'static';
+ lZp.style.right = '30%';
+ // lZoom.style.right = '0';
+
+ }
+ }
+}
+
+// 更新屏幕尺寸的函数
+// const updateScreenSize = () => {
+// const cesiumContainer = document.getElementById('cesiumContainerBigMap');
+// screenWidth.value = window.innerWidth;
+// screenHeight.value = window.innerHeight;
+// console.log('屏幕自身宽度',window.innerWidth)
+// console.log('屏幕自身高度',window.innerHeight)
+// if (isLandscape.value) { //横屏
+// screenWidthMap.value = window.innerWidth/2
+// screenHeightMap.value = window.innerHeight
+// screenWidthVideo.value = window.innerWidth/2
+// screenHeightVideo.value = window.innerHeight
+// cesiumContainer.style.width = `${screenWidthMap.value}px`
+// cesiumContainer.style.height = `${screenHeightMap.value}px`
+// } else {//竖屏
+// screenWidthMap.value = window.innerHeight
+// screenHeightMap.value = (window.innerHeight/2)
+// screenWidthVideo.value = window.innerWidth
+// screenHeightVideo.value = (window.innerHeight/2)
+// // 重新给赋值宽高
+// cesiumContainer.style.width = `${screenWidthMap.value}px`
+// cesiumContainer.style.height = `${screenHeightMap.value}px`
+// console.log('切换竖屏宽度',screenWidthMap.value)
+// console.log('切换竖屏高度度',screenHeightMap.value)
+// }
+
+// };
+
+const checkOrientation = async () => {
+ screenWidth.value = window.innerWidth;
+ screenHeight.value = window.innerHeight;
+ console.log('屏幕宽度', screenWidth.value)
+ console.log('屏幕高度', screenHeight.value)
+ const landscapeBox = document.getElementById('landscapeBox');
+ let cesiumContainer = document.getElementById('cesiumContainerBigMap');
+ const videoModule = document.getElementById('videoModule');
+ const lZp = document.getElementById('lZp');
+ const lZoom = document.getElementById('lZoom');
+
+ if (window.innerWidth > window.innerHeight) {//横屏
+ screenWidthVideo.value = window.innerWidth/2
+ screenHeightVideo.value = window.innerHeight
+ isLandscape.value = true;
+ // updateScreenSize()
+ landscapeBox.style.display = 'flex';
+ lZp.style.right = '16%';
+ lZoom.style.position = 'fixed';
+ cesiumContainer.style.width = `${screenWidth.value/2}px`
+ cesiumContainer.style.height = `${screenHeight.value}px`
+ } else { //竖屏
+ isLandscape.value = false;
+ screenWidthVideo.value = window.innerWidth
+ screenHeightVideo.value = (window.innerHeight/2)
+ // updateScreenSize()
+ landscapeBox.style.display = 'inherit';
+ landscapeBox.style.overflow = 'hidden';
+ lZp.style.right = '32%';
+ cesiumContainer.style.width = `${screenHeight.value}px`
+ cesiumContainer.style.height = `${screenHeight.value/2}px`
+ }
+};
+
+// 获取实时航线
+const getWrjSsLx = () => {
+ globalViewer = window.cesiumViewer;
+ // ssLinePath.value = 'https://wrj.shuixiongit.com/minio/cloud-bucket/wayline/20250211/wayline_1739254562374.kmz'
+ // generateCourse()
+ // return
+ getWaylineFile(sbInfo.sn).then((res:any) => {
+ if (res.code != 200) return
+ ssLinePath.value = res.data
+ // globalViewer = window.cesiumViewer
+ // 先有航线,再飞行
+ generateCourse()
+ })
+}
+
+// 切换航线轨迹
+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, me_latitude, me_longitude)
+}
+
+// 监听
+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})
+
+
+
+// 生成航线轨迹
+const generateCourse = async () => {
+ // ssLinePath.value = 'https://wrj.shuixiongit.com/minio/cloud-bucket/wayline/20250211/wayline_1739254562374.kmz'
+ 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
+ console.log('看看航线值', sbInfo)
+ if (placemark?.Polygon) {
+ // return 'planar'
+ planarWayline(ssLinePath.value, sbInfo.longitude, sbInfo.latitude)
+ } else {
+ // return 'point'
+ pointWayline(ssLinePath.value, sbInfo.longitude, sbInfo.latitude)
+ }
+}
+
+// 获取自身定位
+const getLocation = () => {
+ if (window.cesiumViewer == null) return
+ // 获取设备朝向
+ if (window.DeviceOrientationEvent) {
+ window.addEventListener('deviceorientation', (event) => {
+ const alpha = event.alpha; // 方位角
+ const beta = event.beta; // 前后倾斜
+ const gamma = event.gamma; // 左右倾斜
+
+ if (alpha !== null && beta !== null && gamma !== null) {
+ updateCesiumCamera(alpha, beta, gamma);
+ }
+ });
+ } else {
+ console.log('Device Orientation API is not supported.');
+ }
+ // if (navigator.geolocation) {
+ // navigator.geolocation.getCurrentPosition(
+ // (position) => {
+ me_latitude = 28.624647955787974;// position.coords.latitude;
+ me_longitude = 115.85635808986208;// position.coords.longitude;
+ // 开始给自己定位
+ getLnglatAltitude(me_longitude, me_latitude, window.cesiumViewer).then(res => {
+ removeById('me_coordinate')
+ window.cesiumViewer.entities.add({
+ position: globalCesium.Cartesian3.fromDegrees(
+ me_longitude,
+ me_latitude,
+ res.height || 0,
+ ),
+ id: 'me_coordinate',
+ billboard: {
+ image: meMapSrc,
+ outlineWidth: 0,
+ width: 36,
+ height: 36,
+ scale: 1.0,
+ }
+ })
+ window.cesiumViewer.scene.camera.setView({
+ destination: globalCesium.Cartesian3.fromDegrees(
+ Number(me_longitude),
+ Number(me_latitude),
+ 10000.0,
+ ),
+ })
+ })
+ // },
+ // (err) => {
+ // console.log('定位有误')
+ // }
+ // );
+ // } else {
+ // console.log('您的浏览器不支持地理定位')
+ // }
+};
+
+onMounted(async () => {
+ window.addEventListener('orientationchange', checkOrientation);
+ window.addEventListener('resize', checkOrientation); // 兼容某些设备
+ checkOrientation(); // 初始化时检查一次
+ loadVideo()
+ await nextTick();
+ await _init('cesiumContainerBigMap');
+ await getWrjSsLx();
+ // await generateCourse()
+ await getLocation()
+
+
+
+});
+
+onBeforeUnmount(() => {
+ window.removeEventListener('orientationchange', checkOrientation);
+ window.removeEventListener('resize', checkOrientation);
+ if (player) {
+ player.dispose();
+ }
+});
+
+
+
+</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 {
+ .el-empty {
+ width: 100%;
+ height: 100%;
+ padding: 0;
+ ::v-deep(.el-empty__image) {
+ width: 50%;
+ height: 50%;
+ }
+ ::v-deep(.el-empty__description) {
+ margin: 0;
+ }
+ }
+ }
+ #cesiumContainerBigMap {
+ width: 100%;
+ height: 100%;
+ margin: 0;
+ padding: 0;
+ overflow: 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%;
+ }
+ }
+ }
+ }
+ }
+ }
+ .right-box {}
+}
+</style>
\ No newline at end of file
diff --git a/src/views/DronePilotDetails.vue b/src/views/DronePilotDetails.vue
index ad9d030..b3ce7be 100644
--- a/src/views/DronePilotDetails.vue
+++ b/src/views/DronePilotDetails.vue
@@ -1,12 +1,21 @@
<template>
<div class="drone-pilot-details" :style="{ width: screenWidth,height: screenHeight}">
- <div id="landscapeBox" class="landscape-box" :style="{ width: screenWidth + 'px', height: (screenHeight) + 'px'}">
+ <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>
@@ -26,10 +35,6 @@
<img v-else src="@/assets/images/suo.png" @touchstart="changeModelMap" />
</div>
</div>
- </div>
- <div class="right-box">
- <ptzControl :sn="sn" :osdVisible="sbInfo" />
- <controlConsole :sn="sn" :osdVisible="sbInfo" :cesiumViewe="globalViewer" />
</div>
</div>
</div>
@@ -84,12 +89,15 @@
// 使用 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((window.innerHeight/2)*2)// ref(window.innerWidth);
-const screenHeightMap = ref((window.innerHeight/2));
+const screenWidthMap = ref(boxWidth.value)// ref(((boxHeight.value)/2)*2)
+const screenHeightMap = ref(((boxHeight.value)/2));
// 视频切换
-const screenWidthVideo = ref(window.innerWidth);
-const screenHeightVideo = ref(window.innerHeight/2);
+const screenWidthVideo = ref(boxWidth.value);
+const screenHeightVideo = ref(boxHeight.value/2);
const isBigMap = ref(false)
// 显示高德地图矢量还是影像(2D/3D)
@@ -147,12 +155,10 @@
const drawCanvasHeight = ref<Number>(0)
// 鹰眼视频
-const timer = null;
-const videoPlayer = ref<any>(null); // 视频播放器实例
-let webrtcPlayer = null; // video.js 播放器
+// const timer = null;
// 大屏幕视频
-const timerBig = null;
+// const timerBig = null;
const videoPlayerBig = ref<any>(null); // 视频播放器实例
let webrtcPlayerBig = null
@@ -255,7 +261,6 @@
// 获取实时航线
const getWrjSsLx = () => {
if (window.cesiumViewer== null) return
- globalViewer = window.cesiumViewer;
getWaylineFile(sbInfo.sn).then((res:any) => {
if (res.code != 200) return
ssLinePath.value = res.data
@@ -294,6 +299,7 @@
case EBizCode.DeviceOsd: {
store.commit('SET_DEVICE_INFO', payload.data)
store.commit('SET_WS_MESSAGE', payload)
+ getviewDrone(payload)
break
}
case EBizCode.DockOsd: { // 机场
@@ -302,10 +308,9 @@
}
case EBizCode.FlightTaskProgress: { // 获取进度
// 如果点击列表进入没有获取到航线文件,那么这里重新请求一次
- console.log('航线有么111?',ssLinePath.value)
- if (!ssLinePath || !ssLinePath.value) {
- getWrjSsLx()
- }
+ // if (!ssLinePath || !ssLinePath.value) {
+ // getWrjSsLx()
+ // }
break
}
}
@@ -316,17 +321,45 @@
// 监听ws 消息
useConnectWebSocket(messageHandler, webSorketUrl)
-
-let savedMapState = null; // 保存地图状态
-// 销毁之前先保存状态
-const destroyViewer = () => {
- if ( window.cesiumViewer) {
- // 保存当前地图状态(如相机位置)
- // savedMapState = window.cesiumViewer.camera.view;
- window.cesiumViewer.destroy();
- window.cesiumViewer = null;
+// 定义方法直接监听机场消息
+const getviewDrone = (newValue) => {
+ 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
}
}
+
+
+// let savedMapState = null; // 保存地图状态
+// // 销毁之前先保存状态
+// const destroyViewer = () => {
+// if ( window.cesiumViewer) {
+// // 保存当前地图状态(如相机位置)
+// // savedMapState = window.cesiumViewer.camera.view;
+// window.cesiumViewer.destroy();
+// window.cesiumViewer = null;
+// }
+// }
+
+// 视频全屏切换
+
+// const changeModelVideo = (value:Boolean) => {
+// const videoModule = document.getElementById('videoModule');
+// // 不管是否竖/横屏都一样
+// if (value) { // 全屏
+// videoModule.style.width = `${screenWidth.value}px`
+// videoModule.style.height = `${screenHeight.value}px`
+// } else {
+// if (isLandscape.value) {
+// videoModule.style.width = `${screenWidth.value/2}px`
+// videoModule.style.height = `${screenHeight.value}px`
+// } else {
+// videoModule.style.height = `${screenHeight.value/2}px`
+// videoModule.style.width = `${screenWidth.value}px`
+// }
+// }
+// }
// 地图全屏切换
const changeModelMap = () => {
@@ -337,34 +370,32 @@
if (isLandscape.value) { // 横屏
if (isBigMap.value) { // 全屏
cesiumContainer.style.position = 'fixed';
- cesiumContainer.style.top = '0';
+ cesiumContainer.style.top = '46px';
cesiumContainer.style.left = '0';
- cesiumContainer.style.width = `${screenWidthMap.value * 2}px`
- cesiumContainer.style.height = `${screenHeightMap.value}px`
+ cesiumContainer.style.width = `${boxWidth.value}px`
+ // cesiumContainer.style.height = `${screenHeightMap.value}px`
lZp.style.right = '40%';
// lZoom.style.left = '0';
} else { // 缩放
- cesiumContainer.style.width = `${screenWidthMap.value}px`
- cesiumContainer.style.height = `${screenHeightMap.value}px`
+ 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) { // 全屏
+ if (isBigMap.value) { // 竖-全屏
cesiumContainer.style.position = 'fixed';
- cesiumContainer.style.top = '0';
+ cesiumContainer.style.top = '46px';
cesiumContainer.style.left = '0';
- cesiumContainer.style.width = `${screenWidthMap.value}px`
- cesiumContainer.style.height = `${screenHeightMap.value * 2}px`
+ // cesiumContainer.style.width = `${boxHeight.value}px`
+ cesiumContainer.style.height = `${boxHeight.value}px`
lZp.style.right = '30%';
// lZoom.style.right = '0';
} else { // 缩放
- console.log('竖屏宽度',screenWidthMap.value)
- console.log('竖屏高度度',screenHeightMap.value)
- cesiumContainer.style.width = `${screenWidthMap.value}px`
- cesiumContainer.style.height = `${screenHeightMap.value}px`
+ 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';
@@ -402,39 +433,36 @@
// };
const checkOrientation = async () => {
- screenWidth.value = window.innerWidth;
- screenHeight.value = window.innerHeight;
- console.log('屏幕宽度', screenWidth.value)
- console.log('屏幕高度', screenHeight.value)
+ boxWidth.value = window.innerWidth;
+ boxHeight.value = window.innerHeight - 46;
const landscapeBox = document.getElementById('landscapeBox');
let cesiumContainer = document.getElementById('cesiumContainerBigMap');
- const videoModule = document.getElementById('videoModule');
const lZp = document.getElementById('lZp');
const lZoom = document.getElementById('lZoom');
if (window.innerWidth > window.innerHeight) {//横屏
- screenWidthVideo.value = window.innerWidth/2
- screenHeightVideo.value = 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 = `${screenWidth.value/2}px`
- cesiumContainer.style.height = `${screenHeight.value}px`
+ 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 = window.innerWidth
- screenHeightVideo.value = (window.innerHeight/2)
+ 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 = `${screenHeight.value}px`
- cesiumContainer.style.height = `${screenHeight.value/2}px`
+ cesiumContainer.style.width = `${boxHeight.value}px`
+ cesiumContainer.style.height = `${boxHeight.value/2}px`
// cesiumContainer.style.position = 'absolute'
cesiumContainer.style.marginLeft = '-32%'
}
@@ -501,17 +529,26 @@
}
}, {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})
+// 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);
@@ -520,16 +557,16 @@
loadVideo()
await nextTick();
await _init('cesiumContainerBigMap');
+ await getGlobalViewer()
await getWrjSsLx();
// 判断是遥控器还是机场
let domain = sessionStorage.getItem('domain')
if (domain !== '0') {
await getPhoneLocation(workspaceId.value)
- clickPhoneLocation()
// // // 设置定时器,每隔 5 秒刷新一次数据
- intervalId = setInterval(()=> {
- getPhoneLocation(workspaceId.value)
- }, 5000);
+ // intervalId = setInterval(()=> {
+ // getPhoneLocation(workspaceId.value)
+ // }, 5000);
}
});
@@ -611,6 +648,7 @@
transform: translate(-50%, -50%);
justify-content: center;
}
+ .right-box {}
}
#cesiumContainerBigMap {
width: 100%;
@@ -641,6 +679,6 @@
}
}
}
- .right-box {}
+
}
</style>
\ No newline at end of file
diff --git a/src/views/DronePilotList.vue b/src/views/DronePilotList.vue
index f677d5a..ed47927 100644
--- a/src/views/DronePilotList.vue
+++ b/src/views/DronePilotList.vue
@@ -1,5 +1,10 @@
<template>
<div class="drone-pilot-list-warp">
+ <van-nav-bar
+ title="机场/遥控器"
+ left-arrow
+ @click-left="onClickLogin"
+ />
<div class="drone-pilot-list">
<template v-if="droneOrPilotTxt == 'drone'">
<div class="box-info" v-for="item in cardList" :key="item.sn">
@@ -103,6 +108,10 @@
}
+const onClickLogin = () => {
+ history.back();
+}
+
onMounted(() => {
init()
// 设置定时器,每隔 3 秒刷新一次数据
@@ -123,7 +132,7 @@
background-color: #b8b3b338;
// background-image: url('@/assets/images/newcon_box.png');
.drone-pilot-list {
- height: calc(100vh - 10%);
+ height: calc(100vh - 50px - 46px);
overflow: auto;
.box-info {
background-color: #fff;
diff --git a/src/views/components/controlConsole.vue b/src/views/components/controlConsole.vue
index ca5f142..bed1403 100644
--- a/src/views/components/controlConsole.vue
+++ b/src/views/components/controlConsole.vue
@@ -205,9 +205,9 @@
.control-console {
.control {
- position: fixed;
- top: 0.2rem;
- left: 0.2rem;
+ position: absolute;
+ bottom: 12.8rem;
+ right: 0.2rem;
width: 2rem;
height: 2rem;
border-radius: 3px;
diff --git a/src/views/components/ptzControl-copy.vue b/src/views/components/ptzControl-copy.vue
new file mode 100644
index 0000000..62b818d
--- /dev/null
+++ b/src/views/components/ptzControl-copy.vue
@@ -0,0 +1,564 @@
+<template>
+ <div class="ptz-control">
+ <!-- <div class="ptz" :class="isPtz?'actived-blue':''">
+ <img src="@/assets/images/yt.png" @touchstart="ptzClick" />
+ </div> -->
+ <div class="btn-group">
+ <div class="btn-item" :class="isPtz?'actived-blue':''" @touchstart="ptzClick">
+ <img src="@/assets/images/yt.png" />
+ </div>
+ <div class="btn-item" @touchstart="onTakeCameraPhoto">
+ <img src="@/assets/images/photo.png" />
+ </div>
+ <div class="btn-item" v-if="!isCameraRecording" @touchstart="onStartCameraRecording">
+ <img src="@/assets/images/sx-open.png" />
+ </div>
+ <div class="btn-item" v-else @touchstart="onStopCameraRecording">
+ <img src="@/assets/images/sx-close.png"/>
+ </div>
+ <div class="btn-item" @touchstart="onZoom(true)">
+ <img src="@/assets/images/fangda.png" />
+ </div>
+ <div class="btn-item" @touchstart="onZoom(false)">
+ <img src="@/assets/images/suoxiao.png" />
+ </div>
+ <!-- <div class="btn-item">
+ <img v-if="!isBigVideo" src="@/assets/images/fang.png" @touchstart="changeModelVideo(true)" />
+ <img v-else src="@/assets/images/suo.png" @touchstart="changeModelVideo(false)" />
+ </div> -->
+ </div>
+ <div class="circle-box" v-show="isPtz">
+ <div class="top" @click="ytClick('up')" @mousedown="onMouseDown(KeyCode.ARROW_UP)" @mouseup="onMouseUp"
+ @touchstart="onMouseDown(KeyCode.ARROW_UP)" @touchend="onMouseUp" @mouseleave="onMouseUp"></div>
+ <div class="left" @click="ytClick('left')" @mousedown="onMouseDown(KeyCode.ARROW_LEFT)" @mouseup="onMouseUp"
+ @touchstart="onMouseDown(KeyCode.ARROW_LEFT)" @touchend="onMouseUp" @mouseleave="onMouseUp"></div>
+ <div class="bottom" @click="ytClick('down')" @mousedown="onMouseDown(KeyCode.ARROW_DOWN)" @mouseup="onMouseUp"
+ @touchstart="onMouseDown(KeyCode.ARROW_DOWN)" @touchend="onMouseUp" @mouseleave="onMouseUp"></div>
+ <div class="right" @click="ytClick('right')" @mousedown="onMouseDown(KeyCode.ARROW_RIGHT)" @mouseup="onMouseUp"
+ @touchstart="onMouseDown(KeyCode.ARROW_RIGHT)" @touchend="onMouseUp" @mouseleave="onMouseUp"></div>
+ <div class="reset-center" @click="onResetGimbal(0)">
+ <el-icon><Refresh /></el-icon>
+ </div>
+ <div class="blue-bgc"></div>
+ </div>
+ </div>
+</template>
+
+<script lang="ts" setup>
+import { ElMessage } from 'element-plus'
+import { Refresh } from '@element-plus/icons-vue';
+import { ControlSource, EModeCode } from '@/types/device'
+import { CameraMode } from '@/types/live-stream'
+import { ref, onMounted, onBeforeUnmount, nextTick, reactive, computed, watch } from 'vue';
+import { postPayloadCommands, postPayloadAuth } from '@/api/drone/payload'
+import { switchLivestream } from '@/api/drone'
+import { usePayloadControl } from './use-payload-control'
+import { useDroneControlWsEvent } from '@/utils/websocket/drone-ws-control'
+import cesiumOperation from '@/utils/cesium-tsa.js';
+import { useMyStore } from '@/store'
+import { fa } from 'element-plus/es/locale';
+
+const { loadLAYER } = cesiumOperation()
+
+const {
+ checkPayloadAuth,
+ authPayload,
+ resetGimbal,
+ switchCameraMode,
+ takeCameraPhoto,
+ startCameraRecording,
+ stopCameraRecording,
+ changeCameraFocalLength,
+} = usePayloadControl()
+
+const props = defineProps({
+ sn: {
+ type: String,
+ required: true,
+ },
+ osdVisible: {
+ type: Object,
+ required: true,
+ },
+ cesiumViewe: {
+ type: Object,
+ required: true,
+ }
+});
+const emitEvents = defineEmits(['changeModelVideo'])
+
+// 放大和缩小
+// let isBigVideo = ref<Boolean>(false)
+
+// 录像
+let isCameraRecording = ref<Boolean>(false)
+//
+let isActionComplete = ref<Boolean>(false)
+
+let payloadCameraMode = ref<String>('wide')
+
+// 缩放监听更新值
+let zoom_factor_watch = ref(0)
+// 缩放初始值
+let zoom_factor_start = ref(0)
+// 缩放变化值
+let zoom_factor = ref(0)
+// is_First 记录第一次
+let is_First = ref<Boolean>(false)
+
+// 是否显示云台图标
+let isPtz = ref(false)
+
+// 显示高德地图矢量还是影像(2D/3D)
+// let is2d = ref(true)
+
+let yaw_speed = ref((payloadCameraMode.value == 'wide'? 1 : Math.ceil(zoom_factor.value))*10) // 云台横向角度
+let pitch_speed = ref((payloadCameraMode.value == 'wide'? 1 : Math.ceil(zoom_factor.value))*10) // 云台纵向角度
+
+let resultProps = reactive<any>(props.osdVisible.children.payloads_list[0])
+// console.log(resultProps, '现在勒')
+const store = useMyStore()
+// 负载控制
+const payloadSelectInfo = reactive<any>({
+ value: resultProps.payload_sn,
+ controlSource: resultProps.control_source,
+ options: [],
+ payloadIndex: resultProps.payload_index,
+ camera: '', // 当前负载osd信息
+})
+
+watch(() => store.state.wsMessage, (newValue, oldValue) => {
+ if (newValue && newValue.cameras) {
+ // console.log('监控值', newValue.cameras)
+ zoom_factor_watch.value = Math.ceil(newValue.cameras[0].zoom_factor)
+ if (!is_First.value) {
+ zoom_factor.value = zoom_factor_watch.value
+ zoom_factor_start.value = zoom_factor_watch.value
+ is_First.value = true
+ }
+
+ payloadSelectInfo.camera = newValue.cameras.find(
+ (item:any) =>
+ item.payload_index === payloadSelectInfo.payloadIndex,
+ )
+ } else {
+ payloadSelectInfo.camera = undefined
+ }
+},
+{
+ deep: true,
+})
+// ws 消息通知
+const { droneControlSource, payloadControlSource } = useDroneControlWsEvent(props.sn, payloadSelectInfo.value)
+watch(() => payloadControlSource, (controlSource) => {
+ payloadSelectInfo.controlSource = 'A'// controlSource.value
+}, {
+ immediate: true,
+ deep: true
+})
+
+
+// 切换是否出现云台上下左右图
+const ptzClick = () => {
+ isPtz.value = !isPtz.value
+}
+
+// 拍照
+const onTakeCameraPhoto = () => {
+ if (!checkPayloadAuth(payloadSelectInfo.controlSource)) {
+ return
+ }
+
+ if (isActionComplete.value) {
+ ElMessage.warning('当前相机动作暂未完成,请勿点击!!')
+ return
+ }
+ if (payloadSelectInfo.camera?.recording_state) {
+ ElMessage.warning('当前摄像头正在录制视频,请先结束视频录制!!')
+ return
+ }
+
+ if (payloadSelectInfo.camera?.camera_mode !== CameraMode.Photo) {
+ const res = switchCamera(CameraMode.Photo)
+ res.then((response:any) => {
+ if (!response) return
+
+ if (payloadSelectInfo.camera?.camera_mode !== CameraMode.Photo) {
+ curTaskCameraPhoto(2000)
+ } else {
+ curTaskCameraPhoto(0)
+ }
+ })
+
+ return
+ }
+
+ curTaskCameraPhoto(0)
+}
+const curTaskCameraPhoto = (time:any) => {
+ isActionComplete.value = true
+
+ ElMessage({
+ message: '请稍后,正在拍摄中。。。',
+ duration: 2000,
+ })
+ setTimeout(() => {
+ let cameraType = ref('wide')
+ if (payloadCameraMode.value === 'zoom') {
+ cameraType.value = payloadCameraMode
+ }
+ console.log('1111',cameraType.value)
+ takeCameraPhoto(
+ props.osdVisible.dockSn,
+ payloadSelectInfo.payloadIndex,
+ cameraType.value
+ ).then((photoRes:any) => {
+ if (!photoRes) {
+ return
+ }
+
+ isActionComplete.value = false
+ })
+ }, time)
+}
+const switchCamera = (camera_mode:any) => {
+ if (!checkPayloadAuth(payloadSelectInfo.controlSource)) {
+ return
+ }
+ return switchCameraMode(props.osdVisible.dockSn, {
+ payload_index: payloadSelectInfo.payloadIndex,
+ camera_mode: camera_mode,
+ video_type: payloadCameraMode.value,
+ })
+}
+
+// 相机操作
+const onAuthPayload = () => {
+ //负载控制
+ authPayload(props.osdVisible.dockSn, payloadSelectInfo.payloadIndex).then(
+ (payloadRes:any) => {
+ if (payloadRes.code !== 0)
+ return ElMessage.error('获得有效载荷控制失败,请稍后重试!!')
+ payloadControlSource.value = ControlSource.A
+ ElMessage.success('成功获得有效载荷控制')
+ },
+ )
+}
+// 开始录像
+const onStartCameraRecording = () => {
+ if (!checkPayloadAuth(payloadSelectInfo.controlSource)) {
+ return
+ }
+ if (isActionComplete.value) {
+ ElMessage.warning('当前相机动作暂未完成,请勿点击!!')
+ return
+ }
+
+ if (payloadSelectInfo.camera?.recording_state) {
+ return ElMessage.warning(
+ '当前摄像头正在录制视频,请勿重复点击!!',
+ )
+ }
+
+ if (payloadSelectInfo.camera?.camera_mode !== CameraMode.Video) {
+ const res = switchCamera(CameraMode.Video)
+ res.then((response) => {
+ if (!response) return
+ if (payloadSelectInfo.camera?.camera_mode !== CameraMode.Video) {
+ curStartCameraRecording(2000)
+ } else {
+ curStartCameraRecording(0)
+ }
+ })
+
+ return
+ }
+
+ curStartCameraRecording(0)
+}
+
+ const curStartCameraRecording = (time:any) => {
+ isActionComplete.value = true
+
+ ElMessage({
+ message: '请稍后,正在开启视频录制。。。',
+ duration: 2000,
+ })
+
+ setTimeout(() => {
+ startCameraRecording(props.osdVisible.dockSn, payloadSelectInfo.payloadIndex)
+ .then((res) => {
+ if (res.code !== 0)
+ return ElMessage.warning('开始录制失败,请稍后重试!!')
+ ElMessage.success('开始录制成功')
+ // 开始录像
+ isCameraRecording.value = true
+ isActionComplete.value = false
+ }).catch((e) => {
+ isActionComplete.value = false
+ })
+ }, time)
+ }
+
+// 停止录像
+const onStopCameraRecording = () => {
+ if (!checkPayloadAuth(payloadSelectInfo.controlSource)) {
+ return
+ }
+ if (isActionComplete.value) {
+ ElMessage.warning('当前相机动作暂未完成,请勿点击!!')
+ return
+ }
+ isActionComplete.value = true
+ stopCameraRecording(props.osdVisible.dockSn, payloadSelectInfo.payloadIndex)
+ .then((res) => {
+ if (res.code !== 0)
+ return ElMessage.warning('停止录制失败,请稍后重试!!')
+ ElMessage.success('停止录制成功')
+ // 停止录像
+ isCameraRecording.value = false
+ isActionComplete.value = false
+ })
+ .catch((e) => {
+ isActionComplete.value = false
+ })
+}
+
+
+// 缩放
+const onZoom = (value:any) => {
+ if (value) {
+ zoom_factor.value += 1
+ if (zoom_factor.value >= 200) {
+ zoom_factor.value = 200
+ }
+ } else {
+ zoom_factor.value -= 1
+ if (zoom_factor.value <= 2) {
+ zoom_factor.value = 2
+ }
+ console.log('9999', zoom_factor.value)
+ }
+ switchLivestream({
+ video_id: `${props.osdVisible.dockSn}/${payloadSelectInfo.payloadIndex}/normal-0`,
+ video_type: 'zoom',
+ }).then((res:any) => {
+ if (res.data.code != 0) return
+ // 只考虑 广角:wide模式
+ changeCameraFocalLength(props.osdVisible.dockSn, {
+ payload_index: payloadSelectInfo.payloadIndex,
+ camera_type: 'zoom',
+ zoom_factor: zoom_factor.value,
+ }, value)
+ })
+}
+
+// const onChangeD = () => {
+// is2d.value = !is2d.value
+// store.commit('SET_MAP_SETTING_MODE', is2d.value?2:3);
+// loadLAYER();
+// }
+
+// 重置云台角度
+const onResetGimbal = async (resetMode:any) => {
+ try {
+ await resetGimbal(props.osdVisible.dockSn, {
+ payload_index: payloadSelectInfo.payloadIndex,
+ reset_mode: resetMode,
+ })
+ await changeCameraFocalLength(props.osdVisible.dockSn, {
+ payload_index: payloadSelectInfo.payloadIndex,
+ camera_type: 'zoom',
+ zoom_factor: zoom_factor_start.value,
+ })
+ // this.zoomMultiple = 8 // 设置步长
+ } catch (err) {
+ console.log(err)
+ }
+}
+// 记得加loading
+// 云台上 下 左 右
+const ytClick = async (value:String) => {
+ if (payloadCameraMode.value == 'zoom' && zoom_factor.value > 35 && (value == 'up' || value == 'right')) { pitch_speed.value = 350 }
+ if (payloadCameraMode.value == 'zoom' && zoom_factor.value > 35 && (value == 'left' || value == 'down')) { pitch_speed.value = -350 }
+ if (value == 'up' || value == 'down') {
+ postPayloadCommands(props.osdVisible.dockSn, {cmd: 'camera_screen_drag',
+ data: {
+ locked: false,
+ payload_index: payloadSelectInfo.payloadIndex,
+ pitch_speed: value == 'up' ? pitch_speed.value : -pitch_speed.value,
+ yaw_speed: 0,
+ }})
+ } else if ((value == 'left' || value == 'right')) {
+ postPayloadCommands(props.osdVisible.dockSn, {cmd: 'camera_screen_drag',
+ data: {
+ locked: false,
+ payload_index: payloadSelectInfo.payloadIndex,
+ pitch_speed: 0,
+ yaw_speed: value == 'right' ? pitch_speed.value : -pitch_speed.value,
+ }})
+ }
+}
+
+// 视频大小的缩放
+// const changeModelVideo = (value:Boolean) => {
+// isBigVideo.value = value
+// emitEvents('changeModelVideo', value)
+// }
+
+</script>
+
+<style lang="scss" scoped>
+.ptz-control {
+ .btn-group {
+ position: absolute;
+ left: 0.2rem;
+ bottom: 2rem;
+ .btn-item {
+ width: 2rem;
+ height: 2rem;
+ border-radius: 3px;
+ background-color: rgba(0, 0, 0, 0.5);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ overflow: hidden;
+ cursor: pointer;
+ margin-bottom: 0.5rem;
+ pointer-events: all;
+ img {
+ width: 1.6rem;
+ height: 1.6rem;
+ }
+ }
+ .actived-blue {
+ background-image: none;
+ background-color: rgba(23, 124, 198, 0.7);
+ }
+ }
+ .circle-box {
+ width: 100px;
+ height: 100px;
+ border-radius: 50%;
+ overflow: hidden;
+ background-color: rgba(0, 0, 0, 0.5);
+ position: fixed;
+ top: 0;
+ right: 4rem;
+
+ .top,
+ .left,
+ .bottom,
+ .right {
+ width: 10px;
+ height: 10px;
+ position: absolute;
+ cursor: pointer;
+ z-index: 2;
+ }
+
+ .top {
+ top: -2px;
+ left: 50%;
+ transform: translateX(-50%);
+ border-top: 10px solid transparent;
+ border-right: 10px solid transparent;
+ border-left: 10px solid transparent;
+ border-bottom: 10px solid #fff;
+
+ &:hover {
+ &~.blue-bgc {
+ border-top: 50px solid #409eff;
+ }
+ }
+ }
+
+ .left {
+ top: 50%;
+ left: -2px;
+ transform: translateY(-50%);
+ border-top: 10px solid transparent;
+ border-right: 10px solid #fff;
+ border-left: 10px solid transparent;
+ border-bottom: 10px solid transparent;
+
+ &:hover {
+ &~.blue-bgc {
+ border-left: 50px solid #409eff;
+ }
+ }
+ }
+
+ .bottom {
+ bottom: -2px;
+ left: 50%;
+ transform: translateX(-50%);
+ border-top: 10px solid #fff;
+ border-right: 10px solid transparent;
+ border-left: 10px solid transparent;
+ border-bottom: 10px solid transparent;
+
+ &:hover {
+ &~.blue-bgc {
+ border-bottom: 50px solid #409eff;
+ }
+ }
+ }
+
+ .right {
+ top: 50%;
+ right: -2px;
+ transform: translateY(-50%);
+ border-top: 10px solid transparent;
+ border-right: 10px solid transparent;
+ border-left: 10px solid #fff;
+ border-bottom: 10px solid transparent;
+
+ &:hover {
+ &~.blue-bgc {
+ border-right: 50px solid #409eff;
+ }
+ }
+ }
+
+ .reset-center {
+ position: absolute;
+ width: 40%;
+ height: 40%;
+ left: 50%;
+ top: 50%;
+ transform: translate(-50%, -50%);
+ border-radius: 50%;
+ background-color: rgb(63, 66, 68);
+ z-index: 2;
+ cursor: pointer;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ i {
+ color: #fff;
+ font-size: 20px;
+ font-weight: bolder;
+ }
+
+ &:hover {
+ background-color: #409eff;
+ }
+ }
+
+ .blue-bgc {
+ width: 100px;
+ height: 100px;
+ position: absolute;
+ top: 0;
+ left: 0;
+ z-index: 1;
+ border-top: 50px solid transparent;
+ border-right: 50px solid transparent;
+ border-left: 50px solid transparent;
+ border-bottom: 50px solid transparent;
+ }
+ }
+}
+</style>
diff --git a/src/views/components/ptzControl.vue b/src/views/components/ptzControl.vue
index 68f83ae..458f8c0 100644
--- a/src/views/components/ptzControl.vue
+++ b/src/views/components/ptzControl.vue
@@ -1,9 +1,9 @@
<template>
<div class="ptz-control">
- <div class="ptz" :class="isPtz?'actived-blue':''">
- <img src="@/assets/images/yt.png" @touchstart="ptzClick" />
- </div>
<div class="btn-group">
+ <div class="btn-item" :class="isPtz?'actived-blue':''" @touchstart="ptzClick">
+ <img src="@/assets/images/yt.png" />
+ </div>
<div class="btn-item" @touchstart="onTakeCameraPhoto">
<img src="@/assets/images/photo.png" />
</div>
@@ -19,20 +19,16 @@
<div class="btn-item" @touchstart="onZoom(false)">
<img src="@/assets/images/suoxiao.png" />
</div>
- <!-- <div class="btn-item" @touchstart="onChangeD">
- <img v-if="is2d" src="@/assets/images/stand.png" />
- <img v-else src="@/assets/images/satellite.png" />
+ <!-- <div class="btn-item">
+ <img v-if="!isBigVideo" src="@/assets/images/fang.png" @touchstart="changeModelVideo(true)" />
+ <img v-else src="@/assets/images/suo.png" @touchstart="changeModelVideo(false)" />
</div> -->
</div>
<div class="circle-box" v-show="isPtz">
- <div class="top" @click="ytClick('up')" @mousedown="onMouseDown(KeyCode.ARROW_UP)" @mouseup="onMouseUp"
- @touchstart="onMouseDown(KeyCode.ARROW_UP)" @touchend="onMouseUp" @mouseleave="onMouseUp"></div>
- <div class="left" @click="ytClick('left')" @mousedown="onMouseDown(KeyCode.ARROW_LEFT)" @mouseup="onMouseUp"
- @touchstart="onMouseDown(KeyCode.ARROW_LEFT)" @touchend="onMouseUp" @mouseleave="onMouseUp"></div>
- <div class="bottom" @click="ytClick('down')" @mousedown="onMouseDown(KeyCode.ARROW_DOWN)" @mouseup="onMouseUp"
- @touchstart="onMouseDown(KeyCode.ARROW_DOWN)" @touchend="onMouseUp" @mouseleave="onMouseUp"></div>
- <div class="right" @click="ytClick('right')" @mousedown="onMouseDown(KeyCode.ARROW_RIGHT)" @mouseup="onMouseUp"
- @touchstart="onMouseDown(KeyCode.ARROW_RIGHT)" @touchend="onMouseUp" @mouseleave="onMouseUp"></div>
+ <div class="top" @click="ytClick('up')"></div>
+ <div class="left" @click="ytClick('left')"></div>
+ <div class="bottom" @click="ytClick('down')"></div>
+ <div class="right" @click="ytClick('right')"></div>
<div class="reset-center" @click="onResetGimbal(0)">
<el-icon><Refresh /></el-icon>
</div>
@@ -53,6 +49,7 @@
import { useDroneControlWsEvent } from '@/utils/websocket/drone-ws-control'
import cesiumOperation from '@/utils/cesium-tsa.js';
import { useMyStore } from '@/store'
+import { fa } from 'element-plus/es/locale';
const { loadLAYER } = cesiumOperation()
@@ -81,6 +78,10 @@
required: true,
}
});
+const emitEvents = defineEmits(['changeModelVideo'])
+
+// 放大和缩小
+// let isBigVideo = ref<Boolean>(false)
// 录像
let isCameraRecording = ref<Boolean>(false)
@@ -238,7 +239,6 @@
}
// 开始录像
const onStartCameraRecording = () => {
- console.log('设施',payloadSelectInfo.controlSource)
if (!checkPayloadAuth(payloadSelectInfo.controlSource)) {
return
}
@@ -393,40 +393,20 @@
}
}
+// 视频大小的缩放
+// const changeModelVideo = (value:Boolean) => {
+// isBigVideo.value = value
+// emitEvents('changeModelVideo', value)
+// }
</script>
<style lang="scss" scoped>
.ptz-control {
- .ptz {
- position: fixed;
- top: 3rem;
- left: 0.2rem;
- width: 2rem;
- height: 2rem;
- border-radius: 3px;
- background-color: rgba(0, 0, 0, 0.5);
- display: flex;
- align-items: center;
- justify-content: center;
- overflow: hidden;
- cursor: pointer;
- margin-bottom: 1rem;
- pointer-events: all;
- img {
- width: 1.6rem;
- height: 1.6rem;
- }
- }
- .actived-blue {
- background-image: none;
- background-color: rgba(23, 124, 198, 0.7);
- }
.btn-group {
- position: fixed;
- top: 5.8rem;
- left: 0.2rem;
- // padding: 4px;
+ position: absolute;
+ right: 0.2rem;
+ bottom: 0rem;
.btn-item {
width: 2rem;
height: 2rem;
@@ -437,12 +417,16 @@
justify-content: center;
overflow: hidden;
cursor: pointer;
- margin-bottom: 1rem;
+ margin-bottom: 0.5rem;
pointer-events: all;
img {
width: 1.6rem;
height: 1.6rem;
}
+ }
+ .actived-blue {
+ background-image: none;
+ background-color: rgba(23, 124, 198, 0.7);
}
}
.circle-box {
@@ -452,8 +436,8 @@
overflow: hidden;
background-color: rgba(0, 0, 0, 0.5);
position: fixed;
- top: 0;
- right: 4rem;
+ top: 20%;
+ left: 1%;
.top,
.left,
diff --git a/src/views/hooks/droneFly-copy.ts b/src/views/hooks/droneFly-copy.ts
new file mode 100644
index 0000000..c49b3da
--- /dev/null
+++ b/src/views/hooks/droneFly-copy.ts
@@ -0,0 +1,664 @@
+
+import { EDeviceTypeName, EHmsLevel } from '@/types/enums'
+import cesiumOperation from '@/utils/cesium-tsa'
+import { analyzeKmzFile, XMLToJSON } from '@/utils/cesium/kmz.js'
+import IndexedDBService from '@/utils/indexDB'
+import CreateFrustum from '@/utils/cesium/frustum/CreateFrustum'
+import { useMyStore } from '@/store'
+import { getTwoPointInfo } from '@/api/manage'
+
+const { getEntityById, removeById, globalCesium, addLeftClickEvent, removeLeftClickEvent, flyTo } = cesiumOperation()
+// 无人机实体
+let droneEntity = null
+
+// 激光实体
+let laserEntity = null
+
+// 两点之间线实体
+let twoLineEntity = null
+let myAppEntity = null;
+export function droneFly () {
+ const store = useMyStore()
+ let deviceInfo = {} // 无人机信息
+ let dockInfo = {} // 机场信息
+ let previousPositions = []
+ let wId = ''
+ let snCode = []
+ let viewDroneInfo = {} // 获取实时经纬度
+ let waylinePoints = [] // 当前航线点
+ let currentTaskCreateTime = null // 当前飞行任务创建时间
+ let switchSaving = true // 有selectedWorkSpaceId 就为true
+ // 航线飞行处于第几个点位
+ let currentWaypointIndex = 0
+ // 速度集合
+ let droneSpeedArr = []
+ let lastTime = null
+ let viewInfoFrustum = null
+
+ // 手机设备定位
+ let me_latitude = 0;
+ let me_longitude = 0;
+ let me_altitude = 0;
+ // 扫射定位
+ let measure_target_longitude = 0;
+ let measure_target_latitude = 0;
+ let measure_target_altitude = 0;
+
+
+ async function initDock(data:any, sn:any, dockSn:any, workspaceId:any, ssLinePath:any, viewDroneInfo:any) {
+ // 获取workspaceId
+ wId = workspaceId
+ // 给无人机参数赋值
+ deviceInfo = data.deviceInfo[`${sn}`]
+ // 机场信息
+ dockInfo = data.dockInfo[`${dockSn}`]
+ viewDroneInfo = viewDroneInfo
+ console.log('导致地图不显示原因1',viewDroneInfo.longitude)
+ // 当前飞行任务创建时间
+ if (data.currentType === EDeviceTypeName.Aircraft && deviceInfo) {
+ const droneLngLat = {
+ latitude: deviceInfo.latitude || 0,
+ longitude: deviceInfo.longitude || 0,
+ }
+ // 当飞机模型存在
+ if (droneEntity) {
+ // 且在空中飞行时,实时更新飞机模型的位置
+ if (deviceInfo.mode_code && [3, 4, 5, 9, 10, 16].includes(deviceInfo.mode_code)) {
+ if (store.currentAngle == 3) {
+ viewInfoFrustum?.clear()
+ } else {
+ initCreateFrustum(deviceInfo, viewDroneInfo)
+ }
+ addFlyGltf(
+ viewDroneInfo.longitude,
+ viewDroneInfo.latitude,
+ deviceInfo.height,
+ deviceInfo.payloads[0],
+ deviceInfo.attitude_head,
+ deviceInfo,
+ dockInfo,
+ ssLinePath
+ )
+ let domain = sessionStorage.getItem('domain')
+ if (domain !== '0') {
+ addFlyLaser(
+ deviceInfo.payloads[0]
+ )
+ }
+ setelCamera(
+ viewDroneInfo.longitude,
+ viewDroneInfo.latitude,
+ deviceInfo.height,
+ deviceInfo.payloads[0],
+ deviceInfo.attitude_head,
+ )
+ } else {
+ removeById('drone_fly')
+ droneEntity = null
+
+ lastTime = null
+ viewInfoFrustum?.clear()
+ viewInfoFrustum = null
+
+ window.cesiumViewer.camera.lookAtTransform(globalCesium.Matrix4.IDENTITY)
+ store.commit('SET_CURRENTANGLE', 1)
+ }
+ } else {
+ if (deviceInfo.mode_code && [3, 4, 5, 9, 10].includes(deviceInfo.mode_code)) {
+ initCreateFrustum(deviceInfo, viewDroneInfo)
+ addFlyGltf(
+ viewDroneInfo.longitude,
+ viewDroneInfo.latitude,
+ deviceInfo.height,
+ deviceInfo.payloads[0],
+ deviceInfo.attitude_head,
+ deviceInfo,
+ dockInfo,
+ ssLinePath
+ )
+ let domain = sessionStorage.getItem('domain')
+ if (domain !== '0') {
+ addFlyLaser(
+ deviceInfo.payloads[0]
+ )
+ }
+ }
+ }
+ const imgRightBlue = new URL('@/assets/images/arrow-right-pink.png', import.meta.url).href
+ // 无人机路线轨迹,判断是否存在drone_route_remove
+ if (getEntityById('drone_route_remove')) {
+ window.cesiumViewer.entities.add({
+ name: 'drone_route_remove',
+ id: 'drone_route_remove',
+ polyline: {
+ width: 7,
+ positions: previousPositions,
+ material: new ImageTrailMaterial({
+ color: globalCesium.Color.ORANGE,
+ speed: 10,
+ image: imgRightBlue,
+ repeat: { x: 1, y: 0 },
+ }),
+ clampToGround: false,
+ },
+ })
+ }
+
+ if (data.deviceInfo[data.currentSn].mode_code && [3, 4, 5, 9, 10].includes(deviceInfo.mode_code)) {
+ if (switchSaving) return (switchSaving = false)
+ // console.log('5555', data.deviceInfo[data.currentSn].longitude)
+ const newPositions = globalCesium.Cartesian3.fromDegrees(
+ data.deviceInfo[data.currentSn].longitude,
+ data.deviceInfo[data.currentSn].latitude,
+ Math.floor(viewDroneInfo.elevation),
+ )
+ //若刷新,恢复无人机之前的路线轨迹
+ const dataIndexedDB = await IndexedDBService.getDataById(wId)
+ if (previousPositions.length == 0 && dataIndexedDB) {
+ previousPositions = previousPositions.concat(dataIndexedDB?.temporaryflightData)
+ } else {
+ previousPositions = previousPositions.concat(newPositions)
+ }
+ // 保存无人机当前执行的路线,防止刷新数据丢失
+ if (!dataIndexedDB) {
+ await IndexedDBService.addData({
+ id: wId,
+ temporaryflightData: previousPositions,
+ })
+ } else {
+ await IndexedDBService.updateData(wId, {temporaryflightData: previousPositions})
+ }
+ // 更新线段的位置
+ const drone_route_remove = getEntityById('drone_route_remove')
+ if (!drone_route_remove) return
+ drone_route_remove.polyline.positions = previousPositions
+ drone_route_remove.polyline._material._repeat._value.x = Math.floor(previousPositions.length / 7)
+ } else if ([0, 14].includes(deviceInfo.mode_code)) {
+ previousPositions = []
+ await IndexedDBService.deleteData(wId)
+ const drone_route_remove = getEntityById('drone_route_remove')
+ if (!drone_route_remove) return
+ drone_route_remove.polyline.positions = previousPositions
+ removeById('drone_route_remove')
+ viewInfoFrustum?.clear()
+ viewInfoFrustum = null
+ }
+ }
+ if (data.currentType === EDeviceTypeName.Dock && data.dockInfo[data.currentSn]) {
+ if (!deviceInfo?.basic_osd?.longitude || !deviceInfo?.basic_osd?.latitude) return
+ console.log('6666', deviceInfo?.basic_osd?.longitude)
+ if (snCode.includes(data.currentSn)) return
+ snCode.push(data.currentSn)
+ }
+ }
+
+ const imgLaser = new URL('@/assets/images/laser.png', import.meta.url).href
+ // 增加激光扫射点
+ function addFlyLaser(payloads:any) {
+ const startPosition = globalCesium.Cartesian3.fromDegrees(
+ me_longitude, me_latitude, 0
+ )
+ const position = globalCesium.Cartesian3.fromDegrees(
+ payloads.measure_target_longitude, payloads.measure_target_latitude, payloads.measure_target_altitude
+ )
+ measure_target_longitude = payloads.measure_target_longitude;
+ measure_target_latitude = payloads.measure_target_latitude;
+ measure_target_altitude = payloads.measure_target_altitude;
+
+ if (laserEntity && laserEntity != null) {
+ laserEntity.position = new globalCesium.CallbackProperty(function () {
+ return position
+ }, false)
+ // laserEntity.orientation = new globalCesium.CallbackProperty(function () {
+ // return orientation
+ // }, false)
+
+ laserEntity.show = true
+
+ window.cesiumViewer.scene.requestRender()
+ } else {
+ laserEntity = window.cesiumViewer.entities.add({
+ position,
+ id: 'laser_coordinate',
+ billboard: {
+ image: imgLaser,
+ outlineWidth: 0,
+ width: 36,
+ height: 36,
+ scale: 1.0,
+ }
+ })
+ }
+ removeById('route_two_line')
+ twoLineEntity = window.cesiumViewer.entities.add({
+ id: 'route_two_line',
+ polyline: {
+ positions: new globalCesium.CallbackProperty(() => [startPosition, position], false),
+ width: 5,
+ material: new globalCesium.PolylineDashMaterialProperty({
+ color: globalCesium.Color.YELLOW, // 虚线颜色
+ dashLength: 6.0, // 虚线段的长度
+ }),
+ zIndex: 1,
+ clampToGround: false,
+ }
+ })
+ // 增加激光扫射信息
+ laserEntity.label = new globalCesium.LabelGraphics({
+ text: `目标海拔:${Math.ceil(payloads.measure_target_altitude)}m\n测距距离:${Math.round(
+ Math.ceil(payloads.measure_target_distance),
+ )}m`,
+ font: '13px monospace',
+ showBackground: true,
+ horizontalOrigin: globalCesium.HorizontalOrigin.CENTER,
+ verticalOrigin: globalCesium.VerticalOrigin.BOTTOM,
+ disableDepthTestDistance: Number.POSITIVE_INFINITY,
+ pixelOffset: new globalCesium.Cartesian2(0, -40),
+ show: true,
+ })
+ }
+
+ function addFlyGltf (lng:any, lat:any, alt:any, payloads:any, attitude_head:any, deviceInfo:any, dockInfo:any, ssLinePath:any) {
+ const { gimbal_pitch, gimbal_yaw } = payloads
+ const position = globalCesium.Cartesian3.fromDegrees(
+ lng, lat, alt
+ )
+
+ const heading = globalCesium.Math.toRadians(Number(attitude_head)) //135度转弧度
+ const pitch = globalCesium.Math.toRadians(0)
+ const roll = 0
+ const hpr = new globalCesium.HeadingPitchRoll(heading, pitch, roll)
+ const orientation = globalCesium.Transforms.headingPitchRollQuaternion(
+ position,
+ hpr,
+ )
+
+ if (droneEntity && droneEntity != null) {
+ droneEntity.position = new globalCesium.CallbackProperty(function () {
+ return position
+ }, false)
+
+ droneEntity.orientation = new globalCesium.CallbackProperty(function () {
+ return orientation
+ }, false)
+
+ droneEntity.show = true
+
+ window.cesiumViewer.scene.requestRender()
+ } else {
+ droneEntity = window.cesiumViewer.entities.add({
+ show: true,
+ position,
+ id: 'drone_fly',
+ orientation: orientation,
+ model: {
+ uri: '/pilot-h5/gltf/dajiang-processed.gltf', //注意entitits.add方式加载gltf文件时,这里是uri,不是url,并且这种方式只能加载.glb格式的文件
+ scale: 1, //缩放比例
+ minimumPixelSize: 64, //最小像素大小,可以避免太小看不见
+ maximumScale: 128, //模型的最大比例尺大小。minimumPixelSize的上限
+ incrementallyLoadTextures: true, //加载模型后纹理是否可以继续流入
+ runAnimations: true, //是否启动模型中制定的gltf动画
+ clampAnimations: true, //制定gltf动画是否在没有关键帧的持续时间内保持最后一个姿势
+ shadows: globalCesium.ShadowMode.ENABLED,
+ heightReference: globalCesium.HeightReference.NONE,
+ },
+ })
+ }
+
+ // 创建无人机信息
+ createDroneEntityLabel(droneEntity, deviceInfo, dockInfo, ssLinePath)
+ }
+ // 创建无人机信息
+ async function createDroneEntityLabel (entity:any, deviceInfo:any, dockInfo:any, ssLinePath:any) {
+ deviceInfo = deviceInfo
+ if (currentTaskCreateTime === null) return
+ if (!deviceInfo?.longitude && !deviceInfo?.latitude) return
+ if (deviceInfo.mode_code !== 5) {
+ currentTaskCreateTime = null
+ return
+ }
+ const executingWayline = store.executingWayline
+ if (!executingWayline?.id) return
+ if (waylinePoints.length === 0) {
+ // 改成获取航线地址
+ // const routeRes = await getRoutePlanningUrl(
+ // wId,
+ // executingWayline.id,
+ // )
+ // const routeData = routeRes.data
+ // if (routeData.code !== 0) return this.$message.error('获取航线信息失败!')
+ waylinePoints = await getWaylineFilePoints(ssLinePath)
+ }
+
+ const nextPoint = waylinePoints[currentWaypointIndex]
+ if (!nextPoint) return
+ // 两点之间的距离
+ console.log('8888', deviceInfo.longitude)
+ const devicePosition = globalCesium.Cartesian3.fromDegrees(
+ Number(deviceInfo.longitude),
+ Number(deviceInfo.latitude),
+ 0,
+ )
+ console.log('9999', nextPoint.longitude)
+ const targetPosition = globalCesium.Cartesian3.fromDegrees(
+ Number(nextPoint.longitude),
+ Number(nextPoint.latitude),
+ 0,
+ )
+ // 距离下个点位的距离
+ let distance = globalCesium.Cartesian3.distance(
+ devicePosition,
+ targetPosition,
+ )
+ const dockPosition = {
+ longitude: dockInfo?.basic_osd?.longitude,
+ latitude: dockInfo?.basic_osd?.latitude,
+ }
+
+ const distArr = waylinePoints.map((lnglat, index) => {
+ const { longitude, latitude } = lnglat
+ console.log('10000', longitude)
+ // 两点之间的距离
+ let devicePosition = null
+ if (index === 0) {
+ devicePosition = globalCesium.Cartesian3.fromDegrees(
+ Number(dockPosition.longitude),
+ Number(dockPosition.latitude),
+ 0,
+ )
+ } else {
+ console.log('110', prevPoint.longitude)
+ const prevPoint = waylinePoints[index - 1]
+ devicePosition = globalCesium.Cartesian3.fromDegrees(
+ Number(prevPoint.longitude),
+ Number(prevPoint.latitude),
+ 0,
+ )
+ }
+ const targetPosition = globalCesium.Cartesian3.fromDegrees(
+ Number(longitude),
+ Number(latitude),
+ 0,
+ )
+ let distance = globalCesium.Cartesian3.distance(
+ devicePosition,
+ targetPosition,
+ )
+ return Math.round(distance)
+ })
+ // 计算到达点位和之前点位的总距离
+ let sum = null
+ if (distArr.length > 2) {
+ sum = distArr
+ .slice(0, currentWaypointIndex)
+ .reduce((accumulator, currentValue) => accumulator + currentValue, 0)
+ } else {
+ sum = distArr.reduce(
+ (accumulator, currentValue) => accumulator + currentValue,
+ 0,
+ )
+ }
+
+ if (sum == 0) {
+ sum = distArr[0]
+ }
+
+ // 距离机场的位置
+ const homeDistance = Math.floor(deviceInfo.home_distance) || 0
+ // 速度
+ let horizontalSpeed = deviceInfo.horizontal_speed || 0
+ if (!droneSpeedArr.includes(horizontalSpeed) && horizontalSpeed > 1) {
+ droneSpeedArr.push(horizontalSpeed)
+ }
+ // 平均速度
+ const averageSpeed =
+ droneSpeedArr.reduce((sum, current) => sum + current, 0) /
+ droneSpeedArr.length
+ // 已用时间
+ let usedTime = Math.round((new Date().getTime() - currentTaskCreateTime) / 1000) - 30
+ let throughDistance = usedTime * averageSpeed
+
+ if (throughDistance < 0) {
+ throughDistance = 0
+ usedTime = 0
+ }
+ if (isNaN(throughDistance)) {
+ throughDistance = 0
+ }
+ if (isNaN(usedTime)) {
+ usedTime = 0
+ }
+ if (usedTime > 60) {
+ const minute = Math.floor(usedTime / 60)
+ const second = Math.round(usedTime % 60)
+ usedTime = `${minute}m${second}s`
+ } else {
+ usedTime = Math.round(usedTime) + 's'
+ }
+
+ if (horizontalSpeed < 5) {
+ horizontalSpeed = 10
+ }
+ // 预计到达下一个航点时间
+ let arrivalTime = distance / horizontalSpeed
+ if (arrivalTime === Infinity || isNaN(arrivalTime)) {
+ arrivalTime = 0
+ }
+ if (arrivalTime > 60) {
+ const minute = Math.floor(arrivalTime / 60)
+ const second = Math.round(arrivalTime % 60)
+ arrivalTime = `${minute}m${second}s`
+ } else {
+ arrivalTime = Math.round(arrivalTime) + 's'
+ }
+ entity.label = new globalCesium.LabelGraphics({
+ text: `距离机场水平距离:${homeDistance}m\n距离下一个航点:${Math.round(
+ distance,
+ )}m\n预计到达下一航点时间:${arrivalTime}\n本次飞行时间:${usedTime}\n本次航线平面里程:${Math.round(
+ throughDistance,
+ )}m`,
+ font: '13px monospace',
+ showBackground: true,
+ horizontalOrigin: globalCesium.HorizontalOrigin.CENTER,
+ verticalOrigin: globalCesium.VerticalOrigin.BOTTOM,
+ disableDepthTestDistance: Number.POSITIVE_INFINITY,
+ pixelOffset: new globalCesium.Cartesian2(0, -40),
+ show: true,
+ })
+ }
+ async function getWaylineFilePoints(fileUrl:any) {
+ const fileRes = await analyzeKmzFile(fileUrl);
+ const waylineContent = await fileRes['fileInfoObj']?.['wpmz/waylines.wpml'];
+ const xml = XMLToJSON(waylineContent);
+ const placemarkArr = xml?.Document?.Folder.Placemark;
+ const points = placemarkArr.map((placemark) => {
+ const lnglat = placemark.Point.coordinates?.['#text'].split(',');
+ return {
+ longitude: lnglat[0],
+ latitude: lnglat[1],
+ };
+ });
+ return points;
+ }
+
+ function setelCamera (lng, lat, alt, payloads, attitude_head) {
+ const { gimbal_pitch, gimbal_yaw } = payloads
+ let time = 0
+
+ let current = store.currentAngle
+ var heading = globalCesium.Math.toRadians(Number(gimbal_yaw || attitude_head)) // 相机左右方向
+
+ if (current == 1) {
+ lastTime = null
+ droneEntity.show = true
+ window.cesiumViewer.camera.lookAtTransform(globalCesium.Matrix4.IDENTITY)
+ } else if (current == 2) {
+ lastTime = null
+ droneEntity.show = true
+ window.cesiumViewer.camera.lookAtTransform(globalCesium.Matrix4.IDENTITY)
+
+ let center = globalCesium.Cartesian3.fromDegrees(lng, lat)
+
+ window.cesiumViewer.camera.lookAt(center, new globalCesium.Cartesian3(0.0, 0, 1500))
+ } else if (current == 3) {
+ droneEntity.show = false
+
+ let curTime = Date.now()
+ if (lastTime && lastTime != null) {
+ time = (curTime - lastTime) / 1000
+ }
+
+ window.cesiumViewer.camera.lookAtTransform(globalCesium.Matrix4.IDENTITY)
+ let center = globalCesium.Cartesian3.fromDegrees(lng, lat, alt)
+ var pitch = globalCesium.Math.toRadians(pitchAngle({ gimbal_pitch })) // 相机上下方向,负值表示向下看
+
+ window.cesiumViewer.camera.flyTo({
+ destination: center,
+ orientation: {
+ heading,
+ pitch,
+ roll: 0
+ },
+ duration: time,
+ complete () {
+ window.cesiumViewer.camera.lookAt(center, new globalCesium.HeadingPitchRange(
+ heading,
+ pitch,
+ 0.01
+ ))
+ }
+ })
+
+ lastTime = Date.now()
+ }
+ }
+ // pitch
+ function pitchAngle (options) {
+ const gimbal_pitch = Number(options?.gimbal_pitch) || 0
+ return gimbal_pitch
+ }
+ // 创建视锥
+ function initCreateFrustum (deviceInfo:any, test:any) {
+
+ if (!deviceInfo || !deviceInfo.longitude || !deviceInfo.latitude) return
+
+ const attitude_head = 180 + deviceInfo.attitude_head
+
+ const gimbal_pitch = 90 - pitchAngle({ gimbal_pitch: deviceInfo.payloads[0].gimbal_pitch })
+
+ viewInfoFrustum?.clear()
+ viewInfoFrustum = new CreateFrustum(window.cesiumViewer, {
+ position: {
+ longitude: test.longitude,
+ latitude: test.latitude,
+ altitude: deviceInfo.height,
+ },
+ width: 30,
+ height: 30,
+ fov: 20.0,
+ near: 3.0,
+ far: 250.0,
+ roll: gimbal_pitch,
+ pitch: 0,
+ heading: attitude_head,
+ })
+ }
+
+ // 获取手机定位和激光发射定位的高度和距离
+ function getTwoHeightWidth(workspaceId:String) {
+ // 国内地图不会为0或者小于0
+ console.log('自身定位经纬度1', me_latitude)
+ console.log('自身定位经纬度2', me_longitude)
+ console.log('激光扫过定位1', measure_target_latitude)
+ console.log('激光扫过定位2', measure_target_longitude)
+ if (me_latitude <= 0 || me_longitude <= 0 || measure_target_latitude <= 0 || measure_target_longitude <= 0) return
+ getTwoPointInfo(workspaceId,me_latitude,me_longitude,measure_target_latitude,measure_target_longitude).then((res:any) => {
+ const heightDifference = res.data.heightDifference
+ const distance = res.data.distance
+ // 增加激光扫射信息
+ myAppEntity.label = new globalCesium.LabelGraphics({
+ text: `高度:${Math.ceil(heightDifference)}m\n距离:${Math.round(
+ Math.ceil(distance),
+ )}m`,
+ font: '13px monospace',
+ showBackground: true,
+ horizontalOrigin: globalCesium.HorizontalOrigin.CENTER,
+ verticalOrigin: globalCesium.VerticalOrigin.BOTTOM,
+ disableDepthTestDistance: Number.POSITIVE_INFINITY,
+ pixelOffset: new globalCesium.Cartesian2(0, -40),
+ show: true,
+ })
+ })
+ }
+
+ const meMapSrc = new URL('@/assets/images/me.png', import.meta.url).href
+ // 获取手机设备定位
+ function getPhoneLocation(value:Boolean, workspaceId:String) {
+ if (window.cesiumViewer == null) return
+ // 创建一个箭头实体
+ removeById('me_dw')
+ myAppEntity = window.cesiumViewer.entities.add({
+ id: 'me_dw',
+ position: globalCesium.Cartesian3.fromDegrees(0, 0, 0), // 初始位置
+ billboard: {
+ image: meMapSrc, // 箭头图片路径
+ width: 30,
+ height: 30,
+ alignedAxis: globalCesium.Cartesian3.UNIT_Z, // 对齐轴
+ },
+ // orientation: new globalCesium.CallbackProperty(() => {
+ // return globalCesium.Transforms.headingPitchRollQuaternion(
+ // myAppEntity.position.getValue(window.cesiumViewer.clock.currentTime),
+ // new globalCesium.HeadingPitchRoll(heading, 0, 0) // 设置朝向
+ // );
+ // }, false),
+ });
+
+ let heading = 0; // 初始朝向
+ // 获取设备朝向
+ // if (window.DeviceOrientationEvent) {
+ // window.addEventListener('deviceorientation', (event) => {
+ // if (event.alpha !== null) {
+ // heading = globalCesium.Math.toRadians(event.alpha); // 将 alpha 转换为弧度
+ // }
+ // });
+ // } else {
+ // console.log('手机不支持.');
+ // }
+
+ // 获取设备位置
+ if (navigator.geolocation) {
+ navigator.geolocation.watchPosition((position) => {
+ me_longitude = position.coords.longitude;
+ me_latitude = position.coords.latitude;
+ me_altitude = position.coords.altitude || 0;
+
+ // 更新箭头实体的位置
+ myAppEntity.position = globalCesium.Cartesian3.fromDegrees(me_longitude, me_latitude, me_altitude);
+ if (value) {
+ // 将相机定位到设备位置 只有点击图标才可以执行这里
+ window.cesiumViewer.camera.setView({
+ destination: globalCesium.Cartesian3.fromDegrees(me_longitude, me_latitude, me_altitude + 1000), // 相机高度
+ // orientation: {
+ // heading: heading, // 相机朝向
+ // pitch: globalCesium.Math.toRadians(-90), // 俯视角度
+ // roll: 0,
+ // },
+ });
+ }
+ // 查看两条线直接距离和高度
+ getTwoHeightWidth(workspaceId)
+ }, (error) => {
+ console.error('定位有误:', error);
+ });
+ } else {
+ console.log('手机不支持.');
+ }
+ }
+
+
+ return {
+ initDock,
+ getPhoneLocation,
+ }
+}
\ No newline at end of file
diff --git a/src/views/hooks/droneFly.ts b/src/views/hooks/droneFly.ts
index 6fa5560..a42f788 100644
--- a/src/views/hooks/droneFly.ts
+++ b/src/views/hooks/droneFly.ts
@@ -36,16 +36,17 @@
let viewInfoFrustum = null
// 手机设备定位
- let me_latitude = 0;
- let me_longitude = 0;
+ let me_latitude = 0;// 28.624647955787974;
+ let me_longitude = 0;//115.85635808986208;
let me_altitude = 0;
// 扫射定位
- let measure_target_longitude = 0;
- let measure_target_latitude = 0;
+ let measure_target_longitude = 0;//115.85635872364763;
+ let measure_target_latitude = 0;//28.624648387113627;
let measure_target_altitude = 0;
async function initDock(data:any, sn:any, dockSn:any, workspaceId:any, ssLinePath:any, viewDroneInfo:any) {
+ // console.log('globalCesium查看是', globalCesium)
// 获取workspaceId
wId = workspaceId
// 给无人机参数赋值
@@ -53,7 +54,6 @@
// 机场信息
dockInfo = data.dockInfo[`${dockSn}`]
viewDroneInfo = viewDroneInfo
- console.log('导致地图不显示原因1',viewDroneInfo.longitude)
// 当前飞行任务创建时间
if (data.currentType === EDeviceTypeName.Aircraft && deviceInfo) {
const droneLngLat = {
@@ -67,6 +67,7 @@
if (store.currentAngle == 3) {
viewInfoFrustum?.clear()
} else {
+ // 暂时把视锥代码注释掉
initCreateFrustum(deviceInfo, viewDroneInfo)
}
addFlyGltf(
@@ -105,6 +106,7 @@
}
} else {
if (deviceInfo.mode_code && [3, 4, 5, 9, 10].includes(deviceInfo.mode_code)) {
+ // 暂时把视锥代码注释掉
initCreateFrustum(deviceInfo, viewDroneInfo)
addFlyGltf(
viewDroneInfo.longitude,
@@ -125,68 +127,68 @@
}
}
const imgRightBlue = new URL('@/assets/images/arrow-right-pink.png', import.meta.url).href
- // 无人机路线轨迹,判断是否存在drone_route_remove
- if (getEntityById('drone_route_remove')) {
- window.cesiumViewer.entities.add({
- name: 'drone_route_remove',
- id: 'drone_route_remove',
- polyline: {
- width: 7,
- positions: previousPositions,
- material: new ImageTrailMaterial({
- color: globalCesium.Color.ORANGE,
- speed: 10,
- image: imgRightBlue,
- repeat: { x: 1, y: 0 },
- }),
- clampToGround: false,
- },
- })
- }
+ // 暂时把航线屏蔽
+ // 无人机路线轨迹,判断是否存在drone_route_remove
+ // if (getEntityById('drone_route_remove')) {
+ // window.cesiumViewer.entities.add({
+ // name: 'drone_route_remove',
+ // id: 'drone_route_remove',
+ // polyline: {
+ // width: 7,
+ // positions: previousPositions,
+ // material: new ImageTrailMaterial({
+ // color: globalCesium.Color.ORANGE,
+ // speed: 10,
+ // image: imgRightBlue,
+ // repeat: { x: 1, y: 0 },
+ // }),
+ // clampToGround: false,
+ // },
+ // })
+ // }
- if (data.deviceInfo[data.currentSn].mode_code && [3, 4, 5, 9, 10].includes(deviceInfo.mode_code)) {
- if (switchSaving) return (switchSaving = false)
- // console.log('5555', data.deviceInfo[data.currentSn].longitude)
- const newPositions = globalCesium.Cartesian3.fromDegrees(
- data.deviceInfo[data.currentSn].longitude,
- data.deviceInfo[data.currentSn].latitude,
- Math.floor(viewDroneInfo.elevation),
- )
- //若刷新,恢复无人机之前的路线轨迹
- const dataIndexedDB = await IndexedDBService.getDataById(wId)
- if (previousPositions.length == 0 && dataIndexedDB) {
- previousPositions = previousPositions.concat(dataIndexedDB?.temporaryflightData)
- } else {
- previousPositions = previousPositions.concat(newPositions)
- }
- // 保存无人机当前执行的路线,防止刷新数据丢失
- if (!dataIndexedDB) {
- await IndexedDBService.addData({
- id: wId,
- temporaryflightData: previousPositions,
- })
- } else {
- await IndexedDBService.updateData(wId, {temporaryflightData: previousPositions})
- }
- // 更新线段的位置
- const drone_route_remove = getEntityById('drone_route_remove')
- if (!drone_route_remove) return
- drone_route_remove.polyline.positions = previousPositions
- drone_route_remove.polyline._material._repeat._value.x = Math.floor(previousPositions.length / 7)
- } else if ([0, 14].includes(deviceInfo.mode_code)) {
- previousPositions = []
- await IndexedDBService.deleteData(wId)
- const drone_route_remove = getEntityById('drone_route_remove')
- if (!drone_route_remove) return
- drone_route_remove.polyline.positions = previousPositions
- removeById('drone_route_remove')
- viewInfoFrustum?.clear()
- viewInfoFrustum = null
- }
+ // if (data.deviceInfo[data.currentSn].mode_code && [3, 4, 5, 9, 10].includes(deviceInfo.mode_code)) {
+ // if (switchSaving) return (switchSaving = false)
+ // // console.log('5555', data.deviceInfo[data.currentSn].longitude)
+ // const newPositions = globalCesium.Cartesian3.fromDegrees(
+ // data.deviceInfo[data.currentSn].longitude,
+ // data.deviceInfo[data.currentSn].latitude,
+ // Math.floor(viewDroneInfo.elevation),
+ // )
+ // //若刷新,恢复无人机之前的路线轨迹
+ // const dataIndexedDB = await IndexedDBService.getDataById(wId)
+ // if (previousPositions.length == 0 && dataIndexedDB) {
+ // previousPositions = previousPositions.concat(dataIndexedDB?.temporaryflightData)
+ // } else {
+ // previousPositions = previousPositions.concat(newPositions)
+ // }
+ // // 保存无人机当前执行的路线,防止刷新数据丢失
+ // if (!dataIndexedDB) {
+ // await IndexedDBService.addData({
+ // id: wId,
+ // temporaryflightData: previousPositions,
+ // })
+ // } else {
+ // await IndexedDBService.updateData(wId, {temporaryflightData: previousPositions})
+ // }
+ // // 更新线段的位置
+ // const drone_route_remove = getEntityById('drone_route_remove')
+ // if (!drone_route_remove) return
+ // drone_route_remove.polyline.positions = previousPositions
+ // drone_route_remove.polyline._material._repeat._value.x = Math.floor(previousPositions.length / 7)
+ // } else if ([0, 14].includes(deviceInfo.mode_code)) {
+ // previousPositions = []
+ // await IndexedDBService.deleteData(wId)
+ // const drone_route_remove = getEntityById('drone_route_remove')
+ // if (!drone_route_remove) return
+ // drone_route_remove.polyline.positions = previousPositions
+ // removeById('drone_route_remove')
+ // viewInfoFrustum?.clear()
+ // viewInfoFrustum = null
+ // }
}
if (data.currentType === EDeviceTypeName.Dock && data.dockInfo[data.currentSn]) {
if (!deviceInfo?.basic_osd?.longitude || !deviceInfo?.basic_osd?.latitude) return
- console.log('6666', deviceInfo?.basic_osd?.longitude)
if (snCode.includes(data.currentSn)) return
snCode.push(data.currentSn)
}
@@ -195,6 +197,8 @@
const imgLaser = new URL('@/assets/images/laser.png', import.meta.url).href
// 增加激光扫射点
function addFlyLaser(payloads:any) {
+ console.log('激光扫射经度',payloads.measure_target_longitude)
+ console.log('激光扫射纬度',payloads.measure_target_latitude)
const startPosition = globalCesium.Cartesian3.fromDegrees(
me_longitude, me_latitude, 0
)
@@ -205,57 +209,59 @@
measure_target_latitude = payloads.measure_target_latitude;
measure_target_altitude = payloads.measure_target_altitude;
- if (laserEntity && laserEntity != null) {
- laserEntity.position = new globalCesium.CallbackProperty(function () {
- return position
- }, false)
- // laserEntity.orientation = new globalCesium.CallbackProperty(function () {
- // return orientation
- // }, false)
+ // if (laserEntity && laserEntity != null) {
+ // laserEntity.position = new globalCesium.CallbackProperty(function () {
+ // return position
+ // }, false)
+ // // laserEntity.orientation = new globalCesium.CallbackProperty(function () {
+ // // return orientation
+ // // }, false)
- laserEntity.show = true
+ // laserEntity.show = true
- window.cesiumViewer.scene.requestRender()
- } else {
+ // window.cesiumViewer.scene.requestRender()
+ // } else {
+ removeById('laser_coordinate')
laserEntity = window.cesiumViewer.entities.add({
- position,
+ position: globalCesium.Cartesian3.fromDegrees(payloads.measure_target_longitude, payloads.measure_target_latitude, payloads.measure_target_altitude),
id: 'laser_coordinate',
billboard: {
image: imgLaser,
- outlineWidth: 0,
- width: 36,
- height: 36,
+ width: 30,
+ height: 30,
scale: 1.0,
+ outlineWidth: 0,
}
})
- }
- removeById('route_two_line')
- twoLineEntity = window.cesiumViewer.entities.add({
- id: 'route_two_line',
- polyline: {
- positions: new globalCesium.CallbackProperty(() => [startPosition, position], false),
- width: 5,
- material: new globalCesium.PolylineDashMaterialProperty({
- color: globalCesium.Color.YELLOW, // 虚线颜色
- dashLength: 6.0, // 虚线段的长度
- }),
- zIndex: 1,
- clampToGround: false,
- }
- })
+ // }
// 增加激光扫射信息
laserEntity.label = new globalCesium.LabelGraphics({
- text: `目标海拔:${Math.ceil(payloads.measure_target_altitude)}m\n测距距离:${Math.round(
- Math.ceil(payloads.measure_target_distance),
- )}m`,
+ text: `H:${Math.ceil(payloads.measure_target_altitude)}m-L:${Math.round(Math.ceil(payloads.measure_target_distance),)}m`,
font: '13px monospace',
+ fillColor: globalCesium.Color.fromCssColorString('red'), // 文字颜色
+ backgroundColor: globalCesium.Color.WHITE, // 背景颜色
showBackground: true,
horizontalOrigin: globalCesium.HorizontalOrigin.CENTER,
verticalOrigin: globalCesium.VerticalOrigin.BOTTOM,
disableDepthTestDistance: Number.POSITIVE_INFINITY,
- pixelOffset: new globalCesium.Cartesian2(0, -40),
+ pixelOffset: new globalCesium.Cartesian2(0, -20),
show: true,
})
+ console.log('起点',startPosition)
+ console.log('终点',position)
+ if (startPosition && position) {
+ removeById('route_two_line')
+ twoLineEntity = window.cesiumViewer.entities.add({
+ id: 'route_two_line',
+ polyline: {
+ positions: new globalCesium.CallbackProperty(() => [startPosition, position], false),
+ width: 3,
+ material: globalCesium.Color.RED,
+ zIndex: -1,
+ clampToGround: false,
+ }
+ })
+ }
}
function addFlyGltf (lng:any, lat:any, alt:any, payloads:any, attitude_head:any, deviceInfo:any, dockInfo:any, ssLinePath:any) {
@@ -306,179 +312,7 @@
}
// 创建无人机信息
- createDroneEntityLabel(droneEntity, deviceInfo, dockInfo, ssLinePath)
- }
- // 创建无人机信息
- async function createDroneEntityLabel (entity:any, deviceInfo:any, dockInfo:any, ssLinePath:any) {
- deviceInfo = deviceInfo
- if (currentTaskCreateTime === null) return
- if (!deviceInfo?.longitude && !deviceInfo?.latitude) return
- if (deviceInfo.mode_code !== 5) {
- currentTaskCreateTime = null
- return
- }
- const executingWayline = store.executingWayline
- if (!executingWayline?.id) return
- if (waylinePoints.length === 0) {
- // 改成获取航线地址
- // const routeRes = await getRoutePlanningUrl(
- // wId,
- // executingWayline.id,
- // )
- // const routeData = routeRes.data
- // if (routeData.code !== 0) return this.$message.error('获取航线信息失败!')
- waylinePoints = await getWaylineFilePoints(ssLinePath)
- }
-
- const nextPoint = waylinePoints[currentWaypointIndex]
- if (!nextPoint) return
- // 两点之间的距离
- console.log('8888', deviceInfo.longitude)
- const devicePosition = globalCesium.Cartesian3.fromDegrees(
- Number(deviceInfo.longitude),
- Number(deviceInfo.latitude),
- 0,
- )
- console.log('9999', nextPoint.longitude)
- const targetPosition = globalCesium.Cartesian3.fromDegrees(
- Number(nextPoint.longitude),
- Number(nextPoint.latitude),
- 0,
- )
- // 距离下个点位的距离
- let distance = globalCesium.Cartesian3.distance(
- devicePosition,
- targetPosition,
- )
- const dockPosition = {
- longitude: dockInfo?.basic_osd?.longitude,
- latitude: dockInfo?.basic_osd?.latitude,
- }
-
- const distArr = waylinePoints.map((lnglat, index) => {
- const { longitude, latitude } = lnglat
- console.log('10000', longitude)
- // 两点之间的距离
- let devicePosition = null
- if (index === 0) {
- devicePosition = globalCesium.Cartesian3.fromDegrees(
- Number(dockPosition.longitude),
- Number(dockPosition.latitude),
- 0,
- )
- } else {
- console.log('110', prevPoint.longitude)
- const prevPoint = waylinePoints[index - 1]
- devicePosition = globalCesium.Cartesian3.fromDegrees(
- Number(prevPoint.longitude),
- Number(prevPoint.latitude),
- 0,
- )
- }
- const targetPosition = globalCesium.Cartesian3.fromDegrees(
- Number(longitude),
- Number(latitude),
- 0,
- )
- let distance = globalCesium.Cartesian3.distance(
- devicePosition,
- targetPosition,
- )
- return Math.round(distance)
- })
- // 计算到达点位和之前点位的总距离
- let sum = null
- if (distArr.length > 2) {
- sum = distArr
- .slice(0, currentWaypointIndex)
- .reduce((accumulator, currentValue) => accumulator + currentValue, 0)
- } else {
- sum = distArr.reduce(
- (accumulator, currentValue) => accumulator + currentValue,
- 0,
- )
- }
-
- if (sum == 0) {
- sum = distArr[0]
- }
-
- // 距离机场的位置
- const homeDistance = Math.floor(deviceInfo.home_distance) || 0
- // 速度
- let horizontalSpeed = deviceInfo.horizontal_speed || 0
- if (!droneSpeedArr.includes(horizontalSpeed) && horizontalSpeed > 1) {
- droneSpeedArr.push(horizontalSpeed)
- }
- // 平均速度
- const averageSpeed =
- droneSpeedArr.reduce((sum, current) => sum + current, 0) /
- droneSpeedArr.length
- // 已用时间
- let usedTime = Math.round((new Date().getTime() - currentTaskCreateTime) / 1000) - 30
- let throughDistance = usedTime * averageSpeed
-
- if (throughDistance < 0) {
- throughDistance = 0
- usedTime = 0
- }
- if (isNaN(throughDistance)) {
- throughDistance = 0
- }
- if (isNaN(usedTime)) {
- usedTime = 0
- }
- if (usedTime > 60) {
- const minute = Math.floor(usedTime / 60)
- const second = Math.round(usedTime % 60)
- usedTime = `${minute}m${second}s`
- } else {
- usedTime = Math.round(usedTime) + 's'
- }
-
- if (horizontalSpeed < 5) {
- horizontalSpeed = 10
- }
- // 预计到达下一个航点时间
- let arrivalTime = distance / horizontalSpeed
- if (arrivalTime === Infinity || isNaN(arrivalTime)) {
- arrivalTime = 0
- }
- if (arrivalTime > 60) {
- const minute = Math.floor(arrivalTime / 60)
- const second = Math.round(arrivalTime % 60)
- arrivalTime = `${minute}m${second}s`
- } else {
- arrivalTime = Math.round(arrivalTime) + 's'
- }
- entity.label = new globalCesium.LabelGraphics({
- text: `距离机场水平距离:${homeDistance}m\n距离下一个航点:${Math.round(
- distance,
- )}m\n预计到达下一航点时间:${arrivalTime}\n本次飞行时间:${usedTime}\n本次航线平面里程:${Math.round(
- throughDistance,
- )}m`,
- font: '13px monospace',
- showBackground: true,
- horizontalOrigin: globalCesium.HorizontalOrigin.CENTER,
- verticalOrigin: globalCesium.VerticalOrigin.BOTTOM,
- disableDepthTestDistance: Number.POSITIVE_INFINITY,
- pixelOffset: new globalCesium.Cartesian2(0, -40),
- show: true,
- })
- }
- async function getWaylineFilePoints(fileUrl:any) {
- const fileRes = await analyzeKmzFile(fileUrl);
- const waylineContent = await fileRes['fileInfoObj']?.['wpmz/waylines.wpml'];
- const xml = XMLToJSON(waylineContent);
- const placemarkArr = xml?.Document?.Folder.Placemark;
- const points = placemarkArr.map((placemark) => {
- const lnglat = placemark.Point.coordinates?.['#text'].split(',');
- return {
- longitude: lnglat[0],
- latitude: lnglat[1],
- };
- });
- return points;
+ // createDroneEntityLabel(droneEntity, deviceInfo, dockInfo, ssLinePath)
}
function setelCamera (lng, lat, alt, payloads, attitude_head) {
@@ -539,8 +373,9 @@
}
// 创建视锥
function initCreateFrustum (deviceInfo:any, test:any) {
-
- if (!deviceInfo || !deviceInfo.longitude || !deviceInfo.latitude) return
+ // console.log('视锥传参1',deviceInfo)
+ // console.log('视锥传参2',test)
+ if (!deviceInfo || !deviceInfo.longitude || !deviceInfo.latitude || !test.longitude || !test.latitude) return
const attitude_head = 180 + deviceInfo.attitude_head
@@ -566,26 +401,21 @@
// 获取手机定位和激光发射定位的高度和距离
function getTwoHeightWidth(workspaceId:String) {
- // 国内地图不会为0或者小于0
- console.log('自身定位经纬度1', me_latitude)
- console.log('自身定位经纬度2', me_longitude)
- console.log('激光扫过定位1', measure_target_latitude)
- console.log('激光扫过定位2', measure_target_longitude)
if (me_latitude <= 0 || me_longitude <= 0 || measure_target_latitude <= 0 || measure_target_longitude <= 0) return
getTwoPointInfo(workspaceId,me_latitude,me_longitude,measure_target_latitude,measure_target_longitude).then((res:any) => {
const heightDifference = res.data.heightDifference
const distance = res.data.distance
// 增加激光扫射信息
myAppEntity.label = new globalCesium.LabelGraphics({
- text: `高度:${Math.ceil(heightDifference)}m\n距离:${Math.round(
- Math.ceil(distance),
- )}m`,
+ text: `H:${Math.ceil(heightDifference)}m-L:${Math.round(Math.ceil(distance),)}m`,
font: '13px monospace',
+ fillColor: globalCesium.Color.fromCssColorString('red'), // 文字颜色
+ backgroundColor: globalCesium.Color.WHITE, // 背景颜色
showBackground: true,
horizontalOrigin: globalCesium.HorizontalOrigin.CENTER,
verticalOrigin: globalCesium.VerticalOrigin.BOTTOM,
disableDepthTestDistance: Number.POSITIVE_INFINITY,
- pixelOffset: new globalCesium.Cartesian2(0, -40),
+ pixelOffset: new globalCesium.Cartesian2(0, -24),
show: true,
})
})
@@ -597,6 +427,8 @@
if (window.cesiumViewer == null) return
if (navigator.geolocation) {
navigator.geolocation.watchPosition((position) => {
+ console.log(me_longitude,position.coords.longitude)
+ console.log(me_latitude,position.coords.latitude)
if (me_longitude > 0 && me_longitude == position.coords.longitude && me_latitude > 0 && me_latitude == position.coords.latitude) return
me_longitude = position.coords.longitude;
me_latitude = position.coords.latitude;
@@ -611,6 +443,7 @@
width: 30,
height: 30,
scale: 1.0,
+ outlineWidth: 0,
}
});
// 查看两条线直接距离和高度
--
Gitblit v1.9.3