From bdfc99ddd7390ed67533ed68af79ecb80104b115 Mon Sep 17 00:00:00 2001
From: GuLiMmo <2820890765@qq.com>
Date: Thu, 30 Nov 2023 15:41:24 +0800
Subject: [PATCH] 航线事件编辑修改

---
 env/.env.dev                            |    1 
 src/pages/page-web/projects/wayline.vue |   16 +
 src/components/MediaPanel.vue           |    2 
 env/.env.production                     |    4 
 src/components/waylinetool/index.vue    |  375 +++++++++++++++++++++++++++++++++++++++++++++++-----
 5 files changed, 350 insertions(+), 48 deletions(-)

diff --git a/env/.env.dev b/env/.env.dev
index 629bab3..68dada4 100644
--- a/env/.env.dev
+++ b/env/.env.dev
@@ -1,4 +1,5 @@
 VITE_API_URL = 'http://192.168.1.133:6789'
+VITE_MEDIAPANEL_API_URL = 'https://dev.jxpskj.com:8026/cloud-bucket',
 VITE_WS_API_URL = 'ws://192.168.1.133:6789/api/v1/ws'
 VITE_APP_ENVIRONMENT=DEV
 VITE_BASE_API = '/drone-api'
\ No newline at end of file
diff --git a/env/.env.production b/env/.env.production
index ea9232a..57d71b4 100644
--- a/env/.env.production
+++ b/env/.env.production
@@ -1,6 +1,6 @@
 VITE_APP_ENVIRONMENT=PROD
 VITE_APP_APIGATEWAY_BACKEND_HOST=''
 VITE_API_URL = 'https://dev.jxpskj.com:36789'
+VITE_MEDIAPANEL_API_URL = 'https://dev.jxpskj.com:8026/cloud-bucket',
 VITE_WS_API_URL = 'wss://dev.jxpskj.com:36789/api/v1/ws'
-VITE_BASE_API = '/drone-api'
-
+VITE_BASE_API = '/drone-api'
\ No newline at end of file
diff --git a/src/components/MediaPanel.vue b/src/components/MediaPanel.vue
index e00294d..55dca59 100644
--- a/src/components/MediaPanel.vue
+++ b/src/components/MediaPanel.vue
@@ -126,7 +126,7 @@
 const videoPlayerId = ref('videoPlayerId')
 // 文件前缀
 // const prefix = 'https://dev.jxpskj.com:8026/cloud-bucket'
