diff --git a/apps/fo/app/pages/(with-layout)/dashboard/page.tsx b/apps/fo/app/pages/(with-layout)/dashboard/page.tsx index 77e4168ec..bf15f2493 100644 --- a/apps/fo/app/pages/(with-layout)/dashboard/page.tsx +++ b/apps/fo/app/pages/(with-layout)/dashboard/page.tsx @@ -145,71 +145,74 @@ export default function DashBoard() { const excelDownload = () => { if (!fcltyList.bscFctCd) return; + switch (activeTab) { case 'facilityInfo': - searchOperationStatus(false, searchYear, fcltyList.bscFctCd); + searchOperationStatus(false, searchYear, fcltyList.bscFctCd).then(() => {setTimeout(() => {makeExcelData();}, 200);}); - fetch(`/mainDashBoard.xlsx`, {}).then(async (response) => { - if (!response.ok) { - throw Error(response.statusText); - } - // 받은 파일 바인너리 처리 - const blobRet = await response.arrayBuffer(); - - const excelData1 = planFcltyList.map((item) => { - return [ - item.item, - Number(item.provisionalApproval.toString().replaceAll(',',''))?Number(item.provisionalApproval.toString().replaceAll(',','')):item.provisionalApproval, - item.now, - item.stepOne, - item.stepTwo, - item.stepThree, - item.stepFour, - ]; - }); - - // operationalStatusList를 2차원 배열로 변환 - const excelData2 = operationalStatusList.map((item, index) => { - const baseData = [ - Number(item.yearAvg?.replaceAll(',','')), - Number(item.yearMax?.replaceAll(',','')), - Number(item.yearMin?.replaceAll(',','')), - Number(item.summerAll?.replaceAll(',','')), - Number(item.summerThree?.replaceAll(',','')), - Number(item.summerRain?.replaceAll(',','')), - Number(item.winterAll?.replaceAll(',','')), - Number(item.winterSunnyAvg?.replaceAll(',','')), - Number(item.winterSunnyMax?.replaceAll(',','')), - Number(item.winterSunnyMin?.replaceAll(',','')), - Number(item.groundWaterAvg?.replaceAll(',','').replaceAll('%','')), - Number(item.groundWaterSummer?.replaceAll(',','').replaceAll('%','')), - Number(item.groundWaterWinter?.replaceAll(',','').replaceAll('%','')) - ]; - - // index가 0일 때만 rdi와 rdiRain 추가 - console.log(item.rdiRain); - return index === 0 ? [...baseData, item.rdi&&item.rdi.toString().trim()!=''?Number(item.rdi.toString().replaceAll(',','')):'', item.rdiRain&&item.rdiRain.toString().trim()!=''?Number(item.rdiRain.toString().replaceAll(',','')):''] : baseData; - }); - - const sheetParameter = [ - { - data: excelData1, - headerNum: 3, - headerRange: 'A1:G2', - sheetName: '운영실태', - startCol: 'A' - }, - { - data: excelData2, - headerNum: 4, - headerRange: 'A1:Q3', - sheetName: '운영실태', - startCol: 'C' + const makeExcelData = async () => { + fetch(`/mainDashBoard.xlsx`, {}).then(async (response) => { + if (!response.ok) { + throw Error(response.statusText); } - ]; - // 버퍼에 있는 엑셀데이터 전달 후 json으로 변환 - await filePickedBlobArray(blobRet, '시설정보및운영현황황', sheetParameter); - }); + // 받은 파일 바인너리 처리 + const blobRet = await response.arrayBuffer(); + + const excelData1 = planFcltyList.map((item) => { + return [ + item.item, + item.provisionalApproval?Number(item.provisionalApproval.toString().replaceAll(',',''))?Number(item.provisionalApproval.toString().replaceAll(',','')):item.provisionalApproval:'', + item.now, + item.stepOne, + item.stepTwo, + item.stepThree, + item.stepFour, + ]; + }); + + // operationalStatusList를 2차원 배열로 변환 + const excelData2 = operationalStatusList.map((item, index) => { + console.log(item.yearAvg+' '+item.yearMax+' '+item.yearMin); + const baseData = [ + Number(item.yearAvg?.toString().replaceAll(',','')), + Number(item.yearMax?.toString().replaceAll(',','')), + Number(item.yearMin?.toString().replaceAll(',','')), + Number(item.summerAll?.toString().replaceAll(',','')), + Number(item.summerThree?.toString().replaceAll(',','')), + Number(item.summerRain?.toString().replaceAll(',','')), + Number(item.winterAll?.toString().replaceAll(',','')), + Number(item.winterSunnyAvg?.toString().replaceAll(',','')), + Number(item.winterSunnyMax?.toString().replaceAll(',','')), + Number(item.winterSunnyMin?.toString().replaceAll(',','')), + Number(item.groundWaterAvg?.toString().replaceAll(',','').replaceAll('%','')), + Number(item.groundWaterSummer?.toString().replaceAll(',','').replaceAll('%','')), + Number(item.groundWaterWinter?.toString().replaceAll(',','').replaceAll('%','')) + ]; + + // index가 0일 때만 rdi와 rdiRain 추가 + return index === 0 ? [...baseData, item.rdi&&item.rdi.toString().trim()!=''?Number(item.rdi.toString().replaceAll(',','')):'', item.rdiRain&&item.rdiRain.toString().trim()!=''?Number(item.rdiRain.toString().replaceAll(',','')):''] : baseData; + }); + + const sheetParameter = [ + { + data: excelData1, + headerNum: 3, + headerRange: 'A1:G2', + sheetName: '운영실태', + startCol: 'A' + }, + { + data: excelData2, + headerNum: 4, + headerRange: 'A1:Q3', + sheetName: '운영실태', + startCol: 'C' + } + ]; + // 버퍼에 있는 엑셀데이터 전달 후 json으로 변환 + await filePickedBlobArray(blobRet, '시설정보및운영현황황', sheetParameter); + }); + }; break; case 'mainGraph': diff --git a/apps/fo/app/pages/(with-layout)/rdi/page.tsx b/apps/fo/app/pages/(with-layout)/rdi/page.tsx index 69fb49b14..51670c38c 100644 --- a/apps/fo/app/pages/(with-layout)/rdi/page.tsx +++ b/apps/fo/app/pages/(with-layout)/rdi/page.tsx @@ -83,10 +83,8 @@ export default function Rdi() { const [operationValue, setOperationValue] = useState("0"); // 조회기간 const [searchYear, setSearchYear] = useState(selectSearchYear[0]?.val || "0"); - - useEffect(() => { - console.log(isLoading); - }, [isLoading]); + // 엑셀 다운로드 트리거 + const [isInitExcelDownload, setIsInitExcelDownload] = useState(false); /** * 페이지 함수 @@ -122,6 +120,7 @@ export default function Rdi() { }, [isInit, initializeCommonData, searchRdiList, selectSearchYear]); useEffect(() => { + if(!isInitExcelDownload) return ; if (excelList && excelList.length > 0) { downloadExcel({ data: excelList as unknown as JSON[], @@ -130,7 +129,7 @@ export default function Rdi() { headerRange: 'A1:O1' }); } - }, [excelList]); + }, [excelList, isInitExcelDownload]); /** * 콜백함수 @@ -168,8 +167,9 @@ export default function Rdi() { // 엑셀다운로드 const excelDownload = useCallback(() => { - searchRdi(ref.current, 1, 10000); - }, [searchRdi]); + searchRdi(ref.current, 1, rdiCount); + setIsInitExcelDownload(true); + }, [searchRdi, rdiCount]); // 페이지 변경 const handlePageChange = useCallback((page: number) => { diff --git a/apps/fo/app/pages/(with-layout)/sewageOccur/page.tsx b/apps/fo/app/pages/(with-layout)/sewageOccur/page.tsx index 87bc8e7a8..24b03ccc9 100644 --- a/apps/fo/app/pages/(with-layout)/sewageOccur/page.tsx +++ b/apps/fo/app/pages/(with-layout)/sewageOccur/page.tsx @@ -21,7 +21,6 @@ export default function PlanSewage() { ,SewageOccurSearch ,initializeCommonData ,searchSewageOccurList - ,resetExcelList } = useFoStore((state) => state) || { Common: { isInit: false, selectUpdownData: [], selectOperationData: [], selectSidoData: [], selectSigunData: [], selectRegionData: [], selectUnitWaterShedData: [], selectMiddleRegionData: [], selectEnvPartData: [], selectSearchYear: [{ val: "0" }], btnClass: "" }, SewageOccur: { sewageOccurList: [], sewageOccurCount: 0, excelList: [], upHeadList1: [], upHeadList2: [], upHeadList3: [] }, @@ -95,6 +94,8 @@ export default function PlanSewage() { const [operationValue, setOperationValue] = useState("0"); // 조회기간 const [searchYear, setSearchYear] = useState(selectSearchYear[0]?.val || "0"); + // 엑셀다운로드 트리거 + const [isInitExcelDownload, setIsInitExcelDownload] = useState(false); /** * 페이지 함수 @@ -136,14 +137,15 @@ export default function PlanSewage() { * useEffect */ useEffect(() => { - resetExcelList(); if (!isInit) { initializeCommonData(); searchSewageOccurList(1, 10, selectSearchYear[0]?.val || "0"); } - }, [isInit, initializeCommonData, searchSewageOccurList]); + }, [isInit, initializeCommonData, searchSewageOccurList, selectSearchYear]); useEffect(() => { + if(!isInitExcelDownload) return ; + const timer = setTimeout(() => { if (excelList && excelList.length > 0) { downloadExcel({ @@ -156,7 +158,7 @@ export default function PlanSewage() { }, 500); // 약간의 지연을 두고 다운로드 실행 return () => clearTimeout(timer); - }, [excelList, isInit]); + }, [excelList, isInit, isInitExcelDownload]); /** * 콜백함수 @@ -195,10 +197,7 @@ export default function PlanSewage() { // 엑셀다운로드 const excelDownload = useCallback(() => { searchFacilityPrecipitation(ref.current, 1, sewageOccurCount); - - setTimeout(() => { - resetExcelList() - }, 1000); // 약간의 지연을 두고 초기화 (다운로드 완료 후) + setIsInitExcelDownload(true); }, [searchFacilityPrecipitation, sewageOccurCount]); // 페이지 변경 diff --git a/apps/fo/app/pages/(with-layout)/underWaterInput/page.tsx b/apps/fo/app/pages/(with-layout)/underWaterInput/page.tsx index 52184d591..ede01fadd 100644 --- a/apps/fo/app/pages/(with-layout)/underWaterInput/page.tsx +++ b/apps/fo/app/pages/(with-layout)/underWaterInput/page.tsx @@ -22,7 +22,6 @@ export default function UnderWaterInput() { ,UnderWaterSearch ,initializeCommonData ,searchUnderWaterList - ,resetExcelList } = useFoStore((state) => state) || { Common: { isInit: false, selectUpdownData: [], selectOperationData: [], selectSidoData: [], selectSigunData: [], selectRegionData: [], selectUnitWaterShedData: [], selectMiddleRegionData: [], selectEnvPartData: [], selectSearchYear: [{ val: "0" }], btnClass: "" }, UnderWater: { underWaterList: [], underWaterCount: 0, excelList: [], upHeadList1: [], upHeadList2: [], upHeadList3: [] }, @@ -94,6 +93,8 @@ export default function UnderWaterInput() { const [operationValue, setOperationValue] = useState("0"); // 조회기간 const [searchYear, setSearchYear] = useState(selectSearchYear[0]?.val || "0"); + // 엑셀다운로드 트리거 + const [isMount, setIsMount] = useState(false); /** * 페이지 함수 @@ -135,14 +136,15 @@ export default function UnderWaterInput() { * useEffect */ useEffect(() => { - resetExcelList(); if (!isInit) { initializeCommonData(); searchUnderWaterList(1, 10, selectSearchYear[0]?.val || "0"); } - }, [isInit, initializeCommonData, searchUnderWaterList, selectSearchYear, resetExcelList]); + }, [isInit, initializeCommonData, searchUnderWaterList, selectSearchYear]); useEffect(() => { + if(!isMount) return ; + const timer = setTimeout(() => { if (excelList && excelList.length > 0) { downloadExcel({ @@ -152,9 +154,9 @@ export default function UnderWaterInput() { headerRange: 'A1:V1' }); } - }, 500); // 약간의 지연을 두고 다운로드 실행 + }, 1000); // 약간의 지연을 두고 다운로드 실행 return () => clearTimeout(timer); - }, [excelList]); + }, [excelList, isMount]); /** * 콜백함수 @@ -193,11 +195,8 @@ export default function UnderWaterInput() { // 엑셀다운로드 const excelDownload = useCallback(() => { searchFacilityPrecipitation(ref.current, 1, underWaterCount); - - setTimeout(() => { - resetExcelList(); - }, 1000); // 약간의 지연을 두고 다운로드 실행 - }, [resetExcelList, searchFacilityPrecipitation, underWaterCount]); + setIsMount(true); + }, [searchFacilityPrecipitation, underWaterCount]); // 페이지 변경 const handlePageChange = useCallback((page: number) => { diff --git a/apps/fo/app/store/rdi/index.tsx b/apps/fo/app/store/rdi/index.tsx index c22859043..cf0716c5f 100644 --- a/apps/fo/app/store/rdi/index.tsx +++ b/apps/fo/app/store/rdi/index.tsx @@ -21,16 +21,16 @@ export type RdiProps = { lclgvrnmntNm : string, daddr : string, fctCpc : number, - summerMinBodValue : number, + summerMinBodValue : number | '', inflAmnt : number | string | '', - winterAvgBodLowRainfall : number, - rdiiValue : number, - infoRnDay : number, + winterAvgBodLowRainfall : number | '', + rdiiValue : number | '', + infoRnDay : number | '', rgnNm : string, rvbsn : string, lgbsn : string, mdmbsn : string, - rainEffectDay : number, // 강우영향일 + rainEffectDay : number | '', // 강우영향일 chungchunFlow : string | number | '', rainCalInflAmnt : string | number | '', properYn : string, @@ -174,6 +174,18 @@ const fetchRdiList = async (pageNo: number, pageSize: number, searchYear: string const tailStr = partCd ? '/'+ partCd + '/' + nameCd + '/' + fcltCpct + '/' + operationValue + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : ''; const url = '/rdii/list/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr; const res = await getFetch('fo', url); + + res.data.forEach((item: RdiProps) => { + item.fctCpc = item.fctCpc && item.fctCpc > 0 ? Number(item.fctCpc.toString().trim()) : 0; + item.summerMinBodValue = ''; + item.inflAmnt = ''; + item.winterAvgBodLowRainfall = ''; + item.rdiiValue = ''; + item.infoRnDay = ''; + item.properYn = ''; + item.sido = item.lclgvrnmntNm.split(' ')[0]?.toString() || ''; + item.sigungo = item.lclgvrnmntNm.split(' ')[1]?.toString() || ''; + }); return { data: res.data, count: res.count } }catch(error){ console.log(error); @@ -185,6 +197,7 @@ const fetchRdiList = async (pageNo: number, pageSize: number, searchYear: string * Store - expaort ====================================*/ export type RdiActions = { + resetExcelList: () => void; searchRdiList: (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => void; } @@ -231,10 +244,32 @@ const dataFormSetting=(item : RdiProps)=> { // Add type definitions for state setter function type SetStateFunction = (state: RdiType | ((state: RdiType) => RdiType)) => void; +// 병렬 처리 제한용 헬퍼 +async function processInBatches(items, batchSize, callback) { + const results:any = []; + for (let i = 0; i < items.length; i += batchSize) { + const batch = items.slice(i, i + batchSize); + const batchResults = await Promise.all(batch.map(callback)); + results.push(...batchResults); + } + return results; +} + export type RdiStore = RdiType & RdiActions; export const rdiReducer:(set:SetStateFunction)=>RdiActions=(set: SetStateFunction) => { return { + resetExcelList: () => { + console.log('reset!!!!!'); + set((state: RdiType) => ({ + ...state, + Rdi: { + ...state.Rdi, + excelList: [], + }, + })); + }, + searchRdiList: async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { set((state: RdiType) => ({ ...state, @@ -245,8 +280,31 @@ export const rdiReducer:(set:SetStateFunction)=>RdiActions=(set: SetStateFunctio })); const res = await fetchRdiList(pageNo, pageSize, searchYear, partCd, nameCd, fcltCpct, operationValue, fcltyNm); + if(pageSize>=res.count){ + set((state: RdiType) => ({ + ...state, + Rdi: { + ...state.Rdi, + excelList: [], + isLoading: true, + }, + })); + } + else{ + set((state: RdiType) => ({ + ...state, + Rdi: { + ...state.Rdi, + rdiList: res.data, + excelList: [], + rdiCount: res.count, + isLoading: true, + }, + })); + } + const data = res.data; - const reData = await Promise.all(data.map(async (item: RdiProps) => { + const reData = await processInBatches(data, 15, async (item: any, index: number) => { const bscFctCd = item.bscFctCd; const res2 = await getFetch('fo', '/fclt/flow/' + bscFctCd + '/' + searchYear) as FlowResponse; const calData = CalcRainEffect(res2.data, 'SUNNY'); @@ -266,8 +324,8 @@ export const rdiReducer:(set:SetStateFunction)=>RdiActions=(set: SetStateFunctio avgChungcunSumFlow = chungcunSumFlow&&chungcunSumFlow>0?chungcunSumFlow / calData.length : 0; item.chungchunFlow = avgChungcunSumFlow; item.rainCalInflAmnt = Number(avgChungcunSumFlow) + Number(item.rdiiValue); - return item; - })); + return {...item, index:index} + }); const excelDownList: ExcelType[] = []; const rtnData = reData.map((item: RdiProps) => { @@ -339,11 +397,6 @@ export const rdiReducer:(set:SetStateFunction)=>RdiActions=(set: SetStateFunctio const roundMinSummerBodInfl = Number(roundNumber(minSummerBodInfl, 0).toString().replaceAll(',','')); const roundMinSummerBod = Number(roundNumber(minSummerBod, 1).toString().replaceAll(',','')); const roundWinterBodSunny = Number(roundNumber(winterBodSunny, 1).toString().replaceAll(',','')); - - console.log('bsccd : ', item.bscFctCd); - console.log('roundMinSummerBodInfl : ',roundMinSummerBodInfl); - console.log('roundMinSummerBod : ',roundMinSummerBod); - console.log('roundWinterBodSunny : ',roundWinterBodSunny); const winterBodAvg = Number(roundNumber((roundWinterBodSunny / winterCnt), 1).toString().replaceAll(',','')); // 하절기(6~8월) 중 최저 유입 BOD일때 유입유량 x ((동절기 청천시 유입BOD - 하절기(6~8월) 중 최저 유입BOD) / (동절기 청천시 유입BOD)) @@ -374,7 +427,7 @@ export const rdiReducer:(set:SetStateFunction)=>RdiActions=(set: SetStateFunctio dataFormSetting(item); }) - if(pageSize>5000){ + if(pageSize>=res.count){ rtnData.forEach((item: RdiProps) => { excelDownList.push({ 시설코드: item.bscFctCd, @@ -400,7 +453,7 @@ export const rdiReducer:(set:SetStateFunction)=>RdiActions=(set: SetStateFunctio } set((state: RdiType) => ({ ...state, - Rdi: pageSize<5000?{ + Rdi: pageSize * Store - expaort ====================================*/ export type SewageOccurActions = { - resetExcelList: () => void; searchSewageOccurList: (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => void; searchSewageOccurDetailList: (bscFctCd: string, searchYear: string, startYearMonth?: string, endYearMonth?: string) => void; } @@ -230,16 +229,6 @@ export type FlowResponse = { export const sewageOccurReducer:(set:any)=>SewageOccurActions=(set: any) => { return { - resetExcelList: () => { - set((state: SewageOccurType) => ({ - ...state, - SewageOccur: { - ...state.SewageOccur, - excelList: [], - }, - })); - }, - searchSewageOccurList: async (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { const facilityInitData = await fetchFacilityListInit(pageNo, pageSize, part, nameCd, fcltCpct, operationValue, fcltyNm); if(pageSize < facilityInitData.count){ @@ -269,7 +258,10 @@ export const sewageOccurReducer:(set:any)=>SewageOccurActions=(set: any) => { const calData = await processInBatches(fcltylist, 20, async (item: SewageOccurProps, index: number) => { const bscFctCd = item.bscFctCd; - const res = await fetchSweageOccurListFlow(bscFctCd, searchYear); + const res = await fetchSweageOccurListFlow(bscFctCd, searchYear).catch(() => { + console.log('Error fetching flow data for facility code:', bscFctCd); + return { data: [], count: 0 }; + }); item.sidoNm = item.sidoNm || ''; item.sggNm = item.sggNm || ''; diff --git a/apps/fo/app/store/underWaterInput/index.tsx b/apps/fo/app/store/underWaterInput/index.tsx index a3238b306..dd4f7f6b0 100644 --- a/apps/fo/app/store/underWaterInput/index.tsx +++ b/apps/fo/app/store/underWaterInput/index.tsx @@ -213,7 +213,6 @@ const fetchUnderWaterListFlow = async(fcltyCd : string, searchYear : string) => * Store - expaort ====================================*/ export type UnderWaterActions = { - resetExcelList: () => void; searchUnderWaterList: (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => void; unserWaterModal:(bscFctCd:string, searchYear:string, startYm:string, endYm:string)=>void } @@ -289,16 +288,6 @@ export type UnderWaterStore = UnderWaterType & UnderWaterActions; export const underWaterReducer:(set:(fn: (state: UnderWaterType) => UnderWaterType) => void)=>UnderWaterActions=(set: (fn: (state: UnderWaterType) => UnderWaterType) => void) => { return { - resetExcelList: () => { - set((state: UnderWaterType) => ({ - ...state, - UnderWater: { - ...state.UnderWater, - excelList : [], - }, - })); - }, - searchUnderWaterList: async (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => { const facilityInitData = await fetchFacilityListInit(pageNo, pageSize, part, nameCd, fcltCpct, operationValue, fcltyNm); @@ -328,7 +317,10 @@ export const underWaterReducer:(set:(fn: (state: UnderWaterType) => UnderWaterTy const calData = await processInBatches(fcltylist, 20, async (item: any, index: number) => { const bscFctCd = item.bscFctCd; - const res = await fetchUnderWaterListFlow(bscFctCd, searchYear); + const res = await fetchUnderWaterListFlow(bscFctCd, searchYear).catch((error) => { + console.log('Error fetching flow data for facility code:', bscFctCd, error); + return { data: [], count: 0 }; // 에러 발생 시 빈 데이터 반환 + }); item.sidoNm = item.sido || ''; item.sggNm = item.gugun || '';