ng-cw-v12
Version:
Angular UI component library
282 lines (277 loc) • 12.3 kB
JavaScript
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