From 037a04f1c92e785ad399061f17d7d379ab63f7ad Mon Sep 17 00:00:00 2001 From: swcho Date: Wed, 14 May 2025 13:18:04 +0900 Subject: [PATCH 1/4] =?UTF-8?q?fix=20:=20=ED=85=8C=EB=91=90=EB=A6=AC=20?= =?UTF-8?q?=EA=B3=A1=EB=A5=A0=20=EB=86=92=EC=9E=84,=20BASIC=5FTYPES=20key?= =?UTF-8?q?=20->=20val,=20label=20->=20text=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/sn/app/map/SearchModal.tsx | 53 ++++++++++++++------------------- 1 file changed, 22 insertions(+), 31 deletions(-) diff --git a/apps/sn/app/map/SearchModal.tsx b/apps/sn/app/map/SearchModal.tsx index 0368f84ac..b770b9dc2 100644 --- a/apps/sn/app/map/SearchModal.tsx +++ b/apps/sn/app/map/SearchModal.tsx @@ -1,11 +1,11 @@ import FeatureClickEventPopup from './featureClickEventPopup'; import React, { useState } from 'react'; import axios from 'axios'; -import { UiModal } from '@common/business_components/ui/dialog_ui'; -import { Button } from '@common/components'; +import { SelectBox, UiModal, UiTable } from '@common/business_components/ui'; +import { Button, Input } from '@common/components'; import { UiTable2 } from '@common/business_components/ui/table/index-2'; import { UITab2 } from '@common/business_components/ui/tab/index-2'; -import { SelectBox } from '@common/business_components/ui'; + @@ -19,26 +19,14 @@ const TABS = [ ]; // 기본정보 '구분' 선택 목록 const BASIC_TYPES = [ - { key: 'tb_ms_swage_ppl', label: '하수관로' }, - { key: 'tb_il_swage_prcs_fclt', label: '하수처리시설' }, - { key: 'tb_ms_swage_prcs_zone', label: '하수처리구역' }, - { key: 'tb_ms_swage_mh', label: '하수맨홀' }, - { key: 'tb_ms_swage_psmtn_1', label: '오수펌프' }, - { key: 'tb_ms_swage_psmtn_2', label: '빗물펌프' }, - { key: 'tb_ms_otfl', label: '우수토실' }, + { val: 'tb_ms_swage_ppl', text: '하수관로' }, + { val: 'tb_il_swage_prcs_fclt', text: '하수처리시설' }, + { val: 'tb_ms_swage_prcs_zone', text: '하수처리구역' }, + { val: 'tb_ms_swage_mh', text: '하수맨홀' }, + { val: 'tb_ms_swage_psmtn_1', text: '오수펌프' }, + { val: 'tb_ms_swage_psmtn_2', text: '빗물펌프' }, + { val: 'tb_ms_otfl', text: '우수토실' }, ]; -// const BASIC_TYPES = Array.from( -// [ -// { key: 'tb_ms_swage_ppl', label: '하수관로' }, -// { key: 'tb_il_swage_prcs_fclt', label: '하수처리시설' }, -// { key: 'tb_ms_swage_prcs_zone', label: '하수처리구역' }, -// { key: 'tb_ms_swage_mh', label: '하수맨홀' }, -// { key: 'tb_ms_swage_psmtn_1', label: '오수펌프' }, -// { key: 'tb_ms_swage_psmtn_2', label: '빗물펌프' }, -// { key: 'tb_ms_otfl', label: '우수토실' }, -// ], -// (item) => ({ text: item.label, val: item.key }) -// ); // 운영정보 '구분' 선택 목록 const WORK_TYPES = [ { key: 'maintenance', label: '보수 및 갱신' }, @@ -280,8 +268,8 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature const [activeTab, setActiveTab] = useState(TABS[0]?.key ?? 'basic'); const [workType, setWorkType] = useState(WORK_TYPES[0]?.key ?? ''); const [showTable, setShowTable] = useState(false); - const [curPage, setCurPage] = useState(1); - const [basicType, setBasicType] = useState(BASIC_TYPES[0]?.key ?? ''); + const [curPage, setCurPage] = useState(0); + const [basicType, setBasicType] = useState(BASIC_TYPES[0]?.val ?? ''); const [하수처리구역명, set하수처리구역명] = useState(''); const [하수처리시설명, set하수처리시설명] = useState(''); const [시도, set시도] = useState(0); @@ -529,6 +517,8 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature const queryParams = new URLSearchParams({ lyrTypeNm: basicType }); + // page와 size 사용해서 api 호출하는 걸로 바꾸기 + const size = 10; let cqlFilters: string[] = []; if (activeTab === 'basic') { @@ -569,7 +559,7 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature {/* 각각의 탭 영역 */} {/* 기본정보 탭 */} {activeTab === 'basic' && ( -
+
{/* 구분 */}
@@ -580,6 +570,7 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature label="구분" selectArray={BASIC_TYPES} onSelectValue={(value: string | number) => setBasicType(String(value))} + value={basicType} /> */}
@@ -1110,7 +1101,7 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature )} {/* 운영정보 탭 */} {activeTab === 'operation' && ( -
+
setBasicType(e.target.value)} - > - {BASIC_TYPES.map(opt => ( - - ))} - + />
{/* 분기처리: 하수관로 */} @@ -588,95 +509,71 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature
{/* 설치년도 */}
- - {/* 시작년도 */} - - ~ - {/* 종료년도 */} - - {/* 경과년도 */} - - { - // 숫자만 입력 허용 - const val = e.target.value.replace(/[^0-9]/g, ''); - setYearFilter({ ...yearFilter, 경과년도: val }); - }} + setYearFilter({ ...yearFilter, 시작: range.start, 종료: range.end })} + startLabel="설치년도" + endLabel="" /> + + {/* 경과년도 */} +
+ + { + // 숫자만 입력 허용 + const val = e.target.value.replace(/[^0-9]/g, ''); + setYearFilter({ ...yearFilter, 경과년도: val }); + }} + /> +
{/* 관경 */} - - 관경(mm) + { + onChange={(e) => { // 숫자만 입력 허용 const val = e.target.value.replace(/[^0-9]/g, ''); set시작1(val); }} /> ~ - { + onChange={(e) => { // 숫자만 입력 허용 const val = e.target.value.replace(/[^0-9]/g, ''); set종료1(val); }} /> {/* 연장 */} - - 연장(m) + { + onChange={(e) => { // 숫자만 입력 허용 const val = e.target.value.replace(/[^0-9]/g, ''); set시작2(val); }} /> ~ - { + onChange={(e) => { // 숫자만 입력 허용 const val = e.target.value.replace(/[^0-9]/g, ''); set종료2(val); @@ -685,42 +582,39 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature
{/* 용도코드 */} - - 용도코드 + { + value={변수1} + onChange={(e) => { // 숫자만 입력 허용 const val = e.target.value.replace(/[^0-9]/g, ''); - set하수관로용도코드(val); + set변수1(val); }} /> {/* 재질코드 */} - - 재질코드 + { + value={변수2} + onChange={(e) => { // 숫자만 입력 허용 const val = e.target.value.replace(/[^0-9]/g, ''); - set하수관로재질코드(val); + set변수2(val); }} /> {/* 행정읍면동코드 */} - - 행정읍면동코드 + { + value={변수3} + onChange={(e) => { // 숫자만 입력 허용 const val = e.target.value.replace(/[^0-9]/g, ''); - set행정읍면동코드(val); + set변수3(val); }} />
@@ -731,63 +625,46 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature
{/* 가동개시일자 */}
- - {/* 시작일자 */} - - ~ - {/* 종료일자 */} - + setYearFilter({ ...yearFilter, 시작: range.start, 종료: range.end })} + startLabel="가동개시일자" + endLabel="" + /> {/* 시설용량 */} - - 시설용량 + { + onChange={(e) => { // 숫자만 입력 허용 const val = e.target.value.replace(/[^0-9]/g, ''); set시작1(val); }} /> - ~ - ~ + { + onChange={(e) => { // 숫자만 입력 허용 const val = e.target.value.replace(/[^0-9]/g, ''); set종료1(val); }} /> {/* 하수처리시설명 */} - - set변수1(e.target.value)} /> + + { + set변수1(e.target.value); + }} + />
)} @@ -795,22 +672,30 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature {basicType === 'tb_ms_swage_prcs_zone' && (
- - - - - - set변수1(e.target.value)} /> - - set변수2(e.target.value)} /> + + + { + set변수1(e.target.value); + }} + /> + + { + set변수2(e.target.value); + }} + />
)} @@ -820,43 +705,20 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature
{/* 설치년도 */}
- - {/* 시작년도 */} - - ~ - {/* 종료년도 */} - + setYearFilter({ ...yearFilter, 시작: range.start, 종료: range.end })} + startLabel="설치년도" + endLabel="" + /> {/* 경과년도 */} - - 경과년도 + { + onChange={(e) => { // 숫자만 입력 허용 const val = e.target.value.replace(/[^0-9]/g, ''); setYearFilter({ ...yearFilter, 경과년도: val }); @@ -864,40 +726,37 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature />
-
+
{/* 용도코드 */} - - 용도코드 + { + onChange={(e) => { // 숫자만 입력 허용 const val = e.target.value.replace(/[^0-9]/g, ''); set변수1(val); }} /> {/* 하수맨홀 구경 */} - - 구경(mm) + { + onChange={(e) => { // 숫자만 입력 허용 const val = e.target.value.replace(/[^0-9]/g, ''); set시작1(val); }} /> ~ - { + onChange={(e) => { // 숫자만 입력 허용 const val = e.target.value.replace(/[^0-9]/g, ''); set종료1(val); @@ -912,43 +771,20 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature
{/* 설치년도 */}
- - {/* 시작년도 */} - - ~ - {/* 종료년도 */} - + setYearFilter({ ...yearFilter, 시작: range.start, 종료: range.end })} + startLabel="설치년도" + endLabel="" + /> {/* 경과년도 */} - - 경과년도 + { + onChange={(e) => { // 숫자만 입력 허용 const val = e.target.value.replace(/[^0-9]/g, ''); setYearFilter({ ...yearFilter, 경과년도: val }); @@ -956,52 +792,48 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature />
-
+
{/* 일일처리용량 - 오수맨홀 */} - - 일일처리용량 + { + onChange={(e) => { // 숫자만 입력 허용 const val = e.target.value.replace(/[^0-9]/g, ''); set시작1(val); }} /> ~ - { + onChange={(e) => { // 숫자만 입력 허용 const val = e.target.value.replace(/[^0-9]/g, ''); set종료1(val); }} /> {/* 우수양수능력 - 우수맨홀 */} - - 우수양수능력 + { + onChange={(e) => { // 숫자만 입력 허용 const val = e.target.value.replace(/[^0-9]/g, ''); set시작2(val); }} /> ~ - { + onChange={(e) => { // 숫자만 입력 허용 const val = e.target.value.replace(/[^0-9]/g, ''); set종료2(val); @@ -1016,43 +848,20 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature
{/* 설치년도 */}
- - {/* 시작년도 */} - - ~ - {/* 종료년도 */} - + setYearFilter({ ...yearFilter, 시작: range.start, 종료: range.end })} + startLabel="설치년도" + endLabel="" + /> {/* 경과년도 */} - - 경과년도 + { + onChange={(e) => { // 숫자만 입력 허용 const val = e.target.value.replace(/[^0-9]/g, ''); setYearFilter({ ...yearFilter, 경과년도: val }); @@ -1060,30 +869,35 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature />
-
+
{/* 하천명 */} - - set변수1(e.target.value)} /> + + { + set변수1(e.target.value); + }} + /> {/* 계획방류량 */} - - 계획방류량 + { + onChange={(e) => { // 숫자만 입력 허용 const val = e.target.value.replace(/[^0-9]/g, ''); set시작1(val); }} /> ~ - { + onChange={(e) => { // 숫자만 입력 허용 const val = e.target.value.replace(/[^0-9]/g, ''); set종료1(val); @@ -1103,57 +917,31 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature {activeTab === 'operation' && (
- - + /> {/* 설치년도 */}
- - {/* 시작년도 */} - - ~ - {/* 종료년도 */} - + setYearFilter({ ...yearFilter, 시작: range.start, 종료: range.end })} + startLabel="설치년도" + endLabel="" + /> {/* 경과년도 */} - - 경과년도 + { + onChange={(e) => { // 숫자만 입력 허용 const val = e.target.value.replace(/[^0-9]/g, ''); setYearFilter({ ...yearFilter, 경과년도: val }); @@ -1172,54 +960,37 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature
{/* 시도, 시군구, 하수처리구역 한 줄 */}
- - - - - - - - - {/* 시작일자 */} - - ~ - {/* 종료일자 */} - + + ({ val: opt.val, text: opt.text }))} + onSelectValue={val => set처리구역(String(val))} + value={처리구역} + /> + { + // 숫자만 입력 허용 + const val = e.target.value.replace(/[^0-9]/g, ''); + set변수1(val); + }} + /> + setYearFilter({ ...yearFilter, 시작: range.start, 종료: range.end })} + startLabel="승인년도" + endLabel="" + />
{/* 조회 버튼 중앙 정렬 */}
@@ -1253,7 +1024,7 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature > {/* 선택된 탭의 label */} < div className="m-2 text-2xl font-bold mb-2" > - {TABS.find(tab => tab.key === activeTab)?.label} + {TABS.find(tab => tab.val === activeTab)?.text} {/* 조회 결과 라벨 및 건수 표시 */} { (() => { @@ -1264,10 +1035,10 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature activeTab === 'basic' ? BASIC_TYPES.find(type => type.val === basicType)?.val : activeTab === 'operation' - ? WORK_TYPES.find(type => type.key === workType)?.label + ? WORK_TYPES.find(type => type.val === workType)?.text : activeTab === 'report' ? '하수도정비기본계획보고서' - : TABS.find(tab => tab.key === activeTab)?.label; + : TABS.find(tab => tab.val === activeTab)?.text; return (
▶ {label} 조회 결과 @@ -1311,7 +1082,7 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature w-[60vw] max-w-none max-h-none - overflow-hidden + overflow-visible z-[101] `} body={renderBody()} diff --git a/packages/common/business_components/src/ui/select/index-2.tsx b/packages/common/business_components/src/ui/select/index-2.tsx new file mode 100644 index 000000000..8ff1f4582 --- /dev/null +++ b/packages/common/business_components/src/ui/select/index-2.tsx @@ -0,0 +1,45 @@ +/** + * @remarks + * 이 버전은 SearchModal에서 사용하기 위해 `SelectContent`의 z-index를 `z-[101]`로 수정하였습니다. + */ +import { Label } from "@common/components"; +import React from "react"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./lib/selectBox"; + +/** 차트 기본 스타일 */ +interface SelectBox1 { + text:string, + val:string|number +} + +const SelectBox2 = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & { + label:string, + selectClass:string|undefined, + className:string, + selectArray:SelectBox1[], + onSelectValue:(val:string|number)=>void, + value?: string|number + } +>(({ label, className, selectClass, selectArray, onSelectValue, value }, ref)=>{ + const placeHolder = selectArray?.[0]?.text ?? ""; + + return ( +
+ {label && } + +
+ ) +}) + +export { SelectBox2 }; From d11343726f6a1860f8218cce50b6de38d26552d5 Mon Sep 17 00:00:00 2001 From: swcho Date: Fri, 16 May 2025 16:46:06 +0900 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=EC=97=B0=EB=8F=84=20=EB=B2=94?= =?UTF-8?q?=EC=9C=84=20=EC=84=A0=ED=83=9D=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EB=B0=8F=20SearchModal=20=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20=EC=B2=98=EB=A6=AC=20=EA=B0=9C=EC=84=A0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../app/components/ui/YearRangeSelectBox.tsx | 42 +++ apps/sn/app/map/SearchModal.tsx | 242 ++++++------------ apps/sn/app/map/featureClickEventPopup.tsx | 2 +- .../src/ui/table/index-2.tsx | 3 +- 4 files changed, 127 insertions(+), 162 deletions(-) diff --git a/apps/sn/app/components/ui/YearRangeSelectBox.tsx b/apps/sn/app/components/ui/YearRangeSelectBox.tsx index 5e9aa0b3e..312c03e58 100644 --- a/apps/sn/app/components/ui/YearRangeSelectBox.tsx +++ b/apps/sn/app/components/ui/YearRangeSelectBox.tsx @@ -13,6 +13,48 @@ interface YearRangeSelectBoxProps { selectClass?: string; } +// 시작년도 selectArray 생성 함수 +export function getStartYearArray({ + minYear = 1900, + maxYear = new Date().getFullYear(), + placeholder = "년", + endYear, +}: { + minYear?: number; + maxYear?: number; + placeholder?: string; + endYear?: string; +} = {}) { + const years = Array.from({ length: maxYear - minYear + 1 }, (_, i) => + (maxYear - i).toString() + ).filter(y => !endYear || parseInt(y) <= parseInt(endYear)); + return [ + { val: "0", text: placeholder }, + ...years.map(y => ({ val: y, text: y })), + ]; +} + +// 종료년도 selectArray 생성 함수 +export function getEndYearArray({ + minYear = 1900, + maxYear = new Date().getFullYear(), + placeholder = "년", + startYear, +}: { + minYear?: number; + maxYear?: number; + placeholder?: string; + startYear?: string; +} = {}) { + const years = Array.from({ length: maxYear - minYear + 1 }, (_, i) => + (maxYear - i).toString() + ).filter(y => !startYear || parseInt(y) >= parseInt(startYear)); + return [ + { val: "9999", text: placeholder }, + ...years.map(y => ({ val: y, text: y })), + ]; +} + export function YearRangeSelectBox({ start, end, diff --git a/apps/sn/app/map/SearchModal.tsx b/apps/sn/app/map/SearchModal.tsx index 6dc74eccc..b817e5d2e 100644 --- a/apps/sn/app/map/SearchModal.tsx +++ b/apps/sn/app/map/SearchModal.tsx @@ -38,7 +38,7 @@ const WORK_TYPES = [ const YEARS = Array.from({ length: 100 }, (_, i) => `${2025 - i}`); // 테이블 헤더 리스트 -// 하수관로 (추후 작성 예정) +// 하수관로 const swagePplTableHeadlist = [ { id: 'pipeNumber', title: '관로번호' }, { id: 'gisManageNumber', title: '관리번호(GIS)' }, @@ -66,11 +66,8 @@ const swagePplTableHeadlist = [ ] // 하수처리구역 const swageZoneTableHeadlist = [ - // { id: 'ctpvCd', title: '시도코드' }, { id: 'ctpvNm', title: '시도명' }, - // { id: 'sggCd', title: '시군구코드' }, { id: 'sggNm', title: '시군구명' }, - // { id: 'swtrmtZoneSn', title: '하수처리구역번호' }, { id: 'swtrmtZoneNm', title: '하수처리구역명' }, { id: 'swtrmtFcltCd', title: '하수처리시설번호' }, { id: 'swtrmtFcltNm', title: '하수처리시설명' }, @@ -162,14 +159,13 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature const [activeTab, setActiveTab] = useState(TABS[0]?.val ?? 'basic'); const [workType, setWorkType] = useState(WORK_TYPES[0]?.val ?? ''); const [showTable, setShowTable] = useState(false); - const [curPage, setCurPage] = useState(0); + const [tableData, setTableData] = useState([]); + const [tableCount, setTableCount] = useState(0); + const [curPage, setCurPage] = useState(1); const [basicType, setBasicType] = useState(BASIC_TYPES[0]?.val ?? ''); - const [하수처리구역명, set하수처리구역명] = useState(''); - const [하수처리시설명, set하수처리시설명] = useState(''); const [시도, set시도] = useState(0); const [시군구, set시군구] = useState(0); const [처리구역, set처리구역] = useState(''); - const [tableData, setTableData] = useState([]); // 날짜 상태 선언 const [시작1, set시작1] = useState(''); const [시작2, set시작2] = useState(''); @@ -179,10 +175,6 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature const [변수2, set변수2] = useState(''); const [변수3, set변수3] = useState(''); const [변수4, set변수4] = useState(''); - // 하수관로 변수 - const [하수관로용도코드, set하수관로용도코드] = useState(''); - const [하수관로재질코드, set하수관로재질코드] = useState(''); - const [행정읍면동코드, set행정읍면동코드] = useState(''); // 시작, 종료, 경과년도 const [yearFilter, setYearFilter] = useState({ 시작: '', @@ -206,7 +198,7 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature const todayDate = today.getDate().toString().padStart(2, '0'); // UiTable2에 넘길 headlist를 BASIC_TYPES 값에 따라 분기 처리 - let dynamicHeadlist = swageZoneTableHeadlist; + let dynamicHeadlist = swageZoneTableHeadlist; // default 값 if (activeTab === 'operation') dynamicHeadlist = operationTableHeadlist; // 운영정보 else if (activeTab === 'report') dynamicHeadlist = reportTableHeadlist; // 하수도정비기본계획보고서 else if (basicType === 'tb_ms_swage_ppl') dynamicHeadlist = swagePplTableHeadlist; // 하수관로 @@ -221,27 +213,14 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature setShowTable(true); }; - // 선택된 필터 값을 저장할 상태 추가 - // const [filterParams, setFilterParams] = useState({}); - // cellClick 함수 타입 지정 const handleCellClick = (index?: number, headId?: string) => { - // 원하는 동작 구현 - console.log('Cell clicked:', index, headId); if (typeof index === "number") { - console.log('Selected row data:', tableData[index]); - onFeatureSelect(tableData[index]); + onFeatureSelect(tableData[index % 10]); // 10은 page size 하드 코딩한 숫자 onOpenChange(false); // SearchModal 닫기 } }; - const modalMinWidth = 'min-w-[900px]'; - const modalMaxWidth = 'max-w-6xl'; - - // 모달 높이 동적 계산 - const contentRef = React.useRef(null); - const [dynamicHeight, setDynamicHeight] = useState(undefined); - // 구분(basicType)이 바뀔 때마다 queryParams, filterParams 초기화 React.useEffect(() => { // filterParams 초기화 @@ -258,8 +237,6 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature set가동개시_종료_월(''); set가동개시_시작_일(''); set가동개시_종료_일(''); - set하수처리구역명(''); - set하수처리시설명(''); set시도(0); set시군구(0); set처리구역(''); @@ -269,57 +246,20 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature 경과년도: '' }); setShowTable(false); + setTableCount(0); setTableData([]); // queryParams는 fetchFilteredData 내에서 매번 새로 생성되므로 별도 상태 초기화는 필요 없음 }, [activeTab, basicType]); - + // 페이지 변경 시마다 데이터 fetch React.useEffect(() => { - if (contentRef.current) { - setDynamicHeight(contentRef.current.scrollHeight + 60); // 여유 padding + // showTable이 true일 때만 fetch (최초 렌더 방지) + if (showTable) { + fetchFilteredData(); // 실제 API 사용 시 + // fetchDummyData(); // 더미 데이터 사용 시 } - }, [activeTab, showTable, curPage, tableData.length]); + }, [curPage]); - // 시작년도 selectArray 생성 함수 - function getStartYearArray({ - minYear = 1900, - maxYear = new Date().getFullYear(), - placeholder = '년', - endYear, - }: { - minYear?: number; - maxYear?: number; - placeholder?: string; - endYear?: string; - } = {}) { - const years = Array.from({ length: maxYear - minYear + 1 }, (_, i) => - (maxYear - i).toString() - ).filter(y => !endYear || parseInt(y) <= parseInt(endYear)); - return [ - { val: '0', text: placeholder }, - ...years.map(y => ({ val: y, text: y })), - ]; - } - // 종료년도 selectArray 생성 함수 - function getEndYearArray({ - minYear = 1900, - maxYear = new Date().getFullYear(), - placeholder = '년', - startYear, - }: { - minYear?: number; - maxYear?: number; - placeholder?: string; - startYear?: string; - } = {}) { - const years = Array.from({ length: maxYear - minYear + 1 }, (_, i) => - (maxYear - i).toString() - ).filter(y => !startYear || parseInt(y) >= parseInt(startYear)); - return [ - { val: '9999', text: placeholder }, - ...years.map(y => ({ val: y, text: y })), - ]; - } // 하수관로용 CQL 필터 생성 const pplCqlFilters = () => { @@ -446,10 +386,12 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature // 검색 API 호출 함수 const fetchFilteredData = () => { const queryParams = new URLSearchParams({ - lyrTypeNm: basicType + lyrTypeNm: basicType, + page: `${curPage - 1}`, + size: '10', + sort: 'string', }); // page와 size 사용해서 api 호출하는 걸로 바꾸기 - const size = 10; let cqlFilters: string[] = []; if (activeTab === 'basic') { @@ -465,12 +407,13 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature if (cqlFilters.length > 0) queryParams.append('cqlFiter', cqlFilters.join(' and ')); - console.log('API URL:', `${API_URL}/v1/map/features?${queryParams.toString()}`); - axios.get(`${API_URL}/v1/map/features?${queryParams.toString()}`) + console.log('API URL:', `${API_URL}/v1/fclts?${queryParams.toString()}`); + axios.get(`${API_URL}/v1/fclts?${queryParams.toString()}`) .then(res => { - console.log('필터링된 API 결과:', res.data.length); - console.log('필터링된 API 결과:', res.data); - setTableData(res.data || []); + console.log('필터링된 API 결과(개수):', res.data.count); + console.log('필터링된 API 결과(데이터):', res.data.data); + setTableData(res.data.data || []); + setTableCount(res.data.count || []); setShowTable(true); }) .catch(err => { @@ -507,8 +450,8 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature {/* 분기처리: 하수관로 */} {basicType === 'tb_ms_swage_ppl' && (
- {/* 설치년도 */}
+ {/* 설치년도 */} = ({ open, onOpenChange, onFeature
{ @@ -531,6 +474,42 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature }} />
+ {/* 용도코드 */} + + { + // 숫자만 입력 허용 + const val = e.target.value.replace(/[^0-9]/g, ''); + set변수1(val); + }} + /> + {/* 재질코드 */} + + { + // 숫자만 입력 허용 + const val = e.target.value.replace(/[^0-9]/g, ''); + set변수2(val); + }} + /> + {/* 행정읍면동코드 */} + + { + // 숫자만 입력 허용 + const val = e.target.value.replace(/[^0-9]/g, ''); + set변수3(val); + }} + />
{/* 관경 */} @@ -580,44 +559,6 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature }} />
-
- {/* 용도코드 */} - - { - // 숫자만 입력 허용 - const val = e.target.value.replace(/[^0-9]/g, ''); - set변수1(val); - }} - /> - {/* 재질코드 */} - - { - // 숫자만 입력 허용 - const val = e.target.value.replace(/[^0-9]/g, ''); - set변수2(val); - }} - /> - {/* 행정읍면동코드 */} - - { - // 숫자만 입력 허용 - const val = e.target.value.replace(/[^0-9]/g, ''); - set변수3(val); - }} - /> -
)} {/* 분기처리: 하수처리시설 */} @@ -704,29 +645,25 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature
{/* 설치년도 */} -
- setYearFilter({ ...yearFilter, 시작: range.start, 종료: range.end })} - startLabel="설치년도" - endLabel="" - /> - {/* 경과년도 */} - - { - // 숫자만 입력 허용 - const val = e.target.value.replace(/[^0-9]/g, ''); - setYearFilter({ ...yearFilter, 경과년도: val }); - }} - /> -
-
-
+ setYearFilter({ ...yearFilter, 시작: range.start, 종료: range.end })} + startLabel="설치년도" + endLabel="" + /> + {/* 경과년도 */} + + { + // 숫자만 입력 허용 + const val = e.target.value.replace(/[^0-9]/g, ''); + setYearFilter({ ...yearFilter, 경과년도: val }); + }} + /> {/* 용도코드 */} = ({ open, onOpenChange, onFeature }} />
-
-
{/* 일일처리용량 - 오수맨홀 */} = ({ open, onOpenChange, onFeature }} />
-
-
{/* 하천명 */} = ({ open, onOpenChange, onFeature // 탭별 라벨 const label = activeTab === 'basic' - ? BASIC_TYPES.find(type => type.val === basicType)?.val + ? BASIC_TYPES.find(type => type.val === basicType)?.text : activeTab === 'operation' ? WORK_TYPES.find(type => type.val === workType)?.text : activeTab === 'report' @@ -1042,7 +975,7 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature return (
▶ {label} 조회 결과 - ({resultCount.toLocaleString()}건) + ({tableCount}건)
); })() @@ -1052,22 +985,13 @@ const SearchModal: React.FC = ({ open, onOpenChange, onFeature tableData={tableData} headlist={dynamicHeadlist} pageSize={tableData.length < 10 ? tableData.length : 10} - total={tableData.length} + total={tableCount} children={undefined} cellClick={handleCellClick} onPageChange={setCurPage} resetPageNo={false} cur={curPage} /> - {/* FeatureClickEventPopup 표시 */} - { - popupFeature && ( - setPopupFeature(null)} - /> - ) - }
diff --git a/apps/sn/app/map/featureClickEventPopup.tsx b/apps/sn/app/map/featureClickEventPopup.tsx index 223bebaa3..a7b55dc32 100644 --- a/apps/sn/app/map/featureClickEventPopup.tsx +++ b/apps/sn/app/map/featureClickEventPopup.tsx @@ -31,7 +31,7 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, featureLayer = layer; // 피처가 속한 레이어 찾기 } }); - // SearchMoal 클릭 이벤트와 Gis 클릭 이벤트 구분하기 + // SearchModal 클릭 이벤트와 Gis 클릭 이벤트 구분하기 if (featureLayer.get('lyrNm') === null) { if (featureData.lyrTypeNm === 'tb_il_swage_prcs_fclt') result = '하수처리시설'; else if (featureData.lyrTypeNm === 'tb_ms_swage_prcs_zone') result = '하수처리구역'; diff --git a/packages/common/business_components/src/ui/table/index-2.tsx b/packages/common/business_components/src/ui/table/index-2.tsx index f4f4163f8..58ae1bbd0 100644 --- a/packages/common/business_components/src/ui/table/index-2.tsx +++ b/packages/common/business_components/src/ui/table/index-2.tsx @@ -51,7 +51,6 @@ export function UiTable2({ tableData, headlist, pageSize, total, children, headN const startIdx = (curState - 1) * pageSize; const endIdx = startIdx + pageSize; - const pageData = tableData.slice(startIdx, endIdx); return ( <> @@ -81,7 +80,7 @@ export function UiTable2({ tableData, headlist, pageSize, total, children, headN )} - {pageData.map((el: { [key: string]: string | number | boolean | object }, index) => ( + {tableData.map((el: { [key: string]: string | number | boolean | object }, index) => ( {headlist?.map((head) => ( Date: Mon, 19 May 2025 14:29:17 +0900 Subject: [PATCH 4/4] =?UTF-8?q?modal=20=EB=B3=80=EA=B2=BD=20=EB=B0=8F=20?= =?UTF-8?q?=EC=83=81=EC=84=B8=ED=8C=9D=EC=97=85=20=EC=9E=85=EB=A0=A5?= =?UTF-8?q?=EC=A0=95=EB=B3=B4=20api=20=EC=9E=91=EC=97=85=20=EC=9A=B0?= =?UTF-8?q?=EC=84=A0=20=EC=99=84=EB=A3=8C(=EC=B6=94=ED=9B=84=20=EB=A0=88?= =?UTF-8?q?=EC=9D=B4=EC=96=B4=20=EC=B6=94=EA=B0=80=20=EC=8B=9C=20=ED=99=95?= =?UTF-8?q?=EC=9D=B8=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/ui/modal.tsx | 10 ++-- apps/sn/app/map/components/common/index.ts | 3 +- .../app/map/components/popupComponent/Ssc.tsx | 2 +- .../components/popupComponent/sewagePump.tsx | 4 +- apps/sn/app/map/featureClickEventPopup.tsx | 54 ++++++++++++------- 5 files changed, 48 insertions(+), 25 deletions(-) diff --git a/apps/sn/app/components/ui/modal.tsx b/apps/sn/app/components/ui/modal.tsx index e860ed7b9..f1dda48a6 100644 --- a/apps/sn/app/components/ui/modal.tsx +++ b/apps/sn/app/components/ui/modal.tsx @@ -7,20 +7,24 @@ interface ModalProps { title?: string; children: React.ReactNode; closeModal: () => void; + saveModal?: () => void } -const Modal: React.FC = ({ isModalOpen, setIsModalOpen, title, children, closeModal }) => { +const Modal: React.FC = ({ isModalOpen, setIsModalOpen, title, children, closeModal,saveModal }) => { return ( - - + + {title ? {title} : null} {children} +
+ {saveModal ? : null} +
diff --git a/apps/sn/app/map/components/common/index.ts b/apps/sn/app/map/components/common/index.ts index 5e2826e33..eecfd7b29 100644 --- a/apps/sn/app/map/components/common/index.ts +++ b/apps/sn/app/map/components/common/index.ts @@ -17,9 +17,10 @@ type LayerContentMap = { const layerContentMap: LayerContentMap = { // TODO: gis데이터 추가되면 추가 작업 필요 "하수처리시설": { - component: StormWaterPump, + component: SwtrmtFclt, }, "하수처리구역": { }, + "우수토실":{component: Ssc,}, "중점우수토실":{component: Ssc,}, "하수관로":{component: MsSwagePpl,}, "하수맨홀":{component: smh,}, diff --git a/apps/sn/app/map/components/popupComponent/Ssc.tsx b/apps/sn/app/map/components/popupComponent/Ssc.tsx index e085251f7..8b139de4e 100644 --- a/apps/sn/app/map/components/popupComponent/Ssc.tsx +++ b/apps/sn/app/map/components/popupComponent/Ssc.tsx @@ -5,7 +5,7 @@ import { UiTable } from '@common/business_components/ui'; import { Button, TableHead, TableRow } from '@common/components/ui'; import React from 'react' const inputColumn = [ - { id: "guyeok", title: "처리구역", type: "select", options: ["구역1", "구역2"] }, + { id: "swtrmt_zone_cd", title: "처리구역", type: "input" }, { id: "mainSscYn", title: "중점우수토실 대상여부", type: "select", options: ['Y', 'N'] }, ]; const headlist = [ diff --git a/apps/sn/app/map/components/popupComponent/sewagePump.tsx b/apps/sn/app/map/components/popupComponent/sewagePump.tsx index 9c62762d9..5776df5b2 100644 --- a/apps/sn/app/map/components/popupComponent/sewagePump.tsx +++ b/apps/sn/app/map/components/popupComponent/sewagePump.tsx @@ -1,8 +1,8 @@ import { VerticalTable } from '@/components/ui/verticalTable'; import React from 'react' const inputColumn = [ - { id: "guyeok", title: "처리구역", type: "select", options: ["구역1", "구역2"] }, - { id: "bungu", title: "배수분구", type: "input" }, + { id: "swtrmt_zone_cd", title: "처리구역", type: "select", options: ["구역1", "구역2"] }, + { id: "mltv_dtdv_nm", title: "배수분구", type: "input" }, { id: "freq", title: "설계빈도", type: "input" }, ] // 오수펌프 diff --git a/apps/sn/app/map/featureClickEventPopup.tsx b/apps/sn/app/map/featureClickEventPopup.tsx index 1932bcb14..4056f6130 100644 --- a/apps/sn/app/map/featureClickEventPopup.tsx +++ b/apps/sn/app/map/featureClickEventPopup.tsx @@ -6,6 +6,7 @@ import { SwtrmtFcltCol, SwtrmtPrcsZoneCol } from "./components/common/table"; import { useGisContext } from "@common/business_components/ui/map/Gis"; import { getData, putData } from "@/components/lib/api"; import { getLayerContentComponent } from "./components/common"; +import Modal from "@/components/ui/modal"; // TODO: 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; }; @@ -19,6 +20,7 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, const [buttonChk, setButtonChk] = useState(true) const [fetchData, setfetchData] = useState(); // api 호출여부 const contentRef = useRef(null); + const [isModalOpen, setIsModalOpen] = useState(false); // 모달 열기 상태 // 피쳐데이터의 현재 레이어 이름 가져오는 함수 const getSelectedLayers = () => { let result = null; @@ -46,8 +48,8 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, if (featureId) { const selectedLayerName = getSelectedLayers(); // 레이어 이름 가져오기 setLayerNm(selectedLayerName); // 상태 업데이트 - - const data = await getData(`v1/fclts/${featureId}`); + const layerId = featureId.split('.')[0] + const data = await getData(`v1/fclts/${featureId}?lyrTypeNm=${layerId}`); setfetchData(data) switch (selectedLayerName) { case '하수처리시설': @@ -73,6 +75,9 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, case '빗물펌프': setButtonChk(true) break; + case '하수맨홀': + setButtonChk(true) + break; default: break; } @@ -81,34 +86,46 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, } fetchData(); }, [featureId]); - // TODO: api 호출작업 필요(사용자 입력부분) + // 저장 시 모달 띄우기 + const handleSaveClick = () => { + setIsModalOpen(true); // 모달 열기 + }; + const handleModalSave = () => { + setIsModalOpen(false); // 모달 닫기 + btnClickEvent(); // 저장 이벤트 실행 + }; + const handleModalClose = () => { + setIsModalOpen(false); // 모달 닫기 + }; const btnClickEvent = async() => { const bodyData: Record = {} if (buttonChk && contentRef.current) { const inputElements = contentRef.current.querySelectorAll('input'); const selectElements = contentRef.current.querySelectorAll('select'); + // 카멜케이스 변환 함수 + const toCamelCase = (str: string) => + str.replace(/_([a-z])/g, (_, char) => char.toUpperCase()); // select 태그의 값 가져오기 selectElements.forEach((select) => { - bodyData[select.id] = select.value; + const camelCaseId = toCamelCase(select.id); + bodyData[camelCaseId] = select.value; }); // input 태그의 값 가져오기 inputElements.forEach((input) => { - bodyData[input.id] = input.value; + const camelCaseId = toCamelCase(input.id); + bodyData[camelCaseId] = input.value; }); - if (layerNm === '하수관로' || layerNm === '하수처리시설') { - bodyData['lyrTypeNm'] = 'tb_ms_swage_ppl' - } else if (layerNm === '우수토실') { - bodyData['lyrTypeNm'] = 'tb_ms_stofcbr' - } else if (layerNm === '오수펌프') { - bodyData['lyrTypeNm'] = 'tb_ms_swage_pmstn' - } - else if (layerNm === '빗물펌프') { - bodyData['lyrTypeNm'] = 'tb_ms_swage_pmstn' - } + if (layerNm === '하수관로') bodyData['lyrTypeNm'] = 'tb_ms_swage_ppl' + else if (layerNm === '우수토실') bodyData['lyrTypeNm'] = 'tb_ms_stofcbr' + else if (layerNm === '오수펌프') bodyData['lyrTypeNm'] = 'tb_ms_swage_pmstn' + else if (layerNm === '빗물펌프') bodyData['lyrTypeNm'] = 'tb_ms_swage_pmstn' + else if(layerNm === '하수맨홀')bodyData['lyrTypeNm'] = 'tb_ms_swage_mnhl' bodyData['fid'] = featureId const data = await putData(`v1/fclts/${featureId}`, bodyData); - if(data){ - console.log('저장완료', data); + if(data.status==200){ + alert('저장되었습니다') + setTableData(data) + setfetchData(data) } } } @@ -133,10 +150,11 @@ const FeatureClickEventPopup = ({ onClose, featureData }: { onClose: () => void, throw new Error("Function not implemented."); }}>
- {buttonChk ? : null}
+
정보를 저장하시겠습니까?
{ContentComponent && }