From 7d384f8f946dd081962bd76fc8873ec6f26d2c9e Mon Sep 17 00:00:00 2001
From: shuishen <1109946754@qq.com>
Date: Tue, 15 Apr 2025 21:57:45 +0800
Subject: [PATCH] 地图航线显示相关处理

---
 .env.development                              |    2 
 src/views/tickets/orderLog.vue                | 1869 ++++++++++++++++++++++++---------------------------
 src/utils/cesium/kmz.js                       |  238 ++++++
 src/components/map-container/mapContainer.vue |    6 
 4 files changed, 1,122 insertions(+), 993 deletions(-)

diff --git a/.env.development b/.env.development
index 19098ed..030ebc6 100644
--- a/.env.development
+++ b/.env.development
@@ -14,3 +14,5 @@
 # 域名
 VITE_APP_AREA_NAME =  wss://wrj.shuixiongit.com
 
+# 航线文件地址
+VITE_APP_AIRLINE_URL = https://wrj.shuixiongit.com/minio/cloud-bucket
diff --git a/src/components/map-container/mapContainer.vue b/src/components/map-container/mapContainer.vue
index e74385f..4f8effb 100644
--- a/src/components/map-container/mapContainer.vue
+++ b/src/components/map-container/mapContainer.vue
@@ -2,7 +2,7 @@
  * @Author: shuishen 1109946754@qq.com
  * @Date: 2024-10-25 15:07:51
  * @LastEditors: shuishen 1109946754@qq.com
- * @LastEditTime: 2025-04-14 23:09:33
+ * @LastEditTime: 2025-04-15 21:54:01
  * @FilePath: \drone-web-manage\src\components\map-container\mapContainer.vue
  * @Description: 
  * 
