From a119e5a86c626cc6175cbfdbb236c34270fbe90c Mon Sep 17 00:00:00 2001
From: 罗广辉 <guanghui.luo@foxmail.com>
Date: Mon, 07 Apr 2025 20:41:56 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/master'

---
 .env.development                                                         |    3 
 package-lock.json                                                        |   81 +++++
 src/api/home/task.js                                                     |   31 ++
 src/assets/images/custom-point.png                                       |    0 
 src/views/TaskManage/SearchBox.vue                                       |   46 ++-
 src/views/TaskManage/TaskIntermediateContent/TaskMap.vue                 |  236 ++++++++++++++++++
 src/views/TaskManage/TaskIntermediateContent/AddTask.vue                 |  194 ++++++++++++++
 src/assets/images/EndPointicon.png                                       |    0 
 src/views/TaskManage/TaskIntermediateContent/TaskIntermediateContent.vue |   10 
 src/views/TaskManage/TaskIntermediateContent/TaskTable.vue               |   86 ++++++
 src/views/TaskManage/components/TaskAlgorithmBusiness.vue                |   60 ++++
 src/assets/images/Startingpointicon.png                                  |    0 
 12 files changed, 721 insertions(+), 26 deletions(-)

diff --git a/.env.development b/.env.development
index e59d6d9..b3359ea 100644
--- a/.env.development
+++ b/.env.development
@@ -8,3 +8,6 @@
 # ws地址
 VITE_APP_WS_API_URL = wss://wrj.shuixiongit.com/drone-wss/api/v1/ws
 
+# 航线文件地址
+VITE_APP_AIRLINE_URL = https://wrj.shuixiongit.com/minio/cloud-bucket
+
diff --git a/package-lock.json b/package-lock.json
index 1d1ff4b..603d9d4 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -28,6 +28,7 @@
         "js-base64": "^3.7.4",
         "js-cookie": "^3.0.0",
         "js-md5": "^0.7.3",
+        "jszip": "^3.10.1",
         "lodash": "^4.17.21",
         "nprogress": "^0.2.0",
         "postcss-pxtorem": "^6.1.0",
@@ -3367,6 +3368,11 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/core-util-is": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+      "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
+    },
     "node_modules/cors": {
       "version": "2.8.5",
       "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
@@ -4878,6 +4884,11 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/immediate": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+      "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
+    },
     "node_modules/immer": {
       "version": "9.0.21",
       "resolved": "https://registry.npmmirror.com/immer/-/immer-9.0.21.tgz",
@@ -4898,8 +4909,7 @@
     "node_modules/inherits": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
-      "dev": true
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
     },
     "node_modules/inherits-browser": {
       "version": "0.1.0",
@@ -5387,8 +5397,7 @@
     "node_modules/isarray": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
-      "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
-      "dev": true
+      "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
     },
     "node_modules/isobject": {
       "version": "2.1.0",
@@ -5462,6 +5471,49 @@
         "graceful-fs": "^4.1.6"
       }
     },
+    "node_modules/jszip": {
+      "version": "3.10.1",
+      "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
+      "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
+      "dependencies": {
+        "lie": "~3.3.0",
+        "pako": "~1.0.2",
+        "readable-stream": "~2.3.6",
+        "setimmediate": "^1.0.5"
+      }
+    },
+    "node_modules/jszip/node_modules/pako": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+      "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
+    },
+    "node_modules/jszip/node_modules/readable-stream": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+      "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+      "dependencies": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.3",
+        "isarray": "~1.0.0",
+        "process-nextick-args": "~2.0.0",
+        "safe-buffer": "~5.1.1",
+        "string_decoder": "~1.1.1",
+        "util-deprecate": "~1.0.1"
+      }
+    },
+    "node_modules/jszip/node_modules/safe-buffer": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+    },
+    "node_modules/jszip/node_modules/string_decoder": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+      "dependencies": {
+        "safe-buffer": "~5.1.0"
+      }
+    },
     "node_modules/kdbush": {
       "version": "4.0.2",
       "resolved": "https://registry.npmjs.org/kdbush/-/kdbush-4.0.2.tgz",
@@ -5485,6 +5537,14 @@
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/lerc/-/lerc-2.0.0.tgz",
       "integrity": "sha512-7qo1Mq8ZNmaR4USHHm615nEW2lPeeWJ3bTyoqFbd35DLx0LUH7C6ptt5FDCTAlbIzs3+WKrk5SkJvw8AFDE2hg=="
+    },
+    "node_modules/lie": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+      "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+      "dependencies": {
+        "immediate": "~3.0.5"
+      }
     },
     "node_modules/loader-utils": {
       "version": "1.4.2",
@@ -6415,6 +6475,11 @@
         "node": ">=6"
       }
     },
