地图编辑

This commit is contained in:
yyy 2025-02-11 17:04:33 +08:00
parent 2f8d31b63c
commit 96121faf7e
5 changed files with 378 additions and 157 deletions

View File

@ -75,3 +75,7 @@ export const getPositionMapItemList = async (params) => {
export const getDeviceInformationList = async (params) => {
return await request.get({ url: `/system/device/information/list`, params })
}
//地图绑定设备
export const mapBindDeviceInfo = async (data) => {
return await request.post({ url: `/system/device/information/mapBindDeviceInfo`, data })
}

View File

@ -59,15 +59,15 @@
</el-select>
</div>
</el-form-item>
<el-form-item label="库位长度" prop="length">
<el-form-item label="库位长度" prop="locationDeep">
<div style="display: flex">
<el-input v-model="form.length" placeholder="请输入" />
<el-input v-model="form.locationDeep" placeholder="请输入" />
<span class="ml-2">cm</span>
</div>
</el-form-item>
<el-form-item label="库位宽度" prop="width">
<el-form-item label="库位宽度" prop="locationWide">
<div style="display: flex">
<el-input v-model="form.width" placeholder="请输入" />
<el-input v-model="form.locationWide" placeholder="请输入" />
<span class="ml-2">cm</span>
</div>
</el-form-item>
@ -124,12 +124,12 @@ const props = defineProps({
})
const form = ref({
locationX: undefined,
locationY: undefined,
type: 1,
layersNumber: 1, //
length: undefined, //
width: undefined, //
locationX: undefined,
locationY: undefined,
locationDeep: undefined, //
locationWide: undefined, //
direction: 1, //
inDirection: undefined,
outDirection: undefined,
@ -153,8 +153,8 @@ const submit = () => {
let list = []
for (let index = 0; index < form.value.layersNumber; index++) {
list.push({
locationWide: form.value.width || undefined,
locationDeep: form.value.length || undefined,
locationWide: form.value.locationWide || undefined,
locationDeep: form.value.locationDeep || undefined,
direction: form.value.direction || undefined, //
inDirection: form.value.inDirection || undefined, //
outDirection: form.value.outDirection || undefined, //
@ -166,8 +166,8 @@ const submit = () => {
let obj = {
id: form.value.id || undefined,
mapId: props.positionMapId,
locationWide: form.value.width || undefined,
locationDeep: form.value.length || undefined,
locationWide: form.value.locationWide || undefined,
locationDeep: form.value.locationDeep || undefined,
direction: form.value.direction || undefined, //
inDirection: form.value.inDirection || undefined, //
outDirection: form.value.outDirection || undefined //
@ -181,8 +181,8 @@ const submit = () => {
const open = (item) => {
form.value = item
form.value.locationX = item.x
form.value.locationY = item.y
form.value.locationX = item.locationX
form.value.locationY = item.locationY
form.value.type = item.type || 1
dialogFormVisible.value = true
}

View File

@ -1,10 +1,59 @@
<template>
<Dialog v-model="dialogFormVisible" title="设备" width="600" class="text-form-dialog">
<div> 11 </div>
<Dialog v-model="dialogFormVisible" title="设备" width="586" class="equipment-form-dialog">
<div class="device-list">
<div class="device-item" v-for="(item, index) in deviceList" :key="index">
<img class="img" :src="item.url" />
<div class="name">{{ item.deviceNo }}</div>
<el-icon color="#f56c6c" class="delete-icon" @click="deleteDeviceItem(item, index)"
><DeleteFilled
/></el-icon>
</div>
<div class="add-device-item" @click="addDevice">
<el-icon color="#C4C4C4" class="add-icon"><Plus /></el-icon>
<div class="name">新增设备</div>
</div>
</div>
</Dialog>
<!-- 新增设备 -->
<Dialog v-model="addDeviceVisible" title="设备" width="586" class="equipment-form-dialog">
<el-form :model="deviceInfo" label-width="90">
<el-form-item label="设备类型" required>
<el-select
v-model="deviceInfo.deviceType"
class="!w-400px"
clearable
placeholder="请选择设备类型"
@change="deviceTypeChange()"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.DEVICE_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="设备编号" required>
<el-select
v-model="deviceInfo.deviceInfoId"
class="!w-400px"
clearable
placeholder="请选择设备编号"
>
<el-option
v-for="item in allDeviceList"
:key="item.id"
:label="item.deviceNo"
:value="item.id"
/>
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
<el-button type="primary" @click="formSubmit"> 确认 </el-button>
<el-button @click="addDeviceVisible = false">取消</el-button>
<el-button type="primary" @click="submitAddForm"> 确定 </el-button>
</div>
</template>
</Dialog>
@ -12,27 +61,168 @@
<script setup>
import { reactive, ref } from 'vue'
import * as MapApi from '@/api/map/map'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
const dialogFormVisible = ref(false)
const message = useMessage() //
const props = defineProps({
positionMapId: {
type: String,
default: () => ''
}
})
const dialogFormVisible = ref(false) //
const addDeviceVisible = ref(false) //
//
const deviceList = ref([]) //
const getDeviceList = async () => {
deviceList.value = await MapApi.getDeviceInformationList({
positionMapId: props.positionMapId
})
}
//
const deleteDeviceItem = async (item, index) => {
try {
await message.delConfirm('是否取消绑定所选中数据?')
await MapApi.mapBindDeviceInfo({
positionMapId: item.positionMapId,
deviceInfoId: item.id,
type: 2
})
message.success('取消绑定成功')
await getDeviceList()
} catch {}
}
//
const deviceInfo = ref({
positionMapId: '',
deviceInfoId: '',
deviceType: '',
type: 1
})
const allDeviceList = ref()
//
const addDevice = () => {
addDeviceVisible.value = true
initAddForm()
}
//
const deviceTypeChange = async () => {
getAllDeviceList()
}
const getAllDeviceList = async () => {
allDeviceList.value = await MapApi.getDeviceInformationList({
unboundFlag: 1,
deviceType: deviceInfo.value.deviceType
})
}
//
const submitAddForm = async () => {
deviceInfo.value.positionMapId = props.positionMapId
await MapApi.mapBindDeviceInfo(deviceInfo.value)
message.success('新增成功')
addDeviceVisible.value = false
getDeviceList()
}
const open = (item) => {
dialogFormVisible.value = true
getDeviceList()
}
const formSubmit = () => {}
const initAddForm = () => {
deviceInfo.value.deviceInfoId = ''
deviceInfo.value.deviceType = ''
getAllDeviceList()
}
defineExpose({ open }) // open
</script>
<style lang="scss">
.text-form-dialog {
.el-dialog__header {
border-bottom: none;
}
.equipment-form-dialog {
padding: 0px;
.el-dialog__footer {
padding-bottom: 30px;
padding: 0px 20px 20px 0;
border-top: none !important;
}
.device-list {
display: flex;
flex-wrap: wrap;
.device-item {
position: relative;
width: 164px;
height: 118px;
background: #ffffff;
box-shadow: rgba(0, 0, 0, 0.05) 0px 0px 0px 1px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin: 10px;
.img {
width: 67px;
height: 59px;
}
.name {
margin-top: 8px;
font-family:
PingFangSC,
PingFang SC;
font-weight: 400;
font-size: 14px;
color: #91929e;
line-height: 20px;
text-align: left;
font-style: normal;
}
.delete-icon {
cursor: pointer;
font-size: 18px;
position: absolute;
right: 8px;
top: 8px;
}
}
}
.add-device-item {
width: 164px;
height: 118px;
background: rgba(63, 140, 255, 0.04);
border: 1px dashed #bac8e3;
box-shadow: rgba(0, 0, 0, 0.05) 0px 0px 0px 1px;
margin: 10px;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.add-icon {
font-size: 28px;
}
.name {
margin-top: 8px;
font-family:
PingFangSC,
PingFang SC;
font-weight: 400;
font-size: 14px;
color: #91929e;
line-height: 20px;
text-align: left;
font-style: normal;
}
}
}
</style>

View File

@ -7,7 +7,7 @@
:width="80"
trigger="click"
v-if="item.switchType === 'move' || item.switchType === 'revolve'"
:disabled="!currentItemIndex"
:disabled="currentItemIndex === -1"
>
<template #reference>
<div
@ -28,7 +28,7 @@
<!-- 位置 -->
<el-form :model="state.moveForm" v-if="item.switchType === 'move'">
<el-form-item label="X">
<el-input v-model="state.moveForm.x" placeholder="请输入" />
<el-input v-model="state.moveForm.locationX" placeholder="请输入" />
</el-form-item>
<el-form-item label="Y">
<el-input v-model="state.moveForm.y" placeholder="请输入" />
@ -77,7 +77,7 @@
v-if="
item.switchType === 'saveAs' ||
item.switchType === 'delete' ||
item.switchType === 'ranging'
item.switchType === 'grid'
"
></div>
</div>
@ -127,10 +127,10 @@
v-for="(item, index) in allHistoryList[currentIndex]"
:key="index"
:parent="true"
:x="item.x"
:y="item.y"
:w="item.w"
:h="item.h"
:x="item.locationX"
:y="item.locationY"
:w="item.locationWide"
:h="item.locationDeep"
:r="item.angle"
@rotatestop="(degree) => rotateEnd(degree, item, index)"
@dragstop="(x, y) => dragEnd(x, y, item, index)"
@ -149,18 +149,17 @@
currentItemIndex === index ? 'border: 1px dashed #000;box-sizing: border-box;' : ''
"
>
<img
v-if="item.labelType === 'icon'"
<!-- <img
:src="item.img"
alt=""
style="width: 100%; height: 100%"
/>
/> -->
<div
v-if="item.labelType === 'node'"
v-if="item.type !== 7"
style="width: 100%; height: 100%; background-color: #000; border-radius: 50%"
></div>
<div
v-if="item.labelType === 'text'"
v-if="item.type === 7"
:style="{
fontSize: item.fontSize + 'px',
fontFamily: item.fontFamily,
@ -199,8 +198,8 @@
fontSize: state.inputBoxStyle.fontSize + 'px',
fontFamily: state.inputBoxStyle.fontFamily,
color: state.inputBoxStyle.fontColor,
left: state.inputBoxStyle.x + 'px',
top: state.inputBoxStyle.y + 'px'
left: state.inputBoxStyle.locationX + 'px',
top: state.inputBoxStyle.locationY + 'px'
}"
class="input-box-class"
/>
@ -221,7 +220,7 @@
<!-- 图层选择 -->
<layerSelectionToolDialog v-if="state.isShowLayer" ref="layerSelectionToolDialogRef" />
<!-- 设备弹窗选择 -->
<equipmentToolDialog ref="equipmentToolDialogRef" />
<equipmentToolDialog ref="equipmentToolDialogRef" :positionMapId="imgBgObj.positionMapId" />
</template>
<script setup>
@ -246,42 +245,7 @@ const formLoading = ref(false)
const allHistoryList = ref([]) //
const currentIndex = ref(0) //
const currentItemIndex = ref(-1) //
const imgBgObj = reactive({
imgUrl: '',
positionMapId: ''
}) //
const imgList = ref([]) //
//
const list = ref([])
const getList = async () => {
let data = await MapApi.getPositionMapGetMap()
let mapList = []
for (let key in data) {
mapList.push({
floor: key,
children: data[key]
})
}
list.value = mapList
//
if (data[1][1]) {
getMapData(data[1][1])
}
}
//
const getMapData = async (item) => {
let data = await MapApi.getPositionMapdDwnloadPngBase64({
floor: item.floor,
area: item.area
})
let base64Url = 'data:image/png;base64,'
imgBgObj.imgUrl = data
imgBgObj.positionMapId = item.id
}
const allMapPointInfo = ref([]) //
//
const interfaceRefreshed = ref(true)
@ -289,25 +253,25 @@ const resizeEnd = (x, y, w, h, item, index) => {
interfaceRefreshed.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
allMapPointInfo.value[index].locationX = x
allMapPointInfo.value[index].locationY = y
allMapPointInfo.value[index].locationWide = w
allMapPointInfo.value[index].locationDeep = h
addEditHistory()
})
}
//
const dragEnd = (x, y, item, index) => {
console.log('拖拽')
if (x === item.x && y === item.y) return
imgList.value[index].x = x
imgList.value[index].y = y
if (x === item.locationX && y === item.locationY) return
allMapPointInfo.value[index].locationX = x
allMapPointInfo.value[index].locationY = y
addEditHistory()
}
//
const rotateEnd = (angle, item, index) => {
console.log('旋转')
imgList.value[index].angle = angle
allMapPointInfo.value[index].angle = angle
addEditHistory()
}
@ -318,7 +282,7 @@ const activatedHandle = (item, index) => {
currentItemIndex.value = index
//
if (toolbarSwitchType.value === 'editNode' && item.labelType === 'node') {
if (toolbarSwitchType.value === 'editNode' && item.type !== 7) {
editNodePropertiesRef.value.open(item)
}
}
@ -333,9 +297,9 @@ const addEditHistory = () => {
if (currentIndex.value !== allHistoryList.value.length - 1) {
allHistoryList.value = allHistoryList.value.splice(0, currentIndex.value)
currentIndex.value = allHistoryList.value.length
allHistoryList.value.push(JSON.parse(JSON.stringify(imgList.value)))
allHistoryList.value.push(JSON.parse(JSON.stringify(allMapPointInfo.value)))
} else {
allHistoryList.value.push(JSON.parse(JSON.stringify(imgList.value)))
allHistoryList.value.push(JSON.parse(JSON.stringify(allMapPointInfo.value)))
currentIndex.value++
}
interfaceRefreshed.value = true
@ -346,7 +310,7 @@ const addEditHistory = () => {
const backPreviousStep = () => {
if (currentIndex.value > 0) {
currentIndex.value--
imgList.value = allHistoryList.value[currentIndex.value]
allMapPointInfo.value = allHistoryList.value[currentIndex.value]
console.log('撤销', allHistoryList.value[currentIndex.value])
} else {
message.warning('没了老铁')
@ -356,7 +320,7 @@ const backPreviousStep = () => {
const backNextStep = () => {
if (currentIndex.value < allHistoryList.value.length - 1) {
currentIndex.value++
imgList.value = allHistoryList.value[currentIndex.value]
allMapPointInfo.value = allHistoryList.value[currentIndex.value]
console.log('重做', allHistoryList.value[currentIndex.value])
} else {
message.warning('没了老铁')
@ -367,21 +331,21 @@ const backNextStep = () => {
const mapClick = (e) => {
if (toolbarSwitchType.value === 'drawNodes') {
//
imgList.value.push({
x: e.offsetX,
y: e.offsetY,
h: 16,
w: 16,
allMapPointInfo.value.push({
locationX: e.offsetX,
locationY: e.offsetY,
locationDeep: 16,
locationWide: 16,
angle: 0,
draggable: false,
resizable: false,
rotatable: false,
lockAspectRatio: false, //
img: '',
labelType: 'node'
type: 1 //1
})
currentIndex.value++
allHistoryList.value.push(JSON.parse(JSON.stringify(imgList.value)))
allHistoryList.value.push(JSON.parse(JSON.stringify(allMapPointInfo.value)))
}
//
if (toolbarSwitchType.value === 'text') {
@ -390,8 +354,8 @@ const mapClick = (e) => {
fontSize: state.textForm.fontSize,
fontFamily: state.textForm.fontFamily,
fontColor: state.textForm.fontColor,
x: e.offsetX,
y: e.offsetY
locationX: e.offsetX,
locationY: e.offsetY
}
//
@ -413,13 +377,13 @@ const textFormSuccess = (form) => {
const handleInputEnd = () => {
if (state.inputBoxValue) {
state.showInputBox = false
imgList.value.push({
labelType: 'text', //
allMapPointInfo.value.push({
type: 7, // 7
mapId: '', //id
x: state.inputBoxStyle.x, //x
y: state.inputBoxStyle.y, //y
h: '', //h
w: '', //w
locationX: state.inputBoxStyle.locationX, //x
locationY: state.inputBoxStyle.locationY, //y
locationDeep: '', //h
locationWide: '', //w
angle: 0, //
draggable: true, //
resizable: false, //
@ -445,13 +409,11 @@ const saveMap = async () => {
const saveNodeList = async () => {
formLoading.value = true
let list = allHistoryList.value[currentIndex.value].filter((item) => {
return item.labelType === 'node'
})
let list = allHistoryList.value[currentIndex.value]
list.forEach((item, index) => {
item.locationX = item.locationX || item.x
item.locationY = item.locationY || item.y
item.locationX = item.locationX
item.locationY = item.locationY
item.type = item.type || 1
if (item.type === 1) {
item.dataJson = ''
@ -509,7 +471,7 @@ const state = reactive({
{
switchType: 'revolve',
name: '旋转',
icon: 'ep:folder-add',
icon: 'ep:refresh-right',
isActive: false
},
{
@ -539,25 +501,25 @@ const state = reactive({
{
switchType: 'lineLibrary',
name: '线库',
icon: 'ep:folder-add',
icon: 'ep:message-box',
isActive: false
},
{
switchType: 'region',
name: '区域',
icon: 'ep:folder-add',
icon: 'ep:full-screen',
isActive: false
},
{
switchType: 'text',
name: '文字',
icon: 'ep:folder-add',
icon: 'ep:edit-pen',
isActive: false
},
{
switchType: 'equipment',
name: '设备',
icon: 'ep:folder-add',
icon: 'ep:video-camera-filled',
isActive: false
},
{
@ -569,19 +531,19 @@ const state = reactive({
{
switchType: 'layer',
name: '图层',
icon: 'ep:folder-add',
icon: 'ep:copy-document',
isActive: false
},
{
switchType: 'marker',
name: '标记',
icon: 'ep:folder-add',
icon: 'ep:map-location',
isActive: false
},
{
switchType: 'grid',
name: '网格',
icon: 'ep:folder-add',
icon: 'ep:grid',
isActive: false
},
{
@ -638,8 +600,8 @@ const state = reactive({
isShowToolbar: false, //
isShowGrid: false, //
moveForm: {
x: '',
y: ''
locationX: '',
locationY: ''
}, //
rotationForm: {
angle: ''
@ -687,6 +649,9 @@ const toolbarClick = (item) => {
if (toolbarSwitchType.value !== 'text') {
state.cursorStyle = `auto`
state.textFormToolShow = false
state.showInputBox = false
state.inputBoxValue = ''
}
switch (type) {
@ -728,14 +693,14 @@ const toolbarClick = (item) => {
case 'paste':
//
let copyObj = JSON.parse(JSON.stringify(state.copyMapItem))
copyObj.x = copyObj.x + 50
copyObj.y = copyObj.y + 50
imgList.value.push(copyObj)
copyObj.locationX = copyObj.locationX + 50
copyObj.locationY = copyObj.locationY + 50
allMapPointInfo.value.push(copyObj)
addEditHistory()
break
case 'delete':
//
imgList.value.splice(currentItemIndex.value, 1)
allMapPointInfo.value.splice(currentItemIndex.value, 1)
addEditHistory()
break
case 'tools':
@ -760,7 +725,6 @@ const toolbarClick = (item) => {
state.textFormToolShow = true
state.cursorStyle = `url('${cursorCollection.input}'),auto`
} else {
state.textFormToolShow = false
state.cursorStyle = `auto`
state.textFormToolShow = false
state.showInputBox = false
@ -828,8 +792,10 @@ const toolbarClick = (item) => {
//
const moveFormSubmit = () => {
allHistoryList.value[currentIndex.value][currentItemIndex.value].x = state.moveForm.x
allHistoryList.value[currentIndex.value][currentItemIndex.value].y = state.moveForm.y
allHistoryList.value[currentIndex.value][currentItemIndex.value].locationX =
state.moveForm.locationX
allHistoryList.value[currentIndex.value][currentItemIndex.value].locationY =
state.moveForm.locationY
}
//
const rotationFormSubmit = () => {
@ -883,38 +849,79 @@ const endDrawSelection = () => {
console.log(state.drawSelectionAreaSelectedPoints, '选中的')
}
onMounted(() => {
imgList.value = [
{
x: 100,
y: 100,
h: 100,
w: 100,
angle: 13,
draggable: true,
resizable: true,
rotatable: true,
lockAspectRatio: true, //
img: 'https://sys.znkjfw.com/imgs/process/%E8%AF%B7%E5%81%87.png',
labelType: 'icon'
},
{
x: 454.4,
y: 434.2,
w: 100,
h: 100,
angle: 0,
draggable: true,
resizable: true,
rotatable: true,
lockAspectRatio: false, //
img: 'https://sys.znkjfw.com/imgs/process/%E8%AF%B7%E5%81%87.png',
labelType: 'icon'
}
]
allHistoryList.value[0] = JSON.parse(JSON.stringify(imgList.value))
//
const list = ref([])
const getMapList = async () => {
let data = await MapApi.getPositionMapGetMap()
let mapList = []
for (let key in data) {
mapList.push({
floor: key,
children: data[key]
})
}
list.value = mapList
getList()
//
if (data[1][0]) {
getMapData(data[1][0])
}
}
//
//
const imgBgObj = reactive({
imgUrl: '',
positionMapId: '',
width: '',
height: ''
})
const getMapData = async (item) => {
let data = await MapApi.getPositionMapdDwnloadPngBase64({
floor: item.floor,
area: item.area
})
imgBgObj.imgUrl = data
imgBgObj.positionMapId = item.id
const img = new Image()
img.src = imgBgObj.imgUrl
imgBgObj.width = img.naturalWidth
imgBgObj.height = img.naturalHeight
console.log(imgBgObj)
getAllNodeList()
}
//
const getAllNodeList = async () => {
let list = await MapApi.getPositionMapItemList({
positionMapId: imgBgObj.positionMapId
})
list.forEach((item) => {
if (item.type === 2) {
//
let obj = JSON.parse(item.dataJson)[0]
// allMapPointInfo.value.push()
}
})
// allMapPointInfo.value = list.map((item) => {
// return {
// ...JSON.parse(item.dataJson)
// }
// })
// allMapPointInfo.value = allMapPointInfo.value
// console.log(allMapPointInfo.value)
allHistoryList.value[0] = JSON.parse(JSON.stringify(allMapPointInfo.value))
console.log(allHistoryList.value[0])
}
onMounted(() => {
getMapList()
})
</script>

View File

@ -0,0 +1,20 @@
```
{
labelType: 'wayPoint', //类型 路径点wayPoint 库位点locationPoint 设备点devicePoint 停车点parkingPoint 区域变更点areaPoint 等待点waitPoint 文字text
mapId: '', //地图id
x: 100, //left
y: 100, //top
h: 100, //高
w: 100, //宽
angle: 13, //旋转角度
draggable: true, //是否可以拖动
resizable: true, //是否可以调整大小
rotatable: true, //是否可以旋转
lockAspectRatio: true, //是否保持横纵比
img: 'https://sys.znkjfw.com/imgs/process/%E8%AF%B7%E5%81%87.png',
text: '', //文字
fontColor: '', //文字颜色
fontType: '', //文字类型
fontFamily: '' //字体类型
}
```