From f9ea0d36ce56bda9a353c87d8c30b66a95bc2ffe Mon Sep 17 00:00:00 2001 From: yschoi Date: Thu, 24 Apr 2025 20:57:56 +0900 Subject: [PATCH 1/4] =?UTF-8?q?=ED=95=98=EC=9D=B4=EB=9D=BC=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../business_components/src/ui/map/Gis.js | 18 ++++---- .../src/ui/map/GisContent.js | 24 +++++----- .../business_components/src/ui/map/GisEdit.js | 12 ++--- .../src/ui/map/GisSelect.js | 8 ++-- .../business_components/src/ui/map/GisTree.js | 21 +++------ .../src/ui/map/lib/css/gis.common.css | 6 +-- .../src/ui/map/lib/gis.select.js | 44 +++++++++++++++---- 7 files changed, 77 insertions(+), 56 deletions(-) diff --git a/packages/common/business_components/src/ui/map/Gis.js b/packages/common/business_components/src/ui/map/Gis.js index d0fbefe2a..7c625a928 100644 --- a/packages/common/business_components/src/ui/map/Gis.js +++ b/packages/common/business_components/src/ui/map/Gis.js @@ -21,7 +21,6 @@ export const useGisContext = () => useContext(GisContext); function Gis({ treeData = [] }) { const mapRef = useRef(); - const overviewMapRef = useRef(null); const [mapObject, setMapObject] = useState({}); const gisCore = useMemo(() => new GisApp.core({ targetId: mapRef.current }), []); //const [treeData, setTreeData] = useState([]); @@ -134,22 +133,23 @@ function Gis({ treeData = [] }) {
- - 레이어 - 시설검색 + + 레이어 + 시설검색 - + - +
- - - + + + +
diff --git a/packages/common/business_components/src/ui/map/GisContent.js b/packages/common/business_components/src/ui/map/GisContent.js index 2179a53d8..e58efcaad 100644 --- a/packages/common/business_components/src/ui/map/GisContent.js +++ b/packages/common/business_components/src/ui/map/GisContent.js @@ -6,7 +6,7 @@ import GisSelect from './GisSelect'; import GisToolBar from './GisToolBar'; import './lib/css/gis.edit.css'; import MapToggleBtn from './MapToggleBtn'; -//import GisEdit from './GisEdit'; +import GisEdit from './GisEdit'; export default function GisContent({ edit = false }) { const { gisMap, gisCore } = useGisContext(); @@ -15,14 +15,14 @@ export default function GisContent({ edit = false }) { const [playShow, setPlayShow] = useState({ show: false }); //셀렉트 버튼 이벤트 - const clickFeature = useCallback(feature => { - if (feature) { - const layer = feature.getLayer(); - setVisibleComponents(prevState => ({ - ...prevState, - })); - } - }, []); + // const clickFeature = useCallback(feature => { + // if (feature) { + // const layer = feature.getLayer(); + // setVisibleComponents(prevState => ({ + // ...prevState, + // })); + // } + // }, []); const selectRslt = useCallback(feature => { GisApp.Module.layer.highLightFeature( @@ -52,9 +52,9 @@ export default function GisContent({ edit = false }) { <> {/**/} {/* */} - - - + {/* { */} + {/* */} + {/* {edit && } */} ); diff --git a/packages/common/business_components/src/ui/map/GisEdit.js b/packages/common/business_components/src/ui/map/GisEdit.js index 7c961c865..dcf51545e 100644 --- a/packages/common/business_components/src/ui/map/GisEdit.js +++ b/packages/common/business_components/src/ui/map/GisEdit.js @@ -1,9 +1,9 @@ import GisApp from './lib/gis.core'; import React, { useContext, useEffect, useRef, useState } from 'react'; import { GisContext, setTree } from './Gis'; -import Button from '../button/Button'; +import { Button } from "@common/components"; import { gAlert, gConfirm } from './lib/util'; -import { fclApi } from '../../js/apis/fcl/fclApi'; +// import { fclApi } from '../../js/apis/fcl/fclApi'; export default React.memo(function GisEdit() { const { editLayerItems = [], siteSn, locgovCd, checkedLayerIds } = useContext(GisContext); @@ -96,10 +96,10 @@ export default React.memo(function GisEdit() { return; } if (!gConfirm('CONFIRM', 'SAVE_QUEST')) return false; - const result = await fclApi.saveEditFeatures(editFeatures); - if (result.code === 200) { - editModule.clearUndoRedo(); - } + // const result = await fclApi.saveEditFeatures(editFeatures); + // if (result.code === 200) { + // editModule.clearUndoRedo(); + // } }; useEffect(() => { diff --git a/packages/common/business_components/src/ui/map/GisSelect.js b/packages/common/business_components/src/ui/map/GisSelect.js index 85354d8b5..ae4e15966 100644 --- a/packages/common/business_components/src/ui/map/GisSelect.js +++ b/packages/common/business_components/src/ui/map/GisSelect.js @@ -3,7 +3,7 @@ import GisApp from './lib/gis.core'; import GisOlSelect from './lib/gis.select'; import { useContext } from 'react'; import { GisContext } from './Gis'; -export default React.memo(function GisSelect({ clickFeature }) { +export default React.memo(function GisSelect() { const { gisMap } = useContext(GisContext); const select = new GisOlSelect(gisMap); GisApp.Module.select = select; @@ -12,11 +12,11 @@ export default React.memo(function GisSelect({ clickFeature }) { }; select.getSelectedInfo(function (features) { - clickFeature(features[0]); + //clickFeature(features[0]); }); return ( <> -
+ {/*

선,면,다각형을 이용한 시설물 선택 영역

  • @@ -30,7 +30,7 @@ export default React.memo(function GisSelect({ clickFeature }) {
-
+
*/} ); }); diff --git a/packages/common/business_components/src/ui/map/GisTree.js b/packages/common/business_components/src/ui/map/GisTree.js index 487e8bfec..2e9d6f9c0 100644 --- a/packages/common/business_components/src/ui/map/GisTree.js +++ b/packages/common/business_components/src/ui/map/GisTree.js @@ -23,7 +23,6 @@ export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes setNodes(nodeData); gisCore.setAllLayersOnMap(false); } - console.log(treeData) }, [treeData]); useEffect(() => { @@ -37,7 +36,6 @@ export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes setExpanded(defaultExpanded); gisCore.setLayerOnMap(defaultChecked, true); } - console.log(nodes) }, [nodes]); const getDefaultCheckedNodes = (nodes, values = []) => { @@ -113,7 +111,7 @@ export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes parent: item.upperLyrSn, droppable: !!item.children, data: item, - icon: src ? {item.lyrNm} : '', + icon: src ? {item.lyrNm} : '', }; }); }; @@ -208,18 +206,13 @@ export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes style={{ marginInlineStart: depth * 16 + (!node.droppable ? 16 : 0) }} > {node.droppable && ( - + + )} - handleCheck(node.id)} - className="cursor-pointer" - /> - {node.icon && {node.icon}} - {node.text} + handleCheck(node.id)} className="cursor-pointer"/> + {node.icon && {node.icon}} + {node.text} )} sort={(a, b) => { diff --git a/packages/common/business_components/src/ui/map/lib/css/gis.common.css b/packages/common/business_components/src/ui/map/lib/css/gis.common.css index de98d8a9b..190a98e2e 100644 --- a/packages/common/business_components/src/ui/map/lib/css/gis.common.css +++ b/packages/common/business_components/src/ui/map/lib/css/gis.common.css @@ -102,7 +102,7 @@ .tool-bar { position: absolute; - top: 140px; + top: 70px; right: 20px; z-index: 2; } @@ -244,7 +244,7 @@ background: url(../images/toolbar/btn_cont_zoomin_on.png) no-repeat center #67aee3; } .btn_control_zoomout { - background: url(../images/toolbar/btn_cont_zoomout.png) no-repeat center !important; + background: url(../images/toolbar/btn_cont_zoomout.png) no-repeat center; } .btn_control_zoomout:hover { background: url(../images/toolbar/btn_cont_zoomout_on.png) no-repeat center #67aee3; @@ -346,7 +346,7 @@ .select_control { position: absolute; top: 80px; - left: 250px; + left: 450px; } .select_control h3 { overflow: hidden; diff --git a/packages/common/business_components/src/ui/map/lib/gis.select.js b/packages/common/business_components/src/ui/map/lib/gis.select.js index d48c67a5e..973ae6f95 100644 --- a/packages/common/business_components/src/ui/map/lib/gis.select.js +++ b/packages/common/business_components/src/ui/map/lib/gis.select.js @@ -48,19 +48,31 @@ GisOlSelect.prototype = { this.removeInteration(); } let baseStyle = new Style({ - stroke: new Stroke({ - color: 'rgba(135, 116, 243, 0.4)', - width: 10, + image : new CircleStyle({ + fill : new Fill({ + color : 'rgba(135, 116, 243, 0.4)', + }), + radius : 30 }), + stroke : new Stroke({ + color : 'rgba(135, 116, 243, 0.4)', + width : 10, + }) }); this.selectInteraction = new Select({ - style: function (feature) { + style: function (feature, resolution) { let styles = []; let layer = feature.getLayer(); - let style = GisApp.Module.style.getLayerStyle(layer, null); - if (style) { - styles.push(style); + let styleFn = GisApp.Module.style.getLayerStyleFunction(layer, null); + + if (styleFn) { + const style = styleFn(feature, resolution); + if (Array.isArray(style)) { + styles.push(...style); + } else { + styles.push(style); + } } styles.push(baseStyle); return styles; @@ -72,9 +84,25 @@ GisOlSelect.prototype = { condition: click, hitTolerance: me.defaults.hitTolerance, }); - map.addInteraction(this.selectInteraction); + map.addInteraction(this.selectInteraction); this.selectedFeatures = this.selectInteraction.getFeatures(); + + // 선택된 피처 중심으로 지도 이동 + this.selectInteraction.on('select', function (e) { + const feature = e.selected[0]; + if (feature) { + const geometry = feature.getGeometry(); + if (geometry) { + const extent = geometry.getExtent(); + map.getView().fit(extent, { + duration: 300, + padding: [50, 50, 50, 50], + maxZoom: 16 + }); + } + } + }); }, /** * SELECT 피처 초기화 From 81afc9f3413a3dd92515566c0e1bf361274e911d Mon Sep 17 00:00:00 2001 From: yschoi Date: Fri, 25 Apr 2025 16:12:09 +0900 Subject: [PATCH 2/4] =?UTF-8?q?=ED=83=AD=20=EC=BB=B4=ED=8F=AC=EB=84=8C?= =?UTF-8?q?=ED=8A=B8=20=20=EB=B6=84=EB=A6=AC/=EB=94=94=EC=9E=90=EC=9D=B8?= =?UTF-8?q?=20=EC=88=98=EC=A0=95=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/sn/app/map/page.tsx | 2 +- .../business_components/src/ui/map/Gis.js | 26 ++----- .../business_components/src/ui/map/GisTab.js | 67 +++++++++++++++++++ .../business_components/src/ui/map/GisTree.js | 16 ----- .../src/ui/map/lib/css/gis.common.css | 2 - 5 files changed, 74 insertions(+), 39 deletions(-) create mode 100644 packages/common/business_components/src/ui/map/GisTab.js diff --git a/apps/sn/app/map/page.tsx b/apps/sn/app/map/page.tsx index 4dbb90c49..bd8feb360 100644 --- a/apps/sn/app/map/page.tsx +++ b/apps/sn/app/map/page.tsx @@ -27,7 +27,7 @@ const Map = () => { return (
- +
); }; diff --git a/packages/common/business_components/src/ui/map/Gis.js b/packages/common/business_components/src/ui/map/Gis.js index 7c625a928..2b5f9fc35 100644 --- a/packages/common/business_components/src/ui/map/Gis.js +++ b/packages/common/business_components/src/ui/map/Gis.js @@ -10,18 +10,18 @@ import proj4 from 'proj4'; import { register } from 'ol/proj/proj4'; import GisApp from './lib/gis.core'; import GisContent from './GisContent'; -import GisTree from './GisTree'; import GisLonLat from './GisLonLat'; import GisToolBar from './GisToolBar'; -import GisSearch from './GisSearch'; +import GisTab from './GisTab'; import MapToggleBtn from './MapToggleBtn'; export const GisContext = createContext({gisCore: null, gisMap: null, treeData:null, checkedLayerIds:null}); export const useGisContext = () => useContext(GisContext); -function Gis({ treeData = [] }) { +function Gis({ treeData = [], isSearch = false }) { const mapRef = useRef(); const [mapObject, setMapObject] = useState({}); + const [tabVisible, setTabVisible] = useState(true); const gisCore = useMemo(() => new GisApp.core({ targetId: mapRef.current }), []); //const [treeData, setTreeData] = useState([]); const checkedLayerIds = useRef([]); @@ -125,34 +125,20 @@ function Gis({ treeData = [] }) { } } - const [isSidebarOpen, setIsSidebarOpen] = useState(true); return ( + <> - <>
-
- - - 레이어 - 시설검색 - - - - - - - - -
+
- + ); } diff --git a/packages/common/business_components/src/ui/map/GisTab.js b/packages/common/business_components/src/ui/map/GisTab.js new file mode 100644 index 000000000..baf268879 --- /dev/null +++ b/packages/common/business_components/src/ui/map/GisTab.js @@ -0,0 +1,67 @@ +import React, { useState } from 'react'; +import { Tabs, TabsList, TabsTrigger } from '@common/components/ui/tabs'; +import GisTree from './GisTree'; +import GisSearch from './GisSearch'; +import { ChevronLeft, ChevronRight } from 'lucide-react'; + +function GisTab({ visible, setVisible, isSearch }) { + const [activeTab, setActiveTab] = useState('layers'); + + return ( + <> +
+ + + 레이어 + {isSearch && ( + 검색 + )} + + + {/* GisTree는 항상 마운트되며, 탭 상태에 따라 보여주기만 함 */} +
+ +
+ + {/* GisSearch도 동일하게 처리 */} + {isSearch && ( +
+ +
+ )} +
+ + {/* 접기 버튼 */} + +
+ + {/* 열기 버튼 */} + {!visible && ( + + )} + + ); +} + +export default React.memo(GisTab); diff --git a/packages/common/business_components/src/ui/map/GisTree.js b/packages/common/business_components/src/ui/map/GisTree.js index 2e9d6f9c0..01a3fabc3 100644 --- a/packages/common/business_components/src/ui/map/GisTree.js +++ b/packages/common/business_components/src/ui/map/GisTree.js @@ -116,22 +116,6 @@ export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes }); }; - const findUpLyrId = (nodes, targetLyrId) => { - for (let i = 0; i < nodes.length; i++) { - const node = nodes[i]; - if (node.data && node.data.lyrSn === targetLyrId) { - return node.data.upperLyrSn; - } - if (node.children) { - const upLyrId = findUpLyrId(node.children, targetLyrId); - if (upLyrId) { - return upLyrId; - } - } - } - return null; - }; - const makeLegendSrc = layerinfo => { if(!layerinfo.gobjTy) return null; diff --git a/packages/common/business_components/src/ui/map/lib/css/gis.common.css b/packages/common/business_components/src/ui/map/lib/css/gis.common.css index 190a98e2e..e479fc501 100644 --- a/packages/common/business_components/src/ui/map/lib/css/gis.common.css +++ b/packages/common/business_components/src/ui/map/lib/css/gis.common.css @@ -759,8 +759,6 @@ .puGrayTb { position: relative; width: 98.3%; - cellpadding: 0; - cellspcing: 0; margin-top: 5px; table-layout: fixed; font-family: Verdana; From 11fd638b192255784bb127f4bfad143ad970c1a7 Mon Sep 17 00:00:00 2001 From: JIeunhuh Date: Tue, 29 Apr 2025 11:07:31 +0900 Subject: [PATCH 3/4] =?UTF-8?q?=ED=95=98=EC=88=98=EA=B4=80=EB=A1=9C=20?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=ED=8C=9D=EC=97=85=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/sn/app/components/verticalTable.tsx | 128 ++++++++++++ apps/sn/app/map/featureClickEventPopup.tsx | 192 ++++++++++++++++++ .../business_components/src/ui/map/Gis.js | 11 +- 3 files changed, 330 insertions(+), 1 deletion(-) create mode 100644 apps/sn/app/components/verticalTable.tsx create mode 100644 apps/sn/app/map/featureClickEventPopup.tsx diff --git a/apps/sn/app/components/verticalTable.tsx b/apps/sn/app/components/verticalTable.tsx new file mode 100644 index 000000000..ee27224ac --- /dev/null +++ b/apps/sn/app/components/verticalTable.tsx @@ -0,0 +1,128 @@ +import UiPageNations from "@common/business_components/ui/table/uiPageNations"; +import { Table, TableBody, TableCell, TableRow } from "@common/components"; +import { useEffect, useState } from "react"; + +type UiTableProps = { + headName: React.ReactNode | undefined; + tableData: { [key: string]: string | number | boolean | object }[]; + headlist: UiHeadProps[]; + pageSize: number; + total: number; + children: React.ReactNode | undefined; + cellClick: ((index: number | undefined, headId: string) => void) | undefined; + onPageChange: (page: number) => void; + className?: string; + resetPageNo?: boolean; + isEditable?: boolean; +}; + +type UiHeadProps = { + id: string; + title: string; + align?: string; + type?: string; + options?: string[]; +}; + +export function VerticalTable({ + tableData, + headlist, + pageSize, + total, + children, + headName, + cellClick, + onPageChange, + className = "", + resetPageNo = false, + isEditable = false, +}: UiTableProps) { + const [cur, setCur] = useState(1); + const [formData, setFormData] = useState<{ [key: string]: string | number | boolean }>({}); + + useEffect(() => { + if (resetPageNo) { + setCur(1); + onPageChange(1); + } + }, [onPageChange, resetPageNo]); + + useEffect(() => { + if (tableData.length > 0 && tableData[0]) { + setFormData(tableData[0] as { [key: string]: string | number | boolean }); + } + }, [tableData]); + + const handleInputChange = (id: string, value: string | number | boolean) => { + setFormData((prev) => ({ + ...prev, + [id]: value, + })); + }; + + const handlePageChange = (page: number) => { + setCur(page); + onPageChange(page); + }; + + return ( + <> + {headName ? ( +
+

{headName}

+
+ ) : null} +
+ + + {headlist?.map((head) => ( + + {/* 컬럼 제목 */} + + {head.title} + + {/* 데이터 값 또는 입력 필드 */} + + {isEditable ? ( + head.type === "select" ? ( + + ) : head.type === "input" ? ( + handleInputChange(head.id, e.target.value)} + className="w-full text-xl border-radius-0 border border-gray-300 rounded-sm px-2 py-1" + /> + ) : ( + {formData[head.id] ?? "-"} + ) + ) : ( + {formData[head.id] ?? "-"} + )} + + + ))} + +
+ {total > 0 && ( + + )} +
+ + ); +} \ No newline at end of file diff --git a/apps/sn/app/map/featureClickEventPopup.tsx b/apps/sn/app/map/featureClickEventPopup.tsx new file mode 100644 index 000000000..f30d4cd78 --- /dev/null +++ b/apps/sn/app/map/featureClickEventPopup.tsx @@ -0,0 +1,192 @@ +'use client' +import { VerticalTable } from "@/components/verticalTable"; +import { Button, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@common/components/ui"; +import { useState } from "react"; +const RxTableColumn = [ + { id: 'FTR_CDE', title: '지형지물부호' }, + { id: 'FTR_IDN', title: '관리번호' }, + { id: 'HJD_CDE', title: '행정읍면동코드' }, + { id: 'SHT_NUM', title: '도엽번호' }, + { id: 'MNG_CDE', title: '관리기관코드' }, + { id: 'IST_YMD', title: '설치일자' }, + { id: 'SBA_CDE', title: '하수관용도' }, + { id: 'MOP_CDE', title: '관재질' }, + { id: 'LIT_CDE', title: '규모' }, + { id: 'FOR_CDE', title: '시설물형태' }, + { id: 'STD_DIP', title: '관경' }, + { id: 'STD_HOL', title: '가로길이' }, + { id: 'STD_VEL', title: '세로길이' }, + { id: 'BYC_LEN', title: '연장' }, + { id: 'BEG_DEP', title: '시점깊이' }, + { id: 'END_DEP', title: '종점깊이' }, + { id: 'SBK_HSL', title: '시점관저고' }, + { id: 'SBL_HSL', title: '종점관저고' }, + { id: 'PIP_SLP', title: '평균구배' }, + { id: 'SPH_LIN', title: '차선통로수' }, + { id: 'BST_ARA', title: '우수배수면적' }, + { id: 'DRT_ARA', title: '오수배수면적' }, + { id: 'SBQ_SPD', title: '우천시유속' }, + { id: 'SBR_SPD', title: '청천시유속' }, + { id: 'CNT_NUM', title: '공사번호' }, + { id: 'BOM_CDE', title: '시점맨홀지형지물부호' }, + { id: 'BOM_IDN', title: '시점맨홀관리번호' }, + { id: 'EOM_CDE', title: '중점맨홀지형지물부호' }, + { id: 'EOM_IDN', title: '중점맨홀관리번호' }, + { id: 'SYS_CHK', title: '대장초기화여부' }, + { id: 'PIP_LBL', title: '관라벨' }, + { id: 'IQT_CDE', title: '탐사구분' }, + { id: 'ORG_IDN', title: '기관관리번호' }, + { id: 'SRV_CHK', title: '인입관 유무' } +]; +const tableData = [ + { + FTR_CDE: "001", + FTR_IDN: "관리번호001", + HJD_CDE: "행정코드001", + SHT_NUM: "도엽번호001", + MNG_CDE: "관리기관001", + IST_YMD: "2025-04-16", + SBA_CDE: "하수관용도001", + MOP_CDE: "관재질001", + LIT_CDE: "규모001", + FOR_CDE: "시설물형태001", + STD_DIP: "300mm", + STD_HOL: "500mm", + STD_VEL: "1.2m/s", + BYC_LEN: "100m", + BEG_DEP: "1.5m", + END_DEP: "2.0m", + SBK_HSL: "50m", + SBL_HSL: "48m", + PIP_SLP: "2%", + SPH_LIN: "2개", + BST_ARA: "200㎡", + DRT_ARA: "150㎡", + SBQ_SPD: "3.5m/s", + SBR_SPD: "2.0m/s", + CNT_NUM: "공사번호001", + BOM_CDE: "맨홀지형지물부호001", + BOM_IDN: "맨홀관리번호001", + EOM_CDE: "중점맨홀지형지물부호001", + EOM_IDN: "중점맨홀관리번호001", + SYS_CHK: "초기화여부001", + PIP_LBL: "관라벨001", + IQT_CDE: "탐사구분001", + ORG_IDN: "기관관리번호001", + SRV_CHK: "인입관 유무001", + }, +]; +const inputColumn = [ + { id: "title", title: "설치주제", type: "select", options: ["BTL", "재정사업"] }, + { id: "guyeok", title: "처리구역", type: "select", options: ["구역1", "구역2"] }, + { id: "basu", title: "배수구역", type: "input" }, + { id: "bungu", title: "배수분구", type: "input" }, + { id: "wltjs", title: "지선/간선", type: "select", options: ["지선", "간선"] }, + { id: "flow", title: "흐름방식", type: "select", options: ["자연유하", "압송"] }, + { id: "place", title: "매설위치", type: "select", options: ["도로", "하상"] }, +]; + +const FeatureClickEventPopup = ({ onClose }: { onClose: () => void }) => { + // TODO: 런타임 에러 해결 필요 + const [repairData, setRepairData] = useState([ + { year: "2025", type: "개대체" }, + { year: "2025", type: "보수" }, + ]); + + const [cleaningData, setCleaningData] = useState([ + { year: "2025", type: "청소" }, + { year: "2025", type: "준설" }, + ]); + + const [inspectionData, setInspectionData] = useState([ + { year: "2025", type: "관로기술진단" }, + { year: "2025", type: "관로기술진단" }, + ]); + const handleInputChange = (section: string, index: number, field: string, value: string) => { + const updateData = (data: { year: string; type: string }[], setData: React.Dispatch>) => { + const updated = [...data]; + if (updated[index]) { + updated[index][field as keyof typeof updated[number]] = value; + } + setData(updated); + }; + + if (section === "repair") { + updateData(repairData, setRepairData); + } else if (section === "cleaning") { + updateData(cleaningData, setCleaningData); + } else if (section === "inspection") { + updateData(inspectionData, setInspectionData); + } + }; + + const renderSection = (title: string, data: { year: string; type: string }[], sectionKey: string) => ( +
+ {/* 섹션 제목 */} +
+

{title}

+
+ {/* 테이블 */} +
+ + + + 년도 + 구분 + + + + {data.map((row, index) => ( + + + handleInputChange(sectionKey, index, "year", e.target.value)} + className="w-full text-xl border-radius-0 px-3 py-2 text-base text-center leading-normal" + /> + + {row.type} + + ))} + +
+
+
+ ); + return ( + + ) +} +export default FeatureClickEventPopup; \ No newline at end of file diff --git a/packages/common/business_components/src/ui/map/Gis.js b/packages/common/business_components/src/ui/map/Gis.js index 2b5f9fc35..4361fda09 100644 --- a/packages/common/business_components/src/ui/map/Gis.js +++ b/packages/common/business_components/src/ui/map/Gis.js @@ -14,6 +14,7 @@ import GisLonLat from './GisLonLat'; import GisToolBar from './GisToolBar'; import GisTab from './GisTab'; import MapToggleBtn from './MapToggleBtn'; +import FeatureClickEventPopup from '@/map/featureClickEventPopup'; export const GisContext = createContext({gisCore: null, gisMap: null, treeData:null, checkedLayerIds:null}); export const useGisContext = () => useContext(GisContext); @@ -22,11 +23,14 @@ function Gis({ treeData = [], isSearch = false }) { const mapRef = useRef(); const [mapObject, setMapObject] = useState({}); const [tabVisible, setTabVisible] = useState(true); + const [isOpen, setIsOpen] = useState(true); // 팝업 열기/닫기 상태 const gisCore = useMemo(() => new GisApp.core({ targetId: mapRef.current }), []); //const [treeData, setTreeData] = useState([]); const checkedLayerIds = useRef([]); let gisMap = null; - + const closePopup = () => { + setIsOpen(false); // 팝업 닫기 + }; useEffect(() => { if (!mapRef.current) return; @@ -131,6 +135,11 @@ function Gis({ treeData = [], isSearch = false }) {
+ {isOpen && ( + + )}
From 525307d1ee392efef669de8f912620e6158d142b Mon Sep 17 00:00:00 2001 From: JIeunhuh Date: Wed, 30 Apr 2025 10:49:07 +0900 Subject: [PATCH 4/4] =?UTF-8?q?=ED=94=BC=EC=B3=90=20=ED=81=B4=EB=A6=AD?= =?UTF-8?q?=EC=8B=9C=20=EB=AA=A8=EB=8B=AC=20=EC=98=A4=ED=94=88=20=ED=95=98?= =?UTF-8?q?=EA=B3=A0=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EB=82=98=EC=98=A4?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=9E=91=EC=97=85(=EB=B3=80=EA=B2=BD?= =?UTF-8?q?=ED=95=84=EC=9A=94)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/sn/app/components/verticalTable.tsx | 4 +- apps/sn/app/map/featureClickEventPopup.tsx | 92 +++---------------- apps/sn/app/map/table.ts | 40 ++++++++ .../business_components/src/ui/map/Gis.js | 17 +++- .../src/ui/map/GisContent.js | 4 +- .../src/ui/map/GisSelect.js | 6 +- 6 files changed, 77 insertions(+), 86 deletions(-) create mode 100644 apps/sn/app/map/table.ts diff --git a/apps/sn/app/components/verticalTable.tsx b/apps/sn/app/components/verticalTable.tsx index ee27224ac..b792f3426 100644 --- a/apps/sn/app/components/verticalTable.tsx +++ b/apps/sn/app/components/verticalTable.tsx @@ -39,15 +39,15 @@ export function VerticalTable({ }: UiTableProps) { const [cur, setCur] = useState(1); const [formData, setFormData] = useState<{ [key: string]: string | number | boolean }>({}); - useEffect(() => { if (resetPageNo) { setCur(1); onPageChange(1); } }, [onPageChange, resetPageNo]); - + useEffect(() => { + console.log('data',tableData) if (tableData.length > 0 && tableData[0]) { setFormData(tableData[0] as { [key: string]: string | number | boolean }); } diff --git a/apps/sn/app/map/featureClickEventPopup.tsx b/apps/sn/app/map/featureClickEventPopup.tsx index f30d4cd78..545bc1697 100644 --- a/apps/sn/app/map/featureClickEventPopup.tsx +++ b/apps/sn/app/map/featureClickEventPopup.tsx @@ -2,80 +2,7 @@ import { VerticalTable } from "@/components/verticalTable"; import { Button, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@common/components/ui"; import { useState } from "react"; -const RxTableColumn = [ - { id: 'FTR_CDE', title: '지형지물부호' }, - { id: 'FTR_IDN', title: '관리번호' }, - { id: 'HJD_CDE', title: '행정읍면동코드' }, - { id: 'SHT_NUM', title: '도엽번호' }, - { id: 'MNG_CDE', title: '관리기관코드' }, - { id: 'IST_YMD', title: '설치일자' }, - { id: 'SBA_CDE', title: '하수관용도' }, - { id: 'MOP_CDE', title: '관재질' }, - { id: 'LIT_CDE', title: '규모' }, - { id: 'FOR_CDE', title: '시설물형태' }, - { id: 'STD_DIP', title: '관경' }, - { id: 'STD_HOL', title: '가로길이' }, - { id: 'STD_VEL', title: '세로길이' }, - { id: 'BYC_LEN', title: '연장' }, - { id: 'BEG_DEP', title: '시점깊이' }, - { id: 'END_DEP', title: '종점깊이' }, - { id: 'SBK_HSL', title: '시점관저고' }, - { id: 'SBL_HSL', title: '종점관저고' }, - { id: 'PIP_SLP', title: '평균구배' }, - { id: 'SPH_LIN', title: '차선통로수' }, - { id: 'BST_ARA', title: '우수배수면적' }, - { id: 'DRT_ARA', title: '오수배수면적' }, - { id: 'SBQ_SPD', title: '우천시유속' }, - { id: 'SBR_SPD', title: '청천시유속' }, - { id: 'CNT_NUM', title: '공사번호' }, - { id: 'BOM_CDE', title: '시점맨홀지형지물부호' }, - { id: 'BOM_IDN', title: '시점맨홀관리번호' }, - { id: 'EOM_CDE', title: '중점맨홀지형지물부호' }, - { id: 'EOM_IDN', title: '중점맨홀관리번호' }, - { id: 'SYS_CHK', title: '대장초기화여부' }, - { id: 'PIP_LBL', title: '관라벨' }, - { id: 'IQT_CDE', title: '탐사구분' }, - { id: 'ORG_IDN', title: '기관관리번호' }, - { id: 'SRV_CHK', title: '인입관 유무' } -]; -const tableData = [ - { - FTR_CDE: "001", - FTR_IDN: "관리번호001", - HJD_CDE: "행정코드001", - SHT_NUM: "도엽번호001", - MNG_CDE: "관리기관001", - IST_YMD: "2025-04-16", - SBA_CDE: "하수관용도001", - MOP_CDE: "관재질001", - LIT_CDE: "규모001", - FOR_CDE: "시설물형태001", - STD_DIP: "300mm", - STD_HOL: "500mm", - STD_VEL: "1.2m/s", - BYC_LEN: "100m", - BEG_DEP: "1.5m", - END_DEP: "2.0m", - SBK_HSL: "50m", - SBL_HSL: "48m", - PIP_SLP: "2%", - SPH_LIN: "2개", - BST_ARA: "200㎡", - DRT_ARA: "150㎡", - SBQ_SPD: "3.5m/s", - SBR_SPD: "2.0m/s", - CNT_NUM: "공사번호001", - BOM_CDE: "맨홀지형지물부호001", - BOM_IDN: "맨홀관리번호001", - EOM_CDE: "중점맨홀지형지물부호001", - EOM_IDN: "중점맨홀관리번호001", - SYS_CHK: "초기화여부001", - PIP_LBL: "관라벨001", - IQT_CDE: "탐사구분001", - ORG_IDN: "기관관리번호001", - SRV_CHK: "인입관 유무001", - }, -]; +import { CommonTableColumn } from "./table"; const inputColumn = [ { id: "title", title: "설치주제", type: "select", options: ["BTL", "재정사업"] }, { id: "guyeok", title: "처리구역", type: "select", options: ["구역1", "구역2"] }, @@ -86,7 +13,7 @@ const inputColumn = [ { id: "place", title: "매설위치", type: "select", options: ["도로", "하상"] }, ]; -const FeatureClickEventPopup = ({ onClose }: { onClose: () => void }) => { +const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, featureData:any }) => { // TODO: 런타임 에러 해결 필요 const [repairData, setRepairData] = useState([ { year: "2025", type: "개대체" }, @@ -119,7 +46,18 @@ const FeatureClickEventPopup = ({ onClose }: { onClose: () => void }) => { updateData(inspectionData, setInspectionData); } }; - + function transformFeatureDataToArray(featureData: any, keys: string[]) { + // 필요한 키만 추출하여 새로운 객체 생성 + const transformedData = keys.reduce((acc, key) => { + acc[key] = featureData[key] !== undefined ? featureData[key] : "N/A"; // 값이 없으면 "N/A"로 처리 + return acc; + }, {} as Record); + + // 배열로 감싸서 반환 + return [transformedData]; + } + const keys = CommonTableColumn.map((column) => column.id); + const transformedFeatureData = transformFeatureDataToArray(featureData, keys); const renderSection = (title: string, data: { year: string; type: string }[], sectionKey: string) => (
{/* 섹션 제목 */} @@ -170,7 +108,7 @@ const FeatureClickEventPopup = ({ onClose }: { onClose: () => void }) => {
- new GisApp.core({ targetId: mapRef.current }), []); //const [treeData, setTreeData] = useState([]); const checkedLayerIds = useRef([]); let gisMap = null; const closePopup = () => { setIsOpen(false); // 팝업 닫기 + setFeatureData(null) }; + const handleFeatureSelect = (data) => { + console.log('check',data) + setFeatureData(data); // 선택된 피처 데이터 저장 + setIsOpen(true); // 팝업 열기 + }; + useEffect(() => { if (!mapRef.current) return; @@ -101,7 +109,7 @@ function Gis({ treeData = [], isSearch = false }) { //setTreeData(data); const layerItems = getLayerItems(layerCode); - //setEditLayerItems(prev => [...layerItems]); + //setEditLayerItems(prev => [...layerItems]) }, [treeData]); const getLayerItems= (layerCode) => { @@ -135,8 +143,10 @@ function Gis({ treeData = [], isSearch = false }) {
- {isOpen && ( + + {isOpen && featureData && ( )} @@ -144,7 +154,6 @@ function Gis({ treeData = [], isSearch = false }) { -
diff --git a/packages/common/business_components/src/ui/map/GisContent.js b/packages/common/business_components/src/ui/map/GisContent.js index e58efcaad..686a3ae70 100644 --- a/packages/common/business_components/src/ui/map/GisContent.js +++ b/packages/common/business_components/src/ui/map/GisContent.js @@ -8,7 +8,7 @@ import './lib/css/gis.edit.css'; import MapToggleBtn from './MapToggleBtn'; import GisEdit from './GisEdit'; -export default function GisContent({ edit = false }) { +export default function GisContent({ edit = false,onFeatureSelect }) { const { gisMap, gisCore } = useGisContext(); const [visibleComponents, setVisibleComponents] = useState({}); const addressResultLayer = useRef(null); @@ -54,7 +54,7 @@ export default function GisContent({ edit = false }) { {/* */} {/* { */} {/* */} - + {/* {edit && } */} ); diff --git a/packages/common/business_components/src/ui/map/GisSelect.js b/packages/common/business_components/src/ui/map/GisSelect.js index ae4e15966..3b430fa02 100644 --- a/packages/common/business_components/src/ui/map/GisSelect.js +++ b/packages/common/business_components/src/ui/map/GisSelect.js @@ -3,7 +3,7 @@ import GisApp from './lib/gis.core'; import GisOlSelect from './lib/gis.select'; import { useContext } from 'react'; import { GisContext } from './Gis'; -export default React.memo(function GisSelect() { +export default React.memo(function GisSelect({ onFeatureSelect }) { const { gisMap } = useContext(GisContext); const select = new GisOlSelect(gisMap); GisApp.Module.select = select; @@ -13,6 +13,10 @@ export default React.memo(function GisSelect() { select.getSelectedInfo(function (features) { //clickFeature(features[0]); + if(features.length > 0) { + const layer = features[0]; + onFeatureSelect(layer.getProperties()); //피쳐 속성데이터 상위컴포넌트로 전달 + } }); return ( <>