forked from drone/command-center-dashboard

chenyao
2025-04-18 dfac8531d4ed65183ebffcbc7f88aea16f6ac64b
Merge branch 'master' of http://139.196.74.78:10010/r/drone/command-center-dashboard
20 files modified
1 files renamed
1648 ■■■■■ changed files
src/api/payload.js 12 ●●●●● patch | view | raw | blame | history
src/assets/images/home/useEventOperate/point-active.png patch | view | raw | blame | history
src/assets/images/home/useEventOperate/point.png patch | view | raw | blame | history
src/components/CurrentTaskDetails/ControlPanel/ControlComPass/ControlComPass.vue 6 ●●●● patch | view | raw | blame | history
src/components/CurrentTaskDetails/ControlPanel/ControlPanel.vue 117 ●●●● patch | view | raw | blame | history
src/components/CurrentTaskDetails/CurrentTaskDetails.vue 55 ●●●●● patch | view | raw | blame | history
src/components/CurrentTaskDetails/RealTimeMap.vue 45 ●●●● patch | view | raw | blame | history
src/components/CurrentTaskDetails/TaskDetailsHead.vue 176 ●●●● patch | view | raw | blame | history
src/components/CurrentTaskDetails/TaskDetailsLeft.vue 9 ●●●●● patch | view | raw | blame | history
src/const/drc.js 3 ●●●●● patch | view | raw | blame | history
src/hooks/controlDrone/useManualControl.js 9 ●●●●● patch | view | raw | blame | history
src/hooks/controlDrone/useMqtt.js 1 ●●●● patch | view | raw | blame | history
src/page/login/index.vue 2 ●●● patch | view | raw | blame | history
src/styles/element-ui.scss 136 ●●●●● patch | view | raw | blame | history
src/views/Home/EventOverviewDetail/EventOverviewDetailRight.vue 268 ●●●●● patch | view | raw | blame | history
src/views/Home/HomeLeft/InspectionRaskDetails/InspectionRaskDetailsDialog.vue 59 ●●●● patch | view | raw | blame | history
src/views/Home/HomeLeft/OverviewNext.vue 5 ●●●●● patch | view | raw | blame | history
src/views/Home/HomeRight/EventOverview.vue 695 ●●●● patch | view | raw | blame | history
src/views/TaskManage/SearchBox.vue 17 ●●●● patch | view | raw | blame | history
src/views/TaskManage/TaskIntermediateContent/TaskIntermediateContent.vue 29 ●●●● patch | view | raw | blame | history
src/views/TaskManage/components/TaskAlgorithmBusiness.vue 4 ●●●● patch | view | raw | blame | history
src/api/payload.js
@@ -44,7 +44,7 @@
// 拍照和录像
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',
  })
}
@@ -132,4 +132,14 @@
};
// 无人机开启ai
export const getLiveAiLinkApi = (data) => {
  return request({
    url:`/drone-device-core/manage/api/v1/live/streams/start/ai`,
    method: 'post',
    data,
  });
};
src/assets/images/home/useEventOperate/point-active.png

src/assets/images/home/useEventOperate/point.png

src/components/CurrentTaskDetails/ControlPanel/ControlComPass/ControlComPass.vue
File was renamed from src/components/CurrentTaskDetails/ControlComPass/ControlComPass.vue
@@ -3,7 +3,7 @@
    <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">
@@ -14,13 +14,13 @@
        </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>
src/components/CurrentTaskDetails/ControlPanel/ControlPanel.vue
@@ -126,7 +126,7 @@
    </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'
@@ -144,12 +144,13 @@
    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')
@@ -183,18 +184,106 @@
    { 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: '',
src/components/CurrentTaskDetails/CurrentTaskDetails.vue
@@ -47,21 +47,29 @@
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'])
@@ -70,19 +78,27 @@
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
}
// 无人机直播画质切换
@@ -113,14 +129,41 @@
    })
}
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:
@@ -140,6 +183,8 @@
onMounted(() => {
    getTaskDetails()
    EventBus.on('CurrentTaskDetails-timeStop', changeLineQuality)
    EventBus.on('CurrentTaskDetails-getAiLiveUrl', getAiLiveUrl)
    EventBus.on('CurrentTaskDetails-getDroneLiveUrl', getDroneLiveUrl)
})
onBeforeUnmount(() => {
@@ -147,6 +192,8 @@
    deviceOsdInfo.value = {}
    droneWebSocket = null
    EventBus.off('CurrentTaskDetails-timeStop', changeLineQuality)
    EventBus.off('CurrentTaskDetails-getAiLiveUrl', getAiLiveUrl)
    EventBus.off('CurrentTaskDetails-getDroneLiveUrl', getDroneLiveUrl)
})
</script>
src/components/CurrentTaskDetails/RealTimeMap.vue
@@ -26,12 +26,14 @@
    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, // 左下角的动画仪表盘
