完整代码
<template>
<div class="my_mark">
<div class="mark_tool">
<div class="tool_e">
<div class="tool_b" @click="handleAddPoint">
<el-tooltip content="添加标记" effect="customized">
<el-icon size="22">
<Edit />
</el-icon>
</el-tooltip>
</div>
</div>
<div class="tool_d">
<div class="tool_b" @click="removeAllPoints">
<el-tooltip content="清空标记" effect="customized">
<el-icon size="22">
<Delete />
</el-icon>
</el-tooltip>
</div>
<!-- <div
class="tool_b"
@click="
() => {
isEdit = !isEdit;
}
"
>
</div>
</div>
<div style="margin-top: 10px">
<el-table
:data="popupInfo"
border
:default-sort="{ prop: 'name', order: 'descending' }"
style="width: 100%"
class="table"
@row-click="handleRow"
>
<el-table-column prop="name" label="名称" width="180" sortable />
<el-table-column label="操作" align="center">
<template #default="scope">
<el-icon size="16" @click="deletePoint(scope.row)">
<Delete />
</el-icon>
</template>
</el-table-column>
</el-table>
</div>
</div>
<!-- 标记弹框 -->
<div
class="popover"
ref="popup"
:style="{ left: popupLeft, top: popupTop, display: popupDisplay }"
>
<div class="header">
<div @click="closePopup">
<el-icon>
<CloseBold />
</el-icon>
</div>
<div class="font">我的标记</div>
</div>
<div class="container">
<el-row>
<el-col :span="4">名称:</el-col>
<el-col :span="20">
<el-input v-model="pointInfo.name" placeholder="请输入" clearable />
</el-col>
</el-row>
<el-row>
<el-col :span="4">备注:</el-col>
<el-col :span="20">
<el-input
v-model="pointInfo.remark"
placeholder="请输入备注"
style="height: 50px"
clearable
/>
</el-col>
</el-row>
</div>
<div class="btn">
<el-button type="primary" plain @click="handleSave">确定</el-button>
</div>
</div>
<!-- 鼠标移入展示内容 -->
<div
class="Mouse"
ref="tooltip"
:style="{ left: tooltipLeft, top: tooltipTop, display: tooltipDisplay }"
>
拖动该点后修改位置
</div>
</template>
<script setup lang="ts">
import { ref, inject, watch, onUnmounted } from 'vue';
import {
Viewer,
ScreenSpaceEventHandler,
Cartographic,
Math,
ScreenSpaceEventType,
Cartesian2,
Entity,
Cartesian3,
defined,
Color,
ConstantPositionProperty
} from 'cesium';
import SvgIcon from '../icons/SvgIcon.vue';
import { point } from '@turf/helpers';
const viewer: Viewer = inject('viewer')!;
// 是否可编辑
// const isEdit = ref(true);
// 存储绘制的点
const pointsEntity = ref<any>([]);
// 屏幕事件
const handlerPoint = ref();
//标记点的信息
const pointInfo = ref({
name: '我的标记点',
remark: ''
});
//弹窗控制
const popup = ref();
const popupDisplay = ref('none');
const popupLeft = ref('0px');
const popupTop = ref('0px');
const popupInfo = ref<
{
name: string;
remark: string;
id: string;
}[]
>([]);
const idNum = ref(0);
//提示位置
const tooltip = ref();
const tooltipDisplay = ref('none');
const tooltipLeft = ref('0px');
const tooltipTop = ref('0px');
/**
* 添加点
*/
const handleAddPoint = () => {
// 设置鼠标样式为crosshair
document.body.style.cursor = 'crosshair';
handlerPoint.value = new ScreenSpaceEventHandler(viewer.canvas);
handlerPoint.value.setInputAction(function (click: { position: Cartesian2 }) {
const cartesian = viewer.camera.pickEllipsoid(click.position, viewer.scene.globe.ellipsoid);
if (cartesian) {
const cartographic = Cartographic.fromCartesian(cartesian);
const longitude = Math.toDegrees(cartographic.longitude);
const latitude = Math.toDegrees(cartographic.latitude);
if (pointsEntity.value.length != 0) {
idNum.value++;
}
const point = viewer.entities.add({
position: cartesian,
id: 'point' + idNum.value,
billboard: {
image: 'src/assets/img/icons/position.png', // 替换为你的图标路径
width: 32,
height: 32
}
});
pointsEntity.value.push(point);
showPopup(longitude, latitude);
}
}, ScreenSpaceEventType.LEFT_CLICK);
};
/**
* 弹窗的展示函数
* @param longitude
* @param latitude
*
*/
function showPopup(longitude: number, latitude: number) {
const position = Cartesian3.fromDegrees(longitude, latitude);
const canvasPosition = viewer.scene.cartesianToCanvasCoordinates(position, new Cartesian2());
if (canvasPosition && popup.value) {
popupLeft.value = canvasPosition.x - popup.value?.offsetWidth / 2 - 140 - 1632 + 'px';
popupTop.value = canvasPosition.y - 97 - 170 + 'px';
popupDisplay.value = 'block';
}
// 添加操作完成后恢复鼠标样式为默认箭头
document.body.style.cursor = 'default';
}
/**
* 关闭弹窗
*/
function closePopup() {
popupDisplay.value = 'none';
}
//是否编辑点
const isPointEdit = ref(false);
//编辑的index
const pointEditIndex = ref(-1);
/**
* 弹窗确认函数
*/
const handleSave = () => {
let index: number = -1;
popupDisplay.value = 'none';
if (isPointEdit.value) {
popupInfo.value[pointEditIndex.value].name = pointInfo.value.name;
popupInfo.value[pointEditIndex.value].remark = pointInfo.value.remark;
index = pointsEntity.value.findIndex(
(point: { id: any }) => point.id === popupInfo.value[pointEditIndex.value].id
);
} else {
popupInfo.value.push({ ...pointInfo.value, id: 'point' + idNum.value });
index = pointsEntity.value.findIndex(
(point: { id: any }) => point.id === 'point' + idNum.value
);
}
pointsEntity.value[index].label = {
text: pointInfo.value.name,
pixelOffset: new Cartesian2(0, -30),
show: true,
font: '18px sans-serif', // 设置字体大小
fillColor: Color.AQUA // 设置文本颜色
};
};
// 创建第二个点击事件处理器:点击已经添加的点执行的内容
const handler2 = new ScreenSpaceEventHandler(viewer.scene.canvas);
handler2.setInputAction(function (event: { position: Cartesian2 }) {
const pick = viewer.scene.pick(event.position);
if (pick && pick.id) {
const index = popupInfo.value.findIndex((point: { id: any }) => point.id === pick.id._id);
if (index != -1) {
const cartographic = Cartographic.fromCartesian(pick.primitive._actualPosition);
const longitude = Math.toDegrees(cartographic.longitude);
const latitude = Math.toDegrees(cartographic.latitude);
pointInfo.value = popupInfo.value[index];
pointEditIndex.value = index;
isPointEdit.value = true;
showPopup(longitude, latitude);
}
}
}, ScreenSpaceEventType.LEFT_CLICK);
/**
* 鼠标移入移出实体后提示的显示与隐藏
*/
handler2.setInputAction(function (movement: { endPosition: Cartesian2 }) {
const pick = viewer.scene.pick(movement.endPosition);
if (defined(pick) && defined(pick.id)) {
const index = pointsEntity.value.findIndex((point: { id: any }) => point.id === pick.id._id);
if (index != -1) {
const cartesian = viewer.scene.pickPosition(movement.endPosition);
if (defined(cartesian)) {
const cartographic = Cartographic.fromCartesian(cartesian);
const longitude = Math.toDegrees(cartographic.longitude);
const latitude = Math.toDegrees(cartographic.latitude);
const position = Cartesian3.fromDegrees(longitude, latitude);
const canvasPosition = viewer.scene.cartesianToCanvasCoordinates(
position,
new Cartesian2()
);
if (canvasPosition && tooltip.value) {
tooltipLeft.value = canvasPosition.x - tooltip.value?.offsetWidth / 2 - 1517 + 'px';
tooltipTop.value = canvasPosition.y - 59 + 'px';
tooltipDisplay.value = 'block';
}
}
}
} else {
tooltipDisplay.value = 'none';
}
}, ScreenSpaceEventType.MOUSE_MOVE);
// 创建第三个点击事件处理器:拖拽功能
const dragHandler = new ScreenSpaceEventHandler(viewer.scene.canvas);
dragHandler.setInputAction(function (event: { position: Cartesian2 }) {
const pickInfo = viewer.scene.pick(event.position);
if (!pickInfo) {
return;
}
// 如果点击空白区域,则不往下执行
// 将相机锁定,不然后续移动实体时相机也会动
viewer.scene.screenSpaceCameraController.enableRotate = false;
// 为viewer绑定MOUSE_MOVE事件监听器(执行函数,监听的事件)
dragHandler.setInputAction((movement) => {
// arg有startPosition与endPosition两个属性,即移动前后的位置信息:Cartesian2对象
const cartesian = viewer.scene.globe.pick(
viewer.camera.getPickRay(movement.endPosition)!,
viewer.scene
); //将Cartesian2转为Cartesian3
const selectedEntity = viewer.entities.getById(pickInfo.id._id); // 实体
if (!selectedEntity) {
return false;
}
//注意需要赋值三维坐标
if (cartesian) {
selectedEntity.position = new ConstantPositionProperty(cartesian);
}
// 更新实体位置为当前鼠标位置
}, ScreenSpaceEventType.MOUSE_MOVE);
//为viewer绑定LEFT_UP事件监听器(执行函数,监听的事件)
dragHandler.setInputAction(() => {
dragHandler.removeInputAction(ScreenSpaceEventType.LEFT_UP);
dragHandler.removeInputAction(ScreenSpaceEventType.MOUSE_MOVE);
viewer.scene.screenSpaceCameraController.enableRotate = true; // 取消相机锁定
}, ScreenSpaceEventType.LEFT_UP);
}, ScreenSpaceEventType.LEFT_DOWN);
/**
* 清空所有标点
*/
const removeAllPoints = () => {
popupDisplay.value = 'none';
pointsEntity.value.forEach(function (point: Entity) {
viewer.entities.remove(point);
});
pointsEntity.value.length = 0;
};
/**
* 根据点的id删除点
* @param row
*/
const deletePoint = (row: { id: string }) => {
const index = popupInfo.value.findIndex((point: { id: any }) => point.id === row.id);
popupInfo.value.splice(index, 1);
const entityIndex = pointsEntity.value.findIndex((point: { id: any }) => point.id === row.id);
if (entityIndex !== -1) {
pointsEntity.value.splice(entityIndex, 1);
viewer.entities.removeById(row.id);
}
};
/**
* 表格点击事件
* @param row
*/
const handleRow = (row: { id: string }) => {
const entity = pointsEntity.value.find((point: { id: string }) => point.id === row.id);
if (entity) {
const option = Cartographic.fromCartesian(entity._position._value);
const longitude = Math.toDegrees(option.longitude);
const latitude = Math.toDegrees(option.latitude);
const height = Cartographic.fromCartesian(viewer.camera.position).height;
viewer?.camera.flyTo({
destination: Cartesian3.fromDegrees(Number(longitude), Number(latitude), height),
duration: 2
});
}
};
/**
* 监听点数组的变化添加点后移除鼠标左击事件
*/
watch(
() => pointsEntity,
() => {
handlerPoint.value.removeInputAction(ScreenSpaceEventType.LEFT_CLICK);
},
{
deep: true
}
);
onUnmounted(() => {
dragHandler.removeInputAction(ScreenSpaceEventType.LEFT_DOWN);
handler2.removeInputAction(ScreenSpaceEventType.MOUSE_MOVE);
handler2.removeInputAction(ScreenSpaceEventType.LEFT_CLICK);
});
</script>
<style lang="scss" scoped>
.my_mark {
width: 261px;
min-height: 186px;
overflow: hidden;
font-size: 14px;
.mark_tool {
display: flex;
justify-content: space-around;
align-items: center;
margin-top: 10px;
span {
font-weight: 600;
}
.tool_e {
// width: 16%;
width: 20%;
text-align: center;
}
.tool_b {
width: 30px;
height: 30px;
border: 1px solid #fff;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.tool_f {
display: flex;
width: 46%;
justify-content: space-around;
align-items: center;
}
.tool_d {
display: flex;
// width: 32%;
width: 72%;
// justify-content: space-around;
align-items: center;
}
}
}
.popover {
position: absolute;
width: 310px;
padding: 2px 10px 10px;
color: #fff;
background-color: #3c4f7f;
border: 1px solid #00effe;
.header {
div {
text-align: right;
}
.font {
text-align: left;
font-size: 18px;
margin-bottom: 6px;
font-weight: 600;
}
border-bottom: 1px solid #616161;
}
.container {
margin-top: 10px;
font-size: 16px;
.el-row {
margin-bottom: 10px;
}
}
.btn {
text-align: center;
}
}
.Mouse {
position: absolute;
padding: 4px;
font-size: 12px;
color: #fff;
background-color: rgba(0, 0, 0, 0.6);
}
.Mouse::before {
content: '';
position: absolute;
top: 50%;
left: -12px;
/* 控制三角形的位置 */
transform: translateY(-50%);
border-top: 12px solid transparent;
border-bottom: 12px solid transparent;
border-right: 12px solid rgba(0, 0, 0, 0.6);
/* 控制三角形的颜色 */
}
:deep(.el-table) {
background-color: transparent !important;
color: #fff;
}
:deep(.el-table tr) {
background-color: transparent !important;
color: #fff;
}
:deep(.el-table tr:hover) {
background-color: transparent !important;
}
:deep(.el-table th.el-table__cell) {
background-color: transparent;
color: #fff;
}
:deep(.el-table--enable-row-hover .el-table__body tr:hover > td.el-table__cell) {
background-color: rgba(0, 0, 0, 0.6);
color: #fff;
}
:deep(.el-table .cell) {
background-color: transparent;
color: #fff;
}
:deep(.el-table__empty-text) {
color: #fff !important;
}
:deep(.el-table .caret-wrapper) {
float: right;
top: 6px;
}
</style>
<style>
.el-popper.is-customized {
/* Set padding to ensure the height is 32px */
padding: 6px 12px;
background: #fff;
}
.el-popper.is-customized .el-popper__arrow::before {
background: #fff;
right: 0;
}
</style>