Merge branch 'master' of http://139.196.74.78:10010/r/drone/command-center-dashboard
20 files modified
1 files renamed
| | |
| | | // 拍照和录像 |
| | | export async function callPhotoAndVideoCmd(sn, type) { |
| | | return await request({ |
| | | url:`/drone-device-core/droneAirport/liveStreamApi/${sn}/payload/photoAndVideoCmd/${type}`, |
| | | url:`${API_PREFIX}/devices/${sn}/payload/photoAndVideoCmd/${type}`, |
| | | method:'get', |
| | | }) |
| | | } |
| | |
| | | }; |
| | | |
| | | |
| | | // 无人机开启ai |
| | | export const getLiveAiLinkApi = (data) => { |
| | | return request({ |
| | | url:`/drone-device-core/manage/api/v1/live/streams/start/ai`, |
| | | method: 'post', |
| | | data, |
| | | }); |
| | | }; |
| | | |
| | | |
| | | |
| File was renamed from src/components/CurrentTaskDetails/ControlComPass/ControlComPass.vue |
| | |
| | | <div class="left-img" :data-text="`${attitude_pitch}°`"> |
| | | <div class="scaleImg"> |
| | | <p class="scale" :style="{ top: 45 + ScaleTop + 'px' }"></p> |
| | | <img src="../../../assets/images/rightmapidentification.png" /> |
| | | <img src="../../../../assets/images/rightmapidentification.png" /> |
| | | </div> |
| | | </div> |
| | | <div class="instrument-center"> |
| | |
| | | </div> |
| | | </div> |
| | | <div class="center-show"> |
| | | <img src="../../../assets/images/mapidentification.png" /> |
| | | <img src="../../../../assets/images/mapidentification.png" /> |
| | | </div> |
| | | <div class="rotat-btn"></div> |
| | | </div> |
| | | <div class="right-img" :data-text="`${height}m`"> |
| | | <div class="ident-arrow"> |
| | | <img src="../../../assets/images/leftmapidentification.png" /> |
| | | <img src="../../../../assets/images/leftmapidentification.png" /> |
| | | <div class="arrow-box" :style="{ bottom: realHeight }"> |
| | | <div class="arrow"></div> |
| | | </div> |
| | |
| | | </div> |
| | | </template> |
| | | <script setup> |
| | | import ControlComPass from '../ControlComPass/ControlComPass.vue' |
| | | import ControlComPass from '@/components/CurrentTaskDetails/ControlPanel/ControlComPass/ControlComPass.vue' |
| | | import { KeyCode, useManualControl } from '@/hooks/controlDrone/useManualControl' |
| | | import { droneController, exitController, postDrc, returnHome, returnHomeCancel } from '@/api/drc' |
| | | import { ElMessage } from 'element-plus' |
| | |
| | | RefreshLeft, |
| | | RefreshRight, |
| | | } from '@element-plus/icons-vue' |
| | | |
| | | import _ from 'lodash' |
| | | import BaseControl from '@/components/CurrentTaskDetails/ControlPanel/BaseControl.vue' |
| | | import EventBus from '@/event-bus' |
| | | import { getPayloadControlApi, ptzControlApi } from '@/api/payload' |
| | | |
| | | const deviceOsdInfo = inject('deviceOsdInfo') |
| | | const host = computed(() => deviceOsdInfo?.value?.data?.host || {}) |
| | | const taskDetails = inject('taskDetails') |
| | | const dockSn = inject('dockSn') |
| | | const droneSn = inject('droneSn') |
| | |
| | | { name: '左', key: KeyCode.ARROW_LEFT, operate: 'left', style: { left: '-70%' } }, |
| | | ] |
| | | |
| | | const list4 = [ |
| | | [ |
| | | { name: '焦距倍数', value: '0' }, |
| | | { name: '俯仰角度', value: '0.0°' }, |
| | | { name: '横向角度', value: '0.0°' }, |
| | | ], |
| | | [ |
| | | { name: '储存', value: '64.5G' }, |
| | | { name: '方向', value: '正北' }, |
| | | { name: '方向', value: '正北' }, |
| | | ], |
| | | ] |
| | | const list4 = computed(() => { |
| | | const { longitude, latitude, height, payloads } = host?.value || {} |
| | | const { gimbal_pitch } = payloads?.[0] || {} //俯仰角度 |
| | | return [ |
| | | [ |
| | | { name: '焦距倍数', value: '0' }, |
| | | { name: '俯仰角度', value: pitchAngle.value.angle }, |
| | | { name: '横向角度', value: yawAngle.value.angle }, |
| | | ], |
| | | [ |
| | | { name: '储存', value: '64.5G' }, |
| | | { name: '方向', value: pitchAngle.value.direction }, |
| | | { name: '方向', value: yawAngle.value.direction }, |
| | | ], |
| | | ] |
| | | }) |
| | | const pitchAngle = computed(() => { |
| | | const { longitude, latitude, height, payloads } = host?.value || {} |
| | | const gimbal_pitch = payloads?.[0]?.gimbal_pitch || 0 |
| | | let direction = '' |
| | | if (gimbal_pitch > -2 && gimbal_pitch < 2) { |
| | | direction = '正前' |
| | | } else if (gimbal_pitch >= 2 && gimbal_pitch < 90) { |
| | | direction = '斜上' |
| | | } else if (gimbal_pitch === 90) { |
| | | direction = '正上' |
| | | } else if (gimbal_pitch <= -2 && gimbal_pitch > -90) { |
| | | direction = '斜下' |
| | | } else if (gimbal_pitch === -90 || gimbal_pitch < -90) { |
| | | direction = '正下' |
| | | } |
| | | return { |
| | | angle: _.round(gimbal_pitch || 0, 1) + '°', |
| | | direction, |
| | | } |
| | | }) |
| | | |
| | | const yawAngle = computed(() => { |
| | | let { longitude, latitude, height, payloads, attitude_head } = host?.value || {} |
| | | const gimbal_pitch = payloads?.[0]?.gimbal_pitch || 0 |
| | | const gimbal_yaw = payloads?.[0]?.gimbal_yaw || 0 |
| | | attitude_head = attitude_head || 0 |
| | | let yaw = '' |
| | | if (gimbal_yaw > 180) { |
| | | yaw = gimbal_yaw |
| | | } else { |
| | | yaw = gimbal_yaw - attitude_head |
| | | } |
| | | let result = 0 |
| | | if (yaw < 0) { |
| | | result = yaw + 360 |
| | | } |
| | | if (yaw > 0) { |
| | | result = yaw |
| | | } |
| | | if ((yaw > -2 && yaw < 2) || parseInt(attitude_head) === parseInt(gimbal_yaw)) { |
| | | result = attitude_head < 0 ? 180 + (180 + attitude_head) : attitude_head |
| | | } |
| | | let direction = '' |
| | | const roundResult = Math.round(result) |
| | | if (roundResult === 0) { |
| | | direction = '正北' |
| | | } else if (roundResult > 0 && roundResult < 45) { |
| | | direction = '北偏东' |
| | | } else if (roundResult === 45) { |
| | | direction = '东北' |
| | | } else if (roundResult > 45 && roundResult < 90) { |
| | | direction = '北偏东' |
| | | } else if (roundResult === 90) { |
| | | direction = '正东' |
| | | } else if (roundResult > 90 && roundResult < 135) { |
| | | direction = '东偏南' |
| | | } else if (roundResult === 135) { |
| | | direction = '东南' |
| | | } else if (roundResult > 135 && roundResult < 180) { |
| | | direction = '南偏东' |
| | | } else if (roundResult === 180) { |
| | | direction = '正南' |
| | | } else if (roundResult > 180 && roundResult < 225) { |
| | | direction = '南偏西' |
| | | } else if (roundResult === 225) { |
| | | direction = '西南' |
| | | } else if (roundResult > 225 && roundResult < 270) { |
| | | direction = '西偏南' |
| | | } else if (roundResult === 270) { |
| | | direction = '正西' |
| | | } else if (roundResult > 270 && roundResult < 315) { |
| | | direction = '西偏北' |
| | | } else if (roundResult === 315) { |
| | | direction = '西北' |
| | | } else if (roundResult > 315 && roundResult < 360) { |
| | | direction = '北偏西' |
| | | } else if (roundResult === 360) { |
| | | direction = '正北' |
| | | } |
| | | return { |
| | | angle: _.round(result, 1) + '°', |
| | | direction, |
| | | } |
| | | }) |
| | | |
| | | const deviceTopicInfo = ref({ |
| | | pubTopic: '', |
| | |
| | | import { ElMessage } from 'element-plus' |
| | | import EventBus from '@/event-bus' |
| | | import { updateDroneQualityApi } from '@/api/drc' |
| | | import { getLiveAiLinkApi } from '@/api/payload' |
| | | import { CURRENT_CONFIG } from '@/utils/http/config' |
| | | |
| | | const isAutoControl = ref(true) |
| | | const lineQuality = ref(1) //1流畅,2标清 |
| | | provide('isAutoControl', isAutoControl) |
| | | provide('lineQuality', lineQuality) |
| | | |
| | | const taskDetailsViewer = ref(null) |
| | | provide('taskDetailsViewer', taskDetailsViewer) |
| | | |
| | | let taskDetails = ref({}) |
| | | const deviceOsdInfo = ref({}) |
| | | provide('taskDetails', taskDetails) |
| | | const deviceOsdInfo = ref({}) |
| | | provide('deviceOsdInfo', deviceOsdInfo) |
| | | |
| | | const dockSn = computed(() => taskDetails?.value?.device_sns?.[0]) |
| | | const droneSn = computed(() => deviceOsdInfo?.value?.data?.sn) |
| | | provide('dockSn', dockSn) |
| | | provide('droneSn', droneSn) |
| | | |
| | | const isAiLive = ref(false) |
| | | provide('isAiLive', isAiLive) |
| | | |
| | | const isShow = defineModel('show') |
| | | const props = defineProps(['id']) |
| | |
| | | const isMaxMap = ref(false) |
| | | let droneWebSocket //WS实例 |
| | | |
| | | // 机巢直播 |
| | | const video_id = ref('') |
| | | provide('video_id', video_id) |
| | | |
| | | // 获取机巢直播 |
| | | const getDeviceLiveUrl = async () => { |
| | | const res = await liveStart(dockSn.value, 2) |
| | | currentLiveUrl.value = res.data.data.rtcs_url |
| | | } |
| | | |
| | | const video_id = ref('') |
| | | const getAiLiveUrl = ()=>{ |
| | | const res = getLiveAiLinkApi({ original_stream_url: `${CURRENT_CONFIG.rtmpURL}${video_id.value}`, video_id:video_id.value }) |
| | | currentLiveUrl.value = res.data.data.rtcs_url |
| | | isAiLive.value = true |
| | | } |
| | | |
| | | // 获取无人机直播url |
| | | async function getDroneLiveUrl() { |
| | | const res = await liveStart(droneSn.value, lineQuality.value) |
| | | currentLiveUrl.value = res.data.data.rtcs_url |
| | | video_id.value = res.data.data.video_id |
| | | isAiLive.value = false |
| | | } |
| | | |
| | | // 无人机直播画质切换 |
| | |
| | | }) |
| | | } |
| | | |
| | | const dockOsdInfo = ref({}) |
| | | provide('dockOsdInfo', dockOsdInfo) |
| | | |
| | | const wsInfo = ref({}) |
| | | provide('wsInfo', wsInfo) |
| | | // websocket 的消息回调 |
| | | const messageHandler = result => { |
| | | let payload = JSON.parse(result) |
| | | wsInfo.value[payload.biz_code] = payload |
| | | switch (payload.biz_code) { |
| | | // 无人机 |
| | | case EBizCode.DeviceOsd: { |
| | | deviceOsdInfo.value = payload |
| | | setCurrentLiveUrl() |
| | | console.log(deviceOsdInfo.value, 'device_osd信息') |
| | | console.log(payload, 'DeviceOsd--信息') |
| | | break |
| | | } |
| | | // 遥控器 |
| | | case EBizCode.GatewayOsd: { |
| | | console.log(payload, 'GatewayOsd--信息') |
| | | break |
| | | } |
| | | // 机巢 |
| | | case EBizCode.DockOsd: { |
| | | console.log(payload, 'DockOsd--信息') |
| | | break |
| | | } |
| | | // PsdkWidgetValues |
| | | case EBizCode.PsdkWidgetValues: { |
| | | console.log(payload, 'PsdkWidgetValues--信息') |
| | | break |
| | | } |
| | | // VideoSurveillance |
| | | case EBizCode.VideoSurveillance: { |
| | | console.log(payload, 'VideoSurveillance--信息') |
| | | break |
| | | } |
| | | default: |
| | |
| | | onMounted(() => { |
| | | getTaskDetails() |
| | | EventBus.on('CurrentTaskDetails-timeStop', changeLineQuality) |
| | | EventBus.on('CurrentTaskDetails-getAiLiveUrl', getAiLiveUrl) |
| | | EventBus.on('CurrentTaskDetails-getDroneLiveUrl', getDroneLiveUrl) |
| | | }) |
| | | |
| | | onBeforeUnmount(() => { |
| | |
| | | deviceOsdInfo.value = {} |
| | | droneWebSocket = null |
| | | EventBus.off('CurrentTaskDetails-timeStop', changeLineQuality) |
| | | EventBus.off('CurrentTaskDetails-getAiLiveUrl', getAiLiveUrl) |
| | | EventBus.off('CurrentTaskDetails-getDroneLiveUrl', getDroneLiveUrl) |
| | | }) |
| | | </script> |
| | | |
| | |
| | | tilingScheme: new AmapMercatorTilingScheme(), |
| | | credit: 'amap_SL', |
| | | }) |
| | | let viewer = null |
| | | |
| | | const taskDetailsViewer = inject('taskDetailsViewer') |
| | | |
| | | const taskDetails = inject('taskDetails') |
| | | const deviceOsdInfo = inject('deviceOsdInfo') |
| | | |
| | | const initMap = () => { |
| | | viewer = new Viewer('currentTaskMap', { |
| | | taskDetailsViewer.value = new Viewer('currentTaskMap', { |
| | | terrain: Terrain.fromWorldTerrain(), |
| | | infoBox: false, // 禁用沙箱,解决控制台报错 |
| | | animation: false, // 左下角的动画仪表盘 |
| | |
| | | baseLayer: false, |
| | | fullscreenButton: false, |
| | | }) |
| | | const gdLayer = viewer.imageryLayers.addImageryProvider(imageryProvider_ammapSL) |
| | | const gdLayer = taskDetailsViewer?.value.imageryLayers.addImageryProvider(imageryProvider_ammapSL) |
| | | const options = { |
| | | bInvertColor: true, |
| | | bFilterColor: true, |
| | | filterColor: '#4e70a6', |
| | | } |
| | | // 添加蓝色滤镜 |
| | | addBlueFilter(options, viewer, gdLayer) |
| | | viewer.scene.morphTo2D(0) |
| | | addBlueFilter(options, taskDetailsViewer?.value, gdLayer) |
| | | taskDetailsViewer?.value.scene.morphTo2D(0) |
| | | //设置默认点 |
| | | viewer.camera.setView({ |
| | | taskDetailsViewer?.value.camera.setView({ |
| | | destination: Cartesian3.fromDegrees(115.763819, 28.787374, 5000), |
| | | }) |
| | | } |
| | |
| | | return Cartesian3.fromDegrees(Number(lon), Number(lat)) |
| | | }) |
| | | // 起点 |
| | | viewer.entities.add({ |
| | | taskDetailsViewer?.value.entities.add({ |
| | | position: positions[0], |
| | | billboard: { |
| | | image: new Cesium.ConstantProperty(rwqfdImg), |
| | |
| | | }, |
| | | }) |
| | | // 终点 |
| | | viewer.entities.add({ |
| | | taskDetailsViewer?.value.entities.add({ |
| | | position: positions[positions.length - 1], |
| | | billboard: { |
| | | image: new Cesium.ConstantProperty(endPointImg), |
| | |
| | | }, |
| | | }) |
| | | // 路径线 |
| | | viewer.entities.add({ |
| | | taskDetailsViewer?.value.entities.add({ |
| | | polyline: { |
| | | width: 4, |
| | | positions: positions, |
| | |
| | | const waylinesXMLObj = removeTextKey(waylinesXMLJSON.Folder) |
| | | if (!waylinesXMLObj.Placemark.length) return |
| | | const allPoint = waylinesXMLObj.Placemark.map(item => item.Point.coordinates.split(',')) |
| | | flyVisual(allPoint, viewer) |
| | | flyVisual(allPoint, taskDetailsViewer?.value) |
| | | drawWayline(waylinesXMLObj) |
| | | } |
| | | |
| | | const removeMap = () => { |
| | | viewer.entities.removeAll() |
| | | viewer.destroy() |
| | | taskDetailsViewer?.value.entities.removeAll() |
| | | taskDetailsViewer?.value.destroy() |
| | | } |
| | | |
| | | let viewInfoFrustum |
| | | // 设置视椎 |
| | | const setCreateFrustum = () => { |
| | | const deviceInfo = deviceOsdInfo.value?.data?.host |
| | | if (!deviceInfo) return |
| | | const host = deviceOsdInfo.value?.data?.host |
| | | if (!host) return |
| | | viewInfoFrustum?.clear() |
| | | if ([14, 0].includes(deviceInfo.mode_code)) return |
| | | const attitude_head = 180 + deviceInfo.attitude_head |
| | | const gimbal_pitch = 90 - Number(deviceInfo?.payloads[0]?.gimbal_pitch) || 0 |
| | | viewInfoFrustum = new CreateFrustum(viewer, { |
| | | if ([14, 0].includes(host.mode_code)) return |
| | | const attitude_head = 180 + host.attitude_head |
| | | const gimbal_pitch = 90 - Number(host?.payloads[0]?.gimbal_pitch) || 0 |
| | | viewInfoFrustum = new CreateFrustum(taskDetailsViewer?.value, { |
| | | position: { |
| | | longitude: deviceInfo.longitude, |
| | | latitude: deviceInfo.latitude, |
| | | altitude: deviceInfo.height, |
| | | longitude: host.longitude, |
| | | latitude: host.latitude, |
| | | altitude: host.height, |
| | | }, |
| | | width: 30, |
| | | height: 30, |
| | |
| | | <template> |
| | | <div class="detailsHead"> |
| | | <div class="droneName">小蓝工业园</div> |
| | | <div class="droneName" :title="taskDetails.name">{{ taskDetails.name }}</div> |
| | | <div class="infoListBox"> |
| | | <div v-for="item in infoList"> |
| | | <div class="infoValue">{{ item.value }}</div> |
| | | <div class="infoValue" :title="item.value">{{ item.value }}{{ item.unit }}</div> |
| | | <div class="infoTitle">{{ item.title }}</div> |
| | | </div> |
| | | </div> |
| | | <div class="controlBtn"> |
| | | <el-icon class="refresh"><Refresh /></el-icon> |
| | | <el-icon class="refresh" @click="refreshLive"> |
| | | <Refresh /> |
| | | </el-icon> |
| | | <div class="switchBtn" @click="switchBtn"> |
| | | <div :class="{ open: open }">NO</div> |
| | | <div :class="{ open: !open }">OFF</div> |
| | | <div :class="{ open: !isAiLive }">OFF</div> |
| | | <div :class="{ open: isAiLive }">ON</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | |
| | | <script setup> |
| | | import { Refresh } from '@element-plus/icons-vue' |
| | | import { getFlightStatistics } from '@/api/home/machineNest' |
| | | import _, { throttle } from 'lodash' |
| | | import { getLnglatAltitude, getLnglatDist } from '@/utils/cesium/mapUtil' |
| | | import { fourGQuality, SDRQuality } from '@/const/drc' |
| | | import { ElMessage } from 'element-plus' |
| | | import EventBus from '@/event-bus' |
| | | |
| | | const open = ref(true) |
| | | const taskDetailsViewer = inject('taskDetailsViewer') |
| | | const wsInfo = inject('wsInfo') |
| | | const dockSn = inject('dockSn') |
| | | const taskDetails = inject('taskDetails') |
| | | const isAiLive = inject('isAiLive') |
| | | const singleTotal = ref({}) |
| | | |
| | | // 不要随意更换顺序,有联动 |
| | | const infoList = ref([ |
| | | { index: 0, title: '实时真高', value: 0, unit: 'M' }, |
| | | { index: 1, title: '绝对高度', value: 0, unit: 'M' }, |
| | | { index: 2, title: '水平速度', value: 0, unit: 'M/s' }, |
| | | { index: 3, title: '垂直速度', value: 0, unit: 'M/s' }, |
| | | { 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: 9, title: 'GPS搜星数', value: 0, unit: '' }, |
| | | { index: 10, title: 'RTK搜星数', value: 0, unit: '' }, |
| | | { index: 11, title: '距离机场', value: 0, unit: 'M' }, |
| | | { index: 12, title: '飞行时长', value: 0, unit: '小时' }, |
| | | { index: 13, title: '电池电量', value: 0, unit: '%' }, |
| | | ]) |
| | | |
| | | const switchBtn = () => { |
| | | open.value = !open.value |
| | | if (isAiLive.value) { |
| | | EventBus.emit('CurrentTaskDetails-getDroneLiveUrl') |
| | | } else { |
| | | EventBus.emit('CurrentTaskDetails-getAiLiveUrl') |
| | | } |
| | | } |
| | | const infoList = [ |
| | | { title: '实时真高', value: '0' }, |
| | | { title: '绝对高度', value: '0' }, |
| | | { title: '水平速度', value: '0' }, |
| | | { title: '垂直速度', value: '0' }, |
| | | { title: '经度', value: '0' }, |
| | | { title: '纬度', value: '0' }, |
| | | { title: '4G信号', value: '0' }, |
| | | { title: 'SDR信号', value: '0' }, |
| | | { title: 'GPS搜星数', value: '0' }, |
| | | { title: 'RTK搜星数', value: '0' }, |
| | | { title: '距离机场', value: '0' }, |
| | | { title: '飞行时长', value: '0' }, |
| | | { title: '电池电量', value: '0' }, |
| | | ] |
| | | |
| | | function refreshLive(){ |
| | | EventBus.emit('CurrentTaskDetails-getDroneLiveUrl') |
| | | } |
| | | |
| | | function getFlightStatisticsFun() { |
| | | if (!dockSn.value) return |
| | | getFlightStatistics(dockSn.value).then(res => { |
| | | singleTotal.value = res.data.data |
| | | }) |
| | | } |
| | | |
| | | function getRealTimeReallyHigh() { |
| | | if (!taskDetailsViewer?.value) return |
| | | const device_osd_host = wsInfo?.value?.device_osd?.data?.host || {} |
| | | const { latitude, longitude, height } = device_osd_host |
| | | if (!latitude) return |
| | | getLnglatAltitude(longitude, latitude, taskDetailsViewer.value).then(res => { |
| | | const last = height - res?.height |
| | | infoList.value[0].value = last ? infoList.value[0].value : _.round(height - res?.height, 1) |
| | | }) |
| | | } |
| | | |
| | | function getHeadInfo() { |
| | | const device_osd_host = wsInfo?.value?.device_osd?.data?.host || {} |
| | | const dock_osd_host = wsInfo?.value?.dock_osd?.data?.host || {} |
| | | const { longitude, latitude, height, horizontal_speed, vertical_speed, wind_speed, battery } = device_osd_host |
| | | const { longitude: dockLon, latitude: dockLat, wireless_link } = dock_osd_host |
| | | |
| | | let dist = infoList.value[11].value |
| | | if (longitude && latitude && dockLon && dockLat) { |
| | | dist = _.round(getLnglatDist(longitude, latitude, dockLon, dockLat), 0) |
| | | } |
| | | |
| | | const newGPSNum = dock_osd_host?.position_state?.gps_number |
| | | const newRTKNum = dock_osd_host?.position_state?.rtk_number |
| | | const newFourG = wireless_link?.['4g_quality'] |
| | | const newSdr = wireless_link?.['sdr_quality'] |
| | | |
| | | infoList.value[1].value = _.round(height || 0, 1) |
| | | infoList.value[2].value = _.round(horizontal_speed || 0, 0) |
| | | infoList.value[3].value = _.round(vertical_speed || 0, 0) |
| | | infoList.value[4].value = _.round(longitude || 0, 2) |
| | | infoList.value[5].value = _.round(latitude || 0, 2) |
| | | infoList.value[6].value = _.round(wind_speed || 0, 0) |
| | | if (newFourG !== undefined) infoList.value[7].value = fourGQuality[newFourG] |
| | | if (newSdr !== undefined) infoList.value[8].value = SDRQuality[newSdr] |
| | | if (newGPSNum !== undefined) infoList.value[9].value = newGPSNum |
| | | if (newRTKNum !== undefined) infoList.value[10].value = newRTKNum |
| | | infoList.value[11].value = dist |
| | | infoList.value[12].value = _.round(singleTotal.value?.hour_count || 0, 1) |
| | | infoList.value[13].value = battery?.capacity_percent || 0 |
| | | getRealTimeReallyHigh() |
| | | } |
| | | |
| | | const getHeadInfoThrottle = throttle(getHeadInfo, 1000, { leading: true, trailing: true }) |
| | | |
| | | watch( |
| | | wsInfo, |
| | | () => { |
| | | getHeadInfoThrottle() |
| | | }, |
| | | { immediate: true, deep: true } |
| | | ) |
| | | |
| | | watch( |
| | | dockSn, |
| | | () => { |
| | | getFlightStatisticsFun() |
| | | }, |
| | | { immediate: true } |
| | | ) |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | |
| | | z-index: 5; |
| | | width: 100%; |
| | | height: 68px; |
| | | background: rgb(0,0,0,.4); /* 半透明背景 */ |
| | | background: rgb(0, 0, 0, 0.4); /* 半透明背景 */ |
| | | backdrop-filter: blur(5px); |
| | | padding: 0 31px; |
| | | display: flex; |
| | |
| | | color: #ededed; |
| | | text-align: center; |
| | | line-height: 42px; |
| | | white-space: nowrap; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | padding: 0 10px; |
| | | } |
| | | |
| | | .infoListBox { |
| | |
| | | flex: 1; |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-around; |
| | | justify-content: space-evenly; |
| | | font-family: Segoe UI, Segoe UI; |
| | | font-weight: 400; |
| | | margin-left: 15px; |
| | | |
| | | .infoValue { |
| | | font-size: 20px; |
| | | color: #ffffff; |
| | | line-height: 15px; |
| | | margin-bottom: 10px; |
| | | } |
| | | > div { |
| | | width: 90px; |
| | | |
| | | .infoTitle { |
| | | font-size: 12px; |
| | | color: #d2e8fa; |
| | | line-height: 15px; |
| | | .infoValue { |
| | | white-space: nowrap; |
| | | overflow: hidden; |
| | | text-overflow: ellipsis; |
| | | font-size: 20px; |
| | | color: #ffffff; |
| | | line-height: 18px; |
| | | margin-bottom: 10px; |
| | | |
| | | } |
| | | |
| | | .infoTitle { |
| | | font-size: 12px; |
| | | color: #d2e8fa; |
| | | line-height: 15px; |
| | | } |
| | | } |
| | | } |
| | | |
| | |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | |
| | | .refresh{ |
| | | .refresh { |
| | | color: white; |
| | | font-size: 30px; |
| | | cursor: pointer; |
| | |
| | | |
| | | // 修改相机参数 |
| | | function cameraParamsChange() { |
| | | const { camera_type, zoom_factor} = cameraParams.value |
| | | const { camera_type, zoom_factor } = cameraParams.value |
| | | cameraParamsChangeApi({ |
| | | sn: dockSn.value, |
| | | ...cameraParams.value, |
| | |
| | | const msg = isRecording.value ? '停止录像' : '开始录像' |
| | | const emitType = isRecording.value ? 'controlPanel-timeStop' : 'controlPanel-timeStart' |
| | | |
| | | callPhotoAndVideoCmd(droneSn.value, type).then(res => { |
| | | callPhotoAndVideoCmd(dockSn.value, type).then(res => { |
| | | ElMessage.success(msg) |
| | | EventBus.emit(emitType) |
| | | isRecording.value = !isRecording.value |
| | |
| | | |
| | | // 拍照 |
| | | function takePictures() { |
| | | if (isRecording.value) { |
| | | return ElMessage.warning('请先结束录像') |
| | | } |
| | | // photo拍照,video_start开始录像,video_stop结束录像 |
| | | callPhotoAndVideoCmd(droneSn.value, 'photo').then(res => { |
| | | callPhotoAndVideoCmd(dockSn.value, 'photo').then(res => { |
| | | ElMessage.success('拍照成功') |
| | | }) |
| | | } |
| | |
| | | HSI_INFO_PUSH: 'hsi_info_push', // 避障信息上报 |
| | | DELAY_TIME_INFO_PUSH: 'delay_info_push', // 图传链路延时信息上报 |
| | | } |
| | | |
| | | export const fourGQuality = { 0: '无信号', 1: '差', 2: '较差', 3: '一般', 4: '较好', 5: '好' } |
| | | export const SDRQuality = { 0: '无信号', 1: '差', 2: '较差', 3: '一般', 4: '较好', 5: '好' } |
| | |
| | | export function useManualControl(mqttState,deviceTopicInfo, isCurrentFlightController,paramsRef) { |
| | | let activeCodeKey = null |
| | | let genPortOne = true //是一代机场 |
| | | const mqttHooks = useMqtt(mqttState,deviceTopicInfo) |
| | | let mqttHooks = useMqtt(mqttState,deviceTopicInfo) |
| | | let seq = 0 |
| | | const keysPressed = [ |
| | | {key: 'KeyQ', value: false}, |
| | |
| | | { immediate: true } |
| | | ) |
| | | |
| | | onBeforeUnmount(()=>{ |
| | | mqttHooks?.d |
| | | }) |
| | | |
| | | onUnmounted(() => { |
| | | deviceTopicInfo.subTopic = '' |
| | | deviceTopicInfo.pubTopic = '' |
| | | mqttHooks = null |
| | | closeKeyboardManualControl() |
| | | }) |
| | | |
| | |
| | | // 2.发心跳 |
| | | publishDrcPing(deviceTopicInfo.sn) |
| | | } else { |
| | | console.log('清除pingInterval') |
| | | clearInterval(state.heartState.get(deviceTopicInfo.sn)?.pingInterval) |
| | | state.heartState.delete(deviceTopicInfo.sn) |
| | | heartBeatSeq.value = 0 |
| | |
| | | <div class="login-header"> |
| | | <div class="title">中图智飞低空智能感知网平台</div> |
| | | </div> |
| | | <div class="login-left-title">中国图强 智领飞跃</div> |
| | | <!-- <div class="login-left-title">中国图强 智领飞跃</div> --> |
| | | <div class="login-left"></div> |
| | | <userLogin v-if="activeName === 'user'"></userLogin> |
| | | </div> |
| | |
| | | .el-range-separator { |
| | | color: #ffffff; |
| | | } |
| | | |
| | | |
| | | } |
| | | // 日期下拉框 |
| | | .custom-date-picker { |
| | | background: #012350 !important; |
| | | border: 1px solid !important; |
| | | border-radius: 0px 0px 8px 8px; |
| | | border-image: linear-gradient(180deg, rgba(255, 255, 255, 0), rgba(115, 192, 255, 1)) 1 1 !important; |
| | | /* 修改箭头样式 */ |
| | | .el-popper__arrow { |
| | | &::before { |
| | | background: #0d3556 !important; |
| | | border: 1px solid #0d3556 !important; |
| | | box-sizing: border-box; |
| | | } |
| | | } |
| | | /* 修改头部背景色 */ |
| | | .custom-date-picker .el-picker-panel__header { |
| | | background: linear-gradient(180deg, rgba(13, 53, 86, 0.85) 0%, rgba(1, 35, 80, 0.85) 100%) !important; |
| | | color: #fff !important; |
| | | } |
| | | .el-date-table { |
| | | th { |
| | | color: #fff !important; /* 星期标题颜色 */ |
| | | } |
| | | } |
| | | /* 修改日期单元格悬停/选中状态 */ |
| | | .custom-date-picker .el-date-table td:hover, |
| | | .custom-date-picker .el-date-table td.current:not(.disabled) { |
| | | background: rgba(60, 121, 202) !important; |
| | | color: white !important; |
| | | } |
| | | // 头部按钮 |
| | | .el-date-table td.start-date span, |
| | | .el-date-table td.start-date span, |
| | | .el-date-table td.end-date span { |
| | | background-color: #012350 !important; |
| | | } |
| | | |
| | | .el-date-table td.in-range div, |
| | | .el-date-table td.in-range div:hover, |
| | | .el-date-table.is-week-mode .el-date-table__row.current div, |
| | | .el-date-table.is-week-mode .el-date-table__row:hover div { |
| | | background-color: rgba(60, 121, 202) !important; |
| | | } |
| | | .el-picker-panel__icon-btn { |
| | | color: #fff !important; |
| | | |
| | | &:hover { |
| | | color: var(--el-color-primary) !important; |
| | | } |
| | | |
| | | .el-icon { |
| | | &::before { |
| | | color: inherit !important; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | //下拉 |
| | | // :teleported="false" |
| | | .ztzf-select { |
| | | .el-select__wrapper { |
| | | background: #012A50; |
| | |
| | | color: #ffffff; |
| | | } |
| | | } |
| | | |
| | | |
| | | // 下拉框 |
| | | .el-select-dropdown__empty { |
| | | background: #012A50; |
| | | border-radius: 0px 0px 8px 8px; |
| | | border: 1px solid; |
| | | border-image: linear-gradient(180deg, rgba(255, 255, 255, 0), rgba(115, 192, 255, 1)) 1 1; |
| | | } |
| | | .el-select-dropdown__list { |
| | | padding: 0 !important; |
| | | } |
| | | .el-select-dropdown { |
| | | overflow-x: hidden !important; |
| | | |
| | | .el-select-dropdown__wrap { |
| | | overflow-x: hidden !important; |
| | | margin-right: 0 !important; |
| | | } |
| | | } |
| | | .el-select-dropdown__wrap { |
| | | overflow-x: hidden !important; |
| | | margin-right: 0 !important; |
| | | } |
| | | /* 箭头样式 */ |
| | | .el-popper__arrow::before { |
| | | background: #0d3556 !important; |
| | | border: 1px solid #0d3556 !important; |
| | | } |
| | | .el-select { |
| | | --el-select-border-color-hover: rgb(0, 162, 255) !important; |
| | | /* // 修改下拉框hover的默认样式 */ |
| | | } |
| | | |
| | | .el-input { |
| | | --el-border-color: rgb(0, 162, 255); |
| | | /* // 修改下拉框的边框 */ |
| | | } |
| | | |
| | | .el-select .el-input__wrapper { |
| | | background-color: #012350 !important; |
| | | /* // 外层下拉框背景 */ |
| | | } |
| | | |
| | | .el-input__wrapper { |
| | | --el-input-text-color: #012350 !important; /* // 修改外层下拉框的颜色 */ |
| | | } |
| | | |
| | | .el-select__popper.el-popper { |
| | | border: rgba(0, 27, 63) !important; |
| | | /* // 修改内部下拉框的边框颜色 */ |
| | | } |
| | | |
| | | .el-select-dropdown__wrap { |
| | | background: linear-gradient(180deg, rgba(13, 53, 86, 0.85) 0%, rgba(1, 35, 80, 0.85) 100%); |
| | | border-radius: 0px 0px 8px 8px; |
| | | border: 1px solid; |
| | | border-image: linear-gradient(180deg, rgba(255, 255, 255, 0), rgba(115, 192, 255, 1)) 1 1; |
| | | /* // 下拉框背景色 */ |
| | | } |
| | | |
| | | .el-select-dropdown__item { |
| | | background: rgba(0, 27, 63) !important; /* // 下拉框选项的颜色 */ |
| | | } |
| | | |
| | | .el-select-dropdown__item { |
| | | color: white; |
| | | } |
| | | |
| | | .el-select-dropdown__item.hover { |
| | | background-color: rgba(0, 120, 233, 0.63) !important; |
| | | color: white; |
| | | } |
| | | .el-select-dropdown__item.selected { |
| | | color: #8ac3fd !important; |
| | | font-weight: 700; |
| | | } |
| | | } |
| | | |
| | | .ztzf-input{ |
| | |
| | | <template> |
| | | <div class="event-overviewdetail-right" :class="{ isMore }"> |
| | | <CommonTitle title="事件概况" :style="{width: isMore?pxToRem(820):pxToRem(404)}" /> |
| | | <CommonTitle title="事件概况" :style="{ width: isMore ? pxToRem(820) : pxToRem(404) }" /> |
| | | |
| | | <div class="content"> |
| | | <img class="leftArrow" :class="isMore?'rightArrow':''" :src="isMore ? rightArrowImg : leftArrowImg" @click="leftArrowFun" alt="" /> |
| | | <img |
| | | class="leftArrow" |
| | | :class="isMore ? 'rightArrow' : ''" |
| | | :src="isMore ? rightArrowImg : leftArrowImg" |
| | | @click="leftArrowFun" |
| | | alt="" |
| | | /> |
| | | |
| | | <el-date-picker |
| | | class="ztzf-date-picker" |
| | |
| | | <img class="statusItemImg" :src="item.img" alt="" /> |
| | | <div class="statusItemInfo"> |
| | | <div class="statusItemName">{{ item.name }}</div> |
| | | <div class="statusItemNum" :style="{ color:item.color }">{{ item.num || 0 }}</div> |
| | | <div class="statusItemNum" :style="{ color: item.color }">{{ item.num || 0 }}</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | |
| | | <div class="eventList"> |
| | | <div class="eventListItem" v-for="item in list"> |
| | | <img |
| | | class="eventListItemImg" |
| | | :src="getSmallImg(item.photo_url)" |
| | | alt="" |
| | | @click="getFindImgHistory(item)" |
| | | /> |
| | | <img class="eventListItemImg" :src="getSmallImg(item.photo_url)" alt="" @click="getFindImgHistory(item)" /> |
| | | <div class="eventListItemPosition" :title="item.address" @click="positioning(item)"> |
| | | <img :src="positioningImg" alt="" title="点击定位" /> |
| | | <!-- <div class="address">{{ item.address }}</div> --> |
| | |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div class="image-list" v-if="isShowBigImg" :style="{ right: isMore?pxToRem(840):pxToRem(460) }"> |
| | | <div class="image-list" v-if="isShowBigImg" :style="{ right: isMore ? pxToRem(838) : pxToRem(460) }"> |
| | | <div class="title"> |
| | | <img @click="isShowBigImg=false" src="@/assets/images/home/useEventOperate/close.png" alt=""> |
| | | <img @click="isShowBigImg = false" src="@/assets/images/home/useEventOperate/close.png" alt="" /> |
| | | </div> |
| | | <div class="content"> |
| | | <img :src="clickImgSrc" alt=""> |
| | | <img :src="clickImgSrc" alt="" /> |
| | | </div> |
| | | <div class="card"> |
| | | <div v-for="(item,index) in imageList"> |
| | | <div class="time-top" :class="index===selectedImgIndex?'active':''" @click="clickWeekTime(item,index)">{{ item.create_time_str }}</div> |
| | | <div class="time-point"> |
| | | <img v-if="index===selectedImgIndex" src="@/assets/images/home/useEventOperate/point-active.png" alt=""> |
| | | <img v-else src="@/assets/images/home/useEventOperate/point.png" alt=""> |
| | | <div class="card-content"> |
| | | <div v-for="(item, index) in imageList"> |
| | | <div class="time-top" :class="index === selectedImgIndex ? 'active' : ''" @click="clickWeekTime(item, index)"> |
| | | {{ item.create_time_str }} |
| | | </div> |
| | | <div class="time-point"> |
| | | <img |
| | | class="active" |
| | | v-if="index === selectedImgIndex" |
| | | src="@/assets/images/home/useEventOperate/point-active.png" |
| | | alt="" |
| | | /> |
| | | <img v-else src="@/assets/images/home/useEventOperate/point.png" alt="" /> |
| | | </div> |
| | | <div class="time-bottom">{{ item.create_time }}</div> |
| | | </div> |
| | | <div class="time-bottom">{{ item.create_time }}</div> |
| | | <div class="time-line"></div> |
| | | </div> |
| | | <div class="time-line"></div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | |
| | | size: 8, |
| | | }) |
| | | |
| | | const getSmallImg = (url) => { |
| | | const getSmallImg = url => { |
| | | return url ? url.substring(0, url.lastIndexOf('.')) + '_small' + url.substring(url.lastIndexOf('.')) : '' |
| | | } |
| | | |
| | |
| | | }) |
| | | } |
| | | const { flyTo } = cesiumOperation() |
| | | const longitudeOffset = ref(0); |
| | | const longitudeOffset = ref(0) |
| | | const positioning = row => { |
| | | const longitude = Number(row.longitude) |
| | | const latitude = Number(row.latitude) |
| | | flyTo({ longitude: longitude, latitude:latitude + 0.0002 }, 1, 1000); |
| | | flyTo({ longitude: longitude, latitude: latitude + 0.0008 }, 1, 1000) |
| | | } |
| | | |
| | | // 事件状态+数量 |
| | | const statusList = ref([ |
| | | { name: '全部状态', img: status0Img, id: undefined,few: true, color: '#FFD509' }, |
| | | { name: '全部状态', img: status0Img, id: undefined, few: true, color: '#FFD509' }, |
| | | { name: '待审核', img: status1Img, id: 2, color: '#8CFEA7' }, |
| | | { name: '待处理', img: status2Img, id: 0, few: true, color: '#FF7411' }, |
| | | { name: '处理中', img: status3Img, id: 3, few: true, color: '#FFC398' }, |
| | |
| | | params.value.start_date = start_date |
| | | params.value.end_date = end_date |
| | | // 提交数据,更新聚合 |
| | | store.commit('setEventTimeRang', timeArr.value); |
| | | store.commit('setEventTimeRang', timeArr.value) |
| | | // 重置 |
| | | getEventStatusNumFun() |
| | | getEventList() |
| | |
| | | } |
| | | getEventPage(params.value, pageParams).then(res => { |
| | | const resData = res.data.data |
| | | list.value = (resData?.records || []) |
| | | list.value = resData?.records || [] |
| | | total.value = resData.total |
| | | }) |
| | | } |
| | | const isShowBigImg = ref(false) |
| | | const imageList = ref([]); |
| | | const clickImgSrc = ref(''); |
| | | const selectedImgIndex = ref(0); |
| | | const imageList = ref([]) |
| | | const clickImgSrc = ref('') |
| | | const selectedImgIndex = ref(0) |
| | | // 点击图片放大 显示详细信息 |
| | | const getFindImgHistory = (item) => { |
| | | const getFindImgHistory = item => { |
| | | // 下半部分隐藏 右侧隐藏 |
| | | isShowBigImg.value = true |
| | | store.commit('setHideBottomIcon', false) |
| | | findImgHistory(item.id).then((res) => { |
| | | findImgHistory(item.id).then(res => { |
| | | if (res.data.code !== 0) return |
| | | imageList.value = res.data.data |
| | | clickImgSrc.value = res.data.data[0]?.url; |
| | | clickImgSrc.value = res.data.data[0]?.url |
| | | }) |
| | | positioning(item); |
| | | positioning(item) |
| | | } |
| | | // 点击周期 |
| | | const clickWeekTime = (item,index) => { |
| | | const clickWeekTime = (item, index) => { |
| | | clickImgSrc.value = item.url |
| | | selectedImgIndex.value = index |
| | | } |
| | | |
| | | const getDateRange = unit => { |
| | | if (unit === 'today') { |
| | | return [dayjs().format('YYYY-MM-DD HH:mm:ss'), dayjs().format('YYYY-MM-DD HH:mm:ss')]; |
| | | } |
| | | return [dayjs().startOf(unit).format('YYYY-MM-DD HH:mm:ss'), dayjs().endOf(unit).format('YYYY-MM-DD HH:mm:ss')]; |
| | | }; |
| | | if (unit === 'today') { |
| | | return [dayjs().format('YYYY-MM-DD HH:mm:ss'), dayjs().format('YYYY-MM-DD HH:mm:ss')] |
| | | } |
| | | return [dayjs().startOf(unit).format('YYYY-MM-DD HH:mm:ss'), dayjs().endOf(unit).format('YYYY-MM-DD HH:mm:ss')] |
| | | } |
| | | |
| | | // 监听事件时间范围变化 |
| | | watch(() => store.state.home.eventTimeType, (newVal) => { |
| | | timeArr.value = getDateRange(newVal); |
| | | const [start_date, end_date] = getDateRange(newVal); |
| | | params.value.start_date = start_date |
| | | params.value.end_date = end_date |
| | | getEventStatusNumFun() |
| | | getEventList() |
| | | getList() |
| | | }, { immediate: false,deep: true }) |
| | | watch( |
| | | () => store.state.home.eventTimeType, |
| | | newVal => { |
| | | timeArr.value = getDateRange(newVal) |
| | | const [start_date, end_date] = getDateRange(newVal) |
| | | params.value.start_date = start_date |
| | | params.value.end_date = end_date |
| | | getEventStatusNumFun() |
| | | getEventList() |
| | | getList() |
| | | }, |
| | | { immediate: false, deep: true } |
| | | ) |
| | | |
| | | onMounted(() => { |
| | | getDeviceList() |
| | |
| | | .content { |
| | | width: 390px; |
| | | height: 877px; |
| | | background: linear-gradient( |
| | | 270deg, |
| | | rgba(31, 62, 122, 0) 0%, |
| | | rgba(31, 62, 122, 0.35) 21%, |
| | | #1f3e7a 100%); |
| | | background: linear-gradient(270deg, rgba(31, 62, 122, 0) 0%, rgba(31, 62, 122, 0.35) 21%, #1f3e7a 100%); |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-content: start; |
| | |
| | | // margin-right: 4px; |
| | | |
| | | &.active { |
| | | background: linear-gradient( 180deg, rgba(19,80,141,0) 0%, rgba(22,56,91,0.48) 48%, #053462 98%, #259DFF 98%, #259DFF 98%); |
| | | background: linear-gradient( |
| | | 180deg, |
| | | rgba(19, 80, 141, 0) 0%, |
| | | rgba(22, 56, 91, 0.48) 48%, |
| | | #053462 98%, |
| | | #259dff 98%, |
| | | #259dff 98% |
| | | ); |
| | | } |
| | | |
| | | .statusItemImg { |
| | |
| | | display: flex; |
| | | justify-content: center; |
| | | } |
| | | |
| | | .image-list { |
| | | border: 1px solid red; |
| | | position: absolute; |
| | | z-index: 1; |
| | | top: 148px; |
| | | |
| | | right: 460px; |
| | | top: 122px; |
| | | width: 540px; |
| | | height: 420px; |
| | | background: #0F1929; |
| | | // box-shadow: inset 0px -50px 50px 0px rgba(27,148,255,0.13); |
| | | // border-radius: 0px 0px 0px 0px; |
| | | border: 2px solid; |
| | | border-image: linear-gradient(180deg, rgba(81, 168, 255, 0), rgba(48, 111, 202, 1), rgba(255, 255, 255, 1), rgba(27, 148, 255, 1)) 2 2; |
| | | |
| | | width: 632px; |
| | | height: 476px; |
| | | border: solid; |
| | | background: #0f1929; |
| | | z-index: 1; |
| | | |
| | | border: solid 2px transparent; |
| | | border-radius: 20px; |
| | | background-image: linear-gradient(#fff, #fff), linear-gradient(180deg, rgba(81, 168, 255, 1), rgba(189, 228, 255, 1)); |
| | | background-origin: border-box; |
| | | background-clip: content-box, border-box; |
| | | |
| | | overflow: hidden; |
| | | .title { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | position: absolute; |
| | | text-align: right; |
| | | right: 12px; |
| | | // top: 12px; |
| | | top: 0; |
| | | right: 0; |
| | | width: 45px; |
| | | height: 44px; |
| | | background: rgba(0, 0, 0, 0.5); |
| | | border-radius: 0px 20px 0px 8px; |
| | | |
| | | img { |
| | | width: 10px; |
| | | height: 10px; |
| | | width: 10.55px; |
| | | height: 10.55px; |
| | | cursor: pointer; |
| | | } |
| | | } |
| | | |
| | | .content { |
| | | img { |
| | | width: 100%; |
| | |
| | | border-radius: 10px 10px 10px 10px; |
| | | } |
| | | } |
| | | |
| | | .card { |
| | | background: linear-gradient( 180deg, rgba(13,30,70,0.72) 0%, #142E6B 100%); |
| | | padding: 0px 10px 0px 10px; |
| | | color: #BECBEA; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | position: absolute; |
| | | bottom: 0px; |
| | | width: 100%; |
| | | .time-top { |
| | | width: 50px; |
| | | height: 30px; |
| | | line-height: 30px; |
| | | text-align: center; |
| | | color: #ffffff; |
| | | background: linear-gradient( 180deg, rgba(13,30,70,0.72) 0%, #142E6B 100%); |
| | | border: 1px solid #0054D3; |
| | | cursor: pointer; |
| | | } |
| | | .active { |
| | | background: linear-gradient( 180deg, rgba(13,48,131,0.72) 0%, #023DC8 100%); |
| | | border: 1px solid; |
| | | border-image: linear-gradient(180deg, rgba(0, 84, 211, 1), rgba(146, 186, 245, 1)) 1 1; |
| | | } |
| | | .time-point { |
| | | text-align: center; |
| | | img { |
| | | width: 8px; |
| | | height: 12px; |
| | | } |
| | | } |
| | | .time-line { |
| | | height: 67px; |
| | | background: rgba(0, 0, 0, 0.42); |
| | | border-radius: 0px 0px 20px 20px; |
| | | padding: 0px 6px 0px 29px; |
| | | |
| | | color: #d5d5d5; |
| | | font-size: 14px; |
| | | |
| | | .card-content { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | |
| | | position: absolute; |
| | | background: linear-gradient( 82deg, rgba(23,40,79,0) 0%, #576D9F 17%, #576D9F 86%, rgba(23,40,79,0) 100%); |
| | | height: 2px; |
| | | top: 38px; |
| | | width: 100%; |
| | | left: 29px; |
| | | width: calc(100% - 29px - 6px); |
| | | height: 100%; |
| | | |
| | | & > div { |
| | | position: relative; |
| | | } |
| | | |
| | | .time-top { |
| | | margin-top: 10px; |
| | | line-height: 1; |
| | | text-align: center; |
| | | cursor: pointer; |
| | | } |
| | | |
| | | .active { |
| | | font-weight: 400; |
| | | color: #ffffff; |
| | | } |
| | | |
| | | .time-point { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: center; |
| | | position: absolute; |
| | | top: 28px; |
| | | left: 50%; |
| | | width: 18px; |
| | | height: 18px; |
| | | z-index: 1; |
| | | transform: translate(-50%, 0); |
| | | |
| | | img { |
| | | width: 11.2px; |
| | | height: 11.2px; |
| | | } |
| | | |
| | | img.active { |
| | | width: 16.89px; |
| | | height: 16.89px; |
| | | } |
| | | } |
| | | |
| | | .time-bottom { |
| | | margin-top: 20px; |
| | | } |
| | | |
| | | .time-line { |
| | | position: absolute; |
| | | top: 35px; |
| | | width: 100%; |
| | | width: 595px; |
| | | border-radius: 0px 0px 0px 0px; |
| | | border: 1px solid rgba(237, 237, 237, 0.6); |
| | | z-index: 0; |
| | | } |
| | | } |
| | | } |
| | | } |
| | |
| | | <el-input class="ztzf-input" v-model="searchForm.key_word" placeholder="请输入任务/机巢名称" clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="关联算法"> |
| | | <el-select class="ztzf-select" v-model="searchForm.ai_types" placeholder="请选择"> |
| | | <el-select :teleported="false" class="ztzf-select" v-model="searchForm.ai_types" placeholder="请选择"> |
| | | <el-option v-for="item in taskAlgorithm" :key="item.id" :label="item.dictValue" :value="item.dictKey" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="任务状态"> |
| | | <el-select class="ztzf-select" v-model="searchForm.status" placeholder="请选择" clearable> |
| | | <el-select :teleported="false" class="ztzf-select" v-model="searchForm.status" placeholder="请选择" clearable> |
| | | <el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </div> |
| | | <div class="search-row"> |
| | | <el-form-item label="选择机巢"> |
| | | <el-select class="ztzf-select" v-model="searchForm.device_sn" placeholder="请选择"> |
| | | <el-select :teleported="false" class="ztzf-select" v-model="searchForm.device_sn" placeholder="请选择"> |
| | | <el-option |
| | | v-for="item in deviceList" |
| | | :label="item.nickname" |
| | |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-date-picker |
| | | popper-class="custom-date-picker" |
| | | class="ztzf-date-picker" |
| | | v-model="dateRange" |
| | | type="daterange" |
| | |
| | | ? 'finish ' |
| | | : scope.row.status === 5 |
| | | ? 'fail ' |
| | | : ' '"> |
| | | : ' ' |
| | | " |
| | | > |
| | | {{ |
| | | scope.row.status === 1 |
| | | ? '待执行' |
| | |
| | | <span>{{ scope.row.event_number ? scope.row.event_number : '/' }}</span> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column label="操作" width="80"><div class="ztzf-view">查看</div></el-table-column> |
| | | <el-table-column label="操作" width="80"> |
| | | <template #default="scope"> |
| | | <div class="ztzf-view" @click="viewDetail(scope.row)">查看</div> |
| | | </template> |
| | | </el-table-column> |
| | | </el-table> |
| | | </div> |
| | | <!-- 分页 --> |
| | |
| | | /> |
| | | </div> |
| | | </el-dialog> |
| | | <!-- 当前任务详情 --> |
| | | <CurrentTaskDetails |
| | | v-if="isShowCurrentTaskDetails" |
| | | v-model:show="isShowCurrentTaskDetails" |
| | | :id="currentTaskDetailsId" |
| | | /> |
| | | <!-- 历史任务详情 --> |
| | | <DeviceJobDetails |
| | | v-if="deviceJobDetailsShow" |
| | | v-model:show="deviceJobDetailsShow" |
| | | :wayLineJodInfoId="wayLineJodInfoId" |
| | | /> |
| | | </template> |
| | | <script setup> |
| | | import { pxToRem } from '@/utils/rem' |
| | |
| | | import { getDictionary } from '@/api/system/dict' |
| | | import { selectDevicePage } from '@/api/home/machineNest' |
| | | import { getMultipleDictionary } from '@/api/system/dictbiz' |
| | | import CurrentTaskDetails from '@/components/CurrentTaskDetails/CurrentTaskDetails.vue' |
| | | import DeviceJobDetails from '@/components/DeviceJobDetails/DeviceJobDetails.vue' |
| | | const isShowDetailsDialog = defineModel('show') |
| | | const dateRange = ref('') |
| | | const searchForm = reactive({ |
| | |
| | | const statusOptions = [ |
| | | { label: '待执行', value: 1 }, |
| | | { label: '执行中', value: 2 }, |
| | | { label: '已完成', value: 3 }, |
| | | { label: '已执行', value: 3 }, |
| | | |
| | | { label: '执行失败', value: 5 }, |
| | | ] |
| | |
| | | } |
| | | // 获取任务列表 |
| | | const getJobList = () => { |
| | | // 事件状态:0 =待处理,1=待分拨,2=待处理,3=处理中,4=已完成 5=已完结 |
| | | jobList(taskDetailParams).then(res => { |
| | | if (res.data.code !== 0) return |
| | | taskDetailData.value = res.data.data.records |
| | |
| | | return 'success-row' |
| | | } |
| | | } |
| | | // 查看 |
| | | const isShowCurrentTaskDetails = ref(false) |
| | | const currentTaskDetailsId = ref(null) |
| | | const deviceJobDetailsShow = ref(false) |
| | | const wayLineJodInfoId = ref(null) |
| | | const viewDetail = row => { |
| | | if (row.status === 1 || row.status === 2) { |
| | | currentTaskDetailsId.value = row.id |
| | | isShowCurrentTaskDetails.value = true |
| | | } |
| | | if (row.status === 3 || row.status === 5) { |
| | | wayLineJodInfoId.value = row.id |
| | | deviceJobDetailsShow.value = true |
| | | } |
| | | } |
| | | onMounted(() => { |
| | | requestDictionary() |
| | | getJobList() |
| | | getDeviceList() |
| | | }) |
| | | </script> |
| | | <style lang="scss"> |
| | | /* 修改日期单元格背景色 */ |
| | | .custom-date-picker .el-picker-panel__body { |
| | | background: #012350 !important; |
| | | color: #fff !important; |
| | | } |
| | | |
| | | </style> |
| | | <style scoped lang="scss"> |
| | | .search-row { |
| | | display: flex; |
| | |
| | | } |
| | | // 执行中 |
| | | .distributed { |
| | | color: #FFA768; |
| | | color: #ffa768; |
| | | } |
| | | // 已执行 |
| | | .finish { |
| | |
| | | margin-bottom: 13px; |
| | | .name { |
| | | margin-left: 46px; |
| | | |
| | | } |
| | | } |
| | | .table-body { |
| | | height: 190px; |
| | | |
| | | overflow: auto; |
| | | &::-webkit-scrollbar { |
| | | width: 0; |
| | |
| | | } |
| | | .active { |
| | | color: #ffa768; |
| | | background: linear-gradient(90deg, rgba(12, 45, 92, 1) 0%, #154671 50%, rgba(12, 45, 92, 1) 100%), |
| | | linear-gradient(90deg, rgba(12, 45, 92, 1) 0%, rgba(12, 45, 92, 1) 50%, rgba(12, 45, 92, 1) 100%); |
| | | |
| | | } |
| | | .idle-active { |
| | | color: #8effac; |
| | |
| | | <template> |
| | | <CommonTitle title="事件概况" @Details="details"/> |
| | | <div :style="{ marginLeft: pxToRem(14) }"> |
| | | <div class="eventOverview"> |
| | | <div class="overviewData"> |
| | | <div class="totalBox"> |
| | | <div class="totalNumber">{{ eventTotal }}</div> |
| | | <div class="totalLabel">总事件数</div> |
| | | </div> |
| | | <div class="contentBox"> |
| | | <div class="overviewItem" v-for="item in eventTypeList" :key="item.name"> |
| | | <div class="itemName"><img :src="item.img" alt="" />{{ item.name }}</div> |
| | | <div class="itemValue" :style="{ color: item.color }">{{ item.value }}</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <CommonDateTime class="dateTime" v-model="timeArr" @change="timeChange" /> |
| | | <CommonTitle title="事件概况" @Details="details" /> |
| | | <div :style="{ marginLeft: pxToRem(14) }"> |
| | | <div class="eventOverview"> |
| | | <div class="overviewData"> |
| | | <div class="totalBox"> |
| | | <div class="totalNumber">{{ eventTotal }}</div> |
| | | <div class="totalLabel">总事件数</div> |
| | | </div> |
| | | <div class="contentBox"> |
| | | <div class="overviewItem" v-for="item in eventTypeList" :key="item.name"> |
| | | <div class="itemName"> |
| | | <img :src="item.img" alt="" /> |
| | | {{ item.name }} |
| | | </div> |
| | | <div class="itemValue" :style="{ color: item.color }">{{ item.value }}</div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <CommonDateTime class="dateTime" v-model="timeArr" @change="timeChange" /> |
| | | |
| | | <el-select |
| | | class="homeRightSelect" |
| | | v-model="params.device_sn" |
| | | placeholder="请选择" |
| | | size="large" |
| | | filterable |
| | | :remote-method="remoteMethod" |
| | | remote-show-suffix |
| | | remote |
| | | clearable |
| | | popper-class="uavHomePopper" |
| | | v-select-load:uavHomePopper="scrollEvent" |
| | | @change="selectChange" |
| | | > |
| | | <el-option |
| | | v-for="item in deviceList" |
| | | :key="item.value" |
| | | :label="item.label" |
| | | :value="item.value" |
| | | /> |
| | | </el-select> |
| | | <div class="completion"> |
| | | <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="" /> |
| | | </div> |
| | | <el-select |
| | | :teleported="false" |
| | | class="homeRightSelect ztzf-select" |
| | | v-model="params.device_sn" |
| | | placeholder="请选择机巢" |
| | | size="large" |
| | | filterable |
| | | :remote-method="remoteMethod" |
| | | remote-show-suffix |
| | | remote |
| | | clearable |
| | | popper-class="uavHomePopper" |
| | | v-select-load:uavHomePopper="scrollEvent" |
| | | @change="selectChange" |
| | | > |
| | | <el-option v-for="item in deviceList" :key="item.value" :label="item.label" :value="item.value" /> |
| | | </el-select> |
| | | <div class="completion"> |
| | | <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="" /> |
| | | </div> |
| | | |
| | | <div class="chart" ref="echartsRef"></div> |
| | | </div> |
| | | </div> |
| | | <div class="chart" ref="echartsRef"></div> |
| | | </div> |
| | | </div> |
| | | </template> |
| | | <script setup> |
| | | import CommonTitle from '@/components/CommonTitle.vue'; |
| | | import overviewImg1 from '@/assets/images/home/homeRight/overview1.png'; |
| | | import overviewImg2 from '@/assets/images/home/homeRight/overview2.png'; |
| | | import overviewImg3 from '@/assets/images/home/homeRight/overview3.png'; |
| | | import overviewImg4 from '@/assets/images/home/homeRight/overview4.png'; |
| | | 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 * as echarts from 'echarts'; |
| | | import CommonDateTime from '@/components/CommonDateTime.vue'; |
| | | import VSelectLoad from '@/directive/selectLoad'; |
| | | import { getJobEventBrokerLine, getJobEventByStatus, getJobEventTotal } from '@/api/home'; |
| | | import dayjs from 'dayjs'; |
| | | import { selectDevicePage } from '@/api/home/machineNest'; |
| | | import CommonTitle from '@/components/CommonTitle.vue' |
| | | import overviewImg1 from '@/assets/images/home/homeRight/overview1.png' |
| | | import overviewImg2 from '@/assets/images/home/homeRight/overview2.png' |
| | | import overviewImg3 from '@/assets/images/home/homeRight/overview3.png' |
| | | import overviewImg4 from '@/assets/images/home/homeRight/overview4.png' |
| | | 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 * as echarts from 'echarts' |
| | | import CommonDateTime from '@/components/CommonDateTime.vue' |
| | | import VSelectLoad from '@/directive/selectLoad' |
| | | import { getJobEventBrokerLine, getJobEventByStatus, getJobEventTotal } from '@/api/home' |
| | | import dayjs from 'dayjs' |
| | | import { selectDevicePage } from '@/api/home/machineNest' |
| | | import { useStore } from 'vuex' |
| | | import useEchartsResize from '@/hooks/useEchartsResize' |
| | | |
| | | const echartsOption = { |
| | | tooltip: { |
| | | trigger: 'axis', |
| | | axisPointer: { |
| | | type: 'shadow', |
| | | }, |
| | | }, |
| | | grid: { |
| | | top: '5%', |
| | | left: 0, |
| | | right: 0, |
| | | bottom: 0, |
| | | containLabel: true, |
| | | }, |
| | | xAxis: { |
| | | type: 'category', |
| | | axisLabel: { |
| | | rotate: -45, // 旋转角度 |
| | | interval: 0, // 显示所有标签 |
| | | color: '#FFFFFF', |
| | | fontFamily: 'Source Han Sans CN, Source Han Sans CN', |
| | | fontWeight: 400, |
| | | fontSize: 10, |
| | | }, |
| | | data: [], |
| | | }, |
| | | yAxis: [ |
| | | { |
| | | type: 'value', |
| | | axisLabel: { |
| | | interval: 0, // 显示所有标签 |
| | | color: '#FFFFFF', |
| | | fontFamily: 'Source Han Sans CN, Source Han Sans CN', |
| | | fontWeight: 400, |
| | | fontSize: 10, |
| | | }, |
| | | axisLine: { |
| | | lineStyle: { |
| | | color: '#ffffff', |
| | | }, |
| | | }, |
| | | splitLine: { |
| | | lineStyle: { |
| | | color: 'rgba(255, 255, 255, 0.1)', |
| | | type: 'dashed', // 设置为虚线 |
| | | }, |
| | | }, |
| | | }, |
| | | ], |
| | | series: [], |
| | | }; |
| | | tooltip: { |
| | | trigger: 'axis', |
| | | axisPointer: { |
| | | type: 'shadow', |
| | | }, |
| | | }, |
| | | grid: { |
| | | top: '5%', |
| | | left: 0, |
| | | right: 0, |
| | | bottom: 0, |
| | | containLabel: true, |
| | | }, |
| | | xAxis: { |
| | | type: 'category', |
| | | axisLabel: { |
| | | rotate: -45, // 旋转角度 |
| | | interval: 0, // 显示所有标签 |
| | | color: '#FFFFFF', |
| | | fontFamily: 'Source Han Sans CN, Source Han Sans CN', |
| | | fontWeight: 400, |
| | | fontSize: 10, |
| | | }, |
| | | data: [], |
| | | }, |
| | | yAxis: [ |
| | | { |
| | | type: 'value', |
| | | axisLabel: { |
| | | interval: 0, // 显示所有标签 |
| | | color: '#FFFFFF', |
| | | fontFamily: 'Source Han Sans CN, Source Han Sans CN', |
| | | fontWeight: 400, |
| | | fontSize: 10, |
| | | }, |
| | | axisLine: { |
| | | lineStyle: { |
| | | color: '#ffffff', |
| | | }, |
| | | }, |
| | | splitLine: { |
| | | lineStyle: { |
| | | color: 'rgba(255, 255, 255, 0.1)', |
| | | type: 'dashed', // 设置为虚线 |
| | | }, |
| | | }, |
| | | }, |
| | | ], |
| | | series: [], |
| | | } |
| | | |
| | | const eventTypeList = ref([ |
| | | { name: '待审核', value: 0, img: overviewImg1, color: '#8CFEA7', status: '2' }, |
| | | // { name: '待分拨', value: 0, img: overviewImg2, color: '#6FCAFF', status: '1' }, |
| | | { name: '待处理', value: 0, img: overviewImg3, color: '#E36913', status: '0' }, |
| | | { name: '处理中', value: 0, img: overviewImg4, color: '#FFC398', status: '3' }, |
| | | { name: '已完成', value: 0, img: overviewImg5, color: '#AFD9FB', status: '4' }, |
| | | { name: '已完结', value: 0, img: overviewImg6, color: '#11C4FF', status: '5' }, |
| | | ]); |
| | | const seriesObj = {}; |
| | | { name: '待审核', value: 0, img: overviewImg1, color: '#8CFEA7', status: '2' }, |
| | | // { name: '待分拨', value: 0, img: overviewImg2, color: '#6FCAFF', status: '1' }, |
| | | { name: '待处理', value: 0, img: overviewImg3, color: '#E36913', status: '0' }, |
| | | { name: '处理中', value: 0, img: overviewImg4, color: '#FFC398', status: '3' }, |
| | | { name: '已完成', value: 0, img: overviewImg5, color: '#AFD9FB', status: '4' }, |
| | | { name: '已完结', value: 0, img: overviewImg6, color: '#11C4FF', status: '5' }, |
| | | ]) |
| | | const seriesObj = {} |
| | | eventTypeList.value.forEach(item => { |
| | | seriesObj[item.status] = { |
| | | type: 'bar', |
| | | stack: 'Ad', |
| | | emphasis: { |
| | | focus: 'series', |
| | | }, |
| | | name: item.name, |
| | | itemStyle: { color: item.color }, |
| | | data: [], |
| | | }; |
| | | }); |
| | | const echartsRef = ref(null); |
| | | seriesObj[item.status] = { |
| | | type: 'bar', |
| | | stack: 'Ad', |
| | | emphasis: { |
| | | focus: 'series', |
| | | }, |
| | | name: item.name, |
| | | itemStyle: { color: item.color }, |
| | | data: [], |
| | | } |
| | | }) |
| | | const echartsRef = ref(null) |
| | | let { chart } = useEchartsResize(echartsRef) |
| | | const deviceList = ref([]); |
| | | const today = dayjs().format('YYYY-MM-DD'); |
| | | const timeArr = ref([today, today]); |
| | | const deviceList = ref([]) |
| | | const today = dayjs().format('YYYY-MM-DD') |
| | | const timeArr = ref([today, today]) |
| | | const completionRateSeries = { |
| | | name: '完成率', |
| | | type: 'line', |
| | | itemStyle: { |
| | | color: '#0CEBF7', // 设置颜色 |
| | | }, |
| | | lineStyle: { |
| | | width: 2, // 线条宽度 |
| | | type: 'solid', // 线条类型 |
| | | }, |
| | | symbol: 'circle', // 数据点符号 |
| | | symbolSize: 6, // 数据点符号大小 |
| | | emphasis: { |
| | | focus: 'series', |
| | | }, |
| | | data: [], // 示例数据,根据实际完成率计算 |
| | | }; |
| | | name: '完成率', |
| | | type: 'line', |
| | | itemStyle: { |
| | | color: '#0CEBF7', // 设置颜色 |
| | | }, |
| | | lineStyle: { |
| | | width: 2, // 线条宽度 |
| | | type: 'solid', // 线条类型 |
| | | }, |
| | | symbol: 'circle', // 数据点符号 |
| | | symbolSize: 6, // 数据点符号大小 |
| | | emphasis: { |
| | | focus: 'series', |
| | | }, |
| | | data: [], // 示例数据,根据实际完成率计算 |
| | | } |
| | | |
| | | const eventTotal = ref(0); |
| | | let deviceTotal = 0; |
| | | const eventTotal = ref(0) |
| | | let deviceTotal = 0 |
| | | const devicePageParams = ref({ |
| | | current: 1, |
| | | size: 10, |
| | | total: 0, |
| | | nickname: '', |
| | | }); |
| | | current: 1, |
| | | size: 10, |
| | | total: 0, |
| | | nickname: '', |
| | | }) |
| | | const params = ref({ |
| | | date_enum: 'TODAY', |
| | | device_sn: '', |
| | | end_date: undefined, |
| | | start_date: undefined, |
| | | }); |
| | | date_enum: 'TODAY', |
| | | device_sn: '', |
| | | end_date: undefined, |
| | | start_date: undefined, |
| | | }) |
| | | |
| | | const store = useStore() |
| | | const details = () =>{ |
| | | store.commit('setFootActiveIndex',1) |
| | | store.commit('setIsEventOverviewDetail',true) |
| | | const details = () => { |
| | | store.commit('setFootActiveIndex', 1) |
| | | store.commit('setIsEventOverviewDetail', true) |
| | | } |
| | | |
| | | // 远程查询 |
| | | const remoteMethod = nickname => { |
| | | devicePageParams.value.nickname = nickname; |
| | | devicePageParams.value.current = 1; |
| | | devicePageParams.value.size = 10; |
| | | deviceList.value = []; |
| | | getTableList(); |
| | | }; |
| | | devicePageParams.value.nickname = nickname |
| | | devicePageParams.value.current = 1 |
| | | devicePageParams.value.size = 10 |
| | | deviceList.value = [] |
| | | getTableList() |
| | | } |
| | | // 滚动查询 |
| | | const scrollEvent = () => { |
| | | const { current, size } = devicePageParams.value; |
| | | if (current * size < deviceTotal) { |
| | | devicePageParams.value.current = current + 1; |
| | | getTableList(); |
| | | } |
| | | }; |
| | | const { current, size } = devicePageParams.value |
| | | if (current * size < deviceTotal) { |
| | | devicePageParams.value.current = current + 1 |
| | | getTableList() |
| | | } |
| | | } |
| | | // 机巢列表数据 |
| | | const getTableList = async () => { |
| | | const res = await selectDevicePage({ ...devicePageParams.value, type: 1 }); |
| | | const arr = res?.data?.data?.records || []; |
| | | deviceList.value = deviceList.value.concat( |
| | | arr.map(item => ({ |
| | | value: item.device_sn, |
| | | label: item.nickname, |
| | | })) |
| | | ); |
| | | deviceTotal = res?.data?.data?.total || 0; |
| | | }; |
| | | const res = await selectDevicePage({ ...devicePageParams.value, type: 1 }) |
| | | const arr = res?.data?.data?.records || [] |
| | | deviceList.value = deviceList.value.concat( |
| | | arr.map(item => ({ |
| | | value: item.device_sn, |
| | | label: item.nickname, |
| | | })) |
| | | ) |
| | | deviceTotal = res?.data?.data?.total || 0 |
| | | } |
| | | // 下拉变化 |
| | | const selectChange = val => { |
| | | getData(); |
| | | }; |
| | | getData() |
| | | } |
| | | |
| | | // 时间变化 |
| | | const timeChange = (value, date_enum) => { |
| | | params.value = { |
| | | ...params.value, |
| | | date_enum, |
| | | }; |
| | | getData(); |
| | | }; |
| | | params.value = { |
| | | ...params.value, |
| | | date_enum, |
| | | } |
| | | getData() |
| | | } |
| | | |
| | | // 获取柱状图数据 |
| | | const getBarChartData = () => { |
| | | getJobEventBrokerLine(params.value).then(res => { |
| | | const list = res?.data?.data || []; |
| | | echartsOption.xAxis.data = list.map(item => item.name); |
| | | getJobEventBrokerLine(params.value).then(res => { |
| | | const list = res?.data?.data || [] |
| | | echartsOption.xAxis.data = list.map(item => item.name) |
| | | // 赋值前清空数据 |
| | | Object.keys(seriesObj).forEach(key => {seriesObj[key].data = []}) |
| | | list.forEach(item => { |
| | | item.data.forEach((item1, index) => { |
| | | Object.keys(seriesObj).forEach(key => { |
| | | seriesObj[key].data = [] |
| | | }) |
| | | list.forEach(item => { |
| | | item.data.forEach((item1, index) => { |
| | | if (!seriesObj?.[item1.status]) return |
| | | seriesObj[item1.status].data.push(item1.value); |
| | | }); |
| | | }); |
| | | completionRateSeries.data = list.map(item => item.rate); |
| | | echartsOption.series = Object.values(seriesObj); |
| | | echartsOption.series.push(completionRateSeries); |
| | | chart.value.setOption(echartsOption); |
| | | }); |
| | | }; |
| | | seriesObj[item1.status].data.push(item1.value) |
| | | }) |
| | | }) |
| | | completionRateSeries.data = list.map(item => item.rate) |
| | | echartsOption.series = Object.values(seriesObj) |
| | | echartsOption.series.push(completionRateSeries) |
| | | chart.value.setOption(echartsOption) |
| | | }) |
| | | } |
| | | |
| | | // 事件类型数据 |
| | | const getTypeData = () => { |
| | | getJobEventByStatus(params.value).then(res => { |
| | | const resList = res?.data?.data || []; |
| | | resList.forEach(item => { |
| | | eventTypeList.value.forEach(item1 => { |
| | | if (item1.name === item.name) { |
| | | item1.value = item.num; |
| | | } |
| | | }); |
| | | }); |
| | | }); |
| | | }; |
| | | getJobEventByStatus(params.value).then(res => { |
| | | const resList = res?.data?.data || [] |
| | | resList.forEach(item => { |
| | | eventTypeList.value.forEach(item1 => { |
| | | if (item1.name === item.name) { |
| | | item1.value = item.num |
| | | } |
| | | }) |
| | | }) |
| | | }) |
| | | } |
| | | |
| | | // 获取事件概括数据 |
| | | const getData = () => { |
| | | getTypeData(); |
| | | getBarChartData(); |
| | | }; |
| | | getTypeData() |
| | | getBarChartData() |
| | | } |
| | | |
| | | onMounted(() => { |
| | | getJobEventTotal().then(res => { |
| | | eventTotal.value = res?.data?.data || 0; |
| | | }); |
| | | getData(); |
| | | getTableList(); |
| | | }); |
| | | getJobEventTotal().then(res => { |
| | | eventTotal.value = res?.data?.data || 0 |
| | | }) |
| | | getData() |
| | | getTableList() |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .homeRightSelect { |
| | | width: 356px; |
| | | width: 356px; |
| | | |
| | | :deep() { |
| | | .el-select__wrapper { |
| | | background: linear-gradient(90deg, #195bad 0%, rgba(25, 91, 173, 0) 100%); |
| | | min-height: 28px; |
| | | height: 28px; |
| | | box-shadow: none; |
| | | border: 1px solid #306fca; |
| | | } |
| | | :deep() { |
| | | .el-select__wrapper { |
| | | background: linear-gradient(90deg, #195bad 0%, rgba(25, 91, 173, 0) 100%); |
| | | min-height: 28px; |
| | | height: 28px; |
| | | box-shadow: none; |
| | | border: 1px solid #306fca; |
| | | } |
| | | |
| | | .el-select__placeholder { |
| | | font-family: Microsoft YaHei, Microsoft YaHei, serif; |
| | | color: #ffffff; |
| | | font-size: 14px; |
| | | } |
| | | } |
| | | .el-select__placeholder { |
| | | font-family: Microsoft YaHei, Microsoft YaHei, serif; |
| | | color: #ffffff; |
| | | font-size: 14px; |
| | | } |
| | | } |
| | | } |
| | | |
| | | .eventOverview { |
| | | width: 390px; |
| | | height: 445px; |
| | | background: linear-gradient( |
| | | 270deg, |
| | | #1f3e7a 0%, |
| | | rgba(31, 62, 122, 0.35) 79%, |
| | | rgba(31, 62, 122, 0) 100% |
| | | ); |
| | | border-radius: 0px 0px 0px 0px; |
| | | opacity: 0.85; |
| | | margin: 3px 0 0 0; |
| | | padding-top: 15px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | width: 390px; |
| | | height: 445px; |
| | | background: linear-gradient(270deg, #1f3e7a 0%, rgba(31, 62, 122, 0.35) 79%, rgba(31, 62, 122, 0) 100%); |
| | | border-radius: 0px 0px 0px 0px; |
| | | opacity: 0.85; |
| | | margin: 3px 0 0 0; |
| | | padding-top: 15px; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | | |
| | | .dateTime { |
| | | width: 356px; |
| | | margin: 15px 0 8px 0; |
| | | } |
| | | .dateTime { |
| | | width: 356px; |
| | | margin: 15px 0 8px 0; |
| | | } |
| | | |
| | | .overviewData { |
| | | width: 360px; |
| | | height: 122px; |
| | | background: url('@/assets/images/home/homeRight/overviewBg.png') no-repeat center / 100% 100%; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding-left: 11px; |
| | | .overviewData { |
| | | width: 360px; |
| | | height: 122px; |
| | | background: url('@/assets/images/home/homeRight/overviewBg.png') no-repeat center / 100% 100%; |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | padding-left: 11px; |
| | | |
| | | .totalBox { |
| | | width: 77px; |
| | | .totalBox { |
| | | width: 77px; |
| | | |
| | | .totalNumber { |
| | | font-family: YouSheBiaoTiHei, YouSheBiaoTiHei; |
| | | font-weight: 400; |
| | | font-size: 26px; |
| | | color: #ffffff; |
| | | line-height: 30px; |
| | | text-align: center; |
| | | } |
| | | .totalNumber { |
| | | font-family: YouSheBiaoTiHei, YouSheBiaoTiHei; |
| | | font-weight: 400; |
| | | font-size: 26px; |
| | | color: #ffffff; |
| | | line-height: 30px; |
| | | text-align: center; |
| | | } |
| | | |
| | | .totalLabel { |
| | | font-family: Source Han Sans CN, Source Han Sans CN; |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #ffffff; |
| | | line-height: 20px; |
| | | text-align: center; |
| | | } |
| | | } |
| | | .totalLabel { |
| | | font-family: Source Han Sans CN, Source Han Sans CN; |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #ffffff; |
| | | line-height: 20px; |
| | | text-align: center; |
| | | } |
| | | } |
| | | |
| | | .contentBox { |
| | | width: 246px; |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 13px 0; |
| | | font-family: Source Han Sans CN, Source Han Sans CN, serif; |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #ffffff; |
| | | line-height: 14px; |
| | | .contentBox { |
| | | width: 246px; |
| | | display: flex; |
| | | flex-wrap: wrap; |
| | | gap: 13px 0; |
| | | font-family: Source Han Sans CN, Source Han Sans CN, serif; |
| | | font-weight: 400; |
| | | font-size: 14px; |
| | | color: #ffffff; |
| | | line-height: 14px; |
| | | |
| | | .overviewItem { |
| | | width: calc(100% / 3); |
| | | .overviewItem { |
| | | width: calc(100% / 3); |
| | | |
| | | .itemName { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-bottom: 6px; |
| | | .itemName { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-bottom: 6px; |
| | | |
| | | img { |
| | | width: 16px; |
| | | height: 16px; |
| | | } |
| | | } |
| | | img { |
| | | width: 16px; |
| | | height: 16px; |
| | | } |
| | | } |
| | | |
| | | .itemValue { |
| | | font-family: YouSheBiaoTiHei, YouSheBiaoTiHei, serif; |
| | | font-weight: 400; |
| | | font-size: 24px; |
| | | line-height: 18px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | .itemValue { |
| | | font-family: YouSheBiaoTiHei, YouSheBiaoTiHei, serif; |
| | | font-weight: 400; |
| | | font-size: 24px; |
| | | line-height: 18px; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | |
| | | .completion { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | width: 356px; |
| | | height: 23px; |
| | | margin-top: 16px; |
| | | .completion { |
| | | display: flex; |
| | | align-items: center; |
| | | justify-content: space-between; |
| | | width: 356px; |
| | | height: 23px; |
| | | margin-top: 16px; |
| | | |
| | | &--left-img { |
| | | width: 16px; |
| | | height: 16px; |
| | | } |
| | | &--left-img { |
| | | width: 16px; |
| | | height: 16px; |
| | | } |
| | | |
| | | &-text { |
| | | font-family: YouSheBiaoTiHei, YouSheBiaoTiHei, serif; |
| | | font-weight: 400; |
| | | font-size: 18px; |
| | | color: #ffffff; |
| | | line-height: 21px; |
| | | } |
| | | &-text { |
| | | font-family: YouSheBiaoTiHei, YouSheBiaoTiHei, serif; |
| | | font-weight: 400; |
| | | font-size: 18px; |
| | | color: #ffffff; |
| | | line-height: 21px; |
| | | } |
| | | |
| | | &-separator { |
| | | width: 149px; |
| | | height: 0px; |
| | | border: 1px solid #4ca6ff; |
| | | opacity: 0.2; |
| | | } |
| | | } |
| | | &-separator { |
| | | width: 149px; |
| | | height: 0px; |
| | | border: 1px solid #4ca6ff; |
| | | opacity: 0.2; |
| | | } |
| | | } |
| | | |
| | | .chart { |
| | | width: 356px; |
| | | height: 190px; |
| | | } |
| | | .chart { |
| | | width: 356px; |
| | | height: 190px; |
| | | } |
| | | } |
| | | </style> |
| | |
| | | <el-input class="ztzf-input" v-model="searchForm.key_word" placeholder="请输入任务/机巢名称" clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="行政区划:"> |
| | | <el-select class="ztzf-select" v-model="searchForm.area_code" @change="deptChange" placeholder="请选择部门" clearable> |
| | | <el-select :teleported="false" class="ztzf-select" v-model="searchForm.area_code" @change="deptChange" placeholder="请选择部门" clearable> |
| | | <el-option v-for="item in deptTreeData" :key="item.area_code" :label="item.area_name" :value="item.area_code" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="所属机巢:"> |
| | | <el-select class="ztzf-select" v-model="searchForm.device_sn" placeholder="请选择机巢" clearable> |
| | | <el-select :teleported="false" class="ztzf-select" v-model="searchForm.device_sn" placeholder="请选择机巢" clearable> |
| | | <el-option v-for="item in machineData" :key="item.device_sn" :label="item.nickname" :value="item.device_sn" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item> |
| | | <el-date-picker |
| | | popper-class="custom-date-picker" |
| | | class="ztzf-date-picker" |
| | | v-model="dateRange" |
| | | type="daterange" |
| | |
| | | <TaskAlgorithmBusiness :setWidth="186" :showAlgorithm="true" @algorithmChange="algorithmChange"/> |
| | | </el-form-item> |
| | | <el-form-item label="所属部门:" v-if="isExpand"> |
| | | <el-select class="ztzf-select" v-model="searchForm.create_dept" placeholder="请选择部门" clearable> |
| | | <el-select :teleported="false" class="ztzf-select" v-model="searchForm.create_dept" placeholder="请选择部门" clearable> |
| | | <el-option v-for="item in deptData" :key="item.id" :label="item.deptName" :value="item.id" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | <el-form-item label="任务状态:" v-if="isExpand"> |
| | | <el-select class="ztzf-select" v-model="searchForm.status" placeholder="请选择状态" clearable> |
| | | <el-select :teleported="false" class="ztzf-select" v-model="searchForm.status" placeholder="请选择状态" clearable> |
| | | <el-option v-for="item in statusOptions" :key="item.value" :label="item.label" :value="item.value" /> |
| | | </el-select> |
| | | </el-form-item> |
| | |
| | | handleSearch(); |
| | | }); |
| | | </script> |
| | | |
| | | <style lang="scss"> |
| | | /* 修改日期单元格背景色 */ |
| | | .custom-date-picker .el-picker-panel__body { |
| | | background: #012350 !important; |
| | | color: #fff !important; |
| | | } |
| | | </style> |
| | | <style lang="scss" scoped> |
| | | .search-box-test { |
| | | transition: all 0.3s; |
| | |
| | | <el-table-column label="任务状态" > |
| | | <template #default="scope"> |
| | | <span :style="{ |
| | | color: scope.row.status === 1 ? '#e36913' : |
| | | scope.row.status === 2 ? '#ffc398' : |
| | | scope.row.status === 3 ? '#afd9fb' : |
| | | scope.row.status === 4 ? '#11c4ff' : '8cfea7' |
| | | color: scope.row.status === 1 ? '#ffe17e' : |
| | | scope.row.status === 2 ? '#ffa768' : |
| | | scope.row.status === 3 ? '#8effac' : |
| | | scope.row.status === 5 ? '#ff8e8e':'' |
| | | }"> |
| | | {{ scope.row.status ? getStatusText(scope.row.status) : '' }} |
| | | </span> |
| | |
| | | <!-- 添加任务 --> |
| | | <AddTask v-model:show="isShowAddTask" @refresh="searchClick"/> |
| | | <!-- 当前任务详情 --> |
| | | <CurrentTaskDetails v-if="isShowCurrentTaskDetails" v-model:show="isShowCurrentTaskDetails" :id="rowData.id"/> |
| | | <CurrentTaskDetails |
| | | v-if="isShowCurrentTaskDetails" |
| | | v-model:show="isShowCurrentTaskDetails" |
| | | :id="rowData.id"/> |
| | | <!-- 历史人物详情 --> |
| | | <DeviceJobDetails |
| | | v-if="isShowDeviceJobDetails" |
| | | v-model:show="isShowDeviceJobDetails" |
| | | :wayLineJodInfoId="wayLineJodInfoId"/> |
| | | </template> |
| | | |
| | | <script setup> |
| | |
| | | import CurrentTaskDetails from '@/components/CurrentTaskDetails/CurrentTaskDetails.vue'; |
| | | import { jobList } from '@/api/home/task'; |
| | | import { ElMessage } from 'element-plus' |
| | | import DeviceJobDetails from '@/components/DeviceJobDetails/DeviceJobDetails.vue' |
| | | |
| | | const jobListParams = reactive({ |
| | | current: 1, |
| | |
| | | }); |
| | | const jobListData = ref([]); |
| | | const total = ref(0); |
| | | let wayLineJodInfoId = ref('') |
| | | let isShowDeviceJobDetails = ref(false); |
| | | let isShowCurrentTaskDetails = ref(false); |
| | | |
| | | // 获取任务列表 |
| | | const getJobList = () => { |
| | |
| | | const statusMap = { |
| | | 1: '待执行', |
| | | 2: '执行中', |
| | | 3: '已完成', |
| | | 3: '已执行', |
| | | 4: '已取消', |
| | | 5: '执行失败' |
| | | }; |
| | |
| | | }; |
| | | |
| | | // 查看当前任务详情 如果是一台机则显示详情 如果是多台机则进入集群调度(暂未开发) |
| | | let isShowCurrentTaskDetails = ref(true); |
| | | let rowData = ref({}); |
| | | const handleDetail = (row) => { |
| | | if (row.device_sns.length === 1){ |
| | | console.log(row) |
| | | if (row.status === 2 || row.status === 1){ |
| | | rowData.value = row? row : {}; |
| | | isShowCurrentTaskDetails.value = true; |
| | | } else{ |
| | | ElMessage.warning('todo 跳历史任务详情') |
| | | wayLineJodInfoId.value = row.id |
| | | isShowDeviceJobDetails.value = true |
| | | } |
| | | }else{ |
| | | ElMessage.warning('即将跳转到集群调度'); |
| | |
| | | <!-- 关联算法和综合业务 --> |
| | | <template> |
| | | <div class="task-algorithm" v-if="showAlgorithm"> |
| | | <el-select class="ztzf-select" :style="{ width: pxToRem(setWidth) }" v-model="ai_types" multiple collapse-tags collapse-tags-tooltip placeholder="请选择算法" clearable @change="handleAlgorithmChange"> |
| | | <el-select :teleported="false" class="ztzf-select" :style="{ width: pxToRem(setWidth) }" v-model="ai_types" multiple collapse-tags collapse-tags-tooltip placeholder="请选择算法" clearable @change="handleAlgorithmChange"> |
| | | <el-option v-for="item in taskAlgorithm" :key="item.id" :label="item.dictValue" :value="item.dictKey" /> |
| | | </el-select> |
| | | </div> |
| | | <div class="task-business" v-if="showBusiness"> |
| | | <el-select class="ztzf-select" :style="{ width: pxToRem(setWidth) }" v-model="industry_type" placeholder="请选择类型" clearable @change="handleBusinessChange"> |
| | | <el-select :teleported="false" class="ztzf-select" :style="{ width: pxToRem(setWidth) }" v-model="industry_type" placeholder="请选择类型" clearable @change="handleBusinessChange"> |
| | | <el-option v-for="item in taskBusiness" :key="item.id" :label="item.dictValue" :value="item.dictKey" /> |
| | | </el-select> |
| | | </div> |