forked from drone/command-center-dashboard

罗广辉
2025-04-07 49f1198d1f2da71f6213270dfe94be9b34ebf235
feat: 量尺
3 files renamed
8 files modified
6 files added
346 ■■■■■ changed files
src/assets/images/cpoint.png patch | view | raw | blame | history
src/assets/images/newcon_box.png patch | view | raw | blame | history
src/assets/images/rSide/dw.png patch | view | raw | blame | history
src/assets/images/rSide/dw1.png patch | view | raw | blame | history
src/assets/images/rSide/lc.png patch | view | raw | blame | history
src/assets/images/rSide/lc1.png patch | view | raw | blame | history
src/assets/images/rSide/tc.png patch | view | raw | blame | history
src/assets/images/rSide/tc1.png patch | view | raw | blame | history
src/components/MeasuringDistance.vue 254 ●●●●● patch | view | raw | blame | history
src/components/UserOperate.vue 2 ●●● patch | view | raw | blame | history
src/views/Home/Home.vue 2 ●●●●● patch | view | raw | blame | history
src/views/Home/RSide.vue 46 ●●●● patch | view | raw | blame | history
src/views/Home/useMapAggregation/useMapAggregation.js 6 ●●●● patch | view | raw | blame | history
src/views/SignMachineNest/MachineRight/MachineTableDetails/DeviceJob/DeviceJobDetails/DeviceJobDetails.vue 1 ●●●● patch | view | raw | blame | history
src/views/SignMachineNest/MachineRight/MachineTableDetails/DeviceJob/DeviceJobDetails/DeviceJobDetailsMap.vue 8 ●●●● patch | view | raw | blame | history
src/views/SignMachineNest/MachineRight/MachineTableDetails/MachineTableDetails.vue 25 ●●●● patch | view | raw | blame | history
src/views/TestTemplate.vue 2 ●●● patch | view | raw | blame | history
src/assets/images/cpoint.png
src/assets/images/newcon_box.png
src/assets/images/rSide/dw.png
src/assets/images/rSide/dw1.png

src/assets/images/rSide/lc.png

src/assets/images/rSide/lc1.png
src/assets/images/rSide/tc.png

src/assets/images/rSide/tc1.png
src/components/MeasuringDistance.vue
New file
@@ -0,0 +1,254 @@
<!-- 量尺模式 -->
<template>
  <div class="container"></div>
