123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505 |
- <template>
- <div class="draw-tools-container">
- <div class="draw-item" @mouseover="showColorSelect = true" @mouseleave="showColorSelect = false">
- <div class="item-label">画笔选择</div>
- <i class="color-box" :style="{ backgroundColor: colorList[drawerColorIndex]?.value }" />
- <!--弹窗选择器-->
- <div v-show="showColorSelect" ref="selectBoxRef" class="select-box" style="top: -242px; padding-bottom: 20px">
- <div class="box-content">
- <div
- v-for="(item, index) in colorList"
- :key="index"
- :class="mouseToolState.drawType === item.value ? 'select-item active' : 'select-item'"
- @click="selectColor(index)"
- >
- <i class="color-box" :style="{ backgroundColor: item.value }" />
- <span>{{ item.label }}</span>
- </div>
- </div>
- </div>
- </div>
- <div class="draw-item" @mouseover="showDrawTypeSelect = true" @mouseleave="showDrawTypeSelect = false">
- <div class="item-label">框面类型</div>
- <!--弹窗选择器-->
- <div v-show="showDrawTypeSelect" ref="selectBoxRef2" class="select-box" style="top: -127px; padding-bottom: 20px">
- <div class="box-content">
- <div
- v-for="(item, index) in drawTypeList"
- :key="index"
- :class="mouseToolState.drawType === item.value ? 'select-item active' : 'select-item'"
- @click="selectDrawType(index)"
- >
- <span>{{ item.label }}</span>
- </div>
- </div>
- </div>
- </div>
- <div class="draw-item" @mouseover="showGraphicsTypeSelect = true" @mouseleave="showGraphicsTypeSelect = false">
- <div class="item-label">图像类型</div>
- <!--弹窗选择器-151-->
- <div v-show="showGraphicsTypeSelect" ref="selectBoxRef2" class="select-box" style="top: -184.5px; padding-bottom: 20px">
- <div class="box-content">
- <div
- v-for="(item, index) in graphicsTypeList"
- :key="index"
- :class="mouseToolState.graphicsType === item.value ? 'select-item active' : 'select-item'"
- @click="selectGraphicsType(index)"
- >
- <span>{{ item.label }}</span>
- </div>
- </div>
- </div>
- </div>
- <div class="draw-item" @click="openDraw">{{ mouseToolState.drawing ? '关闭绘制' : '开启绘制' }}</div>
- <div class="draw-item" @click="handleUndo">撤销</div>
- <div id="menu-box">
- <div class="menu-item">删除</div>
- </div>
- </div>
- </template>
- <script lang="ts" setup name="DrawTools">
- import { useHistory } from '@/hooks/useHistory';
- import { nanoid } from 'nanoid';
- import { deepClone } from '@/utils';
- import * as turf from '@turf/turf';
- interface ListItem {
- label: string;
- value: string;
- }
- const props = defineProps({
- activeMap: String
- });
- const AMapType = ['vectorgraph', 'satellite'];
- const getDrawTool = inject('getDrawTool');
- const getMap = inject('getMap');
- const emits = defineEmits(['handleAnalysisData']);
- const { currentState, commit, undo, history, future } = useHistory();
- const mouseToolState = reactive({
- drawing: false,
- color: '#f80102',
- // 1线框 2区域
- drawType: '1',
- // 图形形状 circle圆形 rectangle矩形 polygon多边形
- graphicsType: 'circle'
- });
- watch(
- mouseToolState,
- () => {
- const drawTool = getDrawTool();
- if (mouseToolState.drawing) {
- drawOptions.color = mouseToolState.color;
- drawOptions.drawType = mouseToolState.drawType;
- drawOptions.graphicsType = mouseToolState.graphicsType;
- drawTool.drawGraphics2(drawOptions);
- if (AMapType.includes(props.activeMap)) {
- drawTool.getMouseTool().on('draw', onDraw);
- } else {
- drawTool.getMouseTool().on('drawend', onDraw2);
- }
- } else {
- mouseToolState.drawing = false;
- drawTool.closeDraw();
- }
- },
- {
- deep: true
- }
- );
- const drawOptions = reactive({
- color: '#f80102',
- // 1线框 2区域
- drawType: '1',
- // 图形形状 circle圆形 rectangle矩形 polygon多边形
- graphicsType: 'circle'
- });
- const selectBoxRef = ref(null);
- const selectBoxRef2 = ref(null);
- const selectBoxRef3 = ref(null);
- // 显示画笔选择弹窗
- let showColorSelect = ref(false);
- // 画笔颜色索引
- let drawerColorIndex = ref(0);
- // 画笔颜色列表
- const colorList: ListItem[] = reactive([
- { label: '红色', value: '#f80102' },
- { label: '橙色', value: '#f4511e' },
- { label: '蓝色', value: '#0197f8' },
- { label: '绿色', value: '#42b884' }
- ]);
- // 选中颜色
- const selectColor = (index) => {
- drawerColorIndex.value = index;
- mouseToolState.color = colorList[index].value;
- showColorSelect.value = false;
- };
- const overlays = [];
- const overlaysData = [];
- // 点击颜色选择之外
- onClickOutside(selectBoxRef, () => {
- if (!showColorSelect.value) return;
- showColorSelect.value = false;
- });
- // 绘制类型列表
- const drawTypeList: ListItem[] = reactive([
- { label: '线框', value: '1' },
- { label: '面框', value: '2' }
- ]);
- // 显示隐藏绘制类型弹窗
- let showDrawTypeSelect = ref(false);
- // 选择
- const selectDrawType = (index) => {
- mouseToolState.drawType = drawTypeList[index].value;
- showDrawTypeSelect.value = false;
- };
- // 点击绘制类型选择之外
- onClickOutside(selectBoxRef2, () => {
- if (!showDrawTypeSelect.value) return;
- showDrawTypeSelect.value = false;
- });
- // 绘制图形列表
- const graphicsTypeList: ListItem[] = reactive([
- { label: '圆形', value: 'circle' },
- { label: '矩形', value: 'rectangle' },
- { label: '多边形', value: 'polygon' }
- // { label: '索套', value: 'freePolygon' }
- ]);
- // 显隐图像类型弹窗
- let showGraphicsTypeSelect = ref(false);
- // 选择
- const selectGraphicsType = (index) => {
- mouseToolState.graphicsType = graphicsTypeList[index].value;
- showGraphicsTypeSelect.value = false;
- };
- // 点击绘制类型选择之外
- onClickOutside(selectBoxRef3, () => {
- if (!showGraphicsTypeSelect.value) return;
- showGraphicsTypeSelect.value = false;
- });
- // 开启结束绘制
- const openDraw = () => {
- mouseToolState.drawing = !mouseToolState.drawing;
- };
- // 高德地图绘制结束
- const onDraw = (event) => {
- const obj = event.obj;
- const id = nanoid();
- obj._opts.extData = {
- id: id
- };
- const data: any = {
- id: id,
- type: drawOptions.graphicsType,
- color: obj._opts.strokeColor,
- drawType: obj._opts.fillOpacity === 0 ? '1' : '2'
- };
- if (data.type === 'circle') {
- data.center = [obj.getCenter().lng, obj.getCenter().lat];
- data.radius = obj.getRadius();
- }
- const path = obj.getPath();
- // 将AMap.LngLat对象数组转换为经纬度数组
- const pathArr = path.map((lngLat) => {
- // 返回经度和纬度的数组
- return [lngLat.lng, lngLat.lat];
- });
- data.path = pathArr;
- overlays.push(obj);
- overlaysData.push(data);
- commit(deepClone(overlaysData));
- // 右击进入编辑
- obj.on('rightclick', handleRightClick);
- if (overlaysData.length === 1) {
- analysisSpatial(data);
- }
- // 点击空间分析
- obj.on('click', function () {
- // 没在编辑时
- if (!mouseToolState.drawing) {
- analysisSpatial(data);
- }
- });
- };
- // 粤政图绘制结束
- const onDraw2 = (event) => {
- const feature = event.feature;
- const geometry = feature.getGeometry();
- const geometryType = geometry.getType();
- const id = nanoid();
- feature.set('id', id);
- const data: any = {
- id: id,
- type: drawOptions.graphicsType,
- // color: feature._opts.strokeColor,
- // drawType: feature._opts.fillOpacity === 0 ? '1' : '2'
- };
- // 获取绘制的几何信息(包括经纬度)
- if (geometryType === 'Point') {
- const coordinates = geometry.getCoordinates();
- console.log('绘制了一个点:', coordinates);
- } else if (geometryType === 'LineString') {
- const coordinates = geometry.getCoordinates();
- console.log('绘制了一条线:', coordinates);
- // 线的坐标是点的数组,每个点都是一个经纬度数组
- } else if (geometryType === 'Polygon') {
- const coordinates = geometry.getCoordinates();
- console.log('绘制了一个多边形:', coordinates);
- // 多边形的坐标是环的数组,每个环是点的数组,每个点都是一个经纬度数组
- } else if (geometryType === 'Circle') {
- data.center = geometry.getCenter();
- data.radius = geometry.getRadius();
- const data2 = turf.sector(data.center, data.radius, 0, 360);
- const pathArr = data2.geometry.coordinates[0];
- data.path = pathArr;
- console.log('绘制了一个圆:', data);
- }
- // const path = geometry.getCoordinates();
- // debugger
- // // 将AMap.LngLat对象数组转换为经纬度数组
- // const pathArr = path.map((lngLat) => {
- // // 返回经度和纬度的数组
- // return [lngLat.lng, lngLat.lat];
- // });
- // data.path = pathArr;
- overlays.push(feature);
- overlaysData.push(data);
- commit(deepClone(overlaysData));
- // 右击进入编辑
- feature.on('contextmenu', handleRightClick);
- if (overlaysData.length === 1) {
- analysisSpatial(data);
- }
- // // 点击空间分析
- // feature.on('click', function () {
- // // 没在编辑时
- // if (!mouseToolState.drawing) {
- // analysisSpatial(data);
- // }
- // });
- };
- let rightClickObj;
- // 图形右击事件
- let initContextMenu = false;
- const handleRightClick = (event) => {
- debugger
- rightClickObj = event.target;
- const contextMenu = getDrawTool().getContextMenu();
- if (!initContextMenu) {
- // 右键删除按钮
- contextMenu.addItem(
- '删除',
- function () {
- contextMenu.close();
- deleteGraphics();
- },
- 0
- );
- initContextMenu = true;
- }
- contextMenu.open(getMap(), event.lnglat);
- };
- // 删除图形
- const deleteGraphics = () => {
- const id = rightClickObj.getExtData()?.id;
- if (id) {
- for (let i = 0; i < overlays.length; i++) {
- const overlay = Array.isArray(overlays[i]) ? overlays[i][0] : overlays[i];
- if (overlay?.getExtData().id === id) {
- removeOverlayByIndex(i);
- commit(deepClone(overlaysData));
- rightClickObj = null;
- }
- }
- }
- };
- // 撤销绘制
- const handleUndo = () => {
- mouseToolState.drawing = false;
- const previous = history.value[history.value.length - 2];
- if (history.value.length > 1) {
- if (currentState.value.length > previous.length) {
- // 撤销新增
- removeOverlayByIndex(currentState.value.length - 1);
- } else {
- let restoreData;
- for (let i = 0; i < previous.length; i++) {
- let index = 0;
- for (let k = 0; k < currentState.value.length; k++) {
- if (previous[i].id !== currentState.value[k].id) {
- index++;
- } else {
- break;
- }
- }
- if (index === previous.length - 1) {
- restoreData = previous[i];
- break;
- }
- }
- if (restoreData) {
- const newData = {
- type: restoreData.type,
- path: restoreData.path,
- strokeColor: restoreData.color,
- strokeOpacity: 1,
- strokeWeight: '1',
- fillColor: restoreData.color,
- fillOpacity: restoreData.drawType === '1' ? 0 : 0.5
- };
- if (restoreData.type === 'circle') {
- newData.center = restoreData.center;
- newData.radius = restoreData.radius;
- }
- const obj = getDrawTool().createGraphics(newData);
- overlays.push(obj);
- }
- }
- undo();
- console.log(history.value, future.value, currentState.value);
- }
- };
- // 根据索引移除覆盖物
- const removeOverlayByIndex = (index: number) => {
- const map = getMap();
- if (Array.isArray(overlays[index])) {
- overlays[index].forEach((overlay) => {
- // 移除地图上覆盖物
- map.remove(overlay);
- });
- } else {
- // 移除地图上覆盖物
- map.remove(overlays[index]);
- }
- overlays.splice(index, 1);
- overlaysData.splice(index, 1);
- };
- let selectedScope = reactive({});
- // 空间分析数据
- const analysisSpatial = (data) => {
- // 已选中的范围
- if (selectedScope[data.id]) {
- delete selectedScope[data.id];
- if (JSON.stringify(selectedScope) === '{}') {
- // rightMenuRef.value.clickContractMenu();
- return;
- }
- } else {
- selectedScope[data.id] = data;
- }
- let location = [];
- for (let key in selectedScope) {
- let itemLocation = [];
- if (selectedScope[key].path && selectedScope[key].path.length > 1) {
- selectedScope[key].path.forEach((item) => {
- itemLocation.push({
- x: item[0],
- y: item[1]
- });
- });
- itemLocation.push(itemLocation[0]);
- location.push(itemLocation);
- }
- }
- emits('handleAnalysisData', location);
- };
- onMounted(() => {
- // 监听右击事件
- getMap().on('contextmenu', (event) => {
- event.preventDefault(); // 阻止默认上下文菜单
- // 获取右击位置的要素
- const featuresAtPixel = getMap().getFeaturesAtPixel(event.pixel);
- // 检查是否有要素在右击位置
- if (featuresAtPixel && featuresAtPixel.length > 0) {
- // 遍历要素并检查条件(这里可以根据需要添加条件)
- featuresAtPixel.forEach(function(feature) {
- // 执行你想要的动作,例如显示一个信息框或弹出菜单
- console.log('右击了要素:', feature.get('id')); // 假设要素有一个'id'属性
- // 你可以在这里显示一个自定义的右击菜单
- // 例如,创建一个<div>元素,设置其内容和样式,然后添加到DOM中
- // 并监听菜单项的点击事件来执行相应的动作
- });
- }
- });
- })
- onBeforeUnmount(() => {
- //
- });
- </script>
- <style lang="scss" scoped>
- .draw-tools-container {
- position: absolute;
- right: 250px;
- bottom: 460px;
- background-color: #304468;
- border-radius: 5px;
- display: flex;
- color: #fff;
- font-size: 36px;
- padding: 10px 0;
- .draw-item {
- display: flex;
- align-items: center;
- position: relative;
- padding: 0 10px;
- cursor: pointer;
- border-left: 1px solid #3a74be;
- &:first-child {
- border-left: none;
- }
- .item-label {
- margin-right: 10px;
- }
- .select-box {
- position: absolute;
- left: 0;
- z-index: 7;
- width: 100%;
- .box-content {
- background-color: #102341;
- }
- .select-item {
- display: flex;
- align-items: center;
- cursor: pointer;
- padding: 5px 10px;
- &:hover {
- background-color: rgba(22, 73, 142, 0.5);
- }
- &:first-child {
- margin-left: 0;
- }
- }
- .color-box {
- margin-right: 5px;
- }
- }
- }
- .color-box {
- display: block;
- width: 20px;
- height: 20px;
- cursor: pointer;
- border-radius: 3px;
- }
- }
- .active {
- background-color: rgba(22, 73, 142, 0.5);
- }
- .menu-box {
- .menu-item {
- font-size: 32px;
- }
- }
- </style>
|