feat: 재이용시설현황 상단 검색바 시설명 자동완성 검색 구현 (조회 버튼, 초기화 버튼 미구현)

This commit is contained in:
swcho
2025-07-01 10:26:40 +09:00
parent c6b8e53c17
commit 5579ab677d
5 changed files with 88 additions and 111 deletions
+1
View File
@@ -106,6 +106,7 @@ const Map = () => {
setIsSearchModalOpen={setIsSearchModalOpen}
isOpen={isOpen}
isSearchModalOpen={isSearchModalOpen}
onCheckedLayersChange={undefined}
/>
</div>
);
+21 -10
View File
@@ -108,15 +108,15 @@ const Map = () => {
};
// MapSearchBar 래퍼 컴포넌트
const CustomMapSearchBar = (props: any) => {
return (
<MapSearchBar
{...props}
searchType="공급처 후보지 조회"
showApplyButton={false}
/>
);
};
// const CustomMapSearchBar = (props: any) => {
// return (
// <MapSearchBar
// {...props}
// searchType="공급처 후보지 조회"
// showApplyButton={false}
// />
// );
// };
return (
<div className="bg-container">
@@ -125,10 +125,21 @@ const Map = () => {
isSearch={false}
gisMaps={gisMaps}
isMiniMap={true}
searchView={CustomMapSearchBar}
searchView={(props: any) => (
<MapSearchBar
searchType="공급처 후보지 조회"
showApplyButton={false}
checkedLayers={checkedLayers}
checkedLayersData={checkedLayersData}
{...props}
/>
)
}
detailView={(props: any) => (
<NewWindowPopup
onClose={handlePopupClose} // 메모이제이션된 함수 사용
checkedLayers={checkedLayers}
checkedLayersData={checkedLayersData}
{...props}
/>
)}
+59 -98
View File
@@ -1,16 +1,12 @@
"use client"
import { Button, Input, Label } from "@common/components/ui";
import React, { useCallback, useEffect, useRef, useState, useMemo } from "react";
import { useForm } from "react-hook-form";
import { MapSearchFormLeft } from "./mapSearchForm";
import { useWrStore } from "@/app/store";
import { btnClass } from "@/app/store/common/code";
import { SearchBaseForm } from "@common/business_components";
import { FormUi, SelectBox, SearchFormLeft, SearchFormRight, Form, FormField } from "@common/business_components/ui";
import type { ReturnType, SelectDataType, EnvPartSelectDataType } from "@common/business_components/ui";
import SearchInputText from "@/components/ui/searchInput/searchInput";
import { zodResolver } from "@hookform/resolvers/zod";
import type { EnvPartSelectDataType, ReturnType, SelectDataType } from "@common/business_components/ui";
import { SelectBox } from "@common/business_components/ui";
import { Button, Input, Label } from "@common/components/ui";
import React, { useEffect, useMemo, useRef, useState } from "react";
import { MapSearchFormLeft } from "./mapSearchForm";
// 건물 검색 타입 정의
@@ -49,6 +45,8 @@ type SearchBaseType = {
showQueryButton?: boolean;
showInitButton?: boolean;
showApplyButton?: boolean;
checkedLayers?: string[];
checkedLayersData?: any[];
}
const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}`;
@@ -82,17 +80,29 @@ export function MapSearchBar({
rightClassName,
showQueryButton = true,
showInitButton = true,
showApplyButton = true
showApplyButton = true,
checkedLayers = [],
checkedLayersData = [],
}: SearchBaseType) {
// 로딩바
const [isLoading, setIsLoading] = useState(false);
// 상세 정보 조회를 위한 상태 추가
const [popupFeature, setPopupFeature] = useState<any>(null);
const { code } = useWrStore((state) => state);
const [isChHead, setIsChHead] = useState(false);
const ref = useRef<ReturnType>({} as ReturnType);
// zustand store에서 건물 검색 함수와 데이터 가져오기
const {
code,
bdst,
findBdst,
wrMap,
// buildingSearchRes,
buildingNmSearch,
buildingNmSearchMultiple
} = useWrStore((state) => state);
const [selectedBuilding, setSelectedBuilding] = useState<BuildingInfo | null>(null);
const [buildingSearchText, setBuildingSearchText] = useState("");
const [buildingList, setBuildingList] = useState<BuildingInfo[]>([]);
@@ -141,77 +151,16 @@ export function MapSearchBar({
];
}, [selectedSigun]);
// 건물 검색 API 호출 함수
const searchBuildings = async (searchText: string) => {
if (!searchText.trim()) {
setBuildingList([]);
return;
}
try {
const flg = "41"; // 적절한 플래그 값으로 설정
const sigunCode = selectedSigun.val !== '0' ? selectedSigun.val : '';
const apiEndpoint = `${API_URL}/v1/reuseFc/building/${flg}/${sigunCode}/${encodeURIComponent(searchText)}`;
const response = await fetch(apiEndpoint, {
method: "GET",
headers: {
accept: "application/json",
"Content-Type": "application/json"
}
});
if (!response.ok) {
throw new Error(`API 호출 실패: ${response.status}`);
}
const result = await response.json();
if (result.status === 200 && Array.isArray(result.data)) {
// 건물 검색 결과를 demandCompList 형태로 변환
const convertedList = result.data.map((building: BuildingInfo) => ({
dmdInfoPk: String(building.bdstInfoPk),
dmdCoNm: building.bdstNm,
daddr: building.daddr,
useUsgCd: "",
splyCpct: 0,
splyFmYn: "Y",
// 추가 필드들...
}));
setBuildingList(result.data);
// 글로벌 상태에도 업데이트 (SearchInputText가 사용할 수 있도록)
// getDemandCompList를 직접 호출하는 대신 상태를 직접 설정
const { setBuildingSearchResults } = useWrStore.getState();
if (setBuildingSearchResults) {
setBuildingSearchResults(convertedList);
}
} else {
setBuildingList([]);
}
} catch (error) {
console.error("건물 검색 중 오류 발생:", error);
setBuildingList([]);
}
};
// 조회 함수
const queryVal = () => {
console.log("조회버튼 클릭");
if (searchType == '공급처 후보지 조회') {
// queryClick({
// partValue: partValue,
// sidoValue: sidoValue,
// sigunValue: sigunValue,
// regionValue: regionValue,
// unitWaterShedValue: untitWaterShedValue,
// middleRegionValue: middleRegionValue,
// envPartValue: envPartValue,
// sidoText: selectedSido.text,
// sigunText: selectedSigun.text,
// });
} else if (searchType == '우리동네가뭄지도보기') {
} else if (searchType == '지도보기') {
// 46 : 하수처리시설
// 47 : 하수재이용시설
// 48 : 빗물이용시설
// 49 : 중수도시설
// 50 : 온배수시설
console.log("조회버튼 클릭: ", checkedLayers);
if (checkedLayers && checkedLayers.includes('48')) {
// searchBuildings
}
}
@@ -308,6 +257,27 @@ export function MapSearchBar({
}
}, [partValue, sidoValue, sigunValue, regionValue, untitWaterShedValue, middleRegionValue, envPartValue, ref]);
// buildingSearchText가 변경될 때 API 호출
useEffect(() => {
if (buildingSearchText && buildingSearchText.length > 1) {
if (checkedLayers && checkedLayers.length > 0) {
// 다중 레이어 검색
// buildingNmSearchMultiple(checkedLayers, selectedSigun.val || "", buildingSearchText);
buildingNmSearch("RNB", selectedSigun.val, buildingSearchText);
} else {
// 단일 레이어 검색
// buildingNmSearch(checkedLayers[0], selectedSigun.val, buildingSearchText);
}
} else {
// 검색 텍스트가 짧으면 리스트 초기화
if (checkedLayers && checkedLayers.length > 0) {
// buildingNmSearchMultiple(checkedLayers, selectedSigun.val || "", "");
} else {
buildingNmSearch("RNB", selectedSigun.val || "", "");
}
}
}, [buildingSearchText]);
return (
<div className="absolute left-1/2 -translate-x-1/2 bg-white flex items-center justify-center p-4 rounded-md shadow-lg w-auto min-w-fit">
<div className={leftClassName ? leftClassName : `flex items-center justify-center space-x-2 `}>
@@ -338,32 +308,23 @@ export function MapSearchBar({
value={selectedSigun.val}
selectClass={'w-32'}
/>
{/* <Label className="w-auto text-xl text-gray-600 whitespace-nowrap">시설명</Label>
<div className="w-44">
<Input className="mr-4" />
</div> */}
<div className="flex items-center">
<Label className="w-20 text-xl text-gray-600">시설명</Label>
<div className="w-64">
<SearchInputText
label="시설명"
initValue={buildingSearchText}
setValue={(searchText) => {
setBuildingSearchText(searchText || "");
if (searchText) {
searchBuildings(searchText);
}
setValue={(searchNm?: string) => {
setBuildingSearchText(searchNm || "");
}}
setRtnData={(buildingData) => {
if (buildingData && buildingData.length > 0 && buildingData[0]) {
// 선택된 건물 정보를 찾아서 설정
const selectedBuildingInfo = buildingList.find(building =>
building.bdstNm === buildingData[0].dmdCoNm
);
setSelectedBuilding(selectedBuildingInfo || null);
console.log("선택된 시설:", selectedBuildingInfo);
}
setRtnData={function (rtnData: any): void {
// 선택된 건물 데이터 처리
// if (rtnData && rtnData.bdstInfoPk) {
// setSelectedBuilding(rtnData);
// setBuildingSearchText(rtnData.bdstNm || "");
// }
}}
searchList={wrMap.buildingSearchRes || []}
/>
</div>
</div>
+2 -2
View File
@@ -159,8 +159,8 @@ export default function Home() {
<h2 className="text-xl font-semibold mb-4">시설 현황(지도)</h2>
<div className="grid grid-cols-12 gap-4">
<div className="col-span-2">
<Button className="w-full">
<Link href="map/htdflSttn">시설 현황(지도)</Link>
<Button className="w-full" onClick={() => {router.push('/map/htdflSttn')}}>
시설 현황(지도)
</Button>
</div>
</div>
@@ -41,8 +41,12 @@ export default React.memo(function GisTree({ checkedNodes = [], setCheckedNodes
// 기본 체크된 레이어들을 지도에 표시
gisCore.setLayerOnMap(defaultChecked, true);
// 초기 체크된 레이어 정보를 상위로 전달
const defaultCheckedNodesData = nodes.filter(node => defaultChecked.includes(node.id));
onCheckedChange?.(defaultChecked, defaultCheckedNodesData);
}
}, [nodes]);
}, [nodes]); // onCheckedChange를 의존성에 추가
// 초기 표시 여부가 'Y'인 레이어들 찾기
const getDefaultCheckedNodes = (nodes, values = []) => {