地图编辑
This commit is contained in:
parent
b3bd0635fc
commit
48d26d8479
BIN
src/assets/location-icon.png
Normal file
BIN
src/assets/location-icon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.3 KiB |
BIN
src/assets/warehouse-map.png
Normal file
BIN
src/assets/warehouse-map.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 29 KiB |
@ -62,7 +62,7 @@ export const useAppStore = defineStore('app', {
|
|||||||
tagsViewIcon: true, // 是否显示标签图标
|
tagsViewIcon: true, // 是否显示标签图标
|
||||||
logo: true, // logo
|
logo: true, // logo
|
||||||
fixedHeader: true, // 固定toolheader
|
fixedHeader: true, // 固定toolheader
|
||||||
footer: true, // 显示页脚
|
footer: false, // 显示页脚
|
||||||
greyMode: false, // 是否开始灰色模式,用于特殊悼念日
|
greyMode: false, // 是否开始灰色模式,用于特殊悼念日
|
||||||
fixedMenu: wsCache.get('fixedMenu') || false, // 是否固定菜单
|
fixedMenu: wsCache.get('fixedMenu') || false, // 是否固定菜单
|
||||||
layout: wsCache.get(CACHE_KEY.LAYOUT) || 'classic', // layout布局
|
layout: wsCache.get(CACHE_KEY.LAYOUT) || 'classic', // layout布局
|
||||||
|
1413
src/views/mapPage/example/第一版编辑地图.vue
Normal file
1413
src/views/mapPage/example/第一版编辑地图.vue
Normal file
File diff suppressed because it is too large
Load Diff
151
src/views/mapPage/example/转换成实际坐标demo.vue
Normal file
151
src/views/mapPage/example/转换成实际坐标demo.vue
Normal file
@ -0,0 +1,151 @@
|
|||||||
|
<template>
|
||||||
|
<div class="map-container" ref="mapContainer">
|
||||||
|
<!-- 地图 -->
|
||||||
|
<div
|
||||||
|
class="map"
|
||||||
|
:style="{
|
||||||
|
backgroundImage: `url(${mapImage})`,
|
||||||
|
transform: `scale(${scale})`,
|
||||||
|
transformOrigin: '0 0',
|
||||||
|
width: `${mapWidth}px`,
|
||||||
|
height: `${mapHeight}px`
|
||||||
|
}"
|
||||||
|
@click="addLocation"
|
||||||
|
>
|
||||||
|
<!-- 库位 -->
|
||||||
|
<div
|
||||||
|
v-for="(location, index) in locations"
|
||||||
|
:key="index"
|
||||||
|
class="location"
|
||||||
|
:style="{
|
||||||
|
left: `${location.projectX}px`,
|
||||||
|
top: `${location.projectY}px`,
|
||||||
|
transform: `scale(${scale})` // 反向缩放库位图片
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<img src="@/assets/location-icon.png" alt="库位" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 放大缩小按钮 -->
|
||||||
|
<div class="controls">
|
||||||
|
<button @click="zoomIn">放大</button>
|
||||||
|
<button @click="zoomOut">缩小</button>
|
||||||
|
<button @click="saveLocations">保存</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, onMounted } from 'vue'
|
||||||
|
import mapImage from '@/assets/warehouse-map.png' // 扫描图路径
|
||||||
|
import locationIcon from '@/assets/location-icon.png' // 库位图标路径
|
||||||
|
|
||||||
|
const origin = [-54.4, -34.2]
|
||||||
|
const resolution = 0.05
|
||||||
|
|
||||||
|
const mapContainer = ref(null) // 地图容器
|
||||||
|
const scale = ref(1) // 缩放比例
|
||||||
|
const locations = ref([]) // 库位列表
|
||||||
|
const mapWidth = ref(0) // 地图宽度
|
||||||
|
const mapHeight = ref(0) // 地图高度
|
||||||
|
|
||||||
|
// 加载地图图片并设置初始尺寸
|
||||||
|
onMounted(() => {
|
||||||
|
const img = new Image()
|
||||||
|
img.src = mapImage
|
||||||
|
img.onload = () => {
|
||||||
|
mapWidth.value = img.width
|
||||||
|
mapHeight.value = img.height
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 添加库位
|
||||||
|
const addLocation = (event) => {
|
||||||
|
const rect = mapContainer.value.getBoundingClientRect()
|
||||||
|
const scrollLeft = mapContainer.value.scrollLeft // 水平滚动条偏移量
|
||||||
|
const scrollTop = mapContainer.value.scrollTop // 垂直滚动条偏移量
|
||||||
|
const devicePixelRatio = window.devicePixelRatio || 1
|
||||||
|
|
||||||
|
// 计算页面坐标(考虑设备像素比和滚动条偏移量)
|
||||||
|
const projectX = (event.clientX - rect.left + scrollLeft) / scale.value / devicePixelRatio
|
||||||
|
const projectY = (event.clientY - rect.top + scrollTop) / scale.value / devicePixelRatio
|
||||||
|
|
||||||
|
// 转换为实际坐标
|
||||||
|
const actualX = (projectX - origin[0]) / resolution
|
||||||
|
const actualY = (projectY - origin[1]) / resolution
|
||||||
|
|
||||||
|
// 添加库位
|
||||||
|
locations.value.push({
|
||||||
|
projectX,
|
||||||
|
projectY,
|
||||||
|
actualX,
|
||||||
|
actualY
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 放大
|
||||||
|
const zoomIn = () => {
|
||||||
|
scale.value *= 1.2
|
||||||
|
}
|
||||||
|
|
||||||
|
// 缩小
|
||||||
|
const zoomOut = () => {
|
||||||
|
scale.value /= 1.2
|
||||||
|
}
|
||||||
|
|
||||||
|
// 保存点位坐标
|
||||||
|
const saveLocations = () => {
|
||||||
|
console.log(
|
||||||
|
'项目点位坐标:',
|
||||||
|
locations.value.map((loc) => ({ x: loc.projectX, y: loc.projectY }))
|
||||||
|
)
|
||||||
|
console.log(
|
||||||
|
'实际现场点位坐标:',
|
||||||
|
locations.value.map((loc) => ({ x: loc.actualX, y: loc.actualY }))
|
||||||
|
)
|
||||||
|
alert('点位坐标已保存,请查看控制台输出。')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.map-container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 80vh;
|
||||||
|
overflow: auto;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
.map {
|
||||||
|
background-size: contain;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.location {
|
||||||
|
position: absolute;
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.location img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls {
|
||||||
|
position: fixed;
|
||||||
|
top: 110px;
|
||||||
|
right: 110px;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.controls button {
|
||||||
|
padding: 5px 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
</style>
|
@ -115,24 +115,23 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="map-box" ref="imgWrap" :style="{ cursor: state.cursorStyle }">
|
<div class="map-container" ref="mapContainer" :style="{ cursor: state.cursorStyle }">
|
||||||
<div
|
<div
|
||||||
class="map-box-inner"
|
class="map-bg"
|
||||||
|
:style="{
|
||||||
|
backgroundImage: `url(${imgBgObj.imgUrl})`,
|
||||||
|
transform: `scale(${state.imageChangeMultiple})`,
|
||||||
|
transformOrigin: '0 0',
|
||||||
|
width: `${imgBgObj.width}px`,
|
||||||
|
height: `${imgBgObj.height}px`
|
||||||
|
}"
|
||||||
@mousedown.stop="startDrawSelection"
|
@mousedown.stop="startDrawSelection"
|
||||||
@mousemove.stop="updateDrawSelection"
|
@mousemove.stop="updateDrawSelection"
|
||||||
@mouseup.stop="endDrawSelection"
|
@mouseup.stop="endDrawSelection"
|
||||||
>
|
>
|
||||||
<img
|
|
||||||
:src="imgBgObj.imgUrl"
|
|
||||||
:style="{
|
|
||||||
width: imgBgObj.width + 'px',
|
|
||||||
height: imgBgObj.height + 'px'
|
|
||||||
}"
|
|
||||||
id="mapBg"
|
|
||||||
/>
|
|
||||||
<div
|
<div
|
||||||
ref="mapBackgroundRef"
|
ref="mapBackgroundRef"
|
||||||
class="map-box-inner-dot"
|
class="map-box-inner"
|
||||||
@click="mapClick"
|
@click="mapClick"
|
||||||
:class="state.isShowGrid ? 'grid-show' : ''"
|
:class="state.isShowGrid ? 'grid-show' : ''"
|
||||||
:style="{
|
:style="{
|
||||||
@ -155,9 +154,9 @@
|
|||||||
@resizestop="(x, y, width, height) => resizeEnd(x, y, width, height, item, index)"
|
@resizestop="(x, y, width, height) => resizeEnd(x, y, width, height, item, index)"
|
||||||
@activated="() => activatedHandle(item, index)"
|
@activated="() => activatedHandle(item, index)"
|
||||||
@deactivated="deactivatedHandle"
|
@deactivated="deactivatedHandle"
|
||||||
:draggable="item.draggable"
|
:draggable="item.draggable && !state.prohibitedOperation"
|
||||||
:resizable="item.resizable"
|
:resizable="item.resizable && !state.prohibitedOperation"
|
||||||
:rotatable="item.rotatable"
|
:rotatable="item.rotatable && !state.prohibitedOperation"
|
||||||
:lock-aspect-ratio="item.lockAspectRatio"
|
:lock-aspect-ratio="item.lockAspectRatio"
|
||||||
style="border: none"
|
style="border: none"
|
||||||
>
|
>
|
||||||
@ -212,7 +211,7 @@
|
|||||||
src="https://api.znkjfw.com/admin-api/infra/file/4/get/区域_png_179_1739327151876.png"
|
src="https://api.znkjfw.com/admin-api/infra/file/4/get/区域_png_179_1739327151876.png"
|
||||||
alt=""
|
alt=""
|
||||||
:style="{
|
:style="{
|
||||||
width: item.locationWide + 'px',
|
width: item.locationWidee + 'px',
|
||||||
height: item.locationDeep + 'px',
|
height: item.locationDeep + 'px',
|
||||||
border: currentItemIndex === index ? '1px dashed #000' : 'none'
|
border: currentItemIndex === index ? '1px dashed #000' : 'none'
|
||||||
}"
|
}"
|
||||||
@ -324,6 +323,7 @@
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 节点编辑 -->
|
<!-- 节点编辑 -->
|
||||||
<editNodeProperties
|
<editNodeProperties
|
||||||
ref="editNodePropertiesRef"
|
ref="editNodePropertiesRef"
|
||||||
@ -418,7 +418,7 @@ const activatedHandle = (item, index) => {
|
|||||||
currentItemIndex.value = index
|
currentItemIndex.value = index
|
||||||
|
|
||||||
//节点编辑
|
//节点编辑
|
||||||
if (toolbarSwitchType.value === 'editNode' && item.type !== 7) {
|
if (toolbarSwitchType.value === 'editNode') {
|
||||||
editNodePropertiesRef.value.open(JSON.parse(JSON.stringify(item)))
|
editNodePropertiesRef.value.open(JSON.parse(JSON.stringify(item)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -476,7 +476,7 @@ const mapClick = (e) => {
|
|||||||
locationDeep: 16,
|
locationDeep: 16,
|
||||||
locationWide: 16,
|
locationWide: 16,
|
||||||
angle: 0,
|
angle: 0,
|
||||||
draggable: false,
|
draggable: true,
|
||||||
resizable: false,
|
resizable: false,
|
||||||
rotatable: false,
|
rotatable: false,
|
||||||
lockAspectRatio: false, //横纵比
|
lockAspectRatio: false, //横纵比
|
||||||
@ -737,7 +737,9 @@ const state = reactive({
|
|||||||
inputBoxValue: '', //输入的值
|
inputBoxValue: '', //输入的值
|
||||||
isShowLayer: false, //图层显示
|
isShowLayer: false, //图层显示
|
||||||
measureDistancesPoints: [], // 存储点击的点位
|
measureDistancesPoints: [], // 存储点击的点位
|
||||||
measureDistancesNum: 0 // 存储两点之间的距离
|
measureDistancesNum: 0, // 存储两点之间的距离
|
||||||
|
imageChangeMultiple: 1, //图片放大缩小的倍数
|
||||||
|
prohibitedOperation: false //禁止操作 在框选测距等操作时,禁止所有拖动等操作
|
||||||
})
|
})
|
||||||
const toolbarClick = (item) => {
|
const toolbarClick = (item) => {
|
||||||
let type = item.switchType
|
let type = item.switchType
|
||||||
@ -782,6 +784,17 @@ const toolbarClick = (item) => {
|
|||||||
state.measureDistancesNum = 0 // 清空存储两点之间的距离
|
state.measureDistancesNum = 0 // 清空存储两点之间的距离
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//禁止操作 在框选测距等操作时,禁止所有拖动等操作
|
||||||
|
if (
|
||||||
|
toolbarSwitchType.value === 'ranging' ||
|
||||||
|
toolbarSwitchType.value === 'lineLibrary' ||
|
||||||
|
toolbarSwitchType.value === 'region'
|
||||||
|
) {
|
||||||
|
state.prohibitedOperation = true
|
||||||
|
} else {
|
||||||
|
state.prohibitedOperation = false
|
||||||
|
}
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'open':
|
case 'open':
|
||||||
// 打开
|
// 打开
|
||||||
@ -903,18 +916,16 @@ const toolbarClick = (item) => {
|
|||||||
break
|
break
|
||||||
case 'larger':
|
case 'larger':
|
||||||
//放大
|
//放大
|
||||||
if (imgBgObj.width < 10000) {
|
if (state.imageChangeMultiple < 3) {
|
||||||
imgBgObj.width *= 1.2
|
state.imageChangeMultiple *= 1.2
|
||||||
imgBgObj.height *= 1.2
|
|
||||||
} else {
|
} else {
|
||||||
message.warning('不能在放大了')
|
message.warning('不能在放大了')
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
case 'smaller':
|
case 'smaller':
|
||||||
//缩小
|
//缩小
|
||||||
if (imgBgObj.width > 500) {
|
if (state.imageChangeMultiple > 0.3) {
|
||||||
imgBgObj.width *= 0.8
|
state.imageChangeMultiple *= 0.8
|
||||||
imgBgObj.height *= 0.8
|
|
||||||
} else {
|
} else {
|
||||||
message.warning('不能在缩小了')
|
message.warning('不能在缩小了')
|
||||||
}
|
}
|
||||||
@ -964,8 +975,8 @@ const startDrawSelection = (event) => {
|
|||||||
|
|
||||||
const backgroundRect = mapBackgroundRef.value.getBoundingClientRect()
|
const backgroundRect = mapBackgroundRef.value.getBoundingClientRect()
|
||||||
|
|
||||||
const x = Number(event.clientX) - backgroundRect.left
|
const x = disposeEventPoints(event).x
|
||||||
const y = event.clientY - backgroundRect.top
|
const y = disposeEventPoints(event).y
|
||||||
|
|
||||||
// 确保点击在背景区域内
|
// 确保点击在背景区域内
|
||||||
if (x >= 0 && x <= backgroundRect.width && y >= 0 && y <= backgroundRect.height) {
|
if (x >= 0 && x <= backgroundRect.width && y >= 0 && y <= backgroundRect.height) {
|
||||||
@ -981,9 +992,8 @@ const startDrawSelection = (event) => {
|
|||||||
const updateDrawSelection = (event) => {
|
const updateDrawSelection = (event) => {
|
||||||
if (toolbarSwitchType.value !== 'lineLibrary' && toolbarSwitchType.value !== 'region') return
|
if (toolbarSwitchType.value !== 'lineLibrary' && toolbarSwitchType.value !== 'region') return
|
||||||
if (state.drawSelectionAreaShow) {
|
if (state.drawSelectionAreaShow) {
|
||||||
const backgroundRect = mapBackgroundRef.value.getBoundingClientRect()
|
const x = disposeEventPoints(event).x
|
||||||
const x = event.clientX - backgroundRect.left
|
const y = disposeEventPoints(event).y
|
||||||
const y = event.clientY - backgroundRect.top
|
|
||||||
|
|
||||||
state.drawSelectionAreaBox.width = Number(x) - Number(state.drawSelectionStartPoint.x)
|
state.drawSelectionAreaBox.width = Number(x) - Number(state.drawSelectionStartPoint.x)
|
||||||
state.drawSelectionAreaBox.height = Number(y) - Number(state.drawSelectionStartPoint.y)
|
state.drawSelectionAreaBox.height = Number(y) - Number(state.drawSelectionStartPoint.y)
|
||||||
@ -1112,25 +1122,19 @@ const computedLineWidth = computed(() => {
|
|||||||
// 处理点击事件
|
// 处理点击事件
|
||||||
const measureDistancesClick = (event) => {
|
const measureDistancesClick = (event) => {
|
||||||
// 获取点击点相对于整个页面的坐标
|
// 获取点击点相对于整个页面的坐标
|
||||||
const x = event.clientX
|
|
||||||
const y = event.clientY
|
const x = disposeEventPoints(event).x
|
||||||
|
const y = disposeEventPoints(event).y
|
||||||
|
|
||||||
// 检查点击是否发生在背景区域内
|
// 检查点击是否发生在背景区域内
|
||||||
const backgroundRect = mapBackgroundRef.value.getBoundingClientRect()
|
|
||||||
if (
|
|
||||||
x >= backgroundRect.left &&
|
|
||||||
x <= backgroundRect.right &&
|
|
||||||
y >= backgroundRect.top &&
|
|
||||||
y <= backgroundRect.bottom
|
|
||||||
) {
|
|
||||||
if (state.measureDistancesPoints.length === 2) {
|
if (state.measureDistancesPoints.length === 2) {
|
||||||
// 如果已经有两个点,清空信息
|
// 如果已经有两个点,清空信息
|
||||||
state.measureDistancesPoints = []
|
state.measureDistancesPoints = []
|
||||||
state.measureDistancesNum = null
|
state.measureDistancesNum = null
|
||||||
} else {
|
} else {
|
||||||
// 记录点击的点位(相对于背景区域的坐标)
|
// 记录点击的点位(相对于背景区域的坐标)
|
||||||
const offsetX = x - backgroundRect.left
|
const offsetX = x
|
||||||
const offsetY = y - backgroundRect.top
|
const offsetY = y
|
||||||
state.measureDistancesPoints.push({ x: offsetX, y: offsetY })
|
state.measureDistancesPoints.push({ x: offsetX, y: offsetY })
|
||||||
if (state.measureDistancesPoints.length === 2) {
|
if (state.measureDistancesPoints.length === 2) {
|
||||||
// 计算两点之间的距离
|
// 计算两点之间的距离
|
||||||
@ -1140,7 +1144,6 @@ const measureDistancesClick = (event) => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//获取扫描图 地图背景相关的信息
|
//获取扫描图 地图背景相关的信息
|
||||||
@ -1150,7 +1153,9 @@ const imgBgObj = reactive({
|
|||||||
width: '',
|
width: '',
|
||||||
height: '',
|
height: '',
|
||||||
floor: '',
|
floor: '',
|
||||||
area: ''
|
area: '',
|
||||||
|
resolution: 0.05,
|
||||||
|
origin: [-54.4, -34.2]
|
||||||
})
|
})
|
||||||
//接收参数
|
//接收参数
|
||||||
const { query } = useRoute() // 查询参数
|
const { query } = useRoute() // 查询参数
|
||||||
@ -1201,7 +1206,7 @@ const getAllNodeList = async () => {
|
|||||||
item.dataList = []
|
item.dataList = []
|
||||||
item.locationDeep = 16
|
item.locationDeep = 16
|
||||||
item.locationWide = 16
|
item.locationWide = 16
|
||||||
item.draggable = false
|
item.draggable = true
|
||||||
item.resizable = false
|
item.resizable = false
|
||||||
item.rotatable = false
|
item.rotatable = false
|
||||||
item.lockAspectRatio = true
|
item.lockAspectRatio = true
|
||||||
@ -1211,7 +1216,7 @@ const getAllNodeList = async () => {
|
|||||||
item.locationDeep = item.dataList[0].locationDeep
|
item.locationDeep = item.dataList[0].locationDeep
|
||||||
item.locationWide = item.dataList[0].locationWide
|
item.locationWide = item.dataList[0].locationWide
|
||||||
item.draggable = true
|
item.draggable = true
|
||||||
item.resizable = false
|
item.resizable = true
|
||||||
item.rotatable = false
|
item.rotatable = false
|
||||||
item.lockAspectRatio = true
|
item.lockAspectRatio = true
|
||||||
} else if (item.type === 3 || item.type === 4) {
|
} else if (item.type === 3 || item.type === 4) {
|
||||||
@ -1220,7 +1225,7 @@ const getAllNodeList = async () => {
|
|||||||
item.locationDeep = item.dataObj.locationDeep
|
item.locationDeep = item.dataObj.locationDeep
|
||||||
item.locationWide = item.dataObj.locationWide
|
item.locationWide = item.dataObj.locationWide
|
||||||
item.draggable = true
|
item.draggable = true
|
||||||
item.resizable = false
|
item.resizable = true
|
||||||
item.rotatable = false
|
item.rotatable = false
|
||||||
item.lockAspectRatio = true
|
item.lockAspectRatio = true
|
||||||
} else if (item.type === 7) {
|
} else if (item.type === 7) {
|
||||||
@ -1285,34 +1290,51 @@ const layerSelectionSuccess = (typeList) => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//处理数据
|
||||||
|
const disposeEventPoints = (event) => {
|
||||||
|
const rect = mapBackgroundRef.value.getBoundingClientRect()
|
||||||
|
const scrollLeft = mapBackgroundRef.value.scrollLeft // 水平滚动条偏移量
|
||||||
|
const scrollTop = mapBackgroundRef.value.scrollTop // 垂直滚动条偏移量
|
||||||
|
const devicePixelRatio = window.devicePixelRatio || 1
|
||||||
|
|
||||||
|
// 计算页面坐标(考虑设备像素比和滚动条偏移量)
|
||||||
|
const x = (event.clientX - rect.left + scrollLeft) / state.imageChangeMultiple / devicePixelRatio
|
||||||
|
const y = (event.clientY - rect.top + scrollTop) / state.imageChangeMultiple / devicePixelRatio
|
||||||
|
|
||||||
|
// 转换为实际坐标
|
||||||
|
const actualLocationX = (x - imgBgObj.origin[0]) / imgBgObj.resolution
|
||||||
|
const actualLocationY = (y - imgBgObj.origin[1]) / imgBgObj.resolution
|
||||||
|
|
||||||
|
return {
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
actualLocationX,
|
||||||
|
actualLocationY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getMapList()
|
getMapList()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
.map-box {
|
.map-container {
|
||||||
width: 100%;
|
|
||||||
position: relative;
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
overflow: auto;
|
||||||
|
border: 1px solid #ccc;
|
||||||
|
// height: 80vh;
|
||||||
|
.map-bg {
|
||||||
|
background-size: contain;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
.map-box-inner {
|
.map-box-inner {
|
||||||
position: relative;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
.map-box-inner-dot {
|
|
||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.grid-show {
|
|
||||||
background: linear-gradient(-90deg, rgba(0, 0, 0, 0.1) 1px, transparent 1px),
|
|
||||||
linear-gradient(rgba(0, 0, 0, 0.1) 1px, transparent 1px);
|
|
||||||
background-size:
|
|
||||||
10px 10px,
|
|
||||||
10px 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.top-tool {
|
.top-tool {
|
||||||
@ -1411,4 +1433,12 @@ onMounted(() => {
|
|||||||
width: 80px;
|
width: 80px;
|
||||||
margin-left: 4px;
|
margin-left: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.grid-show {
|
||||||
|
background: linear-gradient(-90deg, rgba(0, 0, 0, 0.1) 1px, transparent 1px),
|
||||||
|
linear-gradient(rgba(0, 0, 0, 0.1) 1px, transparent 1px);
|
||||||
|
background-size:
|
||||||
|
10px 10px,
|
||||||
|
10px 10px;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
@ -17,10 +17,7 @@
|
|||||||
<!-- 背景区域 -->
|
<!-- 背景区域 -->
|
||||||
<div
|
<div
|
||||||
ref="background"
|
ref="background"
|
||||||
@mousedown="startSelection"
|
style="margin-left: 200px; height: 200vh; position: relative; background-color: #fff"
|
||||||
@mousemove="updateSelection"
|
|
||||||
@mouseup="endSelection"
|
|
||||||
style="margin-left: 200px; height: 100vh; position: relative; background-color: #fff"
|
|
||||||
>
|
>
|
||||||
<!-- 其他定位元素(如图片) -->
|
<!-- 其他定位元素(如图片) -->
|
||||||
<img
|
<img
|
||||||
@ -29,36 +26,13 @@
|
|||||||
alt="示例图片"
|
alt="示例图片"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<!-- 绘制框选区域 -->
|
<!-- 显示测量结果 -->
|
||||||
<div
|
<div v-if="distance !== null" style="position: fixed; top: 20px; left: 220px">
|
||||||
v-for="(rect, index) in selectionRects"
|
距离:{{ distance.toFixed(2) }} 像素
|
||||||
:key="index"
|
</div>
|
||||||
:style="{
|
|
||||||
position: 'absolute',
|
|
||||||
left: `${rect.x}px`,
|
|
||||||
top: `${rect.y}px`,
|
|
||||||
width: `${rect.width}px`,
|
|
||||||
height: `${rect.height}px`,
|
|
||||||
border: '2px solid blue',
|
|
||||||
backgroundColor: 'rgba(0, 0, 255, 0.1)'
|
|
||||||
}"
|
|
||||||
></div>
|
|
||||||
|
|
||||||
<!-- 当前框选区域 -->
|
<!-- 绘制点和连线 -->
|
||||||
<div
|
<template v-if="points.length > 0">
|
||||||
v-if="isSelecting"
|
|
||||||
:style="{
|
|
||||||
position: 'absolute',
|
|
||||||
left: `${selectionBox.x}px`,
|
|
||||||
top: `${selectionBox.y}px`,
|
|
||||||
width: `${selectionBox.width}px`,
|
|
||||||
height: `${selectionBox.height}px`,
|
|
||||||
border: '2px dashed red',
|
|
||||||
backgroundColor: 'rgba(255, 0, 0, 0.1)'
|
|
||||||
}"
|
|
||||||
></div>
|
|
||||||
|
|
||||||
<!-- 绘制点位 -->
|
|
||||||
<div
|
<div
|
||||||
v-for="(point, index) in points"
|
v-for="(point, index) in points"
|
||||||
:key="index"
|
:key="index"
|
||||||
@ -68,107 +42,96 @@
|
|||||||
top: `${point.y}px`,
|
top: `${point.y}px`,
|
||||||
width: '10px',
|
width: '10px',
|
||||||
height: '10px',
|
height: '10px',
|
||||||
backgroundColor: isPointSelected(point) ? 'red' : 'black',
|
backgroundColor: 'red',
|
||||||
borderRadius: '50%'
|
borderRadius: '50%'
|
||||||
}"
|
}"
|
||||||
></div>
|
></div>
|
||||||
|
<div
|
||||||
|
v-if="points.length === 2"
|
||||||
|
:style="{
|
||||||
|
position: 'absolute',
|
||||||
|
left: `${points[0].x}px`,
|
||||||
|
top: `${points[0].y}px`,
|
||||||
|
width: `${lineWidth}px`,
|
||||||
|
height: '2px',
|
||||||
|
backgroundColor: 'blue',
|
||||||
|
transform: `rotate(${lineAngle}deg)`,
|
||||||
|
transformOrigin: '0 0'
|
||||||
|
}"
|
||||||
|
></div>
|
||||||
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 确定按钮 -->
|
|
||||||
<button @click="confirmSelection" style="position: fixed; top: 20px; left: 220px">
|
|
||||||
确定
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script setup>
|
||||||
import { ref, computed, onMounted } from 'vue'
|
const points = ref([]) // 存储点击的点位
|
||||||
|
const distance = ref(null) // 存储两点之间的距离
|
||||||
|
const background = ref()
|
||||||
|
|
||||||
export default {
|
// 计算两点之间的距离
|
||||||
setup() {
|
const calculateDistance = (point1, point2) => {
|
||||||
const background = ref(null) // 背景区域的 DOM 元素
|
const dx = point2.x - point1.x
|
||||||
const points = ref([
|
const dy = point2.y - point1.y
|
||||||
{ x: 10, y: 10 },
|
return Math.sqrt(dx * dx + dy * dy)
|
||||||
{ x: 30, y: 100 },
|
}
|
||||||
{ x: 40, y: 40 },
|
|
||||||
{ x: 230, y: 400 },
|
|
||||||
{ x: 750, y: 640 }
|
|
||||||
]) // 点位数组
|
|
||||||
const isSelecting = ref(false) // 是否正在框选
|
|
||||||
const selectionBox = ref({ x: 0, y: 0, width: 0, height: 0 }) // 当前框选区域
|
|
||||||
const selectionRects = ref([]) // 所有框选区域
|
|
||||||
const startPos = ref({ x: 0, y: 0 }) // 框选起始位置
|
|
||||||
|
|
||||||
// 判断点位是否在框选区域内
|
// 计算连线的角度
|
||||||
const isPointInRect = (point, rect) => {
|
const lineAngle = computed(() => {
|
||||||
return (
|
if (points.value.length === 2) {
|
||||||
point.x >= rect.x &&
|
const dx = points.value[1].x - points.value[0].x
|
||||||
point.x <= rect.x + rect.width &&
|
const dy = points.value[1].y - points.value[0].y
|
||||||
point.y >= rect.y &&
|
return Math.atan2(dy, dx) * (180 / Math.PI)
|
||||||
point.y <= rect.y + rect.height
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
|
||||||
// 判断点位是否被选中
|
// 计算连线的长度
|
||||||
const isPointSelected = (point) => {
|
const lineWidth = computed(() => {
|
||||||
return selectionRects.value.some((rect) => isPointInRect(point, rect))
|
if (points.value.length === 2) {
|
||||||
|
return calculateDistance(points.value[0], points.value[1])
|
||||||
}
|
}
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
|
||||||
// 开始框选
|
// 处理点击事件
|
||||||
const startSelection = (event) => {
|
const handleClick = (event) => {
|
||||||
|
// 获取点击点相对于整个页面的坐标(考虑滚动条)
|
||||||
|
const x = event.clientX + window.scrollX
|
||||||
|
const y = event.clientY + window.scrollY
|
||||||
|
|
||||||
|
// 检查点击是否发生在背景区域内
|
||||||
const backgroundRect = background.value.getBoundingClientRect()
|
const backgroundRect = background.value.getBoundingClientRect()
|
||||||
const x = event.clientX - backgroundRect.left
|
if (
|
||||||
const y = event.clientY - backgroundRect.top
|
x >= backgroundRect.left + window.scrollX &&
|
||||||
|
x <= backgroundRect.right + window.scrollX &&
|
||||||
// 确保点击在背景区域内
|
y >= backgroundRect.top + window.scrollY &&
|
||||||
if (x >= 0 && x <= backgroundRect.width && y >= 0 && y <= backgroundRect.height) {
|
y <= backgroundRect.bottom + window.scrollY
|
||||||
isSelecting.value = true
|
) {
|
||||||
startPos.value = { x, y }
|
if (points.value.length === 2) {
|
||||||
selectionBox.value = { x, y, width: 0, height: 0 }
|
// 如果已经有两个点,清空信息
|
||||||
|
points.value = []
|
||||||
|
distance.value = null
|
||||||
|
} else {
|
||||||
|
// 记录点击的点位
|
||||||
|
points.value.push({ x, y })
|
||||||
|
if (points.value.length === 2) {
|
||||||
|
// 计算两点之间的距离
|
||||||
|
distance.value = calculateDistance(points.value[0], points.value[1])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新框选区域
|
|
||||||
const updateSelection = (event) => {
|
|
||||||
if (isSelecting.value) {
|
|
||||||
const backgroundRect = background.value.getBoundingClientRect()
|
|
||||||
const x = event.clientX - backgroundRect.left
|
|
||||||
const y = event.clientY - backgroundRect.top
|
|
||||||
|
|
||||||
selectionBox.value.width = x - startPos.value.x
|
|
||||||
selectionBox.value.height = y - startPos.value.y
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 结束框选
|
|
||||||
const endSelection = () => {
|
|
||||||
if (isSelecting.value) {
|
|
||||||
isSelecting.value = false
|
|
||||||
selectionRects.value.push({ ...selectionBox.value })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 点击确定按钮
|
|
||||||
const confirmSelection = () => {
|
|
||||||
const selectedPoints = points.value.filter((point) => isPointSelected(point))
|
|
||||||
console.log('选中的点位:', selectedPoints)
|
|
||||||
alert(`选中的点位:${JSON.stringify(selectedPoints)}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
background,
|
|
||||||
points,
|
|
||||||
isSelecting,
|
|
||||||
selectionBox,
|
|
||||||
selectionRects,
|
|
||||||
startSelection,
|
|
||||||
updateSelection,
|
|
||||||
endSelection,
|
|
||||||
confirmSelection,
|
|
||||||
isPointSelected
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 监听全局点击事件
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('click', handleClick)
|
||||||
|
})
|
||||||
|
|
||||||
|
// 移除全局点击事件监听
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('click', handleClick)
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped>
|
<style scoped>
|
||||||
|
Loading…
Reference in New Issue
Block a user