From 11fd638b192255784bb127f4bfad143ad970c1a7 Mon Sep 17 00:00:00 2001 From: JIeunhuh Date: Tue, 29 Apr 2025 11:07:31 +0900 Subject: [PATCH] =?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 && ( + + )}