ERP-node/frontend/lib/registry/components/pivot-grid/utils/exportExcel.ts

203 lines
4.8 KiB
TypeScript
Raw Normal View History

/**
* Excel
* Excel
* xlsx ( )
*/
import * as XLSX from "xlsx";
import {
PivotResult,
PivotFieldConfig,
PivotTotalsConfig,
} from "../types";
import { pathToKey } from "./pivotEngine";
// ==================== 타입 ====================
export interface ExportOptions {
fileName?: string;
sheetName?: string;
title?: string;
subtitle?: string;
includeHeaders?: boolean;
includeTotals?: boolean;
}
// ==================== 메인 함수 ====================
/**
* Excel로
*/
export async function exportPivotToExcel(
pivotResult: PivotResult,
fields: PivotFieldConfig[],
totals: PivotTotalsConfig,
options: ExportOptions = {}
): Promise<void> {
const {
fileName = "pivot_export",
sheetName = "Pivot",
title,
includeHeaders = true,
includeTotals = true,
} = options;
// 필드 분류
const rowFields = fields
.filter((f) => f.area === "row" && f.visible !== false)
.sort((a, b) => (a.areaIndex || 0) - (b.areaIndex || 0));
// 데이터 배열 생성
const data: any[][] = [];
// 제목 추가
if (title) {
data.push([title]);
data.push([]); // 빈 행
}
// 헤더 행
if (includeHeaders) {
const headerRow: any[] = [
rowFields.map((f) => f.caption).join(" / ") || "항목",
];
// 열 헤더
for (const col of pivotResult.flatColumns) {
headerRow.push(col.caption || "(전체)");
}
// 총계 헤더
if (totals?.showRowGrandTotals && includeTotals) {
headerRow.push("총계");
}
data.push(headerRow);
}
// 데이터 행
for (const row of pivotResult.flatRows) {
const excelRow: any[] = [];
// 행 헤더 (들여쓰기 포함)
const indent = " ".repeat(row.level);
excelRow.push(indent + row.caption);
// 데이터 셀
for (const col of pivotResult.flatColumns) {
const cellKey = `${pathToKey(row.path)}|||${pathToKey(col.path)}`;
const values = pivotResult.dataMatrix.get(cellKey);
if (values && values.length > 0) {
excelRow.push(values[0].value);
} else {
excelRow.push("");
}
}
// 행 총계
if (totals?.showRowGrandTotals && includeTotals) {
const rowTotal = pivotResult.grandTotals.row.get(pathToKey(row.path));
if (rowTotal && rowTotal.length > 0) {
excelRow.push(rowTotal[0].value);
} else {
excelRow.push("");
}
}
data.push(excelRow);
}
// 열 총계 행
if (totals?.showColumnGrandTotals && includeTotals) {
const totalRow: any[] = ["총계"];
for (const col of pivotResult.flatColumns) {
const colTotal = pivotResult.grandTotals.column.get(pathToKey(col.path));
if (colTotal && colTotal.length > 0) {
totalRow.push(colTotal[0].value);
} else {
totalRow.push("");
}
}
// 대총합
if (totals?.showRowGrandTotals) {
const grandTotal = pivotResult.grandTotals.grand;
if (grandTotal && grandTotal.length > 0) {
totalRow.push(grandTotal[0].value);
} else {
totalRow.push("");
}
}
data.push(totalRow);
}
// 워크시트 생성
const worksheet = XLSX.utils.aoa_to_sheet(data);
// 컬럼 너비 설정
const colWidths: XLSX.ColInfo[] = [];
const maxCols = data.reduce((max, row) => Math.max(max, row.length), 0);
for (let i = 0; i < maxCols; i++) {
colWidths.push({ wch: i === 0 ? 25 : 15 });
}
worksheet["!cols"] = colWidths;
// 워크북 생성
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, sheetName);
// 파일 다운로드
XLSX.writeFile(workbook, `${fileName}.xlsx`);
}
/**
* Drill Down Excel로
*/
export async function exportDrillDownToExcel(
data: any[],
columns: { field: string; caption: string }[],
options: ExportOptions = {}
): Promise<void> {
const {
fileName = "drilldown_export",
sheetName = "Data",
title,
} = options;
// 데이터 배열 생성
const sheetData: any[][] = [];
// 제목
if (title) {
sheetData.push([title]);
sheetData.push([]); // 빈 행
}
// 헤더
const headerRow = columns.map((col) => col.caption);
sheetData.push(headerRow);
// 데이터
for (const row of data) {
const dataRow = columns.map((col) => row[col.field] ?? "");
sheetData.push(dataRow);
}
// 워크시트 생성
const worksheet = XLSX.utils.aoa_to_sheet(sheetData);
// 컬럼 너비 설정
const colWidths: XLSX.ColInfo[] = columns.map(() => ({ wch: 15 }));
worksheet["!cols"] = colWidths;
// 워크북 생성
const workbook = XLSX.utils.book_new();
XLSX.utils.book_append_sheet(workbook, worksheet, sheetName);
// 파일 다운로드
XLSX.writeFile(workbook, `${fileName}.xlsx`);
}