forked from drone/command-center-dashboard

罗广辉
2025-04-09 ba62351d90516cf6db30332cf8ba411fd7c44526
feat: 智引即飞70%
7 files modified
1 files added
281 ■■■■ changed files
src/api/home/task.js 5 ●●●● patch | view | raw | blame | history
src/store/modules/home.js 5 ●●●●● patch | view | raw | blame | history
src/views/Home/AINowFly.vue 179 ●●●●● patch | view | raw | blame | history
src/views/Home/Footer.vue 55 ●●●●● patch | view | raw | blame | history
src/views/Home/Home.vue 25 ●●●●● patch | view | raw | blame | history
src/views/Home/HomeLeft/OverviewNext.vue 4 ●●●● patch | view | raw | blame | history
src/views/Home/useMapAggregation/useMapAggregation.js 7 ●●●● patch | view | raw | blame | history
src/views/SignMachineNest/MachineRight/MachineStatus/MachineTableDetails/DeviceJob/DeviceJobDetails/DeviceJobDetailsMap.vue 1 ●●●● patch | view | raw | blame | history
src/api/home/task.js
@@ -90,10 +90,7 @@
    return request({
        url: `/drone-device-core/manage/api/v1/devices/getFlyingNestBy?current=${data.page}&size=${data.limit}`,
        method: 'post',
        data: {
            wayline_id: data.wayline_id,
            type: data.type
        },
        data
    })
}
src/store/modules/home.js
@@ -1,17 +1,16 @@
import { set } from 'lodash'
import { EDeviceTypeName } from '@/utils/staticData/enums'
import { getStore, setStore } from '@/utils/store'
const home = {
  state: {
    machineNestDetail: false, // 机巢详情
    machineNestDetail: false, // 机巢长列表
        isEventOverviewDetail: false,//是事件概述详情
        // 用户行政区划中心点
        userAreaPosition: getStore({ name: 'userAreaPosition' }) || {},
        // 用户切换后行政区划中心点
        currentAreaPosition:getStore({ name: 'currentAreaPosition' }) || {},
    singleUavHome: {},
        footActiveIndex: 0,
        isEventOverviewDetail: false,//是事件概述详情
    singleTotal: {}, // 统计单个机巢数据
    // 项目id
    selectedWorkSpaceId: window.localStorage.getItem('bs_workspace_id'),
src/views/Home/AINowFly.vue
New file
@@ -0,0 +1,179 @@
<template>
    <div class="AINowFly">
        <div>智引即飞</div>
        <div>
            任务名称
            <el-input type="text" v-model="params.taskName" />
        </div>
        <div>
            关联算法:
            <el-select v-model="params.ai_types" placeholder="请选择">
                <el-option v-for="item in taskAlgorithm" :key="item.id" :label="item.dictValue" :value="item.dictKey" />
            </el-select>
        </div>
        <div>
            地址:
            <el-input type="text" v-model="params.dz" />
        </div>
        <el-table :data="list">
            <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>
    </div>
</template>
<script setup>
import { getMultipleDictionary } from '@/api/system/dictbiz'
import * as Cesium from 'cesium'
import { getFlyingNestBy } from '@/api/home/task'
import { cartesian3Convert } from '@/utils/cesium/mapUtil'
import { Cartesian3 } from 'cesium'
import uavImg from '@/assets/images/home/useUavHome/uavImg.png'
import ImageTrailMaterial from '@/utils/cesium/ImageTrailMaterial'
import lineImg from '@/assets/images/arrow-right-blue.png'
import _ from 'lodash'
import { ElMessage } from 'element-plus'
const params = ref({
    taskName: '',
    ai_types: '',
    dz: '',
})
let handler = null
const taskAlgorithm = ref([])
let viewer
const list = ref([])
let eventPoint = {}
const renderingLine = () => {
    const { longitude, latitude } = eventPoint
    list.value.forEach((item, index) => {
        const start = Cartesian3.fromDegrees(Number(item.longitude), Number(item.latitude))
        const end = Cartesian3.fromDegrees(Number(longitude), Number(latitude))
        const positionsList = [start, end]
        // 线
        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 },
                }),
                clampToGround: false,
            },
        })
        // 点
        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),
            },
            billboard: {
                image: new Cesium.ConstantProperty(uavImg),
                width: 24,
                height: 24,
            },
            properties: {
                customData: {
                    data: item,
                },
            },
        })
    })
}
const removeEntities = () => {
    const entitiesIDs = viewer.entities.values.map(i => i.id)
    entitiesIDs.forEach(item => {
        item.includes('AINow-') && viewer.entities.removeById(item)
    })
}
// 单击事件
const singleClickEvent = click => {
    removeEntities()
    const earthPosition = viewer.scene.pickPosition(click.position)
    viewer.entities.add({
        position: earthPosition,
        id: `AINow-event-0`,
        point: {
            pixelSize: 10,
            color: Cesium.Color.RED,
            outlineColor: Cesium.Color.WHITE,
            outlineWidth: 2,
        },
    })
    const { longitude, latitude } = cartesian3Convert(earthPosition, viewer)
    eventPoint = { longitude, latitude }
    getFJList()
}
// 请求字典字段
const requestDictionary = () => {
    getMultipleDictionary('SF').then(res => {
        if (res.code !== 0) {
            taskAlgorithm.value = res.data.data['SF']
        }
    })
}
// 获取飞机列表
const getFJList = async () => {
    const params = { ...eventPoint, page: 1, limit: 9999, type: 1 }
    const res = await getFlyingNestBy(params)
    list.value = (res.data?.data || []).map(item => ({
        ...item,
        minute: _.round(item.estimated_arrival_time / 60, 0),
    }))
    if (!list.value.length) ElMessage.warning('附近暂无可用无人机')
    renderingLine()
}
const initMap = () => {
    handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
    handler.setInputAction(singleClickEvent, Cesium.ScreenSpaceEventType.LEFT_CLICK)
}
const removeAll = () => {
    removeEntities()
    handler?.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK)
    handler?.destroy()
    handler = null
}
onBeforeUnmount(() => {
    removeAll()
})
onMounted(() => {
    requestDictionary()
    viewer = window.$viewer
    initMap()
})
</script>
<style scoped lang="scss">
.AINowFly {
    position: absolute;
    top: 200px;
    right: 0;
    width: 400px;
    height: 700px;
    background: white;
    color: black;
    font-size: 20px;
}
</style>
src/views/Home/Footer.vue
@@ -1,15 +1,20 @@
<template>
    <div class="footer">
        <img
            v-for="item in list"
            v-for="(item,index) in list.filter(i => !i.disable)"
            :class="item.className"
            :src="item.active ? item.activeImg : item.img"
            alt=""
            @click="imgClick(item)"
            @click="footAction(item,index)"
        />
    </div>
    <div class="intelligent-introduction-flying">
        <img class="orthophoto" src="@/assets/images/intelligent-introduction-flying.png" alt="" />
        <img
            class="orthophoto"
            src="@/assets/images/intelligent-introduction-flying.png"
            alt=""
            @click="footAction(list[5],5)"
        />
    </div>