@@ -45,17 +47,17 @@
        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),
    })
}
@@ -66,7 +68,7 @@
        return Cartesian3.fromDegrees(Number(lon), Number(lat))
    })
    // 起点
    viewer.entities.add({
    taskDetailsViewer?.value.entities.add({
        position: positions[0],
        billboard: {
            image: new Cesium.ConstantProperty(rwqfdImg),
@@ -75,7 +77,7 @@
        },
    })
    // 终点
    viewer.entities.add({
    taskDetailsViewer?.value.entities.add({
        position: positions[positions.length - 1],
        billboard: {
            image: new Cesium.ConstantProperty(endPointImg),
@@ -85,7 +87,7 @@
        },
    })
    // 路径线
    viewer.entities.add({
    taskDetailsViewer?.value.entities.add({
        polyline: {
            width: 4,
            positions: positions,
@@ -108,29 +110,28 @@
    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,
src/components/CurrentTaskDetails/TaskDetailsHead.vue
@@ -1,17 +1,19 @@
<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>
@@ -19,26 +21,117 @@
<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">
@@ -48,7 +141,7 @@
    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;
@@ -67,6 +160,10 @@
        color: #ededed;
        text-align: center;
        line-height: 42px;
        white-space: nowrap;
        overflow: hidden;
        text-overflow: ellipsis;
        padding: 0 10px;
    }
    .infoListBox {
@@ -74,21 +171,30 @@
        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;
            }
        }
    }
@@ -98,7 +204,7 @@
        align-items: center;
        justify-content: space-between;
        .refresh{
        .refresh {
            color: white;
            font-size: 30px;
            cursor: pointer;
src/components/CurrentTaskDetails/TaskDetailsLeft.vue
@@ -143,7 +143,7 @@
// 修改相机参数
function cameraParamsChange() {
    const { camera_type, zoom_factor} = cameraParams.value
    const { camera_type, zoom_factor } = cameraParams.value
    cameraParamsChangeApi({
        sn: dockSn.value,
        ...cameraParams.value,
@@ -176,7 +176,7 @@
    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
@@ -185,8 +185,11 @@
// 拍照
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('拍照成功')
    })
}
src/const/drc.js
@@ -7,3 +7,6 @@
    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: '好' }
src/hooks/controlDrone/useManualControl.js
@@ -38,7 +38,7 @@
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},
@@ -281,11 +281,10 @@
        { immediate: true }
    )
    onBeforeUnmount(()=>{
        mqttHooks?.d
    })
    onUnmounted(() => {
        deviceTopicInfo.subTopic = ''
        deviceTopicInfo.pubTopic = ''
        mqttHooks = null
        closeKeyboardManualControl()
    })
src/hooks/controlDrone/useMqtt.js
@@ -67,6 +67,7 @@
      // 2.发心跳
      publishDrcPing(deviceTopicInfo.sn)
    } else {
      console.log('清除pingInterval')
      clearInterval(state.heartState.get(deviceTopicInfo.sn)?.pingInterval)
      state.heartState.delete(deviceTopicInfo.sn)
      heartBeatSeq.value = 0
src/page/login/index.vue
@@ -3,7 +3,7 @@
    <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>
src/styles/element-ui.scss
@@ -236,9 +236,68 @@
  .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;
@@ -254,6 +313,83 @@
      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{
src/views/Home/EventOverviewDetail/EventOverviewDetailRight.vue
@@ -1,9 +1,15 @@
<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"
@@ -50,7 +56,7 @@
                    <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>
@@ -66,12 +72,7 @@
            <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> -->
@@ -96,23 +97,32 @@
            </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>
@@ -159,7 +169,7 @@
    size: 8,
})
const getSmallImg = (url) => {
const getSmallImg = url => {
    return url ? url.substring(0, url.lastIndexOf('.')) + '_small' + url.substring(url.lastIndexOf('.')) : ''
}
@@ -195,16 +205,16 @@
    })
}
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' },
@@ -241,7 +251,7 @@
    params.value.start_date = start_date
    params.value.end_date = end_date
    // 提交数据,更新聚合
    store.commit('setEventTimeRang', timeArr.value);
    store.commit('setEventTimeRang', timeArr.value)
    // 重置
    getEventStatusNumFun()
    getEventList()
@@ -263,49 +273,53 @@
    }
    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()
@@ -340,11 +354,7 @@
    .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;
@@ -384,7 +394,14 @@
                // 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 {
@@ -531,30 +548,46 @@
    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%;
@@ -562,43 +595,80 @@
            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;
            }
        }
    }
}
src/views/Home/HomeLeft/InspectionRaskDetails/InspectionRaskDetailsDialog.vue
@@ -20,19 +20,19 @@
                    <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"
