栈板维护
@@ -633,7 +816,7 @@
updateEcssDel,
cancerConfirmEcssDel,
searchCoDelPalletDataNew,
-
+ selectBoxList,
searchEcssCoDelPalletHeaderData,
searchEcssCoDelPalletDetailData,
savePalletHeader,
@@ -646,7 +829,8 @@
searchPalletList,
updateExportFlag,
updateCodelPalletHeaderPalletQty,
- getPartPackageProperties
+ getPartPackageProperties,
+ mergeBox
}from "@/api/ecss/ecss.js"
import {getBuList}from '@/api/factory/site.js'
import excel from "@/utils/excel-util.js";
@@ -711,6 +895,18 @@
boxRemnant: false,
},
+ // 合箱批量编辑相关
+ mergeBoxDialogVisible: false,
+ mergeBoxLoading: false,
+ mergeBoxSaving: false,
+ mergeBoxTableData: [], // 扁平化的表格数据
+ mergeBoxSelection: [], // 已选择的箱(用于合箱)
+ mergeBoxOriginalData: {}, // 原始数据快照(用于对比是否修改)
+ mergeBoxModifiedBoxes: {}, // 修改过的Box记录
+ mergeBoxMergeHistory: [], // 合并历史记录(用于取消合并)
+ mergeBoxModifiedDetails: {}, // 修改过的明细记录
+ nextItemNo: 1, // 下一个序号
+
// 栈板维护相关
palletMaintenanceModelFlag: false,
palletMaintenanceRecords: [],
@@ -1957,6 +2153,14 @@
});
// 如果有任何明细有有效的rolls值,则禁用总rolls输入框
return hasValidRolls;
+ },
+
+ /**
+ * 合箱批量编辑修改数量
+ */
+ mergeBoxModifiedCount() {
+ return Object.keys(this.mergeBoxModifiedBoxes).length +
+ Object.keys(this.mergeBoxModifiedDetails).length;
}
},
@@ -3077,6 +3281,849 @@
})
},
+ // ========== 合箱批量编辑相关方法 ==========
+
+ /**
+ * 打开合箱批量编辑弹出框
+ */
+ openMergeBoxDialog() {
+ if(this.currentRow.site===''||this.currentRow.site==null){
+ this.$alert('请先选择发货通知单!', '错误', {
+ confirmButtonText: '确定'
+ })
+ return false
+ }
+ this.mergeBoxDialogVisible = true
+ },
+
+ /**
+ * 加载合箱批量编辑数据
+ * 将发货通知单明细扁平化为表格数据,每条明细默认是一个箱子
+ */
+ async loadMergeBoxData() {
+ this.mergeBoxLoading = true
+ this.mergeBoxTableData = []
+ this.mergeBoxSelection = []
+ this.mergeBoxOriginalData = {}
+ this.mergeBoxModifiedBoxes = {}
+ this.mergeBoxModifiedDetails = {}
+ this.mergeBoxMergeHistory = []
+ this.nextItemNo = 1
+
+ try {
+ // 每次都重新查询最新的发货通知单明细数据
+ const detailResponse = await searchEcssCoDelNotifyDetail(this.currentRow)
+ const detailList = (detailResponse.data && detailResponse.data.code === 0)
+ ? detailResponse.data.rows || []
+ : []
+
+ if (detailList.length === 0) {
+ this.$message.warning('当前发货通知单没有明细数据')
+ this.mergeBoxLoading = false
+ return
+ }
+
+ // 处理明细数据 - 每条明细作为一个单独的箱
+ const tableData = []
+ let itemNo = 1
+
+ // 只处理剩余数量(surplus_qty)大于0的明细
+ const detailsToProcess = []
+ detailList.forEach(detail => {
+ const surplusQty = Number(detail.surplusQty) || 0
+
+ // 只处理剩余数量大于0的明细
+ if (surplusQty > 0) {
+ detailsToProcess.push({
+ detail: detail,
+ unpackedQty: surplusQty // 使用剩余数量
+ })
+ }
+ })
+
+ if (detailsToProcess.length === 0) {
+ this.$message.warning('所有明细都已装箱完毕')
+ this.mergeBoxLoading = false
+ return
+ }
+
+ // 创建表格数据
+ // searchEcssCoDelNotifyDetail已经返回了rollqty、boxrolls、boxweight,直接使用
+ detailsToProcess.forEach(({detail, unpackedQty}) => {
+ const boxKey = `box_${itemNo}`
+
+ // 直接从detail中读取包装属性(小写字段名)
+ const rollQty = Number(detail.rollqty) || 0
+ const boxRolls = Number(detail.boxrolls) || 0
+ const boxWeight = Number(detail.boxweight) || 0
+
+ // 计算默认值
+ let detailRolls = 0
+ let box_qty = 1
+ let grossWeight = 0
+ let netWeight = 0
+ let rolls = 0
+
+ // 1. 根据数量计算明细rolls(与calculateRolls保持一致)
+ if (rollQty > 0) {
+ detailRolls = parseFloat((unpackedQty / rollQty).toFixed(4))
+ // 总rolls = 明细rolls四舍五入取整(初始时每箱只有一条明细)
+ rolls = Math.round(detailRolls)
+ }
+
+ // 2. 根据数量计算箱数
+ if (rollQty > 0 && boxRolls > 0) {
+ const eaPerBox = rollQty * boxRolls
+ box_qty = Math.ceil(unpackedQty / eaPerBox)
+ }
+
+ // 3. 根据箱数计算毛重
+ if (boxWeight > 0) {
+ grossWeight = parseFloat((box_qty * boxWeight).toFixed(3))
+ }
+
+ // 4. 根据毛重计算净重(与装箱按钮保持一致)
+ netWeight = parseFloat((grossWeight - (box_qty / 2)).toFixed(3))
+
+ // 创建一行数据(Box和明细合并在一起)
+ const rowData = {
+ // Box信息
+ _boxKey: boxKey,
+ _isFirstRowOfBox: true,
+ _rowSpan: 1,
+ item_no: itemNo,
+ box_qty: box_qty,
+ grossWeight: grossWeight,
+ netWeight: netWeight,
+ rolls: rolls,
+ // 明细信息
+ _detailKey: `${detail.itemNo}_${detail.customerPO}_${detail.pn}`,
+ _hasDetail: true,
+ itemNo: detail.itemNo,
+ poNo: detail.customerPO,
+ pn: detail.pn,
+ qty: unpackedQty,
+ detailRolls: detailRolls,
+ readyDate: detail.readyDate,
+ partDesc: detail.partDesc,
+ // 缓存物料属性(用于后续自动计算)
+ rollQtyCache: rollQty,
+ boxRollsCache: boxRolls,
+ boxWeightCache: boxWeight
+ }
+
+ tableData.push(rowData)
+
+ // 保存Box原始数据
+ this.mergeBoxOriginalData[boxKey] = {
+ box_qty: box_qty,
+ grossWeight: grossWeight,
+ netWeight: netWeight,
+ rolls: rolls,
+ boxWeight: boxWeight
+ }
+
+ // 保存明细原始数据
+ this.mergeBoxOriginalData[rowData._detailKey] = {
+ qty: unpackedQty,
+ detailRolls: detailRolls
+ }
+
+ itemNo++
+ })
+
+ this.mergeBoxTableData = tableData
+ this.nextItemNo = itemNo
+
+ } catch (error) {
+ console.error('加载合箱数据失败:', error)
+ this.$message.error('加载数据失败')
+ } finally {
+ this.mergeBoxLoading = false
+ }
+ },
+
+ /**
+ * 选择变化事件处理
+ */
+ handleMergeBoxSelectionChange(selection) {
+ this.mergeBoxSelection = selection
+ },
+
+ /**
+ * 合并选中的箱
+ */
+ doMergeBoxes() {
+ if (this.mergeBoxSelection.length < 2) {
+ this.$message.warning('请至少选择2个箱进行合并')
+ return
+ }
+
+ // 获取选中箱的所有行数据
+ const selectedBoxKeys = this.mergeBoxSelection.map(row => row._boxKey)
+ const rowsToMerge = this.mergeBoxTableData.filter(row =>
+ selectedBoxKeys.includes(row._boxKey)
+ )
+
+ // 保存合并前的状态(用于取消合并)
+ const mergeHistoryItem = {
+ timestamp: Date.now(),
+ boxes: []
+ }
+
+ // 记录每个被合并的箱的原始状态
+ selectedBoxKeys.forEach(boxKey => {
+ const boxRows = this.mergeBoxTableData.filter(row => row._boxKey === boxKey)
+ if (boxRows.length > 0) {
+ mergeHistoryItem.boxes.push({
+ boxKey: boxKey,
+ item_no: boxRows[0].item_no,
+ box_qty: boxRows[0].box_qty,
+ grossWeight: boxRows[0].grossWeight,
+ netWeight: boxRows[0].netWeight,
+ rolls: boxRows[0].rolls,
+ rowCount: boxRows.length,
+ details: boxRows.map(r => ({
+ _detailKey: r._detailKey,
+ itemNo: r.itemNo,
+ poNo: r.poNo,
+ pn: r.pn,
+ qty: r.qty,
+ detailRolls: r.detailRolls
+ }))
+ })
+ }
+ })
+
+ this.mergeBoxMergeHistory.push(mergeHistoryItem)
+
+ // 使用第一个箱的序号作为合并后的序号
+ const targetItemNo = this.mergeBoxSelection[0].item_no
+ const targetBoxKey = this.mergeBoxSelection[0]._boxKey
+
+ // 计算合并后的累加值
+ let totalBoxQty = 0
+ let totalGrossWeight = 0
+ let totalNetWeight = 0
+ let totalRolls = 0
+
+ // 按箱Key分组,计算每个箱的值
+ const boxMap = new Map()
+ rowsToMerge.forEach(row => {
+ if (!boxMap.has(row._boxKey)) {
+ boxMap.set(row._boxKey, {
+ box_qty: parseFloat(row.box_qty) || 0,
+ grossWeight: parseFloat(row.grossWeight) || 0,
+ netWeight: parseFloat(row.netWeight) || 0,
+ rolls: parseFloat(row.rolls) || 0
+ })
+ }
+ })
+
+ // 累加所有箱的值
+ boxMap.forEach(boxData => {
+ totalBoxQty += boxData.box_qty
+ totalGrossWeight += boxData.grossWeight
+ totalNetWeight += boxData.netWeight
+ })
+
+ // 累加所有明细的rolls
+ rowsToMerge.forEach(row => {
+ if (row.detailRolls && row.detailRolls > 0) {
+ totalRolls += parseFloat(row.detailRolls)
+ }
+ })
+
+ // 四舍五入取整(与装箱按钮保持一致)
+ const finalRolls = Math.round(totalRolls)
+
+ // 更新所有被合并的行
+ rowsToMerge.forEach((row, index) => {
+ row.item_no = targetItemNo
+ row._boxKey = targetBoxKey
+ row._isFirstRowOfBox = index === 0
+ row._rowSpan = index === 0 ? rowsToMerge.length : 0
+
+ // 所有行使用累加后的值
+ row.box_qty = totalBoxQty
+ row.grossWeight = parseFloat(totalGrossWeight.toFixed(3))
+ row.netWeight = parseFloat(totalNetWeight.toFixed(3))
+ row.rolls = finalRolls
+ })
+
+ // 合并后重新编号所有箱
+ this.renumberBoxes()
+
+ // 按序号重新排序表格数据
+ this.sortTableDataByItemNo()
+
+ // 清空选择
+ this.$refs.mergeBoxTable.clearSelection()
+ this.mergeBoxSelection = []
+
+ this.$message.success(`已合并 ${selectedBoxKeys.length} 个箱(箱数: ${totalBoxQty}, 毛重: ${totalGrossWeight.toFixed(3)}, 净重: ${totalNetWeight.toFixed(3)}),当前共 ${this.getUniqueBoxCount()} 个箱`)
+ },
+
+ /**
+ * 取消选中箱的合并
+ */
+ doUnmergeSelectedBoxes() {
+ if (this.mergeBoxSelection.length === 0) {
+ this.$message.warning('请先选择要取消合并的箱')
+ return
+ }
+
+ const selectedBoxKeys = this.mergeBoxSelection.map(row => row._boxKey)
+
+ // 找到选中箱的所有明细
+ const rowsToUnmerge = this.mergeBoxTableData.filter(row =>
+ selectedBoxKeys.includes(row._boxKey) && row._hasDetail
+ )
+
+ if (rowsToUnmerge.length === 0) {
+ this.$message.warning('所选箱没有明细数据')
+ return
+ }
+
+ // 获取原箱的序号,用于为新箱分配临时序号
+ const originalItemNo = rowsToUnmerge[0].item_no
+
+ // 为每条明细创建独立的箱,恢复成初始化时的原始数据
+ const newRows = []
+ rowsToUnmerge.forEach((row, index) => {
+ const newBoxKey = `box_${Date.now()}_${index}`
+
+ // 从mergeBoxOriginalData恢复该明细的原始数据
+ const detailOriginalData = this.mergeBoxOriginalData[row._detailKey]
+
+ let box_qty, grossWeight, netWeight, rolls, detailRolls
+
+ // 始终使用缓存的包装属性重新计算(确保恢复到初始值)
+ const rollQty = row.rollQtyCache || 0
+ const boxRolls = row.boxRollsCache || 0
+ const boxWeight = row.boxWeightCache || 0
+ const qty = row.qty || 0
+
+ // 恢复明细rolls
+ if (detailOriginalData && detailOriginalData.detailRolls !== undefined) {
+ detailRolls = detailOriginalData.detailRolls
+ } else if (rollQty > 0) {
+ detailRolls = parseFloat((qty / rollQty).toFixed(4))
+ } else {
+ detailRolls = 0
+ }
+
+ // 计算总rolls(初始时每箱只有一条明细)
+ rolls = Math.round(detailRolls)
+
+ // 计算箱数
+ if (rollQty > 0 && boxRolls > 0) {
+ const eaPerBox = rollQty * boxRolls
+ box_qty = Math.ceil(qty / eaPerBox)
+ } else {
+ box_qty = 1
+ }
+
+ // 计算毛重
+ if (boxWeight > 0) {
+ grossWeight = parseFloat((box_qty * boxWeight).toFixed(3))
+ } else {
+ grossWeight = 0
+ }
+
+ // 计算净重
+ netWeight = parseFloat((grossWeight - (box_qty / 2)).toFixed(3))
+
+ const newRow = {
+ ...row,
+ _boxKey: newBoxKey,
+ _isFirstRowOfBox: true,
+ _rowSpan: 1,
+ item_no: originalItemNo + index * 0.001, // 使用原序号加小数,保持顺序
+ box_qty: box_qty,
+ grossWeight: grossWeight,
+ netWeight: netWeight,
+ rolls: rolls,
+ detailRolls: detailRolls // 恢复明细rolls
+ }
+
+ newRows.push(newRow)
+
+ // 为新箱保存原始数据(用于后续编辑)
+ this.mergeBoxOriginalData[newBoxKey] = {
+ box_qty: box_qty,
+ grossWeight: grossWeight,
+ netWeight: netWeight,
+ rolls: rolls,
+ boxWeight: boxWeight
+ }
+ })
+
+ // 删除旧的行
+ selectedBoxKeys.forEach(boxKey => {
+ for (let i = this.mergeBoxTableData.length - 1; i >= 0; i--) {
+ if (this.mergeBoxTableData[i]._boxKey === boxKey) {
+ this.mergeBoxTableData.splice(i, 1)
+ }
+ }
+ })
+
+ // 添加新的行
+ this.mergeBoxTableData.push(...newRows)
+
+ // 重新编号
+ this.renumberBoxes()
+
+ // 按序号重新排序表格数据
+ this.sortTableDataByItemNo()
+
+ // 清空选择
+ this.$refs.mergeBoxTable.clearSelection()
+ this.mergeBoxSelection = []
+
+ this.$message.success(`已取消合并,拆分为 ${newRows.length} 个独立的箱,当前共 ${this.getUniqueBoxCount()} 个箱`)
+ },
+
+ /**
+ * 重新编号所有箱
+ */
+ renumberBoxes() {
+ // 获取所有唯一的箱,并记录每个箱在表格中第一次出现的位置
+ const boxKeysWithIndex = []
+ const seenBoxKeys = new Set()
+
+ this.mergeBoxTableData.forEach((row, index) => {
+ if (!seenBoxKeys.has(row._boxKey)) {
+ seenBoxKeys.add(row._boxKey)
+ boxKeysWithIndex.push({
+ boxKey: row._boxKey,
+ item_no: row.item_no,
+ firstIndex: index // 在表格中第一次出现的位置
+ })
+ }
+ })
+
+ // 按当前序号排序,如果序号相同则按在表格中出现的位置排序
+ const sortedBoxKeys = boxKeysWithIndex.sort((a, b) => {
+ if (a.item_no !== b.item_no) {
+ return a.item_no - b.item_no
+ }
+ // 序号相同时,按在表格中的位置排序
+ return a.firstIndex - b.firstIndex
+ })
+
+ // 重新分配序号
+ sortedBoxKeys.forEach((boxInfo, index) => {
+ const newItemNo = index + 1
+ this.mergeBoxTableData.forEach(row => {
+ if (row._boxKey === boxInfo.boxKey) {
+ row.item_no = newItemNo
+ }
+ })
+ })
+
+ // 更新下一个序号
+ this.nextItemNo = sortedBoxKeys.length + 1
+ },
+
+ /**
+ * 按序号对表格数据重新排序
+ */
+ sortTableDataByItemNo() {
+ this.mergeBoxTableData.sort((a, b) => {
+ // 先按序号排序
+ if (a.item_no !== b.item_no) {
+ return a.item_no - b.item_no
+ }
+ // 序号相同时,保持原有顺序(同一箱内的明细)
+ return 0
+ })
+ },
+
+ /**
+ * 获取唯一箱数量
+ */
+ getUniqueBoxCount() {
+ const boxKeys = new Set(this.mergeBoxTableData.map(row => row._boxKey))
+ return boxKeys.size
+ },
+
+ /**
+ * 表格行合并方法
+ */
+ mergeBoxSpanMethod({ row, column, rowIndex, columnIndex }) {
+ // Box信息列(前6列:复选框、序号、箱数、毛重、净重、总Rolls)需要合并
+ if (columnIndex < 6) {
+ if (row._isFirstRowOfBox) {
+ return {
+ rowspan: row._rowSpan,
+ colspan: 1
+ };
+ } else {
+ return {
+ rowspan: 0,
+ colspan: 0
+ };
+ }
+ }
+ return {
+ rowspan: 1,
+ colspan: 1
+ };
+ },
+
+ /**
+ * 获取行样式类名
+ */
+ getMergeBoxRowClassName({ row }) {
+ const classes = [];
+ // 检查该行是否有Box修改
+ if (this.mergeBoxModifiedBoxes[row._boxKey]) {
+ classes.push('box-modified-row');
+ }
+ // 检查该行是否有明细修改
+ if (row._hasDetail && this.mergeBoxModifiedDetails[row._detailKey]) {
+ classes.push('detail-modified-row');
+ }
+ return classes.join(' ');
+ },
+
+ /**
+ * 检查Box字段是否被修改
+ */
+ isBoxFieldModified(row, field) {
+ const boxKey = row._boxKey;
+ return this.mergeBoxModifiedBoxes[boxKey] &&
+ this.mergeBoxModifiedBoxes[boxKey][field] !== undefined;
+ },
+
+ /**
+ * 检查明细字段是否被修改
+ */
+ isDetailFieldModified(row, field) {
+ if (!row._hasDetail) return false;
+ const detailKey = row._detailKey;
+ return this.mergeBoxModifiedDetails[detailKey] &&
+ this.mergeBoxModifiedDetails[detailKey][field] !== undefined;
+ },
+
+ /**
+ * 箱数输入时联动计算毛重和净重
+ */
+ onMergeBoxQtyInput(row) {
+ if (this._isMergeCalculating) return;
+ this._isMergeCalculating = true;
+
+ try {
+ const boxKey = row._boxKey;
+ const newBoxQty = parseFloat(row.box_qty) || 0;
+
+ // 先从原始数据获取,如果没有则从row缓存获取
+ let boxWeight = 0;
+ const originalData = this.mergeBoxOriginalData[boxKey];
+ if (originalData && originalData.boxWeight) {
+ boxWeight = parseFloat(originalData.boxWeight);
+ } else if (row.boxWeightCache) {
+ boxWeight = parseFloat(row.boxWeightCache);
+ }
+
+ if (boxWeight > 0 && newBoxQty > 0) {
+ const newGrossWeight = boxWeight * newBoxQty;
+ const newNetWeight = newGrossWeight - (newBoxQty / 2);
+ row.grossWeight = parseFloat(newGrossWeight.toFixed(3));
+ row.netWeight = parseFloat(newNetWeight.toFixed(3));
+
+ // 同步更新所有同Box的行
+ this.mergeBoxTableData.forEach(r => {
+ if (r._boxKey === boxKey) {
+ r.box_qty = row.box_qty;
+ r.grossWeight = row.grossWeight;
+ r.netWeight = row.netWeight;
+ }
+ });
+
+ this.onMergeBoxFieldChange(row, 'grossWeight');
+ this.onMergeBoxFieldChange(row, 'netWeight');
+ }
+ } finally {
+ this._isMergeCalculating = false;
+ }
+ },
+
+ /**
+ * 毛重输入时联动计算净重
+ */
+ onMergeGrossWeightInput(row) {
+ if (this._isMergeCalculating) return;
+ this._isMergeCalculating = true;
+
+ try {
+ const grossWeight = parseFloat(row.grossWeight) || 0;
+ const boxQty = parseFloat(row.box_qty) || 0;
+ const netWeight = grossWeight - (boxQty / 2);
+ row.netWeight = netWeight.toFixed(2);
+
+ // 同步更新所有同Box的行
+ const boxKey = row._boxKey;
+ this.mergeBoxTableData.forEach(r => {
+ if (r._boxKey === boxKey) {
+ r.grossWeight = row.grossWeight;
+ r.netWeight = row.netWeight;
+ }
+ });
+
+ this.onMergeBoxFieldChange(row, 'netWeight');
+ } finally {
+ this._isMergeCalculating = false;
+ }
+ },
+
+ /**
+ * 净重输入时联动计算毛重
+ */
+ onMergeNetWeightInput(row) {
+ if (this._isMergeCalculating) return;
+ this._isMergeCalculating = true;
+
+ try {
+ const netWeight = parseFloat(row.netWeight) || 0;
+ const boxQty = parseFloat(row.box_qty) || 0;
+ const grossWeight = netWeight + (boxQty / 2);
+ row.grossWeight = grossWeight.toFixed(2);
+
+ // 同步更新所有同Box的行
+ const boxKey = row._boxKey;
+ this.mergeBoxTableData.forEach(r => {
+ if (r._boxKey === boxKey) {
+ r.netWeight = row.netWeight;
+ r.grossWeight = row.grossWeight;
+ }
+ });
+
+ this.onMergeBoxFieldChange(row, 'grossWeight');
+ } finally {
+ this._isMergeCalculating = false;
+ }
+ },
+
+ /**
+ * Box字段变化处理
+ */
+ onMergeBoxFieldChange(row, field) {
+ const boxKey = row._boxKey;
+ const original = this.mergeBoxOriginalData[boxKey];
+ if (!original) return;
+
+ const currentValue = String(row[field]);
+ const originalValue = String(original[field]);
+
+ if (currentValue !== originalValue) {
+ if (!this.mergeBoxModifiedBoxes[boxKey]) {
+ this.$set(this.mergeBoxModifiedBoxes, boxKey, {});
+ }
+ this.$set(this.mergeBoxModifiedBoxes[boxKey], field, {
+ oldValue: original[field],
+ newValue: row[field]
+ });
+
+ // 同步更新所有同Box的行
+ this.mergeBoxTableData.forEach(r => {
+ if (r._boxKey === boxKey) {
+ r[field] = row[field];
+ }
+ });
+ } else {
+ if (this.mergeBoxModifiedBoxes[boxKey]) {
+ this.$delete(this.mergeBoxModifiedBoxes[boxKey], field);
+ if (Object.keys(this.mergeBoxModifiedBoxes[boxKey]).length === 0) {
+ this.$delete(this.mergeBoxModifiedBoxes, boxKey);
+ }
+ }
+ }
+ },
+
+ /**
+ * 明细字段变化处理
+ */
+ onMergeDetailFieldChange(row, field) {
+ if (!row._hasDetail) return;
+
+ const detailKey = row._detailKey;
+ const original = this.mergeBoxOriginalData[detailKey];
+ if (!original) return;
+
+ const currentValue = String(row[field]);
+ const originalValue = String(original[field]);
+
+ if (currentValue !== originalValue) {
+ if (!this.mergeBoxModifiedDetails[detailKey]) {
+ this.$set(this.mergeBoxModifiedDetails, detailKey, {
+ row: row
+ });
+ }
+ this.$set(this.mergeBoxModifiedDetails[detailKey], field, {
+ oldValue: original[field],
+ newValue: row[field]
+ });
+ } else {
+ if (this.mergeBoxModifiedDetails[detailKey]) {
+ this.$delete(this.mergeBoxModifiedDetails[detailKey], field);
+ const remainingFields = Object.keys(this.mergeBoxModifiedDetails[detailKey])
+ .filter(k => k !== 'row');
+ if (remainingFields.length === 0) {
+ this.$delete(this.mergeBoxModifiedDetails, detailKey);
+ }
+ }
+ }
+ },
+
+ /**
+ * 明细Rolls输入事件处理 - 自动计算总Rolls
+ */
+ onDetailRollsInput(row) {
+ this.$nextTick(() => {
+ this.calculateBoxTotalRolls(row);
+ });
+ },
+
+ /**
+ * 计算同一Box下所有明细Rolls的总和
+ * 计算规则:总Rolls = Sum(所有明细rolls),然后四舍五入取整
+ */
+ calculateBoxTotalRolls(row) {
+ const boxKey = row._boxKey;
+ const boxRows = this.mergeBoxTableData.filter(r => r._boxKey === boxKey && r._hasDetail);
+
+ let totalDetailRolls = 0;
+ let hasDetailRolls = false;
+
+ boxRows.forEach(r => {
+ const rollsValue = parseFloat(r.detailRolls);
+ if (!isNaN(rollsValue) && rollsValue > 0) {
+ totalDetailRolls += rollsValue;
+ hasDetailRolls = true;
+ }
+ });
+
+ // 四舍五入取整(与装箱按钮保持一致)
+ const newRolls = hasDetailRolls ? Math.round(totalDetailRolls) : 0;
+
+ this.mergeBoxTableData.forEach(r => {
+ if (r._boxKey === boxKey) {
+ this.$set(r, 'rolls', newRolls);
+ }
+ });
+
+ this.onMergeBoxFieldChange(row, 'rolls');
+ },
+
+ /**
+ * 检查Box是否有明细Rolls值
+ */
+ hasDetailRolls(row) {
+ const boxKey = row._boxKey;
+ const boxRows = this.mergeBoxTableData.filter(r => r._boxKey === boxKey && r._hasDetail);
+
+ return boxRows.some(r => {
+ const rollsValue = parseFloat(r.detailRolls);
+ return !isNaN(rollsValue) && rollsValue > 0;
+ });
+ },
+
+ /**
+ * 明细数量变化时重新计算rolls
+ */
+ onDetailQtyChange(row) {
+ // 如果有缓存的每卷数量,重新计算rolls
+ if (row.rollQtyCache && row.rollQtyCache > 0 && row.qty > 0) {
+ const newRolls = parseFloat((row.qty / row.rollQtyCache).toFixed(4))
+ this.$set(row, 'detailRolls', newRolls)
+
+ // 更新总Rolls
+ this.$nextTick(() => {
+ this.calculateBoxTotalRolls(row)
+ })
+ }
+ },
+
+ /**
+ * 保存合箱批量编辑
+ */
+ async saveMergeBoxEdit() {
+ // 允许直接保存,即使没有修改
+ this.mergeBoxSaving = true
+
+ try {
+ // 构造提交数据 - 按箱分组
+ const boxMap = new Map()
+
+ // 遍历表格数据,按箱分组
+ this.mergeBoxTableData.forEach(row => {
+ const boxKey = row._boxKey
+ if (!boxMap.has(boxKey)) {
+ boxMap.set(boxKey, {
+ item_no: row.item_no,
+ box_qty: row.box_qty,
+ grossWeight: row.grossWeight,
+ netWeight: row.netWeight,
+ rolls: row.rolls,
+ details: []
+ })
+ }
+
+ // 添加明细
+ if (row._hasDetail) {
+ boxMap.get(boxKey).details.push({
+ itemNo: row.itemNo,
+ poNo: row.poNo,
+ pn: row.pn,
+ qty: row.qty,
+ detailRolls: row.detailRolls,
+ readyDate: row.readyDate
+ })
+ }
+ })
+
+ // 转换为数组
+ const boxList = Array.from(boxMap.values())
+
+ if (boxList.length === 0) {
+ this.$message.warning('没有可保存的箱数据')
+ this.mergeBoxSaving = false
+ return
+ }
+
+ const mergeData = {
+ site: this.currentRow.site,
+ buNo: this.currentRow.buNo,
+ delNo: this.currentRow.delNo,
+ createBy: this.$store.state.user.name,
+ boxList: boxList
+ }
+
+ // 调用合箱API
+ const response = await mergeBox(mergeData)
+
+ if (response.data && response.data.code === 0) {
+ this.$message.success(`成功保存 ${boxList.length} 个箱的数据`)
+ this.mergeBoxDialogVisible = false
+ // 刷新当前tab表格
+ this.refreshCurrentTabTable()
+ // 刷新上方的发货通知单列表
+ this.searchTable()
+ } else {
+ this.$alert(response.data.msg || '保存失败', '错误', {
+ confirmButtonText: '确定'
+ })
+ }
+ } catch (error) {
+ console.error('保存失败:', error)
+ this.$message.error('保存失败: ' + (error.message || '未知错误'))
+ } finally {
+ this.mergeBoxSaving = false
+ }
+ },
+
openPalletDialog () {
//请求
this.searchPalletList();
@@ -3419,4 +4466,109 @@
overflow-y: auto;
}
+/* ========== 合箱批量编辑弹出框样式 ========== */
+
+/* 修复表格滚动条右侧的颜色问题 - 强制设置gutter背景色为白色 */
+.el-dialog__wrapper .batch-edit-dialog .el-table th.gutter {
+ background-color: #f5f7fa !important;
+}
+
+/* 工具栏样式 */
+.merge-box-toolbar {
+ display: flex;
+ align-items: center;
+ margin-bottom: 15px;
+ padding: 10px;
+ background: #f5f7fa;
+ border-radius: 4px;
+}
+
+.merge-box-toolbar .merge-stats {
+ margin-left: auto;
+ display: flex;
+ align-items: center;
+}
+
+.merge-box-toolbar .modified-count {
+ color: #e6a23c;
+ margin-left: 10px;
+}
+
+.merge-box-toolbar .modified-count b {
+ color: #f56c6c;
+ font-size: 16px;
+}
+
+/* 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+6) {
+ background-color: #fdf6ec !important;
+}
+
+/deep/ .batch-edit-container .detail-modified-row td:nth-child(n+7) {
+ 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;
+}
+