forked from drone/command-center-dashboard

chenyao
2025-04-09 cc1aacf766bc902001f460fdfce20a9ec2ea097d
Merge branch 'master' of http://139.196.74.78:10010/r/drone/command-center-dashboard
9 files modified
1 files added
571 ■■■■ changed files
src/store/modules/common.js 6 ●●●●● 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 21 ●●●●● patch | view | raw | blame | history
src/views/Home/HomeLeft/InspectionRaskDetails/InspectionRaskDetails.vue 13 ●●●●● patch | view | raw | blame | history
src/views/Home/HomeLeft/InspectionRaskDetails/InspectionRaskDetailsDialog.vue 241 ●●●●● patch | view | raw | blame | history
src/views/Home/HomeLeft/OverviewNext.vue 2 ●●● patch | view | raw | blame | history
src/views/Home/useMapAggregation/useMapAggregation.js 48 ●●●● patch | view | raw | blame | history
src/views/SignMachineNest/MachineRight/MachineStatus/MachineTableDetails/DeviceJob/DeviceJobDetails/DeviceJobDetailsMap.vue 1 ●●●● patch | view | raw | blame | history
src/store/modules/common.js
@@ -10,7 +10,9 @@
 */
import { setStore, getStore, removeStore } from 'utils/store'
import website from '@/config/website'
import cesiumOptions from '@/utils/cesium-tsa'
const { loadLAYER } = cesiumOptions()
const common = {
  state: {
    language: getStore({ name: 'language' }) || 'zh-cn',
@@ -34,6 +36,10 @@
    },
  },
  mutations: {
    setMapSetting: (state, data) => {
      state.mapSetting = data
      loadLAYER()
    },
    SET_LANGUAGE: (state, language) => {
      state.language = language
      setStore({
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>
    </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/InspectionRaskDetails/InspectionRaskDetails.vue
@@ -20,6 +20,7 @@
        <CommonDateTime :style="{ top: pxToRem(19) }" v-model="newTime" @change="getData" />
        <div class="chart-container" ref="chartRef"></div>
    </div>
    <InspectionRaskDetailsDialog v-model:show="isShowDetailsDialog"></InspectionRaskDetailsDialog>
</template>
<script setup>
@@ -29,7 +30,7 @@
import CommonDateTime from '@/components/CommonDateTime.vue'
import { getJobNumBar, getJobStatistics, getTotalJobNum } from '@/api/home'
import dayjs from 'dayjs'
import InspectionRaskDetailsDialog from '@/views/Home/HomeLeft/InspectionRaskDetails/InspectionRaskDetailsDialog.vue'
const currenDate = dayjs().format('YYYY-MM-DD')
const newTime = ref([currenDate, currenDate])
const list = ref([
@@ -103,9 +104,12 @@
        },
    },
}
// 是否展示巡检任务详情
const isShowDetailsDialog = ref(false)
const detailsFun = () => {
    console.log('details')
console.log(isShowDetailsDialog.value)
    isShowDetailsDialog.value = true
}
let chart = null
@@ -166,8 +170,7 @@
    opacity: 0.85;
    .inspection-num {
        background: url('@/assets/images/home/homeLeft/inspection-num.png') no-repeat center / 100%
            100%;
        background: url('@/assets/images/home/homeLeft/inspection-num.png') no-repeat center / 100% 100%;
        width: 360px;
        height: 118px;
        position: relative;
src/views/Home/HomeLeft/InspectionRaskDetails/InspectionRaskDetailsDialog.vue
@@ -1,9 +1,248 @@
 <!--  巡检任务情况-详情 -->
<template>
    <el-dialog
        v-model="isShowDetailsDialog"
        title="巡检任务详情"
        :width="pxToRem(1000)"
        :close-on-click-modal="false"
        :destroy-on-close="true"
    >
        <!-- 搜索 -->
        <el-form :model="searchForm" inline>
            <div class="search-row">
                <el-form-item>
                    <el-input v-model="searchForm.key_word" placeholder="请输入任务/机巢名称" clearable />
                </el-form-item>
                <el-form-item label="关联算法">
                    <el-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 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 v-model="searchForm.device_sn" placeholder="请选择">
                        <el-option
                            v-for="item in deviceList"
                            :label="item.nickname"
                            :value="item.device_sn"
                            :key="item.device_sn"
                        />
                    </el-select>
                </el-form-item>
                <el-form-item>
                    <el-date-picker
                        v-model="dateRange"
                        type="daterange"
                        range-separator="至"
                        start-placeholder="开始日期"
                        end-placeholder="结束日期"
                        value-format="YYYY-MM-DD"
                        @change="changeselect"
                    />
                </el-form-item>
                <el-form-item>
                    <el-button @click="handleReset">重置</el-button>
                    <el-button type="primary" @click="handleSearch">查询</el-button>
                </el-form-item>
            </div>
        </el-form>
        <!-- 表格 -->
        <el-table :data="taskDetailData" style="width: 100%">
            <el-table-column label="序号" width="60">
                <template #default="scope">
                    {{ (taskDetailParams.page - 1) * taskDetailParams.limit + scope.$index + 1 }}
                </template>
            </el-table-column>
            <el-table-column prop="job_info_num" label="任务编号" />
            <el-table-column prop="name" label="任务名称" />
            <el-table-column prop="device_names" label="所属机巢" />
            <el-table-column prop="ai_type_str" label="关联算法" />
            <el-table-column prop="status" label="任务状态" />
            <el-table-column prop="industry_type_str" label="任务类型" />
            <el-table-column prop="begin_time" label="任务时间" />
            <el-table-column prop="name" label="关联事件" />
            <el-table-column label="操作"> <el-button link type="primary" size="small">查看</el-button></el-table-column>
        </el-table>
        <!-- 分页 -->
        <div class="pagination">
            <el-pagination
                v-model:current-page="pageParams.page"
                v-model:page-size="pageParams.limit"
                :page-sizes="[10, 20, 30, 50]"
                background
                :disabled="disabled"
                layout="total, sizes, prev, pager, next, jumper"
                :total="total"
                @size-change="handleSizeChange"
                @current-change="handleCurrentChange"
            />
        </div>
    </el-dialog>
</template>
<script setup>
import { pxToRem } from '@/utils/rem'
import { jobList } from '@/api/home/task'
import { getDictionary } from '@/api/system/dict'
import { selectDevicePage } from '@/api/home/machineNest'
import { getMultipleDictionary } from '@/api/system/dictbiz'
const isShowDetailsDialog = defineModel('show')
const dateRange = ref('')
const searchForm = reactive({
    key_word: '', // 模糊搜索关键词(匹配名称/昵称/设备sn)
    ai_types: [], // 算法类型
    device_sn: '', // 设备编号
    end_date: null, // 结束时间
    start_date: null, // 开始时间
    status: '', // 作业状态
})
const statusOptions = [
    { label: '待执行', value: 1 },
    { label: '执行中', value: 2 },
    { label: '已完成', value: 3 },
    { label: '已取消', value: 4 },
    { label: '执行失败', value: 5 },
]
// 设备页面参数
const devicePageParams = ref({
    current: 1,
    size: 10,
    total: 0,
    nickname: '',
})
const deviceList = ref([])
// 表格
const taskDetailData = ref([])
// 表格参数
const taskDetailParams = reactive({
    page: 1,
    limit: 10,
    searchParams: {},
})
// 分页
const total = ref(0)
// 分页参数
let pageParams = reactive({
    page: 1,
    limit: 10,
})
// 日期选择
const changeselect = () => {
    searchForm.start_date = dateRange.value.length ? `${dateRange.value[0]} 00:00:00` : null
    searchForm.end_date = dateRange.value.length ? `${dateRange.value[1]} 00:00:00` : null
}
// 重置
const handleReset = () => {
    dateRange.value = null
    Object.keys(searchForm).forEach(key => {
        if (key == 'start_date' || key == 'end_date') {
            searchForm[key] = null
        } else if (key == 'ai_types') {
            searchForm[key] = []
        } else {
            searchForm[key] = ''
        }
    })
    handleSearch()
}
// 查询
const handleSearch = () => {
    taskDetailParams.page = 1
    taskDetailParams.limit = 10
    taskDetailParams.searchParams = searchForm
    console.log('taskDetailParams.value', taskDetailParams)
    getJobList()
}
// 分页大小改变
const handleSizeChange = val => {
    pageParams.limit = val
    getJobList()
}
// 页码改变
const handleCurrentChange = val => {
    pageParams.page = val
    getJobList()
}
// 获取任务列表
const getJobList = () => {
    jobList(taskDetailParams).then(res => {
        if (res.data.code !== 0) return
        taskDetailData.value = res.data.data.records
        total.value = res.data.data.total
    })
}
// 机巢列表数据
const getDeviceList = async () => {
    const res = await selectDevicePage({ current: 1, size: 99999, type: 1 })
    deviceList.value = res.data?.data?.records || []
}
// 关联算法
let taskBusiness = ref([])
let taskAlgorithm = ref([])
// 请求字典字段
const requestDictionary = () => {
    getMultipleDictionary('SF,HYLB').then(res => {
        if (res.code !== 0) {
            // 处理数据
            taskAlgorithm.value = res.data.data['SF']
            taskBusiness.value = res.data.data['HYLB']
        }
    })
}
const algorithmChange = val => {
    searchForm.ai_types = val
}
onMounted(() => {
    requestDictionary()
    getJobList()
    getDeviceList()
})
</script>
<style scoped lang="scss">
.search-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    .el-form-item {
        flex: 1;
    }
}
:deep(.el-form) {
    .el-form-item {
        margin-bottom: 0;
        margin-right: 20px;
        .el-form-item__label {
            color: #fff;
        }
        .el-input,
        .el-select,
        .el-date-editor {
            width: 200px;
            --el-input-bg-color: transparent;
            --el-input-border-color: rgba(255, 255, 255, 0.3);
            --el-input-text-color: #fff;
            --el-input-placeholder-color: rgba(255, 255, 255, 0.5);
        }
    }
}
:deep() {
    .el-date-editor.el-input,
    .el-date-editor.el-input__wrapper {
        height: var(--el-input-height, var(--el-component-size));
        width: 0 !important;
    }
}
.pagination {
    margin-top: 20px;
}
</style>
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">
src/views/Home/useMapAggregation/useMapAggregation.js
@@ -203,7 +203,7 @@
    flyTo({ longitude, latitude }, 0, height)
  }
  const userAreaPosition = computed(() => store.state.home.userAreaPosition);
  const userAreaPosition = computed(() => store.state.home.userAreaPosition)
  const setCenterPosition = (position) => {
    store.commit('setCurrentAreaPosition', position)
@@ -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,
@@ -263,15 +262,12 @@
  const renderOutline = item => {
    item.outline &&
    Cesium.GeoJsonDataSource.load(item.outline).then(dataSource => {
      viewer.dataSources.add(dataSource)
      const entities = dataSource.entities.values
      entities.forEach(entity => {
        // 隐藏多边形填充
        entity.polygon.material = Cesium.Color.TRANSPARENT
        entity.polygon.outline = false // 关闭原生轮廓
        entities.forEach((entity, index) => {
        // 创建独立折线作为轮廓
        const positions = entity.polygon.hierarchy.getValue().positions
        viewer.entities.add({
            id: `aggregation-outline-${index}`,
          polyline: {
            positions: positions,
            width: 5, // 直接设置宽度
@@ -279,7 +275,6 @@
              glowPower: 0.5,
              color: Cesium.Color.AQUA,
            }),
            clampToGround: true, // 贴地显示
          },
        })
      })
@@ -301,7 +296,7 @@
    // 遍历特征并添加实体
    featuresList.forEach((feature, index) => {
      if (!feature.position) return
      const position = Cesium.Cartesian3.fromDegrees(feature.position[0], feature.position[1])
      const position = Cesium.Cartesian3.fromDegrees(feature.position[0], feature.position[1], 12000)
      viewer.entities.add({
        position: position,
        id: `aggregation-name-${index}`,
@@ -348,10 +343,12 @@
      return arrColor[++index % arrColor.length]
    }
    // 加载新的 GeoJSON 数据
    // 加载边界
    Cesium.GeoJsonDataSource.load(item.gJson).then(dataSource => {
      console.log(dataSource, item.gJson)
      viewer.dataSources.add(dataSource)
      item.dataSource = dataSource // 保存数据源以便后续删除
      item.BJDataSource = dataSource // 保存数据源以便后续删除
      // 获取数据源中的实体
      const entities = dataSource.entities.values
@@ -362,18 +359,14 @@
          alphaPower: 1.3
        })
        entity.polygon.extrudedHeight = (entity.propertyNames.length || 1) * 500
        entity.polygon.extrudedHeight = (entity.properties.childrenNum._value || 1) * 500
        entity.polygon.material = material
        entity.polygon.outline = false // 显示边框
      })
      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
@@ -410,13 +403,17 @@
  // 左键单机事件
  const singleMachineEvent = async click => {
    const pickedObject = viewer.scene.pick(click.position)
    if (Cesium.defined(pickedObject) && pickedObject.id) {
    if (!(Cesium.defined(pickedObject) && pickedObject.id)) return
      const entity = pickedObject.id
      currentEntity = entity
      positionC3 = entity?.position?._value
      if (!positionC3) return
    const entityPosition = entity?.position?._value
    if (!entityPosition) return
    if (entityPosition) {
      positionC3 = entityPosition
    }
      removeLabel()
      const customData = entity.properties.customData._value.data
    const customData = entity?.properties?.customData?._value?.data
    if (!customData) return
      console.log('customData', customData)
      switch (customData.type) {
        case 'deviceAggregation':
@@ -435,7 +432,6 @@
          break
      }
    }
  }
  const removeDom = () => {
    const dom = document.querySelector('#mapPopUpBox')
@@ -446,8 +442,12 @@
  // 移除 点 和 gjson 实体
  const removeEntities = () => {
    viewer.dataSources?.removeAll(true)
    // viewer.entities?.removeAll();
    // dataSources移除
    scalingJudgment.forEach(item => {
      item.BJDataSource && viewer.dataSources.remove(item.BJDataSource)
      item.BJDataSource = null
    })
    // entities移除
    const entitiesIDs = viewer.entities.values.map(i => i.id)
    entitiesIDs.forEach(item => {
      item.includes('aggregation-') && viewer.entities.removeById(item)
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'])