297 lines
11 KiB
TypeScript
297 lines
11 KiB
TypeScript
"use client";
|
|
import { useWrStore } from "@/app/store";
|
|
import type { RswtStatProps } from "@/app/store/stats/rswtStat";
|
|
import { excelDownload, tableData, yearOptions } from "@/components/lib/func/commom";
|
|
import {
|
|
ContentsDiv,
|
|
downloadExcel,
|
|
ScrollArea,
|
|
SearchDiv,
|
|
SelectBox,
|
|
Title,
|
|
UiTable
|
|
} from "@common/business_components/ui";
|
|
import ListDiv from "@common/business_components/ui/list_div";
|
|
import { Button } from "@common/components/ui";
|
|
import { useEffect, useState } from "react";
|
|
import toast, { Toaster } from "react-hot-toast";
|
|
import { useTranslation } from "react-i18next";
|
|
|
|
export default function RswtStat() {
|
|
const { code, rswtStat, searchRswtStatList } = useWrStore((state) => state);
|
|
const { rswtStatList, rswtDetailList } = rswtStat;
|
|
const [rswtTable, setRswtTable] = useState<RswtStatProps[]>();
|
|
const [searchYear, setSearchYear] = useState<string>(
|
|
String(new Date().getFullYear())
|
|
);
|
|
|
|
const { t } = useTranslation('base');
|
|
useEffect(() => {
|
|
searchRswtStatList(searchYear, code.cityCd)
|
|
}, [searchRswtStatList, code.cityCd])
|
|
|
|
const excelClick = () => {
|
|
|
|
if (rswtTable) {
|
|
const data = rswtTable.map((item: RswtStatProps) => {
|
|
return {
|
|
"시도": item.cityText,
|
|
"개소": item.ctpvCnt,
|
|
// "건축연면적(m³)": item.archArea,
|
|
"처리용량(m³)": item.filtrTrtmfctCpct,
|
|
"물사용량(m³/년)": item.wtrUsqty,
|
|
"중수도+대체수이용량(m³/년)": item.rswtAltSum,
|
|
"중수도이용량(m³/년)": item.rswtrUsqty,
|
|
"대체수이용량(m³/년)": item.altUswtrUtztnQnt,
|
|
"조경용수": item.lndwtr,
|
|
"청소화장실용수": item.cleanToilUswtr,
|
|
"농업용수": item.farmUswtr,
|
|
"공업용수": item.indstUswtr,
|
|
"친수용수": item.hydUswtr,
|
|
"기타": item.ithlEtcUswtr,
|
|
"연간운영비(백만원/년)": item.anlOperCst,
|
|
};
|
|
})
|
|
|
|
downloadExcel({
|
|
data: (data as unknown as JSON[]) ?? [],
|
|
fileName: "중수도재이용통계",
|
|
sheetNameArray: ["Sheet1"],
|
|
headerRange: "A1:N1"
|
|
})
|
|
} else {
|
|
toast.error(t("excelDownErrMsg"))
|
|
}
|
|
}
|
|
|
|
useEffect(() => {
|
|
const transData = Array.isArray(rswtStatList) ? rswtStatList : [rswtStatList];
|
|
const cityData = tableData(transData, { code });
|
|
const tbData = Array.isArray(cityData)
|
|
? cityData.map((item: RswtStatProps) => {
|
|
return {
|
|
...item,
|
|
ctpvCd: item.cityText === '전체' ? '전국' : item.cityText,
|
|
cityText: item.cityText === '전체' ? '전국' : item.cityText
|
|
};
|
|
}) : [];
|
|
|
|
setRswtTable(tbData);
|
|
}, [code, rswtStatList]);
|
|
|
|
const excelDetailDownLoad = () => {
|
|
|
|
const sidoList = new Set<string>();
|
|
const sidoDataMap = new Map<string, (string | number)[]>(); // 시도별 데이터를 저장할 Map
|
|
const sidoDetailMap = new Map<string, (string | number)[][]>(); // 시도별 상세 데이터를 저장할 Map
|
|
|
|
// 먼저 시도별 상세 데이터를 그룹화
|
|
const transformedRswtDetailList = rswtDetailList.map((originItem: RswtStatProps[]) => {
|
|
// rswtDetailList.forEach((item: RswtStatProps[]) => {
|
|
const item = [...originItem]; // 원본 복사
|
|
const sidoVal = item.at(0)?.toString().trim();
|
|
const sigunVal = item.at(1)?.toString().trim();
|
|
let sidoText = '';
|
|
let sigunText = '';
|
|
|
|
const sido = code.cityCd.find((city) => city.val === sidoVal);
|
|
const sigun = code.sggCd.find((sgg) => sgg.val === sigunVal);
|
|
|
|
sidoText = sido?.text || '';
|
|
sigunText = sigun?.text || '';
|
|
|
|
// 시도, 시군구 코드를 텍스트로 변환하여 데이터에 할당
|
|
if (item.length > 0) {
|
|
item[0] = sidoText as unknown as RswtStatProps;
|
|
}
|
|
if (item.length > 1) {
|
|
item[1] = sigunText as unknown as RswtStatProps;
|
|
}
|
|
if (item.length > 2) {
|
|
item[2] = sidoText + ' ' + sigunText as unknown as RswtStatProps;
|
|
}
|
|
|
|
// 시도별 상세 데이터 수집
|
|
if (sidoText) {
|
|
if (!sidoDetailMap.has(sidoText)) {
|
|
sidoDetailMap.set(sidoText, []);
|
|
}
|
|
sidoDetailMap.get(sidoText)!.push(item as unknown as (string | number)[]);
|
|
}
|
|
|
|
// 시도별 합계 데이터 수집
|
|
if (sidoText && !sidoList.has(sidoText)) {
|
|
sidoList.add(sidoText);
|
|
// 시도별 초기 데이터 구조 생성 (예: [시도명, 0, 0, 0, ...])
|
|
const initialData: (string | number)[] = [sidoText];
|
|
// 데이터의 나머지 컬럼들을 0으로 초기화 (실제 컬럼 수에 맞게 조정 필요)
|
|
for (let i = 1; i < item.length; i++) {
|
|
initialData.push(0);
|
|
}
|
|
sidoDataMap.set(sidoText, initialData);
|
|
}
|
|
return item;
|
|
});
|
|
|
|
// 시도별 합계 계산
|
|
transformedRswtDetailList.forEach((item: RswtStatProps[]) => {
|
|
const sidoText = item[0]?.toString();
|
|
if (sidoText && sidoDataMap.has(sidoText)) {
|
|
const sidoData = sidoDataMap.get(sidoText)!;
|
|
// 각 컬럼의 값을 합계에 더하기 (1번 인덱스부터 시작)
|
|
for (let i = 1; i < item.length && i < sidoData.length; i++) {
|
|
if (i == 2) {
|
|
sidoData[i] = '합계';
|
|
} else if (i == 3) {
|
|
// 해당 시도의 데이터 개수를 계산
|
|
const sidoDataCount = rswtDetailList.filter((data: unknown[]) =>
|
|
Array.isArray(data) && data.length > 0 && String(data[0]) === sidoText
|
|
).length;
|
|
sidoData[i] = sidoDataCount;
|
|
} else if (i == 4) {
|
|
sidoData[i] = '';
|
|
} else if (i == 6) {
|
|
// 법정 대상 건축물 개수 (인덱스 6이 'Y'인 경우)
|
|
const bumCnt = rswtDetailList.filter((data: unknown[]) =>
|
|
Array.isArray(data) && data.length > 6 && String(data[0]) === sidoText
|
|
).filter((data: unknown[]) => String(data[6]) === '대상').length;
|
|
sidoData[i] = bumCnt;
|
|
} else if (i == 9) {
|
|
const value = Number(item[9]) || 0;
|
|
sidoData[i] = (Number(sidoData[i]) + value) as string | number;
|
|
} else if (i == 10) {
|
|
const value = Number(item[10]) || 0;
|
|
sidoData[i] = (Number(sidoData[i]) + value) as string | number;
|
|
} else if (i == 11) {
|
|
const value = Number(item[11]) || 0;
|
|
sidoData[i] = (Number(sidoData[i]) + value) as string | number;
|
|
} else if (i == 12) {
|
|
const value = Number(item[12]) || 0;
|
|
sidoData[i] = (Number(sidoData[i]) + value) as string | number;
|
|
} else if (i == 13) {
|
|
const value = Number(item[13]) || 0;
|
|
sidoData[i] = (Number(sidoData[i]) + value) as string | number;
|
|
} else if (i == 14) {
|
|
const value = Number(item[14]) || 0;
|
|
sidoData[i] = (Number(sidoData[i]) + value) as string | number;
|
|
} else if (i == 16) {
|
|
const value = Number(item[16]) || 0;
|
|
sidoData[i] = (Number(sidoData[i]) + value) as string | number;
|
|
} else {
|
|
sidoData[i] = '';
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
// 시도별로 합계와 상세 데이터를 순서대로 배치
|
|
const summaryExcelData: (string | number)[][] = [];
|
|
|
|
// 시도별로 순회하면서 합계와 상세 데이터를 추가
|
|
Array.from(sidoList).forEach((sidoText) => {
|
|
const sidoSummary = sidoDataMap.get(sidoText);
|
|
const sidoDetails = sidoDetailMap.get(sidoText) || [];
|
|
|
|
if (sidoSummary) {
|
|
// 시도 합계 데이터 추가
|
|
summaryExcelData.push(sidoSummary);
|
|
// 해당 시도의 상세 데이터들 추가
|
|
sidoDetails.forEach(detail => {
|
|
summaryExcelData.push(detail);
|
|
});
|
|
}
|
|
});
|
|
|
|
const sidoSummaryList = Array.from(sidoDataMap.values());
|
|
const sidoTotalSummary = [['전국', '', '합계', 0, '', '', 0, '', '', 0, 0, 0, 0, 0, 0, '']];
|
|
sidoSummaryList.forEach((item: (string | number)[]) => {
|
|
item.forEach((value: string | number, index: number) => {
|
|
if (typeof value === 'number') {
|
|
if (sidoTotalSummary[0] && index < sidoTotalSummary[0].length) {
|
|
sidoTotalSummary[0][index] = Number(sidoTotalSummary[0][index]) + Number(value);
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
// sidoSummaryList와 rainDetailList를 합쳐서 엑셀 데이터 생성
|
|
const excelData = [...sidoTotalSummary, ...sidoSummaryList, ...summaryExcelData];
|
|
|
|
excelDownload(
|
|
excelData as unknown as [][],
|
|
"중수도시설 통계",
|
|
"sample_rswtStats.xlsx",
|
|
"A1:P3",
|
|
4
|
|
);
|
|
}
|
|
|
|
return (<>
|
|
<ContentsDiv>
|
|
<div className="grid grid-cols-4 gap-4">
|
|
<div className="col-span-4">
|
|
<Title title="중수도시설 통계" />
|
|
<SearchDiv>
|
|
<div className="grid grid-cols-12 gap-5 m-4">
|
|
<div className="col-span-2">
|
|
<SelectBox
|
|
className="w-full h-full text-2xl"
|
|
label="통계년도"
|
|
selectClass=""
|
|
selectArray={yearOptions}
|
|
value={searchYear}
|
|
onSelectValue={(val: string | number) => setSearchYear(String(val))}
|
|
/>
|
|
</div>
|
|
<div className="flex flex-row space-x-2 items-center">
|
|
<Button
|
|
className=""
|
|
size="sm"
|
|
onClick={() => {
|
|
searchRswtStatList(searchYear, code.cityCd)
|
|
}}
|
|
>
|
|
조회
|
|
</Button>
|
|
<Button
|
|
className=""
|
|
size="sm"
|
|
onClick={() => excelClick()}
|
|
>
|
|
다운로드
|
|
</Button>
|
|
<Button
|
|
className=""
|
|
size="sm"
|
|
onClick={() => { excelDetailDownLoad() }}
|
|
>
|
|
전체통계다운로드
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</SearchDiv>
|
|
</div>
|
|
</div>
|
|
<div className="grid grid-cols-6 gap-4">
|
|
<div className="col-span-6 h-full min-h-0">
|
|
<ListDiv className="">
|
|
<ScrollArea className="h-[67vh]">
|
|
<UiTable
|
|
tableData={rswtTable || []}
|
|
headlist={rswtStat.rswtStatHeadList}
|
|
pageSize={10}
|
|
total={0}
|
|
headName=""
|
|
cellClick={undefined}
|
|
onPageChange={() => { }}
|
|
/>
|
|
</ScrollArea>
|
|
</ListDiv>
|
|
</div>
|
|
</div>
|
|
<Toaster position="top-center"></Toaster>
|
|
</ContentsDiv>
|
|
</>
|
|
);
|
|
}
|