@@ -100,6 +100,8 @@
  * @param data 数据  数据格式 [lng, lat]
  */
 function addPoint (data) {
+    if (pointLayer) pointLayer.clear()
+
     const [lng, lat] = data
 
     if (!lng || !lat) return
@@ -117,6 +119,8 @@
  * @param data 数据  数据格式 [[lng, lat], [lng, lat], [lng, lat]]
  */
 function addPolyline (data) {
+    if (polylineLayer) polylineLayer.clear()
+
     if (data.length === 0) return
 
     const positionStr = data.map(item => {
diff --git a/src/utils/cesium/kmz.js b/src/utils/cesium/kmz.js
new file mode 100644
index 0000000..a6cf77f
--- /dev/null
+++ b/src/utils/cesium/kmz.js
@@ -0,0 +1,238 @@
+import axios from 'axios'
+import JsZip from 'jszip'
+
+const noWmpl = ['Folder', 'Placemark', 'Point', 'coordinates']
+
+/**
+ * @description: 读取kmz文件解析出kml文件
+ * @param {string} filePath 文件地址
+ * @return {*}
+ */
+export const analyzeKmzFile = filePath => {
+	return (
+		axios({
+			url: filePath,
+			method: 'get',
+			responseType: 'arraybuffer',
+			authorization: false,
+		})
+			// .get(filePath, { responseType: 'arraybuffer' },)
+			.then(fileRes => fileRes.data)
+			.then(kmzData => JsZip.loadAsync(kmzData)) // 解压kmz文件
+			.then(zipFile => {
+				const files = {}
+				Object.keys(zipFile.files).forEach(key => {
+					files[key] = zipFile.files[key].async('text')
+				})
+				return {
+					zip: zipFile,
+					fileInfoObj: files,
+				}
+			})
+	)
+}
+
+export const getKmlParams = (source, isWpml, params) => {
+	let regx = null
+	if (isWpml) {
+		if (params.mode) {
+			regx = new RegExp(
+				`<wpml:${params.name}>${params.findRegx}<\\/wpml:${params.name}>`,
+				params.mode
+			)
+		} else {
+			regx = new RegExp(`<wpml:${params.name}>${params.findRegx}<\\/wpml:${params.name}>`)
+		}
+	} else {
+		if (params.mode) {
+			regx = new RegExp(`<${params.name}>${params.findRegx}<\\/${params.name}>`, params.mode)
+		} else {
+			regx = new RegExp(`<${params.name}>${params.findRegx}<\\/${params.name}>`)
+		}
+	}
+	return source?.match(regx)
+}
+
+export const generateKmlFormat = settingParmas => {
+	let paramGroup = ''
+	Object.keys(settingParmas).forEach(key => {
+		if (Object.prototype.toString.call(settingParmas[key]) === '[object Object]') {
+			let parmaStr = ''
+			Object.keys(settingParmas[key]).forEach(v => {
+				const xml = noWmpl.includes(key)
+					? `<${v}>${settingParmas[key][v]}</${v}>`
+					: `<wpml:${v}>${settingParmas[key][v]}</wpml:${v}>`
+				parmaStr += xml
+			})
+			const xml = noWmpl.includes(key)
+				? `<${key}>${parmaStr}</${key}>`
+				: `<wpml:${key}>${parmaStr}</wpml:${key}>`
+			paramGroup += xml
+		} else {
+			const xml = noWmpl.includes(key)
+				? `<${key}>${settingParmas[key]}</${key}>`
+				: `<wpml:${key}>${settingParmas[key]}</wpml:${key}>`
+			paramGroup += xml
+		}
+	})
+	return paramGroup
+}
+
+/**
+ * @description: JSON转XML
+ * @param {any} obj 要转换的对象
+ * @param {string} rootName xml根节点名称
+ * @return {*} string
+ */
+export const JSONToXML = (obj, rootName, isEnd) => {
+	let xml = ''
+
+	const buildXml = (obj, rootName) => {
+		let xml = ''
+		if (Array.isArray(obj)) {
+			obj.forEach(item => {
+				const str = !noWmpl.includes(rootName) && isEnd ? 'wpml:' : ''
+				xml += `<${str}${rootName}>${buildXml(item)}</${str}${rootName}>`
+			})
+		} else if (typeof obj === 'object') {
+			Object.keys(obj).forEach(key => {
+				if (key === '#text') {
+					xml += obj[key]
+				} else {
+					const str = !noWmpl.includes(key) && isEnd ? 'wpml:' : ''
+					if (key === 'Placemark') {
+						xml += `${buildXml(obj[key], key)}`
+					} else if (key === 'action') {
+						if (Array.isArray(obj[key])) {
+							xml += `${buildXml(obj[key], key)}`
+						} else {
+							xml += `<${str}${key}>${buildXml(obj[key], key)}</${str}${key}>`
+						}
+					} else {
+						xml += `<${str}${key}>${buildXml(obj[key], key)}</${str}${key}>`
+					}
+				}
+			})
+		} else {
+			xml += obj
+		}
+		return xml
+	}
+
+	const header = isEnd ? '<?xml version="1.0" encoding="UTF-8"?>' : ''
+	xml += `${header}
+  <kml xmlns="http://www.opengis.net/kml/2.2" xmlns:wpml="http://www.dji.com/wpmz/1.0.5">
+    <Document>
+      ${buildXml(obj, rootName)}
+    </Document>
+  </kml>`
+	return xml
+}
+
+export const getTagNameFromXml = xmlString => {
+	const parser = new DOMParser()
+	const xmlDoc = parser.parseFromString(xmlString, 'text/xml')
+	const element = xmlDoc.documentElement
+	if (element && element.tagName) {
+		return element.tagName // 返回的是类似 "WPML:USEGLOBALHEADINGPARAM" 的全大写形式
+	}
+	return null
+}
+
+// 将xml转为json
+const deepParse = xml => {
+	let obj = {}
+	// 如果是文档节点,遍历其子节点
+	if (xml.nodeType === 1) {
+		// element node
+		// 处理子节点
+		if (xml.childNodes.length > 0) {
+			for (let i = 0; i < xml.childNodes.length; i++) {
+				const item = xml.childNodes[i]
+				const nodeName = item.nodeName.replace('wpml:', '')
+
+				// 检查是否是文本节点
+				if (item.nodeType === 3) {
+					// text node
+					// 处理文本节点的内容
+					if (item.nodeValue.trim() !== '') {
+						obj[nodeName] = item.nodeValue.trim()
+					}
+				} else if (item.nodeType === 1) {
+					// element node
+					// 递归调用处理子节点
+					if (obj[nodeName] === undefined) {
+						const nodeValue = deepParse(item)
+						if (nodeValue[nodeName] === undefined) {
+							obj[nodeName] = nodeValue
+						} else {
+							obj[nodeName] = nodeValue[nodeName]
+						}
+					} else {
+						if (!obj[nodeName].push) {
+							const old = obj[nodeName]
+							obj[nodeName] = []
+							obj[nodeName].push(old)
+						}
+						const nodeValue = deepParse(item)
+						if (nodeValue[nodeName] === undefined) {
+							obj[nodeName].push(nodeValue)
+						}
+					}
+				}
+			}
+		}
+	} else if (xml.nodeType === 3) {
+		// text node
+		// 处理文本节点的内容
+		obj = xml.nodeValue.trim()
+	}
+	return obj
+}
+
+/**
+ * @description: XML转JSON
+ * @param {string} xmlStr xml字符串
+ * @return {*} string
+ */
+export const XMLToJSON = xmlStr => {
+	const parser = new DOMParser()
+	const xmlDoc = parser.parseFromString(xmlStr, 'text/xml')
+	const xmlObj = deepParse(xmlDoc.documentElement)
+	if (Reflect.has(xmlObj, 'Document')) {
+		return xmlObj
+	} else {
+		return {
+			Document: xmlObj,
+		}
+	}
+}
+
+
+export function removeTextKey(obj) {
+	if (typeof obj !== 'object' || obj === null) {
+		// 如果是数值字符串,转换为数值
+		if (typeof obj === 'string' && /^-?\d+(\.\d+)?$/.test(obj)) {
+			return Number(obj);
+		}
+		return obj;
+	}
+
+	if (Array.isArray(obj)) {
+		return obj.map(item => removeTextKey(item));
+	}
+
+	if (Object.prototype.hasOwnProperty.call(obj, '#text')) {
+		// 如果 #text 的值是数值字符串,转换为数值
+		const textValue = obj['#text'];
+		return typeof textValue === 'string' && /^-?\d+(\.\d+)?$/.test(textValue) ? Number(textValue) : textValue;
+	}
+
+	const newObj = {};
+	for (const key in obj) {
+		if (Object.prototype.hasOwnProperty.call(obj, key)) {
+			newObj[key] = Object.keys(obj[key]).length === 0 ? '' : removeTextKey(obj[key]);
+		}
+	}
+	return newObj;
+}
diff --git a/src/views/tickets/orderLog.vue b/src/views/tickets/orderLog.vue
index 03e60dd..9ff71c5 100644
--- a/src/views/tickets/orderLog.vue
+++ b/src/views/tickets/orderLog.vue
@@ -1,331 +1,206 @@
+<!--
+ * @Author: shuishen 1109946754@qq.com
+ * @Date: 2025-04-15 20:02:29
+ * @LastEditors: shuishen 1109946754@qq.com
+ * @LastEditTime: 2025-04-15 21:56:14
+ * @FilePath: \drone-web-manage\src\views\tickets\orderLog.vue
+ * @Description: 
+ * 
+ * Copyright (c) 2025 by shuishen, All Rights Reserved. 
+-->
 <template>
-  <basic-container>
-    <el-tabs v-model="activeTab" @tab-click="handleTabChange">
-      <el-tab-pane
-        v-for="tab in tabs"
-        :key="tab.name"
-        :label="`${tab.label} (${tab.count})`"
-        :name="tab.name"
-      >
-        <div class="tab-content">
-          <!-- 查询条件筛选栏 -->
-          <div class="filter-bar">
-            <el-input
-              v-model="filters.key_word"
-              placeholder="输入工单编号/名称/内容/姓名"
-              class="filter-item"
-              clearable
-              @keyup.enter="handleSearch"
-            />
+    <basic-container>
+        <el-tabs v-model="activeTab" @tab-click="handleTabChange">
+            <el-tab-pane v-for="tab in tabs" :key="tab.name" :label="`${tab.label} (${tab.count})`" :name="tab.name">
+                <div class="tab-content">
+                    <!-- 查询条件筛选栏 -->
+                    <div class="filter-bar">
+                        <el-input v-model="filters.key_word" placeholder="输入工单编号/名称/内容/姓名" class="filter-item" clearable
+                            @keyup.enter="handleSearch" />
 
-            <el-select
-              placeholder="请选择所属单位"
-              v-model="filters.create_dept"
-              @change="handleDepartmentChange"
-              class="filter-item"
-              clearable
-            >
-              <el-option
-                v-for="dept in departments"
-                :key="dept.value"
-                :label="dept.label"
-                :value="dept.value"
-              />
-            </el-select>
+                        <el-select placeholder="请选择所属单位" v-model="filters.create_dept" @change="handleDepartmentChange"
+                            class="filter-item" clearable>
+                            <el-option v-for="dept in departments" :key="dept.value" :label="dept.label"
+                                :value="dept.value" />
+                        </el-select>
 
-            <el-date-picker
-              v-model="filters.dateRange"
-              type="datetimerange"
-              range-separator="至"
-              start-placeholder="开始日期"
-              end-placeholder="结束日期"
-              class="filter-item"
-              style="width: 100px"
-            />
-            <el-select
-              v-model="filters.file_id"
-              placeholder="请选择关联航线"
-              class="filter-item"
-              clearable
-            >
-              <el-option
-                v-for="item in wayLineList"
-                :key="item.wayline_id"
-                :label="item.name"
-                :value="item.wayline_id"
-              />
-            </el-select>
+                        <el-date-picker v-model="filters.dateRange" type="datetimerange" range-separator="至"
+                            start-placeholder="开始日期" end-placeholder="结束日期" class="filter-item" style="width: 100px" />
+                        <el-select v-model="filters.file_id" placeholder="请选择关联航线" class="filter-item" clearable>
+                            <el-option v-for="item in wayLineList" :key="item.wayline_id" :label="item.name"
+                                :value="item.wayline_id" />
+                        </el-select>
 
-            <el-select
-              v-model="filters.ai_types"
-              placeholder="关联算法"
-              class="filter-item"
-              clearable
-            >
-              <el-option
-                v-for="item in ai_types"
-                :key="item.dictKey"
-                :label="item.dictValue"
-                :value="item.dictKey"
-              />
-            </el-select>
+                        <el-select v-model="filters.ai_types" placeholder="关联算法" class="filter-item" clearable>
+                            <el-option v-for="item in ai_types" :key="item.dictKey" :label="item.dictValue"
+                                :value="item.dictKey" />
+                        </el-select>
 
-            <el-select
-              v-model="filters.type"
-              placeholder="请选择工单类型"
-              class="filter-item"
-              clearable
-            >
-              <el-option
-                v-for="item in types"
-                :key="item.dictValue"
-                :label="item.dictValue"
-                :value="item.dictKey"
-              />
-            </el-select>
+                        <el-select v-model="filters.type" placeholder="请选择工单类型" class="filter-item" clearable>
+                            <el-option v-for="item in types" :key="item.dictValue" :label="item.dictValue"
+                                :value="item.dictKey" />
+                        </el-select>
 
-            <el-select
-              v-model="filters.status"
-              placeholder="请选择工单状态"
-              class="filter-item"
-              clearable
-            >
-              <el-option
-                v-for="item in statuses"
-                :key="item.value"
-                :label="item.label"
-                :value="item.value"
-              />
-       
-           </el-select>
-              <el-date-picker
-              v-model="filters.cycleDateRange"
-              type="datetimerange"
-              range-separator="至"
-              start-placeholder="工单周期开始日期"
-              end-placeholder="工单周期结束时间日期"
-              class="filter-item"
-              style="width: 240px !important"
-            />
-              <el-select v-model="filters.rep_fre_type" placeholder="请选择频次"  class="filter-item">
-              <el-option v-for="item in cycles" :key="item" :label="item" :value="item" />
-            </el-select>
-            <el-time-picker
-              style="width: 100px"
-              v-model="filters.deal_time"
-              placeholder="请选择执行时间"
-              prop="deal_time"
-              value-format="HH:mm"
-              :picker-options="{
-                selectableRange: '00:00 - 23:59',
-              }"
-            />
-              <el-col :span="8">
-              </el-col>
-            <el-button type="primary" icon="el-icon-search" @click="handleSearch">查询</el-button>
-            <el-button icon="el-icon-refresh" @click="handleReset">重置</el-button>
-          </div>
+                        <el-select v-model="filters.status" placeholder="请选择工单状态" class="filter-item" clearable>
+                            <el-option v-for="item in statuses" :key="item.value" :label="item.label"
+                                :value="item.value" />
 
-          <!-- 表格部分 -->
-          <avue-crud
-            :data="tableData"
-            :option="option"
-            v-model:page="page"
-            ref="crud"
-            :table-loading="loading"
-            @current-change="currentChange"
-            @refresh-change="refreshChange"
-            @on-load="onLoad"
-            @search-change="searchChange"
-            @size-change="sizeChange"
-          >
-            <template #menu-left>
-              <el-button type="primary" icon="el-icon-plus" @click="handleAdd">新建工单</el-button>
-              <el-button type="success" plain icon="el-icon-download" @click="exportData"
-                >导出</el-button
-              >
-            </template>
+                        </el-select>
+                        <el-date-picker v-model="filters.cycleDateRange" type="datetimerange" range-separator="至"
+                            start-placeholder="工单周期开始日期" end-placeholder="工单周期结束时间日期" class="filter-item"
+                            style="width: 240px !important" />
+                        <el-select v-model="filters.rep_fre_type" placeholder="请选择频次" class="filter-item">
+                            <el-option v-for="item in cycles" :key="item" :label="item" :value="item" />
+                        </el-select>
+                        <el-time-picker style="width: 100px" v-model="filters.deal_time" placeholder="请选择执行时间"
+                            prop="deal_time" value-format="HH:mm" :picker-options="{
+                                selectableRange: '00:00 - 23:59',
+                            }" />
+                        <el-col :span="8">
+                        </el-col>
+                        <el-button type="primary" icon="el-icon-search" @click="handleSearch">查询</el-button>
+                        <el-button icon="el-icon-refresh" @click="handleReset">重置</el-button>
+                    </div>
 
-            <template #menu="{ row }">
-              <div v-if="row.status == 1 && userInfo.user_id != row.create_user">
-                <el-button type="text" icon="el-icon-view" @click="handleViewDetail(row)"
-                  >审核</el-button
-                >
-              </div>
+                    <!-- 表格部分 -->
+                    <avue-crud :data="tableData" :option="option" v-model:page="page" ref="crud"
+                        :table-loading="loading" @current-change="currentChange" @refresh-change="refreshChange"
+                        @on-load="onLoad" @search-change="searchChange" @size-change="sizeChange">
+                        <template #menu-left>
+                            <el-button type="primary" icon="el-icon-plus" @click="handleAdd">新建工单</el-button>
+                            <el-button type="success" plain icon="el-icon-download" @click="exportData">导出</el-button>
+                        </template>
 
-              <div v-if="userInfo.user_id == row.create_user">
-                <!--待审核状态-->
-                <div v-if="row.status == 1">
-                  <el-button type="text" icon="el-icon-view" @click="orderLogRecall(row.id)"
-                    >撤回</el-button
-                  >
+                        <template #menu="{ row }">
+                            <div v-if="row.status == 1 && userInfo.user_id != row.create_user">
+                                <el-button type="text" icon="el-icon-view" @click="handleViewDetail(row)">审核</el-button>
+                            </div>
+
+                            <div v-if="userInfo.user_id == row.create_user">
+                                <!--待审核状态-->
+                                <div v-if="row.status == 1">
+                                    <el-button type="text" icon="el-icon-view"
+                                        @click="orderLogRecall(row.id)">撤回</el-button>
+                                </div>
+                                <!--已驳回-->
+                                <div v-if="row.status == 2">
+                                    <el-button type="text" icon="el-icon-view"
+                                        @click="rejectDetail(row.id)">驳回原因</el-button>
+                                </div>
+                                <el-button type="text" icon="el-icon-view" @click="handleViewDetail(row)">详情</el-button>
+                            </div>
+                        </template>
+                        <template #status="{ row }">
+                            <el-tag :type="getStatusTagType(row.status)">{{ mapStatus(row.status) }}</el-tag>
+                        </template>
+                        <template #keyData="{ row }">
+                            <el-tooltip :content="row.address" placement="top" effect="light">
+                                <span>{{ row.keyData }}</span>
+                            </el-tooltip>
+                        </template>
+                    </avue-crud>
                 </div>
-                <!--已驳回-->
-                <div v-if="row.status == 2">
-                  <el-button type="text" icon="el-icon-view" @click="rejectDetail(row.id)"
-                    >驳回原因</el-button
-                  >
-                </div>
-                <el-button type="text" icon="el-icon-view" @click="handleViewDetail(row)"
-                  >详情</el-button
-                >
-              </div>
-            </template>
-            <template #status="{ row }">
-              <el-tag :type="getStatusTagType(row.status)">{{ mapStatus(row.status) }}</el-tag>
-            </template>
-            <template #keyData="{ row }">
-              <el-tooltip :content="row.address" placement="top" effect="light">
-                <span>{{ row.keyData }}</span>
-              </el-tooltip>
-            </template>
-          </avue-crud>
-        </div>
-      </el-tab-pane>
-    </el-tabs>
+            </el-tab-pane>
+        </el-tabs>
 
-    <!-- 新建工单对话框 -->
-    <el-dialog
-      v-model="dialogVisible"
-      title="新建工单"
-      width="70%"
-      :close-on-click-modal="false"
-      @close="resetForm"
-    >
-      <el-form :model="form" :rules="rules" ref="form" label-width="100px">
-        <el-row :gutter="20">
-          <el-col :span="12">
-            <el-form-item label="工单名称" prop="name">
-              <el-input v-model="form.name" placeholder="请输入工单名称"></el-input>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="关联航线" prop="file_id">
-              <el-select v-model="form.file_id" placeholder="请选择航线" @change="getFlyingNestBy">
-                <el-option
-                  v-for="item in wayLineList"
-                  :key="item.wayline_id"
-                  :label="item.name"
-                  :value="item.wayline_id"
-                />
-              </el-select>
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row :gutter="20">
-          <el-col :span="12">
-            <el-form-item label="关联机巢" prop="device_sns">
-              <el-select v-model="form.device_sns" placeholder="请选择机巢" multiple>
-                <el-option
-                  v-for="item in device_sns"
-                  :key="item.device_sn"
-                  :label="item.nickname"
-                  :value="item.device_sn"
-                />
-              </el-select>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="关联算法" prop="ai_types">
-              <el-select v-model="form.ai_types" placeholder="请选择关联算法" multiple>
-                <el-option
-                  v-for="item in ai_types"
-                  :key="item.dictKey"
-                  :label="item.dictValue"
-                  :value="item.dictKey"
-                />
-              </el-select>
-            </el-form-item>
-          </el-col>
-        </el-row>
+        <!-- 新建工单对话框 -->
+        <el-dialog v-model="dialogVisible" title="新建工单" width="70%" :close-on-click-modal="false" @close="resetForm">
+            <el-form :model="form" :rules="rules" ref="form" label-width="100px">
+                <el-row :gutter="20">
+                    <el-col :span="12">
+                        <el-form-item label="工单名称" prop="name">
+                            <el-input v-model="form.name" placeholder="请输入工单名称"></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="12">
+                        <el-form-item label="关联航线" prop="file_id">
+                            <el-select v-model="form.file_id" placeholder="请选择航线" @change="getFlyingNestBy">
+                                <el-option v-for="item in wayLineList" :key="item.wayline_id" :label="item.name"
+                                    :value="item.wayline_id" />
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+                <el-row :gutter="20">
+                    <el-col :span="12">
+                        <el-form-item label="关联机巢" prop="device_sns">
+                            <el-select v-model="form.device_sns" placeholder="请选择机巢" multiple>
+                                <el-option v-for="item in device_sns" :key="item.device_sn" :label="item.nickname"
+                                    :value="item.device_sn" />
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="12">
+                        <el-form-item label="关联算法" prop="ai_types">
+                            <el-select v-model="form.ai_types" placeholder="请选择关联算法" multiple>
+                                <el-option v-for="item in ai_types" :key="item.dictKey" :label="item.dictValue"
+                                    :value="item.dictKey" />
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                </el-row>
 
-        <el-row :gutter="20">
-          <el-col :span="12">
-            <el-form-item label="工单内容" prop="content">
-              <el-input
-                type="textarea"
-                v-model="form.content"
-                rows="4"
-                placeholder="请输入工单内容"
-              ></el-input>
-            </el-form-item>
-          </el-col>
+                <el-row :gutter="20">
+                    <el-col :span="12">
+                        <el-form-item label="工单内容" prop="content">
+                            <el-input type="textarea" v-model="form.content" rows="4" placeholder="请输入工单内容"></el-input>
+                        </el-form-item>
+                    </el-col>
 
-          <el-col :span="6">
-            <el-form-item label="周期频次" prop="date_range">
-              <el-date-picker
-                v-model="form.date_range"
-                type="daterange"
-                range-separator="至"
-                start-placeholder="开始日期"
-                end-placeholder="结束日期"
-                class="date-picker"
-              />
-            </el-form-item>
+                    <el-col :span="6">
+                        <el-form-item label="周期频次" prop="date_range">
+                            <el-date-picker v-model="form.date_range" type="daterange" range-separator="至"
+                                start-placeholder="开始日期" end-placeholder="结束日期" class="date-picker" />
+                        </el-form-item>
 
-            <el-button type="danger" @click="submitForm(1)">发布</el-button>
-            <el-button type="primary" @click="submitForm(0)">存草稿</el-button>
-          </el-col>
-          <el-col :span="3">
-            <el-select v-model="form.rep_fre_type" placeholder="请选择频次">
-              <el-option v-for="item in cycles" :key="item" :label="item" :value="item" />
-            </el-select>
-          </el-col>
+                        <el-button type="danger" @click="submitForm(1)">发布</el-button>
+                        <el-button type="primary" @click="submitForm(0)">存草稿</el-button>
+                    </el-col>
+                    <el-col :span="3">
+                        <el-select v-model="form.rep_fre_type" placeholder="请选择频次">
+                            <el-option v-for="item in cycles" :key="item" :label="item" :value="item" />
+                        </el-select>
+                    </el-col>
 
-          <el-col :span="3">
-            <el-time-picker
-              style="width: 100px"
-              v-model="form.deal_time"
-              prop="deal_time"
-              value-format="HH:mm"
-              :picker-options="{
-                selectableRange: '00:00 - 23:59',
-              }"
-            />
-          </el-col>
-        </el-row>
+                    <el-col :span="3">
+                        <el-time-picker style="width: 100px" v-model="form.deal_time" prop="deal_time"
+                            value-format="HH:mm" :picker-options="{
+                                selectableRange: '00:00 - 23:59',
+                            }" />
+                    </el-col>
+                </el-row>
 
