Merge branch 'iipbs/sn/dev'

This commit is contained in:
swcho
2025-08-08 17:37:13 +09:00
13 changed files with 258 additions and 11356 deletions
+2 -1
View File
@@ -13,4 +13,5 @@ NEXT_PUBLIC_REFRESH_TOKEN = refresh-token
NEXT_PUBLIC_NGII_URL = https://map.ngii.go.kr/openapi/Gettile.do?apiKey=04trYP9_xwLAfALjwZ-B8g
NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.128:8883/sewerage-service/api
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.222:8883/sewerage-service/api
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.222:8883/sewerage-service/api
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.66:8883/sewerage-service/api
@@ -373,7 +373,7 @@ export const MsSwageMhlCol = [
// { id: 'usg_cd', title: '용도코드' },
// { id: 'fclty_shp_cd', title: '시설물형태코드' },
// { id: 'mhl_knd_cd', title: '맨홀종류코드' },
{ id: 'swage_mh_diam', title: '하수맨홀구경(mm)' },
{ id: 'swage_mhL_diam_vl', title: '하수맨홀구경(mm)' },
{ id: 'mqlt_cd', title: '재질' },
{ id: 'wdth_len', title: '가로길이(m)' },
{ id: 'vrtc_len', title: '세로길이(m)' },
@@ -396,7 +396,7 @@ export const MsSwageMhlCol2 = [
// { id: 'usgCd', title: '용도코드' },
// { id: 'fcltyShpCd', title: '시설물형태코드' },
// { id: 'mhlKndCd', title: '맨홀종류코드' },
{ id: 'swageMhDiam', title: '하수맨홀구경(mm)' },
{ id: 'swageMhlDiamVl', title: '하수맨홀구경(mm)' },
{ id: 'mqltCd', title: '재질코드' },
{ id: 'wdthLen', title: '가로길이(m)' },
{ id: 'vrtcLen', title: '세로길이(m)' },
@@ -487,7 +487,7 @@ export const ExclnSewagePumpCol = [
{ id: 'max_impnd_cpct', title: '최대저수용량(㎥)' },
{ id: 'altd_vl', title: '표고(EL.m)' },
{ id: 'wtlv_vl', title: '수위(m)' },
{ id: 'excln_wpmp_ablt', title: '우수양수능력(m²/d)' },
{ id: 'excln_wpmp_ablt_vl', title: '우수양수능력(m²/d)' },
// { id: 'instMngNo', title: '기관관리번호' },
]
export const ExclnSewagePumpCol2 = [
@@ -505,10 +505,10 @@ export const ExclnSewagePumpCol2 = [
// { id: 'opngSttsCd', title: '개통상태코드' },
// { id: 'usgCd', title: '용도코드' },
{ id: 'dailyPrcsCpct', title: '일일처리용량(㎥)' },
{ id: 'max_impndCpct', title: '최대저수용량(㎥)' },
{ id: 'maxImpndCpct', title: '최대저수용량(㎥)' },
{ id: 'altdVl', title: '표고(EL.m)' },
{ id: 'wtlvVl', title: '수위(m)' },
{ id: 'exclnWpmpAblt', title: '우수양수능력(m²/d)' },
{ id: 'exclnWpmpAbltVl', title: '우수양수능력(m²/d)' },
// { id: 'instMngNo', title: '기관관리번호' },
]
// 오수펌프장
@@ -529,7 +529,7 @@ export const DrtwSewagePumpCol = [
{ id: 'max_impnd_cpct', title: '최대저수용량(㎥)' },
{ id: 'altd_vl', title: '표고(EL.m)' },
{ id: 'wtlv_vl', title: '수위(m)' },
{ id: 'excln_wpmp_ablt', title: '오수양수능력(m²/d)' },
{ id: 'excln_wpmp_ablt_vl', title: '오수양수능력(m²/d)' },
// { id: 'instMngNo', title: '기관관리번호' },
]
export const DrtwSewagePumpCol2 = [
@@ -547,10 +547,10 @@ export const DrtwSewagePumpCol2 = [
// { id: 'opngSttsCd', title: '개통상태코드' },
// { id: 'usgCd', title: '용도코드' },
{ id: 'dailyPrcsCpct', title: '일일처리용량(㎥)' },
{ id: 'max_impndCpct', title: '최대저수용량(㎥)' },
{ id: 'maxImpndCpct', title: '최대저수용량(㎥)' },
{ id: 'altdVl', title: '표고(EL.m)' },
{ id: 'wtlvVl', title: '수위(m)' },
{ id: 'exclnWpmpAblt', title: '오수양수능력(m²/d)' },
{ id: 'exclnWpmpAbltVl', title: '오수양수능력(m²/d)' },
// { id: 'instMngNo', title: '기관관리번호' },
]
// 펌프장 용도 코드표
@@ -5,13 +5,9 @@ import moment from "moment";
export type TInftwtAnls = {
inftwtAnlsSn:number;
aplcnYm: string;
enoCd: string;
ctpvCd: string;
sggCd: string;
swageTmplntCd: string;
swageTmplntNm: string;
swtrmtZoneCd: string;
swtrmtZoneNm: string;
swtrmtFcltNm: string;
atflCd: string;
atflId: string;
atflInpNm: string;
@@ -32,13 +28,9 @@ export type TInftwtAnlsRslt = {
export type TRdiiAnls = {
rainInfwtAnlsSn:number;
aplcnYm: string;
enoCd: string;
ctpvCd: string;
sggCd: string;
swageTmplntCd: string;
swageTmplntNm: string;
swtrmtZoneCd: string;
swtrmtZoneNm: string;
swtrmtFcltNm: string;
atflCd: string;
atflId: string;
atflInpNm: string;
@@ -52,6 +52,7 @@ type BaseLayout = {
setCtpvCd: (ctpv: number) => void;
setSggCd: (sgg: number) => void;
setSggName?: (sggName: string) => void;
setSwtrmtFcltNm?: (swtrmtFcltNm: string) => void;
setIsLoading?: (isLoading: boolean) => void;
onExcelDownload?: () => void;
onDeleteFile?: () => void;
@@ -81,6 +82,7 @@ const BaseLayout = ({
setCtpvCd,
setSggCd,
setSggName,
setSwtrmtFcltNm,
setIsLoading,
onExcelDownload,
onDeleteFile,
@@ -184,6 +186,7 @@ const BaseLayout = ({
setSggName={setSggName}
selectedSwagePrcsFclt={selectedSwagePrcsFclt}
onSwagePrcsFcltChange={onSwagePrcsFcltChange}
setSwtrmtFcltNm={setSwtrmtFcltNm}
setIsLoading={setIsLoading}
/>
{selectBox?.searchChk && (
@@ -18,6 +18,7 @@ import { AUTH_USER_ID } from "@/shared/config";
import { useQuery } from "@tanstack/react-query";
import { ENDPOINT } from "@common/components/config";
import { getUserInfo } from "@common/components/api";
import LoadingOverlay from '@common/business_components/ui/loadingBar';
const formSchema = z.object({
@@ -55,6 +56,7 @@ const IrSetting = () => {
const [ctpvCd, setCtpvCd] = useState<number>(0); // 시도 코드 상태
const [sggCd, setSggCd] = useState<number>(0); // 시군구 코드 상태
const [sggName, setSggName] = useState<string>(''); // 시군구 이름 상태
const [swtrmtFcltNm, setSwtrmtFcltNm] = useState<string>(''); // 하수처리시설명 상태
const { data: userInfo } = useQuery({
queryKey: [ENDPOINT.USER_SERVICE.USERS],
@@ -115,6 +117,7 @@ const IrSetting = () => {
setInput1(''); // 입력 필드 초기화
setInput2(''); // 입력 필드 초기화
setSelect1(0);
setSwtrmtFcltNm('');
setFormResetKey(prevKey => prevKey + 1); // FileUploadForm 재마운트를 위해 key 변경
};
@@ -156,6 +159,7 @@ const IrSetting = () => {
// 침입수 분석 실행 버튼 클릭 핸들러
const handleAnalysisClick = async () => {
console.log('분석 시작');
setIsLoadingResult(true); // 로딩 상태 시작
// 기본 선택 항목들 검증 (환경청, 시도, 시군구, 하수처리장)
@@ -296,9 +300,7 @@ const IrSetting = () => {
const combinedAnalysisData = {
ctpvCd: (ctpvCd ? String(ctpvCd) : null),
sggCd: (sggCd ? String(sggCd) : null), // 시군구코드 (선택값)
swageTmplntCd: (select1 && select1 !== '0' && select1 !== 0)
? select1.toString().split('.').pop() || null // '.'으로 split해서 마지막 부분만 사용
: null, // 하수처리장코드 (선택값)
swtrmtFcltNm: swtrmtFcltNm ?? null,
aplcnYm: selectedDate, // 적용연월
drtwSwtcRt: parseFloat(input1) || null, // 오수전환율 (숫자로 변환)
anlsInfo: input2, // 분석정보
@@ -330,6 +332,7 @@ const IrSetting = () => {
return (
<>
<LoadingOverlay isLoading={isLoadingResult} />
<BaseLayout
titleName={{ mainTitle: '침입수 분석', firstCardTitle: '침입수 분석 자료 입력' }}
form={form}
@@ -345,6 +348,7 @@ const IrSetting = () => {
setCtpvCd={setCtpvCd}
setSggCd={setSggCd}
setSggName={setSggName}
setSwtrmtFcltNm={setSwtrmtFcltNm}
setIsLoading={setIsLoadingResult}
onAnalysis={handleAnalysisClick}
>
+146 -145
View File
@@ -20,15 +20,15 @@ import { AUTH_USER_ID } from "@/shared/config";
import { useQuery } from "@tanstack/react-query";
import { ENDPOINT } from "@common/components/config";
import { getUserInfo } from "@common/components/api";
import LoadingOverlay from '@common/business_components/ui/loadingBar';
// zod 스키마 정의
const formSchema = z.object({
enoCd: z.string(), // 환경청코드
ctpvCd: z.string(), // 시도코드
sggCd: z.string(), // 시군구코드
swageTmplntCd: z.string(), // 하수처리장코드
swtrmtZoneCd: z.string(), // 하수처리구역코드
swtrmtFcltNm: z.string(), // 하수처리시설명
rtkSource: z.string(), // RTK계수 출처
rdiiArea: z.string(), // RDII 기여면적
unitFactors: z.array(z.object({ // 단위도 계수
@@ -56,11 +56,9 @@ const formSchema = z.object({
type FormSchemaType = z.infer<typeof formSchema>;
// 기본 폼 값 정의 (테스트 편의를 위해 초기값 설정, 추후 삭제 필요)
const defaultFormValues: FormSchemaType = {
enoCd: "",
ctpvCd: "",
sggCd: "",
swageTmplntCd: "",
swtrmtZoneCd: "",
swtrmtFcltNm: "",
rtkSource: "",
rdiiArea: "", // 문자열로 기본값 설정
unitFactors: [
@@ -114,10 +112,11 @@ export default function RTKForm() {
initialFileInfo, // 2: 강우자료
]);
const [select1, setSelect1] = useState<string | number>('0'); // 하수처리장 (swageTmplntCd)
const [select1, setSelect1] = useState<string | number>('0'); // 하수처리장 (swtrmtFcltCd)
const [ctpvCd, setCtpvCd] = useState<number>(0); // 시도 코드 상태
const [sggCd, setSggCd] = useState<number>(0); // 시군구 코드 상태
const [sggName, setSggName] = useState<string>(''); // 시군구 이름 상태
const [swtrmtFcltNm, setSwtrmtFcltNm] = useState<string>(''); // 하수처리시설명 상태
const { data: userInfo } = useQuery({
queryKey: [ENDPOINT.USER_SERVICE.USERS],
@@ -293,9 +292,7 @@ export default function RTKForm() {
aplcnYm: formData.date, // 적용연월 (YYYYMM 형식)
ctpvCd: ctpvCd ? String(ctpvCd) : null, // 시도코드 (선택값)
sggCd: sggCd ? String(sggCd) : null, // 시군구코드 (선택값)
swageTmplntCd: (select1 && select1 !== '0' && select1 !== 0)
? select1.toString().split('.').pop() || null // '.'으로 split해서 마지막 부분만 사용
: null, // 하수처리장코드 (선택값)
swtrmtFcltNm: swtrmtFcltNm ?? null,
atflCd: file1?.atflCd, // 첨부파일CD (오수관망도)
atflId: file1?.atflId, // 첨부파일ID (오수관망도)
atflInpNm: file1?.file?.name || "", // 오수관망도 파일명
@@ -324,9 +321,9 @@ export default function RTKForm() {
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"),
longPrdMaxDpth: parseFloat(formData.initialDepths[2]?.Dmax || "0"),
longPrdRcvryRt: parseFloat(formData.initialDepths[2]?.Drec || "0"),
longPrdBgngDpth: parseFloat(formData.initialDepths[2]?.Do || "0"),
drtwSwtcRt: parseFloat(formData.rate || "0"), // 오수전환율
anlsInfo: formData.analysisInfo, // 분석정보
@@ -386,140 +383,144 @@ export default function RTKForm() {
}
return (
<BaseLayout
selectBox={{ title: '분석 대상지역 선택' }}
titleName={{ mainTitle: 'RDII 분석', firstCardTitle: 'RDII 분석자료 입력', secondCardTitle: '실측자료 등록' }}
form={form} // react-hook-form 인스턴스 전달
onSubmitEvent={() => { }} // BaseLayout의 form 태그에 연결될 onSubmit
graphBtn={false}
analysisBtn={'RDII분석 실행'}
ctpvCd={ctpvCd}
sggCd={sggCd}
sggName={sggName}
selectedSwagePrcsFclt={select1}
onSwagePrcsFcltChange={setSelect1}
setCtpvCd={setCtpvCd}
setSggCd={setSggCd}
setSggName={setSggName}
setIsLoading={setIsLoadingResult}
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]"
/>
<>
<LoadingOverlay isLoading={isLoadingResult} />
<BaseLayout
selectBox={{ title: '분석 대상지역 선택' }}
titleName={{ mainTitle: 'RDII 분석', firstCardTitle: 'RDII 분석자료 입력', secondCardTitle: '실측자료 등록' }}
form={form} // react-hook-form 인스턴스 전달
onSubmitEvent={() => { }} // BaseLayout의 form 태그에 연결될 onSubmit
graphBtn={false}
analysisBtn={'RDII분석 실행'}
ctpvCd={ctpvCd}
sggCd={sggCd}
sggName={sggName}
selectedSwagePrcsFclt={select1}
onSwagePrcsFcltChange={setSelect1}
setCtpvCd={setCtpvCd}
setSggCd={setSggCd}
setSggName={setSggName}
setSwtrmtFcltNm={setSwtrmtFcltNm}
setIsLoading={setIsLoadingResult}
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>
<Label className="mb-[1.2rem] text-xl font-medium">○ 초기 차단 깊이</Label>
<EditableFormTable
form={form}
name="initialDepths"
fields={initialDepthFields}
headlist={initialDepthHeadlist}
tableClassName="w-[400px]"
/>
<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={".inp"}
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
value={form.watch("rate")} // form의 rate 값을 가져옴
onChange={(e) => {
const newValue = e.target.value;
if (newValue === "") {
form.setValue("rate", newValue); // form의 rate 필드에 설정
return;
}
// 숫자와 '.'만 허용하고, '.'은 최대 하나인지 확인
const isValidChars = /^[0-9]*\.?[0-9]*$/.test(newValue);
if (!isValidChars) return; // 유효하지 않은 문자 또는 여러 개의 '.' 포함
// '.' 기호가 처음에 오면 안 됨
if (newValue.startsWith('.')) return;
// 완성된 숫자인 경우에만 범위 체크 (입력 중인 경우 허용)
if (!newValue.endsWith('.')) {
const numericValue = parseFloat(newValue);
if (isNaN(numericValue) || numericValue < 0 || numericValue > 1) return;
}
form.setValue("rate", newValue, { shouldValidate: true }); // form의 rate 필드에 설정
}}
className="h-12 text-center text-xl w-[225px] ml-5"
placeholder="0 ~ 1 사이의 값 입력"
/>
</FormSection>
<FormSection name='date' form={form} titleNm='○ 상수사용량 적용월'>
<DateMonthPicker
field={{
value: form.watch("date"), // form의 date 값을 직접 사용
onChange: (value: string) => {
form.setValue("date", value, { shouldValidate: true }); // form의 date 필드에 직접 설정
}
}}
selectClass="ml-5 w-[225px]"
startYear={1900} // 시작 연도 설정
/>
</FormSection>
<FormSection name='rate' form={form} titleNm='○ 분석 정보'>
<Input
{...form.register("analysisInfo")}
className="h-12 text-center text-xl w-[225px] ml-5"
placeholder='세부 분석정보 입력'
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault(); // Enter 키 기본 동작 방지
handleAnalysisClick();
}
}}
/>
</FormSection>
</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={".inp"}
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
value={form.watch("rate")} // form의 rate 값을 가져옴
onChange={(e) => {
const newValue = e.target.value;
if (newValue === "") {
form.setValue("rate", newValue); // form의 rate 필드에 설정
return;
}
// 숫자와 '.'만 허용하고, '.'은 최대 하나인지 확인
const isValidChars = /^[0-9]*\.?[0-9]*$/.test(newValue);
if (!isValidChars) return; // 유효하지 않은 문자 또는 여러 개의 '.' 포함
// '.' 기호가 처음에 오면 안 됨
if (newValue.startsWith('.')) return;
// 완성된 숫자인 경우에만 범위 체크 (입력 중인 경우 허용)
if (!newValue.endsWith('.')) {
const numericValue = parseFloat(newValue);
if (isNaN(numericValue) || numericValue < 0 || numericValue > 1) return;
}
form.setValue("rate", newValue, { shouldValidate: true }); // form의 rate 필드에 설정
}}
className="h-12 text-center text-xl w-[225px] ml-5"
placeholder="0 ~ 1 사이의 값 입력"
/>
</FormSection>
<FormSection name='date' form={form} titleNm='○ 상수사용량 적용월'>
<DateMonthPicker
field={{
value: form.watch("date"), // form의 date 값을 직접 사용
onChange: (value: string) => {
form.setValue("date", value, { shouldValidate: true }); // form의 date 필드에 직접 설정
}
}}
selectClass="ml-5 w-[225px]"
startYear={1900} // 시작 연도 설정
/>
</FormSection>
<FormSection name='rate' form={form} titleNm='○ 분석 정보'>
<Input
{...form.register("analysisInfo")}
className="h-12 text-center text-xl w-[225px] ml-5"
placeholder='세부 분석정보 입력'
onKeyDown={(e) => {
if (e.key === 'Enter') {
e.preventDefault(); // Enter 키 기본 동작 방지
handleAnalysisClick();
}
}}
/>
</FormSection>
</div>
</div>
]}
</BaseLayout >
]}
</BaseLayout >
</>
)
}
@@ -8,11 +8,12 @@ import { z } from 'zod'
import BaseLayout from '../BaseLayout'
import { handleDelete } from '@/components/lib/utils'
import { formatDisplayData } from '@/components/lib/utils/formatDisplayValue';
import useStore from '../../../store/useStore';
const formSchema = z.object({
swageTmplntCd: z.string().default('0'), // 하수처리장
swtrmtZoneCd: z.string().default('0'), // 하수처리구역
swtrmtFcltCd: z.string().default('0'), // 하수처리시설코드
swtrmtFcltNm: z.string().default(''), // 하수처리시설명
aplcnYmFrom: z.string().default(() => { // Date에서 string(yyyyMM 형식)으로 변경
const now = new Date();
const year = now.getFullYear();
@@ -32,11 +33,9 @@ type ResultFormValues = z.infer<typeof formSchema>
const tableColumn = [
{ id: 'rowNumber', title: '일련번호' },
{ id: 'enoNm', title: '환경청' },
{ id: 'ctpvNm', title: '시도' },
{ id: 'sggNm', title: '시군구' },
{ id: 'swageTmplntNm', title: '하수처리장' },
// { id: 'swtrmtZoneNm', title: '처리구역' },
{ id: 'swtrmtFcltNm', title: '하수처리시설' },
{ id: 'aplcnYm', title: '상수사용연월' },
{ id: 'drtwSwtcRt', title: '오수 전환율' },
{ id: 'srcNm', title: 'RTK 계수 출처' },
@@ -53,8 +52,7 @@ const Result = () => {
const form = useForm<ResultFormValues>({
resolver: zodResolver(formSchema),
defaultValues: {
swageTmplntCd: '0',
swtrmtZoneCd: '0',
swtrmtFcltCd: '0',
aplcnYmFrom: (() => { // 현재 연월을 yyyyMM 형식으로 설정
const now = new Date();
const year = now.getFullYear();
@@ -83,6 +81,8 @@ const Result = () => {
const [sggCd, setSggCd] = useState<number>(0); // 시군구 코드 상태
const [sggName, setSggName] = useState<string>(''); // 시군구 이름 상태
const {ctpv, sgg} = useStore();
useEffect(() => {
// setCurPage(1);
handleSearchButtonClick();
@@ -109,22 +109,13 @@ const Result = () => {
const formValues = form.getValues(); // react-hook-form에서 현재 값 가져오기
console.log("formValues", formValues);
// swageTmplntCd 처리 로직 추가
const processedSwageTmplntCd = formValues.swageTmplntCd && formValues.swageTmplntCd !== '0'
? (() => {
const valString = String(formValues.swageTmplntCd);
const parts = valString.split('.');
return parts.length > 1 ? parts[1] : valString;
})()
: null;
let inftwtAnlsSearchDto = {
"aplcnYmFrom": formValues.aplcnYmFrom && formValues.aplcnYmFrom !== '' ? formValues.aplcnYmFrom : null,
"aplcnYmTo": formValues.aplcnYmTo && formValues.aplcnYmTo !== '' ? formValues.aplcnYmTo : null,
"ctpvCd": ctpvCd === 0 ? null : ctpvCd,
"sggCd": sggCd === 0 ? null : sggCd,
"swageTmplntCd": processedSwageTmplntCd,
"auth": AUTH,
const inftwtAnlsSearchDto = {
aplcnYmFrom: formValues.aplcnYmFrom?.trim() ? formValues.aplcnYmFrom : null,
aplcnYmTo: formValues.aplcnYmTo?.trim() ? formValues.aplcnYmTo : null,
ctpvCd: ctpvCd || null,
sggCd: sggCd || null,
swtrmtFcltNm: formValues.swtrmtFcltNm?.trim() ? formValues.swtrmtFcltNm : null,
auth: AUTH,
};
console.log("Search DTO:", inftwtAnlsSearchDto);
@@ -149,12 +140,15 @@ const Result = () => {
const dataWithRowNumber = (data.content || []).map((item: any, index: number) => {
// 🎯 먼저 각 아이템을 포맷팅
const formattedItem = formatDisplayData(item);
const ctpvOne = ctpv.find(code => code.id === item.ctpvCd);
const sggOne = sgg.find(code => code.id === item.sggCd);
// 추가 처리 및 일련번호 추가
return {
...formattedItem,
rowNumber: (curPage - 1) * 10 + index + 1, // 1부터 시작하는 일련번호 추가
scsYn: item.scsYn === null || item.scsYn === undefined ? 'N' : item.scsYn // scsYn이 null이면 'N'으로 변경
scsYn: item.scsYn === null || item.scsYn === undefined ? 'N' : item.scsYn, // scsYn이 null이면 'N'으로 변경
ctpvNm: ctpvOne ? ctpvOne.name : item.ctpvCd,
sggNm: sggOne ? sggOne.name : item.sggCd,
};
})
.filter((item: any) => item.scsYn === 'Y');
@@ -211,7 +205,7 @@ const Result = () => {
};
// 파일명 생성 (분석번호 기반)
const fileName = `RDII분석결과_${selectedRowData.swageTmplntNm}_${selectedRowData.aplcnYm}_${selectedRowData.rainInfwtAnlsSn}`;
const fileName = `RDII분석결과_${selectedRowData.swtrmtFcltNm}_${selectedRowData.aplcnYm}_${selectedRowData.rainInfwtAnlsSn}`;
await downloadExcelWithKoreanHeaders({
data: excelData as JSON[],
@@ -286,8 +280,8 @@ const Result = () => {
ctpvCd={ctpvCd}
sggCd={sggCd}
sggName={sggName}
selectedSwagePrcsFclt={form.watch("swageTmplntCd")}
onSwagePrcsFcltChange={(val) => form.setValue("swageTmplntCd", String(val))}
selectedSwagePrcsFclt={form.watch("swtrmtFcltCd")}
onSwagePrcsFcltChange={(val) => form.setValue("swtrmtFcltCd", String(val))}
aplcnYmFrom={form.watch("aplcnYmFrom")}
setAplcnYmFrom={(val: string) => { form.setValue("aplcnYmFrom", val) }}
aplcnYmTo={form.watch("aplcnYmTo")}
@@ -295,6 +289,7 @@ const Result = () => {
setCtpvCd={setCtpvCd}
setSggCd={setSggCd}
setSggName={setSggName}
setSwtrmtFcltNm={(val: string) => { form.setValue("swtrmtFcltNm", val) }}
setIsLoading={setIsLoadingResult}
// Excel 다운로드 핸들러 추가
onExcelDownload={handleExcelDownload}
@@ -8,11 +8,11 @@ import { z } from 'zod'
import BaseLayout from '../BaseLayout'
import { handleDelete } from '@/components/lib/utils'
import { formatDisplayData } from '@/components/lib/utils/formatDisplayValue';
import useStore from '../../../store/useStore';
const formSchema = z.object({
swageTmplntCd: z.string().default('0'), // 하수처리장
swtrmtZoneCd: z.string().default('0'), // 하수처리구역
swtrmtFcltCd: z.string().default('0'), // 하수처리시설코드
swtrmtFcltNm: z.string().default(''), // 하수처리시설명
aplcnYmFrom: z.string().default(() => { // Date에서 string(yyyyMM 형식)으로 변경
const now = new Date();
const year = now.getFullYear();
@@ -32,11 +32,9 @@ type ResultFormValues = z.infer<typeof formSchema>
const tableColumn = [
{ id: 'rowNumber', title: '일련번호' },
{ id: 'enoNm', title: '환경청' },
{ id: 'ctpvNm', title: '시도' },
{ id: 'sggNm', title: '시군구' },
{ id: 'swageTmplntNm', title: '하수처리장' },
// { id: 'swtrmtZoneNm', title: '처리구역' },
{ id: 'swtrmtFcltNm', title: '하수처리시설' },
{ id: 'aplcnYm', title: '적용연월' },
{ id: 'drtwSwtcRt', title: '오수 전환율' },
{ id: 'anlsInfo', title: '침입수 분석정보' },
@@ -51,8 +49,8 @@ const Result = () => {
const form = useForm<ResultFormValues>({
resolver: zodResolver(formSchema),
defaultValues: {
swageTmplntCd: '0',
swtrmtZoneCd: '0',
swtrmtFcltCd: '0',
swtrmtFcltNm: '',
aplcnYmFrom: (() => { // 현재 연월을 yyyyMM 형식으로 설정
const now = new Date();
const year = now.getFullYear();
@@ -79,6 +77,7 @@ const Result = () => {
const [ctpvCd, setCtpvCd] = useState<number>(0); // 시도 코드 상태
const [sggCd, setSggCd] = useState<number>(0); // 시군구 코드 상태
const [sggName, setSggName] = useState<string>(''); // 시군구 이름 상태
const {ctpv, sgg} = useStore();
useEffect(() => {
// setCurPage(1);
@@ -104,23 +103,12 @@ const Result = () => {
const formValues = form.getValues(); // react-hook-form에서 현재 값 가져오기
console.log("formValues", formValues);
// swageTmplntCd 처리 로직 추가
const processedSwageTmplntCd = formValues.swageTmplntCd && formValues.swageTmplntCd !== '0'
? (() => {
const valString = String(formValues.swageTmplntCd);
const parts = valString.split('.');
return parts.length > 1 ? parts[1] : valString;
})()
: null;
let inftwtAnlsSearchDto = {
"aplcnYmFrom": formValues.aplcnYmFrom && formValues.aplcnYmFrom !== '' ? formValues.aplcnYmFrom : null,
"aplcnYmTo": formValues.aplcnYmTo && formValues.aplcnYmTo !== '' ? formValues.aplcnYmTo : null,
"ctpvCd": ctpvCd === 0 ? null : ctpvCd,
"sggCd": sggCd === 0 ? null : sggCd,
"swageTmplntCd": processedSwageTmplntCd,
"swtrmtFcltNm": formValues.swtrmtFcltNm?.trim() ? formValues.swtrmtFcltNm : null,
"auth": AUTH,
};
@@ -147,12 +135,15 @@ const Result = () => {
const dataWithRowNumber = (data.content || []).map((item: any, index: number) => {
// 🎯 먼저 각 아이템을 포맷팅
const formattedItem = formatDisplayData(item);
const ctpvOne = ctpv.find(code => code.id === item.ctpvCd);
const sggOne = sgg.find(code => code.id === item.sggCd);
// 추가 처리 및 일련번호 추가
return {
...formattedItem,
rowNumber: (curPage - 1) * 10 + index + 1, // 1부터 시작하는 일련번호 추가
scsYn: item.scsYn === null || item.scsYn === undefined ? 'N' : item.scsYn // scsYn이 null이면 'N'으로 변경
scsYn: item.scsYn === null || item.scsYn === undefined ? 'N' : item.scsYn, // scsYn이 null이면 'N'으로 변경
ctpvNm: ctpvOne ? ctpvOne.name : item.ctpvCd,
sggNm: sggOne ? sggOne.name : item.sggCd,
};
})
.filter((item: any) => item.scsYn === 'Y');
@@ -209,7 +200,7 @@ const Result = () => {
};
// 파일명 생성 (분석번호 기반)
const fileName = `침입수분석결과_${selectedRowData.swageTmplntNm}_${selectedRowData.aplcnYm}_${selectedRowData.inftwtAnlsSn}`;
const fileName = `침입수분석결과_${selectedRowData.swtrmtFcltNm}_${selectedRowData.aplcnYm}_${selectedRowData.inftwtAnlsSn}`;
await downloadExcelWithKoreanHeaders({
data: excelData as JSON[],
@@ -285,8 +276,8 @@ const Result = () => {
ctpvCd={ctpvCd}
sggCd={sggCd}
sggName={sggName}
selectedSwagePrcsFclt={form.watch("swageTmplntCd")}
onSwagePrcsFcltChange={(val) => form.setValue("swageTmplntCd", String(val))}
selectedSwagePrcsFclt={form.watch("swtrmtFcltCd")}
onSwagePrcsFcltChange={(val) => form.setValue("swtrmtFcltCd", String(val))}
aplcnYmFrom={form.watch("aplcnYmFrom")}
setAplcnYmFrom={(val: string) => { form.setValue("aplcnYmFrom", val) }}
aplcnYmTo={form.watch("aplcnYmTo")}
@@ -294,8 +285,8 @@ const Result = () => {
setCtpvCd={setCtpvCd}
setSggCd={setSggCd}
setSggName={setSggName}
setSwtrmtFcltNm={(val: string) => { form.setValue("swtrmtFcltNm", val) }}
setIsLoading={setIsLoadingResult}
// Excel 다운로드 핸들러 추가
onExcelDownload={handleExcelDownload}
onDeleteFile={handleFileDelete}
selectedRowData={selectedRowData}
@@ -1,5 +1,5 @@
'use client'
import { deleteData, postData } from '@/components/lib/api'
import { deleteData, postData, useCookieAuth } from '@/components/lib/api'
import { searchData } from '@/components/lib/common/apiHelpers'
import { formatDate, handleDownload } from '@/components/lib/utils'
import LoadingOverlay from '@/components/ui/LoadingBar'
@@ -9,9 +9,16 @@ import { Button, DatePicker, Input, Label } from '@common/components/ui'
import { useEffect, useState } from 'react'
import { tableHeadlist } from '..'
import OperFileUploadForm from '../../../components/ui/operFileUploadForm'
import { tokenManager } from '@/entities/auth/lib/token-manager';
import { AUTH_USER_ID } from "@/shared/config";
import { useQuery } from "@tanstack/react-query";
import { ENDPOINT } from "@common/components/config";
import { getUserInfo } from "@common/components/api";
const Page = () => {
const AUTH = useCookieAuth();
// 조회조건
const [fromDate, setFromDate] = useState(() => { const date = new Date(); date.setDate(date.getDate() - 7); return date })
const [toDate, setToDate] = useState(new Date())
@@ -29,6 +36,12 @@ const Page = () => {
const [resetSelection, setResetSelection] = useState<boolean>(false); // 선택 초기화 상태
const [isResetPage, setIsResetPage] = useState<boolean>(false);
const { data: userInfo } = useQuery({
queryKey: [ENDPOINT.USER_SERVICE.USERS],
queryFn: () => getUserInfo(),
enabled: true,
});
useEffect(() => {
setCurPage(1)
fetchData()
@@ -57,7 +70,7 @@ const Page = () => {
fromRegDt: formatDate(fromDate),
toRegDt: formatDate(toDate),
sbmrgPrevntBizNm: pjNm,
rgtrNm: usr,
auth: AUTH,
page: curPage - 1,
size: '10'
})
@@ -109,14 +122,17 @@ const Page = () => {
// 현황 등록
const registFile = async (fileName: string, year: string, quater: string, cd: string, id: string) => {
const data = await postData('v1/sbmrgPrevnt', {
const json = {
atflNm: fileName,
mngYr: year,
mngQu: quater,
atflCd: cd,
atflId: id,
sbmrgPrevntBizNm: fileName
})
sbmrgPrevntBizNm: fileName,
rgtr: tokenManager.getToken(AUTH_USER_ID) || null
}
console.log('request DTO', json);
const data = await postData('v1/sbmrgPrevnt', json)
if (data) {
alert('파일이 등록되었습니다.');
setIsModalOpen(false);
+2 -4
View File
@@ -1,19 +1,17 @@
export const tableHeadlist = [
{ id: 'rowNumber', title: '일련번호' },
// { id: 'sbmrgPrevntBizSn', title: '일련번호' },
{ id: 'sbmrgPrevntBizNm', title: '파일명' },
{ id: 'mngYr', title: '년도' },
{ id: 'mngQu', title: '분기' },
{ id: 'regDt', title: '등록 일자' },
{ id: 'rgtr', title: '등록자' }
{ id: 'rgtrNm', title: '등록자' }
]
export const tableHeadlistOldPp = [
{ id: 'rowNumber', title: '일련번호' },
// { id: 'dcrpPplMtncBizSn', title: '일련번호' },
{ id: 'dcrpPplMtncBizNm', title: '파일명' },
{ id: 'mngYr', title: '년도' },
{ id: 'mngQu', title: '분기' },
{ id: 'regDt', title: '등록 일자' },
{ id: 'rgtr', title: '등록자' }
{ id: 'rgtrNm', title: '등록자' }
]
@@ -1,5 +1,5 @@
'use client'
import { deleteData, postData } from '@/components/lib/api'
import { deleteData, postData, useCookieAuth } from '@/components/lib/api'
import { searchData } from '@/components/lib/common/apiHelpers'
import { formatDate, handleDownload, yearOptions } from '@/components/lib/utils'
import LoadingOverlay from '@/components/ui/LoadingBar'
@@ -9,9 +9,16 @@ import { Button, DatePicker, Input, Label } from '@common/components/ui'
import { useEffect, useState } from 'react'
import { tableHeadlistOldPp } from '..'
import OperFileUploadForm from '../../../components/ui/operFileUploadForm'
import { tokenManager } from '@/entities/auth/lib/token-manager';
import { AUTH_USER_ID } from "@/shared/config";
import { useQuery } from "@tanstack/react-query";
import { ENDPOINT } from "@common/components/config";
import { getUserInfo } from "@common/components/api";
const Page = () => {
const AUTH = useCookieAuth();
const [fromDate, setFromDate] = useState<Date>(() => { const date = new Date(); date.setDate(date.getDate() - 7); return date });
const [toDate, setToDate] = useState<Date>(new Date());
const [usr, setUsr] = useState('')
@@ -27,6 +34,12 @@ const Page = () => {
const [resetSelection, setResetSelection] = useState<boolean>(false); // 선택 초기화 상태
const [isResetPage, setIsResetPage] = useState<boolean>(false);
const { data: userInfo } = useQuery({
queryKey: [ENDPOINT.USER_SERVICE.USERS],
queryFn: () => getUserInfo(),
enabled: true,
});
useEffect(() => {
setCurPage(1)
fetchData()
@@ -54,7 +67,7 @@ const Page = () => {
fromRegDt: formatDate(fromDate),
toRegDt: formatDate(toDate),
dcrpPplMtncBizNm: pjNm,
rgtrNm: usr,
auth: AUTH,
page: curPage - 1,
size: '10'
})
@@ -112,7 +125,9 @@ const Page = () => {
mngQu: quater,
atflCd: cd,
atflId: id,
dcrpPplMtncBizNm: fileNm
dcrpPplMtncBizNm: fileNm,
rgtr: tokenManager.getToken(AUTH_USER_ID) || null,
rgtrNm: userInfo?.userName,
})
if (data) {
alert('파일이 등록되었습니다.');
@@ -20,6 +20,7 @@ interface CtpvSggPrcsFcltSelectBoxProps {
className?: string;
selectedSwagePrcsFclt?: string | number;
onSwagePrcsFcltChange?: (value: string | number) => void;
setSwtrmtFcltNm?: (name: string) => void;
setIsLoading?: (isLoading: boolean) => void;
}
@@ -36,6 +37,7 @@ export function CtpvSggPrcsFcltSelectBox({
className = "",
selectedSwagePrcsFclt,
onSwagePrcsFcltChange,
setSwtrmtFcltNm,
setIsLoading,
}: CtpvSggPrcsFcltSelectBoxProps) {
@@ -50,6 +52,7 @@ export function CtpvSggPrcsFcltSelectBox({
setCtpvName("");
setSggName("");
}
if (setSwtrmtFcltNm) setSwtrmtFcltNm("");
})();
}, []);
@@ -118,6 +121,7 @@ export function CtpvSggPrcsFcltSelectBox({
onSelectValue={val => {
onSidoChange(Number(val));
if (setCtpvName) setCtpvName(ctpv.find(opt => Number(opt.id) === Number(val))?.name || "");
if (setSwtrmtFcltNm) setSwtrmtFcltNm("");
}}
selectContentClass="z-[101]"
/>
@@ -137,6 +141,7 @@ export function CtpvSggPrcsFcltSelectBox({
onSelectValue={val => {
onSigunguChange(Number(val))
if (setSggName) setSggName(sgg.find(opt => Number(opt.id) === Number(val))?.name || "");
if (setSwtrmtFcltNm) setSwtrmtFcltNm("")
}}
selectContentClass="z-[101]"
/>
@@ -149,7 +154,10 @@ export function CtpvSggPrcsFcltSelectBox({
label=""
selectArray={[defaultOption, ...prcsFcltOptions]}
value={selectedSwagePrcsFclt}
onSelectValue={(val) => onSwagePrcsFcltChange?.(val)}
onSelectValue={(val) => {
onSwagePrcsFcltChange?.(val)
if (setSwtrmtFcltNm) setSwtrmtFcltNm(prcsFcltOptions.find(opt => String(opt.val) === String(val))?.text || "")
}}
selectContentClass="z-[101]"
/>
</>
-11122
View File
File diff suppressed because it is too large Load Diff