wr: 빗물사용시설 excel 천단위 처리

This commit is contained in:
dongsilove
2026-03-03 23:23:00 +09:00
parent 9cf73141df
commit aa05cb5d73
3 changed files with 204 additions and 115 deletions
@@ -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"));
+23
View File
@@ -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,
@@ -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<array.length; idx++){
const cellAddr = XLSX.utils.encode_cell({ r: row, c: Number(array[idx]) });
const cell = worksheet[cellAddr];
if (cell && typeof cell.v === "number") {
if(decimal===1){
cell.z = "#,##0.0"; // 소수점 3자리
}else if(decimal===2){
cell.z = "#,##0.00";
}else if(decimal===3){
cell.z = "#,##0.000";
}else{
cell.z = "#,##0"; // 기본 천단위 콤마
}
}
}
}
}
};
const applyRangeFormat = (rangeStr: string | undefined, decimal: number) => {
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<JSON | undefined> => {
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<JSON[] |
});
}
export const filePickedBlob=(oFile: ArrayBuffer, data:[][],fileNm:string, headerNum:number=0, headerRange='A1:V3', startCol?:number):Promise<JSON | undefined> =>{ // Create A File Reader HTML5
export const filePickedBlob = (
oFile: ArrayBuffer,
data:[][],fileNm:string,
headerNum:number=0,
headerRange='A1:V3',
startCol?:number
):Promise<JSON | undefined> =>{ // Create A File Reader HTML5
return new Promise<JSON | undefined>(() => {
// 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<JSON | undefined> => { // 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<JSON | undefined> => {
// return new Promise<JSON | undefined>(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.`);
}