일단 다운로드 성공

This commit is contained in:
2025-07-14 12:52:45 +09:00
parent dec208d640
commit b66b3a9b55
2 changed files with 171 additions and 117 deletions
+28 -5
View File
@@ -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,getDcFile } from './components/searchModalReportApi';
import { getDcTree,useDcTree,flattenFolders, useSggCd, useCityCd,getDcFile,addFileName,getDcFileDownload } from './components/searchModalReportApi';
import { useCityCdStore, type SelectDataType } from './commonStore';
const API_URL = `${process.env.NEXT_PUBLIC_SEWERAGE_API_URL}`;
@@ -130,7 +130,7 @@ const SearchModal: React.FC<SearchModalProps> = ({
const sidos = useCityCd();
const siguns = useSggCd();
const reportOriginData = useDcTree("");
const reportOriginData = useDcTree(""); //:Folder[]
// 오늘 날짜 구하기
const today = new Date();
@@ -179,6 +179,26 @@ const SearchModal: React.FC<SearchModalProps> = ({
onOpenChange(false); // SearchModal 닫기
}
};
// 하수도정비기본계획.table cellClick
const handleReportCellClick = (index: number, headId?: string) => {
console.log(index);
const reportData = tableData[index];
console.log(reportData);
const fileName = reportData.fileName;
if (reportData.filePath) {
console.log(reportData.filePath);
getDcFileDownload(reportData.filePath).then((data) => {
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);
});
}
};
const handleCtpvCdChange = (val: string|number) => { // 하수도정비기본계획.시도
const sidoOption = sidos.find((n) => n.val === val);
@@ -286,9 +306,12 @@ const SearchModal: React.FC<SearchModalProps> = ({
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 || []);
const fileList = await getDcFile("CLS20250530000041");
console.log(fileList);
} else {
console.log('API URL:', `${API_URL}/v1/fclts?${queryParams.toString()}`);
const res = await axios.get(`${API_URL}/v1/fclts?${queryParams.toString()}`);
@@ -1117,7 +1140,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,126 @@ 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 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 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 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 getCityCd = async (): Promise<SelectDataType[]> => {
try {
const response = await fetch(`${ADDR_URL}/api/v1/addr/city`, { method: "GET", headers: { accept: "application/json" } });
@@ -75,6 +173,27 @@ export const 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[]>([]);
@@ -91,91 +210,3 @@ export const 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;
}