-          <el-row :gutter="20" style="height: 400px">
+                <el-row :gutter="20" style="height: 400px">
 
-          <el-col :span="24">
-              这里放地图
-          </el-col>
-          
-          </el-row>
-      </el-form>
-    </el-dialog>
+                    <el-col :span="24">
+                        <map-container v-if='dialogVisible' ref="MapContainer"></map-container>
+                    </el-col>
 
-    <!-- 工单详情对话框 -->
-    <el-dialog
-      v-model="detailVisible"
-      title="工单详情"
-      width="70%"
-      :close-on-click-modal="false"
-      @close="resetForm"
-    >
-      <el-form :model="form" ref="form" label-width="100px">
-        <div class="custom-steps-container">
-          <!-- 标题行 -->
-          <div class="steps-titles">
-            <div
-              v-for="(record, index) in form.record_list"
-              :class="{ active: record.user_id >= 0 }"
-              :key="index"
-              class="step-title"
-            >
-              {{ record.status_str }}
-            </div>
-          </div>
+                </el-row>
+            </el-form>
+        </el-dialog>
 
-          <!-- Element Steps 组件 -->
-          <el-steps :active="form.active" align-center class="custom-steps">
-            <el-step v-for="(record, index) in form.record_list" :key="index">
-              <template #description>
-               
-                <span class="step-description" style="position: relative; display: inline-block;">
-                  {{ record.user_name }}
-                </span >
-                 <span style="
+        <!-- 工单详情对话框 -->
+        <el-dialog v-model="detailVisible" title="工单详情" width="70%" :close-on-click-modal="false" @close="resetForm">
+            <el-form :model="form" ref="form" label-width="100px">
+                <div class="custom-steps-container">
+                    <!-- 标题行 -->
+                    <div class="steps-titles">
+                        <div v-for="(record, index) in form.record_list" :class="{ active: record.user_id >= 0 }"
+                            :key="index" class="step-title">
+                            {{ record.status_str }}
+                        </div>
+                    </div>
+
+                    <!-- Element Steps 组件 -->
+                    <el-steps :active="form.active" align-center class="custom-steps">
+                        <el-step v-for="(record, index) in form.record_list" :key="index">
+                            <template #description>
+
+                                <span class="step-description" style="position: relative; display: inline-block;">
+                                    {{ record.user_name }}
+                                </span>
+                                <span style="
           position: absolute;
           left: 80%;  
           top: 50%;
