451 lines
12 KiB
Vue
451 lines
12 KiB
Vue
<template>
|
|
<div class="toolbar-container">
|
|
<div
|
|
class="toolbar-thumbnail"
|
|
:title="isOpen ? '收起工具栏' : '展开工具栏'"
|
|
@click="isOpen = !isOpen"
|
|
></div>
|
|
<div class="toolbar">
|
|
<!-- 一级工具菜单 -->
|
|
<div
|
|
v-for="tool in options"
|
|
:key="tool.id"
|
|
class="tool-wrapper"
|
|
:class="{'open': isOpen}"
|
|
>
|
|
<!-- 主工具按钮 -->
|
|
<div class="tool-button" @click="tool.name && bus.emit(`toolbar_${tool.name}`, params)">
|
|
<el-image class="tool-icon" :src="tool.icon" fit="contain" />
|
|
</div>
|
|
<!-- 主工具名称 (右侧淡入淡出) -->
|
|
<div class="tool-name main-name">{{ tool.label }}</div>
|
|
<!-- 二级工具菜单 (左侧淡入淡出) -->
|
|
<div v-if="tool.subtools && tool.subtools?.length > 0" class="toolbar submenu">
|
|
<!-- 遍历二级工具按钮 -->
|
|
<div
|
|
v-for="subtool in tool.subtools"
|
|
:key="subtool.id"
|
|
class="tool-wrapper"
|
|
>
|
|
<!-- 二级工具按钮 -->
|
|
<div class="tool-button" @click="subtool.name && bus.emit(`toolbar_${subtool.name}`, params)">
|
|
<el-image class="tool-icon" :src="subtool.icon" fit="contain" />
|
|
</div>
|
|
<!-- 二级工具名称 (左侧淡入淡出) -->
|
|
<div class="tool-name sub-name">{{ subtool.label }}</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup>
|
|
import * as Cesium from 'cesium';
|
|
|
|
import { useDrawTool } from '@/components/CesiumMap/mixins/useDrawTool';
|
|
import { useEventBus } from '@/components/CesiumMap/mixins/useEventBus';
|
|
import { useMeasureTool } from '@/components/CesiumMap/mixins/useMeasureTool';
|
|
import {
|
|
drawLocation,
|
|
drawPolyline,
|
|
drawPolygon,
|
|
drawCurvePolygon,
|
|
drawStraightArrow,
|
|
drawWideArrow,
|
|
drawAttackArrow,
|
|
drawDoubleArrow,
|
|
drawMeasureDistance,
|
|
drawMeasureArea,
|
|
} from './baseDraw'
|
|
import {
|
|
drawWarning,
|
|
drawTrack,
|
|
drawRanger,
|
|
drawPtz,
|
|
drawBayonet,
|
|
drawAirport,
|
|
drawHot,
|
|
drawWarehouse,
|
|
drawWatersource,
|
|
drawRoad,
|
|
drawKeyarea,
|
|
} from './forestFireDraw'
|
|
|
|
// 工具栏图标
|
|
import baseDrawIcon from "@/assets/icons/toolbar_base_draw.png";
|
|
import forestDrawIcon from "@/assets/icons/toolbar_forest_draw.png";
|
|
import weatherIcon from "@/assets/icons/toolbar_weather.png";
|
|
import toolbarClearIcon from '@/assets/icons/toolbar_clear.png';
|
|
// 基础绘制工具图标
|
|
import toolbarLocationIcon from '@/assets/icons/toolbar_location.png';
|
|
import toolbarPolylineIcon from '@/assets/icons/toolbar_polyline.png';
|
|
import toolbarPolygonIcon from '@/assets/icons/toolbar_polygon.png';
|
|
import toolbarCurvePolygonIcon from '@/assets/icons/toolbar_curve_polygon.png';
|
|
import toolbarStraightArrowIcon from '@/assets/icons/toolbar_straight_arrow.png';
|
|
import toolbarWideArrowIcon from '@/assets/icons/toolbar_wide_arrow.png';
|
|
import toolbarAttackArrowIcon from '@/assets/icons/toolbar_attack_arrow.png';
|
|
import toolbarDoubleArrowIcon from '@/assets/icons/toolbar_double_arrow.png';
|
|
import toolbarMeasureDistanceIcon from '@/assets/icons/toolbar_measure_distance.png';
|
|
import toolbarMeasureAreaIcon from '@/assets/icons/toolbar_measure_area.png';
|
|
// 森防绘制工具图标
|
|
import toolbarWarningIcon from '@/assets/icons/toolbar_warning.png';
|
|
import toolbarTrackIcon from '@/assets/icons/toolbar_track.png';
|
|
import toolbarRangerIcon from '@/assets/icons/toolbar_ranger.png';
|
|
import toolbarPtzIcon from '@/assets/icons/toolbar_ptz.png';
|
|
import toolbarBayonetIcon from '@/assets/icons/toolbar_bayonet.png';
|
|
import toolbarAirportIcon from '@/assets/icons/toolbar_airport.png';
|
|
import toolbarHotIcon from '@/assets/icons/toolbar_hot.png';
|
|
import toolbarWarehouseIcon from '@/assets/icons/toolbar_warehouse.png';
|
|
import toolbarWatersourceIcon from '@/assets/icons/toolbar_watersource.png';
|
|
import toolbarRoadIcon from '@/assets/icons/toolbar_road.png';
|
|
import toolbarKeyareaIcon from '@/assets/icons/toolbar_keyarea.png';
|
|
|
|
const props = defineProps({
|
|
viewer: {
|
|
default: null
|
|
}
|
|
})
|
|
|
|
// 初始化地图实例
|
|
let viewer = null
|
|
let drawTool = null
|
|
let measureTool = null
|
|
let bus = null
|
|
let toolbarLayer = null
|
|
let primitiveList = []
|
|
let params = {}
|
|
const isOpen = ref(false)
|
|
const options = ref([
|
|
{
|
|
id: 1,
|
|
icon: baseDrawIcon,
|
|
label: '基础绘制',
|
|
subtools: [
|
|
{
|
|
id: 11,
|
|
name: 'location',
|
|
label: '位置',
|
|
icon: toolbarLocationIcon
|
|
}, {
|
|
id: 12,
|
|
name: 'polyline',
|
|
label: '线',
|
|
icon: toolbarPolylineIcon
|
|
}, {
|
|
id: 13,
|
|
name: 'polygon',
|
|
label: '面',
|
|
icon: toolbarPolygonIcon
|
|
}, {
|
|
id: 14,
|
|
name: 'curvePolygon',
|
|
label: '曲面',
|
|
icon: toolbarCurvePolygonIcon
|
|
}, {
|
|
id: 15,
|
|
name: 'straightArrow',
|
|
label: '直箭头',
|
|
icon: toolbarStraightArrowIcon
|
|
}, {
|
|
id: 16,
|
|
name: 'wideArrow',
|
|
label: '宽箭头',
|
|
icon: toolbarWideArrowIcon
|
|
}, {
|
|
id: 17,
|
|
name: 'attackArrow',
|
|
label: '攻击箭头',
|
|
icon: toolbarAttackArrowIcon
|
|
}, {
|
|
id: 18,
|
|
name: 'doubleArrow',
|
|
label: '双箭头',
|
|
icon: toolbarDoubleArrowIcon
|
|
}, {
|
|
id: 19,
|
|
name: 'measureDistance',
|
|
label: '测距',
|
|
icon: toolbarMeasureDistanceIcon
|
|
}, {
|
|
id: 110,
|
|
name: 'measureArea',
|
|
label: '测面',
|
|
icon: toolbarMeasureAreaIcon
|
|
}
|
|
],
|
|
},
|
|
{
|
|
id: 2,
|
|
icon: forestDrawIcon,
|
|
label: '森防绘制',
|
|
subtools: [
|
|
{
|
|
id: 21,
|
|
name: 'warning',
|
|
label: '火情点',
|
|
icon: toolbarWarningIcon
|
|
}, {
|
|
id: 22,
|
|
name: 'track',
|
|
label: '巡护路线',
|
|
icon: toolbarTrackIcon
|
|
}, {
|
|
id: 23,
|
|
name: 'ranger',
|
|
label: '护林员',
|
|
icon: toolbarRangerIcon
|
|
}, {
|
|
id: 24,
|
|
name: 'ptz',
|
|
label: '云台',
|
|
icon: toolbarPtzIcon
|
|
}, {
|
|
id: 25,
|
|
name: 'bayonet',
|
|
label: '卡口',
|
|
icon: toolbarBayonetIcon
|
|
}, {
|
|
id: 26,
|
|
name: 'airport',
|
|
label: '无人机机场',
|
|
icon: toolbarAirportIcon
|
|
}, {
|
|
id: 27,
|
|
name: 'hot',
|
|
label: '热点',
|
|
icon: toolbarHotIcon
|
|
}, {
|
|
id: 28,
|
|
name: 'warehouse',
|
|
label: '仓库',
|
|
icon: toolbarWarehouseIcon
|
|
}, {
|
|
id: 29,
|
|
name: 'watersource',
|
|
label: '水源',
|
|
icon: toolbarWatersourceIcon
|
|
}, {
|
|
id: 210,
|
|
name: 'road',
|
|
label: '林区道路',
|
|
icon: toolbarRoadIcon
|
|
}, {
|
|
id: 211,
|
|
name: 'keyarea',
|
|
label: '重点区域',
|
|
icon: toolbarKeyareaIcon
|
|
},
|
|
]
|
|
},
|
|
{
|
|
id: 3,
|
|
icon: weatherIcon,
|
|
label: '气象效果',
|
|
},
|
|
{
|
|
id: 4,
|
|
icon: toolbarClearIcon,
|
|
label: '清空绘制',
|
|
name: 'clear',
|
|
}
|
|
]);
|
|
|
|
// 清除工具栏上的所有绘制实体
|
|
const toolbarClear = () => {
|
|
if (!viewer || !toolbarLayer) return;
|
|
|
|
// 清除所有绘制的实体
|
|
toolbarLayer.entities.removeAll();
|
|
// 清除所有绘制的图元
|
|
for (let index = primitiveList.length - 1; index >= 0; index--) {
|
|
const p = primitiveList[index];
|
|
if(viewer.scene.primitives.contains(p)) {
|
|
viewer.scene.primitives.remove(p);
|
|
primitiveList.splice(index, 1);
|
|
}
|
|
}
|
|
measureTool?.clear();
|
|
|
|
// 清除当前测量
|
|
if (measureTool) {
|
|
measureTool.abort();
|
|
}
|
|
|
|
// 清除当前绘制
|
|
if (drawTool) {
|
|
drawTool.abort();
|
|
}
|
|
};
|
|
|
|
watch(() => props.viewer, (v) => {
|
|
if (v) {
|
|
viewer = v;
|
|
|
|
// 添加绘制图层
|
|
toolbarLayer = new Cesium.CustomDataSource('toolbarLayer');
|
|
viewer.dataSources.add(toolbarLayer);
|
|
|
|
drawTool = useDrawTool(viewer);
|
|
measureTool = useMeasureTool(viewer);
|
|
// 绑定事件
|
|
params = {
|
|
drawTool,
|
|
measureTool,
|
|
viewer,
|
|
toolbarLayer,
|
|
primitiveList,
|
|
}
|
|
bus = useEventBus(viewer);
|
|
// 基础绘制部分
|
|
bus.on('toolbar_location', drawLocation);
|
|
bus.on('toolbar_polyline', drawPolyline);
|
|
bus.on('toolbar_polygon', drawPolygon);
|
|
bus.on('toolbar_curvePolygon', drawCurvePolygon);
|
|
bus.on('toolbar_straightArrow', drawStraightArrow);
|
|
bus.on('toolbar_wideArrow', drawWideArrow);
|
|
bus.on('toolbar_attackArrow', drawAttackArrow);
|
|
bus.on('toolbar_doubleArrow', drawDoubleArrow);
|
|
bus.on('toolbar_measureDistance', drawMeasureDistance);
|
|
bus.on('toolbar_measureArea', drawMeasureArea);
|
|
// 森防绘制部分
|
|
bus.on('toolbar_warning', drawWarning);
|
|
bus.on('toolbar_track', drawTrack);
|
|
bus.on('toolbar_ranger', drawRanger);
|
|
bus.on('toolbar_ptz', drawPtz);
|
|
bus.on('toolbar_bayonet', drawBayonet);
|
|
bus.on('toolbar_airport', drawAirport);
|
|
bus.on('toolbar_hot', drawHot);
|
|
bus.on('toolbar_watersource', drawWatersource);
|
|
bus.on('toolbar_warehouse', drawWarehouse);
|
|
bus.on('toolbar_road', drawRoad);
|
|
bus.on('toolbar_keyarea', drawKeyarea);
|
|
// 清空
|
|
bus.on('toolbar_clear', toolbarClear);
|
|
}
|
|
}, { immediate: true });
|
|
|
|
</script>
|
|
|
|
<style lang="scss" scoped>
|
|
.toolbar-container {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
|
|
.toolbar-thumbnail {
|
|
position: absolute;
|
|
width: 40px;
|
|
height: 40px;
|
|
background-image: url(../../../../assets/icons/toolbar_thumbnail.png);
|
|
cursor: pointer;
|
|
z-index: 1000;
|
|
}
|
|
|
|
.toolbar {
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding: 10px;
|
|
z-index: 0;
|
|
|
|
/* 通用工具按钮样式 */
|
|
.tool-wrapper {
|
|
position: relative;
|
|
width: 32px;
|
|
height: 0px;
|
|
background-color: rgba(7, 111, 111, 0.7);
|
|
margin-bottom: 2px;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
opacity: 0;
|
|
transition-duration: 0.8s;
|
|
|
|
&.open {
|
|
height: 40px;
|
|
opacity: 1;
|
|
}
|
|
/* 图标 */
|
|
.tool-button {
|
|
width: 100%;
|
|
height: 100%;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
cursor: pointer;
|
|
|
|
.tool-icon {
|
|
width: 20px;
|
|
height: 20px;
|
|
}
|
|
}
|
|
/* 名称 */
|
|
.tool-name {
|
|
position: absolute;
|
|
writing-mode: vertical-rl;
|
|
letter-spacing: 2px;
|
|
color: #8cf8fe;
|
|
white-space: nowrap;
|
|
z-index: 3;
|
|
|
|
opacity: 0; /* 默认隐藏 */
|
|
pointer-events: none;
|
|
transition: opacity 0.3s ease, transform 0.3s ease; /* 动画持续时间和缓动函数 */
|
|
}
|
|
|
|
&:first-child {
|
|
border-radius: 16px 16px 0 0;
|
|
margin-top: 12px;
|
|
padding-top: 12px;
|
|
}
|
|
&:last-child {
|
|
border-radius: 0 0 16px 16px;
|
|
padding-bottom: 5px;
|
|
}
|
|
&:hover {
|
|
background-color: rgba($color: #FFD700, $alpha: .5);
|
|
}
|
|
}
|
|
}
|
|
/* 主菜单名称(右侧) */
|
|
.main-name {
|
|
left: 100%;
|
|
transform: translateX(0px); /* 初始位置 */
|
|
}
|
|
.tool-wrapper:hover > .main-name {
|
|
opacity: 1;
|
|
transform: translateX(4px); /* 移动到最终位置 */
|
|
transition-delay: 0.2s; /* 延迟 */
|
|
}
|
|
/* 子菜单容器样式 */
|
|
.submenu {
|
|
position: absolute;
|
|
right: 100%;
|
|
pointer-events: none;
|
|
transition: opacity 0.3s ease, transform 0.3s ease; /* 动画持续时间和缓动函数 */
|
|
}
|
|
/* 二级工具名称的定位 (左侧) */
|
|
.sub-name {
|
|
right: 100%;
|
|
transform: translateX(0); /* 初始位置 */
|
|
}
|
|
/* 当主 tool-wrapper 被 hover 且包含二级菜单时,显示二级菜单 */
|
|
.tool-wrapper:hover > .submenu {
|
|
pointer-events: all;
|
|
transition-delay: 0.1s; /* 延迟 */
|
|
|
|
& > .tool-wrapper {
|
|
height: 40px;
|
|
opacity: 1;
|
|
}
|
|
}
|
|
|
|
/* 当二级 tool-wrapper 被 hover 时,显示二级工具名称 */
|
|
/* 注意这里的选择器,因为二级名称在二级 tool-wrapper 内部 */
|
|
.submenu > .tool-wrapper:hover > .sub-name {
|
|
opacity: 1;
|
|
transform: translateX(-8px); /* 移动到最终位置 */
|
|
transition-delay: 0.2s; /* 延迟 */
|
|
}
|
|
}
|
|
</style> |