Browse Source

批量修改箱明细

java8
han\hanst 1 month ago
parent
commit
c39111beec
  1. 2
      src/api/ecss/ecss.js
  2. 747
      src/views/modules/ecss/components/PackingDetailTab.vue

2
src/api/ecss/ecss.js

@ -108,6 +108,8 @@ export const updateBoxInfo = data => createAPI(`/ecss/coDel/updateBoxInfo`,'post
export const deleteBoxInfo = data => createAPI(`/ecss/coDel/deleteBoxInfo`,'post',data) export const deleteBoxInfo = data => createAPI(`/ecss/coDel/deleteBoxInfo`,'post',data)
export const updateDetailInfo = data => createAPI(`/ecss/coDel/updateDetailInfo`,'post',data) export const updateDetailInfo = data => createAPI(`/ecss/coDel/updateDetailInfo`,'post',data)
export const deleteDetailInfo = data => createAPI(`/ecss/coDel/deleteDetailInfo`,'post',data) export const deleteDetailInfo = data => createAPI(`/ecss/coDel/deleteDetailInfo`,'post',data)
// 批量修改装箱明细
export const batchUpdatePackingInfo = data => createAPI(`/ecss/coDel/batchUpdatePackingInfo`,'post',data)
export const searchTemplateList = data => createAPI(`/select/ecssMapper/searchTemplateList/list`,'post',data) export const searchTemplateList = data => createAPI(`/select/ecssMapper/searchTemplateList/list`,'post',data)

747
src/views/modules/ecss/components/PackingDetailTab.vue

@ -1,5 +1,12 @@
<template> <template>
<div class="packing-detail-tab"> <div class="packing-detail-tab">
<!-- 批量编辑按钮 -->
<div class="batch-edit-toolbar" v-if="showActions">
<el-button type="primary" size="small" icon="el-icon-edit" @click="openBatchEditDialog">
批量编辑
</el-button>
</div>
<el-table <el-table
:data="dataListBoxes" :data="dataListBoxes"
:height="height" :height="height"
@ -168,11 +175,144 @@
<el-button type="primary" @click="submitEditDetail" >确定</el-button> <el-button type="primary" @click="submitEditDetail" >确定</el-button>
</div> </div>
</el-dialog> </el-dialog>
<!-- 批量编辑弹窗 -->
<el-dialog
title="批量编辑装箱明细"
:visible.sync="batchEditDialogVisible"
width="60%"
top="5vh"
custom-class="batch-edit-dialog"
:close-on-click-modal="false"
@open="loadBatchEditData">
<div class="batch-edit-container" v-loading="batchEditLoading">
<!-- 操作提示 -->
<div class="batch-edit-tips">
<el-alert
type="info"
:closable="false"
show-icon>
<template slot="title">
<span>直接在表格中编辑数据修改后点击"保存所有修改"按钮提交</span>
<span v-if="batchEditModifiedCount > 0" class="modified-count">
已修改 <b>{{ batchEditModifiedCount }}</b>
</span>
</template>
</el-alert>
</div>
<!-- 行合并表格 -->
<el-table
:data="batchEditTableData"
:span-method="batchEditSpanMethod"
border
size="small"
max-height="60vh"
style="width: 100%"
:row-class-name="getBatchEditRowClassName"
:header-cell-style="{background:'#f5f7fa', color:'#606266', fontWeight:'600'}">
<!-- Box信息列会合并 -->
<el-table-column label="序号" prop="item_no" width="60" align="center">
<template slot-scope="scope">
<span class="box-info-cell">{{ scope.row.item_no }}</span>
</template>
</el-table-column>
<el-table-column label="箱数" prop="box_qty" width="80" align="center">
<template slot-scope="scope">
<el-input
v-model="scope.row.box_qty"
size="mini"
type="number"
:class="{'modified-input': isBoxFieldModified(scope.row, 'box_qty')}"
@input="onBatchBoxQtyInput(scope.row)"
@change="onBatchBoxFieldChange(scope.row, 'box_qty')">
</el-input>
</template>
</el-table-column>
<el-table-column label="毛重" prop="grossWeight" width="100" align="center">
<template slot-scope="scope">
<el-input
v-model="scope.row.grossWeight"
size="mini"
type="number"
:class="{'modified-input': isBoxFieldModified(scope.row, 'grossWeight')}"
@input="onBatchGrossWeightInput(scope.row)"
@change="onBatchBoxFieldChange(scope.row, 'grossWeight')">
</el-input>
</template>
</el-table-column>
<el-table-column label="净重" prop="netWeight" width="100" align="center">
<template slot-scope="scope">
<el-input
v-model="scope.row.netWeight"
size="mini"
type="number"
:class="{'modified-input': isBoxFieldModified(scope.row, 'netWeight')}"
@input="onBatchNetWeightInput(scope.row)"
@change="onBatchBoxFieldChange(scope.row, 'netWeight')">
</el-input>
</template>
</el-table-column>
<!-- 明细信息列不合并 -->
<el-table-column label="PO" prop="poNo" min-width="120" align="left">
<template slot-scope="scope">
<span>{{ scope.row.poNo || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="PN" prop="pn" min-width="120" align="left">
<template slot-scope="scope">
<span>{{ scope.row.pn || '-' }}</span>
</template>
</el-table-column>
<el-table-column label="数量" prop="qty" width="100" align="center">
<template slot-scope="scope">
<el-input
v-model="scope.row.qty"
size="mini"
type="number"
:class="{'modified-input': isDetailFieldModified(scope.row, 'qty')}"
@change="onBatchDetailFieldChange(scope.row, 'qty')">
</el-input>
</template>
</el-table-column>
<el-table-column label="Rolls" prop="rolls" width="80" align="center">
<template slot-scope="scope">
<el-input
v-model="scope.row.rolls"
size="mini"
type="number"
:class="{'modified-input': isDetailFieldModified(scope.row, 'rolls')}"
@change="onBatchDetailFieldChange(scope.row, 'rolls')">
</el-input>
</template>
</el-table-column>
</el-table>
</div>
<div slot="footer" class="dialog-footer">
<el-button @click="batchEditDialogVisible = false">取消</el-button>
<el-button
type="primary"
:loading="batchEditSaving"
:disabled="batchEditModifiedCount === 0"
@click="saveBatchEdit">
保存所有修改 ({{ batchEditModifiedCount }})
</el-button>
</div>
</el-dialog>
</div> </div>
</template> </template>
<script> <script>
import { selectBoxList, searchCoDelPalletDataNew, updateBoxInfo, deleteBoxInfo, updateDetailInfo, deleteDetailInfo } from "@/api/ecss/ecss.js"
import { selectBoxList, searchCoDelPalletDataNew, updateBoxInfo, deleteBoxInfo, updateDetailInfo, deleteDetailInfo, batchUpdatePackingInfo } from "@/api/ecss/ecss.js"
export default { export default {
name: "PackingDetailTab", name: "PackingDetailTab",
@ -220,6 +360,16 @@ export default {
currentEditDetail: null, currentEditDetail: null,
currentEditBoxForDetail: null, currentEditBoxForDetail: null,
//
batchEditDialogVisible: false,
batchEditLoading: false,
batchEditSaving: false,
batchEditTableData: [], //
batchEditOriginalData: {}, //
batchEditModifiedBoxes: {}, // Box
batchEditModifiedDetails: {}, //
batchEditSpanInfo: [], //
// //
columnList3: [ columnList3: [
{ {
@ -390,6 +540,13 @@ export default {
] ]
} }
}, },
computed: {
//
batchEditModifiedCount() {
return Object.keys(this.batchEditModifiedBoxes).length +
Object.keys(this.batchEditModifiedDetails).length;
}
},
watch: { watch: {
currentRow: { currentRow: {
handler(newVal) { handler(newVal) {
@ -865,6 +1022,444 @@ export default {
}); });
return sums; return sums;
},
// ========== ==========
/**
* 打开批量编辑弹窗
*/
openBatchEditDialog() {
this.batchEditDialogVisible = true;
},
/**
* 加载批量编辑数据
* 将Box和明细数据扁平化为一个表格数据
*/
async loadBatchEditData() {
this.batchEditLoading = true;
this.batchEditTableData = [];
this.batchEditOriginalData = {};
this.batchEditModifiedBoxes = {};
this.batchEditModifiedDetails = {};
this.batchEditSpanInfo = [];
try {
// Box
const boxResponse = await selectBoxList(this.currentRow);
if (!boxResponse.data || boxResponse.data.code !== 0) {
this.$message.error('加载箱数据失败');
return;
}
const boxes = boxResponse.data.rows || [];
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 detailResponse = await searchCoDelPalletDataNew(detailParams);
const details = (detailResponse.data && detailResponse.data.code === 0)
? detailResponse.data.rows || []
: [];
// Box
const startIndex = tableData.length;
const rowCount = details.length > 0 ? details.length : 1;
spanInfo.push({ startIndex, rowCount, boxKey: this.getBoxRowKey(box) });
if (details.length > 0) {
//
details.forEach((detail, idx) => {
const rowData = {
// Box
_boxKey: this.getBoxRowKey(box),
_isFirstRowOfBox: idx === 0,
_rowSpan: idx === 0 ? details.length : 0,
item_no: box.item_no,
palletRemark: box.palletRemark,
box_qty: box.box_qty,
grossWeight: box.grossWeight,
netWeight: box.netWeight,
//
_detailKey: `${detail.seqNo}_${detail.itemNo}_${detail.notifyDetailItemNo}`,
_hasDetail: true,
seqNo: detail.seqNo,
itemNo: detail.itemNo,
notifyDetailItemNo: detail.notifyDetailItemNo,
poNo: detail.poNo,
pn: detail.pn,
qty: detail.qty,
rolls: detail.rolls
};
tableData.push(rowData);
//
this.batchEditOriginalData[rowData._detailKey] = {
qty: detail.qty,
rolls: detail.rolls
};
});
// Box
this.batchEditOriginalData[this.getBoxRowKey(box)] = {
box_qty: box.box_qty,
grossWeight: box.grossWeight,
netWeight: box.netWeight
};
} else {
//
const rowData = {
_boxKey: this.getBoxRowKey(box),
_isFirstRowOfBox: true,
_rowSpan: 1,
item_no: box.item_no,
palletRemark: box.palletRemark,
box_qty: box.box_qty,
grossWeight: box.grossWeight,
netWeight: box.netWeight,
_hasDetail: false,
poNo: '',
pn: '',
qty: '',
rolls: ''
};
tableData.push(rowData);
// Box
this.batchEditOriginalData[this.getBoxRowKey(box)] = {
box_qty: box.box_qty,
grossWeight: box.grossWeight,
netWeight: box.netWeight
};
}
}
this.batchEditTableData = tableData;
this.batchEditSpanInfo = spanInfo;
} catch (error) {
console.error('加载批量编辑数据失败:', error);
this.$message.error('加载数据失败');
} finally {
this.batchEditLoading = false;
}
},
/**
* 表格行合并方法
*/
batchEditSpanMethod({ row, column, rowIndex, columnIndex }) {
// Box4
if (columnIndex < 4) {
if (row._isFirstRowOfBox) {
return {
rowspan: row._rowSpan,
colspan: 1
};
} else {
return {
rowspan: 0,
colspan: 0
};
}
}
return {
rowspan: 1,
colspan: 1
};
},
/**
* 检查Box字段是否被修改
*/
isBoxFieldModified(row, field) {
const boxKey = row._boxKey;
return this.batchEditModifiedBoxes[boxKey] &&
this.batchEditModifiedBoxes[boxKey][field] !== undefined;
},
/**
* 检查明细字段是否被修改
*/
isDetailFieldModified(row, field) {
if (!row._hasDetail) return false;
const detailKey = row._detailKey;
return this.batchEditModifiedDetails[detailKey] &&
this.batchEditModifiedDetails[detailKey][field] !== undefined;
},
/**
* 批量编辑-箱数输入时联动计算净重
* 计算公式净重 = 毛重 - box_qty/2
*/
onBatchBoxQtyInput(row) {
if (this._isBatchCalculating) return;
this._isBatchCalculating = true;
try {
const boxQty = parseFloat(row.box_qty) || 0;
const grossWeight = parseFloat(row.grossWeight) || 0;
// = - box_qty/2
const netWeight = grossWeight - (boxQty / 2);
row.netWeight = netWeight.toFixed(2);
// Box
const boxKey = row._boxKey;
this.batchEditTableData.forEach(r => {
if (r._boxKey === boxKey) {
r.box_qty = row.box_qty;
r.netWeight = row.netWeight;
}
});
//
this.onBatchBoxFieldChange(row, 'netWeight');
} finally {
this._isBatchCalculating = false;
}
},
/**
* 批量编辑-毛重输入时联动计算净重
* 计算公式净重 = 毛重 - box_qty/2
*/
onBatchGrossWeightInput(row) {
if (this._isBatchCalculating) return;
this._isBatchCalculating = true;
try {
const grossWeight = parseFloat(row.grossWeight) || 0;
const boxQty = parseFloat(row.box_qty) || 0;
// = - box_qty/2
const netWeight = grossWeight - (boxQty / 2);
row.netWeight = netWeight.toFixed(2);
// Box
const boxKey = row._boxKey;
this.batchEditTableData.forEach(r => {
if (r._boxKey === boxKey) {
r.grossWeight = row.grossWeight;
r.netWeight = row.netWeight;
}
});
//
this.onBatchBoxFieldChange(row, 'netWeight');
} finally {
this._isBatchCalculating = false;
}
},
/**
* 批量编辑-净重输入时联动计算毛重
* 计算公式毛重 = 净重 + box_qty/2
*/
onBatchNetWeightInput(row) {
if (this._isBatchCalculating) return;
this._isBatchCalculating = true;
try {
const netWeight = parseFloat(row.netWeight) || 0;
const boxQty = parseFloat(row.box_qty) || 0;
// = + box_qty/2
const grossWeight = netWeight + (boxQty / 2);
row.grossWeight = grossWeight.toFixed(2);
// Box
const boxKey = row._boxKey;
this.batchEditTableData.forEach(r => {
if (r._boxKey === boxKey) {
r.netWeight = row.netWeight;
r.grossWeight = row.grossWeight;
}
});
//
this.onBatchBoxFieldChange(row, 'grossWeight');
} finally {
this._isBatchCalculating = false;
}
},
/**
* Box字段变化处理
*/
onBatchBoxFieldChange(row, field) {
const boxKey = row._boxKey;
const original = this.batchEditOriginalData[boxKey];
if (!original) return;
//
const currentValue = String(row[field]);
const originalValue = String(original[field]);
if (currentValue !== originalValue) {
//
if (!this.batchEditModifiedBoxes[boxKey]) {
this.$set(this.batchEditModifiedBoxes, boxKey, {});
}
this.$set(this.batchEditModifiedBoxes[boxKey], field, {
oldValue: original[field],
newValue: row[field]
});
// Box
this.batchEditTableData.forEach(r => {
if (r._boxKey === boxKey) {
r[field] = row[field];
}
});
} else {
//
if (this.batchEditModifiedBoxes[boxKey]) {
this.$delete(this.batchEditModifiedBoxes[boxKey], field);
if (Object.keys(this.batchEditModifiedBoxes[boxKey]).length === 0) {
this.$delete(this.batchEditModifiedBoxes, boxKey);
}
}
}
},
/**
* 明细字段变化处理
*/
onBatchDetailFieldChange(row, field) {
if (!row._hasDetail) return;
const detailKey = row._detailKey;
const original = this.batchEditOriginalData[detailKey];
if (!original) return;
const currentValue = String(row[field]);
const originalValue = String(original[field]);
if (currentValue !== originalValue) {
//
if (!this.batchEditModifiedDetails[detailKey]) {
this.$set(this.batchEditModifiedDetails, detailKey, {
row: row // 便
});
}
this.$set(this.batchEditModifiedDetails[detailKey], field, {
oldValue: original[field],
newValue: row[field]
});
} else {
//
if (this.batchEditModifiedDetails[detailKey]) {
this.$delete(this.batchEditModifiedDetails[detailKey], field);
//
const remainingFields = Object.keys(this.batchEditModifiedDetails[detailKey])
.filter(k => k !== 'row');
if (remainingFields.length === 0) {
this.$delete(this.batchEditModifiedDetails, detailKey);
}
}
}
},
/**
* 获取批量编辑行样式
*/
getBatchEditRowClassName({ row, rowIndex }) {
const classes = [];
// Box
if (this.batchEditModifiedBoxes[row._boxKey]) {
classes.push('box-modified-row');
}
//
if (row._hasDetail && this.batchEditModifiedDetails[row._detailKey]) {
classes.push('detail-modified-row');
}
return classes.join(' ');
},
/**
* 保存批量编辑
*/
async saveBatchEdit() {
if (this.batchEditModifiedCount === 0) {
this.$message.warning('没有需要保存的修改');
return;
}
this.batchEditSaving = true;
try {
// Box
const boxList = [];
for (const boxKey of Object.keys(this.batchEditModifiedBoxes)) {
const row = this.batchEditTableData.find(r => r._boxKey === boxKey);
if (!row) continue;
boxList.push({
item_no: row.item_no,
palletRemark: row.palletRemark,
box_qty: row.box_qty,
grossWeight: row.grossWeight,
netWeight: row.netWeight
});
}
//
const detailList = [];
for (const detailKey of Object.keys(this.batchEditModifiedDetails)) {
const detailMod = this.batchEditModifiedDetails[detailKey];
const row = detailMod.row;
if (!row) continue;
detailList.push({
seqNo: row.seqNo,
itemNo: row.itemNo,
notifyDetailItemNo: row.notifyDetailItemNo,
poNo: row.poNo,
pn: row.pn,
qty: row.qty,
rolls: row.rolls
});
}
// API
const batchParams = {
site: this.currentRow.site,
buNo: this.currentRow.buNo,
delNo: this.currentRow.delNo,
updateBy: this.$store.state.user.name,
boxList: boxList,
detailList: detailList
};
const response = await batchUpdatePackingInfo(batchParams);
if (response.data && response.data.code === 0) {
this.$message.success(`成功保存 ${this.batchEditModifiedCount} 处修改`);
this.batchEditDialogVisible = false;
this.loadBoxList(); //
} else {
this.$alert(response.data.msg || '保存失败', '错误', {
confirmButtonText: '确定'
});
}
} catch (error) {
console.error('批量保存失败:', error);
this.$message.error('保存失败');
} finally {
this.batchEditSaving = false;
}
} }
} }
} }
@ -996,4 +1591,154 @@ export default {
/deep/ .el-table__body-wrapper::-webkit-scrollbar-thumb:hover { /deep/ .el-table__body-wrapper::-webkit-scrollbar-thumb:hover {
background: #a8a8a8; background: #a8a8a8;
} }
/* 批量编辑工具栏 */
.batch-edit-toolbar {
margin-bottom: 10px;
display: flex;
justify-content: flex-end;
}
/* 批量编辑弹窗样式 */
/deep/ .batch-edit-dialog {
border-radius: 8px;
}
/deep/ .batch-edit-dialog .el-dialog__header {
background-color: #f5f7fa;
border-bottom: 1px solid #e4e7ed;
padding: 15px 20px;
border-radius: 8px 8px 0 0;
}
/deep/ .batch-edit-dialog .el-dialog__title {
font-size: 16px;
font-weight: 600;
color: #303133;
}
/deep/ .batch-edit-dialog .el-dialog__body {
padding: 15px 20px;
}
/deep/ .batch-edit-dialog .el-dialog__footer {
border-top: 1px solid #e4e7ed;
padding: 12px 20px;
}
/* 批量编辑弹窗容器 */
.batch-edit-container {
min-height: 300px;
}
/* 批量编辑提示 */
.batch-edit-tips {
margin-bottom: 15px;
}
.batch-edit-tips .modified-count {
color: #e6a23c;
margin-left: 10px;
}
.batch-edit-tips .modified-count b {
color: #f56c6c;
font-size: 16px;
}
/* 批量编辑表格样式 */
/deep/ .batch-edit-container .el-table {
border-radius: 4px;
border: 1px solid #ebeef5;
}
/* 表头样式 - 确保表头可见 */
/deep/ .batch-edit-container .el-table__header-wrapper {
background-color: #f5f7fa;
}
/deep/ .batch-edit-container .el-table th {
background-color: #f5f7fa !important;
color: #606266 !important;
font-weight: 600;
font-size: 13px;
padding: 8px 0;
}
/deep/ .batch-edit-container .el-table th .cell {
color: #606266 !important;
font-weight: 600;
}
/* Box信息单元格样式 */
.box-info-cell {
font-weight: 600;
color: #409eff;
font-size: 13px;
}
/* 修改过的输入框样式 */
/deep/ .batch-edit-container .modified-input .el-input__inner {
background-color: #fff7e6;
border-color: #e6a23c;
color: #e6a23c;
}
/* 修改过的行样式 */
/deep/ .batch-edit-container .box-modified-row td:nth-child(-n+4) {
background-color: #fdf6ec !important;
}
/deep/ .batch-edit-container .detail-modified-row td:nth-child(n+5) {
background-color: #fff7e6 !important;
}
/* 输入框样式 */
/deep/ .batch-edit-container .el-input--mini .el-input__inner {
text-align: center;
padding: 0 8px;
border-color: #dcdfe6;
}
/deep/ .batch-edit-container .el-input--mini .el-input__inner:focus {
border-color: #409eff;
}
/* 表格单元格内边距 */
/deep/ .batch-edit-container .el-table td {
padding: 6px 0;
}
/deep/ .batch-edit-container .el-table .cell {
padding: 0 8px;
}
/* 数字输入框隐藏上下箭头 */
/deep/ .batch-edit-container input[type="number"]::-webkit-inner-spin-button,
/deep/ .batch-edit-container input[type="number"]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
/deep/ .batch-edit-container input[type="number"] {
-moz-appearance: textfield;
}
/* 合并单元格的垂直居中 */
/deep/ .batch-edit-container .el-table__body td[rowspan] {
vertical-align: middle;
}
/deep/ .batch-edit-container .el-table__body td[rowspan] .cell {
display: flex;
align-items: center;
justify-content: center;
height: 100%;
}
/* 文字样式 */
/deep/ .batch-edit-container .el-table .cell span {
font-size: 13px;
color: #606266;
}
</style> </style>
Loading…
Cancel
Save