This commit is contained in:
xhf 2025-02-13 17:39:31 +08:00
commit 168c463451
10 changed files with 761 additions and 262 deletions

View File

@ -79,3 +79,11 @@ export const getDeviceInformationList = async (params) => {
export const mapBindDeviceInfo = async (data) => { export const mapBindDeviceInfo = async (data) => {
return await request.post({ url: `/system/device/information/mapBindDeviceInfo`, 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

@ -25,7 +25,12 @@
required required
v-if="form.type === 2" v-if="form.type === 2"
> >
<el-input-number v-model="form.layersNumber" :min="1" :max="3" /> <el-input-number
v-model="form.layersNumber"
:min="1"
:max="3"
@change="layersNumberChange"
/>
</el-form-item> </el-form-item>
<el-form-item <el-form-item
label="编号" label="编号"
@ -77,6 +82,7 @@
<span class="ml-2">cm</span> <span class="ml-2">cm</span>
</div> </div>
</el-form-item> </el-form-item>
<div v-if="form.type === 2 || form.type === 4">
<el-form-item label="库位方向" prop="direction"> <el-form-item label="库位方向" prop="direction">
<el-select <el-select
v-model="form.direction" v-model="form.direction"
@ -104,6 +110,7 @@
</el-form-item> </el-form-item>
</div> </div>
</div> </div>
</div>
</el-form> </el-form>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">
@ -164,20 +171,17 @@ const submit = async (formEl) => {
form.value.dataJson = '' form.value.dataJson = ''
} else if (form.value.type === 2) { } else if (form.value.type === 2) {
// //
let list = []
for (let index = 0; index < form.value.layersNumber; index++) { for (let index = 0; index < form.value.layersNumber; index++) {
if ( if (
form.value.dataList.length > 0 && form.value.dataList.length > 0 &&
form.value.dataList[index] && form.value.dataList[index] &&
form.value.dataList[index].laneId form.value.dataList[index].id
) { ) {
form.value.dataList[index].locationWide = form.value.locationWide list.push(form.value.dataList[index])
form.value.dataList[index].locationDeep = form.value.locationDeep
form.value.dataList[index].direction = form.value.direction //
form.value.dataList[index].inDirection = form.value.inDirection //
form.value.dataList[index].outDirection = form.value.outDirection //
form.value.dataList[index].locationStorey = index + 1 //
} else { } else {
form.value.dataList.push({ list.push({
positionMapId: props.positionMapId, positionMapId: props.positionMapId,
locationWide: form.value.locationWide || undefined, locationWide: form.value.locationWide || undefined,
locationDeep: form.value.locationDeep || undefined, locationDeep: form.value.locationDeep || undefined,
@ -188,8 +192,18 @@ const submit = async (formEl) => {
}) })
} }
} }
form.value.dataList = list
//dataJson //dataJson
form.value.dataJson = JSON.stringify(form.value.dataList) form.value.dataJson = JSON.stringify(form.value.dataList)
} else if (form.value.type === 3) {
//
form.value.dataObj.positionMapId = props.positionMapId
form.value.dataObj.locationWide = form.value.locationWide
form.value.dataObj.locationDeep = form.value.locationDeep
form.value.dataObj.direction = form.value.direction
form.value.dataObj.inDirection = form.value.inDirection
form.value.dataObj.outDirection = form.value.outDirection
form.value.dataObj.deviceType = deviceInfo.value.deviceType //
} else { } else {
// //
form.value.dataObj.positionMapId = props.positionMapId form.value.dataObj.positionMapId = props.positionMapId
@ -206,9 +220,13 @@ const submit = async (formEl) => {
}) })
} }
//
const layersNumberChange = (e) => {}
const open = (item) => { const open = (item) => {
console.log(item)
form.value = item form.value = item
form.value.layersNumber = item.layersNumber || 1 //1 form.value.layersNumber = item.dataList.length || ''
form.value.positionMapId = props.positionMapId form.value.positionMapId = props.positionMapId
dialogFormVisible.value = true dialogFormVisible.value = true
} }
@ -219,6 +237,12 @@ const typeChange = () => {
form.value.layersNumber = undefined form.value.layersNumber = undefined
form.value.dataJson = '' form.value.dataJson = ''
} }
if (form.value.type !== 2 && form.value.type !== 4) {
form.value.direction = undefined
form.value.inDirection = undefined
form.value.outDirection = undefined
}
} }
// //
const directionChange = (e) => { const directionChange = (e) => {

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

@ -18,23 +18,26 @@
<script setup> <script setup>
import { reactive, ref } from 'vue' import { reactive, ref } from 'vue'
const emit = defineEmits(['layerSelectionSuccess'])
const list = ref([ const list = ref([
{ {
labelType: 'locationPoint', type: 2,
name: '库位点', name: '库位点',
isShow: true isShow: true
}, },
{ {
labelType: 'wayPoint', type: 5,
name: '路径点', name: '路径点',
isShow: true isShow: true
}, },
{ {
labelType: 'devicePoint', type: 3,
name: '设备点', name: '设备点',
isShow: true isShow: true
} }
]) ])
const isUnfold = ref(true) const isUnfold = ref(true)
const changeUnfold = () => { const changeUnfold = () => {
@ -42,6 +45,8 @@ const changeUnfold = () => {
} }
const changeSelectList = (item) => { const changeSelectList = (item) => {
item.isShow = !item.isShow item.isShow = !item.isShow
const types = list.value.filter((item) => !item.isShow).map((item) => item.type)
emit('layerSelectionSuccess', types)
} }
const open = (item) => {} const open = (item) => {}

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" /> <Icon :icon="item.icon" :size="24" />
<div class="name"> {{ item.name }} </div> <div class="name"> {{ item.name }} </div>
</div> </div>
<!-- 分隔线 -->
<div <div
class="line" class="line"
v-if=" v-if="
item.switchType === 'saveAs' || item.switchType === 'saveAs' ||
item.switchType === 'delete' || item.switchType === 'delete' ||
item.switchType === 'grid' item.switchType === 'grid' ||
(item.switchType === 'next' &&
(toolbarSwitchType === 'lineLibrary' || toolbarSwitchType === 'region'))
" "
></div> ></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> </div>
<div class="right-tool-list" v-if="state.isShowToolbar"> <div class="right-tool-list" v-if="state.isShowToolbar">
@ -130,7 +143,7 @@
> >
<VueDragResizeRotate <VueDragResizeRotate
v-for="(item, index) in allHistoryList[currentIndex]" v-for="(item, index) in allHistoryList[currentIndex]"
:key="index" :key="item.locationX"
:parent="true" :parent="true"
:x="item.locationX" :x="item.locationX"
:y="item.locationY" :y="item.locationY"
@ -150,7 +163,7 @@
> >
<!-- 1 路径点 --> <!-- 1 路径点 -->
<div <div
v-if="item.type === 1" v-if="item.type === 1 && item.layerSelectionShow"
:style="{ :style="{
width: item.locationWide + 'px', width: item.locationWide + 'px',
height: item.locationDeep + 'px', height: item.locationDeep + 'px',
@ -162,7 +175,7 @@
</div> </div>
<!-- 2 库位点 --> <!-- 2 库位点 -->
<img <img
v-if="item.type === 2" v-if="item.type === 2 && item.layerSelectionShow"
src="https://api.znkjfw.com/admin-api/infra/file/4/get/库位库存_png_179_1739326653035.png" src="https://api.znkjfw.com/admin-api/infra/file/4/get/库位库存_png_179_1739326653035.png"
alt="" alt=""
:style="{ :style="{
@ -173,7 +186,7 @@
/> />
<!-- 3 设备点 --> <!-- 3 设备点 -->
<img <img
v-if="item.type === 3" v-if="item.type === 3 && item.layerSelectionShow"
src="https://api.znkjfw.com/admin-api/infra/file/4/get/设备点_png_179_1739327151877.png" src="https://api.znkjfw.com/admin-api/infra/file/4/get/设备点_png_179_1739327151877.png"
alt="" alt=""
:style="{ :style="{
@ -184,7 +197,7 @@
/> />
<!-- 4 停车点 --> <!-- 4 停车点 -->
<img <img
v-if="item.type === 4" v-if="item.type === 4 && item.layerSelectionShow"
src="https://api.znkjfw.com/admin-api/infra/file/4/get/停车场-01_png_179_1739326933020.png" src="https://api.znkjfw.com/admin-api/infra/file/4/get/停车场-01_png_179_1739326933020.png"
alt="" alt=""
:style="{ :style="{
@ -195,7 +208,7 @@
/> />
<!-- 5 区域变更点 --> <!-- 5 区域变更点 -->
<img <img
v-if="item.type === 5" v-if="item.type === 5 && item.layerSelectionShow"
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="{
@ -206,7 +219,7 @@
/> />
<!-- 6 等待点 --> <!-- 6 等待点 -->
<img <img
v-if="item.type === 6" v-if="item.type === 6 && item.layerSelectionShow"
src="https://api.znkjfw.com/admin-api/infra/file/4/get/等待点_png_179_1739326991439.png" src="https://api.znkjfw.com/admin-api/infra/file/4/get/等待点_png_179_1739326991439.png"
alt="" alt=""
:style="{ :style="{
@ -216,7 +229,7 @@
}" }"
/> />
<div <div
v-if="item.type === 7" v-if="item.type === 7 && item.layerSelectionShow"
:style="{ :style="{
color: item.fontColor, color: item.fontColor,
fontSize: item.fontSize + 'px', fontSize: item.fontSize + 'px',
@ -230,20 +243,36 @@
<!-- 文档 https://github.com/a7650/vue3-draggable-resizable/blob/main/docs/document_zh.md#resizable --> <!-- 文档 https://github.com/a7650/vue3-draggable-resizable/blob/main/docs/document_zh.md#resizable -->
</div> </div>
<!-- 绘制框选区域 --> <!-- 框选区域 -->
<div <div
v-if="state.drawSelectionArea" 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="{ :style="{
position: 'absolute', position: 'absolute',
left: `${state.drawSelectionAreaBox.x}px`, left: `${state.drawSelectionAreaBox.x}px`,
top: `${state.drawSelectionAreaBox.y}px`, top: `${state.drawSelectionAreaBox.y}px`,
width: `${state.drawSelectionAreaBox.width}px`, width: `${state.drawSelectionAreaBox.width}px`,
height: `${state.drawSelectionAreaBox.height}px`, height: `${state.drawSelectionAreaBox.height}px`,
border: '1px solid blue', border: '2px dashed #007bff',
backgroundColor: 'rgba(0, 0, 255, 0.1)', backgroundColor: 'rgba(0, 123, 255, 0.1)',
zIndex: 99999 zIndex: 999
}" }"
></div> ></div>
<!-- 文字输入区域 --> <!-- 文字输入区域 -->
<input <input
v-if="state.showInputBox" v-if="state.showInputBox"
@ -258,6 +287,7 @@
left: state.inputBoxStyle.locationX + 'px', left: state.inputBoxStyle.locationX + 'px',
top: state.inputBoxStyle.locationY + 'px' top: state.inputBoxStyle.locationY + 'px'
}" }"
:maxlength="30"
class="input-box-class" class="input-box-class"
/> />
</div> </div>
@ -276,9 +306,20 @@
@textFormSuccess="textFormSuccess" @textFormSuccess="textFormSuccess"
/> />
<!-- 图层选择 --> <!-- 图层选择 -->
<layerSelectionToolDialog v-if="state.isShowLayer" ref="layerSelectionToolDialogRef" /> <layerSelectionToolDialog
v-if="state.isShowLayer"
ref="layerSelectionToolDialogRef"
@layerSelectionSuccess="layerSelectionSuccess"
/>
<!-- 设备弹窗选择 --> <!-- 设备弹窗选择 -->
<equipmentToolDialog ref="equipmentToolDialogRef" :positionMapId="imgBgObj.positionMapId" /> <equipmentToolDialog ref="equipmentToolDialogRef" :positionMapId="imgBgObj.positionMapId" />
<!-- 区域选择 -->
<itemAreaSettingDialog ref="itemAreaSettingDialogRef" :positionMapId="imgBgObj.positionMapId" />
<!-- 线库设置 -->
<lineLibrarySettingDialog
ref="lineLibrarySettingDialogRef"
:positionMapId="imgBgObj.positionMapId"
/>
</template> </template>
<script setup> <script setup>
@ -286,6 +327,8 @@ import { ref, defineComponent, reactive, nextTick, onMounted } from 'vue'
import editNodeProperties from './components-tool/editNodeProperties.vue' import editNodeProperties from './components-tool/editNodeProperties.vue'
import textFormToolDialog from './components-tool/textFormToolDialog.vue' import textFormToolDialog from './components-tool/textFormToolDialog.vue'
import equipmentToolDialog from './components-tool/equipmentToolDialog.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 layerSelectionToolDialog from './components-tool/layerSelectionToolDialog.vue'
import * as MapApi from '@/api/map/map' import * as MapApi from '@/api/map/map'
@ -293,6 +336,8 @@ import cursorCollection from './cursorCollection'
defineOptions({ name: 'editMapPageRealTimeMap' }) defineOptions({ name: 'editMapPageRealTimeMap' })
const lineLibrarySettingDialogRef = ref() //线
const itemAreaSettingDialogRef = ref() //
const equipmentToolDialogRef = ref() // const equipmentToolDialogRef = ref() //
const textFormToolDialogRef = ref() // const textFormToolDialogRef = ref() //
const inputBoxRef = ref() // const inputBoxRef = ref() //
@ -391,6 +436,7 @@ const mapClick = (e) => {
// //
allMapPointInfo.value.push({ allMapPointInfo.value.push({
positionMapId: imgBgObj.positionMapId, //id positionMapId: imgBgObj.positionMapId, //id
layerSelectionShow: true,
locationX: e.offsetX, locationX: e.offsetX,
locationY: e.offsetY, locationY: e.offsetY,
locationDeep: 16, locationDeep: 16,
@ -449,7 +495,8 @@ const handleInputEnd = () => {
fontColor: state.inputBoxStyle.fontColor, // fontColor: state.inputBoxStyle.fontColor, //
fontFamily: state.inputBoxStyle.fontFamily, // fontFamily: state.inputBoxStyle.fontFamily, //
fontSize: state.inputBoxStyle.fontSize, fontSize: state.inputBoxStyle.fontSize,
dataObj: {} // dataObj: {}, //
layerSelectionShow: true
}) })
addEditHistory() addEditHistory()
state.inputBoxValue = '' state.inputBoxValue = ''
@ -523,7 +570,7 @@ const state = reactive({
switchType: 'tools', switchType: 'tools',
name: '工具', name: '工具',
icon: 'ep:briefcase', icon: 'ep:briefcase',
isActive: false isActive: true
}, },
{ {
switchType: 'lineLibrary', switchType: 'lineLibrary',
@ -571,7 +618,7 @@ const state = reactive({
switchType: 'grid', switchType: 'grid',
name: '网格', name: '网格',
icon: 'ep:grid', icon: 'ep:grid',
isActive: false isActive: true
}, },
{ {
switchType: 'larger', switchType: 'larger',
@ -624,8 +671,8 @@ const state = reactive({
isActive: false isActive: false
} }
], ],
isShowToolbar: false, // isShowToolbar: true, //
isShowGrid: false, // isShowGrid: true, //
moveForm: { moveForm: {
locationX: '', locationX: '',
locationY: '' locationY: ''
@ -635,10 +682,11 @@ const state = reactive({
}, // }, //
copyMapItem: '', // copyMapItem: '', //
cursorStyle: 'auto', cursorStyle: 'auto',
drawSelectionArea: false, // drawSelectionAreaShow: false, //
allDrawSelectionAreaBox: [], //
drawSelectionAreaBox: { x: 0, y: 0, width: 0, height: 0 }, // drawSelectionAreaBox: { x: 0, y: 0, width: 0, height: 0 }, //
drawSelectionAreaStartPos: { x: 0, y: 0 }, // drawSelectionStartPoint: { x: 0, y: 0 }, //
drawSelectionAreaSelectedPoints: [], //list drawSelectionPointList: [], //list
textFormToolShow: false, // textFormToolShow: false, //
showInputBox: false, // showInputBox: false, //
inputBoxStyle: { inputBoxStyle: {
@ -654,7 +702,7 @@ const state = reactive({
const toolbarClick = (item) => { const toolbarClick = (item) => {
let type = item.switchType let type = item.switchType
if ( if (
currentItemIndex.value && currentItemIndex.value === -1 &&
(type === 'move' || type === 'revolve' || type === 'copy' || type === 'delete') (type === 'move' || type === 'revolve' || type === 'copy' || type === 'delete')
) { ) {
message.warning('请先选择要操作的对象') message.warning('请先选择要操作的对象')
@ -682,6 +730,13 @@ const toolbarClick = (item) => {
state.inputBoxValue = '' state.inputBoxValue = ''
} }
//
if (toolbarSwitchType.value !== 'lineLibrary' && toolbarSwitchType.value !== 'region') {
state.drawSelectionAreaShow = false
state.allDrawSelectionAreaBox = []
state.drawSelectionPointList = []
}
switch (type) { switch (type) {
case 'open': case 'open':
// //
@ -716,13 +771,29 @@ const toolbarClick = (item) => {
break break
case 'copy': case 'copy':
// //
state.copyMapItem = allHistoryList.value[currentIndex.value][currentItemIndex.value] let copyMapItem = allHistoryList.value[currentIndex.value][currentItemIndex.value]
state.copyMapItem = {
positionMapId: copyMapItem.positionMapId,
locationX: copyMapItem.locationX,
locationY: copyMapItem.locationY,
type: copyMapItem.type,
dataJson: copyMapItem.dataJson || '',
dataObj: copyMapItem.dataObj || {},
dataList: copyMapItem.dataList || [],
locationDeep: copyMapItem.locationDeep,
locationWide: copyMapItem.locationWide,
draggable: copyMapItem.draggable,
resizable: copyMapItem.resizable,
rotatable: copyMapItem.rotatable,
lockAspectRatio: copyMapItem.lockAspectRatio
}
message.success('复制成功')
break break
case 'paste': case 'paste':
// //
let copyObj = JSON.parse(JSON.stringify(state.copyMapItem)) let copyObj = JSON.parse(JSON.stringify(state.copyMapItem))
copyObj.locationX = copyObj.locationX + 50 copyObj.locationX = Number(copyObj.locationX) + 50
copyObj.locationY = copyObj.locationY + 50 copyObj.locationY = Number(copyObj.locationY) + 50
allMapPointInfo.value.push(copyObj) allMapPointInfo.value.push(copyObj)
addEditHistory() addEditHistory()
break break
@ -845,48 +916,70 @@ const rotationFormSubmit = () => {
// //
const startDrawSelection = (event) => { const startDrawSelection = (event) => {
if (toolbarSwitchType.value !== 'lineLibrary' && toolbarSwitchType.value !== 'region') return if (toolbarSwitchType.value !== 'lineLibrary' && toolbarSwitchType.value !== 'region') return
state.drawSelectionArea = true 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 } state.drawSelectionAreaBox = { x: event.offsetX, y: event.offsetY, width: 0, height: 0 }
//
event.preventDefault()
} }
//
const updateDrawSelection = (event) => { const updateDrawSelection = (event) => {
if (toolbarSwitchType.value !== 'lineLibrary' && toolbarSwitchType.value !== 'region') return if (toolbarSwitchType.value !== 'lineLibrary' && toolbarSwitchType.value !== 'region') return
if (state.drawSelectionArea) { if (state.drawSelectionAreaShow) {
state.drawSelectionAreaBox.width = event.offsetX - state.drawSelectionAreaStartPos.x state.drawSelectionAreaBox.width = event.offsetX - state.drawSelectionStartPoint.x
state.drawSelectionAreaBox.height = event.offsetY - state.drawSelectionAreaStartPos.y state.drawSelectionAreaBox.height = event.offsetY - state.drawSelectionStartPoint.y
} }
//
event.preventDefault()
} }
// //
const endDrawSelection = () => { const endDrawSelection = () => {
if (toolbarSwitchType.value !== 'lineLibrary' && toolbarSwitchType.value !== 'region') return if (toolbarSwitchType.value !== 'lineLibrary' && toolbarSwitchType.value !== 'region') return
state.drawSelectionAreaShow = false
state.allDrawSelectionAreaBox.push({ ...state.drawSelectionAreaBox })
state.drawSelectionAreaBox = { x: 0, y: 0, width: 0, height: 0 }
}
//
const clickDrawSelectionArea = () => {
let points = allHistoryList.value[currentIndex.value] let points = allHistoryList.value[currentIndex.value]
state.drawSelectionArea = false
state.drawSelectionAreaSelectedPoints = points.filter((point) => { state.drawSelectionPointList = []
return ( state.allDrawSelectionAreaBox.forEach((box) => {
point.locationX >= points.forEach((point) => {
Math.min( if (
state.drawSelectionAreaStartPos.x, point.locationX >= box.x &&
state.drawSelectionAreaStartPos.x + state.drawSelectionAreaBox.width point.locationX <= box.x + box.width &&
) && point.locationY >= box.y &&
point.locationX <= point.locationY <= box.y + box.height
Math.max( ) {
state.drawSelectionAreaStartPos.x, state.drawSelectionPointList.push(point)
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
)
)
}) })
console.log(state.drawSelectionAreaSelectedPoints, '选中的') })
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)
}
}
} }
// //
@ -938,15 +1031,19 @@ const getAllNodeList = async () => {
positionMapId: imgBgObj.positionMapId positionMapId: imgBgObj.positionMapId
}) })
list.forEach((item) => { list.forEach((item) => {
item.layerSelectionShow = true //
item.locationX = Number(item.locationX)
item.locationY = Number(item.locationY)
if (item.type === 1 || item.type === 5 || item.type === 6) { if (item.type === 1 || item.type === 5 || item.type === 6) {
item.dataObj = {} item.dataObj = {}
item.dataList = [] item.dataList = []
item.locationDeep = 16 item.locationDeep = 16
item.locationWide = 16 item.locationWide = 16
item.draggable = true item.draggable = false
item.resizable = false item.resizable = false
item.rotatable = false item.rotatable = false
item.lockAspectRatio = false item.lockAspectRatio = true
} else if (item.type === 2) { } else if (item.type === 2) {
// //
item.dataList = JSON.parse(item.dataJson) item.dataList = JSON.parse(item.dataJson)
@ -955,7 +1052,7 @@ const getAllNodeList = async () => {
item.draggable = true item.draggable = true
item.resizable = false item.resizable = false
item.rotatable = false item.rotatable = false
item.lockAspectRatio = false item.lockAspectRatio = true
} else if (item.type === 3 || item.type === 4) { } else if (item.type === 3 || item.type === 4) {
item.dataObj = JSON.parse(item.dataJson) item.dataObj = JSON.parse(item.dataJson)
item.dataList = [] item.dataList = []
@ -964,7 +1061,7 @@ const getAllNodeList = async () => {
item.draggable = true item.draggable = true
item.resizable = false item.resizable = false
item.rotatable = false item.rotatable = false
item.lockAspectRatio = false item.lockAspectRatio = true
} else if (item.type === 7) { } else if (item.type === 7) {
item.dataObj = JSON.parse(item.dataJson) item.dataObj = JSON.parse(item.dataJson)
item.text = item.dataObj.text item.text = item.dataObj.text
@ -974,8 +1071,8 @@ const getAllNodeList = async () => {
item.angle = item.dataObj.angle item.angle = item.dataObj.angle
item.draggable = true item.draggable = true
item.resizable = false item.resizable = false
item.rotatable = false item.rotatable = true
item.lockAspectRatio = false item.lockAspectRatio = true
} }
allMapPointInfo.value.push(item) allMapPointInfo.value.push(item)
}) })
@ -1015,6 +1112,18 @@ const saveNodeList = async () => {
} }
} }
//
const layerSelectionSuccess = (typeList) => {
allHistoryList.value[currentIndex.value].forEach((item) => {
// type layerSelectionShow false
if (typeList.includes(item.type)) {
item.layerSelectionShow = false
} else {
item.layerSelectionShow = true
}
})
}
onMounted(() => { onMounted(() => {
getMapList() getMapList()
}) })
@ -1136,4 +1245,9 @@ onMounted(() => {
padding: 4px; padding: 4px;
outline: none; outline: none;
} }
.selection-area-btn {
width: 80px;
margin-left: 6px;
}
</style> </style>

View File

@ -1,180 +1,125 @@
<template> <template>
<div> <div
<!-- SVG 画布 --> @mousedown="startSelection"
<svg @mousemove="updateSelection"
ref="svg" @mouseup="endSelection"
width="500" style="position: relative; height: 100vh; border: 1px solid #ccc"
height="300"
@mousedown="handleMouseDown"
@mousemove="handleMouseMove"
@mouseup="handleMouseUp"
> >
<!-- 绘制所有曲线 --> <!-- 框选区域 -->
<path <div
v-for="(curve, index) in curves" v-if="isSelecting || selectionBox.width > 0"
:key="index" :style="{
:d="getCurvePath(curve)" position: 'absolute',
stroke="#000" left: `${selectionBox.x}px`,
fill="none" top: `${selectionBox.y}px`,
stroke-width="2" width: `${selectionBox.width}px`,
:class="{ selected: selectedCurve === curve }" height: `${selectionBox.height}px`,
@click="svgClick(curve)" border: '2px dashed #007bff',
/> backgroundColor: 'rgba(0, 123, 255, 0.1)'
<!-- 绘制控制点和连线 --> }"
<line ></div>
v-if="selectedCurve"
:x1="selectedCurve.start.x"
:y1="selectedCurve.start.y"
:x2="selectedCurve.control1.x"
:y2="selectedCurve.control1.y"
stroke="gray"
stroke-dasharray="2"
/>
<line
v-if="selectedCurve"
:x1="selectedCurve.end.x"
:y1="selectedCurve.end.y"
:x2="selectedCurve.control2.x"
:y2="selectedCurve.control2.y"
stroke="gray"
stroke-dasharray="2"
/>
<!-- 绘制起点终点和控制点 -->
<circle
v-for="(curve, index) in curves"
:key="'start' + index"
:cx="curve.start.x"
:cy="curve.start.y"
r="5"
fill="red"
@mousedown="startDrag($event, curve, 'start')"
/>
<circle
v-for="(curve, index) in curves"
:key="'end' + index"
:cx="curve.end.x"
:cy="curve.end.y"
r="5"
fill="red"
@mousedown="startDrag($event, curve, 'end')"
/>
<circle
v-if="selectedCurve"
:cx="selectedCurve.control1.x"
:cy="selectedCurve.control1.y"
r="5"
fill="green"
@mousedown="startDrag($event, selectedCurve, 'control1')"
/>
<circle
v-if="selectedCurve"
:cx="selectedCurve.control2.x"
:cy="selectedCurve.control2.y"
r="5"
fill="green"
@mousedown="startDrag($event, selectedCurve, 'control2')"
/>
</svg>
<!-- 显示控制点坐标 --> <!-- 图片 -->
<div class="points" v-if="selectedCurve"> <img
<p>起点: ({{ selectedCurve.start.x }}, {{ selectedCurve.start.y }})</p> v-for="(img, index) in images"
<p>控制点 1: ({{ selectedCurve.control1.x }}, {{ selectedCurve.control1.y }})</p> :key="index"
<p>控制点 2: ({{ selectedCurve.control2.x }}, {{ selectedCurve.control2.y }})</p> :src="img.src"
<p>终点: ({{ selectedCurve.end.x }}, {{ selectedCurve.end.y }})</p> :style="{
</div> position: 'absolute',
left: `${img.x}px`,
top: `${img.y}px`,
width: '100px',
height: '100px',
userSelect: isSelecting ? 'none' : 'auto', //
pointerEvents: isSelecting ? 'none' : 'auto' //
}"
/>
</div> </div>
</template> </template>
<script setup> <script>
import { ref } from 'vue' import { ref } from 'vue'
const svg = ref(null) // SVG
const curves = ref([]) // 线
const selectedCurve = ref(null) // 线
const isDragging = ref(false) //
const dragTarget = ref(null) //
// export default {
const getMousePos = (event) => { setup() {
const rect = svg.value.getBoundingClientRect() //
return { const images = ref([
x: event.clientX - rect.left, { src: 'https://via.placeholder.com/100', x: 100, y: 100 },
y: event.clientY - rect.top { src: 'https://via.placeholder.com/100', x: 300, y: 200 },
} { src: 'https://via.placeholder.com/100', x: 500, y: 300 }
])
//
const selectionBox = ref({
x: 0,
y: 0,
width: 0,
height: 0
})
//
const isSelecting = ref(false)
//
const startPos = ref({ x: 0, y: 0 })
//
const startSelection = (event) => {
isSelecting.value = true
startPos.value = { x: event.clientX, y: event.clientY }
selectionBox.value = {
x: event.clientX,
y: event.clientY,
width: 0,
height: 0
} }
// //
const startDrag = (event, curve, target) => {
event.preventDefault() event.preventDefault()
isDragging.value = true
selectedCurve.value = curve
dragTarget.value = target
} }
// //
const handleMouseDown = (event) => { const updateSelection = (event) => {
const mousePos = getMousePos(event) if (!isSelecting.value) return
// 线 const currentX = event.clientX
if (!isDragging.value && curves.value.length === 0) { const currentY = event.clientY
const newCurve = {
start: mousePos, selectionBox.value = {
end: mousePos, x: Math.min(startPos.value.x, currentX),
control1: { x: mousePos.x + 50, y: mousePos.y - 50 }, y: Math.min(startPos.value.y, currentY),
control2: { x: mousePos.x + 100, y: mousePos.y - 50 } width: Math.abs(currentX - startPos.value.x),
} height: Math.abs(currentY - startPos.value.y)
curves.value.push(newCurve)
selectedCurve.value = newCurve
dragTarget.value = 'end'
isDragging.value = true
}
} }
// //
const handleMouseMove = (event) => { event.preventDefault()
if (!isDragging.value || !selectedCurve.value) return
const mousePos = getMousePos(event)
//
if (dragTarget.value === 'start') {
selectedCurve.value.start = mousePos
} else if (dragTarget.value === 'end') {
selectedCurve.value.end = mousePos
} else if (dragTarget.value === 'control1') {
selectedCurve.value.control1 = mousePos
} else if (dragTarget.value === 'control2') {
selectedCurve.value.control2 = mousePos
}
} }
// //
const handleMouseUp = () => { const endSelection = () => {
isDragging.value = false isSelecting.value = false
dragTarget.value = null console.log('框选区域:', selectionBox.value)
} }
// 线 return {
const getCurvePath = (curve) => { images,
return `M ${curve.start.x} ${curve.start.y} C ${curve.control1.x} ${curve.control1.y}, ${curve.control2.x} ${curve.control2.y}, ${curve.end.x} ${curve.end.y}` selectionBox,
isSelecting,
startSelection,
updateSelection,
endSelection
}
} }
const svgClick = (item) => {
console.log(item)
} }
</script> </script>
<style scoped> <style scoped>
svg { /* 禁用用户选择 */
border: 1px solid #000; img {
cursor: crosshair; user-select: none;
} -webkit-user-select: none;
-moz-user-select: none;
path.selected { -ms-user-select: none;
stroke: blue;
}
.points {
margin-top: 10px;
font-size: 14px;
} }
</style> </style>

View File

@ -18,3 +18,74 @@
fontFamily: '' //字体类型 fontFamily: '' //字体类型
} }
``` ```
```
1.普通节点 dataJson 不传数据
2.库位点 dataJson 数据格式:
[
{
"id": 1881176241622024200, //库位id - 编辑的时候会有需要传过来 新增的时候没有则不传
"laneId": 1881177420829642800, //线库id - 编辑的时候有就传过来 新增的时候没有就不传
"laneName": "测试33", //线库名称 - 编辑的时候有就传过来 新增的时候没有就不传
"locationX": "2", //库位坐标x轴 - 编辑的时候有就传过来 新增的时候不用传
"locationY": "7", //库位坐标y轴 - 编辑的时候有就传过来 新增的时候不用传
"locationWide": 10, //宽度 - 编辑的时候有就传过来 新增的时候需要传
"locationDeep": 10, //高度 - 编辑的时候有就传过来 新增的时候需要传
"direction": 1, //库位方向1单向、2双向、3三向、4四向 编辑的时候有就传过来 新增的时候需要传
"inDirection": 1, //进入方向0尾入、1头入 编辑的时候有就传过来 新增的时候需要传
"outDirection": 1, //离开方向0尾出、1头出 编辑的时候有就传过来 新增的时候需要传
"locationStorey": 3, //层数 编辑的时候有就传过来 新增的时候需要传
"mapId": 1, //地图id 编辑的时候有就传过来 新增的时候不需要传
"mapItemId": 1881176241622024200 //地图子表id 编辑的时候有就传过来 新增的时候不需要传
},
{
"id": 1881176241622024200,
"laneId": 1881177420829642800,
"laneName": "测试33",
"locationX": "2",
"locationY": "7",
"locationWide": 10,
"locationDeep": 10,
"direction": 1,
"inDirection": 1,
"outDirection": 1,
"locationStorey": 3,
"mapId": 1,
"mapItemId": 1881176241622024200
}
]
3.设备点 dataJson 数据格式:
{
"id": 1881176241622024200, //设备id - 新增编辑都需要传 - 根据选择的设备获取到
"locationX": "2", //库位坐标x轴 - 编辑的时候有就传过来 新增的时候不用传
"locationY": "7", //库位坐标y轴 - 编辑的时候有就传过来 新增的时候不用传
"locationWide": 10, //宽度 - 编辑的时候有就传过来 新增的时候需要传
"locationDeep": 10, //高度 - 编辑的时候有就传过来 新增的时候需要传
"mapId": 1, //地图id 编辑的时候有就传过来 新增的时候不需要传
"mapItemId": 1881176241622024200 //地图子表id 编辑的时候有就传过来 新增的时候不需要传
}
4.停车点 dataJson 数据格式:
{
"id": 1881176241622024200, //停车点id - 编辑的时候会有需要传过来 新增的时候没有则不传
"locationX": "2", //库位坐标x轴 - 编辑的时候有就传过来 新增的时候不用传
"locationY": "7", //库位坐标y轴 - 编辑的时候有就传过来 新增的时候不用传
"locationWide": 10, //宽度 - 编辑的时候有就传过来 新增的时候需要传
"locationDeep": 10, //高度 - 编辑的时候有就传过来 新增的时候需要传
"direction": 1, //库位方向1单向、2双向、3三向、4四向 编辑的时候有就传过来 新增的时候需要传
"inDirection": 1, //进入方向0尾入、1头入 编辑的时候有就传过来 新增的时候需要传
"outDirection": 1, //离开方向0尾出、1头出 编辑的时候有就传过来 新增的时候需要传
"mapId": 1, //地图id 编辑的时候有就传过来 新增的时候不需要传
"mapItemId": 1881176241622024200 //地图子表id 编辑的时候有就传过来 新增的时候不需要传
},
5.路径点 dataJson 不传数据
6.等待点 dataJson 不传数据
```

View File

@ -0,0 +1,180 @@
<template>
<div>
<!-- SVG 画布 -->
<svg
ref="svg"
width="500"
height="300"
@mousedown="handleMouseDown"
@mousemove="handleMouseMove"
@mouseup="handleMouseUp"
>
<!-- 绘制所有曲线 -->
<path
v-for="(curve, index) in curves"
:key="index"
:d="getCurvePath(curve)"
stroke="#000"
fill="none"
stroke-width="2"
:class="{ selected: selectedCurve === curve }"
@click="svgClick(curve)"
/>
<!-- 绘制控制点和连线 -->
<line
v-if="selectedCurve"
:x1="selectedCurve.start.x"
:y1="selectedCurve.start.y"
:x2="selectedCurve.control1.x"
:y2="selectedCurve.control1.y"
stroke="gray"
stroke-dasharray="2"
/>
<line
v-if="selectedCurve"
:x1="selectedCurve.end.x"
:y1="selectedCurve.end.y"
:x2="selectedCurve.control2.x"
:y2="selectedCurve.control2.y"
stroke="gray"
stroke-dasharray="2"
/>
<!-- 绘制起点终点和控制点 -->
<circle
v-for="(curve, index) in curves"
:key="'start' + index"
:cx="curve.start.x"
:cy="curve.start.y"
r="5"
fill="red"
@mousedown="startDrag($event, curve, 'start')"
/>
<circle
v-for="(curve, index) in curves"
:key="'end' + index"
:cx="curve.end.x"
:cy="curve.end.y"
r="5"
fill="red"
@mousedown="startDrag($event, curve, 'end')"
/>
<circle
v-if="selectedCurve"
:cx="selectedCurve.control1.x"
:cy="selectedCurve.control1.y"
r="5"
fill="green"
@mousedown="startDrag($event, selectedCurve, 'control1')"
/>
<circle
v-if="selectedCurve"
:cx="selectedCurve.control2.x"
:cy="selectedCurve.control2.y"
r="5"
fill="green"
@mousedown="startDrag($event, selectedCurve, 'control2')"
/>
</svg>
<!-- 显示控制点坐标 -->
<div class="points" v-if="selectedCurve">
<p>起点: ({{ selectedCurve.start.x }}, {{ selectedCurve.start.y }})</p>
<p>控制点 1: ({{ selectedCurve.control1.x }}, {{ selectedCurve.control1.y }})</p>
<p>控制点 2: ({{ selectedCurve.control2.x }}, {{ selectedCurve.control2.y }})</p>
<p>终点: ({{ selectedCurve.end.x }}, {{ selectedCurve.end.y }})</p>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const svg = ref(null) // SVG
const curves = ref([]) // 线
const selectedCurve = ref(null) // 线
const isDragging = ref(false) //
const dragTarget = ref(null) //
//
const getMousePos = (event) => {
const rect = svg.value.getBoundingClientRect()
return {
x: event.clientX - rect.left,
y: event.clientY - rect.top
}
}
//
const startDrag = (event, curve, target) => {
event.preventDefault()
isDragging.value = true
selectedCurve.value = curve
dragTarget.value = target
}
//
const handleMouseDown = (event) => {
const mousePos = getMousePos(event)
// 线
if (!isDragging.value && curves.value.length === 0) {
const newCurve = {
start: mousePos,
end: mousePos,
control1: { x: mousePos.x + 50, y: mousePos.y - 50 },
control2: { x: mousePos.x + 100, y: mousePos.y - 50 }
}
curves.value.push(newCurve)
selectedCurve.value = newCurve
dragTarget.value = 'end'
isDragging.value = true
}
}
//
const handleMouseMove = (event) => {
if (!isDragging.value || !selectedCurve.value) return
const mousePos = getMousePos(event)
//
if (dragTarget.value === 'start') {
selectedCurve.value.start = mousePos
} else if (dragTarget.value === 'end') {
selectedCurve.value.end = mousePos
} else if (dragTarget.value === 'control1') {
selectedCurve.value.control1 = mousePos
} else if (dragTarget.value === 'control2') {
selectedCurve.value.control2 = mousePos
}
}
//
const handleMouseUp = () => {
isDragging.value = false
dragTarget.value = null
}
// 线
const getCurvePath = (curve) => {
return `M ${curve.start.x} ${curve.start.y} C ${curve.control1.x} ${curve.control1.y}, ${curve.control2.x} ${curve.control2.y}, ${curve.end.x} ${curve.end.y}`
}
const svgClick = (item) => {
console.log(item)
}
</script>
<style scoped>
svg {
border: 1px solid #000;
cursor: crosshair;
}
path.selected {
stroke: blue;
}
.points {
margin-top: 10px;
font-size: 14px;
}
</style>