From 96fac698de514711087b7533dd7e05a882aad761 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=B8=B8=E7=86=9F=E5=90=B4=E5=BD=A6=E7=A5=96?= Date: Wed, 31 Dec 2025 11:13:28 +0800 Subject: [PATCH] =?UTF-8?q?feat(physicalInventory):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E7=9B=98=E7=82=B9=E6=95=B0=E6=8D=AE=E5=AF=BC=E5=87=BA=E5=8A=9F?= =?UTF-8?q?=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在API中新增exportCountDataExcel方法用于后端导出Excel - 在搜索盘点页面添加导出明细按钮和前端导出功能 - 实现前端使用xlsx库生成多sheet Excel文件的导出逻辑 - 添加标签明细、物料汇总、栈板明细、盘点结果等多个工作表 - 集成后端API导出方式作为备选方案 - 添加导出加载状态和错误处理机制 --- src/api/check/physicalInventory.js | 5 + .../modules/check/searchPhysicalInventory.vue | 201 +++++++++++++++++- 2 files changed, 202 insertions(+), 4 deletions(-) diff --git a/src/api/check/physicalInventory.js b/src/api/check/physicalInventory.js index 5a0886d..572212e 100644 --- a/src/api/check/physicalInventory.js +++ b/src/api/check/physicalInventory.js @@ -124,3 +124,8 @@ export const queryAdjustmentTransSubList = data => createAPI(`/check/physicalInv export const selectNowUnCountPercent = data => createAPI(`/check/physicalInventory/selectNowUnCountPercent`, 'post', data) + +// ==================== 导出功能 ==================== - rqrq + +// 后端导出盘点数据到Excel(多sheet)- rqrq +export const exportCountDataExcel = data => createAPI(`/check/physicalInventory/exportCountDataExcel`, 'post', data, 'download') \ No newline at end of file diff --git a/src/views/modules/check/searchPhysicalInventory.vue b/src/views/modules/check/searchPhysicalInventory.vue index 938a052..0a37649 100644 --- a/src/views/modules/check/searchPhysicalInventory.vue +++ b/src/views/modules/check/searchPhysicalInventory.vue @@ -79,14 +79,15 @@ {{ scope.row[item.columnProp] }} - + @@ -410,7 +411,8 @@ import { searchCountHeaderList, createCycleCount, createManualCount, queryMateri releaseCount, pushCountToWcs, completeCount, cancelCount, deleteCount, searchCountLabelList, searchCountPalletList, searchCountResultList, searchMaterialSummary, searchOrderTaskByCountNo, searchOrderTaskDetail, getSysIfCount, updateSysIfCount, queryAdjustmentTransList, - queryAdjustmentTransSubList,selectNowUnCountPercent } from '@/api/check/physicalInventory' + queryAdjustmentTransSubList, selectNowUnCountPercent, exportCountDataExcel } from '@/api/check/physicalInventory' +import * as XLSX from 'xlsx' // 前端Excel导出 - rqrq export default { name: 'searchPhysicalInventory', @@ -616,7 +618,10 @@ export default { adjustmentTransLoading: false, adjustmentTransSubList: [], // 标签明细列表 adjustmentTransSubLoading: false, - currentAdjustmentTrans: null // 当前选中的事务 + currentAdjustmentTrans: null, // 当前选中的事务 + + // 导出相关 - rqrq + exportLoading: false } }, mounted() { @@ -1350,6 +1355,194 @@ export default { 'QTY_DIFF': '#F56C6C' } return colorMap[result] || '#909399' + }, + + // ==================== 导出功能 ==================== - rqrq + + /** + * 前端导出Excel(方式一:使用xlsx库在前端生成)- rqrq + * @param row 当前行数据 + */ + async handleExportFrontend(row) { + if (this.exportLoading) return + this.exportLoading = true + this.$message.success('正在导出数据,请稍候...') + + try { + const site = this.$store.state.user.site + const countNo = row.countNo + const params = { site, countNo } + + // 并行获取所有数据 - rqrq + const [labelRes, summaryRes, palletRes, resultRes, adjustmentRes] = await Promise.all([ + searchCountLabelList(params), + searchMaterialSummary(params), + searchCountPalletList(params), + searchCountResultList(params), + queryAdjustmentTransList(params) + ]) + + const labelList = (labelRes.data && labelRes.data.code === 0) ? labelRes.data.rows || [] : [] + const summaryList = (summaryRes.data && summaryRes.data.code === 0) ? summaryRes.data.rows || [] : [] + const palletList = (palletRes.data && palletRes.data.code === 0) ? palletRes.data.rows || [] : [] + const resultList = (resultRes.data && resultRes.data.code === 0) ? resultRes.data.rows || [] : [] + const resultDiffList = resultList.filter(item => item.countResult !== 'OK') + const adjustmentList = (adjustmentRes.data && adjustmentRes.data.code === 0) ? adjustmentRes.data.rows || [] : [] + + // 创建工作簿 - rqrq + const wb = XLSX.utils.book_new() + + // Sheet1: 标签明细 - rqrq + const labelData = labelList.map(item => ({ + '标签号': item.unitId, + '物料号': item.partNo, + '物料描述': item.partDesc, + '数量': item.qty, + '批号': item.batchNo, + '栈板号': item.palletId, + '所在层数': item.locationZ, + '标签类型': item.labelTypeDesc, + '盘点状态': item.countFlagDesc, + '盘点人': item.countUser, + '盘点时间': this.formatDateTime(item.countDate), + '仓库': item.warehouseName, + '库位': item.locationName + })) + const ws1 = XLSX.utils.json_to_sheet(labelData) + XLSX.utils.book_append_sheet(wb, ws1, '标签明细') + + // Sheet2: 物料汇总 - rqrq + const summaryData = summaryList.map(item => ({ + '物料号': item.partNo, + '物料描述': item.partDesc, + '批号': item.batchNo, + 'WDR号': item.wdr, + '仓库': item.warehouseName, + '库位': item.locationName, + '标签数': item.labelCount, + '总数量': item.totalQty, + '栈板数': item.palletCount, + '已盘点': item.checkedLabelCount, + '进度': item.progressPercent + })) + const ws2 = XLSX.utils.json_to_sheet(summaryData) + XLSX.utils.book_append_sheet(wb, ws2, '物料汇总') + + // Sheet3: 栈板明细 - rqrq + const palletData = palletList.map(item => ({ + '序号': item.seqNo, + '栈板号': item.palletId, + '标签数': item.labelCount, + '已盘点': item.checkedCount, + '进度': item.progressPercent, + '盘点状态': item.countFlagDesc, + '站点区域': item.stationArea, + '站点ID': item.stationId, + '所在层数': item.locationZ, + '盘点人': item.countUser, + '盘点时间': this.formatDateTime(item.countDate) + })) + const ws3 = XLSX.utils.json_to_sheet(palletData) + XLSX.utils.book_append_sheet(wb, ws3, '栈板明细') + + // Sheet4: 盘点结果 - rqrq + const resultData = resultList.map(item => ({ + '标签号': item.unitId, + '物料号': item.partNo, + '物料描述': item.partDesc, + '数量': item.qty, + '差异数量': item.diffQty, + '批号': item.batchNo, + '栈板号': item.palletId, + '盘点结果': item.countResultDesc, + '处理方式': item.handleTypeDesc, + '盘点人': item.countUser, + '盘点时间': this.formatDateTime(item.countDate), + '备注': item.remark + })) + const ws4 = XLSX.utils.json_to_sheet(resultData) + XLSX.utils.book_append_sheet(wb, ws4, '盘点结果') + + // Sheet5: 盘点结果差异 - rqrq + const resultDiffData = resultDiffList.map(item => ({ + '标签号': item.unitId, + '物料号': item.partNo, + '物料描述': item.partDesc, + '数量': item.qty, + '差异数量': item.diffQty, + '批号': item.batchNo, + '栈板号': item.palletId, + '盘点结果': item.countResultDesc, + '是否处理': item.handleFlagDesc, + '处理方式': item.handleTypeDesc, + '盘点人': item.countUser, + '盘点时间': this.formatDateTime(item.countDate), + '备注': item.remark + })) + const ws5 = XLSX.utils.json_to_sheet(resultDiffData) + XLSX.utils.book_append_sheet(wb, ws5, '盘点结果差异') + + // Sheet6: 盘盈盘亏明细 - rqrq + const adjustmentData = adjustmentList.map(item => ({ + '单据号': item.transNo, + '事务类型': item.transTypeDesc, + '行号': item.itemNo, + '物料号': item.partNo, + '数量': item.transQty, + '批号': item.batchNo, + '库位': item.locationId, + '方向': item.directionDesc, + '仓库': item.warehouseId, + '操作人': item.userName + })) + const ws6 = XLSX.utils.json_to_sheet(adjustmentData) + XLSX.utils.book_append_sheet(wb, ws6, '盘盈盘亏明细') + + // 导出文件 - rqrq + const fileName = `盘点数据_${countNo}_${this.dayjs().format('YYYYMMDDHHmmss')}.xlsx` + XLSX.writeFile(wb, fileName) + this.$message.success('导出成功') + } catch (error) { + console.error('导出失败:', error) + this.$alert('导出失败:' + (error.message || error), '错误', { confirmButtonText: '确定' }) + } finally { + this.exportLoading = false + } + }, + + /** + * 后端导出Excel(方式二:后端生成Excel文件下载)- rqrq + * @param row 当前行数据 + */ + handleExportBackend(row) { + if (this.exportLoading) return + this.exportLoading = true + this.$message.success('正在导出数据,请稍候...') + + const params = { + site: this.$store.state.user.site, + countNo: row.countNo + } + + exportCountDataExcel(params).then(response => { + // 从响应中获取blob数据 - rqrq + const blob = new Blob([response.data], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }) + const fileName = `盘点数据_${row.countNo}_${this.dayjs().format('YYYYMMDDHHmmss')}.xlsx` + + // 创建下载链接 - rqrq + const link = document.createElement('a') + link.href = window.URL.createObjectURL(blob) + link.download = fileName + link.click() + window.URL.revokeObjectURL(link.href) + + this.$message.success('导出成功') + }).catch(error => { + console.error('导出失败:', error) + this.$alert('导出失败:' + (error.message || error), '错误', { confirmButtonText: '确定' }) + }).finally(() => { + this.exportLoading = false + }) } } }