|
|
<template> <div class="packing-detail-tab"> <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" :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" :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" :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> </div></template>
<script>import { selectBoxList, searchCoDelPalletDataNew, updateBoxInfo, deleteBoxInfo, updateDetailInfo, deleteDetailInfo } 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,
// 装箱明细列定义
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 }, ] } }, 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; },
// 提交修改箱信息
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; } }}</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;}</style>
|