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