diff --git a/apps/fo/app/pages/(with-layout)/layout.tsx b/apps/fo/app/pages/(with-layout)/layout.tsx index f77bc244b..e1f6f664e 100644 --- a/apps/fo/app/pages/(with-layout)/layout.tsx +++ b/apps/fo/app/pages/(with-layout)/layout.tsx @@ -21,7 +21,6 @@ export default function RootLayout({ setCurrentMenuInfo(id); getMenuAuth(id.menuCd); router.push(id.urlPath); - console.log(id); }; return ( diff --git a/apps/sn/app/(map-layout)/map/components/common/index.ts b/apps/sn/app/(map-layout)/map/components/common/index.ts index 2581ac0fa..d2aed5956 100644 --- a/apps/sn/app/(map-layout)/map/components/common/index.ts +++ b/apps/sn/app/(map-layout)/map/components/common/index.ts @@ -1,5 +1,5 @@ -import MsSwagePpl from "../popupComponent/MsSwagePpl"; -import sewagePump from "../popupComponent/sewagePump"; +import MsSwagePpl from "../popupComponent/MsSwagePpl"; // 하수관로 +import sewagePump from "../popupComponent/sewagePump"; // 하수펌프 import smh from "../popupComponent/smh"; import Ssc from "../popupComponent/Ssc"; import StormWaterPump from "../popupComponent/stormWaterPump"; diff --git a/apps/sn/app/(map-layout)/map/components/popupComponent/featureClickEventPopup.tsx b/apps/sn/app/(map-layout)/map/components/popupComponent/featureClickEventPopup.tsx index 031c69d8a..11e61b006 100644 --- a/apps/sn/app/(map-layout)/map/components/popupComponent/featureClickEventPopup.tsx +++ b/apps/sn/app/(map-layout)/map/components/popupComponent/featureClickEventPopup.tsx @@ -28,6 +28,10 @@ import { VerticalTable } from "@/components/ui/verticalTable"; import { useGisContext } from "@common/business_components/ui/map/Gis"; import { Button } from "@common/components/ui"; import { useEffect, useRef, useState, useMemo } from "react"; +import Style from "ol/style/Style.js"; +import Fill from "ol/style/Fill.js"; +import Stroke from "ol/style/Stroke.js"; +import CircleStyle from "ol/style/Circle.js"; import { getLayerContentComponent } from "../common"; import { SwtrmtFcltCol, SwtrmtFcltCol2, SwtrmtPrcsZoneCol, SwtrmtPrcsZoneCol2, untrtSwgRainCol1, untrtSwgRainCol2, MsSwagePplCol, MsSwagePplCol2, ExclnSewagePumpCol, ExclnSewagePumpCol2, DrtwSewagePumpCol, DrtwSewagePumpCol2, MsSwageMhlCol, MsSwageMhlCol2, stofcbrCol, stofcbrCol2, MS_SWAGE_MHL_CODES, DRTW_SEWAGE_PUMP_USE_CODES } from "../common/table"; import InfilChart from './chart/infilChart'; @@ -38,6 +42,27 @@ import { codeMapper } from "../common/codeMapper"; import LoadingOverlay from '@/components/ui/LoadingBar'; import { searchUpperPpls } from "./smh"; +const linkedFacilityHighlightStyle = new Style({ + image: new CircleStyle({ + radius: 18, + fill: new Fill({ + color: 'rgba(255, 235, 59, 0.35)', + }), + stroke: new Stroke({ + color: 'rgba(255, 193, 7, 0.45)', + width: 2, + }), + }), + fill: new Fill({ + color: 'rgba(255, 235, 59, 0.2)', + }), + stroke: new Stroke({ + color: 'rgba(255, 193, 7, 0.35)', + width: 2.5, + }), + zIndex: 9999, +}); + const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, featureData: any }) => { const { gisMap } = useGisContext() as unknown as { checkedLayerIds: React.MutableRefObject; treeData: { lyrSn: number }[] | null; gisCore: any; gisMap: any; }; @@ -59,6 +84,8 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, const [isLoading, setIsLoading] = useState(false); // 로딩 상태 const [chmbrXcnts, setChmbrXcnts] = useState(''); // 로딩 상태 const [chmbrYdnts, setChmbrYdnts] = useState(''); // 로딩 상태 + const linkedFacilityFeatureRef = useRef(null); + const linkedFacilityOriginalStyleRef = useRef(null); // 🎯 관로관리번호 추출 const pplMngNo = featureData?.values_?.fclt_mng_no || ""; @@ -77,6 +104,70 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, }; }, [featureData?.values_]); + const clearLinkedFacilityHighlight = () => { + if (linkedFacilityFeatureRef.current) { + linkedFacilityFeatureRef.current.setStyle(linkedFacilityOriginalStyleRef.current ?? undefined); + linkedFacilityFeatureRef.current.changed?.(); + } + + linkedFacilityFeatureRef.current = null; + linkedFacilityOriginalStyleRef.current = null; + }; + + // 하수처리구역에 연결된 하수처리시설 피처를 찾아 기존 아이콘 위에 노랑 하이라이트를 덧씌운다. + const highlightLinkedFacility = (facilityCode: string | null | undefined) => { + clearLinkedFacilityHighlight(); + + if (!gisMap || !facilityCode) return; + + const normalizedFacilityCode = String(facilityCode).trim(); + const layers = gisMap.getLayers().getArray(); + + for (const layer of layers) { + const source = layer?.getSource?.(); + if (!source?.getFeatures) continue; + + const features = source.getFeatures(); + const targetFeature = features.find((feature: any) => { + const featureId = feature?.getId?.(); + const isFacilityLayer = typeof featureId === 'string' && featureId.startsWith('tb_il_swage_prcs_fclt.'); + const featureFacilityCode = feature?.get?.('swtrmt_fclt_cd'); + + return isFacilityLayer && String(featureFacilityCode ?? '').trim() === normalizedFacilityCode; + }); + + if (targetFeature) { + const originalFeatureStyle = targetFeature.getStyle?.(); + const featureLayer = targetFeature.getLayer?.(); + const layerStyleFn = featureLayer?.getStyleFunction?.() ?? null; + + linkedFacilityOriginalStyleRef.current = originalFeatureStyle; + linkedFacilityFeatureRef.current = targetFeature; + targetFeature.setStyle((feature: any, resolution: number) => { + let styles: any[] = []; + + if (typeof originalFeatureStyle === 'function') { + const originalStyles = originalFeatureStyle(feature, resolution); + if (Array.isArray(originalStyles)) styles.push(...originalStyles); + else if (originalStyles) styles.push(originalStyles); + } else if (originalFeatureStyle) { + if (Array.isArray(originalFeatureStyle)) styles.push(...originalFeatureStyle); + else styles.push(originalFeatureStyle); + } else if (layerStyleFn) { + const layerStyles = layerStyleFn(feature, resolution); + if (Array.isArray(layerStyles)) styles.push(...layerStyles); + else if (layerStyles) styles.push(layerStyles); + } + + styles.push(linkedFacilityHighlightStyle); + return styles; + }); + targetFeature.changed?.(); + return; + } + } + }; + // 피쳐데이터의 현재 레이어 이름 가져오는 함수 const getSelectedLayers = () => { let result = null; @@ -348,6 +439,26 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, fetchData(); }, [featureId, processedData]); + useEffect(() => { + const layerTypeNm = featureData?.values_ + ? featureData?.id_?.split('.')?.[0] + : featureData?.lyrTypeNm; + + if (layerTypeNm === 'tb_ms_swage_prcs_zone') { + const facilityCode = featureData?.values_?.swtrmt_fclt_cd ?? featureData?.swtrmtFcltCd; + highlightLinkedFacility(facilityCode); + return () => { + clearLinkedFacilityHighlight(); + }; + } + + clearLinkedFacilityHighlight(); + + return () => { + clearLinkedFacilityHighlight(); + }; + }, [featureData, gisMap]); + // 저장 시 모달 띄우기 const handleSaveClick = () => { setIsModalOpen(true); // 모달 열기 @@ -525,4 +636,4 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, ) } -export default FeatureClickEventPopup; \ No newline at end of file +export default FeatureClickEventPopup; diff --git a/apps/sn/app/(map-layout)/map/searchModal/reportTab.tsx b/apps/sn/app/(map-layout)/map/searchModal/reportTab.tsx index a90284dde..ad57c0a5f 100644 --- a/apps/sn/app/(map-layout)/map/searchModal/reportTab.tsx +++ b/apps/sn/app/(map-layout)/map/searchModal/reportTab.tsx @@ -249,7 +249,7 @@ const ReportTab: React.FC = ({ open, isSearch = true, ctpvNm, sg { - return (<>조회 결과 ({tableCount.toLocaleString('ko-KR')})건); + return (<>조회 결과); })() } tableData={tableData} diff --git a/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx b/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx index d179652f3..3ce334518 100644 --- a/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx +++ b/apps/sn/app/(map-layout)/map/searchModal/searchModal.tsx @@ -130,7 +130,7 @@ const SearchModal: React.FC = () => { { const label = BASIC_TYPES.find((type) => type.val === basicType)?.text - return (<>{label} 조회 결과 ({tableCount.toLocaleString('ko-KR')})건); + return (<>{label} 조회 결과); })() } tableData={tableData} @@ -161,7 +161,7 @@ const SearchModal: React.FC = () => { headName={ (() => { const label = WORK_TYPES.find((type) => type.val === workType)?.text - return (<>{label} 사업 현황 ({tableCount.toLocaleString('ko-KR')})건); + return (<>{label} 사업 현황); })() } tableData={tableData} @@ -188,7 +188,7 @@ const SearchModal: React.FC = () => { headName={ (() => { const label = WORK_TYPES.find((type) => type.val === workType)?.text - return (<>{label} 결과표 ({detailTableCount.toLocaleString('ko-KR')})건); + return (<>{label} 결과표); })() } tableData={detailTableData} diff --git a/apps/wr/app/(with-layout)/matMng/coMng/ldrmntCoSttn/page.tsx b/apps/wr/app/(with-layout)/matMng/coMng/ldrmntCoSttn/page.tsx index 8a6744833..eee7d9f73 100644 --- a/apps/wr/app/(with-layout)/matMng/coMng/ldrmntCoSttn/page.tsx +++ b/apps/wr/app/(with-layout)/matMng/coMng/ldrmntCoSttn/page.tsx @@ -248,7 +248,7 @@ export default function LdrmntCoSttn() {
= { headName: React.ReactNode | undefined; tableData: T[]; @@ -123,17 +124,20 @@ export function UiTable>({ }; const leafCols = getLeafCols(headlist); + const totalLabel = typeof total === "number" ? total.toLocaleString("ko-KR") : " "; + const hasTotal = typeof total === "number"; + return ( <> {headName ? ( headButton ? (
-

{headName}

+

{headName} 총 {totalLabel}건

<>{headButton}
) : (
-

{headName}

+

{headName} 총 {totalLabel}건

) ) : ( @@ -286,7 +290,7 @@ export function UiTable>({ )} - {total <= 0 ? ( + {!hasTotal || total <= 0 ? ( <> ) : (