diff --git a/apps/fo/app/pages/(with-layout)/dashboard/page.tsx b/apps/fo/app/pages/(with-layout)/dashboard/page.tsx index 833f706ed..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"; @@ -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,27 @@ export default function DashBoard() { headerNum: 3, headerRange: 'A1:G2', sheetName: '운영실태', - startCol: 'A' + startCol: 'A', + decimalRange0: 'B3:G2,B9:B11', + decimalRange1: 'B5:G5', }, { 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 filePickedBlobArrayFormat(blobRet, '시설정보및운영현황', sheetParameter); }); }; + await searchOperationStatus(false, searchYear, fcltyList.bscFctCd); + await makeExcelData(); break; case 'mainGraph': // 주요그래프 @@ -262,7 +267,15 @@ 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' }); + downloadExcelFormat({ + data: excelList as unknown as JSON[], + fileName: '강우일 운영현황', + sheetNameArray: ['강우일 운영현황'], + 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)/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('조회된 결과가 없습니다.'); 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/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); } }} /> diff --git a/packages/common/business_components/src/ui/excel/index.tsx b/packages/common/business_components/src/ui/excel/index.tsx index 048a0cc42..80acd9696 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,9 +247,29 @@ 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' }); +// 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(function (sheetName, index) { if (sheetArray[index]) { const sheet = wb.Sheets[sheetName]; @@ -215,9 +281,68 @@ export const filePickedBlobArray = (oFile: ArrayBuffer, fileNm: string, sheetArr } } }); - await excelArrayStyle(XLSX.write(wb, { bookType: "xlsx", type: "buffer" }), sheetArray, fileNm) + 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" }); + + 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 + ); + + return undefined; +}; type ExcelDownloadWithHeaderProps = { data: JSON | JSON[]; @@ -251,4 +376,4 @@ export const downloadExcelWithKoreanHeaders = async ({ // 3. 헤더만 한글로 교체하는 커스텀 스타일 함수 호출 await excelStyleWithKoreanHeaders(buffer, headerRange, fileName, headerMapping, dataArray[0]); -}; \ No newline at end of file +};