<template>
|
<div class="gridManagement">
|
<div class="search-box">
|
<el-form :model="params" inline>
|
<div style="display: flex;justify-content: space-between">
|
<div>
|
<el-form-item label="网格名称:">
|
<el-input v-model="params.gridName" placeholder="请输入网格名称" clearable />
|
</el-form-item>
|
</div>
|
<div>
|
<el-form-item>
|
<el-button type="primary" @click="getList">搜索</el-button>
|
<el-button @click="cancelSearch">清空</el-button>
|
</el-form-item>
|
</div>
|
</div>
|
</el-form>
|
<div>
|
<el-button type="primary" icon="el-icon-plus" @click="handleAdd">新增网格</el-button>
|
</div>
|
</div>
|
<div class="mange-table">
|
<el-table border :data="tableList" class="custom-header">
|
<el-table-column label="序号" type="index" width="60"></el-table-column>
|
<el-table-column prop="grid_name" label="网格名称" align="center" show-overflow-tooltip></el-table-column>
|
<el-table-column prop="start_point" label="起点" align="center" show-overflow-tooltip></el-table-column>
|
<el-table-column prop="end_point" label="终点" align="center" show-overflow-tooltip></el-table-column>
|
<el-table-column prop="create_time" label="创建时间" align="center"></el-table-column>
|
<el-table-column prop="nick_name" label="创建人" align="center"></el-table-column>
|
<el-table-column label="操作" width="180" align="center">
|
<template #default="scope">
|
<!-- <el-button icon="el-icon-view" type="text" @click="handleDetail(scope.row)">查看</el-button> -->
|
<el-button icon="el-icon-edit" type="text" @click="handleEdit(scope.row)">编辑</el-button>
|
<el-button icon="el-icon-delete" type="text" @click="handleDelete(scope.row)">删除</el-button>
|
</template>
|
</el-table-column>
|
</el-table>
|
</div>
|
<div class="pagination">
|
<el-pagination class="ztzf-pagination" popper-class="custom-pagination-dropdown" background
|
:page-sizes="[10, 20, 30, 40, 50, 100]" :size="size" v-model:current-page="params.current"
|
v-model:page-size="params.size" layout="total, sizes, prev, pager, next, jumper" :total="total"
|
@size-change="handleSizeChange" @current-change="handleCurrentChange" />
|
</div>
|
</div>
|
<el-dialog class="ztzf-dialog" append-to-body v-model="isShowEditView" :title="titleTxt"
|
:width="pxToRem(1000)" :close-on-click-modal="false" :destroy-on-close="true" @close="handleClose">
|
<div class="content-edit">
|
<el-form ref="ruleFormRef" :model="editParams" :rules="rules" inline>
|
<el-form-item label="网格名称" prop="grid_name">
|
<el-input v-model="editParams.grid_name" />
|
</el-form-item>
|
</el-form>
|
<div class="map-container">
|
<div id="GridManagementMap" class="ztzf-cesium"></div>
|
</div>
|
<div class="btns">
|
<el-button type="primary" @click="meshAnalysis"><el-icon><Location /></el-icon>开启网格</el-button>
|
<el-button type="primary" @click="clearGrid"><el-icon><DeleteLocation /></el-icon>清除网格</el-button>
|
<el-button v-if="titleTxt === '新增'" type="primary" @click="submit(ruleFormRef)"><el-icon><CirclePlus /></el-icon>确认</el-button>
|
<el-button v-else type="primary" @click="submit(ruleFormRef)"><el-icon><CircleCheck /></el-icon>修改</el-button>
|
<el-button @click="isShowEditView = false"><el-icon><CircleClose /></el-icon>取消</el-button>
|
</div>
|
<div v-show="showContextMenu" class="context-menu" :style="contextMenuStyle">
|
<div class="menu-item" @click="clearAllPoints">清空网格</div>
|
</div>
|
</div>
|
</el-dialog>
|
</template>
|
<script setup>
|
import { airGridPage, airGridUpdate, airGridAdd, airGridDelete } from '@/api/airspace/airspace';
|
import { ElMessage, ElMessageBox, ElLoading } from 'element-plus';
|
import * as Cesium from 'cesium';
|
import { PublicCesium } from '@/utils/cesium/publicCesium'
|
import OccupancyGrid from './GridSettings/OccupancyGrid'
|
import PathPlanning from './GridSettings/PathPlanning'
|
import newStartPoint from '@/assets/images/newStartPoint.png'
|
import { cartesian3Convert,getLnglatAltitude } from '@/utils/cesium/mapUtil'
|
import { ArrowLineMaterialProperty } from '@/utils/cesium/Material'
|
import { getPolyLine } from '@/views/RoutePlan/PointAirLine/pointWayLineUtils'
|
|
let arrowLineMaterialProperty = new ArrowLineMaterialProperty({
|
color: new Cesium.Color(128 / 255, 215 / 255, 255 / 255, 1),
|
directionColor: new Cesium.Color(1, 1, 1, 1),
|
outlineColor: new Cesium.Color(1, 1, 1, 1),
|
outlineWidth: 0,
|
speed: 5,
|
})
|
|
const total = ref(0)
|
const params = ref({
|
current: 1,
|
size: 10,
|
gridName: '',
|
});
|
|
let titleTxt = ref('新增')
|
let tableList = ref([])
|
|
let isShowView = ref(false)
|
|
let rowView = ref({})
|
|
const showContextMenu = ref(false)
|
const contextMenuStyle = ref({
|
left: '0px',
|
top: '0px',
|
})
|
|
let isShowEditView = ref(false)
|
|
const ruleFormRef = ref()
|
let editParams = ref({
|
id: '',
|
grid_name: '',
|
start_point: '',
|
end_point: '',
|
})
|
const rules = reactive({
|
grid_name: [
|
{
|
required: true,
|
message: '请输入网格名称',
|
trigger: 'blur',
|
},
|
],
|
})
|
|
function cancelSearch() {
|
params.value = {
|
id: '',
|
gridName: '',
|
}
|
params.value.current = 1
|
getList()
|
}
|
|
function getList() {
|
airGridPage(params.value).then(res => {
|
tableList.value = res.data.data.records || []
|
total.value = res.data.data.total || 0
|
})
|
}
|
|
function handleDelete (row) {
|
ElMessageBox.confirm('确定删除吗?', '提示', {
|
confirmButtonText: '确定',
|
cancelButtonText: '取消',
|
type: 'warning',
|
})
|
.then(() => {
|
airGridDelete(row.id).then(res => {
|
ElMessage.success('删除成功')
|
getList()
|
}).catch(error => {
|
ElMessage.error('删除失败');
|
});
|
})
|
.catch(() => {
|
ElMessage({
|
type: 'info',
|
message: '已取消删除',
|
})
|
});
|
}
|
async function handleAdd() {
|
titleTxt.value = '新增'
|
editParams.value.grid_name = ''
|
isShowEditView.value = true
|
await nextTick()
|
initMap()
|
}
|
|
async function handleEdit(row) {
|
titleTxt.value = '编辑'
|
isShowEditView.value = true
|
editParams.value = { ...row }
|
const startPoint = row.start_point.split(',')
|
const endPoint = row.end_point.split(',')
|
const height = row.grid_height.split(',')
|
pointList.value.push({
|
longitude: Number(startPoint[0]),
|
latitude: Number(startPoint[1]),
|
height: Number(height[0]),
|
})
|
pointList.value.push({
|
longitude: Number(endPoint[0]),
|
latitude: Number(endPoint[1]),
|
height: Number(height[0]),
|
})
|
await nextTick()
|
initMap()
|
}
|
|
function handleSizeChange(val) {
|
params.value.size = val
|
getList()
|
}
|
|
function handleCurrentChange(val) {
|
params.value.current = val
|
getList()
|
}
|
|
async function submit(formValidate) {
|
if (!formValidate) return
|
await formValidate.validate((valid, fields) => {
|
if (valid) {
|
if (pointList.value.length === 0) {
|
ElMessage.warning('请先添加起点和终点')
|
return
|
}
|
const params = {
|
id: editParams.value.id,
|
grid_name: editParams.value.grid_name,
|
start_point: `${pointList.value[0].longitude},${pointList.value[0].latitude}`,
|
end_point: `${pointList.value[1].longitude},${pointList.value[1].latitude}`,
|
grid_height: `${pointList.value[0].height},${pointList.value[1].height}`,
|
}
|
if (titleTxt.value === '新增') {
|
airGridAdd(params).then(res => {
|
isShowEditView.value = false
|
ElMessage.success('新增成功')
|
getList()
|
})
|
} else {
|
airGridUpdate(params).then(res => {
|
isShowEditView.value = false
|
ElMessage.success('操作成功')
|
getList()
|
})
|
}
|
|
}
|
})
|
}
|
|
let publicCesiumInstance = null
|
let viewer = null
|
const viewInstance = shallowRef(null);
|
let handler = null
|
let occupancyGrid = null
|
// 地图
|
const initMap = async () => {
|
if (!document.getElementById('GridManagementMap')) {
|
return
|
}
|
publicCesiumInstance = new PublicCesium({
|
dom: 'GridManagementMap',
|
flyToContour: false,
|
flatMode: false,
|
terrain: true,
|
layerMode: 4,
|
contour: true,
|
})
|
viewer = publicCesiumInstance.getViewer()
|
viewInstance.value = publicCesiumInstance;
|
viewer.scene.globe.depthTestAgainstTerrain = true;
|
|
// viewer.flyTo({latitude: 28.624613214568868,longitude:115.85669891599704}, 4, 65)
|
viewer?.camera.flyTo({
|
destination: Cesium.Cartesian3.fromDegrees(115.85669891599704, 28.624613214568868, 1000),
|
duration: 1,
|
orientation: {
|
heading: Cesium.Math.toRadians(0.0),
|
pitch: Cesium.Math.toRadians(-90.0),
|
roll: 0.0,
|
},
|
});
|
addCustomLayers(tilesTreeData.value.title, tilesTreeData.value)
|
occupancyGrid = new OccupancyGrid(viewer, gridConfig, gridParams)
|
|
// 添加事件
|
handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas)
|
handler.setInputAction(singleClickEvent, Cesium.ScreenSpaceEventType.LEFT_CLICK)
|
handler.setInputAction(click => {
|
// 获取鼠标在页面上的坐标
|
const mousePosition = {
|
x: event.clientX,
|
y: event.clientY,
|
}
|
|
showContextMenu.value = true
|
contextMenuStyle.value = {
|
left: mousePosition.x + 'px',
|
top: mousePosition.y + 'px',
|
position: 'fixed', // 确保使用固定定位
|
zIndex: 9999, // 确保菜单显示在最上层
|
}
|
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
|
cesiumContextMenu(false)
|
if (titleTxt.value === '编辑') {
|
ShowTwoPoints()
|
// meshAnalysis()
|
}
|
}
|
|
// 倾斜摄影
|
const tilesTreeData = ref(
|
{
|
id: '1',
|
label: '空间大厦',
|
type: 'layer-map',
|
title: 'kjds_3Dtile',
|
mapType: 'Cesium3DTile',
|
src: '/3Dtile/kjds/tileset.json',
|
},
|
)
|
|
const gridParams = reactive({
|
hasGrid: false, //生成了网格
|
hasRoute: false, //生成了航线
|
showIdle: true, //显示占用网格
|
showOccupancy: true, //显示空闲网格
|
})
|
|
// 初始化占用网格 - 使用自定义配置
|
const gridConfig = {
|
// 网格尺寸配置
|
gridSize: 6,
|
|
gridWidth: 12,
|
gridHeight: 12,
|
gridDepth: 12,
|
|
// 区域扩展配置(确保4层网格生成)
|
heightExtension: 72, // 向上向下各扩展120米,确保4层网格(总高度240米,4×50=200米,留有余量)
|
widthExtension: 12, // 水平方向扩展100米
|
|
// 颜色配置
|
occupiedColor: Cesium.Color.RED.withAlpha(0.05),
|
freeColor: Cesium.Color.GREEN.withAlpha(0.05),
|
occupiedOutlineColor: Cesium.Color.DARKRED,
|
freeOutlineColor: Cesium.Color.DARKGREEN,
|
outlineWidth: 1,
|
|
// 性能配置100000
|
maxGridCount: 500000,
|
enableBatching: true,
|
|
// 显示配置
|
showOccupiedOnly: false,
|
showFreeOnly: false,
|
enableLogging: true,
|
}
|
|
const addCustomLayers = async (layerName, options) => {
|
if (options.mapType === 'arcgis') {
|
let imageryProvider = new Cesium.WebMapTileServiceImageryProvider({
|
url: options.src,
|
layer: options.title,
|
style: 'default',
|
format: 'image/png',
|
tileMatrixSetID: 'default',
|
tilingScheme: new Cesium.GeographicTilingScheme(),
|
tileMatrixLabels: [
|
'0',
|
'1',
|
'2',
|
'3',
|
'4',
|
'5',
|
'6',
|
'7',
|
'8',
|
'9',
|
'10',
|
'11',
|
'12',
|
'13',
|
'14',
|
'15',
|
'16',
|
'17',
|
'18',
|
'19',
|
],
|
})
|
|
viewer?.imageryLayers.addImageryProvider(imageryProvider)
|
}
|
|
if (options.mapType === 'Cesium3DTile') {
|
const tileset = await Cesium.Cesium3DTileset.fromUrl(options.src)
|
|
viewer?.scene.primitives.add(tileset)
|
}
|
}
|
let pointList = ref([])
|
async function singleClickEvent(movement) {
|
cesiumContextMenu(false)
|
if (pointList.value.length === 2) {
|
ElMessage.warning('最多只能添加两个点,可以先清空')
|
return
|
}
|
// 先清除point-
|
viewer.entities.removeAll()
|
const { longitude, latitude } = cartesian3Convert(viewer.scene.pickPosition(movement.position), viewer)
|
let result = await getLnglatAltitude(longitude, latitude, viewer)
|
let height = result.height + 2
|
const point = { longitude, latitude, height }
|
pointList.value.push(point)
|
ShowTwoPoints()
|
}
|
|
// 生成两个点
|
function ShowTwoPoints() {
|
pointList.value.map((item, index) => {
|
const position = Cesium.Cartesian3.fromDegrees(
|
Number(item.longitude),
|
Number(item.latitude),
|
Number(item.height),
|
)
|
viewer.entities.add({
|
id: `point-${index}`,
|
position: position,
|
point: {
|
pixelSize: 10,
|
color: Cesium.Color.fromCssColorString('#FFFFFF'),
|
clampToGround: true,
|
heightReference: Cesium.HeightReference.NONE,
|
},
|
})
|
// viewer?.entities.add({
|
// id: 'placement-' + index,
|
// position: position,
|
// polyline: getPolyLine(viewer, { value: pointList.value }, index),
|
// })
|
})
|
if (pointList.value.length < 2) return
|
const position1 = Cesium.Cartesian3.fromDegrees(
|
Number(pointList.value[0].longitude),
|
Number(pointList.value[0].latitude),
|
Number(pointList.value[0].height)
|
)
|
const position2= Cesium.Cartesian3.fromDegrees(
|
Number(pointList.value[1].longitude),
|
Number(pointList.value[1].latitude),
|
Number(pointList.value[1].height)
|
)
|
|
console.log(position1, position2)
|
|
const carto1 = Cesium.Cartographic.fromCartesian(position1);
|
const carto2 = Cesium.Cartographic.fromCartesian(position2);
|
|
const lon1 = Cesium.Math.toDegrees(carto1.longitude);
|
const lat1 = Cesium.Math.toDegrees(carto1.latitude);
|
const lon2 = Cesium.Math.toDegrees(carto2.longitude);
|
const lat2 = Cesium.Math.toDegrees(carto2.latitude);
|
|
const west = Math.min(lon1, lon2);
|
const east = Math.max(lon1, lon2);
|
const south = Math.min(lat1, lat2);
|
const north = Math.max(lat1, lat2);
|
|
viewer.entities.add({
|
rectangle: {
|
// disableDepthTestDistance: Number.POSITIVE_INFINITY,
|
coordinates: Cesium.Rectangle.fromDegrees(west, south, east, north),
|
material: Cesium.Color.RED.withAlpha(0.5),
|
outline: true,
|
outlineColor: Cesium.Color.RED,
|
outlineWidth: 2,
|
height: pointList.value[1].height,
|
extrudedHeight: pointList.value[1].height,
|
heightReference: Cesium.HeightReference.NONE,
|
}
|
});
|
// viewer.entities.add({
|
// id: `line`,
|
// polyline: {
|
// width: 2,
|
// eyeOffset: new Cesium.Cartesian3(0, 0, -6),
|
// material: arrowLineMaterialProperty,
|
// clampToGround: false,
|
// positions: [position1, position2],
|
// },
|
// })
|
}
|
|
// 禁用浏览器默认行为处理
|
const preventDefault = event => {
|
event.preventDefault()
|
return
|
}
|
|
const cesiumContextMenu = (isAdd = true) => {
|
let cesium = document.getElementById('GridManagementMap')
|
if (!cesium) return
|
if (isAdd) {
|
cesium.addEventListener('contextmenu', preventDefault)
|
} else {
|
cesium.removeEventListener('contextmenu', preventDefault)
|
}
|
}
|
|
// 生成网格
|
function meshAnalysis() {
|
if (pointList.value.length !== 2) {
|
ElMessage.warning('请先添加起点和终点')
|
return
|
}
|
const pointList1 = pointList.value.map(i => {
|
const cartographic = Cesium.Cartographic.fromDegrees(i.longitude, i.latitude, i.height)
|
const { x, y, z } = Cesium.Cartesian3.fromRadians(
|
cartographic.longitude,
|
cartographic.latitude,
|
cartographic.height
|
)
|
return { ...i, position: { x, y, z } }
|
})
|
window.uavApp = {
|
waypoints: pointList1,
|
}
|
occupancyGrid.generateOccupancyGridWithTiles(pointList1)
|
}
|
// 清除网格
|
function clearGrid() {
|
occupancyGrid.clearGrid()
|
gridParams.hasGrid = false
|
}
|
|
const handleClose = () => {
|
// 清除editParams
|
editParams.value = {
|
id: '',
|
grid_name: '',
|
grid_height: '',
|
start_point: '',
|
end_point: '',
|
}
|
pointList.value = []
|
publicCesiumInstance?.viewerDestroy()
|
publicCesiumInstance = null
|
viewer = null
|
isShowEditView.value = false
|
viewer?.scene.primitives.remove(tilesTreeData.value.title)
|
cesiumContextMenu(false)
|
}
|
|
function clearAllPoints() {
|
showContextMenu.value = false
|
pointList.value = []
|
viewer?.entities.removeAll()
|
clearGrid()
|
}
|
|
onMounted(() => {
|
getList()
|
})
|
</script>
|
|
<style lang="scss" scoped>
|
.gridManagement {
|
height: 0;
|
flex: 1;
|
padding: 20px;
|
margin: 0 10px 10px 10px;
|
background-color: #ffffff;
|
// padding: 10px 20px;
|
border-radius: 5px;
|
display: flex;
|
flex-direction: column;
|
.search-box {
|
margin-top: 20px;
|
height: 80px;
|
}
|
|
:deep(.el-input) {
|
.el-input__wrapper {
|
width: 200px;
|
}
|
}
|
|
// 表格
|
.mange-table {
|
height: 0;
|
flex: 1;
|
margin-top: 18px;
|
overflow: auto;
|
}
|
:deep(.el-pagination) {
|
display: flex;
|
justify-content: right;
|
}
|
|
:deep(.el-pagination button) {
|
background: center center no-repeat none !important;
|
color: #8eb8ea !important;
|
}
|
}
|
.content-edit {
|
height: 550px;
|
.map-container {
|
height: 440px;
|
|
#GridManagementMap {
|
width: 100%;
|
height: 100%;
|
}
|
}
|
.btns {
|
margin-top: 16px;
|
display: flex;
|
justify-content: center;
|
}
|
.context-menu {
|
position: fixed;
|
background: #0f1929;
|
border: 1px solid #51a8ff;
|
border-radius: 4px;
|
padding: 4px 0;
|
z-index: 9999;
|
min-width: 100px; // 设置最小宽度
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.3);
|
|
.menu-item {
|
padding: 8px 16px;
|
color: #d3e9ff;
|
cursor: pointer;
|
|
&:hover {
|
background: rgba(81, 168, 255, 0.1);
|
}
|
}
|
}
|
}
|
</style>
|