| | |
| | | <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() |
| | | |
| | |
| | | |
| | | // 点位按钮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> |
| | |
| | | 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; |
| | |
| | | text-shadow: 2px 2px 2px #000; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .btn { |
| | | width: 34px; |
| | | height: 34px; |
| | |
| | | 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> |