Merge remote-tracking branch 'origin/iipbs/sn/dev' into iipbs/sn/ii
This commit is contained in:
@@ -11,7 +11,7 @@ import { SidoSigunguSelectBox } from '@/components/ui/SidoSigunguSelectBox';
|
||||
import { MsSwagePplCol2, SwtrmtFcltCol2, SwtrmtPrcsZoneCol2, MsSwageMhlCol2, ExclnSewagePumpCol2, stofcbrCol2 } from './components/common/table';
|
||||
import LoadingOverlay from '@/components/ui/LoadingBar';
|
||||
//import { useDcTree, flattenFolders, useSggCd, useCityCd } from './components/searchModalReportApi';
|
||||
import { getDcTree,useDcTree,flattenFolders, useSggCd, useCityCd } from './components/searchModalReportApi';
|
||||
import { useDcTree,flattenFolders, useSggCd, useCityCd, addFileName, getFileDownload } from './components/searchModalReportApi';
|
||||
import { useCityCdStore, type SelectDataType } from './commonStore';
|
||||
|
||||
const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}`;
|
||||
@@ -128,7 +128,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
|
||||
const sidos = useCityCd();
|
||||
const siguns = useSggCd();
|
||||
const reportOriginData = useDcTree("");
|
||||
const reportOriginData = useDcTree(""); //:Folder[]
|
||||
|
||||
// 오늘 날짜 구하기
|
||||
const today = new Date();
|
||||
@@ -177,15 +177,27 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
onOpenChange(false); // SearchModal 닫기
|
||||
}
|
||||
};
|
||||
// 하수도정비기본계획.table cellClick
|
||||
const handleReportCellClick = (index: number) => {
|
||||
console.log(index);
|
||||
const reportData = tableData[index];
|
||||
const fileName = reportData.fileName;
|
||||
if (reportData.fileSn !== "") {
|
||||
getFileDownload(reportData.fileSn, fileName);
|
||||
}
|
||||
};
|
||||
|
||||
const handleCtpvCdChange = (val: string|number) => {
|
||||
const handleCtpvCdChange = (val: string|number) => { // 하수도정비기본계획.시도
|
||||
const sidoOption = sidos.find((n) => n.val === val);
|
||||
setCtpvCd(String(val));
|
||||
if (val !== '0' && sidoOption) {
|
||||
const filterSiguns = siguns.filter(item => item.sido === sidoOption.text);
|
||||
if (val !== '0') {
|
||||
const filterSiguns = siguns.filter(item => item.sido === sidoOption?.text);
|
||||
filterSiguns.unshift({ val: '0', text: '전체', sido: '0' });
|
||||
setFilteredSiguns(filterSiguns);
|
||||
}
|
||||
setSigungu("0");
|
||||
setGubun("");
|
||||
setYearFilter({시작: "",종료: "",});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@@ -257,15 +269,15 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
//const reportOriginData = await getDcTree('경기도');
|
||||
const reportData = flattenFolders(reportOriginData);
|
||||
let filterReportData = reportData;
|
||||
if (ctpvCd !== "0") {
|
||||
if (ctpvCd !== "0") { // 시도검색
|
||||
const sidoOption = sidos.find((n) => n.val === ctpvCd);
|
||||
filterReportData = filterReportData.filter(item => item.sido === sidoOption?.text);
|
||||
}
|
||||
if (sigungu !== "0") {
|
||||
if (sigungu !== "0") { // 시군구검색
|
||||
const sigunguOption = filteredSiguns.find((n) => n.val === sigungu);
|
||||
filterReportData = filterReportData.filter(item => item.sigungu === sigunguOption?.text);
|
||||
}
|
||||
if (yearFilter.시작 != "" && yearFilter.종료 == "")
|
||||
if (yearFilter.시작 != "" && yearFilter.종료 == "") //승인년도검색
|
||||
filterReportData = filterReportData.filter(item => (item.approvalYear)? item.approvalYear >= yearFilter.시작+"년" : null);
|
||||
if (yearFilter.시작 == "" && yearFilter.종료 != "")
|
||||
filterReportData = filterReportData.filter(item => (item.approvalYear)? item.approvalYear <= yearFilter.종료+"년" : null);
|
||||
@@ -274,15 +286,19 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
return (item.approvalYear)? item.approvalYear >= yearFilter.시작+"년" && item.approvalYear <= yearFilter.종료+"년": null;
|
||||
});
|
||||
}
|
||||
if (gubun !== "") {
|
||||
if (gubun !== "") { // 구분검색
|
||||
filterReportData = filterReportData.filter(item => (item.plantZoneGugun)? String(item.plantZoneGugun).includes(String(gubun)) : null);
|
||||
}
|
||||
console.log('report count:', filterReportData.length);
|
||||
setTableCount(filterReportData.length || 0);
|
||||
filterReportData = filterReportData.slice((curPage-1)*10, curPage*10); // paging
|
||||
filterReportData = filterReportData?.map((item, index)=>({...item, no:(curPage-1)*10+index+1}));
|
||||
//const fileList = await getDcFile("CLS20250530000041");
|
||||
addFileName(filterReportData).then(data => {
|
||||
filterReportData = data;
|
||||
setTableData(filterReportData || []);
|
||||
});
|
||||
setTableData(filterReportData || []);
|
||||
|
||||
} else {
|
||||
console.log('API URL:', `${API_URL}/v1/fclts?${queryParams.toString()}`);
|
||||
const res = await axios.get(`${API_URL}/v1/fclts?${queryParams.toString()}`);
|
||||
@@ -984,7 +1000,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
)}
|
||||
{/* 운영정보 탭 */}
|
||||
{activeTab === 'operation' && (
|
||||
<div className="relative flex flex-col border rounded-lg p-6 mb-4 w-full h-[23vh]">
|
||||
<div className="relative flex flex-col border rounded-lg p-6 mt-4 w-full h-[23vh]">
|
||||
<div className="flex items-center mb-6">
|
||||
<SelectBox2
|
||||
className="w-60 mr-8"
|
||||
@@ -1038,7 +1054,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
)}
|
||||
{/* 하수도정비기본계획보고서 탭 */}
|
||||
{activeTab === 'report' && (
|
||||
<div className="relative flex flex-col border rounded-lg p-6 mb-4 w-full h-[23vh]">
|
||||
<div className="relative flex flex-col border rounded-lg p-6 mt-4 w-full h-[23vh]">
|
||||
{/* 시도, 시군구, 하수처리구역 한 줄 */}
|
||||
<div className="mb-6 flex items-center">
|
||||
<SelectBox2
|
||||
@@ -1117,7 +1133,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
|
||||
pageSize={tableData.length < 10 ? tableData.length : 10}
|
||||
total={tableCount}
|
||||
children={undefined}
|
||||
cellClick={handleCellClick}
|
||||
cellClick={(activeTab!=='report')?handleCellClick:handleReportCellClick}
|
||||
onPageChange={setCurPage}
|
||||
resetPageNo={false}
|
||||
cur={curPage}
|
||||
|
||||
@@ -1,18 +1,17 @@
|
||||
import {useState, useEffect} from 'react';
|
||||
import { getDcData } from '@/components/lib/api';
|
||||
import { json } from 'stream/consumers';
|
||||
|
||||
const ADDR_URL = process.env.NEXT_PUBLIC_ADDR_URL;
|
||||
|
||||
type Folder = {
|
||||
export type Folder = {
|
||||
clsfSystmSn: string;
|
||||
upClsfSystmSn: string;
|
||||
clsfSystmNm: string;
|
||||
children: Folder[];
|
||||
};
|
||||
|
||||
type FlattenedFolder = {
|
||||
[key: string]: string|number;
|
||||
export type FlattenedFolder = {
|
||||
[key: string]: string|number|undefined;
|
||||
};
|
||||
|
||||
type SelectDataType = {
|
||||
@@ -21,27 +20,143 @@ type SelectDataType = {
|
||||
sido?: string; // 시도 코드 (선택 사항)
|
||||
}
|
||||
|
||||
// 시군구 데이터
|
||||
export const getSggCd = async (): Promise<SelectDataType[]> => {
|
||||
try {
|
||||
const response = await fetch(`${ADDR_URL}/api/v1/addr/sgg`, { method: "GET", headers: { accept: "application/json" } });
|
||||
if (!response.ok) throw new Error(`시군구 데이터 오류: ${response.status}`);
|
||||
const res = await response.json();
|
||||
const sgg = res.data;
|
||||
const sggCd = sgg.map((item: any) => ({
|
||||
val: item.adstDstrCd,
|
||||
sido: item.sido,
|
||||
text: item.gugun,
|
||||
}));
|
||||
sggCd.unshift({ val: '0', text: '전체' }); // "전체" 옵션 추가
|
||||
return sggCd;
|
||||
} catch (error) {
|
||||
console.error('시군구 데이터를 가져오는데 실패했습니다:', error);
|
||||
return [{ val: '0', text: '전체', sido: '0' }];
|
||||
}
|
||||
};
|
||||
type ReportDataFile = {
|
||||
no : number;
|
||||
fileTitle : string;
|
||||
regDt : string;
|
||||
fileSn : string;
|
||||
filePath : string;
|
||||
}
|
||||
const isYear = (value: string): boolean => {
|
||||
return /^[0-9]{4}년$/.test(value);
|
||||
};
|
||||
|
||||
// 시도 데이터
|
||||
export function flattenFolders(data: Folder[]): FlattenedFolder[] {
|
||||
const result: FlattenedFolder[] = [];
|
||||
const exceptNodeNm = ["부분","통합","전체"];
|
||||
const addNodeNm = ["보고서","요약보고서","부록","기타"]; // level5
|
||||
const columnNms: { [key: number]: string } = { 0: "root", 1:"sido", 2:"sigungu", 3:"approvalYear"
|
||||
,4:"plantZoneGugun", 5:"reportGubun", 7:"fileName", 8:"regDate" };
|
||||
|
||||
function traverse(
|
||||
node: Folder,
|
||||
path: { [key: string]: string| number },
|
||||
level: number
|
||||
) {
|
||||
if (level === 2 && isYear(node.clsfSystmNm)) {
|
||||
level++;
|
||||
}
|
||||
if (level === 4 && addNodeNm.includes(node.clsfSystmNm)) {
|
||||
level++; // 레벨을 증가시켜 하위값에 추가되도록 함.
|
||||
}
|
||||
|
||||
const newPath = {
|
||||
...path,
|
||||
[`${columnNms[level]}`]: node.clsfSystmNm,
|
||||
};
|
||||
|
||||
if (node.children.length === 0) {
|
||||
newPath.no = result.length + 1;
|
||||
newPath.clsfSystmSn = node.clsfSystmSn; // 파일 조회를 위한 고유번호
|
||||
result.push(newPath);
|
||||
} else {
|
||||
if (level === 4) {
|
||||
if (exceptNodeNm.includes(node.clsfSystmNm)) {
|
||||
level--; // 제외할 노드 이름인 경우, 현재의 레벨에 하위값이 써지도록 함.
|
||||
}
|
||||
}
|
||||
node.children.forEach((child) => {
|
||||
traverse(child, newPath, level + 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
data.forEach((root) => traverse(root, {}, 0));
|
||||
console.log(result.slice(0, 10));
|
||||
return result;
|
||||
}
|
||||
|
||||
export const getDcTree = async (clsfSystmNm:string) => {
|
||||
//const cityName = encodeURIComponent(clsfSystmNm);
|
||||
const queryString = 'clsfSystmNm='+clsfSystmNm;
|
||||
console.log("getDcTree queryString:", queryString);
|
||||
//const result = await getDcData(`/api/v1/dc/clsf${queryString ? `?${queryString}` : ''}`);
|
||||
const result = await getDcData("/api/v1/dc/clsf?clsfSystmNm=" + clsfSystmNm);
|
||||
return result;
|
||||
};
|
||||
export const useDcTree = (clsfSystmNm:string) => {
|
||||
const [data, setData] = useState([]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetch = async () => {
|
||||
const result = await getDcTree(clsfSystmNm);
|
||||
if (result) {
|
||||
setData(result);
|
||||
}
|
||||
};
|
||||
fetch();
|
||||
console.log("useDcTree호출");
|
||||
}, [clsfSystmNm]);
|
||||
|
||||
return data;
|
||||
};
|
||||
export const getDcFile = async (clsfSystmSn:string) => {
|
||||
const queryString = 'clsfSystmSn='+clsfSystmSn;
|
||||
const result = await getDcData(`/api/v1/dc/file${queryString ? `?${queryString}` : ''}`);
|
||||
return result;
|
||||
};
|
||||
export const addFileName = async (data:FlattenedFolder[]) => {
|
||||
const fileReportData = await Promise.all(
|
||||
data.map(async (item) => {
|
||||
const filenameArr:ReportDataFile[] = await getDcFile(String(item.clsfSystmSn));
|
||||
const f = filenameArr[0];
|
||||
const result = {...item, fileName:f?.fileTitle, regDate:f?.regDt, fileSn:f?.fileSn, filePath:""};
|
||||
// if (f !== undefined && f.fileSn !== "") {
|
||||
// const fprcs = await getDcFileprcs(f?.fileSn);
|
||||
// if (fprcs.length>0)
|
||||
// result.filePath = fprcs[0].filePath;
|
||||
// }
|
||||
return result;
|
||||
})
|
||||
);
|
||||
return fileReportData;
|
||||
}
|
||||
export const getDcFileprcs = async (fileSn:string) => {
|
||||
const queryString = 'fileSn='+fileSn;
|
||||
console.log("getDcTree queryString:", queryString);
|
||||
const result = await getDcData(`/api/v1/dc/fileprcs${queryString ? `?${queryString}` : ''}`);
|
||||
return result;
|
||||
};
|
||||
|
||||
export const getDcFileDownload = async (filePath:string) => {
|
||||
const queryString = 'filePath='+ encodeURIComponent(filePath);
|
||||
const response = await fetch(`/dc-service/api/v1/dc/file/download?${queryString}`, {
|
||||
method: 'GET',
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error(`File download failed: ${response.status}`);
|
||||
}
|
||||
const blob = await response.blob(); // PDF, image 등은 blob으로 받아야 함
|
||||
return blob;
|
||||
};
|
||||
export const fileDownload = (data:Blob, fileName:string) => { // Blob데이터 임시link로 연결
|
||||
const url = window.URL.createObjectURL(data);
|
||||
const link = document.createElement('a');
|
||||
link.href = url;
|
||||
link.download = fileName; // 원하는 파일명 지정
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
link.remove();
|
||||
window.URL.revokeObjectURL(url);
|
||||
}
|
||||
export const getFileDownload = async (fileSn:string, fileName:string) => {
|
||||
const fileprcsData = await getDcFileprcs(fileSn); // filePath 조회
|
||||
if (fileprcsData?.length === 0 ) { alert("filePath 조회 오류"); }
|
||||
if (fileprcsData[0].filePath === "") alert("filePath 정보 없으므로 다운로드 불가");
|
||||
const blobData = await getDcFileDownload(fileprcsData[0].filePath); // 실제 다운로드 API
|
||||
fileDownload(blobData, fileName);
|
||||
}
|
||||
// 시도 데이터
|
||||
export const getCityCd = async (): Promise<SelectDataType[]> => {
|
||||
try {
|
||||
const response = await fetch(`${ADDR_URL}/api/v1/addr/city`, { method: "GET", headers: { accept: "application/json" } });
|
||||
@@ -71,10 +186,31 @@ export const useSggCd = () => {
|
||||
}
|
||||
};
|
||||
fetch();
|
||||
console.log("useSggCd호출");
|
||||
}, []);
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
// 시군구 데이터
|
||||
export const getSggCd = async (): Promise<SelectDataType[]> => {
|
||||
try {
|
||||
const response = await fetch(`${ADDR_URL}/api/v1/addr/sgg`, { method: "GET", headers: { accept: "application/json" } });
|
||||
if (!response.ok) throw new Error(`시군구 데이터 오류: ${response.status}`);
|
||||
const res = await response.json();
|
||||
const sgg = res.data;
|
||||
const sggCd = sgg.map((item: any) => ({
|
||||
val: item.adstDstrCd,
|
||||
sido: item.sido,
|
||||
text: item.gugun,
|
||||
}));
|
||||
sggCd.unshift({ val: '0', text: '전체' }); // "전체" 옵션 추가
|
||||
return sggCd;
|
||||
} catch (error) {
|
||||
console.error('시군구 데이터를 가져오는데 실패했습니다:', error);
|
||||
return [{ val: '0', text: '전체', sido: '0' }];
|
||||
}
|
||||
};
|
||||
|
||||
export const useCityCd = () => {
|
||||
const [data, setData] = useState<SelectDataType[]>([]);
|
||||
|
||||
@@ -86,96 +222,8 @@ export const useCityCd = () => {
|
||||
}
|
||||
};
|
||||
fetch();
|
||||
console.log("useCityCd호출");
|
||||
}, []);
|
||||
|
||||
return data;
|
||||
};
|
||||
|
||||
const isYear = (value: string): boolean => {
|
||||
return /^[0-9]{4}년$/.test(value);
|
||||
};
|
||||
|
||||
export function flattenFolders(data: Folder[]): FlattenedFolder[] {
|
||||
const result: FlattenedFolder[] = [];
|
||||
const exceptNodeNm = ["부분","통합","전체"];
|
||||
const addNodeNm = ["보고서","요약보고서","부록","기타"]; // level5
|
||||
const columnNms: { [key: number]: string } = { 0: "root", 1:"sido", 2:"sigungu", 3:"approvalYear"
|
||||
,4:"plantZoneGugun", 5:"reportGubun", 7:"fileName", 8:"regDate" };
|
||||
|
||||
function traverse(
|
||||
node: Folder,
|
||||
path: { [key: string]: string| number },
|
||||
level: number
|
||||
) {
|
||||
if (level === 2 && isYear(node.clsfSystmNm)) {
|
||||
level++;
|
||||
}
|
||||
if (level === 4 && addNodeNm.includes(node.clsfSystmNm)) {
|
||||
level++; // 레벨을 증가시켜 하위값에 추가되도록 함.
|
||||
}
|
||||
|
||||
const newPath = {
|
||||
...path,
|
||||
[`${columnNms[level]}`]: node.clsfSystmNm,
|
||||
};
|
||||
|
||||
if (node.children.length === 0) {
|
||||
newPath.no = result.length + 1;
|
||||
newPath.clsfSystmSn = node.clsfSystmSn; // 파일 조회를 위한 고유번호
|
||||
result.push(newPath);
|
||||
} else {
|
||||
if (level === 4) {
|
||||
if (exceptNodeNm.includes(node.clsfSystmNm)) {
|
||||
level--; // 제외할 노드 이름인 경우, 현재의 레벨에 하위값이 써지도록 함.
|
||||
}
|
||||
}
|
||||
node.children.forEach((child) => {
|
||||
traverse(child, newPath, level + 1);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
data.forEach((root) => traverse(root, {}, 0));
|
||||
console.log(result.slice(0, 10));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
export const getDcTree = async (clsfSystmNm:string) => {
|
||||
const cityName = encodeURIComponent(clsfSystmNm);
|
||||
const queryString = 'clsfSystmNm='+cityName;
|
||||
console.log("getDcTree queryString:", queryString);
|
||||
//const result = await getDcData(`/api/v1/dc/clsf${queryString ? `?${queryString}` : ''}`);
|
||||
const result = await getDcData("/api/v1/dc/clsf?clsfSystmNm=" + clsfSystmNm);
|
||||
return result;
|
||||
};
|
||||
export const useDcTree = (clsfSystmNm:string) => {
|
||||
const [data, setData] = useState<SelectDataType[]>([]);
|
||||
|
||||
useEffect(() => {
|
||||
const fetch = async () => {
|
||||
const result = await getDcTree(clsfSystmNm);
|
||||
if (result) {
|
||||
setData(result);
|
||||
}
|
||||
};
|
||||
fetch();
|
||||
}, [clsfSystmNm]);
|
||||
|
||||
return data;
|
||||
};
|
||||
export const getDcFile = async (clsfSystmSn:string) => {
|
||||
const queryString = 'clsfSystmNm='+clsfSystmSn;
|
||||
console.log("getDcTree queryString:", queryString);
|
||||
const result = await getDcData(`/api/v1/dc/file${queryString ? `?${queryString}` : ''}`);
|
||||
return result;
|
||||
};
|
||||
export const addDcFileName = async(data: any) => {
|
||||
const updated = await Promise.all(
|
||||
data.map(async (item) => {
|
||||
const fileName = await getDcFile(item.systmSn);
|
||||
return { ...item, fileName };
|
||||
})
|
||||
);
|
||||
return updated;
|
||||
}
|
||||
|
||||
@@ -102,8 +102,11 @@ export function middleware(req: NextRequest) {
|
||||
|
||||
if (pathname.startsWith("/dc-service")) {
|
||||
const requestHeaders = new Headers(req.headers);
|
||||
const newUrl = `${RSONE_DC_URL}${pathname}`;
|
||||
|
||||
const decodedParams = Array.from(searchParams.entries())
|
||||
.map(([key, value]) => `${key}=${decodeURIComponent(value)}`)
|
||||
.join("&");
|
||||
|
||||
const newUrl = `${RSONE_DC_URL}${pathname}${decodedParams ? `?${decodedParams}` : ""}`;
|
||||
console.log(
|
||||
`[Middleware] Rewriting from '/dc-service' path to: ${newUrl}`,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user