From fee18d19a1d961847cd09469a20fc8231c1df46c Mon Sep 17 00:00:00 2001
From: 张含笑 <zhx18749296735@163.com>
Date: Thu, 10 Apr 2025 11:09:34 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master'
---
src/views/TaskManage/TaskIntermediateContent/CurrentTaskDetails.vue | 20
src/views/Home/HomeLeft/MachineNestList.vue | 1
src/api/home/task.js | 21
src/api/home/machineNest.js | 3
src/views/Home/useMapAggregation/useMapAggregation.js | 73 +-
src/views/SignMachineNest/MachineLeft/MachineLeft.vue | 1
src/views/SignMachineNest/MachineRight/MachineStatus/MachineTableDetails/DeviceJob/DeviceJobDetails/DeviceJobDetailsMap.vue | 1
src/views/Home/AINowFly.vue | 228 ++++++++++
src/views/Home/useMapAggregation/DevicePopUpBox.vue | 4
src/views/Home/HomeLeft/OverviewNext.vue | 5
src/views/TaskManage/TaskIntermediateContent/TaskMap.vue | 542 ++++++++++++++++++++++--
src/store/modules/home.js | 5
src/views/TaskManage/TaskIntermediateContent/AddTask.vue | 141 +++++
src/views/Home/Footer.vue | 55 +-
src/views/Home/HomeLeft/components/MachineNestTotal.vue | 4
src/views/TaskManage/TaskIntermediateContent/TaskIntermediateContent.vue | 43 -
src/views/TaskManage/TaskIntermediateContent/TaskTable.vue | 123 ++++
src/views/Home/Home.vue | 25
18 files changed, 1,084 insertions(+), 211 deletions(-)
diff --git a/src/api/home/machineNest.js b/src/api/home/machineNest.js
index de87e64..d91121e 100644
--- a/src/api/home/machineNest.js
+++ b/src/api/home/machineNest.js
@@ -9,13 +9,12 @@
})
}
// 机巢列表
-export const selectDevicePage = ({ nickname, is_execute, ...params }) => {
+export const selectDevicePage = ({ nickname, ...params }) => {
return request({
url: `/drone-device-core/manage/api/v1/devices/selectDevicePage?type=${params.type}¤t=${params.current}&size=${params.size}`,
method: 'post',
data: {
nickname,
- is_execute,
},
})
}
diff --git a/src/api/home/task.js b/src/api/home/task.js
index b26b7b2..c9d2aa5 100644
--- a/src/api/home/task.js
+++ b/src/api/home/task.js
@@ -43,7 +43,7 @@
// 任务列表
export const jobList = data => {
return request({
- url: '/drone-device-core/wayline/waylineJobInfo/jobList?current=' + data.page + '&size=' + data.limit,
+ url: '/drone-device-core/wayline/waylineJobInfo/jobList?current=' + data.page + '&size=' + data.size,
method: 'post',
data: data.searchParams,
})
@@ -86,14 +86,11 @@
}
// 获取可飞行机巢列表
-export const getFlyingNestBy = (data) => {
+export const getFlyingNestBy = (data,page) => {
return request({
- url: `/drone-device-core/manage/api/v1/devices/getFlyingNestBy?current=${data.page}&size=${data.limit}`,
+ url: `/drone-device-core/manage/api/v1/devices/getFlyingNestBy?current=${page.page}&size=${page.size}`,
method: 'post',
- data: {
- wayline_id: data.wayline_id,
- type: data.type
- },
+ data: data,
})
}
@@ -106,5 +103,15 @@
})
}
+// 面状航线
+export const getWaylineByArea = (data) => {
+ return request({
+ url: `/drone-device-core/wayline/api/v1/workspaces/getWaylineByArea`,
+ method: 'post',
+ data,
+ })
+}
+
+
diff --git a/src/store/modules/home.js b/src/store/modules/home.js
index 0c96d8c..e39501a 100644
--- a/src/store/modules/home.js
+++ b/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'),
diff --git a/src/views/Home/AINowFly.vue b/src/views/Home/AINowFly.vue
new file mode 100644
index 0000000..6cbaf78
--- /dev/null
+++ b/src/views/Home/AINowFly.vue
@@ -0,0 +1,228 @@
+<template>
+ <div class="AINowFly">
+ <div>智引即飞</div>
+ <el-form ref="ruleFormRef" :model="params" :rules="rules" label-width="auto" size="small" status-icon>
+ <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="请选择" multiple>
+ <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>
+ <!-- todo-->
+ <el-form-item label="事件">
+ <div>默认拍照</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="序号" />
+ <el-table-column prop="nickname" label="机巢名称" />
+ <el-table-column prop="minute" label="预计飞行时长(分钟)" />
+ <el-table-column prop="exe_distance" label="预计飞行距离(m)" />
+ </el-table>
+ <el-button type="primary" @click="nowFly">迅速起飞</el-button>
+ </div>
+</template>
+<script setup>
+import { getMultipleDictionary } from '@/api/system/dictbiz'
+import * as Cesium from 'cesium'
+import { createTask, 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'
+import dayjs from 'dayjs'
+import { useStore } from 'vuex'
+
+const now = dayjs().format('YYYYMMDDHHmm')
+const nowTime = dayjs().format('YYYY-MM-DD HH:mm:ss')
+
+const paramsInit = {
+ 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({
+ name: [
+ { required: true, message: '请输入任务名称', trigger: 'blur' },
+ { min: 3, max: 50, message: '长度在 3 到 20 个字符', trigger: 'blur' },
+ ],
+ ai_types: [{ required: true, message: '请选择算法', trigger: 'change' }],
+})
+
+const ruleFormRef = ref(null)
+let handler = null
+const taskAlgorithm = ref([])
+
+let viewer
+const list = ref([])
+let eventPoint = {}
+const store = useStore()
+const handleSelectionChange = val => {
+ params.value.dock_sns = val.map(item => item.device_sn)
+}
+
+const nowFly = async () => {
+ await ruleFormRef.value.validate()
+ if (!params.value.dock_sns.length) return ElMessage.warning('请选择将要飞行的机巢')
+ if (!params.value.longitude) return ElMessage.warning('请选择飞行的点')
+ 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 = () => {
+ 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 }
+ params.value = { ...params.value, longitude, latitude }
+ getFJList()
+}
+
+// 请求字典字段
+const requestDictionary = () => {
+ getMultipleDictionary('SF').then(res => {
+ if (res.code !== 0) {
+ taskAlgorithm.value = res.data.data['SF']
+ }
+ })
+}
+
+// 获取飞机列表
+const getFJList = async () => {
+ params.value.dock_sns = []
+ const getListParams = { ...eventPoint, page: 1, limit: 9999, type: 1 }
+ const res = await getFlyingNestBy(getListParams)
+ 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>
diff --git a/src/views/Home/Footer.vue b/src/views/Home/Footer.vue
index 8c9d4d4..33baaf6 100644
--- a/src/views/Home/Footer.vue
+++ b/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;
}
diff --git a/src/views/Home/Home.vue b/src/views/Home/Home.vue
index cb9d703..d9b14cb 100644
--- a/src/views/Home/Home.vue
+++ b/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()
})
diff --git a/src/views/Home/HomeLeft/MachineNestList.vue b/src/views/Home/HomeLeft/MachineNestList.vue
index 1c3a449..e5d862e 100644
--- a/src/views/Home/HomeLeft/MachineNestList.vue
+++ b/src/views/Home/HomeLeft/MachineNestList.vue
@@ -67,7 +67,6 @@
// 搜索数据
const handleSearch = (name) => {
- console.log(name, '哒哒哒');
searchText.value = name;
pageParams.value.current = 1;
tableList.value = [];
diff --git a/src/views/Home/HomeLeft/OverviewNext.vue b/src/views/Home/HomeLeft/OverviewNext.vue
index 60e2327..c208113 100644
--- a/src/views/Home/HomeLeft/OverviewNext.vue
+++ b/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">
@@ -56,7 +56,6 @@
const getTableList = () => {
const params = {
nickname: searchText.value,
- is_execute: true,
current: pageParams.value.current,
size: pageParams.value.size
};
@@ -114,7 +113,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;
diff --git a/src/views/Home/HomeLeft/components/MachineNestTotal.vue b/src/views/Home/HomeLeft/components/MachineNestTotal.vue
index 95f3158..2d9e187 100644
--- a/src/views/Home/HomeLeft/components/MachineNestTotal.vue
+++ b/src/views/Home/HomeLeft/components/MachineNestTotal.vue
@@ -41,8 +41,8 @@
getDeviceInfoNum().then((res) => {
if (res.data.code !== 0) return;
let result = res.data.data;
- listNum.value[0].value = result.ex_num;
- listNum.value[1].value = result.leisure_num;
+ listNum.value[0].value = result.leisure_num;
+ listNum.value[1].value = result.ex_num;
listNum.value[2].value = result.offline_num;
total.value = result.device_num;
});
diff --git a/src/views/Home/useMapAggregation/DevicePopUpBox.vue b/src/views/Home/useMapAggregation/DevicePopUpBox.vue
index 84afddb..9431515 100644
--- a/src/views/Home/useMapAggregation/DevicePopUpBox.vue
+++ b/src/views/Home/useMapAggregation/DevicePopUpBox.vue
@@ -58,8 +58,8 @@
const resJob = await getTotalJobNum({ areaCode });
dataObj.value.jobNum = resJob.data.data;
dataObj.value.device_num = res.data.data.device_num;
- list.value[0].value = res.data.data.ex_num;
- list.value[1].value = res.data.data.leisure_num;
+ list.value[0].value = res.data.data.leisure_num;
+ list.value[1].value = res.data.data.ex_num;
list.value[2].value = res.data.data.offline_num;
loading.value = false;
});
diff --git a/src/views/Home/useMapAggregation/useMapAggregation.js b/src/views/Home/useMapAggregation/useMapAggregation.js
index 1f6aeec..53ccd4a 100644
--- a/src/views/Home/useMapAggregation/useMapAggregation.js
+++ b/src/views/Home/useMapAggregation/useMapAggregation.js
@@ -199,17 +199,17 @@
scalingJudgment.forEach(item => item.show && (item.outline = outlineGJson))
const [longitude, latitude] = outlineGJson.features[0].properties.centroid
const height = scalingJudgment[(hierarchy.length - 3) * (-1)].height
- setCenterPosition({longitude, latitude,height})
+ setCenterPosition({ longitude, latitude, height })
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)
- if (!userAreaPosition.value.longitude){
- store.commit('setUserAreaPosition', position)
- }
+ if (!userAreaPosition.value.longitude) {
+ store.commit('setUserAreaPosition', 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,
@@ -262,28 +261,24 @@
// 渲染轮廓
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 // 关闭原生轮廓
- // 创建独立折线作为轮廓
- const positions = entity.polygon.hierarchy.getValue().positions
- viewer.entities.add({
- polyline: {
- positions: positions,
- width: 5, // 直接设置宽度
- material: new Cesium.PolylineGlowMaterialProperty({
- glowPower: 0.5,
- color: Cesium.Color.AQUA,
- }),
- clampToGround: true, // 贴地显示
- },
+ Cesium.GeoJsonDataSource.load(item.outline).then(dataSource => {
+ const entities = dataSource.entities.values
+ entities.forEach((entity, index) => {
+ // 创建独立折线作为轮廓
+ const positions = entity.polygon.hierarchy.getValue().positions
+ viewer.entities.add({
+ id: `aggregation-outline-${index}`,
+ polyline: {
+ positions: positions,
+ width: 5, // 直接设置宽度
+ material: new Cesium.PolylineGlowMaterialProperty({
+ glowPower: 0.5,
+ color: Cesium.Color.AQUA,
+ }),
+ },
+ })
})
})
- })
}
// 聚合机巢
@@ -301,7 +296,7 @@
// 遍历特征并添加实体
featuresList.forEach((feature, index) => {
if (!feature.position) return
- const position = Cesium.Cartesian3.fromDegrees(feature.position[0], feature.position[1],12000)
+ 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
@@ -415,7 +408,7 @@
currentEntity = entity
const entityPosition = entity?.position?._value
if (!entityPosition) return
- if (entityPosition){
+ if (entityPosition) {
positionC3 = entityPosition
}
removeLabel()
@@ -449,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)
diff --git a/src/views/SignMachineNest/MachineLeft/MachineLeft.vue b/src/views/SignMachineNest/MachineLeft/MachineLeft.vue
index f746ffc..1b6e110 100644
--- a/src/views/SignMachineNest/MachineLeft/MachineLeft.vue
+++ b/src/views/SignMachineNest/MachineLeft/MachineLeft.vue
@@ -24,6 +24,7 @@
const goBack = () => {
store.commit('setSingleUavHome', {});
+ store.commit('setFootActiveIndex', 0)
};
</script>
diff --git a/src/views/SignMachineNest/MachineRight/MachineStatus/MachineTableDetails/DeviceJob/DeviceJobDetails/DeviceJobDetailsMap.vue b/src/views/SignMachineNest/MachineRight/MachineStatus/MachineTableDetails/DeviceJob/DeviceJobDetails/DeviceJobDetailsMap.vue
index cef055c..a4df43e 100644
--- a/src/views/SignMachineNest/MachineRight/MachineStatus/MachineTableDetails/DeviceJob/DeviceJobDetails/DeviceJobDetailsMap.vue
+++ b/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'])
diff --git a/src/views/TaskManage/TaskIntermediateContent/AddTask.vue b/src/views/TaskManage/TaskIntermediateContent/AddTask.vue
index 9d23405..451704a 100644
--- a/src/views/TaskManage/TaskIntermediateContent/AddTask.vue
+++ b/src/views/TaskManage/TaskIntermediateContent/AddTask.vue
@@ -11,20 +11,21 @@
<div class="task-contain">
<div class="left">
<div class="search">
- <div class="item">任务名称:<el-input v-model="searchForm.name" placeholder="请输入任务名称"></el-input></div>
- <div class="item"><span style="color: red;">*</span>任务日期:
+ <div class="item"><span class="required">*</span>任务名称:<el-input v-model="searchForm.name" placeholder="请输入任务名称"></el-input></div>
+ <div class="item"><span class="required">*</span>任务日期:
<el-date-picker
- v-model="searchForm.begin_time"
+ v-model="taskData"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
+ :disabled-date="disabledDate"
/>
</div>
<div class="item">任务时间:
- <!-- <el-time-picker
+ <el-time-picker
v-model="searchForm.execute_time_arr"
placeholder="选择时间"
format="HH:mm"
- value-format="HH:mm"/> -->
+ value-format="HH:mm"/>
</div>
<div class="item">选择航线:
<el-select
@@ -46,16 +47,28 @@
</div>
<div class="lines">
<div class="wayline-type">
- <el-radio-group v-model="searchForm.wayline_type">
- <el-radio label="1">单点航线</el-radio>
- <el-radio label="2">智能规划选区</el-radio>
+ <el-radio-group v-model="waylineType" @change="radioChange">
+ <el-radio :value="1" label="单点航线"></el-radio>
+ <el-radio :value="2" label="智能规划选区"></el-radio>
</el-radio-group>
</div>
- <TaskMap class="wayline-map" :wayLineFile="wayLineFile"/>
+ <TaskMap class="wayline-map"
+ :wayLineFile="wayLineFile"
+ :checkedTableData="checkedTableData"
+ :waylineTypeTest="waylineType"
+ @clickPosition="clickSignPosition"
+ @saveWayline="savePlanerWayline"
+ />
</div>
</div>
<div class="right">
- <TaskTable :waylineId="waylineId" :waylineType="waylineType" @update:selected="handleSelected" />
+ <TaskTable ref="taskTableRef"
+ :waylineId="waylineId"
+ :waylineType="waylineType"
+ :singlePoint="singlePoint"
+ :planarPoints="planarPoints"
+ @update:selected="handleSelected"
+ />
<div class="btn">
<el-button type="primary" @click="cancel">取消</el-button>
<el-button type="primary" @click="submitClick">发布</el-button>
@@ -73,14 +86,18 @@
import TaskMap from './TaskMap.vue';
import TaskTable from './TaskTable.vue';
+const emit = defineEmits(['refresh']);
+
const rangDate = ref([]);
// 航线ID
-let waylineId = ref('');
+const waylineId = ref('');
// 航线文件
-let wayLineFile = ref('');
+const wayLineFile = ref('');
// 航线类型
-let waylineType = ref(0);
-
+const waylineType = ref(3);
+// 添加子组件引用
+const taskTableRef = ref(null);
+const taskData = ref('');
const searchForm = reactive({
name: '',
ai_types: [],
@@ -91,8 +108,16 @@
remark: '',
type: waylineType.value,
dock_sns: [],
+ longitude: '',
+ latitude: '',
+ polygon: [],
});
const isShowAddTask = defineModel('show');
+
+// 禁用当天之前的日期
+const disabledDate = (time) => {
+ return time.getTime() < Date.now() - 8.64e7; // 86400000 = 24 * 60 * 60 * 1000
+};
// 获取航线列表数据
const routeOptions = ref([]);
@@ -102,32 +127,98 @@
routeOptions.value = res.data.data;
}
};
+// 关联算法
const algorithmChange = (val) => {
searchForm.ai_types = val;
};
+// 切换航线类型
+const radioChange = (val) => {
+ // 清空选中航线值
+ searchForm.file_id = '';
+ // 航线ID也清空
+ waylineId.value = '';
+ wayLineFile.value = '';
+ waylineType.value = val;
+ searchForm.type = val;
+ // 清空列表数据
+ nextTick(() => {
+ if (taskTableRef.value) {
+ taskTableRef.value.clearTableData();
+ }
+ });
+};
+
// 获取航线文件
const getWayLineFile = async (val) => {
+ searchForm.type = 0;
+ waylineType.value = 0;
waylineId.value = val;
const currentRoute = routeOptions.value.find(item => item.wayline_id === val);
wayLineFile.value = currentRoute.object_key;
};
+
// 获取选中机场列表数据,并且发布
+let checkedTableData = ref([]);
const handleSelected = (val) => {
searchForm.dock_sns = val.map(item => item.device_sn);
+ checkedTableData.value = val;
};
-const emit = defineEmits(['refresh']);
+
+// 地图点击事件 表格重新刷新数据
+let singlePoint = ref({});
+const clickSignPosition = (val) => {
+ singlePoint.value = val;
+ searchForm.longitude = val.longitude;
+ searchForm.latitude = val.latitude;
+};
+
+// 保存面状航线
+let planarPoints = ref([]);
+const savePlanerWayline = (val) => {
+ planarPoints.value = val;
+ const polygonArray = val.map(point => [point.longitude, point.latitude]);
+ searchForm.polygon = polygonArray;
+};
+
// 提交
const submitClick = () => {
- if (!searchForm.begin_time) {
+ if (!taskData.value) {
ElMessage({
message: '请选择任务日期',
type: 'warning'
});
return;
}
- searchForm.end_time = `${searchForm.begin_time} 23:59:59`;
- searchForm.begin_time = `${searchForm.begin_time} 00:00:00`;
+ if (!searchForm.name) {
+ ElMessage({
+ message: '请输入任务名称',
+ type: 'warning'
+ });
+ return;
+ }
+ // 检查任务时间
+ if (searchForm.execute_time_arr) {
+ const now = new Date();
+ const today = now.toDateString();
+ const selectedDate = new Date(taskData.value).toDateString();
+
+ if (today === selectedDate) {
+ const [hours, minutes] = searchForm.execute_time_arr.split(':');
+ const selectedTime = new Date();
+ selectedTime.setHours(parseInt(hours), parseInt(minutes));
+
+ if (selectedTime < now) {
+ ElMessage({
+ message: '任务时间不能小于当前时间',
+ type: 'warning'
+ });
+ return;
+ }
+ }
+ }
+ searchForm.begin_time = `${taskData.value} 00:00:00`;
+ searchForm.end_time = `${taskData.value} 23:59:59`;
createTask(searchForm).then((res) => {
if (res.data.code === 0) {
@@ -152,7 +243,9 @@
rangDate.value = [];
waylineId.value = '';
wayLineFile.value = '';
+ taskData.value = '';
};
+
onMounted(() => {
getRouteList();
})
@@ -171,7 +264,7 @@
.task-contain {
display: flex;
.left {
- width: 70%;
+ width: 68%;
.search {
display: grid;
grid-template-columns: repeat(3, 1fr);
@@ -179,6 +272,7 @@
gap: 30px; // 增加间距使布局更合理
margin-bottom: 8px; // 添加底部间距
.item {
+ position: relative;
display: flex;
align-items: center;
:deep(.el-input),
@@ -191,11 +285,18 @@
:deep(.el-date-editor.el-input__wrapper) {
width: 200px; // 调整日期选择器宽度
}
+ .required {
+ color: #f56c6c;
+ margin-left: 4px;
+ position: absolute;
+ left: -10px;
+ top: 8px;
+ }
}
}
}
.right {
- width: 30%;
+ width: 32%;
display: flex;
flex-direction: column;
justify-content: space-between;
diff --git a/src/views/TaskManage/TaskIntermediateContent/CurrentTaskDetails.vue b/src/views/TaskManage/TaskIntermediateContent/CurrentTaskDetails.vue
new file mode 100644
index 0000000..107d7e1
--- /dev/null
+++ b/src/views/TaskManage/TaskIntermediateContent/CurrentTaskDetails.vue
@@ -0,0 +1,20 @@
+<!--当前任务详情-->
+<template>
+ <el-dialog
+ modal-class="current-task-details"
+ v-model="isShowCurrentTaskDetails"
+ title="当前任务详情"
+ :width="pxToRem(1500)"
+ :close-on-click-modal="false"
+ :destroy-on-close="true">
+ <div class=""></div>
+ </el-dialog>
+</template>
+
+<script setup>
+const isShowCurrentTaskDetails = defineModel('show');
+</script>
+
+<style lang="scss" scoped>
+.current-task-details {}
+</style>
\ No newline at end of file
diff --git a/src/views/TaskManage/TaskIntermediateContent/TaskIntermediateContent.vue b/src/views/TaskManage/TaskIntermediateContent/TaskIntermediateContent.vue
index f6b3f34..b7c13ed 100644
--- a/src/views/TaskManage/TaskIntermediateContent/TaskIntermediateContent.vue
+++ b/src/views/TaskManage/TaskIntermediateContent/TaskIntermediateContent.vue
@@ -5,7 +5,7 @@
<el-table :data="jobListData" style="width: 100%" height="calc(100vh - 180px)">
<el-table-column label="序号" width="60">
<template #default="scope">
- {{ (jobListParams.page - 1) * jobListParams.limit + scope.$index + 1 }}
+ {{ (jobListParams.current - 1) * jobListParams.size + scope.$index + 1 }}
</template>
</el-table-column>
<el-table-column prop="job_info_num" label="任务编号" />
@@ -13,7 +13,7 @@
<el-table-column prop="dept_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 label="任务状态" >
<template #default="scope">
{{ scope.row.status ? getStatusText(scope.row.status) : '' }}
</template>
@@ -30,8 +30,8 @@
</el-table>
<div class="pagination">
<el-pagination
- v-model:current-page="jobListParams.page"
- v-model:page-size="jobListParams.limit"
+ v-model:current-page="jobListParams.current"
+ v-model:page-size="jobListParams.size"
:page-sizes="[10, 20, 30, 50]"
layout="total, sizes, prev, pager, next"
:total="total"
@@ -40,17 +40,21 @@
/>
</div>
</div>
+ <!-- 添加任务 -->
<AddTask v-model:show="isShowAddTask" @refresh="searchClick"/>
+ <!-- 当前任务详情 -->
+ <CurrentTaskDetails v-model:show="isShowCurrentTaskDetails"/>
</template>
<script setup>
import SearchBox from '../SearchBox.vue';
import AddTask from './AddTask.vue';
+import CurrentTaskDetails from './CurrentTaskDetails.vue';
import { jobList } from '@/api/home/task';
const jobListParams = reactive({
- page: 1,
- limit: 10,
+ current: 1,
+ size: 10,
searchParams:{}
});
const jobListData = ref([]);
@@ -73,42 +77,31 @@
4: '已取消',
5: '执行失败'
};
- return statusMap[status] || '未知';
+ return statusMap[status] || '-';
};
-// 状态标签类型
-const getStatusType = (status) => {
- const typeMap = {
- 1: 'info',
- 2: 'warning',
- 3: 'success',
- 4: '',
- 5: 'danger'
- };
- return typeMap[status] || '';
-};
-
-// 查看详情
+// 查看当前任务详情
+let isShowCurrentTaskDetails = ref(false);
const handleDetail = (row) => {
- console.log('查看详情', row);
+ isShowCurrentTaskDetails.value = true;
};
// 分页大小改变
const handleSizeChange = (val) => {
- jobListParams.limit = val;
+ jobListParams.size = val;
getJobList();
};
// 页码改变
const handleCurrentChange = (val) => {
- jobListParams.page = val;
+ jobListParams.current = val;
getJobList();
};
// 传参查询条件
const searchClick = (params) => {
- jobListParams.page = 1;
- jobListParams.limit = 10;
+ jobListParams.current = 1;
+ jobListParams.size = 10;
jobListParams.searchParams = params;
getJobList();
};
diff --git a/src/views/TaskManage/TaskIntermediateContent/TaskMap.vue b/src/views/TaskManage/TaskIntermediateContent/TaskMap.vue
index fe8cdf7..b360db9 100644
--- a/src/views/TaskManage/TaskIntermediateContent/TaskMap.vue
+++ b/src/views/TaskManage/TaskIntermediateContent/TaskMap.vue
@@ -1,5 +1,5 @@
<template>
- <div id="taskMap" />
+ <div id="taskMap"></div>
</template>
<script setup>
import * as Cesium from 'cesium'
@@ -12,9 +12,29 @@
import EndPointicon from '@/assets/images/EndPointicon.png';
import uavImg from '@/assets/images/home/useUavHome/uavImg.png'
import { getCenterPoint } from '@/utils/cesium/mapUtil'
+import { getWaylineByArea } from '@/api/home/task';
+import { useStore } from 'vuex';
-const props = defineProps(['wayLineFile'])
+const store = useStore();
+const userAreaPosition = computed(() => store.state.home.userAreaPosition);
+// 声明事件
+const emit = defineEmits(['clickPosition', 'saveWayline']);
+
+const props = defineProps({
+ wayLineFile: {
+ type: String,
+ default: ''
+ },
+ checkedTableData: {
+ type: Array,
+ default: () => []
+ },
+ waylineTypeTest: {
+ type: Number,
+ default: 3
+ }
+})
const imageryProvider_ammapSL = new Cesium.UrlTemplateImageryProvider({
url: 'https://webrd02.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}',
@@ -26,8 +46,18 @@
maximumLevel: 18,
tilingScheme: new AmapMercatorTilingScheme(),
credit: 'amap_SL',
-})
-let viewer = null
+});
+
+let viewer = null;
+let currentEntity = null;
+let connectLines = []; // 存储连接线实体
+let polygonPoints = []; // 存储多边形的点
+let polygonEntity = null; // 存储多边形实体
+let existingEntity = null; // 后端返回数据生成得面状线
+// 添加变量跟踪当前菜单
+let currentMenu = null;
+
+
const init = () => {
viewer = new Viewer('taskMap', {
terrain: Terrain.fromWorldTerrain(),
@@ -46,22 +76,214 @@
})
viewer.imageryLayers.addImageryProvider(imageryProvider_ammapSL)
viewer.scene.morphTo2D(0);
+ //设置默认点
+ const { longitude = 115.763819, latitude = 28.787374, height = 10 } = userAreaPosition.value || {};
+ viewer.camera.setView({
+ destination: Cartesian3.fromDegrees(longitude, latitude, height),
+ });
}
-// 渲染线和点
+// 单点左键点击事件
+const singlePointLeftClick = () => {
+ viewer.screenSpaceEventHandler.setInputAction((click) => {
+ if (props.waylineTypeTest !== 1) return
+ const cartesian = viewer.camera.pickEllipsoid(
+ click.position,
+ viewer.scene.globe.ellipsoid
+ );
+ if (cartesian) {
+ // 清除之前的实体
+ viewer.entities.removeAll();
+
+ // 添加新点
+ const point = viewer.entities.add({
+ position: cartesian,
+ point: {
+ pixelSize: 10,
+ color: Cesium.Color.WHITE
+ }
+ });
+
+ // 转换坐标
+ const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
+ const longitude = Cesium.Math.toDegrees(cartographic.longitude);
+ const latitude = Cesium.Math.toDegrees(cartographic.latitude);
+
+ // 更新当前实体引用
+ currentEntity = point;
+
+ // 发送坐标
+ emit('clickPosition', { longitude, latitude });
+ }
+ }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
+};
+
+// 智能规划航线
+const intelligentPlanning = () => {
+ // 添加点击事件监听
+ viewer.screenSpaceEventHandler.setInputAction((click) => {
+ if (props.waylineTypeTest !== 2) return
+ const cartesian = viewer.camera.pickEllipsoid(
+ click.position,
+ viewer.scene.globe.ellipsoid
+ );
+
+ if (cartesian) {
+ // 添加新点
+ const point = viewer.entities.add({
+ position: cartesian,
+ point: {
+ pixelSize: 10,
+ color: Cesium.Color.WHITE
+ }
+ });
+
+ // 存储点位
+ polygonPoints.push(cartesian);
+
+ // 当点击超过2个点时绘制多边形
+ if (polygonPoints.length > 2) {
+ // 移除旧的多边形
+ if (polygonEntity) {
+ viewer.entities.remove(polygonEntity);
+ }
+
+ // 创建新的多边形
+ polygonEntity = viewer.entities.add({
+ polygon: {
+ hierarchy: new Cesium.PolygonHierarchy(polygonPoints),
+ // material: new Cesium.Color.fromBytes(212, 46, 32, 100),
+ material: new Cesium.Color(0, 0.5, 1, 0.3), // 蓝色
+ outline: true,
+ // outlineColor: new Cesium.Color.fromBytes(212, 46, 32, 255),
+ outlineColor: new Cesium.Color(0, 0.5, 1, 1), // 蓝
+ outlineWidth: 2,
+ height: 0, // Set explicit height
+ heightReference: Cesium.HeightReference.NONE // Disable terrain clamping
+ }
+ });
+ }
+
+ // 转换坐标并发送
+ const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
+ const longitude = Cesium.Math.toDegrees(cartographic.longitude);
+ const latitude = Cesium.Math.toDegrees(cartographic.latitude);
+ // emit('clickPosition', cartographic);
+
+ }
+ }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
+ // 修改右键点击事件,添加菜单
+ viewer.screenSpaceEventHandler.setInputAction((movement) => {
+ if (props.waylineTypeTest !== 2) return
+ if (polygonPoints.length > 2) {
+ // 清除之前的菜单
+ if (currentMenu) {
+ document.body.querySelectorAll('.context-menu').forEach(menu => menu.remove());
+ }
+
+ const menuContainer = document.createElement('div');
+ menuContainer.className = 'context-menu';
+
+ // 获取地图容器
+ const mapContainer = document.getElementById('taskMap');
+ // 使用鼠标右键点击的实际位置
+ menuContainer.style.position = 'absolute';
+ menuContainer.style.left = `${movement.position.x}px`;
+ menuContainer.style.top = `${movement.position.y}px`;
+ menuContainer.style.zIndex = '1000';
+
+ menuContainer.innerHTML = `
+ <div class="menu-item" id="saveWayline">保存航线</div>
+ <div class="menu-item" id="cancelDraw">取消绘制</div>
+ `;
+
+ mapContainer.appendChild(menuContainer);
+ currentMenu = menuContainer;
+
+ // 添加全局点击事件监听
+ const handleClickOutside = (e) => {
+ if (!menuContainer) return;
+ const isClickInside = menuContainer.contains(e.target);
+ if (!isClickInside) {
+ menuContainer.remove();
+ document.removeEventListener('mousedown', handleClickOutside);
+ }
+ };
+
+ // 延迟添加事件监听,避免右键点击立即触发
+ setTimeout(() => {
+ document.addEventListener('mousedown', handleClickOutside);
+ }, 100);
+
+ // 菜单按钮点击事件
+ document.getElementById('saveWayline').onclick = () => {
+ const coordinates = polygonPoints.map(point => {
+ const cartographic = Cesium.Cartographic.fromCartesian(point);
+ return {
+ longitude: Cesium.Math.toDegrees(cartographic.longitude),
+ latitude: Cesium.Math.toDegrees(cartographic.latitude)
+ };
+ });
+ emit('saveWayline', coordinates);
+ saveWaylineByArea(coordinates);
+ mapContainer.removeChild(menuContainer);
+ };
+
+ document.getElementById('cancelDraw').onclick = () => {
+ polygonPoints = [];
+ viewer.entities.removeAll();
+ mapContainer.removeChild(menuContainer);
+ };
+ }
+ }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
+};
+
+// 保存航线并且获取线
+const saveWaylineByArea = (dataValue) => {
+ const polygonArray = dataValue.map(point => [point.longitude, point.latitude]);
+ getWaylineByArea({ type: 2, polygon: polygonArray }).then(res => {
+ if (res.data.code !== 0) retrun;
+ drawResultWayline(res.data.data);
+ });
+}
+
+// 绘制后端生成得面状线
+const drawResultWayline = (dataValue) => {
+ // 先检查并删除已存在的航线
+ existingEntity = viewer.entities.getById('result_wayline');
+ if (existingEntity) {
+ viewer.entities.remove(existingEntity);
+ }
+ const cartesian3List = ref([]);
+ dataValue.forEach((lnglat) => {
+ const cartesian3 = Cesium.Cartesian3.fromDegrees(
+ Number(lnglat.x),
+ Number(lnglat.y),
+ Number(100), // 默认100
+ )
+ cartesian3List.value.push(cartesian3)
+ });
+ const setting = {
+ id: 'result_wayline',
+ polyline: {
+ width: 2,
+ positions: cartesian3List.value,
+ material: Cesium.Color.CHARTREUSE,
+ },
+ }
+ existingEntity = viewer?.entities.add({
+ polyline: setting.polyline,
+ id: setting.id,
+ })
+};
+
+// 选中航线时调用 渲染线和点 type = 0
const renderingLine = lineObj => {
const positions = lineObj.Placemark.map(item => {
const [lon, lat] = item.Point.coordinates.split(',')
return Cartesian3.fromDegrees(Number(lon), Number(lat))
})
- // viewer.entities.add({
- // position: positions[0],
- // billboard: {
- // image: new Cesium.ConstantProperty(uavImg),
- // width: 24,
- // height: 24,
- // },
- // });
+
viewer.entities.add({
polyline: {
width: 4,
@@ -75,24 +297,26 @@
clampToGround: false,
},
})
- console.log('positions',positions)
+
positions.forEach((point, index) => {
let setting = {};
- if (index === 0) {
- //TODO
- } else if (index === 1) {
- setting = {
- position: point,
- id: `point_${index}`,
- billboard: {
- image: Startingpointicon,
- outlineWidth: 0,
- width: 20,
- height: 20,
- scale: 1.0,
- },
- }
- } else if (index === positions.length - 1) {
+ // if (index === 0) {
+ // //TODO
+ // }
+ // else if (index === 1) {
+ // setting = {
+ // position: point,
+ // id: `point_${index}`,
+ // billboard: {
+ // image: Startingpointicon,
+ // outlineWidth: 0,
+ // width: 20,
+ // height: 20,
+ // scale: 1.0,
+ // },
+ // }
+ // } else
+ if (index === positions.length - 1) {
setting = {
position: point,
id: `point_${index}`,
@@ -109,7 +333,7 @@
position: point,
id: `point_${index}`,
label: {
- text: `${index}`,
+ text: `${index+1}`,
font: 'bold 14px serif',
style: Cesium.LabelStyle.FILL,
verticalOrigin: Cesium.VerticalOrigin.CENTER, // 垂直居中
@@ -144,25 +368,11 @@
})
}
-// const drawLine = () => {
-// console.log('尽量么');
-// // props.wayLineFile.way_lines.forEach(item => {
-// // let prexUrl = ref(import.meta.env.VITE_APP_AIRLINE_URL+ props.wayLineFile);
-// let prexUrl2 = ref('https://wrj.shuixiongit.com/minio/cloud-bucket/wayline/20241224/wayline_1735032173222.kmz');
-
-// analyzeKmzFile(`${prexUrl2.value}?_t=${new Date().getTime()}`).then(async res => {
-// const templateXML = await res.fileInfoObj['wpmz/template.kml']
-// const templateXMLJSON = XMLToJSON(templateXML)?.['Document']
-// const templateXMLObj = removeTextKey(templateXMLJSON.Folder)
-// renderingLine(templateXMLObj)
-// })
-// // })
-// }
// 异步解析kmz文件
const analysis = async url => {
return new Promise(async resolve => {
- const res = await analyzeKmzFile(`${url}?_t=${new Date().getTime()}`)
+ const res = await analyzeKmzFile(`${url}?_t=${new Date().getTime()}`);
const templateXML = await res.fileInfoObj['wpmz/template.kml']
const templateXMLJSON = XMLToJSON(templateXML)?.['Document']
const templateXMLObj = removeTextKey(templateXMLJSON.Folder)
@@ -174,37 +384,239 @@
const drawLine = async () => {
let prexUrl = ref(import.meta.env.VITE_APP_AIRLINE_URL+ props.wayLineFile);
const res = await analysis(prexUrl.value);
+ if (!res.Placemark.length) return
renderingLine(res);
const points = res.Placemark.map(item => item.Point.coordinates.split(','))
flyToPoints(points)
}
-watch(() => props.wayLineFile, async (newValue, oldValue) => {
- if(newValue){
- await removeMap();
- await init();
+// 选择航线时,根据选择机巢连线
+const selectLineFile = (newVal) => {
+ // 清除之前的实体
+ // viewer.entities.removeAll();
+ // 重新绘制航线
+ // if (props.wayLineFile) {
+ // drawLine();
+ // }
+ // 添加选中点和连接线
+ const positions = newVal.map(item => {
+ const position = Cartesian3.fromDegrees(Number(item.longitude), Number(item.latitude));
+ // 添加机巢点
+ viewer.entities.add({
+ position: position,
+ billboard: {
+ image: new Cesium.ConstantProperty(uavImg),
+ width: 24,
+ height: 24,
+ },
+ });
+ return position;
+ });
+
+ // 添加连接线
+ if (positions.length > 1) {
+ viewer.entities.add({
+ polyline: {
+ positions: positions,
+ width: 2,
+ material: new Cesium.PolylineDashMaterialProperty({
+ color: Cesium.Color.RED,
+ dashLength: 8.0
+ })
+ }
+ });
+ }
+ // 飞到中心点
+ const lngLatArr = newVal.map(item => [item.longitude, item.latitude]);
+ flyToPoints(lngLatArr);
+};
+
+// 单个点生成选择多个机巢生成航线
+const singlePointLines = (newVal) => {
+ // 清除之前的连接线
+ connectLines.forEach(line => viewer.entities.remove(line));
+ connectLines = [];
+
+ if (currentEntity) {
+ // 获取当前点的位置
+ const currentPosition = currentEntity.position.getValue();
+
+ // 为每个选中的机巢创建点和连接线
+ newVal.forEach(item => {
+ // 创建机巢点
+ const nestPosition = Cartesian3.fromDegrees(
+ Number(item.longitude),
+ Number(item.latitude)
+ );
+ viewer.entities.add({
+ position: nestPosition,
+ billboard: {
+ image: new Cesium.ConstantProperty(uavImg),
+ width: 24,
+ height: 24,
+ },
+ });
+
+ // 创建连接线
+ const line = viewer.entities.add({
+ polyline: {
+ positions: [currentPosition, nestPosition],
+ width: 2,
+ material: new Cesium.PolylineDashMaterialProperty({
+ color: Cesium.Color.RED,
+ dashLength: 8.0
+ })
+ }
+ });
+ connectLines.push(line);
+ });
+
+ // 飞到所有点的中心位置
+ const lngLatArr = newVal.map(item => [item.longitude, item.latitude]);
+ flyToPoints(lngLatArr);
+ }
+};
+
+// 智慧规划航线-面状航线
+const planarPointsLines = (newVal) => {
+ // 如果存在面状航线
+ if (existingEntity) {
+ const waylinePositions = existingEntity.polyline.positions.getValue();
+
+ newVal.forEach(item => {
+ // 创建机巢点
+ const nestPosition = Cartesian3.fromDegrees(
+ Number(item.longitude),
+ Number(item.latitude)
+ );
+
+ // 添加机巢图标
+ viewer.entities.add({
+ position: nestPosition,
+ billboard: {
+ image: new Cesium.ConstantProperty(uavImg),
+ width: 24,
+ height: 24,
+ },
+ });
+
+ // 找到最近的航线点并连线
+ let minDistance = Number.MAX_VALUE;
+ let closestPosition = null;
+
+ waylinePositions.forEach(waylinePos => {
+ const distance = Cartesian3.distance(nestPosition, waylinePos);
+ if (distance < minDistance) {
+ minDistance = distance;
+ closestPosition = waylinePos;
+ }
+ });
+
+ // 创建连接线
+ if (closestPosition) {
+ const line = viewer.entities.add({
+ polyline: {
+ positions: [nestPosition, closestPosition],
+ width: 2,
+ material: new Cesium.PolylineDashMaterialProperty({
+ color: Cesium.Color.CHARTREUSE,
+ dashLength: 8.0
+ })
+ }
+ });
+ connectLines.push(line);
+ }
+ });
+
+ // 飞到所有点的中心位置
+ const lngLatArr = newVal.map(item => [item.longitude, item.latitude]);
+ flyToPoints(lngLatArr);
+ }
+};
+
+// 监听选择航线文件事件
+watch(() => props.wayLineFile, async (newVal, oldValue) => {
+ await removeMap();
+ if(newVal){
await drawLine();
}
-})
+}, { deep: true });
+
+// 监听表格选中数据变化
+watch(() => props.checkedTableData, (newVal) => {
+ if (newVal.length > 0 && props.waylineTypeTest === 0) {
+ selectLineFile(newVal);
+ } else if (newVal.length > 0 && props.waylineTypeTest === 1) {
+ singlePointLines(newVal);
+ } else if (newVal.length > 0 && props.waylineTypeTest === 2) {
+ planarPointsLines(newVal);
+ }
+}, { deep: true });
+
+// 监听航线类型
+watch(() => props.waylineTypeTest, async (newVal) => {
+ await removeMap();
+ if (newVal === 1) {
+ await singlePointLeftClick();
+ } else if (newVal === 2) {
+ await intelligentPlanning();
+ }
+}, { deep: true });
+
+const removeEvent = () => {
+ // 清除事件监听器
+ viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
+ viewer.screenSpaceEventHandler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK);
+};
const removeMap = () => {
- viewer.entities.removeAll()
- viewer.destroy()
+ // 清除所有实体
+ if (viewer) {
+ // 清除连接线
+ connectLines.forEach(line => viewer.entities.remove(line));
+ connectLines = [];
+
+ // 清除多边形点和实体
+ polygonPoints = [];
+ if (polygonEntity) {
+ viewer.entities.remove(polygonEntity);
+ }
+
+ // 清除当前实体和面状航线
+ if (currentEntity) {
+ viewer.entities.remove(currentEntity);
+ }
+ if (existingEntity) {
+ viewer.entities.remove(existingEntity);
+ }
+
+ viewer.entities.removeAll();
+
+ // 重置所有变量
+ currentEntity = null;
+ polygonEntity = null;
+ existingEntity = null;
+ }
}
onBeforeUnmount(() => {
- removeMap()
+ removeMap();
+ removeEvent();
+ // 移除所有实体并销毁viewer
+ viewer.destroy();
+ viewer = null;
})
onMounted(() => {
nextTick(() => {
init()
})
-})
+});
</script>
<style scoped lang="scss">
#taskMap {
+ position: relative;
height: 100%;
:deep() {
@@ -232,5 +644,27 @@
display: none;
}
}
+
+:deep(.context-menu) {
+ position: absolute;
+ background: rgba(0, 21, 41, 0.9);;
+ border-radius: 4px;
+ padding: 8px 0;
+ min-width: 120px;
+ box-shadow: 0 2px 12px rgba(0, 0, 0, 0.3);
+
+ .menu-item {
+ padding: 8px 16px;
+ color: #fff;
+ cursor: pointer;
+ transition: all 0.3s;
+ font-size: 14px;
+
+ &:hover {
+ background: rgba(255, 255, 255, 0.1);
+ }
+ }
+ }
}
+
</style>
diff --git a/src/views/TaskManage/TaskIntermediateContent/TaskTable.vue b/src/views/TaskManage/TaskIntermediateContent/TaskTable.vue
index 7b349c3..9129de8 100644
--- a/src/views/TaskManage/TaskIntermediateContent/TaskTable.vue
+++ b/src/views/TaskManage/TaskIntermediateContent/TaskTable.vue
@@ -1,7 +1,9 @@
<template>
<div class="table-container">
- <el-table :data="tableData" style="width: 100%" height="400" @selection-change="handleSelectionChange">
- <el-table-column type="selection" width="55" />
+ <el-table :data="tableData" style="width: 100%" height="400"
+ @selection-change="handleSelectionChange"
+ @select="handleSelect">
+ <el-table-column type="selection" width="55" :selectable="checkSelectable" />
<el-table-column type="index" label="序号" width="60" />
<el-table-column prop="nickname" label="机巢名称" />
<el-table-column prop="estimated_arrival_time" label="预计到达时间" />
@@ -9,8 +11,8 @@
</el-table>
<div class="pagination">
<el-pagination
- v-model:current-page="pageParams.page"
- v-model:page-size="pageParams.limit"
+ v-model:current-page="pagingParams.page"
+ v-model:page-size="pagingParams.size"
:page-sizes="[10, 20, 30]"
layout="total, sizes, prev, pager, next"
:total="total"
@@ -31,59 +33,138 @@
},
waylineType: {
type: Number,
- default: 0
+ default: 3
+ },
+ singlePoint: {
+ type: Object,
+ default: () => ({})
+ },
+ planarPoints: {
+ type: Array,
+ default: () => ([])
}
});
// 选中的数据
const selectedRows = ref([]);
// 分页参数
-let pageParams = reactive({
+let pageParams = ref({
wayline_id: props.waylineId,
type: props.waylineType,
+ longitude: props.singlePoint.longitude,
+ latitude: props.singlePoint.latitude,
+ polygon: [],
+});
+let pagingParams = ref({
page: 1,
- limit: 10
+ size: 10
});
// 获取可用机巢列表数据
const total = ref(0);
const tableData = ref([]);
const getNestList = async () => {
tableData.value = [];
- const res = await getFlyingNestBy(pageParams);
-
+ const res = await getFlyingNestBy(pageParams.value, pagingParams.value);
if (res.data.code === 0) {
- console.log(res.data.data, '哒哒哒');
tableData.value = res.data.data;
}
};
// 分页大小改变
const handleSizeChange = (val) => {
- pageParams.limit = val;
+ pagingParams.value.size = val;
getNestList();
};
// 页码改变
const handleCurrentChange = (val) => {
- pageParams.page = val;
+ pagingParams.value.page = val;
getNestList();
};
+
+// 刷新数据
+const refreshData = () => {
+ tableData.value = [];
+ pagingParams.value.page = 1;
+ getNestList();
+};
+
const emit = defineEmits(['update:selected']);
const handleSelectionChange = (val) => {
- selectedRows.value = val;
- // 可以通过emit将选中数据传给父组件
- emit('update:selected', val);
+ // 如果不是智能规划选区模式,才更新所有选中数据
+ if (props.waylineType !== 2) {
+ selectedRows.value = val;
+ emit('update:selected', val);
+ }
};
+
+// 控制表格行是否可选
+const checkSelectable = (row) => {
+ if (props.waylineType === 2) {
+ // 如果已经有选中的行,且当前行未被选中,则不允许选择
+ return selectedRows.value.length === 0 || selectedRows.value.some(selected => selected.device_sn === row.device_sn);
+ }
+ return true;
+};
+
+// 处理单行选择
+const handleSelect = (selection, row) => {
+ if (props.waylineType === 2) {
+ // 如果是智能规划选区模式,确保只能选中一行
+ if (selection.length > 1) {
+ // 保留最后选中的那一行
+ const lastSelected = selection[selection.length - 1];
+ selectedRows.value = [lastSelected];
+ // 触发更新事件
+ emit('update:selected', selectedRows.value);
+ }
+ }
+};
+
// 监听航线ID变化
watch(() => props.waylineId, (newVal) => {
- pageParams.wayline_id = newVal;
+ pageParams.value.type = 0;
if (newVal) {
- pageParams.page = 1;
- getNestList();
+ pageParams.value.wayline_id = newVal;
+ refreshData();
+ } else {
+ // 数据为空时,清除列表数据
+ tableData.value = [];
}
-}, { immediate: true });
-onMounted(() => {
- // getNestList();
+}, { deep: true });
+
+// 监听单点返回的数据
+watch(() => props.singlePoint, (newVal) => {
+ pageParams.value.wayline_id = '';
+ pageParams.value.type = 1;
+ if (newVal && newVal.latitude && newVal.longitude) {
+ pageParams.value.latitude = newVal.latitude;
+ pageParams.value.longitude = newVal.longitude;
+ refreshData();
+ }
+}, { deep: true });
+
+// 监听面状航线返回的数据
+watch(() => props.planarPoints, (newVal) => {
+ pageParams.value.wayline_id = '';
+ pageParams.value.type = 2;
+ if (newVal && newVal.length > 0) {
+ const polygonArray = newVal.map(point => [point.longitude, point.latitude]);
+ pageParams.value.polygon = polygonArray;
+ refreshData();
+ }
+}, { deep: true });
+
+// 暴露给父组件的方法
+const clearTableData = () => {
+ tableData.value = [];
+};
+defineExpose({
+ clearTableData
});
+
+
+onMounted(() => {});
+
</script>
<style lang="scss" scoped>
--
Gitblit v1.9.3