forked from drone/command-center-dashboard

shuishen
2025-04-20 18ba6b7aed78d1fc639b0b7702f9cb5bdf66e12c
Merge branch 'master' of http://139.196.74.78:10010/r/drone/command-center-dashboard
18 files modified
3 files renamed
1 files added
219 ■■■■ changed files
.env 6 ●●●●● patch | view | raw | blame | history
.env.development 4 ●●●● patch | view | raw | blame | history
.env.production 2 ●●● patch | view | raw | blame | history
.env.test 4 ●●●● patch | view | raw | blame | history
src/assets/images/aiNowFly/event/hover.png patch | view | raw | blame | history
src/assets/images/aiNowFly/event/startRecord.png patch | view | raw | blame | history
src/assets/images/aiNowFly/event/takePhoto.png patch | view | raw | blame | history
src/assets/images/home/homeRight/leftTriangle.png patch | view | raw | blame | history
src/components/CurrentTaskDetails/ControlPanel/BaseControl.vue 7 ●●●●● patch | view | raw | blame | history
src/components/CurrentTaskDetails/ControlPanel/ControlPanel.vue 3 ●●●● patch | view | raw | blame | history
src/components/CurrentTaskDetails/CurrentTaskDetails.vue 95 ●●●●● patch | view | raw | blame | history
src/components/CurrentTaskDetails/TaskDetailsRight.vue 29 ●●●● patch | view | raw | blame | history
src/components/DeviceJobDetails/JobRelatedEvents.vue 13 ●●●●● patch | view | raw | blame | history
src/components/UserOperate.vue 2 ●●●●● patch | view | raw | blame | history
src/const/drc.js 9 ●●●●● patch | view | raw | blame | history
src/hooks/useTaskWayline/useTaskWayline.js 10 ●●●●● patch | view | raw | blame | history
src/layout/Header.vue 2 ●●● patch | view | raw | blame | history
src/views/Home/AINowFly.vue 14 ●●●● patch | view | raw | blame | history
src/views/Home/HomeRight/EventOverview.vue 6 ●●●● patch | view | raw | blame | history
src/views/Home/RSide.vue 2 ●●●●● patch | view | raw | blame | history
src/views/SignMachineNest/MachineLeft/InspectionRaskDetails.vue 6 ●●●● patch | view | raw | blame | history
src/views/SignMachineNest/MachineRight/MachineStatus/MachineTableDetails/DeviceEvent.vue 5 ●●●● patch | view | raw | blame | history
.env
@@ -1,3 +1,5 @@
#开发、测试、生产环境共用环境配置
VITE_APP_API = /api
#调试参数
VITE_APP_DEBUG_KEY = saber
@@ -9,7 +11,3 @@
VITE_APP_TDT_TOKEN = c6eea7dad4fa1e2d1e32ec0e7c9735db
#cesium token
VITE_APP_CESIUM_TOKEN = eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJkYTZlNGNlYS01NTU1LTQ1MGEtYmNlZS0yNTE2NDk5YWM2MjEiLCJpZCI6MTc5Njk2LCJpYXQiOjE3MDA1NDcwMjV9.qcl4AH2731cfFd0-I1ZLUINPXqvglLkDFD-UGR2zU5M
# 环境路径
VITE_APP_TICKET_BASE_URL=https://wrj.shuixiongit.com
VITE_APP_TICKET_BACKUP_URL=https://aisky.org.cn
.env.development
@@ -11,5 +11,5 @@
# 航线文件地址
VITE_APP_AIRLINE_URL = https://wrj.shuixiongit.com/minio/cloud-bucket
#系统运维
VITE_APP_ADMIN_URL = 'https://wrj.shuixiongit.com/manage'
#管理后台地址
VITE_APP_ADMIN_URL = 'https://wrj.shuixiongit.com/manage/wel/index'
.env.production
@@ -6,4 +6,4 @@
# ws地址
VITE_APP_WS_API_URL = wss://wrj.shuixiongit.com/drone-wss/api/v1/ws
# 管理后台地址
VITE_APP_ADMIN_URL = 'https://aisky.org.cn/manage'
VITE_APP_ADMIN_URL = 'https://aisky.org.cn/manage/wel/index'
.env.test
@@ -6,5 +6,5 @@
# 航线文件地址
VITE_APP_AIRLINE_URL = https://wrj.shuixiongit.com/minio/cloud-bucket
#系统运维
VITE_APP_ADMIN_URL = 'https://wrj.shuixiongit.com/manage'
#管理后台地址
VITE_APP_ADMIN_URL = 'https://wrj.shuixiongit.com/manage/wel/index'
src/assets/images/aiNowFly/event/hover.png

