forked from drone/command-center-dashboard

shuishen
2025-04-16 a7e6761ba0cfccdf33ed552eb2d3b783c8e4ab4a
src/views/Home/AINowFly.vue
@@ -1,29 +1,35 @@
<template>
   <div class="AINowFly">
      <div>智引即飞</div>
      <div class="title">智引即飞<img @click="closeClick" src="@/assets/images/aiNowFly/close.png"/></div>
      <el-form ref="ruleFormRef" :model="params" :rules="rules" label-width="auto" size="small" status-icon>
         <el-form-item label="任务名称" prop="taskName">
            <el-input v-model="params.taskName" />
         <el-form-item label="任务名称" prop="name">
            <el-input v-model="params.name" />
         </el-form-item>
         <el-form-item label="关联算法" prop="ai_types">
            <el-select v-model="params.ai_types" placeholder="请选择">
            <el-select v-model="params.ai_types" placeholder="请选择" multiple collapse-tags collapse-tags-tooltip>
               <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-input disabled type="text" v-model="di" />
         <el-form-item label="地址">
            <el-input disabled type="text" v-model="di" placeholder="地名&经纬度"/>
         </el-form-item>
         <!--         todo-->
         <el-form-item label="事件">
            <div class="event">
               <img src="@/assets/images/aiNowFly/picture-recording.png" alt="">
            </div>
         </el-form-item>
      </el-form>
      <div>可用机巢列表</div>
      <el-table :data="list" @selection-change="handleSelectionChange">
         <el-table-column type="selection" width="55" />
         <el-table-column type="index" label="序号" />
      <div class="title-list">可飞行机巢列表:</div>
      <el-table :data="list" height="120" @selection-change="handleSelectionChange">
         <el-table-column type="selection" />
         <!-- <el-table-column type="index" label="序号" /> -->
         <el-table-column prop="nickname" label="机巢名称" />
         <el-table-column prop="minute" label="预计飞行时长(分钟)" />
         <el-table-column prop="exe_distance" label="预计飞行距离(m)" />
         <el-table-column prop="minute" label="可飞行时间" />
         <el-table-column prop="exe_distance" label="可飞行距离" />
      </el-table>
      <el-button type="primary" @click="nowFly">迅速起飞</el-button>
      <div class="btn-submit" @click="nowFly"><img src="@/assets/images/aiNowFly/fly.png" alt=""></div>
   </div>
