feat:增加围栏名称

This commit is contained in:
2025-12-07 10:57:14 +08:00
parent 478642e780
commit 55741c3f3a
2 changed files with 157 additions and 34 deletions

2
pnpm-lock.yaml generated
View File

@@ -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':

View File

@@ -88,8 +88,7 @@
class="fence-item"
>
<span class="fence-info">
<span class="fence-type">{{ getTypeName(fence.type) }}</span>
#{{ fence.id.slice(0, 8) }}
<span class="fence-name">{{ fence.name || '未命名围栏' }}</span>
</span>
<div class="fence-actions">
<el-button
@@ -164,7 +163,7 @@ const initMap = async () => {
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']
})
// 创建地图实例
@@ -498,25 +497,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)
// 关闭绘制工具,确保不能再继续绘制
@@ -525,10 +516,49 @@ const handleDrawComplete = (event) => {
mouseTool.close() // 关闭绘制工具,但不删除已绘制的图形
}
// 确保图形显示延迟设置确保mouseTool关闭后图形仍然存在
setTimeout(() => {
if (fence.shape && fence.shape.getMap()) {
// 重新设置样式确保显示
// 先更新样式和标签(使用默认名称
updateFenceStyleAndLabel(fence)
// 获取当前围栏在数组中的索引,用于后续更新
const fenceIndex = fences.value.length - 1
// 弹出输入框让用户输入围栏名称(异步,不阻塞列表显示)
ElMessageBox.prompt('请输入围栏名称', '围栏命名', {
confirmButtonText: '确定',
cancelButtonText: '取消',
inputPlaceholder: '请输入围栏名称',
inputValue: defaultName,
inputValidator: (value) => {
if (!value || value.trim() === '') {
return '围栏名称不能为空'
}
return true
}
}).then(({ value }) => {
// 用户输入了名称通过数组索引更新确保Vue响应式系统能检测到变化
const newName = value.trim()
if (fences.value[fenceIndex]) {
fences.value[fenceIndex].name = newName
// 更新标签
addFenceLabel(fences.value[fenceIndex])
}
}).catch(() => {
// 用户取消,保持默认名称(已经在上面设置了)
// 不需要额外操作,标签已经用默认名称创建了
})
// 重置绘制状态
isDrawing.value = false
currentDrawType.value = null
console.log('围栏绘制完成:', fence)
}
}
// 更新围栏样式和标签
const updateFenceStyleAndLabel = (fence) => {
// 设置围栏样式
if (fence.shape.setOptions) {
fence.shape.setOptions({
strokeColor: '#0066FF',
strokeWeight: 2,
@@ -536,20 +566,98 @@ const handleDrawComplete = (event) => {
fillColor: '#0066FF',
fillOpacity: 0.15,
zIndex: 50,
visible: true // 确保可见
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)
}
}, 50)
// 重置绘制状态
isDrawing.value = false
currentDrawType.value = null
// 计算标签位置
let labelPosition = null
console.log('围栏绘制完成:', fence)
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
}
}
@@ -781,6 +889,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)
@@ -832,6 +944,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)
@@ -1003,6 +1119,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;