From 443f9e32f632de821c790bf93bbddc79a173976b Mon Sep 17 00:00:00 2001 From: swcho Date: Tue, 19 Aug 2025 13:11:50 +0900 Subject: [PATCH 1/5] =?UTF-8?q?fix:=20=ED=95=98=EC=88=98=EA=B4=80=EB=A1=9C?= =?UTF-8?q?,=20=ED=95=98=EC=88=98=EB=A7=A8=ED=99=80=20cql=20=ED=95=84?= =?UTF-8?q?=ED=84=B0=20=EC=97=90=EB=9F=AC=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx b/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx index ce495aa92..124871745 100644 --- a/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx +++ b/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx @@ -297,7 +297,7 @@ const SearchModal: React.FC = ({ const pplCqlFilters = () => { const cqlFilters: string[] = []; // 추후 작성 예정 - if (var1 !== '0') cqlFilters.push(`usg_cd='${var1}'`); + if (var1) cqlFilters.push(`usg_cd='${var1}'`); if (var2) cqlFilters.push(`mqlt_cd='${var2}'`); if (ctpvCd !== '0' && sggCd !== '0') cqlFilters.push(`pbadms_emd_cd LIKE '${sggCd}%'`); else if (ctpvCd !== '0') cqlFilters.push(`pbadms_emd_cd LIKE '${ctpvCd.trim()}%'`); @@ -396,7 +396,7 @@ const SearchModal: React.FC = ({ cqlFilters.push( `instl_ymd >= '${startDate}' and instl_ymd <= '${endDate}'`, ); - if (var1 !== '0') cqlFilters.push(`usg_cd='${var1}'`); + if (var1) cqlFilters.push(`usg_cd='${var1}'`); // 구경(mm) 범위 if (start1 && end1) cqlFilters.push(`swage_mhL_diam_vl BETWEEN ${start1} AND ${end1}`); else if (start1) cqlFilters.push(`swage_mhL_diam_vl >= ${start1}`); From d676d726f2c5b178a4d64b18a53d0861e3f30c79 Mon Sep 17 00:00:00 2001 From: swcho Date: Tue, 19 Aug 2025 15:23:36 +0900 Subject: [PATCH 2/5] =?UTF-8?q?feat:=20searchModal.tsx=20=EA=B8=B0?= =?UTF-8?q?=EB=B3=B8=EC=A0=95=EB=B3=B4,=20=EC=9A=B4=EC=98=81=EC=A0=95?= =?UTF-8?q?=EB=B3=B4=20=ED=8C=8C=EC=9D=BC=20=EB=8B=A4=EC=9A=B4=EB=A1=9C?= =?UTF-8?q?=EB=93=9C=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../map/components/common/table.ts | 66 +++ .../map/searchModal/searchModal.tsx | 446 ++++++++++++++++-- 2 files changed, 485 insertions(+), 27 deletions(-) diff --git a/apps/sn/app/(map-layout)/map/components/common/table.ts b/apps/sn/app/(map-layout)/map/components/common/table.ts index 92b0e3c4a..531c72bc6 100644 --- a/apps/sn/app/(map-layout)/map/components/common/table.ts +++ b/apps/sn/app/(map-layout)/map/components/common/table.ts @@ -55,6 +55,20 @@ export const MsSwagePplCol2 = [ // { id: 'usgCd', title: '용도코드' }, // { id: 'lyrTypeNm', title: '레이어타입명' }, ] +export const MsSwagePplExcelCol = { + 'fcltMngNo': '시설 관리번호', + 'instMngNo': '기관 관리번호', + 'ctpvNm': '시도', + 'sggNm': '시군구', + 'swtrmt_zone_nm': '처리구역', + 'usgCd': '용도', + 'pdmtVl': '관경(mm)', + 'prlgVl': '연장(m)', + 'tmptDpth': '시점 깊이(m)', + 'tmptMhlMngNo': '시점맨홀 관리번호', + 'scendDpth': '종점 깊이(m)', + 'scendMhlMngNo': '종점맨홀 관리번호', +}; // 하수관로 용도 코드표 export const MS_SWAGE_PPL_USE_CODES = [ { val: "SBA000", text: "미분류" }, @@ -339,6 +353,18 @@ export const SwtrmtFcltCol2 = [ // { id: 'bsnNm', title: '유역명' }, // { id: 'obsrvnSptCd', title: '관측지점코드' }, ]; +export const SwtrmtFcltExcelCol = { + 'swtrmtFcltNm': '하수처리시설명', + 'fcltCpct': '시설 용량(㎥/d)', + 'rgnExpln': '지역설명', + 'oprtngStrtYmd': '가동개시 일자', + 'laeNm': '지자체명', + 'sareaNm': '권역명', + 'rvrNm': '하천명', + 'dschgWtstNm': '방류수계명', + 'swtrmtPpltnCnt': '하수 처리 인구수(인)', + 'rprsMsrcCn': '대표 공법 내용' +}; // 하수처리구역 export const SwtrmtPrcsZoneCol = [ @@ -362,6 +388,12 @@ export const SwtrmtPrcsZoneCol2 = [ { id: 'sggNm', title: '시군구' }, { id: 'swtrmtFcltNm', title: '하수처리시설명' }, ]; +export const SwtrmtPrcsZoneExcelCol = { + 'swtrmtZoneNm': '하수처리구역명', + 'ctpvNm': '시도', + 'sggNm': '시군구', + 'swtrmtFcltNm': '하수처리시설명' +}; // 하수맨홀 export const MsSwageMhlCol = [ @@ -411,6 +443,18 @@ export const MsSwageMhlCol2 = [ { id: 'dpthlVl', title: '심도(m)' }, // { id: 'instMngNo', title: '기관관리번호' } ]; +export const MsSwageMhlExcelCol = { + 'fcltMngNo': '시설관리번호', + 'ctpvNm': '시도', + 'sggNm': '시군구', + 'instlYmd': '설치일자', + 'usgCd': '용도', + 'swageMhlDiamVl': '하수맨홀구경(mm)', + 'wdthLen': '가로길이(m)', + 'vrtcLen': '세로길이(m)', + 'altdVl': '표고(EL.m)', + 'dpthlVl': '심도(m)' +}; // 하수맨홀 뚜껑재질 코드표 export const MS_SWAGE_MHL_CODES = [ { val: "SBC000", text: "미분류" }, @@ -519,6 +563,18 @@ export const ExclnSewagePumpCol2 = [ { id: 'exclnWpmpAbltVl', title: '우수양수능력(m²/d)' }, // { id: 'instMngNo', title: '기관관리번호' }, ] +export const ExclnSewagePumpExcelCol = { + 'fcltMngNo': '시설관리번호', + 'ctpvNm': '시도', + 'sggNm': '시군구', + 'swagePmstnNm': '하수펌프장명', + 'instlYmd': '설치일자', + 'dailyPrcsCpct': '일일처리용량(㎥)', + 'maxImpndCpct': '최대저수용량(㎥)', + 'altdVl': '표고(EL.m)', + 'wtlvVl': '수위(m)', + 'exclnWpmpAbltVl': '우수양수능력(m²/d)' +}; // 오수펌프장 export const DrtwSewagePumpCol = [ // { id: 'fid', title: 'FID' }, @@ -606,6 +662,16 @@ export const stofcbrCol2 = [ { id: 'swageMhlMngNo', title: '하수맨홀관리번호' }, { id: 'drtnAngl', title: '방향각도(°)' }, ] +export const stofcbrExcelCol = { + 'fcltMngNo': '시설관리번호', + 'mapshtNo': '도엽번호', + 'ctpvNm': '시도', + 'sggNm': '시군구', + 'instlYmd': '설치일자', + 'worflwWallHgt': '월류벽높이(m)', + 'swageMhlMngNo': '하수맨홀관리번호', + 'drtnAngl': '방향각도(°)' +}; // 중점우수토실 측정지점 export const untrtSwgRainCol1 = [ diff --git a/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx b/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx index 124871745..4f5070850 100644 --- a/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx +++ b/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx @@ -1,9 +1,13 @@ -import LoadingOverlay from '@/components/ui/LoadingBar'; -import { UiModal, UITab, UiTable } from '@common/business_components/ui'; -import axios from 'axios'; import React, { useEffect, useMemo, useState } from 'react'; -import { ExclnSewagePumpCol2, MsSwageMhlCol2, MsSwagePplCol2, stofcbrCol2, SwtrmtFcltCol2, SwtrmtPrcsZoneCol2 } from '../components/common/table'; -import { MS_SWAGE_PPL_CODES, MS_SWAGE_PPL_USE_CODES, MS_SWAGE_MHL_CODES, MS_SWAGE_MHL_USE_CODES } from '../components/common/table'; +import LoadingOverlay from '@/components/ui/LoadingBar'; +import { UiModal, UITab, UiTable, downloadExcelWithKoreanHeaders } from '@common/business_components/ui'; +import { Button } from '@common/components/ui'; +import axios from 'axios'; +import { + ExclnSewagePumpCol2, MsSwageMhlCol2, MsSwagePplCol2, stofcbrCol2, SwtrmtFcltCol2, SwtrmtPrcsZoneCol2, + MS_SWAGE_PPL_CODES, MS_SWAGE_PPL_USE_CODES, MS_SWAGE_MHL_CODES, MS_SWAGE_MHL_USE_CODES, + MsSwagePplExcelCol, SwtrmtFcltExcelCol, SwtrmtPrcsZoneExcelCol, MsSwageMhlExcelCol, ExclnSewagePumpExcelCol, stofcbrExcelCol +} from '../components/common/table'; import { getData } from "@/components/lib/api"; import BasicTab, { BASIC_TYPES } from './basicTab'; import OperationTab, { operationCleaningTableHeadlist, operationInspectionTableHeadlist, operationMaintenanceTableHeadlist, operationTableHeadlist, WORK_TYPES } from './operationTab'; @@ -90,12 +94,12 @@ const SearchModal: React.FC = ({ const dynamicHeadlist: { id: string, title: string }[] = useMemo(() => { if (activeTab === 'basic') { - if (basicType === 'tb_ms_swage_ppl') return MsSwagePplCol2; - if (basicType === 'tb_il_swage_prcs_fclt') return SwtrmtFcltCol2; - if (basicType === 'tb_ms_swage_prcs_zone') return SwtrmtPrcsZoneCol2; - if (basicType === 'tb_ms_swage_mhl') return MsSwageMhlCol2; - if (basicType === 'tb_ms_swage_pmstn') return ExclnSewagePumpCol2; - if (basicType === 'tb_ms_stofcbr') return stofcbrCol2; + if (basicType === 'tb_ms_swage_ppl') return MsSwagePplCol2; // 하수관로 + if (basicType === 'tb_il_swage_prcs_fclt') return SwtrmtFcltCol2; // 하수처리시설 + if (basicType === 'tb_ms_swage_prcs_zone') return SwtrmtPrcsZoneCol2; // 하수처리구역 + if (basicType === 'tb_ms_swage_mhl') return MsSwageMhlCol2; // 하수맨홀 + if (basicType === 'tb_ms_swage_pmstn') return ExclnSewagePumpCol2; // 하수펌프장 + if (basicType === 'tb_ms_stofcbr') return stofcbrCol2; // 우수토실 return []; } @@ -198,6 +202,7 @@ const SearchModal: React.FC = ({ if (cqlFilters.length > 0) queryParams.append('cqlFiter', cqlFilters.join(' and ')); const res = await axios.get(`${API_URL}/v1/fclts?${queryParams.toString()}`); + console.log('api:', `${API_URL}/v1/fclts?${queryParams.toString()}`); // 🎯 각 아이템을 개별적으로 포맷팅하면서 일련번호 추가 const dataWithRowNumber = (res.data.data || []).map((item: any, index: number) => { @@ -208,7 +213,7 @@ const SearchModal: React.FC = ({ let sggCd = item?.sggNm ?? formattedItem?.sggNm ?? ''; if (!ctpvCd || !sggCd) { - const code = String(item?.pbadmsEmdCd ?? formattedItem?.pbadmsEmdCd ?? '').trim(); + const code = String(item?.pbadmsEmdCd ?? formattedItem?.pbadmsEmdCd ?? '')?.trim(); if (!ctpvCd && code) ctpvCd = code.slice(0, 2); if (!sggCd && code) sggCd = code.slice(0, 5); } @@ -265,6 +270,7 @@ const SearchModal: React.FC = ({ const params = new URLSearchParams(RequestDTO).toString(); const res = await getData(`v1/bizInfo?${params}`); + console.log('운영정보 사업 데이터:', res.content); const mappedContent = (res.content || []).map((item: any, index: number) => { const formattedItem = formatDisplayData(item); @@ -283,9 +289,11 @@ const SearchModal: React.FC = ({ } setShowTable(true); + setIsResetSelection(false); } catch (err) { console.error('API ERROR:', err); setTableData([]); + setIsResetSelection(false); } finally { setIsLoading(false); // 로딩 종료 setIsResetSelection(false); @@ -300,7 +308,7 @@ const SearchModal: React.FC = ({ if (var1) cqlFilters.push(`usg_cd='${var1}'`); if (var2) cqlFilters.push(`mqlt_cd='${var2}'`); if (ctpvCd !== '0' && sggCd !== '0') cqlFilters.push(`pbadms_emd_cd LIKE '${sggCd}%'`); - else if (ctpvCd !== '0') cqlFilters.push(`pbadms_emd_cd LIKE '${ctpvCd.trim()}%'`); + else if (ctpvCd !== '0') cqlFilters.push(`pbadms_emd_cd LIKE '${ctpvCd?.trim()}%'`); // 설치년도 범위 const startDate = getDateString( yearFilter.start, @@ -368,8 +376,8 @@ const SearchModal: React.FC = ({ // 하수처리구역용 CQL 필터 생성 const zoneCqlFilters = () => { const cqlFilters: string[] = []; - if (ctpvCd !== '' && ctpvCd !== '0') cqlFilters.push(`ctpv_cd='${ctpvCd.trim()}'`); - if (sggCd !== '' && sggCd !== '0') cqlFilters.push(`sgg_cd='${sggCd.trim()}'`); + if (ctpvCd !== '' && ctpvCd !== '0') cqlFilters.push(`ctpv_cd='${ctpvCd?.trim()}'`); + if (sggCd !== '' && sggCd !== '0') cqlFilters.push(`sgg_cd='${sggCd}'`); if (var1) cqlFilters.push(`swtrmt_zone_nm ILIKE '%${var1}%'`); if (var2) cqlFilters.push(`swtrmt_fclt_nm ILIKE '%${var2}%'`); return cqlFilters; @@ -379,7 +387,7 @@ const SearchModal: React.FC = ({ const cqlFilters: string[] = []; // 시도, 시군구 if (ctpvCd !== '0' && sggCd !== '0') cqlFilters.push(`pbadms_emd_cd LIKE '${sggCd}%'`); - else if (ctpvCd !== '0') cqlFilters.push(`pbadms_emd_cd LIKE '${ctpvCd.trim()}%'`); + else if (ctpvCd !== '0') cqlFilters.push(`pbadms_emd_cd LIKE '${ctpvCd?.trim()}%'`); // // 설치년도 범위 const startDate = getDateString( yearFilter.start, @@ -409,7 +417,7 @@ const SearchModal: React.FC = ({ const cqlFilters: string[] = []; // 시도, 시군구 if (ctpvCd !== '0' && sggCd !== '0') cqlFilters.push(`pbadms_emd_cd LIKE '${sggCd}%'`); - else if (ctpvCd !== '0') cqlFilters.push(`pbadms_emd_cd LIKE '${ctpvCd.trim()}%'`); + else if (ctpvCd !== '0') cqlFilters.push(`pbadms_emd_cd LIKE '${ctpvCd?.trim()}%'`); // 설치년도 범위 const startDate = getDateString( yearFilter.start, @@ -444,7 +452,7 @@ const SearchModal: React.FC = ({ const cqlFilters: string[] = []; // 시도, 시군구 if (ctpvCd !== '0' && sggCd !== '0') cqlFilters.push(`pbadms_emd_cd LIKE '${sggCd}%'`); - else if (ctpvCd !== '0') cqlFilters.push(`pbadms_emd_cd LIKE '${ctpvCd.trim()}%'`); + else if (ctpvCd !== '0') cqlFilters.push(`pbadms_emd_cd LIKE '${ctpvCd?.trim()}%'`); // 설치년도 범위 const startDate = getDateString( yearFilter.start, @@ -508,18 +516,23 @@ const SearchModal: React.FC = ({ RequestDTO.workTypes = JSON.parse(workType.replace(/'/g, '"')); const res = await getData(`v1/bizInfo/fclts?${new URLSearchParams(RequestDTO).toString()}`); + console.log('운영정보 cellClick 데이터1:', res); + console.log('운영정보 cellClick 데이터2:', res.data); + const mappedData = (res.data || []).map((item: any, detailIndex: number) => { const formattedItem = formatDisplayData(item); - const usgCdObj = MS_SWAGE_PPL_USE_CODES.find(code => code.val === item.usgCd.trim()); - const mqltCdObj = MS_SWAGE_PPL_CODES.find(code => code.val === item.mqltCd.trim()); + const usgCdObj = MS_SWAGE_PPL_USE_CODES.find(code => code.val === item?.usgCd?.trim()); + const mqltCdObj = MS_SWAGE_PPL_CODES.find(code => code.val === item?.mqltCd?.trim()); return { ...formattedItem, rowNumber: (detailCurPage - 1) * OPERATION_TABLE_SIZE + detailIndex + 1, usgCd: usgCdObj ? usgCdObj.text : item.usgCd, mqltCd: mqltCdObj ? mqltCdObj.text : item.mqltCd, + swtrmt_zone_nm: item.swtrmt_zone_nm ? item.swtrmt_zone_nm : '-', }; }); + console.log('운영정보 cellClick 데이터:', mappedData); setDetailTableData(mappedData); setDetailTableCount(res.count || 0); @@ -547,15 +560,17 @@ const SearchModal: React.FC = ({ const mappedData = (res.data || []).map((item: any, detailIndex: number) => { const formattedItem = formatDisplayData(item); - const usgCdObj = MS_SWAGE_PPL_USE_CODES.find(code => code.val === item.usgCd.trim()); - const mqltCdObj = MS_SWAGE_PPL_CODES.find(code => code.val === item.mqltCd.trim()); + const usgCdObj = MS_SWAGE_PPL_USE_CODES.find(code => code.val === item?.usgCd?.trim()); + const mqltCdObj = MS_SWAGE_PPL_CODES.find(code => code.val === item?.mqltCd?.trim()); return { ...formattedItem, rowNumber: (detailCurPage - 1) * OPERATION_TABLE_SIZE + detailIndex + 1, usgCd: usgCdObj ? usgCdObj.text : item.usgCd, mqltCd: mqltCdObj ? mqltCdObj.text : item.mqltCd, + swtrmt_zone_nm: item.swtrmt_zone_nm ? item.swtrmt_zone_nm : '-', }; }); + console.log('운영정보 PageNation 데이터:', mappedData); setDetailTableData(mappedData); setDetailTableCount(res.count || 0); @@ -567,6 +582,377 @@ const SearchModal: React.FC = ({ } }; + // 기본정보 Excel 다운로드 핸들러 + const BasicExcelDownload = async () => { + setIsLoading(true); + + try { + const queryParams = new URLSearchParams({ + lyrTypeNm: basicType, + size: '10000', + }); + + let cqlFilters: string[] = []; + let headerMapping: { [key: string]: string } = {}; + let headerRange: string = "A1:F1"; + + switch (basicType) { + case 'tb_ms_swage_ppl': { + // 하수관로 + cqlFilters = pplCqlFilters() + headerMapping = MsSwagePplExcelCol; + headerRange = "A1:F1"; + break; + } + case 'tb_il_swage_prcs_fclt': { + // 하수처리시설 + cqlFilters = facilityCqlFilters(); + headerMapping = SwtrmtFcltExcelCol; + headerRange = "A1:J1"; + break; + } + case 'tb_ms_swage_prcs_zone': { + // 하수처리구역 + cqlFilters = zoneCqlFilters(); + headerMapping = SwtrmtPrcsZoneExcelCol; + headerRange = "A1:D1"; + break; + } + case 'tb_ms_swage_mhl': { + // 하수맨홀 + cqlFilters = mhCqlFilters(); + headerMapping = MsSwageMhlExcelCol; + headerRange = "A1:J1"; + break; + } + case 'tb_ms_swage_pmstn': { + // 하수펌프장 + cqlFilters = psmtnCqlFilters(); + headerMapping = ExclnSewagePumpExcelCol; + headerRange = "A1:J1"; + break; + } + case 'tb_ms_stofcbr': { + // 우수토실 + cqlFilters = otflCqlFilters(); + headerMapping = stofcbrExcelCol; + headerRange = "A1:H1"; + break; + } + } + + if (cqlFilters.length > 0) queryParams.append('cqlFiter', cqlFilters.join(' and ')); + + const res = await axios.get(`${API_URL}/v1/fclts?${queryParams.toString()}`); + console.log('res.data.data', res.data.data); + + + + + // const mhlCdMap = new Map(MS_SWAGE_MHL_CODES.map(code => [code.val?.trim(), code.text])); + const mhlUsgCdMap = new Map(MS_SWAGE_MHL_USE_CODES.map(code => [code.val?.trim(), code.text])); + const pplUsgCdMap = new Map(MS_SWAGE_PPL_USE_CODES.map(code => [code.val?.trim(), code.text])); + const ctpvMap = new Map(ctpv.map(code => [code.val?.trim(), code.text])); + const sggMap = new Map(sgg.map(code => [code.val?.trim(), code.text])); + + const mappedContent = (res.data.data || []).map((item: any) => { + const formattedItem = formatDisplayData(item); + + let ctpvCd = item?.ctpvCd ?? formattedItem?.ctpvCd ?? ''; + let sggCd = item?.sggCd ?? formattedItem?.sggCd ?? ''; + + if (!ctpvCd || !sggCd) { + const code = String(item?.pbadmsEmdCd ?? formattedItem?.pbadmsEmdCd ?? '')?.trim(); + if (!ctpvCd && code) ctpvCd = code.slice(0, 2); + if (!sggCd && code) sggCd = code.slice(0, 5); + } + + // trim 한 번만 수행 + const trimmedUsgCd = item?.usgCd?.trim(); + // const trimmedMqltCd = item?.mqltCd?.trim(); + + switch (basicType) { + case 'tb_ms_swage_ppl': { + // 하수관로 + return { + fcltMngNo: formattedItem.fcltMngNo ?? item.fcltMngNo ?? '-', + instMngNo: formattedItem.instMngNo ?? item.instMngNo ?? '-', + ctpvNm: ctpvMap.get(ctpvCd) ?? item.ctpvNm ?? '-', + sggNm: sggMap.get(sggCd) ?? item.sggNm ?? '-', + swtrmt_zone_nm: formattedItem.swtrmt_zone_nm ?? item.swtrmt_zone_nm ?? '-', + usgCd: pplUsgCdMap.get(trimmedUsgCd) || item.usgCd || formattedItem.usgCd || '-', + pdmtVl: formattedItem.pdmtVl ?? item.pdmtVl ?? '-', + prlgVl: formattedItem.prlgVl ?? item.prlgVl ?? '-', + tmptDpth: formattedItem.tmptDpth ?? item.tmptDpth ?? '-', + tmptMhlMngNo: formattedItem.tmptMhlMngNo ?? item.tmptMhlMngNo ?? '-', + scendDpth: formattedItem.scendDpth ?? item.scendDpth ?? '-', + scendMhlMngNo: formattedItem.scendMhlMngNo ?? item.scendMhlMngNo ?? '-', + swtrmtFcltNm: formattedItem.swtrmtFcltNm ?? item.swtrmtFcltNm ?? '-', + }; + } + case 'tb_il_swage_prcs_fclt': { + // 하수처리시설 + return { + swtrmtFcltNm: formattedItem.swtrmtFcltNm ?? item.swtrmtFcltNm ?? '-', + fcltCpct: formattedItem.fcltCpct ?? item.fcltCpct ?? '-', + rgnExpln: formattedItem.rgnExpln ?? item.rgnExpln ?? '-', + oprtngStrtYmd: formattedItem.oprtngStrtYmd ?? item.oprtngStrtYmd ?? '-', + laeNm: formattedItem.laeNm ?? item.laeNm ?? '-', + sareaNm: formattedItem.sareaNm ?? item.sareaNm ?? '-', + rvrNm: formattedItem.rvrNm ?? item.rvrNm ?? '-', + dschgWtstNm: formattedItem.dschgWtstNm ?? item.dschgWtstNm ?? '-', + swtrmtPpltnCnt: formattedItem.swtrmtPpltnCnt ?? item.swtrmtPpltnCnt ?? '-', + rprsMsrcCn: formattedItem.rprsMsrcCn ?? item.rprsMsrcCn ?? '-' + }; + } + case 'tb_ms_swage_prcs_zone': { + // 하수처리구역 + return { + swtrmtZoneNm: formattedItem.swtrmtZoneNm ?? item.swtrmtZoneNm ?? '-', + ctpvNm: ctpvMap.get(ctpvCd) ?? formattedItem.ctpvNm ?? '-', + sggNm: sggMap.get(sggCd) ?? formattedItem.sggNm ?? '-', + swtrmtFcltNm: formattedItem.swtrmtFcltNm ?? item.swtrmtFcltNm ?? '-' + }; + } + case 'tb_ms_swage_mhl': { + // 하수맨홀 + return { + fcltMngNo: formattedItem.fcltMngNo ?? item.fcltMngNo ?? '-', + ctpvNm: ctpvMap.get(ctpvCd) ?? formattedItem.ctpvNm ?? '-', + sggNm: sggMap.get(sggCd) ?? formattedItem.sggNm ?? '-', + instlYmd: formattedItem.instlYmd ?? item.instlYmd ?? '-', + usgCd: mhlUsgCdMap.get(trimmedUsgCd) || item.usgCd || formattedItem.usgCd || '-', + swageMhlDiamVl: formattedItem.swageMhlDiamVl ?? item.swageMhlDiamVl ?? '-', + wdthLen: formattedItem.wdthLen ?? item.wdthLen ?? '-', + vrtcLen: formattedItem.vrtcLen ?? item.vrtcLen ?? '-', + altdVl: formattedItem.altdVl ?? item.altdVl ?? '-', + dpthlVl: formattedItem.dpthlVl ?? item.dpthlVl ?? '-' + }; + } + case 'tb_ms_swage_pmstn': { + // 하수펌프장 + return { + fcltMngNo: formattedItem.fcltMngNo ?? item.fcltMngNo ?? '-', + ctpvNm: ctpvMap.get(ctpvCd) ?? formattedItem.ctpvNm ?? '-', + sggNm: sggMap.get(sggCd) ?? formattedItem.sggNm ?? '-', + swagePmstnNm: formattedItem.swagePmstnNm ?? item.swagePmstnNm ?? '-', + instlYmd: formattedItem.instlYmd ?? item.instlYmd ?? '-', + dailyPrcsCpct: formattedItem.dailyPrcsCpct ?? item.dailyPrcsCpct ?? '-', + maxImpndCpct: formattedItem.maxImpndCpct ?? item.maxImpndCpct ?? '-', + altdVl: formattedItem.altdVl ?? item.altdVl ?? '-', + wtlvVl: formattedItem.wtlvVl ?? item.wtlvVl ?? '-', + exclnWpmpAbltVl: formattedItem.exclnWpmpAbltVl ?? item.exclnWpmpAbltVl ?? '-' + }; + } + case 'tb_ms_stofcbr': { + // 우수토실 + return { + fcltMngNo: formattedItem.fcltMngNo ?? item.fcltMngNo ?? '-', + mapshtNo: formattedItem.mapshtNo ?? item.mapshtNo ?? '-', + ctpvNm: ctpvMap.get(ctpvCd) ?? formattedItem.ctpvNm ?? '-', + sggNm: sggMap.get(sggCd) ?? formattedItem.sggNm ?? '-', + instlYmd: formattedItem.instlYmd ?? item.instlYmd ?? '-', + worflwWallHgt: formattedItem.worflwWallHgt ?? item.worflwWallHgt ?? '-', + swageMhlMngNo: formattedItem.swageMhlMngNo ?? item.swageMhlMngNo ?? '-', + drtnAngl: formattedItem.drtnAngl ?? item.drtnAngl ?? '-' + }; + } + } + }); + + // 선택된 행 데이터를 Excel 다운로드 형식으로 변환 + const excelData = mappedContent; // 배열로 감싸서 전달 + console.log('excelData', excelData); + + // 파일명 생성 (분석번호 기반) + const label = BASIC_TYPES.find((type) => type.val === basicType)?.text + const fileName = `${label} 조회 결과_${todayYear}${todayMonth}${todayDate}`; + + await downloadExcelWithKoreanHeaders({ + data: excelData as JSON[], + fileName: fileName, + sheetNameArray: [fileName], + headerRange: headerRange, + headerMapping: headerMapping + }); + setIsLoading(false); + } catch (error) { + setIsLoading(false); + console.error("Excel 다운로드 중 오류 발생:", error); + alert("Excel 다운로드 중 오류가 발생했습니다. 다시 시도해주세요."); + } + }; + + // 운영정보 Excel 다운로드 핸들러 추가 + const OperationExcelDownload = async () => { + setIsLoading(true); + + try { + const RequestDTO: Record = { + size: '1000000', // 최대 개수 백만건으로 설정 + }; + if (var1) RequestDTO.bizInfoNm = var1; + if (var2 != '0' && var2) RequestDTO.bizTypeCd = var2; + // 수행기간 범위 + const startDate = getDateString( + yearFilter.start, + '', + '', + "19000101", + ); + const endDate = getDateString( + yearFilter.end, + '', + '', + `${todayYear}${todayMonth}${todayDate}`, + ); + RequestDTO.fromflfmtEndYmd = startDate; + RequestDTO.toflfmtEndYmd = endDate; + + // JSON.parse 사용으로 문자열 -> 배열 변환 + RequestDTO.workTypes = JSON.parse(workType.replace(/'/g, '"')); + + const params = new URLSearchParams(RequestDTO).toString(); + const res = await getData(`v1/bizInfo?${params}`); + + // 헤더 매핑 정의 + const headerMapping: { [key: string]: string } = { + 'bizInfoNm': '사업명', + 'flfmtBgngYmd': '수행시작일자', + 'flfmtEndYmd': '수행종료일자', + 'flfmtYr': '수행년도', + 'bizTypeCd': '사업유형', + 'bizAmt': '사업금액(원)', + }; + + const bizTyCdMap = new Map(BIZ_TY_CD.map(code => [code.lwpoCd, code.lwpoCdNm])); + + const mappedContent = (res.content || []).map((item: any) => { + const formattedItem = formatDisplayData(item); + + return { + bizInfoNm: formattedItem.bizInfoNm ?? item.bizInfoNm ?? '', + flfmtBgngYmd: formattedItem.flfmtBgngYmd ?? item.flfmtBgngYmd ?? '', + flfmtEndYmd: formattedItem.flfmtEndYmd ?? item.flfmtEndYmd ?? '', + flfmtYr: formattedItem.flfmtYr ?? item.flfmtYr ?? '', + bizTypeCd: bizTyCdMap.get(item.bizTypeCd) || item.bizTypeCd || formattedItem.bizTypeCd || '', + bizAmt: ((v) => (v === 0 || v === '0') ? '0' : (v ?? ''))(formattedItem.bizAmt ?? item.bizAmt), + }; + }); + + // 선택된 행 데이터를 Excel 다운로드 형식으로 변환 + const excelData = mappedContent; // 배열로 감싸서 전달 + + // 파일명 생성 (분석번호 기반) + const label = WORK_TYPES.find((type) => type.val === workType)?.text + const fileName = `${label} 사업 현황_${todayYear}${todayMonth}${todayDate}`; + + await downloadExcelWithKoreanHeaders({ + data: excelData as JSON[], + fileName: fileName, + sheetNameArray: [fileName], + headerRange: "A1:F1", + headerMapping: headerMapping + }); + setIsLoading(false); + } catch (error) { + setIsLoading(false); + console.error("Excel 다운로드 중 오류 발생:", error); + alert("Excel 다운로드 중 오류가 발생했습니다. 다시 시도해주세요."); + } + }; + + + // 운영정보 Excel 다운로드 핸들러 추가 + const OperationResultExcelDownload = async () => { + setIsLoading(true); + + try { + const totalStartTime = performance.now(); // 전체 시작 시간 + + const RequestDTO: Record = { + size: '1000', // 최대 개수 백만건으로 설정 + bizId: String(detailBizId), + }; + + const apiStartTime = performance.now(); + const res = await getData(`v1/bizInfo/fclts?${new URLSearchParams(RequestDTO).toString()}`); + const apiEndTime = performance.now(); + console.log(`🚀 1. API 호출 시간: ${(apiEndTime - apiStartTime).toFixed(2)}ms`); + console.log('운영정보 결과 다운로드.data:', res.data); + + const mappingStartTime = performance.now(); + // 헤더 매핑 정의 + const headerMapping: { [key: string]: string } = { + 'fcltMngNo': '관리번호', + 'swtrmt_zone_nm': '처리구역', + 'usgCd': '용도', + 'mqltCd': '재질', + 'pdmtVl': '관경(mm)', + 'prlgVl': '연장(m)', + 'instlYmd': '설치일자', + 'strc_stts_grd': '구조적 상태등급', + 'oper_stts_grd': '운영적 상태등급', + }; + + const usgCdMap = new Map(MS_SWAGE_PPL_USE_CODES.map(code => [code.val, code.text])); + const mqltCdMap = new Map(MS_SWAGE_PPL_CODES.map(code => [code.val, code.text])); + + // 🎯 단일 반복문으로 최적화 - 각 아이템마다 필요한 필드만 직접 매핑 + const mappedData = (res.data || []).map((item: any) => { + const formattedItem = formatDisplayData(item); + + // trim 한 번만 수행 + const trimmedUsgCd = item?.usgCd?.trim(); + const trimmedMqltCd = item?.mqltCd?.trim(); + + // zone 값 한 번만 계산 + const zone = item.swtrmtZoneNm ?? formattedItem.swtrmt_zone_nm ?? item.swtrmt_zone_nm ?? ''; + + // 직접 매핑 - 반복문 없이 한 번에 객체 생성 + return { + fcltMngNo: formattedItem.fcltMngNo ?? item.fcltMngNo ?? '', + swtrmt_zone_nm: (zone === null || zone === '') ? '-' : zone, + usgCd: usgCdMap.get(trimmedUsgCd) || item.usgCd || formattedItem.usgCd || '', + mqltCd: mqltCdMap.get(trimmedMqltCd) || item.mqltCd || formattedItem.mqltCd || '', + pdmtVl: formattedItem.pdmtVl ?? item.pdmtVl ?? '', + prlgVl: formattedItem.prlgVl ?? item.prlgVl ?? '', + instlYmd: formattedItem.instlYmd ?? item.instlYmd ?? '', + strc_stts_grd: ((v) => (v === 0 || v === '0') ? '0' : (v ?? ''))(formattedItem.strc_stts_grd ?? item.strc_stts_grd), + oper_stts_grd: ((v) => (v === 0 || v === '0') ? '0' : (v ?? ''))(formattedItem.oper_stts_grd ?? item.oper_stts_grd), + }; + }); + const mappingEndTime = performance.now(); + console.log(`🔄 2. 데이터 매핑 시간: ${(mappingEndTime - mappingStartTime).toFixed(2)}ms`); + + // 선택된 행 데이터를 Excel 다운로드 형식으로 변환 + const excelData = mappedData; // 배열로 감싸서 전달 + + console.log('excelData', excelData); + + const excelStartTime = performance.now(); + // 파일명 생성 (분석번호 기반) + const label = WORK_TYPES.find((type) => type.val === workType)?.text + const fileName = `${label} 결과표_${todayYear}${todayMonth}${todayDate}`; + + await downloadExcelWithKoreanHeaders({ + data: excelData as JSON[], + fileName: fileName, + sheetNameArray: [fileName], + headerRange: "A1:I1", + headerMapping: headerMapping + }); + const excelEndTime = performance.now(); + console.log(`📊 3. Excel 다운로드 시간: ${(excelEndTime - excelStartTime).toFixed(2)}ms`); + + setIsLoading(false); + } catch (error) { + setIsLoading(false); + console.error("Excel 다운로드 중 오류 발생:", error); + alert("Excel 다운로드 중 오류가 발생했습니다. 다시 시도해주세요."); + } + }; + const renderBody = () => ( <> @@ -578,9 +964,7 @@ const SearchModal: React.FC = ({ label: tab.text }))} onValueChange={(value: string) => setActiveTab(value)} - > - - {/* 각각의 탭 영역 */} + /> {/* 기본정보 탭 */} {activeTab === 'basic' && ( = ({ open={open} /> )} - {/* */} {/* 📋 결과 리스트 */} - {/* 하수도정비기본계획보고서 탭 */} + {/* 기본정보 탭 */} {activeTab === 'basic' && (
@@ -652,6 +1035,9 @@ const SearchModal: React.FC = ({ onPageChange={setCurPage} resetPageNo={isResetPage} resetSelection={isResetSelection} + headButton={ + + } />
@@ -677,6 +1063,9 @@ const SearchModal: React.FC = ({ onPageChange={setCurPage} resetPageNo={isResetPage} resetSelection={isResetSelection} + headButton={ + + } /> @@ -700,6 +1089,9 @@ const SearchModal: React.FC = ({ onPageChange={setDetailCurPage} resetPageNo={isResetDetailPage} resetSelection={isResetDetailSelection} + headButton={ + + } /> From 4df286b0863bd5b5ca0aab2c138733f49d029947 Mon Sep 17 00:00:00 2001 From: swcho Date: Tue, 19 Aug 2025 15:24:52 +0900 Subject: [PATCH 3/5] =?UTF-8?q?feat:=20UiTable=20=EC=BB=B4=ED=8F=AC?= =?UTF-8?q?=EB=84=8C=ED=8A=B8=EC=97=90=20headButton=20props=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../business_components/src/ui/table/index.tsx | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/common/business_components/src/ui/table/index.tsx b/packages/common/business_components/src/ui/table/index.tsx index fa7c9b7fe..dcf7f4aa4 100644 --- a/packages/common/business_components/src/ui/table/index.tsx +++ b/packages/common/business_components/src/ui/table/index.tsx @@ -30,6 +30,7 @@ type UiTableProps = { boldArray?: number[]; resetSelection?: boolean; index?: number; + headButton?: React.ReactNode | undefined; }; type UiHeadProps = { @@ -53,6 +54,7 @@ export function UiTable({ boldArray = [], resetSelection = false, index = -1, + headButton = undefined, }: UiTableProps) { const [cur, setCur] = useState(1); const [idx, setIdx] = useState(index); @@ -100,9 +102,16 @@ export function UiTable({ return ( <> {headName ? ( -
-

{headName}

-
+ headButton ? ( +
+

{headName}

+ <>{headButton} +
+ ) : ( +
+

{headName}

+
+ ) ) : ( <> )} From 42c94e8b43db9c6193292a1fd103b6cfc2e98af4 Mon Sep 17 00:00:00 2001 From: swcho Date: Tue, 19 Aug 2025 16:08:24 +0900 Subject: [PATCH 4/5] =?UTF-8?q?feat:=20=ED=8C=8C=EC=9D=BC=20=EB=8B=A4?= =?UTF-8?q?=EC=9A=B4=EB=A1=9C=EB=93=9C=EC=95=88=EB=82=B4=20alert=20?= =?UTF-8?q?=EB=B0=8F=20toast=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../map/searchModal/searchModal.tsx | 681 ++++++++++-------- 1 file changed, 366 insertions(+), 315 deletions(-) diff --git a/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx b/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx index 4f5070850..e121b73fc 100644 --- a/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx +++ b/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx @@ -15,6 +15,8 @@ import ReportTab from './reportTab'; import { formatDisplayData } from '@/components/lib/utils/formatDisplayValue'; import useStore from '../../../store/useStore'; import GisApp from "@common/business_components/ui/map/lib/gis.core"; +import { confirmToast } from '@/components/ui/toast' +import { toast, Toaster } from "react-hot-toast"; const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}`; @@ -145,6 +147,7 @@ const SearchModal: React.FC = ({ setIsResetDetailPage(true); // 디테일 페이지를 초기화 setIsResetSelection(true); // 선택 초기화 setIsResetDetailSelection(true); // 선택 초기화 + setDetailBizId(0); // queryParams는 fetchFilteredData 내에서 매번 새로 생성되므로 별도 상태 초기화는 필요 없음 }, [activeTab, basicType, workType, open]); @@ -156,6 +159,7 @@ const SearchModal: React.FC = ({ setTableData([]); setDetailTableCount(0); setDetailTableData([]); + setDetailBizId(0); }, [open]); // 페이지 변경 시마다 데이터 fetch @@ -163,6 +167,7 @@ const SearchModal: React.FC = ({ // showTable이 true일 때만 fetch (최초 렌더 방지) setIsResetPage(false); setIsResetDetailPage(true); + setDetailBizId(0); if (showTable) fetchFilteredData(); }, [curPage]); @@ -584,372 +589,415 @@ const SearchModal: React.FC = ({ // 기본정보 Excel 다운로드 핸들러 const BasicExcelDownload = async () => { - setIsLoading(true); + if (tableData.length === 0) { + alert("다운로드 할 Excel 파일이 없습니다.\n조회 후 다시 시도해주세요."); + return; + } - try { - const queryParams = new URLSearchParams({ - lyrTypeNm: basicType, - size: '10000', - }); + toast.dismiss("global-confirm-toast"); - let cqlFilters: string[] = []; - let headerMapping: { [key: string]: string } = {}; - let headerRange: string = "A1:F1"; + confirmToast( + "파일 다운로드에는 2분 내외의 시간이 소요될 수 있습니다.\n파일 다운로드를 진행하시겠습니까?", + onAnalysis ?? (() => { }), + "text-xl font-medium text-gray-800" + ); - switch (basicType) { - case 'tb_ms_swage_ppl': { - // 하수관로 - cqlFilters = pplCqlFilters() - headerMapping = MsSwagePplExcelCol; - headerRange = "A1:F1"; - break; - } - case 'tb_il_swage_prcs_fclt': { - // 하수처리시설 - cqlFilters = facilityCqlFilters(); - headerMapping = SwtrmtFcltExcelCol; - headerRange = "A1:J1"; - break; - } - case 'tb_ms_swage_prcs_zone': { - // 하수처리구역 - cqlFilters = zoneCqlFilters(); - headerMapping = SwtrmtPrcsZoneExcelCol; - headerRange = "A1:D1"; - break; - } - case 'tb_ms_swage_mhl': { - // 하수맨홀 - cqlFilters = mhCqlFilters(); - headerMapping = MsSwageMhlExcelCol; - headerRange = "A1:J1"; - break; - } - case 'tb_ms_swage_pmstn': { - // 하수펌프장 - cqlFilters = psmtnCqlFilters(); - headerMapping = ExclnSewagePumpExcelCol; - headerRange = "A1:J1"; - break; - } - case 'tb_ms_stofcbr': { - // 우수토실 - cqlFilters = otflCqlFilters(); - headerMapping = stofcbrExcelCol; - headerRange = "A1:H1"; - break; - } - } + async function onAnalysis() { + setIsLoading(true); - if (cqlFilters.length > 0) queryParams.append('cqlFiter', cqlFilters.join(' and ')); + try { + const queryParams = new URLSearchParams({ + lyrTypeNm: basicType, + size: '10000', + }); - const res = await axios.get(`${API_URL}/v1/fclts?${queryParams.toString()}`); - console.log('res.data.data', res.data.data); - - - - - // const mhlCdMap = new Map(MS_SWAGE_MHL_CODES.map(code => [code.val?.trim(), code.text])); - const mhlUsgCdMap = new Map(MS_SWAGE_MHL_USE_CODES.map(code => [code.val?.trim(), code.text])); - const pplUsgCdMap = new Map(MS_SWAGE_PPL_USE_CODES.map(code => [code.val?.trim(), code.text])); - const ctpvMap = new Map(ctpv.map(code => [code.val?.trim(), code.text])); - const sggMap = new Map(sgg.map(code => [code.val?.trim(), code.text])); - - const mappedContent = (res.data.data || []).map((item: any) => { - const formattedItem = formatDisplayData(item); - - let ctpvCd = item?.ctpvCd ?? formattedItem?.ctpvCd ?? ''; - let sggCd = item?.sggCd ?? formattedItem?.sggCd ?? ''; - - if (!ctpvCd || !sggCd) { - const code = String(item?.pbadmsEmdCd ?? formattedItem?.pbadmsEmdCd ?? '')?.trim(); - if (!ctpvCd && code) ctpvCd = code.slice(0, 2); - if (!sggCd && code) sggCd = code.slice(0, 5); - } - - // trim 한 번만 수행 - const trimmedUsgCd = item?.usgCd?.trim(); - // const trimmedMqltCd = item?.mqltCd?.trim(); + let cqlFilters: string[] = []; + let headerMapping: { [key: string]: string } = {}; + let headerRange: string = "A1:F1"; switch (basicType) { case 'tb_ms_swage_ppl': { // 하수관로 - return { - fcltMngNo: formattedItem.fcltMngNo ?? item.fcltMngNo ?? '-', - instMngNo: formattedItem.instMngNo ?? item.instMngNo ?? '-', - ctpvNm: ctpvMap.get(ctpvCd) ?? item.ctpvNm ?? '-', - sggNm: sggMap.get(sggCd) ?? item.sggNm ?? '-', - swtrmt_zone_nm: formattedItem.swtrmt_zone_nm ?? item.swtrmt_zone_nm ?? '-', - usgCd: pplUsgCdMap.get(trimmedUsgCd) || item.usgCd || formattedItem.usgCd || '-', - pdmtVl: formattedItem.pdmtVl ?? item.pdmtVl ?? '-', - prlgVl: formattedItem.prlgVl ?? item.prlgVl ?? '-', - tmptDpth: formattedItem.tmptDpth ?? item.tmptDpth ?? '-', - tmptMhlMngNo: formattedItem.tmptMhlMngNo ?? item.tmptMhlMngNo ?? '-', - scendDpth: formattedItem.scendDpth ?? item.scendDpth ?? '-', - scendMhlMngNo: formattedItem.scendMhlMngNo ?? item.scendMhlMngNo ?? '-', - swtrmtFcltNm: formattedItem.swtrmtFcltNm ?? item.swtrmtFcltNm ?? '-', - }; + cqlFilters = pplCqlFilters() + headerMapping = MsSwagePplExcelCol; + headerRange = "A1:F1"; + break; } case 'tb_il_swage_prcs_fclt': { // 하수처리시설 - return { - swtrmtFcltNm: formattedItem.swtrmtFcltNm ?? item.swtrmtFcltNm ?? '-', - fcltCpct: formattedItem.fcltCpct ?? item.fcltCpct ?? '-', - rgnExpln: formattedItem.rgnExpln ?? item.rgnExpln ?? '-', - oprtngStrtYmd: formattedItem.oprtngStrtYmd ?? item.oprtngStrtYmd ?? '-', - laeNm: formattedItem.laeNm ?? item.laeNm ?? '-', - sareaNm: formattedItem.sareaNm ?? item.sareaNm ?? '-', - rvrNm: formattedItem.rvrNm ?? item.rvrNm ?? '-', - dschgWtstNm: formattedItem.dschgWtstNm ?? item.dschgWtstNm ?? '-', - swtrmtPpltnCnt: formattedItem.swtrmtPpltnCnt ?? item.swtrmtPpltnCnt ?? '-', - rprsMsrcCn: formattedItem.rprsMsrcCn ?? item.rprsMsrcCn ?? '-' - }; + cqlFilters = facilityCqlFilters(); + headerMapping = SwtrmtFcltExcelCol; + headerRange = "A1:J1"; + break; } case 'tb_ms_swage_prcs_zone': { // 하수처리구역 - return { - swtrmtZoneNm: formattedItem.swtrmtZoneNm ?? item.swtrmtZoneNm ?? '-', - ctpvNm: ctpvMap.get(ctpvCd) ?? formattedItem.ctpvNm ?? '-', - sggNm: sggMap.get(sggCd) ?? formattedItem.sggNm ?? '-', - swtrmtFcltNm: formattedItem.swtrmtFcltNm ?? item.swtrmtFcltNm ?? '-' - }; + cqlFilters = zoneCqlFilters(); + headerMapping = SwtrmtPrcsZoneExcelCol; + headerRange = "A1:D1"; + break; } case 'tb_ms_swage_mhl': { // 하수맨홀 - return { - fcltMngNo: formattedItem.fcltMngNo ?? item.fcltMngNo ?? '-', - ctpvNm: ctpvMap.get(ctpvCd) ?? formattedItem.ctpvNm ?? '-', - sggNm: sggMap.get(sggCd) ?? formattedItem.sggNm ?? '-', - instlYmd: formattedItem.instlYmd ?? item.instlYmd ?? '-', - usgCd: mhlUsgCdMap.get(trimmedUsgCd) || item.usgCd || formattedItem.usgCd || '-', - swageMhlDiamVl: formattedItem.swageMhlDiamVl ?? item.swageMhlDiamVl ?? '-', - wdthLen: formattedItem.wdthLen ?? item.wdthLen ?? '-', - vrtcLen: formattedItem.vrtcLen ?? item.vrtcLen ?? '-', - altdVl: formattedItem.altdVl ?? item.altdVl ?? '-', - dpthlVl: formattedItem.dpthlVl ?? item.dpthlVl ?? '-' - }; + cqlFilters = mhCqlFilters(); + headerMapping = MsSwageMhlExcelCol; + headerRange = "A1:J1"; + break; } case 'tb_ms_swage_pmstn': { // 하수펌프장 - return { - fcltMngNo: formattedItem.fcltMngNo ?? item.fcltMngNo ?? '-', - ctpvNm: ctpvMap.get(ctpvCd) ?? formattedItem.ctpvNm ?? '-', - sggNm: sggMap.get(sggCd) ?? formattedItem.sggNm ?? '-', - swagePmstnNm: formattedItem.swagePmstnNm ?? item.swagePmstnNm ?? '-', - instlYmd: formattedItem.instlYmd ?? item.instlYmd ?? '-', - dailyPrcsCpct: formattedItem.dailyPrcsCpct ?? item.dailyPrcsCpct ?? '-', - maxImpndCpct: formattedItem.maxImpndCpct ?? item.maxImpndCpct ?? '-', - altdVl: formattedItem.altdVl ?? item.altdVl ?? '-', - wtlvVl: formattedItem.wtlvVl ?? item.wtlvVl ?? '-', - exclnWpmpAbltVl: formattedItem.exclnWpmpAbltVl ?? item.exclnWpmpAbltVl ?? '-' - }; + cqlFilters = psmtnCqlFilters(); + headerMapping = ExclnSewagePumpExcelCol; + headerRange = "A1:J1"; + break; } case 'tb_ms_stofcbr': { // 우수토실 - return { - fcltMngNo: formattedItem.fcltMngNo ?? item.fcltMngNo ?? '-', - mapshtNo: formattedItem.mapshtNo ?? item.mapshtNo ?? '-', - ctpvNm: ctpvMap.get(ctpvCd) ?? formattedItem.ctpvNm ?? '-', - sggNm: sggMap.get(sggCd) ?? formattedItem.sggNm ?? '-', - instlYmd: formattedItem.instlYmd ?? item.instlYmd ?? '-', - worflwWallHgt: formattedItem.worflwWallHgt ?? item.worflwWallHgt ?? '-', - swageMhlMngNo: formattedItem.swageMhlMngNo ?? item.swageMhlMngNo ?? '-', - drtnAngl: formattedItem.drtnAngl ?? item.drtnAngl ?? '-' - }; + cqlFilters = otflCqlFilters(); + headerMapping = stofcbrExcelCol; + headerRange = "A1:H1"; + break; } } - }); - // 선택된 행 데이터를 Excel 다운로드 형식으로 변환 - const excelData = mappedContent; // 배열로 감싸서 전달 - console.log('excelData', excelData); + if (cqlFilters.length > 0) queryParams.append('cqlFiter', cqlFilters.join(' and ')); - // 파일명 생성 (분석번호 기반) - const label = BASIC_TYPES.find((type) => type.val === basicType)?.text - const fileName = `${label} 조회 결과_${todayYear}${todayMonth}${todayDate}`; + const res = await axios.get(`${API_URL}/v1/fclts?${queryParams.toString()}`); + console.log('res.data.data', res.data.data); - await downloadExcelWithKoreanHeaders({ - data: excelData as JSON[], - fileName: fileName, - sheetNameArray: [fileName], - headerRange: headerRange, - headerMapping: headerMapping - }); - setIsLoading(false); - } catch (error) { - setIsLoading(false); - console.error("Excel 다운로드 중 오류 발생:", error); - alert("Excel 다운로드 중 오류가 발생했습니다. 다시 시도해주세요."); + + + + // const mhlCdMap = new Map(MS_SWAGE_MHL_CODES.map(code => [code.val?.trim(), code.text])); + const mhlUsgCdMap = new Map(MS_SWAGE_MHL_USE_CODES.map(code => [code.val?.trim(), code.text])); + const pplUsgCdMap = new Map(MS_SWAGE_PPL_USE_CODES.map(code => [code.val?.trim(), code.text])); + const ctpvMap = new Map(ctpv.map(code => [code.val?.trim(), code.text])); + const sggMap = new Map(sgg.map(code => [code.val?.trim(), code.text])); + + const mappedContent = (res.data.data || []).map((item: any) => { + const formattedItem = formatDisplayData(item); + + let ctpvCd = item?.ctpvCd ?? formattedItem?.ctpvCd ?? ''; + let sggCd = item?.sggCd ?? formattedItem?.sggCd ?? ''; + + if (!ctpvCd || !sggCd) { + const code = String(item?.pbadmsEmdCd ?? formattedItem?.pbadmsEmdCd ?? '')?.trim(); + if (!ctpvCd && code) ctpvCd = code.slice(0, 2); + if (!sggCd && code) sggCd = code.slice(0, 5); + } + + // trim 한 번만 수행 + const trimmedUsgCd = item?.usgCd?.trim(); + // const trimmedMqltCd = item?.mqltCd?.trim(); + + switch (basicType) { + case 'tb_ms_swage_ppl': { + // 하수관로 + return { + fcltMngNo: formattedItem.fcltMngNo ?? item.fcltMngNo ?? '-', + instMngNo: formattedItem.instMngNo ?? item.instMngNo ?? '-', + ctpvNm: ctpvMap.get(ctpvCd) ?? item.ctpvNm ?? '-', + sggNm: sggMap.get(sggCd) ?? item.sggNm ?? '-', + swtrmt_zone_nm: formattedItem.swtrmt_zone_nm ?? item.swtrmt_zone_nm ?? '-', + usgCd: pplUsgCdMap.get(trimmedUsgCd) || item.usgCd || formattedItem.usgCd || '-', + pdmtVl: formattedItem.pdmtVl ?? item.pdmtVl ?? '-', + prlgVl: formattedItem.prlgVl ?? item.prlgVl ?? '-', + tmptDpth: formattedItem.tmptDpth ?? item.tmptDpth ?? '-', + tmptMhlMngNo: formattedItem.tmptMhlMngNo ?? item.tmptMhlMngNo ?? '-', + scendDpth: formattedItem.scendDpth ?? item.scendDpth ?? '-', + scendMhlMngNo: formattedItem.scendMhlMngNo ?? item.scendMhlMngNo ?? '-', + swtrmtFcltNm: formattedItem.swtrmtFcltNm ?? item.swtrmtFcltNm ?? '-', + }; + } + case 'tb_il_swage_prcs_fclt': { + // 하수처리시설 + return { + swtrmtFcltNm: formattedItem.swtrmtFcltNm ?? item.swtrmtFcltNm ?? '-', + fcltCpct: formattedItem.fcltCpct ?? item.fcltCpct ?? '-', + rgnExpln: formattedItem.rgnExpln ?? item.rgnExpln ?? '-', + oprtngStrtYmd: formattedItem.oprtngStrtYmd ?? item.oprtngStrtYmd ?? '-', + laeNm: formattedItem.laeNm ?? item.laeNm ?? '-', + sareaNm: formattedItem.sareaNm ?? item.sareaNm ?? '-', + rvrNm: formattedItem.rvrNm ?? item.rvrNm ?? '-', + dschgWtstNm: formattedItem.dschgWtstNm ?? item.dschgWtstNm ?? '-', + swtrmtPpltnCnt: formattedItem.swtrmtPpltnCnt ?? item.swtrmtPpltnCnt ?? '-', + rprsMsrcCn: formattedItem.rprsMsrcCn ?? item.rprsMsrcCn ?? '-' + }; + } + case 'tb_ms_swage_prcs_zone': { + // 하수처리구역 + return { + swtrmtZoneNm: formattedItem.swtrmtZoneNm ?? item.swtrmtZoneNm ?? '-', + ctpvNm: ctpvMap.get(ctpvCd) ?? formattedItem.ctpvNm ?? '-', + sggNm: sggMap.get(sggCd) ?? formattedItem.sggNm ?? '-', + swtrmtFcltNm: formattedItem.swtrmtFcltNm ?? item.swtrmtFcltNm ?? '-' + }; + } + case 'tb_ms_swage_mhl': { + // 하수맨홀 + return { + fcltMngNo: formattedItem.fcltMngNo ?? item.fcltMngNo ?? '-', + ctpvNm: ctpvMap.get(ctpvCd) ?? formattedItem.ctpvNm ?? '-', + sggNm: sggMap.get(sggCd) ?? formattedItem.sggNm ?? '-', + instlYmd: formattedItem.instlYmd ?? item.instlYmd ?? '-', + usgCd: mhlUsgCdMap.get(trimmedUsgCd) || item.usgCd || formattedItem.usgCd || '-', + swageMhlDiamVl: formattedItem.swageMhlDiamVl ?? item.swageMhlDiamVl ?? '-', + wdthLen: formattedItem.wdthLen ?? item.wdthLen ?? '-', + vrtcLen: formattedItem.vrtcLen ?? item.vrtcLen ?? '-', + altdVl: formattedItem.altdVl ?? item.altdVl ?? '-', + dpthlVl: formattedItem.dpthlVl ?? item.dpthlVl ?? '-' + }; + } + case 'tb_ms_swage_pmstn': { + // 하수펌프장 + return { + fcltMngNo: formattedItem.fcltMngNo ?? item.fcltMngNo ?? '-', + ctpvNm: ctpvMap.get(ctpvCd) ?? formattedItem.ctpvNm ?? '-', + sggNm: sggMap.get(sggCd) ?? formattedItem.sggNm ?? '-', + swagePmstnNm: formattedItem.swagePmstnNm ?? item.swagePmstnNm ?? '-', + instlYmd: formattedItem.instlYmd ?? item.instlYmd ?? '-', + dailyPrcsCpct: formattedItem.dailyPrcsCpct ?? item.dailyPrcsCpct ?? '-', + maxImpndCpct: formattedItem.maxImpndCpct ?? item.maxImpndCpct ?? '-', + altdVl: formattedItem.altdVl ?? item.altdVl ?? '-', + wtlvVl: formattedItem.wtlvVl ?? item.wtlvVl ?? '-', + exclnWpmpAbltVl: formattedItem.exclnWpmpAbltVl ?? item.exclnWpmpAbltVl ?? '-' + }; + } + case 'tb_ms_stofcbr': { + // 우수토실 + return { + fcltMngNo: formattedItem.fcltMngNo ?? item.fcltMngNo ?? '-', + mapshtNo: formattedItem.mapshtNo ?? item.mapshtNo ?? '-', + ctpvNm: ctpvMap.get(ctpvCd) ?? formattedItem.ctpvNm ?? '-', + sggNm: sggMap.get(sggCd) ?? formattedItem.sggNm ?? '-', + instlYmd: formattedItem.instlYmd ?? item.instlYmd ?? '-', + worflwWallHgt: formattedItem.worflwWallHgt ?? item.worflwWallHgt ?? '-', + swageMhlMngNo: formattedItem.swageMhlMngNo ?? item.swageMhlMngNo ?? '-', + drtnAngl: formattedItem.drtnAngl ?? item.drtnAngl ?? '-' + }; + } + } + }); + + // 선택된 행 데이터를 Excel 다운로드 형식으로 변환 + const excelData = mappedContent; // 배열로 감싸서 전달 + console.log('excelData', excelData); + + // 파일명 생성 (분석번호 기반) + const label = BASIC_TYPES.find((type) => type.val === basicType)?.text + const fileName = `${label} 조회 결과_${todayYear}${todayMonth}${todayDate}`; + + await downloadExcelWithKoreanHeaders({ + data: excelData as JSON[], + fileName: fileName, + sheetNameArray: [fileName], + headerRange: headerRange, + headerMapping: headerMapping + }); + setIsLoading(false); + } catch (error) { + setIsLoading(false); + console.error("Excel 다운로드 중 오류 발생:", error); + alert("Excel 다운로드 중 오류가 발생했습니다. 다시 시도해주세요."); + } } }; // 운영정보 Excel 다운로드 핸들러 추가 const OperationExcelDownload = async () => { - setIsLoading(true); + if (tableData.length === 0) { + alert("다운로드 할 Excel 파일이 없습니다.\n조회 후 다시 시도해주세요."); + return; + } - try { - const RequestDTO: Record = { - size: '1000000', // 최대 개수 백만건으로 설정 - }; - if (var1) RequestDTO.bizInfoNm = var1; - if (var2 != '0' && var2) RequestDTO.bizTypeCd = var2; - // 수행기간 범위 - const startDate = getDateString( - yearFilter.start, - '', - '', - "19000101", - ); - const endDate = getDateString( - yearFilter.end, - '', - '', - `${todayYear}${todayMonth}${todayDate}`, - ); - RequestDTO.fromflfmtEndYmd = startDate; - RequestDTO.toflfmtEndYmd = endDate; + toast.dismiss("global-confirm-toast"); - // JSON.parse 사용으로 문자열 -> 배열 변환 - RequestDTO.workTypes = JSON.parse(workType.replace(/'/g, '"')); + confirmToast( + "파일 다운로드에는 2분 내외의 시간이 소요될 수 있습니다.\n파일 다운로드를 진행하시겠습니까?", + onAnalysis ?? (() => { }), + "text-xl font-medium text-gray-800" + ); - const params = new URLSearchParams(RequestDTO).toString(); - const res = await getData(`v1/bizInfo?${params}`); + async function onAnalysis() { + setIsLoading(true); - // 헤더 매핑 정의 - const headerMapping: { [key: string]: string } = { - 'bizInfoNm': '사업명', - 'flfmtBgngYmd': '수행시작일자', - 'flfmtEndYmd': '수행종료일자', - 'flfmtYr': '수행년도', - 'bizTypeCd': '사업유형', - 'bizAmt': '사업금액(원)', - }; - - const bizTyCdMap = new Map(BIZ_TY_CD.map(code => [code.lwpoCd, code.lwpoCdNm])); - - const mappedContent = (res.content || []).map((item: any) => { - const formattedItem = formatDisplayData(item); - - return { - bizInfoNm: formattedItem.bizInfoNm ?? item.bizInfoNm ?? '', - flfmtBgngYmd: formattedItem.flfmtBgngYmd ?? item.flfmtBgngYmd ?? '', - flfmtEndYmd: formattedItem.flfmtEndYmd ?? item.flfmtEndYmd ?? '', - flfmtYr: formattedItem.flfmtYr ?? item.flfmtYr ?? '', - bizTypeCd: bizTyCdMap.get(item.bizTypeCd) || item.bizTypeCd || formattedItem.bizTypeCd || '', - bizAmt: ((v) => (v === 0 || v === '0') ? '0' : (v ?? ''))(formattedItem.bizAmt ?? item.bizAmt), + try { + const RequestDTO: Record = { + size: '1000000', // 최대 개수 백만건으로 설정 }; - }); + if (var1) RequestDTO.bizInfoNm = var1; + if (var2 != '0' && var2) RequestDTO.bizTypeCd = var2; + // 수행기간 범위 + const startDate = getDateString( + yearFilter.start, + '', + '', + "19000101", + ); + const endDate = getDateString( + yearFilter.end, + '', + '', + `${todayYear}${todayMonth}${todayDate}`, + ); + RequestDTO.fromflfmtEndYmd = startDate; + RequestDTO.toflfmtEndYmd = endDate; - // 선택된 행 데이터를 Excel 다운로드 형식으로 변환 - const excelData = mappedContent; // 배열로 감싸서 전달 + // JSON.parse 사용으로 문자열 -> 배열 변환 + RequestDTO.workTypes = JSON.parse(workType.replace(/'/g, '"')); - // 파일명 생성 (분석번호 기반) - const label = WORK_TYPES.find((type) => type.val === workType)?.text - const fileName = `${label} 사업 현황_${todayYear}${todayMonth}${todayDate}`; + const params = new URLSearchParams(RequestDTO).toString(); + const res = await getData(`v1/bizInfo?${params}`); - await downloadExcelWithKoreanHeaders({ - data: excelData as JSON[], - fileName: fileName, - sheetNameArray: [fileName], - headerRange: "A1:F1", - headerMapping: headerMapping - }); - setIsLoading(false); - } catch (error) { - setIsLoading(false); - console.error("Excel 다운로드 중 오류 발생:", error); - alert("Excel 다운로드 중 오류가 발생했습니다. 다시 시도해주세요."); + // 헤더 매핑 정의 + const headerMapping: { [key: string]: string } = { + 'bizInfoNm': '사업명', + 'flfmtBgngYmd': '수행시작일자', + 'flfmtEndYmd': '수행종료일자', + 'flfmtYr': '수행년도', + 'bizTypeCd': '사업유형', + 'bizAmt': '사업금액(원)', + }; + + const bizTyCdMap = new Map(BIZ_TY_CD.map(code => [code.lwpoCd, code.lwpoCdNm])); + + const mappedContent = (res.content || []).map((item: any) => { + const formattedItem = formatDisplayData(item); + + return { + bizInfoNm: formattedItem.bizInfoNm ?? item.bizInfoNm ?? '', + flfmtBgngYmd: formattedItem.flfmtBgngYmd ?? item.flfmtBgngYmd ?? '', + flfmtEndYmd: formattedItem.flfmtEndYmd ?? item.flfmtEndYmd ?? '', + flfmtYr: formattedItem.flfmtYr ?? item.flfmtYr ?? '', + bizTypeCd: bizTyCdMap.get(item.bizTypeCd) || item.bizTypeCd || formattedItem.bizTypeCd || '', + bizAmt: ((v) => (v === 0 || v === '0') ? '0' : (v ?? ''))(formattedItem.bizAmt ?? item.bizAmt), + }; + }); + + // 선택된 행 데이터를 Excel 다운로드 형식으로 변환 + const excelData = mappedContent; // 배열로 감싸서 전달 + + // 파일명 생성 (분석번호 기반) + const label = WORK_TYPES.find((type) => type.val === workType)?.text + const fileName = `${label} 사업 현황_${todayYear}${todayMonth}${todayDate}`; + + await downloadExcelWithKoreanHeaders({ + data: excelData as JSON[], + fileName: fileName, + sheetNameArray: [fileName], + headerRange: "A1:F1", + headerMapping: headerMapping + }); + setIsLoading(false); + } catch (error) { + setIsLoading(false); + console.error("Excel 다운로드 중 오류 발생:", error); + alert("Excel 다운로드 중 오류가 발생했습니다. 다시 시도해주세요."); + } } }; - // 운영정보 Excel 다운로드 핸들러 추가 const OperationResultExcelDownload = async () => { - setIsLoading(true); + if (detailTableData.length === 0) { + alert("다운로드 할 Excel 파일이 없습니다.\n조회 후 다시 시도해주세요."); + return; + } - try { - const totalStartTime = performance.now(); // 전체 시작 시간 + toast.dismiss("global-confirm-toast"); - const RequestDTO: Record = { - size: '1000', // 최대 개수 백만건으로 설정 - bizId: String(detailBizId), - }; + confirmToast( + "파일 다운로드에는 2분 내외의 시간이 소요될 수 있습니다.\n파일 다운로드를 진행하시겠습니까?", + onAnalysis ?? (() => { }), + "text-xl font-medium text-gray-800" + ); - const apiStartTime = performance.now(); - const res = await getData(`v1/bizInfo/fclts?${new URLSearchParams(RequestDTO).toString()}`); - const apiEndTime = performance.now(); - console.log(`🚀 1. API 호출 시간: ${(apiEndTime - apiStartTime).toFixed(2)}ms`); - console.log('운영정보 결과 다운로드.data:', res.data); + async function onAnalysis() { - const mappingStartTime = performance.now(); - // 헤더 매핑 정의 - const headerMapping: { [key: string]: string } = { - 'fcltMngNo': '관리번호', - 'swtrmt_zone_nm': '처리구역', - 'usgCd': '용도', - 'mqltCd': '재질', - 'pdmtVl': '관경(mm)', - 'prlgVl': '연장(m)', - 'instlYmd': '설치일자', - 'strc_stts_grd': '구조적 상태등급', - 'oper_stts_grd': '운영적 상태등급', - }; + setIsLoading(true); - const usgCdMap = new Map(MS_SWAGE_PPL_USE_CODES.map(code => [code.val, code.text])); - const mqltCdMap = new Map(MS_SWAGE_PPL_CODES.map(code => [code.val, code.text])); - - // 🎯 단일 반복문으로 최적화 - 각 아이템마다 필요한 필드만 직접 매핑 - const mappedData = (res.data || []).map((item: any) => { - const formattedItem = formatDisplayData(item); - - // trim 한 번만 수행 - const trimmedUsgCd = item?.usgCd?.trim(); - const trimmedMqltCd = item?.mqltCd?.trim(); - - // zone 값 한 번만 계산 - const zone = item.swtrmtZoneNm ?? formattedItem.swtrmt_zone_nm ?? item.swtrmt_zone_nm ?? ''; - - // 직접 매핑 - 반복문 없이 한 번에 객체 생성 - return { - fcltMngNo: formattedItem.fcltMngNo ?? item.fcltMngNo ?? '', - swtrmt_zone_nm: (zone === null || zone === '') ? '-' : zone, - usgCd: usgCdMap.get(trimmedUsgCd) || item.usgCd || formattedItem.usgCd || '', - mqltCd: mqltCdMap.get(trimmedMqltCd) || item.mqltCd || formattedItem.mqltCd || '', - pdmtVl: formattedItem.pdmtVl ?? item.pdmtVl ?? '', - prlgVl: formattedItem.prlgVl ?? item.prlgVl ?? '', - instlYmd: formattedItem.instlYmd ?? item.instlYmd ?? '', - strc_stts_grd: ((v) => (v === 0 || v === '0') ? '0' : (v ?? ''))(formattedItem.strc_stts_grd ?? item.strc_stts_grd), - oper_stts_grd: ((v) => (v === 0 || v === '0') ? '0' : (v ?? ''))(formattedItem.oper_stts_grd ?? item.oper_stts_grd), + try { + const RequestDTO: Record = { + size: '10000', // 최대 개수 백만건으로 설정 + bizId: String(detailBizId), }; - }); - const mappingEndTime = performance.now(); - console.log(`🔄 2. 데이터 매핑 시간: ${(mappingEndTime - mappingStartTime).toFixed(2)}ms`); - // 선택된 행 데이터를 Excel 다운로드 형식으로 변환 - const excelData = mappedData; // 배열로 감싸서 전달 + const apiStartTime = performance.now(); + const res = await getData(`v1/bizInfo/fclts?${new URLSearchParams(RequestDTO).toString()}`); + const apiEndTime = performance.now(); + console.log(`🚀 1. API 호출 시간: ${(apiEndTime - apiStartTime).toFixed(2)}ms`); + console.log('운영정보 결과 다운로드.data:', res.data); - console.log('excelData', excelData); + const mappingStartTime = performance.now(); + // 헤더 매핑 정의 + const headerMapping: { [key: string]: string } = { + 'fcltMngNo': '관리번호', + 'swtrmt_zone_nm': '처리구역', + 'usgCd': '용도', + 'mqltCd': '재질', + 'pdmtVl': '관경(mm)', + 'prlgVl': '연장(m)', + 'instlYmd': '설치일자', + 'strc_stts_grd': '구조적 상태등급', + 'oper_stts_grd': '운영적 상태등급', + }; - const excelStartTime = performance.now(); - // 파일명 생성 (분석번호 기반) - const label = WORK_TYPES.find((type) => type.val === workType)?.text - const fileName = `${label} 결과표_${todayYear}${todayMonth}${todayDate}`; + const usgCdMap = new Map(MS_SWAGE_PPL_USE_CODES.map(code => [code.val, code.text])); + const mqltCdMap = new Map(MS_SWAGE_PPL_CODES.map(code => [code.val, code.text])); - await downloadExcelWithKoreanHeaders({ - data: excelData as JSON[], - fileName: fileName, - sheetNameArray: [fileName], - headerRange: "A1:I1", - headerMapping: headerMapping - }); - const excelEndTime = performance.now(); - console.log(`📊 3. Excel 다운로드 시간: ${(excelEndTime - excelStartTime).toFixed(2)}ms`); + // 🎯 단일 반복문으로 최적화 - 각 아이템마다 필요한 필드만 직접 매핑 + const mappedData = (res.data || []).map((item: any) => { + const formattedItem = formatDisplayData(item); - setIsLoading(false); - } catch (error) { - setIsLoading(false); - console.error("Excel 다운로드 중 오류 발생:", error); - alert("Excel 다운로드 중 오류가 발생했습니다. 다시 시도해주세요."); + // trim 한 번만 수행 + const trimmedUsgCd = item?.usgCd?.trim(); + const trimmedMqltCd = item?.mqltCd?.trim(); + + // zone 값 한 번만 계산 + const zone = item.swtrmtZoneNm ?? formattedItem.swtrmt_zone_nm ?? item.swtrmt_zone_nm ?? ''; + + // 직접 매핑 - 반복문 없이 한 번에 객체 생성 + return { + fcltMngNo: formattedItem.fcltMngNo ?? item.fcltMngNo ?? '', + swtrmt_zone_nm: (zone === null || zone === '') ? '-' : zone, + usgCd: usgCdMap.get(trimmedUsgCd) || item.usgCd || formattedItem.usgCd || '', + mqltCd: mqltCdMap.get(trimmedMqltCd) || item.mqltCd || formattedItem.mqltCd || '', + pdmtVl: formattedItem.pdmtVl ?? item.pdmtVl ?? '', + prlgVl: formattedItem.prlgVl ?? item.prlgVl ?? '', + instlYmd: formattedItem.instlYmd ?? item.instlYmd ?? '', + strc_stts_grd: ((v) => (v === 0 || v === '0') ? '0' : (v ?? ''))(formattedItem.strc_stts_grd ?? item.strc_stts_grd), + oper_stts_grd: ((v) => (v === 0 || v === '0') ? '0' : (v ?? ''))(formattedItem.oper_stts_grd ?? item.oper_stts_grd), + }; + }); + const mappingEndTime = performance.now(); + console.log(`🔄 2. 데이터 매핑 시간: ${(mappingEndTime - mappingStartTime).toFixed(2)}ms`); + + // 선택된 행 데이터를 Excel 다운로드 형식으로 변환 + const excelData = mappedData; // 배열로 감싸서 전달 + + console.log('excelData', excelData); + + const excelStartTime = performance.now(); + // 파일명 생성 (분석번호 기반) + const label = WORK_TYPES.find((type) => type.val === workType)?.text + const fileName = `${label} 결과표_${todayYear}${todayMonth}${todayDate}`; + + await downloadExcelWithKoreanHeaders({ + data: excelData as JSON[], + fileName: fileName, + sheetNameArray: [fileName], + headerRange: "A1:I1", + headerMapping: headerMapping + }); + const excelEndTime = performance.now(); + console.log(`📊 3. Excel 다운로드 시간: ${(excelEndTime - excelStartTime).toFixed(2)}ms`); + + setIsLoading(false); + } catch (error) { + setIsLoading(false); + console.error("Excel 다운로드 중 오류 발생:", error); + alert("Excel 다운로드 중 오류가 발생했습니다. 다시 시도해주세요."); + } } }; @@ -1101,10 +1149,11 @@ const SearchModal: React.FC = ({ ); return ( - + = ({ overflow-hidden z-[101] `} - body={renderBody()} - useDirectRender={true} - /> + body={renderBody()} + useDirectRender={true} + /> + + ); }; From 40e0dde0e0bc62377132e37da20d11c325739027 Mon Sep 17 00:00:00 2001 From: swcho Date: Tue, 19 Aug 2025 16:31:03 +0900 Subject: [PATCH 5/5] =?UTF-8?q?fix:=20searchModal=20=EC=97=91=EC=85=80=20?= =?UTF-8?q?=EB=8B=A4=EC=9A=B4=EB=A1=9C=EB=93=9C=20=EC=97=90=EB=9F=AC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/sn/app/(map-layout)/map/components/common/table.ts | 1 + apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx | 8 ++++---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/apps/sn/app/(map-layout)/map/components/common/table.ts b/apps/sn/app/(map-layout)/map/components/common/table.ts index 531c72bc6..a16e68013 100644 --- a/apps/sn/app/(map-layout)/map/components/common/table.ts +++ b/apps/sn/app/(map-layout)/map/components/common/table.ts @@ -61,6 +61,7 @@ export const MsSwagePplExcelCol = { 'ctpvNm': '시도', 'sggNm': '시군구', 'swtrmt_zone_nm': '처리구역', + 'instlYmd': '설치일자', 'usgCd': '용도', 'pdmtVl': '관경(mm)', 'prlgVl': '연장(m)', diff --git a/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx b/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx index e121b73fc..8b38d232d 100644 --- a/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx +++ b/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx @@ -620,7 +620,7 @@ const SearchModal: React.FC = ({ // 하수관로 cqlFilters = pplCqlFilters() headerMapping = MsSwagePplExcelCol; - headerRange = "A1:F1"; + headerRange = "A1:M1"; break; } case 'tb_il_swage_prcs_fclt': { @@ -699,6 +699,7 @@ const SearchModal: React.FC = ({ ctpvNm: ctpvMap.get(ctpvCd) ?? item.ctpvNm ?? '-', sggNm: sggMap.get(sggCd) ?? item.sggNm ?? '-', swtrmt_zone_nm: formattedItem.swtrmt_zone_nm ?? item.swtrmt_zone_nm ?? '-', + instlYmd: formattedItem.instlYmd ?? item.instlYmd ?? '-', usgCd: pplUsgCdMap.get(trimmedUsgCd) || item.usgCd || formattedItem.usgCd || '-', pdmtVl: formattedItem.pdmtVl ?? item.pdmtVl ?? '-', prlgVl: formattedItem.prlgVl ?? item.prlgVl ?? '-', @@ -706,7 +707,6 @@ const SearchModal: React.FC = ({ tmptMhlMngNo: formattedItem.tmptMhlMngNo ?? item.tmptMhlMngNo ?? '-', scendDpth: formattedItem.scendDpth ?? item.scendDpth ?? '-', scendMhlMngNo: formattedItem.scendMhlMngNo ?? item.scendMhlMngNo ?? '-', - swtrmtFcltNm: formattedItem.swtrmtFcltNm ?? item.swtrmtFcltNm ?? '-', }; } case 'tb_il_swage_prcs_fclt': { @@ -823,7 +823,7 @@ const SearchModal: React.FC = ({ try { const RequestDTO: Record = { - size: '1000000', // 최대 개수 백만건으로 설정 + size: '10000', }; if (var1) RequestDTO.bizInfoNm = var1; if (var2 != '0' && var2) RequestDTO.bizTypeCd = var2; @@ -918,7 +918,7 @@ const SearchModal: React.FC = ({ try { const RequestDTO: Record = { - size: '10000', // 최대 개수 백만건으로 설정 + size: '10000', bizId: String(detailBizId), };