From 37e3b6ebdb832d03f7d8feb50c42316f2de168e1 Mon Sep 17 00:00:00 2001 From: rsone001 Date: Mon, 16 Feb 2026 16:33:25 +0900 Subject: [PATCH] =?UTF-8?q?feat:=2002.=ED=95=98=EC=88=98=EA=B4=80=EB=A1=9C?= =?UTF-8?q?=20tab4=EA=B4=80=EA=B2=BD=EB=B3=84=20=ED=98=84=ED=99=A9=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=8A=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../statistics/review/SewagePipeRvw.tsx | 2 + .../util/column/review/SewagePipeRvwColumn.ts | 155 ++++++++++++++---- .../api/statistics-management-service.ts | 22 ++- ...statistics-sewage-pipe-review-interface.ts | 46 +++++- 4 files changed, 193 insertions(+), 32 deletions(-) diff --git a/apps/st/src/components/statistics/review/SewagePipeRvw.tsx b/apps/st/src/components/statistics/review/SewagePipeRvw.tsx index f8129ad85..ac7033e53 100644 --- a/apps/st/src/components/statistics/review/SewagePipeRvw.tsx +++ b/apps/st/src/components/statistics/review/SewagePipeRvw.tsx @@ -96,6 +96,8 @@ const SewagePipeRvw = () => { response = await statisticsManagementApi.getSewagePipeFacilityList(searchParamRef.current); }else if(tabKey === 'c'){ response = await statisticsManagementApi.getSewagePipeTypeList(searchParamRef.current); + }else if(tabKey === 'd'){ + response = await statisticsManagementApi.getSewagePipeDiamList(searchParamRef.current); } if (response?.status === 'success' && response.data) { diff --git a/apps/st/src/components/util/column/review/SewagePipeRvwColumn.ts b/apps/st/src/components/util/column/review/SewagePipeRvwColumn.ts index a6e3d79b4..b57a54d44 100644 --- a/apps/st/src/components/util/column/review/SewagePipeRvwColumn.ts +++ b/apps/st/src/components/util/column/review/SewagePipeRvwColumn.ts @@ -436,7 +436,6 @@ const rawList = [ { name: 'layingExtnSum', fieldName: 'layingExtnSum', header: { text: '매설연장(m)' }, footer: footerAvgDecimalArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, { name: 'remvlExtnSum', fieldName: 'remvlExtnSum', header: { text: '철거연장(m)' }, footer: footerAvgDecimalArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, - /* 30개년 동적 컬럼 생성 (SewerPipeTypeItem 필드 매핑) */ ...Array.from({ length: 30 }, (_, i) => { const yNum = (30 - i).toString().padStart(2, '0'); const yrOffset = 29 - i; @@ -455,7 +454,7 @@ const rawList = [ name: 'areaGroup', header: { text: '행정구역' }, items: [ - // sido부터 swrgPipeNm(관종)까지 푸터 병합 + { column: 'sido', footerUserSpans: [{ colspan: 4 }, { colspan: 4 }] }, 'gugun', 'swrgPipeNm', @@ -478,33 +477,131 @@ const rawList = [ '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', - ] - } + { + tabKey: 'd', + tabLabel: '관경별 현황', + rowStartNum: 2, + fixedColumns: 3, + 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); + 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: 'gugun', fieldName: 'gugun', header: { text: '시군구' }, mergeCells: true, editable: false, sortdisable: true }, + { name: 'swrgPipeNm', fieldName: 'swrgPipeNm', header: { text: '관종' }, mergeCells: true, 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: 'totalExtnM', fieldName: 'totalExtnM', header: { text: '총계(m)' }, footer: footerAvgDecimalArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'under200', fieldName: 'under200', header: { text: '200mm 미만' }, footer: footerAvgDecimalArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'over200Undev300', fieldName: 'over200Undev300', header: { text: '200mm이상~\n300mm미만' }, footer: footerAvgDecimalArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'over300Undev500', fieldName: 'over300Undev500', header: { text: '300mm이상~\n500mm미만' }, footer: footerAvgDecimalArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'over500Undev500', fieldName: 'over500Undev500', header: { text: '500mm이상~\n800mm미만' }, footer: footerAvgDecimalArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'over800', fieldName: 'over800', header: { text: '800mm 이상' }, footer: footerAvgDecimalArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'box', fieldName: 'box', header: { text: '사각형(m)' }, footer: footerAvgDecimalArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + { name: 'etc', fieldName: 'etc', header: { text: '기타\n(측구/개거 등)' }, footer: footerAvgDecimalArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' }, + + { name: 'rmk', fieldName: 'rmk', header: { text: '비고' } }, + ], + columnLayout: [ + { + name: 'addrGroup', + header: { text: '행정구역' }, + items: [ + { column: 'sido', footerUserSpans: [{ colspan: 4 }, { colspan: 4 }] }, + 'gugun', + 'swrgPipeNm', + ] + }, + 'displayYear', + 'totalExtnM', + { + name: 'circleGroup', + header: { text: '원형(m)' }, + items: ['under200', 'over200Undev300', 'over300Undev500', 'over500Undev500', 'over800'] + }, + 'box', + 'etc', + 'rmk' + ] +} ]; export const SewagePipeColumnInfoListRvw = rawList.map(tab => ({ diff --git a/apps/st/src/entities/statistics/api/statistics-management-service.ts b/apps/st/src/entities/statistics/api/statistics-management-service.ts index 5fc81ab34..4413a8e1a 100644 --- a/apps/st/src/entities/statistics/api/statistics-management-service.ts +++ b/apps/st/src/entities/statistics/api/statistics-management-service.ts @@ -13,6 +13,7 @@ import { type SewagePipeReviewRegionalApiResponse , type SewagePipeFacilityApiResponse, type SewerPipeTypeApiResponse, + type SewerPipeDiamApiResponse } from "../model/statistics-sewage-pipe-review-interface"; @@ -95,7 +96,7 @@ export class StatisticsManagementApiService { } /** - * 02. 하수관로 관종별 시설현황 조회 (Tab2) + * 02. 하수관로 관종별 시설현황 조회 (Tab3) * @param params (sido, gugun, yr, bsnmCd) */ public async getSewagePipeTypeList( @@ -112,6 +113,25 @@ export class StatisticsManagementApiService { ); return response.data; } + + /** + * 02. 하수관로 관경별 시설현황 조회 (Tab 4) + * @param params (sido, gugun, yr, bsnmCd) + */ + public async getSewagePipeDiamList( + params: { + yr?: string; + bsnmCd?: string; + sido?: string; + gugun?: string; + } + ): Promise { + const response = await axios.get( + `${ENDPOINT.ST_SERVICE.REVIEW}/sewer-pipe/diam-status`, + { params } + ); + return response.data; + } } // 통합 인스턴스 수출 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 index ea7500cf5..6abb98a80 100644 --- 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 @@ -82,7 +82,10 @@ export interface SewagePipeReviewService { }): Promise; } - +/** + * 02. 하수관로 시설별 시설현황 (Tab 2) + * + */ export interface SewagePipeReviewFacilityItem { // 1. 행정구역 및 기본 정보 sido: string; // 시도 @@ -152,7 +155,7 @@ export interface SewagePipeFacilityService { } /** - * 02. 하수관로 관종별 시설현황 (PipeType) + * 02. 하수관로 관종별 시설현황 (Tab 3) * */ export interface SewerPipeTypeItem { @@ -196,4 +199,43 @@ export interface SewagePipeTypeService { sido?: string; // 시도 gugun?: string; // 구군 }): Promise; +} + +/** + * 02. 하수관로 관경별 시설현황 아이템 (Tab 4) + */ +export interface SewerPipeDiamItem { + sido: string; // 시도 + gugun: string; // 시군구 + adstCd: string; // 행정코드 + swrgPipeNm: string; // 관종명 (또는 관경 관련 명칭) + displayYear: string; // 구분 (예: 2023년 확정 / 2024년) + + // 관경별 수치 데이터 + under200: number; // 200미만 + over200Undev300: number; // 200~300 + over300Undev500: number; // 300~500 + over500Undev500: number; // 500~800 (DTO 명칭 기준) + over800: number; // 800이상 + box: number; // BOX + etc: number; // 기타 + rmk: string; // 비고 +} + + +export interface SewerPipeDiamResponseData { + pipeDiamStatusList: SewerPipeDiamItem[]; +} + + +export type SewerPipeDiamApiResponse = ApiResponse; + + +export interface SewagePipeDiamService { + getSewagePipeDiamList(params: { + yr?: string; // 조회 연도 + bsnmCd?: string; // 사업장 코드 + sido?: string; // 시도 + gugun?: string; // 구군 + }): Promise; } \ No newline at end of file