地图右键菜单
This commit is contained in:
@ -1,17 +1,45 @@
|
||||
<template>
|
||||
<div class="flex justify-between">
|
||||
<el-tree
|
||||
style="width: 340px; height: 500px; overflow-y: auto"
|
||||
<el-tree-v2
|
||||
style="width: 340px; overflow-y: auto"
|
||||
show-checkbox
|
||||
:data="jsonData"
|
||||
:height="500"
|
||||
@check-change="handleCheckChange"
|
||||
@node-contextmenu="nodeMenu"
|
||||
:props="treeProps"
|
||||
@node-contextmenu="showMenu"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<span @dblclick="handlePosition(data)">{{ data.name }}</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
<div class="ol-map" id="olMap"></div>
|
||||
</el-tree-v2>
|
||||
<div>
|
||||
<div class="ol-map" id="olMap"></div>
|
||||
<div class="bg-#909399 p-1 m-0 border-rd c-white w-100 pl-xl mt-2 text-3 pr" v-if="selectLayer.length">
|
||||
<p v-for="(item, index) in selectLayer" class="flex justify-between items-center cursor-pointer" @click="delLayer(index)">
|
||||
{{ item }}
|
||||
<el-icon>
|
||||
<Close />
|
||||
</el-icon>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="isMenuVisible" :style="{ left: menuX + 'px', top: menuY + 'px' }" class="fixed bg-white shadow-md rounded-md overflow-hidden">
|
||||
<ul class="py-1 pl-0">
|
||||
<li class="px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 cursor-pointer" @click="handleMenuItemClick('光伏板')">
|
||||
<i class="fa-solid fa-check mr-2"></i>光伏板
|
||||
</li>
|
||||
<li class="px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 cursor-pointer" @click="handleMenuItemClick('桩点/支架')">
|
||||
<i class="fa-solid fa-times mr-2"></i>桩点/支架
|
||||
</li>
|
||||
<li class="px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 cursor-pointer" @click="handleMenuItemClick('方阵')">
|
||||
<i class="fa-solid fa-times mr-2"></i>方阵
|
||||
</li>
|
||||
<li class="px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 cursor-pointer" @click="handleMenuItemClick('名称')">
|
||||
<i class="fa-solid fa-times mr-2"></i>名称
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -27,11 +55,11 @@ import Feature from 'ol/Feature'; // OpenLayers的要素类,表示地图上的
|
||||
import Point from 'ol/geom/Point'; // OpenLayers的点几何类,用于表示点状的地理数据
|
||||
import { Vector as VectorLayer } from 'ol/layer'; // OpenLayers的矢量图层类,用于显示矢量数据
|
||||
import { Vector as VectorSource } from 'ol/source'; // OpenLayers的矢量数据源类,用于管理和提供矢量数据
|
||||
import { Circle, Style, Stroke, Fill, Icon } from 'ol/style'; // OpenLayers的样式类,用于定义图层的样式,包括圆形样式、基本样式、边框、填充和图标
|
||||
import { Circle, Style, Stroke, Fill, Icon, Text } from 'ol/style'; // OpenLayers的样式类,用于定义图层的样式,包括圆形样式、基本样式、边框、填充和图标
|
||||
import LineString from 'ol/geom/LineString'; // OpenLayers的线几何类,用于表示线状的地理数据
|
||||
import Polygon from 'ol/geom/Polygon'; // OpenLayers的多边形几何类,用于表示面状的地理数据
|
||||
import * as turf from '@turf/turf';
|
||||
import { Snowflake } from '@/utils/snowflake';
|
||||
import { SnowflakeIdGenerator } from '@/utils/snowflake';
|
||||
const props = defineProps({
|
||||
treeData: {
|
||||
type: Array,
|
||||
@ -39,19 +67,25 @@ const props = defineProps({
|
||||
},
|
||||
projectId: String
|
||||
});
|
||||
const snowflake = new Snowflake(1, 1);
|
||||
const contextMenu = ref(null);
|
||||
const selectLayer = ref([]);
|
||||
const treeProps = {
|
||||
value: 'name'
|
||||
};
|
||||
const snowflake = new SnowflakeIdGenerator(1, 1);
|
||||
let map: any = null;
|
||||
|
||||
const layerData = reactive<any>({});
|
||||
const centerPosition = ref(fromLonLat([107.12932603888963, 23.80590052110889]));
|
||||
const centerPosition = ref(fromLonLat([107.13761560163239, 23.80480003743964]));
|
||||
const nodeMenu = (e: any) => {
|
||||
console.log(e);
|
||||
};
|
||||
|
||||
const jsonData = computed(() => {
|
||||
let id = 0;
|
||||
props.treeData.forEach((item: any) => {
|
||||
item.features.forEach((itm, idx) => {
|
||||
itm.geometry.id = snowflake.nextId();
|
||||
item.features.forEach((itm) => {
|
||||
itm.geometry.id = ++id;
|
||||
itm.geometry.coordinates = convertStrToNum(itm.geometry.coordinates);
|
||||
});
|
||||
});
|
||||
@ -70,7 +104,7 @@ const handleCheckChange = (data: any, bool) => {
|
||||
if (bool) {
|
||||
if (!layerData[data.features[0].properties.id]) {
|
||||
data.features.forEach((item: any) => {
|
||||
creatPoint(item.geometry.coordinates, item.geometry.type, item.geometry.id);
|
||||
creatPoint(item.geometry.coordinates, item.geometry.type, item.geometry.id, item.properties.text);
|
||||
});
|
||||
} else {
|
||||
data.features.forEach((item: any) => {
|
||||
@ -164,13 +198,13 @@ function convertStrToNum(arr) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Date:2024/3/26
|
||||
* Author:zx
|
||||
* Function:【面】
|
||||
* @param 无
|
||||
*/
|
||||
|
||||
const creatPoint = (pointObj: Array<any>, type: string, id: string) => {
|
||||
* 创建图层
|
||||
* @param {*} pointObj 坐标数组
|
||||
* @param {*} type 类型
|
||||
* @param {*} id 唯一id
|
||||
* @param {*} name 名称
|
||||
* */
|
||||
const creatPoint = (pointObj: Array<any>, type: string, id: string, name?: string) => {
|
||||
// 创建多边形的几何对象
|
||||
let polygon;
|
||||
if (type === 'Point') {
|
||||
@ -193,6 +227,14 @@ const creatPoint = (pointObj: Array<any>, type: string, id: string) => {
|
||||
fill: new Fill({
|
||||
color: 'red'
|
||||
})
|
||||
}),
|
||||
text: new Text({
|
||||
font: '12px Microsoft YaHei',
|
||||
text: name,
|
||||
scale: 1,
|
||||
fill: new Fill({
|
||||
color: '#7bdd63'
|
||||
})
|
||||
})
|
||||
});
|
||||
const polygonStyle = new Style({
|
||||
@ -216,15 +258,68 @@ const creatPoint = (pointObj: Array<any>, type: string, id: string) => {
|
||||
map.addLayer(layerData[id]);
|
||||
};
|
||||
|
||||
// 控制菜单是否显示
|
||||
const isMenuVisible = ref(false);
|
||||
// 菜单的 x 坐标
|
||||
const menuX = ref(0);
|
||||
// 菜单的 y 坐标
|
||||
const menuY = ref(0);
|
||||
|
||||
// 显示菜单的方法
|
||||
const showMenu = (event: MouseEvent, data) => {
|
||||
console.log(data);
|
||||
contextMenu.value = data.name;
|
||||
isMenuVisible.value = true;
|
||||
menuX.value = event.clientX;
|
||||
menuY.value = event.clientY;
|
||||
};
|
||||
|
||||
// 处理菜单项点击事件的方法
|
||||
const handleMenuItemClick = (option: string) => {
|
||||
selectLayer.value.push(`${contextMenu.value}被选中为${option}`);
|
||||
isMenuVisible.value = false;
|
||||
};
|
||||
|
||||
//删除菜单
|
||||
const delLayer = (index) => {
|
||||
selectLayer.value.splice(index, 1);
|
||||
};
|
||||
|
||||
// 点击页面其他区域隐藏菜单
|
||||
const closeMenuOnClickOutside = (event: MouseEvent) => {
|
||||
if (isMenuVisible.value) {
|
||||
const menuElement = document.querySelector('.fixed.bg-white');
|
||||
if (menuElement && !menuElement.contains(event.target as Node)) {
|
||||
isMenuVisible.value = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 添加全局点击事件监听器
|
||||
window.addEventListener('click', closeMenuOnClickOutside);
|
||||
|
||||
// 组件卸载时移除事件监听器
|
||||
const onUnmounted = () => {
|
||||
window.removeEventListener('click', closeMenuOnClickOutside);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
// 地图初始化
|
||||
initOLMap();
|
||||
});
|
||||
|
||||
onDeactivated(() => {
|
||||
console.log('地图销毁');
|
||||
|
||||
map.getOverlays().clear();
|
||||
creatPoint(
|
||||
[
|
||||
[
|
||||
[107.13761912095511, 23.80479336386864],
|
||||
[107.13776644838967, 23.804823038597075],
|
||||
[107.13775276784109, 23.80486256412652],
|
||||
[107.1376054403658, 23.80483288938397],
|
||||
[107.13761912095511, 23.80479336386864]
|
||||
]
|
||||
],
|
||||
'Polygon',
|
||||
'1'
|
||||
);
|
||||
// creatPoint([107.13761560163239, 23.80480003743964], 'Point', '2', '点');
|
||||
});
|
||||
</script>
|
||||
<style scoped lang="scss">
|
||||
@ -250,4 +345,7 @@ onDeactivated(() => {
|
||||
border-color: #79bbff;
|
||||
}
|
||||
}
|
||||
li {
|
||||
list-style-type: none;
|
||||
}
|
||||
</style>
|
||||
|
@ -1,59 +1,33 @@
|
||||
export class Snowflake {
|
||||
private startTimeStamp: number = 1609459200000;
|
||||
private workerIdBits: number = 5;
|
||||
private dataCenterIdBits: number = 5;
|
||||
private sequenceBits: number = 12;
|
||||
export class SnowflakeIdGenerator {
|
||||
private readonly startTimeStamp = 1609459200000; // 起始时间戳,这里设置为 2021-01-01 00:00:00
|
||||
private readonly workerIdBits = 5;
|
||||
private readonly dataCenterIdBits = 5;
|
||||
private readonly sequenceBits = 12;
|
||||
|
||||
private maxWorkerId: number = -1 ^ (-1 << this.workerIdBits);
|
||||
private maxDataCenterId: number = -1 ^ (-1 << this.dataCenterIdBits);
|
||||
private readonly maxWorkerId = -1 ^ (-1 << this.workerIdBits);
|
||||
private readonly maxDataCenterId = -1 ^ (-1 << this.dataCenterIdBits);
|
||||
|
||||
private workerIdShift: number = this.sequenceBits;
|
||||
private dataCenterIdShift: number = this.sequenceBits + this.workerIdBits;
|
||||
private timestampLeftShift: number = this.sequenceBits + this.workerIdBits + this.dataCenterIdBits;
|
||||
private sequenceMask: number = -1 ^ (-1 << this.sequenceBits);
|
||||
private readonly workerIdShift = this.sequenceBits;
|
||||
private readonly dataCenterIdShift = this.sequenceBits + this.workerIdBits;
|
||||
private readonly timestampLeftShift = this.sequenceBits + this.workerIdBits + this.dataCenterIdBits;
|
||||
private readonly sequenceMask = -1 ^ (-1 << this.sequenceBits);
|
||||
|
||||
private workerId: number;
|
||||
private dataCenterId: number;
|
||||
private sequence: number = 0;
|
||||
private lastTimestamp: number = -1;
|
||||
private sequence = 0;
|
||||
private lastTimestamp = -1;
|
||||
|
||||
constructor(workerId: number, dataCenterId: number) {
|
||||
if (workerId > this.maxWorkerId || workerId < 0) {
|
||||
throw new Error(`Worker ID 不能大于 ${this.maxWorkerId} 或小于 0`);
|
||||
throw new Error(`Worker ID 必须在 0 到 ${this.maxWorkerId} 之间`);
|
||||
}
|
||||
if (dataCenterId > this.maxDataCenterId || dataCenterId < 0) {
|
||||
throw new Error(`数据中心 ID 不能大于 ${this.maxDataCenterId} 或小于 0`);
|
||||
throw new Error(`数据中心 ID 必须在 0 到 ${this.maxDataCenterId} 之间`);
|
||||
}
|
||||
this.workerId = workerId;
|
||||
this.dataCenterId = dataCenterId;
|
||||
}
|
||||
|
||||
public nextId(): number {
|
||||
let timestamp = this.getCurrentTimestamp();
|
||||
if (timestamp < this.lastTimestamp) {
|
||||
throw new Error('检测到时钟回拨,拒绝生成 ID');
|
||||
}
|
||||
if (timestamp === this.lastTimestamp) {
|
||||
this.sequence = (this.sequence + 1) & this.sequenceMask;
|
||||
if (this.sequence === 0) {
|
||||
timestamp = this.waitNextMillis(this.lastTimestamp);
|
||||
}
|
||||
} else {
|
||||
this.sequence = 0;
|
||||
}
|
||||
this.lastTimestamp = timestamp;
|
||||
return (
|
||||
((timestamp - this.startTimeStamp) << this.timestampLeftShift) |
|
||||
(this.dataCenterId << this.dataCenterIdShift) |
|
||||
(this.workerId << this.workerIdShift) |
|
||||
this.sequence
|
||||
);
|
||||
}
|
||||
|
||||
private getCurrentTimestamp(): number {
|
||||
return Date.now();
|
||||
}
|
||||
|
||||
private waitNextMillis(lastTimestamp: number): number {
|
||||
let timestamp = this.getCurrentTimestamp();
|
||||
while (timestamp <= lastTimestamp) {
|
||||
@ -61,11 +35,34 @@ export class Snowflake {
|
||||
}
|
||||
return timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
// 使用示例
|
||||
// const workerId: number = 1;
|
||||
// const dataCenterId: number = 1;
|
||||
// const snowflake = new Snowflake(workerId, dataCenterId);
|
||||
// const id: number = snowflake.nextId();
|
||||
// console.log('生成的唯一 ID:', id);
|
||||
private getCurrentTimestamp(): number {
|
||||
return Date.now();
|
||||
}
|
||||
|
||||
nextId(): number {
|
||||
let timestamp = this.getCurrentTimestamp();
|
||||
|
||||
if (timestamp < this.lastTimestamp) {
|
||||
throw new Error('时钟回拨,拒绝生成 ID 达 ' + (this.lastTimestamp - timestamp) + ' 毫秒');
|
||||
}
|
||||
|
||||
if (timestamp === this.lastTimestamp) {
|
||||
this.sequence = (this.sequence + 1) & this.sequenceMask;
|
||||
if (this.sequence === 0) {
|
||||
timestamp = this.waitNextMillis(this.lastTimestamp);
|
||||
}
|
||||
} else {
|
||||
this.sequence = 0;
|
||||
}
|
||||
|
||||
this.lastTimestamp = timestamp;
|
||||
|
||||
return (
|
||||
((timestamp - this.startTimeStamp) << this.timestampLeftShift) |
|
||||
(this.dataCenterId << this.dataCenterIdShift) |
|
||||
(this.workerId << this.workerIdShift) |
|
||||
this.sequence
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -261,7 +261,12 @@
|
||||
</el-dialog>
|
||||
<!-- 选取方阵地址 -->
|
||||
<el-dialog title="设置方阵" v-model="polygonStatus" width="1400px">
|
||||
<open-layers-map :tree-data="jsonData" :project-id="projectId"></open-layers-map>
|
||||
<open-layers-map :tree-data="jsonData" :project-id="projectId" v-if="polygonStatus"></open-layers-map>
|
||||
<el-radio-group v-model="layerType" class="ml-100">
|
||||
<el-radio value="1" size="large">光伏板</el-radio>
|
||||
<el-radio value="2" size="large">桩点/支架</el-radio>
|
||||
<el-radio value="3" size="large">方阵</el-radio>
|
||||
</el-radio-group>
|
||||
<template #footer>
|
||||
<span>
|
||||
<el-button @click="polygonStatus = false">取消</el-button>
|
||||
@ -280,9 +285,6 @@ const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
||||
const { sys_normal_disable, project_category_type, project_type } = toRefs<any>(
|
||||
proxy?.useDict('sys_normal_disable', 'project_category_type', 'project_type')
|
||||
);
|
||||
const change = (val: any) => {
|
||||
console.log(val, 1212, form.value.playCardStart);
|
||||
};
|
||||
const projectList = ref<ProjectVO[]>([]);
|
||||
const buttonLoading = ref(false);
|
||||
const loading = ref(true);
|
||||
@ -297,6 +299,7 @@ const projectFormRef = ref<ElFormInstance>();
|
||||
const polygonStatus = ref(false);
|
||||
const dxfFile = ref(null);
|
||||
const projectId = ref<string>('');
|
||||
const layerType = ref(null);
|
||||
const dialog = reactive<DialogOption>({
|
||||
visible: false,
|
||||
title: ''
|
||||
@ -460,7 +463,9 @@ const handleShowUpload = (row?: ProjectVO) => {
|
||||
};
|
||||
|
||||
const handleOpenLayer = async (row: ProjectVO) => {
|
||||
if (projectId.value == row.id) return (polygonStatus.value = true);
|
||||
fullscreenLoading.value = true;
|
||||
|
||||
const res = await listDXFProject(row.designId);
|
||||
projectId.value = row.id;
|
||||
jsonData.value = res.data.layers;
|
||||
|
Reference in New Issue
Block a user