@@ -43,6 +43,7 @@
                </el-form-item>
                <el-form-item>
                    <el-date-picker
                        popper-class="custom-date-picker"
                        class="ztzf-date-picker"
                        v-model="dateRange"
                        type="daterange"
@@ -88,7 +89,9 @@
                                    ? 'finish '
                                    : scope.row.status === 5
                                    ? 'fail '
                                    : ' '">
                                    : ' '
                            "
                        >
                            {{
                                scope.row.status === 1
                                    ? '待执行'
@@ -110,7 +113,11 @@
                        <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>
        <!-- 分页 -->
@@ -128,6 +135,18 @@
            />
        </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'
@@ -135,6 +154,8 @@
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({
@@ -148,7 +169,7 @@
const statusOptions = [
    { label: '待执行', value: 1 },
    { label: '执行中', value: 2 },
    { label: '已完成', value: 3 },
    { label: '已执行', value: 3 },
    { label: '执行失败', value: 5 },
]
@@ -215,7 +236,6 @@
}
// 获取任务列表
const getJobList = () => {
    // 事件状态:0 =待处理,1=待分拨,2=待处理,3=处理中,4=已完成 5=已完结
    jobList(taskDetailParams).then(res => {
        if (res.data.code !== 0) return
        taskDetailData.value = res.data.data.records
@@ -251,12 +271,35 @@
        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;
@@ -331,7 +374,7 @@
}
// 执行中
.distributed {
    color: #FFA768;
    color: #ffa768;
}
// 已执行
.finish {
src/views/Home/HomeLeft/OverviewNext.vue
@@ -155,11 +155,11 @@
            margin-bottom: 13px;
            .name {
                margin-left: 46px;
            }
        }
        .table-body {
            height: 190px;
            overflow: auto;
            &::-webkit-scrollbar {
                width: 0;
@@ -211,8 +211,7 @@
            }
            .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;
src/views/Home/HomeRight/EventOverview.vue
@@ -1,420 +1,415 @@
<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>
src/views/TaskManage/SearchBox.vue
@@ -6,17 +6,18 @@
          <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"
@@ -43,12 +44,12 @@
          <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>
@@ -208,7 +209,13 @@
  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;
src/views/TaskManage/TaskIntermediateContent/TaskIntermediateContent.vue
@@ -20,10 +20,10 @@
        <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>
@@ -59,7 +59,15 @@
  <!-- 添加任务 -->
  <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>
@@ -68,6 +76,7 @@
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,
@@ -76,6 +85,9 @@
});
const jobListData = ref([]);
const total = ref(0);
let wayLineJodInfoId = ref('')
let isShowDeviceJobDetails = ref(false);
let isShowCurrentTaskDetails = ref(false);
// 获取任务列表
const getJobList = () => {
@@ -90,7 +102,7 @@
  const statusMap = {
    1: '待执行',
    2: '执行中',
    3: '已完成',
    3: '已执行',
    4: '已取消',
    5: '执行失败'
  };
@@ -98,15 +110,16 @@
};
// 查看当前任务详情 如果是一台机则显示详情 如果是多台机则进入集群调度(暂未开发)
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('即将跳转到集群调度');
src/views/TaskManage/components/TaskAlgorithmBusiness.vue
@@ -1,12 +1,12 @@
<!-- 关联算法和综合业务 -->
<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>