src/assets/images/aiNowFly/event/startRecord.png

src/assets/images/aiNowFly/event/takePhoto.png

src/assets/images/home/homeRight/leftTriangle.png
src/components/CurrentTaskDetails/ControlPanel/BaseControl.vue
@@ -31,9 +31,10 @@
import EventBus from '@/event-bus'
const isAutoControl = inject('isAutoControl')
const isBackDock = inject('isBackDock')
const list3 = computed(() => [
    { name: '自动控制', svg: 'autoControl', style: { top: '-70%' }, active: isAutoControl.value, handle: autoControl },
    { name: '自动控制', svg: 'autoControl', style: { top: '-70%' }, active: isAutoControl.value && !isBackDock.value, handle: autoControl },
    //如果是返航, 继续任务 就是自动任务
    //如果是取消返航, 继续任务 就是自动任务
    { name: '继续任务', svg: 'continueTask', style: { left: '70%' }, active: false, handle: autoControl },
@@ -41,10 +42,10 @@
        name: '手动控制',
        svg: 'manualControl',
        style: { top: '70%' },
        active: !isAutoControl.value,
        active: !isAutoControl.value && !isBackDock.value,
        handle: manualControl,
    },
    { name: '返航/取消返航', svg: 'turnBack', style: { left: '-70%' }, active: false, handle: turnBack },
    { name: '返航/取消返航', svg: 'turnBack', style: { left: '-70%' }, active: isBackDock.value, handle: turnBack },
])
function autoControl() {
src/components/CurrentTaskDetails/ControlPanel/ControlPanel.vue
@@ -158,6 +158,7 @@
const droneSn = inject('droneSn')
const trueAltitude = inject('trueAltitude')
const client_id = inject('client_id')
const isBackDock = inject('isBackDock')
const deviceTopicInfo = ref({
    pubTopic: '',
@@ -359,7 +360,7 @@
    })
}
const isBackDock = ref(false)
// 返航
async function onBackDock() {
src/components/CurrentTaskDetails/CurrentTaskDetails.vue
@@ -41,8 +41,6 @@
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 '@/components/CurrentTaskDetails/RealTimeMap.vue'
import ControlPanel from '@/components/CurrentTaskDetails/ControlPanel/ControlPanel.vue'
import TaskDetailsHead from '@/components/CurrentTaskDetails/TaskDetailsHead.vue'
@@ -60,9 +58,8 @@
const isAutoControl = ref(true) //是否自动控制
const lineQuality = ref(1) //1流畅,2标清
const taskDetailsViewer = ref(null) //地图实例
const deviceOsdInfo = computed(() => wsInfo.value?.device_osd)
const dockSn = computed(() => taskDetails?.value?.device_sns?.[0])
const droneSn = computed(() => deviceOsdInfo?.value?.data?.sn)
const dockSn = computed(() => taskDetails?.value?.device_sns?.[0]) //机巢sn
const droneSn = computed(() => wsInfo.value?.device_osd?.data?.sn) //无人机sn
const trueAltitude = ref('') // 真实高度
const isAiLive = ref(false) // 是ai直播
const video_id = ref('') // 直播视频id
@@ -71,42 +68,16 @@
const currentLiveUrl = ref('') // 当前直播地址
const isTakeOff = ref(false) // 是在飞行中
const isMaxMap = ref(false) //是大地图
const client_id = ref('') //是大地图
// 获取机巢直播
const getDeviceLiveUrl = async () => {
    const res = await liveStart(dockSn.value, 2)
    currentLiveUrl.value = res.data.data.rtcs_url
}
const hasIr = ref(false)
provide('hasIr', hasIr)
//获取相机能力
async function getLiveCapacity() {
    const res = await getLiveCapacityApi({ sn: droneSn.value})
    res?.data?.data?.forEach((item) => {
        item?.cameras_list?.forEach((item1) => {
            item1?.videos_list?.forEach((item2) => {
                item2?.switch_video_types?.forEach(item3 =>{
                      if (item3 === 'ir'){
                            hasIr.value = true
                        }
                })
            })
        })
    })
}
const useTaskDetailsCallBack = () => {
    getDeviceLiveUrl()
}
let { taskDetails, workspace_id, getTaskDetails } = useTaskDetails(useTaskDetailsCallBack)
let { wsInfo, removeWS } = useDroneWS(workspace_id) //ws信息,是一个ref对象
const client_id = ref('') //mqtt id
const hasIr = ref(false) //有红外能力
let once = true //第一次触发
const isBackDock = ref(false)
let { taskDetails, workspace_id, getTaskDetails:initTaskDetails } = useTaskDetails(()=> getDeviceLiveUrl())
let { wsInfo } = useDroneWS(workspace_id) //ws信息,是一个ref对象
provide('wsInfo', wsInfo)
provide('isBackDock', isBackDock)
provide('workspace_id', workspace_id)
provide('deviceOsdInfo', deviceOsdInfo)
provide('dockOsdInfo', wsInfo?.value?.dock_osd)
provide('dockSn', dockSn)
provide('droneSn', droneSn)
@@ -118,24 +89,33 @@
provide('isAiLive', isAiLive)
provide('video_id', video_id)
provide('client_id', client_id)
provide('hasIr', hasIr)
let once = true
watch(deviceOsdInfo,()=>{
    if (once){
        getLiveCapacity()
        once =false
    }
})
// 获取机巢直播
const getDeviceLiveUrl = async () => {
    const res = await liveStart(dockSn.value, 2)
    currentLiveUrl.value = res.data.data.rtcs_url
}
watch(
    wsInfo,
    () => {
        // wsInfo 变化触发
        setCurrentLiveUrl()
    },
    { deep: true }
)
//获取是否有红外功能
async function getLiveCapacity() {
    if (!once) return
    once = false
    const res = await getLiveCapacityApi({ sn: droneSn.value })
    res?.data?.data?.forEach(item => {
        item?.cameras_list?.forEach(item1 => {
            item1?.videos_list?.forEach(item2 => {
                item2?.switch_video_types?.forEach(item3 => {
                    if (item3 === 'ir') {
                        hasIr.value = true
                    }
                })
            })
        })
    })
}
// set Ai直播
const getAiLiveUrl = async () => {
    const res = await getLiveAiLinkApi({
        original_stream_url: `${CURRENT_CONFIG.rtmpURL}${video_id.value.replace(/\//g, '-')}`,
@@ -159,13 +139,13 @@
// 无人机直播画质切换
const changeLineQuality = async () => {
    const res = await updateDroneQualityApi({ video_id: video_id.value, video_quality: lineQuality.value })
    await updateDroneQualityApi({ video_id: video_id.value, video_quality: lineQuality.value })
    ElMessage.success('切换画质成功')
}
// 设置当前直播地址
const setCurrentLiveUrl = async () => {
    const deviceInfo = deviceOsdInfo.value?.data?.host
    const deviceInfo = wsInfo.value?.device_osd?.data?.host
    if (!deviceInfo) return
    const currentIsTakeOff = ![14, 0].includes(deviceInfo?.mode_code)
    // 如果还是之前的状态,不切换
@@ -174,8 +154,11 @@
    isTakeOff.value ? await getDroneLiveUrl() : await getDeviceLiveUrl()
}
watch(() => wsInfo.value?.device_osd, getLiveCapacity)
watch(wsInfo, setCurrentLiveUrl, { deep: true })
onMounted(() => {
    getTaskDetails(props?.id)
    initTaskDetails(props?.id)
    EventBus.on('CurrentTaskDetails-timeStop', changeLineQuality)
    EventBus.on('CurrentTaskDetails-getAiLiveUrl', getAiLiveUrl)
    EventBus.on('CurrentTaskDetails-getDroneLiveUrl', getDroneLiveUrl)
src/components/CurrentTaskDetails/TaskDetailsRight.vue
@@ -7,7 +7,10 @@
        <div class="taskInfo">
            <div v-for="item in list" class="itemBox">
                <div class="itemName">{{ item.name }}:</div>
                <div class="itemValue">{{ item.value }}</div>
                <div class="itemValue" v-if="item.name !== '飞行事件'">{{ item.value }}</div>
                <div class="flightEvents" v-else>
                    <img v-for="item in flightEvents" alt="" :src="item.img" :title="item.name"></img>
                </div>
            </div>
        </div>
@@ -17,6 +20,7 @@
<script setup>
import droneImg from '@/assets/images/taskManagement/taskIntermediateContent/droneImg.png'
import BaseControl from '@/components/CurrentTaskDetails/ControlPanel/BaseControl.vue'
import { droneEventList } from '@/const/drc'
const taskDetails = inject('taskDetails')
const workspace_id = inject('workspace_id')
@@ -32,7 +36,8 @@
    { name: '关联算法', value: '', field: 'ai_type_str' },
    { name: '任务描述', value: '', field: 'remark' },
])
// takePhoto,hover
const flightEvents = ref([])
watch(
    taskDetails,
    () => {
@@ -44,7 +49,9 @@
            }
            if (item.name === '飞行事件') {
                const { action_modes = [] } = taskDetails?.value || {}
                // todo item.value = action_modes.
                flightEvents.value = action_modes.flatMap(({ actionActuatorFunc }) =>
                    droneEventList.filter(({ value }) => actionActuatorFunc === value)
                )
            }
        })
    },
@@ -93,7 +100,7 @@
    }
    .taskInfo {
        width: 220px;
        width: 230px;
        height: 520px;
        display: flex;
        overflow: auto;
@@ -115,8 +122,18 @@
            .itemValue {
                font-weight: bold;
                color: #ffffff;
                word-break: break-all;     /* 强制在任意字符断行 */
                white-space: normal;       /* 允许正常换行 */
                word-break: break-all; /* 强制在任意字符断行 */
                white-space: normal; /* 允许正常换行 */
            }
            .flightEvents{
                display: flex;
                flex-wrap: wrap;
                gap: 10px 10px;
                img {
                    width: 30px;
                    height: 30px;
                }
            }
        }
    }
