diff --git a/apps/st/src/components/module/TabDataGrid.tsx b/apps/st/src/components/module/TabDataGrid.tsx new file mode 100644 index 000000000..886861848 --- /dev/null +++ b/apps/st/src/components/module/TabDataGrid.tsx @@ -0,0 +1,352 @@ +import { useRef, useEffect, useState } from "react" + +import RealGrid, { GridFitStyle } from "realgrid" +import { RealGridReact, RGDataColumn } from "realgrid-react" +import type { TabDataGridProps } from "../type/DataGrid.type" +import JSZip from "jszip" +import * as XLSX from "xlsx" +import { useAlertStore } from "@/shared/lib"; +import { useTabStore } from "@/store/tab-store" + +import { Tabs, TabsList, TabsTrigger, TabsContent } from "@common/components"; +import { Button } from "@common/components"; + +// Window 인터페이스 확장 +declare global { + interface Window { + JSZip: typeof JSZip; + } +} + +// RealGrid가 jszip을 사용할 수 있도록 전역에 등록 +if (typeof window !== "undefined") { + window.JSZip = JSZip; +} + +RealGrid.setLicenseKey(process.env.NEXT_PUBLIC_ST_REALGRID_KEY || ''); + +const TabDataGrid = ({ columnInfo, isExcelImport = true, rows = [] }: TabDataGridProps) => { + // 각 탭별로 그리드 ref와 데이터를 관리 + const gridRefs = useRef>(new Map()); + const fileInputRefs = useRef>(new Map()); + const { setMessage: alert } = useAlertStore((state) => state); + const activeTabId = useTabStore((state) => state.activeTabId); + const menuList = useTabStore((state) => state.menuList); + + // 각 탭별로 데이터를 관리 (key: tabKey) + const [rowDataMap, setRowDataMap] = useState[]>>(new Map()); + const [activeTab, setActiveTab] = useState(() => { + // 초기 활성 탭을 첫 번째 tabKey로 설정 + const firstTab = columnInfo[0]; + return firstTab?.tabKey || ''; + }); + + const exportExcel = (tabKey: string) => { + const currentRowData = rowDataMap.get(tabKey) || []; + if (currentRowData.length === 0) { + alert('데이터가 없습니다.'); + return; + } + + const gridRef = gridRefs.current.get(tabKey); + if (!gridRef?.gridView) return; + + const gridView = gridRef.gridView; + + const menuName = menuList + ?.flatMap((m) => m.children || []) + .flatMap((subM) => subM || []) + .find((s) => s.menuEngNm === activeTabId) + ?.menuKornNm || 'export'; + + const columnInfoItem = columnInfo.find(info => info.tabKey === tabKey); + const tabLabel = columnInfoItem?.tabLabel || tabKey; + gridView.exportGrid({ + type: 'excel', + target: 'local', + fileName: `${menuName}_${tabLabel}.xlsx`, + footer: 'hidden', + done: () => { + alert('내보내기가 완료되었습니다.'); + }, + }); + }; + + const importExcel = (tabKey: string) => { + const fileInput = fileInputRefs.current.get(tabKey); + fileInput?.click(); + }; + + const handleFileChange = async (e: React.ChangeEvent, tabKey: string) => { + const file = e.target.files?.[0]; + if (!file) return; + + const columnInfoItem = columnInfo.find(info => info.tabKey === tabKey); + if (!columnInfoItem) return; + + const rowStartNum = columnInfoItem.rowStartNum || 1; + + try { + const reader = new FileReader(); + reader.onload = (event) => { + try { + const data = event.target?.result; + if (!data) { + alert('파일을 읽을 수 없습니다.'); + return; + } + + // SheetJS로 엑셀 파일을 JSON으로 변환 + const workbook = XLSX.read(data, { type: 'array', cellDates: true }); + const firstSheetName = workbook.SheetNames[0]; + const worksheet = workbook.Sheets[firstSheetName || '']; + + if (!worksheet || !worksheet['!ref']) { + alert('워크시트를 읽을 수 없습니다.'); + return; + } + + const range = XLSX.utils.decode_range(worksheet['!ref']); + const headerRowCount = rowStartNum || 1; // 기본값 1 + const headerStartRow = Math.max(0, headerRowCount - 1); // 0-based index + + // 각 컬럼별로 헤더 찾기 (rowStartNum 행부터 위로 올라가면서) + const headers: string[] = []; + const headerRowIndexes: number[] = []; + + for (let col = range.s.c; col <= range.e.c; col++) { + let headerValue = ''; + let headerRowIndex = headerStartRow; + + // rowStartNum 행부터 시작해서 위로 올라가면서 헤더 찾기 + for (let rowIndex = headerStartRow; rowIndex >= 0; rowIndex--) { + const cell = worksheet[XLSX.utils.encode_cell({ r: rowIndex, c: col })]; + if (cell && cell.v !== undefined && cell.v !== null && cell.v !== '') { + headerValue = String(cell.v); + headerRowIndex = rowIndex; + break; + } + } + + // 헤더를 찾지 못한 경우 기본값 사용 + if (!headerValue) { + headerValue = `Column${col + 1}`; + headerRowIndex = headerStartRow; + } + + headers.push(headerValue); + headerRowIndexes.push(headerRowIndex); + } + + // 실제 데이터가 시작하는 행 찾기 (헤더가 있는 최대 행 번호 + 1) + const maxHeaderRowIndex = headerRowIndexes.length > 0 ? Math.max(...headerRowIndexes) : headerStartRow; + const dataStartRow = maxHeaderRowIndex + 1; + + // 데이터 행 읽기 + const dataRows: Record[] = []; + for (let row = dataStartRow; row <= range.e.r; row++) { + const rowData: Record = {}; + let hasData = false; + + for (let colIndex = 0; colIndex < headers.length; colIndex++) { + const col = range.s.c + colIndex; + const cell = worksheet[XLSX.utils.encode_cell({ r: row, c: col })]; + const headerName = headers[colIndex] || `Column${colIndex + 1}`; + const cellValue = cell && cell.v !== undefined && cell.v !== null + ? String(cell.v) + : ''; + + if (cellValue !== '') { + hasData = true; + } + + rowData[headerName] = cellValue; + } + + // 데이터가 하나라도 있는 행만 추가 + if (hasData) { + dataRows.push(rowData); + } + } + + setRowDataMap(prev => { + const newMap = new Map(prev); + newMap.set(tabKey, dataRows); + return newMap; + }); + } catch (error) { + console.error('엑셀 파일 파싱 오류:', error); + alert('엑셀 파일 파싱 중 오류가 발생했습니다.'); + } finally { + const fileInput = fileInputRefs.current.get(tabKey); + if (fileInput) { + fileInput.value = ''; + } + } + }; + + reader.onerror = () => { + alert('파일 읽기 중 오류가 발생했습니다.'); + const fileInput = fileInputRefs.current.get(tabKey); + if (fileInput) { + fileInput.value = ''; + } + }; + + reader.readAsArrayBuffer(file); + } catch (error) { + console.error('엑셀 파일 가져오기 오류:', error); + alert('엑셀 파일 가져오기 중 오류가 발생했습니다.'); + const fileInput = fileInputRefs.current.get(tabKey); + if (fileInput) { + fileInput.value = ''; + } + } + }; + + // 초기 데이터 설정 + useEffect(() => { + if (rows.length > 0) { + columnInfo.forEach((info) => { + const tabKey = info.tabKey; + if (!rowDataMap.has(tabKey)) { + setRowDataMap(prev => { + const newMap = new Map(prev); + newMap.set(tabKey, rows); + return newMap; + }); + } + }); + } + }, [rows, columnInfo]); + + // 활성 탭이 변경될 때마다 fitStyle 재적용 (그리드가 보일 때) + useEffect(() => { + const gridRef = gridRefs.current.get(activeTab); + if (!gridRef?.gridView) return; + + const gridView = gridRef.gridView; + // 약간의 지연을 두고 설정 (그리드 렌더링 완료 후) + const timer = setTimeout(() => { + gridView.setDisplayOptions({ + fitStyle: GridFitStyle.EVEN + }); + gridView.refresh(); + }, 50); + + return () => clearTimeout(timer); + }, [activeTab]); + + if (columnInfo.length === 0) { + return
컬럼 정보가 없습니다.
; + } + + return ( +
+ + + {columnInfo.map((info) => { + const tabKey = info.tabKey; + const tabLabel = info.tabLabel || tabKey; + return ( + + {tabLabel} + + ); + })} + + + {columnInfo.map((info) => { + const tabKey = info.tabKey; + const { columns, columnLayout } = info; + const currentRowData = rowDataMap.get(tabKey) || []; + + return ( + +
+ { + if (el) { + fileInputRefs.current.set(tabKey, el); + } else { + fileInputRefs.current.delete(tabKey); + } + }} + type="file" + accept=".xlsx,.xls,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel" + style={{ display: 'none' }} + onChange={(e) => handleFileChange(e, tabKey)} + /> +
+ {isExcelImport && ( + + )} + +
+
+ { + if (el) { + gridRefs.current.set(tabKey, el); + // 그리드가 생성된 직후 fitStyle 설정 + if (el.gridView) { + setTimeout(() => { + const gridView = el.gridView; + if (!gridView) return; + + gridView.setDisplayOptions({ + fitStyle: GridFitStyle.EVEN + }); + gridView.header.height = 60; + + // 고정 컬럼 설정 + const currentInfo = columnInfo.find(info => info.tabKey === tabKey); + if (currentInfo?.fixedColumns !== undefined && currentInfo.fixedColumns > 0) { + gridView.setFixedOptions({ + colCount: currentInfo.fixedColumns, + }); + } + }, 0); + } + } else { + gridRefs.current.delete(tabKey); + } + }} + autoGenerateField={true} + layout={columnLayout} + rows={currentRowData} + > + {columns.map((column) => ( + + ))} + +
+
+
+ ); + })} +
+
+ ); +}; + +export default TabDataGrid; \ No newline at end of file diff --git a/apps/st/src/components/statistics/management/SewagePipeMng.tsx b/apps/st/src/components/statistics/management/SewagePipeMng.tsx index d49184044..2986449ec 100644 --- a/apps/st/src/components/statistics/management/SewagePipeMng.tsx +++ b/apps/st/src/components/statistics/management/SewagePipeMng.tsx @@ -1,9 +1,30 @@ import SearchForm from "@/components/module/SearchForm"; +import TabDataGrid from "@/components/module/TabDataGrid"; +import ApproveButton from "@/components/button/ApproveButton"; +import RejectButton from "@/components/button/RejectButton" + +import { SewagePipeColumnInfoList } from "@/components/util/column/SewagePipeColumn"; const SewagePipeMng = () => { + + const approveFunc = () => { + console.log('appoved'); + // 승인 요청 로직 + }; + + const rejectFunc = () => { + console.log('reject'); + // 보완 요청 로직 + }; + return ( <> +
+ + +
+ ); }; diff --git a/apps/st/src/components/statistics/management/SeweragePenetrationRateMng.tsx b/apps/st/src/components/statistics/management/SeweragePenetrationRateMng.tsx index 5d1302f17..c9f1e6fbc 100644 --- a/apps/st/src/components/statistics/management/SeweragePenetrationRateMng.tsx +++ b/apps/st/src/components/statistics/management/SeweragePenetrationRateMng.tsx @@ -5,7 +5,7 @@ import SaveButton from "@/components/button/SaveButton"; import LastYearDataButton from "@/components/button/LastYearDataButton"; import SubmitExceptionButton from "@/components/button/SubmitExceptionButton"; -const SeweragePenetrationRate = () => { +const SeweragePenetrationRateMng = () => { const columns = [ { name: 'sido', fieldName: '시도', header: { text: '시도' } }, { name: 'sgg', fieldName: '구군', header: { text: '구군' } }, @@ -136,4 +136,4 @@ const SeweragePenetrationRate = () => { ); }; -export default SeweragePenetrationRate; \ No newline at end of file +export default SeweragePenetrationRateMng; \ No newline at end of file diff --git a/apps/st/src/components/statistics/management/StatsInputSatusMng.tsx b/apps/st/src/components/statistics/management/StatsInputSatusMng.tsx index 28886e17d..a6d47df30 100644 --- a/apps/st/src/components/statistics/management/StatsInputSatusMng.tsx +++ b/apps/st/src/components/statistics/management/StatsInputSatusMng.tsx @@ -65,7 +65,7 @@ const StatsInputSatusMng = () => { - + ); }; diff --git a/apps/st/src/components/statistics/total/ExcretaTreatmentFacilityTot.tsx b/apps/st/src/components/statistics/total/ExcretaTreatmentFacilityTot.tsx new file mode 100644 index 000000000..cae2b07e6 --- /dev/null +++ b/apps/st/src/components/statistics/total/ExcretaTreatmentFacilityTot.tsx @@ -0,0 +1,11 @@ +import SearchForm from "@/components/module/SearchForm"; + +const ExcretaTreatmentFacilityTot = () => { + return ( + <> + + + ); +}; + +export default ExcretaTreatmentFacilityTot; \ No newline at end of file diff --git a/apps/st/src/components/statistics/total/PrivateSewageFacilityTot.tsx b/apps/st/src/components/statistics/total/PrivateSewageFacilityTot.tsx new file mode 100644 index 000000000..6ce297104 --- /dev/null +++ b/apps/st/src/components/statistics/total/PrivateSewageFacilityTot.tsx @@ -0,0 +1,11 @@ +import SearchForm from "@/components/module/SearchForm"; + +const PrivateSewageFacilityTot = () => { + return ( + <> + + + ); +}; + +export default PrivateSewageFacilityTot; \ No newline at end of file diff --git a/apps/st/src/components/statistics/total/PublicSewageFacilityTot.tsx b/apps/st/src/components/statistics/total/PublicSewageFacilityTot.tsx new file mode 100644 index 000000000..34b1e70ed --- /dev/null +++ b/apps/st/src/components/statistics/total/PublicSewageFacilityTot.tsx @@ -0,0 +1,11 @@ +import SearchForm from "@/components/module/SearchForm"; + +const PublicSewageFacilityTot = () => { + return ( + <> + + + ); +}; + +export default PublicSewageFacilityTot; \ No newline at end of file diff --git a/apps/st/src/components/statistics/total/PumpingStationTot.tsx b/apps/st/src/components/statistics/total/PumpingStationTot.tsx new file mode 100644 index 000000000..620388576 --- /dev/null +++ b/apps/st/src/components/statistics/total/PumpingStationTot.tsx @@ -0,0 +1,11 @@ +import SearchForm from "@/components/module/SearchForm"; + +const PumpingStationTot = () => { + return ( + <> + + + ); +}; + +export default PumpingStationTot; \ No newline at end of file diff --git a/apps/st/src/components/statistics/total/SewageExcretaRelatedTot.tsx b/apps/st/src/components/statistics/total/SewageExcretaRelatedTot.tsx new file mode 100644 index 000000000..772e0d7fd --- /dev/null +++ b/apps/st/src/components/statistics/total/SewageExcretaRelatedTot.tsx @@ -0,0 +1,11 @@ +import SearchForm from "@/components/module/SearchForm"; + +const SewageExcretaRelatedTot = () => { + return ( + <> + + + ); +}; + +export default SewageExcretaRelatedTot; \ No newline at end of file diff --git a/apps/st/src/components/statistics/total/SewageExcretaSlurgeTot.tsx b/apps/st/src/components/statistics/total/SewageExcretaSlurgeTot.tsx new file mode 100644 index 000000000..0abb6bae2 --- /dev/null +++ b/apps/st/src/components/statistics/total/SewageExcretaSlurgeTot.tsx @@ -0,0 +1,11 @@ +import SearchForm from "@/components/module/SearchForm"; + +const SewageExcretaSlurgeTot = () => { + return ( + <> + + + ); +}; + +export default SewageExcretaSlurgeTot; \ No newline at end of file diff --git a/apps/st/src/components/statistics/total/SewagePipeMaintainTot.tsx b/apps/st/src/components/statistics/total/SewagePipeMaintainTot.tsx new file mode 100644 index 000000000..8d9c4e3ea --- /dev/null +++ b/apps/st/src/components/statistics/total/SewagePipeMaintainTot.tsx @@ -0,0 +1,11 @@ +import SearchForm from "@/components/module/SearchForm"; + +const SewagePipeMaintainTot = () => { + return ( + <> + + + ); +}; + +export default SewagePipeMaintainTot; \ No newline at end of file diff --git a/apps/st/src/components/statistics/total/SewagePipeTot.tsx b/apps/st/src/components/statistics/total/SewagePipeTot.tsx new file mode 100644 index 000000000..400eea75e --- /dev/null +++ b/apps/st/src/components/statistics/total/SewagePipeTot.tsx @@ -0,0 +1,11 @@ +import SearchForm from "@/components/module/SearchForm"; + +const SewagePipeTot = () => { + return ( + <> + + + ); +}; + +export default SewagePipeTot; \ No newline at end of file diff --git a/apps/st/src/components/statistics/total/SewageReservoirPumpTot.tsx b/apps/st/src/components/statistics/total/SewageReservoirPumpTot.tsx new file mode 100644 index 000000000..5607b1061 --- /dev/null +++ b/apps/st/src/components/statistics/total/SewageReservoirPumpTot.tsx @@ -0,0 +1,11 @@ +import SearchForm from "@/components/module/SearchForm"; + +const SewageReservoirPumpTot = () => { + return ( + <> + + + ); +}; + +export default SewageReservoirPumpTot; \ No newline at end of file diff --git a/apps/st/src/components/statistics/total/SewerageFinanceTot.tsx b/apps/st/src/components/statistics/total/SewerageFinanceTot.tsx new file mode 100644 index 000000000..ca5f21730 --- /dev/null +++ b/apps/st/src/components/statistics/total/SewerageFinanceTot.tsx @@ -0,0 +1,11 @@ +import SearchForm from "@/components/module/SearchForm"; + +const SewerageFinanceTot = () => { + return ( + <> + + + ); +}; + +export default SewerageFinanceTot; \ No newline at end of file diff --git a/apps/st/src/components/statistics/total/SeweragePenetrationRateTot.tsx b/apps/st/src/components/statistics/total/SeweragePenetrationRateTot.tsx new file mode 100644 index 000000000..e12fc5e70 --- /dev/null +++ b/apps/st/src/components/statistics/total/SeweragePenetrationRateTot.tsx @@ -0,0 +1,139 @@ +import SearchForm from "@/components/module/SearchForm"; +import DataGrid from "@/components/module/DataGrid"; +import SubmitButton from "@/components/button/SumitButton"; +import SaveButton from "@/components/button/SaveButton"; +import LastYearDataButton from "@/components/button/LastYearDataButton"; +import SubmitExceptionButton from "@/components/button/SubmitExceptionButton"; + +const SeweragePenetrationRateTot = () => { + const columns = [ + { name: 'sido', fieldName: '시도', header: { text: '시도' } }, + { name: 'sgg', fieldName: '구군', header: { text: '구군' } }, + { name: 'rvbsn', fieldName: '수계', header: { text: '수계' } }, + { name: 'bstrm', fieldName: '지류', header: { text: '지류' } }, + { name: 'mdbsn', fieldName: '세부단위유역', header: { text: '세부단위유역' } }, + { name: 'lgbsn', fieldName: '중권역', header: { text: '중권역' } }, + { name: 'mdmbsn', fieldName: '소권역', header: { text: '소권역' } }, + { name: 'specl_cntrpln_ar', fieldName: '특별대책지역', header: { text: '특별대책지역' } }, + { name: 'total_popl', fieldName: '총인구', header: { text: '총인구' } }, + { name: 'popl_sm', fieldName: '내국인', header: { text: '내국인' } }, + { name: 'frgnr_popl_sm', fieldName: '외국인', header: { text: '외국인' } }, + { name: 'area', fieldName: '행정참고자료면적(㎢)', header: { text: '행정참고자료면적(㎢)' } }, + { name: 'popltn_sm', fieldName: '총인구(명)', header: { text: '총인구(명)' } }, + { name: 'ar_sm', fieldName: '총면적(㎢)', header: { text: '총면적(㎢)' } }, + { name: 'swge_proc_zone_inner_sm', fieldName: '합계', header: { text: '합계' } }, + { name: 'pblswrg_popltn_sm', fieldName: '소계', header: { text: '소계' } }, + { name: 'pblswrg_prm_trt', fieldName: '물리적처리', header: { text: '물리적처리' } }, + { name: 'pblswrg_scd_trt', fieldName: '생물학적처리', header: { text: '생물학적처리' } }, + { name: 'pblswrg_tert_trt', fieldName: '고도처리', header: { text: '고도처리' } }, + { name: 'wstr_popltn_sm', fieldName: '소계', header: { text: '소계' } }, + { name: 'wstr_prm_trt', fieldName: '물리적처리', header: { text: '물리적처리' } }, + { name: 'wstr_scd_trt', fieldName: '생물학적처리', header: { text: '생물학적처리' } }, + { name: 'wstr_tert_trt', fieldName: '고도처리', header: { text: '고도처리' } }, + { name: 'not_conect_popltn', fieldName: '미접속인구', header: { text: '미접속인구' } }, + { name: 'swge_proc_zone_inner_ar', fieldName: '면적(㎢)', header: { text: '면적(㎢)' } }, + { name: 'swge_proc_zone_outer_sm', fieldName: '합계', header: { text: '합계' } }, + { name: 'town_unproc_popltn', fieldName: '미처리인구', header: { text: '미처리인구' } }, + { name: 'town_swtt_popltn', fieldName: '오수처리인구', header: { text: '오수처리인구' } }, + { name: 'town_wrrtn_popltn', fieldName: '정화조인구', header: { text: '정화조인구' } }, + { name: 'swge_proc_zone_outer_ar', fieldName: '면적(㎢)', header: { text: '면적(㎢)' } }, + { name: 'pblswrg_popltn_suprt', fieldName: '공공하수처리구역인구보급율(%)', header: { text: '공공하수처리구역인구보급율(%)' } }, + { name: 'adtrt_popltn_suprt', fieldName: '고도처리인구보급율(%)', header: { text: `고도처리인구보급율(%)` } }, + { name: 'swrg_instlrt', fieldName: '하수도설치율(%)', header: { text: '하수도설치율(%)' } }, + { name: 'rmk', fieldName: '비고', header: { text: '비고' } }, + ]; + + const columnLayout = [ + { + name: 'groupA', + hideChildHeaders: false, + header: { text: '행정구역' }, + items: ['sido', 'sgg'], + }, + 'rvbsn', + 'bstrm', + { + name: 'groupB', + hideChildHeaders: false, + header: { text: '수계영향권별분류류' }, + items: ['mdbsn', 'lgbsn', 'mdmbsn'], + }, + 'specl_cntrpln_ar', + { + name: 'groupC', + hideChildHeaders: false, + header: { text: '행정참고자료인구' }, + items: ['total_popl', 'popl_sm', 'frgnr_popl_sm'], + }, + 'area', + 'popltn_sm', + 'ar_sm', + { + name: 'groupD', + hideChildHeaders: false, + header: { text: '하수처리구역내' }, + items: [ + 'swge_proc_zone_inner_sm', + { + name: 'pblswrgGroup', + hideChildHeaders: false, + header: { text: '공공하수처리인구(명)' }, + items: ['pblswrg_popltn_sm', 'pblswrg_prm_trt', 'pblswrg_scd_trt', 'pblswrg_tert_trt'], + }, + { + name: 'wstrGroup', + hideChildHeaders: false, + header: { text: '폐수처리인구(명)' }, + items: ['wstr_popltn_sm', 'wstr_prm_trt', 'wstr_scd_trt', 'wstr_tert_trt'], + }, + 'not_conect_popltn', + 'swge_proc_zone_inner_ar' + ], + }, + { + name: 'groupE', + hideChildHeaders: false, + header: { text: '하수처리구역외' }, + items: ['swge_proc_zone_outer_sm', 'town_swtt_popltn', 'town_unproc_popltn', 'town_wrrtn_popltn', 'swge_proc_zone_outer_ar'], + }, + 'pblswrg_popltn_suprt', + 'adtrt_popltn_suprt', + 'swrg_instlrt', + 'rmk', + ]; + + const submitFunc = () => { + console.log('submit'); + // 제출 로직 + }; + + const saveFunc = () => { + console.log('submit'); + // 저장 로직 + }; + + const getLastYearDataFunc = () => { + console.log('get last year data'); + // 지난연도 데이터 가져오기 로직 + }; + + const SubmitExceptionFunc = () => { + console.log('submit exception'); + // 예외 제출 로직 + }; + + return ( + <> + +
+ + + + +
+ + + ); +}; + +export default SeweragePenetrationRateTot; \ No newline at end of file diff --git a/apps/st/src/components/statistics/total/SewerageServiceLevelTot.tsx b/apps/st/src/components/statistics/total/SewerageServiceLevelTot.tsx new file mode 100644 index 000000000..b926e113b --- /dev/null +++ b/apps/st/src/components/statistics/total/SewerageServiceLevelTot.tsx @@ -0,0 +1,11 @@ +import SearchForm from "../../module/SearchForm"; + +const SewerageServiceLevelTot = () => { + return ( + <> + + + ); +}; + +export default SewerageServiceLevelTot; \ No newline at end of file diff --git a/apps/st/src/components/statistics/total/WaterReuseTot.tsx b/apps/st/src/components/statistics/total/WaterReuseTot.tsx new file mode 100644 index 000000000..b0ee88c7c --- /dev/null +++ b/apps/st/src/components/statistics/total/WaterReuseTot.tsx @@ -0,0 +1,11 @@ +import SearchForm from "../../module/SearchForm"; + +const WaterReuseTot = () => { + return ( + <> + + + ); +}; + +export default WaterReuseTot; \ No newline at end of file diff --git a/apps/st/src/components/type/DataGrid.type.ts b/apps/st/src/components/type/DataGrid.type.ts index 01284cef9..f168631de 100644 --- a/apps/st/src/components/type/DataGrid.type.ts +++ b/apps/st/src/components/type/DataGrid.type.ts @@ -16,11 +16,27 @@ export type ColumnInfo = { columnLayout: ColumnLayoutItem[]; }; +export type ColumnInfoList = { + tabKey: string; // 탭을 식별하는 고유 키 (필수), server(api)에 전송할 변수 값 + columns: ColumnConfig[]; + columnLayout: ColumnLayoutItem[]; + rowStartNum?: number; + fixedColumns?: number; + tabLabel?: string; // 탭에 표시될 레이블 +} + export type DataGridProps = { columnInfo: ColumnInfo; rows?: Record[]; rowStartNum?: number; // 헤더 행 개수 (1부터 시작, 예: 2면 1행과 2행까지 헤더로 간주) - isExcelImport?: boolean; // 엑셀 가져오기 유무무 + isExcelImport?: boolean; // 엑셀 가져오기 유무 onRowsChange?: (rows: Record[]) => void; fixedColumns?: number; // 고정할 컬럼 개수 (좌측부터) +}; + +export type TabDataGridProps = { + columnInfo: ColumnInfoList[]; + rows?: Record[]; + isExcelImport?: boolean; // 엑셀 가져오기 유무 + onRowsChange?: (rows: Record[]) => void; }; \ No newline at end of file diff --git a/apps/st/src/components/util/column/SewagePipeColumn.ts b/apps/st/src/components/util/column/SewagePipeColumn.ts new file mode 100644 index 000000000..598d99d3a --- /dev/null +++ b/apps/st/src/components/util/column/SewagePipeColumn.ts @@ -0,0 +1,134 @@ +export const SewagePipeColumnInfoList = [ + { + columns: [ + { name: 'sido', fieldName: '시도', header: { text: '시도' } }, + { name: 'sgg', fieldName: '구군', header: { text: '구군' } }, + { name: 'menu_nm', fieldName: '구분', header: { text: '구분' } }, + { name: 'locg_status_nm', fieldName: '진행상황', header: { text: '진행상황' } }, + { name: 'locg_status', fieldName: '작성률', header: { text: '작성률' } }, + { name: 'locg_charger', fieldName: '작성자', header: { text: '작성자' } }, + { name: 'locg_cttpc', fieldName: '작성자 연락처', header: { text: '작성자 연락처' } }, + { name: 'locg_dept', fieldName: '작성자 부서', header: { text: '작성자 부서' } }, + { name: 'locg_job', fieldName: '작성자 직제', header: { text: '작성자 직제' } }, + { name: 'locg_cnfirm_nm', fieldName: '승인자', header: { text: '승인자' } }, + { name: 'locg_cnfirm_dept', fieldName: '승인자 부서', header: { text: '승인자 부서' } }, + { name: 'locg_cnfirm_job', fieldName: '승인자 직제', header: { text: '승인자 직제' } }, + { name: 'return_opinion_sm', fieldName: '반려 사유', header: { text: '반려 사유' } }, + ], + columnLayout: [ + 'sido', + 'sgg', + 'menu_nm', + { + name: 'groupA', + hideChildHeaders: false, + header: { text: '하수도사업자' }, + items: ['locg_status_nm', 'locg_status', 'locg_charger', 'locg_cttpc', 'locg_dept', 'locg_job', 'locg_cnfirm_nm', 'locg_cnfirm_dept', 'locg_cnfirm_job', 'return_opinion_sm'], + }, + ], + rowStartNum: 2, + fixedColumns: 3, + tabKey: 'a', + tabLabel: '1번째 텝' + }, + { + columns: [ + { name: 'sido', fieldName: '시도', header: { text: '시도' } }, + { name: 'sgg', fieldName: '구군', header: { text: '구군' } }, + { name: 'rvbsn', fieldName: '수계', header: { text: '수계' } }, + { name: 'bstrm', fieldName: '지류', header: { text: '지류' } }, + { name: 'mdbsn', fieldName: '세부단위유역', header: { text: '세부단위유역' } }, + { name: 'lgbsn', fieldName: '중권역', header: { text: '중권역' } }, + { name: 'mdmbsn', fieldName: '소권역', header: { text: '소권역' } }, + { name: 'specl_cntrpln_ar', fieldName: '특별대책지역', header: { text: '특별대책지역' } }, + { name: 'total_popl', fieldName: '총인구', header: { text: '총인구' } }, + { name: 'popl_sm', fieldName: '내국인', header: { text: '내국인' } }, + { name: 'frgnr_popl_sm', fieldName: '외국인', header: { text: '외국인' } }, + { name: 'area', fieldName: '행정참고자료면적(㎢)', header: { text: '행정참고자료면적(㎢)' } }, + { name: 'popltn_sm', fieldName: '총인구(명)', header: { text: '총인구(명)' } }, + { name: 'ar_sm', fieldName: '총면적(㎢)', header: { text: '총면적(㎢)' } }, + { name: 'swge_proc_zone_inner_sm', fieldName: '합계', header: { text: '합계' } }, + { name: 'pblswrg_popltn_sm', fieldName: '소계', header: { text: '소계' } }, + { name: 'pblswrg_prm_trt', fieldName: '물리적처리', header: { text: '물리적처리' } }, + { name: 'pblswrg_scd_trt', fieldName: '생물학적처리', header: { text: '생물학적처리' } }, + { name: 'pblswrg_tert_trt', fieldName: '고도처리', header: { text: '고도처리' } }, + { name: 'wstr_popltn_sm', fieldName: '소계', header: { text: '소계' } }, + { name: 'wstr_prm_trt', fieldName: '물리적처리', header: { text: '물리적처리' } }, + { name: 'wstr_scd_trt', fieldName: '생물학적처리', header: { text: '생물학적처리' } }, + { name: 'wstr_tert_trt', fieldName: '고도처리', header: { text: '고도처리' } }, + { name: 'not_conect_popltn', fieldName: '미접속인구', header: { text: '미접속인구' } }, + { name: 'swge_proc_zone_inner_ar', fieldName: '면적(㎢)', header: { text: '면적(㎢)' } }, + { name: 'swge_proc_zone_outer_sm', fieldName: '합계', header: { text: '합계' } }, + { name: 'town_unproc_popltn', fieldName: '미처리인구', header: { text: '미처리인구' } }, + { name: 'town_swtt_popltn', fieldName: '오수처리인구', header: { text: '오수처리인구' } }, + { name: 'town_wrrtn_popltn', fieldName: '정화조인구', header: { text: '정화조인구' } }, + { name: 'swge_proc_zone_outer_ar', fieldName: '면적(㎢)', header: { text: '면적(㎢)' } }, + { name: 'pblswrg_popltn_suprt', fieldName: '공공하수처리구역인구보급율(%)', header: { text: '공공하수처리구역인구보급율(%)' } }, + { name: 'adtrt_popltn_suprt', fieldName: '고도처리인구보급율(%)', header: { text: `고도처리인구보급율(%)` } }, + { name: 'swrg_instlrt', fieldName: '하수도설치율(%)', header: { text: '하수도설치율(%)' } }, + { name: 'rmk', fieldName: '비고', header: { text: '비고' } }, + ], + columnLayout: [ + { + name: 'groupA', + hideChildHeaders: false, + header: { text: '행정구역' }, + items: ['sido', 'sgg'], + }, + 'rvbsn', + 'bstrm', + { + name: 'groupB', + hideChildHeaders: false, + header: { text: '수계영향권별분류류' }, + items: ['mdbsn', 'lgbsn', 'mdmbsn'], + }, + 'specl_cntrpln_ar', + { + name: 'groupC', + hideChildHeaders: false, + header: { text: '행정참고자료인구' }, + items: ['total_popl', 'popl_sm', 'frgnr_popl_sm'], + }, + 'area', + 'popltn_sm', + 'ar_sm', + { + name: 'groupD', + hideChildHeaders: false, + header: { text: '하수처리구역내' }, + items: [ + 'swge_proc_zone_inner_sm', + { + name: 'pblswrgGroup', + hideChildHeaders: false, + header: { text: '공공하수처리인구(명)' }, + items: ['pblswrg_popltn_sm', 'pblswrg_prm_trt', 'pblswrg_scd_trt', 'pblswrg_tert_trt'], + }, + { + name: 'wstrGroup', + hideChildHeaders: false, + header: { text: '폐수처리인구(명)' }, + items: ['wstr_popltn_sm', 'wstr_prm_trt', 'wstr_scd_trt', 'wstr_tert_trt'], + }, + 'not_conect_popltn', + 'swge_proc_zone_inner_ar' + ], + }, + { + name: 'groupE', + hideChildHeaders: false, + header: { text: '하수처리구역외' }, + items: ['swge_proc_zone_outer_sm', 'town_swtt_popltn', 'town_unproc_popltn', 'town_wrrtn_popltn', 'swge_proc_zone_outer_ar'], + }, + 'pblswrg_popltn_suprt', + 'adtrt_popltn_suprt', + 'swrg_instlrt', + 'rmk', + ], + rowStartNum: 2, + fixedColumns: 1, + tabKey: 'b', + tabLabel: '2번째 텝' + }, +] \ No newline at end of file