Merge remote-tracking branch 'origin/iipbs/sn/dev' into iipbs/sn/search

This commit is contained in:
swcho
2025-05-21 11:17:58 +09:00
7 changed files with 88 additions and 28 deletions
+7 -3
View File
@@ -7,20 +7,24 @@ interface ModalProps {
title?: string;
children: React.ReactNode;
closeModal: () => void;
saveModal?: () => void
}
const Modal: React.FC<ModalProps> = ({ isModalOpen, setIsModalOpen, title, children, closeModal }) => {
const Modal: React.FC<ModalProps> = ({ isModalOpen, setIsModalOpen, title, children, closeModal,saveModal }) => {
return (
<Dialog open={isModalOpen} onOpenChange={setIsModalOpen}>
<DialogOverlay className='z-[200]'/>
<DialogContent className="w-auto max-w-[1200px] min-w-[400px] min-h-[300px] h-auto max-h-[450px] overflow-auto z-[1000]">
<DialogOverlay className='z-[200]' />
<DialogContent className="w-auto max-w-[1200px] min-w-[400px] min-h-[150px] h-auto max-h-[450px] overflow-auto z-[1000]">
{title ? <DialogHeader>
<DialogTitle>{title}</DialogTitle>
</DialogHeader> : null}
{children}
<DialogFooter>
<DialogClose>
<div className='flex justify-end gap-2'>
{saveModal ? <button className="px-4 py-2 bg-primary text-white rounded" onClick={saveModal}>저장</button> : null}
<button className="px-4 py-2 bg-gray-600 text-white rounded" onClick={closeModal}>닫기</button>
</div>
</DialogClose>
</DialogFooter>
</DialogContent>
+2 -1
View File
@@ -17,9 +17,10 @@ type LayerContentMap = {
const layerContentMap: LayerContentMap = {
// TODO: gis데이터 추가되면 추가 작업 필요
"하수처리시설": {
component: StormWaterPump,
component: SwtrmtFclt,
},
"하수처리구역": { },
"우수토실":{component: Ssc,},
"중점우수토실":{component: Ssc,},
"하수관로":{component: MsSwagePpl,},
"하수맨홀":{component: smh,},
@@ -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 = [
@@ -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" },
]
// 오수펌프
+36 -18
View File
@@ -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<number[]>; 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<any>(); // api 호출여부
const contentRef = useRef<HTMLDivElement>(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<string, any> = {}
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.");
}}></VerticalTable>
<div className="w-full mx-5 flex items-center justify-end space-x-2">
{buttonChk ? <Button onClick={btnClickEvent} className="px-4 mr-5 py-2 bg-blue-600 text-white rounded-sm text-sm font-medium hover:bg-blue-700">
{buttonChk ? <Button onClick={handleSaveClick} className="px-4 mr-5 py-2 bg-blue-600 text-white rounded-sm text-sm font-medium hover:bg-blue-700">
저장
</Button> : null}
</div>
<Modal isModalOpen={isModalOpen} title="자체입력정보 저장" setIsModalOpen={setIsModalOpen} closeModal={handleModalClose} saveModal={handleModalSave}><div className="text-lg">정보를 저장하시겠습니까?</div></Modal>
{ContentComponent && <ContentComponent featureData={fetchData} />}
</div>
</div>
+32 -1
View File
@@ -11,6 +11,37 @@ const Page = () => {
const [selectedDate, setSelectedDate] = useState(new Date())
const [company, setCompany] = useState('')
const [pjNm, setpjNm] = useState('')
const handleDownload = async () => {
try {
const response = await fetch('/sn/attach-service/api/v1/download/6066b08c-2a55-438c-b8ff-2aed9739a0c6', {
method: 'GET',
});
if (!response.ok) {
throw new Error('파일 다운로드 실패');
}
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
// 다운로드를 트리거할 가짜 <a> 태그 생성
const link = document.createElement('a');
link.href = url;
link.download = 'test.jpg'; // 원하는 파일명 지정
document.body.appendChild(link);
link.click();
// 메모리 해제
link.remove();
window.URL.revokeObjectURL(url);
} catch (error) {
console.error('다운로드 중 오류 발생:', error);
}
};
return (
<ContentLayout title='도시침수 예방사업'>
<SearchDiv>
@@ -53,7 +84,7 @@ const Page = () => {
<h3 className="text-2xl font-medium">사업정보 등록현황</h3>
<div>
<Button className="px-4 py-2 !mr-[1.2rem] bg-blue-600 text-white rounded-sm text-sm font-medium hover:bg-blue-700" size="sm" >파일 다운로드</Button>
<Button className="px-4 py-2 bg-blue-600 text-white rounded-sm text-sm font-medium hover:bg-blue-700 !mr-[1.2rem]" size="sm" >삭제</Button>
<Button className="px-4 py-2 bg-blue-600 text-white rounded-sm text-sm font-medium hover:bg-blue-700 !mr-[1.2rem]" size="sm" onClick={handleDownload} >삭제</Button>
</div>
</div>
<UiTable headName='' tableData={tableData} headlist={tableHeadlist} pageSize={10} total={20} children={undefined} cellClick={() => { }} onPageChange={function (page: number): void {
+8 -2
View File
@@ -1,7 +1,9 @@
import type { NextConfig } from "next";
import webpack from "webpack";
const geoserverUrl = process.env.NEXT_PUBLIC_GEOSERVER_URL?.replace(/\/$/, '') ?? '';
const geoServerUrl = process.env.NEXT_PUBLIC_GEOSERVER_URL?.replace(/\/$/, '') ?? '';
const cnServerUrl = process.env.NEXT_PUBLIC_SERVER_API_URL?.replace(/\/$/, '') ?? '';
const nextConfig: NextConfig = {
basePath: "/sn",
@@ -46,7 +48,11 @@ const nextConfig: NextConfig = {
return [
{
source: '/geoserver/:hasudo*',
destination: `${geoserverUrl}/:hasudo*`,
destination: `${geoServerUrl}/:hasudo*`,
},
{
source: '/attach-service/:path*',
destination: `${cnServerUrl}/attach-service/:path*`,
},
];
},