src/components/DeviceJobDetails/JobRelatedEvents.vue
@@ -59,23 +59,20 @@
const total = ref(10)
const examine = row => {
    const orderNumber = row.event_num
    const primaryUrl = `${import.meta.env.VITE_APP_TICKET_BASE_URL}/manage/tickets/ticket?orderNumber=${orderNumber}`
    const backupUrl = `${import.meta.env.VITE_APP_TICKET_BACKUP_URL}/manage/tickets/ticket?orderNumber=${orderNumber}`
    const win = window.open(primaryUrl, '_blank')
    if (!win || win.closed) {
        window.open(backupUrl, '_blank')
    }
    const adminUrl = import.meta.env.VITE_APP_ADMIN_URL
    const targetPath = `/tickets/ticket?orderNumber=${orderNumber}`;
    window.open(`${adminUrl}?redirect=${encodeURIComponent(targetPath)}`, '_blank');
}
const wayLineJodInfoId = inject('wayLineJodInfoId')
const getList = () => {
    params.value.way_line_jod_info_id = wayLineJodInfoId.value
    getDeviceEventList(params.value, sizeParams.value).then(res => {
        const resData = res?.data?.data || {}
        list.value = resData.records
        total.value = resData.total
    })
}
src/components/UserOperate.vue
@@ -1,6 +1,7 @@
<script setup>
import { fullscreenToggel } from '@/utils/util';
import { useStore } from 'vuex';
import { ElMessage } from 'element-plus'
const router = useRouter();
const store = useStore();
const dropdownClick = val => {
@@ -20,6 +21,7 @@
}
const info = () =>{
    ElMessage.warning('加急开发中...')
  console.log('点击了个人信息');
}
</script>
src/const/drc.js
@@ -29,3 +29,12 @@
    { min: 315, max: 360, value: '北偏西' },
    { min: 360, max: 360, value: '正北' }
];
import hoverImg from '@/assets/images/aiNowFly/event/hover.png'
import startRecordImg from '@/assets/images/aiNowFly/event/startRecord.png'
import takePhotoImg from '@/assets/images/aiNowFly/event/takePhoto.png'
export const droneEventList = [
    {name:'开始录像',value:'startRecord',img:startRecordImg},
    {name:'悬停',value:'hover',img:hoverImg},
    {name:'拍照',value:'takePhoto',img:takePhotoImg},
]
src/hooks/useTaskWayline/useTaskWayline.js
@@ -4,9 +4,9 @@
 * @LastEditors: shuishen 1109946754@qq.com
 * @LastEditTime: 2025-04-19 19:00:36
 * @FilePath: \command-center-dashboard\src\hooks\useTaskWayline\useTaskWayline.js
 * @Description:
 *
 * Copyright (c) 2025 by shuishen, All Rights Reserved.
 * @Description:
 *
 * Copyright (c) 2025 by shuishen, All Rights Reserved.
 */
