UNPKG

ng-cw-v12

Version:

Angular UI component library

282 lines (277 loc) 12.3 kB
import * as i0 from '@angular/core'; import { Injectable, NgModule } from '@angular/core'; import * as XLSX from 'xlsx-js-style'; class NcExcelService { /** * 导出Excel,表头支持多级和合并 * @param headers 表头配置数组,支持多级和合并单元格 * @param data 要导出的数据数组,每个元素为一个对象,对象的属性需要与表头的key对应 * @param filename 导出的文件名(不需要包含.xlsx后缀) * @param maxWordCount 单元格最大字数,超过此数量将自动换行显示,默认值:10 * @returns 直接触发文件下载,不返回值 */ exportExcel(headers, data, filename, maxWordCount = 10) { // 创建工作簿和工作表 const wb = XLSX.utils.book_new(); const ws = XLSX.utils.json_to_sheet([]); // 处理表头 const headerRows = this.processHeaders(headers); XLSX.utils.sheet_add_aoa(ws, headerRows, { origin: 'A1' }); // 获取所有有效的键 const validKeys = this.getValidKeys(headers); // 过滤并转换数据 const filteredData = data.map(item => this.filterData(item, validKeys)); // 添加过滤后的数据 XLSX.utils.sheet_add_json(ws, filteredData, { origin: { r: headerRows.length, c: 0 }, skipHeader: true }); // 设置合并单元格 ws['!merges'] = this.getMerges(headers); // 设置单元格样式(居中对齐) this.setCellStyles(ws, headerRows.length, maxWordCount); // 将工作表添加到工作簿 XLSX.utils.book_append_sheet(wb, ws, 'Sheet1'); // 导出Excel文件 XLSX.writeFile(wb, `${filename}.xlsx`); } processHeaders(headers) { const result = []; let maxDepth = this.getMaxDepth(headers); for (let i = 0; i < maxDepth; i++) { result.push([]); } const fillHeaders = (headers, depth = 0, colIndex = 0) => { headers.forEach((header) => { const colspan = header.colspan || 1; const rowspan = header.rowspan || 1; // 填充当前单元格 result[depth][colIndex] = header.label; // 如果有子项,递归处理 if (header.children) { fillHeaders(header.children, depth + 1, colIndex); } else { // 如果没有子项,填充空白单元格 for (let i = depth + 1; i < maxDepth; i++) { result[i][colIndex] = ''; } } // 处理跨列 for (let i = 1; i < colspan; i++) { result[depth][colIndex + i] = ''; } // 处理跨行 for (let i = 1; i < rowspan; i++) { if (!result[depth + i]) { result[depth + i] = []; } result[depth + i][colIndex] = ''; } colIndex += colspan; }); }; fillHeaders(headers); return result; } getMaxDepth(headers, depth = 1) { let maxDepth = depth; headers.forEach((header) => { if (header.children) { const childDepth = this.getMaxDepth(header.children, depth + 1); maxDepth = Math.max(maxDepth, childDepth); } }); return maxDepth; } getValidKeys(headers) { const keys = []; const extractKeys = (headers) => { headers.forEach((header) => { if (header.children) { extractKeys(header.children); } else { if (header.key) { keys.push(header.key); } } }); }; extractKeys(headers); return keys; } filterData(item, validKeys) { const filteredItem = {}; validKeys.forEach(key => { var _a; filteredItem[key] = (_a = item[key]) !== null && _a !== void 0 ? _a : ''; }); return filteredItem; } getMerges(headers) { const merges = []; let colIndex = 0; const processMerges = (headers, rowIndex = 0) => { headers.forEach((header) => { const colspan = header.colspan || 1; const rowspan = header.rowspan || 1; if (colspan > 1 || rowspan > 1) { merges.push({ s: { r: rowIndex, c: colIndex }, e: { r: rowIndex + rowspan - 1, c: colIndex + colspan - 1 } }); } if (header.children) { processMerges(header.children, rowIndex + 1); } else { colIndex += colspan; } }); }; processMerges(headers); return merges; } setCellStyles(ws, headerRows, maxWordCount) { if (!ws['!cols']) ws['!cols'] = []; if (!ws['!rows']) ws['!rows'] = []; const range = XLSX.utils.decode_range(ws['!ref'] || 'A1:Z1000'); // 列宽 const maxLengths = this.getMaxLengths(ws, range); //每列的最大字数,例:[10,3,4,5,6,15] for (let i = 0; i <= range.e.c; i++) { ws['!cols'][i] = { wch: Math.max(8, Math.min(this.mathMultiply(maxWordCount, 2.829), this.mathMultiply(maxLengths[i], 2.829))) }; //字体大小为13px时,字数为11,会在28.43宽度时换行,测试时excel会在设置的宽度上加0.14,所以设置单个字宽度为2.829 } //单元格样式和行高 for (let R = range.s.r; R <= range.e.r; R++) { let maxLines = 1; //当前行内单元格最大文字行数 const isHeader = R < headerRows; const isEvenRow = R % 2 === 0; for (let C = range.s.c; C <= range.e.c; C++) { const cellAddress = XLSX.utils.encode_cell({ r: R, c: C }); if (!ws[cellAddress]) continue; if (!ws[cellAddress].s) ws[cellAddress].s = {}; // 计算单元格内容的行数 const cellValue = String(ws[cellAddress].v || ''); const cellWidth = ws['!cols'][C].wch || 8; const totalWidth = this.mathMultiply(this.getAdjustedLength(cellValue), 2.829); const lines = Math.ceil(this.mathDivide(totalWidth, cellWidth)); maxLines = Math.max(maxLines, lines); ws[cellAddress].s = { alignment: { vertical: 'center', horizontal: 'center', wrapText: true }, font: { name: '仿宋', sz: 13, bold: isHeader, color: { rgb: isHeader ? 'FFFFFF' : '000000' } }, fill: { fgColor: { rgb: isHeader ? "538dd5" : (isEvenRow ? "F2F2F2" : "FFFFFF") } }, border: { top: { style: 'thin', color: { rgb: isHeader ? 'e0e0e0' : 'a9a9a9' } }, bottom: { style: 'thin', color: { rgb: isHeader ? 'e0e0e0' : 'a9a9a9' } }, left: { style: 'thin', color: { rgb: isHeader ? 'e0e0e0' : 'a9a9a9' } }, right: { style: 'thin', color: { rgb: isHeader ? 'e0e0e0' : 'a9a9a9' } } } }; } // 设置行高 const baseHeight = isHeader ? 30 : 25; const lineHeight = isHeader ? 20 : 18; ws['!rows'][R] = { hpt: Math.max(baseHeight, lineHeight * maxLines) }; } } getMaxLengths(ws, range) { const maxLengths = []; for (let C = range.s.c; C <= range.e.c; C++) { let maxLength = 0; for (let R = range.s.r; R <= range.e.r; R++) { const cellAddress = XLSX.utils.encode_cell({ r: R, c: C }); if (ws[cellAddress] && ws[cellAddress].v) { const cellValue = String(ws[cellAddress].v); const adjustedLength = this.getAdjustedLength(cellValue); maxLength = Math.max(maxLength, adjustedLength); } } maxLengths[C] = maxLength; } return maxLengths; } getAdjustedLength(value) { let length = 0; for (let i = 0; i < value.length; i++) { if (/[\u4e00-\u9fa5]/.test(value[i])) { length += 1; // 中文字符 } else if (/[a-zA-Z0-9]/.test(value[i])) { length += 0.5; // 英文字母和数字 } else { length += 1; // 其他字符 } } return length; } mathMultiply(value1, value2) { // 将浮点数转换为字符串 let aStr = value1.toString(); let bStr = value2.toString(); // 找到小数点后的位置 let aDecimals = (aStr.split('.')[1] || '').length; let bDecimals = (bStr.split('.')[1] || '').length; // 将浮点数转换为整数 let aInt = parseInt(aStr.replace('.', '')); let bInt = parseInt(bStr.replace('.', '')); // 进行整数乘法 let resultInt = aInt * bInt; // 计算最终的小数位数 let totalDecimals = aDecimals + bDecimals; // 将结果转换回浮点数 return resultInt / Math.pow(10, totalDecimals); } mathDivide(value1, value2) { // 将浮点数转换为字符串 let aStr = value1.toString(); let bStr = value2.toString(); // 找到小数点后的位置 let aDecimals = (aStr.split('.')[1] || '').length; let bDecimals = (bStr.split('.')[1] || '').length; // 找到最大的小数位数 let maxDecimals = Math.max(aDecimals, bDecimals); // 将浮点数转换为整数 let aInt = Math.round(value1 * Math.pow(10, maxDecimals)); let bInt = Math.round(value2 * Math.pow(10, maxDecimals)); // 进行整数除法并直接返回结果 return aInt / bInt; } } NcExcelService.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcExcelService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); NcExcelService.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcExcelService }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcExcelService, decorators: [{ type: Injectable }] }); class NcExcelServiceModule { } NcExcelServiceModule.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcExcelServiceModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); NcExcelServiceModule.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcExcelServiceModule }); NcExcelServiceModule.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcExcelServiceModule, providers: [NcExcelService] }); i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "12.1.5", ngImport: i0, type: NcExcelServiceModule, decorators: [{ type: NgModule, args: [{ providers: [NcExcelService] }] }] }); /** * Generated bundle index. Do not edit. */ export { NcExcelService, NcExcelServiceModule }; //# sourceMappingURL=ng-cw-v12-excel-service.js.map