rafactor: tab size 4 -> 2로 수정
This commit is contained in:
@@ -438,43 +438,43 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
|
||||
// 검색 API 호출 함수
|
||||
const fetchFilteredData = async () => {
|
||||
setIsLoading(true); // 로딩 시작
|
||||
try {
|
||||
const queryParams = new URLSearchParams({
|
||||
lyrTypeNm: basicType,
|
||||
page: `${curPage - 1}`,
|
||||
size: '10',
|
||||
sort: 'string',
|
||||
});
|
||||
|
||||
let cqlFilters: string[] = [];
|
||||
if (activeTab === 'basic') {
|
||||
if (basicType === 'tb_ms_swage_prcs_zone') cqlFilters = zoneCqlFilters();
|
||||
else if (basicType === 'tb_il_swage_prcs_fclt') cqlFilters = facilityCqlFilters();
|
||||
else if (basicType === 'tb_ms_swage_ppl') cqlFilters = pplCqlFilters();
|
||||
} else if (activeTab === 'operation') {
|
||||
cqlFilters = zoneCqlFilters();
|
||||
} else {
|
||||
cqlFilters = reportCqlFilters();
|
||||
}
|
||||
|
||||
if (cqlFilters.length > 0) queryParams.append('cqlFiter', cqlFilters.join(' and '));
|
||||
|
||||
console.log('API URL:', `${API_URL}/v1/fclts?${queryParams.toString()}`);
|
||||
const res = await axios.get(`${API_URL}/v1/fclts?${queryParams.toString()}`);
|
||||
console.log('필터링된 API 결과(개수):', res.data.count);
|
||||
console.log('필터링된 API 결과(데이터):', res.data.data);
|
||||
|
||||
setTableData(res.data.data || []);
|
||||
setTableCount(res.data.count || []);
|
||||
console.log('tableCount 상태 업데이트 후:', res.data.count); // tableCount 로그 추가
|
||||
setShowTable(true);
|
||||
} catch (err) {
|
||||
console.error('API ERROR:', err);
|
||||
setTableData([]);
|
||||
} finally {
|
||||
setIsLoading(false); // 로딩 종료
|
||||
setIsLoading(true); // 로딩 시작
|
||||
try {
|
||||
const queryParams = new URLSearchParams({
|
||||
lyrTypeNm: basicType,
|
||||
page: `${curPage - 1}`,
|
||||
size: '10',
|
||||
sort: 'string',
|
||||
});
|
||||
|
||||
let cqlFilters: string[] = [];
|
||||
if (activeTab === 'basic') {
|
||||
if (basicType === 'tb_ms_swage_prcs_zone') cqlFilters = zoneCqlFilters();
|
||||
else if (basicType === 'tb_il_swage_prcs_fclt') cqlFilters = facilityCqlFilters();
|
||||
else if (basicType === 'tb_ms_swage_ppl') cqlFilters = pplCqlFilters();
|
||||
} else if (activeTab === 'operation') {
|
||||
cqlFilters = zoneCqlFilters();
|
||||
} else {
|
||||
cqlFilters = reportCqlFilters();
|
||||
}
|
||||
|
||||
if (cqlFilters.length > 0) queryParams.append('cqlFiter', cqlFilters.join(' and '));
|
||||
|
||||
console.log('API URL:', `${API_URL}/v1/fclts?${queryParams.toString()}`);
|
||||
const res = await axios.get(`${API_URL}/v1/fclts?${queryParams.toString()}`);
|
||||
console.log('필터링된 API 결과(개수):', res.data.count);
|
||||
console.log('필터링된 API 결과(데이터):', res.data.data);
|
||||
|
||||
setTableData(res.data.data || []);
|
||||
setTableCount(res.data.count || []);
|
||||
console.log('tableCount 상태 업데이트 후:', res.data.count); // tableCount 로그 추가
|
||||
setShowTable(true);
|
||||
} catch (err) {
|
||||
console.error('API ERROR:', err);
|
||||
setTableData([]);
|
||||
} finally {
|
||||
setIsLoading(false); // 로딩 종료
|
||||
}
|
||||
};
|
||||
|
||||
const renderBody = () => (
|
||||
@@ -814,83 +814,83 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
{/* 분기처리: 하수펌프장 */}
|
||||
{(basicType === "tb_ms_swage_psmtn_1" ||
|
||||
basicType === "tb_ms_swage_psmtn_2") && (
|
||||
<div className="mb-3">
|
||||
{/* 설치년도 */}
|
||||
<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 className="mb-3">
|
||||
{/* 설치년도 */}
|
||||
<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 className="flex items-center mb-2">
|
||||
{/* 일일처리용량 - 오수맨홀 */}
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">
|
||||
일일처리용량
|
||||
</Label>
|
||||
<Input
|
||||
className="w-[100px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={시작1}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
set시작1(val);
|
||||
}}
|
||||
/>
|
||||
<span className="mx-2 text-xl font-semibold">~</span>
|
||||
<Input
|
||||
className="mr-8 w-[100px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={종료1}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
set종료1(val);
|
||||
}}
|
||||
/>
|
||||
{/* 우수양수능력 - 우수맨홀 */}
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">
|
||||
우수양수능력
|
||||
</Label>
|
||||
<Input
|
||||
className="w-[100px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={시작2}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
set시작2(val);
|
||||
}}
|
||||
/>
|
||||
<span className="mx-2 text-xl font-semibold">~</span>
|
||||
<Input
|
||||
className="mr-8 w-[100px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={종료2}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
set종료2(val);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center mb-2">
|
||||
{/* 일일처리용량 - 오수맨홀 */}
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">
|
||||
일일처리용량
|
||||
</Label>
|
||||
<Input
|
||||
className="w-[100px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={시작1}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
set시작1(val);
|
||||
}}
|
||||
/>
|
||||
<span className="mx-2 text-xl font-semibold">~</span>
|
||||
<Input
|
||||
className="mr-8 w-[100px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={종료1}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
set종료1(val);
|
||||
}}
|
||||
/>
|
||||
{/* 우수양수능력 - 우수맨홀 */}
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">
|
||||
우수양수능력
|
||||
</Label>
|
||||
<Input
|
||||
className="w-[100px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={시작2}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
set시작2(val);
|
||||
}}
|
||||
/>
|
||||
<span className="mx-2 text-xl font-semibold">~</span>
|
||||
<Input
|
||||
className="mr-8 w-[100px] !text-xl placeholder:text-center"
|
||||
placeholder="입력"
|
||||
value={종료2}
|
||||
onChange={(e) => {
|
||||
// 숫자만 입력 허용
|
||||
const val = e.target.value.replace(/[^0-9]/g, "");
|
||||
set종료2(val);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
)}
|
||||
{/* 분기처리: 우수토실? 토구? */}
|
||||
{basicType === "tb_ms_otfl" && (
|
||||
<div className="mb-3">
|
||||
@@ -981,20 +981,20 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
/>
|
||||
|
||||
{/* 설치년도 */}
|
||||
<YearRangeSelectBox
|
||||
start={yearFilter.시작}
|
||||
end={yearFilter.종료}
|
||||
onChange={(range) =>
|
||||
setYearFilter({
|
||||
...yearFilter,
|
||||
시작: range.start,
|
||||
종료: range.end,
|
||||
})
|
||||
}
|
||||
startLabel="설치년도"
|
||||
endLabel=""
|
||||
/>
|
||||
<div className="flex items-center">
|
||||
<YearRangeSelectBox
|
||||
start={yearFilter.시작}
|
||||
end={yearFilter.종료}
|
||||
onChange={(range) =>
|
||||
setYearFilter({
|
||||
...yearFilter,
|
||||
시작: range.start,
|
||||
종료: range.end,
|
||||
})
|
||||
}
|
||||
startLabel="설치년도"
|
||||
endLabel=""
|
||||
/>
|
||||
<div className="flex items-center">
|
||||
{/* 경과년도 */}
|
||||
<Label className="mr-2 w-auto text-xl text-gray-600">
|
||||
경과년도
|
||||
@@ -1009,12 +1009,12 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
setYearFilter({ ...yearFilter, 경과년도: val });
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{/* 조회 버튼 중앙 정렬 */}
|
||||
<div className="absolute bottom-4 right-4 mr-2">
|
||||
<Button className="text-base py-2" onClick={handleSearch}>조회</Button>
|
||||
</div>
|
||||
</div>
|
||||
{/* 조회 버튼 중앙 정렬 */}
|
||||
<div className="absolute bottom-4 right-4 mr-2">
|
||||
<Button className="text-base py-2" onClick={handleSearch}>조회</Button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{/* 하수도정비기본계획보고서 탭 */}
|
||||
@@ -1026,9 +1026,9 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
sido={시도}
|
||||
sigungu={시군구}
|
||||
onSidoChange={(sidoId) => {
|
||||
set시도(sidoId);
|
||||
set시군구(0);
|
||||
}}
|
||||
set시도(sidoId);
|
||||
set시군구(0);
|
||||
}}
|
||||
onSigunguChange={set시군구}
|
||||
/>
|
||||
</div>
|
||||
@@ -1071,28 +1071,28 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
|
||||
{/* 📋 결과 리스트 */}
|
||||
<div className="h-[60vh] w-full space-y-2 overflow-hidden rounded-lg border">
|
||||
{/* 테이블 영역 (가로/세로 스크롤 가능) */}
|
||||
<div className="flex-1 px-2 pb-2">
|
||||
<LoadingOverlay isLoading={isLoading}/>
|
||||
<UiTable2
|
||||
headName={
|
||||
(() => {
|
||||
const resultCount = tableData.length;
|
||||
const label =
|
||||
activeTab === "basic"
|
||||
? BASIC_TYPES.find((type) => type.val === basicType)?.text
|
||||
: activeTab === "operation"
|
||||
? WORK_TYPES.find((type) => type.val === workType)?.text
|
||||
: activeTab === "report"
|
||||
? "하수도정비기본계획보고서"
|
||||
: TABS.find((tab) => tab.val === activeTab)?.text;
|
||||
return (
|
||||
<>
|
||||
{label} 조회 결과 ({tableCount})건
|
||||
</>
|
||||
);
|
||||
})()
|
||||
}
|
||||
{/* 테이블 영역 (가로/세로 스크롤 가능) */}
|
||||
<div className="flex-1 px-2 pb-2">
|
||||
<LoadingOverlay isLoading={isLoading} />
|
||||
<UiTable2
|
||||
headName={
|
||||
(() => {
|
||||
const resultCount = tableData.length;
|
||||
const label =
|
||||
activeTab === "basic"
|
||||
? BASIC_TYPES.find((type) => type.val === basicType)?.text
|
||||
: activeTab === "operation"
|
||||
? WORK_TYPES.find((type) => type.val === workType)?.text
|
||||
: activeTab === "report"
|
||||
? "하수도정비기본계획보고서"
|
||||
: TABS.find((tab) => tab.val === activeTab)?.text;
|
||||
return (
|
||||
<>
|
||||
{label} 조회 결과 ({tableCount})건
|
||||
</>
|
||||
);
|
||||
})()
|
||||
}
|
||||
tableData={tableData}
|
||||
headlist={dynamicHeadlist}
|
||||
pageSize={tableData.length < 10 ? tableData.length : 10}
|
||||
@@ -1103,8 +1103,8 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
resetPageNo={false}
|
||||
cur={curPage}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
|
||||
@@ -2,49 +2,49 @@ import { Table, TableHeader, TableRow, TableHead, TableBody, TableCell } from "@
|
||||
import React from "react";
|
||||
|
||||
interface InputSectionProps {
|
||||
title: string;
|
||||
data: { year: string; type: string }[];
|
||||
sectionKey: string;
|
||||
onInputChange: (section: string, index: number, field: string, value: string) => void;
|
||||
title: string;
|
||||
data: { year: string; type: string }[];
|
||||
sectionKey: string;
|
||||
onInputChange: (section: string, index: number, field: string, value: string) => void;
|
||||
}
|
||||
|
||||
const InputSection: React.FC<InputSectionProps> = ({ title, data, sectionKey, onInputChange }) => {
|
||||
return (
|
||||
<div>
|
||||
{/* 섹션 제목 */}
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl mx-3 font-medium text-label">{title}</h3>
|
||||
</div>
|
||||
{/* 테이블 */}
|
||||
<div className="card !m-[1.2rem] !p-0r">
|
||||
<Table variant="secondary" className="w-full">
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="border border-gray-300 px-2 py-2 text-sm">년도</TableHead>
|
||||
<TableHead className="border border-gray-300 px-2 py-2 text-sm">구분</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{data.map((row, index) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell className="border border-gray-300 px-2 py-2 text-sm">
|
||||
<input
|
||||
type="text"
|
||||
value={row.year}
|
||||
onChange={(e) => onInputChange(sectionKey, index, "year", e.target.value)}
|
||||
className="w-full text-xl border-radius-0 px-3 py-2 text-base text-center leading-normal"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className="border border-gray-300 px-2 py-2 text-sm text-center leading-normal">
|
||||
{row.type}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
return (
|
||||
<div>
|
||||
{/* 섹션 제목 */}
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl mx-3 font-medium text-label">{title}</h3>
|
||||
</div>
|
||||
{/* 테이블 */}
|
||||
<div className="card !m-[1.2rem] !p-0r">
|
||||
<Table variant="secondary" className="w-full">
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHead className="border border-gray-300 px-2 py-2 text-sm">년도</TableHead>
|
||||
<TableHead className="border border-gray-300 px-2 py-2 text-sm">구분</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{data.map((row, index) => (
|
||||
<TableRow key={index}>
|
||||
<TableCell className="border border-gray-300 px-2 py-2 text-sm">
|
||||
<input
|
||||
type="text"
|
||||
value={row.year}
|
||||
onChange={(e) => onInputChange(sectionKey, index, "year", e.target.value)}
|
||||
className="w-full text-xl border-radius-0 px-3 py-2 text-base text-center leading-normal"
|
||||
/>
|
||||
</TableCell>
|
||||
<TableCell className="border border-gray-300 px-2 py-2 text-sm text-center leading-normal">
|
||||
{row.type}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default InputSection;
|
||||
@@ -5,58 +5,58 @@ import Ssc from "../popupComponent/Ssc";
|
||||
import StormWaterPump from "../popupComponent/stormWaterPump";
|
||||
import SwtrmtFclt from "../popupComponent/SwtrmtFclt";
|
||||
|
||||
type LayerComponentProps = {
|
||||
featureData: any;
|
||||
chkMainSsc?: boolean;
|
||||
additionalData?: any;
|
||||
type LayerComponentProps = {
|
||||
featureData: any;
|
||||
chkMainSsc?: boolean;
|
||||
additionalData?: any;
|
||||
};
|
||||
type LayerContentMap = {
|
||||
[key: string]: {
|
||||
component?: React.FC<LayerComponentProps>;
|
||||
props?: any;
|
||||
};
|
||||
[key: string]: {
|
||||
component?: React.FC<LayerComponentProps>;
|
||||
props?: any;
|
||||
};
|
||||
};
|
||||
// 레이어 이름과 컴포넌트를 매핑
|
||||
// 레이어 선택시 변경되는 팝업 컨텐츠 동적으로 변경하도록 작업
|
||||
const layerContentMap: LayerContentMap = {
|
||||
// TODO: gis데이터 추가되면 추가 작업 필요
|
||||
"하수처리시설": { component: SwtrmtFclt, },
|
||||
"하수처리구역": { },
|
||||
"우수토실":{component: Ssc,},
|
||||
"중점우수토실":{component: Ssc, }, // 중점우수토실 여부 설정
|
||||
"하수관로":{component: MsSwagePpl,},
|
||||
"하수맨홀":{component: smh,},
|
||||
"빗물펌프":{component:StormWaterPump},
|
||||
"오수펌프":{component:sewagePump},
|
||||
// TODO: gis데이터 추가되면 추가 작업 필요
|
||||
"하수처리시설": { component: SwtrmtFclt, },
|
||||
"하수처리구역": {},
|
||||
"우수토실": { component: Ssc, },
|
||||
"중점우수토실": { component: Ssc, }, // 중점우수토실 여부 설정
|
||||
"하수관로": { component: MsSwagePpl, },
|
||||
"하수맨홀": { component: smh, },
|
||||
"빗물펌프": { component: StormWaterPump },
|
||||
"오수펌프": { component: sewagePump },
|
||||
};
|
||||
|
||||
// 동적 컴포넌트 반환 함수
|
||||
export const getLayerContentComponent = (layerNm: string): React.FC<LayerComponentProps> | null => {
|
||||
const component = layerContentMap[layerNm];
|
||||
if (!component || !component.component) {
|
||||
// component가 없으면 null 반환
|
||||
return () => null;
|
||||
}
|
||||
return component.component
|
||||
const component = layerContentMap[layerNm];
|
||||
if (!component || !component.component) {
|
||||
// component가 없으면 null 반환
|
||||
return () => null;
|
||||
}
|
||||
return component.component
|
||||
};
|
||||
// 사용자 입력 필드 (보수 및 갱신, 청소 및 준설, 점검 및 진단)
|
||||
export const handleInputChange = (section: string,
|
||||
index: number,
|
||||
field: string,
|
||||
value: string,
|
||||
dataMap: {
|
||||
[key: string]: {
|
||||
data: { year: string; type: string }[];
|
||||
setData: React.Dispatch<React.SetStateAction<{ year: string; type: string }[]>>;
|
||||
};
|
||||
}
|
||||
index: number,
|
||||
field: string,
|
||||
value: string,
|
||||
dataMap: {
|
||||
[key: string]: {
|
||||
data: { year: string; type: string }[];
|
||||
setData: React.Dispatch<React.SetStateAction<{ year: string; type: string }[]>>;
|
||||
};
|
||||
}
|
||||
) => {
|
||||
const target = dataMap[section];
|
||||
if (!target) return;
|
||||
const target = dataMap[section];
|
||||
if (!target) return;
|
||||
|
||||
const updated = [...target.data];
|
||||
if (updated[index]) {
|
||||
updated[index][field as keyof typeof updated[number]] = value;
|
||||
}
|
||||
target.setData(updated);
|
||||
const updated = [...target.data];
|
||||
if (updated[index]) {
|
||||
updated[index][field as keyof typeof updated[number]] = value;
|
||||
}
|
||||
target.setData(updated);
|
||||
};
|
||||
@@ -1,128 +1,128 @@
|
||||
// 하수처리시설
|
||||
export const SwtrmtFcltCol = [
|
||||
{ id: 'swtrmtFcltNm', title: '하수처리시설명' },
|
||||
{ id: 'fcltCpct', title: '시설용량' },
|
||||
{ id: 'rgnExpln', title: '지역설명' },
|
||||
{ id: 'cmcnYmd', title: '준공일자' },
|
||||
{ id: 'oprtngStrtYmd', title: '가동개시일자' },
|
||||
{ id: 'clsgYmd', title: '폐쇄일자' },
|
||||
{ id: 'prcsMsrcCd', title: '처리공법코드' },
|
||||
// { id: 'laeCd', title: '지자체코드' },
|
||||
{ id: 'laeNm', title: '지자체명' },
|
||||
{ id: 'ctpvNm', title: '시도명' },
|
||||
{ id: 'sggNm', title: '시군구명' },
|
||||
// { id: 'sareaCd', title: '권역코드' },
|
||||
{ id: 'sareaNm', title: '권역명' },
|
||||
{ id: 'lrgeSareaNm', title: '대권역명' },
|
||||
{ id: 'rvrNm', title: '하천명' },
|
||||
{ id: 'addr', title: '주소' },
|
||||
{ id: 'daddr', title: '상세주소' },
|
||||
{ id: 'rdnmAddr', title: '도로명주소' },
|
||||
{ id: 'dtlRdnmAddr', title: '상세도로명주소' },
|
||||
{ id: 'dschgWtstNm', title: '방류수계명' },
|
||||
{ id: 'pllnArea', title: '부지면적' },
|
||||
{ id: 'mngRprtvBzentyNm', title: '관리대행업제명' },
|
||||
{ id: 'swtrmtPpltnCnt', title: '하수처리인구수' },
|
||||
{ id: 'prcsZoneAreaSum', title: '처리구역면적합계' },
|
||||
{ id: 'xcrd', title: 'X좌표' },
|
||||
{ id: 'ycrd', title: 'Y좌표' },
|
||||
// { id: 'oprtngSttsCd', title: '가동상태코드' },
|
||||
{ id: 'rprsMsrcCn', title: '대표공법내용' },
|
||||
{ id: 'sttyCrtrBod', title: '법정기준생물화학적산소요구량' },
|
||||
{ id: 'sttyCrtrToc', title: '법정기준TOC' },
|
||||
{ id: 'sttyCrtrSs', title: '법정기준SS' },
|
||||
{ id: 'sttyCrtrTn', title: '법정기준총질소' },
|
||||
{ id: 'sttyCrtrTph', title: '법정기준총인' },
|
||||
{ id: 'sttyCrtrClbclCnt', title: '법정기준대장균수' },
|
||||
{ id: 'bsnNm', title: '유역명' },
|
||||
// { id: 'obsrvnSptCd', title: '관측지점코드' },
|
||||
{ id: 'swtrmtFcltNm', title: '하수처리시설명' },
|
||||
{ id: 'fcltCpct', title: '시설용량' },
|
||||
{ id: 'rgnExpln', title: '지역설명' },
|
||||
{ id: 'cmcnYmd', title: '준공일자' },
|
||||
{ id: 'oprtngStrtYmd', title: '가동개시일자' },
|
||||
{ id: 'clsgYmd', title: '폐쇄일자' },
|
||||
{ id: 'prcsMsrcCd', title: '처리공법코드' },
|
||||
// { id: 'laeCd', title: '지자체코드' },
|
||||
{ id: 'laeNm', title: '지자체명' },
|
||||
{ id: 'ctpvNm', title: '시도명' },
|
||||
{ id: 'sggNm', title: '시군구명' },
|
||||
// { id: 'sareaCd', title: '권역코드' },
|
||||
{ id: 'sareaNm', title: '권역명' },
|
||||
{ id: 'lrgeSareaNm', title: '대권역명' },
|
||||
{ id: 'rvrNm', title: '하천명' },
|
||||
{ id: 'addr', title: '주소' },
|
||||
{ id: 'daddr', title: '상세주소' },
|
||||
{ id: 'rdnmAddr', title: '도로명주소' },
|
||||
{ id: 'dtlRdnmAddr', title: '상세도로명주소' },
|
||||
{ id: 'dschgWtstNm', title: '방류수계명' },
|
||||
{ id: 'pllnArea', title: '부지면적' },
|
||||
{ id: 'mngRprtvBzentyNm', title: '관리대행업제명' },
|
||||
{ id: 'swtrmtPpltnCnt', title: '하수처리인구수' },
|
||||
{ id: 'prcsZoneAreaSum', title: '처리구역면적합계' },
|
||||
{ id: 'xcrd', title: 'X좌표' },
|
||||
{ id: 'ycrd', title: 'Y좌표' },
|
||||
// { id: 'oprtngSttsCd', title: '가동상태코드' },
|
||||
{ id: 'rprsMsrcCn', title: '대표공법내용' },
|
||||
{ id: 'sttyCrtrBod', title: '법정기준생물화학적산소요구량' },
|
||||
{ id: 'sttyCrtrToc', title: '법정기준TOC' },
|
||||
{ id: 'sttyCrtrSs', title: '법정기준SS' },
|
||||
{ id: 'sttyCrtrTn', title: '법정기준총질소' },
|
||||
{ id: 'sttyCrtrTph', title: '법정기준총인' },
|
||||
{ id: 'sttyCrtrClbclCnt', title: '법정기준대장균수' },
|
||||
{ id: 'bsnNm', title: '유역명' },
|
||||
// { id: 'obsrvnSptCd', title: '관측지점코드' },
|
||||
];
|
||||
|
||||
// 하수처리구역
|
||||
export const SwtrmtPrcsZoneCol = [
|
||||
{ id: 'swtrmtZoneSn', title: '하수처리구역 일련번호' },
|
||||
{ id: 'swtrmtZoneNm', title: '하수처리구역명' },
|
||||
// { id: 'swtrmtFcltCd', title: '하수처리시설코드' },
|
||||
// { id: 'ctpvCd', title: '시도코드' },
|
||||
{ id: 'ctpvNm', title: '시도명' },
|
||||
// { id: 'sggCd', title: '시군구코드' },
|
||||
{ id: 'sggNm', title: '시군구명' },
|
||||
{ id: 'swtrmtFcltNm', title: '하수처리시설명' },
|
||||
{ id: 'swtrmtZoneSn', title: '하수처리구역 일련번호' },
|
||||
{ id: 'swtrmtZoneNm', title: '하수처리구역명' },
|
||||
// { id: 'swtrmtFcltCd', title: '하수처리시설코드' },
|
||||
// { id: 'ctpvCd', title: '시도코드' },
|
||||
{ id: 'ctpvNm', title: '시도명' },
|
||||
// { id: 'sggCd', title: '시군구코드' },
|
||||
{ id: 'sggNm', title: '시군구명' },
|
||||
{ id: 'swtrmtFcltNm', title: '하수처리시설명' },
|
||||
];
|
||||
|
||||
// 하수맨홀
|
||||
export const MsSwageMhlCol = [
|
||||
{ id: 'fid', title: 'FID' },
|
||||
{ id: 'geom', title: 'geometry' },
|
||||
{ id: 'ftreSmblCd', title: '지형지물부호코드' },
|
||||
{ id: 'mngNo', title: '관리번호' },
|
||||
// { id: 'pbadmsEmdCd', title: '행정읍면동코드' },
|
||||
{ id: 'mapshtNo', title: '도엽번호' },
|
||||
// { id: 'mngInstCd', title: '관리기관코드' },
|
||||
{ id: 'instlYmd', title: '설치일자' },
|
||||
{ id: 'lastDrdgnYmd', title: '최종준설일자' },
|
||||
{ id: 'usgCd', title: '용도코드' },
|
||||
// { id: 'fcltyShpCd', title: '시설물형태코드' },
|
||||
{ id: 'mhlKndCd', title: '맨홀종류코드' },
|
||||
{ id: 'swageMhDiam', title: '하수맨홀구경' },
|
||||
{ id: 'wdthLen', title: '가로길이' },
|
||||
{ id: 'vrtcLen', title: '세로길이' },
|
||||
// { id: 'mqltCd', title: '재질코드' },
|
||||
{ id: 'altdVl', title: '표고값' },
|
||||
{ id: 'dpthlVl', title: '심도값' },
|
||||
{ id: 'instMngNo', title:'기관관리번호'}
|
||||
{ id: 'fid', title: 'FID' },
|
||||
{ id: 'geom', title: 'geometry' },
|
||||
{ id: 'ftreSmblCd', title: '지형지물부호코드' },
|
||||
{ id: 'mngNo', title: '관리번호' },
|
||||
// { id: 'pbadmsEmdCd', title: '행정읍면동코드' },
|
||||
{ id: 'mapshtNo', title: '도엽번호' },
|
||||
// { id: 'mngInstCd', title: '관리기관코드' },
|
||||
{ id: 'instlYmd', title: '설치일자' },
|
||||
{ id: 'lastDrdgnYmd', title: '최종준설일자' },
|
||||
{ id: 'usgCd', title: '용도코드' },
|
||||
// { id: 'fcltyShpCd', title: '시설물형태코드' },
|
||||
{ id: 'mhlKndCd', title: '맨홀종류코드' },
|
||||
{ id: 'swageMhDiam', title: '하수맨홀구경' },
|
||||
{ id: 'wdthLen', title: '가로길이' },
|
||||
{ id: 'vrtcLen', title: '세로길이' },
|
||||
// { id: 'mqltCd', title: '재질코드' },
|
||||
{ id: 'altdVl', title: '표고값' },
|
||||
{ id: 'dpthlVl', title: '심도값' },
|
||||
{ id: 'instMngNo', title: '기관관리번호' }
|
||||
];
|
||||
|
||||
// 중점우수토실 측정지점
|
||||
export const untrtSwgRainCol1 = [
|
||||
{ id: 'manage_no', title: '관리번호' },
|
||||
{ id: 'swr_ppl_eqp_nm', title: '처리구역' },
|
||||
{ id: 'swr_ppl_trt_nm', title: '처리분구' },
|
||||
{ id: 'addr', title: '인근주소' },
|
||||
{ id: 'chmbr_xcnts', title: '토실 위도' },
|
||||
{ id: 'chmbr_ydnts', title: '토실 경도' }
|
||||
{ id: 'manage_no', title: '관리번호' },
|
||||
{ id: 'swr_ppl_eqp_nm', title: '처리구역' },
|
||||
{ id: 'swr_ppl_trt_nm', title: '처리분구' },
|
||||
{ id: 'addr', title: '인근주소' },
|
||||
{ id: 'chmbr_xcnts', title: '토실 위도' },
|
||||
{ id: 'chmbr_ydnts', title: '토실 경도' }
|
||||
];
|
||||
|
||||
// 중점우수토실 측정지점 시설현황
|
||||
export const untrtSwgRainCol2 = [
|
||||
{ id: 'flwmtr_mesure_tp_etc', title: '구분' },
|
||||
{ id: 'discharge_pipe_spec', title: '방류관로 규격' },
|
||||
{ id: 'swr_ppl_eqp_ar', title: '배수구역 면적(m²)' },
|
||||
{ id: 'swr_trt_popl', title: '하수처리 인구(인)' },
|
||||
{ id: 'screen_instl_at', title: '스크린설치 여부' },
|
||||
{ id: 'weir_hg', title: '위어 높이(m)' },
|
||||
{ id: 'weir_lt', title: '위어 길이(m)' }
|
||||
{ id: 'flwmtr_mesure_tp_etc', title: '구분' },
|
||||
{ id: 'discharge_pipe_spec', title: '방류관로 규격' },
|
||||
{ id: 'swr_ppl_eqp_ar', title: '배수구역 면적(m²)' },
|
||||
{ id: 'swr_trt_popl', title: '하수처리 인구(인)' },
|
||||
{ id: 'screen_instl_at', title: '스크린설치 여부' },
|
||||
{ id: 'weir_hg', title: '위어 높이(m)' },
|
||||
{ id: 'weir_lt', title: '위어 길이(m)' }
|
||||
];
|
||||
|
||||
// 중점우수토실 강우정보 (id값 틀릴 수 있음)
|
||||
export const untrtSwgRainCol3 = [
|
||||
{ id: 'rain_begin_dt', title: '강우시작일시' },
|
||||
{ id: 'rain_end_dt', title: '강우종료일시' },
|
||||
{ id: 'precdng_dry', title: '선행건기(h)' },
|
||||
{ id: 'rain_info', title: '강우정보' },
|
||||
{ id: 'rain_fall', title: '누적강우량(mm)' },
|
||||
{ id: 'cntnc_time', title: '지속시간(h)' },
|
||||
{ id: 'rain_in', title: '강우강도(mm/h)' }
|
||||
{ id: 'rain_begin_dt', title: '강우시작일시' },
|
||||
{ id: 'rain_end_dt', title: '강우종료일시' },
|
||||
{ id: 'precdng_dry', title: '선행건기(h)' },
|
||||
{ id: 'rain_info', title: '강우정보' },
|
||||
{ id: 'rain_fall', title: '누적강우량(mm)' },
|
||||
{ id: 'cntnc_time', title: '지속시간(h)' },
|
||||
{ id: 'rain_in', title: '강우강도(mm/h)' }
|
||||
];
|
||||
|
||||
// 중점우수토실 측정정보 (id값 틀릴 수 있음)
|
||||
export const untrtSwgRainCol4 = [
|
||||
{ id: 'mesure_begin_dt', title: '측정시작일시' },
|
||||
{ id: 'mesure_end_dt', title: '측정종료일시' },
|
||||
{ id: 'mesure_intrvl', title: '측정간격(분)' },
|
||||
{ id: 'mesure_mhrls_instl_lc', title: '측정기기 설치위치' },
|
||||
{ id: 'flwmtr_combined', title: '유량계 종류 및 모델명' }, // 합쳐진 컬럼
|
||||
{ id: 'qltwtr_begin_dt', title: '채수시작일시' },
|
||||
{ id: 'qltwtr_end_dt', title: '채수종료일시' },
|
||||
{ id: 'qltwtr_co', title: '채수횟수' }
|
||||
{ id: 'mesure_begin_dt', title: '측정시작일시' },
|
||||
{ id: 'mesure_end_dt', title: '측정종료일시' },
|
||||
{ id: 'mesure_intrvl', title: '측정간격(분)' },
|
||||
{ id: 'mesure_mhrls_instl_lc', title: '측정기기 설치위치' },
|
||||
{ id: 'flwmtr_combined', title: '유량계 종류 및 모델명' }, // 합쳐진 컬럼
|
||||
{ id: 'qltwtr_begin_dt', title: '채수시작일시' },
|
||||
{ id: 'qltwtr_end_dt', title: '채수종료일시' },
|
||||
{ id: 'qltwtr_co', title: '채수횟수' }
|
||||
];
|
||||
|
||||
// 중점우수토실 수량/수질측정 정보
|
||||
export const untrtSwgRainCol5 = [
|
||||
{ id: 'odr', title: '회차' },
|
||||
{ id: 'mesureDt', title: '측정일자' },
|
||||
{ id: 'mesureQy', title: '수량(m)' },
|
||||
{ id: 'mesureBod', title: 'BOD(mg/L)' },
|
||||
{ id: 'mesureToc', title: 'TOC(mg/L)' },
|
||||
{ id: 'mesureLoad', title: '오염부하량(kg)' }
|
||||
{ id: 'odr', title: '회차' },
|
||||
{ id: 'mesureDt', title: '측정일자' },
|
||||
{ id: 'mesureQy', title: '수량(m)' },
|
||||
{ id: 'mesureBod', title: 'BOD(mg/L)' },
|
||||
{ id: 'mesureToc', title: 'TOC(mg/L)' },
|
||||
{ id: 'mesureLoad', title: '오염부하량(kg)' }
|
||||
];
|
||||
@@ -7,81 +7,81 @@ import InfilChart from './chart/infilChart';
|
||||
// 하수관로
|
||||
// TODO: RDII 버튼인가 그거 팝업 두개 구현 필요
|
||||
const inputColumn = [
|
||||
{ id: "title", title: "설치주제", type: "select", options: ["BTL", "재정사업"] },
|
||||
{ id: "swtrmt_zone_cd", title: "처리구역", type: "select", options: ["구역1", "구역2"] },
|
||||
{ id: "basu", title: "배수구역", type: "input" },
|
||||
{ id: "mltv_dtdv_nm", title: "배수분구", type: "input" },
|
||||
{ id: "wltjs", title: "지선/간선", type: "select", options: ["지선", "간선"] },
|
||||
{ id: "flow", title: "흐름방식", type: "select", options: ["자연유하", "압송"] },
|
||||
{ id: "place", title: "매설위치", type: "select", options: ["도로", "하상"] },
|
||||
{ id: "title", title: "설치주제", type: "select", options: ["BTL", "재정사업"] },
|
||||
{ id: "swtrmt_zone_cd", title: "처리구역", type: "select", options: ["구역1", "구역2"] },
|
||||
{ id: "basu", title: "배수구역", type: "input" },
|
||||
{ id: "mltv_dtdv_nm", title: "배수분구", type: "input" },
|
||||
{ id: "wltjs", title: "지선/간선", type: "select", options: ["지선", "간선"] },
|
||||
{ id: "flow", title: "흐름방식", type: "select", options: ["자연유하", "압송"] },
|
||||
{ id: "place", title: "매설위치", type: "select", options: ["도로", "하상"] },
|
||||
];
|
||||
const SwtrmtFclt = ({ featureData }: { featureData: any }) => {
|
||||
console.log("featureData", featureData);
|
||||
const [isModalOpen, setIsModalOpen] = React.useState(false);
|
||||
const [btnTxt, setBtnTxt] = useState<string>("");
|
||||
const openModal = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.currentTarget.textContent && setBtnTxt(e.currentTarget.textContent)
|
||||
setIsModalOpen(true);
|
||||
};
|
||||
const closeModal = () => {
|
||||
setIsModalOpen(false);
|
||||
};
|
||||
const [repairData, setRepairData] = useState([
|
||||
{ year: "2025", type: "개대체" },
|
||||
{ year: "2025", type: "보수" },
|
||||
]);
|
||||
const [cleaningData, setCleaningData] = useState([
|
||||
{ year: "2025", type: "청소" },
|
||||
{ year: "2025", type: "준설" },
|
||||
]);
|
||||
const [inspectionData, setInspectionData] = useState([
|
||||
{ year: "2025", type: "관로기술진단" },
|
||||
{ year: "2025", type: "관로기술진단" },
|
||||
]);
|
||||
const dataMap = {
|
||||
repair: { data: repairData, setData: setRepairData },
|
||||
cleaning: { data: cleaningData, setData: setCleaningData },
|
||||
inspection: { data: inspectionData, setData: setInspectionData },
|
||||
};
|
||||
const keysToExtract = [
|
||||
"swtrmt_zone_cd",
|
||||
"mltv_dtdv_nm",
|
||||
"fid",
|
||||
"lyrTypeNm",
|
||||
"reg_dt",
|
||||
"mdfcn_dt",
|
||||
"rgtr",
|
||||
"mdfr",
|
||||
];
|
||||
// const filteredData = Object.entries(featureData)
|
||||
// .filter(([key]) => keysToExtract.includes(key)) // 필요한 키만 필터링
|
||||
// .map(([key, value]) => ({ key, value: value ?? "N/A" })); // 테이블 데이터 형식으로 변환
|
||||
console.log("featureData", featureData);
|
||||
const [isModalOpen, setIsModalOpen] = React.useState(false);
|
||||
const [btnTxt, setBtnTxt] = useState<string>("");
|
||||
const openModal = (e: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e.currentTarget.textContent && setBtnTxt(e.currentTarget.textContent)
|
||||
setIsModalOpen(true);
|
||||
};
|
||||
const closeModal = () => {
|
||||
setIsModalOpen(false);
|
||||
};
|
||||
const [repairData, setRepairData] = useState([
|
||||
{ year: "2025", type: "개대체" },
|
||||
{ year: "2025", type: "보수" },
|
||||
]);
|
||||
const [cleaningData, setCleaningData] = useState([
|
||||
{ year: "2025", type: "청소" },
|
||||
{ year: "2025", type: "준설" },
|
||||
]);
|
||||
const [inspectionData, setInspectionData] = useState([
|
||||
{ year: "2025", type: "관로기술진단" },
|
||||
{ year: "2025", type: "관로기술진단" },
|
||||
]);
|
||||
const dataMap = {
|
||||
repair: { data: repairData, setData: setRepairData },
|
||||
cleaning: { data: cleaningData, setData: setCleaningData },
|
||||
inspection: { data: inspectionData, setData: setInspectionData },
|
||||
};
|
||||
const keysToExtract = [
|
||||
"swtrmt_zone_cd",
|
||||
"mltv_dtdv_nm",
|
||||
"fid",
|
||||
"lyrTypeNm",
|
||||
"reg_dt",
|
||||
"mdfcn_dt",
|
||||
"rgtr",
|
||||
"mdfr",
|
||||
];
|
||||
// const filteredData = Object.entries(featureData)
|
||||
// .filter(([key]) => keysToExtract.includes(key)) // 필요한 키만 필터링
|
||||
// .map(([key, value]) => ({ key, value: value ?? "N/A" })); // 테이블 데이터 형식으로 변환
|
||||
|
||||
return (
|
||||
<div>
|
||||
<VerticalTable headName='자체 입력정보' tableData={[]} headlist={inputColumn} pageSize={20} total={0} children={undefined} cellClick={undefined} isEditable={true} onPageChange={function (page: number): void {
|
||||
throw new Error("Function not implemented.");
|
||||
}}></VerticalTable>
|
||||
<InputSection title="보수 및 갱신" data={repairData} sectionKey="repair" onInputChange={(section, index, field, value) => handleInputChange(section, index, field, value, dataMap)} />
|
||||
<InputSection title="청소 및 준설" data={cleaningData} sectionKey="cleaning" onInputChange={(section, index, field, value) => handleInputChange(section, index, field, value, dataMap)} />
|
||||
<InputSection title="점검 및 진단" data={inspectionData} sectionKey="inspection" onInputChange={(section, index, field, value) => handleInputChange(section, index, field, value, dataMap)} />
|
||||
<div className="absolute bottom-0 left-0 w-full flex justify-start space-x-2 p-4">
|
||||
<Button
|
||||
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
|
||||
onClick={openModal}
|
||||
>
|
||||
Infil
|
||||
</Button>
|
||||
<Button
|
||||
className="px-4 py-2 bg-gray-600 text-white rounded hover:bg-gray-700"
|
||||
onClick={openModal}
|
||||
>
|
||||
RDII
|
||||
</Button>
|
||||
</div>
|
||||
<InfilChart btnTxt={btnTxt} isModalOpen={isModalOpen} setIsModalOpen={setIsModalOpen} closeModal={closeModal} />
|
||||
</div>
|
||||
)
|
||||
return (
|
||||
<div>
|
||||
<VerticalTable headName='자체 입력정보' tableData={[]} headlist={inputColumn} pageSize={20} total={0} children={undefined} cellClick={undefined} isEditable={true} onPageChange={function (page: number): void {
|
||||
throw new Error("Function not implemented.");
|
||||
}}></VerticalTable>
|
||||
<InputSection title="보수 및 갱신" data={repairData} sectionKey="repair" onInputChange={(section, index, field, value) => handleInputChange(section, index, field, value, dataMap)} />
|
||||
<InputSection title="청소 및 준설" data={cleaningData} sectionKey="cleaning" onInputChange={(section, index, field, value) => handleInputChange(section, index, field, value, dataMap)} />
|
||||
<InputSection title="점검 및 진단" data={inspectionData} sectionKey="inspection" onInputChange={(section, index, field, value) => handleInputChange(section, index, field, value, dataMap)} />
|
||||
<div className="absolute bottom-0 left-0 w-full flex justify-start space-x-2 p-4">
|
||||
<Button
|
||||
className="px-4 py-2 bg-blue-600 text-white rounded hover:bg-blue-700"
|
||||
onClick={openModal}
|
||||
>
|
||||
Infil
|
||||
</Button>
|
||||
<Button
|
||||
className="px-4 py-2 bg-gray-600 text-white rounded hover:bg-gray-700"
|
||||
onClick={openModal}
|
||||
>
|
||||
RDII
|
||||
</Button>
|
||||
</div>
|
||||
<InfilChart btnTxt={btnTxt} isModalOpen={isModalOpen} setIsModalOpen={setIsModalOpen} closeModal={closeModal} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SwtrmtFclt
|
||||
@@ -4,10 +4,10 @@ import { Button, TableHead, TableRow } from '@common/components/ui';
|
||||
import React from 'react';
|
||||
import { untrtSwgRainCol3, untrtSwgRainCol4, untrtSwgRainCol5 } from '../common/table';
|
||||
|
||||
const Ssc = ({ featureData, chkMainSsc, additionalData }: {
|
||||
featureData: any,
|
||||
chkMainSsc?: boolean,
|
||||
additionalData?: any
|
||||
const Ssc = ({ featureData, chkMainSsc, additionalData }: {
|
||||
featureData: any,
|
||||
chkMainSsc?: boolean,
|
||||
additionalData?: any
|
||||
}) => {
|
||||
console.log('Additional data received:', additionalData); // 추가 데이터 확인용
|
||||
|
||||
@@ -128,7 +128,7 @@ const Ssc = ({ featureData, chkMainSsc, additionalData }: {
|
||||
body={renderModalBody()}
|
||||
noHeaderYn='Y'
|
||||
noFooterYn='N'
|
||||
// footerContent={renderFooterContent()}
|
||||
// footerContent={renderFooterContent()}
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
|
||||
@@ -6,44 +6,44 @@ import React, { useState } from 'react'
|
||||
|
||||
// 하수처리시설
|
||||
const headList = [
|
||||
{ id: "title", title: "하수도정비 기본계획보고서" },
|
||||
{ id: "cad", title: "하수도대장 평면도" },]
|
||||
{ id: "title", title: "하수도정비 기본계획보고서" },
|
||||
{ id: "cad", title: "하수도대장 평면도" },]
|
||||
const modalHeadList = [
|
||||
{ id: "no", title: "번호" },
|
||||
{ id: "year", title: "승인연도" },
|
||||
{ id: "cad", title: '평면도'},
|
||||
{ id: "fileNm", title: "파일명" },
|
||||
{ id: "date", title: "입력시간" },
|
||||
{ id: "no", title: "번호" },
|
||||
{ id: "year", title: "승인연도" },
|
||||
{ id: "cad", title: '평면도' },
|
||||
{ id: "fileNm", title: "파일명" },
|
||||
{ id: "date", title: "입력시간" },
|
||||
]
|
||||
|
||||
const SwtrmtFclt = () => {
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [modalTitle, setModalTitle] = useState<string>('');
|
||||
const openModal = (title:string) => {
|
||||
setIsModalOpen(true);
|
||||
setModalTitle(title)
|
||||
};
|
||||
const closeModal = () => {
|
||||
setIsModalOpen(false);
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<VerticalTable children={{
|
||||
title: <Button onClick={() => openModal('하수도정비 기본계획보고서')}>보고서</Button>,
|
||||
cad: <Button onClick={() => openModal('하수도대장 평면도')}>평면도</Button>,
|
||||
}} headName='디지털 자료관 연계정보' tableData={[]} headlist={headList} pageSize={20} total={0} cellClick={undefined} isEditable={true} onPageChange={function (page: number): void {
|
||||
throw new Error("Function not implemented.");
|
||||
}}></VerticalTable>
|
||||
<Modal isModalOpen={isModalOpen} setIsModalOpen={setIsModalOpen} closeModal={closeModal}>
|
||||
<div className='grid grid-col-2'>
|
||||
<UiTable headName={modalTitle} tableData={[]} headlist={modalHeadList} pageSize={20} total={0} cellClick={undefined} children={undefined} onPageChange={function (page: number): void {
|
||||
throw new Error("Function not implemented.");
|
||||
}
|
||||
}></UiTable>
|
||||
</div>
|
||||
</Modal>
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
const [modalTitle, setModalTitle] = useState<string>('');
|
||||
const openModal = (title: string) => {
|
||||
setIsModalOpen(true);
|
||||
setModalTitle(title)
|
||||
};
|
||||
const closeModal = () => {
|
||||
setIsModalOpen(false);
|
||||
};
|
||||
return (
|
||||
<div>
|
||||
<VerticalTable children={{
|
||||
title: <Button onClick={() => openModal('하수도정비 기본계획보고서')}>보고서</Button>,
|
||||
cad: <Button onClick={() => openModal('하수도대장 평면도')}>평면도</Button>,
|
||||
}} headName='디지털 자료관 연계정보' tableData={[]} headlist={headList} pageSize={20} total={0} cellClick={undefined} isEditable={true} onPageChange={function (page: number): void {
|
||||
throw new Error("Function not implemented.");
|
||||
}}></VerticalTable>
|
||||
<Modal isModalOpen={isModalOpen} setIsModalOpen={setIsModalOpen} closeModal={closeModal}>
|
||||
<div className='grid grid-col-2'>
|
||||
<UiTable headName={modalTitle} tableData={[]} headlist={modalHeadList} pageSize={20} total={0} cellClick={undefined} children={undefined} onPageChange={function (page: number): void {
|
||||
throw new Error("Function not implemented.");
|
||||
}
|
||||
}></UiTable>
|
||||
</div>
|
||||
)
|
||||
</Modal>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default SwtrmtFclt
|
||||
@@ -3,138 +3,138 @@ import { type ChartConfig, MainChart, CHART_FORM } from "@common/business_compon
|
||||
import Modal from '@/components/ui/modal';
|
||||
import { Button } from '@common/components/ui';
|
||||
type ModalOpenProp = {
|
||||
isModalOpen: boolean,
|
||||
setIsModalOpen: (open: boolean) => void,
|
||||
closeModal: () => void,
|
||||
btnTxt?: string,
|
||||
isModalOpen: boolean,
|
||||
setIsModalOpen: (open: boolean) => void,
|
||||
closeModal: () => void,
|
||||
btnTxt?: string,
|
||||
}
|
||||
const InfilChart = ({ btnTxt, isModalOpen, setIsModalOpen, closeModal }: ModalOpenProp) => {
|
||||
const [popupOpen, setPopupOpen] = React.useState(false);
|
||||
const buttonRef = React.useRef<HTMLButtonElement>(null); // 버튼 참조
|
||||
const [popupPosition, setPopupPosition] = useState<{ top: number; left: number }>({ top: 0, left: 0 }); // 팝업 위치
|
||||
const openPopup = () => {
|
||||
if (buttonRef.current) {
|
||||
const rect = buttonRef.current.getBoundingClientRect(); // 버튼 위치 계산
|
||||
setPopupPosition({
|
||||
top: rect.bottom + window.scrollY, // 버튼 아래쪽
|
||||
left: rect.left + window.scrollX, // 버튼 왼쪽
|
||||
});
|
||||
}
|
||||
const [popupOpen, setPopupOpen] = React.useState(false);
|
||||
const buttonRef = React.useRef<HTMLButtonElement>(null); // 버튼 참조
|
||||
const [popupPosition, setPopupPosition] = useState<{ top: number; left: number }>({ top: 0, left: 0 }); // 팝업 위치
|
||||
const openPopup = () => {
|
||||
if (buttonRef.current) {
|
||||
const rect = buttonRef.current.getBoundingClientRect(); // 버튼 위치 계산
|
||||
setPopupPosition({
|
||||
top: rect.bottom + window.scrollY, // 버튼 아래쪽
|
||||
left: rect.left + window.scrollX, // 버튼 왼쪽
|
||||
});
|
||||
}
|
||||
setPopupOpen(true); // 팝업 열기
|
||||
};
|
||||
console.log("btnTxt", btnTxt);
|
||||
const closePopup = () => { setPopupOpen(false); }; // 팝업 닫기
|
||||
const chartForm = CHART_FORM.LINE;
|
||||
const chartData = {
|
||||
chartData1: [
|
||||
{ label: "1", val: 20 },
|
||||
{ label: "3", val: 40 },
|
||||
{ label: "5", val: 60 },
|
||||
],
|
||||
chartData2: [
|
||||
{ label: "1", val: 15 },
|
||||
{ label: "3", val: 35 },
|
||||
{ label: "5", val: 55 },
|
||||
],
|
||||
}
|
||||
const chartConfig = Object.values(chartData).flat().reduce((config, element) => {
|
||||
config[element.label] = {
|
||||
label: element.label,
|
||||
color: "hsl(var(--chart-1))",
|
||||
};
|
||||
console.log("btnTxt", btnTxt);
|
||||
const closePopup = () => {setPopupOpen(false);}; // 팝업 닫기
|
||||
const chartForm = CHART_FORM.LINE;
|
||||
const chartData = {
|
||||
chartData1: [
|
||||
{ label: "1", val: 20 },
|
||||
{ label: "3", val: 40 },
|
||||
{ label: "5", val: 60 },
|
||||
],
|
||||
chartData2: [
|
||||
{ label: "1", val: 15 },
|
||||
{ label: "3", val: 35 },
|
||||
{ label: "5", val: 55 },
|
||||
],
|
||||
}
|
||||
const chartConfig = Object.values(chartData).flat().reduce((config, element) => {
|
||||
config[element.label] = {
|
||||
label: element.label,
|
||||
color: "hsl(var(--chart-1))",
|
||||
};
|
||||
return config;
|
||||
}, {} as Record<string, { label: string; color: string }>) satisfies ChartConfig;
|
||||
const getCurrentDate = () => {
|
||||
const today = new Date();
|
||||
const year = today.getFullYear();
|
||||
const month = String(today.getMonth() + 1).padStart(2, "0"); // 월은 0부터 시작하므로 +1
|
||||
return `${year}-${month}`;
|
||||
};
|
||||
return (
|
||||
<Modal isModalOpen={isModalOpen} setIsModalOpen={setIsModalOpen} closeModal={closeModal}>
|
||||
{/* 분석 입력현황 조회 */}
|
||||
<div className='grid grid-cols-4 gap-4'>
|
||||
<div className="grid grid-rows-4 gap-4 mb-4">
|
||||
<Button className="bg-white text-white w-[130px] font-bold px-4 mt-2 py-2" onClick={openPopup}>
|
||||
분석 입력현황 조회
|
||||
</Button>
|
||||
return config;
|
||||
}, {} as Record<string, { label: string; color: string }>) satisfies ChartConfig;
|
||||
const getCurrentDate = () => {
|
||||
const today = new Date();
|
||||
const year = today.getFullYear();
|
||||
const month = String(today.getMonth() + 1).padStart(2, "0"); // 월은 0부터 시작하므로 +1
|
||||
return `${year}-${month}`;
|
||||
};
|
||||
return (
|
||||
<Modal isModalOpen={isModalOpen} setIsModalOpen={setIsModalOpen} closeModal={closeModal}>
|
||||
{/* 분석 입력현황 조회 */}
|
||||
<div className='grid grid-cols-4 gap-4'>
|
||||
<div className="grid grid-rows-4 gap-4 mb-4">
|
||||
<Button className="bg-white text-white w-[130px] font-bold px-4 mt-2 py-2" onClick={openPopup}>
|
||||
분석 입력현황 조회
|
||||
</Button>
|
||||
|
||||
{/* 침입수량 분석결과 */}
|
||||
<div className="bg-white text-red-600 font-bold px-4 py-2 ">
|
||||
침입수량 분석결과
|
||||
<div className="flex items-center mt-2">
|
||||
<span className="text-black text-xl font-bold">120</span>
|
||||
<span className="ml-2 text-black">m³/d</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{/* 차트 */}
|
||||
<div className="w-[500px] h-[300px] bg-white p-4">
|
||||
<MainChart chartConfig={chartConfig} chartForm={chartForm} chartData={chartData} chartTitle={'관로유량'} />
|
||||
</div>
|
||||
{/* 침입수량 분석결과 */}
|
||||
<div className="bg-white text-red-600 font-bold px-4 py-2 ">
|
||||
침입수량 분석결과
|
||||
<div className="flex items-center mt-2">
|
||||
<span className="text-black text-xl font-bold">120</span>
|
||||
<span className="ml-2 text-black">m³/d</span>
|
||||
</div>
|
||||
{popupOpen && (
|
||||
<div
|
||||
className="absolute bg-gray-100 border border-gray-300 w-[320px] shadow-lg p-4 rounded"
|
||||
style={{
|
||||
top: popupPosition.top,
|
||||
left: popupPosition.left,
|
||||
zIndex: 1000,
|
||||
}}
|
||||
>
|
||||
<div className="text-lg font-bold mb-2">분석 입력현황</div>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<div>상수사용량 정보:</div>
|
||||
<div>
|
||||
<input type='month' defaultValue={getCurrentDate()} className="border border-gray-300 rounded px-2 py-1"/>
|
||||
</div>
|
||||
<div>오수 전환율:</div>
|
||||
<div>
|
||||
<select className="border border-gray-300 rounded px-2 py-1">
|
||||
<option>0.85</option>
|
||||
<option>0.90</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>수용가-맨홀 매칭:</div>
|
||||
<div>
|
||||
<select className="border border-gray-300 rounded px-2 py-1">
|
||||
<option>매칭(양평).shp</option>
|
||||
<option>매칭(서울).shp</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>관망도 자료:</div>
|
||||
<div>
|
||||
<select className="border border-gray-300 rounded px-2 py-1">
|
||||
<option>양평.inp</option>
|
||||
<option>서울.inp</option>
|
||||
</select>
|
||||
</div>
|
||||
{btnTxt === 'RDII' ? <><div>강우 자료:</div>
|
||||
<div>
|
||||
<select className="border border-gray-300 rounded px-2 py-1">
|
||||
<option>R20240815.xlsx</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>RTX 계수 출처:</div>
|
||||
<div>
|
||||
<select className="border border-gray-300 rounded px-2 py-1">
|
||||
<option>2015년 하수도정비기본계획</option>
|
||||
</select>
|
||||
</div>
|
||||
</> : null}
|
||||
</div>
|
||||
<div className="flex justify-end mt-4 space-x-2">
|
||||
<button className="px-4 py-2 bg-gray-600 text-white rounded" onClick={closePopup}>
|
||||
저장
|
||||
</button>
|
||||
<button className="px-4 py-2 bg-gray-600 text-white rounded" onClick={closePopup}>
|
||||
종료
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Modal>
|
||||
)
|
||||
</div>
|
||||
</div>
|
||||
{/* 차트 */}
|
||||
<div className="w-[500px] h-[300px] bg-white p-4">
|
||||
<MainChart chartConfig={chartConfig} chartForm={chartForm} chartData={chartData} chartTitle={'관로유량'} />
|
||||
</div>
|
||||
</div>
|
||||
{popupOpen && (
|
||||
<div
|
||||
className="absolute bg-gray-100 border border-gray-300 w-[320px] shadow-lg p-4 rounded"
|
||||
style={{
|
||||
top: popupPosition.top,
|
||||
left: popupPosition.left,
|
||||
zIndex: 1000,
|
||||
}}
|
||||
>
|
||||
<div className="text-lg font-bold mb-2">분석 입력현황</div>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
<div>상수사용량 정보:</div>
|
||||
<div>
|
||||
<input type='month' defaultValue={getCurrentDate()} className="border border-gray-300 rounded px-2 py-1" />
|
||||
</div>
|
||||
<div>오수 전환율:</div>
|
||||
<div>
|
||||
<select className="border border-gray-300 rounded px-2 py-1">
|
||||
<option>0.85</option>
|
||||
<option>0.90</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>수용가-맨홀 매칭:</div>
|
||||
<div>
|
||||
<select className="border border-gray-300 rounded px-2 py-1">
|
||||
<option>매칭(양평).shp</option>
|
||||
<option>매칭(서울).shp</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>관망도 자료:</div>
|
||||
<div>
|
||||
<select className="border border-gray-300 rounded px-2 py-1">
|
||||
<option>양평.inp</option>
|
||||
<option>서울.inp</option>
|
||||
</select>
|
||||
</div>
|
||||
{btnTxt === 'RDII' ? <><div>강우 자료:</div>
|
||||
<div>
|
||||
<select className="border border-gray-300 rounded px-2 py-1">
|
||||
<option>R20240815.xlsx</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>RTX 계수 출처:</div>
|
||||
<div>
|
||||
<select className="border border-gray-300 rounded px-2 py-1">
|
||||
<option>2015년 하수도정비기본계획</option>
|
||||
</select>
|
||||
</div>
|
||||
</> : null}
|
||||
</div>
|
||||
<div className="flex justify-end mt-4 space-x-2">
|
||||
<button className="px-4 py-2 bg-gray-600 text-white rounded" onClick={closePopup}>
|
||||
저장
|
||||
</button>
|
||||
<button className="px-4 py-2 bg-gray-600 text-white rounded" onClick={closePopup}>
|
||||
종료
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default InfilChart
|
||||
@@ -1,17 +1,17 @@
|
||||
import { VerticalTable } from '@/components/ui/verticalTable';
|
||||
import React from 'react'
|
||||
const inputColumn = [
|
||||
{ id: "swtrmt_zone_cd", title: "처리구역", type: "select", options: ["구역1", "구역2"] },
|
||||
{ id: "mltv_dtdv_nm", title: "배수분구", type: "input" },
|
||||
{ id: "freq", 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" },
|
||||
]
|
||||
// 오수펌프
|
||||
const sewagePump = ({ featureData }: { featureData: any }) => {
|
||||
return (
|
||||
<div><VerticalTable headName='자체 입력정보' tableData={[]} headlist={inputColumn} pageSize={0} total={0} children={undefined} cellClick={undefined} isEditable={true} onPageChange={function (page: number): void {
|
||||
throw new Error('Function not implemented.');
|
||||
}} />
|
||||
</div>
|
||||
throw new Error('Function not implemented.');
|
||||
}} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -2,19 +2,19 @@
|
||||
import { VerticalTable } from '@/components/ui/verticalTable';
|
||||
import React from 'react'
|
||||
const inputColumn = [
|
||||
{ id: "guyeok", title: "처리구역", type: "select", options: ["구역1", "구역2"] },
|
||||
{ id: "basu", title: "배수구역", type: "input" },
|
||||
{ id: "bungu", title: "배수분구", type: "input" },
|
||||
{ id: "place", title: "매설위치", type: "select", options: ["도로", "하상"] },
|
||||
{id:'spec', title:'규격', type:'input'},
|
||||
{ id: "guyeok", title: "처리구역", type: "select", options: ["구역1", "구역2"] },
|
||||
{ id: "basu", title: "배수구역", type: "input" },
|
||||
{ id: "bungu", title: "배수분구", type: "input" },
|
||||
{ id: "place", title: "매설위치", type: "select", options: ["도로", "하상"] },
|
||||
{ id: 'spec', title: '규격', type: 'input' },
|
||||
];
|
||||
const Smh = () => {
|
||||
return (
|
||||
<div>
|
||||
<VerticalTable headName='자체 입력정보' tableData={[]} headlist={inputColumn} pageSize={20} total={0} children={undefined} cellClick={undefined} isEditable={true} onPageChange={function (page: number): void {
|
||||
<div>
|
||||
<VerticalTable headName='자체 입력정보' tableData={[]} headlist={inputColumn} pageSize={20} total={0} children={undefined} cellClick={undefined} isEditable={true} onPageChange={function (page: number): void {
|
||||
throw new Error("Function not implemented.");
|
||||
}}></VerticalTable>
|
||||
</div>
|
||||
}}></VerticalTable>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import { VerticalTable } from '@/components/ui/verticalTable';
|
||||
import React from 'react'
|
||||
const inputColumn = [
|
||||
{ id: "swtrmt_zone_cd", title: "처리구역", type: "input" },
|
||||
{ id: "mltv_dtdv_nm", title: "배수분구", type: "input" },
|
||||
{ id: "swtrmt_zone_cd", title: "처리구역", type: "input" },
|
||||
{ id: "mltv_dtdv_nm", title: "배수분구", type: "input" },
|
||||
]
|
||||
// 빗물펌프
|
||||
const StormWaterPump = ({ featureData }: { featureData: Record<string, any> }) => {
|
||||
const validKey = inputColumn.map((item) => item.id);
|
||||
const filteredData = featureData && Object.entries(featureData).filter(([key]) => validKey.includes(key)).reduce((obj: Record<string, any>, [key, value]) => {obj[key] = value; return obj;}, {});
|
||||
const filteredData = featureData && Object.entries(featureData).filter(([key]) => validKey.includes(key)).reduce((obj: Record<string, any>, [key, value]) => { obj[key] = value; return obj; }, {});
|
||||
return (
|
||||
<div><VerticalTable headName='자체 입력정보' tableData={[filteredData]} headlist={inputColumn} pageSize={0} total={0} children={undefined} cellClick={undefined} isEditable={true} onPageChange={function (page: number): void {
|
||||
throw new Error('Function not implemented.');
|
||||
}} />
|
||||
</div>
|
||||
throw new Error('Function not implemented.');
|
||||
}} />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -9,202 +9,202 @@ import { getLayerContentComponent } from "./components/common";
|
||||
import { SwtrmtFcltCol, SwtrmtPrcsZoneCol, untrtSwgRainCol1, untrtSwgRainCol2 } from "./components/common/table";
|
||||
// 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; };
|
||||
// const featureId = featureData.getId(); // 피처의 id 가져오기
|
||||
const featureId = typeof featureData.getId === 'function'
|
||||
? featureData.getId()
|
||||
: featureData.fid;
|
||||
const [layerNm, setLayerNm] = useState<string>(""); // 레이어 이름 상태
|
||||
const [tableData, setTableData] = useState<any>(null); // 피처 데이터
|
||||
const [mntrngsData, setMntrngsData] = useState(false); // 수량/수질 측정 정보
|
||||
const [headlist, setHeadlist] = useState<{ id: string; title: string; }[]>([]); // 컬럼리스트 변경상태
|
||||
const [headlist2, setHeadlist2] = useState<{ id: string; title: string; }[]>([]); // 컬럼리스트 변경상태
|
||||
const [buttonChk, setButtonChk] = useState(true)
|
||||
const [fetchData, setfetchData] = useState<any>(); // api 호출여부
|
||||
const contentRef = useRef<HTMLDivElement>(null);
|
||||
const [isModalOpen, setIsModalOpen] = useState(false); // 모달 열기 상태
|
||||
const [chkMainSsc, setChkMainSsc] = useState(false); // 중점우수토실 여부
|
||||
const { gisMap } = useGisContext() as unknown as { checkedLayerIds: React.MutableRefObject<number[]>; treeData: { lyrSn: number }[] | null; gisCore: any; gisMap: any; };
|
||||
// const featureId = featureData.getId(); // 피처의 id 가져오기
|
||||
const featureId = typeof featureData.getId === 'function'
|
||||
? featureData.getId()
|
||||
: featureData.fid;
|
||||
const [layerNm, setLayerNm] = useState<string>(""); // 레이어 이름 상태
|
||||
const [tableData, setTableData] = useState<any>(null); // 피처 데이터
|
||||
const [mntrngsData, setMntrngsData] = useState(false); // 수량/수질 측정 정보
|
||||
const [headlist, setHeadlist] = useState<{ id: string; title: string; }[]>([]); // 컬럼리스트 변경상태
|
||||
const [headlist2, setHeadlist2] = useState<{ id: string; title: string; }[]>([]); // 컬럼리스트 변경상태
|
||||
const [buttonChk, setButtonChk] = useState(true)
|
||||
const [fetchData, setfetchData] = useState<any>(); // api 호출여부
|
||||
const contentRef = useRef<HTMLDivElement>(null);
|
||||
const [isModalOpen, setIsModalOpen] = useState(false); // 모달 열기 상태
|
||||
const [chkMainSsc, setChkMainSsc] = useState(false); // 중점우수토실 여부
|
||||
|
||||
// 피쳐데이터의 현재 레이어 이름 가져오는 함수
|
||||
const getSelectedLayers = () => {
|
||||
let result = null;
|
||||
const layers = gisMap.getLayers().getArray(); // gisMap에서 모든 레이어 가져오기
|
||||
let featureLayer: { get: (key: string) => any } = { get: (key: string) => null }; // 피처가 속한 레이어를 찾기 위한 변수
|
||||
layers.forEach((layer: any) => {
|
||||
const source = layer.getSource();
|
||||
if (source && source.hasFeature && featureData
|
||||
&& typeof featureData.getId === 'function' && source.hasFeature(featureData)) {
|
||||
featureLayer = layer; // 피처가 속한 레이어 찾기
|
||||
}
|
||||
});
|
||||
// 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 = '하수처리구역';
|
||||
else if (featureData.lyrTypeNm === 'om_dt_untrt_swg_rain') result = '중점우수토실';
|
||||
}
|
||||
else result = featureLayer.get('lyrNm');
|
||||
|
||||
return result;
|
||||
// return featureLayer.get('lyrNm'); // 레이어 이름 출력
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
if (featureId) {
|
||||
const selectedLayerName = getSelectedLayers(); // 레이어 이름 가져오기
|
||||
setLayerNm(selectedLayerName); // 상태 업데이트
|
||||
const layerId = featureId.split('.')[0]
|
||||
let data = await getData(`v1/fclts/${featureId}?lyrTypeNm=${layerId}`);
|
||||
setfetchData(data)
|
||||
switch (selectedLayerName) {
|
||||
case '하수처리시설':
|
||||
setHeadlist(SwtrmtFcltCol);
|
||||
setButtonChk(false)
|
||||
break;
|
||||
case '하수처리구역':
|
||||
setHeadlist(SwtrmtPrcsZoneCol)
|
||||
setButtonChk(false)
|
||||
break;
|
||||
case '중점우수토실':
|
||||
setHeadlist(untrtSwgRainCol1)
|
||||
setHeadlist2(untrtSwgRainCol2)
|
||||
setButtonChk(false)
|
||||
setChkMainSsc(true)
|
||||
|
||||
// 데이터 전처리: 합쳐진 필드 추가
|
||||
data = data ? {
|
||||
...data,
|
||||
discharge_pipe_spec: `직경: ${data.pipe_diameter || '-'} / 높이: ${data.pipe_hg || '-'} / 길이: ${data.pipe_lt || '-'}`
|
||||
} : {};
|
||||
|
||||
// 수량/수질측정 정보 데이터 호출
|
||||
const additionalResponse = await getData(`v1/onsemiro/swgRain/${data.adst_dstr_cd}/${data.manage_no}/mntrngs`);
|
||||
setMntrngsData(additionalResponse.content);
|
||||
break;
|
||||
case '우수토실':
|
||||
setButtonChk(true)
|
||||
break;
|
||||
case '하수관로':
|
||||
setButtonChk(true)
|
||||
break;
|
||||
case '오수펌프':
|
||||
setButtonChk(true)
|
||||
break;
|
||||
case '빗물펌프':
|
||||
setButtonChk(true)
|
||||
break;
|
||||
case '하수맨홀':
|
||||
setButtonChk(true)
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
setTableData(data);
|
||||
};
|
||||
}
|
||||
fetchData();
|
||||
}, [featureId]);
|
||||
|
||||
// 저장 시 모달 띄우기
|
||||
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) => {
|
||||
const camelCaseId = toCamelCase(select.id);
|
||||
bodyData[camelCaseId] = select.value;
|
||||
});
|
||||
// input 태그의 값 가져오기
|
||||
inputElements.forEach((input) => {
|
||||
const camelCaseId = toCamelCase(input.id);
|
||||
bodyData[camelCaseId] = input.value;
|
||||
});
|
||||
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.status == 200) {
|
||||
alert('저장되었습니다')
|
||||
setTableData(data)
|
||||
setfetchData(data)
|
||||
}
|
||||
}
|
||||
// 피쳐데이터의 현재 레이어 이름 가져오는 함수
|
||||
const getSelectedLayers = () => {
|
||||
let result = null;
|
||||
const layers = gisMap.getLayers().getArray(); // gisMap에서 모든 레이어 가져오기
|
||||
let featureLayer: { get: (key: string) => any } = { get: (key: string) => null }; // 피처가 속한 레이어를 찾기 위한 변수
|
||||
layers.forEach((layer: any) => {
|
||||
const source = layer.getSource();
|
||||
if (source && source.hasFeature && featureData
|
||||
&& typeof featureData.getId === 'function' && source.hasFeature(featureData)) {
|
||||
featureLayer = layer; // 피처가 속한 레이어 찾기
|
||||
}
|
||||
});
|
||||
// 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 = '하수처리구역';
|
||||
else if (featureData.lyrTypeNm === 'om_dt_untrt_swg_rain') result = '중점우수토실';
|
||||
}
|
||||
const ContentComponent = layerNm ? getLayerContentComponent(layerNm) : null;
|
||||
return (
|
||||
<div id="popup" className="absolute top-0 right-0 w-[350px] h-[calc(100vh-10rem)] bg-white border-r border-gray-300 flex flex-col overflow-hidden transition-all duration-300 z-[50]">
|
||||
<div className="card-header bg-primary flex items-center justify-between p-4">
|
||||
<div className="flex mx-3 flex-col">
|
||||
<h3 className="text-2xl font-medium text-white text-label">{layerNm} 정보</h3>
|
||||
{/* <div className="font-medium text-white text-label">(00구 00리 하수관로)</div> */}
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-white hover:text-gray-700 focus:outline-none"
|
||||
aria-label="닫기"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
<div className="overflow-y-auto overflow-x-hidden flex-1" ref={contentRef}>
|
||||
<VerticalTable
|
||||
headName='LX 연계정보'
|
||||
tableName1={layerNm === '중점우수토실' ? "측정지점" : undefined}
|
||||
tableData={[tableData]}
|
||||
headlist={headlist}
|
||||
pageSize={20}
|
||||
total={0}
|
||||
children={undefined}
|
||||
cellClick={undefined}
|
||||
onPageChange={function (page: number): void {
|
||||
throw new Error("Function not implemented.");
|
||||
}}
|
||||
// 두 번째 테이블 조건부 props
|
||||
showSecondTable={layerNm === '중점우수토실' ? true : false}
|
||||
tableName2={layerNm === '중점우수토실' ? "시설현황" : undefined}
|
||||
tableData2={layerNm === '중점우수토실' && tableData ? [tableData] : []}
|
||||
headlist2={layerNm === '중점우수토실' ? headlist2 : undefined}
|
||||
pageSize2={20}
|
||||
total2={0}
|
||||
isEditable2={buttonChk}
|
||||
/>
|
||||
<div className="w-full mx-5 flex items-center justify-end space-x-2">
|
||||
{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}
|
||||
chkMainSsc={chkMainSsc}
|
||||
additionalData={mntrngsData}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
else result = featureLayer.get('lyrNm');
|
||||
|
||||
return result;
|
||||
// return featureLayer.get('lyrNm'); // 레이어 이름 출력
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
if (featureId) {
|
||||
const selectedLayerName = getSelectedLayers(); // 레이어 이름 가져오기
|
||||
setLayerNm(selectedLayerName); // 상태 업데이트
|
||||
const layerId = featureId.split('.')[0]
|
||||
let data = await getData(`v1/fclts/${featureId}?lyrTypeNm=${layerId}`);
|
||||
setfetchData(data)
|
||||
switch (selectedLayerName) {
|
||||
case '하수처리시설':
|
||||
setHeadlist(SwtrmtFcltCol);
|
||||
setButtonChk(false)
|
||||
break;
|
||||
case '하수처리구역':
|
||||
setHeadlist(SwtrmtPrcsZoneCol)
|
||||
setButtonChk(false)
|
||||
break;
|
||||
case '중점우수토실':
|
||||
setHeadlist(untrtSwgRainCol1)
|
||||
setHeadlist2(untrtSwgRainCol2)
|
||||
setButtonChk(false)
|
||||
setChkMainSsc(true)
|
||||
|
||||
// 데이터 전처리: 합쳐진 필드 추가
|
||||
data = data ? {
|
||||
...data,
|
||||
discharge_pipe_spec: `직경: ${data.pipe_diameter || '-'} / 높이: ${data.pipe_hg || '-'} / 길이: ${data.pipe_lt || '-'}`
|
||||
} : {};
|
||||
|
||||
// 수량/수질측정 정보 데이터 호출
|
||||
const additionalResponse = await getData(`v1/onsemiro/swgRain/${data.adst_dstr_cd}/${data.manage_no}/mntrngs`);
|
||||
setMntrngsData(additionalResponse.content);
|
||||
break;
|
||||
case '우수토실':
|
||||
setButtonChk(true)
|
||||
break;
|
||||
case '하수관로':
|
||||
setButtonChk(true)
|
||||
break;
|
||||
case '오수펌프':
|
||||
setButtonChk(true)
|
||||
break;
|
||||
case '빗물펌프':
|
||||
setButtonChk(true)
|
||||
break;
|
||||
case '하수맨홀':
|
||||
setButtonChk(true)
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
setTableData(data);
|
||||
};
|
||||
}
|
||||
fetchData();
|
||||
}, [featureId]);
|
||||
|
||||
// 저장 시 모달 띄우기
|
||||
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) => {
|
||||
const camelCaseId = toCamelCase(select.id);
|
||||
bodyData[camelCaseId] = select.value;
|
||||
});
|
||||
// input 태그의 값 가져오기
|
||||
inputElements.forEach((input) => {
|
||||
const camelCaseId = toCamelCase(input.id);
|
||||
bodyData[camelCaseId] = input.value;
|
||||
});
|
||||
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.status == 200) {
|
||||
alert('저장되었습니다')
|
||||
setTableData(data)
|
||||
setfetchData(data)
|
||||
}
|
||||
}
|
||||
}
|
||||
const ContentComponent = layerNm ? getLayerContentComponent(layerNm) : null;
|
||||
return (
|
||||
<div id="popup" className="absolute top-0 right-0 w-[350px] h-[calc(100vh-10rem)] bg-white border-r border-gray-300 flex flex-col overflow-hidden transition-all duration-300 z-[50]">
|
||||
<div className="card-header bg-primary flex items-center justify-between p-4">
|
||||
<div className="flex mx-3 flex-col">
|
||||
<h3 className="text-2xl font-medium text-white text-label">{layerNm} 정보</h3>
|
||||
{/* <div className="font-medium text-white text-label">(00구 00리 하수관로)</div> */}
|
||||
</div>
|
||||
)
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="text-white hover:text-gray-700 focus:outline-none"
|
||||
aria-label="닫기"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
</div>
|
||||
<div className="overflow-y-auto overflow-x-hidden flex-1" ref={contentRef}>
|
||||
<VerticalTable
|
||||
headName='LX 연계정보'
|
||||
tableName1={layerNm === '중점우수토실' ? "측정지점" : undefined}
|
||||
tableData={[tableData]}
|
||||
headlist={headlist}
|
||||
pageSize={20}
|
||||
total={0}
|
||||
children={undefined}
|
||||
cellClick={undefined}
|
||||
onPageChange={function (page: number): void {
|
||||
throw new Error("Function not implemented.");
|
||||
}}
|
||||
// 두 번째 테이블 조건부 props
|
||||
showSecondTable={layerNm === '중점우수토실' ? true : false}
|
||||
tableName2={layerNm === '중점우수토실' ? "시설현황" : undefined}
|
||||
tableData2={layerNm === '중점우수토실' && tableData ? [tableData] : []}
|
||||
headlist2={layerNm === '중점우수토실' ? headlist2 : undefined}
|
||||
pageSize2={20}
|
||||
total2={0}
|
||||
isEditable2={buttonChk}
|
||||
/>
|
||||
<div className="w-full mx-5 flex items-center justify-end space-x-2">
|
||||
{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}
|
||||
chkMainSsc={chkMainSsc}
|
||||
additionalData={mntrngsData}
|
||||
/>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
export default FeatureClickEventPopup;
|
||||
@@ -38,7 +38,7 @@ const Map = () => {
|
||||
const [isOpen, setIsOpen] = useState(false); // 팝업 열기/닫기 상태
|
||||
const [isSearchModalOpen, setIsSearchModalOpen] = useState(false); // SearchModal 열림 상태
|
||||
const [featureData, setFeatureData] = useState(null); // 선택된 피쳐 데이터
|
||||
|
||||
|
||||
const fetchData = async () => {
|
||||
try {
|
||||
const data = await getData('v1/map/lyrStngs/SN_NETWORK');
|
||||
|
||||
@@ -10,55 +10,55 @@ import { useEffect, useState, type ReactNode } from 'react'
|
||||
import LoadingOverlay from '@/components/ui/LoadingBar'
|
||||
|
||||
type TitleName = {
|
||||
mainTitle: string
|
||||
firstCardTitle: string
|
||||
secondCardTitle?: string
|
||||
mainTitle: string
|
||||
firstCardTitle: string
|
||||
secondCardTitle?: string
|
||||
}
|
||||
type SelectBox = {
|
||||
labelNm?: Label
|
||||
searchChk?: boolean
|
||||
title?: string;
|
||||
onSearchClick?: () => Promise<void>;
|
||||
labelNm?: Label
|
||||
searchChk?: boolean
|
||||
title?: string;
|
||||
onSearchClick?: () => Promise<void>;
|
||||
}
|
||||
type Label = {
|
||||
first: string
|
||||
second: string
|
||||
third: string
|
||||
fourth: string
|
||||
first: string
|
||||
second: string
|
||||
third: string
|
||||
fourth: string
|
||||
}
|
||||
// API로부터 받아올 데이터 타입을 정의 (SelectBox에 사용될 형태)
|
||||
interface ApiSelectOption {
|
||||
text: string;
|
||||
val: string;
|
||||
text: string;
|
||||
val: string;
|
||||
}
|
||||
type BaseLayout = {
|
||||
children?: ReactNode[]
|
||||
titleName: TitleName
|
||||
form: any
|
||||
onSubmitEvent: Function
|
||||
graphBtn?: boolean
|
||||
analysisBtn?: string
|
||||
firstDivBtn?: boolean
|
||||
selectBox?: SelectBox
|
||||
showDatePicker?: boolean;
|
||||
enoCd?: string | number
|
||||
setEnoCd?: (enoCd: string) => void
|
||||
ctpvCd?: string | number
|
||||
setCtpvCd?: (ctpvCd: string) => void
|
||||
sigungu?: ApiSelectOption
|
||||
setSigungu?: (sigungu: ApiSelectOption) => void
|
||||
selectedSwageTreatmentPlant?: string | number;
|
||||
onSwageTreatmentPlantChange?: (value: string | number) => void;
|
||||
selectedSwageProcessingZone?: string | number;
|
||||
onSwageProcessingZoneChange?: (value: string | number) => void;
|
||||
aplcnYm?: Date
|
||||
setAplcnYm?: (aplcnYm: Date | undefined) => void;
|
||||
isLoading?: boolean;
|
||||
// Excel 다운로드 관련 props 추가
|
||||
onExcelDownload?: () => void;
|
||||
onDeleteFile?: () => void;
|
||||
onAnalysis?: () => void;
|
||||
selectedRowData?: any;
|
||||
children?: ReactNode[]
|
||||
titleName: TitleName
|
||||
form: any
|
||||
onSubmitEvent: Function
|
||||
graphBtn?: boolean
|
||||
analysisBtn?: string
|
||||
firstDivBtn?: boolean
|
||||
selectBox?: SelectBox
|
||||
showDatePicker?: boolean;
|
||||
enoCd?: string | number
|
||||
setEnoCd?: (enoCd: string) => void
|
||||
ctpvCd?: string | number
|
||||
setCtpvCd?: (ctpvCd: string) => void
|
||||
sigungu?: ApiSelectOption
|
||||
setSigungu?: (sigungu: ApiSelectOption) => void
|
||||
selectedSwageTreatmentPlant?: string | number;
|
||||
onSwageTreatmentPlantChange?: (value: string | number) => void;
|
||||
selectedSwageProcessingZone?: string | number;
|
||||
onSwageProcessingZoneChange?: (value: string | number) => void;
|
||||
aplcnYm?: Date
|
||||
setAplcnYm?: (aplcnYm: Date | undefined) => void;
|
||||
isLoading?: boolean;
|
||||
// Excel 다운로드 관련 props 추가
|
||||
onExcelDownload?: () => void;
|
||||
onDeleteFile?: () => void;
|
||||
onAnalysis?: () => void;
|
||||
selectedRowData?: any;
|
||||
}
|
||||
// TODO: 나중에 이벤트 추가되면 파라미터 추가 필요 ?
|
||||
/**
|
||||
@@ -71,207 +71,207 @@ type BaseLayout = {
|
||||
* @returns
|
||||
*/
|
||||
const BaseLayout = ({
|
||||
children,
|
||||
titleName,
|
||||
form,
|
||||
onSubmitEvent,
|
||||
graphBtn = false,
|
||||
analysisBtn,
|
||||
selectBox,
|
||||
showDatePicker = false,
|
||||
enoCd,
|
||||
setEnoCd,
|
||||
ctpvCd,
|
||||
setCtpvCd,
|
||||
sigungu,
|
||||
setSigungu,
|
||||
selectedSwageTreatmentPlant,
|
||||
onSwageTreatmentPlantChange,
|
||||
selectedSwageProcessingZone,
|
||||
onSwageProcessingZoneChange,
|
||||
aplcnYm,
|
||||
setAplcnYm,
|
||||
isLoading,
|
||||
// Excel 다운로드 관련 props 추가
|
||||
onExcelDownload,
|
||||
onDeleteFile,
|
||||
onAnalysis,
|
||||
selectedRowData
|
||||
children,
|
||||
titleName,
|
||||
form,
|
||||
onSubmitEvent,
|
||||
graphBtn = false,
|
||||
analysisBtn,
|
||||
selectBox,
|
||||
showDatePicker = false,
|
||||
enoCd,
|
||||
setEnoCd,
|
||||
ctpvCd,
|
||||
setCtpvCd,
|
||||
sigungu,
|
||||
setSigungu,
|
||||
selectedSwageTreatmentPlant,
|
||||
onSwageTreatmentPlantChange,
|
||||
selectedSwageProcessingZone,
|
||||
onSwageProcessingZoneChange,
|
||||
aplcnYm,
|
||||
setAplcnYm,
|
||||
isLoading,
|
||||
// Excel 다운로드 관련 props 추가
|
||||
onExcelDownload,
|
||||
onDeleteFile,
|
||||
onAnalysis,
|
||||
selectedRowData
|
||||
}: BaseLayout) => {
|
||||
|
||||
const defaultOption: ApiSelectOption = { val: '0', text: '선택' };
|
||||
const defaultOption: ApiSelectOption = { val: '0', text: '선택' };
|
||||
|
||||
const [processingZoneOptions, setProcessingZoneOptions] = useState<ApiSelectOption[]>([]);
|
||||
const [treatmentFacilityOptions, setTreatmentFacilityOptions] = useState<ApiSelectOption[]>([]);
|
||||
const [isLoadingAreaData, setIsLoadingAreaData] = useState<boolean>(isLoading || false);
|
||||
const [atflId, setAtflId] = useState('')
|
||||
const [fileNm, setFileNm] = useState('')
|
||||
const [dcrpPplMtncBizSn, setDcrpPplMtncBizSn] = useState('')
|
||||
const [processingZoneOptions, setProcessingZoneOptions] = useState<ApiSelectOption[]>([]);
|
||||
const [treatmentFacilityOptions, setTreatmentFacilityOptions] = useState<ApiSelectOption[]>([]);
|
||||
const [isLoadingAreaData, setIsLoadingAreaData] = useState<boolean>(isLoading || false);
|
||||
const [atflId, setAtflId] = useState('')
|
||||
const [fileNm, setFileNm] = useState('')
|
||||
const [dcrpPplMtncBizSn, setDcrpPplMtncBizSn] = useState('')
|
||||
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
if (!sigungu || sigungu.val === '0' || !sigungu.val) {
|
||||
console.log("BaseLayout: Skipping fetchData because sigungu.val is:", sigungu?.val);
|
||||
setProcessingZoneOptions([defaultOption]);
|
||||
setTreatmentFacilityOptions([defaultOption]);
|
||||
setIsLoadingAreaData(false);
|
||||
return;
|
||||
}
|
||||
useEffect(() => {
|
||||
const fetchData = async () => {
|
||||
if (!sigungu || sigungu.val === '0' || !sigungu.val) {
|
||||
console.log("BaseLayout: Skipping fetchData because sigungu.val is:", sigungu?.val);
|
||||
setProcessingZoneOptions([defaultOption]);
|
||||
setTreatmentFacilityOptions([defaultOption]);
|
||||
setIsLoadingAreaData(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setIsLoadingAreaData(true);
|
||||
try {
|
||||
const queryParamsZone = new URLSearchParams({
|
||||
lyrTypeNm: 'tb_ms_swage_prcs_zone',
|
||||
page: '0',
|
||||
size: '100000',
|
||||
sort: 'string',
|
||||
});
|
||||
const cqlFiltersZone: string[] = [];
|
||||
if (sigungu.val && sigungu.val !== '0') {
|
||||
// console.log("시군구 코드", sigungu.val);
|
||||
cqlFiltersZone.push(`sgg_cd='${sigungu.val}'`);
|
||||
}
|
||||
if (cqlFiltersZone.length > 0) {
|
||||
queryParamsZone.append('cqlFiter', cqlFiltersZone.join(' and '));
|
||||
}
|
||||
|
||||
const queryParamsFacility = new URLSearchParams({
|
||||
lyrTypeNm: 'tb_il_swage_prcs_fclt',
|
||||
page: '0',
|
||||
size: '100000', // 요청하신 URL에 맞게 50으로 변경 가능
|
||||
sort: 'string',
|
||||
});
|
||||
const cqlFiltersFacility: string[] = [];
|
||||
if (sigungu.text && sigungu.val !== '0') {
|
||||
cqlFiltersFacility.push(`sgg_nm ILIKE '%${sigungu.text}%'`);
|
||||
}
|
||||
if (cqlFiltersFacility.length > 0) {
|
||||
queryParamsFacility.append('cqlFiter', cqlFiltersFacility.join(' and ')); // API 명세에 따라 cqlFilter 또는 cqlFiter
|
||||
}
|
||||
|
||||
console.log("BaseLayout API URL for Zones:", `v1/fclts?${queryParamsZone.toString()}`);
|
||||
console.log("BaseLayout API URL for Facilities:", `v1/fclts?${queryParamsFacility.toString()}`);
|
||||
|
||||
const [zoneRes, facilityRes] = await Promise.all([
|
||||
getData(`v1/fclts?${queryParamsZone.toString()}`),
|
||||
getData(`v1/fclts?${queryParamsFacility.toString()}`)
|
||||
]);
|
||||
|
||||
const zones = (zoneRes.data || []).map((item: any) => ({
|
||||
text: item.swtrmtZoneNm || item.name || `구역 ${item.id || item.gid || item.objectid}`,
|
||||
val: item.fid || item.id || item.gid || item.objectid
|
||||
}));
|
||||
setProcessingZoneOptions([defaultOption, ...zones]);
|
||||
console.log("BaseLayout Fetched Processing Zones:", [defaultOption, ...zones]);
|
||||
|
||||
|
||||
const facilities = (facilityRes.data || []).map((item: any) => ({
|
||||
text: item.swtrmtFcltNm || item.name || `시설 ${item.id || item.gid || item.objectid}`,
|
||||
val: item.fid || item.id || item.gid || item.objectid
|
||||
}));
|
||||
setTreatmentFacilityOptions([defaultOption, ...facilities]);
|
||||
console.log("BaseLayout Fetched Treatment Facilities:", [defaultOption, ...facilities]);
|
||||
|
||||
} catch (error) {
|
||||
console.error("BaseLayout: 지역/시설 데이터 조회 중 오류 발생:", error);
|
||||
setProcessingZoneOptions([defaultOption]);
|
||||
setTreatmentFacilityOptions([defaultOption]);
|
||||
} finally {
|
||||
setIsLoadingAreaData(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, [sigungu]); // sigungu 상태가 변경될 때마다 실행
|
||||
|
||||
// 파일 다운로드
|
||||
const isChckFile = () => {
|
||||
// Excel 다운로드 함수가 있으면 Excel 다운로드 실행
|
||||
if (onExcelDownload) {
|
||||
onExcelDownload();
|
||||
return;
|
||||
setIsLoadingAreaData(true);
|
||||
try {
|
||||
const queryParamsZone = new URLSearchParams({
|
||||
lyrTypeNm: 'tb_ms_swage_prcs_zone',
|
||||
page: '0',
|
||||
size: '100000',
|
||||
sort: 'string',
|
||||
});
|
||||
const cqlFiltersZone: string[] = [];
|
||||
if (sigungu.val && sigungu.val !== '0') {
|
||||
// console.log("시군구 코드", sigungu.val);
|
||||
cqlFiltersZone.push(`sgg_cd='${sigungu.val}'`);
|
||||
}
|
||||
return true
|
||||
}
|
||||
// 파일 삭제
|
||||
const isDeleteFile = async () => {
|
||||
// Excel 다운로드 함수가 있으면 Excel 다운로드 실행
|
||||
if (onDeleteFile) {
|
||||
onDeleteFile();
|
||||
return;
|
||||
if (cqlFiltersZone.length > 0) {
|
||||
queryParamsZone.append('cqlFiter', cqlFiltersZone.join(' and '));
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Form {...form} className="min-h-screen flex flex-col">
|
||||
<LoadingOverlay isLoading={isLoading ?? false} />
|
||||
<ListDiv>
|
||||
{/* searchBox.title이 있으면 h3 태그로 제목을 표시합니다. */}
|
||||
{selectBox?.title && (
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium">{selectBox.title}</h3>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center w-full">
|
||||
{showDatePicker && ( // showDatePicker 값에 따라 DatePicker 렌더링
|
||||
<>
|
||||
<Label className="ml-4 w-20 text-xl text-gray-600 whitespace-nowrap">분석기간</Label>
|
||||
<DatePicker
|
||||
mode={"single"}
|
||||
field={{
|
||||
value: aplcnYm ?? new Date(),
|
||||
onChange: setAplcnYm ?? ((date: Date | undefined) => console.log("DatePicker onChange:", date))
|
||||
}}
|
||||
selectClass="ml-5 date-picker-trigger" />
|
||||
</>
|
||||
)}
|
||||
<GovSidoSigunguSearchForm
|
||||
isShowPart={true}
|
||||
setRtnEnvPartValue={(val) => setEnoCd?.(val)}
|
||||
setRtnEnvPartSidoValue={(val) => setCtpvCd?.(val)}
|
||||
setRtnEnvPartSigunValue={setSigungu} // 선택된 시군구 값을 받을 콜백 함수 전달
|
||||
>
|
||||
<SelectBox2 label={"하수처리장"} value={selectedSwageTreatmentPlant} selectArray={treatmentFacilityOptions} className="col-span-3" selectClass="rounded-lg bg-white truncate" onSelectValue={(val) => onSwageTreatmentPlantChange?.(val)} />
|
||||
{/* <SelectBox2 label={"하수처리구역"} value={selectedSwageProcessingZone} selectArray={processingZoneOptions} className="col-span-3" selectClass="rounded-lg bg-white truncate" onSelectValue={(val) => onSwageProcessingZoneChange?.(val)} /> */}
|
||||
</GovSidoSigunguSearchForm>
|
||||
{selectBox?.searchChk ? <SearchFormRight><Button size="sm" onClick={() => selectBox.onSearchClick?.()}>조회</Button></SearchFormRight> : null}
|
||||
</div>
|
||||
</ListDiv>
|
||||
<ListDiv>
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium">{titleName.firstCardTitle}</h3>
|
||||
<div>
|
||||
{graphBtn ? <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" onClick={isChckFile}>파일 다운로드</Button> : null}
|
||||
{/* 그래프 있을 시 */}
|
||||
{graphBtn ? <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={isDeleteFile}>삭제</Button> : null}
|
||||
{/* 분석 버튼 */}
|
||||
{analysisBtn ? <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={onAnalysis}>{analysisBtn}</Button> : null}
|
||||
</div>
|
||||
</div>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmitEvent)} className="flex-grow flex flex-col">
|
||||
<div className="card !m-[1.2rem] flex-grow flex flex-col">
|
||||
{/* 폼 정보 입력 칸 */}
|
||||
{children?.[0]}
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
</ListDiv>
|
||||
{
|
||||
children?.[1] != null ? <ListDiv>
|
||||
<div className="card-header flex items-center justify-between">
|
||||
<h3 className="text-2xl font-medium">{titleName?.secondCardTitle}</h3>
|
||||
<div>
|
||||
{graphBtn ? <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> : null}
|
||||
{/* 그래프 있을 시 */}
|
||||
{graphBtn ? <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> : null}
|
||||
</div>
|
||||
</div>
|
||||
<div className="card !m-[1.2rem]">{children?.[1]}</div>
|
||||
</ListDiv> : null
|
||||
}
|
||||
const queryParamsFacility = new URLSearchParams({
|
||||
lyrTypeNm: 'tb_il_swage_prcs_fclt',
|
||||
page: '0',
|
||||
size: '100000', // 요청하신 URL에 맞게 50으로 변경 가능
|
||||
sort: 'string',
|
||||
});
|
||||
const cqlFiltersFacility: string[] = [];
|
||||
if (sigungu.text && sigungu.val !== '0') {
|
||||
cqlFiltersFacility.push(`sgg_nm ILIKE '%${sigungu.text}%'`);
|
||||
}
|
||||
if (cqlFiltersFacility.length > 0) {
|
||||
queryParamsFacility.append('cqlFiter', cqlFiltersFacility.join(' and ')); // API 명세에 따라 cqlFilter 또는 cqlFiter
|
||||
}
|
||||
|
||||
console.log("BaseLayout API URL for Zones:", `v1/fclts?${queryParamsZone.toString()}`);
|
||||
console.log("BaseLayout API URL for Facilities:", `v1/fclts?${queryParamsFacility.toString()}`);
|
||||
|
||||
const [zoneRes, facilityRes] = await Promise.all([
|
||||
getData(`v1/fclts?${queryParamsZone.toString()}`),
|
||||
getData(`v1/fclts?${queryParamsFacility.toString()}`)
|
||||
]);
|
||||
|
||||
const zones = (zoneRes.data || []).map((item: any) => ({
|
||||
text: item.swtrmtZoneNm || item.name || `구역 ${item.id || item.gid || item.objectid}`,
|
||||
val: item.fid || item.id || item.gid || item.objectid
|
||||
}));
|
||||
setProcessingZoneOptions([defaultOption, ...zones]);
|
||||
console.log("BaseLayout Fetched Processing Zones:", [defaultOption, ...zones]);
|
||||
|
||||
|
||||
const facilities = (facilityRes.data || []).map((item: any) => ({
|
||||
text: item.swtrmtFcltNm || item.name || `시설 ${item.id || item.gid || item.objectid}`,
|
||||
val: item.fid || item.id || item.gid || item.objectid
|
||||
}));
|
||||
setTreatmentFacilityOptions([defaultOption, ...facilities]);
|
||||
console.log("BaseLayout Fetched Treatment Facilities:", [defaultOption, ...facilities]);
|
||||
|
||||
} catch (error) {
|
||||
console.error("BaseLayout: 지역/시설 데이터 조회 중 오류 발생:", error);
|
||||
setProcessingZoneOptions([defaultOption]);
|
||||
setTreatmentFacilityOptions([defaultOption]);
|
||||
} finally {
|
||||
setIsLoadingAreaData(false);
|
||||
}
|
||||
};
|
||||
|
||||
fetchData();
|
||||
}, [sigungu]); // sigungu 상태가 변경될 때마다 실행
|
||||
|
||||
// 파일 다운로드
|
||||
const isChckFile = () => {
|
||||
// Excel 다운로드 함수가 있으면 Excel 다운로드 실행
|
||||
if (onExcelDownload) {
|
||||
onExcelDownload();
|
||||
return;
|
||||
}
|
||||
return true
|
||||
}
|
||||
// 파일 삭제
|
||||
const isDeleteFile = async () => {
|
||||
// Excel 다운로드 함수가 있으면 Excel 다운로드 실행
|
||||
if (onDeleteFile) {
|
||||
onDeleteFile();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<Form {...form} className="min-h-screen flex flex-col">
|
||||
<LoadingOverlay isLoading={isLoading ?? false} />
|
||||
<ListDiv>
|
||||
{/* searchBox.title이 있으면 h3 태그로 제목을 표시합니다. */}
|
||||
{selectBox?.title && (
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium">{selectBox.title}</h3>
|
||||
</div>
|
||||
)}
|
||||
<div className="flex items-center w-full">
|
||||
{showDatePicker && ( // showDatePicker 값에 따라 DatePicker 렌더링
|
||||
<>
|
||||
<Label className="ml-4 w-20 text-xl text-gray-600 whitespace-nowrap">분석기간</Label>
|
||||
<DatePicker
|
||||
mode={"single"}
|
||||
field={{
|
||||
value: aplcnYm ?? new Date(),
|
||||
onChange: setAplcnYm ?? ((date: Date | undefined) => console.log("DatePicker onChange:", date))
|
||||
}}
|
||||
selectClass="ml-5 date-picker-trigger" />
|
||||
</>
|
||||
)}
|
||||
<GovSidoSigunguSearchForm
|
||||
isShowPart={true}
|
||||
setRtnEnvPartValue={(val) => setEnoCd?.(val)}
|
||||
setRtnEnvPartSidoValue={(val) => setCtpvCd?.(val)}
|
||||
setRtnEnvPartSigunValue={setSigungu} // 선택된 시군구 값을 받을 콜백 함수 전달
|
||||
>
|
||||
<SelectBox2 label={"하수처리장"} value={selectedSwageTreatmentPlant} selectArray={treatmentFacilityOptions} className="col-span-3" selectClass="rounded-lg bg-white truncate" onSelectValue={(val) => onSwageTreatmentPlantChange?.(val)} />
|
||||
{/* <SelectBox2 label={"하수처리구역"} value={selectedSwageProcessingZone} selectArray={processingZoneOptions} className="col-span-3" selectClass="rounded-lg bg-white truncate" onSelectValue={(val) => onSwageProcessingZoneChange?.(val)} /> */}
|
||||
</GovSidoSigunguSearchForm>
|
||||
{selectBox?.searchChk ? <SearchFormRight><Button size="sm" onClick={() => selectBox.onSearchClick?.()}>조회</Button></SearchFormRight> : null}
|
||||
</div>
|
||||
</ListDiv>
|
||||
<ListDiv>
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl font-medium">{titleName.firstCardTitle}</h3>
|
||||
<div>
|
||||
{graphBtn ? <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" onClick={isChckFile}>파일 다운로드</Button> : null}
|
||||
{/* 그래프 있을 시 */}
|
||||
{graphBtn ? <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={isDeleteFile}>삭제</Button> : null}
|
||||
{/* 분석 버튼 */}
|
||||
{analysisBtn ? <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={onAnalysis}>{analysisBtn}</Button> : null}
|
||||
</div>
|
||||
</div>
|
||||
<Form {...form}>
|
||||
<form onSubmit={form.handleSubmit(onSubmitEvent)} className="flex-grow flex flex-col">
|
||||
<div className="card !m-[1.2rem] flex-grow flex flex-col">
|
||||
{/* 폼 정보 입력 칸 */}
|
||||
{children?.[0]}
|
||||
</div>
|
||||
</form>
|
||||
</Form>
|
||||
)
|
||||
</ListDiv>
|
||||
{
|
||||
children?.[1] != null ? <ListDiv>
|
||||
<div className="card-header flex items-center justify-between">
|
||||
<h3 className="text-2xl font-medium">{titleName?.secondCardTitle}</h3>
|
||||
<div>
|
||||
{graphBtn ? <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> : null}
|
||||
{/* 그래프 있을 시 */}
|
||||
{graphBtn ? <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> : null}
|
||||
</div>
|
||||
</div>
|
||||
<div className="card !m-[1.2rem]">{children?.[1]}</div>
|
||||
</ListDiv> : null
|
||||
}
|
||||
</Form>
|
||||
)
|
||||
}
|
||||
|
||||
export default BaseLayout
|
||||
@@ -13,358 +13,358 @@ import { excelToFlowRateData, excelToMatchingResultData, excelToTimePatternData
|
||||
import BaseLayout from '../BaseLayout'
|
||||
|
||||
const formSchema = z.object({
|
||||
managenm: z.string(),
|
||||
region: z.string(),
|
||||
suyeonga: z.number(),
|
||||
inputdate: z.coerce.date(),
|
||||
rtkSource: z.string(),
|
||||
suyeongaPlace: z.string(),
|
||||
pattern: z.string()
|
||||
managenm: z.string(),
|
||||
region: z.string(),
|
||||
suyeonga: z.number(),
|
||||
inputdate: z.coerce.date(),
|
||||
rtkSource: z.string(),
|
||||
suyeongaPlace: z.string(),
|
||||
pattern: z.string()
|
||||
})
|
||||
|
||||
// 각 파일 업로드 폼의 데이터를 저장할 인터페이스 정의
|
||||
interface UploadedFileInfo {
|
||||
file: File | null;
|
||||
atflCd: string | null;
|
||||
atflId: string | null;
|
||||
// 필요하다면 파일 이름 등 추가 정보 저장 가능
|
||||
// fileName: string;
|
||||
file: File | null;
|
||||
atflCd: string | null;
|
||||
atflId: string | null;
|
||||
// 필요하다면 파일 이름 등 추가 정보 저장 가능
|
||||
// fileName: string;
|
||||
}
|
||||
// 초기 파일 정보 상태
|
||||
const initialFileInfo: UploadedFileInfo = { file: null, atflCd: null, atflId: null };
|
||||
|
||||
interface ApiSelectOption {
|
||||
text: string;
|
||||
val: string;
|
||||
text: string;
|
||||
val: string;
|
||||
}
|
||||
|
||||
const IrSetting = () => {
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
managenm: "",
|
||||
region: "",
|
||||
suyeonga: 0,
|
||||
inputdate: new Date(),
|
||||
rtkSource: "",
|
||||
suyeongaPlace: "",
|
||||
pattern: ""
|
||||
}
|
||||
})
|
||||
const form = useForm<z.infer<typeof formSchema>>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
managenm: "",
|
||||
region: "",
|
||||
suyeonga: 0,
|
||||
inputdate: new Date(),
|
||||
rtkSource: "",
|
||||
suyeongaPlace: "",
|
||||
pattern: ""
|
||||
}
|
||||
})
|
||||
|
||||
const [uploadedFilesData, setUploadedFilesData] = useState<UploadedFileInfo[]>([
|
||||
initialFileInfo, // 0: 오수관망도
|
||||
initialFileInfo, // 1: 수용가-맨홀 매칭결과
|
||||
initialFileInfo, // 2: 오수량 시간패턴
|
||||
initialFileInfo // 3: 유량자료 등록
|
||||
const [uploadedFilesData, setUploadedFilesData] = useState<UploadedFileInfo[]>([
|
||||
initialFileInfo, // 0: 오수관망도
|
||||
initialFileInfo, // 1: 수용가-맨홀 매칭결과
|
||||
initialFileInfo, // 2: 오수량 시간패턴
|
||||
initialFileInfo // 3: 유량자료 등록
|
||||
]);
|
||||
|
||||
const [selectedDate, setSelectedDate] = useState(new Date());
|
||||
const [input1, setInput1] = useState('');
|
||||
const [input2, setInput2] = useState('');
|
||||
const [select1, setSelect1] = useState('0'); // 환경청코드
|
||||
const [select2, setSelect2] = useState('0'); // 시도코드
|
||||
const [select3, setSelect3] = useState<ApiSelectOption>({ val: '0', text: '전체' }); // 시군구 상태를 객체로 변경
|
||||
const [select4, setSelect4] = useState<string | number>('0'); // 하수처리장코드
|
||||
const [select5, setSelect5] = useState<string | number>('0'); // 하수처리구역코드
|
||||
const [formResetKey, setFormResetKey] = useState(0); // FileUploadForm 리셋을 위한 key 상태
|
||||
const [isLoadingResult, setIsLoadingResult] = useState<boolean>(false); // 로딩 상태
|
||||
|
||||
// 폼 초기화 함수
|
||||
const resetAllForms = () => {
|
||||
form.reset({ // react-hook-form 초기화
|
||||
managenm: "",
|
||||
region: "",
|
||||
suyeonga: 0,
|
||||
inputdate: new Date(),
|
||||
rtkSource: "",
|
||||
suyeongaPlace: "",
|
||||
pattern: ""
|
||||
});
|
||||
setUploadedFilesData([ // 파일 업로드 상태 초기화
|
||||
initialFileInfo,
|
||||
initialFileInfo,
|
||||
initialFileInfo,
|
||||
initialFileInfo
|
||||
]);
|
||||
setSelectedDate(new Date()); // 날짜 선택 초기화
|
||||
setInput1(''); // 입력 필드 초기화
|
||||
setInput2(''); // 입력 필드 초기화
|
||||
setSelect1('0'); // 선택 필드 초기화
|
||||
setSelect2('0');
|
||||
setSelect3({ val: '0', text: '전체' });
|
||||
setSelect4(0);
|
||||
setSelect5(0);
|
||||
setFormResetKey(prevKey => prevKey + 1); // FileUploadForm 재마운트를 위해 key 변경
|
||||
};
|
||||
|
||||
const [selectedDate, setSelectedDate] = useState(new Date());
|
||||
const [input1, setInput1] = useState('');
|
||||
const [input2, setInput2] = useState('');
|
||||
const [select1, setSelect1] = useState('0'); // 환경청코드
|
||||
const [select2, setSelect2] = useState('0'); // 시도코드
|
||||
const [select3, setSelect3] = useState<ApiSelectOption>({ val: '0', text: '전체' }); // 시군구 상태를 객체로 변경
|
||||
const [select4, setSelect4] = useState<string | number>('0'); // 하수처리장코드
|
||||
const [select5, setSelect5] = useState<string | number>('0'); // 하수처리구역코드
|
||||
const [formResetKey, setFormResetKey] = useState(0); // FileUploadForm 리셋을 위한 key 상태
|
||||
const [isLoadingResult, setIsLoadingResult] = useState<boolean>(false); // 로딩 상태
|
||||
|
||||
// 폼 초기화 함수
|
||||
const resetAllForms = () => {
|
||||
form.reset({ // react-hook-form 초기화
|
||||
managenm: "",
|
||||
region: "",
|
||||
suyeonga: 0,
|
||||
inputdate: new Date(),
|
||||
rtkSource: "",
|
||||
suyeongaPlace: "",
|
||||
pattern: ""
|
||||
});
|
||||
setUploadedFilesData([ // 파일 업로드 상태 초기화
|
||||
initialFileInfo,
|
||||
initialFileInfo,
|
||||
initialFileInfo,
|
||||
initialFileInfo
|
||||
]);
|
||||
setSelectedDate(new Date()); // 날짜 선택 초기화
|
||||
setInput1(''); // 입력 필드 초기화
|
||||
setInput2(''); // 입력 필드 초기화
|
||||
setSelect1('0'); // 선택 필드 초기화
|
||||
setSelect2('0');
|
||||
setSelect3({ val: '0', text: '전체' });
|
||||
setSelect4(0);
|
||||
setSelect5(0);
|
||||
setFormResetKey(prevKey => prevKey + 1); // FileUploadForm 재마운트를 위해 key 변경
|
||||
};
|
||||
|
||||
// FileUploadForm의 onFileSelect prop으로 전달될 콜백 함수
|
||||
const handleFileSelection = (
|
||||
index: number, // FileUploadForm의 fileIdx 값
|
||||
selectedFile: File | null,
|
||||
cd: string | null,
|
||||
id: string | null
|
||||
) => {
|
||||
setUploadedFilesData(prevData => {
|
||||
const newData = [...prevData];
|
||||
newData[index] = {
|
||||
file: selectedFile,
|
||||
atflCd: cd,
|
||||
atflId: id,
|
||||
};
|
||||
return newData;
|
||||
});
|
||||
console.log(`Page: File data received for form index ${index}`, { fileName: selectedFile?.name, cd, id });
|
||||
};
|
||||
// FileUploadForm의 onFileSelect prop으로 전달될 콜백 함수
|
||||
const handleFileSelection = (
|
||||
index: number, // FileUploadForm의 fileIdx 값
|
||||
selectedFile: File | null,
|
||||
cd: string | null,
|
||||
id: string | null
|
||||
) => {
|
||||
setUploadedFilesData(prevData => {
|
||||
const newData = [...prevData];
|
||||
newData[index] = {
|
||||
file: selectedFile,
|
||||
atflCd: cd,
|
||||
atflId: id,
|
||||
};
|
||||
return newData;
|
||||
});
|
||||
console.log(`Page: File data received for form index ${index}`, { fileName: selectedFile?.name, cd, id });
|
||||
};
|
||||
|
||||
|
||||
const registFile = async (analysisData: any) => {
|
||||
setIsLoadingResult(true); // 로딩 상태 시작
|
||||
console.log("Submitting combined analysis data to backend:", analysisData);
|
||||
// 추후 API 엔드포인트에 맞게 URL 수정 필요
|
||||
const data = await postData('v1/inftwtAnls', analysisData);
|
||||
console.log("Response from backend:", data);
|
||||
if (data) {
|
||||
setIsLoadingResult(false); // 로딩 상태 시작
|
||||
alert("분석 데이터가 성공적으로 제출되었습니다.");
|
||||
resetAllForms(); // 성공 시 폼 초기화 함수 호출
|
||||
} else {
|
||||
setIsLoadingResult(false); // 로딩 상태 시작
|
||||
alert("분석 데이터 제출 중 오류가 발생했습니다.");
|
||||
}
|
||||
};
|
||||
const registFile = async (analysisData: any) => {
|
||||
setIsLoadingResult(true); // 로딩 상태 시작
|
||||
console.log("Submitting combined analysis data to backend:", analysisData);
|
||||
// 추후 API 엔드포인트에 맞게 URL 수정 필요
|
||||
const data = await postData('v1/inftwtAnls', analysisData);
|
||||
console.log("Response from backend:", data);
|
||||
if (data) {
|
||||
setIsLoadingResult(false); // 로딩 상태 시작
|
||||
alert("분석 데이터가 성공적으로 제출되었습니다.");
|
||||
resetAllForms(); // 성공 시 폼 초기화 함수 호출
|
||||
} else {
|
||||
setIsLoadingResult(false); // 로딩 상태 시작
|
||||
alert("분석 데이터 제출 중 오류가 발생했습니다.");
|
||||
}
|
||||
};
|
||||
|
||||
// 침입수 분석 실행 버튼 클릭 핸들러
|
||||
const handleAnalysisClick = async () => {
|
||||
setIsLoadingResult(true); // 로딩 상태 시작
|
||||
console.log(uploadedFilesData);
|
||||
const file1 = uploadedFilesData[0]; // 오수관망도
|
||||
const file2 = uploadedFilesData[1]; // 수용가-맨홀 매칭결과
|
||||
const file3 = uploadedFilesData[2]; // 오수량 시간패턴
|
||||
const file4 = uploadedFilesData[3]; // 유량자료
|
||||
// 침입수 분석 실행 버튼 클릭 핸들러
|
||||
const handleAnalysisClick = async () => {
|
||||
setIsLoadingResult(true); // 로딩 상태 시작
|
||||
console.log(uploadedFilesData);
|
||||
const file1 = uploadedFilesData[0]; // 오수관망도
|
||||
const file2 = uploadedFilesData[1]; // 수용가-맨홀 매칭결과
|
||||
const file3 = uploadedFilesData[2]; // 오수량 시간패턴
|
||||
const file4 = uploadedFilesData[3]; // 유량자료
|
||||
|
||||
console.log("오수관망도 정보:", file1?.file);
|
||||
console.log("수용가-맨홀 매칭결과 정보:", file2);
|
||||
console.log("오수량 시간패턴 정보:", file3);
|
||||
console.log("유량자료 정보:", file4);
|
||||
console.log("오수관망도 정보:", file1?.file);
|
||||
console.log("수용가-맨홀 매칭결과 정보:", file2);
|
||||
console.log("오수량 시간패턴 정보:", file3);
|
||||
console.log("유량자료 정보:", file4);
|
||||
|
||||
// 모든 필수 파일이 선택되었는지 확인 (UploadedFileInfo 객체와 그 안의 file 객체 모두 확인)
|
||||
// 유량자료는 선택값
|
||||
if (!file1?.file || !file2?.file || !file3?.file) {
|
||||
setIsLoadingResult(false);
|
||||
alert('모든 분석용 파일을 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
type MatchingResult = { anlsSn: number; mhlMngNo: string; rcptrNo: string; };
|
||||
type TimePattern = { hrId: number; rt?: number | null | undefined; };
|
||||
type FlowRate = { pplMngNo: string; mtrngDt: string; oilq?: number | null | undefined; };
|
||||
|
||||
const filePickers: {
|
||||
file: UploadedFileInfo;
|
||||
parser: (excelData: any) => MatchingResult[] | TimePattern[] | FlowRate[];
|
||||
}[] = [
|
||||
{ file: file2, parser: excelToMatchingResultData },
|
||||
{ file: file3, parser: excelToTimePatternData }
|
||||
];
|
||||
|
||||
if (file4?.file) {
|
||||
filePickers.push({ file: file4, parser: excelToFlowRateData });
|
||||
}
|
||||
|
||||
try {
|
||||
const parsedResults = await Promise.all(
|
||||
filePickers.map(async ({ file, parser }) => {
|
||||
if (!file || !file.file) return null;
|
||||
|
||||
const jsonData = await filePicked(file.file, 0);
|
||||
|
||||
if (jsonData === undefined) {
|
||||
console.warn(`Warning: filePicked returned undefined for ${file.file.name}`);
|
||||
return {
|
||||
fileName: file.file.name,
|
||||
atflCd: file.atflCd,
|
||||
atflId: file.atflId,
|
||||
parsedData: null
|
||||
};
|
||||
}
|
||||
|
||||
// jsonData를 각 parser 함수로 처리
|
||||
const parsedDataBySpecificFunction = parser(jsonData);
|
||||
|
||||
return {
|
||||
fileName: file.file.name,
|
||||
atflCd: file.atflCd,
|
||||
atflId: file.atflId,
|
||||
parsedData: parsedDataBySpecificFunction
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
// 각 파싱 결과 접근
|
||||
const matchingResultData = parsedResults.find(r => r && r.fileName === file2.file?.name)?.parsedData;
|
||||
const timePatternData = parsedResults.find(r => r && r.fileName === file3.file?.name)?.parsedData;
|
||||
const flowRateData = file4?.file
|
||||
? parsedResults.find(r => r && r.fileName === file4.file?.name)?.parsedData
|
||||
: null;
|
||||
|
||||
// 새로운 JSON 데이터 생성
|
||||
const combinedAnalysisData = {
|
||||
enoCd: (select1 && select1 !== '0') ? select1.toString() : null, // 환경청코드 (선택값)
|
||||
ctpvCd: (select2 && select2 !== '0') ? select2.toString() : null, // 시도코드 (선택값)
|
||||
sggCd: (select3.val && select3.val !== '0') ? select3.val.toString() : null, // 시군구코드 (선택값)
|
||||
swageTmplntCd: (select4 && select4 !== '0' && select4 !== 0)
|
||||
? select4.toString().split('.').pop() || null // '.'으로 split해서 마지막 부분만 사용
|
||||
: null, // 하수처리장코드 (선택값)
|
||||
swtrmtZoneCd: (select5 && select5 !== '0' && select5 !== 0)
|
||||
? select5.toString().split('.').pop() || null // '.'으로 split해서 마지막 부분만 사용
|
||||
: null, // 하수처리구역코드 (선택값)
|
||||
aplcnYm: new Date(selectedDate).toISOString().substring(0, 7).replace("-", ""), // 적용연월
|
||||
drtwSwtcRt: parseFloat(input1) || null, // 오수전환율 (숫자로 변환)
|
||||
anlsInfo: input2, // 분석정보
|
||||
atflCd: file1?.atflCd, // 첨부파일CD (오수관망도)
|
||||
atflId: file1?.atflId, // 첨부파일ID (오수관망도)
|
||||
atflInpNm: file1?.file?.name, // 오수관망도 파일명
|
||||
atflHrPttnNm: file3?.file?.name, // 오수량 시간패턴 파일명
|
||||
atflMpngNm: file2?.file?.name, // 수용가-맨홀 매칭결과 파일명
|
||||
atflOilqNm: file4?.file?.name || null, // 유량자료 파일명 (없을 수 있음)
|
||||
tbSnRcptrMhlMpng: matchingResultData, // 수용가-맨홀 매칭결과
|
||||
tbSnHrPttn: timePatternData, // 오수량 시간패턴
|
||||
tbSnOilqData: flowRateData, // 유량자료 (없을 수 있음)
|
||||
};
|
||||
|
||||
console.log("통합 분석 데이터 (JSON):", combinedAnalysisData);
|
||||
|
||||
// 수정된 registFile 함수를 호출하여 combinedAnalysisData를 전송합니다.
|
||||
await registFile(combinedAnalysisData);
|
||||
setIsLoadingResult(false);
|
||||
} catch (error) {
|
||||
setIsLoadingResult(false);
|
||||
console.error("파일 파싱 중 오류 발생:", error);
|
||||
alert("파일 처리 중 오류가 발생했습니다.");
|
||||
return;
|
||||
}
|
||||
// 모든 필수 파일이 선택되었는지 확인 (UploadedFileInfo 객체와 그 안의 file 객체 모두 확인)
|
||||
// 유량자료는 선택값
|
||||
if (!file1?.file || !file2?.file || !file3?.file) {
|
||||
setIsLoadingResult(false);
|
||||
alert('모든 분석용 파일을 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<BaseLayout
|
||||
titleName={{ mainTitle: '침입수 분석', firstCardTitle: '침입수 분석 자료 입력' }}
|
||||
form={form}
|
||||
onSubmitEvent={() => { }}
|
||||
graphBtn={false}
|
||||
analysisBtn={'침입수분석 실행'}
|
||||
selectBox={{ title: '분석 대상지역 선택' }}
|
||||
enoCd={select1}
|
||||
setEnoCd={setSelect1}
|
||||
ctpvCd={select2}
|
||||
setCtpvCd={setSelect2}
|
||||
sigungu={select3}
|
||||
setSigungu={setSelect3}
|
||||
selectedSwageTreatmentPlant={select4}
|
||||
onSwageTreatmentPlantChange={setSelect4}
|
||||
selectedSwageProcessingZone={select5}
|
||||
onSwageProcessingZoneChange={setSelect5}
|
||||
isLoading={isLoadingResult}
|
||||
onAnalysis={handleAnalysisClick}
|
||||
>
|
||||
{[<div key="irsetting-layout-key" className="grid gap-4">
|
||||
<FormSection name='suyeongaPlace' form={form} titleNm='▷ 오수관망도'>
|
||||
<FileUploadForm
|
||||
key={`file-upload-0-${formResetKey}`} // 폼 초기화를 위한 key
|
||||
onFileSelect={(file, cd, id) => handleFileSelection(0, file, cd, id)}
|
||||
showButton={false}
|
||||
showPreview={true}
|
||||
accept={""}
|
||||
fileIdx={0} />
|
||||
</FormSection>
|
||||
<FormSection name='suyeonga' form={form} titleNm='▷ 수용가-맨홀 매칭결과'>
|
||||
<FileUploadForm
|
||||
key={`file-upload-1-${formResetKey}`} // 폼 초기화를 위한 key
|
||||
onFileSelect={(file, cd, id) => handleFileSelection(1, file, cd, id)}
|
||||
showButton={false}
|
||||
showPreview={true}
|
||||
accept={".xls, .xlsx, .xlsm, .xltx"}
|
||||
fileIdx={1} />
|
||||
</FormSection>
|
||||
<FormSection name='pattern' form={form} titleNm='▷ 오수량 시간패턴'>
|
||||
<FileUploadForm
|
||||
key={`file-upload-2-${formResetKey}`} // 폼 초기화를 위한 key
|
||||
onFileSelect={(file, cd, id) => handleFileSelection(2, file, cd, id)}
|
||||
showButton={false}
|
||||
showPreview={true}
|
||||
accept={".xls, .xlsx, .xlsm, .xltx"}
|
||||
fileIdx={2} />
|
||||
</FormSection>
|
||||
<FormSection
|
||||
name='inputdate'
|
||||
form={form}
|
||||
titleNm='▷ 상수사용량 적용기간'>
|
||||
<DatePicker
|
||||
mode={"single"}
|
||||
field={{
|
||||
value: selectedDate, // 현재 페이지의 selectedDate 상태 사용
|
||||
onChange: (date: Date | undefined) => {
|
||||
if (date) {
|
||||
setSelectedDate(date);
|
||||
form.setValue('inputdate', date, { shouldValidate: true });
|
||||
}
|
||||
}
|
||||
}}
|
||||
selectClass="ml-5 date-picker-trigger" // date-picker.css의 스타일 적용
|
||||
/>
|
||||
</FormSection>
|
||||
<FormSection
|
||||
name='rtkSource'
|
||||
form={form}
|
||||
titleNm='▷ 오수전환율'>
|
||||
<Input
|
||||
value={input1}
|
||||
onChange={(e) => {
|
||||
const newValue = e.target.value;
|
||||
type MatchingResult = { anlsSn: number; mhlMngNo: string; rcptrNo: string; };
|
||||
type TimePattern = { hrId: number; rt?: number | null | undefined; };
|
||||
type FlowRate = { pplMngNo: string; mtrngDt: string; oilq?: number | null | undefined; };
|
||||
|
||||
if (newValue === "") {
|
||||
setInput1(newValue);
|
||||
return;
|
||||
}
|
||||
const filePickers: {
|
||||
file: UploadedFileInfo;
|
||||
parser: (excelData: any) => MatchingResult[] | TimePattern[] | FlowRate[];
|
||||
}[] = [
|
||||
{ file: file2, parser: excelToMatchingResultData },
|
||||
{ file: file3, parser: excelToTimePatternData }
|
||||
];
|
||||
|
||||
// 숫자와 '.'만 허용하고, '.'은 최대 하나인지 확인
|
||||
const isValidChars = /^[0-9]*\.?[0-9]*$/.test(newValue);
|
||||
if (!isValidChars) return; // 유효하지 않은 문자 또는 여러 개의 '.' 포함
|
||||
if (file4?.file) {
|
||||
filePickers.push({ file: file4, parser: excelToFlowRateData });
|
||||
}
|
||||
|
||||
// '.' 기호가 포함된 경우, 처음이나 끝에 오면 안 됨
|
||||
if (newValue.includes('.') && newValue.startsWith('.')) return; // '.'이 처음에는 올 수 없음
|
||||
try {
|
||||
const parsedResults = await Promise.all(
|
||||
filePickers.map(async ({ file, parser }) => {
|
||||
if (!file || !file.file) return null;
|
||||
|
||||
setInput1(newValue);
|
||||
}}
|
||||
className="ml-5 h-[3.2rem] text-center text-xl w-[150px]"
|
||||
placeholder="예: 0.75" />
|
||||
</FormSection>
|
||||
<FormSection
|
||||
name='analysis information'
|
||||
form={form}
|
||||
titleNm='▷ 분석 정보'>
|
||||
<Input
|
||||
value={input2}
|
||||
onChange={(e) => setInput2(e.target.value)}
|
||||
className="ml-5 h-[3.2rem] text-center text-xl w-[150px]" />
|
||||
</FormSection>
|
||||
<ListDiv>
|
||||
<div className="card !m-[1.2rem]">
|
||||
<FormSection name='region' form={form} titleNm='▷ 유량자료 등록'>
|
||||
<FileUploadForm
|
||||
key={`file-upload-3-${formResetKey}`} // 폼 초기화를 위한 key
|
||||
onFileSelect={(file, cd, id) => handleFileSelection(3, file, cd, id)}
|
||||
showButton={false}
|
||||
showPreview={true}
|
||||
accept={".xls, .xlsx, .xlsm, .xltx"}
|
||||
fileIdx={3}
|
||||
/>
|
||||
</FormSection>
|
||||
</div>
|
||||
</ListDiv>
|
||||
</div>]}
|
||||
{/* <div className="flex items-center gap-4">
|
||||
const jsonData = await filePicked(file.file, 0);
|
||||
|
||||
if (jsonData === undefined) {
|
||||
console.warn(`Warning: filePicked returned undefined for ${file.file.name}`);
|
||||
return {
|
||||
fileName: file.file.name,
|
||||
atflCd: file.atflCd,
|
||||
atflId: file.atflId,
|
||||
parsedData: null
|
||||
};
|
||||
}
|
||||
|
||||
// jsonData를 각 parser 함수로 처리
|
||||
const parsedDataBySpecificFunction = parser(jsonData);
|
||||
|
||||
return {
|
||||
fileName: file.file.name,
|
||||
atflCd: file.atflCd,
|
||||
atflId: file.atflId,
|
||||
parsedData: parsedDataBySpecificFunction
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
// 각 파싱 결과 접근
|
||||
const matchingResultData = parsedResults.find(r => r && r.fileName === file2.file?.name)?.parsedData;
|
||||
const timePatternData = parsedResults.find(r => r && r.fileName === file3.file?.name)?.parsedData;
|
||||
const flowRateData = file4?.file
|
||||
? parsedResults.find(r => r && r.fileName === file4.file?.name)?.parsedData
|
||||
: null;
|
||||
|
||||
// 새로운 JSON 데이터 생성
|
||||
const combinedAnalysisData = {
|
||||
enoCd: (select1 && select1 !== '0') ? select1.toString() : null, // 환경청코드 (선택값)
|
||||
ctpvCd: (select2 && select2 !== '0') ? select2.toString() : null, // 시도코드 (선택값)
|
||||
sggCd: (select3.val && select3.val !== '0') ? select3.val.toString() : null, // 시군구코드 (선택값)
|
||||
swageTmplntCd: (select4 && select4 !== '0' && select4 !== 0)
|
||||
? select4.toString().split('.').pop() || null // '.'으로 split해서 마지막 부분만 사용
|
||||
: null, // 하수처리장코드 (선택값)
|
||||
swtrmtZoneCd: (select5 && select5 !== '0' && select5 !== 0)
|
||||
? select5.toString().split('.').pop() || null // '.'으로 split해서 마지막 부분만 사용
|
||||
: null, // 하수처리구역코드 (선택값)
|
||||
aplcnYm: new Date(selectedDate).toISOString().substring(0, 7).replace("-", ""), // 적용연월
|
||||
drtwSwtcRt: parseFloat(input1) || null, // 오수전환율 (숫자로 변환)
|
||||
anlsInfo: input2, // 분석정보
|
||||
atflCd: file1?.atflCd, // 첨부파일CD (오수관망도)
|
||||
atflId: file1?.atflId, // 첨부파일ID (오수관망도)
|
||||
atflInpNm: file1?.file?.name, // 오수관망도 파일명
|
||||
atflHrPttnNm: file3?.file?.name, // 오수량 시간패턴 파일명
|
||||
atflMpngNm: file2?.file?.name, // 수용가-맨홀 매칭결과 파일명
|
||||
atflOilqNm: file4?.file?.name || null, // 유량자료 파일명 (없을 수 있음)
|
||||
tbSnRcptrMhlMpng: matchingResultData, // 수용가-맨홀 매칭결과
|
||||
tbSnHrPttn: timePatternData, // 오수량 시간패턴
|
||||
tbSnOilqData: flowRateData, // 유량자료 (없을 수 있음)
|
||||
};
|
||||
|
||||
console.log("통합 분석 데이터 (JSON):", combinedAnalysisData);
|
||||
|
||||
// 수정된 registFile 함수를 호출하여 combinedAnalysisData를 전송합니다.
|
||||
await registFile(combinedAnalysisData);
|
||||
setIsLoadingResult(false);
|
||||
} catch (error) {
|
||||
setIsLoadingResult(false);
|
||||
console.error("파일 파싱 중 오류 발생:", error);
|
||||
alert("파일 처리 중 오류가 발생했습니다.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<BaseLayout
|
||||
titleName={{ mainTitle: '침입수 분석', firstCardTitle: '침입수 분석 자료 입력' }}
|
||||
form={form}
|
||||
onSubmitEvent={() => { }}
|
||||
graphBtn={false}
|
||||
analysisBtn={'침입수분석 실행'}
|
||||
selectBox={{ title: '분석 대상지역 선택' }}
|
||||
enoCd={select1}
|
||||
setEnoCd={setSelect1}
|
||||
ctpvCd={select2}
|
||||
setCtpvCd={setSelect2}
|
||||
sigungu={select3}
|
||||
setSigungu={setSelect3}
|
||||
selectedSwageTreatmentPlant={select4}
|
||||
onSwageTreatmentPlantChange={setSelect4}
|
||||
selectedSwageProcessingZone={select5}
|
||||
onSwageProcessingZoneChange={setSelect5}
|
||||
isLoading={isLoadingResult}
|
||||
onAnalysis={handleAnalysisClick}
|
||||
>
|
||||
{[<div key="irsetting-layout-key" className="grid gap-4">
|
||||
<FormSection name='suyeongaPlace' form={form} titleNm='▷ 오수관망도'>
|
||||
<FileUploadForm
|
||||
key={`file-upload-0-${formResetKey}`} // 폼 초기화를 위한 key
|
||||
onFileSelect={(file, cd, id) => handleFileSelection(0, file, cd, id)}
|
||||
showButton={false}
|
||||
showPreview={true}
|
||||
accept={""}
|
||||
fileIdx={0} />
|
||||
</FormSection>
|
||||
<FormSection name='suyeonga' form={form} titleNm='▷ 수용가-맨홀 매칭결과'>
|
||||
<FileUploadForm
|
||||
key={`file-upload-1-${formResetKey}`} // 폼 초기화를 위한 key
|
||||
onFileSelect={(file, cd, id) => handleFileSelection(1, file, cd, id)}
|
||||
showButton={false}
|
||||
showPreview={true}
|
||||
accept={".xls, .xlsx, .xlsm, .xltx"}
|
||||
fileIdx={1} />
|
||||
</FormSection>
|
||||
<FormSection name='pattern' form={form} titleNm='▷ 오수량 시간패턴'>
|
||||
<FileUploadForm
|
||||
key={`file-upload-2-${formResetKey}`} // 폼 초기화를 위한 key
|
||||
onFileSelect={(file, cd, id) => handleFileSelection(2, file, cd, id)}
|
||||
showButton={false}
|
||||
showPreview={true}
|
||||
accept={".xls, .xlsx, .xlsm, .xltx"}
|
||||
fileIdx={2} />
|
||||
</FormSection>
|
||||
<FormSection
|
||||
name='inputdate'
|
||||
form={form}
|
||||
titleNm='▷ 상수사용량 적용기간'>
|
||||
<DatePicker
|
||||
mode={"single"}
|
||||
field={{
|
||||
value: selectedDate, // 현재 페이지의 selectedDate 상태 사용
|
||||
onChange: (date: Date | undefined) => {
|
||||
if (date) {
|
||||
setSelectedDate(date);
|
||||
form.setValue('inputdate', date, { shouldValidate: true });
|
||||
}
|
||||
}
|
||||
}}
|
||||
selectClass="ml-5 date-picker-trigger" // date-picker.css의 스타일 적용
|
||||
/>
|
||||
</FormSection>
|
||||
<FormSection
|
||||
name='rtkSource'
|
||||
form={form}
|
||||
titleNm='▷ 오수전환율'>
|
||||
<Input
|
||||
value={input1}
|
||||
onChange={(e) => {
|
||||
const newValue = e.target.value;
|
||||
|
||||
if (newValue === "") {
|
||||
setInput1(newValue);
|
||||
return;
|
||||
}
|
||||
|
||||
// 숫자와 '.'만 허용하고, '.'은 최대 하나인지 확인
|
||||
const isValidChars = /^[0-9]*\.?[0-9]*$/.test(newValue);
|
||||
if (!isValidChars) return; // 유효하지 않은 문자 또는 여러 개의 '.' 포함
|
||||
|
||||
// '.' 기호가 포함된 경우, 처음이나 끝에 오면 안 됨
|
||||
if (newValue.includes('.') && newValue.startsWith('.')) return; // '.'이 처음에는 올 수 없음
|
||||
|
||||
setInput1(newValue);
|
||||
}}
|
||||
className="ml-5 h-[3.2rem] text-center text-xl w-[150px]"
|
||||
placeholder="예: 0.75" />
|
||||
</FormSection>
|
||||
<FormSection
|
||||
name='analysis information'
|
||||
form={form}
|
||||
titleNm='▷ 분석 정보'>
|
||||
<Input
|
||||
value={input2}
|
||||
onChange={(e) => setInput2(e.target.value)}
|
||||
className="ml-5 h-[3.2rem] text-center text-xl w-[150px]" />
|
||||
</FormSection>
|
||||
<ListDiv>
|
||||
<div className="card !m-[1.2rem]">
|
||||
<FormSection name='region' form={form} titleNm='▷ 유량자료 등록'>
|
||||
<FileUploadForm
|
||||
key={`file-upload-3-${formResetKey}`} // 폼 초기화를 위한 key
|
||||
onFileSelect={(file, cd, id) => handleFileSelection(3, file, cd, id)}
|
||||
showButton={false}
|
||||
showPreview={true}
|
||||
accept={".xls, .xlsx, .xlsm, .xltx"}
|
||||
fileIdx={3}
|
||||
/>
|
||||
</FormSection>
|
||||
</div>
|
||||
</ListDiv>
|
||||
</div>]}
|
||||
{/* <div className="flex items-center gap-4">
|
||||
<FormSection
|
||||
name='region' form={form} titleNm='▷ 유량자료 등록'>
|
||||
<FileUploadForm
|
||||
@@ -379,9 +379,9 @@ const IrSetting = () => {
|
||||
<Button size="sm" type="submit" onClick={handleAnalysisClick}>침입수분석 실행</Button>
|
||||
</div>
|
||||
</div> */}
|
||||
</BaseLayout>
|
||||
</>
|
||||
)
|
||||
</BaseLayout>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default IrSetting
|
||||
@@ -17,393 +17,393 @@ import { EditableFormTable, type EditableTableHeadItem } from '../../../componen
|
||||
|
||||
// zod 스키마 정의
|
||||
const formSchema = z.object({
|
||||
enoCd: z.string(), // 환경청코드
|
||||
ctpvCd: z.string(), // 시도코드
|
||||
sggCd: z.string(), // 시군구코드
|
||||
swageTmplntCd: z.string(), // 하수처리장코드
|
||||
swtrmtZoneCd: z.string(), // 하수처리구역코드
|
||||
rtkSource: z.string(), // RTK계수 출처
|
||||
rdiiArea: z.string(), // RDII 기여면적
|
||||
unitFactors: z.array(z.object({ // 단위도 계수
|
||||
response: z.string(),
|
||||
R: z.string(),
|
||||
T: z.string(),
|
||||
K: z.string(),
|
||||
})),
|
||||
initialDepths: z.array(z.object({ // 초기 차단 깊이
|
||||
response: z.string(),
|
||||
Dmax: z.string(),
|
||||
Drec: z.string(),
|
||||
Do: z.string(),
|
||||
})),
|
||||
// osumap: z.instanceof(File).optional(), // 오수관망도
|
||||
// matching: z.instanceof(File).optional(), // 수용가-맨홀 매칭결과
|
||||
aplcnYm: z.string(), // 상수사용량 적용기간 (YYYYMM 형식)
|
||||
rate: z.string(), // 오수전환율
|
||||
// rain: z.instanceof(File).optional(), // 강우자료
|
||||
analysisInfo: z.string().optional(), // 분석 정보
|
||||
date: z.date(),
|
||||
// date: z.coerce.date(),
|
||||
enoCd: z.string(), // 환경청코드
|
||||
ctpvCd: z.string(), // 시도코드
|
||||
sggCd: z.string(), // 시군구코드
|
||||
swageTmplntCd: z.string(), // 하수처리장코드
|
||||
swtrmtZoneCd: z.string(), // 하수처리구역코드
|
||||
rtkSource: z.string(), // RTK계수 출처
|
||||
rdiiArea: z.string(), // RDII 기여면적
|
||||
unitFactors: z.array(z.object({ // 단위도 계수
|
||||
response: z.string(),
|
||||
R: z.string(),
|
||||
T: z.string(),
|
||||
K: z.string(),
|
||||
})),
|
||||
initialDepths: z.array(z.object({ // 초기 차단 깊이
|
||||
response: z.string(),
|
||||
Dmax: z.string(),
|
||||
Drec: z.string(),
|
||||
Do: z.string(),
|
||||
})),
|
||||
// osumap: z.instanceof(File).optional(), // 오수관망도
|
||||
// matching: z.instanceof(File).optional(), // 수용가-맨홀 매칭결과
|
||||
aplcnYm: z.string(), // 상수사용량 적용기간 (YYYYMM 형식)
|
||||
rate: z.string(), // 오수전환율
|
||||
// rain: z.instanceof(File).optional(), // 강우자료
|
||||
analysisInfo: z.string().optional(), // 분석 정보
|
||||
date: z.date(),
|
||||
// date: z.coerce.date(),
|
||||
})
|
||||
// zod 스키마 타입 추론
|
||||
type FormSchemaType = z.infer<typeof formSchema>;
|
||||
// 기본 폼 값 정의 (테스트 편의를 위해 초기값 설정, 추후 삭제 필요)
|
||||
const defaultFormValues: FormSchemaType = {
|
||||
enoCd: "",
|
||||
ctpvCd: "",
|
||||
sggCd: "",
|
||||
swageTmplntCd: "",
|
||||
swtrmtZoneCd: "",
|
||||
rtkSource: "출처",
|
||||
rdiiArea: "123", // 문자열로 기본값 설정
|
||||
unitFactors: [
|
||||
{ response: "Short-Term", R: "0.040", T: "0.5", K: "1" }, // 문자열로 기본값 설정
|
||||
{ response: "Medium-Term", R: "0.005", T: "1", K: "2" },
|
||||
{ response: "Long-Term", R: "0.005", T: "2", K: "3" },
|
||||
],
|
||||
initialDepths: [
|
||||
{ response: "Short-Term", Dmax: "0", Drec: "0", Do: "0" }, // 문자열로 기본값 설정
|
||||
{ response: "Medium-Term", Dmax: "0", Drec: "0", Do: "0" },
|
||||
{ response: "Long-Term", Dmax: "0", Drec: "0", Do: "0" },
|
||||
],
|
||||
// osumap: undefined,
|
||||
// matchingResultFile: undefined,
|
||||
aplcnYm: "",
|
||||
rate: "0.8", // 문자열로 기본값 설정
|
||||
// rainFile: undefined,
|
||||
analysisInfo: "test 분석정보",
|
||||
date: new Date(),
|
||||
enoCd: "",
|
||||
ctpvCd: "",
|
||||
sggCd: "",
|
||||
swageTmplntCd: "",
|
||||
swtrmtZoneCd: "",
|
||||
rtkSource: "출처",
|
||||
rdiiArea: "123", // 문자열로 기본값 설정
|
||||
unitFactors: [
|
||||
{ response: "Short-Term", R: "0.040", T: "0.5", K: "1" }, // 문자열로 기본값 설정
|
||||
{ response: "Medium-Term", R: "0.005", T: "1", K: "2" },
|
||||
{ response: "Long-Term", R: "0.005", T: "2", K: "3" },
|
||||
],
|
||||
initialDepths: [
|
||||
{ response: "Short-Term", Dmax: "0", Drec: "0", Do: "0" }, // 문자열로 기본값 설정
|
||||
{ response: "Medium-Term", Dmax: "0", Drec: "0", Do: "0" },
|
||||
{ response: "Long-Term", Dmax: "0", Drec: "0", Do: "0" },
|
||||
],
|
||||
// osumap: undefined,
|
||||
// matchingResultFile: undefined,
|
||||
aplcnYm: "",
|
||||
rate: "0.8", // 문자열로 기본값 설정
|
||||
// rainFile: undefined,
|
||||
analysisInfo: "test 분석정보",
|
||||
date: new Date(),
|
||||
};
|
||||
|
||||
// 각 파일 업로드 폼의 데이터를 저장할 인터페이스 정의
|
||||
interface UploadedFileInfo {
|
||||
file: File | null;
|
||||
atflCd: string | null;
|
||||
atflId: string | null;
|
||||
file: File | null;
|
||||
atflCd: string | null;
|
||||
atflId: string | null;
|
||||
}
|
||||
|
||||
// 초기 파일 정보 상태
|
||||
const initialFileInfo: UploadedFileInfo = { file: null, atflCd: null, atflId: null };
|
||||
|
||||
interface ApiSelectOption {
|
||||
text: string;
|
||||
val: string;
|
||||
text: string;
|
||||
val: string;
|
||||
}
|
||||
|
||||
export default function RTKForm() {
|
||||
const [formResetKey, setFormResetKey] = useState(0); // FileUploadForm 리셋을 위한 key 상태
|
||||
const [isLoadingResult, setIsLoadingResult] = useState<boolean>(false); // 로딩 상태
|
||||
const [formResetKey, setFormResetKey] = useState(0); // FileUploadForm 리셋을 위한 key 상태
|
||||
const [isLoadingResult, setIsLoadingResult] = useState<boolean>(false); // 로딩 상태
|
||||
|
||||
// FileUploadForm에서 전달받은 파일 정보를 저장할 상태 배열
|
||||
// 각 인덱스는 FileUploadForm의 fileIdx와 매칭됨
|
||||
const [uploadedFilesData, setUploadedFilesData] = useState<UploadedFileInfo[]>([
|
||||
initialFileInfo, // 0: 오수관망도
|
||||
initialFileInfo, // 1: 수용가-맨홀 매칭결과
|
||||
initialFileInfo, // 2: 강우자료
|
||||
// FileUploadForm에서 전달받은 파일 정보를 저장할 상태 배열
|
||||
// 각 인덱스는 FileUploadForm의 fileIdx와 매칭됨
|
||||
const [uploadedFilesData, setUploadedFilesData] = useState<UploadedFileInfo[]>([
|
||||
initialFileInfo, // 0: 오수관망도
|
||||
initialFileInfo, // 1: 수용가-맨홀 매칭결과
|
||||
initialFileInfo, // 2: 강우자료
|
||||
]);
|
||||
|
||||
const [select1, setSelect1] = useState('0'); // 환경청코드 (enoCd)
|
||||
const [select2, setSelect2] = useState('0'); // 시도코드 (ctpvCd)
|
||||
const [select3, setSelect3] = useState<ApiSelectOption>({ val: '0', text: '전체' }); // 시군구 (sggCd)
|
||||
const [select4, setSelect4] = useState<string | number>('0'); // 하수처리장 (swageTmplntCd)
|
||||
const [select5, setSelect5] = useState<string | number>('0'); // 하수처리구역 (swtrmtZoneCd)
|
||||
|
||||
|
||||
// form 인스턴스 생성
|
||||
const form = useForm<FormSchemaType>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: defaultFormValues
|
||||
});
|
||||
|
||||
// 폼 초기화 함수
|
||||
const resetAllForms = () => {
|
||||
form.reset(defaultFormValues); // 폼 값 초기화
|
||||
setUploadedFilesData([ // 파일 업로드 상태 초기화 (3개 파일)
|
||||
initialFileInfo,
|
||||
initialFileInfo,
|
||||
initialFileInfo,
|
||||
]);
|
||||
setFormResetKey(prevKey => prevKey + 1); // FileUploadForm 재마운트를 위해 key 변경
|
||||
setSelect1('0');
|
||||
setSelect2('0');
|
||||
setSelect3({ val: '0', text: '전체' });
|
||||
setSelect4('0');
|
||||
setSelect5('0');
|
||||
};
|
||||
|
||||
const [select1, setSelect1] = useState('0'); // 환경청코드 (enoCd)
|
||||
const [select2, setSelect2] = useState('0'); // 시도코드 (ctpvCd)
|
||||
const [select3, setSelect3] = useState<ApiSelectOption>({ val: '0', text: '전체' }); // 시군구 (sggCd)
|
||||
const [select4, setSelect4] = useState<string | number>('0'); // 하수처리장 (swageTmplntCd)
|
||||
const [select5, setSelect5] = useState<string | number>('0'); // 하수처리구역 (swtrmtZoneCd)
|
||||
|
||||
|
||||
// form 인스턴스 생성
|
||||
const form = useForm<FormSchemaType>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: defaultFormValues
|
||||
// FileUploadForm의 onFileSelect prop으로 전달될 콜백 함수
|
||||
const handleFileSelection = (
|
||||
index: number, // FileUploadForm의 fileIdx 값
|
||||
selectedFile: File | null,
|
||||
cd: string | null,
|
||||
id: string | null
|
||||
) => {
|
||||
setUploadedFilesData(prevData => {
|
||||
const newData = [...prevData];
|
||||
newData[index] = {
|
||||
file: selectedFile,
|
||||
atflCd: cd,
|
||||
atflId: id,
|
||||
};
|
||||
return newData;
|
||||
});
|
||||
console.log(`Page: File data received for form index ${index}`, { fileName: selectedFile?.name, cd, id });
|
||||
};
|
||||
|
||||
// 폼 초기화 함수
|
||||
const resetAllForms = () => {
|
||||
form.reset(defaultFormValues); // 폼 값 초기화
|
||||
setUploadedFilesData([ // 파일 업로드 상태 초기화 (3개 파일)
|
||||
initialFileInfo,
|
||||
initialFileInfo,
|
||||
initialFileInfo,
|
||||
]);
|
||||
setFormResetKey(prevKey => prevKey + 1); // FileUploadForm 재마운트를 위해 key 변경
|
||||
setSelect1('0');
|
||||
setSelect2('0');
|
||||
setSelect3({ val: '0', text: '전체' });
|
||||
setSelect4('0');
|
||||
setSelect5('0');
|
||||
};
|
||||
const registFile = async (analysisData: any) => {
|
||||
console.log("Submitting combined analysis data to backend");
|
||||
// 추후 API 엔드포인트에 맞게 URL 수정 필요
|
||||
const data = await postData('v1/rdiiAnls', analysisData);
|
||||
if (data) {
|
||||
alert("분석 데이터가 성공적으로 제출되었습니다.");
|
||||
resetAllForms(); // 성공 시 폼 초기화 함수 호출
|
||||
} else alert("분석 데이터 제출 중 오류가 발생했습니다.");
|
||||
};
|
||||
|
||||
// FileUploadForm의 onFileSelect prop으로 전달될 콜백 함수
|
||||
const handleFileSelection = (
|
||||
index: number, // FileUploadForm의 fileIdx 값
|
||||
selectedFile: File | null,
|
||||
cd: string | null,
|
||||
id: string | null
|
||||
) => {
|
||||
setUploadedFilesData(prevData => {
|
||||
const newData = [...prevData];
|
||||
newData[index] = {
|
||||
file: selectedFile,
|
||||
atflCd: cd,
|
||||
atflId: id,
|
||||
};
|
||||
return newData;
|
||||
});
|
||||
console.log(`Page: File data received for form index ${index}`, { fileName: selectedFile?.name, cd, id });
|
||||
};
|
||||
// 침입수 분석 실행 버튼 클릭 핸들러
|
||||
const handleAnalysisClick = async () => {
|
||||
setIsLoadingResult(true); // 로딩 상태 시작
|
||||
const formData = form.getValues();
|
||||
console.log(uploadedFilesData);
|
||||
const file1 = uploadedFilesData[0]; // 오수관망도
|
||||
const file2 = uploadedFilesData[1]; // 수용가-맨홀 매칭결과
|
||||
const file3 = uploadedFilesData[2]; // 강우자료
|
||||
|
||||
const registFile = async (analysisData: any) => {
|
||||
console.log("Submitting combined analysis data to backend");
|
||||
// 추후 API 엔드포인트에 맞게 URL 수정 필요
|
||||
const data = await postData('v1/rdiiAnls', analysisData);
|
||||
if (data) {
|
||||
alert("분석 데이터가 성공적으로 제출되었습니다.");
|
||||
resetAllForms(); // 성공 시 폼 초기화 함수 호출
|
||||
} else alert("분석 데이터 제출 중 오류가 발생했습니다.");
|
||||
};
|
||||
console.log("오수관망도 정보:", file1?.file);
|
||||
console.log("수용가-맨홀 매칭결과 정보:", file2);
|
||||
console.log("강우자료:", file3);
|
||||
|
||||
// 침입수 분석 실행 버튼 클릭 핸들러
|
||||
const handleAnalysisClick = async () => {
|
||||
setIsLoadingResult(true); // 로딩 상태 시작
|
||||
const formData = form.getValues();
|
||||
console.log(uploadedFilesData);
|
||||
const file1 = uploadedFilesData[0]; // 오수관망도
|
||||
const file2 = uploadedFilesData[1]; // 수용가-맨홀 매칭결과
|
||||
const file3 = uploadedFilesData[2]; // 강우자료
|
||||
|
||||
console.log("오수관망도 정보:", file1?.file);
|
||||
console.log("수용가-맨홀 매칭결과 정보:", file2);
|
||||
console.log("강우자료:", file3);
|
||||
|
||||
// 모든 필수 파일이 선택되었는지 확인
|
||||
if (!file1?.file || !file2?.file || !file3?.file) {
|
||||
setIsLoadingResult(false);
|
||||
alert('모든 분석용 파일을 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
const filePickers = [
|
||||
{ file: file2, parser: excelToMatchingResultData },
|
||||
{ file: file3, parser: excelToRainData },
|
||||
];
|
||||
|
||||
try {
|
||||
const parsedResults = await Promise.all(
|
||||
filePickers.map(async ({ file, parser }) => {
|
||||
if (!file || !file.file) return null; // fileInfo 객체 및 file 객체 null 체크
|
||||
|
||||
// filePicked 함수에 File 객체와 headerNum 전달
|
||||
const jsonData = await filePicked(file.file, 0);
|
||||
|
||||
if (jsonData === undefined) {
|
||||
console.warn(`Warning: filePicked returned undefined for ${file.file.name}`);
|
||||
return {
|
||||
fileName: file.file.name,
|
||||
atflCd: file.atflCd,
|
||||
atflId: file.atflId,
|
||||
parsedData: null // 또는 적절한 기본값
|
||||
};
|
||||
}
|
||||
|
||||
// jsonData를 각 parser 함수로 처리
|
||||
// parser 함수가 filePicked가 반환하는 JSON 형식을 직접 처리한다고 가정
|
||||
const parsedDataBySpecificFunction = parser(jsonData);
|
||||
|
||||
return {
|
||||
fileName: file.file.name,
|
||||
atflCd: file.atflCd,
|
||||
atflId: file.atflId,
|
||||
parsedData: parsedDataBySpecificFunction
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
// 각 파싱 결과 접근
|
||||
const matchingResultData = parsedResults.find(r => r && r.fileName === file2.file?.name)?.parsedData;
|
||||
const rainData = parsedResults.find(r => r && r.fileName === file3.file?.name)?.parsedData;
|
||||
|
||||
// 새로운 JSON 데이터 생성
|
||||
const combinedAnalysisData = {
|
||||
enoCd: (select1 && select1 !== '0') ? select1.toString() : null, // 환경청코드 (선택값)
|
||||
aplcnYm: new Date(formData.date).toISOString().substring(0, 7).replace("-", ""), // 적용연월 (YYYYMM 형식)
|
||||
ctpvCd: (select2 && select2 !== '0') ? select2.toString() : null, // 시도코드 (선택값)
|
||||
sggCd: (select3.val && select3.val !== '0') ? select3.val.toString() : null, // 시군구코드 (선택값)
|
||||
swageTmplntCd: (select4 && select4 !== '0' && select4 !== 0)
|
||||
? select4.toString().split('.').pop() || null // '.'으로 split해서 마지막 부분만 사용
|
||||
: null, // 하수처리장코드 (선택값)
|
||||
swtrmtZoneCd: (select5 && select5 !== '0' && select5 !== 0)
|
||||
? select5.toString().split('.').pop() || null // '.'으로 split해서 마지막 부분만 사용
|
||||
: null, // 하수처리구역코드 (선택값)
|
||||
atflCd: file1?.atflCd, // 첨부파일CD (오수관망도)
|
||||
atflId: file1?.atflId, // 첨부파일ID (오수관망도)
|
||||
atflInpNm: file1?.file?.name || "", // 오수관망도 파일명
|
||||
atflMpngNm: file2?.file?.name || "", // 수용가-맨홀 매칭결과 파일명
|
||||
atflRainNm: file3?.file?.name || "", // 강우자료 파일명
|
||||
cntbArea: parseFloat(formData.rdiiArea) || null, // RDII 기여면적(ha)
|
||||
srcNm: formData.rtkSource, // 출처이름 (RTK계수 출처)
|
||||
|
||||
shortPrdR: parseFloat(formData.unitFactors[0]?.R || "0"),
|
||||
shortPrdT: parseFloat(formData.unitFactors[0]?.T || "0"),
|
||||
shortPrdK: parseFloat(formData.unitFactors[0]?.K || "0"),
|
||||
|
||||
mdPrdR: parseFloat(formData.unitFactors[1]?.R || "0"),
|
||||
mdPrdT: parseFloat(formData.unitFactors[1]?.T || "0"),
|
||||
mdPrdK: parseFloat(formData.unitFactors[1]?.K || "0"),
|
||||
|
||||
longPrdR: parseFloat(formData.unitFactors[2]?.R || "0"),
|
||||
longPrdT: parseFloat(formData.unitFactors[2]?.T || "0"),
|
||||
longPrdK: parseFloat(formData.unitFactors[2]?.K || "0"),
|
||||
|
||||
shortPrdMaxDpth: parseFloat(formData.initialDepths[0]?.Dmax || "0"),
|
||||
shortPrdRcvryRt: parseFloat(formData.initialDepths[0]?.Drec || "0"),
|
||||
shortPrdBgngDpth: parseFloat(formData.initialDepths[0]?.Do || "0"),
|
||||
|
||||
mdPrdMaxDpth: parseFloat(formData.initialDepths[1]?.Dmax || "0"),
|
||||
mdPrdRcvryRt: parseFloat(formData.initialDepths[1]?.Drec || "0"),
|
||||
mdPrdBgngDpth: parseFloat(formData.initialDepths[1]?.Do || "0"),
|
||||
|
||||
longTermMaxDpth: parseFloat(formData.initialDepths[2]?.Dmax || "0"),
|
||||
longTermRcvryRt: parseFloat(formData.initialDepths[2]?.Drec || "0"),
|
||||
longTermBgngDpth: parseFloat(formData.initialDepths[2]?.Do || "0"),
|
||||
|
||||
drtwSwtcRt: parseFloat(formData.rate || "0"), // 오수전환율
|
||||
anlsInfo: formData.analysisInfo, // 분석정보
|
||||
|
||||
tbSnRcptrMhlMpng: matchingResultData, // 수용가-맨홀 매칭결과
|
||||
tbSnRainData: rainData, // 강우자료
|
||||
};
|
||||
|
||||
console.log("통합 분석 데이터 (JSON):", combinedAnalysisData);
|
||||
|
||||
// 수정된 registFile 함수를 호출하여 combinedAnalysisData를 전송합니다.
|
||||
await registFile(combinedAnalysisData);
|
||||
setIsLoadingResult(false);
|
||||
} catch (error) {
|
||||
setIsLoadingResult(false);
|
||||
console.error("파일 파싱 중 오류 발생:", error);
|
||||
alert("파일 처리 중 오류가 발생했습니다.");
|
||||
return;
|
||||
}
|
||||
// 모든 필수 파일이 선택되었는지 확인
|
||||
if (!file1?.file || !file2?.file || !file3?.file) {
|
||||
setIsLoadingResult(false);
|
||||
alert('모든 분석용 파일을 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
const { fields: unitFactorFields } = useFieldArray({
|
||||
control: form.control,
|
||||
name: "unitFactors"
|
||||
})
|
||||
|
||||
const { fields: initialDepthFields } = useFieldArray({
|
||||
control: form.control,
|
||||
name: "initialDepths"
|
||||
})
|
||||
|
||||
const onSubmit = (data: z.infer<typeof formSchema>) => {
|
||||
console.log("✅ 제출 데이터:", data)
|
||||
}
|
||||
|
||||
// 단위도 계수 테이블 설정 (EditableTableHeadItem 타입 사용 및 id 수정)
|
||||
const unitFactorHeadlist: EditableTableHeadItem<FormSchemaType['unitFactors'][number]>[] = [
|
||||
{ id: 'response', title: 'Response', editable: false, width: '150px' }, // width 속성 추가
|
||||
{ id: 'R', title: 'R', editable: true, inputProps: { type: 'text' } },
|
||||
{ id: 'T', title: 'T', editable: true, inputProps: { type: 'text' } },
|
||||
{ id: 'K', title: 'K', editable: true, inputProps: { type: 'text' } }
|
||||
const filePickers = [
|
||||
{ file: file2, parser: excelToMatchingResultData },
|
||||
{ file: file3, parser: excelToRainData },
|
||||
];
|
||||
|
||||
// 초기 차단 깊이 테이블 설정 (EditableTableHeadItem 타입 사용)
|
||||
const initialDepthHeadlist: EditableTableHeadItem<FormSchemaType['initialDepths'][number]>[] = [
|
||||
{ id: 'response', title: 'Response', editable: false, width: '150px' }, // width 속성 추가
|
||||
{ id: 'Dmax', title: 'Dmax', editable: true, inputProps: { type: 'text' } },
|
||||
{ id: 'Drec', title: 'Drec', editable: true, inputProps: { type: 'text' } },
|
||||
{ id: 'Do', title: 'Do', editable: true, inputProps: { type: 'text' } }
|
||||
];
|
||||
try {
|
||||
const parsedResults = await Promise.all(
|
||||
filePickers.map(async ({ file, parser }) => {
|
||||
if (!file || !file.file) return null; // fileInfo 객체 및 file 객체 null 체크
|
||||
|
||||
// 테이블 페이지 변경 핸들러
|
||||
const handlePageChange = (page: number) => {
|
||||
console.log('Page changed:', page)
|
||||
// filePicked 함수에 File 객체와 headerNum 전달
|
||||
const jsonData = await filePicked(file.file, 0);
|
||||
|
||||
if (jsonData === undefined) {
|
||||
console.warn(`Warning: filePicked returned undefined for ${file.file.name}`);
|
||||
return {
|
||||
fileName: file.file.name,
|
||||
atflCd: file.atflCd,
|
||||
atflId: file.atflId,
|
||||
parsedData: null // 또는 적절한 기본값
|
||||
};
|
||||
}
|
||||
|
||||
// jsonData를 각 parser 함수로 처리
|
||||
// parser 함수가 filePicked가 반환하는 JSON 형식을 직접 처리한다고 가정
|
||||
const parsedDataBySpecificFunction = parser(jsonData);
|
||||
|
||||
return {
|
||||
fileName: file.file.name,
|
||||
atflCd: file.atflCd,
|
||||
atflId: file.atflId,
|
||||
parsedData: parsedDataBySpecificFunction
|
||||
};
|
||||
})
|
||||
);
|
||||
|
||||
// 각 파싱 결과 접근
|
||||
const matchingResultData = parsedResults.find(r => r && r.fileName === file2.file?.name)?.parsedData;
|
||||
const rainData = parsedResults.find(r => r && r.fileName === file3.file?.name)?.parsedData;
|
||||
|
||||
// 새로운 JSON 데이터 생성
|
||||
const combinedAnalysisData = {
|
||||
enoCd: (select1 && select1 !== '0') ? select1.toString() : null, // 환경청코드 (선택값)
|
||||
aplcnYm: new Date(formData.date).toISOString().substring(0, 7).replace("-", ""), // 적용연월 (YYYYMM 형식)
|
||||
ctpvCd: (select2 && select2 !== '0') ? select2.toString() : null, // 시도코드 (선택값)
|
||||
sggCd: (select3.val && select3.val !== '0') ? select3.val.toString() : null, // 시군구코드 (선택값)
|
||||
swageTmplntCd: (select4 && select4 !== '0' && select4 !== 0)
|
||||
? select4.toString().split('.').pop() || null // '.'으로 split해서 마지막 부분만 사용
|
||||
: null, // 하수처리장코드 (선택값)
|
||||
swtrmtZoneCd: (select5 && select5 !== '0' && select5 !== 0)
|
||||
? select5.toString().split('.').pop() || null // '.'으로 split해서 마지막 부분만 사용
|
||||
: null, // 하수처리구역코드 (선택값)
|
||||
atflCd: file1?.atflCd, // 첨부파일CD (오수관망도)
|
||||
atflId: file1?.atflId, // 첨부파일ID (오수관망도)
|
||||
atflInpNm: file1?.file?.name || "", // 오수관망도 파일명
|
||||
atflMpngNm: file2?.file?.name || "", // 수용가-맨홀 매칭결과 파일명
|
||||
atflRainNm: file3?.file?.name || "", // 강우자료 파일명
|
||||
cntbArea: parseFloat(formData.rdiiArea) || null, // RDII 기여면적(ha)
|
||||
srcNm: formData.rtkSource, // 출처이름 (RTK계수 출처)
|
||||
|
||||
shortPrdR: parseFloat(formData.unitFactors[0]?.R || "0"),
|
||||
shortPrdT: parseFloat(formData.unitFactors[0]?.T || "0"),
|
||||
shortPrdK: parseFloat(formData.unitFactors[0]?.K || "0"),
|
||||
|
||||
mdPrdR: parseFloat(formData.unitFactors[1]?.R || "0"),
|
||||
mdPrdT: parseFloat(formData.unitFactors[1]?.T || "0"),
|
||||
mdPrdK: parseFloat(formData.unitFactors[1]?.K || "0"),
|
||||
|
||||
longPrdR: parseFloat(formData.unitFactors[2]?.R || "0"),
|
||||
longPrdT: parseFloat(formData.unitFactors[2]?.T || "0"),
|
||||
longPrdK: parseFloat(formData.unitFactors[2]?.K || "0"),
|
||||
|
||||
shortPrdMaxDpth: parseFloat(formData.initialDepths[0]?.Dmax || "0"),
|
||||
shortPrdRcvryRt: parseFloat(formData.initialDepths[0]?.Drec || "0"),
|
||||
shortPrdBgngDpth: parseFloat(formData.initialDepths[0]?.Do || "0"),
|
||||
|
||||
mdPrdMaxDpth: parseFloat(formData.initialDepths[1]?.Dmax || "0"),
|
||||
mdPrdRcvryRt: parseFloat(formData.initialDepths[1]?.Drec || "0"),
|
||||
mdPrdBgngDpth: parseFloat(formData.initialDepths[1]?.Do || "0"),
|
||||
|
||||
longTermMaxDpth: parseFloat(formData.initialDepths[2]?.Dmax || "0"),
|
||||
longTermRcvryRt: parseFloat(formData.initialDepths[2]?.Drec || "0"),
|
||||
longTermBgngDpth: parseFloat(formData.initialDepths[2]?.Do || "0"),
|
||||
|
||||
drtwSwtcRt: parseFloat(formData.rate || "0"), // 오수전환율
|
||||
anlsInfo: formData.analysisInfo, // 분석정보
|
||||
|
||||
tbSnRcptrMhlMpng: matchingResultData, // 수용가-맨홀 매칭결과
|
||||
tbSnRainData: rainData, // 강우자료
|
||||
};
|
||||
|
||||
console.log("통합 분석 데이터 (JSON):", combinedAnalysisData);
|
||||
|
||||
// 수정된 registFile 함수를 호출하여 combinedAnalysisData를 전송합니다.
|
||||
await registFile(combinedAnalysisData);
|
||||
setIsLoadingResult(false);
|
||||
} catch (error) {
|
||||
setIsLoadingResult(false);
|
||||
console.error("파일 파싱 중 오류 발생:", error);
|
||||
alert("파일 처리 중 오류가 발생했습니다.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
// <BaseLayout searchBox={{ title: '분석 대상지역 선택' }} titleName={{ mainTitle: 'RDII 분석', firstCardTitle: 'RDII 분석자료 입력', secondCardTitle: '실측자료 등록' }} form={form} onSubmitEvent={() => { }} graphBtn={false} >
|
||||
<BaseLayout
|
||||
selectBox={{ title: '분석 대상지역 선택' }}
|
||||
titleName={{ mainTitle: 'RDII 분석', firstCardTitle: 'RDII 분석자료 입력', secondCardTitle: '실측자료 등록' }}
|
||||
form={form} // react-hook-form 인스턴스 전달
|
||||
onSubmitEvent={() => { }} // BaseLayout의 form 태그에 연결될 onSubmit
|
||||
graphBtn={false}
|
||||
analysisBtn={'RDII분석 실행'}
|
||||
enoCd={select1}
|
||||
setEnoCd={setSelect1}
|
||||
ctpvCd={select2}
|
||||
setCtpvCd={setSelect2}
|
||||
sigungu={select3}
|
||||
setSigungu={setSelect3}
|
||||
selectedSwageTreatmentPlant={select4}
|
||||
onSwageTreatmentPlantChange={setSelect4}
|
||||
selectedSwageProcessingZone={select5}
|
||||
onSwageProcessingZoneChange={setSelect5}
|
||||
isLoading={isLoadingResult}
|
||||
onAnalysis={handleAnalysisClick}
|
||||
>
|
||||
{[<div className="space-y-[1.2rem] overflow-x-auto pt-4 pb-4 px-2" key="form-section">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<FormSection name="rtkSource" form={form} titleNm="○ RTK계수 출처">
|
||||
<Input {...form.register("rtkSource")} className="h-12 text-center text-xl w-[225px]" />
|
||||
</FormSection>
|
||||
<FormSection name="rdiiArea" form={form} titleNm="○ RDII 기여면적(ha)">
|
||||
<Input {...form.register("rdiiArea")} className="h-12 text-center text-xl w-[225px]" />
|
||||
</FormSection></div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label className="mb-2 text-xl font-medium">○ 단위도 계수</Label>
|
||||
<EditableFormTable
|
||||
form={form}
|
||||
name="unitFactors"
|
||||
fields={unitFactorFields}
|
||||
headlist={unitFactorHeadlist}
|
||||
tableClassName="w-[400px]"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label className="mb-[1.2rem] text-xl font-medium">○ 초기 차단 깊이</Label>
|
||||
<EditableFormTable
|
||||
form={form}
|
||||
name="initialDepths"
|
||||
fields={initialDepthFields}
|
||||
headlist={initialDepthHeadlist}
|
||||
tableClassName="w-[400px]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<FormSection name="suyeonga" form={form} titleNm="○ 오수관망도"><div className="w-[350px]">
|
||||
<FileUploadForm
|
||||
key={`file-upload-0-${formResetKey}`} // 폼 초기화를 위한 key
|
||||
onFileSelect={(file, cd, id) => handleFileSelection(0, file, cd, id)}
|
||||
showButton={false}
|
||||
showPreview={true}
|
||||
accept={""}
|
||||
fileIdx={0} />
|
||||
</div></FormSection>
|
||||
<FormSection name="regopm" form={form} titleNm="○ 수용가-맨홀 매칭결과"><div className="w-[350px]">
|
||||
<FileUploadForm
|
||||
key={`file-upload-1-${formResetKey}`} // 폼 초기화를 위한 key
|
||||
onFileSelect={(file, cd, id) => handleFileSelection(1, file, cd, id)}
|
||||
showButton={false}
|
||||
showPreview={true}
|
||||
accept={".xls, .xlsx, .xlsm, .xltx"}
|
||||
fileIdx={1} />
|
||||
</div></FormSection>
|
||||
<FormSection name="rain" form={form} titleNm="○ 강우자료"><div className="w-[350px]">
|
||||
<FileUploadForm
|
||||
key={`file-upload-2-${formResetKey}`} // 폼 초기화를 위한 key
|
||||
onFileSelect={(file, cd, id) => handleFileSelection(2, file, cd, id)}
|
||||
showButton={false}
|
||||
showPreview={true}
|
||||
accept={".xls, .xlsx, .xlsm, .xltx"}
|
||||
fileIdx={2} />
|
||||
</div></FormSection>
|
||||
<FormSection name='rate' form={form} titleNm='○ 오수전환율'>
|
||||
<Input {...form.register("rate")} className="h-12 text-center text-xl w-[225px] ml-5" />
|
||||
</FormSection>
|
||||
<FormSection name='date' form={form} titleNm='○ 상수사용량 적용기간'>
|
||||
{/* <DatePicker
|
||||
const { fields: unitFactorFields } = useFieldArray({
|
||||
control: form.control,
|
||||
name: "unitFactors"
|
||||
})
|
||||
|
||||
const { fields: initialDepthFields } = useFieldArray({
|
||||
control: form.control,
|
||||
name: "initialDepths"
|
||||
})
|
||||
|
||||
const onSubmit = (data: z.infer<typeof formSchema>) => {
|
||||
console.log("✅ 제출 데이터:", data)
|
||||
}
|
||||
|
||||
// 단위도 계수 테이블 설정 (EditableTableHeadItem 타입 사용 및 id 수정)
|
||||
const unitFactorHeadlist: EditableTableHeadItem<FormSchemaType['unitFactors'][number]>[] = [
|
||||
{ id: 'response', title: 'Response', editable: false, width: '150px' }, // width 속성 추가
|
||||
{ id: 'R', title: 'R', editable: true, inputProps: { type: 'text' } },
|
||||
{ id: 'T', title: 'T', editable: true, inputProps: { type: 'text' } },
|
||||
{ id: 'K', title: 'K', editable: true, inputProps: { type: 'text' } }
|
||||
];
|
||||
|
||||
// 초기 차단 깊이 테이블 설정 (EditableTableHeadItem 타입 사용)
|
||||
const initialDepthHeadlist: EditableTableHeadItem<FormSchemaType['initialDepths'][number]>[] = [
|
||||
{ id: 'response', title: 'Response', editable: false, width: '150px' }, // width 속성 추가
|
||||
{ id: 'Dmax', title: 'Dmax', editable: true, inputProps: { type: 'text' } },
|
||||
{ id: 'Drec', title: 'Drec', editable: true, inputProps: { type: 'text' } },
|
||||
{ id: 'Do', title: 'Do', editable: true, inputProps: { type: 'text' } }
|
||||
];
|
||||
|
||||
// 테이블 페이지 변경 핸들러
|
||||
const handlePageChange = (page: number) => {
|
||||
console.log('Page changed:', page)
|
||||
}
|
||||
|
||||
return (
|
||||
// <BaseLayout searchBox={{ title: '분석 대상지역 선택' }} titleName={{ mainTitle: 'RDII 분석', firstCardTitle: 'RDII 분석자료 입력', secondCardTitle: '실측자료 등록' }} form={form} onSubmitEvent={() => { }} graphBtn={false} >
|
||||
<BaseLayout
|
||||
selectBox={{ title: '분석 대상지역 선택' }}
|
||||
titleName={{ mainTitle: 'RDII 분석', firstCardTitle: 'RDII 분석자료 입력', secondCardTitle: '실측자료 등록' }}
|
||||
form={form} // react-hook-form 인스턴스 전달
|
||||
onSubmitEvent={() => { }} // BaseLayout의 form 태그에 연결될 onSubmit
|
||||
graphBtn={false}
|
||||
analysisBtn={'RDII분석 실행'}
|
||||
enoCd={select1}
|
||||
setEnoCd={setSelect1}
|
||||
ctpvCd={select2}
|
||||
setCtpvCd={setSelect2}
|
||||
sigungu={select3}
|
||||
setSigungu={setSelect3}
|
||||
selectedSwageTreatmentPlant={select4}
|
||||
onSwageTreatmentPlantChange={setSelect4}
|
||||
selectedSwageProcessingZone={select5}
|
||||
onSwageProcessingZoneChange={setSelect5}
|
||||
isLoading={isLoadingResult}
|
||||
onAnalysis={handleAnalysisClick}
|
||||
>
|
||||
{[<div className="space-y-[1.2rem] overflow-x-auto pt-4 pb-4 px-2" key="form-section">
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<FormSection name="rtkSource" form={form} titleNm="○ RTK계수 출처">
|
||||
<Input {...form.register("rtkSource")} className="h-12 text-center text-xl w-[225px]" />
|
||||
</FormSection>
|
||||
<FormSection name="rdiiArea" form={form} titleNm="○ RDII 기여면적(ha)">
|
||||
<Input {...form.register("rdiiArea")} className="h-12 text-center text-xl w-[225px]" />
|
||||
</FormSection></div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label className="mb-2 text-xl font-medium">○ 단위도 계수</Label>
|
||||
<EditableFormTable
|
||||
form={form}
|
||||
name="unitFactors"
|
||||
fields={unitFactorFields}
|
||||
headlist={unitFactorHeadlist}
|
||||
tableClassName="w-[400px]"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Label className="mb-[1.2rem] text-xl font-medium">○ 초기 차단 깊이</Label>
|
||||
<EditableFormTable
|
||||
form={form}
|
||||
name="initialDepths"
|
||||
fields={initialDepthFields}
|
||||
headlist={initialDepthHeadlist}
|
||||
tableClassName="w-[400px]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-4">
|
||||
<FormSection name="suyeonga" form={form} titleNm="○ 오수관망도"><div className="w-[350px]">
|
||||
<FileUploadForm
|
||||
key={`file-upload-0-${formResetKey}`} // 폼 초기화를 위한 key
|
||||
onFileSelect={(file, cd, id) => handleFileSelection(0, file, cd, id)}
|
||||
showButton={false}
|
||||
showPreview={true}
|
||||
accept={""}
|
||||
fileIdx={0} />
|
||||
</div></FormSection>
|
||||
<FormSection name="regopm" form={form} titleNm="○ 수용가-맨홀 매칭결과"><div className="w-[350px]">
|
||||
<FileUploadForm
|
||||
key={`file-upload-1-${formResetKey}`} // 폼 초기화를 위한 key
|
||||
onFileSelect={(file, cd, id) => handleFileSelection(1, file, cd, id)}
|
||||
showButton={false}
|
||||
showPreview={true}
|
||||
accept={".xls, .xlsx, .xlsm, .xltx"}
|
||||
fileIdx={1} />
|
||||
</div></FormSection>
|
||||
<FormSection name="rain" form={form} titleNm="○ 강우자료"><div className="w-[350px]">
|
||||
<FileUploadForm
|
||||
key={`file-upload-2-${formResetKey}`} // 폼 초기화를 위한 key
|
||||
onFileSelect={(file, cd, id) => handleFileSelection(2, file, cd, id)}
|
||||
showButton={false}
|
||||
showPreview={true}
|
||||
accept={".xls, .xlsx, .xlsm, .xltx"}
|
||||
fileIdx={2} />
|
||||
</div></FormSection>
|
||||
<FormSection name='rate' form={form} titleNm='○ 오수전환율'>
|
||||
<Input {...form.register("rate")} className="h-12 text-center text-xl w-[225px] ml-5" />
|
||||
</FormSection>
|
||||
<FormSection name='date' form={form} titleNm='○ 상수사용량 적용기간'>
|
||||
{/* <DatePicker
|
||||
selected={form.watch("date")}
|
||||
onSelect={(date: Date | undefined) => {
|
||||
if (date) {
|
||||
@@ -413,30 +413,30 @@ export default function RTKForm() {
|
||||
mode={"single"}
|
||||
className="ml-5"
|
||||
/> */}
|
||||
<DatePicker
|
||||
mode={"single"} // mode는 DayPickerSingleProps에서 오므로 그대로 사용 가능
|
||||
field={{
|
||||
value: form.watch("date"), // react-hook-form의 "date" 필드 값 사용
|
||||
onChange: (date: Date | undefined) => {
|
||||
if (date) {
|
||||
form.setValue("date", date, { shouldValidate: true });
|
||||
}
|
||||
}
|
||||
}}
|
||||
// className을 직접 사용하거나, DatePicker 내부에서 selectClass를 처리하도록 수정 필요
|
||||
// 여기서는 DatePicker의 selectClass prop을 사용하고, date-picker.css의 스타일을 적용
|
||||
selectClass="ml-5 date-picker-trigger"
|
||||
/>
|
||||
</FormSection>
|
||||
<FormSection name='rate' form={form} titleNm='○ 분석 정보'>
|
||||
<Input {...form.register("analysisInfo")} className="h-12 text-center text-xl w-[225px] ml-5" />
|
||||
</FormSection>
|
||||
</div>
|
||||
{/* <div className="flex justify-end mt-1">
|
||||
<DatePicker
|
||||
mode={"single"} // mode는 DayPickerSingleProps에서 오므로 그대로 사용 가능
|
||||
field={{
|
||||
value: form.watch("date"), // react-hook-form의 "date" 필드 값 사용
|
||||
onChange: (date: Date | undefined) => {
|
||||
if (date) {
|
||||
form.setValue("date", date, { shouldValidate: true });
|
||||
}
|
||||
}
|
||||
}}
|
||||
// className을 직접 사용하거나, DatePicker 내부에서 selectClass를 처리하도록 수정 필요
|
||||
// 여기서는 DatePicker의 selectClass prop을 사용하고, date-picker.css의 스타일을 적용
|
||||
selectClass="ml-5 date-picker-trigger"
|
||||
/>
|
||||
</FormSection>
|
||||
<FormSection name='rate' form={form} titleNm='○ 분석 정보'>
|
||||
<Input {...form.register("analysisInfo")} className="h-12 text-center text-xl w-[225px] ml-5" />
|
||||
</FormSection>
|
||||
</div>
|
||||
{/* <div className="flex justify-end mt-1">
|
||||
<Button size="sm" type="submit" onClick={handleAnalysisClick}>RDII분석 실행</Button>
|
||||
</div> */}
|
||||
</div>
|
||||
]}
|
||||
</BaseLayout >
|
||||
)
|
||||
</div>
|
||||
]}
|
||||
</BaseLayout >
|
||||
)
|
||||
}
|
||||
|
||||
@@ -10,259 +10,259 @@ import { handleDelete } from '@/components/lib/utils'
|
||||
|
||||
|
||||
const formSchema = z.object({
|
||||
enoCd: z.string().default('0'), // 환경청코드
|
||||
ctpvCd: z.string().default('0'), // 시도코드
|
||||
sggCd: z.object({ // 시군구
|
||||
val: z.string().default('0'),
|
||||
text: z.string().default('전체'),
|
||||
}).default({ val: '0', text: '전체' }),
|
||||
swageTmplntCd: z.string().default('0'), // 하수처리장
|
||||
swtrmtZoneCd: z.string().default('0'), // 하수처리구역
|
||||
aplcnYm: z.date().default(new Date()), // 분석일자 (DatePicker와 연동)
|
||||
enoCd: z.string().default('0'), // 환경청코드
|
||||
ctpvCd: z.string().default('0'), // 시도코드
|
||||
sggCd: z.object({ // 시군구
|
||||
val: z.string().default('0'),
|
||||
text: z.string().default('전체'),
|
||||
}).default({ val: '0', text: '전체' }),
|
||||
swageTmplntCd: z.string().default('0'), // 하수처리장
|
||||
swtrmtZoneCd: z.string().default('0'), // 하수처리구역
|
||||
aplcnYm: z.date().default(new Date()), // 분석일자 (DatePicker와 연동)
|
||||
})
|
||||
|
||||
type ResultFormValues = z.infer<typeof formSchema>
|
||||
|
||||
const tableColumn = [
|
||||
{ id: 'rainInfwtAnlsSn', title: '분석번호' },
|
||||
{ id: 'swageTmplntNm', title: '하수처리장' },
|
||||
// { id: 'swtrmtZoneNm', title: '처리구역' },
|
||||
{ id: 'aplcnYm', title: '상수사용연월' },
|
||||
{ id: 'drtwSwtcRt', title: '오수 전환율' },
|
||||
{ id: 'srcNm', title: 'RTX 계수 출처' },
|
||||
{ id: 'cntbArea', title: 'RDII 기여면적' },
|
||||
{ id: 'anlsInfo', title: 'RDII 분석정보' },
|
||||
{ id: 'regDt', title: '분석일자' },
|
||||
{ id: 'rgtrNm', title: '분석자' }
|
||||
{ id: 'rainInfwtAnlsSn', title: '분석번호' },
|
||||
{ id: 'swageTmplntNm', title: '하수처리장' },
|
||||
// { id: 'swtrmtZoneNm', title: '처리구역' },
|
||||
{ id: 'aplcnYm', title: '상수사용연월' },
|
||||
{ id: 'drtwSwtcRt', title: '오수 전환율' },
|
||||
{ id: 'srcNm', title: 'RTX 계수 출처' },
|
||||
{ id: 'cntbArea', title: 'RDII 기여면적' },
|
||||
{ id: 'anlsInfo', title: 'RDII 분석정보' },
|
||||
{ id: 'regDt', title: '분석일자' },
|
||||
{ id: 'rgtrNm', title: '분석자' }
|
||||
]
|
||||
|
||||
const Result = () => {
|
||||
const form = useForm<ResultFormValues>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
enoCd: '0',
|
||||
ctpvCd: '0',
|
||||
sggCd: { val: '0', text: '전체' },
|
||||
swageTmplntCd: '0',
|
||||
swtrmtZoneCd: '0',
|
||||
aplcnYm: new Date(),
|
||||
const form = useForm<ResultFormValues>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
enoCd: '0',
|
||||
ctpvCd: '0',
|
||||
sggCd: { val: '0', text: '전체' },
|
||||
swageTmplntCd: '0',
|
||||
swtrmtZoneCd: '0',
|
||||
aplcnYm: new Date(),
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const [tableData, setTableData] = useState<any[]>([]); // API 조회 결과 저장 상태
|
||||
const [totalData, setTotalData] = useState(0) //총 데이터 수
|
||||
const [isLoadingResult, setIsLoadingResult] = useState<boolean>(false); // 로딩 상태
|
||||
const [selectedRowData, setSelectedRowData] = useState<any>(null);
|
||||
const [selectedRowIndex, setSelectedRowIndex] = useState<number | undefined>(undefined);
|
||||
const [resetSelection, setResetSelection] = useState<boolean>(false);
|
||||
|
||||
|
||||
const handleSearchButtonClick = async (customFormValues?: Partial<ResultFormValues>) => {
|
||||
setIsLoadingResult(true);
|
||||
const formValues = customFormValues || form.getValues(); // react-hook-form에서 현재 값 가져오기
|
||||
console.log("formValues", formValues);
|
||||
|
||||
let inftwtAnlsSearchDto;
|
||||
|
||||
if (customFormValues) {
|
||||
inftwtAnlsSearchDto = {
|
||||
"aplcnYm": formValues.aplcnYm,
|
||||
"enoCd": formValues.enoCd,
|
||||
"ctpvCd": formValues.ctpvCd,
|
||||
"sggCd": formValues.sggCd,
|
||||
"swtrmtZoneCd": formValues.swtrmtZoneCd,
|
||||
};
|
||||
} else {
|
||||
inftwtAnlsSearchDto = {
|
||||
"aplcnYm": formValues.aplcnYm ? formValues.aplcnYm.toISOString().substring(0, 7).replace("-", "") : null,
|
||||
"enoCd": formValues.enoCd === '0' ? null : formValues.enoCd,
|
||||
"ctpvCd": formValues.ctpvCd === '0' ? null : formValues.ctpvCd,
|
||||
"sggCd": formValues.sggCd?.val === '0' ? null : formValues.sggCd?.val,
|
||||
"swtrmtZoneCd": formValues.swtrmtZoneCd === '0' ? null : formValues.swtrmtZoneCd,
|
||||
};
|
||||
}
|
||||
console.log("Search DTO:", inftwtAnlsSearchDto);
|
||||
|
||||
const queryParams = new URLSearchParams({
|
||||
page: '0',
|
||||
size: '10000000',
|
||||
sort: 'rainInfwtAnlsSn,desc'
|
||||
});
|
||||
Object.entries(inftwtAnlsSearchDto).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && String(value).trim() !== '') { // undefined, null, 빈 문자열이 아닌 경우에만 추가
|
||||
queryParams.append(key, String(value));
|
||||
}
|
||||
});
|
||||
const queryString = queryParams.toString();
|
||||
|
||||
console.log("API: ", `v1/rdiiAnls${queryString ? `?${queryString}` : ''}`);
|
||||
try {
|
||||
const data = await getData(`v1/rdiiAnls${queryString ? `?${queryString}` : ''}`);
|
||||
setTableData(data.content || []);
|
||||
setTotalData(tableData.length);
|
||||
console.log("response:", data);
|
||||
console.log("response data:", data.content || []);
|
||||
setIsLoadingResult(false);
|
||||
} catch (error) {
|
||||
setIsLoadingResult(false);
|
||||
console.error("Error fetching analysis result data:", error);
|
||||
alert("분석결과 조회 중 오류가 발생했습니다.");
|
||||
setTableData([]);
|
||||
setTotalData(0);
|
||||
}
|
||||
};
|
||||
|
||||
// 테이블 셀 클릭 핸들러 추가
|
||||
const handleCellClick = (index: number | undefined, headId: string) => {
|
||||
if (index !== undefined && tableData[index]) {
|
||||
setSelectedRowData(tableData[index]);
|
||||
setSelectedRowIndex(index);
|
||||
console.log("Selected row data:", tableData[index]);
|
||||
}
|
||||
};
|
||||
|
||||
// Excel 다운로드 핸들러 추가
|
||||
const handleExcelDownload = async () => {
|
||||
setIsLoadingResult(true);
|
||||
console.log("Selected row data for download:", selectedRowData);
|
||||
if (!selectedRowData || selectedRowData.rainInfwtAnlsSn === undefined) {
|
||||
setIsLoadingResult(false);
|
||||
alert('다운로드할 데이터를 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await getData(`v1/rdiiAnlsRslt/ppl/${selectedRowData.rainInfwtAnlsSn}`);
|
||||
|
||||
// 선택된 행 데이터를 Excel 다운로드 형식으로 변환
|
||||
const excelData = data.content; // 배열로 감싸서 전달
|
||||
|
||||
// 파일명 생성 (분석번호 기반)
|
||||
const fileName = `RDII분석결과_${selectedRowData.rainInfwtAnlsSn}_${selectedRowData.aplcnYm}`;
|
||||
|
||||
downloadExcel({
|
||||
data: excelData as JSON[],
|
||||
fileName: fileName,
|
||||
sheetNameArray: ["RDII 분석결과"],
|
||||
headerRange: "A1:D1" // 테이블 컬럼 수에 맞춰 조정
|
||||
});
|
||||
setIsLoadingResult(false);
|
||||
} catch (error) {
|
||||
setIsLoadingResult(false);
|
||||
console.error("Excel 다운로드 중 오류 발생:", error);
|
||||
alert("Excel 다운로드 중 오류가 발생했습니다. 다시 시도해주세요.");
|
||||
}
|
||||
};
|
||||
|
||||
// 파일 삭제 핸들러 추가
|
||||
const handleFileDelete = async () => {
|
||||
setIsLoadingResult(true);
|
||||
console.log("Selected row data for deletion:", selectedRowData);
|
||||
if (!selectedRowData || selectedRowData.rainInfwtAnlsSn === undefined) {
|
||||
alert('삭제할 파일을 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
// 삭제 확인 대화상자
|
||||
const confirmDelete = window.confirm('선택된 분석 결과를 삭제하시겠습니까?');
|
||||
if (!confirmDelete) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 삭제 API 호출
|
||||
const response1 = await deleteData(`v1/rdiiAnlsRslt/${selectedRowData.rainInfwtAnlsSn}`);
|
||||
const response2 = await deleteData(`v1/rdiiAnls/${selectedRowData.rainInfwtAnlsSn}`);
|
||||
// 첨부파일(inp파일) 삭제 API 호출
|
||||
handleDelete(selectedRowData.atflCd, {
|
||||
handleDeleteStart: () => console.log("파일 삭제 시작"),
|
||||
handleDeleteSuccess: (response) => console.log("파일 삭제 성공:", response),
|
||||
handleDeleteError: (error) => console.error("파일 삭제 실패:", error),
|
||||
});
|
||||
console.log("File delete response1:", response1);
|
||||
console.log("File delete response2:", response2);
|
||||
|
||||
// 테이블 선택 상태 초기화 트리거
|
||||
setResetSelection(true);
|
||||
|
||||
// 삭제 후 테이블 새로고침을 위해 재검색
|
||||
const customFormValues: Partial<ResultFormValues> = {
|
||||
aplcnYm: selectedRowData.aplcnYm || null,
|
||||
enoCd: selectedRowData.enoCd || null,
|
||||
ctpvCd: selectedRowData.ctpvCd || null,
|
||||
sggCd: selectedRowData.sggCd || null,
|
||||
swtrmtZoneCd: selectedRowData.swtrmtZoneCd || null,
|
||||
};
|
||||
await handleSearchButtonClick(customFormValues);
|
||||
|
||||
// 선택된 행 초기화
|
||||
setSelectedRowData(null);
|
||||
setSelectedRowIndex(undefined);
|
||||
setIsLoadingResult(false);
|
||||
|
||||
alert('삭제가 완료되었습니다.');
|
||||
|
||||
setTimeout(() => {
|
||||
setResetSelection(false);
|
||||
}, 100);
|
||||
} catch (error) {
|
||||
console.error("분석결과 파일 삭제 중 오류 발생:", error);
|
||||
alert("분석결과 파일 삭제 중 오류가 발생했습니다. 다시 시도해주세요.");
|
||||
}
|
||||
setIsLoadingResult(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseLayout
|
||||
selectBox={{ title: '분석 대상지역 선택', searchChk: true, onSearchClick: handleSearchButtonClick }}
|
||||
titleName={{ mainTitle: '분석결과', firstCardTitle: 'RDII 산정결과 현황' }}
|
||||
form={form} // form 인스턴스 전달
|
||||
onSubmitEvent={() => { }} // BaseLayout의 form 태그에 연결될 onSubmit
|
||||
graphBtn={true}
|
||||
showDatePicker={true}
|
||||
enoCd={form.watch("enoCd")}
|
||||
setEnoCd={(val) => form.setValue("enoCd", val)}
|
||||
ctpvCd={form.watch("ctpvCd")}
|
||||
setCtpvCd={(val) => form.setValue("ctpvCd", val)}
|
||||
sigungu={form.watch("sggCd")}
|
||||
setSigungu={(val) => form.setValue("sggCd", val)}
|
||||
selectedSwageTreatmentPlant={form.watch("swageTmplntCd")}
|
||||
onSwageTreatmentPlantChange={(val) => form.setValue("swageTmplntCd", String(val))}
|
||||
selectedSwageProcessingZone={form.watch("swtrmtZoneCd")}
|
||||
onSwageProcessingZoneChange={(val) => form.setValue("swtrmtZoneCd", String(val))}
|
||||
aplcnYm={form.watch("aplcnYm")}
|
||||
setAplcnYm={(val) => {
|
||||
if (val instanceof Date) {
|
||||
form.setValue("aplcnYm", val)
|
||||
} else if (val) {
|
||||
form.setValue("aplcnYm", new Date(val))
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const [tableData, setTableData] = useState<any[]>([]); // API 조회 결과 저장 상태
|
||||
const [totalData, setTotalData] = useState(0) //총 데이터 수
|
||||
const [isLoadingResult, setIsLoadingResult] = useState<boolean>(false); // 로딩 상태
|
||||
const [selectedRowData, setSelectedRowData] = useState<any>(null);
|
||||
const [selectedRowIndex, setSelectedRowIndex] = useState<number | undefined>(undefined);
|
||||
const [resetSelection, setResetSelection] = useState<boolean>(false);
|
||||
|
||||
|
||||
const handleSearchButtonClick = async (customFormValues?: Partial<ResultFormValues>) => {
|
||||
setIsLoadingResult(true);
|
||||
const formValues = customFormValues || form.getValues(); // react-hook-form에서 현재 값 가져오기
|
||||
console.log("formValues", formValues);
|
||||
|
||||
let inftwtAnlsSearchDto;
|
||||
|
||||
if (customFormValues) {
|
||||
inftwtAnlsSearchDto = {
|
||||
"aplcnYm": formValues.aplcnYm,
|
||||
"enoCd": formValues.enoCd,
|
||||
"ctpvCd": formValues.ctpvCd,
|
||||
"sggCd": formValues.sggCd,
|
||||
"swtrmtZoneCd": formValues.swtrmtZoneCd,
|
||||
};
|
||||
} else {
|
||||
inftwtAnlsSearchDto = {
|
||||
"aplcnYm": formValues.aplcnYm ? formValues.aplcnYm.toISOString().substring(0, 7).replace("-", "") : null,
|
||||
"enoCd": formValues.enoCd === '0' ? null : formValues.enoCd,
|
||||
"ctpvCd": formValues.ctpvCd === '0' ? null : formValues.ctpvCd,
|
||||
"sggCd": formValues.sggCd?.val === '0' ? null : formValues.sggCd?.val,
|
||||
"swtrmtZoneCd": formValues.swtrmtZoneCd === '0' ? null : formValues.swtrmtZoneCd,
|
||||
};
|
||||
}
|
||||
console.log("Search DTO:", inftwtAnlsSearchDto);
|
||||
|
||||
const queryParams = new URLSearchParams({
|
||||
page: '0',
|
||||
size: '10000000',
|
||||
sort: 'rainInfwtAnlsSn,desc'
|
||||
});
|
||||
Object.entries(inftwtAnlsSearchDto).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && String(value).trim() !== '') { // undefined, null, 빈 문자열이 아닌 경우에만 추가
|
||||
queryParams.append(key, String(value));
|
||||
}
|
||||
});
|
||||
const queryString = queryParams.toString();
|
||||
|
||||
console.log("API: ", `v1/rdiiAnls${queryString ? `?${queryString}` : ''}`);
|
||||
try {
|
||||
const data = await getData(`v1/rdiiAnls${queryString ? `?${queryString}` : ''}`);
|
||||
setTableData(data.content || []);
|
||||
setTotalData(tableData.length);
|
||||
console.log("response:", data);
|
||||
console.log("response data:", data.content || []);
|
||||
setIsLoadingResult(false);
|
||||
} catch (error) {
|
||||
setIsLoadingResult(false);
|
||||
console.error("Error fetching analysis result data:", error);
|
||||
alert("분석결과 조회 중 오류가 발생했습니다.");
|
||||
setTableData([]);
|
||||
setTotalData(0);
|
||||
}
|
||||
};
|
||||
|
||||
// 테이블 셀 클릭 핸들러 추가
|
||||
const handleCellClick = (index: number | undefined, headId: string) => {
|
||||
if (index !== undefined && tableData[index]) {
|
||||
setSelectedRowData(tableData[index]);
|
||||
setSelectedRowIndex(index);
|
||||
console.log("Selected row data:", tableData[index]);
|
||||
}
|
||||
};
|
||||
|
||||
// Excel 다운로드 핸들러 추가
|
||||
const handleExcelDownload = async () => {
|
||||
setIsLoadingResult(true);
|
||||
console.log("Selected row data for download:", selectedRowData);
|
||||
if (!selectedRowData || selectedRowData.rainInfwtAnlsSn === undefined) {
|
||||
setIsLoadingResult(false);
|
||||
alert('다운로드할 데이터를 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await getData(`v1/rdiiAnlsRslt/ppl/${selectedRowData.rainInfwtAnlsSn}`);
|
||||
|
||||
// 선택된 행 데이터를 Excel 다운로드 형식으로 변환
|
||||
const excelData = data.content; // 배열로 감싸서 전달
|
||||
|
||||
// 파일명 생성 (분석번호 기반)
|
||||
const fileName = `RDII분석결과_${selectedRowData.rainInfwtAnlsSn}_${selectedRowData.aplcnYm}`;
|
||||
|
||||
downloadExcel({
|
||||
data: excelData as JSON[],
|
||||
fileName: fileName,
|
||||
sheetNameArray: ["RDII 분석결과"],
|
||||
headerRange: "A1:D1" // 테이블 컬럼 수에 맞춰 조정
|
||||
});
|
||||
setIsLoadingResult(false);
|
||||
} catch (error) {
|
||||
setIsLoadingResult(false);
|
||||
console.error("Excel 다운로드 중 오류 발생:", error);
|
||||
alert("Excel 다운로드 중 오류가 발생했습니다. 다시 시도해주세요.");
|
||||
}
|
||||
};
|
||||
|
||||
// 파일 삭제 핸들러 추가
|
||||
const handleFileDelete = async () => {
|
||||
setIsLoadingResult(true);
|
||||
console.log("Selected row data for deletion:", selectedRowData);
|
||||
if (!selectedRowData || selectedRowData.rainInfwtAnlsSn === undefined) {
|
||||
alert('삭제할 파일을 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
// 삭제 확인 대화상자
|
||||
const confirmDelete = window.confirm('선택된 분석 결과를 삭제하시겠습니까?');
|
||||
if (!confirmDelete) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// 삭제 API 호출
|
||||
const response1 = await deleteData(`v1/rdiiAnlsRslt/${selectedRowData.rainInfwtAnlsSn}`);
|
||||
const response2 = await deleteData(`v1/rdiiAnls/${selectedRowData.rainInfwtAnlsSn}`);
|
||||
// 첨부파일(inp파일) 삭제 API 호출
|
||||
handleDelete(selectedRowData.atflCd, {
|
||||
handleDeleteStart: () => console.log("파일 삭제 시작"),
|
||||
handleDeleteSuccess: (response) => console.log("파일 삭제 성공:", response),
|
||||
handleDeleteError: (error) => console.error("파일 삭제 실패:", error),
|
||||
});
|
||||
console.log("File delete response1:", response1);
|
||||
console.log("File delete response2:", response2);
|
||||
|
||||
// 테이블 선택 상태 초기화 트리거
|
||||
setResetSelection(true);
|
||||
|
||||
// 삭제 후 테이블 새로고침을 위해 재검색
|
||||
const customFormValues: Partial<ResultFormValues> = {
|
||||
aplcnYm: selectedRowData.aplcnYm || null,
|
||||
enoCd: selectedRowData.enoCd || null,
|
||||
ctpvCd: selectedRowData.ctpvCd || null,
|
||||
sggCd: selectedRowData.sggCd || null,
|
||||
swtrmtZoneCd: selectedRowData.swtrmtZoneCd || null,
|
||||
};
|
||||
await handleSearchButtonClick(customFormValues);
|
||||
|
||||
// 선택된 행 초기화
|
||||
setSelectedRowData(null);
|
||||
setSelectedRowIndex(undefined);
|
||||
setIsLoadingResult(false);
|
||||
|
||||
alert('삭제가 완료되었습니다.');
|
||||
|
||||
setTimeout(() => {
|
||||
setResetSelection(false);
|
||||
}, 100);
|
||||
} catch (error) {
|
||||
console.error("분석결과 파일 삭제 중 오류 발생:", error);
|
||||
alert("분석결과 파일 삭제 중 오류가 발생했습니다. 다시 시도해주세요.");
|
||||
}
|
||||
setIsLoadingResult(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseLayout
|
||||
selectBox={{ title: '분석 대상지역 선택', searchChk: true, onSearchClick: handleSearchButtonClick }}
|
||||
titleName={{ mainTitle: '분석결과', firstCardTitle: 'RDII 산정결과 현황' }}
|
||||
form={form} // form 인스턴스 전달
|
||||
onSubmitEvent={() => { }} // BaseLayout의 form 태그에 연결될 onSubmit
|
||||
graphBtn={true}
|
||||
showDatePicker={true}
|
||||
enoCd={form.watch("enoCd")}
|
||||
setEnoCd={(val) => form.setValue("enoCd", val)}
|
||||
ctpvCd={form.watch("ctpvCd")}
|
||||
setCtpvCd={(val) => form.setValue("ctpvCd", val)}
|
||||
sigungu={form.watch("sggCd")}
|
||||
setSigungu={(val) => form.setValue("sggCd", val)}
|
||||
selectedSwageTreatmentPlant={form.watch("swageTmplntCd")}
|
||||
onSwageTreatmentPlantChange={(val) => form.setValue("swageTmplntCd", String(val))}
|
||||
selectedSwageProcessingZone={form.watch("swtrmtZoneCd")}
|
||||
onSwageProcessingZoneChange={(val) => form.setValue("swtrmtZoneCd", String(val))}
|
||||
aplcnYm={form.watch("aplcnYm")}
|
||||
setAplcnYm={(val) => {
|
||||
if (val instanceof Date) {
|
||||
form.setValue("aplcnYm", val)
|
||||
} else if (val) {
|
||||
form.setValue("aplcnYm", new Date(val))
|
||||
}
|
||||
}}
|
||||
isLoading={isLoadingResult}
|
||||
// Excel 다운로드 핸들러 추가
|
||||
onExcelDownload={handleExcelDownload}
|
||||
onDeleteFile={handleFileDelete}
|
||||
selectedRowData={selectedRowData}
|
||||
>
|
||||
{[<div key='table1' className="h-[55vh] flex flex-col p-4 border rounded-lg bg-white shadow-md">
|
||||
<div className="mb-[1.2rem] text-xl font-medium">○ RDII 분석결과 현황</div>
|
||||
<div className="flex-grow overflow-auto">
|
||||
<UiTable
|
||||
headName=''
|
||||
headlist={tableColumn}
|
||||
tableData={tableData}
|
||||
total={totalData}
|
||||
pageSize={10}
|
||||
children={undefined}
|
||||
cellClick={handleCellClick}
|
||||
onPageChange={(e) => { }}
|
||||
resetSelection={resetSelection}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
]}
|
||||
</BaseLayout>
|
||||
)
|
||||
}}
|
||||
isLoading={isLoadingResult}
|
||||
// Excel 다운로드 핸들러 추가
|
||||
onExcelDownload={handleExcelDownload}
|
||||
onDeleteFile={handleFileDelete}
|
||||
selectedRowData={selectedRowData}
|
||||
>
|
||||
{[<div key='table1' className="h-[55vh] flex flex-col p-4 border rounded-lg bg-white shadow-md">
|
||||
<div className="mb-[1.2rem] text-xl font-medium">○ RDII 분석결과 현황</div>
|
||||
<div className="flex-grow overflow-auto">
|
||||
<UiTable
|
||||
headName=''
|
||||
headlist={tableColumn}
|
||||
tableData={tableData}
|
||||
total={totalData}
|
||||
pageSize={10}
|
||||
children={undefined}
|
||||
cellClick={handleCellClick}
|
||||
onPageChange={(e) => { }}
|
||||
resetSelection={resetSelection}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
]}
|
||||
</BaseLayout>
|
||||
)
|
||||
}
|
||||
|
||||
export default Result
|
||||
@@ -10,260 +10,260 @@ import { handleDelete } from '@/components/lib/utils'
|
||||
|
||||
|
||||
const formSchema = z.object({
|
||||
enoCd: z.string().default('0'), // 환경청코드
|
||||
ctpvCd: z.string().default('0'), // 시도코드
|
||||
sggCd: z.object({ // 시군구
|
||||
val: z.string().default('0'),
|
||||
text: z.string().default('전체'),
|
||||
}).default({ val: '0', text: '전체' }),
|
||||
swageTmplntCd: z.string().default('0'), // 하수처리장
|
||||
swtrmtZoneCd: z.string().default('0'), // 하수처리구역
|
||||
aplcnYm: z.date().default(new Date()), // 분석일자 (DatePicker와 연동)
|
||||
enoCd: z.string().default('0'), // 환경청코드
|
||||
ctpvCd: z.string().default('0'), // 시도코드
|
||||
sggCd: z.object({ // 시군구
|
||||
val: z.string().default('0'),
|
||||
text: z.string().default('전체'),
|
||||
}).default({ val: '0', text: '전체' }),
|
||||
swageTmplntCd: z.string().default('0'), // 하수처리장
|
||||
swtrmtZoneCd: z.string().default('0'), // 하수처리구역
|
||||
aplcnYm: z.date().default(new Date()), // 분석일자 (DatePicker와 연동)
|
||||
})
|
||||
|
||||
type ResultFormValues = z.infer<typeof formSchema>
|
||||
|
||||
const tableColumn = [
|
||||
// { id: 'no', title: '번호' },
|
||||
{ id: 'inftwtAnlsSn', title: '분석번호' },
|
||||
{ id: 'swageTmplntNm', title: '하수처리장' },
|
||||
// { id: 'swtrmtZoneNm', title: '처리구역' },
|
||||
{ id: 'aplcnYm', title: '적용연월' },
|
||||
{ id: 'drtwSwtcRt', title: '오수 전환율' },
|
||||
{ id: 'anlsInfo', title: '침입수 분석정보' },
|
||||
{ id: 'regDt', title: '분석일자' },
|
||||
{ id: 'rgtrNm', title: '분석자' }
|
||||
// { id: 'no', title: '번호' },
|
||||
{ id: 'inftwtAnlsSn', title: '분석번호' },
|
||||
{ id: 'swageTmplntNm', title: '하수처리장' },
|
||||
// { id: 'swtrmtZoneNm', title: '처리구역' },
|
||||
{ id: 'aplcnYm', title: '적용연월' },
|
||||
{ id: 'drtwSwtcRt', title: '오수 전환율' },
|
||||
{ id: 'anlsInfo', title: '침입수 분석정보' },
|
||||
{ id: 'regDt', title: '분석일자' },
|
||||
{ id: 'rgtrNm', title: '분석자' }
|
||||
]
|
||||
|
||||
const Result = () => {
|
||||
const form = useForm<ResultFormValues>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
enoCd: '0',
|
||||
ctpvCd: '0',
|
||||
sggCd: { val: '0', text: '전체' },
|
||||
swageTmplntCd: '0',
|
||||
swtrmtZoneCd: '0',
|
||||
aplcnYm: new Date(),
|
||||
const form = useForm<ResultFormValues>({
|
||||
resolver: zodResolver(formSchema),
|
||||
defaultValues: {
|
||||
enoCd: '0',
|
||||
ctpvCd: '0',
|
||||
sggCd: { val: '0', text: '전체' },
|
||||
swageTmplntCd: '0',
|
||||
swtrmtZoneCd: '0',
|
||||
aplcnYm: new Date(),
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const [tableData, setTableData] = useState<any[]>([]); // API 조회 결과 저장 상태
|
||||
const [totalData, setTotalData] = useState(0) //총 데이터 수
|
||||
const [isLoadingResult, setIsLoadingResult] = useState<boolean>(false); // 로딩 상태
|
||||
const [selectedRowData, setSelectedRowData] = useState<any>(null);
|
||||
const [selectedRowIndex, setSelectedRowIndex] = useState<number | undefined>(undefined);
|
||||
const [resetSelection, setResetSelection] = useState<boolean>(false);
|
||||
|
||||
|
||||
const handleSearchButtonClick = async (customFormValues?: Partial<ResultFormValues>) => {
|
||||
setIsLoadingResult(true);
|
||||
const formValues = customFormValues || form.getValues(); // react-hook-form에서 현재 값 가져오기
|
||||
console.log("formValues", formValues);
|
||||
|
||||
let inftwtAnlsSearchDto;
|
||||
|
||||
if (customFormValues) {
|
||||
inftwtAnlsSearchDto = {
|
||||
"aplcnYm": formValues.aplcnYm,
|
||||
"enoCd": formValues.enoCd,
|
||||
"ctpvCd": formValues.ctpvCd,
|
||||
"sggCd": formValues.sggCd,
|
||||
"swtrmtZoneCd": formValues.swtrmtZoneCd,
|
||||
};
|
||||
} else {
|
||||
inftwtAnlsSearchDto = {
|
||||
"aplcnYm": formValues.aplcnYm ? formValues.aplcnYm.toISOString().substring(0, 7).replace("-", "") : null,
|
||||
"enoCd": formValues.enoCd === '0' ? null : formValues.enoCd,
|
||||
"ctpvCd": formValues.ctpvCd === '0' ? null : formValues.ctpvCd,
|
||||
"sggCd": formValues.sggCd?.val === '0' ? null : formValues.sggCd?.val,
|
||||
"swtrmtZoneCd": formValues.swtrmtZoneCd === '0' ? null : formValues.swtrmtZoneCd,
|
||||
};
|
||||
}
|
||||
console.log("Search DTO:", inftwtAnlsSearchDto);
|
||||
|
||||
const queryParams = new URLSearchParams({
|
||||
page: '0',
|
||||
size: '10000000',
|
||||
sort: 'inftwtAnlsSn,desc'
|
||||
});
|
||||
Object.entries(inftwtAnlsSearchDto).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && String(value).trim() !== '') { // undefined, null, 빈 문자열이 아닌 경우에만 추가
|
||||
queryParams.append(key, String(value));
|
||||
}
|
||||
});
|
||||
const queryString = queryParams.toString();
|
||||
|
||||
console.log("API: ", `v1/inftwtAnls${queryString ? `?${queryString}` : ''}`);
|
||||
try {
|
||||
const data = await getData(`v1/inftwtAnls${queryString ? `?${queryString}` : ''}`);
|
||||
setTableData(data.content || []);
|
||||
setTotalData(tableData.length);
|
||||
console.log("response:", data);
|
||||
console.log("response data:", data.content || []);
|
||||
setIsLoadingResult(false);
|
||||
} catch (error) {
|
||||
setIsLoadingResult(false);
|
||||
console.error("Error fetching analysis result data:", error);
|
||||
alert("분석결과 조회 중 오류가 발생했습니다.");
|
||||
setTableData([]);
|
||||
setTotalData(0);
|
||||
}
|
||||
};
|
||||
|
||||
// 테이블 셀 클릭 핸들러 추가
|
||||
const handleCellClick = (index: number | undefined, headId: string) => {
|
||||
if (index !== undefined && tableData[index]) {
|
||||
setSelectedRowData(tableData[index]);
|
||||
setSelectedRowIndex(index);
|
||||
console.log("Selected row data:", tableData[index]);
|
||||
}
|
||||
};
|
||||
|
||||
// Excel 다운로드 핸들러 추가
|
||||
const handleExcelDownload = async () => {
|
||||
setIsLoadingResult(true);
|
||||
console.log("Selected row data for download:", selectedRowData);
|
||||
if (!selectedRowData || selectedRowData.inftwtAnlsSn === undefined) {
|
||||
setIsLoadingResult(false);
|
||||
alert('다운로드할 데이터를 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await getData(`v1/inftwtAnlsRslt/ppl/${selectedRowData.inftwtAnlsSn}`);
|
||||
|
||||
// 선택된 행 데이터를 Excel 다운로드 형식으로 변환
|
||||
const excelData = data.content; // 배열로 감싸서 전달
|
||||
|
||||
// 파일명 생성 (분석번호 기반)
|
||||
const fileName = `침입수분석결과_${selectedRowData.inftwtAnlsSn}_${selectedRowData.aplcnYm}`;
|
||||
|
||||
downloadExcel({
|
||||
data: excelData as JSON[],
|
||||
fileName: fileName,
|
||||
sheetNameArray: ["침입수 분석결과"],
|
||||
headerRange: "A1:D1" // 테이블 컬럼 수에 맞춰 조정
|
||||
});
|
||||
setIsLoadingResult(false);
|
||||
} catch (error) {
|
||||
setIsLoadingResult(false);
|
||||
console.error("Excel 다운로드 중 오류 발생:", error);
|
||||
alert("Excel 다운로드 중 오류가 발생했습니다. 다시 시도해주세요.");
|
||||
}
|
||||
};
|
||||
|
||||
// 파일 삭제 핸들러 추가
|
||||
const handleFileDelete = async () => {
|
||||
setIsLoadingResult(true);
|
||||
console.log("Selected row data for deletion:", selectedRowData);
|
||||
if (!selectedRowData || selectedRowData.inftwtAnlsSn === undefined) {
|
||||
setIsLoadingResult(false);
|
||||
alert('삭제할 파일을 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
// 삭제 확인 대화상자
|
||||
const confirmDelete = window.confirm('선택된 분석 결과를 삭제하시겠습니까?');
|
||||
if (!confirmDelete) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// // 삭제 API 호출
|
||||
console.log("Deleting analysis result with inftwtAnlsSn:", selectedRowData.inftwtAnlsSn);
|
||||
const response1 = await deleteData(`v1/inftwtAnlsRslt/${selectedRowData.inftwtAnlsSn}`);
|
||||
const response2 = await deleteData(`v1/inftwtAnls/${selectedRowData.inftwtAnlsSn}`);
|
||||
// 첨부파일(inp파일) 삭제 API 호출
|
||||
handleDelete(selectedRowData.atflCd, {
|
||||
handleDeleteStart: () => console.log("파일 삭제 시작"),
|
||||
handleDeleteSuccess: (response) => console.log("파일 삭제 성공:", response),
|
||||
handleDeleteError: (error) => console.error("파일 삭제 실패:", error),
|
||||
});
|
||||
console.log("File delete response1:", response1);
|
||||
console.log("File delete response2:", response2);
|
||||
|
||||
// 테이블 선택 상태 초기화 트리거
|
||||
setResetSelection(true);
|
||||
|
||||
// 삭제 후 테이블 새로고침을 위해 재검색
|
||||
const customFormValues: Partial<ResultFormValues> = {
|
||||
aplcnYm: selectedRowData.aplcnYm || null,
|
||||
enoCd: selectedRowData.enoCd || null,
|
||||
ctpvCd: selectedRowData.ctpvCd || null,
|
||||
sggCd: selectedRowData.sggCd || null,
|
||||
swtrmtZoneCd: selectedRowData.swtrmtZoneCd || null,
|
||||
};
|
||||
await handleSearchButtonClick(customFormValues);
|
||||
|
||||
// 선택된 행 초기화
|
||||
setSelectedRowData(null);
|
||||
setSelectedRowIndex(undefined);
|
||||
setIsLoadingResult(false);
|
||||
|
||||
alert('삭제가 완료되었습니다.');
|
||||
|
||||
setTimeout(() => {
|
||||
setResetSelection(false);
|
||||
}, 100);
|
||||
} catch (error) {
|
||||
setIsLoadingResult(false);
|
||||
console.error("분석결과 파일 삭제 중 오류 발생:", error);
|
||||
alert("분석결과 파일 삭제 중 오류가 발생했습니다. 다시 시도해주세요.");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseLayout
|
||||
selectBox={{ title: '분석 대상지역 선택', searchChk: true, onSearchClick: handleSearchButtonClick }}
|
||||
titleName={{ mainTitle: '분석결과', firstCardTitle: '분석이력' }}
|
||||
form={form} // form 인스턴스 전달
|
||||
onSubmitEvent={() => { }} // BaseLayout의 form 태그에 연결될 onSubmit
|
||||
graphBtn={true}
|
||||
showDatePicker={true}
|
||||
enoCd={form.watch("enoCd")}
|
||||
setEnoCd={(val) => form.setValue("enoCd", val)}
|
||||
ctpvCd={form.watch("ctpvCd")}
|
||||
setCtpvCd={(val) => form.setValue("ctpvCd", val)}
|
||||
sigungu={form.watch("sggCd")}
|
||||
setSigungu={(val) => form.setValue("sggCd", val)}
|
||||
selectedSwageTreatmentPlant={form.watch("swageTmplntCd")}
|
||||
onSwageTreatmentPlantChange={(val) => form.setValue("swageTmplntCd", String(val))}
|
||||
selectedSwageProcessingZone={form.watch("swtrmtZoneCd")}
|
||||
onSwageProcessingZoneChange={(val) => form.setValue("swtrmtZoneCd", String(val))}
|
||||
aplcnYm={form.watch("aplcnYm")}
|
||||
setAplcnYm={(val) => {
|
||||
if (val instanceof Date) {
|
||||
form.setValue("aplcnYm", val)
|
||||
} else if (val) {
|
||||
form.setValue("aplcnYm", new Date(val))
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const [tableData, setTableData] = useState<any[]>([]); // API 조회 결과 저장 상태
|
||||
const [totalData, setTotalData] = useState(0) //총 데이터 수
|
||||
const [isLoadingResult, setIsLoadingResult] = useState<boolean>(false); // 로딩 상태
|
||||
const [selectedRowData, setSelectedRowData] = useState<any>(null);
|
||||
const [selectedRowIndex, setSelectedRowIndex] = useState<number | undefined>(undefined);
|
||||
const [resetSelection, setResetSelection] = useState<boolean>(false);
|
||||
|
||||
|
||||
const handleSearchButtonClick = async (customFormValues?: Partial<ResultFormValues>) => {
|
||||
setIsLoadingResult(true);
|
||||
const formValues = customFormValues || form.getValues(); // react-hook-form에서 현재 값 가져오기
|
||||
console.log("formValues", formValues);
|
||||
|
||||
let inftwtAnlsSearchDto;
|
||||
|
||||
if (customFormValues) {
|
||||
inftwtAnlsSearchDto = {
|
||||
"aplcnYm": formValues.aplcnYm,
|
||||
"enoCd": formValues.enoCd,
|
||||
"ctpvCd": formValues.ctpvCd,
|
||||
"sggCd": formValues.sggCd,
|
||||
"swtrmtZoneCd": formValues.swtrmtZoneCd,
|
||||
};
|
||||
} else {
|
||||
inftwtAnlsSearchDto = {
|
||||
"aplcnYm": formValues.aplcnYm ? formValues.aplcnYm.toISOString().substring(0, 7).replace("-", "") : null,
|
||||
"enoCd": formValues.enoCd === '0' ? null : formValues.enoCd,
|
||||
"ctpvCd": formValues.ctpvCd === '0' ? null : formValues.ctpvCd,
|
||||
"sggCd": formValues.sggCd?.val === '0' ? null : formValues.sggCd?.val,
|
||||
"swtrmtZoneCd": formValues.swtrmtZoneCd === '0' ? null : formValues.swtrmtZoneCd,
|
||||
};
|
||||
}
|
||||
console.log("Search DTO:", inftwtAnlsSearchDto);
|
||||
|
||||
const queryParams = new URLSearchParams({
|
||||
page: '0',
|
||||
size: '10000000',
|
||||
sort: 'inftwtAnlsSn,desc'
|
||||
});
|
||||
Object.entries(inftwtAnlsSearchDto).forEach(([key, value]) => {
|
||||
if (value !== undefined && value !== null && String(value).trim() !== '') { // undefined, null, 빈 문자열이 아닌 경우에만 추가
|
||||
queryParams.append(key, String(value));
|
||||
}
|
||||
});
|
||||
const queryString = queryParams.toString();
|
||||
|
||||
console.log("API: ", `v1/inftwtAnls${queryString ? `?${queryString}` : ''}`);
|
||||
try {
|
||||
const data = await getData(`v1/inftwtAnls${queryString ? `?${queryString}` : ''}`);
|
||||
setTableData(data.content || []);
|
||||
setTotalData(tableData.length);
|
||||
console.log("response:", data);
|
||||
console.log("response data:", data.content || []);
|
||||
setIsLoadingResult(false);
|
||||
} catch (error) {
|
||||
setIsLoadingResult(false);
|
||||
console.error("Error fetching analysis result data:", error);
|
||||
alert("분석결과 조회 중 오류가 발생했습니다.");
|
||||
setTableData([]);
|
||||
setTotalData(0);
|
||||
}
|
||||
};
|
||||
|
||||
// 테이블 셀 클릭 핸들러 추가
|
||||
const handleCellClick = (index: number | undefined, headId: string) => {
|
||||
if (index !== undefined && tableData[index]) {
|
||||
setSelectedRowData(tableData[index]);
|
||||
setSelectedRowIndex(index);
|
||||
console.log("Selected row data:", tableData[index]);
|
||||
}
|
||||
};
|
||||
|
||||
// Excel 다운로드 핸들러 추가
|
||||
const handleExcelDownload = async () => {
|
||||
setIsLoadingResult(true);
|
||||
console.log("Selected row data for download:", selectedRowData);
|
||||
if (!selectedRowData || selectedRowData.inftwtAnlsSn === undefined) {
|
||||
setIsLoadingResult(false);
|
||||
alert('다운로드할 데이터를 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const data = await getData(`v1/inftwtAnlsRslt/ppl/${selectedRowData.inftwtAnlsSn}`);
|
||||
|
||||
// 선택된 행 데이터를 Excel 다운로드 형식으로 변환
|
||||
const excelData = data.content; // 배열로 감싸서 전달
|
||||
|
||||
// 파일명 생성 (분석번호 기반)
|
||||
const fileName = `침입수분석결과_${selectedRowData.inftwtAnlsSn}_${selectedRowData.aplcnYm}`;
|
||||
|
||||
downloadExcel({
|
||||
data: excelData as JSON[],
|
||||
fileName: fileName,
|
||||
sheetNameArray: ["침입수 분석결과"],
|
||||
headerRange: "A1:D1" // 테이블 컬럼 수에 맞춰 조정
|
||||
});
|
||||
setIsLoadingResult(false);
|
||||
} catch (error) {
|
||||
setIsLoadingResult(false);
|
||||
console.error("Excel 다운로드 중 오류 발생:", error);
|
||||
alert("Excel 다운로드 중 오류가 발생했습니다. 다시 시도해주세요.");
|
||||
}
|
||||
};
|
||||
|
||||
// 파일 삭제 핸들러 추가
|
||||
const handleFileDelete = async () => {
|
||||
setIsLoadingResult(true);
|
||||
console.log("Selected row data for deletion:", selectedRowData);
|
||||
if (!selectedRowData || selectedRowData.inftwtAnlsSn === undefined) {
|
||||
setIsLoadingResult(false);
|
||||
alert('삭제할 파일을 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
// 삭제 확인 대화상자
|
||||
const confirmDelete = window.confirm('선택된 분석 결과를 삭제하시겠습니까?');
|
||||
if (!confirmDelete) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// // 삭제 API 호출
|
||||
console.log("Deleting analysis result with inftwtAnlsSn:", selectedRowData.inftwtAnlsSn);
|
||||
const response1 = await deleteData(`v1/inftwtAnlsRslt/${selectedRowData.inftwtAnlsSn}`);
|
||||
const response2 = await deleteData(`v1/inftwtAnls/${selectedRowData.inftwtAnlsSn}`);
|
||||
// 첨부파일(inp파일) 삭제 API 호출
|
||||
handleDelete(selectedRowData.atflCd, {
|
||||
handleDeleteStart: () => console.log("파일 삭제 시작"),
|
||||
handleDeleteSuccess: (response) => console.log("파일 삭제 성공:", response),
|
||||
handleDeleteError: (error) => console.error("파일 삭제 실패:", error),
|
||||
});
|
||||
console.log("File delete response1:", response1);
|
||||
console.log("File delete response2:", response2);
|
||||
|
||||
// 테이블 선택 상태 초기화 트리거
|
||||
setResetSelection(true);
|
||||
|
||||
// 삭제 후 테이블 새로고침을 위해 재검색
|
||||
const customFormValues: Partial<ResultFormValues> = {
|
||||
aplcnYm: selectedRowData.aplcnYm || null,
|
||||
enoCd: selectedRowData.enoCd || null,
|
||||
ctpvCd: selectedRowData.ctpvCd || null,
|
||||
sggCd: selectedRowData.sggCd || null,
|
||||
swtrmtZoneCd: selectedRowData.swtrmtZoneCd || null,
|
||||
};
|
||||
await handleSearchButtonClick(customFormValues);
|
||||
|
||||
// 선택된 행 초기화
|
||||
setSelectedRowData(null);
|
||||
setSelectedRowIndex(undefined);
|
||||
setIsLoadingResult(false);
|
||||
|
||||
alert('삭제가 완료되었습니다.');
|
||||
|
||||
setTimeout(() => {
|
||||
setResetSelection(false);
|
||||
}, 100);
|
||||
} catch (error) {
|
||||
setIsLoadingResult(false);
|
||||
console.error("분석결과 파일 삭제 중 오류 발생:", error);
|
||||
alert("분석결과 파일 삭제 중 오류가 발생했습니다. 다시 시도해주세요.");
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<BaseLayout
|
||||
selectBox={{ title: '분석 대상지역 선택', searchChk: true, onSearchClick: handleSearchButtonClick }}
|
||||
titleName={{ mainTitle: '분석결과', firstCardTitle: '분석이력' }}
|
||||
form={form} // form 인스턴스 전달
|
||||
onSubmitEvent={() => { }} // BaseLayout의 form 태그에 연결될 onSubmit
|
||||
graphBtn={true}
|
||||
showDatePicker={true}
|
||||
enoCd={form.watch("enoCd")}
|
||||
setEnoCd={(val) => form.setValue("enoCd", val)}
|
||||
ctpvCd={form.watch("ctpvCd")}
|
||||
setCtpvCd={(val) => form.setValue("ctpvCd", val)}
|
||||
sigungu={form.watch("sggCd")}
|
||||
setSigungu={(val) => form.setValue("sggCd", val)}
|
||||
selectedSwageTreatmentPlant={form.watch("swageTmplntCd")}
|
||||
onSwageTreatmentPlantChange={(val) => form.setValue("swageTmplntCd", String(val))}
|
||||
selectedSwageProcessingZone={form.watch("swtrmtZoneCd")}
|
||||
onSwageProcessingZoneChange={(val) => form.setValue("swtrmtZoneCd", String(val))}
|
||||
aplcnYm={form.watch("aplcnYm")}
|
||||
setAplcnYm={(val) => {
|
||||
if (val instanceof Date) {
|
||||
form.setValue("aplcnYm", val)
|
||||
} else if (val) {
|
||||
form.setValue("aplcnYm", new Date(val))
|
||||
}
|
||||
}}
|
||||
isLoading={isLoadingResult} // 로딩 상태 전달
|
||||
// Excel 다운로드 핸들러 추가
|
||||
onExcelDownload={handleExcelDownload}
|
||||
onDeleteFile={handleFileDelete}
|
||||
selectedRowData={selectedRowData}
|
||||
>
|
||||
{[<div key='table1' className="h-[55vh] flex flex-col p-4 border rounded-lg bg-white shadow-md">
|
||||
<div className="mb-[1.2rem] text-xl font-medium">○ 침입수 산정결과 현황</div>
|
||||
<div className="flex-grow overflow-auto">
|
||||
<UiTable
|
||||
headName=''
|
||||
headlist={tableColumn}
|
||||
tableData={tableData}
|
||||
total={totalData}
|
||||
pageSize={10}
|
||||
children={undefined}
|
||||
cellClick={handleCellClick}
|
||||
onPageChange={(e) => { }}
|
||||
resetSelection={resetSelection}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
]}
|
||||
</BaseLayout>
|
||||
)
|
||||
}}
|
||||
isLoading={isLoadingResult} // 로딩 상태 전달
|
||||
// Excel 다운로드 핸들러 추가
|
||||
onExcelDownload={handleExcelDownload}
|
||||
onDeleteFile={handleFileDelete}
|
||||
selectedRowData={selectedRowData}
|
||||
>
|
||||
{[<div key='table1' className="h-[55vh] flex flex-col p-4 border rounded-lg bg-white shadow-md">
|
||||
<div className="mb-[1.2rem] text-xl font-medium">○ 침입수 산정결과 현황</div>
|
||||
<div className="flex-grow overflow-auto">
|
||||
<UiTable
|
||||
headName=''
|
||||
headlist={tableColumn}
|
||||
tableData={tableData}
|
||||
total={totalData}
|
||||
pageSize={10}
|
||||
children={undefined}
|
||||
cellClick={handleCellClick}
|
||||
onPageChange={(e) => { }}
|
||||
resetSelection={resetSelection}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
]}
|
||||
</BaseLayout>
|
||||
)
|
||||
}
|
||||
|
||||
export default Result
|
||||
@@ -12,150 +12,150 @@ import { deleteData, postData } from '@/components/lib/api'
|
||||
import LoadingOverlay from '@/components/ui/LoadingBar'
|
||||
|
||||
const Page = () => {
|
||||
// 조회조건
|
||||
const [fromDate, setFromDate] = useState(() => { const date = new Date(); date.setDate(date.getDate() - 7); return date })
|
||||
const [toDate, setToDate] = useState(new Date())
|
||||
const [manageYear, setManageYear] = useState(yearOptions[0]?.val || '');
|
||||
const [mngQu, setMngQu] = useState(yearOptions[0]?.val || '');
|
||||
const [usr, setUsr] = useState('')
|
||||
const [pjNm, setpjNm] = useState('')
|
||||
// 파일업로드 관련 상태
|
||||
const [fileNm, setFileNm] = useState('') //파일명
|
||||
const [atflId, setAtflId] = useState('') //파일 ID
|
||||
const [sbmrgPrevntBizSn, setSbmrgPrevntBizSn] = useState('') //파일 일련번호
|
||||
const [tableData, setTableData] = useState<any>([{}]) //테이블 데이터
|
||||
const [curPage, setCurPage] = useState(1) //현재 페이지
|
||||
const [totalData, setTotalData] = useState(0) //총 데이터 수
|
||||
const [isLoading, setIsLoading] = useState(true) //로딩 상태
|
||||
const fetchData = async () => {
|
||||
setIsLoading(true)
|
||||
searchCmmnCd('SITEKNDCD')
|
||||
try {
|
||||
const { data, total } = await searchData('v1/sbmrgPrevnt', {
|
||||
fromRegDt: formatDate(fromDate),
|
||||
fromMngYr: manageYear,
|
||||
toMngYr: mngQu,
|
||||
toRegDt: formatDate(toDate),
|
||||
atflNm: pjNm,
|
||||
rgtrNm: usr,
|
||||
page: curPage - 1,
|
||||
size: '10'
|
||||
})
|
||||
setTableData(data)
|
||||
setTotalData(total)
|
||||
}
|
||||
catch (error) { console.error('Error fetching data:', error) }
|
||||
finally { setIsLoading(false); }
|
||||
// 조회조건
|
||||
const [fromDate, setFromDate] = useState(() => { const date = new Date(); date.setDate(date.getDate() - 7); return date })
|
||||
const [toDate, setToDate] = useState(new Date())
|
||||
const [manageYear, setManageYear] = useState(yearOptions[0]?.val || '');
|
||||
const [mngQu, setMngQu] = useState(yearOptions[0]?.val || '');
|
||||
const [usr, setUsr] = useState('')
|
||||
const [pjNm, setpjNm] = useState('')
|
||||
// 파일업로드 관련 상태
|
||||
const [fileNm, setFileNm] = useState('') //파일명
|
||||
const [atflId, setAtflId] = useState('') //파일 ID
|
||||
const [sbmrgPrevntBizSn, setSbmrgPrevntBizSn] = useState('') //파일 일련번호
|
||||
const [tableData, setTableData] = useState<any>([{}]) //테이블 데이터
|
||||
const [curPage, setCurPage] = useState(1) //현재 페이지
|
||||
const [totalData, setTotalData] = useState(0) //총 데이터 수
|
||||
const [isLoading, setIsLoading] = useState(true) //로딩 상태
|
||||
const fetchData = async () => {
|
||||
setIsLoading(true)
|
||||
searchCmmnCd('SITEKNDCD')
|
||||
try {
|
||||
const { data, total } = await searchData('v1/sbmrgPrevnt', {
|
||||
fromRegDt: formatDate(fromDate),
|
||||
fromMngYr: manageYear,
|
||||
toMngYr: mngQu,
|
||||
toRegDt: formatDate(toDate),
|
||||
atflNm: pjNm,
|
||||
rgtrNm: usr,
|
||||
page: curPage - 1,
|
||||
size: '10'
|
||||
})
|
||||
setTableData(data)
|
||||
setTotalData(total)
|
||||
}
|
||||
useEffect(() => {
|
||||
setCurPage(1)
|
||||
catch (error) { console.error('Error fetching data:', error) }
|
||||
finally { setIsLoading(false); }
|
||||
}
|
||||
useEffect(() => {
|
||||
setCurPage(1)
|
||||
fetchData()
|
||||
}, [])
|
||||
// 파일 다운로드
|
||||
const isChckFile = () => {
|
||||
if (fileNm === '' || fileNm === undefined) {
|
||||
alert('파일을 선택해주세요')
|
||||
return false
|
||||
}
|
||||
handleDownload(`/attach-service/api/v1/download/${atflId}`, fileNm)
|
||||
}
|
||||
// 선택 파일 삭제
|
||||
const isDeleteFile = async () => {
|
||||
if (sbmrgPrevntBizSn === '' || sbmrgPrevntBizSn === undefined) {
|
||||
alert('삭제할 파일을 선택해주세요')
|
||||
return false
|
||||
}
|
||||
const data = await deleteData(`/attach-service/api/v1/atfl/attachments/${atflId}`)
|
||||
if (data) {
|
||||
const delData = await deleteData(`v1/sbmrgPrevnt/${sbmrgPrevntBizSn}`)
|
||||
if (delData.status === 200) {
|
||||
alert('삭제되었습니다.')
|
||||
fetchData()
|
||||
}, [])
|
||||
// 파일 다운로드
|
||||
const isChckFile = () => {
|
||||
if (fileNm === '' || fileNm === undefined) {
|
||||
alert('파일을 선택해주세요')
|
||||
return false
|
||||
}
|
||||
handleDownload(`/attach-service/api/v1/download/${atflId}`, fileNm)
|
||||
}
|
||||
}
|
||||
// 선택 파일 삭제
|
||||
const isDeleteFile = async () => {
|
||||
if (sbmrgPrevntBizSn === '' || sbmrgPrevntBizSn === undefined) {
|
||||
alert('삭제할 파일을 선택해주세요')
|
||||
return false
|
||||
}
|
||||
const data = await deleteData(`/attach-service/api/v1/atfl/attachments/${atflId}`)
|
||||
if (data) {
|
||||
const delData = await deleteData(`v1/sbmrgPrevnt/${sbmrgPrevntBizSn}`)
|
||||
if (delData.status === 200) {
|
||||
alert('삭제되었습니다.')
|
||||
fetchData()
|
||||
}
|
||||
}
|
||||
}
|
||||
// 현황 등록
|
||||
const registFile = async (fileName: string, year: string, quater: string, cd: string, id: string) => {
|
||||
const data = await postData('v1/sbmrgPrevnt', {
|
||||
atflNm: fileName,
|
||||
mngYr: year,
|
||||
mngQu: quater,
|
||||
atflCd: cd,
|
||||
atflId: id,
|
||||
sbmrgPrevntBizNm: fileName
|
||||
})
|
||||
if (data) { alert('파일이 등록되었습니다.'); fetchData() }
|
||||
}
|
||||
return (
|
||||
<ContentLayout title='도시침수 예방사업' isLoading={isLoading}>
|
||||
<SearchDiv>
|
||||
<SearchFormLeft>
|
||||
<div className='flex items-center gap-5'>
|
||||
<div className='flex items-center gap-2'>
|
||||
<Label className="w-20 text-xl text-gray-600">등록기간</Label>
|
||||
<DatePicker mode={'single'}
|
||||
selected={fromDate}
|
||||
onSelect={(date: Date | undefined) => { if (date) setFromDate(date) }} />
|
||||
<span>~</span>
|
||||
<DatePicker mode={'single'}
|
||||
selected={toDate}
|
||||
onSelect={(date: Date | undefined) => { if (date) setToDate(date) }} />
|
||||
</div>
|
||||
<div className='flex items-center gap-2'>
|
||||
<Label className="w-20 text-xl text-gray-600">관리년도</Label>
|
||||
<SelectBox
|
||||
className="w-[90px] mr-3 ml-4"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label=""
|
||||
selectArray={yearOptions}
|
||||
onSelectValue={(value: string | number) => setManageYear(String(value))}
|
||||
/>
|
||||
<span>~</span>
|
||||
<SelectBox
|
||||
className="w-[90px] mr-3 ml-4"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label=""
|
||||
selectArray={yearOptions}
|
||||
onSelectValue={(value: string | number) => setMngQu(String(value))}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex items-center'>
|
||||
<Label className="w-20 text-xl text-gray-600">등록자</Label>
|
||||
<Input
|
||||
className='w-[100px]'
|
||||
value={usr}
|
||||
onChange={(e) => setUsr(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex items-center'>
|
||||
<Label className="w-20 text-xl text-gray-600">파일명</Label>
|
||||
<Input
|
||||
className='w-[100px]'
|
||||
value={pjNm}
|
||||
onChange={(e) => setpjNm(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</SearchFormLeft>
|
||||
<SearchFormRight>
|
||||
<Button className="px-4 py-2 bg-blue-600 text-white rounded-sm text-sm font-medium hover:bg-blue-700" size="sm" onClick={async () => await fetchData()} >조회</Button>
|
||||
</SearchFormRight>
|
||||
</SearchDiv>
|
||||
<ListDiv className="mt-4">
|
||||
<div className="card-header flex items-center justify-between">
|
||||
<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" onClick={isChckFile} >파일 다운로드</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={isDeleteFile} >삭제</Button>
|
||||
</div>
|
||||
</div>
|
||||
<UiTable headName='' tableData={tableData} headlist={tableHeadlist} pageSize={10} total={totalData} cellClick={(index) => { if (index != null) { const fileName = tableData[index]?.sbmrgPrevntBizNm ?? ""; setFileNm(fileName); const id = tableData[index]?.atflId ?? ''; setAtflId(id); setSbmrgPrevntBizSn(tableData[index]?.sbmrgPrevntBizSn); } }} onPageChange={async (page: number) => { setCurPage(page); await fetchData() }}></UiTable>
|
||||
</ListDiv>
|
||||
<ListDiv className="mt-4">
|
||||
<OperFileUploadForm title='도시침수 예방사업 사업추진현황 등록' onSubmit={async (file, year, quater, cd, id) => { await registFile(file, year, quater, cd, id) }} />
|
||||
</ListDiv>
|
||||
</ContentLayout>
|
||||
)
|
||||
}
|
||||
// 현황 등록
|
||||
const registFile = async (fileName: string, year: string, quater: string, cd: string, id: string) => {
|
||||
const data = await postData('v1/sbmrgPrevnt', {
|
||||
atflNm: fileName,
|
||||
mngYr: year,
|
||||
mngQu: quater,
|
||||
atflCd: cd,
|
||||
atflId: id,
|
||||
sbmrgPrevntBizNm: fileName
|
||||
})
|
||||
if (data) { alert('파일이 등록되었습니다.'); fetchData() }
|
||||
}
|
||||
return (
|
||||
<ContentLayout title='도시침수 예방사업' isLoading={isLoading}>
|
||||
<SearchDiv>
|
||||
<SearchFormLeft>
|
||||
<div className='flex items-center gap-5'>
|
||||
<div className='flex items-center gap-2'>
|
||||
<Label className="w-20 text-xl text-gray-600">등록기간</Label>
|
||||
<DatePicker mode={'single'}
|
||||
selected={fromDate}
|
||||
onSelect={(date: Date | undefined) => { if (date) setFromDate(date) }} />
|
||||
<span>~</span>
|
||||
<DatePicker mode={'single'}
|
||||
selected={toDate}
|
||||
onSelect={(date: Date | undefined) => { if (date) setToDate(date) }} />
|
||||
</div>
|
||||
<div className='flex items-center gap-2'>
|
||||
<Label className="w-20 text-xl text-gray-600">관리년도</Label>
|
||||
<SelectBox
|
||||
className="w-[90px] mr-3 ml-4"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label=""
|
||||
selectArray={yearOptions}
|
||||
onSelectValue={(value: string | number) => setManageYear(String(value))}
|
||||
/>
|
||||
<span>~</span>
|
||||
<SelectBox
|
||||
className="w-[90px] mr-3 ml-4"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label=""
|
||||
selectArray={yearOptions}
|
||||
onSelectValue={(value: string | number) => setMngQu(String(value))}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex items-center'>
|
||||
<Label className="w-20 text-xl text-gray-600">등록자</Label>
|
||||
<Input
|
||||
className='w-[100px]'
|
||||
value={usr}
|
||||
onChange={(e) => setUsr(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex items-center'>
|
||||
<Label className="w-20 text-xl text-gray-600">파일명</Label>
|
||||
<Input
|
||||
className='w-[100px]'
|
||||
value={pjNm}
|
||||
onChange={(e) => setpjNm(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</SearchFormLeft>
|
||||
<SearchFormRight>
|
||||
<Button className="px-4 py-2 bg-blue-600 text-white rounded-sm text-sm font-medium hover:bg-blue-700" size="sm" onClick={async () => await fetchData()} >조회</Button>
|
||||
</SearchFormRight>
|
||||
</SearchDiv>
|
||||
<ListDiv className="mt-4">
|
||||
<div className="card-header flex items-center justify-between">
|
||||
<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" onClick={isChckFile} >파일 다운로드</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={isDeleteFile} >삭제</Button>
|
||||
</div>
|
||||
</div>
|
||||
<UiTable headName='' tableData={tableData} headlist={tableHeadlist} pageSize={10} total={totalData} cellClick={(index) => { if (index != null) { const fileName = tableData[index]?.sbmrgPrevntBizNm ?? ""; setFileNm(fileName); const id = tableData[index]?.atflId ?? ''; setAtflId(id); setSbmrgPrevntBizSn(tableData[index]?.sbmrgPrevntBizSn); } }} onPageChange={async (page: number) => { setCurPage(page); await fetchData() }}></UiTable>
|
||||
</ListDiv>
|
||||
<ListDiv className="mt-4">
|
||||
<OperFileUploadForm title='도시침수 예방사업 사업추진현황 등록' onSubmit={async (file, year, quater, cd, id) => { await registFile(file, year, quater, cd, id) }} />
|
||||
</ListDiv>
|
||||
</ContentLayout>
|
||||
)
|
||||
}
|
||||
|
||||
export default Page
|
||||
@@ -1,17 +1,17 @@
|
||||
export const tableHeadlist = [
|
||||
{ id: 'sbmrgPrevntBizSn', title: '일련번호' },
|
||||
{ id: 'sbmrgPrevntBizNm', title: '파일명' },
|
||||
{ id: 'mngYr', title: '년도' },
|
||||
{ id: 'mngQu', title: '분기' },
|
||||
{ id: 'regDt', title: '등록 일자' },
|
||||
{ id: 'rgtr', title: '등록자' }
|
||||
{ id: 'sbmrgPrevntBizSn', title: '일련번호' },
|
||||
{ id: 'sbmrgPrevntBizNm', title: '파일명' },
|
||||
{ id: 'mngYr', title: '년도' },
|
||||
{ id: 'mngQu', title: '분기' },
|
||||
{ id: 'regDt', title: '등록 일자' },
|
||||
{ id: 'rgtr', title: '등록자' }
|
||||
|
||||
]
|
||||
export const tableHeadlistOldPp = [
|
||||
{ id: 'dcrpPplMtncBizSn', title: '일련번호' },
|
||||
{ id: 'dcrpPplMtncBizNm', title: '파일명' },
|
||||
{ id: 'mngYr', title: '년도' },
|
||||
{ id: 'mngQu', title: '분기' },
|
||||
{ id: 'regDt', title: '등록 일자' },
|
||||
{ id: 'rgtr', title: '등록자' }
|
||||
{ id: 'dcrpPplMtncBizSn', title: '일련번호' },
|
||||
{ id: 'dcrpPplMtncBizNm', title: '파일명' },
|
||||
{ id: 'mngYr', title: '년도' },
|
||||
{ id: 'mngQu', title: '분기' },
|
||||
{ id: 'regDt', title: '등록 일자' },
|
||||
{ id: 'rgtr', title: '등록자' }
|
||||
]
|
||||
@@ -10,151 +10,152 @@ import { searchData } from '@/components/lib/common/apiHelpers'
|
||||
import { formatDate, yearOptions, handleDownload } from '@/components/lib/utils'
|
||||
import { deleteData, postData } from '@/components/lib/api'
|
||||
const Page = () => {
|
||||
const [fromDate, setFromDate] = useState<Date>(()=>{const date = new Date(); date.setDate(date.getDate()-7); return date});
|
||||
const [toDate, setToDate] = useState<Date>(new Date());
|
||||
const [manageYear, setManageYear] = useState(yearOptions[0]?.val || '');
|
||||
const [mngQu, setMngQu] = useState(yearOptions[0]?.val || '');
|
||||
const [usr, setUsr] = useState('')
|
||||
const [pjNm, setpjNm] = useState('')
|
||||
const [fileNm, setFileNm] = useState('')
|
||||
const [atflId, setAtflId] = useState('')
|
||||
const [dcrpPplMtncBizSn, setDcrpPplMtncBizSn] = useState('')
|
||||
const [tableData, setTableData] = useState<any>([{}])
|
||||
const [curPage, setCurPage] = useState(1)
|
||||
const [totalData, setTotalData] = useState(0) //총 데이터 수
|
||||
const [isLoading, setIsLoading] = useState(true) //로딩 상태
|
||||
// 데이터 조회
|
||||
const fetchData = async () => {
|
||||
setIsLoading(true)
|
||||
try {
|
||||
const {data, total} = await searchData('v1/dcrpPpl', {
|
||||
fromRegDt: formatDate(fromDate),
|
||||
fromMngYr: manageYear,
|
||||
toMngYr: mngQu,
|
||||
toRegDt: formatDate(toDate),
|
||||
dcrpPplMtncBizNm: pjNm,
|
||||
rgtrNm: usr,
|
||||
page: curPage - 1,
|
||||
size: '10'
|
||||
})
|
||||
setTableData(data)
|
||||
setTotalData(total)
|
||||
console.log(data, total)
|
||||
}catch (error) {
|
||||
console.error('Error fetching data:', error)}finally{setIsLoading(false)}
|
||||
const [fromDate, setFromDate] = useState<Date>(() => { const date = new Date(); date.setDate(date.getDate() - 7); return date });
|
||||
const [toDate, setToDate] = useState<Date>(new Date());
|
||||
const [manageYear, setManageYear] = useState(yearOptions[0]?.val || '');
|
||||
const [mngQu, setMngQu] = useState(yearOptions[0]?.val || '');
|
||||
const [usr, setUsr] = useState('')
|
||||
const [pjNm, setpjNm] = useState('')
|
||||
const [fileNm, setFileNm] = useState('')
|
||||
const [atflId, setAtflId] = useState('')
|
||||
const [dcrpPplMtncBizSn, setDcrpPplMtncBizSn] = useState('')
|
||||
const [tableData, setTableData] = useState<any>([{}])
|
||||
const [curPage, setCurPage] = useState(1)
|
||||
const [totalData, setTotalData] = useState(0) //총 데이터 수
|
||||
const [isLoading, setIsLoading] = useState(true) //로딩 상태
|
||||
// 데이터 조회
|
||||
const fetchData = async () => {
|
||||
setIsLoading(true)
|
||||
try {
|
||||
const { data, total } = await searchData('v1/dcrpPpl', {
|
||||
fromRegDt: formatDate(fromDate),
|
||||
fromMngYr: manageYear,
|
||||
toMngYr: mngQu,
|
||||
toRegDt: formatDate(toDate),
|
||||
dcrpPplMtncBizNm: pjNm,
|
||||
rgtrNm: usr,
|
||||
page: curPage - 1,
|
||||
size: '10'
|
||||
})
|
||||
setTableData(data)
|
||||
setTotalData(total)
|
||||
console.log(data, total)
|
||||
} catch (error) {
|
||||
console.error('Error fetching data:', error)
|
||||
} finally { setIsLoading(false) }
|
||||
}
|
||||
useEffect(() => {
|
||||
setCurPage(1)
|
||||
fetchData()
|
||||
}, [])
|
||||
// 파일 다운로드
|
||||
const isChckFile = () => {
|
||||
if (fileNm === '' || fileNm === undefined) {
|
||||
alert('파일을 선택해주세요')
|
||||
return false
|
||||
}
|
||||
useEffect(() => {
|
||||
setCurPage(1)
|
||||
handleDownload(`/attach-service/api/v1/download/${atflId}`, fileNm)
|
||||
return true
|
||||
}
|
||||
// 파일 삭제
|
||||
const isDeleteFile = async () => {
|
||||
if (dcrpPplMtncBizSn === '' || dcrpPplMtncBizSn === undefined) {
|
||||
alert('삭제할 파일을 선택해주세요')
|
||||
return false
|
||||
}
|
||||
const data = await deleteData(`/attach-service/api/v1/atfl/attachments/${atflId}`)
|
||||
if (data) {
|
||||
const delData = await deleteData(`v1/dcrpPpl/${dcrpPplMtncBizSn}`)
|
||||
if (delData.status == 200) {
|
||||
alert('삭제되었습니다.')
|
||||
fetchData()
|
||||
}, [])
|
||||
// 파일 다운로드
|
||||
const isChckFile = () => {
|
||||
if (fileNm === '' || fileNm === undefined) {
|
||||
alert('파일을 선택해주세요')
|
||||
return false
|
||||
}
|
||||
handleDownload(`/attach-service/api/v1/download/${atflId}`, fileNm)
|
||||
return true
|
||||
} else {
|
||||
alert('삭제에 실패했습니다.')
|
||||
}
|
||||
}
|
||||
// 파일 삭제
|
||||
const isDeleteFile = async () => {
|
||||
if (dcrpPplMtncBizSn === '' || dcrpPplMtncBizSn === undefined) {
|
||||
alert('삭제할 파일을 선택해주세요')
|
||||
return false
|
||||
}
|
||||
const data = await deleteData(`/attach-service/api/v1/atfl/attachments/${atflId}`)
|
||||
if (data) {
|
||||
const delData = await deleteData(`v1/dcrpPpl/${dcrpPplMtncBizSn}`)
|
||||
if (delData.status == 200) {
|
||||
alert('삭제되었습니다.')
|
||||
fetchData()
|
||||
} else {
|
||||
alert('삭제에 실패했습니다.')
|
||||
}
|
||||
}
|
||||
}
|
||||
// 파일 등록
|
||||
const registFile = async (fileNm: string, year: string, quater: string, cd: string, id: string) => {
|
||||
const data = await postData('v1/dcrpPpl', {
|
||||
atflNm: fileNm,
|
||||
mngYr: year,
|
||||
mngQu: quater,
|
||||
atflCd: cd,
|
||||
atflId: id,
|
||||
dcrpPplMtncBizNm: fileNm
|
||||
})
|
||||
if (data) {alert('파일이 등록되었습니다.');fetchData() }
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<SearchDiv>
|
||||
<SearchFormLeft>
|
||||
<div className='flex items-center gap-4'>
|
||||
<div className='flex items-center space-x-2'>
|
||||
<Label className="w-20 text-xl text-gray-600">등록기간</Label>
|
||||
<DatePicker mode={'single'}
|
||||
selected={fromDate}
|
||||
onSelect={(date: Date | undefined) => { if (date) setFromDate(date) }} />
|
||||
<span>~</span>
|
||||
<DatePicker mode={'single'}
|
||||
selected={toDate}
|
||||
onSelect={(date: Date | undefined) => { if (date) setToDate(date) }} />
|
||||
<div className='flex items-center gap-2'>
|
||||
<Label className="w-20 text-xl text-gray-600">관리년도</Label>
|
||||
<SelectBox
|
||||
className="w-[90px] mr-3 ml-4"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label=""
|
||||
selectArray={yearOptions}
|
||||
onSelectValue={(value: string | number) => setManageYear(String(value))}
|
||||
/>
|
||||
<span>~</span>
|
||||
<SelectBox
|
||||
className="w-[90px] mr-3 ml-4"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label=""
|
||||
selectArray={yearOptions}
|
||||
onSelectValue={(value: string | number) => setMngQu(String(value))}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex items-center'>
|
||||
<Label className="w-20 text-xl text-gray-600">등록자</Label>
|
||||
<Input
|
||||
className='w-[100px] !text-2xl'
|
||||
value={usr}
|
||||
onChange={(e) => setUsr(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex items-center'>
|
||||
<Label className="w-20 text-xl text-gray-600">파일명</Label>
|
||||
<Input
|
||||
className='w-[100px] !text-2xl'
|
||||
value={pjNm}
|
||||
onChange={(e) => {setpjNm(e.target.value)}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</SearchFormLeft>
|
||||
<SearchFormRight>
|
||||
<Button className="px-4 py-2 bg-blue-600 text-white rounded-sm text-sm font-medium hover:bg-blue-700 mr-2" size="sm" onClick={async () => {await fetchData()}}>조회</Button>
|
||||
</SearchFormRight>
|
||||
</SearchDiv>
|
||||
<ListDiv className="mt-4">
|
||||
<div className="card-header flex items-center justify-between">
|
||||
<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" onClick={isChckFile}>파일 다운로드</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={isDeleteFile}>삭제</Button>
|
||||
</div>
|
||||
</div>
|
||||
<UiTable headName='' tableData={tableData} headlist={tableHeadlistOldPp} pageSize={10} total={totalData} children={undefined} cellClick={(index) => { if (index != null) { const fileName = tableData[index]?.dcrpPplMtncBizNm ?? ""; setFileNm(fileName); const id = tableData[index]?.atflId ?? ''; setAtflId(id); setDcrpPplMtncBizSn(tableData[index]?.dcrpPplMtncBizSn) } }} onPageChange={async (page:number)=>{setCurPage(page); await fetchData()}}></UiTable>
|
||||
</ListDiv>
|
||||
<ListDiv className="mt-4">
|
||||
<OperFileUploadForm title='노후 하수관로 정비사업 주진현황 등록' onSubmit={async (file, year, quater, cd, id) => { await registFile(file, year, quater, cd, id) }} />
|
||||
</ListDiv>
|
||||
</>
|
||||
)
|
||||
}
|
||||
// 파일 등록
|
||||
const registFile = async (fileNm: string, year: string, quater: string, cd: string, id: string) => {
|
||||
const data = await postData('v1/dcrpPpl', {
|
||||
atflNm: fileNm,
|
||||
mngYr: year,
|
||||
mngQu: quater,
|
||||
atflCd: cd,
|
||||
atflId: id,
|
||||
dcrpPplMtncBizNm: fileNm
|
||||
})
|
||||
if (data) { alert('파일이 등록되었습니다.'); fetchData() }
|
||||
}
|
||||
return (
|
||||
<>
|
||||
<SearchDiv>
|
||||
<SearchFormLeft>
|
||||
<div className='flex items-center gap-4'>
|
||||
<div className='flex items-center space-x-2'>
|
||||
<Label className="w-20 text-xl text-gray-600">등록기간</Label>
|
||||
<DatePicker mode={'single'}
|
||||
selected={fromDate}
|
||||
onSelect={(date: Date | undefined) => { if (date) setFromDate(date) }} />
|
||||
<span>~</span>
|
||||
<DatePicker mode={'single'}
|
||||
selected={toDate}
|
||||
onSelect={(date: Date | undefined) => { if (date) setToDate(date) }} />
|
||||
<div className='flex items-center gap-2'>
|
||||
<Label className="w-20 text-xl text-gray-600">관리년도</Label>
|
||||
<SelectBox
|
||||
className="w-[90px] mr-3 ml-4"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label=""
|
||||
selectArray={yearOptions}
|
||||
onSelectValue={(value: string | number) => setManageYear(String(value))}
|
||||
/>
|
||||
<span>~</span>
|
||||
<SelectBox
|
||||
className="w-[90px] mr-3 ml-4"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label=""
|
||||
selectArray={yearOptions}
|
||||
onSelectValue={(value: string | number) => setMngQu(String(value))}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex items-center'>
|
||||
<Label className="w-20 text-xl text-gray-600">등록자</Label>
|
||||
<Input
|
||||
className='w-[100px] !text-2xl'
|
||||
value={usr}
|
||||
onChange={(e) => setUsr(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<div className='flex items-center'>
|
||||
<Label className="w-20 text-xl text-gray-600">파일명</Label>
|
||||
<Input
|
||||
className='w-[100px] !text-2xl'
|
||||
value={pjNm}
|
||||
onChange={(e) => { setpjNm(e.target.value) }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</SearchFormLeft>
|
||||
<SearchFormRight>
|
||||
<Button className="px-4 py-2 bg-blue-600 text-white rounded-sm text-sm font-medium hover:bg-blue-700 mr-2" size="sm" onClick={async () => { await fetchData() }}>조회</Button>
|
||||
</SearchFormRight>
|
||||
</SearchDiv>
|
||||
<ListDiv className="mt-4">
|
||||
<div className="card-header flex items-center justify-between">
|
||||
<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" onClick={isChckFile}>파일 다운로드</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={isDeleteFile}>삭제</Button>
|
||||
</div>
|
||||
</div>
|
||||
<UiTable headName='' tableData={tableData} headlist={tableHeadlistOldPp} pageSize={10} total={totalData} children={undefined} cellClick={(index) => { if (index != null) { const fileName = tableData[index]?.dcrpPplMtncBizNm ?? ""; setFileNm(fileName); const id = tableData[index]?.atflId ?? ''; setAtflId(id); setDcrpPplMtncBizSn(tableData[index]?.dcrpPplMtncBizSn) } }} onPageChange={async (page: number) => { setCurPage(page); await fetchData() }}></UiTable>
|
||||
</ListDiv>
|
||||
<ListDiv className="mt-4">
|
||||
<OperFileUploadForm title='노후 하수관로 정비사업 주진현황 등록' onSubmit={async (file, year, quater, cd, id) => { await registFile(file, year, quater, cd, id) }} />
|
||||
</ListDiv>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default Page
|
||||
@@ -1,68 +1,68 @@
|
||||
const BASE_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || '';
|
||||
|
||||
export const postData = async (url: string, data: object): Promise<any> => {
|
||||
const options: RequestInit = {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
};
|
||||
const res = await fetch(`${BASE_URL}/${url}`, options);
|
||||
if (res.ok) {
|
||||
const resData = await res.json();
|
||||
return resData;
|
||||
// const text = await res.text();
|
||||
// if (text) {
|
||||
// return JSON.parse(text); // JSON 데이터가 있으면 파싱하여 반환
|
||||
// }
|
||||
// return true; // 응답 본문이 비어 있으면 true 반환
|
||||
}
|
||||
const options: RequestInit = {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
};
|
||||
const res = await fetch(`${BASE_URL}/${url}`, options);
|
||||
if (res.ok) {
|
||||
const resData = await res.json();
|
||||
return resData;
|
||||
// const text = await res.text();
|
||||
// if (text) {
|
||||
// return JSON.parse(text); // JSON 데이터가 있으면 파싱하여 반환
|
||||
// }
|
||||
// return true; // 응답 본문이 비어 있으면 true 반환
|
||||
}
|
||||
};
|
||||
|
||||
export const postMutiPart = async (url: string, data: FormData): Promise<any> => {
|
||||
const options: RequestInit = {
|
||||
method: "POST",
|
||||
body: data,
|
||||
};
|
||||
const res = await fetch(`${BASE_URL}/${url}`, options);
|
||||
const resData = await res.json();
|
||||
return resData;
|
||||
const options: RequestInit = {
|
||||
method: "POST",
|
||||
body: data,
|
||||
};
|
||||
const res = await fetch(`${BASE_URL}/${url}`, options);
|
||||
const resData = await res.json();
|
||||
return resData;
|
||||
};
|
||||
|
||||
export const putData = async (url: string, data: object): Promise<any> => {
|
||||
const options: RequestInit = {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
};
|
||||
const res = await fetch(`${BASE_URL}/${url}`, options);
|
||||
const resData = await res.json();
|
||||
return resData;
|
||||
const options: RequestInit = {
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(data),
|
||||
};
|
||||
const res = await fetch(`${BASE_URL}/${url}`, options);
|
||||
const resData = await res.json();
|
||||
return resData;
|
||||
};
|
||||
|
||||
export const getData = async (url: string, data?: object): Promise<any> => {
|
||||
const res = await fetch(url.startsWith('/') ? `${url}` : `${BASE_URL}/${url}`, {
|
||||
method: "GET",
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
return data;
|
||||
}
|
||||
throw new Error('Failed to fetch data');
|
||||
const res = await fetch(url.startsWith('/') ? `${url}` : `${BASE_URL}/${url}`, {
|
||||
method: "GET",
|
||||
});
|
||||
if (res.ok) {
|
||||
const data = await res.json();
|
||||
return data;
|
||||
}
|
||||
throw new Error('Failed to fetch data');
|
||||
};
|
||||
|
||||
export const deleteData = async (url: string, data?: object): Promise<any> => {
|
||||
const options: RequestInit = {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: data ? JSON.stringify(data) : undefined,
|
||||
};
|
||||
const res = await fetch(url.startsWith('/') ? `${url}` : `${BASE_URL}/${url}`, options);
|
||||
if (res.status === 204) return true
|
||||
return res;
|
||||
const options: RequestInit = {
|
||||
method: "DELETE",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: data ? JSON.stringify(data) : undefined,
|
||||
};
|
||||
const res = await fetch(url.startsWith('/') ? `${url}` : `${BASE_URL}/${url}`, options);
|
||||
if (res.status === 204) return true
|
||||
return res;
|
||||
};
|
||||
@@ -1,24 +1,24 @@
|
||||
import { getData } from "../api";
|
||||
|
||||
export const searchData = async<T extends Record<string, any>>(url: string, paramObj: T) => {
|
||||
const params = new URLSearchParams(
|
||||
Object.entries(paramObj)
|
||||
.filter(([_, value]) => value !== undefined && value !== '') // 값이 있는 항목만 포함
|
||||
.reduce((acc, [key, value]) => {
|
||||
acc[key] = String(value);
|
||||
return acc;
|
||||
}, {} as Record<string, string>)
|
||||
);
|
||||
const data = await getData(`${url}?${params.toString()}`);
|
||||
return data && { data: data.content, total: data.totalElements }
|
||||
const params = new URLSearchParams(
|
||||
Object.entries(paramObj)
|
||||
.filter(([_, value]) => value !== undefined && value !== '') // 값이 있는 항목만 포함
|
||||
.reduce((acc, [key, value]) => {
|
||||
acc[key] = String(value);
|
||||
return acc;
|
||||
}, {} as Record<string, string>)
|
||||
);
|
||||
const data = await getData(`${url}?${params.toString()}`);
|
||||
return data && { data: data.content, total: data.totalElements }
|
||||
}
|
||||
|
||||
// 상위코드로 하위코드 조회
|
||||
export const searchCmmnCd = async (upCd: string) => {
|
||||
const data = await getData(`/cms-service/api/v1/code/cache/${upCd}`);
|
||||
const filteredData = data.map((item: any) => ({
|
||||
lwpoCd: item.lwpoCd,
|
||||
lwpoCdNm: item.lwpoCdNm,
|
||||
}));
|
||||
return filteredData;
|
||||
const data = await getData(`/cms-service/api/v1/code/cache/${upCd}`);
|
||||
const filteredData = data.map((item: any) => ({
|
||||
lwpoCd: item.lwpoCd,
|
||||
lwpoCdNm: item.lwpoCdNm,
|
||||
}));
|
||||
return filteredData;
|
||||
}
|
||||
@@ -1,13 +1,13 @@
|
||||
export const formatDate = (date: Date): string => {
|
||||
if (!date) return '';
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0'); // 월은 0부터 시작하므로 +1
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
return `${year}-${month}-${day}`;
|
||||
if (!date) return '';
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0'); // 월은 0부터 시작하므로 +1
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
return `${year}-${month}-${day}`;
|
||||
};
|
||||
|
||||
// 현재 년도부터 10년 전까지의 년도 옵션 생성
|
||||
export const yearOptions = Array.from({ length: 10 }, (_, i) => {
|
||||
const year = new Date().getFullYear() - i;
|
||||
return { text: `${year}년`, val: year.toString() };
|
||||
});
|
||||
// 현재 년도부터 10년 전까지의 년도 옵션 생성
|
||||
export const yearOptions = Array.from({ length: 10 }, (_, i) => {
|
||||
const year = new Date().getFullYear() - i;
|
||||
return { text: `${year}년`, val: year.toString() };
|
||||
});
|
||||
|
||||
@@ -6,143 +6,143 @@ import { useId, useState } from 'react';
|
||||
import type { HttpResponse, Upload } from "tus-js-client";
|
||||
|
||||
interface FileUploadFormProps {
|
||||
onSubmit?: (files: string, year: string, quarter: string, code: string, id: string) => void;
|
||||
onFileSelect?: (file: File | null, atflCd: string | null, atflId: string | null) => void; // 파일 선택 시 호출될 콜백 추가
|
||||
showButton?: boolean; // 버튼 표시 여부 (기본값 true)
|
||||
showPreview?: boolean; // 파일명 프리뷰 표시 여부
|
||||
accept?: string; // 파일 형식 지정
|
||||
fileIdx?: number; // 부모가 어떤 FileUploadForm인지 구분하기 위한 인덱스
|
||||
onSubmit?: (files: string, year: string, quarter: string, code: string, id: string) => void;
|
||||
onFileSelect?: (file: File | null, atflCd: string | null, atflId: string | null) => void; // 파일 선택 시 호출될 콜백 추가
|
||||
showButton?: boolean; // 버튼 표시 여부 (기본값 true)
|
||||
showPreview?: boolean; // 파일명 프리뷰 표시 여부
|
||||
accept?: string; // 파일 형식 지정
|
||||
fileIdx?: number; // 부모가 어떤 FileUploadForm인지 구분하기 위한 인덱스
|
||||
}
|
||||
|
||||
const FileUploadForm = ({
|
||||
onSubmit,
|
||||
onFileSelect,
|
||||
showButton = true,
|
||||
showPreview = false,
|
||||
accept = ".jpeg, .jpg, .png",
|
||||
fileIdx // props에서 fileIdx 받기
|
||||
onSubmit,
|
||||
onFileSelect,
|
||||
showButton = true,
|
||||
showPreview = false,
|
||||
accept = ".jpeg, .jpg, .png",
|
||||
fileIdx // props에서 fileIdx 받기
|
||||
}: FileUploadFormProps) => {
|
||||
const [internalAtflCd, setInternalAtflCd] = useState<string | null>(null);
|
||||
const [internalAtflId, setInternalAtflId] = useState<string | null>(null);
|
||||
const [currentFile, setCurrentFile] = useState<File | null>(null); // 현재 선택된 파일을 저장할 상태
|
||||
const [fileName, setFileName] = useState<string>('');
|
||||
const [files, setFiles] = useState<FileList | null>(null);
|
||||
const [upload, setUpload] = useState<Upload | null>(null);
|
||||
const uniqueId = useId(); // 각 FileUploadForm 인스턴스에 대한 고유 ID 생성
|
||||
const [internalAtflCd, setInternalAtflCd] = useState<string | null>(null);
|
||||
const [internalAtflId, setInternalAtflId] = useState<string | null>(null);
|
||||
const [currentFile, setCurrentFile] = useState<File | null>(null); // 현재 선택된 파일을 저장할 상태
|
||||
const [fileName, setFileName] = useState<string>('');
|
||||
const [files, setFiles] = useState<FileList | null>(null);
|
||||
const [upload, setUpload] = useState<Upload | null>(null);
|
||||
const uniqueId = useId(); // 각 FileUploadForm 인스턴스에 대한 고유 ID 생성
|
||||
|
||||
const quater = [{ text: '1분기', val: '1' }, { text: '2분기', val: '2' }, { text: '3분기', val: '3' }, { text: '4분기', val: '4' }];
|
||||
// 현재 년도부터 10년 전까지의 년도 옵션 생성
|
||||
const yearOptions = Array.from({ length: 10 }, (_, i) => {
|
||||
const year = new Date().getFullYear() - i;
|
||||
return { text: `${year}년`, val: year.toString() };
|
||||
});
|
||||
const [selectedYear, setSelectedYear] = useState<string>(yearOptions[0]?.val || '');
|
||||
const [selectedQuarter, setSelectedQuarter] = useState<string>(quater[0]?.val || '');
|
||||
const quater = [{ text: '1분기', val: '1' }, { text: '2분기', val: '2' }, { text: '3분기', val: '3' }, { text: '4분기', val: '4' }];
|
||||
// 현재 년도부터 10년 전까지의 년도 옵션 생성
|
||||
const yearOptions = Array.from({ length: 10 }, (_, i) => {
|
||||
const year = new Date().getFullYear() - i;
|
||||
return { text: `${year}년`, val: year.toString() };
|
||||
});
|
||||
const [selectedYear, setSelectedYear] = useState<string>(yearOptions[0]?.val || '');
|
||||
const [selectedQuarter, setSelectedQuarter] = useState<string>(quater[0]?.val || '');
|
||||
|
||||
const internalHandleUploadResponse = (file: File) => async (res: HttpResponse) => {
|
||||
const fileInfo = res.getHeader("X-File-Info");
|
||||
if (!fileInfo) {
|
||||
if (onFileSelect) onFileSelect(file, null, null);
|
||||
return;
|
||||
}
|
||||
const { atflCd: newAtflCd, atflId: newAtflId } = JSON.parse(fileInfo) ?? {};
|
||||
const orgnlFileNm = file.name;
|
||||
console.log(`FileUploadForm (idx: ${fileIdx ?? 'N/A'}) - orgnlFileNm:`, orgnlFileNm, 'New AtflCd:', newAtflCd, 'New AtflId:', newAtflId);
|
||||
setFileName(orgnlFileNm);
|
||||
setInternalAtflCd(newAtflCd);
|
||||
setInternalAtflId(newAtflId);
|
||||
const internalHandleUploadResponse = (file: File) => async (res: HttpResponse) => {
|
||||
const fileInfo = res.getHeader("X-File-Info");
|
||||
if (!fileInfo) {
|
||||
if (onFileSelect) onFileSelect(file, null, null);
|
||||
return;
|
||||
}
|
||||
const { atflCd: newAtflCd, atflId: newAtflId } = JSON.parse(fileInfo) ?? {};
|
||||
const orgnlFileNm = file.name;
|
||||
console.log(`FileUploadForm (idx: ${fileIdx ?? 'N/A'}) - orgnlFileNm:`, orgnlFileNm, 'New AtflCd:', newAtflCd, 'New AtflId:', newAtflId);
|
||||
setFileName(orgnlFileNm);
|
||||
setInternalAtflCd(newAtflCd);
|
||||
setInternalAtflId(newAtflId);
|
||||
|
||||
if (onFileSelect) {
|
||||
onFileSelect(file, newAtflCd, newAtflId);
|
||||
}
|
||||
};
|
||||
if (onFileSelect) {
|
||||
onFileSelect(file, newAtflCd, newAtflId);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div> {/* 컴포넌트 루트 div */}
|
||||
<div className="card p-6"> {/* 카드 스타일링 */}
|
||||
<div className='flex items-center gap-2'> {/* FileUploader와 Button을 정렬하는 메인 flex 컨테이너 */}
|
||||
<div className="w-[300px] mr-4"> {/* FileUploader의 너비와 오른쪽 여백을 제어하는 컨테이너 */}
|
||||
<FileUploader
|
||||
id={uniqueId}
|
||||
className="!h-auto !max-w-none flex-row items-center gap-2"
|
||||
accept={accept}
|
||||
onUpload={(e) => {
|
||||
const selectedFiles = e.target.files;
|
||||
if (!selectedFiles || selectedFiles.length === 0) {
|
||||
setFiles(null);
|
||||
setCurrentFile(null);
|
||||
setInternalAtflCd(null);
|
||||
setInternalAtflId(null);
|
||||
if (onFileSelect) {
|
||||
onFileSelect(null, null, null); // 파일 선택 취소 시 null 전달
|
||||
}
|
||||
return;
|
||||
}
|
||||
return (
|
||||
<div> {/* 컴포넌트 루트 div */}
|
||||
<div className="card p-6"> {/* 카드 스타일링 */}
|
||||
<div className='flex items-center gap-2'> {/* FileUploader와 Button을 정렬하는 메인 flex 컨테이너 */}
|
||||
<div className="w-[300px] mr-4"> {/* FileUploader의 너비와 오른쪽 여백을 제어하는 컨테이너 */}
|
||||
<FileUploader
|
||||
id={uniqueId}
|
||||
className="!h-auto !max-w-none flex-row items-center gap-2"
|
||||
accept={accept}
|
||||
onUpload={(e) => {
|
||||
const selectedFiles = e.target.files;
|
||||
if (!selectedFiles || selectedFiles.length === 0) {
|
||||
setFiles(null);
|
||||
setCurrentFile(null);
|
||||
setInternalAtflCd(null);
|
||||
setInternalAtflId(null);
|
||||
if (onFileSelect) {
|
||||
onFileSelect(null, null, null); // 파일 선택 취소 시 null 전달
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const firstFile = selectedFiles[0];
|
||||
if (!firstFile) return; // 타입스크립트 undefined 가능성 제거
|
||||
const firstFile = selectedFiles[0];
|
||||
if (!firstFile) return; // 타입스크립트 undefined 가능성 제거
|
||||
|
||||
setFiles(selectedFiles);
|
||||
setCurrentFile(firstFile);
|
||||
setFiles(selectedFiles);
|
||||
setCurrentFile(firstFile);
|
||||
|
||||
if (onFileSelect) {
|
||||
onFileSelect(firstFile, internalAtflCd, internalAtflId);
|
||||
}
|
||||
if (onFileSelect) {
|
||||
onFileSelect(firstFile, internalAtflCd, internalAtflId);
|
||||
}
|
||||
|
||||
handleUpload(selectedFiles, {
|
||||
handleUploadStart: setUpload,
|
||||
handleSuccess: setUpload,
|
||||
// handleResponse는 tus-js-client의 Upload 인스턴스 콜백과 연동 필요
|
||||
// 여기서는 handleUpload 유틸리티가 응답을 직접 처리한다고 가정
|
||||
handleResponse: internalHandleUploadResponse(firstFile),
|
||||
});
|
||||
}}
|
||||
customPreview={
|
||||
showPreview ? (
|
||||
<div className="file-uploader-preview">
|
||||
{files && files.length > 0
|
||||
? fileName
|
||||
: "파일을 선택하세요."}
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
{showButton ? ( // showButton이 true일 때만 버튼 렌더링
|
||||
<Button
|
||||
className="px-4 py-2 bg-blue-600 text-white rounded-sm text-sm font-medium hover:bg-blue-700"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
console.log(files?.[0]?.name)
|
||||
if (!currentFile) {
|
||||
alert('파일을 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
if (!selectedYear) {
|
||||
alert('등록년도를 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
if (!selectedQuarter) {
|
||||
alert('분기를 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
const fileNm = currentFile.name;
|
||||
if (onSubmit) {
|
||||
// 부모에게 전달할 때는 내부 상태인 internalAtflCd, internalAtflId 사용
|
||||
onSubmit(fileNm, selectedYear, selectedQuarter, internalAtflCd || '', internalAtflId || '');
|
||||
setFiles(null); // 파일 업로드 후 파일 목록 초기화
|
||||
setFileName(''); // 파일명 초기화
|
||||
}
|
||||
}}
|
||||
>
|
||||
등록
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
handleUpload(selectedFiles, {
|
||||
handleUploadStart: setUpload,
|
||||
handleSuccess: setUpload,
|
||||
// handleResponse는 tus-js-client의 Upload 인스턴스 콜백과 연동 필요
|
||||
// 여기서는 handleUpload 유틸리티가 응답을 직접 처리한다고 가정
|
||||
handleResponse: internalHandleUploadResponse(firstFile),
|
||||
});
|
||||
}}
|
||||
customPreview={
|
||||
showPreview ? (
|
||||
<div className="file-uploader-preview">
|
||||
{files && files.length > 0
|
||||
? fileName
|
||||
: "파일을 선택하세요."}
|
||||
</div>
|
||||
) : null
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
{showButton ? ( // showButton이 true일 때만 버튼 렌더링
|
||||
<Button
|
||||
className="px-4 py-2 bg-blue-600 text-white rounded-sm text-sm font-medium hover:bg-blue-700"
|
||||
size="sm"
|
||||
onClick={() => {
|
||||
console.log(files?.[0]?.name)
|
||||
if (!currentFile) {
|
||||
alert('파일을 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
if (!selectedYear) {
|
||||
alert('등록년도를 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
if (!selectedQuarter) {
|
||||
alert('분기를 선택해주세요.');
|
||||
return;
|
||||
}
|
||||
const fileNm = currentFile.name;
|
||||
if (onSubmit) {
|
||||
// 부모에게 전달할 때는 내부 상태인 internalAtflCd, internalAtflId 사용
|
||||
onSubmit(fileNm, selectedYear, selectedQuarter, internalAtflCd || '', internalAtflId || '');
|
||||
setFiles(null); // 파일 업로드 후 파일 목록 초기화
|
||||
setFileName(''); // 파일명 초기화
|
||||
}
|
||||
}}
|
||||
>
|
||||
등록
|
||||
</Button>
|
||||
) : null}
|
||||
</div>
|
||||
)
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default FileUploadForm
|
||||
@@ -2,23 +2,23 @@ import { FormField } from '@common/business_components/ui'
|
||||
import React, { type ReactElement } from 'react'
|
||||
|
||||
interface FormSectionProps {
|
||||
children: ReactElement
|
||||
titleNm: string
|
||||
form: any
|
||||
name: string
|
||||
children: ReactElement
|
||||
titleNm: string
|
||||
form: any
|
||||
name: string
|
||||
}
|
||||
|
||||
const FormSection = ({ children, titleNm, form, name }: FormSectionProps) => {
|
||||
return (
|
||||
<div className="grid grid-cols-[200px_1fr] items-center">
|
||||
<div className="text-xl font-medium">{titleNm}</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={name}
|
||||
render={({ field }) => children}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
return (
|
||||
<div className="grid grid-cols-[200px_1fr] items-center">
|
||||
<div className="text-xl font-medium">{titleNm}</div>
|
||||
<FormField
|
||||
control={form.control}
|
||||
name={name}
|
||||
render={({ field }) => children}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default FormSection
|
||||
@@ -2,34 +2,34 @@ import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogC
|
||||
import React from 'react'
|
||||
|
||||
interface ModalProps {
|
||||
isModalOpen: boolean;
|
||||
setIsModalOpen: (open: boolean) => void;
|
||||
title?: string;
|
||||
children: React.ReactNode;
|
||||
closeModal: () => void;
|
||||
saveModal?: () => void
|
||||
isModalOpen: boolean;
|
||||
setIsModalOpen: (open: boolean) => void;
|
||||
title?: string;
|
||||
children: React.ReactNode;
|
||||
closeModal: () => void;
|
||||
saveModal?: () => void
|
||||
}
|
||||
|
||||
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-[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>
|
||||
</Dialog>
|
||||
)
|
||||
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-[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>
|
||||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
export default Modal
|
||||
@@ -4,48 +4,48 @@ import React, { useState } from 'react'
|
||||
import FileUploadForm from './fileUploadForm';
|
||||
import { yearOptions } from '@/components/lib/utils';
|
||||
|
||||
const operFileUploadForm = ({ title, onSubmit }: { title: string, onSubmit: (files: string, year: string, quarter: string, code:string, id:string) => void }) => {
|
||||
const [atflCd, setAtflCd] = useState<string | null>(null);
|
||||
const [atflId, setAtflId] = useState<string | null>(null);
|
||||
const operFileUploadForm = ({ title, onSubmit }: { title: string, onSubmit: (files: string, year: string, quarter: string, code: string, id: string) => void }) => {
|
||||
const [atflCd, setAtflCd] = useState<string | null>(null);
|
||||
const [atflId, setAtflId] = useState<string | null>(null);
|
||||
|
||||
|
||||
const quater = [{ text: '1분기', val: '1' }, { text: '2분기', val: '2' }, { text: '3분기', val: '3' }, { text: '4분기', val: '4' }]
|
||||
const [selectedYear, setSelectedYear] = useState<string>(yearOptions[0]?.val || '');
|
||||
const [selectedQuarter, setSelectedQuarter] = useState<string>(quater[0]?.val || '');
|
||||
const quater = [{ text: '1분기', val: '1' }, { text: '2분기', val: '2' }, { text: '3분기', val: '3' }, { text: '4분기', val: '4' }]
|
||||
const [selectedYear, setSelectedYear] = useState<string>(yearOptions[0]?.val || '');
|
||||
const [selectedQuarter, setSelectedQuarter] = useState<string>(quater[0]?.val || '');
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className='card-header'>
|
||||
<h3 className="text-2xl font-medium">{title}</h3>
|
||||
</div>
|
||||
<div className="card p-6">
|
||||
<div className="grid grid-col-2 items-center space-x-4">
|
||||
<div className='flex'>
|
||||
<SelectBox
|
||||
className="w-50 mr-3 ml-4"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label="년도"
|
||||
selectArray={yearOptions}
|
||||
onSelectValue={(value: string | number) => setSelectedYear(String(value))}
|
||||
/><SelectBox
|
||||
className="w-50"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label="분기"
|
||||
selectArray={quater}
|
||||
onSelectValue={(value: string | number) => setSelectedQuarter(String(value))}
|
||||
/>
|
||||
<FileUploadForm
|
||||
onSubmit={(files, year, quarter, code, id) => {
|
||||
onSubmit(files, (selectedYear ?? year) || '', (selectedQuarter ?? quarter) || '', code, id);
|
||||
}}
|
||||
showButton={true}
|
||||
showPreview={true}
|
||||
accept={""} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
return (
|
||||
<div>
|
||||
<div className='card-header'>
|
||||
<h3 className="text-2xl font-medium">{title}</h3>
|
||||
</div>
|
||||
<div className="card p-6">
|
||||
<div className="grid grid-col-2 items-center space-x-4">
|
||||
<div className='flex'>
|
||||
<SelectBox
|
||||
className="w-50 mr-3 ml-4"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label="년도"
|
||||
selectArray={yearOptions}
|
||||
onSelectValue={(value: string | number) => setSelectedYear(String(value))}
|
||||
/><SelectBox
|
||||
className="w-50"
|
||||
selectClass="rounded-lg bg-white"
|
||||
label="분기"
|
||||
selectArray={quater}
|
||||
onSelectValue={(value: string | number) => setSelectedQuarter(String(value))}
|
||||
/>
|
||||
<FileUploadForm
|
||||
onSubmit={(files, year, quarter, code, id) => {
|
||||
onSubmit(files, (selectedYear ?? year) || '', (selectedQuarter ?? quarter) || '', code, id);
|
||||
}}
|
||||
showButton={true}
|
||||
showPreview={true}
|
||||
accept={""} />
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default operFileUploadForm
|
||||
@@ -3,204 +3,204 @@ import { Table, TableBody, TableCell, TableRow } from "@common/components";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
type UiTableProps = {
|
||||
headName: React.ReactNode | undefined;
|
||||
tableName1?: React.ReactNode | undefined;
|
||||
tableData: { [key: string]: string | number | boolean | object }[];
|
||||
headlist: UiHeadProps[];
|
||||
pageSize: number;
|
||||
total: number;
|
||||
children: Record<string, React.ReactNode> | undefined;
|
||||
cellClick: ((index: number | undefined, headId: string) => void) | undefined;
|
||||
onPageChange: (page: number) => void;
|
||||
className?: string;
|
||||
resetPageNo?: boolean;
|
||||
isEditable?: boolean;
|
||||
tableName2?: React.ReactNode | undefined;
|
||||
tableData2?: { [key: string]: string | number | boolean | object }[];
|
||||
headlist2?: UiHeadProps[];
|
||||
pageSize2?: number;
|
||||
total2?: number;
|
||||
children2?: Record<string, React.ReactNode> | undefined;
|
||||
cellClick2?: ((index: number | undefined, headId: string) => void) | undefined;
|
||||
onPageChange2?: (page: number) => void;
|
||||
className2?: string;
|
||||
resetPageNo2?: boolean;
|
||||
isEditable2?: boolean;
|
||||
showSecondTable?: boolean;
|
||||
headName: React.ReactNode | undefined;
|
||||
tableName1?: React.ReactNode | undefined;
|
||||
tableData: { [key: string]: string | number | boolean | object }[];
|
||||
headlist: UiHeadProps[];
|
||||
pageSize: number;
|
||||
total: number;
|
||||
children: Record<string, React.ReactNode> | undefined;
|
||||
cellClick: ((index: number | undefined, headId: string) => void) | undefined;
|
||||
onPageChange: (page: number) => void;
|
||||
className?: string;
|
||||
resetPageNo?: boolean;
|
||||
isEditable?: boolean;
|
||||
tableName2?: React.ReactNode | undefined;
|
||||
tableData2?: { [key: string]: string | number | boolean | object }[];
|
||||
headlist2?: UiHeadProps[];
|
||||
pageSize2?: number;
|
||||
total2?: number;
|
||||
children2?: Record<string, React.ReactNode> | undefined;
|
||||
cellClick2?: ((index: number | undefined, headId: string) => void) | undefined;
|
||||
onPageChange2?: (page: number) => void;
|
||||
className2?: string;
|
||||
resetPageNo2?: boolean;
|
||||
isEditable2?: boolean;
|
||||
showSecondTable?: boolean;
|
||||
};
|
||||
|
||||
type UiHeadProps = {
|
||||
id: string;
|
||||
title: string;
|
||||
align?: string;
|
||||
type?: string;
|
||||
options?: string[];
|
||||
id: string;
|
||||
title: string;
|
||||
align?: string;
|
||||
type?: string;
|
||||
options?: string[];
|
||||
};
|
||||
|
||||
export function VerticalTable({
|
||||
headName,
|
||||
tableName1,
|
||||
tableData,
|
||||
headlist,
|
||||
pageSize,
|
||||
total,
|
||||
children,
|
||||
cellClick,
|
||||
onPageChange,
|
||||
className = "",
|
||||
resetPageNo = false,
|
||||
isEditable = false,
|
||||
tableName2,
|
||||
tableData2,
|
||||
headlist2,
|
||||
pageSize2,
|
||||
total2,
|
||||
children2,
|
||||
cellClick2,
|
||||
onPageChange2,
|
||||
className2 = "",
|
||||
resetPageNo2 = false,
|
||||
isEditable2 = false,
|
||||
showSecondTable = false,
|
||||
headName,
|
||||
tableName1,
|
||||
tableData,
|
||||
headlist,
|
||||
pageSize,
|
||||
total,
|
||||
children,
|
||||
cellClick,
|
||||
onPageChange,
|
||||
className = "",
|
||||
resetPageNo = false,
|
||||
isEditable = false,
|
||||
tableName2,
|
||||
tableData2,
|
||||
headlist2,
|
||||
pageSize2,
|
||||
total2,
|
||||
children2,
|
||||
cellClick2,
|
||||
onPageChange2,
|
||||
className2 = "",
|
||||
resetPageNo2 = false,
|
||||
isEditable2 = false,
|
||||
showSecondTable = false,
|
||||
}: UiTableProps) {
|
||||
const [cur, setCur] = useState<number>(1);
|
||||
const [formData, setFormData] = useState<{ [key: string]: string | number | boolean }>({});
|
||||
useEffect(() => {
|
||||
if (resetPageNo) {
|
||||
setCur(1);
|
||||
onPageChange(1);
|
||||
}
|
||||
}, [onPageChange, resetPageNo]);
|
||||
const [cur, setCur] = useState<number>(1);
|
||||
const [formData, setFormData] = useState<{ [key: string]: string | number | boolean }>({});
|
||||
useEffect(() => {
|
||||
if (resetPageNo) {
|
||||
setCur(1);
|
||||
onPageChange(1);
|
||||
}
|
||||
}, [onPageChange, resetPageNo]);
|
||||
|
||||
useEffect(() => {
|
||||
if (tableData.length > 0 && tableData[0]) {
|
||||
setFormData(tableData[0] as { [key: string]: string | number | boolean });
|
||||
}
|
||||
}, [tableData]);
|
||||
useEffect(() => {
|
||||
if (tableData.length > 0 && tableData[0]) {
|
||||
setFormData(tableData[0] as { [key: string]: string | number | boolean });
|
||||
}
|
||||
}, [tableData]);
|
||||
|
||||
const handleInputChange = (id: string, value: string | number | boolean) => {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[id]: value,
|
||||
}));
|
||||
};
|
||||
const handleInputChange = (id: string, value: string | number | boolean) => {
|
||||
setFormData((prev) => ({
|
||||
...prev,
|
||||
[id]: value,
|
||||
}));
|
||||
};
|
||||
|
||||
const handlePageChange = (page: number) => {
|
||||
setCur(page);
|
||||
onPageChange(page);
|
||||
};
|
||||
const handlePageChange = (page: number) => {
|
||||
setCur(page);
|
||||
onPageChange(page);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
{headName ? (
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl mx-3 font-medium text-label">{headName}</h3>
|
||||
</div>
|
||||
) : null}
|
||||
<div className={`card !m-[1.2rem] !p-0r ${className}`}>
|
||||
{tableName1 && ( // tableName prop이 있으면 표시
|
||||
<div className="text-xl font-semibold my-2 mx-3">{tableName1}</div>
|
||||
)}
|
||||
<Table variant="secondary">
|
||||
<TableBody className="text-center">
|
||||
{headlist?.map((head) => (
|
||||
<TableRow key={head.id}>
|
||||
{/* 컬럼 제목 */}
|
||||
<TableCell className="font-bold bg-blue-100 text-xs">
|
||||
{head.title}
|
||||
</TableCell>
|
||||
{/* 데이터 값 또는 입력 필드 */}
|
||||
<TableCell className="text-xs">
|
||||
{children && children[head.id] ? children[head.id] : isEditable ? (
|
||||
head.type === "select" ? (
|
||||
<select
|
||||
id={head.id}
|
||||
value={formData[head.id] ?? "-"}
|
||||
onChange={(e) => handleInputChange(head.id, e.target.value)}
|
||||
className="w-full border border-gray-300 rounded-sm px-2 py-1 text-xl border-radius-0"
|
||||
>
|
||||
{head.options?.map((option) => (
|
||||
<option key={option} value={option}>
|
||||
{option}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
) : head.type === "input" ? (
|
||||
<input
|
||||
id={head.id}
|
||||
type="text"
|
||||
value={formData[head.id] ?? ""}
|
||||
onChange={(e) => handleInputChange(head.id, e.target.value)}
|
||||
className="w-full text-xl border-radius-0 border border-gray-300 rounded-sm px-2 py-1"
|
||||
/>
|
||||
) : (
|
||||
<span>{formData[head.id] ?? "-"}</span>
|
||||
)
|
||||
) : (
|
||||
<span>{formData[head.id] ?? "-"}</span>
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
return (
|
||||
<>
|
||||
{headName ? (
|
||||
<div className="card-header">
|
||||
<h3 className="text-2xl mx-3 font-medium text-label">{headName}</h3>
|
||||
</div>
|
||||
) : null}
|
||||
<div className={`card !m-[1.2rem] !p-0r ${className}`}>
|
||||
{tableName1 && ( // tableName prop이 있으면 표시
|
||||
<div className="text-xl font-semibold my-2 mx-3">{tableName1}</div>
|
||||
)}
|
||||
<Table variant="secondary">
|
||||
<TableBody className="text-center">
|
||||
{headlist?.map((head) => (
|
||||
<TableRow key={head.id}>
|
||||
{/* 컬럼 제목 */}
|
||||
<TableCell className="font-bold bg-blue-100 text-xs">
|
||||
{head.title}
|
||||
</TableCell>
|
||||
{/* 데이터 값 또는 입력 필드 */}
|
||||
<TableCell className="text-xs">
|
||||
{children && children[head.id] ? children[head.id] : isEditable ? (
|
||||
head.type === "select" ? (
|
||||
<select
|
||||
id={head.id}
|
||||
value={formData[head.id] ?? "-"}
|
||||
onChange={(e) => handleInputChange(head.id, e.target.value)}
|
||||
className="w-full border border-gray-300 rounded-sm px-2 py-1 text-xl border-radius-0"
|
||||
>
|
||||
{head.options?.map((option) => (
|
||||
<option key={option} value={option}>
|
||||
{option}
|
||||
</option>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
{tableName2 && ( // tableName prop이 있으면 표시
|
||||
<div className="text-xl font-semibold mt-8 my-2 mx-3">{tableName2}</div>
|
||||
)}
|
||||
{showSecondTable && (
|
||||
<Table variant="secondary">
|
||||
<TableBody className="text-center">
|
||||
{headlist2?.map((head) => (
|
||||
<TableRow key={head.id}>
|
||||
{/* 컬럼 제목 */}
|
||||
<TableCell className="font-bold bg-blue-100 text-xs w-fit whitespace-nowrap">
|
||||
{head.title}
|
||||
</TableCell>
|
||||
{/* 데이터 값 또는 입력 필드 */}
|
||||
<TableCell className="text-xs">
|
||||
{children && children[head.id] ? children[head.id] : isEditable2 ? (
|
||||
head.type === "select" ? (
|
||||
<select
|
||||
id={head.id}
|
||||
value={formData[head.id] ?? "-"}
|
||||
onChange={(e) => handleInputChange(head.id, e.target.value)}
|
||||
className="w-full border border-gray-300 rounded-sm px-2 py-1 text-xl border-radius-0"
|
||||
>
|
||||
{head.options?.map((option) => (
|
||||
<option key={option} value={option}>
|
||||
{option}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
) : head.type === "input" ? (
|
||||
<input
|
||||
id={head.id}
|
||||
type="text"
|
||||
value={formData[head.id] ?? ""}
|
||||
onChange={(e) => handleInputChange(head.id, e.target.value)}
|
||||
className="w-full text-xl border-radius-0 border border-gray-300 rounded-sm px-2 py-1"
|
||||
/>
|
||||
) : (
|
||||
<span>{formData[head.id] ?? "-"}</span>
|
||||
)
|
||||
) : (
|
||||
<span>{formData[head.id] ?? "-"}</span>
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
)}
|
||||
{total > 0 && (
|
||||
<UiPageNations
|
||||
total={total}
|
||||
cur={cur}
|
||||
pageSize={pageSize}
|
||||
onPageChange={handlePageChange}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
</select>
|
||||
) : head.type === "input" ? (
|
||||
<input
|
||||
id={head.id}
|
||||
type="text"
|
||||
value={formData[head.id] ?? ""}
|
||||
onChange={(e) => handleInputChange(head.id, e.target.value)}
|
||||
className="w-full text-xl border-radius-0 border border-gray-300 rounded-sm px-2 py-1"
|
||||
/>
|
||||
) : (
|
||||
<span>{formData[head.id] ?? "-"}</span>
|
||||
)
|
||||
) : (
|
||||
<span>{formData[head.id] ?? "-"}</span>
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
{tableName2 && ( // tableName prop이 있으면 표시
|
||||
<div className="text-xl font-semibold mt-8 my-2 mx-3">{tableName2}</div>
|
||||
)}
|
||||
{showSecondTable && (
|
||||
<Table variant="secondary">
|
||||
<TableBody className="text-center">
|
||||
{headlist2?.map((head) => (
|
||||
<TableRow key={head.id}>
|
||||
{/* 컬럼 제목 */}
|
||||
<TableCell className="font-bold bg-blue-100 text-xs w-fit whitespace-nowrap">
|
||||
{head.title}
|
||||
</TableCell>
|
||||
{/* 데이터 값 또는 입력 필드 */}
|
||||
<TableCell className="text-xs">
|
||||
{children && children[head.id] ? children[head.id] : isEditable2 ? (
|
||||
head.type === "select" ? (
|
||||
<select
|
||||
id={head.id}
|
||||
value={formData[head.id] ?? "-"}
|
||||
onChange={(e) => handleInputChange(head.id, e.target.value)}
|
||||
className="w-full border border-gray-300 rounded-sm px-2 py-1 text-xl border-radius-0"
|
||||
>
|
||||
{head.options?.map((option) => (
|
||||
<option key={option} value={option}>
|
||||
{option}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
) : head.type === "input" ? (
|
||||
<input
|
||||
id={head.id}
|
||||
type="text"
|
||||
value={formData[head.id] ?? ""}
|
||||
onChange={(e) => handleInputChange(head.id, e.target.value)}
|
||||
className="w-full text-xl border-radius-0 border border-gray-300 rounded-sm px-2 py-1"
|
||||
/>
|
||||
) : (
|
||||
<span>{formData[head.id] ?? "-"}</span>
|
||||
)
|
||||
) : (
|
||||
<span>{formData[head.id] ?? "-"}</span>
|
||||
)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</TableBody>
|
||||
</Table>
|
||||
)}
|
||||
{total > 0 && (
|
||||
<UiPageNations
|
||||
total={total}
|
||||
cur={cur}
|
||||
pageSize={pageSize}
|
||||
onPageChange={handlePageChange}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user