feat(仓库管理): 优化仓库网格布局并增强库位编码解析
- 在WarehouseGrid组件中添加layerData变化的调试日志 - 将overview.vue中的网格列数从10增加到30 - 重构WarehouseBird组件中的库位编码解析逻辑,支持更灵活的编码格式 - 优化WarehouseInterlaced组件的布局,添加横向滚动容器并改进单元格宽度计算 - 同步列标尺和网格区域的滚动行为
This commit is contained in:
@@ -48,11 +48,8 @@
|
||||
<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" />
|
||||
<warehouse-interlaced v-else="warehouseList.length" :id="id" :columns="columns"
|
||||
@split-warehouse="handleSplitWarehouse" @merge-warehouse="handleMergeWarehouse" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -105,10 +102,21 @@ export default {
|
||||
},
|
||||
/**
|
||||
* 解析第三级库位编码
|
||||
* 新规则:
|
||||
* 1. 前三位:数字或字母的任意组合
|
||||
* 2. column:从第五位开始到第一个"-"为止(支持多位数)
|
||||
* 3. 保留 row(两位数字)、layer(数字)的解析规则
|
||||
*/
|
||||
parseWarehouseCode(code) {
|
||||
if (!code) return null;
|
||||
const reg = /^([A-Za-z])(\d+[A-Za-z])(\d)-(\d{2})-(\d+)$/;
|
||||
|
||||
// 新正则表达式解析规则
|
||||
// ^([A-Za-z0-9]{3}) :匹配前3位(数字/字母)
|
||||
// . :匹配第4位(任意单个字符)
|
||||
// ([^-]+) :匹配column(到第一个"-"为止,至少1位)
|
||||
// -(\d{2}) :匹配row(两位数字)
|
||||
// -(\d+) :匹配layer(一位或多位数字)
|
||||
const reg = /^([A-Za-z0-9]{3})([^-]+)-(\d{2})-(\d+)$/;
|
||||
const match = code.match(reg);
|
||||
|
||||
if (!match) {
|
||||
@@ -118,11 +126,12 @@ export default {
|
||||
|
||||
return {
|
||||
level: 3,
|
||||
warehouseFirst: match[1],
|
||||
warehouseSecond: match[2],
|
||||
column: Number(match[3]),
|
||||
row: Number(match[4]),
|
||||
layer: match[5],
|
||||
warehousePrefix: match[1], // 前三位(数字/字母),替代原warehouseFirst/warehouseSecond
|
||||
warehouseFirst: match[1].slice(0, 2), // 前三位的前两个字符
|
||||
warehouseSecond: match[1].slice(2, 3), // 前三位的第三个字符
|
||||
column: Number(match[2]), // 第五位到第一个"-"的内容(多位数)
|
||||
row: Number(match[3]), // 两位数字的行号
|
||||
layer: match[4] // 层级数字(可多位数)
|
||||
};
|
||||
},
|
||||
|
||||
@@ -131,7 +140,7 @@ export default {
|
||||
*/
|
||||
parseWarehouseCodeFourth(code) {
|
||||
if (!code) return null;
|
||||
const reg = /^([A-Za-z])(\d+[A-Za-z])(\d)-X(\d{2})-(\d+)$/;
|
||||
const reg = /^([A-Za-z0-9]{3})([^-]+)-X(\d{2})-(\d+)$/;
|
||||
const match = code.match(reg);
|
||||
|
||||
if (!match) {
|
||||
@@ -139,13 +148,17 @@ export default {
|
||||
return null;
|
||||
}
|
||||
|
||||
// console.log('match:', match);
|
||||
|
||||
return {
|
||||
level: 4,
|
||||
warehouseFirst: match[1],
|
||||
warehouseSecond: match[2],
|
||||
column: Number(match[3]),
|
||||
row: Number(match[4]),
|
||||
layer: match[5],
|
||||
warehousePrefix: match[1],
|
||||
warehouseFirst: match[1].slice(0, 2), // 前三位的前两个字符
|
||||
warehouseSecond: match[1].slice(2, 3), // 前三位的第三个字符
|
||||
// warehouseSecond: match[2],
|
||||
column: Number(match[2]),
|
||||
row: Number(match[3]),
|
||||
layer: match[4],
|
||||
};
|
||||
},
|
||||
|
||||
@@ -200,7 +213,7 @@ export default {
|
||||
// 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);
|
||||
@@ -310,7 +323,7 @@ export default {
|
||||
// 分列容器样式
|
||||
.layers-container {
|
||||
display: flex;
|
||||
|
||||
|
||||
.layer-section {
|
||||
flex: 1;
|
||||
max-width: 50%;
|
||||
|
||||
@@ -163,6 +163,7 @@ export default {
|
||||
layerData: {
|
||||
deep: true,
|
||||
handler() {
|
||||
console.log('layerData 变化:', this.layerData);
|
||||
this.calcContainerWidth();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,72 +1,75 @@
|
||||
<template>
|
||||
<div class="multi-layer-grid" ref="gridContainer">
|
||||
<el-alert v-if="!isComposite" type="warning" title="当前库区不支持复合架" show-icon></el-alert>
|
||||
<!-- 列标尺区域 - 添加切换按钮 -->
|
||||
<div class="col-ruler" :style="{ '--cell-width': `${cellWidth}px` }">
|
||||
<div class="ruler-empty"></div>
|
||||
<div v-for="col in sortedColumnKeys" :key="`col-${col}`" class="ruler-item">
|
||||
<!-- 列标尺文本 -->
|
||||
<span class="column-number">{{ col }}</span>
|
||||
<!-- 拆分/合并切换按钮 -->
|
||||
<button class="split-merge-toggle"
|
||||
v-if="isComposite && splitColumns.includes(Number(col))"
|
||||
:class="{ 'is-split': getColumnLevel(col) === 4, 'is-merge': getColumnLevel(col) === 3 }"
|
||||
@click.stop="handleColumnToggle(col)" :title="getColumnLevel(col) === 3 ? '点击切换为小卷状态' : '点击切换为大卷状态'">
|
||||
<i class="el-icon-s-tools"></i>
|
||||
<span class="toggle-text">{{ getColumnLevel(col) === 3 ? '大卷状态' : '小卷状态' }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row-grid-wrapper">
|
||||
<div class="row-ruler" :style="{ '--total-height': `${rulerTotalHeight}px` }">
|
||||
<div v-for="row in rulerMaxRow" :key="`row-${row}`" class="ruler-item">
|
||||
{{ row }}
|
||||
<!-- 1. 新增横向滚动容器,包裹列标尺和网格区域 -->
|
||||
<div class="scroll-wrapper" ref="scrollWrapper">
|
||||
<!-- 列标尺区域 - 添加切换按钮 -->
|
||||
<div class="col-ruler" :style="{ '--cell-width': `${cellWidth}px` }">
|
||||
<div class="ruler-empty"></div>
|
||||
<div v-for="col in sortedColumnKeys" :key="`col-${col}`" class="ruler-item">
|
||||
<!-- 列标尺文本 -->
|
||||
<span class="column-number">{{ col }}</span>
|
||||
<!-- 拆分/合并切换按钮 -->
|
||||
<button class="split-merge-toggle"
|
||||
v-if="isComposite && splitColumns.includes(Number(col))"
|
||||
:class="{ 'is-split': getColumnLevel(col) === 4, 'is-merge': getColumnLevel(col) === 3 }"
|
||||
@click.stop="handleColumnToggle(col)" :title="getColumnLevel(col) === 3 ? '点击切换为小卷状态' : '点击切换为大卷状态'">
|
||||
<i class="el-icon-s-tools"></i>
|
||||
<span class="toggle-text">{{ getColumnLevel(col) === 3 ? '大卷状态' : '小卷状态' }}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid-container" :style="{
|
||||
'--half-cell-width': `${halfCellWidth}px`,
|
||||
'--cell-width': `${cellWidth}px`,
|
||||
'--column-count': sortedColumnKeys.length * 2,
|
||||
'--total-height': `${rulerTotalHeight}px`
|
||||
}">
|
||||
<template v-for="column in sortedColumnKeys">
|
||||
<div class="column-container layer-1-container" v-if="columnWarehouseData[column]" :style="{
|
||||
gridRow: '1 / -1',
|
||||
gridColumn: `${(column - 1) * 2 + 1} / ${(column - 1) * 2 + 2}`,
|
||||
'--item-height': `${columnWarehouseData[column].cellHeight}px`
|
||||
}">
|
||||
<div v-for="warehouse in columnWarehouseData[column].layer1"
|
||||
:key="`warehouse-1-${warehouse.actualWarehouseId}`" class="warehouse-cell layer-1"
|
||||
:class="{ disabled: warehouse.isEnabled === 0 }" @click.stop="handleCellClick(warehouse)">
|
||||
<div class="cell-name">
|
||||
<div class="cell-line1">{{ warehouse.actualWarehouseName || '-' }}</div>
|
||||
<div class="cell-line2">{{ warehouse.currentCoilNo || '-' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row-grid-wrapper">
|
||||
<div class="row-ruler" :style="{ '--total-height': `${rulerTotalHeight}px` }">
|
||||
<div v-for="row in rulerMaxRow" :key="`row-${row}`" class="ruler-item">
|
||||
{{ row }}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column-container layer-2-container" v-if="columnWarehouseData[column]" :style="{
|
||||
gridRow: '1 / -1',
|
||||
gridColumn: `${(column - 1) * 2 + 2} / ${(column - 1) * 2 + 3}`,
|
||||
'--item-height': `${columnWarehouseData[column].cellHeight}px`,
|
||||
'--offset-value': `${columnWarehouseData[column].cellHeight * 0.5}px`,
|
||||
height: `${(columnWarehouseData[column].layer2.length * columnWarehouseData[column].cellHeight)}px`
|
||||
}">
|
||||
<div v-for="warehouse in columnWarehouseData[column].layer2"
|
||||
:key="`warehouse-2-${warehouse.actualWarehouseId}`" class="warehouse-cell layer-2"
|
||||
:class="{ disabled: warehouse.isEnabled === 0 }" :style="{
|
||||
transform: `translateY(var(--offset-value))`,
|
||||
position: 'relative'
|
||||
}" @click.stop="handleCellClick(warehouse)">
|
||||
<div class="cell-name">
|
||||
<div class="cell-line1">{{ warehouse.actualWarehouseName || '-' }}</div>
|
||||
<div class="cell-line2">{{ warehouse.currentCoilNo || '-' }}</div>
|
||||
<div class="grid-container" :style="{
|
||||
'--half-cell-width': `${halfCellWidth}px`,
|
||||
'--cell-width': `${cellWidth}px`,
|
||||
'--column-count': sortedColumnKeys.length * 2,
|
||||
'--total-height': `${rulerTotalHeight}px`
|
||||
}">
|
||||
<template v-for="column in sortedColumnKeys">
|
||||
<div class="column-container layer-1-container" v-if="columnWarehouseData[column]" :style="{
|
||||
gridRow: '1 / -1',
|
||||
gridColumn: `${(column - 1) * 2 + 1} / ${(column - 1) * 2 + 2}`,
|
||||
'--item-height': `${columnWarehouseData[column].cellHeight}px`
|
||||
}">
|
||||
<div v-for="warehouse in columnWarehouseData[column].layer1"
|
||||
:key="`warehouse-1-${warehouse.actualWarehouseId}`" class="warehouse-cell layer-1"
|
||||
:class="{ disabled: warehouse.isEnabled === 0 }" @click.stop="handleCellClick(warehouse)">
|
||||
<div class="cell-name">
|
||||
<div class="cell-line1">{{ warehouse.actualWarehouseName || '-' }}</div>
|
||||
<div class="cell-line2">{{ warehouse.currentCoilNo || '-' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<div class="column-container layer-2-container" v-if="columnWarehouseData[column]" :style="{
|
||||
gridRow: '1 / -1',
|
||||
gridColumn: `${(column - 1) * 2 + 2} / ${(column - 1) * 2 + 3}`,
|
||||
'--item-height': `${columnWarehouseData[column].cellHeight}px`,
|
||||
'--offset-value': `${columnWarehouseData[column].cellHeight * 0.5}px`,
|
||||
height: `${(columnWarehouseData[column].layer2.length * columnWarehouseData[column].cellHeight)}px`
|
||||
}">
|
||||
<div v-for="warehouse in columnWarehouseData[column].layer2"
|
||||
:key="`warehouse-2-${warehouse.actualWarehouseId}`" class="warehouse-cell layer-2"
|
||||
:class="{ disabled: warehouse.isEnabled === 0 }" :style="{
|
||||
transform: `translateY(var(--offset-value))`,
|
||||
position: 'relative'
|
||||
}" @click.stop="handleCellClick(warehouse)">
|
||||
<div class="cell-name">
|
||||
<div class="cell-line1">{{ warehouse.actualWarehouseName || '-' }}</div>
|
||||
<div class="cell-line2">{{ warehouse.currentCoilNo || '-' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -186,9 +189,12 @@ export default {
|
||||
return this.rulerMaxRow * this.rulerRowHeight;
|
||||
},
|
||||
cellWidth() {
|
||||
if (!this.containerWidth || this.sortedColumnKeys.length === 0) return 60;
|
||||
const availableWidth = Math.max(0, this.containerWidth - 30);
|
||||
return availableWidth / this.sortedColumnKeys.length;
|
||||
// 2. 优化cellWidth计算:最小宽度从120调整为80,避免列数过多时宽度过大导致溢出
|
||||
if (!this.containerWidth || this.sortedColumnKeys.length === 0) return 120;
|
||||
const availableWidth = Math.max(0, this.containerWidth - 30); // 30是行标尺宽度
|
||||
const calcWidth = availableWidth / this.sortedColumnKeys.length;
|
||||
// 最小宽度80px,最大不限制(交给滚动容器处理)
|
||||
return Math.max(120, calcWidth);
|
||||
},
|
||||
halfCellWidth() {
|
||||
return this.cellWidth / 2;
|
||||
@@ -237,6 +243,8 @@ export default {
|
||||
immediate: true,
|
||||
deep: true,
|
||||
handler(newVal) {
|
||||
console.log('columns 变化:', newVal);
|
||||
|
||||
if (this.isMounted) {
|
||||
clearTimeout(this.resizeTimer);
|
||||
this.resizeTimer = setTimeout(() => {
|
||||
@@ -250,13 +258,34 @@ export default {
|
||||
this.isMounted = true;
|
||||
this.calcContainerWidth();
|
||||
window.addEventListener('resize', this.handleResize);
|
||||
// 3. 绑定滚动同步事件:列标尺和网格区域同步滚动
|
||||
const scrollWrapper = this.$refs.scrollWrapper;
|
||||
if (scrollWrapper) {
|
||||
scrollWrapper.addEventListener('scroll', this.syncScroll);
|
||||
}
|
||||
},
|
||||
beforeUnmount() {
|
||||
window.removeEventListener('resize', this.handleResize);
|
||||
clearTimeout(this.resizeTimer);
|
||||
this.isMounted = false;
|
||||
// 移除滚动监听
|
||||
const scrollWrapper = this.$refs.scrollWrapper;
|
||||
if (scrollWrapper) {
|
||||
scrollWrapper.removeEventListener('scroll', this.syncScroll);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
// 4. 同步滚动方法(确保列标尺和网格滚动位置一致)
|
||||
syncScroll(e) {
|
||||
const target = e.target;
|
||||
// 确保滚动行为同步(此处主要是统一滚动容器的滚动,无需额外处理,因为都在同一个scroll-wrapper里)
|
||||
this.$nextTick(() => {
|
||||
const colRuler = target.querySelector('.col-ruler');
|
||||
if (colRuler) {
|
||||
colRuler.scrollLeft = target.scrollLeft;
|
||||
}
|
||||
});
|
||||
},
|
||||
// 获取指定列的level值(3=合并,4=拆分)
|
||||
getColumnLevel(column) {
|
||||
const columnData = this.columns[column] || {};
|
||||
@@ -391,6 +420,34 @@ export default {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
// 主容器隐藏横向溢出(滚动交给内部scroll-wrapper)
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
// 5. 新增滚动容器样式:统一管理列标尺和网格的横向滚动
|
||||
.scroll-wrapper {
|
||||
width: 100%;
|
||||
height: calc(100% - 42px); // 42是alert提示框高度,可根据实际调整
|
||||
overflow-x: auto; // 横向滚动,纵向隐藏
|
||||
overflow-y: hidden;
|
||||
scrollbar-width: thin; // 优化滚动条样式(Firefox)
|
||||
scrollbar-color: #ccc #f5f7fa;
|
||||
|
||||
// 优化Chrome滚动条样式
|
||||
&::-webkit-scrollbar {
|
||||
height: 6px;
|
||||
}
|
||||
&::-webkit-scrollbar-track {
|
||||
background: #f5f7fa;
|
||||
border-radius: 3px;
|
||||
}
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background: #ccc;
|
||||
border-radius: 3px;
|
||||
&:hover {
|
||||
background: #999;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.col-ruler {
|
||||
@@ -399,9 +456,10 @@ export default {
|
||||
line-height: 30px;
|
||||
background: #f5f7fa;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
width: 100%;
|
||||
width: fit-content; // 6. 列标尺宽度自适应内容,而非100%
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
// 移除overflow: hidden,让滚动容器控制溢出
|
||||
// overflow: hidden;
|
||||
|
||||
.ruler-empty {
|
||||
width: 30px;
|
||||
@@ -481,10 +539,11 @@ export default {
|
||||
|
||||
.row-grid-wrapper {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
width: fit-content; // 7. 网格容器宽度自适应内容
|
||||
height: calc(100% - 30px);
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
// 移除overflow: hidden,交给scroll-wrapper控制
|
||||
// overflow: hidden;
|
||||
}
|
||||
|
||||
.row-ruler {
|
||||
@@ -507,16 +566,17 @@ export default {
|
||||
}
|
||||
|
||||
.grid-container {
|
||||
display: grid;
|
||||
width: 100%;
|
||||
grid-template-columns: repeat(var(--column-count), minmax(0, var(--half-cell-width)));
|
||||
grid-auto-rows: var(--total-height);
|
||||
display: flex;
|
||||
width: fit-content; // 8. 网格宽度自适应
|
||||
// grid-template-columns: repeat(var(--column-count), minmax(0, var(--half-cell-width)));
|
||||
// grid-auto-rows: var(--total-height);
|
||||
gap: 0px;
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
height: var(--total-height);
|
||||
overflow: hidden;
|
||||
// 移除overflow: hidden,交给scroll-wrapper控制
|
||||
// overflow: hidden;
|
||||
-ms-overflow-style: none;
|
||||
scrollbar-width: none;
|
||||
|
||||
@@ -529,6 +589,7 @@ export default {
|
||||
|
||||
.column-container {
|
||||
display: flex;
|
||||
width: var(--half-cell-width);
|
||||
flex-direction: column;
|
||||
justify-content: flex-start;
|
||||
padding: 1px;
|
||||
@@ -553,6 +614,7 @@ export default {
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
min-width: var(--half-cell-width);
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
|
||||
@@ -42,7 +42,7 @@
|
||||
<div class="grid-selector" @mousemove="handleGridHover" @click="confirmGridSelect"
|
||||
@mouseleave="resetGridHover">
|
||||
<div v-for="row in 40" :key="`grid-row-${row}`" class="grid-selector-row">
|
||||
<div v-for="col in 10" :key="`grid-col-${col}`" class="grid-selector-cell" :class="{
|
||||
<div v-for="col in 30" :key="`grid-col-${col}`" class="grid-selector-cell" :class="{
|
||||
hovered: row <= hoverRow && col <= hoverCol,
|
||||
selected: row <= initForm.rowCount && col <= initForm.columnCount
|
||||
}">
|
||||
|
||||
Reference in New Issue
Block a user