From 3e36d411768a92c4f83be5c8b6dbaeed31ecd10b Mon Sep 17 00:00:00 2001 From: Joshi <3040996759@qq.com> Date: Wed, 8 Jul 2026 10:56:50 +0800 Subject: [PATCH] =?UTF-8?q?feat(wms):=20=E6=B7=BB=E5=8A=A0=E9=92=A2?= =?UTF-8?q?=E5=8D=B7=E6=8A=A5=E8=A1=A8=E5=AF=BC=E5=87=BA=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E7=9A=84=E5=A4=9A=E8=A1=8C=E5=90=88=E5=B9=B6=E9=80=89=E9=A1=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在异常报表页面添加导出产出钢卷(多行合并)和导出消耗钢卷(多行合并)菜单项 - 新增 mergeCoilCells 工具函数实现 Excel 单元格合并功能 - 实现 exportWithMerge 通用方法处理带合并的 Excel 导出 - 添加导出时的加载提示和错误处理机制 - 优化 MES 质检样本详情加载逻辑,移除不必要的异步请求 --- .../views/mes/qc/inspectionSample/index.vue | 35 ++++----- klp-ui/src/views/wms/report/abnormal.vue | 59 +++++++++++++++ .../views/wms/report/js/mergeExcelCells.js | 74 +++++++++++++++++++ 3 files changed, 149 insertions(+), 19 deletions(-) create mode 100644 klp-ui/src/views/wms/report/js/mergeExcelCells.js diff --git a/klp-ui/src/views/mes/qc/inspectionSample/index.vue b/klp-ui/src/views/mes/qc/inspectionSample/index.vue index 84b709aed..d422f538e 100644 --- a/klp-ui/src/views/mes/qc/inspectionSample/index.vue +++ b/klp-ui/src/views/mes/qc/inspectionSample/index.vue @@ -344,27 +344,24 @@ 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 || [] - details.forEach(detail => { - if (main._items[detail.itemName]) { - main._items[detail.itemName] = { - detailId: detail.detailId, - mainId: detail.mainId, - itemName: detail.itemName, - itemValue: detail.itemValue || '', - itemUnit: detail.itemUnit || main._items[detail.itemName].itemUnit, - upperLimit: detail.upperLimit, - lowerLimit: detail.lowerLimit, - rangeDesc: detail.rangeDesc - } + this.mainList.forEach(main => { + const details = main.detailList || [] + details.forEach(detail => { + if (main._items[detail.itemName]) { + main._items[detail.itemName] = { + detailId: detail.detailId, + mainId: detail.mainId, + itemName: detail.itemName, + itemValue: detail.itemValue || '', + itemUnit: detail.itemUnit || main._items[detail.itemName].itemUnit, + upperLimit: detail.upperLimit, + lowerLimit: detail.lowerLimit, + rangeDesc: detail.rangeDesc } - }) - main._originalItems = JSON.parse(JSON.stringify(main._items)) - }).catch(() => {}) + } + }) + main._originalItems = JSON.parse(JSON.stringify(main._items)) }) - return Promise.all(promises) }, handleAddMain() { diff --git a/klp-ui/src/views/wms/report/abnormal.vue b/klp-ui/src/views/wms/report/abnormal.vue index 9e62b3b1b..66cdf9d96 100644 --- a/klp-ui/src/views/wms/report/abnormal.vue +++ b/klp-ui/src/views/wms/report/abnormal.vue @@ -58,6 +58,8 @@ 导出产出钢卷(多行) 导出消耗钢卷(多行) + 导出产出钢卷(多行合并) + 导出消耗钢卷(多行合并) @@ -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() } }, // 加载列设置 diff --git a/klp-ui/src/views/wms/report/js/mergeExcelCells.js b/klp-ui/src/views/wms/report/js/mergeExcelCells.js new file mode 100644 index 000000000..3b6e80994 --- /dev/null +++ b/klp-ui/src/views/wms/report/js/mergeExcelCells.js @@ -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 + */ +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 +}