Merge branch 'master' of http://139.196.74.78:10010/r/drone/command-center-dashboard
18 files modified
3 files renamed
1 files added
| | |
| | | #开发、测试、生产环境共用环境配置 |
| | | |
| | | VITE_APP_API = /api |
| | | #调试参数 |
| | | VITE_APP_DEBUG_KEY = saber |
| | |
| | | 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 |
| | |
| | | # 航线文件地址 |
| | | 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' |
| | |
| | | # 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' |
| | |
| | | # 航线文件地址 |
| | | 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' |
| | |
| | | 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 }, |
| | |
| | | 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() { |
| | |
| | | const droneSn = inject('droneSn') |
| | | const trueAltitude = inject('trueAltitude') |
| | | const client_id = inject('client_id') |
| | | const isBackDock = inject('isBackDock') |
| | | |
| | | const deviceTopicInfo = ref({ |
| | | pubTopic: '', |
| | |
| | | }) |
| | | } |
| | | |
| | | const isBackDock = ref(false) |
| | | |
| | | |
| | | // 返航 |
| | | async function onBackDock() { |
| | |
| | | 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' |
| | |
| | | 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 |
| | |
| | | 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) |
| | |
| | | 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, '-')}`, |
| | |
| | | |
| | | // 无人机直播画质切换 |
| | | 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) |
| | | // 如果还是之前的状态,不切换 |
| | |
| | | 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) |
| | |
| | | <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> |
| | | |
| | |
| | | <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') |
| | |
| | | { name: '关联算法', value: '', field: 'ai_type_str' }, |
| | | { name: '任务描述', value: '', field: 'remark' }, |
| | | ]) |
| | | // takePhoto,hover |
| | | const flightEvents = ref([]) |
| | | |
| | | watch( |
| | | taskDetails, |
| | | () => { |
| | |
| | | } |
| | | if (item.name === '飞行事件') { |
| | | const { action_modes = [] } = taskDetails?.value || {} |
| | | // todo item.value = action_modes. |
| | | flightEvents.value = action_modes.flatMap(({ actionActuatorFunc }) => |
| | | droneEventList.filter(({ value }) => actionActuatorFunc === value) |
| | | ) |
| | | } |
| | | }) |
| | | }, |
| | |
| | | } |
| | | |
| | | .taskInfo { |
| | | width: 220px; |
| | | width: 230px; |
| | | height: 520px; |
| | | display: flex; |
| | | overflow: auto; |
| | |
| | | .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; |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | 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 |
| | | |
| | | |
| | | }) |
| | | } |
| | | |
| | |
| | | <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 => { |
| | |
| | | } |
| | | |
| | | const info = () =>{ |
| | | ElMessage.warning('加急开发中...') |
| | | console.log('点击了个人信息'); |
| | | } |
| | | </script> |
| | |
| | | { 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}, |
| | | ] |
| | |
| | | * @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' |
| | |
| | | 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) { |
| | |
| | | removeEntitys, |
| | | mapEntityRemove, |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | 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('正在加急开发中...'); |
| | |
| | | <!-- 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"> |
| | |
| | | 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, |
| | |
| | | :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; |
| | |
| | | white-space: nowrap; |
| | | } |
| | | } |
| | | |
| | | |
| | | .btn-submit { |
| | | cursor: pointer; |
| | | margin-left: 88px; |
| | |
| | | <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> |
| | |
| | | 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' |
| | |
| | | width: 2, // 线条宽度 |
| | | type: 'solid', // 线条类型 |
| | | }, |
| | | tooltip: { |
| | | valueFormatter: (value) => value + '%' |
| | | }, |
| | | symbol: 'circle', // 数据点符号 |
| | | symbolSize: 6, // 数据点符号大小 |
| | | emphasis: { |
| | |
| | | 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 => { |
| | |
| | | activeIndex.value = activeIndex.value === 1 ? null : value |
| | | } |
| | | if (value === 2) { |
| | | ElMessage.warning('加急开发中...') |
| | | activeIndex.value = activeIndex.value === 2 ? null : value |
| | | } |
| | | } |
| | |
| | | }, |
| | | ], |
| | | } |
| | | |
| | | import _ from 'lodash' |
| | | // chart.setOption(option); |
| | | |
| | | const pieOption = { |
| | |
| | | tarValue = data[i].value |
| | | } |
| | | } |
| | | let percentage = ((tarValue / total) * 100).toFixed(1) |
| | | let percentage = total ? _.round(((tarValue / total) * 100),1) : 0 |
| | | return `${name} ${percentage}%` |
| | | }, |
| | | }, |
| | |
| | | { value: 0, name: '处理中', itemStyle: { color: '#FFC398' } }, |
| | | { value: 0, name: '已完成', itemStyle: { color: '#AFD9FB' } }, |
| | | // { value: 0, name: '待分拨', itemStyle: { color: '#E9C81A' } }, |
| | | |
| | | |
| | | { value: 0, name: '已完结', itemStyle: { color: '#11C4FF' } }, |
| | | ], |
| | | }, |
| | |
| | | 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('正在加急开发中...') |