</template>
<script setup>
@@ -33,6 +39,9 @@
import { cartesian3Convert } from '@/utils/cesium/mapUtil'
import { Cartesian3 } from 'cesium'
import uavImg from '@/assets/images/home/useUavHome/uavImg.png'
import endingImg from '@/assets/images/aiNowFly/ending.png'
import endingHighImg from '@/assets/images/aiNowFly/ending-high.png'
import startingImg from '@/assets/images/aiNowFly/starting.png'
import ImageTrailMaterial from '@/utils/cesium/ImageTrailMaterial'
import lineImg from '@/assets/images/arrow-right-blue.png'
import _ from 'lodash'
@@ -40,23 +49,24 @@
import dayjs from 'dayjs'
import { useStore } from 'vuex'
const now = dayjs().format('YYYY/MM/DD HH:mm')
const now = dayjs().format('YYYYMMDDHHmm')
const nowTime = dayjs().format('YYYY-MM-DD HH:mm:ss')
const paramsInit = {
   taskName: `智引即飞 ${now}`,
   name: `智引即飞${now}`,
   ai_types: '',
   longitude: '',
   latitude: '',
   dock_sns: [],
   type: 1,
   begin_time: nowTime,
   end_time: nowTime,
}
const params = ref(_.cloneDeep(paramsInit))
const di = computed(() => params.value.longitude + ',' + params.value.latitude)
const rules = ref({
   taskName: [
   name: [
      { required: true, message: '请输入任务名称', trigger: 'blur' },
      { min: 3, max: 50, message: '长度在 3 到 20 个字符', trigger: 'blur' },
   ],
@@ -79,11 +89,14 @@
   await ruleFormRef.value.validate()
   if (!params.value.dock_sns.length) return ElMessage.warning('请选择将要飞行的机巢')
   if (!params.value.longitude) return ElMessage.warning('请选择飞行的点')
   if (params.value.dock_sns.length === 1){
      store.commit('setSingleUavHome', { device_sn: params.value.dock_sns[0]})
   }else{
      store.commit('setFootActiveIndex', 0)
   }
   createTask(params.value).then(res => {
      ElMessage.success('起飞成功')
      if (params.value.dock_sns.length === 1) {
         store.commit('setSingleUavHome', { device_sn: params.value.dock_sns[0] })
      } else {
         store.commit('setFootActiveIndex', 0)
      }
   })
}
const renderingLine = () => {
@@ -92,18 +105,35 @@
      const start = Cartesian3.fromDegrees(Number(item.longitude), Number(item.latitude))
      const end = Cartesian3.fromDegrees(Number(longitude), Number(latitude))
      const positionsList = [start, end]
      // 计算中点位置
    const midpoint = Cartesian3.midpoint(start, end, new Cartesian3())
      // 计算线段方向
      const direction = Cartesian3.subtract(end, start, new Cartesian3())
      Cartesian3.normalize(direction, direction)
      // 线
      // 添加中点文字标签
    viewer.entities.add({
      id: `AINow-time-${index}`,
      position: midpoint,
      label: {
        text: `预计${item.minute}分钟`,
            font: 'bold 16px Source Han Sans CN',  // 加粗并增大字号
           fillColor: new Cesium.Color(27/255, 179/255, 255/255, 1.0),
            fillColor: Cesium.Color.WHITE,
            style: Cesium.LabelStyle.FILL_AND_OUTLINE,
            verticalOrigin: Cesium.VerticalOrigin.CENTER,
            horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
            eyeOffset: new Cesium.Cartesian3(0, 0, -10),
            pixelOffset: new Cesium.Cartesian2(0, -10),
            rotation: Math.atan2(direction.y, direction.x)
      }
    })
      viewer.entities.add({
         id: `AINow-line-${index}`,
         polyline: {
            width: 4,
            positions: positionsList,
            material: new ImageTrailMaterial({
               color: { alpha: 1, blue: 1, green: 1, red: 1 },
               speed: 20,
               image: lineImg,
               repeat: { x: Math.floor(40), y: 1 },
            }),
            material: new Cesium.Color(27/255, 179/255, 255/255, 1.0),
            clampToGround: false,
         },
      })
@@ -111,20 +141,20 @@
      viewer.entities.add({
         id: `AINow-point-${index}`,
         position: start,
         label: {
            text: item.nickname,
            font: '12pt Source Han Sans CN',
            fillColor: Cesium.Color.WHITE,
            outlineColor: Cesium.Color.BLACK,
            outlineWidth: 2,
            style: Cesium.LabelStyle.FILL_AND_OUTLINE,
            verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
            pixelOffset: new Cesium.Cartesian2(0, -9),
         },
         // label: {
         //    text: item.nickname,
         //    font: '12pt Source Han Sans CN',
         //    fillColor: Cesium.Color.WHITE,
         //    outlineColor: Cesium.Color.BLACK,
         //    outlineWidth: 2,
         //    style: Cesium.LabelStyle.FILL_AND_OUTLINE,
         //    verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
         //    pixelOffset: new Cesium.Cartesian2(0, -9),
         // },
         billboard: {
            image: new Cesium.ConstantProperty(uavImg),
            width: 24,
            height: 24,
            image: new Cesium.ConstantProperty(index === 0 ? endingHighImg : endingImg),
            width: 40,
            height: 40,
         },
         properties: {
            customData: {
@@ -149,16 +179,15 @@
   viewer.entities.add({
      position: earthPosition,
      id: `AINow-event-0`,
      point: {
         pixelSize: 10,
         color: Cesium.Color.RED,
         outlineColor: Cesium.Color.WHITE,
         outlineWidth: 2,
      },
      billboard: {
         image: new Cesium.ConstantProperty(startingImg),
         width: 40,
         height: 40,
      }
   })
   const { longitude, latitude } = cartesian3Convert(earthPosition, viewer)
   eventPoint = { longitude, latitude }
   params.value = {...params.value,longitude, latitude}
   params.value = { ...params.value, longitude, latitude }
   getFJList()
}
@@ -171,11 +200,19 @@
   })
}
let pagingParams = ref({
  current: 1,
  size: 10
});
// 获取飞机列表
const getFJList = async () => {
   params.value.dock_sns = []
   const getListParams = { ...eventPoint, page: 1, limit: 9999, type: 1 }
   const res = await getFlyingNestBy(getListParams)
   let pageParams = ref({
      ...eventPoint,
      type: 1,
   });
   params.value.dock_sns = [];
   const res = await getFlyingNestBy(pageParams.value, pagingParams.value)
   list.value = (res.data?.data || []).map(item => ({
      ...item,
      minute: _.round(item.estimated_arrival_time / 60, 0),
@@ -196,6 +233,15 @@
   handler = null
}
// 关闭
const closeClick = () => {
   removeAll()
   params.value = _.cloneDeep(paramsInit)
   list.value = []
   eventPoint = {}
   store.commit('setFootActiveIndex', 0)
}
onBeforeUnmount(() => {
   removeAll()
})
@@ -209,12 +255,128 @@
<style scoped lang="scss">
.AINowFly {
   position: absolute;
   top: 200px;
   right: 0;
   width: 400px;
   height: 700px;
   background: white;
   color: black;
   font-size: 20px;
   top: 263px;
   right: 74px;
   width: 320px;
   height: 518.6px;
   font-size: 14px;
   color: #FFFFFF;
   background: url('@/assets/images/aiNowFly/ai-fly-bg.png') no-repeat center / 100% 100%;
   .title {
      height: 50px;
      line-height: 50px;
      margin-left: 20px;
      font-family: Segoe UI, Segoe UI;
      font-weight: bold;
      font-size: 20px;
      letter-spacing: 2px;
      background: linear-gradient(180deg, #FFFFFF 50%, #35D0FF 100%);
      -webkit-background-clip: text;
      -webkit-text-fill-color: transparent;
      background-clip: text;
      img {
         position: absolute;
         cursor: pointer;
         width: 16px;
         height: 16px;
         right: 15px;
         top: 15px;
      }
   }
   .el-form {
      padding: 20px 16px 13px 11px;
      :deep(.el-form-item__label) {
         color: #FFFFFF;
      }
      :deep(.el-input__wrapper),
    :deep(.el-select),
    :deep(.el-select__wrapper) {
      background-color: #021740;
      box-shadow: 0 0 0 1px #026AD6;
      &.is-focus {
        box-shadow: 0 0 0 1px #026AD6;
      }
    }
    :deep(.el-input__wrapper.is-disabled) {
      background-color: #021740;
      box-shadow: 0 0 0 1px #026AD6;
    }
      .event {
         img {
            width: 40px;
            height: 34px;
         }
      }
   }
   .title-list {
      padding: 0 0 14px 11px;
   }
   .el-table {
      width: 300px;
      height: 120px;
      margin: 0 0 0 11px;
      overflow: hidden;
      :deep(.el-scrollbar__wrap) {
         overflow-x: hidden !important;
      }
      :deep(.el-table__body-wrapper) {
         overflow-x: hidden !important;
      }
      :deep(.el-table__header-wrapper) {
         th {
            font-size: 12px;
            color: #D3E9FF;
            background-color: #122348;
            font-weight: normal;
            border-bottom: none;
         }
      }
      :deep(.el-table__empty-block) {
         background-color: #0A1734;
         height: auto !important;  // 移除固定高度
       min-height: 60px;        // 设置最小高度
         .el-table__empty-text {
            color: #FFFFFF;
         }
      }
      :deep(.el-table__body) {
         background-color: #0A1734;
         tr {
            background-color: #0A1734 !important;
            color: #FFFFFF;
            &:hover > td {
               background-color: #0A1734 !important;
            }
         }
      }
      :deep(.el-scrollbar) {
         background-color: #0A1734;
      }
   }
   .btn-submit {
      cursor: pointer;
      margin-left: 88px;
      margin-top: 12px;
      img { width: 138px; height: 38px; }
   }
   :deep(.el-table__inner-wrapper) {
    border: 1px dashed #2F497D;
    &::before {
      display: none;
    }
  }
  :deep(.el-table__cell) {
    border: none;
  }
  :deep(.el-table__border) {
    display: none;
  }
}
</style>