fo: 유량현황 강우시 평균 단수 처리

This commit is contained in:
dongsilove
2026-01-10 20:43:46 +09:00
parent ac8c58d2a9
commit 94fa8eb721
4 changed files with 107 additions and 95 deletions
+21 -21
View File
@@ -26,7 +26,7 @@ export const getDaysInMonth = (yyyymm: string) => {
return new Date(year, month, 0).getDate();
}
export const roundNumber = (value : any, decimals: number) => {
export const roundNumber = (value: any, decimals: number) => {
if (typeof value === 'number') {
return addCommas((Math.round(value * Math.pow(10, decimals)) / Math.pow(10, decimals)).toFixed(decimals));
} else if (typeof value === 'string') {
@@ -45,48 +45,48 @@ export const addCommas = (value: string | number): any => {
index === 0 ? part : part.replace(/(\d{3})/g, '$1,')
).join('.');
} else if (typeof value === 'string') {
return value.includes('.')?value.split('.').map((part, index) =>
return value.includes('.') ? value.split('.').map((part, index) =>
index === 0 ? part.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : part
).join('.'):String(value).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
).join('.') : String(value).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
}
export default function DataForm({ type, column, item }: DataFormProps) {
if(item) {
if (item) {
const value = item[column];
switch(type){
switch (type) {
case 'date':
if(value && typeof value === 'string' && value.length >= 8)
if (value && typeof value === 'string' && value.length >= 8 && !value.includes('-'))
item[column] = value.substring(0, 4) + "-" + value.substring(4, 6) + "-" + value.substring(6, 8);
break;
break;
case 'number':
if(value && value != '') {
if (value && value != '') {
item[column] = value.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
break;
break;
case 'float0':
if(value) {
item[column] = item[column] = String(value).includes('.')?String(Math.round(parseFloat(value.toString().replace(/,/g, '')) * 1) / 1).split('.').map((part, index) =>
index === 0 ? part.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : part
).join('.')
: String(value).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
if (value) {
item[column] = item[column] = String(value).includes('.') ? String(Math.round(parseFloat(value.toString().replace(/,/g, '')) * 1) / 1).split('.').map((part, index) =>
index === 0 ? part.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : part
).join('.')
: String(value).replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
break;
break;
case 'float1':
if(value) {
if (value) {
item[column] = roundNumber(value, 1);
}
break;
break;
case 'float2':
if(value) {
if (value) {
item[column] = roundNumber(value, 2);
}
break;
break;
case 'float3':
if(value) {
if (value) {
item[column] = roundNumber(value, 3);
}
break;
break;
}
}
}
@@ -4,14 +4,14 @@ 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;
type FacilityTermModalHeaderProps<T extends { msrDt: string }> = { // T는 반드시 msrDt: string 필드를 가져야 한다
fcltyCd: string;
fcltyName: string;
tabName: string;
searchYear: string;
searchList: T[];
ref: React.RefObject<ReturnType<T>>;
queryClick:(returnType:ReturnType<T>) => void;
queryClick: (returnType: ReturnType<T>) => void;
}
export type ReturnType<T> = {
@@ -21,28 +21,28 @@ export type ReturnType<T> = {
filteredList: T[];
}
export default function FacilityTermModalHeader<T extends { msrDt: string }>({
export default function FacilityTermModalHeader<T extends { msrDt: string }>({
searchList,
fcltyName,
tabName,
fcltyName,
tabName,
searchYear,
queryClick,
ref
}: FacilityTermModalHeaderProps<T & { msrDt: string }>) {
const {Common} = useFoStore((state) => state);
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 [flowPanel, setFlowPanel] = useState<string>(selectFlowPanel[0]?.val ?? '0'); // 초기값 "전체"
const [copySearchList, setCopySearchList] = useState<T[]>(searchList);
const [standSearchList, setStandSearchList] = useState<T[]>(searchList);
const isDateInRanges = (date: Date) => {
const standYear = parseInt(searchYear);
const isLeapYear = (standYear % 4 === 0 && standYear % 100 !== 0) || (standYear % 400 === 0);
const dateRanges:{start:Date, end:Date}[] = [];
const dateRanges: { start: Date, end: Date }[] = [];
if (tabName.includes("_summer")) {
dateRanges.push({
@@ -74,66 +74,66 @@ export default function FacilityTermModalHeader<T extends { msrDt: string }>({
const standYear = Number(searchYear);
const isLeapYear = (standYear % 4 === 0 && standYear % 100 !== 0) || (standYear % 400 === 0);
if(tabName.includes("_summer")) {
startDate = new Date(Number(searchYear), 5, 1);
endDate = new Date(Number(searchYear), 7, 31);
if (tabName.includes("_summer")) { // 하절기
startDate = new Date(Number(searchYear), 5, 1); // 6월 ~
endDate = new Date(Number(searchYear), 7, 31); // 8월
setStartDate(startDate);
setEndDate(endDate);
} else if (tabName.includes("_winter")) {
startDate = new Date(Number(searchYear), 0, 1);
endDate = new Date(Number(searchYear), 1, isLeapYear ? 29 : 28);
} else if (tabName.includes("_winter")) { // 동절기
startDate = new Date(Number(searchYear), 0, 1); // 1월 ~
endDate = new Date(Number(searchYear), 1, isLeapYear ? 29 : 28); // 2월
setStartDate(startDate);
setEndDate(endDate);
} else {
} 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")) {
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 === '2'){
if (flowPanel === '2') { //3~10mm미만
const infoRnDay = (item as T & { toBeInfoRnDay: number }).toBeInfoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 3 && infoRnDay < 10;
} else if(flowPanel === '3'){
} else if (flowPanel === '3') { //10~20mm미만
const infoRnDay = (item as T & { toBeInfoRnDay: number }).toBeInfoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 10 && infoRnDay < 20;
} else if(flowPanel === '4'){
} else if (flowPanel === '4') { //20~30mm미만
const infoRnDay = (item as T & { toBeInfoRnDay: number }).toBeInfoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 20 && infoRnDay < 30;
} else if(flowPanel === '5'){
} else if (flowPanel === '5') { //30~40mm미만
const infoRnDay = (item as T & { toBeInfoRnDay: number }).toBeInfoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 30 && infoRnDay < 40;
} else if(flowPanel === '6'){
} else if (flowPanel === '6') { //40~50mm미만
const infoRnDay = (item as T & { toBeInfoRnDay: number }).toBeInfoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 40 && infoRnDay < 50;
} else if(flowPanel === '7'){
} else if (flowPanel === '7') { //50~60mm미만
const infoRnDay = (item as T & { toBeInfoRnDay: number }).toBeInfoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 50 && infoRnDay < 60;
} else if(flowPanel === '8'){
} else if (flowPanel === '8') {
const infoRnDay = (item as T & { toBeInfoRnDay: number }).toBeInfoRnDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && infoRnDay >= 60;
} else {
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && (rainEffectDay ===1);
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && (rainEffectDay === 1);
}
});
} else if(tabName.includes("_chuncheon")) {
} 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 rainEffectDay = (item as T & { rainEffectDay: number }).rainEffectDay;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && rainEffectDay==0;
return (itemDateOnly >= startDateOnly && itemDateOnly <= endDateOnly) && rainEffectDay == 0;
});
} else {
filteredData = searchList.filter((item) => {
@@ -149,7 +149,7 @@ export default function FacilityTermModalHeader<T extends { msrDt: string }>({
}, [searchList, searchYear, tabName, flowPanel]);
const handleStartDateSelect = (day: Date | undefined) => {
if(day && day > endDate) {
if (day && day > endDate) {
alert('종료일은 시작일 이전일 수 없습니다.');
return;
}
@@ -172,7 +172,7 @@ export default function FacilityTermModalHeader<T extends { msrDt: string }>({
};
const handleEndDateSelect = (day: Date | undefined) => {
if(day && day < startDate) {
if (day && day < startDate) {
alert('종료일은 시작일 이후일 수 없습니다.');
return;
}
@@ -193,7 +193,7 @@ export default function FacilityTermModalHeader<T extends { msrDt: string }>({
});
setCopySearchList(filteredData);
};
const fncSetFilteredList = useCallback(() => {
queryClick({
startDate: startDate,
@@ -206,9 +206,9 @@ export default function FacilityTermModalHeader<T extends { msrDt: string }>({
useEffect(() => {
fncSetFilteredList();
}, [fncSetFilteredList]);
useEffect(() => {
if(ref && ref.current) {
if (ref && ref.current) {
const customRef = ref.current;
customRef.startDate = startDate;
customRef.endDate = endDate;
@@ -225,18 +225,18 @@ export default function FacilityTermModalHeader<T extends { msrDt: string }>({
<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>}
<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}
<SelectBox
selectArray={selectFlowPanel}
value={flowPanel}
onSelectValue={(val) => setFlowPanel(val.toString())}
className="mx-3 w-[150px]"
label=""
className="mx-3 w-[150px]"
label=""
selectClass="w-full p-1 border rounded bg-white"
/>
</div>
@@ -247,7 +247,7 @@ export default function FacilityTermModalHeader<T extends { msrDt: string }>({
<TableCell className="">
<div className="flex">
<div ref={startPickerRef} className="flex justify-center">
<DatePickerRange
<DatePickerRange
mode="single"
selected={startDate}
onSelect={(date) => handleStartDateSelect(date as Date)}
@@ -261,7 +261,7 @@ export default function FacilityTermModalHeader<T extends { msrDt: string }>({
<DatePickerRange
mode="single"
selected={endDate}
onSelect={(date) => handleEndDateSelect(date as Date)}
onSelect={(date) => handleEndDateSelect(date as Date)}
className="w-full p-1 border rounded bg-white"
disabled={(date) => !isDateInRanges(date)}
defaultMonth={endDate}
@@ -8,6 +8,7 @@
import FacilityTermModalHeader from "@/app/components/common/FacilityTermModalHeader";
import { useFoStore } from "@/app/store";
import type { FlowRateModalInterface, modalExcelType } from "@/app/store/flowRate";
import { dataFormSetting } 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";
@@ -76,7 +77,7 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st
let minreturnAmnt = parseNumber(filteredData[0]?.returnAmnt);
let minefflAmnt = parseNumber(filteredData[0]?.efflAmnt);
let mininfoRnDay = parseNumber(filteredData[0]?.infoRnDay && filteredData[0]?.infoRnDay.toString().trim() !== 'x' ? filteredData[0]?.infoRnDay.toString().trim() : 0);
let minNoProcessInflAmnt = 99999999;
let minNoProcessInflAmnt = 99999999; // 미처리하수 추정량
let avgtotInflAmnt = 0;
let avginflAmnt = 0;
@@ -125,6 +126,12 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st
}
});
const formattedData = filteredData.map((item: FlowRateModalInterface) => {
const newItem = { ...item };
dataFormSetting(newItem);
return newItem;
});
const newFilteredData = [
{
msrDt: '평균',
@@ -159,7 +166,7 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st
infoRnDay: mininfoRnDay > 0 ? roundUp(mininfoRnDay, 1) : '0.0',
rainEffectDay: 0,
},
...filteredData
...formattedData
];
return newFilteredData;
}
@@ -210,7 +217,7 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st
<Button className={btnClass} style={{ float: 'right' }} color="green" onClick={excelDownload}>
{"엑셀다운로드"}
</Button>
<TabsContent value="tab_term_year">
<TabsContent value="tab_term_year"> {/* 연간 */}
<FacilityTermModalHeader<FlowRateModalInterface>
searchList={modalFlowRateListYear}
fcltyCd={props.facilityCd}
@@ -237,7 +244,7 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st
<UnderComment commentArray={[commentArray]} />
</div>
</TabsContent>
<TabsContent value="tab_term_summer">
<TabsContent value="tab_term_summer"> {/* 하절기 */}
<FacilityTermModalHeader<FlowRateModalInterface>
searchList={modalFlowRateListYear}
fcltyCd={props.facilityCd}
@@ -264,7 +271,7 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st
<UnderComment commentArray={[commentArray]} />
</div>
</TabsContent>
<TabsContent value="tab_term_winter">
<TabsContent value="tab_term_winter"> {/* 동절기 */}
<FacilityTermModalHeader<FlowRateModalInterface>
searchList={modalFlowRateListYear}
fcltyCd={props.facilityCd}
@@ -291,7 +298,7 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st
<UnderComment commentArray={[commentArray]} />
</div>
</TabsContent>
<TabsContent value="tab_term_chuncheon">
<TabsContent value="tab_term_chuncheon"> {/* 청천시 */}
<FacilityTermModalHeader<FlowRateModalInterface>
searchList={modalFlowRateListYear}
fcltyCd={props.facilityCd}
@@ -318,7 +325,7 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st
<UnderComment commentArray={[commentArray]} />
</div>
</TabsContent>
<TabsContent value="tab_term_rain">
<TabsContent value="tab_term_rain"> {/* 강우시 */}
<FacilityTermModalHeader<FlowRateModalInterface>
searchList={modalFlowRateListYear}
fcltyCd={props.facilityCd}
@@ -329,7 +336,8 @@ export default function FlowRateModal(props: { fcltyName: string; facilityCd: st
queryClick={filterData}
/>
<div className="h-[370px] overflow-y-auto overflow-x-hidden">
<UiTable headName="유량현황" tableData={copyYearList} headlist={upHeadListFlowRateModalYear} pageSize={0} total={0} cellClick={undefined} onPageChange={() => { }} className="h-[370px]" boldArray={[0, 1, 2]} />
<UiTable headName="유량현황" tableData={copyYearList} headlist={upHeadListFlowRateModalYear} pageSize={0} total={0}
cellClick={undefined} onPageChange={() => { }} className="h-[370px]" boldArray={[0, 1, 2]} />
</div>
<div className="mt-20">
<UnderComment commentArray={["미처리하수 추정량 산식 =[(강우시 일별유입유량*강우시 일별유입BOD)-(청천시 연평균유입유량*청천시 연평균유입BOD)]/강우시 일별유입BOD)", "강우량 3mm 이상만 날과 강우영향일(강우일+2일)의 날"]} />
+29 -25
View File
@@ -237,7 +237,7 @@ export type FlowRateSearchType = {
export type FlowRateSearchStore = FlowRateSearchType & FlowRateSearchActions;
const dataFormSetting = (item: flowRateListProps | FlowRateModalProps) => {
export const dataFormSetting = (item: flowRateListProps | FlowRateModalProps | FlowRateModalInterface) => {
DataForm({ type: 'date', column: 'msrDt', item: item as unknown as Record<string, unknown> });
DataForm({ type: 'float0', column: 'totInflAmnt', item: item as unknown as Record<string, unknown> });
DataForm({ type: 'float0', column: 'inflAmnt', item: item as unknown as Record<string, unknown> });
@@ -330,44 +330,48 @@ export const flowRateSearchReducer: (set: (fn: (state: FlowRateSearchType) => Fl
const inflData = CalcRainEffect(res.data);
const res2 = await fetchFacilityWaterQltyListHist(searchYear, bscFctCd);
const wqltData = CalcRainEffect(res2?.data.wqltObjs);
const wqltData = res2?.data.wqltObjs;
//const wqltData = CalcRainEffect(res2?.data.wqltObjs);
/**
* [(강우시 일별유입유량*강우시 일별유입BOD)-(청천시 연평균유입유량*청천시 연평균유입BOD)]​
/강우시 일별유입BOD)
* [(강우시 일별유입유량*강우시 일별유입BOD)-(청천시 연평균유입유량*청천시 연평균유입BOD)] / 강우시 일별유입BOD
*/
//let rainInflAmnt = 0;
let sunnyInflAmnt = 0; // 청천시 일별유입유량
let sunnyBod = 0; // 청천시 일별유입BOD
//let rainDay = 0;
let sunnyBod = 0; // 청천시 일별유입BOD
let sunnyDay = 0;
inflData.forEach(item => {
if (item.rainEffectDay == 1) {
//rainDay += 1;
//rainInflAmnt += Number(item.inflAmnt);
let rainEffectDay = 0;
wqltData?.forEach(item => {
if (item.infoRnDay == null) item.infoRnDay = 0;
if (Number(item.infoRnDay) >= 3) {
rainEffectDay = 2;
} else {
const msrDt = item.msrDt.replaceAll('-', '');
rainEffectDay--;
}
if (rainEffectDay < 0) { //청천시
sunnyDay += 1;
sunnyInflAmnt += Number(item.inflAmnt);
sunnyBod += Number(wqltData.filter((wqitem) => wqitem.msrDt === msrDt)[0].inflAssctBfrWqltBod);
sunnyBod += Number(item.inflAssctBfrWqltBod);
}
});
//const yearRainAmnt = rainInflAmnt===0?0:rainInflAmnt / rainDay;
const yearSunnyAmnt = sunnyInflAmnt === 0 ? 0 : sunnyInflAmnt / sunnyDay; // 청천시 연평균유입유량
const yearSunnyBod = sunnyBod === 0 ? 0 : sunnyBod / sunnyDay; // 청천시 연평균유입BOD
const yearSunnyAmnt = sunnyDay > 0 ? sunnyInflAmnt / sunnyDay : 0; // 청천시 연평균유입유량
const yearSunnyBod = sunnyDay > 0 ? sunnyBod / sunnyDay : 0; // 청천시 연평균유입BOD
console.log(inflData);
const rtnData = inflData.map((item: FlowRateModalProps) => {
const dayBod = Number(wqltData.filter(wqitem => wqitem.msrDt === item.msrDt.replaceAll('-', ''))[0].inflAssctBfrWqltBod);
const targetWqItem = wqltData?.find(wqitem => wqitem.msrDt === item.msrDt.replaceAll('-', ''));
const dayBod = targetWqItem ? Number(targetWqItem.inflAssctBfrWqltBod) : 0;
const noProcessInflAmnt = Math.round(((Number(item.inflAmnt) * dayBod) - (yearSunnyAmnt * yearSunnyBod)) / dayBod)
const totInflAmnt = Number(item.inflAmnt || 0) + Number(item.sumFlow || 0) + Number(item.returnAmnt || 0);
item.totInflAmnt = Number(item.inflAmnt || 0) + Number(item.sumFlow || 0) + Number(item.returnAmnt || 0);
item.noProcessInflAmnt = noProcessInflAmnt && noProcessInflAmnt > 0 ? noProcessInflAmnt : '';
dataFormSetting(item);
return item;
return {
...item,
totInflAmnt,
noProcessInflAmnt: noProcessInflAmnt && noProcessInflAmnt > 0 ? noProcessInflAmnt : '',
msrDt: (item.msrDt.length === 8 && !item.msrDt.includes('-')) ?
`${item.msrDt.slice(0, 4)}-${item.msrDt.slice(4, 6)}-${item.msrDt.slice(6, 8)}`
: item.msrDt,
};
});
console.log(rtnData);
set((state: FlowRateSearchType) => ({
...state,
FlowRateModal: {