2025-09-09 19:52:26 +08:00
|
|
|
|
<script setup>
|
2025-09-09 21:52:59 +08:00
|
|
|
|
import { onMounted, ref, onUnmounted,defineProps} from 'vue';
|
2025-09-09 19:52:26 +08:00
|
|
|
|
import CesiumImageLabelEntity from '../js/CesiumImageLabelEntity.js';
|
|
|
|
|
import CesiumFlyToRoamingController from '../js/CesiumFlyToRoamingController.js';
|
|
|
|
|
import { setSelect, getSelectList, getGps } from '@/api/projectScreen/index.ts'
|
2025-09-09 21:52:59 +08:00
|
|
|
|
import videoDialog from "./video.vue"
|
2025-09-10 10:03:35 +08:00
|
|
|
|
import { getToken } from '@/utils/auth';
|
2025-09-09 19:52:26 +08:00
|
|
|
|
const defaultExpandedKeys = [1, 2, 3] //默认展开第一级节点
|
|
|
|
|
const defaultCheckedKeys = ref([]) //默认选中节点
|
|
|
|
|
const data = ref([]);
|
2025-09-09 21:52:59 +08:00
|
|
|
|
const deviceId = ref('');
|
|
|
|
|
const videoDialogRef = ref(null);
|
2025-09-10 10:03:35 +08:00
|
|
|
|
let token = 'Bearer '+ getToken()
|
|
|
|
|
let ws = new ReconnectingWebSocket( import.meta.env.VITE_APP_BASE_WS_API + '?Authorization='+token+'&clientid='+import.meta.env.VITE_APP_CLIENT_ID);
|
|
|
|
|
// 连接ws
|
|
|
|
|
const connectWs = () => {
|
|
|
|
|
ws.onopen = (e) => {
|
|
|
|
|
console.log('this.gateWay', e);
|
|
|
|
|
// ws.send(JSON.stringify(message));
|
|
|
|
|
ws.onmessage = (e) => {
|
|
|
|
|
console.log('ws', e);
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
}
|
2025-09-09 21:52:59 +08:00
|
|
|
|
const props = defineProps({
|
|
|
|
|
isHide:{
|
|
|
|
|
type:Boolean,
|
|
|
|
|
default:true,
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
console.log('props', props);
|
|
|
|
|
|
2025-09-09 19:52:26 +08:00
|
|
|
|
const defaultProps = {
|
|
|
|
|
children: 'children',
|
|
|
|
|
label: 'label',
|
|
|
|
|
}
|
|
|
|
|
let entityManager = null;
|
|
|
|
|
window.deviceMap = new Map();
|
|
|
|
|
let list = ref([]);
|
|
|
|
|
// 漫游实例
|
|
|
|
|
let roamingController = null;
|
|
|
|
|
// 获取GPS数据
|
|
|
|
|
function getGpsData() {
|
|
|
|
|
getGps('1897160897167638529').then(res => {
|
|
|
|
|
console.log('res', res);
|
|
|
|
|
if (res.code === 200) {
|
|
|
|
|
data.value = res.data;
|
|
|
|
|
if (res.data.length > 0) {
|
|
|
|
|
res.data.forEach(element => {
|
|
|
|
|
list.value = [...list.value, ...element.children]
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
// 设置选中节点
|
|
|
|
|
function setCheckedNode(idList) {
|
|
|
|
|
let obj = {
|
|
|
|
|
projectId: '1897160897167638529',
|
|
|
|
|
idList
|
|
|
|
|
}
|
|
|
|
|
setSelect(obj).then(res => {
|
|
|
|
|
console.log('res', res);
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
// 获取选中节点
|
|
|
|
|
function getCheckedNode() {
|
|
|
|
|
getSelectList({
|
|
|
|
|
projectId: '1897160897167638529'
|
|
|
|
|
}).then(res => {
|
|
|
|
|
if (res.code == 200) {
|
|
|
|
|
defaultCheckedKeys.value = res.data || []
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
// 渲染无人机、摄像头、定位设备
|
|
|
|
|
function renderDevice(item) {
|
|
|
|
|
const imageEntity = new CesiumImageLabelEntity(Earth1.viewer, {
|
|
|
|
|
id: item.id,
|
|
|
|
|
position: {
|
|
|
|
|
lng: Number(item.lng),
|
|
|
|
|
lat: Number(item.lat),
|
|
|
|
|
height: 0
|
|
|
|
|
},
|
2025-09-09 21:52:59 +08:00
|
|
|
|
imageWidth: 64,
|
|
|
|
|
imageHeight: 64,
|
2025-09-09 19:52:26 +08:00
|
|
|
|
name: item.label || item.id,
|
2025-09-09 21:52:59 +08:00
|
|
|
|
imageUrl: `/image/${item.type}.png`,
|
|
|
|
|
onClick: (entity)=>{
|
|
|
|
|
entityClickHandler(entity,item);
|
|
|
|
|
}
|
2025-09-09 19:52:26 +08:00
|
|
|
|
});
|
|
|
|
|
window.deviceMap.set(item.id, imageEntity);
|
|
|
|
|
}
|
|
|
|
|
// 实体的点击事件
|
2025-09-09 21:52:59 +08:00
|
|
|
|
function entityClickHandler(entity,item) {
|
|
|
|
|
console.log('entity', entity,item);
|
|
|
|
|
if (item.type == 'camera') {
|
|
|
|
|
deviceId.value = 'AE9470016';
|
|
|
|
|
videoDialogRef.value.show();
|
|
|
|
|
videoDialogRef.value.videoPlay(deviceId.value);
|
|
|
|
|
}
|
2025-09-09 19:52:26 +08:00
|
|
|
|
}
|
|
|
|
|
// 初始化地球
|
|
|
|
|
function initEarth() {
|
|
|
|
|
YJ.on({
|
|
|
|
|
ws: true,
|
|
|
|
|
host: '', //资源所在服务器地址
|
|
|
|
|
username: '', //用户名 可以不登录(不填写用户名),不登录时无法加载服务端的数据
|
|
|
|
|
password: '', //密码 生成方式:md5(用户名_密码)
|
|
|
|
|
}).then((res) => {
|
|
|
|
|
let earth = new YJ.YJEarth("earth");
|
|
|
|
|
|
|
|
|
|
window.Earth1 = earth;
|
|
|
|
|
// 加载底图
|
|
|
|
|
// earth.viewer.terrainProvider = Cesium.createWorldTerrain();
|
|
|
|
|
// Earth1.viewer
|
|
|
|
|
addArcgisLayer(Earth1.viewer, 'img_w')
|
|
|
|
|
// 添加倾斜数据
|
|
|
|
|
// loadTiltData(Earth1.viewer)
|
|
|
|
|
// 获取中心点
|
|
|
|
|
YJ.Global.CesiumContainer(window.Earth1, {
|
|
|
|
|
compass: false,//罗盘
|
|
|
|
|
legend: false, //图例
|
|
|
|
|
});
|
|
|
|
|
// 创建实体管理器实例
|
|
|
|
|
list.value.forEach(item => {
|
|
|
|
|
if (defaultCheckedKeys.value.includes(item.id)) {
|
|
|
|
|
console.log("defaultCheckedKeys", item.id);
|
|
|
|
|
renderDevice(item)
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
roamingController = new CesiumFlyToRoamingController(window.Earth1.viewer, {
|
|
|
|
|
duration: 5, // 每个点之间飞行5秒
|
|
|
|
|
pitch: -89 // 20度俯角
|
|
|
|
|
});
|
|
|
|
|
window.roamingController = roamingController;
|
|
|
|
|
})
|
|
|
|
|
}
|
|
|
|
|
// 加载倾斜数据
|
|
|
|
|
function loadTiltData(viewer) {
|
|
|
|
|
viewer.terrainProvider = new Cesium.CesiumTerrainProvider({
|
|
|
|
|
// url: 'http://192.168.110.2:8895/yjearth4.0/data/pak/e904acb32aaa8b872c64866ebaaaf5e2',
|
|
|
|
|
// url:"http://58.17.134.85:7363/yjearth4.0/data/pak/e904acb32aaa8b872c64866ebaaaf5e2"
|
|
|
|
|
url: import.meta.env.VITE_EARTH_URL + "/yjearth4.0/data/pak/4eb21d3fc02873092e75640e261544b3"
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
// 获取ArcGIS服务的URL
|
|
|
|
|
function getArcGisUrlByType(type) {
|
|
|
|
|
switch (type) {
|
|
|
|
|
//影像
|
|
|
|
|
case "img_w":
|
|
|
|
|
return "https://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer";
|
|
|
|
|
//电子
|
|
|
|
|
case "vec_w":
|
|
|
|
|
return "https://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer";
|
|
|
|
|
//蓝色底图
|
|
|
|
|
case "vec_blue":
|
|
|
|
|
return "http://map.geoq.cn/arcgis/rest/services/ChinaOnlineStreetPurplishBlue/MapServer";
|
|
|
|
|
//灰色底图
|
|
|
|
|
case "vec_gray":
|
|
|
|
|
return "http://map.geoq.cn/arcgis/rest/services/ChinaOnlineStreetGray/MapServer";
|
|
|
|
|
//暖色底图
|
|
|
|
|
case "vec_warm":
|
2025-09-09 21:52:59 +08:00
|
|
|
|
return "http://map.geoq.cn/arcgis/rest/services/ChinaOnlineStreetWarm/MapServer";
|
2025-09-09 19:52:26 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 添加ArcGIS图层
|
|
|
|
|
function addArcgisLayer(viewer, type) {
|
|
|
|
|
let url = getArcGisUrlByType(type)
|
|
|
|
|
const layerProvider = new Cesium.ArcGisMapServerImageryProvider({
|
|
|
|
|
url: url
|
|
|
|
|
});
|
|
|
|
|
viewer.imageryLayers.addImageryProvider(layerProvider);
|
|
|
|
|
}
|
|
|
|
|
// 节点单击事件
|
|
|
|
|
function handleNodeClick(data) {
|
|
|
|
|
console.log('data', data);
|
|
|
|
|
let entity = window.deviceMap.get(data.id);
|
|
|
|
|
if (entity) {
|
|
|
|
|
entity.flyTo();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 复选框选中事件
|
|
|
|
|
function handleCheck(checkedNodes, nodes) {
|
|
|
|
|
console.log('check', checkedNodes, nodes);
|
|
|
|
|
// 处理单个节点的通用函数
|
|
|
|
|
const handleNode = (node) => {
|
|
|
|
|
if (!window.deviceMap.has(node.id)) {
|
|
|
|
|
console.log("defaultCheckedKeys", node.id);
|
|
|
|
|
renderDevice(node);
|
|
|
|
|
} else {
|
|
|
|
|
const device = window.deviceMap.get(node.id);
|
|
|
|
|
// 根据当前显示状态切换显示/隐藏
|
|
|
|
|
device[device.entity.show ? 'hide' : 'show']();
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 处理选中的节点(可能是单个节点或包含子节点的集合)
|
|
|
|
|
if (checkedNodes?.children?.length) {
|
|
|
|
|
console.log('children', checkedNodes.children);
|
|
|
|
|
checkedNodes.children.forEach(handleNode);
|
|
|
|
|
} else {
|
|
|
|
|
handleNode(checkedNodes);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
setCheckedNode(nodes.checkedKeys);
|
|
|
|
|
}
|
|
|
|
|
// 开始漫游
|
|
|
|
|
function startRoaming() {
|
|
|
|
|
if (roamingController) {
|
|
|
|
|
roamingController.startPathRoaming([
|
|
|
|
|
Cesium.Cartesian3.fromDegrees(106.49556855602525, 29.534393226355515, 200),
|
|
|
|
|
Cesium.Cartesian3.fromDegrees(106.49142431645038, 29.534472802500083, 200),
|
|
|
|
|
Cesium.Cartesian3.fromDegrees(106.49142125177437, 29.541881138875755, 200)
|
|
|
|
|
], 3, false);
|
|
|
|
|
} else {
|
|
|
|
|
console.log('请先初始化地球');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// 停止漫游
|
|
|
|
|
function stopRoaming() {
|
|
|
|
|
if (roamingController) {
|
|
|
|
|
roamingController.stopRoaming();
|
|
|
|
|
} else {
|
|
|
|
|
console.log('请先初始化地球');
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
onMounted(() => {
|
2025-09-10 10:03:35 +08:00
|
|
|
|
// 连接ws
|
|
|
|
|
connectWs();
|
2025-09-09 19:52:26 +08:00
|
|
|
|
// 获取选中节点
|
|
|
|
|
getCheckedNode();
|
|
|
|
|
// 获取GPS数据
|
|
|
|
|
getGpsData();
|
|
|
|
|
// 初始化地球
|
|
|
|
|
initEarth();
|
|
|
|
|
});
|
|
|
|
|
onUnmounted(() => {
|
|
|
|
|
window.deviceMap.forEach((item) => {
|
|
|
|
|
item.destroy();
|
|
|
|
|
})
|
|
|
|
|
window.deviceMap.clear();
|
|
|
|
|
window.roamingController.destroy();
|
|
|
|
|
window.Earth1.destroy();
|
|
|
|
|
})
|
|
|
|
|
</script>
|
|
|
|
|
<template>
|
|
|
|
|
<div class="earth-container-big">
|
|
|
|
|
<div class="earth" id="earth"></div>
|
2025-09-09 21:52:59 +08:00
|
|
|
|
<div v-show="isHide" class="left">
|
2025-09-09 19:52:26 +08:00
|
|
|
|
<div style="width: 100%;height: 100%;">
|
|
|
|
|
<el-button type="primary" @click="startRoaming">开始漫游</el-button>
|
|
|
|
|
<el-button type="primary" @click="stopRoaming">停止漫游</el-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2025-09-09 21:52:59 +08:00
|
|
|
|
<div v-show="isHide" class="right">
|
2025-09-09 19:52:26 +08:00
|
|
|
|
<el-tree show-checkbox :data="data" :props="defaultProps" node-key="id" :expand-on-click-node="false"
|
|
|
|
|
:check-on-click-node="false" :check-on-click-leaf="false" :default-expanded-keys="defaultExpandedKeys"
|
|
|
|
|
:default-checked-keys="defaultCheckedKeys" @check="handleCheck" @node-click="handleNodeClick" />
|
|
|
|
|
</div>
|
2025-09-09 21:52:59 +08:00
|
|
|
|
<videoDialog :data="deviceId" ref="videoDialogRef"></videoDialog>
|
2025-09-09 19:52:26 +08:00
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
<style lang="scss">
|
|
|
|
|
.earth-container-big {
|
|
|
|
|
position: relative;
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
|
|
|
|
.earth {
|
|
|
|
|
width: 100%;
|
|
|
|
|
height: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.right {
|
|
|
|
|
top: 50%;
|
|
|
|
|
right: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.left {
|
|
|
|
|
top: 50%;
|
|
|
|
|
left: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.right,
|
|
|
|
|
.left {
|
|
|
|
|
position: absolute;
|
2025-09-09 21:52:59 +08:00
|
|
|
|
width: 400px;
|
2025-09-09 19:52:26 +08:00
|
|
|
|
height: 100%;
|
|
|
|
|
transform: translateY(-50%);
|
|
|
|
|
background-color: #00000052;
|
|
|
|
|
padding: 20px;
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
z-index: 10;
|
|
|
|
|
|
|
|
|
|
.el-tree {
|
|
|
|
|
background-color: transparent;
|
|
|
|
|
--el-tree-node-hover-bg-color: transparent;
|
|
|
|
|
--el-tree-text-color: #fff;
|
|
|
|
|
.el-text {
|
|
|
|
|
color: azure;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.el-tree-node__content:hover {
|
|
|
|
|
background-color: transparent;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|