물재이용 지도 오버레이 팝업 수정(드래그, 축소 기능 추가)
This commit is contained in:
@@ -18,7 +18,6 @@ import toast, { Toaster } from "react-hot-toast";
|
||||
import createOverlayManager from "@common/business_components/ui/map/GisOverlay";
|
||||
import { createRoot } from "react-dom/client";
|
||||
import WrOverLay from "@/components/ui/map/wrOverLay/wrOverLay";
|
||||
import type { comCodeState } from "@/app/store/common/code";
|
||||
|
||||
const Map = () => {
|
||||
|
||||
@@ -257,60 +256,41 @@ const overlayManagersRef = useRef<any[]>([]);
|
||||
const overlayActiveRef = useRef(false);
|
||||
const moveEndDebounceRef = useRef<number | null>(null);
|
||||
|
||||
const overlayFormatter = (feature: any, props: any) => {
|
||||
const propertise = props || {};
|
||||
// 반환: Promise<HTMLDivElement | null>
|
||||
const overlayFormatter = async (feature: any, props: any) => {
|
||||
const p = props || {};
|
||||
const featureId =
|
||||
feature.getId?.() ??
|
||||
propertise?.id ??
|
||||
propertise?.fid ??
|
||||
propertise?.ftrIdn ??
|
||||
'';
|
||||
feature.getId?.() ?? p?.id ?? p?.fid ?? p?.ftrIdn ?? '';
|
||||
if (!featureId) return null;
|
||||
|
||||
const fcltTableId = featureId.split('.')[0];
|
||||
const fcltCd = getFeatureCd(props, fcltTableId);
|
||||
const tableId = String(featureId).split('.')[0]??'';
|
||||
const fcltCd = getFeatureCd(props, tableId);
|
||||
if (!fcltCd) return null;
|
||||
|
||||
const div = document.createElement("div");
|
||||
let data: any = null;
|
||||
let title = '';
|
||||
|
||||
if (tableId === 'tb_il_swage_prcs_fclt') { data = await pbnMapDetaile(fcltCd); title = '하수처리시설'; }
|
||||
else if (tableId === 'tb_wr_bdst_ruse_fclt') { data = await getRuseMaplListReturn(fcltCd); title = '하수재이용시설'; }
|
||||
else if (tableId === 'tb_wr_rnwtr_utztn_fclt'){ data = await rainMapDetile(fcltCd); title = '빗물이용시설'; }
|
||||
else if (tableId === 'tb_wr_rswtr_fclt') { data = await rswtMapDetile(fcltCd); title = '중수도시설'; }
|
||||
else if (tableId === 'tb_wr_htdfl_fclt') { data = await htdfMapDetile(fcltCd); title = '온배수시설'; }
|
||||
else if (tableId === 'tb_wr_ruse_fclt_dmd_co'){ data = await coSttnMapDetail(fcltCd); title = '수요처'; }
|
||||
else if (tableId === 'tb_wr_tmpr_dmd_co') { data = await ldrmntMapDetile(fcltCd); title = '잠재수요처'; }
|
||||
|
||||
const empty =
|
||||
data == null ||
|
||||
(Array.isArray(data) && data.length === 0) ||
|
||||
(typeof data === 'object' && !Array.isArray(data) && Object.keys(data).length === 0);
|
||||
|
||||
if (empty) return null;
|
||||
|
||||
const div = document.createElement('div');
|
||||
const root = createRoot(div);
|
||||
|
||||
// 비동기 처리만 따로
|
||||
(async () => {
|
||||
let overLayData;
|
||||
let title = '';
|
||||
|
||||
if (fcltTableId === 'tb_il_swage_prcs_fclt') {
|
||||
overLayData = await pbnMapDetaile( fcltCd);
|
||||
title = '하수처리시설';
|
||||
} else if (fcltTableId === 'tb_wr_bdst_ruse_fclt') {
|
||||
overLayData = await getRuseMaplListReturn(fcltCd);
|
||||
title = '하수재이용시설';
|
||||
|
||||
} else if (fcltTableId === 'tb_wr_rnwtr_utztn_fclt') {
|
||||
overLayData = await rainMapDetile(fcltCd);
|
||||
title = '빗물이용시설';
|
||||
} else if (fcltTableId === 'tb_wr_rswtr_fclt') {
|
||||
overLayData = await rswtMapDetile(fcltCd);
|
||||
title = '중수도시설';
|
||||
} else if (fcltTableId === 'tb_wr_htdfl_fclt') {
|
||||
overLayData = await htdfMapDetile(fcltCd);
|
||||
title = '온배수시설';
|
||||
} else if (fcltTableId === 'tb_wr_ruse_fclt_dmd_co') {
|
||||
overLayData = await coSttnMapDetail(fcltCd);
|
||||
title = '수요처';
|
||||
} else if (fcltTableId === 'tb_wr_tmpr_dmd_co') {
|
||||
overLayData = await ldrmntMapDetile(fcltCd);
|
||||
title = '잠재수요처';
|
||||
}
|
||||
|
||||
root.render(<WrOverLay title={title} data={overLayData} code={code as Partial<comCodeState>} />);
|
||||
})();
|
||||
|
||||
// 제거 시 React unmount
|
||||
(div as any).__dispose = () => root.unmount();
|
||||
|
||||
return div; // <-- HTMLDivElement 동기 리턴
|
||||
(div as any).__dispose = () => { try { root.unmount(); } catch {} };
|
||||
root.render(<WrOverLay title={title} data={data} code={code} />);
|
||||
return div; // ← 데이터 있을 때만 HTMLElement 반환
|
||||
};
|
||||
|
||||
const destroyAllManagers = () => {
|
||||
overlayManagersRef.current.forEach(m => { try { m.destroy(); } catch {} });
|
||||
overlayManagersRef.current = [];
|
||||
|
||||
@@ -114,8 +114,6 @@ export const rainMapActionsExport: (set: any) => rainMapActions = (
|
||||
},
|
||||
rainMapDetile: async (featureCd: string) => {
|
||||
try {
|
||||
console.log('featureCd ', featureCd)
|
||||
|
||||
const rainDetail = await getFetch(fetchType.WR,
|
||||
`/reuseFc/rainyInfoList/1/3/0/${featureCd}` )
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { comCodeState } from "@/app/store/common/code";
|
||||
import { formatCommaNumber, formatPhoneNumber } from "./commom";
|
||||
|
||||
const wrLayerList = [
|
||||
{ text: "tb_il_swage_prcs_fclt", val: "bscFctCd" },
|
||||
@@ -28,7 +28,6 @@ const wrFeaturePK = [
|
||||
// { text: "잠재수요처", val: "tb_wr_tmpr_dmd_co" },
|
||||
|
||||
export const getFeatureCd = (featurePropertise: any, layerNm: string) => {
|
||||
console.log("featurePropertise ", featurePropertise);
|
||||
const featureCd =
|
||||
layerNm === "tb_wr_bdst_ruse_fclt"
|
||||
? featurePropertise?.swtrmt_fclt_cd
|
||||
@@ -150,72 +149,73 @@ export function featurePopUpOpen(
|
||||
);
|
||||
}
|
||||
|
||||
export const getOverLayObj = (fclType: string, data: any,code?:Partial<comCodeState>) => {
|
||||
export const getOverLayObj = (fclType: string, data: any,code?:any) => {
|
||||
let newObj = {};
|
||||
let title = "";
|
||||
|
||||
|
||||
|
||||
|
||||
if (fclType === "하수재이용시설") {
|
||||
newObj = {
|
||||
"시설용량(톤/일)": data?.fcltCpct ??"",
|
||||
"시설용량(톤/일)": formatCommaNumber(data?.fcltCpct) ??'확인불가',
|
||||
"재이용률(%)": (data?.fcltCpct / data?.bscFctCpc)||0,
|
||||
"장내용수(m³/년)": data?.inUsage ?? "",
|
||||
"장외용수(m3/년)":data?.outUsage ?? "",
|
||||
// 수요처: data?.reuseBuilding[0]?.rainyObj.pntfColArea ?? "",
|
||||
"장내용수(m³/년)": formatCommaNumber(data?.inUsage) ?? '확인불가',
|
||||
"장외용수(m3/년)":formatCommaNumber(data?.outUsage) ?? '확인불가',
|
||||
// 수요처: data?.reuseBuilding[0]?.rainyObj.pntfColArea ?? '확인불가',
|
||||
};
|
||||
title = data?.bdstNm ?? "";
|
||||
title = data?.bdstNm ?? '확인불가';
|
||||
} else if (fclType === "하수처리시설") {
|
||||
newObj = {
|
||||
"시설용량(톤/일)": data?.fctCpc ?? "",
|
||||
지역구분: data?.efflWqltStndrdNm ?? "",
|
||||
재이용여부: data?.rswtrYn ?? "",
|
||||
"재이용시설 등록여부": data?.ruse ?? "",
|
||||
"시설용량(톤/일)": formatCommaNumber(data?.fctCpc) ?? '확인불가',
|
||||
지역구분: data?.efflWqltStndrdNm ?? '확인불가',
|
||||
재이용여부: data?.rswtrYn ?? '확인불가',
|
||||
"재이용시설 등록여부": data?.ruse ?? '확인불가',
|
||||
};
|
||||
title = data?.bscFctNm ?? "";
|
||||
title = data?.bscFctNm ?? '확인불가';
|
||||
} else if (fclType === "빗물이용시설" ) {
|
||||
newObj = {
|
||||
건물용도: code?.code?.bdstUsgCd.find((item) => item.val===data?.reuseBuilding[0].bdstUsgCd )?.text ?? "",
|
||||
설치완료일: data?.reuseBuilding[0]?.instlCmptnDt ?? "",
|
||||
처리시설유무: data?.reuseBuilding[0]?.rainyObj.filtrTrtmfctYn ?? "",
|
||||
"처리시설용량(m³/일)":
|
||||
data?.reuseBuilding[0]?.rainyObj.filtrTrtmfctCpct ?? "",
|
||||
"저류조용량(m³)": data?.reuseBuilding[0]?.rainyObj.pntfColArea ?? "",
|
||||
건물용도: code?.bdstUsgCd.find((item) => item.val=== String(data?.reuseBuilding[0].bdstUsgCd) )?.text ?? '확인불가',
|
||||
설치완료일: data?.reuseBuilding[0]?.instlCmptnDt ?? '확인불가',
|
||||
처리시설유무: data?.reuseBuilding[0]?.rainyObj.filtrTrtmfctYn ?? '확인불가',
|
||||
"처리시설용량(m³/일)": formatCommaNumber(data?.reuseBuilding[0]?.rainyObj.filtrTrtmfctCpct) ?? '확인불가',
|
||||
"저류조용량(m³)": formatCommaNumber(data?.reuseBuilding[0]?.rainyObj.pntfColArea) ?? '확인불가',
|
||||
};
|
||||
title = data?.bdstNm ?? "";
|
||||
title = data?.bdstNm ?? '확인불가';
|
||||
} else if (fclType === "중수도시설") {
|
||||
newObj = {
|
||||
건물용도: code?.code?.bdstUsgCd.find((item) => item.val===data?.reuseBuilding[0].bdstUsgCd )?.text ?? "",
|
||||
설치완료일: data?.reuseBuilding[0]?.instlCmptnDt ?? "",
|
||||
"주요 처리공정": code?.code?.rswtPrscCdMthdCd.find((item) => item.val===data?.reuseBuilding[0]?.rswtrObj.filtrTrtmfctProcs)?.text ?? "",
|
||||
원수종류: code?.code?.orgwtKndCd.find((item) => item.val === data?.reuseBuilding[0]?.rswtrObj.orgwtKndCd)?.text ?? "",
|
||||
//이용용도: isCode.filtrTrtmfctUsgCd data?.reuseBuilding[0]?.rswtrObj.altUswtrKndCd ?? "",
|
||||
건물용도: code?.bdstUsgCd.find((item) => item.val===String(data?.reuseBuilding[0].bdstUsgCd) )?.text ?? '확인불가',
|
||||
설치완료일: data?.reuseBuilding[0]?.instlCmptnDt ?? '확인불가',
|
||||
"주요 처리공정": code?.rswtPrscCdMthdCd.find((item) => item.val===data?.reuseBuilding[0]?.rswtrObj.filtrTrtmfctProcs)?.text ?? '확인불가',
|
||||
원수종류: code?.orgwtKndCd.find((item) => item.val === data?.reuseBuilding[0]?.rswtrObj.orgwtKndCd)?.text ?? '확인불가',
|
||||
//이용용도: isCode.filtrTrtmfctUsgCd data?.reuseBuilding[0]?.rswtrObj.altUswtrKndCd ?? '확인불가',
|
||||
};
|
||||
title = data?.bdstNm ?? "";
|
||||
title = data?.bdstNm ?? '확인불가';
|
||||
} else if (fclType === "온배수시설") {
|
||||
newObj = {
|
||||
미정: "",
|
||||
미정: '확인불가',
|
||||
// '설치완료일': data?.reuseBuilding[0]?.instlCmptnDt??'',
|
||||
// '처리시설유무': data?.reuseBuilding[0]?.rainyObj.filtrTrtmfctYn??'',
|
||||
// '처리시설용량(m³/일)': data?.reuseBuilding[0]?.rainyObj.filtrTrtmfctCpct??'',
|
||||
// '저류조용량(m³)': data?.reuseBuilding[0]?.rainyObj.pntfColArea??'',
|
||||
};
|
||||
title = data?.bdstNm ?? "";
|
||||
title = data?.bdstNm ?? '확인불가';
|
||||
} else if (fclType === "수요처") {
|
||||
newObj = {
|
||||
'사업자등록번호': data?.dmdInfoId??'',
|
||||
'주소': data?.daddr??'',
|
||||
'전화번호': data?.telno??'',
|
||||
'전화번호': formatPhoneNumber(data?.telno)??'',
|
||||
};
|
||||
title = data?.dmdCoNm ?? "";
|
||||
title = data?.dmdCoNm ?? '확인불가';
|
||||
|
||||
} else if (fclType === "잠재수요처") {
|
||||
newObj = {
|
||||
공장구분: data?.fctrySe ?? "",
|
||||
설립구분: data?.fndnSe ?? "",
|
||||
업종명: data?.tpbizNm ?? "",
|
||||
생산폼목: data?.prdctnItem ?? "",
|
||||
공장구분: data?.fctrySe ?? '확인불가',
|
||||
설립구분: data?.fndnSe ?? '확인불가',
|
||||
업종명: data?.tpbizNm ?? '확인불가',
|
||||
생산폼목: data?.prdctnItem ?? '확인불가',
|
||||
};
|
||||
title = data?.dmdCoNm ?? "";
|
||||
title = data?.dmdCoNm ?? '확인불가';
|
||||
}
|
||||
|
||||
return { title, newObj };
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
"use client";
|
||||
import type { comCodeState } from "@/app/store/common/code";
|
||||
import { getOverLayObj } from "@/components/lib/func/mapUtil";
|
||||
import { Card } from "@common/business_components/ui/chart/lib/card";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import WrOverLayTable from "./WrOverLayTable/WrOverLayTable";
|
||||
import WrOverLayRuse from "./WrOverLayRuse/WrOverLayRuse";
|
||||
import Draggable from "react-draggable";
|
||||
|
||||
export default function WrOverLay({
|
||||
title,
|
||||
@@ -13,12 +13,18 @@ export default function WrOverLay({
|
||||
}: {
|
||||
title: string;
|
||||
data: any;
|
||||
code: Partial<comCodeState>;
|
||||
code: any;
|
||||
}) {
|
||||
const [newOverLayData, setNewOverLayData] = useState<Record<string, string>>(
|
||||
{}
|
||||
);
|
||||
const [typeTitle, setTypeTitle] = useState<string>("");
|
||||
const [pos, setPos] = useState<{ x: number; y: number }>({ x: 0, y: 0 });
|
||||
|
||||
const [open, setOpen] = useState(true);
|
||||
const bubbleLabel = title?.[0] ?? "•";
|
||||
|
||||
const dragRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (title !== "하수재이용시설" && data) {
|
||||
@@ -30,21 +36,85 @@ export default function WrOverLay({
|
||||
|
||||
return (
|
||||
<>
|
||||
<Card className="w-[320px] p-0 shadow-md rounded-xl overflow-hidden border border-slate-300">
|
||||
{/* 파란 제목 바 */}
|
||||
{title !== "하수재이용시설" ? (
|
||||
<div className="bg-sky-600 text-white text-center font-semibold py-2 text-2xl">
|
||||
{typeTitle + "(" + title + ")"}
|
||||
{open ? (
|
||||
<Draggable
|
||||
position={pos}
|
||||
nodeRef={dragRef}
|
||||
onDrag={(e, data) => setPos({ x: data.x, y: data.y })}
|
||||
>
|
||||
<div ref={dragRef}>
|
||||
<Card className="w-[320px] p-0 shadow-md rounded-xl overflow-hidden border border-slate-300 relative">
|
||||
{/* 헤더: 제목 바 + 우측 정렬 버튼 (절대배치 제거, 타이틀 중앙 정렬 유지) */}
|
||||
<div className="bg-sky-600 text-white font-semibold py-2 text-xl">
|
||||
<div className="grid grid-cols-[auto_1fr_auto] items-center gap-2 px-2">
|
||||
{/* 좌측 스페이서: 우측 버튼과 동일한 너비를 차지하도록 동일 버튼을 투명하게 복제 */}
|
||||
<div className="opacity-0 pointer-events-none">
|
||||
<div className="flex gap-2">
|
||||
<button
|
||||
onClick={() => setPos({ x: 0, y: 0 })}
|
||||
className="p-2 bg-gray-200 text-gray-700 shadow hover:bg-gray-300 hover:shadow-md transition"
|
||||
title="원위치"
|
||||
>
|
||||
↺
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => setOpen(false)}
|
||||
aria-label="축소"
|
||||
title="축소"
|
||||
className="p-2 bg-gray-200 text-gray-700 shadow hover:bg-gray-300 hover:shadow-md transition"
|
||||
>
|
||||
–
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 중앙 타이틀 */}
|
||||
<div className="text-2xl text-center truncate">
|
||||
{typeTitle + "(" + title + ")"}
|
||||
</div>
|
||||
|
||||
{/* 우측 실제 버튼 영역 */}
|
||||
<div className="flex gap-2 justify-end">
|
||||
<button
|
||||
onClick={() => setPos({ x: 0, y: 0 })}
|
||||
className="p-2 bg-gray-200 text-gray-700 shadow hover:bg-gray-300 hover:shadow-md transition"
|
||||
title="원위치"
|
||||
>
|
||||
↺
|
||||
</button>
|
||||
|
||||
<button
|
||||
onClick={() => setOpen(false)}
|
||||
aria-label="축소"
|
||||
title="축소"
|
||||
className="p-2 bg-gray-200 text-gray-700 shadow hover:bg-gray-300 hover:shadow-md transition"
|
||||
>
|
||||
–
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* 본문 */}
|
||||
{title === "하수재이용시설" ? (
|
||||
<WrOverLayRuse overLayData={data ?? []} />
|
||||
) : (
|
||||
<WrOverLayTable overLayData={newOverLayData} />
|
||||
)}
|
||||
</Card>
|
||||
</div>
|
||||
) : (
|
||||
<></>
|
||||
)}
|
||||
{title === "하수재이용시설" ? (
|
||||
<WrOverLayRuse overLayData={data ?? []} />
|
||||
) : (
|
||||
<WrOverLayTable overLayData={newOverLayData} />
|
||||
)}
|
||||
</Card>
|
||||
</Draggable>
|
||||
) : (
|
||||
<button
|
||||
onClick={() => setOpen(true)}
|
||||
aria-label="확장"
|
||||
title={title || "정보 보기"}
|
||||
className="w-[30px] h-[30px] bg-white font-bold shadow flex items-center justify-center border border-slate-300"
|
||||
>
|
||||
{bubbleLabel}
|
||||
</button>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
"next": "15.1.7",
|
||||
"ol": "^7.3.0",
|
||||
"ol-ext": "^4.0.11",
|
||||
"react-draggable": "^4.5.0",
|
||||
"react-hook-form": "^7.54.2",
|
||||
"react-hot-toast": "^2.5.2",
|
||||
"react-i18next": "^15.6.0",
|
||||
|
||||
Reference in New Issue
Block a user