-const prefix = mediaPanelPrefix || 'https://dev.jxpskj.com:8026/cloud-bucket'
+const prefix = mediaPanelPrefix || import.meta.env.VITE_MEDIAPANEL_API_URL
 // 搜索栏配置项
 const searchPanelOptions = reactive({
   size: 'large',
diff --git a/src/components/waylinetool/index.vue b/src/components/waylinetool/index.vue
index a70281b..fe91a10 100644
--- a/src/components/waylinetool/index.vue
+++ b/src/components/waylinetool/index.vue
@@ -1,16 +1,38 @@
 <template>
-    <ul class="btn-list" v-if="waylineAbout.isShow">
-        <li class="s-btn" v-for="(item, index) in btnList" :key="index">
-          <div class="title">{{ item.title }}</div>
-          <div class="btn" @click="item.event">
-            <img :src="item.icon" alt="icon">
-          </div>
-        </li>
-    </ul>
+  <ul class="btn-list" v-if="waylineAbout.isShow">
+    <li class="s-btn" v-for="(item, index) in btnList" :key="index">
+      <div class="title">{{ item.title }}</div>
+      <div class="btn" @click="item.event(item)" :style="{ backgroundColor: item.selected ? '#2D8CF0' : '#323131' }">
+        <img :src="item.icon" alt="icon">
+      </div>
+    </li>
+    <li class="s-btn">
+      <div class="title">取消编辑</div>
+      <div class="btn cancel-edit" @click="cencalEdit">
+        <CloseOutlined />
+      </div>
+    </li>
+    <li class="s-btn">
+      <div class="title">确认编辑</div>
+      <div class="btn confirm-edit" @click="confirmEdit">
+        <CheckOutlined />
+      </div>
+    </li>
+  </ul>
 </template>
 <script lang="ts" setup>
 import { ref, computed } from 'vue'
 import { useMyStore } from '/@/store'
+import axios from 'axios'
+import JSZIP from 'jszip'
+import { saveAs } from 'file-saver'
+import { message } from 'ant-design-vue'
+import {
+  CloseOutlined,
+  CheckOutlined
+} from '@ant-design/icons-vue'
+// 初始化jszip
+const JsZip = new JSZIP()
 
 const store = useMyStore()
 
@@ -20,78 +42,328 @@
 
 // 点位按钮list
 interface sBtn {
+  key: string,
   icon: string,
   title: string,
-  event: Function
+  event: Function,
+  selected?: boolean
 }
+
+// 保存当前选择的kmz文件
+const kmzFileZip = ref()
+// 当前选中的位置
+const currentPosition = ref()
+const curKmzName = ref<string>('')
+// 当前kmzpath
+const curKmzPath = ref<string>('')
 
 const btnList = ref<sBtn[]>([
   {
+    key: 'startRecord',
     icon: getResource('waylinetool/camera-on.png'),
     title: '开始录像',
-    event: () => {}
+    event: (obj: sBtn) => {
+      obj.selected = !obj.selected
+      editKmlFile('startRecord', {
+        payloadPositionIndex: 0,
+        fileSuffix: `_${new Date().toLocaleString()}`,
+        payloadLensIndex: 'zoom',
+        useGlobalPayloadLensIndex: 0
+      })
+    }
   },
   {
+    key: 'stopRecord',
     icon: getResource('waylinetool/camera-off.png'),
     title: '停止录像',
-    event: () => {}
+    event: (obj: sBtn) => {
+      obj.selected = !obj.selected
+      editKmlFile('stopRecord', {
+        payloadPositionIndex: 0,
+        payloadLensIndex: 'zoom'
+      })
+    }
   },
+  // {
+  //   key: '',
+  //   icon: getResource('waylinetool/shoot1.png'),
+  //   title: '开始等时间隔拍照',
+  //   event: (obj: sBtn) => {
+  //     obj.selected = !obj.selected
+  //     editKmlFile()
+  //   }
+  // },
+  // {
+  //   key: 'shootType',
+  //   icon: getResource('waylinetool/shoot2.png'),
+  //   title: '开始等距间隔拍照',
+  //   event: (obj: sBtn) => {
+  //     obj.selected = !obj.selected
+  //     editKmlFile()
+  //   }
+  // },
+  // {
+  //   key: '',
+  //   icon: getResource('waylinetool/shoot3.png'),
+  //   title: '结束间隔拍照',
+  //   event: (obj: sBtn) => {
+  //     obj.selected = !obj.selected
+  //     editKmlFile()
+  //   }
+  // },
   {
-    icon: '',
-    title: '开始等时间隔拍照',
-    event: () => {}
-  },
-  {
-    icon: '',
-    title: '开始等距间隔拍照',
-    event: () => {}
-  },
-  {
-    icon: '',
-    title: '结束间隔拍照',
-    event: () => {}
-  },
-  {
+    key: 'hover',
     icon: getResource('waylinetool/xt.png'),
     title: '悬停',
-    event: () => {}
+    event: (obj: sBtn) => {
+      obj.selected = !obj.selected
+      editKmlFile('hover', {
+        hoverTime: 10
+      })
+    }
   },
   {
-    icon: '',
+    key: 'rotateYaw',
+    icon: getResource('waylinetool/droneyaw.png'),
     title: '飞行器偏航角',
-    event: () => {}
+    event: (obj: sBtn) => {
+      obj.selected = !obj.selected
+      editKmlFile('rotateYaw', {
+        aircraftHeading: 0,
+        aircraftPathMode: 'clockwise'
+      })
+    }
   },
   {
-    icon: '',
+    key: 'gimbalRotate',
+    icon: getResource('waylinetool/holderyaw.png'),
     title: '云台偏航角',
-    event: () => {}
+    event: (obj: sBtn) => {
+      obj.selected = !obj.selected
+      editKmlFile('gimbalRotate', {
+        payloadPositionIndex: 0,
+        gimbalHeadingYawBase: 'north',
+        gimbalRotateMode: 'relativeAngle',
+        gimbalPitchRotateEnable: 0,
+        gimbalPitchRotateAngle: 0.4,
+        gimbalRollRotateEnable: 0,
+        gimbalRollRotateAngle: 0.4,
+        gimbalYawRotateEnable: 0,
+        gimbalYawRotateAngle: 0.4,
+        gimbalRotateTimeEnable: 0,
+        gimbalRotateTime: 5
+      })
+    }
   },
+  // {
+  //   key: '',
+  //   icon: getResource('waylinetool/holdertilt.png'),
+  //   title: '云台俯仰角',
+  //   event: (obj: sBtn) => {
+  //     obj.selected = !obj.selected
+  //     editKmlFile('gimbalRotate', {
+  //       payloadPositionIndex: 0,
+  //       gimbalHeadingYawBase: 'north',
+  //       gimbalRotateMode: 'relativeAngle',
+  //       gimbalPitchRotateEnable: 0,
+  //       gimbalPitchRotateAngle: 0.4,
+  //       gimbalRollRotateEnable: 0,
+  //       gimbalRollRotateAngle: 0.4,
+  //       gimbalYawRotateEnable: 0,
+  //       gimbalYawRotateAngle: 0.4,
+  //       gimbalRotateTimeEnable: 0,
+  //       gimbalRotateTime: 5
+  //     })
+  //   }
+  // },
   {
-    icon: '',
-    title: '云台俯仰角',
-    event: () => {}
-  },
-  {
+    key: 'takePhoto',
     icon: getResource('waylinetool/camera.png'),
     title: '拍照',
-    event: () => {}
+    event: (obj: sBtn) => {
+      obj.selected = !obj.selected
+      editKmlFile('takePhoto', {
+        payloadPositionIndex: 0,
+        fileSuffix: `_${new Date().toLocaleString()}`,
+        payloadLensIndex: 'zoom',
+        useGlobalPayloadLensIndex: 0
+      })
+    }
   },
   {
+    key: 'zoom',
     icon: getResource('waylinetool/fd.png'),
     title: '相机变焦',
-    event: () => {}
+    event: (obj: sBtn) => {
+      obj.selected = !obj.selected
+      editKmlFile('zoom', {
+        payloadPositionIndex: 0,
+        focalLength: 2
+      })
+    }
   },
   {
+    key: 'customDirName',
     icon: getResource('waylinetool/create-file.png'),
     title: '创建文件夹',
-    event: () => {}
+    event: (obj: sBtn) => {
+      obj.selected = !obj.selected
+      editKmlFile('customDirName', {
+        payloadPositionIndex: 0,
+        directoryName: '新建文件夹'
+      })
+    }
   }
 ])
 
 const waylineAbout = computed(() => {
-  console.log(store.state.waylineTool)
   return store.state.waylineTool
 })
+
+const readKmzFile = (kmzPath: string) => {
+  // return axios.get(kmzPath, { responseType: 'arraybuffer' })
+  return axios({
+    method: 'GET',
+    url: kmzPath,
+    headers: {
+      'X-Auth-Token': window.localStorage.getItem('x-auth-token')
+    },
+    responseType: 'arraybuffer'
+  })
+    .then(fileRes => fileRes.data)
+    .then(kmzData => JsZip.loadAsync(kmzData)) // 解压kmz文件
+    .then(kmzZip => kmzZip)
+    .catch(() => message.error('请求kmz文件失败!!'))
+}
+
+watch(() => store.state.waylineTool.kmzPath, (n: string) => {
+  curKmzPath.value = n
+  const arr = n.split('/')
+  curKmzName.value = decodeURIComponent(arr[arr.length - 1])
+  readKmzFile(n).then(kmzFile => {
+    kmzFileZip.value = kmzFile
+  })
+}, {
+  deep: true
+})
+
+watch(() => store.state.waylineTool.position, (position: number) => {
+  currentPosition.value = position
+  eventLight()
+}, {
+  deep: true
+})
+
+// 事件高亮
+const eventLight = () => {
+  // 已经添加的事件高亮
+  btnList.value.forEach(item => { item.selected = false })
+  kmzFileZip.value.file(/\.kml$/i)[0].async('text').then((content: string) => {
+    const regx = /<Placemark>([\s\S]*?)<\/Placemark>/g
+    const points = content.match(regx) || []
+    const funcRegx = /<wpml:actionActuatorFunc>([\s\S]*?)<\/wpml:actionActuatorFunc>/g
+    const funcs = (points[currentPosition.value] || '').match(funcRegx)
+    const funcNameRegx = /<wpml:actionActuatorFunc>(.*?)<\/wpml:actionActuatorFunc>/
+    btnList.value.forEach(sBtn => {
+      funcs?.forEach((func: string) => {
+        const funcName = func.match(funcNameRegx) || []
+        if (funcName[1] === sBtn.key) {
+          sBtn.selected = true
+        }
+      })
+    })
+  })
+}
+
+// 创建kml模版
+const createActionKML = (actionId: string, actionType: string, param: any) => {
+  let paramKML = ''
+  Object.keys(param).forEach((key: string) => {
+    paramKML += `
+      <wpml:${key}>${param[key]}</wpml:${key}>
+    `
+  })
+  const template = `
+    <wpml:action>
+      <wpml:actionId>${actionId}</wpml:actionId>
+      <wpml:actionActuatorFunc>${actionType}</wpml:actionActuatorFunc>
+      <wpml:actionActuatorFuncParam>
+        ${paramKML}
+      </wpml:actionActuatorFuncParam>
+    </wpml:action>
+  `
+  return template
+}
+
+// 修改template.kml文件
+const editKmlFile = (eventType: string, eventParam: any) => {
+  // kmzZip文件处理
+  const kmlFile = kmzFileZip.value.file(/\.kml$/i)[0]
+  kmlFile.async('text').then((kmlText: string) => {
+    const regx = /<Placemark>([\s\S]*?)<\/Placemark>/g
+    const points = kmlText.match(regx) || []
+    // 当前要修改的字段
+    // const curSelected: string | any = points?.find(item => item.includes(currentPosition.value.join(',')))
+    const curSelected: string | any = points[currentPosition.value]
+    // 查找actionGroup
+    const actionGroupReg = /<wpml:actionGroup>([\s\S]*?)<\/wpml:actionGroup>/g
+    const actionGroup = curSelected.match(actionGroupReg) ? curSelected.match(actionGroupReg)[0] : ''
+    // 查找action
+    const actionReg = /<wpml:action>([\s\S]*?)<\/wpml:action>/g
+    const actions = actionGroup.match(actionReg) || []
+
+    // 判断kml文件中是否存在当前事件
+    const actionIndex = actions.findIndex((action: string) => action.includes(eventType))
+    if (actionIndex === -1) {
+      // 创建事件
+      const actionKML = createActionKML(actions.length, eventType, eventParam)
+      actions.push(actionKML)
+    } else {
+      // 移除当前存在的事件
+      actions.splice(actionIndex, 1)
+    }
+
+    let replacedActionsKML = ''
+    // 创建actionGroup
+    const actionGroupKML = `
+      <wpml:actionGroup>
+        ${actions.join('')}
+      </wpml:actionGroup>
+    `
+    // 判断是否存在事件组
+    if (!actionGroup) {
+      const index = curSelected.indexOf('</Placemark>')
+      replacedActionsKML = curSelected.slice(0, index) + actionGroupKML + curSelected.slice(index)
+    } else {
+      // 替换原有的action
+      // replacedActionsKML = curSelected.replace(actionReg, actions.join(''))
+      replacedActionsKML = curSelected.replace(actionGroupReg, actionGroupKML)
+    }
+    // Placemark代替
+    const replacedTemplateKML = kmlText.replace(curSelected, replacedActionsKML)
+    // 转成unit8格式
+    // const textEncoder = new TextEncoder()
+    // const uint8Array = textEncoder.encode(replacedTemplateKML)
+    JsZip.file('wpmz/template.kml', replacedTemplateKML)
+    kmzFileZip.value.file('wpmz/template.kml', replacedTemplateKML)
+    // JsZip.generateAsync({ type: 'blob' }).then(content => {
+    //   saveAs(content, curKmzName.value)
+    // })
+  })
+}
+
+const cencalEdit = () => {
+  readKmzFile(curKmzPath.value).then(kmzFile => {
+    kmzFileZip.value = kmzFile
+    eventLight()
+  })
+}
+
+const confirmEdit = () => {
+  JsZip.generateAsync({ type: 'blob' }).then(content => {
+    saveAs(content, curKmzName.value)
+  })
+}
 
 </script>
 <style lang="scss" scoped>
@@ -100,13 +372,16 @@
   right: 75px;
   top: 50%;
   transform: translateY(-50%);
+
   .s-btn {
     margin-bottom: 15px;
     display: flex;
     align-items: center;
+
     &:last-child {
       margin: 0;
     }
+
     .title {
       width: 130px;
       text-align: right;
@@ -115,6 +390,7 @@
       text-shadow: 2px 2px 2px #000;
       font-weight: bold;
     }
+
     .btn {
       width: 34px;
       height: 34px;
@@ -123,10 +399,29 @@
       display: flex;
       justify-content: center;
       align-items: center;
+
       img {
         width: 20px;
       }
     }
   }
+
+  .cancel-edit {
+    font-size: 20px;
+    color: #fff;
+
+    &:hover {
+      background-color: #FF4D4F;
+    }
+  }
+
+  .confirm-edit {
+    font-size: 20px;
+    color: #fff;
+
+    &:hover {
+      background-color: #1777FF;
+    }
+  }
 }
 </style>
diff --git a/src/pages/page-web/projects/wayline.vue b/src/pages/page-web/projects/wayline.vue
index fd9060f..c6e07ed 100644
--- a/src/pages/page-web/projects/wayline.vue
+++ b/src/pages/page-web/projects/wayline.vue
@@ -110,7 +110,12 @@
           </div>
           <div class="graph-right">
             <div v-for="(event, index) in item.eventList" :key="index" class="s-event-icon">
-              <img :src="event.icon" alt="icon" />
+              <a-tooltip placement="top">
+                <template #title>
+                  {{ event.name }}
+                </template>
+                <img :src="event.icon" alt="icon" />
+              </a-tooltip>
             </div>
           </div>
         </li>
@@ -136,7 +141,6 @@
 import { getRoot } from '/@/root'
 import * as Cesium from 'cesium'
 import { cesiumOperation } from '/@/hooks/use-cesium-tsa'
-import { gcj02towgs84, wgs84togcj02 } from '/@/vendors/coordtransform'
 import axios from 'axios'
 import JSZIP from 'jszip'
 // 初始化jszip
@@ -178,7 +182,7 @@
 const isPointListOpen = ref<boolean>(false)
 const tragetPointArr = ref<{
   position: Cesium.Cartesian3,
-  eventList: string[]
+  eventList: any[]
 }[]>([])
 const selectedPoint = ref<number | null>(null)
 
@@ -215,7 +219,8 @@
   },
   {
     key: 'gimbalRotate',
-    name: '旋转云台'
+    name: '旋转云台',
+    icon: getResource('waylinetool/holderyaw.png'),
   },
   {
     key: 'rotateYaw',
@@ -245,6 +250,7 @@
   }, 1000)
   console.log(workspaceId.value)
 })
+
 onUnmounted(() => {
   if (kmlDataSource) {
     global.$viewer.dataSources.remove(kmlDataSource)
@@ -400,7 +406,7 @@
   store.commit('SET_WAYLINE_INFO', {
     isShow: true,
     wayline: currentWayLine,
-    position: position
+    position: index
   })
   if (getEntityById('clickBox')) {
     removeById('clickBox')

--
Gitblit v1.9.3