import lineImg from '@/assets/images/arrow-right-blue.png'
import rwqfdImg from '@/assets/images/signMachineNest/rwqfd.png'
@@ -117,8 +117,6 @@
  function setAircraftGltf () {
    const host = deviceOsdInfo.value?.data?.host
    console.log(host, 1111111111)
    const aircraftEntity = viewer?.entities.getById('aircraftGltf')
    const position = Cesium.Cartesian3.fromDegrees(host?.longitude, host?.latitude, host?.height)
    if (aircraftEntity) {
@@ -215,4 +213,4 @@
    removeEntitys,
    mapEntityRemove,
  }
}
}
src/layout/Header.vue
@@ -52,7 +52,7 @@
const handleClick = ({ path, name }) => {
  if (['系统运维'].includes(name)) {
    window.open(import.meta.env.VITE_APP_ADMIN_URL + '?token=' + localStorage.getItem(ELocalStorageKey.Token), '_blank');
    window.open(import.meta.env.VITE_APP_ADMIN_URL, '_blank');
    return;
  }
  if (!['首页', '任务管理'].includes(name)) return ElMessage.warning('正在加急开发中...');
src/views/Home/AINowFly.vue
@@ -16,14 +16,14 @@
            <!--            todo-->
            <el-form-item label="飞行事件">
                <div class="event">
                    <img src="@/assets/images/aiNowFly/picture-recording.png" alt="">
                    <img src="../../assets/images/aiNowFly/event/startRecord.png" alt="">
                    <div class="img-close" v-show="isPhoto">
                        <img @click="isPhoto=false" class="close" src="@/assets/images/aiNowFly/close.png" alt="">
                        <img src="@/assets/images/aiNowFly/photo.png" alt="">
                        <img src="../../assets/images/aiNowFly/event/takePhoto.png" alt="">
                    </div>
                    <div class="img-close" v-show="isStop">
                        <img @click="isStop=false" class="close" src="@/assets/images/aiNowFly/close.png" alt="">
                        <img src="@/assets/images/aiNowFly/stop.png" alt="">
                        <img src="../../assets/images/aiNowFly/event/hover.png" alt="">
                    </div>
                    <div class="add-event" @click="isShowEvent = !isShowEvent">+
                        <div class="event-select" v-show="isShowEvent">
@@ -159,7 +159,7 @@
      label: {
        text: `预计${item.minute}分钟`,
                font: 'bold 16px Source Han Sans CN',  // 加粗并增大字号
              // fillColor: new Cesium.Color(27/255, 179/255, 255/255, 1.0),
              // fillColor: new Cesium.Color(27/255, 179/255, 255/255, 1.0),
                // fillColor: Cesium.Color.WHITE,
                // style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                // verticalOrigin: Cesium.VerticalOrigin.CENTER,
@@ -354,12 +354,12 @@
    :deep(.el-select__wrapper) {
      background-color: #021740;
      box-shadow: 0 0 0 1px #026AD6;
      &.is-focus {
        box-shadow: 0 0 0 1px #026AD6;
      }
    }
    :deep(.el-input__wrapper.is-disabled) {
      background-color: #021740;
      box-shadow: 0 0 0 1px #026AD6;
@@ -440,7 +440,7 @@
            white-space: nowrap;
        }
    }
    .btn-submit {
        cursor: pointer;
        margin-left: 88px;
src/views/Home/HomeRight/EventOverview.vue
@@ -40,7 +40,7 @@
                <img class="completion-left-img" :src="completionLeft" alt="" />
                <div class="completion-text">事件类型完成率情况</div>
                <div class="completion-separator"></div>
                <img class="completion-left-img" :src="completionLeft" alt="" />
                <img class="completion-left-img" :src="leftTriangle" alt="" />
            </div>
            <div class="chart" ref="echartsRef"></div>
@@ -56,6 +56,7 @@
import overviewImg5 from '@/assets/images/home/homeRight/overview5.png'
import overviewImg6 from '@/assets/images/home/homeRight/overview6.png'
import completionLeft from '@/assets/images/home/homeRight/completionLeft.png'
import leftTriangle from '@/assets/images/home/homeRight/leftTriangle.png'
import * as echarts from 'echarts'
import CommonDateTime from '@/components/CommonDateTime.vue'
import VSelectLoad from '@/directive/selectLoad'
@@ -153,6 +154,9 @@
        width: 2, // 线条宽度
        type: 'solid', // 线条类型
    },
    tooltip: {
        valueFormatter: (value) => value + '%'
    },
    symbol: 'circle', // 数据点符号
    symbolSize: 6, // 数据点符号大小
    emphasis: {
src/views/Home/RSide.vue
@@ -40,6 +40,7 @@
import tc from '@/assets/images/rSide/tc.png'
import tc1 from '@/assets/images/rSide/tc1.png'
import cesiumOperation from '@/utils/cesium-tsa'
import { ElMessage } from 'element-plus'
let logIndex = ref(3)
const enterHover = value => {
@@ -61,6 +62,7 @@
        activeIndex.value = activeIndex.value === 1 ? null : value
    }
    if (value === 2) {
        ElMessage.warning('加急开发中...')
        activeIndex.value = activeIndex.value === 2 ? null : value
    }
}
src/views/SignMachineNest/MachineLeft/InspectionRaskDetails.vue
@@ -109,7 +109,7 @@
        },
    ],
}
import _ from 'lodash'
// chart.setOption(option);
const pieOption = {
@@ -138,7 +138,7 @@
                    tarValue = data[i].value
                }
            }
            let percentage = ((tarValue / total) * 100).toFixed(1)
            let percentage = total ? _.round(((tarValue / total) * 100),1) : 0
            return `${name} ${percentage}%`
        },
    },
@@ -167,7 +167,7 @@
                { value: 0, name: '处理中', itemStyle: { color: '#FFC398' } },
                { value: 0, name: '已完成', itemStyle: { color: '#AFD9FB' } },
                // { value: 0, name: '待分拨', itemStyle: { color: '#E9C81A' } },
                { value: 0, name: '已完结', itemStyle: { color: '#11C4FF' } },
            ],
        },
src/views/SignMachineNest/MachineRight/MachineStatus/MachineTableDetails/DeviceEvent.vue
@@ -70,7 +70,10 @@
const total = ref(0)
const distribution = row => {
    ElMessage.warning('正在加急开发中...')
    const orderNumber = row.event_num
    const adminUrl = import.meta.env.VITE_APP_ADMIN_URL
    const targetPath = `/tickets/ticket?orderNumber=${orderNumber}`;
    window.open(`${adminUrl}?redirect=${encodeURIComponent(targetPath)}`, '_blank');
}
const examine = row => {
    ElMessage.warning('正在加急开发中...')