st: fix & refactor: 하수도 보급률 입력 조회 response 수정 및 TadaGrid 컴포넌트 Tab state 부모 컴포넌트에서 prop로 전달하도록 수정, grid 데이터를 가져오기 위한 ref prop 수정(TabDataGrid.tsx는 아직 미구현)

This commit is contained in:
2026-02-12 10:50:07 +09:00
parent e79590b70d
commit 60c515a654
13 changed files with 413 additions and 159 deletions
+30 -9
View File
@@ -164,7 +164,7 @@ function createOnSortingHandler(
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 orderByField = 'displayYear'; // 고정값 또는 설정 가능하게 할 수 있음
const hasMergeCells = mergeCellCols.length > 0;
return function onSorting(g: unknown, fields: unknown[], _directions: unknown[]) {
@@ -289,7 +289,7 @@ function buildFieldsFromColumns(columns: { fieldName: string; valueType?: ValueT
});
}
const DataGrid = ({ columnInfo, rowStartNum, isExcelImport = true, isExcelExport = true, fixedColumns, showHeaderCheckbox = false, rowData, setRowData }: DataGridProps) => {
const DataGrid = ({ columnInfo, rowStartNum, isExcelImport = true, isExcelExport = true, fixedColumns, showHeaderCheckbox = false, rowData, setRowData, getGridDataRef }: DataGridProps) => {
const { columns, columnLayout } = columnInfo;
const gridRef = useRef<RealGridReact>(null);
const fileInputRef = useRef<HTMLInputElement>(null);
@@ -748,8 +748,28 @@ const DataGrid = ({ columnInfo, rowStartNum, isExcelImport = true, isExcelExport
});
}, 200);
}, [columns, columnLayout, fixedColumns, rowData, showHeaderCheckbox]);
if (getGridDataRef) {
const fieldNames = columns.map((c) => c.fieldName).filter(Boolean) as string[];
getGridDataRef.current = () => {
const gv = gridRef.current?.gridView as { getDataSource?: () => { getRowCount(): number; getValue(r: number, f: string): unknown } } | undefined;
if (!gv?.getDataSource) return [];
const ds = gv.getDataSource();
if (!ds) return [];
const cnt = ds.getRowCount();
const rows: Record<string, string | number>[] = [];
for (let i = 0; i < cnt; i++) {
const r: Record<string, string | number> = {};
for (const f of fieldNames) r[f] = ds.getValue(i, f) as string | number;
rows.push(r);
}
return rows;
};
}
return () => {
if (getGridDataRef) getGridDataRef.current = null;
};
}, [columns, columnLayout, fixedColumns, rowData, showHeaderCheckbox, getGridDataRef]);
return (
<div className="flex flex-col gap-[0.6rem] mx-[0.6rem]">
@@ -782,13 +802,14 @@ const DataGrid = ({ columnInfo, rowStartNum, isExcelImport = true, isExcelExport
rows={rowData}
onInitialized={(grid) => {
(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<string, string | number>[]) => void } | undefined;
if (dp?.setFields) {
dp.setFields(buildFieldsFromColumns(columns));
const dataProvider = gridRef.current?.dataProvider as unknown as
| { setFields?(f: unknown[]): void; setRows?(r: Record<string, string | number>[]): void; getJsonRow?(r: number): Record<string, unknown> }
| undefined;
if (dataProvider?.setFields) {
dataProvider.setFields(buildFieldsFromColumns(columns));
}
// setFields 호출 직후 행 데이터 적용 (realgrid-react의 setRows 타이밍 이슈 방지)
if (dp?.setRows && rowData?.length) {
dp.setRows(rowData);
if (dataProvider?.setRows && rowData?.length) {
dataProvider.setRows(rowData);
}
}}
>
+13 -5
View File
@@ -162,7 +162,7 @@ function createOnSortingHandler(
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 orderByField = 'displayYear'; // 고정값 또는 설정 가능하게 할 수 있음
const hasMergeCells = mergeCellCols.length > 0;
return function onSorting(g: unknown, fields: unknown[], _directions: unknown[]) {
@@ -306,7 +306,7 @@ function createNumberComparer(dp: RealGridDataProviderComparer): (field: string
};
}
const TabDataGrid = ({ columnInfo, isExcelImport = true, isExcelExport = true, rows = [], showHeaderCheckbox = false, rowDataMap, setRowDataMap }: TabDataGridProps) => {
const TabDataGrid = ({ columnInfo, isExcelImport = true, isExcelExport = true, rows = [], showHeaderCheckbox = false, rowDataMap, setRowDataMap, activeTab: activeTabProp, onActiveTabChange }: TabDataGridProps) => {
// 각 탭별로 그리드 ref와 데이터를 관리
const gridRefs = useRef<Map<string, RealGridReact>>(new Map());
const fileInputRefs = useRef<Map<string, HTMLInputElement>>(new Map());
@@ -316,11 +316,14 @@ const TabDataGrid = ({ columnInfo, isExcelImport = true, isExcelExport = true, r
const { setMessage: alert } = useAlertStore((state) => state);
const activeTabId = useTabStore((state) => state.activeTabId);
const menuList = useTabStore((state) => state.menuList);
const [activeTab, setActiveTab] = useState<string>(() => {
// 초기 활성 탭을 첫 번째 tabKey로 설정
const [internalActiveTab, setInternalActiveTab] = useState<string>(() => {
const firstTab = columnInfo[0];
return firstTab?.tabKey || '';
});
const isControlled = activeTabProp !== undefined;
const activeTab = isControlled ? activeTabProp : internalActiveTab;
const setActiveTab = isControlled ? (onActiveTabChange ?? (() => {})) : setInternalActiveTab;
const exportExcel = (tabKey: string) => {
const currentRowData = rowDataMap.get(tabKey) || [];
@@ -803,10 +806,15 @@ const TabDataGrid = ({ columnInfo, isExcelImport = true, isExcelExport = true, r
const columns = currentInfo.columns;
const dp = el.dataProvider as { setFields?: (fields: unknown[]) => void } | undefined;
const dp = el.dataProvider as { setFields?: (fields: unknown[]) => void; setRows?: (rows: Record<string, string | number>[]) => void } | undefined;
if (dp?.setFields) {
dp.setFields(buildFieldsFromColumns(columns));
}
// setFields 호출 직후 행 데이터 적용 (realgrid-react의 setRows 타이밍 이슈 방지)
const tabRowData = rowDataMap.get(tabKey) || [];
if (dp?.setRows && tabRowData.length) {
dp.setRows(tabRowData);
}
gridView.setDisplayOptions({
fitStyle: GridFitStyle.EVEN
@@ -1,4 +1,4 @@
import { useState, useMemo } from "react";
import { useState, useMemo, useRef } from "react";
import SearchForm from "@/components/module/SearchForm";
// import { DataGrid } from "@rsone/components";
@@ -8,6 +8,7 @@ import SaveButton from "@/components/button/SaveButton";
import LastYearDataButton from "@/components/button/LastYearDataButton";
import SubmitExceptionButton from "@/components/button/SubmitExceptionButton";
import { LoadingWithAnimation } from "@common/components";
import { ConfirmDialog } from "@/shared/ui/confirm-dialog";
import { useAlertStore } from "@/shared/lib";
import { statisticsInputAPi } from "@/entities/statistics/api/statistics-input-service";
@@ -15,9 +16,13 @@ import type { SearchFormValues } from "@/components/type/SearchForm.type";
import type {
SewerageSupplyRateItem,
SewerageSupplyRateRequestParams,
SewerageSupplyRateSaveItem,
} from "@/entities/statistics/model/statistics-input-interface";
import { SeweragePenetrationRateInpColumn } from "@/components/util/column/input/SeweragePenetrationRateInpColumn";
import { toGridRows } from "@/components/util/grid/gridFunction";
import { getFooterSummary } from "@/components/util/grid/footerSummary";
import { getUserInfo } from "../../../../../../packages/common/components/src/api";
import { useConfirm } from "@frontend-opensource/use-react-hooks";
function getSewerageSupplyRateParams(
values: SearchFormValues,
@@ -28,7 +33,6 @@ function getSewerageSupplyRateParams(
gugun: values.gugun === 'ALL' ? '' : values.gugun,
bsnmCd: values.bsnmCd,
yr: overrides?.yr ?? values.year,
demoYr: overrides?.demoYr ?? values.year,
...(overrides?.mode && { mode: overrides.mode }),
};
}
@@ -41,22 +45,38 @@ const SeweragePenetrationRateInp = () => {
const [searchValues, setSearchValues] = useState<SearchFormValues | null>(null);
const [isEnterable, setIsEnterable] = useState<boolean>(true);
const [lastSearchedYear, setLastSearchedYear] = useState<string | null>(null);
const { confirm } = useConfirm();
const [footerFromApi, setFooterFromApi] = useState<{ totalPopl?: number; poplSm?: number; frgnrPoplSm?: number } | null>(null);
const getGridDataRef = useRef<(() => Record<string, string | number>[]) | null>(null);
const footerFieldNames = ['totalPopl', 'poplSm', 'frgnrPoplSm'] as const;
const footerWithApiDefault = (fieldName: (typeof footerFieldNames)[number], numberFormat: string) => ({
expression: 'sum' as const,
numberFormat,
valueCallback: (grid: Parameters<typeof getFooterSummary>[0], column: Parameters<typeof getFooterSummary>[1]) =>
(footerFromApi && footerFromApi[fieldName] != null ? footerFromApi[fieldName] : null) ?? getFooterSummary(grid, column),
});
// 최상위 관리자일땐 모든 상황에서 edit 가능하게 수정 필요.
const columnInfo = useMemo(() => {
const { columns, columnLayout } = SeweragePenetrationRateInpColumn;
const base = isEnterable ? columns : columns.map((col) => ({ ...col, editable: false as const }));
return {
columnLayout,
columns: isEnterable
? columns
: columns.map((col) => ({ ...col, editable: false as const })),
columns: base.map((col) => {
if (col.fieldName === 'totalPopl') return { ...col, footer: footerWithApiDefault('totalPopl', '#,##0') };
if (col.fieldName === 'poplSm') return { ...col, footer: footerWithApiDefault('poplSm', '#,##0') };
if (col.fieldName === 'frgnrPoplSm') return { ...col, footer: footerWithApiDefault('frgnrPoplSm', '#,##0') };
return col;
}),
};
}, [isEnterable]);
}, [isEnterable, footerFromApi]);
const handleReset = () => {
setRowData([]);
setLastSearchedYear(null);
setFooterFromApi(null);
};
const fetchSewerageSupplyRate = async (params: SewerageSupplyRateRequestParams) => {
@@ -64,18 +84,30 @@ const SeweragePenetrationRateInp = () => {
setIsLoading(true);
const response = await statisticsInputAPi.getSewerageSupplyRateList(params);
if (response.status === 'success' && response.data) {
const commonInfo = response.data.commonInfo;
const convertedData: SewerageSupplyRateItem[] = response.data.sewerageSupplyRateList.map((d) => ({
...d,
sido: commonInfo.sido,
speclCntrplnAr: d.speclCntrplnAr === '' || d.speclCntrplnAr === null ? '해당없음' : d.speclCntrplnAr,
}));
setRowData(toGridRows(convertedData));
setFooterFromApi(
commonInfo
? {
totalPopl: commonInfo.bsnmTotalPopl,
poplSm: commonInfo.bsnmPoplSm,
frgnrPoplSm: commonInfo.bsnmFrgnrPoplSm,
}
: null
);
} else {
alert(response.message);
}
} catch {
alert('데이터 조회 중 오류가 발생했습니다.');
setRowData([]);
setFooterFromApi(null);
} finally {
setIsLoading(false);
}
@@ -126,11 +158,91 @@ const SeweragePenetrationRateInp = () => {
// 제출 로직
};
const saveFunc = () => {
// 저장 로직
const saveFunc = async () => {
try {
if (await confirm('저장 하시겠습니까?')) {
setIsLoading(true);
const userInfo = await getUserInfo();
const currentData = getGridDataRef.current?.();
if (!currentData || currentData.length === 0) {
alert('저장할 데이터가 없습니다.');
return;
}
// 컬럼 정의에서 dataFormat 추출 (소수점 자리수 맞추기 위해)
const columnFormatMap = new Map<string, string>();
SeweragePenetrationRateInpColumn.columns.forEach((col) => {
if (col.fieldName && col.dataFormat) {
columnFormatMap.set(col.fieldName, col.dataFormat);
}
});
// 숫자 포맷팅 함수 (dataFormat에 따라 소수점 자리수 맞춤)
const formatNumber = (value: string | number | null | undefined, fieldName: string): number => {
if (value === null || value === undefined || value === '') return 0;
const num = typeof value === 'string' ? parseFloat(value.replace(/,/g, '')) : value;
if (isNaN(num)) return 0;
const format = columnFormatMap.get(fieldName);
if (!format) return num;
// #,##0.000 → 소수점 3자리, #,##0.0 → 소수점 1자리, #,##0 → 정수
const decimalPlaces = format.match(/\.(\d+)/)?.[1]?.length ?? 0;
return decimalPlaces > 0 ? parseFloat(num.toFixed(decimalPlaces)) : Math.round(num);
};
// SaveItem 타입에 정의된 필드만 추출
const convertedData: SewerageSupplyRateSaveItem[] = currentData.map((d) => ({
bsnmCd: searchValues?.bsnmCd ?? '',
sido: d.sido as string,
gugun: (d.gugun as string) || null,
yr: searchValues?.year ?? '',
adstCd: d.adstCd as string,
adstDong: d.adstDong as string,
bstrm: d.bstrm as string,
speclCntrplnAr: d.speclCntrplnAr === '해당없음' ? null : (d.speclCntrplnAr as string | null),
pblswrgPrmTrt: formatNumber(d.pblswrgPrmTrt, 'pblswrgPrmTrt'),
pblswrgScdTrt: formatNumber(d.pblswrgScdTrt, 'pblswrgScdTrt'),
pblswrgTertTrt: formatNumber(d.pblswrgTertTrt, 'pblswrgTertTrt'),
wstrPrmTrt: formatNumber(d.wstrPrmTrt, 'wstrPrmTrt'),
wstrScdTrt: formatNumber(d.wstrScdTrt, 'wstrScdTrt'),
wstrTertTrt: formatNumber(d.wstrTertTrt, 'wstrTertTrt'),
notConectPopltn: formatNumber(d.notConectPopltn, 'notConectPopltn'),
swgeProcZoneInnerAr: formatNumber(d.swgeProcZoneInnerAr, 'swgeProcZoneInnerAr'),
townUnprocPopltn: formatNumber(d.townUnprocPopltn, 'townUnprocPopltn'),
townSwttPopltn: formatNumber(d.townSwttPopltn, 'townSwttPopltn'),
townWrrtnPopltn: formatNumber(d.townWrrtnPopltn, 'townWrrtnPopltn'),
swgeProcZoneOuterAr: formatNumber(d.swgeProcZoneOuterAr, 'swgeProcZoneOuterAr'),
rmk: (d.rmk as string) ?? '',
popltnSm: formatNumber(d.popltnSm, 'popltnSm'),
arSm: formatNumber(d.arSm, 'arSm'),
swgeProcZoneInnerSm: formatNumber(d.swgeProcZoneInnerSm, 'swgeProcZoneInnerSm'),
pblswrgPopltnSm: formatNumber(d.pblswrgPopltnSm, 'pblswrgPopltnSm'),
wstrPopltnSm: formatNumber(d.wstrPopltnSm, 'wstrPopltnSm'),
swgeProcZoneOuterSm: formatNumber(d.swgeProcZoneOuterSm, 'swgeProcZoneOuterSm'),
pblswrgPopltnSuprt: formatNumber(d.pblswrgPopltnSuprt, 'pblswrgPopltnSuprt'),
adtrtPopltnSuprt: formatNumber(d.adtrtPopltnSuprt, 'adtrtPopltnSuprt'),
swrgInstlrt: formatNumber(d.swrgInstlrt, 'swrgInstlrt'),
userId: userInfo.userName,
}));
const response = await statisticsInputAPi.saveSewerageSupplyRate(convertedData);
console.log(convertedData)
console.log(response);
} else {
// 저장 취소
}
} catch {
alert('데이터 저장 중 오류가 발생했습니다.');
} finally {
setIsLoading(false);
}
};
return (
<>
<div className="relative">
<SearchForm onSubmit={handleSearch} onReset={handleReset} onValuesChange={setSearchValues} />
{rowData.length > 0 &&
@@ -159,8 +271,11 @@ const SeweragePenetrationRateInp = () => {
fixedColumns={1}
rowData={rowData}
setRowData={setRowData}
getGridDataRef={getGridDataRef}
/>
</div>
<ConfirmDialog isOpen={false} />
</>
);
};
@@ -1,4 +1,4 @@
import { useState, useMemo } from "react";
import { useState, useMemo, act } from "react";
import TabDataGrid from "@/components/module/TabDataGrid";
import SearchForm from "@/components/module/SearchForm";
import SaveButton from "@/components/button/SaveButton";
@@ -18,6 +18,7 @@ const SewagePipeRvw = () => {
const { setMessage: alert } = useAlertStore((state) => state);
const [rowDataMap, setRowDataMap] = useState<Map<string, any[]>>(new Map());
const [isLoading, setIsLoading] = useState<boolean>(false);
const [activeTab, setActiveTab] = useState<string>('a');
const columnInfo = useMemo(() => {
return SewagePipeColumnInfoListRvw.map((tab: any) => ({
@@ -40,23 +41,29 @@ const SewagePipeRvw = () => {
try {
setIsLoading(true);
const response = await statisticsManagementApi.getSewagePipeReviewList({
const searchParam = {
sido: values.sido,
gugun: values.gugun === 'ALL' ? '' : values.gugun,
bsnmCd: values.bsnmCd,
yr: values.year,
});
};
if (response.status === 'success' && response.data) {
let response;
if (activeTab === 'a') {
response = await statisticsManagementApi.getSewagePipeReviewList(searchParam);
}
if (response?.status === 'success' && response.data) {
const rawList = response.data;
const newRowDataMap = new Map<string, any[]>();
newRowDataMap.set('a', toGridRows(rawList));
newRowDataMap.set(activeTab, toGridRows(rawList));
setRowDataMap(newRowDataMap);
} else {
handleReset();
alert(response.message || "데이터가 없습니다.");
alert(response?.message || "데이터가 없습니다.");
}
} catch (error) {
console.error("조회 에러:", error);
@@ -68,30 +75,32 @@ const SewagePipeRvw = () => {
};
return (
<div className="relative">
<div className="relative">
<SearchForm onSubmit={handleSearch} onReset={handleReset} />
<div className="card !py-0">
<div className="flex justify-end items-center gap-[0.6rem] m-[0.6rem]">
<ReviewOpinionButton reviewOpinionFunc={() => {}} />
<ApproveButton approveFunc={() => {}} />
<ReturnButton returnFunc={() => {}} />
<SaveButton saveFunc={() => {}} />
<ReviewOpinionButton reviewOpinionFunc={() => { }} />
<ApproveButton approveFunc={() => { }} />
<ReturnButton returnFunc={() => { }} />
<SaveButton saveFunc={() => { }} />
</div>
</div>
{isLoading && (
<div className="absolute inset-0 z-50 [data-theme='black']:bg-transparent bg-white/50 rounded-lg p-10 flex justify-center items-center">
<div className="absolute inset-0 z-50 [data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center">
<div className="relative size-[5rem]">
<LoadingWithAnimation color="primary" />
</div>
</div>
)}
<TabDataGrid
columnInfo={columnInfo}
isExcelImport={false}
rowDataMap={rowDataMap}
setRowDataMap={setRowDataMap}
<TabDataGrid
columnInfo={columnInfo}
isExcelImport={false}
rowDataMap={rowDataMap}
setRowDataMap={setRowDataMap}
activeTab={activeTab}
onActiveTabChange={setActiveTab}
/>
</div>
);
@@ -93,7 +93,7 @@ const SeweragePenetrationRateRvw = () => {
</div>
{isLoading && (
<div className="absolute inset-0 z-50 [data-theme='black']:bg-transparent bg-white/50 rounded-lg p-10 flex justify-center items-center">
<div className="absolute inset-0 z-50 [data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center">
<div className="relative size-[5rem]">
<LoadingWithAnimation color="primary" />
</div>
@@ -91,7 +91,7 @@ const StatsInputStatusRvw = () => {
</div>
{isLoading && (
<div className="absolute inset-0 z-50 [data-theme='black']:bg-transparent bg-white/50 rounded-lg p-10 flex justify-center items-center">
<div className="absolute inset-0 z-50 [data-theme='black']:bg-transparent rounded-lg p-10 flex justify-center items-center">
<div className="relative size-[5rem]">
<LoadingWithAnimation color="primary" />
</div>
@@ -1,7 +1,8 @@
"use client";
import { useState } from "react";
import { DataGrid } from "@rsone/components";
// import { DataGrid } from "@rsone/components";
import DataGrid from "@/components/module/DataGrid";
import SearchForm from "@/components/module/SearchForm";
import {
Button,
@@ -150,6 +150,8 @@ export type DataGridProps = {
showHeaderCheckbox?: boolean; // 헤더 체크박스 표시 여부 (기본값: true)
rowData: Record<string, string | number>[];
setRowData: React.Dispatch<React.SetStateAction<Record<string, string | number>[]>>;
/** 저장 등 시점에 그리드 현재 데이터를 가져올 수 있도록 할당 (편집 반영된 값) */
getGridDataRef?: { current: (() => Record<string, string | number>[]) | null };
};
export type TabDataGridProps = {
@@ -162,6 +164,10 @@ export type TabDataGridProps = {
rowDataMap: Map<string, Record<string, string | number>[]>;
setRowDataMap: React.Dispatch<React.SetStateAction<Map<string, Record<string, string | number>[]>>>;
isSplitGrid?: boolean; // 5:5 분리형 그리드 유무
/** 활성 탭 키 (제어 모드). 부모에서 넘기면 해당 탭이 선택됨 */
activeTab?: string;
/** 활성 탭 변경 시 호출 (제어 모드). activeTab과 함께 넘기면 부모가 탭 상태를 가짐 */
onActiveTabChange?: (tabKey: string) => void;
};
export type SplitDataGridProps = {
@@ -1,4 +1,4 @@
import { footerInt, footerDecimal1, footerDecimal3, footerAvgDecimal } from "@/components/util/grid/footerSummary";
import { footerInt, footerDecimal3, footerAvgDecimal } from "@/components/util/grid/footerSummary";
import { ValueType } from "realgrid";
/**
* 하수도 통계 관리 > 01. 하수도 보급률
@@ -32,7 +32,17 @@ export const SeweragePenetrationRateInpColumn = {
{ 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: 'popltnSm',
fieldName: 'popltnSm',
header: { text: '총인구(명)' },
footer: footerInt,
editable: false,
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
computed: { fields: ['pblswrgPrmTrt', 'pblswrgScdTrt', 'pblswrgTertTrt', 'wstrPrmTrt', 'wstrScdTrt', 'wstrTertTrt', 'notConectPopltn', 'townUnprocPopltn', 'townSwttPopltn', 'townWrrtnPopltn'], type: 'sum' as const },
styleName: 'rg-cell-total',
},
// 총면적
{
@@ -55,7 +65,7 @@ export const SeweragePenetrationRateInpColumn = {
editable: false,
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
computed: { fields: ['pblswrgPrmTrt', 'pblswrgScdTrt', 'pblswrgTertTrt', 'wstrPrmTrt', 'wstrScdTrt', 'wstrTertTrt'], type: 'sum' as const },
computed: { fields: ['pblswrgPrmTrt', 'pblswrgScdTrt', 'pblswrgTertTrt', 'wstrPrmTrt', 'wstrScdTrt', 'wstrTertTrt', 'notConectPopltn'], type: 'sum' as const },
styleName: 'rg-cell-total',
},
@@ -170,6 +180,7 @@ export const SeweragePenetrationRateInpColumn = {
{ name: 'rmk', fieldName: 'rmk', header: { text: '비고' } },
{ name: 'piplSupprt', fieldName: 'piplSupprt', header: { text: '하수관로보급률(%)' }, visible: false },
{ name: 'adstCd', fieldName: 'adstCd', header: { text: '행정코드' }, visible: false },
],
columnLayout: [
@@ -240,5 +251,6 @@ export const SeweragePenetrationRateInpColumn = {
'swrgInstlrt',
'rmk',
'piplSupprt',
'adstCd',
]
};
@@ -18,142 +18,144 @@ const rawList = [
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<string>();
for (let i = 0; i < rowCount; i++) {
const divisionValue = dataSource.getValue(i, 'displayYear');
if (divisionValue !== null && divisionValue !== undefined && divisionValue !== '') {
uniqueValues.add(String(divisionValue));
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<string>();
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 '합계';
}
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<string>();
for (let i = 0; i < rowCount; i++) {
const divisionValue = dataSource.getValue(i, 'displayYear');
if (divisionValue !== null && divisionValue !== undefined && divisionValue !== '') {
uniqueValues.add(String(divisionValue));
},
{
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<string>();
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 '합계';
}
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<string>();
for (let i = 0; i < rowCount; i++) {
const divisionValue = dataSource.getValue(i, 'displayYear');
if (divisionValue !== null && divisionValue !== undefined && divisionValue !== '') {
uniqueValues.add(String(divisionValue));
{
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<string>();
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] + '합계' : '';
}
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<string>();
for (let i = 0; i < rowCount; i++) {
const divisionValue = dataSource.getValue(i, 'displayYear');
if (divisionValue !== null && divisionValue !== undefined && divisionValue !== '') {
uniqueValues.add(String(divisionValue));
},
{
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<string>();
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] + '합계' : '';
}
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' },
{ 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: '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: '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: '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: '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: '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',
] },
{
name: 'areaGroup', header: { text: '행정구역' }, items: [
{ column: 'sido', footerUserSpans: [{ colspan: 4 }, { colspan: 4 }] },
'gugun',
'adstDong',
]
},
'displayYear',
'targetYr', 'piplImprmnPlanExtn', 'piplPlanExtn', 'piplFcltyExtn', 'piplSupprt',
{
@@ -6,6 +6,8 @@ import {
type SewerageSupplyRateRequestParams,
type SewerageSupplyRateApiResponse,
type CheckInputPeriodResponse,
type SewerageSupplyRateSaveItem,
type SewerageSupplyRateSaveApiResponse,
} from "../model/statistics-input-interface";
export class StatisticsInputAPiService implements StatisticsInputService {
@@ -27,6 +29,15 @@ export class StatisticsInputAPiService implements StatisticsInputService {
return response.data;
}
public async saveSewerageSupplyRate(params: SewerageSupplyRateSaveItem[]): Promise<SewerageSupplyRateSaveApiResponse> {
const response = await axios.post<SewerageSupplyRateSaveApiResponse> (
`${ENDPOINT.ST_SERVICE.INPUT}/sewerage-supply-rate`,
params
);
return response.data;
}
}
export const statisticsInputAPi = new StatisticsInputAPiService();
@@ -67,7 +67,7 @@ export class StatisticsManagementApiService {
}
): Promise<SewagePipeReviewApiResponse> {
const response = await axios.get<SewagePipeReviewApiResponse>(
`${ENDPOINT.ST_SERVICE.REVIEW}/pipe/tab1`,
`${ENDPOINT.ST_SERVICE.REVIEW}/sewer-pipe/regional-status`,
{ params }
);
return response.data;
@@ -13,6 +13,28 @@ export interface SewerageSupplyRateStatusInfo {
returnOpinionSm: string | null;
}
export interface SewerageSupplyRateCommonInfo {
bsnmArea: number;
bsnmCd: string;
bsnmFrgnrPoplSm: number;
bsnmPoplSm: number;
bsnmTotalPopl: number;
piplFcltyExtn: number;
piplPlanExtn: number;
piplSupprt: number;
sido: string;
yr: string;
}
/** 검증 결과 한 건 (필드별 메시지) */
export interface VerificationResultItem {
type: string;
rsn: string;
}
/** 필드명 → 검증 결과 배열. 예: { popltnSm: [{ type, rsn }], wstrPrmTrt: [...] } */
export type SewerageSupplyRateVerificationResult = Record<string, VerificationResultItem[]>;
export interface SewerageSupplyRateItem {
sido: string;
gugun: string;
@@ -93,11 +115,13 @@ export interface SewerageSupplyRateItem {
chkAdtrtPopltnSuprt: string;
chkSwrgInstlrt: string;
chkNotConectPopltn: string;
verificationResult?: SewerageSupplyRateVerificationResult;
}
export interface SewerageSupplyRateResponseData {
statusInfo: SewerageSupplyRateStatusInfo;
sewerageSupplyRateList: SewerageSupplyRateItem[];
commonInfo: SewerageSupplyRateCommonInfo;
}
export type SewerageSupplyRateApiResponse = ApiResponse<SewerageSupplyRateResponseData> & {
@@ -109,10 +133,53 @@ export interface SewerageSupplyRateRequestParams {
gugun?: string;
bsnmCd?: string;
yr?: string;
demoYr?: string;
mode?: string;
}
/** 하수도 보급률 저장 API 요청 body (한 행) */
export interface SewerageSupplyRateSaveItem {
bsnmCd: string;
sido: string;
gugun: string | null;
yr: string;
adstCd: string;
adstDong: string;
bstrm: string;
speclCntrplnAr: string | null;
pblswrgPrmTrt: number;
pblswrgScdTrt: number;
pblswrgTertTrt: number;
wstrPrmTrt: number;
wstrScdTrt: number;
wstrTertTrt: number;
notConectPopltn: number;
swgeProcZoneInnerAr: number;
townUnprocPopltn: number;
townSwttPopltn: number;
townWrrtnPopltn: number;
swgeProcZoneOuterAr: number;
rmk: string;
popltnSm: number;
arSm: number;
swgeProcZoneInnerSm: number;
pblswrgPopltnSm: number;
wstrPopltnSm: number;
swgeProcZoneOuterSm: number;
pblswrgPopltnSuprt: number;
adtrtPopltnSuprt: number;
swrgInstlrt: number;
userId: string;
}
export interface SewerageSupplyRateSaveData {
hasMismatch?: boolean;
mismatchIdList?: string[];
httpStatus?: number;
code?: string;
}
export type SewerageSupplyRateSaveApiResponse = ApiResponse<SewerageSupplyRateSaveData>;
// 하수도 통계 입력 기간 조회
export interface CheckInputPeriodResponse {
status: string;
@@ -122,4 +189,6 @@ export interface CheckInputPeriodResponse {
export interface StatisticsInputService {
getSewerageSupplyRateList(params: SewerageSupplyRateRequestParams): Promise<SewerageSupplyRateApiResponse>;
checkInputPeriod(params: string): Promise<CheckInputPeriodResponse>;
saveSewerageSupplyRate(params: SewerageSupplyRateSaveItem[]): Promise<SewerageSupplyRateSaveApiResponse>;
}