Merge branch 'iipbs/st/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/st/dev

This commit is contained in:
2026-02-12 17:00:45 +09:00
14 changed files with 843 additions and 244 deletions
+57 -12
View File
@@ -221,7 +221,7 @@ function createOnSortingHandler(
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(
@@ -412,6 +412,7 @@ const DataGrid = ({
showHeaderCheckbox = false,
rowData,
setRowData,
getGridDataRef,
onRowClick,
}: DataGridProps) => {
const { columns, columnLayout } = columnInfo;
@@ -1030,7 +1031,46 @@ const DataGrid = ({
}
});
}, 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,
]);
// 행 클릭 이벤트 설정
useEffect(() => {
@@ -1117,20 +1157,25 @@ const DataGrid = ({
sortDirRef,
originalRowsRef,
);
const dp = gridRef.current?.dataProvider as
const dataProvider = gridRef.current
?.dataProvider as unknown as
| {
setFields?: (fields: unknown[]) => void;
setRows?: (
rows: Record<string, string | number>[],
) => void;
setFields?(f: unknown[]): void;
setRows?(
r: Record<string, string | number>[],
): void;
getJsonRow?(
r: number,
): Record<string, unknown>;
}
| undefined;
if (dp?.setFields) {
dp.setFields(buildFieldsFromColumns(columns));
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);
}
}}
>
+122 -18
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) || [];
@@ -653,22 +656,115 @@ const TabDataGrid = ({ columnInfo, isExcelImport = true, isExcelExport = true, r
}, [rowDataMap, columnInfo]);
// 활성 탭이 변경될 때마다 fitStyle 재적용 (그리드가 보일 때)
// 탭 전환 시 그리드 레이아웃·푸터·포맷 재적용 (여러 시점 + ResizeObserver)
const appliedTabRef = useRef<string | null>(null);
useEffect(() => {
const gridRef = gridRefs.current.get(activeTab);
if (!gridRef?.gridView) return;
appliedTabRef.current = null;
const currentInfo = columnInfo.find(info => info.tabKey === activeTab);
const columns = currentInfo?.columns;
const columnLayout = currentInfo?.columnLayout;
if (!columns?.length || !columnLayout) return;
const gridView = gridRef.gridView;
// 약간의 지연을 두고 설정 (그리드 렌더링 완료 후)
const timer = setTimeout(() => {
gridView.setDisplayOptions({
fitStyle: GridFitStyle.EVEN
const tryApply = () => {
if (appliedTabRef.current === activeTab) return;
const gridRef = gridRefs.current.get(activeTab);
if (!gridRef?.gridView || !gridRef?.dataProvider) return;
const gridView = gridRef.gridView;
const dp = gridRef.dataProvider as { setFields?: (fields: unknown[]) => void; setRows?: (rows: Record<string, string | number>[]) => void } | undefined;
const tabRowData = rowDataMap.get(activeTab) || [];
try {
if (dp?.setFields) dp.setFields(buildFieldsFromColumns(columns));
gridView.setDisplayOptions({ fitStyle: GridFitStyle.EVEN });
gridView.setColumnLayout?.(columnLayout as ColumnLayoutItem[]);
const hasFooter = columns.some(col => col.footer);
if (hasFooter) {
const hasArrayFooter = columns.some(col => col.footer && Array.isArray(col.footer));
const hasMultiRowFooter = columns.some(col => {
const f = col.footer;
return f && !Array.isArray(f) && 'multiRow' in f && (f as { multiRow?: boolean }).multiRow === true;
});
if (hasArrayFooter || hasMultiRowFooter) {
const lengths = columns.filter(col => col.footer && Array.isArray(col.footer)).map(col => (col.footer as unknown[]).length);
const maxLen = lengths.length ? Math.max(...lengths) : 2;
gridView.setFooters?.(Array.from({ length: maxLen }, () => ({ height: 40 })));
} else {
gridView.footer.height = 40;
}
}
gridView.setOptions?.({ summaryMode: "aggregate" });
if (columns.some(col => col.footer && Array.isArray(col.footer))) {
const columnsWithFooter: RealGridColumnInfo[] = columns.map(col => {
const c: 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)) c.footer = col.footer;
else c.footer = { text: col.footer.text, expression: col.footer.expression, numberFormat: col.footer.numberFormat };
}
return c;
});
(gridView as GridViewWithSetColumns).setColumns?.(columnsWithFooter);
columns.forEach(col => {
if (!col.footer || !Array.isArray(col.footer)) return;
try {
const column = gridView.columnByName(col.name);
const footerArr = column?.footer as unknown[] | undefined;
if (footerArr && Array.isArray(footerArr))
col.footer.forEach((item, i) => {
if (item.valueCallback && footerArr[i])
(footerArr[i] as FooterItemWithCallback).valueCallback = item.valueCallback;
});
} catch (_) {}
});
}
columns.forEach(col => {
if (!col.footer || Array.isArray(col.footer)) return;
try {
const column = gridView.columnByName(col.name);
const fo = col.footer as { valueCallback?: (g: unknown, col: unknown, fi: number, cf: unknown, v: unknown) => unknown };
if (column?.footer && fo.valueCallback)
(column.footer as { valueCallback?: typeof fo.valueCallback }).valueCallback = fo.valueCallback;
} catch (_) {}
});
if (dp?.setRows && tabRowData.length) dp.setRows(tabRowData);
gridView.refresh();
appliedTabRef.current = activeTab;
} catch (e) {
console.warn('TabDataGrid activeTab layout re-apply:', e);
}
};
const delays = [200, 500, 1000, 1500];
const timers = delays.map(d => setTimeout(tryApply, d));
const container = document.querySelector(`[data-tab-key="${activeTab}"]`);
let observer: ResizeObserver | null = null;
if (container && typeof ResizeObserver !== 'undefined') {
observer = new ResizeObserver(() => {
if ((container as HTMLElement).offsetWidth > 0) tryApply();
});
gridView.refresh();
}, 50);
observer.observe(container);
}
return () => clearTimeout(timer);
}, [activeTab]);
return () => {
timers.forEach(t => clearTimeout(t));
observer?.disconnect();
};
}, [activeTab, columnInfo, rowDataMap]);
if (columnInfo.length === 0) {
return <div>컬럼 정보가 없습니다.</div>;
@@ -788,7 +884,9 @@ const TabDataGrid = ({ columnInfo, isExcelImport = true, isExcelExport = true, r
}
</div>
<div className="h-[60vh]" data-tab-key={tabKey}>
{activeTab === tabKey ? (
<RealGridReact
key={tabKey}
ref={(el) => {
if (el) {
gridRefs.current.set(tabKey, el);
@@ -803,10 +901,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
@@ -1018,7 +1121,7 @@ const TabDataGrid = ({ columnInfo, isExcelImport = true, isExcelExport = true, r
const onSortingHandler = createOnSortingHandler(columns, setRowData, sortDirRef, originalRowsRef, findGridElement);
(gridView as { onSorting?: (g: unknown, f: unknown[], d: unknown[]) => boolean }).onSorting = onSortingHandler;
}, 0);
}, 150);
}
} else {
gridRefs.current.delete(tabKey);
@@ -1053,6 +1156,7 @@ const TabDataGrid = ({ columnInfo, isExcelImport = true, isExcelExport = true, r
/>
))}
</RealGridReact>
) : null}
</div>
</div>
</TabsContent>
@@ -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,31 @@ 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,
piplSupprt: commonInfo.piplSupprt,
}));
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 +159,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 +272,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, useRef } from "react";
import TabDataGrid from "@/components/module/TabDataGrid";
import SearchForm from "@/components/module/SearchForm";
import SaveButton from "@/components/button/SaveButton";
@@ -10,19 +10,23 @@ 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 { ColumnInfoList, ColumnConfig } from "@/components/type/DataGrid.type";
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<Map<string, any[]>>(new Map());
const [rowDataMap, setRowDataMap] = useState<Map<string, Record<string, string | number>[]>>(new Map());
const [isLoading, setIsLoading] = useState<boolean>(false);
const [activeTab, setActiveTab] = useState<string>('a');
const searchParamRef = useRef<SearchFormValues | null>(null);
const columnInfo = useMemo(() => {
return SewagePipeColumnInfoListRvw.map((tab: any) => ({
return SewagePipeColumnInfoListRvw.map((tab: ColumnInfoList) => ({
...tab,
columns: tab.columns.map((col: any) => ({
columns: (tab.columns ?? []).map((col: ColumnConfig) => ({
...col,
editable: false as const,
styleName: col.styleName ?? ("rg-cell-readonly" as const),
@@ -30,7 +34,10 @@ const SewagePipeRvw = () => {
}));
}, []);
const handleReset = () => setRowDataMap(new Map());
const handleReset = () => {
setRowDataMap(new Map());
setActiveTab('a');
};
const handleSearch = async (values: SearchFormValues) => {
if (!values.sido || values.sido === 'ALL') {
@@ -38,60 +45,101 @@ const SewagePipeRvw = () => {
return;
}
setActiveTab('a');
setRowDataMap(new Map());
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) {
const rawList = response.data;
const newRowDataMap = new Map<string, any[]>();
newRowDataMap.set('a', toGridRows(rawList));
searchParamRef.current = searchParam;
const response = await statisticsManagementApi.getSewagePipeReviewRegionalList(searchParam);
if (response?.status === 'success' && response.data) {
const newRowDataMap = new Map<string, Record<string, string | number>[]>();
newRowDataMap.set('a', toGridRows(response.data as object[]));
setRowDataMap(newRowDataMap);
} else {
handleReset();
alert(response.message || "데이터가 없습니다.");
alert(response?.message || "데이터가 없습니다.");
}
} catch (error) {
console.error("조회 에러:", error);
alert('데이터 조회 중 오류가 발생했습니다.');
handleReset();
} finally {
setIsLoading(false);
}
};
const handleActiveTabChange = async (tabKey: string) => {
setActiveTab(tabKey);
const existingAData = rowDataMap.get('a');
const activeTabData = rowDataMap.get(tabKey);
const hasAData = Array.isArray(existingAData) && existingAData.length > 0;
const hasActiveTabData = Array.isArray(activeTabData) && activeTabData.length > 0;
if (hasAData && !hasActiveTabData && searchParamRef.current) {
try {
setIsLoading(true);
let response;
if (tabKey === 'b') {
response = await statisticsManagementApi.getSewagePipeFacilityList(searchParamRef.current);
}
if (response?.status === 'success' && response.data) {
setRowDataMap(prev => {
const next = new Map(prev);
next.set(tabKey, toGridRows(response.data as object[]));
return next;
});
} else {
alert(response?.message || "데이터가 없습니다.");
}
} catch {
alert('데이터 조회 중 오류가 발생했습니다.');
} finally {
setIsLoading(false);
}
}
};
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={handleActiveTabChange}
/>
</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,
@@ -214,6 +214,10 @@ export type DataGridProps = {
setRowData: React.Dispatch<
React.SetStateAction<Record<string, string | number>[]>
>;
/** 저장 등 시점에 그리드 현재 데이터를 가져올 수 있도록 할당 (편집 반영된 값) */
getGridDataRef?: {
current: (() => Record<string, string | number>[]) | null;
};
onRowClick?: (rowData: Record<string, string | number>) => void; // 행 클릭 이벤트
};
@@ -229,6 +233,10 @@ export type TabDataGridProps = {
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,7 +18,188 @@ const rawList = [
mergeCells: true,
editable: false,
sortdisable: true,
footer: [
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 '합계';
}
},
{
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 '합계';
}
}
]
},
{ 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));
}
}
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));
}
}
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: 4,
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;
@@ -32,8 +213,7 @@ const rawList = [
}
}
const divisionValues = Array.from(uniqueValues);
if (divisionValues.length > divisionIndex) return divisionValues[divisionIndex] + '합계';
return '합계';
return divisionValues.length > divisionIndex ? divisionValues[divisionIndex] + '합계' : '합계';
}
},
{
@@ -49,15 +229,14 @@ const rawList = [
}
}
const divisionValues = Array.from(uniqueValues);
if (divisionValues.length > divisionIndex) return divisionValues[divisionIndex] + '합계';
return '합계';
return divisionValues.length > divisionIndex ? divisionValues[divisionIndex] + '합계' : '합계';
}
}
]
},
{ name: 'gugun', fieldName: 'gugun', header: { text: '시군구' }, mergeCells: true, editable: false, sortdisable: true },
{ name: 'adstDong', fieldName: 'adstDong', header: { text: '행정구역명' }, editable: false },
{
{ name: 'gugun', fieldName: 'gugun', header: { text: '시군구' }, mergeCells: true, editable: false, sortdisable: true },
{ name: 'bscFctNm', fieldName: 'bscFctNm', header: { text: '하수처리장명' }, editable: false },
{
name: 'displayYear',
fieldName: 'displayYear',
header: { text: '구분' },
@@ -98,164 +277,80 @@ const rawList = [
}
]
},
{ name: 'fctCpc', fieldName: 'fctCpc', header: { text: '하수처리\n시설용량\n(㎥/일)' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' },
/* 하수정비 기본계획 */
{ 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: 'procZoneSm', fieldName: 'procZoneSm', header: { text: '합계' }, footer: footerDecimal3ArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.000' },
{ name: 'cmbnProcZoneAr', fieldName: 'cmbnProcZoneAr', header: { text: '합류식' }, footer: footerDecimal3ArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.000' },
{ name: 'sprtProcZoneAr', fieldName: 'sprtProcZoneAr', header: { text: '분류식' }, footer: footerDecimal3ArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.000' },
{ name: 'sepRate', fieldName: 'sepRate', header: { text: '분류식화(%)' }, footer: footerAvgDecimalArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.0' },
/* 합류식(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: 'drngEqpSm', fieldName: 'drngEqpSm', header: { text: '합계' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0' },
{ name: 'cmbnDrngEqp', fieldName: 'cmbnDrngEqp', header: { text: '합류식' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0' },
{ name: 'sprtDrngEqpSm', fieldName: 'sprtDrngEqpSm', header: { text: '합계(분류)' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0' },
{ name: 'sprtDrngEqpCnnc', fieldName: 'sprtDrngEqpCnnc', header: { text: '연결' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0' },
{ name: 'sprtDrngEqpDcnnc', fieldName: 'sprtDrngEqpDcnnc', header: { text: '미연결' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0' },
/* 분류식 - 오수관로 */
{ 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: 'piplFcltyExtn', fieldName: 'piplFcltyExtn', header: { text: '총\n시설연장\n(m)' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' },
{ name: 'icgMnnSm', fieldName: 'icgMnnSm', header: { text: '합계' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' },
{ name: 'icgExtn', fieldName: 'icgExtn', header: { text: '차집' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' },
{ name: 'mnnExtn', fieldName: 'mnnExtn', header: { text: '오수간선' }, 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: 'cmbnSoilholeSite', fieldName: 'cmbnSoilholeSite', header: { text: '우수토실(합류)' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0' },
{ name: 'sprtSoilholeSite', fieldName: 'sprtSoilholeSite', header: { text: '우수토실(분류)' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0' },
{ name: 'sprtSoilspllSite', fieldName: 'sprtSoilspllSite', 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' },
/* 지선(m) */
{ name: 'brnchlnSm', fieldName: 'brnchlnSm', header: { text: '합계' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' },
{ name: 'cmbnBrnchlnExtn', fieldName: 'cmbnBrnchlnExtn', header: { text: '합류식' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' },
{ name: 'rainwtrBrnchlnExtn', fieldName: 'rainwtrBrnchlnExtn', header: { text: '우수' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' },
{ name: 'pltwtrBrnchlnExtn', fieldName: 'pltwtrBrnchlnExtn', header: { text: '오수' }, footer: footerIntArrayByDivision, valueType: ValueType.NUMBER, numberFormat: '#,##0.00' },
/* 대장 및 기타 */
{ 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: [
{ name: 'rmk', fieldName: 'rmk', header: { text: '비고' } },
],
columnLayout: [
{
name: 'addrGroup',
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'
]
},
'gugun',
'bscFctNm',
]
},
'fctCpc',
'displayYear',
{ name: 'areaGroup', header: { text: '처리구역면적(㎢)' }, items: ['procZoneSm', 'cmbnProcZoneAr', 'sprtProcZoneAr'] },
'sepRate',
{
name: 'drainGroup', header: { text: '배수설비(개소)' },
items: ['drngEqpSm', 'cmbnDrngEqp', { name: 'sepDrainGroup', header: { text: '분류식' }, items: ['sprtDrngEqpSm', 'sprtDrngEqpCnnc', 'sprtDrngEqpDcnnc'] }],
},
{
name: 'pipeGroup', header: { text: '하수관로' },
items: [
'piplFcltyExtn',
{ name: 'trunkGroup', header: { text: '차집/오수간선(m)' }, items: ['icgMnnSm', 'icgExtn', 'mnnExtn'] },
{
name: 'siteGroup', header: { text: '토실/토구(개소)' },
items: [
{ name: 'cmbnSiteGroup', header: { text: '합류식' }, items: ['cmbnSoilholeSite'] },
{ name: 'sepSiteGroup', header: { text: '분류식' }, items: ['sprtSoilholeSite', 'sprtSoilspllSite'] }
]
},
{
name: 'branchGroup', header: { text: '지선(m)' },
items: ['brnchlnSm', 'cmbnBrnchlnExtn', { name: 'sepBranchGroup', header: { text: '우수/오수' }, items: ['rainwtrBrnchlnExtn', 'pltwtrBrnchlnExtn'] }]
}
],
},
'rmk'
]
},
{
tabKey: 'c',
tabLabel: '관종별 현황',
@@ -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();
@@ -6,12 +6,15 @@ import {
type StatsInputStatusReviewApiResponse
} from "../model/statistics-input-review-interface";
import {
type SewagePipeReviewApiResponse
type SewagePipeReviewRegionalApiResponse
} from "../model/statistics-sewage-pipe-review-interface";
import {
type SewerageSupplyRateReviewRequestParams,
type SewerageSupplyRateReviewApiResponse,
} from "../model/statistics-sewerage-penetration-review-interface";
import {
type SewagePipeFacilityApiResponse
} from "../model/statistics-sewage-pipe-review-interface";
export class StatisticsManagementApiService {
/**
@@ -58,16 +61,34 @@ export class StatisticsManagementApiService {
* 02. 하수관로 지역별 시설현황 목록 조회 (Tab1)
* @param params (sido, gugun, yr, bsnmCd)
*/
public async getSewagePipeReviewList(
public async getSewagePipeReviewRegionalList(
params: {
yr?: string;
bsnmCd?: string;
sido?: string;
gugun?: string;
}
): Promise<SewagePipeReviewApiResponse> {
const response = await axios.get<SewagePipeReviewApiResponse>(
`${ENDPOINT.ST_SERVICE.REVIEW}/pipe/tab1`,
): Promise<SewagePipeReviewRegionalApiResponse> {
const response = await axios.get<SewagePipeReviewRegionalApiResponse>(
`${ENDPOINT.ST_SERVICE.REVIEW}/sewer-pipe/regional-status`,
{ params }
);
return response.data;
}
/**
* 02. 하수관로 시설별 설치현황 목록 조회 (Tab2)
* @param params (sido, gugun, yr, bsnmCd)
*/
public async getSewagePipeFacilityList(
params: {
yr?: string;
bsnmCd?: string;
sido?: string;
gugun?: string;
}
): Promise<SewagePipeFacilityApiResponse> {
const response = await axios.get<SewagePipeFacilityApiResponse>(
`${ENDPOINT.ST_SERVICE.REVIEW}/sewer-pipe/facility-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>;
}
@@ -1,9 +1,9 @@
import type { ApiResponse } from "./statistics-interface";
/**
* 02. 하수관로 지역별 시설현황 아이템 인터페이스 (Tab1)
* 02. 하수관로 tab1.지역별 시설현황 아이템 인터페이스 (Tab1)
*/
export interface SewagePipeReviewItem {
export interface SewagePipeReviewrRegionalItem {
// 기본 정보 및 행 제어
sido: string; // 시도
gugun: string; // 시군구
@@ -64,11 +64,11 @@ export interface SewagePipeReviewItem {
}
export interface SewagePipeReviewResponseData {
sewagePipeList: SewagePipeReviewItem[];
sewagePipeList: SewagePipeReviewrRegionalItem[];
}
export type SewagePipeReviewApiResponse = ApiResponse<SewagePipeReviewItem[]> & {
export type SewagePipeReviewRegionalApiResponse = ApiResponse<SewagePipeReviewrRegionalItem[]> & {
message: string;
};
@@ -79,5 +79,74 @@ export interface SewagePipeReviewService {
bsnmCd?: string; // 사업장 코드
sido?: string; // 시도
gugun?: string; // 구군
}): Promise<SewagePipeReviewApiResponse>;
}): Promise<SewagePipeReviewRegionalApiResponse>;
}
export interface SewagePipeReviewFacilityItem {
// 1. 행정구역 및 기본 정보
sido: string; // 시도
gugun: string; // 시군구
adstCd: string; // 행정코드
adstDong: string; // 행정구역명
yr: string; // 연도
rowType: 'OLD' | 'CURR'; // 전년도/당해년도 구분
displayYear: string; // 구분 표시용 (2024년확정 등)
// 2. 시설 정보
bscFctNm: string; // 하수처리장명
fctCpc: number; // 하수처리시설용량(㎥/일)
// 3. 처리구역면적(㎢)
procZoneSm: number; // 합계(면적)
cmbnProcZoneAr: number; // 합류식(면적)
sprtProcZoneAr: number; // 분류식(면적)
sepRate: number; // 분류식화 (%)
// 4. 배수설비(개소)
drngEqpSm: number; // 합계(배수)
cmbnDrngEqp: number; // 합류식(배수)
sprtDrngEqpSm: number; // 합계(분류배수)
sprtDrngEqpCnnc: number; // 연결
sprtDrngEqpDcnnc: number; // 미연결
// 5. 하수관로 (m)
piplFcltyExtn: number; // 총 시설연장(m)
icgMnnSm: number; // 합계(차집/오수간선)
icgExtn: number; // 차집
mnnExtn: number; // 오수간선
// 6. 토실/토구 (개소)
cmbnSoilholeSite: number; // 우수토실(합류)
sprtSoilholeSite: number; // 우수토실(분류)
sprtSoilspllSite: number; // 우수토구(분류)
// 7. 지선 (m)
brnchlnSm: number; // 합계(지선)
cmbnBrnchlnExtn: number; // 합류식(지선)
rainwtrBrnchlnExtn: number; // 우수(지선)
pltwtrBrnchlnExtn: number; // 오수(지선)
// 비고
rmk: string; // 비고
}
export interface SewagePipeFacilityResponseData {
facilityPipeList: SewagePipeReviewFacilityItem[];
}
export type SewagePipeFacilityApiResponse = ApiResponse<SewagePipeReviewFacilityItem[]> & {
message: string;
};
export interface SewagePipeFacilityService {
getSewagePipeFacilityList(params: {
yr?: string; // 조회 연도
bsnmCd?: string; // 사업장 코드
sido?: string; // 시도
gugun?: string; // 구군
}): Promise<SewagePipeFacilityApiResponse>;
}