<template>
|
<div class="container">
|
<div class="header">
|
<h2 class="title">货位排图</h2>
|
</div>
|
|
<div class="content-wrapper">
|
<!-- 控制面板区域 -->
|
<div class="control-panel">
|
<!-- <div class="form-group">
|
<label>区域:</label>
|
<el-select size="mini" filterable v-model="Area.areaCode" placeholder="请选择" class="full-width">
|
<el-option v-for="item in slectData" :value="item.areaCode" :label="item.areaName"
|
:key="item.areaName"></el-option>
|
</el-select>
|
</div> -->
|
|
<!-- <div class="form-group">
|
<label>排:</label>
|
<el-select
|
size="mini"
|
clearable
|
filterable
|
@change="SCChange"
|
v-model="Area.tunnel"
|
placeholder="请选择"
|
class="full-width"
|
>
|
<el-option
|
v-for="item in scList"
|
:value="item"
|
:label="item"
|
:key="item"
|
></el-option>
|
</el-select>
|
</div> -->
|
|
<el-button type="success" class="refresh-btn" @click="GetViewData"> 刷新 </el-button>
|
|
<div class="legend-section">
|
<h4>说明</h4>
|
<div class="legend-grid">
|
<div class="legend-item" v-for="item in infoMsg" :key="item.bgcolor">
|
<span class="color-box" :style="{ 'background-color': item.bgcolor }"></span>
|
<span class="legend-label">{{ item.msg }}</span>
|
</div>
|
</div>
|
</div>
|
</div>
|
|
<!-- 货位展示区域 -->
|
<div class="location-view">
|
<div class="layer-container" v-for="layer in locationData" :key="layer.index">
|
<h3 class="layer-title">第{{ layer.index }}层</h3>
|
<div class="row" v-for="row in layer.rows" :key="row.index">
|
<div class="location-cell" :style="{ 'background-color': GetBgColor(col) }" v-for="col in row.cols"
|
:key="col.index" @mouseenter="showTooltip(col, $event)" @mouseleave="hideTooltip">
|
{{ row.index }}-{{ layer.index }}-{{ col.index }}
|
</div>
|
</div>
|
</div>
|
</div>
|
<!-- 悬浮提示框 -->
|
<transition name="tooltip-fade">
|
<div v-if="showTooltipFlag" class="location-tooltip" :style="{
|
left: tooltipPosition.x + 'px',
|
top: tooltipPosition.y + 'px'
|
}">
|
<div v-if="currentLocation" class="tooltip-content">
|
<div class="tooltip-header">
|
<h4>{{ currentLocation.locationCode }}</h4>
|
<span class="status-badge" :class="getStatusClass(currentLocation)">
|
{{ getStatusText(currentLocation) }}
|
</span>
|
</div>
|
<div class="tooltip-body">
|
<div class="info-section">
|
<h5>基本信息</h5>
|
<div class="info-row">
|
<span class="label">货位号:</span>
|
<span class="value">{{ currentLocation.locationCode }}</span>
|
</div>
|
<div class="info-row">
|
<span class="label">状态:</span>
|
<span class="value status-highlight" :class="getStatusClass(currentLocation)">
|
{{ getStatusText(currentLocation) }}
|
</span>
|
</div>
|
<div class="info-row">
|
<span class="label">禁用:</span>
|
<span class="value" :class="{ 'disabled': currentLocation.location_lock == 2 }">
|
{{ currentLocation.location_lock == 2 ? '是' : '否' }}
|
</span>
|
</div>
|
</div>
|
<div v-if="currentLocation.stockInfo != null" class="info-section">
|
<h5>库存信息</h5>
|
<div class="info-row">
|
<span class="label">PVI:</span>
|
<span class="value">{{ currentLocation.stockInfo.pvi }}</span>
|
</div>
|
<div class="info-row">
|
<span class="label">滑橇号:</span>
|
<span class="value">{{ currentLocation.stockInfo.palletCode }}</span>
|
</div>
|
<div class="info-row">
|
<span class="label">巷道号:</span>
|
<span class="value">{{ currentLocation.stockInfo.roadwayNo }}</span>
|
</div>
|
<div class="info-row">
|
<span class="label">车型:</span>
|
<span class="value">{{ currentLocation.stockInfo.vehicleCharacteristic }}</span>
|
</div>
|
<div class="info-row">
|
<span class="label">工单类型:</span>
|
<span class="value">{{ currentLocation.stockInfo.workOrderType }}</span>
|
</div>
|
<div class="info-row">
|
<span class="label">车身颜色:</span>
|
<span class="value">{{ currentLocation.stockInfo.carBodyCharacteristic }}</span>
|
</div>
|
<div class="info-row">
|
<span class="label">车身类型:</span>
|
<span class="value">{{ getcarType(currentLocation.stockInfo.carType) }}</span>
|
</div>
|
<div class="info-row">
|
<span class="label">彩车身物料号:</span>
|
<span class="value">{{ currentLocation.stockInfo.pbMaterial }}</span>
|
</div>
|
<div class="info-row">
|
<span class="label">白车身物料号:</span>
|
<span class="value">{{ currentLocation.stockInfo.biwMaterialCode }}</span>
|
</div>
|
<div class="info-row">
|
<span class="label">焊装上线时间:</span>
|
<span class="value">{{ currentLocation.stockInfo.biwInPassTime }}</span>
|
</div>
|
<div class="info-row">
|
<span class="label">天窗特征:</span>
|
<span class="value">{{ currentLocation.stockInfo.skylightCharacteristic }}</span>
|
</div>
|
<div class="info-row">
|
<span class="label">拉动锁定:</span>
|
<span class="value">{{ getlockOrder(currentLocation.stockInfo.lockOrder) }}</span>
|
</div>
|
<div class="info-row">
|
<span class="label">锁定状态:</span>
|
<span class="value">{{ getstockStatus(currentLocation.stockInfo.stockStatus) }}</span>
|
</div>
|
<div class="info-row">
|
<span class="label">任务状态:</span>
|
<span class="value">{{ gettaskStatus(currentLocation.stockInfo.taskStatus) }}</span>
|
</div>
|
</div>
|
</div>
|
</div>
|
</div>
|
</transition>
|
</div>
|
</div>
|
</template>
|
|
<script>
|
import { ElButton } from 'element-plus'
|
|
export default {
|
data() {
|
return {
|
slectData: [],
|
scList: [],
|
Area: { areaName: '', areaCode: '' },
|
mian_height: '',
|
infoMsg: [
|
{ bgcolor: 'lightgreen', msg: '空货位', state: 0 },
|
{ bgcolor: 'orange', msg: '有货', state: 2 },
|
{ bgcolor: '#2BB3D5', msg: '锁定', state: 1 },
|
{ bgcolor: '#ccc', msg: '禁用', state: 9 },
|
{ bgcolor: 'blue', msg: '出库', state: '3' },
|
{ bgcolor: 'pink', msg: '入库', state: '4' }
|
],
|
locationData: [],
|
showTooltipFlag: false,
|
currentLocation: null,
|
tooltipPosition: { x: 20, y: 20 }
|
}
|
},
|
computed: {
|
GetBgColor(col) {
|
return (col) => {
|
var bgColor = ''
|
//优先显示禁用状态
|
if (col.location_lock == 2) {
|
this.infoMsg.forEach((el) => {
|
if (el.state == col.location_lock) {
|
bgColor = '#ccc'
|
}
|
})
|
} else {
|
this.infoMsg.forEach((el) => {
|
if (el.state == col.location_state) {
|
bgColor = el.bgcolor
|
}
|
})
|
}
|
return bgColor
|
}
|
}
|
},
|
watch: {
|
//切换库区
|
'Area.areaCode'(newValue, oldValue) {
|
this.scList = []
|
this.slectData.forEach((e) => {
|
if (e.areaCode == newValue) {
|
this.Area.areaCode = e.areaCode[0]
|
//this.scList = e.tunnel;
|
}
|
})
|
this.GetViewData()
|
}
|
},
|
methods: {
|
GetViewData() {
|
var _this = this
|
this.http.post('/api/LocationInfo/GetLocationStatu', _this.Area, '查询中').then((x) => {
|
_this.locationData = x
|
console.log('后端返回:', x)
|
})
|
},
|
// 切换排
|
SCChange() {
|
this.GetViewData()
|
},
|
showTooltip(location, event) {
|
this.currentLocation = location
|
this.showTooltipFlag = true
|
|
// 获取提示框的预估宽度
|
const tooltipWidth = 300
|
const tooltipHeight = 400
|
const offsetX = 15
|
const offsetY = 15
|
|
// 计算提示框位置,优先显示在鼠标上方
|
let x = event.clientX + offsetX
|
let y = event.clientY - tooltipHeight - offsetY
|
|
// 如果上方空间不足,则显示在下方
|
if (y < 10) {
|
y = event.clientY + offsetY
|
}
|
|
// 检查右侧边界
|
if (x + tooltipWidth > window.innerWidth) {
|
x = event.clientX - tooltipWidth - offsetX
|
}
|
|
// 确保不会超出左边界和上边界
|
x = Math.max(10, x)
|
y = Math.max(10, y)
|
|
this.tooltipPosition = { x, y }
|
},
|
|
hideTooltip() {
|
this.showTooltipFlag = false
|
this.currentLocation = null
|
},
|
|
getStatusText(location) {
|
// if (location.location_lock === 3) return "禁用";
|
if (location.location_state === '0') return '空货位'
|
if (location.location_state === '1') return '锁定'
|
if (location.location_state === '3') return '有货锁定'
|
if (location.location_state === '4') return '空闲锁定'
|
if (location.location_state === '2') return '有货'
|
// if (location.location_state > 0 && location.location_state < 100)
|
// return "锁定";
|
return '其他'
|
},
|
getStatusClass(location) {
|
if (location.location_state === '0') return 'status-empty'
|
if (location.location_state === '1') return 'status-locked'
|
if (location.location_state === '3') return 'status-locked-with-stock'
|
if (location.location_state === '4') return 'status-locked-empty'
|
if (location.location_state === '2') return 'status-with-stock'
|
return 'status-other'
|
},
|
getcarType(carType) {
|
if (carType == 1) return '白车身'
|
if (carType == 2) return '彩车身'
|
if (carType == 3) return '空滑橇'
|
},
|
getlockOrder(lockOrder) {
|
if (lockOrder == 1) return '已锁车'
|
if (lockOrder == 0) return '未拉动'
|
},
|
getstockStatus(stockStatus) {
|
if (stockStatus == 1) return '已锁定'
|
if (stockStatus == 0) return '未锁定'
|
},
|
gettaskStatus(taskStatus) {
|
if (taskStatus == 1) return '任务中'
|
if (taskStatus == 0) return '无任务'
|
}
|
},
|
mounted() {
|
var mainHeight = document.getElementById('vol-main')
|
this.mian_height = mainHeight.offsetHeight - 40 + 'px'
|
var _this = this
|
//加载下拉选项
|
this.http.get('/api/dt_AreaInfo/GetAreaInfo', {}, '查询中').then((x) => {
|
console.log(x)
|
_this.slectData = x
|
//加载第一个区域,第一排
|
_this.Area.areaCode = _this.slectData[0].areaCode
|
//_this.scList = _this.slectData[0].tunnel;
|
})
|
},
|
components: { ElButton }
|
}
|
</script>
|
|
<style scoped>
|
.container {
|
display: flex;
|
flex-direction: column;
|
height: 100%;
|
padding: 10px;
|
}
|
|
.header {
|
text-align: center;
|
margin-bottom: 20px;
|
}
|
|
.title {
|
font-size: 20px;
|
font-weight: bold;
|
margin: 0;
|
}
|
|
.content-wrapper {
|
display: flex;
|
flex: 1;
|
min-height: 0;
|
}
|
|
.control-panel {
|
width: 220px;
|
padding: 15px;
|
background-color: #f5f7fa;
|
border-radius: 4px;
|
margin-right: 15px;
|
display: flex;
|
flex-direction: column;
|
}
|
|
.form-group {
|
margin-bottom: 15px;
|
}
|
|
.full-width {
|
width: 100%;
|
}
|
|
.refresh-btn {
|
margin-top: 10px;
|
width: 100%;
|
}
|
|
.legend-section {
|
margin-top: 30px;
|
}
|
|
.legend-section h4 {
|
margin-bottom: 10px;
|
}
|
|
.legend-grid {
|
display: grid;
|
grid-template-columns: 1fr;
|
gap: 8px;
|
}
|
|
.legend-item {
|
display: flex;
|
align-items: center;
|
}
|
|
.color-box {
|
display: inline-block;
|
width: 20px;
|
height: 20px;
|
margin-right: 8px;
|
border-radius: 3px;
|
}
|
|
.legend-label {
|
font-size: 13px;
|
}
|
|
.location-view {
|
flex: 1;
|
overflow: auto;
|
padding: 10px;
|
background-color: white;
|
border-radius: 4px;
|
}
|
|
.layer-container {
|
margin-bottom: 25px;
|
}
|
|
.layer-title {
|
margin: 0 0 10px 0;
|
font-size: 16px;
|
color: #333;
|
}
|
|
.row {
|
display: flex;
|
flex-wrap: wrap;
|
margin-bottom: 8px;
|
}
|
|
.location-cell {
|
width: 66px;
|
height: 38px;
|
margin: 3px;
|
text-align: center;
|
font-size: 14px;
|
border-radius: 3px;
|
line-height: 38px;
|
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
}
|
|
.location-tooltip {
|
position: fixed;
|
z-index: 9999;
|
background-color: white;
|
border: 1px solid #e4e7ed;
|
border-radius: 8px;
|
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
|
pointer-events: none;
|
max-width: 320px;
|
min-width: 280px;
|
overflow: hidden;
|
}
|
|
.tooltip-content {
|
display: flex;
|
flex-direction: column;
|
}
|
|
.tooltip-header {
|
padding: 12px 16px;
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
color: white;
|
display: flex;
|
justify-content: space-between;
|
align-items: center;
|
}
|
|
.tooltip-header h4 {
|
margin: 0;
|
font-size: 15px;
|
font-weight: 600;
|
}
|
|
.status-badge {
|
padding: 4px 10px;
|
border-radius: 12px;
|
font-size: 12px;
|
font-weight: 500;
|
background-color: rgba(255, 255, 255, 0.2);
|
backdrop-filter: blur(4px);
|
}
|
|
.status-empty {
|
background-color: rgba(144, 238, 144, 0.9);
|
}
|
|
.status-locked {
|
background-color: rgba(43, 179, 213, 0.9);
|
}
|
|
.status-locked-with-stock {
|
background-color: rgba(255, 165, 0, 0.9);
|
}
|
|
.status-locked-empty {
|
background-color: rgba(255, 182, 193, 0.9);
|
}
|
|
.status-with-stock {
|
background-color: rgba(255, 99, 71, 0.9);
|
}
|
|
.status-other {
|
background-color: rgba(153, 153, 153, 0.9);
|
}
|
|
.tooltip-body {
|
padding: 16px;
|
}
|
|
.info-section {
|
margin-bottom: 16px;
|
}
|
|
.info-section:last-child {
|
margin-bottom: 0;
|
}
|
|
.info-section h5 {
|
margin: 0 0 10px 0;
|
font-size: 13px;
|
color: #909399;
|
text-transform: uppercase;
|
letter-spacing: 0.5px;
|
padding-bottom: 6px;
|
border-bottom: 1px solid #ebeef5;
|
}
|
|
.info-row {
|
display: flex;
|
margin-bottom: 8px;
|
align-items: center;
|
}
|
|
.info-row:last-child {
|
margin-bottom: 0;
|
}
|
|
.info-row .label {
|
flex-shrink: 0;
|
width: 100px;
|
color: #606266;
|
font-size: 13px;
|
}
|
|
.info-row .value {
|
flex: 1;
|
color: #303133;
|
font-size: 13px;
|
word-break: break-all;
|
}
|
|
.info-row .value.disabled {
|
color: #f56c6c;
|
font-weight: 500;
|
background-color: #fef0f0;
|
padding: 2px 8px;
|
border-radius: 4px;
|
}
|
|
.info-row .value.status-highlight {
|
font-weight: 600;
|
padding: 2px 8px;
|
border-radius: 4px;
|
}
|
|
.status-highlight.status-empty {
|
color: #67c23a;
|
background-color: #f0f9ff;
|
}
|
|
.status-highlight.status-locked {
|
color: #409eff;
|
background-color: #ecf5ff;
|
}
|
|
.status-highlight.status-locked-with-stock {
|
color: #e6a23c;
|
background-color: #fdf6ec;
|
}
|
|
.status-highlight.status-locked-empty {
|
color: #f56c6c;
|
background-color: #fef0f0;
|
}
|
|
.status-highlight.status-with-stock {
|
color: #67c23a;
|
background-color: #f0f9ff;
|
}
|
|
.status-highlight.status-other {
|
color: #909399;
|
background-color: #f4f4f5;
|
}
|
|
/* 添加过渡动画 */
|
.tooltip-fade-enter-active,
|
.tooltip-fade-leave-active {
|
transition: opacity 0.2s, transform 0.2s;
|
}
|
|
.tooltip-fade-enter-from,
|
.tooltip-fade-leave-to {
|
opacity: 0;
|
transform: translateY(5px);
|
}
|
</style>
|