From de61feb06c164238302da048c2f05789911e1d17 Mon Sep 17 00:00:00 2001
From: 罗广辉 <guanghui.luo@foxmail.com>
Date: Wed, 02 Apr 2025 19:12:58 +0800
Subject: [PATCH] feat: 事件聚合80%
---
src/views/Home/useUavHome/useUavHome.js | 289 +++++++++++++++++++++++++---------------
src/views/Home/useEventOperate/EventPopUpBox.vue | 55 +++++--
src/views/Home/useEventOperate/useEventOperate.js | 1
src/views/Home/useUavHome/DevicePopUpBox.vue | 0
src/api/home/aggregation.js | 21 ++
src/views/Home/Footer.vue | 10
src/views/Home/Home.vue | 2
7 files changed, 240 insertions(+), 138 deletions(-)
diff --git a/src/api/home/aggregation.js b/src/api/home/aggregation.js
index f68ed63..293ea18 100644
--- a/src/api/home/aggregation.js
+++ b/src/api/home/aggregation.js
@@ -1,6 +1,6 @@
import request from '@/axios';
-
+// 机巢聚合
export const getDeviceRegionCount = (params) => {
return request({
url: '/drone-device-core/manage/api/v1/devices/getDeviceRegionCount',
@@ -9,7 +9,7 @@
});
};
-
+// 机巢散点
export const getDeviceRegion = (params) => {
return request({
url: '/drone-device-core/manage/api/v1/devices/getDeviceRegion',
@@ -17,3 +17,20 @@
params
});
};
+
+// 事件聚合 和 事件散点
+export const getMapEvents = (data) => {
+ return request({
+ url: '/drone-device-core/jobEvent/mapEvents',
+ method: 'post',
+ data
+ });
+};
+// 事件散点详情
+export const getEventDetails = (params) => {
+ return request({
+ url: '/drone-device-core/jobEvent/eventDetails',
+ method: 'post',
+ params
+ });
+};
diff --git a/src/views/Home/Footer.vue b/src/views/Home/Footer.vue
index ddbb6f4..7ff483e 100644
--- a/src/views/Home/Footer.vue
+++ b/src/views/Home/Footer.vue
@@ -29,9 +29,9 @@
import { useEventOperate } from '@/views/Home/useEventOperate/useEventOperate';
// 机巢聚合
-const { init, removeAll } = useUavHome();
+const { init, removeAll } = useUavHome('device');
// 事件聚合
-const { init: eventInit, removeAll: eventRemove } = useEventOperate();
+const { init: eventInit, removeAll: eventRemove } = useUavHome('event');
const list = ref([
{
@@ -61,16 +61,16 @@
const fromItem = list.value.find(item => item.active);
if (fromItem.name === toItem.name) return;
fromItem?.removeAll?.();
- nextTick(()=>{
+ nextTick(() => {
toItem?.init?.();
- })
+ });
list.value = list.value.map(item => ({ ...item, active: item.name === toItem.name }));
};
// 销毁前钩子
onBeforeUnmount(() => {
- if (!window.$viewer) return;
removeAll();
+ eventRemove()
});
onMounted(() => {
nextTick(() => {
diff --git a/src/views/Home/Home.vue b/src/views/Home/Home.vue
index 5a2c5a7..b7965ab 100644
--- a/src/views/Home/Home.vue
+++ b/src/views/Home/Home.vue
@@ -26,7 +26,7 @@
const singleUavHome = computed(() => store.state.home.singleUavHome);
-onBeforeUnmount(()=>{
+onUnmounted(()=>{
store.commit('setSingleUavHome',null);
viewerDestory()
})
diff --git a/src/views/Home/useEventOperate/EventPopUpBox.vue b/src/views/Home/useEventOperate/EventPopUpBox.vue
index a5abd92..dd87e8d 100644
--- a/src/views/Home/useEventOperate/EventPopUpBox.vue
+++ b/src/views/Home/useEventOperate/EventPopUpBox.vue
@@ -1,37 +1,50 @@
<template>
<div class="mapPopUpBox">
<div class="title">
- 违章建筑
+ {{ info.event_name }}
<el-icon class="header-close" @click="props.removeLabel">
<Close />
</el-icon>
</div>
<div class="content">
- <img
- class="eventImage"
- src="@/assets/images/home/useEventOperate/eventErr.png"
- alt="Event Image"
- />
+ <div class="medium">
+ <img v-if="info.media_type === 'PHOTO_TYPE'" class="eventImage" :src="info.url" alt="Event Image" />
+ <video v-else :src="info.url" autoplay muted playsinline preload="metadata" />
+ </div>
+
<div class="details">
<div class="label">位置:</div>
- <div class="value point">112.12345 23.98726</div>
+ <div class="value point">{{ _.round(info.longitude, 3) }},{{ _.round(info.latitude, 3) }}</div>
<div class="label">时间:</div>
- <div class="value">2025.08.16 22:25</div>
+ <div class="value">{{ info.create_time }}</div>
</div>
</div>
</div>
</template>
<script setup>
+// src="@/assets/images/home/useEventOperate/eventErr.png"
import { Close } from '@element-plus/icons-vue';
-
-const list = [
- { name: '执行中', value: '1', color: '#FFA768' },
- { name: '在线', value: '20', color: '#8EFFAC' },
- { name: '离线', value: '14', color: '#FFFFFF' },
- { name: '异常', value: '25', color: '#FF6262' },
-];
+import { getEventDetails } from '@/api/home/aggregation';
+import _ from 'lodash';
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',
+ media_type: 'PHOTO_TYPE',
+});
+onMounted(async () => {
+ loading.value = true;
+ const res = await getEventDetails({ id: props.data.eventId });
+ info.value = res.data.data;
+ loading.value = false;
+});
</script>
<style scoped lang="scss">
@@ -47,7 +60,7 @@
font-weight: 400;
font-size: 18px;
line-height: 16px;
- background: linear-gradient(180deg, #A8E5FB 0%, #E6F8FF 100%);
+ background: linear-gradient(180deg, #a8e5fb 0%, #e6f8ff 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
-moz-background-clip: text;
@@ -77,10 +90,16 @@
display: flex;
align-items: center;
- img {
+ .medium {
height: 102px;
- width: 102px;
+ width: 120px;
margin-right: 10px;
+
+ > img,
+ video {
+ width: 100%;
+ height: 100%;
+ }
}
.details {
diff --git a/src/views/Home/useEventOperate/useEventOperate.js b/src/views/Home/useEventOperate/useEventOperate.js
index 56033b9..713c0d9 100644
--- a/src/views/Home/useEventOperate/useEventOperate.js
+++ b/src/views/Home/useEventOperate/useEventOperate.js
@@ -1,6 +1,5 @@
import * as Cesium from 'cesium';
import data2 from '@/assets/images/home/homeRight/data2.png';
-import data1 from '@/assets/images/home/homeRight/data1.png';
import eventImg from '@/assets/images/home/useEventOperate/event.png';
import jiangxishi from '@/assets/geojson/jiangxishi.json';
import jiangxi from '@/assets/geojson/jiangxi.json';
diff --git a/src/views/Home/useUavHome/MapPopUpBox.vue b/src/views/Home/useUavHome/DevicePopUpBox.vue
similarity index 100%
rename from src/views/Home/useUavHome/MapPopUpBox.vue
rename to src/views/Home/useUavHome/DevicePopUpBox.vue
diff --git a/src/views/Home/useUavHome/useUavHome.js b/src/views/Home/useUavHome/useUavHome.js
index 655b589..0e7d984 100644
--- a/src/views/Home/useUavHome/useUavHome.js
+++ b/src/views/Home/useUavHome/useUavHome.js
@@ -1,31 +1,51 @@
import * as Cesium from 'cesium';
import aggregationImg from '@/assets/images/home/useUavHome/aggregation.png';
import uavImg from '@/assets/images/home/useUavHome/uavImg.png';
-import MapPopUpBox from '@/views/Home/useUavHome/MapPopUpBox.vue';
+
+import eventImg from '@/assets/images/home/useEventOperate/event.png';
+
+import DevicePopUpBox from '@/views/Home/useUavHome/DevicePopUpBox.vue';
+import EventPopUpBox from '@/views/Home/useEventOperate/EventPopUpBox.vue';
+
import { render } from 'vue';
import { useStore } from 'vuex';
import { getCenterPoint } from '@/utils/cesium/mapUtil';
import cesiumOperation from '@/utils/cesium-tsa';
-import { HeadingPitchRange } from 'cesium';
-import { getDeviceRegion, getDeviceRegionCount } from '@/api/home/aggregation';
-
+import { getDeviceRegion, getDeviceRegionCount, getEventDetails, getMapEvents } from '@/api/home/aggregation';
/**
* 机巢聚合功能
*/
-export const useUavHome = () => {
+export const useUavHome = type => {
const { flyTo } = cesiumOperation();
+ const singleImg = type === 'device' ? uavImg : eventImg;
+ const mergeImg = type === 'device' ? aggregationImg : aggregationImg;
+ const MapPopUpBox = type === 'device' ? DevicePopUpBox : EventPopUpBox;
+ const styleTransform = type === 'device' ? 'translateY(-50%)' : 'translate(-50%,-110%)';
+
let scalingJudgment = [
- { name: '县', splashedList: [], gJson: null, show: false,outline:{}, value: [0, 48651], height: 31753 },
- { name: '市', splashedList: [], gJson: null, show: false,outline:{}, value: [48651, 314863], height: 257731 },
- { name: '省', splashedList: [], gJson: null, show: false,outline:{}, value: [314863, 3796280000], height: 1987280 },
+ { name: '县', splashedList: [], gJson: null, show: false, outline: {}, value: [0, 48651], height: 31753 },
+ { name: '市', splashedList: [], gJson: null, show: false, outline: {}, value: [48651, 314863], height: 257731 },
+ {
+ name: '省',
+ splashedList: [],
+ gJson: null,
+ show: false,
+ outline: {},
+ value: [314863, 3796280000],
+ height: 1987280,
+ },
];
let viewer = null;
let active = null;
let handler = null;
let positionC3 = null;
let currentEntity = null;
+
+ const store = useStore();
+ const userAreaCode = computed(() => store.state.user.userInfo.detail.areaCode);
+ const selectedAreaCode = computed(() => store.state.user.selectedAreaCode);
const listenerHeight = () => {
determineScaling();
viewer.camera.moveEnd.addEventListener(determineScaling);
@@ -44,8 +64,8 @@
active = item.name;
removeEntities();
removeLabel();
- item.gJson ? aggregation(item) : splashed(item)
- renderOutline(item)
+ item.gJson ? aggregation(item) : splashed(item);
+ renderOutline(item);
break;
}
}
@@ -63,100 +83,134 @@
return [provinceCode, cityCode, code];
}
- const store = useStore();
- const selectedAreaCode = computed(() => store.state.user.selectedAreaCode);
-
function getDeviceCount(areaCode) {
- return getDeviceRegionCount({areaCode}).then(res => {
- return res?.data?.data || [];
+ return getDeviceRegionCount({ areaCode }).then(res => {
+ return (res?.data?.data || []).map(i=>({...i,type:'deviceAggregation'}));
});
}
function getDeviceList(areaCode) {
- return getDeviceRegion({areaCode}).then(res => {
- return res?.data?.data || [];
+ return getDeviceRegion({ areaCode }).then(res => {
+ return (res?.data?.data || []).map(i =>({...i,type}));
});
}
+
+ let eventList = []
+ function processChildren(childrens) {
+ return (childrens || []).map(item => {
+ const arr = processChildren(item.childrens);
+ if (item.data){
+ eventList = eventList.concat(item.data)
+ }
+ const returnObj = {
+ total_device_count: item.number,
+ region_code: item.id,
+ region_name: item.name,
+ type:'eventAggregation'
+ };
+ if (arr.length !== 0) {
+ returnObj.childrens = arr;
+ }
+ return returnObj;
+ });
+ }
+
+ function getMapEventCount(areaCode) {
+ return getMapEvents({ areaCode }).then(res => {
+ return processChildren(res?.data?.data?.childrens || []);
+ });
+ }
+ function getEventDetailsFun(areaCode){
+ return getEventDetails({areaCode}).then(res => {
+ console.log(res.data.data,'getEventDetailsFun');
+ });
+ }
+
const findFun = (featItem, numItem) => Number(featItem.region_code.slice(0, 6)) === numItem.properties.adcode;
const { VITE_APP_BASE } = import.meta.env;
const defaultDir = `${VITE_APP_BASE}public/geoJson/100000/`;
- const getFiler = async (url) => {
+ const getFiler = async url => {
const gJson = await import(/* @vite-ignore */ url);
return JSON.parse(JSON.stringify(gJson));
- }
- const getOutLine = async (jsonPathPre,hierarchy) => {
- const parentGJson = await getFiler(`${defaultDir}${jsonPathPre}/index.json`)
+ };
+ const getOutLine = async (jsonPathPre, hierarchy) => {
+ const parentGJson = await getFiler(`${defaultDir}${jsonPathPre}/index.json`);
let features = parentGJson.features.find(
item => item.properties.adcode === Number(hierarchy[hierarchy.length - 1])
);
- return { type: "FeatureCollection", features: [features] }
- }
+ return { type: 'FeatureCollection', features: [features] };
+ };
- const injectData = (gJson,dataList) => {
+ const injectData = (gJson, dataList) => {
return {
...gJson,
features: gJson.features.map(item => ({ ...item, data: dataList.find(item1 => findFun(item1, item)) })),
+ };
+ };
+
+ const initMapData = async areaCode => {
+ if (!areaCode) return;
+ const list = type === 'device' ? await getDeviceCount(areaCode) : await getMapEventCount(areaCode);
+ 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('/');
+ scalingJudgment = scalingJudgment.map(item => ({ ...item, show: true }));
+ scalingJudgment[0].gJson = null;
+ scalingJudgment[0].splashedList = splashedList;
+ active = null;
+ console.log(hierarchy);
+
+ // 省
+ if (hierarchy.length === 1) {
+ const gJson1 = await getFiler(`${defaultDir}${jsonPath}/indexDistrict.json`);
+ const gJson2 = await getFiler(`${defaultDir}${jsonPath}/index.json`);
+ scalingJudgment[1].gJson = {
+ ...gJson1,
+ features: gJson1.features.map(item => ({
+ ...item,
+ data: list.flatMap(item => item.childrens || []).find(item1 => findFun(item1, item)),
+ })),
+ };
+ scalingJudgment[2].gJson = injectData(gJson2, list);
+ const { lng, lat } = getCenterPoint(gJson2.features.map(item => item.properties.center));
+ flyTo({ longitude: lng, latitude: lat }, 0, scalingJudgment[2].height);
}
- }
-
-
+ // 市
+ if (hierarchy.length === 2) {
+ scalingJudgment[2].gJson = null;
+ scalingJudgment[2].show = false;
+ const gJson1 = await getFiler(`${defaultDir}${jsonPath}/index.json`);
+ scalingJudgment[1].gJson = injectData(gJson1, list);
+ const center = getCenterPoint(gJson1.features.map(item => item.properties.center));
+ flyTo({ longitude: center.lng, latitude: center.lat }, 0, scalingJudgment[1].height);
+ }
+ // 区县
+ if (hierarchy.length === 3) {
+ scalingJudgment[1].gJson = null;
+ scalingJudgment[1].show = false;
+ scalingJudgment[2].gJson = null;
+ scalingJudgment[2].show = false;
+ const outlineGJson = await getOutLine(jsonPathPre, hierarchy);
+ const center = outlineGJson.features[0].properties.center;
+ flyTo({ longitude: center[0], latitude: center[1] }, 0, scalingJudgment[0].height);
+ }
+ // 轮廓
+ const outlineGJson = await getOutLine(jsonPathPre, hierarchy);
+ scalingJudgment.forEach(item => item.show && (item.outline = outlineGJson));
+ };
watch(
selectedAreaCode,
async (newValue, oldValue) => {
- if (newValue) {
- const list = await getDeviceCount(newValue);
- const splashedList = await getDeviceList(newValue);
- console.log(list);
- console.log(splashedList);
- const hierarchy = convertToHierarchy(newValue.slice(0, 6));
- const jsonPath = hierarchy.join('/');
- const jsonPathPre = hierarchy.slice(0, hierarchy.length - 1).join('/');
- scalingJudgment = scalingJudgment.map(item => ({ ...item, show: true }));
- scalingJudgment[0].gJson = null;
- scalingJudgment[0].splashedList = splashedList;
- active = null
- // 省
- if (hierarchy.length === 1) {
- const gJson1 = await getFiler(`${defaultDir}${jsonPath}/indexDistrict.json`)
- const gJson2 = await getFiler(`${defaultDir}${jsonPath}/index.json`)
- scalingJudgment[1].gJson = {
- ...gJson1,
- features: gJson1.features.map(item => ({
- ...item,
- data: list.flatMap(item => item.childrens || []).find(item1 => findFun(item1, item)),
- })),
- };
- scalingJudgment[2].gJson = injectData(gJson2,list);
- const { lng, lat } = getCenterPoint(gJson2.features.map(item => item.properties.center));
- flyTo({ longitude: lng, latitude: lat }, 0, scalingJudgment[2].height);
- }
- // 市
- if (hierarchy.length === 2) {
- scalingJudgment[2].gJson = null;
- scalingJudgment[2].show = false;
- const gJson1 = await getFiler(`${defaultDir}${jsonPath}/index.json`)
- scalingJudgment[1].gJson = injectData(gJson1,list);
- const center = getCenterPoint(gJson1.features.map(item => item.properties.center));
- flyTo({ longitude: center.lng, latitude: center.lat }, 0, scalingJudgment[1].height);
- }
- // 区县
- if (hierarchy.length === 3) {
- scalingJudgment[1].gJson = null;
- scalingJudgment[1].show = false;
- scalingJudgment[2].gJson = null;
- scalingJudgment[2].show = false;
- const outlineGJson = await getOutLine(jsonPathPre,hierarchy)
- const center = outlineGJson.features[0].properties.center;
- flyTo({ longitude: center[0], latitude: center[1] }, 0, scalingJudgment[0].height);
- }
- // 轮廓
- const outlineGJson = await getOutLine(jsonPathPre,hierarchy)
- scalingJudgment.forEach(item => item.show &&(item.outline = outlineGJson))
- }
+ initMapData(newValue);
},
- { immediate: true, deep: true }
+ { deep: true }
);
//散点机巢
@@ -176,7 +230,7 @@
pixelOffset: new Cesium.Cartesian2(0, -9),
},
billboard: {
- image: new Cesium.ConstantProperty(uavImg),
+ image: new Cesium.ConstantProperty(singleImg),
width: 24,
height: 24,
},
@@ -190,29 +244,30 @@
}
// 渲染轮廓
- const renderOutline = (item) => {
- item.outline && Cesium.GeoJsonDataSource.load(item.outline).then(dataSource => {
- viewer.dataSources.add(dataSource);
- const entities = dataSource.entities.values;
- entities.forEach(entity => {
- // 隐藏多边形填充
- entity.polygon.material = Cesium.Color.TRANSPARENT;
- entity.polygon.outline = false; // 关闭原生轮廓
- // 创建独立折线作为轮廓
- const positions = entity.polygon.hierarchy.getValue().positions;
- viewer.entities.add({
- polyline: {
- positions: positions,
- width: 5, // 直接设置宽度
- material: new Cesium.PolylineGlowMaterialProperty({
- glowPower: 0.5,
- color: Cesium.Color.AQUA
- }),
- clampToGround: true // 贴地显示
- }
+ const renderOutline = item => {
+ item.outline &&
+ Cesium.GeoJsonDataSource.load(item.outline).then(dataSource => {
+ viewer.dataSources.add(dataSource);
+ const entities = dataSource.entities.values;
+ entities.forEach(entity => {
+ // 隐藏多边形填充
+ entity.polygon.material = Cesium.Color.TRANSPARENT;
+ entity.polygon.outline = false; // 关闭原生轮廓
+ // 创建独立折线作为轮廓
+ const positions = entity.polygon.hierarchy.getValue().positions;
+ viewer.entities.add({
+ polyline: {
+ positions: positions,
+ width: 5, // 直接设置宽度
+ material: new Cesium.PolylineGlowMaterialProperty({
+ glowPower: 0.5,
+ color: Cesium.Color.AQUA,
+ }),
+ clampToGround: true, // 贴地显示
+ },
+ });
});
});
- });
};
// 聚合机巢
@@ -239,20 +294,20 @@
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
pixelOffset: new Cesium.Cartesian2(0, -9),
- }
+ },
});
viewer.entities.add({
id: feature.id,
position: position,
label: {
- text: feature.data.total_device_count,
+ text: feature.data.total_device_count.toString(),
font: '12pt Source Han Sans CN',
fillColor: Cesium.Color.BLACK,
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
eyeOffset: new Cesium.Cartesian3(0, 0, -10), // 让label "浮" 在广告牌前面
},
billboard: {
- image: new Cesium.ConstantProperty(aggregationImg),
+ image: new Cesium.ConstantProperty(mergeImg),
width: 35,
height: 35,
},
@@ -276,7 +331,7 @@
Cesium.Color.YELLOW.withAlpha(0) // 透明填充
);
entity.polygon.outline = new Cesium.ConstantProperty(true); // 显示边框
- entity.polygon.outlineColor = new Cesium.ConstantProperty(Cesium.Color.AQUAMARINE );
+ entity.polygon.outlineColor = new Cesium.ConstantProperty(Cesium.Color.AQUAMARINE);
});
flyTo &&
viewer.flyTo(dataSource, {
@@ -296,7 +351,7 @@
const tooltipContainer = document.createElement('div');
tooltipContainer.id = 'mapPopUpBox';
tooltipContainer.style.position = 'absolute';
- tooltipContainer.style.transform = 'translateY(-50%)';
+ tooltipContainer.style.transform = styleTransform;
tooltipContainer.style.pointerEvents = 'none';
document.querySelector('.page-index').append(tooltipContainer);
render(vNode, tooltipContainer);
@@ -326,13 +381,23 @@
positionC3 = entity?.position?._value;
if (!positionC3) return;
removeLabel();
- const customData = entity.properties.customData._value.data
- console.log(customData);
- if (customData.device_sn){
- store.commit('setSingleUavHome', { id: '123' });
- return
+ const customData = entity.properties.customData._value.data;
+
+ switch (customData.type) {
+ case 'deviceAggregation':
+ viewer.scene.postRender.addEventListener(labelBoxRender);
+ break;
+ case 'device':
+ store.commit('setSingleUavHome', { id: '123' });
+ break;
+ case 'eventAggregation':
+ break;
+ case 'event':
+ viewer.scene.postRender.addEventListener(labelBoxRender);
+ break;
+ default:
+ break;
}
- viewer.scene.postRender.addEventListener(labelBoxRender);
}
};
@@ -368,11 +433,13 @@
currentEntity = null;
};
const init = () => {
+ initMapData(selectedAreaCode.value || userAreaCode.value);
viewer = window.$viewer;
handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);
handler.setInputAction(singleMachineEvent, Cesium.ScreenSpaceEventType.LEFT_CLICK);
listenerHeight();
};
+ onBeforeUnmount(() => {});
// onMounted(() => {
// nextTick(() => {
// viewer = window.$viewer;
--
Gitblit v1.9.3