|
|
<template> <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 :data="dataListBoxes" :height="height" border v-loading="dataListLoading" style="width: 100%;" :expand-row-keys="expandedBoxRows" :row-key="getBoxRowKey" @expand-change="handleBoxExpand" show-summary :summary-method="getSummaries"> <el-table-column type="expand"> <template slot-scope="props"> <!-- 加载状态 --> <div v-if="props.row.loading" class="expand-loading"> <i class="el-icon-loading"></i> <span>正在加载明细数据...</span> </div>
<!-- 明细内容区域 --> <div v-else class="expand-detail-container"> <!-- 明细表格 --> <el-table v-if="props.row.palletDetails && props.row.palletDetails.length > 0" :data="props.row.palletDetails" :max-height="300" size="mini" stripe class="expand-detail-table"> <el-table-column v-for="(item,index) in columnList3" :key="index" :sortable="item.columnSortable" :prop="item.columnProp" :header-align="item.headerAlign" :show-overflow-tooltip="item.showOverflowTooltip" :align="item.align" :min-width="item.columnWidth" :label="item.columnLabel"> <template slot-scope="scope"> <span v-if="!item.columnHidden"> {{ formatDetailValue(scope.row[item.columnProp], item.columnProp) }} </span> <span v-if="item.columnImage"> <img :src="scope.row[item.columnProp]" style="width: 100px; height: 80px"/> </span> </template> </el-table-column>
<!-- 明细操作列 --> <el-table-column label="操作" width="120" align="center" fixed="right" v-if="showActions"> <template slot-scope="scope"> <a type="text" size="small" @click="handleEditDetail(scope.row, props.row)">修改</a> <a type="text" size="small" @click="handleDeleteDetail(scope.row, props.row)">删除</a> </template> </el-table-column> </el-table>
<!-- 空数据状态 --> <div v-else class="expand-empty-state"> <i class="el-icon-box"></i> <p>暂无明细数据</p> <span>该栈板下暂时没有装箱明细信息</span> </div> </div> </template> </el-table-column> <el-table-column v-for="(item,index) in columnListBoxes" :key="index" :sortable="item.columnSortable" :prop="item.columnProp" :header-align="item.headerAlign" :show-overflow-tooltip="item.showOverflowTooltip" :align="item.align" :fixed="item.fixed==''?false:item.fixed" :min-width="item.columnWidth" :label="item.columnLabel"> <template slot-scope="scope"> <span v-if="!item.columnHidden"> {{ scope.row[item.columnProp] }}</span> <span v-if="item.columnImage"><img :src="scope.row[item.columnProp]" style="width: 100px; height: 80px"/></span> </template> </el-table-column>
<!-- 主表格操作列 --> <el-table-column label="操作" width="120" align="center" fixed="right" v-if="showActions"> <template slot-scope="scope"> <a type="text" size="small" @click="handleEditBox(scope.row)">修改</a> <a type="text" size="small" @click="handleDeleteBox(scope.row)">删除</a> </template> </el-table-column> </el-table>
<!-- 修改箱信息弹窗 --> <el-dialog title="修改箱信息" :visible.sync="editBoxDialogVisible" width="200px" :close-on-click-modal="false"> <el-form :model="editBoxForm" ref="editBoxForm" label-position="top" label-width="100px"> <el-row :gutter="20"> <el-col :span="24"> <el-form-item label="箱数" prop="box_qty"> <el-input v-model="editBoxForm.box_qty" @input="onBoxQtyChange" :min="1" :precision="0" style="width: 100%"></el-input> </el-form-item> </el-col> <el-col :span="24"> <el-form-item label="毛重" prop="grossWeight"> <el-input v-model="editBoxForm.grossWeight" @input="onGrossWeightChange" :min="0" :precision="2" style="width: 100%"></el-input> </el-form-item> </el-col> <el-col :span="24"> <el-form-item label="净重" prop="netWeight"> <el-input v-model="editBoxForm.netWeight" @input="onNetWeightChange" :min="0" :precision="2" style="width: 100%"></el-input> </el-form-item> </el-col> </el-row> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="editBoxDialogVisible = false">取消</el-button> <el-button type="primary" @click="submitEditBox" >确定</el-button> </div> </el-dialog>
<!-- 修改明细弹窗 --> <el-dialog title="修改明细信息" :visible.sync="editDetailDialogVisible" width="400px" :close-on-click-modal="false"> <el-form :model="editDetailForm" ref="editDetailForm" label-position="top" label-width="100px"> <el-row :gutter="20"> <el-col :span="12"> <el-form-item label="PO" prop="poNo"> <el-input v-model="editDetailForm.poNo" disabled></el-input> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="PN" prop="pn"> <el-input v-model="editDetailForm.pn" disabled></el-input> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="数量" prop="qty"> <el-input v-model="editDetailForm.qty" style="width: 100%"></el-input> </el-form-item> </el-col> <el-col :span="12"> <el-form-item label="Rolls" prop="rolls"> <el-input v-model="editDetailForm.rolls" style="width: 100%"></el-input> </el-form-item> </el-col> </el-row> </el-form> <div slot="footer" class="dialog-footer"> <el-button @click="editDetailDialogVisible = false">取消</el-button> <el-button type="primary" @click="submitEditDetail" >确定</el-button> </div> </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></template>
<script>import { selectBoxList, searchCoDelPalletDataNew, updateBoxInfo, deleteBoxInfo, updateDetailInfo, deleteDetailInfo, batchUpdatePackingInfo } from "@/api/ecss/ecss.js"
export default { name: "PackingDetailTab", props: { currentRow: { type: Object, default: () => ({}) }, height: { type: Number, default: 200 }, showActions: { type: Boolean, default: true } }, data() { return { dataListBoxes: [], // 箱数据列表
expandedBoxRows: [], // 展开的行
dataListLoading: false,
// 修改箱信息弹窗相关
editBoxDialogVisible: false, editBoxForm: { item_no: '', box_qty: 1, grossWeight: 0, netWeight: 0 }, editBoxSubmitting: false, currentEditBox: null,
// 修改明细弹窗相关
editDetailDialogVisible: false, editDetailForm: { itemNo: '', poNo: '', pn: '', qty: 1, rolls: 0 }, editDetailSubmitting: false, currentEditDetail: null, currentEditBoxForDetail: null,
// 批量编辑弹窗相关
batchEditDialogVisible: false, batchEditLoading: false, batchEditSaving: false, batchEditTableData: [], // 扁平化的表格数据
batchEditOriginalData: {}, // 原始数据快照(用于对比是否修改)
batchEditModifiedBoxes: {}, // 修改过的Box记录
batchEditModifiedDetails: {}, // 修改过的明细记录
batchEditSpanInfo: [], // 行合并信息
// 装箱明细列定义
columnList3: [ { userId: this.$store.state.user.name, functionId: 801002, serialNumber: '801002Table3ItemNo', tableId: "801002Table3", tableName: "装箱明细", columnProp: "itemNo", headerAlign: "center", align: "right", columnLabel: "序号", columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 40 }, { userId: this.$store.state.user.name, functionId: 801002, serialNumber: '801002Table3PoNo', tableId: "801002Table3", tableName: "装箱明细", columnProp: "poNo", headerAlign: "center", align: "left", columnLabel: "PO", columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 100 }, { userId: this.$store.state.user.name, functionId: 801002, serialNumber: '801002Table3PN', tableId: "801002Table3", tableName: "装箱明细", columnProp: "pn", headerAlign: "center", align: "left", columnLabel: "PN", columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 100 }, { userId: this.$store.state.user.name, functionId: 801002, serialNumber: '801002Table3Qty', tableId: "801002Table3", tableName: "装箱明细", columnProp: "qty", headerAlign: "center", align: "right", columnLabel: "数量", columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 50 }, { userId: this.$store.state.user.name, functionId: 801002, serialNumber: '801002Table3Rolls', tableId: "801002Table3", tableName: "装箱明细", columnProp: "rolls", headerAlign: "center", align: "right", columnLabel: "Rolls", columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 50 }, ], // 箱数据列定义
columnListBoxes: [ { userId: this.$store.state.user.name, functionId: 801002, serialNumber: '801002Table3ItemNo', tableId: "801002Table3", tableName: "装箱明细", columnProp: "item_no", headerAlign: "center", align: "right", columnLabel: "序号", columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 40 }, { userId: this.$store.state.user.name, functionId: 801002, serialNumber: '801002Table3BoxQty', tableId: "801002Table3", tableName: "装箱明细", columnProp: "box_qty", headerAlign: "center", align: "right", columnLabel: "箱数", columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 50 }, { userId: this.$store.state.user.name, functionId: 801002, serialNumber: '801002TableGrossWeight', tableId: "801002Table3", tableName: "装箱明细", columnProp: "grossWeight", headerAlign: "center", align: "right", columnLabel: "毛重", columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 50 }, { userId: this.$store.state.user.name, functionId: 801002, serialNumber: '801002TableNetWeight', tableId: "801002Table3", tableName: "装箱明细", columnProp: "netWeight", headerAlign: "center", align: "right", columnLabel: "净重", columnHidden: false, columnImage: false, columnSortable: false, sortLv: 0, status: true, fixed: '', columnWidth: 50 }, ] } }, computed: { // 批量编辑修改数量
batchEditModifiedCount() { return Object.keys(this.batchEditModifiedBoxes).length + Object.keys(this.batchEditModifiedDetails).length; } }, watch: { currentRow: { handler(newVal) { if (newVal && newVal.site) { this.loadBoxList(); } }, deep: true, immediate: true } }, methods: { // 装箱明细相关方法
loadBoxList() { if (!this.currentRow || !this.currentRow.site) { this.dataListBoxes = []; return; }
// 清理之前的展开状态
this.expandedBoxRows = []; this.dataListLoading = true;
// 获取ecss_CoDelBoxList数据
selectBoxList(this.currentRow).then(({data}) => { if (data && data.code == 0) { this.dataListBoxes = data.rows.map(row => ({ ...row, palletDetails: [], // 初始化明细数据为空
loading: false, // 初始化加载状态
hadDetails: false // 初始化是否有明细标记
})) } else { this.dataListBoxes = []; } }).catch(error => { console.error('加载箱数据失败:', error); this.dataListBoxes = []; }).finally(() => { this.dataListLoading = false; }); },
// 获取行的唯一标识
getBoxRowKey(row) { // 使用多个字段组合确保唯一性
return `${row.item_no || ''}_${row.palletRemark || ''}_${row.box_qty || ''}`; },
// 处理行展开事件
handleBoxExpand(row, expandedRows) { // 判断当前操作是展开还是收起
const isExpanding = expandedRows.includes(row);
if (isExpanding) { // 如果是展开操作,则展开所有行
this.expandAll(); } else { // 如果是收起操作,则收起所有行
this.collapseAll(); } },
// 加载栈板明细数据
loadPalletDetails(boxRow) { if (boxRow.palletDetails && boxRow.palletDetails.length > 0) { // 如果已经加载过,直接返回
return; }
// 设置加载状态
this.$set(boxRow, 'loading', true);
// 构造查询参数
let detailParams = { site: this.currentRow.site, buNo: this.currentRow.buNo, delNo: this.currentRow.delNo, seqNo: boxRow.item_no, palletRemark: boxRow.palletRemark };
// 调用API获取明细数据
searchCoDelPalletDataNew(detailParams).then(({data}) => { if (data && data.code == 0) { // 将明细数据赋值给当前行
this.$set(boxRow, 'palletDetails', data.rows);
// 如果明细为空,且之前有明细数据,说明明细被删除了,需要检查box是否也应该被删除
if (data.rows.length === 0 && boxRow.hadDetails) { // 延迟刷新列表,让后端有时间处理删除逻辑
setTimeout(() => { this.loadBoxList(); }, 500); }
// 标记是否有过明细数据
this.$set(boxRow, 'hadDetails', data.rows.length > 0); } else { this.$set(boxRow, 'palletDetails', []); this.$set(boxRow, 'hadDetails', false); } }).catch(error => { console.error('加载栈板明细数据失败:', error); this.$set(boxRow, 'palletDetails', []); this.$set(boxRow, 'hadDetails', false); }).finally(() => { // 清除加载状态
this.$set(boxRow, 'loading', false); }); },
// 格式化明细值显示
formatDetailValue(value, columnProp) { if (value === null || value === undefined || value === '') { return '-'; }
// 数字类型格式化
if (columnProp === 'qty' || columnProp === 'rolls') { return parseInt(value).toLocaleString(); }
return value; },
// 展开所有行
expandAll() { // 获取所有行的key
this.expandedBoxRows = this.dataListBoxes.map(row => this.getBoxRowKey(row));
// 为每个行加载明细数据
this.dataListBoxes.forEach(row => { this.loadPalletDetails(row); }); },
// 收起所有行
collapseAll() { this.expandedBoxRows = []; },
// 刷新数据的公共方法
refresh() { this.loadBoxList(); },
/* * 删除逻辑说明: * 1. 删除明细时: * - 先删除明细数据 * - 检查该明细所属的box是否还有其他明细 * - 如果没有明细了,自动删除对应的box * * 2. 删除box时: * - 先检查是否有明细数据 * - 如果有明细,提示用户并同时删除所有明细和box * - 如果没有明细,直接删除box */
// 处理修改箱信息
handleEditBox(boxRow) { this.currentEditBox = boxRow; this.editBoxForm.item_no = boxRow.item_no; this.editBoxForm.box_qty = boxRow.box_qty; this.editBoxForm.grossWeight = boxRow.grossWeight; this.editBoxForm.netWeight = boxRow.netWeight; this.editBoxDialogVisible = true; },
// 处理删除箱信息
handleDeleteBox(boxRow) { // 直接删除box及其所有明细,无需校验
this.deleteBoxWithDetails(boxRow); },
// 删除箱信息(包含明细)
deleteBoxWithDetails(boxRow) { this.$confirm(`确定要删除序号为 ${boxRow.item_no} 的箱信息吗?删除后无法恢复!`, '警告', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { // 构造删除参数
const deleteParams = { site: this.currentRow.site, buNo: this.currentRow.buNo, delNo: this.currentRow.delNo, item_no: boxRow.item_no, palletRemark: boxRow.palletRemark }; deleteBoxInfo(deleteParams).then(({data}) => { if (data && data.code === 0) { this.loadBoxList() this.$message({ message: '操作成功', type: 'success', duration: 1500, onClose: () => {} }) } else { this.$alert(data.msg, '错误', { confirmButtonText: '确定' }) } }) }); },
// 处理修改明细
handleEditDetail(detailRow, boxRow) { this.currentEditDetail = detailRow; this.currentEditBoxForDetail = boxRow; this.editDetailForm.itemNo = detailRow.itemNo; this.editDetailForm.poNo = detailRow.poNo; this.editDetailForm.pn = detailRow.pn; this.editDetailForm.qty = detailRow.qty; this.editDetailForm.rolls = detailRow.rolls; this.editDetailDialogVisible = true; },
/** * 箱数改变时自动计算净重 * 保持毛重不变,重新计算净重 * 计算公式:净重 = 毛重 - box_qty/2 */ onBoxQtyChange(value) { // 防止循环触发,使用标志位
if (this._isCalculating) { return; }
this._isCalculating = true;
try { const boxQty = parseFloat(value) || 0; const grossWeight = parseFloat(this.editBoxForm.grossWeight) || 0;
// 计算净重:净重 = 毛重 - box_qty/2
const netWeight = grossWeight - (boxQty / 2);
// 保留2位小数
this.editBoxForm.netWeight = netWeight.toFixed(2); } finally { this._isCalculating = false; } },
/** * 毛重改变时自动计算净重 * 计算公式:净重 = 毛重 - box_qty/2 */ onGrossWeightChange(value) { // 防止循环触发,使用标志位
if (this._isCalculating) { return; }
this._isCalculating = true;
try { const grossWeight = parseFloat(value) || 0; const boxQty = parseFloat(this.editBoxForm.box_qty) || 0;
// 计算净重:净重 = 毛重 - box_qty/2
const netWeight = grossWeight - (boxQty / 2);
// 保留2位小数
this.editBoxForm.netWeight = netWeight.toFixed(2); } finally { this._isCalculating = false; } },
/** * 净重改变时自动计算毛重 * 反向计算公式:毛重 = 净重 + box_qty/2 */ onNetWeightChange(value) { // 防止循环触发,使用标志位
if (this._isCalculating) { return; }
this._isCalculating = true;
try { const netWeight = parseFloat(value) || 0; const boxQty = parseFloat(this.editBoxForm.box_qty) || 0;
// 反向计算毛重:毛重 = 净重 + box_qty/2
const grossWeight = netWeight + (boxQty / 2);
// 保留2位小数
this.editBoxForm.grossWeight = grossWeight.toFixed(2); } finally { this._isCalculating = false; } },
// 提交修改箱信息
submitEditBox() { this.$refs.editBoxForm.validate(valid => { if (valid) { this.editBoxSubmitting = true;
// 构造更新参数
const updateParams = { site: this.currentRow.site, buNo: this.currentRow.buNo, delNo: this.currentRow.delNo, item_no: this.currentEditBox.item_no, palletRemark: this.currentEditBox.palletRemark, box_qty: this.editBoxForm.box_qty, grossWeight: this.editBoxForm.grossWeight, netWeight: this.editBoxForm.netWeight, updateBy: this.$store.state.user.name };
updateBoxInfo(updateParams).then(({ data }) => { if (data && data.code === 0) { this.editBoxDialogVisible = false; this.loadBoxList() this.$message({ message: '操作成功', type: 'success', duration: 1500, onClose: () => {} }) } else { this.$alert(data.msg, '错误', { confirmButtonText: '确定' }) } }); } }); },
// 提交修改明细
submitEditDetail() { this.$refs.editDetailForm.validate(valid => { if (valid) { this.editDetailSubmitting = true;
// 构造更新参数
const updateParams = { site: this.currentRow.site, buNo: this.currentRow.buNo, delNo: this.currentRow.delNo, seqNo: this.currentEditDetail.seqNo, itemNo: this.currentEditDetail.itemNo, notifyDetailItemNo: this.currentEditDetail.notifyDetailItemNo, poNo: this.editDetailForm.poNo, pn: this.editDetailForm.pn, qty: this.editDetailForm.qty, oldQty: this.currentEditDetail.qty, rolls: this.editDetailForm.rolls, updateBy: this.$store.state.user.name };
updateDetailInfo(updateParams).then(({ data }) => { if (data && data.code === 0) { this.editDetailDialogVisible = false; this.loadBoxList() this.$message({ message: '操作成功', type: 'success', duration: 1500, onClose: () => {} }) } else { this.$alert(data.msg, '错误', { confirmButtonText: '确定' }) } }); } }); },
// 删除明细
handleDeleteDetail(detailRow, boxRow) { this.$confirm(`确定要删除这个明细信息吗?删除后无法恢复!`, '警告', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' }).then(() => { // 构造删除参数
const deleteParams = { site: this.currentRow.site, buNo: this.currentRow.buNo, delNo: this.currentRow.delNo, seqNo: detailRow.seqNo, itemNo: detailRow.itemNo, notifyDetailItemNo: detailRow.notifyDetailItemNo, partNo: detailRow.part_no, qty: detailRow.qty }; deleteDetailInfo(deleteParams).then(({data}) => { if (data && data.code === 0) { this.loadBoxList() this.$message({ message: '操作成功', type: 'success', duration: 1500, onClose: () => {} }) } else { this.$alert(data.msg, '错误', { confirmButtonText: '确定' }) } }) }); },
// 计算合计行数据
getSummaries(param) { const { columns, data } = param; const sums = [];
columns.forEach((column, index) => { if (index === 0) { // 第一列(展开列)显示"合计"
sums[index] = '合计'; return; }
const values = data.map(item => Number(item[column.property]));
// 根据列属性计算合计
if (column.property === 'box_qty') { // 箱数合计
const sum = values.reduce((prev, curr) => { const value = Number(curr); if (!isNaN(value)) { return prev + value; } else { return prev; } }, 0); sums[index] = sum; } else if (column.property === 'grossWeight') { // 毛重合计
const sum = values.reduce((prev, curr) => { const value = Number(curr); if (!isNaN(value)) { return prev + value; } else { return prev; } }, 0); sums[index] = sum.toFixed(3); } else if (column.property === 'netWeight') { // 净重合计
const sum = values.reduce((prev, curr) => { const value = Number(curr); if (!isNaN(value)) { return prev + value; } else { return prev; } }, 0); sums[index] = sum.toFixed(3); } else if (column.property === 'item_no') { // 序号列显示空
sums[index] = ''; } else { // 其他列显示空
sums[index] = ''; } });
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 }) { // Box信息列(前4列:序号、箱数、毛重、净重)需要合并
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; } } }}</script>
<style scoped>.packing-detail-tab { width: 100%;}
/* 展开明细容器样式 */.expand-detail-container { background: #fafafa; border-radius: 6px; overflow: visible; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);}
/* 加载状态样式 */.expand-loading { text-align: center; padding: 30px; color: #666; background: #fafafa; margin: 8px 16px; border-radius: 6px;}
.expand-loading i { font-size: 18px; margin-right: 8px; color: #409eff;}
/* 明细表格样式 */.expand-detail-table { margin: 0; border: none;}
/deep/ .expand-detail-table .el-table__body-wrapper { overflow-y: auto;}
/deep/ .expand-detail-table .el-table__header { background: #f8f9fa;}
/deep/ .expand-detail-table .el-table__header th { background: #eef7f0; color: #333; font-weight: 500; border-bottom: 2px solid #e9ecef;}
/deep/ .expand-detail-table .el-table__row:hover > td { background-color: #f0f9ff;}
/deep/ .expand-detail-table .el-table__row.el-table__row--striped { background: #fbfcfd;}
/deep/ .expand-detail-table .el-table__row.el-table__row--striped:hover > td { background-color: #f0f9ff;}
/* 空数据状态样式 */.expand-empty-state { text-align: center; padding: 40px 20px; color: #999;}
.expand-empty-state i { font-size: 48px; color: #ddd; margin-bottom: 12px; display: block;}
.expand-empty-state p { font-size: 16px; margin: 0 0 8px 0; color: #666;}
.expand-empty-state span { font-size: 13px; color: #999;}
/* 响应式调整 */@media (max-width: 768px) { .expand-detail-container { margin: 8px 8px; padding: 6px; }}
/* 确保主表格容器的滚动行为 *//deep/ .el-table__expanded-cell { padding: 0 !important;}
/deep/ .el-table__expand-column .cell { padding: 0;}
/* 确保表格滚动条正常显示 *//deep/ .el-table .el-table__body-wrapper { overflow-y: auto;}
/deep/ .el-table__body-wrapper::-webkit-scrollbar { width: 6px;}
/deep/ .el-table__body-wrapper::-webkit-scrollbar-track { background: #f1f1f1; border-radius: 3px;}
/deep/ .el-table__body-wrapper::-webkit-scrollbar-thumb { background: #c1c1c1; border-radius: 3px;}
/deep/ .el-table__body-wrapper::-webkit-scrollbar-thumb:hover { 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>
|