feat(wms): 添加钢卷报表导出功能的多行合并选项
- 在异常报表页面添加导出产出钢卷(多行合并)和导出消耗钢卷(多行合并)菜单项 - 新增 mergeCoilCells 工具函数实现 Excel 单元格合并功能 - 实现 exportWithMerge 通用方法处理带合并的 Excel 导出 - 添加导出时的加载提示和错误处理机制 - 优化 MES 质检样本详情加载逻辑,移除不必要的异步请求
This commit is contained in:
@@ -344,9 +344,8 @@ export default {
|
||||
|
||||
loadMainDetails() {
|
||||
if (this.mainList.length === 0) return
|
||||
const promises = this.mainList.map(main => {
|
||||
return listInspectionDetail({ mainId: main.mainId, pageNum: 1, pageSize: 9999 }).then(response => {
|
||||
const details = response.rows || []
|
||||
this.mainList.forEach(main => {
|
||||
const details = main.detailList || []
|
||||
details.forEach(detail => {
|
||||
if (main._items[detail.itemName]) {
|
||||
main._items[detail.itemName] = {
|
||||
@@ -362,9 +361,7 @@ export default {
|
||||
}
|
||||
})
|
||||
main._originalItems = JSON.parse(JSON.stringify(main._items))
|
||||
}).catch(() => {})
|
||||
})
|
||||
return Promise.all(promises)
|
||||
},
|
||||
|
||||
handleAddMain() {
|
||||
|
||||
@@ -58,6 +58,8 @@
|
||||
|
||||
<el-dropdown-item command="exportDataMore">导出产出钢卷(多行)</el-dropdown-item>
|
||||
<el-dropdown-item command="exportLossDataMore">导出消耗钢卷(多行)</el-dropdown-item>
|
||||
<el-dropdown-item command="exportDataMerged" divided>导出产出钢卷(多行合并)</el-dropdown-item>
|
||||
<el-dropdown-item command="exportLossDataMerged">导出消耗钢卷(多行合并)</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</el-dropdown>
|
||||
</el-form-item>
|
||||
@@ -144,6 +146,11 @@ import CoilTable from "@/views/wms/report/components/coilTable";
|
||||
import ColumnsSetting from "@/views/wms/report/components/setting/columns";
|
||||
import TimeRangePicker from "@/views/wms/report/components/timeRangePicker.vue";
|
||||
import { saveReportFile } from "@/views/wms/report/js/reportFile";
|
||||
import { mergeCoilCells } from "@/views/wms/report/js/mergeExcelCells";
|
||||
import { saveAs } from 'file-saver'
|
||||
import request from '@/utils/request'
|
||||
import { tansParams } from "@/utils/klp";
|
||||
import { Loading } from 'element-ui'
|
||||
|
||||
export default {
|
||||
name: 'MergeTemplate',
|
||||
@@ -362,6 +369,54 @@ export default {
|
||||
abnormalExportCount: 0
|
||||
}, `materialCoil_${new Date().getTime()}.xlsx`)
|
||||
},
|
||||
// 导出产出钢卷(多行合并) — 二次合并钢卷列单元格
|
||||
async exportDataMerged() {
|
||||
if (this.outList.length === 0) {
|
||||
this.$message.warning('暂无数据可导出')
|
||||
return
|
||||
}
|
||||
await this.exportWithMerge('wms/materialCoil/exportAbnormal', {
|
||||
coilIds: this.outList.map(item => item.coilId).join(','),
|
||||
abnormalExportCount: 0
|
||||
}, `abnormalCoil_${new Date().getTime()}.xlsx`)
|
||||
},
|
||||
|
||||
// 导出消耗钢卷(多行合并) — 二次合并钢卷列单元格
|
||||
async exportLossDataMerged() {
|
||||
if (this.lossList.length === 0) {
|
||||
this.$message.warning('暂无数据可导出')
|
||||
return
|
||||
}
|
||||
await this.exportWithMerge('wms/materialCoil/export', {
|
||||
coilIds: this.lossList.map(item => item.coilId).join(','),
|
||||
abnormalExportCount: 0
|
||||
}, `materialCoil_${new Date().getTime()}.xlsx`)
|
||||
},
|
||||
|
||||
// 通用:导出多行 → ExcelJS 二次合并钢卷列 → 保存
|
||||
async exportWithMerge(url, params, filename) {
|
||||
const loading = Loading.service({
|
||||
text: '正在导出并合并单元格,请稍候',
|
||||
spinner: 'el-icon-loading',
|
||||
background: 'rgba(0, 0, 0, 0.7)'
|
||||
})
|
||||
try {
|
||||
const data = await request.post(url, params, {
|
||||
transformRequest: [(params) => { return tansParams(params) }],
|
||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
||||
responseType: 'blob'
|
||||
})
|
||||
// data 是拦截器返回的 res.data(Blob),与 download 函数一致
|
||||
const blob = new Blob([data])
|
||||
const mergedBlob = await mergeCoilCells(blob, 26)
|
||||
saveAs(mergedBlob, filename)
|
||||
} catch (e) {
|
||||
console.error('导出合并失败', e)
|
||||
this.$message.error('导出失败,请联系管理员')
|
||||
} finally {
|
||||
loading.close()
|
||||
}
|
||||
},
|
||||
// 处理命令
|
||||
handleCommand(command) {
|
||||
if (command === 'exportData') {
|
||||
@@ -376,6 +431,10 @@ export default {
|
||||
this.exportDataMore()
|
||||
} else if (command === 'exportLossDataMore') {
|
||||
this.exportLossDataMore()
|
||||
} else if (command === 'exportDataMerged') {
|
||||
this.exportDataMerged()
|
||||
} else if (command === 'exportLossDataMerged') {
|
||||
this.exportLossDataMerged()
|
||||
}
|
||||
},
|
||||
// 加载列设置
|
||||
|
||||
74
klp-ui/src/views/wms/report/js/mergeExcelCells.js
Normal file
74
klp-ui/src/views/wms/report/js/mergeExcelCells.js
Normal file
@@ -0,0 +1,74 @@
|
||||
import ExcelJS from 'exceljs'
|
||||
|
||||
/**
|
||||
* 对异常钢卷报表导出的 Excel 进行二次合并处理
|
||||
* 将钢卷信息列(前26列,A-Z)中连续相同值的单元格合并
|
||||
*
|
||||
* 原理:
|
||||
* 1. 用 row.values 数组代替逐格 getCell,避免 9万+ 次对象创建
|
||||
* 2. 合并单元格后用列级别样式统一居中,代替逐格设置
|
||||
*
|
||||
* @param {Blob} blob - 原始 Excel 文件 Blob
|
||||
* @param {number} mergeColumnCount - 需要合并的钢卷列数,默认 26(A-Z列)
|
||||
* @returns {Promise<Blob>} - 合并后的新 Blob
|
||||
*/
|
||||
export async function mergeCoilCells(blob, mergeColumnCount = 26) {
|
||||
const workbook = new ExcelJS.Workbook()
|
||||
const arrayBuffer = await blob.arrayBuffer()
|
||||
await workbook.xlsx.load(arrayBuffer)
|
||||
|
||||
const worksheet = workbook.worksheets[0]
|
||||
const rowCount = worksheet.rowCount
|
||||
|
||||
if (rowCount <= 1) return blob
|
||||
|
||||
// === 第一步:按钢卷身份标识分组 ===
|
||||
// 使用 row.values 数组(1-based),字符拼接一次性比较,避免逐格 getCell
|
||||
const groups = []
|
||||
let groupStart = 2
|
||||
let prevFingerprint = rowFingerprint(worksheet.getRow(2), mergeColumnCount)
|
||||
|
||||
for (let row = 3; row <= rowCount; row++) {
|
||||
const curFingerprint = rowFingerprint(worksheet.getRow(row), mergeColumnCount)
|
||||
if (curFingerprint !== prevFingerprint) {
|
||||
groups.push({ start: groupStart, end: row - 1 })
|
||||
groupStart = row
|
||||
prevFingerprint = curFingerprint
|
||||
}
|
||||
}
|
||||
groups.push({ start: groupStart, end: rowCount })
|
||||
|
||||
// === 第二步:合并单元格 ===
|
||||
for (const group of groups) {
|
||||
if (group.end > group.start) {
|
||||
for (let col = 1; col <= mergeColumnCount; col++) {
|
||||
worksheet.mergeCells(group.start, col, group.end, col)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// === 第三步:列级别统一居中(替换逐格设置,26 次 vs 9万次) ===
|
||||
for (let col = 1; col <= mergeColumnCount; col++) {
|
||||
const column = worksheet.getColumn(col)
|
||||
column.alignment = { vertical: 'middle', horizontal: 'center', wrapText: true }
|
||||
}
|
||||
|
||||
// === 第四步:写回 Blob ===
|
||||
const buffer = await workbook.xlsx.writeBuffer()
|
||||
return new Blob([buffer], {
|
||||
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 生成行的"指纹"字符串,用于快速比较两行钢卷列是否相同
|
||||
* row.values 是 ExcelJS 的 1-based 数组:values[1]=A列, values[2]=B列 ...
|
||||
*/
|
||||
function rowFingerprint(row, colCount) {
|
||||
const vals = row.values || []
|
||||
let fp = ''
|
||||
for (let i = 1; i <= colCount; i++) {
|
||||
fp += String(vals[i] ?? '') + '\x00'
|
||||
}
|
||||
return fp
|
||||
}
|
||||
Reference in New Issue
Block a user