+    "node_modules/process-nextick-args": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+    },
     "node_modules/protobufjs": {
       "version": "7.4.0",
       "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.4.0.tgz",
@@ -6970,6 +7035,11 @@
       "engines": {
         "node": ">=0.10.0"
       }
+    },
+    "node_modules/setimmediate": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+      "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
     },
     "node_modules/side-channel": {
       "version": "1.1.0",
@@ -8300,8 +8370,7 @@
     "node_modules/util-deprecate": {
       "version": "1.0.2",
       "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
-      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
-      "dev": true
+      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
     },
     "node_modules/vary": {
       "version": "1.1.2",
diff --git a/src/api/home/task.js b/src/api/home/task.js
index 171409c..b26b7b2 100644
--- a/src/api/home/task.js
+++ b/src/api/home/task.js
@@ -76,4 +76,35 @@
 	})
 }
 
+// 获取航线列表
+export const getWaylineList = (params) => {
+	return request({
+		url: `/drone-device-core/wayline/api/v1/workspaces/getWaylineFileListByArea`,
+		method: 'get',
+		params
+	})
+}
+
+// 获取可飞行机巢列表
+export const getFlyingNestBy = (data) => {
+	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
+		},
+	})
+}
+
+// 机场发布功能
+export const createTask = (data) => {
+	return request({
+		url: `/drone-device-core/wayline/waylineJobInfo/createTask`,
+		method: 'post',
+		data,
+	})
+}
+
+
 
diff --git a/src/assets/images/EndPointicon.png b/src/assets/images/EndPointicon.png
new file mode 100644
index 0000000..eb54b86
--- /dev/null
+++ b/src/assets/images/EndPointicon.png
Binary files differ
diff --git a/src/assets/images/Startingpointicon.png b/src/assets/images/Startingpointicon.png
new file mode 100644
index 0000000..e79d9a6
--- /dev/null
+++ b/src/assets/images/Startingpointicon.png
Binary files differ
diff --git a/src/assets/images/custom-point.png b/src/assets/images/custom-point.png
new file mode 100644
index 0000000..c997c99
--- /dev/null
+++ b/src/assets/images/custom-point.png
Binary files differ
diff --git a/src/views/TaskManage/SearchBox.vue b/src/views/TaskManage/SearchBox.vue
index 181cfd9..c96de38 100644
--- a/src/views/TaskManage/SearchBox.vue
+++ b/src/views/TaskManage/SearchBox.vue
@@ -53,9 +53,10 @@
           />
         </el-form-item>
         <el-form-item label="任务算法">
-          <el-select v-model="searchForm.ai_type" placeholder="请选择算法" clearable>
+          <!-- <el-select v-model="searchForm.ai_type" placeholder="请选择算法" clearable>
             <el-option v-for="item in taskAlgorithm" :key="item.id" :label="item.dictValue" :value="item.dictKey" />
-          </el-select>
+          </el-select> -->
+          <TaskAlgorithmBusiness :showAlgorithm="true" @algorithmChange="algorithmChange"/>
         </el-form-item>
       </div>
       <div class="search-row">
@@ -70,9 +71,10 @@
           </el-select>
         </el-form-item>
         <el-form-item label="任务类型">
-          <el-select v-model="searchForm.industry_type" placeholder="请选择类型" clearable>
+          <!-- <el-select v-model="searchForm.industry_type" placeholder="请选择类型" clearable>
             <el-option v-for="item in taskBusiness" :key="item.id" :label="item.dictValue" :value="item.dictKey" />