@@ -335,780 +210,790 @@
           color: #666;
           font-size: 12px;
         ">
-                  {{ record.interval_time_str }}
-                </span>
-                <div class="step-description" >
-                  {{ record.create_time_str }}
+                                    {{ record.interval_time_str }}
+                                </span>
+                                <div class="step-description">
+                                    {{ record.create_time_str }}
+                                </div>
+                            </template>
+                        </el-step>
+                    </el-steps>
                 </div>
-              </template>
-            </el-step>
-          </el-steps>
-        </div>
 
-        <el-row :gutter="20">
-          <el-col :span="12">
-            <el-form-item label="工单名称" prop="name">
-              <el-input v-model="form.name" placeholder="请输入工单名称"></el-input>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="关联航线" prop="file_id">
-              <el-select v-model="form.file_id" placeholder="请选择航线" @change="getFlyingNestBy">
-                <el-option
-                  v-for="item in wayLineList"
-                  :key="item.wayline_id"
-                  :label="item.name"
-                  :value="item.wayline_id"
-                />
-              </el-select>
-            </el-form-item>
-          </el-col>
-        </el-row>
-        <el-row :gutter="20">
-          <el-col :span="12">
-            <el-form-item label="关联机巢" prop="device_sns">
-              <el-select v-model="form.device_sns" placeholder="请选择机巢" multiple>
-                <el-option
-                  v-for="item in device_sns"
-                  :key="item.device_sn"
-                  :label="item.nickname"
-                  :value="item.device_sn"
-                />
-              </el-select>
-            </el-form-item>
-          </el-col>
-          <el-col :span="12">
-            <el-form-item label="关联算法" prop="ai_types">
-              <el-select v-model="form.ai_types" placeholder="请选择关联算法" multiple>
-                <el-option
-                  v-for="item in ai_types"
-                  :key="item.dictKey"
-                  :label="item.dictValue"
-                  :value="item.dictKey"
-                />
-              </el-select>
-            </el-form-item>
-          </el-col>
-        </el-row>
+                <el-row :gutter="20">
+                    <el-col :span="12">
+                        <el-form-item label="工单名称" prop="name">
+                            <el-input v-model="form.name" placeholder="请输入工单名称"></el-input>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="12">
+                        <el-form-item label="关联航线" prop="file_id">
+                            <el-select v-model="form.file_id" placeholder="请选择航线" @change="getFlyingNestBy">
+                                <el-option v-for="item in wayLineList" :key="item.wayline_id" :label="item.name"
+                                    :value="item.wayline_id" />
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                </el-row>
+                <el-row :gutter="20">
+                    <el-col :span="12">
+                        <el-form-item label="关联机巢" prop="device_sns">
+                            <el-select v-model="form.device_sns" placeholder="请选择机巢" multiple>
+                                <el-option v-for="item in device_sns" :key="item.device_sn" :label="item.nickname"
+                                    :value="item.device_sn" />
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                    <el-col :span="12">
+                        <el-form-item label="关联算法" prop="ai_types">
+                            <el-select v-model="form.ai_types" placeholder="请选择关联算法" multiple>
+                                <el-option v-for="item in ai_types" :key="item.dictKey" :label="item.dictValue"
+                                    :value="item.dictKey" />
+                            </el-select>
+                        </el-form-item>
+                    </el-col>
+                </el-row>
 
-        <el-row :gutter="20">
-          <el-col :span="12">
-            <el-form-item label="工单内容" prop="content">
-              <el-input
-                type="textarea"
-                v-model="form.content"
-                rows="4"
-                placeholder="请输入工单内容"
-              ></el-input>
-            </el-form-item>
-          </el-col>
+                <el-row :gutter="20">
+                    <el-col :span="12">
+                        <el-form-item label="工单内容" prop="content">
+                            <el-input type="textarea" v-model="form.content" rows="4" placeholder="请输入工单内容"></el-input>
+                        </el-form-item>
+                    </el-col>
 
-          <el-col :span="6">
-            <el-form-item label="周期频次" prop="date_range">
-              <el-date-picker
-                v-model="form.date_range"
-                type="daterange"
-                range-separator="至"
-                start-placeholder="开始日期"
-                end-placeholder="结束日期"
-                class="date-picker"
-              />
-            </el-form-item>
+                    <el-col :span="6">
+                        <el-form-item label="周期频次" prop="date_range">
+                            <el-date-picker v-model="form.date_range" type="daterange" range-separator="至"
+                                start-placeholder="开始日期" end-placeholder="结束日期" class="date-picker" />
+                        </el-form-item>
 
 
-            <el-button
-              type="danger"
-              v-if="form.status == 0 || (form.status == 2 && userInfo.user_id == form.create_user)"
-              @click="submitForm(1)"
-              >发布</el-button
-            >
-            <el-button
-              type="primary"
-              v-if="form.status == 1 && userInfo.user_id != form.create_user"
-              @click="orderLogPass(form.id)"
-              >通过</el-button
-            >
-            <el-button
-              type="danger"
-              v-if="form.status == 1 && userInfo.user_id != form.create_user"
-              @click="orderLogReject(form.id)"
-              >驳回</el-button
-            >
-          </el-col>
-          <el-col :span="3">
-            <el-select v-model="form.rep_fre_type" placeholder="请选择频次">
-              <el-option v-for="item in cycles" :key="item" :label="item" :value="item" />
-            </el-select>
-          </el-col>
+                        <el-button type="danger"
+                            v-if="form.status == 0 || (form.status == 2 && userInfo.user_id == form.create_user)"
+                            @click="submitForm(1)">发布</el-button>
+                        <el-button type="primary" v-if="form.status == 1 && userInfo.user_id != form.create_user"
+                            @click="orderLogPass(form.id)">通过</el-button>
+                        <el-button type="danger" v-if="form.status == 1 && userInfo.user_id != form.create_user"
+                            @click="orderLogReject(form.id)">驳回</el-button>
+                    </el-col>
+                    <el-col :span="3">
+                        <el-select v-model="form.rep_fre_type" placeholder="请选择频次">
+                            <el-option v-for="item in cycles" :key="item" :label="item" :value="item" />
+                        </el-select>
+                    </el-col>
 
-          <el-col :span="3">
-            <el-time-picker
-              style="width: 100px"
-              v-model="form.deal_time"
-              prop="deal_time"
-              value-format="HH:mm"
-              :picker-options="{
-                selectableRange: '00:00 - 23:59',
-              }"
-            />
-          </el-col>
-        </el-row>
+                    <el-col :span="3">
+                        <el-time-picker style="width: 100px" v-model="form.deal_time" prop="deal_time"
+                            value-format="HH:mm" :picker-options="{
+                                selectableRange: '00:00 - 23:59',
+                            }" />
+                    </el-col>
+                </el-row>
 
-         <el-row :gutter="20" style="height: 400px">
+                <el-row :gutter="20" style="height: 400px">
 
