Files
klp-oa/klp-ui/src/views/wms/warehouse/components/WarehouseBird.vue
砂糖 f1637501b2 fix(wms): 修复复合架拆分按钮显示逻辑及更新库位编码注释
修复复合架拆分按钮仅在支持拆分的列显示的问题
更新库位编码解析注释以反映最新格式要求
2025-12-30 10:36:44 +08:00

333 lines
8.1 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>
<div class="bird-container">
<!-- 统计信息卡片 -->
<div class="statistics-card">
<el-card shadow="hover">
<div class="statistics-item">
<span class="label">总库位数</span>
<span class="value">{{ statistics.total }}</span>
</div>
<div class="statistics-item">
<span class="label">总列数</span>
<span class="value">{{ statistics.columnCount }}</span>
</div>
<div class="statistics-item">
<span class="label">各列库位分布</span>
<span class="value">
<span v-for="(count, column) in statistics.columnDetail" :key="column">
{{ column }}{{ count }}
</span>
</span>
</div>
</el-card>
</div>
<!-- 图例 -->
<div class="legend-container">
<div class="legend-item">
<div class="legend-color layer-1"></div>
<span class="legend-text">一层</span>
</div>
<div class="legend-item">
<div class="legend-color layer-2"></div>
<span class="legend-text">二层</span>
</div>
<div class="legend-item">
<div class="legend-color occupied"></div>
<span class="legend-text">已占用</span>
</div>
</div>
<!-- 分列库位容器 -->
<div class="layers-container">
<!-- 无数据提示 -->
<div class="empty-tip" v-if="Object.keys(columns).length === 0 && warehouseList.length > 0">
暂无解析到有效的库位分列数据
</div>
<div class="empty-tip" v-else-if="warehouseList.length === 0">
<div class="empty-text">该分类下暂无库位数据</div>
<el-button type="primary" icon="el-icon-plus" @click="openInitDialog">初始化库位</el-button>
</div>
<warehouse-interlaced
v-else="warehouseList.length"
:id="id" :columns="columns"
@split-warehouse="handleSplitWarehouse"
@merge-warehouse="handleMergeWarehouse" />
</div>
</div>
</template>
<script>
import WarehouseInterlaced from './WarehouseInterlaced.vue';
export default {
name: "WarehouseBird",
components: { WarehouseInterlaced },
props: {
// 原始库位列表
warehouseList: {
type: Array,
default: () => []
},
id: {
type: String,
default: ''
}
},
data() {
return {
// 分列库位数据核心修改从layers改为columns
columns: {},
// 统计信息(适配分列逻辑)
statistics: {
total: 0,
columnCount: 0,
columnDetail: {}
}
};
},
watch: {
// 监听库位列表变化,重新构建分列数据
warehouseList: {
immediate: true,
handler(newVal) {
this.buildWarehouseBox(newVal);
}
}
},
methods: {
handleSplitWarehouse(warehouse) {
this.$emit('split-warehouse', warehouse);
},
handleMergeWarehouse(warehouse) {
this.$emit('merge-warehouse', warehouse);
},
/**
* 解析第三级库位编码
*/
parseWarehouseCode(code) {
if (!code) return null;
const reg = /^([A-Za-z])(\d+[A-Za-z])(\d)-(\d{2})-(\d+)$/;
const match = code.match(reg);
if (!match) {
console.warn(`库位编码解析失败:${code},格式不符合规范`);
return null;
}
return {
level: 3,
warehouseFirst: match[1],
warehouseSecond: match[2],
column: Number(match[3]),
row: Number(match[4]),
layer: match[5],
};
},
/**
* 解析第四级库位编码格式为F2A1-X01-1
*/
parseWarehouseCodeFourth(code) {
if (!code) return null;
const reg = /^([A-Za-z])(\d+[A-Za-z])(\d)-X(\d{2})-(\d+)$/;
const match = code.match(reg);
if (!match) {
console.warn(`库位编码解析失败:${code},格式不符合规范`);
return null;
}
return {
level: 4,
warehouseFirst: match[1],
warehouseSecond: match[2],
column: Number(match[3]),
row: Number(match[4]),
layer: match[5],
};
},
/**
* 重构:按列构建库位数据结构,每列分为两层数组
*/
buildWarehouseBox(list) {
const columnMap = {}; // 按列分组的核心对象
const statistics = {
total: list.length,
columnCount: 0,
columnDetail: {},
};
// 1. 按列分组每列内部分为layer1和layer2两个数组
list.forEach((warehouse) => {
let codeInfo = {}
if (warehouse.actualWarehouseType == 4) {
codeInfo = this.parseWarehouseCodeFourth(warehouse.actualWarehouseCode);
} else {
codeInfo = this.parseWarehouseCode(warehouse.actualWarehouseCode);
}
if (!codeInfo) return;
const { layer, row, column } = codeInfo;
warehouse.parsedInfo = codeInfo;
// 初始化列数据结构每列包含layer1、layer2数组以及最大行号
if (!columnMap[column]) {
columnMap[column] = {
maxRow: 0,
layer1: [], // 第一层库位数组
layer2: [], // 第二层库位数组
total: 0 // 该列总库位数
};
}
// 更新列的最大行号
columnMap[column].maxRow = Math.max(columnMap[column].maxRow, row);
// 根据层数将库位放入对应数组
if (layer === '1' || layer === 1) {
columnMap[column].layer1.push(warehouse);
} else if (layer === '2' || layer === 2) {
columnMap[column].layer2.push(warehouse);
}
// 更新该列总库位数
columnMap[column].total = columnMap[column].layer1.length + columnMap[column].layer2.length;
});
// 2. 对每列的两层数据分别按行号排序
Object.keys(columnMap).forEach((column) => {
const columnData = columnMap[column];
// 按行号排序(保证展示顺序正确)
columnData.layer1.sort((a, b) => a.parsedInfo.row - b.parsedInfo.row);
columnData.layer2.sort((a, b) => a.parsedInfo.row - b.parsedInfo.row);
});
// 3. 更新统计信息(适配分列逻辑)
statistics.columnCount = Object.keys(columnMap).length;
Object.keys(columnMap).forEach((column) => {
statistics.columnDetail[column] = columnMap[column].total;
});
// 4. 赋值到响应式数据
this.columns = columnMap;
this.statistics = statistics;
},
/**
* 打开初始化弹窗(透传至根组件)
*/
openInitDialog() {
this.$emit('open-init-dialog');
}
},
};
</script>
<style scoped lang="scss">
.bird-container {
width: 100%;
height: 100%;
}
// 统计卡片样式
.statistics-card {
margin-bottom: 16px;
.statistics-item {
margin-bottom: 8px;
&:last-child {
margin-bottom: 0;
}
.label {
color: #606266;
font-size: 14px;
}
.value {
color: #303133;
font-size: 14px;
font-weight: 500;
margin-left: 8px;
}
}
}
// 图例样式
.legend-container {
display: flex;
align-items: center;
margin-bottom: 16px;
padding: 8px 12px;
background: #f8f9fa;
border-radius: 4px;
.legend-item {
display: flex;
align-items: center;
margin-right: 24px;
&:last-child {
margin-right: 0;
}
.legend-color {
width: 16px;
height: 16px;
border-radius: 2px;
margin-right: 8px;
border: 1px solid #e6e6e6;
}
.normal {
background-color: #ffffff;
}
.layer-1 {
background-color: #fff3e0;
}
.layer-2 {
background-color: #e8f5e9;
}
.occupied {
background-color: #fafafa;
}
.legend-text {
font-size: 14px;
color: #606266;
}
}
}
// 分列容器样式
.layers-container {
display: flex;
.layer-section {
flex: 1;
max-width: 50%;
margin-bottom: 24px;
}
.empty-tip {
text-align: center;
padding: 32px;
color: #909399;
font-size: 14px;
background: #fff;
border-radius: 8px;
.empty-text {
margin-bottom: 16px;
}
}
}
</style>