-          </el-select>
+          </el-select> -->
+          <TaskAlgorithmBusiness :showBusiness="true" @businessChange="businessChange"/>
         </el-form-item>
       </div>
     </div>
@@ -80,7 +82,8 @@
   </div>
 </template>
 <script setup>
-import { getDictionary, deptsByAreaCode, getDockInfo } from '@/api/home/common';
+import { deptsByAreaCode, getDockInfo } from '@/api/home/common';
+import TaskAlgorithmBusiness from './components/TaskAlgorithmBusiness.vue';
 
 const isExpand = ref(false);
 const toggleExpand = () => {
@@ -109,7 +112,7 @@
   { label: '执行失败', value: 5 }
 ];
 
-const emit = defineEmits(['search']);
+const emit = defineEmits(['search','addTask']);
 
 const handleSearch = () => {
   emit('search', {
@@ -129,24 +132,29 @@
 
 // 新增任务
 const addTask = () => {
-  // 跳转新增任务页面
-  window.open('/#/task/add');
+  emit('addTask');
 };
 
-let taskAlgorithm = ref([]);
-let taskBusiness = ref([]);
+// let taskAlgorithm = ref([]);
+// let taskBusiness = ref([]);
 
 // 请求字典字段
-const requestDictionary = () => {
-  getDictionary('SF,HYLB').then((res) => {
-    if (res.code !== 0) {
-      // 处理数据
-      taskAlgorithm.value = res.data.data['SF'];
-      taskBusiness.value = res.data.data['HYLB'];
-    }
-  });
+// const requestDictionary = () => {
+//   getDictionary('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_type = val;
 };
 
+const businessChange = (val) => {
+  searchForm.industry_type = val;
+};
 
 let deptData = ref([]);
 // 所属部门信息
@@ -183,7 +191,7 @@
 
 
 onMounted(() => {
-  requestDictionary();
+  // requestDictionary();
   getDeptsByAreaCode();
   requestDockInfo();
 });
diff --git a/src/views/TaskManage/TaskIntermediateContent/AddTask.vue b/src/views/TaskManage/TaskIntermediateContent/AddTask.vue
new file mode 100644
index 0000000..a1c0060
--- /dev/null
+++ b/src/views/TaskManage/TaskIntermediateContent/AddTask.vue
@@ -0,0 +1,194 @@
+<!-- 机巢列表详情 -->
+<template>
+	<el-dialog
+		modal-class="add-task"
+		v-model="isShowAddTask"
+		title="新建任务"
+		:width="pxToRem(1500)"
+		:close-on-click-modal="false"
+		:destroy-on-close="true">
+		<!-- <el-divider content-position="left">新建任务</el-divider> -->
+		<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">任务日期:
+						<el-date-picker
+							v-model="searchForm.begin_time"
+							format="YYYY-MM-DD"
+							value-format="YYYY-MM-DD"
+						/>
+					</div>
+					<div class="item">任务时间:
+						<!-- <el-time-picker
+							v-model="searchForm.execute_time_arr"
+							placeholder="选择时间"
+							format="HH:mm"
+							value-format="HH:mm"/> -->
+					</div>
+					<div class="item">选择航线:
+						<el-select
+							v-model="searchForm.file_id"
+							@change="getWayLineFile"
+							placeholder="请选择航线"
+							clearable>
+							<el-option
+								v-for="item in routeOptions"
+								:key="item.wayline_id"
+								:label="item.name"
+								:value="item.wayline_id"/>
+							</el-select>
+						</div>
+						<div class="item">关联算法:
+							<TaskAlgorithmBusiness :showAlgorithm="true" @algorithmChange="algorithmChange"/>
+						</div>
+					<div class="item">任务描述:<el-input v-model="searchForm.remark" placeholder="请输入任务名称"></el-input></div>
+				</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>
+					</div>
+					<TaskMap class="wayline-map" :wayLineFile="wayLineFile"/>
+				</div>
+			</div>
+			<div class="right">
+				<TaskTable :waylineId="waylineId" :waylineType="waylineType" @update:selected="handleSelected" />
+				<div class="btn">
+					<el-button type="primary" @click="cancel">取消</el-button>
+					<el-button type="primary" @click="submitClick">发布</el-button>
+				</div>
+			</div>
+		</div>
+	</el-dialog>
+</template>
+
+<script setup>
+import { pxToRem } from '@/utils/rem';
+import { getWaylineList, createTask } from '@/api/home/task';
+import TaskAlgorithmBusiness from '../components/TaskAlgorithmBusiness.vue';
+import TaskMap from './TaskMap.vue';
+import TaskTable from './TaskTable.vue';
+
+const rangDate = ref([]);
+// 航线ID
+let waylineId = ref('');
+// 航线文件
+let wayLineFile = ref('');
+// 航线类型
+let waylineType = ref(0);
+
+const searchForm = reactive({
+	name: '',
+	ai_type: [],
+	file_id: '',
+	begin_time: '',
+	end_time: '',
+	execute_time_arr: '',
+	remark: '',
+	type: waylineType.value,
+	dock_sns: [],
+});
+const isShowAddTask = defineModel('show');
+
+// 获取航线列表数据
+const routeOptions = ref([]);
+const getRouteList = async () => {
+  const res = await getWaylineList();
+  if (res.data.code === 0) {
+    routeOptions.value = res.data.data;
+  }
+};
+const algorithmChange = (val) => {
+  searchForm.ai_type = val;
+};
+
+// 获取航线文件
+const getWayLineFile = async (val) => {
+	waylineId.value = val;
+	const currentRoute = routeOptions.value.find(item => item.wayline_id === val);
+  wayLineFile.value = currentRoute.object_key;
+};
+// 获取选中机场列表数据,并且发布
+const handleSelected = (val) => {
+  searchForm.dock_sns = val.map(item => item.device_sn);
+};
+const emit = defineEmits(['refresh']);
+// 提交
+const submitClick = () => {
+	searchForm.end_time = `${searchForm.begin_time} 23:59:59`;
+	searchForm.begin_time = `${searchForm.begin_time} 00:00:00`;
+	
+	createTask(searchForm).then((res) => {
+		console.log(res);
+		if (res.code === 0) {
+			ElMessage.success('任务创建成功');
+			// 关闭当前窗口,刷新任务管理列表
+			isShowAddTask.value = false;
+			emit('refresh');
+		}
+	});
+};
+const cancel = () => {
+  isShowAddTask.value = false;
+	// 清除搜索数据
+	searchForm.name = '';
+  searchForm.ai_type = [];
+  searchForm.file_id = '';
+  searchForm.begin_time = '';
+  searchForm.end_time = '';
+  searchForm.execute_time_arr = '';
+  searchForm.remark = '';
+  searchForm.dock_sns = [];
+  rangDate.value = [];
+  waylineId.value = '';
+  wayLineFile.value = '';
+};
+onMounted(() => {
+  getRouteList();
+})
+</script>
+
+<style lang="scss">
+.add-task{
+	.el-pagination {
+		text-align: left;
+		padding: 20px;
+	}
+}
+</style>
+
+<style lang="scss" scoped>
+.task-contain {
+	display: flex;
+	.left {
+		width: 70%;
+		.search {
+			display: grid;
+      grid-template-columns: repeat(3, 1fr);
+      grid-template-rows: repeat(2, 1fr);
+      gap: 30px; // 增加间距使布局更合理
+      margin-bottom: 8px; // 添加底部间距
+			.item {
+        display: flex;
+        align-items: center;
+        :deep(.el-input),
+        :deep(.el-select),
+        :deep(.el-date-picker),
+        :deep(.el-time-picker) {
+          width: 240px;
+          margin-left: 10px;
+        }
+				:deep(.el-date-editor.el-input__wrapper) {
+          width: 200px;  // 调整日期选择器宽度
+        }
+      }
+		}
+	}
+	.right {
+		width: 30%;
+	}
+}
+</style>
diff --git a/src/views/TaskManage/TaskIntermediateContent/TaskIntermediateContent.vue b/src/views/TaskManage/TaskIntermediateContent/TaskIntermediateContent.vue
index ee3f45b..05beb46 100644
--- a/src/views/TaskManage/TaskIntermediateContent/TaskIntermediateContent.vue
+++ b/src/views/TaskManage/TaskIntermediateContent/TaskIntermediateContent.vue
@@ -1,6 +1,6 @@
 <!-- 任务统计表格 -->
 <template>
-  <SearchBox @search="searchClick"></SearchBox>
+  <SearchBox @search="searchClick" @addTask="handleAddTask"></SearchBox>
   <div class="task-intermediate-content">
     <el-table :data="jobListData" style="width: 100%" height="calc(100vh - 180px)">
       <el-table-column label="序号" width="60">
@@ -40,10 +40,12 @@
       />
     </div>
   </div>
+  <AddTask v-model:show="isShowAddTask" @refresh="searchClick"/>
 </template>
 
 <script setup>
 import SearchBox from '../SearchBox.vue';
+import AddTask from './AddTask.vue';
 import { jobList } from '@/api/home/task';
 
 const jobListParams = reactive({
@@ -112,6 +114,12 @@
   getJobList();
 };
 
+// 新建任务
+const isShowAddTask = ref(false);
+const handleAddTask = () => {
+  isShowAddTask.value = true;
+};
+
 onMounted(() => {
   getJobList();
 });
diff --git a/src/views/TaskManage/TaskIntermediateContent/TaskMap.vue b/src/views/TaskManage/TaskIntermediateContent/TaskMap.vue
new file mode 100644
index 0000000..fe8cdf7
--- /dev/null
+++ b/src/views/TaskManage/TaskIntermediateContent/TaskMap.vue
@@ -0,0 +1,236 @@
+<template>
+	<div id="taskMap" />
+</template>
+<script setup>
+import * as Cesium from 'cesium'
+import { Cartesian3, Math as CesiumMath, Terrain, Viewer } from 'cesium'
+import AmapMercatorTilingScheme from '@/utils/cesium/AmapMercatorTilingScheme'
+import { analyzeKmzFile, removeTextKey, XMLToJSON } from '@/utils/cesium/kmz'
+import ImageTrailMaterial from '@/utils/cesium/ImageTrailMaterial'
+import lineImg from '@/assets/images/arrow-right-blue.png';
+import Startingpointicon from '@/assets/images/Startingpointicon.png';
+import EndPointicon from '@/assets/images/EndPointicon.png';
+import uavImg from '@/assets/images/home/useUavHome/uavImg.png'
+import { getCenterPoint } from '@/utils/cesium/mapUtil'
+
+const props = defineProps(['wayLineFile'])
+
+
+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}',
+	layer: 'tdtVecBasicLayer',
+	style: 'default',
+	format: 'image/png',
+	tileMatrixSetID: 'GoogleMapsCompatible',
+	subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
+	maximumLevel: 18,
+	tilingScheme: new AmapMercatorTilingScheme(),
+	credit: 'amap_SL',
+})
+let viewer = null
+const init = () => {
+	viewer = new Viewer('taskMap', {
+		terrain: Terrain.fromWorldTerrain(),
+		infoBox: false, // 禁用沙箱,解决控制台报错
+		animation: false, // 左下角的动画仪表盘
+		baseLayerPicker: false, // 右上角的图层选择按钮
+		geocoder: false, // 搜索框
+		homeButton: false, // home按钮
+		sceneModePicker: false, // 模式切换按钮
+		timeline: false, // 底部的时间轴
+		navigationHelpButton: false, // 右上角的帮助按钮,
+		selectionIndicator: false, // 是否显示选择指示器
+		baseLayer: false,
+		fullscreenButton: false,
+		// sceneMode: Cesium.SceneMode.COLUMBUS_VIEW,
+	})
+	viewer.imageryLayers.addImageryProvider(imageryProvider_ammapSL)
+	viewer.scene.morphTo2D(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,
+			positions: positions,
+			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,
+		},
+	})
+  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) {
+      setting = {
+        position: point,
+        id: `point_${index}`,
+        billboard: {
+          image: EndPointicon,
+          outlineWidth: 0,
+          width: 20,
+          height: 20,
+          scale: 1.0,
+        },
+      }
+    } else {
+      setting = {
+        position: point,
+        id: `point_${index}`,
+        label: {
+          text: `${index}`,
+          font: 'bold 14px serif',
+          style: Cesium.LabelStyle.FILL,
+          verticalOrigin: Cesium.VerticalOrigin.CENTER, // 垂直居中
+          horizontalOrigin: Cesium.HorizontalOrigin.CENTER, // 水平居中
+          pixelOffset: new Cesium.Cartesian2(0, 0), // 根据需要调整偏移量
+          eyeOffset: new Cesium.Cartesian3(0, 0, -10) // 使标签在点的上方
+        },
+
+        point: {
+          pixelSize: 24,
+          color: new Cesium.Color.fromBytes(255, 186, 0, 255),
+        },
+
+        offset: new Cesium.Cartesian2(10, 30),
+      }
+    }
+    viewer.entities.add(setting)
+  });
+}
+
+// 飞到中心点
+function flyToPoints(lngLatArr) {
+	if (!Array.isArray(lngLatArr) || lngLatArr.length === 0) return
+	const positions = lngLatArr.map(([lon, lat]) =>
+		Cesium.Cartesian3.fromDegrees(Number(lon), Number(lat))
+	)
+	// 计算包围盒 BoundingSphere(所有点的外接球)
+	const boundingSphere = Cesium.BoundingSphere.fromPoints(positions)
+	viewer.camera.flyToBoundingSphere(boundingSphere, {
+		duration: 0,
+		offset: new Cesium.HeadingPitchRange(0, 0, boundingSphere.radius * 2),
+	})
+}
+
+// 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 templateXML = await res.fileInfoObj['wpmz/template.kml']
+		const templateXMLJSON = XMLToJSON(templateXML)?.['Document']
+		const templateXMLObj = removeTextKey(templateXMLJSON.Folder)
+		resolve(templateXMLObj)
+	})
+}
+
+// 绘制线和飞行
+const drawLine = async () => {
+  let prexUrl = ref(import.meta.env.VITE_APP_AIRLINE_URL+ props.wayLineFile);
+	const res = await analysis(prexUrl.value);
+  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();
+		await drawLine();
+	}
+})
+
+const removeMap = () => {
+	viewer.entities.removeAll()
+	viewer.destroy()
+}
+
+
+onBeforeUnmount(() => {
+	removeMap()
+})
+
+onMounted(() => {
+	nextTick(() => {
+		init()
+	})
+})
+</script>
+<style scoped lang="scss">
+#taskMap {
+	height: 100%;
+
+	:deep() {
+		.cesium-viewer {
+			height: 100%;
+			overflow: hidden;
+
+			.cesium-viewer-cesiumWidgetContainer {
+				width: 100%;
+				height: 100%;
+
+				.cesium-widget {
+					width: 100%;
+					height: 100%;
+
+					canvas {
+						width: 100%;
+						height: 100%;
+					}
+				}
+			}
+		}
+
+		.cesium-viewer-bottom {
+			display: none;
+		}
+	}
+}
+</style>
diff --git a/src/views/TaskManage/TaskIntermediateContent/TaskTable.vue b/src/views/TaskManage/TaskIntermediateContent/TaskTable.vue
new file mode 100644
index 0000000..26d834a
--- /dev/null
+++ b/src/views/TaskManage/TaskIntermediateContent/TaskTable.vue
@@ -0,0 +1,86 @@
+<template>
+  <el-table :data="tableData" style="width: 100%" height="400" @selection-change="handleSelectionChange">
+    <el-table-column type="selection" width="55" />
+    <el-table-column type="index" label="序号" width="60" />
+    <el-table-column prop="nickname" label="机巢名称" />
+    <el-table-column prop="estimated_arrival_time" label="预计到达时间" />
+    <el-table-column prop="exe_distance" label="执行里程" />
+  </el-table>
+  <div class="pagination">
+    <el-pagination
+      v-model:current-page="pageParams.page"
+      v-model:page-size="pageParams.limit"
+      :page-sizes="[10, 20, 30]"
+      layout="total, sizes, prev, pager, next"
+      :total="total"
+      @size-change="handleSizeChange"
+      @current-change="handleCurrentChange"
+    />
+  </div>
+</template>
+
+<script setup>
+import { getFlyingNestBy } from '@/api/home/task';
+
+const props = defineProps({
+  waylineId: {
+    type: String,
+    default: ''
+  },
+  waylineType: {
+    type: Number,
+    default: 0
+  }
+});
+
+// 选中的数据
+const selectedRows = ref([]);
+// 分页参数
+let pageParams = reactive({
+  wayline_id: props.waylineId, 
+  type: props.waylineType,
+  page: 1,
+  limit: 10
+});
+// 获取可用机巢列表数据
+const total = ref(0);
+const tableData = ref([]);
+const getNestList = async () => {
+	tableData.value = [];
+  const res = await getFlyingNestBy(pageParams);
+  if (res.data.code === 0) {
+    tableData.value = res.data.data;
+  }
+};
+// 分页大小改变
+const handleSizeChange = (val) => {
+  pageParams.limit = val;
+  getNestList();
+};
+
+// 页码改变
+const handleCurrentChange = (val) => {
+  pageParams.page = val;
+  getNestList();
+};
+const emit = defineEmits(['update:selected']);
+const handleSelectionChange = (val) => {
+  selectedRows.value = val;
+  // 可以通过emit将选中数据传给父组件
+  emit('update:selected', val);
+};
+// 监听航线ID变化
+watch(() => props.waylineId, (newVal) => {
+  pageParams.wayline_id = newVal;
+  if (newVal) {
+    pageParams.page = 1;
+    getNestList();
+  }
+}, { immediate: true });
+onMounted(() => {
+  // getNestList();
+});
+</script>
+
+<style lang="scss" scoped>
+</style>
\ No newline at end of file
diff --git a/src/views/TaskManage/components/TaskAlgorithmBusiness.vue b/src/views/TaskManage/components/TaskAlgorithmBusiness.vue
new file mode 100644
index 0000000..5a773ac
--- /dev/null
+++ b/src/views/TaskManage/components/TaskAlgorithmBusiness.vue
@@ -0,0 +1,60 @@
+<!-- 关联算法和综合业务 -->
+<template>
+  <div class="task-algorithm" v-if="showAlgorithm">
+    <el-select v-model="ai_type" multiple collapse-tags collapse-tags-tooltip placeholder="请选择算法" clearable @change="handleAlgorithmChange">
+      <el-option v-for="item in taskAlgorithm" :key="item.id" :label="item.dictValue" :value="item.dictKey" />
+    </el-select>
+  </div>
+  <div class="task-business" v-if="showBusiness">
+    <el-select v-model="industry_type" placeholder="请选择类型" clearable @change="handleBusinessChange">
+      <el-option v-for="item in taskBusiness" :key="item.id" :label="item.dictValue" :value="item.dictKey" />
+    </el-select>
+  </div>
+</template>
+
+<script setup>
+import { getDictionary } from '@/api/home/common';
+
+// 接收父组件传参
+const props = defineProps({
+  showAlgorithm: {
+    type: Boolean,
+    default: false
+  },
+  showBusiness: {
+    type: Boolean,
+    default: false
+  }
+});
+
+// 算法
+let ai_type = ref('');
+let taskAlgorithm = ref([]);
+// 综合业务
+let industry_type = ref('');
+let taskBusiness = ref([]);
+
+// 请求字典字段
+const requestDictionary = () => {
+  getDictionary('SF,HYLB').then((res) => {
+    if (res.code !== 0) {
+      // 处理数据
+      taskAlgorithm.value = res.data.data['SF'];
+      taskBusiness.value = res.data.data['HYLB'];
+    }
+  });
+};
+const emit = defineEmits(['algorithmChange', 'businessChange']);
+// 算法选择事件
+const handleAlgorithmChange = (value) => {
+  emit('algorithmChange', value);
+};
+// 业务选择事件
+const handleBusinessChange = (value) => {
+  emit('businessChange', value);
+};
+
+onMounted(() => {
+  requestDictionary();
+});
+</script>
\ No newline at end of file

--
Gitblit v1.9.3