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

444 lines
13 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">
<div
class="location-selection-map"
:style="{
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="230"
>
<template #reference>
<img
src="https://api.znkjfw.com/admin-api/infra/file/4/get/库位库存_png_179_1739326653035.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)"
>
<div>层数: 第{{ floorIndex + 1 }}层</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="https://api.znkjfw.com/admin-api/infra/file/4/get/库位库存_png_179_1739326653035.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 open = (locationTypeNum) => {
dialogFormVisible.value = true
currentItem.value = null
locationTypeNumber.value = locationTypeNum
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)
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
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 (item.id === 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('该库位已被禁用')
} else if (item.locationLock === 0) {
message.warning('该库位已被锁定')
} else if (item.locationUseStatus === 1) {
message.warning('该库位已被占用')
} else {
currentItem.value = item
}
}
const submitAddForm = () => {
if (currentItem.value) {
emit('locationSelectionDialogSuccess', currentItem.value)
dialogFormVisible.value = false
} else {
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;
.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: 230px;
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 20px;
}
}
.tool-active {
background: #ebf1ff !important;
}
</style>