</template>
@@ -27,6 +32,10 @@
import { useMapAggregation } from '@/views/Home/useMapAggregation/useMapAggregation'
import { useStore } from 'vuex'
import func from '@/utils/func'
const store = useStore()
const footActiveIndex = computed(() => store.state.home.footActiveIndex)
// 机巢聚合
const { init, removeAll } = useMapAggregation('device')
@@ -35,7 +44,7 @@
const list = ref([
    {
        name: 'event1',
        name: 'jc',
        img: img1,
        activeImg: activeImg1,
        active: true,
@@ -44,7 +53,7 @@
        removeAll: removeAll,
    },
    {
        name: 'event2',
        name: 'sj',
        img: img2,
        activeImg: activeImg2,
        active: false,
@@ -52,27 +61,28 @@
        init: eventInit,
        removeAll: eventRemove,
    },
    { name: 'event3', img: img3, activeImg: activeImg3, active: false, className: 'panorama' },
    { name: 'event4', img: img4, activeImg: activeImg4, active: false, className: 'threeD' },
    { name: 'event5', img: img5, activeImg: activeImg5, active: false, className: 'orthophoto' },
    { name: 'qj', img: img3, activeImg: activeImg3, active: false, className: 'panorama' },
    { name: 'sw', img: img4, activeImg: activeImg4, active: false, className: 'threeD' },
    { name: 'zs', img: img5, activeImg: activeImg5, active: false, className: 'orthophoto' },
    { name: 'zyjf', active: false, disable: true },
])
const store = useStore()
const imgClick = (toItem,index) => {
    index !== undefined && store.commit('setFootActiveIndex',index)
    const fromItem = list.value.find(item => item.active)
    if (fromItem.name === toItem.name) return
const footAction = (toItem, index) => {
    const fromItem = list.value.find(item => item.active)  //上一个激活item
    if (fromItem.name === toItem?.name) return  //是重复点击
    index !== undefined && store.commit('setFootActiveIndex', index) //是按钮点击得动作
    fromItem?.removeAll?.()
    nextTick(() => {
        toItem?.init?.()
    })
    list.value = list.value.map(item => ({ ...item, active: item.name === toItem.name }))
    nextTick(() => toItem?.init?.())
    list.value = list.value.map(item => ({ ...item, active: item.name === toItem?.name }))
}
const footActiveIndex = computed(() => store.state.home.footActiveIndex)
watch(footActiveIndex, val => {
    console.log(6666666)
    imgClick(list.value[val])
},{deep:true})
watch(
    footActiveIndex,
    val => {
        footAction(list.value[val])
    },
    { deep: true }
)
// 销毁前钩子
onBeforeUnmount(() => {
@@ -128,6 +138,7 @@
    bottom: 23px;
    img {
        cursor: pointer;
        width: 146px;
        height: 54px;
    }
src/views/Home/Home.vue
@@ -1,15 +1,17 @@
<template>
    <template v-if="singleUavHome?.device_sn">
        <SignMachineNest />
    </template>
    <!--        单个机巢页面-->
    <SignMachineNest v-if="singleUavHome?.device_sn" />
    <!--        事件概述详细信息页面-->
    <EventOverviewDetail v-else-if="isEventOverviewDetail" />
    <AINowFly v-else-if="isAINowFly" />
    <!--            首页默认的页面-->
    <template v-else>
        <EventOverviewDetail v-if="isEventOverviewDetail" />
        <template v-else>
            <SearchBox />
            <HomeLeft />
            <HomeRight />
        </template>
        <HomeLeft />
        <HomeRight />
    </template>
    <SearchBox />
    <RSide />
    <Footer />
</template>
@@ -25,16 +27,19 @@
import { onMounted } from 'vue'
import cesiumOperation from '@/utils/cesium-tsa'
import EventOverviewDetail from '@/views/Home/EventOverviewDetail/EventOverviewDetail.vue'
import AINowFly from '@/views/Home/AINowFly.vue'
const store = useStore()
const { _init, viewerDestory } = cesiumOperation()
const singleUavHome = computed(() => store.state.home.singleUavHome)
const isEventOverviewDetail = computed(() => store.state.home.isEventOverviewDetail)
const isAINowFly = computed(() => store.state.home.footActiveIndex === 5)
onUnmounted(() => {
    store.commit('setSingleUavHome', null)
    console.log('home销毁')
    store.commit('setIsEventOverviewDetail', false)
    store.commit('setFootActiveIndex', 0)
    viewerDestory()
})
src/views/Home/HomeLeft/OverviewNext.vue
@@ -1,6 +1,6 @@
<!-- 机巢概况 -->
<template>
  <common-title :style="{ marginLeft: pxToRem(14) }" @details="detailsFun"></common-title>
  <common-title title="机巢概况" :style="{ marginLeft: pxToRem(14) }" @details="detailsFun"></common-title>
  <div class="overview-next">
    <MachineNestTotal @searchNickName="handleSearch" />
    <div class="table-list">
@@ -114,7 +114,7 @@
  border-radius: 0px 0px 0px 0px;
  opacity: 0.85;
  margin-bottom: 10px;
  .table-list {
    font-family: Source Han Sans CN, Source Han Sans CN;
    margin: 16px;
src/views/Home/useMapAggregation/useMapAggregation.js
@@ -235,7 +235,6 @@
        id: `aggregation-splashed-${index}`,
        position: Cesium.Cartesian3.fromDegrees(item.longitude, item.latitude),
        label: {
          // 随机整数
          text: item.nickname,
          font: '12pt Source Han Sans CN',
          fillColor: Cesium.Color.WHITE,
@@ -365,11 +364,7 @@
      })
      needFly && viewer.flyTo(dataSource, {
        offset: new Cesium.HeadingPitchRange(
          0, // heading: 0 (朝向不变)
          Cesium.Math.toRadians(-60), // pitch: -90° (垂直向下)
          0 // range: 0 (默认距离)
        ),
        offset: new Cesium.HeadingPitchRange(0, Cesium.Math.toRadians(-60), 0),
        duration: 0.5,
      })
      needFly = false
src/views/SignMachineNest/MachineRight/MachineStatus/MachineTableDetails/DeviceJob/DeviceJobDetails/DeviceJobDetailsMap.vue
@@ -9,7 +9,6 @@
import ImageTrailMaterial from '@/utils/cesium/ImageTrailMaterial'
import lineImg from '@/assets/images/arrow-right-blue.png'
import uavImg from '@/assets/images/home/useUavHome/uavImg.png'
import { getCenterPoint } from '@/utils/cesium/mapUtil'
const props = defineProps(['detailsData'])