diff --git a/apps/st/.gitignore b/apps/st/.gitignore index b63718b61..f06d9d40b 100644 --- a/apps/st/.gitignore +++ b/apps/st/.gitignore @@ -38,4 +38,5 @@ yarn-error.log* next-env.d.ts .env* -!.env.example \ No newline at end of file +!.env.example +*.md \ No newline at end of file diff --git a/apps/st/.prettierrc b/apps/st/.prettierrc index b4bfed357..a7963dac2 100644 --- a/apps/st/.prettierrc +++ b/apps/st/.prettierrc @@ -1,3 +1,4 @@ { - "plugins": ["prettier-plugin-tailwindcss"] + "plugins": ["prettier-plugin-tailwindcss"], + "tabWidth": 4 } diff --git a/apps/st/app/(with-base-layout)/layout.tsx b/apps/st/app/(with-base-layout)/layout.tsx index b6f1e1cf8..3fe61855e 100644 --- a/apps/st/app/(with-base-layout)/layout.tsx +++ b/apps/st/app/(with-base-layout)/layout.tsx @@ -1,12 +1,13 @@ "use client"; -import { useEffect, useCallback } from "react"; +import { useEffect, useCallback, useMemo } from "react"; import { usePathname } from "next/navigation"; import { useQueryClient, useQuery } from "@tanstack/react-query"; import type { MenuItem } from "@/entities/menu"; import { useMenuStore, useAlertStore } from "@/shared/lib"; import { useTabStore, getTabStoreState } from "@/store/tab-store"; +import { useTabStore as gridTabStore } from "@rsone/components"; import { getConfig } from "@common/config"; import { getCookie } from "@/shared/lib"; import { getMenuList } from "../../../../packages/common/components/src/api"; @@ -29,10 +30,17 @@ export default function WithBaseLayout({ const queryClient = useQueryClient(); const setCurrentMenuInfo = useMenuStore((state) => state.setCurrentMenuInfo); + const currentMenuInfo = useMenuStore((state) => state.currentMenuInfo); const addTab = useTabStore((state) => state.addTab); const setActiveTab = useTabStore((state) => state.setActiveTab); const setMenuList = useTabStore((state) => state.setMenuList); + const activeTabId = useTabStore((state) => state.activeTabId); + const menuList = useTabStore((state) => state.menuList); + const tabs = useTabStore((state) => state.tabs); + const addTabForGrid = gridTabStore((state) => state.addTab); + const setActiveTabForGrid = gridTabStore((state) => state.setActiveTab); + const setMenuListForGrid = gridTabStore((state) => state.setMenuList); const alert = useAlertStore((state) => state.setMessage); const { data: menuListData } = useQuery({ @@ -44,8 +52,9 @@ export default function WithBaseLayout({ useEffect(() => { if (menuListData) { setMenuList(menuListData); + setMenuListForGrid(menuListData); } - }, [menuListData, setMenuList]); + }, [menuListData, setMenuList, setMenuListForGrid]); useEffect(() => { if (!pathname) return; @@ -55,23 +64,73 @@ export default function WithBaseLayout({ const tab = currentTabs.find((t) => t.path === pathname); if (tab) { setActiveTab(tab.id); + setActiveTabForGrid(tab.id); } - }, [pathname, setActiveTab]); + }, [pathname, setActiveTab, setActiveTabForGrid]); + + // 활성 탭의 메뉴를 찾아서 urlPath를 가져옴. 탭이 전부 닫히면 ''를 넘겨 LNB에서 메뉴 강조 제거 + const currentPath = useMemo(() => { + if (tabs.length === 0) return ""; + + if (activeTabId && menuList) { + // menuList에서 활성 탭의 메뉴 찾기 + const findMenuByEngNm = (items: MenuItem[]): MenuItem | null => { + for (const item of items) { + if (item.menuEngNm === activeTabId) { + return item; + } + if (item.children) { + const found = findMenuByEngNm(item.children); + if (found) return found; + } + } + return null; + }; + + const activeMenu = findMenuByEngNm(menuList); + + if (activeMenu?.urlPath) { + return activeMenu.urlPath; + } + } + + // 탭에서 직접 찾기 + if (activeTabId) { + const currentTabs = getTabStoreState().tabs; + const activeTab = currentTabs.find((t) => t.id === activeTabId); + if (activeTab?.menuItem?.urlPath) { + return activeTab.menuItem.urlPath; + } + } + + // currentMenuInfo에서 가져오기 + if (currentMenuInfo?.urlPath) { + return currentMenuInfo.urlPath; + } + + // 기본값으로 pathname 사용 + return pathname ?? ""; + }, [tabs.length, activeTabId, menuList, currentMenuInfo, pathname]); const handleMenuClick = useCallback((item: MenuItem) => { setCurrentMenuInfo(item); const addTabResult: boolean = addTab(item); + + // packages/rsone의 스토어에도 동기화 + if (addTabResult) { + addTabForGrid(item); + } if (!addTabResult) { alert(`최대 추가 할 수 있는 화면 수를 초과 하였습니다. 기존 화면을 닫은 후 이용해주세요.`); } - }, [setCurrentMenuInfo, addTab]); + }, [setCurrentMenuInfo, addTab, addTabForGrid, alert]); return ( {children} diff --git a/apps/st/middleware.ts b/apps/st/middleware.ts index 4ed1ed018..8b1ecb501 100644 --- a/apps/st/middleware.ts +++ b/apps/st/middleware.ts @@ -32,7 +32,7 @@ export function middleware(req: NextRequest) { `[Request]\n Original Pathname: ${pathname}`, ]; - if (pathname.startsWith("/st-service")) { + if (pathname.startsWith("/st-management-service") || pathname.startsWith("/st-input-service")) { const requestHeaders = new Headers(req.headers); const token = req.cookies.get(CLAIM_NAME)?.value; const userId = req.cookies.get(AUTH_USER_ID)?.value; @@ -138,5 +138,5 @@ export function middleware(req: NextRequest) { } export const config = { - matcher: ["/server/:path*", "/attach-service/:path*", "/api/link/:path*", "/st-service/:path*", "/"], + matcher: ["/server/:path*", "/attach-service/:path*", "/api/link/:path*", "/st-management-service/:path*", "/st-input-service/:path*", "/"], }; diff --git a/apps/st/package.json b/apps/st/package.json index b6de459f8..cd951af0d 100644 --- a/apps/st/package.json +++ b/apps/st/package.json @@ -12,6 +12,7 @@ "@common/assets": "workspace:*", "@common/components": "workspace:*", "@common/config": "workspace:*", + "@faker-js/faker": "^9.9.0", "@fontsource-variable/noto-sans-kr": "^5.2.5", "@frontend-opensource/use-react-hooks": "^1.1.4", "@fullcalendar/core": "^6.1.17", @@ -25,6 +26,7 @@ "@radix-ui/react-label": "^2.1.2", "@radix-ui/react-slot": "^1.1.2", "@radix-ui/react-tooltip": "^1.1.8", + "@rsone/components": "workspace:*", "@tanstack/react-query": "^5.67.1", "@tanstack/react-query-devtools": "^5.75.5", "@tanstack/react-table": "^8.21.2", diff --git a/apps/st/src/app/providers/app-provider.tsx b/apps/st/src/app/providers/app-provider.tsx index efd1f308e..70e69eee8 100644 --- a/apps/st/src/app/providers/app-provider.tsx +++ b/apps/st/src/app/providers/app-provider.tsx @@ -13,6 +13,7 @@ import { PromptDialog } from "@/shared/ui/prompt-dialog"; import { ConfirmDialog } from "@/shared/ui/confirm-dialog"; import { SelectPromptDialog } from "@/shared/ui/select-prompt-dialog"; import { PromptProvider, StoreProvider } from "@/shared/providers"; +import { AlertDialog as RsoneAlertDialog } from "@rsone/components"; const AppProvider = ({ children }: Readonly<{ children: React.ReactNode }>) => { const queryClient = new QueryClient(); @@ -31,6 +32,7 @@ const AppProvider = ({ children }: Readonly<{ children: React.ReactNode }>) => { + diff --git a/apps/st/src/components/button/AddArticleButton.tsx b/apps/st/src/components/button/AddArticleButton.tsx new file mode 100644 index 000000000..b1bb3252f --- /dev/null +++ b/apps/st/src/components/button/AddArticleButton.tsx @@ -0,0 +1,16 @@ +import { Button } from "@common/components"; + +interface AddArticleButtonProps { + addArticleFunc: () => void; +} + +const AddArticleButton = ({ addArticleFunc }: AddArticleButtonProps) => { + return ( + + ); +}; + +export default AddArticleButton; \ No newline at end of file diff --git a/apps/st/src/components/button/ApproveButton.tsx b/apps/st/src/components/button/ApproveButton.tsx index 1cbfb0b28..36455f7ff 100644 --- a/apps/st/src/components/button/ApproveButton.tsx +++ b/apps/st/src/components/button/ApproveButton.tsx @@ -6,9 +6,9 @@ interface ApproveButtonProps { const ApproveButton = ({ approveFunc }: ApproveButtonProps) => { return ( - ); }; diff --git a/apps/st/src/components/button/DeleteArticleButton.tsx b/apps/st/src/components/button/DeleteArticleButton.tsx new file mode 100644 index 000000000..1a6432a64 --- /dev/null +++ b/apps/st/src/components/button/DeleteArticleButton.tsx @@ -0,0 +1,16 @@ +import { Button } from "@common/components"; + +interface DeleteArticleButtonProps { + deleteArticleFunc: () => void; +} + +const DeleteArticleButton = ({ deleteArticleFunc }: DeleteArticleButtonProps) => { + return ( + + ); +}; + +export default DeleteArticleButton; \ No newline at end of file diff --git a/apps/st/src/components/button/InstallationStatusButton.tsx b/apps/st/src/components/button/InstallationStatusButton.tsx new file mode 100644 index 000000000..fffe4bb9c --- /dev/null +++ b/apps/st/src/components/button/InstallationStatusButton.tsx @@ -0,0 +1,16 @@ +import { Button } from "@common/components"; + +interface InstallationStatusButtonProps { + installationStatusFunc: () => void; +} + +const InstallationStatusButton = ({ installationStatusFunc }: InstallationStatusButtonProps) => { + return ( + + ); +}; + +export default InstallationStatusButton; \ No newline at end of file diff --git a/apps/st/src/components/button/LastYearDataButton.tsx b/apps/st/src/components/button/LastYearDataButton.tsx new file mode 100644 index 000000000..d9020c4ae --- /dev/null +++ b/apps/st/src/components/button/LastYearDataButton.tsx @@ -0,0 +1,16 @@ +import { Button } from "@common/components"; + +interface LastYearDataButtonProps { + getLastYearDataFunc: () => void; +} + +const LastYearDataButton = ({ getLastYearDataFunc }: LastYearDataButtonProps) => { + return ( + + ); +}; + +export default LastYearDataButton; \ No newline at end of file diff --git a/apps/st/src/components/button/NotApplicableCheckButton.tsx b/apps/st/src/components/button/NotApplicableCheckButton.tsx new file mode 100644 index 000000000..507334e2e --- /dev/null +++ b/apps/st/src/components/button/NotApplicableCheckButton.tsx @@ -0,0 +1,28 @@ +import { Checkbox, buttonVariants, cn } from "@common/components"; +import type React from "react"; + +interface NotApplicableCheckButtonProp { + isCheck: boolean; + setIsCheck: React.Dispatch>; +} + +const NotApplicableCheckButton = ({ isCheck, setIsCheck }: NotApplicableCheckButtonProp) => { + + const handleCheckChange = (value: boolean) => { + setIsCheck(value); + }; + + return ( +
+ + +
+ ); +}; + +export default NotApplicableCheckButton; \ No newline at end of file diff --git a/apps/st/src/components/button/OperateDataButton.tsx b/apps/st/src/components/button/OperateDataButton.tsx new file mode 100644 index 000000000..f2972180d --- /dev/null +++ b/apps/st/src/components/button/OperateDataButton.tsx @@ -0,0 +1,16 @@ +import { Button } from "@common/components"; + +interface OperateDataButtonProps { + getOperateDataFunc: () => void; +} + +const OperateDataButton = ({ getOperateDataFunc }: OperateDataButtonProps) => { + return ( + + ); +}; + +export default OperateDataButton; \ No newline at end of file diff --git a/apps/st/src/components/button/RejectButton.tsx b/apps/st/src/components/button/RejectButton.tsx index 66dd34735..8210059ab 100644 --- a/apps/st/src/components/button/RejectButton.tsx +++ b/apps/st/src/components/button/RejectButton.tsx @@ -6,9 +6,9 @@ interface RejectButtonProps { const RejectButton = ({ rejectFunc }: RejectButtonProps) => { return ( - ); }; diff --git a/apps/st/src/components/button/ReturnButton.tsx b/apps/st/src/components/button/ReturnButton.tsx new file mode 100644 index 000000000..b433d66a7 --- /dev/null +++ b/apps/st/src/components/button/ReturnButton.tsx @@ -0,0 +1,16 @@ +import { Button } from "@common/components"; + +interface ReturnButtonProps { + returnFunc: () => void; +} + +const ReturnButton = ({ returnFunc }: ReturnButtonProps) => { + return ( + + ); +}; + +export default ReturnButton; \ No newline at end of file diff --git a/apps/st/src/components/button/ReviewOpinionButton.tsx b/apps/st/src/components/button/ReviewOpinionButton.tsx new file mode 100644 index 000000000..50df8fcb4 --- /dev/null +++ b/apps/st/src/components/button/ReviewOpinionButton.tsx @@ -0,0 +1,16 @@ +import { Button } from "@common/components"; + +interface ReviewOpinionButtonProps { + reviewOpinionFunc: () => void; +} + +const ReviewOpinionButton = ({ reviewOpinionFunc }: ReviewOpinionButtonProps) => { + return ( + + ); +}; + +export default ReviewOpinionButton; \ No newline at end of file diff --git a/apps/st/src/components/button/SaveButton.tsx b/apps/st/src/components/button/SaveButton.tsx new file mode 100644 index 000000000..08fbb8d94 --- /dev/null +++ b/apps/st/src/components/button/SaveButton.tsx @@ -0,0 +1,16 @@ +import { Button } from "@common/components"; + +interface SaveButtonProps { + saveFunc: () => void; +} + +const SaveButton = ({ saveFunc }: SaveButtonProps) => { + return ( + + ); +}; + +export default SaveButton; \ No newline at end of file diff --git a/apps/st/src/components/button/SubmitExceptionButton.tsx b/apps/st/src/components/button/SubmitExceptionButton.tsx new file mode 100644 index 000000000..0bf100e68 --- /dev/null +++ b/apps/st/src/components/button/SubmitExceptionButton.tsx @@ -0,0 +1,16 @@ +import { Button } from "@common/components"; + +interface SubmitExceptionButtonProps { + SubmitExceptionFunc: () => void; +} + +const SubmitExceptionButton = ({ SubmitExceptionFunc }: SubmitExceptionButtonProps) => { + return ( + + ); +}; + +export default SubmitExceptionButton; \ No newline at end of file diff --git a/apps/st/src/components/button/SumitButton.tsx b/apps/st/src/components/button/SumitButton.tsx new file mode 100644 index 000000000..0443889b7 --- /dev/null +++ b/apps/st/src/components/button/SumitButton.tsx @@ -0,0 +1,16 @@ +import { Button } from "@common/components"; + +interface SubmitButtonProps { + submitFunc: () => void; +} + +const SubmitButton = ({ submitFunc }: SubmitButtonProps) => { + return ( + + ); +}; + +export default SubmitButton; \ No newline at end of file diff --git a/apps/st/src/components/button/UpdateOperateDataButton.tsx b/apps/st/src/components/button/UpdateOperateDataButton.tsx new file mode 100644 index 000000000..8229e7da3 --- /dev/null +++ b/apps/st/src/components/button/UpdateOperateDataButton.tsx @@ -0,0 +1,16 @@ +import { Button } from "@common/components"; + +interface UpdateOperateDataButtonProps { + updateOperateDataButtonFunc: () => void; +} + +const UpdateOperateDataButton = ({ updateOperateDataButtonFunc }: UpdateOperateDataButtonProps) => { + return ( + + ); +}; + +export default UpdateOperateDataButton; \ No newline at end of file diff --git a/apps/st/src/components/css/DataGrid.css b/apps/st/src/components/css/DataGrid.css index b7d8352da..e69de29bb 100644 --- a/apps/st/src/components/css/DataGrid.css +++ b/apps/st/src/components/css/DataGrid.css @@ -1,105 +0,0 @@ -/* RealGrid2 폰트 설정 - 루트 폰트와 일치 */ -.rg-root, -.rg-grid, -.rg-body, -.rg-cell, -.rg-header, -.rg-head, -.rg-rowbarcontainer { - font-family: "Noto Sans KR", sans-serif !important; - font-size: 12px !important; - font-weight: 500; - color: #333 !important; -} - -/* RealGrid2 컨테이너 스타일 */ -.rg-root { - border-radius: 0.5rem !important; - overflow: hidden !important; - border: 1px solid #dbe2e6 !important; -} - -.rg-grid { - border-radius: 0.5rem !important; - /* box-shadow: 0 0 10px -1px rgba(0, 0, 0, 0.06); */ -} - -/* 헤더 스타일 */ -.rg-header, -.rg-head, -.rg-foot, -.rg-footer, -.rg-fixed-footer { - background-color: #f5f7fa !important; - color: #333 !important; -} - -/* 헤더 셀 스타일 */ -.rg-header .rg-cell, -.rg-head .rg-cell { - background-color: #f5f7fa !important; -} - -/* 헤더 필터링 영역 위치 조정 - 오른쪽 여백 추가 */ -.rg-header-filtering { - left: auto !important; - right: 8px !important; -} - -/* 그룹 헤더 스타일 */ -.rg-header-group, -.rg-header-group-cell { - background-color: #f5f7fa !important; -} - -/* 첫 번째 헤더 셀 - 왼쪽 상단 모서리 */ -.rg-header .rg-cell:first-child, -.rg-head .rg-cell:first-child { - border-top-left-radius: 8px !important; -} - -/* 마지막 헤더 셀 - 오른쪽 상단 모서리 */ -.rg-header .rg-cell:last-child, -.rg-head .rg-cell:last-child { - border-top-right-radius: 8px !important; -} - -/* border color 통일 */ -.rg-header .rg-table tr td, -.rg-head .rg-table tr td:not(:first-child), -.rg-body .rg-table tr td, -.rg-rowbarcontainer .rg-table tr td:not(:first-child) { - border-left: 1px solid #dbe2e6 !important; - border-bottom: 1px solid #dbe2e6 !important; - border-right: none !important; -} -.rg-rowindicator-head, -.rg-rowbarcontainer .rg-table tr td:first-child { - border-right: none !important; - border-bottom: 1px solid #dbe2e6 !important; - border-left: none !important; -} -.rg-footer .rg-table tr td, -.rg-fixed-footer .rg-table tr td, -.rg-foot .rg-table tr td { - border-right: 1px solid #dbe2e6 !important; - border-top: 1px solid #dbe2e6 !important; -} - -.rg-rowbarcontainer .rg-table tr td { - background-color: #f5f7fa !important; -} - -/* 푸터 마지막 요소 - 오른쪽 border 제거 */ -.rg-footer .rg-table tr td:last-child { - border-right: none !important; -} - -/* 고정 컬럼 바 */ -.rg-fixed-column-bar { - background-color: #ccc !important; - border-right: 1px solid #ccc !important; -} - - - diff --git a/apps/st/src/components/module/CompactDataGrid.tsx b/apps/st/src/components/module/CompactDataGrid.tsx new file mode 100644 index 000000000..4377eb762 --- /dev/null +++ b/apps/st/src/components/module/CompactDataGrid.tsx @@ -0,0 +1,295 @@ +import { useRef, useEffect } from "react" +import { ValueType } from "realgrid" + +import RealGrid, { GridFitStyle } from "realgrid" +import { RealGridReact, RGDataColumn } from "realgrid-react" +import type { DataGridProps, RealGridColumnInfo, GridViewWithSetColumns, FooterItemWithCallback } from "../type/DataGrid.type" +import { getComputedValueCallback } from "@/components/util/grid/computedFieldCallbacks" + +// RealGrid 타입 확장 +declare module "realgrid" { + interface GridView { + setOptions(options: { + footer?: { visible?: boolean }; + summaryMode?: 'aggregate' | 'statistical' | 'none'; + checkBar?: { visible?: boolean }; + }): void; + } +} + +RealGrid.setLicenseKey(process.env.NEXT_PUBLIC_ST_REALGRID_KEY || ''); + +/** setDataComparer(field, comparer) - setFields 없이 숫자 정렬용. comparer(field, row1, row2) => -1|0|1 */ +interface RealGridDataProviderComparer { + setDataComparer?(field: string | number, comparer: (field: string | number, row1: number, row2: number) => number): void; + getValue?(row: number, field: string | number): unknown; +} + +/** columnInfo → RealGrid DataField 배열 (computed → valueCallback 포함, setFields용) */ +function buildFieldsFromColumns(columns: { fieldName: string; valueType?: ValueType; computed?: import("../type/DataGrid.type").ComputedFieldConfig }[]) { + return columns.map((col) => { + const field: { fieldName: string; dataType: string; valueCallback?: (prod: unknown, dataRow: number, fieldName: string, fieldNames: string[], values: unknown[]) => number } = { + fieldName: col.fieldName, + dataType: col.valueType === ValueType.NUMBER ? "number" : "text", + }; + if (col.computed) { + const hasFields = "fields" in col.computed && col.computed.fields.length > 0; + const hasNumeratorDenominator = "numeratorFields" in col.computed && col.computed.numeratorFields.length > 0 && col.computed.denominatorFields.length > 0; + if (hasFields || hasNumeratorDenominator) { + field.valueCallback = getComputedValueCallback(col.computed) as (prod: unknown, dataRow: number, fieldName: string, fieldNames: string[], values: unknown[]) => number; + } + } + return field; + }); +} + +/** setDataComparer용 숫자 비교기. setFields 없이 숫자 정렬. */ +function createNumberComparer(dp: RealGridDataProviderComparer): (field: string | number, row1: number, row2: number) => number { + return (field, row1, row2) => { + const get = dp.getValue?.bind(dp); + if (!get) return 0; + const val1 = get(row1, field); + const val2 = get(row2, field); + if (val1 == null && val2 == null) return 0; + if (val1 == null) return 1; + if (val2 == null) return -1; + const num1 = typeof val1 === "number" ? val1 : parseFloat(String(val1)); + const num2 = typeof val2 === "number" ? val2 : parseFloat(String(val2)); + if (isNaN(num1) && isNaN(num2)) return 0; + if (isNaN(num1)) return 1; + if (isNaN(num2)) return -1; + return num1 > num2 ? 1 : num1 < num2 ? -1 : 0; + }; +} + +const CompactDataGrid = ({ columnInfo, fixedColumns, showHeaderCheckbox = false, rowData, setRowData }: DataGridProps) => { + const { columns, columnLayout } = columnInfo; + const gridRef = useRef(null); + + useEffect(() => { + if (!gridRef.current?.gridView || !gridRef.current?.dataProvider) return; + + const gridView = gridRef.current.gridView; + const dataProvider = gridRef.current.dataProvider; + + // setDataComparer로 숫자 정렬 (setFields 미사용 → 데이터 유실·필드 중복 오류 없음) + const dp = dataProvider as RealGridDataProviderComparer; + if (typeof dp.setDataComparer === "function" && typeof dp.getValue === "function") { + const numberComparer = createNumberComparer(dp); + const seen = new Set(); + columns.forEach((col) => { + if (col.valueType === ValueType.NUMBER && col.fieldName && !seen.has(col.fieldName)) { + seen.add(col.fieldName); + try { + dp.setDataComparer!(col.fieldName, numberComparer); + } catch (e) { + console.warn("setDataComparer:", e); + } + } + }); + } + + gridView.setDisplayOptions({ + fitStyle: GridFitStyle.EVEN + }); + + gridView.header.height = 60; + + // 헤더 체크박스 표시 여부 설정 + gridView.setOptions({ + checkBar: { visible: showHeaderCheckbox }, + rowIndicator: { visible: false }, + }); + + // footer가 있는 컬럼이 있으면 footer 높이 설정 + const hasFooter = columns.some(col => col.footer); + if (hasFooter) { + // footer가 배열이거나 multiRow가 true인 footer가 있으면 멀티푸터 설정 (여러 행) + const hasArrayFooter = columns.some(col => col.footer && Array.isArray(col.footer)); + const hasMultiRowFooter = columns.some(col => { + const footer = col.footer; + return footer && !Array.isArray(footer) && 'multiRow' in footer && footer.multiRow === true; + }); + if (hasArrayFooter || hasMultiRowFooter) { + // footer 배열의 길이를 확인하여 행 수 결정 + const arrayFooterLengths = columns + .filter(col => col.footer && Array.isArray(col.footer)) + .map(col => (col.footer as unknown[]).length); + const maxFooterLength = arrayFooterLengths.length > 0 + ? Math.max(...arrayFooterLengths) + : 2; // 기본값 2 + const footerHeights = Array.from({ length: maxFooterLength }, () => ({ height: 40 })); + gridView.setFooters?.(footerHeights); + } else { + gridView.footer.height = 40; + } + } + + // 고정 컬럼 설정 + if (fixedColumns !== undefined && fixedColumns > 0) { + gridView.setFixedOptions({ + colCount: fixedColumns, + }); + } + + gridView.setOptions({ + summaryMode: "aggregate" + }); + + // footerUserSpans가 있는 경우 setColumnLayout으로 설정 + const checkFooterUserSpans = (layout: unknown[]): boolean => { + return layout.some(item => { + if (typeof item === 'string') return false; + if (item && typeof item === 'object' && 'footerUserSpans' in item) return true; + if (item && typeof item === 'object' && 'items' in item && Array.isArray(item.items)) { + return checkFooterUserSpans(item.items); + } + return false; + }); + }; + + if (checkFooterUserSpans(columnLayout)) { + // setColumnLayout은 그리드가 완전히 렌더링된 후 호출해야 함 + setTimeout(() => { + if (gridRef.current?.gridView && columnLayout) { + gridRef.current.gridView.setColumnLayout?.(columnLayout); + } + }, 100); + } + + // footer 설정 (배열 또는 단일 객체 모두 처리) + // setFooters 호출 후 footer 배열 설정 + setTimeout(() => { + const currentGridView = gridRef.current?.gridView; + if (!currentGridView || !currentGridView.columnByName) { + console.warn('GridView not available for footer setup'); + return; + } + + // footer가 배열인 컬럼이 있으면 setColumns를 다시 호출하여 footer 설정 + const hasArrayFooter = columns.some(col => col.footer && Array.isArray(col.footer)); + if (hasArrayFooter) { + // 모든 컬럼 정보를 다시 가져와서 footer 배열을 포함하여 setColumns 호출 + const columnsWithFooter: RealGridColumnInfo[] = columns.map(col => { + const colInfo: RealGridColumnInfo = { + name: col.name, + fieldName: col.fieldName, + header: col.header, + editable: col.editable, + valueType: col.valueType, + dataFormat: col.dataFormat, + ...(col.styleName != null && { styleName: col.styleName }), + ...(col.mergeCells && { mergeRule: { criteria: 'value' as const } }), + }; + + if (col.footer) { + if (Array.isArray(col.footer)) { + colInfo.footer = col.footer; + } else { + colInfo.footer = { + text: col.footer.text, + expression: col.footer.expression, + numberFormat: col.footer.numberFormat, + }; + } + } + + return colInfo; + }); + + try { + (currentGridView as GridViewWithSetColumns).setColumns?.(columnsWithFooter); + + // setColumns 호출 후 footer 배열의 valueCallback을 다시 설정 + setTimeout(() => { + columns.forEach(col => { + if (!col.footer || !Array.isArray(col.footer)) return; + + try { + const column = currentGridView.columnByName(col.name); + if (!column) return; + + // footer 배열의 각 요소에 valueCallback 설정 + col.footer.forEach((footerItem, index) => { + if (footerItem.valueCallback && column.footer) { + // footer가 배열인 경우 + if (Array.isArray(column.footer) && column.footer[index]) { + (column.footer[index] as FooterItemWithCallback).valueCallback = footerItem.valueCallback; + } + } + }); + } catch (e) { + console.warn(`Error setting footer array valueCallback for column ${col.name}:`, e); + } + }); + }, 50); + } catch (e) { + console.warn('Failed to set columns with footer array:', e); + } + } + + // footer가 단일 객체이고 valueCallback이 있는 경우 + columns.forEach(col => { + if (!col.footer || Array.isArray(col.footer)) return; + + try { + const column = currentGridView.columnByName(col.name); + if (!column || !column.footer) return; + + if (col.footer.valueCallback) { + (column.footer as { valueCallback?: typeof col.footer.valueCallback }).valueCallback = col.footer.valueCallback; + } + } catch (e) { + console.warn(`Error setting footer valueCallback for column ${col.name}:`, e); + } + }); + }, 200); + + }, [columns, columnLayout, fixedColumns, rowData, showHeaderCheckbox]); + + return ( +
+
+ { + const dp = gridRef.current?.dataProvider as { setFields?: (fields: unknown[]) => void } | undefined; + if (dp?.setFields) { + dp.setFields(buildFieldsFromColumns(columns)); + } + }} + > + {columns.map((column) => ( + + ))} + +
+
+ ); +}; + +export default CompactDataGrid; \ No newline at end of file diff --git a/apps/st/src/components/module/DataGrid.tsx b/apps/st/src/components/module/DataGrid.tsx index be6adc932..b762ed5cb 100644 --- a/apps/st/src/components/module/DataGrid.tsx +++ b/apps/st/src/components/module/DataGrid.tsx @@ -1,14 +1,28 @@ -import { useRef, useEffect, useState } from "react" +import { useRef, useEffect } from "react"; +import { ValueType } from "realgrid"; + +import RealGrid, { GridFitStyle, type DisplayOptions } from "realgrid"; +import { RealGridReact, RGDataColumn } from "realgrid-react"; +import type { DataGridProps, RealGridColumnInfo, GridViewWithSetColumns, FooterItemWithCallback, ColumnLayoutItem } from "../type/DataGrid.type"; +import { getComputedValueCallback } from "@/components/util/grid/computedFieldCallbacks"; +import JSZip from "jszip"; +import * as XLSX from "xlsx"; -import RealGrid, { GridFitStyle, type DisplayOptions } from "realgrid" -import { RealGridReact, RGDataColumn } from "realgrid-react" -import type { DataGridProps } from "../type/DataGrid.type" -import JSZip from "jszip" -import * as XLSX from "xlsx" import { useAlertStore } from "@/shared/lib"; import { Button } from "@common/components"; -import { useTabStore } from "@/store/tab-store" +import { useTabStore } from "@/store/tab-store"; + +// RealGrid 타입 확장 +declare module "realgrid" { + interface GridView { + setOptions(options: { + footer?: { visible?: boolean }; + summaryMode?: 'aggregate' | 'statistical' | 'none'; + checkBar?: { visible?: boolean }; + }): void; + } +} // Window 인터페이스 확장 declare global { @@ -24,16 +38,267 @@ if (typeof window !== "undefined") { RealGrid.setLicenseKey(process.env.NEXT_PUBLIC_ST_REALGRID_KEY || ''); -const DataGrid = ({ columnInfo, rows = [], rowStartNum, isExcelImport = true, fixedColumns }: DataGridProps) => { +/** setDataComparer(field, comparer) - setFields 없이 숫자 정렬용. comparer(field, row1, row2) => -1|0|1 */ +interface RealGridDataProviderComparer { + setDataComparer?(field: string | number, comparer: (field: string | number, row1: number, row2: number) => number): void; + getValue?(row: number, field: string | number): unknown; +} + +/** setDataComparer용 숫자 비교기. setFields 없이 숫자 정렬. */ +function createNumberComparer(dp: RealGridDataProviderComparer): (field: string | number, row1: number, row2: number) => number { + return (field, row1, row2) => { + const get = dp.getValue?.bind(dp); + if (!get) return 0; + const val1 = get(row1, field); + const val2 = get(row2, field); + if (val1 == null && val2 == null) return 0; + if (val1 == null) return 1; + if (val2 == null) return -1; + const num1 = typeof val1 === "number" ? val1 : parseFloat(String(val1)); + const num2 = typeof val2 === "number" ? val2 : parseFloat(String(val2)); + if (isNaN(num1) && isNaN(num2)) return 0; + if (isNaN(num1)) return 1; + if (isNaN(num2)) return -1; + return num1 > num2 ? 1 : num1 < num2 ? -1 : 0; + }; +} + +/** 정렬 필드 값을 숫자로 파싱 (쉼표 제거) */ +function toNum(v: unknown): number { + if (v == null) return NaN; + if (typeof v === "number") return isNaN(v) ? NaN : v; + return parseFloat(String(v).replace(/,/g, "")) || NaN; +} + +/** 헤더에 정렬 화살표 표시 (CSS 방식) */ +function updateHeaderSortIndicator( + gridView: { + columnByIndex?: (i: number) => { fieldName?: string; name?: string } | undefined; + columnByName?: (name: string) => { fieldName?: string; name?: string } | undefined; + getColumn?: (i: number) => { fieldName?: string; name?: string } | undefined; + }, + columns: { fieldName?: string; name: string }[], + sortDir: Record +) { + try { + columns.forEach((col) => { + if (!col.fieldName && !col.name) return; + + // 컬럼 찾기 + let gridCol: { fieldName?: string; name?: string } | undefined; + for (let i = 0; i < 100; i++) { + const c = gridView.columnByIndex?.(i) ?? gridView.getColumn?.(i); + if (!c) break; + if (c.fieldName === (col.fieldName || col.name) || c.name === (col.fieldName || col.name)) { + gridCol = c; + break; + } + } + if (!gridCol) { + gridCol = gridView.columnByName?.(col.name); + } + + if (!gridCol) return; + + const fieldName = gridCol.fieldName || gridCol.name || col.fieldName || col.name; + const direction = sortDir[fieldName] || sortDir[col.name] || "none"; + + // DOM에서 헤더 요소 찾기 + const gridEl1 = document.querySelector('[data-grid-id]') as HTMLElement | null; + const gridEl2 = document.querySelector('.rg-grid') as HTMLElement | null; + const gridEl = gridEl1 || gridEl2; + if (!gridEl) return; + + // 헤더 셀 찾기 (fieldName 또는 name으로) + const headerCells = gridEl.querySelectorAll('.rg-header-cell, [class*="header"]'); + let targetCell: HTMLElement | null = null; + + headerCells.forEach((cell) => { + const el = cell as HTMLElement; + const text = el.textContent?.trim(); + const colName = col.name; + const colFieldName = col.fieldName; + if (text && (text.includes(colName) || (colFieldName && text.includes(colFieldName)))) { + targetCell = el; + } + }); + + if (!targetCell) return; + + const headerCell = targetCell as HTMLElement; + + // 기존 화살표 제거 + const existingIndicator = headerCell.querySelector(".rg-sort-indicator") as HTMLElement | null; + if (existingIndicator) { + existingIndicator.remove(); + } + + // 화살표 추가 + if (direction === "asc" || direction === "desc") { + const indicator = document.createElement("span"); + indicator.className = "rg-sort-indicator"; + indicator.textContent = direction === "asc" ? "▲" : "▼"; + indicator.style.marginLeft = "4px"; + indicator.style.fontSize = "10px"; + indicator.style.color = "#666"; + indicator.style.display = "inline-block"; + indicator.style.verticalAlign = "middle"; + headerCell.appendChild(indicator); + } + }); + } catch (e) { + console.warn("updateHeaderSortIndicator error:", e); + } +} + +/** onSorting: mergeCells: true인 컬럼이 있으면 그룹 내 순서 유지 정렬, 없으면 RealGrid 기본 정렬 */ +function createOnSortingHandler( + columns: { fieldName?: string; name: string; mergeCells?: boolean; sortdisable?: boolean; valueType?: ValueType }[], + setRowData: React.Dispatch[]>>, + sortDirRef: { current: Record }, + originalRowsRef: { current: Record[]> } +) { + const fieldNames = [...new Set(columns.map((c) => c.fieldName).filter((x): x is string => typeof x === "string"))]; + // mergeCells: true인 컬럼 찾기 (시군구 등) - 그룹 필드로 사용 + // 시군구를 우선으로 찾고, 없으면 첫 번째 mergeCells 컬럼 사용 + const mergeCellCols = columns.filter((c) => c.mergeCells === true && c.fieldName); + const mergeCellCol = mergeCellCols.find((c) => c.fieldName === 'gugun') || mergeCellCols[0]; + const groupField = mergeCellCol?.fieldName || null; + const orderByField = 'rgnNm'; // 고정값 또는 설정 가능하게 할 수 있음 + const hasMergeCells = mergeCellCols.length > 0; + + return function onSorting(g: unknown, fields: unknown[], _directions: unknown[]) { + const grid = g as { getColumn?: (i: number) => { fieldName?: string; name?: string } | undefined; getDataSource?: () => { getRowCount(): number; getValue(r: number, f: string): unknown } }; + const idx = fields?.[0]; + if (idx === undefined) return true; + let sortField: string | null = null; + if (typeof idx === "string") sortField = idx; + else { + const colObj = grid?.getColumn?.(typeof idx === "number" ? idx : 0); + sortField = (colObj?.fieldName ?? colObj?.name) ?? null; + } + if (!sortField) return true; + const col = columns.find((c) => (c.fieldName === sortField || c.name === sortField)); + if (!col || col.sortdisable) return true; + + // mergeCells: true인 컬럼이 없으면 RealGrid 기본 정렬 사용 + if (!hasMergeCells || !groupField) return true; + + const ds = grid.getDataSource?.(); + if (!ds) return true; + const cnt = ds.getRowCount(); + const rows: Record[] = []; + for (let i = 0; i < cnt; i++) { + const r: Record = {}; + for (const f of fieldNames) r[f] = ds.getValue(i, f) as string | number; + rows.push(r); + } + const prev = sortDirRef.current[sortField] ?? "none"; + const setRows = (ds as { setRows?: (r: unknown[]) => void }).setRows?.bind(ds); + + if (prev === "desc") { + const orig = originalRowsRef.current[sortField]; + if (orig && setRows) { + setRows(orig); + setRowData(orig); + } + sortDirRef.current[sortField] = "none"; + return false; + } + if (prev === "none") { + originalRowsRef.current[sortField] = rows.map((r) => ({ ...r })); + } + const sortAsc = prev !== "asc"; + sortDirRef.current[sortField] = sortAsc ? "asc" : "desc"; + const sorted = sortRowsGroupPreserving(rows, sortField, sortAsc, { groupField, orderByField }, col.valueType); + setRows?.(sorted); + setRowData(sorted); + return false; + }; +} + +/** sortPreserveGroupOrder: 그룹 간에는 sortField로 정렬, 그룹 내에서는 원본 순서 유지. */ +function sortRowsGroupPreserving( + rows: Record[], + sortField: string, + sortAsc: boolean, + opt: { groupField: string; orderByField?: string }, + valueType?: ValueType +): Record[] { + const groups = new Map[]>(); + for (const r of rows) { + const k = String(r[opt.groupField] ?? ""); + if (!groups.has(k)) groups.set(k, []); + groups.get(k)!.push(r); + } + + // valueType에 따라 비교 함수 선택 + const compareValue = (val: unknown): number | string => { + if (valueType === ValueType.NUMBER) { + return toNum(val); + } + // 텍스트나 기타 타입은 문자열로 비교 + return String(val ?? ""); + }; + + const rep = (arr: Record[]): number | string => { + const values = arr.map((r) => compareValue(r[sortField])); + if (valueType === ValueType.NUMBER) { + const nums = values.filter((v): v is number => typeof v === "number" && !isNaN(v)); + if (nums.length === 0) return sortAsc ? Infinity : -Infinity; + return sortAsc ? Math.min(...nums) : Math.max(...nums); + } else { + // 문자열 정렬 + const strs = values.filter((v): v is string => typeof v === "string"); + if (strs.length === 0) return sortAsc ? "\uffff" : ""; + strs.sort(); + return sortAsc ? (strs[0] as string) : (strs[strs.length - 1] as string); + } + }; + + const entries = [...groups.entries()].sort((a, b) => { + const valA = rep(a[1]); + const valB = rep(b[1]); + if (typeof valA === "number" && typeof valB === "number") { + return (sortAsc ? 1 : -1) * (valA - valB); + } + // 문자열 비교 + const strA = String(valA); + const strB = String(valB); + return (sortAsc ? 1 : -1) * strA.localeCompare(strB); + }); + + return entries.flatMap(([, arr]) => arr); +} + +/** columnInfo → RealGrid DataField 배열 (computed → valueCallback 포함, setFields용) */ +function buildFieldsFromColumns(columns: { fieldName: string; valueType?: ValueType; computed?: import("../type/DataGrid.type").ComputedFieldConfig }[]) { + return columns.map((col) => { + const field: { fieldName: string; dataType: string; valueCallback?: (prod: unknown, dataRow: number, fieldName: string, fieldNames: string[], values: unknown[]) => number } = { + fieldName: col.fieldName, + dataType: col.valueType === ValueType.NUMBER ? "number" : "text", + }; + if (col.computed) { + const hasFields = "fields" in col.computed && col.computed.fields.length > 0; + const hasNumeratorDenominator = "numeratorFields" in col.computed && col.computed.numeratorFields.length > 0 && col.computed.denominatorFields.length > 0; + if (hasFields || hasNumeratorDenominator) { + field.valueCallback = getComputedValueCallback(col.computed) as (prod: unknown, dataRow: number, fieldName: string, fieldNames: string[], values: unknown[]) => number; + } + } + return field; + }); +} + +const DataGrid = ({ columnInfo, rowStartNum, isExcelImport = true, isExcelExport = true, fixedColumns, showHeaderCheckbox = false, rowData, setRowData }: DataGridProps) => { const { columns, columnLayout } = columnInfo; const gridRef = useRef(null); const fileInputRef = useRef(null); + const sortDirRef = useRef>({}); + const originalRowsRef = useRef[]>>({}); const { setMessage: alert } = useAlertStore((state) => state); const activeTabId = useTabStore((state) => state.activeTabId); const menuList = useTabStore((state) => state.menuList); - const [rowData, setRowData] = useState[]>([]); - const exportExcel = () => { if (rowData.length === 0) { alert('데이터가 없습니다.'); @@ -50,12 +315,94 @@ const DataGrid = ({ columnInfo, rows = [], rowStartNum, isExcelImport = true, fi .find((s) => s.menuEngNm === activeTabId) ?.menuKornNm || 'export'; + // 엑셀 import 시 매칭을 위해 헤더에 fieldName 포함 (예: "합계 (swgeProcZoneInnerSm)") + const originalHeaders: Map = new Map(); + columns.forEach((col) => { + if (col.fieldName && gridView.columnByName) { + const gridCol = gridView.columnByName(col.name); + if (gridCol && gridCol.header) { + const originalText = gridCol.header.text || ''; + originalHeaders.set(col.name, originalText); + const headerWithFieldName = `${originalText} (${col.fieldName})`; + gridCol.header.text = headerWithFieldName; + } + } + }); + + // exportGrid 호출 전에 columnLayout 정규화 (col 속성 추가) + const normalizeLayoutForExport = (layout: ColumnLayoutItem[]): ColumnLayoutItem[] => { + return layout.map(item => { + if (typeof item === 'string') { + return item; + } + if (item && typeof item === 'object') { + // { column: string, footerUserSpans?: ... } 형태인 경우 + if ('column' in item && typeof item.column === 'string') { + const colItem = item as { column: string; footerUserSpans?: unknown[] }; + // col 속성 추가 (RealGrid가 exportGrid에서 필요로 함) + return { + ...colItem, + col: gridView.columnByName?.(colItem.column) || null, + } as ColumnLayoutItem; + } + // { name: string, items: ... } 형태인 경우 + if ('items' in item && Array.isArray(item.items)) { + return { + ...item, + items: normalizeLayoutForExport(item.items as ColumnLayoutItem[]), + }; + } + } + return item; + }); + }; + + const originalLayout = columnLayout; + let normalizedLayout: ColumnLayoutItem[] | undefined; + let layoutRestored = false; + + // footerUserSpans가 있는 경우에만 정규화 + const checkFooterUserSpans = (layout: ColumnLayoutItem[]): boolean => { + return layout.some(item => { + if (typeof item === 'string') return false; + if (item && typeof item === 'object' && 'footerUserSpans' in item) return true; + if (item && typeof item === 'object' && 'items' in item && Array.isArray(item.items)) { + return checkFooterUserSpans(item.items as ColumnLayoutItem[]); + } + return false; + }); + }; + + if (checkFooterUserSpans(columnLayout)) { + normalizedLayout = normalizeLayoutForExport(columnLayout); + // 정규화된 layout을 임시로 설정 + gridView.setColumnLayout?.(normalizedLayout); + } + gridView.exportGrid({ type: 'excel', target: 'local', fileName: `${menuName}.xlsx`, footer: 'hidden', done: () => { + // 헤더 원복 + columns.forEach((col) => { + if (col.fieldName && gridView.columnByName) { + const gridCol = gridView.columnByName(col.name); + if (gridCol && gridCol.header && originalHeaders.has(col.name)) { + gridCol.header.text = originalHeaders.get(col.name) || ''; + } + } + }); + // columnLayout 원복 (footerUserSpans 유지) + if (normalizedLayout && !layoutRestored) { + layoutRestored = true; + setTimeout(() => { + if (gridRef.current?.gridView && originalLayout) { + gridRef.current.gridView.setColumnLayout?.(originalLayout); + } + }, 100); + } alert('내보내기가 완료되었습니다.'); }, }); @@ -125,25 +472,64 @@ const DataGrid = ({ columnInfo, rows = [], rowStartNum, isExcelImport = true, fi const maxHeaderRowIndex = headerRowIndexes.length > 0 ? Math.max(...headerRowIndexes) : headerStartRow; const dataStartRow = maxHeaderRowIndex + 1; + // 엑셀 컬럼 인덱스 → 그리드 fieldName 매핑 + const headerToFieldNameMap = new Map(); + headers.forEach((headerText, colIndex) => { + let fieldName: string | null = null; + + // 헤더에 fieldName이 포함된 경우 (예: "합계 (swgeProcZoneInnerSm)") + const fieldNameMatch = headerText.match(/\(([a-zA-Z_][a-zA-Z0-9_]*)\)$/); + if (fieldNameMatch && fieldNameMatch[1]) { + const extractedFieldName = fieldNameMatch[1]; + if (columns.find(c => c.fieldName === extractedFieldName)) { + fieldName = extractedFieldName; + } + } + + // 매칭 실패 시 원본 헤더 텍스트 사용 + if (!fieldName) { + console.warn(`엑셀 컬럼 ${colIndex + 1} (헤더: "${headerText}") 매칭 실패 → 헤더 텍스트를 fieldName으로 사용`); + fieldName = headerText; + } + + headerToFieldNameMap.set(headerText, fieldName); + }); + // 데이터 행 읽기 - const dataRows: Record[] = []; + const dataRows: Record[] = []; for (let row = dataStartRow; row <= range.e.r; row++) { - const rowData: Record = {}; + 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) - : ''; + // 헤더 텍스트를 fieldName으로 변환 + const fieldName = headerToFieldNameMap.get(headerName) || headerName; - if (cellValue !== '') { + let cellValue: string | number = ''; + if (cell && cell.v !== undefined && cell.v !== null) { + // 엑셀 셀 타입이 숫자이거나 값이 숫자인 경우 숫자로 변환 + if (cell.t === 'n' || (typeof cell.v === 'number' && !isNaN(cell.v))) { + cellValue = cell.v as number; + } else { + const stringValue = String(cell.v); + // 문자열이지만 숫자로 변환 가능한 경우 숫자로 변환 + const numericValue = Number(stringValue.replace(/,/g, '')); + if (!isNaN(numericValue) && stringValue.trim() !== '') { + cellValue = numericValue; + } else { + cellValue = stringValue; + } + } + } + + if (cellValue !== '' && cellValue !== null && cellValue !== undefined) { hasData = true; } - rowData[headerName] = cellValue; + rowData[fieldName] = cellValue; } // 데이터가 하나라도 있는 행만 추가 @@ -151,7 +537,6 @@ const DataGrid = ({ columnInfo, rows = [], rowStartNum, isExcelImport = true, fi dataRows.push(rowData); } } - setRowData(dataRows); } catch (error) { console.error('엑셀 파일 파싱 오류:', error); @@ -181,14 +566,66 @@ const DataGrid = ({ columnInfo, rows = [], rowStartNum, isExcelImport = true, fi }; useEffect(() => { - if (!gridRef.current?.gridView) return; + if (!gridRef.current?.gridView || !gridRef.current?.dataProvider) return; const gridView = gridRef.current.gridView; + const dataProvider = gridRef.current.dataProvider; + + // mergeCells: true인 컬럼이 있으면 onSorting에서 클라이언트 정렬로 처리 + const hasMergeCells = columns.some((col) => col.mergeCells === true); + + // setDataComparer로 숫자 정렬 (mergeCells가 있으면 onSorting에서 클라이언트 정렬로 처리) + const dp = dataProvider as RealGridDataProviderComparer; + if (typeof dp.setDataComparer === "function" && typeof dp.getValue === "function") { + const numberComparer = createNumberComparer(dp); + const seen = new Set(); + columns.forEach((col) => { + if (hasMergeCells) return; // mergeCells가 있으면 모든 컬럼을 클라이언트 정렬로 처리 + if (col.valueType === ValueType.NUMBER && col.fieldName && !seen.has(col.fieldName)) { + seen.add(col.fieldName); + try { + dp.setDataComparer!(col.fieldName, numberComparer); + } catch (e) { + console.warn("setDataComparer:", e); + } + } + }); + } + + // mergeCells: true인 컬럼이 있으면 onSorting에서 그룹 내 순서 유지 정렬 적용 + (gridView as { onSorting?: (g: unknown, f: unknown[], d: unknown[]) => boolean }).onSorting = createOnSortingHandler(columns, setRowData, sortDirRef, originalRowsRef); (gridView.displayOptions as DisplayOptions).fitStyle = GridFitStyle.EVEN; gridView.header.height = 60; - if (rows.length > 0) { - setRowData(rows); + + // 헤더 체크박스 표시 여부 설정 + gridView.setOptions({ + checkBar: { visible: showHeaderCheckbox }, + rowIndicator: { visible: false }, + }); + + // footer가 있는 컬럼이 있으면 footer 높이 설정 + const hasFooter = columns.some(col => col.footer); + if (hasFooter) { + // footer가 배열이거나 multiRow가 true인 footer가 있으면 멀티푸터 설정 (여러 행) + const hasArrayFooter = columns.some(col => col.footer && Array.isArray(col.footer)); + const hasMultiRowFooter = columns.some(col => { + const footer = col.footer; + return footer && !Array.isArray(footer) && 'multiRow' in footer && footer.multiRow === true; + }); + if (hasArrayFooter || hasMultiRowFooter) { + // footer 배열의 길이를 확인하여 행 수 결정 + const arrayFooterLengths = columns + .filter(col => col.footer && Array.isArray(col.footer)) + .map(col => (col.footer as unknown[]).length); + const maxFooterLength = arrayFooterLengths.length > 0 + ? Math.max(...arrayFooterLengths) + : 2; // 기본값 2 + const footerHeights = Array.from({ length: maxFooterLength }, () => ({ height: 40 })); + gridView.setFooters?.(footerHeights); + } else { + gridView.footer.height = 40; + } } // 고정 컬럼 설정 @@ -197,7 +634,122 @@ const DataGrid = ({ columnInfo, rows = [], rowStartNum, isExcelImport = true, fi colCount: fixedColumns, }); } - }, [rows, columns, columnLayout, fixedColumns]); + + gridView.setOptions({ + summaryMode: "aggregate" + }); + + // footerUserSpans가 있는 경우 setColumnLayout으로 설정 + const checkFooterUserSpans = (layout: unknown[]): boolean => { + return layout.some(item => { + if (typeof item === 'string') return false; + if (item && typeof item === 'object' && 'footerUserSpans' in item) return true; + if (item && typeof item === 'object' && 'items' in item && Array.isArray(item.items)) { + return checkFooterUserSpans(item.items); + } + return false; + }); + }; + + if (checkFooterUserSpans(columnLayout)) { + // setColumnLayout은 그리드가 완전히 렌더링된 후 호출해야 함 + setTimeout(() => { + if (gridRef.current?.gridView && columnLayout) { + gridRef.current.gridView.setColumnLayout?.(columnLayout); + } + }, 100); + } + + // footer 설정 (배열 또는 단일 객체 모두 처리) + // setFooters 호출 후 footer 배열 설정 + setTimeout(() => { + const currentGridView = gridRef.current?.gridView; + if (!currentGridView || !currentGridView.columnByName) { + console.warn('GridView not available for footer setup'); + return; + } + + // footer가 배열인 컬럼이 있으면 setColumns를 다시 호출하여 footer 설정 + const hasArrayFooter = columns.some(col => col.footer && Array.isArray(col.footer)); + if (hasArrayFooter) { + // 모든 컬럼 정보를 다시 가져와서 footer 배열을 포함하여 setColumns 호출 + const columnsWithFooter: RealGridColumnInfo[] = columns.map(col => { + const colInfo: RealGridColumnInfo = { + name: col.name, + fieldName: col.fieldName, + header: col.header, + editable: col.editable, + valueType: col.valueType, + dataFormat: col.dataFormat, + ...(col.styleName != null && { styleName: col.styleName }), + ...(col.mergeCells && { mergeRule: { criteria: 'value' as const } }), + }; + + if (col.footer) { + if (Array.isArray(col.footer)) { + colInfo.footer = col.footer; + } else { + colInfo.footer = { + text: col.footer.text, + expression: col.footer.expression, + numberFormat: col.footer.numberFormat, + }; + } + } + + return colInfo; + }); + + try { + (currentGridView as GridViewWithSetColumns).setColumns?.(columnsWithFooter); + + // setColumns 호출 후 footer 배열의 valueCallback을 다시 설정 + setTimeout(() => { + columns.forEach(col => { + if (!col.footer || !Array.isArray(col.footer)) return; + + try { + const column = currentGridView.columnByName(col.name); + if (!column) return; + + // footer 배열의 각 요소에 valueCallback 설정 + col.footer.forEach((footerItem, index) => { + if (footerItem.valueCallback && column.footer) { + // footer가 배열인 경우 + if (Array.isArray(column.footer) && column.footer[index]) { + (column.footer[index] as FooterItemWithCallback).valueCallback = footerItem.valueCallback; + } + } + }); + } catch (e) { + console.warn(`Error setting footer array valueCallback for column ${col.name}:`, e); + } + }); + }, 50); + } catch (e) { + console.warn('Failed to set columns with footer array:', e); + } + } + + // footer가 단일 객체이고 valueCallback이 있는 경우 + columns.forEach(col => { + if (!col.footer || Array.isArray(col.footer)) return; + + try { + const column = currentGridView.columnByName(col.name); + if (!column || !column.footer) return; + + if (col.footer.valueCallback) { + (column.footer as { valueCallback?: typeof col.footer.valueCallback }).valueCallback = col.footer.valueCallback; + } + } catch (e) { + console.warn(`Error setting footer valueCallback for column ${col.name}:`, e); + } + }); + }, 200); + + }, [columns, columnLayout, fixedColumns, rowData, showHeaderCheckbox]); + return (
@@ -208,32 +760,60 @@ const DataGrid = ({ columnInfo, rows = [], rowStartNum, isExcelImport = true, fi style={{ display: 'none' }} onChange={handleFileChange} /> -
+
{isExcelImport && - + } + {isExcelExport && + } -
- + { + (grid as { onSorting?: (g: unknown, f: unknown[], d: unknown[]) => boolean }).onSorting = createOnSortingHandler(columns, setRowData, sortDirRef, originalRowsRef); + const dp = gridRef.current?.dataProvider as { setFields?: (fields: unknown[]) => void; setRows?: (rows: Record[]) => void } | undefined; + if (dp?.setFields) { + dp.setFields(buildFieldsFromColumns(columns)); + } + // setFields 호출 직후 행 데이터 적용 (realgrid-react의 setRows 타이밍 이슈 방지) + if (dp?.setRows && rowData?.length) { + dp.setRows(rowData); + } + }} + > {columns.map((column) => ( ))} diff --git a/apps/st/src/components/module/SearchForm.tsx b/apps/st/src/components/module/SearchForm.tsx index b1fda5a07..91d823ed6 100644 --- a/apps/st/src/components/module/SearchForm.tsx +++ b/apps/st/src/components/module/SearchForm.tsx @@ -1,6 +1,6 @@ "use client"; -import { useState } from "react"; +import { useState, useEffect } from "react"; import useSWR from 'swr'; import { @@ -18,9 +18,9 @@ from "@common/components"; import { useAlertStore } from "@/shared/lib"; import { type SearchFormProps, type SearchFormValues, defaultSearchFormProps } from "../type/SearchForm.type"; import { statisticsAPi } from "@/entities/statistics/api/statistics-service"; -import type { ApiResponse, Sido, Sgg } from "@/entities/statistics/model/statistics-interface"; +import type { ApiResponse, SearchCondition, Sgg } from "@/entities/statistics/model/statistics-interface"; -const getSidoListFetcher = async (): Promise> => { +const getSidoListFetcher = async (): Promise> => { return statisticsAPi.getSidoList(); }; @@ -31,15 +31,11 @@ const getSggListFetcher = async (key: [string, string]): Promise { const { setMessage: alert } = useAlertStore((state) => state); const mergedProps = { ...defaultSearchFormProps, ...props }; - const [values, setValues] = useState({ - sido: props.defaultValues?.sido || "", - sgg: props.defaultValues?.sgg || "", - year: props.defaultValues?.year || "", - order: props.defaultValues?.order || "", - fctName: props.defaultValues?.fctName || "", - }); - const { data: sidoList } = useSWR>( + const currentYear = new Date().getFullYear(); + const defaultYear = currentYear - 1; + + const { data: sidoList } = useSWR>( "sido-list", getSidoListFetcher, { @@ -61,6 +57,15 @@ const SearchForm = (props: SearchFormProps = {}) => { } ); + const [values, setValues] = useState({ + sido: props.defaultValues?.sido || '', + gugun: props.defaultValues?.gugun || '', + year: props.defaultValues?.year || defaultYear.toString(), + order: props.defaultValues?.order || '', + fctName: props.defaultValues?.fctName || '', + bsnmCd: props.defaultValues?.bsnmCd || '', + }); + const { data: sggList } = useSWR>( values.sido && values.sido !== "ALL" ? ["sgg-list", values.sido] : null, getSggListFetcher, @@ -83,6 +88,7 @@ const SearchForm = (props: SearchFormProps = {}) => { } ); + const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); props.onSubmit?.(values); @@ -90,17 +96,44 @@ const SearchForm = (props: SearchFormProps = {}) => { const handleReset = () => { const resetValues: SearchFormValues = { - sido: "", - sgg: "", - year: "", - order: "", - fctName: "", + sido: '', + gugun: '', + year: String(defaultYear), + order: '', + fctName: '', + bsnmCd: '', }; setValues(resetValues); props.onReset?.(); - props.onSubmit?.(resetValues); }; + const handleSidoChange = (value: string) => { + const selectedBsnm = sidoList?.data.sidoList.find((v) => v.sido === value)?.bsnmCd || ''; + + setValues((prev) => ({ + ...prev, + sido: value, + gugun: '', + bsnmCd: selectedBsnm, + })); + }; + + const handleSggChange = (value: string) => { + const selectedBsnm = value === 'ALL' + ? (sidoList?.data.sidoList.find((v) => v.sido === values.sido)?.bsnmCd || '') + : (sggList?.data?.find((v) => v.gugun === value)?.bsnmCd || ''); + + setValues((prev) => ({ + ...prev, + gugun: value, + bsnmCd: selectedBsnm, + })); + }; + + useEffect(() => { + props.onValuesChange?.(values); + }, [values]); + return (
@@ -110,13 +143,13 @@ const SearchForm = (props: SearchFormProps = {}) => { - - + - 전체 - {sidoList?.data?.map((sido) => ( + 선택 + {sidoList?.data?.sidoList?.map((sido) => ( {sido.sido} @@ -125,12 +158,12 @@ const SearchForm = (props: SearchFormProps = {}) => {
} - {mergedProps.sgg && + {mergedProps.gugun &&
- @@ -152,10 +185,14 @@ const SearchForm = (props: SearchFormProps = {}) => {
@@ -176,10 +213,10 @@ const SearchForm = (props: SearchFormProps = {}) => {
}
- -
@@ -202,7 +239,7 @@ const SearchForm = (props: SearchFormProps = {}) => { } {mergedProps.userMenual &&
- diff --git a/apps/st/src/components/module/SplitDataGrid.tsx b/apps/st/src/components/module/SplitDataGrid.tsx new file mode 100644 index 000000000..7d24c96c0 --- /dev/null +++ b/apps/st/src/components/module/SplitDataGrid.tsx @@ -0,0 +1,35 @@ +import CompactDataGrid from "./CompactDataGrid"; +import type { SplitDataGridProps } from "../type/DataGrid.type"; + +const SplitDataGrid = ({ + leftColumnInfo, + rightColumnInfo, + leftFixedColumns, + rightFixedColumns, + showHeaderCheckbox = false, + leftRowData, + rightRowData, + setLeftRowData, + setRightRowData, +}: SplitDataGridProps) => { + return ( +
+ + +
+ ); +}; + +export default SplitDataGrid; diff --git a/apps/st/src/components/module/TabDataGrid.tsx b/apps/st/src/components/module/TabDataGrid.tsx new file mode 100644 index 000000000..532a342bd --- /dev/null +++ b/apps/st/src/components/module/TabDataGrid.tsx @@ -0,0 +1,1066 @@ +import { useRef, useEffect, useState } from "react" +import { ValueType } from "realgrid" + +import RealGrid, { GridFitStyle } from "realgrid" +import { RealGridReact, RGDataColumn } from "realgrid-react" +import type { TabDataGridProps, ColumnInfo, RealGridColumnInfo, GridViewWithSetColumns, FooterItemWithCallback, ColumnLayoutItem } from "../type/DataGrid.type" +import { getComputedValueCallback } from "@/components/util/grid/computedFieldCallbacks" +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"; +import SplitDataGrid from "./SplitDataGrid"; + +// RealGrid 타입 확장 +declare module "realgrid" { + interface GridView { + setOptions(options: { + footer?: { visible?: boolean }; + summaryMode?: 'aggregate' | 'statistical' | 'none'; + checkBar?: { visible?: boolean }; + }): void; + } +} + +interface RealGridDataProviderFill { + fillJsonData?(data: Record[], options?: { fillMode?: string }): void; +} + +/** setDataComparer(field, comparer) - setFields 없이 숫자 정렬용. comparer(field, row1, row2) => -1|0|1 */ +interface RealGridDataProviderComparer { + setDataComparer?(field: string | number, comparer: (field: string | number, row1: number, row2: number) => number): void; + getValue?(row: number, field: string | number): unknown; +} + +// 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 || ''); + +/** 정렬 필드 값을 숫자로 파싱 (쉼표 제거) */ +function toNum(v: unknown): number { + if (v == null) return NaN; + if (typeof v === "number") return isNaN(v) ? NaN : v; + return parseFloat(String(v).replace(/,/g, "")) || NaN; +} + +/** 헤더에 정렬 화살표 표시 (CSS 방식) */ +function updateHeaderSortIndicator( + gridView: { + columnByIndex?: (i: number) => { fieldName?: string; name?: string } | undefined; + columnByName?: (name: string) => { fieldName?: string; name?: string } | undefined; + getColumn?: (i: number) => { fieldName?: string; name?: string } | undefined; + getElement?: () => HTMLElement | null; + container?: HTMLElement; + }, + columns: { fieldName?: string; name: string }[], + sortDir: Record, + gridElement?: HTMLElement | null +) { + try { + // 그리드 DOM 요소 찾기 + let gridEl: HTMLElement | null = null; + if (gridElement) { + gridEl = gridElement; + } else if (gridView.getElement) { + gridEl = gridView.getElement(); + } else if (gridView.container) { + gridEl = gridView.container; + } else { + // 폴백: 모든 그리드 중에서 찾기 + const allGrids = document.querySelectorAll('.rg-grid, [data-grid-id]'); + if (allGrids.length > 0) { + // 활성 탭의 그리드를 찾기 위해 TabsContent 내부의 그리드 찾기 + const activeTabContent = document.querySelector('[data-state="active"]')?.closest('[role="tabpanel"]'); + if (activeTabContent) { + gridEl = activeTabContent.querySelector('.rg-grid, [data-grid-id]') as HTMLElement; + } + if (!gridEl && allGrids.length === 1) { + gridEl = allGrids[0] as HTMLElement; + } + } + } + + if (!gridEl) return; + + columns.forEach((col) => { + if (!col.fieldName && !col.name) return; + + const fieldName = col.fieldName || col.name; + const direction = sortDir[fieldName] || sortDir[col.name] || "none"; + if (direction === "none") return; + + // 헤더 셀 찾기 (fieldName 또는 name으로) + const headerCells = gridEl.querySelectorAll('.rg-header-cell, [class*="header"], .rg-header'); + let targetCell: HTMLElement | null = null; + + headerCells.forEach((cell) => { + const el = cell as HTMLElement; + const text = el.textContent?.trim(); + const colName = col.name; + const colFieldName = col.fieldName; + // 헤더 텍스트와 컬럼 이름이 일치하는지 확인 + if (text && ( + text === colName || + text === colFieldName || + text.includes(colName) || + (colFieldName && text.includes(colFieldName)) + )) { + targetCell = el; + } + }); + + if (!targetCell) return; + + const headerCell = targetCell as HTMLElement; + + // 기존 화살표 제거 + const existingIndicator = headerCell.querySelector(".rg-sort-indicator") as HTMLElement | null; + if (existingIndicator) { + existingIndicator.remove(); + } + + // 화살표 추가 + const indicator = document.createElement("span"); + indicator.className = "rg-sort-indicator"; + indicator.textContent = direction === "asc" ? "▲" : "▼"; + indicator.style.marginLeft = "4px"; + indicator.style.fontSize = "10px"; + indicator.style.color = "#666"; + indicator.style.display = "inline-block"; + indicator.style.verticalAlign = "middle"; + headerCell.appendChild(indicator); + }); + } catch (e) { + console.warn("updateHeaderSortIndicator error:", e); + } +} + +/** onSorting: mergeCells: true인 컬럼이 있으면 그룹 내 순서 유지 정렬, 없으면 RealGrid 기본 정렬 */ +function createOnSortingHandler( + columns: { fieldName?: string; name: string; mergeCells?: boolean; sortdisable?: boolean; valueType?: ValueType }[], + setRowData: React.Dispatch[]>>, + sortDirRef: { current: Record }, + originalRowsRef: { current: Record[]> }, + findGridElement?: () => HTMLElement | null +) { + const fieldNames = [...new Set(columns.map((c) => c.fieldName).filter((x): x is string => typeof x === "string"))]; + // mergeCells: true인 컬럼 찾기 (시군구 등) - 그룹 필드로 사용 + // 시군구를 우선으로 찾고, 없으면 첫 번째 mergeCells 컬럼 사용 + const mergeCellCols = columns.filter((c) => c.mergeCells === true && c.fieldName); + const mergeCellCol = mergeCellCols.find((c) => c.fieldName === 'gugun') || mergeCellCols[0]; + const groupField = mergeCellCol?.fieldName || null; + const orderByField = 'rgnNm'; // 고정값 또는 설정 가능하게 할 수 있음 + const hasMergeCells = mergeCellCols.length > 0; + + return function onSorting(g: unknown, fields: unknown[], _directions: unknown[]) { + const grid = g as { getColumn?: (i: number) => { fieldName?: string; name?: string } | undefined; getDataSource?: () => { getRowCount(): number; getValue(r: number, f: string): unknown } }; + const idx = fields?.[0]; + if (idx === undefined) return true; + let sortField: string | null = null; + if (typeof idx === "string") sortField = idx; + else { + const colObj = grid?.getColumn?.(typeof idx === "number" ? idx : 0); + sortField = (colObj?.fieldName ?? colObj?.name) ?? null; + } + if (!sortField) return true; + const col = columns.find((c) => (c.fieldName === sortField || c.name === sortField)); + if (!col || col.sortdisable) return true; + + // mergeCells: true인 컬럼이 없으면 RealGrid 기본 정렬 사용 + if (!hasMergeCells || !groupField) return true; + + const ds = grid.getDataSource?.(); + if (!ds) return true; + const cnt = ds.getRowCount(); + const rows: Record[] = []; + for (let i = 0; i < cnt; i++) { + const r: Record = {}; + for (const f of fieldNames) r[f] = ds.getValue(i, f) as string | number; + rows.push(r); + } + const prev = sortDirRef.current[sortField] ?? "none"; + const setRows = (ds as { setRows?: (r: unknown[]) => void }).setRows?.bind(ds); + + if (prev === "desc") { + const orig = originalRowsRef.current[sortField]; + if (orig && setRows) { + setRows(orig); + setRowData(orig); + } + sortDirRef.current[sortField] = "none"; + return false; + } + if (prev === "none") { + originalRowsRef.current[sortField] = rows.map((r) => ({ ...r })); + } + const sortAsc = prev !== "asc"; + sortDirRef.current[sortField] = sortAsc ? "asc" : "desc"; + const sorted = sortRowsGroupPreserving(rows, sortField, sortAsc, { groupField, orderByField }, col.valueType); + setRows?.(sorted); + setRowData(sorted); + return false; + }; +} + +/** sortPreserveGroupOrder: 그룹 간에는 sortField로 정렬, 그룹 내에서는 원본 순서 유지. */ +function sortRowsGroupPreserving( + rows: Record[], + sortField: string, + sortAsc: boolean, + opt: { groupField: string; orderByField?: string }, + valueType?: ValueType +): Record[] { + const groups = new Map[]>(); + for (const r of rows) { + const k = String(r[opt.groupField] ?? ""); + if (!groups.has(k)) groups.set(k, []); + groups.get(k)!.push(r); + } + + // valueType에 따라 비교 함수 선택 + const compareValue = (val: unknown): number | string => { + if (valueType === ValueType.NUMBER) { + return toNum(val); + } + // 텍스트나 기타 타입은 문자열로 비교 + return String(val ?? ""); + }; + + const rep = (arr: Record[]): number | string => { + const values = arr.map((r) => compareValue(r[sortField])); + if (valueType === ValueType.NUMBER) { + const nums = values.filter((v): v is number => typeof v === "number" && !isNaN(v)); + if (nums.length === 0) return sortAsc ? Infinity : -Infinity; + return sortAsc ? Math.min(...nums) : Math.max(...nums); + } else { + // 문자열 정렬 + const strs = values.filter((v): v is string => typeof v === "string"); + if (strs.length === 0) return sortAsc ? "\uffff" : ""; + strs.sort(); + return sortAsc ? (strs[0] as string) : (strs[strs.length - 1] as string); + } + }; + + const entries = [...groups.entries()].sort((a, b) => { + const valA = rep(a[1]); + const valB = rep(b[1]); + if (typeof valA === "number" && typeof valB === "number") { + return (sortAsc ? 1 : -1) * (valA - valB); + } + // 문자열 비교 + const strA = String(valA); + const strB = String(valB); + return (sortAsc ? 1 : -1) * strA.localeCompare(strB); + }); + + return entries.flatMap(([, arr]) => arr); +} + +/** columnInfo → RealGrid DataField 배열 (computed → valueCallback 포함, setFields용) */ +function buildFieldsFromColumns(columns: { fieldName: string; valueType?: ValueType; computed?: import("../type/DataGrid.type").ComputedFieldConfig }[]) { + return columns.map((col) => { + const field: { fieldName: string; dataType: string; valueCallback?: (prod: unknown, dataRow: number, fieldName: string, fieldNames: string[], values: unknown[]) => number } = { + fieldName: col.fieldName, + dataType: col.valueType === ValueType.NUMBER ? "number" : "text", + }; + if (col.computed) { + const hasFields = "fields" in col.computed && col.computed.fields.length > 0; + const hasNumeratorDenominator = "numeratorFields" in col.computed && col.computed.numeratorFields.length > 0 && col.computed.denominatorFields.length > 0; + if (hasFields || hasNumeratorDenominator) { + field.valueCallback = getComputedValueCallback(col.computed) as (prod: unknown, dataRow: number, fieldName: string, fieldNames: string[], values: unknown[]) => number; + } + } + return field; + }); +} + +/** setDataComparer용 숫자 비교기. setFields 없이 숫자 정렬. */ +function createNumberComparer(dp: RealGridDataProviderComparer): (field: string | number, row1: number, row2: number) => number { + return (field, row1, row2) => { + const get = dp.getValue?.bind(dp); + if (!get) return 0; + const val1 = get(row1, field); + const val2 = get(row2, field); + if (val1 == null && val2 == null) return 0; + if (val1 == null) return 1; + if (val2 == null) return -1; + const num1 = typeof val1 === "number" ? val1 : parseFloat(String(val1)); + const num2 = typeof val2 === "number" ? val2 : parseFloat(String(val2)); + if (isNaN(num1) && isNaN(num2)) return 0; + if (isNaN(num1)) return 1; + if (isNaN(num2)) return -1; + return num1 > num2 ? 1 : num1 < num2 ? -1 : 0; + }; +} + +const TabDataGrid = ({ columnInfo, isExcelImport = true, isExcelExport = true, rows = [], showHeaderCheckbox = false, rowDataMap, setRowDataMap }: TabDataGridProps) => { + // 각 탭별로 그리드 ref와 데이터를 관리 + const gridRefs = useRef>(new Map()); + const fileInputRefs = useRef>(new Map()); + // 각 탭별로 정렬 상태 관리 + const sortDirRefs = useRef>>>(new Map()); + const originalRowsRefs = useRef[]>>>>(new Map()); + const { setMessage: alert } = useAlertStore((state) => state); + const activeTabId = useTabStore((state) => state.activeTabId); + const menuList = useTabStore((state) => state.menuList); + 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); + if (!columnInfoItem) return; + + const tabLabel = columnInfoItem.tabLabel || tabKey; + const columns = columnInfoItem.columns || []; + const columnLayout = columnInfoItem.columnLayout || []; + + // 엑셀 import 시 매칭을 위해 헤더에 fieldName 포함 (예: "합계 (swgeProcZoneInnerSm)") + const originalHeaders: Map = new Map(); + columns.forEach((col) => { + if (col.fieldName && gridView.columnByName) { + const gridCol = gridView.columnByName(col.name); + if (gridCol && gridCol.header) { + const originalText = gridCol.header.text || ''; + originalHeaders.set(col.name, originalText); + const headerWithFieldName = `${originalText} (${col.fieldName})`; + gridCol.header.text = headerWithFieldName; + } + } + }); + + // exportGrid 호출 전에 columnLayout 정규화 (col 속성 추가) + const normalizeLayoutForExport = (layout: ColumnLayoutItem[]): ColumnLayoutItem[] => { + return layout.map(item => { + if (typeof item === 'string') { + return item; + } + if (item && typeof item === 'object') { + // { column: string, footerUserSpans?: ... } 형태인 경우 + if ('column' in item && typeof item.column === 'string') { + const colItem = item as { column: string; footerUserSpans?: unknown[] }; + // col 속성 추가 (RealGrid가 exportGrid에서 필요로 함) + return { + ...colItem, + col: gridView.columnByName?.(colItem.column) || null, + } as ColumnLayoutItem; + } + // { name: string, items: ... } 형태인 경우 + if ('items' in item && Array.isArray(item.items)) { + return { + ...item, + items: normalizeLayoutForExport(item.items as ColumnLayoutItem[]), + }; + } + } + return item; + }); + }; + + const originalLayout = columnLayout; + let normalizedLayout: ColumnLayoutItem[] | undefined; + let layoutRestored = false; + + // footerUserSpans가 있는 경우에만 정규화 + const checkFooterUserSpans = (layout: ColumnLayoutItem[]): boolean => { + return layout.some(item => { + if (typeof item === 'string') return false; + if (item && typeof item === 'object' && 'footerUserSpans' in item) return true; + if (item && typeof item === 'object' && 'items' in item && Array.isArray(item.items)) { + return checkFooterUserSpans(item.items as ColumnLayoutItem[]); + } + return false; + }); + }; + + if (checkFooterUserSpans(columnLayout)) { + normalizedLayout = normalizeLayoutForExport(columnLayout); + // 정규화된 layout을 임시로 설정 + gridView.setColumnLayout?.(normalizedLayout); + } + + gridView.exportGrid({ + type: 'excel', + target: 'local', + fileName: `${menuName}_${tabLabel}.xlsx`, + footer: 'hidden', + done: () => { + // 헤더 원복 + columns.forEach((col) => { + if (col.fieldName && gridView.columnByName) { + const gridCol = gridView.columnByName(col.name); + if (gridCol && gridCol.header && originalHeaders.has(col.name)) { + gridCol.header.text = originalHeaders.get(col.name) || ''; + } + } + }); + // columnLayout 원복 (footerUserSpans 유지) + if (normalizedLayout && !layoutRestored) { + layoutRestored = true; + setTimeout(() => { + const currentGridRef = gridRefs.current.get(tabKey); + if (currentGridRef?.gridView && originalLayout) { + currentGridRef.gridView.setColumnLayout?.(originalLayout); + } + }, 100); + } + 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; + + // 엑셀 컬럼 인덱스 → 그리드 fieldName 매핑 + const headerToFieldNameMap = new Map(); + const columns = columnInfoItem.columns || []; + headers.forEach((headerText, colIndex) => { + let fieldName: string | null = null; + + // 헤더에 fieldName이 포함된 경우 (예: "합계 (swgeProcZoneInnerSm)") + const fieldNameMatch = headerText.match(/\(([a-zA-Z_][a-zA-Z0-9_]*)\)$/); + if (fieldNameMatch && fieldNameMatch[1]) { + const extractedFieldName = fieldNameMatch[1]; + if (columns.find(c => c.fieldName === extractedFieldName)) { + fieldName = extractedFieldName; + } + } + + // 매칭 실패 시 원본 헤더 텍스트 사용 + if (!fieldName) { + console.warn(`엑셀 컬럼 ${colIndex + 1} (헤더: "${headerText}") 매칭 실패 → 헤더 텍스트를 fieldName으로 사용`); + fieldName = headerText; + } + + headerToFieldNameMap.set(headerText, fieldName); + }); + + // 데이터 행 읽기 + 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}`; + // 헤더 텍스트를 fieldName으로 변환 + const fieldName = headerToFieldNameMap.get(headerName) || headerName; + + let cellValue: string | number = ''; + if (cell && cell.v !== undefined && cell.v !== null) { + // 엑셀 셀 타입이 숫자이거나 값이 숫자인 경우 숫자로 변환 + if (cell.t === 'n' || (typeof cell.v === 'number' && !isNaN(cell.v))) { + cellValue = cell.v as number; + } else { + const stringValue = String(cell.v); + // 문자열이지만 숫자로 변환 가능한 경우 숫자로 변환 + const numericValue = Number(stringValue.replace(/,/g, '')); + if (!isNaN(numericValue) && stringValue.trim() !== '') { + cellValue = numericValue; + } else { + cellValue = stringValue; + } + } + } + + if (cellValue !== '' && cellValue !== null && cellValue !== undefined) { + hasData = true; + } + + rowData[fieldName] = 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]); + + // rowDataMap이 변경될 때 그리드에 데이터 반영 (Excel import 등) + useEffect(() => { + columnInfo.forEach((info) => { + const tabKey = info.tabKey; + const gridRef = gridRefs.current.get(tabKey); + const currentData = rowDataMap.get(tabKey); + + if (gridRef?.gridView && gridRef?.dataProvider && currentData && currentData.length > 0) { + setTimeout(() => { + try { + const dp = gridRef.dataProvider as RealGridDataProviderFill | null; + if (dp?.fillJsonData) { + dp.fillJsonData(currentData, { fillMode: "set" }); + } + gridRef.gridView.refresh(); + } catch (error) { + console.error("그리드 데이터 설정 오류:", error); + } + }, 100); + } + }); + }, [rowDataMap, 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; + + // isSplitGrid가 true이고 left, right가 있으면 SplitDataGrid 사용 + if (info.isSplitGrid && info.left && info.right) { + const leftRowData = rowDataMap.get(info.left.tabKey || `${tabKey}_left`) || []; + const rightRowData = rowDataMap.get(info.right.tabKey || `${tabKey}_right`) || []; + + // ColumnInfoList를 ColumnInfo로 변환 + const leftColumnInfo: ColumnInfo = { + columns: info.left.columns || [], + columnLayout: info.left.columnLayout || [] + }; + const rightColumnInfo: ColumnInfo = { + columns: info.right.columns || [], + columnLayout: info.right.columnLayout || [] + }; + + return ( + + { + setRowDataMap(prev => { + const newMap = new Map(prev); + const leftTabKey = info.left?.tabKey || `${tabKey}_left`; + const currentData = newMap.get(leftTabKey) || []; + const newData = typeof updater === 'function' ? updater(currentData) : updater; + newMap.set(leftTabKey, newData); + return newMap; + }); + }} + setRightRowData={(updater) => { + setRowDataMap(prev => { + const newMap = new Map(prev); + const rightTabKey = info.right?.tabKey || `${tabKey}_right`; + const currentData = newMap.get(rightTabKey) || []; + const newData = typeof updater === 'function' ? updater(currentData) : updater; + newMap.set(rightTabKey, newData); + return newMap; + }); + }} + /> + + ); + } + + // 일반 그리드 사용 + if (!info.columns || !info.columnLayout) { + return null; + } + 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 && + + } + {isExcelExport && + + } +
+
+ { + if (el) { + gridRefs.current.set(tabKey, el); + // 그리드가 생성된 직후 fitStyle 설정 + if (el.gridView) { + setTimeout(() => { + const gridView = el.gridView; + if (!gridView) return; + + const currentInfo = columnInfo.find(info => info.tabKey === tabKey); + if (!currentInfo || !currentInfo.columns) return; + + const columns = currentInfo.columns; + + const dp = el.dataProvider as { setFields?: (fields: unknown[]) => void } | undefined; + if (dp?.setFields) { + dp.setFields(buildFieldsFromColumns(columns)); + } + + gridView.setDisplayOptions({ + fitStyle: GridFitStyle.EVEN + }); + gridView.header.height = 60; + + // 헤더 체크박스 표시 여부 설정 + gridView.setOptions({ + checkBar: { visible: showHeaderCheckbox }, + rowIndicator: { visible: false }, + }); + + // footer가 있는 컬럼이 있으면 footer 높이 설정 + const hasFooter = columns.some(col => col.footer); + if (hasFooter) { + // footer가 배열이거나 multiRow가 true인 footer가 있으면 멀티푸터 설정 (여러 행) + const hasArrayFooter = columns.some(col => col.footer && Array.isArray(col.footer)); + const hasMultiRowFooter = columns.some(col => { + const footer = col.footer; + return footer && !Array.isArray(footer) && 'multiRow' in footer && footer.multiRow === true; + }); + if (hasArrayFooter || hasMultiRowFooter) { + // footer 배열의 길이를 확인하여 행 수 결정 + const arrayFooterLengths = columns + .filter(col => col.footer && Array.isArray(col.footer)) + .map(col => (col.footer as unknown[]).length); + const maxFooterLength = arrayFooterLengths.length > 0 + ? Math.max(...arrayFooterLengths) + : 2; // 기본값 2 + const footerHeights = Array.from({ length: maxFooterLength }, () => ({ height: 40 })); + gridView.setFooters?.(footerHeights); + } else { + gridView.footer.height = 40; + } + } + + gridView.setOptions({ + summaryMode: "aggregate" + }); + + // footerUserSpans가 있는 경우 setColumnLayout으로 설정 + const checkFooterUserSpans = (layout: unknown[]): boolean => { + return layout.some(item => { + if (typeof item === 'string') return false; + if (item && typeof item === 'object' && 'footerUserSpans' in item) return true; + if (item && typeof item === 'object' && 'items' in item && Array.isArray(item.items)) { + return checkFooterUserSpans(item.items); + } + return false; + }); + }; + + if (checkFooterUserSpans(columnLayout)) { + // setColumnLayout은 그리드가 완전히 렌더링된 후 호출해야 함 + setTimeout(() => { + if (gridView && columnLayout) { + gridView.setColumnLayout?.(columnLayout); + } + }, 100); + } + + // footer 설정 (배열 또는 단일 객체 모두 처리) + // setFooters 호출 후 footer 배열 설정 + setTimeout(() => { + const currentGridView = gridView; + if (!currentGridView || !currentGridView.columnByName) { + console.warn('GridView not available for footer setup'); + return; + } + + // footer가 배열인 컬럼이 있으면 setColumns를 다시 호출하여 footer 설정 + const hasArrayFooter = columns.some(col => col.footer && Array.isArray(col.footer)); + if (hasArrayFooter) { + // 모든 컬럼 정보를 다시 가져와서 footer 배열을 포함하여 setColumns 호출 + const columnsWithFooter: RealGridColumnInfo[] = columns.map(col => { + const colInfo: RealGridColumnInfo = { + name: col.name, + fieldName: col.fieldName, + header: col.header, + editable: col.editable, + valueType: col.valueType, + dataFormat: col.dataFormat, + ...(col.styleName != null && { styleName: col.styleName }), + ...(col.mergeCells && { mergeRule: { criteria: 'value' as const } }), + }; + + if (col.footer) { + if (Array.isArray(col.footer)) { + colInfo.footer = col.footer; + } else { + colInfo.footer = { + text: col.footer.text, + expression: col.footer.expression, + numberFormat: col.footer.numberFormat, + }; + } + } + + return colInfo; + }); + + try { + (currentGridView as GridViewWithSetColumns).setColumns?.(columnsWithFooter); + + // setColumns 호출 후 footer 배열의 valueCallback을 다시 설정 + setTimeout(() => { + columns.forEach(col => { + if (!col.footer || !Array.isArray(col.footer)) return; + + try { + const column = currentGridView.columnByName(col.name); + if (!column) return; + + // footer 배열의 각 요소에 valueCallback 설정 + col.footer.forEach((footerItem, index) => { + if (footerItem.valueCallback && column.footer) { + // footer가 배열인 경우 + if (Array.isArray(column.footer) && column.footer[index]) { + (column.footer[index] as FooterItemWithCallback).valueCallback = footerItem.valueCallback; + } + } + }); + } catch (e) { + console.warn(`Error setting footer array valueCallback for column ${col.name}:`, e); + } + }); + }, 50); + } catch (e) { + console.warn('Failed to set columns with footer array:', e); + } + } + + // footer가 단일 객체이고 valueCallback이 있는 경우 + columns.forEach(col => { + if (!col.footer || Array.isArray(col.footer)) return; + + try { + const column = currentGridView.columnByName(col.name); + if (!column || !column.footer) return; + + const footerObj = col.footer as { valueCallback?: (grid: unknown, column: unknown, footerIndex: number, columnFooter: unknown, value: unknown) => unknown }; + if (footerObj.valueCallback) { + (column.footer as { valueCallback?: typeof footerObj.valueCallback }).valueCallback = footerObj.valueCallback; + } + } catch (e) { + console.warn(`Error setting footer valueCallback for column ${col.name}:`, e); + } + }); + }, 200); + + // 고정 컬럼 설정 + if (currentInfo.fixedColumns !== undefined && currentInfo.fixedColumns > 0) { + gridView.setFixedOptions({ + colCount: currentInfo.fixedColumns, + }); + } + + // 각 탭별 ref 초기화 + if (!sortDirRefs.current.has(tabKey)) { + sortDirRefs.current.set(tabKey, { current: {} }); + } + if (!originalRowsRefs.current.has(tabKey)) { + originalRowsRefs.current.set(tabKey, { current: {} }); + } + + const sortDirRef = sortDirRefs.current.get(tabKey)!; + const originalRowsRef = originalRowsRefs.current.get(tabKey)!; + + // mergeCells: true인 컬럼이 있으면 onSorting에서 클라이언트 정렬로 처리 + const hasMergeCells = columns.some((col) => col.mergeCells === true); + + // setDataComparer로 숫자 정렬 (mergeCells가 있으면 onSorting에서 클라이언트 정렬로 처리) + if (el.dataProvider) { + const dp = el.dataProvider as RealGridDataProviderComparer; + if (typeof dp.setDataComparer === "function" && typeof dp.getValue === "function") { + const numberComparer = createNumberComparer(dp); + const seen = new Set(); + columns.forEach((col) => { + if (hasMergeCells) return; // mergeCells가 있으면 모든 컬럼을 클라이언트 정렬로 처리 + if (col.valueType === ValueType.NUMBER && col.fieldName && !seen.has(col.fieldName)) { + seen.add(col.fieldName); + try { + dp.setDataComparer!(col.fieldName, numberComparer); + } catch (e) { + console.warn("setDataComparer:", e); + } + } + }); + } + } + + // mergeCells: true인 컬럼이 있으면 onSorting에서 그룹 내 순서 유지 정렬 적용 + const setRowData = (updater: React.SetStateAction[]>) => { + setRowDataMap(prev => { + const newMap = new Map(prev); + const currentData = newMap.get(tabKey) || []; + const newData = typeof updater === 'function' ? updater(currentData) : updater; + newMap.set(tabKey, newData); + return newMap; + }); + }; + + // 그리드 DOM 요소 찾기 (나중에 화살표 업데이트에 사용) + // 탭별로 그리드 요소를 찾기 위해 data-tab-key 사용 + const findGridElement = () => { + const tabContent = document.querySelector(`[data-tab-key="${tabKey}"]`); + return tabContent?.querySelector('.rg-grid, [data-grid-id]') as HTMLElement | null; + }; + + const onSortingHandler = createOnSortingHandler(columns, setRowData, sortDirRef, originalRowsRef, findGridElement); + (gridView as { onSorting?: (g: unknown, f: unknown[], d: unknown[]) => boolean }).onSorting = onSortingHandler; + }, 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/input/ExcretaTreatmentFacilityInp.tsx b/apps/st/src/components/statistics/input/ExcretaTreatmentFacilityInp.tsx new file mode 100644 index 000000000..94fbb6a3e --- /dev/null +++ b/apps/st/src/components/statistics/input/ExcretaTreatmentFacilityInp.tsx @@ -0,0 +1,80 @@ +import { useState } from "react"; + +import SearchForm from "@/components/module/SearchForm"; +import { DataGrid } from "@rsone/components"; +import SubmitButton from "@/components/button/SumitButton"; +import SaveButton from "@/components/button/SaveButton"; +import LastYearDataButton from "@/components/button/LastYearDataButton"; +import SubmitExceptionButton from "@/components/button/SubmitExceptionButton"; +import AddArticleButton from "@/components/button/AddArticleButton"; +import DeleteArticleButton from "@/components/button/DeleteArticleButton"; +import NotApplicableCheckButton from "@/components/button/NotApplicableCheckButton"; + +import { ExcretaTreatFactInpColumn } from "@/components/util/column/input/ExcretaTreatFactInpColumn" + +// 하수도 통계 관리 > 07. 분뇨처리시설 +const ExcretaTreatmentFacilityInp = () => { + + const submitExceptionFunc = () => { + console.log('submit exception'); + // 예외제출 로직 + }; + + const getLastYearDataFunc = () => { + console.log('get last year data'); + // 지난연도 데이터 가져오기 로직 + }; + + const submitFunc = () => { + console.log('submit'); + // 제출 로직 + }; + + const saveFunc = () => { + console.log('save'); + // 저장 로직 + }; + + const addArticleFunc = () => { + console.log('save'); + // 항목 추가 로직 + }; + + const deleteArticleFunc = () => { + console.log('save'); + // 항목 제외 로직 + }; + + const [rowData, setRowData] = useState[]>([]); + const [isNotApplicable, setIsNotApplicable] = useState(false); + + return ( + <> + +
+
+ + + + +
+
+
+
+ + + +
+
+ + + ); +}; + +export default ExcretaTreatmentFacilityInp; \ No newline at end of file diff --git a/apps/st/src/components/statistics/input/PrivateSewageFacilityInp.tsx b/apps/st/src/components/statistics/input/PrivateSewageFacilityInp.tsx new file mode 100644 index 000000000..0dabb52c8 --- /dev/null +++ b/apps/st/src/components/statistics/input/PrivateSewageFacilityInp.tsx @@ -0,0 +1,62 @@ +import { useState } from "react"; + +import SearchForm from "@/components/module/SearchForm"; +import { TabDataGrid } from "@rsone/components"; +import SubmitButton from "@/components/button/SumitButton"; +import SaveButton from "@/components/button/SaveButton"; +import SubmitExceptionButton from "@/components/button/SubmitExceptionButton"; +import LastYearDataButton from "@/components/button/LastYearDataButton"; + +import { SewageTreatFactStatusInpColumn } from "@/components/util/column/input/privateSewageFacility/SewageTreatFactStatusInpColumn" +import { SepticTankStatusInpColumn } from "@/components/util/column/input/privateSewageFacility/SepticTankStatusInpColumn" +import { PrivateSewageDesigStatusInpColumn } from "@/components/util/column/input/privateSewageFacility/PrivateSewageDesigStatusInpColumn" + +// 하수도 통계 관리 > 08. 개인하수처리시설 +const PrivateSewageFacilityInp = () => { + + // tab list + const PrivateSewageFacilityInpTabList = [ + SewageTreatFactStatusInpColumn, + SepticTankStatusInpColumn, + PrivateSewageDesigStatusInpColumn + ]; + + const submitExceptionFunc = () => { + // 예외 제출 로직 + }; + + const lastYearDataFunc = () => { + // 지난연도 데이터 가져오기 로직 + }; + + const submitFunc = () => { + // 제출 로직 + }; + + const saveFunc = () => { + // 저장 로직 + }; + + const [rowDataMap, setRowDataMap] = useState[]>>(new Map()); + + return ( + <> + +
+
+ + + + +
+
+ + + ); +}; + +export default PrivateSewageFacilityInp; \ No newline at end of file diff --git a/apps/st/src/components/statistics/input/PublicSewageFacilityInp.tsx b/apps/st/src/components/statistics/input/PublicSewageFacilityInp.tsx new file mode 100644 index 000000000..0a8e1d277 --- /dev/null +++ b/apps/st/src/components/statistics/input/PublicSewageFacilityInp.tsx @@ -0,0 +1,73 @@ +import { useState } from 'react'; + +import SearchForm from '@/components/module/SearchForm'; +import { DataGrid } from '@rsone/components'; +import SubmitButton from '@/components/button/SumitButton'; +import SaveButton from '@/components/button/SaveButton'; +import SubmitExceptionButton from '@/components/button/SubmitExceptionButton'; +import OperateDataButton from '@/components/button/OperateDataButton'; +import UpdateOperateDataButton from "@/components/button/UpdateOperateDataButton" +import NotApplicableCheckButton from "@/components/button/NotApplicableCheckButton"; + +import { PublicSewageFactInpColumn } from '@/components/util/column/input/PublicSewageFactInpColumn'; + +// 하수도 통계 관리 > 06. 공공하수처리시설 +const PublicSewageFacilityInp = () => { + + const SubmitExceptionFunc = () => { + console.log('submitException'); + // 예외제출 로직 + }; + + const submitFunc = () => { + console.log('submit'); + // 제출 로직 + }; + + const saveFunc = () => { + console.log('save'); + // 저장 로직 + }; + + const getOperateDataFunc = () => { + console.log('get operate data'); + // 운영 데이터 가져오기 + }; + + const updateOperateDataButtonFunc = () => { + console.log('updatedOperateData'); + // 운영 데이터 업데이트 로직 + }; + + const [rowData, setRowData] = useState[]>([]); + const [isNotApplicable, setIsNotApplicable] = useState(false); + + return ( + <> + +
+
+ + + +
+
+
+
+ + + +
+
+ + + ); +}; + +export default PublicSewageFacilityInp; \ No newline at end of file diff --git a/apps/st/src/components/statistics/input/PumpingStationInp.tsx b/apps/st/src/components/statistics/input/PumpingStationInp.tsx new file mode 100644 index 000000000..2261a8eac --- /dev/null +++ b/apps/st/src/components/statistics/input/PumpingStationInp.tsx @@ -0,0 +1,80 @@ +import { useState } from "react"; + +import SearchForm from "@/components/module/SearchForm"; +import { DataGrid } from '@rsone/components'; +import SubmitButton from "@/components/button/SumitButton"; +import SaveButton from "@/components/button/SaveButton"; +import LastYearDataButton from "@/components/button/LastYearDataButton"; +import SubmitExceptionButton from "@/components/button/SubmitExceptionButton"; +import AddArticleButton from "@/components/button/AddArticleButton"; +import DeleteArticleButton from "@/components/button/DeleteArticleButton"; +import NotApplicableCheckButton from "@/components/button/NotApplicableCheckButton"; + +import { PumpingStationInpColumn } from "@/components/util/column/input/PumpingStationInpColumn" + +// 하수도 통계 관리 > 05. 펌프장 +const PumpingStationInp = () => { + + const SubmitExceptionFunc = () => { + console.log('submit exception'); + // 예외 제출 로직 + }; + + const getLastYearDataFunc = () => { + console.log('get last year data'); + // 지난연도 데이터 가져오기 로직 + }; + + const submitFunc = () => { + console.log('submit'); + // 제출 로직 + }; + + const saveFunc = () => { + console.log('save'); + // 저장 로직 + }; + + const addArticleFunc = () => { + console.log('save'); + // 항목 추가 로직 + }; + + const deleteArticleFunc = () => { + console.log('save'); + // 항목 제외 로직 + }; + + const [rowData, setRowData] = useState[]>([]); + const [isNotApplicable, setIsNotApplicable] = useState(false); + + return ( + <> + +
+
+ + + + +
+
+
+
+ + + +
+
+ + + ); +}; + +export default PumpingStationInp; \ No newline at end of file diff --git a/apps/st/src/components/statistics/input/SewageExcretaRelatedInp.tsx b/apps/st/src/components/statistics/input/SewageExcretaRelatedInp.tsx new file mode 100644 index 000000000..726d5718a --- /dev/null +++ b/apps/st/src/components/statistics/input/SewageExcretaRelatedInp.tsx @@ -0,0 +1,81 @@ +import { useState } from "react"; + +import SearchForm from "@/components/module/SearchForm"; +import { TabDataGrid } from "@rsone/components"; +import SubmitButton from "@/components/button/SumitButton"; +import SaveButton from "@/components/button/SaveButton"; +import SubmitExceptionButton from "@/components/button/SubmitExceptionButton"; +import LastYearDataButton from "@/components/button/LastYearDataButton"; +import AddArticleButton from "@/components/button/AddArticleButton"; +import DeleteArticleButton from "@/components/button/DeleteArticleButton"; + +import { PublicInstitutionSewerIndustryInpColumn } from "@/components/util/column/input/sewageExcretaRelated/PublicInstitutionSewerIndustryInpColumn" +import { PublicInstitutionExcretaIndustryInpColumn } from "@/components/util/column/input/sewageExcretaRelated/PublicInstitutionExcretaIndustryInpColumn" +import { ExcretaRelatedIndustryInpColumn } from "@/components/util/column/input/sewageExcretaRelated/ExcretaRelatedIndustryInpColumn"; +import { PrivateSewageTreatRelatedIndustryInpColumn } from "@/components/util/column/input/sewageExcretaRelated/PrivateSewageTreatRelatedIndustryInpColumn"; + +// 하수도 통계 관리 > 12. 하수 및 분뇨관련산업 +const SewageExcretaRelatedInp = () => { + + // tab list + const SewageExcretaRelatedInpTabList = [ + PublicInstitutionSewerIndustryInpColumn, + PublicInstitutionExcretaIndustryInpColumn, + ExcretaRelatedIndustryInpColumn, + PrivateSewageTreatRelatedIndustryInpColumn + ]; + + const submitExceptionFunc = () => { + // 예외 제출 로직 + }; + + const lastYearDataFunc = () => { + // 지난연도 데이터 가져오기 로직 + }; + + const submitFunc = () => { + // 제출 로직 + }; + + const saveFunc = () => { + // 저장 로직 + }; + + const addArticleFunc = () => { + // 항목 추가 로직 + }; + + const deleteArticleFunc = () => { + console.log('save'); + // 항목 제외 로직 + }; + + const [rowDataMap, setRowDataMap] = useState[]>>(new Map()); + + return ( + <> + +
+
+ + + + +
+
+
+
+ + +
+
+ + + ); +}; + +export default SewageExcretaRelatedInp; \ No newline at end of file diff --git a/apps/st/src/components/statistics/input/SewageExcretaSlurgeInp.tsx b/apps/st/src/components/statistics/input/SewageExcretaSlurgeInp.tsx new file mode 100644 index 000000000..a4ab5c947 --- /dev/null +++ b/apps/st/src/components/statistics/input/SewageExcretaSlurgeInp.tsx @@ -0,0 +1,84 @@ +import { useState } from "react"; + +import SearchForm from "@/components/module/SearchForm"; +import { TabDataGrid } from "@rsone/components"; +import SubmitButton from "@/components/button/SumitButton"; +import SaveButton from "@/components/button/SaveButton"; +import SubmitExceptionButton from "@/components/button/SubmitExceptionButton"; +import LastYearDataButton from "@/components/button/LastYearDataButton"; +import AddArticleButton from "@/components/button/AddArticleButton"; +import DeleteArticleButton from "@/components/button/DeleteArticleButton"; +import NotApplicableCheckButton from "@/components/button/NotApplicableCheckButton"; + +import { SlurgeProcessingFacStatusInpColumn } from "@/components/util/column/input/sewageExcretaSlurge/SlurgeProcessingFacStatusInpColumn"; +import { DigestorOperatingStatusInpColumn } from "@/components/util/column/input/sewageExcretaSlurge/DigestorOperatingStatusInpColumn"; +import { SewageSlurgeProcessingFacStatusInpColumn } from "@/components/util/column/input/sewageExcretaSlurge/SewageSlurgeProcessingFacStatusInpColumn"; +import { ExcretaSlurgeProcessingStatusInpColumn } from "@/components/util/column/input/sewageExcretaSlurge/ExcretaSlurgeProcessingStatusInpColumn"; + + +// 하수도 통계 관리 > 11. 하수 및 분뇨찌꺼기(슬러지)처리 +const SewageExcretaSlurgeInp = () => { + + // tab list + const SewageExcretaSlurgeInpTabList = [ + SlurgeProcessingFacStatusInpColumn, + DigestorOperatingStatusInpColumn, + SewageSlurgeProcessingFacStatusInpColumn, + ExcretaSlurgeProcessingStatusInpColumn + ]; + + const submitExceptionFunc = () => { + // 예외 제출 로직 + }; + + const lastYearDataFunc = () => { + // 지난연도 데이터 가져오기 로직 + }; + + const submitFunc = () => { + // 제출 로직 + }; + + const saveFunc = () => { + // 저장 로직 + }; + + const addArticleFunc = () => { + // 항목 추가 로직 + }; + + const deleteArticleFunc = () => { + // 항목 제외 로직 + }; + + const [rowDataMap, setRowDataMap] = useState[]>>(new Map()); + const [isNotApplicable, setIsNotApplicable] = useState(false); + + return ( + <> + +
+
+ + + + +
+
+
+
+ + + +
+
+ + + ); +}; + +export default SewageExcretaSlurgeInp; diff --git a/apps/st/src/components/statistics/input/SewagePipeInp.tsx b/apps/st/src/components/statistics/input/SewagePipeInp.tsx new file mode 100644 index 000000000..4eac16126 --- /dev/null +++ b/apps/st/src/components/statistics/input/SewagePipeInp.tsx @@ -0,0 +1,72 @@ +import { useState } from "react"; + +import SearchForm from "@/components/module/SearchForm"; +import { TabDataGrid } from '@rsone/components'; +import SubmitButton from "@/components/button/SumitButton"; +import SaveButton from "@/components/button/SaveButton"; +import LastYearDataButton from "@/components/button/LastYearDataButton"; +import SubmitExceptionButton from "@/components/button/SubmitExceptionButton"; + +import { RegionalFactStatusInpColumn } from "@/components/util/column/input/sewagePipe/RegionalFactStatusInpColumn"; +import { FactInstallStatusInpColumn } from "@/components/util/column/input/sewagePipe/FactInstallStatusInpColumn"; +import { PipeTypeStatusInpColumn } from "@/components/util/column/input/sewagePipe/PipeTypeStatusInpColumn"; +import { PipeDiameterStatusInpColumn } from "@/components/util/column/input/sewagePipe/PipeDiameterStatusInpColumn"; +import { ManholeStatusInpColumn } from "@/components/util/column/input/sewagePipe/ManholeStatusInpColumn"; +import { ConnectionStatusInpColumn } from "@/components/util/column/input/sewagePipe/ConnectionStatusInpColumn"; + +// 하수도 통계 관리 > 02. 하수관로 +const SewagePipeInp = () => { + + // tab List + const SewagePipeInpTapList = [ + RegionalFactStatusInpColumn, + FactInstallStatusInpColumn, + PipeTypeStatusInpColumn, + PipeDiameterStatusInpColumn, + ManholeStatusInpColumn, + ConnectionStatusInpColumn, + ]; + + const SubmitExceptionFunc = () => { + console.log('submit exception'); + // 예외 제출 로직 + }; + + const getLastYearDataFunc = () => { + console.log('get last year data'); + // 지난연도 데이터 가져오기 로직 + }; + + const submitFunc = () => { + console.log('submit'); + // 제출 로직 + }; + + const saveFunc = () => { + console.log('save'); + // 저장 로직 + }; + + const [rowDataMap, setRowDataMap] = useState[]>>(new Map()); + + return ( + <> + +
+
+ + + + +
+
+ + + ); +}; + +export default SewagePipeInp; \ No newline at end of file diff --git a/apps/st/src/components/statistics/input/SewagePipeMaintainInp.tsx b/apps/st/src/components/statistics/input/SewagePipeMaintainInp.tsx new file mode 100644 index 000000000..c34d35498 --- /dev/null +++ b/apps/st/src/components/statistics/input/SewagePipeMaintainInp.tsx @@ -0,0 +1,64 @@ +import { useState } from "react"; + +import SearchForm from "@/components/module/SearchForm"; +import { TabDataGrid } from '@rsone/components'; +import SubmitButton from "@/components/button/SumitButton"; +import SaveButton from "@/components/button/SaveButton"; +import LastYearDataButton from "@/components/button/LastYearDataButton"; +import SubmitExceptionButton from "@/components/button/SubmitExceptionButton"; + +import { RepairStatusInpColumn } from "@/components/util/column/input/sewagePipeMaintain/RepairStatusInpColumn"; +import { PipeDredgingStatusInpColumn } from "@/components/util/column/input/sewagePipeMaintain/PipeDredgingStatusInpColumn"; + +// 하수도 통계 관리 > 03. 하수관로 유지관리 +const SewagePipeMaintainInp = () => { + + // tab List + const SewagePipeMaintainInpTabList = [ + RepairStatusInpColumn, + PipeDredgingStatusInpColumn + ]; + + const SubmitExceptionFunc = () => { + console.log('submit exception'); + // 예외 제출 로직 + }; + + const getLastYearDataFunc = () => { + console.log('get last year data'); + // 지난연도 데이터 가져오기 로직 + }; + + const submitFunc = () => { + console.log('submit'); + // 제출 로직 + }; + + const saveFunc = () => { + console.log('save'); + // 저장 로직 + }; + + const [rowDataMap, setRowDataMap] = useState[]>>(new Map()); + + return ( + <> + +
+
+ + + + +
+
+ + + ); +}; + +export default SewagePipeMaintainInp; \ No newline at end of file diff --git a/apps/st/src/components/statistics/input/SewageReservoirPumpInp.tsx b/apps/st/src/components/statistics/input/SewageReservoirPumpInp.tsx new file mode 100644 index 000000000..1ea0892c5 --- /dev/null +++ b/apps/st/src/components/statistics/input/SewageReservoirPumpInp.tsx @@ -0,0 +1,85 @@ +import { useState } from "react"; + +import SearchForm from "@/components/module/SearchForm"; +import { TabDataGrid } from '@rsone/components'; +import SubmitButton from "@/components/button/SumitButton"; +import SaveButton from "@/components/button/SaveButton"; +import LastYearDataButton from "@/components/button/LastYearDataButton"; +import SubmitExceptionButton from "@/components/button/SubmitExceptionButton"; +import AddArticleButton from "@/components/button/AddArticleButton"; +import DeleteArticleButton from "@/components/button/DeleteArticleButton"; +import NotApplicableCheckButton from "@/components/button/NotApplicableCheckButton"; + +import { SewageReservoirInpColumn } from "@/components/util/column/input/sewageReservoirPump/SewageReservoirInpColumn"; +import { RainfallPumpStationInpColumn } from "@/components/util/column/input/sewageReservoirPump/RainfallPumpStationInpColumn"; + +// 하수도 통계 관리 > 04. 하수저류 시설 및 빗물 펌프장 +const SewageReservoirPumpInp = () => { + + // tab List + const SewageReservoirPumpInpTabList = [ + SewageReservoirInpColumn, + RainfallPumpStationInpColumn + ]; + + const submitExceptionFunc = () => { + console.log('submit exception'); + // 예외 제출 로직 + }; + + const getLastYearDataFunc = () => { + console.log('get last year data'); + // 지난연도 데이터 가져오기 로직 + }; + + const submitFunc = () => { + console.log('submit'); + // 제출 로직 + }; + + const saveFunc = () => { + console.log('save'); + // 저장 로직 + }; + + const addArticleFunc = () => { + console.log('save'); + // 항목 추가 로직 + }; + + const deleteArticleFunc = () => { + console.log('save'); + // 항목 제외 로직 + }; + + const [rowDataMap, setRowDataMap] = useState[]>>(new Map()); + const [isNotApplicable, setIsNotApplicable] = useState(false); + + return ( + <> + +
+
+ + + + +
+
+
+
+ + + +
+
+ + + ); +}; + +export default SewageReservoirPumpInp; \ No newline at end of file diff --git a/apps/st/src/components/statistics/input/SewerageFinanceInp.tsx b/apps/st/src/components/statistics/input/SewerageFinanceInp.tsx new file mode 100644 index 000000000..c1c2177de --- /dev/null +++ b/apps/st/src/components/statistics/input/SewerageFinanceInp.tsx @@ -0,0 +1,64 @@ +import { useState } from "react"; + +import SearchForm from "@/components/module/SearchForm"; +import { TabDataGrid } from '@rsone/components'; +import SubmitButton from "@/components/button/SumitButton"; +import SaveButton from "@/components/button/SaveButton"; +import SubmitExceptionButton from "@/components/button/SubmitExceptionButton"; +import LastYearDataButton from "@/components/button/LastYearDataButton"; + +import { SewerageFinanceSettleInpColumn } from "@/components/util/column/input/sewerageFinance/SewerageFinanceSettleInpColumn" +import { SewerageChargeInpColumn } from "@/components/util/column/input/sewerageFinance/SewerageChargeInpColumn" +import { SewerageUsageFeeInpColumn } from "@/components/util/column/input/sewerageFinance/SewerageUsageFeeInpColumn" +import { CauserBurdenInpColumn } from "@/components/util/column/input/sewerageFinance/CauserBurdenInpColumn" + +// 하수도 통계 관리 > 09. 하수도재정 +const SewerageFinanceInp = () => { + + // tab list + const SewerageFinanceInpTabList = [ + SewerageFinanceSettleInpColumn, + SewerageChargeInpColumn, + SewerageUsageFeeInpColumn, + CauserBurdenInpColumn + ]; + + const submitExceptionFunc = () => { + // 예외 제출 로직 + }; + + const lastYearDataFunc = () => { + // 지난연도 데이터 가져오기 로직 + }; + + const submitFunc = () => { + // 제출 로직 + }; + + const saveFunc = () => { + // 저장 로직 + }; + + const [rowDataMap, setRowDataMap] = useState[]>>(new Map()); + + return ( + <> + +
+
+ + + + +
+
+ + + ); +}; + +export default SewerageFinanceInp; \ No newline at end of file diff --git a/apps/st/src/components/statistics/input/SeweragePenetrationRateInp.tsx b/apps/st/src/components/statistics/input/SeweragePenetrationRateInp.tsx new file mode 100644 index 000000000..7644ab1e3 --- /dev/null +++ b/apps/st/src/components/statistics/input/SeweragePenetrationRateInp.tsx @@ -0,0 +1,167 @@ +import { useState, useMemo } from "react"; + +import SearchForm from "@/components/module/SearchForm"; +// import { DataGrid } from "@rsone/components"; +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"; +import { LoadingWithAnimation } from "@common/components"; + +import { useAlertStore } from "@/shared/lib"; +import { statisticsInputAPi } from "@/entities/statistics/api/statistics-input-service"; +import type { SearchFormValues } from "@/components/type/SearchForm.type"; +import type { + SewerageSupplyRateItem, + SewerageSupplyRateRequestParams, +} from "@/entities/statistics/model/statistics-input-interface"; +import { SeweragePenetrationRateInpColumn } from "@/components/util/column/input/SeweragePenetrationRateInpColumn"; +import { toGridRows } from "@/components/util/grid/gridFunction"; + +function getSewerageSupplyRateParams( + values: SearchFormValues, + overrides?: { yr?: string; demoYr?: string; mode?: string } +): SewerageSupplyRateRequestParams { + return { + sido: values.sido, + gugun: values.gugun === 'ALL' ? '' : values.gugun, + bsnmCd: values.bsnmCd, + yr: overrides?.yr ?? values.year, + demoYr: overrides?.demoYr ?? values.year, + ...(overrides?.mode && { mode: overrides.mode }), + }; +} + +// 하수도 통계 관리 > 01. 하수도 보급률 +const SeweragePenetrationRateInp = () => { + const { setMessage: alert } = useAlertStore((state) => state); + const [rowData, setRowData] = useState[]>([]); + const [isLoading, setIsLoading] = useState(false); + const [searchValues, setSearchValues] = useState(null); + const [isEnterable, setIsEnterable] = useState(true); + const [lastSearchedYear, setLastSearchedYear] = useState(null); + + + // 최상위 관리자일땐 모든 상황에서 edit 가능하게 수정 필요. + const columnInfo = useMemo(() => { + const { columns, columnLayout } = SeweragePenetrationRateInpColumn; + return { + columnLayout, + columns: isEnterable + ? columns + : columns.map((col) => ({ ...col, editable: false as const })), + }; + }, [isEnterable]); + + const handleReset = () => { + setRowData([]); + setLastSearchedYear(null); + }; + + const fetchSewerageSupplyRate = async (params: SewerageSupplyRateRequestParams) => { + try { + setIsLoading(true); + const response = await statisticsInputAPi.getSewerageSupplyRateList(params); + if (response.status === 'success' && response.data) { + const convertedData: SewerageSupplyRateItem[] = response.data.sewerageSupplyRateList.map((d) => ({ + ...d, + speclCntrplnAr: d.speclCntrplnAr === '' || d.speclCntrplnAr === null ? '해당없음' : d.speclCntrplnAr, + })); + + setRowData(toGridRows(convertedData)); + } else { + alert(response.message); + } + } catch { + alert('데이터 조회 중 오류가 발생했습니다.'); + setRowData([]); + } finally { + setIsLoading(false); + } + }; + + const handleSearch = async (values: SearchFormValues) => { + if (values.sido === '' || values.sido === 'ALL') { + alert('시도를 선택해주세요.'); + return; + } + + try { + const response = await statisticsInputAPi.checkInputPeriod(values.year || ''); + + if (response.status === 'success') { + setIsEnterable(response.data); + setLastSearchedYear(values.year || null); + + await fetchSewerageSupplyRate(getSewerageSupplyRateParams(values)); + } else { + alert(response.message); + } + } catch { + alert('데이터 조회 중 오류가 발생했습니다.'); + } + }; + + const SubmitExceptionFunc = () => { + // 예외 제출 로직 + }; + + const getLastYearDataFunc = async (values: SearchFormValues) => { + if (values.sido === '' || values.sido === 'ALL') { + alert('시도를 선택해주세요.'); + return; + } + + // 실제 조회된 연도가 없으면 실행 불가 + if (!lastSearchedYear) { + alert('먼저 데이터를 조회해주세요.'); + return; + } + + await fetchSewerageSupplyRate(getSewerageSupplyRateParams(values, { mode: 'PRE' })); + }; + + const submitFunc = () => { + // 제출 로직 + }; + + const saveFunc = () => { + // 저장 로직 + }; + + return ( +
+ + {rowData.length > 0 && +
+
+ {isEnterable && } + {isEnterable && getLastYearDataFunc(searchValues ?? {})} />} + {isEnterable && } + { + // 최고 관리자일땐 저장 가능하게 + + } +
+
+ } + {isLoading &&
+
+ +
+
} + 0) && isEnterable} + rowStartNum={3} + fixedColumns={1} + rowData={rowData} + setRowData={setRowData} + /> +
+ ); +}; + +export default SeweragePenetrationRateInp; \ No newline at end of file diff --git a/apps/st/src/components/statistics/input/SewerageServiceLevelInp.tsx b/apps/st/src/components/statistics/input/SewerageServiceLevelInp.tsx new file mode 100644 index 000000000..7ce370bbb --- /dev/null +++ b/apps/st/src/components/statistics/input/SewerageServiceLevelInp.tsx @@ -0,0 +1,67 @@ +import { useState } from "react"; + +import SearchForm from "@/components/module/SearchForm"; +import { TabDataGrid } from '@rsone/components'; +import SubmitButton from "@/components/button/SumitButton"; +import SaveButton from "@/components/button/SaveButton"; +import LastYearDataButton from "@/components/button/LastYearDataButton"; +import SubmitExceptionButton from "@/components/button/SubmitExceptionButton"; +import NotApplicableCheckButton from "@/components/button/NotApplicableCheckButton"; + +import { ComplaintInpColumn } from "@/components/util/column/input/sewerageServiceLevel/ComplaintInpColumn"; +import { ResidentFriendlyFactInpColumn } from "@/components/util/column/input/sewerageServiceLevel/ResidentFriendlyFactInpColumn"; + +// 하수도 통계 관리 > 13. 하수도 서비스 수준 +const SewerageServiceLevelInp = () => { + + // tab List + const SewerageServiceLevelInpTabList = [ + ComplaintInpColumn, + ResidentFriendlyFactInpColumn + ]; + + const submitExceptionFunc = () => { + // 예외 제출 로직 + }; + + const getLastYearDataFunc = () => { + // 지난연도 데이터 가져오기 로직 + }; + + const submitFunc = () => { + // 제출 로직 + }; + + const saveFunc = () => { + // 저장 로직 + }; + + const [rowDataMap, setRowDataMap] = useState[]>>(new Map()); + const [isNotApplicable, setIsNotApplicable] = useState(false); + + return ( + <> + +
+
+ + + + +
+
+
+
+ +
+
+ + + ); +}; + +export default SewerageServiceLevelInp; \ No newline at end of file diff --git a/apps/st/src/components/statistics/input/StatsInputStatusInp.tsx b/apps/st/src/components/statistics/input/StatsInputStatusInp.tsx new file mode 100644 index 000000000..46f8620c2 --- /dev/null +++ b/apps/st/src/components/statistics/input/StatsInputStatusInp.tsx @@ -0,0 +1,44 @@ +import { useState } from "react"; + +import SearchForm from "@/components/module/SearchForm"; +import { DataGrid } from "@rsone/components"; +import ApproveButton from "@/components/button/ApproveButton"; +import RejectButton from "@/components/button/RejectButton"; + +import { StatsInputStatusInpColumn } from "@/components/util/column/input/StatsInputStatusInpColumn"; + +// 하수도 통계 관리 > 00. 통계 입력 현황 +const StatsInputStatusInp = () => { + + const rejectFunc = () => { + // 보완 요청 로직 + }; + + const approveFunc = () => { + // 승인 요청 로직 + }; + + const [rowData, setRowData] = useState[]>([]); + + return ( + <> + +
+
+ + +
+
+ + + ); +}; + +export default StatsInputStatusInp; \ No newline at end of file diff --git a/apps/st/src/components/statistics/input/WaterReuseInp.tsx b/apps/st/src/components/statistics/input/WaterReuseInp.tsx new file mode 100644 index 000000000..2479ef030 --- /dev/null +++ b/apps/st/src/components/statistics/input/WaterReuseInp.tsx @@ -0,0 +1,92 @@ +import { useState } from "react"; + +import SearchForm from "@/components/module/SearchForm"; +import { TabDataGrid } from '@rsone/components'; +import SubmitButton from "@/components/button/SumitButton"; +import SaveButton from "@/components/button/SaveButton"; +import LastYearDataButton from "@/components/button/LastYearDataButton"; +import SubmitExceptionButton from "@/components/button/SubmitExceptionButton"; +import UpdateOperateDataButton from "@/components/button/UpdateOperateDataButton" +import AddArticleButton from "@/components/button/AddArticleButton"; +import DeleteArticleButton from "@/components/button/DeleteArticleButton"; +import NotApplicableCheckButton from "@/components/button/NotApplicableCheckButton"; + +import { PublicSewageTreatWaterReuseInpColumn } from "@/components/util/column/input/waterReuse/PublicSewageTreatWaterReuseInpColumn"; +import { RecycledWaterStatusInpColumn } from "@/components/util/column/input/waterReuse/RecycledWaterStatusInpColumn"; +import { RainFallUtilFactStatusInpColumn } from "@/components/util/column/input/waterReuse/RainFallUtilFactStatusInpColumn"; +import { TreatWaterReuseDesignConstStatusInpColumn } from "@/components/util/column/input/waterReuse/TreatWaterReuseDesignConstStatusInpColumn"; +import { TreatWaterReuseBusinessApprStatusInpColumn } from "@/components/util/column/input/waterReuse/TreatWaterReuseBusinessApprStatusInpColumn"; + +// 하수도 통계 관리 > 10. 물 재이용 +const WaterReuseInp = () => { + + // tab list + const WaterReuseInpTabList = [ + PublicSewageTreatWaterReuseInpColumn, + RecycledWaterStatusInpColumn, + RainFallUtilFactStatusInpColumn, + TreatWaterReuseDesignConstStatusInpColumn, + TreatWaterReuseBusinessApprStatusInpColumn + ]; + const SubmitExceptionFunc = () => { + // 예외 제출 로직 + }; + + const getLastYearDataFunc = () => { + // 전연도 데이터 가져오기 로직 + }; + + const submitFunc = () => { + // 제출 로직 + }; + + const saveFunc = () => { + // 저장 로직 + }; + + const updateOperateDataButtonFunc = () => { + // 운영 데이터 업데이트 로직 - tab1. 공공하수처리수 재이용 + }; + + const addArticleFunc = () => { + console.log('save'); + // 항목 추가 로직 + }; + + const deleteArticleFunc = () => { + console.log('save'); + // 항목 제외 로직 + }; + + const [rowDataMap, setRowDataMap] = useState[]>>(new Map()); + const [isNotApplicable, setIsNotApplicable] = useState(false); + + return ( + <> + +
+
+ + + + +
+
+
+
+ + + + +
+
+ + + ); +}; + +export default WaterReuseInp; \ No newline at end of file diff --git a/apps/st/src/components/statistics/management/ExcretaTreatmentFacilityMng.tsx b/apps/st/src/components/statistics/management/ExcretaTreatmentFacilityMng.tsx deleted file mode 100644 index 4b4988240..000000000 --- a/apps/st/src/components/statistics/management/ExcretaTreatmentFacilityMng.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import SearchForm from "@/components/module/SearchForm"; - -const ExcretaTreatmentFacilityMng = () => { - return ( - <> - - - ); -}; - -export default ExcretaTreatmentFacilityMng; \ No newline at end of file diff --git a/apps/st/src/components/statistics/management/NotEnteredStatus.tsx b/apps/st/src/components/statistics/management/NotEnteredStatus.tsx new file mode 100644 index 000000000..723c29b18 --- /dev/null +++ b/apps/st/src/components/statistics/management/NotEnteredStatus.tsx @@ -0,0 +1,41 @@ +import { useState } from "react"; + +import { DataGrid } from "@rsone/components"; +import SearchForm from "@/components/module/SearchForm"; +import type { ColumnConfig } from "@/components/type/DataGrid.type"; + +const NotEnteredStatus = () => { + const [rowData, setRowData] = useState[]>([]); + + const columns: ColumnConfig[] = [ + { name: 'sido', fieldName: '시도', header: { text: '시도' }, mergeCells: true, editable: false, sortdisable: true }, + { name: 'sgg', fieldName: '시군구', header: { text: '시군구' }, mergeCells: true, editable: false, sortdisable: true }, + { name: '1', fieldName: '1', header: { text: '시군구' }, mergeCells: true, editable: false, sortdisable: true }, + { name: '2-1', fieldName: '2-1', header: { text: '2-1' }, mergeCells: true, editable: false, sortdisable: true }, + { name: '2-2', fieldName: '2-2', header: { text: '2-2' }, mergeCells: true, editable: false, sortdisable: true }, + { name: '2-3', fieldName: '2-3', header: { text: '2-3' }, mergeCells: true, editable: false, sortdisable: true }, + { name: '2-4', fieldName: '2-4', header: { text: '2-4' }, mergeCells: true, editable: false, sortdisable: true }, + { name: '3-1', fieldName: '3-1', header: { text: '3-1' }, mergeCells: true, editable: false, sortdisable: true }, + { name: '3-2', fieldName: '3-2', header: { text: '3-2' }, mergeCells: true, editable: false, sortdisable: true }, + ]; + + const columnLayout = [ + 'sido', + 'sgg', + { + name: 'groupA', + hideChildHeaders: false, + header: { text: '미입력 하수도 통계 세부항목(번호)' }, + items: ['1', '2-1', '2-2', '2-3', '2-4', '3-1', '3-2'], + }, + ]; + + return ( + <> + + + + ); +}; + +export default NotEnteredStatus; \ No newline at end of file diff --git a/apps/st/src/components/statistics/management/PrivateSewageFacilityMng.tsx b/apps/st/src/components/statistics/management/PrivateSewageFacilityMng.tsx deleted file mode 100644 index a57011038..000000000 --- a/apps/st/src/components/statistics/management/PrivateSewageFacilityMng.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import SearchForm from "@/components/module/SearchForm"; - -const PrivateSewageFacilityMng = () => { - return ( - <> - - - ); -}; - -export default PrivateSewageFacilityMng; \ No newline at end of file diff --git a/apps/st/src/components/statistics/management/PublicSewageFacilityMng.tsx b/apps/st/src/components/statistics/management/PublicSewageFacilityMng.tsx deleted file mode 100644 index 3097a90c0..000000000 --- a/apps/st/src/components/statistics/management/PublicSewageFacilityMng.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import SearchForm from "@/components/module/SearchForm"; - -const PublicSewageFacilityMng = () => { - return ( - <> - - - ); -}; - -export default PublicSewageFacilityMng; \ No newline at end of file diff --git a/apps/st/src/components/statistics/management/PumpingStationMng.tsx b/apps/st/src/components/statistics/management/PumpingStationMng.tsx deleted file mode 100644 index 1d8dadd65..000000000 --- a/apps/st/src/components/statistics/management/PumpingStationMng.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import SearchForm from "@/components/module/SearchForm"; - -const PumpingStationMng = () => { - return ( - <> - - - ); -}; - -export default PumpingStationMng; \ No newline at end of file diff --git a/apps/st/src/components/statistics/management/SewageExcretaRelatedMng.tsx b/apps/st/src/components/statistics/management/SewageExcretaRelatedMng.tsx deleted file mode 100644 index 4305c6344..000000000 --- a/apps/st/src/components/statistics/management/SewageExcretaRelatedMng.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import SearchForm from "@/components/module/SearchForm"; - -const SewageExcretaRelatedMng = () => { - return ( - <> - - - ); -}; - -export default SewageExcretaRelatedMng; \ No newline at end of file diff --git a/apps/st/src/components/statistics/management/SewageExcretaSlurgeMng.tsx b/apps/st/src/components/statistics/management/SewageExcretaSlurgeMng.tsx deleted file mode 100644 index ea3bd241a..000000000 --- a/apps/st/src/components/statistics/management/SewageExcretaSlurgeMng.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import SearchForm from "@/components/module/SearchForm"; - -const SewageExcretaSlurgeMng = () => { - return ( - <> - - - ); -}; - -export default SewageExcretaSlurgeMng; \ No newline at end of file diff --git a/apps/st/src/components/statistics/management/SewagePipeMaintainMng.tsx b/apps/st/src/components/statistics/management/SewagePipeMaintainMng.tsx deleted file mode 100644 index 34cf8752a..000000000 --- a/apps/st/src/components/statistics/management/SewagePipeMaintainMng.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import SearchForm from "@/components/module/SearchForm"; - -const SewagePipeMaintainMng = () => { - return ( - <> - - - ); -}; - -export default SewagePipeMaintainMng; \ No newline at end of file diff --git a/apps/st/src/components/statistics/management/SewageReservoirPumpMng.tsx b/apps/st/src/components/statistics/management/SewageReservoirPumpMng.tsx deleted file mode 100644 index 16b177121..000000000 --- a/apps/st/src/components/statistics/management/SewageReservoirPumpMng.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import SearchForm from "@/components/module/SearchForm"; - -const SewageReservoirPumpMng = () => { - return ( - <> - - - ); -}; - -export default SewageReservoirPumpMng; \ No newline at end of file diff --git a/apps/st/src/components/statistics/management/SewerageFinanceMng.tsx b/apps/st/src/components/statistics/management/SewerageFinanceMng.tsx deleted file mode 100644 index 85e35d715..000000000 --- a/apps/st/src/components/statistics/management/SewerageFinanceMng.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import SearchForm from "@/components/module/SearchForm"; - -const SewerageFinanceMng = () => { - return ( - <> - - - ); -}; - -export default SewerageFinanceMng; \ No newline at end of file diff --git a/apps/st/src/components/statistics/management/SewerageServiceLevelMng.tsx b/apps/st/src/components/statistics/management/SewerageServiceLevelMng.tsx deleted file mode 100644 index a9c2adf00..000000000 --- a/apps/st/src/components/statistics/management/SewerageServiceLevelMng.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import SearchForm from "../../module/SearchForm"; - -const SewerageServiceLevelMng = () => { - return ( - <> - - - ); -}; - -export default SewerageServiceLevelMng; \ No newline at end of file diff --git a/apps/st/src/components/statistics/management/StatisticsInputManagement.tsx b/apps/st/src/components/statistics/management/StatisticsInputManagement.tsx new file mode 100644 index 000000000..be4158ede --- /dev/null +++ b/apps/st/src/components/statistics/management/StatisticsInputManagement.tsx @@ -0,0 +1,39 @@ +import { useState } from "react"; + +import { DataGrid } from "@rsone/components"; +import SearchForm from "@/components/module/SearchForm"; +import type { ColumnConfig } from "@/components/type/DataGrid.type"; + +const StatisticsInputManagement = () => { + const [rowData, setRowData] = useState[]>([]); + + const columns: ColumnConfig[] = [ + { name: 'sido', fieldName: '시도', header: { text: '시도' }, mergeCells: true, editable: false, sortdisable: true }, + { name: 'sgg', fieldName: '시군구', header: { text: '시군구' }, mergeCells: true, editable: false, sortdisable: true }, + { name: '구분', fieldName: '구분', header: { text: '구분' }, editable: false, sortdisable: true }, + { name: '광역자치단체', fieldName: '광역자치단체', header: { text: '광역자치단체' }, editable: false, sortdisable: true }, + { name: '공단', fieldName: '공단', header: { text: '공단' }, editable: false, sortdisable: true }, + { name: '처리내역', fieldName: '처리내역', header: { text: '처리내역' }, editable: false, sortdisable: true }, + ]; + + const columnLayout = [ + 'sido', + 'sgg', + '구분', + { + name: 'groupA', + hideChildHeaders: false, + header: { text: '검토내역' }, + items: ['광역자치단체', '공단', '처리내역'], + }, + ]; + + return ( + <> + + + + ); +}; + +export default StatisticsInputManagement; \ No newline at end of file diff --git a/apps/st/src/components/statistics/management/StatisticsPopulationManagement.tsx b/apps/st/src/components/statistics/management/StatisticsPopulationManagement.tsx new file mode 100644 index 000000000..3d979e9d6 --- /dev/null +++ b/apps/st/src/components/statistics/management/StatisticsPopulationManagement.tsx @@ -0,0 +1,11 @@ +import SearchForm from "@/components/module/SearchForm"; + +const StatisticsPopulationManagement = () => { + return ( + <> + + + ); +}; + +export default StatisticsPopulationManagement; \ No newline at end of file diff --git a/apps/st/src/components/statistics/management/StatisticsReviewHistory.tsx b/apps/st/src/components/statistics/management/StatisticsReviewHistory.tsx new file mode 100644 index 000000000..96bde4c32 --- /dev/null +++ b/apps/st/src/components/statistics/management/StatisticsReviewHistory.tsx @@ -0,0 +1,39 @@ +import { useState } from "react"; + +import { DataGrid } from "@rsone/components"; +import SearchForm from "@/components/module/SearchForm"; +import type { ColumnConfig } from "@/components/type/DataGrid.type"; + +const StatisticsReviewHistory = () => { + const [rowData, setRowData] = useState[]>([]); + + const columns: ColumnConfig[] = [ + { name: 'sido', fieldName: '시도', header: { text: '시도' }, mergeCells: true, editable: false, sortdisable: true }, + { name: 'sgg', fieldName: '시군구', header: { text: '시군구' }, mergeCells: true, editable: false, sortdisable: true }, + { name: '구분', fieldName: '구분', header: { text: '구분' }, editable: false, sortdisable: true }, + { name: '광역자치단체', fieldName: '광역자치단체', header: { text: '광역자치단체' }, editable: false, sortdisable: true }, + { name: '공단', fieldName: '공단', header: { text: '공단' }, editable: false, sortdisable: true }, + { name: '처리내역', fieldName: '처리내역', header: { text: '처리내역' }, editable: false, sortdisable: true }, + ]; + + const columnLayout = [ + 'sido', + 'sgg', + '구분', + { + name: 'groupA', + hideChildHeaders: false, + header: { text: '검토내역' }, + items: ['광역자치단체', '공단', '처리내역'], + }, + ]; + + return ( + <> + + + + ); +}; + +export default StatisticsReviewHistory; \ 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 deleted file mode 100644 index 32e134538..000000000 --- a/apps/st/src/components/statistics/management/StatsInputSatusMng.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import SearchForm from "@/components/module/SearchForm"; -import DataGrid from "@/components/module/DataGrid"; -import ApproveButton from "@/components/button/ApproveButton"; -import RejectButton from "@/components/button/RejectButton"; - -const StatsInputSatusMng = () => { - const 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: '반려 사유' } }, - ]; - - const columnLayout = [ - 'sido', - 'sgg', - 'menu_nm', - { - name: 'groupA', - hideChildHeaders: false, - header: { text: '하수도사업자' }, - items: columns.filter((c) => ['sido', 'sgg', 'menu_nm'].includes(c.name) === false).map((m) => m.name), - }, - ]; - - const approveFunc = () => { - console.log('appoved'); - }; - - const rejectFunc = () => { - console.log('reject'); - }; - - return ( - <> - -
- - -
- - - ); -}; - -export default StatsInputSatusMng; \ No newline at end of file diff --git a/apps/st/src/components/statistics/management/WaterReuseMng.tsx b/apps/st/src/components/statistics/management/WaterReuseMng.tsx deleted file mode 100644 index b4afc8254..000000000 --- a/apps/st/src/components/statistics/management/WaterReuseMng.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import SearchForm from "../../module/SearchForm"; - -const WaterReuseMng = () => { - return ( - <> - - - ); -}; - -export default WaterReuseMng; \ No newline at end of file diff --git a/apps/st/src/components/statistics/review/ExcretaTreatmentFacilityRvw.tsx b/apps/st/src/components/statistics/review/ExcretaTreatmentFacilityRvw.tsx new file mode 100644 index 000000000..961f0a79e --- /dev/null +++ b/apps/st/src/components/statistics/review/ExcretaTreatmentFacilityRvw.tsx @@ -0,0 +1,38 @@ +import { useState } from "react"; + +import { TabDataGrid } from "@rsone/components"; +import SearchForm from "@/components/module/SearchForm"; +import SaveButton from "@/components/button/SaveButton"; +import ApproveButton from "@/components/button/ApproveButton"; +import ReviewOpinionButton from "@/components/button/ReviewOpinionButton"; +import ReturnButton from "@/components/button/ReturnButton"; + +import { ExcretaTreatmentFacilityInfoList } from "@/components/util/column/review/ExcretaTreatmentFacilityRvwColumn"; + +// 하수도 통계 관리 07.분뇨처리 시설 +const ExcretaTreatmentFacilityRvw = () => { + + const [rowDataMap, setRowDataMap] = useState[]>>(new Map()); + + const approveFunc = () => console.log('approveFunc'); + const saveFunc = () => console.log('submit'); + const reviewOpinionFunc = () => console.log('reviewOpinionFunc'); + const returnFunc = () => console.log('returnFunc'); + + return ( + <> + +
+
+ + + + +
+
+ + + ); +}; + +export default ExcretaTreatmentFacilityRvw; \ No newline at end of file diff --git a/apps/st/src/components/statistics/review/PrivateSewageFacilityRvw.tsx b/apps/st/src/components/statistics/review/PrivateSewageFacilityRvw.tsx new file mode 100644 index 000000000..619085736 --- /dev/null +++ b/apps/st/src/components/statistics/review/PrivateSewageFacilityRvw.tsx @@ -0,0 +1,52 @@ +import { useState } from "react"; + +import { TabDataGrid } from "@rsone/components"; +import SearchForm from "@/components/module/SearchForm"; +import SaveButton from "@/components/button/SaveButton"; +import ApproveButton from "@/components/button/ApproveButton"; +import ReviewOpinionButton from "@/components/button/ReviewOpinionButton"; +import ReturnButton from "@/components/button/ReturnButton"; + +import { PrivateSewageFacilityInfoListRvw } from "@/components/util/column/review/PrivateSewageFacilityColumn"; + +// 하수도 통계 관리 08.개인하수처리시설 +const PrivateSewageFacilityRvw = () => { + const [rowDataMap, setRowDataMap] = useState[]>>(new Map()); + + const approveFunc = () => { + console.log('approveFunc'); + // 승인 로직 + }; + + const saveFunc = () => { + console.log('submit'); + // 저장 로직 + }; + + const reviewOpinionFunc = () => { + console.log('reviewOpinionFunc'); + // 검토의견 작성 + }; + + const returnFunc = () => { + console.log('returnFunc'); + // 반려 + }; + + return ( + <> + +
+
+ + + + +
+
+ + + ); +}; + +export default PrivateSewageFacilityRvw; diff --git a/apps/st/src/components/statistics/review/PublicSewageFacilityRvw.tsx b/apps/st/src/components/statistics/review/PublicSewageFacilityRvw.tsx new file mode 100644 index 000000000..b929e1e3f --- /dev/null +++ b/apps/st/src/components/statistics/review/PublicSewageFacilityRvw.tsx @@ -0,0 +1,48 @@ +import { useState } from "react"; + +import { DataGrid } from "@rsone/components"; +import SearchForm from "@/components/module/SearchForm"; +import SaveButton from "@/components/button/SaveButton"; +import ApproveButton from "@/components/button/ApproveButton"; +import ReviewOpinionButton from "@/components/button/ReviewOpinionButton"; +import ReturnButton from "@/components/button/ReturnButton"; + +import { PublicSewageFacilityInfoList } from "@/components/util/column/review/PublicSewageFacilityRvwColumn"; + +// 하수도 통계 관리 06.공공하수처리시설 +const PublicSewageFacilityRvw = () => { + const [rowData, setRowData] = useState[]>([]); + + const approveFunc = () => { + console.log('approveFunc'); + }; + + const saveFunc = () => { + console.log('submit'); + }; + + const reviewOpinionFunc = () => { + console.log('reviewOpinionFunc'); + }; + + const returnFunc = () => { + console.log('returnFunc'); + }; + + return ( + <> + +
+
+ + + + +
+
+ + + ); +}; + +export default PublicSewageFacilityRvw; \ No newline at end of file diff --git a/apps/st/src/components/statistics/review/PumpingStationRvw.tsx b/apps/st/src/components/statistics/review/PumpingStationRvw.tsx index cb6407e25..8a9df541a 100644 --- a/apps/st/src/components/statistics/review/PumpingStationRvw.tsx +++ b/apps/st/src/components/statistics/review/PumpingStationRvw.tsx @@ -1,12 +1,37 @@ -import SearchForm from "@/components/module/SearchForm"; +import { useState } from "react"; +import { DataGrid } from "@rsone/components"; +import SearchForm from "@/components/module/SearchForm"; +import SaveButton from "@/components/button/SaveButton"; +import ApproveButton from "@/components/button/ApproveButton"; +import ReviewOpinionButton from "@/components/button/ReviewOpinionButton"; +import ReturnButton from "@/components/button/ReturnButton"; + +import { PumpingStationInfoList } from "@/components/util/column/review/PumpingStationRvwColumn"; + +// 하수도 통계 관리 05.펌프장 const PumpingStationRvw = () => { - + const [rowData, setRowData] = useState[]>([]); + + const approveFunc = () => { console.log('approveFunc'); }; + const saveFunc = () => { console.log('submit'); }; + const reviewOpinionFunc = () => { console.log('reviewOpinionFunc'); }; + const returnFunc = () => { console.log('returnFunc'); }; + return ( <> - + +
+
+ + + + +
+
+ ); -} +}; export default PumpingStationRvw; \ No newline at end of file diff --git a/apps/st/src/components/statistics/review/SewageExcretaRelatedRvw.tsx b/apps/st/src/components/statistics/review/SewageExcretaRelatedRvw.tsx new file mode 100644 index 000000000..bd594d334 --- /dev/null +++ b/apps/st/src/components/statistics/review/SewageExcretaRelatedRvw.tsx @@ -0,0 +1,53 @@ +import { useState } from "react"; + +import { TabDataGrid } from "@rsone/components"; +import SearchForm from "@/components/module/SearchForm"; +import SaveButton from "@/components/button/SaveButton"; +import ApproveButton from "@/components/button/ApproveButton"; +import ReviewOpinionButton from "@/components/button/ReviewOpinionButton"; +import ReturnButton from "@/components/button/ReturnButton"; + +import { SewageExcretaRelatedInfoListRvw } from "@/components/util/column/review/SewageExcretaRelatedRvwColumn"; + +// 하수도 통계 관리 12. 하수 및 분뇨 관련 산업 +const SewageExcretaRelatedRvw = () => { + const [rowDataMap, setRowDataMap] = useState[]>>(new Map()); + + const approveFunc = () => { + console.log('approveFunc'); + // 승인 로직 + }; + + const saveFunc = () => { + console.log('submit'); + // 저장 로직 + }; + + const reviewOpinionFunc = () => { + console.log('reviewOpinionFunc'); + // 검토의견 작성 + }; + + const returnFunc = () => { + console.log('returnFunc'); + // 반려 + }; + + return ( + <> + +
+
+ + + + +
+
+ + + ); +}; + + +export default SewageExcretaRelatedRvw; diff --git a/apps/st/src/components/statistics/review/SewageExcretaSlurgeRvw.tsx b/apps/st/src/components/statistics/review/SewageExcretaSlurgeRvw.tsx new file mode 100644 index 000000000..d0dbcb383 --- /dev/null +++ b/apps/st/src/components/statistics/review/SewageExcretaSlurgeRvw.tsx @@ -0,0 +1,52 @@ +import { useState } from "react"; + +import { TabDataGrid } from "@rsone/components"; +import SearchForm from "@/components/module/SearchForm"; +import SaveButton from "@/components/button/SaveButton"; +import ApproveButton from "@/components/button/ApproveButton"; +import ReviewOpinionButton from "@/components/button/ReviewOpinionButton"; +import ReturnButton from "@/components/button/ReturnButton"; + +import { SewageExcretaSludgeInfoListRvw } from "@/components/util/column/review/SewageExcretaSlurgeRvwColumn"; + +// 하수도 통계 관리 11. 하수 및 분뇨 찌꺼기(슬러지) 처리 +const SewageExcretaSlurgeRvw = () => { + const [rowDataMap, setRowDataMap] = useState[]>>(new Map()); + + const approveFunc = () => { + console.log('approveFunc'); + // 승인 로직 + }; + + const saveFunc = () => { + console.log('submit'); + // 저장 로직 + }; + + const reviewOpinionFunc = () => { + console.log('reviewOpinionFunc'); + // 검토의견 작성 + }; + + const returnFunc = () => { + console.log('returnFunc'); + // 반려 + }; + + return ( + <> + +
+
+ + + + +
+
+ + + ); +}; + +export default SewageExcretaSlurgeRvw; \ No newline at end of file diff --git a/apps/st/src/components/statistics/review/SewagePipeMaintainRvw.tsx b/apps/st/src/components/statistics/review/SewagePipeMaintainRvw.tsx index 68e4e9e7d..03549083e 100644 --- a/apps/st/src/components/statistics/review/SewagePipeMaintainRvw.tsx +++ b/apps/st/src/components/statistics/review/SewagePipeMaintainRvw.tsx @@ -1,11 +1,52 @@ -import SearchForm from "@/components/module/SearchForm"; +import { useState } from "react"; +import { TabDataGrid } from "@rsone/components"; +import SearchForm from "@/components/module/SearchForm"; +import SaveButton from "@/components/button/SaveButton"; +import ApproveButton from "@/components/button/ApproveButton"; +import ReviewOpinionButton from "@/components/button/ReviewOpinionButton"; +import ReturnButton from "@/components/button/ReturnButton"; + +import { SewagePipeMaintainColumnInfoList } from "@/components/util/column/review/SewagePipeMaintainRvwColumn"; + +// 하수도 통계 관리 03. 하수관로 유지 관리 const SewagePipeMaintainRvw = () => { + const [rowDataMap, setRowDataMap] = useState[]>>(new Map()); + + const approveFunc = () => { + console.log('approveFunc'); + // 승인 로직 + }; + + const saveFunc = () => { + console.log('submit'); + // 저장 로직 + }; + + const reviewOpinionFunc = () => { + console.log('reviewOpinionFunc'); + // 검토의견 작성 + }; + + const returnFunc = () => { + console.log('returnFunc'); + // 반려 + }; + return ( <> - + +
+
+ + + + +
+
+ ); -} +}; export default SewagePipeMaintainRvw; \ No newline at end of file diff --git a/apps/st/src/components/statistics/review/SewagePipeRvw.tsx b/apps/st/src/components/statistics/review/SewagePipeRvw.tsx index c6fbd817e..ccfc62355 100644 --- a/apps/st/src/components/statistics/review/SewagePipeRvw.tsx +++ b/apps/st/src/components/statistics/review/SewagePipeRvw.tsx @@ -1,11 +1,100 @@ +import { useState, useMemo } from "react"; +import TabDataGrid from "@/components/module/TabDataGrid"; import SearchForm from "@/components/module/SearchForm"; +import SaveButton from "@/components/button/SaveButton"; +import ApproveButton from "@/components/button/ApproveButton"; +import ReviewOpinionButton from "@/components/button/ReviewOpinionButton"; +import ReturnButton from "@/components/button/ReturnButton"; +import { LoadingWithAnimation } from "@common/components"; +import { useAlertStore } from "@/shared/lib"; +import { statisticsManagementApi } from "@/entities/statistics/api/statistics-management-service"; +import { SewagePipeColumnInfoListRvw } from "@/components/util/column/review/SewagePipeRvwColumn"; +import type { SearchFormValues } from "@/components/type/SearchForm.type"; +import { toGridRows } from "@/components/util/grid/gridFunction"; + +// 하수도 통계 검토 02.하수관로 const SewagePipeRvw = () => { + const { setMessage: alert } = useAlertStore((state) => state); + const [rowDataMap, setRowDataMap] = useState>(new Map()); + const [isLoading, setIsLoading] = useState(false); + + const columnInfo = useMemo(() => { + return SewagePipeColumnInfoListRvw.map((tab: any) => ({ + ...tab, + columns: tab.columns.map((col: any) => ({ + ...col, + editable: false as const, + styleName: col.styleName ?? ("rg-cell-readonly" as const), + })), + })); + }, []); + + const handleReset = () => setRowDataMap(new Map()); + + const handleSearch = async (values: SearchFormValues) => { + if (!values.sido || values.sido === 'ALL') { + alert('시도를 선택해주세요.'); + return; + } + + try { + setIsLoading(true); + const response = await statisticsManagementApi.getSewagePipeReviewList({ + sido: values.sido, + gugun: values.gugun === 'ALL' ? '' : values.gugun, + bsnmCd: values.bsnmCd, + yr: values.year, + }); + + if (response.status === 'success' && response.data) { + const rawList = response.data; + const newRowDataMap = new Map(); + + newRowDataMap.set('a', toGridRows(rawList)); + + setRowDataMap(newRowDataMap); + } else { + handleReset(); + alert(response.message || "데이터가 없습니다."); + } + } catch (error) { + console.error("조회 에러:", error); + alert('데이터 조회 중 오류가 발생했습니다.'); + handleReset(); + } finally { + setIsLoading(false); + } + }; + return ( - <> - - +
+ + +
+
+ {}} /> + {}} /> + {}} /> + {}} /> +
+
+ {isLoading && ( +
+
+ +
+
+ )} + + +
); -} +}; export default SewagePipeRvw; \ No newline at end of file diff --git a/apps/st/src/components/statistics/review/SewageReservoirPumpRvw.tsx b/apps/st/src/components/statistics/review/SewageReservoirPumpRvw.tsx index f5ce87d59..a2b5e8de7 100644 --- a/apps/st/src/components/statistics/review/SewageReservoirPumpRvw.tsx +++ b/apps/st/src/components/statistics/review/SewageReservoirPumpRvw.tsx @@ -1,11 +1,52 @@ -import SearchForm from "@/components/module/SearchForm"; +import { useState } from "react"; +import { TabDataGrid } from "@rsone/components"; +import SearchForm from "@/components/module/SearchForm"; +import SaveButton from "@/components/button/SaveButton"; +import ApproveButton from "@/components/button/ApproveButton"; +import ReviewOpinionButton from "@/components/button/ReviewOpinionButton"; +import ReturnButton from "@/components/button/ReturnButton"; + +import { SewageReservoirPumpInfoListRvw } from "@/components/util/column/review/SewageReservoirPumpRvwColumn"; + +// 하수도 통계 관리 04. 하수저류 시설 및 빗물 펌프장 const SewageReservoirPumpRvw = () => { + const [rowDataMap, setRowDataMap] = useState[]>>(new Map()); + + const approveFunc = () => { + console.log('approveFunc'); + // 승인 로직 + }; + + const saveFunc = () => { + console.log('submit'); + // 저장 로직 + }; + + const reviewOpinionFunc = () => { + console.log('reviewOpinionFunc'); + // 검토의견 작성 + }; + + const returnFunc = () => { + console.log('returnFunc'); + // 반려 + }; + return ( <> - + +
+
+ + + + +
+
+ ); -} +}; -export default SewageReservoirPumpRvw; \ No newline at end of file +export default SewageReservoirPumpRvw; diff --git a/apps/st/src/components/statistics/review/SewerageFinanceRvw.tsx b/apps/st/src/components/statistics/review/SewerageFinanceRvw.tsx new file mode 100644 index 000000000..68a5b6cc0 --- /dev/null +++ b/apps/st/src/components/statistics/review/SewerageFinanceRvw.tsx @@ -0,0 +1,54 @@ +import { useState } from "react"; + +import { TabDataGrid } from "@rsone/components"; +import SearchForm from "@/components/module/SearchForm"; +import SaveButton from "@/components/button/SaveButton"; +import ApproveButton from "@/components/button/ApproveButton"; +import ReviewOpinionButton from "@/components/button/ReviewOpinionButton"; +import ReturnButton from "@/components/button/ReturnButton"; + +import { SewerageFinanceInfoListRvw } from "@/components/util/column/review/SewerageFinanceRvwColumn"; + + +// 하수도 통계 관리 09. 하수도 재정 +const SewerageFinanceRvw = () => { + const [rowDataMap, setRowDataMap] = useState[]>>(new Map()); + + const approveFunc = () => { + console.log('approveFunc'); + // 승인 로직 + }; + + const saveFunc = () => { + console.log('submit'); + // 저장 로직 + }; + + const reviewOpinionFunc = () => { + console.log('reviewOpinionFunc'); + // 검토의견 작성 + }; + + const returnFunc = () => { + console.log('returnFunc'); + // 반려 + }; + + return ( + <> + +
+
+ + + + +
+
+ + + ); +}; + + +export default SewerageFinanceRvw; \ No newline at end of file diff --git a/apps/st/src/components/statistics/review/SeweragePenetrationRateRvw.tsx b/apps/st/src/components/statistics/review/SeweragePenetrationRateRvw.tsx index 17df757f0..bf6ef9911 100644 --- a/apps/st/src/components/statistics/review/SeweragePenetrationRateRvw.tsx +++ b/apps/st/src/components/statistics/review/SeweragePenetrationRateRvw.tsx @@ -1,11 +1,115 @@ +import { useState, useMemo } from "react"; +import DataGrid from "@/components/module/DataGrid"; import SearchForm from "@/components/module/SearchForm"; +import SaveButton from "@/components/button/SaveButton"; +import ApproveButton from "@/components/button/ApproveButton"; +import ReviewOpinionButton from "@/components/button/ReviewOpinionButton"; +import ReturnButton from "@/components/button/ReturnButton"; +import { LoadingWithAnimation } from "@common/components"; +import { useAlertStore } from "@/shared/lib"; +import { SeweragePenetrationRateInfoList } from "@/components/util/column/review/SeweragePenetrationRateRvwColumn"; +import type { SewerageSupplyRateReviewItem } from "@/entities/statistics/model/statistics-sewerage-penetration-review-interface"; +import { statisticsManagementApi } from "@/entities/statistics/api/statistics-management-service"; +import type { SearchFormValues } from "@/components/type/SearchForm.type"; +import { toGridRows } from "@/components/util/grid/gridFunction"; + +// 하수도 통계 검토 01.하수도 보급률 const SeweragePenetrationRateRvw = () => { + const { setMessage: alert } = useAlertStore((state) => state); + const [rowData, setRowData] = useState([]); + const [isLoading, setIsLoading] = useState(false); + + const columnInfo = useMemo(() => { + const { columns, columnLayout, fixedColumns, rowStartNum } = SeweragePenetrationRateInfoList; + return { + columnLayout, + fixedColumns, + rowStartNum, + columns: columns.map((col) => ({ + ...col, + editable: false as const, + styleName: col.styleName ?? ("rg-cell-readonly" as const), + })), + }; + }, []); + + const handleReset = () => { + setRowData([]); + }; + + const handleSearch = async (values: SearchFormValues) => { + if (values.sido === '' || values.sido === 'ALL') { + alert('시도를 선택해주세요.'); + return; + } + + try { + setIsLoading(true); + + const response = await statisticsManagementApi.getSewerageSupplyRateReviewList({ + sido: values.sido, + gugun: values.gugun === 'ALL' ? '' : values.gugun, + bsnmCd: values.bsnmCd, + yr: values.year, + }); + + if (response.status === 'success' && response.data) { + const rawList = response.data.sewerageSupplyRateList; + const convertedData: SewerageSupplyRateReviewItem[] = rawList.map((item) => ({ + ...item, + speclCntrplnAr: !item.speclCntrplnAr ? '해당없음' : item.speclCntrplnAr, + })); + + setRowData(toGridRows(convertedData)); + } else { + setRowData([]); + alert(response.message || "데이터가 없습니다."); + } + } catch (error) { + console.error("조회 중 에러 발생:", error); + alert('데이터 조회 중 오류가 발생했습니다.'); + } finally { + setIsLoading(false); + } + }; + + const approveFunc = () => console.log('approveFunc'); + const saveFunc = () => console.log('submit'); + const reviewOpinionFunc = () => console.log('reviewOpinionFunc'); + const returnFunc = () => console.log('returnFunc'); + return ( - <> - - +
+ + +
+
+ + + + +
+
+ + {isLoading && ( +
+
+ +
+
+ )} + + +
); -} +}; export default SeweragePenetrationRateRvw; \ No newline at end of file diff --git a/apps/st/src/components/statistics/review/SewerageServiceLevelRvw.tsx b/apps/st/src/components/statistics/review/SewerageServiceLevelRvw.tsx new file mode 100644 index 000000000..e8aa771df --- /dev/null +++ b/apps/st/src/components/statistics/review/SewerageServiceLevelRvw.tsx @@ -0,0 +1,53 @@ +import { useState } from "react"; + +import { TabDataGrid } from "@rsone/components"; +import SearchForm from "@/components/module/SearchForm"; +import SaveButton from "@/components/button/SaveButton"; +import ApproveButton from "@/components/button/ApproveButton"; +import ReviewOpinionButton from "@/components/button/ReviewOpinionButton"; +import ReturnButton from "@/components/button/ReturnButton"; + +import { SewerageServiceLevelInfoListRvw } from "@/components/util/column/review/SewerageServiceLevelRvwColumn"; + +// 하수도 통계 관리 13. 하수도 서비스 수준 +const SewerageServiceLevelRvw = () => { + const [rowDataMap, setRowDataMap] = useState[]>>(new Map()); + + const approveFunc = () => { + console.log('approveFunc'); + // 승인 로직 + }; + + const saveFunc = () => { + console.log('submit'); + // 저장 로직 + }; + + const reviewOpinionFunc = () => { + console.log('reviewOpinionFunc'); + // 검토의견 작성 + }; + + const returnFunc = () => { + console.log('returnFunc'); + // 반려 + }; + + return ( + <> + +
+
+ + + + +
+
+ + + ); +}; + + +export default SewerageServiceLevelRvw; diff --git a/apps/st/src/components/statistics/review/StatsInputSatusRvw.tsx b/apps/st/src/components/statistics/review/StatsInputSatusRvw.tsx deleted file mode 100644 index d89ff9290..000000000 --- a/apps/st/src/components/statistics/review/StatsInputSatusRvw.tsx +++ /dev/null @@ -1,11 +0,0 @@ -import SearchForm from "@/components/module/SearchForm"; - -const StatsInputSatusRvw = () => { - return ( - <> - - - ); -} - -export default StatsInputSatusRvw; \ No newline at end of file diff --git a/apps/st/src/components/statistics/review/StatsInputStatusRvw.tsx b/apps/st/src/components/statistics/review/StatsInputStatusRvw.tsx new file mode 100644 index 000000000..7a7bb4788 --- /dev/null +++ b/apps/st/src/components/statistics/review/StatsInputStatusRvw.tsx @@ -0,0 +1,113 @@ +import { useState, useMemo } from "react"; +import { DataGrid } from "@rsone/components"; +import SearchForm from "@/components/module/SearchForm"; +import SaveButton from "@/components/button/SaveButton"; +import ApproveButton from "@/components/button/ApproveButton"; +import ReviewOpinionButton from "@/components/button/ReviewOpinionButton"; +import ReturnButton from "@/components/button/ReturnButton"; +import { LoadingWithAnimation } from "@common/components"; + +import { useAlertStore } from "@/shared/lib"; +import { StatsInputStatusInfoList } from "@/components/util/column/review/StatsInputStatusRvwColumn"; +import { statisticsManagementApi } from "@/entities/statistics/api/statistics-management-service"; +import type { StatsInputStatusReviewItem } from "@/entities/statistics/model/statistics-input-review-interface"; +import { toGridRows } from "@/components/util/grid/gridFunction"; + +// 하수도 통계 검토 00. 통계 입력 현황 +const StatsInputStatusRvw = () => { + const { setMessage: alert } = useAlertStore((state) => state); + const [rowData, setRowData] = useState([]); + const [isLoading, setIsLoading] = useState(false); + + const columnInfo = useMemo(() => { + const { columns, columnLayout } = StatsInputStatusInfoList; + return { + columnLayout, + columns: columns.map((col) => ({ + ...col, + editable: false as const, + })), + }; + }, []); + + const handleReset = () => { + setRowData([]); + }; + + const handleSearch = async (values: any) => { + if (!values.sido || values.sido === 'ALL') { + alert('시도를 선택해주세요.'); + return; + } + + try { + setIsLoading(true); + + const response = await statisticsManagementApi.getStatsInputStatusList({ + sido: values.sido, + gugun: values.gugun === 'ALL' ? '' : values.gugun, + yr: values.year || values.yr, + bsnmCd: values.bsnmCd + }); + + if (response.status === 'success' && response.data) { + const rawList = response.data.sewerageSupplyRateList || response.data; + + const convertedData: StatsInputStatusReviewItem[] = rawList.map((item: any) => ({ + ...item, + status: item.status || '미확인', + })); + + setRowData(toGridRows(convertedData)); + } else { + setRowData([]); + alert(response.message || "조회된 데이터가 없습니다."); + } + } catch (error) { + console.error("입력현황 조회 중 에러 발생:", error); + alert('데이터 조회 중 오류가 발생했습니다.'); + setRowData([]); + } finally { + setIsLoading(false); + } + }; + + const approveFunc = () => console.log('approveFunc'); + const saveFunc = () => console.log('submit'); + const reviewOpinionFunc = () => console.log('reviewOpinionFunc'); + const returnFunc = () => console.log('returnFunc'); + + return ( +
+ + +
+
+ + + + +
+
+ + {isLoading && ( +
+
+ +
+
+ )} + + +
+ ); +}; + +export default StatsInputStatusRvw; \ No newline at end of file diff --git a/apps/st/src/components/statistics/review/WaterReuseRvw.tsx b/apps/st/src/components/statistics/review/WaterReuseRvw.tsx new file mode 100644 index 000000000..e642a4448 --- /dev/null +++ b/apps/st/src/components/statistics/review/WaterReuseRvw.tsx @@ -0,0 +1,51 @@ +import { useState } from "react"; + +import { TabDataGrid } from "@rsone/components"; +import SearchForm from "@/components/module/SearchForm"; +import SaveButton from "@/components/button/SaveButton"; +import ApproveButton from "@/components/button/ApproveButton"; +import ReviewOpinionButton from "@/components/button/ReviewOpinionButton"; +import ReturnButton from "@/components/button/ReturnButton"; + +import { WaterReuseInfoList } from "@/components/util/column/review/WaterReuseRvwColumn"; + +const WaterReuseRvw = () => { + const [rowDataMap, setRowDataMap] = useState[]>>(new Map()); + + const approveFunc = () => { + console.log('approveFunc'); + // 승인 로직 + }; + + const saveFunc = () => { + console.log('submit'); + // 저장 로직 + }; + + const reviewOpinionFunc = () => { + console.log('reviewOpinionFunc'); + // 검토의견 작성 + }; + + const returnFunc = () => { + console.log('returnFunc'); + // 반려 + }; + + return ( + <> + +
+
+ + + + +
+
+ + + ); +}; + +export default WaterReuseRvw; \ No newline at end of file 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..e470b07fa --- /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..1171f7b2c --- /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..6eb3f2991 --- /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/management/SewagePipeMng.tsx b/apps/st/src/components/statistics/total/PumpingStationTot.tsx similarity index 50% rename from apps/st/src/components/statistics/management/SewagePipeMng.tsx rename to apps/st/src/components/statistics/total/PumpingStationTot.tsx index d49184044..fd4cc679d 100644 --- a/apps/st/src/components/statistics/management/SewagePipeMng.tsx +++ b/apps/st/src/components/statistics/total/PumpingStationTot.tsx @@ -1,11 +1,11 @@ import SearchForm from "@/components/module/SearchForm"; -const SewagePipeMng = () => { +const PumpingStationTot = () => { return ( <> - + ); }; -export default SewagePipeMng; \ No newline at end of file +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..adcc810ad --- /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..c384f1461 --- /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..12e71151d --- /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..232ef7883 --- /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..6016a7930 --- /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..a73eaba2f --- /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/management/SeweragePenetrationRateMng.tsx b/apps/st/src/components/statistics/total/SeweragePenetrationRateTot.tsx similarity index 78% rename from apps/st/src/components/statistics/management/SeweragePenetrationRateMng.tsx rename to apps/st/src/components/statistics/total/SeweragePenetrationRateTot.tsx index ffd10f966..fe266e743 100644 --- a/apps/st/src/components/statistics/management/SeweragePenetrationRateMng.tsx +++ b/apps/st/src/components/statistics/total/SeweragePenetrationRateTot.tsx @@ -1,10 +1,15 @@ 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"; +import { useState } from "react"; -const SeweragePenetrationRate = () => { +const SeweragePenetrationRateTot = () => { const columns = [ { name: 'sido', fieldName: '시도', header: { text: '시도' } }, - { name: 'sgg', fieldName: '구군', header: { text: '구군' } }, + { name: 'sgg', fieldName: '시군구', header: { text: '시군구' } }, { name: 'rvbsn', fieldName: '수계', header: { text: '수계' } }, { name: 'bstrm', fieldName: '지류', header: { text: '지류' } }, { name: 'mdbsn', fieldName: '세부단위유역', header: { text: '세부단위유역' } }, @@ -98,12 +103,40 @@ const SeweragePenetrationRate = () => { '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'); + // 예외 제출 로직 + }; + + const [rowData, setRowData] = useState[]>([]); + return ( <> - - + +
+ + + + +
+ ); }; -export default SeweragePenetrationRate; \ No newline at end of file +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..2f6b5131e --- /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..567797983 --- /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/statistics/verification/ManagementVerificationRule.tsx b/apps/st/src/components/statistics/verification/ManagementVerificationRule.tsx new file mode 100644 index 000000000..1d5dfd169 --- /dev/null +++ b/apps/st/src/components/statistics/verification/ManagementVerificationRule.tsx @@ -0,0 +1,482 @@ +"use client"; + +import { useState } from "react"; +import { DataGrid } from "@rsone/components"; +import SearchForm from "@/components/module/SearchForm"; +import { + Button, + Input, + Label, + Select, + SelectTrigger, + SelectContent, + SelectItem, + SelectValue, + Checkbox, +} from "@common/components"; +import { verificationHistoryColumnInfo } from "@/entities/statistics/model/verification-history-column"; +import { verificationHistoryMockData } from "@/entities/statistics/model/verification-history-mock"; + +interface VerificationFormValues { + mu: string; + percentile: string; + a: string; + c: string; + verificationYear: string; + checkboxOptions: { + sidoDistribution: boolean; + countyDistribution: boolean; + countyDistributionUserManagement: boolean; + lowerDistribution: boolean; + sidoServiceAvailability: boolean; + individualService: boolean; + legalServiceUsage: boolean; + excessUse: boolean; + lowerOrgStatistics: boolean; + studentStatistics: boolean; + developmentStatistics: boolean; + lowerManagementEnterprise: boolean; + }; +} + +const ManagementVerificationRule = () => { + const [verificationHistoryRowData, setVerificationHistoryRowData] = + useState[]>( + verificationHistoryMockData, + ); + + const [formValues, setFormValues] = useState({ + mu: "0", + percentile: "0", + a: "0", + c: "0", + verificationYear: "2025", + checkboxOptions: { + sidoDistribution: false, + countyDistribution: false, + countyDistributionUserManagement: false, + lowerDistribution: false, + sidoServiceAvailability: false, + individualService: false, + legalServiceUsage: false, + excessUse: false, + lowerOrgStatistics: false, + studentStatistics: false, + developmentStatistics: false, + lowerManagementEnterprise: false, + }, + }); + + const handleCheckboxChange = ( + key: keyof VerificationFormValues["checkboxOptions"], + ) => { + setFormValues((prev) => ({ + ...prev, + checkboxOptions: { + ...prev.checkboxOptions, + [key]: !prev.checkboxOptions[key], + }, + })); + }; + + const handleInputChange = ( + key: keyof Omit, + value: string, + ) => { + setFormValues((prev) => ({ + ...prev, + [key]: value, + })); + }; + + const handleSubmit = () => { + console.log("검증 실행:", formValues); + }; + + const handleReset = () => { + console.log("초기화"); + }; + + return ( +
+
+ {/* 통계 검증 섹션 */} +
+

+ 통계 검증 +

+ +
+ {/* 왼쪽: 검증 변수 */} +
+

+ 검증 변수 +

+ +
+
+ + + handleInputChange( + "mu", + e.target.value, + ) + } + className="flex-1" + /> +
+ +
+ + + handleInputChange( + "percentile", + e.target.value, + ) + } + className="flex-1" + /> +
+ +
+ + + handleInputChange( + "a", + e.target.value, + ) + } + className="flex-1" + /> +
+ +
+ + + handleInputChange( + "c", + e.target.value, + ) + } + className="flex-1" + /> +
+ +
+ + +
+
+
+ + {/* 오른쪽: 검증 대상 */} +
+

+ 검증 대상 +

+ +
+
+
+ + handleCheckboxChange( + "sidoDistribution", + ) + } + /> + +
+ +
+ + handleCheckboxChange( + "countyDistribution", + ) + } + /> + +
+ +
+ + handleCheckboxChange( + "countyDistributionUserManagement", + ) + } + /> + +
+ +
+ + handleCheckboxChange( + "lowerDistribution", + ) + } + /> + +
+
+ +
+
+ + handleCheckboxChange( + "sidoServiceAvailability", + ) + } + /> + +
+ +
+ + handleCheckboxChange( + "individualService", + ) + } + /> + +
+ +
+ + handleCheckboxChange( + "sidoServiceAvailability", + ) + } + /> + +
+ +
+ + handleCheckboxChange( + "individualService", + ) + } + /> + +
+
+ +
+
+ + handleCheckboxChange( + "lowerOrgStatistics", + ) + } + /> + +
+ +
+ + handleCheckboxChange( + "studentStatistics", + ) + } + /> + +
+ +
+ + handleCheckboxChange( + "lowerOrgStatistics", + ) + } + /> + +
+ +
+ + handleCheckboxChange( + "studentStatistics", + ) + } + /> + +
+
+ +
+
+ + handleCheckboxChange( + "lowerManagementEnterprise", + ) + } + /> + +
+
+
+
+
+
+ + {/* 버튼 */} +
+ + +
+ + {/* 검증 이력 섹션 */} +
+

+ 검증 이력 +

+ +
+
+
+ ); +}; + +export default ManagementVerificationRule; diff --git a/apps/st/src/components/type/DataGrid.type.ts b/apps/st/src/components/type/DataGrid.type.ts index 01284cef9..4d31facbe 100644 --- a/apps/st/src/components/type/DataGrid.type.ts +++ b/apps/st/src/components/type/DataGrid.type.ts @@ -1,7 +1,115 @@ +import React from "react"; +import { ValueType } from "realgrid"; + +// RealGrid 타입 정의 +/** setColumns에 전달하는 컬럼 정보 (footer 배열/단일 객체 포함) */ +export type RealGridColumnInfo = { + name: string; + fieldName: string; + header: { text: string }; + editable?: boolean; + valueType?: import("realgrid").ValueType; + dataFormat?: string; + styleName?: string; + mergeRule?: { criteria: 'value' }; + footer?: { + text?: string; + expression?: 'sum' | 'avg' | 'count' | 'max' | 'min'; + numberFormat?: string; + valueCallback?: (grid: GridView, column: DataColumn, footerIndex: number, columnFooter: ColumnFooter, value: number | string | undefined) => number | string; + } | Array<{ + text?: string; + expression?: 'sum' | 'avg' | 'count' | 'max' | 'min'; + numberFormat?: string; + valueCallback?: (grid: GridView, column: DataColumn, footerIndex: number, columnFooter: ColumnFooter, value: number | string | undefined) => number | string; + }>; +}; + +/** setColumns 메서드가 있는 GridView */ +export type GridViewWithSetColumns = GridView & { setColumns?: (columns: RealGridColumnInfo[]) => void }; + +/** footer 배열 요소에 valueCallback 할당용 */ +export type FooterItemWithCallback = { valueCallback?: (grid: GridView, column: DataColumn, footerIndex: number, columnFooter: ColumnFooter, value: number | string | undefined) => number | string }; + +export type GridView = { + getSummary(field: string, summary: 'sum' | 'avg' | 'count' | 'max' | 'min'): number | undefined; + getDataSource(): { + getRowCount(): number; + getValue(rowIndex: number, field: string): number | string; + getDataRowId(dataRow: number): number | string; + }; + getDataRow(itemIndex: number): number; +}; + +export type MergeCallback = (grid: GridView, column: DataColumn, itemIndex: number, value: number | string | null | undefined) => number | null; + +export type DataColumn = { + name: string; + fieldName: string; +}; + +export type ColumnFooter = { + text?: string; + expression?: string; + numberFormat?: string; +}; + +/** 정렬 시 같은 그룹 내에서는 orderByField 순서 유지, 그룹 간에는 정렬 필드로 비교. */ +export type SortPreserveGroupOrder = { + groupField: string; + orderByField?: string; +}; + +/** RealGrid valueCallback 시그니처 (계산 필드용) */ +export type DataFieldValueCallback = ( + prod: unknown, + dataRow: number, + fieldName: string, + fieldNames: string[], + values: unknown[] +) => number | string | undefined; + +/** 계산 필드 설정 (computedFieldCallbacks.ts 참고) */ +export type ComputedFieldConfig = + | { fields: string[]; type: "sum" | "avg" } + | { numeratorFields: string[]; denominatorFields: string[]; type: "pblswrgPopltnSuprtRate" } + | { numeratorFields: string[]; denominatorFields: string[]; type: "adtrtPopltnSuprtRate" } + | { numeratorFields: string[]; denominatorFields: string[]; thirdFields: string[], type: "swrgInstlrtRate" }; + export type ColumnConfig = { name: string; fieldName: string; header: { text: string }; + /** 계산 필드: fields 합계/평균 등. valueCallback으로 dataProvider.setFields 시 전달 */ + computed?: ComputedFieldConfig; + editable?: boolean; // 셀 편집 가능 여부 + /** 그리드에서 컬럼 숨김 (수식용 필드 등) */ + visible?: boolean; + /** 셀에 적용할 CSS 클래스명 (예: 'rg-cell-readonly', 'rg-cell-total') */ + styleName?: string; + mergeCells?: boolean; + sortdisable?: boolean; + sortPreserveGroupOrder?: SortPreserveGroupOrder; + valueType?: ValueType; + dataFormat?: string; + footer?: { + text?: string; + expression?: 'sum' | 'avg' | 'count' | 'max' | 'min'; + numberFormat?: string; + valueCallback?: (grid: GridView, column: DataColumn, footerIndex: number, columnFooter: ColumnFooter, value: number | string | undefined) => number | string; + multiRow?: boolean; // 여러 행 footer 사용 여부 + } | Array<{ + text?: string; + expression?: 'sum' | 'avg' | 'count' | 'max' | 'min'; + numberFormat?: string; + valueCallback?: (grid: GridView, column: DataColumn, footerIndex: number, columnFooter: ColumnFooter, value: number | string | undefined) => number | string; + }>, // RealGrid에서 footer를 배열로 설정할 수 있음 +}; + +export type FooterUserSpan = { + rowspan?: number; // 병합할 행 수 + colspan?: number; // 병합할 컬럼 수 + text?: string; // 표시할 텍스트 }; export type ColumnLayoutItem = string | { @@ -9,6 +117,10 @@ export type ColumnLayoutItem = string | { hideChildHeaders?: boolean; header: { text: string }; items: (string | ColumnLayoutItem)[]; + footerUserSpans?: FooterUserSpan[]; // footer 병합 설정 +} | { + column: string; // 컬럼 이름 (footerUserSpans 사용 시) + footerUserSpans?: FooterUserSpan[]; // footer 병합 설정 }; export type ColumnInfo = { @@ -16,11 +128,50 @@ export type ColumnInfo = { columnLayout: ColumnLayoutItem[]; }; +export type ColumnInfoList = { + tabKey: string; // 탭을 식별하는 고유 키 (필수), server(api)에 전송할 변수 값 + columns?: ColumnConfig[]; // 일반 그리드용 (isSplitGrid가 false일 때) + columnLayout?: ColumnLayoutItem[]; // 일반 그리드용 (isSplitGrid가 false일 때) + rowStartNum?: number; + fixedColumns?: number; + tabLabel?: string; // 탭에 표시될 레이블 + isSplitGrid?: boolean; // 5:5 분리형 그리드 유무 + left?: ColumnInfoList; // SplitDataGrid용 왼쪽 그리드 정보 + right?: ColumnInfoList; // SplitDataGrid용 오른쪽 그리드 정보 +} + export type DataGridProps = { columnInfo: ColumnInfo; - rows?: Record[]; rowStartNum?: number; // 헤더 행 개수 (1부터 시작, 예: 2면 1행과 2행까지 헤더로 간주) - isExcelImport?: boolean; // 엑셀 가져오기 유무무 - onRowsChange?: (rows: Record[]) => void; + isExcelImport?: boolean; // 엑셀 가져오기 유무 + isExcelExport?: boolean; + onRowsChange?: (rows: Record[]) => void; fixedColumns?: number; // 고정할 컬럼 개수 (좌측부터) + showHeaderCheckbox?: boolean; // 헤더 체크박스 표시 여부 (기본값: true) + rowData: Record[]; + setRowData: React.Dispatch[]>>; +}; + +export type TabDataGridProps = { + columnInfo: ColumnInfoList[]; + rows?: Record[]; + isExcelImport?: boolean; // 엑셀 가져오기 유무 + isExcelExport?: boolean; + onRowsChange?: (rows: Record[]) => void; + showHeaderCheckbox?: boolean; // 헤더 체크박스 표시 여부 (기본값: true) + rowDataMap: Map[]>; + setRowDataMap: React.Dispatch[]>>>; + isSplitGrid?: boolean; // 5:5 분리형 그리드 유무 +}; + +export type SplitDataGridProps = { + leftColumnInfo: ColumnInfo; + rightColumnInfo: ColumnInfo; + leftFixedColumns?: number; + rightFixedColumns?: number; + showHeaderCheckbox?: boolean; + leftRowData: Record[]; + rightRowData: Record[]; + setLeftRowData: React.Dispatch[]>>; + setRightRowData: React.Dispatch[]>>; }; \ No newline at end of file diff --git a/apps/st/src/components/type/SearchForm.type.ts b/apps/st/src/components/type/SearchForm.type.ts index 50bd72a5d..b6fd4211d 100644 --- a/apps/st/src/components/type/SearchForm.type.ts +++ b/apps/st/src/components/type/SearchForm.type.ts @@ -1,26 +1,28 @@ export type SearchFormValues = { sido?: string; - sgg?: string; + gugun?: string; year?: string; order?: string; fctName?: string; + bsnmCd?: string; }; export type SearchFormProps = { sido?: boolean; - sgg?: boolean; + gugun?: boolean; year?: boolean; order?: boolean; fctName?: boolean; userMenual?: boolean; onSubmit?: (values: SearchFormValues) => void; onReset?: () => void; + onValuesChange?: (values: SearchFormValues) => void; defaultValues?: SearchFormValues; }; -export const defaultSearchFormProps: Required> = { +export const defaultSearchFormProps: Required> = { sido: true, - sgg: true, + gugun: true, year: true, order: false, fctName: false, diff --git a/apps/st/src/components/util/column/input/ExcretaTreatFactInpColumn.ts b/apps/st/src/components/util/column/input/ExcretaTreatFactInpColumn.ts new file mode 100644 index 000000000..4896a2194 --- /dev/null +++ b/apps/st/src/components/util/column/input/ExcretaTreatFactInpColumn.ts @@ -0,0 +1,107 @@ +import { footerInt, footerDecimal1, footerDecimal3 } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + +/** + * 하수도 통계 관리 > 07. 분뇨처리시설 + * 단일 Tab + */ +export const ExcretaTreatFactInpColumn = { + columns: [ + // 행정구역 + { name: 'sido', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'gugun', fieldName: '시군구', header: { text: '시군구' }, footer: { text: '합계' } }, + + // 기본 정보 + { name: 'ecscs_nm', fieldName: '시설명', header: { text: '시설명' } }, + { name: 'locplcs', fieldName: '소재지', header: { text: '소재지' } }, + { name: 'telno', fieldName: '전화번호', header: { text: '전화번호' } }, + + // 날짜 정보 + { name: 'comp_dt', fieldName: '준공일 yyyy-mm-dd', header: { text: '준공일 yyyy-mm-dd' } }, + { name: 'rn_dt', fieldName: '가동개시일 yyyy-mm-dd', header: { text: '가동개시일 yyyy-mm-dd' } }, + + // 사업비/용량 정보 + { name: 'wct', fieldName: '사업비(백만원)', header: { text: '사업비(백만원)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'ecscs_cpcty', fieldName: '시설용량(m³/일)', header: { text: '시설용량(m³/일)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'process_cpcty', fieldName: '처리량(m³/일)', header: { text: '처리량(m³/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'opr_daycnt', fieldName: '가동일수(일/년)', header: { text: '가동일수(일/년)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 처리 공법 및 연계 + { name: 'trt_msrc', fieldName: '처리공법', header: { text: '처리공법' } }, + { name: 'cntc_prcplt', fieldName: '연계처리장명', header: { text: '연계처리장명' } }, + { name: 'assct_trt_cpc', fieldName: '연계처리량(m³/일)', header: { text: '연계처리량(m³/일)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 유입수질 + { name: 'infl_wqlt_bod', fieldName: 'BOD', header: { text: 'BOD' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'infl_wqlt_toc', fieldName: 'TOC', header: { text: 'TOC' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'infl_wqlt_ss', fieldName: 'SS', header: { text: 'SS' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'infl_wqlt_tn', fieldName: 'T-N', header: { text: 'T-N' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'infl_wqlt_tp', fieldName: 'T-P', header: { text: 'T-P' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'infl_wqlt_clfgrpcnt', fieldName: '대장균군수', header: { text: '대장균군수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 방류수질 + { name: 'effl_wqlt_bod', fieldName: 'BOD', header: { text: 'BOD' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'effl_wqlt_toc', fieldName: 'TOC', header: { text: 'TOC' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'effl_wqlt_ss', fieldName: 'SS', header: { text: 'SS' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'effl_wqlt_tn', fieldName: 'T-N', header: { text: 'T-N' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'effl_wqlt_tp', fieldName: 'T-P', header: { text: 'T-P' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'effl_wqlt_clfgrpcnt', fieldName: '대장균군수', header: { text: '대장균군수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 방류수역 + { name: 'rvbsn', fieldName: '수계', header: { text: '수계' } }, + { name: 'bstrm', fieldName: '지류', header: { text: '지류' } }, + { name: 'lgbsn', fieldName: '중권역', header: { text: '중권역' } }, + { name: 'mdmbsn', fieldName: '소권역', header: { text: '소권역' } }, + + // 운영 정보 + { name: 'oper_mth', fieldName: '운영주체(자체/공기업/민간위탁)', header: { text: '운영주체(자체/공기업/민간위탁)' } }, + { name: 'cntc_prcplt_cpc', fieldName: '직원총수(명)', header: { text: '직원총수(명)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'safe_acdnt_co', fieldName: '안전사고건수(건/인/년)', header: { text: '안전사고건수(건/인/년)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 비고 + { name: 'rmk', fieldName: '비고', header: { text: '비고' } }, + ], + + columnLayout: [ + { + name: 'adminGroup', + hideChildHeaders: false, + header: { text: '행정구역' }, + items: ['sido', 'gugun'], + }, + 'ecscs_nm', + 'locplcs', + 'telno', + 'comp_dt', + 'rn_dt', + 'wct', + 'ecscs_cpcty', + 'process_cpcty', + 'opr_daycnt', + 'trt_msrc', + 'cntc_prcplt', + 'assct_trt_cpc', + { + name: 'influenceGroup', + hideChildHeaders: false, + header: { text: '유입수질(mg/L, 개/mL)' }, + items: ['infl_wqlt_bod', 'infl_wqlt_toc', 'infl_wqlt_ss', 'infl_wqlt_tn', 'infl_wqlt_tp', 'infl_wqlt_clfgrpcnt'], + }, + { + name: 'effluentGroup', + hideChildHeaders: false, + header: { text: '방류수질(mg/L, 개/mL)' }, + items: ['effl_wqlt_bod', 'effl_wqlt_toc', 'effl_wqlt_ss', 'effl_wqlt_tn', 'effl_wqlt_tp', 'effl_wqlt_clfgrpcnt'], + }, + { + name: 'dischargeGroup', + hideChildHeaders: false, + header: { text: '방류수역' }, + items: ['rvbsn', 'bstrm', 'lgbsn', 'mdmbsn'], + }, + 'oper_mth', + 'cntc_prcplt_cpc', + 'safe_acdnt_co', + 'rmk', + ] +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/PublicSewageFactInpColumn.ts b/apps/st/src/components/util/column/input/PublicSewageFactInpColumn.ts new file mode 100644 index 000000000..d070c9b9d --- /dev/null +++ b/apps/st/src/components/util/column/input/PublicSewageFactInpColumn.ts @@ -0,0 +1,211 @@ +import { footerInt, footerDecimal1, footerDecimal3 } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + +/** + * 하수도 통계 관리 > 06. 공공하수처리시설 + * 단일 Tab + */ +export const PublicSewageFactInpColumn = { + columns: [ + // 행정구역 + { name: 'sidoCd', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'gugun', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } }, + + // 시설명 + { name: 'bscFctNm', fieldName: '시설명(500m³/일 이상/미만)', header: { text: '시설명(500m³/일 이상/미만)' }, editable: false }, + + // 소재지 + { name: 'addr', fieldName: '소재지', header: { text: '소재지' }, editable: false }, + + // 시설용량 + { name: 'fctCpc', fieldName: '시설용량(500m³/일 이상/미만)', header: { text: '시설용량(500m³/일 이상/미만)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // (m³/일) + { name: 'fctCpc1', fieldName: '물리적', header: { text: '물리적' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'fctCpc2', fieldName: '생물학적', header: { text: '생물학적' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'fctCpc3', fieldName: '고도', header: { text: '고도' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 유입하수량 + { name: 'totInflAmntAve', fieldName: '유입하수량(500m³/일 이상/미만)', header: { text: '유입하수량(500m³/일 이상/미만)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // (m³/일) + { name: 'pInflAmntAve', fieldName: '물리적', header: { text: '물리적' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'oInflAmntAve', fieldName: '생물학적', header: { text: '생물학적' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'aInflAmntAve', fieldName: '고도', header: { text: '고도' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 방류량 + { name: 'totTputAve', fieldName: '방류량(500m³/일 이상/미만)', header: { text: '방류량(500m³/일 이상/미만)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 방류량 - (m³/일) + { name: 'pTputAve', fieldName: '물리적', header: { text: '물리적' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'oTputAve', fieldName: '생물학적', header: { text: '생물학적' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'aTputAve', fieldName: '고도', header: { text: '고도' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 수질 + { name: 'inflAssctBfrWqltBod', fieldName: '유입BOD', header: { text: '유입BOD' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'efflWqltBod', fieldName: '방류BOD', header: { text: '방류BOD' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'inflAssctBfrWqltToc', fieldName: '유입TOC', header: { text: '유입TOC' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'efflWqltToc', fieldName: '방류TOC', header: { text: '방류TOC' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'inflAssctBfrWqltSs', fieldName: '유입SS', header: { text: '유입SS' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'efflWqltSs', fieldName: '방류SS', header: { text: '방류SS' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'inflAssctBfrWqltTn', fieldName: '유입T-N', header: { text: '유입T-N' }, editable: false, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'efflWqltTn', fieldName: '방류T-N', header: { text: '방류T-N' }, editable: false, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'inflAssctBfrWqltTp', fieldName: '유입T-P', header: { text: '유입T-P' }, editable: false, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'efflWqltTp', fieldName: '방류T-P', header: { text: '방류T-P' }, editable: false, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'inflAssctBfrWqltClfgrpcnt', fieldName: '유입총대장균군수', header: { text: '유입총대장균군수' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'efflWqltClfgrpcnt', fieldName: '방류총대장균군수', header: { text: '방류총대장균군수' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 처리특성 + { name: 'tmEfficiency', fieldName: '처리효율(%)', header: { text: '처리효율(%)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'pcLoad', fieldName: '처리부하량(kgBOD/D)', header: { text: '처리부하량(kgBOD/D)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'trtFrfHanNm', fieldName: '처리방법', header: { text: '처리방법' }, editable: false }, + + // 연계처리량 + { name: 'excretionsTot', fieldName: '분뇨', header: { text: '분뇨' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'stkrsngWstwtrTot', fieldName: '축산', header: { text: '축산' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'lchtTot', fieldName: '침출수', header: { text: '침출수' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'etcTot', fieldName: '기타', header: { text: '기타' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 시설정보 + { name: 'tel', fieldName: '전화번호', header: { text: '전화번호' }, editable: false }, + { name: 'rnDt', fieldName: '준공일', header: { text: '준공일' }, editable: false }, + { name: 'compDt', fieldName: '가동개시일', header: { text: '가동개시일' }, editable: false }, + { name: 'tot', fieldName: '사업비', header: { text: '사업비' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 방류수 소독방법 + { name: 'dsfctNm', fieldName: '방류수 소독방법', header: { text: '방류수 소독방법' }, editable: false }, + + // 방류수역 + { name: 'wbsnNm', fieldName: '수계', header: { text: '수계' }, editable: false }, + { name: 'rvNm', fieldName: '지류', header: { text: '지류' }, editable: false }, + { name: 'mdmbsn', fieldName: '세부단위구역', header: { text: '세부단위구역' }, editable: false }, + { name: 'efflWqltStndrdNm', fieldName: '지역구분', header: { text: '지역구분' }, editable: false }, + + // 방류수역 > 중권역 + { name: 'lgbsn', fieldName: '명칭', header: { text: '명칭' }, editable: false }, + { name: 'lgbsnBod', fieldName: '목표수질(BOD)', header: { text: '목표수질(BOD)' }, editable: false }, + + // 에너지 자립률 + { name: 'toeRt', fieldName: '에너지자립률(%)', header: { text: '에너지자립률(%)' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 에너지 자체 생산량 + { name: 'useQnty', fieldName: '에너지자체생산량(TOE)', header: { text: '에너지자체생산량(TOE)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 연간 총 에너지 사용량 + { name: 'toeSum', fieldName: '계(TOE)', header: { text: '계(TOE)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'elcpwAmnt', fieldName: '총 전력사용량(kWh)', header: { text: '총 전력사용량(kWh)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'toeQnty', fieldName: '자체 에너지사용량(TOE)', header: { text: '자체 에너지사용량(TOE)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 하수처리량당 CO2배출량 + { name: 'co2Emissions', fieldName: '하수처리량당 CO2배출량(kgCO2/m³)', header: { text: '하수처리량당 CO2배출량(kgCO2/m³)' }, editable: false, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + + // 운영주체 + { name: 'oprClsNm', fieldName: '운영주체(자체/공기업/민간대행)', header: { text: '운영주체(자체/공기업/민간대행)' }, editable: false }, + + // 위탁현황 + { name: 'trstOprEtpNm', fieldName: '위탁업체명', header: { text: '위탁업체명' } }, + { name: 'yrstpTrstCst', fieldName: '위탁비용(백만원/년)', header: { text: '위탁비용(백만원/년)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'trstOprDt', fieldName: '위탁계약기간(yyyy-mm-dd~yyyy-mm-dd)', header: { text: '위탁계약기간(yyyy-mm-dd~yyyy-mm-dd)' } }, + + // 기타 + { name: 'cntrTrtFctNm', fieldName: '중심처리시설', header: { text: '중심처리시설' } }, + { name: 'totWtrQrm', fieldName: '직원총수(명)', header: { text: '직원총수(명)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'safeAcdntCo', fieldName: '안전사고건수', header: { text: '안전사고건수' }, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'rmk', fieldName: '비고', header: { text: '비고' } }, + ], + + columnLayout: [ + { + name: 'administrative', + hideChildHeaders: false, + header: { text: '행정구역' }, + items: ['sidoCd', 'gugun'], + }, + 'bscFctNm', + 'addr', + 'fctCpc', + { + name: 'facilityCapacityDetail', + hideChildHeaders: false, + header: { text: '(m³/일)' }, + items: ['fctCpc1', 'fctCpc2', 'fctCpc3'], + }, + 'totInflAmntAve', + { + name: 'influentWaterDetail', + hideChildHeaders: false, + header: { text: '(m³/일)' }, + items: ['pInflAmntAve', 'oInflAmntAve', 'aInflAmntAve'], + }, + 'totTputAve', + { + name: 'effluentWaterDetail', + hideChildHeaders: false, + header: { text: '(m³/일)' }, + items: ['pTputAve', 'oTputAve', 'aTputAve'], + }, + 'inflAssctBfrWqltBod', + 'efflWqltBod', + 'inflAssctBfrWqltToc', + 'efflWqltToc', + 'inflAssctBfrWqltSs', + 'efflWqltSs', + 'inflAssctBfrWqltTn', + 'efflWqltTn', + 'inflAssctBfrWqltTp', + 'efflWqltTp', + 'inflAssctBfrWqltClfgrpcnt', + 'efflWqltClfgrpcnt', + 'tmEfficiency', + 'pcLoad', + 'trtFrfHanNm', + { + name: 'linkedTreatment', + hideChildHeaders: false, + header: { text: '연계처리량(m³/일) (500m³/일 이상/미만)' }, + items: ['excretionsTot', 'stkrsngWstwtrTot', 'lchtTot', 'etcTot'], + }, + 'tel', + 'rnDt', + 'compDt', + 'tot', + 'dsfctNm', + { + name: 'dischargeArea', + hideChildHeaders: false, + header: { text: '방류수역' }, + items: [ + 'wbsnNm', + 'rvNm', + 'mdmbsn', + 'efflWqltStndrdNm', + { + name: 'mediumWatershed', + hideChildHeaders: false, + header: { text: '중권역' }, + items: ['lgbsn', 'lgbsnBod'], + }, + ], + }, + 'toeRt', + 'useQnty', + { + name: 'energy', + hideChildHeaders: false, + header: { text: '연간 총 에너지 사용량(TOE)' }, + items: ['toeSum', 'elcpwAmnt', 'toeQnty'], + }, + 'co2Emissions', + 'oprClsNm', + { + name: 'consignmentStatus', + hideChildHeaders: false, + header: { text: '위탁현황' }, + items: ['trstOprEtpNm', 'yrstpTrstCst', 'trstOprDt'], + }, + 'cntrTrtFctNm', + 'totWtrQrm', + 'safeAcdntCo', + 'rmk', + ] +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/PumpingStationInpColumn.ts b/apps/st/src/components/util/column/input/PumpingStationInpColumn.ts new file mode 100644 index 000000000..6a174daca --- /dev/null +++ b/apps/st/src/components/util/column/input/PumpingStationInpColumn.ts @@ -0,0 +1,143 @@ +import { footerInt, footerDecimal1, footerDecimal3 } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + +/** + * 하수도 통계 관리 > 05. 펌프장 + * 단일 Tab + */ +export const PumpingStationInpColumn = { + columns: [ + // 행정구역 + { name: 'sido', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'sgg', fieldName: '시군구', header: { text: '시군구' }, footer: { text: '합계' } }, + + // 기본 정보 + { name: 'treatmentFacilityName', fieldName: '처리장명', header: { text: '처리장명' } }, + { name: 'facilityName', fieldName: '시설명', header: { text: '시설명' } }, + { name: 'classification', fieldName: '구분(합류식 분류식)', header: { text: '구분(합류식 분류식)' } }, + { name: 'location', fieldName: '위치', header: { text: '위치' } }, + { name: 'operationDate', fieldName: '가동 년월일 yyyy-mm-dd', header: { text: '가동 년월일 yyyy-mm-dd' } }, + { name: 'planDrainageArea', fieldName: '계획배수면적(㎢)', header: { text: '계획배수면적(㎢)' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + + // 계획시간 대 최대배수량 + { name: 'fairWeatherFlow', fieldName: '청천시(A)', header: { text: '청천시(A)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'rainyWeatherFlow', fieldName: '우천시(B)', header: { text: '우천시(B)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'rainwaterFlow', fieldName: '우수량(B-A)', header: { text: '우수량(B-A)' }, footer: footerDecimal1, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 배수능력 > 총 양수량 (오수) + { name: 'sewageTotalPumpCapacity', fieldName: '총양수량(m³/min)', header: { text: '총양수량(m³/min)' }, footer: footerDecimal1, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 배수능력 > 오수펌프(1) + { name: 'sewagePump1Diameter', fieldName: '오수펌프(1) 구경(mm)', header: { text: '구경(mm)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'sewagePump1Capacity', fieldName: '오수펌프(1) 대당 양수량(m³/min·대)', header: { text: '대당 양수량(m³/min·대)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'sewagePump1Count', fieldName: '오수펌프(1) 펌프대수', header: { text: '펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'sewagePump1Reserve', fieldName: '오수펌프(1) 예비펌프대수', header: { text: '예비펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 배수능력 > 오수펌프(2) + { name: 'sewagePump2Diameter', fieldName: '오수펌프(2) 구경(mm)', header: { text: '구경(mm)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'sewagePump2Capacity', fieldName: '오수펌프(2) 대당 양수량(m³/min·대)', header: { text: '대당 양수량(m³/min·대)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'sewagePump2Count', fieldName: '오수펌프(2) 펌프대수', header: { text: '펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'sewagePump2Reserve', fieldName: '오수펌프(2) 예비펌프대수', header: { text: '예비펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 배수능력 > 오수펌프(3) + { name: 'sewagePump3Diameter', fieldName: '오수펌프(3) 구경(mm)', header: { text: '구경(mm)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'sewagePump3Capacity', fieldName: '오수펌프(3) 대당 양수량(m³/min·대)', header: { text: '대당 양수량(m³/min·대)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'sewagePump3Count', fieldName: '오수펌프(3) 펌프대수', header: { text: '펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'sewagePump3Reserve', fieldName: '오수펌프(3) 예비펌프대수', header: { text: '예비펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 배수능력 > 총 양수량 (우수) + { name: 'rainwaterTotalPumpCapacity', fieldName: '총양수량(m³/min)', header: { text: '총양수량(m³/min)' }, footer: footerDecimal1, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 배수능력 > 우수펌프(1) + { name: 'rainwaterPump1Diameter', fieldName: '우수펌프(1) 구경(mm)', header: { text: '구경(mm)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'rainwaterPump1Capacity', fieldName: '우수펌프(1) 대당 양수량(m³/min·대)', header: { text: '대당 양수량(m³/min·대)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'rainwaterPump1Count', fieldName: '우수펌프(1) 펌프대수', header: { text: '펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'rainwaterPump1Reserve', fieldName: '우수펌프(1) 예비펌프대수', header: { text: '예비펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 배수능력 > 우수펌프(2) + { name: 'rainwaterPump2Diameter', fieldName: '우수펌프(2) 구경(mm)', header: { text: '구경(mm)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'rainwaterPump2Capacity', fieldName: '우수펌프(2) 대당 양수량(m³/min·대)', header: { text: '대당 양수량(m³/min·대)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'rainwaterPump2Count', fieldName: '우수펌프(2) 펌프대수', header: { text: '펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'rainwaterPump2Reserve', fieldName: '우수펌프(2) 예비펌프대수', header: { text: '예비펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 배수능력 > 우수펌프(3) + { name: 'rainwaterPump3Diameter', fieldName: '우수펌프(3) 구경(mm)', header: { text: '구경(mm)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'rainwaterPump3Capacity', fieldName: '우수펌프(3) 대당 양수량(m³/min·대)', header: { text: '대당 양수량(m³/min·대)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'rainwaterPump3Count', fieldName: '우수펌프(3) 펌프대수', header: { text: '펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'rainwaterPump3Reserve', fieldName: '우수펌프(3) 예비펌프대수', header: { text: '예비펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 연간전력 사용량 + { name: 'annualPowerUsage', fieldName: '연간전력사용량(kWh/년)', header: { text: '연간전력사용량(kWh/년)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 비고 + { name: 'remarks', fieldName: '비고', header: { text: '비고' } }, + ], + + columnLayout: [ + { + name: 'administrativeArea', + hideChildHeaders: false, + header: { text: '행정구역' }, + items: ['sido', 'sgg'], + }, + 'treatmentFacilityName', + 'facilityName', + 'classification', + 'location', + 'operationDate', + 'planDrainageArea', + { + name: 'planFlowGroup', + hideChildHeaders: false, + header: { text: '계획시간대최대배수량(m³/hr)' }, + items: ['fairWeatherFlow', 'rainyWeatherFlow', 'rainwaterFlow'], + }, + { + name: 'drainageCapability', + hideChildHeaders: false, + header: { text: '배수능력' }, + items: [ + 'sewageTotalPumpCapacity', + { + name: 'sewagePump1Group', + hideChildHeaders: false, + header: { text: '오수펌프(1)' }, + items: ['sewagePump1Diameter', 'sewagePump1Capacity', 'sewagePump1Count', 'sewagePump1Reserve'], + }, + { + name: 'sewagePump2Group', + hideChildHeaders: false, + header: { text: '오수펌프(2)' }, + items: ['sewagePump2Diameter', 'sewagePump2Capacity', 'sewagePump2Count', 'sewagePump2Reserve'], + }, + { + name: 'sewagePump3Group', + hideChildHeaders: false, + header: { text: '오수펌프(3)' }, + items: ['sewagePump3Diameter', 'sewagePump3Capacity', 'sewagePump3Count', 'sewagePump3Reserve'], + }, + 'rainwaterTotalPumpCapacity', + { + name: 'rainwaterPump1Group', + hideChildHeaders: false, + header: { text: '우수펌프(1)' }, + items: ['rainwaterPump1Diameter', 'rainwaterPump1Capacity', 'rainwaterPump1Count', 'rainwaterPump1Reserve'], + }, + { + name: 'rainwaterPump2Group', + hideChildHeaders: false, + header: { text: '우수펌프(2)' }, + items: ['rainwaterPump2Diameter', 'rainwaterPump2Capacity', 'rainwaterPump2Count', 'rainwaterPump2Reserve'], + }, + { + name: 'rainwaterPump3Group', + hideChildHeaders: false, + header: { text: '우수펌프(3)' }, + items: ['rainwaterPump3Diameter', 'rainwaterPump3Capacity', 'rainwaterPump3Count', 'rainwaterPump3Reserve'], + }, + ], + }, + 'annualPowerUsage', + 'remarks', + ] +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/SeweragePenetrationRateInpColumn.ts b/apps/st/src/components/util/column/input/SeweragePenetrationRateInpColumn.ts new file mode 100644 index 000000000..39d3c2234 --- /dev/null +++ b/apps/st/src/components/util/column/input/SeweragePenetrationRateInpColumn.ts @@ -0,0 +1,244 @@ +import { footerInt, footerDecimal1, footerDecimal3, footerAvgDecimal } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; +/** + * 하수도 통계 관리 > 01. 하수도 보급률 + * 단일 Tab + */ +export const SeweragePenetrationRateInpColumn = { + columns: [ + // 행정구역 + { name: 'sido', fieldName: 'sido', header: { text: '시도' }, footer: { text: '합계' }, editable: false }, + { name: 'gugun', fieldName: 'gugun', header: { text: '시군구' }, editable: false }, + { name: 'adstDong', fieldName: 'adstDong', header: { text: '행정구역명' }, editable: false }, + + // 수계 및 권역 + { name: 'rvbsn', fieldName: 'rvbsn', header: { text: '수계' }, editable: false }, + { name: 'bstrm', fieldName: 'bstrm', header: { text: '지류' } }, + + // 수계영향권별분류 + { name: 'mdbsn', fieldName: 'mdbsn', header: { text: '세부단위유역' }, editable: false }, + { name: 'lgbsn', fieldName: 'lgbsn', header: { text: '중권역' }, editable: false }, + { name: 'mdmbsn', fieldName: 'mdmbsn', header: { text: '소권역' }, editable: false }, + + // 특별대책지역 + { name: 'speclCntrplnAr', fieldName: 'speclCntrplnAr', header: { text: '특별대책지역' } }, + + // 행정참고자료인구 + { name: 'totalPopl', fieldName: 'totalPopl', header: { text: '총인구' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'poplSm', fieldName: 'poplSm', header: { text: '내국인' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'frgnrPoplSm', fieldName: 'frgnrPoplSm', header: { text: '외국인' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 행정참고자료면적 + { name: 'area', fieldName: 'area', header: { text: '행정참고자료면적(㎢)' }, footer: footerDecimal3, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + + // 총인구 + { name: 'popltnSm', fieldName: 'popltnSm', header: { text: '총인구(명)' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 총면적 + { + name: 'arSm', + fieldName: 'arSm', + header: { text: '총면적(㎢)' }, + footer: footerDecimal3, + valueType: ValueType.NUMBER, + dataFormat: '#,##0.000', + computed: { fields: ['swgeProcZoneInnerAr', 'swgeProcZoneOuterAr'], type: 'sum' as const }, + styleName: 'rg-cell-total', + }, + + // 하수처리구역 내 > 합계 + { + name: 'swgeProcZoneInnerSm', + fieldName: 'swgeProcZoneInnerSm', + header: { text: '합계' }, + footer: footerInt, + editable: false, + valueType: ValueType.NUMBER, + dataFormat: '#,##0', + computed: { fields: ['pblswrgPrmTrt', 'pblswrgScdTrt', 'pblswrgTertTrt', 'wstrPrmTrt', 'wstrScdTrt', 'wstrTertTrt'], type: 'sum' as const }, + styleName: 'rg-cell-total', + }, + + // 하수처리구역 내 > 공공하수처리인구 + { + name: 'pblswrgPopltnSm', + fieldName: 'pblswrgPopltnSm', + header: { text: '소계' }, + footer: footerInt, + editable: false, + valueType: ValueType.NUMBER, + dataFormat: '#,##0', + computed: { fields: ['pblswrgPrmTrt', 'pblswrgScdTrt', 'pblswrgTertTrt'], type: 'sum' as const }, + styleName: 'rg-cell-total', + }, + { name: 'pblswrgPrmTrt', fieldName: 'pblswrgPrmTrt', header: { text: '물리적처리' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'pblswrgScdTrt', fieldName: 'pblswrgScdTrt', header: { text: '생물학적처리' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'pblswrgTertTrt', fieldName: 'pblswrgTertTrt', header: { text: '고도처리' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 하수처리구역 내 > 폐수처리인구 + { + name: 'wstrPopltnSm', + fieldName: 'wstrPopltnSm', + header: { text: '소계' }, + footer: footerInt, + editable: false, + valueType: ValueType.NUMBER, + dataFormat: '#,##0', + computed: { fields: ['wstrPrmTrt', 'wstrScdTrt', 'wstrTertTrt'], type: 'sum' as const }, + styleName: 'rg-cell-total', + }, + { name: 'wstrPrmTrt', fieldName: 'wstrPrmTrt', header: { text: '물리적처리' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'wstrScdTrt', fieldName: 'wstrScdTrt', header: { text: '생물학적처리' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'wstrTertTrt', fieldName: 'wstrTertTrt', header: { text: '고도처리' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 하수처리구역 내 > 미접속인구 + { name: 'notConectPopltn', fieldName: 'notConectPopltn', header: { text: '미접속인구' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 하수처리구역 내 > 면적 + { name: 'swgeProcZoneInnerAr', fieldName: 'swgeProcZoneInnerAr', header: { text: '면적(㎢)' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + + // 하수처리구역 외 + { + name: 'swgeProcZoneOuterSm', + fieldName: 'swgeProcZoneOuterSm', + header: { text: '합계' }, + footer: footerInt, + editable: false, + valueType: ValueType.NUMBER, + dataFormat: '#,##0', + computed: { fields: ['townUnprocPopltn', 'townSwttPopltn', 'townWrrtnPopltn'], type: 'sum' as const }, + styleName: 'rg-cell-total', + }, + { name: 'townUnprocPopltn', fieldName: 'townUnprocPopltn', header: { text: '미처리인구' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'townSwttPopltn', fieldName: 'townSwttPopltn', header: { text: '오수처리인구' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'townWrrtnPopltn', fieldName: 'townWrrtnPopltn', header: { text: '정화조인구' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'swgeProcZoneOuterAr', fieldName: 'swgeProcZoneOuterAr', header: { text: '면적(㎢)' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + + // 공공하수처리구역 인구보급률 (하수처리구역 내 인구 / 전체 인구 * 100) + { + name: 'pblswrgPopltnSuprt', + fieldName: 'pblswrgPopltnSuprt', + header: { text: '공공하수처리구역인구보급률(%)' }, + footer: footerAvgDecimal, + editable: false, + valueType: ValueType.NUMBER, + dataFormat: '#,##0.0', + computed: { + numeratorFields: ['pblswrgPrmTrt', 'pblswrgScdTrt', 'pblswrgTertTrt', 'wstrPrmTrt', 'wstrScdTrt', 'wstrTertTrt', 'notConectPopltn'], + denominatorFields: ['pblswrgPrmTrt', 'pblswrgScdTrt', 'pblswrgTertTrt', 'wstrPrmTrt', 'wstrScdTrt', 'wstrTertTrt', 'notConectPopltn', 'townUnprocPopltn', 'townSwttPopltn', 'townWrrtnPopltn'], + type: 'pblswrgPopltnSuprtRate' as const + }, + styleName: 'rg-cell-total', + }, + + // 고도처리 인구보급률 (고도처리 인구 / 전체 인구 * 100) + { + name: 'adtrtPopltnSuprt', + fieldName: 'adtrtPopltnSuprt', + header: { text: '고도처리인구보급률(%)' }, + footer: footerAvgDecimal, + editable: false, + valueType: ValueType.NUMBER, + dataFormat: '#,##0.0', + computed: { + numeratorFields: ['pblswrgTertTrt', 'wstrTertTrt'], + denominatorFields: ['pblswrgPrmTrt', 'pblswrgScdTrt', 'pblswrgTertTrt', 'wstrPrmTrt', 'wstrScdTrt', 'wstrTertTrt', 'notConectPopltn', 'townUnprocPopltn', 'townSwttPopltn', 'townWrrtnPopltn'], + type: 'adtrtPopltnSuprtRate' as const + }, + styleName: 'rg-cell-total', + }, + + // 하수도 설치율 + { + name: 'swrgInstlrt', + fieldName: 'swrgInstlrt', + header: { text: '하수도설치율(%)' }, + footer: footerAvgDecimal, + editable: false, + valueType: ValueType.NUMBER, + dataFormat: '#,##0.0', + computed: { + numeratorFields: ['pblswrgPrmTrt', 'pblswrgScdTrt', 'pblswrgTertTrt', 'wstrPrmTrt', 'wstrScdTrt', 'wstrTertTrt', 'notConectPopltn', 'swgeProcZoneOuterSm'], + denominatorFields: ['pblswrgPrmTrt', 'pblswrgScdTrt', 'pblswrgTertTrt', 'wstrPrmTrt', 'wstrScdTrt', 'wstrTertTrt', 'notConectPopltn'], + thirdFields: ['piplSupprt'], + type: 'swrgInstlrtRate' as const + }, + styleName: 'rg-cell-total', + }, + + // 비고 + { name: 'rmk', fieldName: 'rmk', header: { text: '비고' } }, + + { name: 'piplSupprt', fieldName: 'piplSupprt', header: { text: '하수관로보급률(%)' }, visible: false }, + ], + + columnLayout: [ + { + name: 'groupA', + hideChildHeaders: false, + header: { text: '행정구역' }, + items: [ + { column: 'sido', footerUserSpans: [{ colspan: 3 }] }, + 'gugun', + 'adstDong', + ], + }, + 'rvbsn', + 'bstrm', + { + name: 'groupB', + hideChildHeaders: false, + header: { text: '수계영향권별분류' }, + items: ['mdbsn', 'lgbsn', 'mdmbsn'], + }, + 'speclCntrplnAr', + { + name: 'groupC', + hideChildHeaders: false, + header: { text: '행정참고자료인구' }, + items: ['totalPopl', 'poplSm', 'frgnrPoplSm'], + }, + 'area', + 'popltnSm', + 'arSm', + { + name: 'groupD', + hideChildHeaders: false, + header: { text: '하수처리구역내' }, + items: [ + 'swgeProcZoneInnerSm', + { + name: 'pblswrgGroup', + hideChildHeaders: false, + header: { text: '공공하수처리인구(명)' }, + items: ['pblswrgPopltnSm', 'pblswrgPrmTrt', 'pblswrgScdTrt', 'pblswrgTertTrt'], + }, + { + name: 'wstrGroup', + hideChildHeaders: false, + header: { text: '폐수처리인구(명)' }, + items: ['wstrPopltnSm', 'wstrPrmTrt', 'wstrScdTrt', 'wstrTertTrt'], + }, + 'notConectPopltn', + 'swgeProcZoneInnerAr' + ], + }, + { + name: 'groupE', + hideChildHeaders: false, + header: { text: '하수처리구역외' }, + items: [ + 'swgeProcZoneOuterSm', + 'townUnprocPopltn', + 'townSwttPopltn', + 'townWrrtnPopltn', + 'swgeProcZoneOuterAr' + ], + }, + 'pblswrgPopltnSuprt', + 'adtrtPopltnSuprt', + 'swrgInstlrt', + 'rmk', + 'piplSupprt', + ] +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/StatsInputStatusInpColumn.ts b/apps/st/src/components/util/column/input/StatsInputStatusInpColumn.ts new file mode 100644 index 000000000..09d10d3d1 --- /dev/null +++ b/apps/st/src/components/util/column/input/StatsInputStatusInpColumn.ts @@ -0,0 +1,46 @@ +import { ValueType } from "realgrid"; + +/** + * 하수도 통계 관리 > 00. 통계 입력 현황 + * 단일 Tab + */ +export const StatsInputStatusInpColumn = { + 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: '작성률' }, valueType: ValueType.NUMBER }, + { 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' + ], + }, + ] +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/privateSewageFacility/PrivateSewageDesigStatusInpColumn.ts b/apps/st/src/components/util/column/input/privateSewageFacility/PrivateSewageDesigStatusInpColumn.ts new file mode 100644 index 000000000..b7eaf2cca --- /dev/null +++ b/apps/st/src/components/util/column/input/privateSewageFacility/PrivateSewageDesigStatusInpColumn.ts @@ -0,0 +1,73 @@ +import { footerInt, footerDecimal1 } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 > 08.개인하수처리시설 > tab 3. 개인하수도 관리지역 지정공고현황 +export const PrivateSewageDesigStatusInpColumn = { + columns: [ + // 행정구역 + { name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } }, + + // 개인하수도관리지역공고현황 + { name: 'MGMT_AR', fieldName: '공고지역(개소)', header: { text: '공고지역(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'APPN_SCOPE', fieldName: '지정범위(㎢)', header: { text: '지정범위(㎢)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 개인하수도관리지역공고현황 > 정화조 + { name: 'STTY_CNT', fieldName: '개소수', header: { text: '개소수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'STTY_CPC', fieldName: '처리용량(인/일)', header: { text: '처리용량(인/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 개인하수도관리지역공고현황 > 오수시설 + { name: 'WRRTN_CNT', fieldName: '개소수', header: { text: '개소수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'WRRTN_CPC', fieldName: '처리용량(m³/일)', header: { text: '처리용량(m³/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 비고 + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + + // 검증 필드 (hidden) + { name: 'CHK_MGMT_AR', fieldName: 'CHK_MGMT_AR', header: { text: '' } }, + { name: 'CHK_APPN_SCOPE', fieldName: 'CHK_APPN_SCOPE', header: { text: '' } }, + { name: 'CHK_STTY_CNT', fieldName: 'CHK_STTY_CNT', header: { text: '' } }, + { name: 'CHK_STTY_CPC', fieldName: 'CHK_STTY_CPC', header: { text: '' } }, + { name: 'CHK_WRRTN_CNT', fieldName: 'CHK_WRRTN_CNT', header: { text: '' } }, + { name: 'CHK_WRRTN_CPC', fieldName: 'CHK_WRRTN_CPC', header: { text: '' } }, + + { name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' } }, + { name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' } }, + ], + columnLayout: [ + { + name: 'group_admin', + header: { text: '행정구역' }, + items: ['SIDO', 'GUGUN'] + }, + { + name: 'group_notice', + header: { text: '개인하수도관리지역공고현황 (하수도법제34조의2)' }, + items: [ + 'MGMT_AR', + 'APPN_SCOPE', + { + name: 'group_septic', + header: { text: '정화조' }, + items: [ + 'STTY_CNT', + 'STTY_CPC' + ] + }, + { + name: 'group_sewage', + header: { text: '오수시설' }, + items: [ + 'WRRTN_CNT', + 'WRRTN_CPC' + ] + } + ] + }, + 'RMK' + ], + rowStartNum: 3, + fixedColumns: 1, + tabKey: 'privateSewageDesigStatusInp', + tabLabel: '개인하수도 관리지역 지정공고 현황' +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/privateSewageFacility/SepticTankStatusInpColumn.ts b/apps/st/src/components/util/column/input/privateSewageFacility/SepticTankStatusInpColumn.ts new file mode 100644 index 000000000..4d2b45b8f --- /dev/null +++ b/apps/st/src/components/util/column/input/privateSewageFacility/SepticTankStatusInpColumn.ts @@ -0,0 +1,235 @@ +import { footerInt } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 > 08.개인하수처리시설 > tab 2. 정화조 현황 +export const SepticTankStatusInpColumn = { + columns: [ + // 행정구역 + { name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } }, + + // 총 시설 수 + { name: 'TOT_FCLTY_CNT', fieldName: '총 시설 수 (A+D)', header: { text: '총 시설 수 (A+D)' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 하수처리구역 내 > 총계 + { name: 'SPZI_SM', fieldName: '총계 A=(B+C)/2', header: { text: '총계 A=(B+C)/2' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 하수처리구역 내 > 처리방식별 + { name: 'SPZI_MTHD_SM', fieldName: '계(B)', header: { text: '계(B)' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 하수처리구역 내 > 처리방식별 > 부패탱크 + { name: 'SPZI_ROT_NPFL_CNT', fieldName: '자연유하', header: { text: '자연유하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'SPZI_ROT_ENFCT_PUMP_1', fieldName: '강제펌핑식', header: { text: '강제펌핑식' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 하수처리구역 내 > 처리방식별 + { name: 'SPZI_WTROT_NPFL_CNT', fieldName: '살수여상', header: { text: '살수여상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'SPZI_TFM', fieldName: '폭기방법', header: { text: '폭기방법' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'SPZI_ARTM', fieldName: '접촉폭기', header: { text: '접촉폭기' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'SPZI_CTARM', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 하수처리구역 내 > 처리용량별 + { name: 'SPZI_PROC_CPC_SM', fieldName: '계(C)', header: { text: '계(C)' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'SPZI_PROC_CPC_1', fieldName: '10인조이하', header: { text: '10인조이하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'SPZI_PROC_CPC_2', fieldName: '11~20', header: { text: '11~20' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'SPZI_PROC_CPC_3', fieldName: '21~30', header: { text: '21~30' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'SPZI_PROC_CPC_4', fieldName: '31~50', header: { text: '31~50' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'SPZI_PROC_CPC_5', fieldName: '51~199', header: { text: '51~199' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'SPZI_PROC_CPC_6', fieldName: '200~499', header: { text: '200~499' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'SPZI_PROC_CPC_7', fieldName: '500~999', header: { text: '500~999' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'SPZI_PROC_CPC_8', fieldName: '1,000 이상', header: { text: '1,000 이상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 하수처리구역 외 > 총계 + { name: 'SPZO_SM', fieldName: '총계 D=(E+F)/2', header: { text: '총계 D=(E+F)/2' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 하수처리구역 외 > 처리방식별 + { name: 'SPZO_MTHD_SM', fieldName: '계(E)', header: { text: '계(E)' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 하수처리구역 외 > 처리방식별 > 부패탱크 + { name: 'SPZO_ROT_NPFL_CNT', fieldName: '자연유하', header: { text: '자연유하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'SPZO_ROT_ENFCT_PUMP_1', fieldName: '강제펌핑식', header: { text: '강제펌핑식' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 하수처리구역 외 > 처리방식별 + { name: 'SPZO_WTROT_NPFL_CNT', fieldName: '살수여상', header: { text: '살수여상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'SPZO_TFM', fieldName: '폭기방법', header: { text: '폭기방법' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'SPZO_ARTM', fieldName: '접촉폭기', header: { text: '접촉폭기' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'SPZO_CTARM', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 하수처리구역 외 > 처리용량별 + { name: 'SPZO_PROC_CPC_SM', fieldName: '계(F)', header: { text: '계(F)' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'SPZO_PROC_CPC_1', fieldName: '10인조이하', header: { text: '10인조이하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'SPZO_PROC_CPC_2', fieldName: '11~20', header: { text: '11~20' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'SPZO_PROC_CPC_3', fieldName: '21~30', header: { text: '21~30' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'SPZO_PROC_CPC_4', fieldName: '31~50', header: { text: '31~50' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'SPZO_PROC_CPC_5', fieldName: '51~199', header: { text: '51~199' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'SPZO_PROC_CPC_6', fieldName: '200~499', header: { text: '200~499' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'SPZO_PROC_CPC_7', fieldName: '500~999', header: { text: '500~999' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'SPZO_PROC_CPC_8', fieldName: '1,000 이상', header: { text: '1,000 이상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 비고 + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + + // hidden + { name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' } }, + { name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' } }, + + // 검증 필드 (검증 여부 표시용 - 빨간색 표시) + { name: 'CHK_TOT_FCLTY_CNT', fieldName: 'CHK_TOT_FCLTY_CNT', header: { text: '' } }, + { name: 'CHK_SPZI_SM', fieldName: 'CHK_SPZI_SM', header: { text: '' } }, + { name: 'CHK_SPZI_MTHD_SM', fieldName: 'CHK_SPZI_MTHD_SM', header: { text: '' } }, + { name: 'CHK_SPZI_ROT_NPFL_CNT', fieldName: 'CHK_SPZI_ROT_NPFL_CNT', header: { text: '' } }, + { name: 'CHK_SPZI_ROT_ENFCT_PUMP_1', fieldName: 'CHK_SPZI_ROT_ENFCT_PUMP_1', header: { text: '' } }, + { name: 'CHK_SPZI_ROT_ENFCT_PUMP_2', fieldName: 'CHK_SPZI_ROT_ENFCT_PUMP_2', header: { text: '' } }, + { name: 'CHK_SPZI_ROT_ENFCT_PUMP_3', fieldName: 'CHK_SPZI_ROT_ENFCT_PUMP_3', header: { text: '' } }, + { name: 'CHK_SPZI_ROT_TANK_SM', fieldName: 'CHK_SPZI_ROT_TANK_SM', header: { text: '' } }, + { name: 'CHK_SPZI_IMHF_NPFL_CNT', fieldName: 'CHK_SPZI_IMHF_NPFL_CNT', header: { text: '' } }, + { name: 'CHK_SPZI_IMHF_ENFCT_PUMP_1', fieldName: 'CHK_SPZI_IMHF_ENFCT_PUMP_1', header: { text: '' } }, + { name: 'CHK_SPZI_IMHF_ENFCT_PUMP_2', fieldName: 'CHK_SPZI_IMHF_ENFCT_PUMP_2', header: { text: '' } }, + { name: 'CHK_SPZI_IMHF_ENFCT_PUMP_3', fieldName: 'CHK_SPZI_IMHF_ENFCT_PUMP_3', header: { text: '' } }, + { name: 'CHK_SPZI_IMHF_TANK_SM', fieldName: 'CHK_SPZI_IMHF_TANK_SM', header: { text: '' } }, + { name: 'CHK_SPZI_WTROT_NPFL_CNT', fieldName: 'CHK_SPZI_WTROT_NPFL_CNT', header: { text: '' } }, + { name: 'CHK_SPZI_WTROT_ENFCT_PUMP_1', fieldName: 'CHK_SPZI_WTROT_ENFCT_PUMP_1', header: { text: '' } }, + { name: 'CHK_SPZI_WTROT_ENFCT_PUMP_2', fieldName: 'CHK_SPZI_WTROT_ENFCT_PUMP_2', header: { text: '' } }, + { name: 'CHK_SPZI_WTROT_ENFCT_PUMP_3', fieldName: 'CHK_SPZI_WTROT_ENFCT_PUMP_3', header: { text: '' } }, + { name: 'CHK_SPZI_WTROT_TANK_SM', fieldName: 'CHK_SPZI_WTROT_TANK_SM', header: { text: '' } }, + { name: 'CHK_SPZI_TFM', fieldName: 'CHK_SPZI_TFM', header: { text: '' } }, + { name: 'CHK_SPZI_ARTM', fieldName: 'CHK_SPZI_ARTM', header: { text: '' } }, + { name: 'CHK_SPZI_CTARM', fieldName: 'CHK_SPZI_CTARM', header: { text: '' } }, + { name: 'CHK_SPZI_NPFL', fieldName: 'CHK_SPZI_NPFL', header: { text: '' } }, + { name: 'CHK_SPZI_ENFCT_PUMP', fieldName: 'CHK_SPZI_ENFCT_PUMP', header: { text: '' } }, + { name: 'CHK_SPZI_PROC_CPC_SM', fieldName: 'CHK_SPZI_PROC_CPC_SM', header: { text: '' } }, + { name: 'CHK_SPZI_PROC_CPC_1', fieldName: 'CHK_SPZI_PROC_CPC_1', header: { text: '' } }, + { name: 'CHK_SPZI_PROC_CPC_2', fieldName: 'CHK_SPZI_PROC_CPC_2', header: { text: '' } }, + { name: 'CHK_SPZI_PROC_CPC_3', fieldName: 'CHK_SPZI_PROC_CPC_3', header: { text: '' } }, + { name: 'CHK_SPZI_PROC_CPC_4', fieldName: 'CHK_SPZI_PROC_CPC_4', header: { text: '' } }, + { name: 'CHK_SPZI_PROC_CPC_5', fieldName: 'CHK_SPZI_PROC_CPC_5', header: { text: '' } }, + { name: 'CHK_SPZI_PROC_CPC_6', fieldName: 'CHK_SPZI_PROC_CPC_6', header: { text: '' } }, + { name: 'CHK_SPZI_PROC_CPC_7', fieldName: 'CHK_SPZI_PROC_CPC_7', header: { text: '' } }, + { name: 'CHK_SPZI_PROC_CPC_8', fieldName: 'CHK_SPZI_PROC_CPC_8', header: { text: '' } }, + { name: 'CHK_SPZO_SM', fieldName: 'CHK_SPZO_SM', header: { text: '' } }, + { name: 'CHK_SPZO_MTHD_SM', fieldName: 'CHK_SPZO_MTHD_SM', header: { text: '' } }, + { name: 'CHK_SPZO_ROT_NPFL_CNT', fieldName: 'CHK_SPZO_ROT_NPFL_CNT', header: { text: '' } }, + { name: 'CHK_SPZO_ROT_ENFCT_PUMP_1', fieldName: 'CHK_SPZO_ROT_ENFCT_PUMP_1', header: { text: '' } }, + { name: 'CHK_SPZO_ROT_ENFCT_PUMP_2', fieldName: 'CHK_SPZO_ROT_ENFCT_PUMP_2', header: { text: '' } }, + { name: 'CHK_SPZO_ROT_ENFCT_PUMP_3', fieldName: 'CHK_SPZO_ROT_ENFCT_PUMP_3', header: { text: '' } }, + { name: 'CHK_SPZO_ROT_TANK_SM', fieldName: 'CHK_SPZO_ROT_TANK_SM', header: { text: '' } }, + { name: 'CHK_SPZO_IMHF_NPFL_CNT', fieldName: 'CHK_SPZO_IMHF_NPFL_CNT', header: { text: '' } }, + { name: 'CHK_SPZO_IMHF_ENFCT_PUMP_1', fieldName: 'CHK_SPZO_IMHF_ENFCT_PUMP_1', header: { text: '' } }, + { name: 'CHK_SPZO_IMHF_ENFCT_PUMP_2', fieldName: 'CHK_SPZO_IMHF_ENFCT_PUMP_2', header: { text: '' } }, + { name: 'CHK_SPZO_IMHF_ENFCT_PUMP_3', fieldName: 'CHK_SPZO_IMHF_ENFCT_PUMP_3', header: { text: '' } }, + { name: 'CHK_SPZO_IMHF_TANK_SM', fieldName: 'CHK_SPZO_IMHF_TANK_SM', header: { text: '' } }, + { name: 'CHK_SPZO_WTROT_NPFL_CNT', fieldName: 'CHK_SPZO_WTROT_NPFL_CNT', header: { text: '' } }, + { name: 'CHK_SPZO_WTROT_ENFCT_PUMP_1', fieldName: 'CHK_SPZO_WTROT_ENFCT_PUMP_1', header: { text: '' } }, + { name: 'CHK_SPZO_WTROT_ENFCT_PUMP_2', fieldName: 'CHK_SPZO_WTROT_ENFCT_PUMP_2', header: { text: '' } }, + { name: 'CHK_SPZO_WTROT_ENFCT_PUMP_3', fieldName: 'CHK_SPZO_WTROT_ENFCT_PUMP_3', header: { text: '' } }, + { name: 'CHK_SPZO_WTROT_TANK_SM', fieldName: 'CHK_SPZO_WTROT_TANK_SM', header: { text: '' } }, + { name: 'CHK_SPZO_TFM', fieldName: 'CHK_SPZO_TFM', header: { text: '' } }, + { name: 'CHK_SPZO_ARTM', fieldName: 'CHK_SPZO_ARTM', header: { text: '' } }, + { name: 'CHK_SPZO_CTARM', fieldName: 'CHK_SPZO_CTARM', header: { text: '' } }, + { name: 'CHK_SPZO_NPFL', fieldName: 'CHK_SPZO_NPFL', header: { text: '' } }, + { name: 'CHK_SPZO_ENFCT_PUMP', fieldName: 'CHK_SPZO_ENFCT_PUMP', header: { text: '' } }, + { name: 'CHK_SPZO_PROC_CPC_SM', fieldName: 'CHK_SPZO_PROC_CPC_SM', header: { text: '' } }, + { name: 'CHK_SPZO_PROC_CPC_1', fieldName: 'CHK_SPZO_PROC_CPC_1', header: { text: '' } }, + { name: 'CHK_SPZO_PROC_CPC_2', fieldName: 'CHK_SPZO_PROC_CPC_2', header: { text: '' } }, + { name: 'CHK_SPZO_PROC_CPC_3', fieldName: 'CHK_SPZO_PROC_CPC_3', header: { text: '' } }, + { name: 'CHK_SPZO_PROC_CPC_4', fieldName: 'CHK_SPZO_PROC_CPC_4', header: { text: '' } }, + { name: 'CHK_SPZO_PROC_CPC_5', fieldName: 'CHK_SPZO_PROC_CPC_5', header: { text: '' } }, + { name: 'CHK_SPZO_PROC_CPC_6', fieldName: 'CHK_SPZO_PROC_CPC_6', header: { text: '' } }, + { name: 'CHK_SPZO_PROC_CPC_7', fieldName: 'CHK_SPZO_PROC_CPC_7', header: { text: '' } }, + { name: 'CHK_SPZO_PROC_CPC_8', fieldName: 'CHK_SPZO_PROC_CPC_8', header: { text: '' } }, + ], + columnLayout: [ + { + name: 'group_admin', + header: { text: '행정구역' }, + items: ['SIDO', 'GUGUN'] + }, + 'TOT_FCLTY_CNT', + { + name: 'group_spzi', + header: { text: '하수처리구역 내' }, + items: [ + 'SPZI_SM', + { + name: 'group_spzi_mthd', + header: { text: '처리방식별' }, + items: [ + 'SPZI_MTHD_SM', + { + name: 'group_spzi_rot', + header: { text: '부패탱크' }, + items: [ + 'SPZI_ROT_NPFL_CNT', + 'SPZI_ROT_ENFCT_PUMP_1' + ] + }, + 'SPZI_WTROT_NPFL_CNT', + 'SPZI_TFM', + 'SPZI_ARTM', + 'SPZI_CTARM' + ] + }, + { + name: 'group_spzi_proc', + header: { text: '처리용량별' }, + items: [ + 'SPZI_PROC_CPC_SM', + 'SPZI_PROC_CPC_1', + 'SPZI_PROC_CPC_2', + 'SPZI_PROC_CPC_3', + 'SPZI_PROC_CPC_4', + 'SPZI_PROC_CPC_5', + 'SPZI_PROC_CPC_6', + 'SPZI_PROC_CPC_7', + 'SPZI_PROC_CPC_8' + ] + } + ] + }, + { + name: 'group_spzo', + header: { text: '하수처리구역 외' }, + items: [ + 'SPZO_SM', + { + name: 'group_spzo_mthd', + header: { text: '처리방식별' }, + items: [ + 'SPZO_MTHD_SM', + { + name: 'group_spzo_rot', + header: { text: '부패탱크' }, + items: [ + 'SPZO_ROT_NPFL_CNT', + 'SPZO_ROT_ENFCT_PUMP_1' + ] + }, + 'SPZO_WTROT_NPFL_CNT', + 'SPZO_TFM', + 'SPZO_ARTM', + 'SPZO_CTARM' + ] + }, + { + name: 'group_spzo_proc', + header: { text: '처리용량별' }, + items: [ + 'SPZO_PROC_CPC_SM', + 'SPZO_PROC_CPC_1', + 'SPZO_PROC_CPC_2', + 'SPZO_PROC_CPC_3', + 'SPZO_PROC_CPC_4', + 'SPZO_PROC_CPC_5', + 'SPZO_PROC_CPC_6', + 'SPZO_PROC_CPC_7', + 'SPZO_PROC_CPC_8' + ] + } + ] + }, + 'RMK' + ], + rowStartNum: 4, + fixedColumns: 1, + tabKey: 'septicTankStatusInp', + tabLabel: '정화조 현황' +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/privateSewageFacility/SewageTreatFactStatusInpColumn.ts b/apps/st/src/components/util/column/input/privateSewageFacility/SewageTreatFactStatusInpColumn.ts new file mode 100644 index 000000000..40e77978a --- /dev/null +++ b/apps/st/src/components/util/column/input/privateSewageFacility/SewageTreatFactStatusInpColumn.ts @@ -0,0 +1,107 @@ +import { footerInt } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 > 08.개인하수처리시설 > tab 1. 처리용량별 오수처리시설현황 +export const SewageTreatFactStatusInpColumn = { + columns: [ + // 행정구역 + { name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } }, + + // 총계 + { name: 'STTY_SM', fieldName: '총계', header: { text: '총계' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 오수처리시설 > 하수처리구역 내(m³/일) + { name: 'STTY_ZONE_INNER_SM', fieldName: '계', header: { text: '계' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'STTY_ZONE_INNER_2', fieldName: '2이하', header: { text: '2이하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'STTY_ZONE_INNER_3', fieldName: '3~10', header: { text: '3~10' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'STTY_ZONE_INNER_11', fieldName: '11~20', header: { text: '11~20' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'STTY_ZONE_INNER_21', fieldName: '21~50', header: { text: '21~50' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'STTY_ZONE_INNER_51', fieldName: '51~100', header: { text: '51~100' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'STTY_ZONE_INNER_101', fieldName: '101~500', header: { text: '101~500' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'STTY_ZONE_INNER_501', fieldName: '501이상', header: { text: '501이상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 오수처리시설 > 하수처리구역 외(m³/일) + { name: 'STTY_ZONE_OUTER_SM', fieldName: '계', header: { text: '계' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'STTY_ZONE_OUTER_2', fieldName: '2이하', header: { text: '2이하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'STTY_ZONE_OUTER_3', fieldName: '3~10', header: { text: '3~10' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'STTY_ZONE_OUTER_11', fieldName: '11~20', header: { text: '11~20' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'STTY_ZONE_OUTER_21', fieldName: '21~50', header: { text: '21~50' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'STTY_ZONE_OUTER_51', fieldName: '51~100', header: { text: '51~100' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'STTY_ZONE_OUTER_101', fieldName: '101~500', header: { text: '101~500' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'STTY_ZONE_OUTER_501', fieldName: '501이상', header: { text: '501이상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 비고 + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + + // hidden + { name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' } }, + { name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' } }, + + // 검증 필드 (검증 여부 표시용, 빨간색 표시) + { name: 'CHK_STTY_SM', fieldName: 'CHK_STTY_SM', header: { text: '' } }, + { name: 'CHK_STTY_ZONE_INNER_SM', fieldName: 'CHK_STTY_ZONE_INNER_SM', header: { text: '' } }, + { name: 'CHK_STTY_ZONE_INNER_2', fieldName: 'CHK_STTY_ZONE_INNER_2', header: { text: '' } }, + { name: 'CHK_STTY_ZONE_INNER_3', fieldName: 'CHK_STTY_ZONE_INNER_3', header: { text: '' } }, + { name: 'CHK_STTY_ZONE_INNER_11', fieldName: 'CHK_STTY_ZONE_INNER_11', header: { text: '' } }, + { name: 'CHK_STTY_ZONE_INNER_21', fieldName: 'CHK_STTY_ZONE_INNER_21', header: { text: '' } }, + { name: 'CHK_STTY_ZONE_INNER_51', fieldName: 'CHK_STTY_ZONE_INNER_51', header: { text: '' } }, + { name: 'CHK_STTY_ZONE_INNER_101', fieldName: 'CHK_STTY_ZONE_INNER_101', header: { text: '' } }, + { name: 'CHK_STTY_ZONE_INNER_501', fieldName: 'CHK_STTY_ZONE_INNER_501', header: { text: '' } }, + { name: 'CHK_STTY_ZONE_OUTER_SM', fieldName: 'CHK_STTY_ZONE_OUTER_SM', header: { text: '' } }, + { name: 'CHK_STTY_ZONE_OUTER_2', fieldName: 'CHK_STTY_ZONE_OUTER_2', header: { text: '' } }, + { name: 'CHK_STTY_ZONE_OUTER_3', fieldName: 'CHK_STTY_ZONE_OUTER_3', header: { text: '' } }, + { name: 'CHK_STTY_ZONE_OUTER_11', fieldName: 'CHK_STTY_ZONE_OUTER_11', header: { text: '' } }, + { name: 'CHK_STTY_ZONE_OUTER_21', fieldName: 'CHK_STTY_ZONE_OUTER_21', header: { text: '' } }, + { name: 'CHK_STTY_ZONE_OUTER_51', fieldName: 'CHK_STTY_ZONE_OUTER_51', header: { text: '' } }, + { name: 'CHK_STTY_ZONE_OUTER_101', fieldName: 'CHK_STTY_ZONE_OUTER_101', header: { text: '' } }, + { name: 'CHK_STTY_ZONE_OUTER_501', fieldName: 'CHK_STTY_ZONE_OUTER_501', header: { text: '' } }, + ], + columnLayout: [ + { + name: 'group_admin', + header: { text: '행정구역' }, + items: ['SIDO', 'GUGUN'] + }, + 'STTY_SM', + { + name: 'group_stty', + header: { text: '오수처리시설' }, + items: [ + { + name: 'group_inner', + header: { text: '하수처리구역 내(m³/일)' }, + items: [ + 'STTY_ZONE_INNER_SM', + 'STTY_ZONE_INNER_2', + 'STTY_ZONE_INNER_3', + 'STTY_ZONE_INNER_11', + 'STTY_ZONE_INNER_21', + 'STTY_ZONE_INNER_51', + 'STTY_ZONE_INNER_101', + 'STTY_ZONE_INNER_501' + ] + }, + { + name: 'group_outer', + header: { text: '하수처리구역 외(m³/일)' }, + items: [ + 'STTY_ZONE_OUTER_SM', + 'STTY_ZONE_OUTER_2', + 'STTY_ZONE_OUTER_3', + 'STTY_ZONE_OUTER_11', + 'STTY_ZONE_OUTER_21', + 'STTY_ZONE_OUTER_51', + 'STTY_ZONE_OUTER_101', + 'STTY_ZONE_OUTER_501' + ] + } + ] + }, + 'RMK' + ], + rowStartNum: 3, + fixedColumns: 1, + tabKey: 'sewageTreatFactStatusInp', + tabLabel: '처리용량별 오수처리시설 현황' +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/sewageExcretaRelated/ExcretaRelatedIndustryInpColumn.ts b/apps/st/src/components/util/column/input/sewageExcretaRelated/ExcretaRelatedIndustryInpColumn.ts new file mode 100644 index 000000000..a2b645123 --- /dev/null +++ b/apps/st/src/components/util/column/input/sewageExcretaRelated/ExcretaRelatedIndustryInpColumn.ts @@ -0,0 +1,78 @@ +import { footerInt } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 > 12. 하수 및 분뇨관련산업 > tab 3. 분뇨수집·운반업 +export const ExcretaRelatedIndustryInpColumn = { + columns: [ + // 행정구역 + { name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } }, + + // 행정코드 (hidden) + { name: 'ADST_DSTR_CD', fieldName: '행정구역코드', header: { text: '행정구역코드' }, visible: false }, + { name: 'ADST_CD', fieldName: '행정코드', header: { text: '행정코드' }, visible: false }, + { name: 'ADST_DONG', fieldName: '행정동', header: { text: '행정동' }, visible: false }, + { name: 'RGN_NM', fieldName: '지역명', header: { text: '지역명' }, visible: false }, + + // 업체수(개소) + { name: 'ECSCS_SM', fieldName: '업체수(개소)', header: { text: '업체수(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 종사인원(명) + { name: 'ECSCS_BSMN', fieldName: '종사인원(명)', header: { text: '종사인원(명)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 시설(차량) 확보현황(대수) + { name: 'VHCLE_SM', fieldName: '계', header: { text: '계' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'THREE_BELOW', fieldName: '3톤이하', header: { text: '3톤이하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'FOUR_BELOW', fieldName: '4.5톤이하', header: { text: '4.5톤이하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'EIGHT_BELOW', fieldName: '8톤이하', header: { text: '8톤이하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'ETC', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 수집·운반 실적(m³/년) + { name: 'COLCT_TRSP_ACMSLT', fieldName: '수집·운반 실적(m³/년)', header: { text: '수집·운반 실적(m³/년)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 비고 + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + + // Hidden: 의견 필드 + { name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' }, visible: false }, + { name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' }, visible: false }, + + // Hidden: Check fields : 데이터 변경 여부를 판단하는 필드로, UI에서 숨김 처리 + { name: 'CHK_ECSCS_SM', fieldName: 'CHK_ECSCS_SM', header: { text: 'CHK_ECSCS_SM' }, visible: false }, + { name: 'CHK_ECSCS_BSMN', fieldName: 'CHK_ECSCS_BSMN', header: { text: 'CHK_ECSCS_BSMN' }, visible: false }, + { name: 'CHK_VHCLE_SM', fieldName: 'CHK_VHCLE_SM', header: { text: 'CHK_VHCLE_SM' }, visible: false }, + { name: 'CHK_THREE_BELOW', fieldName: 'CHK_THREE_BELOW', header: { text: 'CHK_THREE_BELOW' }, visible: false }, + { name: 'CHK_FOUR_BELOW', fieldName: 'CHK_FOUR_BELOW', header: { text: 'CHK_FOUR_BELOW' }, visible: false }, + { name: 'CHK_EIGHT_BELOW', fieldName: 'CHK_EIGHT_BELOW', header: { text: 'CHK_EIGHT_BELOW' }, visible: false }, + { name: 'CHK_ETC', fieldName: 'CHK_ETC', header: { text: 'CHK_ETC' }, visible: false }, + { name: 'CHK_COLCT_TRSP_ACMSLT', fieldName: 'CHK_COLCT_TRSP_ACMSLT', header: { text: 'CHK_COLCT_TRSP_ACMSLT' }, visible: false }, + ], + + columnLayout: [ + { + name: 'group_admin', + header: { text: '행정구역' }, + items: ['SIDO', 'GUGUN'] + }, + 'ECSCS_SM', + 'ECSCS_BSMN', + { + name: 'group_vehicle_status', + header: { text: '시설(차량) 확보현황(대수)' }, + items: [ + 'VHCLE_SM', + 'THREE_BELOW', + 'FOUR_BELOW', + 'EIGHT_BELOW', + 'ETC' + ] + }, + 'COLCT_TRSP_ACMSLT', + 'RMK' + ], + + rowStartNum: 2, + fixedColumns: 1, + tabKey: 'excretaRelatedIndustryInp', + tabLabel: '분뇨수집·운반업' +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/sewageExcretaRelated/PrivateSewageTreatRelatedIndustryInpColumn.ts b/apps/st/src/components/util/column/input/sewageExcretaRelated/PrivateSewageTreatRelatedIndustryInpColumn.ts new file mode 100644 index 000000000..5e61f6afe --- /dev/null +++ b/apps/st/src/components/util/column/input/sewageExcretaRelated/PrivateSewageTreatRelatedIndustryInpColumn.ts @@ -0,0 +1,86 @@ +import { footerInt } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 > 12. 하수 및 분뇨관련산업 > tab 4. 개인하수처리관련업 +export const PrivateSewageTreatRelatedIndustryInpColumn = { + columns: [ + // 행정구역 + { name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } }, + + // 행정코드 (hidden) + { name: 'ADST_DONG', fieldName: '행정동', header: { text: '행정동' }, visible: false }, + { name: 'RGN_NM', fieldName: '지역명', header: { text: '지역명' }, visible: false }, + { name: 'ADST_CD', fieldName: '행정코드', header: { text: '행정코드' }, visible: false }, + + // 개인하수처리시설 설계·시공업 + { name: 'CNSTRCT_ENTRPS', fieldName: '업소수', header: { text: '업소수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'CNSTRCT_BSMN', fieldName: '종사인원(명)', header: { text: '종사인원(명)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 개인하수처리시설 제조업 + { name: 'MNFCTUR_ENTRPS', fieldName: '업소수', header: { text: '업소수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'MNFCTUR_BSMN', fieldName: '종사인원(명)', header: { text: '종사인원(명)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 개인하수처리시설 관리업 + { name: 'MGMT_ENTRPS', fieldName: '업소수', header: { text: '업소수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'MGMT_BSMN', fieldName: '종사인원(명)', header: { text: '종사인원(명)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 비고 + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + + // Hidden: 의견 필드 + { name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' }, visible: false }, + { name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' }, visible: false }, + + // Hidden: 추가 필드 (화면에 미노출이, DB 저장) + { name: 'CNSTRCT_FCT_CNT', fieldName: '설계시공시설개수', header: { text: '설계시공시설개수' }, visible: false }, + { name: 'CNSTRCT_FCT_CPCTY', fieldName: '설계시공시설용량', header: { text: '설계시공시설용량' }, visible: false }, + { name: 'MNFCTUR_STTY_CNT', fieldName: '제조정원', header: { text: '제조정원' }, visible: false }, + { name: 'MNFCTUR_WRRTN_CNT', fieldName: '제조권원', header: { text: '제조권원' }, visible: false }, + { name: 'MGMT_STTY_CNT', fieldName: '관리정원', header: { text: '관리정원' }, visible: false }, + { name: 'MGMT_WRRTN_CNT', fieldName: '관리권원', header: { text: '관리권원' }, visible: false }, + + // Hidden: Check fields : 데이터 변경 여부를 판단하는 필드로, UI에서 숨김 처리 + { name: 'CHK_CNSTRCT_ENTRPS', fieldName: 'CHK_CNSTRCT_ENTRPS', header: { text: 'CHK_CNSTRCT_ENTRPS' }, visible: false }, + { name: 'CHK_CNSTRCT_BSMN', fieldName: 'CHK_CNSTRCT_BSMN', header: { text: 'CHK_CNSTRCT_BSMN' }, visible: false }, + { name: 'CHK_CNSTRCT_FCT_CNT', fieldName: 'CHK_CNSTRCT_FCT_CNT', header: { text: 'CHK_CNSTRCT_FCT_CNT' }, visible: false }, + { name: 'CHK_CNSTRCT_FCT_CPCTY', fieldName: 'CHK_CNSTRCT_FCT_CPCTY', header: { text: 'CHK_CNSTRCT_FCT_CPCTY' }, visible: false }, + { name: 'CHK_MNFCTUR_ENTRPS', fieldName: 'CHK_MNFCTUR_ENTRPS', header: { text: 'CHK_MNFCTUR_ENTRPS' }, visible: false }, + { name: 'CHK_MNFCTUR_BSMN', fieldName: 'CHK_MNFCTUR_BSMN', header: { text: 'CHK_MNFCTUR_BSMN' }, visible: false }, + { name: 'CHK_MNFCTUR_STTY_CNT', fieldName: 'CHK_MNFCTUR_STTY_CNT', header: { text: 'CHK_MNFCTUR_STTY_CNT' }, visible: false }, + { name: 'CHK_MNFCTUR_WRRTN_CNT', fieldName: 'CHK_MNFCTUR_WRRTN_CNT', header: { text: 'CHK_MNFCTUR_WRRTN_CNT' }, visible: false }, + { name: 'CHK_MGMT_ENTRPS', fieldName: 'CHK_MGMT_ENTRPS', header: { text: 'CHK_MGMT_ENTRPS' }, visible: false }, + { name: 'CHK_MGMT_BSMN', fieldName: 'CHK_MGMT_BSMN', header: { text: 'CHK_MGMT_BSMN' }, visible: false }, + { name: 'CHK_MGMT_STTY_CNT', fieldName: 'CHK_MGMT_STTY_CNT', header: { text: 'CHK_MGMT_STTY_CNT' }, visible: false }, + { name: 'CHK_MGMT_WRRTN_CNT', fieldName: 'CHK_MGMT_WRRTN_CNT', header: { text: 'CHK_MGMT_WRRTN_CNT' }, visible: false }, + ], + + columnLayout: [ + { + name: 'group_admin', + header: { text: '행정구역' }, + items: ['SIDO', 'GUGUN'] + }, + { + name: 'group_design_construct', + header: { text: '개인하수처리시설 설계·시공업' }, + items: ['CNSTRCT_ENTRPS', 'CNSTRCT_BSMN'] + }, + { + name: 'group_manufacture', + header: { text: '개인하수처리시설 제조업' }, + items: ['MNFCTUR_ENTRPS', 'MNFCTUR_BSMN'] + }, + { + name: 'group_management', + header: { text: '개인하수처리시설 관리업' }, + items: ['MGMT_ENTRPS', 'MGMT_BSMN'] + }, + 'RMK' + ], + + rowStartNum: 2, + fixedColumns: 1, + tabKey: 'privateSewageTreatRelatedIndustryInp', + tabLabel: '개인하수처리관련업' +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/sewageExcretaRelated/PublicInstitutionExcretaIndustryInpColumn.ts b/apps/st/src/components/util/column/input/sewageExcretaRelated/PublicInstitutionExcretaIndustryInpColumn.ts new file mode 100644 index 000000000..8df679707 --- /dev/null +++ b/apps/st/src/components/util/column/input/sewageExcretaRelated/PublicInstitutionExcretaIndustryInpColumn.ts @@ -0,0 +1,90 @@ +import { footerInt } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 > 12. 하수 및 분뇨관련산업 > tab 2. 공공기관 분뇨분야 +export const PublicInstitutionExcretaIndustryInpColumn = { + columns: [ + // 행정구역 + { name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } }, + + // 행정코드 (hidden) + { name: 'ADST_CD', fieldName: '행정코드', header: { text: '행정코드' }, visible: false }, + + // 직제 + { name: 'OCGNZ', fieldName: '직 제', header: { text: '직 제' } }, + + // 직원총수 + { name: 'TOT_WTR_QRM', fieldName: '직원총수', header: { text: '직원총수' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 행정직 + { name: 'ADMINIST_JSSFC', fieldName: '행정직', header: { text: '행정직' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 기술직 + { name: 'TCHNLGY_JSSFC_SM', fieldName: '계', header: { text: '계' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'ENGRK_JSSFC', fieldName: '토목', header: { text: '토목' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'MCHN_JSSFC', fieldName: '기계', header: { text: '기계' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'ELCTY_JSSFC', fieldName: '전기', header: { text: '전기' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'CHMSTRY_JSSFC', fieldName: '화공', header: { text: '화공' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'ENVRN_JSSFC', fieldName: '환경', header: { text: '환경' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'TCHNLGY_ETC', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 기능직 (hidden) + { name: 'TCNSRV', fieldName: '기능직', header: { text: '기능직' }, visible: false }, + + // 기타 + { name: 'ETC', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 비고 + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + + // Hidden: 의견 필드 + { name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' }, visible: false }, + { name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' }, visible: false }, + + // Hidden: Check fields : 데이터 변경 여부를 판단하는 필드로, UI에서 숨김 처리 + { name: 'CHK_OCGNZ', fieldName: 'CHK_OCGNZ', header: { text: 'CHK_OCGNZ' }, visible: false }, + { name: 'CHK_TOT_WTR_QRM', fieldName: 'CHK_TOT_WTR_QRM', header: { text: 'CHK_TOT_WTR_QRM' }, visible: false }, + { name: 'CHK_ADMINIST_JSSFC', fieldName: 'CHK_ADMINIST_JSSFC', header: { text: 'CHK_ADMINIST_JSSFC' }, visible: false }, + { name: 'CHK_TCHNLGY_JSSFC_SM', fieldName: 'CHK_TCHNLGY_JSSFC_SM', header: { text: 'CHK_TCHNLGY_JSSFC_SM' }, visible: false }, + { name: 'CHK_ENGRK_JSSFC', fieldName: 'CHK_ENGRK_JSSFC', header: { text: 'CHK_ENGRK_JSSFC' }, visible: false }, + { name: 'CHK_MCHN_JSSFC', fieldName: 'CHK_MCHN_JSSFC', header: { text: 'CHK_MCHN_JSSFC' }, visible: false }, + { name: 'CHK_ELCTY_JSSFC', fieldName: 'CHK_ELCTY_JSSFC', header: { text: 'CHK_ELCTY_JSSFC' }, visible: false }, + { name: 'CHK_CHMSTRY_JSSFC', fieldName: 'CHK_CHMSTRY_JSSFC', header: { text: 'CHK_CHMSTRY_JSSFC' }, visible: false }, + { name: 'CHK_ENVRN_JSSFC', fieldName: 'CHK_ENVRN_JSSFC', header: { text: 'CHK_ENVRN_JSSFC' }, visible: false }, + { name: 'CHK_TCHNLGY_ETC', fieldName: 'CHK_TCHNLGY_ETC', header: { text: 'CHK_TCHNLGY_ETC' }, visible: false }, + { name: 'CHK_TCNSRV', fieldName: 'CHK_TCNSRV', header: { text: 'CHK_TCNSRV' }, visible: false }, + { name: 'CHK_ETC', fieldName: 'CHK_ETC', header: { text: 'CHK_ETC' }, visible: false }, + ], + + columnLayout: [ + { + name: 'group_admin', + header: { text: '행정구역' }, + items: ['SIDO', 'GUGUN'] + }, + 'OCGNZ', + 'TOT_WTR_QRM', + 'ADMINIST_JSSFC', + { + name: 'group_technology', + header: { text: '기술직' }, + items: [ + 'TCHNLGY_JSSFC_SM', + 'ENGRK_JSSFC', + 'MCHN_JSSFC', + 'ELCTY_JSSFC', + 'CHMSTRY_JSSFC', + 'ENVRN_JSSFC', + 'TCHNLGY_ETC' + ] + }, + 'ETC', + 'RMK' + ], + + rowStartNum: 2, + fixedColumns: 0, + tabKey: 'publicInstitutionExcretaIndustryInp', + tabLabel: '공공기관 분뇨분야' +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/sewageExcretaRelated/PublicInstitutionSewerIndustryInpColumn.ts b/apps/st/src/components/util/column/input/sewageExcretaRelated/PublicInstitutionSewerIndustryInpColumn.ts new file mode 100644 index 000000000..9df115260 --- /dev/null +++ b/apps/st/src/components/util/column/input/sewageExcretaRelated/PublicInstitutionSewerIndustryInpColumn.ts @@ -0,0 +1,90 @@ +import { footerInt } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 > 12. 하수 및 분뇨관련산업 > tab 1. 공공기관 하수분야 +export const PublicInstitutionSewerIndustryInpColumn = { + columns: [ + // 행정구역 + { name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } }, + + // 행정코드 (hidden) + { name: 'ADST_CD', fieldName: '행정코드', header: { text: '행정코드' }, visible: false }, + + // 직제 + { name: 'OCGNZ', fieldName: '직 제', header: { text: '직 제' } }, + + // 직원총수 + { name: 'TOT_WTR_QRM', fieldName: '직원총수', header: { text: '직원총수' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 행정직 + { name: 'ADMINIST_JSSFC', fieldName: '행정직', header: { text: '행정직' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 기술직 + { name: 'TCHNLGY_JSSFC_SM', fieldName: '계', header: { text: '계' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'ENGRK_JSSFC', fieldName: '토목', header: { text: '토목' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'MCHN_JSSFC', fieldName: '기계', header: { text: '기계' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'ELCTY_JSSFC', fieldName: '전기', header: { text: '전기' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'CHMSTRY_JSSFC', fieldName: '화공', header: { text: '화공' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'ENVRN_JSSFC', fieldName: '환경', header: { text: '환경' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'TCHNLGY_ETC', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 기능직 (hidden) + { name: 'TCNSRV', fieldName: '기능직', header: { text: '기능직' }, visible: false }, + + // 기타 + { name: 'ETC', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 비고 + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + + // Hidden: 의견 필드 + { name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' }, visible: false }, + { name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' }, visible: false }, + + // Hidden: Check fields : 데이터 변경 여부를 판단하는 필드로, UI에서 숨김 처리 + { name: 'CHK_OCGNZ', fieldName: 'CHK_OCGNZ', header: { text: 'CHK_OCGNZ' }, visible: false }, + { name: 'CHK_TOT_WTR_QRM', fieldName: 'CHK_TOT_WTR_QRM', header: { text: 'CHK_TOT_WTR_QRM' }, visible: false }, + { name: 'CHK_ADMINIST_JSSFC', fieldName: 'CHK_ADMINIST_JSSFC', header: { text: 'CHK_ADMINIST_JSSFC' }, visible: false }, + { name: 'CHK_TCHNLGY_JSSFC_SM', fieldName: 'CHK_TCHNLGY_JSSFC_SM', header: { text: 'CHK_TCHNLGY_JSSFC_SM' }, visible: false }, + { name: 'CHK_ENGRK_JSSFC', fieldName: 'CHK_ENGRK_JSSFC', header: { text: 'CHK_ENGRK_JSSFC' }, visible: false }, + { name: 'CHK_MCHN_JSSFC', fieldName: 'CHK_MCHN_JSSFC', header: { text: 'CHK_MCHN_JSSFC' }, visible: false }, + { name: 'CHK_ELCTY_JSSFC', fieldName: 'CHK_ELCTY_JSSFC', header: { text: 'CHK_ELCTY_JSSFC' }, visible: false }, + { name: 'CHK_CHMSTRY_JSSFC', fieldName: 'CHK_CHMSTRY_JSSFC', header: { text: 'CHK_CHMSTRY_JSSFC' }, visible: false }, + { name: 'CHK_ENVRN_JSSFC', fieldName: 'CHK_ENVRN_JSSFC', header: { text: 'CHK_ENVRN_JSSFC' }, visible: false }, + { name: 'CHK_TCHNLGY_ETC', fieldName: 'CHK_TCHNLGY_ETC', header: { text: 'CHK_TCHNLGY_ETC' }, visible: false }, + { name: 'CHK_TCNSRV', fieldName: 'CHK_TCNSRV', header: { text: 'CHK_TCNSRV' }, visible: false }, + { name: 'CHK_ETC', fieldName: 'CHK_ETC', header: { text: 'CHK_ETC' }, visible: false }, + ], + + columnLayout: [ + { + name: 'group_admin', + header: { text: '행정구역' }, + items: ['SIDO', 'GUGUN'] + }, + 'OCGNZ', + 'TOT_WTR_QRM', + 'ADMINIST_JSSFC', + { + name: 'group_technology', + header: { text: '기술직' }, + items: [ + 'TCHNLGY_JSSFC_SM', + 'ENGRK_JSSFC', + 'MCHN_JSSFC', + 'ELCTY_JSSFC', + 'CHMSTRY_JSSFC', + 'ENVRN_JSSFC', + 'TCHNLGY_ETC' + ] + }, + 'ETC', + 'RMK' + ], + + rowStartNum: 2, + fixedColumns: 1, + tabKey: 'publicInstitutionSewerIndustryInp', + tabLabel: '공공기관 하수분야' +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/sewageExcretaSlurge/DigestorOperatingStatusInpColumn.ts b/apps/st/src/components/util/column/input/sewageExcretaSlurge/DigestorOperatingStatusInpColumn.ts new file mode 100644 index 000000000..c6b70ea6c --- /dev/null +++ b/apps/st/src/components/util/column/input/sewageExcretaSlurge/DigestorOperatingStatusInpColumn.ts @@ -0,0 +1,91 @@ +import { footerDecimal1 } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 > 11. 하수 및 분뇨찌꺼기(슬러지)처리 > tab 2. 소화조 운영현황 +export const DigestorOperatingStatusInpColumn = { + columns: [ + // 행정구역 + { name: 'SIDO_CD', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'ADST_CD', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } }, + + // 하수처리장명 + { name: 'BSC_FCT_NM', fieldName: '하수처리장명', header: { text: '하수처리장명' }, editable: false }, + + // 소화조 운영현황 + { name: 'DGSTR_FNNC', fieldName: '소화조 유/무(Y/N)선택', header: { text: '소화조 유/무(Y/N)선택' } }, + { name: 'DGSTR_CPC', fieldName: '설계용량(m³)', header: { text: '설계용량(m³)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'DGSTR_OPR_CPC', fieldName: '실제운전용량(m³)', header: { text: '실제운전용량(m³)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'DGSTR_IN', fieldName: '투입량(m³/일)', header: { text: '투입량(m³/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'DGSTR_TS', fieldName: '투입 TS 농도(%)', header: { text: '투입 TS 농도(%)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'DGSTR_YRSTP_RN_DYN', fieldName: '가동일수(연간)', header: { text: '가동일수(연간)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'DGSTR_OCCUR_GAS', fieldName: '발생가스량(m³/일)', header: { text: '발생가스량(m³/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 소화조 운영현황 > 소화가스 이용현황(m³/일) + { name: 'DGSTR_SM', fieldName: '계', header: { text: '계' }, footer: footerDecimal1, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'DGSTR_SLE', fieldName: '판매', header: { text: '판매' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'DGSTR_ELTGNR', fieldName: '발전', header: { text: '발전' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'DGSTR_ONSLF', fieldName: '자체활용', header: { text: '자체활용' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'DGSTR_ETC', fieldName: '기타(CNG등)', header: { text: '기타(CNG등)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 소화조 운영현황 > 잉여가스량 + { name: 'DGSTR_GAS', fieldName: '잉여가스량(m³/일)', header: { text: '잉여가스량(m³/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 비고 + { name: 'DGSTR_RMK', fieldName: '비고', header: { text: '비고' } }, + + // Hidden: Check fields for change detection + { name: 'CHK_DGSTR_FNNC', fieldName: 'CHK_DGSTR_FNNC', header: { text: 'CHK_DGSTR_FNNC' }, visible: false }, + { name: 'CHK_DGSTR_IN', fieldName: 'CHK_DGSTR_IN', header: { text: 'CHK_DGSTR_IN' }, visible: false }, + { name: 'CHK_DGSTR_TS', fieldName: 'CHK_DGSTR_TS', header: { text: 'CHK_DGSTR_TS' }, visible: false }, + { name: 'CHK_DGSTR_YRSTP_RN_DYN', fieldName: 'CHK_DGSTR_YRSTP_RN_DYN', header: { text: 'CHK_DGSTR_YRSTP_RN_DYN' }, visible: false }, + { name: 'CHK_DGSTR_OCCUR_GAS', fieldName: 'CHK_DGSTR_OCCUR_GAS', header: { text: 'CHK_DGSTR_OCCUR_GAS' }, visible: false }, + { name: 'CHK_DGSTR_CPC', fieldName: 'CHK_DGSTR_CPC', header: { text: 'CHK_DGSTR_CPC' }, visible: false }, + { name: 'CHK_DGSTR_OPR_CPC', fieldName: 'CHK_DGSTR_OPR_CPC', header: { text: 'CHK_DGSTR_OPR_CPC' }, visible: false }, + { name: 'CHK_DGSTR_SM', fieldName: 'CHK_DGSTR_SM', header: { text: 'CHK_DGSTR_SM' }, visible: false }, + { name: 'CHK_DGSTR_SLE', fieldName: 'CHK_DGSTR_SLE', header: { text: 'CHK_DGSTR_SLE' }, visible: false }, + { name: 'CHK_DGSTR_ELTGNR', fieldName: 'CHK_DGSTR_ELTGNR', header: { text: 'CHK_DGSTR_ELTGNR' }, visible: false }, + { name: 'CHK_DGSTR_ONSLF', fieldName: 'CHK_DGSTR_ONSLF', header: { text: 'CHK_DGSTR_ONSLF' }, visible: false }, + { name: 'CHK_DGSTR_ETC', fieldName: 'CHK_DGSTR_ETC', header: { text: 'CHK_DGSTR_ETC' }, visible: false }, + { name: 'CHK_DGSTR_GAS', fieldName: 'CHK_DGSTR_GAS', header: { text: 'CHK_DGSTR_GAS' }, visible: false }, + ], + + columnLayout: [ + { + name: 'group_admin', + header: { text: '행정구역' }, + items: ['SIDO_CD', 'ADST_CD'] + }, + 'BSC_FCT_NM', + { + name: 'group_digestor_operating', + header: { text: '소화조 운영현황' }, + items: [ + 'DGSTR_FNNC', + 'DGSTR_CPC', + 'DGSTR_OPR_CPC', + 'DGSTR_IN', + 'DGSTR_TS', + 'DGSTR_YRSTP_RN_DYN', + 'DGSTR_OCCUR_GAS', + { + name: 'group_gas_usage', + header: { text: '소화가스 이용현황(m³/일)' }, + items: [ + 'DGSTR_SM', + 'DGSTR_SLE', + 'DGSTR_ELTGNR', + 'DGSTR_ONSLF', + 'DGSTR_ETC' + ] + }, + 'DGSTR_GAS' + ] + }, + 'DGSTR_RMK' + ], + + rowStartNum: 3, + fixedColumns: 0, + tabKey: 'digestorOperatingStatusInp', + tabLabel: '소화조 운영현황' +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/sewageExcretaSlurge/ExcretaSlurgeProcessingStatusInpColumn.ts b/apps/st/src/components/util/column/input/sewageExcretaSlurge/ExcretaSlurgeProcessingStatusInpColumn.ts new file mode 100644 index 000000000..7edfb7a40 --- /dev/null +++ b/apps/st/src/components/util/column/input/sewageExcretaSlurge/ExcretaSlurgeProcessingStatusInpColumn.ts @@ -0,0 +1,83 @@ +import { footerDecimal1 } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 > 11.하수 및 분뇨찌꺼기(슬러지) 처리 > tab 4. 분뇨찌꺼기(슬러지)처리 현황 +export const ExcretaSlurgeProcessingStatusInpColumn = { + columns: [ + // 행정구역 + { name: 'SIDO_CD', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'ADST_CD', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } }, + + // 분뇨처리장명 + { name: 'ECSCS_NM', fieldName: '분 뇨 처리장명', header: { text: '분 뇨 처리장명' }, editable: false }, + + // 발생량 + { name: 'OCTY', fieldName: '발생량 (A)=(B)+(C)', header: { text: '발생량 (A)=(B)+(C)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 처 분 량(톤/년) + { name: 'PROCESS_CPCTY_SM', fieldName: '계(B)', header: { text: '계(B)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'RUSE', fieldName: '재활용', header: { text: '재활용' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'LAND_RCLM', fieldName: '육상매립', header: { text: '육상매립' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'INCNR', fieldName: '소각', header: { text: '소각' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'OCEAN_DPSL', fieldName: '해양투기', header: { text: '해양투기' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'FUEL', fieldName: '연료화', header: { text: '연료화' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'ETC', fieldName: '기타', header: { text: '기타' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 미처분량(이월량)(C) + { name: 'CYFD_CPCTY', fieldName: '미처분량(이월량)(C)', header: { text: '미처분량(이월량)(C)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 타시설연계여부 + { name: 'OUT_BSC_FCT_NM', fieldName: '타시설연계여부', header: { text: '타시설연계여부' } }, + + // 비고 + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + + // Hidden: 의견 + { name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' }, visible: false }, + { name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' }, visible: false }, + + // Hidden: Check fields for change detection + { name: 'CHK_ECSCS_NM', fieldName: 'CHK_ECSCS_NM', header: { text: 'CHK_ECSCS_NM' }, visible: false }, + { name: 'CHK_OCTY', fieldName: 'CHK_OCTY', header: { text: 'CHK_OCTY' }, visible: false }, + { name: 'CHK_PROCESS_CPCTY_SM', fieldName: 'CHK_PROCESS_CPCTY_SM', header: { text: 'CHK_PROCESS_CPCTY_SM' }, visible: false }, + { name: 'CHK_RUSE', fieldName: 'CHK_RUSE', header: { text: 'CHK_RUSE' }, visible: false }, + { name: 'CHK_LAND_RCLM', fieldName: 'CHK_LAND_RCLM', header: { text: 'CHK_LAND_RCLM' }, visible: false }, + { name: 'CHK_INCNR', fieldName: 'CHK_INCNR', header: { text: 'CHK_INCNR' }, visible: false }, + { name: 'CHK_OCEAN_DPSL', fieldName: 'CHK_OCEAN_DPSL', header: { text: 'CHK_OCEAN_DPSL' }, visible: false }, + { name: 'CHK_FUEL', fieldName: 'CHK_FUEL', header: { text: 'CHK_FUEL' }, visible: false }, + { name: 'CHK_ETC', fieldName: 'CHK_ETC', header: { text: 'CHK_ETC' }, visible: false }, + { name: 'CHK_CYFD_CPCTY', fieldName: 'CHK_CYFD_CPCTY', header: { text: 'CHK_CYFD_CPCTY' }, visible: false }, + { name: 'CHK_OUT_BSC_FCT_NM', fieldName: 'CHK_OUT_BSC_FCT_NM', header: { text: 'CHK_OUT_BSC_FCT_NM' }, visible: false }, + ], + + columnLayout: [ + { + name: 'group_admin', + header: { text: '행정구역' }, + items: ['SIDO_CD', 'ADST_CD'] + }, + 'ECSCS_NM', + 'OCTY', + { + name: 'group_process_amount', + header: { text: '처 분 량(톤/년)' }, + items: [ + 'PROCESS_CPCTY_SM', + 'RUSE', + 'LAND_RCLM', + 'INCNR', + 'OCEAN_DPSL', + 'FUEL', + 'ETC' + ] + }, + 'CYFD_CPCTY', + 'OUT_BSC_FCT_NM', + 'RMK' + ], + + rowStartNum: 2, + fixedColumns: 2, + tabKey: 'excretaSlurgeProcessingStatusInp', + tabLabel: '분뇨찌꺼기(슬러지)처리현황' +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/sewageExcretaSlurge/SewageSlurgeProcessingFacStatusInpColumn.ts b/apps/st/src/components/util/column/input/sewageExcretaSlurge/SewageSlurgeProcessingFacStatusInpColumn.ts new file mode 100644 index 000000000..dedf4af98 --- /dev/null +++ b/apps/st/src/components/util/column/input/sewageExcretaSlurge/SewageSlurgeProcessingFacStatusInpColumn.ts @@ -0,0 +1,69 @@ +import { footerInt, footerDecimal1 } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 > 11.하수 및 분뇨찌꺼기(슬러지) 처리 > tab 3. 하수찌꺼기 처리시설 운영현황 +export const SewageSlurgeProcessingFacStatusInpColumn = { + columns: [ + // 행정구역 + { name: 'SIDO_CD', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'ADST_CD', fieldName: '시군구', header: { text: '시군구' }, footer: { text: '합계' } }, + + // 찌꺼기처리시설명 + { name: 'SLG_FCT_NM', fieldName: '찌꺼기처리시설명', header: { text: '찌꺼기처리시설명' } }, + + // 처리시설 운영현황 + { name: 'TRT_METHOD', fieldName: '처리방식', header: { text: '처리방식' } }, + { name: 'SLG_FCT_CPC', fieldName: '시설용량(톤/일)', header: { text: '시설용량(톤/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'OPR_CPC', fieldName: '연간처리량(톤/년)', header: { text: '연간처리량(톤/년)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'ODY_RN_TIME', fieldName: '일평균가동시간(1~24)', header: { text: '일평균가동시간(1~24)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'YRSTP_RN_DYN', fieldName: '연중 가동일(1~330)', header: { text: '연중 가동일(1~330)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'OPR_RT', fieldName: '가동율(%)', header: { text: '가동율(%)' }, footer: footerDecimal1, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 비고 + { name: 'SLG_FCT_RMK', fieldName: '비고', header: { text: '비고' } }, + + // Hidden: 기본 정보 + { name: 'STPN_SEQ', fieldName: 'STPN_SEQ', header: { text: 'STPN_SEQ' }, visible: false }, + { name: 'BSC_FCT_NM', fieldName: 'BSC_FCT_NM', header: { text: 'BSC_FCT_NM' }, visible: false }, + { name: 'YR', fieldName: 'YR', header: { text: 'YR' }, visible: false }, + { name: 'ODR', fieldName: 'ODR', header: { text: 'ODR' }, visible: false }, + { name: 'BSNM_CD', fieldName: 'BSNM_CD', header: { text: 'BSNM_CD' }, visible: false }, + { name: 'BSC_FCT_CD', fieldName: 'BSC_FCT_CD', header: { text: 'BSC_FCT_CD' }, visible: false }, + + // Hidden: Check fields for change detection + { name: 'CHK_SLG_FCT_NM', fieldName: 'CHK_SLG_FCT_NM', header: { text: 'CHK_SLG_FCT_NM' }, visible: false }, + { name: 'CHK_TRT_METHOD', fieldName: 'CHK_TRT_METHOD', header: { text: 'CHK_TRT_METHOD' }, visible: false }, + { name: 'CHK_SLG_FCT_CPC', fieldName: 'CHK_SLG_FCT_CPC', header: { text: 'CHK_SLG_FCT_CPC' }, visible: false }, + { name: 'CHK_OPR_CPC', fieldName: 'CHK_OPR_CPC', header: { text: 'CHK_OPR_CPC' }, visible: false }, + { name: 'CHK_ODY_RN_TIME', fieldName: 'CHK_ODY_RN_TIME', header: { text: 'CHK_ODY_RN_TIME' }, visible: false }, + { name: 'CHK_YRSTP_RN_DYN', fieldName: 'CHK_YRSTP_RN_DYN', header: { text: 'CHK_YRSTP_RN_DYN' }, visible: false }, + { name: 'CHK_OPR_RT', fieldName: 'CHK_OPR_RT', header: { text: 'CHK_OPR_RT' }, visible: false }, + ], + + columnLayout: [ + { + name: 'group_admin', + header: { text: '행정구역' }, + items: ['SIDO_CD', 'ADST_CD'] + }, + 'SLG_FCT_NM', + { + name: 'group_processing_fac_operating', + header: { text: '처리시설 운영현황' }, + items: [ + 'TRT_METHOD', + 'SLG_FCT_CPC', + 'OPR_CPC', + 'ODY_RN_TIME', + 'YRSTP_RN_DYN', + 'OPR_RT' + ] + }, + 'SLG_FCT_RMK' + ], + + rowStartNum: 2, + fixedColumns: 1, + tabKey: 'sewageSlurgeProcessingFacStatusInp', + tabLabel: '하수찌꺼기 처리시설 운영현황' +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/sewageExcretaSlurge/SlurgeProcessingFacStatusInpColumn.ts b/apps/st/src/components/util/column/input/sewageExcretaSlurge/SlurgeProcessingFacStatusInpColumn.ts new file mode 100644 index 000000000..364c4e378 --- /dev/null +++ b/apps/st/src/components/util/column/input/sewageExcretaSlurge/SlurgeProcessingFacStatusInpColumn.ts @@ -0,0 +1,216 @@ +import { footerDecimal1 } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 > 11. 하수 및 분뇨찌꺼기(슬러지)처리 > tab 1: 찌꺼기(슬러지)처리시설현황 +export const SlurgeProcessingFacStatusInpColumn = { + columns: [ + // 행정구역 + { name: 'SIDO_CD', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'ADST_CD', fieldName: '시군구', header: { text: '시군구' }, footer: { text: '합계' } }, + + // 하수처리장명 + { name: 'BSC_FCT_NM', fieldName: '하수처리장명', header: { text: '하수처리장명' }, }, + + // 찌꺼기발생량(톤/년, 탈수기준) + { name: 'TOT_AMNT', fieldName: '합계', header: { text: '합계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'P_AMNT', fieldName: '자체', header: { text: '자체' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'OUT_AMNT', fieldName: '외부유입량', header: { text: '외부유입량' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 이송처리장 + { name: 'OUT_FCT_NM', fieldName: '이송처리장', header: { text: '이송처리장' } }, + + // 이송처리량 - 쿼리에서 못찾음 (임의) + { name: 'a', fieldName: '이송처리량(m³/년)', header: { text: '이송처리량(m³/년)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 함수율 + { name: 'DSP_MOIRAT', fieldName: '함수율(%, 탈수기준)', header: { text: '함수율(%, 탈수기준)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 찌꺼기처리량합계 + { name: 'DSP_SM', fieldName: '찌꺼기처리량합계(톤/년)', header: { text: '찌꺼기처리량합계(톤/년)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 자체 찌꺼기 처리량 > 자체찌꺼기처리량소계 + { name: 'DSP_SELF_SM', fieldName: '자체찌꺼기처리량소계', header: { text: '자체찌꺼기처리량소계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 자체 찌꺼기 처리량 > 매립 + { name: 'O_DSP_RCLM', fieldName: '매립', header: { text: '매립' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 자체 찌꺼기 처리량 > 소각(1차) + { name: 'O_DSP_INCNR', fieldName: '소각(1차)', header: { text: '소각(1차)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 자체 찌꺼기 처리량 > 소각 후 처리(2차) - 쿼리에 없음 (임의 생성) + { name: 'b1', fieldName: '소계', header: { text: '소계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'b2', fieldName: '매립', header: { text: '매립' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'b3', fieldName: '제품원료', header: { text: '제품원료' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'b4', fieldName: '복토재', header: { text: '복토재' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'b5', fieldName: '시설(업체명)', header: { text: '시설(업체명)' } }, + + // 자체 찌꺼기 처리량 > 고화(1차) - 쿼리에 없음 (임의 생성) + { name: 'c1', fieldName: '고화(1차)', header: { text: '고화(1차)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 자체 찌꺼기 처리량 > 고화 후 처리(2차) - 쿼리에 없음 (임의 생성) + { name: 'c2', fieldName: '소계', header: { text: '소계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'c3', fieldName: '매립', header: { text: '매립' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'c4', fieldName: '복토재', header: { text: '복토재' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'c5', fieldName: '시설(업체명)', header: { text: '시설(업체명)' } }, + + // 자체 찌꺼기 처리량 > 건조(1차) + { name: 'DSP_DESIC', fieldName: '건조(1차)', header: { text: '건조(1차)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 자체 찌꺼기 처리량 > 건조 후 처리(2차) - 쿼리에 없음 + { name: 'd1', fieldName: '소계', header: { text: '소계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'd2', fieldName: '매립', header: { text: '매립' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'd3', fieldName: '소각', header: { text: '소각' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'd4', fieldName: '연료', header: { text: '연료' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'd5', fieldName: '제품원료', header: { text: '제품원료' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'd6', fieldName: '복토재', header: { text: '복토재' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'd7', fieldName: '퇴비화', header: { text: '퇴비화' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'd8', fieldName: '시설(업체명)', header: { text: '시설(업체명)' }, editable: true }, + + // 자체 찌꺼기 처리량 > 탄화(1차) - 쿼리에 없음 (임의 생성) + { name: 'e1', fieldName: '탄화(1차)', header: { text: '탄화(1차)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 자체 찌꺼기 처리량 > 탄화 후 처리(2차) - 쿼리에 없음 (임의 생성) + { name: 'e2', fieldName: '소계', header: { text: '소계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'e3', fieldName: '소각', header: { text: '소각' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'e4', fieldName: '연료', header: { text: '연료' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'e5', fieldName: '시설(업체명)', header: { text: '시설(업체명)' } }, + + // 자체 찌꺼기 처리량 > 퇴비화 - 쿼리에 없음 (임의 생성) + { name: 'f1', fieldName: '퇴비화', header: { text: '퇴비화' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 자체 찌꺼기 처리량 > 퇴비화 > 시설(업체명) - 쿼리에 없음 (임의 생성) + { name: 'f2', fieldName: '시설(업체명)', header: { text: '시설(업체명)' } }, + + // 외부위탁 처리량(톤/년) + { name: 'DSP_CONSIGNMENT', fieldName: '소계', header: { text: '소계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'g1', fieldName: '매립', header: { text: '매립' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'g2', fieldName: '소각', header: { text: '소각' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'g3', fieldName: '연료', header: { text: '연료' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'g4', fieldName: '제품원료', header: { text: '제품원료' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'g5', fieldName: '복토재', header: { text: '복토재' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'g6', fieldName: '퇴비화', header: { text: '퇴비화' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'g7', fieldName: '시설(업체명)', header: { text: '시설(업체명)' } }, + + // 수도권광역위탁처리량 + { name: 'DSP_WIDE', fieldName: '수도권광역위탁처리량(톤/년)', header: { text: '수도권광역위탁처리량(톤/년)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 비고 + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + + // Hidden: Check fields + { name: 'CHK_TOT_AMNT', fieldName: 'CHK_TOT_AMNT', header: { text: 'CHK_TOT_AMNT' }, visible: false }, + { name: 'CHK_P_AMNT', fieldName: 'CHK_P_AMNT', header: { text: 'CHK_P_AMNT' }, visible: false }, + { name: 'CHK_OUT_AMNT', fieldName: 'CHK_OUT_AMNT', header: { text: 'CHK_OUT_AMNT' }, visible: false }, + { name: 'CHK_DSP_MOIRAT', fieldName: 'CHK_DSP_MOIRAT', header: { text: 'CHK_DSP_MOIRAT' }, visible: false }, + { name: 'CHK_DSP_SM', fieldName: 'CHK_DSP_SM', header: { text: 'CHK_DSP_SM' }, visible: false }, + { name: 'CHK_DSP_SELF_SM', fieldName: 'CHK_DSP_SELF_SM', header: { text: 'CHK_DSP_SELF_SM' }, visible: false }, + { name: 'CHK_O_DSP_RCLM', fieldName: 'CHK_O_DSP_RCLM', header: { text: 'CHK_O_DSP_RCLM' }, visible: false }, + { name: 'CHK_O_DSP_INCNR', fieldName: 'CHK_O_DSP_INCNR', header: { text: 'CHK_O_DSP_INCNR' }, visible: false }, + { name: 'CHK_DSP_DESIC', fieldName: 'CHK_DSP_DESIC', header: { text: 'CHK_DSP_DESIC' }, visible: false }, + { name: 'CHK_DSP_CONSIGNMENT', fieldName: 'CHK_DSP_CONSIGNMENT', header: { text: 'CHK_DSP_CONSIGNMENT' }, visible: false }, + { name: 'CHK_DSP_WIDE', fieldName: 'CHK_DSP_WIDE', header: { text: 'CHK_DSP_WIDE' }, visible: false }, + { name: 'CHK_OUT_FCT_NM', fieldName: 'CHK_OUT_FCT_NM', header: { text: 'CHK_OUT_FCT_NM' }, visible: false }, + ], + + columnLayout: [ + { + name: 'group_admin', + header: { text: '행정구역' }, + items: ['SIDO_CD', 'ADST_CD'] + }, + 'BSC_FCT_NM', + { + name: 'group_occur_amount', + header: { text: '찌꺼기발생량(톤/년, 탈수기준)' }, + items: [ + 'TOT_AMNT', + 'P_AMNT', + 'OUT_AMNT' + ] + }, + 'OUT_FCT_NM', + 'a', + 'DSP_MOIRAT', + 'DSP_SM', + { + name: 'group_self_treatment', + header: { text: '자체 찌꺼기 처리량(톤/년)' }, + items: [ + 'DSP_SELF_SM', + 'O_DSP_RCLM', + 'O_DSP_INCNR', + { + name: 'group_incinerate_2nd', + header: { text: '소각 후 처리(2차)' }, + items: [ + 'b1', + 'b2', + 'b3', + 'b4', + 'b5' + ] + }, + 'c1', + { + name: 'group_solidify_2nd', + header: { text: '고화 후 처리(2차)' }, + items: [ + 'c2', + 'c3', + 'c4', + 'c5' + ] + }, + 'DSP_DESIC', + { + name: 'group_drying_2nd', + header: { text: '건조 후 처리(2차)' }, + items: [ + 'd1', + 'd2', + 'd3', + 'd4', + 'd5', + 'd6', + 'd7', + 'd8' + ] + }, + 'e1', + { + name: 'group_carbonize_2nd', + header: { text: '탄화 후 처리(2차)' }, + items: [ + 'e2', + 'e3', + 'e4', + 'e5' + ] + }, + 'f1', + 'f2' + ] + }, + { + name: 'group_external_treatment', + header: { text: '외부위탁 처리량(톤/년)' }, + items: [ + 'DSP_CONSIGNMENT', + 'g1', + 'g2', + 'g3', + 'g4', + 'g5', + 'g6', + 'g7' + ] + }, + 'DSP_WIDE', + 'RMK' + ], + + rowStartNum: 3, + fixedColumns: 2, + tabKey: 'slurgeProcessingFacStatusInp', + tabLabel: '찌꺼기(슬러지)처리시설현황' +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/sewagePipe/ConnectionStatusInpColumn.ts b/apps/st/src/components/util/column/input/sewagePipe/ConnectionStatusInpColumn.ts new file mode 100644 index 000000000..03a3157da --- /dev/null +++ b/apps/st/src/components/util/column/input/sewagePipe/ConnectionStatusInpColumn.ts @@ -0,0 +1,63 @@ +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 > 02.하수관로 > tab 6. 시설별 현황관리 +// Tab 6-1: 행정구역별 연결 현황 (오른쪽 그리드) +export const AdminAreaConnectionInpColumn = { + columns: [ + // 행정구역 + { name: 'sido', fieldName: '시도', header: { text: '시도' } }, + { name: 'sgg', fieldName: '시군구', header: { text: '시군구' } }, + { name: 'adstDong', fieldName: '행정구역명', header: { text: '행정구역명' } }, + // 연결여부 + { name: 'iscan', fieldName: '연결여부', header: { text: '연결여부' } }, + // 연결하수처리장 + { name: 'bscFctNm', fieldName: '연결하수처리장', header: { text: '연결하수처리장' } }, + ], + columnLayout: [ + { + name: 'adminArea', + hideChildHeaders: false, + header: { text: '행정구역' }, + items: ['sido', 'sgg', 'adstDong'], + }, + 'iscan', + 'bscFctNm', + ], + rowStartNum: 2, + fixedColumns: 1, + tabKey: 'adminAreaConnectionInp', + tabLabel: '행정구역별 연결 현황' +}; + +// Tab 6-2: 하수처리장별 연결 현황 (왼쪽 그리드) +export const TreatPlantConnectionInpColumn = { + columns: [ + // 하수처리장 정보 + { name: 'bscFctNm', fieldName: '하수처리장명', header: { text: '하수처리장명' } }, + { name: 'fctCpc', fieldName: '용량', header: { text: '용량' }, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'bscFctCd', fieldName: '하수처리장코드', header: { text: '하수처리장코드' } }, + // 연결여부 + { name: 'iscan', fieldName: '연결여부', header: { text: '연결여부' } }, + // 연결지역 + { name: 'nmlist', fieldName: '연결지역', header: { text: '연결지역' } }, + ], + columnLayout: [ + 'bscFctNm', + 'fctCpc', + 'bscFctCd', + 'iscan', + 'nmlist', + ], + rowStartNum: 1, + fixedColumns: 1, + tabKey: 'treatmentPlantConnectionInp', + tabLabel: '하수처리장별 연결 현황' +}; + +export const ConnectionStatusInpColumn = { + tabKey: 'treatmentPlantConnectionInp', + tabLabel: '시설별 현황 관리', + isSplitGrid: true, + left: AdminAreaConnectionInpColumn, + right: TreatPlantConnectionInpColumn, +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/sewagePipe/FactInstallStatusInpColumn.ts b/apps/st/src/components/util/column/input/sewagePipe/FactInstallStatusInpColumn.ts new file mode 100644 index 000000000..213ceb818 --- /dev/null +++ b/apps/st/src/components/util/column/input/sewagePipe/FactInstallStatusInpColumn.ts @@ -0,0 +1,142 @@ +import { footerInt, footerDecimal2, footerDecimal3 } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 > 02.하수관로 > tab 2. 시설별 설치현황 + +export const FactInstallStatusInpColumn = { + columns: [ + // 행정구역 + { name: 'sido', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'sgg', fieldName: '시군구', header: { text: '시군구' }, editable: false }, + + // 하수처리장 정보 + { name: 'bscFctNm', fieldName: '하수처리장명', header: { text: '하수처리장명' }, footer: { text: '합계' }, editable: false }, + { name: 'fctCpc', fieldName: '하수처리시설용량(m³/일)', header: { text: '하수처리시설용량(m³/일)' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 처리구역면적(km²) + { name: 'procZoneSm', fieldName: '합계', header: { text: '합계' }, footer: footerDecimal3, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'cmbnProcZoneAr', fieldName: '합류식', header: { text: '합류식' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'sprtProcZoneAr', fieldName: '분류식', header: { text: '분류식' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + + // 분류식화율 + { name: 'sprtRt', fieldName: '분류식화율', header: { text: '분류식화율' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 배수설비(개소) + { name: 'drngEqpSm', fieldName: '합계', header: { text: '합계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'cmbnDrngEqp', fieldName: '합류식', header: { text: '합류식' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'sprtDrngEqpSm', fieldName: '소계', header: { text: '소계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'sprtDrngEqpCnnc', fieldName: '연결', header: { text: '연결' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'sprtDrngEqpDcnnc', fieldName: '미연결', header: { text: '미연결' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 하수관로 > 총시설연장 + { name: 'piplFcltyExtn', fieldName: '총시설연장(m)', header: { text: '총시설연장(m)' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 하수관로 > 차집/오수간선(m) + { name: 'icgMnnSm', fieldName: '합계', header: { text: '합계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'icgExtn', fieldName: '차집', header: { text: '차집' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'mnnExtn', fieldName: '오수 간선', header: { text: '오수 간선' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 하수관로 > 토실/토구(개소) > 합류식 + { name: 'cmbnSoilholeSite', fieldName: '우수토실', header: { text: '우수토실' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 하수관로 > 토실/토구(개소) > 분류식 + { name: 'sprtSoilholeSite', fieldName: '우수토실', header: { text: '우수토실' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'sprtSoilspllSite', fieldName: '우수토구', header: { text: '우수토구' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 하수관로 > 지선(m) + { name: 'brnchlnSm', fieldName: '합계', header: { text: '합계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'cmbnBrnchlnExtn', fieldName: '합류식', header: { text: '합류식' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 하수관로 > 지선(m) > 분류식 + { name: 'rainwtrBrnchlnExtn', fieldName: '우수', header: { text: '우수' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'pltwtrBrnchlnExtn', fieldName: '오수', header: { text: '오수' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 비고 + { name: 'rmk', fieldName: '비고', header: { text: '비고' } }, + ], + columnLayout: [ + { + name: 'adminArea', + hideChildHeaders: false, + header: { text: '행정구역' }, + items: ['sido', 'sgg'], + }, + 'bscFctNm', + 'fctCpc', + { + name: 'procZone', + hideChildHeaders: false, + header: { text: '처리구역면적(km²)' }, + items: ['procZoneSm', 'cmbnProcZoneAr', 'sprtProcZoneAr'], + }, + 'sprtRt', + { + name: 'drainEquip', + hideChildHeaders: false, + header: { text: '배수설비(개소)' }, + items: [ + 'drngEqpSm', + 'cmbnDrngEqp', + { + name: 'sprtDrainEquip', + hideChildHeaders: false, + header: { text: '분류식' }, + items: ['sprtDrngEqpSm', 'sprtDrngEqpCnnc', 'sprtDrngEqpDcnnc'], + }, + ], + }, + { + name: 'sewagePipe', + hideChildHeaders: false, + header: { text: '하수관로' }, + items: [ + 'piplFcltyExtn', + { + name: 'interceptor', + hideChildHeaders: false, + header: { text: '차집/오수간선(m)' }, + items: ['icgMnnSm', 'icgExtn', 'mnnExtn'], + }, + { + name: 'soilHole', + hideChildHeaders: false, + header: { text: '토실/토구(개소)' }, + items: [ + { + name: 'cmbnSoilHole', + hideChildHeaders: false, + header: { text: '합류식' }, + items: ['cmbnSoilholeSite'], + }, + { + name: 'sprtSoilHole', + hideChildHeaders: false, + header: { text: '분류식' }, + items: ['sprtSoilholeSite', 'sprtSoilspllSite'], + }, + ], + }, + { + name: 'branchLine', + hideChildHeaders: false, + header: { text: '지선(m)' }, + items: [ + 'brnchlnSm', + 'cmbnBrnchlnExtn', + { + name: 'sprtBranchLine', + hideChildHeaders: false, + header: { text: '분류식' }, + items: ['rainwtrBrnchlnExtn', 'pltwtrBrnchlnExtn'], + }, + ], + }, + ], + }, + 'rmk', + ], + rowStartNum: 4, + fixedColumns: 3, + tabKey: 'factInstallStatusInp', + tabLabel: '시설별 설치현황' +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/sewagePipe/ManholeStatusInpColumn.ts b/apps/st/src/components/util/column/input/sewagePipe/ManholeStatusInpColumn.ts new file mode 100644 index 000000000..5732a82df --- /dev/null +++ b/apps/st/src/components/util/column/input/sewagePipe/ManholeStatusInpColumn.ts @@ -0,0 +1,331 @@ +import { footerInt } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 > 02.하수관로 > tab 5. 맨홀 현황 + +export const ManholeStatusInpColumn = { + columns: [ + // 행정구역 + { name: 'a1', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'a2', fieldName: '시군구', header: { text: '시군구' }, editable: false }, + + // 집중강우중점관리구역구분 + { name: 'a3', fieldName: '집중강우중점관리구역구분', header: { text: '집중강우중점관리구역구분' }, editable: false, footer: { text: '합계' } }, + + // 연도별총계 > 맨홀뚜껑 재질(개) + { name: 'b1', fieldName: '계', header: { text: '계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'b2', fieldName: '주철', header: { text: '주철' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'b3', fieldName: '주철(조화)', header: { text: '주철(조화)' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'b4', fieldName: '콘크리트', header: { text: '콘크리트' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'b5', fieldName: '기타', header: { text: '기타' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + // 연도별총계 > 추락방지시설설치맨홀(개) + { name: 'b6', fieldName: '추락방지시설설치맨홀(개)', header: { text: '추락방지시설설치맨홀(개)' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 2015년 이전 > 맨홀뚜껑 재질(개) + { name: 'c1', fieldName: '계', header: { text: '계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'c2', fieldName: '주철', header: { text: '주철' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'c3', fieldName: '주철(조화)', header: { text: '주철(조화)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'c4', fieldName: '콘크리트', header: { text: '콘크리트' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'c5', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + // 2015년 이전 > 추락방지시설설치맨홀(개) + { name: 'c6', fieldName: '추락방지시설설치맨홀(개)', header: { text: '추락방지시설설치맨홀(개)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 2015년 > 맨홀뚜껑 재질(개) + { name: 'd1', fieldName: '계', header: { text: '계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'd2', fieldName: '주철', header: { text: '주철' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'd3', fieldName: '주철(조화)', header: { text: '주철(조화)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'd4', fieldName: '콘크리트', header: { text: '콘크리트' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'd5', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + // 2015년 > 추락방지시설설치맨홀(개) + { name: 'd6', fieldName: '추락방지시설설치맨홀(개)', header: { text: '추락방지시설설치맨홀(개)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 2016년 > 맨홀뚜껑 재질(개) + { name: 'e1', fieldName: '계', header: { text: '계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'e2', fieldName: '주철', header: { text: '주철' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'e3', fieldName: '주철(조화)', header: { text: '주철(조화)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'e4', fieldName: '콘크리트', header: { text: '콘크리트' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'e5', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + // 2016년 > 추락방지시설설치맨홀(개) + { name: 'e6', fieldName: '추락방지시설설치맨홀(개)', header: { text: '추락방지시설설치맨홀(개)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 2017년 > 맨홀뚜껑 재질(개) + { name: 'f1', fieldName: '계', header: { text: '계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'f2', fieldName: '주철', header: { text: '주철' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'f3', fieldName: '주철(조화)', header: { text: '주철(조화)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'f4', fieldName: '콘크리트', header: { text: '콘크리트' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'f5', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + // 2017년 > 추락방지시설설치맨홀(개) + { name: 'f6', fieldName: '추락방지시설설치맨홀(개)', header: { text: '추락방지시설설치맨홀(개)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 2018년 > 맨홀뚜껑 재질(개) + { name: 'g1', fieldName: '계', header: { text: '계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'g2', fieldName: '주철', header: { text: '주철' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'g3', fieldName: '주철(조화)', header: { text: '주철(조화)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'g4', fieldName: '콘크리트', header: { text: '콘크리트' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'g5', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + // 2018년 > 추락방지시설설치맨홀(개) + { name: 'g6', fieldName: '추락방지시설설치맨홀(개)', header: { text: '추락방지시설설치맨홀(개)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 2019년 > 맨홀뚜껑 재질(개) + { name: 'h1', fieldName: '계', header: { text: '계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'h2', fieldName: '주철', header: { text: '주철' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'h3', fieldName: '주철(조화)', header: { text: '주철(조화)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'h4', fieldName: '콘크리트', header: { text: '콘크리트' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'h5', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + // 2019년 > 추락방지시설설치맨홀(개) + { name: 'h6', fieldName: '추락방지시설설치맨홀(개)', header: { text: '추락방지시설설치맨홀(개)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 2020년 > 맨홀뚜껑 재질(개) + { name: 'i1', fieldName: '계', header: { text: '계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'i2', fieldName: '주철', header: { text: '주철' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'i3', fieldName: '주철(조화)', header: { text: '주철(조화)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'i4', fieldName: '콘크리트', header: { text: '콘크리트' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'i5', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + // 2020년 > 추락방지시설설치맨홀(개) + { name: 'i6', fieldName: '추락방지시설설치맨홀(개)', header: { text: '추락방지시설설치맨홀(개)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 2021년 > 맨홀뚜껑 재질(개) + { name: 'j1', fieldName: '계', header: { text: '계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'j2', fieldName: '주철', header: { text: '주철' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'j3', fieldName: '주철(조화)', header: { text: '주철(조화)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'j4', fieldName: '콘크리트', header: { text: '콘크리트' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'j5', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + // 2021년 > 추락방지시설설치맨홀(개) + { name: 'j6', fieldName: '추락방지시설설치맨홀(개)', header: { text: '추락방지시설설치맨홀(개)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 2022년 > 맨홀뚜껑 재질(개) + { name: 'k1', fieldName: '계', header: { text: '계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'k2', fieldName: '주철', header: { text: '주철' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'k3', fieldName: '주철(조화)', header: { text: '주철(조화)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'k4', fieldName: '콘크리트', header: { text: '콘크리트' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'k5', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + // 2022년 > 추락방지시설설치맨홀(개) + { name: 'k6', fieldName: '추락방지시설설치맨홀(개)', header: { text: '추락방지시설설치맨홀(개)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 2023년 > 맨홀뚜껑 재질(개) + { name: 'l1', fieldName: '계', header: { text: '계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'l2', fieldName: '주철', header: { text: '주철' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'l3', fieldName: '주철(조화)', header: { text: '주철(조화)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'l4', fieldName: '콘크리트', header: { text: '콘크리트' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'l5', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + // 2023년 > 추락방지시설설치맨홀(개) + { name: 'l6', fieldName: '추락방지시설설치맨홀(개)', header: { text: '추락방지시설설치맨홀(개)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 2024년 > 맨홀뚜껑 재질(개) + { name: 'm1', fieldName: '계', header: { text: '계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'm2', fieldName: '주철', header: { text: '주철' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'm3', fieldName: '주철(조화)', header: { text: '주철(조화)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'm4', fieldName: '콘크리트', header: { text: '콘크리트' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'm5', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + // 2024년 > 추락방지시설설치맨홀(개) + { name: 'm6', fieldName: '추락방지시설설치맨홀(개)', header: { text: '추락방지시설설치맨홀(개)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 2025년 > 맨홀뚜껑 재질(개) + { name: 'n1', fieldName: '계', header: { text: '계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'n2', fieldName: '주철', header: { text: '주철' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'n3', fieldName: '주철(조화)', header: { text: '주철(조화)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'n4', fieldName: '콘크리트', header: { text: '콘크리트' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'n5', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + // 2025년 > 추락방지시설설치맨홀(개) + { name: 'n6', fieldName: '추락방지시설설치맨홀(개)', header: { text: '추락방지시설설치맨홀(개)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 비고 + { name: 'z1', fieldName: '비고', header: { text: '비고' } }, + ], + columnLayout: [ + { + name: 'adminArea', + hideChildHeaders: false, + header: { text: '행정구역' }, + items: ['a1', 'a2'], + }, + 'a3', + { + name: 'yearlyTotal', + hideChildHeaders: false, + header: { text: '연도별총계' }, + items: [ + { + name: 'yearlyTotalMaterial', + hideChildHeaders: false, + header: { text: '맨홀뚜껑 재질(개)' }, + items: ['b1', 'b2', 'b3', 'b4', 'b5'], + }, + 'b6', + ], + }, + { + name: 'before2015', + hideChildHeaders: false, + header: { text: '2015년 이전' }, + items: [ + { + name: 'before2015Material', + hideChildHeaders: false, + header: { text: '맨홀뚜껑 재질(개)' }, + items: ['c1', 'c2', 'c3', 'c4', 'c5'], + }, + 'c6', + ], + }, + { + name: 'year2015', + hideChildHeaders: false, + header: { text: '2015년' }, + items: [ + { + name: 'year2015Material', + hideChildHeaders: false, + header: { text: '맨홀뚜껑 재질(개)' }, + items: ['d1', 'd2', 'd3', 'd4', 'd5'], + }, + 'd6', + ], + }, + { + name: 'year2016', + hideChildHeaders: false, + header: { text: '2016년' }, + items: [ + { + name: 'year2016Material', + hideChildHeaders: false, + header: { text: '맨홀뚜껑 재질(개)' }, + items: ['e1', 'e2', 'e3', 'e4', 'e5'], + }, + 'e6', + ], + }, + { + name: 'year2017', + hideChildHeaders: false, + header: { text: '2017년' }, + items: [ + { + name: 'year2017Material', + hideChildHeaders: false, + header: { text: '맨홀뚜껑 재질(개)' }, + items: ['f1', 'f2', 'f3', 'f4', 'f5'], + }, + 'f6', + ], + }, + { + name: 'year2018', + hideChildHeaders: false, + header: { text: '2018년' }, + items: [ + { + name: 'year2018Material', + hideChildHeaders: false, + header: { text: '맨홀뚜껑 재질(개)' }, + items: ['g1', 'g2', 'g3', 'g4', 'g5'], + }, + 'g6', + ], + }, + { + name: 'year2019', + hideChildHeaders: false, + header: { text: '2019년' }, + items: [ + { + name: 'year2019Material', + hideChildHeaders: false, + header: { text: '맨홀뚜껑 재질(개)' }, + items: ['h1', 'h2', 'h3', 'h4', 'h5'], + }, + 'h6', + ], + }, + { + name: 'year2020', + hideChildHeaders: false, + header: { text: '2020년' }, + items: [ + { + name: 'year2020Material', + hideChildHeaders: false, + header: { text: '맨홀뚜껑 재질(개)' }, + items: ['i1', 'i2', 'i3', 'i4', 'i5'], + }, + 'i6', + ], + }, + { + name: 'year2021', + hideChildHeaders: false, + header: { text: '2021년' }, + items: [ + { + name: 'year2021Material', + hideChildHeaders: false, + header: { text: '맨홀뚜껑 재질(개)' }, + items: ['j1', 'j2', 'j3', 'j4', 'j5'], + }, + 'j6', + ], + }, + { + name: 'year2022', + hideChildHeaders: false, + header: { text: '2022년' }, + items: [ + { + name: 'year2022Material', + hideChildHeaders: false, + header: { text: '맨홀뚜껑 재질(개)' }, + items: ['k1', 'k2', 'k3', 'k4', 'k5'], + }, + 'k6', + ], + }, + { + name: 'year2023', + hideChildHeaders: false, + header: { text: '2023년' }, + items: [ + { + name: 'year2023Material', + hideChildHeaders: false, + header: { text: '맨홀뚜껑 재질(개)' }, + items: ['l1', 'l2', 'l3', 'l4', 'l5'], + }, + 'l6', + ], + }, + { + name: 'year2024', + hideChildHeaders: false, + header: { text: '2024년' }, + items: [ + { + name: 'year2024Material', + hideChildHeaders: false, + header: { text: '맨홀뚜껑 재질(개)' }, + items: ['m1', 'm2', 'm3', 'm4', 'm5'], + }, + 'm6', + ], + }, + { + name: 'year2025', + hideChildHeaders: false, + header: { text: '2025년' }, + items: [ + { + name: 'year2025Material', + hideChildHeaders: false, + header: { text: '맨홀뚜껑 재질(개)' }, + items: ['n1', 'n2', 'n3', 'n4', 'n5'], + }, + 'n6', + ], + }, + 'z1', + ], + rowStartNum: 3, + fixedColumns: 2, + tabKey: 'manholeStatusInp', + tabLabel: '맨홀 현황' +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/sewagePipe/PipeDiameterStatusInpColumn.ts b/apps/st/src/components/util/column/input/sewagePipe/PipeDiameterStatusInpColumn.ts new file mode 100644 index 000000000..af009e292 --- /dev/null +++ b/apps/st/src/components/util/column/input/sewagePipe/PipeDiameterStatusInpColumn.ts @@ -0,0 +1,63 @@ +import { footerDecimal2 } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 > 02.하수관로 > tab 4. 관경별 현황 + +export const PipeDiameterStatusInpColumn = { + columns: [ + // 행정구역 + { name: 'sido', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'sgg', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } }, + + // 관종 + { name: 'swrgPipeCd', fieldName: '관종', header: { text: '관종' }, editable: false }, + + // 총계(m) - 계산 + { name: 'totalDiameter', fieldName: '총계(m)', header: { text: '총계(m)' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 원형(m) + { name: 'under200', fieldName: '200mm 미만', header: { text: '200mm 미만' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'over200Undev300', fieldName: '200mm이상~300mm미만', header: { text: '200mm이상~300mm미만' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'over300Undev500', fieldName: '300mm이상~500mm미만', header: { text: '300mm이상~500mm미만' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'over500Undev500', fieldName: '500mm이상~800mm미만', header: { text: '500mm이상~800mm미만' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'over800', fieldName: '800이상mm', header: { text: '800이상mm' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 사각형(m) + { name: 'box', fieldName: '사각형(m)', header: { text: '사각형(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 기타(측구 개거 등) + { name: 'etc', fieldName: '기타(측구 개거 등)', header: { text: '기타(측구 개거 등)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 비고 + { name: 'rmk', fieldName: '비고', header: { text: '비고' } }, + ], + columnLayout: [ + { + name: 'adminArea', + hideChildHeaders: false, + header: { text: '행정구역' }, + items: ['sido', 'sgg'], + }, + 'swrgPipeCd', + 'totalDiameter', + { + name: 'circular', + hideChildHeaders: false, + header: { text: '원형(m)' }, + items: [ + 'under200', + 'over200Undev300', + 'over300Undev500', + 'over500Undev500', + 'over800', + ], + }, + 'box', + 'etc', + 'rmk', + ], + rowStartNum: 2, + fixedColumns: 1, + tabKey: 'pipeDiameterStatusInp', + tabLabel: '관경별 현황' +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/sewagePipe/PipeTypeStatusInpColumn.ts b/apps/st/src/components/util/column/input/sewagePipe/PipeTypeStatusInpColumn.ts new file mode 100644 index 000000000..e1e33a3c8 --- /dev/null +++ b/apps/st/src/components/util/column/input/sewagePipe/PipeTypeStatusInpColumn.ts @@ -0,0 +1,377 @@ +import { footerDecimal2 } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 > 02.하수관로 > tab 3. 관종별 현황 + +export const PipeTypeStatusInpColumn = { + columns: [ + // 행정구역 + { name: 'sido', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'sgg', fieldName: '시군구', header: { text: '시군구' }, editable: false }, + + // 관종 + { name: 'swrgPipeCd', fieldName: '관종', header: { text: '관종' }, footer: { text: '합계' }, editable: false }, + + // 총계 + { name: 'totalSum', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'totalLayingExtn', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'totalRemvlExtn', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 1996년 이전 (footer 없음) + { name: 'totalExtn22y', fieldName: '계', header: { text: '계' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn22y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn22y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 1996년 + { name: 'totalExtn01y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn01y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn01y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 1997년 + { name: 'totalExtn02y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn02y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn02y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 1998년 + { name: 'totalExtn03y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn03y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn03y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 1999년 + { name: 'totalExtn04y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn04y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn04y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 2000년 + { name: 'totalExtn05y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn05y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn05y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 2001년 + { name: 'totalExtn06y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn06y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn06y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 2002년 + { name: 'totalExtn07y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn07y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn07y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 2003년 + { name: 'totalExtn08y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn08y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn08y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 2004년 + { name: 'totalExtn09y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn09y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn09y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 2005년 + { name: 'totalExtn10y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn10y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn10y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 2006년 + { name: 'totalExtn11y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn11y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn11y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 2007년 + { name: 'totalExtn12y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn12y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn12y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 2008년 + { name: 'totalExtn13y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn13y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn13y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 2009년 + { name: 'totalExtn14y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn14y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn14y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 2010년 + { name: 'totalExtn15y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn15y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn15y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 2011년 + { name: 'totalExtn16y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn16y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn16y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 2012년 + { name: 'totalExtn17y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn17y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn17y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 2013년 + { name: 'totalExtn18y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn18y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn18y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 2014년 + { name: 'totalExtn19y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn19y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn19y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 2015년 + { name: 'totalExtn20y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn20y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn20y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 2016년 + { name: 'totalExtn21y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn21y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn21y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 2017년 + { name: 'totalExtn23y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn23y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn23y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 2018년 + { name: 'totalExtn24y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn24y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn24y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 2019년 + { name: 'totalExtn25y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn25y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn25y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 2020년 + { name: 'totalExtn26y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn26y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn26y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 2021년 + { name: 'totalExtn27y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn27y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn27y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 2022년 + { name: 'totalExtn28y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn28y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn28y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 2023년 + { name: 'totalExtn29y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn29y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn29y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 2024년 + { name: 'totalExtn30y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'layingExtn30y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'remvlExtn30y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 2025년 + { name: 'layingExtn', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 비고 + { name: 'rmk', fieldName: '비고', header: { text: '비고' } }, + ], + columnLayout: [ + { + name: 'adminArea', + hideChildHeaders: false, + header: { text: '행정구역' }, + items: ['sido', 'sgg'], + }, + 'swrgPipeCd', + { + name: 'totalGroup', + hideChildHeaders: false, + header: { text: '총계' }, + items: ['totalSum', 'totalLayingExtn', 'totalRemvlExtn'], + }, + { + name: 'year1996Before', + hideChildHeaders: false, + header: { text: '1996년 이전' }, + items: ['totalExtn22y', 'layingExtn22y', 'remvlExtn22y'], + }, + { + name: 'year1996', + hideChildHeaders: false, + header: { text: '1996년' }, + items: ['totalExtn01y', 'layingExtn01y', 'remvlExtn01y'], + }, + { + name: 'year1997', + hideChildHeaders: false, + header: { text: '1997년' }, + items: ['totalExtn02y', 'layingExtn02y', 'remvlExtn02y'], + }, + { + name: 'year1998', + hideChildHeaders: false, + header: { text: '1998년' }, + items: ['totalExtn03y', 'layingExtn03y', 'remvlExtn03y'], + }, + { + name: 'year1999', + hideChildHeaders: false, + header: { text: '1999년' }, + items: ['totalExtn04y', 'layingExtn04y', 'remvlExtn04y'], + }, + { + name: 'year2000', + hideChildHeaders: false, + header: { text: '2000년' }, + items: ['totalExtn05y', 'layingExtn05y', 'remvlExtn05y'], + }, + { + name: 'year2001', + hideChildHeaders: false, + header: { text: '2001년' }, + items: ['totalExtn06y', 'layingExtn06y', 'remvlExtn06y'], + }, + { + name: 'year2002', + hideChildHeaders: false, + header: { text: '2002년' }, + items: ['totalExtn07y', 'layingExtn07y', 'remvlExtn07y'], + }, + { + name: 'year2003', + hideChildHeaders: false, + header: { text: '2003년' }, + items: ['totalExtn08y', 'layingExtn08y', 'remvlExtn08y'], + }, + { + name: 'year2004', + hideChildHeaders: false, + header: { text: '2004년' }, + items: ['totalExtn09y', 'layingExtn09y', 'remvlExtn09y'], + }, + { + name: 'year2005', + hideChildHeaders: false, + header: { text: '2005년' }, + items: ['totalExtn10y', 'layingExtn10y', 'remvlExtn10y'], + }, + { + name: 'year2006', + hideChildHeaders: false, + header: { text: '2006년' }, + items: ['totalExtn11y', 'layingExtn11y', 'remvlExtn11y'], + }, + { + name: 'year2007', + hideChildHeaders: false, + header: { text: '2007년' }, + items: ['totalExtn12y', 'layingExtn12y', 'remvlExtn12y'], + }, + { + name: 'year2008', + hideChildHeaders: false, + header: { text: '2008년' }, + items: ['totalExtn13y', 'layingExtn13y', 'remvlExtn13y'], + }, + { + name: 'year2009', + hideChildHeaders: false, + header: { text: '2009년' }, + items: ['totalExtn14y', 'layingExtn14y', 'remvlExtn14y'], + }, + { + name: 'year2010', + hideChildHeaders: false, + header: { text: '2010년' }, + items: ['totalExtn15y', 'layingExtn15y', 'remvlExtn15y'], + }, + { + name: 'year2011', + hideChildHeaders: false, + header: { text: '2011년' }, + items: ['totalExtn16y', 'layingExtn16y', 'remvlExtn16y'], + }, + { + name: 'year2012', + hideChildHeaders: false, + header: { text: '2012년' }, + items: ['totalExtn17y', 'layingExtn17y', 'remvlExtn17y'], + }, + { + name: 'year2013', + hideChildHeaders: false, + header: { text: '2013년' }, + items: ['totalExtn18y', 'layingExtn18y', 'remvlExtn18y'], + }, + { + name: 'year2014', + hideChildHeaders: false, + header: { text: '2014년' }, + items: ['totalExtn19y', 'layingExtn19y', 'remvlExtn19y'], + }, + { + name: 'year2015', + hideChildHeaders: false, + header: { text: '2015년' }, + items: ['totalExtn20y', 'layingExtn20y', 'remvlExtn20y'], + }, + { + name: 'year2016', + hideChildHeaders: false, + header: { text: '2016년' }, + items: ['totalExtn21y', 'layingExtn21y', 'remvlExtn21y'], + }, + { + name: 'year2017', + hideChildHeaders: false, + header: { text: '2017년' }, + items: ['totalExtn23y', 'layingExtn23y', 'remvlExtn23y'], + }, + { + name: 'year2018', + hideChildHeaders: false, + header: { text: '2018년' }, + items: ['totalExtn24y', 'layingExtn24y', 'remvlExtn24y'], + }, + { + name: 'year2019', + hideChildHeaders: false, + header: { text: '2019년' }, + items: ['totalExtn25y', 'layingExtn25y', 'remvlExtn25y'], + }, + { + name: 'year2020', + hideChildHeaders: false, + header: { text: '2020년' }, + items: ['totalExtn26y', 'layingExtn26y', 'remvlExtn26y'], + }, + { + name: 'year2021', + hideChildHeaders: false, + header: { text: '2021년' }, + items: ['totalExtn27y', 'layingExtn27y', 'remvlExtn27y'], + }, + { + name: 'year2022', + hideChildHeaders: false, + header: { text: '2022년' }, + items: ['totalExtn28y', 'layingExtn28y', 'remvlExtn28y'], + }, + { + name: 'year2023', + hideChildHeaders: false, + header: { text: '2023년' }, + items: ['totalExtn29y', 'layingExtn29y', 'remvlExtn29y'], + }, + { + name: 'year2024', + hideChildHeaders: false, + header: { text: '2024년' }, + items: ['totalExtn30y', 'layingExtn30y', 'remvlExtn30y'], + }, + 'layingExtn', + 'rmk', + ], + rowStartNum: 2, + fixedColumns: 2, + tabKey: 'pipeTypeStatusInp', + tabLabel: '관종별 현황' + }; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/sewagePipe/RegionalFactStatusInpColumn.ts b/apps/st/src/components/util/column/input/sewagePipe/RegionalFactStatusInpColumn.ts new file mode 100644 index 000000000..a02919150 --- /dev/null +++ b/apps/st/src/components/util/column/input/sewagePipe/RegionalFactStatusInpColumn.ts @@ -0,0 +1,159 @@ +import { footerInt, footerDecimal1, footerDecimal2, footerDecimal3 } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 > 02.하수관로 > tab 1. 지역별 시설현황 + +export const RegionalFactStatusInpColumn = { + columns: [ + // 행정구역 + { name: 'sido', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'sgg', fieldName: '시군구', header: { text: '시군구' }, footer: { text: '합계' }, editable: false }, + + // 기본 정보 + { name: 'targetYr', fieldName: '하수정비기본계획목표년도', header: { text: '하수정비기본계획목표년도' }}, + { name: 'piplImprnPlanExtn', fieldName: '총관로정비계획연장(m)', header: { text: '총관로정비계획연장(m)' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'piplPlanExtn', fieldName: '총계획연장(m)', header: { text: '총계획연장(m)' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'piplFcltyExtn', fieldName: '총시설연장(m)', header: { text: '총시설연장(m)' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'piplSupprt', fieldName: '하수관로보급률(%)', header: { text: '하수관로보급률(%)' }, footer: footerDecimal1, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 합류식(m) + { name: 'cmbnPlanAr', fieldName: '관로정비계획면적(㎢)', header: { text: '관로정비계획면적(㎢)' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'cmbnImprnPlanExtn', fieldName: '관로정비계획연장(m)', header: { text: '관로정비계획연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'cmbnPlanExtn', fieldName: '계획연장(m)', header: { text: '계획연장(m)' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'cmbnFcltyExtn', fieldName: '시설연장(m)', header: { text: '시설연장(m)' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'cmbnRctglExtn', fieldName: '사각형(m)', header: { text: '사각형(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'cmbnCicleExtn', fieldName: '원형(m)', header: { text: '원형(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'cmbnOpswrExtn', fieldName: '개거(m)', header: { text: '개거(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'cmbnSdgttrExtn', fieldName: '측구(m)', header: { text: '측구(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 분류식(m) > 오수관로 + { name: 'pltwtrPlanAr', fieldName: '관로정비계획면적(㎢)', header: { text: '관로정비계획면적(㎢)' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'pltwtrImprnPlanExtn', fieldName: '관로정비계획연장(m)', header: { text: '관로정비계획연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'pltwtrPlanExtn', fieldName: '계획연장(m)', header: { text: '계획연장(m)' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'pltwtrFlctyExtn', fieldName: '시설연장(m)', header: { text: '시설연장(m)' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'pltwtrSqreExtn', fieldName: '사각형(m)', header: { text: '사각형(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'pltwtrCiclExtn', fieldName: '원형(m)', header: { text: '원형(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 분류식(m) > 우수관로 + { name: 'rainwtrPlanAr', fieldName: '관로정비계획면적(㎢)', header: { text: '관로정비계획면적(㎢)' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'rainwtrImprnPlanExtn', fieldName: '관로정비계획연장(m)', header: { text: '관로정비계획연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'rainwtrPlanExtn', fieldName: '계획연장(m)', header: { text: '계획연장(m)' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'rainwtrFlctyExtn', fieldName: '시설연장(m)', header: { text: '시설연장(m)' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'rainwtrSqreExtn', fieldName: '사각형(m)', header: { text: '사각형(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'rainwtrCiclExtn', fieldName: '원형(m)', header: { text: '원형(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'rainwtrOpswrExtn', fieldName: '개거(m)', header: { text: '개거(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + { name: 'rainwtrSdgttrExtn', fieldName: '측구(m)', header: { text: '측구(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 합류식맨홀(개소) + { name: 'mnhleSite', fieldName: '합류식맨홀(개소)', header: { text: '합류식맨홀(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 분류식 > 오수맨홀, 우수맨홀 + { name: 'pltwtrMnhleSite', fieldName: '오수맨홀(개소)', header: { text: '오수맨홀(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'rainwtrMnhleSite', fieldName: '우수맨홀(개소)', header: { text: '우수맨홀(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + { name: 'pltwtrRcvrSite', fieldName: '오수받이(개소)', header: { text: '오수받이(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'rainwtrRcvrSite', fieldName: '빗물받이(개소)', header: { text: '빗물받이(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'soilspllSite', fieldName: '우수토실(개소)', header: { text: '우수토실(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'soilholeSite', fieldName: '우수토구(개소)', header: { text: '우수토구(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 하수도대장 전산화 + { name: 'sewerRegstrCmptExtn', fieldName: '하수도대장전산화연장(m)', header: { text: '하수도대장전산화연장(m)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'sewerRegstrCmptRt', fieldName: '하수도대장전산화율(%)', header: { text: '하수도대장전산화율(%)' }, footer: footerDecimal1, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 비고 + { name: 'rmk', fieldName: '비고', header: { text: '비고' } }, + ], + columnLayout: [ + { + name: 'adminArea', + hideChildHeaders: false, + header: { text: '행정구역' }, + items: ['sido', 'sgg'], + }, + 'targetYr', + 'piplImprnPlanExtn', + 'piplPlanExtn', + 'piplFcltyExtn', + 'piplSupprt', + { + name: 'combined', + hideChildHeaders: false, + header: { text: '합류식(m)' }, + items: [ + 'cmbnPlanAr', + 'cmbnImprnPlanExtn', + 'cmbnPlanExtn', + 'cmbnFcltyExtn', + { + name: 'cmbnCulvert', + hideChildHeaders: false, + header: { text: '암거' }, + items: ['cmbnRctglExtn', 'cmbnCicleExtn'], + }, + 'cmbnOpswrExtn', + 'cmbnSdgttrExtn', + ], + }, + { + name: 'separated', + hideChildHeaders: false, + header: { text: '분류식(m)' }, + items: [ + { + name: 'pollutedWater', + hideChildHeaders: false, + header: { text: '오수관로' }, + items: [ + 'pltwtrPlanAr', + 'pltwtrImprnPlanExtn', + 'pltwtrPlanExtn', + 'pltwtrFlctyExtn', + { + name: 'pltwtrCulvert', + hideChildHeaders: false, + header: { text: '암거' }, + items: ['pltwtrSqreExtn', 'pltwtrCiclExtn'], + }, + ], + }, + { + name: 'rainWater', + hideChildHeaders: false, + header: { text: '우수관로' }, + items: [ + 'rainwtrPlanAr', + 'rainwtrImprnPlanExtn', + 'rainwtrPlanExtn', + 'rainwtrFlctyExtn', + { + name: 'rainwtrCulvert', + hideChildHeaders: false, + header: { text: '암거' }, + items: ['rainwtrSqreExtn', 'rainwtrCiclExtn'], + }, + 'rainwtrOpswrExtn', + 'rainwtrSdgttrExtn', + ], + }, + ], + }, + 'mnhleSite', + { + name: 'separatedManhole', + hideChildHeaders: false, + header: { text: '분류식' }, + items: ['pltwtrMnhleSite', 'rainwtrMnhleSite'], + }, + 'pltwtrRcvrSite', + 'rainwtrRcvrSite', + 'soilspllSite', + 'soilholeSite', + 'sewerRegstrCmptExtn', + 'sewerRegstrCmptRt', + 'rmk', + ], + rowStartNum: 4, + fixedColumns: 1, + tabKey: 'regionalFactStatusInp', + tabLabel: '지역별 시설현황' +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/sewagePipeMaintain/PipeDredgingStatusInpColumn.ts b/apps/st/src/components/util/column/input/sewagePipeMaintain/PipeDredgingStatusInpColumn.ts new file mode 100644 index 000000000..983df126e --- /dev/null +++ b/apps/st/src/components/util/column/input/sewagePipeMaintain/PipeDredgingStatusInpColumn.ts @@ -0,0 +1,82 @@ +import { footerInt, footerDecimal1, footerDecimal2 } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 > 03.하수관로 유지관리 > tab 2. 관로준설 +export const PipeDredgingStatusInpColumn = { + columns: [ + // 행정구역 + { name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } }, + + // 하수관로 총 시설연장 + { name: 'PIPL_FCLTY_EXTN', fieldName: '하수관로총시설연장(m)', header: { text: '하수관로총시설연장(m)' }, editable: false, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 준설연장 및 개소수 > 관로(m) + { name: 'DRDG_PIPE_SM', fieldName: '합계', header: { text: '합계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'DRDG_CMBN_PIPE', fieldName: '합류식', header: { text: '합류식' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 준설연장 및 개소수 > 관로(m) > 분류식 + { name: 'DRDG_RAINWTR_PIPE', fieldName: '우수', header: { text: '우수' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'DRDG_PLTWTR_PIPE', fieldName: '오수', header: { text: '오수' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 준설연장 및 개소수 > 맨홀, 오·우수받이, 토실·토구 + { name: 'MNHLE_SITE', fieldName: '맨홀(개소)', header: { text: '맨홀(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'PLTWTR_RCVR_SITE', fieldName: '오·우수받이(개소)', header: { text: '오·우수받이(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'SOILSPLL_SOILHOLE_SITE', fieldName: '토실·토구(개소)', header: { text: '토실·토구(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 계획 & 비율 + { name: 'DRDG_TRGET_EXTN', fieldName: '준설 계획 연장(m)', header: { text: '준설 계획 연장(m)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'DRDG_TRGET_RT', fieldName: '계획대비 준설율(%)', header: { text: '계획대비 준설율(%)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'PIPL_FCLTY_RT', fieldName: '총시설연장대비 준설율(%)', header: { text: '총시설연장대비 준설율(%)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 준설실적 + { name: 'DRDG_ACMSLT_AMOUNT', fieldName: '금액(백만원)', header: { text: '금액(백만원)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'DRDG_ACMSLT_QY', fieldName: '준설량(m³)', header: { text: '준설량(m³)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 준설토사처리 + { name: 'DRDG_ACMSLT_UNTPC', fieldName: '준설토처리비(백만원)', header: { text: '준설토처리비(백만원)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'DRDG_ETND_SM', fieldName: '처리량 계(톤)', header: { text: '처리량 계(톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'RCLM', fieldName: '매립', header: { text: '매립' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'OCEAN_DPSL', fieldName: '해양투기', header: { text: '해양투기' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'RUSE', fieldName: '재활용', header: { text: '재활용' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'ETC', fieldName: '기타(소각, 고형화, 시멘트원료 등)', header: { text: '기타(소각, 고형화, 시멘트원료 등)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 비고 + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + ], + columnLayout: [ + { name: 'group_admin', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'PIPL_FCLTY_EXTN', + { + name: 'group_dredge_info', + header: { text: '준설연장 및 개소수' }, + items: [ + { + name: 'group_pipe', + header: { text: '관로(m)' }, + items: [ + 'DRDG_PIPE_SM', 'DRDG_CMBN_PIPE', + { name: 'group_sep', header: { text: '분류식' }, items: ['DRDG_RAINWTR_PIPE', 'DRDG_PLTWTR_PIPE'] } + ] + }, + 'MNHLE_SITE', 'PLTWTR_RCVR_SITE', 'SOILSPLL_SOILHOLE_SITE' + ] + }, + 'DRDG_TRGET_EXTN', 'DRDG_TRGET_RT', 'PIPL_FCLTY_RT', + { + name: 'group_perf', + header: { text: '준설실적' }, + items: ['DRDG_ACMSLT_AMOUNT', 'DRDG_ACMSLT_QY'] + }, + { + name: 'group_soil_treat', + header: { text: '준설토사처리' }, + items: ['DRDG_ACMSLT_UNTPC', 'DRDG_ETND_SM', 'RCLM', 'OCEAN_DPSL', 'RUSE', 'ETC'] + }, + 'RMK' + ], + rowStartNum: 4, + fixedColumns: 1, + tabKey: 'pipeDredgingStatusInp', + tabLabel: '관로준설' +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/sewagePipeMaintain/RepairStatusInpColumn.ts b/apps/st/src/components/util/column/input/sewagePipeMaintain/RepairStatusInpColumn.ts new file mode 100644 index 000000000..5f22b373f --- /dev/null +++ b/apps/st/src/components/util/column/input/sewagePipeMaintain/RepairStatusInpColumn.ts @@ -0,0 +1,115 @@ +import { footerInt, footerDecimal1 } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 > 03.하수관로 유지관리 > tab 1. 개보수 +export const RepairStatusInpColumn = { + columns: [ + // 행정구역 + { name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } }, + + // 개·보수관로 > 전체보수(m) + { name: 'RPAIR_PIPE_SM', fieldName: '소계', header: { text: '소계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'RPAIR_CMBN_PIPE', fieldName: '합류식', header: { text: '합류식' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 개·보수관로 > 전체보수(m) > 분류식 + { name: 'RPAIR_RAINWTR_PIPE', fieldName: '우수', header: { text: '우수' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'RPAIR_PLTWTR_PIPE', fieldName: '오수', header: { text: '오수' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 개·보수관로 > 부분보수(개소) + { name: 'RPAIR_PIPE_SM2', fieldName: '소계', header: { text: '소계' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'RPAIR_CMBN_PIPE2', fieldName: '합류식', header: { text: '합류식' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 개·보수관로 > 부분보수(개소) > 분류식 + { name: 'RPAIR_RAINWTR_PIPE2', fieldName: '우수', header: { text: '우수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'RPAIR_PLTWTR_PIPE2', fieldName: '오수', header: { text: '오수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 맨홀(개소) + { name: 'MNHLE_SITE', fieldName: '합류식맨홀', header: { text: '합류식맨홀' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 맨홀(개소) > 분류식 + { name: 'PLTWTR_MNHLE_SITE', fieldName: '오수맨홀', header: { text: '오수맨홀' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'RAINWTR_MNHLE_SITE', fieldName: '우수맨홀', header: { text: '우수맨홀' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 받이 + { name: 'PLTWTR_RCVR_SITE', fieldName: '오수받이(개소)', header: { text: '오수받이(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'RAINWTR_RCVR_SITE', fieldName: '빗물받이(개소)', header: { text: '빗물받이(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 토실, 토구 + { name: 'SOILSPLL_SITE', fieldName: '토실(개소)', header: { text: '토실(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'SOILHOLE_SITE', fieldName: '토구(개소)', header: { text: '토구(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 수밀검사CCTV조사량(m) + { name: 'CCTV', fieldName: '수밀검사CCTV조사량(m)', header: { text: '수밀검사CCTV조사량(m)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 비고 + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + ], + columnLayout: [ + { + name: 'group_admin', + header: { text: '행정구역' }, + items: ['SIDO', 'GUGUN'] + }, + { + name: 'group_repair_main', + header: { text: '개·보수관로' }, + items: [ + { + name: 'group_full_repair', + header: { text: '전체보수(m)' }, + items: [ + 'RPAIR_PIPE_SM', + 'RPAIR_CMBN_PIPE', + { + name: 'group_full_sep', + header: { text: '분류식' }, + items: ['RPAIR_RAINWTR_PIPE', 'RPAIR_PLTWTR_PIPE'] + } + ] + }, + { + name: 'group_part_repair', + header: { text: '부분보수(개소)' }, + items: [ + 'RPAIR_PIPE_SM2', + 'RPAIR_CMBN_PIPE2', + { + name: 'group_part_sep', + header: { text: '분류식' }, + items: ['RPAIR_RAINWTR_PIPE2', 'RPAIR_PLTWTR_PIPE2'] + } + ] + } + ] + }, + { + name: 'group_manhole', + header: { text: '맨홀(개소)' }, + items: [ + 'MNHLE_SITE', + { + name: 'group_mh_sep', + header: { text: '분류식' }, + items: ['PLTWTR_MNHLE_SITE', 'RAINWTR_MNHLE_SITE'] + } + ] + }, + { + name: 'group_inlet', + header: { text: '받이' }, + items: ['PLTWTR_RCVR_SITE', 'RAINWTR_RCVR_SITE'] + }, + { + name: 'group_chamber', + header: { text: '토실,토구' }, + items: ['SOILSPLL_SITE', 'SOILHOLE_SITE'] + }, + 'CCTV', + 'RMK' + ], + rowStartNum: 4, + fixedColumns: 1, + tabKey: 'repairStatusInp', + tabLabel: '개보수' +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/sewageReservoirPump/RainfallPumpStationInpColumn.ts b/apps/st/src/components/util/column/input/sewageReservoirPump/RainfallPumpStationInpColumn.ts new file mode 100644 index 000000000..e000f4904 --- /dev/null +++ b/apps/st/src/components/util/column/input/sewageReservoirPump/RainfallPumpStationInpColumn.ts @@ -0,0 +1,211 @@ +import { footerInt, footerDecimal1, footerDecimal2, footerDecimal3 } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + + // 하수도 통계 관리 > 04.하수저류시설 및 빗물펌프장 > tab 2: 빗물펌프장 +export const RainfallPumpStationInpColumn = { + columns: [ + // Hidden Columns : 시스템 관리를 위한 값 + { name: 'sidoCd', fieldName: '시도코드', header: { text: '시도코드' }, hidden: true }, + { name: 'adstDstrCd', fieldName: '행정구역코드', header: { text: '행정구역코드' }, hidden: true }, + { name: 'adstCd', fieldName: '행정코드', header: { text: '행정코드' }, hidden: true }, + { name: 'swgeStrgeFctSeq', fieldName: '시설SEQ', header: { text: '시설SEQ' }, hidden: true }, + { name: 'yr', fieldName: '년도', header: { text: '년도' }, hidden: true }, + { name: 'odr', fieldName: '차수', header: { text: '차수' }, hidden: true }, + { name: 'bsnmCd', fieldName: '사업자코드', header: { text: '사업자코드' }, hidden: true }, + + // Visible Columns + // 행정구역 + { name: 'sido', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'gugun', fieldName: '시군구', header: { text: '시군구' }, footer: { text: '합계' } }, + + // 시설명 + { name: 'swgeStrgeNm', fieldName: '시설명', header: { text: '시설명' } }, + + // 위치 + { name: 'addr', fieldName: '위치', header: { text: '위치' } }, + + // 시설물종류 + { name: 'facRes', fieldName: '시설물종류', header: { text: '시설물종류' } }, + + // 가동개시 년월일 yyyy-mm-dd + { name: 'rnDt', fieldName: '가동개시 년월일 yyyy-mm-dd', header: { text: '가동개시 년월일 yyyy-mm-dd' } }, + + // 계획설계빈도 + { name: 'plDsgnFrqnc', fieldName: '계획설계빈도', header: { text: '계획설계빈도' } }, + + // 집수구역면적(㎢) + { name: 'dgrAr', fieldName: '집수구역면적(㎢)', header: { text: '집수구역면적(㎢)' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + + // 유수지 + { name: 'revFnnc', fieldName: '유수지유무(유,무)', header: { text: '유수지유무(유,무)' } }, + { name: 'revCpcty', fieldName: '저류랑(m³)', header: { text: '저류랑(m³)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 흡수정 + { name: 'absCpcty', fieldName: '저류랑(m³)', header: { text: '저류랑(m³)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 펌프시설 > 총양수량(m³/min) + { name: 'wpCpcty', fieldName: '총양수량(m³/min)', header: { text: '총양수량(m³/min)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 펌프시설1 + { name: 'pumpCalbr', fieldName: '구경(mm)', header: { text: '구경(mm)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'pumpHpCo', fieldName: '대당 양수량(m³/min·대)', header: { text: '대당 양수량(m³/min·대)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'pumpCo', fieldName: '펌프대수', header: { text: '펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'pumpPreparCo', fieldName: '예비펌프대수', header: { text: '예비펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 펌프시설2 + { name: 'pumpCalbr2', fieldName: '구경(mm)', header: { text: '구경(mm)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'pumpHpCo2', fieldName: '대당 양수량(m³/min·대)', header: { text: '대당 양수량(m³/min·대)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'pumpCo2', fieldName: '펌프대수', header: { text: '펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'pumpPreparCo2', fieldName: '예비펌프대수', header: { text: '예비펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 펌프시설3 + { name: 'pumpCalbr3', fieldName: '구경(mm)', header: { text: '구경(mm)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'pumpHpCo3', fieldName: '대당 양수량(m³/min·대)', header: { text: '대당 양수량(m³/min·대)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'pumpCo3', fieldName: '펌프대수', header: { text: '펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'pumpPreparCo3', fieldName: '예비펌프대수', header: { text: '예비펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 부대시설 + { name: 'dorZeqpFnnc', fieldName: '전처리시설유무(유/무)', header: { text: '전처리시설유무(유/무)' } }, + { name: 'selfEtrqFnnc', fieldName: '탈취설비유무(유/무)', header: { text: '탈취설비유무(유/무)' } }, + { name: 'freFnnc', fieldName: '자가발전유무(유/무)', header: { text: '자가발전유무(유/무)' } }, + + // 방류수역 + { name: 'disWat', fieldName: '방류수역', header: { text: '방류수역' } }, + + // 최종처분량(ton/년) + { name: 'sdmtTreCpcty', fieldName: '침사', header: { text: '침사' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'iprtTreCpcty', fieldName: '협잡물', header: { text: '협잡물' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 최종처분방법 + { name: 'sdmtTreMth', fieldName: '침사', header: { text: '침사' } }, + { name: 'iprtTreMth', fieldName: '협잡물', header: { text: '협잡물' } }, + + // 운영실적 + { name: 'yrstpRnDyn', fieldName: '가동일수(일/년)', header: { text: '가동일수(일/년)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, // 입력값 3자리만 가능 + { name: 'dsplcmntAmnt', fieldName: '총배수량(m³/년)', header: { text: '총배수량(m³/년)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' }, + + // 비고 + { name: 'rmk', fieldName: '비고', header: { text: '비고' } }, + { name: 'chckerOpinion', fieldName: '검토자의견', header: { text: '검토자의견' } }, + { name: 'wrterOpinion', fieldName: '사용자의견', header: { text: '사용자의견' } }, + ], + + columnLayout: [ + { + name: 'adminArea', + hideChildHeaders: false, + header: { text: '행정구역' }, + items: ['sido', 'gugun'], + }, + 'swgeStrgeNm', + 'addr', + 'facRes', + 'rnDt', + 'plDsgnFrqnc', + 'dgrAr', + { + name: 'reservoir', + hideChildHeaders: false, + header: { text: '유수지' }, + items: [ + 'revFnnc', + 'revCpcty', + ], + }, + { + name: 'absorptionWell', + hideChildHeaders: false, + header: { text: '흡수정' }, + items: [ + 'absCpcty', + ], + }, + { + name: 'pumpSystem', + hideChildHeaders: false, + header: { text: '펌프시설' }, + items: [ + 'wpCpcty', + { + name: 'pumpSystem1', + hideChildHeaders: false, + header: { text: '펌프시설1' }, + items: [ + 'pumpCalbr', + 'pumpHpCo', + 'pumpCo', + 'pumpPreparCo', + ], + }, + { + name: 'pumpSystem2', + hideChildHeaders: false, + header: { text: '펌프시설2' }, + items: [ + 'pumpCalbr2', + 'pumpHpCo2', + 'pumpCo2', + 'pumpPreparCo2', + ], + }, + { + name: 'pumpSystem3', + hideChildHeaders: false, + header: { text: '펌프시설3' }, + items: [ + 'pumpCalbr3', + 'pumpHpCo3', + 'pumpCo3', + 'pumpPreparCo3', + ], + }, + ], + }, + { + name: 'auxiliaryFacilities', + hideChildHeaders: false, + header: { text: '부대시설' }, + items: [ + 'dorZeqpFnnc', + 'selfEtrqFnnc', + 'freFnnc', + ], + }, + 'disWat', + { + name: 'finalDisposalAmount', + hideChildHeaders: false, + header: { text: '최종처분량(ton/년)' }, + items: [ + 'sdmtTreCpcty', + 'iprtTreCpcty', + ], + }, + { + name: 'finalDisposalMethod', + hideChildHeaders: false, + header: { text: '최종처분방법' }, + items: [ + 'sdmtTreMth', + 'iprtTreMth', + ], + }, + { + name: 'operationResults', + hideChildHeaders: false, + header: { text: '운영실적' }, + items: [ + 'yrstpRnDyn', + 'dsplcmntAmnt', + ], + }, + 'rmk', + 'chckerOpinion', + 'wrterOpinion', + ], + + rowStartNum: 3, + fixedColumns: 2, + tabKey: 'rainfallPumpStationInp', + tabLabel: '빗물펌프장', +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/sewageReservoirPump/SewageReservoirInpColumn.ts b/apps/st/src/components/util/column/input/sewageReservoirPump/SewageReservoirInpColumn.ts new file mode 100644 index 000000000..8dc69e5e9 --- /dev/null +++ b/apps/st/src/components/util/column/input/sewageReservoirPump/SewageReservoirInpColumn.ts @@ -0,0 +1,213 @@ +import { footerInt, footerDecimal1, footerDecimal3 } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + + // 하수도 통계 관리 > 04.하수저류시설 및 빗물펌프장 > tab 1: 하수저류시설 +export const SewageReservoirInpColumn = { + columns: [ + // Hidden Columns : 시스템 관리를 위한 값 + { name: 'sidoCd', fieldName: '시도코드', header: { text: '시도코드' }, hidden: true }, + { name: 'adstDstrCd', fieldName: '행정구역코드', header: { text: '행정구역코드' }, hidden: true }, + { name: 'adstCd', fieldName: '행정코드', header: { text: '행정코드' }, hidden: true }, + { name: 'swgeStrgeFctSeq', fieldName: '시설SEQ', header: { text: '시설SEQ' }, hidden: true }, + + // Visible Columns + // 행정구역 + { name: 'sido', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'gugun', fieldName: '시군구', header: { text: '시군구' }, footer: { text: '합계' } }, + + // 기본 정보 + { name: 'swgeStrgeNm', fieldName: '시설명', header: { text: '시설명' } }, + { name: 'addr', fieldName: '위치', header: { text: '위치' } }, + { name: 'rnDt', fieldName: '가동개시 년월일 yyyy-mm-dd', header: { text: '가동개시 년월일 yyyy-mm-dd' } }, + { name: 'designFrequency', fieldName: '계획설계빈도', header: { text: '계획설계빈도' } }, + + // 설치목적 + { name: 'floodingPrevention', fieldName: '침수예방', header: { text: '침수예방' } }, + { name: 'waterQualityReduction', fieldName: '수질오염저감', header: { text: '수질오염저감' } }, + { name: 'sewerageReuseUse', fieldName: '하수재이용용도', header: { text: '하수재이용용도' } }, + + // 시설 구조 + { name: 'facilityStructure', fieldName: '시설구조(일반지하식,지하터널식,기타)', header: { text: '시설구조(일반지하식,지하터널식,기타)' } }, + + // 집수구역면적 + { name: 'catchmentAreaSize', fieldName: '집수구역면적(㎢)', header: { text: '집수구역면적(㎢)' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + + // 시설용량 + { name: 'facilityCapacity', fieldName: '시설용량(m³)', header: { text: '시설용량(m³)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 펌프시설 > 총양수량 + { name: 'totalPumpingCapacity', fieldName: '총양수량(m³/min)', header: { text: '총양수량(m³/min)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 펌프시설 1 + { name: 'pump1Diameter', fieldName: '구경(mm)', header: { text: '구경(mm)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'pump1PumpingCapacity', fieldName: '대당 양수량(m³/min·대)', header: { text: '대당 양수량(m³/min·대)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'pump1Count', fieldName: '펌프대수', header: { text: '펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'pump1StandbyCount', fieldName: '예비펌프대수', header: { text: '예비펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 펌프시설 2 + { name: 'pump2Diameter', fieldName: '구경(mm)', header: { text: '구경(mm)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'pump2PumpingCapacity', fieldName: '대당 양수량(m³/min·대)', header: { text: '대당 양수량(m³/min·대)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'pump2Count', fieldName: '펌프대수', header: { text: '펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'pump2StandbyCount', fieldName: '예비펌프대수', header: { text: '예비펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 펌프시설 3 + { name: 'pump3Diameter', fieldName: '구경(mm)', header: { text: '구경(mm)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'pump3PumpingCapacity', fieldName: '대당 양수량(m³/min·대)', header: { text: '대당 양수량(m³/min·대)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'pump3Count', fieldName: '펌프대수', header: { text: '펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'pump3StandbyCount', fieldName: '예비펌프대수', header: { text: '예비펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 부대시설 + { name: 'preprocessingFacility', fieldName: '전처리시설유무(유/무)', header: { text: '전처리시설유무(유/무)' } }, + { name: 'odorRemovalEquipment', fieldName: '탈취설비유무(유/무)', header: { text: '탈취설비유무(유/무)' } }, + { name: 'selfGenerationEquipment', fieldName: '자가발전유무(유/무)', header: { text: '자가발전유무(유/무)' } }, + + // 배수 방식 + { name: 'drainageMethod', fieldName: '배수방식(자연배수,강제배수)', header: { text: '배수방식(자연배수,강제배수)' } }, + + // 저류 방식 + { name: 'storageMethod', fieldName: '저류방식(직렬,병렬)', header: { text: '저류방식(직렬,병렬)' } }, + + // 저류수 처리방법 + { name: 'storageWaterTreatmentMethod', fieldName: '저류수처리방법(자체처리,연계처리)', header: { text: '저류수처리방법(자체처리,연계처리)' } }, + + // 방류 수역 + { name: 'dischargeDestination', fieldName: '방류수역(또는 연계처리장명)', header: { text: '방류수역(또는 연계처리장명)' } }, + + // 최종처분량 + { name: 'sedimentDisposalAmount', fieldName: '침사', header: { text: '침사' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'debrisDisposalAmount', fieldName: '협잡물', header: { text: '협잡물' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 최종처분방법 + { name: 'sedimentDisposalMethod', fieldName: '침사', header: { text: '침사' } }, + { name: 'debrisDisposalMethod', fieldName: '협잡물', header: { text: '협잡물' } }, + + // 운영실적 + { name: 'operationDaysPerYear', fieldName: '가동일수(일/년)', header: { text: '가동일수(일/년)' }, valueType: ValueType.NUMBER }, // 3자리 수만 입력 가능 + { name: 'totalDischargePerYear', fieldName: '총배수량(m³/년)', header: { text: '총배수량(m³/년)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 비고 + { name: 'remarks', fieldName: '비고', header: { text: '비고' } }, + ], + + columnLayout: [ + { + name: 'adminArea', + hideChildHeaders: false, + header: { text: '행정구역' }, + items: ['sido', 'gugun'], + }, + 'swgeStrgeNm', + 'addr', + 'rnDt', + 'designFrequency', + + { + name: 'installationPurpose', + hideChildHeaders: false, + header: { text: '설치목적(O,X로 표기, 중복체크가능)' }, + items: [ + 'floodingPrevention', + 'waterQualityReduction', + 'sewerageReuseUse', + ], + }, + 'facilityStructure', + 'catchmentAreaSize', + 'facilityCapacity', + + { + name: 'pumpSystem', + hideChildHeaders: false, + header: { text: '펌프시설' }, + items: [ + 'totalPumpingCapacity', + { + name: 'pumpSystem1', + hideChildHeaders: false, + header: { text: '펌프시설1' }, + items: [ + 'pump1Diameter', + 'pump1PumpingCapacity', + 'pump1Count', + 'pump1StandbyCount', + ], + }, + + { + name: 'pumpSystem2', + hideChildHeaders: false, + header: { text: '펌프시설2' }, + items: [ + 'pump2Diameter', + 'pump2PumpingCapacity', + 'pump2Count', + 'pump2StandbyCount', + ], + }, + + { + name: 'pumpSystem3', + hideChildHeaders: false, + header: { text: '펌프시설3' }, + items: [ + 'pump3Diameter', + 'pump3PumpingCapacity', + 'pump3Count', + 'pump3StandbyCount', + ], + }, + ], + }, + + { + name: 'auxiliaryFacilities', + hideChildHeaders: false, + header: { text: '부대시설' }, + items: [ + 'preprocessingFacility', + 'odorRemovalEquipment', + 'selfGenerationEquipment', + ], + }, + 'drainageMethod', + 'storageMethod', + 'storageWaterTreatmentMethod', + 'dischargeDestination', + + { + name: 'finalDisposalAmount', + hideChildHeaders: false, + header: { text: '최종처분량(ton/년)' }, + items: [ + 'sedimentDisposalAmount', + 'debrisDisposalAmount', + ], + }, + + { + name: 'finalDisposalMethod', + hideChildHeaders: false, + header: { text: '최종처분방법' }, + items: [ + 'sedimentDisposalMethod', + 'debrisDisposalMethod', + ], + }, + + { + name: 'operationResults', + hideChildHeaders: false, + header: { text: '운영실적' }, + items: [ + 'operationDaysPerYear', + 'totalDischargePerYear', + ], + }, + 'remarks', + ], + + rowStartNum: 3, + fixedColumns: 2, + tabKey: 'sewageReservoirInp', + tabLabel: '하수저류시설', +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/sewerageFinance/CauserBurdenInpColumn.ts b/apps/st/src/components/util/column/input/sewerageFinance/CauserBurdenInpColumn.ts new file mode 100644 index 000000000..419f00d9d --- /dev/null +++ b/apps/st/src/components/util/column/input/sewerageFinance/CauserBurdenInpColumn.ts @@ -0,0 +1,77 @@ +import { footerInt, footerDecimal1 } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + + // 하수도 통계 관리 > 09.하수도 재정 > tab 4: 원인자 부담금 +export const CauserBurdenInpColumn = { + columns: [ + // 행정구역 + { name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } }, + + // 부과액 + { name: 'LEVY_CO', fieldName: '건수', header: { text: '건수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'LEVY_AMOUNT', fieldName: '금액(백만원)', header: { text: '금액(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 대상별 징수현황 > 계 + { name: 'TRGET_LEV_STTUS_CO', fieldName: '건수', header: { text: '건수' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'TRGET_LEV_STTUS_AMOUNT', fieldName: '금액(백만원)', header: { text: '금액(백만원)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 대상별 징수현황 > 배수설비설치자 + { name: 'DRNG_EQP_CO', fieldName: '건수', header: { text: '건수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'DRNG_EQP_AMOUNT', fieldName: '금액(백만원)', header: { text: '금액(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 대상별 징수현황 > 타공사·타행위자 + { name: 'CNTRWK_CO', fieldName: '건수', header: { text: '건수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'CNTRWK_AMOUNT', fieldName: '금액(백만원)', header: { text: '금액(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 비고 + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + + // hidden + { name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' } }, + { name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' } }, + + // 참조 필드 + { name: 'AMNTBRNCSR', fieldName: 'AMNTBRNCSR', header: { text: 'AMNTBRNCSR' } }, + ], + + columnLayout: [ + { + name: 'group_admin', + header: { text: '행정구역' }, + items: ['SIDO', 'GUGUN'] + }, + { + name: 'group_assess', + header: { text: '부과액' }, + items: ['LEVY_CO', 'LEVY_AMOUNT'] + }, + { + name: 'group_collection', + header: { text: '대상별 징수현황' }, + items: [ + { + name: 'coll_group_total', + header: { text: '계' }, + items: ['TRGET_LEV_STTUS_CO', 'TRGET_LEV_STTUS_AMOUNT'] + }, + { + name: 'coll_group_inst', + header: { text: '배수설비설치자(하수도법 제61조 제1항)' }, + items: ['DRNG_EQP_CO', 'DRNG_EQP_AMOUNT'] + }, + { + name: 'coll_group_otr', + header: { text: '타공사·타행위자(하수도법 제61조 제2항)' }, + items: ['CNTRWK_CO', 'CNTRWK_AMOUNT'] + } + ] + }, + 'RMK' + ], + + rowStartNum: 3, + fixedColumns: 1, + tabKey: 'causerBurdenInp', + tabLabel: '원인자 부담금' +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/sewerageFinance/SewerageChargeInpColumn.ts b/apps/st/src/components/util/column/input/sewerageFinance/SewerageChargeInpColumn.ts new file mode 100644 index 000000000..5940acc81 --- /dev/null +++ b/apps/st/src/components/util/column/input/sewerageFinance/SewerageChargeInpColumn.ts @@ -0,0 +1,49 @@ +import { footerInt, footerDecimal1 } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 > 09.하수도 재정 > tab 2: 하수도 요금 +export const SewerageChargeInpColumn = { + columns: [ + // 행정구역 + { name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } }, + + // 요금 + { name: 'FYER_LEVY_QY', fieldName: '연간조정량(m³)(A)', header: { text: '연간조정량(m³)(A)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'LEVY_AMOUNT', fieldName: '하수도사용료수익(원)(B)', header: { text: '하수도사용료수익(원)(B)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'AVRG_UNTPC', fieldName: 'm³당요금(원/m³) C=(B/A)', header: { text: 'm³당요금(원/m³) C=(B/A)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + { name: 'SMRIZE_PRMPC', fieldName: '총괄원가(원)(D)', header: { text: '총괄원가(원)(D)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'SMRIZE_UNIT_PRMPC', fieldName: 'm³당원가(원/m³) E=(D/A)', header: { text: 'm³당원가(원/m³) E=(D/A)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'RLTY_RT', fieldName: '요금현실화율(%) F=(C/E*100)', header: { text: '요금현실화율(%) F=(C/E*100)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 비고 + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + + // hidden + { name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 참조 필드 + { name: 'SEWER_RNTFEE', fieldName: 'SEWER_RNTFEE', header: { text: 'SEWER_RNTFEE' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'C_LEVY_AMOUNT', fieldName: 'C_LEVY_AMOUNT', header: { text: 'C_LEVY_AMOUNT' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + ], + columnLayout: [ + { + name: 'group_admin', + header: { text: '행정구역' }, + items: ['SIDO', 'GUGUN'] + }, + 'FYER_LEVY_QY', + 'LEVY_AMOUNT', + 'AVRG_UNTPC', + 'SMRIZE_PRMPC', + 'SMRIZE_UNIT_PRMPC', + 'RLTY_RT', + 'RMK' + ], + rowStartNum: 2, + fixedColumns: 1, + tabKey: 'sewerageChargeInp', + tabLabel: '하수도 요금' +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/sewerageFinance/SewerageFinanceSettleInpColumn.ts b/apps/st/src/components/util/column/input/sewerageFinance/SewerageFinanceSettleInpColumn.ts new file mode 100644 index 000000000..24b46e1e6 --- /dev/null +++ b/apps/st/src/components/util/column/input/sewerageFinance/SewerageFinanceSettleInpColumn.ts @@ -0,0 +1,150 @@ +import { footerDecimal1 } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 > 09.하수도 재정 > tab 1: 하수도 재정 결산 +export const SewerageFinanceSettleInpColumn = { + columns: [ + // 행정구역 + { name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } }, + + // 재정별 사업비 + { name: 'FNANC_WCT_SM', fieldName: '계', header: { text: '계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'NTNXPN', fieldName: '중앙정부지원액', header: { text: '중앙정부지원액' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 재정별 사업비 > 지방비 + { name: 'LCLEXPNS', fieldName: '특·광역시, 도', header: { text: '특·광역시, 도' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'LCLTY_CT', fieldName: '시,군,구 지자체', header: { text: '시,군,구 지자체' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 재정별 사업비 + { name: 'SEWER_RNTFEE', fieldName: '하수도사용료', header: { text: '하수도사용료' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'AMNTBRNCSR', fieldName: '원인자부담금', header: { text: '원인자부담금' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'ETC_INCME', fieldName: '기타잡수입', header: { text: '기타잡수입' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'CYFD_AMOUNT', fieldName: '이월액', header: { text: '이월액' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 부채액 + { name: 'DEBT_AMOUNT', fieldName: '부채액(백만원)', header: { text: '부채액(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 용도별사업비 > 계 + { name: 'PRPOS_WCT_SM', fieldName: '계', header: { text: '계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 용도별사업비 > 처리장 + { name: 'PRCPLT_SM', fieldName: '소계', header: { text: '소계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'PRCPLT_FCLTY', fieldName: '시설비', header: { text: '시설비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'PRCPLT_RPAIR', fieldName: '개보수비', header: { text: '개보수비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'PRCPLT_ORPNS', fieldName: '운영유지비', header: { text: '운영유지비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'PRCPLT_ETC', fieldName: '기타', header: { text: '기타' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 용도별사업비 > 하수관로 + { name: 'SEWAGE_SM', fieldName: '소계', header: { text: '소계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'SEWAGE_FCLTY', fieldName: '시설비', header: { text: '시설비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'SEWAGE_RPAIR', fieldName: '개보수비', header: { text: '개보수비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'SEWAGE_ORPNS', fieldName: '운영유지비', header: { text: '운영유지비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'SEWAGE_ETC', fieldName: '기타', header: { text: '기타' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 용도별사업비 > 하수저류시설 + { name: 'SWGE_STRGE_SM', fieldName: '소계', header: { text: '소계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'SWGE_STRGE_FCLTY', fieldName: '시설비', header: { text: '시설비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'SWGE_STRGE_RPAIR', fieldName: '개보수비', header: { text: '개보수비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'SWGE_STRGE_ORPNS', fieldName: '운영유지비', header: { text: '운영유지비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'SWGE_STRGE_ETC', fieldName: '기타', header: { text: '기타' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 용도별사업비 > 기타 + { name: 'ETC_SM', fieldName: '소계', header: { text: '소계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'ACDNT_CYFD_AMOUNT', fieldName: '사고이월액', header: { text: '사고이월액' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'UNRCIV_AMOUNT', fieldName: '미수납액', header: { text: '미수납액' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'ETC_AMOUNT', fieldName: '기타', header: { text: '기타' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 비고 + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + + // hidden + { name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 참조 필드 + { name: 'B_LEVY_AMOUNT', fieldName: 'B_LEVY_AMOUNT', header: { text: 'B_LEVY_AMOUNT' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'C_LEVY_AMOUNT', fieldName: 'C_LEVY_AMOUNT', header: { text: 'C_LEVY_AMOUNT' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'D_LEVY_AMOUNT', fieldName: 'D_LEVY_AMOUNT', header: { text: 'D_LEVY_AMOUNT' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + ], + columnLayout: [ + { + name: 'group_admin', + header: { text: '행정구역' }, + items: ['SIDO', 'GUGUN'] + }, + { + name: 'group_finance', + header: { text: '재정별 사업비(백만원)' }, + items: [ + 'FNANC_WCT_SM', + 'NTNXPN', + { + name: 'group_local_finance', + header: { text: '지방비' }, + items: ['LCLEXPNS', 'LCLTY_CT'] + }, + 'SEWER_RNTFEE', + 'AMNTBRNCSR', + 'ETC_INCME', + 'CYFD_AMOUNT' + ] + }, + 'DEBT_AMOUNT', + { + name: 'group_purpose', + header: { text: '용도별사업비(백만원)' }, + items: [ + 'PRPOS_WCT_SM', + { + name: 'group_treatment', + header: { text: '처리장' }, + items: [ + 'PRCPLT_SM', + 'PRCPLT_FCLTY', + 'PRCPLT_RPAIR', + 'PRCPLT_ORPNS', + 'PRCPLT_ETC' + ] + }, + { + name: 'group_pipe', + header: { text: '하수관로' }, + items: [ + 'SEWAGE_SM', + 'SEWAGE_FCLTY', + 'SEWAGE_RPAIR', + 'SEWAGE_ORPNS', + 'SEWAGE_ETC' + ] + }, + { + name: 'group_storage', + header: { text: '하수저류시설' }, + items: [ + 'SWGE_STRGE_SM', + 'SWGE_STRGE_FCLTY', + 'SWGE_STRGE_RPAIR', + 'SWGE_STRGE_ORPNS', + 'SWGE_STRGE_ETC' + ] + }, + { + name: 'group_etc_purpose', + header: { text: '기타' }, + items: [ + 'ETC_SM', + 'ACDNT_CYFD_AMOUNT', + 'UNRCIV_AMOUNT', + 'ETC_AMOUNT' + ] + } + ] + }, + 'RMK' + ], + rowStartNum: 3, + fixedColumns: 1, + tabKey: 'sewerageFinanceSettleInp', + tabLabel: '하수도재정 결산' +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/sewerageFinance/SewerageUsageFeeInpColumn.ts b/apps/st/src/components/util/column/input/sewerageFinance/SewerageUsageFeeInpColumn.ts new file mode 100644 index 000000000..bd4be4668 --- /dev/null +++ b/apps/st/src/components/util/column/input/sewerageFinance/SewerageUsageFeeInpColumn.ts @@ -0,0 +1,240 @@ +import { footerDecimal1 } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 > 09.하수도 재정 > tab 3: 하수도 사용료 +export const SewerageUsageFeeInpColumn = { + columns: [ + // 행정구역 + { name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } }, + + // 총계 > 부과액 + { name: 'LEVY_AMOUNT', fieldName: '(백만원)', header: { text: '(백만원)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 총계 > 부과량 + { name: 'LEVY_QY', fieldName: '(천톤)', header: { text: '(천톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 가정용 + { name: 'HOME_LEVY_AMOUNT', fieldName: '(백만원)', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'HOME_LEVY_QY', fieldName: '(천톤)', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'HOME_UNTPC', fieldName: '(원/톤)', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 업무용 + { name: 'JOB_LEVY_AMOUNT', fieldName: '(백만원)', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'JOB_LEVY_QY', fieldName: '(천톤)', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'JOB_UNTPC', fieldName: '(원/톤)', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 영업용(일반용) + { name: 'BSN_LEVY_AMOUNT', fieldName: '(백만원)', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'BSN_LEVY_QY', fieldName: '(천톤)', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'BSN_UNTPC', fieldName: '(원/톤)', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 욕탕1종(대중탕용) + { name: 'BATHS1_LEVY_AMOUNT', fieldName: '(백만원)', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'BATHS1_LEVY_QY', fieldName: '(천톤)', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'BATHS1_UNTPC', fieldName: '(원/톤)', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 욕탕2종 + { name: 'PBBATH_LEVY_AMOUNT', fieldName: '(백만원)', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'PBBATH_LEVY_QY', fieldName: '(천톤)', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'PBBATH_UNTPC', fieldName: '(원/톤)', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 산업 및 공업용 + { name: 'INDUST_LEVY_AMOUNT', fieldName: '(백만원)', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'INDUST_LEVY_QY', fieldName: '(천톤)', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'INDUST_UNTPC', fieldName: '(원/톤)', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 기타 + { name: 'ETC_LEVY_AMOUNT', fieldName: '(백만원)', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'ETC_LEVY_QY', fieldName: '(천톤)', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'ETC_UNTPC', fieldName: '(원/톤)', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 비고 + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + + // hidden + { name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' } }, + { name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' } }, + + // 참조 필드 + { name: 'SEWER_RNTFEE', fieldName: 'SEWER_RNTFEE', header: { text: 'SEWER_RNTFEE' } }, + { name: 'B_LEVY_AMOUNT', fieldName: 'B_LEVY_AMOUNT', header: { text: 'B_LEVY_AMOUNT' } }, + ], + + columnLayout: [ + { + name: 'group_admin', + header: { text: '행정구역' }, + items: ['SIDO', 'GUGUN'] + }, + { + name: 'group_total', + header: { text: '총계' }, + items: [ + { + name: 'total_amount', + header: { text: '부과액' }, + items: ['LEVY_AMOUNT'] + }, + { + name: 'total_qty', + header: { text: '부과량' }, + items: ['LEVY_QY'] + } + ] + }, + { + name: 'group_home', + header: { text: '가 정 용' }, + items: [ + { + name: 'home_amount', + header: { text: '부과액' }, + items: ['HOME_LEVY_AMOUNT'] + }, + { + name: 'home_qty', + header: { text: '부과량' }, + items: ['HOME_LEVY_QY'] + }, + { + name: 'home_price', + header: { text: '단가' }, + items: ['HOME_UNTPC'] + } + ] + }, + { + name: 'group_job', + header: { text: '업 무 용' }, + items: [ + { + name: 'job_amount', + header: { text: '부과액' }, + items: ['JOB_LEVY_AMOUNT'] + }, + { + name: 'job_qty', + header: { text: '부과량' }, + items: ['JOB_LEVY_QY'] + }, + { + name: 'job_price', + header: { text: '단가' }, + items: ['JOB_UNTPC'] + } + ] + }, + { + name: 'group_business', + header: { text: '영 업 용(일 반 용)' }, + items: [ + { + name: 'bsn_amount', + header: { text: '부과액' }, + items: ['BSN_LEVY_AMOUNT'] + }, + { + name: 'bsn_qty', + header: { text: '부과량' }, + items: ['BSN_LEVY_QY'] + }, + { + name: 'bsn_price', + header: { text: '단가' }, + items: ['BSN_UNTPC'] + } + ] + }, + { + name: 'group_baths1', + header: { text: '욕 탕 1종(대중탕용)' }, + items: [ + { + name: 'baths1_amount', + header: { text: '부과액' }, + items: ['BATHS1_LEVY_AMOUNT'] + }, + { + name: 'baths1_qty', + header: { text: '부과량' }, + items: ['BATHS1_LEVY_QY'] + }, + { + name: 'baths1_price', + header: { text: '단가' }, + items: ['BATHS1_UNTPC'] + } + ] + }, + { + name: 'group_pbbath', + header: { text: '욕 탕 2종' }, + items: [ + { + name: 'pbbath_amount', + header: { text: '부과액' }, + items: ['PBBATH_LEVY_AMOUNT'] + }, + { + name: 'pbbath_qty', + header: { text: '부과량' }, + items: ['PBBATH_LEVY_QY'] + }, + { + name: 'pbbath_price', + header: { text: '단가' }, + items: ['PBBATH_UNTPC'] + } + ] + }, + { + name: 'group_industry_all', + header: { text: '산업 및 공업용' }, + items: [ + { + name: 'indust_amount', + header: { text: '부과액' }, + items: ['INDUST_LEVY_AMOUNT'] + }, + { + name: 'indust_qty', + header: { text: '부과량' }, + items: ['INDUST_LEVY_QY'] + }, + { + name: 'indust_price', + header: { text: '단가' }, + items: ['INDUST_UNTPC'] + } + ] + }, + { + name: 'group_etc', + header: { text: '기타' }, + items: [ + { + name: 'etc_amount', + header: { text: '부과액' }, + items: ['ETC_LEVY_AMOUNT'] + }, + { + name: 'etc_qty', + header: { text: '부과량' }, + items: ['ETC_LEVY_QY'] + }, + { + name: 'etc_price', + header: { text: '단가' }, + items: ['ETC_UNTPC'] + } + ] + }, + 'RMK' + ], + + rowStartNum: 3, + fixedColumns: 1, + tabKey: 'sewerageUsageFeeInp', + tabLabel: '하수도 사용료' +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/sewerageServiceLevel/ComplaintInpColumn.ts b/apps/st/src/components/util/column/input/sewerageServiceLevel/ComplaintInpColumn.ts new file mode 100644 index 000000000..68624e658 --- /dev/null +++ b/apps/st/src/components/util/column/input/sewerageServiceLevel/ComplaintInpColumn.ts @@ -0,0 +1,80 @@ +import { footerInt, footerDecimal1 } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 > 13. 하수도 서비스 수준 > tab 1: 민원 +export const ComplaintInpColumn = { + columns: [ + // 행정구역 + { name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } }, + + // 행정코드 (hidden) + { name: 'ADST_CD', fieldName: '행정구역코드', header: { text: '행정구역코드' }, visible: false }, + { name: 'ADST_DONG', fieldName: '행정구역명', header: { text: '행정구역명' }, visible: false }, + + // 하수처리인구(명) + { name: 'SWGE_PROC_POPLTN', fieldName: '하수처리인구(명)', header: { text: '하수처리인구(명)' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 민원유형(건) + { name: 'CVPL_CM', fieldName: '총계', header: { text: '총계' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'CVPL_BSML', fieldName: '악취', header: { text: '악취' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'CVPL_DCWTR_QLTWTR', fieldName: '하수관로', header: { text: '하수관로' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'CVPL_FIVW', fieldName: '배수설비', header: { text: '배수설비' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'CVPL_POLLUTN_ACDNT', fieldName: '맨홀', header: { text: '맨홀' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'CVPL_SSOS', fieldName: '정화조', header: { text: '정화조' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'CVPL_CHRGE', fieldName: '하수도요금', header: { text: '하수도요금' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'CVPL_UNKD', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 기준인구당 민원건수(건/10,000명) + { name: 'STDR_POPLTN_PER_CVPL', fieldName: '기준인구당민원건수(건/10,000명)', header: { text: '기준인구당민원건수(건/10,000명)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 비고 + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + + // Hidden: 의견 필드 + { name: 'WRTER_OPINION', fieldName: '작성자의견', header: { text: '작성자의견' }, visible: false }, + { name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' }, visible: false }, + + // Hidden: Check fields : 데이터 변경 여부를 판단하는 필드로, UI에서 숨김 처리 + { name: 'CHK_SWGE_PROC_POPLTN', fieldName: 'CHK_SWGE_PROC_POPLTN', header: { text: 'CHK_SWGE_PROC_POPLTN' }, visible: false }, + { name: 'CHK_CVPL_CM', fieldName: 'CHK_CVPL_CM', header: { text: 'CHK_CVPL_CM' }, visible: false }, + { name: 'CHK_CVPL_BSML', fieldName: 'CHK_CVPL_BSML', header: { text: 'CHK_CVPL_BSML' }, visible: false }, + { name: 'CHK_CVPL_DCWTR_QLTWTR', fieldName: 'CHK_CVPL_DCWTR_QLTWTR', header: { text: 'CHK_CVPL_DCWTR_QLTWTR' }, visible: false }, + { name: 'CHK_CVPL_FIVW', fieldName: 'CHK_CVPL_FIVW', header: { text: 'CHK_CVPL_FIVW' }, visible: false }, + { name: 'CHK_CVPL_POLLUTN_ACDNT', fieldName: 'CHK_CVPL_POLLUTN_ACDNT', header: { text: 'CHK_CVPL_POLLUTN_ACDNT' }, visible: false }, + { name: 'CHK_CVPL_SSOS', fieldName: 'CHK_CVPL_SSOS', header: { text: 'CHK_CVPL_SSOS' }, visible: false }, + { name: 'CHK_CVPL_CHRGE', fieldName: 'CHK_CVPL_CHRGE', header: { text: 'CHK_CVPL_CHRGE' }, visible: false }, + { name: 'CHK_CVPL_UNKD', fieldName: 'CHK_CVPL_UNKD', header: { text: 'CHK_CVPL_UNKD' }, visible: false }, + { name: 'CHK_STDR_POPLTN_PER_CVPL', fieldName: 'CHK_STDR_POPLTN_PER_CVPL', header: { text: 'CHK_STDR_POPLTN_PER_CVPL' }, visible: false }, + ], + + columnLayout: [ + { + name: 'group_admin', + header: { text: '행정구역' }, + items: ['SIDO', 'GUGUN'] + }, + 'SWGE_PROC_POPLTN', + { + name: 'group_complaint_type', + header: { text: '민원유형(건)' }, + items: [ + 'CVPL_CM', + 'CVPL_BSML', + 'CVPL_DCWTR_QLTWTR', + 'CVPL_FIVW', + 'CVPL_POLLUTN_ACDNT', + 'CVPL_SSOS', + 'CVPL_CHRGE', + 'CVPL_UNKD' + ] + }, + 'STDR_POPLTN_PER_CVPL', + 'RMK' + ], + + rowStartNum: 2, + fixedColumns: 1, + tabKey: 'complaintInp', + tabLabel: '민원' +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/sewerageServiceLevel/ResidentFriendlyFactInpColumn.ts b/apps/st/src/components/util/column/input/sewerageServiceLevel/ResidentFriendlyFactInpColumn.ts new file mode 100644 index 000000000..3f59e6b07 --- /dev/null +++ b/apps/st/src/components/util/column/input/sewerageServiceLevel/ResidentFriendlyFactInpColumn.ts @@ -0,0 +1,89 @@ +import { footerInt, footerDecimal1 } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 > 13. 하수도 서비스 수준 > tab 2: 주민친화시설 +export const ResidentFriendlyFactInpColumn = { + columns: [ + // 행정구역 + { name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } }, + + // 시설 기본 정보 + { name: 'BSC_FCT_NM', fieldName: '처리장명', header: { text: '처리장명' }, editable: false }, + { name: 'FCT_CPC', fieldName: '시설용량(m³/일)', header: { text: '시설용량(m³/일)' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 처리장면적(㎡) + { name: 'PRCPLT_AR', fieldName: '처리장면적(㎡)', header: { text: '처리장면적(㎡)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 주민친화시설 면적(㎡) + { name: 'TOTAL_AR', fieldName: '총계', header: { text: '총계' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'RESTRM_AR', fieldName: '휴게시설', header: { text: '휴게시설' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'CHILDPLAY_AR', fieldName: '어린이놀이공간', header: { text: '어린이놀이공간' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'ALSFC_AR', fieldName: '체력단련시설', header: { text: '체력단련시설' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'PLGRND_AR', fieldName: '운동시설', header: { text: '운동시설' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'PRKPLCE_AR', fieldName: '주차장', header: { text: '주차장' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'CTLSTT_AR', fieldName: '교육 및 문화시설', header: { text: '교육 및 문화시설' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'NRPRK_AR', fieldName: '습지조성(생태공원)', header: { text: '습지조성(생태공원)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'ETC_AR', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 비고 + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + + // Hidden: 시설 정보 + { name: 'STPN_SEQ', fieldName: '일련번호', header: { text: '일련번호' }, visible: false }, + { name: 'ADST_CD', fieldName: '행정동코드', header: { text: '행정동코드' }, visible: false }, + { name: 'BSC_FCT_CD', fieldName: '기초시설코드', header: { text: '기초시설코드' }, visible: false }, + + // Hidden: 의견 필드 + { name: 'WRTER_OPINION', fieldName: '작성자의견', header: { text: '작성자의견' }, visible: false }, + { name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' }, visible: false }, + + // Hidden: 활용율 필드 + { name: 'PRCUSE_RT', fieldName: '주민친화시설활용율(%)', header: { text: '주민친화시설활용율(%)' }, visible: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // Hidden: Check fields : 데이터 변경 여부를 판단하는 필드로, UI에서 숨김 처리 + { name: 'CHK_PRCPLT_AR', fieldName: 'CHK_PRCPLT_AR', header: { text: 'CHK_PRCPLT_AR' }, visible: false }, + { name: 'CHK_TOTAL_AR', fieldName: 'CHK_TOTAL_AR', header: { text: 'CHK_TOTAL_AR' }, visible: false }, + { name: 'CHK_RESTRM_AR', fieldName: 'CHK_RESTRM_AR', header: { text: 'CHK_RESTRM_AR' }, visible: false }, + { name: 'CHK_CHILDPLAY_AR', fieldName: 'CHK_CHILDPLAY_AR', header: { text: 'CHK_CHILDPLAY_AR' }, visible: false }, + { name: 'CHK_ALSFC_AR', fieldName: 'CHK_ALSFC_AR', header: { text: 'CHK_ALSFC_AR' }, visible: false }, + { name: 'CHK_PLGRND_AR', fieldName: 'CHK_PLGRND_AR', header: { text: 'CHK_PLGRND_AR' }, visible: false }, + { name: 'CHK_PRKPLCE_AR', fieldName: 'CHK_PRKPLCE_AR', header: { text: 'CHK_PRKPLCE_AR' }, visible: false }, + { name: 'CHK_CTLSTT_AR', fieldName: 'CHK_CTLSTT_AR', header: { text: 'CHK_CTLSTT_AR' }, visible: false }, + { name: 'CHK_NRPRK_AR', fieldName: 'CHK_NRPRK_AR', header: { text: 'CHK_NRPRK_AR' }, visible: false }, + { name: 'CHK_ETC_AR', fieldName: 'CHK_ETC_AR', header: { text: 'CHK_ETC_AR' }, visible: false }, + { name: 'CHK_PRCUSE_RT', fieldName: 'CHK_PRCUSE_RT', header: { text: 'CHK_PRCUSE_RT' }, visible: false }, + ], + + columnLayout: [ + { + name: 'group_admin', + header: { text: '행정구역' }, + items: ['SIDO', 'GUGUN'] + }, + 'BSC_FCT_NM', + 'FCT_CPC', + 'PRCPLT_AR', + { + name: 'group_resident_friendly_facility', + header: { text: '주민친화시설 면적(㎡)' }, + items: [ + 'TOTAL_AR', + 'RESTRM_AR', + 'CHILDPLAY_AR', + 'ALSFC_AR', + 'PLGRND_AR', + 'PRKPLCE_AR', + 'CTLSTT_AR', + 'NRPRK_AR', + 'ETC_AR' + ] + }, + 'RMK' + ], + + rowStartNum: 2, + fixedColumns: 3, + tabKey: 'residentFriendlyFactInp', + tabLabel: '주민친화시설' +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/waterReuse/PublicSewageTreatWaterReuseInpColumn.ts b/apps/st/src/components/util/column/input/waterReuse/PublicSewageTreatWaterReuseInpColumn.ts new file mode 100644 index 000000000..0e6da19f4 --- /dev/null +++ b/apps/st/src/components/util/column/input/waterReuse/PublicSewageTreatWaterReuseInpColumn.ts @@ -0,0 +1,176 @@ +import { footerDecimal1 } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 > 10. 물 재이용 > tab 1: 공공하수처리수 재이용 +export const PublicSewageTreatWaterReuseInpColumn = { + columns: [ + // 행정구역 + { name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } }, + + // 하수처리장 + { name: 'BSC_FCT_NM', fieldName: '시설명', header: { text: '시설명' }, editable: false }, + { name: 'EQP_PRMS_DT', fieldName: '인가일(년/월/일)', header: { text: '인가일(년/월/일)' }, editable: false }, + { name: 'FCT_CPC', fieldName: '시설용량(㎥/일)', header: { text: '시설용량(㎥/일)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'TPUT_TOTAL', fieldName: '연간하수처리량(천톤/년)', header: { text: '연간하수처리량(천톤/년)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'RSWTR_LAW_YN', fieldName: '법적의무대상(대상/미대상)', header: { text: '법적의무대상(대상/미대상)' } }, + + // 재이용시설 > 설치유무 + { name: 'INST_YN', fieldName: '설치유무(유/무)', header: { text: '설치유무(유/무)' } }, + + // 재이용시설 > 재이용시설 설치시 + { name: 'RSWTR_RN_DT', fieldName: '가동개시일(yyyy-mm-dd)', header: { text: '가동개시일(yyyy-mm-dd)' } }, + { name: 'RSWTR_PRMS_DT', fieldName: '설치승인일(yyyy-mm-dd)', header: { text: '설치승인일(yyyy-mm-dd)' } }, + + // 재이용시설 > 재이용시설 설치시 > 재처리시설 + { name: 'RSWTR_CPC', fieldName: '용량(톤/일)', header: { text: '용량(톤/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'RSWTR_TRT_NM', fieldName: '공법/기자재', header: { text: '공법/기자재' } }, + + // 재이용시설 > 재이용시설 설치시 + { name: 'RSWTR_PIPE', fieldName: '공급관로길이(km)', header: { text: '공급관로길이(km)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'RSWTR_PUMP', fieldName: '공급펌프(톤/일)', header: { text: '공급펌프(톤/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 하수처리수 재이용현황 > 총계 + { name: 'USE_AMNT_TOTAL', fieldName: '총계', header: { text: '총계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 하수처리수 재이용현황 > 장내용수 + { name: 'USE_AMNT_D01', fieldName: '계', header: { text: '계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'USE_AMNT_S01', fieldName: '세척수', header: { text: '세척수' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'USE_AMNT_S02', fieldName: '냉각수', header: { text: '냉각수' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'USE_AMNT_S03', fieldName: '청소수', header: { text: '청소수' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'USE_AMNT_S04', fieldName: '조경용수', header: { text: '조경용수' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, // (레거시) 식수대 + { name: 'USE_AMNT_S05', fieldName: '희석용수', header: { text: '희석용수' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'USE_AMNT_S06', fieldName: '장내중수도', header: { text: '장내중수도' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'USE_AMNT_S07', fieldName: '장내기타용수', header: { text: '장내기타용수' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 하수처리수 재이용현황 > 장외용수 + { name: 'USE_AMNT_D02', fieldName: '계', header: { text: '계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'USE_AMNT_S11', fieldName: '청소·화장실용수', header: { text: '청소·화장실용수' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'USE_AMNT_S12', fieldName: '세척·살수용수', header: { text: '세척·살수용수' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'USE_AMNT_S13', fieldName: '조경용수', header: { text: '조경용수' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'USE_AMNT_S14', fieldName: '친수용수', header: { text: '친수용수' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'USE_AMNT_S15', fieldName: '하천등유지용수', header: { text: '하천등유지용수' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'USE_AMNT_S16', fieldName: '농업용수', header: { text: '농업용수' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'USE_AMNT_S17', fieldName: '지하수충전', header: { text: '지하수충전' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'USE_AMNT_S18', fieldName: '공업용수', header: { text: '공업용수' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 재이용률 + { name: 'REUSE_RT', fieldName: '재이용률(%)', header: { text: '재이용률(%)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 비고 + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + + // Hidden: Check fields : 데이터 변경 여부를 판단하는 필드로, UI에서 숨김 처리 + { name: 'CHK_RSWTR_LAW_YN', fieldName: 'CHK_RSWTR_LAW_YN', header: { text: 'CHK_RSWTR_LAW_YN' }, visible: false }, + { name: 'CHK_INST_YN', fieldName: 'CHK_INST_YN', header: { text: 'CHK_INST_YN' }, visible: false }, + { name: 'CHK_RSWTR_RN_DT', fieldName: 'CHK_RSWTR_RN_DT', header: { text: 'CHK_RSWTR_RN_DT' }, visible: false }, + { name: 'CHK_RSWTR_PRMS_DT', fieldName: 'CHK_RSWTR_PRMS_DT', header: { text: 'CHK_RSWTR_PRMS_DT' }, visible: false }, + { name: 'CHK_RSWTR_CPC', fieldName: 'CHK_RSWTR_CPC', header: { text: 'CHK_RSWTR_CPC' }, visible: false }, + { name: 'CHK_RSWTR_TRT_NM', fieldName: 'CHK_RSWTR_TRT_NM', header: { text: 'CHK_RSWTR_TRT_NM' }, visible: false }, + { name: 'CHK_RSWTR_PIPE', fieldName: 'CHK_RSWTR_PIPE', header: { text: 'CHK_RSWTR_PIPE' }, visible: false }, + { name: 'CHK_RSWTR_PUMP', fieldName: 'CHK_RSWTR_PUMP', header: { text: 'CHK_RSWTR_PUMP' }, visible: false }, + { name: 'CHK_TPUT_TOTAL', fieldName: 'CHK_TPUT_TOTAL', header: { text: 'CHK_TPUT_TOTAL' }, visible: false }, + { name: 'CHK_USE_AMNT_S01', fieldName: 'CHK_USE_AMNT_S01', header: { text: 'CHK_USE_AMNT_S01' }, visible: false }, + { name: 'CHK_USE_AMNT_S02', fieldName: 'CHK_USE_AMNT_S02', header: { text: 'CHK_USE_AMNT_S02' }, visible: false }, + { name: 'CHK_USE_AMNT_S03', fieldName: 'CHK_USE_AMNT_S03', header: { text: 'CHK_USE_AMNT_S03' }, visible: false }, + { name: 'CHK_USE_AMNT_S04', fieldName: 'CHK_USE_AMNT_S04', header: { text: 'CHK_USE_AMNT_S04' }, visible: false }, + { name: 'CHK_USE_AMNT_S05', fieldName: 'CHK_USE_AMNT_S05', header: { text: 'CHK_USE_AMNT_S05' }, visible: false }, + { name: 'CHK_USE_AMNT_S06', fieldName: 'CHK_USE_AMNT_S06', header: { text: 'CHK_USE_AMNT_S06' }, visible: false }, + { name: 'CHK_USE_AMNT_S07', fieldName: 'CHK_USE_AMNT_S07', header: { text: 'CHK_USE_AMNT_S07' }, visible: false }, + { name: 'CHK_USE_AMNT_S11', fieldName: 'CHK_USE_AMNT_S11', header: { text: 'CHK_USE_AMNT_S11' }, visible: false }, + { name: 'CHK_USE_AMNT_S12', fieldName: 'CHK_USE_AMNT_S12', header: { text: 'CHK_USE_AMNT_S12' }, visible: false }, + { name: 'CHK_USE_AMNT_S13', fieldName: 'CHK_USE_AMNT_S13', header: { text: 'CHK_USE_AMNT_S13' }, visible: false }, + { name: 'CHK_USE_AMNT_S14', fieldName: 'CHK_USE_AMNT_S14', header: { text: 'CHK_USE_AMNT_S14' }, visible: false }, + { name: 'CHK_USE_AMNT_S15', fieldName: 'CHK_USE_AMNT_S15', header: { text: 'CHK_USE_AMNT_S15' }, visible: false }, + { name: 'CHK_USE_AMNT_S16', fieldName: 'CHK_USE_AMNT_S16', header: { text: 'CHK_USE_AMNT_S16' }, visible: false }, + { name: 'CHK_USE_AMNT_S17', fieldName: 'CHK_USE_AMNT_S17', header: { text: 'CHK_USE_AMNT_S17' }, visible: false }, + { name: 'CHK_USE_AMNT_S18', fieldName: 'CHK_USE_AMNT_S18', header: { text: 'CHK_USE_AMNT_S18' }, visible: false }, + ], + + columnLayout: [ + { + name: 'group_admin', + header: { text: '행정구역' }, + items: ['SIDO', 'GUGUN'] + }, + { + name: 'group_facility', + header: { text: '하수처리장' }, + items: [ + 'BSC_FCT_NM', + 'EQP_PRMS_DT', + 'FCT_CPC', + 'RSWTR_LAW_YN' + ] + }, + { + name: 'group_reuse_facility', + header: { text: '재이용시설' }, + items: [ + 'INST_YN', + { + name: 'group_reuse_install', + header: { text: '재이용시설 설치시' }, + items: [ + 'RSWTR_RN_DT', + 'RSWTR_PRMS_DT', + { + name: 'group_treatment_facility', + header: { text: '처리시설' }, + items: [ + 'RSWTR_CPC', + 'RSWTR_TRT_NM' + ] + }, + 'RSWTR_PIPE', + 'RSWTR_PUMP' + ] + } + ] + }, + 'TPUT_TOTAL', + { + name: 'group_reuse_current', + header: { text: '하수처리수 재이용현황(천톤/년)' }, + items: [ + 'USE_AMNT_TOTAL', + { + name: 'group_indoor_use', + header: { text: '장 내 용 수' }, + items: [ + 'USE_AMNT_D01', + 'USE_AMNT_S01', + 'USE_AMNT_S02', + 'USE_AMNT_S03', + 'USE_AMNT_S04', + 'USE_AMNT_S05', + 'USE_AMNT_S06', + 'USE_AMNT_S07' + ] + }, + { + name: 'group_outdoor_use', + header: { text: '장 외 용 수' }, + items: [ + 'USE_AMNT_D02', + 'USE_AMNT_S11', + 'USE_AMNT_S12', + 'USE_AMNT_S13', + 'USE_AMNT_S14', + 'USE_AMNT_S15', + 'USE_AMNT_S16', + 'USE_AMNT_S17', + 'USE_AMNT_S18' + ] + } + ] + }, + 'REUSE_RT', + 'RMK' + ], + + rowStartNum: 4, + fixedColumns: 0, + tabKey: 'publicSewageTreatWaterReuseInp', + tabLabel: '공공하수처리수 재이용' +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/waterReuse/RainFallUtilFactStatusInpColumn.ts b/apps/st/src/components/util/column/input/waterReuse/RainFallUtilFactStatusInpColumn.ts new file mode 100644 index 000000000..10dc53ccd --- /dev/null +++ b/apps/st/src/components/util/column/input/waterReuse/RainFallUtilFactStatusInpColumn.ts @@ -0,0 +1,120 @@ +import { footerDecimal1 } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 > 10. 물 재이용 > tab 3: 빗물이용시설현황 +export const RainFallUtilFactStatusInpColumn = { + columns: [ + // 행정구역 + { name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, footer: { text: '합계' } }, + + // 행정코드 (hidden) + { name: 'ADST_CD', fieldName: '행정코드', header: { text: '행정코드' }, visible: false }, + { name: 'ADST_DSTR_CD', fieldName: '행정구역코드', header: { text: '행정구역코드' }, visible: false }, + { name: 'ST_RWR_REUSE_FCT_SEQ', fieldName: '일련번호', header: { text: '일련번호' }, visible: false }, + + // 시설명 + { name: 'RWR_REUSE_FCT_NM', fieldName: '시설명', header: { text: '시설명' } }, + + // 건축물용도 + { name: 'BILD_TP', fieldName: '건축물용도', header: { text: '건축물용도' } }, + + // 위치(주소) + { name: 'ADDR', fieldName: '위치(주소)', header: { text: '위치(주소)' } }, + + // 법적의무대상여부(대상/미대상) + { name: 'LAW_YN', fieldName: '법적의무대상여부(대상/미대상)', header: { text: '법적의무대상여부(대상/미대상)' } }, + + // 설치완료일 yyyy-mm-dd + { name: 'RN_DT', fieldName: '설치완료일 yyyy-mm-dd', header: { text: '설치완료일 yyyy-mm-dd' } }, + + // 설치비(백만원) + { name: 'INSTL_CT', fieldName: '설치비(백만원)', header: { text: '설치비(백만원)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 집수면 종류 + { name: 'CCM', fieldName: '집수면 종류', header: { text: '집수면 종류' } }, + + // 여과등처리시설유무(유:처리시설종류,무:미설치) + { name: 'TRTFC_ENNC', fieldName: '여과등처리시설유무(유:처리시설종류,무:미설치)', header: { text: '여과등처리시설유무(유:처리시설종류,무:미설치)' } }, + + // 저류조용량(m³) + { name: 'SGBS_CPCTY', fieldName: '저류조용량(m³)', header: { text: '저류조용량(m³)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 연간이용량(m³/년) + { name: 'FYER_USEQTY', fieldName: '연간이용량(m³/년)', header: { text: '연간이용량(m³/년)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 이용용도 + { name: 'RWR_PRCUSE_PRPPOS', fieldName: '이용용도', header: { text: '이용용도' } }, + + // 연간운영비(백만원/년) + { name: 'FYER_OPER_CT', fieldName: '연간운영비(백만원/년)', header: { text: '연간운영비(백만원/년)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 비고 + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + + // Hidden: 의견 필드 + { name: 'WRTER_OPINION', fieldName: '작성자의견', header: { text: '작성자의견' }, visible: false }, + { name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' }, visible: false }, + + // Hidden: 용도별 이용량 (화면에 표시 안 함) + { name: 'FYER_USE_01', fieldName: '빗물사용 1', header: { text: '빗물사용 1' }, visible: false }, + { name: 'FYER_USE_02', fieldName: '빗물사용 2', header: { text: '빗물사용 2' }, visible: false }, + { name: 'FYER_USE_03', fieldName: '빗물사용 3', header: { text: '빗물사용 3' }, visible: false }, + { name: 'FYER_USE_04', fieldName: '빗물사용 4', header: { text: '빗물사용 4' }, visible: false }, + { name: 'FYER_USE_05', fieldName: '빗물사용 5', header: { text: '빗물사용 5' }, visible: false }, + { name: 'FYER_USE_06', fieldName: '빗물사용 6', header: { text: '빗물사용 6' }, visible: false }, + { name: 'FYER_USE_07', fieldName: '빗물사용 7', header: { text: '빗물사용 7' }, visible: false }, + + // Hidden: 인가일 (화면에 표시 안 함) + { name: 'PERMIT_DT', fieldName: '인가일', header: { text: '인가일' }, visible: false }, + + // Hidden: Check fields : 데이터 변경 여부를 판단하는 필드로, UI에서 숨김 처리 + { name: 'CHK_RWR_REUSE_FCT_NM', fieldName: 'CHK_RWR_REUSE_FCT_NM', header: { text: 'CHK_RWR_REUSE_FCT_NM' }, visible: false }, + { name: 'CHK_ADDR', fieldName: 'CHK_ADDR', header: { text: 'CHK_ADDR' }, visible: false }, + { name: 'CHK_RN_DT', fieldName: 'CHK_RN_DT', header: { text: 'CHK_RN_DT' }, visible: false }, + { name: 'CHK_INSTL_CT', fieldName: 'CHK_INSTL_CT', header: { text: 'CHK_INSTL_CT' }, visible: false }, + { name: 'CHK_CCM', fieldName: 'CHK_CCM', header: { text: 'CHK_CCM' }, visible: false }, + { name: 'CHK_CCM_AR', fieldName: 'CHK_CCM_AR', header: { text: 'CHK_CCM_AR' }, visible: false }, + { name: 'CHK_TRTFC_ENNC', fieldName: 'CHK_TRTFC_ENNC', header: { text: 'CHK_TRTFC_ENNC' }, visible: false }, + { name: 'CHK_SGBS_CPCTY', fieldName: 'CHK_SGBS_CPCTY', header: { text: 'CHK_SGBS_CPCTY' }, visible: false }, + { name: 'CHK_FYER_USEQTY', fieldName: 'CHK_FYER_USEQTY', header: { text: 'CHK_FYER_USEQTY' }, visible: false }, + { name: 'CHK_RWR_PRCUSE_PRPPOS', fieldName: 'CHK_RWR_PRCUSE_PRPPOS', header: { text: 'CHK_RWR_PRCUSE_PRPPOS' }, visible: false }, + { name: 'CHK_FYER_OPER_CT', fieldName: 'CHK_FYER_OPER_CT', header: { text: 'CHK_FYER_OPER_CT' }, visible: false }, + { name: 'CHK_LAW_YN', fieldName: 'CHK_LAW_YN', header: { text: 'CHK_LAW_YN' }, visible: false }, + { name: 'CHK_BILD_TP', fieldName: 'CHK_BILD_TP', header: { text: 'CHK_BILD_TP' }, visible: false }, + { name: 'CHK_PERMIT_DT', fieldName: 'CHK_PERMIT_DT', header: { text: 'CHK_PERMIT_DT' }, visible: false }, + { name: 'CHK_FYER_USE_01', fieldName: 'CHK_FYER_USE_01', header: { text: 'CHK_FYER_USE_01' }, visible: false }, + { name: 'CHK_FYER_USE_02', fieldName: 'CHK_FYER_USE_02', header: { text: 'CHK_FYER_USE_02' }, visible: false }, + { name: 'CHK_FYER_USE_03', fieldName: 'CHK_FYER_USE_03', header: { text: 'CHK_FYER_USE_03' }, visible: false }, + { name: 'CHK_FYER_USE_04', fieldName: 'CHK_FYER_USE_04', header: { text: 'CHK_FYER_USE_04' }, visible: false }, + { name: 'CHK_FYER_USE_05', fieldName: 'CHK_FYER_USE_05', header: { text: 'CHK_FYER_USE_05' }, visible: false }, + { name: 'CHK_FYER_USE_06', fieldName: 'CHK_FYER_USE_06', header: { text: 'CHK_FYER_USE_06' }, visible: false }, + { name: 'CHK_FYER_USE_07', fieldName: 'CHK_FYER_USE_07', header: { text: 'CHK_FYER_USE_07' }, visible: false }, + ], + + columnLayout: [ + { + name: 'group_admin', + header: { text: '행정구역' }, + items: ['SIDO', 'GUGUN'] + }, + 'RWR_REUSE_FCT_NM', + 'BILD_TP', + 'ADDR', + 'LAW_YN', + 'RN_DT', + 'INSTL_CT', + 'CCM', + 'TRTFC_ENNC', + 'SGBS_CPCTY', + 'FYER_USEQTY', + 'RWR_PRCUSE_PRPPOS', + 'FYER_OPER_CT', + 'RMK' + ], + + rowStartNum: 2, + fixedColumns: 0, + tabKey: 'rainFallUtilFactStatusInp', + tabLabel: '빗물이용시설현황' +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/waterReuse/RecycledWaterStatusInpColumn.ts b/apps/st/src/components/util/column/input/waterReuse/RecycledWaterStatusInpColumn.ts new file mode 100644 index 000000000..5ec78024c --- /dev/null +++ b/apps/st/src/components/util/column/input/waterReuse/RecycledWaterStatusInpColumn.ts @@ -0,0 +1,109 @@ +import { footerDecimal1 } from "@/components/util/grid/footerSummary"; +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 > 10. 물 재이용 > tab 2: 중수도 현황 +export const RecycledWaterStatusInpColumn = { + columns: [ + // 행정구역 + { name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, footer: { text: '합계' } }, + + // 행정코드 (hidden) + { name: 'ADST_CD', fieldName: '행정코드', header: { text: '행정코드' }, visible: false }, + { name: 'ADST_DSTR_CD', fieldName: '행정구역코드', header: { text: '행정구역코드' }, visible: false }, + { name: 'RUSWTR_FCT_SEQ', fieldName: '일련번호', header: { text: '일련번호' }, visible: false }, + + // 건축물명/개발사업명 + { name: 'BILD_NM', fieldName: '건축물명/개발사업명', header: { text: '건축물명/개발사업명' } }, + + // 시설물용도/개발사업종류 + { name: 'BILD_TP', fieldName: '시설물용도/개발사업종류', header: { text: '시설물용도/개발사업종류' } }, + + // 주소 + { name: 'ADDR', fieldName: '주소', header: { text: '주소' } }, + + // 법적의무대상여부(대상/미대상) + { name: 'LAW_YN', fieldName: '법적의무대상여부(대상/미대상)', header: { text: '법적의무대상여부(대상/미대상)' } }, + + // 설치완료일(년-월-일) + { name: 'COMP_DT', fieldName: '설치완료일(년-월-일)', header: { text: '설치완료일(년-월-일)' } }, + + // 가동개시일(년-월-일) + { name: 'RN_DT', fieldName: '가동개시일(년-월-일)', header: { text: '가동개시일(년-월-일)' } }, + + // 시설현황 + { name: 'TOTAR', fieldName: '건축연면적(m²)', header: { text: '건축연면적(m²)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + { name: 'PROC_CPCTY', fieldName: '처리용량(m³/일)', header: { text: '처리용량(m³/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 물 사용량(m³/일) + { name: 'WATER_USE_CPCTY', fieldName: '물 사용량(m³/일)', header: { text: '물 사용량(m³/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 중수도이용량(m³/일) + { name: 'RUSWTR_USE_CPCTY', fieldName: '중수도이용량(m³/일)', header: { text: '중수도이용량(m³/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 대체수이용량(m³/일) + { name: 'SUB_USE_CPCTY', fieldName: '대체수이용량(m³/일)', header: { text: '대체수이용량(m³/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 중수도용도 + { name: 'RUSWTR_PRPOS', fieldName: '중수도용도', header: { text: '중수도용도' } }, + + // 연간운영비(백만원/년) + { name: 'FYER_OPER_CT', fieldName: '연간운영비(백만원/년)', header: { text: '연간운영비(백만원/년)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' }, + + // 비고 + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + + // Hidden: 사용자의견, 검토자의견 + { name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' }, visible: false }, + { name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' }, visible: false }, + + // Hidden: Check fields : 데이터 변경 여부를 판단하는 필드로, UI에서 숨김 처리 + { name: 'CHK_BILD_NM', fieldName: 'CHK_BILD_NM', header: { text: 'CHK_BILD_NM' }, visible: false }, + { name: 'CHK_ADDR', fieldName: 'CHK_ADDR', header: { text: 'CHK_ADDR' }, visible: false }, + { name: 'CHK_TOTAR', fieldName: 'CHK_TOTAR', header: { text: 'CHK_TOTAR' }, visible: false }, + { name: 'CHK_PROC_CPCTY', fieldName: 'CHK_PROC_CPCTY', header: { text: 'CHK_PROC_CPCTY' }, visible: false }, + { name: 'CHK_RUSWTR_USE_CPCTY', fieldName: 'CHK_RUSWTR_USE_CPCTY', header: { text: 'CHK_RUSWTR_USE_CPCTY' }, visible: false }, + { name: 'CHK_COMP_DT', fieldName: 'CHK_COMP_DT', header: { text: 'CHK_COMP_DT' }, visible: false }, + { name: 'CHK_RN_DT', fieldName: 'CHK_RN_DT', header: { text: 'CHK_RN_DT' }, visible: false }, + { name: 'CHK_RUSWTR_PRPOS', fieldName: 'CHK_RUSWTR_PRPOS', header: { text: 'CHK_RUSWTR_PRPOS' }, visible: false }, + { name: 'CHK_LAW_YN', fieldName: 'CHK_LAW_YN', header: { text: 'CHK_LAW_YN' }, visible: false }, + { name: 'CHK_BILD_TP', fieldName: 'CHK_BILD_TP', header: { text: 'CHK_BILD_TP' }, visible: false }, + { name: 'CHK_PERMIT_DT', fieldName: 'CHK_PERMIT_DT', header: { text: 'CHK_PERMIT_DT' }, visible: false }, + { name: 'CHK_FYER_OPER_CT', fieldName: 'CHK_FYER_OPER_CT', header: { text: 'CHK_FYER_OPER_CT' }, visible: false }, + { name: 'CHK_WATER_USE_CPCTY', fieldName: 'CHK_WATER_USE_CPCTY', header: { text: 'CHK_WATER_USE_CPCTY' }, visible: false }, + { name: 'CHK_SUB_USE_CPCTY', fieldName: 'CHK_SUB_USE_CPCTY', header: { text: 'CHK_SUB_USE_CPCTY' }, visible: false }, + ], + + columnLayout: [ + { + name: 'group_admin', + header: { text: '행정구역' }, + items: ['SIDO', 'GUGUN'] + }, + 'BILD_NM', + 'BILD_TP', + 'ADDR', + 'LAW_YN', + 'COMP_DT', + 'RN_DT', + { + name: 'group_facility_status', + header: { text: '시설현황' }, + items: [ + 'TOTAR', + 'PROC_CPCTY' + ] + }, + 'WATER_USE_CPCTY', + 'RUSWTR_USE_CPCTY', + 'SUB_USE_CPCTY', + 'RUSWTR_PRPOS', + 'FYER_OPER_CT', + 'RMK' + ], + + rowStartNum: 2, + fixedColumns: 0, + tabKey: 'recycledWaterStatusInp', + tabLabel: '중수도 현황' +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/waterReuse/TreatWaterReuseBusinessApprStatusInpColumn.ts b/apps/st/src/components/util/column/input/waterReuse/TreatWaterReuseBusinessApprStatusInpColumn.ts new file mode 100644 index 000000000..7144c1edc --- /dev/null +++ b/apps/st/src/components/util/column/input/waterReuse/TreatWaterReuseBusinessApprStatusInpColumn.ts @@ -0,0 +1,57 @@ +// 하수도 통계 관리 > 10. 물 재이용 > tab 5: 하·폐수처리수 재이용사업 인가현황 +export const TreatWaterReuseBusinessApprStatusInpColumn = { + columns: [ + // 행정구역 + { name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' } }, + + // 행정코드 (hidden) + { name: 'ADST_CD', fieldName: '행정코드', header: { text: '행정코드' }, visible: false }, + { name: 'ADST_DSTR_CD', fieldName: '행정구역코드', header: { text: '행정구역코드' }, visible: false }, + { name: 'SIDO_CD', fieldName: '시도코드', header: { text: '시도코드' }, visible: false }, + { name: 'REUSE_CMPN_SEQ', fieldName: '일련번호', header: { text: '일련번호' }, visible: false }, + + // 상호(명칭) + { name: 'CMPNM', fieldName: '상호(명칭)', header: { text: '상호(명칭)' } }, + + // 대표자 + { name: 'RPRSNTV', fieldName: '대표자', header: { text: '대표자' } }, + + // 주소 + { name: 'ADDR', fieldName: '주소', header: { text: '주소' } }, + + // 인가일 + { name: 'CMPN_RGSDE', fieldName: '인가일 yyyy-mm-dd', header: { text: '인가일 yyyy-mm-dd' } }, + + // 비고 + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + + // Hidden: 의견 필드 + { name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' }, visible: false }, + { name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' }, visible: false }, + + // Hidden: Check fields : 데이터 변경 여부를 판단하는 필드로, UI에서 숨김 처리 + { name: 'CHK_CMPNM', fieldName: 'CHK_CMPNM', header: { text: 'CHK_CMPNM' }, visible: false }, + { name: 'CHK_RPRSNTV', fieldName: 'CHK_RPRSNTV', header: { text: 'CHK_RPRSNTV' }, visible: false }, + { name: 'CHK_ADDR', fieldName: 'CHK_ADDR', header: { text: 'CHK_ADDR' }, visible: false }, + { name: 'CHK_CMPN_RGSDE', fieldName: 'CHK_CMPN_RGSDE', header: { text: 'CHK_CMPN_RGSDE' }, visible: false }, + ], + + columnLayout: [ + { + name: 'group_admin', + header: { text: '행정구역' }, + items: ['SIDO', 'GUGUN'] + }, + 'CMPNM', + 'RPRSNTV', + 'ADDR', + 'CMPN_RGSDE', + 'RMK' + ], + + rowStartNum: 2, + fixedColumns: 0, + tabKey: 'treatWaterReuseBusinessApprStatusInp', + tabLabel: '하·폐수처리수 재이용사업 인가현황' +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/input/waterReuse/TreatWaterReuseDesignConstStatusInpColumn.ts b/apps/st/src/components/util/column/input/waterReuse/TreatWaterReuseDesignConstStatusInpColumn.ts new file mode 100644 index 000000000..15a3b12b2 --- /dev/null +++ b/apps/st/src/components/util/column/input/waterReuse/TreatWaterReuseDesignConstStatusInpColumn.ts @@ -0,0 +1,57 @@ +// 하수도 통계 관리 > 10. 물 재이용 > tab 4: 하·폐수처리수 재이용시설 설계시공업 등록현황 +export const TreatWaterReuseDesignConstStatusInpColumn = { + columns: [ + // 행정구역 + { name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false }, // editable 임의로 + + // 행정코드 (hidden) + { name: 'ADST_CD', fieldName: '행정코드', header: { text: '행정코드' }, visible: false }, + { name: 'ADST_DSTR_CD', fieldName: '행정구역코드', header: { text: '행정구역코드' }, visible: false }, + { name: 'SIDO_CD', fieldName: '시도코드', header: { text: '시도코드' }, visible: false }, + { name: 'PLCST_CMPNY_SEQ', fieldName: '일련번호', header: { text: '일련번호' }, visible: false }, + + // 상호(명칭) + { name: 'CMPNM', fieldName: '상호(명칭)', header: { text: '상호(명칭)' } }, + + // 대표자 + { name: 'RPRSNTV', fieldName: '대표자', header: { text: '대표자' } }, + + // 주소 + { name: 'ADDR', fieldName: '주소', header: { text: '주소' } }, + + // 등록일 yyyy-mm-dd + { name: 'CMPN_RGSDE', fieldName: '등록일 yyyy-mm-dd', header: { text: '등록일 yyyy-mm-dd' } }, + + // 비고 + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + + // Hidden: 의견 필드 + { name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' }, visible: false }, + { name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' }, visible: false }, + + // Hidden: Check fields : 데이터 변경 여부를 판단하는 필드로, UI에서 숨김 처리 + { name: 'CHK_CMPNM', fieldName: 'CHK_CMPNM', header: { text: 'CHK_CMPNM' }, visible: false }, + { name: 'CHK_RPRSNTV', fieldName: 'CHK_RPRSNTV', header: { text: 'CHK_RPRSNTV' }, visible: false }, + { name: 'CHK_ADDR', fieldName: 'CHK_ADDR', header: { text: 'CHK_ADDR' }, visible: false }, + { name: 'CHK_CMPN_RGSDE', fieldName: 'CHK_CMPN_RGSDE', header: { text: 'CHK_CMPN_RGSDE' }, visible: false }, + ], + + columnLayout: [ + { + name: 'group_admin', + header: { text: '행정구역' }, + items: ['SIDO', 'GUGUN'] + }, + 'CMPNM', + 'RPRSNTV', + 'ADDR', + 'CMPN_RGSDE', + 'RMK' + ], + + rowStartNum: 2, + fixedColumns: 0, + tabKey: 'treatWaterReuseDesignConstStatusInp', + tabLabel: '하·폐수처리수 재이용시설 설계시공업 등록현황' +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/review/ExcretaTreatmentFacilityRvwColumn.ts b/apps/st/src/components/util/column/review/ExcretaTreatmentFacilityRvwColumn.ts new file mode 100644 index 000000000..3cf7f9343 --- /dev/null +++ b/apps/st/src/components/util/column/review/ExcretaTreatmentFacilityRvwColumn.ts @@ -0,0 +1,118 @@ +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 07.분뇨처리 시설 +const rawServiceLevelList = [ + { + tabKey: 'a', + tabLabel: '민원', + rowStartNum: 2, + fixedColumns: 2, + columns: [ + /* 1. 기본 정보 */ + { name: 'SIDO', fieldName: '시도', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' } }, + { name: 'YR_GBN', fieldName: '구분', header: { text: '구분' } }, + + /* 2. 하수처리 인구 */ + { name: 'POPLTN_SM', fieldName: '하수처리인구(명)', header: { text: '하수처리인구\n(명)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + + /* 3. 민원유형 (단위: 건) */ + { name: 'CVPL_CM', fieldName: '총계(민원)', header: { text: '총계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'CVPL_DCWTR_QLTWTR', fieldName: '악취', header: { text: '악취' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'CVPL_BSML', fieldName: '하수관로', header: { text: '하수관로' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'CVPL_FIVW', fieldName: '배수설비', header: { text: '배수설비' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'CVPL_POLLUTN_ACDNT', fieldName: '맨홀', header: { text: '맨홀' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'CVPL_SSOS', fieldName: '정화조', header: { text: '정화조' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'CVPL_CHRGE', fieldName: '하수도요금', header: { text: '하수도요금' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'CVPL_ETC', fieldName: '기타(민원)', header: { text: '기타' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + + /* 4. 성과 지표 및 비고 */ + { name: 'STDR_POPLTN_PER_CVPL', fieldName: '기준인구당 민원건수(건/10,000명)', header: { text: '기준인구당\n민원건수\n(건/10,000명)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RMK', fieldName: '비고', header: { text: '비고' } } + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'YR_GBN', + 'POPLTN_SM', + { + name: 'complaintTypeGroup', + header: { text: '민원유형(건)' }, + items: [ + 'CVPL_CM', + 'CVPL_DCWTR_QLTWTR', + 'CVPL_BSML', + 'CVPL_FIVW', + 'CVPL_POLLUTN_ACDNT', + 'CVPL_SSOS', + 'CVPL_CHRGE', + 'CVPL_ETC' + ] + }, + 'STDR_POPLTN_PER_CVPL', + 'RMK' + ] + }, + { + tabKey: 'b', + tabLabel: '주민친화시설', + rowStartNum: 2, + fixedColumns: 5, + columns: [ + /* 1. 기본 및 시설 정보 */ + { name: 'SIDO', fieldName: '시도', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' } }, + { name: 'BSC_FCT_NM', fieldName: '처리장명', header: { text: '처리장명' } }, + { name: 'FCT_CPC', fieldName: '시설용량(㎥/일)', header: { text: '시설용량\n(㎥/일)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'YR_GBN', fieldName: '구분', header: { text: '구분' } }, + + /* 2. 처리장 전체 면적 */ + { name: 'PRCPLT_AR', fieldName: '처리장 면적(㎡)', header: { text: '처리장\n면적(㎡)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + + /* 3. 주민친화시설 면적 상세 (단위: ㎡) */ + { name: 'TOTAL_AR', fieldName: '총계(시설)', header: { text: '총계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'RESTRM_AR', fieldName: '휴게시설', header: { text: '휴게시설' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'CHILDPLAY_AR', fieldName: '어린이놀이공간', header: { text: '어린이놀이공간' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'ALSFC_AR', fieldName: '체력단련시설', header: { text: '체력단련시설' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'PLGRND_AR', fieldName: '운동시설', header: { text: '운동시설' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'PRKPLCE_AR', fieldName: '주차장', header: { text: '주차장' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'CTLSTT_AR', fieldName: '교육 및 문화시설', header: { text: '교육 및\n문화시설' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'NRPRK_AR', fieldName: '습지조성(생태공원)', header: { text: '습지조성\n(생태공원)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'ETC_AR', fieldName: '기타(시설)', header: { text: '기타' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + + /* 4. 비고 */ + { name: 'RMK', fieldName: '비고', header: { text: '비고' } } + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'BSC_FCT_NM', + 'FCT_CPC', + 'YR_GBN', + 'PRCPLT_AR', + { + name: 'friendlyFacilityGroup', + header: { text: '주민친화시설 면적(㎡)' }, + items: [ + 'TOTAL_AR', + 'RESTRM_AR', + 'CHILDPLAY_AR', + 'ALSFC_AR', + 'PLGRND_AR', + 'PRKPLCE_AR', + 'CTLSTT_AR', + 'NRPRK_AR', + 'ETC_AR' + ] + }, + 'RMK' + ] + } +]; + + +export const ExcretaTreatmentFacilityInfoList = rawServiceLevelList.map(tab => ({ + ...tab, + columns: tab.columns.map(col => ({ + ...col, + editable: false + })) +})); \ No newline at end of file diff --git a/apps/st/src/components/util/column/review/PrivateSewageFacilityColumn.ts b/apps/st/src/components/util/column/review/PrivateSewageFacilityColumn.ts new file mode 100644 index 000000000..c626ba504 --- /dev/null +++ b/apps/st/src/components/util/column/review/PrivateSewageFacilityColumn.ts @@ -0,0 +1,139 @@ +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 08.개인하수처리시설 +const rawPrivateSewageList = [ + { + tabKey: 'a', + tabLabel: '처리용량별 오수처리시설 현황', + rowStartNum: 3, + fixedColumns: 2, + columns: [ + { name: 'SIDO', fieldName: '시도', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' } }, + { name: 'YR_GBN', fieldName: '구분', header: { text: '구분' } }, + { name: 'STTY_SM', fieldName: '총계', header: { text: '총계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'STTY_ZONE_INNER_SM', fieldName: '계(처리구역 내)', header: { text: '계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'STTY_ZONE_INNER_2', fieldName: '2이하(처리구역 내)', header: { text: '2이하' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'STTY_ZONE_INNER_3', fieldName: '3~10(처리구역 내)', header: { text: '3~10' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'STTY_ZONE_INNER_11', fieldName: '11~20(처리구역 내)', header: { text: '11~20' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'STTY_ZONE_INNER_21', fieldName: '21~50(처리구역 내)', header: { text: '21~50' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'STTY_ZONE_INNER_51', fieldName: '51~100(처리구역 내)', header: { text: '51~100' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'STTY_ZONE_INNER_101', fieldName: '101~500(처리구역 내)', header: { text: '101~500' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'STTY_ZONE_INNER_501', fieldName: '501이상(처리구역 내)', header: { text: '501이상' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'STTY_ZONE_OUTER_SM', fieldName: '계(처리구역 외)', header: { text: '계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'STTY_ZONE_OUTER_2', fieldName: '2이하(처리구역 외)', header: { text: '2이하' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'STTY_ZONE_OUTER_3', fieldName: '3~10(처리구역 외)', header: { text: '3~10' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'STTY_ZONE_OUTER_11', fieldName: '11~20(처리구역 외)', header: { text: '11~20' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'STTY_ZONE_OUTER_21', fieldName: '21~50(처리구역 외)', header: { text: '21~50' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'STTY_ZONE_OUTER_51', fieldName: '51~100(처리구역 외)', header: { text: '51~100' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'STTY_ZONE_OUTER_101', fieldName: '101~500(처리구역 외)', header: { text: '101~500' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'STTY_ZONE_OUTER_501', fieldName: '501이상(처리구역 외)', header: { text: '501이상' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'YR_GBN', 'STTY_SM', + { + name: 'individualGroup', header: { text: '오수처리시설' }, + items: [ + { + name: 'innerGroup', header: { text: '하수처리구역 내(㎥/일)' }, + items: ['STTY_ZONE_INNER_SM', 'STTY_ZONE_INNER_2', 'STTY_ZONE_INNER_3', 'STTY_ZONE_INNER_11', 'STTY_ZONE_INNER_21', 'STTY_ZONE_INNER_51', 'STTY_ZONE_INNER_101', 'STTY_ZONE_INNER_501'] + }, + { + name: 'outerGroup', header: { text: '하수처리구역 외(㎥/일)' }, + items: ['STTY_ZONE_OUTER_SM', 'STTY_ZONE_OUTER_2', 'STTY_ZONE_OUTER_3', 'STTY_ZONE_OUTER_11', 'STTY_ZONE_OUTER_21', 'STTY_ZONE_OUTER_51', 'STTY_ZONE_OUTER_101', 'STTY_ZONE_OUTER_501'] + } + ] + }, + 'RMK' + ] + }, + { + tabKey: 'b', + tabLabel: '정화조현황', + rowStartNum: 4, + fixedColumns: 1, + columns: [ + { name: 'SIDO', fieldName: '시도', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' } }, + { name: 'YR_GBN', fieldName: '구분', header: { text: '구분' } }, + { name: 'TOT_FCLTY_CNT', fieldName: '총 시설 수(A+D)', header: { text: '총 시설 수\n(A+D)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'SPZI_SM', fieldName: '총계 A=(B+C)/2', header: { text: '총계\nA=(B+C)/2' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'SPZI_NPFL', fieldName: '자연유하', header: { text: '자연유하' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'SPZI_ENFCT_PUMP', fieldName: '강제펌핑식', header: { text: '강제펌핑식' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'SPZI_TFM', fieldName: '살수여상', header: { text: '살수여상' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'SPZI_ARTM', fieldName: '폭기방법', header: { text: '폭기방법' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'SPZI_CTARM', fieldName: '접촉폭기', header: { text: '접촉폭기' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'SPZI_ETC', fieldName: '기타(방식)', header: { text: '기타' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'SPZI_PROC_CPC_SM', fieldName: '계(C)', header: { text: '계(C)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'SPZI_PROC_CPC_1', fieldName: '10인조 이하', header: { text: '10인조 이하' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'SPZI_PROC_CPC_2', fieldName: '11~20', header: { text: '11~20' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'SPZI_PROC_CPC_3', fieldName: '21~30', header: { text: '21~30' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'SPZI_PROC_CPC_4', fieldName: '31~50', header: { text: '31~50' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'SPZI_PROC_CPC_5', fieldName: '51~199', header: { text: '51~199' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'SPZI_PROC_CPC_6', fieldName: '200~499', header: { text: '200~499' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'SPZI_PROC_CPC_7', fieldName: '500~999', header: { text: '500~999' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'SPZI_PROC_CPC_8', fieldName: '1,000이상', header: { text: '1,000이상' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'RMK', fieldName: '비고', header: { text: '비고' } } + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'YR_GBN', 'TOT_FCLTY_CNT', + { + name: 'innerMainGroup', header: { text: '하수처리구역 내' }, + items: [ + 'SPZI_SM', + { + name: 'innerMethodGroup', header: { text: '처리방식별' }, + items: [{ name: 'innerTankGroup', header: { text: '부패탱크' }, items: ['SPZI_NPFL', 'SPZI_ENFCT_PUMP'] }, 'SPZI_TFM', 'SPZI_ARTM', 'SPZI_CTARM', 'SPZI_ETC'] + }, + { + name: 'innerCpcGroup', header: { text: '처리용량별' }, + items: ['SPZI_PROC_CPC_SM', 'SPZI_PROC_CPC_1', 'SPZI_PROC_CPC_2', 'SPZI_PROC_CPC_3', 'SPZI_PROC_CPC_4', 'SPZI_PROC_CPC_5', 'SPZI_PROC_CPC_6', 'SPZI_PROC_CPC_7', 'SPZI_PROC_CPC_8'] + } + ] + }, + 'RMK' + ] + }, + { + tabKey: 'c', + tabLabel: '개인하수도 관리지역 지정공고 현황', + rowStartNum: 3, + fixedColumns: 1, + columns: [ + { name: 'SIDO', fieldName: '시도', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' } }, + { name: 'YR_GBN', fieldName: '구분', header: { text: '구분' } }, + { name: 'MGMT_AR', fieldName: '공고지역(개소)', header: { text: '공고지역\n(개소)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'APPN_SCOPE', fieldName: '지정범위(㎢)', header: { text: '지정범위\n(㎢)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'WRRTN_CNT', fieldName: '개소수(정화조)', header: { text: '개소수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'WRRTN_CPC', fieldName: '처리용량(인/일)', header: { text: '처리용량\n(인/일)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'STTY_CNT', fieldName: '개소수(오수시설)', header: { text: '개소수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'STTY_CPC', fieldName: '처리용량(㎥/일)', header: { text: '처리용량\n(㎥/일)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'YR_GBN', + { + name: 'mgmtMainGroup', header: { text: '개인하수도관리지역공고현황\n(하수도법 제34조의2)' }, + items: [ + 'MGMT_AR', 'APPN_SCOPE', + { name: 'septictankGroup', header: { text: '정화조' }, items: ['WRRTN_CNT', 'WRRTN_CPC'] }, + { name: 'sewageFacGroup', header: { text: '오수시설' }, items: ['STTY_CNT', 'STTY_CPC'] } + ] + }, + 'RMK' + ] + } +]; + +export const PrivateSewageFacilityInfoListRvw = rawPrivateSewageList.map(tab => ({ + ...tab, + columns: tab.columns.map(col => ({ + ...col, + editable: false + })) +})); \ No newline at end of file diff --git a/apps/st/src/components/util/column/review/PublicSewageFacilityRvwColumn.ts b/apps/st/src/components/util/column/review/PublicSewageFacilityRvwColumn.ts new file mode 100644 index 000000000..8b3db0d16 --- /dev/null +++ b/apps/st/src/components/util/column/review/PublicSewageFacilityRvwColumn.ts @@ -0,0 +1,149 @@ +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 06.공공하수처리시설 +export const rawPublicSewageFacilityInfoList = { + columns: [ + /* 1. 기본 정보 */ + { name: 'sido', fieldName: '시도', header: { text: '시도' } }, + { name: 'sgg', fieldName: '시군구', header: { text: '시군구' } }, + { name: 'GBN', fieldName: '구분', header: { text: '구분' } }, + { name: 'ADDR', fieldName: '소재지', header: { text: '소재지' } }, + { name: 'FCT_NM_GROUP', fieldName: '시설명(500m3/일이상/미만)', header: { text: '시설명(500m3/일이상/미만)' } }, + + /* 2. 시설용량 (㎥/일) */ + { name: 'FCT_CPC_GROUP', fieldName: '시설용량(500m3/일이상/미만)_용량', header: { text: '시설용량(500m3/일이상/미만)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'FCT_CPC_PHY', fieldName: '물리적_용량', header: { text: '물리적' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'FCT_CPC_BIO', fieldName: '생물학적_용량', header: { text: '생물학적' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'FCT_CPC_ADV', fieldName: '고도_용량', header: { text: '고도' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + + /* 3. 유입하수량 (㎥/일) */ + { name: 'IN_QTY_GROUP', fieldName: '유입하수량(500m3/일이상/미만)', header: { text: '유입하수량(500m3/일이상/미만)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'IN_QTY_PHY', fieldName: '물리적_유입', header: { text: '물리적' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'IN_QTY_BIO', fieldName: '생물학적_유입', header: { text: '생물학적' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'IN_QTY_ADV', fieldName: '고도_유입', header: { text: '고도' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + + /* 4. 방류량 (㎥/일) */ + { name: 'OUT_QTY_GROUP', fieldName: '방류량(500m3/일이상/미만)', header: { text: '방류량(500m3/일이상/미만)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'OUT_QTY_PHY', fieldName: '물리적_방류', header: { text: '물리적' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'OUT_QTY_BIO', fieldName: '생물학적_방류', header: { text: '생물학적' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'OUT_QTY_ADV', fieldName: '고도_방류', header: { text: '고도' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + + /* 5. 수질 및 처리효율 */ + { name: 'IN_BOD', fieldName: '유입BOD', header: { text: '유입BOD' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'OUT_BOD', fieldName: '방류BOD', header: { text: '방류BOD' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'IN_TOC', fieldName: '유입TOC', header: { text: '유입TOC' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'OUT_TOC', fieldName: '방류TOC', header: { text: '방류TOC' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'IN_SS', fieldName: '유입SS', header: { text: '유입SS' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'OUT_SS', fieldName: '방류SS', header: { text: '방류SS' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'IN_TN', fieldName: '유입T-N', header: { text: '유입T-N' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.000' }, + { name: 'OUT_TN', fieldName: '방류T-N', header: { text: '방류T-N' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.000' }, + { name: 'IN_TP', fieldName: '유입T-P', header: { text: '유입T-P' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.000' }, + { name: 'OUT_TP', fieldName: '방류T-P', header: { text: '방류T-P' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.000' }, + { name: 'IN_COLI', fieldName: '유입총대장균군수', header: { text: '유입총대장균군수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'OUT_COLI', fieldName: '방대총대장균군수', header: { text: '방대총대장균군수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'TRT_EFF', fieldName: '처리효율(%)', header: { text: '처리효율(%)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'TRT_LOD', fieldName: '처리부하량(kgBOD/D)', header: { text: '처리부하량(kgBOD/D)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'TRT_MTHD', fieldName: '처리방법', header: { text: '처리방법' } }, + + /* 6. 연계처리량 */ + { name: 'LNK_EXC', fieldName: '분뇨', header: { text: '분뇨' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'LNK_LVS', fieldName: '축산', header: { text: '축산' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'LNK_LCH', fieldName: '침출수', header: { text: '침출수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'LNK_ETC', fieldName: '기타_연계', header: { text: '기타' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + + /* 7. 상세 시설 정보 */ + { name: 'TEL_NO', fieldName: '전화번호', header: { text: '전화번호' } }, + { name: 'COMP_DT', fieldName: '준공일', header: { text: '준공일' } }, + { name: 'RN_DT', fieldName: '가동개시일', header: { text: '가동개시일' } }, + { name: 'BSNS_CT', fieldName: '사업비(백만원)', header: { text: '사업비(백만원)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'DSCH_DSF_MTHD', fieldName: '방류수소독방법', header: { text: '방류수소독방법' } }, + + /* 8. 방류수역 */ + { name: 'WTR_SYS', fieldName: '수계', header: { text: '수계' } }, + { name: 'STRM', fieldName: '지류', header: { text: '지류' } }, + { name: 'DET_UNIT_ZONE', fieldName: '세부단위구역', header: { text: '세부단위구역' } }, + { name: 'RGN_GBN', fieldName: '지역구분', header: { text: '지역구분' } }, + { name: 'MID_ZONE_NM', fieldName: '명칭', header: { text: '명칭' } }, + { name: 'MID_ZONE_BOD', fieldName: '목표수질(BOD)', header: { text: '목표수질(BOD)' } }, + + /* 9. 에너지 및 환경 */ + { name: 'ENG_SLF_RT', fieldName: '에너지자립율(%)', header: { text: '에너지자립율(%)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'ENG_SLF_QTY', fieldName: '에너지자체생산량(TOE)', header: { text: '에너지자체생산량(TOE)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'ENG_TOT_SUM', fieldName: '계(TOE)', header: { text: '계(TOE)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'ENG_TOT_PWR', fieldName: '총전력사용량(KWh)', header: { text: '총전력사용량(KWh)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'ENG_TOT_SLF', fieldName: '자체에너지사용량(TOE)', header: { text: '자체에너지사용량(TOE)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'CO2_EMISS', fieldName: '하수처리량당CO2배출량(KgCO2/m3)', header: { text: '하수처리량당CO2배출량(KgCO2/m3)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.000' }, + + /* 10. 운영 및 위탁 */ + { name: 'OPR_MBY', fieldName: '운영주체', header: { text: '운영주체' } }, + { name: 'ENTRPS_NM', fieldName: '위탁업체명', header: { text: '위탁업체명' } }, + { name: 'ENTRPS_CT', fieldName: '위탁비용(백만원/년)', header: { text: '위탁비용(백만원/년)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'ENTRPS_TERM', fieldName: '위탁계약기간', header: { text: '위탁계약기간' } }, + { name: 'CTR_TRT_FCT', fieldName: '중심처리시설', header: { text: '중심처리시설' } }, + { name: 'EMP_CNT', fieldName: '직원총수(명)', header: { text: '직원총수(명)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'ACC_CNT', fieldName: '안전사고건수', header: { text: '안전사고건수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + ], + columnLayout: [ + { name: 'groupA', header: { text: '행정구역' }, items: ['sido', 'sgg'] }, + 'FCT_NM_GROUP', 'GBN', 'ADDR', 'FCT_CPC_GROUP', + { + name: 'groupB', + header: { text: '시설용량(㎥/일)' }, + items: ['FCT_CPC_PHY', 'FCT_CPC_BIO', 'FCT_CPC_ADV'], + }, + 'IN_QTY_GROUP', + { + name: 'groupC', + header: { text: '유입하수량(㎥/일)' }, + items: ['IN_QTY_PHY', 'IN_QTY_BIO', 'IN_QTY_ADV'], + }, + 'OUT_QTY_GROUP', + { + name: 'groupD', + header: { text: '방류량(㎥/일)' }, + items: ['OUT_QTY_PHY', 'OUT_QTY_BIO', 'OUT_QTY_ADV'], + }, + 'IN_BOD', 'OUT_BOD', 'IN_TOC', 'OUT_TOC', 'IN_SS', 'OUT_SS', 'IN_TN', 'OUT_TN', 'IN_TP', 'OUT_TP', 'IN_COLI', 'OUT_COLI', + 'TRT_EFF', 'TRT_LOD', 'TRT_MTHD', + { + name: 'groupE', + header: { text: '연계처리량(㎥/일)' }, + items: ['LNK_EXC', 'LNK_LVS', 'LNK_LCH', 'LNK_ETC'], + }, + 'TEL_NO', 'COMP_DT', 'RN_DT', 'BSNS_CT', 'DSCH_DSF_MTHD', + { + name: 'groupF', + header: { text: '방류수역' }, + items: ['WTR_SYS', 'STRM', 'DET_UNIT_ZONE', 'RGN_GBN', + { + name: 'groupG', + header: { text: '중권역' }, + items: ['MID_ZONE_NM', 'MID_ZONE_BOD'], + }, + ], + }, + 'ENG_SLF_RT', 'ENG_SLF_QTY', + { + name: 'groupH', + header: { text: '연간 총 에너지 사용량(TOE)' }, + items: ['ENG_TOT_SUM', 'ENG_TOT_PWR', 'ENG_TOT_SLF'], + }, + 'CO2_EMISS', 'OPR_MBY', + { + name: 'groupI', + header: { text: '위탁현황' }, + items: ['ENTRPS_NM', 'ENTRPS_CT', 'ENTRPS_TERM'], + }, + 'CTR_TRT_FCT', 'EMP_CNT', 'ACC_CNT', 'RMK' + ] +}; + +// 최종 Export 객체 (일괄 editable: false 적용) +export const PublicSewageFacilityInfoList = { + ...rawPublicSewageFacilityInfoList, + columns: rawPublicSewageFacilityInfoList.columns.map(col => ({ + ...col, + editable: false + })) +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/review/PumpingStationRvwColumn.ts b/apps/st/src/components/util/column/review/PumpingStationRvwColumn.ts new file mode 100644 index 000000000..058eb39e3 --- /dev/null +++ b/apps/st/src/components/util/column/review/PumpingStationRvwColumn.ts @@ -0,0 +1,106 @@ +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 05.펌프장 +export const rawPumpingStationInfoList = { + columns: [ + /* 1. 행정구역 및 기본정보 */ + { name: 'sido', fieldName: '시도', header: { text: '시도' } }, + { name: 'sgg', fieldName: '구군', header: { text: '구군' } }, + { name: 'PUMP_NM', fieldName: '펌프장명', header: { text: '펌프장명' } }, + { name: 'GBN', fieldName: '구분', header: { text: '구분' } }, + { name: 'TRT_FCT_NM', fieldName: '처리장명', header: { text: '처리장명' } }, + { name: 'TYPE_GBN', fieldName: '구분(합류식분류식)', header: { text: '구분(합류식분류식)' } }, + { name: 'ADDR', fieldName: '위치', header: { text: '위치' } }, + { name: 'RN_DT', fieldName: '가동년월일 yyyy-mm-dd', header: { text: '가동년월일\nyyyy-mm-dd' } }, + { name: 'DSN_AR', fieldName: '계획배수면적(km2)', header: { text: '계획배수면적\n(km2)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.000' }, + + /* 2. 계획배수량 (m3/hr) */ + { name: 'DSN_QTY_A', fieldName: '청천시(A)', header: { text: '청천시(A)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'DSN_QTY_B', fieldName: '우천시(B)', header: { text: '우천시(B)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'DSN_QTY_DIFF', fieldName: '우수량(B-A)', header: { text: '우수량(B-A)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + + /* 3. 총 양수량 (m3/min) */ + { name: 'TOT_PUMP_QTY_O', fieldName: '오수펌프 총양수량', header: { text: '오수펌프\n총양수량' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'TOT_PUMP_QTY_W', fieldName: '우수펌프 총양수량', header: { text: '우수펌프\n총양수량' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + + /* 4. 오수펌프 상세 (1, 2, 3) */ + { name: 'PUMP_O1_D', fieldName: '구경(mm)_오수1', header: { text: '구경(mm)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'PUMP_O1_Q', fieldName: '양수량(m3/min)_오수1', header: { text: '양수량\n(m3/min)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'PUMP_O1_C', fieldName: '펌프대수_오수1', header: { text: '펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'PUMP_O1_S', fieldName: '예비대수_오수1', header: { text: '예비대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + + { name: 'PUMP_O2_D', fieldName: '구경(mm)_오수2', header: { text: '구경(mm)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'PUMP_O2_Q', fieldName: '양수량(m3/min)_오수2', header: { text: '양수량\n(m3/min)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'PUMP_O2_C', fieldName: '펌프대수_오수2', header: { text: '펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'PUMP_O2_S', fieldName: '예비대수_오수2', header: { text: '예비대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + + { name: 'PUMP_O3_D', fieldName: '구경(mm)_오수3', header: { text: '구경(mm)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'PUMP_O3_Q', fieldName: '양수량(m3/min)_오수3', header: { text: '양수량\n(m3/min)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'PUMP_O3_C', fieldName: '펌프대수_오수3', header: { text: '펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'PUMP_O3_S', fieldName: '예비대수_오수3', header: { text: '예비대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + + /* 5. 우수펌프 상세 (1, 2, 3) */ + { name: 'PUMP_W1_D', fieldName: '구경(mm)_우수1', header: { text: '구경(mm)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'PUMP_W1_Q', fieldName: '양수량(m3/min)_우수1', header: { text: '양수량\n(m3/min)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'PUMP_W1_C', fieldName: '펌프대수_우수1', header: { text: '펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'PUMP_W1_S', fieldName: '예비대수_우수1', header: { text: '예비대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + + { name: 'PUMP_W2_D', fieldName: '구경(mm)_우수2', header: { text: '구경(mm)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'PUMP_W2_Q', fieldName: '양수량(m3/min)_우수2', header: { text: '양수량\n(m3/min)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'PUMP_W2_C', fieldName: '펌프대수_우수2', header: { text: '펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'PUMP_W2_S', fieldName: '예비대수_우수2', header: { text: '예비대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + + { name: 'PUMP_W3_D', fieldName: '구경(mm)_우수3', header: { text: '구경(mm)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'PUMP_W3_Q', fieldName: '양수량(m3/min)_우수3', header: { text: '양수량\n(m3/min)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'PUMP_W3_C', fieldName: '펌프대수_우수3', header: { text: '펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'PUMP_W3_S', fieldName: '예비대수_우수3', header: { text: '예비대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + + /* 6. 운영 정보 */ + { name: 'PWR_USAGE', fieldName: '연간전력사용량(kWh/년)', header: { text: '연간전력사용량\n(kWh/년)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + ], + columnLayout: [ + { name: 'groupA', header: { text: '행정구역' }, items: ['sido', 'sgg'] }, + 'PUMP_NM', 'GBN', 'TRT_FCT_NM', 'TYPE_GBN', 'ADDR', 'RN_DT', 'DSN_AR', + { + name: 'groupB', + header: { text: '계획시간대최대배수량(m3/hr)' }, + items: ['DSN_QTY_A', 'DSN_QTY_B', 'DSN_QTY_DIFF'], + }, + { + name: 'groupC', + header: { text: '배수능력' }, + items: [ + 'TOT_PUMP_QTY_O', + { + name: 'pumpO_Detail', + header: { text: '오수펌프 상세' }, + items: [ + { name: 'O1', header: { text: '오수펌프(1)' }, items: ['PUMP_O1_D', 'PUMP_O1_Q', 'PUMP_O1_C', 'PUMP_O1_S'] }, + { name: 'O2', header: { text: '오수펌프(2)' }, items: ['PUMP_O2_D', 'PUMP_O2_Q', 'PUMP_O2_C', 'PUMP_O2_S'] }, + { name: 'O3', header: { text: '오수펌프(3)' }, items: ['PUMP_O3_D', 'PUMP_O3_Q', 'PUMP_O3_C', 'PUMP_O3_S'] } + ] + }, + 'TOT_PUMP_QTY_W', + { + name: 'pumpW_Detail', + header: { text: '우수펌프 상세' }, + items: [ + { name: 'W1', header: { text: '우수펌프(1)' }, items: ['PUMP_W1_D', 'PUMP_W1_Q', 'PUMP_W1_C', 'PUMP_W1_S'] }, + { name: 'W2', header: { text: '우수펌프(2)' }, items: ['PUMP_W2_D', 'PUMP_W2_Q', 'PUMP_W2_C', 'PUMP_W2_S'] }, + { name: 'W3', header: { text: '우수펌프(3)' }, items: ['PUMP_W3_D', 'PUMP_W3_Q', 'PUMP_W3_C', 'PUMP_W3_S'] } + ] + } + ], + }, + 'PWR_USAGE', 'RMK', + ] +}; + +export const PumpingStationInfoList = { + ...rawPumpingStationInfoList, + columns: rawPumpingStationInfoList.columns.map(col => ({ + ...col, + editable: false + })) +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/review/SewageExcretaRelatedRvwColumn.ts b/apps/st/src/components/util/column/review/SewageExcretaRelatedRvwColumn.ts new file mode 100644 index 000000000..be414d00f --- /dev/null +++ b/apps/st/src/components/util/column/review/SewageExcretaRelatedRvwColumn.ts @@ -0,0 +1,135 @@ +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 12. 하수 및 분뇨 관련 산업 +const rawExcretaRelatedList = [ + { + tabKey: 'a', + tabLabel: '공공기관 하수분야', + rowStartNum: 2, + fixedColumns: 2, + columns: [ + { name: 'SIDO', fieldName: '시도', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' } }, + { name: 'OCGNZ', fieldName: '직제', header: { text: '직제' } }, + { name: 'YR_GBN', fieldName: '구분', header: { text: '구분' } }, + { name: 'TOT_WTR_QRM', fieldName: '직원총수', header: { text: '직원총수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'ADMINIST_JSSFC', fieldName: '행정직', header: { text: '행정직' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'TCHNLGY_JSSFC_SM', fieldName: '계(기술직)', header: { text: '계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'ENGRK_JSSFC', fieldName: '토목', header: { text: '토목' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'MCHN_JSSFC', fieldName: '기계', header: { text: '기계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'ELCTY_JSSFC', fieldName: '전기', header: { text: '전기' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'CHMSTRY_JSSFC', fieldName: '화공', header: { text: '화공' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'ENVRN_JSSFC', fieldName: '환경', header: { text: '환경' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'TCHNLGY_ETC', fieldName: '기타(기술직)', header: { text: '기타' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'ETC', fieldName: '기타', header: { text: '기타' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'OCGNZ', 'YR_GBN', 'TOT_WTR_QRM', 'ADMINIST_JSSFC', + { + name: 'techJobGroup', + header: { text: '기술직' }, + items: ['TCHNLGY_JSSFC_SM', 'ENGRK_JSSFC', 'MCHN_JSSFC', 'ELCTY_JSSFC', 'CHMSTRY_JSSFC', 'ENVRN_JSSFC', 'TCHNLGY_ETC'] + }, + 'ETC', 'RMK' + ] + }, + { + tabKey: 'b', + tabLabel: '공공기관 분뇨분야', + rowStartNum: 2, + fixedColumns: 1, + columns: [ + { name: 'SIDO', fieldName: '시도', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' } }, + { name: 'OCGNZ', fieldName: '직제', header: { text: '직제' } }, + { name: 'YR_GBN', fieldName: '구분', header: { text: '구분' } }, + { name: 'TOT_WTR_QRM', fieldName: '직원총수', header: { text: '직원총수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'ADMINIST_JSSFC', fieldName: '행정직', header: { text: '행정직' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'TCHNLGY_JSSFC_SM', fieldName: '계(기술직)', header: { text: '계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'ENGRK_JSSFC', fieldName: '토목', header: { text: '토목' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'MCHN_JSSFC', fieldName: '기계', header: { text: '기계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'ELCTY_JSSFC', fieldName: '전기', header: { text: '전기' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'CHMSTRY_JSSFC', fieldName: '화공', header: { text: '화공' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'ENVRN_JSSFC', fieldName: '환경', header: { text: '환경' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'TCHNLGY_ETC', fieldName: '기타(기술직)', header: { text: '기타' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'ETC', fieldName: '기타', header: { text: '기타' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'OCGNZ', 'YR_GBN', 'TOT_WTR_QRM', 'ADMINIST_JSSFC', + { + name: 'techJobGroup', + header: { text: '기술직' }, + items: ['TCHNLGY_JSSFC_SM', 'ENGRK_JSSFC', 'MCHN_JSSFC', 'ELCTY_JSSFC', 'CHMSTRY_JSSFC', 'ENVRN_JSSFC', 'TCHNLGY_ETC'] + }, + 'ETC', 'RMK' + ] + }, + { + tabKey: 'c', + tabLabel: '분뇨수집·운반업', + rowStartNum: 2, + fixedColumns: 2, + columns: [ + { name: 'SIDO', fieldName: '시도', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' } }, + { name: 'YR_GBN', fieldName: '구분', header: { text: '구분' } }, + { name: 'ECSCS_SM', fieldName: '업체수(개소)', header: { text: '업체수(개소)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'ECSCS_BSMN', fieldName: '종사인원(명)', header: { text: '종사인원(명)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'VHCLE_SM', fieldName: '계(차량)', header: { text: '계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'THREE_BELOW', fieldName: '3톤이하', header: { text: '3톤이하' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'FOUR_BELOW', fieldName: '4.5톤이하', header: { text: '4.5톤이하' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'EIGHT_BELOW', fieldName: '8톤이하', header: { text: '8톤이하' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'ETC_VHCLE', fieldName: '기타(차량)', header: { text: '기타' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, // name 중복 방지를 위해 ETC_VHCLE로 수정 권장 + { name: 'COLCT_TRSP_ACMSLT', fieldName: '수집·운반실적(㎥/년)', header: { text: '수집·운반실적\n(㎥/년)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'YR_GBN', 'ECSCS_SM', 'ECSCS_BSMN', + { + name: 'vehicleGroup', + header: { text: '시설(차량) 확보현황(대수)' }, + items: ['VHCLE_SM', 'THREE_BELOW', 'FOUR_BELOW', 'EIGHT_BELOW', 'ETC_VHCLE'] + }, + 'COLCT_TRSP_ACMSLT', 'RMK' + ] + }, + { + tabKey: 'd', + tabLabel: '개인하수처리관련업', + rowStartNum: 2, + fixedColumns: 2, + columns: [ + { name: 'SIDO', fieldName: '시도', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' } }, + { name: 'YR_GBN', fieldName: '구분', header: { text: '구분' } }, + { name: 'CNSTRCT_ENTRPS', fieldName: '업소수(시공업)', header: { text: '업소수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'CNSTRCT_BSMN', fieldName: '종사인원(명)_시공업', header: { text: '종사인원(명)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'MNFCTUR_ENTRPS', fieldName: '업소수(제조업)', header: { text: '업소수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'MNFCTUR_BSMN', fieldName: '종사인원(명)_제조업', header: { text: '종사인원(명)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'MGMT_ENTRPS', fieldName: '업소수(관리업)', header: { text: '업소수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'MGMT_BSMN', fieldName: '종사인원(명)_관리업', header: { text: '종사인원(명)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'RMK', fieldName: '비고', header: { text: '비고' } } + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'YR_GBN', + { name: 'constructGroup', header: { text: '개인하수처리시설 설계ㆍ시공업' }, items: ['CNSTRCT_ENTRPS', 'CNSTRCT_BSMN'] }, + { name: 'manufactureGroup', header: { text: '개인하수처리시설 제조업' }, items: ['MNFCTUR_ENTRPS', 'MNFCTUR_BSMN'] }, + { name: 'managementGroup', header: { text: '개인하수처리시설 관리업' }, items: ['MGMT_ENTRPS', 'MGMT_BSMN'] }, + 'RMK' + ] + } +]; + +export const SewageExcretaRelatedInfoListRvw = rawExcretaRelatedList.map(tab => ({ + ...tab, + columns: tab.columns.map(col => ({ + ...col, + editable: false + })) +})); \ No newline at end of file diff --git a/apps/st/src/components/util/column/review/SewageExcretaSlurgeRvwColumn.ts b/apps/st/src/components/util/column/review/SewageExcretaSlurgeRvwColumn.ts new file mode 100644 index 000000000..b9dfcbf98 --- /dev/null +++ b/apps/st/src/components/util/column/review/SewageExcretaSlurgeRvwColumn.ts @@ -0,0 +1,199 @@ +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 11. 하수 및 분뇨 찌꺼기(슬러지) 처리 +const rawSludgeList = [ + { + tabKey: 'a', + tabLabel: '찌꺼기 처리시설 현황', + rowStartNum: 4, + fixedColumns: 2, + columns: [ + /* 기본 정보 */ + { name: 'SIDO', fieldName: '시도', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' } }, + { name: 'BSC_FCT_NM', fieldName: '하수처리장명', header: { text: '하수\n처리장명' } }, + { name: 'YR_GBN', fieldName: '구분', header: { text: '구분' } }, + + /* 찌꺼기발생량 */ + { name: 'TOT_AMNT', fieldName: '합계(발생)', header: { text: '합계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'P_AMNT', fieldName: '자체(발생)', header: { text: '자체' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'O_AMNT', fieldName: '외부유입량(발생)', header: { text: '외부유입량' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + + /* 처리 정보 */ + { name: 'OUT_FCT_NM', fieldName: '이송처리장', header: { text: '이송처리장' } }, + { name: 'OUT_AMNT', fieldName: '이송처리량(㎥/년)', header: { text: '이송처리량\n(㎥/년)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'DSP_MOIRAT', fieldName: '함수율(%,탈수기준)', header: { text: '함수율\n(%,\n탈수기준)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'DSP_SM', fieldName: '찌꺼기 처리량 합계(톤/년)', header: { text: '찌꺼기\n처리량\n합계(톤/년)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + + /* 자체 찌꺼기 처리량 */ + { name: 'I_1CH_SM', fieldName: '자체찌꺼기 처리량소계', header: { text: '자체찌꺼기\n처리량소계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'I_1CH_REC', fieldName: '매립(자체)', header: { text: '매립' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'I_1CH_INC', fieldName: '소각(자체)', header: { text: '소각' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'I_1CH_INC_2CH_SM', fieldName: '소계(소각후)', header: { text: '소계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'I_1CH_INC_2CH_REC', fieldName: '매립(소각후)', header: { text: '매립' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'I_1CH_INC_2CH_RAW', fieldName: '제품원료(소각후)', header: { text: '제품원료' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'I_1CH_INC_2CH_COV', fieldName: '복토재(소각후)', header: { text: '복토재' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'I_1CH_INC_2CH_FCT', fieldName: '시설(업체명)_소각후', header: { text: '시설(업체명)' } }, + { name: 'I_1CH_SOL', fieldName: '고화(자체)', header: { text: '고화' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'I_1CH_SOL_2CH_SM', fieldName: '소계(고화후)', header: { text: '소계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'I_1CH_SOL_2CH_REC', fieldName: '매립(고화후)', header: { text: '매립' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'I_1CH_SOL_2CH_COV', fieldName: '복토재(고화후)', header: { text: '복토재' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'I_1CH_SOL_2CH_FCT', fieldName: '시설(업체명)_고화후', header: { text: '시설(업체명)' } }, + { name: 'I_1CH_DRY', fieldName: '건조(자체)', header: { text: '건조' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'I_1CH_DRY_2CH_SM', fieldName: '소계(건조후)', header: { text: '소계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'I_1CH_DRY_2CH_REC', fieldName: '매립(건조후)', header: { text: '매립' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'I_1CH_DRY_2CH_INC', fieldName: '소각(건조후)', header: { text: '소각' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'I_1CH_DRY_2CH_FUE', fieldName: '연료(건조후)', header: { text: '연료' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'I_1CH_DRY_2CH_RAW', fieldName: '제품원료(건조후)', header: { text: '제품원료' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'I_1CH_DRY_2CH_COV', fieldName: '복토재(건조후)', header: { text: '복토재' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'I_1CH_DRY_2CH_COM', fieldName: '퇴비화(건조후)', header: { text: '퇴비화' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'I_1CH_DRY_2CH_FCT', fieldName: '시설(업체명)_건조후', header: { text: '시설(업체명)' } }, + { name: 'I_1CH_CAR', fieldName: '탄화(자체)', header: { text: '탄화' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'I_1CH_CAR_2CH_SM', fieldName: '소계(탄화후)', header: { text: '소계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'I_1CH_CAR_2CH_INC', fieldName: '소각(탄화후)', header: { text: '소각' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'I_1CH_CAR_2CH_FUE', fieldName: '연료(탄화후)', header: { text: '연료' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'I_1CH_CAR_2CH_FCT', fieldName: '시설(업체명)_탄화후', header: { text: '시설(업체명)' } }, + { name: 'I_1CH_COM', fieldName: '퇴비화(자체)', header: { text: '퇴비화' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'I_1CH_COM_FCT', fieldName: '시설(업체명)_퇴비화', header: { text: '시설(업체명)' } }, + + /* 외부위탁 처리량 */ + { name: 'O_CON_SM', fieldName: '소계(외부위탁)', header: { text: '소계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'O_CON_REC', fieldName: '매립(외부위탁)', header: { text: '매립' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'O_CON_INC', fieldName: '소각(외부위탁)', header: { text: '소각' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'O_CON_FUE1', fieldName: '연료(외부위탁)', header: { text: '연료' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'O_CON_RAW', fieldName: '제품원료(외부위탁)', header: { text: '제품원료' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'O_CON_COV', fieldName: '복토재(외부위탁)', header: { text: '복토재' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'O_CON_COM', fieldName: '퇴비화(외부위탁)', header: { text: '퇴비화' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'O_CON_FCT_NM', fieldName: '시설(업체명)_외부위탁', header: { text: '시설(업체명)' } }, + + /* 기타 */ + { name: 'OM_1CH_SM', fieldName: '수도권광역 위탁처리량(톤/년)', header: { text: '수도권광역\n위탁처리량\n(톤/년)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'BSC_FCT_NM', 'YR_GBN', + { name: 'genAmntGroup', header: { text: '찌꺼기발생량(톤/년,탈수기준)' }, items: ['TOT_AMNT', 'P_AMNT', 'O_AMNT'] }, + 'OUT_FCT_NM', 'OUT_AMNT', 'DSP_MOIRAT', 'DSP_SM', + { + name: 'internalProcessGroup', header: { text: '자체 찌꺼기 처리량(톤/년)' }, + items: [ + 'I_1CH_SM', 'I_1CH_REC', 'I_1CH_INC', + { name: 'inc2ndGroup', header: { text: '소각 후 처리(2차)' }, items: ['I_1CH_INC_2CH_SM', 'I_1CH_INC_2CH_REC', 'I_1CH_INC_2CH_RAW', 'I_1CH_INC_2CH_COV', 'I_1CH_INC_2CH_FCT'] }, + 'I_1CH_SOL', + { name: 'sol2ndGroup', header: { text: '고화 후 처리(2차)' }, items: ['I_1CH_SOL_2CH_SM', 'I_1CH_SOL_2CH_REC', 'I_1CH_SOL_2CH_COV', 'I_1CH_SOL_2CH_FCT'] }, + 'I_1CH_DRY', + { name: 'dry2ndGroup', header: { text: '건조 후 처리(2차)' }, items: ['I_1CH_DRY_2CH_SM', 'I_1CH_DRY_2CH_REC', 'I_1CH_DRY_2CH_INC', 'I_1CH_DRY_2CH_FUE', 'I_1CH_DRY_2CH_RAW', 'I_1CH_DRY_2CH_COV', 'I_1CH_DRY_2CH_COM', 'I_1CH_DRY_2CH_FCT'] }, + 'I_1CH_CAR', + { name: 'car2ndGroup', header: { text: '탄화 후 처리(2차)' }, items: ['I_1CH_CAR_2CH_SM', 'I_1CH_CAR_2CH_INC', 'I_1CH_CAR_2CH_FUE', 'I_1CH_CAR_2CH_FCT'] }, + { name: 'compostGroup', header: { text: '퇴비화' }, items: ['I_1CH_COM_FCT'] } + ] + }, + { + name: 'externalProcessGroup', header: { text: '외부(민간)위탁 처리량(톤/년)' }, + items: ['O_CON_SM', 'O_CON_REC', 'O_CON_INC', 'O_CON_FUE1', 'O_CON_RAW', 'O_CON_COV', 'O_CON_COM', 'O_CON_FCT_NM'] + }, + 'OM_1CH_SM', 'RMK' + ] + }, + { + tabKey: 'b', + tabLabel: '소화조 운영현황', + rowStartNum: 3, + columns: [ + { name: 'SIDO', fieldName: '시도', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' } }, + { name: 'BSC_FCT_NM', fieldName: '하수처리장명', header: { text: '하수\n처리장명' } }, + { name: 'YR_GBN', fieldName: '구분', header: { text: '구분' } }, + { name: 'DGSTR_FNNC', fieldName: '소화조 유/무(Y/N)선택', header: { text: '소화조 유/무\n(Y/N)선택' } }, + { name: 'DGSTR_CPC', fieldName: '설계용량(㎥)', header: { text: '설계\n용량\n(㎥)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'DGSTR_OPR_CPC', fieldName: '실제운전용량(㎥)', header: { text: '실제운전\n용량\n(㎥)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'DGSTR_IN', fieldName: '투입량(㎥/일)', header: { text: '투입량\n(㎥/일)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'DGSTR_TS', fieldName: '투입 TS 농도(%)', header: { text: '투입 TS 농도\n(%)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'DGSTR_YRSTP_RN_DYN', fieldName: '가동일수(연간)', header: { text: '가동일수\n(연간)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'DGSTR_OCCUR_GAS', fieldName: '발생가스량(㎥/일)', header: { text: '발생가스량\n(㎥/일)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'DGSTR_SM', fieldName: '계(이용현황)', header: { text: '계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'DGSTR_SLE', fieldName: '판매', header: { text: '판매' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'DGSTR_ELTGNR', fieldName: '발전', header: { text: '발전' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'DGSTR_ONSLF', fieldName: '자체활용', header: { text: '자체활용' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'DGSTR_ETC', fieldName: '기타(CNG등)', header: { text: '기타(CNG등)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'DGSTR_GAS', fieldName: '잉여가스량(㎥/일)', header: { text: '잉여가스량\n(㎥/일)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'BSC_FCT_NM', 'YR_GBN', + { + name: 'digesterMainGroup', header: { text: '소화조 운영현황' }, + items: [ + 'DGSTR_FNNC', 'DGSTR_CPC', 'DGSTR_OPR_CPC', 'DGSTR_IN', 'DGSTR_TS', 'DGSTR_YRSTP_RN_DYN', 'DGSTR_OCCUR_GAS', + { name: 'gasUsageGroup', header: { text: '소화가스 이용현황(㎥/일)' }, items: ['DGSTR_SM', 'DGSTR_SLE', 'DGSTR_ELTGNR', 'DGSTR_ONSLF', 'DGSTR_ETC'] }, + 'DGSTR_GAS' + ] + }, + 'RMK' + ] + }, + { + tabKey: 'c', + tabLabel: '하수찌꺼기 처리시설 운영현황', + rowStartNum: 2, + columns: [ + { name: 'SIDO', fieldName: '시도', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' } }, + { name: 'SLG_FCT_NM', fieldName: '찌꺼기처리시설명', header: { text: '찌꺼기처리시설명' } }, + { name: 'YR_GBN', fieldName: '구분', header: { text: '구분' } }, + { name: 'TRT_METHOD', fieldName: '처리방식', header: { text: '처리방식' } }, + { name: 'SLG_FCT_CPC', fieldName: '시설용량(톤/일)', header: { text: '시설용량\n(톤/일)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'OPR_CPC', fieldName: '연간처리량(톤/년)', header: { text: '연간처리량\n(톤/년)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'ODY_RN_TIME', fieldName: '일평균 가동시간(1~24)', header: { text: '일평균\n가동시간\n(1~24)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'YRSTP_RN_DYN', fieldName: '연중 가동일(1~330)', header: { text: '연중 가동일\n(1~330)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'OPR_RT', fieldName: '가동율', header: { text: '가동율' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'SLG_FCT_NM', 'YR_GBN', + { name: 'sludgeFctOprGroup', header: { text: '처리시설 운영현황' }, items: ['TRT_METHOD', 'SLG_FCT_CPC', 'OPR_CPC', 'ODY_RN_TIME', 'YRSTP_RN_DYN', 'OPR_RT'] }, + 'RMK' + ] + }, + { + tabKey: 'd', + tabLabel: '분뇨찌꺼기 처리현황', + rowStartNum: 2, + columns: [ + { name: 'SIDO', fieldName: '시도', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' } }, + { name: 'ECSCS_NM', fieldName: '분뇨 처리장명', header: { text: '분 뇨\n처리장명' } }, + { name: 'YR_GBN', fieldName: '구분', header: { text: '구분' } }, + { name: 'OCTY', fieldName: '발생량(A)=(B)+(C)(㎥/년)', header: { text: '발생량\n(A)=(B)+(C)\n(㎥/년)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'PROCESS_CPCTY_SM', fieldName: '계(B)', header: { text: '계(B)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RUSE', fieldName: '재활용', header: { text: '재활용' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'LAND_RCLM', fieldName: '육상매립', header: { text: '육상매립' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'INCNR', fieldName: '소각', header: { text: '소각' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'OCEAN_DPSL', fieldName: '해양투기', header: { text: '해양투기' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'FUEL', fieldName: '연료화', header: { text: '연료화' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'ETC', fieldName: '기타', header: { text: '기타' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'CYFD_CPCTY', fieldName: '미처분량(이월량)(C)(㎥/년)', header: { text: '미처분량\n(이월량)\n(C)\n(㎥/년)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'OUT_BSC_FCT_NM', fieldName: '타시설 연계여부', header: { text: '타시설\n연계여부' } }, + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + { name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' } }, + { name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' } }, + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'ECSCS_NM', 'YR_GBN', 'OCTY', + { name: 'disposalAmntGroup', header: { text: '처 분 량(㎥/년)' }, items: ['PROCESS_CPCTY_SM', 'RUSE', 'LAND_RCLM', 'INCNR', 'OCEAN_DPSL', 'FUEL', 'ETC'] }, + 'CYFD_CPCTY', 'OUT_BSC_FCT_NM', 'RMK' + ] + } +]; + +export const SewageExcretaSludgeInfoListRvw = rawSludgeList.map(tab => ({ + ...tab, + columns: tab.columns.map(col => ({ + ...col, + editable: false + })) +})); \ No newline at end of file diff --git a/apps/st/src/components/util/column/review/SewagePipeMaintainRvwColumn.ts b/apps/st/src/components/util/column/review/SewagePipeMaintainRvwColumn.ts new file mode 100644 index 000000000..28aa25ce5 --- /dev/null +++ b/apps/st/src/components/util/column/review/SewagePipeMaintainRvwColumn.ts @@ -0,0 +1,137 @@ +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 03. 하수관로 유지 관리 +const rawMaintainList = [ + { + tabKey: 'a', + tabLabel: '개보수', + rowStartNum: 4, + fixedColumns: 2, + columns: [ + /* 1. 행정 및 기본 정보 */ + { name: 'SIDO', fieldName: '시도', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' } }, + { name: 'YR_GBN', fieldName: '구분', header: { text: '구분' } }, + + /* 2. 전체보수 (단위: m) */ + { name: 'RPAIR_PIPE_SM', fieldName: '계(전체보수)', header: { text: '계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RPAIR_CMBN_PIPE', fieldName: '합류식(전체보수)', header: { text: '합류식' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RPAIR_RAINWTR_PIPE', fieldName: '우수(전체보수)', header: { text: '우수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RPAIR_PLTWTR_PIPE', fieldName: '오수(전체보수)', header: { text: '오수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + + /* 3. 부분보수 (단위: 개소) */ + { name: 'RPAIR_PIPE_SM2', fieldName: '계(부분보수)', header: { text: '계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RPAIR_CMBN_PIPE2', fieldName: '합류식(부분보수)', header: { text: '합류식' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RPAIR_RAINWTR_PIPE2', fieldName: '우수(부분보수)', header: { text: '우수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RPAIR_PLTWTR_PIPE2', fieldName: '오수(부분보수)', header: { text: '오수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + + /* 4. 부속시설 (개소) */ + { name: 'MNHLE_SITE', fieldName: '합류식 맨홀', header: { text: '합류식\n맨홀' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'PLTWTR_MNHLE_SITE', fieldName: '오수맨홀', header: { text: '오수맨홀' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'RAINWTR_MNHLE_SITE', fieldName: '우수맨홀', header: { text: '우수맨홀' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'PLTWTR_RCVR_SITE', fieldName: '오수받이(개소)', header: { text: '오수받이(개소)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'RAINWTR_RCVR_SITE', fieldName: '빗물받이(개소)', header: { text: '빗물받이(개소)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'SOILHOLE_SITE', fieldName: '토실(개소)', header: { text: '토실(개소)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'SOILSPLL_SITE', fieldName: '토구(개소)', header: { text: '토구(개소)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + + /* 5. 조사량 및 비고 */ + { name: 'CCTV', fieldName: '수밀검사·CCTV조사량(m)', header: { text: '수밀검사·\nCCTV조사량\n(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'YR_GBN', + { + name: 'mtcPipeGroup', header: { text: '개·보수관로' }, + items: [ + { + name: 'totalRepairGroup', header: { text: '전체보수(m)' }, + items: ['RPAIR_PIPE_SM', 'RPAIR_CMBN_PIPE', { name: 'sepTotalGroup', header: { text: '분류식' }, items: ['RPAIR_RAINWTR_PIPE', 'RPAIR_PLTWTR_PIPE'] }] + }, + { + name: 'partRepairGroup', header: { text: '부분보수(개소)' }, + items: ['RPAIR_PIPE_SM2', 'RPAIR_CMBN_PIPE2', { name: 'sepPartGroup', header: { text: '분류식' }, items: ['RPAIR_RAINWTR_PIPE2', 'RPAIR_PLTWTR_PIPE2'] }] + } + ] + }, + 'MNHLE_SITE', + { name: 'sepMnhleGroup', header: { text: '분류식' }, items: ['PLTWTR_MNHLE_SITE', 'RAINWTR_MNHLE_SITE'] }, + 'PLTWTR_RCVR_SITE', 'RAINWTR_RCVR_SITE', + { name: 'soilGroup', header: { text: '토실·토구(개소)' }, items: ['SOILHOLE_SITE', 'SOILSPLL_SITE'] }, + 'CCTV', 'RMK' + ] + }, + { + tabKey: 'b', + tabLabel: '관로준설', + rowStartNum: 4, + fixedColumns: 2, + columns: [ + /* 1. 행정 및 기본 정보 */ + { name: 'SIDO', fieldName: '시도', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' } }, + { name: 'YR_GBN', fieldName: '구분', header: { text: '구분' } }, + + /* 2. 시설 기본 정보 */ + { name: 'PIPL_FCLTY_EXTN', fieldName: '하수관로 총시설연장(m)', header: { text: '하수관로\n총시설연장\n(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + + /* 3. 준설연장 및 개소수 */ + { name: 'DRDG_PIPE_SM', fieldName: '합계(준설)', header: { text: '합계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'DRDG_CMBN_PIPE', fieldName: '합류식(준설)', header: { text: '합류식' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'DRDG_RAINWTR_PIPE', fieldName: '우수(준설)', header: { text: '우수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'DRDG_PLTWTR_PIPE', fieldName: '오수(준설)', header: { text: '오수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'MNHLE_SITE_DRDG', fieldName: '맨홀(개소)', header: { text: '맨홀\n(개소)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'PLTWTR_RCVR_SITE_DRDG', fieldName: '오·우수받이(개소)', header: { text: '오·우수받이\n(개소)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'SOILSPLL_SOILHOLE_SITE', fieldName: '토실·토구(개소)', header: { text: '토실·토구\n(개소)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + + /* 4. 준설 계획 및 율 */ + { name: 'DRDG_TRGET_EXTN', fieldName: '준설 계획 연장(m)', header: { text: '준설 계획\n연장(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'DRDG_TRGET_RT', fieldName: '계획대비 준설율(%)', header: { text: '계획대비\n준설율\n(%)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'PIPL_FCLTY_RT', fieldName: '준설계획 연장대비 준설율(%)', header: { text: '준설계획\n연장대비\n준설율(%)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + + /* 5. 준설실적 */ + { name: 'DRDG_ACMSLT_AMOUNT', fieldName: '금액(백만원)', header: { text: '금액\n(백만원)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'DRDG_ACMSLT_QY', fieldName: '준설량(㎥)', header: { text: '준설량\n(㎥)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + + /* 6. 준설토사처리 */ + { name: 'DRDG_ACMSLT_UNTPC', fieldName: '준설토 처리비(백만원)', header: { text: '준설토\n처리비\n(백만원)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'DRDG_ETND_SM', fieldName: '처리량 계(톤)', header: { text: '처리량 계\n(톤)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RCLM', fieldName: '매립', header: { text: '매립' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'OCEAN_DPSL', fieldName: '해양투기', header: { text: '해양투기' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RUSE', fieldName: '재활용', header: { text: '재활용' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'ETC', fieldName: '기타(준설)', header: { text: '기타\n(소각, 고형화,\n시멘트원료 등)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + + /* 7. 기타 및 의견 */ + { name: 'RMK_DRDG', fieldName: '비고(준설)', header: { text: '비고' } }, + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'YR_GBN', 'PIPL_FCLTY_EXTN', + { + name: 'drdgGroup', header: { text: '준설연장 및 개소수' }, + items: [ + { + name: 'pipeLenGroup', header: { text: '관로(m)' }, + items: ['DRDG_PIPE_SM', 'DRDG_CMBN_PIPE', { name: 'sepRepairGroup', header: { text: '분류식' }, items: ['DRDG_RAINWTR_PIPE', 'DRDG_PLTWTR_PIPE'] }] + }, + 'MNHLE_SITE_DRDG', 'PLTWTR_RCVR_SITE_DRDG', 'SOILSPLL_SOILHOLE_SITE' + ] + }, + 'DRDG_TRGET_EXTN', 'DRDG_TRGET_RT', 'PIPL_FCLTY_RT', + { name: 'acmsltGroup', header: { text: '준설실적' }, items: ['DRDG_ACMSLT_AMOUNT', 'DRDG_ACMSLT_QY'] }, + { + name: 'processGroup', header: { text: '준설토사처리' }, + items: ['DRDG_ACMSLT_UNTPC', 'DRDG_ETND_SM', 'RCLM', 'OCEAN_DPSL', 'RUSE', 'ETC'] + }, + 'RMK_DRDG' + ] + } +]; + +export const SewagePipeMaintainColumnInfoList = rawMaintainList.map(tab => ({ + ...tab, + columns: tab.columns.map(col => ({ + ...col, + editable: false + })) +})); \ No newline at end of file diff --git a/apps/st/src/components/util/column/review/SewagePipeRvwColumn.ts b/apps/st/src/components/util/column/review/SewagePipeRvwColumn.ts new file mode 100644 index 000000000..732752343 --- /dev/null +++ b/apps/st/src/components/util/column/review/SewagePipeRvwColumn.ts @@ -0,0 +1,335 @@ +import { ValueType } from "realgrid"; +const baseYear = new Date().getFullYear(); +import { footerIntArrayByDivision, footerDecimal3ArrayByDivision, footerAvgDecimalArrayByDivision } from "@/components/util/grid/footerSummary"; +import type { GridView, DataColumn, ColumnFooter } from "@/components/type/DataGrid.type"; + +// 하수도 통계 관리 02. 하수관로 +const rawList = [ + { + tabKey: 'a', + tabLabel: '지역별 시설현황', + rowStartNum: 4, + fixedColumns: 2, + columns: [ + { + name: 'sido', + fieldName: 'sido', + header: { text: '시도' }, + mergeCells: true, + editable: false, + sortdisable: true, + footer: [ + { + valueCallback: (grid: GridView, column: DataColumn, footerIndex: number, columnFooter: ColumnFooter, value: number | string | undefined) => { + const divisionIndex = 0; + const dataSource = grid.getDataSource(); + const rowCount = dataSource.getRowCount(); + const uniqueValues = new Set(); + for (let i = 0; i < rowCount; i++) { + const divisionValue = dataSource.getValue(i, 'displayYear'); + if (divisionValue !== null && divisionValue !== undefined && divisionValue !== '') { + uniqueValues.add(String(divisionValue)); + } + } + const divisionValues = Array.from(uniqueValues); + if (divisionValues.length > divisionIndex) return divisionValues[divisionIndex] + '합계'; + return '합계'; + } + }, + { + valueCallback: (grid: GridView, column: DataColumn, footerIndex: number, columnFooter: ColumnFooter, value: number | string | undefined) => { + const divisionIndex = 1; + const dataSource = grid.getDataSource(); + const rowCount = dataSource.getRowCount(); + const uniqueValues = new Set(); + for (let i = 0; i < rowCount; i++) { + const divisionValue = dataSource.getValue(i, 'displayYear'); + if (divisionValue !== null && divisionValue !== undefined && divisionValue !== '') { + uniqueValues.add(String(divisionValue)); + } + } + const divisionValues = Array.from(uniqueValues); + if (divisionValues.length > divisionIndex) return divisionValues[divisionIndex] + '합계'; + return '합계'; + } + } + ] + }, + { name: 'gugun', fieldName: 'gugun', header: { text: '시군구' }, mergeCells: true, editable: false, sortdisable: true }, + { name: 'adstDong', fieldName: 'adstDong', header: { text: '행정구역명' }, editable: false }, + { + name: 'displayYear', + fieldName: 'displayYear', + header: { text: '구분' }, + editable: false, + sortdisable: true, + footer: [ + { + valueCallback: (grid: GridView, column: DataColumn, footerIndex: number, columnFooter: ColumnFooter, value: number | string | undefined) => { + const divisionIndex = 0; + const dataSource = grid.getDataSource(); + const rowCount = dataSource.getRowCount(); + const uniqueValues = new Set(); + for (let i = 0; i < rowCount; i++) { + const divisionValue = dataSource.getValue(i, 'displayYear'); + if (divisionValue !== null && divisionValue !== undefined && divisionValue !== '') { + uniqueValues.add(String(divisionValue)); + } + } + const divisionValues = Array.from(uniqueValues); + return divisionValues.length > divisionIndex ? divisionValues[divisionIndex] + '합계' : ''; + } + }, + { + valueCallback: (grid: GridView, column: DataColumn, footerIndex: number, columnFooter: ColumnFooter, value: number | string | undefined) => { + const divisionIndex = 1; + const dataSource = grid.getDataSource(); + const rowCount = dataSource.getRowCount(); + const uniqueValues = new Set(); + for (let i = 0; i < rowCount; i++) { + const divisionValue = dataSource.getValue(i, 'displayYear'); + if (divisionValue !== null && divisionValue !== undefined && divisionValue !== '') { + uniqueValues.add(String(divisionValue)); + } + } + const divisionValues = Array.from(uniqueValues); + return divisionValues.length > divisionIndex ? divisionValues[divisionIndex] + '합계' : ''; + } + } + ] + }, + + /* 하수정비 기본계획 */ + { name: 'targetYr', fieldName: 'targetYr', header: { text: '하수정보기본계획목표년도' } }, + { name: 'piplImprmnPlanExtn', fieldName: 'piplImprmnPlanExtn', header: { text: '총관로정비\n계획연장(m)' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + { name: 'piplPlanExtn', fieldName: 'piplPlanExtn', header: { text: '총계획연장(m)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + { name: 'piplFcltyExtn', fieldName: 'piplFcltyExtn', header: { text: '총시설연장(m)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + { name: 'piplSupprt', fieldName: 'piplSupprt', header: { text: '하수관로\n보급률(%)' }, footer: footerAvgDecimalArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + + /* 합류식(m) */ + { name: 'cmbnPlanAr', fieldName: 'cmbnPlanAr', header: { text: '관로정비\n계획면적(㎢)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0.000' }, + { name: 'cmbnImprmnPlanExtn', fieldName: 'cmbnImprmnPlanExtn', header: { text: '관로정비\n계획연장(m)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + { name: 'cmbnPlanExtn', fieldName: 'cmbnPlanExtn', header: { text: '계획연장(m)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + { name: 'cmbnFcltyExtn', fieldName: 'cmbnFcltyExtn', header: { text: '시설연장(m)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + { name: 'cmbnRctglExtn', fieldName: 'cmbnRctglExtn', header: { text: '사각형(m)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + { name: 'cmbnCicleExtn', fieldName: 'cmbnCicleExtn', header: { text: '원형(m)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + { name: 'cmbnOpswrExtn', fieldName: 'cmbnOpswrExtn', header: { text: '개거(m)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + { name: 'cmbnSdgttrExtn', fieldName: 'cmbnSdgttrExtn', header: { text: '측구(m)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + + /* 분류식 - 오수관로 */ + { name: 'pltwtrPlanAr', fieldName: 'pltwtrPlanAr', header: { text: '관로정비\n계획면적(㎢)' }, footer: footerDecimal3ArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0.000' }, + { name: 'pltwtrImprmnPlanExtn', fieldName: 'pltwtrImprmnPlanExtn', header: { text: '관로정비\n계획연장(m)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + { name: 'pltwtrPlanExtn', fieldName: 'pltwtrPlanExtn', header: { text: '계획연장(m)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + { name: 'pltwtrFlctyExtn', fieldName: 'pltwtrFlctyExtn', header: { text: '시설연장(m)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + { name: 'pltwtrSqreExtn', fieldName: 'pltwtrSqreExtn', header: { text: '사각형(m)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + { name: 'pltwtrCiclExtn', fieldName: 'pltwtrCiclExtn', header: { text: '원형(m)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + + /* 분류식 - 우수관로 */ + { name: 'rainwtrPlanAr', fieldName: 'rainwtrPlanAr', header: { text: '관로정비\n계획면적(㎢)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0.000' }, + { name: 'rainwtrImprmnPlanExtn', fieldName: 'rainwtrImprmnPlanExtn', header: { text: '관로정비\n계획연장(m)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + { name: 'rainwtrPlanExtn', fieldName: 'rainwtrPlanExtn', header: { text: '계획연장(m)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + { name: 'rainwtrFlctyExtn', fieldName: 'rainwtrFlctyExtn', header: { text: '시설연장(m)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + { name: 'rainwtrSqreExtn', fieldName: 'rainwtrSqreExtn', header: { text: '사각형(m)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + { name: 'rainwtrCiclExtn', fieldName: 'rainwtrCiclExtn', header: { text: '원형(m)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + { name: 'rainwtrOpswrExtn', fieldName: 'rainwtrOpswrExtn', header: { text: '개거(m)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + { name: 'rainwtrSdgttrExtn', fieldName: 'rainwtrSdgttrExtn', header: { text: '측구(m)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + + /* 부속시설 */ + { name: 'mnhleSite', fieldName: 'mnhleSite', header: { text: '합류식맨홀(개소)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'pltwtrMnhleSite', fieldName: 'pltwtrMnhleSite', header: { text: '오수맨홀(개소)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'rainwtrMnhleSite', fieldName: 'rainwtrMnhleSite', header: { text: '우수맨홀(개소)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'pltwtrRcvrSite', fieldName: 'pltwtrRcvrSite', header: { text: '오수받이(개소)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'rainwtrRcvrSite', fieldName: 'rainwtrRcvrSite', header: { text: '빗물받이(개소)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'soilspllSite', fieldName: 'soilspllSite', header: { text: '우수토실(개소)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'soilholeSite', fieldName: 'soilholeSite', header: { text: '우수토구(개소)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + + /* 대장 및 기타 */ + { name: 'sewerRegstrCmptExtn', fieldName: 'sewerRegstrCmptExtn', header: { text: '하수도대장전산화연장(m)' }, footer: footerIntArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'sewerRegstrCmptRt', fieldName: 'sewerRegstrCmptRt', header: { text: '하수도대장전산화율(%)' }, footer: footerAvgDecimalArrayByDivision,valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'rmk', fieldName: 'rmk', header: { text: '비고' } }, + ], + columnLayout: [ + { name: 'areaGroup', header: { text: '행정구역' }, items: [ + { column: 'sido', footerUserSpans: [{ colspan: 4 }, { colspan: 4 }] }, + 'gugun', + 'adstDong', + ] }, + 'displayYear', + 'targetYr', 'piplImprmnPlanExtn', 'piplPlanExtn', 'piplFcltyExtn', 'piplSupprt', + { + name: 'combinedGroup', header: { text: '합류식(m)' }, + items: ['cmbnPlanAr', 'cmbnImprmnPlanExtn', 'cmbnPlanExtn', 'cmbnFcltyExtn', + { name: 'cmbnAmGroup', header: { text: '암거' }, items: ['cmbnRctglExtn', 'cmbnCicleExtn'] }, + 'cmbnOpswrExtn', 'cmbnSdgttrExtn' + ], + }, + { + name: 'sepGroup', header: { text: '분류식(m)' }, + items: [ + { + name: 'foulPipeGroup', header: { text: '오수관로' }, + items: ['pltwtrPlanAr', 'pltwtrImprmnPlanExtn', 'pltwtrPlanExtn', 'pltwtrFlctyExtn', + { name: 'foulAmGroup', header: { text: '암거' }, items: ['pltwtrSqreExtn', 'pltwtrCiclExtn'] }] + }, + { + name: 'rainPipeGroup', header: { text: '우수관로' }, + items: ['rainwtrPlanAr', 'rainwtrImprmnPlanExtn', 'rainwtrPlanExtn', 'rainwtrFlctyExtn', + { name: 'rainAmGroup', header: { text: '암거' }, items: ['rainwtrSqreExtn', 'rainwtrCiclExtn'] }, + 'rainwtrOpswrExtn', 'rainwtrSdgttrExtn'] + } + ], + }, + 'mnhleSite', + { name: 'fcltyGroup', header: { text: '부속시설(개소)' }, items: ['pltwtrMnhleSite', 'rainwtrMnhleSite'] }, + 'pltwtrRcvrSite', 'rainwtrRcvrSite', 'soilspllSite', 'soilholeSite', 'sewerRegstrCmptExtn', 'sewerRegstrCmptRt', 'rmk' + ] + }, + { + tabKey: 'b', + tabLabel: '시설별 설치현황', + rowStartNum: 5, + fixedColumns: 4, + columns: [ + { name: 'SIDO', fieldName: '시도', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' } }, + { name: '구분', fieldName: '구분', header: { text: '구분' } }, + { name: '하수처리장명', fieldName: '하수처리장명', header: { text: '하수처리장명' } }, + { name: 'FCT_CPC', fieldName: '하수처리시설용량(㎥/일)', header: { text: '하수처리\n시설용량\n(㎥/일)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + + { name: 'PROC_ZONE_SM', fieldName: '합계(면적)', header: { text: '합계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.000' }, + { name: 'CMBN_PROC_ZONE_AR', fieldName: '합류식(면적)', header: { text: '합류식' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.000' }, + { name: 'SPRT_PROC_ZONE_AR', fieldName: '분류식(면적)', header: { text: '분류식' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.000' }, + + { name: 'SEP_RATE', fieldName: '분류식화', header: { text: '분류식화' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + + { name: 'DRNG_EQP_SM', fieldName: '합계(배수)', header: { text: '합계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'CMBN_DRNG_EQP', fieldName: '합류식(배수)', header: { text: '합류식' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'SPRT_DRNG_EQP_SM', fieldName: '합계(분류배수)', header: { text: '합계(분류)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'SPRT_DRNG_EQP_CNNC', fieldName: '연결', header: { text: '연결' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'SPRT_DRNG_EQP_DCNNC', fieldName: '미연결', header: { text: '미연결' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + + { name: 'PIPL_FCLTY_EXTN', fieldName: '총 시설연장(m)', header: { text: '총\n시설연장\n(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + + { name: 'ICG_MNN_SM', fieldName: '합계(차집)', header: { text: '합계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + { name: 'ICG_EXTN', fieldName: '차집', header: { text: '차집' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + { name: 'MNN_EXTN', fieldName: '오수간선', header: { text: '오수간선' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + + { name: '합류식', fieldName: '합류식(토실)', header: { text: '합류식' } }, + { name: 'CMBN_SOILHOLE_SITE', fieldName: '우수토실(합류)', header: { text: '우수토실(합류)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'SPRT_SOILHOLE_SITE', fieldName: '우수토실(분류)', header: { text: '우수토실(분류)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'SPRT_SOILSPLL_SITE', fieldName: '우수토구(분류)', header: { text: '우수토구(분류)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + + { name: 'BRNCHLN_SM', fieldName: '합계(지선)', header: { text: '합계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + { name: 'CMBN_BRNCHLN_EXTN', fieldName: '합류식(지선)', header: { text: '합류식' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + { name: 'RAINWTR_BRNCHLN_EXTN', fieldName: '우수(지선)', header: { text: '우수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + { name: 'PLTWTR_BRNCHLN_EXTN', fieldName: '오수(지선)', header: { text: '오수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + '하수처리장명', 'FCT_CPC', '구분', + { name: 'areaGroup', header: { text: '처리구역면적(㎢)' }, items: ['PROC_ZONE_SM', 'CMBN_PROC_ZONE_AR', 'SPRT_PROC_ZONE_AR'] }, + 'SEP_RATE', + { + name: 'drainGroup', header: { text: '배수설비(개소)' }, + items: ['DRNG_EQP_SM', 'CMBN_DRNG_EQP', { name: 'sepDrainGroup', header: { text: '분류식' }, items: ['SPRT_DRNG_EQP_SM', 'SPRT_DRNG_EQP_CNNC', 'SPRT_DRNG_EQP_DCNNC'] }], + }, + { + name: 'pipeGroup', header: { text: '하수관로' }, + items: [ + 'PIPL_FCLTY_EXTN', + { name: 'trunkGroup', header: { text: '차집/오수간선(m)' }, items: ['ICG_MNN_SM', 'ICG_EXTN', 'MNN_EXTN'] }, + { + name: 'siteGroup', header: { text: '토실/토구(개소)' }, + items: [ + { name: 'cmbnSiteGroup', header: { text: '합류식' }, items: ['CMBN_SOILHOLE_SITE'] }, + { name: 'sepSiteGroup', header: { text: '분류식' }, items: ['SPRT_SOILHOLE_SITE', 'SPRT_SOILSPLL_SITE'] } + ] + }, + { + name: 'branchGroup', header: { text: '지선(m)' }, + items: ['BRNCHLN_SM', 'CMBN_BRNCHLN_EXTN', { name: 'sepBranchGroup', header: { text: '분류식' }, items: ['RAINWTR_BRNCHLN_EXTN', 'PLTWTR_BRNCHLN_EXTN'] }] + } + ], + }, + 'RMK' + ] + }, + { + tabKey: 'c', + tabLabel: '관종별 현황', + rowStartNum: 2, + fixedColumns: 3, + columns: [ + { name: 'SIDO', fieldName: '시도', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' } }, + { name: 'SWRG_PIPE_NM', fieldName: '관종', header: { text: '관종' } }, + { name: 'YR_GBN', fieldName: '구분', header: { text: '구분' } }, + + { name: 'TOTAL_EXTN_SUM', fieldName: '계(총계)', header: { text: '계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'LAYING_EXTN_SUM', fieldName: '매설연장(m)_총계', header: { text: '매설연장(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'REMVL_EXTN_SUM', fieldName: '철거연장(m)_총계', header: { text: '철거연장(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + + ...Array.from({ length: 30 }, (_, i) => { + const yNum = (30 - i).toString().padStart(2, '0'); + const yrOffset = 29 - i; + return [ + { name: `TOTAL_EXTN_${yNum}Y`, fieldName: `계(-${yrOffset}년)`, header: { text: '계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: `LAYING_EXTN_${yNum}Y`, fieldName: `매설연장(m)_(-${yrOffset}년)`, header: { text: '매설연장(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: `REMVL_EXTN_${yNum}Y`, fieldName: `철거연장(m)_(-${yrOffset}년)`, header: { text: '철거연장(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' } + ]; + }).flat(), + + { name: 'LAYING_EXTN', fieldName: `매설연장(m)_당해년도`, header: { text: '매설연장(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'SWRG_PIPE_NM', 'YR_GBN', + { name: 'summaryGroup', header: { text: '총계' }, items: ['TOTAL_EXTN_SUM', 'LAYING_EXTN_SUM', 'REMVL_EXTN_SUM'] }, + ...Array.from({ length: 30 }, (_, i) => { + const yNum = (30 - i).toString().padStart(2, '0'); + const yrOffset = 29 - i; + return { name: `yr${yNum}Group`, header: { text: `-${yrOffset}년` }, items: [`TOTAL_EXTN_${yNum}Y`, `LAYING_EXTN_${yNum}Y`, `REMVL_EXTN_${yNum}Y`] }; + }), + { name: 'currentYrGroup', header: { text: `${baseYear}년` }, items: ['LAYING_EXTN'] }, + 'RMK' + ] + }, + { + tabKey: 'd', + tabLabel: '관경별 현황', + rowStartNum: 2, + fixedColumns: 3, + columns: [ + { name: 'SIDO', fieldName: '시도', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '구군' } }, + { name: 'SWRG_PIPE_NM', fieldName: '관종', header: { text: '관종' } }, + { name: 'YR_GBN', fieldName: '구분', header: { text: '구분' } }, + { name: 'TOTAL_EXTN_M', fieldName: '총계(m)', header: { text: '총계(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'UNDER_200', fieldName: '200mm 미만', header: { text: '200mm 미만' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'OVER_200_UNDEV_300', fieldName: '200mm이상~300mm미만', header: { text: '200mm이상~\n300mm미만' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'OVER_300_UNDEV_500', fieldName: '300mm이상~500mm미만', header: { text: '300mm이상~\n500mm미만' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'OVER_500_UNDEV_800', fieldName: '500mm이상~800mm미만', header: { text: '500mm이상~\n800mm미만' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'OVER_800', fieldName: '800mm 이상', header: { text: '800mm 이상' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'BOX', fieldName: '사각형(m)', header: { text: '사각형(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'ETC', fieldName: '기타(측구/개거 등)', header: { text: '기타\n(측구/개거 등)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'SWRG_PIPE_NM', 'YR_GBN', 'TOTAL_EXTN_M', + { name: 'circleGroup', header: { text: '원형(m)' }, items: ['UNDER_200', 'OVER_200_UNDEV_300', 'OVER_300_UNDEV_500', 'OVER_500_UNDEV_800', 'OVER_800'] }, + 'BOX', 'ETC', 'RMK', + ] + } +]; + +export const SewagePipeColumnInfoListRvw = rawList.map(tab => ({ + ...tab, + columns: tab.columns.map(col => ({ + ...col, + editable: false + })) +})); \ No newline at end of file diff --git a/apps/st/src/components/util/column/review/SewageReservoirPumpRvwColumn.ts b/apps/st/src/components/util/column/review/SewageReservoirPumpRvwColumn.ts new file mode 100644 index 000000000..43aa1c62d --- /dev/null +++ b/apps/st/src/components/util/column/review/SewageReservoirPumpRvwColumn.ts @@ -0,0 +1,194 @@ +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 04. 하수저류 시설 및 빗물 펌프장 +const rawReservoirList = [ + { + tabKey: 'a', + tabLabel: '하수저류시설', + rowStartNum: 3, + fixedColumns: 2, + columns: [ + /* 1. 기본 정보 */ + { name: 'SIDO', fieldName: '시도', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' } }, + { name: 'SWGE_STRGE_NM', fieldName: '시설명', header: { text: '시설명' } }, + { name: 'ADDR', fieldName: '위치', header: { text: '위치' } }, + { name: 'YR_GBN', fieldName: '구분', header: { text: '구분' } }, + + /* 2. 시설 제원 */ + { name: 'RN_DT', fieldName: '가동개시 년월일', header: { text: '가동개시\n년월일' } }, + { name: 'PL_DSGN_FRQNC', fieldName: '계획설계빈도', header: { text: '계획설계빈도' } }, + { name: 'INS_PER1', fieldName: '침수예방', header: { text: '침수예방' } }, + { name: 'INS_PER2', fieldName: '수질오염저감', header: { text: '수질오염저감' } }, + { name: 'INS_PER3', fieldName: '하수 재이용 용도', header: { text: '하수\n재이용\n용도' } }, + { name: 'FAC_RES', fieldName: '시설구조', header: { text: '시설구조(일반지하식,지하터널식,기타)' } }, + { name: 'DGR_AR', fieldName: '집수구역면적(㎢)', header: { text: '집수구역면적(㎢)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.000' }, + { name: 'SWGE_SGBS_CPCTY', fieldName: '시설용량(㎥)', header: { text: '시설용량(㎥)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + + /* 3. 펌프시설 */ + { name: 'TOT_CPCTY', fieldName: '총양수량(㎥/min)', header: { text: '총양수량\n(㎥/min)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'PUMP_CALBR', fieldName: '구경(mm)_1', header: { text: '구경(mm)' } }, + { name: 'WP_CPCTY', fieldName: '대당 양수량_1', header: { text: '대당 양수량' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'PUMP_CO', fieldName: '펌프대수_1', header: { text: '펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'PUMP_PREPAR_CO', fieldName: '예비펌프대수_1', header: { text: '예비펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'PUMP_CALBR2', fieldName: '구경(mm)_2', header: { text: '구경(mm)' } }, + { name: 'WP_CPCTY2', fieldName: '대당 양수량_2', header: { text: '대당 양수량' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'PUMP_CO2', fieldName: '펌프대수_2', header: { text: '펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'PUMP_PREPAR_CO2', fieldName: '예비펌프대수_2', header: { text: '예비펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'PUMP_CALBR3', fieldName: '구경(mm)_3', header: { text: '구경(mm)' } }, + { name: 'WP_CPCTY3', fieldName: '대당 양수량_3', header: { text: '대당 양수량' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'PUMP_CO3', fieldName: '펌프대수_3', header: { text: '펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'PUMP_PREPAR_CO3', fieldName: '예비펌프대수_3', header: { text: '예비펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + + /* 4. 부대시설 및 방식 */ + { name: 'FRE_FNNC', fieldName: '전처리시설(유/무)', header: { text: '전처리시설(유/무)' } }, + { name: 'DORZEQP_FNNC', fieldName: '탈취설비(유/무)', header: { text: '탈취설비(유/무)' } }, + { name: 'SELF_ETRQ_FNNC', fieldName: '자가발전(유/무)', header: { text: '자가발전(유/무)' } }, + { name: 'DRA_MTH', fieldName: '배수방식(자연배수,강제배수)', header: { text: '배수방식(자연배수,강제배수)' } }, + { name: 'ELEC_MTH', fieldName: '저류방식(직렬,병렬)', header: { text: '저류방식(직렬,병렬)' } }, + { name: 'ELEC_PRO_MAT', fieldName: '저류수 처리방법', header: { text: '저류수\n처리방법' } }, + { name: 'DIS_WAT', fieldName: '방류수역(또는 연계처리장명)', header: { text: '방류수역(또는 연계처리장명)' } }, + + /* 5. 최종처분량 및 방법 */ + { name: 'SDMT_TRE_CPCTY', fieldName: '침사(량)', header: { text: '침사' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'IPRT_TRE_CPCTY', fieldName: '협잡물(량)', header: { text: '협잡물' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'SDMT_TRE_MTH', fieldName: '침사(방법)', header: { text: '침사' } }, + { name: 'IPRT_TRE_MTH', fieldName: '협잡물(방법)', header: { text: '협잡물' } }, + + /* 6. 운영실적 */ + { name: 'YRSTP_RN_DYN', fieldName: '가동일수', header: { text: '가동일수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'DSPLCMNT_AMNT', fieldName: '총배수량(㎥/년)', header: { text: '총배수량\n(㎥/년)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'SWGE_STRGE_NM', 'ADDR', 'YR_GBN', 'RN_DT', 'PL_DSGN_FRQNC', + { + name: 'purposeGroup', + header: { text: '설치목적(O,X로 표기, 중복체크 가능)' }, + items: ['INS_PER1', 'INS_PER2', 'INS_PER3'] + }, + 'FAC_RES', 'DGR_AR', 'SWGE_SGBS_CPCTY', + { + name: 'pumpMainGroup', + header: { text: '펌프시설' }, + items: [ + 'TOT_CPCTY', + { name: 'p1Group', header: { text: '펌프시설1' }, items: ['PUMP_CALBR', 'WP_CPCTY', 'PUMP_CO', 'PUMP_PREPAR_CO'] }, + { name: 'p2Group', header: { text: '펌프시설2' }, items: ['PUMP_CALBR2', 'WP_CPCTY2', 'PUMP_CO2', 'PUMP_PREPAR_CO2'] }, + { name: 'p3Group', header: { text: '펌프시설3' }, items: ['PUMP_CALBR3', 'WP_CPCTY3', 'PUMP_CO3', 'PUMP_PREPAR_CO3'] } + ] + }, + { + name: 'subFacGroup', + header: { text: '부대시설' }, + items: ['FRE_FNNC', 'DORZEQP_FNNC', 'SELF_ETRQ_FNNC'] + }, + 'DRA_MTH', 'ELEC_MTH', 'ELEC_PRO_MAT', 'DIS_WAT', + { + name: 'disposalAmtGroup', + header: { text: '최종처분량(ton/년)' }, + items: ['SDMT_TRE_CPCTY', 'IPRT_TRE_CPCTY'] + }, + { + name: 'disposalMthGroup', + header: { text: '최종처분방법' }, + items: ['SDMT_TRE_MTH', 'IPRT_TRE_MTH'] + }, + { + name: 'opGroup', + header: { text: '운영실적' }, + items: ['YRSTP_RN_DYN', 'DSPLCMNT_AMNT'] + }, + 'RMK' + ] + }, + { + tabKey: 'b', + tabLabel: '빗물펌프장', + rowStartNum: 3, + fixedColumns: 2, + columns: [ + /* 1. 기본 정보 */ + { name: 'SIDO', fieldName: '시도', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' } }, + { name: 'SWGE_STRGE_NM', fieldName: '시설명', header: { text: '시설명' } }, + { name: 'ADDR', fieldName: '위치', header: { text: '위치' } }, + { name: 'YR_GBN', fieldName: '구분', header: { text: '구분' } }, + + /* 2. 시설 제원 */ + { name: 'FCT_KND', fieldName: '시설물종류', header: { text: '시설물종류' } }, + { name: 'RN_DT', fieldName: '가동개시 년월일', header: { text: '가동개시\n년월일' } }, + { name: 'PL_DSGN_FRQNC', fieldName: '계획설계빈도', header: { text: '계획설계빈도' } }, + { name: 'DGR_AR', fieldName: '집수구역면적(㎢)', header: { text: '집수구역면적(㎢)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.000' }, + + /* 3. 유수지 및 흡수정 */ + { name: 'REV_FNNC', fieldName: '유수지유무(유,무)', header: { text: '유수지유무\n(유,무)' } }, + { name: 'REV_CPCTY', fieldName: '저류량(유수지)', header: { text: '저류량(㎥)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'ABS_CPCTY', fieldName: '저류량(흡수정)', header: { text: '저류량(㎥)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'SWGE_SGBS_CPCTY', fieldName: '시설용량(㎥)', header: { text: '시설용량(㎥)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + + /* 4. 펌프시설 */ + { name: 'TOT_CPCTY', fieldName: '총양수량(㎥/min)', header: { text: '총양수량\n(㎥/min)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'PUMP_CALBR', fieldName: '구경(mm)_1', header: { text: '구경(mm)' } }, + { name: 'WP_CPCTY', fieldName: '대당 양수량_1', header: { text: '대당 양수량' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'PUMP_CO', fieldName: '펌프대수_1', header: { text: '펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'PUMP_PREPAR_CO', fieldName: '예비펌프대수_1', header: { text: '예비펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'PUMP_CALBR2', fieldName: '구경(mm)_2', header: { text: '구경(mm)' } }, + { name: 'WP_CPCTY2', fieldName: '대당 양수량_2', header: { text: '대당 양수량' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'PUMP_CO2', fieldName: '펌프대수_2', header: { text: '펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'PUMP_PREPAR_CO2', fieldName: '예비펌프대수_2', header: { text: '예비펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'PUMP_CALBR3', fieldName: '구경(mm)_3', header: { text: '구경(mm)' } }, + { name: 'WP_CPCTY3', fieldName: '대당 양수량_3', header: { text: '대당 양수량' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'PUMP_CO3', fieldName: '펌프대수_3', header: { text: '펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'PUMP_PREPAR_CO3', fieldName: '예비펌프대수_3', header: { text: '예비펌프대수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + + /* 5. 부대시설 및 기타 */ + { name: 'FRE_FNNC', fieldName: '전처리시설', header: { text: '전처리시설' } }, + { name: 'DORZEQP_FNNC', fieldName: '탈취설비', header: { text: '탈취설비' } }, + { name: 'SELF_ETRQ_FNNC', fieldName: '자가발전', header: { text: '자가발전' } }, + { name: 'DIS_WAT', fieldName: '방류수역', header: { text: '방류수역' } }, + + /* 6. 최종처분량 및 방법 */ + { name: 'SDMT_TRE_CPCTY', fieldName: '침사(량)', header: { text: '침사(량)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'IPRT_TRE_CPCTY', fieldName: '협잡물(량)', header: { text: '협잡물(량)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'SDMT_TRE_MTH', fieldName: '침사(방법)', header: { text: '침사(방법)' } }, + { name: 'IPRT_TRE_MTH', fieldName: '협잡물(방법)', header: { text: '협잡물(방법)' } }, + + /* 7. 운영실적 */ + { name: 'YRSTP_RN_DYN', fieldName: '가동일수', header: { text: '가동일수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'DSPLCMNT_AMNT', fieldName: '총배수량(㎥/년)', header: { text: '총배수량\n(㎥/년)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'SWGE_STRGE_NM', 'ADDR', 'YR_GBN', 'FCT_KND', 'RN_DT', 'PL_DSGN_FRQNC', 'DGR_AR', + { name: 'revGroup', header: { text: '유수지' }, items: ['REV_FNNC', 'REV_CPCTY'] }, + { name: 'absGroup', header: { text: '흡수정' }, items: ['ABS_CPCTY'] }, + 'SWGE_SGBS_CPCTY', + { + name: 'pumpMainGroup', header: { text: '펌프시설' }, + items: [ + 'TOT_CPCTY', + { name: 'p1Group', header: { text: '펌프시설1' }, items: ['PUMP_CALBR', 'WP_CPCTY', 'PUMP_CO', 'PUMP_PREPAR_CO'] }, + { name: 'p2Group', header: { text: '펌프시설2' }, items: ['PUMP_CALBR2', 'WP_CPCTY2', 'PUMP_CO2', 'PUMP_PREPAR_CO2'] }, + { name: 'p3Group', header: { text: '펌프시설3' }, items: ['PUMP_CALBR3', 'WP_CPCTY3', 'PUMP_CO3', 'PUMP_PREPAR_CO3'] } + ] + }, + { name: 'subFacGroup', header: { text: '부대시설' }, items: ['FRE_FNNC', 'DORZEQP_FNNC', 'SELF_ETRQ_FNNC'] }, + 'DIS_WAT', + { name: 'disposalAmtGroup', header: { text: '최종처분량(ton/년)' }, items: ['SDMT_TRE_CPCTY', 'IPRT_TRE_CPCTY'] }, + { name: 'disposalMthGroup', header: { text: '최종처분방법' }, items: ['SDMT_TRE_MTH', 'IPRT_TRE_MTH'] }, + { name: 'opGroup', header: { text: '운영실적' }, items: ['YRSTP_RN_DYN', 'DSPLCMNT_AMNT'] }, + 'RMK' + ] + } +]; + +export const SewageReservoirPumpInfoListRvw = rawReservoirList.map(tab => ({ + ...tab, + columns: tab.columns.map(col => ({ + ...col, + editable: false + })) +})); \ No newline at end of file diff --git a/apps/st/src/components/util/column/review/SewerageFinanceRvwColumn.ts b/apps/st/src/components/util/column/review/SewerageFinanceRvwColumn.ts new file mode 100644 index 000000000..b3dc2c73e --- /dev/null +++ b/apps/st/src/components/util/column/review/SewerageFinanceRvwColumn.ts @@ -0,0 +1,215 @@ +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 09. 하수도 재정 +const rawFinanceList = [ + { + tabKey: 'a', + tabLabel: '하수도재정 결산', + rowStartNum: 3, + fixedColumns: 1, + columns: [ + /* 1. 기본 정보 */ + { name: 'SIDO', fieldName: '시도', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' } }, + { name: 'ADST_CD', fieldName: '행정코드', header: { text: '행정코드' } }, + { name: 'YR_GBN', fieldName: '구분', header: { text: '구분' } }, + + /* 2. 재정별 사업비 (수입) */ + { name: 'FNANC_WCT_SM', fieldName: '계(수입)', header: { text: '계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'NTNXPN', fieldName: '중앙정부 지원액', header: { text: '중앙정부\n지원액' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'LCLEXPNS', fieldName: '특․광역시, 도', header: { text: '특․광역시, 도' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'LCLTY_CT', fieldName: '시,군,구 지자체', header: { text: '시,군,구 지자체' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'SEWER_RNTFEE', fieldName: '하수도 사용료', header: { text: '하수도\n사용료' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'AMNTBRNCSR', fieldName: '원인자 부담금', header: { text: '원인자\n부담금' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'ETC_INCME', fieldName: '기타 잡수입', header: { text: '기타\n잡수입' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'CYFD_AMOUNT', fieldName: '이월액', header: { text: '이월액' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + + /* 3. 부채액 */ + { name: 'DEBT_AMOUNT', fieldName: '부채액(백만원)', header: { text: '부채액\n(백만원)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + + /* 4. 용도별 사업비 (지출) */ + { name: 'PRPOS_WCT_SM', fieldName: '계(지출)', header: { text: '계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'PRCPLT_SM', fieldName: '소계(처리장)', header: { text: '소계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'PRCPLT_FCLTY', fieldName: '시설비(처리장)', header: { text: '시설비' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'PRCPLT_RPAIR', fieldName: '개보수비(처리장)', header: { text: '개보수비' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'PRCPLT_ORPNS', fieldName: '운영 유지비(처리장)', header: { text: '운영\n유지비' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'PRCPLT_ETC', fieldName: '기타(처리장)', header: { text: '기타' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'SEWAGE_SM', fieldName: '소계(관로)', header: { text: '소계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'SEWAGE_FCLTY', fieldName: '시설비(관로)', header: { text: '시설비' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'SEWAGE_RPAIR', fieldName: '개보수비(관로)', header: { text: '개보수비' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'SEWAGE_ORPNS', fieldName: '운영 유지비(관로)', header: { text: '운영\n유지비' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'SEWAGE_ETC', fieldName: '기타(관로)', header: { text: '기타' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'SWGE_STRGE_SM', fieldName: '소계(저류시설)', header: { text: '소계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'SWGE_STRGE_FCLTY', fieldName: '시설비(저류시설)', header: { text: '시설비' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'SWGE_STRGE_RPAIR', fieldName: '개보수비(저류시설)', header: { text: '개보수비' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'SWGE_STRGE_ORPNS', fieldName: '운영 유지비(저류시설)', header: { text: '운영\n유지비' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'SWGE_STRGE_ETC', fieldName: '기타(저류시설)', header: { text: '기타' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'ETC_SM', fieldName: '소계(기타지출)', header: { text: '소계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'ACDNT_CYFD_AMOUNT', fieldName: '사고 이월액', header: { text: '사고\n이월액' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'UNRCIV_AMOUNT', fieldName: '미수납액', header: { text: '미수납액' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'ETC_AMOUNT', fieldName: '기타(지출)', header: { text: '기타' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + + /* 5. 비고 */ + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'ADST_CD', + { + name: 'financeGroup', + header: { text: '재정별 사업비(백만원)' }, + items: [ + 'FNANC_WCT_SM', 'NTNXPN', + { name: 'localGroup', header: { text: '지방비' }, items: ['LCLEXPNS', 'LCLTY_CT'] }, + 'SEWER_RNTFEE', 'AMNTBRNCSR', 'ETC_INCME', 'CYFD_AMOUNT' + ] + }, + 'DEBT_AMOUNT', + { + name: 'usageGroup', + header: { text: '용도별사업비(백만원)' }, + items: [ + 'PRPOS_WCT_SM', + { name: 'plantGroup', header: { text: '처리장' }, items: ['PRCPLT_SM', 'PRCPLT_FCLTY', 'PRCPLT_RPAIR', 'PRCPLT_ORPNS', 'PRCPLT_ETC'] }, + { name: 'pipeGroup', header: { text: '하수관로' }, items: ['SEWAGE_SM', 'SEWAGE_FCLTY', 'SEWAGE_RPAIR', 'SEWAGE_ORPNS', 'SEWAGE_ETC'] }, + { name: 'storageGroup', header: { text: '하수저류시설' }, items: ['SWGE_STRGE_SM', 'SWGE_STRGE_FCLTY', 'SWGE_STRGE_RPAIR', 'SWGE_STRGE_ORPNS', 'SWGE_STRGE_ETC'] }, + { name: 'etcUsageGroup', header: { text: '기타' }, items: ['ETC_SM', 'ACDNT_CYFD_AMOUNT', 'UNRCIV_AMOUNT', 'ETC_AMOUNT'] } + ] + }, + 'RMK' + ] + }, + { + tabKey: 'b', + tabLabel: '하수도 요금', + rowStartNum: 2, + fixedColumns: 1, + columns: [ + { name: 'SIDO', fieldName: '시도', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' } }, + { name: 'FYER_LEVY_QY', fieldName: '연간조정량(㎥)(A)', header: { text: '연간조정량\n(㎥)\n(A)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'LEVY_AMOUNT', fieldName: '하수도사용료수익(원)(B)', header: { text: '하수도사용료수익\n(원)\n(B)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'AVRG_UNTPC', fieldName: '㎥당요금(원/㎥) C=(B/A)', header: { text: '㎥당요금(원/㎥)\nC=(B/A)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'SMRIZE_PRMPC', fieldName: '총괄원가(원)(D)', header: { text: '총괄원가(원)\n(D)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'SMRIZE_UNIT_PRMPC', fieldName: '㎥당원가(원/㎥) E=(D/A)', header: { text: '㎥당원가(원/㎥)\nE=(D/A)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RLTY_RT', fieldName: '요금현실화율(%) F=(C/E*100)', header: { text: '요금현실화율(%)\nF=(C/E*100)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RMK', fieldName: '비고', header: { text: '비고' } } + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'FYER_LEVY_QY', 'LEVY_AMOUNT', 'AVRG_UNTPC', 'SMRIZE_PRMPC', 'SMRIZE_UNIT_PRMPC', 'RLTY_RT', 'RMK' + ] + }, + { + tabKey: 'c', + tabLabel: '하수도 사용료', + rowStartNum: 2, + fixedColumns: 1, + columns: [ + { name: 'SIDO', fieldName: '시도', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' } }, + { name: 'LEVY_AMOUNT', fieldName: '부과액(총계)', header: { text: '부과액' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'LEVY_QY', fieldName: '부과량(총계)', header: { text: '부과량' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'HOME_LEVY_AMOUNT', fieldName: '부과액(가정용)', header: { text: '부과액' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'HOME_LEVY_QY', fieldName: '부과량(가정용)', header: { text: '부과량' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'HOME_UNTPC', fieldName: '단가(가정용)', header: { text: '단가' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'JOB_LEVY_AMOUNT', fieldName: '부과액(업무용)', header: { text: '부과액' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'JOB_LEVY_QY', fieldName: '부과량(업무용)', header: { text: '부과량' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'JOB_UNTPC', fieldName: '단가(업무용)', header: { text: '단가' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'BSN_LEVY_AMOUNT', fieldName: '부과액(영업용)', header: { text: '부과액' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'BSN_LEVY_QY', fieldName: '부과량(영업용)', header: { text: '부과량' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'BSN_UNTPC', fieldName: '단가(영업용)', header: { text: '단가' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'BATHS1_LEVY_AMOUNT', fieldName: '부과액(욕탕1)', header: { text: '부과액' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'BATHS1_LEVY_QY', fieldName: '부과량(욕탕1)', header: { text: '부과량' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'BATHS1_UNTPC', fieldName: '단가(욕탕1)', header: { text: '단가' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'BATHS2_LEVY_AMOUNT', fieldName: '부과액(욕탕2)', header: { text: '부과액' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'BATHS2_LEVY_QY', fieldName: '부과량(욕탕2)', header: { text: '부과량' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'BATHS2_UNTPC', fieldName: '단가(욕탕2)', header: { text: '단가' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'INDUST_LEVY_AMOUNT', fieldName: '부과액(산업용)', header: { text: '부과액' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'INDUST_LEVY_QY', fieldName: '부과량(산업용)', header: { text: '부과량' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'INDUST_UNTPC', fieldName: '단가(산업용)', header: { text: '단가' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'ETC_LEVY_AMOUNT', fieldName: '부과액(기타사용료)', header: { text: '부과액' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'ETC_LEVY_QY', fieldName: '부과량(기타사용료)', header: { text: '부과량' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'ETC_UNTPC', fieldName: '단가(기타사용료)', header: { text: '단가' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + { + name: 'totalGroup', header: { text: '총계' }, + items: [{ name: 'tAmtUnit', header: { text: '부과액\n(백만원)' }, items: ['LEVY_AMOUNT'] }, { name: 'tQyUnit', header: { text: '부과량\n(천톤)' }, items: ['LEVY_QY'] }] + }, + { + name: 'homeGroup', header: { text: '가 정 용' }, + items: [{ name: 'hAmtUnit', header: { text: '부과액\n(백만원)' }, items: ['HOME_LEVY_AMOUNT'] }, { name: 'hQyUnit', header: { text: '부과량\n(천톤)' }, items: ['HOME_LEVY_QY'] }, { name: 'hUntUnit', header: { text: '단가\n(원/톤)' }, items: ['HOME_UNTPC'] }] + }, + { + name: 'jobGroup', header: { text: '업 무 용' }, + items: [{ name: 'jAmtUnit', header: { text: '부과액\n(백만원)' }, items: ['JOB_LEVY_AMOUNT'] }, { name: 'jQyUnit', header: { text: '부과량\n(천톤)' }, items: ['JOB_LEVY_QY'] }, { name: 'jUntUnit', header: { text: '단가\n(원/톤)' }, items: ['JOB_UNTPC'] }] + }, + { + name: 'bsnGroup', header: { text: '영 업 용(일 반 용)' }, + items: [{ name: 'bAmtUnit', header: { text: '부과액\n(백만원)' }, items: ['BSN_LEVY_AMOUNT'] }, { name: 'bQyUnit', header: { text: '부과량\n(천톤)' }, items: ['BSN_LEVY_QY'] }, { name: 'bUntUnit', header: { text: '단가\n(원/톤)' }, items: ['BSN_UNTPC'] }] + }, + { + name: 'bath1Group', header: { text: '욕 탕 1종(대중탕용)' }, + items: [{ name: 'bt1AmtUnit', header: { text: '부과액\n(백만원)' }, items: ['BATHS1_LEVY_AMOUNT'] }, { name: 'bt1QyUnit', header: { text: '부과량\n(천톤)' }, items: ['BATHS1_LEVY_QY'] }, { name: 'bt1UntUnit', header: { text: '단가\n(원/톤)' }, items: ['BATHS1_UNTPC'] }] + }, + { + name: 'bath2Group', header: { text: '욕 탕 2종' }, + items: [{ name: 'bt2AmtUnit', header: { text: '부과액\n(백만원)' }, items: ['BATHS2_LEVY_AMOUNT'] }, { name: 'bt2QyUnit', header: { text: '부과량\n(천톤)' }, items: ['BATHS2_LEVY_QY'] }, { name: 'bt2UntUnit', header: { text: '단가\n(원/톤)' }, items: ['BATHS2_UNTPC'] }] + }, + { + name: 'industGroup', header: { text: '산업용 및 공업용' }, + items: [{ name: 'iAmtUnit', header: { text: '부과액\n(백만원)' }, items: ['INDUST_LEVY_AMOUNT'] }, { name: 'iQyUnit', header: { text: '부과량\n(천톤)' }, items: ['INDUST_LEVY_QY'] }, { name: 'iUntUnit', header: { text: '단가\n(원/톤)' }, items: ['INDUST_UNTPC'] }] + }, + { + name: 'etcGroup', header: { text: '기타' }, + items: [{ name: 'eAmtUnit', header: { text: '부과액\n(백만원)' }, items: ['ETC_LEVY_AMOUNT'] }, { name: 'eQyUnit', header: { text: '부과량\n(천톤)' }, items: ['ETC_LEVY_QY'] }, { name: 'eUntUnit', header: { text: '단가\n(원/톤)' }, items: ['ETC_UNTPC'] }] + }, + 'RMK' + ] + }, + { + tabKey: 'd', + tabLabel: '원인자부담금', + rowStartNum: 3, + fixedColumns: 2, + columns: [ + { name: 'SIDO', fieldName: '시도', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' } }, + { name: 'YR_GBN', fieldName: '구분', header: { text: '구분' } }, + { name: 'LEVY_CO', fieldName: '건수(부과액)', header: { text: '건수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'LEVY_AMOUNT', fieldName: '금액(백만원)(부과액)', header: { text: '금액(백만원)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'TRGET_LEV_STTUS_CO', fieldName: '건수(계)', header: { text: '건수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'TRGET_LEV_STTUS_AMOUNT', fieldName: '금액(백만원)(계)', header: { text: '금액(백만원)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'DRNG_EQP_CO', fieldName: '건수(배수설비)', header: { text: '건수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'DRNG_EQP_AMOUNT', fieldName: '금액(백만원)(배수설비)', header: { text: '금액(백만원)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'CNTRWK_CO', fieldName: '건수(타공사)', header: { text: '건수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'CNTRWK_AMOUNT', fieldName: '금액(백만원)(타공사)', header: { text: '금액(백만원)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'YR_GBN', + { name: 'levyMainGroup', header: { text: '부과액' }, items: ['LEVY_CO', 'LEVY_AMOUNT'] }, + { + name: 'collectGroup', header: { text: '대상별 징수현황' }, + items: [ + { name: 'collectTotal', header: { text: '계' }, items: ['TRGET_LEV_STTUS_CO', 'TRGET_LEV_STTUS_AMOUNT'] }, + { name: 'contractorGroup', header: { text: '배수설비설치자\n(하수도법 제61조 제1항)' }, items: ['DRNG_EQP_CO', 'DRNG_EQP_AMOUNT'] }, + { name: 'otherGroup', header: { text: '타공사·타행위자\n(하수도법 제61조 제2항)' }, items: ['CNTRWK_CO', 'CNTRWK_AMOUNT'] } + ] + }, + 'RMK' + ] + } +]; + +export const SewerageFinanceInfoListRvw = rawFinanceList.map(tab => ({ + ...tab, + columns: tab.columns.map(col => ({ + ...col, + editable: false + })) +})); \ No newline at end of file diff --git a/apps/st/src/components/util/column/review/SeweragePenetrationRateRvwColumn.ts b/apps/st/src/components/util/column/review/SeweragePenetrationRateRvwColumn.ts new file mode 100644 index 000000000..bf1af2259 --- /dev/null +++ b/apps/st/src/components/util/column/review/SeweragePenetrationRateRvwColumn.ts @@ -0,0 +1,186 @@ +import { ValueType } from "realgrid"; +import { footerIntArrayByDivision, footerDecimal3ArrayByDivision, footerAvgDecimalArrayByDivision } from "@/components/util/grid/footerSummary"; +import type { GridView, DataColumn, ColumnFooter } from "@/components/type/DataGrid.type"; + +export const SeweragePenetrationRateInfoList = { + columns: [ + { + name: 'sido', + fieldName: 'sido', + header: { text: '시도' }, + mergeCells: true, + editable: false, + sortdisable: true, + footer: [ + { + valueCallback: (grid: GridView, column: DataColumn, footerIndex: number, columnFooter: ColumnFooter, value: number | string | undefined) => { + const divisionIndex = 0; + const dataSource = grid.getDataSource(); + const rowCount = dataSource.getRowCount(); + const uniqueValues = new Set(); + for (let i = 0; i < rowCount; i++) { + const divisionValue = dataSource.getValue(i, 'displayYear'); + if (divisionValue !== null && divisionValue !== undefined && divisionValue !== '') { + uniqueValues.add(String(divisionValue)); + } + } + const divisionValues = Array.from(uniqueValues); + if (divisionValues.length > divisionIndex) return divisionValues[divisionIndex] + '합계'; + return '합계'; + } + }, + { + valueCallback: (grid: GridView, column: DataColumn, footerIndex: number, columnFooter: ColumnFooter, value: number | string | undefined) => { + const divisionIndex = 1; + const dataSource = grid.getDataSource(); + const rowCount = dataSource.getRowCount(); + const uniqueValues = new Set(); + for (let i = 0; i < rowCount; i++) { + const divisionValue = dataSource.getValue(i, 'displayYear'); + if (divisionValue !== null && divisionValue !== undefined && divisionValue !== '') { + uniqueValues.add(String(divisionValue)); + } + } + const divisionValues = Array.from(uniqueValues); + if (divisionValues.length > divisionIndex) return divisionValues[divisionIndex] + '합계'; + return '합계'; + } + } + ] + }, + { name: 'gugun', fieldName: 'gugun', header: { text: '시군구' }, mergeCells: true, editable: false, sortdisable: true }, + { name: 'adstDong', fieldName: 'adstDong', header: { text: '행정구역명' }, mergeCells: true, editable: false, sortdisable: true }, + { + name: 'displayYear', + fieldName: 'displayYear', + header: { text: '구분' }, + editable: false, + sortdisable: true, + footer: [ + { + valueCallback: (grid: GridView, column: DataColumn, footerIndex: number, columnFooter: ColumnFooter, value: number | string | undefined) => { + const divisionIndex = 0; + const dataSource = grid.getDataSource(); + const rowCount = dataSource.getRowCount(); + const uniqueValues = new Set(); + for (let i = 0; i < rowCount; i++) { + const divisionValue = dataSource.getValue(i, 'displayYear'); + if (divisionValue !== null && divisionValue !== undefined && divisionValue !== '') { + uniqueValues.add(String(divisionValue)); + } + } + const divisionValues = Array.from(uniqueValues); + return divisionValues.length > divisionIndex ? divisionValues[divisionIndex] + '합계' : ''; + } + }, + { + valueCallback: (grid: GridView, column: DataColumn, footerIndex: number, columnFooter: ColumnFooter, value: number | string | undefined) => { + const divisionIndex = 1; + const dataSource = grid.getDataSource(); + const rowCount = dataSource.getRowCount(); + const uniqueValues = new Set(); + for (let i = 0; i < rowCount; i++) { + const divisionValue = dataSource.getValue(i, 'displayYear'); + if (divisionValue !== null && divisionValue !== undefined && divisionValue !== '') { + uniqueValues.add(String(divisionValue)); + } + } + const divisionValues = Array.from(uniqueValues); + return divisionValues.length > divisionIndex ? divisionValues[divisionIndex] + '합계' : ''; + } + } + ] + }, + { name: 'rvbsn', fieldName: 'rvbsn', header: { text: '수계' } }, + { name: 'bstrm', fieldName: 'bstrm', header: { text: '지류' } }, + { name: 'mdbsn', fieldName: 'mdbsn', header: { text: '세부단위유역' } }, + { name: 'lgbsn', fieldName: 'lgbsn', header: { text: '중권역' } }, + { name: 'mdmbsn', fieldName: 'mdmbsn', header: { text: '소권역' } }, + { name: 'speclCntrplnAr', fieldName: 'speclCntrplnAr', header: { text: '특별대책지역' } }, + { name: 'popltnSm', fieldName: 'popltnSm', header: { text: '총인구' }, footer: footerIntArrayByDivision, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'arSm', fieldName: 'arSm', header: { text: '총면적(㎢)' }, footer: footerDecimal3ArrayByDivision, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'swgeProcZoneInnerSm', fieldName: 'swgeProcZoneInnerSm', header: { text: '계' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: '#,##0', styleName: 'rg-cell-total' }, + + // 공공하수처리인구 + { name: 'pblswrgPopltnSm', fieldName: 'pblswrgPopltnSm', header: { text: '합계' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'pblswrgPrmTrt', fieldName: 'pblswrgPrmTrt', header: { text: '1차처리' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'pblswrgScdTrt', fieldName: 'pblswrgScdTrt', header: { text: '2차처리' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'pblswrgTertTrt', fieldName: 'pblswrgTertTrt', header: { text: '3차처리' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + // 폐수처리인구(명) + { name: 'wstrPopltnSm', fieldName: 'wstrPopltnSm', header: { text: '합계' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'wstrPrmTrt', fieldName: 'wstrPrmTrt', header: { text: '1차처리' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'wstrScdTrt', fieldName: 'wstrScdTrt', header: { text: '2차처리' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'wstrTertTrt', fieldName: 'wstrTertTrt', header: { text: '3차처리' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + + { name: 'notConectPopltn', fieldName: 'notConectPopltn', header: { text: '미접속인구' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'swgeProcZoneInnerAr', fieldName: 'swgeProcZoneInnerAr', header: { text: '면적(㎢)' }, footer: footerDecimal3ArrayByDivision, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'swgeProcZoneOuterSm', fieldName: 'swgeProcZoneOuterSm', header: { text: '계' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'townUnprocPopltn', fieldName: 'townUnprocPopltn', header: { text: '미처리인구' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'townSwttPopltn', fieldName: 'townSwttPopltn', header: { text: '오수처리인구' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'townWrrtnPopltn', fieldName: 'townWrrtnPopltn', header: { text: '정화조인구' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, dataFormat: '#,##0' }, + { name: 'swgeProcZoneOuterAr', fieldName: 'swgeProcZoneOuterAr', header: { text: '면적(㎢)' }, footer: footerDecimal3ArrayByDivision, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'pblswrgPopltnSuprt', fieldName: 'pblswrgPopltnSuprt', header: { text: '공공하수처리구역인구보급율(%)' }, footer: footerAvgDecimalArrayByDivision, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'adtrtPopltnSuprt', fieldName: 'adtrtPopltnSuprt', header: { text: '고도처리인구보급율(%)' }, footer: footerAvgDecimalArrayByDivision, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'swrgInstlrt', fieldName: 'swrgInstlrt', header: { text: '하수도설치율(%)' }, footer: footerAvgDecimalArrayByDivision, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' }, + { name: 'rmk', fieldName: 'rmk', header: { text: '비고' } }, + ], + columnLayout: [ + { + name: 'groupA', + hideChildHeaders: false, + header: { text: '행정구역' }, + items: [ + { column: 'sido', footerUserSpans: [{ colspan: 4 }, { colspan: 4 }] }, + 'gugun', + 'adstDong', + ] + }, + 'displayYear', + 'rvbsn', + 'bstrm', + { + name: 'groupB', + hideChildHeaders: false, + header: { text: '수계영향권별분류' }, + items: ['mdbsn', 'lgbsn', 'mdmbsn'], + }, + 'speclCntrplnAr', + 'popltnSm', + 'arSm', + { + name: 'groupD', + hideChildHeaders: false, + header: { text: '하수처리구역내' }, + items: [ + 'swgeProcZoneInnerSm', + { + name: 'pblswrgGroup', + hideChildHeaders: false, + header: { text: '공공하수처리인구(명)' }, + items: ['pblswrgPopltnSm', 'pblswrgPrmTrt', 'pblswrgScdTrt', 'pblswrgTertTrt'], + }, + { + name: 'wstrGroup', + hideChildHeaders: false, + header: { text: '폐수처리인구(명)' }, + items: ['wstrPopltnSm', 'wstrPrmTrt', 'wstrScdTrt', 'wstrTertTrt'], + }, + 'notConectPopltn', + 'swgeProcZoneInnerAr' + ], + }, + { + name: 'groupE', + hideChildHeaders: false, + header: { text: '하수처리구역외' }, + items: ['swgeProcZoneOuterSm', 'townUnprocPopltn', 'townSwttPopltn', 'townWrrtnPopltn', 'swgeProcZoneOuterAr'], + }, + 'pblswrgPopltnSuprt', + 'adtrtPopltnSuprt', + 'swrgInstlrt', + 'rmk', + ], + fixedColumns: 2, + rowStartNum: 3 +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/review/SewerageServiceLevelRvwColumn.ts b/apps/st/src/components/util/column/review/SewerageServiceLevelRvwColumn.ts new file mode 100644 index 000000000..c673bbda5 --- /dev/null +++ b/apps/st/src/components/util/column/review/SewerageServiceLevelRvwColumn.ts @@ -0,0 +1,117 @@ +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 13. 하수도 서비스 수준 +const rawServiceLevelList = [ + { + tabKey: 'a', + tabLabel: '민원', + rowStartNum: 2, + fixedColumns: 2, + columns: [ + /* 1. 기본 정보 */ + { name: 'SIDO', fieldName: 'SIDO', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: 'GUGUN', header: { text: '시군구' } }, + { name: 'YR_GBN', fieldName: 'YR_GBN', header: { text: '구분' } }, + + /* 2. 하수처리 인구 (정수 반영) */ + { name: 'POPLTN_SM', fieldName: 'POPLTN_SM', header: { text: '하수처리인구\n(명)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + + /* 3. 민원유형 (단위: 건 / 정수 반영) */ + { name: 'CVPL_CM', fieldName: 'CVPL_CM', header: { text: '총계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'CVPL_DCWTR_QLTWTR', fieldName: 'CVPL_DCWTR_QLTWTR', header: { text: '악취' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'CVPL_BSML', fieldName: 'CVPL_BSML', header: { text: '하수관로' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'CVPL_FIVW', fieldName: 'CVPL_FIVW', header: { text: '배수설비' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'CVPL_POLLUTN_ACDNT', fieldName: 'CVPL_POLLUTN_ACDNT', header: { text: '맨홀' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'CVPL_SSOS', fieldName: 'CVPL_SSOS', header: { text: '정화조' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'CVPL_CHRGE', fieldName: 'CVPL_CHRGE', header: { text: '하수도요금' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'CVPL_ETC', fieldName: 'CVPL_ETC', header: { text: '기타' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + + /* 4. 성과 지표 및 비고 (소수점 1자리 반영) */ + { name: 'STDR_POPLTN_PER_CVPL', fieldName: 'STDR_POPLTN_PER_CVPL', header: { text: '기준인구당\n민원건수\n(건/10,000명)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RMK', fieldName: 'RMK', header: { text: '비고' } } + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'YR_GBN', + 'POPLTN_SM', + { + name: 'complaintTypeGroup', + header: { text: '민원유형(건)' }, + items: [ + 'CVPL_CM', + 'CVPL_DCWTR_QLTWTR', + 'CVPL_BSML', + 'CVPL_FIVW', + 'CVPL_POLLUTN_ACDNT', + 'CVPL_SSOS', + 'CVPL_CHRGE', + 'CVPL_ETC' + ] + }, + 'STDR_POPLTN_PER_CVPL', + 'RMK' + ] + }, + { + tabKey: 'b', + tabLabel: '주민친화시설', + rowStartNum: 2, + fixedColumns: 5, + columns: [ + /* 1. 기본 및 시설 정보 */ + { name: 'SIDO', fieldName: 'SIDO', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: 'GUGUN', header: { text: '시군구' } }, + { name: 'BSC_FCT_NM', fieldName: 'BSC_FCT_NM', header: { text: '처리장명' } }, + { name: 'FCT_CPC', fieldName: 'FCT_CPC', header: { text: '시설용량\n(㎥/일)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'YR_GBN', fieldName: 'YR_GBN', header: { text: '구분' } }, + + /* 2. 처리장 전체 면적 (소수점 1자리 반영) */ + { name: 'PRCPLT_AR', fieldName: 'PRCPLT_AR', header: { text: '처리장\n면적(㎡)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + + /* 3. 주민친화시설 면적 상세 (단위: ㎡ / 정수 반영) */ + { name: 'TOTAL_AR', fieldName: 'TOTAL_AR', header: { text: '총계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'RESTRM_AR', fieldName: 'RESTRM_AR', header: { text: '휴게시설' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'CHILDPLAY_AR', fieldName: 'CHILDPLAY_AR', header: { text: '어린이놀이공간' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'ALSFC_AR', fieldName: 'ALSFC_AR', header: { text: '체력단련시설' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'PLGRND_AR', fieldName: 'PLGRND_AR', header: { text: '운동시설' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'PRKPLCE_AR', fieldName: 'PRKPLCE_AR', header: { text: '주차장' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'CTLSTT_AR', fieldName: 'CTLSTT_AR', header: { text: '교육 및\n문화시설' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'NRPRK_AR', fieldName: 'NRPRK_AR', header: { text: '습지조성\n(생태공원)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { name: 'ETC_AR', fieldName: 'ETC_AR', header: { text: '기타' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + + /* 4. 비고 */ + { name: 'RMK', fieldName: 'RMK', header: { text: '비고' } } + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'BSC_FCT_NM', + 'FCT_CPC', + 'YR_GBN', + 'PRCPLT_AR', + { + name: 'friendlyFacilityGroup', + header: { text: '주민친화시설 면적(㎡)' }, + items: [ + 'TOTAL_AR', + 'RESTRM_AR', + 'CHILDPLAY_AR', + 'ALSFC_AR', + 'PLGRND_AR', + 'PRKPLCE_AR', + 'CTLSTT_AR', + 'NRPRK_AR', + 'ETC_AR' + ] + }, + 'RMK' + ] + } +]; + +export const SewerageServiceLevelInfoListRvw = rawServiceLevelList.map(tab => ({ + ...tab, + columns: tab.columns.map(col => ({ + ...col, + editable: false + })) +})); \ No newline at end of file diff --git a/apps/st/src/components/util/column/review/StatsInputStatusRvwColumn.ts b/apps/st/src/components/util/column/review/StatsInputStatusRvwColumn.ts new file mode 100644 index 000000000..a5408cc7f --- /dev/null +++ b/apps/st/src/components/util/column/review/StatsInputStatusRvwColumn.ts @@ -0,0 +1,120 @@ +import { ValueType } from "realgrid"; + +/** + * 하수도 통계 검토 00. 통계 입력 현황 + */ +const StatsInputStatusInfoColumnRaw = { + columns: [ + // 기본 정보 + { name: 'chk', fieldName: 'chk', header: { text: '체크' }, width: 40 }, + { name: 'sido', fieldName: 'sido', header: { text: '시도' }, width: 80,mergeCells: true}, + { name: 'gugun', fieldName: 'gugun', header: { text: '시군구' }, width: 100 }, + { name: 'status', fieldName: 'status', header: { text: '진행상태' }, width: 120 }, + + // 01. 하수도보급율 (supply) + { name: 'supplyCharger', fieldName: 'supplyCharger', header: { text: '제출자' } }, + { name: 'supplyCttpc', fieldName: 'supplyCttpc', header: { text: '연락처' } }, + { name: 'supplyStatusNm', fieldName: 'supplyStatusNm', header: { text: '지자체진행상태' } }, + { name: 'supplyStatus', fieldName: 'supplyStatus', header: { text: '진행상태' } }, + { name: 'supplyCnt', fieldName: 'supplyCnt', header: { text: '건수' }, valueType: ValueType.NUMBER }, + + // 02. 하수관로 (facility) - 기존 3번 + { name: 'facilityCharger', fieldName: 'facilityCharger', header: { text: '제출자' } }, + { name: 'facilityCttpc', fieldName: 'facilityCttpc', header: { text: '연락처' } }, + { name: 'facilityStatusNm', fieldName: 'facilityStatusNm', header: { text: '지자체진행상태' } }, + { name: 'facilityStatus', fieldName: 'facilityStatus', header: { text: '진행상태' } }, + { name: 'facilityCnt', fieldName: 'facilityCnt', header: { text: '건수' }, valueType: ValueType.NUMBER }, + + // 03. 하수관로유지관리 (pipe) - 기존 4번 + { name: 'pipeCharger', fieldName: 'pipeCharger', header: { text: '제출자' } }, + { name: 'pipeCttpc', fieldName: 'pipeCttpc', header: { text: '연락처' } }, + { name: 'pipeStatusNm', fieldName: 'pipeStatusNm', header: { text: '지자체진행상태' } }, + { name: 'pipeCnt', fieldName: 'pipeCnt', header: { text: '건수' }, valueType: ValueType.NUMBER }, + + // 04. 하수저류시설 (storage) - 기존 5번 + { name: 'storageCharger', fieldName: 'storageCharger', header: { text: '제출자' } }, + { name: 'storageCttpc', fieldName: 'storageCttpc', header: { text: '연락처' } }, + { name: 'storageStatusNm', fieldName: 'storageStatusNm', header: { text: '지자체진행상태' } }, + { name: 'storageCnt', fieldName: 'storageCnt', header: { text: '건수' }, valueType: ValueType.NUMBER }, + + // 05. 펌프장 (pump) - 기존 6번 + { name: 'pumpCharger', fieldName: 'pumpCharger', header: { text: '제출자' } }, + { name: 'pumpCttpc', fieldName: 'pumpCttpc', header: { text: '연락처' } }, + { name: 'pumpStatusNm', fieldName: 'pumpStatusNm', header: { text: '지자체진행상태' } }, + { name: 'pumpCnt', fieldName: 'pumpCnt', header: { text: '건수' }, valueType: ValueType.NUMBER }, + + // 06. 공공하수처리시설 (treat) - 기존 7번 + { name: 'treatCharger', fieldName: 'treatCharger', header: { text: '제출자' } }, + { name: 'treatCttpc', fieldName: 'treatCttpc', header: { text: '연락처' } }, + { name: 'treatStatusNm', fieldName: 'treatStatusNm', header: { text: '지자체진행상태' } }, + { name: 'treatCnt', fieldName: 'treatCnt', header: { text: '건수' }, valueType: ValueType.NUMBER }, + + // 07. 분뇨처리시설 (small) - 기존 8번 + { name: 'smallCharger', fieldName: 'smallCharger', header: { text: '제출자' } }, + { name: 'smallCttpc', fieldName: 'smallCttpc', header: { text: '연락처' } }, + { name: 'smallStatusNm', fieldName: 'smallStatusNm', header: { text: '지자체진행상태' } }, + { name: 'smallCnt', fieldName: 'smallCnt', header: { text: '건수' }, valueType: ValueType.NUMBER }, + + // 08. 개인하수처리시설 (private) - 기존 9번 + { name: 'privateCharger', fieldName: 'privateCharger', header: { text: '제출자' } }, + { name: 'privateCttpc', fieldName: 'privateCttpc', header: { text: '연락처' } }, + { name: 'privateStatusNm', fieldName: 'privateStatusNm', header: { text: '지자체진행상태' } }, + { name: 'privateCnt', fieldName: 'privateCnt', header: { text: '건수' }, valueType: ValueType.NUMBER }, + + // 09. 하수도재정 (finance) - 기존 10번 + { name: 'financeCharger', fieldName: 'financeCharger', header: { text: '제출자' } }, + { name: 'financeCttpc', fieldName: 'financeCttpc', header: { text: '연락처' } }, + { name: 'financeStatusNm', fieldName: 'financeStatusNm', header: { text: '지자체진행상태' } }, + { name: 'financeCnt', fieldName: 'financeCnt', header: { text: '건수' }, valueType: ValueType.NUMBER }, + + // 10. 물재이용 (reuse) - 기존 11번 + { name: 'reuseCharger', fieldName: 'reuseCharger', header: { text: '제출자' } }, + { name: 'reuseCttpc', fieldName: 'reuseCttpc', header: { text: '연락처' } }, + { name: 'reuseStatusNm', fieldName: 'reuseStatusNm', header: { text: '지자체진행상태' } }, + { name: 'reuseCnt', fieldName: 'reuseCnt', header: { text: '건수' }, valueType: ValueType.NUMBER }, + + // 11. 하수및분뇨찌꺼기 (sludge) - 기존 12번 + { name: 'sludgeCharger', fieldName: 'sludgeCharger', header: { text: '제출자' } }, + { name: 'sludgeCttpc', fieldName: 'sludgeCttpc', header: { text: '연락처' } }, + { name: 'sludgeStatusNm', fieldName: 'sludgeStatusNm', header: { text: '지자체진행상태' } }, + { name: 'sludgeCnt', fieldName: 'sludgeCnt', header: { text: '건수' }, valueType: ValueType.NUMBER }, + + // 12. 하수및분뇨관련산업 (excreta) - 기존 13번 + { name: 'excretaCharger', fieldName: 'excretaCharger', header: { text: '제출자' } }, + { name: 'excretaCttpc', fieldName: 'excretaCttpc', header: { text: '연락처' } }, + { name: 'excretaStatusNm', fieldName: 'excretaStatusNm', header: { text: '지자체진행상태' } }, + { name: 'excretaCnt', fieldName: 'excretaCnt', header: { text: '건수' }, valueType: ValueType.NUMBER }, + + // 13. 하수도서비스수준 (complaint) - 기존 15번 + { name: 'complaintCharger', fieldName: 'complaintCharger', header: { text: '제출자' } }, + { name: 'complaintCttpc', fieldName: 'complaintCttpc', header: { text: '연락처' } }, + { name: 'complaintStatusNm', fieldName: 'complaintStatusNm', header: { text: '지자체진행상태' } }, + { name: 'complaintCnt', fieldName: 'complaintCnt', header: { text: '건수' }, valueType: ValueType.NUMBER }, + ], + + columnLayout: [ + { name: 'addrGroup', header: { text: '구분' }, items: ['sido', 'gugun'] }, + 'status', + { name: 'group01', header: { text: '01. 하수도보급율' }, items: ['supplyCharger', 'supplyCttpc', 'supplyStatusNm', 'supplyStatus', 'supplyCnt'] }, + { name: 'group02', header: { text: '02. 하수관로' }, items: ['facilityCharger', 'facilityCttpc', 'facilityStatusNm', 'facilityStatus', 'facilityCnt'] }, + { name: 'group03', header: { text: '03. 하수관로유지관리' }, items: ['pipeCharger', 'pipeCttpc', 'pipeStatusNm', 'pipeCnt'] }, + { name: 'group04', header: { text: '04. 하수저류시설' }, items: ['storageCharger', 'storageCttpc', 'storageStatusNm', 'storageCnt'] }, + { name: 'group05', header: { text: '05. 펌프장' }, items: ['pumpCharger', 'pumpCttpc', 'pumpStatusNm', 'pumpCnt'] }, + { name: 'group06', header: { text: '06. 공공하수처리시설' }, items: ['treatCharger', 'treatCttpc', 'treatStatusNm', 'treatCnt'] }, + { name: 'group07', header: { text: '07. 분뇨처리시설' }, items: ['smallCharger', 'smallCttpc', 'smallStatusNm', 'smallCnt'] }, + { name: 'group08', header: { text: '08. 개인하수처리시설' }, items: ['privateCharger', 'privateCttpc', 'privateStatusNm', 'privateCnt'] }, + { name: 'group09', header: { text: '09. 하수도재정' }, items: ['financeCharger', 'financeCttpc', 'financeStatusNm', 'financeCnt'] }, + { name: 'group10', header: { text: '10. 물재이용' }, items: ['reuseCharger', 'reuseCttpc', 'reuseStatusNm', 'reuseCnt'] }, + { name: 'group11', header: { text: '11. 하수및분뇨찌꺼기' }, items: ['sludgeCharger', 'sludgeCttpc', 'sludgeStatusNm', 'sludgeCnt'] }, + { name: 'group12', header: { text: '12. 하수및분뇨관련산업' }, items: ['excretaCharger', 'excretaCttpc', 'excretaStatusNm', 'excretaCnt'] }, + { name: 'group13', header: { text: '13. 하수도서비스수준' }, items: ['complaintCharger', 'complaintCttpc', 'complaintStatusNm', 'complaintCnt'] } + ] +}; + +export const StatsInputStatusInfoList = { + ...StatsInputStatusInfoColumnRaw, + columns: StatsInputStatusInfoColumnRaw.columns.map(col => ({ + ...col, + editable: false + })) +}; \ No newline at end of file diff --git a/apps/st/src/components/util/column/review/WaterReuseRvwColumn.ts b/apps/st/src/components/util/column/review/WaterReuseRvwColumn.ts new file mode 100644 index 000000000..04026197f --- /dev/null +++ b/apps/st/src/components/util/column/review/WaterReuseRvwColumn.ts @@ -0,0 +1,224 @@ +import { ValueType } from "realgrid"; + +// 하수도 통계 관리 10. 물 재이용 +const rawWaterReuseList = [ + { + tabKey: 'a', + tabLabel: '공공하수처리수 재이용', + rowStartNum: 4, + fixedColumns: 2, + columns: [ + /* 1. 기본 및 행정 정보 */ + { name: 'SIDO', fieldName: '시도', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' } }, + { name: 'YR_GBN', fieldName: '구분', header: { text: '구분' } }, + + /* 2. 하수처리장 기본 정보 */ + { name: 'BSC_FCT_NM', fieldName: '시설명', header: { text: '시설명' } }, + { name: 'EQP_PRMS_DT', fieldName: '인가일(년/월/일)', header: { text: '인가일\n(년/월/일)' } }, + { name: 'FCT_CPC', fieldName: '시설용량(㎥/일)', header: { text: '시설용량\n(㎥/일)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'TPUT_TOTAL', fieldName: '연간 하수처리량(천톤/년)', header: { text: '연간\n하수처리량\n(천톤/년)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RSWTR_LAW_YN', fieldName: '법적의무대상(대상/미대상)', header: { text: '법적의무대상\n(대상/미대상)' } }, + + /* 3. 재이용시설 현황 */ + { name: 'INST_YN', fieldName: '설치유무(유/무)', header: { text: '설치유무\n(유/무)' } }, + { name: 'RSWTR_RN_DT', fieldName: '가동개시일 yyyy-mm-dd', header: { text: '가동개시일\nyyyy-mm-dd' } }, + { name: 'RSWTR_PRMS_DT', fieldName: '설치승인일 yyyy-mm-dd', header: { text: '설치승인일\nyyyy-mm-dd' } }, + { name: 'RSWTR_CPC', fieldName: '용량(톤/일)', header: { text: '용량(톤/일)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RSWTR_TRT_NM', fieldName: '공법/기자재', header: { text: '공법/기자재' } }, + { name: 'RSWTR_PIPE', fieldName: '공급관로길이(km)', header: { text: '공급관로길이\n(km)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RSWTR_PUMP', fieldName: '공급펌프(톤/일)', header: { text: '공급펌프\n(톤/일)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + + /* 4. 재이용 실적 - 총계 및 장내용수 */ + { name: 'USE_AMNT_TOTAL', fieldName: '총계', header: { text: '총계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'USE_AMNT_D01', fieldName: '계(장내)', header: { text: '계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'USE_AMNT_S01', fieldName: '세척수', header: { text: '세척수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'USE_AMNT_S02', fieldName: '냉각수', header: { text: '냉각수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'USE_AMNT_S03', fieldName: '청소수', header: { text: '청소수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'USE_AMNT_S04', fieldName: '조경용수(장내)', header: { text: '조경용수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'USE_AMNT_S05', fieldName: '희석용수', header: { text: '희석용수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'USE_AMNT_S06', fieldName: '장내중수도', header: { text: '장내중수도' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'USE_AMNT_S07', fieldName: '장내기타용수', header: { text: '장내기타용수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + + /* 5. 재이용 실적 - 장외용수 */ + { name: 'USE_AMNT_D02', fieldName: '계(장외)', header: { text: '계' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'USE_AMNT_S11', fieldName: '청소·화장실 용수', header: { text: '청소·화장실\n용수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'USE_AMNT_S12', fieldName: '세척·살수용수', header: { text: '세척·살수용수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'USE_AMNT_S13', fieldName: '조경용수(장외)', header: { text: '조경용수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'USE_AMNT_S14', fieldName: '친수용수', header: { text: '친수용수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'USE_AMNT_S15', fieldName: '하천등 유지용수', header: { text: '하천등 유지용수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'USE_AMNT_S16', fieldName: '농업용수', header: { text: '농업용수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'USE_AMNT_S17', fieldName: '지하수충전', header: { text: '지하수충전' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'USE_AMNT_S18', fieldName: '공업용수', header: { text: '공업용수' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + + /* 6. 지표 및 비고 */ + { name: 'REUSE_RT', fieldName: '재이용률(%)', header: { text: '재이용률\n(%)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'YR_GBN', + { + name: 'sewagePlantGroup', + header: { text: '하수처리장' }, + items: ['BSC_FCT_NM', 'EQP_PRMS_DT', 'FCT_CPC', 'TPUT_TOTAL', 'RSWTR_LAW_YN'] + }, + { + name: 'reuseFctGroup', + header: { text: '재이용시설' }, + items: [ + 'INST_YN', + { + name: 'installDetailGroup', + header: { text: '재이용시설 설치시' }, + items: [ + 'RSWTR_RN_DT', + 'RSWTR_PRMS_DT', + { + name: 'reProcessGroup', + header: { text: '재처리시설' }, + items: ['RSWTR_CPC', 'RSWTR_TRT_NM'] + }, + 'RSWTR_PIPE', + 'RSWTR_PUMP' + ] + } + ] + }, + { + name: 'reuseStatusGroup', + header: { text: '하수처리수 재이용현황(천톤/년)' }, + items: [ + 'USE_AMNT_TOTAL', + { + name: 'internalUseGroup', + header: { text: '장 내 용 수' }, + items: [ + 'USE_AMNT_D01', 'USE_AMNT_S01', 'USE_AMNT_S02', 'USE_AMNT_S03', + 'USE_AMNT_S04', 'USE_AMNT_S05', 'USE_AMNT_S06', 'USE_AMNT_S07' + ] + }, + { + name: 'externalUseGroup', + header: { text: '장 외 용 수' }, + items: [ + 'USE_AMNT_D02', 'USE_AMNT_S11', 'USE_AMNT_S12', 'USE_AMNT_S13', + 'USE_AMNT_S14', 'USE_AMNT_S15', 'USE_AMNT_S16', 'USE_AMNT_S17', 'USE_AMNT_S18' + ] + } + ] + }, + 'REUSE_RT', + 'RMK' + ] + }, + { + tabKey: 'b', + tabLabel: '중수도 현황', + rowStartNum: 2, + columns: [ + { name: 'SIDO', fieldName: '시도', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' } }, + { name: 'BILD_NM', fieldName: '건축물명/개발사업명', header: { text: '건축물명/\n개발사업명' } }, + { name: 'ADDR', fieldName: '주소', header: { text: '주소' } }, + { name: 'YR_GBN', fieldName: '구분', header: { text: '구분' } }, + { name: 'BILD_TP', fieldName: '시설물용도/개발사업종류', header: { text: '시설물용도/\n개발사업종류' } }, + { name: 'LAW_YN', fieldName: '법적의무 대상여부(대상/미대상)', header: { text: '법적의무\n대상여부\n(대상/미대상)' } }, + { name: 'COMP_DT', fieldName: '설치완료일 yyyy-mm-dd', header: { text: '설치완료일\nyyyy-mm-dd' } }, + { name: 'RN_DT', fieldName: '가동개시일 yyyy-mm-dd', header: { text: '가동개시일\nyyyy-mm-dd' } }, + { name: 'TOTAR', fieldName: '건축연면적(㎡)', header: { text: '건축연면적\n(㎡)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'PROC_CPCTY', fieldName: '처리용량(㎥/일)', header: { text: '처리용량\n(㎥/일)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'WATER_USE_CPCTY', fieldName: '물 사용량(㎥/년)', header: { text: '물 사용량\n(㎥/년)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RUSWTR_USE_SM', fieldName: '중수도 이용량(㎥/년)', header: { text: '중수도\n이용량\n(㎥/년)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'SUB_USE_CPCTY', fieldName: '대체수 이용량(㎥/년)', header: { text: '대체수\n이용량\n(㎥/년)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RUSWTR_PRPOS', fieldName: '중수도용도', header: { text: '중수도용도' } }, + { name: 'FYER_OPER_CT', fieldName: '연간운영비(백만원/년)', header: { text: '연간운영비\n(백만원/년)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RMK', fieldName: '비고', header: { text: '비고' } } + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'BILD_NM', 'ADDR', 'YR_GBN', 'BILD_TP', 'LAW_YN', 'COMP_DT', 'RN_DT', + { + name: 'fctStatusGroup', + header: { text: '시설현황' }, + items: ['TOTAR', 'PROC_CPCTY'] + }, + 'WATER_USE_CPCTY', 'RUSWTR_USE_SM', 'SUB_USE_CPCTY', 'RUSWTR_PRPOS', 'FYER_OPER_CT', 'RMK' + ] + }, + { + tabKey: 'c', + tabLabel: '빗물이용시설 현황', + rowStartNum: 2, + columns: [ + { name: 'SIDO', fieldName: '시도', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' } }, + { name: 'RWR_REUSE_FCT_NM', fieldName: '시설명', header: { text: '시설명' } }, + { name: 'YR_GBN', fieldName: '구분', header: { text: '구분' } }, + { name: 'BILD_TP', fieldName: '건축물용도', header: { text: '건축물용도' } }, + { name: 'ADDR', fieldName: '위치(주소)', header: { text: '위치\n(주소)' } }, + { name: 'LAW_YN', fieldName: '법적의무 대상여부(대상/미대상)', header: { text: '법적의무\n대상여부\n(대상/미대상)' } }, + { name: 'RN_DT', fieldName: '설치완료일 yyyy-mm-dd', header: { text: '설치완료일\nyyyy-mm-dd' } }, + { name: 'INSTL_CT', fieldName: '설치비(백만원)', header: { text: '설치비\n(백만원)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'CCM', fieldName: '집수면 종류', header: { text: '집수면 종류' } }, + { name: 'TRTFC_ENNC', fieldName: '여과등 처리시설 유무', header: { text: '여과등\n처리시설\n유무' } }, + { name: 'SGBS_CPCTY', fieldName: '저류조 용량(㎥)', header: { text: '저류조 용량\n(㎥)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'FYER_USEQTY', fieldName: '연간 이용량(㎥/년)', header: { text: '연간 이용량\n(㎥/년)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RWR_PRCUSE_PRPPOS', fieldName: '이용용도', header: { text: '이용용도' } }, + { name: 'FYER_OPER_CT', fieldName: '연간운영비(백만원/년)', header: { text: '연간운영비\n(백만원/년)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'RWR_REUSE_FCT_NM', 'YR_GBN', 'BILD_TP', 'ADDR', 'LAW_YN', 'RN_DT', 'INSTL_CT', 'CCM', 'TRTFC_ENNC', 'SGBS_CPCTY', 'FYER_USEQTY', 'RWR_PRCUSE_PRPPOS', 'FYER_OPER_CT', 'RMK' + ] + }, + { + tabKey: 'd', + tabLabel: '하ㆍ폐수처리수 재이용시설 시공업 등록현황', + rowStartNum: 2, + columns: [ + { name: 'SIDO', fieldName: '시도', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' } }, + { name: 'YR_GBN', fieldName: '구분', header: { text: '구분' } }, + { name: 'CMPNM', fieldName: '상호(명칭)', header: { text: '상호(명칭)' } }, + { name: 'RPRSNTV', fieldName: '대표자', header: { text: '대표자' } }, + { name: 'ADDR', fieldName: '주소', header: { text: '주소' } }, + { name: 'CMPN_RGSDE', fieldName: '등록일 yyyy-mm-dd', header: { text: '등록일\nyyyy-mm-dd' } }, + { name: 'RMK', fieldName: '비고', header: { text: '비고' } }, + { name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' } }, + { name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' } }, + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'YR_GBN', 'CMPNM', 'RPRSNTV', 'ADDR', 'CMPN_RGSDE', 'RMK' + ] + }, + { + tabKey: 'e', + tabLabel: '하ㆍ폐수처리수 재이용시설 시공업 인가현황', + rowStartNum: 2, + columns: [ + { name: 'SIDO', fieldName: '시도', header: { text: '시도' } }, + { name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' } }, + { name: 'YR_GBN', fieldName: '구분', header: { text: '구분' } }, + { name: 'CMPNM', fieldName: '상호(명칭)', header: { text: '상호(명칭)' } }, + { name: 'RPRSNTV', fieldName: '대표자', header: { text: '대표자' } }, + { name: 'ADDR', fieldName: '주소', header: { text: '주소' } }, + { name: 'CMPN_RGSDE', fieldName: '인가일(년/월/일)', header: { text: '인가일\n(년/월/일)' } }, + { name: 'RMK', fieldName: '비고', header: { text: '비고' } } + ], + columnLayout: [ + { name: 'addrGroup', header: { text: '행정구역' }, items: ['SIDO', 'GUGUN'] }, + 'YR_GBN', 'CMPNM', 'RPRSNTV', 'ADDR', 'CMPN_RGSDE', 'RMK' + ] + } +]; + +export const WaterReuseInfoList = rawWaterReuseList.map(tab => ({ + ...tab, + columns: tab.columns.map(col => ({ + ...col, + editable: false + })) +})); \ No newline at end of file diff --git a/apps/st/src/components/util/grid/computedFieldCallbacks.ts b/apps/st/src/components/util/grid/computedFieldCallbacks.ts new file mode 100644 index 000000000..a19b9f7ea --- /dev/null +++ b/apps/st/src/components/util/grid/computedFieldCallbacks.ts @@ -0,0 +1,99 @@ +import type { DataFieldValueCallback, ComputedFieldConfig } from "@/components/type/DataGrid.type"; + +/** 값 → 숫자 변환 (null/undefined/빈문자열 → 0) */ +function toNum(v: unknown): number { + if (v == null || v === "") return 0; + if (typeof v === "number") return isNaN(v) ? 0 : v; + const n = parseFloat(String(v).replace(/,/g, "")); + return isNaN(n) ? 0 : n; +} + +/** 합계용 valueCallback 생성 */ +export function createSumValueCallback(fieldNames: string[]): DataFieldValueCallback { + return (_prod, _dataRow, _fieldName, fieldNamesArr, values) => { + let sum = 0; + for (const f of fieldNames) { + const idx = fieldNamesArr.indexOf(f); + if (idx >= 0) sum += toNum(values[idx]); + } + return sum; + }; +} + +/** 평균용 valueCallback 생성 */ +export function createAvgValueCallback(fieldNames: string[]): DataFieldValueCallback { + return (_prod, _dataRow, _fieldName, fieldNamesArr, values) => { + let sum = 0; + let count = 0; + for (const f of fieldNames) { + const idx = fieldNamesArr.indexOf(f); + if (idx >= 0) { + const val = toNum(values[idx]); + sum += val; + count += 1; + } + } + return count > 0 ? sum / count : 0; + }; +} + +// 공공하수처리구역인구보급률(%), 고도처리인구보급률(%) valueCallback +export function createCoverageRateValueCallback( + numeratorFields: string[], + denominatorFields: string[] +): DataFieldValueCallback { + return (_prod, _dataRow, _fieldName, fieldNamesArr, values) => { + const getSum = (fields: string[]) => { + let sum = 0; + for (const f of fields) { + const idx = fieldNamesArr.indexOf(f); + if (idx >= 0) sum += toNum(values[idx]); + } + return sum; + }; + const total2 = getSum(numeratorFields); + const total1 = getSum(denominatorFields); + return total1 === 0 ? 0 : (total2 / total1) * 100; + }; +} + +// 하수도 설치율(%) valueCallback +export function createSwrgInstlrtRateValueCallback( + numeratorFields: string[], + denominatorFields: string[], + thirdFields: string[], +): DataFieldValueCallback { + return (_prod, _dataRow, _fieldName, fieldNamesArr, values) => { + const getSum = (fields: string[]) => { + let sum = 0; + for (const f of fields) { + const idx = fieldNamesArr.indexOf(f); + if (idx >= 0) sum += toNum(values[idx]); + } + return sum; + }; + const sum1 = getSum(numeratorFields); + const sum2 = getSum(denominatorFields); + + const piplSupprt = toNum(values[fieldNamesArr.indexOf(thirdFields[0] || 'piplSupprt')]); + + const total1 = (sum1 === 0 ? 0 : Math.round(sum2 / sum1 * 100)); + + return total1 * piplSupprt / 100; + }; +} + +/** 연산 타입에 맞는 valueCallback 반환 */ +export function getComputedValueCallback(config: ComputedFieldConfig): DataFieldValueCallback { + switch (config.type) { + case 'sum': + return createSumValueCallback(config.fields); + case 'avg': + return createAvgValueCallback(config.fields); + case 'pblswrgPopltnSuprtRate': + case 'adtrtPopltnSuprtRate': + return createCoverageRateValueCallback(config.numeratorFields, config.denominatorFields); + case 'swrgInstlrtRate': + return createSwrgInstlrtRateValueCallback(config.numeratorFields, config.denominatorFields, config.thirdFields); + } +} diff --git a/apps/st/src/components/util/grid/footerSummary.ts b/apps/st/src/components/util/grid/footerSummary.ts new file mode 100644 index 000000000..0b9bd148b --- /dev/null +++ b/apps/st/src/components/util/grid/footerSummary.ts @@ -0,0 +1,252 @@ +import type { GridView, DataColumn } from "@/components/type/DataGrid.type"; + +// 그리드 푸터 합계 계산 함수 (계산 로직) +export const getFooterSummary = (grid: GridView, column: DataColumn) => { + // 데이터 소스에서 직접 합계 계산 + const dataSource = grid.getDataSource(); + const rowCount = dataSource.getRowCount(); + let sum = 0; + + for (let i = 0; i < rowCount; i++) { + const cellValue = dataSource.getValue(i, column.fieldName); + + // 문자열인 경우 숫자로 변환 + const numericValue = typeof cellValue === 'string' + ? Number(String(cellValue).replace(/,/g, '')) + : Number(cellValue); + + if (!isNaN(numericValue) && numericValue !== null && numericValue !== undefined) { + sum += numericValue; + } + } + + return sum; +}; + +// 평균 계산 함수 +export const getFooterAvgSummary = (grid: GridView, column: DataColumn) => { + const dataSource = grid.getDataSource(); + const rowCount = dataSource.getRowCount(); + + let sum = 0; + let avg = 0; + + for (let i = 0; i < rowCount; i++) { + const cellValue = dataSource.getValue(i, column.fieldName); + + const numericValue = typeof cellValue === 'string' + ? Number(String(cellValue).replace(/,/g, '')) + : Number(cellValue); + + if (!isNaN(numericValue) && numericValue !== null && numericValue !== undefined) { + sum += numericValue; + } + } + + avg = sum / rowCount; + + return Math.floor(avg * 10) / 10; +} + +// 구분 필드의 고유값들을 찾는 함수 (첫 번째, 두 번째). 기본 필드명 'displayYear' +const getDivisionValues = (grid: GridView, divisionFieldName: string = 'displayYear'): string[] => { + const dataSource = grid.getDataSource(); + const rowCount = dataSource.getRowCount(); + const uniqueValues = new Set(); + + for (let i = 0; i < rowCount; i++) { + const divisionValue = dataSource.getValue(i, divisionFieldName); + if (divisionValue !== null && divisionValue !== undefined && divisionValue !== '') { + uniqueValues.add(String(divisionValue)); + } + } + + // Set을 배열로 변환하고 순서 유지 + return Array.from(uniqueValues); +}; + +// 구분 필드 값에 따라 필터링하여 합계 계산하는 함수. 기본 필드명 'displayYear' +// divisionIndex: 0 = 첫 번째 고유값, 1 = 두 번째 고유값 +export const getFooterSummaryByDivision = ( + grid: GridView, + column: DataColumn, + divisionIndex: number, + divisionFieldName: string = 'displayYear' +) => { + const dataSource = grid.getDataSource(); + const rowCount = dataSource.getRowCount(); + let sum = 0; + + // "구분" 필드의 고유값 찾기 + const divisionValues = getDivisionValues(grid, divisionFieldName); + if (divisionValues.length <= divisionIndex) { + return 0; // 해당 인덱스의 고유값이 없으면 0 반환 + } + + const targetDivision = divisionValues[divisionIndex]; + + for (let i = 0; i < rowCount; i++) { + // "구분" 필드 값 확인 + const divisionValue = dataSource.getValue(i, divisionFieldName); + if (String(divisionValue) !== targetDivision) { + continue; // 해당 "구분" 값이 아니면 스킵 + } + + const cellValue = dataSource.getValue(i, column.fieldName); + + // 문자열인 경우 숫자로 변환 + const numericValue = typeof cellValue === 'string' + ? Number(String(cellValue).replace(/,/g, '')) + : Number(cellValue); + + if (!isNaN(numericValue) && numericValue !== null && numericValue !== undefined) { + sum += numericValue; + } + } + + return sum; +}; + +// 구분 필드 값에 따라 필터링하여 평균 계산하는 함수. 기본 필드명 'displayYear' +// divisionIndex: 0 = 첫 번째 고유값, 1 = 두 번째 고유값 +export const getFooterAvgSummaryByDivision = ( + grid: GridView, + column: DataColumn, + divisionIndex: number, + divisionFieldName: string = 'displayYear' +) => { + const dataSource = grid.getDataSource(); + const rowCount = dataSource.getRowCount(); + let sum = 0; + let count = 0; + + // "구분" 필드의 고유값 찾기 + const divisionValues = getDivisionValues(grid, divisionFieldName); + if (divisionValues.length <= divisionIndex) { + return 0; // 해당 인덱스의 고유값이 없으면 0 반환 + } + + const targetDivision = divisionValues[divisionIndex]; + + for (let i = 0; i < rowCount; i++) { + // "구분" 필드 값 확인 + const divisionValue = dataSource.getValue(i, divisionFieldName); + if (String(divisionValue) !== targetDivision) { + continue; // 해당 "구분" 값이 아니면 스킵 + } + + const cellValue = dataSource.getValue(i, column.fieldName); + + // 문자열인 경우 숫자로 변환 + const numericValue = typeof cellValue === 'string' + ? Number(String(cellValue).replace(/,/g, '')) + : Number(cellValue); + + if (!isNaN(numericValue) && numericValue !== null && numericValue !== undefined) { + sum += numericValue; + count += 1; + } + } + + if (count === 0) { + return 0; + } + + const avg = sum / count; + return Math.floor(avg * 10) / 10; +}; + +// Footer 설정 객체 생성 함수 +const createSumFooter = (format: string) => ({ + expression: 'sum' as const, + numberFormat: format, + valueCallback: (grid: GridView, column: DataColumn) => { + return getFooterSummary(grid, column); + }, +}); + +const createAvgFooter = (format: string) => ({ + expression: 'avg' as const, + numberFormat: format, + valueCallback: (grid: GridView, column: DataColumn) => { + return getFooterAvgSummary(grid, column); + }, +}); + +// 구분 값에 따라 필터링하는 Footer 배열 생성 함수 (2줄). 기본 필드명 'displayYear' +const createSumFooterArrayByDivision = (format: string, divisionFieldName: string = 'displayYear') => [ + { + numberFormat: format, + valueCallback: ( + grid: GridView, + column: DataColumn, + footerIndex: number, + columnFooter: unknown, + value: number | string | undefined + ) => getFooterSummaryByDivision(grid, column, footerIndex, divisionFieldName), + }, + { + numberFormat: format, + valueCallback: ( + grid: GridView, + column: DataColumn, + footerIndex: number, + columnFooter: unknown, + value: number | string | undefined + ) => getFooterSummaryByDivision(grid, column, footerIndex, divisionFieldName), + }, +]; + +// 구분 값에 따라 필터링하는 평균 Footer 배열 생성 함수 (2줄). 기본 필드명 'displayYear' +const createAvgFooterArrayByDivision = (format: string, divisionFieldName: string = 'displayYear') => [ + { + numberFormat: format, + valueCallback: ( + grid: GridView, + column: DataColumn, + footerIndex: number, + columnFooter: unknown, + value: number | string | undefined + ) => getFooterAvgSummaryByDivision(grid, column, footerIndex, divisionFieldName), + }, + { + numberFormat: format, + valueCallback: ( + grid: GridView, + column: DataColumn, + footerIndex: number, + columnFooter: unknown, + value: number | string | undefined + ) => getFooterAvgSummaryByDivision(grid, column, footerIndex, divisionFieldName), + }, +]; + +// 1. 정수형 : #,##0 +export const footerInt = createSumFooter('#,##0'); + +// 2. 소수점 1자리 : #,##0.0 +export const footerDecimal1 = createSumFooter('#,##0.0'); + +// 3. 소수점 2자리 : #,##0.00 +export const footerDecimal2 = createSumFooter('#,##0.00'); + +// 4. 소수점 3자리 : #,##0.000 +export const footerDecimal3 = createSumFooter('#,##0.000'); + +// "구분" 값에 따라 필터링하는 Footer 배열 (정수형, 2줄) +export const footerIntArrayByDivision = createSumFooterArrayByDivision('#,##0'); + +// "구분" 값에 따라 필터링하는 Footer 배열 (소수점 1자리, 2줄) +export const footerDecimal1ArrayByDivision = createSumFooterArrayByDivision('#,##0.0'); + +// "구분" 값에 따라 필터링하는 Footer 배열 (소수점 2자리, 2줄) +export const footerDecimal2ArrayByDivision = createSumFooterArrayByDivision('#,##0.00'); + +// "구분" 값에 따라 필터링하는 Footer 배열 (소수점 3자리, 2줄) +export const footerDecimal3ArrayByDivision = createSumFooterArrayByDivision('#,##0.000'); + +// 률(%) 평균 값(기본 소수점 1자리) +export const footerAvgDecimal = createAvgFooter('#,##0.0'); + +// "구분" 값에 따라 필터링하는 평균 Footer 배열 (소수점 1자리, 2줄) +export const footerAvgDecimalArrayByDivision = createAvgFooterArrayByDivision('#,##0.0'); \ No newline at end of file diff --git a/apps/st/src/components/util/grid/gridFunction.ts b/apps/st/src/components/util/grid/gridFunction.ts new file mode 100644 index 000000000..28755a7f8 --- /dev/null +++ b/apps/st/src/components/util/grid/gridFunction.ts @@ -0,0 +1,16 @@ +/** + * 객체 배열을 그리드 row 형식으로 변환 (null/undefined → ""). + * 어떤 엔티티 타입이든 공통 사용 가능. + */ +export function toGridRows( + items: T[] +): Record[] { + return items.map((item) => { + const row: Record = {}; + for (const [key, value] of Object.entries(item)) { + row[key] = + value === null || value === undefined ? "" : (value as string | number); + } + return row; + }); +}; \ No newline at end of file diff --git a/apps/st/src/entities/code/@x/posting.ts b/apps/st/src/entities/code/@x/posting.ts new file mode 100644 index 000000000..0525684d9 --- /dev/null +++ b/apps/st/src/entities/code/@x/posting.ts @@ -0,0 +1 @@ +export { CodeSelect } from "../ui/code-select"; diff --git a/apps/st/src/entities/code/api/code-service.ts b/apps/st/src/entities/code/api/code-service.ts new file mode 100644 index 000000000..0d1055f6a --- /dev/null +++ b/apps/st/src/entities/code/api/code-service.ts @@ -0,0 +1,14 @@ +import { client } from "@/shared/api"; +import { ENDPOINT } from "@/shared/config"; +import type { CodeService, ICode } from "../model/code-interface"; + +export class CodeApiService implements CodeService { + public async getCode(upCd: string): Promise { + const response = await client.get( + `${ENDPOINT.CMS_SERVICE.CODE}/${upCd}`, + ); + return response.data; + } +} + +export const CodeApi = new CodeApiService(); diff --git a/apps/st/src/entities/code/api/lwpocode-service.ts b/apps/st/src/entities/code/api/lwpocode-service.ts new file mode 100644 index 000000000..818ddb120 --- /dev/null +++ b/apps/st/src/entities/code/api/lwpocode-service.ts @@ -0,0 +1,73 @@ +import { client } from "@/shared/api"; +import { ENDPOINT } from "@/shared/config"; +import { + LwpoCodeFormSchema, + type LwpoCodeService, + type LwpoCodeList, + type LwpoCodeDetail, + type LwpoCodeParams, + type LwpoCodeFormData, +} from "../model/lwpocode-interface"; + +export class LwpoCodeApiService implements LwpoCodeService { + public async getLwpoCodeList(params?: LwpoCodeParams): Promise { + const response = await client.get( + `${ENDPOINT.CMS_SERVICE.LWPO_CODE}`, + { params }, + ); + return response.data; + } + + public async getUpCodeLwpoCodeList(upCd: string): Promise { + const response = await client.get( + `${ENDPOINT.CMS_SERVICE.LWPO_CODE}/up-cd/${upCd}`, + ); + return response.data; + } + + public async lwpoCodeDetail( + upCd: string, + lwpoCd: string, + ): Promise { + const response = await client.get( + `${ENDPOINT.CMS_SERVICE.LWPO_CODE}/${lwpoCd}/up-cd/${upCd}`, + ); + return response.data; + } + + public async createLwpoCode(data: LwpoCodeFormData): Promise { + const validatedData = LwpoCodeFormSchema.parse(data); + const response = await client.post( + `${ENDPOINT.CMS_SERVICE.LWPO_CODE}`, + validatedData, + ); + return response.data; + } + + public async updateLwpoCode( + upCd: string, + lwpoCd: string, + data: LwpoCodeFormData, + ): Promise { + const validatedData = LwpoCodeFormSchema.parse(data); + const response = await client.put( + `${ENDPOINT.CMS_SERVICE.LWPO_CODE}/${lwpoCd}/up-cd/${upCd}`, + validatedData, + ); + return response.data; + } + + public async deleteLwpoCode(upCd: string, lwpoCd: string): Promise { + await client.delete( + `${ENDPOINT.CMS_SERVICE.LWPO_CODE}/${lwpoCd}/up-cd/${upCd}`, + ); + } + + public async codeRefreshAll(): Promise { + await client.post( + `${ENDPOINT.CMS_SERVICE.LWPO_CODE}/cache/refresh-all`, + ); + } +} + +export const LwpoCodeApi = new LwpoCodeApiService(); diff --git a/apps/st/src/entities/code/api/upcode-service.ts b/apps/st/src/entities/code/api/upcode-service.ts new file mode 100644 index 000000000..dcccd9534 --- /dev/null +++ b/apps/st/src/entities/code/api/upcode-service.ts @@ -0,0 +1,47 @@ +import { client } from "@/shared/api"; +import { ENDPOINT } from "@/shared/config"; +import { + upCodeFormSchema, + type UpCodeService, + type UpCodeList, + type UpCodeDetail, + type UpCodeParams, + type UpCodeFormData, +} from "../model/upcode-interface"; + +export class UpCodeApiService implements UpCodeService { + public async getUpCodeList(params?: UpCodeParams): Promise { + const response = await client.get( + `${ENDPOINT.CMS_SERVICE.UP_CODE}`, + { params }, + ); + return response.data; + } + + public async upCodeDetail(upCd: string): Promise { + const response = await client.get( + `${ENDPOINT.CMS_SERVICE.UP_CODE}/${upCd}`, + ); + return response.data; + } + + public async createUpCode(data: UpCodeFormData): Promise { + const validatedData = upCodeFormSchema.parse(data); + const response = await client.post( + `${ENDPOINT.CMS_SERVICE.UP_CODE}`, + validatedData, + ); + return response.data; + } + + public async updateUpCode(upCd: string, data: UpCodeFormData): Promise { + const validatedData = upCodeFormSchema.parse(data); + const response = await client.put( + `${ENDPOINT.CMS_SERVICE.UP_CODE}/${upCd}`, + validatedData, + ); + return response.data; + } +} + +export const UpCodeApi = new UpCodeApiService(); diff --git a/apps/st/src/entities/code/index.ts b/apps/st/src/entities/code/index.ts new file mode 100644 index 000000000..e166e2c29 --- /dev/null +++ b/apps/st/src/entities/code/index.ts @@ -0,0 +1,19 @@ +export { CodeApi } from "./api/code-service"; +export type { ICode } from "./model/code-interface"; +export { CodeSelect } from "./ui/code-select"; + +export { UpCodeApi } from "./api/upcode-service"; +export { + upCodeFormSchema, + type UpCode, + type UpCodeFormData, + type UpCodeParams, +} from "./model/upcode-interface"; + +export { LwpoCodeApi } from "./api/lwpocode-service"; +export { + LwpoCodeFormSchema, + type LwpoCode, + type LwpoCodeFormData, + type LwpoCodeParams, +} from "./model/lwpocode-interface"; diff --git a/apps/st/src/entities/code/model/code-interface.ts b/apps/st/src/entities/code/model/code-interface.ts new file mode 100644 index 000000000..4562e2559 --- /dev/null +++ b/apps/st/src/entities/code/model/code-interface.ts @@ -0,0 +1,10 @@ +export interface ICode { + lwpoCd: string; + upCd: string; + lwpoCdNm: string; + unit: string; +} + +export interface CodeService { + getCode(upCd: string): Promise; +} diff --git a/apps/st/src/entities/code/model/lwpocode-interface.ts b/apps/st/src/entities/code/model/lwpocode-interface.ts new file mode 100644 index 000000000..2538c1c30 --- /dev/null +++ b/apps/st/src/entities/code/model/lwpocode-interface.ts @@ -0,0 +1,69 @@ +import { z } from "zod"; +import type { PageableParams, PageableList } from "@/shared/model"; + +export interface LwpoCodeParams extends PageableParams { + upCd?: string; + lwpoCd?: string; + lwpoCdNm?: string; + useYn?: string; +} + +export interface LwpoCode { + lwpoCd: string; + upCd: string; + lwpoCdNm: string; + lwpoCdExpln: string; + unit: string; + prntLwpoCd: string; + useYn: string; + sortSeq: number; + regDt: string; + rgTr: string; + rgTrNm: string; + mdfcnDt: string; + mdFr: string; +} + +export interface LwpoCodeDetail extends LwpoCodeFormData { + lwpoCd: string; + upCd: string; + lwpoCdNm: string; + lwpoCdExpln: string; + unit: string; + prntLwpoCd: string; + useYn: string; + sortSeq: 0; +} + +export const LwpoCodeFormSchema = z.object({ + lwpoCd: z.string().max(10).nonempty(), + upCd: z.string().max(10).nonempty(), + lwpoCdNm: z.string().max(100).nonempty(), + lwpoCdExpln: z.string().max(1000).nullish(), + unit: z.string().max(20).nullish(), + prntLwpoCd: z.string().max(10).nullish(), + useYn: z.string(), + sortSeq: z.number(), + delYn: z.string(), +}); + +export type LwpoCodeFormData = z.infer; +export type LwpoCodeList = PageableList; + +export interface LwpoCodeService { + getLwpoCodeList(params: LwpoCodeParams): Promise; + getUpCodeLwpoCodeList(upCd: string): Promise; + lwpoCodeDetail(upCd: string, lwpoCd: string): Promise; + updateLwpoCode( + upCd: string, + lwpoCd: string, + data: LwpoCodeFormData, + ): Promise; + createLwpoCode(data: LwpoCodeFormData): Promise; + deleteLwpoCode( + upCd: string, + lwpoCd: string, + data: LwpoCodeFormData, + ): Promise; + codeRefreshAll(): Promise; +} diff --git a/apps/st/src/entities/code/model/upcode-interface.ts b/apps/st/src/entities/code/model/upcode-interface.ts new file mode 100644 index 000000000..73bf4f3ae --- /dev/null +++ b/apps/st/src/entities/code/model/upcode-interface.ts @@ -0,0 +1,52 @@ +import { z } from "zod"; +import type { PageableParams, PageableList } from "@/shared/model"; + +export interface UpCodeParams extends PageableParams { + keyword?: string; + upCd?: string; + cdSe?: string; + useYn?: string; + siteId?: string; +} + +export interface UpCode { + upCd: string; + upCdNm: string; + upCdExpln: string; + cdSe: string; + siteId: string; + useYn: string; + regDt: string; +} + +export interface UpCodeDetail extends UpCodeFormData { + upCd: string; + upCdNm: string; + upCdExpln: string; + cdSe: string; + siteId: string; + useYn: string; + delYn: string; + regDt: string; + rgTrNm: string; +} + +export const upCodeFormSchema = z.object({ + upCd: z.string().max(10).nonempty(), + upCdNm: z.string().max(100).nonempty(), + upCdExpln: z.string().max(1000).nullish(), + cdSe: z.string().max(10).nonempty(), + siteId: z.string().max(2).nullish(), + useYn: z.string(), + delYn: z.string(), +}); + +export type UpCodeFormData = z.infer; +export type UpCodeList = PageableList; + +export interface UpCodeService { + getUpCodeList(params: UpCodeParams): Promise; + upCodeDetail(upCodeId: string): Promise; + updateUpCode(upCodeId: string, data: UpCodeFormData): Promise; + createUpCode(data: UpCodeFormData): Promise; +} diff --git a/apps/st/src/entities/code/ui/code-select.tsx b/apps/st/src/entities/code/ui/code-select.tsx new file mode 100644 index 000000000..08df4ce51 --- /dev/null +++ b/apps/st/src/entities/code/ui/code-select.tsx @@ -0,0 +1,47 @@ +import { useQuery } from "@tanstack/react-query"; +import { + cn, + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@common/components"; +import { ENDPOINT } from "@/shared/config"; +import { CodeApi } from "../api/code-service"; + +type CodeSelectProps = { + upCd: string; + placeholder?: string; + className?: string; +} & React.ComponentPropsWithoutRef; + +function CodeSelect({ + upCd, + placeholder = "선택", + className, + ...props +}: CodeSelectProps) { + const { data: code } = useQuery({ + queryKey: [ENDPOINT.CMS_SERVICE.CODE, upCd], + queryFn: () => CodeApi.getCode(upCd), + enabled: !!upCd, + }); + + return ( + + ); +} + +export { CodeSelect }; diff --git a/apps/st/src/entities/statistics/api/statistics-input-service.ts b/apps/st/src/entities/statistics/api/statistics-input-service.ts new file mode 100644 index 000000000..32cae5723 --- /dev/null +++ b/apps/st/src/entities/statistics/api/statistics-input-service.ts @@ -0,0 +1,32 @@ +import axios from "axios"; +import { ENDPOINT } from "@/shared/config"; + +import { + type StatisticsInputService, + type SewerageSupplyRateRequestParams, + type SewerageSupplyRateApiResponse, + type CheckInputPeriodResponse, +} from "../model/statistics-input-interface"; + +export class StatisticsInputAPiService implements StatisticsInputService { + public async getSewerageSupplyRateList(params: SewerageSupplyRateRequestParams): Promise { + const response = await axios.get ( + `${ENDPOINT.ST_SERVICE.INPUT}/sewerage-supply-rate`, + { + params: params + } + ); + + return response.data; + } + + public async checkInputPeriod(params: string): Promise { + const response = await axios.get ( + `${ENDPOINT.ST_SERVICE.INPUTCOMMON}/check-input-period?year=${params}`, + ); + + return response.data; + } +} + +export const statisticsInputAPi = new StatisticsInputAPiService(); \ No newline at end of file diff --git a/apps/st/src/entities/statistics/api/statistics-management-service.ts b/apps/st/src/entities/statistics/api/statistics-management-service.ts new file mode 100644 index 000000000..b566d19c3 --- /dev/null +++ b/apps/st/src/entities/statistics/api/statistics-management-service.ts @@ -0,0 +1,78 @@ +import axios from "axios"; +import { ENDPOINT } from "@/shared/config"; + + +import { + type StatsInputStatusReviewApiResponse +} from "../model/statistics-input-review-interface"; +import { + type SewagePipeReviewApiResponse +} from "../model/statistics-sewage-pipe-review-interface"; +import { + type SewerageSupplyRateReviewRequestParams, + type SewerageSupplyRateReviewApiResponse, +} from "../model/statistics-sewerage-penetration-review-interface"; + +export class StatisticsManagementApiService { + /** + * 00. 통계 입력 현황 목록 조회 + * @param params (yr, bsnmCd, sido, gugun) + */ + public async getStatsInputStatusList( + params: { yr?: string; bsnmCd?: string; sido?: string; gugun?: string } + ): Promise { + const response = await axios.get( + `${ENDPOINT.ST_SERVICE.REVIEW}/input-status`, + { params } + ); + return response.data; + } + + /** + * 입력 기간 확인 (공통) + * @param year 연도 + */ + public async checkInputPeriod(year: string | number): Promise { + const response = await axios.get( + `${ENDPOINT.ST_SERVICE.INPUTCOMMON}/check-input-period`, + { params: { year } } + ); + return response.data; + } + + /** + * 01. 하수도 보급률 검토 목록 조회 + * @param params (sido, gugun, yr, bsnmCd) + */ + public async getSewerageSupplyRateReviewList( + params: SewerageSupplyRateReviewRequestParams + ): Promise { + const response = await axios.get( + `${ENDPOINT.ST_SERVICE.REVIEW}/supply-rate`, + { params } + ); + return response.data; + } + + /** + * 02. 하수관로 지역별 시설현황 목록 조회 (Tab1) + * @param params (sido, gugun, yr, bsnmCd) + */ + public async getSewagePipeReviewList( + params: { + yr?: string; + bsnmCd?: string; + sido?: string; + gugun?: string; + } + ): Promise { + const response = await axios.get( + `${ENDPOINT.ST_SERVICE.REVIEW}/pipe/tab1`, + { params } + ); + return response.data; + } +} + +// 통합 인스턴스 수출 +export const statisticsManagementApi = new StatisticsManagementApiService(); \ No newline at end of file diff --git a/apps/st/src/entities/statistics/api/statistics-service.ts b/apps/st/src/entities/statistics/api/statistics-service.ts index dfec7d694..8455e68c5 100644 --- a/apps/st/src/entities/statistics/api/statistics-service.ts +++ b/apps/st/src/entities/statistics/api/statistics-service.ts @@ -3,16 +3,16 @@ import { ENDPOINT } from "@/shared/config"; import { type StatisticsService, - type Sido, + type SearchCondition, type ApiResponse, type Sgg } from "../model/statistics-interface"; export class StatisticsAPiService implements StatisticsService { - public async getSidoList(): Promise> { - const response = await axios.get>( - `${ENDPOINT.ST_SERVICE.COMMON}/sido`, + public async getSidoList(): Promise> { + const response = await axios.get>( + `${ENDPOINT.ST_SERVICE.COMMON}/search-condition`, ); return response.data; diff --git a/apps/st/src/entities/statistics/model/statistics-input-interface.ts b/apps/st/src/entities/statistics/model/statistics-input-interface.ts new file mode 100644 index 000000000..6a76d567e --- /dev/null +++ b/apps/st/src/entities/statistics/model/statistics-input-interface.ts @@ -0,0 +1,125 @@ +import type { ApiResponse } from "./statistics-interface"; + +// 하수도 보급률 데이터 항목 +export interface SewerageSupplyRateStatusInfo { + locgStatus: string; + locgDt: string; + locgCharger: string; + locgCttpc: string; + prvnStatus: string; + dcsnStatus: string; + wrterOpinionSm: string | null; + chckerOpinionSm: string | null; + returnOpinionSm: string | null; +} + +export interface SewerageSupplyRateItem { + sido: string; + gugun: string; + adstDong: string; + rgnNm: string; + adstCd: string; + yr: string; + bsnmCd: string; + rvbsn: string; + lgbsn: string; + mdbsn: string; + mdmbsn: string; + totalPopl: number; + poplSm: number; + frgnrPoplSm: number; + area: number; + bsnmTotalPopl: number; + bsnmPoplSm: number; + bsnmFrgnrPoplSm: number; + bsnmArea: number; + piplPlanExtn: number; + piplFcltyExtn: number; + piplSupprt: number; + wrssm: string | null; + bstrm: string; + speclCntrplnAr: string | null; + popltnSm: number; + arSm: number; + swgeProcZoneInnerSm: number; + pblswrgPopltnSm: number; + pblswrgPrmTrt: number; + pblswrgScdTrt: number; + pblswrgTertTrt: number; + wstrPopltnSm: number; + wstrPrmTrt: number; + wstrScdTrt: number; + wstrTertTrt: number; + swgeProcZoneInnerAr: number; + swgeProcZoneOuterSm: number; + townUnprocPopltn: number; + townSwttPopltn: number; + townWrrtnPopltn: number; + uptownUnprPopltn: number; + uptownSwttPopltn: number; + uptownWrrtnPopltn: number; + swgeProcZoneOuterAr: number; + pblswrgPopltnSuprt: number; + adtrtPopltnSuprt: number; + swrgInstlrt: number; + wrterOpinion: string | null; + chckerOpinion: string | null; + notConectPopltn: number; + rmk: string; + chkWrssm: string; + chkBstrm: string; + chkSpeclCntrplnAr: string; + chkPopltnSm: string; + chkArSm: string; + chkSwgeProcZoneInnerSm: string; + chkPblswrgPopltnSm: string; + chkPblswrgPrmTrt: string; + chkPblswrgScdTrt: string; + chkPblswrgTertTrt: string; + chkWstrPopltnSm: string; + chkWstrPrmTrt: string; + chkWstrScdTrt: string; + chkWstrTertTrt: string; + chkSwgeProcZoneInnerAr: string; + chkSwgeProcZoneOuterSm: string; + chkTownUnprocPopltn: string; + chkTownSwttPopltn: string; + chkTownWrrtnPopltn: string; + chkUptownUnprPopltn: string; + chkUptownSwttPopltn: string; + chkUptownWrrtnPopltn: string; + chkSwgeProcZoneOuterAr: string; + chkPblswrgPopltnSuprt: string; + chkAdtrtPopltnSuprt: string; + chkSwrgInstlrt: string; + chkNotConectPopltn: string; +} + +export interface SewerageSupplyRateResponseData { + statusInfo: SewerageSupplyRateStatusInfo; + sewerageSupplyRateList: SewerageSupplyRateItem[]; +} + +export type SewerageSupplyRateApiResponse = ApiResponse & { + message: string; +} + +export interface SewerageSupplyRateRequestParams { + sido?: string; + gugun?: string; + bsnmCd?: string; + yr?: string; + demoYr?: string; + mode?: string; +} + +// 하수도 통계 입력 기간 조회 +export interface CheckInputPeriodResponse { + status: string; + message: string; + data: boolean; +} + +export interface StatisticsInputService { + getSewerageSupplyRateList(params: SewerageSupplyRateRequestParams): Promise; +} \ No newline at end of file diff --git a/apps/st/src/entities/statistics/model/statistics-input-review-interface.ts b/apps/st/src/entities/statistics/model/statistics-input-review-interface.ts new file mode 100644 index 000000000..93f1a4651 --- /dev/null +++ b/apps/st/src/entities/statistics/model/statistics-input-review-interface.ts @@ -0,0 +1,112 @@ +import type { ApiResponse } from "./statistics-interface"; + +/** + * 검토-00.통계 입력현황 아이템 인터페이스 + */ +export interface StatsInputStatusReviewItem { + chk: string; + sido: string; + gugun: string; + yr: string; + bsnmCd: string; + status: string; + + // 01. 하수도보급율 (supply) + supplyCharger: string; + supplyCttpc: string; + supplyStatusNm: string; + supplyStatus: string; + supplyCnt: number; + + // 02. 하수관로 (facility - 기존 3번) + facilityCharger: string; + facilityCttpc: string; + facilityStatusNm: string; + facilityStatus: string; + facilityCnt: number; + + // 03. 하수관로유지관리 (pipe - 기존 4번) + pipeCharger: string; + pipeCttpc: string; + pipeStatusNm: string; + pipeCnt: number; + + // 04. 하수저류시설 (storage - 기존 5번) + storageCharger: string; + storageCttpc: string; + storageStatusNm: string; + storageCnt: number; + + // 05. 펌프장 (pump - 기존 6번) + pumpCharger: string; + pumpCttpc: string; + pumpStatusNm: string; + pumpCnt: number; + + // 06. 공공하수처리시설 (treat - 기존 7번) + treatCharger: string; + treatCttpc: string; + treatStatusNm: string; + treatCnt: number; + + // 07. 분뇨처리시설 (small - 기존 8번) + smallCharger: string; + smallCttpc: string; + smallStatusNm: string; + smallCnt: number; + + // 08. 개인하수처리시설 (private - 기존 9번) + privateCharger: string; + privateCttpc: string; + privateStatusNm: string; + privateCnt: number; + + // 09. 하수도재정 (finance - 기존 10번) + financeCharger: string; + financeCttpc: string; + financeStatusNm: string; + financeCnt: number; + + // 10. 물재이용 (reuse - 기존 11번) + reuseCharger: string; + reuseCttpc: string; + reuseStatusNm: string; + reuseCnt: number; + + // 11. 하수및분뇨찌꺼기 (sludge - 기존 12번) + sludgeCharger: string; + sludgeCttpc: string; + sludgeStatusNm: string; + sludgeCnt: number; + + // 12. 하수및분뇨관련산업 (excreta - 기존 13번) + excretaCharger: string; + excretaCttpc: string; + excretaStatusNm: string; + excretaCnt: number; + + // 13. 하수도서비스수준 (complaint - 기존 15번) + complaintCharger: string; + complaintCttpc: string; + complaintStatusNm: string; + complaintCnt: number; +} + +export interface StatsInputStatusReviewResponseData { + sewerageSupplyRateList: StatsInputStatusReviewItem[]; +} + + +export type StatsInputStatusReviewApiResponse = ApiResponse & { + message: string; +}; + + +export interface StatsInputStatusReviewService { + getStatsInputStatusList(params: { + yr?: String; + bsnmCd?: string; + sido?: string; + gugun?: string; + }): Promise; +} \ No newline at end of file diff --git a/apps/st/src/entities/statistics/model/statistics-interface.ts b/apps/st/src/entities/statistics/model/statistics-interface.ts index d687df5c2..b909eba92 100644 --- a/apps/st/src/entities/statistics/model/statistics-interface.ts +++ b/apps/st/src/entities/statistics/model/statistics-interface.ts @@ -1,19 +1,27 @@ export interface Sido { adstDstrCd: string; sido: string; + bsnmCd: string; +} + +export interface SearchCondition { + yearList: string[]; + sidoList: Sido[]; } export interface Sgg { adstDstrCd: string; gugun: string; + bsnmCd: string; } export interface ApiResponse { status: string; data: T; + message?: string; } export interface StatisticsService { - getSidoList(): Promise>; + getSidoList(): Promise>; getSggList(sidoName: string): Promise>; } \ No newline at end of file diff --git a/apps/st/src/entities/statistics/model/statistics-sewage-pipe-review-interface.ts b/apps/st/src/entities/statistics/model/statistics-sewage-pipe-review-interface.ts new file mode 100644 index 000000000..581d31ba1 --- /dev/null +++ b/apps/st/src/entities/statistics/model/statistics-sewage-pipe-review-interface.ts @@ -0,0 +1,83 @@ +import type { ApiResponse } from "./statistics-interface"; + +/** + * 02. 하수관로 지역별 시설현황 아이템 인터페이스 (Tab1) + */ +export interface SewagePipeReviewItem { + // 기본 정보 및 행 제어 + sido: string; // 시도 + gugun: string; // 시군구 + adstDong: string; // 행정구역명 + adstCd: string; // 행정코드 + yr: string; // 연도 + rowType: 'OLD' | 'CURR'; // 전년도/당해년도 구분 + displayYear: string; // 구분 표시용 (2024년확정 등) + + // 하수정비 기본계획 + targetYr: number; // 하수정보기본계획목표년도 + piplImprmnPlanExtn: number; // 총관로정비 계획연장(m) + piplPlanExtn: number; // 총계획연장(m) + piplFcltyExtn: number; // 총시설연장(m) + piplSupprt: number; // 하수관로 보급률(%) + + // 합류식 (CMBN) + cmbnPlanAr: number; // 관로정비 계획면적(㎢) + cmbnImprmnPlanExtn: number; // 관로정비 계획연장(m) + cmbnPlanExtn: number; // 계획연장(m) + cmbnFcltyExtn: number; // 시설연장(m) + cmbnRctglExtn: number; // 사각형(m) + cmbnCicleExtn: number; // 원형(m) + cmbnOpswrExtn: number; // 개거(m) + cmbnSdgttrExtn: number; // 측구(m) + + // 분류식 - 오수관로 (PLTWTR) + pltwtrPlanAr: number; // 관로정비 계획면적(㎢) + pltwtrImprmnPlanExtn: number; // 관로정비 계획연장(m) + pltwtrPlanExtn: number; // 계획연장(m) + pltwtrFlctyExtn: number; // 시설연장(m) + pltwtrSqreExtn: number; // 사각형(m) + pltwtrCiclExtn: number; // 원형(m) + + // 분류식 - 우수관로 (RAINWTR) + rainwtrPlanAr: number; // 관로정비 계획면적(㎢) + rainwtrImprmnPlanExtn: number; // 관로정비 계획연장(m) + rainwtrPlanExtn: number; // 계획연장(m) + rainwtrFlctyExtn: number; // 시설연장(m) + rainwtrSqreExtn: number; // 사각형(m) + rainwtrCiclExtn: number; // 원형(m) + rainwtrOpswrExtn: number; // 개거(m) + rainwtrSdgttrExtn: number; // 측구(m) + + // 부속시설 (개소) + mnhleSite: number; // 합류식맨홀 + pltwtrMnhleSite: number; // 오수맨홀 + rainwtrMnhleSite: number; // 우수맨홀 + pltwtrRcvrSite: number; // 오수받이 + rainwtrRcvrSite: number; // 빗물받이 + soilspllSite: number; // 우수토실 + soilholeSite: number; // 우수토구 + + // 대장 및 기타 + sewerRegstrCmptExtn: number; // 하수도대장 전산화연장(m) + sewerRegstrCmptRt: number; // 하수도대장 전산화율(%) + rmk: string; // 비고 +} + +export interface SewagePipeReviewResponseData { + sewagePipeList: SewagePipeReviewItem[]; +} + + +export type SewagePipeReviewApiResponse = ApiResponse & { + message: string; +}; + +export interface SewagePipeReviewService { + + getSewagePipeReviewList(params: { + yr?: string; // 조회 연도 + bsnmCd?: string; // 사업장 코드 + sido?: string; // 시도 + gugun?: string; // 구군 + }): Promise; +} \ No newline at end of file diff --git a/apps/st/src/entities/statistics/model/statistics-sewerage-penetration-review-interface.ts b/apps/st/src/entities/statistics/model/statistics-sewerage-penetration-review-interface.ts new file mode 100644 index 000000000..8fbf7ea1f --- /dev/null +++ b/apps/st/src/entities/statistics/model/statistics-sewerage-penetration-review-interface.ts @@ -0,0 +1,88 @@ +import type { ApiResponse } from "./statistics-interface"; + +// 검토 01.하수도 보급률 데이터 항목 +export interface SewerageSupplyRateReviewItem { + + sido: string; + gugun: string; + adstDong: string; + displayYear: string; + rowType: 'OLD' | 'CURR'; + adstCd: string; + bsnmCd: string; + yr: number; + + /* ====================================================================== + * 수계 및 지역 정보 + * ====================================================================== */ + rvbsn: string; + lgbsn: string; + mdbsn: string; + mdmbsn: string; + wrssm: string | null; + bstrm: string; + speclCntrplnAr: string | null; + + /* ====================================================================== + * 인구 및 면적 데이터 (공통 필드명) + * ====================================================================== */ + popltnSm: number; // 총인구 + arSm: number; // 총면적(㎢) + + /* 하수처리구역내 (groupD) */ + swgeProcZoneInnerSm: number; // 계 + pblswrgPopltnSm: number; // 공공하수처리인구-합계 + pblswrgPrmTrt: number; // 1차처리 + pblswrgScdTrt: number; // 2차처리 + pblswrgTertTrt: number; // 3차처리 + + wstrPopltnSm: number; // 폐수처리인구-합계 + wstrPrmTrt: number; // 1차처리 + wstrScdTrt: number; // 2차처리 + wstrTertTrt: number; // 3차처리 + + notConectPopltn: number; // 미접속인구 + swgeProcZoneInnerAr: number; // 면적(㎢) + + /* 하수처리구역외 (groupE) */ + swgeProcZoneOuterSm: number; // 계 + townUnprocPopltn: number; // 미처리인구 (Town + Uptown 합산값) + townSwttPopltn: number; // 오수처리인구 (Town + Uptown 합산값) + townWrrtnPopltn: number; // 정화조인구 (Town + Uptown 합산값) + swgeProcZoneOuterAr: number; // 면적(㎢) + + /* ====================================================================== + * 보급률 및 기타 + * ====================================================================== */ + pblswrgPopltnSuprt: number; // 공공하수보급률(%) + adtrtPopltnSuprt: number; // 고도처리보급률(%) + swrgInstlrt: number; // 하수도설치율(%) + rmk: string | null; // 비고 + + /* ====================================================================== + * Flag (필요 시 유지) + * ====================================================================== */ + lcnt: number | null; + ocnt: number | null; + +} + +export interface SewerageSupplyRateReviewResponseData { + sewerageSupplyRateList: SewerageSupplyRateReviewItem[]; +} + +export type SewerageSupplyRateReviewApiResponse = ApiResponse & { + message: string; +}; + + +export interface SewerageSupplyRateReviewRequestParams { + sido?: string; + gugun?: string; + yr?: string; + bsnmCd?: string; +} + +export interface SewerageSupplyRateReviewService { + getSewerageSupplyRateReviewList(params: SewerageSupplyRateReviewRequestParams): Promise; +} \ No newline at end of file diff --git a/apps/st/src/entities/statistics/model/verification-history-column.ts b/apps/st/src/entities/statistics/model/verification-history-column.ts new file mode 100644 index 000000000..aea9b7f4f --- /dev/null +++ b/apps/st/src/entities/statistics/model/verification-history-column.ts @@ -0,0 +1,120 @@ +import { ValueType } from "realgrid"; +import type { ColumnInfo } from "@rsone/components"; + +/** 검증 이력 그리드 컬럼 설정 */ +export const verificationHistoryColumnInfo: ColumnInfo = { + columns: [ + { + name: "executedAt", + fieldName: "executedAt", + header: { text: "검증 실행 일시" }, + editable: false, + sortdisable: false, + }, + { + name: "progressStatus", + fieldName: "progressStatus", + header: { text: "진행 상태" }, + editable: false, + sortdisable: false, + }, + { + name: "endedAt", + fieldName: "endedAt", + header: { text: "종료 일시" }, + editable: false, + sortdisable: false, + }, + { + name: "verificationYear", + fieldName: "verificationYear", + header: { text: "검증년도" }, + editable: false, + sortdisable: false, + }, + { + name: "mu", + fieldName: "mu", + header: { text: "μ" }, + editable: false, + sortdisable: false, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.###", + }, + { + name: "percentile", + fieldName: "percentile", + header: { text: "Percentile" }, + editable: false, + sortdisable: false, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.###", + }, + { + name: "a", + fieldName: "a", + header: { text: "A" }, + editable: false, + sortdisable: false, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.###", + }, + { + name: "c", + fieldName: "c", + header: { text: "C" }, + editable: false, + sortdisable: false, + valueType: ValueType.NUMBER, + dataFormat: "#,##0.###", + }, + { + name: "target", + fieldName: "target", + header: { text: "검증 대상" }, + editable: false, + sortdisable: false, + }, + { + name: "normalCount", + fieldName: "normalCount", + header: { text: "정상건수" }, + editable: false, + sortdisable: false, + valueType: ValueType.NUMBER, + dataFormat: "#,##0", + }, + { + name: "excludedCount", + fieldName: "excludedCount", + header: { text: "제외건수" }, + editable: false, + sortdisable: false, + valueType: ValueType.NUMBER, + dataFormat: "#,##0", + }, + { + name: "abnormalCount", + fieldName: "abnormalCount", + header: { text: "이상건수" }, + editable: false, + sortdisable: false, + valueType: ValueType.NUMBER, + dataFormat: "#,##0", + }, + ], + columnLayout: [ + "executedAt", + "progressStatus", + "endedAt", + "verificationYear", + "mu", + "percentile", + "a", + "c", + "target", + "normalCount", + "excludedCount", + "abnormalCount", + ], +}; diff --git a/apps/st/src/entities/statistics/model/verification-history-interface.ts b/apps/st/src/entities/statistics/model/verification-history-interface.ts new file mode 100644 index 000000000..869399cba --- /dev/null +++ b/apps/st/src/entities/statistics/model/verification-history-interface.ts @@ -0,0 +1,27 @@ +/** 검증 이력 한 건 (그리드 행 데이터) */ +export interface VerificationHistoryItem { + /** 검증 실행 일시 */ + executedAt: string; + /** 진행 상태 */ + progressStatus: string; + /** 종료 일시 */ + endedAt: string; + /** 검증년도 */ + verificationYear: string; + /** μ */ + mu: number; + /** Percentile */ + percentile: number; + /** A */ + a: number; + /** C */ + c: number; + /** 검증 대상 */ + target: string; + /** 정상건수 */ + normalCount: number; + /** 제외건수 */ + excludedCount: number; + /** 이상건수 */ + abnormalCount: number; +} diff --git a/apps/st/src/entities/statistics/model/verification-history-mock.ts b/apps/st/src/entities/statistics/model/verification-history-mock.ts new file mode 100644 index 000000000..90c152be7 --- /dev/null +++ b/apps/st/src/entities/statistics/model/verification-history-mock.ts @@ -0,0 +1,67 @@ +import { fakerKO as faker } from "@faker-js/faker"; +import type { VerificationHistoryItem } from "./verification-history-interface"; + +const VERIFICATION_TARGETS = [ + "하수도 보급률", + "하수관로", + "하수관로 유지관리", + "하수저류시설 및 빗물펌프장", + "펌프장", + "공공하수처리시설", + "분뇨처리시설", + "개인하수처리시설", + "하수도재정", + "물 재이용", + "하수도 서비스 수준", +]; + +const PROGRESS_STATUSES = ["대기", "진행중", "완료", "실패"]; + +function formatDateTime(date: Date): string { + const y = date.getFullYear(); + const m = String(date.getMonth() + 1).padStart(2, "0"); + const d = String(date.getDate()).padStart(2, "0"); + const h = String(date.getHours()).padStart(2, "0"); + const min = String(date.getMinutes()).padStart(2, "0"); + return `${y}-${m}-${d} ${h}:${min}`; +} + +/** 검증 이력 목데이터 생성 (그리드 행 형식) */ +export function createVerificationHistoryMock( + count = 10, +): Record[] { + faker.seed(42); + return Array.from({ length: count }, () => { + const executedAt = faker.date.recent({ days: 90 }); + const durationMinutes = faker.number.int({ min: 1, max: 120 }); + const endedAt = new Date( + executedAt.getTime() + durationMinutes * 60 * 1000, + ); + const status = faker.helpers.arrayElement(PROGRESS_STATUSES); + const normalCount = faker.number.int({ min: 0, max: 500 }); + const excludedCount = faker.number.int({ min: 0, max: 50 }); + const abnormalCount = faker.number.int({ min: 0, max: 30 }); + const item: VerificationHistoryItem = { + executedAt: formatDateTime(executedAt), + progressStatus: status, + endedAt: formatDateTime(endedAt), + verificationYear: String(executedAt.getFullYear()), + mu: faker.number.float({ min: 0, max: 1, fractionDigits: 3 }), + percentile: faker.number.float({ + min: 0, + max: 100, + fractionDigits: 2, + }), + a: faker.number.float({ min: 0, max: 10, fractionDigits: 3 }), + c: faker.number.float({ min: 0, max: 10, fractionDigits: 3 }), + target: faker.helpers.arrayElement(VERIFICATION_TARGETS), + normalCount, + excludedCount, + abnormalCount, + }; + return { ...item } as Record; + }); +} + +/** 검증 이력 기본 목데이터 (10건) */ +export const verificationHistoryMockData = createVerificationHistoryMock(10); diff --git a/apps/st/src/pages/statistics/ui/statistics-page.tsx b/apps/st/src/pages/statistics/ui/statistics-page.tsx index 3efa3b04f..7513b0ffe 100644 --- a/apps/st/src/pages/statistics/ui/statistics-page.tsx +++ b/apps/st/src/pages/statistics/ui/statistics-page.tsx @@ -6,6 +6,8 @@ import { useTabStore } from "@/store/tab-store"; import Dashboard from "@/components/Dashboard"; import { SkeletonPage } from "@/shared/ui/skeleton-page"; +import "@rsone/components/index.css"; + const StatisticsPage = () => { const { tabs, activeTabId } = useTabStore(); @@ -38,7 +40,7 @@ const StatisticsPage = () => { {tabs.map((tab) => { // 렌더링 시점에 컴포넌트가 없으면 바로 로드 if (!componentCache.current.has(tab.id)) { - const componentName = tab.id; + const componentName = tab.urlPath; const parentComponentName = tab.parentEngNm; const DynamicComponent = dynamic( diff --git a/apps/st/src/shared/config/api.ts b/apps/st/src/shared/config/api.ts index 61c2bec0e..c8ff0983e 100644 --- a/apps/st/src/shared/config/api.ts +++ b/apps/st/src/shared/config/api.ts @@ -65,7 +65,10 @@ export const ENDPOINT = { POST_EVALUATION: "/board-service/api/v1/pstEvl", }, ST_SERVICE: { - COMMON: "/st-service/api/v1/common", + COMMON: "/st-management-service/api/v1/common", + INPUT: "/st-input-service/api/v1/input", + REVIEW: "/st-management-service/api/v1/review", + INPUTCOMMON: "/st-input-service/api/v1/common", } }; diff --git a/apps/st/src/store/tab-store.ts b/apps/st/src/store/tab-store.ts index f5b49bb29..6c48a5074 100644 --- a/apps/st/src/store/tab-store.ts +++ b/apps/st/src/store/tab-store.ts @@ -4,7 +4,8 @@ import type { MenuItem } from "@/entities/menu"; export interface Tab { id: string; // 고유 ID (menuEngNm) label: string; // 탭에 표시될 이름 - path: string; // URL 경로 + path: string; // 메뉴 경로 + urlPath: string; // url 경로 parentEngNm: string; // 부모 menuEngNm menuItem: MenuItem; // 메뉴 정보 } @@ -43,6 +44,7 @@ const useTabStoreHook = create((set, get) => ({ id: tabId, label: menuItem.menuKornNm, path: menuItem.menuPath, + urlPath: menuItem.urlPath, parentEngNm: menuList?.find((m) => m.menuCd === menuItem.parentId)?.menuEngNm || '', menuItem, }; @@ -112,5 +114,4 @@ const useTabStoreHook = create((set, get) => ({ })); export const useTabStore = useTabStoreHook; -export const getTabStoreState = () => useTabStoreHook.getState(); - +export const getTabStoreState = () => useTabStoreHook.getState(); \ No newline at end of file diff --git a/apps/st/tsconfig.json b/apps/st/tsconfig.json index 5b8c29899..e91347649 100644 --- a/apps/st/tsconfig.json +++ b/apps/st/tsconfig.json @@ -27,7 +27,8 @@ "@/*": ["./*"], "@common/components": ["../../../packages/common/components/src/"], "@common/config": ["../../../packages/common/config/dist/"], - "@common/network": ["../../../packages/common/network/"] + "@common/network": ["../../../packages/common/network/"], + "@rsone/components": ["../../../packages/rsone/dist/"] } }, "include": [ diff --git a/packages/rsone/.gitignore b/packages/rsone/.gitignore new file mode 100644 index 000000000..cf52894f8 --- /dev/null +++ b/packages/rsone/.gitignore @@ -0,0 +1,29 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +.env* + +# TypeScript +*.tsbuildinfo \ No newline at end of file diff --git a/packages/rsone/components/grid/CompactDataGrid.tsx b/packages/rsone/components/grid/CompactDataGrid.tsx new file mode 100644 index 000000000..837a5daac --- /dev/null +++ b/packages/rsone/components/grid/CompactDataGrid.tsx @@ -0,0 +1,289 @@ +import { useRef, useEffect } from "react"; +import { ValueType } from "realgrid"; + +import RealGrid, { GridFitStyle } from "realgrid"; +import { RealGridReact, RGDataColumn } from "realgrid-react"; +import type { DataGridProps, RealGridColumnInfo, GridViewWithSetColumns, FooterItemWithCallback } from "../type/DataGrid.type"; +import { getComputedValueCallback } from "@/type/computedFieldCallbacks"; + +// RealGrid 타입 확장 +declare module "realgrid" { + interface GridView { + setOptions(options: { + footer?: { visible?: boolean }; + summaryMode?: 'aggregate' | 'statistical' | 'none'; + checkBar?: { visible?: boolean }; + }): void; + } +} + +RealGrid.setLicenseKey(process.env.NEXT_PUBLIC_ST_REALGRID_KEY || ''); + +/** setDataComparer(field, comparer) - setFields 없이 숫자 정렬용. comparer(field, row1, row2) => -1|0|1 */ +interface RealGridDataProviderComparer { + setDataComparer?(field: string | number, comparer: (field: string | number, row1: number, row2: number) => number): void; + getValue?(row: number, field: string | number): unknown; +} + +/** columnInfo → RealGrid DataField 배열 (computed → valueCallback 포함, setFields용) */ +function buildFieldsFromColumns(columns: { fieldName: string; valueType?: ValueType; computed?: import("../type/DataGrid.type").ComputedFieldConfig }[]) { + return columns.map((col) => { + const field: { fieldName: string; dataType: string; valueCallback?: (prod: unknown, dataRow: number, fieldName: string, fieldNames: string[], values: unknown[]) => number } = { + fieldName: col.fieldName, + dataType: col.valueType === ValueType.NUMBER ? "number" : "text", + }; + if (col.computed) { + const hasFields = "fields" in col.computed && col.computed.fields.length > 0; + const hasNumeratorDenominator = "numeratorFields" in col.computed && col.computed.numeratorFields.length > 0 && col.computed.denominatorFields.length > 0; + if (hasFields || hasNumeratorDenominator) { + field.valueCallback = getComputedValueCallback(col.computed) as (prod: unknown, dataRow: number, fieldName: string, fieldNames: string[], values: unknown[]) => number; + } + } + return field; + }); +} + +/** setDataComparer용 숫자 비교기. setFields 없이 숫자 정렬. */ +function createNumberComparer(dp: RealGridDataProviderComparer): (field: string | number, row1: number, row2: number) => number { + return (field, row1, row2) => { + const get = dp.getValue?.bind(dp); + if (!get) return 0; + const val1 = get(row1, field); + const val2 = get(row2, field); + if (val1 == null && val2 == null) return 0; + if (val1 == null) return 1; + if (val2 == null) return -1; + const num1 = typeof val1 === "number" ? val1 : parseFloat(String(val1)); + const num2 = typeof val2 === "number" ? val2 : parseFloat(String(val2)); + if (isNaN(num1) && isNaN(num2)) return 0; + if (isNaN(num1)) return 1; + if (isNaN(num2)) return -1; + return num1 > num2 ? 1 : num1 < num2 ? -1 : 0; + }; +} + +const CompactDataGrid = ({ columnInfo, fixedColumns, showHeaderCheckbox = false, rowData, setRowData }: DataGridProps) => { + const { columns, columnLayout } = columnInfo; + const gridRef = useRef(null); + + useEffect(() => { + if (!gridRef.current?.gridView || !gridRef.current?.dataProvider) return; + + const gridView = gridRef.current.gridView; + const dataProvider = gridRef.current.dataProvider; + + // setDataComparer로 숫자 정렬 (setFields 미사용 → 데이터 유실·필드 중복 오류 없음) + const dp = dataProvider as RealGridDataProviderComparer; + if (typeof dp.setDataComparer === "function" && typeof dp.getValue === "function") { + const numberComparer = createNumberComparer(dp); + const seen = new Set(); + columns.forEach((col) => { + if (col.valueType === ValueType.NUMBER && col.fieldName && !seen.has(col.fieldName)) { + seen.add(col.fieldName); + try { + dp.setDataComparer!(col.fieldName, numberComparer); + } catch (e) { + console.warn("setDataComparer:", e); + } + } + }); + } + + gridView.setDisplayOptions({ + fitStyle: GridFitStyle.EVEN + }); + + gridView.header.height = 60; + + // 헤더 체크박스 표시 여부 설정 + gridView.setOptions({ + checkBar: { visible: showHeaderCheckbox }, + rowIndicator: { visible: false }, + }); + + // footer가 있는 컬럼이 있으면 footer 높이 설정 + const hasFooter = columns.some(col => col.footer); + if (hasFooter) { + // footer가 배열이거나 multiRow가 true인 footer가 있으면 멀티푸터 설정 (여러 행) + const hasArrayFooter = columns.some(col => col.footer && Array.isArray(col.footer)); + const hasMultiRowFooter = columns.some(col => { + const footer = col.footer; + return footer && !Array.isArray(footer) && 'multiRow' in footer && footer.multiRow === true; + }); + if (hasArrayFooter || hasMultiRowFooter) { + // footer 배열의 길이를 확인하여 행 수 결정 + const arrayFooterLengths = columns + .filter(col => col.footer && Array.isArray(col.footer)) + .map(col => (col.footer as unknown[]).length); + const maxFooterLength = arrayFooterLengths.length > 0 + ? Math.max(...arrayFooterLengths) + : 2; // 기본값 2 + const footerHeights = Array.from({ length: maxFooterLength }, () => ({ height: 40 })); + gridView.setFooters?.(footerHeights); + } else { + gridView.footer.height = 40; + } + } + + // 고정 컬럼 설정 + if (fixedColumns !== undefined && fixedColumns > 0) { + gridView.setFixedOptions({ + colCount: fixedColumns, + }); + } + + gridView.setOptions({ + summaryMode: "aggregate" + }); + + // footerUserSpans가 있는 경우 setColumnLayout으로 설정 + const checkFooterUserSpans = (layout: unknown[]): boolean => { + return layout.some(item => { + if (typeof item === 'string') return false; + if (item && typeof item === 'object' && 'footerUserSpans' in item) return true; + if (item && typeof item === 'object' && 'items' in item && Array.isArray(item.items)) { + return checkFooterUserSpans(item.items); + } + return false; + }); + }; + + if (checkFooterUserSpans(columnLayout)) { + // setColumnLayout은 그리드가 완전히 렌더링된 후 호출해야 함 + setTimeout(() => { + if (gridRef.current?.gridView && columnLayout) { + gridRef.current.gridView.setColumnLayout?.(columnLayout); + } + }, 100); + } + + // footer 설정 (배열 또는 단일 객체 모두 처리) + // setFooters 호출 후 footer 배열 설정 + setTimeout(() => { + const currentGridView = gridRef.current?.gridView; + if (!currentGridView || !currentGridView.columnByName) { + console.warn('GridView not available for footer setup'); + return; + } + + // footer가 배열인 컬럼이 있으면 setColumns를 다시 호출하여 footer 설정 + const hasArrayFooter = columns.some(col => col.footer && Array.isArray(col.footer)); + if (hasArrayFooter) { + // 모든 컬럼 정보를 다시 가져와서 footer 배열을 포함하여 setColumns 호출 + const columnsWithFooter: RealGridColumnInfo[] = columns.map(col => { + const colInfo: RealGridColumnInfo = { + name: col.name, + fieldName: col.fieldName, + header: col.header, + editable: col.editable, + valueType: col.valueType, + dataFormat: col.dataFormat, + ...(col.mergeCells && { mergeRule: { criteria: 'value' as const } }), + }; + + if (col.footer) { + if (Array.isArray(col.footer)) { + colInfo.footer = col.footer; + } else { + colInfo.footer = { + text: col.footer.text, + expression: col.footer.expression, + numberFormat: col.footer.numberFormat, + }; + } + } + + return colInfo; + }); + + try { + (currentGridView as GridViewWithSetColumns).setColumns?.(columnsWithFooter); + + // setColumns 호출 후 footer 배열의 valueCallback을 다시 설정 + setTimeout(() => { + columns.forEach(col => { + if (!col.footer || !Array.isArray(col.footer)) return; + + try { + const column = currentGridView.columnByName(col.name); + if (!column) return; + + // footer 배열의 각 요소에 valueCallback 설정 + col.footer.forEach((footerItem, index) => { + if (footerItem.valueCallback && column.footer) { + // footer가 배열인 경우 + if (Array.isArray(column.footer) && column.footer[index]) { + (column.footer[index] as FooterItemWithCallback).valueCallback = footerItem.valueCallback; + } + } + }); + } catch (e) { + console.warn(`Error setting footer array valueCallback for column ${col.name}:`, e); + } + }); + }, 50); + } catch (e) { + console.warn('Failed to set columns with footer array:', e); + } + } + + // footer가 단일 객체이고 valueCallback이 있는 경우 + columns.forEach(col => { + if (!col.footer || Array.isArray(col.footer)) return; + + try { + const column = currentGridView.columnByName(col.name); + if (!column || !column.footer) return; + + if (col.footer.valueCallback) { + (column.footer as { valueCallback?: typeof col.footer.valueCallback }).valueCallback = col.footer.valueCallback; + } + } catch (e) { + console.warn(`Error setting footer valueCallback for column ${col.name}:`, e); + } + }); + }, 200); + + }, [columns, columnLayout, fixedColumns, rowData, showHeaderCheckbox]); + + return ( +
+
+ { + const dp = gridRef.current?.dataProvider as { setFields?: (fields: unknown[]) => void } | undefined; + if (dp?.setFields) { + dp.setFields(buildFieldsFromColumns(columns)); + } + }} + > + {columns.map((column) => ( + + ))} + +
+
+ ); +}; + +export default CompactDataGrid; \ No newline at end of file diff --git a/packages/rsone/components/grid/DataGrid.tsx b/packages/rsone/components/grid/DataGrid.tsx new file mode 100644 index 000000000..ed6dedf51 --- /dev/null +++ b/packages/rsone/components/grid/DataGrid.tsx @@ -0,0 +1,725 @@ +import { useRef, useEffect } from "react" +import { ValueType } from "realgrid" + +import RealGrid, { GridFitStyle, type DisplayOptions } from "realgrid" +import { RealGridReact, RGDataColumn } from "realgrid-react" +import type { DataGridProps, RealGridColumnInfo, GridViewWithSetColumns, FooterItemWithCallback } from "../type/DataGrid.type" +import { getComputedValueCallback } from "../type/computedFieldCallbacks" +import JSZip from "jszip" +import * as XLSX from "xlsx" + +import { useAlertStore } from "../store/alert-store"; + +import { Button } from "@common/components"; +import { useTabStore } from "../store/tab-store"; + +// RealGrid 타입 확장 +declare module "realgrid" { + interface GridView { + setOptions(options: { + footer?: { visible?: boolean }; + summaryMode?: 'aggregate' | 'statistical' | 'none'; + checkBar?: { visible?: boolean }; + }): void; + } +} + +// 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 || ''); + +/** setDataComparer(field, comparer) - setFields 없이 숫자 정렬용. comparer(field, row1, row2) => -1|0|1 */ +interface RealGridDataProviderComparer { + setDataComparer?(field: string | number, comparer: (field: string | number, row1: number, row2: number) => number): void; + getValue?(row: number, field: string | number): unknown; +} + +/** setDataComparer용 숫자 비교기. setFields 없이 숫자 정렬. */ +function createNumberComparer(dp: RealGridDataProviderComparer): (field: string | number, row1: number, row2: number) => number { + return (field, row1, row2) => { + const get = dp.getValue?.bind(dp); + if (!get) return 0; + const val1 = get(row1, field); + const val2 = get(row2, field); + if (val1 == null && val2 == null) return 0; + if (val1 == null) return 1; + if (val2 == null) return -1; + const num1 = typeof val1 === "number" ? val1 : parseFloat(String(val1)); + const num2 = typeof val2 === "number" ? val2 : parseFloat(String(val2)); + if (isNaN(num1) && isNaN(num2)) return 0; + if (isNaN(num1)) return 1; + if (isNaN(num2)) return -1; + return num1 > num2 ? 1 : num1 < num2 ? -1 : 0; + }; +} + +/** 정렬 필드 값을 숫자로 파싱 (쉼표 제거) */ +function toNum(v: unknown): number { + if (v == null) return NaN; + if (typeof v === "number") return isNaN(v) ? NaN : v; + return parseFloat(String(v).replace(/,/g, "")) || NaN; +} + +/** 헤더에 정렬 화살표 표시 (CSS 방식) */ +function updateHeaderSortIndicator( + gridView: { + columnByIndex?: (i: number) => { fieldName?: string; name?: string } | undefined; + columnByName?: (name: string) => { fieldName?: string; name?: string } | undefined; + getColumn?: (i: number) => { fieldName?: string; name?: string } | undefined; + }, + columns: { fieldName?: string; name: string }[], + sortDir: Record +) { + try { + columns.forEach((col) => { + if (!col.fieldName && !col.name) return; + + // 컬럼 찾기 + let gridCol: { fieldName?: string; name?: string } | undefined; + for (let i = 0; i < 100; i++) { + const c = gridView.columnByIndex?.(i) ?? gridView.getColumn?.(i); + if (!c) break; + if (c.fieldName === (col.fieldName || col.name) || c.name === (col.fieldName || col.name)) { + gridCol = c; + break; + } + } + if (!gridCol) { + gridCol = gridView.columnByName?.(col.name); + } + + if (!gridCol) return; + + const fieldName = gridCol.fieldName || gridCol.name || col.fieldName || col.name; + const direction = sortDir[fieldName] || sortDir[col.name] || "none"; + + // DOM에서 헤더 요소 찾기 + const gridEl1 = document.querySelector('[data-grid-id]') as HTMLElement | null; + const gridEl2 = document.querySelector('.rg-grid') as HTMLElement | null; + const gridEl = gridEl1 || gridEl2; + if (!gridEl) return; + + // 헤더 셀 찾기 (fieldName 또는 name으로) + const headerCells = gridEl.querySelectorAll('.rg-header-cell, [class*="header"]'); + let targetCell: HTMLElement | null = null; + + headerCells.forEach((cell) => { + const el = cell as HTMLElement; + const text = el.textContent?.trim(); + const colName = col.name; + const colFieldName = col.fieldName; + if (text && (text.includes(colName) || (colFieldName && text.includes(colFieldName)))) { + targetCell = el; + } + }); + + if (!targetCell) return; + + const headerCell = targetCell as HTMLElement; + + // 기존 화살표 제거 + const existingIndicator = headerCell.querySelector(".rg-sort-indicator") as HTMLElement | null; + if (existingIndicator) { + existingIndicator.remove(); + } + + // 화살표 추가 + if (direction === "asc" || direction === "desc") { + const indicator = document.createElement("span"); + indicator.className = "rg-sort-indicator"; + indicator.textContent = direction === "asc" ? "▲" : "▼"; + indicator.style.marginLeft = "4px"; + indicator.style.fontSize = "10px"; + indicator.style.color = "#666"; + indicator.style.display = "inline-block"; + indicator.style.verticalAlign = "middle"; + headerCell.appendChild(indicator); + } + }); + } catch (e) { + console.warn("updateHeaderSortIndicator error:", e); + } +} + +/** onSorting: mergeCells: true인 컬럼이 있으면 그룹 내 순서 유지 정렬, 없으면 RealGrid 기본 정렬 */ +function createOnSortingHandler( + columns: { fieldName?: string; name: string; mergeCells?: boolean; sortdisable?: boolean; valueType?: ValueType }[], + setRowData: React.Dispatch[]>>, + sortDirRef: React.MutableRefObject>, + originalRowsRef: React.MutableRefObject[]>> +) { + const fieldNames = [...new Set(columns.map((c) => c.fieldName).filter((x): x is string => typeof x === "string"))]; + // mergeCells: true인 컬럼 찾기 (시군구 등) - 그룹 필드로 사용 + // 시군구를 우선으로 찾고, 없으면 첫 번째 mergeCells 컬럼 사용 + const mergeCellCols = columns.filter((c) => c.mergeCells === true && c.fieldName); + const mergeCellCol = mergeCellCols.find((c) => c.fieldName === '시군구') || mergeCellCols[0]; + const groupField = mergeCellCol?.fieldName || null; + const orderByField = '구분'; // 고정값 또는 설정 가능하게 할 수 있음 + const hasMergeCells = mergeCellCols.length > 0; + + return function onSorting(g: unknown, fields: unknown[], _directions: unknown[]) { + const grid = g as { getColumn?: (i: number) => { fieldName?: string; name?: string } | undefined; getDataSource?: () => { getRowCount(): number; getValue(r: number, f: string): unknown } }; + const idx = fields?.[0]; + if (idx === undefined) return true; + let sortField: string | null = null; + if (typeof idx === "string") sortField = idx; + else { + const colObj = grid?.getColumn?.(typeof idx === "number" ? idx : 0); + sortField = (colObj?.fieldName ?? colObj?.name) ?? null; + } + if (!sortField) return true; + const col = columns.find((c) => (c.fieldName === sortField || c.name === sortField)); + if (!col || col.sortdisable) return true; + + // mergeCells: true인 컬럼이 없으면 RealGrid 기본 정렬 사용 + if (!hasMergeCells || !groupField) return true; + + const ds = grid.getDataSource?.(); + if (!ds) return true; + const cnt = ds.getRowCount(); + const rows: Record[] = []; + for (let i = 0; i < cnt; i++) { + const r: Record = {}; + for (const f of fieldNames) r[f] = ds.getValue(i, f) as string | number; + rows.push(r); + } + const prev = sortDirRef.current[sortField] ?? "none"; + const setRows = (ds as { setRows?: (r: unknown[]) => void }).setRows?.bind(ds); + + if (prev === "desc") { + const orig = originalRowsRef.current[sortField]; + if (orig && setRows) { + setRows(orig); + setRowData(orig); + } + sortDirRef.current[sortField] = "none"; + return false; + } + if (prev === "none") { + originalRowsRef.current[sortField] = rows.map((r) => ({ ...r })); + } + const sortAsc = prev !== "asc"; + sortDirRef.current[sortField] = sortAsc ? "asc" : "desc"; + const sorted = sortRowsGroupPreserving(rows, sortField, sortAsc, { groupField, orderByField }, col.valueType); + setRows?.(sorted); + setRowData(sorted); + return false; + }; +} + +/** sortPreserveGroupOrder: 그룹 간에는 sortField로 정렬, 그룹 내에서는 원본 순서 유지. */ +function sortRowsGroupPreserving( + rows: Record[], + sortField: string, + sortAsc: boolean, + opt: { groupField: string; orderByField?: string }, + valueType?: ValueType +): Record[] { + const groups = new Map[]>(); + for (const r of rows) { + const k = String(r[opt.groupField] ?? ""); + if (!groups.has(k)) groups.set(k, []); + groups.get(k)!.push(r); + } + + // valueType에 따라 비교 함수 선택 + const compareValue = (val: unknown): number | string => { + if (valueType === ValueType.NUMBER) { + return toNum(val); + } + // 텍스트나 기타 타입은 문자열로 비교 + return String(val ?? ""); + }; + + const rep = (arr: Record[]): number | string => { + const values = arr.map((r) => compareValue(r[sortField])); + if (valueType === ValueType.NUMBER) { + const nums = values.filter((v): v is number => typeof v === "number" && !isNaN(v)); + if (nums.length === 0) return sortAsc ? Infinity : -Infinity; + return sortAsc ? Math.min(...nums) : Math.max(...nums); + } else { + // 문자열 정렬 + const strs = values.filter((v): v is string => typeof v === "string"); + if (strs.length === 0) return sortAsc ? "\uffff" : ""; + strs.sort(); + return sortAsc ? (strs[0] as string) : (strs[strs.length - 1] as string); + } + }; + + const entries = [...groups.entries()].sort((a, b) => { + const valA = rep(a[1]); + const valB = rep(b[1]); + if (typeof valA === "number" && typeof valB === "number") { + return (sortAsc ? 1 : -1) * (valA - valB); + } + // 문자열 비교 + const strA = String(valA); + const strB = String(valB); + return (sortAsc ? 1 : -1) * strA.localeCompare(strB); + }); + + return entries.flatMap(([, arr]) => arr); +} + +/** columnInfo → RealGrid DataField 배열 (computed → valueCallback 포함, setFields용) */ +function buildFieldsFromColumns(columns: { fieldName: string; valueType?: ValueType; computed?: import("../type/DataGrid.type").ComputedFieldConfig }[]) { + return columns.map((col) => { + const field: { fieldName: string; dataType: string; valueCallback?: (prod: unknown, dataRow: number, fieldName: string, fieldNames: string[], values: unknown[]) => number } = { + fieldName: col.fieldName, + dataType: col.valueType === ValueType.NUMBER ? "number" : "text", + }; + if (col.computed) { + const hasFields = "fields" in col.computed && col.computed.fields.length > 0; + const hasNumeratorDenominator = "numeratorFields" in col.computed && col.computed.numeratorFields.length > 0 && col.computed.denominatorFields.length > 0; + if (hasFields || hasNumeratorDenominator) { + field.valueCallback = getComputedValueCallback(col.computed) as (prod: unknown, dataRow: number, fieldName: string, fieldNames: string[], values: unknown[]) => number; + } + } + return field; + }); +} + +const DataGrid = ({ columnInfo, rowStartNum, isExcelImport = true, isExcelExport = true, fixedColumns, showHeaderCheckbox = false, rowData, setRowData }: DataGridProps) => { + const { columns, columnLayout } = columnInfo; + const gridRef = useRef(null); + const fileInputRef = useRef(null); + const sortDirRef = useRef>({}); + const originalRowsRef = useRef[]>>({}); + const { setMessage: alert } = useAlertStore((state) => state); + const activeTabId = useTabStore((state) => state.activeTabId); + const menuList = useTabStore((state) => state.menuList); + + const exportExcel = () => { + if (rowData.length === 0) { + alert('데이터가 없습니다.'); + return; + } + + if (!gridRef.current?.gridView) return; + + const gridView = gridRef.current.gridView; + + const menuName = menuList + ?.flatMap((m) => m.children || []) + .flatMap((subM) => subM || []) + .find((s) => s.menuEngNm === activeTabId) + ?.menuKornNm || 'export'; + + gridView.exportGrid({ + type: 'excel', + target: 'local', + fileName: `${menuName}.xlsx`, + footer: 'hidden', + done: () => { + alert('내보내기가 완료되었습니다.'); + }, + }); + }; + + const importExcel = () => { + fileInputRef.current?.click(); + }; + + const handleFileChange = async (e: React.ChangeEvent) => { + const file = e.target.files?.[0]; + if (!file) return; + + 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; + + // 헤더 텍스트를 fieldName으로 매핑하는 맵 생성 + const headerToFieldNameMap = new Map(); + headers.forEach((headerText) => { + // columns에서 header.text가 일치하는 컬럼 찾기 + const matchedColumn = columns.find( + (col) => col.header?.text === headerText || col.fieldName === headerText + ); + if (matchedColumn?.fieldName) { + headerToFieldNameMap.set(headerText, matchedColumn.fieldName); + } else { + // 매칭되지 않으면 원본 헤더 텍스트 사용 (하위 호환성) + headerToFieldNameMap.set(headerText, headerText); + } + }); + + // 데이터 행 읽기 + 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}`; + // 헤더 텍스트를 fieldName으로 변환 + const fieldName = headerToFieldNameMap.get(headerName) || headerName; + + let cellValue: string | number = ''; + if (cell && cell.v !== undefined && cell.v !== null) { + // 엑셀 셀 타입이 숫자이거나 값이 숫자인 경우 숫자로 변환 + if (cell.t === 'n' || (typeof cell.v === 'number' && !isNaN(cell.v))) { + cellValue = cell.v as number; + } else { + const stringValue = String(cell.v); + // 문자열이지만 숫자로 변환 가능한 경우 숫자로 변환 + const numericValue = Number(stringValue.replace(/,/g, '')); + if (!isNaN(numericValue) && stringValue.trim() !== '') { + cellValue = numericValue; + } else { + cellValue = stringValue; + } + } + } + + if (cellValue !== '' && cellValue !== null && cellValue !== undefined) { + hasData = true; + } + + rowData[fieldName] = cellValue; + } + + // 데이터가 하나라도 있는 행만 추가 + if (hasData) { + dataRows.push(rowData); + } + } + setRowData(dataRows); + } catch (error) { + console.error('엑셀 파일 파싱 오류:', error); + alert('엑셀 파일 파싱 중 오류가 발생했습니다.'); + } finally { + if (fileInputRef.current) { + fileInputRef.current.value = ''; + } + } + }; + + reader.onerror = () => { + alert('파일 읽기 중 오류가 발생했습니다.'); + if (fileInputRef.current) { + fileInputRef.current.value = ''; + } + }; + + reader.readAsArrayBuffer(file); + } catch (error) { + console.error('엑셀 파일 가져오기 오류:', error); + alert('엑셀 파일 가져오기 중 오류가 발생했습니다.'); + if (fileInputRef.current) { + fileInputRef.current.value = ''; + } + } + }; + + useEffect(() => { + if (!gridRef.current?.gridView || !gridRef.current?.dataProvider) return; + + const gridView = gridRef.current.gridView; + const dataProvider = gridRef.current.dataProvider; + + // mergeCells: true인 컬럼이 있으면 onSorting에서 클라이언트 정렬로 처리 + const hasMergeCells = columns.some((col) => col.mergeCells === true); + + // setDataComparer로 숫자 정렬 (mergeCells가 있으면 onSorting에서 클라이언트 정렬로 처리) + const dp = dataProvider as RealGridDataProviderComparer; + if (typeof dp.setDataComparer === "function" && typeof dp.getValue === "function") { + const numberComparer = createNumberComparer(dp); + const seen = new Set(); + columns.forEach((col) => { + if (hasMergeCells) return; // mergeCells가 있으면 모든 컬럼을 클라이언트 정렬로 처리 + if (col.valueType === ValueType.NUMBER && col.fieldName && !seen.has(col.fieldName)) { + seen.add(col.fieldName); + try { + dp.setDataComparer!(col.fieldName, numberComparer); + } catch (e) { + console.warn("setDataComparer:", e); + } + } + }); + } + + // mergeCells: true인 컬럼이 있으면 onSorting에서 그룹 내 순서 유지 정렬 적용 + (gridView as { onSorting?: (g: unknown, f: unknown[], d: unknown[]) => boolean }).onSorting = createOnSortingHandler(columns, setRowData, sortDirRef, originalRowsRef); + + (gridView.displayOptions as DisplayOptions).fitStyle = GridFitStyle.EVEN; + gridView.header.height = 60; + + // 헤더 체크박스 표시 여부 설정 + gridView.setOptions({ + checkBar: { visible: showHeaderCheckbox }, + rowIndicator: { visible: false }, + }); + + // footer가 있는 컬럼이 있으면 footer 높이 설정 + const hasFooter = columns.some(col => col.footer); + if (hasFooter) { + // footer가 배열이거나 multiRow가 true인 footer가 있으면 멀티푸터 설정 (여러 행) + const hasArrayFooter = columns.some(col => col.footer && Array.isArray(col.footer)); + const hasMultiRowFooter = columns.some(col => { + const footer = col.footer; + return footer && !Array.isArray(footer) && 'multiRow' in footer && footer.multiRow === true; + }); + if (hasArrayFooter || hasMultiRowFooter) { + // footer 배열의 길이를 확인하여 행 수 결정 + const arrayFooterLengths = columns + .filter(col => col.footer && Array.isArray(col.footer)) + .map(col => (col.footer as unknown[]).length); + const maxFooterLength = arrayFooterLengths.length > 0 + ? Math.max(...arrayFooterLengths) + : 2; // 기본값 2 + const footerHeights = Array.from({ length: maxFooterLength }, () => ({ height: 40 })); + gridView.setFooters?.(footerHeights); + } else { + gridView.footer.height = 40; + } + } + + // 고정 컬럼 설정 + if (fixedColumns !== undefined && fixedColumns > 0) { + gridView.setFixedOptions({ + colCount: fixedColumns, + }); + } + + gridView.setOptions({ + summaryMode: "aggregate" + }); + + // footerUserSpans가 있는 경우 setColumnLayout으로 설정 + const checkFooterUserSpans = (layout: unknown[]): boolean => { + return layout.some(item => { + if (typeof item === 'string') return false; + if (item && typeof item === 'object' && 'footerUserSpans' in item) return true; + if (item && typeof item === 'object' && 'items' in item && Array.isArray(item.items)) { + return checkFooterUserSpans(item.items); + } + return false; + }); + }; + + if (checkFooterUserSpans(columnLayout)) { + // setColumnLayout은 그리드가 완전히 렌더링된 후 호출해야 함 + setTimeout(() => { + if (gridRef.current?.gridView && columnLayout) { + gridRef.current.gridView.setColumnLayout?.(columnLayout); + } + }, 100); + } + + // footer 설정 (배열 또는 단일 객체 모두 처리) + // setFooters 호출 후 footer 배열 설정 + setTimeout(() => { + const currentGridView = gridRef.current?.gridView; + if (!currentGridView || !currentGridView.columnByName) { + console.warn('GridView not available for footer setup'); + return; + } + + // footer가 배열인 컬럼이 있으면 setColumns를 다시 호출하여 footer 설정 + const hasArrayFooter = columns.some(col => col.footer && Array.isArray(col.footer)); + if (hasArrayFooter) { + // 모든 컬럼 정보를 다시 가져와서 footer 배열을 포함하여 setColumns 호출 + const columnsWithFooter: RealGridColumnInfo[] = columns.map(col => { + const colInfo: RealGridColumnInfo = { + name: col.name, + fieldName: col.fieldName, + header: col.header, + editable: col.editable, + valueType: col.valueType, + dataFormat: col.dataFormat, + ...(col.mergeCells && { mergeRule: { criteria: 'value' as const } }), + }; + + if (col.footer) { + if (Array.isArray(col.footer)) { + colInfo.footer = col.footer; + } else { + colInfo.footer = { + text: col.footer.text, + expression: col.footer.expression, + numberFormat: col.footer.numberFormat, + }; + } + } + + return colInfo; + }); + + try { + (currentGridView as GridViewWithSetColumns).setColumns?.(columnsWithFooter); + + // setColumns 호출 후 footer 배열의 valueCallback을 다시 설정 + setTimeout(() => { + columns.forEach(col => { + if (!col.footer || !Array.isArray(col.footer)) return; + + try { + const column = currentGridView.columnByName(col.name); + if (!column) return; + + // footer 배열의 각 요소에 valueCallback 설정 + col.footer.forEach((footerItem, index) => { + if (footerItem.valueCallback && column.footer) { + // footer가 배열인 경우 + if (Array.isArray(column.footer) && column.footer[index]) { + (column.footer[index] as FooterItemWithCallback).valueCallback = footerItem.valueCallback; + } + } + }); + } catch (e) { + console.warn(`Error setting footer array valueCallback for column ${col.name}:`, e); + } + }); + }, 50); + } catch (e) { + console.warn('Failed to set columns with footer array:', e); + } + } + + // footer가 단일 객체이고 valueCallback이 있는 경우 + columns.forEach(col => { + if (!col.footer || Array.isArray(col.footer)) return; + + try { + const column = currentGridView.columnByName(col.name); + if (!column || !column.footer) return; + + if (col.footer.valueCallback) { + (column.footer as { valueCallback?: typeof col.footer.valueCallback }).valueCallback = col.footer.valueCallback; + } + } catch (e) { + console.warn(`Error setting footer valueCallback for column ${col.name}:`, e); + } + }); + }, 200); + + }, [columns, columnLayout, fixedColumns, rowData, showHeaderCheckbox]); + + + return ( +
+ +
+ {isExcelImport && + + } + {isExcelExport && + + } +
+
+ { + (grid as { onSorting?: (g: unknown, f: unknown[], d: unknown[]) => boolean }).onSorting = createOnSortingHandler(columns, setRowData, sortDirRef, originalRowsRef); + const dp = gridRef.current?.dataProvider as { setFields?: (fields: unknown[]) => void } | undefined; + if (dp?.setFields) { + dp.setFields(buildFieldsFromColumns(columns)); + } + }} + > + {columns.map((column) => ( + + ))} + +
+
+ ); +}; + +export default DataGrid; \ No newline at end of file diff --git a/packages/rsone/components/grid/SplitDataGrid.tsx b/packages/rsone/components/grid/SplitDataGrid.tsx new file mode 100644 index 000000000..7d24c96c0 --- /dev/null +++ b/packages/rsone/components/grid/SplitDataGrid.tsx @@ -0,0 +1,35 @@ +import CompactDataGrid from "./CompactDataGrid"; +import type { SplitDataGridProps } from "../type/DataGrid.type"; + +const SplitDataGrid = ({ + leftColumnInfo, + rightColumnInfo, + leftFixedColumns, + rightFixedColumns, + showHeaderCheckbox = false, + leftRowData, + rightRowData, + setLeftRowData, + setRightRowData, +}: SplitDataGridProps) => { + return ( +
+ + +
+ ); +}; + +export default SplitDataGrid; diff --git a/packages/rsone/components/grid/TabDataGrid.tsx b/packages/rsone/components/grid/TabDataGrid.tsx new file mode 100644 index 000000000..ea618e8ac --- /dev/null +++ b/packages/rsone/components/grid/TabDataGrid.tsx @@ -0,0 +1,964 @@ +import { useRef, useEffect, useState } from "react" +import { ValueType } from "realgrid" + +import RealGrid, { GridFitStyle } from "realgrid" +import { RealGridReact, RGDataColumn } from "realgrid-react" +import type { TabDataGridProps, ColumnInfo, RealGridColumnInfo, GridViewWithSetColumns, FooterItemWithCallback } from "../type/DataGrid.type" +import { getComputedValueCallback } from "@/type/computedFieldCallbacks" +import JSZip from "jszip" +import * as XLSX from "xlsx" +import { useAlertStore } from "../store/alert-store"; +import { useTabStore } from "../store/tab-store"; + +import { Tabs, TabsList, TabsTrigger, TabsContent } from "@common/components"; +import { Button } from "@common/components"; +import SplitDataGrid from "./SplitDataGrid" + +// RealGrid 타입 확장 +declare module "realgrid" { + interface GridView { + setOptions(options: { + footer?: { visible?: boolean }; + summaryMode?: 'aggregate' | 'statistical' | 'none'; + checkBar?: { visible?: boolean }; + }): void; + } +} + +interface RealGridDataProviderFill { + fillJsonData?(data: Record[], options?: { fillMode?: string }): void; +} + +/** setDataComparer(field, comparer) - setFields 없이 숫자 정렬용. comparer(field, row1, row2) => -1|0|1 */ +interface RealGridDataProviderComparer { + setDataComparer?(field: string | number, comparer: (field: string | number, row1: number, row2: number) => number): void; + getValue?(row: number, field: string | number): unknown; +} + +// 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 || ''); + +/** 정렬 필드 값을 숫자로 파싱 (쉼표 제거) */ +function toNum(v: unknown): number { + if (v == null) return NaN; + if (typeof v === "number") return isNaN(v) ? NaN : v; + return parseFloat(String(v).replace(/,/g, "")) || NaN; +} + +/** 헤더에 정렬 화살표 표시 (CSS 방식) */ +function updateHeaderSortIndicator( + gridView: { + columnByIndex?: (i: number) => { fieldName?: string; name?: string } | undefined; + columnByName?: (name: string) => { fieldName?: string; name?: string } | undefined; + getColumn?: (i: number) => { fieldName?: string; name?: string } | undefined; + getElement?: () => HTMLElement | null; + container?: HTMLElement; + }, + columns: { fieldName?: string; name: string }[], + sortDir: Record, + gridElement?: HTMLElement | null +) { + try { + // 그리드 DOM 요소 찾기 + let gridEl: HTMLElement | null = null; + if (gridElement) { + gridEl = gridElement; + } else if (gridView.getElement) { + gridEl = gridView.getElement(); + } else if (gridView.container) { + gridEl = gridView.container; + } else { + // 폴백: 모든 그리드 중에서 찾기 + const allGrids = document.querySelectorAll('.rg-grid, [data-grid-id]'); + if (allGrids.length > 0) { + // 활성 탭의 그리드를 찾기 위해 TabsContent 내부의 그리드 찾기 + const activeTabContent = document.querySelector('[data-state="active"]')?.closest('[role="tabpanel"]'); + if (activeTabContent) { + gridEl = activeTabContent.querySelector('.rg-grid, [data-grid-id]') as HTMLElement; + } + if (!gridEl && allGrids.length === 1) { + gridEl = allGrids[0] as HTMLElement; + } + } + } + + if (!gridEl) return; + + columns.forEach((col) => { + if (!col.fieldName && !col.name) return; + + const fieldName = col.fieldName || col.name; + const direction = sortDir[fieldName] || sortDir[col.name] || "none"; + if (direction === "none") return; + + // 헤더 셀 찾기 (fieldName 또는 name으로) + const headerCells = gridEl.querySelectorAll('.rg-header-cell, [class*="header"], .rg-header'); + let targetCell: HTMLElement | null = null; + + headerCells.forEach((cell) => { + const el = cell as HTMLElement; + const text = el.textContent?.trim(); + const colName = col.name; + const colFieldName = col.fieldName; + // 헤더 텍스트와 컬럼 이름이 일치하는지 확인 + if (text && ( + text === colName || + text === colFieldName || + text.includes(colName) || + (colFieldName && text.includes(colFieldName)) + )) { + targetCell = el; + } + }); + + if (!targetCell) return; + + const headerCell = targetCell as HTMLElement; + + // 기존 화살표 제거 + const existingIndicator = headerCell.querySelector(".rg-sort-indicator") as HTMLElement | null; + if (existingIndicator) { + existingIndicator.remove(); + } + + // 화살표 추가 + const indicator = document.createElement("span"); + indicator.className = "rg-sort-indicator"; + indicator.textContent = direction === "asc" ? "▲" : "▼"; + indicator.style.marginLeft = "4px"; + indicator.style.fontSize = "10px"; + indicator.style.color = "#666"; + indicator.style.display = "inline-block"; + indicator.style.verticalAlign = "middle"; + headerCell.appendChild(indicator); + }); + } catch (e) { + console.warn("updateHeaderSortIndicator error:", e); + } +} + +/** onSorting: mergeCells: true인 컬럼이 있으면 그룹 내 순서 유지 정렬, 없으면 RealGrid 기본 정렬 */ +function createOnSortingHandler( + columns: { fieldName?: string; name: string; mergeCells?: boolean; sortdisable?: boolean; valueType?: ValueType }[], + setRowData: (updater: React.SetStateAction[]>) => void, + sortDirRef: React.MutableRefObject>, + originalRowsRef: React.MutableRefObject[]>>, + findGridElement?: () => HTMLElement | null +) { + const fieldNames = [...new Set(columns.map((c) => c.fieldName).filter((x): x is string => typeof x === "string"))]; + // mergeCells: true인 컬럼 찾기 (시군구 등) - 그룹 필드로 사용 + // 시군구를 우선으로 찾고, 없으면 첫 번째 mergeCells 컬럼 사용 + const mergeCellCols = columns.filter((c) => c.mergeCells === true && c.fieldName); + const mergeCellCol = mergeCellCols.find((c) => c.fieldName === '시군구') || mergeCellCols[0]; + const groupField = mergeCellCol?.fieldName || null; + const orderByField = '구분'; // 고정값 또는 설정 가능하게 할 수 있음 + const hasMergeCells = mergeCellCols.length > 0; + + return function onSorting(g: unknown, fields: unknown[], _directions: unknown[]) { + const grid = g as { getColumn?: (i: number) => { fieldName?: string; name?: string } | undefined; getDataSource?: () => { getRowCount(): number; getValue(r: number, f: string): unknown } }; + const idx = fields?.[0]; + if (idx === undefined) return true; + let sortField: string | null = null; + if (typeof idx === "string") sortField = idx; + else { + const colObj = grid?.getColumn?.(typeof idx === "number" ? idx : 0); + sortField = (colObj?.fieldName ?? colObj?.name) ?? null; + } + if (!sortField) return true; + const col = columns.find((c) => (c.fieldName === sortField || c.name === sortField)); + if (!col || col.sortdisable) return true; + + // mergeCells: true인 컬럼이 없으면 RealGrid 기본 정렬 사용 + if (!hasMergeCells || !groupField) return true; + + const ds = grid.getDataSource?.(); + if (!ds) return true; + const cnt = ds.getRowCount(); + const rows: Record[] = []; + for (let i = 0; i < cnt; i++) { + const r: Record = {}; + for (const f of fieldNames) r[f] = ds.getValue(i, f) as string | number; + rows.push(r); + } + const prev = sortDirRef.current[sortField] ?? "none"; + const setRows = (ds as { setRows?: (r: unknown[]) => void }).setRows?.bind(ds); + + if (prev === "desc") { + const orig = originalRowsRef.current[sortField]; + if (orig && setRows) { + setRows(orig); + setRowData(orig); + } + sortDirRef.current[sortField] = "none"; + return false; + } + if (prev === "none") { + originalRowsRef.current[sortField] = rows.map((r) => ({ ...r })); + } + const sortAsc = prev !== "asc"; + sortDirRef.current[sortField] = sortAsc ? "asc" : "desc"; + const sorted = sortRowsGroupPreserving(rows, sortField, sortAsc, { groupField, orderByField }, col.valueType); + setRows?.(sorted); + setRowData(sorted); + return false; + }; +} + +/** sortPreserveGroupOrder: 그룹 간에는 sortField로 정렬, 그룹 내에서는 원본 순서 유지. */ +function sortRowsGroupPreserving( + rows: Record[], + sortField: string, + sortAsc: boolean, + opt: { groupField: string; orderByField?: string }, + valueType?: ValueType +): Record[] { + const groups = new Map[]>(); + for (const r of rows) { + const k = String(r[opt.groupField] ?? ""); + if (!groups.has(k)) groups.set(k, []); + groups.get(k)!.push(r); + } + + // valueType에 따라 비교 함수 선택 + const compareValue = (val: unknown): number | string => { + if (valueType === ValueType.NUMBER) { + return toNum(val); + } + // 텍스트나 기타 타입은 문자열로 비교 + return String(val ?? ""); + }; + + const rep = (arr: Record[]): number | string => { + const values = arr.map((r) => compareValue(r[sortField])); + if (valueType === ValueType.NUMBER) { + const nums = values.filter((v): v is number => typeof v === "number" && !isNaN(v)); + if (nums.length === 0) return sortAsc ? Infinity : -Infinity; + return sortAsc ? Math.min(...nums) : Math.max(...nums); + } else { + // 문자열 정렬 + const strs = values.filter((v): v is string => typeof v === "string"); + if (strs.length === 0) return sortAsc ? "\uffff" : ""; + strs.sort(); + return sortAsc ? (strs[0] as string) : (strs[strs.length - 1] as string); + } + }; + + const entries = [...groups.entries()].sort((a, b) => { + const valA = rep(a[1]); + const valB = rep(b[1]); + if (typeof valA === "number" && typeof valB === "number") { + return (sortAsc ? 1 : -1) * (valA - valB); + } + // 문자열 비교 + const strA = String(valA); + const strB = String(valB); + return (sortAsc ? 1 : -1) * strA.localeCompare(strB); + }); + + return entries.flatMap(([, arr]) => arr); +} + +/** columnInfo → RealGrid DataField 배열 (computed → valueCallback 포함, setFields용) */ +function buildFieldsFromColumns(columns: { fieldName: string; valueType?: ValueType; computed?: import("../type/DataGrid.type").ComputedFieldConfig }[]) { + return columns.map((col) => { + const field: { fieldName: string; dataType: string; valueCallback?: (prod: unknown, dataRow: number, fieldName: string, fieldNames: string[], values: unknown[]) => number } = { + fieldName: col.fieldName, + dataType: col.valueType === ValueType.NUMBER ? "number" : "text", + }; + if (col.computed) { + const hasFields = "fields" in col.computed && col.computed.fields.length > 0; + const hasNumeratorDenominator = "numeratorFields" in col.computed && col.computed.numeratorFields.length > 0 && col.computed.denominatorFields.length > 0; + if (hasFields || hasNumeratorDenominator) { + field.valueCallback = getComputedValueCallback(col.computed) as (prod: unknown, dataRow: number, fieldName: string, fieldNames: string[], values: unknown[]) => number; + } + } + return field; + }); +} + +/** setDataComparer용 숫자 비교기. setFields 없이 숫자 정렬. */ +function createNumberComparer(dp: RealGridDataProviderComparer): (field: string | number, row1: number, row2: number) => number { + return (field, row1, row2) => { + const get = dp.getValue?.bind(dp); + if (!get) return 0; + const val1 = get(row1, field); + const val2 = get(row2, field); + if (val1 == null && val2 == null) return 0; + if (val1 == null) return 1; + if (val2 == null) return -1; + const num1 = typeof val1 === "number" ? val1 : parseFloat(String(val1)); + const num2 = typeof val2 === "number" ? val2 : parseFloat(String(val2)); + if (isNaN(num1) && isNaN(num2)) return 0; + if (isNaN(num1)) return 1; + if (isNaN(num2)) return -1; + return num1 > num2 ? 1 : num1 < num2 ? -1 : 0; + }; +} + +const TabDataGrid = ({ columnInfo, isExcelImport = true, isExcelExport = true, rows = [], showHeaderCheckbox = false, rowDataMap, setRowDataMap }: TabDataGridProps) => { + // 각 탭별로 그리드 ref와 데이터를 관리 + const gridRefs = useRef>(new Map()); + const fileInputRefs = useRef>(new Map()); + // 각 탭별로 정렬 상태 관리 + const sortDirRefs = useRef>>>(new Map()); + const originalRowsRefs = useRef[]>>>>(new Map()); + const { setMessage: alert } = useAlertStore((state) => state); + const activeTabId = useTabStore((state) => state.activeTabId); + const menuList = useTabStore((state) => state.menuList); + 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; + + // 헤더 텍스트를 fieldName으로 매핑하는 맵 생성 + const headerToFieldNameMap = new Map(); + const columns = columnInfoItem.columns || []; + headers.forEach((headerText) => { + // columns에서 header.text가 일치하는 컬럼 찾기 + const matchedColumn = columns.find( + (col) => col.header?.text === headerText || col.fieldName === headerText + ); + if (matchedColumn?.fieldName) { + headerToFieldNameMap.set(headerText, matchedColumn.fieldName); + } else { + // 매칭되지 않으면 원본 헤더 텍스트 사용 (하위 호환성) + headerToFieldNameMap.set(headerText, headerText); + } + }); + + // 데이터 행 읽기 + 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}`; + // 헤더 텍스트를 fieldName으로 변환 + const fieldName = headerToFieldNameMap.get(headerName) || headerName; + + let cellValue: string | number = ''; + if (cell && cell.v !== undefined && cell.v !== null) { + // 엑셀 셀 타입이 숫자이거나 값이 숫자인 경우 숫자로 변환 + if (cell.t === 'n' || (typeof cell.v === 'number' && !isNaN(cell.v))) { + cellValue = cell.v as number; + } else { + const stringValue = String(cell.v); + // 문자열이지만 숫자로 변환 가능한 경우 숫자로 변환 + const numericValue = Number(stringValue.replace(/,/g, '')); + if (!isNaN(numericValue) && stringValue.trim() !== '') { + cellValue = numericValue; + } else { + cellValue = stringValue; + } + } + } + + if (cellValue !== '' && cellValue !== null && cellValue !== undefined) { + hasData = true; + } + + rowData[fieldName] = 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]); + + // rowDataMap이 변경될 때 그리드에 데이터 반영 (Excel import 등) + useEffect(() => { + columnInfo.forEach((info) => { + const tabKey = info.tabKey; + const gridRef = gridRefs.current.get(tabKey); + const currentData = rowDataMap.get(tabKey); + + if (gridRef?.gridView && gridRef?.dataProvider && currentData && currentData.length > 0) { + setTimeout(() => { + try { + const dp = gridRef.dataProvider as RealGridDataProviderFill | null; + if (dp?.fillJsonData) { + dp.fillJsonData(currentData, { fillMode: "set" }); + } + gridRef.gridView.refresh(); + } catch (error) { + console.error("그리드 데이터 설정 오류:", error); + } + }, 100); + } + }); + }, [rowDataMap, 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; + + // isSplitGrid가 true이고 left, right가 있으면 SplitDataGrid 사용 + if (info.isSplitGrid && info.left && info.right) { + const leftRowData = rowDataMap.get(info.left.tabKey || `${tabKey}_left`) || []; + const rightRowData = rowDataMap.get(info.right.tabKey || `${tabKey}_right`) || []; + + // ColumnInfoList를 ColumnInfo로 변환 + const leftColumnInfo: ColumnInfo = { + columns: info.left.columns || [], + columnLayout: info.left.columnLayout || [] + }; + const rightColumnInfo: ColumnInfo = { + columns: info.right.columns || [], + columnLayout: info.right.columnLayout || [] + }; + + return ( + + { + setRowDataMap(prev => { + const newMap = new Map(prev); + const leftTabKey = info.left?.tabKey || `${tabKey}_left`; + const currentData = newMap.get(leftTabKey) || []; + const newData = typeof updater === 'function' ? updater(currentData) : updater; + newMap.set(leftTabKey, newData); + return newMap; + }); + }} + setRightRowData={(updater) => { + setRowDataMap(prev => { + const newMap = new Map(prev); + const rightTabKey = info.right?.tabKey || `${tabKey}_right`; + const currentData = newMap.get(rightTabKey) || []; + const newData = typeof updater === 'function' ? updater(currentData) : updater; + newMap.set(rightTabKey, newData); + return newMap; + }); + }} + /> + + ); + } + + // 일반 그리드 사용 + if (!info.columns || !info.columnLayout) { + return null; + } + 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 && + + } + {isExcelExport && + + } +
+
+ { + if (el) { + gridRefs.current.set(tabKey, el); + // 그리드가 생성된 직후 fitStyle 설정 + if (el.gridView) { + setTimeout(() => { + const gridView = el.gridView; + if (!gridView) return; + + const currentInfo = columnInfo.find(info => info.tabKey === tabKey); + if (!currentInfo || !currentInfo.columns) return; + + const columns = currentInfo.columns; + + const dp = el.dataProvider as { setFields?: (fields: unknown[]) => void } | undefined; + if (dp?.setFields) { + dp.setFields(buildFieldsFromColumns(columns)); + } + + gridView.setDisplayOptions({ + fitStyle: GridFitStyle.EVEN + }); + gridView.header.height = 60; + + // 헤더 체크박스 표시 여부 설정 + gridView.setOptions({ + checkBar: { visible: showHeaderCheckbox }, + rowIndicator: { visible: false }, + }); + + // footer가 있는 컬럼이 있으면 footer 높이 설정 + const hasFooter = columns.some(col => col.footer); + if (hasFooter) { + // footer가 배열이거나 multiRow가 true인 footer가 있으면 멀티푸터 설정 (여러 행) + const hasArrayFooter = columns.some(col => col.footer && Array.isArray(col.footer)); + const hasMultiRowFooter = columns.some(col => { + const footer = col.footer; + return footer && !Array.isArray(footer) && 'multiRow' in footer && footer.multiRow === true; + }); + if (hasArrayFooter || hasMultiRowFooter) { + // footer 배열의 길이를 확인하여 행 수 결정 + const arrayFooterLengths = columns + .filter(col => col.footer && Array.isArray(col.footer)) + .map(col => (col.footer as unknown[]).length); + const maxFooterLength = arrayFooterLengths.length > 0 + ? Math.max(...arrayFooterLengths) + : 2; // 기본값 2 + const footerHeights = Array.from({ length: maxFooterLength }, () => ({ height: 40 })); + gridView.setFooters?.(footerHeights); + } else { + gridView.footer.height = 40; + } + } + + gridView.setOptions({ + summaryMode: "aggregate" + }); + + // footerUserSpans가 있는 경우 setColumnLayout으로 설정 + const checkFooterUserSpans = (layout: unknown[]): boolean => { + return layout.some(item => { + if (typeof item === 'string') return false; + if (item && typeof item === 'object' && 'footerUserSpans' in item) return true; + if (item && typeof item === 'object' && 'items' in item && Array.isArray(item.items)) { + return checkFooterUserSpans(item.items); + } + return false; + }); + }; + + if (checkFooterUserSpans(columnLayout)) { + // setColumnLayout은 그리드가 완전히 렌더링된 후 호출해야 함 + setTimeout(() => { + if (gridView && columnLayout) { + gridView.setColumnLayout?.(columnLayout); + } + }, 100); + } + + // footer 설정 (배열 또는 단일 객체 모두 처리) + // setFooters 호출 후 footer 배열 설정 + setTimeout(() => { + const currentGridView = gridView; + if (!currentGridView || !currentGridView.columnByName) { + console.warn('GridView not available for footer setup'); + return; + } + + // footer가 배열인 컬럼이 있으면 setColumns를 다시 호출하여 footer 설정 + const hasArrayFooter = columns.some(col => col.footer && Array.isArray(col.footer)); + if (hasArrayFooter) { + // 모든 컬럼 정보를 다시 가져와서 footer 배열을 포함하여 setColumns 호출 + const columnsWithFooter: RealGridColumnInfo[] = columns.map(col => { + const colInfo: RealGridColumnInfo = { + name: col.name, + fieldName: col.fieldName, + header: col.header, + editable: col.editable, + valueType: col.valueType, + dataFormat: col.dataFormat, + ...(col.mergeCells && { mergeRule: { criteria: 'value' as const } }), + }; + + if (col.footer) { + if (Array.isArray(col.footer)) { + colInfo.footer = col.footer; + } else { + colInfo.footer = { + text: col.footer.text, + expression: col.footer.expression, + numberFormat: col.footer.numberFormat, + }; + } + } + + return colInfo; + }); + + try { + (currentGridView as GridViewWithSetColumns).setColumns?.(columnsWithFooter); + + // setColumns 호출 후 footer 배열의 valueCallback을 다시 설정 + setTimeout(() => { + columns.forEach(col => { + if (!col.footer || !Array.isArray(col.footer)) return; + + try { + const column = currentGridView.columnByName(col.name); + if (!column) return; + + // footer 배열의 각 요소에 valueCallback 설정 + col.footer.forEach((footerItem, index) => { + if (footerItem.valueCallback && column.footer) { + // footer가 배열인 경우 + if (Array.isArray(column.footer) && column.footer[index]) { + (column.footer[index] as FooterItemWithCallback).valueCallback = footerItem.valueCallback; + } + } + }); + } catch (e) { + console.warn(`Error setting footer array valueCallback for column ${col.name}:`, e); + } + }); + }, 50); + } catch (e) { + console.warn('Failed to set columns with footer array:', e); + } + } + + // footer가 단일 객체이고 valueCallback이 있는 경우 + columns.forEach(col => { + if (!col.footer || Array.isArray(col.footer)) return; + + try { + const column = currentGridView.columnByName(col.name); + if (!column || !column.footer) return; + + const footerObj = col.footer as { valueCallback?: (grid: unknown, column: unknown, footerIndex: number, columnFooter: unknown, value: unknown) => unknown }; + if (footerObj.valueCallback) { + (column.footer as { valueCallback?: typeof footerObj.valueCallback }).valueCallback = footerObj.valueCallback; + } + } catch (e) { + console.warn(`Error setting footer valueCallback for column ${col.name}:`, e); + } + }); + }, 200); + + // 고정 컬럼 설정 + if (currentInfo.fixedColumns !== undefined && currentInfo.fixedColumns > 0) { + gridView.setFixedOptions({ + colCount: currentInfo.fixedColumns, + }); + } + + // 각 탭별 ref 초기화 + if (!sortDirRefs.current.has(tabKey)) { + sortDirRefs.current.set(tabKey, { current: {} }); + } + if (!originalRowsRefs.current.has(tabKey)) { + originalRowsRefs.current.set(tabKey, { current: {} }); + } + + const sortDirRef = sortDirRefs.current.get(tabKey)!; + const originalRowsRef = originalRowsRefs.current.get(tabKey)!; + + // mergeCells: true인 컬럼이 있으면 onSorting에서 클라이언트 정렬로 처리 + const hasMergeCells = columns.some((col) => col.mergeCells === true); + + // setDataComparer로 숫자 정렬 (mergeCells가 있으면 onSorting에서 클라이언트 정렬로 처리) + if (el.dataProvider) { + const dp = el.dataProvider as RealGridDataProviderComparer; + if (typeof dp.setDataComparer === "function" && typeof dp.getValue === "function") { + const numberComparer = createNumberComparer(dp); + const seen = new Set(); + columns.forEach((col) => { + if (hasMergeCells) return; // mergeCells가 있으면 모든 컬럼을 클라이언트 정렬로 처리 + if (col.valueType === ValueType.NUMBER && col.fieldName && !seen.has(col.fieldName)) { + seen.add(col.fieldName); + try { + dp.setDataComparer!(col.fieldName, numberComparer); + } catch (e) { + console.warn("setDataComparer:", e); + } + } + }); + } + } + + // mergeCells: true인 컬럼이 있으면 onSorting에서 그룹 내 순서 유지 정렬 적용 + const setRowData = (updater: React.SetStateAction[]>) => { + setRowDataMap(prev => { + const newMap = new Map(prev); + const currentData = newMap.get(tabKey) || []; + const newData = typeof updater === 'function' ? updater(currentData) : updater; + newMap.set(tabKey, newData); + return newMap; + }); + }; + + // 그리드 DOM 요소 찾기 (나중에 화살표 업데이트에 사용) + // 탭별로 그리드 요소를 찾기 위해 data-tab-key 사용 + const findGridElement = () => { + const tabContent = document.querySelector(`[data-tab-key="${tabKey}"]`); + return tabContent?.querySelector('.rg-grid, [data-grid-id]') as HTMLElement | null; + }; + + const onSortingHandler = createOnSortingHandler(columns, setRowData, sortDirRef, originalRowsRef, findGridElement); + (gridView as { onSorting?: (g: unknown, f: unknown[], d: unknown[]) => boolean }).onSorting = onSortingHandler; + }, 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/packages/rsone/components/grid/css/DataGrid.css b/packages/rsone/components/grid/css/DataGrid.css new file mode 100644 index 000000000..4469b8490 --- /dev/null +++ b/packages/rsone/components/grid/css/DataGrid.css @@ -0,0 +1,113 @@ +/* RealGrid2 폰트 설정 - 루트 폰트와 일치 */ +.rg-root, +.rg-grid, +.rg-body, +.rg-cell, +.rg-header, +.rg-head, +.rg-rowbarcontainer { + font-family: "Noto Sans KR", sans-serif !important; + font-size: 12px !important; + font-weight: 500; + color: #333 !important; +} + +/* RealGrid2 컨테이너 스타일 */ +.rg-root { + border-radius: 0.5rem !important; + overflow: hidden !important; + border: 1px solid #dbe2e6 !important; +} + +.rg-grid { + border-radius: 0.5rem !important; + /* box-shadow: 0 0 10px -1px rgba(0, 0, 0, 0.06); */ +} + +/* 헤더 스타일 */ +.rg-header, +.rg-head, +.rg-foot, +.rg-footer, +.rg-fixed-footer { + background-color: #f5f7fa !important; + color: #333 !important; +} + +/* 헤더 셀 스타일 */ +.rg-header .rg-cell, +.rg-head .rg-cell { + background-color: #f5f7fa !important; +} + +/* 헤더 필터링 영역 위치 조정 - 오른쪽 여백 추가 */ +.rg-header-filtering { + left: auto !important; + right: 8px !important; +} + +/* 그룹 헤더 스타일 */ +.rg-header-group, +.rg-header-group-cell { + background-color: #f5f7fa !important; +} + +/* 첫 번째 헤더 셀 - 왼쪽 상단 모서리 */ +.rg-header .rg-cell:first-child, +.rg-head .rg-cell:first-child { + border-top-left-radius: 8px !important; +} + +/* 마지막 헤더 셀 - 오른쪽 상단 모서리 */ +.rg-header .rg-cell:last-child, +.rg-head .rg-cell:last-child { + border-top-right-radius: 8px !important; +} + +/* border color 통일 */ +.rg-header .rg-table tr td, +.rg-head .rg-table tr td:not(:first-child), +.rg-body .rg-table tr td, +.rg-rowbarcontainer .rg-table tr td:not(:first-child) { + border-left: 1px solid #dbe2e6 !important; + border-bottom: 1px solid #dbe2e6 !important; + border-right: none !important; +} +.rg-rowindicator-head, +.rg-rowbarcontainer .rg-table tr td:first-child { + border-right: none !important; + border-bottom: 1px solid #dbe2e6 !important; + border-left: none !important; +} +.rg-footer .rg-table tr td, +.rg-fixed-footer .rg-table tr td, +.rg-foot .rg-table tr td { + border-right: 1px solid #dbe2e6 !important; + border-top: 1px solid #dbe2e6 !important; +} + +.rg-rowbarcontainer .rg-table tr td { + background-color: #f5f7fa !important; +} + +/* 푸터 마지막 요소 - 오른쪽 border 제거 */ +.rg-footer .rg-table tr td:last-child { + border-right: none !important; +} + +/* 고정 컬럼 바 */ +.rg-fixed-column-bar { + background-color: #ccc !important; + border-right: 1px solid #ccc !important; +} + +/* 읽기 전용 cell */ +.rg-cell-readonly { + background-color: #f0f0f0 !important; + color: #666 !important; +} + +/* 소계, 합계, 율(%) cell */ +.rg-cell-total { + background-color: #e3eaf2 !important; +} \ No newline at end of file diff --git a/packages/rsone/components/index.ts b/packages/rsone/components/index.ts new file mode 100644 index 000000000..767d0dc79 --- /dev/null +++ b/packages/rsone/components/index.ts @@ -0,0 +1,8 @@ +import "./grid/css/DataGrid.css"; + +export { default as DataGrid } from "./grid/DataGrid"; +export { default as TabDataGrid } from "./grid/TabDataGrid"; +export * from "./type/DataGrid.type"; +export * from "./store/alert-store"; +export * from "./store/tab-store"; +export { AlertDialog } from "./ui/alert-dialog"; \ No newline at end of file diff --git a/packages/rsone/components/store/alert-store.ts b/packages/rsone/components/store/alert-store.ts new file mode 100644 index 000000000..3313ed87d --- /dev/null +++ b/packages/rsone/components/store/alert-store.ts @@ -0,0 +1,25 @@ +import { create } from "zustand"; + +export type AlertState = { + message: string; + callback?: () => void; +}; + +export type AlertActions = { + setMessage: (message: string, callback?: () => void) => void; +}; + +export type AlertStore = AlertState & AlertActions; + +interface AlertStoreState extends AlertStore {} + +const useAlertStoreHook = create((set) => ({ + message: "", + callback: undefined, + setMessage: (message: string, callback?: () => void) => + set(() => ({ message, callback })), +})); + +export const useAlertStore = useAlertStoreHook; +export const getAlertStoreState = () => useAlertStoreHook.getState(); + diff --git a/packages/rsone/components/store/tab-store.ts b/packages/rsone/components/store/tab-store.ts new file mode 100644 index 000000000..def86ecb3 --- /dev/null +++ b/packages/rsone/components/store/tab-store.ts @@ -0,0 +1,137 @@ +import { create } from "zustand"; + +// MenuItem 타입 정의 (필요한 필드만 포함) +export interface MenuItem { + menuCd: number; + menuKornNm: string; + menuEngNm: string; + parentId: number | null; + sortSeq: number; + iconNm?: string; + children: MenuItem[] | null; + levelNo: number; + urlPath: string; + menuLnkgTypeCd: string; + menuLnkgSn: number; + menuPath: string; + qrcd: string; + useYn: string; + otptYn: string; + newWndwYn: string; + requiredUrlPath: boolean; + menuHlpCn: string; +} + +export interface Tab { + id: string; // 고유 ID (menuEngNm) + label: string; // 탭에 표시될 이름 + path: string; // 메뉴 경로 + urlPath: string; // url 경로 + parentEngNm: string; // 부모 menuEngNm + menuItem: MenuItem; // 메뉴 정보 +} + +interface TabStore { + tabs: Tab[]; + activeTabId: string | null; + menuList: MenuItem[] | null; + addTab: (menuItem: MenuItem) => boolean; + removeTab: (tabId: string) => void; + setActiveTab: (tabId: string) => void; + setMenuList: (menuList: MenuItem[] | null) => void; + closeAllTabs: () => void; + closeOtherTabs: (tabId: string) => void; +} + +const useTabStoreHook = create((set, get) => ({ + tabs: [], + activeTabId: null, + menuList: null, + addTab: (menuItem: MenuItem) => { + const { tabs, menuList } = get(); + const tabId = menuItem.menuEngNm || ''; + + // 이미 존재하는 탭인지 확인 + const existingTab = tabs.find((tab) => tab.id === tabId); + + if (existingTab) { + // 이미 존재하면 활성화만 + set({ activeTabId: tabId }); + return true; + } + + if (tabs.length < 100) { + const newTab: Tab = { + id: tabId, + label: menuItem.menuKornNm, + path: menuItem.menuPath, + urlPath: menuItem.urlPath, + parentEngNm: menuList?.find((m) => m.menuCd === menuItem.parentId)?.menuEngNm || '', + menuItem, + }; + + set({ + tabs: [...tabs, newTab], + activeTabId: tabId, + }); + + return true; + } else { + return false; + } + }, + removeTab: (tabId: string): void => { + const { tabs, activeTabId } = get(); + const newTabs = tabs.filter((tab) => tab.id !== tabId); + + // 닫는 탭이 현재 활성 탭이면 다른 탭으로 전환 + let newActiveTabId = activeTabId; + if (activeTabId === tabId) { + const closedTabIndex = tabs.findIndex((tab) => tab.id === tabId); + if (newTabs.length > 0) { + // 닫힌 탭의 이전 탭을 활성화, 없으면 다음 탭 + if (closedTabIndex > 0) { + const prevTab = tabs[closedTabIndex - 1]; + newActiveTabId = prevTab ? prevTab.id : null; + } else { + const firstTab = newTabs[0]; + newActiveTabId = firstTab ? firstTab.id : null; + } + } else { + newActiveTabId = null; + } + } + + set({ + tabs: newTabs, + activeTabId: newActiveTabId, + }); + }, + setActiveTab: (tabId: string) => { + set({ activeTabId: tabId }); + }, + setMenuList: (menuList: MenuItem[] | null) => { + set({ menuList }); + }, + closeAllTabs: () => { + set({ + tabs: [], + activeTabId: null, + }); + }, + closeOtherTabs: (tabId: string) => { + const { tabs } = get(); + const tabToKeep = tabs.find((tab) => tab.id === tabId); + + if (tabToKeep) { + set({ + tabs: [tabToKeep], + activeTabId: tabId, + }); + } + }, +})); + +export const useTabStore = useTabStoreHook; +export const getTabStoreState = () => useTabStoreHook.getState(); + diff --git a/packages/rsone/components/type/DataGrid.type.ts b/packages/rsone/components/type/DataGrid.type.ts new file mode 100644 index 000000000..2574b4d1b --- /dev/null +++ b/packages/rsone/components/type/DataGrid.type.ts @@ -0,0 +1,174 @@ +import React from "react"; +import { ValueType } from "realgrid"; + +// RealGrid 타입 정의 +/** setColumns에 전달하는 컬럼 정보 (footer 배열/단일 객체 포함) */ +export type RealGridColumnInfo = { + name: string; + fieldName: string; + header: { text: string }; + editable?: boolean; + valueType?: import("realgrid").ValueType; + dataFormat?: string; + mergeRule?: { criteria: 'value' }; + footer?: { + text?: string; + expression?: 'sum' | 'avg' | 'count' | 'max' | 'min'; + numberFormat?: string; + valueCallback?: (grid: GridView, column: DataColumn, footerIndex: number, columnFooter: ColumnFooter, value: number | string | undefined) => number | string; + } | Array<{ + text?: string; + expression?: 'sum' | 'avg' | 'count' | 'max' | 'min'; + numberFormat?: string; + valueCallback?: (grid: GridView, column: DataColumn, footerIndex: number, columnFooter: ColumnFooter, value: number | string | undefined) => number | string; + }>; +}; + +/** setColumns 메서드가 있는 GridView */ +export type GridViewWithSetColumns = GridView & { setColumns?: (columns: RealGridColumnInfo[]) => void }; + +/** footer 배열 요소에 valueCallback 할당용 */ +export type FooterItemWithCallback = { valueCallback?: (grid: GridView, column: DataColumn, footerIndex: number, columnFooter: ColumnFooter, value: number | string | undefined) => number | string }; + +export type GridView = { + getSummary(field: string, summary: 'sum' | 'avg' | 'count' | 'max' | 'min'): number | undefined; + getDataSource(): { + getRowCount(): number; + getValue(rowIndex: number, field: string): number | string; + getDataRowId(dataRow: number): number | string; + }; + getDataRow(itemIndex: number): number; +}; + +export type MergeCallback = (grid: GridView, column: DataColumn, itemIndex: number, value: number | string | null | undefined) => number | null; + +export type DataColumn = { + name: string; + fieldName: string; +}; + +export type ColumnFooter = { + text?: string; + expression?: string; + numberFormat?: string; +}; + +/** 정렬 시 같은 그룹 내에서는 orderByField 순서 유지, 그룹 간에는 정렬 필드로 비교. */ +export type SortPreserveGroupOrder = { + groupField: string; + orderByField?: string; +}; + +/** RealGrid valueCallback 시그니처 (계산 필드용) */ +export type DataFieldValueCallback = ( + prod: unknown, + dataRow: number, + fieldName: string, + fieldNames: string[], + values: unknown[] +) => number | string | undefined; + +/** 계산 필드 설정 (computedFieldCallbacks.ts 참고) */ +export type ComputedFieldConfig = + | { fields: string[]; type: "sum" | "avg" } + | { numeratorFields: string[]; denominatorFields: string[]; type: "pblswrgPopltnSuprtRate" } + | { numeratorFields: string[]; denominatorFields: string[]; type: "adtrtPopltnSuprtRate" } + | { numeratorFields: string[]; denominatorFields: string[]; thirdFields: string[], type: "swrgInstlrtRate" }; + +export type ColumnConfig = { + name: string; + fieldName: string; + header: { text: string }; + /** 계산 필드: fields 합계/평균 등. valueCallback으로 dataProvider.setFields 시 전달 */ + computed?: ComputedFieldConfig; + editable?: boolean; // 셀 편집 가능 여부 + /** 그리드에서 컬럼 숨김 (수식용 필드 등) */ + visible?: boolean; + mergeCells?: boolean; + sortdisable?: boolean; + sortPreserveGroupOrder?: SortPreserveGroupOrder; + valueType?: ValueType; + dataFormat?: string; + footer?: { + text?: string; + expression?: 'sum' | 'avg' | 'count' | 'max' | 'min'; + numberFormat?: string; + valueCallback?: (grid: GridView, column: DataColumn, footerIndex: number, columnFooter: ColumnFooter, value: number | string | undefined) => number | string; + multiRow?: boolean; // 여러 행 footer 사용 여부 + } | Array<{ + text?: string; + expression?: 'sum' | 'avg' | 'count' | 'max' | 'min'; + numberFormat?: string; + valueCallback?: (grid: GridView, column: DataColumn, footerIndex: number, columnFooter: ColumnFooter, value: number | string | undefined) => number | string; + }>, // RealGrid에서 footer를 배열로 설정할 수 있음 +}; + +export type FooterUserSpan = { + rowspan?: number; // 병합할 행 수 + colspan?: number; // 병합할 컬럼 수 + text?: string; // 표시할 텍스트 +}; + +export type ColumnLayoutItem = string | { + name: string; + hideChildHeaders?: boolean; + header: { text: string }; + items: (string | ColumnLayoutItem)[]; + footerUserSpans?: FooterUserSpan[]; // footer 병합 설정 +} | { + column: string; // 컬럼 이름 (footerUserSpans 사용 시) + footerUserSpans?: FooterUserSpan[]; // footer 병합 설정 +}; + +export type ColumnInfo = { + columns: ColumnConfig[]; + columnLayout: ColumnLayoutItem[]; +}; + +export type ColumnInfoList = { + tabKey: string; // 탭을 식별하는 고유 키 (필수), server(api)에 전송할 변수 값 + columns?: ColumnConfig[]; // 일반 그리드용 (isSplitGrid가 false일 때) + columnLayout?: ColumnLayoutItem[]; // 일반 그리드용 (isSplitGrid가 false일 때) + rowStartNum?: number; + fixedColumns?: number; + tabLabel?: string; // 탭에 표시될 레이블 + isSplitGrid?: boolean; // 5:5 분리형 그리드 유무 + left?: ColumnInfoList; // SplitDataGrid용 왼쪽 그리드 정보 + right?: ColumnInfoList; // SplitDataGrid용 오른쪽 그리드 정보 +} + +export type DataGridProps = { + columnInfo: ColumnInfo; + rowStartNum?: number; // 헤더 행 개수 (1부터 시작, 예: 2면 1행과 2행까지 헤더로 간주) + isExcelImport?: boolean; // 엑셀 가져오기 유무 + isExcelExport?: boolean; + onRowsChange?: (rows: Record[]) => void; + fixedColumns?: number; // 고정할 컬럼 개수 (좌측부터) + showHeaderCheckbox?: boolean; // 헤더 체크박스 표시 여부 (기본값: true) + rowData: Record[]; + setRowData: React.Dispatch[]>>; +}; + +export type TabDataGridProps = { + columnInfo: ColumnInfoList[]; + rows?: Record[]; + isExcelImport?: boolean; // 엑셀 가져오기 유무 + isExcelExport?: boolean; + onRowsChange?: (rows: Record[]) => void; + showHeaderCheckbox?: boolean; // 헤더 체크박스 표시 여부 (기본값: true) + rowDataMap: Map[]>; + setRowDataMap: React.Dispatch[]>>>; + isSplitGrid?: boolean; // 5:5 분리형 그리드 유무 +}; + +export type SplitDataGridProps = { + leftColumnInfo: ColumnInfo; + rightColumnInfo: ColumnInfo; + leftFixedColumns?: number; + rightFixedColumns?: number; + showHeaderCheckbox?: boolean; + leftRowData: Record[]; + rightRowData: Record[]; + setLeftRowData: React.Dispatch[]>>; + setRightRowData: React.Dispatch[]>>; +}; \ No newline at end of file diff --git a/packages/rsone/components/type/computedFieldCallbacks.ts b/packages/rsone/components/type/computedFieldCallbacks.ts new file mode 100644 index 000000000..362d0da7f --- /dev/null +++ b/packages/rsone/components/type/computedFieldCallbacks.ts @@ -0,0 +1,99 @@ +import type { DataFieldValueCallback, ComputedFieldConfig } from './DataGrid.type' + +/** 값 → 숫자 변환 (null/undefined/빈문자열 → 0) */ +function toNum(v: unknown): number { + if (v == null || v === "") return 0; + if (typeof v === "number") return isNaN(v) ? 0 : v; + const n = parseFloat(String(v).replace(/,/g, "")); + return isNaN(n) ? 0 : n; +} + +/** 합계용 valueCallback 생성 */ +export function createSumValueCallback(fieldNames: string[]): DataFieldValueCallback { + return (_prod, _dataRow, _fieldName, fieldNamesArr, values) => { + let sum = 0; + for (const f of fieldNames) { + const idx = fieldNamesArr.indexOf(f); + if (idx >= 0) sum += toNum(values[idx]); + } + return sum; + }; +} + +/** 평균용 valueCallback 생성 */ +export function createAvgValueCallback(fieldNames: string[]): DataFieldValueCallback { + return (_prod, _dataRow, _fieldName, fieldNamesArr, values) => { + let sum = 0; + let count = 0; + for (const f of fieldNames) { + const idx = fieldNamesArr.indexOf(f); + if (idx >= 0) { + const val = toNum(values[idx]); + sum += val; + count += 1; + } + } + return count > 0 ? sum / count : 0; + }; +} + +// 공공하수처리구역인구보급률(%), 고도처리인구보급률(%) valueCallback +export function createCoverageRateValueCallback( + numeratorFields: string[], + denominatorFields: string[] +): DataFieldValueCallback { + return (_prod, _dataRow, _fieldName, fieldNamesArr, values) => { + const getSum = (fields: string[]) => { + let sum = 0; + for (const f of fields) { + const idx = fieldNamesArr.indexOf(f); + if (idx >= 0) sum += toNum(values[idx]); + } + return sum; + }; + const total2 = getSum(numeratorFields); + const total1 = getSum(denominatorFields); + return total1 === 0 ? 0 : (total2 / total1) * 100; + }; +} + +// 하수도 설치율(%) valueCallback +export function createSwrgInstlrtRateValueCallback( + numeratorFields: string[], + denominatorFields: string[], + thirdFields: string[], +): DataFieldValueCallback { + return (_prod, _dataRow, _fieldName, fieldNamesArr, values) => { + const getSum = (fields: string[]) => { + let sum = 0; + for (const f of fields) { + const idx = fieldNamesArr.indexOf(f); + if (idx >= 0) sum += toNum(values[idx]); + } + return sum; + }; + const sum1 = getSum(numeratorFields); + const sum2 = getSum(denominatorFields); + + const piplSupprt = toNum(values[fieldNamesArr.indexOf(thirdFields[0] || 'piplSupprt')]); + + const total1 = (sum1 === 0 ? 0 : Math.round(sum2 / sum1 * 100)); + + return total1 * piplSupprt / 100; + }; +} + +/** 연산 타입에 맞는 valueCallback 반환 */ +export function getComputedValueCallback(config: ComputedFieldConfig): DataFieldValueCallback { + switch (config.type) { + case 'sum': + return createSumValueCallback(config.fields); + case 'avg': + return createAvgValueCallback(config.fields); + case 'pblswrgPopltnSuprtRate': + case 'adtrtPopltnSuprtRate': + return createCoverageRateValueCallback(config.numeratorFields, config.denominatorFields); + case 'swrgInstlrtRate': + return createSwrgInstlrtRateValueCallback(config.numeratorFields, config.denominatorFields, config.thirdFields); + } +} diff --git a/packages/rsone/components/ui/alert-dialog.tsx b/packages/rsone/components/ui/alert-dialog.tsx new file mode 100644 index 000000000..e6227ac3d --- /dev/null +++ b/packages/rsone/components/ui/alert-dialog.tsx @@ -0,0 +1,34 @@ +import { + Dialog, + DialogContent, + DialogFooter, + DialogClose, + Button, +} from "@common/components"; +import { useAlertStore } from "../store/alert-store"; + +function AlertDialog() { + const { message, callback, setMessage } = useAlertStore((state) => state); + const handleOpenChange = () => { + setMessage("", undefined); + callback?.(); + }; + return ( + + +
+

+ {message} +

+
+ + + + + +
+
+ ); +} + +export { AlertDialog }; \ No newline at end of file diff --git a/packages/rsone/package.json b/packages/rsone/package.json new file mode 100644 index 000000000..b5e7f6c3d --- /dev/null +++ b/packages/rsone/package.json @@ -0,0 +1,41 @@ +{ + "name": "@rsone/components", + "version": "1.0.0", + "private": true, + "sideEffects": false, + "type": "module", + "main": "./dist/index.js", + "module": "./dist/index.mjs", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "require": "./dist/index.js" + }, + "./index.css": "./dist/index.css" + }, + "files": [ + "dist/**" + ], + "scripts": { + "build": "tsup", + "clean": "rm -rf dist" + }, + "dependencies": { + "@common/components": "workspace:*", + "realgrid": "^2.9.5", + "realgrid-react": "^0.9.7", + "jszip": "^3.10.1", + "xlsx": "^0.18.5", + "zustand": "^5.0.3" + }, + "peerDependencies": { + "react": "^19.2.1", + "react-dom": "^19.2.1" + }, + "devDependencies": { + "tsup": "8.3.6", + "typescript": "~5.6.2" + } +} \ No newline at end of file diff --git a/packages/rsone/tsconfig.build.json b/packages/rsone/tsconfig.build.json new file mode 100644 index 000000000..adc74fce3 --- /dev/null +++ b/packages/rsone/tsconfig.build.json @@ -0,0 +1,10 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "./tsconfig.json", + "compilerOptions": { + "target": "esnext", + "module": "esnext", + "jsx": "react-jsx", + "incremental": false + } +} \ No newline at end of file diff --git a/packages/rsone/tsconfig.json b/packages/rsone/tsconfig.json new file mode 100644 index 000000000..cd23787e0 --- /dev/null +++ b/packages/rsone/tsconfig.json @@ -0,0 +1,38 @@ +{ + "$schema": "https://json.schemastore.org/tsconfig", + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "target": "ES2020", + "lib": [ + "ES2020", + "DOM", + "DOM.Iterable" + ], + "module": "ESNext", + "skipLibCheck": true, + "moduleResolution": "bundler", + "isolatedModules": true, + "jsx": "react-jsx", + "noEmit": false, + "incremental": true, + "strict": true, + "baseUrl": ".", + "paths": { + "@/*": [ + "components/*" + ], + "@common/components": [ + "../common/components/src" + ] + } + }, + "exclude": [ + "**/node_modules", + "**/.*/*", + "dist" + ], + "include": [ + "**/*.ts", + "**/*.tsx" + ] +} \ No newline at end of file diff --git a/packages/rsone/tsup.config.ts b/packages/rsone/tsup.config.ts new file mode 100644 index 000000000..dcb9e8b62 --- /dev/null +++ b/packages/rsone/tsup.config.ts @@ -0,0 +1,17 @@ +import path from "path"; +import { defineConfig } from "tsup"; + +export default defineConfig((options) => ({ + entry: ["components/index.ts"], + splitting: true, + treeshake: true, + clean: true, + dts: true, + format: ["esm"], + external: ["react", "react-dom", "realgrid", "realgrid-react"], + platform: "browser", + target: ["es2020"], + tsconfig: path.resolve(__dirname, "tsconfig.build.json"), + sourcemap: !options.watch, + minify: !options.watch, +})); \ No newline at end of file diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a17a827c5..2a3d2ab29 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -170,10 +170,10 @@ importers: version: 5.28.5 eslint: specifier: ^9.0.0 - version: 9.39.2(jiti@1.21.7) + version: 9.39.2(jiti@2.6.1) eslint-config-next: specifier: 15.4.0 - version: 15.4.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + version: 15.4.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) postcss: specifier: ^8.4.49 version: 8.5.6 @@ -897,6 +897,9 @@ importers: '@common/config': specifier: workspace:* version: link:../../packages/common/config + '@faker-js/faker': + specifier: ^9.9.0 + version: 9.9.0 '@fontsource-variable/noto-sans-kr': specifier: ^5.2.5 version: 5.2.9 @@ -936,6 +939,9 @@ importers: '@radix-ui/react-tooltip': specifier: ^1.1.8 version: 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.7))(@types/react@19.2.7)(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + '@rsone/components': + specifier: workspace:* + version: link:../../packages/rsone '@tanstack/react-query': specifier: ^5.67.1 version: 5.90.12(react@19.2.3) @@ -1032,10 +1038,10 @@ importers: version: 5.28.5 eslint: specifier: ^9 - version: 9.39.2(jiti@2.6.1) + version: 9.39.2(jiti@1.21.7) eslint-config-next: specifier: 15.1.7 - version: 15.1.7(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3) + version: 15.1.7(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) postcss: specifier: ^8 version: 8.5.6 @@ -1626,6 +1632,40 @@ importers: specifier: ^5.3.3 version: 5.9.3 + packages/rsone: + dependencies: + '@common/components': + specifier: workspace:* + version: link:../common/components + jszip: + specifier: ^3.10.1 + version: 3.10.1 + react: + specifier: ^19.2.1 + version: 19.2.3 + react-dom: + specifier: ^19.2.1 + version: 19.2.3(react@19.2.3) + realgrid: + specifier: ^2.9.5 + version: 2.9.6 + realgrid-react: + specifier: ^0.9.7 + version: 0.9.8(react-dom@19.2.3(react@19.2.3))(react@19.2.3) + xlsx: + specifier: ^0.18.5 + version: 0.18.5 + zustand: + specifier: ^5.0.3 + version: 5.0.9(@types/react@19.2.7)(immer@10.2.0)(react@19.2.3)(use-sync-external-store@1.6.0(react@19.2.3)) + devDependencies: + tsup: + specifier: 8.3.6 + version: 8.3.6(jiti@2.6.1)(postcss@8.5.6)(typescript@5.6.3) + typescript: + specifier: ~5.6.2 + version: 5.6.3 + packages: '@adobe/css-tools@4.4.4': @@ -4999,6 +5039,7 @@ packages: glob@10.5.0: resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me hasBin: true globals@14.0.0: @@ -10279,6 +10320,26 @@ snapshots: optionalDependencies: source-map: 0.7.6 + eslint-config-next@15.1.7(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3): + dependencies: + '@next/eslint-plugin-next': 15.1.7 + '@rushstack/eslint-patch': 1.15.0 + '@typescript-eslint/eslint-plugin': 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + '@typescript-eslint/parser': 8.49.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) + eslint: 9.39.2(jiti@1.21.7) + eslint-import-resolver-node: 0.3.9 + eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@1.21.7)) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.49.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@1.21.7)) + eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.2(jiti@1.21.7)) + eslint-plugin-react: 7.37.5(eslint@9.39.2(jiti@1.21.7)) + eslint-plugin-react-hooks: 5.2.0(eslint@9.39.2(jiti@1.21.7)) + optionalDependencies: + typescript: 5.9.3 + transitivePeerDependencies: + - eslint-import-resolver-webpack + - eslint-plugin-import-x + - supports-color + eslint-config-next@15.1.7(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3): dependencies: '@next/eslint-plugin-next': 15.1.7 @@ -10299,26 +10360,6 @@ snapshots: - eslint-plugin-import-x - supports-color - eslint-config-next@15.4.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3): - dependencies: - '@next/eslint-plugin-next': 15.4.0 - '@rushstack/eslint-patch': 1.15.0 - '@typescript-eslint/eslint-plugin': 8.49.0(@typescript-eslint/parser@8.49.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - '@typescript-eslint/parser': 8.49.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3) - eslint: 9.39.2(jiti@1.21.7) - eslint-import-resolver-node: 0.3.9 - eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.2(jiti@1.21.7)) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.49.0(eslint@9.39.2(jiti@1.21.7))(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.2(jiti@1.21.7)) - eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.2(jiti@1.21.7)) - eslint-plugin-react: 7.37.5(eslint@9.39.2(jiti@1.21.7)) - eslint-plugin-react-hooks: 5.2.0(eslint@9.39.2(jiti@1.21.7)) - optionalDependencies: - typescript: 5.9.3 - transitivePeerDependencies: - - eslint-import-resolver-webpack - - eslint-plugin-import-x - - supports-color - eslint-config-next@15.4.0(eslint@9.39.2(jiti@2.6.1))(typescript@5.9.3): dependencies: '@next/eslint-plugin-next': 15.4.0