无人机管理后台前端(已迁走)
张含笑
2025-06-10 57412c916fca9c2c66446a3651c2ff7f6b2efa19
feat:地图弹框
1 files modified
6 files added
386 ■■■■■ changed files
src/api/dataCenter/dataCenter.js 8 ●●●●● patch | view | raw | blame | history
src/assets/images/dataCenter/datamap/activeevent.png patch | view | raw | blame | history
src/assets/images/dataCenter/datamap/eventCompleted.png patch | view | raw | blame | history
src/assets/images/dataCenter/datamap/popUpBox.png patch | view | raw | blame | history
src/hooks/components/EventPopUpBox.vue 142 ●●●●● patch | view | raw | blame | history
src/views/dataCenter/components/dataCenterMap.vue 210 ●●●●● patch | view | raw | blame | history
src/views/dataCenter/dataCenter.vue 26 ●●●●● patch | view | raw | blame | history
src/api/dataCenter/dataCenter.js
New file
@@ -0,0 +1,8 @@
import request from '@/axios';
export const getaiImagesPage = (data) => {
    return request({
        url: `/blade-resource/attach/aiImagesPage`,
        method: 'post',
        data
    })
  };
src/assets/images/dataCenter/datamap/activeevent.png
src/assets/images/dataCenter/datamap/eventCompleted.png
src/assets/images/dataCenter/datamap/popUpBox.png
src/hooks/components/EventPopUpBox.vue
New file
@@ -0,0 +1,142 @@
<template>
    <div class="mapPopUpBox">
        <div class="title">
            <span>222</span>
            <el-icon class="header-close" @click.stop="props.removeLabel">
                <Close />
            </el-icon>
        </div>
        <div class="content">
            <div class="medium">
                <el-image
                    class="eventImage"
                    :src="infoList?.smallUrl"
                    :preview-src-list="[infoList?.smallUrl]"
                    fit="cover"
                    preview-teleported
                ></el-image>
            </div>
            <div class="details">
                <div class="label">时间:</div>
                <div class="value point">
                    11
                </div>
                <div class="label">地点:</div>
                <div class="value">
                    22
                </div>
            </div>
        </div>
    </div>
