From 89380e6260a75d1d3b94de687ebcc2f50d50659d Mon Sep 17 00:00:00 2001
From: shuishen <1109946754@qq.com>
Date: Tue, 03 Feb 2026 15:44:33 +0800
Subject: [PATCH] feat:环境变量配置调整

---
 applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue |  422 +++++++++++++++++++++++++++++++++++++++++-----------
 1 files changed, 332 insertions(+), 90 deletions(-)

diff --git a/applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue b/applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue
index 7a186c3..342b43c 100644
--- a/applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue
+++ b/applications/drone-command/src/views/areaManage/partition/FormDiaLog.vue
@@ -23,22 +23,26 @@
 				</div>
 				<div class="map-toolbar">
 					<el-tooltip content="多边形" placement="left">
-						<button class="tool-btn" type="button" @click="handleDraw('polygon')">
+						<button class="tool-btn" :class="{ active: currentShapeType === 'polygon' }" type="button"
+							@click="handleDraw('polygon')">
 							<img class="tool-icon-image" :src="polygonIcon" alt="多边形" />
 						</button>
 					</el-tooltip>
 					<el-tooltip content="矩形" placement="left">
-						<button class="tool-btn" type="button" @click="handleDraw('rectangle')">
+						<button class="tool-btn" :class="{ active: currentShapeType === 'rectangle' }" type="button"
+							@click="handleDraw('rectangle')">
 							<img class="tool-icon-image" :src="rectIcon" alt="矩形" />
 						</button>
 					</el-tooltip>
 					<el-tooltip content="椭圆" placement="left">
-						<button class="tool-btn" type="button" @click="handleDraw('ellipse')">
+						<button class="tool-btn" :class="{ active: currentShapeType === 'ellipse' }" type="button"
+							@click="handleDraw('ellipse')">
 							<img class="tool-icon-image" :src="ellipseIcon" alt="椭圆" />
 						</button>
 					</el-tooltip>
 					<el-tooltip content="缓冲圆" placement="left">
-						<button class="tool-btn" type="button" @click="handleDraw('buffer')">
+						<button class="tool-btn" :class="{ active: currentShapeType === 'buffer' }" type="button"
+							@click="handleDraw('buffer')">
 							<img class="tool-icon-image" :src="bufferIcon" alt="缓冲圆" />
 						</button>
 					</el-tooltip>
@@ -156,7 +160,8 @@
 					</el-form-item>
 					<el-form-item label="关联派出所" prop="policeStationId">
 						<el-select class="command-select" popper-class="command-select-popper"
-							v-model="formData.policeStationId" placeholder="请选择" clearable>
+							v-model="formData.policeStationId" placeholder="请选择" clearable filterable remote
+							:remote-method="handlePoliceStationSearch" :loading="policeStationLoading">
 							<el-option v-for="item in policeStationOptions" :key="item.id" :label="item.stationName"
 								:value="item.id" />
 						</el-select>
@@ -226,14 +231,19 @@
 <script setup>
 import { Close } from '@element-plus/icons-vue'
 
