Compare commits

...
Author SHA1 Message Date
leedj312 3b6e8ceedb 빗물 통계 수정 2025-07-22 20:55:00 +09:00
leedj312 b4e3d922b1 Merge commit 'a028b6ae8bba8e074e82900d05925d4e5a9c6951' into iipbs/wr/dev 2025-07-22 15:38:12 +09:00
leedj312 a028b6ae8b ui 테이블 체크박스 사용시 클래스 타입을 사용 체크 2025-07-22 15:37:54 +09:00
leedj312 ad0803f405 Merge commit '03675aaa6062402fbb3772e97cd966c0f7abd890' into iipbs/wr/dev 2025-07-22 14:38:24 +09:00
jyha00 03675aaa60 재이용통계 엑셀 다운로드시 시도명 없음 수정 2025-07-22 14:36:09 +09:00
leedj312 c0c19c0425 매칭완료 오류사항 수정 2025-07-22 14:32:25 +09:00
leedj312 214d283366 Merge commit '7d95ddf6a521f108b3b49d71760ba6e92a56d2dc' into iipbs/wr/login-test 2025-07-22 13:56:46 +09:00
truthwing 7d95ddf6a5 ChkClass 타입의 정보로 동작되는 체크박스 추가. 2025-07-22 13:55:31 +09:00
jyha00 a19c5c269a 지도점검 테이블 재수정 2025-07-22 13:37:35 +09:00
jyha00 6102e0af6b 지도점검 테이블 수정 2025-07-22 13:10:22 +09:00
jyha00 225bae08d3 readOnly 수정 2025-07-22 13:09:20 +09:00
leedj312 d8b5abb12f 미들웨어 반영 기본값 반영 2025-07-22 10:36:56 +09:00
leedj312 d9a3da96bb 베이스 변경사항 2025-07-22 09:24:19 +09:00
leedj312 87d1f33c05 Merge commit 'd68c6c151c5135ccb1f326fd36cd4b02200106f7' into iipbs/wr/login-test 2025-07-22 09:23:56 +09:00
leedj312 d68c6c151c Merge commit 'ddf3a5cfa0aedbb2bb73e073ff97fa68ca2c5d9a' into iipbs/wr/dev 2025-07-21 20:00:43 +09:00
leedj312 dfb910f270 Merge commit '5976b35c902b1fb1da1fdc637e620a0d0d3e9ede' into iipbs/wr/dev 2025-07-21 19:58:48 +09:00
leedj312 ddf3a5cfa0 캘린더 함수 오류 2025-07-21 19:56:57 +09:00
jyha00 5976b35c90 결함 오류 수정 2025-07-21 19:56:41 +09:00
truthwing 6b204fce15 djlee product 2025-07-21 19:51:52 +09:00
leedj312 f731d0f9f0 변경사항 2025-07-21 19:04:13 +09:00
leedj312 e231d0c4be 수정 사항 발견 2025-07-21 18:05:13 +09:00
leedj312 e4328a2ed4 Merge commit '069e489d031203200fc4d73e470767503a3dc227' into iipbs/wr/dev 2025-07-21 18:01:43 +09:00
leedj312 069e489d03 온배수 현황 수질 작업중 2025-07-21 17:59:55 +09:00
leedj312 990dffc11c 물재이용 / 이동시 메인으로 이동 2025-07-21 17:59:38 +09:00
jyha00 b4140d6457 결함 수정 2025-07-21 17:50:47 +09:00
jyha00 91af5fd27c Merge branch 'iipbs/common/main' into iipbs/wr/pfmncSttn 2025-07-21 16:15:56 +09:00
jyha00 0bb16f9795 도넛차트 범례 높이 조정 2025-07-21 15:28:42 +09:00
jyha00 be7efb2ad0 결함 오류 수정 2025-07-21 15:27:18 +09:00
jyha00 fbd9575df7 잠재수요처 페이지 오류 수정 2025-07-21 11:07:51 +09:00
truthwing 14608cca4d Merge remote-tracking branch 'origin/iipbs/wr/dev' 2025-07-21 10:11:39 +09:00
truthwing 0dab113a3f Merge commit '65d2cbd73284d64bddc0c5495a97cf094e518f6a' 2025-07-21 10:07:02 +09:00
yschoi 95fc7a22dc 운영 env 수정 2025-07-19 22:15:18 +09:00
jyha00 4c955beff2 결함 수정 2025-07-18 18:11:09 +09:00
leedj312 ae41ea100c map 주소 변경 2025-07-17 19:53:16 +09:00
leedj312 07e34beca1 Merge commit 'ae97a48ba246ef429633640b97742adbb524c239' into iipbs/wr/dev 2025-07-17 19:33:01 +09:00
leedj312 87d8ddef98 Merge commit '8b29d0288005b94e2c9206d7f40c3975efb4a485' into iipbs/wr/dev 2025-07-17 19:31:42 +09:00
jyha00 8b29d02880 하수재이용/빗물/중수도/온배수 등록 페이지 전체적인 레이아웃 수정 2025-07-17 18:08:53 +09:00
leedj312 ae97a48ba2 사이드바 매뉴이동 수정 2025-07-17 17:25:05 +09:00
mind-one003 65d2cbd732 빌드에러 수정 2025-07-17 16:56:41 +09:00
leedj312 db70ba56c6 개발서버 env변경 2025-07-17 16:26:47 +09:00
leedj312 3fa1c526fe env 변경 2025-07-17 16:20:13 +09:00
leedj312 992893510e Merge remote-tracking branch 'origin/iipbs/wr/pfmncSttn' into iipbs/wr/dev 2025-07-17 16:05:11 +09:00
leedj312 5fe4bc94da Merge commit 'c22e07bc16afbc2888806b4e0d36606b8ea0d35f' into iipbs/wr/dev 2025-07-17 16:00:31 +09:00
leedj312 c22e07bc16 로딩바 className으로 z-index변경할수 있도록 수정 2025-07-17 15:59:22 +09:00
leedj312 5f302a5711 로그인 테스트 작업중 2025-07-17 15:57:20 +09:00
jyha00 cc749c0c24 Merge remote-tracking branch 'origin/iipbs/common/main' into iipbs/wr/pfmncSttn 2025-07-17 13:16:56 +09:00
jyha00 18ec5c937f 도넛차트 custom label 추가 2025-07-17 13:08:14 +09:00
leedj312 bd6dfd31fd 엑셀 중수도 지도점검 데이터 확인 2025-07-16 19:27:24 +09:00
YangJinSoo94 e4f4f70bc9 유량현황, 수질현황 모달 엑셀다운로드 완료 2025-07-16 17:13:19 +09:00
jyha00 5aaa55d0c9 수요매칭검토 기본사항 readonly 추가 2025-07-16 16:29:25 +09:00
jyha00 8b1e8ae2eb datepicker 날짜가 하루 전 날로 들어가는 오류 수정 2025-07-16 15:33:57 +09:00
jyha00 5a3ef19e5d 하수재이용실적 현황 검색조건 수정 2025-07-16 15:32:18 +09:00
jyha00 01cb48e5b6 하수재이용시설 등록 보증수질 input 변경, 신규등록 저장 버튼 위치 수정, 상단 고정 추가 2025-07-16 14:55:44 +09:00
leedj312 ea7536bea3 안쓰는 함수 제거 2025-07-16 13:58:19 +09:00
leedj312 2356c10d34 빗물지도점검 오류 검증 2025-07-16 13:43:03 +09:00
jyha00 8b2dd27a79 하수재이용시설 등록 저장 버튼 위치 변경 2025-07-16 13:37:57 +09:00
YangJinSoo94 114293bb00 Merge branch 'iipbs/common/main' into iipbs/fo/dev 2025-07-16 13:32:44 +09:00
YangJinSoo94 e0171a5a28 바차트 색상 구성 수정 2025-07-16 13:30:25 +09:00
YangJinSoo94 d7db174c1d 대시보드 차트 색상 수정, 모달 강우량 컬럼추가 2025-07-16 13:29:45 +09:00
jyha00 079542cf74 Merge branch 'iipbs/common/main' into iipbs/wr/pfmncSttn 2025-07-16 13:20:32 +09:00
jyha00 43f210a141 searchBaseForm, searchFormLeft에 className 프로퍼티 추가 2025-07-16 13:17:04 +09:00
yschoi 5015b8e0d9 output: "standalone", 추가 2025-07-16 11:11:50 +09:00
YangJinSoo94 4d0c0ad48c 오류수정 2025-07-16 10:19:03 +09:00
truthwing 7db61928b1 양진수 대리 컴퓨터 바꿔야하는 오류 발견. 2025-07-15 21:03:56 +09:00
YangJinSoo94 3dae039765 first init 2025-07-15 20:54:38 +09:00
leedj312 35ff13fb38 헤드리스트 수정 중 2025-07-15 20:27:43 +09:00
leedj312 231c0c2940 Merge commit 'a92f567e5c624486d80984c5e4427543aafdfdde' into iipbs/wr/dev 2025-07-15 20:27:30 +09:00
jyha00 a92f567e5c 재이용실적 현황 페이지 오류 수정 2025-07-15 20:23:02 +09:00
leedj312 ff9d373fcd Merge commit 'c111c2c084cfe533453f568d17c9fa5bcba02797' into iipbs/wr/dev 2025-07-15 20:22:57 +09:00
leedj312 c111c2c084 서버설정 작업 완료 2025-07-15 20:22:31 +09:00
leedj312 2d5a08a271 안쓰는 코드 삭제 2025-07-15 17:16:37 +09:00
leedj312 9a8abee77c 빌드에러 수정 2025-07-15 16:52:42 +09:00
jyha00 4c30c2fa47 물재이용 페이지별 오류 수정 2025-07-15 15:40:55 +09:00
leedj312 0cca1c8dbe 빌드 확인필요 2025-07-15 15:39:32 +09:00
jyha00 def7b2b01c 지도점검 등록 및 현황 단위 추가, 용수명 가운데 정렬로 수정 2025-07-15 13:54:30 +09:00
jyha00 4891725f68 물재이용 페이지 단위 추가 2025-07-15 11:11:29 +09:00
283 changed files with 16801 additions and 12077 deletions
View File
+12
View File
@@ -0,0 +1,12 @@
NEXT_PUBLIC_SITE_ID = FO
NEXT_PUBLIC_PORT = 11300
NEXT_PUBLIC_PROXY_HOST = http://con.mind-one.co.kr:11300
NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
NEXT_PUBLIC_GEOSERVER_URL = http://rsone.iptime.org:10010/geoserver
NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver
NEXT_PUBLIC_CLAIM_NAME = Authorization
NEXT_PUBLIC_AUTH_USER_ID = token-id
NEXT_PUBLIC_ACCESS_TOKEN = access-token
NEXT_PUBLIC_REFRESH_TOKEN = refresh-token
NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api # 개발 서버 API
+13
View File
@@ -0,0 +1,13 @@
NEXT_PUBLIC_SITE_ID = FO
NEXT_PUBLIC_PORT = 3000
NEXT_PUBLIC_PROXY_HOST = http://localhost:3000
NEXT_PUBLIC_SERVER_API_URL = http://dev.onasys.co.kr:8000
NEXT_PUBLIC_GEOSERVER_URL = http://rsone.iptime.org:10010/geoserver
NEXT_PUBLIC_LX_GEOSERVER_URL = http://con.mind-one.co.kr:11306/geoserver
NEXT_PUBLIC_CLAIM_NAME = Authorization
NEXT_PUBLIC_AUTH_USER_ID = token-id
NEXT_PUBLIC_ACCESS_TOKEN = access-token
NEXT_PUBLIC_REFRESH_TOKEN = refresh-token
NEXT_PUBLIC_SEWERAGE_API_URL=http://con.mind-one.co.kr:11083/sewerage-service/api # 개발 서버 API
#NEXT_PUBLIC_SEWERAGE_API_URL=http://192.168.1.128:8883/sewerage-service/api # 최용성 PC 서버 API
+41
View File
@@ -0,0 +1,41 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
/node_modules
/.pnp
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/versions
# testing
/coverage
# next.js
/.next/
/out/
# production
/build
# misc
.DS_Store
*.pem
# debug
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# env files (can opt-in for committing if needed)
#.env*
# vercel
.vercel
# typescript
*.tsbuildinfo
next-env.d.ts
+12
View File
@@ -0,0 +1,12 @@
const DEV_PORT_COMMON = "11081";
const DEV_PORT_FO = "11082";
const DEV_PORT_WR = "11080";
export const LOCAL_URL_COMMON = `http://192.168.1.129:${DEV_PORT_COMMON}/api/v1`;
export const DEV_URL_COMMON = `http://con.mind-one.co.kr:${DEV_PORT_COMMON}/api/v1`;
export const LOCAL_URL_FO = `http://192.168.1.129:${DEV_PORT_FO}/api/v1`;
export const DEV_URL_FO = `http://con.mind-one.co.kr:${DEV_PORT_FO}/api/v1`;
export const LOCAL_URL_WR = `http://192.168.1.129:${DEV_PORT_WR}/api/v1`;
export const DEV_URL_WR = `http://con.mind-one.co.kr:${DEV_PORT_WR}/api/v1`;
@@ -0,0 +1 @@
export * from './env';
+79
View File
@@ -0,0 +1,79 @@
import { DEV_URL_COMMON, DEV_URL_FO, DEV_URL_WR } from "./config/env";
export const getFetch = async (part : string, url: string) => {
let reqUrl = "";
reqUrl = part === "fo" ? DEV_URL_FO+url : part === "wr" ? DEV_URL_WR+url : DEV_URL_COMMON+url;
const response = await fetch(reqUrl , {
method: "GET",
headers: {
accept: "application/json",
}
});
if(!response.ok) {
throw new Error(`오류 발생: ${response.status}`);
}
const res = await response.json()
return res;
}
export const getFetchAreaList = async (url: string) => {
const response = await fetch(url , {
method: "GET",
headers: {
accept: "application/json",
}
});
if(!response.ok) {
throw new Error(`오류 발생: ${response.status}`);
}
const res = await response.json()
return res;
}
export const postFetch = async (part : string, url: string, params: unknown) => {
let reqUrl = "";
reqUrl = part === "fo" ? DEV_URL_FO+url : DEV_URL_COMMON+url;
const response = await fetch(reqUrl, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(params),
});
if(!response.ok) {
throw new Error(`오류 발생: ${response.status}`);
}
return await response.json();
}
export const putFetch = async (part : string, url: string, params: unknown) => {
let reqUrl = "";
reqUrl = part === "fo" ? DEV_URL_FO+url : DEV_URL_COMMON+url;
const response = await fetch(reqUrl, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(params),
});
if(!response.ok) {
throw new Error(`오류 발생: ${response.status}`);
}
return response;
}
export const deleteFetch = async (part : string, url: string, params: unknown) => {
let reqUrl = "";
reqUrl = part === "fo" ? DEV_URL_FO+url : DEV_URL_COMMON+url;
const response = await fetch(reqUrl, {
method: "DELETE",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(params),
});
if(!response.ok) {
throw new Error(`오류 발생: ${response.status}`);
}
return response;
}
@@ -0,0 +1,46 @@
type DataFormProps = {
type: string;
column: string;
item: Record<string, unknown>;
}
export default function DataForm({ type, column, item }: DataFormProps) {
if(item) {
const value = item[column];
switch(type){
case 'date':
if(value && typeof value === 'string' && value.length >= 8)
item[column] = value.substring(0, 4) + "-" + value.substring(4, 6) + "-" + value.substring(6, 8);
break;
case 'number':
if(value && value != '') {
item[column] = value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
break;
case 'float1':
if(value) {
item[column] = String(value).includes('.')?String(Math.round(parseFloat(value.toString().replace(/,/g, '')) * 10) / 10).split('.').map((part, index) =>
index === 0 ? part.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : part
).join('.')
: String(value).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
break;
case 'float2':
if(value) {
item[column] = String(value).includes('.')?String(Math.round(parseFloat(value.toString().replace(/,/g, '')) * 100) / 100).split('.').map((part, index) =>
index === 0 ? part.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : part
).join('.')
: String(value).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
break;
case 'float3':
if(value) {
item[column] = String(value).includes('.')?String(Math.round(parseFloat(value.toString().replace(/,/g, '')) * 1000) / 1000).split('.').map((part, index) =>
index === 0 ? part.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : part
).join('.')
: String(value).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
break;
}
}
}
@@ -0,0 +1,279 @@
import { useFoStore } from '@/app/store';
import { DatePickerRange, SelectBox } from '@common/business_components/ui';
import ListDiv from '@common/business_components/ui/list_div';
import { Table, TableCell, TableHead, TableRow } from '@common/components/ui';
import { useCallback, useEffect, useRef, useState } from 'react';
type FacilityTermModalHeaderProps<T extends { msrDt: string }> = {
fcltyCd : string;
fcltyName: string;
tabName: string;
searchYear: string;
searchList: T[];
ref: React.RefObject<ReturnType<T>>;
queryClick:(returnType:ReturnType<T>) => void;
}
export type ReturnType<T> = {
startDate: Date;
endDate: Date;
tabName: string;
filteredList: T[];
}
export default function FacilityTermModalHeader<T extends { msrDt: string }>({
searchList,
fcltyName,
tabName,
searchYear,
queryClick,
ref
}: FacilityTermModalHeaderProps<T & { msrDt: string }>) {
const {Common} = useFoStore((state) => state);
const { selectFlowPanel } = Common;
const [startDate, setStartDate] = useState<Date>(new Date(Number(searchYear), 0, 1));
const [endDate, setEndDate] = useState<Date>(new Date(Number(searchYear), 11, 31));
const startPickerRef = useRef<HTMLDivElement>(null);
const endPickerRef = useRef<HTMLDivElement>(null);
const [flowPanel, setFlowPanel] = useState<string>(selectFlowPanel[0]?.val ?? '00');
const [copySearchList, setCopySearchList] = useState<T[]>(searchList);
const [standSearchList, setStandSearchList] = useState<T[]>(searchList);
const isDateInRanges = (date: Date) => {
const standYear = parseInt(searchYear);
const dateRanges:{start:Date, end:Date}[] = [];
if (tabName.includes("_summer")) {
dateRanges.push({
start: new Date(standYear, 5, 1),
end: new Date(standYear, 7, 31)
});
} else if (tabName.includes("_winter")) {
dateRanges.push({
start: new Date(standYear, 0, 1),
end: new Date(standYear, 1, 28)
});
} else {
dateRanges.push({
start: new Date(standYear, 0, 1),
end: new Date(standYear, 11, 31)
});
}
return dateRanges.some(range => {
const start = new Date(range.start);
const end = new Date(range.end);
return date >= start && date <= end;
});
};
useEffect(() => {
let startDate = new Date(Number(searchYear), 0, 1);
let endDate = new Date(Number(searchYear), 11, 31);
if(tabName.includes("_summer")) {
startDate = new Date(Number(searchYear), 5, 1);
endDate = new Date(Number(searchYear), 7, 31);
setStartDate(startDate);
setEndDate(endDate);
} else if (tabName.includes("_winter")) {
startDate = new Date(Number(searchYear), 0, 1);
endDate = new Date(Number(searchYear), 1, 28);
setStartDate(startDate);
setEndDate(endDate);
} else {
startDate = new Date(Number(searchYear), 0, 1);
endDate = new Date(Number(searchYear), 11, 31);
setStartDate(startDate);
setEndDate(endDate);
}
let filteredData: T[] = [];
if(tabName.includes("_rain")) {
filteredData = searchList.filter((item) => {
const itemDate = new Date((item as T & { msrDt: string }).msrDt);
const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate());
const startDateOnly = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());
const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
const rainEffectDay = (item as T & { rainEffectDay: number }).rainEffectDay;
if(flowPanel === '1') {
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay>0 && infoRnDay < 3;
} else if(flowPanel === '2'){
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 3 && infoRnDay < 10;
} else if(flowPanel === '3'){
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 10 && infoRnDay < 20;
} else if(flowPanel === '4'){
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 20 && infoRnDay < 30;
} else if(flowPanel === '5'){
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 30 && infoRnDay < 40;
} else if(flowPanel === '6'){
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 40 && infoRnDay < 50;
} else if(flowPanel === '7'){
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 50 && infoRnDay < 60;
} else if(flowPanel === '8'){
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 60;
} else {
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && (infoRnDay>0 || rainEffectDay ===1);
}
});
} else if(tabName.includes("_chuncheon")) {
filteredData = searchList.filter((item) => {
const itemDate = new Date((item as T & { msrDt: string }).msrDt);
const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate());
const startDateOnly = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());
const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay==0;
});
} else {
filteredData = searchList.filter((item) => {
const itemDate = new Date((item as T & { msrDt: string }).msrDt);
const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate());
const startDateOnly = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());
const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
return itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly;
});
}
setCopySearchList(filteredData);
setStandSearchList(filteredData);
}, [searchList, searchYear, tabName, flowPanel]);
const handleStartDateSelect = (day: Date | undefined) => {
if(day && day > endDate) {
alert('종료일은 시작일 이전일 수 없습니다.');
return;
}
const selectedDate = day ?? startDate;
setStartDate(selectedDate);
const button = startPickerRef.current?.querySelector('button');
if (button) {
setTimeout(() => button.click(), 0);
}
const filteredData = standSearchList.filter((item) => {
const itemDate = new Date((item as T & { msrDt: string }).msrDt);
const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate());
const startDateOnly = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate());
const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
return itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly;
});
setCopySearchList(filteredData);
};
const handleEndDateSelect = (day: Date | undefined) => {
if(day && day < startDate) {
alert('종료일은 시작일 이후일 수 없습니다.');
return;
}
const selectedDate = day ?? endDate;
setEndDate(selectedDate);
const button = endPickerRef.current?.querySelector('button');
if (button) {
setTimeout(() => button.click(), 0);
}
const filteredData = standSearchList.filter((item) => {
const itemDate = new Date((item as T & { msrDt: string }).msrDt);
const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate());
const startDateOnly = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());
const endDateOnly = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate());
return itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly;
});
setCopySearchList(filteredData);
};
const fncSetFilteredList = useCallback(() => {
queryClick({
startDate: startDate,
endDate: endDate,
tabName: tabName,
filteredList: copySearchList
});
}, [queryClick, startDate, endDate, tabName, copySearchList]);
useEffect(() => {
fncSetFilteredList();
}, [fncSetFilteredList]);
useEffect(() => {
if(ref && ref.current) {
const customRef = ref.current;
customRef.startDate = startDate;
customRef.endDate = endDate;
customRef.tabName = tabName;
customRef.filteredList = copySearchList;
}
}, [startDate, endDate, tabName, copySearchList, ref]);
return (
<ListDiv className="!h-[80px]">
<div className="!m-[0.5rem] max-h-[40rem] overflow-auto">
<Table>
<thead>
<TableRow className="!h-[20px]">
<TableHead className="w-[120px]">{'시설명'}</TableHead>
{tabName === "tab_term_rain" ? <TableCell className="w-[300px]">{fcltyName}</TableCell> :
<TableCell className="w-[380px]">{fcltyName}</TableCell>}
{tabName === "tab_term_rain" && (
<>
<TableHead className="w-[120px]">{'강수구분'}</TableHead>
<TableCell className="w-[250px]">
<div className="justify-center">
<SelectBox
selectArray={selectFlowPanel}
value={flowPanel}
onSelectValue={(val) => setFlowPanel(val.toString())}
className="mx-3 w-[150px]"
label=""
selectClass="w-full p-1 border rounded bg-white"
/>
</div>
</TableCell>
</>
)}
<TableHead className="w-[120px]">{'년/월'}</TableHead>
<TableCell className="">
<div className="flex">
<div ref={startPickerRef} className="flex justify-center">
<DatePickerRange
mode="single"
selected={startDate}
onSelect={(date) => handleStartDateSelect(date as Date)}
className="w-full p-1 border rounded bg-white"
disabled={(date) => !isDateInRanges(date)}
defaultMonth={startDate}
/>
</div>
<h3 className="justify-center px-5">~</h3>
<div ref={endPickerRef} className="flex justify-center">
<DatePickerRange
mode="single"
selected={endDate}
onSelect={(date) => handleEndDateSelect(date as Date)}
className="w-full p-1 border rounded bg-white"
disabled={(date) => !isDateInRanges(date)}
defaultMonth={endDate}
/>
</div>
</div>
</TableCell>
</TableRow>
</thead>
</Table>
</div>
</ListDiv>
);
}
@@ -0,0 +1,276 @@
import { useFoStore } from '@/app/store';
import { SelectBox } from '@common/business_components/ui';
import ListDiv from '@common/business_components/ui/list_div';
import { DatePicker, Table, TableCell, TableHead, TableRow } from '@common/components/ui';
import { useCallback, useEffect, useRef, useState } from 'react';
type FacilityTermModalHeaderProps<T extends { msrDt: string }> = {
fcltyCd : string;
fcltyName: string;
tabName: string;
searchYear: string;
searchList: T[];
ref: React.RefObject<ReturnType<T>>;
queryClick:(returnType:ReturnType<T>) => void;
}
export type ReturnType<T> = {
startDate: Date;
endDate: Date;
tabName: string;
filteredList: T[];
}
export default function FacilityTermModalHeaderMonth<T extends { msrDt: string }>({
searchList,
fcltyName,
tabName,
searchYear,
queryClick,
ref
}: FacilityTermModalHeaderProps<T & { msrDt: string }>) {
const {Common} = useFoStore((state) => state);
const { selectFlowPanel } = Common;
const [startDate, setStartDate] = useState<Date>(new Date(Number(searchYear), 0, 1));
const [endDate, setEndDate] = useState<Date>(new Date(Number(searchYear), 11, 31));
const startPickerRef = useRef<HTMLDivElement>(null);
const endPickerRef = useRef<HTMLDivElement>(null);
const [flowPanel, setFlowPanel] = useState<string>(selectFlowPanel[0]?.val ?? '00');
const [copySearchList, setCopySearchList] = useState<T[]>(searchList);
/* const isDateInRanges = (date: Date) => {
const standYear = parseInt(searchYear);
const dateRanges = [];
if (tabName.includes("_summer")) {
dateRanges.push({
start: new Date(standYear, 5, 1),
end: new Date(standYear, 7, 31)
});
} else if (tabName.includes("_winter")) {
dateRanges.push({
start: new Date(standYear, 0, 1),
end: new Date(standYear, 1, 28)
});
} else {
dateRanges.push({
start: new Date(standYear, 0, 1),
end: new Date(standYear, 11, 31)
});
}
return dateRanges.some(range => {
const start = new Date(range.start);
const end = new Date(range.end);
return date >= start && date <= end;
});
}; */
useEffect(() => {
let startDate = new Date(Number(searchYear), 0, 1);
let endDate = new Date(Number(searchYear), 11, 31);
if(tabName.includes("_summer")) {
startDate = new Date(Number(searchYear), 5, 1);
endDate = new Date(Number(searchYear), 7, 31);
setStartDate(startDate);
setEndDate(endDate);
} else if (tabName.includes("_winter")) {
startDate = new Date(Number(searchYear), 0, 1);
endDate = new Date(Number(searchYear), 1, 28);
setStartDate(startDate);
setEndDate(endDate);
} else {
startDate = new Date(Number(searchYear), 0, 1);
endDate = new Date(Number(searchYear), 11, 31);
setStartDate(startDate);
setEndDate(endDate);
}
let filteredData: T[] = [];
if(tabName.includes("_rain")) {
filteredData = searchList.filter((item) => {
const itemDate = new Date((item as T & { msrDt: string }).msrDt);
const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate());
const startDateOnly = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());
const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
const rainEffectDay = (item as T & { rainEffectDay: number }).rainEffectDay;
if(flowPanel === '1') {
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay>0 && infoRnDay < 3;
} else if(flowPanel === '2'){
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 3 && infoRnDay < 10;
} else if(flowPanel === '3'){
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 10 && infoRnDay < 20;
} else if(flowPanel === '4'){
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 20 && infoRnDay < 30;
} else if(flowPanel === '5'){
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 30 && infoRnDay < 40;
} else if(flowPanel === '6'){
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 40 && infoRnDay < 50;
} else if(flowPanel === '7'){
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 50 && infoRnDay < 60;
} else if(flowPanel === '8'){
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 60;
} else {
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && (infoRnDay>0 || rainEffectDay ===1);
}
});
} else if(tabName.includes("_chuncheon")) {
filteredData = searchList.filter((item) => {
const itemDate = new Date((item as T & { msrDt: string }).msrDt);
const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate());
const startDateOnly = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());
const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
const infoRnDay = (item as T & { infoRnDay: number }).infoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay==0;
});
} else {
filteredData = searchList.filter((item) => {
const itemDate = new Date((item as T & { msrDt: string }).msrDt);
const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate());
const startDateOnly = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());
const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
return itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly;
});
}
setCopySearchList(filteredData);
}, [searchList, searchYear, tabName, flowPanel]);
const handleStartDateSelect = (day: Date | undefined) => {
if(day && day > endDate) {
alert('종료일은 시작일 이전일 수 없습니다.');
return;
}
const selectedDate = day ?? startDate;
setStartDate(selectedDate);
const button = startPickerRef.current?.querySelector('button');
if (button) {
setTimeout(() => button.click(), 0);
}
const filteredData = searchList.filter((item) => {
const itemDate = new Date((item as T & { msrDt: string }).msrDt);
const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate());
const startDateOnly = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate());
const endDateOnly = new Date(endDate.getFullYear(), endDate.getMonth(), endDate.getDate());
return itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly;
});
setCopySearchList(filteredData);
};
const handleEndDateSelect = (day: Date | undefined) => {
if(day && day < startDate) {
alert('종료일은 시작일 이후일 수 없습니다.');
return;
}
const selectedDate = day ?? endDate;
setEndDate(selectedDate);
const button = endPickerRef.current?.querySelector('button');
if (button) {
setTimeout(() => button.click(), 0);
}
const filteredData = searchList.filter((item) => {
const itemDate = new Date((item as T & { msrDt: string }).msrDt);
const itemDateOnly = new Date(itemDate.getFullYear(), itemDate.getMonth(), itemDate.getDate());
const startDateOnly = new Date(startDate.getFullYear(), startDate.getMonth(), startDate.getDate());
const endDateOnly = new Date(selectedDate.getFullYear(), selectedDate.getMonth(), selectedDate.getDate());
return itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly;
});
setCopySearchList(filteredData);
};
const fncSetFilteredList = useCallback(() => {
queryClick({
startDate: startDate,
endDate: endDate,
tabName: tabName,
filteredList: copySearchList
});
}, [queryClick, startDate, endDate, tabName, copySearchList]);
useEffect(() => {
fncSetFilteredList();
}, [fncSetFilteredList]);
useEffect(() => {
if(ref && ref.current) {
const customRef = ref.current;
customRef.startDate = startDate;
customRef.endDate = endDate;
customRef.tabName = tabName;
customRef.filteredList = copySearchList;
}
}, [startDate, endDate, tabName, copySearchList, ref]);
return (
<ListDiv className="!h-[100px]">
<div className="card !m-[1.2rem] max-h-[40rem] overflow-auto !p-0r">
<Table>
<thead>
<TableRow>
<TableHead className="w-[120px]">{'시설명'}</TableHead>
{tabName === "tab_term_rain" ? <TableCell className="w-[300px]">{fcltyName}</TableCell> :
<TableCell className="w-[380px]">{fcltyName}</TableCell>}
{tabName === "tab_term_rain" && (
<>
<TableHead className="w-[120px]">{'강수구분'}</TableHead>
<TableCell className="w-[250px]">
<div className="justify-center">
<SelectBox
selectArray={selectFlowPanel}
value={flowPanel}
onSelectValue={(val) => setFlowPanel(val.toString())}
className="mx-3 w-[150px]"
label=""
selectClass="w-full p-1 border rounded bg-white"
/>
</div>
</TableCell>
</>
)}
<TableHead className="w-[120px]">{'년/월'}</TableHead>
<TableCell className="">
<div className="flex">
<div ref={startPickerRef} className="flex justify-center">
<DatePicker
mode="single"
selected={startDate}
onSelect={(date) => handleStartDateSelect(date as Date)}
className="w-full p-1 border rounded bg-white"
defaultMonth={startDate}
/>
</div>
<h3 className="justify-center px-5">~</h3>
<div ref={endPickerRef} className="flex justify-center">
<DatePicker
mode="single"
selected={endDate}
onSelect={(date) => handleEndDateSelect(date as Date)}
className="w-full p-1 border rounded bg-white"
defaultMonth={endDate}
/>
</div>
</div>
</TableCell>
</TableRow>
</thead>
</Table>
</div>
</ListDiv>
);
}
@@ -0,0 +1,65 @@
import { TableHead, TableRow } from "@common/components/ui";
import type { TableUpperProps } from "../../store/dashboard";
const HeadMakeColSpan = ({ upHeadList, layoutNum }: { upHeadList: TableUpperProps[], layoutNum: string }) => {
return (
layoutNum === 'A' ? (
<>
<TableRow className="bg-gray-50">{upHeadList.map((head: TableUpperProps) =>
(head.id === 'trtFrfNm' ? <TableHead className="w-1/12" rowSpan={2} key={head.id}>{head.title}</TableHead> :
head.id === 'laeNm' ? <TableHead className="w-1/12" rowSpan={2} key={head.id}>{head.title}</TableHead> :
head.id == 'fctCpc' || head.id == 'planInputWaterQlty' || head.id == 'designInputWaterQlty' ? <TableHead rowSpan={2} className="w-40" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> :
head.id === 'trstOprEtpNm' ? <TableHead className="w-1/15" rowSpan={2} key={head.id}>{head.title}</TableHead> :
head.upName === 'upChangeRe' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={2}>{'행정구역'}</TableHead> :
!head.upName ? <TableHead rowSpan={2} className="w-1/20 bg-gray-20" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> : null))}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => {
if (head.upName === 'upChangeRe') {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-40 bg-gray-50">{head.title}</TableHead>;
}
})}
</TableRow>
</>
) : layoutNum === 'R' ? (
<>
<TableRow className="bg-gray-50">{upHeadList.map((head: TableUpperProps) =>
(head.id === 'trtFrfNm' ? <TableHead className="w-1/12" rowSpan={2} key={head.id}>{head.title}</TableHead> :
head.id === 'laeNm' ? <TableHead className="w-1/12" rowSpan={2} key={head.id}>{head.title}</TableHead> :
head.id == 'fctCpc' || head.id == 'planInputWaterQlty' || head.id == 'designInputWaterQlty' ? <TableHead rowSpan={2} className="w-40" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> :
head.id === 'trstOprEtpNm' ? <TableHead className="w-1/15" rowSpan={2} key={head.id}>{head.title}</TableHead> :
head.upName === 'upChangeRe' && head.upSequnce === 1 ? <TableHead className="w-75 bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={3}>{'유역'}</TableHead> :
!head.upName ? <TableHead rowSpan={2} className="w-1/20 bg-gray-20" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> : null))}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => {
if (head.upName === 'upChangeRe') {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-40 bg-gray-50">{head.title}</TableHead>;
}
})}
</TableRow>
</>
) : (
<>
<TableRow className="bg-gray-50">{upHeadList.map((head: TableUpperProps) =>
(head.id === 'trtFrfNm' ? <TableHead className="w-1/12" rowSpan={2} key={head.id}>{head.title}</TableHead> :
head.id === 'laeNm' ? <TableHead className="w-1/12" rowSpan={2} key={head.id}>{head.title}</TableHead> :
head.id == 'fctCpc' || head.id == 'planInputWaterQlty' || head.id == 'designInputWaterQlty' ? <TableHead rowSpan={2} className="w-40" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> :
head.id === 'trstOprEtpNm' ? <TableHead className="w-1/15" rowSpan={2} key={head.id}>{head.title}</TableHead> :
head.upName === 'upChangeRe1' && head.upSequnce === 1 ? <TableHead className="w-75 bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={3}>{'권역'}</TableHead> :
head.upName === 'upChangeRe2' && head.upSequnce === 1 ? <TableHead className="w-75 bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={2}>{'행정구역'}</TableHead> :
!head.upName ? <TableHead rowSpan={2} className="w-1/20 bg-gray-20" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> : null))}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => {
if (head.upName === 'upChangeRe1' || head.upName === 'upChangeRe2') {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-40 bg-gray-50">{head.title}</TableHead>;
}
})}
</TableRow>
</>
)
);
}
export { HeadMakeColSpan };
@@ -0,0 +1,108 @@
import { useFoStore } from '@/app/store';
import type { facilityListProps } from '@/app/store/processFacility';
import { Label } from '@common/components/ui';
import React, { useEffect, useState, useRef, useCallback } from 'react'
type thisProps = {
label: string | undefined;
setValue : (fcltyCd?: string, fcltyNm?: string, fcltyCpc?: number | '', trtFrfNm?: string | '', efflWqltStndrdNm?: string | '') => void;
sidoSigun? : string | undefined;
fcltyName? : string | undefined;
}
export default function SearchInputText({label=undefined, setValue=()=>{}, sidoSigun=undefined, fcltyName=undefined} :thisProps) {
const { ProcessFacilityList, searchProcessFacilitySearching } = useFoStore((state) => state);
const [isOpen, setIsOpen] = useState(false);
const [textValue, setTextValue] = useState(fcltyName || '');
const wrapperRef = useRef<HTMLDivElement>(null);
const processFacilityList = ProcessFacilityList?.processFacilityList || [];
const handleInputChange = useCallback((e: React.ChangeEvent<HTMLInputElement>) => {
const value = e.target.value;
setTextValue(value);
if (value) {
setIsOpen(true);
if(sidoSigun) {
searchProcessFacilitySearching(1, 4999, 'A', sidoSigun, '0', '0', '0', value);
} else {
searchProcessFacilitySearching(1, 4999, '0', '0', '0', '0', '0', value);
}
} else {
setIsOpen(false);
}
}, [searchProcessFacilitySearching, sidoSigun]);
const onInputFocus = useCallback(() => {
setIsOpen(true);
if(sidoSigun) {
searchProcessFacilitySearching(1, 4999, 'A', sidoSigun, '0', '0', '0');
} else {
searchProcessFacilitySearching(1, 4999, '0', '0', '0', '0', '0');
}
}, [searchProcessFacilitySearching, sidoSigun]);
useEffect(() => {
setTextValue(fcltyName || '');
}, [fcltyName]);
const handleSuggestionClick = useCallback((bscFctCd: string, bscFctNm: string, fctCpc: number | '', trtFrfNm: string | '', efflWqltStndrdNm: string | '') => {
console.log('bscFctNm : ', bscFctNm);
console.log('bscFctCd : ', bscFctCd);
console.log('fctCpc : ', fctCpc);
console.log('trtFrfNm : ', trtFrfNm);
console.log('efflWqltStndrdNm : ', efflWqltStndrdNm);
setTextValue(bscFctNm);
setValue(bscFctCd, bscFctNm, fctCpc, trtFrfNm, efflWqltStndrdNm);
setIsOpen(false);
}, [setValue]);
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (wrapperRef.current && !wrapperRef.current.contains(event.target as Node)) {
setIsOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, []);
return (
<div className={'relative'} ref={wrapperRef}>
{label && <Label className="w-20 text-xl text-gray-600 justify-self-center">{label}</Label>}
<input
type="text"
value={textValue}
onChange={handleInputChange}
onFocus={onInputFocus}
className="w-[200px] ml-6 px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
placeholder={`${label}을(를) 입력하세요`}
/>
{isOpen && processFacilityList.length > 0 && (
<div className="absolute z-10 w-[300px] px-2 py-1 !mt-5 ml-20 bg-white border border-gray-300 rounded-md shadow-lg h-90 overflow-auto">
<div className="max-h-72">
{processFacilityList.map((item : facilityListProps, index : number) => (
<div
key={index}
className="w-full text-left pl-1 pr-10 py-2 hover:bg-gray-100 cursor-pointer overflow-auto"
onClick={() => handleSuggestionClick(item.bscFctCd, item.bscFctNm, Number(item.fctCpc), item.trtFrfNm, item.efflWqltStndrdNm)}
role="button"
tabIndex={0}
onKeyDown={(e) => {
if (e.key === 'Enter' || e.key === ' ') {
handleSuggestionClick(item.bscFctCd, item.bscFctNm, Number(item.fctCpc), item.trtFrfNm, item.efflWqltStndrdNm);
}
}}
>
{item.sido}&nbsp;{item.gugun}&nbsp;<span className="float-right">{item.bscFctNm}</span>
</div>
))}
</div>
</div>
)}
</div>
)
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

+7
View File
@@ -0,0 +1,7 @@
@import "../../../packages/common/components/src/base.css";
html,
body {
font-family: "Noto Sans KR", sans-serif;
font-size: 10px;
}
+24
View File
@@ -0,0 +1,24 @@
"use client"
import { BaseLayout } from "@common/components/ui";
import "./globals.css";
import { StoreProvider } from "./store";
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang="en">
<body>
<StoreProvider>
<BaseLayout>
{children}
</BaseLayout>
</StoreProvider>
</body>
</html>
);
}
+44
View File
@@ -0,0 +1,44 @@
"use client"
import Link from "next/link";
import { Button } from "@common/components/ui";
export default function Home() {
return (
<div className="grid grid-cols-12 gap-2">
<div className="col-span-2">
<Link href={"./pages/processFacility"}><Button>처리시설 시설정보</Button></Link>
</div>
<div className="col-span-2">
<Link href={"./pages/flowRate"}><Button>시설별 유량현황</Button></Link>
</div>
<div className="col-span-2">
<Link href={"./pages/dashboard"}><Button>대시보드</Button></Link>
</div>
<div className="col-span-2">
<Link href={"./pages/processArea"}><Button>처리구역정보</Button></Link>
</div>
<div className="col-span-2">
<Link href={"./pages/facilityPrecipitation"}><Button>시설별 강수현황</Button></Link>
</div>
<div className="col-span-2">
<Link href={"./pages/facilityWaterQlty"}><Button>시설별 수질현황</Button></Link>
</div>
<div className="col-span-2">
<Link href={"./pages/planSewage"}><Button>계획하수량 산정지표 현황</Button></Link>
</div>
<div className="col-span-2">
<Link href={"./pages/sewageOccur"}><Button>오수발생량 추정</Button></Link>
</div>
<div className="col-span-2">
<Link href={"./pages/underWaterInput"}><Button>지하수 유입량 추정</Button></Link>
</div>
<div className="col-span-2">
<Link href={"./pages/rdi"}><Button>RDII 조회</Button></Link>
</div>
<div className="col-span-2">
<Link href={"./pages/map"}><Button>지도보기</Button></Link>
</div>
</div>
);
}
@@ -0,0 +1,98 @@
"use client"
import { useFoStore } from '@/app/store';
import type { DrainageEquipmentListProps, TableUpperProps } from '@/app/store/dashboard';
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '@common/components';
import { useEffect, useState } from 'react';
export type DrainageEquipmentProps = {
searchYear?: string;
fcltyList?: { bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string, efflWqltStndrdNm: string }[];
};
export default function DrainageEquipment({searchYear = '', fcltyList = []}: DrainageEquipmentProps) {
const {
Common,
DrainageEquipment,
searchDrainageEquipment,
} = useFoStore((state) => state);
const {
isInit,
} = Common;
const {
upHeadListDrainageEquipment,
drainageEquipmentList,
} = DrainageEquipment;
const [hasSearched, setHasSearched] = useState(false);
// 이전 시설코드 저장
const [prevFacilityCode, setPrevFacilityCode] = useState<string>('');
const tableData = fcltyList;
useEffect(() => {
if (!isInit && tableData[0]?.bscFctCd && (tableData[0].bscFctCd !== prevFacilityCode || !hasSearched)) {
searchDrainageEquipment(searchYear, tableData[0].bscFctCd, false);
setHasSearched(true);
setPrevFacilityCode(tableData[0].bscFctCd);
}
}, [fcltyList, isInit, searchDrainageEquipment, searchYear, tableData, hasSearched, prevFacilityCode]);
// 연도가 바뀌면 hasSearched를 false로 리셋
useEffect(() => {
setHasSearched(false);
}, [searchYear]);
// 시설계획 table header 변경
const HeadMakeColSpan = ({ upHeadList }: { upHeadList: TableUpperProps[] }) => {
return (
<>
<TableRow className="bg-gray-50">{upHeadList.map((head: TableUpperProps) =>
(head.id === 'part1' ? <TableHead rowSpan={3} className='w-60' key={head.id}>구분</TableHead> :
head.id === 'part2' ? <TableHead rowSpan={3} className='w-60' key={head.id}>구분</TableHead> :
head.id == 'nowSum' ? <TableHead rowSpan={2} colSpan={3} key={head.id} style={{ whiteSpace: 'pre-line' }}>{'현재\n(기준년도)'}</TableHead> :
head.id == 'stepOneSum' ? <TableHead rowSpan={1} colSpan={12} key={head.id} style={{ whiteSpace: 'pre-line' }}>{'변경(금회계획)'}</TableHead> :
head.id == 'etc' ? <TableHead rowSpan={3} key={head.id}>비고</TableHead> : null))}
</TableRow>
<TableRow className="bg-gray-50">{upHeadList.map((head: TableUpperProps) =>
(head.id === 'stepOneSum' ? <TableHead rowSpan={1} colSpan={3} key={head.id}>1단계</TableHead> :
head.id === 'stepTwoSum' ? <TableHead rowSpan={1} colSpan={3} key={head.id}>2단계</TableHead> :
head.id === 'stepThreeSum' ? <TableHead rowSpan={1} colSpan={3} key={head.id}>3단계</TableHead> :
head.id === 'stepFourSum' ? <TableHead rowSpan={1} colSpan={3} key={head.id}>4단계</TableHead> : null))}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => {
if (head.upName === 'upChangeRe2' || head.upName === 'upChangeRe3' || head.upName === 'upChangeRe4' || head.upName === 'upChangeRe5' || head.upName === 'upChangeRe6') {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-40 bg-gray-50">{head.title}</TableHead>;
}
})}
</TableRow>
</>
)
}
const upHeadList = HeadMakeColSpan({upHeadList:upHeadListDrainageEquipment});
return (
<div className="overflow-x-auto">
<Table variant="secondary" className='w-[130%]'>
<TableHeader>
{upHeadList}
</TableHeader>
<TableBody>
{drainageEquipmentList.map((el: DrainageEquipmentListProps, index) => (
<TableRow key={index+"row"}>
{upHeadListDrainageEquipment.map((head) => (
(head.id==='nowSum')?
<TableCell className="text-right" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{333}</TableCell>
:<TableCell className="text-center" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id as keyof DrainageEquipmentListProps]}</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
</div>
)
}
@@ -0,0 +1,184 @@
"use client"
import type { OperationalStatusListProps } from "@/app/store/dashboard";
import { CHART_FORM, MainChart, type ChartConfig } from "@common/business_components";
import { Button } from "@common/business_components/ui/table/lib/button";
import html2canvas from "html2canvas";
import { useEffect, useState, useRef } from "react";
type yearProps = {
label: string;
평균 : number | string | '';
최대 : number | string | '';
최소 : number | string | '';
}
type summerProps = {
전기간 : number | string | '';
강우일3mm이상 : number | string | '';
비강우시 : number | string | '';
}
type winterProps = {
전기간 : number | string | '';
청천시최대 : number | string | '';
청천시평균 : number | string | '';
청천시최소 : number | string | '';
}
type dashBoardChartModalProps = {
title : string;
item : string;
period : string;
modalData : OperationalStatusListProps[];
waterQlty : string;
}
export default function DashBoardChartModal({item='', period='', title='',modalData=[], waterQlty=''} : dashBoardChartModalProps) {
const [chartData, setChartData] = useState<yearProps[] | summerProps[] | winterProps[]>([]);
const [chartConfig, setChartConfig] = useState<ChartConfig>({});
const chartRef = useRef<HTMLDivElement>(null);
const chartOption = {
chartForm : CHART_FORM.BAR,
chartConfig : chartConfig,
chartData : chartData,
chartTitle : title,
chartClassName : "h-[calc(100%-15%)] w-[90%] mt-5",
graphSize : 40,
yFieldText : item === 'flowRate' ? '유량(㎥/일)' : item === 'waterQlty' ? '수질(mg/L)' : item === 'chongin' ? '수질(mg/L)' : '',
showLegend : true
}
const handleDownload = () => {
if (!chartRef.current) {
return alert('결과 저장에 실패했습니다.');
}
html2canvas(chartRef.current).then((canvas) => {
const link = document.createElement('a');
document.body.appendChild(link);
link.href = canvas.toDataURL('image/png');
link.download = 'result.png';
link.click();
document.body.removeChild(link);
});
}
/**
* useEffect
*/
useEffect(() => {
if(period === 'year'){
const filteredData: yearProps[] = modalData.map((item) => {
if(waterQlty.trim().includes('T-N') || waterQlty.trim().includes('T-P')){
return {
label: item.waterQlty,
평균: typeof item.yearAvg === 'string' ? Number(item.yearAvg.replace(/,/g, '')).toFixed(3) : (item.yearAvg || 0),
최대: typeof item.yearMax === 'string' ? Number(item.yearMax.replace(/,/g, '')).toFixed(3) : (item.yearMax || 0),
최소: typeof item.yearMin === 'string' ? Number(item.yearMin.replace(/,/g, '')).toFixed(3) : (item.yearMin || 0),
}
}
else{
return {
label: item.waterQlty,
평균: typeof item.yearAvg === 'string' ? Number(item.yearAvg.replace(/,/g, '')).toFixed(1) : (item.yearAvg || 0),
최대: typeof item.yearMax === 'string' ? Number(item.yearMax.replace(/,/g, '')).toFixed(1) : (item.yearMax || 0),
최소: typeof item.yearMin === 'string' ? Number(item.yearMin.replace(/,/g, '')).toFixed(1) : (item.yearMin || 0),
}
}
})
if (item === 'flowRate') {
setChartData(filteredData.slice(0,5));
} else if (item === 'waterQlty') {
setChartData(filteredData.slice(5,11));
} else {
setChartData(filteredData.slice(11,13));
}
}
else if (period === 'summer') {
const filteredData: summerProps[] = modalData.map((item) => {
if(waterQlty.trim().includes('T-N') || waterQlty.trim().includes('T-P')){
return {
label: item.waterQlty,
전기간: typeof item.summerAll === 'string' ? Number(item.summerAll.replace(/,/g, '')).toFixed(3) : (item.summerAll || 0),
강우일3mm이상: typeof item.summerThree === 'string' ? Number(item.summerThree.replace(/,/g, '')).toFixed(3) : (item.summerThree || 0),
비강우시: typeof item.summerRain === 'string' ? Number(item.summerRain.replace(/,/g, '')).toFixed(3) : (item.summerRain || 0),
}
}else{
return {
label: item.waterQlty,
전기간: typeof item.summerAll === 'string' ? Number(item.summerAll.replace(/,/g, '')).toFixed(1) : (item.summerAll || 0),
강우일3mm이상: typeof item.summerThree === 'string' ? Number(item.summerThree.replace(/,/g, '')).toFixed(1) : (item.summerThree || 0),
비강우시: typeof item.summerRain === 'string' ? Number(item.summerRain.replace(/,/g, '')).toFixed(1) : (item.summerRain || 0),
}
}
})
if (item === 'flowRate') {
setChartData(filteredData.slice(0,5));
} else if (item === 'waterQlty') {
setChartData(filteredData.slice(5,11));
} else {
setChartData(filteredData.slice(11,13));
}
}
else if (period === 'winter') {
const filteredData: winterProps[] = modalData.map((item) => {
return {
label: item.waterQlty,
전기간: typeof item.winterAll === 'string' ? Number(item.winterAll.replace(/,/g, '')).toFixed(3) : (item.winterAll || 0),
청천시최대: typeof item.winterSunnyMax === 'string' ? Number(item.winterSunnyMax.replace(/,/g, '')).toFixed(3) : (item.winterSunnyMax || 0),
청천시평균: typeof item.winterSunnyAvg === 'string' ? Number(item.winterSunnyAvg.replace(/,/g, '')).toFixed(3) : (item.winterSunnyAvg || 0),
청천시최소: typeof item.winterSunnyMin === 'string' ? Number(item.winterSunnyMin.replace(/,/g, '')).toFixed(3) : (item.winterSunnyMin || 0),
}
})
setChartData(filteredData);
if (item === 'flowRate') {
setChartData(filteredData.slice(0,5));
} else if (item === 'waterQlty') {
setChartData(filteredData.slice(5,11));
} else {
setChartData(filteredData.slice(11,13));
}
} else {
setChartData([]);
}
}, [item, modalData, period, waterQlty]);
useEffect(() => {
const newChartConfig = chartData.reduce((config, element) => {
const label = 'label' in element ? element.label : '';
if (label) {
config[label] = {
label: label,
color : "hsl(var(--chart-1))"
};
}
return config;
}, {} as Record<string, { label: string; color: string; }>) satisfies ChartConfig;
setChartConfig(newChartConfig);
}, [chartData]);
return (
<>
<div className="flex justify-end">
<span className="text-[13px] font-bold mt-2 w-[260px] h-[30px] -mb-9">
(하절기 : 6/1~8/31 동절기 : 1/1~2/28)
</span>
<Button className="text-[13px] w-[80px] h-[30px] -mb-9 mr-20" onClick={handleDownload}>캡처</Button>
</div>
<div ref={chartRef}>
<MainChart
chartForm={chartOption.chartForm}
chartConfig={chartOption.chartConfig}
chartData={chartOption.chartData}
chartTitle={chartOption.chartTitle}
chartClassName={chartOption.chartClassName}
graphSize={chartOption.graphSize}
yFieldText={chartOption.yFieldText}
showLegend={chartOption.showLegend}
/>
</div>
</>
);
}
@@ -0,0 +1,277 @@
"use client"
import { useFoStore } from "@/app/store";
import type { TableUpperProps } from "@/app/store/processFacility";
import { ContentsDiv, UiModal, UiTable } from "@common/business_components";
import { cn, Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@common/components";
import { useEffect, useState } from "react";
import DashBoardChartModal from "./modal/page";
export type facilityInfoProps = {
searchYear?: string;
fcltyList?: { bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string, efflWqltStndrdNm: string }[];
};
// 시설계획 table header 변경
const headMakeColSpanPlanFclty = (upHeadList: TableUpperProps[], planYearList: string[]) => {
return ( <>
<TableRow>{
upHeadList.map((head: TableUpperProps) =>
(!head.upSequnce?<TableHead rowSpan={2}key={head.id}>{head.title}</TableHead>:
head.upName === 'upChangeRe'&& head.upSequnce=== 1?<TableHead key={head.id} rowSpan={1} colSpan={7} style={{ whiteSpace: 'pre-line' }}>{'시설계획\n(금회)'}</TableHead>:null))}
</TableRow>
<TableRow>
{upHeadList.map((head: TableUpperProps, index: number) =>
(head.upName === 'upChangeRe'?<TableHead key={head.id} style={{ whiteSpace: 'pre-line' }}>{index===0?head.title:head.title+'\n('+planYearList[index-1]+')'}</TableHead>:null))}
</TableRow>
</> )
}
// 시설계획 table header 변경
const headMakeColSpanOperationStatus = (upHeadList: TableUpperProps[], searchYear: string) => {
return ( <>
<TableRow>
<TableHead rowSpan={1} colSpan={16} style={{ whiteSpace: 'pre-line' }}>{searchYear+'년도 운영실태'}</TableHead>
</TableRow>
<TableRow>{
upHeadList.map((head: TableUpperProps) =>
(head.upName === 'upChangeRe1'&&head.upSequnce===1?<TableHead key={head.id} rowSpan={2}colSpan={2} style={{ whiteSpace: 'pre-line' }}>{head.title}</TableHead>:
!head.upSequnce?<TableHead rowSpan={2}key={head.id}>{head.title}</TableHead>:
head.upName === 'upChangeRe2'&&head.upSequnce===1?<TableHead key={head.id} colSpan={3} style={{ whiteSpace: 'pre-line' }}>{'연간 운영실태'}</TableHead>:
head.upName === 'upChangeRe3'&&head.upSequnce===1?<TableHead key={head.id} colSpan={3} style={{ whiteSpace: 'pre-line' }}>{'하절기 운영실태\n(6~8월)'}</TableHead>:
head.upName === 'upChangeRe4'&&head.upSequnce===1?<TableHead key={head.id} colSpan={4} style={{ whiteSpace: 'pre-line' }}>{'동절기 운영실태\n(1~2월)'}</TableHead>:
head.upName === 'upChangeRe5'&&head.upSequnce===1?<TableHead key={head.id} colSpan={3} style={{ whiteSpace: 'pre-line' }}>{'지하수유입량'}</TableHead>:null))}
</TableRow>
<TableRow>
{upHeadList.map((head: TableUpperProps) =>
(head.upName === 'upChangeRe2'||head.upName === 'upChangeRe3'||head.upName === 'upChangeRe4'||head.upName === 'upChangeRe5' ?<TableHead key={head.id}>{head.title}</TableHead>:null))}
</TableRow>
</> )
}
export default function ProcessFacilitySearch({searchYear = '', fcltyList = []}: facilityInfoProps & { onFacilityChange: (facility: { bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string, efflWqltStndrdNm: string }) => void }) {
// 입력값 상태 관리
const {
Common,
FcltyMain,
PlanFclty,
OperationStatus,
searchOperationStatus,
searchPlanInfo
} = useFoStore((state) => state);
const [modalItem, setModalItem] = useState<{item: string, period: string, title: string, waterQlty: string} | null>(null);
const [hasSearched, setHasSearched] = useState(false);
const [prevFacilityCode, setPrevFacilityCode] = useState<string>('');
const {
isInit,
} = Common;
// Tab1
// fclty 관련
const {
upHeadListFclty,
} = FcltyMain;
// 시설계획 관련
const {
upHeadListPlanFclty,
planFcltyList,
planYearList
} = PlanFclty;
// 그리드 관련
const {
upHeadListOperationStatus,
operationalStatusList
} = OperationStatus;
const closeModal = () => {
setModalItem(null);
};
// const fcltyListArr = useMemo(() => [fcltyList], [fcltyList]);
const tableData = fcltyList;
// key 변환
const mappedTableData = tableData.map(item => ({
fcltyNm: item.bscFctNm,
facilityCapacity: item.fcltyCpc,
publicMethod: item.trtFrfNm,
location: item.efflWqltStndrdNm,
}));
const fncLoadModal = (item: string, period: string, waterQlty: string) => {
// 유량
if(item === 'flowRate'){
if(period === 'year'){
setModalItem({item: 'flowRate', period: 'year', title: '연간 유량', waterQlty: waterQlty});
}
else if(period === 'summer'){
setModalItem({item: 'flowRate', period: 'summer', title: '하절기 유량', waterQlty: waterQlty});
}
else if(period === 'winter'){
setModalItem({item: 'flowRate', period: 'winter', title: '동절기 유량', waterQlty: waterQlty});
}
}
// 수질
else if (item == 'waterQlty'){
if(period === 'year'){
setModalItem({item: 'waterQlty', period: 'year', title: '연간 수질', waterQlty: waterQlty});
}
else if(period === 'summer'){
setModalItem({item: 'waterQlty', period: 'summer', title: '하절기 수질', waterQlty: waterQlty});
}
else if(period === 'winter'){
setModalItem({item: 'waterQlty', period: 'winter', title: '동절기 수질', waterQlty: waterQlty});
}
}
// 총인
else {
if(period === 'year'){
setModalItem({item: 'chongin', period: 'year', title: '총인설비 연간 수질', waterQlty: waterQlty});
}
else if(period === 'summer'){
setModalItem({item: 'chongin', period: 'summer', title: '총인설비 하절기 수질', waterQlty: waterQlty});
}
else if(period === 'winter'){
setModalItem({item: 'chongin', period: 'winter', title: '총인설비 동절기 수질', waterQlty: waterQlty});
}
}
}
useEffect(() => {
if (!isInit && fcltyList[0]?.bscFctCd && (fcltyList[0].bscFctCd !== prevFacilityCode || !hasSearched)) {
searchOperationStatus(false, searchYear, fcltyList[0].bscFctCd);
searchPlanInfo(searchYear, fcltyList[0].bscFctCd);
setHasSearched(true);
setPrevFacilityCode(fcltyList[0].bscFctCd);
planFcltyList.forEach((el: { [key: string]: string | number | boolean }, index) => {
if (index === 6) {
el['item'] = (Number(searchYear)-2).toString();
el['provisionalApproval'] = Number(fcltyList[0]?.fcltyCpc)* 0.6;
} else if (index === 7) {
el['item'] = (Number(searchYear)-1).toString();
el['provisionalApproval'] = Number(fcltyList[0]?.fcltyCpc)* 0.6;
} else if (index === 8) {
el['item'] = (Number(searchYear)-0).toString();
el['provisionalApproval'] = Number(fcltyList[0]?.fcltyCpc)* 0.6;
}
});
}
}, [isInit, searchOperationStatus, searchYear, fcltyList, hasSearched, prevFacilityCode, searchPlanInfo, planFcltyList]);
// 연도가 바뀌면 hasSearched를 false로 리셋
useEffect(() => {
setHasSearched(false);
}, [searchYear]);
useEffect(() => {
operationalStatusList.map((item: { [key: string]: string | number | boolean },index) => {
if(index===8 || index===9 || index===10 || index===11 || index===12 || index===13 || index===14 || index===15){
item['yearAvg'] = Number(item['yearAvg']).toFixed(3);
item['yearMax'] = item['yearMax']+'%';
item['yearMin'] = item['yearMin']+'%';
item['summerAll'] = item['summerAll']+'%';
item['summerThree'] = item['summerThree']+'%';
item['summerRain'] = item['summerRain']+'%';
item['winterAll'] = item['winterAll']+'%';
item['winterSunnyAvg'] = item['winterSunnyAvg']+'%';
item['winterSunnyMax'] = item['winterSunnyMax']+'%';
item['winterSunnyMin'] = item['winterSunnyMin']+'%';
}
});
}, [operationalStatusList]);
const upChangeRePlanFclty = headMakeColSpanPlanFclty(upHeadListPlanFclty, planYearList);
const upChangeHeadListOperationStatus = headMakeColSpanOperationStatus(upHeadListOperationStatus, searchYear);
return (
<>
<UiTable headName={""} tableData={mappedTableData} headlist={upHeadListFclty} pageSize={0} total={0} cellClick={ undefined } onPageChange={() => { } } />
<ContentsDiv>
<div className="grid grid-cols-12 gap-1">
<div className="col-span-5">
<Table variant="secondary">
<TableHeader>
{upChangeRePlanFclty}
</TableHeader>
<TableBody>
{planFcltyList.map((el: { [key: string]: string | number | boolean },index) => (
<TableRow key={index+"row"} >
{upHeadListPlanFclty.map((head) => (
((index === 6 || index === 7 || index === 8) && (head.id === 'provisionalApproval' || head.id === 'stepTwo'))?
<TableCell className="text-right" colSpan={3} style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>
:((index === 6 || index === 7 || index === 8) && (head.id === 'now' || head.id === 'stepOne' || head.id === 'stepThree' || head.id === 'stepFour'))?
null
:((index === 5) && (head.id === 'provisionalApproval' || head.id === 'stepTwo'))?
<TableCell colSpan={3} className="text-center border border-[var(--border)] bg-[rgb(234,243,251)] text-[rgb(100,119,132)]" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>
:((index === 5) && (head.id === 'now' || head.id === 'stepOne' || head.id === 'stepThree'|| head.id === 'stepFour'))?
null
:(index < 5 || index > 5)&& (index!= 4) && (head.id ==='provisionalApproval'||head.id ==='now'||head.id ==='stepOne'||head.id ==='stepTwo'||head.id ==='stepThree'||head.id ==='stepFour')
?<TableCell className="text-right" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>
:(head.id === "provisionalApproval" && index === 4)
?<TableCell colSpan={6} className="text-right border border-[var(--border)] text-[rgb(100,119,132)]" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{(Number(fcltyList[0]?.fcltyCpc)* 0.6).toLocaleString('en-US', { minimumFractionDigits: 1, maximumFractionDigits: 1 })} </TableCell>
:((head.id ==='now'||head.id ==='stepOne'||head.id ==='stepTwo'||head.id ==='stepThree'||head.id ==='stepFour') && index === 4)
?null
:(head.id === 'item' && index != 5)?
<TableCell className="text-center text-[rgb(100,119,132)]" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>
:<TableCell className="text-center border border-[var(--border)] bg-[rgb(234,243,251)] text-[rgb(100,119,132)]" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>
))}
</TableRow>
))}
</TableBody>
</Table>
</div>
<div className="col-span-7 h-[600px] overflow-x-auto">
<div className="w-[200%]">
<Table variant="secondary">
<TableHeader>
{upChangeHeadListOperationStatus}
</TableHeader>
<TableBody>
{operationalStatusList.map((el: { [key: string]: string | number | boolean },index) => (
<TableRow key={index+"row"} >
{upHeadListOperationStatus.map((head) => (
(head.id === 'flowRate' && index === 0)?<TableCell rowSpan={5} className="text-center" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>:
(head.id === 'flowRate' && index === 5)?<TableCell rowSpan={6} className="text-center" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>:
(head.id === 'flowRate' && index === 11)?<TableCell rowSpan={2} className="text-center" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>:
(head.id === 'groundWaterAvg'||head.id === 'groundWaterSummer'||head.id === 'groundWaterWinter')&&(index === 0)?<TableCell rowSpan={7} className="text-right" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>:
(head.id === 'groundWaterAvg'||head.id === 'groundWaterSummer'||head.id === 'groundWaterWinter')&&(index === 7)?<TableCell rowSpan={6} className="text-right" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>:
(head.id === 'rdi'&&index === 0)?<TableCell rowSpan={2} className="text-right" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>:
(head.id === 'rdi'&&index === 2)?<TableCell rowSpan={3} className={cn("sticky top-0 bg-secondary [&_tr]:border-b", "text-center" )} style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{'RDII추정시\n일강수량(mm)'}</TableCell>:
(head.id === 'rdi'&&index === 5)?<TableCell rowSpan={2} className="text-right" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el['rdiRain']}</TableCell>:
(head.id === 'rdi'&&index === 7)?<TableCell rowSpan={6} className="text-right" style={{ whiteSpace: 'pre-line' }} key={head.id+index}></TableCell>:
(head.id === 'waterQlty')?<TableCell className="text-center" style={{ whiteSpace: 'pre-line' }} key={head.id+index}>{el[head.id]}</TableCell>:
((head.id === 'yearAvg' || head.id === 'yearMax' || head.id === 'yearMin') && (index>=0 && index<=4))?<TableCell className="bg-yellow-50 text-right cursor-pointer" style={{ whiteSpace: 'pre-line' }} key={head.id+index} onClick={()=>{fncLoadModal('flowRate', 'year', el['waterQlty'] as string);}}>{el[head.id]}</TableCell>:
((head.id === 'yearAvg' || head.id === 'yearMax' || head.id === 'yearMin') && (index>=5 && index<=10))?<TableCell className="bg-green-50 text-right cursor-pointer" style={{ whiteSpace: 'pre-line' }} key={head.id+index} onClick={()=>{fncLoadModal('waterQlty', 'year', el['waterQlty'] as string);}}>{el[head.id]}</TableCell>:
((head.id === 'yearAvg' || head.id === 'yearMax' || head.id === 'yearMin') && (index>=11 && index<=12))?<TableCell className="bg-blue-50 text-right cursor-pointer" style={{ whiteSpace: 'pre-line' }} key={head.id+index} onClick={()=>{fncLoadModal('chongin', 'year', el['waterQlty'] as string);}}>{el[head.id]}</TableCell>:
((head.id === 'summerAll' || head.id === 'summerThree' || head.id === 'summerRain') && (index>=0 && index<=4))?<TableCell className="bg-yellow-100 text-right cursor-pointer" style={{ whiteSpace: 'pre-line' }} key={head.id+index} onClick={()=>{fncLoadModal('flowRate', 'summer', el['waterQlty'] as string);}}>{el[head.id]}</TableCell>:
((head.id === 'summerAll' || head.id === 'summerThree' || head.id === 'summerRain') && (index>=5 && index<=10))?<TableCell className="bg-green-100 text-right cursor-pointer" style={{ whiteSpace: 'pre-line' }} key={head.id+index} onClick={()=>{fncLoadModal('waterQlty', 'summer', el['waterQlty'] as string);}}>{el[head.id]}</TableCell>:
((head.id === 'summerAll' || head.id === 'summerThree' || head.id === 'summerRain') && (index>=11 && index<=12))?<TableCell className="bg-blue-100 text-right cursor-pointer" style={{ whiteSpace: 'pre-line' }} key={head.id+index} onClick={()=>{fncLoadModal('chongin', 'summer', el['waterQlty'] as string);}}>{el[head.id]}</TableCell>:
((head.id === 'winterAll' || head.id === 'winterSunnyAvg' || head.id === 'winterSunnyMax' || head.id === 'winterSunnyMin') && (index>=0 && index<=4))?<TableCell className="bg-yellow-200 text-right cursor-pointer" style={{ whiteSpace: 'pre-line' }} key={head.id+index} onClick={()=>{fncLoadModal('flowRate', 'winter', el['waterQlty'] as string);}}>{el[head.id]}</TableCell>:
((head.id === 'winterAll' || head.id === 'winterSunnyAvg' || head.id === 'winterSunnyMax' || head.id === 'winterSunnyMin') && (index>=5 && index<=10))?<TableCell className="bg-green-200 text-right cursor-pointer" style={{ whiteSpace: 'pre-line' }} key={head.id+index} onClick={()=>{fncLoadModal('waterQlty', 'winter', el['waterQlty'] as string);}}>{el[head.id]}</TableCell>:
((head.id === 'winterAll' || head.id === 'winterSunnyAvg' || head.id === 'winterSunnyMax' || head.id === 'winterSunnyMin') && (index>=11 && index<=12))?<TableCell className="bg-blue-200 text-right cursor-pointer" style={{ whiteSpace: 'pre-line' }} key={head.id+index} onClick={()=>{fncLoadModal('chongin', 'winter', el['waterQlty'] as string);}}>{el[head.id]}</TableCell>:
null
))}
</TableRow>
))}
</TableBody>
</Table>
</div>
</div>
</div>
</ContentsDiv>
{modalItem && (modalItem.item === 'flowRate' || modalItem.item === 'waterQlty' || modalItem.item === 'chongin') && (
<UiModal
body={<DashBoardChartModal item={modalItem.item} period={modalItem.period} title={modalItem.title} modalData={operationalStatusList} waterQlty={modalItem.waterQlty}></DashBoardChartModal>}
bodyClass="sm:max-w-[80%] h-[88%]"
open={true}
onOpenChange={(open) => !open && closeModal()}
noHeaderYn="Y"
noFooterYn="Y"
/>
)}
</>
);
}
@@ -0,0 +1,243 @@
"use client"
import { useFoStore } from "@/app/store";
import type { lineChartDataProps } from "@/app/store/dashboard";
import { CHART_FORM, type ChartConfig, ContentsDiv, MainChart } from "@common/business_components";
import { Card, CardContent } from "@common/business_components/ui/chart/lib/card";
import ListDiv from "@common/business_components/ui/list_div";
import { useEffect, useState } from "react";
type facilityInfoProps = {
searchYear?: string;
fcltyList?: { bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string, efflWqltStndrdNm: string }[];
};
export default function DashBoardChart({searchYear = '', fcltyList = []}: facilityInfoProps) {
const {MainGraph
,searchMainGraph
} = useFoStore((state) => state);
const [hasSearched, setHasSearched] = useState(false);
const [prevFacilityCode, setPrevFacilityCode] = useState<string>('');
const [chart_tpTp, setChart_tpTp] = useState<{
chartData1: lineChartDataProps[];
chartData2: lineChartDataProps[];
}>();
const [chart_tntn, setChart_tntn] = useState<{
chartData1: lineChartDataProps[];
chartData2: lineChartDataProps[];
}>();
const [chart_inflBodInflTn, setChart_inflBodInflTn] = useState<{
chartData1: lineChartDataProps[];
chartData2: lineChartDataProps[];
}>();
const [chart_inflBodInflTp, setChart_inflBodInflTp] = useState<{
chartData1: lineChartDataProps[];
chartData2: lineChartDataProps[];
}>();
const {
chart_inflowRain,
chart_bodRain,
chart_inflTp,
chart_chongInTp,
chart_efflTp,
chart_inflTn,
chart_efflTn,
chart_inflBod,
} = MainGraph;
/**
* 차트 Config
*/
const colors = ["#FF6B6B", "#4ECDC4", "#45B7D1", "#96CEB4", "#00b50c", "#D4A5A5"];
//유입하수량-강수량
const chartConfig_inflowRain = {
barVal0: {
label: '강수량',
color: colors[2],
},
lineVal0: {
label: '시설용량',
color: colors[1],
},
lineVal1: {
label: '유입량',
color: colors[4],
}
} satisfies ChartConfig;
//강우량-유입BOD
const chartConfig_rainBod = {
barVal0: {
label: '강수량',
color: colors[2],
},
lineVal0: {
label: '유입BOD',
color: colors[0],
},
} satisfies ChartConfig;
//총인유입T-P-방류T-P
const chartConfig_tptp = {
chartData1: {
label: '총인유입T-P',
color: colors[0],
},
chartData2: {
label: '방류T-P',
color: colors[1],
},
} satisfies ChartConfig;
// 유입T-N-방류T-N
const chartConfig_tntn = {
chartData1: {
label: '유입T-N',
color: colors[0],
},
chartData2: {
label: '방류T-N',
color: colors[1],
},
} satisfies ChartConfig;
// 유입BOD-유입T-N
const chartConfig_inflBodInflTn = {
chartData1: {
label: '유입BOD',
color: colors[0],
},
chartData2: {
label: '유입T-N',
color: colors[1],
},
} satisfies ChartConfig;
// 유입BOD-유입T-P
const chartConfig_inflBodInflTp = {
chartData1: {
label: '유입BOD',
color: colors[0],
},
chartData2: {
label: '유입T-P',
color: colors[1],
},
} satisfies ChartConfig;
useEffect(() => {
if (fcltyList.length > 0 && fcltyList[0]?.bscFctCd && (fcltyList[0].bscFctCd !== prevFacilityCode || !hasSearched)) {
searchMainGraph(searchYear, fcltyList[0].bscFctCd);
setHasSearched(true);
setPrevFacilityCode(fcltyList[0].bscFctCd);
}
}, [searchYear, fcltyList, searchMainGraph, hasSearched, chart_inflTp, chart_efflTp, prevFacilityCode]);
useEffect(() => {
if (!chart_chongInTp || !chart_efflTp) return;
const chartTpTpData = {
chartData1: chart_chongInTp,
chartData2: chart_efflTp
};
setChart_tpTp(chartTpTpData);
}, [chart_chongInTp, chart_efflTp]);
useEffect(() => {
if (!chart_inflTn || !chart_efflTn) return;
const chartTntnData = {
chartData1: chart_inflTn,
chartData2: chart_efflTn
};
setChart_tntn(chartTntnData);
}, [chart_inflTn, chart_efflTn]);
useEffect(() => {
if (!chart_inflBod || !chart_inflTn) return;
const chartInflBodInflTnData = {
chartData1: chart_inflBod,
chartData2: chart_inflTn
};
setChart_inflBodInflTn(chartInflBodInflTnData);
}, [chart_inflBod, chart_inflTn]);
useEffect(() => {
if (!chart_inflBod || !chart_inflTp) return;
const chartInflBodInflTpData = {
chartData1: chart_inflBod,
chartData2: chart_inflTp
};
setChart_inflBodInflTp(chartInflBodInflTpData);
}, [chart_inflBod, chart_inflTp]);
// 연도가 바뀌면 hasSearched를 false로 리셋
useEffect(() => {
setHasSearched(false);
}, [searchYear]);
return (
<>
<ContentsDiv>
<ListDiv className="h-[300px]">
<Card className="h-full">
<CardContent className="h-full">
<MainChart chartForm={CHART_FORM.LINEBAR} chartConfig={chartConfig_inflowRain} chartData={chart_inflowRain} chartTitle="유입하수량-강수량" yFieldText="유입하수량(m³/일)" yFieldText2="강수량(mm)"></MainChart>
</CardContent>
</Card>
</ListDiv>
<ListDiv className="h-[300px]">
<Card className="h-full">
<CardContent className="h-full">
<MainChart chartForm={CHART_FORM.LINEBAR} chartConfig={chartConfig_rainBod} chartData={chart_bodRain} chartTitle="유입BOD-강수량" yFieldText="BOD(mg/L)" yFieldText2="강수량(mm)"></MainChart>
</CardContent>
</Card>
</ListDiv>
<ListDiv className="h-[300px]">
<Card className="h-full">
<CardContent className="h-full">
<MainChart chartForm={CHART_FORM.LINE} chartConfig={chartConfig_tptp} chartData={chart_tpTp} chartTitle="총인유입T-P-방류T-P" yFieldText="T-P(mg/L)"></MainChart>
</CardContent>
</Card>
</ListDiv>
{/* 유입T-N-방류T-N */}
<ListDiv className="h-[300px]">
<Card className="h-full">
<CardContent className="h-full">
<MainChart chartForm={CHART_FORM.LINE} chartConfig={chartConfig_tntn} chartData={chart_tntn} chartTitle="유입T-N-방류T-N" yFieldText="T-N(mg/L)"></MainChart>
</CardContent>
</Card>
</ListDiv>
{/* 유입BOD-유입T-N */}
<ListDiv className="h-[300px]">
<Card className="h-full">
<CardContent className="h-full">
<MainChart chartForm={CHART_FORM.LINE} chartConfig={chartConfig_inflBodInflTn} chartData={chart_inflBodInflTn} chartTitle="유입BOD-유입T-N" yFieldText="농도(mg/L)"></MainChart>
</CardContent>
</Card>
</ListDiv>
{/* 유입BOD-유입T-P */}
<ListDiv className="h-[300px]">
<Card className="h-full">
<CardContent className="h-full">
<MainChart chartForm={CHART_FORM.LINE} chartConfig={chartConfig_inflBodInflTp} chartData={chart_inflBodInflTp} chartTitle="유입BOD-유입T-P" yFieldText="농도(mg/L)"></MainChart>
</CardContent>
</Card>
</ListDiv>
</ContentsDiv>
</>
);
{/* <div>>
<MainChart chartForm={CHART_FORM.DN} chartConfig={chartConfig} chartData={chartData} chartTitle="유입하수량-강수량"></MainChart>
</div>
<div className="col-span-6">
<MainChart chartForm={CHART_FORM.LINEBAR} chartConfig={chartConfig} chartData={chart_inflowRain} chartTitle="유입BOD-강수량"></MainChart>
</div>
<div className="col-span-6">
<MainChart chartForm={CHART_FORM.LINE} chartConfig={chartConfig} chartData={chartData} chartTitle="총인유입T-P-방류T-P"></MainChart>
</div>
<div className="col-span-6">
<MainChart chartForm={CHART_FORM.LINE} chartConfig={chartConfig} chartData={chartData} chartTitle="유입T-N-방류T-N"></MainChart>
</div>
</div> */}
}
+306
View File
@@ -0,0 +1,306 @@
"use client"
import { useFoStore } from "@/app/store";
import { ContentsDiv, downloadExcel, downloadMultipleExcel, filePickedBlobArray, MainDiv, SelectBox, UITab, type SelectDataType } from "@common/business_components";
import { Button, TabsContent } from "@common/components/ui";
import { useEffect, useState } from "react";
import DashBoardFclty from "../dashboardMain/page";
/* import DrainageEquipment from "./drainageEquipment/page"; */
import ProcessFacilitySearch from "./facilityInfo/page";
import DashBoardChart from "./maingraph/page";
import RainManageTab from "./rainManage/page";
import DrainageEquipment from "./drainageEquipment/page";
const tabList = [
{ value: 'facilityInfo', label: '시설정보 및 운영현황' },
{ value: 'mainGraph', label: '주요그래프' },
{ value: 'rainManage', label: '강우일 운영현황' },
{ value: 'drainageEquipment', label: '배수설비 정비현황' }
];
export default function DashBoard() {
// 입력값 상태 관리
const { Common, initializeCommonData,searchMainGraph, searchRainManage, searchOperationStatus, OperationStatus, RainManage, MainGraph, PlanFclty } = useFoStore((state) => state);
const [activeTab, setActiveTab] = useState<string>('facilityInfo');
const [filteredSigunData, setFilteredSigunData] = useState<SelectDataType[]>([{ text: "전체", val: '0' }]);
const [sidoValue, setSidoValue] = useState<string>('0');
const [sigunValue, setSigunValue] = useState<string>('0');
const {
isInit,
selectSearchYear,
btnClass,
selectSidoData,
selectSigunData,
} = Common;
const {
planFcltyList
} = PlanFclty;
const {
operationalStatusList
} = OperationStatus;
const {
excelList
} = RainManage;
const {
chart_inflowRain,
chart_bodRain,
chart_inflTp,
chart_chongInTp,
chart_efflTp,
chart_inflTn,
chart_efflTn,
} = MainGraph;
/******************선택된 데이터******************/
// 연도
const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || '0');
// 시설명
const [fcltyList, setFcltyList] = useState<{bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string | '', efflWqltStndrdNm: string | ''}>({
bscFctCd: '',
bscFctNm: '',
fcltyCpc: '',
trtFrfNm: '',
efflWqltStndrdNm: ''
});
useEffect(() => {
if (!isInit) {
initializeCommonData();
setFcltyList({
bscFctCd: '11000SW001R',
bscFctNm: '난지',
fcltyCpc: 860000,
trtFrfNm: '표준활성슬러지법, MLE, A2O',
efflWqltStndrdNm: '500톤 이상(Ⅲ지역)'
});
}
}, [isInit, initializeCommonData]);
const handleFacilityChange = (facility: { bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string | '', efflWqltStndrdNm: string | '' }) => {
setFcltyList(facility);
};
const onSelectValue = (val: string, type: string) => {
switch(type) {
case 'sido':
setSidoValue(val);
initializeSigunData(val);
onSelectValue('0', 'sigun');
break;
case 'sigun':
setSigunValue(val);
break;
}
}
// 시군구 세팅
const initializeSigunData = (sidoCd: string) => {
if (sidoCd === '0') {
setSigunValue('0');
setFilteredSigunData([{ text: "전체", val: '0' }]);
} else {
const newFilteredData = [
{ text: "전체", val: '0' },
...selectSigunData.filter((item: SelectDataType) => item.val.substring(0, 2) === sidoCd)
];
setSigunValue('0');
setFilteredSigunData(newFilteredData);
}
}
const excelDownload = () => {
if (!fcltyList.bscFctCd) return;
switch (activeTab) {
case 'facilityInfo':
searchOperationStatus(false, searchYear, fcltyList.bscFctCd);
fetch(`/mainDashBoard.xlsx`, {}).then(async (response) => {
if (!response.ok) {
throw Error(response.statusText);
}
// 받은 파일 바인너리 처리
const blobRet = await response.arrayBuffer();
const excelData1 = planFcltyList.map((item) => {
return [
item.item,
item.provisionalApproval,
item.now,
item.stepOne,
item.stepTwo,
item.stepThree,
item.stepFour,
];
});
// operationalStatusList를 2차원 배열로 변환
const excelData2 = operationalStatusList.map((item, index) => {
const baseData = [
item.yearAvg,
item.yearMax,
item.yearMin,
item.summerAll,
item.summerThree,
item.summerRain,
item.winterAll,
item.winterSunnyAvg,
item.winterSunnyMax,
item.winterSunnyMin,
item.groundWaterAvg,
item.groundWaterSummer,
item.groundWaterWinter
];
// index가 0일 때만 rdi와 rdiRain 추가
return index === 0 ? [...baseData, item.rdi, item.rdiRain] : baseData;
});
const sheetParameter = [
{
data: excelData1,
headerNum: 3,
headerRange: 'A1:G2',
sheetName: '운영실태',
startCol: 'A'
},
{
data: excelData2,
headerNum: 4,
headerRange: 'A1:Q3',
sheetName: '운영실태',
startCol: 'C'
}
];
// 버퍼에 있는 엑셀데이터 전달 후 json으로 변환
await filePickedBlobArray(blobRet, '시설정보및운영현황황', sheetParameter);
});
break;
case 'mainGraph':
searchMainGraph(searchYear, fcltyList.bscFctCd);
const sheetParameter = [
{
data: [['날짜', '강수량'],...chart_inflowRain.map((item) => [item.label, item.barVal0])],
headerNum: 1,
headerRange: 'A1:B1',
sheetName: '강수량'
},
{
data: [['날짜', '유입하수량'],...chart_inflowRain.map((item) => [item.label, item.lineVal1])],
headerNum: 1,
headerRange: 'A1:B1',
sheetName: '유입하수량'
},
{
data: [['날짜', '시설용량'],...chart_inflowRain.map((item) => [item.label, item.lineVal0])],
headerNum: 1,
headerRange: 'A1:B1',
sheetName: '시설용량'
},
{
data: [['날짜', '유입BOD'],...chart_bodRain.map((item) => [item.label, item.lineVal0])],
headerNum: 1,
headerRange: 'A1:B1',
sheetName: '유입BOD'
},
{
data: [['날짜', '유입T-P'],...chart_inflTp.map((item) => [item.label, item.val])],
headerNum: 1,
headerRange: 'A1:B1',
sheetName: '유입T-P'
},
{
data: [['날짜', '총인유입T-P'],...chart_chongInTp.map((item) => [item.label, item.val])],
headerNum: 1,
headerRange: 'A1:B1',
sheetName: '총인유입T-P'
},
{
data: [['날짜', '방류T-P'],...chart_efflTp.map((item) => [item.label, item.val])],
headerNum: 1,
headerRange: 'A1:B1',
sheetName: '방류T-P'
},
{
data: [['날짜', '유입T-N'],...chart_inflTn.map((item) => [item.label, item.val])],
headerNum: 1,
headerRange: 'A1:B1',
sheetName: '유입T-N'
},
{
data: [['날짜', '방류T-N'],...chart_efflTn.map((item) => [item.label, item.val])],
headerNum: 1,
headerRange: 'A1:B1',
sheetName: '방류T-N'
}
];
downloadMultipleExcel('주요그래프', sheetParameter);
break;
case 'rainManage':
searchRainManage(searchYear, fcltyList.bscFctCd, true);
if (excelList && excelList.length > 0) {
downloadExcel({ data: excelList as unknown as JSON[], fileName: '강우일 운영현황', sheetNameArray: ['강우일 운영현황'], headerRange: 'A1:T1' });
}
break;
// Add other cases for different tabs as needed
default:
break;
}
}
const initVal = () => {
setFcltyList({
bscFctCd: '11000SW001R',
bscFctNm: '난지',
fcltyCpc: 860000,
trtFrfNm: '표준활성슬러지법, MLE, A2O',
efflWqltStndrdNm: '500톤 이상(Ⅲ지역)'
});
}
return (
<MainDiv>
<ContentsDiv>
<div className="flex card card-border !mt-6">
<div className="grid grid-cols-12 gap-5 w-8/12 m-4">
<SelectBox className="col-span-2" label={'연도'} selectArray={selectSearchYear} onSelectValue={ (val) => setSearchYear(val.toString()) } value={searchYear} selectClass={undefined}/>
<SelectBox className="col-span-3" label={'시도'} selectArray={selectSidoData} onSelectValue={(val) => onSelectValue(val.toString(), 'sido')} value={sidoValue} selectClass={undefined}/>
<SelectBox className="col-span-3" label={'시군구'} selectArray={filteredSigunData} onSelectValue={(val) => onSelectValue(val.toString(), 'sigun')} value={sigunValue} selectClass={undefined}/>
<div className="col-span-4 flex">
<DashBoardFclty onFacilityChange={handleFacilityChange} sidoSigun={sigunValue!='0'?sigunValue:sidoValue!='0'?sidoValue:''} facilityNm={fcltyList.bscFctNm}></DashBoardFclty>
</div>
</div>
<div className="w-4/12 mx-5 flex items-center justify-end space-x-2">
<Button className={btnClass} onClick={initVal}>
초기화
</Button>
<Button className={btnClass} color="green" onClick={excelDownload}>
엑셀다운로드
</Button>
</div>
</div>
<UITab tabList={tabList} defaultValue="facilityInfo" onValueChange={setActiveTab}>
<TabsContent value="facilityInfo">
<ProcessFacilitySearch searchYear={searchYear} fcltyList={[fcltyList]} onFacilityChange={handleFacilityChange} />
</TabsContent>
<TabsContent value="mainGraph">
<DashBoardChart searchYear={searchYear} fcltyList={[fcltyList]}/>
</TabsContent>
<TabsContent value="rainManage">
<RainManageTab searchYear={searchYear} fcltyList={[fcltyList]}/>
</TabsContent>
<TabsContent value="drainageEquipment">
<DrainageEquipment searchYear={searchYear} fcltyList={[fcltyList]}/>
</TabsContent>
</UITab>
</ContentsDiv>
</MainDiv>
);
}
@@ -0,0 +1,59 @@
"use client"
import { useFoStore } from "@/app/store";
import { UiTable } from "@common/business_components/ui/table";
import { useEffect, useState } from "react";
export type facilityInfoProps = {
searchYear?: string;
fcltyList?: { bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string, efflWqltStndrdNm: string }[];
};
export default function RainManageTab({searchYear = '', fcltyList = []}: facilityInfoProps) {
const {
Common,
searchRainManage,
RainManage
} = useFoStore((state) => state);
const [hasSearched, setHasSearched] = useState(false);
// 이전 시설코드 저장
const [prevFacilityCode, setPrevFacilityCode] = useState<string>('');
const {
isInit,
} = Common;
const {
upHeadListRainManage,
rainManageList,
} = RainManage;
const tableData = fcltyList;
useEffect(() => {
if (!isInit && tableData[0]?.bscFctCd && (tableData[0].bscFctCd !== prevFacilityCode || !hasSearched)) {
searchRainManage(searchYear, tableData[0].bscFctCd, false);
setHasSearched(true);
setPrevFacilityCode(tableData[0].bscFctCd);
}
}, [fcltyList, isInit, searchRainManage, searchYear, tableData, hasSearched, prevFacilityCode]);
// 연도가 바뀌면 hasSearched를 false로 리셋
useEffect(() => {
setHasSearched(false);
}, [searchYear]);
return (
<UiTable
headName=""
tableData={rainManageList}
headlist={upHeadListRainManage}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => {}}
>
</UiTable>
)
}
+58
View File
@@ -0,0 +1,58 @@
"use client"
import { useFoStore } from "@/app/store";
import { useEffect, useState } from "react";
import SearchInput from "../../components/common/SearchInput";
export default function DashBoardFclty({ onFacilityChange, sidoSigun, facilityNm }: { onFacilityChange?: (facility: { bscFctCd: string, bscFctNm: string, fcltyCpc: number | '', trtFrfNm: string | '', efflWqltStndrdNm: string | '' }) => void, sidoSigun?: string, facilityNm?: string }) {
// 입력값 상태 관리
const { Common, initializeCommonData } = useFoStore((state) => state);
const {
isInit,
} = Common;
// 시설명
const [bscFctNm, setFcltyName] = useState<string>(facilityNm || '난지');
const [bscFctCd, setbscFctCd] = useState<string>('11000SW001R');
const [fcltyCpc, setFcltyCpc] = useState<number | ''>(860000);
const [trtFrfNm, setTrtFrfNm] = useState<string | ''>('표준활성슬러지법, MLE, A2O');
const [efflWqltStndrdNm, setEfflWqltStndrdNm] = useState<string | ''>('500톤 이상(Ⅲ지역)');
useEffect(() => {
if (!isInit) {
initializeCommonData();
}
}, [isInit, initializeCommonData ]);
useEffect(() => {
if (facilityNm) {
setFcltyName(facilityNm);
}
}, [facilityNm]);
const fncNextPage = (bscFctCd?: string, bscFctNm?: string, fcltyCpc?: number | '', trtFrfNm?: string | '', efflWqltStndrdNm?: string | '') => {
setbscFctCd(bscFctCd || '');
setFcltyName(bscFctNm || '');
setFcltyCpc(fcltyCpc || '');
setTrtFrfNm(trtFrfNm || '');
setEfflWqltStndrdNm(efflWqltStndrdNm || '');
if (bscFctCd && onFacilityChange) {
onFacilityChange({
bscFctCd: bscFctCd || '',
bscFctNm: bscFctNm || '',
fcltyCpc: fcltyCpc || '',
trtFrfNm: trtFrfNm || '',
efflWqltStndrdNm: efflWqltStndrdNm || ''
});
}
}
console.log('bscFctNm : ', bscFctNm);
return (
<>
<SearchInput label={'시설명'} setValue={fncNextPage} sidoSigun={sidoSigun} fcltyName={bscFctNm}/>
</>
);
}
@@ -0,0 +1,203 @@
"use client"
import { useFoStore } from "@/app/store";
import type { PrecipitationPropsDetailProps } from "@/app/store/facilityPrecipitation";
import { DatePickerRange, UiTable } from "@common/business_components/ui";
import ListDiv from "@common/business_components/ui/list_div";
import { Table, TableCell, TableHead, TableRow } from "@common/components/ui";
import { useEffect, useRef, useState } from "react";
type PrecipitationModalProps = {
searchYear: string;
fcltyName: string;
facilityCd: string;
detailList: PrecipitationPropsDetailProps[];
}
type DateRange = {
start: Date;
end: Date;
}
export default function PrecipitationModal(props: PrecipitationModalProps) {
const { PrecipitationDetail } = useFoStore((state) => state);
const { upHeadListPrecipitationDetail } = PrecipitationDetail;
// Refs
const startPickerRef = useRef<HTMLDivElement>(null);
const endPickerRef = useRef<HTMLDivElement>(null);
// States
const [startDate, setStartDate] = useState<Date | null>(null);
const [endDate, setEndDate] = useState<Date | null>(null);
const [filteredDetailList, setFilteredDetailList] = useState<PrecipitationPropsDetailProps[]>([]);
const [copyDetailList, setCopyDetailList] = useState<PrecipitationPropsDetailProps[]>([]);
const getDateRanges = (): DateRange[] => {
const standYear = parseInt(props.searchYear);
return [{
start: new Date(standYear, 0, 1),
end: new Date(standYear, 11, 31)
}];
};
const isDateInRanges = (date: Date): boolean => {
return getDateRanges().some(range => {
const start = new Date(range.start);
const end = new Date(range.end);
return date >= start && date <= end;
});
};
const filterDataByDateRange = (start: Date | null, end: Date | null): PrecipitationPropsDetailProps[] => {
if (!start || !end) return filteredDetailList;
return filteredDetailList.filter((item) => {
const itemDate = new Date(item.rainDt);
return itemDate >= start && itemDate <= end;
});
};
const handleStartDateSelect = (date: Date | null) => {
if (!date || !endDate) return;
if (date > endDate) {
alert('시작일은 종료일 이후일 수 없습니다.');
return;
}
const filteredData = filterDataByDateRange(date, endDate);
setCopyDetailList(filteredData);
setStartDate(date);
const button = startPickerRef.current?.querySelector('button');
if (button) {
setTimeout(() => button.click(), 0);
}
};
const handleEndDateSelect = (date: Date | null) => {
if (!date || !startDate) return;
if (date < startDate) {
alert('종료일은 시작일 이전일 수 없습니다.');
return;
}
const filteredData = filterDataByDateRange(startDate, date);
setCopyDetailList(filteredData);
setEndDate(date);
const button = endPickerRef.current?.querySelector('button');
if (button) {
setTimeout(() => button.click(), 0);
}
};
const calculateTotals = () => {
return copyDetailList.reduce((acc, item) => ({
infoRnDay: acc.infoRnDay + Number(item.infoRnDay),
rainDay: acc.rainDay + Number(item.rainDay),
rainEffectDay: acc.rainEffectDay + Number(item.rainEffectDay)
}), { infoRnDay: 0, rainDay: 0, rainEffectDay: 0 });
};
const getTableData = () => {
const totals = calculateTotals();
return [
{
bscFctCd: "",
rainDt: '합계',
...totals
},
...copyDetailList
];
};
useEffect(() => {
if (props.detailList.length > 0) {
const filteredList = props.detailList.filter((item) =>
props.facilityCd === item.bscFctCd
);
setFilteredDetailList(filteredList);
setCopyDetailList(filteredList);
if (filteredList.length > 0) {
const firstDateStr = filteredList[0]?.rainDt;
const lastDateStr = filteredList[filteredList.length - 1]?.rainDt;
if (firstDateStr) {
const firstDate = new Date(firstDateStr);
if (!isNaN(firstDate.getTime())) {
setStartDate(firstDate);
}
}
if (lastDateStr) {
const lastDate = new Date(lastDateStr);
if (!isNaN(lastDate.getTime())) {
setEndDate(lastDate);
}
}
}
}
}, [props.detailList, props.facilityCd]);
if (!startDate || !endDate) {
return <div>로딩 중...</div>;
}
return (
<>
<ListDiv className="!h-[80px] !mt-10">
<div className="card !m-[1.2rem] max-h-[40rem] overflow-auto !p-0r">
<Table>
<thead>
<TableRow>
<TableHead className="w-1/6">{'시설명'}</TableHead>
<TableCell className="w-1/3">{props.fcltyName}</TableCell>
<TableHead className="w-1/6">{'년월'}</TableHead>
<TableCell className="w-1/3">
<div className="flex">
<div ref={startPickerRef} className="flex justify-center">
<DatePickerRange
mode="single"
selected={startDate}
onSelect={(date) => handleStartDateSelect(date as Date)}
className="w-full p-1 border rounded bg-white"
disabled={(date) => !isDateInRanges(date)}
defaultMonth={startDate}
/>
</div>
<span className="mx-3">~</span>
<div ref={endPickerRef} className="flex justify-center">
<DatePickerRange
mode="single"
selected={endDate}
onSelect={(date) => handleEndDateSelect(date as Date)}
className="w-full p-1 border rounded bg-white"
disabled={(date) => !isDateInRanges(date)}
defaultMonth={endDate}
/>
</div>
</div>
</TableCell>
</TableRow>
</thead>
</Table>
</div>
</ListDiv>
<ListDiv className="h-[450px] overflow-y-auto">
<UiTable
headName={"강수현황"}
tableData={getTableData()}
headlist={upHeadListPrecipitationDetail}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => {}}
boldArray={[0]}
/>
</ListDiv>
</>
);
}
@@ -0,0 +1,249 @@
"use client"
import { useFoStore } from "@/app/store";
import type { TableUpperProps } from "@/app/store/common";
import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components";
import ListDiv from "@common/business_components/ui/list_div";
import { useCallback, useEffect, useRef, useState } from "react";
import PrecipitationModal from "./modal/page";
export default function FacilityPrecipitation() {
const {Common
,FacilityPrecipitation
,FacilityPrecipitationSearch
,PrecipitationDetail
,initializeCommonData
,searchFacilityPrecipitationList
} = useFoStore((state) => state) || { processAreaList: { processAreaList: [] }};
const {
isInit,
selectSidoData,
selectSigunData,
selectRegionData,
selectUnitWaterShedData,
selectMiddleRegionData,
selectEnvPartData,
selectUpdownData,
selectOperationData,
selectSearchYear,
btnClass
} = Common;
const {
labelArray,
} = FacilityPrecipitationSearch;
const {
upHeadList1,
upHeadList2,
upHeadList3,
excelList,
facilityPrecipitation,
facilityPrecipitationCount
} = FacilityPrecipitation;
const {
gridListPrecipitationDetail
} = PrecipitationDetail;
const ref = useRef<ReturnType>({
partValue: '',
sidoValue: '',
sigunValue: '',
regionValue: '',
unitWaterShedValue: '',
middleRegionValue: '',
envPartValue: '',
sidoText: '',
sigunText: ''
});
const facilityPrecipitationList = FacilityPrecipitation?.facilityPrecipitation || [];
// 그리드 관련
const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null);
// 모달 상태 추가
const [selectedRow, setSelectedRow] = useState<number | null>(null);
const [fcltyNm, setFcltyNm] = useState<string>("");
const [fcltyCd, setFcltyCd] = useState<string>("");
// 초기화 변수
const [resetPage, setResetPage] = useState<boolean>(false);
// 표 너비
const [widthSize, setWidthSize] = useState<string>("w-[120%]");
// 구분별 그리드 헤더
const [upheadList, setUpheadList] = useState<TableUpperProps[]>(upHeadList1);
// 용량별
const [fcltCpct, setFcltCpct] = useState<string>("0");
// 시설명
const [fcltyName, setFcltyName] = useState<string>("");
// 시설상태
const [operationValue, setOperationValue] = useState<string>("0");
// 조회기간
const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0");
/**
* 페이지 함수
*/
// 초기화
const resetVal = () => {
setWidthSize("w-[120%]");
setUpheadList(upHeadList1)
// 상태값 설정
setFcltCpct(selectUpdownData[0]?.val || "0");
setOperationValue(selectOperationData[0]?.val || "0");
setSearchYear(selectSearchYear[0]?.val || "0");
// SearchInputSelect 초기화
setFcltyName("");
if (searchInputRef.current) {
const input = searchInputRef.current.querySelector('input');
if (input) {
input.value = "";
}
}
searchFacilityPrecipitationList(1, 10, selectSearchYear[0]?.val || "0");
}
// 모달 닫기
const closeModal = () => {
setSelectedRow(null);
};
// 셀 클릭 시 모달 표시
const cellClick = (index: number) => {
if (facilityPrecipitation[index]) {
setFcltyNm(facilityPrecipitation[index].bscFctNm);
setFcltyCd(facilityPrecipitation[index].bscFctCd);
setSelectedRow(index); // 클릭한 행의 index 저장
}
};
/**
* useEffect
*/
useEffect(() => {
if (!isInit) {
initializeCommonData();
searchFacilityPrecipitationList(1, 10, selectSearchYear[0]?.val || "0");
}
}, [isInit, initializeCommonData, searchFacilityPrecipitationList, selectSearchYear]);
useEffect(() => {
if (excelList && excelList.length > 0) {
downloadExcel({
data: excelList as unknown as JSON[],
fileName: '시설별 강수현황',
sheetNameArray: ['시설별 강수현황'],
headerRange: 'A1:N1'
});
}
}, [excelList]);
useEffect(() => {
if (facilityPrecipitation.length > 0) {
}
}, [facilityPrecipitation]);
/**
* 콜백함수
*/
// 조회
const searchFacilityPrecipitation = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => {
console.log('partValue : ', partValue);
console.log('regionValue : ', regionValue);
console.log('unitWaterShedValue : ', unitWaterShedValue);
console.log('middleRegionValue : ', middleRegionValue);
console.log('envPartValue : ', envPartValue);
console.log('sidoValue : ', sidoValue);
console.log('sigunValue : ', sigunValue);
let nameCd, partStr
switch (partValue) {
case 'R':
if (regionValue !== '0') {
if(unitWaterShedValue==='0') nameCd = regionValue;
else if (middleRegionValue==='0') nameCd = unitWaterShedValue;
else nameCd = middleRegionValue;
}
partStr = partValue
break;
default:
if(sidoValue !== '0') nameCd = sidoValue;
if(sigunValue !== '0') nameCd = sigunValue;
partStr = envPartValue !== '0' ? envPartValue:partValue;
break;
}
if (partStr.length > 1 || operationValue!== '0' || fcltCpct !== '0' ||nameCd ||(fcltyName.trim() !== "" && fcltyName.trim() !== null)) {
searchFacilityPrecipitationList(pageNo, pageCnt, searchYear, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined);
} else {
searchFacilityPrecipitationList(pageNo, pageCnt, searchYear);
}
}, [operationValue, fcltyName, searchFacilityPrecipitationList, searchYear, fcltCpct]);
const chkVal = useCallback((returntype:ReturnType) => {
setWidthSize(returntype.partValue=='A' ?"w-[120%]":"w-[130%]")
setUpheadList(returntype.partValue == 'R' ? upHeadList2 : returntype.partValue == 'G'?upHeadList3:upHeadList1);
setResetPage(true);
}, [upHeadList1, upHeadList2, upHeadList3]);
// 엑셀다운로드
const excelDownload = useCallback((searchReturn: ReturnType) => {
searchFacilityPrecipitation(searchReturn, 1, 10000);
}, [searchFacilityPrecipitation]);
// 페이지 변경
const handlePageChange = useCallback((page: number) => {
setResetPage(false);
searchFacilityPrecipitation(ref.current, page, 10);
}, [searchFacilityPrecipitation]);
const mainList = facilityPrecipitationList || [];
const total = facilityPrecipitationCount || 0;
return (
<MainDiv>
<Title title="시설별 강수현황" />
<ContentsDiv>
<SearchBaseForm
leftChildren={<>
<SelectBox className="col-span-3" label={labelArray[3] as string} selectArray={selectUpdownData} onSelectValue={(val) => setFcltCpct(val.toString())} value={fcltCpct} selectClass={undefined} />
<SearchInput className={"col-span-3"} label={labelArray[4] as string} textValue={fcltyName} setText={setFcltyName} />
<SelectBox className="col-span-3" label={labelArray[6] as string} selectArray={selectSearchYear} onSelectValue={(val) => setSearchYear(val.toString())} value={searchYear} selectClass={undefined} />
<SelectBox className="col-span-3" label={labelArray[5] as string} selectArray={selectOperationData} onSelectValue={(val) => setOperationValue(val.toString())} value={operationValue} selectClass={undefined} />
</>}
rightChildren={undefined}
selectSidoData={selectSidoData}
selectSigunData={selectSigunData}
selectRegionData={selectRegionData}
selectUnitWaterShedData={selectUnitWaterShedData}
selectMiddleRegionData={selectMiddleRegionData}
selectEnvPartData={selectEnvPartData}
btnClass={btnClass}
queryClick={chkVal}
excelClick={excelDownload}
initClick={resetVal}
ref={ref} isAdmin={false} />
<ListDiv>
<div className="h-full w-full overflow-x-scroll">
<div className={widthSize}>
<UiTable cellClick={(index: number | undefined) => index !== undefined && cellClick(index)} headName={"시설별 강수현황"} tableData={mainList} headlist={upheadList} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} />
</div>
<span className="text-2xl font-bold text-gray-500">☞ 강우일은 3mm 이상만 해당 / 강우영향일은 강우일 및 강우일 후 연속 2일간으로 정의</span>
</div>
</ListDiv>
</ContentsDiv>
{selectedRow !== null && (
<UiModal
body={<PrecipitationModal searchYear={searchYear} fcltyName={fcltyNm} facilityCd={fcltyCd} detailList={gridListPrecipitationDetail}></PrecipitationModal>}
bodyClass="sm:max-w-[90vw] w-[72vw] h-[40vw]"
open={selectedRow !== null}
onOpenChange={(open) => !open && closeModal()}
noHeaderYn="Y"
noFooterYn="Y"
/>
)}
</MainDiv>
);
}
@@ -0,0 +1,176 @@
import { TableHead, TableRow } from "@common/components/ui";
interface TableUpperProps {
id: string;
title: string;
upName?: string;
upSequnce?: number;
}
const HeadMakeColSpan = ({ upHeadList, layoutNum }: { upHeadList: TableUpperProps[], layoutNum: number }) => {
return (
layoutNum === 0 ? (
<>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => {
if(!head.upName) {
return <TableHead className="bg-gray-50" rowSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead>
}else if(head.upName==="upChangeRe1"&&head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>행정구역</TableHead>
}else if(head.upName==="upChangeRe3"&&head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={5} style={{ whiteSpace: 'pre-line' }} key={head.id}>유입</TableHead>
}else if(head.upName==="upChangeRe4"&&head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={5} style={{ whiteSpace: 'pre-line' }} key={head.id}>유출</TableHead>
}
})}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps, idx: number) => {
if (head.upName === 'upChangeRe1' || head.upName === 'upChangeRe3' || head.upName === 'upChangeRe4') {
if (idx == 1 || idx == 2 || idx == 3) {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>;
}
else {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>;
}
}
})}
</TableRow>
</>
) : layoutNum === 1 ? (
<>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => {
if(!head.upName) {
return <TableHead className="bg-gray-50" rowSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead>
}else if(head.upName==="upChangeRe1"&&head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={3} style={{ whiteSpace: 'pre-line' }} key={head.id}>권역</TableHead>
}else if(head.upName==="upChangeRe3"&&head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={5} style={{ whiteSpace: 'pre-line' }} key={head.id}>유입</TableHead>
}else if(head.upName==="upChangeRe4"&&head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={5} style={{ whiteSpace: 'pre-line' }} key={head.id}>유출</TableHead>
}
})}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps, idx: number) => {
if (head.upName === 'upChangeRe1' || head.upName === 'upChangeRe3' || head.upName === 'upChangeRe4') {
if (idx == 1 || idx == 2 || idx == 3) {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>;
}
else {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>;
}
}
})}
</TableRow>
</>
) : layoutNum === 2 ? (
<>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => {
if(!head.upName) {
return <TableHead className="bg-gray-50" rowSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead>
}else if(head.upName==="upChangeRe1"&&head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={3} style={{ whiteSpace: 'pre-line' }} key={head.id}>권역</TableHead>
}else if(head.upName==="upChangeRe2"&&head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>행정구역</TableHead>
}else if(head.upName==="upChangeRe3"&&head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={5} style={{ whiteSpace: 'pre-line' }} key={head.id}>유입</TableHead>
}else if(head.upName==="upChangeRe4"&&head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={5} style={{ whiteSpace: 'pre-line' }} key={head.id}>유출</TableHead>
}
})}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps, idx: number) => {
if (head.upName === 'upChangeRe1' || head.upName === 'upChangeRe2' || head.upName === 'upChangeRe3' || head.upName === 'upChangeRe4') {
if (idx == 1 || idx == 2 || idx == 3) {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>;
}
else {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>;
}
}
})}
</TableRow>
</>
) : layoutNum === 3 ? (
<>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => {
if(!head.upName) {
return <TableHead className="bg-gray-50" rowSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead>
}else if(head.upName==="upChangeRe1"&&head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={5} style={{ whiteSpace: 'pre-line' }} key={head.id}>유입</TableHead>
}else if(head.upName==="upChangeRe2"&&head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={5} style={{ whiteSpace: 'pre-line' }} key={head.id}>방류</TableHead>
}
})}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps, idx: number) => {
if (head.upName === 'upChangeRe1' || head.upName === 'upChangeRe2') {
if (idx == 1 || idx == 2 || idx == 3) {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>;
}
else {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>;
}
}
})}
</TableRow>
</>
) : layoutNum === 4 ? (
<>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => {
if(!head.upName) {
return <TableHead className="bg-gray-50" rowSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead>
}else if(head.upName==="upChangeRe"&&head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={4} style={{ whiteSpace: 'pre-line' }} key={head.id}>방류</TableHead>
}
})}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps, idx: number) => {
if (head.upName === 'upChangeRe') {
if (idx == 1 || idx == 2 || idx == 3) {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>;
}
else {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>;
}
}
})}
</TableRow>
</>
) : layoutNum === 5 ? ( // 총인설비 수질현황 헤더
<>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => {
if(!head.upName) {
return <TableHead className="w-[180px] bg-gray-50" rowSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead>
}else if(head.upName==="upChangeRe1" && head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>유입</TableHead>
}else if(head.upName==="upChangeRe2" && head.upSequnce===1) {
return <TableHead className="bg-gray-50" colSpan={2} style={{ whiteSpace: 'pre-line' }} key={head.id}>방류</TableHead>
}
})}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => {
if (head.upName === 'upChangeRe1' || head.upName === 'upChangeRe2') {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-1/8 bg-gray-50">{head.title}</TableHead>;
}
})}
</TableRow>
</>
): (
<>
</>
)
)
}
export { HeadMakeColSpan };
@@ -0,0 +1,571 @@
"use client"
import { useFoStore } from "@/app/store";
import type { FacilityWaterQltyProps, modalExcelType } from "@/app/store/facilityWaterQlty";
import { downloadExcel, UITab, UiTable } from "@common/business_components/ui";
import { Button, TabsContent } from "@common/components/ui";
import { useCallback, useEffect, useRef, useState } from "react";
import type { ReturnType } from "../../../components/common/FacilityTermModalHeader";
import FacilityTermModalHeader from "../../../components/common/FacilityTermModalHeader";
import { HeadMakeColSpan } from "../HeadMakeColSpan";
type ModalProps ={
fcltyName: string;
facilityCd : string;
searchYear : string;
}
const tabList1 = [
{ value: 'tab_num1', label: '처리시설 수질현황' },
{ value: 'tab_num2', label: '총인설비 수질현황' },
];
const tabList2 = [
{ value: 'tab_term_year', label: '연간' },
{ value: 'tab_term_summer', label: '하절기' },
{ value: 'tab_term_winter', label: '동절기' },
{ value: 'tab_term_chuncheon', label: '청천시' },
{ value: 'tab_term_rain', label: '강우시' }
];
const tabList3 = [
{ value: 'tab_term_year_chongin', label: '연간' },
{ value: 'tab_term_summer_chongin', label: '하절기' },
{ value: 'tab_term_winter_chongin', label: '동절기' }
];
export default function WaterQualityModal({fcltyName='', facilityCd='', searchYear=new Date().getFullYear().toString()} : ModalProps) {
// 그리드 관련
const {FacilityWaterQltyModal,
searchFacilityWaterQltyListHist,
Common
} = useFoStore((state) => state);
const {btnClass} = Common;
const {
upHeadListModalYear,
facilityWaterQltyYearList,
upHeadListModalTms,
tmsList,
msrmtDate,
upHeadListModalChongIn,
} = FacilityWaterQltyModal;
const prevExcelListRef = useRef<modalExcelType[] | null>(null);
// 필터링 된 후 최대, 최소, 평균 값 계산
const setFilterAfterData = (filteredData: FacilityWaterQltyProps[]) => {
// 쉼표가 포함된 문자열을 숫자로 변환하는 함수
const parseNumber = (value: string | number | null | undefined): number => {
if (value === null || value === undefined || value === '') return 0;
if (typeof value === 'number') return value;
return Number(value.replace(/,/g, ''));
};
let maxEfflWqltBod = 0;
let maxEfflWqltTn = 0;
let maxEfflWqltTp = 0;
let maxefflWqltCod = 0;
let maxEfflWqltSs = 0;
let maxInflAssctAftWqltBod = 0;
let maxInflAssctAftWqltTn = 0;
let maxInflAssctBfrWqltTp = 0;
let maxInflAssctAftWqltToc = 0;
let maxInflAssctAftWqltSs = 0;
let maxTpInflWqltBod = 0;
let maxTpInflWqltTp = 0;
let maxInfoRnDay = 0;
// 초기값 설정
let minEfflWqltBod = filteredData.length > 0 ? parseNumber(filteredData[0]?.efflWqltBod) : 0;
let minEfflWqltTn = filteredData.length > 0 ? parseNumber(filteredData[0]?.efflWqltTn) : 0;
let minEfflWqltTp = filteredData.length > 0 ? parseNumber(filteredData[0]?.efflWqltTp) : 0;
let minefflWqltCod = filteredData.length > 0 ? parseNumber(filteredData[0]?.efflWqltCod) : 0;
let minEfflWqltSs = filteredData.length > 0 ? parseNumber(filteredData[0]?.efflWqltSs) : 0;
let minInflAssctAftWqltBod = filteredData.length > 0 ? parseNumber(filteredData[0]?.inflAssctAftWqltBod) : 0;
let minInflAssctAftWqltTn = filteredData.length > 0 ? parseNumber(filteredData[0]?.inflAssctAftWqltTn) : 0;
let minInflAssctBfrWqltTp = filteredData.length > 0 ? parseNumber(filteredData[0]?.inflAssctBfrWqltTp) : 0;
let minInflAssctAftWqltToc = filteredData.length > 0 ? parseNumber(filteredData[0]?.inflAssctAftWqltToc) : 0;
let minInflAssctAftWqltSs = filteredData.length > 0 ? parseNumber(filteredData[0]?.inflAssctAftWqltSs) : 0;
let minTpInflWqltBod = filteredData.length > 0 ? parseNumber(filteredData[0]?.tpInflWqltBod) : 0;
let minTpInflWqltTp = filteredData.length > 0 ? parseNumber(filteredData[0]?.tpInflWqltTp) : 0;
let minInfoRnDay = filteredData.length > 0 ? parseNumber(filteredData[0]?.infoRnDay) : 0;
let avgEfflWqltBod = 0;
let avgEfflWqltTn = 0;
let avgEfflWqltTp = 0;
let avgefflWqltCod = 0;
let avgEfflWqltSs = 0;
let avgInflAssctAftWqltBod = 0;
let avgInflAssctAftWqltTn = 0;
let avgInflAssctBfrWqltTp = 0;
let avgInflAssctAftWqltToc = 0;
let avgInflAssctAftWqltSs = 0;
let avgTpInflWqltBod = 0;
let avgTpInflWqltTp = 0;
let avgInfoRnDay = 0;
filteredData.forEach((item: FacilityWaterQltyProps) => {
const efflWqltBod = parseNumber(item.efflWqltBod);
const efflWqltTn = parseNumber(item.efflWqltTn);
const efflWqltTp = parseNumber(item.efflWqltTp);
const efflWqltCod = parseNumber(item.efflWqltCod);
const efflWqltSs = parseNumber(item.efflWqltSs);
const inflAssctAftWqltBod = parseNumber(item.inflAssctAftWqltBod);
const inflAssctAftWqltTn = parseNumber(item.inflAssctAftWqltTn);
const inflAssctBfrWqltTp = parseNumber(item.inflAssctBfrWqltTp);
const inflAssctAftWqltToc = parseNumber(item.inflAssctAftWqltToc);
const inflAssctAftWqltSs = parseNumber(item.inflAssctAftWqltSs);
const tpInflWqltBod = parseNumber(item.tpInflWqltBod);
const tpInflWqltTp = parseNumber(item.tpInflWqltTp);
const infoRnDay = parseNumber(item.infoRnDay);
maxEfflWqltBod = Math.max(maxEfflWqltBod, efflWqltBod);
maxEfflWqltTn = Math.max(maxEfflWqltTn, efflWqltTn);
maxEfflWqltTp = Math.max(maxEfflWqltTp, efflWqltTp);
maxefflWqltCod = Math.max(maxefflWqltCod, efflWqltCod);
maxEfflWqltSs = Math.max(maxEfflWqltSs, efflWqltSs);
maxInflAssctAftWqltBod = Math.max(maxInflAssctAftWqltBod, inflAssctAftWqltBod);
maxInflAssctAftWqltTn = Math.max(maxInflAssctAftWqltTn, inflAssctAftWqltTn);
maxInflAssctBfrWqltTp = Math.max(maxInflAssctBfrWqltTp, inflAssctBfrWqltTp);
maxInflAssctAftWqltToc = Math.max(maxInflAssctAftWqltToc, inflAssctAftWqltToc);
maxInflAssctAftWqltSs = Math.max(maxInflAssctAftWqltSs, inflAssctAftWqltSs);
maxTpInflWqltBod = Math.max(maxTpInflWqltBod, tpInflWqltBod);
maxTpInflWqltTp = Math.max(maxTpInflWqltTp, tpInflWqltTp);
maxInfoRnDay = Math.max(maxInfoRnDay, infoRnDay);
minEfflWqltBod = Math.min(minEfflWqltBod, efflWqltBod);
minEfflWqltTn = Math.min(minEfflWqltTn, efflWqltTn);
minEfflWqltTp = Math.min(minEfflWqltTp, efflWqltTp);
minefflWqltCod = Math.min(minefflWqltCod, efflWqltCod);
minEfflWqltSs = Math.min(minEfflWqltSs, efflWqltSs);
minInflAssctAftWqltBod = Math.min(minInflAssctAftWqltBod, inflAssctAftWqltBod);
minInflAssctAftWqltTn = Math.min(minInflAssctAftWqltTn, inflAssctAftWqltTn);
minInflAssctBfrWqltTp = Math.min(minInflAssctBfrWqltTp, inflAssctBfrWqltTp);
minInflAssctAftWqltToc = Math.min(minInflAssctAftWqltToc, inflAssctAftWqltToc);
minInflAssctAftWqltSs = Math.min(minInflAssctAftWqltSs, inflAssctAftWqltSs);
minTpInflWqltBod = Math.min(minTpInflWqltBod, tpInflWqltBod);
minTpInflWqltTp = Math.min(minTpInflWqltTp, tpInflWqltTp);
minInfoRnDay = Math.min(minInfoRnDay, infoRnDay);
avgEfflWqltBod += efflWqltBod;
avgEfflWqltTn += efflWqltTn;
avgEfflWqltTp += efflWqltTp;
avgefflWqltCod += efflWqltCod;
avgEfflWqltSs += efflWqltSs;
avgInflAssctAftWqltBod += inflAssctAftWqltBod;
avgInflAssctAftWqltTn += inflAssctAftWqltTn;
avgInflAssctBfrWqltTp += inflAssctBfrWqltTp;
avgInflAssctAftWqltToc += inflAssctAftWqltToc;
avgInflAssctAftWqltSs += inflAssctAftWqltSs;
avgTpInflWqltBod += tpInflWqltBod;
avgTpInflWqltTp += tpInflWqltTp;
avgInfoRnDay += infoRnDay;
});
const newFilteredData = [
{
msrDt: '평균',
efflWqltBod: filteredData.length==0?'0.0':(Math.round(avgEfflWqltBod / filteredData.length * 10) / 10).toFixed(1),
efflWqltCod: filteredData.length==0?'0.0':(Math.round(avgefflWqltCod / filteredData.length * 10) / 10).toFixed(1),
efflWqltSs: filteredData.length==0?'0.0':(Math.round(avgEfflWqltSs / filteredData.length * 10) / 10).toFixed(1),
efflWqltTn: filteredData.length==0?'0.000':(Math.round(avgEfflWqltTn / filteredData.length * 1000) / 1000).toFixed(3),
efflWqltTp: filteredData.length==0?'0.000':(Math.round(avgEfflWqltTp / filteredData.length * 1000) / 1000).toFixed(3),
inflAssctAftWqltBod: filteredData.length==0?'0.0':(Math.round(avgInflAssctAftWqltBod / filteredData.length * 10) / 10).toFixed(1),
inflAssctAftWqltTn: filteredData.length==0?'0.000':(Math.round(avgInflAssctAftWqltTn / filteredData.length * 1000) / 1000).toFixed(3),
inflAssctBfrWqltTp: filteredData.length==0?'0.000':(Math.round(avgInflAssctBfrWqltTp / filteredData.length * 1000) / 1000).toFixed(3),
inflAssctAftWqltToc: filteredData.length==0?'0.0':(Math.round(avgInflAssctAftWqltToc / filteredData.length * 10) / 10).toFixed(1),
inflAssctAftWqltSs: filteredData.length==0?'0.0':(Math.round(avgInflAssctAftWqltSs / filteredData.length * 10) / 10).toFixed(1),
bscFctCd: "",
bscFctNm: "",
lclgvrnmntNm: "",
sidoNm: "",
sggNm: "",
addr: "",
fctCpc: 0,
infoRnDay: filteredData.length==0?'0.0':(Math.round(avgInfoRnDay / filteredData.length * 10) / 10).toFixed(1),
tpInflWqltBod: filteredData.length==0?'0.0':(Math.round(avgTpInflWqltBod / filteredData.length * 10) / 10).toFixed(1),
tpInflWqltTp: filteredData.length==0?'0.000':(Math.round(avgTpInflWqltTp / filteredData.length * 1000) / 1000).toFixed(3)
},
{
msrDt : '최대',
efflWqltBod : Math.round(maxEfflWqltBod * 10) / 10?(Math.round(maxEfflWqltBod * 10) / 10).toFixed(1):'0.0',
efflWqltCod : Math.round(maxefflWqltCod * 10) / 10?(Math.round(maxefflWqltCod * 10) / 10).toFixed(1):'0.0',
efflWqltSs : Math.round(maxEfflWqltSs * 10) / 10?(Math.round(maxEfflWqltSs * 10) / 10).toFixed(1):'0.0',
efflWqltTn : Math.round(maxEfflWqltTn * 1000) / 1000?(Math.round(maxEfflWqltTn * 1000) / 1000).toFixed(3):'0.000',
efflWqltTp : Math.round(maxEfflWqltTp * 1000) / 1000?(Math.round(maxEfflWqltTp * 1000) / 1000).toFixed(3):'0.000',
inflAssctAftWqltBod : Math.round(maxInflAssctAftWqltBod * 10) / 10?(Math.round(maxInflAssctAftWqltBod * 10) / 10).toFixed(1):'0.0',
inflAssctAftWqltTn : Math.round(maxInflAssctAftWqltTn * 1000) / 1000?(Math.round(maxInflAssctAftWqltTn * 1000) / 1000).toFixed(3):'0.000',
inflAssctBfrWqltTp : Math.round(maxInflAssctBfrWqltTp * 1000) / 1000?(Math.round(maxInflAssctBfrWqltTp * 1000) / 1000).toFixed(3):'0.000',
inflAssctAftWqltToc : Math.round(maxInflAssctAftWqltToc * 10) / 10?(Math.round(maxInflAssctAftWqltToc * 10) / 10).toFixed(1):'0.0',
inflAssctAftWqltSs : Math.round(maxInflAssctAftWqltSs * 10) / 10?(Math.round(maxInflAssctAftWqltSs * 10) / 10).toFixed(1):'0.0',
bscFctCd: "",
bscFctNm: "",
lclgvrnmntNm: "",
sidoNm: "",
sggNm: "",
addr: "",
fctCpc: 0,
infoRnDay: 0,
tpInflWqltBod: Math.round(maxTpInflWqltBod * 10) / 10?(Math.round(maxTpInflWqltBod * 10) / 10).toFixed(1):'0.0',
tpInflWqltTp: Math.round(maxTpInflWqltTp * 1000) / 1000?(Math.round(maxTpInflWqltTp * 1000) / 1000).toFixed(3):'0.000'
},
{
msrDt : '최소',
efflWqltBod : Math.round(minEfflWqltBod * 10) / 10?(Math.round(minEfflWqltBod?minEfflWqltBod:0 * 10) / 10).toFixed(1) : '0.0',
efflWqltCod : Math.round(minefflWqltCod * 10) / 10?(Math.round(minefflWqltCod?minefflWqltCod:0 * 10) / 10).toFixed(1) : '0.0',
efflWqltSs : Math.round(minEfflWqltSs * 10) / 10?(Math.round(minEfflWqltSs * 10) / 10).toFixed(1) : '0.0',
efflWqltTn : Math.round(minEfflWqltTn * 1000) / 1000?(Math.round(minEfflWqltTn * 1000) / 1000).toFixed(3) : '0.000',
efflWqltTp : Math.round(minEfflWqltTp * 1000) / 1000?(Math.round(minEfflWqltTp * 1000) / 1000).toFixed(3) : '0.000',
inflAssctAftWqltBod : Math.round(minInflAssctAftWqltBod * 10) / 10?(Math.round(minInflAssctAftWqltBod * 10) / 10).toFixed(1) : '0.0',
inflAssctAftWqltTn : Math.round(minInflAssctAftWqltTn * 1000) / 1000?(Math.round(minInflAssctAftWqltTn * 1000) / 1000).toFixed(3) : '0.000',
inflAssctBfrWqltTp : Math.round(minInflAssctBfrWqltTp * 1000) / 1000?(Math.round(minInflAssctBfrWqltTp * 1000) / 1000).toFixed(3) : '0.000',
inflAssctAftWqltToc : Math.round(minInflAssctAftWqltToc * 10) / 10?(Math.round(minInflAssctAftWqltToc * 10) / 10).toFixed(1) : '0.0',
inflAssctAftWqltSs : Math.round(minInflAssctAftWqltSs * 10) / 10?(Math.round(minInflAssctAftWqltSs * 10) / 10).toFixed(1) : '0.0',
bscFctCd: "",
bscFctNm: "",
lclgvrnmntNm: "",
sidoNm: "",
sggNm: "",
addr: "",
fctCpc: 0,
infoRnDay: 0,
tpInflWqltBod: Math.round(minTpInflWqltBod * 10) / 10?'0.0':(Math.round(minTpInflWqltBod * 10) / 10).toFixed(1),
tpInflWqltTp: Math.round(minTpInflWqltTp * 1000) / 1000?'0.000':(Math.round(minTpInflWqltTp * 1000) / 1000).toFixed(3)
},
...filteredData
];
return newFilteredData;
}
// 날짜 필터링 관련 상태
const [copyYearList, setCopyYearList] = useState<FacilityWaterQltyProps[]>([]);
const [chongInWaterQltyYearList, setChongInWaterQltyYearList] = useState<FacilityWaterQltyProps[]>([]);
useEffect(() => {
searchFacilityWaterQltyListHist(searchYear, facilityCd);
}, [searchYear, facilityCd, searchFacilityWaterQltyListHist]);
useEffect(() => {
const filteredYearData = setFilterAfterData(facilityWaterQltyYearList);
setCopyYearList(filteredYearData as FacilityWaterQltyProps[]);
setChongInWaterQltyYearList(filteredYearData as FacilityWaterQltyProps[]);
}, [facilityWaterQltyYearList]);
const yesterDay = new Date();
yesterDay.setDate(yesterDay.getDate() - 1);
const yesterDayStr = yesterDay.toISOString().split('T')[0];
const ref = useRef<ReturnType<FacilityWaterQltyProps>>({
startDate: new Date(),
endDate: new Date(),
tabName: '',
filteredList: [],
});
const filterData = useCallback((returnType: ReturnType<FacilityWaterQltyProps>) => {
const filteredData = setFilterAfterData(returnType.filteredList);
setCopyYearList(filteredData as FacilityWaterQltyProps[]);
}, []);
const excelDownload = () => {
const excelDownList: modalExcelType[] = [];
if(copyYearList.length > 0){
copyYearList.forEach((item) => {
excelDownList.push({
날짜: item.msrDt,
"유입BOD(mg/L)": item.inflAssctAftWqltBod,
"유입TOC(COD)(mg/L)": item.inflAssctAftWqltToc,
"유입SS(mg/L)": item.inflAssctAftWqltSs,
"유입TN(mg/L)": item.inflAssctAftWqltTn,
"유입TP(mg/L)": item.inflAssctBfrWqltTp,
"유출BOD(mg/L)": item.efflWqltBod,
"유출TOC(COD)(mg/L)": item.efflWqltCod,
"유출SS(mg/L)": item.efflWqltSs,
"유출TN(mg/L)": item.efflWqltTn,
"유출TP(mg/L)": item.efflWqltTp,
"강우량(일)": item.infoRnDay?item.infoRnDay.toString():"0.0",
});
});
}
if (excelDownList && excelDownList.length > 0) {
downloadExcel({ data: JSON.parse(JSON.stringify(excelDownList)), fileName: searchYear+'년 '+fcltyName + ' 수질현황', sheetNameArray: [fcltyName + ' 수질현황'], headerRange: 'A1:L1' });
} else if (prevExcelListRef.current && prevExcelListRef.current.length > 0) {
alert('다운받을 데이터가 없습니다.');
}
prevExcelListRef.current = excelDownList;
}
return (
<>
<UITab tabList={tabList1} defaultValue="tab_num1">
<Button className={btnClass} style={{float: 'right'}} color="green" onClick={excelDownload}>
{"엑셀다운로드"}
</Button>
{/* 처리시설 수질현황 */}
<TabsContent value="tab_num1">
<UITab tabList={tabList2} defaultValue="tab_term_year">
<TabsContent value="tab_term_year">
<FacilityTermModalHeader<FacilityWaterQltyProps>
searchList={facilityWaterQltyYearList}
fcltyCd={facilityCd}
fcltyName={fcltyName}
tabName={"tab_term_year"}
searchYear={searchYear}
ref={ref}
queryClick={filterData}
/>
<div className="overflow-y-auto h-[350px]">
<UiTable
headName={undefined}
tableData={copyYearList}
headlist={upHeadListModalYear}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => {}}
className="h-[310px]"
boldArray={[0,1,2]}
>
<HeadMakeColSpan upHeadList={upHeadListModalYear} layoutNum={3} />
</UiTable>
</div>
<div className="card-header">
<h3 className="text-2xl font-medium text-label">수질 TMS</h3>
<h3 className="text-2xl font-medium text-label text-right">({msrmtDate?msrmtDate.substring(0,4)+'-'+msrmtDate.substring(4,6)+'-'+msrmtDate.substring(6,8):yesterDayStr})</h3>
</div>
<div className="overflow-y-auto h-[250px]">
<UiTable
headName={undefined}
tableData={tmsList}
headlist={upHeadListModalTms}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => {}}
boldArray={[0,1,2]}
>
<HeadMakeColSpan upHeadList={upHeadListModalTms} layoutNum={4} />
</UiTable>
{msrmtDate != yesterDayStr?.replaceAll('-','')?<h3 className="text-2xl text-left text-gray-500">※전날 데이터 결측으로 인해 가장 최근 데이터 표출함</h3> : ''}
</div>
</TabsContent>
<TabsContent value="tab_term_summer">
<FacilityTermModalHeader<FacilityWaterQltyProps>
searchList={facilityWaterQltyYearList}
fcltyCd={facilityCd}
fcltyName={fcltyName}
tabName="tab_term_summer"
searchYear={searchYear}
ref={ref}
queryClick={filterData}
/>
<div className="overflow-y-auto h-[550px]">
<UiTable
headName={undefined}
tableData={copyYearList}
headlist={upHeadListModalYear}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => { } }
className="h-[500px]"
boldArray={[0,1,2]}
>
<HeadMakeColSpan upHeadList={upHeadListModalYear} layoutNum={3} />
</UiTable>
</div>
</TabsContent>
<TabsContent value="tab_term_winter">
<FacilityTermModalHeader<FacilityWaterQltyProps>
searchList={facilityWaterQltyYearList}
fcltyCd={facilityCd}
fcltyName={fcltyName}
tabName="tab_term_winter"
searchYear={searchYear}
ref={ref}
queryClick={filterData}
/>
<div className="overflow-y-auto h-[550px]">
<UiTable
headName={undefined}
tableData={copyYearList}
headlist={upHeadListModalYear}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => {}}
className="h-[500px]"
boldArray={[0,1,2]}
>
<HeadMakeColSpan upHeadList={upHeadListModalYear} layoutNum={3} />
</UiTable>
</div>
</TabsContent>
<TabsContent value="tab_term_chuncheon">
<FacilityTermModalHeader<FacilityWaterQltyProps>
searchList={facilityWaterQltyYearList}
fcltyCd={facilityCd}
fcltyName={fcltyName}
tabName="tab_term_chuncheon"
searchYear={searchYear}
ref={ref}
queryClick={filterData}
/>
<div className="overflow-y-auto h-[550px]">
<UiTable
headName={undefined}
tableData={copyYearList}
headlist={upHeadListModalYear}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => {}}
className="h-[500px]"
boldArray={[0,1,2]}
>
<HeadMakeColSpan upHeadList={upHeadListModalYear} layoutNum={3} />
</UiTable>
</div>
</TabsContent>
<TabsContent value="tab_term_rain">
<FacilityTermModalHeader<FacilityWaterQltyProps>
searchList={facilityWaterQltyYearList}
fcltyCd={facilityCd}
fcltyName={fcltyName}
tabName="tab_term_rain"
searchYear={searchYear}
ref={ref}
queryClick={filterData}
/>
<div className="overflow-y-auto h-[550px]">
<UiTable
headName={undefined}
tableData={copyYearList}
headlist={upHeadListModalYear}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => {}}
className="h-[500px]"
boldArray={[0,1,2]}
>
<HeadMakeColSpan upHeadList={upHeadListModalYear} layoutNum={3} />
</UiTable>
</div>
</TabsContent>
</UITab>
</TabsContent>
{/* 총인설비 수질현황 */}
<TabsContent value="tab_num2">
<UITab tabList={tabList3} defaultValue="tab_term_year_chongin">
<TabsContent value="tab_term_year_chongin">
<FacilityTermModalHeader<FacilityWaterQltyProps>
searchList={chongInWaterQltyYearList}
fcltyCd={facilityCd}
fcltyName={fcltyName}
tabName="tab_term_year"
searchYear={searchYear}
ref={ref}
queryClick={filterData}
/>
<div className="overflow-y-auto h-[550px]">
<UiTable
headName={undefined}
tableData={copyYearList}
headlist={upHeadListModalChongIn}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => {}}
className="h-[500px]"
boldArray={[0,1,2]}
>
<HeadMakeColSpan upHeadList={upHeadListModalChongIn} layoutNum={5} />
</UiTable>
</div>
</TabsContent>
<TabsContent value="tab_term_summer_chongin">
<FacilityTermModalHeader<FacilityWaterQltyProps>
searchList={chongInWaterQltyYearList}
fcltyCd={facilityCd}
fcltyName={fcltyName}
tabName="tab_term_summer"
searchYear={searchYear}
ref={ref}
queryClick={filterData}
/>
<div className="overflow-y-auto h-[550px]">
<UiTable
headName={undefined}
tableData={copyYearList}
headlist={upHeadListModalChongIn}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => {}}
className="h-[500px]"
boldArray={[0,1,2]}
>
<HeadMakeColSpan upHeadList={upHeadListModalChongIn} layoutNum={5} />
</UiTable>
</div>
</TabsContent>
<TabsContent value="tab_term_winter_chongin">
<FacilityTermModalHeader<FacilityWaterQltyProps>
searchList={chongInWaterQltyYearList}
fcltyCd={facilityCd}
fcltyName={fcltyName}
tabName="tab_term_winter"
searchYear={searchYear}
ref={ref}
queryClick={filterData}
/>
<div className="overflow-y-auto h-[550px]">
<UiTable
headName={undefined}
tableData={copyYearList}
headlist={upHeadListModalChongIn}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => {}}
className="h-[500px]"
boldArray={[0,1,2]}
>
<HeadMakeColSpan upHeadList={upHeadListModalChongIn} layoutNum={5} />
</UiTable>
</div>
</TabsContent>
</UITab>
</TabsContent>
</UITab>
</>
);
}
@@ -0,0 +1,208 @@
"use client"
import { useFoStore } from "@/app/store";
import type { TableUpperProps } from "@/app/store/common";
import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components";
import ListDiv from "@common/business_components/ui/list_div";
import { useCallback, useEffect, useRef, useState } from "react";
import WaterQualityModal from "./modal/page";
import type { FacilityWaterQltyProps } from "@/app/store/facilityWaterQlty";
export default function ProcessFacilitySearch() {
const { Common
, FacilityWaterQltyMain
, initializeCommonData
, searchFacilityWaterQltyList
} = useFoStore((state) => state) || { FacilityWaterQltyList: { facilityWaterQltyList: [], facilityWaterQltyCount: 0 } };
const {
isInit,
selectUpdownData,
selectOperationData,
selectSidoData,
selectSigunData,
selectRegionData,
selectUnitWaterShedData,
selectMiddleRegionData,
selectEnvPartData,
selectSearchYear,
btnClass
} = Common;
const {
mainLabelArray,
upHeadList1,
upHeadList2,
upHeadList3,
facilityWaterQltyList,
facilityWaterQltyCount,
excelList
} = FacilityWaterQltyMain;
const mainList: FacilityWaterQltyProps[] = facilityWaterQltyList || [];
// 그리드 관련
const [resetPage, setResetPage] = useState<boolean>(false);
const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null);
const [widthSize, setWidthSize] = useState<string>("w-[120%]");
const [upheadList, setUpheadList] = useState<TableUpperProps[]>(upHeadList1);
const [fcltyName, setFcltyName] = useState<string>("");
const [operationValue, setOperationValue] = useState<string>("0");
const [searchYearValue, setSearchYearValue] = useState<string>(selectSearchYear[0]?.val || "0");
const [fcltCpct, setFcltCpct] = useState<string>("0");
// 모달 상태 추가
const [selectedRow, setSelectedRow] = useState<number | null>(null);
const [fcltyNm, setFcltyNm] = useState<string>("");
const [fcltyCd, setFcltyCd] = useState<string>("");
const [modalYear, setModalYear] = useState<string>("");
const searchFcltyQltyList = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => {
let nameCd, partStr;
switch (partValue) {
case 'R':
if (regionValue !== '0') {
if(unitWaterShedValue==='0') nameCd = regionValue;
else if (middleRegionValue==='0') nameCd = unitWaterShedValue;
else nameCd = middleRegionValue;
}
partStr = partValue
break;
default:
if(sidoValue !== '0') nameCd = sidoValue;
if(sigunValue !== '0') nameCd = sigunValue;
partStr = envPartValue !== '0' ? envPartValue:partValue;
break;
}
if (partStr.length > 1 || fcltCpct !== '0' || operationValue!== '0' ||nameCd ||(fcltyName.trim() !== "" && fcltyName.trim() !== null)) {
searchFacilityWaterQltyList(pageNo, pageCnt, searchYearValue, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined);
} else {
searchFacilityWaterQltyList(pageNo, pageCnt, searchYearValue);
}
}, [operationValue, fcltyName, searchFacilityWaterQltyList, searchYearValue, fcltCpct]);
const resetVal = () => {
setWidthSize("w-[120%]");
setUpheadList(upHeadList1)
// 상태값 설정
setFcltCpct(selectUpdownData[0]?.val || "0");
setOperationValue(selectOperationData[0]?.val || "0");
setSearchYearValue(selectSearchYear[0]?.val || "0");
// SearchInputSelect 초기화
setFcltyName("");
if (searchInputRef.current) {
const input = searchInputRef.current.querySelector('input');
if (input) {
input.value = "";
}
}
searchFacilityWaterQltyList(1, 10, selectSearchYear[0]?.val || "0");
}
// 셀 클릭 시 모달 표시
const cellClick = (index: number) => {
const facility = facilityWaterQltyList[index];
if (facility) {
setFcltyNm(facility.bscFctNm);
setFcltyCd(facility.bscFctCd);
setModalYear(searchYearValue);
setSelectedRow(index); // 클릭한 행의 index 저장
}
}
// 모달 닫기
const closeModal = () => {
setSelectedRow(null);
}
const ref = useRef<ReturnType>({
partValue: '',
sidoValue: '',
sigunValue: '',
regionValue: '',
unitWaterShedValue: '',
middleRegionValue: '',
envPartValue: '',
sidoText: '',
sigunText: ''
});
useEffect(() => {
if (!isInit) {
initializeCommonData();
searchFacilityWaterQltyList(1, 10, selectSearchYear[0]?.val || "0");
}
}, [isInit, initializeCommonData, searchFacilityWaterQltyList, selectSearchYear]);
useEffect(() => {
if (excelList && excelList.length > 0) {
downloadExcel({
data: excelList as unknown as JSON[],
fileName: '시설별 수질현황',
sheetNameArray: ['시설별 수질현황'],
headerRange: 'A1:T1'
});
}
}, [excelList]);
const chkVal = useCallback((returntype:ReturnType) => {
setWidthSize(returntype.partValue=='A' ?"w-[120%]":"w-[130%]")
setUpheadList(returntype.partValue == 'R' ? upHeadList2 : returntype.partValue == 'G'?upHeadList3:upHeadList1);
setResetPage(true);
}, [upHeadList1, upHeadList2, upHeadList3]);
const excelDownload = useCallback(() => {
searchFcltyQltyList(ref.current, 1, 10000);
}, [searchFcltyQltyList]);
const handlePageChange = useCallback((page: number) => {
setResetPage(false);
searchFcltyQltyList(ref.current, page, 10);
}, [searchFcltyQltyList]);
const total = facilityWaterQltyCount || 1;
console.log('total : ', total);
return (
<MainDiv>
<Title title="시설별 수질현황" />
<ContentsDiv>
<SearchBaseForm
leftChildren={<>
<SelectBox className="col-span-3" label={mainLabelArray[3] as string} selectArray={selectUpdownData} onSelectValue={(val) => setFcltCpct(val.toString())} value={fcltCpct} selectClass={undefined} />
<SearchInput className="col-span-3" label={mainLabelArray[4] as string} textValue={fcltyName} setText={setFcltyName} />
<SelectBox className="col-span-3" label={mainLabelArray[5] as string} selectArray={selectSearchYear} onSelectValue={(val) => setSearchYearValue(val.toString())} value={searchYearValue} selectClass={undefined} />
<SelectBox className="col-span-3" label={mainLabelArray[6] as string} selectArray={selectOperationData} onSelectValue={(val) => setOperationValue(val.toString())} value={operationValue} selectClass={undefined} />
</>}
rightChildren={undefined}
selectSidoData={selectSidoData}
selectSigunData={selectSigunData}
selectRegionData={selectRegionData}
selectUnitWaterShedData={selectUnitWaterShedData}
selectMiddleRegionData={selectMiddleRegionData}
selectEnvPartData={selectEnvPartData}
btnClass={btnClass}
queryClick={chkVal}
excelClick={excelDownload}
initClick={resetVal}
ref={ref} isAdmin={false} />
<ListDiv>
<div className="h-full w-full overflow-x-scroll">
<div className={widthSize}>
<UiTable cellClick={(index: number | undefined) => index !== undefined && cellClick(index)} headName={"시설별 수질현황"} tableData={mainList} headlist={upheadList} pageSize={10} total={total<10?facilityWaterQltyList.length:total} resetPageNo={resetPage} onPageChange={handlePageChange} />
</div>
</div>
</ListDiv>
</ContentsDiv>
{selectedRow !== null && (
<UiModal
body={<WaterQualityModal fcltyName={fcltyNm} facilityCd={fcltyCd} searchYear={modalYear}></WaterQualityModal>}
bodyClass="sm:max-w-[90vw] w-[1200px] h-[920px]"
open={selectedRow !== null}
onOpenChange={(open) => !open && closeModal()}
noHeaderYn="Y"
noFooterYn="Y"
/>
)}
</MainDiv>
);
}
+310
View File
@@ -0,0 +1,310 @@
"use client"
import FacilityTermModalHeader from "@/app/components/common/FacilityTermModalHeader";
import { useFoStore } from "@/app/store";
import type { FlowRateModalInterface, modalExcelType } from "@/app/store/flowRate";
import { downloadExcel, UITab, UiTable } from "@common/business_components/ui";
import { Button, TabsContent } from "@common/components/ui";
import { useCallback, useEffect, useRef, useState } from "react";
import type { ReturnType } from "../../../components/common/FacilityTermModalHeader";
const tabList = [
{ value: 'tab_term_year', label: '연간' },
{ value: 'tab_term_summer', label: '하절기' },
{ value: 'tab_term_winter', label: '동절기' },
{ value: 'tab_term_chuncheon', label: '청천시' },
{ value: 'tab_term_rain', label: '강우시' }
];
export default function FlowRateModal(props: { fcltyName: string; facilityCd: string; searchYear: string }) {
// 그리드 관련
const { Common, FlowRateModal, searchFlowRateModalList } = useFoStore((state) => state);
const { btnClass } = Common;
const [copyYearList, setCopyYearList] = useState<FlowRateModalInterface[]>([]);
const [isLoading, setIsLoading] = useState(true);
const prevExcelListRef = useRef<modalExcelType[] | null>(null);
const {
modalFlowRateListYear,
upHeadListFlowRateModalYear,
} = FlowRateModal;
const ref = useRef<ReturnType<FlowRateModalInterface>>({
startDate: new Date(),
endDate: new Date(),
tabName: '',
filteredList: [],
});
const filterData = useCallback((returnType: ReturnType<FlowRateModalInterface>) => {
if (!returnType.filteredList) return;
// 타입을 FlowRateModalInterface[]로 강제 변환하여 타입 오류 해결
const filteredData = setFilterAfterData(returnType.filteredList as FlowRateModalInterface[]);
setCopyYearList(filteredData);
}, []);
// 필터링 된 후 최대, 최소, 평균 값 계산
const setFilterAfterData = (filteredData: FlowRateModalInterface[]) => {
if (!filteredData || filteredData.length === 0) return [];
// 쉼표가 포함된 문자열을 숫자로 변환하는 함수
const parseNumber = (value: string | number | null | undefined): number => {
if (value === null || value === undefined || value === '') return 0;
if (typeof value === 'number') return value;
return Number(value.replace(/,/g, ''));
};
let maxtotInflAmnt = 0;
let maxinflAmnt = 0;
let maxsumFlow = 0;
let maxreturnAmnt = 0;
let maxefflAmnt = 0;
let maxinfoRnDay = 0;
// 초기값 설정
let mintotInflAmnt = parseNumber(filteredData[0]?.totInflAmnt);
let mininflAmnt = parseNumber(filteredData[0]?.inflAmnt);
let minsumFlow = parseNumber(filteredData[0]?.sumFlow);
let minreturnAmnt = parseNumber(filteredData[0]?.returnAmnt);
let minefflAmnt = parseNumber(filteredData[0]?.efflAmnt);
let mininfoRnDay = parseNumber(filteredData[0]?.infoRnDay);
let avgtotInflAmnt = 0;
let avginflAmnt = 0;
let avgsumFlow = 0;
let avgreturnAmnt = 0;
let avgefflAmnt = 0;
let avginfoRnDay = 0;
filteredData.forEach((item: FlowRateModalInterface) => {
const totInflAmnt = parseNumber(item.totInflAmnt);
const inflAmnt = parseNumber(item.inflAmnt);
const sumFlow = parseNumber(item.sumFlow);
const returnAmnt = parseNumber(item.returnAmnt);
const efflAmnt = parseNumber(item.efflAmnt);
const infoRnDay = parseNumber(item.infoRnDay);
maxtotInflAmnt = Math.max(maxtotInflAmnt, totInflAmnt);
maxinflAmnt = Math.max(maxinflAmnt, inflAmnt);
maxsumFlow = Math.max(maxsumFlow, sumFlow);
maxreturnAmnt = Math.max(maxreturnAmnt, returnAmnt);
maxefflAmnt = Math.max(maxefflAmnt, efflAmnt);
maxinfoRnDay = Math.max(maxinfoRnDay, infoRnDay);
mintotInflAmnt = Math.min(mintotInflAmnt, totInflAmnt);
mininflAmnt = Math.min(mininflAmnt, inflAmnt);
minsumFlow = Math.min(minsumFlow, sumFlow);
minreturnAmnt = Math.min(minreturnAmnt, returnAmnt);
minefflAmnt = Math.min(minefflAmnt, efflAmnt);
mininfoRnDay = Math.min(mininfoRnDay, infoRnDay);
avgtotInflAmnt += totInflAmnt;
avginflAmnt += inflAmnt;
avgsumFlow += sumFlow;
avgreturnAmnt += returnAmnt;
avgefflAmnt += efflAmnt;
avginfoRnDay += infoRnDay;
});
const newFilteredData = [
{
msrDt: '평균',
totInflAmnt: Math.round(avgtotInflAmnt / filteredData.length * 10) / 10,
inflAmnt: Math.round(avginflAmnt / filteredData.length * 10) / 10,
sumFlow: Math.round(avgsumFlow / filteredData.length * 10) / 10,
returnAmnt: Math.round(avgreturnAmnt / filteredData.length * 10) / 10,
efflAmnt: Math.round(avgefflAmnt / filteredData.length * 10) / 10,
infoRnDay: avginfoRnDay > 0 ? Math.round(avginfoRnDay / filteredData.length * 10) / 10 : 0,
rainEffectDay: 0,
},
{
msrDt: '최대',
totInflAmnt: Math.round(maxtotInflAmnt * 10) / 10,
inflAmnt: Math.round(maxinflAmnt * 10) / 10,
sumFlow: Math.round(maxsumFlow * 10) / 10,
returnAmnt: Math.round(maxreturnAmnt * 10) / 10,
efflAmnt: Math.round(maxefflAmnt * 10) / 10,
infoRnDay: maxinfoRnDay > 0 ? (Math.round(maxinfoRnDay * 10) / 10).toFixed(1) : '0.0',
rainEffectDay: 0,
},
{
msrDt: '최소',
totInflAmnt: Math.round(mintotInflAmnt * 10) / 10,
inflAmnt: Math.round(mininflAmnt * 10) / 10,
sumFlow: Math.round(minsumFlow * 10) / 10,
returnAmnt: Math.round(minreturnAmnt * 10) / 10,
efflAmnt: Math.round(minefflAmnt * 10) / 10,
infoRnDay: mininfoRnDay > 0 ? (Math.round(mininfoRnDay * 10) / 10).toFixed(1) : '0.0',
rainEffectDay: 0,
},
...filteredData
];
return newFilteredData;
}
useEffect(() => {
const fetchData = async () => {
setIsLoading(true);
try {
await searchFlowRateModalList(props.facilityCd, props.searchYear);
} finally {
setIsLoading(false);
}
};
fetchData();
}, [props.facilityCd, props.searchYear, searchFlowRateModalList]);
if (isLoading) {
return <div>로딩 중...</div>;
}
const excelDownload = () => {
const excelDownList: modalExcelType[] = [];
if(copyYearList.length > 0){
copyYearList.forEach((item) => {
excelDownList.push({
"날짜": item.msrDt,
"총유입량(m³/일)": item.totInflAmnt,
"하수유입량(m³/일)": item.inflAmnt,
"연계처리량(m³/일)": item.sumFlow,
"총인반류수량(m³/일)": item.returnAmnt,
"방류량(m³/일)": item.efflAmnt,
"강우량": item.infoRnDay,
});
});
}
if (excelDownList && excelDownList.length > 0) {
downloadExcel({ data: JSON.parse(JSON.stringify(excelDownList)), fileName: props.searchYear+'년 '+props.fcltyName + ' 유량현황', sheetNameArray: [props.fcltyName + ' 유량현황'], headerRange: 'A1:G1' });
} else if (prevExcelListRef.current && prevExcelListRef.current.length > 0) {
alert('다운받을 데이터가 없습니다.');
}
prevExcelListRef.current = excelDownList;
}
return (
<>
<UITab tabList={tabList} defaultValue="tab_term_year">
<Button className={btnClass} style={{float: 'right'}} color="green" onClick={excelDownload}>
{"엑셀다운로드"}
</Button>
<TabsContent value="tab_term_year">
<FacilityTermModalHeader<FlowRateModalInterface>
searchList={modalFlowRateListYear}
fcltyCd={props.facilityCd}
fcltyName={props.fcltyName}
tabName={"tab_term_year"}
searchYear={props.searchYear}
ref={ref}
queryClick={filterData}
/>
<div className="h-[370px] overflow-y-auto overflow-x-auto">
<div className="w-[120%]">
<UiTable
headName="유량현황"
tableData={copyYearList}
headlist={upHeadListFlowRateModalYear}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => {}}
className="h-[370px]"
boldArray={[0,1,2]}
/>
</div>
</div>
</TabsContent>
<TabsContent value="tab_term_summer">
<FacilityTermModalHeader<FlowRateModalInterface>
searchList={modalFlowRateListYear}
fcltyCd={props.facilityCd}
fcltyName={props.fcltyName}
tabName={"tab_term_summer"}
searchYear={props.searchYear}
ref={ref}
queryClick={filterData}
/>
<div className="h-[370px] overflow-y-auto overflow-x-auto">
<div className="w-[120%]">
<UiTable
headName="유량현황"
tableData={copyYearList}
headlist={upHeadListFlowRateModalYear}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => {}}
className="h-[370px]"
boldArray={[0,1,2]}
/>
</div>
</div>
</TabsContent>
<TabsContent value="tab_term_winter">
<FacilityTermModalHeader<FlowRateModalInterface>
searchList={modalFlowRateListYear}
fcltyCd={props.facilityCd}
fcltyName={props.fcltyName}
tabName={"tab_term_winter"}
searchYear={props.searchYear}
ref={ref}
queryClick={filterData}
/>
<div className="h-[370px] overflow-y-auto overflow-x-auto">
<div className="w-[120%]">
<UiTable
headName="유량현황"
tableData={copyYearList}
headlist={upHeadListFlowRateModalYear}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => {}}
className="h-[370px]"
boldArray={[0,1,2]}
/>
</div>
</div>
</TabsContent>
<TabsContent value="tab_term_chuncheon">
<FacilityTermModalHeader<FlowRateModalInterface>
searchList={modalFlowRateListYear}
fcltyCd={props.facilityCd}
fcltyName={props.fcltyName}
tabName={"tab_term_chuncheon"}
searchYear={props.searchYear}
ref={ref}
queryClick={filterData}
/>
<div className="h-[370px] overflow-y-auto overflow-x-auto">
<div className="w-[120%]">
<UiTable
headName="유량현황"
tableData={copyYearList}
headlist={upHeadListFlowRateModalYear}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => {}}
className="h-[370px]"
boldArray={[0,1,2]}
/>
</div>
</div>
</TabsContent>
<TabsContent value="tab_term_rain">
<FacilityTermModalHeader<FlowRateModalInterface>
searchList={modalFlowRateListYear}
fcltyCd={props.facilityCd}
fcltyName={props.fcltyName}
tabName={"tab_term_rain"}
searchYear={props.searchYear}
ref={ref}
queryClick={filterData}
/>
<div className="h-[370px] overflow-y-auto overflow-x-auto">
<div className="w-[120%]">
<UiTable headName="유량현황" tableData={copyYearList} headlist={upHeadListFlowRateModalYear} pageSize={0} total={0} cellClick={undefined} onPageChange={() => {}} className="h-[370px]" boldArray={[0,1,2]}/>
</div>
</div>
</TabsContent>
</UITab>
</>
);
}
+207
View File
@@ -0,0 +1,207 @@
"use client"
import { useFoStore } from "@/app/store";
import type { TableUpperProps } from "@/app/store/common";
import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components";
import ListDiv from "@common/business_components/ui/list_div";
import { useCallback, useEffect, useRef, useState } from "react";
import FlowRateModal from "./modal/page";
import type { ExcelType } from "@/app/store/flowRate";
export default function ProcessFacilitySearch() {
const {Common
,FlowRateSearch
,FlowRateList
,initializeCommonData
,searchFlowRateList
} = useFoStore((state) => state);
const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null);
const {
isInit,
selectUpdownData,
selectSearchYear,
selectOperationData,
selectSidoData,
selectSigunData,
selectRegionData,
selectUnitWaterShedData,
selectMiddleRegionData,
selectEnvPartData,
btnClass
} = Common;
const {
flowRateSearchLabelArray
} = FlowRateSearch;
// 그리드 관련
const {
upHeadList1,
upHeadList2,
upHeadList3,
flowRateList,
flowRateCount,
excelList
} = FlowRateList;
// 입력값 상태 관리
const [fcltCpct, setFcltCpct] = useState<string>("0");
const [fcltyName, setFcltyName] = useState<string>("");
const [operationValue, setOperationValue] = useState<string>("0");
const [searchYearValue, setSearchYearValue] = useState<string>(selectSearchYear[0]?.val || "0");
const [widthSize, setWidthSize] = useState<string>("w-[120%]");
const [upheadList, setUpheadList] = useState<TableUpperProps[]>(upHeadList1);
const [resetPage, setResetPage] = useState<boolean>(false);
const prevExcelListRef = useRef<ExcelType[] | null>(null);
// 모달 상태 추가
const [selectedRow, setSelectedRow] = useState<number | null>(null);
const [fcltyNm, setFcltyNm] = useState<string>("");
const [fcltyCd, setFcltyCd] = useState<string>("");
const searchFlowRate = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => {
let nameCd, partStr
switch (partValue) {
case 'R':
if (regionValue !== '0') {
if(unitWaterShedValue==='0') nameCd = regionValue;
else if (middleRegionValue==='0') nameCd = unitWaterShedValue;
else nameCd = middleRegionValue;
}
partStr = partValue
break;
default:
if(sidoValue !== '0') nameCd = sidoValue;
if(sigunValue !== '0') nameCd = sigunValue;
partStr = envPartValue !== '0' ? envPartValue:partValue;
break;
}
if (partStr.length > 1 || fcltCpct !== '0' || operationValue!== '0' ||nameCd ||(fcltyName.trim() !== "" && fcltyName.trim() !== null)) {
searchFlowRateList(pageNo, pageCnt, searchYearValue, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined);
} else {
searchFlowRateList(pageNo, pageCnt, searchYearValue);
}
}, [operationValue, fcltyName, searchFlowRateList, searchYearValue, fcltCpct]);
const resetVal = () => {
setWidthSize("w-[120%]");
setUpheadList(upHeadList1)
// 상태값 설정
setFcltCpct(selectUpdownData[0]?.val || "0");
setOperationValue(selectOperationData[0]?.val || "0");
setSearchYearValue(selectSearchYear[0]?.val || "0");
// SearchInputSelect 초기화
setFcltyName("");
if (searchInputRef.current) {
const input = searchInputRef.current.querySelector('input');
if (input) {
input.value = "";
}
}
searchFlowRateList(1, 10, selectSearchYear[0]?.val || "0");
}
// 셀 클릭 시 모달 표시
const cellClick = (index: number) => {
if (flowRateList[index]) {
setFcltyNm(flowRateList[index].bscFctNm);
setFcltyCd(flowRateList[index].bscFctCd);
setSelectedRow(index); // 클릭한 행의 index 저장
}
};
// 페이지 네비게이션션
const handlePageChange = useCallback((page: number) => {
setResetPage(false);
searchFlowRate(ref.current, page, 10);
}, [searchFlowRate]);
// 모달 닫기
const closeModal = () => {
setSelectedRow(null);
};
// 시도 데이터 초기화
useEffect(() => {
if(!isInit) {
initializeCommonData();
searchFlowRateList(1, 10, selectSearchYear[0]?.val || "0");
}
}, [isInit, initializeCommonData, searchFlowRateList, selectSearchYear]);
useEffect(() => {
if (excelList && excelList.length > 0) {
downloadExcel({ data: JSON.parse(JSON.stringify(excelList)), fileName: '시설별 유량현황', sheetNameArray: ['시설별 유량현황'], headerRange: 'A1:O1' });
} else if (prevExcelListRef.current && prevExcelListRef.current.length > 0) {
alert('다운받을 데이터가 없습니다.');
}
prevExcelListRef.current = excelList;
}, [excelList]);
const ref = useRef<ReturnType>({
partValue: '',
sidoValue: '',
sigunValue: '',
regionValue: '',
unitWaterShedValue: '',
middleRegionValue: '',
envPartValue: '',
sidoText: '',
sigunText: ''
});
const chkVal = useCallback((returntype:ReturnType) => {
setWidthSize(returntype.partValue=='A' ?"w-[120%]":"w-[130%]")
setUpheadList(returntype.partValue == 'R' ? upHeadList2 : returntype.partValue == 'G'?upHeadList3:upHeadList1);
setResetPage(true);
}, [upHeadList1, upHeadList2, upHeadList3]);
const excelDownload = useCallback(() => {
searchFlowRate(ref.current, 1, 10000);
}, [searchFlowRate]);
const total = flowRateCount;
return (
<MainDiv>
<Title title="시설별 유량현황" />
<ContentsDiv>
<SearchBaseForm
leftChildren={<>
<SelectBox className="col-span-3" label={flowRateSearchLabelArray[3] as string} selectArray={selectUpdownData} onSelectValue={(val) => setFcltCpct(val.toString())} value={fcltCpct} selectClass={undefined} />
<SearchInput className="col-span-3" label={flowRateSearchLabelArray[4] as string} textValue={fcltyName} setText={setFcltyName} />
<SelectBox className="col-span-3" label={flowRateSearchLabelArray[5] as string} selectArray={selectSearchYear} onSelectValue={(val) => setSearchYearValue(val.toString())} value={searchYearValue} selectClass={undefined} />
<SelectBox className="col-span-3" label={flowRateSearchLabelArray[6] as string} selectArray={selectOperationData} onSelectValue={(val) => setOperationValue(val.toString())} value={operationValue} selectClass={undefined} />
</>}
rightChildren={undefined}
selectSidoData={selectSidoData}
selectSigunData={selectSigunData}
selectRegionData={selectRegionData}
selectUnitWaterShedData={selectUnitWaterShedData}
selectMiddleRegionData={selectMiddleRegionData}
selectEnvPartData={selectEnvPartData}
btnClass={btnClass}
queryClick={chkVal}
excelClick={excelDownload}
initClick={resetVal}
ref={ref} isAdmin={false} />
<ListDiv>
<div className="h-full w-full overflow-x-scroll">
<div className={widthSize}>
<UiTable cellClick={(index: number | undefined) => index !== undefined && cellClick(index)} headName={"시설별 유량현황"} tableData={flowRateList} headlist={upheadList} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} />
</div>
</div>
</ListDiv>
</ContentsDiv>
{selectedRow !== null && (
<UiModal
body={<FlowRateModal fcltyName={fcltyNm} facilityCd={fcltyCd} searchYear={searchYearValue}></FlowRateModal>}
bodyClass="sm:max-w-[90vw] w-[1050px] h-[700px]"
open={selectedRow !== null}
onOpenChange={(open: boolean) => !open && closeModal()}
/>
)}
</MainDiv>
);
}
+145
View File
@@ -0,0 +1,145 @@
"use client";
import Gis from "@common/business_components/ui/map/Gis";
import GisApp from "@common/business_components/ui/map/lib/gis.core";
import { useEffect, useRef, useState } from "react";
interface FeatureData {
fid?: string;
ftrIdn?: string;
id?: string;
getGeometry?: () => { getExtent: () => number[] };
setStyle?: () => void;
}
interface MapInstance {
getLayers: () => { getArray: () => Array<{ getSource?: () => { getFeatureById?: (id: string) => FeatureData | null } }> };
getView: () => { fit: (extent: number[], options: object) => void };
}
const Map = () => {
const [treeData, setTreeData] = useState([]); // 초기값을 객체 또는 null로 설정
const gisMaps = useRef<MapInstance | null>(null);
const [isOpen, setIsOpen] = useState(false); // 팝업 열기/닫기 상태
const [isSearchModalOpen, setIsSearchModalOpen] = useState(false); // SearchModal 열림 상태
const [featureData, setFeatureData] = useState<FeatureData | null>(null); // 선택된 피쳐 데이터
const fetchData = async () => {
try {
const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}/v1/map`
const reqUrl = `${API_URL}/lyrStngs/FO_NETWORK`;
const response = await fetch(reqUrl, {
method: "GET",
headers: {
accept: "application/json",
}
});
if (!response.ok) {
throw new Error(`HTTP 오류! 상태: ${response.status}`);
}
const data = await response.json(); // 여기서 json 파싱
setTreeData(data); // 데이터 설정
} catch (error) {
console.error("데이터 불러오기 실패:", error);
}
};
// 컴포넌트 마운트 시 1회 실행
useEffect(() => {
fetchData();
}, []);
// SearchModal, GisContent 등에서 호출
const onFeatureSelect = (data: FeatureData) => {
const map = gisMaps.current;
if (!map) {
console.warn("지도가 초기화되지 않았습니다.");
return;
}
// 항상 같은 select 객체 사용
const select = (GisApp.Module.select as unknown) as { resetSelectedFeature: () => void; selectFeature: (feature: FeatureData, selected: boolean) => void } | undefined;
if (!select) return;
// 1. 기존 선택 초기화
select.resetSelectedFeature();
// 2. data가 OpenLayers Feature 인스턴스인지 확인
let foundFeature: FeatureData | null = null;
if (
data &&
typeof data.getGeometry === "function" &&
typeof data.setStyle === "function"
) {
// 이미 Feature 인스턴스라면 그대로 사용
foundFeature = data;
} else {
// 아니면 featureId로 맵에서 Feature 인스턴스를 찾아서 사용
const featureId = data.fid || data.ftrIdn || data.id;
map
.getLayers()
.getArray()
.forEach((layer) => {
const source = layer.getSource?.();
if (source?.getFeatureById) {
const feature = source.getFeatureById(String(featureId).trim());
if (feature) foundFeature = feature;
}
});
}
// 3. 피처 선택
if (foundFeature && foundFeature.getGeometry) {
const geometry = foundFeature.getGeometry();
if (geometry) {
const extent = geometry.getExtent();
map.getView().fit(extent, {
duration: 300,
padding: [50, 50, 50, 50],
maxZoom: 16,
constrainResolution: true,
nearest: true,
});
}
select.selectFeature(foundFeature, true);
} else {
console.warn("OpenLayers Feature를 찾을 수 없습니다:", data);
}
setFeatureData(data);
setIsOpen(true);
setIsSearchModalOpen(false);
};
// onCheckedLayersChange handler
const handleCheckedLayersChange = (checkedIds: string[], checkedNodesData: unknown[]) => {
// Handle checked layers change if needed
console.log('Checked layers changed:', checkedIds, checkedNodesData);
};
return (
<div className="bg-container">
<Gis
treeData={treeData}
isSearch={false}
gisMaps={gisMaps}
isMiniMap={true}
searchView={null}
detailView={null}
onFeatureSelect={onFeatureSelect}
setIsOpen={setIsOpen}
setFeatureData={setFeatureData}
featureData={featureData}
setIsSearchModalOpen={setIsSearchModalOpen}
isOpen={isOpen}
isSearchModalOpen={isSearchModalOpen}
onCheckedLayersChange={handleCheckedLayersChange}
/>
</div>
);
};
export default Map;
+197
View File
@@ -0,0 +1,197 @@
"use client"
import { useFoStore } from "@/app/store";
import type { TableUpperProps } from "@/app/store/common";
import { downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiTable, type ReturnType } from "@common/business_components";
import ListDiv from "@common/business_components/ui/list_div";
import { useCallback, useEffect, useRef, useState } from "react";
export default function PlanSewage() {
const {Common
,PlanSewage
,PlanSewageSearch
,initializeCommonData
,searchPlanSewageList
} = useFoStore((state) => state) || { processAreaList: { processAreaList: [] }};
const {
isInit,
selectUpdownData,
selectOperationData,
selectSidoData,
selectSigunData,
selectRegionData,
selectUnitWaterShedData,
selectMiddleRegionData,
selectEnvPartData,
selectSearchYear,
btnClass
} = Common;
const {
labelArray,
} = PlanSewageSearch;
const {
upHeadList1,
upHeadList2,
upHeadList3,
planSewageList,
planSewageCount,
excelList
} = PlanSewage;
const ref = useRef<ReturnType>({
partValue: '',
sidoValue: '',
sigunValue: '',
regionValue: '',
unitWaterShedValue: '',
middleRegionValue: '',
envPartValue: '',
sidoText: '',
sigunText: ''
});
// 그리드 관련
const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null);
// 모달 상태 추가
// 초기화 변수
const [resetPage, setResetPage] = useState<boolean>(false);
// 표 너비
const [widthSize, setWidthSize] = useState<string>("w-[120%]");
// 구분별 그리드 헤더
const [upheadList, setUpheadList] = useState<TableUpperProps[]>(upHeadList1);
// 용량별
const [fcltCpct, setFcltCpct] = useState<string>("0");
// 시설명
const [fcltyName, setFcltyName] = useState<string>("");
// 시설상태
const [operationValue, setOperationValue] = useState<string>("0");
// 조회기간
const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0");
/**
* 페이지 함수
*/
// 초기화
const resetVal = () => {
setWidthSize("w-[120%]");
setUpheadList(upHeadList1);
// 상태값 설정
setFcltCpct(selectUpdownData[0]?.val || "0");
setOperationValue(selectOperationData[0]?.val || "0");
setSearchYear(selectSearchYear[0]?.val || "0");
// SearchInputSelect 초기화
setFcltyName("");
if (searchInputRef.current) {
const input = searchInputRef.current.querySelector('input');
if (input) {
input.value = "";
}
}
}
/**
* useEffect
*/
useEffect(() => {
if (!isInit) {
initializeCommonData();
searchPlanSewageList(1, 10, searchYear, ref.current.partValue);
}
}, [isInit, initializeCommonData, searchYear, searchPlanSewageList]);
useEffect(() => {
console.log('excelList' , excelList);
if (excelList && excelList.length > 0) {
downloadExcel({
data: excelList as unknown as JSON[],
fileName: '계획하수량 산정지표 현황',
sheetNameArray: ['계획하수량 산정지표 현황'],
headerRange: 'A1:M1'
});
}
}, [excelList]);
/**
* 콜백함수
*/
// 조회
const searchPlanSewage = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => {
let nameCd, partStr
switch (partValue) {
case 'R':
if (regionValue !== '0') {
if(unitWaterShedValue==='0') nameCd = regionValue;
else if (middleRegionValue==='0') nameCd = unitWaterShedValue;
else nameCd = middleRegionValue;
}
partStr = partValue
break;
default:
if(sidoValue !== '0') nameCd = sidoValue;
if(sigunValue !== '0') nameCd = sigunValue;
partStr = envPartValue !== '0' ? envPartValue:partValue;
break;
}
if (partStr.length > 1 || fcltCpct !== '0' || operationValue!== '0' ||nameCd ||(fcltyName.trim() !== "" && fcltyName.trim() !== null)) {
searchPlanSewageList(pageNo, pageCnt, searchYear, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined);
} else {
searchPlanSewageList(pageNo, pageCnt, searchYear, partValue);
}
}, [operationValue, fcltyName, searchPlanSewageList, searchYear, fcltCpct]);
const chkVal = useCallback((returntype:ReturnType) => {
setWidthSize(returntype.partValue=='A' ?"w-[120%]":"w-[130%]")
setUpheadList(returntype.partValue == 'R' ? upHeadList2 : returntype.partValue == 'G'?upHeadList3:upHeadList1);
setResetPage(true);
}, [upHeadList1, upHeadList2, upHeadList3]);
// 엑셀다운로드
const excelDownload = useCallback((searchReturn: ReturnType) => {
searchPlanSewage(searchReturn, 1, 10000);
}, [searchPlanSewage]);
// 페이지 변경
const handlePageChange = useCallback((page: number) => {
setResetPage(false);
searchPlanSewage(ref.current, page, 10);
}, [searchPlanSewage]);
const mainList = planSewageList || [];
const total = planSewageCount || 0;
return (
<MainDiv>
<Title title="계획하수량 산정지표 현황" />
<SearchBaseForm
leftChildren={<>
<SelectBox className="col-span-3" label={labelArray[3] as string} selectArray={selectUpdownData} onSelectValue={(val) => setFcltCpct(val.toString())} value={fcltCpct} selectClass={undefined} />
<SearchInput className={"col-span-3"} label={labelArray[4] as string} textValue={fcltyName} setText={setFcltyName} />
<SelectBox className="col-span-3" label={labelArray[5] as string} selectArray={selectSearchYear} onSelectValue={(val) => setSearchYear(val.toString())} value={searchYear} selectClass={undefined} />
<SelectBox className="col-span-3" label={labelArray[6] as string} selectArray={selectOperationData} onSelectValue={(val) => setOperationValue(val.toString())} value={operationValue} selectClass={undefined} />
</>}
rightChildren={undefined}
selectSidoData={selectSidoData}
selectSigunData={selectSigunData}
selectRegionData={selectRegionData}
selectUnitWaterShedData={selectUnitWaterShedData}
selectMiddleRegionData={selectMiddleRegionData}
selectEnvPartData={selectEnvPartData}
btnClass={btnClass}
queryClick={chkVal}
excelClick={excelDownload}
initClick={resetVal}
ref={ref} isAdmin={false}/>
<ListDiv>
<div className="h-full w-full overflow-x-scroll">
<div className={widthSize}>
<UiTable cellClick={ undefined } headName={"계획하수량 산정지표 현황"} tableData={mainList} headlist={upheadList} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} />
</div>
</div>
</ListDiv>
</MainDiv>
);
}
@@ -0,0 +1,87 @@
import { TableHead, TableRow } from "@common/components/ui";
import type { TableUpperProps } from "../../store/dashboard";
const HeadMakeColSpan = ({ upHeadList, layoutNum }: { upHeadList: TableUpperProps[], layoutNum: number }) => {
return (
layoutNum === 0 ? (
<>
<TableRow className="bg-gray-50">{upHeadList.map((head: TableUpperProps) =>
(head.id === 'trtFrfNm' ? <TableHead className="w-1/12" rowSpan={3} key={head.id}>{head.title}</TableHead> :
head.id === 'laeNm' ? <TableHead className="w-1/12" rowSpan={3} key={head.id}>{head.title}</TableHead> :
head.id == 'fctCpc' || head.id == 'planInputWaterQlty' || head.id == 'designInputWaterQlty' ? <TableHead rowSpan={3} className="w-40" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> :
head.id === 'trstOprEtpNm' ? <TableHead className="w-1/15" rowSpan={3} key={head.id}>{head.title}</TableHead> :
head.upName === 'upChangeRe' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={2} colSpan={2}>{'행정구역'}</TableHead> :
head.upName === 'upChangeRe3' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={2} colSpan={6}>{'증설계획'}</TableHead> :
head.upName === 'upChangeRe4' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={19}>{'시설계획'}</TableHead> :
!head.upName ? <TableHead rowSpan={3} className="w-1/20 bg-gray-20" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> : null))}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => (
head.upName === 'upChangeRe4' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={6}>{'증설계획(m³/일)'}</TableHead> :
head.upName === 'upChangeRe5' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={6}>{'처리인구(인)'}</TableHead> :
head.upName === 'upChangeRe6' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={7}>{'처리구역(k㎡)'}</TableHead> :
null
))}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => {
if (head.upName === 'upChangeRe' || head.upName === 'upChangeRe1' || head.upName === 'upChangeRe2' || head.upName === 'upChangeRe3' || head.upName === 'upChangeRe4' || head.upName === 'upChangeRe5' || head.upName === 'upChangeRe6') {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-40 bg-gray-50">{head.title}</TableHead>;
}
})}
</TableRow>
</>
) : layoutNum === 1 ? (
<>
<TableRow className="bg-gray-50">{upHeadList.map((head: TableUpperProps) =>
(head.id === 'trtFrfNm' ? <TableHead className="w-1/12" rowSpan={3} key={head.id}>{head.title}</TableHead> :
head.id === 'laeNm' ? <TableHead className="w-1/12" rowSpan={3} key={head.id}>{head.title}</TableHead> :
head.id == 'fctCpc' || head.id == 'planInputWaterQlty' || head.id == 'designInputWaterQlty' ? <TableHead rowSpan={3} className="w-40" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> :
head.id === 'trstOprEtpNm' ? <TableHead className="w-1/15" rowSpan={3} key={head.id}>{head.title}</TableHead> :
head.upName === 'upChangeRe' && head.upSequnce === 1 ? <TableHead className="w-75 bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={2} colSpan={3}>{'유역'}</TableHead> :
head.upName === 'upChangeRe3' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={2} colSpan={6}>{'증설계획'}</TableHead> :
!head.upName ? <TableHead rowSpan={3} className="w-1/20 bg-gray-20" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> : null))}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => (
head.upName === 'upChangeRe4' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={6}>{'증설계획(m³/일)'}</TableHead> :
head.upName === 'upChangeRe5' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={6}>{'처리인구(인)'}</TableHead> :
head.upName === 'upChangeRe6' && head.upSequnce === 1 ? <TableHead className="bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={7}>{'처리구역(k㎡)'}</TableHead> :
null
))}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => {
if (head.upName === 'upChangeRe' || head.upName === 'upChangeRe1' || head.upName === 'upChangeRe2' || head.upName === 'upChangeRe3' || head.upName === 'upChangeRe4' || head.upName === 'upChangeRe5' || head.upName === 'upChangeRe6') {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-40 bg-gray-50">{head.title}</TableHead>;
}
})}
</TableRow>
</>
) : (
<>
<TableRow className="bg-gray-50">{upHeadList.map((head: TableUpperProps) =>
(head.id === 'trtFrfNm' ? <TableHead className="w-1/12" rowSpan={2} key={head.id}>{head.title}</TableHead> :
head.id === 'laeNm' ? <TableHead className="w-1/12" rowSpan={2} key={head.id}>{head.title}</TableHead> :
head.id == 'fctCpc' || head.id == 'planInputWaterQlty' || head.id == 'designInputWaterQlty' ? <TableHead rowSpan={2} className="w-40" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> :
head.id === 'trstOprEtpNm' ? <TableHead className="w-1/15" rowSpan={2} key={head.id}>{head.title}</TableHead> :
head.upName === 'upChangeRe1' && head.upSequnce === 1 ? <TableHead className="w-75 bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={3}>{'권역'}</TableHead> :
head.upName === 'upChangeRe2' && head.upSequnce === 1 ? <TableHead className="w-75 bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={2}>{'환경청'}</TableHead> :
head.upName === 'upChangeRe3' && head.upSequnce === 1 ? <TableHead className="w-75 bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={6}>{'증설계획(m³/일)'}</TableHead> :
head.upName === 'upChangeRe5' && head.upSequnce === 1 ? <TableHead className="w-75 bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={6}>{'처리인구(인)'}</TableHead> :
head.upName === 'upChangeRe6' && head.upSequnce === 1 ? <TableHead className="w-75 bg-gray-50" style={{ whiteSpace: 'pre-line' }} key={head.id} rowSpan={1} colSpan={7}>{'처리구역(k㎡)'}</TableHead> :
!head.upName ? <TableHead rowSpan={2} className="w-1/20 bg-gray-20" style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead> : null))}
</TableRow>
<TableRow className="bg-gray-50">
{upHeadList.map((head: TableUpperProps) => {
if (head.upName === 'upChangeRe1' || head.upName === 'upChangeRe2' ||head.upName === 'upChangeRe3' || head.upName === 'upChangeRe4' || head.upName === 'upChangeRe5' || head.upName === 'upChangeRe6') {
return <TableHead key={head.id} style={{ whiteSpace: 'pre-line' }} className="w-40 bg-gray-50">{head.title}</TableHead>;
}
})}
</TableRow>
</>
)
);
}
export { HeadMakeColSpan };
@@ -0,0 +1,55 @@
"use client"
import { useFoStore } from "@/app/store";
import { UiTable } from "@common/business_components/ui";
import ListDiv from "@common/business_components/ui/list_div";
import { Table, TableCell, TableHead, TableHeader, TableRow } from "@common/components/ui";
import { useEffect } from "react";
export default function ProcessFacilityInfoModal(props: { fcltyName: string; facilityCd : string;}) {
// 그리드 관련
const {ProcessAreaDetail, initModalData} = useFoStore((state) => state);
useEffect(() => {
initModalData(props.facilityCd);
}, [initModalData, props.facilityCd]);
const {
upHeadListFcltyImproveInfo,
gridListFcltyImproveInfo,
upHeadListProcessPopulation,
gridListProcessPopulation,
upHeadListProcessAreaPlan,
gridListProcessAreaPlan,
} = ProcessAreaDetail
return (
<>
<ListDiv>
<div className="card !m-[1.2rem] max-h-[40rem] overflow-auto !p-0r">
<Table>
<TableHeader>
<TableRow>
<TableHead className="w-1/6">{'시설명'}</TableHead>
<TableCell className="w-1/3">{props.fcltyName}</TableCell>
<TableHead className="w-1/6">{'시설코드'}</TableHead>
<TableCell className="w-1/3">{props.facilityCd}</TableCell>
</TableRow>
</TableHeader>
</Table>
</div>
</ListDiv>
<ListDiv>
<UiTable headName={"시설증설 계획"} tableData={gridListFcltyImproveInfo} headlist={upHeadListFcltyImproveInfo} pageSize={0} total={0} cellClick={undefined} onPageChange={()=>{}}>
</UiTable>
</ListDiv>
<ListDiv>
<UiTable headName={"처리인구 계획"} tableData={gridListProcessPopulation} headlist={upHeadListProcessPopulation} pageSize={0} total={0} cellClick={undefined} onPageChange={()=>{}}>
</UiTable>
</ListDiv>
<ListDiv>
<UiTable headName={"처리구역 계획"} tableData={gridListProcessAreaPlan} headlist={upHeadListProcessAreaPlan} pageSize={0} total={0} cellClick={undefined} onPageChange={()=>{}}>
</UiTable>
</ListDiv>
</>
);
}
+172
View File
@@ -0,0 +1,172 @@
"use client"
import { useFoStore } from "@/app/store";
import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components";
import ListDiv from "@common/business_components/ui/list_div";
import { useCallback, useEffect, useRef, useState } from "react";
import ProcessFacilityInfoModal from "./modal/page";
export default function ProcessArea() {
const {Common,
ProcessArea,
ProcessAreaSearch,
initializeCommonData,
searchProcessAreaList
} = useFoStore((state) => state) || {
Common: { isInit: false, selectSidoData: [], selectSigunData: [], selectRegionData: [], selectUnitWaterShedData: [], selectMiddleRegionData: [], selectEnvPartData: [], selectSearchYear: [{ val: "0" }], btnClass: "" },
ProcessArea: { processAreaList: [], processAreaCount: 0, excelList: [], upHeadList1: [] },
ProcessAreaSearch: { processFacilityLabelArray: [] },
initializeCommonData: () => {},
searchProcessAreaList: () => {}
};
const {
isInit,
selectSidoData,
selectSigunData,
selectRegionData,
selectUnitWaterShedData,
selectMiddleRegionData,
selectEnvPartData,
selectSearchYear,
btnClass,
} = Common;
const {
processFacilityLabelArray,
} = ProcessAreaSearch;
const {
upHeadList1,
processAreaList,
processAreaCount,
excelList
} = ProcessArea;
const mainList: Record<string, unknown>[] = processAreaList || [];
// 그리드 관련
const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null);
// 모달 상태 추가
const [selectedRow, setSelectedRow] = useState<number | null>(null);
const [fcltyNm, setFcltyNm] = useState<string>("");
const [fcltyCd, setFcltyCd] = useState<string>("");
const [resetPage, setResetPage] = useState<boolean>(false);
const [searchYearValue, setSearchYearValue] = useState<string>(selectSearchYear[0]?.val || "0");
const ref = useRef<ReturnType>({
partValue: '',
sidoValue: '',
sigunValue: '',
regionValue: '',
unitWaterShedValue: '',
middleRegionValue: '',
envPartValue: '',
sidoText: '',
sigunText: ''
});
const searchProcessArea = useCallback(({ sidoText, sigunText}: ReturnType, pageNo: number, pageCnt:number=20) => {
searchProcessAreaList(pageNo, pageCnt, sidoText, sigunText, searchYearValue);
}, [searchProcessAreaList, searchYearValue]);
// 조회
const chkVal = useCallback(() => {
setResetPage(true);
}, []);
// 초기화
const resetVal = () => {
if (searchInputRef.current) {
const input = searchInputRef.current.querySelector('input');
if (input) {
input.value = "";
}
}
setSearchYearValue(selectSearchYear[0]?.val || "0");
searchProcessAreaList(1, 10, '', '', selectSearchYear[0]?.val);
}
// 셀 클릭 시 모달 표시
const cellClick = (index: number) => {
if (processAreaList[index]) {
setFcltyNm(processAreaList[index].swtrmtFcltNm);
setFcltyCd(processAreaList[index].swtrmtFcltCd);
setSelectedRow(index); // 클릭한 행의 index 저장
}
};
// 모달 닫기
const closeModal = () => {
setSelectedRow(null);
};
useEffect(() => {
if (!isInit) {
initializeCommonData();
searchProcessAreaList(1, 10, '', '', selectSearchYear[0]?.val);
}
}, [isInit, initializeCommonData, searchProcessAreaList, selectSearchYear]);
useEffect(() => {
if (excelList && excelList.length > 0) {
downloadExcel({
data: excelList as unknown as JSON[],
fileName: '처리구역정보',
sheetNameArray: ['처리구역정보'],
headerRange: 'A1:W1'
});
}
}, [excelList]);
const excelDownload = useCallback(() => {
searchProcessArea(ref.current, 1, 10000);
}, [searchProcessArea]);
const handlePageChange = useCallback((pageNo: number) => {
setResetPage(false);
searchProcessArea(ref.current, pageNo, 10);
}, [searchProcessArea]);
const total = processAreaCount;
return (
<MainDiv>
<Title title="처리구역정보" />
<ContentsDiv>
<SearchBaseForm
leftChildren={<>
<SelectBox className="col-span-3" label={processFacilityLabelArray[6] as string} selectArray={selectSearchYear} onSelectValue={(val) => setSearchYearValue(val.toString())} value={searchYearValue} selectClass={undefined}/>
</>}
rightChildren={undefined}
selectSidoData={selectSidoData}
selectSigunData={selectSigunData}
selectRegionData={selectRegionData}
selectUnitWaterShedData={selectUnitWaterShedData}
selectMiddleRegionData={selectMiddleRegionData}
selectEnvPartData={selectEnvPartData}
btnClass={btnClass}
queryClick={chkVal}
excelClick={excelDownload}
initClick={resetVal}
ref={ref}
isAdmin={true}
/>
<ListDiv>
<div className="h-full w-full overflow-x-scroll">
<div className="w-[180%]">
<UiTable cellClick={(index: number | undefined) => index !== undefined && cellClick(index)} headName={"처리구역정보"} tableData={mainList} headlist={upHeadList1} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} />
</div>
</div>
</ListDiv>
</ContentsDiv>
{selectedRow !== null && (
<UiModal
body={<ProcessFacilityInfoModal fcltyName={fcltyNm} facilityCd={fcltyCd}></ProcessFacilityInfoModal>}
bodyClass="sm:max-w-[90vw] w-[1200px] h-[900px]"
open={selectedRow !== null}
onOpenChange={(open: boolean) => !open && closeModal()}
/>
)}
</MainDiv>
);
}
@@ -0,0 +1,211 @@
"use client"
import { useFoStore } from "@/app/store";
import type { TableUpperProps } from "@/app/store/dashboard";
import { UITab, UiTable } from "@common/business_components/ui";
import ListDiv from "@common/business_components/ui/list_div";
import {
TabsContent
} from "@common/components";
import { Table, TableCell, TableHead, TableRow } from "@common/components/ui";
type ModalType = {
fcltyName: string;
facilityCd : string;
sido : string;
gugun : string;
daddr : string;
fctCpc : number | '';
trtFrfNm : string;
rnDt : string;
cmplDt : string;
trstOprEtpNm : string;
bscFctClsNm : string;
mnstr : string;
efflWtrrgn : string;
lgbsn : string;
efflWqltStndrdNm : string;
rgnNm : string;
rvbsn : string;
}
// 시설계획 table header 변경
const headMakeList = (upHeadList: TableUpperProps[], gridNum:number) => {
if(gridNum===1){
return ( <>
<TableRow>{
upHeadList.map((head: TableUpperProps) =>
(!head.upSequnce?<TableHead rowSpan={2}key={head.id}>{head.title}</TableHead>:
head.upName === 'upChangeRe'&& head.upSequnce=== 1?<TableHead key={head.id} rowSpan={1} colSpan={6} style={{ whiteSpace: 'pre-line' }}>{'증설계획(m³/일)'}</TableHead>:null))}
</TableRow>
<TableRow>
{upHeadList.map((head: TableUpperProps) =>
(head.upName === 'upChangeRe'?<TableHead className="w-150" key={head.id}>{head.title}</TableHead>:null))}
</TableRow>
</> )
} else if(gridNum===2){
return ( <>
<TableRow>{
upHeadList.map((head: TableUpperProps) =>
(!head.upSequnce?<TableHead rowSpan={2}key={head.id}>{head.title}</TableHead>:
head.upName === 'upChangeRe1'&& head.upSequnce=== 1?<TableHead key={head.id} rowSpan={1} colSpan={6} style={{ whiteSpace: 'pre-line' }}>{'계획유입수질(mg/L)'}</TableHead>:
head.upName === 'upChangeRe2'&& head.upSequnce=== 1?<TableHead key={head.id} rowSpan={1} colSpan={6} style={{ whiteSpace: 'pre-line' }}>{'설계유입수질(mg/L)'}</TableHead>:null))}
</TableRow>
<TableRow>
{upHeadList.map((head: TableUpperProps) =>
(head.upName === 'upChangeRe1'||head.upName === 'upChangeRe2'?<TableHead style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead>:null))}
</TableRow>
</> )
} else if(gridNum===3){
return ( <>
<TableRow>{
upHeadList.map((head: TableUpperProps) =>
(!head.upSequnce?<TableHead key={head.id}>{head.title}</TableHead>:
head.upName === 'upChangeRe1'&& head.upSequnce=== 1?<TableHead key={head.id} colSpan={2} style={{ whiteSpace: 'pre-line' }}>{'사업기간'}</TableHead>:null))}
</TableRow>
</> )
} else if(gridNum===4){
return ( <>
<TableRow>{
upHeadList.map((head: TableUpperProps) =>
(!head.upSequnce?<TableHead rowSpan={2}key={head.id}>{head.title}</TableHead>:
head.upName === 'upChangeRe1'&& head.upSequnce=== 1?<TableHead key={head.id} rowSpan={1} colSpan={6} style={{ whiteSpace: 'pre-line' }}>{'계획유입수질(mg/L)'}</TableHead>:
head.upName === 'upChangeRe2'&& head.upSequnce=== 1?<TableHead key={head.id} rowSpan={1} colSpan={6} style={{ whiteSpace: 'pre-line' }}>{'설계유입수질(mg/L)'}</TableHead>:
head.upName === 'upChangeRe3'&& head.upSequnce=== 1?<TableHead key={head.id} rowSpan={1} colSpan={6} style={{ whiteSpace: 'pre-line' }}>{'계획방류수질(mg/L)'}</TableHead>:null))}
</TableRow>
<TableRow>
{upHeadList.map((head: TableUpperProps) =>
(head.upName === 'upChangeRe1'||head.upName === 'upChangeRe2'||head.upName === 'upChangeRe3'?<TableHead style={{ whiteSpace: 'pre-line' }} key={head.id}>{head.title}</TableHead>:null))}
</TableRow>
</> )
}
}
export default function ProcessFacilityInfoModal(props: ModalType) {
// 그리드 관련
const {ProcessFacilityDetail} = useFoStore((state) => state);
const {
upHeadListPlanInfo,
gridListPlanInfo,
upHeadListFcltyAreaInfo,
gridListFcltyAreaInfo,
upHeadListFcltyHistInfo,
gridListFcltyHistInfo,
upHeadListFcltyWaterQltyInfo,
gridListFcltyWaterQltyInfo,
upHeadListLegalWaterQlty,
gridListLegalWaterQlty,
} = ProcessFacilityDetail;
gridListFcltyAreaInfo.map((item) => {
item.efflWtrrgn = props.efflWtrrgn;
item.lgbsn = props.lgbsn;
item.efflWqltStndrdNm = props.efflWqltStndrdNm;
item.rgnNm = props.rgnNm;
item.rvbsn = props.rvbsn;
})
const tabList = [
{ value: 'common_info', label: '기본정보' },
{ value: 'history_info', label: '이력정보' },
];
const fctCpc = props.fctCpc ? props.fctCpc.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",") : '';
const upHeadListPlanInfoChange = headMakeList(upHeadListPlanInfo, 2);
const upHeadListFcltyHistInfoChange = headMakeList(upHeadListFcltyHistInfo, 3);
const upHeadListFcltyWaterQltyInfoChange = headMakeList(upHeadListFcltyWaterQltyInfo, 4);
return (
<>
<ListDiv className="!h-[80px]">
<div className="card !m-[1.2rem] max-h-[40rem] overflow-auto !p-0r">
<Table>
<thead>
<TableRow className="">
<TableHead className="w-1/6">{'시설명'}</TableHead>
<TableCell className="w-1/3">{props.fcltyName}</TableCell>
<TableHead className="w-1/6">{'시설코드'}</TableHead>
<TableCell className="w-1/3">{props.facilityCd}</TableCell>
</TableRow>
</thead>
</Table>
</div>
</ListDiv>
<div className="overflow-y-auto">
<div className="!h-[600px]">
<ListDiv className="">
<UITab tabList={tabList} defaultValue="common_info">
<TabsContent value="common_info">
<div className="card-header">
<h3 className="text-2xl font-medium text-label">시설 기본정보</h3>
</div>
<div className="card !m-[1.2rem] max-h-[40rem] overflow-auto !p-0r">
<Table>
<thead>
<TableRow className="">
<TableHead className="w-1/6">{'시설명'}</TableHead>
<TableCell className="w-1/6">{props.fcltyName}</TableCell>
<TableHead className="w-1/6">{'시설구분'}</TableHead>
<TableCell className="w-1/6">{props.bscFctClsNm}</TableCell>
<TableHead className="w-1/6">{'시설용량(m³/일)'}</TableHead>
<TableCell className="w-1/6">{fctCpc}</TableCell>
</TableRow>
</thead>
<tbody>
<TableRow className="">
<TableHead className="w-1/6">{'시도'}</TableHead>
<TableCell className="w-1/6">{props.sido}</TableCell>
<TableHead className="w-1/6">{'시군구'}</TableHead>
<TableCell className="w-1/6">{props.gugun}</TableCell>
<TableHead className="w-1/6">{'가동개시일'}</TableHead>
<TableCell className="w-1/6">{props.rnDt}</TableCell>
</TableRow>
<TableRow className="">
<TableHead className="w-1/6">{'주소'}</TableHead>
<TableCell className="w-1/6" colSpan={3}>{props.daddr}</TableCell>
<TableHead className="w-1/6">{'준공일'}</TableHead>
<TableCell className="w-1/6">{props.cmplDt}</TableCell>
</TableRow>
<TableRow className="">
<TableHead className="w-1/6">{'공법'}</TableHead>
<TableCell className="w-1/6" colSpan={3}>{props.trtFrfNm}</TableCell>
<TableHead className="w-1/6">{'관리대행업자'}</TableHead>
<TableCell className="w-1/6">{props.trstOprEtpNm}</TableCell>
</TableRow>
</tbody>
</Table>
</div>
<UiTable headName={"시설 계획정보"} tableData={gridListPlanInfo} headlist={upHeadListPlanInfo} pageSize={0} total={0} cellClick={undefined} onPageChange={()=>{}}>
{upHeadListPlanInfoChange}
</UiTable>
<UiTable headName={"시설 법적방류수질"} tableData={gridListLegalWaterQlty} headlist={upHeadListLegalWaterQlty} pageSize={0} total={0} cellClick={undefined} onPageChange={()=>{}} />
<UiTable headName={"시설 수역 및 구역정보"} tableData={gridListFcltyAreaInfo} headlist={upHeadListFcltyAreaInfo} pageSize={0} total={0} cellClick={undefined} onPageChange={()=>{}} />
</TabsContent>
<TabsContent value="history_info">
<ListDiv>
<div className="h-[270px] w-full overflow-y-auto overflow-x-hidden">
<div className="h-[130%]">
<UiTable headName={"시설 이력관리"} tableData={gridListFcltyHistInfo} headlist={upHeadListFcltyHistInfo} pageSize={0} total={0} cellClick={undefined} onPageChange={()=>{}}>
{upHeadListFcltyHistInfoChange}
</UiTable>
</div>
</div>
</ListDiv>
<ListDiv>
<div className="h-[270px] w-full overflow-y-auto overflow-x-hidden">
<div className="h-[130%]">
<UiTable headName={"시설 수질정보"} tableData={gridListFcltyWaterQltyInfo} headlist={upHeadListFcltyWaterQltyInfo} pageSize={0} total={0} cellClick={undefined} onPageChange={()=>{}}>
{upHeadListFcltyWaterQltyInfoChange}
</UiTable>
</div>
</div>
</ListDiv>
</TabsContent>
</UITab>
</ListDiv>
</div>
</div>
</>
);
}
+294
View File
@@ -0,0 +1,294 @@
"use client"
import { HeadMakeColSpan } from "@/app/components/common/HeadMakeColSpan";
import { useFoStore } from "@/app/store";
import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components";
import ListDiv from "@common/business_components/ui/list_div";
import { useCallback, useEffect, useRef, useState } from "react";
import ProcessFacilityInfoModal from "./modal/page";
export default function ProcessFacilitySearch() {
const {Common
,ProcessFacility
,ProcessFacilityList
,initializeCommonData
//시설데이터 조회
,searchProcessFacility
//시설데이터 이력정보 조회
,searchProcessFacilityHist
//시설데이터 수질정보 조회
,searchProcessFacilityWaterQlty
} = useFoStore((state) => state) || { ProcessFacilityList: { processFacilityList: [], processFacilityCount: 0 }};
const {
isInit,
selectUpdownData,
selectOperationData,
selectFacilityPartData,
selectSidoData,
selectSigunData,
selectRegionData,
selectUnitWaterShedData,
selectMiddleRegionData,
selectEnvPartData,
btnClass,
} = Common;
const ref = useRef<ReturnType>({
partValue: '',
sidoValue: '',
sigunValue: '',
regionValue: '',
unitWaterShedValue: '',
middleRegionValue: '',
envPartValue: '',
sidoText: '',
sigunText: ''
});
useEffect(() => {
if(!isInit) {
initializeCommonData();
searchProcessFacility(1, 10, 'A', '0', '0', '0', '0');
}
}, [isInit
,initializeCommonData
,searchProcessFacility
]);
const {
processFacilityLabelArray,
} = ProcessFacility;
const {
upHeadList1,
upHeadList2,
upHeadList3,
excelList
} = ProcessFacilityList;
const processFacilityList = ProcessFacilityList?.processFacilityList || [];
// 그리드 관련
const inputRef = useRef<HTMLInputElement>(null);
const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null);
const [fcltCpct, setFcltCpct] = useState<string>("0");
const [fcltyName, setFcltyName] = useState<string>("");
const [operationValue, setOperationValue] = useState<string>("0");
const [facilityPartValue, setFacilityPartValue] = useState<string>("0");
const [resetPage, setResetPage] = useState<boolean>(false);
const [partValue, setPartValue] = useState<string>("A");
const [widthSize, setWidthSize] = useState<string>("w-[120%]");
const [upHeadList, setUpheadList] = useState(upHeadList1);
/**
* 모달 상태 변수
* selectedRow : 클릭한 행의 index
* modalFcltyNm : 시설명
* modalFcltyCd : 시설코드
* modalSido : 시도
* modalGugun : 시군구
* modalLaeNm : 주소
* modalFctCpc : 시설용량
* modalTrtFrfNm : 공법
* modalRnDt : 가동개시일
* modalCmplDt : 준공일
* modalTrstOprEtpNm : 관리대행업자
* modalBscFctClsNm : 시설구분
*/
const [selectedRow, setSelectedRow] = useState<number | null>(null);
const [modalFcltyNm, setModalFcltyNm] = useState<string>("");
const [modalFcltyCd, setModalFcltyCd] = useState<string>("");
const [modalSido, setModalSido] = useState<string>("");
const [modalGugun, setModalGugun] = useState<string>("");
const [modalAddr, setModalAddr] = useState<string>("");
const [modalFctCpc, setModalFctCpc] = useState<number | ''>("");
const [modalTrtFrfNm, setModalTrtFrfNm] = useState<string>("");
const [modalRnDt, setModalRnDt] = useState<string>("");
const [modalCmplDt, setModalCmplDt] = useState<string>("");
const [modalTrstOprEtpNm, setModalTrstOprEtpNm] = useState<string>("");
const [modalBscFctClsNm, setModalBscFctClsNm] = useState<string>("");
const [modalMnstr, setModalMnstr] = useState<string>("");
const [modalEfflWtrrgn, setModalEfflWtrrgn] = useState<string>("");
const [modalLgbsn, setModalLgbsn] = useState<string>("");
const [modalEfflWqltStndrdNm, setModalEfflWqltStndrdNm] = useState<string>("");
const [modalRgnNm, setModalRgnNm] = useState<string>("");
const [modalRvbsn, setModalRvbsn] = useState<string>("");
const [isSearch, setIsSearch] = useState<boolean>(false);
// 표 레이아웃 상태
const chkVal = useCallback((result:ReturnType) => {
setPartValue(result.partValue);
setResetPage(true);
setIsSearch(true);
}, []);
useEffect(() => {
switch (partValue) {
case 'A':
setWidthSize("w-[130%]");
setUpheadList(upHeadList1);
break;
case 'R':
setWidthSize("w-[140%]");
setUpheadList(upHeadList2);
break;
case 'G':
setWidthSize("w-[150%]");
setUpheadList(upHeadList3);
break;
}
}, [partValue, upHeadList1, upHeadList2, upHeadList3]);
useEffect(() => {
if (excelList && excelList.length > 0) {
downloadExcel({ data: excelList as unknown as JSON[], fileName: '처리시설 시설정보', sheetNameArray: ['처리시설 시설정보'], headerRange: 'A1:P1' });
}
}, [excelList]);
const searchFcltyList = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=10) => {
let nameCd, partStr
switch (partValue) {
case 'R':
if (regionValue !== '0') {
if(unitWaterShedValue==='0') nameCd = regionValue;
else if (middleRegionValue==='0') nameCd = unitWaterShedValue;
else nameCd = middleRegionValue;
}
partStr = partValue
break;
default:
if(sidoValue !== '0') nameCd = sidoValue;
if(sigunValue !== '0') nameCd = sigunValue;
partStr = envPartValue !== '0' ? envPartValue:partValue;
break;
}
searchProcessFacility(pageNo, pageCnt, partStr, nameCd || '0', fcltCpct, operationValue, facilityPartValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined);
if(isSearch) {
setIsSearch(false);
}
}, [facilityPartValue, fcltCpct, fcltyName, isSearch, operationValue, searchProcessFacility])
const changePage = useCallback((page: number) => {
searchFcltyList(ref.current, page);
},[searchFcltyList])
const resetVal = () => {
// SelectBox 초기화 - 각 selectArray의 첫 번째 옵션 값 사용
searchProcessFacility(1, 10, 'A', '0', '0', '0', '0', '');
setFcltCpct("0");
setOperationValue("0");
setFacilityPartValue("0");
setPartValue('A');
setUpheadList(upHeadList1);
// SearchInputSelect 초기화
setFcltyName("");
// ref를 사용하여 input 값 초기화
if (inputRef.current) {
inputRef.current.value = "";
}
if (searchInputRef.current) {
const input = searchInputRef.current.querySelector('input');
if (input) {
input.value = "";
}
}
}
// 셀 클릭 시 모달 표시
const cellClick = (index: number) => {
const facility = processFacilityList[index];
if (facility) {
setModalFcltyNm(facility.bscFctNm);
setModalFcltyCd(facility.bscFctCd);
setModalSido(facility.sido);
setModalGugun(facility.gugun);
setModalAddr(facility.daddr);
setModalFctCpc(Number(facility.fctCpc));
setModalTrtFrfNm(facility.trtFrfNm);
setModalRnDt(facility.rnDt);
setModalCmplDt(facility.cmplDt);
setModalTrstOprEtpNm(facility.trstOprEtpNm);
setModalBscFctClsNm(facility.bscFctClsNm);
setModalMnstr(facility.mnstr);
setModalEfflWtrrgn(facility.efflWtrrgn);
setModalLgbsn(facility.lgbsn);
setModalEfflWqltStndrdNm(facility.efflWqltStndrdNm);
setModalRgnNm(facility.rgnNm);
setModalRvbsn(facility.rvbsn);
searchProcessFacilityHist(facility.bscFctCd);
searchProcessFacilityWaterQlty(facility.bscFctCd);
setSelectedRow(index); // 클릭한 행의 index 저장
}
}
const excelDownload = useCallback(() => {
searchFcltyList(ref.current, 1, 10000);
}, [searchFcltyList]);
// 모달 닫기
const closeModal = () => {
setSelectedRow(null);
}
const total = ProcessFacilityList?.processFacilityCount || 1;
return (
<MainDiv>
<Title title="처리시설 시설정보" />
<ContentsDiv>
<SearchBaseForm
leftChildren={<>
<SelectBox className="col-span-3" label={processFacilityLabelArray[3] as string} selectArray={selectUpdownData} onSelectValue={val => setFcltCpct(val.toString())} value={fcltCpct} selectClass={undefined} />
<SearchInput className="col-span-3" label={processFacilityLabelArray[4] as string} textValue={fcltyName} setText={setFcltyName} />
<SelectBox className="col-span-3" label={processFacilityLabelArray[5] as string} selectArray={selectOperationData} onSelectValue={val => setOperationValue(val.toString())} value={operationValue} selectClass={undefined} />
<SelectBox className="col-span-3" label={processFacilityLabelArray[6] as string} selectArray={selectFacilityPartData} onSelectValue={val => setFacilityPartValue(val.toString())} value={facilityPartValue} selectClass={undefined} />
</>}
rightChildren={undefined}
selectSidoData={selectSidoData}
selectSigunData={selectSigunData}
selectRegionData={selectRegionData}
selectUnitWaterShedData={selectUnitWaterShedData}
selectMiddleRegionData={selectMiddleRegionData}
selectEnvPartData={selectEnvPartData}
btnClass={btnClass}
queryClick={chkVal}
excelClick={excelDownload}
initClick={resetVal}
ref={ref} isAdmin={false} />
<ListDiv>
<div className="h-full w-full overflow-x-scroll">
<div className={widthSize}>
<UiTable
cellClick={(index: number | undefined) => index !== undefined && cellClick(index)}
headName={"처리시설 시설정보"}
tableData={processFacilityList}
headlist={upHeadList}
pageSize={10}
total={total}
resetPageNo={resetPage}
onPageChange={changePage}
>
<HeadMakeColSpan upHeadList={upHeadList} layoutNum={partValue}/>
</UiTable>
</div>
</div>
</ListDiv>
<span className="text-2xl text-gray-500">※계획유입수질 및 설계유입수질은 해당 BOD를 나타냄</span>
</ContentsDiv>
{selectedRow !== null && (
<UiModal
body={<ProcessFacilityInfoModal fcltyName={modalFcltyNm} facilityCd={modalFcltyCd} sido={modalSido} gugun={modalGugun} daddr={modalAddr} fctCpc={modalFctCpc} trtFrfNm={modalTrtFrfNm} rnDt={modalRnDt} cmplDt={modalCmplDt} trstOprEtpNm={modalTrstOprEtpNm} bscFctClsNm={modalBscFctClsNm} mnstr={modalMnstr} efflWtrrgn={modalEfflWtrrgn} lgbsn={modalLgbsn} efflWqltStndrdNm={modalEfflWqltStndrdNm} rgnNm={modalRgnNm} rvbsn={modalRvbsn}></ProcessFacilityInfoModal>}
bodyClass="sm:max-w-[70vw] sm:max-h-[95vh]"
open={selectedRow !== null}
onOpenChange={(open) => !open && closeModal()}
/>
)}
</MainDiv>
);
}
+200
View File
@@ -0,0 +1,200 @@
"use client"
import { useFoStore } from "@/app/store";
import type { TableUpperProps } from "@/app/store/common";
import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiTable, type ReturnType } from "@common/business_components";
import ListDiv from "@common/business_components/ui/list_div";
import { useCallback, useEffect, useRef, useState } from "react";
export default function Rdi() {
const {Common
,initializeCommonData
,Rdi
,RdiSearch
,searchRdiList
} = useFoStore((state) => state) || { processAreaList: { processAreaList: [] }};
const {
isInit,
selectUpdownData,
selectOperationData,
selectSidoData,
selectSigunData,
selectRegionData,
selectUnitWaterShedData,
selectMiddleRegionData,
selectEnvPartData,
selectSearchYear,
btnClass
} = Common;
const {
labelArray,
} = RdiSearch;
const {
upHeadList1,
upHeadList2,
upHeadList3,
rdiList,
rdiCount,
excelList,
} = Rdi;
const ref = useRef<ReturnType>({
partValue: '',
sidoValue: '',
sigunValue: '',
regionValue: '',
unitWaterShedValue: '',
middleRegionValue: '',
envPartValue: '',
sidoText: '',
sigunText: ''
});
// 그리드 관련
const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null);
// 초기화 변수
const [resetPage, setResetPage] = useState<boolean>(false);
// 표 너비
const [widthSize, setWidthSize] = useState<string>("w-[120%]");
// 구분별 그리드 헤더
const [upheadList, setUpheadList] = useState<TableUpperProps[]>(upHeadList1);
// 용량별
const [fcltCpct, setFcltCpct] = useState<string>("0");
// 시설명
const [fcltyName, setFcltyName] = useState<string>("");
// 시설상태
const [operationValue, setOperationValue] = useState<string>("0");
// 조회기간
const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0");
/**
* 페이지 함수
*/
// 초기화
const resetVal = () => {
setWidthSize("w-[120%]");
setUpheadList(upHeadList1)
// 상태값 설정
setFcltCpct(selectUpdownData[0]?.val || "0");
setOperationValue(selectOperationData[0]?.val || "0");
setSearchYear(selectSearchYear[0]?.val || "0");
// SearchInputSelect 초기화
setFcltyName("");
if (searchInputRef.current) {
const input = searchInputRef.current.querySelector('input');
if (input) {
input.value = "";
}
}
searchRdiList(1, 10, selectSearchYear[0]?.val || "0");
}
/**
* useEffect
*/
useEffect(() => {
if (!isInit) {
initializeCommonData();
searchRdiList(1, 10, selectSearchYear[0]?.val || "0");
}
}, [isInit, initializeCommonData, searchRdiList, selectSearchYear]);
useEffect(() => {
if (excelList && excelList.length > 0) {
downloadExcel({
data: excelList as unknown as JSON[],
fileName: 'RDII추정',
sheetNameArray: ['RDII추정'],
headerRange: 'A1:O1'
});
}
}, [excelList]);
/**
* 콜백함수
*/
// 조회
const searchRdi = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => {
let nameCd, partStr
switch (partValue) {
case 'R':
if (regionValue !== '0') {
if(unitWaterShedValue==='0') nameCd = regionValue;
else if (middleRegionValue==='0') nameCd = unitWaterShedValue;
else nameCd = middleRegionValue;
}
partStr = partValue
break;
default:
if(sidoValue !== '0') nameCd = sidoValue;
if(sigunValue !== '0') nameCd = sigunValue;
partStr = envPartValue !== '0' ? envPartValue:partValue;
break;
}
if (partStr.length > 1 || fcltCpct !== '0' || operationValue!== '0' ||nameCd ||(fcltyName.trim() !== "" && fcltyName.trim() !== null)) {
searchRdiList(pageNo, pageCnt, searchYear, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined);
} else {
searchRdiList(pageNo, pageCnt, searchYear);
}
}, [operationValue, fcltyName, searchRdiList, searchYear, fcltCpct]);
const chkVal = useCallback((returntype:ReturnType) => {
setWidthSize(returntype.partValue=='A' ?"w-[120%]":"w-[130%]")
setUpheadList(returntype.partValue == 'R' ? upHeadList2 : returntype.partValue == 'G'?upHeadList3:upHeadList1);
setResetPage(true);
}, [upHeadList1, upHeadList2, upHeadList3]);
// 엑셀다운로드
const excelDownload = useCallback(() => {
searchRdi(ref.current, 1, 10000);
}, [searchRdi]);
// 페이지 변경
const handlePageChange = useCallback((page: number) => {
setResetPage(false);
searchRdi(ref.current, page, 10);
}, [searchRdi]);
const mainList: Record<string, string | number | boolean>[] = rdiList || [];
const total = rdiCount || 0;
return (
<MainDiv>
<Title title="RDII추정" />
<ContentsDiv>
<SearchBaseForm
leftChildren={<>
<SelectBox className="col-span-3" label={labelArray[3] as string} selectArray={selectUpdownData} onSelectValue={(val) => setFcltCpct(val.toString())} value={fcltCpct} selectClass={undefined} />
<SearchInput ref={searchInputRef} className={"col-span-3"} label={labelArray[4] as string} textValue={fcltyName} setText={setFcltyName} />
<SelectBox className="col-span-3" label={labelArray[5] as string} selectArray={selectSearchYear} onSelectValue={(val) => setSearchYear(val.toString())} value={searchYear} selectClass={undefined} />
<SelectBox className="col-span-3" label={labelArray[6] as string} selectArray={selectOperationData} onSelectValue={(val) => setOperationValue(val.toString())} value={operationValue} selectClass={undefined} />
</>}
rightChildren={undefined}
selectSidoData={selectSidoData}
selectSigunData={selectSigunData}
selectRegionData={selectRegionData}
selectUnitWaterShedData={selectUnitWaterShedData}
selectMiddleRegionData={selectMiddleRegionData}
selectEnvPartData={selectEnvPartData}
btnClass={btnClass}
queryClick={chkVal}
excelClick={excelDownload}
initClick={resetVal}
ref={ref} isAdmin={false} />
<ListDiv>
<div className="h-full w-full overflow-x-scroll">
<div className={widthSize}>
<UiTable cellClick={ undefined } headName={"RDII추정"} tableData={mainList} headlist={upheadList} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} />
</div>
<span className="text-2xl text-gray-500">※주) 처리장 유입부에서 측정된 유량 및 수질조사 자료를 기준으로 산정된 결과임</span>
</div>
</ListDiv>
</ContentsDiv>
</MainDiv>
);
}
@@ -0,0 +1,161 @@
"use client"
import { useFoStore } from "@/app/store";
import type { SewageOccurDetailProps } from "@/app/store/sewageOccur";
import { DateMonthPicker, UiTable } from "@common/business_components/ui";
import ListDiv from "@common/business_components/ui/list_div";
import { Table, TableCell, TableHead, TableRow } from "@common/components/ui";
import { useEffect, useState } from "react";
export default function SewageOccurModal(props: { fcltyName: string; facilityCd : string; searchYear : string}) {
// 그리드 관련
const {SewageOccurDetail} = useFoStore((state) => state);
// searchYear 유효성 검사 및 기본값 설정
const validSearchYear = props.searchYear && /^\d{4}$/.test(props.searchYear) ? props.searchYear : new Date().getFullYear().toString();
const [startYearMonth, setStartYearMonth] = useState(validSearchYear+'01');
const [endYearMonth, setEndYearMonth] = useState(validSearchYear+'12');
const [detailList, setDetailList] = useState<SewageOccurDetailProps[]>([]);
const {
upHeadListSewageOccurDetail,
gridListSewageOccurDetail
} = SewageOccurDetail;
useEffect(() => {
setDetailList(gridListSewageOccurDetail);
}, [gridListSewageOccurDetail]);
useEffect(() => {
const filteredList = gridListSewageOccurDetail.filter(item => item.msrDt.replace(/-/g, '') >= startYearMonth && item.msrDt.replace(/-/g, '') <= endYearMonth);
let maxWaterUsage = 0;
let maxSwageChangePercent = 0;
let maxEstimationSewage = 0;
let minWaterUsage = filteredList[0]?.waterUsage || 0;
let minSwageChangePercent = filteredList[0]?.swageChangePercent || 0;
let minEstimationSewage = filteredList[0]?.estimationSewage || 0;
let totalWaterUsage = 0;
let totalSwageChangePercent = 0;
let totalEstimationSewage = 0;
let avgWaterUsage = 0;
let avgSwageChangePercent = 0;
let avgEstimationSewage = 0;
filteredList.forEach(item => {
if (item.waterUsage > maxWaterUsage) {
maxWaterUsage = item.waterUsage;
}
if (item.swageChangePercent > maxSwageChangePercent) {
maxSwageChangePercent = item.swageChangePercent;
}
if (item.estimationSewage > maxEstimationSewage) {
maxEstimationSewage = item.estimationSewage;
}
if (item.waterUsage < minWaterUsage) {
minWaterUsage = item.waterUsage;
}
if (item.swageChangePercent < minSwageChangePercent) {
minSwageChangePercent = item.swageChangePercent;
}
if (item.estimationSewage < minEstimationSewage) {
minEstimationSewage = item.estimationSewage;
}
totalWaterUsage += item.waterUsage;
totalSwageChangePercent += item.swageChangePercent;
totalEstimationSewage += item.estimationSewage;
});
avgWaterUsage = totalWaterUsage?totalWaterUsage / filteredList.length:0;
avgSwageChangePercent = totalSwageChangePercent?totalSwageChangePercent / filteredList.length:0;
avgEstimationSewage = totalEstimationSewage?totalEstimationSewage / filteredList.length:0;
const newData = [
{
msrDt: '평균',
waterUsage: Math.round(avgWaterUsage * 100) / 100,
swageChangePercent: Math.round(avgSwageChangePercent * 100) / 100,
estimationSewage: Math.round(avgEstimationSewage * 100) / 100
},
{
msrDt: '최대',
waterUsage: maxWaterUsage,
swageChangePercent: maxSwageChangePercent,
estimationSewage: maxEstimationSewage
},
{
msrDt: '최소',
waterUsage: minWaterUsage,
swageChangePercent: minSwageChangePercent,
estimationSewage: minEstimationSewage
},
...filteredList
]
setDetailList(newData);
}, [startYearMonth, endYearMonth, gridListSewageOccurDetail]);
return (
<>
<ListDiv className="!h-[80px] !mt-10">
<div className="card !m-[1.2rem] max-h-[40rem] overflow-auto !p-0r">
<Table>
<thead>
<TableRow className="">
<TableHead className="w-1/6">{'시설명'}</TableHead>
<TableCell className="w-1/3">{props.fcltyName}</TableCell>
<TableHead className="w-1/6">{'년월'}</TableHead>
<TableCell className="w-1/3">
<DateMonthPicker
field={{
value: startYearMonth || (() => {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
return `${year}${month}`;
})(),
onChange: (value: string) => {
if (value && /^\d{6}$/.test(value)) {
setStartYearMonth(value);
}
}
}}
selectClass="ml-5 w-[100px]"
startYear={1900}
/>~
<DateMonthPicker
field={{
value: endYearMonth || (() => {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, '0');
return `${year}${month}`;
})(),
onChange: (value: string) => {
if (value && /^\d{6}$/.test(value)) {
setEndYearMonth(value);
}
}
}}
selectClass="ml-5 w-[100px]"
startYear={1900}
/>
</TableCell>
</TableRow>
</thead>
</Table>
</div>
</ListDiv>
<ListDiv className="!h-[570px] overflow-y-auto">
<div className="h-[120%]">
<UiTable headName={"오수발생량 추정"} tableData={detailList} headlist={upHeadListSewageOccurDetail} pageSize={0} total={0} cellClick={undefined} onPageChange={() => {}} boldArray={[0, 1, 2]}>
</UiTable>
</div>
</ListDiv>
</>
);
}
+237
View File
@@ -0,0 +1,237 @@
"use client"
import { useFoStore } from "@/app/store";
import type { TableUpperProps } from "@/app/store/common";
import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components";
import ListDiv from "@common/business_components/ui/list_div";
import { useCallback, useEffect, useRef, useState } from "react";
import SewageOccurModal from "./modal/page";
import type { SewageOccurProps } from "@/app/store/sewageOccur";
export default function PlanSewage() {
const {Common
,SewageOccur
,SewageOccurSearch
,initializeCommonData
,searchSewageOccurList
} = useFoStore((state) => state) || {
Common: { isInit: false, selectUpdownData: [], selectOperationData: [], selectSidoData: [], selectSigunData: [], selectRegionData: [], selectUnitWaterShedData: [], selectMiddleRegionData: [], selectEnvPartData: [], selectSearchYear: [{ val: "0" }], btnClass: "" },
SewageOccur: { sewageOccurList: [], sewageOccurCount: 0, excelList: [], upHeadList1: [], upHeadList2: [], upHeadList3: [] },
SewageOccurSearch: { labelArray: [] },
initializeCommonData: () => {},
searchSewageOccurList: () => {}
};
const {
isInit,
selectUpdownData,
selectOperationData,
selectSidoData,
selectSigunData,
selectRegionData,
selectUnitWaterShedData,
selectMiddleRegionData,
selectEnvPartData,
selectSearchYear,
btnClass
} = Common;
const {
labelArray,
} = SewageOccurSearch;
const {
upHeadList1,
upHeadList2,
upHeadList3,
sewageOccurList,
sewageOccurCount,
excelList
} = SewageOccur;
const ref = useRef<ReturnType>({
partValue: '',
sidoValue: '',
sigunValue: '',
regionValue: '',
unitWaterShedValue: '',
middleRegionValue: '',
envPartValue: '',
sidoText: '',
sigunText: ''
});
const sewageOccur = SewageOccur?.sewageOccurList || [];
// 모달 상태 추가
const [selectedRow, setSelectedRow] = useState<number | null>(null);
const [fcltyNm, setFcltyNm] = useState<string>("");
const [fcltyCd, setFcltyCd] = useState<string>("");
// 그리드 관련
const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null);
// 초기화 변수
const [resetPage, setResetPage] = useState<boolean>(false);
// 표 너비
const [widthSize, setWidthSize] = useState<string>("w-[120%]");
// 구분별 그리드 헤더
const [upheadList, setUpheadList] = useState<TableUpperProps[]>(upHeadList1);
// 용량별
const [fcltCpct, setFcltCpct] = useState<string>("0");
// 시설명
const [fcltyName, setFcltyName] = useState<string>("");
// 시설상태
const [operationValue, setOperationValue] = useState<string>("0");
// 조회기간
const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0");
/**
* 페이지 함수
*/
// 초기화
const resetVal = () => {
setWidthSize("w-[120%]");
setUpheadList(upHeadList1)
// 상태값 설정
setFcltCpct(selectUpdownData[0]?.val || "0");
setOperationValue(selectOperationData[0]?.val || "0");
setSearchYear(selectSearchYear[0]?.val || "0");
// SearchInputSelect 초기화
setFcltyName("");
if (searchInputRef.current) {
const input = searchInputRef.current.querySelector('input');
if (input) {
input.value = "";
}
}
}
// 모달 닫기
const closeModal = () => {
setSelectedRow(null);
};
// 셀 클릭 시 모달 표시
const cellClick = (index: number) => {
if (sewageOccur[index]) {
setFcltyNm(sewageOccur[index].bscFctNm);
setFcltyCd(sewageOccur[index].bscFctCd);
setSelectedRow(index); // 클릭한 행의 index 저장
}
};
/**
* useEffect
*/
useEffect(() => {
if (!isInit) {
initializeCommonData();
searchSewageOccurList(1, 10, selectSearchYear[0]?.val || "0");
}
}, [isInit, initializeCommonData, searchSewageOccurList, selectSearchYear]);
useEffect(() => {
if (excelList && excelList.length > 0) {
downloadExcel({
data: excelList as unknown as JSON[],
fileName: '오수발생량 추정',
sheetNameArray: ['오수발생량 추정'],
headerRange: 'A1:O1'
});
}
}, [excelList]);
/**
* 콜백함수
*/
// 조회
const searchFacilityPrecipitation = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => {
let nameCd, partStr
switch (partValue) {
case 'R':
if (regionValue !== '0') {
if(unitWaterShedValue==='0') nameCd = regionValue;
else if (middleRegionValue==='0') nameCd = unitWaterShedValue;
else nameCd = middleRegionValue;
}
partStr = partValue
break;
default:
if(sidoValue !== '0') nameCd = sidoValue;
if(sigunValue !== '0') nameCd = sigunValue;
partStr = envPartValue !== '0' ? envPartValue:partValue;
break;
}
if (partStr.length > 1 || fcltCpct !== '0' || operationValue!== '0' ||nameCd ||(fcltyName.trim() !== "" && fcltyName.trim() !== null)) {
searchSewageOccurList(pageNo, pageCnt, searchYear, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined);
} else {
searchSewageOccurList(pageNo, pageCnt, searchYear);
}
}, [fcltCpct, operationValue, fcltyName, searchSewageOccurList, searchYear]);
const chkVal = useCallback((returntype:ReturnType) => {
setWidthSize(returntype.partValue=='A' ?"w-[120%]":"w-[130%]")
setUpheadList(returntype.partValue == 'R' ? upHeadList2 : returntype.partValue == 'G'?upHeadList3:upHeadList1);
setResetPage(true);
}, [upHeadList1, upHeadList2, upHeadList3]);
// 엑셀다운로드
const excelDownload = useCallback(() => {
searchFacilityPrecipitation(ref.current, 1, 10000);
}, [searchFacilityPrecipitation]);
// 페이지 변경
const handlePageChange = useCallback((page: number) => {
setResetPage(false);
searchFacilityPrecipitation(ref.current, page, 10);
}, [searchFacilityPrecipitation]);
const mainList: SewageOccurProps[] = sewageOccurList || [];
const total = sewageOccurCount || 0;
return (
<MainDiv>
<Title title="오수발생량 추정" />
<ContentsDiv>
<SearchBaseForm
leftChildren={<>
<SelectBox className="col-span-3" label={labelArray[3] as string} selectArray={selectUpdownData} onSelectValue={(val) => setFcltCpct(val.toString())} value={fcltCpct} selectClass={undefined} />
<SearchInput className={"col-span-3"} label={labelArray[4] as string} textValue={fcltyName} setText={setFcltyName} />
<SelectBox className="col-span-3" label={labelArray[5] as string} selectArray={selectSearchYear} onSelectValue={(val) => setSearchYear(val.toString())} value={searchYear} selectClass={undefined} />
<SelectBox className="col-span-3" label={labelArray[6] as string} selectArray={selectOperationData} onSelectValue={(val) => setOperationValue(val.toString())} value={operationValue} selectClass={undefined} />
</>}
rightChildren={undefined}
selectSidoData={selectSidoData}
selectSigunData={selectSigunData}
selectRegionData={selectRegionData}
selectUnitWaterShedData={selectUnitWaterShedData}
selectMiddleRegionData={selectMiddleRegionData}
selectEnvPartData={selectEnvPartData}
btnClass={btnClass}
queryClick={chkVal}
excelClick={excelDownload}
initClick={resetVal}
ref={ref}
isAdmin={false}
/>
<ListDiv>
<div className="h-full w-full overflow-x-scroll">
<div className={widthSize}>
<UiTable cellClick={(index: number | undefined) => index !== undefined && cellClick(index)} headName={"오수발생량 추정"} tableData={mainList} headlist={upheadList} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} />
</div>
</div>
</ListDiv>
</ContentsDiv>
{selectedRow !== null && (
<UiModal
body={<SewageOccurModal fcltyName={fcltyNm} facilityCd={fcltyCd} searchYear={searchYear}></SewageOccurModal>}
bodyClass="sm:max-w-[90vw] w-[1000px] h-[770px]"
open={selectedRow !== null}
onOpenChange={(open) => !open && closeModal()}
noHeaderYn="Y"
noFooterYn="Y"
/>
)}
</MainDiv>
);
}
@@ -0,0 +1,317 @@
"use client"
import { useFoStore } from "@/app/store";
import type { UnderWaterInputModalProps } from "@/app/store/underWaterInput";
import { DateMonthPicker, UITab, UiTable } from "@common/business_components/ui";
import ListDiv from "@common/business_components/ui/list_div";
import { Table, TableCell, TableHead, TableRow, TabsContent } from "@common/components/ui";
import { useEffect, useState } from "react";
type ModalProps ={
fcltyName: string;
searchYear : string;
}
const tabList1 = [
{ value: 'tab_term_year', label: '연간' },
{ value: 'tab_term_summer', label: '하절기' },
{ value: 'tab_term_winter', label: '동절기' },
];
export default function UnderWaterInputModal({fcltyName='', searchYear=new Date().getFullYear().toString()} : ModalProps) {
// 그리드 관련
const {UnderWaterDetail} = useFoStore((state) => state);
const [startYmDate, setStartYmDate] = useState<string>(searchYear+'01');
const [endYmDate, setEndYmDate] = useState<string>(searchYear+'12');
const [tabTerm, setTabTerm] = useState<string>('tab_term_year');
// 이전 값들을 저장하기 위한 상태
const [prevStartYmDate, setPrevStartYmDate] = useState<string>(searchYear+'01');
const [prevEndYmDate, setPrevEndYmDate] = useState<string>(searchYear+'12');
const {
upHeadListUnderWaterDetail,
gridListUnderWaterDetail,
} = UnderWaterDetail;
// 필터링 된 후 최대, 최소, 평균 값 계산
const setFilterAfterData = (filteredData: UnderWaterInputModalProps[]) => {
let maxRealSewageInput = Number(filteredData[0]?.realSewageInput ?? 0);
let maxSewageOccur = Number(filteredData[0]?.sewageOccur ?? 0);
let maxUnderWaterInput = Number(filteredData[0]?.underWaterInput ?? 0);
let minRealSewageInput = Number(filteredData[0]?.realSewageInput ?? 0);
let minSewageOccur = Number(filteredData[0]?.sewageOccur ?? 0);
let minUnderWaterInput = Number(filteredData[0]?.underWaterInput ?? 0);
let avgRealSewageInput = 0;
let avgSewageOccur = 0;
let avgUnderWaterInput = 0;
filteredData.forEach((item: UnderWaterInputModalProps) => {
maxRealSewageInput = Math.max(Number(maxRealSewageInput), Number(item.realSewageInput));
maxSewageOccur = Math.max(Number(maxSewageOccur), Number(item.sewageOccur));
maxUnderWaterInput = Math.max(Number(maxUnderWaterInput), Number(item.underWaterInput));
minRealSewageInput = Math.min(Number(minRealSewageInput) || 0, Number(item.realSewageInput));
minSewageOccur = Math.min(Number(minSewageOccur) || 0, Number(item.sewageOccur));
minUnderWaterInput = Math.min(Number(minUnderWaterInput) || 0, Number(item.underWaterInput));
avgRealSewageInput += Number(item.realSewageInput ?? 0);
avgSewageOccur += Number(item.sewageOccur ?? 0);
avgUnderWaterInput += Number(item.underWaterInput ?? 0);
})
const newFilteredData = [
{
msrDt: '평균',
realSewageInput: filteredData.length==0?0:Math.round(Number(avgRealSewageInput) / filteredData.length * 10) / 10,
sewageOccur: filteredData.length==0?0:Math.round(Number(avgSewageOccur) / filteredData.length * 1000) / 1000 ,
underWaterInput: filteredData.length==0?0:Math.round(Number(avgUnderWaterInput) / filteredData.length * 1000) / 1000,
},
{
msrDt : '최대',
realSewageInput : Math.round(maxRealSewageInput * 10) / 10,
sewageOccur : Math.round(maxSewageOccur * 1000) / 1000 ,
underWaterInput : Math.round(maxUnderWaterInput * 1000) / 1000,
},
{
msrDt : '최소',
realSewageInput : Math.round(minRealSewageInput * 10) / 10,
sewageOccur : Math.round(minSewageOccur * 1000) / 1000 ,
underWaterInput : Math.round(minUnderWaterInput * 1000) / 1000,
},
...filteredData
];
return newFilteredData;
}
// 날짜 필터링 관련 상태
const [copyYearList, setCopyYearList] = useState<UnderWaterInputModalProps[]>([]);
useEffect(() => {
const filteredYearData = setFilterAfterData(gridListUnderWaterDetail);
setCopyYearList(filteredYearData as UnderWaterInputModalProps[]);
}, [gridListUnderWaterDetail]);
useEffect(() => {
switch (tabTerm) {
case 'tab_term_summer':
setStartYmDate(searchYear+'06');
setEndYmDate(searchYear+'08');
setPrevStartYmDate(searchYear+'06');
setPrevEndYmDate(searchYear+'08');
break;
case 'tab_term_winter':
setStartYmDate(searchYear+'01');
setEndYmDate(searchYear+'02');
setPrevStartYmDate(searchYear+'01');
setPrevEndYmDate(searchYear+'02');
break;
default:
setStartYmDate(searchYear+'01');
setEndYmDate(searchYear+'12');
setPrevStartYmDate(searchYear+'01');
setPrevEndYmDate(searchYear+'12');
}
}, [searchYear, tabTerm]);
useEffect(() => {
if(endYmDate < startYmDate) {
alert('종료일이 시작일보다 작습니다.')
// 이전 값으로 되돌리기
setStartYmDate(prevStartYmDate);
setEndYmDate(prevEndYmDate);
return;
}
// 유효한 경우 현재 값을 이전 값으로 저장
setPrevStartYmDate(startYmDate);
setPrevEndYmDate(endYmDate);
const filteredData = gridListUnderWaterDetail.filter((item: UnderWaterInputModalProps) => {
// msrDt가 "2024-01" 형태이므로 "202401" 형태로 변환
const itemDate = item.msrDt.replace('-', '');
return itemDate >= startYmDate && itemDate <= endYmDate;
});
const filteredYearData = setFilterAfterData(filteredData);
setCopyYearList(filteredYearData as UnderWaterInputModalProps[]);
}, [startYmDate, endYmDate, gridListUnderWaterDetail, prevStartYmDate, prevEndYmDate]);
return (
<>
<UITab tabList={tabList1} defaultValue="tab_term_year" onValueChange={(val) => {setTabTerm(val)}}>
<TabsContent value="tab_term_year">
<ListDiv className="!h-[100px]">
<div className="card !m-[1.2rem] max-h-[40rem] overflow-auto !p-0r">
<Table>
<thead>
<TableRow>
<TableHead className="w-[120px]">{'시설명'}</TableHead>
<TableCell className="w-[300px]">{fcltyName}</TableCell>
<TableHead className="w-[120px]">{'년/월'}</TableHead>
<TableCell className="">
<div className="flex">
<DateMonthPicker
field={{
value: startYmDate,
onChange: (value: string) => {
setStartYmDate(value);
}
}}
selectClass="ml-5 w-[100px]"
startYear={Number(searchYear)} // 시작 연도 설정
/>
<h3 className="justify-center px-5">~</h3>
<DateMonthPicker
field={{
value: endYmDate,
onChange: (value: string) => {
setEndYmDate(value);
}
}}
selectClass="ml-5 w-[100px]"
startYear={Number(searchYear)} // 시작 연도 설정
/>
</div>
</TableCell>
</TableRow>
</thead>
</Table>
</div>
</ListDiv>
<div className="overflow-y-auto h-[500px]">
<UiTable
headName={undefined}
tableData={copyYearList}
headlist={upHeadListUnderWaterDetail}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => {}}
className="h-[200px]"
boldArray={[0,1,2]}
/>
</div>
</TabsContent>
<TabsContent value="tab_term_summer">
{/* <FacilityTermModalHeader<UnderWaterInputModalProps>
searchList={gridListUnderWaterDetail}
fcltyCd={facilityCd}
fcltyName={fcltyName}
tabName="tab_term_summer"
searchYear={searchYear}
ref={ref}
queryClick={filterData}
/> */}
<ListDiv className="!h-[100px]">
<div className="card !m-[1.2rem] max-h-[40rem] overflow-auto !p-0r">
<Table>
<thead>
<TableRow>
<TableHead className="w-[120px]">{'시설명'}</TableHead>
<TableCell className="w-[300px]">{fcltyName}</TableCell>
<TableHead className="w-[120px]">{'년/월'}</TableHead>
<TableCell className="">
<div className="flex">
<DateMonthPicker
field={{
value: startYmDate,
onChange: (value: string) => {
setStartYmDate(value);
}
}}
selectClass="ml-5 w-[100px]"
startYear={Number(searchYear)} // 시작 연도 설정
/>
<h3 className="justify-center px-5">~</h3>
<DateMonthPicker
field={{
value: endYmDate,
onChange: (value: string) => {
setEndYmDate(value);
}
}}
selectClass="ml-5 w-[100px]"
startYear={Number(searchYear)} // 시작 연도 설정
/>
</div>
</TableCell>
</TableRow>
</thead>
</Table>
</div>
</ListDiv>
<div className="overflow-y-auto h-[500px]">
<UiTable
headName={undefined}
tableData={copyYearList}
headlist={upHeadListUnderWaterDetail}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => { } }
className="h-[500px]"
boldArray={[0,1,2]}
/>
</div>
</TabsContent>
<TabsContent value="tab_term_winter">
<ListDiv className="!h-[100px]">
<div className="card !m-[1.2rem] max-h-[40rem] overflow-auto !p-0r">
<Table>
<thead>
<TableRow>
<TableHead className="w-[120px]">{'시설명'}</TableHead>
<TableCell className="w-[300px]">{fcltyName}</TableCell>
<TableHead className="w-[120px]">{'년/월'}</TableHead>
<TableCell className="">
<div className="flex">
<DateMonthPicker
field={{
value: startYmDate,
onChange: (value: string) => {
setStartYmDate(value);
}
}}
selectClass="ml-5 w-[100px]"
startYear={Number(searchYear)} // 시작 연도 설정
/>
<h3 className="justify-center px-5">~</h3>
<DateMonthPicker
field={{
value: endYmDate,
onChange: (value: string) => {
setEndYmDate(value);
}
}}
selectClass="ml-5 w-[100px]"
startYear={Number(searchYear)} // 시작 연도 설정
/>
</div>
</TableCell>
</TableRow>
</thead>
</Table>
</div>
</ListDiv>
<div className="overflow-y-auto h-[500px]">
<UiTable
headName={undefined}
tableData={copyYearList}
headlist={upHeadListUnderWaterDetail}
pageSize={0}
total={0}
cellClick={undefined}
onPageChange={() => {}}
className="h-[500px]"
boldArray={[0,1,2]}
/>
</div>
</TabsContent>
</UITab>
</>
);
}
+237
View File
@@ -0,0 +1,237 @@
"use client"
import { useFoStore } from "@/app/store";
import type { TableUpperProps } from "@/app/store/common";
import { ContentsDiv, downloadExcel, MainDiv, SearchBaseForm, SearchInput, SelectBox, Title, UiModal, UiTable, type ReturnType } from "@common/business_components";
import ListDiv from "@common/business_components/ui/list_div";
import { useCallback, useEffect, useRef, useState } from "react";
import UnderWaterInputModal from "./modal/page";
import type { UnderWaterProps } from "@/app/store/underWaterInput";
export default function UnderWaterInput() {
const {Common
,UnderWater
,UnderWaterSearch
,initializeCommonData
,searchUnderWaterList
} = useFoStore((state) => state) || {
Common: { isInit: false, selectUpdownData: [], selectOperationData: [], selectSidoData: [], selectSigunData: [], selectRegionData: [], selectUnitWaterShedData: [], selectMiddleRegionData: [], selectEnvPartData: [], selectSearchYear: [{ val: "0" }], btnClass: "" },
UnderWater: { underWaterList: [], underWaterCount: 0, excelList: [], upHeadList1: [], upHeadList2: [], upHeadList3: [] },
UnderWaterSearch: { labelArray: [] },
initializeCommonData: () => {},
searchUnderWaterList: () => {}
};
const {
isInit,
selectUpdownData,
selectOperationData,
selectSidoData,
selectSigunData,
selectRegionData,
selectUnitWaterShedData,
selectMiddleRegionData,
selectEnvPartData,
selectSearchYear,
btnClass
} = Common;
const {
labelArray,
} = UnderWaterSearch;
const {
upHeadList1,
upHeadList2,
upHeadList3,
underWaterList,
underWaterCount,
excelList,
} = UnderWater;
const ref = useRef<ReturnType>({
partValue: '',
sidoValue: '',
sigunValue: '',
regionValue: '',
unitWaterShedValue: '',
middleRegionValue: '',
envPartValue: '',
sidoText: '',
sigunText: ''
});
const underWater = UnderWater?.underWaterList || [];
// 모달 상태 추가
const [selectedRow, setSelectedRow] = useState<number | null>(null);
const [fcltyNm, setFcltyNm] = useState<string>("");
// 그리드 관련
const searchInputRef = useRef<HTMLDivElement & HTMLInputElement>(null);
// 초기화 변수
const [resetPage, setResetPage] = useState<boolean>(false);
// 표 너비
const [widthSize, setWidthSize] = useState<string>("w-[120%]");
// 구분별 그리드 헤더
const [upheadList, setUpheadList] = useState<TableUpperProps[]>(upHeadList1);
// 용량별
const [fcltCpct, setFcltCpct] = useState<string>("0");
// 시설명
const [fcltyName, setFcltyName] = useState<string>("");
// 시설상태
const [operationValue, setOperationValue] = useState<string>("0");
// 조회기간
const [searchYear, setSearchYear] = useState<string>(selectSearchYear[0]?.val || "0");
/**
* 페이지 함수
*/
// 초기화
const resetVal = () => {
setWidthSize("w-[120%]");
setUpheadList(upHeadList1)
// 상태값 설정
setFcltCpct(selectUpdownData[0]?.val || "0");
setOperationValue(selectOperationData[0]?.val || "0");
setSearchYear(selectSearchYear[0]?.val || "0");
// SearchInputSelect 초기화
setFcltyName("");
if (searchInputRef.current) {
const input = searchInputRef.current.querySelector('input');
if (input) {
input.value = "";
}
}
searchUnderWaterList(1, 10, selectSearchYear[0]?.val || "0");
}
// 모달 닫기
const closeModal = () => {
setSelectedRow(null);
};
// 셀 클릭 시 모달 표시
const cellClick = (index: number) => {
if (underWater[index]) {
setFcltyNm(underWater[index].bscFctNm);
setSelectedRow(index); // 클릭한 행의 index 저장
}
};
/**
* useEffect
*/
useEffect(() => {
if (!isInit) {
initializeCommonData();
searchUnderWaterList(1, 10, selectSearchYear[0]?.val || "0");
}
}, [isInit, initializeCommonData, searchUnderWaterList, selectSearchYear]);
useEffect(() => {
if (excelList && excelList.length > 0) {
downloadExcel({
data: excelList as unknown as JSON[],
fileName: '지하수유입량 추정',
sheetNameArray: ['지하수유입량 추정'],
headerRange: 'A1:V1'
});
}
}, [excelList]);
/**
* 콜백함수
*/
// 조회
const searchFacilityPrecipitation = useCallback(({ partValue, regionValue, unitWaterShedValue, middleRegionValue, envPartValue, sidoValue, sigunValue }: ReturnType, pageNo: number, pageCnt:number=20) => {
let nameCd, partStr
switch (partValue) {
case 'R':
if (regionValue !== '0') {
if(unitWaterShedValue==='0') nameCd = regionValue;
else if (middleRegionValue==='0') nameCd = unitWaterShedValue;
else nameCd = middleRegionValue;
}
partStr = partValue
break;
default:
if(sidoValue !== '0') nameCd = sidoValue;
if(sigunValue !== '0') nameCd = sigunValue;
partStr = envPartValue !== '0' ? envPartValue:partValue;
break;
}
if (partStr.length > 1 || fcltCpct !== '0' || operationValue!== '0' ||nameCd ||(fcltyName.trim() !== "" && fcltyName.trim() !== null)) {
searchUnderWaterList(pageNo, pageCnt, searchYear, partStr, nameCd || '0', fcltCpct, operationValue, fcltyName.trim() !== "" || fcltyName.trim() !== null ? fcltyName : undefined);
} else {
searchUnderWaterList(pageNo, pageCnt, searchYear);
}
}, [fcltCpct, operationValue, fcltyName, searchUnderWaterList, searchYear]);
const chkVal = useCallback((returntype:ReturnType) => {
setWidthSize(returntype.partValue=='A' ?"w-[120%]":"w-[130%]")
setUpheadList(returntype.partValue == 'R' ? upHeadList2 : returntype.partValue == 'G'?upHeadList3:upHeadList1);
setResetPage(true);
}, [upHeadList1, upHeadList2, upHeadList3]);
// 엑셀다운로드
const excelDownload = useCallback(() => {
searchFacilityPrecipitation(ref.current, 1, 10000);
}, [searchFacilityPrecipitation]);
// 페이지 변경
const handlePageChange = useCallback((page: number) => {
setResetPage(false);
searchFacilityPrecipitation(ref.current, page, 10);
}, [searchFacilityPrecipitation]);
const mainList: UnderWaterProps[] = underWaterList || [];
const total = underWaterCount || 0;
return (
<MainDiv>
<Title title="지하수유입량 추정" />
<ContentsDiv>
<SearchBaseForm
leftChildren={<>
<SelectBox className="col-span-3" label={labelArray[3] as string} selectArray={selectUpdownData} onSelectValue={(val) => setFcltCpct(val.toString())} value={fcltCpct} selectClass={undefined} />
<SearchInput className={"col-span-3"} label={labelArray[4] as string} textValue={fcltyName} setText={setFcltyName} />
<SelectBox className="col-span-3" label={labelArray[5] as string} selectArray={selectSearchYear} onSelectValue={(val) => setSearchYear(val.toString())} value={searchYear} selectClass={undefined} />
<SelectBox className="col-span-3" label={labelArray[6] as string} selectArray={selectOperationData} onSelectValue={(val) => setOperationValue(val.toString())} value={operationValue} selectClass={undefined} />
</>}
rightChildren={undefined}
selectSidoData={selectSidoData}
selectSigunData={selectSigunData}
selectRegionData={selectRegionData}
selectUnitWaterShedData={selectUnitWaterShedData}
selectMiddleRegionData={selectMiddleRegionData}
selectEnvPartData={selectEnvPartData}
btnClass={btnClass}
queryClick={chkVal}
excelClick={excelDownload}
initClick={resetVal}
ref={ref}
isAdmin={false}
/>
<ListDiv>
<div className="h-full w-full overflow-x-scroll">
<div className={widthSize}>
<UiTable cellClick={(index: number | undefined) => index !== undefined && cellClick(index)} headName={"지하수유입량 추정"} tableData={mainList} headlist={upheadList} pageSize={10} total={total} resetPageNo={resetPage} onPageChange={handlePageChange} />
</div>
</div>
<span className="text-2xl text-gray-500">※지하수 유입량 = 하수처리장 청천시 실제유입량 - (물사용량 × 오수전환율)</span>
</ListDiv>
</ContentsDiv>
{selectedRow !== null && (
<UiModal
body={<UnderWaterInputModal fcltyName={fcltyNm} searchYear={searchYear}></UnderWaterInputModal>}
bodyClass="sm:max-w-[90vw] w-[1000px] h-[770px]"
open={selectedRow !== null}
onOpenChange={(open) => !open && closeModal()}
noHeaderYn="Y"
noFooterYn="Y"
/>
)}
</MainDiv>
);
}
+374
View File
@@ -0,0 +1,374 @@
/**====================================
* 보편적으로 쓰는 store 공간
====================================**/
import { getFetch } from "@/app/components/api";
/**
* STORE - TYPE
*/
// selectBox
export type SelectDataType = {
text: string;
val: string;
}
// grid header
export type TableUpperProps = {
id: string;
title: string;
upName?: string;
upSequnce?: number;
}
// 시도 데이터
export type sidoDataType = {
adstDstrCd: number,
sido: string,
}
// 시군구 데이터
export type sigunDataType = {
adstDstrCd: number,
gugun: string
}
// 권역 데이터
export type regionDataType = {
rvbsnCd: string,
rvbsn: string,
}
// 단위유역 데이터
export type unitWaterShedDataType = {
rvbsnCd: string,
lgbsn: string,
}
// 중권역 데이터
export type middleRegionDataType = {
rvbsnCd: string,
mdmbsn: string,
}
// 환경청 데이터
export type envPartDataType = {
lclgvrnmntCd: string,
lclgvrnmntNm: string,
adstDliStrCd: string,
adstDstrCd: string,
}
export type envPartSelectDataType = SelectDataType & {
adstDliStrCd: string,
}
/**
* STORE - DATA
*/
// 시도 데이터
let selectSidoData: SelectDataType[] = [
{ text: "전체", val: '0' }
];
// 시군구 데이터
let selectSigunData: SelectDataType[] = [
{ text: "전체", val: '0' }
];
// 권역 데이터
const selectRegionData: SelectDataType[] = [
{ text: "전체", val: '0' }
];
// 단위유역 데이터
const selectUnitWaterShedData: SelectDataType[] = [
];
// 환경청 데이터
let selectEnvPartData: envPartSelectDataType[] = [
{ text: "전체", val: '0', adstDliStrCd: '' }
];
// 환경청 내 시도 데이터
const selectEnvPartSidoData: SelectDataType[] = [
{ text: "전체", val: '0' }
];
// 환경청 내 시군구 데이터
const selectEnvPartSigunData: SelectDataType[] = [
{ text: "전체", val: '0' }
];
// 구분 데이터
// const selectPartData:SelectDataType[] = [
// { text: "행정별", val: '0' },
// { text: "유역별", val: '01' },
// { text: "환경청별", val: '02' },
// ];
// 상향 하향 데이터
const selectUpdownData:SelectDataType[] = [
{ text: "전체", val: '0' },
{ text: "500이하", val: '400' },
{ text: "500이상", val: '500' },
];
// 운영 상태 데이터
const selectOperationData:SelectDataType[] = [
{ text: "전체", val: '0' },
{ text: "가동중", val: '1' },
{ text: "재가동", val: '2' },
{ text: "가동중지", val: '3' },
{ text: "시설폐쇄", val: '4' },
];
// 시설 구분 데이터
const selectFacilityPartData:SelectDataType[] = [
{ text: "전체", val: '0' },
{ text: "공공하수처리시설", val: 'SW' },
{ text: "오수처리장", val: 'SS' },
{ text: "개인하수처리시설", val: 'PB' },
{ text: "분뇨처리장", val: 'NS' },
{ text: "폐수처리장", val: 'LW' },
{ text: "침출수처리장", val: 'LC' },
{ text: "기타기초시설", val: 'ET' },
];
// 년도 데이터
const getYearData = () => {
const year = new Date().getFullYear();
const year1 = year - 1;
const year2 = year - 2;
const year3 = year - 3;
const year4 = year - 4;
const year5 = year - 5;
return [
{ text: year1 + "년", val: year1.toString() },
{ text: year2 + "년", val: year2.toString() },
{ text: year3 + "년", val: year3.toString() },
{ text: year4 + "년", val: year4.toString() },
{ text: year5 + "년", val: year5.toString() },
]
}
const selectSearchYear:SelectDataType[] = getYearData();
// 유량 기간 구분
const selectFlowTerm : SelectDataType[] = [
{ text: "연간", val: '0' },
{ text: "하절기", val: '01' },
{ text: "동절기", val: '02' },
{ text: "청천시", val: '03' },
{ text: "강우시", val: '04' },
];
// 유량 기간 구분
const selectFlowPanel : SelectDataType[] = [
{ text: "전체", val: '0'},
{ text: "3~10mm미만", val: '2'},
{ text: "10~20mm미만", val: '3'},
{ text: "20~30mm미만", val: '4'},
{ text: "30~40mm미만", val: '5'},
{ text: "40~50mm미만", val: '6'},
{ text: "50~60mm미만", val: '7'},
{ text: "60mm이상", val: '8'},
];
// 중권역 데이터
const selectMiddleRegionData: SelectDataType[] = [
{ text: "전체", val: '0' }
];
/**
* STORE - API
*/
// 시도 API 호출 함수 추가
const fetchSidoData = async (): Promise<SelectDataType[]> => {
try {
const res = await getFetch('fo', '/addr/city');
const data = res.data;
selectSidoData = [
{ text: "전체", val: '0'},
...data.map((item: sidoDataType) => ({
text: item.sido || '',
val: String(item.adstDstrCd) || ''
}))
];
return selectSidoData;
} catch (error) {
console.error('시도 데이터를 가져오는데 실패했습니다:', error);
return selectSidoData;
}
};
// 시군구 API 호출 함수 추가
const fetchSigunData = async (): Promise<SelectDataType[]> => {
try {
const res = await getFetch('fo', '/addr/sgg');
const data = res.data;
selectSigunData = [
{ text: "전체", val: '0' },
...data
.map((item: sigunDataType) => ({
text: item.gugun || '',
val: String(item.adstDstrCd) || ''
}))
];
return selectSigunData;
} catch (error) {
console.error('시군구 데이터를 가져오는데 실패했습니다:', error);
return selectSigunData;
}
};
// 권역 API 호출 함수 추가
const fetchRegionData = async (): Promise<SelectDataType[]> => {
try {
const res = await getFetch('fo', '/addr/sarea');
const data = res.data;
return [
{ text: "전체", val: '0'},
...data.map((item: regionDataType) => ({
text: item.rvbsn || '',
val: String(item.rvbsnCd) || ''
}))
];
} catch (error) {
console.error('권역 데이터를 가져오는데 실패했습니다:', error);
return selectRegionData;
}
};
// 중권역 API 호출 함수 추가
const fetchMiddleRegionData = async (): Promise<SelectDataType[]> => {
try {
const res = await getFetch('fo', '/addr/midArea');
const data = res.data;
return [
{ text: "전체", val: '0' },
...data
.map((item: middleRegionDataType) => ({
text: item.mdmbsn || '',
val: String(item.rvbsnCd) || ''
}))
];
} catch (error) {
console.error('중권역 데이터를 가져오는데 실패했습니다:', error);
return selectMiddleRegionData;
}
};
// 단위유역 API 호출 함수 추가
const fetchUnitWaterShedData = async (): Promise<SelectDataType[]> => {
try {
const res = await getFetch('fo', '/addr/largeArea');
const data = res.data;
return [
{ text: "전체", val: '0' },
...data
.map((item: unitWaterShedDataType) => ({
text: item.lgbsn || '',
val: String(item.rvbsnCd) || ''
}))
];
} catch (error) {
console.error('단위유역 데이터를 가져오는데 실패했습니다:', error);
return selectUnitWaterShedData;
}
};
// 환경청 데이터
const fetchEnvPartData = async (): Promise<envPartSelectDataType[]> => {
try {
const res = await getFetch('fo', '/addr/govArea');
const data = res.data;
selectEnvPartData = [
{ text: "전체", val: '0', adstDliStrCd: '' },
...data
.map((item: envPartDataType) => ({
text: item.lclgvrnmntNm || '',
val: String(item.lclgvrnmntCd) || '',
adstDliStrCd: item.adstDliStrCd || ''
}))
];
return selectEnvPartData;
} catch (error) {
console.error('환경청 데이터를 가져오는데 실패했습니다:', error);
return selectEnvPartData;
}
};
/**====================================
* Store - expaort
====================================*/
export type CommonAction = {
initializeCommonData: () => Promise<void>;
}
export type CommonType = {
Common: {
isInit: boolean;
selectSidoData: SelectDataType[];
selectSigunData: SelectDataType[];
selectUpdownData: SelectDataType[];
selectOperationData: SelectDataType[];
selectFacilityPartData: SelectDataType[];
selectSearchYear: SelectDataType[];
selectUnitWaterShedData: SelectDataType[];
selectRegionData: SelectDataType[];
selectMiddleRegionData: SelectDataType[];
selectEnvPartSidoData: SelectDataType[];
selectEnvPartSigunData: SelectDataType[];
selectFlowTerm: SelectDataType[];
selectFlowPanel: SelectDataType[];
selectEnvPartData: envPartSelectDataType[];
btnClass: string;
}
}
export const commonState:CommonType = {
Common: {
isInit: false,
selectSidoData: selectSidoData,
selectSigunData: selectSigunData,
selectUpdownData: selectUpdownData,
selectOperationData: selectOperationData,
selectFacilityPartData: selectFacilityPartData,
selectSearchYear: selectSearchYear,
selectRegionData: selectRegionData,
selectUnitWaterShedData: selectUnitWaterShedData,
selectMiddleRegionData: selectMiddleRegionData,
selectEnvPartSidoData : selectEnvPartSidoData,
selectEnvPartSigunData : selectEnvPartSigunData,
selectFlowTerm: selectFlowTerm,
selectFlowPanel: selectFlowPanel,
selectEnvPartData : selectEnvPartData,
btnClass: "p-4 m-2 text-white rounded-sm text-lg font-medium"
}
}
export const CommonReducer:(set:(fn: (state: CommonType) => CommonType) => void)=>CommonAction=(set: (fn: (state: CommonType) => CommonType) => void) => {
return {
// 시도 변경 이벤트
initializeCommonData: async () => {
const promiseSet = await Promise.all([fetchSidoData(), fetchSigunData(), fetchRegionData(), fetchUnitWaterShedData(), fetchMiddleRegionData(), fetchEnvPartData()]);
const [sidoData, sggData, regionData, unitData, midData, envData] = promiseSet;
set((state: CommonType) => ({
Common: {
...state.Common,
selectSidoData: sidoData,
selectSigunData: sggData,
selectRegionData: regionData,
selectMiddleRegionData: midData,
selectUnitWaterShedData: unitData,
selectEnvPartData: envData
}
}))
},
}
}
export type CommonStore = CommonType & CommonAction;
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,307 @@
import DataForm from "@/app/components/common/DataForm";
import type { TableUpperProps } from "../dashboard";
import { getFetch } from "@/app/components/api";
export type { TableUpperProps };
/**====================================
* Store - 타입
====================================*/
/**
* (1) 메인페이지
*/
// 시설별 강수현황
type FacilityPrecipitationProps = {
bscFctCd : string,
sido : string,
gugun : string,
bscFctNm : string,
addr : string,
fctCpc : number,
stnKo : string,
infoRnDay : number,
rainEffectDay : number,
totalYearRain : number,
rvbsn : string,
lgbsn : string,
mdmbsn : string,
rgnNm : string,
}
/*
* (2) 강수현황 팝업
*/
// 강수현황
export type PrecipitationPropsDetailProps = {
bscFctCd : string,
rainDt : string,
infoRnDay : number,
rainEffectDay : number,
rainDay : number
}
export interface ExcelType {
시설코드: string,
권역:string|'',
단위유역:string|'',
중권역:string|'',
환경청:string|'',
시도: string,
시군구: string,
시설이름: string,
주소: string,
시설용량: number,
관측소정보 : string,
강우일 : number,
강우영향일 : number,
연간총강수량 : number,
}
/**====================================
* Store - data
====================================*/
/**
* (1) 메인페이지
*/
// 검색단
const LabelArray:string[] = ['구분', '시도', '시군구','용량별', '시설명', '시설상태', '조회기간', '권역', '단위유역', '중권역', '환경청별'];
// 리스트 헤더
const upHeadList1:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row: 0, rowSpan: 2},
{ id: "A", title: "행정구역", row: 0, colSpan: 2 },
{ id: "sido", title: "시도", upName:"upChangeRe", upSequnce:1, row: 1 },
{ id: "gugun", title: "시군구", upName:"upChangeRe",upSequnce:2, row: 1 },
{ id: "bscFctNm", title: "시설명", row: 0, rowSpan: 2},
{ id: "addr", title: "주소", row: 0, rowSpan: 2},
{ id: "fctCpc", title: "시설용량(m³/일)", row: 0, rowSpan: 2},
{ id: "stnKo", title: "관측소 정보", row: 0, rowSpan: 2},
{ id: "infoRnDay", title: "강우일\n(일)", row: 0, rowSpan: 2},
{ id: "rainEffectDay", title: "강우영향일\n(일)", row: 0, rowSpan: 2},
{ id: "totalYearRain", title: "연간 총 총강수량\n(mm)", row: 0, rowSpan: 2}
];
const upHeadList2:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row: 0, rowSpan: 2},
{ id: "A", title: "유역", row: 0, colSpan: 3} ,
{ id: "rvbsn", title: "권역", upName:"upChangeRe", upSequnce:1, row: 1} ,
{ id: "lgbsn", title: "단위유역", upName:"upChangeRe", upSequnce:2, row: 1},
{ id: "mdmbsn", title: "중권역", upName:"upChangeRe", upSequnce:3, row: 1},
{ id: "bscFctNm", title: "시설명", row: 0, rowSpan: 2},
{ id: "addr", title: "주소", row: 0, rowSpan: 2},
{ id: "fctCpc", title: "시설용량(m³/일)", row: 0, rowSpan: 2},
{ id: "stnKo", title: "관측소 정보", row: 0, rowSpan: 2},
{ id: "infoRnDay", title: "강우일\n(일)", row: 0, rowSpan: 2},
{ id: "rainEffectDay", title: "강우영향일\n(일)", row: 0, rowSpan: 2},
{ id: "totalYearRain", title: "연간 총 총강수량\n(mm)", row: 0, rowSpan: 2}
];
const upHeadList3:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row: 0, rowSpan: 2},
{ id: "A", title: "유역", row: 0, colSpan: 3} ,
{ id: "rvbsn", title: "권역", upName:"upChangeRe1", upSequnce:1, row: 1},
{ id: "lgbsn", title: "단위유역", upName:"upChangeRe1", upSequnce:2, row: 1},
{ id: "mdmbsn", title: "중권역", upName:"upChangeRe1", upSequnce:3, row: 1},
{ id: "rgnNm", title: "환경청", row: 0, rowSpan: 2},
{ id: "B", title: "행정구역", upName:"upChangeRe2", row: 0, colSpan: 2},
{ id: "sido", title: "시도", upName:"upChangeRe2", upSequnce:1, row: 1},
{ id: "gugun", title: "시군구", upName:"upChangeRe2",upSequnce:2, row: 1},
{ id: "bscFctNm", title: "시설명", row: 0, rowSpan: 2},
{ id: "addr", title: "주소", row: 0, rowSpan: 2},
{ id: "fctCpc", title: "시설용량(m³/일)", row: 0, rowSpan: 2},
{ id: "stnKo", title: "관측소 정보", row: 0, rowSpan: 2},
{ id: "infoRnDay", title: "강우일\n(일)", row: 0, rowSpan: 2},
{ id: "rainEffectDay", title: "강우영향일\n(일)", row: 0, rowSpan: 2},
{ id: "totalYearRain", title: "연간 총 총강수량\n(mm)", row: 0, rowSpan: 2}
];
const facilityPrecipitation:FacilityPrecipitationProps[] = [
]
/**
* 강수현황 상세팝업
*/
// 강수현황 (헤더)
const upHeadListPrecipitationDetail:TableUpperProps[] = [
{ id: "rainDt", title: "날짜"},
{ id: "infoRnDay", title: "강수량(mm/일)"},
{ id: "rainDay", title: "강우일(일)"},
{ id: "rainEffectDay", title: "강우영향일(일)"}
]
// 강수현황 (리스트)
const gridListPrecipitationDetail:PrecipitationPropsDetailProps[] = [
]
/**====================================
* Store - api
====================================*/
const fetchFacilityPrecipitationList = async (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => {
const tailStr = '/'+pageNo + '/' + pageSize + '/' + searchYear + (part?'/' + part:'' ) + (nameCd?'/' + nameCd:'' ) + (fcltCpct?'/' + fcltCpct:'' ) + (operationValue?'/' + operationValue:'' ) + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') ;
console.log('tailStr', tailStr);
const url = '/fclt/rain' + tailStr;
const res = await getFetch('fo', url);
const tmpData:PrecipitationPropsDetailProps[] = [];
if(res!= null && res.data.length > 0){
for(let idx=0; idx<res.data.length; idx++){
for(let idx2=0; idx2<res.data[idx].rainyDayObjList.length; idx2++){
const bscFctCd = res.data[idx].rainyDayObjList[idx2].bscFctCd;
const rainDt = res.data[idx].rainyDayObjList[idx2].rainDt;
const infoRnDay = res.data[idx].rainyDayObjList[idx2].infoRnDay;
const rainDay = res.data[idx].rainyDayObjList[idx2].infoRnDay>=3?1:0;
let rainEffectDay = 0;
if(idx2 < res.data[idx].rainyDayObjList.length-2){
if(rainDay==1){
rainEffectDay = 1;
} else {
if(idx2 >= 2){
if(res.data[idx].rainyDayObjList[idx2-1].infoRnDay>=3 || res.data[idx].rainyDayObjList[idx2-2].infoRnDay>=3){
rainEffectDay = 1;
}
}
}
} else if(idx2 < res.data[idx].rainyDayObjList.length-1){
if(rainDay==1 || res.data[idx].rainyDayObjList[idx2+1].infoRnDay>=3){
rainEffectDay = 1;
}
} else {
rainEffectDay = rainDay;
}
tmpData.push({
bscFctCd : bscFctCd,
rainDt : rainDt,
infoRnDay : infoRnDay,
rainDay : rainDay,
rainEffectDay : rainEffectDay
})
}
}
res.data.map((item: FacilityPrecipitationProps) => {
const filteredList = tmpData.filter((gridItem) => {
return gridItem.bscFctCd === item.bscFctCd;
});
item.rainEffectDay = filteredList.reduce((acc, curr) => acc + curr.rainEffectDay, 0);
item.totalYearRain = filteredList.reduce((acc, curr) => acc + curr.infoRnDay, 0);
item.infoRnDay = filteredList.reduce((acc, curr) => acc + curr.rainDay, 0);
})
}
return { data: res.data, count: res.count, detailData: tmpData };
}
/**====================================
* Store - expaort
====================================*/
export type FacilityPrecipitationActions = {
searchFacilityPrecipitationList: (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => void;
}
const dataFormSetting=(item : FacilityPrecipitationProps)=> {
DataForm({ type: 'number', column: 'fctCpc', item: item as unknown as Record<string, unknown> });
DataForm({ type: 'float1', column: 'totalYearRain', item: item as unknown as Record<string, unknown>});
};
export type FacilityPrecipitationType = {
FacilityPrecipitationSearch: {
isInit: boolean;
labelArray:string[];
},
FacilityPrecipitation : {
isInit: boolean;
upHeadList1: TableUpperProps[];
upHeadList2: TableUpperProps[];
upHeadList3: TableUpperProps[];
facilityPrecipitation: FacilityPrecipitationProps[];
excelList: ExcelType[];
facilityPrecipitationCount: number;
},
PrecipitationDetail : {
isInit : boolean;
upHeadListPrecipitationDetail : TableUpperProps[];
gridListPrecipitationDetail : PrecipitationPropsDetailProps[];
}
}
export const facilityPrecipitationInitState: FacilityPrecipitationType = {
FacilityPrecipitationSearch: {
isInit: false,
labelArray: LabelArray,
},
FacilityPrecipitation : {
isInit : false,
upHeadList1 : upHeadList1,
upHeadList2 : upHeadList2,
upHeadList3 : upHeadList3,
facilityPrecipitation : facilityPrecipitation,
excelList : [],
facilityPrecipitationCount : 0
},
PrecipitationDetail : {
isInit : false,
upHeadListPrecipitationDetail : upHeadListPrecipitationDetail,
gridListPrecipitationDetail : gridListPrecipitationDetail
}
}
export type FacilityPrecipitationStore = FacilityPrecipitationType & FacilityPrecipitationActions;
export const facilityPrecipitationReducer:(set:(fn: (state: FacilityPrecipitationType) => FacilityPrecipitationType) => void)=>FacilityPrecipitationActions=(set: (fn: (state: FacilityPrecipitationType) => FacilityPrecipitationType) => void) => {
return {
searchFacilityPrecipitationList: async (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => {
const res = await fetchFacilityPrecipitationList(pageNo, pageSize, searchYear, part, nameCd, fcltCpct, operationValue, fcltyNm);
const data = res.data;
const excelDownList: ExcelType[] = [];
if(pageSize>5000){
data.forEach((item: FacilityPrecipitationProps) => {
dataFormSetting(item)
item.fctCpc = item.fctCpc ? parseFloat(item.fctCpc.toString().replace(/,/g, '')) : 0;
item.totalYearRain = item.totalYearRain ? parseFloat(item.totalYearRain.toString().replace(/,/g, '')) : 0;
excelDownList.push({
시설코드: item.bscFctCd,
권역: item.rvbsn,
단위유역: item.lgbsn,
중권역: item.mdmbsn,
환경청: item.rgnNm,
시도: item.sido,
시군구: item.gugun,
시설이름: item.bscFctNm,
주소: item.addr,
시설용량: item.fctCpc,
관측소정보: item.stnKo,
강우일: item.infoRnDay,
강우영향일: item.rainEffectDay,
연간총강수량: item.totalYearRain,
})
})
}else{
data.forEach((item: FacilityPrecipitationProps) => {
dataFormSetting(item)
item.fctCpc = item.fctCpc ? parseFloat(item.fctCpc.toString().replace(/,/g, '')) : 0;
item.totalYearRain = item.totalYearRain ? parseFloat(item.totalYearRain.toString().replace(/,/g, '')) : 0;
})
}
set((state:FacilityPrecipitationType) => ({
...state,
FacilityPrecipitation: pageSize<5000?{
...state.FacilityPrecipitation,
facilityPrecipitation: res.data,
facilityPrecipitationCount: res.count,
}:{
...state.FacilityPrecipitation,
excelList: excelDownList,
},
PrecipitationDetail:{
...state.PrecipitationDetail,
gridListPrecipitationDetail: res.detailData
}
}))
}
}
}
@@ -0,0 +1,659 @@
import { getFetch } from "@/app/components/api";
import DataForm from "@/app/components/common/DataForm";
import type { TableUpperProps } from "../dashboard";
export type { TableUpperProps };
/**====================================
* Store - 타입
*
* tn, tp는 소수점 3자리까지, 나머지는 소수점 1자리까지
====================================*/
/**
* (1) 메인페이지
*/
// 시설수질리스트 타입
export interface FacilityWaterQltyProps {
bscFctCd: string,
bscFctNm: string,
msrDt: string
sidoNm: string,
sggNm: string,
addr: string,
fctCpc: number,
inflAssctAftWqltBod: string | '',
inflAssctAftWqltToc: string | '',
inflAssctAftWqltSs: string | '',
inflAssctAftWqltTn: string | '',
inflAssctAftWqltTp: string | '',
inflAssctBfrWqltTp: string | '',
efflWqltBod: string | '',
efflWqltCod: string | '',
efflWqltSs: string | '',
efflWqltTn: string | '',
efflWqltTp: string | '',
rgnNm:string|'',
rvbsn:string|'',
lgbsn:string|'',
mdmbsn:string|'',
inflAmnt: number | '',
lclgvrnmntNm: string,
infoRnDay: number | '',
rainEffectDay: number | '',
tpInflWqltTmprtr: number | '',
tpInflWqltPh: string | '',
tpInflWqltBod: string | '',
tpInflWqltCod: string | '',
tpInflWqltSs: string | '',
tpInflWqltTn: string | '',
tpInflWqltTp: string | '',
tpInflWqltEclgyTox: string | '',
tpInflWqltClfgrpcnt: string | '',
}
export interface ExcelType {
시설코드: string,
권역:string|'',
단위유역:string|'',
중권역:string|'',
환경청:string|'',
시도: string,
시군구: string,
시설이름: string,
주소: string,
용량: number,
유입BOD: string | '',
유입TOC: string | '',
유입SS: string | '',
유입TN: string | '',
유입TP: string | '',
유출BOD: string | '',
유출TOC: string | '',
유출SS: string | '',
유출TN: string | '',
유출TP: string | '',
}
/**
* (2) 모달페이지
*/
// 시설수질리스트 타입
export type FacilityWaterQltyTmsProps = {
part: string,
toc: number,
ss: number,
tn: number,
tp: number,
}
// API 응답 데이터 타입
export interface WaterQualityHistoryResponse {
data: {
wqltObjs: FacilityWaterQltyProps[];
};
}
export type modalExcelType = {
날짜: string;
"유입BOD(mg/L)": string;
"유입TOC(COD)(mg/L)": string;
"유입SS(mg/L)": string;
"유입TN(mg/L)": string;
"유입TP(mg/L)": string;
"유출BOD(mg/L)": string;
"유출TOC(COD)(mg/L)": string;
"유출SS(mg/L)": string;
"유출TN(mg/L)": string;
"유출TP(mg/L)": string;
"강우량(일)": string;
}
/**====================================
* Store - data
====================================*/
/**
* (1) 메인페이지
*/
// 메인 라벨 배열
const mainLabelArray: string[] = ['구분', '시도', '시군구', '용량별', '시설명', '조회기간', '시설상태', '권역', '단위유역', '중권역', '환경청별'];
// 시설수질리스트 헤더(행정별)
const upHeadList1: TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", rowSpan: 2, row:0 },
{ id: "C", title: "행정명", colSpan: 2, row: 0 },
{ id: "sidoNm", title: "시도", row: 1 },
{ id: "sggNm", title: "시군구", row: 1 },
{ id: "bscFctNm", title: "시설명", rowSpan: 2, row: 0 },
{ id: "addr", title: "주소", rowSpan: 2, row: 0 },
{ id: "fctCpc", title: "시설용량(m³/일)", align: "right", rowSpan: 2, row: 0 },
{ id: "A", title: "유입", colSpan: 5, row: 0 },
{ id: "B", title: "방류", colSpan: 5, row: 0 },
{ id: "inflAssctAftWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe3", upSequnce: 1, align: "right", row: 1 },
{ id: "inflAssctAftWqltToc", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe3", upSequnce: 2, align: "right", row: 1 },
{ id: "inflAssctAftWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row: 1 },
{ id: "inflAssctAftWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe3", upSequnce: 4, align: "right", row: 1 },
{ id: "inflAssctAftWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row: 1 },
{ id: "efflWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe4", upSequnce: 1, align: "right", row: 1 },
{ id: "efflWqltCod", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, align: "right", row: 1 },
{ id: "efflWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row: 1 },
{ id: "efflWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe4", upSequnce: 4, align: "right", row: 1 },
{ id: "efflWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row: 1 },
];
// 시설수질리스트 헤더(권역별)
const upHeadList2: TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 },
{ id: "C", title: "유역", colSpan: 3, row: 0 },
{ id: "rvbsn", title: "권역", upName: "upChangeRe1", upSequnce: 1, row:1 },
{ id: "lgbsn", title: "단위유역", upName: "upChangeRe1", upSequnce: 2, row:1 },
{ id: "mdmbsn", title: "중권역", upName: "upChangeRe1", upSequnce: 3, row:1 },
{ id: "bscFctNm", title: "시설명", row:0, rowSpan:2 },
{ id: "addr", title: "주소", row:0, rowSpan:2 },
{ id: "fctCpc", title: "시설용량(m³/L)", align: "right", row:0, rowSpan:2 },
{ id: "A", title: "유입", colSpan: 5, row: 0 },
{ id: "B", title: "방류", colSpan: 5, row: 0 },
{ id: "inflAssctAftWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe3", upSequnce: 1, align: "right", row:1 },
{ id: "inflAssctAftWqltToc", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe3", upSequnce: 2, align: "right", row:1 },
{ id: "inflAssctAftWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row:1 },
{ id: "inflAssctAftWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe3", upSequnce: 2, align: "right", row:1 },
{ id: "inflAssctAftWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row:1 },
{ id: "efflWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe4", upSequnce: 1, align: "right", row:1 },
{ id: "efflWqltCod", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, align: "right", row:1 },
{ id: "efflWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row:1 },
{ id: "efflWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, align: "right", row:1 },
{ id: "efflWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row:1 },
];
// 시설수질리스트 헤더(환경청별)
const upHeadList3: TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 },
{ id: "C", title: "유역", colSpan: 3, row: 0 },
{ id: "rvbsn", title: "권역", row:1 },
{ id: "lgbsn", title: "단위유역", row:1 },
{ id: "mdmbsn", title: "중권역", row:1 },
{ id: "rgnNm", title: "환경청", row:0, rowSpan:2 },
{ id: "D", title: "행정명", colSpan: 2, row: 0 },
{ id: "sidoNm", title: "시도", upName: "upChangeRe2", row:1 },
{ id: "sggNm", title: "시군구", upName: "upChangeRe2", row:1 },
{ id: "bscFctNm", title: "시설명", row:0, rowSpan:2 },
{ id: "addr", title: "주소", row:0, rowSpan:2 },
{ id: "fctCpc", title: "시설용량(m³/L)", align: "right", row:0, rowSpan:2 },
{ id: "A", title: "유입", colSpan: 5, row: 0 },
{ id: "B", title: "방류", colSpan: 5, row: 0 },
{ id: "inflAssctAftWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe3", upSequnce: 1, align: "right", row:1 },
{ id: "inflAssctAftWqltToc", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe3", upSequnce: 2, align: "right", row:1 },
{ id: "inflAssctAftWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row:1 },
{ id: "inflAssctAftWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe3", upSequnce: 2, align: "right", row:1 },
{ id: "inflAssctAftWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe3", upSequnce: 3, align: "right", row:1 },
{ id: "efflWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe4", upSequnce: 1, align: "right", row:1 },
{ id: "efflWqltCod", title: "TOC\n(COD)\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, align: "right", row:1 },
{ id: "efflWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row:1 },
{ id: "efflWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe4", upSequnce: 2, align: "right", row:1 },
{ id: "efflWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe4", upSequnce: 3, align: "right", row:1 },
];
/**
* (2) 모달페이지
*/
// 처리시설 수질현황 헤더(모달-연간)
const upHeadListModalYear: TableUpperProps[] = [
{ id: "msrDt", title: "날짜" },
{ id: "inflAssctAftWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe1", upSequnce: 1, align: "right" },
{ id: "inflAssctAftWqltToc", title: "TOC(COD)\n(mg/L)", upName: "upChangeRe1", upSequnce: 2, align: "right" },
{ id: "inflAssctAftWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe1", upSequnce: 3, align: "right" },
{ id: "inflAssctAftWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe1", upSequnce: 2, align: "right" },
{ id: "inflAssctBfrWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe1", upSequnce: 3, align: "right" },
{ id: "efflWqltBod", title: "BOD\n(mg/L)", upName: "upChangeRe2", upSequnce: 1, align: "right" },
{ id: "efflWqltCod", title: "TOC(COD)\n(mg/L)", upName: "upChangeRe2", upSequnce: 2, align: "right" },
{ id: "efflWqltSs", title: "SS\n(mg/L)", upName: "upChangeRe2", upSequnce: 3, align: "right" },
{ id: "efflWqltTn", title: "T-N\n(mg/L)", upName: "upChangeRe2", upSequnce: 2, align: "right" },
{ id: "efflWqltTp", title: "T-P\n(mg/L)", upName: "upChangeRe2", upSequnce: 3, align: "right" },
{ id: "infoRnDay", title: "강우량", align: "right" },
];
// 처리시설 수질현황 헤더(모달-수질TMS)
const upHeadListModalTms: TableUpperProps[] = [
{ id: "part", title: "구분" },
{ id: "toc", title: "TOC(mg/L)", upName: "upChangeRe", upSequnce: 1, align: "right" },
{ id: "ss", title: "SS(mg/L)", upName: "upChangeRe", upSequnce: 2, align: "right" },
{ id: "tn", title: "T-N(mg/L)", upName: "upChangeRe", upSequnce: 3, align: "right" },
{ id: "tp", title: "T-P(mg/L)", upName: "upChangeRe", upSequnce: 4, align: "right" },
];
// 처리시설 수질현황 데이터(모달-연간)
const facilityWaterQltyYearList: FacilityWaterQltyProps[] = [
];
// 처리시설 수질현황 데이터(모달-수질TMS)
const facilityWaterQltyTmsList: FacilityWaterQltyTmsProps[] = [
];
// 총인설비 수질현황 헤더 데이터
const upHeadListModalChongIn: TableUpperProps[] = [
{ id: "msrDt", title: "날짜" },
{ id: "tpInflWqltBod", title: "BOD(mg/L)", upName: "upChangeRe1", upSequnce: 1, align: "right" },
{ id: "tpInflWqltTp", title: "T-P(mg/L)", upName: "upChangeRe1", upSequnce: 2, align: "right" },
{ id: "efflWqltBod", title: "BOD(mg/L)", upName: "upChangeRe2", upSequnce: 1, align: "right" },
{ id: "efflWqltTp", title: "T-P(mg/L)", upName: "upChangeRe2", upSequnce: 2, align: "right" },
];
/**====================================
* Store - api (데이터 호출)
====================================*/
// 메인페이지
// 시설정보 수질 리스트 데이터 호출
const fetchFacilityWaterQltyList = async (pageNo: number, pageSize: number, searchYear: string, partCd?: string|undefined, nameCd?: string|undefined, fcltCpct?: string|undefined, operationValue?: string|undefined, fcltyNm?: string | undefined): Promise<{ data: FacilityWaterQltyProps[], count: number }> => {
try {
const tailStr = partCd ? '/'+ partCd + '/' + nameCd + '/' + fcltCpct + '/' + operationValue + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : '';
const url = '/fclt/qlty/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr;
const res = await getFetch('fo', url);
return { data: res.data, count: res.count }
} catch (error) {
console.error('시설정보 수질현황 데이터를 가져오는데 실패했습니다:', error);
return { data: [], count: 0 };
}
}
// 시설정보 수질 리스트 데이터 호출(모달-수질TMS)
const fetchFacilityWaterQltyTmsList = async (facilityCd: string|undefined) => {
try {
const res = await getFetch('wr', '/il/getTms/' + facilityCd);
return { data: res.data, count: res.count };
} catch (error) {
console.error('시설정보 수질현황 TMS 데이터를 가져오는데 실패했습니다:', error);
return { data: [], count: 0 };
}
};
// 시설정보 수질 리스트 데이터 호출(모달-이력)
const fetchFacilityWaterQltyListHist = async (searchYear: string|undefined, facilityCd: string|undefined): Promise<WaterQualityHistoryResponse | null> => {
try {//
const res = await getFetch('fo', '/fclt/qlty/hist/' + searchYear + '/' + facilityCd);
return res;
} catch (error) {
console.error('시설정보 수질현황 이력 데이터를 가져오는데 실패했습니다:', error);
return null;
}
};
/**====================================
* Store - export
====================================*/
export type FacilityWaterQltyType = {
FacilityWaterQltyMain: {
facilityWaterQltyList: FacilityWaterQltyProps[];
excelList: ExcelType[];
facilityWaterQltyCount: number;
isInit: boolean;
mainLabelArray: string[];
upHeadList1: TableUpperProps[];
upHeadList2: TableUpperProps[];
upHeadList3: TableUpperProps[];
},
FacilityWaterQltyModal: {
// 처리시설 수질현황
upHeadListModalYear: TableUpperProps[];
facilityWaterQltyYearList: FacilityWaterQltyProps[];
upHeadListModalTms: TableUpperProps[];
facilityWaterQltyTmsList: FacilityWaterQltyTmsProps[];
// 수질tms
tmsList: FacilityWaterQltyTmsProps[];
msrmtDate: string;
// 총인시설 수질현황
upHeadListModalChongIn: TableUpperProps[];
}
}
export const facilityWaterQltyInitState: FacilityWaterQltyType = {
FacilityWaterQltyMain: {
facilityWaterQltyCount: 0,
facilityWaterQltyList: [],
excelList: [],
isInit: false,
mainLabelArray: mainLabelArray,
upHeadList1: upHeadList1,
upHeadList2: upHeadList2,
upHeadList3: upHeadList3,
},
FacilityWaterQltyModal: {
// 처리시설 수질현황
upHeadListModalYear: upHeadListModalYear,
facilityWaterQltyYearList: facilityWaterQltyYearList,
upHeadListModalTms: upHeadListModalTms,
facilityWaterQltyTmsList: facilityWaterQltyTmsList,
// 수질tms
tmsList: [],
msrmtDate: '',
// 총인시설 수질현황
upHeadListModalChongIn: upHeadListModalChongIn,
}
}
export type FacilityWaterQltyActions = {
/**
* 시설정보 수질현황 리스트 데이터 호출(구분 선택 후 시설명 있을 경우)
* @param pageNo : 페이지 번호
* @param pageSize : 페이지 크기
* @param searchYear : 검색 년도
* @param part : 구분
* @param nameCd : 시설명
* @param fcltCpct : 시설용량
* @param operationValue : 운영상태코드
* @param fcltyNm : 시설명
*/
searchFacilityWaterQltyList: (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => Promise<void>;
/**
* 시설정보 수질현황 이력 데이터 호출
* @param searchYear : 검색 년도
* @param facilityCd : 시설코드
*/
searchFacilityWaterQltyListHist: (searchYear: string, facilityCd: string) => Promise<void>;
}
/**
* @param item : 시설수질리스트 타입
* @returns : 없음
* @description : 시설수질리스트 타입의 데이터를 표현 방식으로 바꿔주는 함수
* - msrDt : YYYY-MM-DD 형식으로 변환
* - inflAssctAftWqltBod : 소수점 1자리까지 표현
* - inflAssctAftWqltTn : 소수점 1자리까지 표현
* - inflAssctBfrWqltTp : 소수점 1자리까지 표현
* - efflWqltBod : 소수점 2자리까지 표현
* - efflWqltTn : 소수점 2자리까지 표현
* - efflWqltTp : 소수점 2자리까지 표현
* @author : 박진우
* @date : 2025-04-24
*/
const dataFormSetting = (item: FacilityWaterQltyProps) => {
DataForm({ type: 'date', column: 'msrDt', item: item as unknown as Record<string, unknown> });
}
export type FacilityWaterQltyStore = FacilityWaterQltyType & FacilityWaterQltyActions;
export const facilityWaterQltyReducer = (set: (fn: (state: FacilityWaterQltyType) => FacilityWaterQltyType) => void): FacilityWaterQltyActions => {
return {
// 메인페이지
/**
* 시설정보 수질현황 리스트 데이터 호출(구분 선택 후 시설명 있을 경우)
* @param pageNo : 페이지 번호
* @param pageSize : 페이지 크기
* @param searchYear : 검색 년도
* @param part : 구분
* @param nameCd : 시설명
* @param fcltCpct : 시설용량
* @param operationValue : 운영상태코드
* @param fcltyNm : 시설명
*/
searchFacilityWaterQltyList: async (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => {
const res = await fetchFacilityWaterQltyList(pageNo, pageSize, searchYear, part, nameCd, fcltCpct, operationValue, fcltyNm);
const rtnData = res.data.map(item => {
dataFormSetting(item)
if(item.inflAssctAftWqltTn) {
item.inflAssctAftWqltTn = Number(Math.round(Number(item.inflAssctAftWqltTn) * 1000)/1000).toFixed(3);
}
if(item.efflWqltTn){
item.efflWqltTn = Number(Math.round(Number(item.efflWqltTn) * 1000)/1000).toFixed(3);
}
if(item.inflAssctAftWqltTp){
item.inflAssctAftWqltTp = Number(Math.round(Number(item.inflAssctAftWqltTp) * 1000)/1000).toFixed(3);
}
if(item.efflWqltTp){
item.efflWqltTp = Number(Math.round(Number(item.efflWqltTp) * 1000)/1000).toFixed(3);
}
if(item.inflAssctAftWqltToc){
item.inflAssctAftWqltToc = Number(Math.round(Number(item.inflAssctAftWqltToc) * 10)/10).toFixed(1);
}
if(item.efflWqltCod){
item.efflWqltCod = Number(Math.round(Number(item.efflWqltCod) * 10)/10).toFixed(1);
}
if(item.inflAssctAftWqltBod){
item.inflAssctAftWqltBod = Number(Math.round(Number(item.inflAssctAftWqltBod) * 10)/10).toFixed(1);
}
if(item.efflWqltBod){
item.efflWqltBod = Number(Math.round(Number(item.efflWqltBod) * 10)/10).toFixed(1);
}
if(item.inflAssctAftWqltSs){
item.inflAssctAftWqltSs = Number(Math.round(Number(item.inflAssctAftWqltSs) * 10)/10).toFixed(1);
}
if(item.efflWqltSs){
item.efflWqltSs = Number(Math.round(Number(item.efflWqltSs) * 10)/10).toFixed(1);
}
if(item.inflAssctAftWqltToc){
item.inflAssctAftWqltToc = Number(Math.round(Number(item.inflAssctAftWqltToc) * 10)/10).toFixed(1);
}
if(item.efflWqltCod){
item.efflWqltCod = Number(Math.round(Number(item.efflWqltCod) * 10)/10).toFixed(1);
}
return item;
});
const excelDownList: ExcelType[] = [];
if(pageSize>5000){
res.data.forEach(item => {
dataFormSetting(item)
item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, ''));
item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || '';
item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || '';
excelDownList.push({
시설코드: item.bscFctCd,
권역: item.rvbsn,
단위유역: item.lgbsn,
중권역: item.mdmbsn,
환경청: item.rgnNm,
시도: item.sidoNm,
시군구: item.sggNm,
시설이름: item.bscFctNm,
주소: item.addr,
용량: item.fctCpc,
유입BOD: item.inflAssctAftWqltBod,
유입TOC: item.inflAssctAftWqltToc,
유입SS: item.inflAssctAftWqltSs,
유입TN: item.inflAssctAftWqltTn,
유입TP: item.inflAssctAftWqltTp,
유출BOD: item.efflWqltBod,
유출TOC: item.efflWqltCod,
유출SS: item.efflWqltSs,
유출TN: item.efflWqltTn,
유출TP: item.efflWqltTp,
});
});
}else{
res.data.forEach((item: FacilityWaterQltyProps) => {
dataFormSetting(item)
item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, ''));
item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || '';
item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || '';
});
}
set((state: FacilityWaterQltyType) => ({
...state,
FacilityWaterQltyMain: pageSize<5000?{
...state.FacilityWaterQltyMain,
facilityWaterQltyList: rtnData,
facilityWaterQltyCount: res.count,
}:{
...state.FacilityWaterQltyMain,
excelList: excelDownList,
}
}))
},
searchFacilityWaterQltyListHist: async (searchYear: string, facilityCd: string) => {
const res = await fetchFacilityWaterQltyListHist(searchYear, facilityCd);
const res1 = await fetchFacilityWaterQltyTmsList(facilityCd);
let rtnMsrmtDate = '';
const rtnTmsList: FacilityWaterQltyTmsProps[] = [];
if(res1.data){
rtnTmsList.push({
part: '평균',
toc: res1.data.tocAvgVl,
ss: res1.data.ssAvgVl,
tn: res1.data.tnAvgVl,
tp: res1.data.tpAvgVl,
});
rtnTmsList.push({
part: '최대',
toc: res1.data.tocMaxVl,
ss: res1.data.ssMaxVl,
tn: res1.data.tnMaxVl,
tp: res1.data.tpMaxVl,
});
rtnTmsList.push({
part: '최소',
toc: res1.data.tocMinVl,
ss: res1.data.ssMinVl,
tn: res1.data.tnMinVl,
tp: res1.data.tpMinVl,
});
rtnMsrmtDate = res1.data.msrmtDate;
}else{
rtnTmsList.push({
part: '평균',
toc: 0,
ss: 0,
tn: 0,
tp: 0,
});
rtnTmsList.push({
part: '최대',
toc: 0,
ss: 0,
tn: 0,
tp: 0,
});
rtnTmsList.push({
part: '최소',
toc: 0,
ss: 0,
tn: 0,
tp: 0,
});
rtnMsrmtDate = '';
}
if(Array.isArray(res) || !res || !('data' in res) || !res.data || !res.data.wqltObjs || res.data.wqltObjs.length === 0) {
set((state: FacilityWaterQltyType) => ({
...state,
FacilityWaterQltyModal: {
...state.FacilityWaterQltyModal,
facilityWaterQltyYearList: [],
tmsList : rtnTmsList,
msrmtDate : rtnMsrmtDate
}
}))
return;
}
const tmpData:FacilityWaterQltyProps[] = [];
// facilityPrecipitation의 rainEffectDay 계산 로직을 적용
for(let idx = 0; idx < res.data.wqltObjs.length; idx++) {
const item = res.data.wqltObjs[idx];
if (!item) continue;
const rainDay = Number(item.infoRnDay) >= 3 ? 1 : 0;
let rainEffectDay = 0;
if(idx < res.data.wqltObjs.length - 2) {
if(rainDay == 1) {
rainEffectDay = 1;
} else {
if(idx >= 2) {
const prevItem1 = res.data.wqltObjs[idx-1];
const prevItem2 = res.data.wqltObjs[idx-2];
if((prevItem1 && Number(prevItem1.infoRnDay) >= 3) || (prevItem2 && Number(prevItem2.infoRnDay) >= 3)) {
rainEffectDay = 1;
}
}
}
} else if(idx < res.data.wqltObjs.length - 1) {
const nextItem = res.data.wqltObjs[idx+1];
if(rainDay == 1 || (nextItem && Number(nextItem.infoRnDay) >= 3)) {
rainEffectDay = 1;
}
} else {
rainEffectDay = rainDay;
}
tmpData.push({
...item,
rainEffectDay: rainEffectDay,
} as FacilityWaterQltyProps);
}
const rtnData = tmpData.map((item: FacilityWaterQltyProps) => {
dataFormSetting(item)
if(item.inflAssctAftWqltTn) {
item.inflAssctAftWqltTn = Number(Math.round(Number(item.inflAssctAftWqltTn) * 1000)/1000).toFixed(3);
}
if(item.efflWqltTn){
item.efflWqltTn = Number(Math.round(Number(item.efflWqltTn) * 1000)/1000).toFixed(3);
}
if(item.inflAssctBfrWqltTp){
item.inflAssctBfrWqltTp = Number(Math.round(Number(item.inflAssctBfrWqltTp) * 1000)/1000).toFixed(3);
}
if(item.efflWqltTp){
item.efflWqltTp = Number(Math.round(Number(item.efflWqltTp) * 1000)/1000).toFixed(3);
}
if(item.inflAssctAftWqltToc){
item.inflAssctAftWqltToc = Number(Math.round(Number(item.inflAssctAftWqltToc) * 10)/10).toFixed(1);
}
if(item.efflWqltCod){
item.efflWqltCod = Number(Math.round(Number(item.efflWqltCod) * 10)/10).toFixed(1);
}
if(item.inflAssctAftWqltBod){
item.inflAssctAftWqltBod = Number(Math.round(Number(item.inflAssctAftWqltBod) * 10)/10).toFixed(1);
}
if(item.efflWqltBod){
item.efflWqltBod = Number(Math.round(Number(item.efflWqltBod) * 10)/10).toFixed(1);
}
if(item.inflAssctAftWqltSs){
item.inflAssctAftWqltSs = Number(Math.round(Number(item.inflAssctAftWqltSs) * 10)/10).toFixed(1);
}
if(item.efflWqltSs){
item.efflWqltSs = Number(Math.round(Number(item.efflWqltSs) * 10)/10).toFixed(1);
}
if(item.inflAssctAftWqltToc){
item.inflAssctAftWqltToc = Number(Math.round(Number(item.inflAssctAftWqltToc) * 10)/10).toFixed(1);
}
if(item.efflWqltCod){
item.efflWqltCod = Number(Math.round(Number(item.efflWqltCod) * 10)/10).toFixed(1);
}
if(item.tpInflWqltBod){
item.tpInflWqltBod = item.tpInflWqltBod?Number(Math.round(Number(item.tpInflWqltBod) * 10)/10).toFixed(1):Number(item.tpInflWqltBod)===0?'0.0':'';
}else{
item.tpInflWqltBod = '';
}
if(item.tpInflWqltTp){
item.tpInflWqltTp = item.tpInflWqltTp?Number(Math.round(Number(item.tpInflWqltTp) * 1000)/1000).toFixed(3): Number(item.tpInflWqltTp)===0?'0.000':'';
}else{
item.tpInflWqltTp = '';
}
return item;
})
set((state: FacilityWaterQltyType) => ({
...state,
FacilityWaterQltyModal: {
...state.FacilityWaterQltyModal,
facilityWaterQltyYearList: rtnData,
tmsList : rtnTmsList,
msrmtDate : rtnMsrmtDate
}
}))
},
}
}
+366
View File
@@ -0,0 +1,366 @@
import { getFetch } from "@/app/components/api";
import type { TableUpperProps } from "../dashboard";
import DataForm from "@/app/components/common/DataForm";
/**====================================
* Store - 타입
====================================*/
/**
* (1) 메인페이지
*/
const FlowRateSearchLabelArray:string[] = ['구분', '시도', '시군구', '용량별', '시설명', '조회기간', '시설상태', '권역', '단위유역', '중권역', '환경청별'];
export type flowRateListProps = {
bscFctCd : string,
sidoNm : string,
sggNm : string,
bscFctNm : string,
daddr : string,
fctCpc : number,
lclgvrnmntNm : string,
totInflAmnt : number | '',
inflAmnt : number | '',
sumFlow : number | '',
returnAmnt : number | '',
efflAmnt : number | '',
rgnNm : string,
rvbsn : string,
lgbsn : string,
mdmbsn : string,
infoRnDay : number | '',
}
export interface ExcelType {
시설코드: string,
권역:string|'',
단위유역:string|'',
중권역:string|'',
시도: string,
시군구: string,
시설이름: string,
주소: string,
용량: number,
총유입량: number | '',
하수유입량: number | '',
연계처리량: number | '',
총인반류수량: number | '',
방류량: number | '',
환경청:string|'',
}
export type modalExcelType = {
날짜: string,
"총유입량(m³/일)": number | '',
"하수유입량(m³/일)": number | '',
"연계처리량(m³/일)": number | '',
"총인반류수량(m³/일)": number | '',
"방류량(m³/일)": number | '',
"강우량": number | string | '',
}
/**
* (2) 모달
*/
export interface FlowRateModalInterface {
msrDt : string,
totInflAmnt : number | '',
inflAmnt : number | '',
sumFlow : number | '',
returnAmnt : number | '',
efflAmnt : number | '',
infoRnDay : number | string | '',
rainEffectDay : number | string | '' ,
}
export type FlowRateModalProps = {
msrDt : string,
totInflAmnt : number,
inflAmnt : number,
sumFlow : number,
returnAmnt : number,
efflAmnt : number,
infoRnDay : number | string | '',
rainEffectDay : number | string | '',
}
/**====================================
* Store - data
====================================*/
/**
* (1) 메인페이지
*/
// 리스트 (행정별)
const upHeadList1:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2},
{ id: "A", title: "행정명", row: 0 ,colSpan: 2 },
{ id: "sidoNm", title: "시도", upName:"upChangeRe", row:1 },
{ id: "sggNm", title: "시군구", upName:"upChangeRe", row:1 },
{ id: "bscFctNm", title: "시설명", row: 0, rowSpan: 2},
{ id: "daddr", title: "주소", row: 0, rowSpan: 2},
{ id: "fctCpc", title: "시설용량\n(m³/일)", align:"right", row: 0, rowSpan: 2},
{ id: "totInflAmnt", title: "총 유입입량\n(m³/일)", align:"right", row: 0, rowSpan: 2},
{ id: "inflAmnt", title: "하수유입량\n(m³/일)", align:"right", row: 0, rowSpan: 2},
{ id: "sumFlow", title: '연계처리량\n(m³/일)', align:"right", row: 0, rowSpan: 2},
{ id: "returnAmnt", title: '총인 반류수량\n(m³/일)', align:"right", row: 0, rowSpan: 2},
{ id: "efflAmnt", title: '방류량\n(m³/일)', align:"right", row: 0, rowSpan: 2}
];
// 리스트 (권역별)
const upHeadList2:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2},
{ id: "A", title: "유역", row: 0, colSpan: 3},
{ id: "rvbsn", title: "권역", upName:"upChangeRe", row:1 },
{ id: "lgbsn", title: "단위유역", upName:"upChangeRe", row:1 },
{ id: "mdmbsn", title: "중권역", upName:"upChangeRe", row:1 },
{ id: "bscFctNm", title: "시설명", row: 0, rowSpan: 2},
{ id: "daddr", title: "주소", row: 0, rowSpan: 2},
{ id: "fctCpc", title: "시설용량\n(m³/일)", align:"right", row: 0, rowSpan: 2},
{ id: "totInflAmnt", title: "총 유입입량\n(m³/일)", align:"right", row: 0, rowSpan: 2},
{ id: "inflAmnt", title: "하수유입량\n(m³/일)", align:"right", row: 0, rowSpan: 2},
{ id: "sumFlow", title: '연계처리량\n(m³/일)', align:"right", row: 0, rowSpan: 2},
{ id: "returnAmnt", title: '총인 반류수량\n(m³/일)', align:"right", row: 0, rowSpan: 2},
{ id: "efflAmnt", title: '방류량\n(m³/일)', align:"right", row: 0, rowSpan: 2}
];
// 리스트 (환경청별별)
const upHeadList3:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 },
{ id: "A", title: "유역", row: 0, colSpan: 3 },
{ id: "rvbsn", title: "권역", upName:"upChangeRe1", row:1 },
{ id: "lgbsn", title: "단위유역", upName:"upChangeRe1", row:1 },
{ id: "mdmbsn", title: "중권역", upName:"upChangeRe1", row:1 },
{ id: "rgnNm", title: "환경청", row:0, rowSpan:2 },
{ id: "B", title: "행정명", row: 0 ,colSpan: 2 },
{ id: "ctpvNm", title: "시도", upName:"upChangeRe2", row:1 },
{ id: "sggNm", title: "시군구", upName:"upChangeRe2", row:1 },
{ id: "bscFctNm", title: "시설명", row: 0, rowSpan: 2 },
{ id: "daddr", title: "주소", row: 0, rowSpan: 2 },
{ id: "fctCpc", title: "시설용량\n(m³/일)", align:"right", row: 0, rowSpan: 2 },
{ id: "totInflAmnt", title: "총 유입입량\n(m³/일)", align:"right", row: 0, rowSpan: 2 },
{ id: "inflAmnt", title: "하수유입량\n(m³/일)", align:"right", row: 0, rowSpan: 2 },
{ id: "sumFlow", title: '연계처리량\n(m³/일)', align:"right", row: 0, rowSpan: 2 },
{ id: "returnAmnt", title: '총인 반류수량\n(m³/일)', align:"right", row: 0, rowSpan: 2 },
{ id: "efflAmnt", title: '방류량\n(m³/일)', align:"right", row: 0, rowSpan: 2 }
];
const flowRateList:flowRateListProps[] = [
]
/**
* (2) 모달
*/
const upHeadListFlowRateModal:TableUpperProps[] = [
{ id: "msrDt", title: "날짜"},
{ id: "totInflAmnt", title: "총 유입량(m³/일)", align:"right"},
{ id: "inflAmnt", title: "하수유입량(m³/일)", align:"right"},
{ id: "sumFlow", title: "연계처리량(m³/일)", align:"right"},
{ id: "returnAmnt", title: "총인 반류수량(m³/일)", align:"right"},
{ id: "efflAmnt", title: "방류량(m³/일)", align:"right"},
{ id: "infoRnDay", title: "강우량", align:"right"}
]
const modalFlowRateListYear:FlowRateModalProps[] = [
]
/**====================================
* Store - API
====================================*/
const fetchFlowRateList = async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => {
try {
const tailStr = partCd ? '/'+ partCd + '/' + nameCd + '/' + fcltCpct + '/' + operationValue + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : ''; const url = '/fclt/flow/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr;
const res = await getFetch('fo', url);
console.log(url);
return { data: res.data, count: res.count }
} catch (error) {
console.error('시설정보 유량현황 데이터를 가져오는데 실패했습니다:', error);
return { data: [], count: 0 };
}
}
const fetchFlowRateModalList = async (bscFctCd: string, searchYear: string) => {
try {
const url = '/fclt/flow/' + bscFctCd + '/' + searchYear;
const res = await getFetch('fo', url);
// msrDt 오름차순으로 정렬
const sortedData = res.data.sort((a: FlowRateModalInterface, b: FlowRateModalInterface) => {
return new Date(a.msrDt).getTime() - new Date(b.msrDt).getTime();
});
return { data: sortedData }
} catch (error) {
console.error('시설정보 유량현황 모달 데이터를 가져오는데 실패했습니다:', error);
return { data: [] }
}
}
/**====================================
* Store - expaort
====================================*/
export type FlowRateSearchActions = {
searchFlowRateList: (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => void;
searchFlowRateModalList : (bscFctCd: string, searchYear: string) => void;
}
export type FlowRateSearchType = {
FlowRateSearch: {
isInit: boolean;
flowRateSearchLabelArray:string[];
},
FlowRateList : {
isInit : boolean;
upHeadList1: TableUpperProps[];
upHeadList2: TableUpperProps[];
upHeadList3: TableUpperProps[];
flowRateList:flowRateListProps[];
flowRateCount: number;
excelList: ExcelType[];
},
FlowRateModal : {
isInit : boolean;
modalFlowRateListYear : FlowRateModalProps[];
upHeadListFlowRateModalYear : TableUpperProps[];
}
}
export type FlowRateSearchStore = FlowRateSearchType & FlowRateSearchActions;
const dataFormSetting=(item : flowRateListProps | FlowRateModalProps)=> {
DataForm({ type: 'date', column: 'msrDt', item: item as unknown as Record<string, unknown> });
DataForm({ type: 'float1', column: 'totInflAmnt', item: item as unknown as Record<string, unknown>});
DataForm({ type: 'float1', column: 'inflAmnt', item: item as unknown as Record<string, unknown>});
DataForm({ type: 'float1', column: 'sumFlow', item: item as unknown as Record<string, unknown>});
DataForm({ type: 'float1', column: 'efflWqltBod', item: item as unknown as Record<string, unknown>});
DataForm({ type: 'float1', column: 'returnAmnt', item: item as unknown as Record<string, unknown>});
DataForm({ type: 'float1', column: 'efflAmnt', item: item as unknown as Record<string, unknown>});
}
export const flowRateSearchInitState:FlowRateSearchType = {
FlowRateSearch: {
isInit: false,
flowRateSearchLabelArray: FlowRateSearchLabelArray,
},
FlowRateList : {
isInit : false,
upHeadList1 : upHeadList1,
upHeadList2 : upHeadList2,
upHeadList3 : upHeadList3,
flowRateList : flowRateList,
flowRateCount : 0,
excelList: []
},
FlowRateModal : {
isInit : false,
upHeadListFlowRateModalYear : upHeadListFlowRateModal,
modalFlowRateListYear : modalFlowRateListYear,
}
}
export const flowRateSearchReducer:(set:(fn: (state: FlowRateSearchType) => FlowRateSearchType) => void)=>FlowRateSearchActions=(set: (fn: (state: FlowRateSearchType) => FlowRateSearchType) => void) => {
return {
searchFlowRateList: async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => {
const res = await fetchFlowRateList(pageNo, pageSize, searchYear, partCd, nameCd, fcltCpct, operationValue, fcltyNm);
const data = res.data;
const excelDownList: ExcelType[] = [];
const rtnData = data.map((item: flowRateListProps) => {
item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, ''));
item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || '';
item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || '';
item.totInflAmnt = Number(item.inflAmnt || 0) + Number(item.sumFlow || 0) + Number(item.returnAmnt || 0)
dataFormSetting(item);
return item;
});
if(pageSize>5000){
rtnData.forEach((item: flowRateListProps) => {
excelDownList.push({
시설코드: item.bscFctCd,
권역: item.rvbsn,
단위유역: item.lgbsn,
중권역: item.mdmbsn,
환경청: item.rgnNm,
시도: item.sidoNm,
시군구: item.sggNm,
시설이름: item.bscFctNm,
주소: item.daddr,
용량: item.fctCpc,
총유입량: item.totInflAmnt,
하수유입량: item.inflAmnt,
연계처리량: item.sumFlow,
총인반류수량: item.returnAmnt,
방류량: item.efflAmnt,
});
});
}
console.log('excelDownList' , excelDownList);
set((state: FlowRateSearchType) => ({
...state,
FlowRateList: pageSize<5000?{
...state.FlowRateList,
flowRateList: rtnData,
flowRateCount: res.count,
}:{
...state.FlowRateList,
excelList: excelDownList,
}
}))
},
searchFlowRateModalList: async (bscFctCd: string, searchYear: string) => {
const res = await fetchFlowRateModalList(bscFctCd, searchYear);
const data = res.data;
const tmpData:FlowRateModalProps[] = [];
// facilityPrecipitation의 rainEffectDay 계산 로직을 적용
for(let idx = 0; idx < data.length; idx++) {
const item = data[idx];
const rainDay = Number(item.infoRnDay) >= 3 ? 1 : 0;
let rainEffectDay = 0;
if(idx < data.length - 2) {
if(rainDay == 1) {
rainEffectDay = 1;
} else {
if(idx >= 2) {
if(Number(data[idx-1].infoRnDay) >= 3 || Number(data[idx-2].infoRnDay) >= 3) {
rainEffectDay = 1;
}
}
}
} else if(idx < data.length - 1) {
if(rainDay == 1 || Number(data[idx+1].infoRnDay) >= 3) {
rainEffectDay = 1;
}
} else {
rainEffectDay = rainDay;
}
tmpData.push({
...item,
rainEffectDay: rainEffectDay,
});
}
const rtnData = tmpData.map((item: FlowRateModalProps) => {
item.totInflAmnt = Number(item.inflAmnt || 0) + Number(item.sumFlow || 0) + Number(item.returnAmnt || 0)
dataFormSetting(item);
return item;
});
set((state: FlowRateSearchType) => ({
...state,
FlowRateModal: {
...state.FlowRateModal,
modalFlowRateListYear: rtnData || [],
}
}))
}
}
}
+45
View File
@@ -0,0 +1,45 @@
'use client'
import { type ReactNode, createContext, useContext, useRef } from 'react'
import { useStore } from 'zustand'
import { type FoStore, StoreZus } from './store'
export type StoreApi = ReturnType<typeof StoreZus>
export const StoreContext = createContext<StoreApi | undefined>(
undefined,
)
export interface StoreProviderProps {
children: ReactNode
}
export const StoreProvider = ({
children,
}: StoreProviderProps) => {
const storeRef = useRef<StoreApi>()
if (!storeRef.current) {
const store = StoreZus()
storeRef.current = store
}
return (
<StoreContext.Provider value={storeRef.current}>
{children}
</StoreContext.Provider>
)
}
export const useFoStore = <T,>(
selector: (store: FoStore) => T,
): T => {
const storeContext = useContext(StoreContext)
if (!storeContext) {
throw new Error(`useStore must be used within StoreProvider`)
}
return useStore(storeContext, selector)
}
+226
View File
@@ -0,0 +1,226 @@
import DataForm from "@/app/components/common/DataForm";
import type { TableUpperProps } from "../dashboard";
import { getFetch } from "@/app/components/api";
export type { TableUpperProps };
/**====================================
* Store - 타입
====================================*/
/**
* (1) 메인페이지
*/
// 시설별 강수현황
type PlanSewageProps = {
bscFctCd : string,
sido : string,
gugun : string,
bscFctNm : string,
daddr : string,
fctCpc : number,
spareCapacity : number,
variableLoadPercent : number,
classifiedSewerPercent : number,
rgnNm : string,
rvbsn : string,
lgbsn : string,
mdmbsn : string,
sprtProcZoneAr : number,
procZoneSm : number,
avgInflAmnt : number,
avgInflAmntSunny : number,
maxInflAmntSunny : number,
}
/**====================================
* Store - data
====================================*/
/**
* (1) 메인페이지
*/
// 검색단
const LabelArray:string[] = ['구분', '시도', '시군구','용량별', '시설명', '조회기간', '시설상태', '권역', '단위유역', '중권역', '환경청별'];
// 리스트 헤더
const upHeadList1:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 },
{ id: "A", title: "행정구역", upSequnce:1, row:0, colSpan:2 },
{ id: "sido", title: "시도", upName:"upChangeRe", upSequnce:1, row:1 },
{ id: "gugun", title: "시군구", upName:"upChangeRe",upSequnce:2, row:1 },
{ id: "bscFctNm", title: "시설명", row:0, rowSpan:2 },
{ id: "daddr", title: "주소", row:0, rowSpan:2 },
{ id: "fctCpc", title: "시설용량(m³/일)", row:0, rowSpan:2, align:'right' },
{ id: "spareCapacity", title: "여유용량(m³/일)", row:0, rowSpan:2, align:'right' },
{ id: "variableLoadPercent", title: "변동부하율(%)", row:0, rowSpan:2, align:'right' },
{ id: "classifiedSewerPercent", title: "분류식화율(%)", row:0, rowSpan:2, align:'right' },
];
const upHeadList2:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 },
{ id: "A", title: "유역", upName:"upChangeRe", upSequnce:1, row:0, colSpan:3 },
{ id: "rvbsn", title: "권역", upName:"upChangeRe", upSequnce:1, row:1},
{ id: "lgbsn", title: "단위유역", upName:"upChangeRe", upSequnce:2, row:1},
{ id: "mdmbsn", title: "중권역", upName:"upChangeRe", upSequnce:3, row:1},
{ id: "bscFctNm", title: "시설명", row:0, rowSpan:2 },
{ id: "daddr", title: "주소", row:0, rowSpan:2 },
{ id: "fctCpc", title: "시설용량(m³/일)", row:0, rowSpan:2, align:'right' },
{ id: "spareCapacity", title: "여유용량(m³/일)", row:0, rowSpan:2, align:'right' },
{ id: "variableLoadPercent", title: "변동부하율(%)", row:0, rowSpan:2, align:'right' },
{ id: "classifiedSewerPercent", title: "분류식화율(%)", row:0, rowSpan:2, align:'right' },
];
const upHeadList3:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 },
{ id: "A", title: "유역", upName:"upChangeRe", upSequnce:1, row:0, colSpan:3 },
{ id: "rvbsn", title: "권역", upName:"upChangeRe1", upSequnce:1, row:1},
{ id: "lgbsn", title: "단위유역", upName:"upChangeRe1", upSequnce:2, row:1},
{ id: "mdmbsn", title: "중권역", upName:"upChangeRe1", upSequnce:3, row:1},
{ id: "rgnNm", title: "환경청", row:0, rowSpan:2 },
{ id: "B", title: "행정구역", upName:"upChangeRe", upSequnce:1, row:0, colSpan:2 },
{ id: "sido", title: "시도", upName:"upChangeRe2", upSequnce:1, row:1 },
{ id: "gugun", title: "시군구", upName:"upChangeRe2",upSequnce:2, row:1 },
{ id: "bscFctNm", title: "시설명", row:0, rowSpan:2 },
{ id: "daddr", title: "주소", row:0, rowSpan:2 },
{ id: "fctCpc", title: "시설용량(m³/일)", row:0, rowSpan:2, align:'right' },
{ id: "spareCapacity", title: "여유용량(m³/일)", row:0, rowSpan:2, align:'right' },
{ id: "variableLoadPercent", title: "변동부하율(%)", row:0, rowSpan:2, align:'right' },
{ id: "classifiedSewerPercent", title: "분류식화율(%)", row:0, rowSpan:2, align:'right' },
];
const planSewageList:PlanSewageProps[] = [
]
/**====================================
* Store - api
====================================*/
const fetchPlanSewageList = async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => {
const tailStr = partCd ? '/'+ partCd + '/' + (nameCd || '0') + '/' + (fcltCpct || '0') + '/' + (operationValue || '0') +'/0'+ (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : '';
const url = '/prcsFclt/info/plan/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr;
console.log(url);
const res = await getFetch('fo', url);
return { data: res.data, count: res.count }
}
/**====================================
* Store - expaort
====================================*/
export type ExcelType = {
시설코드: string;
권역:string|'',
단위유역:string|'',
중권역:string|'',
환경청:string|'',
시도: string;
시군구: string;
시설이름: string;
주소: string;
시설용량: number;
여유용량: number;
변동부하율 : number;
분류식화율 : number;
}
const dataFormSetting=(item : PlanSewageProps)=> {
DataForm({ type: 'float1', column: 'fctCpc', item: item as unknown as Record<string, unknown> });
DataForm({ type: 'float1', column: 'spareCapacity', item: item as unknown as Record<string, unknown>});
DataForm({ type: 'float1', column: 'variableLoadPercent', item: item as unknown as Record<string, unknown>});
DataForm({ type: 'float1', column: 'classifiedSewerPercent', item: item as unknown as Record<string, unknown>});
};
export type PlanSewageActions = {
searchPlanSewageList: (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => void;
}
export type PlanSewageType = {
PlanSewageSearch: {
isInit: boolean;
labelArray:string[];
},
PlanSewage : {
isInit: boolean;
upHeadList1: TableUpperProps[];
upHeadList2: TableUpperProps[];
upHeadList3: TableUpperProps[];
planSewageList: PlanSewageProps[];
planSewageCount: number;
excelList: ExcelType[];
},
}
export const planSewageInitState: PlanSewageType = {
PlanSewageSearch: {
isInit: false,
labelArray: LabelArray,
},
PlanSewage : {
isInit : false,
upHeadList1 : upHeadList1,
upHeadList2 : upHeadList2,
upHeadList3 : upHeadList3,
planSewageList : planSewageList,
planSewageCount : 0,
excelList : []
},
}
export type PlanSewageStore = PlanSewageType & PlanSewageActions;
export const planSewageReducer:(set:(fn: (state: PlanSewageType) => PlanSewageType) => void)=>PlanSewageActions=(set: (fn: (state: PlanSewageType) => PlanSewageType) => void) => {
return {
searchPlanSewageList: async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => {
const res = await fetchPlanSewageList(pageNo, pageSize, searchYear, partCd, nameCd, fcltCpct, operationValue, fcltyNm);
const excelDownList: ExcelType[] = [];
console.log('res' , res.data);
/**
* 결과데이터 계산
* (1) 여유용량 = 시설용량 - 건기시 평균유량
* (2) 변동부하율 = 건기시 일최대 유량/일평균유량(기준 최근 3개년)
* (3) 분류식화율 = 분류식지역 면적(㎢)/처리구역 면적합계(㎢) ×100
*/
const resultData = res.data.map((item:PlanSewageProps) => {
item.spareCapacity = Number(item.fctCpc) - Number(item.avgInflAmntSunny);
item.variableLoadPercent = item.maxInflAmntSunny && item.maxInflAmntSunny!==0 ? Number(item.maxInflAmntSunny) / Number(item.avgInflAmntSunny) : 0;
item.classifiedSewerPercent = item.sprtProcZoneAr && item.sprtProcZoneAr!==0 ? Number(item.sprtProcZoneAr) / Number(item.procZoneSm) * 100 : 0;
return item;
});
resultData.forEach((item:PlanSewageProps) => {
dataFormSetting(item)
})
if(pageSize>5000){
resultData.forEach((item:PlanSewageProps) => {
excelDownList.push({
시설코드: item.bscFctCd,
권역: item.lgbsn,
단위유역: item.rvbsn,
중권역: item.mdmbsn,
환경청: item.rgnNm,
시설이름: item.bscFctNm,
시도: item.sido,
시군구: item.gugun,
주소: item.daddr,
시설용량: item.fctCpc,
여유용량: item.spareCapacity,
변동부하율: item.variableLoadPercent,
분류식화율: item.classifiedSewerPercent,
})
})
}
set((state:PlanSewageType) => ({
...state,
PlanSewage: pageSize<5000?{
...state.PlanSewage,
planSewageList: resultData,
planSewageCount: res.count,
}:{
...state.PlanSewage,
excelList: excelDownList,
}
}))
}
}
}
+507
View File
@@ -0,0 +1,507 @@
import { getFetchAreaList, postFetch } from "@/app/components/api";
import type { TableUpperProps } from "../dashboard";
import DataForm from "@/app/components/common/DataForm";
export type { TableUpperProps };
/**====================================
* Store - 타입
====================================*/
/**
* (1) 메인페이지
*/
// 처리구역 리스트
export type ProcessAreaProps = {
swtrmtFcltCd : string,
sido : string,
gugun : string,
swtrmtFcltNm : string,
location : string,
fctCpc : number | '',
ruse : boolean,
yrDt4 : string,
yr : string,
yrDc1 : string,
yrDc2 : string,
yrDc3 : string,
yrDc4 : string,
/**
* 증설계획
*/
// 기승인(당초4단계), 현재, 변경1~4
planSwageQntSumDt4 : number | '',
planSwageQntSum : number | '',
planSwageQntSumDc1 : number | '',
planSwageQntSumDc2 : number | '',
planSwageQntSumDc3 : number | '',
planSwageQntSumDc4 : number | '',
/**
* 처리인구
*/
// 기승인(당초4단계), 변경1~4
swtrmtPpltnCntDt4 : number | '',
swtrmtPpltnCnt : number | '',
swtrmtPpltnCntDc1 : number | '',
swtrmtPpltnCntDc2 : number | '',
swtrmtPpltnCntDc3 : number | '',
swtrmtPpltnCntDc4 : number | '',
/**
* 처리구역면적
*/
// 기승인(당초4단계), 변경1~4
swtrmtZoneAreaDt4 : number | '',
swtrmtZoneArea : number | '',
swtrmtZoneAreaDc1 : number | '',
swtrmtZoneAreaDc2 : number | '',
swtrmtZoneAreaDc3 : number | '',
swtrmtZoneAreaDc4 : number | '',
// 처리구역
trtDstr : string | '',
}
export interface ExcelType {
시설코드: string,
시설명: string,
주소: string,
시설용량: number | '',
증설계획기존: number | '',
증설계획현재: number | '',
증설계획1단계: number | '',
증설계획2단계: number | '',
증설계획3단계: number | '',
증설계획4단계: number | '',
처리인구기존: number | '',
처리인구현재: number | '',
처리인구1단계: number | '',
처리인구2단계: number | '',
처리인구3단계: number | '',
처리인구4단계: number | '',
처리구역기존: number | '',
처리구역현재: number | '',
처리구역1단계: number | '',
처리구역2단계: number | '',
처리구역3단계: number | '',
처리구역4단계: number | '',
처리구역: string | ''
}
// (2) 처리구역 팝업
// 시설증설 계획
export type FcltyImproveInfoProps = {
planSwageQntSumDt4 : number | '',
planSwageQntSum : number | '',
planSwageQntSumDc1 : number | '',
planSwageQntSumDc2 : number | '',
planSwageQntSumDc3 : number | '',
planSwageQntSumDc4 : number | '',
}
// 처리인구 계획
export type ProcessPopulationProps = {
swtrmtPpltnCntDt4 : number | '',
swtrmtPpltnCnt : number | '',
swtrmtPpltnCntDc1 : number | '',
swtrmtPpltnCntDc2 : number | '',
swtrmtPpltnCntDc3 : number | '',
swtrmtPpltnCntDc4 : number | '',
}
// 처리구역 계획
export type ProcessAreaPlanProps = {
swtrmtZoneAreaDt4 : number | '',
swtrmtZoneArea : number | '',
swtrmtZoneAreaDc1 : number | '',
swtrmtZoneAreaDc2 : number | '',
swtrmtZoneAreaDc3 : number | '',
swtrmtZoneAreaDc4 : number | '',
trtDstr : string,
}
/**====================================
* Store - data
====================================*/
/**
* (1) 메인페이지
*/
// 검색단
const ProcessFacilityLabelArray:string[] = ['구분', '시도', '시군구','용량별', '시설명', '시설상태', '연도별', '권역', '단위유역', '중권역', '환경청별'];
// 리스트 헤더(행정별)
const upHeadList1:TableUpperProps[] = [
{ id: "swtrmtFcltCd", title: "시설코드", rowSpan:3, row:0},
{ id: "A", title: "행정구역", colSpan:2, rowSpan:2, row:0},
{ id: "sido", title: "시도", upName:"upChangeRe", row:2 },
{ id: "sigungo", title: "시군구", upName:"upChangeRe", row:2 },
{ id: "swtrmtFcltNm", title: "시설명", rowSpan:3, row:0},
{ id: "location", title: "주소", rowSpan:3, row:0, },
{ id: "fctCpc", title: "시설용량(m³/일)", align:"right", rowSpan:3, row:0},
{ id: "B", title: "시설계획", colSpan:19, row:0},
{ id: "C", title: "증설계획(m³/일)", colSpan:6, row:1},
{ id: "planSwageQntSumDt4", title: "기승인\n4단계", row:2},
{ id: "planSwageQntSum", title: "현재", row:2},
{ id: "planSwageQntSumDc1", title: "1단계", row:2},
{ id: "planSwageQntSumDc2", title: "2단계", row:2},
{ id: "planSwageQntSumDc3", title: "3단계", row:2},
{ id: "planSwageQntSumDc4", title: "4단계", row:2},
{ id: "D", title: "처리인구(인)", colSpan:6, row:1},
{ id: "swtrmtPpltnCntDt4", title: "기승인\n4단계", row:2},
{ id: "swtrmtPpltnCnt", title: "현재", row:2},
{ id: "swtrmtPpltnCntDc1", title: "1단계", row:2},
{ id: "swtrmtPpltnCntDc2", title: "2단계", row:2},
{ id: "swtrmtPpltnCntDc3", title: "3단계", row:2},
{ id: "swtrmtPpltnCntDc4", title: "4단계", row:2},
{ id: "E", title: "처리구역(km²)", colSpan:7, row:1},
{ id: "swtrmtZoneAreaDt4", title: "기승인\n4단계", row:2},
{ id: "swtrmtZoneArea", title: "현재", row:2},
{ id: "swtrmtZoneAreaDc1", title: "1단계", row:2},
{ id: "swtrmtZoneAreaDc2", title: "2단계", row:2},
{ id: "swtrmtZoneAreaDc3", title: "3단계", row:2},
{ id: "swtrmtZoneAreaDc4", title: "4단계", row:2},
{ id: "trtDstr", title: "처리구역", row:2}
];
const processAreaList:ProcessAreaProps[] = [
]
/**
* 처리구역정보 상세팝업
*/
// 시설증설 계획 (헤더)
const upHeadListFcltyImproveInfo:TableUpperProps[] = [
{ id: "planSwageQntSumDt4", title: "기승인\n4단계(m³/일)", rowSpan:2, row:0},
{ id: "A", title: "증설계획(m³/일)", colSpan:5, row:0},
{ id: "planSwageQntSum", title: "현재", row:1},
{ id: "planSwageQntSumDc1", title: "1단계", row:1},
{ id: "planSwageQntSumDc2", title: "2단계", row:1},
{ id: "planSwageQntSumDc3", title: "3단계", row:1},
{ id: "planSwageQntSumDc4", title: "4단계", row:1},
];
// 시설증설 계획 (리스트)
const gridListFcltyImproveInfo:FcltyImproveInfoProps[] = [
];
// 처리인구 계획 (헤더)
const upHeadListProcessPopulation:TableUpperProps[] = [
{ id: "swtrmtPpltnCntDt4", title: "기승인\n4단계(인)", rowSpan:2, row:0},
{ id: "A", title: "처리인구(인)", colSpan:5, row:0},
{ id: "swtrmtPpltnCnt", title: "현재", row:1},
{ id: "swtrmtPpltnCntDc1", title: "1단계", row:1},
{ id: "swtrmtPpltnCntDc2", title: "2단계", row:1},
{ id: "swtrmtPpltnCntDc3", title: "3단계", row:1},
{ id: "swtrmtPpltnCntDc4", title: "4단계", row:1},
];
// 처리인구 계획 (리스트)
const gridListProcessPopulation:ProcessPopulationProps[] = [
];
// 처리구역 계획 (헤더)
const upHeadListProcessAreaPlan:TableUpperProps[] = [
{ id: "swtrmtZoneAreaDt4", title: "기승인\n4단계(km²)", rowSpan:2, row:0},
{ id: "A", title: "처리면적(km²)", colSpan:6, row:0},
{ id: "swtrmtZoneArea", title: "현재", row:1},
{ id: "swtrmtZoneAreaDc1", title: "1단계", row:1},
{ id: "swtrmtZoneAreaDc2", title: "2단계", row:1},
{ id: "swtrmtZoneAreaDc3", title: "3단계", row:1},
{ id: "swtrmtZoneAreaDc4", title: "4단계", row:1},
{ id: "trtDstr", title: "처리구역", row:1}
];
// 시설증설 계획 (리스트)
const gridListProcessAreaPlan:ProcessAreaPlanProps[] = [
];
/**====================================
* Store - API
=====================================*/
// 시설 상세 정보 조회 API
const fetchFacilityDetailInfo = async (swtrmtFcltCd: string) => {
try {
const tailUrl = '/1/5/A/0/0/0/SW';
const res = await postFetch('fo', '/prcsFclt/ruseInfo'+tailUrl, [swtrmtFcltCd]);
return { data:res.data, count:res.count };
} catch (error) {
console.error('시설정보 데이터를 가져오는데 실패했습니다:', error);
return {data: [], count: 0};
}
};
// 시설정보 리스트 데이터 호출(구분 선택 후 시설명 있을 경우)
const fetchProcessAreaList = async (pageNo : number, pageSize : number, ctpvNm : string | undefined, sggNm : string | undefined, targetYear : string | undefined ): Promise<{data:ProcessAreaProps[], count:number}> => {
try {
targetYear = targetYear ? targetYear : "2024";
const baseUrl = 'https://apigateway.rsonesoft.com/dc-service/api/v1/nowDmgMtncGnsvy';
const queryParams = new URLSearchParams();
if (ctpvNm && ctpvNm !== '전체') queryParams.append('ctpvNm', ctpvNm);
if (sggNm && sggNm !== '전체') queryParams.append('sggNm', sggNm);
if (targetYear) queryParams.append('targetYear',targetYear);
const queryString = queryParams.toString();
const url = queryString ? `${baseUrl}?${queryString}` : baseUrl;
const res = await getFetchAreaList(url);
if(res && res.length > 0){
const enrichedData = await Promise.all(
res.map(async (item: ProcessAreaProps) => {
upHeadList1.forEach((head:TableUpperProps) => {
if (head.id === 'planSwageQntSum' || head.id === 'swtrmtPpltnCnt' || head.id === 'swtrmtZoneArea') {
head.title = '현재\n(' + item.yr + '년)';
} else if(head.id === 'planSwageQntSumDc1' || head.id === 'swtrmtPpltnCntDc1' || head.id === 'swtrmtZoneAreaDc1') {
head.title = '1단계\n(' + item.yrDc1 + '년)';
} else if(head.id === 'planSwageQntSumDc2' || head.id === 'swtrmtPpltnCntDc2' || head.id === 'swtrmtZoneAreaDc2') {
head.title = '2단계\n(' + item.yrDc2 + '년)';
} else if(head.id === 'planSwageQntSumDc3' || head.id === 'swtrmtPpltnCntDc3' || head.id === 'swtrmtZoneAreaDc3') {
head.title = '3단계\n(' + item.yrDc3 + '년)';
} else if(head.id === 'planSwageQntSumDc4' || head.id === 'swtrmtPpltnCntDc4' || head.id === 'swtrmtZoneAreaDc4') {
head.title = '4단계\n(' + item.yrDc4 + '년)';
} else if(head.id === 'planSwageQntSumDt4' || head.id === 'swtrmtPpltnCntDt4' || head.id === 'swtrmtZoneAreaDt4') {
head.title = '기승인\n4단계(' + item.yrDt4 + '년)';
}
});
const res = await fetchFacilityDetailInfo(item.swtrmtFcltCd);
const detailInfo = res.data.filter((item:ProcessAreaProps) => item.ruse === true);
return {
...item,
sido: detailInfo.length > 0 ? detailInfo[0].sido||'' : '',
sigungo: detailInfo.length > 0 ? detailInfo[0].gugun||'' : '',
location: detailInfo.length > 0 ? detailInfo[0].addr||'' : '',
fctCpc: detailInfo.length > 0 ? detailInfo[0].fctCpc||'' : 0,
trtDstr: detailInfo.length > 0 ? detailInfo[0].trtDstr||'' : '',
};
})
);
const startIndex = (pageNo - 1) * pageSize;
const endIndex = startIndex + pageSize;
const paginatedData = enrichedData.slice(startIndex, endIndex);
return {data: paginatedData, count: enrichedData.length};
}else{
return {data: [], count: 0};
}
} catch (error) {
console.error('시설계획정보를 가져오는데 실패했습니다:', error);
return {data: [], count: 0};
}
};
/**====================================
* Store - expaort
=====================================*/
export type ProcessAreaActions = {
searchProcessAreaList : (pageNo: number, pageSize: number, ctpvNm?:string, sggNm?:string, targetYear?:string) => void;
initModalData : (swtrmtFcltCd:string) => void;
}
export type ProcessAreaType = {
ProcessAreaSearch: {
isInit: boolean;
processFacilityLabelArray:string[];
},
ProcessArea : {
isInit: boolean;
upHeadList1: TableUpperProps[];
processAreaList: ProcessAreaProps[];
processAreaCount: number;
excelList: ExcelType[];
},
ProcessAreaDetail : {
isInit: boolean;
upHeadListFcltyImproveInfo: TableUpperProps[];
gridListFcltyImproveInfo: FcltyImproveInfoProps[]; // Changed from facilityAreaProps to ProcessAreaProps
upHeadListProcessPopulation: TableUpperProps[];
gridListProcessPopulation: ProcessPopulationProps[]; // Changed from facilityAreaProps to ProcessAreaProps
upHeadListProcessAreaPlan: TableUpperProps[];
gridListProcessAreaPlan: ProcessAreaPlanProps[]; // Changed from facilityAreaProps to ProcessAreaProps
},
}
export const processAreaInitState: ProcessAreaType = {
ProcessAreaSearch: {
isInit: false,
processFacilityLabelArray: ProcessFacilityLabelArray,
},
ProcessArea : {
isInit : false,
upHeadList1 : upHeadList1,
processAreaList : processAreaList,
processAreaCount : 0,
excelList : []
},
ProcessAreaDetail : {
isInit : false,
upHeadListFcltyImproveInfo : upHeadListFcltyImproveInfo,
gridListFcltyImproveInfo : gridListFcltyImproveInfo,
upHeadListProcessPopulation : upHeadListProcessPopulation,
gridListProcessPopulation : gridListProcessPopulation,
upHeadListProcessAreaPlan : upHeadListProcessAreaPlan,
gridListProcessAreaPlan : gridListProcessAreaPlan,
},
}
// 상태 설정 함수 타입 정의
type SetStateFunction = (state: ProcessAreaType | ((state: ProcessAreaType) => ProcessAreaType)) => void;
export type ProcessAreaStore = ProcessAreaType & ProcessAreaActions;
export const processAreaReducer:(set:SetStateFunction)=>ProcessAreaActions=(set: SetStateFunction) => {
return {
/**
* 시설정보 리스트 데이터 호출
* @param pageNo : 페이지 번호
* @param pageSize : 페이지 크기
*/
searchProcessAreaList: async ( pageNo:number, pageSize:number, ctpvNm?:string, sggNm?:string, targetYear?:string) => {
const res = await fetchProcessAreaList(pageNo, pageSize, ctpvNm, sggNm, targetYear);
res.data.forEach((item:ProcessAreaProps) => {
DataForm({ type: 'float1', column: 'fctCpc', item:item as unknown as Record<string, unknown>});
})
const excelDownList: ExcelType[] = [];
if(pageSize>5000){
res.data.forEach(item => {
item.fctCpc = item.fctCpc ? parseFloat(item.fctCpc.toString().replace(/,/g, '')) : '';
excelDownList.push({
시설코드: item.swtrmtFcltCd,
시설명: item.swtrmtFcltNm,
주소: item.location,
시설용량: item.fctCpc,
증설계획기존: item.planSwageQntSumDt4,
증설계획현재: item.planSwageQntSum,
증설계획1단계: item.planSwageQntSumDc1,
증설계획2단계: item.planSwageQntSumDc2,
증설계획3단계: item.planSwageQntSumDc3,
증설계획4단계: item.planSwageQntSumDc4,
처리인구기존: item.swtrmtPpltnCntDt4,
처리인구현재: item.swtrmtPpltnCnt,
처리인구1단계: item.swtrmtPpltnCntDc1,
처리인구2단계: item.swtrmtPpltnCntDc2,
처리인구3단계: item.swtrmtPpltnCntDc3,
처리인구4단계: item.swtrmtPpltnCntDc4,
처리구역기존: item.swtrmtZoneAreaDt4,
처리구역현재: item.swtrmtZoneArea,
처리구역1단계: item.swtrmtZoneAreaDc1,
처리구역2단계: item.swtrmtZoneAreaDc2,
처리구역3단계: item.swtrmtZoneAreaDc3,
처리구역4단계: item.swtrmtZoneAreaDc4,
처리구역: item.trtDstr
});
});
}else{
res.data.forEach(item => {
item.fctCpc = item.fctCpc ? parseFloat(item.fctCpc.toString().replace(/,/g, '')) : '';
})
}
set((state:ProcessAreaType) => ({
...state,
ProcessArea: pageSize<5000?{
...state.ProcessArea,
processAreaList: res.data,
processAreaCount: res.count,
}:{
...state.ProcessArea,
excelList: excelDownList,
},
}))
},
initModalData: async (swtrmtFcltCd:string) => {
set((state: ProcessAreaType) => {
const rtnData = state.ProcessArea.processAreaList.find((item: ProcessAreaProps) => item.swtrmtFcltCd === swtrmtFcltCd);
if (rtnData) {
upHeadListFcltyImproveInfo.forEach((head:TableUpperProps) => {
if(head.id === 'planSwageQntSum' || head.id === 'swtrmtPpltnCnt' || head.id === 'swtrmtZoneArea') {
head.title = '현재\n(' + rtnData.yr + '년)';
} else if(head.id === 'planSwageQntSumDc1' || head.id === 'swtrmtPpltnCntDc1' || head.id === 'swtrmtZoneAreaDc1') {
head.title = '1단계\n(' + rtnData.yrDc1 + '년)';
} else if(head.id === 'planSwageQntSumDc2' || head.id === 'swtrmtPpltnCntDc2' || head.id === 'swtrmtZoneAreaDc2') {
head.title = '2단계\n(' + rtnData.yrDc2 + '년)';
} else if(head.id === 'planSwageQntSumDc3' || head.id === 'swtrmtPpltnCntDc3' || head.id === 'swtrmtZoneAreaDc3') {
head.title = '3단계\n(' + rtnData.yrDc3 + '년)';
} else if(head.id === 'planSwageQntSumDc4' || head.id === 'swtrmtPpltnCntDc4' || head.id === 'swtrmtZoneAreaDc4') {
head.title = '4단계\n(' + rtnData.yrDc4 + '년)';
} else if(head.id === 'planSwageQntSumDt4' || head.id === 'swtrmtPpltnCntDt4' || head.id === 'swtrmtZoneAreaDt4') {
head.title = '기승인\n4단계(' + rtnData.yrDt4 + '년)';
}
});
upHeadListProcessAreaPlan.forEach((head:TableUpperProps) => {
if(head.id === 'planSwageQntSum' || head.id === 'swtrmtPpltnCnt' || head.id === 'swtrmtZoneArea') {
head.title = '현재\n(' + rtnData.yr + '년)';
} else if(head.id === 'planSwageQntSumDc1' || head.id === 'swtrmtPpltnCntDc1' || head.id === 'swtrmtZoneAreaDc1') {
head.title = '1단계\n(' + rtnData.yrDc1 + '년)';
} else if(head.id === 'planSwageQntSumDc2' || head.id === 'swtrmtPpltnCntDc2' || head.id === 'swtrmtZoneAreaDc2') {
head.title = '2단계\n(' + rtnData.yrDc2 + '년)';
} else if(head.id === 'planSwageQntSumDc3' || head.id === 'swtrmtPpltnCntDc3' || head.id === 'swtrmtZoneAreaDc3') {
head.title = '3단계\n(' + rtnData.yrDc3 + '년)';
} else if(head.id === 'planSwageQntSumDc4' || head.id === 'swtrmtPpltnCntDc4' || head.id === 'swtrmtZoneAreaDc4') {
head.title = '4단계\n(' + rtnData.yrDc4 + '년)';
} else if(head.id === 'planSwageQntSumDt4' || head.id === 'swtrmtPpltnCntDt4' || head.id === 'swtrmtZoneAreaDt4') {
head.title = '기승인\n4단계(' + rtnData.yrDt4 + '년)';
}
});
upHeadListProcessPopulation.forEach((head:TableUpperProps) => {
if(head.id === 'planSwageQntSum' || head.id === 'swtrmtPpltnCnt' || head.id === 'swtrmtZoneArea') {
head.title = '현재\n(' + rtnData.yr + '년)';
} else if(head.id === 'planSwageQntSumDc1' || head.id === 'swtrmtPpltnCntDc1' || head.id === 'swtrmtZoneAreaDc1') {
head.title = '1단계\n(' + rtnData.yrDc1 + '년)';
} else if(head.id === 'planSwageQntSumDc2' || head.id === 'swtrmtPpltnCntDc2' || head.id === 'swtrmtZoneAreaDc2') {
head.title = '2단계\n(' + rtnData.yrDc2 + '년)';
} else if(head.id === 'planSwageQntSumDc3' || head.id === 'swtrmtPpltnCntDc3' || head.id === 'swtrmtZoneAreaDc3') {
head.title = '3단계\n(' + rtnData.yrDc3 + '년)';
} else if(head.id === 'planSwageQntSumDc4' || head.id === 'swtrmtPpltnCntDc4' || head.id === 'swtrmtZoneAreaDc4') {
head.title = '4단계\n(' + rtnData.yrDc4 + '년)';
} else if(head.id === 'planSwageQntSumDt4' || head.id === 'swtrmtPpltnCntDt4' || head.id === 'swtrmtZoneAreaDt4') {
head.title = '기승인\n4단계(' + rtnData.yrDt4 + '년)';
}
});
// 시설증설 계획 데이터 생성
const fcltyImproveInfo: FcltyImproveInfoProps = {
planSwageQntSumDt4: rtnData.planSwageQntSumDt4,
planSwageQntSum: rtnData.planSwageQntSum,
planSwageQntSumDc1: rtnData.planSwageQntSumDc1,
planSwageQntSumDc2: rtnData.planSwageQntSumDc2,
planSwageQntSumDc3: rtnData.planSwageQntSumDc3,
planSwageQntSumDc4: rtnData.planSwageQntSumDc4
};
// 처리인구 계획 데이터 생성
const processPopulation: ProcessPopulationProps = {
swtrmtPpltnCntDt4: rtnData.swtrmtPpltnCntDt4,
swtrmtPpltnCnt: rtnData.swtrmtPpltnCnt,
swtrmtPpltnCntDc1: rtnData.swtrmtPpltnCntDc1,
swtrmtPpltnCntDc2: rtnData.swtrmtPpltnCntDc2,
swtrmtPpltnCntDc3: rtnData.swtrmtPpltnCntDc3,
swtrmtPpltnCntDc4: rtnData.swtrmtPpltnCntDc4
};
// 처리구역 계획 데이터 생성
const processAreaPlan: ProcessAreaPlanProps = {
swtrmtZoneAreaDt4: rtnData.swtrmtZoneAreaDt4,
swtrmtZoneArea: rtnData.swtrmtZoneArea,
swtrmtZoneAreaDc1: rtnData.swtrmtZoneAreaDc1,
swtrmtZoneAreaDc2: rtnData.swtrmtZoneAreaDc2,
swtrmtZoneAreaDc3: rtnData.swtrmtZoneAreaDc3,
swtrmtZoneAreaDc4: rtnData.swtrmtZoneAreaDc4,
trtDstr: rtnData.trtDstr
};
return {
...state,
ProcessAreaDetail: {
...state.ProcessAreaDetail,
gridListFcltyImproveInfo: [fcltyImproveInfo],
gridListProcessPopulation: [processPopulation],
gridListProcessAreaPlan: [processAreaPlan]
}
};
}
return state;
});
}
}
}
+832
View File
@@ -0,0 +1,832 @@
import { getFetch } from "@/app/components/api";
import DataForm from "@/app/components/common/DataForm";
import type { TableUpperProps } from "../dashboard";
export type { TableUpperProps };
/**====================================
* Store - 타입
====================================*/
/**
* (1) 메인페이지
*/
// 처리시설 리스트
export type facilityListProps = {
bscFctCd : string,
bscFctNm : string,
bscFctClsNm : string,
sido : string,
gugun : string,
daddr : string,
fctCpc : number | string | '',
plInflWqltBod : number | string | '',
dsInflWqltBod : number | string | '',
trtFrfNm : string,
rnDt : string,
cmplDt : string,
trstOprEtpNm : string,
rvbsn : string,
mdmbsn : string,
lgbsn : string,
rgnNm : string,
mnstr : string,
efflWtrrgn : string,
efflWqltStndrdNm : string
}
export interface ExcelType {
시설코드: string,
권역:string|'',
단위유역:string|'',
중권역:string|'',
환경청:string|'',
시도: string,
시군구: string,
시설이름: string,
주소: string,
시설용량: number | '',
계획유입수질: number | '',
설계유입수질: number | '',
공법: string,
가동개시일: string,
준공일: string,
관리대행업자: string,
}
// (2) 처리시설 팝업
// 시설 기본정보
export type BasicInfoProps = {
bscFctNm : string,
fctCpc : number | '',
sido : string,
gugun : string,
startDay : string,
addr : string,
cmplDt : string,
trtFrfNm : string,
trstOprEtpNm : string,
}
// 시설 계획정보
export type PlanInfoProps = {
plInflWqltBod? : string,
plInflWqltToc? : string,
plInflWqltCod? : string,
plInflWqltSs? : string,
plInflWqltTn? : string,
plInflWqltTp? : string,
dsInflWqltBod? : string,
dsInflWqltToc? : string,
dsInflWqltCod? : string,
dsInflWqltSs? : string,
dsInflWqltTn? : string,
dsInflWqltTp? : string,
sitArea? : string | number,
trtDstrArea? : string | number,
swrTrtPopl? : string | number
}
// 시설 계획정보
export type LegalWaterQltyProps = {
totClfgrpcnt : number,
bod : number,
cod : number,
ss : number,
tn : number,
tp : number
}
// 시설 수역 및 구역정보
export type fcltyAreaInfoProps = {
rvbsn : string,
efflWtrrgn : string,
lgbsn : string,
efflWqltStndrdNm : string,
rgnNm : string,
}
// 시설 이력관리
export type fcltyHistInfoProps = {
extnsSeqno : string,
extnsNm : string,
bzStrtYrMnth : string,
bzEdYrMnth : string,
eqpPrmsDt : string,
cmplDt : string,
trlrnCmplDt : string,
rnDt : string,
sitArea : number,
trtDstrArea : number,
swrTrtPopl : number
}
// 시설 수질정보
export type fcltyWaterQltyInfoProps = {
extnsSeqno : string,
plInflWqltBod : string,
plInflWqltToc : string,
plInflWqltCod : string,
plInflWqltSs : string,
plInflWqltTn : string,
plInflWqltTp : string,
dsInflWqltBod : string,
dsInflWqltToc : string,
dsInflWqltCod : string,
dsInflWqltSs : string,
dsInflWqltTn : string,
dsInflWqltTp : string,
plEfflWqltBod : string,
plEfflWqltToc : string,
plEfflWqltCod : string,
plEfflWqltSs : string,
plEfflWqltTn : string,
plEfflWqltTp : string,
bod : number,
cod : number,
ss : number,
tn : number,
tp : number,
totClfgrpcnt : number,
}
/**====================================
* Store - data
====================================*/
/**
* (1) 메인페이지
*/
// 검색단
const ProcessFacilityLabelArray:string[] = ['구분', '시도', '시군구','용량별', '시설명', '시설상태', '시설구분', '권역', '단위유역', '중권역', '환경청별'];
// 리스트 헤더
const upHeadList1:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드"},
{ id: "sido", title: "시도", upName:"upChangeRe", upSequnce:1 },
{ id: "gugun", title: "시군구", upName:"upChangeRe",upSequnce:2 },
{ id: "bscFctNm", title: "시설명"},
{ id: "daddr", title: "주소"},
{ id: "fctCpc", title: "시설용량\n(m³/일)", align: 'right'},
{ id: "plInflWqltBod", title: "계획유입수질\n(mg/L)", align:"right"},
{ id: "dsInflWqltBod", title: "설계유입수질\n(mg/L)", align:"right"},
{ id: "trtFrfNm", title: '공법'},
{ id: "rnDt", title: '가동개시일'},
{ id: "cmplDt", title: '준공일' },
{ id: "trstOprEtpNm", title: '관리대행업자'}
];
// 처리시설 리스트 (유역)
const upHeadList2:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드"},
{ id: "rvbsn", title: "권역", upName:"upChangeRe", upSequnce:1},
{ id: "lgbsn", title: "단위유역", upName:"upChangeRe", upSequnce:2},
{ id: "mdmbsn", title: "중권역", upName:"upChangeRe", upSequnce:3},
{ id: "bscFctNm", title: "시설명"},
{ id: "daddr", title: "주소"},
{ id: "fctCpc", title: "시설용량\n(m³/일)", align: 'right'},
{ id: "plInflWqltBod", title: "계획유입수질\n(mg/L)", align:'right'},
{ id: "dsInflWqltBod", title: "설계유입수질\n(mg/L)", align:'right'},
{ id: "trtFrfNm", title: '공법'},
{ id: "rnDt", title: '가동개시일'},
{ id: "cmplDt", title: '준공일' },
{ id: "trstOprEtpNm", title: '관리대행업자'}
];
// 처리시설 리스트 (환경청청)
const upHeadList3:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드"},
{ id: "rvbsn", title: "권역", upName:"upChangeRe1", upSequnce:1},
{ id: "lgbsn", title: "단위유역", upName:"upChangeRe1", upSequnce:2},
{ id: "mdmbsn", title: "중권역", upName:"upChangeRe1", upSequnce:3},
{ id: "rgnNm", title: "환경청"},
{ id: "sido", title: "시도", upName:"upChangeRe2", upSequnce:1 },
{ id: "gugun", title: "시군구", upName:"upChangeRe2",upSequnce:2 },
{ id: "bscFctNm", title: "시설명"},
{ id: "daddr", title: "주소"},
{ id: "fctCpc", title: "시설용량\n(m³/일)", align: 'right'},
{ id: "plInflWqltBod", title: "계획유입수질\n(mg/L)", align:'right'},
{ id: "dsInflWqltBod", title: "설계유입수질\n(mg/L)", align:'right'},
{ id: "trtFrfNm", title: '공법'},
{ id: "rnDt", title: '가동개시일'},
{ id: "cmplDt", title: '준공일' },
{ id: "trstOprEtpNm", title: '관리대행업자'}
];
const processFacilityList:facilityListProps[] = [
]
/**
* 처리시설 상세팝업
*/
// 시설 기본정보 (헤더)
const upHeadListBasicInfo:TableUpperProps[] = [
{ id: "bscFctNm", title: "시설명" },
{ id: "fctCpc", title: "시설용량n(m³/일)", align: 'right'},
{ id: "sido", title: "시도"},
{ id: "gugun", title: "시군구"},
{ id: "rnDt", title: "가동개시일"},
{ id: "daddr", title: "주소"},
{ id: "cmplDt", title: "준공일"},
{ id: "trtFrfNm", title: "공법"},
{ id: "trstOprEtpNm", title: "관리대행업자"},
];
// 시설 기본정보 (리스트)
const gridListBasicInfo:BasicInfoProps[] = [
{
bscFctNm : "난지",
fctCpc : 860000,
sido : "서울특별시",
gugun : "",
startDay : "1987-06-30",
addr : "경기도 고양시 덕양구 대덕로 426 (현천동)",
cmplDt : "2013-04-30",
trtFrfNm : "MLE, A2O",
trstOprEtpNm : "자체",
}
];
// 시설 법적방류수질 (헤더)
const upHeadListLegalWaterQlty:TableUpperProps[] = [
{ id: "bod", title: "BOD(mg/L)", align: 'right'},
{ id: "cod", title: "TOC(COD)(mg/L)", align: 'right'},
{ id: "ss", title: "SS(mg/L)", align: 'right'},
{ id: "tn", title: "T-N(mg/L)", align: 'right'},
{ id: "tp", title: "T-P(mg/L) ", align: 'right'},
{ id: "totClfgrpcnt", title: "대장균군(개/100ml)"},
]
// 시설 법적방류수질 (리스트)
let gridListLegalWaterQlty:LegalWaterQltyProps[] = [
]
// 시설 계획정보 (헤더)
const upHeadListPlanInfo:TableUpperProps[] = [
{ id: "plInflWqltBod", title: "BOD", upName:"upChangeRe1", upSequnce:1, align: 'right' },
{ id: "plInflWqltToc", title: "TOC", upName:"upChangeRe1", upSequnce:2, align: 'right' },
{ id: "plInflWqltCod", title: "COD", upName:"upChangeRe1", upSequnce:3, align: 'right' },
{ id: "plInflWqltSs", title: "SS", upName:"upChangeRe1", upSequnce:4, align: 'right' },
{ id: "plInflWqltTn", title: "T-N", upName:"upChangeRe1", upSequnce:5, align: 'right' },
{ id: "plInflWqltTp", title: "T-P", upName:"upChangeRe1", upSequnce:6, align: 'right' },
{ id: "dsInflWqltBod", title: "BOD", upName:"upChangeRe2", upSequnce:1, align: 'right' },
{ id: "dsInflWqltToc", title: "TOC", upName:"upChangeRe2", upSequnce:2, align: 'right' },
{ id: "dsInflWqltCod", title: "COD", upName:"upChangeRe2", upSequnce:3, align: 'right' },
{ id: "dsInflWqltSs", title: "SS", upName:"upChangeRe2", upSequnce:4, align: 'right' },
{ id: "dsInflWqltTn", title: "T-N", upName:"upChangeRe2", upSequnce:5, align: 'right' },
{ id: "dsInflWqltTp", title: "T-P", upName:"upChangeRe2", upSequnce:6, align: 'right' },
{ id: "sitArea", title: "부지면적\n(m²)", align: 'right'},
{ id: "trtDstrArea", title: "처리구역면적\n(ha)", align: 'right'},
{ id: "swrTrtPopl", title: "하수처리인구\n(인)", align: 'right'},
];
// 시설 계획정보 (리스트)
const gridListPlanInfo:PlanInfoProps[] = [
{
plInflWqltBod : "179.0",
plInflWqltToc : "90.0",
plInflWqltCod : "0.0",
plInflWqltSs : "132.0",
plInflWqltTn : "38.000",
plInflWqltTp : "4.300",
dsInflWqltBod : "178.0",
dsInflWqltToc : "91.0",
dsInflWqltCod : "0.0",
dsInflWqltSs : "131.0",
dsInflWqltTn : "40.200",
dsInflWqltTp : "4.500",
sitArea : "928,574",
trtDstrArea : "7,994.0",
swrTrtPopl : "1,573,277",
}
];
// 시설 수역 및 구역정보 (헤더)
const upHeadListFcltyAreaInfo:TableUpperProps[] = [
{ id: "rvbsn", title: "하천명" },
{ id: "efflWtrrgn", title: "방류수역"},
{ id: "lgbsn", title: "단위유역"},
{ id: "efflWqltStndrdNm", title: "지역구분"},
{ id: "rgnNm", title: "관할 유역(지방) 환경청"}
];
// 시설 수역 및 구역정보 (리스트)
const gridListFcltyAreaInfo:fcltyAreaInfoProps[] = [
{
rvbsn : "한강",
efflWtrrgn : "한강수계",
lgbsn : "한강본류",
efflWqltStndrdNm : "500톤 이상\n(Ⅲ지역)",
rgnNm : "한강유역환경청"
}
];
// 시설 이력관리 (헤더)
const upHeadListFcltyHistInfo:TableUpperProps[] = [
{ id: "extnsSeqno", title: "차수", align: 'center' },
{ id: "extnsNm", title: "구분"},
{ id: "bzStrtYrMnth", title: "사업기간", upName:"upChangeRe1", upSequnce:1},
{ id: "bzEdYrMnth", title: "사업기간", upName:"upChangeRe1", upSequnce:2},
{ id: "eqpPrmsDt", title: "설치인가일"},
{ id: "cmplDt", title: "준공일"},
{ id: "trlrnCmplDt", title: "시운전완료일"},
{ id: "rnDt", title: "가동개시일"},
];
// 시설 이력관리 (리스트)
const gridListFcltyHistInfo:fcltyHistInfoProps[] = [
{
extnsSeqno : "최초",
extnsNm : "최초",
bzStrtYrMnth : "1984.12.01",
bzEdYrMnth : "1987.06.01",
eqpPrmsDt : "1985.09.13",
cmplDt : "1987.06.30",
trlrnCmplDt : "1987.06.30",
rnDt : "1987.06.30",
sitArea : 0,
trtDstrArea : 0,
swrTrtPopl : 0,
}
];
// 시설 수질정보 (헤더)
const upHeadListFcltyWaterQltyInfo:TableUpperProps[] = [
{ id: "extnsSeqno", title: "차수" },
{ id: "plInflWqltBod", title: "BOD", upName:"upChangeRe1", upSequnce:1, align: 'right' },
{ id: "plInflWqltToc", title: "TOC", upName:"upChangeRe1", upSequnce:2, align: 'right' },
{ id: "plInflWqltCod", title: "COD", upName:"upChangeRe1", upSequnce:3, align: 'right' },
{ id: "plInflWqltSs", title: "SS", upName:"upChangeRe1", upSequnce:4, align: 'right' },
{ id: "plInflWqltTn", title: "T-N", upName:"upChangeRe1", upSequnce:5, align: 'right' },
{ id: "plInflWqltTp", title: "T-P", upName:"upChangeRe1", upSequnce:6, align: 'right' },
{ id: "dsInflWqltBod", title: "BOD", upName:"upChangeRe2", upSequnce:1, align: 'right' },
{ id: "dsInflWqltToc", title: "TOC", upName:"upChangeRe2", upSequnce:2, align: 'right' },
{ id: "dsInflWqltCod", title: "COD", upName:"upChangeRe2", upSequnce:3, align: 'right' },
{ id: "dsInflWqltSs", title: "SS", upName:"upChangeRe2", upSequnce:4, align: 'right' },
{ id: "dsInflWqltTn", title: "T-N", upName:"upChangeRe2", upSequnce:5, align: 'right' },
{ id: "dsInflWqltTp", title: "T-P", upName:"upChangeRe2", upSequnce:6, align: 'right' },
{ id: "plEfflWqltBod", title: "BOD", upName:"upChangeRe3", upSequnce:1, align: 'right' },
{ id: "plEfflWqltToc", title: "TOC", upName:"upChangeRe3", upSequnce:2, align: 'right' },
{ id: "plEfflWqltCod", title: "COD", upName:"upChangeRe3", upSequnce:3, align: 'right' },
{ id: "plEfflWqltSs", title: "SS", upName:"upChangeRe3", upSequnce:4, align: 'right' },
{ id: "plEfflWqltTn", title: "T-N", upName:"upChangeRe3", upSequnce:5, align: 'right' },
{ id: "plEfflWqltTp", title: "T-P", upName:"upChangeRe3", upSequnce:6, align: 'right' },
];
// 시설 수질정보 (리스트)
const gridListFcltyWaterQltyInfo:fcltyWaterQltyInfoProps[] = [
{
extnsSeqno: "최초",
plInflWqltBod: "111.0",
plInflWqltToc: "(0.0)",
plInflWqltCod: "(0.0)",
plInflWqltSs: "122.0",
plInflWqltTn: "0.000",
plInflWqltTp: "0.000",
dsInflWqltBod: "111.0",
dsInflWqltToc: "(0.0)",
dsInflWqltCod: "(0.0)",
dsInflWqltSs: "122.0",
dsInflWqltTn: "0.000",
dsInflWqltTp: "0.000",
plEfflWqltBod: "20.0",
plEfflWqltCod: "(0.0)",
plEfflWqltToc: "(0.0)",
plEfflWqltSs: "12.0",
plEfflWqltTn: "0.000",
plEfflWqltTp: "0.000",
bod: 0,
cod: 0,
ss: 0,
tn: 0,
tp: 0,
totClfgrpcnt: 0
}
];
/**====================================
* Store - api (데이터 호출)
====================================*/
// 시설정보 리스트 데이터 호출(구분 선택 후 시설명 있을 경우)
const fetchFacilityList = async (pageNo:number, pageSize:number, partValue?:string, nameCd?:string, fcltCpct?:string, oprtngSttsCd?:string, facilityPartValue?:string, facilityName?:string ): Promise<{data:facilityListProps[], count:number}> => {
try {
const tailStr = partValue ? '/'+ (partValue==='G' ? 'A' : partValue) + '/' + nameCd + '/' + fcltCpct + '/' + oprtngSttsCd + '/' + facilityPartValue + (facilityName && facilityName !== '' ? '/' + facilityName : '') : '';
const url = '/prcsFclt/info/history/'+pageNo+'/'+pageSize+tailStr;
//const res = await getFetch('fo', '/prcsFclt/info/'+pageNo+'/'+pageSize+tailUrl);
const res = await getFetch('fo', url);
return res;
} catch (error) {
console.error('시설정보 데이터를 가져오는데 실패했습니다:', error);
return {data: [], count: 0};
}
};
// 시설정보 리스트 데이터 호출 (대시보드용)
const fetchFacilityListSearching = async (pageNo:number, pageSize:number, partValue?:string, nameCd?:string, fcltCpct?:string, oprtngSttsCd?:string, facilityPartValue?:string, facilityName?:string ): Promise<{data:facilityListProps[], count:number}> => {
try {
const tailStr = partValue ? '/'+ (partValue==='G' ? 'A' : partValue) + '/' + nameCd + '/' + fcltCpct + '/' + oprtngSttsCd + '/' + facilityPartValue + (facilityName && facilityName !== '' ? '/' + facilityName : '') : '';
const url = '/prcsFclt/searching/'+pageNo+'/'+pageSize+tailStr;
//const res = await getFetch('fo', '/prcsFclt/info/'+pageNo+'/'+pageSize+tailUrl);
const res = await getFetch('fo', url);
return res;
} catch (error) {
console.error('시설정보 데이터를 가져오는데 실패했습니다:', error);
return {data: [], count: 0};
}
};
// 모달
// 시설정보 이력정보 데이터 호출
const fetchFacilityHistInfo = async (facilityCd:string ): Promise<fcltyHistInfoProps[]> => {
try {
const res = await getFetch('fo', '/prcsFclt/hist/'+facilityCd);
const data = res.data;
DataForm({ type: 'date', column: 'trlrnCmplDt', item: data as unknown as Record<string, unknown> });
DataForm({ type: 'date', column: 'eqpPrmsDt', item: data as unknown as Record<string, unknown> });
DataForm({ type: 'date', column: 'cmplDt', item: data as unknown as Record<string, unknown> });
DataForm({ type: 'date', column: 'rnDt', item: data as unknown as Record<string, unknown> });
DataForm({ type: 'number', column: 'sitArea', item: data as unknown as Record<string, unknown> });
DataForm({ type: 'number', column: 'trtDstrArea', item: data as unknown as Record<string, unknown> });
DataForm({ type: 'number', column: 'swrTrtPopl', item: data as unknown as Record<string, unknown> });
return data;
} catch (error) {
console.error('시설정보 이력정보 데이터를 가져오는데 실패했습니다:', error);
return [];
}
};
// 시설정보 수질정보 데이터 호출
const fetchFacilityWaterQltyInfo = async (facilityCd:string ): Promise<fcltyWaterQltyInfoProps[]> => {
try {
const res = await getFetch('fo', '/prcsFclt/info/history/'+facilityCd);
const data = res.data;
return data;
} catch (error) {
console.error('시설정보 수질정보 데이터를 가져오는데 실패했습니다:', error);
return [];
}
};
/**====================================
* Store - expaort
====================================*/
export type ProcessFacilityActions = {
/**
* 시설정보 리스트 데이터 호출(구분 선택 후 시설명 있을 경우)
* @param pageNo : 페이지 번호
* @param pageSize : 페이지 크기
* @param partValue : 구분값(행정명, 대유역, 환경청)
* @param nameCd : 행정코드(시도명, 시군구명, 단위유역)
* @param fcltCpct : 시설용량
* @param oprtngSttsCd : 운영 상태 코드
* @param facilityName : 시설명
*/
searchProcessFacility : (pageNo:number, pageSize:number, partValue?:string, nameCd?:string, fcltCpct?:string, oprtngSttsCd?:string, facilityPartValue?:string, facilityName?:string) => void;
/**
* 시설정보 이력정보 데이터 호출
* @param facilityCd : 시설코드
*/
searchProcessFacilityHist : (facilityCd:string) => void;
/**
* 시설정보 수질정보 데이터 호출
* @param facilityCd : 시설코드
*/
searchProcessFacilityWaterQlty : (facilityCd:string) => void;
/**
* 시설정보 리스트 데이터 호출 (대시보드용)
* @param pageNo : 페이지 번호
* @param pageSize : 페이지 크기
*/
searchProcessFacilitySearching : (pageNo:number, pageSize:number, partValue?:string, nameCd?:string, fcltCpct?:string, oprtngSttsCd?:string, facilityPartValue?:string, facilityName?:string) => void;
}
export type ProcessFacilityType = {
ProcessFacility: {
isInit: boolean;
processFacilityLabelArray:string[];
},
ProcessFacilityList : {
isInit: boolean;
upHeadList1:TableUpperProps[];
upHeadList2:TableUpperProps[];
upHeadList3:TableUpperProps[];
processFacilityList:facilityListProps[];
processFacilityCount:number;
excelList : ExcelType[];
},
ProcessFacilityDetail : {
isInit : boolean;
upHeadListBasicInfo : TableUpperProps[];
gridListBasicInfo : BasicInfoProps[];
upHeadListPlanInfo : TableUpperProps[];
gridListPlanInfo : PlanInfoProps[];
upHeadListFcltyAreaInfo : TableUpperProps[];
gridListFcltyAreaInfo : fcltyAreaInfoProps[];
upHeadListFcltyHistInfo : TableUpperProps[];
gridListFcltyHistInfo : fcltyHistInfoProps[];
upHeadListFcltyWaterQltyInfo : TableUpperProps[];
gridListFcltyWaterQltyInfo : fcltyWaterQltyInfoProps[];
upHeadListLegalWaterQlty : TableUpperProps[];
gridListLegalWaterQlty : LegalWaterQltyProps[];
},
}
export const processFacilityInitState:ProcessFacilityType = {
ProcessFacility: {
isInit: false,
processFacilityLabelArray: ProcessFacilityLabelArray,
},
ProcessFacilityList : {
isInit : false,
upHeadList1 : upHeadList1,
upHeadList2 : upHeadList2,
upHeadList3 : upHeadList3,
processFacilityList : processFacilityList,
processFacilityCount:1,
excelList : []
},
ProcessFacilityDetail : {
isInit : false,
upHeadListBasicInfo : upHeadListBasicInfo,
gridListBasicInfo : gridListBasicInfo,
upHeadListPlanInfo : upHeadListPlanInfo,
gridListPlanInfo : gridListPlanInfo,
upHeadListFcltyAreaInfo : upHeadListFcltyAreaInfo,
gridListFcltyAreaInfo : gridListFcltyAreaInfo,
upHeadListFcltyHistInfo : upHeadListFcltyHistInfo,
gridListFcltyHistInfo : gridListFcltyHistInfo,
upHeadListFcltyWaterQltyInfo : upHeadListFcltyWaterQltyInfo,
gridListFcltyWaterQltyInfo : gridListFcltyWaterQltyInfo,
upHeadListLegalWaterQlty : upHeadListLegalWaterQlty,
gridListLegalWaterQlty : gridListLegalWaterQlty,
}
}
// 상태 설정 함수 타입 정의
type SetStateFunction = (state: ProcessFacilityType | ((state: ProcessFacilityType) => ProcessFacilityType)) => void;
export type ProcessFacilityStore = ProcessFacilityType & ProcessFacilityActions;
export const processFacilityReducer:(set:SetStateFunction)=>ProcessFacilityActions=(set: SetStateFunction) => {
return{
// 메인페이지
/**
* 시설정보 리스트 데이터 호출
* @param pageNo : 페이지 번호
* @param pageSize : 페이지 크기
*/
searchProcessFacility: async (pageNo:number, pageSize:number, partValue?:string, nameCd?:string, fcltCpct?:string, oprtngSttsCd?:string, facilityPartValue?:string, facilityName?:string) => {
const res = await fetchFacilityList(pageNo, pageSize, partValue, nameCd, fcltCpct, oprtngSttsCd, facilityPartValue, facilityName);
res.data.forEach((item:facilityListProps) => {
DataForm({ type: 'date', column: 'cmplDt', item:item as unknown as Record<string, unknown>});
DataForm({ type: 'date', column: 'rnDt', item:item as unknown as Record<string, unknown>});
DataForm({ type: 'float1', column: 'fctCpc', item:item as unknown as Record<string, unknown>});
})
res.data.forEach(item => {
item.plInflWqltBod = item.plInflWqltBod ? Number(item.plInflWqltBod).toFixed(1) : '';
item.dsInflWqltBod = item.dsInflWqltBod ? Number(item.dsInflWqltBod).toFixed(1) : '';
})
const excelDownList: ExcelType[] = [];
if(pageSize>5000){
res.data.forEach(item => {
item.fctCpc = item.fctCpc ? parseFloat(item.fctCpc.toString().replace(/,/g, '')) : '';
excelDownList.push({
시설코드: item.bscFctCd,
권역: item.rvbsn,
단위유역: item.lgbsn,
중권역: item.mdmbsn,
환경청: item.rgnNm,
시도: item.sido,
시군구: item.gugun,
시설이름: item.bscFctNm,
주소: item.daddr,
시설용량: item.fctCpc ? Number(item.fctCpc) : '',
계획유입수질: item.plInflWqltBod ? Number(item.plInflWqltBod) : '',
설계유입수질: item.dsInflWqltBod ? Number(item.dsInflWqltBod) : '',
공법: item.trtFrfNm,
가동개시일: item.rnDt,
준공일: item.cmplDt,
관리대행업자: item.mnstr,
})
})
}else{
res.data.forEach(item => {
item.fctCpc = item.fctCpc ? parseFloat(item.fctCpc.toString().replace(/,/g, '')) : '';
})
}
set((state:ProcessFacilityType) => ({
...state,
ProcessFacilityList: pageSize<5000?{
...state.ProcessFacilityList,
processFacilityList: res.data,
processFacilityCount: res.count,
}:{
...state.ProcessFacilityList,
excelList: excelDownList,
},
}))
},
/**
* 시설정보 리스트 데이터 호출
* @param pageNo : 페이지 번호
* @param pageSize : 페이지 크기
*/
searchProcessFacilitySearching: async (pageNo:number, pageSize:number, partValue?:string, nameCd?:string, fcltCpct?:string, oprtngSttsCd?:string, facilityPartValue?:string, facilityName?:string) => {
const res = await fetchFacilityListSearching(pageNo, pageSize, partValue, nameCd, fcltCpct, oprtngSttsCd, facilityPartValue, facilityName);
/* res.data.forEach((item:facilityListProps) => {
DataForm({ type: 'date', column: 'cmplDt', item:item as unknown as Record<string, unknown>});
DataForm({ type: 'date', column: 'rnDt', item:item as unknown as Record<string, unknown>});
})
res.data.forEach(item => {
item.plInflWqltBod = item.plInflWqltBod ? Number(item.plInflWqltBod).toFixed(1) : '';
item.dsInflWqltBod = item.dsInflWqltBod ? Number(item.dsInflWqltBod).toFixed(1) : '';
}) */
console.log(res.data);
set((state:ProcessFacilityType) => ({
...state,
ProcessFacilityList: {
...state.ProcessFacilityList,
processFacilityList: res.data,
processFacilityCount: res.count,
},
}))
},
// 모달 이력정보
/**
* 시설정보 이력정보 데이터 호출
* @param facilityCd : 시설코드
*/
searchProcessFacilityHist: async (facilityCd:string) => {
const promiseSet = await Promise.all([fetchFacilityHistInfo(facilityCd)]);
const [facilityHistInfo] = promiseSet;
// extnsSeqno를 기준으로 오름차순 정렬
const sortedHistInfo = [...facilityHistInfo].sort((a, b) => {
// 숫자로 변환하여 비교
const numA = Number(a.extnsSeqno);
const numB = Number(b.extnsSeqno);
if (!isNaN(numA) && !isNaN(numB)) {
return numA - numB;
}
// 숫자 변환이 실패하면 문자열로 비교
return String(a.extnsSeqno).localeCompare(String(b.extnsSeqno));
});
sortedHistInfo.forEach(item => {
if(item.extnsSeqno == '0'){
item.extnsSeqno = '최초';
item.extnsNm = '신설';
}
});
// yyyy-MM-dd로 변환
sortedHistInfo.forEach(item => {
item.eqpPrmsDt = item.eqpPrmsDt ? item.eqpPrmsDt.substring(0,4) + '-' + item.eqpPrmsDt.substring(4,6) + '-' + item.eqpPrmsDt.substring(6,8) : '';
item.trlrnCmplDt = item.trlrnCmplDt ? item.trlrnCmplDt.substring(0,4) + '-' + item.trlrnCmplDt.substring(4,6) + '-' + item.trlrnCmplDt.substring(6,8) : '';
item.cmplDt = item.cmplDt ? item.cmplDt.substring(0,4) + '-' + item.cmplDt.substring(4,6) + '-' + item.cmplDt.substring(6,8) : '';
item.rnDt = item.rnDt ? item.rnDt.substring(0,4) + '-' + item.rnDt.substring(4,6) + '-' + item.rnDt.substring(6,8) : '';
});
// 마지막 이력 데이터의 값을 gridListPlanInfo에 업데이트
if (sortedHistInfo.length > 0) {
const lastHistInfo = sortedHistInfo[sortedHistInfo.length - 1];
set((state: ProcessFacilityType) => ({
...state,
ProcessFacilityDetail: {
...state.ProcessFacilityDetail,
gridListFcltyHistInfo: sortedHistInfo,
gridListPlanInfo: [{
...state.ProcessFacilityDetail.gridListPlanInfo[0],
swrTrtPopl: lastHistInfo?.swrTrtPopl ?? state.ProcessFacilityDetail.gridListPlanInfo[0]?.swrTrtPopl,
trtDstrArea: lastHistInfo?.trtDstrArea ?? state.ProcessFacilityDetail.gridListPlanInfo[0]?.trtDstrArea,
sitArea: lastHistInfo?.sitArea ?? state.ProcessFacilityDetail.gridListPlanInfo[0]?.sitArea
}]
}
}));
} else {
set((state: ProcessFacilityType) => ({
...state,
ProcessFacilityDetail: {
...state.ProcessFacilityDetail,
gridListFcltyHistInfo: sortedHistInfo,
gridListPlanInfo: [{
...state.ProcessFacilityDetail.gridListPlanInfo[0],
swrTrtPopl: '',
trtDstrArea: '',
sitArea: ''
}]
}
}));
}
},
// 모달 수질정보
/**
* 시설정보 수질정보 데이터 호출
* @param facilityCd : 시설코드
*/
searchProcessFacilityWaterQlty: async (facilityCd:string) => {
const promiseSet = await Promise.all([fetchFacilityWaterQltyInfo(facilityCd)]);
const [facilityWaterQltyInfo] = promiseSet;
// extnsSeqno를 기준으로 오름차순 정렬
const sortedWaterQltyInfo = [...facilityWaterQltyInfo].sort((a, b) => {
// 숫자로 변환하여 비교
const numA = Number(a.extnsSeqno);
const numB = Number(b.extnsSeqno);
if (!isNaN(numA) && !isNaN(numB)) {
return numA - numB;
}
// 숫자 변환이 실패하면 문자열로 비교
return String(a.extnsSeqno).localeCompare(String(b.extnsSeqno));
});
sortedWaterQltyInfo.forEach(item => {
if(item.extnsSeqno == '0'){
item.extnsSeqno = '최초';
}
});
// 법적수질정보 초기화 및 push
gridListLegalWaterQlty = [];
gridListLegalWaterQlty.push({
bod: facilityWaterQltyInfo[0]?.bod ?? 0,
cod: Number(facilityWaterQltyInfo[0]?.cod ?? 0),
ss: Number(facilityWaterQltyInfo[0]?.ss ?? 0),
tn: Number(facilityWaterQltyInfo[0]?.tn ?? 0),
tp: Number(facilityWaterQltyInfo[0]?.tp ?? 0),
totClfgrpcnt : Number(facilityWaterQltyInfo[0]?.totClfgrpcnt ?? 0),
});
// 마지막 수질 데이터의 값을 gridListPlanInfo에 업데이트
if (sortedWaterQltyInfo.length > 0) {
const lastWaterQltyInfo = sortedWaterQltyInfo[sortedWaterQltyInfo.length - 1];
set((state: ProcessFacilityType) => ({
...state,
ProcessFacilityDetail: {
...state.ProcessFacilityDetail,
gridListFcltyWaterQltyInfo: sortedWaterQltyInfo,
gridListLegalWaterQlty: gridListLegalWaterQlty,
gridListPlanInfo: [{
...state.ProcessFacilityDetail.gridListPlanInfo[0],
dsInflWqltBod: lastWaterQltyInfo?.dsInflWqltBod === 'NULL' || lastWaterQltyInfo?.dsInflWqltBod === undefined ? '' : lastWaterQltyInfo.dsInflWqltBod,
dsInflWqltToc: lastWaterQltyInfo?.dsInflWqltToc === 'NULL' || lastWaterQltyInfo?.dsInflWqltToc === undefined ? '' : lastWaterQltyInfo.dsInflWqltToc,
dsInflWqltCod: lastWaterQltyInfo?.dsInflWqltCod === 'NULL' || lastWaterQltyInfo?.dsInflWqltCod === undefined ? '' : lastWaterQltyInfo.dsInflWqltCod,
dsInflWqltSs: lastWaterQltyInfo?.dsInflWqltSs === 'NULL' || lastWaterQltyInfo?.dsInflWqltSs === undefined ? '' : lastWaterQltyInfo.dsInflWqltSs,
dsInflWqltTn: lastWaterQltyInfo?.dsInflWqltTn === 'NULL' || lastWaterQltyInfo?.dsInflWqltTn === undefined ? '' : lastWaterQltyInfo.dsInflWqltTn,
dsInflWqltTp: lastWaterQltyInfo?.dsInflWqltTp === 'NULL' || lastWaterQltyInfo?.dsInflWqltTp === undefined ? '' : lastWaterQltyInfo.dsInflWqltTp,
plInflWqltBod: lastWaterQltyInfo?.plInflWqltBod === 'NULL' || lastWaterQltyInfo?.plInflWqltBod === undefined ? '' : lastWaterQltyInfo.plInflWqltBod,
plInflWqltToc: lastWaterQltyInfo?.plInflWqltToc === 'NULL' || lastWaterQltyInfo?.plInflWqltToc === undefined ? '' : lastWaterQltyInfo.plInflWqltToc,
plInflWqltCod: lastWaterQltyInfo?.plInflWqltCod === 'NULL' || lastWaterQltyInfo?.plInflWqltCod === undefined ? '' : lastWaterQltyInfo.plInflWqltCod,
plInflWqltSs: lastWaterQltyInfo?.plInflWqltSs === 'NULL' || lastWaterQltyInfo?.plInflWqltSs === undefined ? '' : lastWaterQltyInfo.plInflWqltSs,
plInflWqltTn: lastWaterQltyInfo?.plInflWqltTn === 'NULL' || lastWaterQltyInfo?.plInflWqltTn === undefined ? '' : lastWaterQltyInfo.plInflWqltTn,
plInflWqltTp: lastWaterQltyInfo?.plInflWqltTp === 'NULL' || lastWaterQltyInfo?.plInflWqltTp === undefined ? '' : lastWaterQltyInfo.plInflWqltTp
}]
}
}));
} else {
set((state: ProcessFacilityType) => ({
...state,
ProcessFacilityDetail: {
...state.ProcessFacilityDetail,
gridListFcltyWaterQltyInfo: sortedWaterQltyInfo,
gridListLegalWaterQlty: gridListLegalWaterQlty,
gridListPlanInfo: [{
...state.ProcessFacilityDetail.gridListPlanInfo[0],
dsInflWqltBod: '',
dsInflWqltToc: '',
dsInflWqltCod: '',
dsInflWqltSs: '',
dsInflWqltTn: '',
dsInflWqltTp: '',
plEfflWqltBod: '',
plEfflWqltToc: '',
plEfflWqltCod: '',
plEfflWqltSs: '',
plEfflWqltTn: '',
plEfflWqltTp: ''
}]
}
}));
}
}
}
}
@@ -0,0 +1,97 @@
export type TableUpperProps = {
id: string;
title: string;
upName?: string;
upSequnce?: number;
}
type PublicReuseFacility = {
sido: string,
sigungo: string,
facilityName: string,
location: string,
locationgubun: string,
facilityCapacity: string,
yn?: string,
authoDay?: string,
area?: string,
reuse?: string,
register?: string
}
const upHeadList:TableUpperProps[] = [
{ id: "sido", title: "시도"},
{ id: "sigungo", title: "시군구"},
{ id: "facilityName", title: "시설명"},
{ id: "location", title: "위치"},
{ id: "locationgubun", title: "지역구분"},
{ id: "facilityCapacity", title: "시설용량"},
{ id: "yn", title: '여부', upName:'upChangeRe' , upSequnce:1 },
{ id: "authoDay", title: '허가일',upName:'upChangeRe', upSequnce:2 },
{ id: "area", title: '면적' ,upName:'upChangeRe',upSequnce:3 }];
const publicReuseFacilityUper:PublicReuseFacility[] = [
{
sido: "서울",
sigungo: "강남구",
facilityName: "난지",
location: "경기도 고양시 덕양구 대차로 4가",
locationgubun: "500",
facilityCapacity: "800,000",
reuse: "Y",
register: "Y"
},
{
sido: "서울",
sigungo: "강남구",
facilityName: "난지",
location: "경기도 고양시 덕양구 대차로 4가",
locationgubun: "500",
facilityCapacity: "800,000",
reuse: "Y",
register: "Y"
},
{
sido: "서울",
sigungo: "강남구",
facilityName: "난지",
location: "경기도 고양시 덕양구 대차로 4가",
locationgubun: "500",
facilityCapacity: "800,000",
yn: "Y",
authoDay: "2012",
area:"123"
},
]
export type tableState = {
isInit: boolean;
upHeadList: TableUpperProps[];
publicReuseFacilityUper: PublicReuseFacility[];
}
type tableActions = {
decrementList: () => void
}
export type tableType = tableState & tableActions;
export const tableStateExport:tableState= {
isInit:false,
upHeadList: upHeadList,
publicReuseFacilityUper: publicReuseFacilityUper,
}
// 상태 설정 함수 타입 정의
type SetStateFunction = (state: tableState | ((state: tableState) => tableState)) => void;
export const tableActionsExport:(set:SetStateFunction)=>tableActions=(set: SetStateFunction) => {
return {
decrementList:()=>set(
(state:tableState) => {
return {
...state,
publicReuseFacilityUper:
state.publicReuseFacilityUper.slice(0, state.publicReuseFacilityUper.length - 1)
}}
)
}
}
+274
View File
@@ -0,0 +1,274 @@
import DataForm from "@/app/components/common/DataForm";
import type { TableUpperProps } from "../dashboard";
import { getFetch } from "@/app/components/api";
export type { TableUpperProps };
/**====================================
* Store - 타입
====================================*/
/**
* (1) 메인페이지
*/
// 시설별 강수현황
export type RdiProps = {
bscFctCd : string,
sido : string,
sigungo : string,
bscFctNm : string,
lclgvrnmntNm : string,
daddr : string,
fctCpc : number,
summerMinBodValue : number,
inflAmnt : number,
winterAvgBodLowRainfall : number,
rdiiValue : number,
infoRnDay : number,
rgnNm : string,
rvbsn : string,
lgbsn : string,
mdmbsn : string,
}
export interface RdiInterface {
bscFctCd : string,
sido : string,
sigungo : string,
bscFctNm : string,
lclgvrnmntNm : string,
daddr : string,
fctCpc : number,
summerMinBodValue : number,
inflAmnt : number,
winterAvgBodLowRainfall : number,
rdiiValue : number,
infoRnDay : number,
rgnNm : string,
rvbsn : string,
lgbsn : string,
mdmbsn : string,
}
// 모달
export interface RdiInputModalProps {
msrDt : string,
realSewageInput : number | '',
sewageOccur : number | '',
underWaterInput : number | '',
}
/**====================================
* Store - data
====================================*/
/**
* (1) 메인페이지
*/
// 검색단
const LabelArray:string[] = ['구분', '시도', '시군구','용량별', '시설명', '조회기간', '시설상태', '권역', '단위유역', '중권역', '환경청별'];
export interface ExcelType {
시설코드: string,
권역:string|'',
단위유역:string|'',
중권역:string|'',
환경청:string|'',
시도: string,
시군구: string,
시설명: string,
주소: string,
시설용량: number,
하절기강우일최저유입BOD: number | '',
하절기최저유입BOD유입유량: number | '',
동절기청천시유입BOD: number | '',
RDII추정 : number | '',
RDII추정시일강수량 : number | '',
}
// 리스트 헤더
const upHeadList1:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 },
{ id: "A", title: "행정구역", row:0, colSpan:2 },
{ id: "sido", title: "시도", upName:"upChangeRe", upSequnce:1, row:1 },
{ id: "sigungo", title: "시군구", upName:"upChangeRe",upSequnce:2, row:1 },
{ id: "bscFctNm", title: "시설명", row:0, rowSpan:2 },
{ id: "daddr", title: "주소", row:0, rowSpan:2 },
{ id: "fctCpc", title: "시설용량\n(m³/일)", align:"right", row:0, rowSpan:2 },
{ id: "B", title: "하절기 운영현황(6~8월)", row:0, colSpan:2 },
{ id: "summerMinBodValue", title: "3mm 이상 강우일 중 최저 유입BOD\n(mg/L)", align:"right", row:1 },
{ id: "inflAmnt", title: "하절기최저유입BOD유입유량\n(m³/일)", row:1 },
{ id: "C", title: "동절기 운영현황(1~2월)", row:0 },
{ id: "winterAvgBodLowRainfall", title: "청천시 유입BOD\n(mg/L)", row:1 },
{ id: "rdiiValue", title: "RDII추정", row:0, rowSpan:2 },
{ id: "infoRnDay", title: "RDII추정시일강수량\n(mm)", row:0, rowSpan:2 },
];
const upHeadList2:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 },
{ id: "A", title: "유역", row:0, colSpan:3 },
{ id: "rvbsn", title: "권역", upName:"upChangeRe", upSequnce:1, row:1 },
{ id: "lgbsn", title: "단위유역", upName:"upChangeRe", upSequnce:2, row:1 },
{ id: "mdmbsn", title: "중권역", upName:"upChangeRe", upSequnce:3, row:1 },
{ id: "bscFctNm", title: "시설명", row:0, rowSpan:2 },
{ id: "daddr", title: "주소", row:0, rowSpan:2 },
{ id: "fctCpc", title: "시설용량(m³/일)", align:"right", row:0, rowSpan:2 },
{ id: "B", title: "하절기 운영현황(6~8월)", row:0, colSpan:2 },
{ id: "summerMinBodValue", title: "하절기강우일최저유입BOD", align:"right", row:1 },
{ id: "inflAmnt", title: "하절기최저유입BOD유입유량", row:1 },
{ id: "D", title: "동절기 운영현황(1~2월)", row:0 },
{ id: "winterAvgBodLowRainfall", title: "동절기청천시유입BOD", row:1 },
{ id: "rdiiValue", title: "RDII추정", row:0, rowSpan:2 },
{ id: "infoRnDay", title: "RDII추정시일강수량", row:0, rowSpan:2 },
];
const upHeadList3:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 },
{ id: "A", title: "유역", row:0, colSpan:3 },
{ id: "rvbsn", title: "권역", upName:"upChangeRe1", upSequnce:1, row:1 },
{ id: "lgbsn", title: "단위유역", upName:"upChangeRe1", upSequnce:2, row:1 },
{ id: "mdmbsn", title: "중권역", upName:"upChangeRe1", upSequnce:3, row:1 },
{ id: "rgnNm", title: "환경청", row:0, rowSpan:2 },
{ id: "B", title: "행정구역", row:0, colSpan:2 },
{ id: "sido", title: "시도", upName:"upChangeRe2", upSequnce:1, row:1 },
{ id: "sigungo", title: "시군구", upName:"upChangeRe2",upSequnce:2, row:1 },
{ id: "bscFctNm", title: "시설명", row:0, rowSpan:2 },
{ id: "daddr", title: "주소", row:0, rowSpan:2 },
{ id: "fctCpc", title: "시설용량(m³/일)", align:"right", row:0, rowSpan:2 },
{ id: "C", title: "하절기 운영현황(6~8월)", row:0, colSpan:2 },
{ id: "summerMinBodValue", title: "하절기강우일최저유입BOD", align:"right", row:1 },
{ id: "inflAmnt", title: "하절기최저유입BOD유입유량", row:1 },
{ id: "D", title: "동절기 운영현황(1~2월)", row:0 },
{ id: "winterAvgBodLowRainfall", title: "동절기청천시유입BOD", row:1 },
{ id: "rdiiValue", title: "RDII추정", row:0, rowSpan:2 },
{ id: "infoRnDay", title: "RDII추정시일강수량", row:0, rowSpan:2 },
];
const rdiList:RdiProps[] = [
]
/**====================================
* Store - API
====================================*/
const fetchRdiList = async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => {
try{
const tailStr = partCd ? '/'+ partCd + '/' + nameCd + '/' + fcltCpct + '/' + operationValue + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : '';
const url = '/rdii/list/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr;
console.log(url);
const res = await getFetch('fo', url);
return { data: res.data, count: res.count }
}catch(error){
console.log(error);
return { data: rdiList, count: 0 };
}
}
/**====================================
* Store - expaort
====================================*/
export type RdiActions = {
searchRdiList: (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => void;
}
export type RdiType = {
RdiSearch: {
isInit: boolean;
labelArray:string[];
},
Rdi : {
isInit: boolean;
upHeadList1: TableUpperProps[];
upHeadList2: TableUpperProps[];
upHeadList3: TableUpperProps[];
rdiList: RdiProps[]; // Changed from facilityAreaProps to ProcessAreaProps
rdiCount: number;
excelList: ExcelType[];
},
}
export const rdiInitState: RdiType = {
RdiSearch: {
isInit: false,
labelArray: LabelArray,
},
Rdi : {
isInit : false,
upHeadList1 : upHeadList1,
upHeadList2 : upHeadList2,
upHeadList3 : upHeadList3,
rdiList : rdiList,
rdiCount : 2,
excelList : []
}
}
const dataFormSetting=(item : RdiProps)=> {
DataForm({ type: 'float1', column: 'fctCpc', item: item as unknown as Record<string, unknown> });
DataForm({ type: 'float1', column: 'summerMinBodValue', item: item as unknown as Record<string, unknown>});
DataForm({ type: 'float1', column: 'winterAvgBodLowRainfall', item: item as unknown as Record<string, unknown>});
DataForm({ type: 'float2', column: 'rdiiValue', item: item as unknown as Record<string, unknown>});
DataForm({ type: 'float2', column: 'infoRnDay', item: item as unknown as Record<string, unknown>});
}
// Add type definitions for state setter function
type SetStateFunction = (state: RdiType | ((state: RdiType) => RdiType)) => void;
export type RdiStore = RdiType & RdiActions;
export const rdiReducer:(set:SetStateFunction)=>RdiActions=(set: SetStateFunction) => {
console.log("tableActionsExport");
return {
searchRdiList: async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => {
const res = await fetchRdiList(pageNo, pageSize, searchYear, partCd, nameCd, fcltCpct, operationValue, fcltyNm);
const data = res.data;
const excelDownList: ExcelType[] = [];
const rtnData = data.map((item: RdiProps) => {
return {
...item,
sido: item.lclgvrnmntNm.split(' ')[0]?.toString() || '',
sigungo: item.lclgvrnmntNm.split(' ')[1]?.toString() || '',
inflAmnt: item.inflAmnt?item.inflAmnt.toFixed(1) : ''
}
})
rtnData.forEach((item: RdiProps) => {
dataFormSetting(item)
})
if(pageSize>5000){
rtnData.forEach((item: RdiProps) => {
excelDownList.push({
시설코드: item.bscFctCd,
권역: item.rvbsn,
단위유역: item.lgbsn,
중권역: item.mdmbsn,
환경청: item.rgnNm,
시설명: item.bscFctNm,
시도: item.sido,
시군구: item.sigungo,
주소: item.daddr,
시설용량: item.fctCpc,
하절기강우일최저유입BOD: item.summerMinBodValue,
하절기최저유입BOD유입유량: item.inflAmnt,
동절기청천시유입BOD: item.winterAvgBodLowRainfall,
RDII추정 : item.rdiiValue,
RDII추정시일강수량 : item.infoRnDay,
});
});
}
set((state: RdiType) => ({
...state,
Rdi: pageSize<5000?{
...state.Rdi,
rdiList: rtnData,
rdiCount: res.count,
}:{
...state.Rdi,
excelList: excelDownList,
}
}))
},
}
}
+464
View File
@@ -0,0 +1,464 @@
import DataForm from "@/app/components/common/DataForm";
import type { TableUpperProps } from "../dashboard";
import { getFetch } from "@/app/components/api";
export type { TableUpperProps };
/**====================================
* Store - 타입
====================================*/
/**
* (1) 메인페이지
*/
// 시설별 강수현황
export type SewageOccurProps = {
bscFctCd : string,
sidoNm : string,
sggNm : string,
lclgvrnmntNm : string,
bscFctNm : string,
daddr : string,
fctCpc : number,
waterUsage : number,
swageChangePercent : number,
estimationSewage : number,
sewageInputYear : number,
sewageInputChungchun : number,
rgnNm : string,
rvbsn : string,
lgbsn : string,
mdmbsn : string,
msrDt : string,
}
export type SewageOccurDetailProps = {
msrDt : string,
waterUsage : number,
swageChangePercent : number,
estimationSewage : number,
}
/**====================================
* Store - data
====================================*/
/**
* (1) 메인페이지
*/
// 검색단
const LabelArray:string[] = ['구분', '시도', '시군구','용량별', '시설명', '조회기간', '시설상태', '권역', '단위유역', '중권역', '환경청별'];
export interface ExcelType {
시설코드: string,
권역:string|'',
단위유역:string|'',
중권역:string|'',
환경청:string|'',
시도: string,
시군구: string,
시설명: string,
주소: string,
시설용량: number,
물사용량: number | '',
오수전환율: number | '',
오수발생량추정: number | '',
연간하수유입량: number | '',
청천시하수유입량: number | '',
}
// 리스트 헤더
const upHeadList1:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 },
{ id: "A", title: "행정구역", row:0, colSpan:2 },
{ id: "sidoNm", title: "시도", upName:"upChangeRe", upSequnce:1, row:1 },
{ id: "sggNm", title: "시군구", upName:"upChangeRe",upSequnce:2, row:1 },
{ id: "bscFctNm", title: "시설명", row:0, rowSpan:2 },
{ id: "daddr", title: "주소", row:0, rowSpan:2 },
{ id: "fctCpc", title: "시설용량(m³/일)", align:"right", row:0, rowSpan:2 },
{ id: "waterUsage", title: "물사용량(m³/일)", align:"right", row:0, rowSpan:2 },
{ id: "swageChangePercent", title: "오수전환율\n(%)", align:"right", row:0, rowSpan:2 },
{ id: "estimationSewage", title: "오수발생량 추정\n(m³/일)", align:"right", row:0, rowSpan:2 },
{ id: "sewageInputYear", title: "연간 하수유입량\n(m³/일)", align:"right", row:0, rowSpan:2 },
{ id: "sewageInputChungchun", title: "청천시 하수유입량\n(m³/일)", align:"right", row:0, rowSpan:2 },
];
const upHeadList2:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 },
{ id: "A", title: "유역", row:0, colSpan:3 },
{ id: "rvbsn", title: "권역", upName:"upChangeRe", upSequnce:1, row:1 },
{ id: "lgbsn", title: "단위유역", upName:"upChangeRe", upSequnce:2, row:1 },
{ id: "mdmbsn", title: "중권역", upName:"upChangeRe", upSequnce:3, row:1 },
{ id: "bscFctNm", title: "시설명", row:0, rowSpan:2 },
{ id: "daddr", title: "주소", row:0, rowSpan:2 },
{ id: "fctCpc", title: "시설용량(m³/일)", align:"right", row:0, rowSpan:2 },
{ id: "waterUsage", title: "물사용량(m³/일)", align:"right", row:0, rowSpan:2 },
{ id: "swageChangePercent", title: "오수전환율", align:"right", row:0, rowSpan:2 },
{ id: "estimationSewage", title: "오수발생량 추정\n(m³/일)", align:"right", row:0, rowSpan:2 },
{ id: "sewageInputYear", title: "연간 하수유입량\n(m³/일)", align:"right", row:0, rowSpan:2 },
{ id: "sewageInputChungchun", title: "청천시 하수유입량\n(m³/일)", align:"right", row:0, rowSpan:2 },
];
const upHeadList3:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 },
{ id: "A", title: "유역", row:0, colSpan:3 },
{ id: "rvbsn", title: "권역", upName:"upChangeRe1", upSequnce:1, row:1 },
{ id: "lgbsn", title: "단위유역", upName:"upChangeRe1", upSequnce:2, row:1 },
{ id: "mdmbsn", title: "중권역", upName:"upChangeRe1", upSequnce:3, row:1 },
{ id: "rgnNm", title: "환경청", row:0, rowSpan:2 },
{ id: "B", title: "행정구역", row:0, colSpan:2 },
{ id: "sidoNm", title: "시도", upName:"upChangeRe2", upSequnce:1, row:1 },
{ id: "sggNm", title: "시군구", upName:"upChangeRe2",upSequnce:2, row:1 },
{ id: "bscFctNm", title: "시설명", row:0, rowSpan:2 },
{ id: "daddr", title: "주소", row:0, rowSpan:2 },
{ id: "fctCpc", title: "시설용량(m³/일)", align:"right", row:0, rowSpan:2 },
{ id: "waterUsage", title: "물사용량(m³/일)", align:"right", row:0, rowSpan:2 },
{ id: "swageChangePercent", title: "오수전환율", align:"right", row:0, rowSpan:2 },
{ id: "estimationSewage", title: "오수발생량 추정\n(m³/일)", align:"right", row:0, rowSpan:2 },
{ id: "sewageInputYear", title: "연간 하수유입량\n(m³/일)", align:"right", row:0, rowSpan:2 },
{ id: "sewageInputChungchun", title: "청천시 하수유입량\n(m³/일)", align:"right", row:0, rowSpan:2 },
];
const upHeadListSewageOccurDetail:TableUpperProps[] = [
{ id: "msrDt", title: "년월" },
{ id: "waterUsage", title: "물사용량(m³/일)" },
{ id: "swageChangePercent", title: "오수전환율" },
{ id: "estimationSewage", title: "오수발생량 추정(m³/일)", row:0, rowSpan:2 },
];
const sewageOccurList:SewageOccurProps[] = [
]
const tempSewageOccurList:SewageOccurDetailProps[] = [
{
msrDt: '2024-01',
waterUsage: 100,
swageChangePercent: 80,
estimationSewage: 60,
},
{
msrDt: '2024-02',
waterUsage: 106,
swageChangePercent: 80,
estimationSewage: 80,
},
{
msrDt: '2024-03',
waterUsage: 100,
swageChangePercent: 80,
estimationSewage: 80,
},
{
msrDt: '2024-04',
waterUsage: 100,
swageChangePercent: 80,
estimationSewage: 80,
},
{
msrDt: '2024-05',
waterUsage: 100,
swageChangePercent: 10,
estimationSewage: 80,
},
{
msrDt: '2024-06',
waterUsage: 100,
swageChangePercent: 80,
estimationSewage: 80,
},
{
msrDt: '2024-07',
waterUsage: 100,
swageChangePercent: 580,
estimationSewage: 80,
},
{
msrDt: '2024-08',
waterUsage: 100,
swageChangePercent: 80,
estimationSewage: 80,
},
{
msrDt: '2024-09',
waterUsage: 100,
swageChangePercent: 80,
estimationSewage: 80,
},
{
msrDt: '2024-10',
waterUsage: 100,
swageChangePercent: 80,
estimationSewage: 80,
},
{
msrDt: '2024-11',
waterUsage: 100,
swageChangePercent: 80,
estimationSewage: 80,
},
{
msrDt: '2024-12',
waterUsage: 100,
swageChangePercent: 80,
estimationSewage: 80,
},
{
msrDt: '2025-01',
waterUsage: 100,
swageChangePercent: 80,
estimationSewage: 80,
},
{
msrDt: '2025-02',
waterUsage: 100,
swageChangePercent: 80,
estimationSewage: 80,
},
{
msrDt: '2025-03',
waterUsage: 100,
swageChangePercent: 80,
estimationSewage: 80,
},
{
msrDt: '2025-04',
waterUsage: 100,
swageChangePercent: 80,
estimationSewage: 80,
},
{
msrDt: '2025-05',
waterUsage: 100,
swageChangePercent: 80,
estimationSewage: 80,
},
{
msrDt: '2025-06',
waterUsage: 100,
swageChangePercent: 80,
estimationSewage: 80,
},
{
msrDt: '2025-07',
waterUsage: 100,
swageChangePercent: 80,
estimationSewage: 80,
},
{
msrDt: '2025-08',
waterUsage: 100,
swageChangePercent: 0.8,
estimationSewage: 80,
},
{
msrDt: '2025-09',
waterUsage: 100,
swageChangePercent: 80,
estimationSewage: 80,
},
{
msrDt: '2025-10',
waterUsage: 100,
swageChangePercent: 80,
estimationSewage: 80,
},
{
msrDt: '2025-11',
waterUsage: 100,
swageChangePercent: 80,
estimationSewage: 80,
},
{
msrDt: '2025-12',
waterUsage: 100,
swageChangePercent: 80,
estimationSewage: 80,
},
]
/**====================================
* Store - API
====================================*/
const fetchSewageOccurList = async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => {
// 20250701임시시
console.log("partCd", partCd);
console.log("nameCd", nameCd);
console.log("fcltCpct", fcltCpct);
console.log("operationValue", operationValue);
console.log("fcltyNm", fcltyNm);
console.log("searchYear", searchYear);
const tailStr = partCd ? '/'+ partCd + '/' + nameCd + '/' + fcltCpct + '/' + operationValue + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : '';
const url = '/fclt/flow/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr;
const res = await getFetch('fo', url);
console.log("res", res.data);
return { data: res.data, count: res.count }
/*
const tailStr = partCd ? '/'+ partCd + '/' + nameCd + '/' + fcltCpct + '/' + operationValue + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : '';
const url = '/fclt/qlty/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr;
const res = await getFetch('fo', url);
return { data: res.data, count: res.count } */
}
/**====================================
* Store - expaort
====================================*/
export type SewageOccurActions = {
searchSewageOccurList: (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => void;
}
export type SewageOccurType = {
SewageOccurSearch: {
isInit: boolean;
labelArray:string[];
},
SewageOccur : {
isInit: boolean;
upHeadList1: TableUpperProps[];
upHeadList2: TableUpperProps[];
upHeadList3: TableUpperProps[];
sewageOccurList: SewageOccurProps[]; // Changed from facilityAreaProps to ProcessAreaProps
sewageOccurCount: number;
excelList: ExcelType[];
},
SewageOccurDetail : {
isInit: boolean;
upHeadListSewageOccurDetail: TableUpperProps[];
gridListSewageOccurDetail: SewageOccurDetailProps[];
}
}
export const sewageOccurInitState: SewageOccurType = {
SewageOccurSearch: {
isInit: false,
labelArray: LabelArray,
},
SewageOccur : {
isInit : false,
upHeadList1 : upHeadList1,
upHeadList2 : upHeadList2,
upHeadList3 : upHeadList3,
sewageOccurList : sewageOccurList,
sewageOccurCount : 2,
excelList : []
},
SewageOccurDetail : {
isInit: false,
upHeadListSewageOccurDetail: upHeadListSewageOccurDetail,
gridListSewageOccurDetail: tempSewageOccurList,
}
}
const dataFormSetting=(item : SewageOccurProps)=> {
DataForm({ type: 'float1', column: 'fctCpc', item: item as unknown as Record<string, unknown> });
DataForm({ type: 'float1', column: 'waterUsage', item: item as unknown as Record<string, unknown>});
DataForm({ type: 'float2', column: 'estimationSewage', item: item as unknown as Record<string, unknown>});
DataForm({ type: 'float2', column: 'sewageInputYear', item: item as unknown as Record<string, unknown>});
DataForm({ type: 'float2', column: 'sewageInputChungchun', item: item as unknown as Record<string, unknown>});
}
export type SewageOccurStore = SewageOccurType & SewageOccurActions;
// Add type definitions for API responses
type FlowDataItem = {
inflAmnt: string | number;
infoRnDay: string | number;
bscFctCd: string;
msrDt: string;
}
type FlowResponse = {
data: FlowDataItem[];
count: number;
}
type SetStateFunction = (state: SewageOccurType | ((state: SewageOccurType) => SewageOccurType)) => void;
export const sewageOccurReducer:(set:SetStateFunction)=>SewageOccurActions=(set: SetStateFunction) => {
return {
searchSewageOccurList: async (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => {
const res = await fetchSewageOccurList(pageNo, pageSize, searchYear, part, nameCd, fcltCpct, operationValue, fcltyNm);
const data = res.data;
// Promise.all을 사용하여 모든 비동기 작업이 완료될 때까지 기다림
const calData = await Promise.all(data.map(async (item: SewageOccurProps) => {
item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || '';
item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || '';
item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, ''));
item.waterUsage = item.fctCpc * 0.6;
item.swageChangePercent = 80;
item.estimationSewage = item.waterUsage * (item.swageChangePercent/100);
const bscFctCd = item.bscFctCd;
const url2 = '/fclt/flow/' + bscFctCd + '/' + searchYear;
const res2 = await getFetch('fo', url2) as FlowResponse;
const sumFlow = res2.data.reduce((acc: number, curr: FlowDataItem) => Number(acc) + Number(curr.inflAmnt), 0);
let sumChungcunDay = 0;
let chungcunSumFlow = 0;
res2.data.forEach((item: FlowDataItem) => {
if(Number(item.infoRnDay) ===0){
chungcunSumFlow += Number(item.inflAmnt);
sumChungcunDay += 1;
}else{
console.log(item.bscFctCd +' day : ' +' '+item.msrDt);
}
});
item.sewageInputYear = sumFlow/res2.count;
item.sewageInputChungchun = chungcunSumFlow===0?0:(chungcunSumFlow / sumChungcunDay);
return item;
}));
console.log('calData : ', calData);
const excelDownList: ExcelType[] = [];
if(pageSize>5000){
calData.forEach(item => {
dataFormSetting(item)
item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, ''));
/* item.ctpvNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || '';
item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || ''; */
excelDownList.push({
시설코드: item.bscFctCd,
권역: item.rvbsn,
단위유역: item.lgbsn,
중권역: item.mdmbsn,
환경청: item.rgnNm,
시도: item.sidoNm,
시군구: item.sggNm,
시설명: item.bscFctNm,
주소: item.daddr,
시설용량: item.fctCpc,
물사용량: item.waterUsage,
오수전환율: item.swageChangePercent,
오수발생량추정: item.estimationSewage,
연간하수유입량: item.sewageInputYear,
청천시하수유입량: item.sewageInputChungchun,
});
});
}else{
calData.forEach((item: SewageOccurProps) => {
dataFormSetting(item)
/* item.sidoNmNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || '';
item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || ''; */
});
}
set((state: SewageOccurType) => ({
...state,
SewageOccur: pageSize<5000?{
...state.SewageOccur,
sewageOccurList: calData,
sewageOccurCount: res.count,
}:{
...state.SewageOccur,
excelList: excelDownList,
}
}))
},
}
}
+78
View File
@@ -0,0 +1,78 @@
import { createStore } from 'zustand/vanilla';
import { CommonReducer, commonState, type CommonStore, type CommonType } from './common';
import { dashBoardReducer, dashBoardInitState, type DashBoardStore, type DashBoardType } from './dashboard';
import {
facilityPrecipitationInitState,
facilityPrecipitationReducer,
type FacilityPrecipitationStore, type FacilityPrecipitationType
} from './facilityPrecipitation';
import { planSewageInitState, planSewageReducer, type PlanSewageStore, type PlanSewageType } from './planSewage';
import { flowRateSearchInitState, flowRateSearchReducer, type FlowRateSearchStore, type FlowRateSearchType } from './flowRate';
import {
processAreaInitState,
processAreaReducer,
type ProcessAreaStore, type ProcessAreaType
} from './processArea';
import {
processFacilityInitState,
processFacilityReducer,
type ProcessFacilityStore, type ProcessFacilityType
} from './processFacility';
import {
facilityWaterQltyInitState,
facilityWaterQltyReducer,
type FacilityWaterQltyStore, type FacilityWaterQltyType
} from './facilityWaterQlty';
import {
sewageOccurInitState,
sewageOccurReducer,
type SewageOccurStore, type SewageOccurType
} from './sewageOccur';
import {
underWaterInitState,
underWaterReducer,
type UnderWaterStore, type UnderWaterType
} from './underWaterInput';
import {
rdiInitState,
rdiReducer,
type RdiStore, type RdiType
} from './rdi';
import { tableActionsExport, tableStateExport, type tableState, type tableType } from './publicReuseFaciltyInsert/index';
export type FoStore = CommonStore & tableType & DashBoardStore & ProcessFacilityStore & ProcessAreaStore & FlowRateSearchStore & FacilityPrecipitationStore & FacilityWaterQltyStore & PlanSewageStore & SewageOccurStore & UnderWaterStore & RdiStore;
export type FoState = CommonType & tableState & ProcessFacilityType & ProcessAreaType & FacilityPrecipitationType & FlowRateSearchType & DashBoardType & FacilityWaterQltyType & PlanSewageType & SewageOccurType & UnderWaterType & RdiType;
export const StoreZus =
(initState: FoState = { ...commonState,
...tableStateExport
, ...processFacilityInitState
, ...processAreaInitState
, ...facilityPrecipitationInitState
, ...dashBoardInitState
, ...flowRateSearchInitState
, ...facilityWaterQltyInitState
, ...planSewageInitState
, ...sewageOccurInitState
, ...underWaterInitState
, ...rdiInitState
}
) => {
return createStore<FoStore>()((set) => {
return {
...initState,
...CommonReducer(set),
...tableActionsExport(set),
...dashBoardReducer(set),
...processFacilityReducer(set),
...processAreaReducer(set),
...facilityPrecipitationReducer(set),
...flowRateSearchReducer(set),
...facilityWaterQltyReducer(set),
...planSewageReducer(set),
...sewageOccurReducer(set),
...underWaterReducer(set),
...rdiReducer(set),
}
})
}
//store 등록하는 부분
+397
View File
@@ -0,0 +1,397 @@
import DataForm from "@/app/components/common/DataForm";
import type { TableUpperProps } from "../dashboard";
import { getFetch } from "@/app/components/api";
export type { TableUpperProps };
/**====================================
* Store - 타입
====================================*/
/**
* (1) 메인페이지
*/
// 시설별 강수현황
export type UnderWaterProps = {
bscFctCd : string,
lclgvrnmntNm : string,
sidoNm : string,
sggNm : string,
bscFctNm : string,
daddr : string,
fctCpc : number,
underWaterInputYear : number,
underWaterInputSummer : number,
underWaterInputWinter : number,
infoRnDay : number,
rgnNm : string,
rvbsn : string,
lgbsn : string,
mdmbsn : string,
}
export interface UnderWaterInterface {
bscFctCd : string,
sidoNm : string,
sggNm : string,
bscFctNm : string,
daddr : string,
fctCpc : number,
underWaterInputYear : number,
underWaterInputSummer : number,
underWaterInputWinter : number,
infoRnDay : number,
rgnNm : string,
rvbsn : string,
lgbsn : string,
mdmbsn : string,
}
// 모달
export interface UnderWaterInputModalProps {
msrDt : string,
realSewageInput : number | '',
sewageOccur : number | '',
underWaterInput : number | '',
}
/**====================================
* Store - data
====================================*/
/**
* (1) 메인페이지
*/
// 검색단
const LabelArray:string[] = ['구분', '시도', '시군구','용량별', '시설명', '조회기간', '시설상태', '권역', '단위유역', '중권역', '환경청별'];
export interface ExcelType {
시설코드: string,
권역:string|'',
단위유역:string|'',
중권역:string|'',
환경청:string|'',
시도: string,
시군구: string,
시설명: string,
주소: string,
시설용량: number,
연간지하수유입량: number | '',
하절기지하수유입량: number | '',
동절기지하수유입량: number | '',
}
// 리스트 헤더
const upHeadList1:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 },
{ id: "A", title: "행정구역", row:0, colSpan:2 },
{ id: "sidoNm", title: "시도", upName:"upChangeRe", upSequnce:1, row:1 },
{ id: "sggNm", title: "시군구", upName:"upChangeRe",upSequnce:2, row:1 },
{ id: "bscFctNm", title: "시설명", row:0, rowSpan:2 },
{ id: "daddr", title: "주소", row:0, rowSpan:2 },
{ id: "fctCpc", title: "시설용량(m³/일)", align:"right", row:0, rowSpan:2 },
{ id: "C", title: "지하수 유입량 추정", align:"right", row:0, colSpan:3 },
{ id: "underWaterInputYear", title: "연간\n(m³/일)", align:"right", row:1 },
{ id: "underWaterInputSummer", title: "하절기\n(m³/일)", align:"right", row:1 },
{ id: "underWaterInputWinter", title: "동절기\n(m³/일)", align:"right", row:1 },
];
const upHeadList2:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 },
{ id: "A", title: "유역", row:0, colSpan:3 },
{ id: "rvbsn", title: "권역", upName:"upChangeRe", upSequnce:1, row:1 },
{ id: "lgbsn", title: "단위유역", upName:"upChangeRe", upSequnce:2, row:1 },
{ id: "mdmbsn", title: "중권역", upName:"upChangeRe", upSequnce:3, row:1 },
{ id: "bscFctNm", title: "시설명", row:0, rowSpan:2 },
{ id: "daddr", title: "주소", row:0, rowSpan:2 },
{ id: "fctCpc", title: "시설용량(m³/일)", align:"right", row:0, rowSpan:2 },
{ id: "C", title: "지하수 유입량 추정", align:"right", row:0, colSpan:3 },
{ id: "underWaterInputYear", title: "연간\n(m³/일)", align:"right", row:1 },
{ id: "underWaterInputSummer", title: "하절기\n(m³/일)", align:"right", row:1 },
{ id: "underWaterInputWinter", title: "동절기\n(m³/일)", align:"right", row:1 },
];
const upHeadList3:TableUpperProps[] = [
{ id: "bscFctCd", title: "시설코드", row:0, rowSpan:2 },
{ id: "A", title: "유역", row:0, colSpan:3 },
{ id: "rvbsn", title: "권역", upName:"upChangeRe1", upSequnce:1, row:1 },
{ id: "lgbsn", title: "단위유역", upName:"upChangeRe1", upSequnce:2, row:1 },
{ id: "mdmbsn", title: "중권역", upName:"upChangeRe1", upSequnce:3, row:1 },
{ id: "rgnNm", title: "환경청", row:0, rowSpan:2 },
{ id: "B", title: "행정구역", row:0, colSpan:2 },
{ id: "sidoNm", title: "시도", upName:"upChangeRe2", upSequnce:1, row:1 },
{ id: "sggNm", title: "시군구", upName:"upChangeRe2",upSequnce:2, row:1 },
{ id: "bscFctNm", title: "시설명", row:0, rowSpan:2 },
{ id: "daddr", title: "주소", row:0, rowSpan:2 },
{ id: "fctCpc", title: "시설용량(m³/일)", align:"right", row:0, rowSpan:2 },
{ id: "C", title: "지하수 유입량 추정", align:"right", row:0, colSpan:3 },
{ id: "underWaterInputYear", title: "연간\n(m³/일)", align:"right", row:1 },
{ id: "underWaterInputSummer", title: "하절기\n(m³/일)", align:"right", row:1 },
{ id: "underWaterInputWinter", title: "동절기\n(m³/일)", align:"right", row:1 },
];
// 모달
const upHeadListUnderWaterDetail:TableUpperProps[] = [
{ id: "msrDt", title: "날짜" },
{ id: "realSewageInput", title: "실제 하수유입량\n(m³/일)" },
{ id: "sewageOccur", title: "오수발생량\n(m³/일)" },
{ id: "underWaterInput", title: "지하수 유입량\n(m³/일)" },
];
const underWaterList:UnderWaterProps[] = [
]
const underWaterDetailList:UnderWaterInputModalProps[] = [
{
msrDt : "2024-01",
realSewageInput : 178007,
sewageOccur : 10.5,
underWaterInput : 80.6,
},
{
msrDt : "2024-02",
realSewageInput : 123007,
sewageOccur : 10.7,
underWaterInput : 80.6,
},
{
msrDt : "2024-03",
realSewageInput : 213007,
sewageOccur : 10.1,
underWaterInput : 80.6,
},
{
msrDt : "2024-04",
realSewageInput : 83007,
sewageOccur : 9.57,
underWaterInput : 80.6,
},
{
msrDt : "2024-05",
realSewageInput : 15007,
sewageOccur : 3.57,
underWaterInput : 80.6,
},
{
msrDt : "2024-06",
realSewageInput : 123007,
sewageOccur : '',
underWaterInput : 80.6,
},
{
msrDt : "2024-07",
realSewageInput : 101007,
sewageOccur : 1,
underWaterInput : 80.6,
},
{
msrDt : "2024-08",
realSewageInput : 103807,
sewageOccur : 10.57,
underWaterInput : '',
},
{
msrDt : "2024-09",
realSewageInput : 9007,
sewageOccur : 10.57,
underWaterInput : 80.6,
},
{
msrDt : "2024-10",
realSewageInput : 163007,
sewageOccur : 10.57,
underWaterInput : 80.6,
},
{
msrDt : "2024-11",
realSewageInput : 123007,
sewageOccur : 10.57,
underWaterInput : 80.6,
},
{
msrDt : "2024-12",
realSewageInput : 1007,
sewageOccur : 10.57,
underWaterInput : 80.6,
},
]
/**====================================
* Store - API
====================================*/
// API response types
interface FlowDataItem {
inflAmnt: number;
infoRnDay: number;
msrDt: string;
}
interface ApiResponse {
data: FlowDataItem[];
count: number;
}
const fetchUnderWaterList = async (pageNo: number, pageSize: number, searchYear: string, partCd?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => {
const tailStr = partCd ? '/'+ partCd + '/' + nameCd + '/' + fcltCpct + '/' + operationValue + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : '';
const url = '/fclt/flow/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr;
const res = await getFetch('fo', url);
return { data: res.data, count: res.count }
/*
const tailStr = partCd ? '/'+ partCd + '/' + nameCd + '/' + fcltCpct + '/' + operationValue + (fcltyNm && fcltyNm !== '' ? '/' + fcltyNm : '') : '';
const url = '/fclt/qlty/' + pageNo + '/' + pageSize + '/' + searchYear + tailStr;
const res = await getFetch('fo', url);
return { data: res.data, count: res.count } */
//return { data: underWaterList, count: 2 };
}
/**====================================
* Store - expaort
====================================*/
export type UnderWaterActions = {
searchUnderWaterList: (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => void;
}
export type UnderWaterType = {
UnderWaterSearch: {
isInit: boolean;
labelArray:string[];
},
UnderWater : {
isInit: boolean;
upHeadList1: TableUpperProps[];
upHeadList2: TableUpperProps[];
upHeadList3: TableUpperProps[];
underWaterList: UnderWaterProps[]; // Changed from facilityAreaProps to ProcessAreaProps
underWaterCount: number;
excelList: ExcelType[];
},
UnderWaterDetail : {
isInit: boolean;
upHeadListUnderWaterDetail: TableUpperProps[];
gridListUnderWaterDetail: UnderWaterInputModalProps[];
}
}
export const underWaterInitState: UnderWaterType = {
UnderWaterSearch: {
isInit: false,
labelArray: LabelArray,
},
UnderWater : {
isInit : false,
upHeadList1 : upHeadList1,
upHeadList2 : upHeadList2,
upHeadList3 : upHeadList3,
underWaterList : underWaterList,
underWaterCount : 4,
excelList : []
},
UnderWaterDetail : {
isInit: false,
upHeadListUnderWaterDetail: upHeadListUnderWaterDetail,
gridListUnderWaterDetail: underWaterDetailList,
}
}
const dataFormSetting=(item : UnderWaterProps)=> {
DataForm({ type: 'float1', column: 'fctCpc', item: item as unknown as Record<string, unknown> });
DataForm({ type: 'float1', column: 'underWaterInputYear', item: item as unknown as Record<string, unknown>});
DataForm({ type: 'float2', column: 'underWaterInputSummer', item: item as unknown as Record<string, unknown>});
DataForm({ type: 'float2', column: 'underWaterInputWinter', item: item as unknown as Record<string, unknown>});
}
export type UnderWaterStore = UnderWaterType & UnderWaterActions;
export const underWaterReducer:(set:(fn: (state: UnderWaterType) => UnderWaterType) => void)=>UnderWaterActions=(set: (fn: (state: UnderWaterType) => UnderWaterType) => void) => {
return {
searchUnderWaterList: async (pageNo: number, pageSize: number, searchYear: string, part?: string, nameCd?: string, fcltCpct?: string, operationValue?: string, fcltyNm?: string) => {
const res = await fetchUnderWaterList(pageNo, pageSize, searchYear, part, nameCd, fcltCpct, operationValue, fcltyNm);
const data = res.data;
const calData = await Promise.all(data.map(async (item: UnderWaterProps) => {
item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || '';
item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || '';
item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, ''));
const bscFctCd = item.bscFctCd;
const url2 = '/fclt/flow/' + bscFctCd + '/' + searchYear;
const res2 = await getFetch('fo', url2) as ApiResponse;
let sumChungcunDay = 0;
let chungcunSumFlow = 0;
let sumSummerChungcunDay = 0;
let sumSummerChungcunFlow = 0;
let sumWinterChungcunDay = 0;
let sumWinterChungcunFlow = 0;
res2.data.forEach((flowItem: FlowDataItem) => {
// 청천시
if(Number(flowItem.infoRnDay) ===0){
sumChungcunDay += 1;
chungcunSumFlow += Number(flowItem.inflAmnt);
if(flowItem.msrDt.substring(4,6) ==='06' || flowItem.msrDt.substring(4,6) ==='07' || flowItem.msrDt.substring(4,6) ==='08'){
sumSummerChungcunDay += 1;
sumSummerChungcunFlow += Number(flowItem.inflAmnt);
}else if(flowItem.msrDt.includes('01') || flowItem.msrDt.includes('02')){
sumWinterChungcunDay += 1;
sumWinterChungcunFlow += Number(flowItem.inflAmnt);
}
}
});
item.underWaterInputYear = Number(chungcunSumFlow==0?0:Math.round((chungcunSumFlow / sumChungcunDay) * 10) / 10) - Number(item.fctCpc * 0.6 * 0.8);
item.underWaterInputSummer = Number(sumSummerChungcunFlow==0?0:Math.round((sumSummerChungcunFlow / sumSummerChungcunDay) * 10) / 10) - Number(item.fctCpc * 0.6 * 0.8);
item.underWaterInputWinter = Number(sumWinterChungcunFlow==0?0:Math.round((sumWinterChungcunFlow / sumWinterChungcunDay) * 10) / 10) - Number(item.fctCpc * 0.6 * 0.8);
return item;
}));
const excelDownList: ExcelType[] = [];
if(pageSize>5000){
calData.forEach(item => {
dataFormSetting(item)
item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, ''));
/* item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || '';
item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || ''; */
excelDownList.push({
시설코드: item.bscFctCd,
권역: item.rvbsn,
단위유역: item.lgbsn,
중권역: item.mdmbsn,
환경청: item.rgnNm,
시도: item.sidoNm,
시군구: item.sggNm,
시설명: item.bscFctNm,
주소: item.daddr,
시설용량: item.fctCpc,
연간지하수유입량: item.underWaterInputYear,
하절기지하수유입량: item.underWaterInputSummer,
동절기지하수유입량: item.underWaterInputWinter,
});
});
}else{
data.forEach((item: UnderWaterProps) => {
dataFormSetting(item)
item.fctCpc = parseFloat(item.fctCpc.toString().replace(/,/g, ''));
/* item.sidoNm = item.lclgvrnmntNm.split(' ')[0]?.toString() || '';
item.sggNm = item.lclgvrnmntNm.split(' ')[1]?.toString() || ''; */
});
}
set((state: UnderWaterType) => ({
...state,
UnderWater: pageSize<5000?{
...state.UnderWater,
underWaterList: calData,
underWaterCount: res.count,
}:{
...state.UnderWater,
excelList: excelDownList,
}
}))
},
}
}
+21
View File
@@ -0,0 +1,21 @@
{
"$schema": "https://ui.shadcn.com/schema.json",
"style": "new-york",
"rsc": true,
"tsx": true,
"tailwind": {
"config": "tailwind.config.ts",
"css": "app/globals.css",
"baseColor": "neutral",
"cssVariables": true,
"prefix": ""
},
"aliases": {
"components": "@/components",
"utils": "@/lib/utils",
"ui": "@/components/ui",
"lib": "@/lib",
"hooks": "@/hooks"
},
"iconLibrary": "lucide"
}
+16
View File
@@ -0,0 +1,16 @@
import { dirname } from "path";
import { fileURLToPath } from "url";
import { FlatCompat } from "@eslint/eslintrc";
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
});
const eslintConfig = [
...compat.extends("next/core-web-vitals", "next/typescript"),
];
export default eslintConfig;
+115
View File
@@ -0,0 +1,115 @@
import { NextRequest, NextResponse } from "next/server";
const AUTH_USER_ID = process.env.NEXT_PUBLIC_AUTH_USER_ID || "token-id";
const CLAIM_NAME = process.env.NEXT_PUBLIC_CLAIM_NAME || "Authorization";
const SERVER_API_URL = process.env.NEXT_PUBLIC_SERVER_API_URL || "";
const GEOSERVER_URL = process.env.NEXT_PUBLIC_GEOSERVER_URL || "";
const LX_GEOSERVER_URL = process.env.NEXT_PUBLIC_LX_GEOSERVER_URL || "";
const SEWERAGE_API_URL = process.env.NEXT_PUBLIC_SEWERAGE_API_URL || "";
export function middleware(req: NextRequest) {
console.log("Middleware 진입:", req.nextUrl.pathname);
const pathname = req.nextUrl.pathname;
const searchParams = req.nextUrl.searchParams;
if (pathname.startsWith("/server")) {
const queryParams = searchParams.toString();
const newUrl = `${SERVER_API_URL}${pathname.replace("/server", "")}${
queryParams ? `?${queryParams}` : ""
}`;
console.log(`[Middleware] Rewriting from '/server' path to: ${newUrl}`);
return NextResponse.rewrite(new URL(newUrl));
}
if (pathname.startsWith("/geoserver")) {
let queryParams = searchParams.toString();
const typename = searchParams.get("TYPENAME");
let newUrl = `${GEOSERVER_URL}${pathname.replace("/geoserver", "")}${queryParams ? `?${queryParams}` : ""}`;
if (
typename == "hasudo:tb_il_swage_prcs_fclt" //하수처리시설
|| typename == "hasudo:tb_ms_swage_prcs_zone" //하수처리구역
|| typename == "hasudo:tb_ms_sbmrg_dngr_map" //침수위험지도
|| typename == "hasudo:tb_ms_flood_dngr_map" //도시침수지도
) {
newUrl = `${GEOSERVER_URL}${pathname.replace("/geoserver", "")}${queryParams ? `?${queryParams}` : ""}`;
}
else if (
typename == "hasudo:tb_ms_swage_ppl" //관로
|| typename == "hasudo:tb_ms_swage_mhl" //맨홀
|| typename == "hasudo:tb_ms_swage_pmstn" //펌프장
|| typename == "hasudo:tb_ms_stofcbr" //우수토실
) {
newUrl = `${LX_GEOSERVER_URL}${pathname.replace("/geoserver", "")}${queryParams ? `?${queryParams}` : ""}`;
}
else if (
typename == "hasudo:om_dt_untrt_swg_rain" //중점우수토실
|| typename == "hasudo:om_wqlt_station" //관측소
|| typename == "hasudo:tb_wr_bdst_ruse_fclt" //재이용시설
|| typename == "hasudo:tb_wr_rnwtr_utztn_fclt" //빗물이용시설
|| typename == "hasudo:tb_wr_rswtr_fclt" //중수도시설
|| typename == "hasudo:tb_wr_htdfl_fclt" //온배수시설
|| typename == "hasudo:tb_wr_ruse_fclt_dmd_co" //수요처
|| typename == "hasudo:tb_wr_tmpr_dmd_co" //잠재수요처
){
const url = req.nextUrl.clone();
url.searchParams.set('TYPENAME', typename.replace("hasudo:",""));
queryParams = url.searchParams.toString();
newUrl = `${SEWERAGE_API_URL}/v1/map/geometry${queryParams ? `?${queryParams}` : ""}`;
}
console.log(`[Middleware] Rewriting from '/geoserver' path to: ${newUrl}`);
return NextResponse.rewrite(new URL(newUrl));
}
if (pathname.startsWith("/sewerage-service/api/v1/map/geometry")) {
const queryParams = searchParams.toString();
const newUrl = `${SEWERAGE_API_URL}${pathname.replace("/sewerage-service/api", "")}${
queryParams ? `?${queryParams}` : ""
}`;
console.log(`[Middleware] Rewriting from '/geoserver' path to: ${newUrl}`);
return NextResponse.rewrite(new URL(newUrl));
}
if (pathname.startsWith("/attach-service") || pathname.startsWith("/cms-service")) {
const requestHeaders = new Headers(req.headers);
const token = req.cookies.get(CLAIM_NAME)?.value;
const userId = req.cookies.get(AUTH_USER_ID)?.value;
const newUrl = `${SERVER_API_URL}${pathname}`;
if (token) requestHeaders.set(CLAIM_NAME, token);
if (userId) requestHeaders.set(AUTH_USER_ID, userId);
console.log(
`[Middleware] Rewriting from '/attach-service' path to: ${newUrl}`,
);
return NextResponse.rewrite(new URL(newUrl), {
headers: requestHeaders,
});
}
if (pathname.startsWith("/user-service")) {
const requestHeaders = new Headers(req.headers);
const newUrl = `${SERVER_API_URL}${pathname}`;
console.log(
`[Middleware] Rewriting from '/user-service' path to: ${newUrl}`,
);
return NextResponse.rewrite(new URL(newUrl), {
headers: requestHeaders,
});
}
return NextResponse.next();
}
export const config = {
matcher: [
"/server/:path*",
"/geoserver/:path*",
"/attach-service/:path*",
"/user-service/:path*",
"/cms-service/:path*",
],
};
+34
View File
@@ -0,0 +1,34 @@
import type { NextConfig } from "next";
const subDomain =
process.env.NEXT_PUBLIC_SUB_DOMAIN || "http://localhost:13001";
const nextConfig: NextConfig = {
output: "standalone",
async rewrites() {
return {
beforeFiles: [
{
source: "/_next/static/:path*",
destination: `${subDomain}/_next/static/:path*`,
has: [
{
type: "header",
key: "referer",
value: "(.*)/pst(.*)",
},
],
},
],
afterFiles: [
{
source: "/pst/:path*",
destination: `${subDomain}/pst/:path*`,
},
],
fallback: [],
};
},
};
export default nextConfig;
+29
View File
@@ -0,0 +1,29 @@
{
"name": "fo",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
},
"dependencies": {
"@common/business_components": "workspace:*",
"@common/components": "workspace:*",
"@tailwindcss/vite": "^4.0.10",
"html2canvas": "^1.4.1",
"next": "15.1.7",
"select": "^1.1.2",
"webpack": "^5.98.0",
"zustand": "^5.0.3"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
"eslint": "^9",
"eslint-config-next": "15.1.7",
"postcss": "^8",
"tailwindcss": "^3.4.17",
"typescript": "^5.6.3"
}
}
+8
View File
@@ -0,0 +1,8 @@
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
tailwindcss: {},
},
};
export default config;
+1
View File
@@ -0,0 +1 @@
<svg fill="none" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg"><path d="M14.5 13.5V5.41a1 1 0 0 0-.3-.7L9.8.29A1 1 0 0 0 9.08 0H1.5v13.5A2.5 2.5 0 0 0 4 16h8a2.5 2.5 0 0 0 2.5-2.5m-1.5 0v-7H8v-5H3v12a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1M9.5 5V2.12L12.38 5zM5.13 5h-.62v1.25h2.12V5zm-.62 3h7.12v1.25H4.5zm.62 3h-.62v1.25h7.12V11z" clip-rule="evenodd" fill="#666" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 391 B

+1
View File
@@ -0,0 +1 @@
<svg fill="none" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><g clip-path="url(#a)"><path fill-rule="evenodd" clip-rule="evenodd" d="M10.27 14.1a6.5 6.5 0 0 0 3.67-3.45q-1.24.21-2.7.34-.31 1.83-.97 3.1M8 16A8 8 0 1 0 8 0a8 8 0 0 0 0 16m.48-1.52a7 7 0 0 1-.96 0H7.5a4 4 0 0 1-.84-1.32q-.38-.89-.63-2.08a40 40 0 0 0 3.92 0q-.25 1.2-.63 2.08a4 4 0 0 1-.84 1.31zm2.94-4.76q1.66-.15 2.95-.43a7 7 0 0 0 0-2.58q-1.3-.27-2.95-.43a18 18 0 0 1 0 3.44m-1.27-3.54a17 17 0 0 1 0 3.64 39 39 0 0 1-4.3 0 17 17 0 0 1 0-3.64 39 39 0 0 1 4.3 0m1.1-1.17q1.45.13 2.69.34a6.5 6.5 0 0 0-3.67-3.44q.65 1.26.98 3.1M8.48 1.5l.01.02q.41.37.84 1.31.38.89.63 2.08a40 40 0 0 0-3.92 0q.25-1.2.63-2.08a4 4 0 0 1 .85-1.32 7 7 0 0 1 .96 0m-2.75.4a6.5 6.5 0 0 0-3.67 3.44 29 29 0 0 1 2.7-.34q.31-1.83.97-3.1M4.58 6.28q-1.66.16-2.95.43a7 7 0 0 0 0 2.58q1.3.27 2.95.43a18 18 0 0 1 0-3.44m.17 4.71q-1.45-.12-2.69-.34a6.5 6.5 0 0 0 3.67 3.44q-.65-1.27-.98-3.1" fill="#666"/></g><defs><clipPath id="a"><path fill="#fff" d="M0 0h16v16H0z"/></clipPath></defs></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 961 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 970 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1016 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 468 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 757 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 785 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 791 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 782 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 782 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 217 B

Some files were not shown because too many files have changed in this diff Show More