diff --git a/apps/st/src/components/statistics/review/SewagePipeRvw.tsx b/apps/st/src/components/statistics/review/SewagePipeRvw.tsx index 3bd69c42d..ccfc62355 100644 --- a/apps/st/src/components/statistics/review/SewagePipeRvw.tsx +++ b/apps/st/src/components/statistics/review/SewagePipeRvw.tsx @@ -1,25 +1,25 @@ import { useState, useMemo } from "react"; -// import { TabDataGrid } from "@rsone/components"; 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 { useAlertStore } from "@/shared/lib"; +import { LoadingWithAnimation } from "@common/components"; -import { sewagePipeReviewApi } from "@/entities/statistics/api/statistics-sewage-pipe-review-service"; +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) => ({ @@ -32,40 +32,45 @@ const SewagePipeRvw = () => { const handleReset = () => setRowDataMap(new Map()); -const handleSearch = async (values: SearchFormValues) => { - try { - setIsLoading(true); - const response = await sewagePipeReviewApi.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; - - console.log("추출된 리스트 (rawList):", rawList); - - const newRowDataMap = new Map(); - - newRowDataMap.set('a', toGridRows(rawList)); - - setRowDataMap(newRowDataMap); - } else { - handleReset(); - alert(response.message || "데이터가 없습니다."); + const handleSearch = async (values: SearchFormValues) => { + if (!values.sido || values.sido === 'ALL') { + alert('시도를 선택해주세요.'); + return; } - } catch (error) { - console.error("조회 에러:", error); - } finally { - setIsLoading(false); - } -}; + + 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 ( - <> - +
+ +
{}} /> @@ -74,13 +79,21 @@ const handleSearch = async (values: SearchFormValues) => { {}} />
+ {isLoading && ( +
+
+ +
+
+ )} + - +
); }; diff --git a/apps/st/src/components/util/column/review/SewagePipeRvwColumn.ts b/apps/st/src/components/util/column/review/SewagePipeRvwColumn.ts index 74cbb999b..732752343 100644 --- a/apps/st/src/components/util/column/review/SewagePipeRvwColumn.ts +++ b/apps/st/src/components/util/column/review/SewagePipeRvwColumn.ts @@ -18,124 +18,143 @@ const rawList = [ mergeCells: true, editable: false, sortdisable: true, - footer: [ - { - valueCallback: (grid: GridView) => { - const divisionIndex = 0; - const dataSource = grid.getDataSource(); - const uniqueValues = new Set(); - for (let i = 0; i < dataSource.getRowCount(); i++) { - const val = dataSource.getValue(i, 'rgnNm'); - if (val) uniqueValues.add(String(val)); + 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) => { - const divisionIndex = 1; - const dataSource = grid.getDataSource(); - const uniqueValues = new Set(); - for (let i = 0; i < dataSource.getRowCount(); i++) { - const val = dataSource.getValue(i, 'rgnNm'); - if (val) uniqueValues.add(String(val)); - } - const divisionValues = Array.from(uniqueValues); - return divisionValues.length > divisionIndex ? divisionValues[divisionIndex] + '합계' : '합계'; } + 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: 'rgnNm', - fieldName: 'rgnNm', - header: { text: '구분' }, - editable: false, - sortdisable: true, - footer: [ - { - valueCallback: (grid: GridView) => { - const dataSource = grid.getDataSource(); - const uniqueValues = new Set(); - for (let i = 0; i < dataSource.getRowCount(); i++) { - // 푸터 계산 시에도 displayYr 필드를 참조 - const val = dataSource.getValue(i, 'displayYr'); - if (val) uniqueValues.add(String(val)); + { 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 > 0 ? divisionValues[0] + '합계' : ''; - } - }, - { - valueCallback: (grid: GridView) => { - const dataSource = grid.getDataSource(); - const uniqueValues = new Set(); - for (let i = 0; i < dataSource.getRowCount(); i++) { - const val = dataSource.getValue(i, 'displayYr'); - if (val) uniqueValues.add(String(val)); - } - const divisionValues = Array.from(uniqueValues); - return divisionValues.length > 1 ? divisionValues[1] + '합계' : ''; } + 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)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, - { name: 'piplPlanExtn', fieldName: 'piplPlanExtn', header: { text: '총계획연장(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, - { name: 'piplFcltyExtn', fieldName: 'piplFcltyExtn', header: { text: '총시설연장(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, - { name: 'piplSupprt', fieldName: 'piplSupprt', header: { text: '하수관로\n보급률(%)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + { 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계획면적(㎢)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.000' }, - { name: 'cmbnImprmnPlanExtn', fieldName: 'cmbnImprmnPlanExtn', header: { text: '관로정비\n계획연장(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, - { name: 'cmbnPlanExtn', fieldName: 'cmbnPlanExtn', header: { text: '계획연장(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, - { name: 'cmbnFcltyExtn', fieldName: 'cmbnFcltyExtn', header: { text: '시설연장(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, - { name: 'cmbnRctglExtn', fieldName: 'cmbnRctglExtn', header: { text: '사각형(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, - { name: 'cmbnCicleExtn', fieldName: 'cmbnCicleExtn', header: { text: '원형(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, - { name: 'cmbnOpswrExtn', fieldName: 'cmbnOpswrExtn', header: { text: '개거(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, - { name: 'cmbnSdgttrExtn', fieldName: 'cmbnSdgttrExtn', header: { text: '측구(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, + { 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계획면적(㎢)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.000' }, - { name: 'pltwtrImprmnPlanExtn', fieldName: 'pltwtrImprmnPlanExtn', header: { text: '관로정비\n계획연장(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, - { name: 'pltwtrPlanExtn', fieldName: 'pltwtrPlanExtn', header: { text: '계획연장(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, - { name: 'pltwtrFlctyExtn', fieldName: 'pltwtrFlctyExtn', header: { text: '시설연장(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, - { name: 'pltwtrSqreExtn', fieldName: 'pltwtrSqreExtn', header: { text: '사각형(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, - { name: 'pltwtrCiclExtn', fieldName: 'pltwtrCiclExtn', header: { text: '원형(m)' }, 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계획면적(㎢)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.000' }, - { name: 'rainwtrImprmnPlanExtn', fieldName: 'rainwtrImprmnPlanExtn', header: { text: '관로정비\n계획연장(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, - { name: 'rainwtrPlanExtn', fieldName: 'rainwtrPlanExtn', header: { text: '계획연장(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, - { name: 'rainwtrFlctyExtn', fieldName: 'rainwtrFlctyExtn', header: { text: '시설연장(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, - { name: 'rainwtrSqreExtn', fieldName: 'rainwtrSqreExtn', header: { text: '사각형(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, - { name: 'rainwtrCiclExtn', fieldName: 'rainwtrCiclExtn', header: { text: '원형(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, - { name: 'rainwtrOpswrExtn', fieldName: 'rainwtrOpswrExtn', header: { text: '개거(m)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' }, - { name: 'rainwtrSdgttrExtn', fieldName: 'rainwtrSdgttrExtn', header: { text: '측구(m)' }, 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: '합류식맨홀(개소)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, - { name: 'pltwtrMnhleSite', fieldName: 'pltwtrMnhleSite', header: { text: '오수맨홀(개소)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, - { name: 'rainwtrMnhleSite', fieldName: 'rainwtrMnhleSite', header: { text: '우수맨홀(개소)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, - { name: 'pltwtrRcvrSite', fieldName: 'pltwtrRcvrSite', header: { text: '오수받이(개소)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, - { name: 'rainwtrRcvrSite', fieldName: 'rainwtrRcvrSite', header: { text: '빗물받이(개소)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, - { name: 'soilspllSite', fieldName: 'soilspllSite', header: { text: '우수토실(개소)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, - { name: 'soilholeSite', fieldName: 'soilholeSite', header: { text: '우수토구(개소)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, + { 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)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0' }, - { name: 'sewerRegstrCmptRt', fieldName: 'sewerRegstrCmptRt', header: { text: '하수도대장전산화율(%)' }, valueType: ValueType.NUMBER, numberFormat: '#,##0.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: ['sido', 'gugun'] }, - 'rgnNm', + { 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)' },