zn-admin-vue3-wcs/src/views/mapPage/components/locationSelectionDialog.vue

512 lines
15 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<el-dialog
v-model="dialogFormVisible"
:show-close="false"
width="1200"
class="location-selection-dialog"
top="4vh"
>
<template #header="{ close, titleId }">
<div class="my-header">
<div class="left-header">
<div :id="titleId" class="ml-4">位置选择</div>
<div class="ml-4">
<el-cascader
v-model="cascaderValue"
:options="cascaderList"
@change="handleChange"
class="!w-140px"
/>
</div>
</div>
<el-icon @click="close" class="mr-4"><Close /></el-icon>
</div>
</template>
<div class="location-selection-dialog-map" @wheel="handleWheel">
<div
class="location-selection-map"
:style="{
transform: `scale(${imageChangeMultiple})`,
transformOrigin: '0 0',
width: imgBgObj.showWidth + 'px',
height: imgBgObj.showHeight + 'px',
backgroundImage: `url(${imgBgObj.imgUrl})`
}"
>
<div
v-for="(item, index) in allMapPointInfo"
:key="index"
class="location-selection-map-item"
>
<template v-if="locationTypeNumber == 1">
<el-popover
placement="top"
trigger="click"
:popper-style="{ padding: '0px' }"
@before-enter="getByMapItemId(item, index)"
width="300"
>
<template #reference>
<img
src="@/assets/imgs/indexPage/bin-location.png"
alt=""
:style="{
position: 'absolute',
left: Number(item.locationX) - Number(item.locationWidePx) / 2 + 'px',
top: Number(item.locationY) - Number(item.locationDeepPx) / 2 + 'px',
width: item.locationWidePx + 'px',
height: item.locationDeepPx + 'px',
zIndex: 999
}"
/>
</template>
<div class="current-item-list">
<!-- 楼层展开 -->
<div
v-for="(floor, floorIndex) in currentMapItemList"
:key="floorIndex"
class="current-item"
:class="currentItem && currentItem.id == floor.id ? 'tool-active' : ''"
@click="chooseLocationPoint(floor)"
:style="{
background:
floor.locationEnable === 0 ||
floor.locationLock === 0 ||
(floor.locationUseStatus === 1 && locationTypeStr === 'release') ||
(floor.locationUseStatus === 0 && locationTypeStr === 'take')
? '#FFE2E2'
: '#F6FFEF'
}"
>
<div>层数: 第{{ floor.locationStorey }}层</div>
<div class="mt-4px">库位号: {{ floor.locationNo }}</div>
<div class="mt-4px"
>库位状态: {{ floor.locationEnable === 1 ? '启用' : '禁用' }} /
{{ floor.locationLock === 1 ? '正常' : '锁定' }} /
{{ floor.locationUseStatus === 1 ? '占用' : '空闲' }}</div
>
</div>
</div>
</el-popover>
</template>
<template v-else>
<el-popover placement="top" trigger="click" :popper-style="{ padding: '0px' }">
<template #reference>
<img
@click="choosePoint(item)"
src="@/assets/imgs/indexPage/bin-location.png"
alt=""
:style="{
position: 'absolute',
left: Number(item.locationX) - Number(item.locationWidePx) / 2 + 'px',
top: Number(item.locationY) - Number(item.locationDeepPx) / 2 + 'px',
width: item.locationWidePx + 'px',
height: item.locationDeepPx + 'px',
zIndex: 999,
border:
(currentItem &&
locationTypeNumber == 2 &&
currentItem.laneId == item.laneId) ||
(locationTypeNumber == 3 && currentItem?.areaId == item?.areaId)
? '1px dashed #000'
: 'none'
}"
/>
</template>
<div class="drop-down-menu">
<div class="drop-down-menu" v-if="locationTypeNumber == 2">
<div class="drop-down-menu-item"> {{ item.laneName }} </div>
</div>
<div class="drop-down-menu" v-if="locationTypeNumber == 3">
<div class="drop-down-menu-item">{{ item.areaName }}</div>
</div>
</div>
</el-popover>
</template>
</div>
</div>
</div>
<div class="location-selection-dialog-footer">
<el-button style="width: 90px" @click="dialogFormVisible = false">取消</el-button>
<el-button style="width: 90px" type="primary" @click="submitAddForm"> 确定 </el-button>
</div>
</el-dialog>
</template>
<script setup>
import JSONBigInt from 'json-bigint'
import { reactive, ref } from 'vue'
import * as MapApi from '@/api/map/map'
const dialogFormVisible = ref(false) //列表的
const message = useMessage() // 消息弹窗
const props = defineProps({
positionMapId: {
type: String,
default: () => ''
}
})
const locationTypeNumber = ref(1)
const locationTypeStr = ref() //release放货 take取货
const open = (locationTypeNum, locationTyp) => {
dialogFormVisible.value = true
currentItem.value = null
locationTypeNumber.value = locationTypeNum
locationTypeStr.value = locationTyp
imgBgObj.positionMapId = props.positionMapId
getMapList()
}
const allMapPointInfo = ref([]) //点位信息
const imgBgObj = reactive({
imgUrl: '',
positionMapId: '',
width: '',
height: '',
floor: '',
area: '',
resolution: 0,
origin: null,
showWidth: 1182,
showHeight: 0
})
const cascaderList = ref()
const cascaderValue = ref([])
const getMapList = async () => {
// 先获取所有的地图
let res = await MapApi.getPositionMapGetMap()
cascaderList.value = Object.keys(res).map((floor) => {
return {
value: parseInt(floor),
label: `${floor}楼`,
children: res[floor].map((item) => {
return {
value: item.id, //mapId
label: item.area
}
})
}
})
//如果传了地图id
if (props.positionMapId) {
let item = findDataById(res, props.positionMapId)
if (item) {
cascaderValue.value = [item.floor, item.id]
}
} else {
cascaderValue.value = [cascaderList.value[0].value, cascaderList.value[0].children[0].value]
}
getPositionMap()
}
const getPositionMap = async () => {
let res = await MapApi.getPositionMap(cascaderValue.value[1])
let yamlJson = JSON.parse(res.yamlJson)
imgBgObj.positionMapId = res.id
imgBgObj.floor = res.floor
imgBgObj.area = res.area
imgBgObj.width = yamlJson.width
imgBgObj.height = yamlJson.height
imgBgObj.origin = yamlJson.origin
imgBgObj.resolution = yamlJson.resolution
if (imgBgObj.width > 1477) {
imgBgObj.showWidth = imgBgObj.width * 0.8
} else {
imgBgObj.showWidth = 1182
}
imgBgObj.showHeight = (imgBgObj.showWidth * imgBgObj.height) / imgBgObj.width
await getMapData()
await getAllNodeList()
}
const findDataById = (obj, id) => {
// 遍历 obj 的每一层
for (const floor in obj) {
// 遍历当前楼层的每个区域
for (const item of obj[floor]) {
// 如果找到匹配的 id返回该数据
if (String(item.id) === String(id)) {
return item
}
}
}
// 如果没有找到,返回 null
return null
}
const handleChange = (list) => {
getPositionMap()
}
//调转换成png的接口
const getMapData = async () => {
let data = await MapApi.getPositionMapdDwnloadPngBase64({
floor: imgBgObj.floor,
area: imgBgObj.area
})
imgBgObj.imgUrl = data
}
const currentItem = ref(null)
const getAllNodeList = async () => {
let list = await MapApi.getPositionMapItemList({
positionMapId: imgBgObj.positionMapId
})
allMapPointInfo.value = []
list.forEach((item) => {
//只要库位
if (locationTypeNumber.value == 1 && item.type === 2) {
item.locationX = Number(item.locationX) * (imgBgObj.showWidth / imgBgObj.width)
item.locationY = Number(item.locationY) * (imgBgObj.showWidth / imgBgObj.width)
item.dataList = JSONBigInt({ storeAsString: true }).parse(item.dataJson)
item.locationDeep = item.dataList[0].locationDeep
item.locationWide = item.dataList[0].locationWide
//要将实际的cm改成px
if (item.locationWide && item.locationDeep) {
let pxObj = cmConversionPx(item.locationWide, item.locationDeep)
item.locationWidePx = pxObj.pWidth
item.locationDeepPx = pxObj.pHeight
}
allMapPointInfo.value.push(item)
}
//线库 laneId
if (item.type === 2 && locationTypeNumber.value == 2 && item.laneId !== null) {
item.locationX = Number(item.locationX) * (imgBgObj.showWidth / imgBgObj.width)
item.locationY = Number(item.locationY) * (imgBgObj.showWidth / imgBgObj.width)
item.dataList = JSONBigInt({ storeAsString: true }).parse(item.dataJson)
item.locationDeep = item.dataList[0].locationDeep
item.locationWide = item.dataList[0].locationWide
item.laneName = item.dataList[0].laneName
//要将实际的cm改成px
if (item.locationWide && item.locationDeep) {
let pxObj = cmConversionPx(item.locationWide, item.locationDeep)
item.locationWidePx = pxObj.pWidth
item.locationDeepPx = pxObj.pHeight
}
allMapPointInfo.value.push(item)
}
//区域 areaId
if (item.type === 2 && locationTypeNumber.value == 3 && item?.areaId !== null) {
item.locationX = Number(item.locationX) * (imgBgObj.showWidth / imgBgObj.width)
item.locationY = Number(item.locationY) * (imgBgObj.showWidth / imgBgObj.width)
item.dataList = JSONBigInt({ storeAsString: true }).parse(item.dataJson)
item.locationDeep = item.dataList[0].locationDeep
item.locationWide = item.dataList[0].locationWide
item.areaName = item.dataList[0].areaName
//要将实际的cm改成px
if (item.locationWide && item.locationDeep) {
let pxObj = cmConversionPx(item.locationWide, item.locationDeep)
item.locationWidePx = pxObj.pWidth
item.locationDeepPx = pxObj.pHeight
}
allMapPointInfo.value.push(item)
}
if (item.dataList && item.dataList.length > 0) {
item.dataList = item.dataList.reverse()
}
})
}
//库位
const currentMapItemList = ref([])
const getByMapItemId = async (item, index) => {
let list = await MapApi.getByMapItemId(imgBgObj.positionMapId, item.id)
currentMapItemList.value = list.reverse()
}
//将节点实际宽高cm转换成px
const cmConversionPx = (cWidth, cHeight) => {
let pWidth = (Number(cWidth) / imgBgObj.resolution / 100) * (imgBgObj.showWidth / imgBgObj.width)
let pHeight =
(Number(cHeight) / imgBgObj.resolution / 100) * (imgBgObj.showWidth / imgBgObj.width)
return {
pWidth,
pHeight
}
}
const choosePoint = (item) => {
currentItem.value = item
}
const chooseLocationPoint = (item) => {
if (item.locationEnable === 0) {
message.warning('该库位已被禁用')
return
}
if (item.locationLock === 0) {
message.warning('该库位已被锁定')
return
}
// locationUseStatus 1占用 0空闲
if (item.locationUseStatus === 1 && locationTypeStr.value === 'release') {
message.warning('该库位已被占用')
return
}
if (item.locationUseStatus === 0 && locationTypeStr.value === 'take') {
message.warning('该库位没有货物')
return
}
currentItem.value = item
}
const submitAddForm = () => {
if (currentItem.value) {
emit('locationSelectionDialogSuccess', currentItem.value)
dialogFormVisible.value = false
} else {
message.warning('请选择一个位置')
}
}
const imageChangeMultiple = ref(1)
//鼠标滚轮
const handleWheel = (event) => {
// 判断 Ctrl 键是否被按下
if (event.ctrlKey) {
// 阻止默认的滚动行为
event.preventDefault()
// 根据滚轮滚动方向调整缩放比例
if (event.deltaY < 0) {
// 向上滚动,放大
//放大
if (imageChangeMultiple.value < 4) {
imageChangeMultiple.value += 0.2
} else {
imageChangeMultiple.value = 3.8
message.warning('不能在放大了')
}
} else {
//缩小
if (imageChangeMultiple.value > 0.2) {
imageChangeMultiple.value -= 0.1
} else {
imageChangeMultiple.value = 0.1
message.warning('不能在缩小了')
}
}
}
}
const emit = defineEmits(['locationSelectionDialogSuccess'])
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
</script>
<style lang="scss">
.location-selection-dialog {
padding: 0px;
position: relative;
.el-dialog__header {
padding-bottom: 0 !important;
}
.my-header {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
height: 58px;
padding: 0;
margin-right: 0 !important;
border-bottom: 1px solid var(--el-border-color);
.left-header {
display: flex;
align-items: center;
}
}
.location-selection-dialog-map {
position: relative;
width: 100%;
height: 80vh;
overflow: auto;
.location-selection-map {
position: relative;
width: 100%;
background-repeat: no-repeat;
background-size: contain;
position: absolute;
top: 0;
left: 0;
.location-selection-map-item {
position: absolute;
top: 0;
left: 0;
}
}
}
.location-selection-dialog-footer {
position: absolute;
bottom: 30px;
right: 60px;
}
}
.drop-down-menu {
.drop-down-menu-item {
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
padding: 11px 10px;
font-family:
PingFangSC,
PingFang SC;
font-weight: 400;
font-size: 14px;
color: #0d162a;
line-height: 20px;
text-align: left;
font-style: normal;
border-bottom: 1px solid #e9e9e9;
}
:last-child {
border-bottom: none;
}
}
.current-item-list {
.current-item {
width: 300px;
font-family:
PingFangSC,
PingFang SC;
font-weight: 500;
font-size: 14px;
color: #606266;
cursor: pointer;
border: 1px solid rgba(0, 0, 0, 0);
position: relative;
border-bottom: 1px solid #e9e9e9;
padding: 12px 15px;
}
}
.tool-active {
background: #ebf1ff !important;
}
</style>