-          <el-col :span="24">
-              这里放地图
-          </el-col>
-          
-          </el-row>
-      </el-form>
-    </el-dialog>
-  </basic-container>
+                    <el-col :span="24">
+                        <map-container v-if='detailVisible' ref="MapContainer"></map-container>
+                    </el-col>
+
+                </el-row>
+            </el-form>
+        </el-dialog>
+    </basic-container>
 </template>
 
 <script>
 import {
-  getList,
-  saveUpdateOrderLog,
-  orderLogDetails,
-  orderLogRecall,
-  orderLogReject,
-  orderLogPass,
-  orderLogExport,
-} from '@/api/tickets/orderLog';
-import { getTicketInfo } from '@/api/tickets/ticket';
-import { getDictionary } from '@/api/system/dictbiz';
-import { getWaylineFileListByArea } from '@/api/resource/wayline';
-import { export_json_to_excel } from '@/utils/exportExcel';
-import { getFlyingNestBy } from '@/api/device/device';
-import { mapGetters } from 'vuex';
-import NProgress from 'nprogress';
-import { downloadXls } from '@/utils/util';
-import 'nprogress/nprogress.css';
+    getList,
+    saveUpdateOrderLog,
+    orderLogDetails,
+    orderLogRecall,
+    orderLogReject,
+    orderLogPass,
+    orderLogExport,
+} from '@/api/tickets/orderLog'
+import { getTicketInfo } from '@/api/tickets/ticket'
+import { getDictionary } from '@/api/system/dictbiz'
+import { getWaylineFileListByArea } from '@/api/resource/wayline'
+import { export_json_to_excel } from '@/utils/exportExcel'
+import { getFlyingNestBy } from '@/api/device/device'
+import { mapGetters } from 'vuex'
+import NProgress from 'nprogress'
+import { downloadXls } from '@/utils/util'
+import 'nprogress/nprogress.css'
+import { analyzeKmzFile, removeTextKey, XMLToJSON } from '@/utils/cesium/kmz'
 
 export default {
-  name: 'TicketPage',
-  data() {
-    return {
-      activeTab: 'all',
+    name: 'TicketPage',
+    data () {
+        return {
+            activeTab: 'all',
 
-      tabs: [
-        { label: '全部工单', name: 'all', value: null, count: 0 },
-        { label: '待审核', name: 'pending', value: 1, count: 0 },
-        { label: '已驳回', name: 'processing', value: 2, count: 0 },
-        { label: '已通过', name: 'inProgress', value: 3, count: 0 },
-        { label: '草稿', name: 'completed', value: 0, count: 0 },
-      ],
-      filters: {
-        key_word: '',
-        create_dept: '',
-        start_date: null,
-        end_date: null,
-        file_id: '',
-        ai_types: [],
-        status: '',
-        dateRange: [],
-      },
-      departments: [],
-      types: [],
-      device_sns: [],
-      ai_types: [],
-      wayLineList: [],
+            tabs: [
+                { label: '全部工单', name: 'all', value: null, count: 0 },
+                { label: '待审核', name: 'pending', value: 1, count: 0 },
+                { label: '已驳回', name: 'processing', value: 2, count: 0 },
+                { label: '已通过', name: 'inProgress', value: 3, count: 0 },
+                { label: '草稿', name: 'completed', value: 0, count: 0 },
+            ],
+            filters: {
+                key_word: '',
+                create_dept: '',
+                start_date: null,
+                end_date: null,
+                file_id: '',
+                ai_types: [],
+                status: '',
+                dateRange: [],
+            },
+            departments: [],
+            types: [],
+            device_sns: [],
+            ai_types: [],
+            wayLineList: [],
 
-      statuses: [
-        { label: '草稿', value: '0' },
-        { label: '待审核', value: '1' },
-        { label: '已驳回', value: '2' },
-        { label: '已通过', value: '3' },
-      ],
-      //周期
-      cycles: ['周一', '周二', '周三', '周四', '周五', '周六', '周末', '周天', '工作日', '每天'],
+            statuses: [
+                { label: '草稿', value: '0' },
+                { label: '待审核', value: '1' },
+                { label: '已驳回', value: '2' },
+                { label: '已通过', value: '3' },
+            ],
+            //周期
+            cycles: ['周一', '周二', '周三', '周四', '周五', '周六', '周末', '周天', '工作日', '每天'],
 
-      tableData: [],
-      option: {
-        border: true,
-        stripe: true,
-        menuWidth: 150,
-        searchShow: true,
-        searchBtnText: '搜索',
-        searchMenuSpan: 6,
-        addBtnText: '新建工单',
-        addBtn: false,
-        viewBtn: false,
-        editBtn: false,
-        delBtn: false,
-        menu: true,
-        cancelBtn: true,
-        cancelBtnText: '取消',
-        column: [
-          { label: '序号', prop: 'id', width: 50, ellipsis :true },
-          { label: '工单编号', prop: 'job_info_num', width: 100,ellipsis :true,overHidden:true  },
-          { label: '工单名称', prop: 'name', width: 100,ellipsis :true,overHidden:true   },
-          { label: '所属单位', prop: 'dept_name', width: 100 ,ellipsis :true },
-          { label: '发起时间', prop: 'create_time', width: 100,ellipsis :true  },
-          { label: '工单内容', prop: 'content', width: 160,ellipsis :true,overHidden:true  },
-          { label: '关联航线', prop: 'wayline_name', width: 100 ,ellipsis :true,overHidden:true   },
-          { label: '关联算法', prop: 'ai_type_str', width: 100,ellipsis :true,overHidden:true    },
-          { label: '关联机巢', prop: 'device_names', width: 100,ellipsis :true,overHidden:true   },
+            tableData: [],
+            option: {
+                border: true,
+                stripe: true,
+                menuWidth: 150,
+                searchShow: true,
+                searchBtnText: '搜索',
+                searchMenuSpan: 6,
+                addBtnText: '新建工单',
+                addBtn: false,
+                viewBtn: false,
+                editBtn: false,
+                delBtn: false,
+                menu: true,
+                cancelBtn: true,
+                cancelBtnText: '取消',
+                column: [
+                    { label: '序号', prop: 'id', width: 50, ellipsis: true },
+                    { label: '工单编号', prop: 'job_info_num', width: 100, ellipsis: true, overHidden: true },
+                    { label: '工单名称', prop: 'name', width: 100, ellipsis: true, overHidden: true },
+                    { label: '所属单位', prop: 'dept_name', width: 100, ellipsis: true },
+                    { label: '发起时间', prop: 'create_time', width: 100, ellipsis: true },
+                    { label: '工单内容', prop: 'content', width: 160, ellipsis: true, overHidden: true },
+                    { label: '关联航线', prop: 'wayline_name', width: 100, ellipsis: true, overHidden: true },
+                    { label: '关联算法', prop: 'ai_type_str', width: 100, ellipsis: true, overHidden: true },
+                    { label: '关联机巢', prop: 'device_names', width: 100, ellipsis: true, overHidden: true },
 
-          { label: '创建人', prop: 'creator_name', width: 50,ellipsis :true,overHidden:true   },
-          { label: '关联机巢', prop: 'device_names', width: 80,ellipsis :true,overHidden:true   },
-          {
-            label: '工单周期凭次',
-            prop: '',
-            width: 108,
-            formatter: row => this.formatCycleTime(row),
-            html: true,
-            ellipsis :true,overHidden:true  
-      
-    
-          
-          },
-          { label: '工单状态', prop: 'status', width: 60 },
-        ],
-      },
+                    { label: '创建人', prop: 'creator_name', width: 50, ellipsis: true, overHidden: true },
+                    { label: '关联机巢', prop: 'device_names', width: 80, ellipsis: true, overHidden: true },
+                    {
+                        label: '工单周期凭次',
+                        prop: '',
+                        width: 108,
+                        formatter: row => this.formatCycleTime(row),
+                        html: true,
+                        ellipsis: true, overHidden: true
 
-      page: {
-        pageSize: 10,
-        currentPage: 1,
-        total: 0,
-      },
-      dialogVisible: false,
-      detailVisible: false,
-      currentDetail: {},
-      form: {},
-      rules: {
-        name: [{ required: true, message: '请输入工单名称', trigger: 'blur' }],
-        file_id: [{ required: true, message: '需要选择航线', trigger: 'change' }],
-        device_sns: [{ required: true, message: '请选择机巢', trigger: 'change' }],
-        ai_types: [{ required: true, message: '请选择算法', trigger: 'change' }],
-        content: [{ required: true, message: '请输入工单内容', trigger: 'blur' }],
-        date_range: [{ required: true, message: '请选择时间不能为空', trigger: 'blur' }],
-        rep_fre_type: [{ required: true, message: '请选择周期', trigger: 'blur' }],
-        deal_time: [{ required: true, message: '请选择执行时间', trigger: 'blur' }],
-      },
-      loading: false,
-      globalCounts: {},
-      mapLoaded: false,
-    };
-  },
-  async created() {
-    var response = await getDictionary({ code: 'SF' });
-    var word_order_typeResponse = await getDictionary({ code: 'WORK_ORDER_TYPE' });
-    this.ai_types = response.data.data;
-    this.types = word_order_typeResponse.data.data;
-    //获取航线
-    this.asyncgetWaylineFileListByArea();
-       const response2 = await getTicketInfo();
-      const { dept_data, event_type, ai_type } = response2.data.data;
-      this.departments = dept_data.map(item => ({
-        label: item.dept_name,
-        value: item.id,
-      }));
-  },
-  mounted() {
-    this.fetchTableData();
-  },
-  computed: {
-    ...mapGetters(['userInfo']),
-  },
 
-  methods: {
-    searchChange(params, done) {
-      console.log('searchChange');
-      this.query = params;
-      this.parentId = '';
-      this.page.currentPage = 1;
-      this.onLoad(this.page, params);
-      done();
-    },
-    async onLoad(page, params = {}) {
-      this.loading = true;
-      getList(
-        null,
-        this.page.currentPage,
-        this.page.pageSize,
-        Object.assign(params, this.query)
-      ).then(res => {
-        this.tableData = res.data.data;
-        this.loading = false;
-        this.selectionClear();
-      });
 
-   
-    },
-    selectionClear() {
-      this.selectionList = [];
-      this.$refs.crud.toggleSelection();
-    },
-    async loadAMapScripts() {
-      try {
-        // await loadAMap();
-        // await loadAMapUI();
-        this.mapLoaded = true;
-      } catch (error) {
-        console.error('Failed to load AMap scripts:', error);
-        this.$message.error('地图加载失败,请检查网络或API Key配置');
-      }
-    },
-    formatCycleTime(row) {
-      return row.begin_time + '-' + row.end_time +'<br/>'
-       +row.rep_rule_type + row.rep_rule_val;
-    },
+                    },
+                    { label: '工单状态', prop: 'status', width: 60 },
+                ],
+            },
 
-    async fetchTableData() {
-      this.loading = true;
-      try {
-        let params = this.getQueryParam();
-        console.log('发送的参数:', params);
-        const response = await getList(params, this.page.currentPage, this.page.pageSize);
-        if (!response?.data?.data?.records) {
-          throw new Error('接口返回数据格式不正确');
+            page: {
+                pageSize: 10,
+                currentPage: 1,
+                total: 0,
+            },
+            dialogVisible: false,
+            detailVisible: false,
+            currentDetail: {},
+            form: {},
+            rules: {
+                name: [{ required: true, message: '请输入工单名称', trigger: 'blur' }],
+                file_id: [{ required: true, message: '需要选择航线', trigger: 'change' }],
+                device_sns: [{ required: true, message: '请选择机巢', trigger: 'change' }],
+                ai_types: [{ required: true, message: '请选择算法', trigger: 'change' }],
+                content: [{ required: true, message: '请输入工单内容', trigger: 'blur' }],
+                date_range: [{ required: true, message: '请选择时间不能为空', trigger: 'blur' }],
+                rep_fre_type: [{ required: true, message: '请选择周期', trigger: 'blur' }],
+                deal_time: [{ required: true, message: '请选择执行时间', trigger: 'blur' }],
+            },
+            loading: false,
+            globalCounts: {},
+            mapLoaded: false,
         }
-
-        const { total, records } = response.data.data;
-        this.tableData = records.map(item => {
-          return item;
-        });
-
-        if (this.activeTab === 'all') {
-          this.updateGlobalCounts(records, total);
-        }
-
-        this.page.total = total || 0;
-        this.updateTabCounts();
-      } catch (error) {
-        console.error('获取数据失败:', error);
-        this.$message.error(error.message || '获取数据失败');
-        this.tableData = [];
-        this.page.total = 0;
-      } finally {
-        this.loading = false;
-      }
+    },
+    async created () {
+        var response = await getDictionary({ code: 'SF' })
+        var word_order_typeResponse = await getDictionary({ code: 'WORK_ORDER_TYPE' })
+        this.ai_types = response.data.data
+        this.types = word_order_typeResponse.data.data
+        //获取航线
+        this.asyncgetWaylineFileListByArea()
+        const response2 = await getTicketInfo()
+        const { dept_data, event_type, ai_type } = response2.data.data
+        this.departments = dept_data.map(item => ({
+            label: item.dept_name,
+            value: item.id,
+        }))
+    },
+    mounted () {
+        this.fetchTableData()
+    },
+    computed: {
+        ...mapGetters(['userInfo']),
     },
 
-    getQueryParam() {
-      const currentTab = this.tabs.find(tab => tab.name === this.activeTab);
-      if (this.filters.dateRange) {
-        console.log(
-          'this.formatDate(this.filters.dateRange[0])',
-          this.formatDate(this.filters.dateRange[0])
-        );
-      }
+    methods: {
+        searchChange (params, done) {
+            console.log('searchChange')
+            this.query = params
+            this.parentId = ''
+            this.page.currentPage = 1
+            this.onLoad(this.page, params)
+            done()
+        },
+        async onLoad (page, params = {}) {
+            this.loading = true
+            getList(
+                null,
+                this.page.currentPage,
+                this.page.pageSize,
+                Object.assign(params, this.query)
+            ).then(res => {
+                this.tableData = res.data.data
+                this.loading = false
+                this.selectionClear()
+            })
 
-      const params = {
-        key_word: this.filters.key_word || undefined,
-        create_dept: this.filters.create_dept || undefined,
-        ai_types:
-          this.filters.ai_types && this.filters.ai_types.length > 0
-            ? [this.filters.ai_types]
-            : null,
-        file_id: this.filters.file_id || undefined,
-        status: this.filters.status !== '' ? Number(this.filters.status) : currentTab?.value,
-        dept_id: this.filters.department || undefined,
-        creat_start_date: this.filters.dateRange?.[0]
-          ? this.formatDate(this.filters.dateRange[0])
-          : undefined,
-        create_end_date: this.filters.dateRange?.[1]
-          ? this.formatDate(this.filters.dateRange[1]).replace('00:00:00', '23:59:59')
-          : undefined,
 
-        start_date: this.filters.cycleDateRange?.[0]
-          ? this.formatDate(this.filters.cycleDateRange[0])
-          : undefined,
-        end_date: this.filters.dateRacycleDateRangenge?.[1]
-          ? this.formatDate(this.filters.cycleDateRange[1]).replace('00:00:00', '23:59:59')
-          : undefined,
-          rep_fre_type: this.filters.rep_fre_type || undefined,
-          deal_time: this.filters.deal_time || undefined,
-        current: this.page.currentPage,
-        size: this.page.pageSize,
-      };
-      return params;
-    },
-    sizeChange(pageSize) {
-      this.page.pageSize = pageSize;
-    },
-    async submitForm(status) {
-      this.$refs.form.validate(async valid => {
-        if (valid) {
-          let dateRange = this.form.date_range;
-          console.log('dateRange' + dateRange);
+        },
+        selectionClear () {
+            this.selectionList = []
+            this.$refs.crud.toggleSelection()
+        },
+        async loadAMapScripts () {
+            try {
+                // await loadAMap();
+                // await loadAMapUI();
+                this.mapLoaded = true
+            } catch (error) {
+                console.error('Failed to load AMap scripts:', error)
+                this.$message.error('地图加载失败,请检查网络或API Key配置')
+            }
+        },
+        formatCycleTime (row) {
+            return row.begin_time + '-' + row.end_time + '<br/>'
+                + row.rep_rule_type + row.rep_rule_val
+        },
 
-          this.form.begin_time = this.formatDate(dateRange[0]);
-          this.form.end_time = this.formatDate(dateRange[1]);
+        async fetchTableData () {
+            this.loading = true
+            try {
+                let params = this.getQueryParam()
+                console.log('发送的参数:', params)
+                const response = await getList(params, this.page.currentPage, this.page.pageSize)
+                if (!response?.data?.data?.records) {
+                    throw new Error('接口返回数据格式不正确')
+                }
 
-          const submitData = {
-            ...this.form,
+                const { total, records } = response.data.data
+                this.tableData = records.map(item => {
+                    return item
+                })
 
-            status: status,
-          };
-          await saveUpdateOrderLog(submitData);
-          let id = this.form.id;
-          if (id) {
-            this.$message.success('工单发布成功');
-          } else {
-            this.$message.success('工单创建成功');
-          }
-          this.dialogVisible = false;
-          this.detailVisible = false;
-          (this.device_sns = []), (this.wayLineList = []), this.fetchTableData();
-        }
-      });
-    },
-    //驳回原因显示
-    async rejectDetail(id) {
-      const response = await orderLogDetails(id);
-      let data = response.data.data;
-      this.$confirm(data.remark, '驳回原因', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning',
-      }).then(() => {
-        this.form = {
-          ...response.data.data,
-        };
-        this.detailVisible = true;
-      });
-    },
-    formatDate(date) {
-      if (!date) return undefined;
-      const d = new Date(date);
-      return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(
-        d.getDate()
-      ).padStart(2, '0')} 00:00:00`;
+                if (this.activeTab === 'all') {
+                    this.updateGlobalCounts(records, total)
+                }
+
+                this.page.total = total || 0
+                this.updateTabCounts()
+            } catch (error) {
+                console.error('获取数据失败:', error)
+                this.$message.error(error.message || '获取数据失败')
+                this.tableData = []
+                this.page.total = 0
+            } finally {
+                this.loading = false
+            }
+        },
+
+        getQueryParam () {
+            const currentTab = this.tabs.find(tab => tab.name === this.activeTab)
+            if (this.filters.dateRange) {
+                console.log(
+                    'this.formatDate(this.filters.dateRange[0])',
+                    this.formatDate(this.filters.dateRange[0])
+                )
+            }
+
+            const params = {
+                key_word: this.filters.key_word || undefined,
+                create_dept: this.filters.create_dept || undefined,
+                ai_types:
+                    this.filters.ai_types && this.filters.ai_types.length > 0
+                        ? [this.filters.ai_types]
+                        : null,
+                file_id: this.filters.file_id || undefined,
+                status: this.filters.status !== '' ? Number(this.filters.status) : currentTab?.value,
+                dept_id: this.filters.department || undefined,
+                creat_start_date: this.filters.dateRange?.[0]
+                    ? this.formatDate(this.filters.dateRange[0])
+                    : undefined,
+                create_end_date: this.filters.dateRange?.[1]
+                    ? this.formatDate(this.filters.dateRange[1]).replace('00:00:00', '23:59:59')
+                    : undefined,
+
+                start_date: this.filters.cycleDateRange?.[0]
+                    ? this.formatDate(this.filters.cycleDateRange[0])
+                    : undefined,
+                end_date: this.filters.dateRacycleDateRangenge?.[1]
+                    ? this.formatDate(this.filters.cycleDateRange[1]).replace('00:00:00', '23:59:59')
+                    : undefined,
+                rep_fre_type: this.filters.rep_fre_type || undefined,
+                deal_time: this.filters.deal_time || undefined,
+                current: this.page.currentPage,
+                size: this.page.pageSize,
+            }
+            return params
+        },
+        sizeChange (pageSize) {
+            this.page.pageSize = pageSize
+        },
+        async submitForm (status) {
+            this.$refs.form.validate(async valid => {
+                if (valid) {
+                    let dateRange = this.form.date_range
+                    console.log('dateRange' + dateRange)
+
+                    this.form.begin_time = this.formatDate(dateRange[0])
+                    this.form.end_time = this.formatDate(dateRange[1])
+
+                    const submitData = {
+                        ...this.form,
+
+                        status: status,
+                    }
+                    await saveUpdateOrderLog(submitData)
+                    let id = this.form.id
+                    if (id) {
+                        this.$message.success('工单发布成功')
+                    } else {
+                        this.$message.success('工单创建成功')
+                    }
+                    this.dialogVisible = false
+                    this.detailVisible = false;
+                    (this.device_sns = []), (this.wayLineList = []), this.fetchTableData()
+                }
+            })
+        },
+        //驳回原因显示
+        async rejectDetail (id) {
+            const response = await orderLogDetails(id)
+            let data = response.data.data
+            this.$confirm(data.remark, '驳回原因', {
+                confirmButtonText: '确定',
+                cancelButtonText: '取消',
+                type: 'warning',
+            }).then(() => {
+                this.form = {
+                    ...response.data.data,
+                }
+                this.detailVisible = true
+            })
+        },
+        formatDate (date) {
+            if (!date) return undefined
+            const d = new Date(date)
+            return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(
+                d.getDate()
+            ).padStart(2, '0')} 00:00:00`
+        },
+
+        mapStatus (status) {
+            const statusTextMap = {
+                0: '草稿',
+                1: '待审核',
+                2: '已驳回',
+                3: '已通过',
+            }
+            return statusTextMap[status] || '未知状态'
+        },
+
+        getStatusTagType (status) {
+            const statusMap = {
+                1: 'warning',
+                2: 'info',
+                3: 'primary',
+                4: 'success',
+                5: 'danger',
+            }
+            return statusMap[status] || 'info'
+        },
+
+        handleTabChange (tab) {
+            this.activeTab = tab.props?.name || tab.name
+            this.filters.status = ''
+            this.page.currentPage = 1
+            this.fetchTableData()
+        },
+
+        handleSearch () {
+            this.page.currentPage = 1
+            this.fetchTableData()
+        },
+
+        handleReset () {
+            this.filters = {
+                keyword: '',
+                department: '',
+                type: '',
+                dateRange: [],
+                status: '',
+            }
+            this.page.currentPage = 1
+            this.fetchTableData()
+        },
+
+        currentChange (currentPage) {
+            this.page.currentPage = currentPage
+        },
+
+        updateGlobalCounts (records, total) {
+            const counts = {
+                all: total,
+                pending: 0,
+                processing: 0,
+                inProgress: 0,
+                completed: 0,
+                closed: 0,
+                myTickets: 0,
+            }
+
+            records.forEach(item => {
+                const tab = this.tabs.find(t => t.value === Number(item.status))
+                if (tab) {
+                    counts[tab.name] = (counts[tab.name] || 0) + 1
+                }
+            })
+
+            this.globalCounts = counts
+        },
+
+        updateTabCounts () {
+            if (this.activeTab === 'all') {
+                this.tabs.forEach(tab => {
+                    tab.count = this.globalCounts[tab.name] || 0
+                })
+            } else {
+                this.tabs.forEach(tab => {
+                    if (tab.name === this.activeTab) {
+                        tab.count = this.tableData.length
+                    } else {
+                        tab.count = this.globalCounts[tab.name] || 0
+                    }
+                })
+            }
+        },
+
+        handleAdd () {
+            this.form = {}
+            this.dialogVisible = true
+            //航线列表
+            this.asyncgetWaylineFileListByArea()
+        },
+
+        resetForm () {
+            this.form = {
+                name: '',
+                type: '',
+                handler: '',
+                algorithm: '',
+                location: '',
+                address: '',
+                content: '',
+                photos: [],
+            }
+            if (this.$refs.form) {
+                this.$refs.form.resetFields()
+            }
+        },
+
+        formatLocation (location) {
+            if (!Array.isArray(location)) {
+                return '未知位置'
+            }
+            return `${location[0].toFixed(6)}, ${location[1].toFixed(6)}`
+        },
+
+        async handleViewDetail (row) {
+            const response = await orderLogDetails(row.id)
+            this.form = {
+                ...response.data.data,
+            }
+            this.detailVisible = true
+
+            //航线列表
+            this.asyncgetWaylineFileListByArea()
+            this.device_sns = response.data.data.deviceList
+        },
+        //导出
+        async exportData () {
+            this.$confirm('是否智飞工单数据?', '提示', {
+                confirmButtonText: '确定',
+                cancelButtonText: '取消',
+                type: 'warning',
+            }).then(() => {
+                NProgress.start()
+                let params = this.getQueryParam()
+                orderLogExport(params).then(res => {
+                    downloadXls(res.data, `智飞工单${this.$dayjs().format('YYYY-MM-DD')}.xlsx`)
+                    NProgress.done()
+                })
+            })
+        },
+
+        refreshChange () {
+            this.fetchTableData()
+        },
+        //获取航线列表
+        async asyncgetWaylineFileListByArea (name) {
+            var wayLineListResponse = await getWaylineFileListByArea(this.userInfo.detail.areaCode)
+            this.wayLineList = wayLineListResponse.data.data
+
+            this.initMapLine()
+        },
+
+        initMapLine () {
+            let currentLine = this.wayLineList.find(item => item.wayline_id == this.form.file_id)
+
+            if (!currentLine) return
+
+            // 异步解析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 + currentLine.object_key)
+                const res = await analysis(prexUrl.value)
+                if (!res.Placemark.length) return
+                renderingLine(res)
+            }
+
+            const renderingLine = lineObj => {
+                const positions = lineObj.Placemark.map(item => {
+                    return item.Point.coordinates.split(',')
+                })
+
+                this.$nextTick(() => {
+                    if (this.$refs.MapContainer && this.$refs.MapContainer.initAddEntity) {
+                        this.$refs.MapContainer.initAddEntity('polyline', positions)
+                    }
+                })
+            }
+
+            drawLine()
+        },
+
+
+
+
+        //可飞行机巢列表
+        async getFlyingNestBy (waylineId) {
+            this.initMapLine()
+
+            //按照航线来
+            const params = {
+                type: 0,
+                waylineId: waylineId,
+            }
+            var wayLineListResponse = await getFlyingNestBy(params)
+            this.device_sns = wayLineListResponse.data.data
+        },
+
+        //撤回
+        async orderLogRecall (id) {
+            this.$confirm('确定撤回则到草稿箱。', '是否撤回?', {
+                confirmButtonText: '确定',
+                cancelButtonText: '取消',
+                type: 'warning',
+            }).then(async () => {
+                let reposne = await orderLogRecall(id)
+                this.handleSearch()
+            })
+        },
+        onLoad () {
+            this.fetchTableData()
+        },
+        /**
+         * 通过
+         */
+        async orderLogPass (id) {
+            let response = await orderLogPass(id)
+            let data = response.data.data
+            this.$message.success('审核通过')
+            this.detailVisible = false
+        },
+        /**
+         * 驳回
+         */
+        async orderLogReject (id) {
+            this.$prompt('', '驳回原因', {
+                confirmButtonText: '确定',
+                cancelButtonText: '取消',
+            }).then(async ({ value }) => {
+                let response = await orderLogReject(id, value)
+                let data = response.data.data
+                this.$message.success('驳回成果')
+                this.detailVisible = false
+            })
+        },
     },
 
-    mapStatus(status) {
-      const statusTextMap = {
-        0: '草稿',
-        1: '待审核',
-        2: '已驳回',
-        3: '已通过',
-      };
-      return statusTextMap[status] || '未知状态';
+    watch: {
+        tableData: {
+            handler () {
+                this.updateTabCounts()
+            },
+            deep: true,
+        },
     },
-
-    getStatusTagType(status) {
-      const statusMap = {
-        1: 'warning',
-        2: 'info',
-        3: 'primary',
-        4: 'success',
-        5: 'danger',
-      };
-      return statusMap[status] || 'info';
-    },
-
-    handleTabChange(tab) {
-      this.activeTab = tab.props?.name || tab.name;
-      this.filters.status = '';
-      this.page.currentPage = 1;
-      this.fetchTableData();
-    },
-
-    handleSearch() {
-      this.page.currentPage = 1;
-      this.fetchTableData();
-    },
-
-    handleReset() {
-      this.filters = {
-        keyword: '',
-        department: '',
-        type: '',
-        dateRange: [],
-        status: '',
-      };
-      this.page.currentPage = 1;
-      this.fetchTableData();
-    },
-
-    currentChange(currentPage) {
-      this.page.currentPage = currentPage;
-    },
-
-    updateGlobalCounts(records, total) {
-      const counts = {
-        all: total,
-        pending: 0,
-        processing: 0,
-        inProgress: 0,
-        completed: 0,
-        closed: 0,
-        myTickets: 0,
-      };
-
-      records.forEach(item => {
-        const tab = this.tabs.find(t => t.value === Number(item.status));
-        if (tab) {
-          counts[tab.name] = (counts[tab.name] || 0) + 1;
-        }
-      });
-
-      this.globalCounts = counts;
-    },
-
-    updateTabCounts() {
-      if (this.activeTab === 'all') {
-        this.tabs.forEach(tab => {
-          tab.count = this.globalCounts[tab.name] || 0;
-        });
-      } else {
-        this.tabs.forEach(tab => {
-          if (tab.name === this.activeTab) {
-            tab.count = this.tableData.length;
-          } else {
-            tab.count = this.globalCounts[tab.name] || 0;
-          }
-        });
-      }
-    },
-
-    handleAdd() {
-      this.form = {};
-      this.dialogVisible = true;
-      //航线列表
-      this.asyncgetWaylineFileListByArea();
-    },
-
-    resetForm() {
-      this.form = {
-        name: '',
-        type: '',
-        handler: '',
-        algorithm: '',
-        location: '',
-        address: '',
-        content: '',
-        photos: [],
-      };
-      if (this.$refs.form) {
-        this.$refs.form.resetFields();
-      }
-    },
-
-    formatLocation(location) {
-      if (!Array.isArray(location)) {
-        return '未知位置';
-      }
-      return `${location[0].toFixed(6)}, ${location[1].toFixed(6)}`;
-    },
-
-    async handleViewDetail(row) {
-      const response = await orderLogDetails(row.id);
-      this.form = {
-        ...response.data.data,
-      };
-      this.detailVisible = true;
-      //航线列表
-      this.asyncgetWaylineFileListByArea();
-      this.device_sns = response.data.data.deviceList;
-    },
-    //导出
-    async exportData() {
-      this.$confirm('是否智飞工单数据?', '提示', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning',
-      }).then(() => {
-        NProgress.start();
-        let params = this.getQueryParam();
-        orderLogExport(params).then(res => {
-          downloadXls(res.data, `智飞工单${this.$dayjs().format('YYYY-MM-DD')}.xlsx`);
-          NProgress.done();
-        });
-      });
-    },
-
-    refreshChange() {
-      this.fetchTableData();
-    },
-    //获取航线列表
-    async asyncgetWaylineFileListByArea(name) {
-      var wayLineListResponse = await getWaylineFileListByArea(this.userInfo.detail.areaCode);
-      this.wayLineList = wayLineListResponse.data.data;
-    },
-    //可飞行机巢列表
-    async getFlyingNestBy(waylineId) {
-      //按照航线来
-      const params = {
-        type: 0,
-        waylineId: waylineId,
-      };
-      var wayLineListResponse = await getFlyingNestBy(params);
-      this.device_sns = wayLineListResponse.data.data;
-    },
-
-    //撤回
-    async orderLogRecall(id) {
-      this.$confirm('确定撤回则到草稿箱。', '是否撤回?', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-        type: 'warning',
-      }).then(async () => {
-        let reposne = await orderLogRecall(id);
-        this.handleSearch();
-      });
-    },
-    onLoad() {
-      this.fetchTableData();
-    },
-    /**
-     * 通过
-     */
-    async orderLogPass(id) {
-      let response = await orderLogPass(id);
-      let data = response.data.data;
-      this.$message.success('审核通过');
-      this.detailVisible = false;
-    },
-    /**
-     * 驳回
-     */
-    async orderLogReject(id) {
-      this.$prompt('', '驳回原因', {
-        confirmButtonText: '确定',
-        cancelButtonText: '取消',
-      }).then(async ({ value }) => {
-        let response = await orderLogReject(id, value);
-        let data = response.data.data;
-        this.$message.success('驳回成果');
-        this.detailVisible = false;
-      });
-    },
-  },
-
-  watch: {
-    tableData: {
-      handler() {
-        this.updateTabCounts();
-      },
-      deep: true,
-    },
-  },
-};
+}
 </script>
 
 <style lang="scss" scoped>
 .tab-content {
-  padding: 10px;
+    padding: 10px;
 }
 
 .filter-bar {
-  display: flex;
-  align-items: center;
-  margin-bottom: 15px;
-  flex-wrap: wrap;
+    display: flex;
+    align-items: center;
+    margin-bottom: 15px;
+    flex-wrap: wrap;
 
-  .filter-item {
-    margin-right: 10px;
-    margin-bottom: 10px;
-    width: 200px;
-  }
+    .filter-item {
+        margin-right: 10px;
+        margin-bottom: 10px;
+        width: 200px;
+    }
 
-  .date-picker {
-    width: 240px;
-  }
+    .date-picker {
+        width: 240px;
+    }
 }
 
 .action-bar {
-  margin-bottom: 16px;
+    margin-bottom: 16px;
 }
 
 .el-tabs {
-  :deep(.el-tabs__content) {
-    overflow: visible;
-  }
+    :deep(.el-tabs__content) {
+        overflow: visible;
+    }
 }
 
 .tab-content {
-  min-height: 200px;
+    min-height: 200px;
 }
 
 .detail-form {
-  :deep(.el-form-item) {
-    margin-bottom: 10px;
-  }
+    :deep(.el-form-item) {
+        margin-bottom: 10px;
+    }
 
-  :deep(.el-form-item__label) {
-    color: #606266;
-    font-weight: normal;
-  }
+    :deep(.el-form-item__label) {
+        color: #606266;
+        font-weight: normal;
+    }
 
-  :deep(.el-form-item__content) {
-    color: #303133;
-  }
+    :deep(.el-form-item__content) {
+        color: #303133;
+    }
 }
 
 .el-dialog {
-  .el-form-item {
-    margin-bottom: 20px;
-  }
+    .el-form-item {
+        margin-bottom: 20px;
+    }
 }
 
 .el-upload {
-  width: 100%;
-  display: flex;
-  flex-wrap: wrap;
+    width: 100%;
+    display: flex;
+    flex-wrap: wrap;
 
-  :deep(.el-upload-list__item) {
-    transition: all 0.3s ease;
-  }
+    :deep(.el-upload-list__item) {
+        transition: all 0.3s ease;
+    }
 
-  :deep(.el-upload-list__item:hover) {
-    background-color: #f5f7fa;
-  }
+    :deep(.el-upload-list__item:hover) {
+        background-color: #f5f7fa;
+    }
 }
 
 .el-upload__tip {
-  font-size: 12px;
-  color: #909399;
-  margin-top: 7px;
+    font-size: 12px;
+    color: #909399;
+    margin-top: 7px;
 }
 
 .custom-steps-container {
-  width: 100%;
-  margin: 20px 0;
+    width: 100%;
+    margin: 20px 0;
 }
 
 .steps-titles {
-  display: flex;
-  justify-content: space-between;
-  margin-bottom: 16px;
-  position: relative;
+    display: flex;
+    justify-content: space-between;
+    margin-bottom: 16px;
+    position: relative;
 }
 
 .step-title {
-  text-align: center;
-  flex: 1;
-  font-size: 16px;
-  color: #999;
-  position: relative;
-  padding-bottom: 10px;
+    text-align: center;
+    flex: 1;
+    font-size: 16px;
+    color: #999;
+    position: relative;
+    padding-bottom: 10px;
 }
 
 .step-title.active {
-  color: #409eff;
-  font-weight: bold;
+    color: #409eff;
+    font-weight: bold;
 }
 
 .custom-steps {
-  margin-top: -20px; /* 向上移动与标题重叠 */
+    margin-top: -20px;
+    /* 向上移动与标题重叠 */
 }
 
 .el-step__head {
-  margin-top: 0;
+    margin-top: 0;
 }
 
 .el-step__description {
-  margin-top: 8px;
-  padding: 0 20px;
+    margin-top: 8px;
+    padding: 0 20px;
 }
 
 .step-description {
-  font-size: 14px;
-  color: #666;
-  line-height: 1.5;
+    font-size: 14px;
+    color: #666;
+    line-height: 1.5;
 }
 </style>
\ No newline at end of file

--
Gitblit v1.9.3