树形节点优化,对接上传接口

This commit is contained in:
Teo
2025-04-27 18:01:18 +08:00
parent c061111280
commit 9c37e7deec
3 changed files with 287 additions and 79 deletions

View File

@ -76,6 +76,42 @@ export const upLoadProjectDXF = (data: any) => {
}); });
}; };
/**
* 通过GeoJson新增设施-光伏板
* @param data
*/
export const addProjectFacilities = (data: any) => {
return request({
url: '/facility/photovoltaicPanel/geoJson',
method: 'post',
data: data
});
};
/**
* 通过GeoJson新增设施-光伏板桩点、立柱、支架
* @param data
*/
export const addProjectPilePoint = (data: any) => {
return request({
url: '/facility/photovoltaicPanelPoint/parts/geoJson',
method: 'post',
data: data
});
};
/**
* 通过GeoJson新增设施-方阵
* @param data
*/
export const addProjectSquare = (data: any) => {
return request({
url: '/facility/matrix/geoJson',
method: 'post',
data: data
});
};
/** /**
* 删除项目 * 删除项目
* @param id * @param id

View File

@ -1,13 +1,15 @@
<template> <template>
<div class="flex justify-between"> <div class="flex justify-between" v-loading="treeLoading">
<el-tree-v2 <el-tree-v2
style="width: 340px; overflow-y: auto" style="width: 340px; overflow: auto"
show-checkbox show-checkbox
:data="jsonData" :data="jsonData"
:height="500" :height="500"
@check-change="handleCheckChange" @check-change="handleCheckChange"
:props="treeProps" :props="treeProps"
@node-contextmenu="showMenu" @node-contextmenu="showMenu"
ref="treeRef"
@node-click="isMenuVisible = false"
> >
<template #default="{ node, data }"> <template #default="{ node, data }">
<span @dblclick="handlePosition(data)">{{ data.name }}</span> <span @dblclick="handlePosition(data)">{{ data.name }}</span>
@ -15,15 +17,28 @@
</el-tree-v2> </el-tree-v2>
<div> <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"> <div class="h15" v-if="!selectLayer.length"></div>
<p v-for="(item, index) in selectLayer" class="flex justify-between items-center cursor-pointer" @click="delLayer(index)"> <div class="m-0 c-white text-3 flex" v-else>
{{ item }} <p
v-for="(item, index) in selectLayer"
class="pl-xl border-rd pr mt-2 p-3 w-111 mr-1 bg-#909399 flex items-center cursor-pointer justify-between"
@click="delLayer(index)"
>
{{ item.location.name + '被选中为' + item.option }}
<el-icon> <el-icon>
<Close /> <Close />
</el-icon> </el-icon>
</p> </p>
</div> </div>
<el-form-item label="类型" class="items-center">
<el-radio-group v-model="layerType">
<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>
</el-form-item>
</div> </div>
<div v-if="isMenuVisible" :style="{ left: menuX + 'px', top: menuY + 'px' }" class="fixed bg-white shadow-md rounded-md overflow-hidden"> <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"> <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('光伏板')"> <li class="px-4 py-2 text-sm text-gray-700 hover:bg-gray-100 cursor-pointer" @click="handleMenuItemClick('光伏板')">
@ -41,6 +56,10 @@
</ul> </ul>
</div> </div>
</div> </div>
<div class="float-right">
<el-button @click="emit('close')">取消</el-button>
<el-button type="primary" @click="addFacilities" :loading="loading">确定</el-button>
</div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
@ -59,37 +78,47 @@ import { Circle, Style, Stroke, Fill, Icon, Text } from 'ol/style'; // OpenLayer
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 { SnowflakeIdGenerator } from '@/utils/snowflake'; import { TreeInstance } from 'element-plus';
import { addProjectFacilities, addProjectPilePoint, addProjectSquare, listDXFProject } from '@/api/project/project';
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
const props = defineProps({ const props = defineProps({
treeData: { projectId: String,
type: Array, designId: String
default: () => []
},
projectId: String
}); });
const treeData = ref<any>([]);
const layerType = ref(null);
const contextMenu = ref(null); const contextMenu = ref(null);
const selectLayer = ref([]); const selectLayer = ref([]);
const treeRef = ref<TreeInstance>();
const treeProps = { const treeProps = {
value: 'name' value: 'name'
}; };
const snowflake = new SnowflakeIdGenerator(1, 1); const loading = ref(false);
const treeLoading = ref(false);
const emit = defineEmits(['handleCheckChange', 'close']);
let map: any = null; let map: any = null;
const layerData = reactive<any>({}); const layerData = reactive<any>({});
const centerPosition = ref(fromLonLat([107.13761560163239, 23.80480003743964])); const centerPosition = ref(fromLonLat([107.13761560163239, 23.80480003743964]));
const nodeMenu = (e: any) => {
console.log(e);
};
const jsonData = computed(() => { const jsonData = computed(() => {
let id = 0; let id = 0;
props.treeData.forEach((item: any) => { let arr = [];
item.features.forEach((itm) => { treeData.value.forEach((item: any, index: any) => {
arr.push({
name: item.name,
index
});
for (const itm of item.features) {
if (itm.geometry.id) {
break;
}
itm.geometry.id = ++id; itm.geometry.id = ++id;
itm.geometry.coordinates = convertStrToNum(itm.geometry.coordinates); itm.geometry.coordinates = convertStrToNum(itm.geometry.coordinates);
}); }
}); });
return props.treeData; return arr; // treeData.value;
}); });
console.log(jsonData); console.log(jsonData);
@ -101,18 +130,20 @@ const handlePosition = (data: any) => {
map.getView().setCenter(centerPosition.value); map.getView().setCenter(centerPosition.value);
}; };
const handleCheckChange = (data: any, bool) => { const handleCheckChange = (data: any, bool) => {
let features = treeData.value[data.index].features;
if (isMenuVisible.value) isMenuVisible.value = false;
if (bool) { if (bool) {
if (!layerData[data.features[0].properties.id]) { if (!layerData[features[0].properties.id]) {
data.features.forEach((item: any) => { features.forEach((item: any) => {
creatPoint(item.geometry.coordinates, item.geometry.type, item.geometry.id, item.properties.text); creatPoint(item.geometry.coordinates, item.geometry.type, item.geometry.id, item.properties.text);
}); });
} else { } else {
data.features.forEach((item: any) => { features.forEach((item: any) => {
map.addLayer(layerData[item.geometry.id]); map.addLayer(layerData[item.geometry.id]);
}); });
} }
} else { } else {
data.features.forEach((item, index) => { features.forEach((item, index) => {
map.removeLayer(layerData[item.geometry.id]); map.removeLayer(layerData[item.geometry.id]);
}); });
} }
@ -167,23 +198,23 @@ function initOLMap() {
}); });
// 事件 // 事件
map.on('moveend', (e: any) => { // map.on('moveend', (e: any) => {
// console.log('地图移动', e); // // console.log('地图移动', e);
// 获取当前缩放级别 // // 获取当前缩放级别
var zoomLevel = map.getView().getZoom(); // var zoomLevel = map.getView().getZoom();
// console.log('当前缩放级别:', zoomLevel); // // console.log('当前缩放级别:', zoomLevel);
}); // });
map.on('rendercomplete', () => { // map.on('rendercomplete', () => {
// console.log('渲染完成'); // // console.log('渲染完成');
}); // });
map.on('click', (e: any) => { // map.on('click', (e: any) => {
var coordinate = e.coordinate; // var coordinate = e.coordinate;
// 将投影坐标转换为经纬度坐标 // // 将投影坐标转换为经纬度坐标
var lonLatCoordinate = toLonLat(coordinate); // var lonLatCoordinate = toLonLat(coordinate);
// 输出转换后的经纬度坐标 // // 输出转换后的经纬度坐标
console.log('经纬度坐标:', lonLatCoordinate); // console.log('经纬度坐标:', lonLatCoordinate);
}); // });
} }
//递归字符串数组变成数字 //递归字符串数组变成数字
@ -267,8 +298,8 @@ const menuY = ref(0);
// 显示菜单的方法 // 显示菜单的方法
const showMenu = (event: MouseEvent, data) => { const showMenu = (event: MouseEvent, data) => {
console.log(data); console.log('🚀 ~ showMenu ~ data:', data, treeData.value[data.index]);
contextMenu.value = data.name; contextMenu.value = data;
isMenuVisible.value = true; isMenuVisible.value = true;
menuX.value = event.clientX; menuX.value = event.clientX;
menuY.value = event.clientY; menuY.value = event.clientY;
@ -276,13 +307,19 @@ const showMenu = (event: MouseEvent, data) => {
// 处理菜单项点击事件的方法 // 处理菜单项点击事件的方法
const handleMenuItemClick = (option: string) => { const handleMenuItemClick = (option: string) => {
selectLayer.value.push(`${contextMenu.value}被选中为${option}`);
isMenuVisible.value = false; isMenuVisible.value = false;
if (selectLayer.value.length == 2) {
ElMessage.warning('最多只能选择两个图层');
return;
}
selectLayer.value.push({ location: contextMenu.value, option });
emit('handleCheckChange', selectLayer.value);
}; };
//删除菜单 //删除菜单
const delLayer = (index) => { const delLayer = (index) => {
selectLayer.value.splice(index, 1); selectLayer.value.splice(index, 1);
emit('handleCheckChange', selectLayer.value);
}; };
// 点击页面其他区域隐藏菜单 // 点击页面其他区域隐藏菜单
@ -297,29 +334,119 @@ const closeMenuOnClickOutside = (event: MouseEvent) => {
// 添加全局点击事件监听器 // 添加全局点击事件监听器
window.addEventListener('click', closeMenuOnClickOutside); window.addEventListener('click', closeMenuOnClickOutside);
const getTreeData = async () => {
treeLoading.value = true;
try {
const res = await listDXFProject(props.designId);
treeData.value = res.data.layers;
treeLoading.value = false;
} catch (err) {
treeLoading.value = false;
}
};
// 组件卸载时移除事件监听器 // 组件卸载时移除事件监听器
const onUnmounted = () => { const onUnmounted = () => {
window.removeEventListener('click', closeMenuOnClickOutside); window.removeEventListener('click', closeMenuOnClickOutside);
}; };
const addFacilities = async () => {
console.log(layerType.value);
if (!layerType.value) {
return proxy?.$modal.msgError('请选择图层类型');
}
if (!selectLayer.value.length) {
return proxy?.$modal.msgError('请选择需要上传的图层');
}
const data = {
projectId: props.projectId,
nameGeoJson: null,
locationGeoJson: null,
pointGeoJson: null
};
if (layerType.value == 1) {
if (!checkOptions(selectLayer.value, '光伏板')) {
return proxy?.$modal.msgError('请选择名称和光伏板');
}
loading.value = true;
if (selectLayer.value[0].option == '名称') {
data.nameGeoJson = treeData.value[selectLayer.value[0].location.index];
data.locationGeoJson = treeData.value[selectLayer.value[1].location.index];
} else {
data.nameGeoJson = treeData.value[selectLayer.value[1].location.index];
data.locationGeoJson = treeData.value[selectLayer.value[0].location.index];
}
await addProjectFacilities(data);
await proxy?.$modal.msgSuccess('添加成功');
} else if (layerType.value == 2) {
if (selectLayer.value.length > 1) return proxy?.$modal.msgError('最多选择一个桩点/支架');
if (selectLayer.value[0].option != '桩点/支架') return proxy?.$modal.msgError('请选择类型为桩点/支架');
loading.value = true;
data.pointGeoJson = treeData.value[selectLayer.value[0].location.index];
await addProjectPilePoint(data);
await proxy?.$modal.msgSuccess('添加成功');
} else if (layerType.value == 3) {
if (!checkOptions(selectLayer.value, '方阵')) {
return proxy?.$modal.msgError('请选择名称和方阵');
}
loading.value = true;
if (selectLayer.value[0].option == '名称') {
data.nameGeoJson = treeData.value[selectLayer.value[0].location.index];
data.locationGeoJson = treeData.value[selectLayer.value[1].location.index];
} else {
data.nameGeoJson = treeData.value[selectLayer.value[1].location.index];
data.locationGeoJson = treeData.value[selectLayer.value[0].location.index];
}
await addProjectSquare(data);
await proxy?.$modal.msgSuccess('添加成功');
}
reset();
loading.value = false;
};
const reset = () => {
selectLayer.value = [];
treeRef.value?.setCheckedKeys([]);
for (const key in layerData) {
map.removeLayer(layerData[key]);
}
layerType.value = null;
};
//校验
function checkOptions(arr, type) {
let hasName = false;
let hasJson = false;
for (let i = 0; i < arr.length; i++) {
if (arr[i].option === '名称') {
hasName = true;
} else if (arr[i].option === type) {
hasJson = true;
}
}
return hasName && hasJson;
}
watch(
() => props.designId,
(newId, oldId) => {
if (newId !== oldId) {
reset();
getTreeData();
}
},
{ immediate: true }
);
onMounted(() => { onMounted(() => {
// 地图初始化 // 地图初始化
initOLMap(); initOLMap();
creatPoint( console.log(props.designId);
[
[
[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> </script>
<style scoped lang="scss"> <style scoped lang="scss">

View File

@ -260,25 +260,28 @@
<amap height="620px" @setLocation="setPoi"></amap> <amap height="620px" @setLocation="setPoi"></amap>
</el-dialog> </el-dialog>
<!-- 选取方阵地址 --> <!-- 选取方阵地址 -->
<el-dialog title="设置方阵" v-model="polygonStatus" width="1400px"> <el-dialog title="设置方阵" v-model="polygonStatus" width="1400px" :close-on-click-modal="false">
<open-layers-map :tree-data="jsonData" :project-id="projectId" v-if="polygonStatus"></open-layers-map> <open-layers-map
<el-radio-group v-model="layerType" class="ml-100"> :project-id="projectId"
<el-radio value="1" size="large">光伏板</el-radio> :design-id="designId"
<el-radio value="2" size="large">桩点/支架</el-radio> @handleCheckChange="setCheckedNodes"
<el-radio value="3" size="large">方阵</el-radio> @close="polygonStatus = false"
</el-radio-group> ></open-layers-map>
<template #footer>
<span>
<el-button @click="polygonStatus = false">取消</el-button>
<el-button type="primary" @click="">确定</el-button>
</span>
</template>
</el-dialog> </el-dialog>
</div> </div>
</template> </template>
<script setup name="Project" lang="ts"> <script setup name="Project" lang="ts">
import { addProject, delProject, getProject, listDXFProject, listProject, updateProject } from '@/api/project/project'; import {
addProject,
addProjectFacilities,
addProjectPilePoint,
addProjectSquare,
delProject,
getProject,
listProject,
updateProject
} from '@/api/project/project';
import { ProjectForm, ProjectQuery, ProjectVO, locationType } from '@/api/project/project/types'; import { ProjectForm, ProjectQuery, ProjectVO, locationType } from '@/api/project/project/types';
import amap from '@/components/amap/index.vue'; import amap from '@/components/amap/index.vue';
const { proxy } = getCurrentInstance() as ComponentInternalInstance; const { proxy } = getCurrentInstance() as ComponentInternalInstance;
@ -299,7 +302,9 @@ 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 designId = ref<string>('');
//被选中的节点
const nodes = ref<any>([]);
const dialog = reactive<DialogOption>({ const dialog = reactive<DialogOption>({
visible: false, visible: false,
title: '' title: ''
@ -413,7 +418,52 @@ const setPoi = (location: locationType) => {
amapStatus.value = false; amapStatus.value = false;
}; };
//设置需要上传的节点
const setCheckedNodes = (nodeList: any) => {
nodes.value = nodeList;
};
//上传节点
// const addFacilities = async () => {
// if (!layerType.value) {
// return proxy?.$modal.msgError('请选择图层类型');
// }
// if (!nodes.value.length) {
// return proxy?.$modal.msgError('请选择需要上传的图层');
// }
// const data = {
// projectId: projectId.value,
// nameGeoJson: null,
// locationGeoJson: null,
// pointGeoJson: null
// };
// loading.value = true;
// if (layerType.value == 1) {
// if (nodes.value[0].option == '名称') {
// data.nameGeoJson = jsonData.value[nodes.value[0].location.index];
// data.locationGeoJson = jsonData.value[nodes.value[1].location.index];
// } else {
// data.nameGeoJson = jsonData.value[nodes.value[1].location.index];
// data.locationGeoJson = jsonData.value[nodes.value[0].location.index];
// }
// await addProjectFacilities(data);
// await proxy?.$modal.msgSuccess('添加成功');
// } else if (layerType.value == 2) {
// data.pointGeoJson = jsonData.value[nodes.value[0].location.index];
// await addProjectPilePoint(data);
// await proxy?.$modal.msgSuccess('添加成功');
// } else if (layerType.value == 3) {
// if (nodes.value[0].option == '名称') {
// data.nameGeoJson = jsonData.value[nodes.value[0].location.index];
// data.locationGeoJson = jsonData.value[nodes.value[1].location.index];
// } else {
// data.nameGeoJson = jsonData.value[nodes.value[1].location.index];
// data.locationGeoJson = jsonData.value[nodes.value[0].location.index];
// }
// await addProjectSquare(data);
// await proxy?.$modal.msgSuccess('添加成功');
// }
// loading.value = false;
// };
/** 搜索按钮操作 */ /** 搜索按钮操作 */
const handleQuery = () => { const handleQuery = () => {
queryParams.value.pageNum = 1; queryParams.value.pageNum = 1;
@ -463,14 +513,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;
const res = await listDXFProject(row.designId);
projectId.value = row.id;
jsonData.value = res.data.layers;
polygonStatus.value = true; polygonStatus.value = true;
fullscreenLoading.value = false; projectId.value = row.id;
designId.value = row.designId;
}; };
const updateProjectFile = async () => { const updateProjectFile = async () => {