|
|
|
@ -14,6 +14,7 @@ |
|
|
|
</div> |
|
|
|
|
|
|
|
<!-- 行合并表格 - 箱信息和明细一目了然 --> |
|
|
|
<div style="height: 300px"> |
|
|
|
<el-table |
|
|
|
:data="flattenedTableData" |
|
|
|
:max-height="300" |
|
|
|
@ -50,13 +51,13 @@ |
|
|
|
</template> |
|
|
|
</el-table-column> |
|
|
|
|
|
|
|
<el-table-column label="毛重" prop="grossWeight" width="185" align="center"> |
|
|
|
<el-table-column label="毛重(KG)" prop="grossWeight" width="185" align="center"> |
|
|
|
<template slot-scope="scope"> |
|
|
|
<span class="box-info-text">{{ scope.row.grossWeight }}</span> |
|
|
|
</template> |
|
|
|
</el-table-column> |
|
|
|
|
|
|
|
<el-table-column label="净重" prop="netWeight" width="100" align="center"> |
|
|
|
<el-table-column label="净重(KG)" prop="netWeight" width="100" align="center"> |
|
|
|
<template slot-scope="scope"> |
|
|
|
<span class="box-info-text" :style="{ color: scope.row.netWeight < 0 ? 'red' : '' }">{{ scope.row.netWeight }}</span> |
|
|
|
</template> |
|
|
|
@ -99,7 +100,7 @@ |
|
|
|
</template> |
|
|
|
</el-table-column> |
|
|
|
</el-table> |
|
|
|
|
|
|
|
</div> |
|
|
|
<!-- 托盘重量和总毛重显示(模拟表格行布局) --> |
|
|
|
<div class="weight-summary-table"> |
|
|
|
<div class="weight-row"> |
|
|
|
@ -107,13 +108,43 @@ |
|
|
|
<div class="weight-cell cell-label">托盘重量</div> |
|
|
|
<div class="weight-cell cell-value"></div> |
|
|
|
<div class="weight-cell cell-value">{{ palletWeight }}</div> |
|
|
|
<div class="weight-cell cell-empty"></div> |
|
|
|
<div class="weight-cell cell-value">{{currentRow.walMartOrderFlag==='Y'?'总体积(m³)':''}}</div> |
|
|
|
<div class="weight-cell cell-value" v-if="currentRow.walMartOrderFlag==='Y'" style="display: flex; align-items: center; gap: 5px;"> |
|
|
|
<template v-if="!isEditingTotalVolume"> |
|
|
|
<span>{{ totalVolume !== null && totalVolume !== undefined ? totalVolume : '-' }}</span> |
|
|
|
<i v-if="showActions" class="el-icon-edit" |
|
|
|
style="cursor: pointer; color: #409EFF; font-size: 14px;" |
|
|
|
@click="startEditTotalVolume" |
|
|
|
title="编辑总体积"></i> |
|
|
|
</template> |
|
|
|
<template v-else> |
|
|
|
<el-input |
|
|
|
v-model="editingTotalVolumeValue" |
|
|
|
size="mini" |
|
|
|
type="number" |
|
|
|
:min="0" |
|
|
|
style="width: 120px;" |
|
|
|
@keyup.enter.native="saveTotalVolume" |
|
|
|
ref="totalVolumeInput"> |
|
|
|
</el-input> |
|
|
|
<i class="el-icon-check" |
|
|
|
style="cursor: pointer; color: #67C23A; font-size: 16px; font-weight: bold;" |
|
|
|
@click="saveTotalVolume" |
|
|
|
:class="{'disabled-icon': savingTotalVolume}" |
|
|
|
title="保存"></i> |
|
|
|
<i class="el-icon-close" |
|
|
|
style="cursor: pointer; color: #F56C6C; font-size: 16px; font-weight: bold;" |
|
|
|
@click="cancelEditTotalVolume" |
|
|
|
:class="{'disabled-icon': savingTotalVolume}" |
|
|
|
title="取消"></i> |
|
|
|
</template> |
|
|
|
</div> |
|
|
|
<div v-else class="weight-cell cell-empty"></div> |
|
|
|
<div v-if="currentRow.boxSizeFlag==='Y'" class="weight-cell cell-empty"></div> |
|
|
|
<div class="weight-cell cell-empty"></div> |
|
|
|
<div class="weight-cell cell-empty"></div> |
|
|
|
<div class="weight-cell cell-empty"></div> |
|
|
|
<div class="weight-cell cell-empty"></div> |
|
|
|
<div class="weight-cell cell-empty"></div> |
|
|
|
</div> |
|
|
|
<div class="weight-row"> |
|
|
|
<div class="weight-cell cell-checkbox"></div> |
|
|
|
@ -420,7 +451,7 @@ |
|
|
|
</template> |
|
|
|
|
|
|
|
<script> |
|
|
|
import { selectBoxList, searchCoDelPalletDataNew, updateBoxInfo, deleteBoxInfo, updateDetailInfo, deleteDetailInfo, batchUpdatePackingInfo, batchDeleteBoxes, searchEcssCoDelPalletHeaderData, adjustTotalGrossWeight } from "@/api/ecss/ecss.js" |
|
|
|
import { selectBoxList, searchCoDelPalletDataNew, updateBoxInfo, deleteBoxInfo, updateDetailInfo, deleteDetailInfo, batchUpdatePackingInfo, batchDeleteBoxes, searchEcssCoDelPalletHeaderData, adjustTotalGrossWeight, updateTotalVolume } from "@/api/ecss/ecss.js" |
|
|
|
|
|
|
|
export default { |
|
|
|
name: "PackingDetailTab", |
|
|
|
@ -496,6 +527,12 @@ export default { |
|
|
|
actualGrossWeight: '' |
|
|
|
}, |
|
|
|
|
|
|
|
// 总体积相关 |
|
|
|
totalVolume: 0, // 总体积 |
|
|
|
isEditingTotalVolume: false, // 是否正在编辑总体积 |
|
|
|
editingTotalVolumeValue: '', // 编辑中的总体积值 |
|
|
|
savingTotalVolume: false, // 是否正在保存总体积 |
|
|
|
|
|
|
|
// 装箱明细列定义 |
|
|
|
columnList3: [ |
|
|
|
{ |
|
|
|
@ -724,6 +761,7 @@ export default { |
|
|
|
this.flattenedTableData = []; |
|
|
|
this.palletWeight = 0; |
|
|
|
this.totalGrossWeight = 0; |
|
|
|
this.totalVolume = null; |
|
|
|
this.selectedBoxes = []; |
|
|
|
this.currentLoadingId = null; |
|
|
|
return; |
|
|
|
@ -789,6 +827,11 @@ export default { |
|
|
|
// 计算总毛重 = 合计毛重 + 托盘重量 |
|
|
|
this.calculateTotalGrossWeight(); |
|
|
|
|
|
|
|
// 加载总体积(从currentRow中获取) |
|
|
|
this.totalVolume = this.currentRow.totalVolume !== undefined && this.currentRow.totalVolume !== null |
|
|
|
? this.currentRow.totalVolume |
|
|
|
: null; |
|
|
|
|
|
|
|
console.log(`[${loadingId}] 加载完成`); |
|
|
|
} catch (error) { |
|
|
|
console.error(`[${loadingId}] 加载数据失败:`, error); |
|
|
|
@ -808,67 +851,64 @@ export default { |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
// 加载所有明细数据并生成扁平化表格数据(并行加载优化 + 分批处理 + 防竞态) |
|
|
|
// 加载所有明细数据并生成扁平化表格数据(优化为一次性批量查询) |
|
|
|
async loadAllDetailsAndFlatten(loadingId) { |
|
|
|
const startTime = performance.now(); |
|
|
|
const boxCount = this.dataListBoxes.length; |
|
|
|
|
|
|
|
this.loadingText = `正在加载${boxCount}个箱的明细数据...`; |
|
|
|
console.log(`[${loadingId}] 开始加载${boxCount}个箱的明细数据(并行模式)...`); |
|
|
|
console.log(`[${loadingId}] 开始批量加载${boxCount}个箱的明细数据...`); |
|
|
|
|
|
|
|
// 一次性查询所有箱的明细数据(不传seqNo参数,后端会返回所有箱的明细) |
|
|
|
const detailParams = { |
|
|
|
site: this.currentRow.site, |
|
|
|
buNo: this.currentRow.buNo, |
|
|
|
delNo: this.currentRow.delNo |
|
|
|
// 注意:不传seqNo,后端会查询该delNo下的所有箱明细 |
|
|
|
}; |
|
|
|
|
|
|
|
// 分批并行加载(每批最多20个请求,避免一次性请求过多) |
|
|
|
const batchSize = 20; |
|
|
|
const boxDetailsResults = []; |
|
|
|
let allDetails = []; |
|
|
|
try { |
|
|
|
const detailResponse = await searchCoDelPalletDataNew(detailParams); |
|
|
|
|
|
|
|
for (let i = 0; i < this.dataListBoxes.length; i += batchSize) { |
|
|
|
// 每批次开始前检查是否还是最新请求 |
|
|
|
// 加载完成后检查是否还是最新请求 |
|
|
|
if (this.currentLoadingId !== loadingId) { |
|
|
|
console.log(`[${loadingId}] 检测到新请求,停止当前加载`); |
|
|
|
console.log(`[${loadingId}] 检测到新请求,停止处理数据`); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
const batch = this.dataListBoxes.slice(i, i + batchSize); |
|
|
|
const batchPromises = batch.map(box => { |
|
|
|
const detailParams = { |
|
|
|
site: this.currentRow.site, |
|
|
|
buNo: this.currentRow.buNo, |
|
|
|
delNo: this.currentRow.delNo, |
|
|
|
seqNo: box.item_no, |
|
|
|
palletRemark: box.palletRemark |
|
|
|
}; |
|
|
|
|
|
|
|
return searchCoDelPalletDataNew(detailParams) |
|
|
|
.then(detailResponse => { |
|
|
|
const details = (detailResponse.data && detailResponse.data.code === 0) |
|
|
|
? detailResponse.data.rows || [] |
|
|
|
: []; |
|
|
|
return { box, details }; |
|
|
|
}) |
|
|
|
.catch(error => { |
|
|
|
console.error(`[${loadingId}] 加载箱${box.item_no}明细数据失败:`, error); |
|
|
|
return { box, details: [] }; |
|
|
|
}); |
|
|
|
}); |
|
|
|
if (detailResponse.data && detailResponse.data.code === 0) { |
|
|
|
allDetails = detailResponse.data.rows || []; |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error(`[${loadingId}] 批量加载明细数据失败:`, error); |
|
|
|
this.$message.error('加载明细数据失败'); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
// 等待当前批次完成 |
|
|
|
const batchResults = await Promise.all(batchPromises); |
|
|
|
const loadTime = performance.now() - startTime; |
|
|
|
|
|
|
|
// 批次完成后再次检查 |
|
|
|
if (this.currentLoadingId !== loadingId) { |
|
|
|
console.log(`[${loadingId}] 检测到新请求,丢弃批次结果`); |
|
|
|
return; |
|
|
|
// 将明细数据按箱号(seqNo)分组 |
|
|
|
// 注意:后端返回的明细数据是实体类(驼峰命名seqNo),而箱列表是Map(下划线命名item_no) |
|
|
|
// 使用字符串键确保类型一致 |
|
|
|
const detailsMap = new Map(); |
|
|
|
allDetails.forEach(detail => { |
|
|
|
const seqNo = String(detail.seqNo); // 转换为字符串确保类型一致 |
|
|
|
if (!detailsMap.has(seqNo)) { |
|
|
|
detailsMap.set(seqNo, []); |
|
|
|
} |
|
|
|
detailsMap.get(seqNo).push(detail); |
|
|
|
}); |
|
|
|
|
|
|
|
boxDetailsResults.push(...batchResults); |
|
|
|
|
|
|
|
// 更新进度显示 |
|
|
|
if (this.dataListBoxes.length > batchSize) { |
|
|
|
const loaded = i + batch.length; |
|
|
|
const progress = Math.min(100, Math.round(loaded / this.dataListBoxes.length * 100)); |
|
|
|
this.loadingText = `正在加载明细数据... ${progress}% (${loaded}/${boxCount})`; |
|
|
|
console.log(`[${loadingId}] 加载进度: ${progress}% (${loaded}/${boxCount})`); |
|
|
|
// 构建箱与明细的关联关系(box.item_no 对应 detail.seqNo) |
|
|
|
const boxDetailsResults = this.dataListBoxes.map(box => { |
|
|
|
const itemNo = String(box.item_no); // 转换为字符串确保类型一致 |
|
|
|
const details = detailsMap.get(itemNo) || []; |
|
|
|
if (details.length === 0) { |
|
|
|
console.log(`[${loadingId}] 警告: 箱 ${box.item_no} (原始类型: ${typeof box.item_no}) 没有找到对应的明细数据`); |
|
|
|
} |
|
|
|
} |
|
|
|
return { box, details }; |
|
|
|
}); |
|
|
|
|
|
|
|
// 渲染前最后检查一次 |
|
|
|
if (this.currentLoadingId !== loadingId) { |
|
|
|
@ -878,9 +918,6 @@ export default { |
|
|
|
|
|
|
|
this.loadingText = '正在渲染数据...'; |
|
|
|
|
|
|
|
const loadTime = performance.now() - startTime; |
|
|
|
console.log(`[${loadingId}] 明细数据加载完成,耗时:${loadTime.toFixed(0)}ms,平均每个箱:${(loadTime/boxCount).toFixed(0)}ms`); |
|
|
|
|
|
|
|
|
|
|
|
// 生成扁平化数据前最后检查 |
|
|
|
if (this.currentLoadingId !== loadingId) { |
|
|
|
@ -1467,7 +1504,7 @@ export default { |
|
|
|
return prev; |
|
|
|
} |
|
|
|
}, 0); |
|
|
|
sums[index] = sum.toFixed(3); |
|
|
|
sums[index] = sum.toFixed(2); |
|
|
|
} else if (column.property === 'netWeight') { |
|
|
|
// 净重合计 |
|
|
|
const sum = boxFirstRows.reduce((prev, curr) => { |
|
|
|
@ -1478,7 +1515,7 @@ export default { |
|
|
|
return prev; |
|
|
|
} |
|
|
|
}, 0); |
|
|
|
sums[index] = sum.toFixed(3); |
|
|
|
sums[index] = sum.toFixed(2); |
|
|
|
} else if (column.property === 'rolls') { |
|
|
|
// Rolls合计 |
|
|
|
const sum = boxFirstRows.reduce((prev, curr) => { |
|
|
|
@ -1489,7 +1526,7 @@ export default { |
|
|
|
return prev; |
|
|
|
} |
|
|
|
}, 0); |
|
|
|
sums[index] = sum.toFixed(3); |
|
|
|
sums[index] = sum.toFixed(0); |
|
|
|
} else if (column.property === 'qty') { |
|
|
|
// 明细数量合计(所有行) |
|
|
|
const sum = data.filter(item => item._hasDetail).reduce((prev, curr) => { |
|
|
|
@ -1544,20 +1581,40 @@ export default { |
|
|
|
const tableData = []; |
|
|
|
const spanInfo = []; |
|
|
|
|
|
|
|
// 遍历每个Box,获取其明细 |
|
|
|
for (const box of boxes) { |
|
|
|
const detailParams = { |
|
|
|
site: this.currentRow.site, |
|
|
|
buNo: this.currentRow.buNo, |
|
|
|
delNo: this.currentRow.delNo, |
|
|
|
seqNo: box.item_no, |
|
|
|
palletRemark: box.palletRemark |
|
|
|
}; |
|
|
|
// 一次性批量查询所有箱的明细数据(优化:从循环查询改为批量查询) |
|
|
|
const detailParams = { |
|
|
|
site: this.currentRow.site, |
|
|
|
buNo: this.currentRow.buNo, |
|
|
|
delNo: this.currentRow.delNo |
|
|
|
// 注意:不传seqNo,后端会查询该delNo下的所有箱明细 |
|
|
|
}; |
|
|
|
|
|
|
|
let allDetails = []; |
|
|
|
try { |
|
|
|
const detailResponse = await searchCoDelPalletDataNew(detailParams); |
|
|
|
const details = (detailResponse.data && detailResponse.data.code === 0) |
|
|
|
? detailResponse.data.rows || [] |
|
|
|
: []; |
|
|
|
if (detailResponse.data && detailResponse.data.code === 0) { |
|
|
|
allDetails = detailResponse.data.rows || []; |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error('批量加载明细数据失败:', error); |
|
|
|
} |
|
|
|
|
|
|
|
// 将明细数据按箱号(seqNo)分组 |
|
|
|
const detailsMap = new Map(); |
|
|
|
allDetails.forEach(detail => { |
|
|
|
const seqNo = String(detail.seqNo); // 转换为字符串确保类型一致 |
|
|
|
if (!detailsMap.has(seqNo)) { |
|
|
|
detailsMap.set(seqNo, []); |
|
|
|
} |
|
|
|
detailsMap.get(seqNo).push(detail); |
|
|
|
}); |
|
|
|
|
|
|
|
console.log(`批量编辑数据加载: 共${boxes.length}个箱,${allDetails.length}条明细,分组${detailsMap.size}个箱`); |
|
|
|
|
|
|
|
// 遍历每个Box,从分组中获取对应的明细 |
|
|
|
for (const box of boxes) { |
|
|
|
const itemNo = String(box.item_no); // 转换为字符串确保类型一致 |
|
|
|
const details = detailsMap.get(itemNo) || []; |
|
|
|
|
|
|
|
// 记录该Box的起始行索引和跨行数 |
|
|
|
const startIndex = tableData.length; |
|
|
|
@ -2120,6 +2177,78 @@ export default { |
|
|
|
} finally { |
|
|
|
this.adjustWeightSaving = false; |
|
|
|
} |
|
|
|
}, |
|
|
|
|
|
|
|
// ========== 总体积编辑相关方法 ========== |
|
|
|
|
|
|
|
/** |
|
|
|
* 开始编辑总体积 |
|
|
|
*/ |
|
|
|
startEditTotalVolume() { |
|
|
|
this.isEditingTotalVolume = true; |
|
|
|
this.editingTotalVolumeValue = this.totalVolume !== null && this.totalVolume !== undefined ? this.totalVolume.toString() : ''; |
|
|
|
this.$nextTick(() => { |
|
|
|
if (this.$refs.totalVolumeInput) { |
|
|
|
this.$refs.totalVolumeInput.focus(); |
|
|
|
} |
|
|
|
}); |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* 取消编辑总体积 |
|
|
|
*/ |
|
|
|
cancelEditTotalVolume() { |
|
|
|
if (this.savingTotalVolume) { |
|
|
|
return; |
|
|
|
} |
|
|
|
this.isEditingTotalVolume = false; |
|
|
|
this.editingTotalVolumeValue = ''; |
|
|
|
}, |
|
|
|
|
|
|
|
/** |
|
|
|
* 保存总体积 |
|
|
|
*/ |
|
|
|
async saveTotalVolume() { |
|
|
|
if (this.savingTotalVolume) { |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
const newVolume = parseFloat(this.editingTotalVolumeValue); |
|
|
|
|
|
|
|
// 验证输入 |
|
|
|
if (this.editingTotalVolumeValue !== '' && (isNaN(newVolume) || newVolume < 0)) { |
|
|
|
this.$message.warning('请输入有效的总体积(不能小于0)'); |
|
|
|
return; |
|
|
|
} |
|
|
|
|
|
|
|
this.savingTotalVolume = true; |
|
|
|
|
|
|
|
try { |
|
|
|
const params = { |
|
|
|
site: this.currentRow.site, |
|
|
|
buNo: this.currentRow.buNo, |
|
|
|
delNo: this.currentRow.delNo, |
|
|
|
totalVolume: this.editingTotalVolumeValue === '' ? null : newVolume, |
|
|
|
updateBy: this.$store.state.user.name |
|
|
|
}; |
|
|
|
|
|
|
|
const response = await updateTotalVolume(params); |
|
|
|
|
|
|
|
if (response.data && response.data.code === 0) { |
|
|
|
this.$message.success('总体积更新成功'); |
|
|
|
this.totalVolume = this.editingTotalVolumeValue === '' ? null : newVolume; |
|
|
|
this.isEditingTotalVolume = false; |
|
|
|
this.editingTotalVolumeValue = ''; |
|
|
|
this.$emit('refresh'); // 刷新父页面列表 |
|
|
|
} else { |
|
|
|
this.$message.error(response.data.msg || '更新失败'); |
|
|
|
} |
|
|
|
} catch (error) { |
|
|
|
console.error('更新总体积失败:', error); |
|
|
|
this.$message.error('更新失败'); |
|
|
|
} finally { |
|
|
|
this.savingTotalVolume = false; |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
@ -2133,7 +2262,7 @@ export default { |
|
|
|
/* 托盘重量和总毛重显示样式(模拟表格行) */ |
|
|
|
.weight-summary-table { |
|
|
|
border: 1px solid #ebeef5; |
|
|
|
border-top: none; |
|
|
|
//border-top: none; |
|
|
|
background: #fff; |
|
|
|
} |
|
|
|
|
|
|
|
@ -2566,4 +2695,10 @@ export default { |
|
|
|
font-weight: 600; |
|
|
|
color: #303133; |
|
|
|
} |
|
|
|
|
|
|
|
/* 总体积编辑禁用状态图标 */ |
|
|
|
.disabled-icon { |
|
|
|
opacity: 0.5; |
|
|
|
cursor: not-allowed !important; |
|
|
|
} |
|
|
|
</style> |