feat: 연도 범위 선택 기능 추가 및 SearchModal 데이터 처리 개선
This commit is contained in:
@@ -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,
|
||||
|
||||
+83
-159
@@ -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<SearchModalProps> = ({ 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<any[]>([]);
|
||||
const [tableCount, setTableCount] = useState<number>(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<any[]>([]);
|
||||
// 날짜 상태 선언
|
||||
const [시작1, set시작1] = useState('');
|
||||
const [시작2, set시작2] = useState('');
|
||||
@@ -179,10 +175,6 @@ const SearchModal: React.FC<SearchModalProps> = ({ 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<SearchModalProps> = ({ 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<SearchModalProps> = ({ open, onOpenChange, onFeature
|
||||
setShowTable(true);
|
||||
};
|
||||
|
||||
// 선택된 필터 값을 저장할 상태 추가
|
||||
// const [filterParams, setFilterParams] = useState<FilterParams>({});
|
||||
|
||||
// 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<HTMLDivElement>(null);
|
||||
const [dynamicHeight, setDynamicHeight] = useState<number | undefined>(undefined);
|
||||
|
||||
// 구분(basicType)이 바뀔 때마다 queryParams, filterParams 초기화
|
||||
React.useEffect(() => {
|
||||
// filterParams 초기화
|
||||
@@ -258,8 +237,6 @@ const SearchModal: React.FC<SearchModalProps> = ({ open, onOpenChange, onFeature
|
||||
set가동개시_종료_월('');
|
||||
set가동개시_시작_일('');
|
||||
set가동개시_종료_일('');
|
||||
set하수처리구역명('');
|
||||
set하수처리시설명('');
|
||||
set시도(0);
|
||||
set시군구(0);
|
||||
set처리구역('');
|
||||
@@ -269,57 +246,20 @@ const SearchModal: React.FC<SearchModalProps> = ({ 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<SearchModalProps> = ({ 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<SearchModalProps> = ({ 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<SearchModalProps> = ({ open, onOpenChange, onFeature
|
||||
{/* 분기처리: 하수관로 */}
|
||||
{basicType === 'tb_ms_swage_ppl' && (
|
||||
<div className="mb-6">
|
||||
{/* 설치년도 */}
|
||||
<div className="flex items-center mb-2">
|
||||
{/* 설치년도 */}
|
||||
<YearRangeSelectBox
|
||||
start={yearFilter.시작}
|
||||
end={yearFilter.종료}
|
||||
@@ -521,7 +464,7 @@ const SearchModal: React.FC<SearchModalProps> = ({ open, onOpenChange, onFeature
|
||||
<div className='flex items-center'>
|
||||
<Label className="w-auto text-xl text-gray-600 mr-2">경과년도</Label>
|
||||
<Input
|
||||
className='!text-xl w-[100px] placeholder:text-center'
|
||||
className='!text-xl w-[100px] placeholder:text-center mr-8'
|
||||
placeholder="입력"
|
||||
value={yearFilter.경과년도}
|
||||
onChange={(e) => {
|
||||
@@ -531,6 +474,42 @@ const SearchModal: React.FC<SearchModalProps> = ({ open, onOpenChange, onFeature
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/* 용도코드 */}
|
||||
<Label className="w-auto text-xl text-gray-600 mr-2">용도코드</Label>
|
||||
<Input
|
||||
className='!text-xl w-[100px] placeholder:text-center mr-8'
|
||||
placeholder="입력"
|
||||
value={변수1}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, '');
|
||||
set변수1(val);
|
||||
}}
|
||||
/>
|
||||
{/* 재질코드 */}
|
||||
<Label className="w-auto text-xl text-gray-600 mr-2">재질코드</Label>
|
||||
<Input
|
||||
className='!text-xl w-[100px] placeholder:text-center mr-8'
|
||||
placeholder="입력"
|
||||
value={변수2}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, '');
|
||||
set변수2(val);
|
||||
}}
|
||||
/>
|
||||
{/* 행정읍면동코드 */}
|
||||
<Label className="w-auto text-xl text-gray-600 mr-2">행정읍면동코드</Label>
|
||||
<Input
|
||||
className='!text-xl w-[100px] placeholder:text-center mr-8'
|
||||
placeholder="입력"
|
||||
value={변수3}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, '');
|
||||
set변수3(val);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center mb-2">
|
||||
{/* 관경 */}
|
||||
@@ -580,44 +559,6 @@ const SearchModal: React.FC<SearchModalProps> = ({ open, onOpenChange, onFeature
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center mb-2">
|
||||
{/* 용도코드 */}
|
||||
<Label className="w-auto text-xl text-gray-600 mr-2">용도코드</Label>
|
||||
<Input
|
||||
className='!text-xl w-[100px] placeholder:text-center mr-8'
|
||||
placeholder="입력"
|
||||
value={변수1}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, '');
|
||||
set변수1(val);
|
||||
}}
|
||||
/>
|
||||
{/* 재질코드 */}
|
||||
<Label className="w-auto text-xl text-gray-600 mr-2">재질코드</Label>
|
||||
<Input
|
||||
className='!text-xl w-[100px] placeholder:text-center mr-8'
|
||||
placeholder="입력"
|
||||
value={변수2}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, '');
|
||||
set변수2(val);
|
||||
}}
|
||||
/>
|
||||
{/* 행정읍면동코드 */}
|
||||
<Label className="w-auto text-xl text-gray-600 mr-2">행정읍면동코드</Label>
|
||||
<Input
|
||||
className='!text-xl w-[100px] placeholder:text-center mr-8'
|
||||
placeholder="입력"
|
||||
value={변수3}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, '');
|
||||
set변수3(val);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{/* 분기처리: 하수처리시설 */}
|
||||
@@ -704,29 +645,25 @@ const SearchModal: React.FC<SearchModalProps> = ({ open, onOpenChange, onFeature
|
||||
<div className="mb-3">
|
||||
<div className="flex items-center mb-2">
|
||||
{/* 설치년도 */}
|
||||
<div className="flex items-center mb-2">
|
||||
<YearRangeSelectBox
|
||||
start={yearFilter.시작}
|
||||
end={yearFilter.종료}
|
||||
onChange={range => setYearFilter({ ...yearFilter, 시작: range.start, 종료: range.end })}
|
||||
startLabel="설치년도"
|
||||
endLabel=""
|
||||
/>
|
||||
{/* 경과년도 */}
|
||||
<Label className="w-auto text-xl text-gray-600 mr-2">경과년도</Label>
|
||||
<Input
|
||||
className='!text-xl w-[100px] placeholder:text-center mr-8'
|
||||
placeholder="입력"
|
||||
value={yearFilter.경과년도}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, '');
|
||||
setYearFilter({ ...yearFilter, 경과년도: val });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center mb-2">
|
||||
<YearRangeSelectBox
|
||||
start={yearFilter.시작}
|
||||
end={yearFilter.종료}
|
||||
onChange={range => setYearFilter({ ...yearFilter, 시작: range.start, 종료: range.end })}
|
||||
startLabel="설치년도"
|
||||
endLabel=""
|
||||
/>
|
||||
{/* 경과년도 */}
|
||||
<Label className="w-auto text-xl text-gray-600 mr-2">경과년도</Label>
|
||||
<Input
|
||||
className='!text-xl w-[100px] placeholder:text-center mr-8'
|
||||
placeholder="입력"
|
||||
value={yearFilter.경과년도}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, '');
|
||||
setYearFilter({ ...yearFilter, 경과년도: val });
|
||||
}}
|
||||
/>
|
||||
{/* 용도코드 */}
|
||||
<Label className="w-auto text-xl text-gray-600 mr-2">용도코드</Label>
|
||||
<Input
|
||||
@@ -791,8 +728,6 @@ const SearchModal: React.FC<SearchModalProps> = ({ open, onOpenChange, onFeature
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center mb-2">
|
||||
{/* 일일처리용량 - 오수맨홀 */}
|
||||
<Label className="w-auto text-xl text-gray-600 mr-2">일일처리용량</Label>
|
||||
<Input
|
||||
@@ -868,8 +803,6 @@ const SearchModal: React.FC<SearchModalProps> = ({ open, onOpenChange, onFeature
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center mb-2">
|
||||
{/* 하천명 */}
|
||||
<Label className="w-auto text-xl text-gray-600 mr-2">하천명</Label>
|
||||
<Input
|
||||
@@ -1033,7 +966,7 @@ const SearchModal: React.FC<SearchModalProps> = ({ 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<SearchModalProps> = ({ open, onOpenChange, onFeature
|
||||
return (
|
||||
<div className="text-lg font-semibold mb-6">
|
||||
▶ {label} 조회 결과
|
||||
<span className="ml-2 text-base text-gray-500">({resultCount.toLocaleString()}건)</span>
|
||||
<span className="ml-2 text-base text-gray-500">({tableCount}건)</span>
|
||||
</div>
|
||||
);
|
||||
})()
|
||||
@@ -1052,22 +985,13 @@ const SearchModal: React.FC<SearchModalProps> = ({ 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 && (
|
||||
<FeatureClickEventPopup
|
||||
featureData={popupFeature}
|
||||
onClose={() => setPopupFeature(null)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
</div >
|
||||
</div>
|
||||
</>
|
||||
|
||||
@@ -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 = '하수처리구역';
|
||||
|
||||
@@ -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
|
||||
)}
|
||||
</TableHeader>
|
||||
<TableBody className='text-center'>
|
||||
{pageData.map((el: { [key: string]: string | number | boolean | object }, index) => (
|
||||
{tableData.map((el: { [key: string]: string | number | boolean | object }, index) => (
|
||||
<TableRow key={index + "row"}>
|
||||
{headlist?.map((head) => (
|
||||
<TableCell
|
||||
|
||||
Reference in New Issue
Block a user