地图右键菜单
This commit is contained in:
@ -1,17 +1,45 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="flex justify-between">
|
<div class="flex justify-between">
|
||||||
<el-tree
|
<el-tree-v2
|
||||||
style="width: 340px; height: 500px; overflow-y: auto"
|
style="width: 340px; overflow-y: auto"
|
||||||
show-checkbox
|
show-checkbox
|
||||||
:data="jsonData"
|
:data="jsonData"
|
||||||
|
:height="500"
|
||||||
@check-change="handleCheckChange"
|
@check-change="handleCheckChange"
|
||||||
@node-contextmenu="nodeMenu"
|
:props="treeProps"
|
||||||
|
@node-contextmenu="showMenu"
|
||||||
>
|
>
|
||||||
<template #default="{ node, data }">
|
<template #default="{ node, data }">
|
||||||
<span @dblclick="handlePosition(data)">{{ data.name }}</span>
|
<span @dblclick="handlePosition(data)">{{ data.name }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-tree>
|
</el-tree-v2>
|
||||||
|
<div>
|
||||||
<div class="ol-map" id="olMap"></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>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@ -27,11 +55,11 @@ import Feature from 'ol/Feature'; // OpenLayers的要素类,表示地图上的
|
|||||||
import Point from 'ol/geom/Point'; // OpenLayers的点几何类,用于表示点状的地理数据
|
import Point from 'ol/geom/Point'; // OpenLayers的点几何类,用于表示点状的地理数据
|
||||||
import { Vector as VectorLayer } from 'ol/layer'; // OpenLayers的矢量图层类,用于显示矢量数据
|
import { Vector as VectorLayer } from 'ol/layer'; // OpenLayers的矢量图层类,用于显示矢量数据
|
||||||
import { Vector as VectorSource } from 'ol/source'; // 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 LineString from 'ol/geom/LineString'; // OpenLayers的线几何类,用于表示线状的地理数据
|
||||||
import Polygon from 'ol/geom/Polygon'; // OpenLayers的多边形几何类,用于表示面状的地理数据
|
import Polygon from 'ol/geom/Polygon'; // OpenLayers的多边形几何类,用于表示面状的地理数据
|
||||||
import * as turf from '@turf/turf';
|
import * as turf from '@turf/turf';
|
||||||
import { Snowflake } from '@/utils/snowflake';
|
import { SnowflakeIdGenerator } from '@/utils/snowflake';
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
treeData: {
|
treeData: {
|
||||||
type: Array,
|
type: Array,
|
||||||
@ -39,19 +67,25 @@ const props = defineProps({
|
|||||||
},
|
},
|
||||||
projectId: String
|
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;
|
let map: any = null;
|
||||||
|
|
||||||
const layerData = reactive<any>({});
|
const layerData = reactive<any>({});
|
||||||
const centerPosition = ref(fromLonLat([107.12932603888963, 23.80590052110889]));
|
const centerPosition = ref(fromLonLat([107.13761560163239, 23.80480003743964]));
|
||||||
const nodeMenu = (e: any) => {
|
const nodeMenu = (e: any) => {
|
||||||
console.log(e);
|
console.log(e);
|
||||||
};
|
};
|
||||||
|
|
||||||
const jsonData = computed(() => {
|
const jsonData = computed(() => {
|
||||||
|
let id = 0;
|
||||||
props.treeData.forEach((item: any) => {
|
props.treeData.forEach((item: any) => {
|
||||||
item.features.forEach((itm, idx) => {
|
item.features.forEach((itm) => {
|
||||||
itm.geometry.id = snowflake.nextId();
|
itm.geometry.id = ++id;
|
||||||
itm.geometry.coordinates = convertStrToNum(itm.geometry.coordinates);
|
itm.geometry.coordinates = convertStrToNum(itm.geometry.coordinates);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@ -70,7 +104,7 @@ const handleCheckChange = (data: any, bool) => {
|
|||||||
if (bool) {
|
if (bool) {
|
||||||
if (!layerData[data.features[0].properties.id]) {
|
if (!layerData[data.features[0].properties.id]) {
|
||||||
data.features.forEach((item: any) => {
|
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 {
|
} else {
|
||||||
data.features.forEach((item: any) => {
|
data.features.forEach((item: any) => {
|
||||||
@ -164,13 +198,13 @@ function convertStrToNum(arr) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Date:2024/3/26
|
* 创建图层
|
||||||
* Author:zx
|
* @param {*} pointObj 坐标数组
|
||||||
* Function:【面】
|
* @param {*} type 类型
|
||||||
* @param 无
|
* @param {*} id 唯一id
|
||||||
*/
|
* @param {*} name 名称
|
||||||
|
* */
|
||||||
const creatPoint = (pointObj: Array<any>, type: string, id: string) => {
|
const creatPoint = (pointObj: Array<any>, type: string, id: string, name?: string) => {
|
||||||
// 创建多边形的几何对象
|
// 创建多边形的几何对象
|
||||||
let polygon;
|
let polygon;
|
||||||
if (type === 'Point') {
|
if (type === 'Point') {
|
||||||
@ -193,6 +227,14 @@ const creatPoint = (pointObj: Array<any>, type: string, id: string) => {
|
|||||||
fill: new Fill({
|
fill: new Fill({
|
||||||
color: 'red'
|
color: 'red'
|
||||||
})
|
})
|
||||||
|
}),
|
||||||
|
text: new Text({
|
||||||
|
font: '12px Microsoft YaHei',
|
||||||
|
text: name,
|
||||||
|
scale: 1,
|
||||||
|
fill: new Fill({
|
||||||
|
color: '#7bdd63'
|
||||||
|
})
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
const polygonStyle = new Style({
|
const polygonStyle = new Style({
|
||||||
@ -216,15 +258,68 @@ const creatPoint = (pointObj: Array<any>, type: string, id: string) => {
|
|||||||
map.addLayer(layerData[id]);
|
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(() => {
|
onMounted(() => {
|
||||||
// 地图初始化
|
// 地图初始化
|
||||||
initOLMap();
|
initOLMap();
|
||||||
});
|
creatPoint(
|
||||||
|
[
|
||||||
onDeactivated(() => {
|
[
|
||||||
console.log('地图销毁');
|
[107.13761912095511, 23.80479336386864],
|
||||||
|
[107.13776644838967, 23.804823038597075],
|
||||||
map.getOverlays().clear();
|
[107.13775276784109, 23.80486256412652],
|
||||||
|
[107.1376054403658, 23.80483288938397],
|
||||||
|
[107.13761912095511, 23.80479336386864]
|
||||||
|
]
|
||||||
|
],
|
||||||
|
'Polygon',
|
||||||
|
'1'
|
||||||
|
);
|
||||||
|
// creatPoint([107.13761560163239, 23.80480003743964], 'Point', '2', '点');
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@ -250,4 +345,7 @@ onDeactivated(() => {
|
|||||||
border-color: #79bbff;
|
border-color: #79bbff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
li {
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -1,59 +1,33 @@
|
|||||||
export class Snowflake {
|
export class SnowflakeIdGenerator {
|
||||||
private startTimeStamp: number = 1609459200000;
|
private readonly startTimeStamp = 1609459200000; // 起始时间戳,这里设置为 2021-01-01 00:00:00
|
||||||
private workerIdBits: number = 5;
|
private readonly workerIdBits = 5;
|
||||||
private dataCenterIdBits: number = 5;
|
private readonly dataCenterIdBits = 5;
|
||||||
private sequenceBits: number = 12;
|
private readonly sequenceBits = 12;
|
||||||
|
|
||||||
private maxWorkerId: number = -1 ^ (-1 << this.workerIdBits);
|
private readonly maxWorkerId = -1 ^ (-1 << this.workerIdBits);
|
||||||
private maxDataCenterId: number = -1 ^ (-1 << this.dataCenterIdBits);
|
private readonly maxDataCenterId = -1 ^ (-1 << this.dataCenterIdBits);
|
||||||
|
|
||||||
private workerIdShift: number = this.sequenceBits;
|
private readonly workerIdShift = this.sequenceBits;
|
||||||
private dataCenterIdShift: number = this.sequenceBits + this.workerIdBits;
|
private readonly dataCenterIdShift = this.sequenceBits + this.workerIdBits;
|
||||||
private timestampLeftShift: number = this.sequenceBits + this.workerIdBits + this.dataCenterIdBits;
|
private readonly timestampLeftShift = this.sequenceBits + this.workerIdBits + this.dataCenterIdBits;
|
||||||
private sequenceMask: number = -1 ^ (-1 << this.sequenceBits);
|
private readonly sequenceMask = -1 ^ (-1 << this.sequenceBits);
|
||||||
|
|
||||||
private workerId: number;
|
private workerId: number;
|
||||||
private dataCenterId: number;
|
private dataCenterId: number;
|
||||||
private sequence: number = 0;
|
private sequence = 0;
|
||||||
private lastTimestamp: number = -1;
|
private lastTimestamp = -1;
|
||||||
|
|
||||||
constructor(workerId: number, dataCenterId: number) {
|
constructor(workerId: number, dataCenterId: number) {
|
||||||
if (workerId > this.maxWorkerId || workerId < 0) {
|
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) {
|
if (dataCenterId > this.maxDataCenterId || dataCenterId < 0) {
|
||||||
throw new Error(`数据中心 ID 不能大于 ${this.maxDataCenterId} 或小于 0`);
|
throw new Error(`数据中心 ID 必须在 0 到 ${this.maxDataCenterId} 之间`);
|
||||||
}
|
}
|
||||||
this.workerId = workerId;
|
this.workerId = workerId;
|
||||||
this.dataCenterId = dataCenterId;
|
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 {
|
private waitNextMillis(lastTimestamp: number): number {
|
||||||
let timestamp = this.getCurrentTimestamp();
|
let timestamp = this.getCurrentTimestamp();
|
||||||
while (timestamp <= lastTimestamp) {
|
while (timestamp <= lastTimestamp) {
|
||||||
@ -61,11 +35,34 @@ export class Snowflake {
|
|||||||
}
|
}
|
||||||
return timestamp;
|
return timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getCurrentTimestamp(): number {
|
||||||
|
return Date.now();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 使用示例
|
nextId(): number {
|
||||||
// const workerId: number = 1;
|
let timestamp = this.getCurrentTimestamp();
|
||||||
// const dataCenterId: number = 1;
|
|
||||||
// const snowflake = new Snowflake(workerId, dataCenterId);
|
if (timestamp < this.lastTimestamp) {
|
||||||
// const id: number = snowflake.nextId();
|
throw new Error('时钟回拨,拒绝生成 ID 达 ' + (this.lastTimestamp - timestamp) + ' 毫秒');
|
||||||
// console.log('生成的唯一 ID:', 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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -261,7 +261,12 @@
|
|||||||
</el-dialog>
|
</el-dialog>
|
||||||
<!-- 选取方阵地址 -->
|
<!-- 选取方阵地址 -->
|
||||||
<el-dialog title="设置方阵" v-model="polygonStatus" width="1400px">
|
<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>
|
<template #footer>
|
||||||
<span>
|
<span>
|
||||||
<el-button @click="polygonStatus = false">取消</el-button>
|
<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>(
|
const { sys_normal_disable, project_category_type, project_type } = toRefs<any>(
|
||||||
proxy?.useDict('sys_normal_disable', 'project_category_type', 'project_type')
|
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 projectList = ref<ProjectVO[]>([]);
|
||||||
const buttonLoading = ref(false);
|
const buttonLoading = ref(false);
|
||||||
const loading = ref(true);
|
const loading = ref(true);
|
||||||
@ -297,6 +299,7 @@ const projectFormRef = ref<ElFormInstance>();
|
|||||||
const polygonStatus = ref(false);
|
const polygonStatus = ref(false);
|
||||||
const dxfFile = ref(null);
|
const dxfFile = ref(null);
|
||||||
const projectId = ref<string>('');
|
const projectId = ref<string>('');
|
||||||
|
const layerType = ref(null);
|
||||||
const dialog = reactive<DialogOption>({
|
const dialog = reactive<DialogOption>({
|
||||||
visible: false,
|
visible: false,
|
||||||
title: ''
|
title: ''
|
||||||
@ -460,7 +463,9 @@ const handleShowUpload = (row?: ProjectVO) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleOpenLayer = async (row: ProjectVO) => {
|
const handleOpenLayer = async (row: ProjectVO) => {
|
||||||
|
if (projectId.value == row.id) return (polygonStatus.value = true);
|
||||||
fullscreenLoading.value = true;
|
fullscreenLoading.value = true;
|
||||||
|
|
||||||
const res = await listDXFProject(row.designId);
|
const res = await listDXFProject(row.designId);
|
||||||
projectId.value = row.id;
|
projectId.value = row.id;
|
||||||
jsonData.value = res.data.layers;
|
jsonData.value = res.data.layers;
|
||||||
|
Reference in New Issue
Block a user