Files
xgy-oa/klp-ui/src/views/wms/warehouse/components/WarehouseBird.vue
砂糖 32d4a1a6e0 feat(仓库管理): 优化仓库鸟瞰图布局并添加图例
- 在仓库鸟瞰图中添加占用状态图例
- 调整网格布局为两栏显示,优化空间利用率
- 减少网格行数从50到40
- 缩小网格单元格尺寸并移除冗余编码显示
- 添加按钮加载状态控制
2025-12-05 13:03:08 +08:00

275 lines
6.3 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.layerCount }}</span>
</div>
<div class="statistics-item">
<span class="label">各层库位分布</span>
<span class="value">
<span v-for="(count, layer) in statistics.layerDetail" :key="layer">
{{ layer }}{{ count }}
</span>
</span>
</div>
</el-card>
</div>
<!-- 图例 -->
<div class="legend-container">
<div class="legend-item">
<div class="legend-color normal"></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">
<!-- 遍历每层渲染 Grid 组件 -->
<div v-for="(layerData, layer) in layers" :key="layer" class="layer-section">
<WarehouseGrid :layer="layer" :layer-data="layerData" />
</div>
<!-- 无数据提示 -->
<div class="empty-tip" v-if="Object.keys(layers).length === 0 && warehouseList.length > 0">
暂无解析到有效的库位分层数据
</div>
<div class="empty-tip" v-if="warehouseList.length === 0">
<div class="empty-text">该分类下暂无库位数据</div>
<el-button type="primary" icon="el-icon-plus" @click="openInitDialog">初始化库位</el-button>
</div>
</div>
</div>
</template>
<script>
import WarehouseGrid from './WarehouseGrid.vue';
export default {
name: "WarehouseBird",
components: { WarehouseGrid },
props: {
// 原始库位列表
warehouseList: {
type: Array,
default: () => []
}
},
data() {
return {
// 分层库位数据
layers: {},
// 统计信息
statistics: {
total: 0,
layerCount: 0,
layerDetail: {}
}
};
},
watch: {
// 监听库位列表变化,重新构建分层数据
warehouseList: {
immediate: true,
handler(newVal) {
this.buildWarehouseBox(newVal);
}
}
},
methods: {
/**
* 解析库位编码
*/
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 {
warehouseFirst: match[1],
warehouseSecond: match[2],
column: Number(match[3]),
row: Number(match[4]),
layer: match[5],
};
},
/**
* 构建分层库位数据结构
*/
buildWarehouseBox(list) {
const layerMap = {};
const statistics = {
total: list.length,
layerCount: 0,
layerDetail: {},
};
// 按层分组
list.forEach((warehouse) => {
const codeInfo = this.parseWarehouseCode(warehouse.actualWarehouseCode);
if (!codeInfo) return;
const { layer, row, column } = codeInfo;
warehouse.parsedInfo = codeInfo;
if (!layerMap[layer]) {
layerMap[layer] = {
maxRow: 0,
maxColumn: 0,
warehouses: [],
emptyCount: 0
};
}
layerMap[layer].maxRow = Math.max(layerMap[layer].maxRow, row);
layerMap[layer].maxColumn = Math.max(layerMap[layer].maxColumn, column);
layerMap[layer].warehouses.push(warehouse);
});
// 处理空占位和排序
Object.keys(layerMap).forEach((layer) => {
const layerData = layerMap[layer];
const totalGrid = layerData.maxRow * layerData.maxColumn;
layerData.emptyCount = Math.max(0, totalGrid - layerData.warehouses.length);
// 按行号+列号排序
layerData.warehouses.sort((a, b) => {
if (a.parsedInfo.row !== b.parsedInfo.row) {
return a.parsedInfo.row - b.parsedInfo.row;
}
return a.parsedInfo.column - b.parsedInfo.column;
});
});
// 更新统计信息
statistics.layerCount = Object.keys(layerMap).length;
Object.keys(layerMap).forEach((layer) => {
statistics.layerDetail[layer] = layerMap[layer].warehouses.length;
});
this.layers = layerMap;
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;
}
.occupied {
background-color: #c0c4cc;
}
.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>