|
|
@@ -1,1004 +0,0 @@
|
|
|
-<template>
|
|
|
- <div class="tool-bar glass">
|
|
|
- <button @click="startDraw('point')">🟢 绘点</button>
|
|
|
- <button @click="startDraw('line')">📏 绘线</button>
|
|
|
- <button @click="startDraw('polygon')">🔷 绘面</button>
|
|
|
- <button @click="startDraw('circle')">⭕ 绘圆</button>
|
|
|
- <button @click="startDraw('rectangle')"><span style="display: inline-block; transform: rotate(45deg);">🔷</span> 矩形</button>
|
|
|
- <button @click="startDraw('distance')">📏 测距</button>
|
|
|
- <button @click="startDraw('area')">🔷 测面</button>
|
|
|
- <button @click="toggleEditMode" :class="{ active: isEditMode }" :disabled="isEditDisabled">✏️ 编辑</button>
|
|
|
- <button @click="clearAll">🗑️ 清空</button>
|
|
|
- </div>
|
|
|
-</template>
|
|
|
-
|
|
|
-<script>
|
|
|
-
|
|
|
-export default {
|
|
|
- name: "DrawMap",
|
|
|
- components: {
|
|
|
-
|
|
|
- },
|
|
|
- data() {
|
|
|
- return {
|
|
|
- tempSource: null, // 当前绘制的source
|
|
|
- tempPoints: [], // 存储绘制的点
|
|
|
- drawType: '', // 绘制类型
|
|
|
- isDrawing: false, // 是否正在绘制
|
|
|
- hoveredPolygonId: null, // 当前悬停的面ID
|
|
|
- tempLineSource: null, // 当前绘制的线的source
|
|
|
- itemId: '', // 当前绘制的图形ID
|
|
|
- totalDistance: 0, // 总距离
|
|
|
- markers: [], // 存储节点标签
|
|
|
- moveMarkerTips: null, //移动节点显示提示标签
|
|
|
-
|
|
|
- // 用一个变量防止双击触发单击
|
|
|
- lastClickTime: 0, // 最后一次点击的时间
|
|
|
- lastClickEvent: null, // 最后一次点击的事件
|
|
|
-
|
|
|
- // 编辑相关状态
|
|
|
- isEditDisabled: true, // 是否禁用编辑功能
|
|
|
- isDragWhole: false, // 拖拽整个图形
|
|
|
- lastLngLat: null, // 最后一次点击的经纬度
|
|
|
- isEditMode: false, // 编辑模式
|
|
|
- selectedFeature: null, // 当前选中的图形
|
|
|
- selectedFeatureId: '', // 当前选中图形的ID
|
|
|
- layersIds: [], // 当前选中图形的图层ID
|
|
|
- editMarkers: [], // 存储编辑节点的标记
|
|
|
- isDragging: false, // 是否正在拖拽
|
|
|
- draggingPointIndex: -1, // 正在拖拽的节点索引
|
|
|
- dragEventHandlers: null, // 拖拽事件处理器
|
|
|
- };
|
|
|
- },
|
|
|
- mounted() {
|
|
|
- let that = this;
|
|
|
- that.initData();
|
|
|
- },
|
|
|
- methods: {
|
|
|
- initData() {
|
|
|
- let that = this;
|
|
|
- window.mapboxMap.on('load', function() {
|
|
|
- that.bindMapEvents()
|
|
|
- })
|
|
|
- },
|
|
|
-
|
|
|
- // 绑定地图事件
|
|
|
- bindMapEvents() {
|
|
|
- let that = this;
|
|
|
- that.addDrawLayer();
|
|
|
-
|
|
|
- // 地图点击事件,用于选择图形
|
|
|
- window.mapboxMap.on('click', (e) => {
|
|
|
- if (that.isEditMode) {
|
|
|
- that.handleFeatureClick(e);
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- // 鼠标移动事件,用于拖拽
|
|
|
- window.mapboxMap.on('mousemove', (e) => {
|
|
|
- if (that.isEditMode && that.isDragging) {
|
|
|
- window.mapboxMap.getCanvas().style.cursor = 'move';
|
|
|
- that.handleDrag(e);
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- // 鼠标释放事件,用于结束拖拽
|
|
|
- window.mapboxMap.on('mouseup', () => {
|
|
|
- that.isDragging = false;
|
|
|
- that.draggingPointIndex = -1;
|
|
|
- that.tempLineSource.setData({ type: 'FeatureCollection', features: [] });
|
|
|
- window.mapboxMap.getCanvas().style.cursor = '';
|
|
|
- that.showEditMarkers();
|
|
|
- });
|
|
|
- },
|
|
|
- addDrawLayer(){
|
|
|
- let that = this;
|
|
|
- // 添加【临时辅助线】图层(鼠标跟随红线)
|
|
|
- window.mapboxMap.addSource('temp-line', {
|
|
|
- type: 'geojson',
|
|
|
- data: { type: 'FeatureCollection', features: [] }
|
|
|
- });
|
|
|
- window.mapboxMap.addLayer({
|
|
|
- id: 'temp-line-layer',
|
|
|
- type: 'line',
|
|
|
- source: 'temp-line',
|
|
|
- paint: {
|
|
|
- 'line-color': 'red',
|
|
|
- 'line-width': 2,
|
|
|
- 'line-dasharray': [3, 3] // 虚线
|
|
|
- }
|
|
|
- });
|
|
|
- that.tempLineSource = window.mapboxMap.getSource('temp-line');
|
|
|
-
|
|
|
- // ------------- 绑定事件-------------
|
|
|
- window.mapboxMap.off('click', this.boundHandleClick);
|
|
|
- window.mapboxMap.off('dblclick', this.boundHandleDblClick);
|
|
|
-
|
|
|
- this.boundHandleClick = this.handleMapClick.bind(this);
|
|
|
- this.boundHandleDblClick = this.handleMapDblClick.bind(this);
|
|
|
-
|
|
|
- // 单击
|
|
|
- window.mapboxMap.on('click', (e) => {
|
|
|
- const now = Date.now();
|
|
|
- // 两次点击间隔 < 300ms = 双击
|
|
|
- if (now - this.lastClickTime < 300) {
|
|
|
- // 双击:取消上一次单击
|
|
|
- this.lastClickEvent = null;
|
|
|
- } else {
|
|
|
- // 正常单击:保存事件
|
|
|
- this.lastClickEvent = e;
|
|
|
- this.lastClickTime = now;
|
|
|
- // 延迟一点点,确保不与双击冲突
|
|
|
- setTimeout(() => {
|
|
|
- if (this.lastClickEvent) {
|
|
|
- this.handleMapClick(this.lastClickEvent);
|
|
|
- this.lastClickEvent = null;
|
|
|
- }
|
|
|
- }, 10); // 极短延迟,不影响手感
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- // 双击
|
|
|
- window.mapboxMap.on('dblclick', (e) => {
|
|
|
- this.lastClickEvent = null; // 清空单击
|
|
|
- this.lastClickTime = Date.now();
|
|
|
- this.handleMapDblClick(e); // 只执行双击
|
|
|
- });
|
|
|
-
|
|
|
- // 鼠标移动 → 实时更新【最后一个点 ↔ 鼠标】的连接线
|
|
|
- window.mapboxMap.on('mousemove', (e) => {
|
|
|
- if (!that.isDrawing) return
|
|
|
- // if (that.drawType === 'polygon'){
|
|
|
- // if (that.tempPoints.length < 2) return; // 至少有三个点才绘制辅助线
|
|
|
- // }else{
|
|
|
- // if (that.tempPoints.length < 1) return; // 至少有一个点才绘制辅助线
|
|
|
- // }
|
|
|
-
|
|
|
- if(that.moveMarkerTips){
|
|
|
- that.moveMarkerTips.setLngLat(e.lngLat); // 更新标记位置
|
|
|
- }else{
|
|
|
- let text = "鼠标双击完成";
|
|
|
- if(that.drawType === 'circle' || that.drawType === 'rectangle'){
|
|
|
- text = "鼠标双击/单击完成";
|
|
|
- }
|
|
|
- that.addMoveMarkerTips(e.lngLat, text);
|
|
|
- }
|
|
|
- const end = [e.lngLat.lng, e.lngLat.lat];
|
|
|
- // 矩形绘制
|
|
|
- if (that.drawType === 'rectangle'){
|
|
|
- if (that.tempPoints.length < 1) return; // 至少有一个点才绘制辅助线
|
|
|
- const bbox = [
|
|
|
- Math.min(that.tempPoints[0][0], end[0]),
|
|
|
- Math.min(that.tempPoints[0][1], end[1]),
|
|
|
- Math.max(that.tempPoints[0][0], end[0]),
|
|
|
- Math.max(that.tempPoints[0][1], end[1])
|
|
|
- ];
|
|
|
- that.addToMap(turf.bboxPolygon(bbox))
|
|
|
- }else if (that.drawType === 'circle' && that.tempPoints.length >= 1){
|
|
|
- let center = that.tempPoints[0];
|
|
|
- let radius = turf.distance(turf.point(center), turf.point(end), { units: 'kilometers' })
|
|
|
- that.addToMap(turf.circle(center, radius, 64))
|
|
|
- }
|
|
|
-
|
|
|
- if (that.tempPoints.length < 1 || that.drawType === 'rectangle') return; // 至少有一个点才绘制辅助线
|
|
|
- const mousePt = [e.lngLat.lng, e.lngLat.lat];
|
|
|
- const lastPt = that.tempPoints[that.tempPoints.length - 1];
|
|
|
- // ============ Turf 生成辅助线 ============
|
|
|
- const line = turf.lineString([lastPt, mousePt]);
|
|
|
- that.tempLineSource.setData(line);
|
|
|
- });
|
|
|
- },
|
|
|
- // 开始绘制
|
|
|
- startDraw(type) {
|
|
|
- let that = this;
|
|
|
- that.drawType = type
|
|
|
- that.isDrawing = true
|
|
|
- that.tempPoints = []
|
|
|
- that.tempLineSource.setData({ type: 'FeatureCollection', features: [] })
|
|
|
- that.itemId = 'draw-' + Date.now();
|
|
|
- window.mapboxMap.getCanvas().style.cursor = 'crosshair';
|
|
|
- that.totalDistance = 0;
|
|
|
- },
|
|
|
-
|
|
|
- // 地图点击
|
|
|
- handleMapClick(e) {
|
|
|
- let that = this;
|
|
|
- if (!that.isDrawing) return
|
|
|
- let coord = [e.lngLat.lng, e.lngLat.lat]
|
|
|
- that.tempPoints.push(coord)
|
|
|
- if (that.drawType === 'point') {
|
|
|
- let feature = turf.point(coord)
|
|
|
- feature.properties.drawType = 'point'
|
|
|
- feature.properties.isCircle = false;
|
|
|
- that.addToMap(feature)
|
|
|
- that.finishDraw()
|
|
|
- }
|
|
|
- if (that.drawType === 'line' && that.tempPoints.length >= 2) {
|
|
|
- let feature = turf.lineString(that.tempPoints)
|
|
|
- feature.properties.drawType = 'line'
|
|
|
- feature.properties.isCircle = false;
|
|
|
- that.addToMap(feature)
|
|
|
- }
|
|
|
- if (that.drawType === 'polygon' && that.tempPoints.length >= 2) {
|
|
|
- if(that.tempPoints.length >= 3){
|
|
|
- let feature = turf.polygon([[...that.tempPoints, that.tempPoints[0]]])
|
|
|
- feature.properties.drawType = 'polygon'
|
|
|
- feature.properties.isCircle = false;
|
|
|
- that.addToMap(feature)
|
|
|
- }else{
|
|
|
- let feature = turf.lineString(that.tempPoints)
|
|
|
- feature.properties.drawType = 'polygon'
|
|
|
- feature.properties.isCircle = false;
|
|
|
- that.addToMap(feature)
|
|
|
- }
|
|
|
- }
|
|
|
- if (that.drawType === 'circle' && that.tempPoints.length >= 2) {
|
|
|
- let center = that.tempPoints[0]
|
|
|
- let radius = turf.distance(turf.point(center), turf.point(that.tempPoints[1]), { units: 'kilometers' })
|
|
|
- let feature = turf.circle(center, radius, 64)
|
|
|
- feature.properties.drawType = 'circle'
|
|
|
- feature.properties.isCircle = true
|
|
|
- that.addToMap(feature)
|
|
|
- that.finishDraw()
|
|
|
- }
|
|
|
- if (that.drawType === 'distance' && that.tempPoints.length >= 2) {
|
|
|
- let feature = turf.lineString(that.tempPoints)
|
|
|
- feature.properties.drawType = 'distance'
|
|
|
- feature.properties.isCircle = false;
|
|
|
- that.addToMap(feature)
|
|
|
- let distance = turf.distance(turf.point(that.tempPoints[0]), turf.point(that.tempPoints[1]), { units: 'kilometers' })
|
|
|
- that.totalDistance += distance;
|
|
|
- // 在节点上显示距离标签
|
|
|
- that.addMarker(e.lngLat, that.totalDistance.toFixed(2) + ' km');
|
|
|
- }
|
|
|
- if (that.drawType === 'area' && that.tempPoints.length >= 2) {
|
|
|
- if(that.tempPoints.length >= 3){
|
|
|
- let feature = turf.polygon([[...that.tempPoints, that.tempPoints[0]]])
|
|
|
- feature.properties.drawType = 'area'
|
|
|
- feature.properties.isCircle = false;
|
|
|
- that.addToMap(feature)
|
|
|
- let area = turf.area(feature)
|
|
|
- that.totalDistance += area;
|
|
|
- }else{
|
|
|
- let feature = turf.lineString(that.tempPoints)
|
|
|
- feature.properties.drawType = 'area'
|
|
|
- feature.properties.isCircle = false;
|
|
|
- that.addToMap(feature)
|
|
|
- }
|
|
|
- // 在节点上显示面积标签
|
|
|
- // that.addMarker(e.lngLat, that.totalDistance.toFixed(2) + ' km²');
|
|
|
- }
|
|
|
- else if (that.drawType === 'rectangle' && that.tempPoints.length >= 2) {
|
|
|
- that.finishDraw()
|
|
|
- }
|
|
|
- that.updateTempLayer()
|
|
|
- // console.log("click===========")
|
|
|
- },
|
|
|
-
|
|
|
- // 双击结束(线 / 面 / 圆)
|
|
|
- handleMapDblClick(e) {
|
|
|
- let that = this;
|
|
|
- let coord = [e.lngLat.lng, e.lngLat.lat]
|
|
|
- that.tempPoints.push(coord)
|
|
|
- // console.log("double===========")
|
|
|
- if (!that.isDrawing || that.drawType === 'point') return
|
|
|
- if (that.drawType === 'line' && that.tempPoints.length >= 2) {
|
|
|
- let feature = turf.lineString(that.tempPoints)
|
|
|
- feature.properties.drawType = 'line'
|
|
|
- feature.properties.isCircle = false;
|
|
|
- that.addToMap(feature)
|
|
|
- }
|
|
|
- if (that.drawType === 'polygon' && that.tempPoints.length >= 2) {
|
|
|
- if(that.tempPoints.length >= 3){
|
|
|
- let feature = turf.polygon([[...that.tempPoints, that.tempPoints[0]]])
|
|
|
- feature.properties.drawType = 'polygon'
|
|
|
- feature.properties.isCircle = false;
|
|
|
- that.addToMap(feature)
|
|
|
- }else{
|
|
|
- let feature = turf.lineString(that.tempPoints)
|
|
|
- feature.properties.drawType = 'polygon'
|
|
|
- feature.properties.isCircle = false;
|
|
|
- that.addToMap(feature)
|
|
|
- }
|
|
|
- }
|
|
|
- if (that.drawType === 'circle' && that.tempPoints.length >= 2) {
|
|
|
- let center = that.tempPoints[0]
|
|
|
- let radius = turf.distance(turf.point(center), turf.point(that.tempPoints[1]), { units: 'kilometers' })
|
|
|
- let feature = turf.circle(center, radius, 64)
|
|
|
- feature.properties.drawType = 'circle'
|
|
|
- feature.properties.isCircle = true
|
|
|
- that.addToMap(feature)
|
|
|
- }
|
|
|
- if (that.drawType === 'distance' && that.tempPoints.length >= 2) {
|
|
|
- let feature = turf.lineString(that.tempPoints)
|
|
|
- feature.properties.drawType = 'distance'
|
|
|
- feature.properties.isCircle = false;
|
|
|
- that.addToMap(feature)
|
|
|
- let distance = turf.distance(turf.point(that.tempPoints[0]), turf.point(that.tempPoints[1]), { units: 'kilometers' })
|
|
|
- that.totalDistance += distance;
|
|
|
- // 在节点上显示距离标签 1公里 = 1000米
|
|
|
- that.addMarker(e.lngLat, that.totalDistance.toFixed(2) + ' km');
|
|
|
- }
|
|
|
- if (that.drawType === 'area' && that.tempPoints.length >= 2) {
|
|
|
- let area = 0;
|
|
|
- if(that.tempPoints.length >= 3){
|
|
|
- let feature = turf.polygon([[...that.tempPoints, that.tempPoints[0]]])
|
|
|
- feature.properties.drawType = 'area'
|
|
|
- feature.properties.isCircle = false;
|
|
|
- that.addToMap(feature)
|
|
|
- area = turf.area(feature);
|
|
|
- that.addMarker(e.lngLat, (area/10000).toFixed(2) + ' 公顷');
|
|
|
- }else{
|
|
|
- let feature = turf.lineString(that.tempPoints)
|
|
|
- feature.properties.drawType = 'area'
|
|
|
- feature.properties.isCircle = false;
|
|
|
- that.addToMap(feature)
|
|
|
- let distance = turf.distance(turf.point(that.tempPoints[0]), turf.point(that.tempPoints[1]), { units: 'kilometers' })
|
|
|
- that.totalDistance += distance;
|
|
|
- // 在节点上显示距离标签 1公里 = 1000米
|
|
|
- that.addMarker(e.lngLat, that.totalDistance.toFixed(2) + ' km');
|
|
|
- }
|
|
|
-
|
|
|
- // that.addToMap(turf.polygon([[...that.tempPoints, that.tempPoints[0]]]))
|
|
|
- // const area = turf.area(turf.polygon([[...that.tempPoints, that.tempPoints[0]]]));
|
|
|
- // 在节点上显示面积标签 1公顷 = 10000平方米 `✅ 测量完成:${(area/10000).toFixed(2)} 公顷`
|
|
|
-
|
|
|
- }
|
|
|
- that.finishDraw()
|
|
|
- },
|
|
|
-
|
|
|
- // 7. 更新临时预览
|
|
|
- updateTempLayer() {
|
|
|
- let that = this;
|
|
|
- let feature
|
|
|
- if (that.drawType === 'point' && that.tempPoints.length) feature = turf.point(that.tempPoints[0])
|
|
|
- else if (that.drawType === 'line' && that.tempPoints.length >= 2) feature = turf.lineString(that.tempPoints)
|
|
|
- else if (that.drawType === 'polygon' && that.tempPoints.length >= 2) feature = turf.lineString([...that.tempPoints, that.tempPoints[0]])
|
|
|
- else if (that.drawType === 'circle' && that.tempPoints.length >= 1) feature = turf.point(that.tempPoints[0])
|
|
|
- else if (that.drawType === 'distance' && that.tempPoints.length >= 2) feature = turf.lineString(that.tempPoints)
|
|
|
- else if (that.drawType === 'area' && that.tempPoints.length >= 2) feature = turf.lineString([...that.tempPoints, that.tempPoints[0]])
|
|
|
-
|
|
|
- that.tempSource?.setData(feature || { type: 'FeatureCollection', features: [] })
|
|
|
- },
|
|
|
- // 添加移动节点显示提示标签
|
|
|
- addMoveMarkerTips(pt, text) {
|
|
|
- let that = this;
|
|
|
- let el = document.createElement('div');
|
|
|
- el.className = 'tips-label';
|
|
|
- el.innerText = text;
|
|
|
- // 添加到地图
|
|
|
- that.moveMarkerTips = new mapboxgl.Marker(el, { anchor: 'center', offset: [0, -30] })
|
|
|
- .setLngLat(pt)
|
|
|
- .addTo(window.mapboxMap);
|
|
|
-
|
|
|
- },
|
|
|
- // 在节点上添加距离标签
|
|
|
- addMarker(pt, text) {
|
|
|
- let that = this;
|
|
|
- let el = document.createElement('div');
|
|
|
- el.className = 'tips-label';
|
|
|
- el.innerText = text;
|
|
|
- // 添加到地图
|
|
|
- let marker = new mapboxgl.Marker(el, { anchor: 'center' })
|
|
|
- .setLngLat(pt)
|
|
|
- .addTo(window.mapboxMap);
|
|
|
- that.markers.push(marker);
|
|
|
-
|
|
|
- },
|
|
|
- // 清空所有节点标签
|
|
|
- clearMarkers() {
|
|
|
- let that = this;
|
|
|
- that.markers.forEach(m => m.remove());
|
|
|
- that.markers = [];
|
|
|
- },
|
|
|
- // 8. 添加到地图
|
|
|
- addToMap(feature) {
|
|
|
- let that = this;
|
|
|
- let id = that.itemId;
|
|
|
- if (!window.mapboxMap.getSource(id)) {
|
|
|
- window.mapboxMap.addSource(id, { type: 'geojson', data: feature })
|
|
|
- if(that.drawType === 'line'){
|
|
|
- window.mapboxMap.addLayer({ id: id + '-line', type: 'line', source: id, paint: { 'line-color': 'red', 'line-width': 2 } })
|
|
|
- }else if(that.drawType === 'polygon'){
|
|
|
- if(that.tempPoints.length >= 3){
|
|
|
- window.mapboxMap.addLayer({ id: id + '-polygon', type: 'fill', source: id, paint: { 'fill-color': 'red', 'fill-opacity': 0.3 }})
|
|
|
- }
|
|
|
- window.mapboxMap.addLayer({ id: id + '-line', type: 'line', source: id, paint: { 'line-color': 'red', 'line-width': 2 } })
|
|
|
- }else if(that.drawType === 'circle'){
|
|
|
- window.mapboxMap.addLayer({ id: id + '-polygon', type: 'fill', source: id, paint: { 'fill-color': 'red', 'fill-opacity': 0.3 }})
|
|
|
- window.mapboxMap.addLayer({ id: id + '-line', type: 'line', source: id, paint: { 'line-color': 'red', 'line-width': 2 } })
|
|
|
- // window.mapboxMap.addLayer({ id: id + '-point', type: 'circle', source: id, paint: { 'circle-radius': 5, 'circle-color': 'red' } })
|
|
|
- }else if(that.drawType === 'distance'){
|
|
|
- window.mapboxMap.addLayer({ id: id + '-line', type: 'line', source: id, paint: { 'line-color': 'red', 'line-width': 2 } })
|
|
|
- }else if(that.drawType === 'area'){
|
|
|
- if(that.tempPoints.length >= 3){
|
|
|
- window.mapboxMap.addLayer({ id: id + '-polygon', type: 'fill', source: id, paint: { 'fill-color': 'red', 'fill-opacity': 0.3 }})
|
|
|
- }
|
|
|
- window.mapboxMap.addLayer({ id: id + '-line', type: 'line', source: id, paint: { 'line-color': 'red', 'line-width': 2 } })
|
|
|
- }else if(that.drawType === 'rectangle'){
|
|
|
- window.mapboxMap.addLayer({ id: id + '-polygon', type: 'fill', source: id, paint: { 'fill-color': 'red', 'fill-opacity': 0.3 }})
|
|
|
- window.mapboxMap.addLayer({ id: id + '-line', type: 'line', source: id, paint: { 'line-color': 'red', 'line-width': 2 } })
|
|
|
- }else{
|
|
|
- window.mapboxMap.addLayer({ id: id + '-point', type: 'circle', source: id, paint: { 'circle-radius': 5, 'circle-color': 'red' } })
|
|
|
- }
|
|
|
-
|
|
|
- }else{
|
|
|
- window.mapboxMap.getSource(id).setData(feature)
|
|
|
- if(that.tempPoints.length >= 3 && (that.drawType === 'area' || that.drawType === 'polygon')){
|
|
|
- if(!window.mapboxMap.getLayer(id + '-polygon')){
|
|
|
- window.mapboxMap.addLayer({ id: id + '-polygon', type: 'fill', source: id, paint: { 'fill-color': 'red', 'fill-opacity': 0.3 }})
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 结束绘制
|
|
|
- finishDraw() {
|
|
|
- let that = this;
|
|
|
- that.isEditDisabled = false;
|
|
|
- that.isDrawing = false
|
|
|
- that.drawType = ''
|
|
|
- that.tempPoints = []
|
|
|
- that.tempSource?.setData({ type: 'FeatureCollection', features: [] })
|
|
|
- that.tempLineSource.setData({ type: 'FeatureCollection', features: [] })
|
|
|
- window.mapboxMap.getCanvas().style.cursor = '';
|
|
|
- if(that.moveMarkerTips){
|
|
|
- that.moveMarkerTips.remove();
|
|
|
- that.moveMarkerTips=null;
|
|
|
- }
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- // 清空所有
|
|
|
- clearAll() {
|
|
|
- let that = this;
|
|
|
- that.clearMarkers();
|
|
|
- that.clearEditMarkers();
|
|
|
- that.isEditDisabled = true;
|
|
|
- const layers = window.mapboxMap.getStyle().layers;
|
|
|
- layers.sort((a, b) => a.type.localeCompare(b.type));
|
|
|
- layers.forEach(layer => {
|
|
|
- if (layer.id.startsWith('draw-')) {
|
|
|
- if(layer.id.indexOf("-point") !== -1){
|
|
|
- window.mapboxMap.removeLayer(layer.id)
|
|
|
- }
|
|
|
- if(layer.id.indexOf("-line") !== -1){
|
|
|
- window.mapboxMap.removeLayer(layer.id)
|
|
|
- let ids = layer.id.replace("-line",'');
|
|
|
- window.mapboxMap.removeSource(ids)
|
|
|
- }
|
|
|
- if(layer.id.indexOf("-polygon") !== -1){
|
|
|
- window.mapboxMap.removeLayer(layer.id)
|
|
|
- }
|
|
|
- }
|
|
|
- })
|
|
|
- // 清除编辑标记
|
|
|
- that.clearEditMarkers();
|
|
|
- that.selectedFeature = null;
|
|
|
- that.selectedFeatureId = '';
|
|
|
- that.layersIds = [];
|
|
|
- },
|
|
|
-
|
|
|
- // 切换编辑模式
|
|
|
- toggleEditMode() {
|
|
|
- let that = this;
|
|
|
- that.isEditMode = !that.isEditMode;
|
|
|
- if (!that.isEditMode) {
|
|
|
- that.clearEditMarkers();
|
|
|
- that.selectedFeature = null;
|
|
|
- that.selectedFeatureId = '';
|
|
|
- that.layersIds = [];
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 处理图形点击
|
|
|
- handleFeatureClick(e) {
|
|
|
- let that = this;
|
|
|
-
|
|
|
- // 1. 获取绘制图层
|
|
|
- const drawLayers = window.mapboxMap.getStyle().layers
|
|
|
- .filter(layer => layer.id.startsWith('draw-'))
|
|
|
- .map(layer => layer.id);
|
|
|
-
|
|
|
- // 把点 → 扩大成 5px 矩形范围,更容易选中
|
|
|
- const point = e.point;
|
|
|
- const buffer = 5; // 拾取范围扩大 5 像素
|
|
|
- const box = [
|
|
|
- [point.x - buffer, point.y - buffer],
|
|
|
- [point.x + buffer, point.y + buffer]
|
|
|
- ];
|
|
|
- const features = window.mapboxMap.queryRenderedFeatures(box, {
|
|
|
- layers: drawLayers
|
|
|
- });
|
|
|
-
|
|
|
- if (features.length > 0) {
|
|
|
- const feature = features[0];
|
|
|
- const layerId = feature.layer.id;
|
|
|
- const featureId = layerId.split('-').slice(0, -1).join('-');
|
|
|
-
|
|
|
- if (!window.mapboxMap.getSource(featureId)) return;
|
|
|
-
|
|
|
- that.selectedFeatureId = featureId;
|
|
|
- const sourceData = window.mapboxMap.getSource(featureId)._data;
|
|
|
- that.selectedFeature = JSON.parse(JSON.stringify(sourceData));
|
|
|
- that.clearEditMarkers();
|
|
|
- if(that.selectedFeature.properties.drawType === 'distance' || that.selectedFeature.properties.drawType === 'area') return;
|
|
|
- that.showEditMarkers();
|
|
|
-
|
|
|
- // ----------- 清空旧事件 -----------
|
|
|
- if (that.dragEventHandlers) {
|
|
|
- window.mapboxMap.off('mousedown', that.dragEventHandlers.mousedown);
|
|
|
- window.mapboxMap.off('mousemove', that.dragEventHandlers.mousemove);
|
|
|
- window.mapboxMap.off('mouseup', that.dragEventHandlers.mouseup);
|
|
|
- }
|
|
|
-
|
|
|
- // ==============================================
|
|
|
- // 【正确】mousedown 时 动态绑定 mousemove
|
|
|
- // ==============================================
|
|
|
- const mousedownHandler = (evt) => {
|
|
|
-
|
|
|
- // ========== 安全查询要素:自动跳过不存在的图层 ==========
|
|
|
- that.layersIds = window.mapboxMap.getStyle().layers
|
|
|
- .filter(layer => layer.id.startsWith('draw-'))
|
|
|
- .map(layer => layer.id)
|
|
|
-
|
|
|
- // 🔥 关键:只保留真实存在的图层
|
|
|
- const realLayers = window.mapboxMap.getStyle().layers.map(i => i.id)
|
|
|
- const safeLayers = that.layersIds.filter(l => realLayers.includes(l))
|
|
|
- // 把点 → 扩大成 5px 矩形范围,更容易选中
|
|
|
- const point = evt.point;
|
|
|
- const buffer = 5; // 拾取范围扩大 5 像素
|
|
|
- const box = [
|
|
|
- [point.x - buffer, point.y - buffer],
|
|
|
- [point.x + buffer, point.y + buffer]
|
|
|
- ];
|
|
|
- const hit = window.mapboxMap.queryRenderedFeatures(box, {
|
|
|
- layers: safeLayers
|
|
|
- });
|
|
|
- // const hit = window.mapboxMap.queryRenderedFeatures(evt.point, { layers: [layerId] });
|
|
|
- if (hit.length === 0 || !that.selectedFeature) return;
|
|
|
- if(that.selectedFeature.properties.drawType === 'distance' || that.selectedFeature.properties.drawType === 'area') return;
|
|
|
- // 禁止地图默认拖拽(关键!!)
|
|
|
- evt.preventDefault();
|
|
|
- that.isDragWhole = true;
|
|
|
- that.lastLngLat = evt.lngLat;
|
|
|
- window.mapboxMap.getCanvas().style.cursor = 'grabbing';
|
|
|
- that.clearEditMarkers();
|
|
|
- // ========== 在这里绑定 mousemove ==========
|
|
|
- const mousemoveHandler = (moveEvt) => {
|
|
|
- if (!that.isDragWhole || !that.selectedFeature) return;
|
|
|
- if(that.selectedFeature.properties.drawType === 'distance' || that.selectedFeature.properties.drawType === 'area') return;
|
|
|
- window.mapboxMap.getCanvas().style.cursor = 'move';
|
|
|
-
|
|
|
- // 禁止地图默认行为
|
|
|
- moveEvt.preventDefault();
|
|
|
- const curCenter = moveEvt.lngLat;
|
|
|
- const dx = curCenter.lng - that.lastLngLat.lng;
|
|
|
- const dy = curCenter.lat - that.lastLngLat.lat;
|
|
|
-
|
|
|
- // 移动图形
|
|
|
- that.moveWholeFeature(dx, dy);
|
|
|
- that.lastLngLat = curCenter;
|
|
|
- };
|
|
|
-
|
|
|
- const mouseupHandler = () => {
|
|
|
- that.isDragWhole = false;
|
|
|
- window.mapboxMap.getCanvas().style.cursor = '';
|
|
|
- // 重新启用地图的默认拖拽行为
|
|
|
- window.mapboxMap.dragPan.enable();
|
|
|
- that.showEditMarkers();
|
|
|
-
|
|
|
- // 移除事件监听器
|
|
|
- window.mapboxMap.off('mousemove', mousemoveHandler);
|
|
|
- window.mapboxMap.off('mouseup', mouseupHandler);
|
|
|
- // console.log("======handleDrag=selectedFeature=======:", JSON.stringify(that.selectedFeature));
|
|
|
- // that.drawType = that.selectedFeature.properties.drawType;
|
|
|
- // that.addToMap(that.selectedFeature);
|
|
|
- // that.finishDraw();
|
|
|
- };
|
|
|
-
|
|
|
- // 绑定事件监听器
|
|
|
- window.mapboxMap.on('mousemove', mousemoveHandler);
|
|
|
- window.mapboxMap.on('mouseup', mouseupHandler);
|
|
|
- };
|
|
|
-
|
|
|
- // 只绑定 mousedown
|
|
|
- window.mapboxMap.on('mousedown', mousedownHandler);
|
|
|
- that.dragEventHandlers = { mousedown: mousedownHandler };
|
|
|
-
|
|
|
- } else {
|
|
|
- that.clearEditMarkers();
|
|
|
- that.selectedFeature = null;
|
|
|
- that.selectedFeatureId = '';
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 移动整个图形
|
|
|
- moveWholeFeature(dx, dy) {
|
|
|
- let that = this;
|
|
|
- if (!that.selectedFeature) return;
|
|
|
- const feature = that.selectedFeature;
|
|
|
-
|
|
|
- if (feature.geometry.type === 'Point') {
|
|
|
- feature.geometry.coordinates[0] += dx;
|
|
|
- feature.geometry.coordinates[1] += dy;
|
|
|
- } else if (feature.geometry.type === 'LineString') {
|
|
|
- feature.geometry.coordinates.forEach(coord => {
|
|
|
- coord[0] += dx;
|
|
|
- coord[1] += dy;
|
|
|
- });
|
|
|
- } else if (feature.geometry.type === 'Polygon') {
|
|
|
- // 检查是否是圆形(通过顶点数量和几何形状判断)
|
|
|
- // const isCircle = feature.geometry.coordinates[0].length > 30; // 圆形通常有64个顶点
|
|
|
-
|
|
|
- if(feature.properties.isCircle){
|
|
|
- // 圆形优化:只更新圆心位置,然后重新生成圆
|
|
|
- const centerPt = turf.centroid(feature);
|
|
|
- const center = centerPt.geometry.coordinates;
|
|
|
-
|
|
|
- // 计算新的圆心位置
|
|
|
- const newCenter = [center[0] + dx, center[1] + dy];
|
|
|
-
|
|
|
- // 计算半径
|
|
|
- const radiusMeters = turf.distance(center, feature.geometry.coordinates[0][0], { units: 'meters' });
|
|
|
-
|
|
|
- // 重新生成圆
|
|
|
- const newCircle = turf.circle(newCenter, radiusMeters, {
|
|
|
- units: 'meters',
|
|
|
- steps: 64 // 减少顶点数量,提高性能
|
|
|
- });
|
|
|
- // 复制属性
|
|
|
- newCircle.properties = feature.properties;
|
|
|
- // 更新数据
|
|
|
- that.selectedFeature = newCircle;
|
|
|
- } else {
|
|
|
- // 普通多边形:更新所有顶点
|
|
|
- feature.geometry.coordinates.forEach(ring => {
|
|
|
- ring.forEach(coord => {
|
|
|
- coord[0] += dx;
|
|
|
- coord[1] += dy;
|
|
|
- });
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // 更新地图上的图形
|
|
|
- window.mapboxMap.getSource(that.selectedFeatureId).setData(that.selectedFeature);
|
|
|
- const sourceData = window.mapboxMap.getSource(that.selectedFeatureId)._data;
|
|
|
- that.selectedFeature = JSON.parse(JSON.stringify(sourceData));
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- // 编辑圆顶点:拖拽 → 重新生成圆
|
|
|
- editCircleVertex(newPoint) {
|
|
|
- const that = this;
|
|
|
- const feature = that.selectedFeature;
|
|
|
-
|
|
|
- // 1. 获取圆心
|
|
|
- const centerPt = turf.centroid(feature);
|
|
|
- const center = centerPt.geometry.coordinates;
|
|
|
-
|
|
|
- // 2. 计算新半径(米)
|
|
|
- const radiusMeters = turf.distance(center, newPoint, { units: 'meters' }); //kilometers 单位千米 meters 单位:米
|
|
|
-
|
|
|
- // let radius = turf.distance(turf.point(center), turf.point(that.tempPoints[1]), { units: 'kilometers' })
|
|
|
- // let feature = turf.circle(center, radius, 64)
|
|
|
-
|
|
|
- // 最小半径限制
|
|
|
- if (radiusMeters < 5) return;
|
|
|
-
|
|
|
- // 3. 重新生成圆
|
|
|
- const newCircle = turf.circle(center, radiusMeters, {
|
|
|
- units: 'meters',
|
|
|
- steps: 64
|
|
|
- });
|
|
|
- newCircle.properties = feature.properties;
|
|
|
-
|
|
|
-
|
|
|
- // const mousePt = [e.lngLat.lng, e.lngLat.lat];
|
|
|
- // const lastPt = that.tempPoints[that.tempPoints.length - 1];
|
|
|
- // ============ Turf 生成辅助线 ============
|
|
|
- const line = turf.lineString([center, newPoint]);
|
|
|
- that.tempLineSource.setData(line);
|
|
|
-
|
|
|
- // 4. 替换数据
|
|
|
- that.selectedFeature = newCircle;
|
|
|
- return newCircle;
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- // 显示编辑节点
|
|
|
- showEditMarkers() {
|
|
|
- let that = this;
|
|
|
- if (!that.selectedFeature) return;
|
|
|
-
|
|
|
- const feature = that.selectedFeature;
|
|
|
- if (feature.type === 'Feature') {
|
|
|
- if (feature.geometry.type === 'Point') {
|
|
|
- // 点的编辑标记
|
|
|
- that.addEditMarker(feature.geometry.coordinates, 0);
|
|
|
- } else if (feature.geometry.type === 'LineString') {
|
|
|
- // 线的编辑标记
|
|
|
- feature.geometry.coordinates.forEach((coord, index) => {
|
|
|
- that.addEditMarker(coord, index);
|
|
|
- });
|
|
|
- } else if (feature.geometry.type === 'Polygon') {
|
|
|
- // 面的编辑标记
|
|
|
- feature.geometry.coordinates[0].forEach((coord, index) => {
|
|
|
- that.addEditMarker(coord, index);
|
|
|
- });
|
|
|
- }
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 添加编辑标记
|
|
|
- addEditMarker(coord, index) {
|
|
|
- let that = this;
|
|
|
- let el = document.createElement('div');
|
|
|
- el.className = 'edit-marker';
|
|
|
- el.style.width = '10px';
|
|
|
- el.style.height = '10px';
|
|
|
- el.style.borderRadius = '50%';
|
|
|
- el.style.backgroundColor = 'red';
|
|
|
- el.style.border = '2px solid white';
|
|
|
- el.style.cursor = 'pointer';
|
|
|
- // el.style.cursor = 'move';
|
|
|
-
|
|
|
- // 添加拖拽事件
|
|
|
- el.addEventListener('mousedown', (e) => {
|
|
|
- e.stopPropagation();
|
|
|
- that.isDragging = true;
|
|
|
- that.draggingPointIndex = index;
|
|
|
- window.mapboxMap.getCanvas().style.cursor = 'grabbing';
|
|
|
- });
|
|
|
-
|
|
|
- let marker = new mapboxgl.Marker(el)
|
|
|
- .setLngLat(coord)
|
|
|
- .addTo(window.mapboxMap);
|
|
|
- // 关键:提高层级(越大越在上层)
|
|
|
- // marker._element.style.zIndex = 2;
|
|
|
- that.editMarkers.push(marker);
|
|
|
-
|
|
|
- if(!that.selectedFeature.properties.isCircle){
|
|
|
- // 加上这一行即可开启【点击两头补点】
|
|
|
- that.insertTwoMiddlePoints(marker, index);
|
|
|
- }
|
|
|
- },
|
|
|
-
|
|
|
- // 清除编辑标记
|
|
|
- clearEditMarkers() {
|
|
|
- let that = this;
|
|
|
- that.editMarkers.forEach(marker => marker.remove());
|
|
|
- that.editMarkers = [];
|
|
|
- },
|
|
|
-
|
|
|
- insertTwoMiddlePoints(marker, index) {
|
|
|
- const that = this;
|
|
|
- marker.getElement().addEventListener('click', (e) => {
|
|
|
- e.stopPropagation();
|
|
|
- if (!that.selectedFeature) return;
|
|
|
-
|
|
|
- const geom = that.selectedFeature.geometry;
|
|
|
- let coords = [];
|
|
|
- let isPolygon = false;
|
|
|
-
|
|
|
- if (geom.type === 'LineString') {
|
|
|
- coords = [...geom.coordinates];
|
|
|
- } else if (geom.type === 'Polygon') {
|
|
|
- coords = [...geom.coordinates[0]];
|
|
|
- isPolygon = true;
|
|
|
- }
|
|
|
- if (coords.length < 2) return;
|
|
|
-
|
|
|
- // ------------------------------
|
|
|
- // 🔥 核心修复:面闭合点专用逻辑
|
|
|
- // ------------------------------
|
|
|
- if (isPolygon) {
|
|
|
- // 面:移除闭合点,纯顶点数组操作
|
|
|
- let pureCoords = coords.slice(0, -1);
|
|
|
- const total = pureCoords.length;
|
|
|
-
|
|
|
- // 点击的是闭合点(最后一个点)→ 映射到真实起点 0
|
|
|
- let realIndex = index;
|
|
|
- if (index === coords.length - 1) {
|
|
|
- realIndex = 0;
|
|
|
- }
|
|
|
-
|
|
|
- // 自动计算前后索引(闭合循环)
|
|
|
- const prevIdx = (realIndex - 1 + total) % total;
|
|
|
- const nextIdx = (realIndex + 1) % total;
|
|
|
-
|
|
|
- const prevPt = pureCoords[prevIdx];
|
|
|
- const currPt = pureCoords[realIndex];
|
|
|
- const nextPt = pureCoords[nextIdx];
|
|
|
-
|
|
|
- // 计算两个中点
|
|
|
- const midPrev = [(prevPt[0] + currPt[0]) / 2, (prevPt[1] + currPt[1]) / 2];
|
|
|
- const midNext = [(currPt[0] + nextPt[0]) / 2, (currPt[1] + nextPt[1]) / 2];
|
|
|
-
|
|
|
- // 插入
|
|
|
- pureCoords.splice(realIndex + 1, 0, midNext);
|
|
|
- pureCoords.splice(realIndex, 0, midPrev);
|
|
|
-
|
|
|
- // 重新闭合
|
|
|
- coords = [...pureCoords, pureCoords[0]];
|
|
|
- geom.coordinates[0] = coords;
|
|
|
- }
|
|
|
- // ------------------------------
|
|
|
- // 线逻辑(保持不变,已修复)
|
|
|
- // ------------------------------
|
|
|
- else {
|
|
|
- if (index === 0) {
|
|
|
- const nextPt = coords[1];
|
|
|
- const midNext = [(coords[0][0] + nextPt[0]) / 2, (coords[0][1] + nextPt[1]) / 2];
|
|
|
- coords.splice(1, 0, midNext);
|
|
|
- } else {
|
|
|
- const prevPt = coords[index - 1];
|
|
|
- const midPrev = [(prevPt[0] + coords[index][0]) / 2, (prevPt[1] + coords[index][1]) / 2];
|
|
|
- coords.splice(index, 0, midPrev);
|
|
|
-
|
|
|
- if (index < coords.length - 1) {
|
|
|
- const nextPt = coords[index + 1];
|
|
|
- const midNext = [(coords[index][0] + nextPt[0]) / 2, (coords[index][1] + nextPt[1]) / 2];
|
|
|
- coords.splice(index + 1, 0, midNext);
|
|
|
- }
|
|
|
- }
|
|
|
- geom.coordinates = coords;
|
|
|
- }
|
|
|
-
|
|
|
- that.refreshFeature();
|
|
|
- });
|
|
|
- },
|
|
|
-
|
|
|
- // ==========================
|
|
|
- // 统一刷新图形 + 顶点
|
|
|
- // ==========================
|
|
|
- refreshFeature() {
|
|
|
- const that = this;
|
|
|
- if (!that.selectedFeature || !that.selectedFeatureId) return;
|
|
|
-
|
|
|
- // 更新地图
|
|
|
- window.mapboxMap.getSource(that.selectedFeatureId).setData(that.selectedFeature);
|
|
|
- const sourceData = window.mapboxMap.getSource(that.selectedFeatureId)._data;
|
|
|
- that.selectedFeature = JSON.parse(JSON.stringify(sourceData));
|
|
|
-
|
|
|
- // 重新生成编辑顶点
|
|
|
- that.clearEditMarkers();
|
|
|
- that.showEditMarkers();
|
|
|
- },
|
|
|
-
|
|
|
- // 处理拖拽
|
|
|
- handleDrag(e) {
|
|
|
- let that = this;
|
|
|
- if (!that.selectedFeature || that.draggingPointIndex === -1) return;
|
|
|
- if(that.selectedFeature.properties.drawType === 'distance' || that.selectedFeature.properties.drawType === 'area') return;
|
|
|
- let feature = that.selectedFeature;
|
|
|
-
|
|
|
- let newCoord = [e.lngLat.lng, e.lngLat.lat];
|
|
|
- if(that.selectedFeature.properties.isCircle){
|
|
|
- that.clearEditMarkers();
|
|
|
- // 圆:重新生成
|
|
|
- feature = that.editCircleVertex(newCoord);
|
|
|
- }else{
|
|
|
- if (feature.geometry.type === 'Point') {
|
|
|
- feature.geometry.coordinates = newCoord;
|
|
|
- } else if (feature.geometry.type === 'LineString') {
|
|
|
- feature.geometry.coordinates[that.draggingPointIndex] = newCoord;
|
|
|
- } else if (feature.geometry.type === 'Polygon') {
|
|
|
- feature.geometry.coordinates[0][that.draggingPointIndex] = newCoord;
|
|
|
- // 确保多边形闭合
|
|
|
- if (that.draggingPointIndex === 0) {
|
|
|
- feature.geometry.coordinates[0][feature.geometry.coordinates[0].length - 1] = newCoord;
|
|
|
- }
|
|
|
- }
|
|
|
- // 更新编辑标记位置
|
|
|
- that.clearEditMarkers();
|
|
|
- that.showEditMarkers();
|
|
|
- }
|
|
|
- // console.log("======handleDrag=feature=======:", JSON.stringify(feature));
|
|
|
- // 更新地图上的图形
|
|
|
- window.mapboxMap.getSource(that.selectedFeatureId).setData(feature);
|
|
|
- // that.drawType = that.selectedFeature.properties.drawType;
|
|
|
- // that.addToMap(that.selectedFeature);
|
|
|
- // that.finishDraw();
|
|
|
-
|
|
|
-
|
|
|
- },
|
|
|
-
|
|
|
- // 删除选中图形
|
|
|
- deleteSelected() {
|
|
|
- let that = this;
|
|
|
- if (!that.selectedFeatureId) return;
|
|
|
-
|
|
|
- // 移除相关图层
|
|
|
- const layers = window.mapboxMap.getStyle().layers;
|
|
|
- layers.forEach(layer => {
|
|
|
- if (layer.id.startsWith(that.selectedFeatureId)) {
|
|
|
- window.mapboxMap.removeLayer(layer.id);
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- // 移除数据源
|
|
|
- window.mapboxMap.removeSource(that.selectedFeatureId);
|
|
|
-
|
|
|
- // 清除编辑标记
|
|
|
- that.clearEditMarkers();
|
|
|
- that.selectedFeature = null;
|
|
|
- that.selectedFeatureId = '';
|
|
|
- that.layersIds = [];
|
|
|
- },
|
|
|
-
|
|
|
- }
|
|
|
-};
|
|
|
-</script>
|
|
|
-
|
|
|
-<style lang="less" scoped>
|
|
|
-
|
|
|
-/* 工具栏 + 玻璃样式 */
|
|
|
-.tool-bar {
|
|
|
- // position: absolute;
|
|
|
- // top: 20px;
|
|
|
- // left: 50%;
|
|
|
- // transform: translateX(-50%);
|
|
|
- // z-index: 999;
|
|
|
- // padding: 12px 20px;
|
|
|
- // display: flex;
|
|
|
- // gap: 10px;
|
|
|
- position: absolute;
|
|
|
- top: 60px;
|
|
|
- right: 0px;
|
|
|
- z-index: 999;
|
|
|
- padding: 12px 20px;
|
|
|
- display: grid;
|
|
|
- gap: 5px;
|
|
|
- width: 80px;
|
|
|
-}
|
|
|
-.tool-bar button {
|
|
|
- padding: 8px;
|
|
|
- background: rgba(255,255,255,0.25);
|
|
|
- border: 1px solid rgba(255,255,255,0.3);
|
|
|
- border-radius: 8px;
|
|
|
- color: #222;
|
|
|
- cursor: pointer;
|
|
|
- transition: all 0.2s;
|
|
|
- -webkit-backdrop-filter: blur(10px);
|
|
|
- backdrop-filter: blur(10px);
|
|
|
-}
|
|
|
-.tool-bar button:hover {
|
|
|
- background: rgba(255,255,255,0.8);
|
|
|
-}
|
|
|
-
|
|
|
-.tool-bar button.active {
|
|
|
- background: rgba(0,102,255,0.8);
|
|
|
- color: white;
|
|
|
-}
|
|
|
-
|
|
|
-.tool-bar button:disabled {
|
|
|
- opacity: 0.5;
|
|
|
- cursor: not-allowed;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-</style>
|
|
|
-
|
|
|
-<style>
|
|
|
-/*节点标签样式 */
|
|
|
-.tips-label {
|
|
|
- position: absolute;
|
|
|
- background: #0066ff;
|
|
|
- color: white;
|
|
|
- padding: 4px 10px;
|
|
|
- border-radius: 4px;
|
|
|
- font-size: 14px;
|
|
|
- pointer-events: none; /* 不影响鼠标悬浮 */
|
|
|
- transform: translate(-50%, -50%);
|
|
|
- white-space: nowrap;
|
|
|
-}
|
|
|
-
|
|
|
-/* 编辑标记样式 */
|
|
|
-.edit-marker {
|
|
|
- z-index: 2;
|
|
|
- cursor: pointer;
|
|
|
-}
|
|
|
-</style>
|