</template>
<script setup>
import { ElImage, ElIcon } from 'element-plus'
import { Close } from '@element-plus/icons-vue'
// import { getEventDetails } from '@/api/home/aggregation'
import _ from 'lodash'
import { getSmallImg } from '@/utils/util'
const props = defineProps(['data', 'removeLabel'])
const loading = ref(true)
const info = ref({
    event_name: '',
    status: 1,
    url: '1',
    longitude: '',
    latitude: '',
    create_time: '04/01 12:41',
})
const infoList = props.data
onMounted(async () => {})
const disposeUrl = ({ url }) => {
    return getSmallImg(url)
}
</script>
<style scoped lang="scss">
.mapPopUpBox {
    width: 271px;
    height: 173px;
    background: url('@/assets/images/dataCenter/datamap/popUpBox.png') no-repeat center / 100% 100%;
    padding: 13px 15px;
    pointer-events: all;
    .title {
        font-family: YouSheBiaoTiHei, YouSheBiaoTiHei, serif;
        font-weight: 400;
        font-size: 18px;
        line-height: 16px;
        background: linear-gradient(180deg, #a8e5fb 0%, #e6f8ff 100%);
        -webkit-background-clip: text;
        -webkit-text-fill-color: transparent;
        -moz-background-clip: text;
        -moz-text-fill-color: transparent;
        background-clip: text;
        text-fill-color: transparent;
        margin-bottom: 10px;
        display: flex;
        align-items: center;
        justify-content: space-between;
        .header-close {
            width: 20px;
            height: 100%;
            line-height: 100%;
            display: flex;
            align-items: center;
            color: #9cd5ff;
            pointer-events: all;
            cursor: pointer;
        }
    }
    .content {
        height: 102px;
        width: 240px;
        display: flex;
        align-items: center;
        .medium {
            height: 102px;
            width: 120px;
            margin-right: 10px;
            > img,
            video,
            div {
                width: 100%;
                height: 100%;
            }
        }
        .details {
            .label {
                font-family: Source Han Sans CN, Source Han Sans CN;
                font-weight: 400;
                font-size: 14px;
                color: #7babd7;
                line-height: 16px;
                margin-bottom: 2px;
            }
            .value {
                font-family: Source Han Sans CN, Source Han Sans CN;
                font-weight: 400;
                font-size: 14px;
                color: #d8edff;
                line-height: 16px;
                margin-bottom: 2px;
            }
            .point {
                margin-bottom: 14px;
            }
        }
    }
}
</style>
src/views/dataCenter/components/dataCenterMap.vue
New file
@@ -0,0 +1,210 @@
<template>
  <el-dialog modal-class="mapDialog" v-model="isShow"  width="80%">
    <div class="mapBox">
      <div id="dataCenterMap" class="ztzf-cesium"></div>
    </div>
  </el-dialog>
</template>
<script setup>
import { useStore } from 'vuex';
import { PublicCesium } from '@/utils/cesium/publicCesium';
import { Cartesian3 } from 'cesium';
import * as Cesium from 'cesium';
import EventPopUpBox from '@/hooks/components/EventPopUpBox.vue';
import { render } from 'vue';
import defaultIcon from '@/assets/images/dataCenter/datamap/eventCompleted.png';
const isShow = defineModel('show');
const viewerRef = shallowRef(null);
let viewer = null;
const viewInstance = shallowRef(null);
const store = useStore();
const currentAreaPosition = ref({ height: 1987280, latitude: 27.636112, longitude: 115.732975 });
let handler = null;
// 获取弹框box
const createLabelDom = data => {
console.log('data',data);
    const vNode = h(EventPopUpBox, { data, removeLabel })
        const tooltipContainer = document.createElement('div')
        tooltipContainer.id = 'mapPopUpBox'
        tooltipContainer.style.position = 'absolute'
        tooltipContainer.style.transform = 'translate(-50%,-135%)'
        tooltipContainer.style.pointerEvents = 'none'
        document.querySelector('#dataCenterMap').append(tooltipContainer)
        render(vNode, tooltipContainer)
        return tooltipContainer
};
let currentClickEntity = null;
// 弹框位置刷新
const labelBoxUpdate = () => {
  if (!currentClickEntity) return;
  const mapPopUpBox = document.querySelector('#mapPopUpBox');
  let dom = mapPopUpBox
    ? mapPopUpBox
    : createLabelDom(currentClickEntity.properties.customData._value.data);
  const screenPosition = viewer?.scene.cartesianToCanvasCoordinates(
    currentClickEntity?.position?._value
  );
  if (screenPosition) {
    dom.style.left = `${screenPosition.x}px`;
    dom.style.top = `${screenPosition.y}px`;
    dom.style.display = 'block';
  }
};
const removeDom = () => {
  const dom = document.querySelector('#mapPopUpBox');
  if (dom && dom.parentNode) {
    dom.parentNode.removeChild(dom);
  }
};
// 移除弹框标签
const removeLabel = () => {
  viewer?.scene.postRender.removeEventListener(labelBoxUpdate);
  removeDom();
};
// 左键单机事件
const singleMachineEvent = async click => {
  let clickedEntities = viewer?.scene.drillPick(click.position).map(item => item.id);
  if (!clickedEntities.length) return;
  currentClickEntity = clickedEntities[0];
  removeLabel();
  viewer.scene.postRender.addEventListener(labelBoxUpdate);
};
// 事件初始化
const handlerInit = () => {
  if (handler) return;
  handler = new Cesium.ScreenSpaceEventHandler(viewer?.scene.canvas);
  handler.setInputAction(singleMachineEvent, Cesium.ScreenSpaceEventType.LEFT_CLICK);
};
const removeHandler = () => {
  handler?.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
  handler?.destroy();
  handler = null;
};
const dataPointList = [
  {
    longitude: '115.85708286111111',
    latitude: '28.62837602777778',
  },
  {
    longitude: '115.8566981111111',
    latitude: '28.624613027777777',
  },
  {
    longitude: '115.85474438888889',
    latitude: '28.624930444444445',
  },
  {
    longitude: '115.85518375',
    latitude: '28.62479547222222',
  },
];
function renderDataPoint() {
  dataPointList.forEach((item, index) => {
    const sizeObj = { width: 30, height: 30 };
    viewer.entities.add({
      id: `renderDataPoint-${index}`,
      position: Cesium.Cartesian3.fromDegrees(Number(item.longitude), Number(item.latitude)),
      label: {
        font: '12pt Source Han Sans CN',
        fillColor: Cesium.Color.WHITE,
        outlineColor: Cesium.Color.BLACK,
        outlineWidth: 2,
        style: Cesium.LabelStyle.FILL_AND_OUTLINE,
        verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
        pixelOffset: new Cesium.Cartesian2(0, -9),
      },
      billboard: {
        image: defaultIcon,
        ...sizeObj,
      },
      properties: {
        customData: {
          data: item,
        },
      },
    });
  });
}
const initMap = () => {
  const publicCesiumInstance = new PublicCesium({
    dom: 'dataCenterMap',
    flatMode: false,
    terrain: false,
    mapFilter: true,
  });
  viewerRef.value = publicCesiumInstance.getViewer();
  viewer = publicCesiumInstance.getViewer();
  const { longitude, latitude, height } = currentAreaPosition.value;
  const position = Cartesian3.fromDegrees(longitude, latitude, height);
  viewerRef.value.camera.flyTo({
    duration: 0,
    destination: position,
    orientation: {
      heading: Cesium.Math.toRadians(0.0),
      pitch: Cesium.Math.toRadians(-90.0),
      roll: 0.0,
    },
  });
  viewInstance.value = publicCesiumInstance;
};
// 监听对话框状态
watch(isShow, (newVal) => {
  if (newVal) {
    nextTick(() => {
      initMap();
      handlerInit();
      renderDataPoint();
    });
  } else {
    // 对话框关闭时清理资源
    viewInstance.value?.viewerDestroy();
    removeHandler();
  }
});
onMounted(() => {
  nextTick(() => {
    initMap();
    handlerInit();
    renderDataPoint();
  });
});
onBeforeUnmount(() => {
  viewInstance.value?.viewerDestroy();
  removeHandler();
});
</script>
<style >
.mapDialog .el-dialog__body {
  height: 700px !important;
  padding: 0 !important;
}
</style>
<style scoped lang="scss">
.mapBox {
  z-index: 2;
  height: 650px;
  width: 98%;
  position: absolute;
  left: 13px;
  top: 50px;
  #dataCenterMap {
    width: 100%;
    height: 100%;
  }
}
</style>
src/views/dataCenter/dataCenter.vue
@@ -49,7 +49,7 @@
            <span class="delete" @click="deleteDetail()" v-if="scope.row.fileformat !== 'AI识别'"
              >删除</span
            >
            <span class="location" v-if="scope.row.fileformat !== '视频'">定位</span>
            <span class="location" @click="positionDetail()" v-if="scope.row.fileformat !== '视频'">定位</span>
          </template>
        </el-table-column>
      </el-table>
