chenyao
2025-02-24 a0f008e1b176ab0c844bc4721f282e12d18f855c
调整详情和修改地图渲染功能
9 files modified
6 files added
4294 ■■■■ changed files
src/api/http/request-copy.ts 212 ●●●●● patch | view | raw | blame | history
src/api/http/request.ts 52 ●●●● patch | view | raw | blame | history
src/main.ts 3 ●●●●● patch | view | raw | blame | history
src/router/index.ts 4 ●●●● patch | view | raw | blame | history
src/views/DronePilotDetails-copy.vue 2 ●●● patch | view | raw | blame | history
src/views/DronePilotDetails-copy1.vue 754 ●●●●● patch | view | raw | blame | history
src/views/DronePilotDetails-copy2.vue 708 ●●●●● patch | view | raw | blame | history
src/views/DronePilotDetails-copy3.vue 675 ●●●●● patch | view | raw | blame | history
src/views/DronePilotDetails.vue 172 ●●●●● patch | view | raw | blame | history
src/views/DronePilotList.vue 11 ●●●●● patch | view | raw | blame | history
src/views/components/controlConsole.vue 6 ●●●● patch | view | raw | blame | history
src/views/components/ptzControl-copy.vue 564 ●●●●● patch | view | raw | blame | history
src/views/components/ptzControl.vue 76 ●●●●● patch | view | raw | blame | history
src/views/hooks/droneFly-copy.ts 664 ●●●●● patch | view | raw | blame | history
src/views/hooks/droneFly.ts 391 ●●●● patch | view | raw | blame | history
src/api/http/request-copy.ts
New file
@@ -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
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) {
      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)
  },
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)
src/router/index.ts
@@ -26,6 +26,10 @@
  // },
  {
    path: '/',
    redirect: '/login',
  },
  {
    path: '/login',
    name: ERouterName.LOGIN,
    component: () => import('@/views/Login.vue')
  },
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 
src/views/DronePilotDetails-copy1.vue
New file
@@ -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>
src/views/DronePilotDetails-copy2.vue
New file
@@ -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>
src/views/DronePilotDetails-copy3.vue
New file
@@ -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>
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)
// 定义方法直接监听机场消息
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;
  }
}
// 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>
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;
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;
src/views/components/ptzControl-copy.vue
New file
@@ -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>
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,
src/views/hooks/droneFly-copy.ts
New file
@@ -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,
  }
}
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,
          },
        })
      }
      // 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
    // 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,
        }
      })
    }
    // }
    // 增加激光扫射信息
    laserEntity.label = new globalCesium.LabelGraphics({
      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, -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: 5,
        material: new globalCesium.PolylineDashMaterialProperty({
            color: globalCesium.Color.YELLOW, // 虚线颜色
            dashLength: 6.0, // 虚线段的长度
        }),
        zIndex: 1,
          width: 3,
          material: globalCesium.Color.RED,
          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) {
@@ -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,
          }
        });
        // 查看两条线直接距离和高度