forked from drone/command-center-dashboard

chenyao
2025-04-14 bb95bfaecd139e440d21bdcdd5fc578625a7b912
Merge branch 'master' of http://139.196.74.78:10010/r/drone/command-center-dashboard
2 files modified
220 ■■■■■ changed files
src/views/TaskManage/TaskIntermediateContent/CurrentTaskDetails/CurrentTaskDetails.vue 156 ●●●●● patch | view | raw | blame | history
src/views/TaskManage/TaskIntermediateContent/CurrentTaskDetails/RealTimeMap.vue 64 ●●●●● patch | view | raw | blame | history
src/views/TaskManage/TaskIntermediateContent/CurrentTaskDetails/CurrentTaskDetails.vue
@@ -1,104 +1,142 @@
<!--当前任务详情-->
<template>
  <el-dialog
    <el-dialog
        modal-class="current-task-details"
        v-model="isShow"
        title="当前任务详情"
        :width="pxToRem(1500)"
        :close-on-click-modal="false"
        :destroy-on-close="true">
        :destroy-on-close="true"
    >
        <div class="content-container" v-if="isShow">
      <!-- 视频直播 -->
      <div class="video-container">
        <LiveVideo :videoUrl="machineNestUrl"/>
      </div>
            <!-- 视频直播 -->
            <div class="video-container">
                <LiveVideo :videoUrl="currentLiveUrl" />
            </div>
            <!-- 展示地图 -->
            <RealTimeMap />
    </div>
  </el-dialog>
        </div>
    </el-dialog>
