From c68e1d017e768fe5cb2f128fe01d4a8e3eb74267 Mon Sep 17 00:00:00 2001 From: leedj312 Date: Sat, 26 Jul 2025 17:31:32 +0900 Subject: [PATCH 01/15] =?UTF-8?q?dm=20=ED=98=84=ED=99=A9=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20=EB=B0=8F=20env=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/dm/.env.development | 3 ++- apps/dm/.env.local | 3 ++- .../pages/sdmMng/sdmInsert/page.tsx | 1 + .../app/(base-layout)/pages/wrStatus/page.tsx | 24 +++++++++---------- apps/dm/app/middleware.ts | 3 +-- apps/dm/src/shared/config/env.ts | 2 +- 6 files changed, 19 insertions(+), 17 deletions(-) diff --git a/apps/dm/.env.development b/apps/dm/.env.development index 622545fd6..08568f984 100644 --- a/apps/dm/.env.development +++ b/apps/dm/.env.development @@ -1,7 +1,8 @@ NEXT_PUBLIC_SITE_ID = DM NEXT_PUBLIC_PORT = 3000 NEXT_PUBLIC_PROXY_HOST = http://localhost:3000 -NEXT_PUBLIC_SERVER_API_URL = http://con.mind-one.co.kr:11080/api/v1 +NEXT_PUBLIC_DM_SERVER_API_URL = http://con.mind-one.co.kr:11080/api/v1 +NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000 NEXT_PUBLIC_FO_SERVER_API_URL = http://con.mind-one.co.kr:11082/api/v1 NEXT_PUBLIC_CS_SERVER_API_URL = http://dev.onasys.co.kr:8000 NEXT_PUBLIC_CS_TUS_UPLOAD = /attach-service/api/v1/tus/upload diff --git a/apps/dm/.env.local b/apps/dm/.env.local index f691fb313..27a3b0e98 100644 --- a/apps/dm/.env.local +++ b/apps/dm/.env.local @@ -6,7 +6,8 @@ NEXT_PUBLIC_REFRESH_TOKEN = refresh-token NEXT_PUBLIC_PORT = 11035 #NEXT_PUBLIC_PROXY_HOST = http://con.mind-one.co.kr:11035 NEXT_PUBLIC_PROXY_HOST = http://localhost:3000/ -NEXT_PUBLIC_SERVER_API_URL = http://con.mind-one.co.kr:11080/api/v1 +NEXT_PUBLIC_DM_SERVER_API_URL = http://con.mind-one.co.kr:11080/api/v1 +NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000 NEXT_PUBLIC_FO_SERVER_API_URL = http://con.mind-one.co.kr:11082/api/v1 NEXT_PUBLIC_CS_SERVER_API_URL = http://dev.onasys.co.kr:8000 NEXT_PUBLIC_GEOSERVER_URL = http://rsone.iptime.org:10010/geoserver diff --git a/apps/dm/app/(base-layout)/pages/sdmMng/sdmInsert/page.tsx b/apps/dm/app/(base-layout)/pages/sdmMng/sdmInsert/page.tsx index 926a8f902..a0aa35145 100644 --- a/apps/dm/app/(base-layout)/pages/sdmMng/sdmInsert/page.tsx +++ b/apps/dm/app/(base-layout)/pages/sdmMng/sdmInsert/page.tsx @@ -47,6 +47,7 @@ export default function MyForm() { dmdInfoSn: '', dmdInfoFk: '', status: '', + splyCndtList: [] }; formDataRef.current = convertedData; insertSdmData(convertedData).catch(error => { diff --git a/apps/dm/app/(base-layout)/pages/wrStatus/page.tsx b/apps/dm/app/(base-layout)/pages/wrStatus/page.tsx index 7ae840beb..278dd76bb 100644 --- a/apps/dm/app/(base-layout)/pages/wrStatus/page.tsx +++ b/apps/dm/app/(base-layout)/pages/wrStatus/page.tsx @@ -193,16 +193,16 @@ const WrStatusPage: React.FC = () => { }; const tableData: TableData[] = [ - { region: '전국', sewage: '717(1,137,538)', rainwater: '3544(13,945)', reclaimed: '1018(222,476)', thermal: '123' }, + { region: '전국', sewage: '717(1,137,538)', rainwater: '3544(13,945)', reclaimed: '1018(222,476)', thermal: '123(492)' }, { region: '서울특별시', sewage: '4(114,908)', rainwater: '816(430)', reclaimed: '371(341)', thermal: '0' }, - { region: '부산광역시', sewage: '13(146,197)', rainwater: '68(335)', reclaimed: '20(512)', thermal: '1' }, - { region: '대구광역시', sewage: '9(102,380)', rainwater: '294(133)', reclaimed: '14(44)', thermal: '2' }, - { region: '인천광역시', sewage: '17(57,691)', rainwater: '165(642)', reclaimed: '35(7,859)', thermal: '15' }, - { region: '광주광역시', sewage: '3(28,738)', rainwater: '74(23)', reclaimed: '15(450)', thermal: '15' }, - { region: '대전광역시', sewage: '2(11,966)', rainwater: '52(2)', reclaimed: '11(492)', thermal: '20' }, - { region: '울산광역시', sewage: '8(36,076)', rainwater: '111(39)', reclaimed: '19(28,904)', thermal: '30' }, + { region: '부산광역시', sewage: '13(146,197)', rainwater: '68(335)', reclaimed: '20(512)', thermal: '1(18)' }, + { region: '대구광역시', sewage: '9(102,380)', rainwater: '294(133)', reclaimed: '14(44)', thermal: '2(12)' }, + { region: '인천광역시', sewage: '17(57,691)', rainwater: '165(642)', reclaimed: '35(7,859)', thermal: '15(32)' }, + { region: '광주광역시', sewage: '3(28,738)', rainwater: '74(23)', reclaimed: '15(450)', thermal: '15(6)' }, + { region: '대전광역시', sewage: '2(11,966)', rainwater: '52(2)', reclaimed: '11(492)', thermal: '20(100)' }, + { region: '울산광역시', sewage: '8(36,076)', rainwater: '111(39)', reclaimed: '19(28,904)', thermal: '30(130)' }, { region: '세종특별자치시', sewage: '9(2,555)', rainwater: '65(16)', reclaimed: '0(0)', thermal: '0' }, - { region: '경기도', sewage: '176(313,298)', rainwater: '1009(4,735)', reclaimed: '252(77,096)', thermal: '40' }, + { region: '경기도', sewage: '176(313,298)', rainwater: '1009(4,735)', reclaimed: '252(77,096)', thermal: '40(143)' }, { region: '강원특별자치도', sewage: '69(19,677)', rainwater: '69(4)', reclaimed: '22(447)', thermal: '0' }, { region: '충청북도', sewage: '38(15,607)', rainwater: '17(91)', reclaimed: '91(2,211)', thermal: '0' }, { region: '충청남도', sewage: '67(41,368)', rainwater: '54(77)', reclaimed: '188(50,434)', thermal: '0' }, @@ -493,10 +493,10 @@ const WrStatusPage: React.FC = () => { 번호 시도 - 하수(이용량,천m³/년) - 빗물(이용량,천m³/년) - 중수도(이용량,천m³/년) - 온배수(이용량,천m³/년) + 하수(이용량,천𝓂³/년) + 빗물(이용량,천𝓂³/년) + 중수도(이용량,천𝓂³/년) + 온배수(이용량,천𝓂³/년) diff --git a/apps/dm/app/middleware.ts b/apps/dm/app/middleware.ts index 6ddf29f9f..ad35dfba7 100644 --- a/apps/dm/app/middleware.ts +++ b/apps/dm/app/middleware.ts @@ -1,9 +1,8 @@ import { NextRequest, NextResponse } from "next/server"; -import path from "path"; const AUTH_USER_ID = process.env.NEXT_PUBLIC_AUTH_USER_ID || "token-id"; const CLAIM_NAME = process.env.NEXT_PUBLIC_CLAIM_NAME || "Authorization"; -const SERVER_API_URL = process.env.NEXT_PUBLIC_SERVER_API_URL || ""; +const SERVER_API_URL = process.env.NEXT_PUBLIC_DM_SERVER_API_URL || ""; const SEWERAGE_API_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || ""; const GEOSERVER_URL = process.env.NEXT_PUBLIC_GEOSERVER_URL || ""; const LX_GEOSERVER_URL = process.env.NEXT_PUBLIC_LX_GEOSERVER_URL || ""; diff --git a/apps/dm/src/shared/config/env.ts b/apps/dm/src/shared/config/env.ts index b01588df0..a9011b90e 100644 --- a/apps/dm/src/shared/config/env.ts +++ b/apps/dm/src/shared/config/env.ts @@ -1,7 +1,7 @@ export const SITE_ID = process.env.NEXT_PUBLIC_SITE_ID || "DM"; export const PORT = process.env.NEXT_PUBLIC_PORT || "3000"; export const PROXY_HOST = process.env.NEXT_PUBLIC_PROXY_HOST || `http://localhost:${PORT}`; -export const SERVER_API_URL = process.env.NEXT_PUBLIC_SERVER_API_URL ||'http://con.mind-one.co.kr:11035/api/v1'; +export const SERVER_API_DM_URL = process.env.NEXT_PUBLIC_DM_SERVER_API_URL ||'http://con.mind-one.co.kr:11035/api/v1'; export const SERVER_API_FO_URL = process.env.NEXT_PUBLIC_FO_SERVER_API_URL||' http://con.mind-one.co.kr:11082/api/v1'; export const SERVER_API_WR_URL = process.env.NEXT_PUBLIC_WR_SERVER_API_URL||' http://con.mind-one.co.kr:11080/api/v1'; export const SERVER_API_CS_URL = process.env.NEXT_PUBLIC_CS_SERVER_API_URL||'http://dev.onasys.co.kr:8000'; From 95085cbdeb668149fa9cd93c237b879a8191786c Mon Sep 17 00:00:00 2001 From: leedj312 Date: Sun, 27 Jul 2025 08:34:38 +0900 Subject: [PATCH 02/15] =?UTF-8?q?=EC=B4=88=EA=B8=B0=ED=99=94=20=ED=95=A8?= =?UTF-8?q?=EC=88=98=EB=A7=8C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/(base-layout)/pages/sdmMng/sdmInfoInsert/page.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/apps/dm/app/(base-layout)/pages/sdmMng/sdmInfoInsert/page.tsx b/apps/dm/app/(base-layout)/pages/sdmMng/sdmInfoInsert/page.tsx index 40ff3948f..670b2a612 100644 --- a/apps/dm/app/(base-layout)/pages/sdmMng/sdmInfoInsert/page.tsx +++ b/apps/dm/app/(base-layout)/pages/sdmMng/sdmInfoInsert/page.tsx @@ -74,7 +74,7 @@ export type FormData = z.infer; function SdmInfoInsertContent() { const searchParams = useSearchParams(); - const {map, insertSdmInfoData, Sdm } = useDmStore((state) => state); + const {map, insertSdmInfoData, Sdm ,setSubsInfo} = useDmStore((state) => state); const { successLogin } = Sdm; const [ismoneyDisabled, setIsmoneyDisabled] = useState(false); const formSchema = sdmInfoFormSchema; @@ -127,6 +127,8 @@ function SdmInfoInsertContent() { if (local) { setIsLogin(true); } + setSubsInfo([]); // subsInfo 초기화 후보지 처음 화면 들어올때 초기화(스토어내 객체만) + }, []); const form = useForm({ @@ -135,7 +137,10 @@ function SdmInfoInsertContent() { mode: "onChange", }); + + useEffect(()=>{ + //map.subsInfo 스토어 객체 값이 변경되면 해당 값이 폼에 자동으로 들어가도록 수정 대신 값이 있어야함 if(map.subsInfo.length>0){ form.setValue('candidateTitle', map.subsInfo) } @@ -154,6 +159,7 @@ function SdmInfoInsertContent() { setFacilityName(userData.dmdCoNm); form.setValue('sdmNm', userData.dmdCoNm); } + } catch (error) { console.error('Error parsing user data:', error); } From 16b204f83b4fadd9d3aac5fb51861546df0e5ba9 Mon Sep 17 00:00:00 2001 From: yschoi Date: Mon, 28 Jul 2025 04:38:09 +0900 Subject: [PATCH 03/15] =?UTF-8?q?=ED=9B=84=EB=B3=B4=EC=A7=80=20=EC=84=A0?= =?UTF-8?q?=ED=83=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/dm/.env.development | 4 +- apps/dm/.env.local | 5 +- apps/dm/.env.production | 2 +- .../pages/sdmMng/sdmInfoInsert/page.tsx | 24 +- .../app/(base-layout)/pages/wrAbout/page.tsx | 8 +- apps/dm/app/(popup-layout)/map/popup/page.tsx | 268 ++++++++++++------ .../(popup-layout)/map/popup/sdmInfoMap.tsx | 147 ---------- .../map/popup/selectedFcltList.tsx | 81 ++++++ .../(popup-layout)/map/popup/windowPotal.tsx | 94 ------ apps/dm/app/(popup-layout)/map/service/map.ts | 15 + apps/dm/app/store/map/index.tsx | 1 - .../map/SearchInputText/SearchInputText.tsx | 75 +++-- .../components/ui/map/searchBar/searchBar.tsx | 18 +- .../sdmInfoInsertForm/AccessibleFormField.tsx | 4 +- .../ui/sdmInfoInsertForm/component.tsx | 117 ++++---- .../business_components/src/ui/map/Gis.js | 10 +- .../business_components/src/ui/map/GisTree.js | 1 - .../src/ui/map/lib/gis.style.js | 9 +- 18 files changed, 442 insertions(+), 441 deletions(-) delete mode 100644 apps/dm/app/(popup-layout)/map/popup/sdmInfoMap.tsx create mode 100644 apps/dm/app/(popup-layout)/map/popup/selectedFcltList.tsx delete mode 100644 apps/dm/app/(popup-layout)/map/popup/windowPotal.tsx diff --git a/apps/dm/.env.development b/apps/dm/.env.development index 08568f984..96f4da6f5 100644 --- a/apps/dm/.env.development +++ b/apps/dm/.env.development @@ -10,9 +10,7 @@ NEXT_PUBLIC_CLAIM_NAME = Authorization NEXT_PUBLIC_AUTH_USER_ID = token-id NEXT_PUBLIC_ACCESS_TOKEN = access-token NEXT_PUBLIC_REFRESH_TOKEN = refresh-token - - - +NEXT_PUBLIC_REUSE_API_URL=http://con.mind-one.co.kr:11080/api/v1 NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api # 개발 서버 API # NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.128:8883/sewerage-service/api # 최용성 #NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.66:8883/sewerage-service/api # 박이정 diff --git a/apps/dm/.env.local b/apps/dm/.env.local index 27a3b0e98..b60a1d86f 100644 --- a/apps/dm/.env.local +++ b/apps/dm/.env.local @@ -13,8 +13,9 @@ NEXT_PUBLIC_CS_SERVER_API_URL = http://dev.onasys.co.kr:8000 NEXT_PUBLIC_GEOSERVER_URL = http://rsone.iptime.org:10010/geoserver NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver NEXT_PUBLIC_CS_TUS_UPLOAD = /attach-service/api/v1/tus/upload -#NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api # 개발 서버 API -NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.128:8883/sewerage-service/api # 최용성 +NEXT_PUBLIC_REUSE_API_URL=http://con.mind-one.co.kr:11080/api/v1 +NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api # 개발 서버 API +#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.128:8883/sewerage-service/api # 최용성 #NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.66:8883/sewerage-service/api # 박이정 diff --git a/apps/dm/.env.production b/apps/dm/.env.production index 7576a7dfb..85e97b3ab 100644 --- a/apps/dm/.env.production +++ b/apps/dm/.env.production @@ -11,7 +11,7 @@ NEXT_PUBLIC_CLAIM_NAME = Authorization NEXT_PUBLIC_AUTH_USER_ID = token-id NEXT_PUBLIC_ACCESS_TOKEN = access-token NEXT_PUBLIC_REFRESH_TOKEN = refresh-token - +NEXT_PUBLIC_REUSE_API_URL=http://con.mind-one.co.kr:11080/api/v1 NEXT_PUBLIC_SEWERAGE_API_URL = http://apigateway.iipbs-dev.svc.cluster.local:8000/sewerage-service/api diff --git a/apps/dm/app/(base-layout)/pages/sdmMng/sdmInfoInsert/page.tsx b/apps/dm/app/(base-layout)/pages/sdmMng/sdmInfoInsert/page.tsx index 670b2a612..71eaad57d 100644 --- a/apps/dm/app/(base-layout)/pages/sdmMng/sdmInfoInsert/page.tsx +++ b/apps/dm/app/(base-layout)/pages/sdmMng/sdmInfoInsert/page.tsx @@ -74,7 +74,7 @@ export type FormData = z.infer; function SdmInfoInsertContent() { const searchParams = useSearchParams(); - const {map, insertSdmInfoData, Sdm ,setSubsInfo} = useDmStore((state) => state); + const {insertSdmInfoData, Sdm ,setSubsInfo} = useDmStore((state) => state); const { successLogin } = Sdm; const [ismoneyDisabled, setIsmoneyDisabled] = useState(false); const formSchema = sdmInfoFormSchema; @@ -129,7 +129,7 @@ function SdmInfoInsertContent() { } setSubsInfo([]); // subsInfo 초기화 후보지 처음 화면 들어올때 초기화(스토어내 객체만) - }, []); + }, [setSubsInfo]); const form = useForm({ resolver: zodResolver(formSchema), @@ -137,15 +137,27 @@ function SdmInfoInsertContent() { mode: "onChange", }); - + const subsInfo = useDmStore((state) => state.map.subsInfo); useEffect(()=>{ //map.subsInfo 스토어 객체 값이 변경되면 해당 값이 폼에 자동으로 들어가도록 수정 대신 값이 있어야함 - if(map.subsInfo.length>0){ - form.setValue('candidateTitle', map.subsInfo) + if(subsInfo.length>0){ + form.setValue('candidateTitle', subsInfo) } -},[map.subsInfo, form]) +},[subsInfo, form]) + +//후보지 팝업 값 셋팅 +useEffect(() => { + const handleMessage = (event: MessageEvent) => { + if (event.data?.type === 'SET_SUBS_INFO') { + setSubsInfo(event.data.payload); // zustand로 값 설정 + } + }; + + window.addEventListener('message', handleMessage); + return () => window.removeEventListener('message', handleMessage); +}, [setSubsInfo]); // 로그인 성공 시 모달 닫기 및 폼 업데이트 useEffect(() => { diff --git a/apps/dm/app/(base-layout)/pages/wrAbout/page.tsx b/apps/dm/app/(base-layout)/pages/wrAbout/page.tsx index 8d5bdd0ba..04dfa58df 100644 --- a/apps/dm/app/(base-layout)/pages/wrAbout/page.tsx +++ b/apps/dm/app/(base-layout)/pages/wrAbout/page.tsx @@ -53,14 +53,16 @@ export default function WrAboutPage() { }, observerOptions); // 각 카드 요소를 관찰 - cardRefs.current.forEach((card) => { + const cardsSnapshot = [...cardRefs.current]; + + cardsSnapshot.forEach((card) => { if (card) { observer.observe(card); } }); - + return () => { - cardRefs.current.forEach((card) => { + cardsSnapshot.forEach((card) => { if (card) { observer.unobserve(card); } diff --git a/apps/dm/app/(popup-layout)/map/popup/page.tsx b/apps/dm/app/(popup-layout)/map/popup/page.tsx index 908f2f09c..dc4e404ce 100644 --- a/apps/dm/app/(popup-layout)/map/popup/page.tsx +++ b/apps/dm/app/(popup-layout)/map/popup/page.tsx @@ -1,42 +1,46 @@ "use client"; import Gis from "@common/business_components/ui/map/Gis"; -import { useEffect, useRef, useState, useCallback } from "react"; +import { useEffect, useRef, useState } from "react"; import { mapService } from "../service"; import GisApp from "@common/business_components/ui/map/lib/gis.core"; import { SearchBar } from "@/components/ui/map/searchBar/searchBar"; -import { useDmStore } from "@/app/store"; +import SelectedFcltList from "./selectedFcltList"; +import type { SubsInfo } from "@/app/store/map/type"; -// export type SubsInfo={ -// ruseUtztnFcltNm: string; // 재이용시설명 -// ruseUtztnFcltCd: string; // 재이용시설 코드 -// } const Map = () => { - - //수요처 저장하는 함수 setSubsInfo - //setSubsInfo(subsInfo:SubsInfo[]):void - const { map,setSubsInfo } = useDmStore( - (state) => state - ); - //저장된 수요처 리스트 - const { subsInfo } = map; - const [treeData, setTreeData] = useState([]); // 초기값을 객체 또는 null로 설정 const gisMaps = useRef(null); const [isOpen, setIsOpen] = useState(false); // 팝업 열기/닫기 상태 const [isSearchModalOpen, setIsSearchModalOpen] = useState(true); // SearchModal 열림 상태 const [featureData, setFeatureData] = useState(null); // 선택된 피쳐 데이터 - const [checkedLayers, setCheckedLayers] = useState([]); - const [checkedLayersData, setCheckedLayersData] = useState([]); - const [selectDataPk, setSelectDataPk] = useState(""); - const [subs, setSubs] = useState([]); - - //적용 버튼 클릭 이벤트 + const [selectDataPk, setSelectDataPk] = useState>({}); + const [selectedFclts, setSelectedFclts] = useState([]); + + //적용 버튼 클릭 이벤트 const onApply = () => { - //setSubs([{'41110PB001R1':'수원제1,2 재이용1'},{'41110PB001R2':'수원제1,2 재이용2'}]); - console.log(subs) + if (selectedFclts.length > 3) { + alert("재이용시설 후보지는 최대 3개까지만 선택할 수 있습니다."); + return; + } + + const subsInfoList: SubsInfo[] = selectedFclts.map((fclt) => ({ + ruseUtztnFcltNm: fclt.ruseUtztnFcltNm, + ruseUtztnFcltCd: fclt.pbnstRuseFcltId, + })); + + window.opener.postMessage( + { + type: "SET_SUBS_INFO", + payload: subsInfoList, // 또는 필요한 데이터 + }, + "*" + ); + + window.close(); }; + //트리 레이어 데이터 조회 const fetchData = async () => { try { const response = await mapService.getLyrStngs(); @@ -46,76 +50,167 @@ const Map = () => { } }; - // 팝업 닫기 핸들러를 useCallback으로 메모이제이션 - const handlePopupClose = useCallback(() => { - setIsOpen(false); - setFeatureData(null); - }, []); + //하수처리시설코드로 재이용시설 목록 조회 + const reuseData = async (ruseFcltPk: any) => { + try { + const response = await mapService.getPbnstRuseFclts(ruseFcltPk); + const data = response.data || []; + setSelectedFclts((prev) => { + const newItems = data.filter( + (item: any) => + item && + Object.keys(item).length > 0 && + !prev.some((f) => f.pbnstRuseFcltId === item.pbnstRuseFcltId) + ); + console.log(newItems) + return [...prev, ...newItems]; + }); + + } catch (error) { + console.error("데이터 불러오기 실패:", error); + } + }; // 컴포넌트 마운트 시 1회 실행 useEffect(() => { fetchData(); }, []); - useEffect(() => { - console.log("Selected Data Pk: ", selectDataPk); - }, [selectDataPk]); + // 레이어 생성 후 초기화 처리 + const onCheckedLayersChange = () => { + //수요처 ID + const urlParams = new URLSearchParams(window.location.search); + const dmdInfoId = urlParams.get("dmdInfoId"); - // 체크된 레이어 정보를 받는 핸들러 - const handleCheckedLayersChange = ( - checkedIds: string[], - checkedNodesData: any[] - ) => { - setCheckedLayers(checkedIds); - setCheckedLayersData(checkedNodesData); - }; - - // SearchModal, GisContent 등에서 호출 - const onFeatureSelect = (data: any) => { - const map = gisMaps.current; - if (!map) { - console.warn("지도가 초기화되지 않았습니다."); - return; - } - - // 항상 같은 select 객체 사용 - const select = GisApp.Module.select as any; - if (!select) return; - - // 1. 기존 선택 초기화 - select.resetSelectedFeature(); - - // 2. data가 OpenLayers Feature 인스턴스인지 확인 + //수요처 위치로 화면 이동 + const maps = gisMaps.current; + const layer = GisApp.Module.layer as any; + const layerNm = "tb_wr_ruse_fclt_dmd_co"; + const featureId = `${layerNm}.${dmdInfoId}`; let foundFeature: any = null; - if ( - data && - typeof data.getGeometry === "function" && - typeof data.setStyle === "function" - ) { - // 이미 Feature 인스턴스라면 그대로 사용 - foundFeature = data; - } else { - // 아니면 featureId로 맵에서 Feature 인스턴스를 찾아서 사용 - const featureId = data.fid || data.ftrIdn || data.id; - map - .getLayers() - .getArray() - .forEach((layer: any) => { + + maps + .getLayers() + .getArray() + .forEach((layer: any) => { + if (layer.get("lyrTyNm") == layerNm) { const source = layer.getSource?.(); if (source?.getFeatureById) { const feature = source.getFeatureById(String(featureId).trim()); - if (feature) foundFeature = feature; + if (feature) { + foundFeature = feature; + return; + } } - }); + } + }); + + if (foundFeature) { + const geometry = foundFeature.getGeometry(); + + if (geometry) { + const extent = geometry.getExtent(); + + if (geometry.getType() === "Point") { + maps.getView().setCenter(geometry.getCoordinates()); + maps.getView().setZoom(11); + } else { + maps.getView().fit(extent, { + duration: 300, + padding: [30, 30, 30, 30], + maxZoom: 16, + }); + } + } + + layer.highLightFeature( + [foundFeature], + { + id: GisApp.hlLayerId, + zIndex: 10, + opacity: 1, + selectable: true, + }, + { width: 30 } + ); } - // 3. 피처 선택 + const formatNumber = (val: any) => { + const num = Number(val); + return isNaN(num) ? "-" : num.toLocaleString(); + }; + + // 재이용시설 오버레이 + maps + .getLayers() + .getArray() + .forEach((layer: any) => { + if (layer.get("lyrTyNm") === "tb_wr_bdst_ruse_fclt") { + const source = layer.getSource?.(); + if (source?.getFeatures) { + const features = source.getFeatures(); + + features.forEach((feature: any) => { + // 필요한 고유 ID 또는 속성 가져오기 (예: ftrIdn 또는 feature.getId()) + const featureId = + feature.getId() || feature.get("ftrIdn") || Math.random(); + + // HTML 또는 React 오버레이 컴포넌트 구성 + const overlayContent = ` +
+ +
+ ${feature.get("swtrmt_fclt_nm") || "-"} +
+ + + + + + + + + +
용량(m³/일)${formatNumber(feature.get("fclt_cpct"))}
+
+ `; + feature.set("html", overlayContent); + feature.set("overLayId", `overLay.${featureId}`); + }); + } + } + }); + }; + + // 검색해서 재이용시설 선택하면 해당 시설로 화면 이동 처리 + useEffect(() => { + if (!selectDataPk || Object.keys(selectDataPk).length === 0) return; + const maps = gisMaps.current; + let foundFeature: any = null; + const layerNm = "tb_wr_bdst_ruse_fclt"; + const featureId = `${layerNm}.${selectDataPk.bscFctCd}`; + maps + .getLayers() + .getArray() + .forEach((layer: any) => { + if (layer.get("lyrTyNm") == layerNm) { + const source = layer.getSource?.(); + if (source?.getFeatureById) { + const feature = source.getFeatureById(String(featureId).trim()); + if (feature) { + foundFeature = feature; + return; + } + } + } + }); + if (foundFeature) { const geometry = foundFeature.getGeometry(); if (geometry) { const extent = geometry.getExtent(); - map.getView().fit(extent, { + maps.getView().fit(extent, { duration: 300, padding: [50, 50, 50, 50], maxZoom: 16, @@ -123,21 +218,23 @@ const Map = () => { nearest: true, }); } - select.selectFeature(foundFeature, true); } else { - console.warn("OpenLayers Feature를 찾을 수 없습니다:", data); + console.warn("OpenLayers Feature를 찾을 수 없습니다:", selectDataPk); } + }, [selectDataPk]); - setFeatureData(data); - setIsOpen(true); - setIsSearchModalOpen(false); + // 재이용시설 클릭 시 선택 시설목록에 추가 + const onFeatureSelect = (data: any) => { + // 처리시설코드 + const swtrmtFcltCd = data.get("swtrmt_fclt_cd"); + reuseData(swtrmtFcltCd); }; return ( <> { - if (hasuPk !== "") { + setHasuPk={(hasuPk: any) => { + if (hasuPk) { setSelectDataPk(hasuPk); } }} @@ -150,7 +247,7 @@ const Map = () => { gisMaps={gisMaps} isMiniMap={false} searchView={undefined} - detailView={null} + detailView={undefined} onFeatureSelect={onFeatureSelect} setIsOpen={setIsOpen} setFeatureData={setFeatureData} @@ -158,11 +255,14 @@ const Map = () => { setIsSearchModalOpen={setIsSearchModalOpen} isOpen={isOpen} isSearchModalOpen={isSearchModalOpen} - onCheckedLayersChange={handleCheckedLayersChange} + onCheckedLayersChange={onCheckedLayersChange} imgVisble={false} - setData={setSubs} /> + setSelectedFclts(updatedList)} + /> ); }; diff --git a/apps/dm/app/(popup-layout)/map/popup/sdmInfoMap.tsx b/apps/dm/app/(popup-layout)/map/popup/sdmInfoMap.tsx deleted file mode 100644 index 84dc49119..000000000 --- a/apps/dm/app/(popup-layout)/map/popup/sdmInfoMap.tsx +++ /dev/null @@ -1,147 +0,0 @@ -"use client"; - -import Gis from "@common/business_components/ui/map/Gis"; -import { useEffect, useRef, useState, useCallback } from "react"; -import { mapService } from "../service"; -import GisApp from "@common/business_components/ui/map/lib/gis.core"; -import { SearchBar } from "@/components/ui/map/searchBar/searchBar"; - -const Map = () => { - const [treeData, setTreeData] = useState([]); // 초기값을 객체 또는 null로 설정 - const gisMaps = useRef(null); - const [isOpen, setIsOpen] = useState(false); // 팝업 열기/닫기 상태 - const [isSearchModalOpen, setIsSearchModalOpen] = useState(true); // SearchModal 열림 상태 - const [featureData, setFeatureData] = useState(null); // 선택된 피쳐 데이터 - const [checkedLayers, setCheckedLayers] = useState([]); - const [checkedLayersData, setCheckedLayersData] = useState([]); - const [selectDataPk, setSelectDataPk] = useState(""); - - const fetchData = async () => { - try { - const response = await mapService.getLyrStngs(); - setTreeData(response); // API 응답 저장 - } catch (error) { - console.error("데이터 불러오기 실패:", error); - } - }; - - // 팝업 닫기 핸들러를 useCallback으로 메모이제이션 - const handlePopupClose = useCallback(() => { - setIsOpen(false); - setFeatureData(null); - }, []); - - // 컴포넌트 마운트 시 1회 실행 - useEffect(() => { - fetchData(); - }, []); - - useEffect(() => { - console.log("Selected Data Pk: ", selectDataPk); - }, [selectDataPk]); - - // 체크된 레이어 정보를 받는 핸들러 - const handleCheckedLayersChange = ( - checkedIds: string[], - checkedNodesData: any[] - ) => { - setCheckedLayers(checkedIds); - setCheckedLayersData(checkedNodesData); - }; - - // SearchModal, GisContent 등에서 호출 - const onFeatureSelect = (data: any) => { - const map = gisMaps.current; - if (!map) { - console.warn("지도가 초기화되지 않았습니다."); - return; - } - - // 항상 같은 select 객체 사용 - const select = GisApp.Module.select as any; - if (!select) return; - - // 1. 기존 선택 초기화 - select.resetSelectedFeature(); - - // 2. data가 OpenLayers Feature 인스턴스인지 확인 - let foundFeature: any = null; - if ( - data && - typeof data.getGeometry === "function" && - typeof data.setStyle === "function" - ) { - // 이미 Feature 인스턴스라면 그대로 사용 - foundFeature = data; - } else { - // 아니면 featureId로 맵에서 Feature 인스턴스를 찾아서 사용 - const featureId = data.fid || data.ftrIdn || data.id; - map - .getLayers() - .getArray() - .forEach((layer: any) => { - const source = layer.getSource?.(); - if (source?.getFeatureById) { - const feature = source.getFeatureById(String(featureId).trim()); - if (feature) foundFeature = feature; - } - }); - } - - // 3. 피처 선택 - if (foundFeature) { - const geometry = foundFeature.getGeometry(); - if (geometry) { - const extent = geometry.getExtent(); - - map.getView().fit(extent, { - duration: 300, - padding: [50, 50, 50, 50], - maxZoom: 16, - constrainResolution: true, - nearest: true, - }); - } - select.selectFeature(foundFeature, true); - } else { - console.warn("OpenLayers Feature를 찾을 수 없습니다:", data); - } - - setFeatureData(data); - setIsOpen(true); - setIsSearchModalOpen(false); - }; - - return ( - <> - { - if (hasuPk !== "") { - setSelectDataPk(hasuPk); - } - }} - /> -
- -
- - ); -}; - -export default Map; diff --git a/apps/dm/app/(popup-layout)/map/popup/selectedFcltList.tsx b/apps/dm/app/(popup-layout)/map/popup/selectedFcltList.tsx new file mode 100644 index 000000000..bdf4d7e2c --- /dev/null +++ b/apps/dm/app/(popup-layout)/map/popup/selectedFcltList.tsx @@ -0,0 +1,81 @@ +import React from "react"; + +type FcltItem = { + pbnstRuseFcltId: string; + ruseUtztnFcltNm: string; + splyExtraCpct?: number; + ruseCpct?: number; + [key: string]: any; +}; + +export default function SelectedFcltList({ + data = [], + onRemove, +}: { + data: FcltItem[]; + onRemove: (updatedList: FcltItem[]) => void; +}) { + const handleDelete = (code: string) => { + const updatedList = data.filter((item) => item.pbnstRuseFcltId !== code); + onRemove(updatedList); + }; + + const TableContent = ( + <> + + + 시설명 + + 하수처리량 +
+ (㎥/일) + + + 재이용량 +
+ (㎥/일) + + 삭제 + + + + {data.map((item) => ( + + {item.ruseUtztnFcltNm} + + {Number(item.splyExtraCpct || 0).toLocaleString()} + + + {Number(item.ruseCpct || 0).toLocaleString()} + + + + + + ))} + + + ); + + if (data.length === 0) return null; + + return ( +
+

+ 선택된 시설 +

+
+ + {TableContent} +
+
+
+ ); +} diff --git a/apps/dm/app/(popup-layout)/map/popup/windowPotal.tsx b/apps/dm/app/(popup-layout)/map/popup/windowPotal.tsx deleted file mode 100644 index 38b8fc7db..000000000 --- a/apps/dm/app/(popup-layout)/map/popup/windowPotal.tsx +++ /dev/null @@ -1,94 +0,0 @@ -import React, { useState, useEffect, useRef, useCallback } from 'react'; -import { createPortal } from 'react-dom'; - -// 현재 문서의 스타일(CSS)을 새 창으로 복사하는 함수 -const copyStyles = (sourceDoc: Document, targetDoc: Document) => { - Array.from(sourceDoc.styleSheets).forEach(styleSheet => { - try { - if (styleSheet.cssRules) { //