@@ -133,15 +133,19 @@
        <video style="width:100%" class="videoBox" ref="videoRefs" controls autoplay :src="currentVideoUrl"></video>
      </div>
    </el-dialog>
    <!-- 地图弹框 -->
     <dataCenterMap v-model:show="dataCenterMapVisible"></dataCenterMap>
  </div>
</template>
<script setup>
import dataCenterMap from '@/views/dataCenter/components/dataCenterMap.vue'
import PanoramaPopup from '@/components/PanoramaPopup/PanoramaPopup.vue'; //全景
import { ElMessage, ElMessageBox } from 'element-plus';
import searchData from '@/views/dataCenter/components/searchData.vue';
import fy1 from '@/assets/images/dataCenter/1.jpeg';
import {getaiImagesPage} from '@/api/dataCenter/dataCenter'
import { onMounted } from 'vue';
function convertVideoUrlToThumbnail(videoUrl) {
  // 检查是否是有效的视频URL
  if (!videoUrl || typeof videoUrl !== 'string') {
@@ -151,6 +155,7 @@
  const thumbnailUrl = videoUrl.replace(/\.mp4$/, '_small.jpg');
  return thumbnailUrl;
}
const jobListParams = reactive({
  current: 1,
  size: 10,
@@ -213,6 +218,13 @@
    fileformat: '正射',
  },
];
// 获取列表数据
const getgetaiImagesPage =()=>{
  getaiImagesPage(jobListParams).then(res=>{
    console.log('res',res);
  })
}
const handleSizeChange = () => {};
const handleCurrentChange = () => {};
// 多选
@@ -260,6 +272,16 @@
  currentVideoUrl.value = val.img;
  console.log('视频', val);
};
// 地图弹框
const dataCenterMapVisible = ref(false)
const positionDetail=()=>{
  console.log('地图');
  dataCenterMapVisible.value = true
}
onMounted(()=>{
  // getgetaiImagesPage()
})
</script>
<style scoped lang="scss">