</template>
<script>
import cesiumOperation from '@/utils/cesium-tsa'
import cpoint from '@/assets/images/cpoint.png'
const {
  addLeftClickEvent,
  removeLeftClickEvent,
  removeById,
  addRightClickEvent,
  removeRightClickEvent,
  globalCesium
} = cesiumOperation()
export default {
  data () {
    return {
      positions: [],
      menuPopup: null,
      temporaryPointsDeleted: null,
      alldistance: 0,
    }
  },
  methods: {
    // Prevent default Event
    preventDefault (event) {
      event.preventDefault()
    },
    init () {
      addLeftClickEvent(null, this.handleMapClick)
      addRightClickEvent(null, this.RightClickEvent)
      let cesium = document.getElementById('cesium')
      cesium.addEventListener('contextmenu', this.preventDefault)
    },
    remove () {
      removeLeftClickEvent()
      let cesium = document.getElementById('cesium')
      cesium.removeEventListener('contextmenu', this.preventDefault)
      if (this.menuPopup) {
        window.$viewer.container.removeChild(this.menuPopup)
      }
      removeRightClickEvent()
      removeById('route_planning')
      removeById('alldistanceshow')
      this.positions.forEach((_position, index) => {
        removeById('created_point_' + index)
        removeById('distance_' + index)
      })
    },
    // 左键点击事件
    handleMapClick (click) {
      if (this.menuPopup) {
        window.$viewer.container.removeChild(this.menuPopup)
        this.menuPopup = null
      }
      const { position } = click
      const ellipsoid = window.$viewer.scene.globe.ellipsoid
      const c3Position = window.$viewer.scene.globe.pick(
        window.$viewer.camera.getPickRay(position),
        window.$viewer.scene,
      )
      const c2Postion = ellipsoid.cartesianToCartographic(c3Position)
      const longitude = globalCesium.Math.toDegrees(c2Postion.longitude)
      const latitude = globalCesium.Math.toDegrees(c2Postion.latitude)
      this.positions.push({ longitude, latitude })
      this.createWayline(this.positions)
    },
    // 创建点位
    createWayline (positions) {
      this.alldistance = 0
      const cartesian3Arr = []
      positions.forEach((position, index) => {
        removeById('created_point_' + index)
        removeById('distance_' + index)
        const cartesian3 = globalCesium.Cartesian3.fromDegrees(
          Number(position.longitude),
          Number(position.latitude),
          0,
        )
        cartesian3Arr.push(cartesian3)
        window.$viewer.entities.add({
          id: 'created_point_' + index,
          position: cartesian3,
          billboard: {
            image: cpoint,
            pixelOffset: new globalCesium.Cartesian2(0, -13),
            outlineWidth: 0,
            width: 30,
            height: 30,
            scale: 1.0,
          },
        })
        // 给最后一个点增加总长度展示
        if (this.positions.length == index + 1 && this.alldistance != 0) {
          removeById('alldistanceshow')
          window.$viewer.entities.add({
            position: cartesian3,
            id: 'alldistanceshow',
            label: {
              text: `总长度:${this.alldistance}m`,
              font: '12px monospace',
              showBackground: true,
              horizontalOrigin: globalCesium.HorizontalOrigin.CENTER,
              verticalOrigin: globalCesium.VerticalOrigin.BOTTOM,
              disableDepthTestDistance: Number.POSITIVE_INFINITY,
              pixelOffset: new globalCesium.Cartesian2(40, 20),
            },
          })
        }
        //给每个点增加右击事件
        addRightClickEvent('created_point_' + index, this.RightClickEvent)
        if (positions[index + 1]) {
          const next = positions[index + 1]
          const { longitude: nextLng, latitude: nextLat } = next
          const nextPoint = globalCesium.Cartesian3.fromDegrees(nextLng, nextLat, 0)
          // 获取中心点
          const centerPoint = globalCesium.Cartesian3.lerp(
            cartesian3,
            nextPoint,
            0.5,
            new globalCesium.Cartesian3(),
          )
          // 获取两个点之间的距离并展示
          let distance = globalCesium.Cartesian3.distance(cartesian3, nextPoint)
          distance = Math.round(distance)
          this.alldistance = this.alldistance + distance
          if (positions.length < 1 || distance === 0) return
          this.createDistanceLabel(centerPoint, distance, index)
        }
      })
      removeById('route_planning')
      window.$viewer.entities.add({
        id: 'route_planning',
        polyline: {
          width: 7,
          positions: cartesian3Arr,
          material: new globalCesium.PolylineOutlineMaterialProperty({
            color: globalCesium.Color.WHITE,
            outlineWidth: 2,
            outlineColor: globalCesium.Color.BLUE,
          }),
          clampToGround: false,
        },
      })
    },
    createDistanceLabel (position, dist, index) {
      return window.$viewer.entities.add({
        position: position,
        id: 'distance_' + index,
        label: {
          text: `${dist}m`,
          font: '12px monospace',
          showBackground: true,
          horizontalOrigin: globalCesium.HorizontalOrigin.CENTER,
          verticalOrigin: globalCesium.VerticalOrigin.BOTTOM,
          disableDepthTestDistance: Number.POSITIVE_INFINITY,
          pixelOffset: new globalCesium.Cartesian2(0, -0),
        },
      })
    },
    // 添加右键菜单
    RightClickEvent (click, pick, viewer) {
      if (this.menuPopup) {
        window.$viewer.container.removeChild(this.menuPopup)
        this.menuPopup = null
      }
      if (this.positions.length == 0) return
      const { position } = click
      const { x, y } = position
      this.menuPopup = this.createMenuPopup(pick)
      this.menuPopup.addEventListener('click', this.appendPoint)
      this.menuPopup.style.transform = `translate3d(${x}px, ${y}px, 0)`
      window.$viewer.container.appendChild(this.menuPopup)
      if (!pick) return
      this.temporaryPointsDeleted = pick.id._id
    },
    // 创建弹窗
    createMenuPopup (pick) {
      const menuPopup = document.createElement('div')
      menuPopup.className = 'create-wayline-menu'
      const arr = [
        { title: '删除点位', class: 'del-wayline' },
        { title: '清空', class: 'remove-all' },
      ]
      // 只有对点位进行右击时,才能单独进行点位删除
      if (!pick || ['route_planning', 'drone_dock'].includes(pick.id._id)) {
        arr.splice(0, 1)
      }
      arr.forEach((item) => {
        const title = document.createElement('div')
        title.innerText = item.title
        title.style.fontSize = '14px'
        title.style.padding = '5px'
        title.className = item.class
        menuPopup.appendChild(title)
      })
      return menuPopup
    },
    // 对航点增加点击事件
    appendPoint (e) {
      const className = e.target.className
      window.$viewer.container.removeChild(this.menuPopup)
      this.menuPopup = null
      removeById('route_planning')
      removeById('alldistanceshow')
      this.positions.forEach((_position, index) => {
        removeById('created_point_' + index)
        removeById('distance_' + index)
      })
      // 清空
      if (className === 'remove-all') {
        this.positions = []
      }
      // 单点删除
      else {
        this.positions.splice(this.temporaryPointsDeleted.split('_')[2], 1)
        this.createWayline(this.positions)
      }
    },
  },
  mounted () {
    this.init()
  },
    beforeUnmount () {
    this.remove()
  },
}
</script>
<style lang="scss">
.create-wayline-menu {
  width: 'fit-content';
  background: url('@/assets/images/newcon_box.png') no-repeat center / 100% 100%;
  position: absolute;
  top: 0;
  left: 0;
  color: #fff;
  font-size: 16px;
  div {
    padding: 5px 10px;
    cursor: pointer;
    font-weight: bold;
    &:hover {
      background-color: rgba(0, 0, 0, 0.3);
    }
  }
}
</style>
src/components/UserOperate.vue
@@ -45,7 +45,7 @@
.userOperate {
  position: absolute;
  right: 27px;
  top: -30px;
  top: -35px;
  width: 132px;
  height: 38px;
  display: flex;
src/views/Home/Home.vue
@@ -21,6 +21,7 @@
import RSide from '@/views/Home/RSide.vue';
import { onMounted } from 'vue';
import cesiumOperation from '@/utils/cesium-tsa';
import MeasuringDistance from '@/components/MeasuringDistance.vue';
const store = useStore();
const { _init,viewerDestory } = cesiumOperation();
@@ -28,6 +29,7 @@
onUnmounted(()=>{
  store.commit('setSingleUavHome',null);
    console.log('home销毁')
  viewerDestory()
})
src/views/Home/RSide.vue
@@ -1,6 +1,5 @@
<template>
  <div class="ai-chat">
    <el-popover
      placement="bottom"
      :visible="visible"
@@ -16,25 +15,43 @@
        <el-input/>
      </div>
    </el-popover>
    <img class="chat-bottom" src="../../assets/images/chat-bottom.png" alt="">
  </div>
  <div class="r-side">
    <img class="positioning" src="../../assets/images/self-positioning.png" alt="" @mouseenter="enterHover(0)" @mouseleave="logIndex=3">
    <img class="measuring-scale" src="../../assets/images/measuring-scale.png" alt="" @mouseenter="enterHover(1)" @mouseleave="logIndex=3">
    <img class="layer" src="../../assets/images/layer.png" alt="" @mouseenter="enterHover(2)" @mouseleave="logIndex=3">
        <img v-for="(item, index) in images"
                 :key="index" :class="item.class"
                 :src="activeIndex === index ? item.activeSrc : item.src" alt=""
                 @click="activeChange(index)"
                 @mouseenter="enterHover(index)"
                 @mouseleave="logIndex=3"
        >
  </div>
  <div v-if="logIndex===0" class="r-side-positioning">切换地图模式</div>
  <div v-if="logIndex===0" class="r-side-positioning">返回当前位置</div>
  <div v-if="logIndex===1" class="r-side-measuring">量尺</div>
  <div v-if="logIndex===2" class="r-side-layer">返回当前位置</div>
  <div v-if="logIndex===2" class="r-side-layer">切换地图模式</div>
  <MeasuringDistance v-if="activeIndex === 1"/>
</template>
<script setup>
import vDrag from '@/directive/drag'
import MeasuringDistance from '@/components/MeasuringDistance.vue'
import dw from '@/assets/images/rSide/dw.png'
import dw1 from '@/assets/images/rSide/dw1.png'
import lc from '@/assets/images/rSide/lc.png'
import lc1 from '@/assets/images/rSide/lc1.png'
import tc from '@/assets/images/rSide/tc.png'
import tc1 from '@/assets/images/rSide/tc1.png'
let logIndex = ref(3);
const enterHover = (value) => {
  logIndex.value = value;
    logIndex.value = value;
}
let activeIndex = ref(null);
const activeChange = (value) => {
    if (value === 1){
        activeIndex.value = activeIndex.value === 1 ? null : value;
    }
}
const visible = ref(false);
let pressStart = 0;
@@ -50,6 +67,13 @@
    visible.value = !visible.value;
  }
};
// 添加: 定义图片数组
const images = [
    { class: 'positioning', src: dw,activeSrc:dw1 },
    { class: 'measuring-scale', src: lc,activeSrc:lc1 },
    { class: 'layer', src: tc,activeSrc:tc1 }
];
</script>
<style scoped lang="scss">
@@ -63,8 +87,6 @@
    height: 68px;
    display: block;
  };
  .chat {
    position: fixed;
    bottom: 330px;
@@ -116,5 +138,5 @@
.r-side-layer {
  bottom: 122px;
}
</style>
src/views/Home/useMapAggregation/useMapAggregation.js
@@ -160,8 +160,6 @@
    const splashedList = type === 'device'
      ? await getDeviceList(areaCode)
      : eventList.map(i=>({eventId:i.id,latitude:Number(i.latitude),longitude:Number(i.longitude),type:'event'}))
    console.log(list, 'list');
    console.log(splashedList, 'splashedList');
    const hierarchy = convertToHierarchy(areaCode.slice(0, 6));
    const jsonPath = hierarchy.join('/');
    const jsonPathPre = hierarchy.slice(0, hierarchy.length - 1).join('/');
@@ -305,9 +303,10 @@
        id: feature.id,
        position: position,
        label: {
          text: feature.data.total_device_count.toString(),
          text: (feature.data.total_device_count || 0).toString(),
          font: '12pt Source Han Sans CN',
          fillColor: Cesium.Color.BLACK,
          outlineColor: Cesium.Color.BLACK,
          style: Cesium.LabelStyle.FILL_AND_OUTLINE,
          eyeOffset: new Cesium.Cartesian3(0, 0, -10), // 让label "浮" 在广告牌前面
        },
@@ -426,6 +425,7 @@
  // 移除所有监听事件,变量置空
  const removeAll = () => {
    if (!viewer) return
    removeEntities();
    removeLabel();
    // viewer.camera.moveEnd.removeEventListener(determineScaling);
src/views/SignMachineNest/MachineRight/MachineTableDetails/DeviceJob/DeviceJobDetails/DeviceJobDetails.vue
@@ -86,7 +86,6 @@
}
const getDetails = () => {
    getJobDetails({ wayLineJobInfoId: wayLineJodInfoId.value }).then(res => {
        console.log(res.data.data, 666)
        detailsData.value = res.data.data
        infoList.value.forEach(item => {
            item.value = detailsData.value?.[item.field] || ''
src/views/SignMachineNest/MachineRight/MachineTableDetails/DeviceJob/DeviceJobDetails/DeviceJobDetailsMap.vue
@@ -95,9 +95,9 @@
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)
        const waylinesXML = await res.fileInfoObj['wpmz/waylines.wpml']
        const waylinesXMLJSON = XMLToJSON(waylinesXML)?.['Document']
        const templateXMLObj = removeTextKey(waylinesXMLJSON.Folder)
        resolve(templateXMLObj)
    })
}
@@ -106,6 +106,7 @@
const drawLine = async () => {
    const res = await Promise.all(props.detailsData.way_lines.map(item => analysis(item.url)))
    res.map(item => renderingLine(item))
    console.log(res,'jiexi')
    const allPoint = res
        .flatMap(item => item.Placemark)
        .map(item => item.Point.coordinates.split(','))
@@ -113,7 +114,6 @@
}
const removeMap = () => {
    console.log('yichu')
    viewer.entities.removeAll()
    viewer.destroy()
}
src/views/SignMachineNest/MachineRight/MachineTableDetails/MachineTableDetails.vue
@@ -24,18 +24,29 @@
import DeviceEvent from '@/views/SignMachineNest/MachineRight/MachineTableDetails/DeviceEvent.vue'
import DeviceJob from '@/views/SignMachineNest/MachineRight/MachineTableDetails/DeviceJob/DeviceJob.vue'
import { pxToRem } from '@/utils/rem';
import { useStore } from 'vuex'
const isShowDetails = defineModel('show')
const infoList = ref([
    { name: '机巢名称', value: 'xxx' },
    { name: '机巢所属地区', value: 'xxx' },
    { name: '机巢状态', value: 'xxx' },
    { name: '任务成功', value: 'xxx' },
    { name: '机巢位置', value: 'xxx' },
    { name: '机巢名称', value: '',field:'nickname' },
    { name: '机巢所属地区', value: '',field:'device_area' },
    { name: '机巢状态', value: '',field:'status' },
    { name: '任务成功', value: '',field:'result_num' },
    { name: '机巢位置', value: '',field:'address' },
])
onMounted(() => {})
const store = useStore();
// state必须带上.home
const singleTotal = computed(() => store.state.home.singleTotal);
watch(singleTotal, (val) => {
    if (val?.device_info){
        infoList.value.forEach(item => {
            item.value = val.device_info?.[item.field] || ''
        })
    }
})
onMounted(() => {
})
</script>
<style lang="scss">
src/views/TestTemplate.vue
@@ -4,11 +4,11 @@
import { useStore } from 'vuex';
const store = useStore();
// state必须带上.home
const singleUavHome = computed(() => store.state.home.singleUavHome);
// getters不需要带.home
const test = computed(() => store.getters.test);
onMounted(() => {
  // commit dispatch 不需要带.home
  store.commit('xxxx', '同步步修改啦');