From a7d7ffb7673c9fd9bfcc241337cc502dcd8f7e12 Mon Sep 17 00:00:00 2001 From: ljpark Date: Tue, 10 Feb 2026 13:49:48 +0900 Subject: [PATCH 1/5] =?UTF-8?q?wr:=20chRowspanTable=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/wr/components/lib/func/TableData.tsx | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/apps/wr/components/lib/func/TableData.tsx b/apps/wr/components/lib/func/TableData.tsx index 1a66802f5..0c613bed0 100644 --- a/apps/wr/components/lib/func/TableData.tsx +++ b/apps/wr/components/lib/func/TableData.tsx @@ -190,12 +190,10 @@ export const chRowspanTable = ( value={formatCommaNumber(value) || ""} disabled={!selectedIds.includes(row.chkgdnSn)} maxLength={numSize} - onInput={(e: React.ChangeEvent) => { - if ( - head.type === "number" && - e.target.value.length > numSize - ) { - e.target.value = e.target.value.slice(0, numSize); + onInput={(e: React.FormEvent) => { + const el = e.currentTarget; + if (head.type === "number" && el.value.length > numSize) { + el.value = el.value.slice(0, numSize); } }} /> @@ -225,12 +223,10 @@ export const chRowspanTable = ( value={value || ""} disabled={!selectedIds.includes(row.chkgdnSn)} maxLength={numSize} - onInput={(e: React.ChangeEvent) => { - if ( - head.type === "number" && - e.target.value.length > numSize - ) { - e.target.value = e.target.value.slice(0, numSize); + onInput={(e: React.FormEvent) => { + const el = e.currentTarget; + if (head.type === "number" && el.value.length > numSize) { + el.value = el.value.slice(0, numSize); } }} /> From c8912357b2821af700186fd93a211839aeccb07f Mon Sep 17 00:00:00 2001 From: ljpark Date: Tue, 10 Feb 2026 18:02:47 +0900 Subject: [PATCH 2/5] =?UTF-8?q?fo:=20exceldowntl=20,=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=20-=20=ED=91=9C=EB=A1=9C=EB=B3=B4=EA=B8=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/(with-layout)/dashboard/page.tsx | 22 ++- .../src/ui/excel/index.tsx | 137 +++++++++++++++--- 2 files changed, 135 insertions(+), 24 deletions(-) diff --git a/apps/fo/app/pages/(with-layout)/dashboard/page.tsx b/apps/fo/app/pages/(with-layout)/dashboard/page.tsx index 833f706ed..8728cfe1d 100644 --- a/apps/fo/app/pages/(with-layout)/dashboard/page.tsx +++ b/apps/fo/app/pages/(with-layout)/dashboard/page.tsx @@ -7,7 +7,7 @@ "use client" import { useFoStore } from "@/app/store"; import { - ContentsDiv, downloadExcel, downloadMultipleExcel, filePickedBlobArray, MainDiv + ContentsDiv, downloadExcel, downloadMultipleExcel, filePickedBlobArray, MainDiv, downloadExcelFormat , SelectBox, UITab, type SelectDataType } from "@common/business_components"; import { Button, TabsContent } from "@common/components/ui"; @@ -142,13 +142,11 @@ export default function DashBoard() { } } - const excelDownload = () => { + const excelDownload = async () => { if (!fcltyList.bscFctCd) return; switch (activeTab) { case 'facilityInfo': // 시설정보 및 운영현황 - searchOperationStatus(false, searchYear, fcltyList.bscFctCd).then(() => { setTimeout(() => { makeExcelData(); }, 200); }); - const makeExcelData = async () => { fetch(`/mainDashBoard.xlsx`, {}).then(async (response) => { if (!response.ok) { @@ -198,20 +196,26 @@ export default function DashBoard() { headerNum: 3, headerRange: 'A1:G2', sheetName: '운영실태', - startCol: 'A' + startCol: 'A', + decimalRange0: 'B9:B11', }, { data: excelData2, headerNum: 4, headerRange: 'A1:Q3', sheetName: '운영실태', - startCol: 'C' + startCol: 'C', + decimalRange0: 'C4:O8', + decimalRange1: 'C9:L10,M11:O11', + decimalRange3: 'C11:L16', } ]; // 버퍼에 있는 엑셀데이터 전달 후 json으로 변환 await filePickedBlobArray(blobRet, '시설정보및운영현황', sheetParameter); }); }; + await searchOperationStatus(false, searchYear, fcltyList.bscFctCd); + await makeExcelData(); break; case 'mainGraph': // 주요그래프 @@ -262,7 +266,11 @@ export default function DashBoard() { case 'rainManage': // 강우일 운영현황 searchRainManage(searchYear, fcltyList.bscFctCd, true); if (excelList && excelList.length > 0) { - downloadExcel({ data: excelList as unknown as JSON[], fileName: '강우일 운영현황', sheetNameArray: ['강우일 운영현황'], headerRange: 'A1:T1' }); + downloadExcel({ + data: excelList as unknown as JSON[], + fileName: '강우일 운영현황', + sheetNameArray: ['강우일 운영현황'], + headerRange: 'A1:T1' }); } break; // Add other cases for different tabs as needed diff --git a/packages/common/business_components/src/ui/excel/index.tsx b/packages/common/business_components/src/ui/excel/index.tsx index 048a0cc42..662705837 100644 --- a/packages/common/business_components/src/ui/excel/index.tsx +++ b/packages/common/business_components/src/ui/excel/index.tsx @@ -11,6 +11,10 @@ type ExcelDownloadProps = { decimalArray1? : number[]; decimalArray2? : number[]; decimalArray3? : number[]; + decimalRange0?: string; + decimalRange1?: string; + decimalRange2?: string; + decimalRange3?: string; } export const downloadExcel = ({ data, fileName = new Date().getTime().toString() + '.xlsx', sheetNameArray = ["Sheet1"], headerRange='A1:V3'}: ExcelDownloadProps) => { @@ -29,7 +33,20 @@ export const downloadExcelHeaders = ({ data, fileName = new Date().getTime().toS excelStyle(XLSX.write(workbook, { bookType: "xlsx", type: "buffer" }), headerRange, fileName); }; -export const downloadExcelFormat = ({ data, fileName = new Date().getTime().toString() + '.xlsx', sheetNameArray = ["Sheet1"], headerRange='A1:V3', decimalArray0=[], decimalArray1=[], decimalArray2=[], decimalArray3=[]}: ExcelDownloadProps) => { +export const downloadExcelFormat = ({ + data, + fileName = new Date().getTime().toString() + '.xlsx', + sheetNameArray = ["Sheet1"], + headerRange = 'A1:V3', + decimalArray0 = [], + decimalArray1 = [], + decimalArray2 = [], + decimalArray3 = [], + decimalRange0, + decimalRange1, + decimalRange2, + decimalRange3, +}: ExcelDownloadProps) => { const worksheet = XLSX.utils.json_to_sheet(Array.isArray(data) ? data : [data]); const workbook = XLSX.utils.book_new(); XLSX.utils.book_append_sheet(workbook, worksheet, sheetNameArray[0]); @@ -61,10 +78,35 @@ export const downloadExcelFormat = ({ data, fileName = new Date().getTime().toSt } } }; + 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); excelStyle(XLSX.write(workbook, { bookType: "xlsx", type: "buffer" }), headerRange, fileName); }; @@ -74,6 +116,10 @@ export type SheetParameter = { 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 => { @@ -201,23 +247,80 @@ export const filePickedBlob=(oFile: ArrayBuffer, data:[][],fileNm:string, header }); } -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.`); - } +// 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, + sheetArray: SheetParameter[] +): Promise => { + 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"; + 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); + } else { + console.error(`Sheet "${sheetName}" is undefined.`); } - }); - await excelArrayStyle(XLSX.write(wb, { bookType: "xlsx", type: "buffer" }), sheetArray, fileNm) + } }); -} + + await excelArrayStyle( + XLSX.write(wb, { bookType: "xlsx", type: "buffer" }), + sheetArray, + fileNm + ); + + return undefined; +}; type ExcelDownloadWithHeaderProps = { data: JSON | JSON[]; @@ -251,4 +354,4 @@ export const downloadExcelWithKoreanHeaders = async ({ // 3. 헤더만 한글로 교체하는 커스텀 스타일 함수 호출 await excelStyleWithKoreanHeaders(buffer, headerRange, fileName, headerMapping, dataArray[0]); -}; \ No newline at end of file +}; From 7650ec30e877e3ce5b0cb74b3138b9ceb4dc0f71 Mon Sep 17 00:00:00 2001 From: dongsilove Date: Tue, 10 Feb 2026 20:30:41 +0900 Subject: [PATCH 3/5] =?UTF-8?q?fo:=20dashboard=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/fo/app/pages/(with-layout)/dashboard/page.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/fo/app/pages/(with-layout)/dashboard/page.tsx b/apps/fo/app/pages/(with-layout)/dashboard/page.tsx index 8728cfe1d..fec75a0b7 100644 --- a/apps/fo/app/pages/(with-layout)/dashboard/page.tsx +++ b/apps/fo/app/pages/(with-layout)/dashboard/page.tsx @@ -7,7 +7,7 @@ "use client" import { useFoStore } from "@/app/store"; import { - ContentsDiv, downloadExcel, downloadMultipleExcel, filePickedBlobArray, MainDiv, downloadExcelFormat + ContentsDiv, downloadExcel, downloadMultipleExcel, filePickedBlobArray, MainDiv , SelectBox, UITab, type SelectDataType } from "@common/business_components"; import { Button, TabsContent } from "@common/components/ui"; From e264159323dfdf429795043796d2821768da254e Mon Sep 17 00:00:00 2001 From: dongsilove Date: Tue, 10 Feb 2026 22:41:56 +0900 Subject: [PATCH 4/5] =?UTF-8?q?fo:=20=EC=88=98=EC=A7=88=ED=98=84=ED=99=A9?= =?UTF-8?q?=20=ED=8C=9D=EC=97=85=20exceldown=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../pages/(with-layout)/dashboard/page.tsx | 15 ++++++++----- .../facilityWaterQlty/modal/page.tsx | 2 +- .../(with-layout)/flowRate/modal/page.tsx | 11 ++++++++-- .../processFacility/modal/page.tsx | 2 +- .../src/ui/excel/index.tsx | 22 +++++++++++++++++++ 5 files changed, 43 insertions(+), 9 deletions(-) diff --git a/apps/fo/app/pages/(with-layout)/dashboard/page.tsx b/apps/fo/app/pages/(with-layout)/dashboard/page.tsx index fec75a0b7..24d71e719 100644 --- a/apps/fo/app/pages/(with-layout)/dashboard/page.tsx +++ b/apps/fo/app/pages/(with-layout)/dashboard/page.tsx @@ -7,7 +7,7 @@ "use client" import { useFoStore } from "@/app/store"; import { - ContentsDiv, downloadExcel, downloadMultipleExcel, filePickedBlobArray, MainDiv + ContentsDiv, downloadExcelFormat, downloadMultipleExcel, filePickedBlobArrayFormat, MainDiv , SelectBox, UITab, type SelectDataType } from "@common/business_components"; import { Button, TabsContent } from "@common/components/ui"; @@ -197,7 +197,8 @@ export default function DashBoard() { headerRange: 'A1:G2', sheetName: '운영실태', startCol: 'A', - decimalRange0: 'B9:B11', + decimalRange0: 'B3:G2,B9:B11', + decimalRange1: 'B5:G5', }, { data: excelData2, @@ -211,7 +212,7 @@ export default function DashBoard() { } ]; // 버퍼에 있는 엑셀데이터 전달 후 json으로 변환 - await filePickedBlobArray(blobRet, '시설정보및운영현황', sheetParameter); + await filePickedBlobArrayFormat(blobRet, '시설정보및운영현황', sheetParameter); }); }; await searchOperationStatus(false, searchYear, fcltyList.bscFctCd); @@ -266,11 +267,15 @@ export default function DashBoard() { case 'rainManage': // 강우일 운영현황 searchRainManage(searchYear, fcltyList.bscFctCd, true); if (excelList && excelList.length > 0) { - downloadExcel({ + downloadExcelFormat({ data: excelList as unknown as JSON[], fileName: '강우일 운영현황', sheetNameArray: ['강우일 운영현황'], - headerRange: 'A1:T1' }); + headerRange: 'A1:T1', + decimalArray0: [2,3], + decimalArray1: [1,4,5,6,7,10,11,12,15,16,17], + decimalArray3: [8,9,13,14,18,19], + }); } break; // Add other cases for different tabs as needed diff --git a/apps/fo/app/pages/(with-layout)/facilityWaterQlty/modal/page.tsx b/apps/fo/app/pages/(with-layout)/facilityWaterQlty/modal/page.tsx index c1a6afdf8..9c6d5360a 100644 --- a/apps/fo/app/pages/(with-layout)/facilityWaterQlty/modal/page.tsx +++ b/apps/fo/app/pages/(with-layout)/facilityWaterQlty/modal/page.tsx @@ -394,7 +394,7 @@ export default function WaterQualityModal({ fcltyName = '', facilityCd = '', sea "유입BOD(mg/L)": CommaToNumber(item.tpInflWqltBod), "유입TP(mg/L)": CommaToNumber(item.tpInflWqltTp), "유출BOD(mg/L)": CommaToNumber(item.efflWqltBod), - "유출TP(mg/L)": CommaToNumber(item.efflWqltTn), + "유출TP(mg/L)": CommaToNumber(item.efflWqltTp), }); }); } diff --git a/apps/fo/app/pages/(with-layout)/flowRate/modal/page.tsx b/apps/fo/app/pages/(with-layout)/flowRate/modal/page.tsx index b214e0a48..0cffa775e 100644 --- a/apps/fo/app/pages/(with-layout)/flowRate/modal/page.tsx +++ b/apps/fo/app/pages/(with-layout)/flowRate/modal/page.tsx @@ -9,7 +9,7 @@ import FacilityTermModalHeader from "@/app/components/common/FacilityTermModalHe import { useFoStore } from "@/app/store"; import type { FlowRateModalInterface, modalExcelType } from "@/app/store/flowRate"; import { dataFormSetting } from "@/app/store/flowRate"; -import { downloadExcel, UITab, UiTable } from "@common/business_components/ui"; +import { downloadExcelFormat, UITab, UiTable } from "@common/business_components/ui"; import { Button, TabsContent } from "@common/components/ui"; import { useCallback, useEffect, useRef, useState } from "react"; import type { ReturnType } from "../../../../components/common/FacilityTermModalHeader"; @@ -201,7 +201,14 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st }); } if (excelDownList && excelDownList.length > 0) { - downloadExcel({ data: JSON.parse(JSON.stringify(excelDownList)), fileName: props.searchYear + '년 ' + props.fcltyName + ' 유량현황', sheetNameArray: [props.fcltyName + ' 유량현황'], headerRange: 'A1:H1' }); + downloadExcelFormat({ + data: JSON.parse(JSON.stringify(excelDownList)), + fileName: props.searchYear + '년 ' + props.fcltyName + ' 유량현황', + sheetNameArray: [props.fcltyName + ' 유량현황'], + headerRange: 'A1:H1', + decimalArray0: [1, 2, 3, 4, 5, 6], + decimalArray1: [0], + }); } else if (prevExcelListRef.current && prevExcelListRef.current.length > 0) { alert('조회된 결과가 없습니다.'); } diff --git a/apps/fo/app/pages/(with-layout)/processFacility/modal/page.tsx b/apps/fo/app/pages/(with-layout)/processFacility/modal/page.tsx index 20c885797..3118e008f 100644 --- a/apps/fo/app/pages/(with-layout)/processFacility/modal/page.tsx +++ b/apps/fo/app/pages/(with-layout)/processFacility/modal/page.tsx @@ -206,7 +206,7 @@ export default function ProcessFacilityInfoModal(props: ModalType) { const sheetParameter = [ { data: excelData, - headerNum: 2, + headerNum: 3, headerRange: 'A1:S2', sheetName: '시설 수질정보', startCol: 'A' diff --git a/packages/common/business_components/src/ui/excel/index.tsx b/packages/common/business_components/src/ui/excel/index.tsx index 662705837..80acd9696 100644 --- a/packages/common/business_components/src/ui/excel/index.tsx +++ b/packages/common/business_components/src/ui/excel/index.tsx @@ -268,6 +268,27 @@ export const filePickedBlobArray = async ( oFile: ArrayBuffer, fileNm: string, sheetArray: SheetParameter[] +): Promise => { + 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); + return undefined; +}; + +export const filePickedBlobArrayFormat = async ( + oFile: ArrayBuffer, + fileNm: string, + sheetArray: SheetParameter[] ): Promise => { const wb = XLSX.read(oFile, { type: "binary" }); @@ -276,6 +297,7 @@ export const filePickedBlobArray = async ( const sheet = wb.Sheets[sheetName]; if (sheet) { const startCol = sheetArray[index].startCol ?? "A"; + XLSX.utils.sheet_add_aoa(sheet, sheetArray[index].data, { origin: startCol + sheetArray[index].headerNum, }); From 8d5546abd58254ea516da2ba64dfe46665166500 Mon Sep 17 00:00:00 2001 From: dongsilove Date: Tue, 10 Feb 2026 22:55:58 +0900 Subject: [PATCH 5/5] =?UTF-8?q?fo:=20=EC=B2=98=EB=A6=AC=EA=B5=AC=EC=97=AD?= =?UTF-8?q?=20exceldown=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/fo/app/pages/(with-layout)/processArea/page.tsx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/apps/fo/app/pages/(with-layout)/processArea/page.tsx b/apps/fo/app/pages/(with-layout)/processArea/page.tsx index 60dfff2cc..bca5a7b91 100644 --- a/apps/fo/app/pages/(with-layout)/processArea/page.tsx +++ b/apps/fo/app/pages/(with-layout)/processArea/page.tsx @@ -160,11 +160,8 @@ export default function ProcessArea() { fileName: '처리구역정보', sheetNameArray: ['처리구역정보'], headerRange: 'A1:W1', - decimalArray0: [ - 3, 4, 5, 6, 7, 8, 9, - 10, 11, 12, 13, 14, 15, - 17, 18, 19, 20, 21 - ] + decimalArray0: [3, 4, 5, 6, 7, 8, 9,10, 11, 12, 13, 14, 15], + decimalArray2: [16, 17, 18, 19, 20, 21], }); } else { alert('조회된 결과가 없습니다.');