GuLiMmo
2023-12-04 495e5fd61b0093f322b7b26bd25ff9bbbaa9513d
航线点位事件完善
3 files modified
499 ■■■■ changed files
package.json 1 ●●●● patch | view | raw | blame | history
src/components/waylinetool/index.vue 496 ●●●● patch | view | raw | blame | history
src/pages/page-web/projects/wayline.vue 2 ●●●●● patch | view | raw | blame | history
package.json
@@ -22,6 +22,7 @@
    "eventemitter3": "^5.0.0",
    "file-saver": "^2.0.5",
    "jszip": "^3.10.1",
    "lodash": "^4.17.21",
    "mitt": "^3.0.0",
    "moment": "^2.29.4",
    "mqtt": "4.0.1",
src/components/waylinetool/index.vue
@@ -1,11 +1,35 @@
<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(item)" :style="{ backgroundColor: item.selected ? '#2D8CF0' : '#323131' }">
        <img :src="item.icon" alt="icon">
      </div>
      <a-popover placement="leftTop" trigger="click" arrow-point-at-center class="param-popover">
        <template #title>
          <span>{{ item.title }}</span>
        </template>
        <template #content>
          <div class="popover-content">
            <div v-for="sParam in item.param" :key="sParam.key" class="content-box">
              <div>{{ sParam.title }}</div>
              <a-input v-model:value="sParam.value" placeholder="请输入参数" v-if="!sParam.isRadio" />
              <a-radio-group :options="sParam.options" v-model:value="sParam.value" v-else />
              <div class="annotation" v-if="sParam.annotation">
                注:{{ sParam.annotation }}
              </div>
            </div>
            <div class="btn-tool">
              <a-button @click="resetParamSetting(item)">重置</a-button>
              <a-button type="primary" @click="item.event(item)">确定</a-button>
            </div>
          </div>
        </template>
        <div class="btn" @click="() => saveInitPopoverParam(item.param)"
          :style="{ backgroundColor: item.selected ? '#2D8CF0' : '#323131' }">
          <img :src="item.icon" alt="icon">
        </div>
      </a-popover>
    </li>
    <!-- 确认和取消按钮 -->
    <li class="s-btn">
      <div class="title">取消编辑</div>
      <div class="btn cancel-edit" @click="cencalEdit">
