Browse Source

feat(physicalInventory): 添加盘点数据导出功能

- 在API中新增exportCountDataExcel方法用于后端导出Excel
- 在搜索盘点页面添加导出明细按钮和前端导出功能
- 实现前端使用xlsx库生成多sheet Excel文件的导出逻辑
- 添加标签明细、物料汇总、栈板明细、盘点结果等多个工作表
- 集成后端API导出方式作为备选方案
- 添加导出加载状态和错误处理机制
master
常熟吴彦祖 2 months ago
parent
commit
96fac698de
  1. 5
      src/api/check/physicalInventory.js
  2. 201
      src/views/modules/check/searchPhysicalInventory.vue

5
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) 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')

201
src/views/modules/check/searchPhysicalInventory.vue

@ -79,14 +79,15 @@
<span v-else>{{ scope.row[item.columnProp] }}</span> <span v-else>{{ scope.row[item.columnProp] }}</span>
</template> </template>
</el-table-column> </el-table-column>
<el-table-column header-align="center" align="center" fixed="right" width="280" label="操作">
<el-table-column header-align="center" align="center" fixed="right" width="240" label="操作">
<template slot-scope="scope"> <template slot-scope="scope">
<a type="text" style="margin-right: 10px;" @click.stop="handleAddMaterial(scope.row)" v-if="scope.row.status === 'DRAFT' && scope.row.countType === 'MANUAL'">添加物料</a> <a type="text" style="margin-right: 10px;" @click.stop="handleAddMaterial(scope.row)" v-if="scope.row.status === 'DRAFT' && scope.row.countType === 'MANUAL'">添加物料</a>
<a type="text" style="margin-right: 10px;" @click.stop="handleRelease(scope.row)" v-if="scope.row.status === 'DRAFT'">下达</a> <a type="text" style="margin-right: 10px;" @click.stop="handleRelease(scope.row)" v-if="scope.row.status === 'DRAFT'">下达</a>
<a type="text" style="margin-right: 10px;" @click.stop="handlePush(scope.row)" v-if="scope.row.status === 'RELEASED'">推送</a> <a type="text" style="margin-right: 10px;" @click.stop="handlePush(scope.row)" v-if="scope.row.status === 'RELEASED'">推送</a>
<a type="text" style="margin-right: 10px;" @click.stop="handleComplete(scope.row)" v-if=" scope.row.status === 'CHECKING'">完成</a> <a type="text" style="margin-right: 10px;" @click.stop="handleComplete(scope.row)" v-if=" scope.row.status === 'CHECKING'">完成</a>
<a type="text" style="margin-right: 10px;" @click.stop="handleCancel(scope.row)" v-if="scope.row.status !== 'COMPLETED' && scope.row.status !== 'CANCELLED'">取消</a> <a type="text" style="margin-right: 10px;" @click.stop="handleCancel(scope.row)" v-if="scope.row.status !== 'COMPLETED' && scope.row.status !== 'CANCELLED'">取消</a>
<a type="text" style="color: #F56C6C;" @click.stop="handleDelete(scope.row)" v-if="scope.row.status === 'DRAFT'">删除</a>
<a type="text" style="color: #F56C6C; margin-right: 10px;" @click.stop="handleDelete(scope.row)" v-if="scope.row.status === 'DRAFT'">删除</a>
<a type="text" style="color: #67C23A; margin-right: 10px;" @click.stop="handleExportFrontend(scope.row)">导出明细</a>
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
@ -410,7 +411,8 @@ import { searchCountHeaderList, createCycleCount, createManualCount, queryMateri
releaseCount, pushCountToWcs, completeCount, cancelCount, deleteCount, searchCountLabelList, releaseCount, pushCountToWcs, completeCount, cancelCount, deleteCount, searchCountLabelList,
searchCountPalletList, searchCountResultList, searchMaterialSummary, searchOrderTaskByCountNo, searchCountPalletList, searchCountResultList, searchMaterialSummary, searchOrderTaskByCountNo,
searchOrderTaskDetail, getSysIfCount, updateSysIfCount, queryAdjustmentTransList, 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 { export default {
name: 'searchPhysicalInventory', name: 'searchPhysicalInventory',
@ -616,7 +618,10 @@ export default {
adjustmentTransLoading: false, adjustmentTransLoading: false,
adjustmentTransSubList: [], // adjustmentTransSubList: [], //
adjustmentTransSubLoading: false, adjustmentTransSubLoading: false,
currentAdjustmentTrans: null //
currentAdjustmentTrans: null, //
// - rqrq
exportLoading: false
} }
}, },
mounted() { mounted() {
@ -1350,6 +1355,194 @@ export default {
'QTY_DIFF': '#F56C6C' 'QTY_DIFF': '#F56C6C'
} }
return colorMap[result] || '#909399' 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
})
} }
} }
} }

Loading…
Cancel
Save