diff --git a/apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/rainSttn/page.tsx b/apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/rainSttn/page.tsx index 029f19852..d4658297a 100644 --- a/apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/rainSttn/page.tsx +++ b/apps/wr/app/(with-layout)/ruseFcltMng/fcltSttn/rainSttn/page.tsx @@ -9,10 +9,11 @@ import type { } from "@/app/store/ruseFcltMng/fcltReg/rainReg"; import { rainSttnSize } from "@/app/store/ruseFcltMng/fcltSttn/rainSttn"; import { - excelDownload, + excelDownloadStyle, getCity, tableData, } from "@/components/lib/func/commom"; +import { type SheetParameter } from "@common/business_components/ui"; import type { objSearchType } from "@/components/ui/modal/fcltReg/bdstRegModal/bdstRegModal"; import HeadChModal from "@/components/ui/modal/sttn/headChModal/headChModal"; import SttnDetailModal from "@/components/ui/modal/sttn/sttnDetailModal/sttnDetailModal"; @@ -158,11 +159,19 @@ export default function RainSttn() { return; } if (rainSttn.excelList.length > 0) { - excelDownload( - rainSttn.excelList, + const sheetArray: SheetParameter[] = [{ + data: rainSttn.excelList, + headerNum: 3, + headerRange: "A1:W3", + sheetName: 'sheet1', + writeLine: 4, + startCol: 'A', + decimalArray0: [6,10,12,14,16] + }]; + excelDownloadStyle( "빗물이용시설 현황", - "sample_rainSttn.xlsx", - "A1:W3" + "sample_rainSttn.xlsx", // public 폴더에 있는 template 파일 경로 + sheetArray ); } else { toast.error(t("excelDownErrMsg")); diff --git a/apps/wr/components/lib/func/commom.tsx b/apps/wr/components/lib/func/commom.tsx index a99dd8db8..ff0082434 100644 --- a/apps/wr/components/lib/func/commom.tsx +++ b/apps/wr/components/lib/func/commom.tsx @@ -2,11 +2,14 @@ import { lwCds, type comCodeState } from "@/app/store/common/code"; import { filePickedBlob, filePickedBlobArray, + filePickedBlobArrayFormat, + filePickedBlobNumStyle, type ReturnType, type SelectDataType, type SheetParameter, } from "@common/business_components/ui"; import { Button } from "@common/components/ui"; +import { write } from "fs"; import moment from "moment"; import type { RefObject } from "react"; import toast from "react-hot-toast"; @@ -100,6 +103,26 @@ export const excelDownload = ( }); }; +export const excelDownloadStyle = ( + // excelList: [][], + fileNm: string, + sampleFile: string, + sheetArray: SheetParameter[] +) => { + // if (excelList.length === 0) return; + fetch(`/${sampleFile}`, {}).then(async (response) => { + if (!response.ok) { + throw Error(response.statusText); + } + // 받은 파일 바인너리 처리 + const blobRet = await response.arrayBuffer(); + // 버퍼에 있는 엑셀데이터 전달 후 json으로 변환 + // await filePickedBlob(blobRet, excelList, fileNm, writeLine, headerRange); + + await filePickedBlobNumStyle(blobRet, fileNm, sheetArray); + }); +}; + export const excelArrayDownload = ( fileNm: string, sampleFile: string, diff --git a/packages/common/business_components/src/ui/excel/index.tsx b/packages/common/business_components/src/ui/excel/index.tsx index 80acd9696..a532e948e 100644 --- a/packages/common/business_components/src/ui/excel/index.tsx +++ b/packages/common/business_components/src/ui/excel/index.tsx @@ -17,6 +17,105 @@ type ExcelDownloadProps = { decimalRange3?: string; } +export type SheetParameter = { + data: any[][]; + headerNum: number; + headerRange: string; + sheetName: string; + writeLine?: number; + startCol?: string; + decimalArray0?: number[]; + decimalArray1?: number[]; + decimalArray2?: number[]; + decimalArray3?: number[]; + decimalRange0?: string; + decimalRange1?: string; + decimalRange2?: string; + decimalRange3?: string; +}; + +// Shared utility to apply number formatting to a sheet range +export const applyRangeFormat = ( + sheet: XLSX.WorkSheet, + rangeStr: string | undefined, + decimal: number +) => { + if (!rangeStr) return; + const parts = rangeStr + .split(",") + .map((part) => part.trim()) + .filter(Boolean); + for (const part of parts) { + const rangeInfo = XLSX.utils.decode_range(part); + for (let row = rangeInfo.s.r; row <= rangeInfo.e.r; row++) { + for (let col = rangeInfo.s.c; col <= rangeInfo.e.c; col++) { + const cellAddr = XLSX.utils.encode_cell({ r: row, c: col }); + const cell = sheet[cellAddr]; + if (cell) { + if (typeof cell.v === "string") { + const parsed = Number(cell.v.replace(/,/g, "").trim()); + if (!Number.isNaN(parsed)) { + cell.v = parsed; + cell.t = "n"; + } + } + } + if (cell && typeof cell.v === "number") { + if (decimal === 1) { + cell.z = "#,##0.0"; + } else if (decimal === 2) { + cell.z = "#,##0.00"; + } else if (decimal === 3) { + cell.z = "#,##0.000"; + } else { + cell.z = "#,##0"; + } + } + } + } + } +}; + + + +// Shared utility to apply decimal formatting to specific columns in a sheet +export const applyDecimalColumns = ( + sheet: XLSX.WorkSheet, + startRow: number, + range: XLSX.Range, + columns: number[], + decimal: number +) => { + for (let row = startRow; row <= range.e.r; row++) { + if (columns.length > 0) { + for (let idx = 0; idx < columns.length; idx++) { + const cellAddr = XLSX.utils.encode_cell({ r: row, c: Number(columns[idx]) }); + const cell = sheet[cellAddr]; + if (cell) { + if (typeof cell.v === "string") { + const parsed = Number(cell.v.replace(/,/g, "").trim()); + if (!Number.isNaN(parsed)) { + cell.v = parsed; + cell.t = "n"; + } + } + } + if (cell && typeof cell.v === "number") { + if (decimal === 1) { + cell.z = "#,##0.0"; // 소수점 1자리 + } else if (decimal === 2) { + cell.z = "#,##0.00"; + } else if (decimal === 3) { + cell.z = "#,##0.000"; + } else { + cell.z = "#,##0"; // 기본 천단위 콤마 + } + } + } + } + } +}; + export const downloadExcel = ({ data, fileName = new Date().getTime().toString() + '.xlsx', sheetNameArray = ["Sheet1"], headerRange='A1:V3'}: ExcelDownloadProps) => { const worksheet = XLSX.utils.json_to_sheet(Array.isArray(data) ? data : [data]); const workbook = XLSX.utils.book_new(); @@ -57,71 +156,17 @@ export const downloadExcelFormat = ({ // 🔹 전체 범위 const range = XLSX.utils.decode_range(worksheet["!ref"] as string); - const chaneValue = (array: number[], decimal: number) => { - for (let row = startRow; row <= range.e.r; row++) { - if(array.length>0){ - for(let idx=0; idx { - if (!rangeStr) return; - const rangeInfo = XLSX.utils.decode_range(rangeStr); - for (let row = rangeInfo.s.r; row <= rangeInfo.e.r; row++) { - for (let col = rangeInfo.s.c; col <= rangeInfo.e.c; col++) { - const cellAddr = XLSX.utils.encode_cell({ r: row, c: col }); - const cell = worksheet[cellAddr]; - if (cell && typeof cell.v === "number") { - if (decimal === 1) { - cell.z = "#,##0.0"; - } else if (decimal === 2) { - cell.z = "#,##0.00"; - } else if (decimal === 3) { - cell.z = "#,##0.000"; - } else { - cell.z = "#,##0"; - } - } - } - } - }; - chaneValue(decimalArray0, 0); - chaneValue(decimalArray1, 1); - chaneValue(decimalArray2, 2); - chaneValue(decimalArray3, 3); - applyRangeFormat(decimalRange0, 0); - applyRangeFormat(decimalRange1, 1); - applyRangeFormat(decimalRange2, 2); - applyRangeFormat(decimalRange3, 3); + applyDecimalColumns(worksheet, startRow, range, decimalArray0, 0); + applyDecimalColumns(worksheet, startRow, range, decimalArray1, 1); + applyDecimalColumns(worksheet, startRow, range, decimalArray2, 2); + applyDecimalColumns(worksheet, startRow, range, decimalArray3, 3); + applyRangeFormat(worksheet, decimalRange0, 0); + applyRangeFormat(worksheet, decimalRange1, 1); + applyRangeFormat(worksheet, decimalRange2, 2); + applyRangeFormat(worksheet, decimalRange3, 3); excelStyle(XLSX.write(workbook, { bookType: "xlsx", type: "buffer" }), headerRange, fileName); }; -export type SheetParameter = { - data: any[][]; - headerNum: number; - headerRange: string; - sheetName: string; - startCol?: string; - decimalRange0?: string; - decimalRange1?: string; - decimalRange2?: string; - decimalRange3?: string; -}; - export const downloadMultipleExcel = (fileName = new Date().getTime().toString() + '.xlsx', sheetArray: SheetParameter[]): Promise => { const workbook = XLSX.utils.book_new(); for (let i = 0; i < sheetArray.length; i++) { @@ -223,7 +268,13 @@ export const filePickedArray=(oFile: File, headerNum:number=0):Promise =>{ // Create A File Reader HTML5 +export const filePickedBlob = ( + oFile: ArrayBuffer, + data:[][],fileNm:string, + headerNum:number=0, + headerRange='A1:V3', + startCol?:number +):Promise =>{ // Create A File Reader HTML5 return new Promise(() => { // Ready The Event For When A File Gets Selected const wb = XLSX.read(oFile, { type: 'binary' }); @@ -244,26 +295,56 @@ export const filePickedBlob=(oFile: ArrayBuffer, data:[][],fileNm:string, header } } }); - }); - } + }); +} +export const filePickedBlobNumStyle = async ( + oFile: ArrayBuffer, + fileNm:string, + sheetArray: SheetParameter[] +):Promise => { // Create A File Reader HTML5 + + // Ready The Event For When A File Gets Selected + const wb = XLSX.read(oFile, { type: "binary" }); + + wb.SheetNames.forEach((sheetName, index) => { + if (sheetArray[index]) { + const sheet = wb.Sheets[sheetName]; + if (sheet) { + const startCol = sheetArray[index].startCol ?? "A"; + const dataStartLine = sheetArray[index].writeLine ?? sheetArray[index].headerNum; + + XLSX.utils.sheet_add_aoa(sheet, sheetArray[index].data, { + origin: startCol + dataStartLine, // startLine 은 1부터 시작 + }); + const startRow = Math.max(dataStartLine - 1, 0); // 행은 0부터 시작 + + // 🔹 전체 범위 + const range = XLSX.utils.decode_range(sheet["!ref"] as string); + + if(sheetArray[index].decimalArray0){ applyDecimalColumns(sheet, startRow, range, sheetArray[index].decimalArray0, 0); } + if(sheetArray[index].decimalArray1){ applyDecimalColumns(sheet, startRow, range, sheetArray[index].decimalArray1, 1); } + if(sheetArray[index].decimalArray2){ applyDecimalColumns(sheet, startRow, range, sheetArray[index].decimalArray2, 2); } + if(sheetArray[index].decimalArray3){ applyDecimalColumns(sheet, startRow, range, sheetArray[index].decimalArray3, 3); } + if(sheetArray[index].decimalRange0){ applyRangeFormat(sheet, sheetArray[index].decimalRange0, 0); } + if(sheetArray[index].decimalRange1){ applyRangeFormat(sheet, sheetArray[index].decimalRange1, 1); } + if(sheetArray[index].decimalRange2){ applyRangeFormat(sheet, sheetArray[index].decimalRange2, 2); } + if(sheetArray[index].decimalRange3){ applyRangeFormat(sheet, sheetArray[index].decimalRange3, 3); } + } else { + console.error(`Sheet "${sheetName}" is undefined.`); + } + } + }); + + await excelArrayStyle( + XLSX.write(wb, { bookType: "xlsx", type: "buffer" }), + sheetArray, + fileNm + ); + + return undefined; +}; + -// export const filePickedBlobArray = (oFile: ArrayBuffer, fileNm: string, sheetArray: SheetParameter[]): Promise => { -// return new Promise(async () => { -// const wb = XLSX.read(oFile, { type: 'binary' }); -// wb.SheetNames.forEach(function (sheetName, index) { -// if (sheetArray[index]) { -// const sheet = wb.Sheets[sheetName]; -// if (sheet) { -// const startCol = sheetArray[index].startCol ? sheetArray[index].startCol : 'A'; -// XLSX.utils.sheet_add_aoa(sheet, sheetArray[index].data, { origin: startCol + sheetArray[index].headerNum }); -// } else { -// console.error(`Sheet "${sheetName}" is undefined.`); -// } -// } -// }); -// await excelArrayStyle(XLSX.write(wb, { bookType: "xlsx", type: "buffer" }), sheetArray, fileNm) -// }); -// } export const filePickedBlobArray = async ( oFile: ArrayBuffer, fileNm: string, @@ -301,34 +382,10 @@ export const filePickedBlobArrayFormat = async ( XLSX.utils.sheet_add_aoa(sheet, sheetArray[index].data, { origin: startCol + sheetArray[index].headerNum, }); - const applyRangeFormat = (rangeStr: string | undefined, decimal: number) => { - if (!rangeStr) return; - const parts = rangeStr.split(",").map((part) => part.trim()).filter(Boolean); - for (const part of parts) { - const rangeInfo = XLSX.utils.decode_range(part); - for (let row = rangeInfo.s.r; row <= rangeInfo.e.r; row++) { - for (let col = rangeInfo.s.c; col <= rangeInfo.e.c; col++) { - const cellAddr = XLSX.utils.encode_cell({ r: row, c: col }); - const cell = sheet[cellAddr]; - if (cell && typeof cell.v === "number") { - if (decimal === 1) { - cell.z = "#,##0.0"; - } else if (decimal === 2) { - cell.z = "#,##0.00"; - } else if (decimal === 3) { - cell.z = "#,##0.000"; - } else { - cell.z = "#,##0"; - } - } - } - } - } - }; - applyRangeFormat(sheetArray[index].decimalRange0, 0); - applyRangeFormat(sheetArray[index].decimalRange1, 1); - applyRangeFormat(sheetArray[index].decimalRange2, 2); - applyRangeFormat(sheetArray[index].decimalRange3, 3); + applyRangeFormat(sheet, sheetArray[index].decimalRange0, 0); + applyRangeFormat(sheet, sheetArray[index].decimalRange1, 1); + applyRangeFormat(sheet, sheetArray[index].decimalRange2, 2); + applyRangeFormat(sheet, sheetArray[index].decimalRange3, 3); } else { console.error(`Sheet "${sheetName}" is undefined.`); }