diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 48d3bcd..2690139 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -201,7 +201,7 @@ packages: resolution: {integrity: sha512-iHmwV3QcVGGvSC1BG5bZ4z6iwa1SOpAPWmnjOErd4Ske+lZua5K9TtAVdx0gMBClJ28DViCbSmZitjWZsWO3LA==} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: - vite: ^5.0.0 || ^6.0.0 || ^7.0.0 + vite: npm:rolldown-vite@7.2.5 vue: ^3.2.25 '@vue/compiler-core@3.5.25': diff --git a/src/App.vue b/src/App.vue index bee00b7..cad9d01 100644 --- a/src/App.vue +++ b/src/App.vue @@ -88,8 +88,7 @@ class="fence-item" > - {{ getTypeName(fence.type) }} - #{{ fence.id.slice(0, 8) }} + {{ fence.name || '未命名围栏' }}
{ AMap = await AMapLoader.load({ key: mapKey, version: '2.0', - plugins: ['AMap.MouseTool', 'AMap.Polygon', 'AMap.Rectangle', 'AMap.Circle', 'AMap.Marker', 'AMap.Icon'] + plugins: ['AMap.MouseTool', 'AMap.Polygon', 'AMap.Rectangle', 'AMap.Circle', 'AMap.Marker', 'AMap.Icon', 'AMap.Text'] }) // 创建地图实例 @@ -520,25 +519,17 @@ const handleDrawComplete = (event) => { } if (fence) { - // 设置围栏样式 - if (fence.shape.setOptions) { - fence.shape.setOptions({ - strokeColor: '#0066FF', - strokeWeight: 2, - strokeOpacity: 0.8, - fillColor: '#0066FF', - fillOpacity: 0.15, - zIndex: 50 - }) - } - // 注意:使用MouseTool绘制的对象已经自动添加到地图上 // 如果对象还没有添加到地图,则添加它 if (!fence.shape.getMap()) { map.add(fence.shape) } - // 保存围栏 + // 先设置默认名称,确保列表能立即显示 + const defaultName = `${getTypeName(fence.type)}_${fences.value.length + 1}` + fence.name = defaultName + + // 保存围栏到列表(立即显示) fences.value.push(fence) // 关闭绘制工具,确保不能再继续绘制 @@ -547,25 +538,36 @@ const handleDrawComplete = (event) => { mouseTool.close() // 关闭绘制工具,但不删除已绘制的图形 } - // 确保图形显示(延迟设置,确保mouseTool关闭后图形仍然存在) - setTimeout(() => { - if (fence.shape && fence.shape.getMap()) { - // 重新设置样式确保显示 - fence.shape.setOptions({ - strokeColor: '#0066FF', - strokeWeight: 2, - strokeOpacity: 0.8, - fillColor: '#0066FF', - fillOpacity: 0.15, - zIndex: 50, - visible: true // 确保可见 - }) - // 确保在地图上 - if (!fence.shape.getMap()) { - map.add(fence.shape) + // 先更新样式和标签(使用默认名称) + updateFenceStyleAndLabel(fence) + + // 获取当前围栏在数组中的索引,用于后续更新 + const fenceIndex = fences.value.length - 1 + + // 弹出输入框让用户输入围栏名称(异步,不阻塞列表显示) + ElMessageBox.prompt('请输入围栏名称', '围栏命名', { + confirmButtonText: '确定', + cancelButtonText: '取消', + inputPlaceholder: '请输入围栏名称', + inputValue: defaultName, + inputValidator: (value) => { + if (!value || value.trim() === '') { + return '围栏名称不能为空' } + return true } - }, 50) + }).then(({ value }) => { + // 用户输入了名称,通过数组索引更新,确保Vue响应式系统能检测到变化 + const newName = value.trim() + if (fences.value[fenceIndex]) { + fences.value[fenceIndex].name = newName + // 更新标签 + addFenceLabel(fences.value[fenceIndex]) + } + }).catch(() => { + // 用户取消,保持默认名称(已经在上面设置了) + // 不需要额外操作,标签已经用默认名称创建了 + }) // 重置绘制状态 isDrawing.value = false @@ -575,6 +577,112 @@ const handleDrawComplete = (event) => { } } +// 更新围栏样式和标签 +const updateFenceStyleAndLabel = (fence) => { + // 设置围栏样式 + if (fence.shape.setOptions) { + fence.shape.setOptions({ + strokeColor: '#0066FF', + strokeWeight: 2, + strokeOpacity: 0.8, + fillColor: '#0066FF', + fillOpacity: 0.15, + zIndex: 50, + visible: true + }) + } + + // 确保在地图上 + if (!fence.shape.getMap()) { + map.add(fence.shape) + } + + // 添加围栏名称标签到地图上 + addFenceLabel(fence) +} + +// 添加围栏名称标签 +const addFenceLabel = (fence) => { + if (!map || !AMap || !fence.name) return + + // 移除旧的标签(如果存在) + if (fence.label) { + map.remove(fence.label) + } + + // 计算标签位置 + let labelPosition = null + + if (fence.type === 'polygon') { + // 多边形:使用bounds的右上角(东北角) + const bounds = fence.shape.getBounds ? fence.shape.getBounds() : null + if (bounds) { + const ne = bounds.getNorthEast() + labelPosition = [ne.getLng(), ne.getLat()] + } else { + // 如果没有bounds,使用第一个顶点 + const path = fence.shape.getPath ? fence.shape.getPath() : fence.path + if (path && path.length > 0) { + const firstPoint = path[0] + if (Array.isArray(firstPoint)) { + labelPosition = firstPoint + } else if (firstPoint.getLng && firstPoint.getLat) { + labelPosition = [firstPoint.getLng(), firstPoint.getLat()] + } else { + labelPosition = [firstPoint.lng || firstPoint[0], firstPoint.lat || firstPoint[1]] + } + } + } + } else if (fence.type === 'rectangle') { + // 矩形:使用bounds的右上角(东北角) + const bounds = fence.shape.getBounds ? fence.shape.getBounds() : fence.bounds + if (bounds) { + const ne = bounds.getNorthEast() + labelPosition = [ne.getLng(), ne.getLat()] + } + } else if (fence.type === 'circle') { + // 圆形:使用圆心,然后向上偏移到右上角位置 + const center = fence.shape.getCenter ? fence.shape.getCenter() : fence.center + const radius = fence.shape.getRadius ? fence.shape.getRadius() : fence.radius + if (center) { + let centerLng, centerLat + if (center.getLng && center.getLat) { + centerLng = center.getLng() + centerLat = center.getLat() + } else { + centerLng = center.lng || center[0] + centerLat = center.lat || center[1] + } + // 计算右上角位置(圆心 + 半径的偏移) + const offset = radius / 111320 // 转换为经纬度偏移 + labelPosition = [centerLng + offset, centerLat + offset] + } + } + + if (labelPosition) { + // 创建文本标签,位置在右上角 + const label = new AMap.Text({ + text: fence.name, + position: labelPosition, + offset: new AMap.Pixel(-10, -10), // 偏移到右上角 + style: { + padding: '4px 8px', + backgroundColor: 'rgba(0, 102, 255, 0.6)', // 半透明背景 + border: '1px solid rgba(0, 102, 255, 0.8)', + borderRadius: '4px', + fontSize: '12px', + color: '#fff', + fontWeight: 'bold', + whiteSpace: 'nowrap' + }, + zIndex: 100 + }) + + map.add(label) + fence.label = label + } +} + // 开始绘制多边形 const startDrawPolygon = () => { if (!mouseTool) return @@ -803,6 +911,10 @@ const removeFence = (fenceId) => { const currentMap = fence.shape.getMap ? fence.shape.getMap() : null if (currentMap) { currentMap.remove(fence.shape) + // 同时移除标签 + if (fence.label) { + currentMap.remove(fence.label) + } } } catch (error) { console.error('删除围栏时出错:', error) @@ -854,6 +966,10 @@ const clearAllFences = async () => { const currentMap = fence.shape.getMap ? fence.shape.getMap() : null if (currentMap) { currentMap.remove(fence.shape) + // 同时移除标签 + if (fence.label) { + currentMap.remove(fence.label) + } } } catch (error) { console.error('清除围栏时出错:', error) @@ -1025,6 +1141,13 @@ onUnmounted(() => { font-weight: 500; } +.fence-name { + font-size: 12px; + color: #333; + font-weight: 500; + margin-left: 4px; +} + .drone-status-panel { margin-top: 16px; margin-bottom: 16px;