@@ -27,11 +51,11 @@
import JSZIP from 'jszip'
import { saveAs } from 'file-saver'
import { message } from 'ant-design-vue'
import { cloneDeep } from 'lodash'
import {
  CloseOutlined,
  CheckOutlined
} from '@ant-design/icons-vue'
import { Item } from 'ant-design-vue/lib/menu'
// 初始化jszip
const JsZip = new JSZIP()
@@ -41,11 +65,24 @@
  return new URL(`/src/assets/icons/${name}`, import.meta.url).href
}
// 点位按钮list
interface popoverParamType {
  title: string,
  key: string,
  isRadio?: boolean,
  annotation?: string,
  options?: {
    label: string,
    value: string
  }[],
  value: string
}
// 点击按钮list
interface sBtn {
  key: string,
  icon: string,
  title: string,
  param: popoverParamType[],
  event: Function,
  selected?: boolean
}
@@ -57,92 +94,228 @@
const curKmzName = ref<string>('')
// 当前kmzpath
const curKmzPath = ref<string>('')
// popover初始参数
const initPopoverParam = ref<popoverParamType[]>([])
const btnList = ref<sBtn[]>([
  // 开始录像
  {
    key: 'startRecord',
    icon: getResource('waylinetool/camera-on.png'),
    title: '开始录像',
    param: [
      {
        title: '负载挂载位置',
        key: 'payloadPositionIndex',
        isRadio: true,
        options: [
          { label: '飞行器1号挂载位置', value: '0' },
          { label: '飞行器2号挂载位置', value: '1' },
          { label: '飞行器3号挂载位置', value: '2' },
        ],
        annotation: '飞行器1号挂载位置。M300 RTK,M350 RTK机型,对应机身左前方。其它机型,对应主云台。飞行器2号挂载位置。M300 RTK,M350 RTK机型,对应机身右前方。飞行器3号挂载位置。M300 RTK,M350 RTK机型,对应机身上方。',
        value: ''
      },
      {
        title: '拍摄照片文件后缀',
        key: 'fileSuffix',
        annotation: '为生成媒体文件命名时将额外附带该后缀',
        value: ''
      },
      {
        title: '拍摄照片存储类型',
        key: 'payloadLensIndex',
        isRadio: true,
        options: [
          { label: '存储变焦镜头拍摄照片', value: 'zoom' },
          { label: '存储广角镜头拍摄照片', value: 'wide' },
          { label: '存储红外镜头拍摄照片', value: 'ir' },
          { label: '存储窄带镜头拍摄照片', value: 'narrow_band' },
          { label: '全部使用', value: 'zoom, wide, ir, narrow_band' }
        ],
        value: ''
      },
      {
        title: '是否使用全局存储类型',
        key: 'useGlobalPayloadLensIndex',
        isRadio: true,
        options: [
          { label: '不使用全局设置', value: '0' },
          { label: '使用全局设置', value: '1' }
        ],
        value: ''
      }
    ],
    event: (obj: sBtn) => {
      obj.selected = !obj.selected
      editKmlFile('startRecord', {
        payloadPositionIndex: 0,
        fileSuffix: `_${new Date().toLocaleString()}`,
        payloadLensIndex: 'zoom',
        useGlobalPayloadLensIndex: 0
      })
      paramPopover('startRecord', obj)
    }
  },
  // 停止录像
  {
    key: 'stopRecord',
    icon: getResource('waylinetool/camera-off.png'),
    title: '停止录像',
    param: [
      {
        title: '负载挂载位置',
        key: 'payloadPositionIndex',
        isRadio: true,
        options: [
          { label: '飞行器1号挂载位置', value: '0' },
          { label: '飞行器2号挂载位置', value: '1' },
          { label: '飞行器3号挂载位置', value: '2' },
        ],
        annotation: '飞行器1号挂载位置。M300 RTK,M350 RTK机型,对应机身左前方。其它机型,对应主云台。飞行器2号挂载位置。M300 RTK,M350 RTK机型,对应机身右前方。飞行器3号挂载位置。M300 RTK,M350 RTK机型,对应机身上方。',
        value: ''
      },
      {
        title: '拍摄照片存储类型',
        key: 'payloadLensIndex',
        isRadio: true,
        options: [
          { label: '存储变焦镜头拍摄照片', value: 'zoom' },
          { label: '存储广角镜头拍摄照片', value: 'wide' },
          { label: '存储红外镜头拍摄照片', value: 'ir' },
          { label: '存储窄带镜头拍摄照片', value: 'narrow_band' },
          { label: '全部使用', value: 'zoom, wide, ir, narrow_band' }
        ],
        value: ''
      }
    ],
    event: (obj: sBtn) => {
      obj.selected = !obj.selected
      editKmlFile('stopRecord', {
        payloadPositionIndex: 0,
        payloadLensIndex: 'zoom'
      })
      paramPopover('stopRecord', obj)
    }
  },
  // 开始等时间隔拍照
  {
    key: 'time',
    icon: getResource('waylinetool/shoot1.png'),
    title: '开始等时间隔拍照',
    param: [],
    event: (obj: sBtn) => {
      obj.selected = !obj.selected
      obj.selected = true
      editKmlFile('time')
    }
  },
  // 开始等距间隔拍照
  {
    key: 'distance',
    icon: getResource('waylinetool/shoot2.png'),
    title: '开始等距间隔拍照',
    param: [],
    event: (obj: sBtn) => {
      obj.selected = !obj.selected
      obj.selected = true
      editKmlFile('distance')
    }
  },
  // 结束间隔拍照
  {
    key: 'deleteShootType',
    icon: getResource('waylinetool/shoot3.png'),
    title: '结束间隔拍照',
    param: [],
    event: (obj: sBtn) => {
      obj.selected = !obj.selected
      obj.selected = true
      editKmlFile('deleteShootType')
    }
  },
  // 悬停
  {
    key: 'hover',
    icon: getResource('waylinetool/xt.png'),
    title: '悬停',
    param: [
      {
        title: '飞行器悬停等待时间',
        key: 'hoverTime',
        annotation: '参数需大于0, 单位为秒',
        value: ''
      }
    ],
    event: (obj: sBtn) => {
      obj.selected = !obj.selected
      editKmlFile('hover', {
        hoverTime: 10
      })
      paramPopover('hover', obj)
    }
  },
  // 飞行器偏航角
  {
    key: 'rotateYaw',
    icon: getResource('waylinetool/droneyaw.png'),
    title: '飞行器偏航角',
    param: [
      {
        title: '飞行器目标偏航角(相对于地理北)',
        key: 'aircraftHeading',
        annotation: '范围为-180°到180°,飞行器旋转至该目标偏航角。0°为正北方向,90°为正东方向,-90°为正西方向,-180°/180°为正南方向。',
        value: ''
      },
      {
        title: '飞行器偏航角转动模式',
        key: 'aircraftPathMode',
        isRadio: true,
        options: [
          { label: '顺时针旋转', value: 'clockwise' },
          { label: '逆时针旋转', value: 'counterClockwise' }
        ],
        value: ''
      }
    ],
    event: (obj: sBtn) => {
      obj.selected = !obj.selected
      editKmlFile('rotateYaw', {
        aircraftHeading: 0,
        aircraftPathMode: 'clockwise'
      })
      paramPopover('rotateYaw', obj)
    }
  },
  // 云台偏航角
  {
    key: 'gimbalRotate',
    icon: getResource('waylinetool/holderyaw.png'),
    title: '云台偏航角',
    param: [
      {
        title: '负载挂载位置',
        key: 'payloadPositionIndex',
        isRadio: true,
        options: [
          { label: '飞行器1号挂载位置', value: '0' },
          { label: '飞行器2号挂载位置', value: '1' },
          { label: '飞行器3号挂载位置', value: '2' },
        ],
        annotation: '飞行器1号挂载位置。M300 RTK,M350 RTK机型,对应机身左前方。其它机型,对应主云台。飞行器2号挂载位置。M300 RTK,M350 RTK机型,对应机身右前方。飞行器3号挂载位置。M300 RTK,M350 RTK机型,对应机身上方。',
        value: ''
      },
      {
        title: '云台转动模式',
        key: 'gimbalRotateMode',
        isRadio: true,
        options: [
          { label: '绝对角度,相对于正北方的角度', value: 'relativeAngle' },
          { label: '相对角度,相对于飞行器机头的角度', value: 'absoluteAngle' }
        ],
        value: ''
      },
      {
        title: '是否使能云台Yaw转动',
        key: 'gimbalYawRotateEnable',
        isRadio: true,
        options: [
          { label: '不使能', value: '0' },
          { label: '使能', value: '1' }
        ],
        value: ''
      },
      {
        title: '云台Yaw转动角度',
        key: 'gimbalYawRotateAngle',
        annotation: '不同云台可转动范围不同',
        value: ''
      },
      {
        title: '云台完成转动用时',
        key: 'gimbalRotateTime',
        annotation: '单位为秒',
        value: ''
      }
    ],
    event: (obj: sBtn) => {
      obj.selected = !obj.selected
      editKmlFile('gimbalRotate', {
        payloadPositionIndex: 0,
      paramPopover('gimbalRotate', obj, {
        gimbalHeadingYawBase: 'north',
        gimbalRotateMode: 'relativeAngle',
        // 上下摆动 pitch xyz中心点为基准
@@ -152,70 +325,179 @@
        gimbalRollRotateEnable: 0,
        gimbalRollRotateAngle: 0,
        // 左右摆动 yaw xyz中心点为基准
        gimbalYawRotateEnable: 1,
        gimbalYawRotateAngle: 30.1,
        gimbalRotateTimeEnable: 0,
        gimbalRotateTime: 3
      })
    }
  },
  // 云台俯仰角
  {
    key: 'gimbalRotate',
    icon: getResource('waylinetool/holdertilt.png'),
    title: '云台俯仰角',
    param: [
      {
        title: '负载挂载位置',
        key: 'payloadPositionIndex',
        isRadio: true,
        options: [
          { label: '飞行器1号挂载位置', value: '0' },
          { label: '飞行器2号挂载位置', value: '1' },
          { label: '飞行器3号挂载位置', value: '2' },
        ],
        annotation: '飞行器1号挂载位置。M300 RTK,M350 RTK机型,对应机身左前方。其它机型,对应主云台。飞行器2号挂载位置。M300 RTK,M350 RTK机型,对应机身右前方。飞行器3号挂载位置。M300 RTK,M350 RTK机型,对应机身上方。',
        value: ''
      },
      {
        title: '云台转动模式',
        key: 'gimbalRotateMode',
        isRadio: true,
        options: [
          { label: '绝对角度,相对于正北方的角度', value: 'relativeAngle' },
          { label: '相对角度,相对于飞行器机头的角度', value: 'absoluteAngle' }
        ],
        value: ''
      },
      {
        title: '是否使能云台Pitch转动',
        key: 'gimbalPitchRotateEnable',
        isRadio: true,
        options: [
          { label: '不使能', value: '0' },
          { label: '使能', value: '1' }
        ],
        value: ''
      },
      {
        title: '云台Pitch转动角度',
        key: 'gimbalPitchRotateAngle',
        annotation: '不同云台可转动范围不同',
        value: ''
      },
      {
        title: '云台完成转动用时',
        key: 'gimbalRotateTime',
        value: ''
      }
    ],
    event: (obj: sBtn) => {
      obj.selected = !obj.selected
      editKmlFile('gimbalRotate', {
        payloadPositionIndex: 0,
      paramPopover('gimbalRotate', obj, {
        gimbalHeadingYawBase: 'north',
        gimbalRotateMode: 'relativeAngle',
        gimbalPitchRotateEnable: 1,
        gimbalPitchRotateAngle: 31.1,
        gimbalRollRotateEnable: 0,
        gimbalRollRotateAngle: 0,
        gimbalYawRotateEnable: 0,
        gimbalYawRotateAngle: 0,
        gimbalRotateTimeEnable: 0,
        gimbalRotateTime: 3
      })
    }
  },
  // 拍照
  {
    key: 'takePhoto',
    icon: getResource('waylinetool/camera.png'),
    title: '拍照',
    param: [
      {
        title: '负载挂载位置',
        key: 'payloadPositionIndex',
        isRadio: true,
        options: [
          { label: '飞行器1号挂载位置', value: '0' },
          { label: '飞行器2号挂载位置', value: '1' },
          { label: '飞行器3号挂载位置', value: '2' },
        ],
        annotation: '飞行器1号挂载位置。M300 RTK,M350 RTK机型,对应机身左前方。其它机型,对应主云台。飞行器2号挂载位置。M300 RTK,M350 RTK机型,对应机身右前方。飞行器3号挂载位置。M300 RTK,M350 RTK机型,对应机身上方。',
        value: ''
      },
      {
        title: '拍摄照片文件后缀',
        key: 'fileSuffix',
        annotation: '为生成媒体文件命名时将额外附带该后缀。',
        value: ''
      },
      {
        title: '拍摄照片存储类型',
        key: 'payloadLensIndex',
        isRadio: true,
        options: [
          { label: '存储变焦镜头拍摄照片', value: 'zoom' },
          { label: '存储广角镜头拍摄照片', value: 'wide' },
          { label: '存储红外镜头拍摄照片', value: 'ir' },
          { label: '存储窄带镜头拍摄照片', value: 'narrow_band' },
          { label: '全部使用', value: 'zoom, wide, ir, narrow_band' }
        ],
        value: ''
      },
      {
        title: '是否使用全局存储类型',
        key: 'useGlobalPayloadLensIndex',
        isRadio: true,
        options: [
          { label: '不使用全局设置', value: '0' },
          { label: '使用全局设置', value: '1' }
        ],
        value: ''
      }
    ],
    event: (obj: sBtn) => {
      obj.selected = !obj.selected
      editKmlFile('takePhoto', {
        payloadPositionIndex: 0,
        fileSuffix: `_${new Date().toLocaleString()}`,
        payloadLensIndex: 'zoom',
        useGlobalPayloadLensIndex: 0
      })
      paramPopover('takePhoto', obj)
    }
  },
  // 相机变焦
  {
    key: 'zoom',
    icon: getResource('waylinetool/fd.png'),
    title: '相机变焦',
    param: [
      {
        title: '负载挂载位置',
        key: 'payloadPositionIndex',
        isRadio: true,
        options: [
          { label: '飞行器1号挂载位置', value: '0' },
          { label: '飞行器2号挂载位置', value: '1' },
          { label: '飞行器3号挂载位置', value: '2' },
        ],
        annotation: '飞行器1号挂载位置。M300 RTK,M350 RTK机型,对应机身左前方。其它机型,对应主云台。飞行器2号挂载位置。M300 RTK,M350 RTK机型,对应机身右前方。飞行器3号挂载位置。M300 RTK,M350 RTK机型,对应机身上方。',
        value: ''
      },
      {
        title: '变焦焦距',
        key: 'focalLength',
        annotation: '单位mm,值>0',
        value: ''
      }
    ],
    event: (obj: sBtn) => {
      obj.selected = !obj.selected
      editKmlFile('zoom', {
        payloadPositionIndex: 0,
        focalLength: 2
      })
      paramPopover('zoom', obj)
    }
  },
  // 创建文件夹
  {
    key: 'customDirName',
    icon: getResource('waylinetool/create-file.png'),
    title: '创建文件夹',
    param: [
      {
        title: '负载挂载位置',
        key: 'payloadPositionIndex',
        isRadio: true,
        options: [
          { label: '飞行器1号挂载位置', value: '0' },
          { label: '飞行器2号挂载位置', value: '1' },
          { label: '飞行器3号挂载位置', value: '2' },
        ],
        annotation: '飞行器1号挂载位置。M300 RTK,M350 RTK机型,对应机身左前方。其它机型,对应主云台。飞行器2号挂载位置。M300 RTK,M350 RTK机型,对应机身右前方。飞行器3号挂载位置。M300 RTK,M350 RTK机型,对应机身上方。',
        value: ''
      },
      {
        title: '新文件夹的名称',
        key: 'directoryName',
        value: ''
      }
    ],
    event: (obj: sBtn) => {
      obj.selected = !obj.selected
      editKmlFile('customDirName', {
        payloadPositionIndex: 0,
        directoryName: '新建文件夹'
      })
      paramPopover('customDirName', obj)
    }
  }
])
@@ -230,15 +512,8 @@
 * @return {*} void
 */
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'
  })
  const [, address] = kmzPath.split('cloud-bucket')
  return axios.get(import.meta.env.VITE_MEDIAPANEL_API_URL + address, { responseType: 'arraybuffer' })
    .then(fileRes => fileRes.data)
    .then(kmzData => JsZip.loadAsync(kmzData)) // 解压kmz文件
    .then(kmzZip => kmzZip)
@@ -291,6 +566,28 @@
        }
      })
    })
    settingDefaultValue(content)
  })
}
// popover设置默认值
const settingDefaultValue = (content: string) => {
  const regx = /<Placemark>([\s\S]*?)<\/Placemark>/g
  const points = content.match(regx) || []
  // 当前选中的点
  const currentPoint = points[currentPosition.value]
  const actionRegx = /<wpml:action>([\s\S]*?)<\/wpml:action>/g
  const actionsKML = currentPoint.match(actionRegx)
  btnList.value.forEach((sBtn: sBtn) => {
    const kml = `<wpml:actionActuatorFunc>${sBtn.key}</wpml:actionActuatorFunc>`
    const action = actionsKML?.find((action: string) => action.includes(kml))
    sBtn.param.forEach((sParam) => {
      const paramRegex = new RegExp(`<wpml:${sParam.key}>([\\s\\S]*?)<\\/wpml:${sParam.key}>`, 'g')
      const actionParam = action?.match(paramRegex) || ['']
      const infoRegx = new RegExp(`<wpml:${sParam.key}>([\\s\\S]*?)<\\/wpml:${sParam.key}>`)
      const value: string[] = actionParam[0]?.match(infoRegx) || ['']
      sParam.value = value[1] || ''
    })
  })
}
@@ -318,6 +615,38 @@
    </wpml:action>
  `
  return template
}
/**
 * @description: Popover参数编辑
 * @param {*} btnobj: 事件按钮所有参数
 * @param {*} eventType 事件类型
 * @param {*} eventParam 事件参数Param
 * @return {*}
 */
const paramPopover = (eventType: string, btnobj: sBtn, eventParam?: any) => {
  // 判断参数是否为有存在空的
  let isParamNull = false
  btnobj.param.some((sParam: any) => {
    isParamNull = sParam.value === ''
    return isParamNull
  })
  if (isParamNull) return message.warning('确认失败,请检查填写的参数')
  // 参数处理
  const param = { ...eventParam }
  btnobj.param.forEach((sParam: { key: string, value: string | number }) => {
    param[sParam.key] = sParam.value
  })
  editKmlFile(eventType, param)
  btnobj.selected = true
}
const saveInitPopoverParam = (param: popoverParamType[]) => {
  initPopoverParam.value = cloneDeep(param)
}
const resetParamSetting = (btnParam: sBtn) => {
  btnParam.param = cloneDeep(initPopoverParam.value)
}
/**
@@ -432,6 +761,9 @@
  })
}
onMounted(() => {
  kmzFileZip.value = null
})
</script>
<style lang="scss" scoped>
.btn-list {
@@ -491,4 +823,34 @@
    }
  }
}
.popover-content {
  .content-box {
    margin-bottom: 10px;
    .annotation {
      font-size: 12px;
      color: rgb(188, 187, 187);
      max-width: 300px;
    }
  }
  .btn-tool {
    display: flex;
    justify-content: center;
    gap: 10px;
    .ant-btn {
      flex: 1;
    }
  }
}
:deep() {
  .ant-radio-wrapper {
    display: block;
    max-width: 300px;
    text-wrap: wrap;
  }
}
</style>
src/pages/page-web/projects/wayline.vue
@@ -350,6 +350,8 @@
 * @param file
 */
function initKmlFile (file: string) {
  const [, address] = file.split('cloud-bucket')
  file = import.meta.env.VITE_MEDIAPANEL_API_URL + address
  removeById('kmzLine')
  const options = {
    camera: global.$viewer.scene.camera,