Merge branch 'master' of http://139.196.74.78:10010/r/drone/command-center-dashboard
23 files modified
3 files added
| | |
| | | <meta name='apple-mobile-web-app-capable' content='yes'> |
| | | <meta name='apple-mobile-web-app-status-bar-style' content='black'> |
| | | <meta name='format-detection' content='telephone=no'> |
| | | <link rel='icon' href='/favicon.png' /> |
| | | <link rel="icon" href='/favicon.png' /> |
| | | <link rel='stylesheet' href='/fonts/font.css'> |
| | | <link rel='stylesheet' href='/iconfont/index.css'> |
| | | <link rel='stylesheet' href='/iconfont/avue/iconfont.css'> |
| | |
| | | } |
| | | |
| | | // 获得有效载荷控制 |
| | | export async function getPayloadControlApi(params) { |
| | | return await request({ |
| | | url:`${API_PREFIX}/devices/payload-control/requests`, |
| | | method:'get', |
| | | export function getPayloadControlApi(params) { |
| | | return request({ |
| | | url: `${API_PREFIX}/devices/payload-control/requests`, |
| | | method: 'get', |
| | | params |
| | | }) |
| | | } |
| | | |
| | | // 云台控制api |
| | | export async function ptzControlApi(key,params) { |
| | | return await request({ |
| | | export function ptzControlApi(key,params) { |
| | | return request({ |
| | | url:`${API_PREFIX}/devices/payload-control/payload/${key}`, |
| | | method:'get', |
| | | params |
| | |
| | | } |
| | | |
| | | // 拍照和录像 |
| | | export async function callPhotoAndVideoCmd(sn, type) { |
| | | return await request({ |
| | | export function callPhotoAndVideoCmd(sn, type) { |
| | | return request({ |
| | | url:`${API_PREFIX}/devices/${sn}/payload/photoAndVideoCmd/${type}`, |
| | | method:'get', |
| | | }) |
| | |
| | | |
| | | |
| | | // 相机参数调整 |
| | | export async function cameraParamsChangeApi(data) { |
| | | return await request({ |
| | | export function cameraParamsChangeApi(data) { |
| | | return request({ |
| | | url:`${API_PREFIX}/devices/payload-control/payload/zoom/level`, |
| | | method:'post', |
| | | data |
| | | }) |
| | | } |
| | | |
| | | export function getLiveCapacityApi(workspace_id,params) { |
| | | return request({ |
| | | url:`/drone-device-core/manage/api/v1/live/capacity${workspace_id}`, |
| | | method:'get', |
| | | params |
| | | }) |
| | | } |
| | | |
| | | // 云台重置 |
| | | export async function ptzResetModeApi(params) { |
| | | return await request({ |
| | | export function ptzResetModeApi(params) { |
| | | return request({ |
| | | url:`${API_PREFIX}/devices/payload-control/payload/reset`, |
| | | method:'get', |
| | | params |
| | |
| | | |
| | | &:hover { |
| | | cursor: pointer; |
| | | //background: radial-gradient(circle, #5d5c5e 0%, #514f52 50%, #3d3b3d 100%); |
| | | box-shadow: 0 0 20px 5px rgba(0, 0, 0, 0.3); |
| | | } |
| | | } |
| | |
| | | </div> |
| | | |
| | | <div class="speed"> |
| | | <el-icon class="btnIcon" @click="speed = speed + 1"> |
| | | <el-icon class="btnIcon" @click="speed = speed === 15 ? 15 : speed + 1"> |
| | | <Plus /> |
| | | </el-icon> |
| | | <div> |
| | |
| | | <br /> |
| | | m/s |
| | | </div> |
| | | <el-icon class="btnIcon" @click="speed = speed - 1"> |
| | | <el-icon class="btnIcon" @click="speed = speed === 0 ? 0 : speed - 1"> |
| | | <Minus /> |
| | | </el-icon> |
| | | </div> |
| | |
| | | let timer = null |
| | | let totalSeconds = 0 |
| | | |
| | | const workspace_id = computed(() => taskDetails?.value?.workspace_id) |
| | | const workspace_id = inject('workspace_id') |
| | | const list1 = [ |
| | | { key: KeyCode.KEY_Q, text: 'Q', icon: RefreshLeft }, |
| | | { key: KeyCode.KEY_W, text: 'W', icon: ArrowUp }, |
| | |
| | | speed: speed.value, |
| | | })) |
| | | |
| | | watch( |
| | | () => workspace_id.value, |
| | | watch(workspace_id, |
| | | async () => { |
| | | if (workspace_id.value && mqttState === null && client_id.value === '') { |
| | | await createConnect() |
| | |
| | | right: 0; |
| | | width: 1400px; |
| | | height: 217px; |
| | | background: linear-gradient(196deg, rgba(23, 23, 23, 0.11) 0%, rgba(6, 6, 6, 0.11) 100%); |
| | | background: rgba(31, 31, 31, 0.15); |
| | | backdrop-filter: blur(5px); |
| | | border-radius: 40px 0px 40px 40px; |
| | | display: flex; |
| | |
| | | box-shadow: 2px 4px 6px 0px rgba(0, 13, 26, 0.42); |
| | | border-radius: 8px 8px 8px 8px; |
| | | text-align: center; |
| | | padding: 5px 0; |
| | | padding: 10px 0; |
| | | |
| | | .btnIcon { |
| | | font-size: 20px; |
| | | cursor: pointer; |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | .divider { |
| | | position: absolute; |
| | | transform: translateX(90px); |
| | | transform: translateX(95px); |
| | | width: 0; |
| | | height: 137px; |
| | | border: 1px solid rgba(255, 255, 255, 0.07); |
| | |
| | | <!-- |
| | | * @Author: shuishen 1109946754@qq.com |
| | | * @Date: 2025-04-19 13:13:15 |
| | | * @LastEditors: shuishen 1109946754@qq.com |
| | | * @LastEditTime: 2025-04-19 15:07:04 |
| | | * @FilePath: \command-center-dashboard\src\components\CurrentTaskDetails\CurrentTaskDetails.vue |
| | | * @Description: |
| | | * |
| | | * Copyright (c) 2025 by shuishen, All Rights Reserved. |
| | | --> |
| | | <!--当前任务详情--> |
| | | <template> |
| | | <el-dialog |
| | |
| | | > |
| | | <div class="content-container" v-if="isShow"> |
| | | <!-- 视频直播 --> |
| | | <div :class="`${isMaxMap ? 'minBox' : 'maxBox'}`"> |
| | | <div :class="`${isMaxMap ? 'minBox' : 'maxBox'} centerPoint`"> |
| | | <LiveVideo :videoUrl="currentLiveUrl" :controls="false" /> |
| | | </div> |
| | | <!-- 展示地图 --> |
| | |
| | | <TaskDetailsHead /> |
| | | <TaskDetailsLeft /> |
| | | </template> |
| | | |
| | | <!-- 控制面板,里面有方法需要立即执行,不可用v-if --> |
| | | <!-- <ControlPanel />--> |
| | | <ControlPanel v-show="!isAutoControl" /> |
| | | <img alt="" :src="amplifyImg" class="amplify" @click="isMaxMap = !isMaxMap" /> |
| | | </div> |
| | |
| | | import { ElMessage } from 'element-plus' |
| | | import EventBus from '@/event-bus' |
| | | import { updateDroneQualityApi } from '@/api/drc' |
| | | import { getLiveAiLinkApi } from '@/api/payload' |
| | | import { getLiveAiLinkApi, getLiveCapacityApi } from '@/api/payload' |
| | | import { CURRENT_CONFIG } from '@/utils/http/config' |
| | | import { useDroneWS } from '@/hooks/useDroneWS' |
| | | import { useTaskDetails } from '@/hooks/useTaskDetails/useTaskDetails' |
| | | |
| | | const isAutoControl = ref(true) //是否自动控制 |
| | | const lineQuality = ref(1) //1流畅,2标清 |
| | | const taskDetailsViewer = ref(null) //地图实例 |
| | | let taskDetails = ref({}) //任务详情 |
| | | const deviceOsdInfo = computed(() => wsInfo.value?.device_osd) |
| | | const dockSn = computed(() => taskDetails?.value?.device_sns?.[0]) |
| | | const droneSn = computed(() => deviceOsdInfo?.value?.data?.sn) |
| | |
| | | const isTakeOff = ref(false) // 是在飞行中 |
| | | const isMaxMap = ref(false) //是大地图 |
| | | const client_id = ref('') //是大地图 |
| | | const workspace_id = ref('') |
| | | |
| | | // 获取机巢直播 |
| | | const getDeviceLiveUrl = async () => { |
| | | const res = await liveStart(dockSn.value, 2) |
| | | currentLiveUrl.value = res.data.data.rtcs_url |
| | | } |
| | | |
| | | //获取相机能力 |
| | | async function getLiveCapacity() { |
| | | const res = await getLiveCapacityApi(workspace_id.value,{ sn: dockSn.value}) |
| | | } |
| | | const useTaskDetailsCallBack = () => { |
| | | console.log(workspace_id.value,66666666) |
| | | getDeviceLiveUrl() |
| | | } |
| | | |
| | | let { taskDetails, workspace_id, getTaskDetails } = useTaskDetails(useTaskDetailsCallBack) |
| | | let { wsInfo, removeWS } = useDroneWS(workspace_id) //ws信息,是一个ref对象 |
| | | |
| | | provide('wsInfo', wsInfo) |
| | | provide('workspace_id', workspace_id) |
| | | provide('deviceOsdInfo', deviceOsdInfo) |
| | | provide('dockOsdInfo', wsInfo?.value?.dock_osd) |
| | | provide('dockSn', dockSn) |
| | |
| | | provide('video_id', video_id) |
| | | provide('client_id', client_id) |
| | | |
| | | let once = true |
| | | watch(deviceOsdInfo,()=>{ |
| | | if (once){ |
| | | // getLiveCapacity() |
| | | once =false |
| | | } |
| | | }) |
| | | |
| | | watch( |
| | | wsInfo, |
| | | () => { |
| | |
| | | }, |
| | | { deep: true } |
| | | ) |
| | | |
| | | // 获取机巢直播 |
| | | const getDeviceLiveUrl = async () => { |
| | | const res = await liveStart(dockSn.value, 2) |
| | | currentLiveUrl.value = res.data.data.rtcs_url |
| | | } |
| | | |
| | | const getAiLiveUrl = async () => { |
| | | const res = await getLiveAiLinkApi({ |
| | |
| | | isTakeOff.value = currentIsTakeOff |
| | | isTakeOff.value ? await getDroneLiveUrl() : await getDeviceLiveUrl() |
| | | } |
| | | // 获取任务详情获取航线文件 |
| | | const getTaskDetails = () => { |
| | | if (!props.id) ElMessage.warning('请检查是否传入id') |
| | | getJobDetails({ wayLineJobInfoId: props.id }).then(async res => { |
| | | taskDetails.value = res.data.data |
| | | await getDeviceLiveUrl() |
| | | taskDetails.value.workspace_id = taskDetails.value.way_lines[0]?.workspace_id |
| | | workspace_id.value = taskDetails.value.workspace_id |
| | | }) |
| | | } |
| | | |
| | | onMounted(() => { |
| | | getTaskDetails() |
| | | getTaskDetails(props?.id) |
| | | EventBus.on('CurrentTaskDetails-timeStop', changeLineQuality) |
| | | EventBus.on('CurrentTaskDetails-getAiLiveUrl', getAiLiveUrl) |
| | | EventBus.on('CurrentTaskDetails-getDroneLiveUrl', getDroneLiveUrl) |
| | |
| | | border-radius: 4rem; |
| | | overflow: hidden; |
| | | |
| | | .centerPoint { |
| | | &:before { |
| | | content: '+'; |
| | | font-size: 30px; |
| | | color: white; |
| | | position: absolute; |
| | | left: 50%; |
| | | top: 50%; |
| | | transform: translate(-50%, -50%); |
| | | pointer-events: none; |
| | | font-weight: bold; |
| | | text-shadow: -1px -1px 0 black, 1px -1px 0 black, -1px 1px 0 black, 1px 1px 0 black; /* 四方向描边 */ |
| | | } |
| | | } |
| | | |
| | | .maxBox { |
| | | width: 100%; |
| | | height: 100%; |
| | |
| | | position: absolute; |
| | | left: 340px; |
| | | bottom: 183px; |
| | | width: 22px; |
| | | height: 22px; |
| | | } |
| | | } |
| | | </style> |
| | |
| | | import * as Cesium from 'cesium' |
| | | import AmapMercatorTilingScheme from '@/utils/cesium/AmapMercatorTilingScheme' |
| | | import { Cartesian3, Terrain, Viewer } from 'cesium' |
| | | import endPointImg from '@/assets/images/EndPointicon.png' |
| | | import { addBlueFilter } from '@/utils/cesium/common' |
| | | import { analyzeKmzFile, removeTextKey, XMLToJSON } from '@/utils/cesium/kmz' |
| | | import rwqfdImg from '@/assets/images/signMachineNest/rwqfd.png' |
| | | 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' |
| | | import aircraftGltf from '@/assets/gltf/aircraft.gltf' |
| | | import { useTaskWayline } from '@/hooks/useTaskWayline/useTaskWayline' |
| | | |
| | | 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}', |
| | |
| | | destination: Cartesian3.fromDegrees(115.763819, 28.787374, 5000), |
| | | }) |
| | | } |
| | | |
| | | const drawWayline = lineObj => { |
| | | const positions = lineObj.Placemark.map(item => { |
| | | const [lon, lat] = item.Point.coordinates.split(',') |
| | | return Cartesian3.fromDegrees(Number(lon), Number(lat)) |
| | | }) |
| | | // 起点 |
| | | taskDetailsViewer?.value.entities.add({ |
| | | position: positions[0], |
| | | billboard: { |
| | | image: new Cesium.ConstantProperty(rwqfdImg), |
| | | width: 70, |
| | | height: 70, |
| | | }, |
| | | }) |
| | | // 终点 |
| | | taskDetailsViewer?.value.entities.add({ |
| | | position: positions[positions.length - 1], |
| | | billboard: { |
| | | image: new Cesium.ConstantProperty(endPointImg), |
| | | width: 30, |
| | | height: 30, |
| | | verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 底部对齐 |
| | | }, |
| | | }) |
| | | // 路径线 |
| | | taskDetailsViewer?.value.entities.add({ |
| | | polyline: { |
| | | width: 4, |
| | | positions: positions, |
| | | material: new ImageTrailMaterial({ |
| | | color: { alpha: 1, blue: 1, green: 1, red: 1 }, |
| | | speed: 20, |
| | | image: lineImg, |
| | | repeat: { x: Math.floor(40), y: 1 }, |
| | | }), |
| | | clampToGround: false, |
| | | }, |
| | | }) |
| | | } |
| | | |
| | | // 解析kmz文件 |
| | | 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 waylinesXMLObj = removeTextKey(waylinesXMLJSON.Folder) |
| | | if (!waylinesXMLObj.Placemark.length) return |
| | | const allPoint = waylinesXMLObj.Placemark.map(item => item.Point.coordinates.split(',')) |
| | | flyVisual(allPoint, taskDetailsViewer?.value) |
| | | drawWayline(waylinesXMLObj) |
| | | } |
| | | |
| | | |
| | | let viewInfoFrustum |
| | | // 设置视椎 |
| | |
| | | setCreateFrustum() |
| | | setAircraftGltf() |
| | | }) |
| | | watch(taskDetails, () => { |
| | | if (taskDetails.value.way_lines.length) { |
| | | parsingFiles(taskDetails.value.way_lines[0].url) |
| | | } |
| | | }) |
| | | |
| | | useTaskWayline(taskDetailsViewer, taskDetails) |
| | | |
| | | const removeMap = () => { |
| | | taskDetailsViewer?.value.entities.removeAll() |
| | |
| | | { index: 4, title: '经度', value: 0, unit: '°' }, |
| | | { index: 5, title: '纬度', value: 0, unit: '°' }, |
| | | { index: 6, title: '风速', value: 0, unit: 'M/s' }, |
| | | { index: 7, title: '4G信号', value: 0, unit: '' }, |
| | | { index: 8, title: 'SDR信号', value: 0, unit: '' }, |
| | | { index: 7, title: '4G信号', value: '-', unit: '' }, |
| | | { index: 8, title: 'SDR信号', value: '-', unit: '' }, |
| | | { index: 9, title: 'GPS搜星数', value: 0, unit: '' }, |
| | | { index: 10, title: 'RTK搜星数', value: 0, unit: '' }, |
| | | { index: 11, title: '距离机场', value: 0, unit: 'M' }, |
| | |
| | | <div @click="takePictures">拍照</div> |
| | | <div @click="recordFun">{{ isRecording ? '录像中...' : '录像' }}</div> |
| | | </div> |
| | | <div class="multiCol"> |
| | | <div class="multiCol" v-if="wsInfo?.psdk_widget_values"> |
| | | <div @click="shoutFun">{{ isRecordShouting ? '喊话' : '停止喊话' }}</div> |
| | | <div @click="broadcastFun">广播</div> |
| | | </div> |
| | |
| | | :max="cameraParams.camera_type === 'ir' ? 200 : 200" |
| | | @change="sliderChange" |
| | | /> |
| | | <div class="cameraZoomText">{{cameraParams.zoom_factor}}X</div> |
| | | </div> |
| | | </div> |
| | | <!-- 广播列表 --> |
| | |
| | | |
| | | <script setup> |
| | | import EventBus from '@/event-bus' |
| | | import { |
| | | callPhotoAndVideoCmd, |
| | | cameraParamsChangeApi, |
| | | ptzResetModeApi, |
| | | startVoice, |
| | | import { |
| | | callPhotoAndVideoCmd, |
| | | cameraParamsChangeApi, |
| | | ptzResetModeApi, |
| | | startVoice, |
| | | stayAwayRiver, |
| | | getVoiceFile, |
| | | playAudio, |
| | |
| | | import Recorder from 'js-audio-recorder'; |
| | | import dayjs from 'dayjs' |
| | | |
| | | const wsInfo = inject('wsInfo') |
| | | // 初始化喊话 |
| | | let globalShout = null |
| | | |
| | | const isRecording = ref(false) |
| | | |
| | | const list1 = ref([ |
| | |
| | | } |
| | | } |
| | | const tableList = ref([]); |
| | | // 分页相关 |
| | | // 分页相关 |
| | | const searchParams = ref({ |
| | | sn: droneSn.value, |
| | | name: '', |
| | |
| | | top: 50%; |
| | | transform: translateY(-60%); |
| | | width: 178px; |
| | | height: 416px; |
| | | padding: 20px 0; |
| | | background: rgba(0, 0, 0, 0.5); |
| | | backdrop-filter: blur(5px); |
| | | border-radius: 20px 20px 20px 20px; |
| | |
| | | |
| | | .cameraZoom { |
| | | position: absolute; |
| | | padding: 16px 0; |
| | | padding: 20px 0 15px 0; |
| | | left: 1600px; |
| | | top: -59px; |
| | | width: 112px; |
| | | width: 90px; |
| | | height: 490px; |
| | | background: rgba(64, 64, 64, 0.15); |
| | | background: rgba(0, 0, 0, 0.4); |
| | | backdrop-filter: blur(5rem); |
| | | border-radius: 20px 20px 20px 20px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | |
| | | .el-slider { |
| | | height: 100%; |
| | | flex: 1; |
| | | } |
| | | .cameraZoomText{ |
| | | font-family: Segoe UI, Segoe UI; |
| | | font-weight: 400; |
| | | font-size: 22px; |
| | | color: #ffffff; |
| | | margin-top: 20px; |
| | | } |
| | | } |
| | | |
| | |
| | | bottom: 10px; |
| | | height: 32px; |
| | | display: flex; |
| | | |
| | | |
| | | :deep(.number) { |
| | | color: #EDEDED; |
| | | } |
| | |
| | | </div> |
| | | </div> |
| | | |
| | | <BaseControl v-if="taskDetails.workspace_id" /> |
| | | <BaseControl v-if="workspace_id" /> |
| | | </div> |
| | | </template> |
| | | <script setup> |
| | |
| | | import BaseControl from '@/components/CurrentTaskDetails/ControlPanel/BaseControl.vue' |
| | | |
| | | const taskDetails = inject('taskDetails') |
| | | const workspace_id = inject('workspace_id') |
| | | |
| | | const list = ref([ |
| | | { name: '任务编号', value: '', field: 'job_info_num' }, |
| | |
| | | top: 0; |
| | | width: 297px; |
| | | height: 1002px; |
| | | background: rgba(31, 31, 31, 0.15); |
| | | background: rgba(31, 31, 31, 0.5); |
| | | backdrop-filter: blur(0.5rem); |
| | | border-radius: 0px 40px 40px 0px; |
| | | display: flex; |
| | | flex-direction: column; |
| | |
| | | const areaCode = props.data.region_code; |
| | | dataObj.value.region_name = props.data.region_name; |
| | | const res = await getDeviceInfoNum({ areaCode }); |
| | | console.log('首页地图弹框',res); |
| | | |
| | | |
| | | const resJob = await getTotalJobNum({ areaCode }); |
| | | dataObj.value.jobNum = resJob.data.data; |
| New file |
| | |
| | | import { ElMessage } from 'element-plus' |
| | | import { getJobDetails } from '@/api/home/task' |
| | | |
| | | export function useTaskDetails (cb) { |
| | | const taskDetails = ref({}) |
| | | const workspace_id = ref('') |
| | | |
| | | const getTaskDetails = async (id) => { |
| | | if (!id) ElMessage.warning('请检查是否传入id') |
| | | try { |
| | | const res = await getJobDetails({ wayLineJobInfoId: id }) |
| | | taskDetails.value = res.data.data |
| | | cb && await cb() |
| | | workspace_id.value = taskDetails.value.way_lines[0]?.workspace_id |
| | | return taskDetails.value |
| | | } catch (error) { |
| | | ElMessage.warning('获取任务详情失败') |
| | | throw error |
| | | } |
| | | } |
| | | |
| | | return { |
| | | taskDetails, |
| | | workspace_id, |
| | | getTaskDetails |
| | | } |
| | | } |
| New file |
| | |
| | | import * as Cesium from 'cesium' |
| | | import aircraftGltf from '@/assets/gltf/aircraft.gltf' |
| | | import CreateFrustum from '@/utils/cesium/frustum/CreateFrustum' |
| | | |
| | | export function useTaskViewInfo (viewer, wsInfo, removeEntitys) { |
| | | const newViewer = unref(viewer) |
| | | |
| | | let viewInfoFrustum |
| | | // 设置视椎 |
| | | const setCreateFrustum = (host) => { |
| | | if (!host) return |
| | | viewInfoFrustum?.clear() |
| | | |
| | | const attitude_head = 180 + host.attitude_head |
| | | const gimbal_pitch = 90 - Number(host?.payloads[0]?.gimbal_pitch) || 0 |
| | | |
| | | viewInfoFrustum = new CreateFrustum(newViewer, { |
| | | position: { |
| | | longitude: host.longitude, |
| | | latitude: host.latitude, |
| | | altitude: host.height, |
| | | }, |
| | | width: 30, |
| | | height: 30, |
| | | fov: 20.0, |
| | | near: 3.0, |
| | | far: 250.0, |
| | | roll: gimbal_pitch, |
| | | pitch: 0, |
| | | heading: attitude_head, |
| | | }) |
| | | } |
| | | |
| | | function setAircraftGltf () { |
| | | const host = deviceOsdInfo.value?.data?.host |
| | | const aircraftEntity = newViewer.entities.getById('aircraftGltf') |
| | | const position = Cesium.Cartesian3.fromDegrees(host.longitude, host.latitude, host.height) |
| | | if (aircraftEntity) { |
| | | aircraftEntity.position = new Cesium.ConstantPositionProperty(position) |
| | | return |
| | | } |
| | | |
| | | newViewer.entities.add({ |
| | | id: 'aircraftGltf', |
| | | position, |
| | | model: { |
| | | uri: aircraftGltf, // 或 .glb |
| | | scale: 1.0, // 缩放比例 |
| | | minimumPixelSize: 64, // 最小像素尺寸(保证模型远处可见) |
| | | maximumScale: 128, // 最大缩放(可选) |
| | | }, |
| | | }) |
| | | } |
| | | |
| | | // 视椎加载处理 |
| | | const deviceOsdInfo = computed(() => wsInfo.value?.device_osd) |
| | | |
| | | watch(deviceOsdInfo, () => { |
| | | const host = deviceOsdInfo.value?.data?.host |
| | | |
| | | if ([14, 0].includes(host.mode_code)) { |
| | | mapEntityRemove() |
| | | return |
| | | } |
| | | |
| | | setCreateFrustum(host) |
| | | setAircraftGltf() |
| | | }) |
| | | |
| | | const mapEntityRemove = () => { |
| | | viewInfoFrustum?.clear() |
| | | |
| | | newViewer.entities.removeById('aircraftGltf') |
| | | |
| | | removeEntitys && removeEntitys() |
| | | } |
| | | |
| | | onUnmounted(() => { |
| | | mapEntityRemove() |
| | | }) |
| | | } |
| New file |
| | |
| | | /* |
| | | * @Author: shuishen 1109946754@qq.com |
| | | * @Date: 2025-04-19 14:24:34 |
| | | * @LastEditors: shuishen 1109946754@qq.com |
| | | * @LastEditTime: 2025-04-19 15:23:57 |
| | | * @FilePath: \command-center-dashboard\src\hooks\useTaskWayline\useTaskWayline.js |
| | | * @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' |
| | | import endPointImg from '@/assets/images/EndPointicon.png' |
| | | |
| | | import { analyzeKmzFile, removeTextKey, XMLToJSON } from '@/utils/cesium/kmz' |
| | | import { flyVisual } from '@/utils/cesium/mapUtil' |
| | | import ImageTrailMaterial from '@/utils/cesium/ImageTrailMaterial' |
| | | |
| | | import * as Cesium from 'cesium' |
| | | import { Cartesian3 } from 'cesium' |
| | | |
| | | export function useTaskWayline (viewer, taskDetails) { |
| | | const newViewer = unref(viewer) |
| | | |
| | | // 解析kmz文件 |
| | | 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 waylinesXMLObj = removeTextKey(waylinesXMLJSON.Folder) |
| | | if (!waylinesXMLObj.Placemark.length) return |
| | | const allPoint = waylinesXMLObj.Placemark.map(item => item.Point.coordinates.split(',')) |
| | | flyVisual(allPoint, newViewer) |
| | | drawWayline(waylinesXMLObj) |
| | | } |
| | | |
| | | const drawWayline = lineObj => { |
| | | const positions = lineObj.Placemark.map(item => { |
| | | const [lon, lat] = item.Point.coordinates.split(',') |
| | | return Cartesian3.fromDegrees(Number(lon), Number(lat)) |
| | | }) |
| | | // 起点 |
| | | newViewer.entities.add({ |
| | | id: 'drone-job-wayline-start', |
| | | position: positions[0], |
| | | billboard: { |
| | | image: new Cesium.ConstantProperty(rwqfdImg), |
| | | width: 70, |
| | | height: 70, |
| | | }, |
| | | }) |
| | | |
| | | // 终点 |
| | | newViewer.entities.add({ |
| | | id: 'drone-job-wayline-end', |
| | | position: positions[positions.length - 1], |
| | | billboard: { |
| | | image: new Cesium.ConstantProperty(endPointImg), |
| | | width: 30, |
| | | height: 30, |
| | | verticalOrigin: Cesium.VerticalOrigin.BOTTOM, // 底部对齐 |
| | | }, |
| | | }) |
| | | |
| | | // 路径线 |
| | | newViewer.entities.add({ |
| | | id: 'drone-job-wayline-polyline', |
| | | polyline: { |
| | | width: 4, |
| | | positions: positions, |
| | | material: new ImageTrailMaterial({ |
| | | color: { alpha: 1, blue: 1, green: 1, red: 1 }, |
| | | speed: 20, |
| | | image: lineImg, |
| | | repeat: { x: Math.floor(40), y: 1 }, |
| | | }), |
| | | clampToGround: false, |
| | | }, |
| | | }) |
| | | } |
| | | |
| | | watch(taskDetails, () => { |
| | | if (taskDetails.value?.way_lines?.length) { |
| | | parsingFiles(taskDetails.value.way_lines[0].url) |
| | | } |
| | | }, { immediate: true }) |
| | | |
| | | const removeEntitys = () => { |
| | | const entitiesIDs = newViewer?.entities.values.map(i => i.id) |
| | | |
| | | entitiesIDs.forEach(item => { |
| | | item.includes('drone-job-wayline-') && newViewer?.entities.removeById(item) |
| | | }) |
| | | } |
| | | |
| | | onBeforeUnmount(() => { |
| | | removeEntitys() |
| | | }) |
| | | |
| | | return { |
| | | removeEntitys |
| | | } |
| | | } |
| | |
| | | const today = dayjs().format('YYYY-MM-DD') |
| | | const timeArr = ref([today, today]) |
| | | const completionRateSeries = { |
| | | name: '完成率', |
| | | name: '完结率', |
| | | type: 'line', |
| | | itemStyle: { |
| | | color: '#0CEBF7', // 设置颜色 |
| | |
| | | color: '#fff', |
| | | }, |
| | | data: [ |
| | | { value: 0, name: '待审核', itemStyle: { color: '#1860EC' } }, |
| | | { value: 0, name: '待处理', itemStyle: { color: '#47D107' } }, |
| | | { value: 0, name: '已完成', itemStyle: { color: '#F29509' } }, |
| | | { value: 0, name: '待审核', itemStyle: { color: '#8CFEA7' } }, |
| | | { value: 0, name: '待处理', itemStyle: { color: '#FF7411' } }, |
| | | { value: 0, name: '处理中', itemStyle: { color: '#FFC398' } }, |
| | | { value: 0, name: '已完成', itemStyle: { color: '#AFD9FB' } }, |
| | | // { value: 0, name: '待分拨', itemStyle: { color: '#E9C81A' } }, |
| | | { value: 0, name: '处理中', itemStyle: { color: '#0FC1E8' } }, |
| | | { value: 0, name: '已完结', itemStyle: { color: '#FE577F' } }, |
| | | |
| | | { value: 0, name: '已完结', itemStyle: { color: '#11C4FF' } }, |
| | | ], |
| | | }, |
| | | ], |
| | |
| | | <!-- 巡检任务列表 --> |
| | | <template> |
| | | <CommonTitle title="巡检任务列表" /> |
| | | <div :style="{ marginLeft: pxToRem(14) }"> |
| | | <div class="inspection-rask-list"> |
| | | <div class="tab-search"> |
| | | <div class="tab-btn"> |
| | | <div :class="tabIndex===1?'active':''" @click="tabClick(1)">当前任务</div> |
| | | <div :class="tabIndex===2?'active':''" @click="tabClick(2)">历史任务</div> |
| | | </div> |
| | | <div class="search-box"> |
| | | <el-input v-model="searchText" placeholder="请输入搜索内容" class="input-with-select"> |
| | | <template #append> |
| | | <el-button :icon="Search" @click="searchNickName"/> |
| | | </template> |
| | | </el-input> |
| | | </div> |
| | | </div> |
| | | <div class="table-list" v-if="tableList.length > 0" |
| | | infinite-scroll-distance="6" |
| | | v-infinite-scroll="loadMore" |
| | | :infinite-scroll-disabled="busy" |
| | | infinite-scroll-immediate="true">> |
| | | <div class="item" v-for="(item,index) in tableList"> |
| | | <div class="left" @click="taskClick(item)"> |
| | | <div class="left-t"> |
| | | <span>{{ index+1 }}.</span>{{ item.name }} |
| | | <span class="status" :class="item.status===2?'active':''"> |
| | | {{ getStatusText(item.status) }} |
| | | </span> |
| | | </div> |
| | | <div class="left-b"> |
| | | <img src="../../../assets/images/signMachineNest/machineRight/date.png" alt="" />{{ item.begin_time }} |
| | | <img src="../../../assets/images/signMachineNest/machineRight/name.png" alt="" />{{ item.creator_name || '' }} |
| | | </div> |
| | | </div> |
| | | <div class="right" v-if="tabIndex===1" @click="reExecute(item.dock_sn)"> |
| | | <span>立即返航</span> |
| | | <img src="../../../assets/images/signMachineNest/machineRight/return-fly.png" alt=""> |
| | | </div> |
| | | <div class="right" v-else @click="returnImmediately(item.job_id)"> |
| | | <span>再次执行</span> |
| | | <img src="../../../assets/images/signMachineNest/machineRight/return-fly.png" alt=""> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <el-empty class="custom-empty" v-else> |
| | | <template #description> |
| | | <span class="custom-text">暂无数据</span> |
| | | </template> |
| | | </el-empty> |
| | | </div> |
| | | </div> |
| | | <!-- 当前任务详情 --> |
| | | <CurrentTaskDetails |
| | | v-if="isShowCurrentTaskDetails" |
| | | v-model:show="isShowCurrentTaskDetails" |
| | | :id="currentInfoId"/> |
| | | <CommonTitle title="巡检任务列表" /> |
| | | <div :style="{ marginLeft: pxToRem(14) }"> |
| | | <div class="inspection-rask-list"> |
| | | <div class="tab-search"> |
| | | <div class="tab-btn"> |
| | | <div :class="tabIndex === 1 ? 'active' : ''" @click="tabClick(1)">当前任务</div> |
| | | <div :class="tabIndex === 2 ? 'active' : ''" @click="tabClick(2)">历史任务</div> |
| | | </div> |
| | | <div class="search-box"> |
| | | <el-input v-model="searchText" placeholder="请输入搜索内容" class="input-with-select"> |
| | | <template #append> |
| | | <el-button :icon="Search" @click="searchNickName" /> |
| | | </template> |
| | | </el-input> |
| | | </div> |
| | | </div> |
| | | <div |
| | | class="table-list" |
| | | v-if="tableList.length > 0" |
| | | infinite-scroll-distance="6" |
| | | v-infinite-scroll="loadMore" |
| | | :infinite-scroll-disabled="busy" |
| | | infinite-scroll-immediate="true" |
| | | > |
| | | > |
| | | <div class="item" v-for="(item, index) in tableList"> |
| | | <div class="left" @click="taskClick(item)"> |
| | | <div class="left-t"> |
| | | <span>{{ index + 1 }}.</span> |
| | | {{ item.name }} |
| | | <span class="status" :class="item.status === 2 ? 'active' : ''"> |
| | | {{ getStatusText(item.status) }} |
| | | </span> |
| | | </div> |
| | | <div class="left-b"> |
| | | <img src="../../../assets/images/signMachineNest/machineRight/date.png" alt="" /> |
| | | {{ item.begin_time }} |
| | | <img src="../../../assets/images/signMachineNest/machineRight/name.png" alt="" /> |
| | | {{ item.creator_name || '' }} |
| | | </div> |
| | | </div> |
| | | <div class="right" v-if="tabIndex === 1" @click="reExecute(item.dock_sn)"> |
| | | <span>立即返航</span> |
| | | <img src="../../../assets/images/signMachineNest/machineRight/return-fly.png" alt="" /> |
| | | </div> |
| | | <div class="right" v-else @click="returnImmediately(item.job_id)"> |
| | | <span>再次执行</span> |
| | | <img src="../../../assets/images/signMachineNest/machineRight/return-fly.png" alt="" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <el-empty class="custom-empty" v-else> |
| | | <template #description> |
| | | <span class="custom-text">暂无数据</span> |
| | | </template> |
| | | </el-empty> |
| | | </div> |
| | | </div> |
| | | <!-- 当前任务详情 --> |
| | | <CurrentTaskDetails v-if="isShowCurrentTaskDetails" v-model:show="isShowCurrentTaskDetails" :id="currentInfoId" /> |
| | | <!-- 历史任务详情 --> |
| | | <DeviceJobDetails |
| | | <DeviceJobDetails |
| | | v-if="isShowDeviceJobDetails" |
| | | v-model:show="isShowDeviceJobDetails" |
| | | :wayLineJodInfoId="wayLineJodInfoId"/> |
| | | :wayLineJodInfoId="wayLineJodInfoId" |
| | | /> |
| | | </template> |
| | | |
| | | <script setup> |
| | | import { Search } from '@element-plus/icons-vue'; |
| | | import { ElMessage } from 'element-plus'; |
| | | import CommonTitle from '@/components/CommonTitle.vue'; |
| | | import CurrentTaskDetails from '@/components/CurrentTaskDetails/CurrentTaskDetails.vue'; |
| | | import { Search } from '@element-plus/icons-vue' |
| | | import { ElMessage } from 'element-plus' |
| | | import CommonTitle from '@/components/CommonTitle.vue' |
| | | import CurrentTaskDetails from '@/components/CurrentTaskDetails/CurrentTaskDetails.vue' |
| | | import DeviceJobDetails from '@/components/DeviceJobDetails/DeviceJobDetails.vue' |
| | | import { getBeforeJob, getTodayJob, flyByJobId, returnHome } from '@/api/home'; |
| | | import { useStore } from 'vuex'; |
| | | import { getBeforeJob, getTodayJob, flyByJobId, returnHome } from '@/api/home' |
| | | import { useStore } from 'vuex' |
| | | import { useTaskWayline } from '@/hooks/useTaskWayline/useTaskWayline' |
| | | import { useTaskDetails } from '@/hooks/useTaskDetails/useTaskDetails' |
| | | import { useTaskViewInfo } from '@/hooks/useTaskViewInfo/useTaskViewInfo' |
| | | import { useDroneWS } from '@/hooks/useDroneWS' |
| | | let viewer = null |
| | | |
| | | const store = useStore(); |
| | | let { taskDetails, workspace_id, getTaskDetails } = useTaskDetails() |
| | | let { wsInfo, removeWS } = useDroneWS(workspace_id) //ws信息,是一个ref对象 |
| | | |
| | | const store = useStore() |
| | | // 设备任务详情 |
| | | let currentInfoId = ref(''); |
| | | let isShowCurrentTaskDetails = ref(false); |
| | | let wayLineJodInfoId = ref(''); |
| | | let isShowDeviceJobDetails = ref(false); |
| | | let currentInfoId = ref('') |
| | | let isShowCurrentTaskDetails = ref(false) |
| | | let wayLineJodInfoId = ref('') |
| | | let isShowDeviceJobDetails = ref(false) |
| | | |
| | | // 单个机巢信息 |
| | | const singleUavHome = computed(() => store.state.home.singleUavHome); |
| | | const singleUavHome = computed(() => store.state.home.singleUavHome) |
| | | |
| | | const isMore = ref(true); |
| | | const isMore = ref(true) |
| | | // 控制加载状态 |
| | | const busy = ref(false); |
| | | const busy = ref(false) |
| | | |
| | | let searchText = ref('') |
| | | const tableList = ref([]); |
| | | const tableList = ref([]) |
| | | |
| | | // 分页 |
| | | const pageParams = ref({ |
| | | current: 1, |
| | | size: 5, |
| | | total: 0 |
| | | }); |
| | | current: 1, |
| | | size: 5, |
| | | total: 0, |
| | | }) |
| | | |
| | | // 当前任务和历史任务切换 |
| | | let tabIndex = ref(1); |
| | | const tabClick = (value) => { |
| | | tabIndex.value = value; |
| | | clearData(); |
| | | }; |
| | | let tabIndex = ref(1) |
| | | const tabClick = value => { |
| | | tabIndex.value = value |
| | | clearData() |
| | | } |
| | | |
| | | // 状态文字判断 |
| | | const getStatusText = (status) => { |
| | | switch (status) { |
| | | case 1: |
| | | return '待执行'; |
| | | case 2: |
| | | return '执行中'; |
| | | case 3: |
| | | return '完成'; |
| | | case 4: |
| | | return '取消'; |
| | | case 5: |
| | | return '失败'; |
| | | default: |
| | | return '未知'; |
| | | } |
| | | }; |
| | | const getStatusText = status => { |
| | | switch (status) { |
| | | case 1: |
| | | return '待执行' |
| | | case 2: |
| | | return '执行中' |
| | | case 3: |
| | | return '完成' |
| | | case 4: |
| | | return '取消' |
| | | case 5: |
| | | return '失败' |
| | | default: |
| | | return '未知' |
| | | } |
| | | } |
| | | |
| | | // 获取历史巡检任务列表 |
| | | const getJobList = async () => { |
| | | const params = { |
| | | area_code: '', |
| | | device_sn: singleUavHome.value.device_sn, |
| | | job_name: searchText.value, |
| | | current: pageParams.value.current, |
| | | size: pageParams.value.size, |
| | | } |
| | | let result = null; |
| | | if (tabIndex.value === 1) { |
| | | result = await getTodayJob(params) |
| | | } else { |
| | | result = await getBeforeJob(params) |
| | | } |
| | | if (result.data.code !== 0) return; |
| | | if (result.data.data.records.length === 0) return (isMore.value = false); |
| | | pageParams.value.current += 1; |
| | | tableList.value = [...tableList.value, ...result.data.data.records]; |
| | | busy.value = false; |
| | | const params = { |
| | | area_code: '', |
| | | device_sn: singleUavHome.value.device_sn, |
| | | job_name: searchText.value, |
| | | current: pageParams.value.current, |
| | | size: pageParams.value.size, |
| | | } |
| | | let result = null |
| | | if (tabIndex.value === 1) { |
| | | result = await getTodayJob(params) |
| | | } else { |
| | | result = await getBeforeJob(params) |
| | | } |
| | | if (result.data.code !== 0) return |
| | | if (result.data.data.records.length === 0) return (isMore.value = false) |
| | | pageParams.value.current += 1 |
| | | tableList.value = [...tableList.value, ...result.data.data.records] |
| | | |
| | | busy.value = false |
| | | } |
| | | |
| | | // 清除数据 |
| | | const clearData = () => { |
| | | tableList.value = []; |
| | | pageParams.value.current = 1; |
| | | isMore.value = true; |
| | | getJobList(); |
| | | }; |
| | | tableList.value = [] |
| | | pageParams.value.current = 1 |
| | | isMore.value = true |
| | | getJobList() |
| | | } |
| | | |
| | | // 加载更多数据 |
| | | const loadMore = async () => { |
| | | busy.value = true; |
| | | if (!isMore.value) return; |
| | | getJobList(); |
| | | }; |
| | | busy.value = true |
| | | if (!isMore.value) return |
| | | getJobList() |
| | | } |
| | | |
| | | // 搜索数据 |
| | | const searchNickName = () => { |
| | | clearData(); |
| | | }; |
| | | clearData() |
| | | } |
| | | |
| | | // 立即返航 |
| | | const returnImmediately = (id) => { |
| | | flyByJobId(id).then(result => { |
| | | if (result.data.code === 0) { |
| | | ElMessage.success('执行成功'); |
| | | } else { |
| | | ElMessage.error(result.data.message); |
| | | } |
| | | }); |
| | | }; |
| | | const returnImmediately = id => { |
| | | flyByJobId(id).then(result => { |
| | | if (result.data.code === 0) { |
| | | ElMessage.success('执行成功') |
| | | } else { |
| | | ElMessage.error(result.data.message) |
| | | } |
| | | }) |
| | | } |
| | | // 重新执行 |
| | | const reExecute = (dock_sn) => { |
| | | returnHome(dock_sn).then(result => { |
| | | if (result.data.code === 0) { |
| | | ElMessage.success('返航成功'); |
| | | } else { |
| | | ElMessage.error(result.data.message); |
| | | } |
| | | }); |
| | | }; |
| | | const reExecute = dock_sn => { |
| | | returnHome(dock_sn).then(result => { |
| | | if (result.data.code === 0) { |
| | | ElMessage.success('返航成功') |
| | | } else { |
| | | ElMessage.error(result.data.message) |
| | | } |
| | | }) |
| | | } |
| | | |
| | | // 点击当前任务显示当前任务详情 |
| | | const taskClick = (item) => { |
| | | if (tabIndex.value === 1) { |
| | | // 展示当前任务详情 |
| | | currentInfoId.value = item.wayline_job_info_id; |
| | | isShowCurrentTaskDetails.value = true; |
| | | } else { |
| | | // 展示历史任务详情 |
| | | wayLineJodInfoId.value = item.wayline_job_info_id; |
| | | isShowDeviceJobDetails.value = true; |
| | | } |
| | | |
| | | }; |
| | | const taskClick = item => { |
| | | if (tabIndex.value === 1) { |
| | | // 展示当前任务详情 |
| | | currentInfoId.value = item.wayline_job_info_id |
| | | isShowCurrentTaskDetails.value = true |
| | | } else { |
| | | // 展示历史任务详情 |
| | | wayLineJodInfoId.value = item.wayline_job_info_id |
| | | isShowDeviceJobDetails.value = true |
| | | } |
| | | } |
| | | |
| | | onMounted(() => { |
| | | getJobList(); |
| | | }); |
| | | onMounted(async () => { |
| | | viewer = window.$viewer |
| | | |
| | | await getJobList() |
| | | |
| | | // tableList.value = [ |
| | | // { |
| | | // id: 11563, |
| | | // status: 5, |
| | | // begin_time: '2025/04/19 05:46:25', |
| | | // end_time: '2025/04/19 05:46:25', |
| | | // create_time: '2025-04-19T05:46:36.032+00:00', |
| | | // name: '智引即飞202504191252', |
| | | // event_number: 0, |
| | | // dock_sn: '7CTDLCR00BQBM2', |
| | | // wayline_job_info_id: 340, |
| | | // job_id: '484a7846-60e0-4ec3-b9fb-272a055c6938', |
| | | // }, |
| | | // { |
| | | // id: 11559, |
| | | // status: 5, |
| | | // begin_time: '2025/04/19 05:39:42', |
| | | // end_time: '2025/04/19 05:39:42', |
| | | // create_time: '2025-04-19T05:39:52.022+00:00', |
| | | // name: '智引即飞202504191252', |
| | | // event_number: 0, |
| | | // dock_sn: '7CTDLCR00BQBM2', |
| | | // wayline_job_info_id: 340, |
| | | // job_id: 'b8c58bfb-4352-47c5-ba33-242eec8df188', |
| | | // }, |
| | | // { |
| | | // id: 11558, |
| | | // status: 3, |
| | | // begin_time: '2025/04/19 05:39:17', |
| | | // end_time: '2025/04/19 05:39:17', |
| | | // create_time: '2025-04-19T05:39:27.407+00:00', |
| | | // name: '智引即飞202504191252', |
| | | // event_number: 0, |
| | | // dock_sn: '7CTDLCR00BQBM2', |
| | | // wayline_job_info_id: 340, |
| | | // job_id: '839773ce-bd46-4947-a2ef-d859eaea82f6', |
| | | // }, |
| | | // { |
| | | // id: 11557, |
| | | // status: 5, |
| | | // begin_time: '2025/04/19 04:52:04', |
| | | // end_time: '2025/04/19 04:52:04', |
| | | // create_time: '2025-04-19T04:52:14.503+00:00', |
| | | // name: '智引即飞202504191252', |
| | | // event_number: 0, |
| | | // dock_sn: '7CTDLCR00BQBM2', |
| | | // wayline_job_info_id: 340, |
| | | // job_id: '58366fd4-8361-456f-8ad9-3b881f933620', |
| | | // }, |
| | | // { |
| | | // id: 11556, |
| | | // status: 5, |
| | | // begin_time: '2025/04/19 03:45:06', |
| | | // end_time: '2025/04/19 03:45:06', |
| | | // create_time: '2025-04-19T03:45:16.522+00:00', |
| | | // name: '智引即飞202504191145', |
| | | // event_number: 0, |
| | | // dock_sn: '7CTDLCR00BQBM2', |
| | | // wayline_job_info_id: 338, |
| | | // job_id: '39c60fb8-e5c4-4474-912a-c3af3f1b9054', |
| | | // }, |
| | | // { |
| | | // id: 11555, |
| | | // status: 5, |
| | | // begin_time: '2025/04/19 03:27:55', |
| | | // end_time: '2025/04/19 03:27:55', |
| | | // create_time: '2025-04-19T03:28:05.196+00:00', |
| | | // name: '智引即飞202504191127', |
| | | // event_number: 0, |
| | | // dock_sn: '7CTDLCR00BQBM2', |
| | | // wayline_job_info_id: 336, |
| | | // job_id: '78c42532-45e4-4a01-b698-69bca19286ff', |
| | | // }, |
| | | // { |
| | | // id: 11554, |
| | | // status: 5, |
| | | // begin_time: '2025/04/19 02:58:53', |
| | | // end_time: '2025/04/19 02:58:53', |
| | | // create_time: '2025-04-19T02:59:03.602+00:00', |
| | | // name: '智引即飞202504191058', |
| | | // event_number: 0, |
| | | // dock_sn: '7CTDLCR00BQBM2', |
| | | // wayline_job_info_id: 335, |
| | | // job_id: 'd3b25edc-ec23-4ad6-800a-0f659a72bff3', |
| | | // }, |
| | | // { |
| | | // id: 11553, |
| | | // status: 5, |
| | | // begin_time: '2025/04/19 02:52:21', |
| | | // end_time: '2025/04/19 02:52:21', |
| | | // create_time: '2025-04-19T02:52:31.415+00:00', |
| | | // name: '智引即飞202504191052', |
| | | // event_number: 0, |
| | | // dock_sn: '7CTDLCR00BQBM2', |
| | | // wayline_job_info_id: 334, |
| | | // job_id: 'e27090f1-e740-4ae4-8d4d-301354eb0bbc', |
| | | // }, |
| | | // { |
| | | // id: 11548, |
| | | // status: 5, |
| | | // begin_time: '2025/04/19 02:23:06', |
| | | // end_time: '2025/04/19 02:23:06', |
| | | // create_time: '2025-04-19T02:23:16.151+00:00', |
| | | // name: '智引即飞202504191023', |
| | | // event_number: 0, |
| | | // dock_sn: '7CTDLCR00BQBM2', |
| | | // wayline_job_info_id: 332, |
| | | // job_id: '9e1f7283-fe34-4cfd-b468-cd2b34a96d34', |
| | | // }, |
| | | // { |
| | | // id: 11547, |
| | | // status: 3, |
| | | // begin_time: '2025/04/19 02:18:24', |
| | | // end_time: '2025/04/19 02:18:24', |
| | | // create_time: '2025-04-19T02:18:34.590+00:00', |
| | | // name: '智引即飞202504191018', |
| | | // event_number: 0, |
| | | // dock_sn: '7CTDLCR00BQBM2', |
| | | // wayline_job_info_id: 331, |
| | | // job_id: '7aa5222c-2f8b-401e-b6ba-d6a550398829', |
| | | // }, |
| | | // ] |
| | | |
| | | if (tableList.value.length > 0) { |
| | | await getTaskDetails(tableList.value[0].wayline_job_info_id) |
| | | } |
| | | }) |
| | | |
| | | const { removeEntitys } = useTaskWayline(viewer || window.$viewer, taskDetails) |
| | | |
| | | useTaskViewInfo(viewer || window.$viewer, wsInfo, removeEntitys) |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .inspection-rask-list { |
| | | width: 390px; |
| | | height: 325px; |
| | | background: linear-gradient( |
| | | 270deg, |
| | | #1f3e7a 0%, |
| | | rgba(31, 62, 122, 0.35) 79%, |
| | | rgba(31, 62, 122, 0) 100% |
| | | ); |
| | | opacity: 0.85; |
| | | margin: 2px 0 13 0; |
| | | padding: 11px 27px 0; |
| | | .tab-search { |
| | | width: 358px; |
| | | height: 76px; |
| | | margin-bottom: 12px; |
| | | .tab-btn { |
| | | display: flex; |
| | | justify-content: center; |
| | | margin-bottom: 16px; |
| | | div { |
| | | width: 104px; |
| | | height: 32px; |
| | | background: #0E2042; |
| | | border: 1px solid #8EA3D1; |
| | | font-family: Source Han Sans CN, Source Han Sans CN; |
| | | font-weight: 400; |
| | | font-size: 16px; |
| | | color: #8EA3D1; |
| | | line-height: 32px; |
| | | text-align: center; |
| | | cursor: pointer; |
| | | } |
| | | .active { |
| | | width: 104px; |
| | | height: 32px; |
| | | background: linear-gradient( 180deg, rgba(0,82,248,0.58) 0%, rgba(103,209,251,0.8) 100%); |
| | | color: #fff; |
| | | border: 1px solid rgba(103,209,251,0.8); |
| | | } |
| | | } |
| | | .search-box { |
| | | :deep(.el-input__wrapper) { |
| | | background-color: rgba(0, 112, 255, 0.1); |
| | | background: rgba(0, 15, 34, 0.5); |
| | | box-shadow: 0 0 0 1px #0070ff inset; |
| | | } |
| | | .inspection-rask-list { |
| | | width: 390px; |
| | | height: 325px; |
| | | background: linear-gradient(270deg, #1f3e7a 0%, rgba(31, 62, 122, 0.35) 79%, rgba(31, 62, 122, 0) 100%); |
| | | opacity: 0.85; |
| | | margin: 2px 0 13 0; |
| | | padding: 11px 27px 0; |
| | | .tab-search { |
| | | width: 358px; |
| | | height: 76px; |
| | | margin-bottom: 12px; |
| | | .tab-btn { |
| | | display: flex; |
| | | justify-content: center; |
| | | margin-bottom: 16px; |
| | | div { |
| | | width: 104px; |
| | | height: 32px; |
| | | background: #0e2042; |
| | | border: 1px solid #8ea3d1; |
| | | font-family: Source Han Sans CN, Source Han Sans CN; |
| | | font-weight: 400; |
| | | font-size: 16px; |
| | | color: #8ea3d1; |
| | | line-height: 32px; |
| | | text-align: center; |
| | | cursor: pointer; |
| | | } |
| | | .active { |
| | | width: 104px; |
| | | height: 32px; |
| | | background: linear-gradient(180deg, rgba(0, 82, 248, 0.58) 0%, rgba(103, 209, 251, 0.8) 100%); |
| | | color: #fff; |
| | | border: 1px solid rgba(103, 209, 251, 0.8); |
| | | } |
| | | } |
| | | .search-box { |
| | | :deep(.el-input__wrapper) { |
| | | background-color: rgba(0, 112, 255, 0.1); |
| | | background: rgba(0, 15, 34, 0.5); |
| | | box-shadow: 0 0 0 1px #0070ff inset; |
| | | } |
| | | |
| | | :deep(.el-input-group__append) { |
| | | background: rgba(0, 112, 255, 0.38); |
| | | .el-button { |
| | | background-color: transparent; |
| | | border: 1px solid #0070ff; |
| | | border-left: none; |
| | | color: #fff; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .table-list { |
| | | width: 358px; |
| | | height: 202px; |
| | | overflow: auto; |
| | | &::-webkit-scrollbar { |
| | | width: 0; |
| | | display: none; |
| | | } |
| | | -ms-overflow-style: none; /* IE and Edge */ |
| | | scrollbar-width: none; /* Firefox */ |
| | | .item { |
| | | width: 100%; |
| | | height: 72px; |
| | | padding: 12px 16px; |
| | | margin-bottom: 8px; |
| | | font-family: Source Han Sans CN, Source Han Sans CN; |
| | | font-weight: 500; |
| | | font-size: 14px; |
| | | color: #fff; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | background: linear-gradient( 90deg, rgba(71,157,255,0) 0%, rgba(71, 157, 255, 0.12) 50%, rgba(71,157,255,0) 100%); |
| | | .left { |
| | | cursor: pointer; |
| | | .left-t { |
| | | height: 24px; |
| | | font-size: 16px; |
| | | margin-bottom: 4px; |
| | | .status { |
| | | text-align: center; |
| | | font-size: 12px; |
| | | display: inline-block; |
| | | width: 48px; |
| | | height: 20px; |
| | | background: rgba(76,166,255,0.08); |
| | | border-radius: 4px 4px 4px 4px; |
| | | border: 1px solid #4CA6FF; |
| | | color: #4CA6FF; |
| | | margin-left: 10px; |
| | | } |
| | | .active { |
| | | border: 1px solid #04F0D1; |
| | | color: #04F0D1; |
| | | } |
| | | } |
| | | .left-b { |
| | | height: 21px; |
| | | line-height: 21px; |
| | | img { |
| | | width: 16px; |
| | | height: 16px; |
| | | margin-right: 2px; |
| | | &:last-child { |
| | | margin-left: 42px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .right { |
| | | cursor: pointer; |
| | | position: relative; |
| | | span { |
| | | position: absolute; |
| | | display: inline-block; |
| | | top: 12px; |
| | | left: 12px; |
| | | width: 26px; |
| | | line-height: 12px; |
| | | font-size: 12px; |
| | | font-family: YouSheBiaoTiHei, YouSheBiaoTiHei, serif; |
| | | color: rgb(4,18,44) |
| | | } |
| | | img { |
| | | width: 46px; |
| | | height: 46px |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .custom-empty { |
| | | color: #fff; |
| | | margin: 16px 0; |
| | | padding: 0; |
| | | :deep(.el-empty__image) { |
| | | width: 100px; |
| | | height: 100px; |
| | | } |
| | | } |
| | | } |
| | | :deep(.el-input-group__append) { |
| | | background: rgba(0, 112, 255, 0.38); |
| | | .el-button { |
| | | background-color: transparent; |
| | | border: 1px solid #0070ff; |
| | | border-left: none; |
| | | color: #fff; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .table-list { |
| | | width: 358px; |
| | | height: 202px; |
| | | overflow: auto; |
| | | &::-webkit-scrollbar { |
| | | width: 0; |
| | | display: none; |
| | | } |
| | | -ms-overflow-style: none; /* IE and Edge */ |
| | | scrollbar-width: none; /* Firefox */ |
| | | .item { |
| | | width: 100%; |
| | | height: 72px; |
| | | padding: 12px 16px; |
| | | margin-bottom: 8px; |
| | | font-family: Source Han Sans CN, Source Han Sans CN; |
| | | font-weight: 500; |
| | | font-size: 14px; |
| | | color: #fff; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | background: linear-gradient( |
| | | 90deg, |
| | | rgba(71, 157, 255, 0) 0%, |
| | | rgba(71, 157, 255, 0.12) 50%, |
| | | rgba(71, 157, 255, 0) 100% |
| | | ); |
| | | .left { |
| | | cursor: pointer; |
| | | .left-t { |
| | | height: 24px; |
| | | font-size: 16px; |
| | | margin-bottom: 4px; |
| | | .status { |
| | | text-align: center; |
| | | font-size: 12px; |
| | | display: inline-block; |
| | | width: 48px; |
| | | height: 20px; |
| | | background: rgba(76, 166, 255, 0.08); |
| | | border-radius: 4px 4px 4px 4px; |
| | | border: 1px solid #4ca6ff; |
| | | color: #4ca6ff; |
| | | margin-left: 10px; |
| | | } |
| | | .active { |
| | | border: 1px solid #04f0d1; |
| | | color: #04f0d1; |
| | | } |
| | | } |
| | | .left-b { |
| | | height: 21px; |
| | | line-height: 21px; |
| | | img { |
| | | width: 16px; |
| | | height: 16px; |
| | | margin-right: 2px; |
| | | &:last-child { |
| | | margin-left: 42px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .right { |
| | | cursor: pointer; |
| | | position: relative; |
| | | span { |
| | | position: absolute; |
| | | display: inline-block; |
| | | top: 12px; |
| | | left: 12px; |
| | | width: 26px; |
| | | line-height: 12px; |
| | | font-size: 12px; |
| | | font-family: YouSheBiaoTiHei, YouSheBiaoTiHei, serif; |
| | | color: rgb(4, 18, 44); |
| | | } |
| | | img { |
| | | width: 46px; |
| | | height: 46px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .custom-empty { |
| | | color: #fff; |
| | | margin: 16px 0; |
| | | padding: 0; |
| | | :deep(.el-empty__image) { |
| | | width: 100px; |
| | | height: 100px; |
| | | } |
| | | } |
| | | } |
| | | </style> |
| | |
| | | </div> |
| | | <div class="item"> |
| | | <el-date-picker |
| | | popper-class="custom-date-picker" |
| | | class="ztzf-date-picker" |
| | | v-model="taskData" |
| | | type="daterange" |
| | |
| | | <div class="item"> |
| | | <div class="itemchild">任务时间:</div> |
| | | <el-time-picker |
| | | popper-class="custom-time-picker" |
| | | class="ztzf-date-picker tasktimer" |
| | | v-model="timeSlot" |
| | | placeholder="请选择" |
| | |
| | | </div> |
| | | <div class="item"> |
| | | <div class="itemchild">选择航线:</div> |
| | | |
| | | |
| | | <el-select |
| | | :teleported="false" |
| | | class="ztzf-select" |
| | | v-model="searchForm.file_id" |
| | | @change="getWayLineFile" |
| | |
| | | </el-select> |
| | | </div> |
| | | <div class="item"> |
| | | |
| | | <div class="itemchild">关联算法:</div> |
| | | <TaskAlgorithmBusiness :setWidth="200" :showAlgorithm="true" @algorithmChange="algorithmChange" /> |
| | | <TaskAlgorithmBusiness :setWidth="200" :showAlgorithm="true" @algorithmChange="algorithmChange" /> |
| | | </div> |
| | | <div class="item"> |
| | | |
| | | <div class="itemchild">任务描述:</div> |
| | | <el-input class="ztzf-input" v-model="searchForm.remark" placeholder="请输入任务名称"></el-input> |
| | | </div> |
| | |
| | | /> |
| | | </div> |
| | | </div> |
| | | <div class="right "> |
| | | <div class="right"> |
| | | <TaskTable |
| | | ref="taskTableRef" |
| | | :waylineId="waylineId" |
| | |
| | | @update:selected="handleSelected" |
| | | /> |
| | | <div class="btn"> |
| | | <img @click="cancel" style="margin-right:23px" src="@/assets/images/task/cancel.png" alt=""> |
| | | <img @click="submitClick" src="@/assets/images/task/publish.png" alt=""> |
| | | |
| | | <img @click="cancel" style="margin-right: 23px" src="@/assets/images/task/cancel.png" alt="" /> |
| | | <img @click="submitClick" src="@/assets/images/task/publish.png" alt="" /> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | waylineType.value = 0 |
| | | waylineId.value = val |
| | | const currentRoute = routeOptions.value.find(item => item.wayline_id === val) |
| | | wayLineFile.value = currentRoute?.object_key || ''; |
| | | wayLineFile.value = currentRoute?.object_key || '' |
| | | } |
| | | |
| | | // 获取选中机场列表数据,并且发布 |
| | |
| | | }) |
| | | return |
| | | } |
| | | |
| | | |
| | | searchForm.begin_time = `${taskData.value[0]} 00:00:00` |
| | | searchForm.end_time = `${taskData.value[1]} 23:59:59` |
| | | searchForm.execute_time_arr = timeSlot.value ? [timeSlot.value] : []; |
| | | searchForm.execute_time_arr = timeSlot.value ? [timeSlot.value] : [] |
| | | createTask(searchForm).then(res => { |
| | | if (res.data.code === 0) { |
| | | ElMessage.success('任务创建成功') |
| | |
| | | padding: 20px; |
| | | } |
| | | } |
| | | /* 修改日期单元格背景色 */ |
| | | .custom-date-picker .el-picker-panel__body { |
| | | background: #012350 !important; |
| | | color: #fff !important; |
| | | } |
| | | |
| | | </style> |
| | | |
| | | <style lang="scss" scoped> |
| | |
| | | font-size: 16px; |
| | | color: #ffffff; |
| | | .itemchild { |
| | | width: 68px; |
| | | white-space: nowrap; |
| | | margin-right: 5px; |
| | | width: 68px; |
| | | white-space: nowrap; |
| | | margin-right: 5px; |
| | | } |
| | | :deep(.el-date-editor.el-input__wrapper) { |
| | | width: 200px; // 调整日期选择器宽度 |
| | | } |
| | | |
| | | } |
| | | } |
| | | } |
| | |
| | | justify-content: center; |
| | | align-items: center; |
| | | img { |
| | | width: 137px; |
| | | height: 32px; |
| | | cursor: pointer; |
| | | width: 137px; |
| | | height: 32px; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | } |
| | | } |
| | | :deep(.tasktimer .el-input__wrapper) { |
| | | background: none !important; |
| | | color: #8ac3fd !important; |
| | | |
| | | } |
| | | :deep(.el-input__inner) { |
| | | color: #8ac3fd !important; |
| | |
| | | color: #8ac3fd !important; |
| | | } |
| | | } |
| | | :deep(.el-radio__inner){ |
| | | background: none !important; |
| | | border: 1px solid #1B5D9A !important; |
| | | :deep(.el-radio__inner) { |
| | | background: none !important; |
| | | border: 1px solid #1b5d9a !important; |
| | | } |
| | | :deep(.el-radio__label){ |
| | | color: #fff !important; |
| | | :deep(.el-radio__label) { |
| | | color: #fff !important; |
| | | } |
| | | :deep(.el-radio__inner:after ){ |
| | | background: #65B5FF !important; |
| | | :deep(.el-radio__inner:after) { |
| | | background: #65b5ff !important; |
| | | } |
| | | } |
| | | </style> |
| | |
| | | <DeviceJobDetails |
| | | v-if="isShowDeviceJobDetails" |
| | | v-model:show="isShowDeviceJobDetails" |
| | | :wayLineJodInfoId="wayLineJodInfoId"/> |
| | | :wayLineJodInfoId="rowData.id"/> |
| | | </template> |
| | | |
| | | <script setup> |
| | |
| | | }); |
| | | const jobListData = ref([]); |
| | | const total = ref(0); |
| | | let wayLineJodInfoId = ref('') |
| | | let isShowDeviceJobDetails = ref(false); |
| | | let isShowCurrentTaskDetails = ref(false); |
| | | |
| | |
| | | let rowData = ref({}); |
| | | const handleDetail = (row) => { |
| | | if (row.device_sns.length === 1){ |
| | | console.log(row) |
| | | rowData.value = row? row : {}; |
| | | if (row.status === 2 || row.status === 1){ |
| | | rowData.value = row? row : {}; |
| | | isShowCurrentTaskDetails.value = true; |
| | | } else{ |
| | | wayLineJodInfoId.value = row.id |
| | | isShowDeviceJobDetails.value = true |
| | | } |
| | | }else{ |