Merge branch 'iipbs/sn/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/sn/dev

This commit is contained in:
2025-08-20 21:26:00 +09:00
2 changed files with 150 additions and 24 deletions
@@ -149,18 +149,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
setIsResetDetailSelection(true); // 선택 초기화
setDetailBizId(0);
// queryParams는 fetchFilteredData 내에서 매번 새로 생성되므로 별도 상태 초기화는 필요 없음
}, [activeTab, basicType, workType, open]);
useEffect(() => {
setActiveTab(TABS[0]?.val ?? "basic");
setBasicType(BASIC_TYPES[0]?.val ?? "");
setWorkType(WORK_TYPES[0]?.val ?? "");
setTableCount(0);
setTableData([]);
setDetailTableCount(0);
setDetailTableData([]);
setDetailBizId(0);
}, [open]);
}, [activeTab, basicType, workType]);
// 페이지 변경 시마다 데이터 fetch
useEffect(() => {
@@ -490,14 +479,16 @@ const SearchModal: React.FC<SearchModalProps> = ({
};
// 기본정보 테이블 cellClick 핸들러
const handleBasicCellClick = (index?: number | undefined, headId?: String) => {
const handleBasicCellClick = async (index?: number | undefined, headId?: String) => {
if (typeof index === "number") {
setIsLoading(true);
const row = tableData[index];
const layerNm = row.lyrTypeNm;
const fid = row.fid;
const core = GisApp.Module.layer as any;
core.showSearchResultsFromGeoJSON(layerNm, fid);
await core.showSearchResultsFromGeoJSON(layerNm, fid);
setIsLoading(false);
onOpenChange(false); // SearchModal 닫기
}
};
@@ -521,8 +512,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
RequestDTO.workTypes = JSON.parse(workType.replace(/'/g, '"'));
const res = await getData(`v1/bizInfo/fclts?${new URLSearchParams(RequestDTO).toString()}`);
console.log('운영정보 cellClick 데이터1:', res);
console.log('운영정보 cellClick 데이터2:', res.data);
console.log('운영정보 cellClick 데이터:', res.data);
const mappedData = (res.data || []).map((item: any, detailIndex: number) => {
const formattedItem = formatDisplayData(item);
@@ -587,8 +577,22 @@ const SearchModal: React.FC<SearchModalProps> = ({
}
};
const handleOperationResultCellClick = async (index?: number | undefined, headId?: String) => {
if (typeof index === "number") {
setIsLoading(true);
const row = detailTableData[index];
const layerNm = row.lyrTypeNm;
const fid = row.fid;
const core = GisApp.Module.layer as any;
await core.showSearchResultsFromGeoJSON(layerNm, fid);
setIsLoading(false);
onOpenChange(false); // SearchModal 닫기
}
};
// 기본정보 Excel 다운로드 핸들러
const BasicExcelDownload = async () => {
const basicExcelDownload = async () => {
if (tableData.length === 0) {
alert("다운로드 할 Excel 파일이 없습니다.\n조회 후 다시 시도해주세요.");
return;
@@ -804,7 +808,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
};
// 운영정보 Excel 다운로드 핸들러 추가
const OperationExcelDownload = async () => {
const operationExcelDownload = async () => {
if (tableData.length === 0) {
alert("다운로드 할 Excel 파일이 없습니다.\n조회 후 다시 시도해주세요.");
return;
@@ -898,7 +902,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
};
// 운영정보 Excel 다운로드 핸들러 추가
const OperationResultExcelDownload = async () => {
const operationResultExcelDownload = async () => {
if (detailTableData.length === 0) {
alert("다운로드 할 Excel 파일이 없습니다.\n조회 후 다시 시도해주세요.");
return;
@@ -1001,12 +1005,26 @@ const SearchModal: React.FC<SearchModalProps> = ({
}
};
const viewMultipleFeature = async () => {
if (!(tableData.length === 0)) {
setIsLoading(true);
const row = tableData[0];
const layerNm = row.lyrTypeNm;
const fids = tableData.map((item) => item.fid);
const core = GisApp.Module.layer as any;
await core.showMultipleSearchResultsFromGeoJSON(layerNm, fids);
setIsLoading(false);
onOpenChange(false);
}
}
const renderBody = () => (
<>
{/* 탭 영역 */}
<UITab
defaultValue={TABS[0]?.val}
defaultValue={activeTab}
tabList={TABS.map((tab) => ({
value: tab.val,
label: tab.text
@@ -1084,7 +1102,10 @@ const SearchModal: React.FC<SearchModalProps> = ({
resetPageNo={isResetPage}
resetSelection={isResetSelection}
headButton={
<Button className="px-4 py-2 !mr-[1.2rem] text-white rounded-sm text-lg font-medium green" size="sm" onClick={BasicExcelDownload}>파일 다운로드</Button>
<div className='flex'>
<Button className="px-4 py-2 !mr-[1.2rem] text-white rounded-sm text-lg font-medium primary" size="sm" onClick={viewMultipleFeature}>지도에서 보기</Button>
<Button className="px-4 py-2 !mr-[1.2rem] text-white rounded-sm text-lg font-medium green" size="sm" onClick={basicExcelDownload}>파일 다운로드</Button>
</div>
}
/>
</div>
@@ -1112,7 +1133,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
resetPageNo={isResetPage}
resetSelection={isResetSelection}
headButton={
<Button className="px-4 py-2 !mr-[1.2rem] text-white rounded-sm text-sm font-medium green" size="sm" onClick={OperationExcelDownload}>파일 다운로드</Button>
<Button className="px-4 py-2 !mr-[1.2rem] text-white rounded-sm text-sm font-medium green" size="sm" onClick={operationExcelDownload}>파일 다운로드</Button>
}
/>
</div>
@@ -1133,12 +1154,12 @@ const SearchModal: React.FC<SearchModalProps> = ({
headlist={dynamicHeadlist2}
pageSize={OPERATION_TABLE_SIZE}
total={detailTableCount}
cellClick={undefined}
cellClick={handleOperationResultCellClick}
onPageChange={setDetailCurPage}
resetPageNo={isResetDetailPage}
resetSelection={isResetDetailSelection}
headButton={
<Button className="px-4 py-2 !mr-[1.2rem] text-white rounded-sm text-lg font-medium green" size="sm" onClick={OperationResultExcelDownload}>파일 다운로드</Button>
<Button className="px-4 py-2 !mr-[1.2rem] text-white rounded-sm text-lg font-medium green" size="sm" onClick={operationResultExcelDownload}>파일 다운로드</Button>
}
/>
</div>
@@ -688,6 +688,111 @@ GisOlLayer.prototype = {
});
return source;
},
/**
* 여러 개의 검색 결과를 GeoJSON으로 표시
* @param {string} lyrTyNm 레이어 타입명
* @param {Array|string} featureIds 피처 ID 배열 또는 단일 ID
*/
showMultipleSearchResultsFromGeoJSON: function (lyrTyNm, featureIds) {
const layer = this.ensureSearchLayer();
const prev = layer.getSource();
if (prev) prev.clear(true);
const layerCode = GisApp.layerCode;
const matched = Object.values(layerCode).find(obj => obj.lyrTyNm === lyrTyNm);
layer.set("gobjTy", matched.gobjTy);
const source = this.getMultiFeatureSourceFromGeoserver(lyrTyNm, featureIds);
const onLoadEnd = async () => {
const feats = source.getFeatures();
if (feats && feats.length > 0) {
const select = GisApp.Module.select;
await select.resetSelectedFeature();
for (const feature of feats) select.selectMultiFeature(feature, true);
const geometries = feats.map(f => f.getGeometry());
if (geometries.length > 0) {
const extent = this.combineExtents(geometries.map(g => g?.getExtent()));
let map = GisApp.Module.core.map;
map.getView().fit(extent, {
duration: 300,
padding: [50, 50, 50, 50],
maxZoom: 16
});
}
} else {
console.warn('[search-layer] GeoJSON에 피처가 없습니다.');
}
source.un('featuresloadend', onLoadEnd);
source.un('featuresloaderror', onLoadError);
};
const onLoadError = (e) => {
console.error('[search-layer] GeoJSON 로드 실패:', e);
source.un('featuresloadend', onLoadEnd);
source.un('featuresloaderror', onLoadError);
};
source.once('featuresloadend', onLoadEnd);
source.once('featuresloaderror', onLoadError);
layer.setSource(source);
source.refresh();
},
/**
* 피쳐 ID로 지오서버에서 검색해서 여러개 피처의 벡터소스를 리턴
* @param {} lyrTyNm
* @param {*} featureId
* @returns
*/
getMultiFeatureSourceFromGeoserver: function (lyrTyNm, featureId) {
const typeName = this.storageNm + lyrTyNm;
const srsName = GisApp.TO_PROJ;
const baseUrl = GisApp.GEOSERVER_URL;
const ids = Array.isArray(featureId) ? featureId : [featureId];
const source = new VectorSource({
format: new GeoJSON(),
url: () => {
const params = new URLSearchParams({
SERVICE: 'WFS',
VERSION: '1.1.0',
REQUEST: 'GetFeature',
TYPENAME: typeName,
SRSNAME: srsName,
OUTPUTFORMAT: 'application/json',
FEATUREID: ids.join(','),
});
return `${baseUrl}?${params.toString()}`;
},
strategy: all,
});
return source;
},
/**
* 여러 extent 배열을 받아 결합된 extent를 반환
* @param {Array<Array<number>>} extents [[minX,minY,maxX,maxY], ...]
* @returns {Array<number>|null}
*/
combineExtents: function (extents) {
if (!extents || extents.length === 0) return null;
let minX = Infinity, minY = Infinity, maxX = -Infinity, maxY = -Infinity;
for (const e of extents) {
if (!e || e.length < 4) continue;
minX = Math.min(minX, e[0]);
minY = Math.min(minY, e[1]);
maxX = Math.max(maxX, e[2]);
maxY = Math.max(maxY, e[3]);
}
if (!isFinite(minX) || !isFinite(minY) || !isFinite(maxX) || !isFinite(maxY)) return null;
return [minX, minY, maxX, maxY];
}
};