鼠标地图多选框选、地图工具复制、地图工具粘贴、地图工具删除、地图工具文字输入

This commit is contained in:
yyy 2025-02-08 17:38:48 +08:00
parent 9d0cc0b096
commit 325de2cc64
6 changed files with 343 additions and 72 deletions

View File

@ -90,6 +90,18 @@ const remainingRouter: AppRouteRecordRaw[] = [
icon: 'ep:edit',
title: '地图编辑'
}
},
{
path: 'test',
component: () => import('@/views/mapPage/realTimeMap/test.vue'),
name: 'testMapPageRealTimeMap',
meta: {
noCache: true, // 需要缓存
hidden: true,
canTo: true,
icon: 'ep:edit',
title: '测试'
}
}
]
},

View File

@ -1,6 +1,6 @@
<template>
<el-dialog v-model="dialogFormVisible" title="节点属性" width="540">
<el-form :model="form" label-width="75">
<Dialog v-model="dialogFormVisible" title="节点属性" width="540" class="node-form-dialog">
<el-form :model="form" label-width="auto">
<el-form-item label="X" prop="locationX" required>
<el-input v-model="form.locationX" placeholder="请输入" />
</el-form-item>
@ -99,7 +99,7 @@
<el-button type="primary" @click="submit"> 确认 </el-button>
</div>
</template>
</el-dialog>
</Dialog>
</template>
<script setup>
@ -212,3 +212,15 @@ const deviceTypeChange = () => {
defineExpose({ open }) // open
</script>
<style lang="scss">
.node-form-dialog {
.el-dialog__header {
border-bottom: none;
}
.el-dialog__footer {
border-top: none !important;
}
}
</style>

View File

@ -0,0 +1,65 @@
<template>
<Dialog v-model="dialogFormVisible" title="文字设置" width="400" class="text-form-dialog">
<el-form :model="form">
<el-form-item label="字体">
<el-select v-model="form.fontType" placeholder="请选择">
<el-option label="宋体" value="SimSun" />
<el-option label="黑体" value="SimHei" />
<el-option label="微软雅黑" value="Microsoft Yahei" />
<el-option label="楷体" value="KaiTi" />
<el-option label="新宋体" value="NSimSun" />
<el-option label="仿宋" value="FangSong" />
</el-select>
</el-form-item>
<el-form-item label="字号">
<el-input-number
v-model="form.fontSize"
:step="1"
:min="10"
:max="30"
placeholder="请输入"
/>
</el-form-item>
<el-form-item label="颜色">
<el-color-picker v-model="form.fontColor" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
<el-button type="primary" @click="dialogFormVisible = false"> 确认 </el-button>
</div>
</template>
</Dialog>
</template>
<script setup>
import { reactive, ref } from 'vue'
const dialogFormVisible = ref(false)
const formLabelWidth = '140px'
const form = reactive({
fontType: 'SimSun',
fontSize: '14',
fontColor: '#000000'
})
const open = (item) => {
dialogFormVisible.value = true
}
defineExpose({ open }) // open
</script>
<style lang="scss">
.text-form-dialog {
.el-dialog__header {
border-bottom: none;
}
.el-dialog__footer {
border-top: none !important;
}
}
</style>

View File

@ -0,0 +1,6 @@
let cursorCollection = {
node: 'data:image/png;base64,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',
input:
'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAA5klEQVRYR+2VPwrCMBSHfwlFvIizq6u7y0vR2Qt4A0dP4AmcLSUUhI6Ozs5epA5NpKBQ1Ni0tAThZUxe8r735Z9A4CYC5wcDsAGngTiOJ8aYRR+HVEp5StP09m0tJwARLQEc+wAAsNJaJ38NsDXGnOsVSCnnAHbPvqbxTgam1tp1lUAIcdBaX+sAb1v0kYCIfs5/rdX5GjYB+J4dBmADbIANsIHWBpRSs7IspeszyrLsAsAO9hQT0R3AyJWgKIpxnudVjFdrbSA4gFJqY62NXOVFUbRPkqT0Kr/66n0Dh4pjADYQ3MADakKSIQVRq+oAAAAASUVORK5CYII='
}
export default cursorCollection

View File

@ -6,7 +6,7 @@
placement="bottom"
:width="80"
trigger="click"
v-if="item.id === 5"
v-if="item.id === 5 || item.id === 6"
:disabled="currentItemIndex === -1"
>
<template #reference>
@ -25,7 +25,8 @@
<div class="name"> {{ item.name }} </div>
</div>
</template>
<el-form :model="state.moveForm">
<!-- 位置 -->
<el-form :model="state.moveForm" v-if="item.id === 5">
<el-form-item label="X">
<el-input v-model="state.moveForm.x" placeholder="请输入" />
</el-form-item>
@ -36,9 +37,28 @@
<el-button size="small" color="#00329F" @click="moveFormSubmit">确认</el-button>
</div>
</el-form>
<!-- 旋转 -->
<el-form :model="state.rotationForm" v-if="item.id === 6">
<el-form-item label="角度">
<el-input v-model="state.rotationForm.angle" placeholder="请输入" />
</el-form-item>
<div style="text-align: right">
<el-button size="small" color="#00329F" @click="rotationFormSubmit">确认</el-button>
</div>
</el-form>
<!-- 字体 -->
<el-form :model="state.rotationForm" v-if="item.id === 13">
<el-form-item label="角度">
<el-input v-model="state.rotationForm.angle" placeholder="请输入" />
</el-form-item>
<div style="text-align: right">
<el-button size="small" color="#00329F" @click="rotationFormSubmit">确认</el-button>
</div>
</el-form>
</el-popover>
<div
v-if="item.id !== 5"
v-else
class="tool-item"
:class="
toolbarTypeIndex === item.id ? 'tool-active' : item.isActive ? 'right-tool-active' : ''
@ -48,10 +68,10 @@
<Icon :icon="item.icon" :size="24" />
<div class="name"> {{ item.name }} </div>
</div>
<div class="line" v-if="item.id === 3 || item.id === 10 || item.id === 19"></div>
<div class="line" v-if="item.id === 3 || item.id === 10 || item.id === 17"></div>
</div>
</div>
<div class="right-tool-list">
<div class="right-tool-list" v-if="state.isShowToolbar">
<div
v-for="item in state.rightToolList"
:key="item.id"
@ -66,10 +86,21 @@
</div>
</div>
</div>
<div class="map-box" ref="imgWrap" @mousewheel.prevent="rollImg" style="overflow: hidden">
<div
class="map-box"
ref="imgWrap"
@mousewheel.prevent="rollImg"
style="overflow: hidden"
:style="{ cursor: state.cursorStyle }"
>
<div class="map-box-inner" ref="image" @mousedown.prevent="moveImg">
<img :src="imgBgObj.imgUrl" class="map-box-img" id="mapBg" />
<div class="map-box-inner-dot" @click="mapClick" :class="state.isShowGrid ? 'grid-show' : ''">
<div
class="map-box-inner-dot"
@click="mapClick"
:class="state.isShowGrid ? 'grid-show' : ''"
v-if="test"
>
<VueDragResizeRotate
v-for="(item, index) in allHistoryList[currentIndex]"
:key="index"
@ -87,9 +118,16 @@
:draggable="item.draggable"
:resizable="item.resizable"
:rotatable="item.rotatable"
:lockAspectRatio="item.lockAspectRatio"
:lock-aspect-ratio="item.lockAspectRatio"
:handles="['tl', 'tr', 'bl', 'br', 'rot']"
style="border: none"
>
<div class="sdiv">
<div
class="sdiv"
:style="
currentItemIndex === index ? 'border: 1px dashed #000;box-sizing: border-box;' : ''
"
>
<img
v-if="item.labelType === 'icon'"
:src="item.img"
@ -106,21 +144,27 @@
</div>
</div>
</div>
<!-- 节点编辑 -->
<editNodeProperties
ref="editNodePropertiesRef"
:positionMapId="imgBgObj.positionMapId"
@submitNodeSuccess="submitNodeSuccess"
/>
<!-- 文字输入弹窗 -->
<textFormToolDialog ref="textFormToolDialogRef" />
</template>
<script setup>
import { ref, defineComponent, reactive, nextTick, onMounted } from 'vue'
import editNodeProperties from './components/editNodeProperties.vue'
import editNodeProperties from './components-tool/editNodeProperties.vue'
import textFormToolDialog from './components-tool/textFormToolDialog.vue'
import * as MapApi from '@/api/map/map'
import cursorCollection from './cursorCollection'
defineOptions({ name: 'editMapPageRealTimeMap' })
const textFormToolDialogRef = ref() //
const message = useMessage() //
const formLoading = ref(false)
@ -133,8 +177,8 @@ const imgBgObj = reactive({
}) //
const imgList = ref([]) //
const list = ref([])
//
const list = ref([])
const getList = async () => {
let data = await MapApi.getPositionMapGetMap()
let mapList = []
@ -165,13 +209,18 @@ const getMapData = async (item) => {
}
//
const test = ref(true)
const resizeEnd = (x, y, w, h, item, index) => {
console.log('缩放')
imgList.value[index].x = x
imgList.value[index].y = y
imgList.value[index].w = w
imgList.value[index].h = h
addEditHistory()
test.value = false
console.log('缩放', w, h)
nextTick(() => {
imgList.value[index].x = x
imgList.value[index].y = y
imgList.value[index].w = w
imgList.value[index].h = h
addEditHistory()
})
}
//
const dragEnd = (x, y, item, index) => {
@ -191,10 +240,10 @@ const rotateEnd = (angle, item, index) => {
//
const editNodePropertiesRef = ref()
const activatedHandle = (item, index) => {
console.log('选中', item)
// console.log('', item, index)
currentItemIndex.value = index
if (item.labelType === 'node') {
if (toolbarTypeIndex.value === 25) {
if (toolbarTypeIndex.value === 23) {
editNodePropertiesRef.value.open(item)
}
}
@ -212,9 +261,15 @@ const addEditHistory = () => {
currentIndex.value = allHistoryList.value.length
allHistoryList.value.push(JSON.parse(JSON.stringify(imgList.value)))
} else {
currentIndex.value++
allHistoryList.value.push(JSON.parse(JSON.stringify(imgList.value)))
currentIndex.value++
console.log(currentIndex.value)
console.log(
allHistoryList.value[currentIndex.value][currentItemIndex.value],
allHistoryList.value[currentIndex.value][currentItemIndex.value]
)
}
test.value = true
console.log(allHistoryList.value, 'allHistoryList')
}
@ -241,7 +296,7 @@ const backNextStep = () => {
//
const mapClick = (e) => {
if (toolbarTypeIndex.value === 24) {
if (toolbarTypeIndex.value === 22) {
//
imgList.value.push({
x: e.offsetX,
@ -252,6 +307,7 @@ const mapClick = (e) => {
draggable: false,
resizable: false,
rotatable: false,
lockAspectRatio: false, //
img: '',
labelType: 'node'
})
@ -379,60 +435,48 @@ const state = reactive({
},
{
id: 14,
name: '设备',
icon: 'ep:folder-add',
isActive: false
},
{
id: 15,
name: '车辆',
icon: 'ep:folder-add',
isActive: false
},
{
id: 16,
name: '测距',
icon: 'ep:folder-add',
isActive: false
},
{
id: 17,
id: 15,
name: '图层',
icon: 'ep:folder-add',
isActive: false
},
{
id: 18,
id: 16,
name: '标记',
icon: 'ep:folder-add',
isActive: false
},
{
id: 19,
id: 17,
name: '网格',
icon: 'ep:folder-add',
isActive: false
},
{
id: 20,
id: 18,
name: '放大',
icon: 'ep:zoom-in',
isActive: false
},
{
id: 21,
id: 19,
name: '缩小',
icon: 'ep:zoom-out',
isActive: false
},
{
id: 22,
id: 20,
name: '撤回',
icon: 'ep:top-left',
isActive: false
},
{
id: 23,
id: 21,
name: '重做',
icon: 'ep:top-right',
isActive: false
@ -440,48 +484,59 @@ const state = reactive({
],
rightToolList: [
{
id: 24,
id: 22,
name: '绘制节点',
icon: 'ep:circle-plus-filled',
isActive: false
},
{
id: 25,
id: 23,
name: '编辑节点',
icon: 'ep:circle-plus-filled',
isActive: false
},
{
id: 26,
id: 24,
name: '绘制路线',
icon: 'ep:semi-select',
isActive: false
},
{
id: 27,
id: 25,
name: '编辑路线',
icon: 'ep:semi-select',
isActive: false
}
],
isShowGrid: false,
isShowToolbar: false, //
isShowGrid: false, //
moveForm: {
x: '',
y: ''
}
}, //
rotationForm: {
angle: ''
}, //
copyMapItem: '', //
cursorStyle: 'auto'
})
const toolbarClick = (item) => {
let type = item.id
if (currentItemIndex.value === -1 && type === 5) {
message.warning('请先选择要操作的图标')
if (currentItemIndex.value === -1 && (type === 5 || type === 6 || type === 7 || type === 9)) {
message.warning('请先选择要操作的对象')
return
}
if (type === 8 && !state.copyMapItem) {
message.warning('请先复制对象')
return
}
if (toolbarTypeIndex.value === type) {
if ((type === 10 || type === 13 || type === 17) && toolbarTypeIndex.value === type) {
toolbarTypeIndex.value = 0
} else {
toolbarTypeIndex.value = type
}
switch (type) {
case 1:
break
@ -497,18 +552,43 @@ const toolbarClick = (item) => {
case 6:
break
case 7:
//
state.copyMapItem = allHistoryList.value[currentIndex.value][currentItemIndex.value]
break
case 8:
//
let item = JSON.parse(JSON.stringify(state.copyMapItem))
item.x = item.x + 50
item.y = item.y + 50
imgList.value.push(item)
addEditHistory()
break
case 9:
//
imgList.value.splice(currentItemIndex.value, 1)
addEditHistory()
break
case 10:
//
if (toolbarTypeIndex.value === 10) {
state.isShowToolbar = true
} else {
state.isShowToolbar = false
}
break
case 11:
break
case 12:
break
case 13:
//
textFormToolDialogRef.value.open()
// if (toolbarTypeIndex.value === 13) {
// state.cursorStyle = `url('${cursorCollection.input}'),auto`
// } else {
// state.cursorStyle = `auto`
// }
break
case 14:
break
@ -517,12 +597,8 @@ const toolbarClick = (item) => {
case 16:
break
case 17:
break
case 18:
break
case 19:
//
if (toolbarTypeIndex.value === 19) {
if (toolbarTypeIndex.value === 17) {
state.isShowGrid = true
item.isActive = true
} else {
@ -530,29 +606,27 @@ const toolbarClick = (item) => {
item.isActive = false
}
break
case 18:
break
case 19:
break
case 20:
break
case 21:
break
case 22:
//
backPreviousStep()
break
case 23:
case 21:
//
backNextStep()
break
case 24:
case 22:
drawNodes()
break
case 23:
break
case 24:
break
case 25:
break
case 26:
break
case 27:
break
default:
break
}
}
@ -561,6 +635,10 @@ const moveFormSubmit = () => {
allHistoryList.value[currentIndex.value][currentItemIndex.value].x = state.moveForm.x
allHistoryList.value[currentIndex.value][currentItemIndex.value].y = state.moveForm.y
}
//
const rotationFormSubmit = () => {
allHistoryList.value[currentIndex.value][currentItemIndex.value].angle = state.rotationForm.angle
}
onMounted(() => {
imgList.value = [
@ -573,6 +651,7 @@ onMounted(() => {
draggable: true,
resizable: true,
rotatable: true,
lockAspectRatio: true, //
img: 'https://sys.znkjfw.com/imgs/process/%E8%AF%B7%E5%81%87.png',
labelType: 'icon'
},
@ -585,6 +664,7 @@ onMounted(() => {
draggable: true,
resizable: true,
rotatable: true,
lockAspectRatio: false, //
img: 'https://sys.znkjfw.com/imgs/process/%E8%AF%B7%E5%81%87.png',
labelType: 'icon'
}
@ -619,8 +699,8 @@ onMounted(() => {
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:
20px 20px,
20px 20px;
10px 10px,
10px 10px;
}
}
}
@ -628,6 +708,7 @@ onMounted(() => {
.sdiv {
width: 100%;
height: 100%;
box-sizing: border-box;
}
.top-tool-list {
@ -649,6 +730,8 @@ onMounted(() => {
.tool-item {
cursor: pointer;
// cursor: url('https://api.znkjfw.com/admin-api/infra/file/4/get/_png_179_1738982726561.png'),
// auto;
width: 52px;
height: 70px;
display: flex;

View File

@ -0,0 +1,93 @@
<template>
<div
@mousedown="startSelection"
@mousemove="updateSelection"
@mouseup="endSelection"
style="position: relative; width: 100%; height: 900px"
>
<!-- 绘制框选区域 -->
<div
v-if="isSelecting"
:style="{
position: 'absolute',
left: `${selectionBox.x}px`,
top: `${selectionBox.y}px`,
width: `${selectionBox.width}px`,
height: `${selectionBox.height}px`,
border: '1px solid blue',
backgroundColor: 'rgba(0, 0, 255, 0.1)'
}"
></div>
<!-- 绘制点位 -->
<div
v-for="(point, index) in points"
:key="index"
:style="{
position: 'absolute',
left: `${point.x}px`,
top: `${point.y}px`,
width: '100px',
height: '100px',
backgroundColor: selectedPoints.includes(point) ? 'red' : '#fff'
}"
>设备{{ index }}</div
>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const points = ref([
{ x: 210, y: 210 },
{ x: 330, y: 500 },
{ x: 840, y: 440 },
{ x: 230, y: 400 },
{ x: 750, y: 640 }
])
const isSelecting = ref(false)
const selectionBox = ref({ x: 0, y: 0, width: 0, height: 0 })
const startPos = ref({ x: 0, y: 0 })
const selectedPoints = ref([])
const startSelection = (event) => {
isSelecting.value = true
startPos.value = { x: event.offsetX, y: event.offsetY }
selectionBox.value = { x: event.offsetX, y: event.offsetY, width: 0, height: 0 }
}
const updateSelection = (event) => {
if (isSelecting.value) {
selectionBox.value.width = event.offsetX - startPos.value.x
selectionBox.value.height = event.offsetY - startPos.value.y
}
}
const endSelection = () => {
isSelecting.value = false
selectedPoints.value = points.value.filter((point) => {
return (
point.x >= Math.min(startPos.value.x, startPos.value.x + selectionBox.value.width) &&
point.x <= Math.max(startPos.value.x, startPos.value.x + selectionBox.value.width) &&
point.y >= Math.min(startPos.value.y, startPos.value.y + selectionBox.value.height) &&
point.y <= Math.max(startPos.value.y, startPos.value.y + selectionBox.value.height)
)
})
}
return {
points,
isSelecting,
selectionBox,
selectedPoints,
startSelection,
updateSelection,
endSelection
}
}
}
</script>