</template>
<script setup>
import { pxToRem } from '@/utils/rem';
import LiveVideo from '@/components/LiveVideo.vue';
import { liveStart } from '@/api/home/machineNest';
import { getJobDetails } from '@/api/home/task';
import { pxToRem } from '@/utils/rem'
import LiveVideo from '@/components/LiveVideo.vue'
import { liveStart } from '@/api/home/machineNest'
import { getJobDetails } from '@/api/home/task'
import RealTimeMap from '@/views/TaskManage/TaskIntermediateContent/CurrentTaskDetails/RealTimeMap.vue'
import { getWebsocketUrl } from '@/websocket/util/config'
import { useConnectWebSocket } from '@/utils/websocket/connect-websocket'
import { EBizCode } from '@/utils/staticData/enums'
const isShow = defineModel('show');
const isShow = defineModel('show')
const props = defineProps({
  rowData: { // 任务列表row数据
    type: Object,
    default: () => ({})
  }
});
    rowData: {
        // 任务列表row数据
        type: Object,
        default: () => ({}),
    },
})
let taskDetails = ref({})
const machineNestUrl = ref('');
provide('taskDetails', taskDetails);
const currentLiveUrl = ref('')
const machineNestUrl = ref('')
const dockLiveUrl = ref('')
provide('taskDetails', taskDetails)
// 获取机巢直播地址
const getVideoUrl = (deviceSn) => {
  liveStart(deviceSn).then(res => {
    if (res.data.code !== 0) return;
    machineNestUrl.value = res.data.data.rtcs_url;
  });
};
const deviceOsdInfo = ref({})
provide('deviceOsdInfo', deviceOsdInfo)
// 机巢直播
const getDeviceLiveUrl = async () => {
    if (machineNestUrl.value) return machineNestUrl.value
    const res = await liveStart(taskDetails.value.device_sns[0])
    machineNestUrl.value = res.data.data.rtcs_url
    return machineNestUrl.value
}
// 无人机直播
const getDockLiveUrl = async dockSn => {
    if (dockLiveUrl.value) return dockLiveUrl.value
    const res = await liveStart(dockSn)
    dockLiveUrl.value = res.data.data.rtcs_url
    return dockLiveUrl.value
}
// 设置当前直播地址
const setCurrentLiveUrl = async () => {
    const data = deviceOsdInfo.value?.data
    const deviceInfo = data?.host
    const dockSn = data?.sn
    if ([14, 0].includes(deviceInfo.mode_code)) {
        currentLiveUrl.value = await getDeviceLiveUrl()
    } else {
        currentLiveUrl.value = await getDockLiveUrl(dockSn)
    }
}
// 获取任务详情获取航线文件
const getTaskDetails = () => {
    getJobDetails({ wayLineJobInfoId: props.rowData.id }).then(res => {
    getJobDetails({ wayLineJobInfoId: props.rowData.id }).then(async res => {
        taskDetails.value = res.data.data
        getVideoUrl(taskDetails.value.device_sns[0]);
        console.log('taskDetails', taskDetails.value)
        currentLiveUrl.value = await getDeviceLiveUrl()
        createWsConnect(taskDetails.value.way_lines[0].workspace_id)
    })
}
const messageHandler = (result) => {
const messageHandler = result => {
    let payload = JSON.parse(result) // 为了兼容聊天消息
    console.log('result,6666666', payload)
    switch (payload.biz_code) {
        case EBizCode.DeviceOsd: {
            deviceOsdInfo.value = payload
            // console.log(deviceOsdInfo.value,'osd信息')
            setCurrentLiveUrl()
            break
        }
        default:
            break
    }
}
let connectWs
const createWsConnect = (workspaceId) => {
    let webSocketUrl = getWebsocketUrl() + '&workspace-id=' + workspaceId;
const createWsConnect = workspaceId => {
    let webSocketUrl = getWebsocketUrl() + '&workspace-id=' + workspaceId
    // 监听ws 消息
    connectWs = useConnectWebSocket(messageHandler, webSocketUrl);
};
    connectWs = useConnectWebSocket(messageHandler, webSocketUrl)
}
const removeEvent = () => {
    connectWs?.close()
    deviceOsdInfo.value = {}
}
// 监听 rowData 变化
watch(isShow, (newVal) => {
  if (newVal) {
    getTaskDetails();
  }else{
        connectWs?.close();
watch(isShow, newVal => {
    if (newVal) {
        getTaskDetails()
    } else {
        removeEvent()
    }
});
onBeforeUnmount(() => {
    connectWs?.close();
})
onMounted(() => {
});
onBeforeUnmount(() => {
    removeEvent()
})
</script>
<style lang="scss" scoped>
.current-task-details {
  display: flex;
  justify-content: space-between;
  .content-container {
    display: flex;
    // gap: 20px;
    height: 600px;
    display: flex;
    justify-content: space-between;
    .video-container {
      width: 50%;
      padding-right: 10px;
    }
  }
    .content-container {
        display: flex;
        // gap: 20px;
        height: 600px;
        .video-container {
            width: 50%;
            padding-right: 10px;
        }
    }
}
</style>
src/views/TaskManage/TaskIntermediateContent/CurrentTaskDetails/RealTimeMap.vue
@@ -1,13 +1,7 @@
<template>
    <div id="currentTaskMap">
    </div>
    <div id="currentTaskMap"></div>
</template>
<script setup>
import * as Cesium from 'cesium'
import AmapMercatorTilingScheme from '@/utils/cesium/AmapMercatorTilingScheme'
import { Cartesian3, Terrain, Viewer } from 'cesium'
@@ -19,6 +13,7 @@
import ImageTrailMaterial from '@/utils/cesium/ImageTrailMaterial'
import lineImg from '@/assets/images/arrow-right-blue.png'
import { flyVisual } from '@/utils/cesium/mapUtil'
import CreateFrustum from '@/utils/cesium/frustum/CreateFrustum'
const imageryProvider_ammapSL = new Cesium.UrlTemplateImageryProvider({
    url: 'https://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
@@ -32,6 +27,9 @@
    credit: 'amap_SL',
})
let viewer = null
const taskDetails = inject('taskDetails')
const deviceOsdInfo = inject('deviceOsdInfo')
const initMap = () => {
    viewer = new Viewer('currentTaskMap', {
        terrain: Terrain.fromWorldTerrain(),
@@ -51,17 +49,16 @@
    const options = {
        bInvertColor: true,
        bFilterColor: true,
        filterColor: '#4e70a6'
        filterColor: '#4e70a6',
    }
    // 添加蓝色滤镜
    addBlueFilter(options,viewer,gdLayer)
    addBlueFilter(options, viewer, gdLayer)
    viewer.scene.morphTo2D(0)
    //设置默认点
    viewer.camera.setView({
        destination: Cartesian3.fromDegrees(115.763819, 28.787374,5000),
        destination: Cartesian3.fromDegrees(115.763819, 28.787374, 5000),
    })
}
const drawWayline = lineObj => {
    const positions = lineObj.Placemark.map(item => {
@@ -79,7 +76,7 @@
    })
    // 终点
    viewer.entities.add({
        position: positions[positions.length-1],
        position: positions[positions.length - 1],
        billboard: {
            image: new Cesium.ConstantProperty(endPointImg),
            width: 30,
@@ -103,32 +100,55 @@
    })
}
// 解析kmz文件
const parsingFiles = async (url) => {
const parsingFiles = async url => {
    const res = await analyzeKmzFile(`${url}?_t=${new Date().getTime()}`)
    const waylinesXML = await res.fileInfoObj['wpmz/waylines.wpml']
    const waylinesXMLJSON = XMLToJSON(waylinesXML)?.['Document'];
    const waylinesXMLJSON = XMLToJSON(waylinesXML)?.['Document']
    const waylinesXMLObj = removeTextKey(waylinesXMLJSON.Folder)
    if (!waylinesXMLObj.Placemark.length) return;
    if (!waylinesXMLObj.Placemark.length) return
    const allPoint = waylinesXMLObj.Placemark.map(item => item.Point.coordinates.split(','))
    flyVisual(allPoint,viewer)
    flyVisual(allPoint, viewer)
    drawWayline(waylinesXMLObj)
};
}
const removeMap = () => {
    viewer.entities.removeAll()
    viewer.destroy()
}
const taskDetails = inject('taskDetails')
let viewInfoFrustum
// 设置视椎
const setCreateFrustum = () => {
    const deviceInfo = deviceOsdInfo.value?.data?.host
    if (!deviceInfo) return
    viewInfoFrustum?.clear()
    if ([14, 0].includes(deviceInfo.mode_code)) return
    const attitude_head = 180 + deviceInfo.attitude_head
    const gimbal_pitch = 90 - Number(deviceInfo?.payloads[0]?.gimbal_pitch) || 0
    viewInfoFrustum = new CreateFrustum(viewer, {
        position: {
            longitude: deviceInfo.longitude,
            latitude: deviceInfo.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,
    })
}
watch(deviceOsdInfo, setCreateFrustum)
watch(taskDetails, () => {
    if (taskDetails.value.way_lines.length){
    if (taskDetails.value.way_lines.length) {
        parsingFiles(taskDetails.value.way_lines[0].url)
    }
})
onBeforeUnmount(() => {
    removeMap()
})
@@ -138,13 +158,13 @@
        initMap()
    })
})
</script>
<style scoped lang="scss">
#currentTaskMap {
    width: 50%;
    padding-left: 10px;
    height: 100%;
    :deep() {
        .cesium-viewer {
            width: 100%;