From 57412c916fca9c2c66446a3651c2ff7f6b2efa19 Mon Sep 17 00:00:00 2001
From: 张含笑 <zhx18749296735@163.com>
Date: Tue, 10 Jun 2025 16:46:51 +0800
Subject: [PATCH] feat:地图弹框

---
 src/hooks/components/EventPopUpBox.vue                  |  142 ++++++++++++++++++++
 src/views/dataCenter/dataCenter.vue                     |   26 +++
 src/assets/images/dataCenter/datamap/popUpBox.png       |    0 
 src/views/dataCenter/components/dataCenterMap.vue       |  210 ++++++++++++++++++++++++++++++
 src/assets/images/dataCenter/datamap/eventCompleted.png |    0 
 src/api/dataCenter/dataCenter.js                        |    8 +
 src/assets/images/dataCenter/datamap/activeevent.png    |    0 
 7 files changed, 384 insertions(+), 2 deletions(-)

diff --git a/src/api/dataCenter/dataCenter.js b/src/api/dataCenter/dataCenter.js
new file mode 100644
index 0000000..f73dec8
--- /dev/null
+++ b/src/api/dataCenter/dataCenter.js
@@ -0,0 +1,8 @@
+import request from '@/axios';
+export const getaiImagesPage = (data) => {
+    return request({
+		url: `/blade-resource/attach/aiImagesPage`,
+		method: 'post',
+		data
+	})
+  };
\ No newline at end of file
diff --git a/src/assets/images/dataCenter/datamap/activeevent.png b/src/assets/images/dataCenter/datamap/activeevent.png
new file mode 100644
index 0000000..1e6d965
--- /dev/null
+++ b/src/assets/images/dataCenter/datamap/activeevent.png
Binary files differ
diff --git a/src/assets/images/dataCenter/datamap/eventCompleted.png b/src/assets/images/dataCenter/datamap/eventCompleted.png
new file mode 100644
index 0000000..f606331
--- /dev/null
+++ b/src/assets/images/dataCenter/datamap/eventCompleted.png
Binary files differ
diff --git a/src/assets/images/dataCenter/datamap/popUpBox.png b/src/assets/images/dataCenter/datamap/popUpBox.png
new file mode 100644
index 0000000..ab9ea28
--- /dev/null
+++ b/src/assets/images/dataCenter/datamap/popUpBox.png
Binary files differ
diff --git a/src/hooks/components/EventPopUpBox.vue b/src/hooks/components/EventPopUpBox.vue
new file mode 100644
index 0000000..36953d4
--- /dev/null
+++ b/src/hooks/components/EventPopUpBox.vue
@@ -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>
diff --git a/src/views/dataCenter/components/dataCenterMap.vue b/src/views/dataCenter/components/dataCenterMap.vue
new file mode 100644
index 0000000..105d4a1
--- /dev/null
+++ b/src/views/dataCenter/components/dataCenterMap.vue
@@ -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>
diff --git a/src/views/dataCenter/dataCenter.vue b/src/views/dataCenter/dataCenter.vue
index 185cc2f..96de4b7 100644
--- a/src/views/dataCenter/dataCenter.vue
+++ b/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">

--
Gitblit v1.9.3