-import { computed, inject, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
+import { computed, inject, nextTick, onBeforeUnmount, ref, watch } from 'vue'
 import { ElMessage } from 'element-plus'
 import { fwAreaDivideDetailApi, fwAreaDivideSubmitApi } from './partitionApi'
 import { saveOperationLog } from '@ztzf/apis'
 import { useRoute } from 'vue-router'
 import { fieldRules, geomAnalysis, getDictLabel } from '@ztzf/utils'
 import CommonCesiumMap from '@/components/map-container/common-cesium-map.vue'
-import { DrawManager, EditManager, buildEllipsePositions, formatBufferRadius } from '@/utils/cesium/shapeTools'
+import {
+	DrawManager,
+	EditManager,
+	addBufferRadiusLabelEntity,
+	buildEllipsePositions,
+} from '@/utils/cesium/shapeTools'
 import { cartesian3Convert } from '@/utils/cesium/mapUtil'
 import {
 	createDeviceRangePrimitive,
@@ -244,12 +254,13 @@
 import * as Cesium from 'cesium'
 import { AREA_TYPE_STYLE_MAP, BUFFER_LEVEL_STYLES, DEFAULT_AREA_STYLE } from '@ztzf/constants'
 import { fwPoliceStationListApi } from '@/views/areaManage/precinctInfo/precinctInfoApi'
-import { fwDeviceListApi } from '@/views/basicManage/deviceStock/fwDevice'
+import { fwDeviceListByPolygonsApi } from '@/views/basicManage/deviceStock/fwDevice'
 import polygonIcon from '@/assets/images/areaMap/polygon.png'
 import rectIcon from '@/assets/images/areaMap/rect.png'
 import ellipseIcon from '@/assets/images/areaMap/ellipse.png'
 import bufferIcon from '@/assets/images/areaMap/buffer.png'
 import trashIcon from '@/assets/images/areaMap/trash.png'
+import equipmentIcon from '@/assets/images/dataCockpit/map/equipment.png'
 
 const initForm = () => ({
 	areaName: '', // 区域名称
@@ -276,6 +287,10 @@
 const searchName = ref('')
 const selectedDeviceRows = ref([])
 const policeStationOptions = ref([]) // 关联派出所
+const policeStationAllOptions = ref([])
+const policeStationLoading = ref(false)
+const policeStationSearchTimer = ref(null)
+const policeStationSearchKeyword = ref('')
 const deviceOptions = ref([]) // 关联设备
 const dictObj = inject('dictObj')
 const mapRef = ref(null)
@@ -300,10 +315,14 @@
 let viewEntity
 let shapeDisplaySource
 const deviceRangePrimitiveMap = new Map()
+const deviceIconEntityMap = new Map()
 const activeToolMode = ref('')
 const suppressDeviceFly = ref(false)
+const isDialogInitializing = ref(false)
 let lastSelectedDeviceIds = []
 let deviceRangeRenderToken = 0
+let deviceListTimer = null
+let deviceListRequestToken = 0
 
 const rules = {
 	areaName: fieldRules(true, 50),
@@ -406,12 +425,21 @@
 	() => visible.value,
 	val => {
 		if (!val) {
+			if (deviceListTimer) {
+				clearTimeout(deviceListTimer)
+				deviceListTimer = null
+			}
+			if (policeStationSearchTimer.value) {
+				clearTimeout(policeStationSearchTimer.value)
+				policeStationSearchTimer.value = null
+			}
 			viewEntity && viewer?.entities?.remove(viewEntity)
 			viewEntity = null
 			clearActiveTool()
 			hideTypePanel()
 			clearShapeDisplay()
 			clearDeviceRangePrimitives()
+			clearDeviceIconEntities()
 		}
 	}
 )
@@ -420,6 +448,14 @@
 	val => {
 		updateActiveShapeAreaType(val)
 	}
+)
+watch(
+	() => shapeList.value,
+	() => {
+		if (isDialogInitializing.value) return
+		queueDeviceListRefresh()
+	},
+	{ deep: true }
 )
 
 // 加载详情
@@ -471,11 +507,115 @@
 	deviceRangePrimitiveMap.clear()
 }
 
+function clearDeviceIconEntities () {
+	if (!viewer || !deviceIconEntityMap.size) return
+	for (const entity of deviceIconEntityMap.values()) {
+		viewer.entities.remove(entity)
+	}
+	deviceIconEntityMap.clear()
+}
+
+function clearDeviceListState () {
+	deviceOptions.value = []
+	selectedDeviceRows.value = []
+	formData.value.deviceIds = ''
+	clearDeviceRangePrimitives()
+	clearDeviceIconEntities()
+	if (deviceTableRef.value) {
+		deviceTableRef.value.clearSelection()
+	}
+}
+
+function formatWktNumber (value) {
+	if (!Number.isFinite(Number(value))) return null
+	return Number(value).toFixed(6)
+}
+
+function isSameWktPoint (left, right) {
+	if (!left || !right) return false
+	return Math.abs(left.lat - right.lat) < 0.000001 && Math.abs(left.lng - right.lng) < 0.000001
+}
+
+function buildPolygonWktFromPoints (points) {
+	if (!Array.isArray(points) || points.length < 3) return null
+	const ring = points
+		.map(resolveLngLatPoint)
+		.filter(Boolean)
+		.map(point => ({ lng: point.lng, lat: point.lat }))
+	if (ring.length < 3) return null
+	const first = ring[0]
+	const last = ring[ring.length - 1]
+	if (!isSameWktPoint(first, last)) {
+		ring.push({ ...first })
+	}
+	const coordText = ring
+		.map(point => {
+			const lng = formatWktNumber(point.lng)
+			const lat = formatWktNumber(point.lat)
+			if (lat === null || lng === null) return null
+			return `${lng} ${lat}`
+		})
+		.filter(Boolean)
+		.join(', ')
+	if (!coordText) return null
+	return `POLYGON((${coordText}))`
+}
+
+function buildEllipseWktFromMeta (center, semiMajor, semiMinor) {
+	if (!viewer) return null
+	const resolvedCenter = resolveLngLatPoint(center)
+	if (!resolvedCenter) return null
+	if (!Number.isFinite(Number(semiMajor)) || !Number.isFinite(Number(semiMinor))) return null
+	const centerCartesian = Cesium.Cartesian3.fromDegrees(
+		resolvedCenter.lng,
+		resolvedCenter.lat,
+		resolvedCenter.height || 0
+	)
+	const positions = buildEllipsePositions(centerCartesian, Number(semiMajor), Number(semiMinor))
+	if (!positions.length) return null
+	const points = positions
+		.map(item => {
+			const converted = cartesian3Convert(item, viewer)
+			return { lng: converted.longitude, lat: converted.latitude }
+		})
+		.filter(Boolean)
+	return buildPolygonWktFromPoints(points)
+}
+
+function resolveShapePolygonsWkt () {
+	const polygons = []
+	shapeList.value.forEach(shape => {
+		if (!shape) return
+		if (shape.drawType === 'buffer') {
+			const radii = Array.isArray(shape?.meta?.bufferRadii)
+				? shape.meta.bufferRadii.filter(item => Number.isFinite(item) && item > 0)
+				: []
+			const maxRadius = radii.length ? Math.max(...radii) : null
+			const wkt = buildEllipseWktFromMeta(shape?.meta?.center, maxRadius, maxRadius)
+			if (wkt) polygons.push(wkt)
+			return
+		}
+		if (shape.drawType === 'ellipse') {
+			const wkt = buildEllipseWktFromMeta(shape?.meta?.center, shape?.meta?.semiMajor, shape?.meta?.semiMinor)
+			if (wkt) {
+				polygons.push(wkt)
+				return
+			}
+		}
+		const points = getShapeDisplayPoints(shape)
+		const wkt = buildPolygonWktFromPoints(points)
+		if (wkt) polygons.push(wkt)
+	})
+	return polygons
+}
+
 async function renderDeviceRangeSingle (device) {
 	if (!viewer) return
 	clearDeviceRangePrimitives()
+	clearDeviceIconEntities()
 	const position = getDeviceLngLat(device)
 	if (!position) return
+	addDeviceIconEntity(device, position)
 	const rangeMeters = normalizeDeviceRange(device.effectiveRangeKm)
 	const primitive = await createDeviceRangePrimitive(viewer, position, rangeMeters)
 	if (!primitive) return
@@ -495,6 +635,12 @@
 			deviceRangePrimitiveMap.delete(id)
 		}
 	}
+	for (const [id, entity] of deviceIconEntityMap.entries()) {
+		if (!nextIds.has(id)) {
+			viewer.entities.remove(entity)
+			deviceIconEntityMap.delete(id)
+		}
+	}
 	const pendingRows = selectedRows.filter(row => {
 		const id = String(row?.id)
 		return id && !deviceRangePrimitiveMap.has(id)
@@ -503,14 +649,17 @@
 	const primitives = await Promise.all(
 		pendingRows.map(async row => {
 			const position = getDeviceLngLat(row)
-			if (!position) return { row, primitive: null }
+			if (!position) return { row, primitive: null, position: null }
 			const rangeMeters = normalizeDeviceRange(row.effectiveRangeKm)
 			const primitive = await createDeviceRangePrimitive(viewer, position, rangeMeters)
-			return { row, primitive }
+			return { row, primitive, position }
 		})
 	)
 	if (renderToken !== deviceRangeRenderToken) return
-	primitives.forEach(({ row, primitive }) => {
+	primitives.forEach(({ row, primitive, position }) => {
+		if (position) {
+			addDeviceIconEntity(row, position)
+		}
 		if (!primitive) return
 		const id = String(row?.id)
 		if (!nextIds.has(id)) return
@@ -540,6 +689,23 @@
 function clearShapeDisplay () {
 	if (!shapeDisplaySource) return
 	shapeDisplaySource.entities.removeAll()
+}
+
+function addDeviceIconEntity (device, position) {
+	if (!viewer || !position) return
+	const id = String(device?.id ?? '')
+	if (!id || deviceIconEntityMap.has(id)) return
+	const entity = viewer.entities.add({
+		position: Cesium.Cartesian3.fromDegrees(position.lng, position.lat, position.height || 0),
+		billboard: {
+			image: equipmentIcon,
+			width: 28,
+			height: 38,
+			verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
+			heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
+		},
+	})
+	deviceIconEntityMap.set(id, entity)
 }
 
 function normalizeShapePoint (point) {
@@ -609,35 +775,6 @@
 	return AREA_TYPE_STYLE_MAP?.[String(areaType)] || DEFAULT_AREA_STYLE
 }
 
-function getBufferRadiusPoint (centerCartesian, radius) {
-	const frame = Cesium.Transforms.eastNorthUpToFixedFrame(centerCartesian)
-	const local = new Cesium.Cartesian3(radius, 0, 0)
-	return Cesium.Matrix4.multiplyByPoint(frame, local, new Cesium.Cartesian3())
-}
-
-function addBufferRadiusLabel (dataSource, centerCartesian, radius) {
-	dataSource.entities.add({
-		name: 'shape-display',
-		customData: { drawType: 'buffer', label: 'radius' },
-		position: getBufferRadiusPoint(centerCartesian, radius),
-		label: {
-			text: formatBufferRadius(radius),
-			font: '14px Source Han Sans CN',
-			fillColor: Cesium.Color.WHITE,
-			outlineColor: Cesium.Color.fromBytes(24, 48, 92, 255),
-			outlineWidth: 2,
-			style: Cesium.LabelStyle.FILL_AND_OUTLINE,
-			showBackground: true,
-			backgroundColor: Cesium.Color.fromBytes(8, 12, 22, 180),
-			heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
-			verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
-			horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
-			pixelOffset: new Cesium.Cartesian2(0, -12),
-			disableDepthTestDistance: Number.POSITIVE_INFINITY,
-		},
-	})
-}
-
 function renderShapeList () {
 	if (!viewer || !visible.value) {
 		clearShapeDisplay()
@@ -679,7 +816,10 @@
 							material: style.outline,
 						},
 					})
-					addBufferRadiusLabel(dataSource, centerCartesian, radius)
+					addBufferRadiusLabelEntity(dataSource, centerCartesian, radius, {
+						name: 'shape-display',
+						customData: { drawType: 'buffer', label: 'radius' },
+					})
 				})
 			}
 			return
@@ -772,7 +912,8 @@
 }
 
 // 绘制完成回调
-const drawFinished = async data => {
+const drawFinished = async (data, options = {}) => {
+	const shouldContinueDraw = options?.continueDraw || activeToolMode.value === 'draw'
 	const positions = Array.isArray(data?.positions) ? data.positions : data
 	if (!Array.isArray(positions) || positions.length < 3) {
 		pointList = []
@@ -851,6 +992,12 @@
 		shapeList.value.splice(targetIndex, 1, shapePayload)
 	}
 	renderShapeList()
+	if (shouldContinueDraw && !readonly.value) {
+		activeShapeId.value = null
+		pointList = []
+		clearActiveTool()
+		startDraw(currentShapeType.value || 'polygon')
+	}
 }
 
 function getDetectAreaTypeKey () {
@@ -951,11 +1098,13 @@
 	const positions = activeTool?.getPositions?.()
 	if (Array.isArray(positions) && positions.length >= 3) {
 		suppressTypePanelOnce.value = true
-		drawFinished(positions)
+		drawFinished(positions, { continueDraw: true })
+		return
 	}
 	if (positions?.positions?.length >= 3) {
 		suppressTypePanelOnce.value = true
-		drawFinished(positions)
+		drawFinished(positions, { continueDraw: true })
+		return
 	}
 	clearActiveTool()
 }
@@ -1025,15 +1174,17 @@
 
 function handleClearShape () {
 	if (readonly.value) return
-	clearActiveTool()
 	viewEntity && viewer?.entities?.remove(viewEntity)
 	viewEntity = null
 	pointList = []
 	shapeList.value = []
 	activeShapeId.value = null
-	activeAreaType.value = ''
 	hideTypePanel()
 	clearShapeDisplay()
+	clearActiveTool()
+	if (viewer && currentShapeType.value) {
+		startDraw(currentShapeType.value)
+	}
 }
 
 function handleShapeEdit (row) {
@@ -1090,18 +1241,87 @@
 }
 
 // 获取派出所列表
-async function getPoliceStationList () {
-	const res = await fwPoliceStationListApi()
-	policeStationOptions.value = res?.data?.data ?? []
+async function getPoliceStationList (keyword = '') {
+	if (!visible.value) return
+	policeStationLoading.value = true
+	try {
+		const res = await fwPoliceStationListApi({
+			stationName: keyword || undefined,
+		})
+		const list = res?.data?.data ?? []
+		policeStationOptions.value = list
+		if (!keyword) {
+			policeStationAllOptions.value = list
+		}
+		await nextTick()
+		if (!keyword) {
+			const matched = policeStationOptions.value.find(
+				item => String(item.id) === String(formData.value.policeStationId)
+			)
+			if (!matched) {
+				formData.value.policeStationId = ''
+			}
+		}
+	} finally {
+		policeStationLoading.value = false
+	}
+}
+
+function handlePoliceStationSearch (query) {
+	policeStationSearchKeyword.value = (query || '').trim()
+	if (!policeStationSearchKeyword.value) {
+		policeStationOptions.value = [...policeStationAllOptions.value]
+		return
+	}
+	if (policeStationSearchTimer.value) {
+		clearTimeout(policeStationSearchTimer.value)
+	}
+	policeStationSearchTimer.value = setTimeout(() => {
+		policeStationSearchTimer.value = null
+		void getPoliceStationList(policeStationSearchKeyword.value)
+	}, 300)
 }
 
 // 获取设备列表
-async function getDeviceList () {
-	if (deviceOptions.value.length) return
-	const res = await fwDeviceListApi({ isAreaSelect: 1, areaId: formData.value.id, isTrack: 1 })
+function queueDeviceListRefresh () {
+	if (!visible.value) return
+	if (deviceListTimer) {
+		clearTimeout(deviceListTimer)
+	}
+	deviceListTimer = setTimeout(() => {
+		deviceListTimer = null
+		void getDeviceListByPolygons()
+	}, 200)
+}
+
+async function getDeviceListByPolygons () {
+	if (!visible.value) return
+	const polygons = resolveShapePolygonsWkt()
+	if (!polygons.length) {
+		clearDeviceListState()
+		return
+	}
+	deviceListRequestToken += 1
+	const requestToken = deviceListRequestToken
+	const res = await fwDeviceListByPolygonsApi({
+		areaId: dialogMode.value === 'add' ? '' : formData.value.id,
+		isAreaSelect: 1,
+		isTrack: 1,
+		polygons,
+	})
+	if (requestToken !== deviceListRequestToken) return
 	deviceOptions.value = res?.data?.data ?? []
-	if (dialogMode.value === 'view') {
-		deviceOptions.value = deviceOptions.value.filter(item => formData.value.deviceIds.includes(item.id))
+	if (dialogMode.value === 'view' && formData.value.deviceIds) {
+		const targetIds = formData.value.deviceIds.split(',')
+		deviceOptions.value = deviceOptions.value.filter(item => targetIds.includes(String(item.id)))
+	}
+	await nextTick()
+	const prevSuppressDeviceFly = suppressDeviceFly.value
+	suppressDeviceFly.value = true
+	syncDeviceSelection()
+	suppressDeviceFly.value = prevSuppressDeviceFly
+	if (!readonly.value) {
+		renderDeviceRanges(selectedDeviceRows.value)
 	}
 }
 
@@ -1135,14 +1355,16 @@
 
 // 同步选择状态
 function syncDeviceSelection () {
+	if (isDialogInitializing.value && !deviceOptions.value.length) return
 	const rows = []
-	const arr = formData.value.deviceIds.split(',')
+	const arr = formData.value.deviceIds ? formData.value.deviceIds.split(',') : []
 	deviceOptions.value.forEach(row => {
-		if (arr.includes(row.id)) {
+		if (arr.includes(String(row.id))) {
 			rows.push(row)
 		}
 	})
 	selectedDeviceRows.value = rows
+	formData.value.deviceIds = rows.map(item => item.id).join(',')
 	if (readonly.value) {
 		renderDeviceRanges(selectedDeviceRows.value)
 		return
@@ -1156,9 +1378,18 @@
 
 // 打开弹框
 async function open ({ mode, row } = {}) {
+	isDialogInitializing.value = true
 	dialogMode.value = mode || 'add'
 	formData.value = dialogMode.value === 'add' ? initForm() : row
 	selectedDeviceRows.value = []
+	deviceOptions.value = []
+	policeStationOptions.value = []
+	policeStationAllOptions.value = []
+	policeStationSearchKeyword.value = ''
+	if (policeStationSearchTimer.value) {
+		clearTimeout(policeStationSearchTimer.value)
+		policeStationSearchTimer.value = null
+	}
 	shapeList.value = []
 	activeShapeId.value = null
 	activeAreaType.value = ''
@@ -1167,16 +1398,33 @@
 	initMap()
 	clearShapeDisplay()
 	clearDeviceRangePrimitives()
+	clearDeviceIconEntities()
 	viewEntity && viewer?.entities?.remove(viewEntity)
 	viewEntity = null
 	clearActiveTool()
-	await getDeviceList()
 	if (dialogMode.value === 'add') {
 		mapRef.value?.zoomToAdminBoundary?.()
-		// default no draw mode
+		handleDraw('polygon')
 	} else if (dialogMode.value === 'edit') {
 		await loadDetail()
 		viewPolygon()
+		activeShapeId.value = null
+		pointList = []
+		const firstShape = shapeList.value[0]
+		const initialType = firstShape?.drawType || 'polygon'
+		currentShapeType.value = initialType
+		if (initialType === 'buffer') {
+			activeAreaType.value = bufferAreaTypeValue.split(',')
+		} else if (firstShape?.areaType) {
+			activeAreaType.value = firstShape.areaType
+		} else {
+			const defaultAreaType = getDetectAreaTypeKey()
+			if (defaultAreaType) {
+				activeAreaType.value = defaultAreaType
+			}
+		}
+		clearActiveTool()
+		startDraw(initialType)
 	} else {
 		await loadDetail()
 		viewPolygon()
@@ -1190,13 +1438,20 @@
 	if (!readonly.value) {
 		renderDeviceRanges(selectedDeviceRows.value)
 	}
+	isDialogInitializing.value = false
+	queueDeviceListRefresh()
+	void getPoliceStationList()
 }
 
-onMounted(() => {
-	getPoliceStationList()
-})
-
 onBeforeUnmount(() => {
+	if (deviceListTimer) {
+		clearTimeout(deviceListTimer)
+		deviceListTimer = null
+	}
+	if (policeStationSearchTimer.value) {
+		clearTimeout(policeStationSearchTimer.value)
+		policeStationSearchTimer.value = null
+	}
 	hideTypePanel()
 })
 
@@ -1258,44 +1513,31 @@
 	right: 16px;
 	display: flex;
 	flex-direction: column;
-	gap: 10px;
-	padding: 10px 8px;
-	background: rgba(19, 28, 36, 0.75);
+	gap: 14px;
+	padding: 32px 14px;
+	background: rgba(26, 25, 25, 0.6);
+	border-radius: 8px 8px 8px 8px;
 	backdrop-filter: blur(4px);
-	border-radius: 8px;
-	box-shadow: 0 6px 18px rgba(0, 0, 0, 0.2);
 }
 
 .tool-btn {
-	padding: 0;
+	padding: 0 !important;
+	width: 46px;
+	height: 46px;
 	display: flex;
 	flex-direction: column;
 	align-items: center;
-	gap: 6px;
-	background: rgba(255, 255, 255, 0.08);
-	border: 1px solid rgba(255, 255, 255, 0.18);
 	border-radius: 8px;
-	color: #ffffff;
-	font-size: 12px;
-	line-height: 1;
+	border: none !important;
+	box-shadow: none;
 	cursor: pointer;
-	transition: all 0.2s ease;
 	overflow: hidden;
+	background: transparent !important;
+	box-sizing: border-box;
 }
 
-.tool-btn:hover {
-	background: rgba(255, 255, 255, 0.16);
-	border-color: rgba(255, 255, 255, 0.35);
-	transform: translateY(-1px);
-}
-
-.tool-btn:active {
-	transform: translateY(0);
-}
-
-.tool-btn.danger {
-	border-color: rgba(255, 112, 112, 0.6);
-	color: #ffb3b3;
+.tool-btn.active {
+	border: 2px solid #FFFFFF !important;
 }
 
 .tool-icon {
@@ -1303,8 +1545,8 @@
 }
 
 .tool-icon-image {
-	width: 36px;
-	height: 36px;
+	width: 100%;
+	height: 100%;
 }
 
 .tool-icon.polygon {

--
Gitblit v1.9.3