地图编辑

This commit is contained in:
yyy 2025-02-13 17:38:10 +08:00
parent c4a5ea4bb1
commit 2853959c19
6 changed files with 306 additions and 88 deletions

View File

@ -4,7 +4,7 @@ NODE_ENV=development
VITE_DEV=true
# 请求路径
VITE_BASE_URL='http://192.168.0.74:48080'
VITE_BASE_URL='http://192.168.0.66:48080'
# VITE_BASE_URL='http://192.168.0.189:48080'
# 文件上传类型server - 后端上传, client - 前端直连上传,仅支持 S3 服务

View File

@ -79,3 +79,11 @@ export const getDeviceInformationList = async (params) => {
export const mapBindDeviceInfo = async (data) => {
return await request.post({ url: `/system/device/information/mapBindDeviceInfo`, data })
}
//创建修改删除库区
export const createOrEditOrDelHouseArea = async (data) => {
return await request.post({ url: `/system/ware/house-area/createOrEditOrDel`, data })
}
//创建修改删除线库
export const createOrEditOrDelHouseLane = async (data) => {
return await request.post({ url: `/system/ware/house-lane/createOrEditOrDel`, data })
}

View File

@ -0,0 +1,78 @@
<template>
<!-- 新增设备 -->
<Dialog
v-model="dialogFormVisible"
title="物料区域设置"
width="600"
class="equipment-form-dialog"
>
<el-form :model="form" label-width="110">
<el-form-item label="物料区域名称" prop="skuInfo" required>
<el-input v-model="form.skuInfo" placeholder="请输入物料区域名称" />
</el-form-item>
<el-form-item label="物料名称" prop="areaName" required>
<el-input v-model="form.areaName" placeholder="请输入物料名称" />
</el-form-item>
<el-form-item label="库位数量" prop="areaNumber">
<el-input v-model="form.areaNumber" :disabled="true" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
<el-button type="primary" @click="submitForm"> 确定 </el-button>
</div>
</template>
</Dialog>
</template>
<script setup>
import { reactive, ref } from 'vue'
import * as MapApi from '@/api/map/map'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
const message = useMessage() //
const props = defineProps({
positionMapId: {
type: String,
default: () => ''
}
})
const dialogFormVisible = ref(false) //
//
const form = ref({
positionMapId: '',
skuInfo: '', //
areaName: '', //
areaNumber: 0,
mapItemIds: []
})
const open = (list) => {
dialogFormVisible.value = true
form.value.mapItemIds = list.map((item) => item.id)
form.value.areaNumber = form.value.mapItemIds.length
}
const submitForm = async () => {
form.value.positionMapId = props.positionMapId
await MapApi.createOrEditOrDelHouseArea(form.value)
dialogFormVisible.value = false
message.success('设置成功')
}
defineExpose({ open }) // open
</script>
<style lang="scss">
.equipment-form-dialog {
padding: 0px;
.el-dialog__footer {
padding: 0px 20px 20px 0;
border-top: none !important;
}
}
</style>

View File

@ -0,0 +1,74 @@
<template>
<!-- 新增设备 -->
<Dialog
v-model="dialogFormVisible"
title="物料区域设置"
width="600"
class="equipment-form-dialog"
>
<el-form :model="form" label-width="110">
<el-form-item label="线库名称" prop="laneName" required>
<el-input v-model="form.laneName" placeholder="请输入线库名称" />
</el-form-item>
<el-form-item label="库位数量" prop="areaNumber" required>
<el-input v-model="form.areaNumber" :disabled="true" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="dialogFormVisible = false">取消</el-button>
<el-button type="primary" @click="submitForm"> 确定 </el-button>
</div>
</template>
</Dialog>
</template>
<script setup>
import { reactive, ref } from 'vue'
import * as MapApi from '@/api/map/map'
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
const message = useMessage() //
const props = defineProps({
positionMapId: {
type: String,
default: () => ''
}
})
const dialogFormVisible = ref(false) //
//
const form = ref({
positionMapId: '',
laneName: '', //线
areaNumber: 0,
mapItemIds: []
})
const open = (list) => {
dialogFormVisible.value = true
form.value.mapItemIds = list.map((item) => item.id)
form.value.areaNumber = form.value.mapItemIds.length
}
const submitForm = async () => {
form.value.positionMapId = props.positionMapId
await MapApi.createOrEditOrDelHouseLane(form.value)
dialogFormVisible.value = false
message.success('设置成功')
}
defineExpose({ open }) // open
</script>
<style lang="scss">
.equipment-form-dialog {
padding: 0px;
.el-dialog__footer {
padding: 0px 20px 20px 0;
border-top: none !important;
}
}
</style>

View File

@ -72,14 +72,27 @@
<Icon :icon="item.icon" :size="24" />
<div class="name"> {{ item.name }} </div>
</div>
<!-- 分隔线 -->
<div
class="line"
v-if="
item.switchType === 'saveAs' ||
item.switchType === 'delete' ||
item.switchType === 'grid'
item.switchType === 'grid' ||
(item.switchType === 'next' &&
(toolbarSwitchType === 'lineLibrary' || toolbarSwitchType === 'region'))
"
></div>
<el-button
v-if="
item.switchType === 'next' &&
(toolbarSwitchType === 'lineLibrary' || toolbarSwitchType === 'region')
"
type="danger"
class="selection-area-btn"
@click="clickDrawSelectionArea"
>确定</el-button
>
</div>
</div>
<div class="right-tool-list" v-if="state.isShowToolbar">
@ -230,9 +243,24 @@
<!-- 文档 https://github.com/a7650/vue3-draggable-resizable/blob/main/docs/document_zh.md#resizable -->
</div>
<!-- 绘制框选区域 -->
<!-- 框选区域 -->
<div
v-if="state.drawSelectionAreaSelectedPoints"
v-for="(box, index) in state.allDrawSelectionAreaBox"
:key="index"
:style="{
position: 'absolute',
left: `${box.x}px`,
top: `${box.y}px`,
width: `${box.width}px`,
height: `${box.height}px`,
border: '2px dashed #007bff',
backgroundColor: 'rgba(0, 123, 255, 0.1)',
zIndex: 999
}"
></div>
<!-- 当前框选区域 -->
<div
v-if="state.drawSelectionAreaShow"
:style="{
position: 'absolute',
left: `${state.drawSelectionAreaBox.x}px`,
@ -241,10 +269,10 @@
height: `${state.drawSelectionAreaBox.height}px`,
border: '2px dashed #007bff',
backgroundColor: 'rgba(0, 123, 255, 0.1)',
zIndex: 99999
zIndex: 999
}"
@click="clickDrawSelectionArea"
></div>
<!-- 文字输入区域 -->
<input
v-if="state.showInputBox"
@ -259,6 +287,7 @@
left: state.inputBoxStyle.locationX + 'px',
top: state.inputBoxStyle.locationY + 'px'
}"
:maxlength="30"
class="input-box-class"
/>
</div>
@ -284,6 +313,13 @@
/>
<!-- 设备弹窗选择 -->
<equipmentToolDialog ref="equipmentToolDialogRef" :positionMapId="imgBgObj.positionMapId" />
<!-- 区域选择 -->
<itemAreaSettingDialog ref="itemAreaSettingDialogRef" :positionMapId="imgBgObj.positionMapId" />
<!-- 线库设置 -->
<lineLibrarySettingDialog
ref="lineLibrarySettingDialogRef"
:positionMapId="imgBgObj.positionMapId"
/>
</template>
<script setup>
@ -291,6 +327,8 @@ import { ref, defineComponent, reactive, nextTick, onMounted } from 'vue'
import editNodeProperties from './components-tool/editNodeProperties.vue'
import textFormToolDialog from './components-tool/textFormToolDialog.vue'
import equipmentToolDialog from './components-tool/equipmentToolDialog.vue'
import itemAreaSettingDialog from './components-tool/itemAreaSettingDialog.vue'
import lineLibrarySettingDialog from './components-tool/lineLibrarySettingDialog.vue'
import layerSelectionToolDialog from './components-tool/layerSelectionToolDialog.vue'
import * as MapApi from '@/api/map/map'
@ -298,6 +336,8 @@ import cursorCollection from './cursorCollection'
defineOptions({ name: 'editMapPageRealTimeMap' })
const lineLibrarySettingDialogRef = ref() //线
const itemAreaSettingDialogRef = ref() //
const equipmentToolDialogRef = ref() //
const textFormToolDialogRef = ref() //
const inputBoxRef = ref() //
@ -643,9 +683,10 @@ const state = reactive({
copyMapItem: '', //
cursorStyle: 'auto',
drawSelectionAreaShow: false, //
allDrawSelectionAreaBox: [], //
drawSelectionAreaBox: { x: 0, y: 0, width: 0, height: 0 }, //
drawSelectionAreaStartPos: { x: 0, y: 0 }, //
drawSelectionAreaSelectedPoints: [], //list
drawSelectionStartPoint: { x: 0, y: 0 }, //
drawSelectionPointList: [], //list
textFormToolShow: false, //
showInputBox: false, //
inputBoxStyle: {
@ -689,6 +730,13 @@ const toolbarClick = (item) => {
state.inputBoxValue = ''
}
//
if (toolbarSwitchType.value !== 'lineLibrary' && toolbarSwitchType.value !== 'region') {
state.drawSelectionAreaShow = false
state.allDrawSelectionAreaBox = []
state.drawSelectionPointList = []
}
switch (type) {
case 'open':
//
@ -869,57 +917,69 @@ const rotationFormSubmit = () => {
const startDrawSelection = (event) => {
if (toolbarSwitchType.value !== 'lineLibrary' && toolbarSwitchType.value !== 'region') return
state.drawSelectionAreaShow = true
state.drawSelectionAreaStartPos = { x: event.offsetX, y: event.offsetY }
state.drawSelectionStartPoint = { x: event.offsetX, y: event.offsetY }
state.drawSelectionAreaBox = { x: event.offsetX, y: event.offsetY, width: 0, height: 0 }
//
event.preventDefault()
}
//
const updateDrawSelection = (event) => {
if (toolbarSwitchType.value !== 'lineLibrary' && toolbarSwitchType.value !== 'region') return
if (state.drawSelectionAreaShow) {
state.drawSelectionAreaBox.width = event.offsetX - state.drawSelectionAreaStartPos.x
state.drawSelectionAreaBox.height = event.offsetY - state.drawSelectionAreaStartPos.y
state.drawSelectionAreaBox.width = event.offsetX - state.drawSelectionStartPoint.x
state.drawSelectionAreaBox.height = event.offsetY - state.drawSelectionStartPoint.y
}
//
event.preventDefault()
}
//
const endDrawSelection = () => {
if (toolbarSwitchType.value !== 'lineLibrary' && toolbarSwitchType.value !== 'region') return
state.drawSelectionAreaShow = false
let points = allHistoryList.value[currentIndex.value]
state.drawSelectionAreaSelectedPoints = points.filter((point) => {
return (
point.locationX >=
Math.min(
state.drawSelectionAreaStartPos.x,
state.drawSelectionAreaStartPos.x + state.drawSelectionAreaBox.width
) &&
point.locationX <=
Math.max(
state.drawSelectionAreaStartPos.x,
state.drawSelectionAreaStartPos.x + state.drawSelectionAreaBox.width
) &&
point.locationY >=
Math.min(
state.drawSelectionAreaStartPos.y,
state.drawSelectionAreaStartPos.y + state.drawSelectionAreaBox.height
) &&
point.locationY <=
Math.max(
state.drawSelectionAreaStartPos.y,
state.drawSelectionAreaStartPos.y + state.drawSelectionAreaBox.height
)
)
})
state.allDrawSelectionAreaBox.push({ ...state.drawSelectionAreaBox })
state.drawSelectionAreaBox = { x: 0, y: 0, width: 0, height: 0 }
}
//
const clickDrawSelectionArea = () => {
let points = allHistoryList.value[currentIndex.value]
state.drawSelectionPointList = []
state.allDrawSelectionAreaBox.forEach((box) => {
points.forEach((point) => {
if (
point.locationX >= box.x &&
point.locationX <= box.x + box.width &&
point.locationY >= box.y &&
point.locationY <= box.y + box.height
) {
state.drawSelectionPointList.push(point)
}
})
})
console.log(state.drawSelectionPointList, '选中的')
//
state.allDrawSelectionAreaBox = []
//
let binLocation = state.drawSelectionPointList.filter((item) => item.type === 2)
if (toolbarSwitchType.value === 'lineLibrary') {
//线
if (binLocation.length < 2) {
message.warning('至少选择两个库位')
} else {
lineLibrarySettingDialogRef.value.open(binLocation)
}
}
if (toolbarSwitchType.value === 'region') {
//
if (binLocation.length < 1) {
message.warning('至少选择两个库位')
} else {
itemAreaSettingDialogRef.value.open(binLocation)
}
}
// console.log(state.drawSelectionAreaSelectedPoints, '')
}
//
@ -989,7 +1049,7 @@ const getAllNodeList = async () => {
item.dataList = JSON.parse(item.dataJson)
item.locationDeep = item.dataList[0].locationDeep
item.locationWide = item.dataList[0].locationWide
item.draggable = false
item.draggable = true
item.resizable = false
item.rotatable = false
item.lockAspectRatio = true
@ -998,7 +1058,7 @@ const getAllNodeList = async () => {
item.dataList = []
item.locationDeep = item.dataObj.locationDeep
item.locationWide = item.dataObj.locationWide
item.draggable = false
item.draggable = true
item.resizable = false
item.rotatable = false
item.lockAspectRatio = true
@ -1185,4 +1245,9 @@ onMounted(() => {
padding: 4px;
outline: none;
}
.selection-area-btn {
width: 80px;
margin-left: 6px;
}
</style>

View File

@ -19,20 +19,21 @@
}"
></div>
<!-- 坐标点 -->
<div
v-for="(point, index) in points"
<!-- 图片 -->
<img
v-for="(img, index) in images"
:key="index"
:src="img.src"
:style="{
position: 'absolute',
left: `${point.x}px`,
top: `${point.y}px`,
width: '10px',
height: '10px',
backgroundColor: selectedPoints.includes(point) ? 'red' : 'blue',
borderRadius: '50%'
left: `${img.x}px`,
top: `${img.y}px`,
width: '100px',
height: '100px',
userSelect: isSelecting ? 'none' : 'auto', //
pointerEvents: isSelecting ? 'none' : 'auto' //
}"
></div>
/>
</div>
</template>
@ -41,13 +42,11 @@ import { ref } from 'vue'
export default {
setup() {
//
const points = ref([
{ x: 300, y: 400 },
{ x: 400, y: 400 },
{ x: 200, y: 360 },
{ x: 570, y: 260 },
{ x: 600, y: 477 }
//
const images = ref([
{ src: 'https://via.placeholder.com/100', x: 100, y: 100 },
{ src: 'https://via.placeholder.com/100', x: 300, y: 200 },
{ src: 'https://via.placeholder.com/100', x: 500, y: 300 }
])
//
@ -61,64 +60,52 @@ export default {
//
const isSelecting = ref(false)
//
const selectedPoints = ref([])
//
const startPos = ref({ x: 0, y: 0 })
//
const startSelection = (event) => {
isSelecting.value = true
startPos.value = { x: event.offsetX, y: event.offsetY }
startPos.value = { x: event.clientX, y: event.clientY }
selectionBox.value = {
x: event.offsetX,
y: event.offsetY,
x: event.clientX,
y: event.clientY,
width: 0,
height: 0
}
//
event.preventDefault()
}
//
const updateSelection = (event) => {
if (!isSelecting.value) return
const offsetX = event.offsetX
const offsetY = event.offsetY
const currentX = event.clientX
const currentY = event.clientY
selectionBox.value = {
x: Math.min(startPos.value.x, offsetX),
y: Math.min(startPos.value.y, offsetY),
width: Math.abs(offsetX - startPos.value.x),
height: Math.abs(offsetY - startPos.value.y)
x: Math.min(startPos.value.x, currentX),
y: Math.min(startPos.value.y, currentY),
width: Math.abs(currentX - startPos.value.x),
height: Math.abs(currentY - startPos.value.y)
}
//
event.preventDefault()
}
//
const endSelection = () => {
isSelecting.value = false
checkSelectedPoints()
}
//
const checkSelectedPoints = () => {
selectedPoints.value = points.value.filter((point) => {
return (
point.x >= selectionBox.value.x &&
point.x <= selectionBox.value.x + selectionBox.value.width &&
point.y >= selectionBox.value.y &&
point.y <= selectionBox.value.y + selectionBox.value.height
)
})
console.log('选中的点:', selectedPoints.value)
console.log('框选区域:', selectionBox.value)
}
return {
points,
images,
selectionBox,
isSelecting,
selectedPoints,
startSelection,
updateSelection,
endSelection
@ -128,5 +115,11 @@ export default {
</script>
<style scoped>
/* 可选:添加一些样式 */
/* 禁用用户选择 */
img {
user-select: none;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
}
</style>