Merge branch 'main' of https://www.onasys.co.kr:3000/ONAANDALL/frontend
Deploy Frontend / deploy (push) Successful in 7m13s

This commit is contained in:
2026-10-11 13:27:16 +09:00
45 changed files with 1118 additions and 135 deletions
@@ -659,7 +659,7 @@ GisOlLayer.prototype = {
this.searchLayer = null;
}
},
showSearchResultsFromGeoJSON: function (lyrTyNm, featureId) {
showSearchResultsFromGeoJSON: function (lyrTyNm, featureId, options) {
//레이어 타입 설정(point, line 등. 이걸 style에서 사용)
const layerCode = GisApp.layerCode;
const matched = Object.values(layerCode).find(obj => obj.lyrTyNm === lyrTyNm);
@@ -674,10 +674,15 @@ GisOlLayer.prototype = {
// 1) 로드 완료시 extent 맞추기
const onLoadEnd = () => {
// 이전 검색 source의 지연 응답은 현재 선택을 변경하지 않는다.
if (layer.getSource() !== source || this.searchLayer !== layer) return;
if (Object.keys(source.nullGeometryFeatures_).length > 0) showBusinessUserMessage('해당 시설의 위치 정보가 없습니다.');
const feats = source.getFeatures();
if (feats && feats.length > 0) {
const extent = feats[0].getGeometry()?.getExtent();
// 선택적 소비자만 성공 경계에서 초기화/취소를 결정한다.
if (options?.beforeFocus && options.beforeFocus(extent) === false) return;
const select = GisApp.Module.select;
select.selectFeature(feats[0], false);
} else {
@@ -740,7 +745,7 @@ GisOlLayer.prototype = {
* @param {string} lyrTyNm 레이어 타입명
* @param {Array|string} featureIds 피처 ID 배열 또는 단일 ID
*/
showMultipleSearchResultsFromGeoJSON: function (lyrTyNm, featureIds) {
showMultipleSearchResultsFromGeoJSON: function (lyrTyNm, featureIds, options) {
const layerCode = GisApp.layerCode;
const matched = Object.values(layerCode).find(obj => obj.lyrTyNm === lyrTyNm);
@@ -754,10 +759,13 @@ GisOlLayer.prototype = {
const source = this.getMultiFeatureSourceFromGeoserver(lyrTyNm, featureIds);
const onLoadEnd = async () => {
if (layer.getSource() !== source || this.searchLayer !== layer) return;
const feats = source.getFeatures();
if (feats && feats.length > 0) {
const focusExtent = this.combineExtents(feats.map(f => f.getGeometry()?.getExtent()));
if (options?.beforeFocus && options.beforeFocus(focusExtent) === false) return;
const select = GisApp.Module.select;
await select.resetSelectedFeature();
select.resetSelectedFeature();
for (const feature of feats) select.selectMultiFeature(feature, true);
const geometries = feats.map(f => f.getGeometry());
if (geometries.length > 0) {
@@ -1,7 +1,7 @@
"use client";
import { SelectBox, type SelectDataType } from "../../../ui";
import { Button } from "@common/components/ui";
import { useCallback, useEffect, useState } from "react";
import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
import { showBusinessUserMessage } from "../../../lib/user-message";
import GisApp from "../lib/gis.core";
import {
@@ -34,6 +34,10 @@ interface SearchBarProps {
facilityLayerFilter?: FacilityLayerFilter;
/** 관망도 전용 3개 셀렉트와 조회 버튼을 하나로 연결한 검색바 레이아웃이다. */
integrated?: boolean;
/** 지도 목록 선택이 성공했을 때만 증가하는 선택적 초기화 revision이다. */
resetRevision?: number;
/** 직접 검색조건 조작이 이전 비동기 지도 선택을 취소할 수 있게 알린다. */
onSearchInteraction?: () => void;
}
interface CenterSelectDataType extends SelectDataType {
@@ -46,7 +50,13 @@ export const ProcSearchBar = ({
sidoSearchZoom = 11,
facilityLayerFilter,
integrated = false,
resetRevision,
onSearchInteraction,
}: SearchBarProps) => {
/** 초기화·조건 변경 이전 시설 후보 응답이 현재 선택을 되살리지 못하게 한다. */
const facilityRequestRef = useRef(0);
/** 라벨 변경은 선택을 지우지 않고 새 revision에서만 초기화한다. */
const appliedResetRevisionRef = useRef<number | undefined>(undefined);
const [sidoValue, setSidoValue] = useState("0");
const [sigunValue, setSigunValue] = useState("0");
const [sidoData, setSidoData] = useState<CenterSelectDataType[]>([
@@ -157,6 +167,7 @@ export const ProcSearchBar = ({
sidoNm: string,
currentFacilityType?: string,
) => {
const request = ++facilityRequestRef.current;
const type = currentFacilityType ?? facilityType;
if (!type || type === "0") return;
@@ -240,6 +251,7 @@ export const ProcSearchBar = ({
}));
}
if (request !== facilityRequestRef.current) return;
const sorted = list
.slice()
.sort((a, b) => a.text.localeCompare(b.text, "ko"));
@@ -248,6 +260,20 @@ export const ProcSearchBar = ({
},
[defaultFacilityData, facilityLayerFilter, facilityType],
);
/** 외부 이동은 검색값만 초기화하고 지도·레이어 및 원천 지역 목록은 유지한다. */
useLayoutEffect(() => {
if (resetRevision === undefined || appliedResetRevisionRef.current === resetRevision) return;
appliedResetRevisionRef.current = resetRevision;
facilityRequestRef.current += 1;
setSidoValue("0");
setSigunValue("0");
setFacilityValue("0");
setFilteredSigunData([{ text: "시군구", val: "0" }]);
setFacilityData(defaultFacilityData());
setPendingExternalFacilityName("");
}, [resetRevision, defaultFacilityData]);
useEffect(() => () => { facilityRequestRef.current += 1; }, []);
useEffect(() => {
fetchSido();
fetchSigungu();
@@ -262,6 +288,7 @@ export const ProcSearchBar = ({
return;
}
facilityRequestRef.current += 1;
setFacilityData(defaultFacilityData());
setFacilityValue("0");
}, [
@@ -317,6 +344,8 @@ export const ProcSearchBar = ({
const onSelectValue = useCallback(
(val: string, type: string) => {
facilityRequestRef.current += 1;
onSearchInteraction?.();
if (type === "ctpvCd") {
setSidoValue(val);
initializeSigunData(val);
@@ -368,10 +397,12 @@ export const ProcSearchBar = ({
sidoData,
sidoValue,
facilityType,
onSearchInteraction,
],
);
const handleSearch = useCallback(() => {
onSearchInteraction?.();
if (sidoValue === "0") {
showBusinessUserMessage("시도를 선택해 주세요.");
return;
@@ -400,6 +431,7 @@ export const ProcSearchBar = ({
filteredSigunData,
sigunData,
sidoSearchZoom,
onSearchInteraction,
]);
useEffect(() => {
@@ -79,6 +79,8 @@ export const moveMapToCenter = (
gisMaps: RefObject<any>,
centerCoord?: [number, number],
zoom?: number,
/** 유효한 중심을 확보한 뒤 소비자가 이동을 승인/취소하는 선택적 callback이다. */
beforeMove?: (extent: number[]) => boolean,
) => {
if (!centerCoord) return;
@@ -99,6 +101,7 @@ export const moveMapToCenter = (
}
}
if (beforeMove && !beforeMove([center[0], center[1], center[0], center[1]])) return;
view.animate({
center,
...(typeof zoom === "number" ? { zoom } : {}),