From 980017c9508f8b8449e773d8913b3b407f0a3416 Mon Sep 17 00:00:00 2001
From: chenyao <1219716595@qq.com>
Date: Fri, 18 Apr 2025 08:44:13 +0800
Subject: [PATCH] Merge branch 'master' of http://139.196.74.78:10010/r/drone/command-center-dashboard
---
src/utils/stateToImageMap/event.js | 23
src/hooks/components/useMapHandlerClick.js | 89 ++
src/hooks/components/EventPopUpBox.vue | 16
src/api/drc.js | 9
src/views/SignMachineNest/MachineRight/MachineMonitor.vue | 135 ++--
src/api/payload.js | 11
src/views/SignMachineNest/SignMachineNest.vue | 1
src/components/CurrentTaskDetails/TaskDetailsLeft.vue | 21
src/hooks/useMapAggregation/useMapAggregation.js | 139 +----
src/views/Home/SearchBox.vue | 442 ++++++++--------
src/components/CurrentTaskDetails/CurrentTaskDetails.vue | 21
src/views/SignMachineNest/MachineLeft/InspectionRaskDetails.vue | 404 +++++++-------
src/hooks/useSingleDroneMap/useSingleDroneMap.js | 135 ----
src/hooks/controlDrone/useManualControl.js | 130 +++-
src/mqtt/index.js | 2
src/utils/stateToImageMap/drone.js | 32 +
src/components/CurrentTaskDetails/ControlPanel/ControlPanel.vue | 1
src/components/CurrentTaskDetails/ControlPanel/BaseControl.vue | 1
src/components/CurrentTaskDetails/TaskDetailsRight.vue | 4
19 files changed, 838 insertions(+), 778 deletions(-)
diff --git a/src/api/drc.js b/src/api/drc.js
index 674f8c7..9706b3f 100644
--- a/src/api/drc.js
+++ b/src/api/drc.js
@@ -30,6 +30,15 @@
})
}
+// 无人控制
+export async function updateDroneQualityApi (data) {
+ return request({
+ url: '/drone-device-core/manage/api/v1/live/streams/update',
+ method: 'post',
+ data,
+ })
+}
+
// 无人机退出控制
export async function exitController(data) {
return request({
diff --git a/src/api/payload.js b/src/api/payload.js
index 61e91ea..77b9be3 100644
--- a/src/api/payload.js
+++ b/src/api/payload.js
@@ -53,12 +53,21 @@
// 相机参数调整
export async function cameraParamsChangeApi(data) {
return await request({
- url:`/drone-device-core/payload-control/payload/zoom/level`,
+ url:`${API_PREFIX}/devices/payload-control/payload/zoom/level`,
method:'post',
data
})
}
+// 云台重置
+export async function ptzResetModeApi(params) {
+ return await request({
+ url:`${API_PREFIX}/devices/payload-control/payload/reset`,
+ method:'get',
+ params
+ })
+}
+
// 切换直播镜头
diff --git a/src/components/CurrentTaskDetails/ControlPanel/BaseControl.vue b/src/components/CurrentTaskDetails/ControlPanel/BaseControl.vue
index fd914f6..b682f14 100644
--- a/src/components/CurrentTaskDetails/ControlPanel/BaseControl.vue
+++ b/src/components/CurrentTaskDetails/ControlPanel/BaseControl.vue
@@ -55,6 +55,7 @@
}
function turnBack() {
+ isAutoControl.value = true
EventBus.emit('controlPanel-returnOrCancelReturn')
}
</script>
diff --git a/src/components/CurrentTaskDetails/ControlPanel/ControlPanel.vue b/src/components/CurrentTaskDetails/ControlPanel/ControlPanel.vue
index d78a55a..546ab80 100644
--- a/src/components/CurrentTaskDetails/ControlPanel/ControlPanel.vue
+++ b/src/components/CurrentTaskDetails/ControlPanel/ControlPanel.vue
@@ -88,6 +88,7 @@
:style="item.style"
class="ptzControlItem"
@mousedown="onMouseDown(item.key)"
+ @mouseup="onMouseUp"
></div>
<div
diff --git a/src/components/CurrentTaskDetails/CurrentTaskDetails.vue b/src/components/CurrentTaskDetails/CurrentTaskDetails.vue
index 8d51880..c93ba94 100644
--- a/src/components/CurrentTaskDetails/CurrentTaskDetails.vue
+++ b/src/components/CurrentTaskDetails/CurrentTaskDetails.vue
@@ -18,8 +18,8 @@
<TaskDetailsRight v-if="isAutoControl" />
<template v-else>
<TaskDetailsHead />
+ <TaskDetailsLeft />
</template>
- <TaskDetailsLeft />
<!-- 控制面板,里面有方法需要立即执行,不可用v-if -->
<ControlPanel v-show="!isAutoControl" />
@@ -45,7 +45,7 @@
import amplifyImg from '@/assets/images/taskManagement/taskIntermediateContent/amplifyImg.png'
import { ElMessage } from 'element-plus'
import EventBus from '@/event-bus'
-
+import { updateDroneQualityApi } from '@/api/drc'
const isAutoControl = ref(true)
const lineQuality = ref(1) //1流畅,2标清
@@ -67,7 +67,7 @@
const currentLiveUrl = ref('')
const isTakeOff = ref(false)
const isMaxMap = ref(false)
-let droneWebSocket //WS实例
+let droneWebSocket //WS实例
// 机巢直播
const getDeviceLiveUrl = async () => {
@@ -75,10 +75,19 @@
currentLiveUrl.value = res.data.data.rtcs_url
}
-// 无人机直播画质切换
-const changeLineQuality = async () => {
+const video_id = ref('')
+
+// 获取无人机直播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
+}
+
+// 无人机直播画质切换
+const changeLineQuality = async () => {
+ const res = await updateDroneQualityApi({ video_id: video_id.value, video_quality: lineQuality.value })
+ ElMessage.success('切换画质成功')
}
// 设置当前直播地址
@@ -89,7 +98,7 @@
// 如果还是之前的状态,不切换
if (isTakeOff.value === currentIsTakeOff) return
isTakeOff.value = currentIsTakeOff
- isTakeOff.value ? await changeLineQuality() : await getDeviceLiveUrl()
+ isTakeOff.value ? await getDroneLiveUrl() : await getDeviceLiveUrl()
}
// 获取任务详情获取航线文件
diff --git a/src/components/CurrentTaskDetails/TaskDetailsLeft.vue b/src/components/CurrentTaskDetails/TaskDetailsLeft.vue
index 812bd86..861e33f 100644
--- a/src/components/CurrentTaskDetails/TaskDetailsLeft.vue
+++ b/src/components/CurrentTaskDetails/TaskDetailsLeft.vue
@@ -11,8 +11,8 @@
{{ item.name }}相机
</div>
<div class="multiCol">
- <div @click="faceToCenter">云台回中</div>
- <div @click="faceToDown">云台朝下</div>
+ <div @click="ptzResetMode(0)">云台回中</div>
+ <div @click="ptzResetMode(1)">云台朝下</div>
</div>
<el-select class="qualityChange" v-model="lineQuality" @change="qualityChange">
<el-option v-for="item in qualityList" :key="item.id" :label="item.name" :value="item.id" />
@@ -30,8 +30,9 @@
<el-slider
v-model="cameraParams.zoom_factor"
vertical
- :max="cameraParams.camera_type === 'ir' ? 20 : 50"
- @input="sliderChange"
+ :min="cameraParams.camera_type === 'ir' ? 2 : 0"
+ :max="cameraParams.camera_type === 'ir' ? 200 : 200"
+ @change="sliderChange"
/>
</div>
</div>
@@ -39,7 +40,7 @@
<script setup>
import EventBus from '@/event-bus'
-import { callPhotoAndVideoCmd, cameraParamsChangeApi } from '@/api/payload'
+import { callPhotoAndVideoCmd, cameraParamsChangeApi, ptzResetModeApi } from '@/api/payload'
import { ElMessage } from 'element-plus'
import { throttle } from 'lodash'
@@ -65,9 +66,9 @@
{ name: '超清', id: 4 },
]
-function faceToCenter() {}
-
-function faceToDown() {}
+function ptzResetMode(resetMode) {
+ ptzResetModeApi({ sn: dockSn.value, resetMode })
+}
// 倍率调整
function sliderChange(val) {
@@ -82,9 +83,11 @@
// 修改相机参数
function cameraParamsChange() {
+ const { camera_type, zoom_factor} = cameraParams.value
cameraParamsChangeApi({
sn: dockSn.value,
...cameraParams.value,
+ zoom_factor: camera_type === 'wide' ? undefined : zoom_factor,
})
}
@@ -97,7 +100,7 @@
cameraParams.value.zoom_factor = 5
}
if (row.key === 'ir') {
- cameraParams.value.zoom_factor = 0
+ cameraParams.value.zoom_factor = 2
}
cameraParamsChange()
}
diff --git a/src/components/CurrentTaskDetails/TaskDetailsRight.vue b/src/components/CurrentTaskDetails/TaskDetailsRight.vue
index 32105be..61b2189 100644
--- a/src/components/CurrentTaskDetails/TaskDetailsRight.vue
+++ b/src/components/CurrentTaskDetails/TaskDetailsRight.vue
@@ -54,6 +54,10 @@
gap: 25px 0;
align-items: center;
+ .manualControl{
+ background: transparent !important;
+ }
+
.titleImg {
width: 224px;
height: 131px;
diff --git a/src/hooks/components/EventPopUpBox.vue b/src/hooks/components/EventPopUpBox.vue
index 5a50e29..ddee5be 100644
--- a/src/hooks/components/EventPopUpBox.vue
+++ b/src/hooks/components/EventPopUpBox.vue
@@ -31,7 +31,7 @@
</div>
</template>
<script setup>
-import { ElImage } from 'element-plus'
+import { ElImage, ElIcon } from 'element-plus'
// src="@/assets/images/home/useEventOperate/eventErr.png"
import { Close } from '@element-plus/icons-vue'
@@ -54,10 +54,20 @@
loading.value = true
const res = await getEventDetails({ id: props.data.eventId })
info.value = res.data.data
-
- info.value.url = import.meta.env.VITE_APP_AIRLINE_URL + info.value.url
+ info.value.url = disposeUrl(info.value)
loading.value = false
})
+
+const disposeUrl = ({ url, media_type }) => {
+ if (media_type == 'PHOTO_TYPE') {
+ const parts = url.split('.')
+ parts[parts.length - 2] += '_small' // 在最后一个点前插入 _small
+
+ return import.meta.env.VITE_APP_AIRLINE_URL + parts.join('.')
+ }
+
+ return import.meta.env.VITE_APP_AIRLINE_URL + url
+}
</script>
<style scoped lang="scss">
diff --git a/src/hooks/components/useMapHandlerClick.js b/src/hooks/components/useMapHandlerClick.js
index a4854ce..82d59c5 100644
--- a/src/hooks/components/useMapHandlerClick.js
+++ b/src/hooks/components/useMapHandlerClick.js
@@ -2,33 +2,72 @@
* @Author: shuishen 1109946754@qq.com
* @Date: 2025-04-15 22:41:40
* @LastEditors: shuishen 1109946754@qq.com
- * @LastEditTime: 2025-04-16 19:02:39
+ * @LastEditTime: 2025-04-17 19:43:36
* @FilePath: \command-center-dashboard\src\hooks\components\useMapHandlerClick.js
* @Description:
*
* Copyright (c) 2025 by shuishen, All Rights Reserved.
*/
import * as Cesium from 'cesium'
+import { render } from 'vue'
+import { useStore } from 'vuex'
+
+import DevicePopUpBox from '@/hooks/components/DevicePopUpBox.vue'
import EventPopUpBox from '@/hooks/components/EventPopUpBox.vue'
/**
*
* @param {Object} options - 配置选项
*/
-export function useMapHandlerClick (viewer, options = {
- type: 'single-drone-event',
- customDom: EventPopUpBox
-}) {
- const { type } = options
+export function useMapHandlerClick (options = {}) {
+ const {
+ popupType = 'event-popup',
+ eventType = 'single-drone-event',
+ styleTransform = 'translate(-50%,-110%)',
+ getViewer
+ } = options
- const currentClickEntity = ref(null)
+ const store = useStore()
+
+ const popupData = {
+ 'event-popup': EventPopUpBox,
+ 'device-popup': DevicePopUpBox
+ }
+
+ const MapPopUpBox = popupData[popupType]
+
+ let viewer = null
let handler = null
+ let currentClickEntity = null
// 查找特定类型的实体
const findEntityByType = (entities, type) => {
- return entities.find(entity =>
- entity?.properties?.customData?._value?.data?.type === type
- )
+ let types = []
+
+ Array.isArray(type) ? types = type : types = [type]
+
+ return types.reduce((pre, curType) => {
+ let entity = entities.find(entity => entity?.properties?.customData?._value?.data?.type === curType)
+
+ return entity ? (pre.push({
+ type: curType,
+ entity
+ }), pre) : pre
+ }, [])
+ }
+
+ const publicEvent = (entity) => {
+ viewer.scene.postRender.addEventListener(labelBoxRender)
+ }
+
+ const typeEvent = {
+ 'deviceAggregation': publicEvent,
+ 'single-drone-event': publicEvent,
+ 'event': publicEvent,
+ 'device': (entity) => {
+ const device = entity.properties.customData._value.data
+ store.commit('setSingleUavHome', device)
+ }
}
// 左键单机事件
@@ -36,17 +75,21 @@
let clickedEntities = viewer?.scene.drillPick(click.position).map(item => item.id)
if (!clickedEntities.length) return
- currentClickEntity.value = findEntityByType(clickedEntities, type)
+ let curClick = findEntityByType(clickedEntities, eventType)
removeLabel()
- if (currentClickEntity.value) {
- viewer?.scene.postRender.addEventListener(labelBoxRender)
+ if (curClick.length > 0 && typeEvent[curClick[0].type]) {
+ currentClickEntity = curClick[0].entity
+
+ typeEvent[curClick[0].type](currentClickEntity)
}
}
// 事件初始化
const handlerInit = () => {
+ !viewer && (viewer = getViewer())
+
if (handler) return
handler = new Cesium.ScreenSpaceEventHandler(viewer?.scene.canvas)
@@ -62,7 +105,7 @@
// 获取弹框box
const getLabelDom = data => {
- const vNode = h(customDom, { data, removeLabel })
+ const vNode = h(MapPopUpBox, { data, removeLabel })
const tooltipContainer = document.createElement('div')
tooltipContainer.id = 'mapPopUpBox'
tooltipContainer.style.position = 'absolute'
@@ -75,12 +118,12 @@
// 弹框位置刷新
const labelBoxRender = () => {
- if (!currentEntity) return
+ if (!currentClickEntity) return
let dom = document.querySelector('#mapPopUpBox')
if (!dom) {
- dom = getLabelDom(currentEntity.properties.customData._value.data)
+ dom = getLabelDom(currentClickEntity.properties.customData._value.data)
}
- const screenPosition = viewer?.scene.cartesianToCanvasCoordinates(currentEntity?.position?._value)
+ const screenPosition = viewer?.scene.cartesianToCanvasCoordinates(currentClickEntity?.position?._value)
if (screenPosition) {
dom.style.left = `${screenPosition.x}px`
dom.style.top = `${screenPosition.y}px`
@@ -101,15 +144,19 @@
removeDom()
}
- // 自动清理
- onUnmounted(() => {
+ const removeAll = () => {
removeHandler()
removeLabel()
+ }
+
+ // 自动清理
+ onUnmounted(() => {
+ removeAll()
})
return {
- currentClickEntity,
handlerInit,
- removeHandler
+ removeAll,
+ removeLabel
}
}
\ No newline at end of file
diff --git a/src/hooks/controlDrone/useManualControl.js b/src/hooks/controlDrone/useManualControl.js
index 4dc12ff..4ccce9f 100644
--- a/src/hooks/controlDrone/useManualControl.js
+++ b/src/hooks/controlDrone/useManualControl.js
@@ -36,11 +36,18 @@
}
export function useManualControl(mqttState,deviceTopicInfo, isCurrentFlightController,paramsRef) {
- const activeCodeKey = ref(null)
+ let activeCodeKey = null
let genPortOne = true //是一代机场
const mqttHooks = useMqtt(mqttState,deviceTopicInfo)
let seq = 0
- let throttledPtz = null
+ const keysPressed = [
+ {key: 'KeyQ', value: false},
+ {key: 'KeyW', value: false},
+ {key: 'KeyE', value: false},
+ {key: 'KeyA', value: false},
+ {key: 'KeyS', value: false},
+ {key: 'KeyD', value: false},
+ ]
function handlePublish(params) {
const body = {
@@ -51,7 +58,7 @@
myInterval = setInterval(() => {
body.data.seq = seq++
seq++
- window.console.log('keyCode>>>>', activeCodeKey.value, body)
+ window.console.log('keyCode>>>>', activeCodeKey, body)
mqttHooks?.publishMqtt(deviceTopicInfo.pubTopic, body, { qos: 0 })
}, 50)
}
@@ -71,84 +78,137 @@
const HEIGHT = genPortOne ? 5 : 500; // check
const W_SPEED = genPortOne ? 20 : 500 // 机头角速度
+
+ let arrList = []
+ if (genPortOne) {
+ arrList = [
+ { Key: 'KeyQ', category: 'w', value: -W_SPEED },{ Key: 'KeyW',category: 'x', value: SPEED },
+ { Key: 'KeyE', category: 'w', value: W_SPEED},{ Key: 'KeyA', category: 'y', value: -SPEED},
+ { Key: 'KeyS', category: 'x', value: -SPEED},{ Key: 'KeyD', category: 'y', value: SPEED}
+ ]
+ } else {
+ arrList = [
+ { Key: 'KeyQ', category: 'yaw', value: 1024-W_SPEED },{ Key: 'KeyW',category: 'pitch', value: 1024+SPEED },
+ { Key: 'KeyE', category: 'yaw', value: 1024+W_SPEED},{ Key: 'KeyA', category: 'roll', value: 1024-SPEED},
+ { Key: 'KeyS', category: 'pitch', value: 1024-SPEED},{ Key: 'KeyD', category: 'roll', value: 1024+SPEED}
+ ]
+ }
+ let resultObj = []
+ let resultArr = []
+ // 遍历被选中得数组
+ if (keyCode === 'assemblyKeys') {
+ keysPressed.map(key => {
+ if (key.value) {
+ let result = arrList.filter(item =>item.Key===key.key)
+ resultObj.push(result[0])
+ }
+ })
+ // 相同属性值进行计算
+ resultArr = resultObj.reduce((acc, item) => {
+ // 如果累加器中已经有这个 category,则累加 value
+ if (acc[item.category]) {
+ // acc[item.category] += item.value;
+ acc[item.category] = genPortOne ? 0 : 1024;
+ } else {
+ // 否则,初始化这个 category 的 value
+ acc[item.category] = item.value;
+ }
+ return acc;
+ }, {});
+ }
+
seq = 0
switch (keyCode) {
+ case 'assemblyKeys': // 针对Q W E A S D 组合键
+ if (activeCodeKey === keyCode) return
+ handlePublish(resultArr)
+ activeCodeKey = keyCode
+ break
case 'KeyQ':
- if (activeCodeKey.value === keyCode) return
+ if (activeCodeKey === keyCode) return
let objQ = { yaw: 1024-W_SPEED }
if (genPortOne) {objQ = {w: -W_SPEED}}
handlePublish(objQ)
- activeCodeKey.value = keyCode
+ activeCodeKey = keyCode
break
case 'KeyW':
- if (activeCodeKey.value === keyCode) return
+ if (activeCodeKey === keyCode) return
let objW = { pitch: 1024+SPEED }
if (genPortOne) {objW = {x: SPEED}}
handlePublish(objW)
- activeCodeKey.value = keyCode
+ activeCodeKey = keyCode
break
case 'KeyE':
- if (activeCodeKey.value === keyCode) return
+ if (activeCodeKey === keyCode) return
let objE = { yaw: 1024+W_SPEED }
if (genPortOne) {objE = {w: W_SPEED}}
handlePublish(objE)
- activeCodeKey.value = keyCode
+ activeCodeKey = keyCode
break
case 'KeyA':
- if (activeCodeKey.value === keyCode) return
+ if (activeCodeKey === keyCode) return
let objA = { roll: 1024-SPEED }
if (genPortOne) {objA = {y: -SPEED}}
handlePublish(objA)
- activeCodeKey.value = keyCode
+ activeCodeKey = keyCode
break
case 'KeyS':
- if (activeCodeKey.value === keyCode) return
+ if (activeCodeKey === keyCode) return
let objS = { pitch: 1024-SPEED }
if (genPortOne) {objS = {x: -SPEED}}
handlePublish(objS)
- activeCodeKey.value = keyCode
+ activeCodeKey = keyCode
break
case 'KeyD':
- if (activeCodeKey.value === keyCode) return
+ if (activeCodeKey === keyCode) return
let objD = { roll: 1024+SPEED }
if (genPortOne) {objD = {y: SPEED}}
handlePublish(objD)
- activeCodeKey.value = keyCode
+ activeCodeKey = keyCode
break
case 'KeyZ':
- if (activeCodeKey.value === keyCode) return
+ if (activeCodeKey === keyCode) return
let objZ = { throttle: 1024-HEIGHT }
if (genPortOne) {objZ = {h: -HEIGHT}}
handlePublish(objZ)
- activeCodeKey.value = keyCode
+ activeCodeKey = keyCode
break
// case 'ArrowDown':
case 'KeyC':
- if (activeCodeKey.value === keyCode) return
+ if (activeCodeKey === keyCode) return
let objC = { throttle: 1024+HEIGHT }
if (genPortOne) {objC = {h: HEIGHT}}
handlePublish(objC)
- activeCodeKey.value = keyCode
+ activeCodeKey = keyCode
break
case 'ArrowUp':
- ptzThrottle[0]()
+ if (activeCodeKey === keyCode) return
+ clearInterval(myInterval)
+ myInterval = setInterval(()=>ptzControl('up'), 200)
+ activeCodeKey = keyCode
break
case 'ArrowDown':
- ptzThrottle[1]()
+ if (activeCodeKey === keyCode) return
+ clearInterval(myInterval)
+ myInterval = setInterval(()=>ptzControl('down'), 200)
+ activeCodeKey = keyCode
break
case 'ArrowLeft':
- ptzThrottle[2]()
+ if (activeCodeKey === keyCode) return
+ clearInterval(myInterval)
+ myInterval = setInterval(()=>ptzControl('left'), 200)
+ activeCodeKey = keyCode
break
case 'ArrowRight':
- ptzThrottle[3]()
+ if (activeCodeKey === keyCode) return
+ clearInterval(myInterval)
+ myInterval = setInterval(()=>ptzControl('right'), 200)
+ activeCodeKey = keyCode
break
default:
break
}
}
-
- // 云台控制节流list
- const ptzThrottle = ['up','down','left','right'].map(key => throttle(()=>ptzControl(key), 500))
// 云台上下左右
function ptzControl(key) {
@@ -175,7 +235,8 @@
}
function resetControlState() {
- activeCodeKey.value = null
+ keysPressed.map(item => item.value = false)
+ activeCodeKey = null
seq = 0
handleClearInterval()
}
@@ -185,7 +246,16 @@
}
function onKeydown(e) {
- handleKeyup(e.code)
+ e.preventDefault()
+ keysPressed.forEach(item => {
+ if(e.code === item.key) item.value = true
+ })
+ let result = keysPressed.filter(item=>item.value===true)
+ if (result && result.length >= 2) {
+ handleKeyup('assemblyKeys')
+ } else {
+ handleKeyup(e.code)
+ }
}
function startKeyboardManualControl() {
@@ -211,6 +281,10 @@
{ immediate: true }
)
+ onBeforeUnmount(()=>{
+ mqttHooks?.d
+ })
+
onUnmounted(() => {
closeKeyboardManualControl()
})
diff --git a/src/hooks/useMapAggregation/useMapAggregation.js b/src/hooks/useMapAggregation/useMapAggregation.js
index 5557967..d46d74a 100644
--- a/src/hooks/useMapAggregation/useMapAggregation.js
+++ b/src/hooks/useMapAggregation/useMapAggregation.js
@@ -3,30 +3,19 @@
import eventAggregationImg from '@/assets/images/home/useUavHome/eventAggregationImg.png'
import uavImg from '@/assets/images/home/useUavHome/uavImg.png'
-// 事件图标
-import eventSingle from '@/assets/images/home/useEventOperate/eventSingle.png'
+import { getEventImage } from '@/utils/stateToImageMap/event'
+import { getDroneStatusImage } from '@/utils/stateToImageMap/drone'
-import eventPending from '@/assets/images/home/useEventOperate/eventPending.png' // 待处理 0
-import eventWaitAudit from '@/assets/images/home/useEventOperate/eventWaitAudit.png' // 待审核 2
-import eventProcessing from '@/assets/images/home/useEventOperate/eventProcessing.png' // 处理中 3
-import eventCompleted from '@/assets/images/home/useEventOperate/eventCompleted.png' // 已完成 4
-import eventClosed from '@/assets/images/home/useEventOperate/eventClosed.png' // 已完结 5
-
-import DevicePopUpBox from '@/hooks/components/DevicePopUpBox.vue'
-import EventPopUpBox from '@/hooks/components/EventPopUpBox.vue'
-
-
-// 机巢图标
-import endingImg from '@/assets/images/aiNowFly/ending.png'
-import endingHighImg from '@/assets/images/aiNowFly/ending-high.png'
-
-import { render } from 'vue'
import { useStore } from 'vuex'
import { getCenterPoint } from '@/utils/cesium/mapUtil'
import cesiumOperation from '@/utils/cesium-tsa'
import { getDeviceRegion, getDeviceRegionCount, getEventDetails, getMapEvents } from '@/api/home/aggregation'
import { PolyGradientMaterial } from '@/utils/cesium/Material'
import { start } from 'nprogress'
+
+// hook
+import { useMapHandlerClick } from '@/hooks/components/useMapHandlerClick'
+
/**
* 机巢聚合功能
*/
@@ -51,8 +40,21 @@
const { flyTo } = cesiumOperation()
const mergeImg = type === 'device' ? aggregationImg : eventAggregationImg
- const MapPopUpBox = type === 'device' ? DevicePopUpBox : EventPopUpBox
const styleTransform = type === 'device' ? 'translateY(-50%)' : 'translate(-50%,-110%)'
+ const eventType = type === 'device' ? ['deviceAggregation', 'device'] : 'event'
+
+ const {
+ handlerInit: mapHandlerInit,
+ removeAll: removeMapHandlerAll,
+ removeLabel: removeMapLabel
+ } = useMapHandlerClick({
+ popupType: `${type}-popup`,
+ eventType: eventType,
+ styleTransform,
+ getViewer () {
+ return viewer
+ }
+ })
let scalingJudgment = [
{ name: '县', splashedList: [], gJson: null, show: false, outline: {}, value: [0, 48651], height: 31753 },
@@ -69,8 +71,6 @@
]
let viewer = null
let active = null
- let handler = null
- let currentEntity = null
const store = useStore()
const userAreaCode = computed(() => store.state.user.userInfo.detail.areaCode)
@@ -104,7 +104,7 @@
if (active === item.name) return
active = item.name
removeEntities()
- removeLabel()
+ removeMapLabel()
renderOutline(item)
if (!item.gJson && !item.splashedList?.length) return
item.gJson ? aggregation(item) : splashed(item)
@@ -288,7 +288,7 @@
needFly = true
if (!viewer) return
- handlerInit()
+ mapHandlerInit()
viewer.scene.postRender.removeEventListener(determineScaling)
@@ -302,15 +302,7 @@
//散点机巢
function splashed (row) {
row.splashedList.forEach((item, index) => {
- const eventImage = {
- 0: eventPending,
- 2: eventWaitAudit,
- 3: eventProcessing,
- 4: eventCompleted,
- 5: eventClosed
- }
-
- const image = type === 'device' ? (item.status === "OFFLINE" ? endingHighImg : endingImg) : eventImage[item.status] || eventSingle
+ const image = type === 'device' ? getDroneStatusImage(item.status) : getEventImage(item.status)
viewer.entities.add({
id: `aggregation-splashed-${index}`,
@@ -470,33 +462,6 @@
})
}
- // 获取弹框box
- const getLabelDom = data => {
- const vNode = h(MapPopUpBox, { data, removeLabel })
- const tooltipContainer = document.createElement('div')
- tooltipContainer.id = 'mapPopUpBox'
- tooltipContainer.style.position = 'absolute'
- tooltipContainer.style.transform = styleTransform
- tooltipContainer.style.pointerEvents = 'none'
- document.querySelector('.page-index').append(tooltipContainer)
- render(vNode, tooltipContainer)
- return tooltipContainer
- }
-
- // 弹框位置刷新
- const labelBoxRender = () => {
- if (!currentEntity) return
- let dom = document.querySelector('#mapPopUpBox')
- if (!dom) {
- dom = getLabelDom(currentEntity.properties.customData._value.data)
- }
- const screenPosition = viewer.scene.cartesianToCanvasCoordinates(currentEntity?.position?._value)
- if (screenPosition) {
- dom.style.left = `${screenPosition.x}px`
- dom.style.top = `${screenPosition.y}px`
- dom.style.display = 'block'
- }
- }
/**
* 根据条件获取项
@@ -506,41 +471,6 @@
*/
const findTypeItem = (arr, condition) => {
return arr.find(item => condition(item))
- }
-
- // 左键单机事件
- const singleMachineEvent = async click => {
- let clickTargets = viewer.scene.drillPick(click.position).map(item => item.id)
- if (!clickTargets.length) return
-
- let deviceAggregationFind = findTypeItem(
- clickTargets,
- item => item?.properties?.customData?._value?.data?.type === 'deviceAggregation'
- )
- let deviceFind = findTypeItem(clickTargets, item => item?.properties?.customData?._value?.data?.type === 'device')
- // "event"
- let eventFind = findTypeItem(clickTargets, item => item?.properties?.customData?._value?.data?.type === 'event')
- // let eventFind = findTypeItem(clickTargets, (item) => item?.properties?.customData?._value?.data?.type === 'eventAggregation')
- currentEntity = deviceAggregationFind || deviceFind || eventFind
-
- if (!currentEntity) return
- if (!currentEntity?.position?._value) return
- // 一定要移除
- removeLabel()
- if (deviceAggregationFind || eventFind) {
- viewer.scene.postRender.addEventListener(labelBoxRender)
- }
- if (deviceFind) {
- const device = deviceFind.properties.customData._value.data
- store.commit('setSingleUavHome', device)
- }
- }
-
- const removeDom = () => {
- const dom = document.querySelector('#mapPopUpBox')
- if (dom && dom.parentNode) {
- dom.parentNode.removeChild(dom)
- }
}
// 移除 点 和 gjson 实体
@@ -556,11 +486,7 @@
item.includes('aggregation-') && viewer.entities.removeById(item)
})
}
- // 移除弹框标签
- const removeLabel = () => {
- viewer?.scene.postRender.removeEventListener(labelBoxRender)
- removeDom()
- }
+
// 移除所有监听事件,变量置空
const removeAll = () => {
@@ -571,14 +497,12 @@
const clearMapEntity = () => {
if (!viewer) return
removeEntities()
- removeLabel()
+ removeMapHandlerAll()
+
// viewer.camera.moveEnd.removeEventListener(determineScaling);
viewer.scene.postRender.removeEventListener(determineScaling)
- handler?.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK)
- handler?.destroy()
+
active = null
- handler = null
- currentEntity = null
}
const init = () => {
@@ -588,14 +512,7 @@
viewer.scene.postRender.addEventListener(determineScaling)
})
- handlerInit()
- }
-
- const handlerInit = () => {
- if (handler) return
-
- handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
- handler.setInputAction(singleMachineEvent, Cesium.ScreenSpaceEventType.LEFT_CLICK)
+ mapHandlerInit()
}
onBeforeUnmount(() => { })
diff --git a/src/hooks/useSingleDroneMap/useSingleDroneMap.js b/src/hooks/useSingleDroneMap/useSingleDroneMap.js
index ff7f0c3..b8b7aa7 100644
--- a/src/hooks/useSingleDroneMap/useSingleDroneMap.js
+++ b/src/hooks/useSingleDroneMap/useSingleDroneMap.js
@@ -2,25 +2,18 @@
* @Author: shuishen 1109946754@qq.com
* @Date: 2025-04-15 22:41:40
* @LastEditors: shuishen 1109946754@qq.com
- * @LastEditTime: 2025-04-17 15:05:32
+ * @LastEditTime: 2025-04-17 20:24:19
* @FilePath: \command-center-dashboard\src\hooks\useSingleDroneMap\useSingleDroneMap.js
* @Description:
*
* Copyright (c) 2025 by shuishen, All Rights Reserved.
*/
import * as Cesium from 'cesium'
-import { render } from 'vue'
-import endingImg from '@/assets/images/aiNowFly/ending.png'
-import endingHighImg from '@/assets/images/aiNowFly/ending-high.png'
-import EventPopUpBox from '@/hooks/components/EventPopUpBox.vue'
-import eventSingle from '@/assets/images/home/useEventOperate/eventSingle.png'
+import { getEventImage } from '@/utils/stateToImageMap/event'
+import { getDroneFlagImage } from '@/utils/stateToImageMap/drone'
-import eventPending from '@/assets/images/home/useEventOperate/eventPending.png' // 待处理 0
-import eventWaitAudit from '@/assets/images/home/useEventOperate/eventWaitAudit.png' // 待审核 2
-import eventProcessing from '@/assets/images/home/useEventOperate/eventProcessing.png' // 处理中 3
-import eventCompleted from '@/assets/images/home/useEventOperate/eventCompleted.png' // 已完成 4
-import eventClosed from '@/assets/images/home/useEventOperate/eventClosed.png' // 已完结 5
+import { useMapHandlerClick } from '@/hooks/components/useMapHandlerClick'
/**
*
@@ -31,20 +24,18 @@
eventApi: null,
eventApiParams: {}
}) {
- const styleTransform = 'translate(-50%,-110%)'
-
const { eventPositions, eventApi, eventApiParams } = options
- const eventImage = {
- 0: eventPending,
- 2: eventWaitAudit,
- 3: eventProcessing,
- 4: eventCompleted,
- 5: eventClosed
- }
let viewer = null
- let handler = null
- let currentEntity = null
+
+ const {
+ handlerInit: mapHandlerInit,
+ removeAll: removeMapHandlerAll
+ } = useMapHandlerClick({
+ getViewer () {
+ return viewer
+ }
+ })
// 初始化机场位置
const initDroneEntity = (dronePosition) => {
@@ -52,7 +43,7 @@
if (!lng || !lat) return
- const markerImg = status ? endingHighImg : endingImg
+ const markerImg = getDroneFlagImage(status)
const position = Cesium.Cartesian3.fromDegrees(+lng, +lat, 0)
const droneEntity = viewer?.entities.add({
@@ -93,7 +84,7 @@
eventData.length && eventData.forEach((item, index) => {
const { longitude, latitude, status, id } = item
- const curImg = eventImage[status] || eventSingle
+ const curImg = getEventImage(status)
const position = Cesium.Cartesian3.fromDegrees(+longitude, +latitude, 0)
@@ -109,7 +100,7 @@
customData: {
data: {
...item,
- eventId: item.id,
+ eventId: id,
type: 'single-drone-event'
}
}
@@ -155,104 +146,20 @@
})
}
- // 查找特定类型的实体
- const findEntityByType = (entities, type) => {
- return entities.find(entity =>
- entity?.properties?.customData?._value?.data?.type === type
- )
- }
-
- // 左键单机事件
- const singleMachineEvent = async click => {
- currentEntity = null
-
- let clickedEntities = viewer?.scene.drillPick(click.position).map(item => item.id)
-
- if (!clickedEntities?.length) return
-
- currentEntity = findEntityByType(clickedEntities, 'single-drone-event')
-
- removeLabel()
-
- if (currentEntity) {
- viewer?.scene.postRender.addEventListener(labelBoxRender)
- }
- }
-
- // 事件初始化
- const handlerInit = () => {
- if (handler) return
-
- handler = new Cesium.ScreenSpaceEventHandler(viewer?.scene.canvas)
- handler.setInputAction(singleMachineEvent, Cesium.ScreenSpaceEventType.LEFT_CLICK)
- }
-
- // 事件移除
- const removeHandler = () => {
- handler?.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK)
- handler?.destroy()
- handler = null
- }
-
- // 获取弹框box
- const getLabelDom = data => {
- const vNode = h(EventPopUpBox, { data, removeLabel })
- const tooltipContainer = document.createElement('div')
- tooltipContainer.id = 'mapPopUpBox'
- tooltipContainer.style.position = 'absolute'
- tooltipContainer.style.transform = styleTransform
- tooltipContainer.style.pointerEvents = 'none'
- document.querySelector('.page-index').append(tooltipContainer)
- render(vNode, tooltipContainer)
- return tooltipContainer
- }
-
- // 弹框位置刷新
- const labelBoxRender = () => {
- if (!currentEntity) return
- let dom = document.querySelector('#mapPopUpBox')
-
- if (!dom) {
- dom = getLabelDom(currentEntity.properties.customData._value.data)
- }
- const screenPosition = viewer?.scene.cartesianToCanvasCoordinates(currentEntity?.position?._value)
- if (screenPosition) {
- dom.style.left = `${screenPosition.x}px`
- dom.style.top = `${screenPosition.y}px`
- dom.style.display = 'block'
- }
- }
-
- const removeDom = () => {
- const dom = document.querySelector('#mapPopUpBox')
- if (dom && dom.parentNode) {
- dom.parentNode.removeChild(dom)
- }
- }
-
- // 移除弹框标签
- const removeLabel = () => {
- viewer?.scene.postRender.removeEventListener(labelBoxRender)
- removeDom()
- }
-
-
const init = () => {
viewer = window.$viewer
- handlerInit()
+ mapHandlerInit()
}
const removeAll = () => {
removeLayer()
- removeHandler()
- removeLabel()
+ removeMapHandlerAll()
}
- onMounted(() => {
- nextTick(() => {
- init()
- })
+ onMounted(async () => {
+ await nextTick()
+ init()
})
// 自动清理
diff --git a/src/mqtt/index.js b/src/mqtt/index.js
index 896d2a5..8b53f7b 100644
--- a/src/mqtt/index.js
+++ b/src/mqtt/index.js
@@ -65,7 +65,7 @@
// 关闭 mqtt 客户端
destroyed = () => {
- window.console.log('mqtt destroyed')
+ window.console.log('mqtt 销毁')
this._client?.end()
}
diff --git a/src/utils/stateToImageMap/drone.js b/src/utils/stateToImageMap/drone.js
new file mode 100644
index 0000000..192e78b
--- /dev/null
+++ b/src/utils/stateToImageMap/drone.js
@@ -0,0 +1,32 @@
+/*
+ * @Author: shuishen 1109946754@qq.com
+ * @Date: 2025-04-17 20:17:12
+ * @LastEditors: shuishen 1109946754@qq.com
+ * @LastEditTime: 2025-04-17 20:28:08
+ * @FilePath: \command-center-dashboard\src\utils\stateToImageMap\drone.js
+ * @Description:
+ *
+ * Copyright (c) 2025 by shuishen, All Rights Reserved.
+ */
+import endingImg from '@/assets/images/aiNowFly/ending.png'
+import endingHighImg from '@/assets/images/aiNowFly/ending-high.png'
+
+const droneImage = {
+ 'OFFLINE': endingImg,
+ 'WORKING': endingHighImg,
+ 'LEISURE': endingHighImg
+}
+
+/**
+ * 根据机巢状态获取图片
+ * @param {string} status 状态
+ * @returns
+ */
+export const getDroneStatusImage = (status) => droneImage[status] || endingHighImg
+
+/**
+ * 根据机巢状态获取图片
+ * @param {boolean} isOnline 状态
+ * @returns
+ */
+export const getDroneFlagImage = (isOnline) => isOnline ? endingHighImg : endingImg
\ No newline at end of file
diff --git a/src/utils/stateToImageMap/event.js b/src/utils/stateToImageMap/event.js
new file mode 100644
index 0000000..fe6ca7e
--- /dev/null
+++ b/src/utils/stateToImageMap/event.js
@@ -0,0 +1,23 @@
+import eventSingle from '@/assets/images/home/useEventOperate/eventSingle.png'
+
+import eventPending from '@/assets/images/home/useEventOperate/eventPending.png' // 待处理 0
+import eventWaitAudit from '@/assets/images/home/useEventOperate/eventWaitAudit.png' // 待审核 2
+import eventProcessing from '@/assets/images/home/useEventOperate/eventProcessing.png' // 处理中 3
+import eventCompleted from '@/assets/images/home/useEventOperate/eventCompleted.png' // 已完成 4
+import eventClosed from '@/assets/images/home/useEventOperate/eventClosed.png' // 已完结 5
+
+
+const eventImage = {
+ 0: eventPending,
+ 2: eventWaitAudit,
+ 3: eventProcessing,
+ 4: eventCompleted,
+ 5: eventClosed
+}
+
+/**
+ * 根据事件状态获取图片资源
+ * @param {*} status 状态
+ * @returns
+ */
+export const getEventImage = (status) => eventImage[status] || eventSingle
\ No newline at end of file
diff --git a/src/views/Home/SearchBox.vue b/src/views/Home/SearchBox.vue
index 46d23e7..fb3b28c 100644
--- a/src/views/Home/SearchBox.vue
+++ b/src/views/Home/SearchBox.vue
@@ -1,277 +1,281 @@
<template>
- <div class="searchBox" ref="searchBoxRef">
- <div class="searchInput">
- <el-select v-model="optionsValue" @change="optionChange" placeholder="请选择查询">
- <el-option
- v-for="item in options"
- :key="item.value"
- :label="item.label"
- :value="item.value"
- />
- </el-select>
+ <div class="searchBox" ref="searchBoxRef">
+ <div class="searchInput">
+ <el-select v-model="optionsValue" @change="optionChange" placeholder="请选择查询">
+ <el-option v-for="item in options" :key="item.value" :label="item.label" :value="item.value" />
+ </el-select>
- <el-input v-model="searchKey" @focus="handleFocus" placeholder="请输入搜索关键字"></el-input>
- </div>
- <div class="searchBtn" @click="searchClick"></div>
- <div class="region">
- <el-tree-select
- v-model="treeValue"
- check-strictly
- lazy
- :load="load"
- :props="props"
- style="width: 240px"
- @node-click="handleNodeClick"
- />
- </div>
- </div>
- <div class="select-down-list" ref="selectDownRef" v-show="isSelectDown">
- <div class="item" v-for="item in downList" @click="selectedValue(item)">{{ item.nickname || item.name }}</div>
- </div>
+ <el-input
+ v-model="searchKey"
+ @input="handlerInput"
+ @focus="handleFocus"
+ placeholder="请输入搜索关键字"
+ ></el-input>
+ </div>
+ <div class="searchBtn" @click="searchClick"></div>
+ <div class="region">
+ <el-tree-select
+ v-model="treeValue"
+ check-strictly
+ lazy
+ :load="load"
+ :props="props"
+ style="width: 240px"
+ @node-click="handleNodeClick"
+ />
+ </div>
+ </div>
+ <div class="select-down-list" ref="selectDownRef" v-show="isSelectDown">
+ <div class="item" v-for="item in downList" @click="selectedValue(item)">{{ item.nickname || item.name }}</div>
+ </div>
</template>
<script setup>
-import { getRegion } from '@/api/home';
-import { searchByKeyword, selectDeviceList } from '@/api/home/common';
-import { useStore } from 'vuex';
+import _ from 'lodash'
+import { getRegion } from '@/api/home'
+import { searchByKeyword, selectDeviceList } from '@/api/home/common'
+import { useStore } from 'vuex'
import cesiumOperation from '@/utils/cesium-tsa'
const { flyTo } = cesiumOperation()
-const store = useStore();
-const searchKey = ref('');
-const userAreaCode = computed(() => store.state.user.userInfo.detail.areaCode);
-const selectedAreaCode = computed(() => store.state.user.selectedAreaCode);
-const areaValue = ref('江西省');
-const treeValue = ref(userAreaCode.value);
-let first = true;
-const searchBoxRef = ref(null);
-const selectDownRef = ref(null);
+const store = useStore()
+const searchKey = ref('')
+const userAreaCode = computed(() => store.state.user.userInfo.detail.areaCode)
+const selectedAreaCode = computed(() => store.state.user.selectedAreaCode)
+const areaValue = ref('江西省')
+const treeValue = ref(userAreaCode.value)
+let first = true
+const searchBoxRef = ref(null)
+const selectDownRef = ref(null)
function handleNodeClick(data) {
- areaValue.value = data.name;
- store.commit('setSelectedAreaCode', data.code);
+ areaValue.value = data.name
+ store.commit('setSelectedAreaCode', data.code)
}
const props = {
- label: 'name',
- value: 'code',
- children: 'children',
-};
+ label: 'name',
+ value: 'code',
+ children: 'children',
+}
const load = async (node, resolve) => {
- if (first) {
- first = false;
- const res = await getRegion(userAreaCode.value);
- const { provinceCode, provinceName } = res?.data?.data?.[0] || {};
- resolve([{ code: provinceCode, name: provinceName }]);
- return;
- }
- const code = node?.data?.code || userAreaCode.value;
- if ((node?.data?.regionLevel || 0) > 2) return resolve([]);
- getRegion(code).then(res => {
- resolve(res?.data?.data || []);
- });
-};
+ if (first) {
+ first = false
+ const res = await getRegion(userAreaCode.value)
+ const { provinceCode, provinceName } = res?.data?.data?.[0] || {}
+ resolve([{ code: provinceCode, name: provinceName }])
+ return
+ }
+ const code = node?.data?.code || userAreaCode.value
+ if ((node?.data?.regionLevel || 0) > 2) return resolve([])
+ getRegion(code).then(res => {
+ resolve(res?.data?.data || [])
+ })
+}
-const optionsValue = ref('1');
+const optionsValue = ref('1')
const options = [
- {
- value: '1',
- label: '机巢',
- },
- {
- value: '2',
- label: '地址',
- },
-];
+ {
+ value: '1',
+ label: '机巢',
+ },
+ {
+ value: '2',
+ label: '地址',
+ },
+]
-const isSelectDown = ref(false);
-// 下拉数据列表
-const machineNestList = ref([]);
+const isSelectDown = ref(false)
+
// 地址搜索结果
-const downList = ref([]);
-const position = ref({});
+const downList = ref([])
+const position = ref({})
// 获取机巢搜索结果
const getDeviceList = async () => {
- const res = await selectDeviceList({nickname: searchKey.value});
- if (res.data.code !== 0) return;
- machineNestList.value = res?.data?.data || [];
- downList.value = res?.data?.data || [];
-};
+ const res = await selectDeviceList({ nickname: searchKey.value })
+ if (res.data.code !== 0) return
+ downList.value = res?.data?.data || []
+}
// 获取地址搜索结果
const getAddressList = async () => {
- const res = await searchByKeyword(encodeURIComponent(`${areaValue.value}+${searchKey.value}`));
- if (res.data.code !== 0) return;
- downList.value = res?.data?.data.tips || [];
- if (downList.value.length > 0) {
- isSelectDown.value = true;
- }
-};
+ const res = await searchByKeyword(encodeURIComponent(`${areaValue.value}+${searchKey.value}`))
+ if (res.data.code !== 0) return
+ downList.value = res?.data?.data.tips || []
+ if (downList.value.length > 0) {
+ isSelectDown.value = true
+ }
+}
// 搜索结果
const searchClick = () => {
const longitude = Number(position.value.longitude)
const latitude = Number(position.value.latitude)
flyTo({ longitude, latitude }, 1, 1000)
-};
+}
// 地址和机巢的切换
const optionChange = () => {
- searchKey.value = '';
- downList.value = [];
-};
+ searchKey.value = ''
+ downList.value = []
+
+ inputSelect()
+}
+
+// input对应下拉数据初始化
+const inputSelect = () => {
+ if (optionsValue.value === '2') {
+ getAddressList()
+ } else {
+ getDeviceList()
+ }
+}
+
+inputSelect()
+
+// 输入框input事件
+const handlerInput = _.debounce(inputSelect, 1000)
+
// 输入框获取焦点
const handleFocus = () => {
- if (optionsValue.value === '1') {
- isSelectDown.value = true;
- downList.value = machineNestList.value;
- }
-};
+ isSelectDown.value = true
+}
// 机巢下拉获取值
const selectedValue = item => {
- searchKey.value = item.nickname || item.name;
- if (optionsValue.value === '1') {
- position.value = item;
- } else {
- position.value = { longitude: item.location.split(',')[0], latitude: item.location.split(',')[1] };
- }
-
- isSelectDown.value = false;
-};
+ searchKey.value = item.nickname || item.name
+ if (optionsValue.value === '1') {
+ position.value = item
+ } else {
+ position.value = { longitude: item.location.split(',')[0], latitude: item.location.split(',')[1] }
+ }
-// 监听搜索关键字变化
-watch(searchKey, async (newVal) => {
- if (optionsValue.value === '2') {
- await getAddressList();
- } else {
- await getDeviceList();
- }
-}, { immediate: false });
+ isSelectDown.value = false
+}
onMounted(() => {
- document.addEventListener('click', handleClickOutside);
-});
+ document.addEventListener('click', handleClickOutside)
+})
onUnmounted(() => {
- document.removeEventListener('click', handleClickOutside);
-});
+ document.removeEventListener('click', handleClickOutside)
+})
-const handleClickOutside = (event) => {
- if (!searchBoxRef.value?.contains(event.target) && !selectDownRef.value?.contains(event.target)) {
- isSelectDown.value = false;
- }
-};
+const handleClickOutside = event => {
+ if (!searchBoxRef.value?.contains(event.target) && !selectDownRef.value?.contains(event.target)) {
+ isSelectDown.value = false
+ }
+}
</script>
<style scoped lang="scss">
.select-down-list {
- position: absolute;
- top: 188px;
- left: 44%;
- transform: translateX(-40%);
- width: 220px;
- height: 256px;
- overflow-y: auto;
- background: linear-gradient( 180deg, #0D3556 0%, #012350 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;
- opacity: 0.8;
- &::-webkit-scrollbar {
- width: 0;
- display: none;
- }
- -ms-overflow-style: none; /* IE and Edge */
- scrollbar-width: none; /* Firefox */
- .item {
- color: #FFFFFF;
- height: 32px;
- line-height: 32px;
- text-align: center;
- cursor: pointer;
- &:hover {
- background: linear-gradient( 90deg, rgba(0,122,255,0) 0%, rgba(0,98,204,0.6) 50%, rgba(0,73,153,0) 100%);
- border: 1px solid;
- border-image: linear-gradient(90deg, rgba(0, 199, 190, 0), rgba(48, 176, 199, 1), rgba(0, 199, 190, 0)) 1 1;
- }
- }
+ position: absolute;
+ top: 188px;
+ left: 44%;
+ transform: translateX(-40%);
+ width: 220px;
+ height: 256px;
+ overflow-y: auto;
+ background: linear-gradient(180deg, #0d3556 0%, #012350 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;
+ opacity: 0.8;
+ &::-webkit-scrollbar {
+ width: 0;
+ display: none;
+ }
+ -ms-overflow-style: none; /* IE and Edge */
+ scrollbar-width: none; /* Firefox */
+ .item {
+ color: #ffffff;
+ height: 32px;
+ line-height: 32px;
+ text-align: center;
+ cursor: pointer;
+ &:hover {
+ background: linear-gradient(90deg, rgba(0, 122, 255, 0) 0%, rgba(0, 98, 204, 0.6) 50%, rgba(0, 73, 153, 0) 100%);
+ border: 1px solid;
+ border-image: linear-gradient(90deg, rgba(0, 199, 190, 0), rgba(48, 176, 199, 1), rgba(0, 199, 190, 0)) 1 1;
+ }
+ }
}
.searchBox {
- width: 420px;
- height: 43px;
- position: absolute;
- top: 145px;
- left: 50%;
- transform: translateX(-50%);
- display: flex;
+ width: 420px;
+ height: 43px;
+ position: absolute;
+ top: 145px;
+ left: 50%;
+ transform: translateX(-50%);
+ display: flex;
- .el-select {
- width: 130px;
- height: 100%;
+ .el-select {
+ width: 130px;
+ height: 100%;
- :deep() {
- .el-select__wrapper {
- background: transparent;
- border: none;
- box-shadow: none;
- height: 100%;
- padding-left: 20px;
- }
+ :deep() {
+ .el-select__wrapper {
+ background: transparent;
+ border: none;
+ box-shadow: none;
+ height: 100%;
+ padding-left: 20px;
+ }
- .el-select__selected-item {
- font-family: Source Han Sans CN, Source Han Sans CN, serif;
- font-weight: 400;
- font-size: 14px;
- color: #ffffff;
- line-height: 18px;
- }
- }
- }
+ .el-select__selected-item {
+ font-family: Source Han Sans CN, Source Han Sans CN, serif;
+ font-weight: 400;
+ font-size: 14px;
+ color: #ffffff;
+ line-height: 18px;
+ }
+ }
+ }
- .searchInput {
- width: 243px;
- height: 100%;
- background: url('@/assets/images/home/searchBox/searchBg1.png') no-repeat center / 100% 100%;
- display: flex;
+ .searchInput {
+ width: 243px;
+ height: 100%;
+ background: url('@/assets/images/home/searchBox/searchBg1.png') no-repeat center / 100% 100%;
+ display: flex;
- .el-input {
- height: 100%;
+ .el-input {
+ height: 100%;
- :deep() {
- .el-input__wrapper {
- background: transparent;
- border: none;
- box-shadow: none;
- height: 100%;
- }
+ :deep() {
+ .el-input__wrapper {
+ background: transparent;
+ border: none;
+ box-shadow: none;
+ height: 100%;
+ }
- .el-input__inner {
- font-family: Source Han Sans CN, Source Han Sans CN, serif;
- font-weight: 400;
- font-size: 14px;
- color: #ffffff;
- line-height: 18px;
- }
- }
- }
- }
+ .el-input__inner {
+ font-family: Source Han Sans CN, Source Han Sans CN, serif;
+ font-weight: 400;
+ font-size: 14px;
+ color: #ffffff;
+ line-height: 18px;
+ }
+ }
+ }
+ }
- .searchBtn {
- width: 67px;
- height: 100%;
- background: url('@/assets/images/home/searchBox/searchBg2.png') no-repeat center / 100% 100%;
- cursor: pointer;
- }
+ .searchBtn {
+ width: 67px;
+ height: 100%;
+ background: url('@/assets/images/home/searchBox/searchBg2.png') no-repeat center / 100% 100%;
+ cursor: pointer;
+ }
- .region {
- width: 0;
- flex-grow: 1;
- background: url('@/assets/images/home/searchBox/searchBg3.png') no-repeat center / 100% 100%;
+ .region {
+ width: 0;
+ flex-grow: 1;
+ background: url('@/assets/images/home/searchBox/searchBg3.png') no-repeat center / 100% 100%;
- :deep() {
- .el-select__wrapper {
- width: 100px;
- padding-left: 20px;
- }
- }
- }
+ :deep() {
+ .el-select__wrapper {
+ width: 100px;
+ padding-left: 20px;
+ }
+ }
+ }
}
</style>
diff --git a/src/views/SignMachineNest/MachineLeft/InspectionRaskDetails.vue b/src/views/SignMachineNest/MachineLeft/InspectionRaskDetails.vue
index 98c6ac5..2c24401 100644
--- a/src/views/SignMachineNest/MachineLeft/InspectionRaskDetails.vue
+++ b/src/views/SignMachineNest/MachineLeft/InspectionRaskDetails.vue
@@ -1,228 +1,230 @@
<!-- 任务事件概况 -->
<template>
- <common-title title="任务事件概况" :style="{ marginLeft: pxToRem(14) }"></common-title>
- <div class="inspection-rask-details">
- <CommonDateTime :style="{ top: pxToRem(14),marginLeft: pxToRem(10) }" v-model="newTime" @change="getData"></CommonDateTime>
- <div class="chart-container" ref="chartRef"></div>
- <div class="pie-container" ref="pieRef"></div>
- </div>
+ <common-title title="任务事件概况" :style="{ marginLeft: pxToRem(14) }"></common-title>
+ <div class="inspection-rask-details">
+ <CommonDateTime
+ :style="{ top: pxToRem(14), marginLeft: pxToRem(10) }"
+ v-model="newTime"
+ @change="getData"
+ ></CommonDateTime>
+ <div class="chart-container" ref="chartRef"></div>
+ <div class="pie-container" ref="pieRef"></div>
+ </div>
</template>
<script setup>
-import * as echarts from 'echarts';
-import dayjs from 'dayjs';
-import { pxToRem } from '@/utils/rem';
-import CommonTitle from '@/components/CommonTitle.vue';
-import CommonDateTime from '@/components/CommonDateTime.vue';
+import * as echarts from 'echarts'
+import dayjs from 'dayjs'
+import { pxToRem } from '@/utils/rem'
+import CommonTitle from '@/components/CommonTitle.vue'
+import CommonDateTime from '@/components/CommonDateTime.vue'
import { jobNumBar, eventNumPie } from '@/api/home'
import useEchartsResize from '@/hooks/useEchartsResize'
+import { useStore } from 'vuex'
+const store = useStore()
+// 单个机巢信息
+const singleUavHome = computed(() => store.state.home.singleUavHome)
// 日期
-const currenDate = dayjs().format('YYYY-MM-DD');
-const newTime = ref([currenDate, currenDate]);
+const currenDate = dayjs().format('YYYY-MM-DD')
+const newTime = ref([currenDate, currenDate])
// 统计图
-const chartRef = ref(null);
+const chartRef = ref(null)
let { chart } = useEchartsResize(chartRef)
-const pieRef = ref(null);
+const pieRef = ref(null)
let { chart: pieChart } = useEchartsResize(pieRef)
-
const option = {
- tooltip: {
- trigger: 'axis',
- axisPointer: {
- type: 'shadow'
- }
- },
- grid: {
- top: '15%',
- left: '3%',
- right: '4%',
- bottom: '3%',
- containLabel: true
- },
- legend: {
- data: ['任务', '事件'],
- textStyle: {
- color: '#fff'
- }
- },
- xAxis: {
- type: 'category',
- // data: ['1月', '2月', '3月', '4月', '5月', '6月'],
- axisLine: {
- lineStyle: {
- color: '#fff'
- }
- },
- axisLabel: {
- color: '#fff'
- }
- },
- yAxis: {
- type: 'value',
- axisLine: {
- lineStyle: {
- color: '#fff'
- }
- },
- splitLine: {
- lineStyle: {
- color: 'rgba(255, 255, 255, 0.1)'
- }
- },
- axisLabel: {
- color: '#fff'
- }
- },
- series: [
- {
- name: '任务',
- type: 'bar',
- barWidth: '20%',
- // data: [10, 15, 20, 25, 30, 35],
- itemStyle: {
- color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
- { offset: 0, color: '#1EE7E7' },
- { offset: 1, color: 'rgba(30, 231, 231, 0.1)' }
- ])
- }
- },
- {
- name: '事件',
- type: 'bar',
- barWidth: '20%',
- // data: [15, 20, 25, 30, 35, 40],
- itemStyle: {
- color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
- { offset: 0, color: '#0070FF' },
- { offset: 1, color: 'rgba(0, 112, 255, 0.1)' }
- ])
- }
- }
- ]
-};
+ tooltip: {
+ trigger: 'axis',
+ axisPointer: {
+ type: 'shadow',
+ },
+ },
+ grid: {
+ top: '15%',
+ left: '3%',
+ right: '4%',
+ bottom: '3%',
+ containLabel: true,
+ },
+ legend: {
+ data: ['任务', '事件'],
+ textStyle: {
+ color: '#fff',
+ },
+ },
+ xAxis: {
+ type: 'category',
+ // data: ['1月', '2月', '3月', '4月', '5月', '6月'],
+ axisLine: {
+ lineStyle: {
+ color: '#fff',
+ },
+ },
+ axisLabel: {
+ color: '#fff',
+ },
+ },
+ yAxis: {
+ type: 'value',
+ axisLine: {
+ lineStyle: {
+ color: '#fff',
+ },
+ },
+ splitLine: {
+ lineStyle: {
+ color: 'rgba(255, 255, 255, 0.1)',
+ },
+ },
+ axisLabel: {
+ color: '#fff',
+ },
+ },
+ series: [
+ {
+ name: '任务',
+ type: 'bar',
+ barWidth: '20%',
+ // data: [10, 15, 20, 25, 30, 35],
+ itemStyle: {
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+ { offset: 0, color: '#1EE7E7' },
+ { offset: 1, color: 'rgba(30, 231, 231, 0.1)' },
+ ]),
+ },
+ },
+ {
+ name: '事件',
+ type: 'bar',
+ barWidth: '20%',
+ // data: [15, 20, 25, 30, 35, 40],
+ itemStyle: {
+ color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
+ { offset: 0, color: '#0070FF' },
+ { offset: 1, color: 'rgba(0, 112, 255, 0.1)' },
+ ]),
+ },
+ },
+ ],
+}
// chart.setOption(option);
const pieOption = {
- tooltip: {
- trigger: 'item',
- formatter: '{b}: {c} ({d}%)'
- },
- legend: {
- orient: 'vertical',
- right: '5%',
- top: 'middle',
- textStyle: {
- color: '#fff',
- fontSize: 12
- },
- itemWidth: 10,
- itemHeight: 10,
- itemGap: 10,
- formatter: function(name) {
- let data = pieOption.series[0].data;
- let total = 0;
- let tarValue = 0;
- for (let i = 0; i < data.length; i++) {
- total += data[i].value;
- if (data[i].name === name) {
- tarValue = data[i].value;
- }
- }
- let percentage = ((tarValue / total) * 100).toFixed(1);
- return `${name} ${percentage}%`;
- }
- },
- series: [
- {
- name: '任务状态',
- type: 'pie',
- radius: ['10%', '90%'],
- center: ['35%', '50%'], // 将图表向左偏移
- roseType: 'radius',
- itemStyle: {
- borderRadius: 4
- },
- label: {
- show: true,
- position: 'inside',
- formatter: '{c}%',
- fontSize: 12,
- 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: '#E9C81A' } },
- { value: 0, name: '处理中', itemStyle: { color: '#0FC1E8' } },
- { value: 0, name: '已完结', itemStyle: { color: '#FE577F' } }
- ]
- }
- ]
-};
+ tooltip: {
+ trigger: 'item',
+ formatter: '{b}: {c} ({d}%)',
+ },
+ legend: {
+ orient: 'vertical',
+ right: '5%',
+ top: 'middle',
+ textStyle: {
+ color: '#fff',
+ fontSize: 12,
+ },
+ itemWidth: 10,
+ itemHeight: 10,
+ itemGap: 10,
+ formatter: function (name) {
+ let data = pieOption.series[0].data
+ let total = 0
+ let tarValue = 0
+ for (let i = 0; i < data.length; i++) {
+ total += data[i].value
+ if (data[i].name === name) {
+ tarValue = data[i].value
+ }
+ }
+ let percentage = ((tarValue / total) * 100).toFixed(1)
+ return `${name} ${percentage}%`
+ },
+ },
+ series: [
+ {
+ name: '任务状态',
+ type: 'pie',
+ radius: ['10%', '90%'],
+ center: ['35%', '50%'], // 将图表向左偏移
+ roseType: 'radius',
+ itemStyle: {
+ borderRadius: 4,
+ },
+ label: {
+ show: true,
+ position: 'inside',
+ formatter: '{c}%',
+ fontSize: 12,
+ 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: '#E9C81A' } },
+ { value: 0, name: '处理中', itemStyle: { color: '#0FC1E8' } },
+ { value: 0, name: '已完结', itemStyle: { color: '#FE577F' } },
+ ],
+ },
+ ],
+}
// TODAY,CURRENT_WEEK,CURRENT_MONTH,CURRENT_YEAR
-const getData = (value,date_enum) => {
- const params = {
- device_sn: '',
- date_enum,
- // start_date: newTime.value[0],
- // end_date: newTime.value[1]
- };
- // 获取柱状图
- jobNumBar(params).then(res => {
- if (res.data.code !== 0) return;
- option.xAxis.data = res.data?.data.map(item => item.name);
- option.series[0].data = res.data?.data.map(item => item.data[0].value);
- option.series[1].data = res.data?.data.map(item => item.data[1].value);
- chart.value.setOption(option);
- })
- // 获取饼状图
- eventNumPie(params).then(res => {
- if (res.data.code!== 0) return;
- // 更新饼图数据
- const updatedPieData = pieOption.series[0].data.map(item => {
- const matchedData = res.data?.data.find(dataItem => dataItem.name === item.name);
- return {
- ...item,
- value: matchedData ? matchedData.value : item.value
- };
- });
- pieOption.series[0].data = updatedPieData;
- pieChart.value.setOption(pieOption);
- })
-};
+const getData = (value, date_enum) => {
+ const params = {
+ device_sn: singleUavHome.value.device_sn,
+ date_enum,
+ // start_date: newTime.value[0],
+ // end_date: newTime.value[1]
+ }
+ // 获取柱状图
+ jobNumBar(params).then(res => {
+ if (res.data.code !== 0) return
+ option.xAxis.data = res.data?.data.map(item => item.name)
+ option.series[0].data = res.data?.data.map(item => item.data[0].value)
+ option.series[1].data = res.data?.data.map(item => item.data[1].value)
+ chart.value.setOption(option)
+ })
+ // 获取饼状图
+ eventNumPie(params).then(res => {
+ if (res.data.code !== 0) return
+ // 更新饼图数据
+ const updatedPieData = pieOption.series[0].data.map(item => {
+ const matchedData = res.data?.data.find(dataItem => dataItem.name === item.name)
+ return {
+ ...item,
+ value: matchedData ? matchedData.value : item.value,
+ }
+ })
+ pieOption.series[0].data = updatedPieData
+ pieChart.value.setOption(pieOption)
+ })
+}
onMounted(() => {
- getData(newTime.value, 'TODAY');
-});
+ getData(newTime.value, 'TODAY')
+})
</script>
<style lang="scss" scoped>
.inspection-rask-details {
- margin-left: 29px;
- // padding: 16px 16px;
- width: 390px;
- height: 526px;
- background: linear-gradient(
- 270deg,
- rgba(31, 62, 122, 0) 0%,
- rgba(31, 62, 122, 0.35) 21%,
- #1f3e7a 100%
- );
- border-radius: 0px 0px 0px 0px;
- opacity: 0.85;
- .chart-container {
- width: 100%;
- height: 220px;
- margin-top: 20px;
- }
- .pie-container {
- width: 100%;
- height: 212px;
- margin-top: 10px;
- }
+ margin-left: 29px;
+ // padding: 16px 16px;
+ width: 390px;
+ height: 526px;
+ background: linear-gradient(270deg, rgba(31, 62, 122, 0) 0%, rgba(31, 62, 122, 0.35) 21%, #1f3e7a 100%);
+ border-radius: 0px 0px 0px 0px;
+ opacity: 0.85;
+ .chart-container {
+ width: 100%;
+ height: 220px;
+ margin-top: 20px;
+ }
+ .pie-container {
+ width: 100%;
+ height: 212px;
+ margin-top: 10px;
+ }
}
</style>
diff --git a/src/views/SignMachineNest/MachineRight/MachineMonitor.vue b/src/views/SignMachineNest/MachineRight/MachineMonitor.vue
index a0b30b8..ef582ce 100644
--- a/src/views/SignMachineNest/MachineRight/MachineMonitor.vue
+++ b/src/views/SignMachineNest/MachineRight/MachineMonitor.vue
@@ -1,81 +1,90 @@
+<!--
+ * @Author: shuishen 1109946754@qq.com
+ * @Date: 2025-04-15 17:19:35
+ * @LastEditors: shuishen 1109946754@qq.com
+ * @LastEditTime: 2025-04-17 21:16:50
+ * @FilePath: \command-center-dashboard\src\views\SignMachineNest\MachineRight\MachineMonitor.vue
+ * @Description:
+ *
+ * Copyright (c) 2025 by shuishen, All Rights Reserved.
+-->
<!-- 机巢监控 -->
<template>
- <CommonTitle title="直播监控" />
- <div :style="{ marginLeft: pxToRem(14) }">
- <div class="machine-monitor">
- <LiveVideo :videoUrl="airPortUrl" />
- </div>
- </div>
+ <CommonTitle title="直播监控" />
+ <div :style="{ marginLeft: pxToRem(14) }">
+ <div class="machine-monitor">
+ <LiveVideo :videoUrl="airPortUrl" />
+ </div>
+ </div>
</template>
<script setup>
-import CommonTitle from '@/components/CommonTitle.vue';
-import LiveVideo from '@/components/LiveVideo.vue';
-import { liveStart } from '@/api/home/machineNest';
+import CommonTitle from '@/components/CommonTitle.vue'
+import LiveVideo from '@/components/LiveVideo.vue'
+import { liveStart } from '@/api/home/machineNest'
// import { CURRENT_CONFIG as config } from '@/utils/http/config'
-import { useStore } from 'vuex';
+import { useStore } from 'vuex'
-
-const store = useStore();
+const store = useStore()
// 单个机巢信息
-const singleUavHome = computed(() => store.state.home.singleUavHome);
+const singleUavHome = computed(() => store.state.home.singleUavHome)
// 直播地址
-let airPortUrl = ref('');
+let airPortUrl = ref('')
// 获取直播地址
-const getVideoUrl = (sn,quality) => {
- liveStart(sn, quality).then(res => {
- if (res.data.code !== 0) return;
- airPortUrl.value = res.data.data.rtcs_url;
- });
-};
-let isCurrentSn = ref(false);
-let CurrentSn = ref('');
+const getVideoUrl = (sn, quality) => {
+ liveStart(sn, quality).then(res => {
+ if (res.data.code !== 0) return
+ airPortUrl.value = res.data.data.rtcs_url
+ })
+}
+let isCurrentSn = ref(false)
+let CurrentSn = ref('')
// 监听ws消息
watch(
- () => store.state.home.deviceState.deviceInfo,
- (newValue) => {
- CurrentSn.value = Object.keys(newValue)[0];
- const currentDevice = newValue[CurrentSn.value];
- if (currentDevice && currentDevice?.mode_code > 0) {
- isCurrentSn.value = true;
- } else if (currentDevice && currentDevice?.mode_code === 14) {
- isCurrentSn.value = false;
- } else {
- isCurrentSn.value = false;
- }
- },
- {
- immediate: true,
- deep: true,
- }
-);
+ () => store.state.home.deviceState.deviceInfo,
+ newValue => {
+ CurrentSn.value = Object.keys(newValue)[0]
+ const currentDevice = newValue[CurrentSn.value]
+ if (currentDevice && currentDevice?.mode_code > 0) {
+ isCurrentSn.value = true
+ } else if (currentDevice && currentDevice?.mode_code === 14) {
+ isCurrentSn.value = false
+ } else {
+ isCurrentSn.value = false
+ }
+ },
+ {
+ immediate: true,
+ deep: true,
+ }
+)
// 监听 isCurrentSn
-watch(isCurrentSn, (newVal) => {
- if (newVal) {
- getVideoUrl(CurrentSn.value, 2);
- } else {
- getVideoUrl(singleUavHome.value.device_sn,1);
- }
-}, { immediate: true,deep: true });
+watch(
+ isCurrentSn,
+ newVal => {
+ if (newVal) {
+ getVideoUrl(CurrentSn.value, 2)
+ } else {
+ getVideoUrl(singleUavHome.value.device_sn, 1)
+ }
+ },
+ { immediate: true, deep: true }
+)
+
onMounted(() => {
- getVideoUrl(singleUavHome.value.device_sn,1);
-});
+ // getVideoUrl(singleUavHome.value.device_sn,1);
+})
</script>
<style lang="scss" scoped>
- .machine-monitor {
- width: 390px;
- height: 228px;
- 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;
- display: flex;
- justify-content: space-between;
- padding: 12px 10px 0;
- }
+.machine-monitor {
+ width: 390px;
+ height: 228px;
+ 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;
+ display: flex;
+ justify-content: space-between;
+ padding: 12px 10px 0;
+}
</style>
diff --git a/src/views/SignMachineNest/SignMachineNest.vue b/src/views/SignMachineNest/SignMachineNest.vue
index 2c73acd..33c48e8 100644
--- a/src/views/SignMachineNest/SignMachineNest.vue
+++ b/src/views/SignMachineNest/SignMachineNest.vue
@@ -19,7 +19,6 @@
let connectWs = ref(null)
// 单个机巢信息
const singleUavHome = computed(() => store.state.home.singleUavHome)
-const selectedAreaCode = computed(() => store.state.user.selectedAreaCode)
let osdVisible = ref({})
--
Gitblit v1.9.3