st: feat: 로그 조회, 입력-하수관로 조회, 입력-하수도보급률 예외 제출

This commit is contained in:
2026-04-07 14:57:35 +09:00
parent 1489fdc664
commit 51b1daf083
28 changed files with 2489 additions and 1133 deletions
+1 -1
View File
@@ -27,7 +27,7 @@
}
/* 셀 클래스가 있으면 위 기본색보다 우선 (같은 specificity + 나중 규칙) */
.rg-grid .rg-body .rg-table .rg-data-row .rg-data-cell.rg-cell-readonly {
@apply !bg-[#f0f0f0] !text-[#666];
@apply !bg-[#f9f8f8] !text-[#666];
}
.rg-grid .rg-body .rg-table .rg-data-row .rg-data-cell.rg-cell-verify-error {
@apply !bg-[#ffe6e6] !text-[#c00];
@@ -72,6 +72,8 @@ const CompactDataGrid = ({
getGridDataRef,
cellStyleCallback,
onRowClick,
compactFullWidth = false,
compactGridHeightClass = "h-[65vh]",
}: DataGridProps) => {
const { columns, columnLayout } = columnInfo;
const gridRef = useRef<RealGridReact>(null);
@@ -313,7 +315,7 @@ const CompactDataGrid = ({
return () => {
if (getGridDataRef) getGridDataRef.current = null;
};
}, [columns, columnLayout, fixedColumns, rowData, showHeaderCheckbox, getGridDataRef]);
}, [columns, columnLayout, fixedColumns, rowData, showHeaderCheckbox, getGridDataRef, compactFullWidth, compactGridHeightClass]);
// 행 클릭 이벤트
useEffect(() => {
@@ -336,8 +338,8 @@ const CompactDataGrid = ({
}, [onRowClick]);
return (
<div className="flex flex-col gap-[0.6rem] mx-[0.6rem] w-1/2">
<div className="h-[65vh]">
<div className={`flex flex-col gap-[0.6rem] ${compactFullWidth ? "w-full min-w-0" : "w-1/2"}`}>
<div className={compactGridHeightClass}>
<RealGridReact
ref={gridRef}
autoGenerateField={true}
@@ -0,0 +1,51 @@
"use client";
import { useState } from "react";
import { Button, Dialog, DialogContent, DialogHeader, DialogTitle } from "@common/components";
import SidoSggFacilityNameSearchForm from "./SidoSggFacilityNameSearchForm";
type GridTopActionButtonProps = {
text: string;
onClick?: (helpers: { openDialog: (title?: string) => void; closeDialog: () => void }) => void;
/** 상위 화면 검색 통계연도 — 시설 조회 다이얼로그에 전달 */
statsYr?: string;
};
const GridTopActionButton = ({ text, onClick, statsYr }: GridTopActionButtonProps) => {
const [isOpen, setIsOpen] = useState(false);
const [title, setTitle] = useState(text);
const openDialog = (nextTitle?: string) => {
setTitle(nextTitle ?? text);
setIsOpen(true);
};
const closeDialog = () => setIsOpen(false);
const handleClick = () => {
if (onClick) {
onClick({ openDialog, closeDialog });
return;
}
openDialog(text);
};
return (
<>
<Button className="h-[3.2rem] px-6" color="white" type="button" onClick={handleClick}>
<span>{text}</span>
</Button>
<Dialog open={isOpen} onOpenChange={setIsOpen}>
<DialogContent aria-describedby={undefined} className="max-w-[100rem]">
<DialogHeader>
<DialogTitle>{title}</DialogTitle>
</DialogHeader>
<SidoSggFacilityNameSearchForm statsYr={statsYr} />
</DialogContent>
</Dialog>
</>
);
};
export default GridTopActionButton;
+1 -1
View File
@@ -233,7 +233,7 @@ const SearchForm = (props: SearchFormProps = {}) => {
</div>
}
<div className="flex items-center gap-2 ml-auto">
<Button color="green" className="h-[3.2rem] w-[96px]" type="button" onClick={handleReset}>
<Button color="white" className="h-[3.2rem] w-[96px]" type="button" onClick={handleReset}>
초기화
</Button>
<Button className="h-[3.2rem] w-[96px]" type="submit">
@@ -0,0 +1,321 @@
"use client";
import { useState } from "react";
import useSWR from "swr";
import { ValueType } from "realgrid";
import {
Button,
Input,
Label,
Select,
SelectTrigger,
SelectContent,
SelectItem,
SelectValue,
LoadingWithAnimation
} from "@common/components";
import CompactDataGrid from "./CompactDataGrid";
import SaveButton from "../button/SaveButton";
import { useAlertStore } from "@/shared/lib";
import { statisticsAPi } from "@/entities/statistics/api/statistics-service";
import type { ApiResponse, SearchCondition, Sgg } from "@/entities/statistics/model/statistics-interface";
import type { ColumnInfo } from "../type/DataGrid.type";
import type {
SeweragePipeFacilitiesListRequest,
SeweragePipeFacilityAdminAreaListRequest,
} from "@/entities/statistics/model/input/statistics-sewage-pipe-input-interface";
import { statisticsInputSewPipeApiService } from "@/entities/statistics/api/input/statistics-sewage-pipe-input-service";
/** 다이얼로그 좌측: 처리시설 정보 (순번 없음) */
const treatmentFacilityColumnInfo: ColumnInfo = {
columns: [
{ name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false },
{ name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false },
{ name: "fctNm", fieldName: "fctNm", header: { text: "시설명" }, editable: false },
{
name: "fctCpc",
fieldName: "fctCpc",
header: { text: "시설용량(m³/일)" },
valueType: ValueType.NUMBER,
dataFormat: "#,##0",
editable: false,
},
],
columnLayout: ["ctpvNm", "sggNm", "fctNm", "fctCpc"],
};
/** 다이얼로그 우측: 행정구역 정보 (순번 없음, 헤더 체크박스는 showHeaderCheckbox) */
const adminRegionColumnInfo: ColumnInfo = {
columns: [
{ name: "ctpvNm", fieldName: "ctpvNm", header: { text: "시도" }, editable: false },
{ name: "sggNm", fieldName: "sggNm", header: { text: "시군구" }, editable: false },
{ name: "emdNm", fieldName: "emdNm", header: { text: "읍면동" }, editable: false },
],
columnLayout: ["ctpvNm", "sggNm", "emdNm"],
};
const getSidoListFetcher = async (): Promise<ApiResponse<SearchCondition>> => {
return statisticsAPi.getSidoList();
};
const getSggAllListFetcher = async (key: [string, string]): Promise<ApiResponse<Sgg[]>> => {
return statisticsAPi.getSggAllList(key[1]);
};
type SidoSggFacilityNameSearchFormProps = {
/** 상위(SewagePipeInp 등) 검색 조건의 통계연도 — facilitiesList 등 API 필수값 */
statsYr?: string;
};
const SidoSggFacilityNameSearchForm = ({ statsYr }: SidoSggFacilityNameSearchFormProps) => {
const { setMessage: alert } = useAlertStore((state) => state);
const { data: sidoList } = useSWR<ApiResponse<SearchCondition>>("sido-list", getSidoListFetcher, {
revalidateOnFocus: false,
revalidateOnReconnect: false,
revalidateIfStale: false,
dedupingInterval: 2000,
shouldRetryOnError: false,
onSuccess: (data) => {
if (data.status !== "success") {
alert(`시도 목록을 불러오는데 실패했습니다.\n새로고침 후 다시 시도해주세요.`);
}
},
onError: () => {
alert(`시도 목록을 불러오는데 실패했습니다.\n새로고침 후 다시 시도해주세요.`);
},
});
const [sido, setSido] = useState<string>("ALL");
const [gugun, setGugun] = useState<string>("ALL");
const [fctNm, setFctNm] = useState<string>("");
const [isLoading, setIsLoading] = useState<boolean>(false);
const [treatmentFacilityRows, setTreatmentFacilityRows] = useState<Record<string, string | number>[]>([]);
const [adminRegionRows, setAdminRegionRows] = useState<Record<string, string | number>[]>([]);
const sggKey: [string, string] | null = sido && sido !== "ALL" ? ["sgg-all", sido] : null;
const { data: sggList } = useSWR<ApiResponse<Sgg[]>>(sggKey, getSggAllListFetcher, {
revalidateOnFocus: false,
revalidateOnReconnect: false,
revalidateIfStale: false,
dedupingInterval: 2000,
shouldRetryOnError: false,
onSuccess: (data) => {
if (data.status !== "success") {
alert(`시군구 목록을 불러오는데 실패했습니다.\n새로고침 후 다시 시도해주세요.`);
}
},
onError: () => {
alert(`시군구 목록을 불러오는데 실패했습니다.\n새로고침 후 다시 시도해주세요.`);
},
});
const sggOptions = sggList?.data ?? [];
/** 시설 목록·행정구역 API 공통 — 하수도 사업자코드(bsnmCd) */
const resolveSewerBzmnCd = (): string => {
const sidoItem = sidoList?.data?.sidoList?.find((v) => v.sido === sido);
const raw =
gugun !== "ALL" && gugun
? sggOptions.find((s) => s.gugun === gugun)?.bsnmCd
: sidoItem?.bsnmCd;
return raw?.trim() ?? "";
};
const handleSidoChange = (value: string) => {
setSido(value);
setGugun("ALL");
};
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (!statsYr?.trim()) {
alert("상단에서 통계연도로 조회한 뒤 이용해 주세요.");
return;
}
if (!sido || sido === "ALL") {
alert("시도를 선택해주세요.");
return;
}
const trimmedFclty = fctNm.trim();
const sewerBzmnCd = resolveSewerBzmnCd();
const params: SeweragePipeFacilitiesListRequest = {
statsYr: statsYr.trim(),
...(sewerBzmnCd ? { sewerBzmnCd } : {}),
ctpvNm: sido,
...(gugun !== "ALL" && gugun ? { sggNm: gugun } : {}),
...(trimmedFclty ? { fcltyNm: trimmedFclty } : {}),
};
try {
setIsLoading(true);
const response = await statisticsInputSewPipeApiService.getFacilitiesList(params);
if (response.status !== "success") {
alert(response.message ?? "처리시설 목록 조회에 실패했습니다.");
return;
}
const list = response.data ?? [];
setTreatmentFacilityRows(
list.map((item) => ({
ctpvNm: item.ctpvNm,
sggNm: item.sggNm,
fctNm: item.bscFctNm,
fctCpc: item.fctCpc,
bscFctCd: item.bscFctCd,
admdstCd: item.admdstCd,
statsYr: item.statsYr,
})),
);
setAdminRegionRows([]);
} catch {
alert("처리시설 목록 조회 중 오류가 발생했습니다.");
} finally {
setIsLoading(false);
}
};
const handleTreatmentFacilityRowClick = async (row: Record<string, string | number>) => {
if (!statsYr?.trim()) {
alert("상단에서 통계연도로 조회한 뒤 이용해 주세요.");
return;
}
const swtrmtFcltCd = String(row.bscFctCd ?? "").trim();
if (!swtrmtFcltCd) {
alert("처리시설 코드가 없습니다.");
return;
}
const ctpvNm = String(row.ctpvNm ?? sido);
const sggNm = String(
row.sggNm ?? (gugun !== "ALL" && gugun ? gugun : ""),
);
const params: SeweragePipeFacilityAdminAreaListRequest = {
statsYr: statsYr.trim(),
swtrmtFcltCd,
sewerBzmnCd: String(row.admdstCd ?? "").trim(),
ctpvNm,
sggNm,
};
try {
setIsLoading(true);
const response = await statisticsInputSewPipeApiService.getFacilityAdminAreaList(params);
if (response.status !== "success") {
alert(response.message ?? "행정구역 목록 조회에 실패했습니다.");
return;
}
const list = response.data ?? [];
setAdminRegionRows(
list.map((item) => ({
ctpvNm: item.ctpvNm,
sggNm: item.sggNm,
emdNm: item.admdstNm,
admdstCd: item.admdstCd,
})),
);
} catch {
alert("행정구역 목록 조회 중 오류가 발생했습니다.");
} finally {
setIsLoading(false);
}
};
const handleSaveAdminRegions = () => {
alert("저장 API 연동 예정입니다.");
};
return (
<div className="flex flex-col gap-4 pt-2">
<form onSubmit={handleSubmit} className="flex flex-col gap-4">
<div className="flex flex-wrap items-end gap-4">
<div className="flex items-center gap-2">
<Label className="text-label shrink-0">시도</Label>
<Select value={sido} onValueChange={handleSidoChange}>
<SelectTrigger className="w-[12rem]">
<SelectValue placeholder="전체" />
</SelectTrigger>
<SelectContent>
<SelectItem value="ALL">전체</SelectItem>
{sidoList?.data?.sidoList?.map((item) => (
<SelectItem key={item.adstDstrCd} value={item.sido}>
{item.sido}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex items-center gap-2">
<Label className="text-label shrink-0">시군구</Label>
<Select value={gugun} onValueChange={setGugun} disabled={!sido || sido === "ALL"}>
<SelectTrigger className="w-[12rem]">
<SelectValue placeholder="전체" />
</SelectTrigger>
<SelectContent>
<SelectItem value="ALL">전체</SelectItem>
{sggOptions?.map((sgg) => (
<SelectItem key={sgg.adstDstrCd} value={sgg.gugun}>
{sgg.gugun}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="flex items-center gap-2 min-w-[20rem] flex-1">
<Label className="text-label shrink-0">시설명</Label>
<Input
className="flex-1 min-w-[12rem]"
value={fctNm}
onChange={(e) => setFctNm(e.target.value)}
placeholder="시설명 입력"
/>
</div>
<Button type="submit" className="h-[3.2rem] w-[96px]" color="primary">
조회
</Button>
</div>
<div className="ml-auto">
<SaveButton saveFunc={handleSaveAdminRegions} />
</div>
</form>
{isLoading && <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>}
<div className="flex gap-[1.2rem] items-stretch w-full min-w-0">
<div className="flex-1 flex flex-col gap-2 min-w-0">
<h3 className="text-[1.4rem] font-medium text-foreground">처리시설 정보</h3>
<CompactDataGrid
columnInfo={treatmentFacilityColumnInfo}
rowData={treatmentFacilityRows}
setRowData={setTreatmentFacilityRows}
showHeaderCheckbox={false}
compactFullWidth
onRowClick={handleTreatmentFacilityRowClick}
// compactGridHeightClass="h-[min(40vh,28rem)] min-h-[16rem]"
/>
</div>
<div className="flex-1 flex flex-col gap-2 min-w-0">
<div className="flex items-center justify-between gap-2">
<h3 className="text-[1.4rem] font-medium text-foreground">행정구역 정보</h3>
</div>
<CompactDataGrid
columnInfo={adminRegionColumnInfo}
rowData={adminRegionRows}
setRowData={setAdminRegionRows}
showHeaderCheckbox
compactFullWidth
// compactGridHeightClass="h-[min(40vh,28rem)] min-h-[16rem]"
/>
</div>
</div>
</div>
);
};
export default SidoSggFacilityNameSearchForm;
@@ -14,7 +14,7 @@ const SplitDataGrid = ({
enableSort = false,
}: SplitDataGridProps) => {
return (
<div className="flex gap-[0.6rem] mx-[0.6rem]">
<div className="flex gap-[1.2rem]">
<CompactDataGrid
columnInfo={leftColumnInfo}
fixedColumns={leftFixedColumns}
+29 -1
View File
@@ -13,6 +13,7 @@ import { useTabStore } from "@/store/tab-store"
import { Tabs, TabsList, TabsTrigger, TabsContent } from "@common/components";
import { Button } from "@common/components";
import SplitDataGrid from "./SplitDataGrid";
import GridTopActionButton from "./GridTopActionButton";
// RealGrid 타입 확장
declare module "realgrid" {
@@ -214,7 +215,20 @@ function createNumberComparer(dp: RealGridDataProviderComparer): (field: string
};
}
const TabDataGrid = ({ columnInfo, isExcelImport = true, isExcelExport = true, rows = [], showHeaderCheckbox = false, rowDataMap, setRowDataMap, activeTab: activeTabProp, onActiveTabChange, getGridDataRef, enableSort = false }: TabDataGridProps) => {
const TabDataGrid = ({
columnInfo,
isExcelImport = true,
isExcelExport = true,
rows = [],
showHeaderCheckbox = false,
rowDataMap,
setRowDataMap,
activeTab: activeTabProp,
onActiveTabChange,
getGridDataRef,
enableSort = false,
facilitySearchStatsYr,
}: TabDataGridProps) => {
// 각 탭별로 그리드 ref와 데이터를 관리
const gridRefs = useRef<Map<string, RealGridReact>>(new Map());
const activeTabRef = useRef<string>("");
@@ -738,6 +752,11 @@ const TabDataGrid = ({ columnInfo, isExcelImport = true, isExcelExport = true, r
if (info.isSplitGrid && info.left && info.right) {
const leftRowData = rowDataMap.get(info.left.tabKey || `${tabKey}_left`) || [];
const rightRowData = rowDataMap.get(info.right.tabKey || `${tabKey}_right`) || [];
const splitTopButton = info.right.topButton;
const showSplitTopButton =
Boolean(splitTopButton?.text) &&
(splitTopButton?.visible ?? true) &&
isExcelImport;
// ColumnInfoList를 ColumnInfo로 변환
const leftColumnInfo: ColumnInfo = {
@@ -751,6 +770,15 @@ const TabDataGrid = ({ columnInfo, isExcelImport = true, isExcelExport = true, r
return (
<TabsContent key={tabKey} value={tabKey}>
<div className="flex justify-end items-center gap-[0.6rem] mb-[0.6rem]">
{showSplitTopButton && (
<GridTopActionButton
text={splitTopButton?.text ?? ""}
onClick={splitTopButton?.onClick}
statsYr={facilitySearchStatsYr ?? undefined}
/>
)}
</div>
<SplitDataGrid
leftColumnInfo={leftColumnInfo}
rightColumnInfo={rightColumnInfo}
@@ -1,4 +1,4 @@
import { useState, useMemo } from "react";
import { useState, useMemo, useRef } from "react";
import SearchForm from "@/components/module/SearchForm";
import TabDataGrid from "@/components/module/TabDataGrid";
@@ -12,16 +12,98 @@ import type { ColumnInfoList, ColumnConfig } from "@/components/type/DataGrid.ty
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import { useAlertStore } from "@/shared/lib";
import { useAdminStore } from "@/store/admin-store";
import { statisticsInputSewSupplyRateApi } from "@/entities/statistics/api/input/statistics-input-sew-supply-rate-service";
import { useConfirm } from "@frontend-opensource/use-react-hooks";
import { doList } from "@/components/util/column/input/DoList";
import { statisticsInputSewPipeApiService } from "@/entities/statistics/api/input/statistics-sewage-pipe-input-service";
import type {
SeweragePipeInputAdminAreaConnectionItem,
SeweragePipeInputFacilityConnectionItem,
SeweragePipeInputFactInstallStatusItem,
SeweragePipeInputManholeStatusItem,
SeweragePipeInputPipeDiameterStatusItem,
SeweragePipeInputPipeTypeStatusItem,
SeweragePipeInputRegionalFactStatusItem,
SeweragePipeInputRegionalFactStatusSaveRequest,
SeweragePipeInputStatusInfo,
SeweragePipeRequestParams,
} from "@/entities/statistics/model/input/statistics-sewage-pipe-input-interface";
import { RegionalFactStatusInpColumn } from "@/components/util/column/input/sewagePipe/RegionalFactStatusInpColumn";
import { toGridRows } from "@/components/util/grid/gridFunction";
import { LoadingWithAnimation } from "@common/components";
import { getUserInfo } from "../../../../../../packages/common/components/src/api";
const applyEnterableFromStatus = (
statusInfo: SeweragePipeInputStatusInfo | null | undefined,
periodEnterable: boolean,
setIsEnterable: (v: boolean) => void
) => {
if (statusInfo?.laeSbmsnSttsCd) {
if (["2", "3"].includes(statusInfo.laeSbmsnSttsCd)) {
setIsEnterable(false);
} else {
setIsEnterable(periodEnterable);
}
} else {
setIsEnterable(periodEnterable);
}
};
function buildSewagePipeSearchParam(
values: SearchFormValues,
overrides?: { mode?: string },
): SeweragePipeRequestParams {
return {
statsYr: values.year,
sewerBzmnCd: values.bsnmCd,
ctpvNm: values.sido,
sggNm: values.gugun === "ALL" ? "" : values.gugun,
...(overrides?.mode ? { mode: overrides.mode } : {}),
};
}
// 하수도 통계 관리 > 02. 하수관로
const SewagePipeInp = () => {
const [rowDataMap, setRowDataMap] = useState<Map<string, Record<string, string | number>[]>>(new Map());
const { setMessage: alert } = useAlertStore((state) => state);
const [_isLoading, setIsLoading] = useState<boolean>(false);
const [activeTab, setActiveTab] = useState<string>(SewagePipeInpColumnInfoList[0]?.tabKey ?? "ST00020001");
const [isEnterable, _setIsEnterable] = useState<boolean>(true);
const [isLoading, setIsLoading] = useState<boolean>(false);
const [searchValues, setSearchValues] = useState<SearchFormValues | null>(null);
const [isEnterable, setIsEnterable] = useState<boolean>(true);
const [lastSearchedYear, setLastSearchedYear] = useState<string | null>(null);
const [lastSearchedValues, setLastSearchedValues] = useState<SearchFormValues | null>(null);
const [statusInfo, setStatusInfo] = useState<SeweragePipeInputStatusInfo | null>(null);
// const [showDailog, setShowDailog] = useState<boolean>(false);
// const [isExceptionSubmitFlow, setIsExceptionSubmitFlow] = useState(false);
const [_statsCd, setStatsCd] = useState<string>('');
/** API 연결 전에도 검색 후 툴바 등 UI를 쓸 수 있게 함 */
const [hasSearched, setHasSearched] = useState(false);
// const { confirm } = useConfirm();
const { isAdminInfo } = useAdminStore();
const getGridDataRef = useRef<(() => Record<string, string | number>[]) | null>(null);
const searchParamRef = useRef<SeweragePipeRequestParams | null>(null);
/** 검색 시 checkInputPeriod 결과 — 탭 전환 지연 조회 시 편집 가능 여부와 동일하게 맞춤 */
const periodEnterableRef = useRef(true);
/** 검색 완료 후(API 연결 전) 탭별 지연 로그용 — 첫 탭에 행이 없어도 동작하도록 보조 */
const searchDoneRef = useRef(false);
const lazyTabLoggedRef = useRef<Set<string>>(new Set());
const firstTabKey = SewagePipeInpColumnInfoList[0]?.tabKey ?? "ST00020001";
const handleReset = () => {
setRowDataMap(new Map());
setStatusInfo(null);
setLastSearchedYear(null);
setLastSearchedValues(null);
setHasSearched(false);
searchParamRef.current = null;
searchDoneRef.current = false;
lazyTabLoggedRef.current.clear();
periodEnterableRef.current = true;
setActiveTab(firstTabKey);
};
const columnInfo = useMemo(() => {
const canEdit = isEnterable || isAdminInfo;
return SewagePipeInpColumnInfoList.map((tab: ColumnInfoList) => {
@@ -46,51 +128,313 @@ const SewagePipeInp = () => {
});
}, [isEnterable, isAdminInfo]);
// const handleSearch = async (values: SearchFormValues) => {
// if (!values.sido || values.sido === 'ALL') {
// alert('시도를 선택해주세요.');
// return;
// }
/** mergeIntoExistingMap: true면 지난연도(PRE) 등 — 현재 탭만 갱신, 나머지 탭 캐시 유지 */
const loadSewagePipeActiveTabData = async (
tabKey: string,
searchParam: SeweragePipeRequestParams,
values: SearchFormValues,
checkEnterable: boolean,
mergeIntoExistingMap: boolean,
updateLastSearchedValues: boolean,
): Promise<boolean> => {
const applyRowMap = (apply: (m: Map<string, Record<string, string | number>[]>) => void) => {
if (mergeIntoExistingMap) {
setRowDataMap((prev) => {
const next = new Map(prev);
apply(next);
return next;
});
} else {
const next = new Map<string, Record<string, string | number>[]>();
apply(next);
setRowDataMap(next);
}
};
// setActiveTab('ST00020001');
if (tabKey === 'ST00020001') {
const response = await statisticsInputSewPipeApiService.getRegionalFactStatus(searchParam);
if (response.status === 'success') {
const commonInfo = response.data.commonInfo;
const convertedData = (response.data.regionalFactStatusList ?? []).map((d) => ({
ctpvNm: commonInfo.ctpvNm,
...d,
}));
applyRowMap((m) => m.set('ST00020001', toGridRows(convertedData)));
setStatusInfo(response.data.statusInfo);
setStatsCd(commonInfo.statsCd);
applyEnterableFromStatus(response.data.statusInfo, checkEnterable, setIsEnterable);
if (updateLastSearchedValues) setLastSearchedValues(values);
return true;
}
alert(response.message || '데이터 조회 중 오류가 발생했습니다.');
return false;
}
if (tabKey === 'ST00020002') {
const response = await statisticsInputSewPipeApiService.getFactInstallStatus(searchParam);
if (response.status === 'success') {
const commonInfo = response.data.commonInfo;
const convertedData = (response.data.factInstallStatusList ?? []).map((d) => ({
ctpvNm: commonInfo.ctpvNm,
...d,
}));
applyRowMap((m) => m.set('ST00020002', toGridRows(convertedData)));
setStatusInfo(response.data.statusInfo);
setStatsCd(commonInfo.statsCd);
applyEnterableFromStatus(response.data.statusInfo, checkEnterable, setIsEnterable);
if (updateLastSearchedValues) setLastSearchedValues(values);
return true;
}
alert(response.message || '데이터 조회 중 오류가 발생했습니다.');
return false;
}
if (tabKey === 'ST00020003') {
const response = await statisticsInputSewPipeApiService.getPipeTypeStatus(searchParam);
if (response.status === 'success') {
const commonInfo = response.data.commonInfo;
const convertedData = (response.data.pipeTypeStatusList ?? []).map((d) => ({
ctpvNm: commonInfo.ctpvNm,
...d,
}));
applyRowMap((m) => m.set('ST00020003', toGridRows(convertedData)));
setStatusInfo(response.data.statusInfo);
setStatsCd(commonInfo.statsCd);
applyEnterableFromStatus(response.data.statusInfo, checkEnterable, setIsEnterable);
if (updateLastSearchedValues) setLastSearchedValues(values);
return true;
}
alert(response.message || '데이터 조회 중 오류가 발생했습니다.');
return false;
}
if (tabKey === 'ST00020004') {
const response = await statisticsInputSewPipeApiService.getPipeDiameterStatus(searchParam);
if (response.status === 'success') {
const commonInfo = response.data.commonInfo;
const convertedData = (response.data.pipeDiameterStatusList ?? []).map((d) => ({
ctpvNm: commonInfo.ctpvNm,
...d,
}));
applyRowMap((m) => m.set('ST00020004', toGridRows(convertedData)));
setStatusInfo(response.data.statusInfo);
setStatsCd(commonInfo.statsCd);
applyEnterableFromStatus(response.data.statusInfo, checkEnterable, setIsEnterable);
if (updateLastSearchedValues) setLastSearchedValues(values);
return true;
}
alert(response.message || '데이터 조회 중 오류가 발생했습니다.');
return false;
}
if (tabKey === 'ST00020005') {
const response = await statisticsInputSewPipeApiService.getManholeStatus(searchParam);
if (response.status === 'success') {
const commonInfo = response.data.commonInfo;
const convertedData = (response.data.manholeStatusList ?? []).map((d) => ({
ctpvNm: commonInfo.ctpvNm,
...d,
}));
applyRowMap((m) => m.set('ST00020005', toGridRows(convertedData)));
setStatusInfo(response.data.statusInfo);
setStatsCd(commonInfo.statsCd);
applyEnterableFromStatus(response.data.statusInfo, checkEnterable, setIsEnterable);
if (updateLastSearchedValues) setLastSearchedValues(values);
return true;
}
alert(response.message || '데이터 조회 중 오류가 발생했습니다.');
return false;
}
if (tabKey === 'ST00020006') {
const response = await statisticsInputSewPipeApiService.getConnectionStatus(searchParam);
if (response.status === 'success') {
const commonInfo = response.data.commonInfo;
const facilityConvertedData = (response.data.facilityConnectionList ?? []).map((d) => ({
...d,
}));
const adminConvertedData = (response.data.adminAreaConnectionList ?? []).map((d) => ({
...d,
ctpvNm: d.ctpvNm || commonInfo.ctpvNm,
}));
applyRowMap((m) => {
m.set('treatmentPlantConnectionInp', toGridRows(facilityConvertedData));
m.set('adminAreaConnectionInp', toGridRows(adminConvertedData));
});
setStatusInfo(response.data.statusInfo);
setStatsCd(commonInfo.statsCd);
applyEnterableFromStatus(response.data.statusInfo, checkEnterable, setIsEnterable);
if (updateLastSearchedValues) setLastSearchedValues(values);
return true;
}
alert(response.message || '데이터 조회 중 오류가 발생했습니다.');
return false;
}
if (!mergeIntoExistingMap) {
setRowDataMap(new Map());
}
if (updateLastSearchedValues) setLastSearchedValues(values);
applyEnterableFromStatus(null, checkEnterable, setIsEnterable);
return true;
};
// setRowDataMap(new Map());
const handleSearch = async (values: SearchFormValues) => {
if (!values.sido || values.sido === 'ALL') {
alert('시도를 선택해주세요.');
return;
}
// try {
// setIsLoading(true);
try {
const periodResponse = await statisticsInputSewSupplyRateApi.checkInputPeriod(values.year || '');
// // const searchParam: SewagePipeReviewRequestParams = {
// // statsYr: values.year,
// // sewerBzmnCd: values.bsnmCd,
// // ctpvNm: values.sido,
// // sggNm: values.gugun === 'ALL' ? '' : values.gugun
// // };
if (periodResponse.status !== 'success') {
alert(periodResponse.message);
return;
}
// // searchParamRef.current = searchParam;
// // const response = await statisticsManagementApi.getSewagePipeReviewRegionalList(searchParam);
const checkEnterable = periodResponse.data;
periodEnterableRef.current = checkEnterable;
// // if (response?.status === 'success' && response.data) {
// // const newRowDataMap = new Map<string, Record<string, string | number>[]>();
// // newRowDataMap.set('ST00020001', toGridRows(response.data as object[]));
// // setRowDataMap(newRowDataMap);
// // } else {
// // alert(response?.message || "데이터가 없습니다.");
// // }
// } catch {
// alert('데이터 조회 중 오류가 발생했습니다.');
// } finally {
// setIsLoading(false);
// }
// };
setRowDataMap(new Map());
lazyTabLoggedRef.current.clear();
setIsLoading(true);
const searchParam = buildSewagePipeSearchParam(values);
searchParamRef.current = searchParam;
searchDoneRef.current = true;
setHasSearched(true);
setLastSearchedYear(values.year ?? null);
await loadSewagePipeActiveTabData(activeTab, searchParam, values, checkEnterable, false, true);
} catch {
alert('데이터 조회 중 오류가 발생했습니다.');
} finally {
setIsLoading(false);
}
};
const handleActiveTabChange = async (tabKey: string) => {
setActiveTab(tabKey);
const activeTabData = rowDataMap.get(tabKey);
const hasActiveTabData = Array.isArray(activeTabData) && activeTabData.length > 0;
const canLazyLoad =
searchParamRef.current &&
!hasActiveTabData &&
!lazyTabLoggedRef.current.has(tabKey) &&
searchDoneRef.current;
if (!canLazyLoad) return;
const searchParam = searchParamRef.current;
if (!searchParam) return;
try {
setIsLoading(true);
let response;
if (tabKey === 'ST00020001') {
response = await statisticsInputSewPipeApiService.getRegionalFactStatus(searchParam);
} else if (tabKey === 'ST00020002') {
response = await statisticsInputSewPipeApiService.getFactInstallStatus(searchParam);
} else if (tabKey === 'ST00020003') {
response = await statisticsInputSewPipeApiService.getPipeTypeStatus(searchParam);
} else if (tabKey === 'ST00020004') {
response = await statisticsInputSewPipeApiService.getPipeDiameterStatus(searchParam);
} else if (tabKey === 'ST00020005') {
response = await statisticsInputSewPipeApiService.getManholeStatus(searchParam);
} else if (tabKey === 'ST00020006') {
response = await statisticsInputSewPipeApiService.getConnectionStatus(searchParam);
}
lazyTabLoggedRef.current.add(tabKey);
if (response?.status === 'success') {
setStatusInfo(response.data.statusInfo);
setStatsCd(response.data.commonInfo.statsCd);
applyEnterableFromStatus(response.data.statusInfo, periodEnterableRef.current, setIsEnterable);
setRowDataMap(prev => {
const next = new Map(prev);
if (tabKey === 'ST00020001') {
const commonInfo = response.data.commonInfo;
const convertedData = (response.data.regionalFactStatusList ?? []).map((d: SeweragePipeInputRegionalFactStatusItem) => ({
ctpvNm: commonInfo.ctpvNm,
...d
}));
next.set(tabKey, toGridRows(convertedData));
} else if (tabKey === 'ST00020002') {
const commonInfo = response.data.commonInfo;
const convertedData = (response.data.factInstallStatusList ?? []).map((d: SeweragePipeInputFactInstallStatusItem) => ({
ctpvNm: commonInfo.ctpvNm,
...d
}));
next.set(tabKey, toGridRows(convertedData));
} else if (tabKey === 'ST00020003') {
const commonInfo = response.data.commonInfo;
const convertedData = (response.data.pipeTypeStatusList ?? []).map((d: SeweragePipeInputPipeTypeStatusItem) => ({
ctpvNm: commonInfo.ctpvNm,
...d
}));
next.set(tabKey, toGridRows(convertedData));
} else if (tabKey === 'ST00020004') {
const commonInfo = response.data.commonInfo;
const convertedData = (response.data.pipeDiameterStatusList ?? []).map((d: SeweragePipeInputPipeDiameterStatusItem) => ({
ctpvNm: commonInfo.ctpvNm,
...d
}));
next.set(tabKey, toGridRows(convertedData));
} else if (tabKey === 'ST00020005') {
const commonInfo = response.data.commonInfo;
const convertedData = (response.data.manholeStatusList ?? []).map((d: SeweragePipeInputManholeStatusItem) => ({
ctpvNm: commonInfo.ctpvNm,
...d
}));
next.set(tabKey, toGridRows(convertedData));
} else if (tabKey === 'ST00020006') {
const commonInfo = response.data.commonInfo;
const facilityConvertedData = (response.data.facilityConnectionList ?? []).map((d: SeweragePipeInputFacilityConnectionItem) => ({
...d
}));
const adminConvertedData = (response.data.adminAreaConnectionList ?? []).map((d: SeweragePipeInputAdminAreaConnectionItem) => ({
...d,
ctpvNm: d.ctpvNm || commonInfo.ctpvNm,
}));
next.set('treatmentPlantConnectionInp', toGridRows(facilityConvertedData));
next.set('adminAreaConnectionInp', toGridRows(adminConvertedData));
}
return next;
});
} else {
alert(response?.message || '데이터 조회 중 오류가 발생했습니다.');
}
} catch {
alert('데이터 조회 중 오류가 발생했습니다.');
} finally {
setIsLoading(false);
}
};
const SubmitExceptionFunc = () => {
console.log('submit exception');
// 예외 제출 로직
};
const getLastYearDataFunc = () => {
console.log('get last year data');
// 지난연도 데이터 가져오기 로직
const getLastYearDataFunc = async () => {
const values = lastSearchedValues ?? searchValues;
if (!values?.sido || values.sido === 'ALL') {
alert('시도를 선택해주세요.');
return;
}
if (!lastSearchedYear) {
alert('먼저 데이터를 조회해주세요.');
return;
}
try {
setIsLoading(true);
const param = buildSewagePipeSearchParam(values, { mode: 'PRE' });
await loadSewagePipeActiveTabData(activeTab, param, values, periodEnterableRef.current, true, false);
} catch {
alert('데이터 조회 중 오류가 발생했습니다.');
} finally {
setIsLoading(false);
}
};
const submitFunc = () => {
@@ -98,29 +442,146 @@ const SewagePipeInp = () => {
// 제출 로직
};
const saveFunc = () => {
console.log('save');
// 저장 로직
const saveFunc = async () => {
if (activeTab !== "ST00020001") {
return;
}
try {
const currentData = getGridDataRef.current?.();
if (!currentData || currentData.length === 0) {
alert("저장할 데이터가 없습니다.");
return;
}
const values = lastSearchedValues ?? searchValues;
if (!values?.year) {
alert("검색 후 저장해 주세요.");
return;
}
const columnFormatMap = new Map<string, string>();
RegionalFactStatusInpColumn.columns.forEach((col) => {
if (col.fieldName && col.dataFormat) {
columnFormatMap.set(col.fieldName, col.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;
const decimalPlaces = format.match(/\.(\d+)/)?.[1]?.length ?? 0;
return decimalPlaces > 0 ? parseFloat(num.toFixed(decimalPlaces)) : Math.round(num);
};
const userInfo = await getUserInfo();
const saveList = currentData.map((d) => ({
admdstCd: String(d.admdstCd ?? ""),
sggNm: String(d.sggNm ?? ""),
admdstNm: String(d.admdstNm ?? ""),
goalYr: String(d.goalYr ?? ""),
wholPplMtncPlanPrlg: formatNumber(d.wholPplMtncPlanPrlg, "wholPplMtncPlanPrlg"),
wholPlanPrlg: formatNumber(d.wholPlanPrlg, "wholPlanPrlg"),
wholFcltPrlg: formatNumber(d.wholFcltPrlg, "wholFcltPrlg"),
swagePplPsplyRt: formatNumber(d.swagePplPsplyRt, "swagePplPsplyRt"),
cbsysPplMtncPlanArea: formatNumber(d.cbsysPplMtncPlanArea, "cbsysPplMtncPlanArea"),
cbsysPplMtncPlanPrlg: formatNumber(d.cbsysPplMtncPlanPrlg, "cbsysPplMtncPlanPrlg"),
cbsysPlanPrlg: formatNumber(d.cbsysPlanPrlg, "cbsysPlanPrlg"),
cbsysFcltPrlg: formatNumber(d.cbsysFcltPrlg, "cbsysFcltPrlg"),
cbsysSqrePrlg: formatNumber(d.cbsysSqrePrlg, "cbsysSqrePrlg"),
cbsysCiclPrlg: formatNumber(d.cbsysCiclPrlg, "cbsysCiclPrlg"),
cbsysOpnPrlg: formatNumber(d.cbsysOpnPrlg, "cbsysOpnPrlg"),
cbsysGttrPrlg: formatNumber(d.cbsysGttrPrlg, "cbsysGttrPrlg"),
drtwPplMtncPlanArea: formatNumber(d.drtwPplMtncPlanArea, "drtwPplMtncPlanArea"),
drtwPplMtncPlanPrlg: formatNumber(d.drtwPplMtncPlanPrlg, "drtwPplMtncPlanPrlg"),
drtwPplPlanPrlg: formatNumber(d.drtwPplPlanPrlg, "drtwPplPlanPrlg"),
drtwPplFcltPrlg: formatNumber(d.drtwPplFcltPrlg, "drtwPplFcltPrlg"),
drtwPplSqrePrlg: formatNumber(d.drtwPplSqrePrlg, "drtwPplSqrePrlg"),
drtwPplCiclPrlg: formatNumber(d.drtwPplCiclPrlg, "drtwPplCiclPrlg"),
exlnPplMtncPlanArea: formatNumber(d.exlnPplMtncPlanArea, "exlnPplMtncPlanArea"),
exlnPplMtncPlanPrlg: formatNumber(d.exlnPplMtncPlanPrlg, "exlnPplMtncPlanPrlg"),
exlnPplPlanPrlg: formatNumber(d.exlnPplPlanPrlg, "exlnPplPlanPrlg"),
exlnPplFcltPrlg: formatNumber(d.exlnPplFcltPrlg, "exlnPplFcltPrlg"),
exlnPplSqrePrlg: formatNumber(d.exlnPplSqrePrlg, "exlnPplSqrePrlg"),
exlnPplCiclPrlg: formatNumber(d.exlnPplCiclPrlg, "exlnPplCiclPrlg"),
exlnPplOpnPrlg: formatNumber(d.exlnPplOpnPrlg, "exlnPplOpnPrlg"),
exlnPplGttrPrlg: formatNumber(d.exlnPplGttrPrlg, "exlnPplGttrPrlg"),
cbsysMnhlCnt: formatNumber(d.cbsysMnhlCnt, "cbsysMnhlCnt"),
sprsysDrtwMnhlCnt: formatNumber(d.sprsysDrtwMnhlCnt, "sprsysDrtwMnhlCnt"),
sprsysExlnMnhlCnt: formatNumber(d.sprsysExlnMnhlCnt, "sprsysExlnMnhlCnt"),
drtwRcvprpCnt: formatNumber(d.drtwRcvprpCnt, "drtwRcvprpCnt"),
exlnRcvprpCnt: formatNumber(d.exlnRcvprpCnt, "exlnRcvprpCnt"),
stofcbrCnt: formatNumber(d.stofcbrCnt, "stofcbrCnt"),
exlnOtflCnt: formatNumber(d.exlnOtflCnt, "exlnOtflCnt"),
sewerLdgrEdpPrlg: formatNumber(d.sewerLdgrEdpPrlg, "sewerLdgrEdpPrlg"),
sewerLdgrEdpRt: formatNumber(d.sewerLdgrEdpRt, "sewerLdgrEdpRt"),
rmrk: (d.rmrk as string | null) ?? null,
}));
const param: SeweragePipeInputRegionalFactStatusSaveRequest = {
statsYr: values.year ?? "",
ctpvNm: values.sido ?? "",
sggNm: values.gugun === "ALL" ? "" : (values.gugun ?? ""),
sewerBzmnCd: values.bsnmCd ?? "",
userId: userInfo.userUnqId,
superAdmin: isAdminInfo ? "superAdmin" : "",
saveList,
};
console.log(param);
} catch {
alert("저장 준비 중 오류가 발생했습니다.");
}
};
const gugunOkOrNotDoList = doList.includes(lastSearchedValues?.bsnmCd || '')
? Boolean(lastSearchedValues?.gugun && lastSearchedValues.gugun !== 'ALL')
: true;
const showSaveInToolbar = (isEnterable || isAdminInfo) && gugunOkOrNotDoList;
const showEnterableToolbarButtons = isEnterable && gugunOkOrNotDoList;
/** TabDataGrid는 rowDataMap 사용 — 행이 하나라도 있을 때만 툴바 카드 의미 있음 */
const hasRowData = Array.from(rowDataMap.values()).some((rows) => rows.length > 0);
const showActionToolbarCard =
(hasRowData || hasSearched) &&
(showSaveInToolbar ||
(statusInfo?.laeSbmsnSttsCd === '2' && (isAdminInfo || (isEnterable && gugunOkOrNotDoList))));
return (
<>
<SearchForm />
<div className="card !py-0">
<div className="flex justify-end items-center gap-[0.6rem] mx-[0.6rem]">
<SubmitExceptionButton SubmitExceptionFunc={SubmitExceptionFunc} />
<LastYearDataButton getLastYearDataFunc={getLastYearDataFunc} />
<SubmitButton submitFunc={submitFunc} />
<SaveButton saveFunc={saveFunc} />
<SearchForm onSubmit={handleSearch} onReset={handleReset} onValuesChange={setSearchValues} />
{showActionToolbarCard && (
<div className="card !py-0">
<div className="flex justify-end items-center gap-[0.6rem] mx-[0.6rem]">
{showEnterableToolbarButtons && (
<>
<SubmitExceptionButton SubmitExceptionFunc={SubmitExceptionFunc} />
<LastYearDataButton getLastYearDataFunc={() => void getLastYearDataFunc()} />
<SubmitButton submitFunc={submitFunc} />
</>
)}
{showSaveInToolbar && <SaveButton saveFunc={saveFunc} />}
</div>
</div>
</div>
)}
{isLoading && <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}
rowDataMap={rowDataMap}
setRowDataMap={setRowDataMap}
activeTab={activeTab}
onActiveTabChange={setActiveTab}
onActiveTabChange={handleActiveTabChange}
isExcelImport={hasRowData && isEnterable && gugunOkOrNotDoList}
facilitySearchStatsYr={lastSearchedYear}
getGridDataRef={getGridDataRef}
/>
</>
);
@@ -30,7 +30,7 @@ import type {
SewerageSupplyRateSaveRequest,
SewerageSupplyRateStatusInfo,
} from "@/entities/statistics/model/input/statistics-sewage-supply-rate-input-interface";
import type { cancelSubmitRequest } from "@/entities/statistics/model/statistics-interface";
import type { cancelSubmitRequest, SubmitExceptionRequest } from "@/entities/statistics/model/statistics-interface";
import type { StatisticsInputSubmitInfoFormData } from "@/entities/statistics/model/statistics-input-submit-info-schema";
import { SeweragePenetrationRateInpColumn } from "@/components/util/column/input/SeweragePenetrationRateInpColumn";
import { doList } from "@/components/util/column/input/DoList";
@@ -69,6 +69,8 @@ const SeweragePenetrationRateInp = () => {
const [lastSearchedValues, setLastSearchedValues] = useState<SearchFormValues | null>(null);
const [statusInfo, setStatusInfo] = useState<SewerageSupplyRateStatusInfo | null>(null);
const [showDailog, setShowDailog] = useState<boolean>(false);
/** 예외제출 버튼으로 연 다이얼로그인지 (확인 시 executeSubmit 대신 SubmitExceptionFunc 경로) */
const [isExceptionSubmitFlow, setIsExceptionSubmitFlow] = useState(false);
const [statsCd, setStatsCd] = useState<string>('');
const [footerFromApi, setFooterFromApi] = useState<{ sewerBzmnWholPpltnCnt?: number; sewerBzmnDomplWholPpltnCnt?: number; sewerBzmnFrgnrWholPpltnCnt?: number; sewerBzmnArea?: number } | null>(null);
const { confirm } = useConfirm();
@@ -190,8 +192,39 @@ const SeweragePenetrationRateInp = () => {
}
};
const SubmitExceptionFunc = () => {
const SubmitExceptionFunc = async (submitInfo: StatisticsInputSubmitInfoFormData) => {
const userInfo = await getUserInfo();
const params: SubmitExceptionRequest = {
...submitInfo,
sewerBzmnCd: searchValues?.bsnmCd ?? '',
ctpvNm: searchValues?.sido ?? '',
sggNm: searchValues?.gugun ?? '',
statsYr: searchValues?.year ?? '',
userId: userInfo.userUnqId,
statsCd: statsCd,
};
// if (isAdminInfo) {
// params.superAdmin = 'superAdmin';
// }
try {
setIsLoading(true);
const response = await statisticsAPi.submitException(params);
if (response.status === 'success') {
await fetchSewerageSupplyRate(getSewerageSupplyRateParams(searchValues ?? {}), false);
alert('제출되었습니다.');
} else {
alert(response.message || '데이터 제출 중 오류가 발생했습니다.');
}
} catch {
alert('데이터 제출 중 오류가 발생했습니다.');
} finally {
setIsLoading(false);
}
};
const getLastYearDataFunc = async (values: SearchFormValues) => {
@@ -209,21 +242,33 @@ const SeweragePenetrationRateInp = () => {
await fetchSewerageSupplyRate(getSewerageSupplyRateParams(values, { mode: 'PRE' }), true);
};
const submitFunc = async () => {
const isError = rowData.filter((d) =>
hasVerificationErrors((d as GridRowWithVerification).verificationResult)
);
const submitFunc = async (isException: boolean) => {
if (!isException) {
const isError = rowData.filter((d) =>
hasVerificationErrors((d as GridRowWithVerification).verificationResult)
);
if (isError.length > 0) {
alert(`데이터에 오류가 있습니다.
수정 후 제출해주세요.`);
return;
}
if (await confirm('제출 하시겠습니까?')) {
setShowDailog(true);
if (isError.length > 0) {
alert(`데이터에 오류가 있습니다.
수정 후 제출해주세요.`);
return;
}
if (await confirm('제출 하시겠습니까?')) {
setIsExceptionSubmitFlow(false);
setShowDailog(true);
}
} else {
if (await confirm('예외 제출 시 검증 결과에 오류가 있어도 제출 처리됩니다.')) {
setIsExceptionSubmitFlow(true);
setShowDailog(true);
}
}
};
const executeExceptionSubmit = async (submitInfo: StatisticsInputSubmitInfoFormData) => {
await SubmitExceptionFunc(submitInfo);
};
const executeSubmit = async (submitInfo: StatisticsInputSubmitInfoFormData) => {
try {
setIsLoading(true);
@@ -457,7 +502,7 @@ const SeweragePenetrationRateInp = () => {
if (await confirm('제출된 내용을 취소하시겠습니까?')) {
const userInfo = await getUserInfo();
const requestData: cancelSubmitRequest = {
sewerBzmnCd: searchValues?.bsnmCd ?? '',
ctpvNm: searchValues?.sido ?? '',
@@ -503,11 +548,11 @@ const SeweragePenetrationRateInp = () => {
{showEnterableToolbarButtons && (
<>
{isAdminInfo && statusInfo && statusInfo.laeSbmsnSttsCd !== '0' && (
<SubmitExceptionButton SubmitExceptionFunc={SubmitExceptionFunc} />
<SubmitExceptionButton SubmitExceptionFunc={() => submitFunc(true)} />
)}
<LastYearDataButton getLastYearDataFunc={() => getLastYearDataFunc(searchValues ?? {})} />
<SubmitButton submitFunc={submitFunc} />
<SubmitButton submitFunc={() => submitFunc(false)} />
</>
)}
{statusInfo?.laeSbmsnSttsCd === '2' && <CancelSubmitButton cnacelSubmitFunc={cnacelSubmitFunc} />}
@@ -557,7 +602,18 @@ const SeweragePenetrationRateInp = () => {
/>
</div>
<ConfirmDialog isOpen={false} />
<SubmitDailog open={showDailog} onOpenChange={setShowDailog} statusInfo={statusInfo} onConfirm={executeSubmit} isLoading={isLoading} />
<SubmitDailog
open={showDailog}
onOpenChange={(open) => {
setShowDailog(open);
if (!open) setIsExceptionSubmitFlow(false);
}}
statusInfo={statusInfo}
onConfirm={executeSubmit}
onExceptionConfirm={executeExceptionSubmit}
submitMode={isExceptionSubmitFlow ? 'exception' : 'normal'}
isLoading={isLoading}
/>
</>
);
};
@@ -47,7 +47,10 @@ interface SubmitDialogProps {
statusInfo: SewerageSupplyRateStatusInfo | null;
/** 다이얼로그에서 저장 클릭 시 호출. 제출 정보를 넘기면 부모에서 requestData에 포함해 제출 API 호출 */
onConfirm?: (data: StatisticsInputSubmitInfoFormData) => void | Promise<void>;
onExceptionConfirm?: (data: any) => void | Promise<void>;
/** 예외 제출로 다이얼로그를 연 경우: 일반 제출(onConfirm) 대신 호출 */
onExceptionConfirm?: (data: StatisticsInputSubmitInfoFormData) => void | Promise<void>;
/** 'normal': onConfirm / 'exception': onExceptionConfirm */
submitMode?: "normal" | "exception";
isLoading?: boolean;
}
@@ -75,7 +78,15 @@ function statusInfoToFormData(info: SewerageSupplyRateStatusInfo): StatisticsInp
};
};
const SubmitDailog = ({ open, onOpenChange, statusInfo, onConfirm, onExceptionConfirm, isLoading }: SubmitDialogProps) => {
const SubmitDailog = ({
open,
onOpenChange,
statusInfo,
onConfirm,
onExceptionConfirm,
submitMode = "normal",
isLoading,
}: SubmitDialogProps) => {
const { control, handleSubmit, reset, formState: { errors } } = useForm<StatisticsInputSubmitInfoFormData>({
resolver: zodResolver(statisticsInputSubmitInfoSchema),
defaultValues,
@@ -88,7 +99,11 @@ const SubmitDailog = ({ open, onOpenChange, statusInfo, onConfirm, onExceptionCo
}, [open, statusInfo, reset]);
async function onSubmit(data: StatisticsInputSubmitInfoFormData) {
await onConfirm?.(data);
if (submitMode === "exception") {
await onExceptionConfirm?.(data);
} else {
await onConfirm?.(data);
}
onOpenChange(false);
reset(defaultValues);
}
@@ -164,7 +179,7 @@ const SubmitDailog = ({ open, onOpenChange, statusInfo, onConfirm, onExceptionCo
취소
</Button>
<Button type="submit" color="primary">
제출
{submitMode === 'normal' ? '제출' : '예외 제출'}
</Button>
</DialogFooter>
</form>
@@ -1,87 +1,78 @@
"use client";
import { useState } from 'react';
// import useSWR from 'swr';
import { useCallback, useState } from 'react';
import {
Button,
// Input,
Label,
// Select,
// SelectTrigger,
// SelectContent,
// SelectItem,
// SelectValue
LoadingWithAnimation,
}
from "@common/components";
import DataGrid from '@/components/module/DataGrid';
import { CodeSelect } from '@/entities/code';
import { CodeSelectWithAll } from '@/entities/code';
import { statisticsAPi } from '@/entities/statistics/api/statistics-service';
import type { StatisticsLogRequest } from '@/entities/statistics/model/statistics-interface';
import { toGridRows } from '@/components/util/grid/gridFunction';
import { LogHistoryColumn } from '@/components/util/column/management/LogHistoryColumn';
// import { LwpoCodeApi } from "@/entities/code";
import { LogHistoryTablePagination } from '@/components/statistics/management/LogHistoryTablePagination';
// const getStateCdListFetcher = async () => {
// return LwpoCodeApi.getLwpoCodeList({
// sort: 'regDt,desc',
// upCd: 'STITEM',
// page: 0,
// size: 100,
// });
// }
const PAGE_SIZE = 100;
const LogHistory = () => {
// const { setMessage: alert } = useAlertStore((state) => state);
const [statsCd, setStatsCd] = useState('');
const [statsCd, setStatsCd] = useState<string>('');
const [isLoading, setIsLoading] = useState<boolean>(false);
const [rowData, setRowData] = useState<Record<string, string | number>[]>([]);
const [pageIndex, setPageIndex] = useState(0);
const [totalPages, setTotalPages] = useState(0);
// const { data: lwpoCdList } = useSWR(
// "lwpo-code-list",
// getStateCdListFetcher,
// {
// revalidateOnFocus: false,
// revalidateOnReconnect: false,
// revalidateIfStale: false,
// dedupingInterval: 2000,
// shouldRetryOnError: false,
// onSuccess: (data) => {
// if (!data.content || data.content.length == 0) {
// alert(`시도 목록을 불러오는데 실패했습니다.
// 새로고침 후 다시 시도해주세요.`);
// }
// },
// onError: () => {
// alert(`시도 목록을 불러오는데 실패했습니다.
// 새로고침 후 다시 시도해주세요.`);
// },
// }
// );
const loadLogs = useCallback(async (page1Based: number) => {
const param: StatisticsLogRequest = {
statsCd: (statsCd === '' || statsCd === 'ALL') ? '' : statsCd,
page: page1Based,
size: PAGE_SIZE,
};
try {
setIsLoading(true);
const response = await statisticsAPi.getLogList(param);
if (response.status === 'success') {
setRowData(toGridRows(response.data?.statsLogList ?? []));
setTotalPages(response.data?.pageInfo?.totalPages ?? 0);
setPageIndex(Math.max(0, page1Based - 1));
} else {
alert(response.message || '데이터 조회 중 오류가 발생했습니다.');
}
} catch {
alert('데이터 조회 중 오류가 발생했습니다.');
} finally {
setIsLoading(false);
}
}, [statsCd]);
const handleSearch = async (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
await loadLogs(1);
};
return (
<div className="relative">
<div className="card card-border">
<form>
<form onSubmit={handleSearch}>
<div className="flex items-center gap-8 p-2">
<div className="flex items-center gap-2">
<Label className="text-label">
통계항목
</Label>
{/* <Select value={lwpoCd} onValueChange={setLwpoCd}>
<SelectTrigger className="w-[22rem]">
<SelectValue placeholder="전체"></SelectValue>
</SelectTrigger>
<SelectContent>
<SelectItem value="ALL">전체</SelectItem>
{lwpoCdList?.content?.map((l) => (
<SelectItem key={l.lwpoCd} value={l.lwpoCd}>
{l.lwpoCdNm}
</SelectItem>
))}
</SelectContent>
</Select> */}
<CodeSelect
<CodeSelectWithAll
upCd="STITEM"
value={statsCd}
onValueChange={setStatsCd}
placeholder="선택"
placeholder="전체"
className="w-[16rem]"
/>
</div>
@@ -96,14 +87,23 @@ const LogHistory = () => {
</div>
</form>
</div>
<DataGrid
{isLoading && <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>}
<DataGrid
columnInfo={LogHistoryColumn}
// rowStartNum={1}
// fixedColumns={1}
isExcelImport={false}
// showHeaderCheckbox={true}
rowData={[]}
setRowData={() => {}}
rowData={rowData}
setRowData={() => { }}
/>
<LogHistoryTablePagination
pageCount={totalPages}
pageIndex={pageIndex}
onPageIndexChange={(idx) => {
void loadLogs(idx + 1);
}}
/>
</div>
);
@@ -0,0 +1,105 @@
"use client";
/**
* @common/components DataTable 내부 Pagination과 동일 마크업/클래스 (복제).
* 공용 패키지 수정 없이 LogHistory 전용으로 둠.
*/
import {
ChevronLeftIcon,
ChevronRightIcon,
DoubleArrowLeftIcon,
DoubleArrowRightIcon,
} from "@radix-ui/react-icons";
import { Button, cn } from "@common/components";
export type LogHistoryTablePaginationProps = {
/** 총 페이지 수 (TanStack table.getPageCount()와 동일) */
pageCount: number;
/** 0부터 시작하는 현재 페이지 인덱스 */
pageIndex: number;
onPageIndexChange: (pageIndex: number) => void;
};
export function LogHistoryTablePagination({
pageCount,
pageIndex: currentPage,
onPageIndexChange,
}: LogHistoryTablePaginationProps) {
const maxVisiblePages = 10;
let startPage = Math.max(0, currentPage - maxVisiblePages / 2);
const endPage = Math.min(pageCount, startPage + maxVisiblePages);
if (endPage - startPage < maxVisiblePages) {
startPage = Math.max(0, endPage - maxVisiblePages);
}
const pageNumbers = Array(endPage - startPage)
.fill(0)
.map((_, i) => startPage + i);
const canPrevious = currentPage > 0;
const canNext = pageCount > 0 && currentPage < pageCount - 1;
return (
<div className="mt-[24px] flex items-center justify-center space-x-2">
<Button
variant="ghost"
color="white"
className="hidden size-10 p-0 lg:flex"
onClick={() => onPageIndexChange(0)}
disabled={!canPrevious}
>
<span className="sr-only">첫 페이지로</span>
<DoubleArrowLeftIcon className="size-4" />
</Button>
<Button
variant="ghost"
color="white"
className="size-10 p-0"
onClick={() => onPageIndexChange(currentPage - 1)}
disabled={!canPrevious}
>
<span className="sr-only">이전 페이지로</span>
<ChevronLeftIcon className="size-4" />
</Button>
<div className="flex items-center justify-center text-sm font-medium">
{pageNumbers.map((pageNumber) => (
<Button
key={pageNumber}
variant="ghost"
className={cn(
"inline-block h-10 min-w-10 !bg-transparent p-1 text-foreground hover:!bg-accent",
currentPage === pageNumber &&
"!bg-primary !font-bold !text-primary-foreground hover:!bg-primary",
)}
onClick={() => onPageIndexChange(pageNumber)}
>
{pageNumber + 1}
</Button>
))}
</div>
<Button
variant="ghost"
color="white"
className="size-10 p-0"
onClick={() => onPageIndexChange(currentPage + 1)}
disabled={!canNext}
>
<span className="sr-only">다음 페이지로</span>
<ChevronRightIcon className="size-4" />
</Button>
<Button
variant="ghost"
color="white"
className="hidden size-10 p-0 lg:flex"
onClick={() => onPageIndexChange(Math.max(0, pageCount - 1))}
disabled={!canNext}
>
<span className="sr-only">마지막 페이지로</span>
<DoubleArrowRightIcon className="size-4" />
</Button>
</div>
);
}
@@ -54,11 +54,11 @@ const SewagePipeRvw = () => {
try {
setIsLoading(true);
const searchParam: SewagePipeReviewRequestParams = {
statsYr: values.year,
sewerBzmnCd: values.bsnmCd,
ctpvNm: values.sido,
sggNm: values.gugun === 'ALL' ? '' : values.gugun
const searchParam: SewagePipeReviewRequestParams = {
statsYr: values.year,
sewerBzmnCd: values.bsnmCd,
ctpvNm: values.sido,
sggNm: values.gugun === 'ALL' ? '' : values.gugun
};
searchParamRef.current = searchParam;
@@ -69,7 +69,7 @@ const SewagePipeRvw = () => {
newRowDataMap.set('ST00020001', toGridRows(response.data as object[]));
setRowDataMap(newRowDataMap);
} else {
alert(response?.message || "데이터가 없습니다.");
alert(response?.message || '데이터 조회 중 오류가 발생했습니다.');
}
} catch {
alert('데이터 조회 중 오류가 발생했습니다.');
@@ -96,14 +96,14 @@ const SewagePipeRvw = () => {
if (tabKey === 'ST00020002') {
response = await statisticsManagementApi.getSewagePipeFacilityList(searchParamRef.current);
}else if(tabKey === 'ST00020003'){
response = await statisticsManagementApi.getSewagePipeTypeList(searchParamRef.current);
}else if(tabKey === 'ST00020004'){
response = await statisticsManagementApi.getSewagePipeDiamList(searchParamRef.current);
} else if (tabKey === 'ST00020003') {
response = await statisticsManagementApi.getSewagePipeTypeList(searchParamRef.current);
} else if (tabKey === 'ST00020004') {
response = await statisticsManagementApi.getSewagePipeDiamList(searchParamRef.current);
}
if (response?.status === 'success' && response.data) {
setRowDataMap(prev => {
const next = new Map(prev);
next.set(tabKey, toGridRows(response.data as object[]));
@@ -1,5 +1,5 @@
import { useState, useMemo } from "react";
import { DataGrid } from "@rsone/components";
import DataGrid from "@/components/module/DataGrid";
import SearchForm from "@/components/module/SearchForm";
import SaveButton from "@/components/button/SaveButton";
import ApproveButton from "@/components/button/ApproveButton";
@@ -45,17 +45,17 @@ const StatsInputStatusRvw = () => {
setIsLoading(true);
const response = await statisticsManagementApi.getStatsInputStatusList({
sido: values.sido,
gugun: values.gugun === 'ALL' ? '' : values.gugun,
yr: values.year,
bsnmCd: values.bsnmCd
ctpvNm: values.sido,
sggNm: values.gugun === 'ALL' ? '' : values.gugun,
statsYr: values.year,
sewerBzmnCd: values.bsnmCd
});
if (response.status === 'success' && response.data) {
const rawList = response.data.sewerageSupplyRateList ?? [];
const convertedData: StatsInputStatusReviewItem[] = rawList.map((item: StatsInputStatusReviewItem) => ({
...item,
status: item.status || '미확인',
// status: item.status || '미확인',
}));
setRowData(toGridRows(convertedData));
@@ -100,9 +100,10 @@ const StatsInputStatusRvw = () => {
<DataGrid
columnInfo={columnInfo}
isExcelImport={false}
isExcelImport={false}
showHeaderCheckbox={true}
rowStartNum={2}
fixedColumns={2}
fixedColumns={4}
rowData={rowData}
setRowData={setRowData}
/>
@@ -197,6 +197,12 @@ export type ColumnInfo = {
columnLayout: ColumnLayoutItem[];
};
export type GridTopButtonConfig = {
visible?: boolean;
text: string;
onClick?: (helpers: { openDialog: (title?: string) => void; closeDialog: () => void }) => void;
};
export type ColumnInfoList = {
tabKey: string; // 탭을 식별하는 고유 키 (필수), server(api)에 전송할 변수 값
columns?: ColumnConfig[]; // 일반 그리드용 (isSplitGrid가 false일 때)
@@ -204,6 +210,7 @@ export type ColumnInfoList = {
rowStartNum?: number;
fixedColumns?: number;
tabLabel?: string; // 탭에 표시될 레이블
topButton?: GridTopButtonConfig; // 탭 상단 커스텀 버튼
isSplitGrid?: boolean; // 5:5 분리형 그리드 유무
left?: ColumnInfoList; // SplitDataGrid용 왼쪽 그리드 정보
right?: ColumnInfoList; // SplitDataGrid용 오른쪽 그리드 정보
@@ -250,6 +257,10 @@ export type DataGridProps = {
idFieldName?: string;
/** 전체 정렬 기능 on/off (기본값: false) */
enableSort?: boolean;
/** CompactDataGrid 전용: true면 래퍼 너비 w-full (다이얼로그 등 분할 열 안에서 사용) */
compactFullWidth?: boolean;
/** CompactDataGrid 전용: 그리드 영역 높이 Tailwind 클래스 */
compactGridHeightClass?: string;
};
export type TabDataGridProps = {
@@ -274,6 +285,8 @@ export type TabDataGridProps = {
};
/** 전체 정렬 기능 on/off (기본값: false) */
enableSort?: boolean;
/** 분할 탭 상단 시설 조회 다이얼로그용 — 상위 화면 검색의 통계연도(searchParam.statsYr) */
facilitySearchStatsYr?: string | null;
};
export type SplitDataGridProps = {
@@ -1,52 +1,68 @@
import { ValueType } from "realgrid";
import type { GridTopButtonConfig } from "@/components/type/DataGrid.type";
// 하수도 통계 관리 > 02.하수관로 > tab 6. 시설별 현황관리
// Tab 6-1: 행정구역별 연결 현황 (오른쪽 그리드)
export const AdminAreaConnectionInpColumn = {
columns: [
// 행정구역
{ name: 'sido', fieldName: '시도', header: { text: '시도' } },
{ name: 'sgg', fieldName: '시군구', header: { text: '시군구' } },
{ name: 'adstDong', fieldName: '행정구역명', header: { text: '행정구역명' } },
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, editable: false },
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, editable: false },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, editable: false },
// 연결여부
{ name: 'iscan', fieldName: '연결여부', header: { text: '연결여부' } },
{ name: 'isConnection', fieldName: 'isConnection', header: { text: '연결여부' }, editable: false },
// 연결하수처리장
{ name: 'bscFctNm', fieldName: '연결하수처리장', header: { text: '연결하수처리장' } },
{ name: 'connectionBscFctNmList', fieldName: 'connectionBscFctNmList', header: { text: '연결하수처리장' }, editable: false },
{
name: 'admdstCd',
fieldName: 'admdstCd',
header: { text: '행정구역코드' },
visible: false,
editable: false,
},
],
columnLayout: [
{
name: 'adminArea',
hideChildHeaders: false,
header: { text: '행정구역' },
items: ['sido', 'sgg', 'adstDong'],
items: ['ctpvNm', 'sggNm', 'admdstNm'],
},
'iscan',
'bscFctNm',
'isConnection',
'connectionBscFctNmList',
'admdstCd'
],
rowStartNum: 2,
fixedColumns: 1,
tabKey: 'adminAreaConnectionInp',
tabLabel: '행정구역별 연결 현황'
tabLabel: '행정구역별 연결 현황',
topButton: {
visible: true,
text: '시설별 설치 현황 관리',
onClick: ({ openDialog }: Parameters<NonNullable<GridTopButtonConfig["onClick"]>>[0]) => {
openDialog('시설별 설치 현황 관리');
},
},
};
// Tab 6-2: 하수처리장별 연결 현황 (왼쪽 그리드)
export const TreatPlantConnectionInpColumn = {
columns: [
// 하수처리장 정보
{ name: 'bscFctNm', fieldName: '하수처리장명', header: { text: '하수처리장명' } },
{ name: 'fctCpc', fieldName: '용량', header: { text: '용량' }, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'bscFctCd', fieldName: '하수처리장코드', header: { text: '하수처리장코드' } },
{ name: 'bscFctNm', fieldName: 'bscFctNm', header: { text: '하수처리장명' }, editable: false },
{ name: 'fctCpc', fieldName: 'fctCpc', header: { text: '용량' }, valueType: ValueType.NUMBER, dataFormat: '#,##0', editable: false },
{ name: 'bscFctCd', fieldName: 'bscFctCd', header: { text: '하수처리장코드' }, editable: false },
// 연결여부
{ name: 'iscan', fieldName: '연결여부', header: { text: '연결여부' } },
{ name: 'isConnection', fieldName: 'isConnection', header: { text: '연결여부' }, editable: false },
// 연결지역
{ name: 'nmlist', fieldName: '연결지역', header: { text: '연결지역' } },
{ name: 'connectionAreaList', fieldName: 'connectionAreaList', header: { text: '연결지역' }, editable: false },
],
columnLayout: [
'bscFctNm',
'fctCpc',
'bscFctCd',
'iscan',
'nmlist',
'isConnection',
'connectionAreaList',
],
rowStartNum: 1,
fixedColumns: 1,
@@ -55,9 +71,9 @@ export const TreatPlantConnectionInpColumn = {
};
export const ConnectionStatusInpColumn = {
tabKey: 'treatmentPlantConnectionInp',
tabKey: 'ST00020006',
tabLabel: '시설별 현황 관리',
isSplitGrid: true,
left: AdminAreaConnectionInpColumn,
right: TreatPlantConnectionInpColumn,
left: TreatPlantConnectionInpColumn,
right: AdminAreaConnectionInpColumn,
};
@@ -2,86 +2,97 @@ import { footerInt, footerDecimal2, footerDecimal3 } from "@/components/util/gri
import { ValueType } from "realgrid";
// 하수도 통계 관리 > 02.하수관로 > tab 2. 시설별 설치현황
// SeweragePipeInputFactInstallStatusItem + commonInfo.ctpvNm (그리드 행에 병합)
export const FactInstallStatusInpColumn = {
columns: [
// 행정구역
{ name: 'sido', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'sgg', fieldName: '시군구', header: { text: '시군구' }, editable: false },
// 행정구역 (ctpvNm은 commonInfo에서 주입)
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, footer: { text: '합계' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, editable: false, styleName: 'rg-cell-readonly' },
// 하수처리장 정보
{ name: 'bscFctNm', fieldName: '하수처리장명', header: { text: '하수처리장명' }, footer: { text: '합계' }, editable: false },
{ name: 'fctCpc', fieldName: '하수처리시설용량(m³/일)', header: { text: '하수처리시설용량(m³/일)' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'swtrmtFcltNm', fieldName: 'swtrmtFcltNm', header: { text: '하수처리장명' }, footer: { text: '합계' }, editable: false },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'prcsFcltCpct', fieldName: 'prcsFcltCpct', header: { text: '하수처리시설용량(m³/일)' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 처리구역면적(km²)
{ name: 'procZoneSm', fieldName: '합계', header: { text: '합계' }, footer: footerDecimal3, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' },
{ name: 'cmbnProcZoneAr', fieldName: '합류식', header: { text: '합류식' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' },
{ name: 'sprtProcZoneAr', fieldName: '분류식', header: { text: '분류식' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' },
{ name: 'prcsZoneArea', fieldName: 'prcsZoneArea', header: { text: '합계' }, footer: footerDecimal3, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' },
{ name: 'cbsysPrcsZoneArea', fieldName: 'cbsysPrcsZoneArea', header: { text: '합류식' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' },
{ name: 'sprsysPrcsZoneArea', fieldName: 'sprsysPrcsZoneArea', header: { text: '분류식' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' },
// 분류식화율
{ name: 'sprtRt', fieldName: '분류식화율', header: { text: '분류식화율' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'sprsysVartnRt', fieldName: 'sprsysVartnRt', header: { text: '분류식화율' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 배수설비(개소)
{ name: 'drngEqpSm', fieldName: '합계', header: { text: '합계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'cmbnDrngEqp', fieldName: '합류식', header: { text: '합류식' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'sprtDrngEqpSm', fieldName: '소계', header: { text: '소계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'sprtDrngEqpCnnc', fieldName: '연결', header: { text: '연결' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'sprtDrngEqpDcnnc', fieldName: '미연결', header: { text: '미연결' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'mltvEqpmCnt', fieldName: 'mltvEqpmCnt', header: { text: '합계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'cbsysMltvEqpmCnt', fieldName: 'cbsysMltvEqpmCnt', header: { text: '합류식' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'sprsysMltvEqpmCnt', fieldName: 'sprsysMltvEqpmCnt', header: { text: '소계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'sprsysMltvLnkgCnt', fieldName: 'sprsysMltvLnkgCnt', header: { text: '연결' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'sprsysMltvYetLnkgCnt', fieldName: 'sprsysMltvYetLnkgCnt', header: { text: '미연결' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 하수관로 > 총시설연장
{ name: 'piplFcltyExtn', fieldName: '총시설연장(m)', header: { text: '총시설연장(m)' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'wholFcltPrlg', fieldName: 'wholFcltPrlg', header: { text: '총시설연장(m)' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 하수관로 > 차집/오수간선(m)
{ name: 'icgMnnSm', fieldName: '합계', header: { text: '합계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'icgExtn', fieldName: '차집', header: { text: '차집' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'mnnExtn', fieldName: '오수 간선', header: { text: '오수 간선' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'wholIcptgAtlfcsPrlg', fieldName: 'wholIcptgAtlfcsPrlg', header: { text: '합계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'icptgPrlg', fieldName: 'icptgPrlg', header: { text: '차집' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'atlfcsPrlg', fieldName: 'atlfcsPrlg', header: { text: '오수 간선' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 하수관로 > 토실/토구(개소) > 합류식
{ name: 'cmbnSoilholeSite', fieldName: '우수토실', header: { text: '우수토실' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'cbsysStofcbrCnt', fieldName: 'cbsysStofcbrCnt', header: { text: '우수토실' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 하수관로 > 토실/토구(개소) > 분류식
{ name: 'sprtSoilholeSite', fieldName: '우수토실', header: { text: '우수토실' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'sprtSoilspllSite', fieldName: '우수토구', header: { text: '우수토구' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'sprsysStofcbrCnt', fieldName: 'sprsysStofcbrCnt', header: { text: '우수토실' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'sprsysExlnOtflCnt', fieldName: 'sprsysExlnOtflCnt', header: { text: '우수토구' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 하수관로 > 지선(m)
{ name: 'brnchlnSm', fieldName: '합계', header: { text: '합계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'cmbnBrnchlnExtn', fieldName: '합류식', header: { text: '합류식' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'wholBrchlnPrlg', fieldName: 'wholBrchlnPrlg', header: { text: '합계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'cbsysBrchlnPrlg', fieldName: 'cbsysBrchlnPrlg', header: { text: '합류식' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 하수관로 > 지선(m) > 분류식
{ name: 'rainwtrBrnchlnExtn', fieldName: '우수', header: { text: '우수' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'pltwtrBrnchlnExtn', fieldName: '오수', header: { text: '오수' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 하수관로 > 지선(m) > 분류식
{ name: 'exlnBrchlnPrlg', fieldName: 'exlnBrchlnPrlg', header: { text: '우수' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'drtwBrchlnPrlg', fieldName: 'drtwBrchlnPrlg', header: { text: '오수' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 비고
{ name: 'rmk', fieldName: '비고', header: { text: '비고' } },
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
// 식별·키 필드 (API SeweragePipeInputFactInstallStatusItem, 그리드 비표시 — fixedColumns와 겹치지 않도록 맨 뒤)
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'prcsFcltSn', fieldName: 'prcsFcltSn', header: { text: '처리시설일련번호' }, visible: false, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'swtrmtFcltCd', fieldName: 'swtrmtFcltCd', header: { text: '하수처리시설코드' }, visible: false, editable: false, styleName: 'rg-cell-readonly' },
],
columnLayout: [
{
name: 'adminArea',
hideChildHeaders: false,
header: { text: '행정구역' },
items: ['sido', 'sgg'],
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
'bscFctNm',
'fctCpc',
'swtrmtFcltNm',
'admdstNm',
'prcsFcltCpct',
{
name: 'procZone',
hideChildHeaders: false,
header: { text: '처리구역면적(km²)' },
items: ['procZoneSm', 'cmbnProcZoneAr', 'sprtProcZoneAr'],
items: ['prcsZoneArea', 'cbsysPrcsZoneArea', 'sprsysPrcsZoneArea'],
},
'sprtRt',
'sprsysVartnRt',
{
name: 'drainEquip',
hideChildHeaders: false,
header: { text: '배수설비(개소)' },
items: [
'drngEqpSm',
'cmbnDrngEqp',
'mltvEqpmCnt',
'cbsysMltvEqpmCnt',
{
name: 'sprtDrainEquip',
hideChildHeaders: false,
header: { text: '분류식' },
items: ['sprtDrngEqpSm', 'sprtDrngEqpCnnc', 'sprtDrngEqpDcnnc'],
items: ['sprsysMltvEqpmCnt', 'sprsysMltvLnkgCnt', 'sprsysMltvYetLnkgCnt'],
},
],
},
@@ -90,12 +101,12 @@ export const FactInstallStatusInpColumn = {
hideChildHeaders: false,
header: { text: '하수관로' },
items: [
'piplFcltyExtn',
'wholFcltPrlg',
{
name: 'interceptor',
hideChildHeaders: false,
header: { text: '차집/오수간선(m)' },
items: ['icgMnnSm', 'icgExtn', 'mnnExtn'],
items: ['wholIcptgAtlfcsPrlg', 'icptgPrlg', 'atlfcsPrlg'],
},
{
name: 'soilHole',
@@ -106,13 +117,13 @@ export const FactInstallStatusInpColumn = {
name: 'cmbnSoilHole',
hideChildHeaders: false,
header: { text: '합류식' },
items: ['cmbnSoilholeSite'],
items: ['cbsysStofcbrCnt'],
},
{
name: 'sprtSoilHole',
hideChildHeaders: false,
header: { text: '분류식' },
items: ['sprtSoilholeSite', 'sprtSoilspllSite'],
items: ['sprsysStofcbrCnt', 'sprsysExlnOtflCnt'],
},
],
},
@@ -121,22 +132,25 @@ export const FactInstallStatusInpColumn = {
hideChildHeaders: false,
header: { text: '지선(m)' },
items: [
'brnchlnSm',
'cmbnBrnchlnExtn',
'wholBrchlnPrlg',
'cbsysBrchlnPrlg',
{
name: 'sprtBranchLine',
hideChildHeaders: false,
header: { text: '분류식' },
items: ['rainwtrBrnchlnExtn', 'pltwtrBrnchlnExtn'],
items: ['exlnBrchlnPrlg', 'drtwBrchlnPrlg'],
},
],
},
],
},
'rmk',
'rmrk',
'admdstCd',
'prcsFcltSn',
'swtrmtFcltCd',
],
rowStartNum: 4,
fixedColumns: 3,
tabKey: 'factInstallStatusInp',
tabKey: 'ST00020002',
tabLabel: '시설별 설치현황'
};
};
@@ -1,331 +1,158 @@
import type { ColumnConfig, ColumnLayoutItem } from "@/components/type/DataGrid.type";
import { footerInt } from "@/components/util/grid/footerSummary";
import { ValueType } from "realgrid";
// 하수도 통계 관리 > 02.하수관로 > tab 5. 맨홀 현황
// SeweragePipeInputManholeStatusItem + commonInfo.ctpvNm (그리드 행에 병합)
// API: yy14=2014년 … yy25=2025년 (필드 접두 yy + 연도 끝자리)
const MANHOLE_YY_MIN = 14;
const MANHOLE_YY_MAX = 25;
const calendarYearFromYy = (yy: number) => 2000 + yy;
const intCol = (name: string, headerText: string): ColumnConfig => ({
name,
fieldName: name,
header: { text: headerText },
footer: footerInt,
editable: false,
valueType: ValueType.NUMBER,
dataFormat: "#,##0",
});
function totalBlockColumns(): ColumnConfig[] {
return [
intCol("totalWholMnhlCnt", "계"),
intCol("totalStep1MnhlCnt", "주철"),
intCol("totalStep2MnhlCnt", "주철(조화)"),
intCol("totalStep3MnhlCnt", "콘크리트"),
intCol("totalStep4MnhlCnt", "기타"),
intCol("totalStep5MnhlCnt", "추락방지시설설치맨홀(개)"),
];
}
function yyBlockColumns(yy: number): ColumnConfig[] {
const p = `yy${yy}`;
return [
intCol(`${p}WholMnhlCnt`, "계"),
intCol(`${p}Step1MnhlCnt`, "주철"),
intCol(`${p}Step2MnhlCnt`, "주철(조화)"),
intCol(`${p}Step3MnhlCnt`, "콘크리트"),
intCol(`${p}Step4MnhlCnt`, "기타"),
intCol(`${p}Step5MnhlCnt`, "추락방지시설설치맨홀(개)"),
];
}
function yyYearLayout(yy: number): ColumnLayoutItem {
const y = calendarYearFromYy(yy);
const p = `yy${yy}`;
return {
name: `year${y}`,
hideChildHeaders: false,
header: { text: `${y}년` },
items: [
{
name: `year${y}Material`,
hideChildHeaders: false,
header: { text: "맨홀뚜껑 재질(개)" },
items: [
`${p}WholMnhlCnt`,
`${p}Step1MnhlCnt`,
`${p}Step2MnhlCnt`,
`${p}Step3MnhlCnt`,
`${p}Step4MnhlCnt`,
],
},
`${p}Step5MnhlCnt`,
],
};
}
const yearLayoutItems: ColumnLayoutItem[] = [];
for (let yy = MANHOLE_YY_MIN; yy <= MANHOLE_YY_MAX; yy++) {
yearLayoutItems.push(yyYearLayout(yy));
}
export const ManholeStatusInpColumn = {
columns: [
// 행정구역
{ name: 'a1', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'a2', fieldName: '시군구', header: { text: '시군구' }, editable: false },
// 집중강우중점관리구역구분
{ name: 'a3', fieldName: '집중강우중점관리구역구분', header: { text: '집중강우중점관리구역구분' }, editable: false, footer: { text: '합계' } },
// 연도별총계 > 맨홀뚜껑 재질(개)
{ name: 'b1', fieldName: '계', header: { text: '계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'b2', fieldName: '주철', header: { text: '주철' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'b3', fieldName: '주철(조화)', header: { text: '주철(조화)' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'b4', fieldName: '콘크리트', header: { text: '콘크리트' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'b5', fieldName: '기타', header: { text: '기타' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 연도별총계 > 추락방지시설설치맨홀(개)
{ name: 'b6', fieldName: '추락방지시설설치맨홀(개)', header: { text: '추락방지시설설치맨홀(개)' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 2015년 이전 > 맨홀뚜껑 재질(개)
{ name: 'c1', fieldName: '계', header: { text: '계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'c2', fieldName: '주철', header: { text: '주철' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'c3', fieldName: '주철(조화)', header: { text: '주철(조화)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'c4', fieldName: '콘크리트', header: { text: '콘크리트' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'c5', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 2015년 이전 > 추락방지시설설치맨홀(개)
{ name: 'c6', fieldName: '추락방지시설설치맨홀(개)', header: { text: '추락방지시설설치맨홀(개)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 2015년 > 맨홀뚜껑 재질(개)
{ name: 'd1', fieldName: '계', header: { text: '계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'd2', fieldName: '주철', header: { text: '주철' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'd3', fieldName: '주철(조화)', header: { text: '주철(조화)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'd4', fieldName: '콘크리트', header: { text: '콘크리트' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'd5', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 2015년 > 추락방지시설설치맨홀(개)
{ name: 'd6', fieldName: '추락방지시설설치맨홀(개)', header: { text: '추락방지시설설치맨홀(개)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 2016년 > 맨홀뚜껑 재질(개)
{ name: 'e1', fieldName: '계', header: { text: '계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'e2', fieldName: '주철', header: { text: '주철' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'e3', fieldName: '주철(조화)', header: { text: '주철(조화)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'e4', fieldName: '콘크리트', header: { text: '콘크리트' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'e5', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 2016년 > 추락방지시설설치맨홀(개)
{ name: 'e6', fieldName: '추락방지시설설치맨홀(개)', header: { text: '추락방지시설설치맨홀(개)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 2017년 > 맨홀뚜껑 재질(개)
{ name: 'f1', fieldName: '계', header: { text: '계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'f2', fieldName: '주철', header: { text: '주철' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'f3', fieldName: '주철(조화)', header: { text: '주철(조화)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'f4', fieldName: '콘크리트', header: { text: '콘크리트' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'f5', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 2017년 > 추락방지시설설치맨홀(개)
{ name: 'f6', fieldName: '추락방지시설설치맨홀(개)', header: { text: '추락방지시설설치맨홀(개)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 2018년 > 맨홀뚜껑 재질(개)
{ name: 'g1', fieldName: '계', header: { text: '계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'g2', fieldName: '주철', header: { text: '주철' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'g3', fieldName: '주철(조화)', header: { text: '주철(조화)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'g4', fieldName: '콘크리트', header: { text: '콘크리트' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'g5', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 2018년 > 추락방지시설설치맨홀(개)
{ name: 'g6', fieldName: '추락방지시설설치맨홀(개)', header: { text: '추락방지시설설치맨홀(개)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 2019년 > 맨홀뚜껑 재질(개)
{ name: 'h1', fieldName: '계', header: { text: '계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'h2', fieldName: '주철', header: { text: '주철' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'h3', fieldName: '주철(조화)', header: { text: '주철(조화)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'h4', fieldName: '콘크리트', header: { text: '콘크리트' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'h5', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 2019년 > 추락방지시설설치맨홀(개)
{ name: 'h6', fieldName: '추락방지시설설치맨홀(개)', header: { text: '추락방지시설설치맨홀(개)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 2020년 > 맨홀뚜껑 재질(개)
{ name: 'i1', fieldName: '계', header: { text: '계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'i2', fieldName: '주철', header: { text: '주철' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'i3', fieldName: '주철(조화)', header: { text: '주철(조화)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'i4', fieldName: '콘크리트', header: { text: '콘크리트' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'i5', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 2020년 > 추락방지시설설치맨홀(개)
{ name: 'i6', fieldName: '추락방지시설설치맨홀(개)', header: { text: '추락방지시설설치맨홀(개)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 2021년 > 맨홀뚜껑 재질(개)
{ name: 'j1', fieldName: '계', header: { text: '계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'j2', fieldName: '주철', header: { text: '주철' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'j3', fieldName: '주철(조화)', header: { text: '주철(조화)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'j4', fieldName: '콘크리트', header: { text: '콘크리트' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'j5', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 2021년 > 추락방지시설설치맨홀(개)
{ name: 'j6', fieldName: '추락방지시설설치맨홀(개)', header: { text: '추락방지시설설치맨홀(개)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 2022년 > 맨홀뚜껑 재질(개)
{ name: 'k1', fieldName: '계', header: { text: '계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'k2', fieldName: '주철', header: { text: '주철' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'k3', fieldName: '주철(조화)', header: { text: '주철(조화)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'k4', fieldName: '콘크리트', header: { text: '콘크리트' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'k5', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 2022년 > 추락방지시설설치맨홀(개)
{ name: 'k6', fieldName: '추락방지시설설치맨홀(개)', header: { text: '추락방지시설설치맨홀(개)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 2023년 > 맨홀뚜껑 재질(개)
{ name: 'l1', fieldName: '계', header: { text: '계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'l2', fieldName: '주철', header: { text: '주철' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'l3', fieldName: '주철(조화)', header: { text: '주철(조화)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'l4', fieldName: '콘크리트', header: { text: '콘크리트' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'l5', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 2023년 > 추락방지시설설치맨홀(개)
{ name: 'l6', fieldName: '추락방지시설설치맨홀(개)', header: { text: '추락방지시설설치맨홀(개)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 2024년 > 맨홀뚜껑 재질(개)
{ name: 'm1', fieldName: '계', header: { text: '계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'm2', fieldName: '주철', header: { text: '주철' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'm3', fieldName: '주철(조화)', header: { text: '주철(조화)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'm4', fieldName: '콘크리트', header: { text: '콘크리트' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'm5', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 2024년 > 추락방지시설설치맨홀(개)
{ name: 'm6', fieldName: '추락방지시설설치맨홀(개)', header: { text: '추락방지시설설치맨홀(개)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 2025년 > 맨홀뚜껑 재질(개)
{ name: 'n1', fieldName: '계', header: { text: '계' }, footer: footerInt, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'n2', fieldName: '주철', header: { text: '주철' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'n3', fieldName: '주철(조화)', header: { text: '주철(조화)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'n4', fieldName: '콘크리트', header: { text: '콘크리트' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'n5', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 2025년 > 추락방지시설설치맨홀(개)
{ name: 'n6', fieldName: '추락방지시설설치맨홀(개)', header: { text: '추락방지시설설치맨홀(개)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 비고
{ name: 'z1', fieldName: '비고', header: { text: '비고' } },
],
{
name: "ctpvNm",
fieldName: "ctpvNm",
header: { text: "시도" },
footer: { text: "합계" },
editable: false,
styleName: "rg-cell-readonly",
},
{
name: "sggNm",
fieldName: "sggNm",
header: { text: "시군구" },
editable: false,
styleName: "rg-cell-readonly",
},
{
name: "admdstNm",
fieldName: "admdstNm",
header: { text: "행정구역명" },
editable: false,
styleName: "rg-cell-readonly",
},
{
name: "emphsMngSeNm",
fieldName: "emphsMngSeNm",
header: { text: "집중강우중점관리구역구분" },
footer: { text: "합계" },
editable: false,
},
...totalBlockColumns(),
...Array.from({ length: MANHOLE_YY_MAX - MANHOLE_YY_MIN + 1 }, (_, i) =>
yyBlockColumns(MANHOLE_YY_MIN + i)
).flat(),
{ name: "rmrk", fieldName: "rmrk", header: { text: "비고" } },
{
name: "admdstCd",
fieldName: "admdstCd",
header: { text: "행정구역코드" },
visible: false,
editable: false,
styleName: "rg-cell-readonly",
},
] as ColumnConfig[],
columnLayout: [
{
name: 'adminArea',
name: "adminArea",
hideChildHeaders: false,
header: { text: '행정구역' },
items: ['a1', 'a2'],
header: { text: "행정구역" },
items: [{ column: "ctpvNm", footerUserSpans: [{ colspan: 2 }] }, "sggNm"],
},
'a3',
"admdstNm",
"emphsMngSeNm",
{
name: 'yearlyTotal',
name: "yearlyTotal",
hideChildHeaders: false,
header: { text: '연도별총계' },
header: { text: "연도별총계" },
items: [
{
name: 'yearlyTotalMaterial',
name: "yearlyTotalMaterial",
hideChildHeaders: false,
header: { text: '맨홀뚜껑 재질(개)' },
items: ['b1', 'b2', 'b3', 'b4', 'b5'],
header: { text: "맨홀뚜껑 재질(개)" },
items: [
"totalWholMnhlCnt",
"totalStep1MnhlCnt",
"totalStep2MnhlCnt",
"totalStep3MnhlCnt",
"totalStep4MnhlCnt",
],
},
'b6',
"totalStep5MnhlCnt",
],
},
{
name: 'before2015',
hideChildHeaders: false,
header: { text: '2015년 이전' },
items: [
{
name: 'before2015Material',
hideChildHeaders: false,
header: { text: '맨홀뚜껑 재질(개)' },
items: ['c1', 'c2', 'c3', 'c4', 'c5'],
},
'c6',
],
},
{
name: 'year2015',
hideChildHeaders: false,
header: { text: '2015년' },
items: [
{
name: 'year2015Material',
hideChildHeaders: false,
header: { text: '맨홀뚜껑 재질(개)' },
items: ['d1', 'd2', 'd3', 'd4', 'd5'],
},
'd6',
],
},
{
name: 'year2016',
hideChildHeaders: false,
header: { text: '2016년' },
items: [
{
name: 'year2016Material',
hideChildHeaders: false,
header: { text: '맨홀뚜껑 재질(개)' },
items: ['e1', 'e2', 'e3', 'e4', 'e5'],
},
'e6',
],
},
{
name: 'year2017',
hideChildHeaders: false,
header: { text: '2017년' },
items: [
{
name: 'year2017Material',
hideChildHeaders: false,
header: { text: '맨홀뚜껑 재질(개)' },
items: ['f1', 'f2', 'f3', 'f4', 'f5'],
},
'f6',
],
},
{
name: 'year2018',
hideChildHeaders: false,
header: { text: '2018년' },
items: [
{
name: 'year2018Material',
hideChildHeaders: false,
header: { text: '맨홀뚜껑 재질(개)' },
items: ['g1', 'g2', 'g3', 'g4', 'g5'],
},
'g6',
],
},
{
name: 'year2019',
hideChildHeaders: false,
header: { text: '2019년' },
items: [
{
name: 'year2019Material',
hideChildHeaders: false,
header: { text: '맨홀뚜껑 재질(개)' },
items: ['h1', 'h2', 'h3', 'h4', 'h5'],
},
'h6',
],
},
{
name: 'year2020',
hideChildHeaders: false,
header: { text: '2020년' },
items: [
{
name: 'year2020Material',
hideChildHeaders: false,
header: { text: '맨홀뚜껑 재질(개)' },
items: ['i1', 'i2', 'i3', 'i4', 'i5'],
},
'i6',
],
},
{
name: 'year2021',
hideChildHeaders: false,
header: { text: '2021년' },
items: [
{
name: 'year2021Material',
hideChildHeaders: false,
header: { text: '맨홀뚜껑 재질(개)' },
items: ['j1', 'j2', 'j3', 'j4', 'j5'],
},
'j6',
],
},
{
name: 'year2022',
hideChildHeaders: false,
header: { text: '2022년' },
items: [
{
name: 'year2022Material',
hideChildHeaders: false,
header: { text: '맨홀뚜껑 재질(개)' },
items: ['k1', 'k2', 'k3', 'k4', 'k5'],
},
'k6',
],
},
{
name: 'year2023',
hideChildHeaders: false,
header: { text: '2023년' },
items: [
{
name: 'year2023Material',
hideChildHeaders: false,
header: { text: '맨홀뚜껑 재질(개)' },
items: ['l1', 'l2', 'l3', 'l4', 'l5'],
},
'l6',
],
},
{
name: 'year2024',
hideChildHeaders: false,
header: { text: '2024년' },
items: [
{
name: 'year2024Material',
hideChildHeaders: false,
header: { text: '맨홀뚜껑 재질(개)' },
items: ['m1', 'm2', 'm3', 'm4', 'm5'],
},
'm6',
],
},
{
name: 'year2025',
hideChildHeaders: false,
header: { text: '2025년' },
items: [
{
name: 'year2025Material',
hideChildHeaders: false,
header: { text: '맨홀뚜껑 재질(개)' },
items: ['n1', 'n2', 'n3', 'n4', 'n5'],
},
'n6',
],
},
'z1',
],
...yearLayoutItems,
"rmrk",
"admdstCd"
] as ColumnLayoutItem[],
rowStartNum: 3,
fixedColumns: 2,
tabKey: 'manholeStatusInp',
tabLabel: '맨홀 현황'
};
tabKey: "ST00020005",
tabLabel: "맨홀 현황",
};
@@ -2,62 +2,151 @@ import { footerDecimal2 } from "@/components/util/grid/footerSummary";
import { ValueType } from "realgrid";
// 하수도 통계 관리 > 02.하수관로 > tab 4. 관경별 현황
// SeweragePipeInputPipeDiameterStatusItem + commonInfo.ctpvNm (그리드 행에 병합)
export const PipeDiameterStatusInpColumn = {
columns: [
// 행정구역
{ name: 'sido', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'sgg', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } },
// 관종
{ name: 'swrgPipeCd', fieldName: '관종', header: { text: '관종' }, editable: false },
// 총계(m) - 계산
{ name: 'totalDiameter', fieldName: '총계(m)', header: { text: '총계(m)' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 원형(m)
{ name: 'under200', fieldName: '200mm 미만', header: { text: '200mm 미만' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'over200Undev300', fieldName: '200mm이상~300mm미만', header: { text: '200mm이상~300mm미만' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'over300Undev500', fieldName: '300mm이상~500mm미만', header: { text: '300mm이상~500mm미만' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'over500Undev500', fieldName: '500mm이상~800mm미만', header: { text: '500mm이상~800mm미만' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'over800', fieldName: '800이상mm', header: { text: '800이상mm' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 사각형(m)
{ name: 'box', fieldName: '사각형(m)', header: { text: '사각형(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 기타(측구 개거 등)
{ name: 'etc', fieldName: '기타(측구 개거 등)', header: { text: '기타(측구 개거 등)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 비고
{ name: 'rmk', fieldName: '비고', header: { text: '비고' } },
{
name: "ctpvNm",
fieldName: "ctpvNm",
header: { text: "시도" },
footer: { text: "합계" },
editable: false,
styleName: "rg-cell-readonly",
},
{
name: "sggNm",
fieldName: "sggNm",
header: { text: "시군구" },
editable: false,
styleName: "rg-cell-readonly",
},
{
name: "pipeSeNm",
fieldName: "pipeSeNm",
header: { text: "관종" },
editable: false,
},
{
name: "admdstNm",
fieldName: "admdstNm",
header: { text: "행정구역명" },
editable: false,
styleName: "rg-cell-readonly",
},
{
name: "pdmtByTsmWhol",
fieldName: "pdmtByTsmWhol",
header: { text: "총계(m)" },
footer: footerDecimal2,
editable: false,
valueType: ValueType.NUMBER,
dataFormat: "#,##0.00",
},
{
name: "step1PdmtPrlg",
fieldName: "step1PdmtPrlg",
header: { text: "200mm 미만" },
footer: footerDecimal2,
valueType: ValueType.NUMBER,
dataFormat: "#,##0.00",
},
{
name: "step2PdmtPrlg",
fieldName: "step2PdmtPrlg",
header: { text: "200mm이상~300mm미만" },
footer: footerDecimal2,
valueType: ValueType.NUMBER,
dataFormat: "#,##0.00",
},
{
name: "step3PdmtPrlg",
fieldName: "step3PdmtPrlg",
header: { text: "300mm이상~500mm미만" },
footer: footerDecimal2,
valueType: ValueType.NUMBER,
dataFormat: "#,##0.00",
},
{
name: "step4PdmtPrlg",
fieldName: "step4PdmtPrlg",
header: { text: "500mm이상~800mm미만" },
footer: footerDecimal2,
valueType: ValueType.NUMBER,
dataFormat: "#,##0.00",
},
{
name: "step5PdmtPrlg",
fieldName: "step5PdmtPrlg",
header: { text: "800mm 이상" },
footer: footerDecimal2,
valueType: ValueType.NUMBER,
dataFormat: "#,##0.00",
},
{
name: "sqrePdmtPrlg",
fieldName: "sqrePdmtPrlg",
header: { text: "사각형(m)" },
footer: footerDecimal2,
valueType: ValueType.NUMBER,
dataFormat: "#,##0.00",
},
{
name: "etcPdmtPrlg",
fieldName: "etcPdmtPrlg",
header: { text: "기타(측구 개거 등)" },
footer: footerDecimal2,
valueType: ValueType.NUMBER,
dataFormat: "#,##0.00",
},
{ name: "rmrk", fieldName: "rmrk", header: { text: "비고" } },
{
name: "admdstCd",
fieldName: "admdstCd",
header: { text: "행정구역코드" },
visible: false,
editable: false,
styleName: "rg-cell-readonly",
},
{
name: "pipeSeCd",
fieldName: "pipeSeCd",
header: { text: "관종코드" },
visible: false,
editable: false,
styleName: "rg-cell-readonly",
},
],
columnLayout: [
{
name: 'adminArea',
name: "adminArea",
hideChildHeaders: false,
header: { text: '행정구역' },
items: ['sido', 'sgg'],
header: { text: "행정구역" },
items: [{ column: "ctpvNm", footerUserSpans: [{ colspan: 2 }] }, "sggNm"],
},
'swrgPipeCd',
'totalDiameter',
"pipeSeNm",
"admdstNm",
"pdmtByTsmWhol",
{
name: 'circular',
name: "circular",
hideChildHeaders: false,
header: { text: '원형(m)' },
header: { text: "원형(m)" },
items: [
'under200',
'over200Undev300',
'over300Undev500',
'over500Undev500',
'over800',
"step1PdmtPrlg",
"step2PdmtPrlg",
"step3PdmtPrlg",
"step4PdmtPrlg",
"step5PdmtPrlg",
],
},
'box',
'etc',
'rmk',
"sqrePdmtPrlg",
"etcPdmtPrlg",
"rmrk",
"admdstCd",
"pipeSeCd",
],
rowStartNum: 2,
fixedColumns: 1,
tabKey: 'pipeDiameterStatusInp',
tabLabel: '관경별 현황'
};
fixedColumns: 2,
tabKey: "ST00020004",
tabLabel: "관경별 현황",
};
@@ -1,377 +1,186 @@
import type { ColumnConfig, ColumnLayoutItem } from "@/components/type/DataGrid.type";
import { footerDecimal2 } from "@/components/util/grid/footerSummary";
import { ValueType } from "realgrid";
// 하수도 통계 관리 > 02.하수관로 > tab 3. 관종별 현황
// SeweragePipeInputPipeTypeStatusItem + commonInfo.ctpvNm (그리드 행에 병합)
//
// 연도 그룹: yy1 = 1996년 이전, yy2 = 1996년 … API 상한 yy30(2024년)까지.
// 표시 구간: 1996년 ~ (현재 연도 − 1) 까지. API 밖 연도는 yy 인덱스 상한으로 캡.
const YY_API_MAX = 30;
/** yy 인덱스 → 달력 연도 (yy2 → 1996) */
const calendarYearForYyIndex = (yyIndex: number) => 1994 + yyIndex;
/** 이 모듈이 로드될 때의 “데이터 기준 마지막 연도” (현재 연도 − 1) */
const lastDataYear = new Date().getFullYear() - 1;
const yyYearCols = (
n: number,
opts: { footer?: typeof footerDecimal2; visible?: boolean } = {}
): ColumnConfig[] => {
const whol = `yy${n}PrlgWhol`;
const lyng = `yy${n}LyngPrlg`;
const dmltn = `yy${n}DmltnPrlg`;
const vis = opts.visible === false ? ({ visible: false } as const) : {};
return [
{
name: whol,
fieldName: whol,
header: { text: "계" },
...(opts.footer !== undefined ? { footer: opts.footer } : {}),
editable: false,
valueType: ValueType.NUMBER,
dataFormat: "#,##0.00",
...vis,
},
{
name: lyng,
fieldName: lyng,
header: { text: "매설연장(m)" },
...(opts.footer !== undefined ? { footer: opts.footer } : {}),
valueType: ValueType.NUMBER,
dataFormat: "#,##0.00",
...vis,
},
{
name: dmltn,
fieldName: dmltn,
header: { text: "철거연장(m)" },
...(opts.footer !== undefined ? { footer: opts.footer } : {}),
valueType: ValueType.NUMBER,
dataFormat: "#,##0.00",
...vis,
},
];
};
/** yy2(1996) ~ yyK 까지 표시. K는 API 상한 YY_API_MAX */
const maxYyIndex =
lastDataYear < 1996 ? 1 : Math.min(YY_API_MAX, lastDataYear - 1994);
const yearLayoutItems: ColumnLayoutItem[] = [];
for (let yy = 2; yy <= maxYyIndex; yy++) {
const y = calendarYearForYyIndex(yy);
yearLayoutItems.push({
name: `year${y}`,
hideChildHeaders: false,
header: { text: `${y}년` },
items: [`yy${yy}PrlgWhol`, `yy${yy}LyngPrlg`, `yy${yy}DmltnPrlg`],
});
}
export const PipeTypeStatusInpColumn = {
columns: [
// 행정구역
{ name: 'sido', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'sgg', fieldName: '시군구', header: { text: '시군구' }, editable: false },
// 관종
{ name: 'swrgPipeCd', fieldName: '관종', header: { text: '관종' }, footer: { text: '합계' }, editable: false },
// 총계
{ name: 'totalSum', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'totalLayingExtn', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'totalRemvlExtn', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 1996년 이전 (footer 없음)
{ name: 'totalExtn22y', fieldName: '계', header: { text: '계' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn22y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn22y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 1996년
{ name: 'totalExtn01y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn01y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn01y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 1997년
{ name: 'totalExtn02y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn02y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn02y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 1998년
{ name: 'totalExtn03y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn03y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn03y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 1999년
{ name: 'totalExtn04y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn04y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn04y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 2000년
{ name: 'totalExtn05y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn05y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn05y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 2001년
{ name: 'totalExtn06y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn06y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn06y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 2002년
{ name: 'totalExtn07y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn07y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn07y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 2003년
{ name: 'totalExtn08y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn08y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn08y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 2004년
{ name: 'totalExtn09y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn09y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn09y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 2005년
{ name: 'totalExtn10y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn10y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn10y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 2006년
{ name: 'totalExtn11y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn11y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn11y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 2007년
{ name: 'totalExtn12y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn12y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn12y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 2008년
{ name: 'totalExtn13y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn13y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn13y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 2009년
{ name: 'totalExtn14y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn14y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn14y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 2010년
{ name: 'totalExtn15y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn15y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn15y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 2011년
{ name: 'totalExtn16y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn16y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn16y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 2012년
{ name: 'totalExtn17y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn17y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn17y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 2013년
{ name: 'totalExtn18y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn18y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn18y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 2014년
{ name: 'totalExtn19y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn19y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn19y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 2015년
{ name: 'totalExtn20y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn20y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn20y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 2016년
{ name: 'totalExtn21y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn21y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn21y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 2017년
{ name: 'totalExtn23y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn23y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn23y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 2018년
{ name: 'totalExtn24y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn24y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn24y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 2019년
{ name: 'totalExtn25y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn25y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn25y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 2020년
{ name: 'totalExtn26y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn26y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn26y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 2021년
{ name: 'totalExtn27y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn27y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn27y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 2022년
{ name: 'totalExtn28y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn28y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn28y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 2023년
{ name: 'totalExtn29y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn29y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn29y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 2024년
{ name: 'totalExtn30y', fieldName: '계', header: { text: '계' }, footer: footerDecimal2, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'layingExtn30y', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
{ name: 'remvlExtn30y', fieldName: '철거연장(m)', header: { text: '철거연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 2025년
{ name: 'layingExtn', fieldName: '매설연장(m)', header: { text: '매설연장(m)' }, footer: footerDecimal2, valueType: ValueType.NUMBER, dataFormat: '#,##0.00' },
// 비고
{ name: 'rmk', fieldName: '비고', header: { text: '비고' } },
],
columnLayout: [
{
name: 'adminArea',
hideChildHeaders: false,
header: { text: '행정구역' },
items: ['sido', 'sgg'],
},
'swrgPipeCd',
{
name: 'totalGroup',
hideChildHeaders: false,
header: { text: '총계' },
items: ['totalSum', 'totalLayingExtn', 'totalRemvlExtn'],
},
{
name: 'year1996Before',
hideChildHeaders: false,
header: { text: '1996년 이전' },
items: ['totalExtn22y', 'layingExtn22y', 'remvlExtn22y'],
},
{
name: 'year1996',
hideChildHeaders: false,
header: { text: '1996년' },
items: ['totalExtn01y', 'layingExtn01y', 'remvlExtn01y'],
},
{
name: 'year1997',
hideChildHeaders: false,
header: { text: '1997년' },
items: ['totalExtn02y', 'layingExtn02y', 'remvlExtn02y'],
},
{
name: 'year1998',
hideChildHeaders: false,
header: { text: '1998년' },
items: ['totalExtn03y', 'layingExtn03y', 'remvlExtn03y'],
},
{
name: 'year1999',
hideChildHeaders: false,
header: { text: '1999년' },
items: ['totalExtn04y', 'layingExtn04y', 'remvlExtn04y'],
},
{
name: 'year2000',
hideChildHeaders: false,
header: { text: '2000년' },
items: ['totalExtn05y', 'layingExtn05y', 'remvlExtn05y'],
},
{
name: 'year2001',
hideChildHeaders: false,
header: { text: '2001년' },
items: ['totalExtn06y', 'layingExtn06y', 'remvlExtn06y'],
},
{
name: 'year2002',
hideChildHeaders: false,
header: { text: '2002년' },
items: ['totalExtn07y', 'layingExtn07y', 'remvlExtn07y'],
},
{
name: 'year2003',
hideChildHeaders: false,
header: { text: '2003년' },
items: ['totalExtn08y', 'layingExtn08y', 'remvlExtn08y'],
},
{
name: 'year2004',
hideChildHeaders: false,
header: { text: '2004년' },
items: ['totalExtn09y', 'layingExtn09y', 'remvlExtn09y'],
},
{
name: 'year2005',
hideChildHeaders: false,
header: { text: '2005년' },
items: ['totalExtn10y', 'layingExtn10y', 'remvlExtn10y'],
},
{
name: 'year2006',
hideChildHeaders: false,
header: { text: '2006년' },
items: ['totalExtn11y', 'layingExtn11y', 'remvlExtn11y'],
},
{
name: 'year2007',
hideChildHeaders: false,
header: { text: '2007년' },
items: ['totalExtn12y', 'layingExtn12y', 'remvlExtn12y'],
},
{
name: 'year2008',
hideChildHeaders: false,
header: { text: '2008년' },
items: ['totalExtn13y', 'layingExtn13y', 'remvlExtn13y'],
},
{
name: 'year2009',
hideChildHeaders: false,
header: { text: '2009년' },
items: ['totalExtn14y', 'layingExtn14y', 'remvlExtn14y'],
},
{
name: 'year2010',
hideChildHeaders: false,
header: { text: '2010년' },
items: ['totalExtn15y', 'layingExtn15y', 'remvlExtn15y'],
},
{
name: 'year2011',
hideChildHeaders: false,
header: { text: '2011년' },
items: ['totalExtn16y', 'layingExtn16y', 'remvlExtn16y'],
},
{
name: 'year2012',
hideChildHeaders: false,
header: { text: '2012년' },
items: ['totalExtn17y', 'layingExtn17y', 'remvlExtn17y'],
},
{
name: 'year2013',
hideChildHeaders: false,
header: { text: '2013년' },
items: ['totalExtn18y', 'layingExtn18y', 'remvlExtn18y'],
},
{
name: 'year2014',
hideChildHeaders: false,
header: { text: '2014년' },
items: ['totalExtn19y', 'layingExtn19y', 'remvlExtn19y'],
},
{
name: 'year2015',
hideChildHeaders: false,
header: { text: '2015년' },
items: ['totalExtn20y', 'layingExtn20y', 'remvlExtn20y'],
},
{
name: 'year2016',
hideChildHeaders: false,
header: { text: '2016년' },
items: ['totalExtn21y', 'layingExtn21y', 'remvlExtn21y'],
},
{
name: 'year2017',
hideChildHeaders: false,
header: { text: '2017년' },
items: ['totalExtn23y', 'layingExtn23y', 'remvlExtn23y'],
},
{
name: 'year2018',
hideChildHeaders: false,
header: { text: '2018년' },
items: ['totalExtn24y', 'layingExtn24y', 'remvlExtn24y'],
},
{
name: 'year2019',
hideChildHeaders: false,
header: { text: '2019년' },
items: ['totalExtn25y', 'layingExtn25y', 'remvlExtn25y'],
},
{
name: 'year2020',
hideChildHeaders: false,
header: { text: '2020년' },
items: ['totalExtn26y', 'layingExtn26y', 'remvlExtn26y'],
},
{
name: 'year2021',
hideChildHeaders: false,
header: { text: '2021년' },
items: ['totalExtn27y', 'layingExtn27y', 'remvlExtn27y'],
},
{
name: 'year2022',
hideChildHeaders: false,
header: { text: '2022년' },
items: ['totalExtn28y', 'layingExtn28y', 'remvlExtn28y'],
},
{
name: 'year2023',
hideChildHeaders: false,
header: { text: '2023년' },
items: ['totalExtn29y', 'layingExtn29y', 'remvlExtn29y'],
},
{
name: 'year2024',
hideChildHeaders: false,
header: { text: '2024년' },
items: ['totalExtn30y', 'layingExtn30y', 'remvlExtn30y'],
},
'layingExtn',
'rmk',
],
rowStartNum: 2,
fixedColumns: 2,
tabKey: 'pipeTypeStatusInp',
tabLabel: '관종별 현황'
};
{
name: "ctpvNm",
fieldName: "ctpvNm",
header: { text: "시도" },
footer: { text: "합계" },
editable: false,
styleName: "rg-cell-readonly",
},
{
name: "sggNm",
fieldName: "sggNm",
header: { text: "시군구" },
editable: false,
styleName: "rg-cell-readonly",
},
{
name: "pipeSeNm",
fieldName: "pipeSeNm",
header: { text: "관종" },
footer: { text: "합계" },
editable: false,
},
{
name: "admdstNm",
fieldName: "admdstNm",
header: { text: "행정구역명" },
editable: false,
styleName: "rg-cell-readonly",
},
{
name: "pipeByPrstWhol",
fieldName: "pipeByPrstWhol",
header: { text: "계" },
footer: footerDecimal2,
editable: false,
valueType: ValueType.NUMBER,
dataFormat: "#,##0.00",
},
{
name: "lyngPrlg",
fieldName: "lyngPrlg",
header: { text: "매설연장(m)" },
footer: footerDecimal2,
editable: false,
valueType: ValueType.NUMBER,
dataFormat: "#,##0.00",
},
{
name: "dmltnPrlg",
fieldName: "dmltnPrlg",
header: { text: "철거연장(m)" },
footer: footerDecimal2,
editable: false,
valueType: ValueType.NUMBER,
dataFormat: "#,##0.00",
},
...yyYearCols(1, {}),
...Array.from({ length: YY_API_MAX - 1 }, (_, i) => {
const yy = i + 2;
return yyYearCols(yy, {
footer: footerDecimal2,
visible: yy <= maxYyIndex,
});
}).flat(),
{ name: "rmrk", fieldName: "rmrk", header: { text: "비고" } },
{
name: "admdstCd",
fieldName: "admdstCd",
header: { text: "행정구역코드" },
visible: false,
editable: false,
styleName: "rg-cell-readonly",
},
{
name: "pipeSeCd",
fieldName: "pipeSeCd",
header: { text: "관종코드" },
visible: false,
editable: false,
styleName: "rg-cell-readonly",
},
] as ColumnConfig[],
columnLayout: [
{
name: "adminArea",
hideChildHeaders: false,
header: { text: "행정구역" },
items: [{ column: "ctpvNm", footerUserSpans: [{ colspan: 2 }] }, "sggNm"],
},
"pipeSeNm",
"admdstNm",
{
name: "totalGroup",
hideChildHeaders: false,
header: { text: "총계" },
items: ["pipeByPrstWhol", "lyngPrlg", "dmltnPrlg"],
},
{
name: "year1996Before",
hideChildHeaders: false,
header: { text: "1996년 이전" },
items: ["yy1PrlgWhol", "yy1LyngPrlg", "yy1DmltnPrlg"],
},
...yearLayoutItems,
"rmrk",
'admdstCd',
] as ColumnLayoutItem[],
rowStartNum: 2,
fixedColumns: 2,
tabKey: "ST00020003",
tabLabel: "관종별 현황",
};
@@ -63,6 +63,7 @@ export const RegionalFactStatusInpColumn = {
// 비고
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정코드' }, visible: false },
],
columnLayout: [
{
@@ -156,6 +157,7 @@ export const RegionalFactStatusInpColumn = {
'sewerLdgrEdpPrlg',
'sewerLdgrEdpRt',
'rmrk',
'admdstCd',
],
rowStartNum: 4,
fixedColumns: 1,
@@ -1,113 +1,33 @@
import { ValueType } from "realgrid";
/**
* 하수도 통계 검토 00. 통계 입력 현황
*/
const StatsInputStatusInfoColumnRaw = {
columns: [
// 기본 정보
{ name: 'chk', fieldName: 'chk', header: { text: '체크' }, width: 40 },
{ name: 'sido', fieldName: 'sido', header: { text: '시도' }, width: 80,mergeCells: true},
{ name: 'gugun', fieldName: 'gugun', header: { text: '시군구' }, width: 100 },
{ name: 'status', fieldName: 'status', header: { text: '진행상태' }, width: 120 },
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, },
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, },
{ name: 'sewerBzmnCd', fieldName: 'sewerBzmnCd', header: { text: '사업자코드' }, visible: false },
{ name: 'statsCd', fieldName: 'statsCd', header: { text: '메뉴코드' }, visible: false },
{ name: 'status', fieldName: 'status', header: { text: '진행상태' }, mergeCells: true },
{ name: 'statsNm', fieldName: 'statsNm', header: { text: '구분' }, width: 220 },
// 01. 하수도보급율 (supply)
{ name: 'supplyCharger', fieldName: 'supplyCharger', header: { text: '제출자' } },
{ name: 'supplyCttpc', fieldName: 'supplyCttpc', header: { text: '연락처' } },
{ name: 'supplyStatusNm', fieldName: 'supplyStatusNm', header: { text: '지자체진행상태' } },
{ name: 'supplyStatus', fieldName: 'supplyStatus', header: { text: '진행상태' } },
{ name: 'supplyCnt', fieldName: 'supplyCnt', header: { text: '건수' }, valueType: ValueType.NUMBER },
// 02. 하수관로 (facility) - 기존 3번
{ name: 'facilityCharger', fieldName: 'facilityCharger', header: { text: '제출자' } },
{ name: 'facilityCttpc', fieldName: 'facilityCttpc', header: { text: '연락처' } },
{ name: 'facilityStatusNm', fieldName: 'facilityStatusNm', header: { text: '지자체진행상태' } },
{ name: 'facilityStatus', fieldName: 'facilityStatus', header: { text: '진행상태' } },
{ name: 'facilityCnt', fieldName: 'facilityCnt', header: { text: '건수' }, valueType: ValueType.NUMBER },
// 03. 하수관로유지관리 (pipe) - 기존 4번
{ name: 'pipeCharger', fieldName: 'pipeCharger', header: { text: '제출자' } },
{ name: 'pipeCttpc', fieldName: 'pipeCttpc', header: { text: '연락처' } },
{ name: 'pipeStatusNm', fieldName: 'pipeStatusNm', header: { text: '지자체진행상태' } },
{ name: 'pipeCnt', fieldName: 'pipeCnt', header: { text: '건수' }, valueType: ValueType.NUMBER },
// 04. 하수저류시설 (storage) - 기존 5번
{ name: 'storageCharger', fieldName: 'storageCharger', header: { text: '제출자' } },
{ name: 'storageCttpc', fieldName: 'storageCttpc', header: { text: '연락처' } },
{ name: 'storageStatusNm', fieldName: 'storageStatusNm', header: { text: '지자체진행상태' } },
{ name: 'storageCnt', fieldName: 'storageCnt', header: { text: '건수' }, valueType: ValueType.NUMBER },
// 05. 펌프장 (pump) - 기존 6번
{ name: 'pumpCharger', fieldName: 'pumpCharger', header: { text: '제출자' } },
{ name: 'pumpCttpc', fieldName: 'pumpCttpc', header: { text: '연락처' } },
{ name: 'pumpStatusNm', fieldName: 'pumpStatusNm', header: { text: '지자체진행상태' } },
{ name: 'pumpCnt', fieldName: 'pumpCnt', header: { text: '건수' }, valueType: ValueType.NUMBER },
// 06. 공공하수처리시설 (treat) - 기존 7번
{ name: 'treatCharger', fieldName: 'treatCharger', header: { text: '제출자' } },
{ name: 'treatCttpc', fieldName: 'treatCttpc', header: { text: '연락처' } },
{ name: 'treatStatusNm', fieldName: 'treatStatusNm', header: { text: '지자체진행상태' } },
{ name: 'treatCnt', fieldName: 'treatCnt', header: { text: '건수' }, valueType: ValueType.NUMBER },
// 07. 분뇨처리시설 (small) - 기존 8번
{ name: 'smallCharger', fieldName: 'smallCharger', header: { text: '제출자' } },
{ name: 'smallCttpc', fieldName: 'smallCttpc', header: { text: '연락처' } },
{ name: 'smallStatusNm', fieldName: 'smallStatusNm', header: { text: '지자체진행상태' } },
{ name: 'smallCnt', fieldName: 'smallCnt', header: { text: '건수' }, valueType: ValueType.NUMBER },
// 08. 개인하수처리시설 (private) - 기존 9번
{ name: 'privateCharger', fieldName: 'privateCharger', header: { text: '제출자' } },
{ name: 'privateCttpc', fieldName: 'privateCttpc', header: { text: '연락처' } },
{ name: 'privateStatusNm', fieldName: 'privateStatusNm', header: { text: '지자체진행상태' } },
{ name: 'privateCnt', fieldName: 'privateCnt', header: { text: '건수' }, valueType: ValueType.NUMBER },
// 09. 하수도재정 (finance) - 기존 10번
{ name: 'financeCharger', fieldName: 'financeCharger', header: { text: '제출자' } },
{ name: 'financeCttpc', fieldName: 'financeCttpc', header: { text: '연락처' } },
{ name: 'financeStatusNm', fieldName: 'financeStatusNm', header: { text: '지자체진행상태' } },
{ name: 'financeCnt', fieldName: 'financeCnt', header: { text: '건수' }, valueType: ValueType.NUMBER },
// 10. 물재이용 (reuse) - 기존 11번
{ name: 'reuseCharger', fieldName: 'reuseCharger', header: { text: '제출자' } },
{ name: 'reuseCttpc', fieldName: 'reuseCttpc', header: { text: '연락처' } },
{ name: 'reuseStatusNm', fieldName: 'reuseStatusNm', header: { text: '지자체진행상태' } },
{ name: 'reuseCnt', fieldName: 'reuseCnt', header: { text: '건수' }, valueType: ValueType.NUMBER },
// 11. 하수및분뇨찌꺼기 (sludge) - 기존 12번
{ name: 'sludgeCharger', fieldName: 'sludgeCharger', header: { text: '제출자' } },
{ name: 'sludgeCttpc', fieldName: 'sludgeCttpc', header: { text: '연락처' } },
{ name: 'sludgeStatusNm', fieldName: 'sludgeStatusNm', header: { text: '지자체진행상태' } },
{ name: 'sludgeCnt', fieldName: 'sludgeCnt', header: { text: '건수' }, valueType: ValueType.NUMBER },
// 12. 하수및분뇨관련산업 (excreta) - 기존 13번
{ name: 'excretaCharger', fieldName: 'excretaCharger', header: { text: '제출자' } },
{ name: 'excretaCttpc', fieldName: 'excretaCttpc', header: { text: '연락처' } },
{ name: 'excretaStatusNm', fieldName: 'excretaStatusNm', header: { text: '지자체진행상태' } },
{ name: 'excretaCnt', fieldName: 'excretaCnt', header: { text: '건수' }, valueType: ValueType.NUMBER },
// 13. 하수도서비스수준 (complaint) - 기존 15번
{ name: 'complaintCharger', fieldName: 'complaintCharger', header: { text: '제출자' } },
{ name: 'complaintCttpc', fieldName: 'complaintCttpc', header: { text: '연락처' } },
{ name: 'complaintStatusNm', fieldName: 'complaintStatusNm', header: { text: '지자체진행상태' } },
{ name: 'complaintCnt', fieldName: 'complaintCnt', header: { text: '건수' }, valueType: ValueType.NUMBER },
{ name: 'chargerNm', fieldName: 'chargerNm', header: { text: '제출자' } },
{ name: 'chargerCttpc', fieldName: 'chargerCttpc', header: { text: '연락처' } },
{ name: 'statusNm', fieldName: 'statusNm', header: { text: '지자체진행상태' } },
{ name: 'statusNm2', fieldName: 'statusNm2', header: { text: '진행상태' } },
],
columnLayout: [
{ name: 'addrGroup', header: { text: '구분' }, items: ['sido', 'gugun'] },
'ctpvNm',
'sggNm',
'statsNm',
'status',
{ name: 'group01', header: { text: '01. 하수도보급율' }, items: ['supplyCharger', 'supplyCttpc', 'supplyStatusNm', 'supplyStatus', 'supplyCnt'] },
{ name: 'group02', header: { text: '02. 하수관로' }, items: ['facilityCharger', 'facilityCttpc', 'facilityStatusNm', 'facilityStatus', 'facilityCnt'] },
{ name: 'group03', header: { text: '03. 하수관로유지관리' }, items: ['pipeCharger', 'pipeCttpc', 'pipeStatusNm', 'pipeCnt'] },
{ name: 'group04', header: { text: '04. 하수저류시설' }, items: ['storageCharger', 'storageCttpc', 'storageStatusNm', 'storageCnt'] },
{ name: 'group05', header: { text: '05. 펌프장' }, items: ['pumpCharger', 'pumpCttpc', 'pumpStatusNm', 'pumpCnt'] },
{ name: 'group06', header: { text: '06. 공공하수처리시설' }, items: ['treatCharger', 'treatCttpc', 'treatStatusNm', 'treatCnt'] },
{ name: 'group07', header: { text: '07. 분뇨처리시설' }, items: ['smallCharger', 'smallCttpc', 'smallStatusNm', 'smallCnt'] },
{ name: 'group08', header: { text: '08. 개인하수처리시설' }, items: ['privateCharger', 'privateCttpc', 'privateStatusNm', 'privateCnt'] },
{ name: 'group09', header: { text: '09. 하수도재정' }, items: ['financeCharger', 'financeCttpc', 'financeStatusNm', 'financeCnt'] },
{ name: 'group10', header: { text: '10. 물재이용' }, items: ['reuseCharger', 'reuseCttpc', 'reuseStatusNm', 'reuseCnt'] },
{ name: 'group11', header: { text: '11. 하수및분뇨찌꺼기' }, items: ['sludgeCharger', 'sludgeCttpc', 'sludgeStatusNm', 'sludgeCnt'] },
{ name: 'group12', header: { text: '12. 하수및분뇨관련산업' }, items: ['excretaCharger', 'excretaCttpc', 'excretaStatusNm', 'excretaCnt'] },
{ name: 'group13', header: { text: '13. 하수도서비스수준' }, items: ['complaintCharger', 'complaintCttpc', 'complaintStatusNm', 'complaintCnt'] }
'chargerNm',
'chargerCttpc',
'statusNm',
'statusNm2',
'statsCd',
'sewerBzmnCd',
]
};
@@ -0,0 +1,94 @@
import { ENDPOINT } from "@/shared/config";
import { stClient } from "@/shared/api/client";
import type {
SeweragePipeFacilityAdminAreaListRequest,
SeweragePipeFacilityAdminAreaListResponse,
SeweragePipeFacilitiesListRequest,
SeweragePipeFacilitiesListResponse,
SeweragePipeInputConnectionStatusResponse,
SeweragePipeInputFactInstallResponse,
SeweragePipeInputManholeResponse,
SeweragePipeInputPipeDiameterResponse,
SeweragePipeInputPipeTypeResponse,
SeweragePipeInputRegionalFactStatusSaveRequest,
SeweragePipeInputRegionalFactStatusSaveApiResponse,
SeweragePipeInputRegionalResponse,
SeweragePipeRequestParams,
StatisticsInputSewPipeService,
} from "../../model/input/statistics-sewage-pipe-input-interface";
export class StatisticsInputSewPipeApiService implements StatisticsInputSewPipeService {
public async getRegionalFactStatus(param: SeweragePipeRequestParams): Promise<SeweragePipeInputRegionalResponse> {
const response = await stClient.get<SeweragePipeInputRegionalResponse> (
`${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/regional-facility-status`,
{ params: param },
);
return response.data;
}
public async getFactInstallStatus(param: SeweragePipeRequestParams): Promise<SeweragePipeInputFactInstallResponse> {
const response = await stClient.get<SeweragePipeInputFactInstallResponse> (
`${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/facility-install-status`,
{ params: param },
);
return response.data;
}
public async getPipeTypeStatus(param: SeweragePipeRequestParams): Promise<SeweragePipeInputPipeTypeResponse> {
const response = await stClient.get<SeweragePipeInputPipeTypeResponse> (
`${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/pipe-type-status`,
{ params: param },
);
return response.data;
}
public async getPipeDiameterStatus(param: SeweragePipeRequestParams): Promise<SeweragePipeInputPipeDiameterResponse> {
const response = await stClient.get<SeweragePipeInputPipeDiameterResponse>(
`${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/pipe-diameter-status`,
{ params: param },
);
return response.data;
}
public async getManholeStatus(param: SeweragePipeRequestParams): Promise<SeweragePipeInputManholeResponse> {
const response = await stClient.get<SeweragePipeInputManholeResponse>(
`${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/manhole-status`,
{ params: param },
);
return response.data;
}
public async getConnectionStatus(param: SeweragePipeRequestParams): Promise<SeweragePipeInputConnectionStatusResponse> {
const response = await stClient.get<SeweragePipeInputConnectionStatusResponse>(
`${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/facility-connection-status`,
{ params: param },
);
return response.data;
}
public async getFacilitiesList(param: SeweragePipeFacilitiesListRequest): Promise<SeweragePipeFacilitiesListResponse> {
const response = await stClient.get<SeweragePipeFacilitiesListResponse>(
`${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/facility-connection-status/popup/facilities`,
{ params: param },
);
return response.data;
}
public async getFacilityAdminAreaList(param: SeweragePipeFacilityAdminAreaListRequest): Promise<SeweragePipeFacilityAdminAreaListResponse> {
const response = await stClient.get<SeweragePipeFacilityAdminAreaListResponse>(
`${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/facility-connection-status/popup/areas`,
{ params: param },
);
return response.data;
}
public async saveRegionalFactStatus(param: SeweragePipeInputRegionalFactStatusSaveRequest): Promise<SeweragePipeInputRegionalFactStatusSaveApiResponse> {
const response = await stClient.post<SeweragePipeInputRegionalFactStatusSaveApiResponse>(
`${ENDPOINT.ST_SERVICE.INPUT}/sewage-pipe/regional-facility-status/save`,
param,
);
return response.data;
}
};
export const statisticsInputSewPipeApiService = new StatisticsInputSewPipeApiService();
@@ -36,7 +36,7 @@ export class StatisticsManagementApiService {
* @param params (yr, bsnmCd, sido, gugun)
*/
public async getStatsInputStatusList(
params: { yr?: string; bsnmCd?: string; sido?: string; gugun?: string }
params: { statsYr?: string; sewerBzmnCd?: string; ctpvNm?: string; sggNm?: string }
): Promise<StatsInputStatusReviewApiResponse> {
const response = await stClient.get<StatsInputStatusReviewApiResponse>(
`${ENDPOINT.ST_SERVICE.REVIEW}/input-status`,
@@ -9,6 +9,10 @@ import {
type cancelSubmitRequest,
type cancelSubmitResponse,
type ImrRequest,
type SubmitExceptionRequest,
type SubmitExceptionResponse,
type StatisticsLogRequest,
type StatisticsLogResponse,
} from "../model/statistics-interface";
export class StatisticsAPiService implements StatisticsService {
@@ -27,6 +31,14 @@ export class StatisticsAPiService implements StatisticsService {
return response.data;
}
public async getSggAllList(sidoName: string): Promise<ApiResponse<Sgg[]>> {
const response = await stClient.get<ApiResponse<Sgg[]>>(
`${ENDPOINT.ST_SERVICE.COMMON}/sgg-all`,
{ params: { sidoName } },
);
return response.data;
}
// 입력 - 제출 취소
public async cancelSubmit(param: cancelSubmitRequest): Promise<cancelSubmitResponse> {
const response = await stClient.put<cancelSubmitResponse>(
@@ -43,6 +55,23 @@ export class StatisticsAPiService implements StatisticsService {
);
return response.data;
}
// 예외 제출
public async submitException(param: SubmitExceptionRequest): Promise<SubmitExceptionResponse> {
const response = await stClient.post<SubmitExceptionResponse>(
`${ENDPOINT.ST_SERVICE.INPUTCOMMON}/submit-exception`,
param
);
return response.data;
}
public async getLogList(param: StatisticsLogRequest): Promise<StatisticsLogResponse> {
const response = await stClient.get<StatisticsLogResponse>(
`${ENDPOINT.ST_SERVICE.INPUTCOMMON}/logs`,
{ params: param },
);
return response.data;
}
}
export const statisticsAPi = new StatisticsAPiService();
@@ -0,0 +1,523 @@
import type { SewerageSupplyRateSaveApiResponse } from "./statistics-sewage-supply-rate-input-interface";
export interface SeweragePipeRequestParams {
ctpvNm?: string;
sggNm?: string;
sewerBzmnCd?: string;
statsYr?: string;
mode?: string;
}
export interface SeweragePipeInputStatusInfo {
laePicNm: string | null;
laePicTelNo: string | null;
laeKbrdrDeptNm: string | null;
laeKbrdrJbgdNm: string | null;
laeIdfrNm: string | null;
laeIdfrTelNo: string | null;
laeIdfrDeptNm: string | null;
laeIdfrJbgdNm: string | null;
laeSbmsnSttsCd: string | null;
laeSbmsnYmd: string | null;
}
export interface VerifyResultItem {
vrfcCd: string;
rsnExpln: string;
}
/** 필드명 -> 검증 결과 배열 */
export type SeweragePipeInputVerificationResult = Record<string, VerifyResultItem[]>;
export interface SeweragePipeInputCommonInfo {
statsYr: string;
ctpvNm: string;
sewerBzmnCd: string;
statsCd: string;
verificationResult?: SeweragePipeInputVerificationResult;
}
export interface SeweragePipeInputRegionalFactStatusItem {
admdstCd: string;
sggNm: string;
admdstNm: string;
goalYr: string;
wholPplMtncPlanPrlg: number;
wholPlanPrlg: number;
wholFcltPrlg: number;
swagePplPsplyRt: number;
cbsysPplMtncPlanArea: number;
cbsysPplMtncPlanPrlg: number;
cbsysPlanPrlg: number;
cbsysFcltPrlg: number;
cbsysSqrePrlg: number;
cbsysCiclPrlg: number;
cbsysOpnPrlg: number;
cbsysGttrPrlg: number;
drtwPplMtncPlanArea: number;
drtwPplMtncPlanPrlg: number;
drtwPplPlanPrlg: number;
drtwPplFcltPrlg: number;
drtwPplSqrePrlg: number;
drtwPplCiclPrlg: number;
exlnPplMtncPlanArea: number;
exlnPplMtncPlanPrlg: number;
exlnPplPlanPrlg: number;
exlnPplFcltPrlg: number;
exlnPplSqrePrlg: number;
exlnPplCiclPrlg: number;
exlnPplOpnPrlg: number;
exlnPplGttrPrlg: number;
cbsysMnhlCnt: number;
sprsysDrtwMnhlCnt: number;
sprsysExlnMnhlCnt: number;
drtwRcvprpCnt: number;
exlnRcvprpCnt: number;
stofcbrCnt: number;
exlnOtflCnt: number;
sewerLdgrEdpPrlg: number;
sewerLdgrEdpRt: number;
rmrk: string | null;
verificationResult?: SeweragePipeInputVerificationResult;
}
export interface SeweragePipeInputRegionalResponseData {
statusInfo: SeweragePipeInputStatusInfo;
commonInfo: SeweragePipeInputCommonInfo;
regionalFactStatusList: SeweragePipeInputRegionalFactStatusItem[];
}
export interface SeweragePipeInputRegionalResponse {
status: string;
data: SeweragePipeInputRegionalResponseData;
message?: string;
}
/** 지역별 시설현황(Tab1) 저장 — saveList 행 (검증 결과 제외) */
export type SeweragePipeInputRegionalFactStatusSaveItem = Omit<
SeweragePipeInputRegionalFactStatusItem,
"verificationResult"
>;
/** 지역별 시설현황 저장 API 요청 */
export interface SeweragePipeInputRegionalFactStatusSaveRequest {
statsYr: string;
ctpvNm: string;
sggNm: string;
sewerBzmnCd: string;
userId: string;
superAdmin: string;
saveList: SeweragePipeInputRegionalFactStatusSaveItem[];
}
/** 지역별 시설현황 저장 API 응답 — 보급률 저장과 동일 래핑 */
export type SeweragePipeInputRegionalFactStatusSaveApiResponse = SewerageSupplyRateSaveApiResponse;
/** 시설별 설치 현황 — 목록 항목 */
export interface SeweragePipeInputFactInstallStatusItem {
admdstCd: string;
sggNm: string;
admdstNm: string;
prcsFcltSn: number;
swtrmtFcltCd: string;
swtrmtFcltNm: string;
prcsFcltCpct: number;
prcsZoneArea: number;
cbsysPrcsZoneArea: number;
sprsysPrcsZoneArea: number;
sprsysVartnRt: number;
mltvEqpmCnt: number;
cbsysMltvEqpmCnt: number;
sprsysMltvEqpmCnt: number;
sprsysMltvLnkgCnt: number;
sprsysMltvYetLnkgCnt: number;
wholFcltPrlg: number;
wholIcptgAtlfcsPrlg: number;
icptgPrlg: number;
atlfcsPrlg: number;
cbsysStofcbrCnt: number;
sprsysStofcbrCnt: number;
sprsysExlnOtflCnt: number;
wholBrchlnPrlg: number;
cbsysBrchlnPrlg: number;
exlnBrchlnPrlg: number;
drtwBrchlnPrlg: number;
rmrk: string;
verificationResult?: SeweragePipeInputVerificationResult;
}
/** 시설별 설치 현황 — data 본문 */
export interface SeweragePipeInputFactInstallResponseData {
statusInfo: SeweragePipeInputStatusInfo;
commonInfo: SeweragePipeInputCommonInfo;
factInstallStatusList: SeweragePipeInputFactInstallStatusItem[];
}
/** 시설별 설치 현황 — API 응답 */
export interface SeweragePipeInputFactInstallResponse {
status: string;
data: SeweragePipeInputFactInstallResponseData;
message?: string;
}
/** 관종별 현황 — 목록 항목 */
export interface SeweragePipeInputPipeTypeStatusItem {
admdstCd: string;
sggNm: string;
admdstNm: string;
pipeSeCd: string;
pipeSeNm: string;
pipeByPrstWhol: number;
lyngPrlg: number;
dmltnPrlg: number;
yy1PrlgWhol: number;
yy1LyngPrlg: number;
yy1DmltnPrlg: number;
yy2PrlgWhol: number;
yy2LyngPrlg: number;
yy2DmltnPrlg: number;
yy3PrlgWhol: number;
yy3LyngPrlg: number;
yy3DmltnPrlg: number;
yy4PrlgWhol: number;
yy4LyngPrlg: number;
yy4DmltnPrlg: number;
yy5PrlgWhol: number;
yy5LyngPrlg: number;
yy5DmltnPrlg: number;
yy6PrlgWhol: number;
yy6LyngPrlg: number;
yy6DmltnPrlg: number;
yy7PrlgWhol: number;
yy7LyngPrlg: number;
yy7DmltnPrlg: number;
yy8PrlgWhol: number;
yy8LyngPrlg: number;
yy8DmltnPrlg: number;
yy9PrlgWhol: number;
yy9LyngPrlg: number;
yy9DmltnPrlg: number;
yy10PrlgWhol: number;
yy10LyngPrlg: number;
yy10DmltnPrlg: number;
yy11PrlgWhol: number;
yy11LyngPrlg: number;
yy11DmltnPrlg: number;
yy12PrlgWhol: number;
yy12LyngPrlg: number;
yy12DmltnPrlg: number;
yy13PrlgWhol: number;
yy13LyngPrlg: number;
yy13DmltnPrlg: number;
yy14PrlgWhol: number;
yy14LyngPrlg: number;
yy14DmltnPrlg: number;
yy15PrlgWhol: number;
yy15LyngPrlg: number;
yy15DmltnPrlg: number;
yy16PrlgWhol: number;
yy16LyngPrlg: number;
yy16DmltnPrlg: number;
yy17PrlgWhol: number;
yy17LyngPrlg: number;
yy17DmltnPrlg: number;
yy18PrlgWhol: number;
yy18LyngPrlg: number;
yy18DmltnPrlg: number;
yy19PrlgWhol: number;
yy19LyngPrlg: number;
yy19DmltnPrlg: number;
yy20PrlgWhol: number;
yy20LyngPrlg: number;
yy20DmltnPrlg: number;
yy21PrlgWhol: number;
yy21LyngPrlg: number;
yy21DmltnPrlg: number;
yy22PrlgWhol: number;
yy22LyngPrlg: number;
yy22DmltnPrlg: number;
yy23PrlgWhol: number;
yy23LyngPrlg: number;
yy23DmltnPrlg: number;
yy24PrlgWhol: number;
yy24LyngPrlg: number;
yy24DmltnPrlg: number;
yy25PrlgWhol: number;
yy25LyngPrlg: number;
yy25DmltnPrlg: number;
yy26PrlgWhol: number;
yy26LyngPrlg: number;
yy26DmltnPrlg: number;
yy27PrlgWhol: number;
yy27LyngPrlg: number;
yy27DmltnPrlg: number;
yy28PrlgWhol: number;
yy28LyngPrlg: number;
yy28DmltnPrlg: number;
yy29PrlgWhol: number;
yy29LyngPrlg: number;
yy29DmltnPrlg: number;
yy30PrlgWhol: number;
yy30LyngPrlg: number;
yy30DmltnPrlg: number;
rmrk: string;
verificationResult?: SeweragePipeInputVerificationResult;
}
/** 관종별 현황 — data 본문 */
export interface SeweragePipeInputPipeTypeResponseData {
statusInfo: SeweragePipeInputStatusInfo;
commonInfo: SeweragePipeInputCommonInfo;
pipeTypeStatusList: SeweragePipeInputPipeTypeStatusItem[];
}
/** 관종별 현황 — API 응답 */
export interface SeweragePipeInputPipeTypeResponse {
status: string;
data: SeweragePipeInputPipeTypeResponseData;
message?: string;
}
/** 관경별 현황 — 목록 항목 */
export interface SeweragePipeInputPipeDiameterStatusItem {
admdstCd: string;
sggNm: string;
admdstNm: string;
pipeSeCd: string;
pipeSeNm: string;
pdmtByTsmWhol: number;
step1PdmtPrlg: number;
step2PdmtPrlg: number;
step3PdmtPrlg: number;
step4PdmtPrlg: number;
step5PdmtPrlg: number;
sqrePdmtPrlg: number;
etcPdmtPrlg: number;
rmrk: string;
verificationResult?: SeweragePipeInputVerificationResult;
}
/** 관경별 현황 — data 본문 */
export interface SeweragePipeInputPipeDiameterResponseData {
statusInfo: SeweragePipeInputStatusInfo;
commonInfo: SeweragePipeInputCommonInfo;
pipeDiameterStatusList: SeweragePipeInputPipeDiameterStatusItem[];
}
/** 관경별 현황 — API 응답 */
export interface SeweragePipeInputPipeDiameterResponse {
status: string;
data: SeweragePipeInputPipeDiameterResponseData;
message?: string;
}
/** 맨홀 현황 — 목록 항목 (yy14=2014년 … yy25=2025년) */
export interface SeweragePipeInputManholeStatusItem {
sggNm: string;
admdstCd: string;
admdstNm: string;
emphsMngSeNm: string;
totalWholMnhlCnt: number;
totalStep1MnhlCnt: number;
totalStep2MnhlCnt: number;
totalStep3MnhlCnt: number;
totalStep4MnhlCnt: number;
totalStep5MnhlCnt: number;
yy14WholMnhlCnt: number;
yy14Step1MnhlCnt: number;
yy14Step2MnhlCnt: number;
yy14Step3MnhlCnt: number;
yy14Step4MnhlCnt: number;
yy14Step5MnhlCnt: number;
yy15WholMnhlCnt: number;
yy15Step1MnhlCnt: number;
yy15Step2MnhlCnt: number;
yy15Step3MnhlCnt: number;
yy15Step4MnhlCnt: number;
yy15Step5MnhlCnt: number;
yy16WholMnhlCnt: number;
yy16Step1MnhlCnt: number;
yy16Step2MnhlCnt: number;
yy16Step3MnhlCnt: number;
yy16Step4MnhlCnt: number;
yy16Step5MnhlCnt: number;
yy17WholMnhlCnt: number;
yy17Step1MnhlCnt: number;
yy17Step2MnhlCnt: number;
yy17Step3MnhlCnt: number;
yy17Step4MnhlCnt: number;
yy17Step5MnhlCnt: number;
yy18WholMnhlCnt: number;
yy18Step1MnhlCnt: number;
yy18Step2MnhlCnt: number;
yy18Step3MnhlCnt: number;
yy18Step4MnhlCnt: number;
yy18Step5MnhlCnt: number;
yy19WholMnhlCnt: number;
yy19Step1MnhlCnt: number;
yy19Step2MnhlCnt: number;
yy19Step3MnhlCnt: number;
yy19Step4MnhlCnt: number;
yy19Step5MnhlCnt: number;
yy20WholMnhlCnt: number;
yy20Step1MnhlCnt: number;
yy20Step2MnhlCnt: number;
yy20Step3MnhlCnt: number;
yy20Step4MnhlCnt: number;
yy20Step5MnhlCnt: number;
yy21WholMnhlCnt: number;
yy21Step1MnhlCnt: number;
yy21Step2MnhlCnt: number;
yy21Step3MnhlCnt: number;
yy21Step4MnhlCnt: number;
yy21Step5MnhlCnt: number;
yy22WholMnhlCnt: number;
yy22Step1MnhlCnt: number;
yy22Step2MnhlCnt: number;
yy22Step3MnhlCnt: number;
yy22Step4MnhlCnt: number;
yy22Step5MnhlCnt: number;
yy23WholMnhlCnt: number;
yy23Step1MnhlCnt: number;
yy23Step2MnhlCnt: number;
yy23Step3MnhlCnt: number;
yy23Step4MnhlCnt: number;
yy23Step5MnhlCnt: number;
yy24WholMnhlCnt: number;
yy24Step1MnhlCnt: number;
yy24Step2MnhlCnt: number;
yy24Step3MnhlCnt: number;
yy24Step4MnhlCnt: number;
yy24Step5MnhlCnt: number;
yy25WholMnhlCnt: number;
yy25Step1MnhlCnt: number;
yy25Step2MnhlCnt: number;
yy25Step3MnhlCnt: number;
yy25Step4MnhlCnt: number;
yy25Step5MnhlCnt: number;
rmrk: string;
verificationResult?: SeweragePipeInputVerificationResult;
}
/** 맨홀 현황 — data 본문 */
export interface SeweragePipeInputManholeResponseData {
statusInfo: SeweragePipeInputStatusInfo;
commonInfo: SeweragePipeInputCommonInfo;
manholeStatusList: SeweragePipeInputManholeStatusItem[];
}
/** 맨홀 현황 — API 응답 */
export interface SeweragePipeInputManholeResponse {
status: string;
data: SeweragePipeInputManholeResponseData;
message?: string;
}
/** 시설별 현황 관리 - 하수처리장별 연결 현황 목록 항목 */
export interface SeweragePipeInputFacilityConnectionItem {
bscFctNm: string;
fctCpc: number;
bscFctCd: string;
isConnection: string;
connectionAreaList: string;
}
/** 시설별 현황 관리 - 행정구역별 연결 현황 목록 항목 */
export interface SeweragePipeInputAdminAreaConnectionItem {
ctpvNm: string;
sggNm: string;
admdstNm: string;
admdstCd: string;
isConnection: string;
connectionBscFctNmList: string;
}
/** 시설별 현황 관리 — data 본문 */
export interface SeweragePipeInputConnectionStatusResponseData {
statusInfo: SeweragePipeInputStatusInfo;
commonInfo: SeweragePipeInputCommonInfo;
facilityConnectionList: SeweragePipeInputFacilityConnectionItem[];
adminAreaConnectionList: SeweragePipeInputAdminAreaConnectionItem[];
}
/** 시설별 현황 관리 — API 응답 */
export interface SeweragePipeInputConnectionStatusResponse {
status: string;
data: SeweragePipeInputConnectionStatusResponseData;
message?: string;
}
/** facilitiesList 요청 — statsYr 필수, 나머지 선택 */
export interface SeweragePipeFacilitiesListRequest {
statsYr: string;
sewerBzmnCd?: string;
ctpvNm?: string;
sggNm?: string;
fcltyNm?: string;
}
/** facilitiesList 응답 — 목록 항목 */
export interface SeweragePipeFacilitiesListItem {
statsYr: string;
ctpvNm: string;
sggNm: string;
admdstCd: string;
bscFctNm: string;
fctCpc: number;
bscFctCd: string;
}
/** facilitiesList API 응답 */
export interface SeweragePipeFacilitiesListResponse {
status: string;
data: SeweragePipeFacilitiesListItem[];
message?: string;
}
/** 처리시설 선택 후 행정구역(연결) 목록 조회 요청 */
export interface SeweragePipeFacilityAdminAreaListRequest {
statsYr: string;
swtrmtFcltCd: string;
sewerBzmnCd: string;
ctpvNm: string;
sggNm: string;
}
/** 처리시설별 행정구역 목록 항목 */
export interface SeweragePipeFacilityAdminAreaItem {
ctpvNm: string;
sggNm: string;
admdstNm: string;
admdstCd: string;
isConnection: string;
}
/** 처리시설별 행정구역 목록 API 응답 */
export interface SeweragePipeFacilityAdminAreaListResponse {
status: string;
data: SeweragePipeFacilityAdminAreaItem[];
message?: string;
}
export interface StatisticsInputSewPipeService {
getRegionalFactStatus(param: SeweragePipeRequestParams): Promise<SeweragePipeInputRegionalResponse>;
getFactInstallStatus(param: SeweragePipeRequestParams): Promise<SeweragePipeInputFactInstallResponse>;
getPipeTypeStatus(param: SeweragePipeRequestParams): Promise<SeweragePipeInputPipeTypeResponse>;
getPipeDiameterStatus(param: SeweragePipeRequestParams): Promise<SeweragePipeInputPipeDiameterResponse>;
getManholeStatus(param: SeweragePipeRequestParams): Promise<SeweragePipeInputManholeResponse>;
getConnectionStatus(param: SeweragePipeRequestParams): Promise<SeweragePipeInputConnectionStatusResponse>;
getFacilitiesList(param: SeweragePipeFacilitiesListRequest): Promise<SeweragePipeFacilitiesListResponse>;
getFacilityAdminAreaList(param: SeweragePipeFacilityAdminAreaListRequest): Promise<SeweragePipeFacilityAdminAreaListResponse>;
saveRegionalFactStatus(
param: SeweragePipeInputRegionalFactStatusSaveRequest,
): Promise<SeweragePipeInputRegionalFactStatusSaveApiResponse>;
}
@@ -4,92 +4,16 @@ import type { ApiResponse } from "./statistics-interface";
* 검토-00.통계 입력현황 아이템 인터페이스
*/
export interface StatsInputStatusReviewItem {
chk: string;
sido: string;
gugun: string;
yr: string;
bsnmCd: string;
status: string;
// 01. 하수도보급율 (supply)
supplyCharger: string;
supplyCttpc: string;
supplyStatusNm: string;
supplyStatus: string;
supplyCnt: number;
// 02. 하수관로 (facility - 기존 3번)
facilityCharger: string;
facilityCttpc: string;
facilityStatusNm: string;
facilityStatus: string;
facilityCnt: number;
// 03. 하수관로유지관리 (pipe - 기존 4번)
pipeCharger: string;
pipeCttpc: string;
pipeStatusNm: string;
pipeCnt: number;
// 04. 하수저류시설 (storage - 기존 5번)
storageCharger: string;
storageCttpc: string;
storageStatusNm: string;
storageCnt: number;
// 05. 펌프장 (pump - 기존 6번)
pumpCharger: string;
pumpCttpc: string;
pumpStatusNm: string;
pumpCnt: number;
// 06. 공공하수처리시설 (treat - 기존 7번)
treatCharger: string;
treatCttpc: string;
treatStatusNm: string;
treatCnt: number;
// 07. 분뇨처리시설 (small - 기존 8번)
smallCharger: string;
smallCttpc: string;
smallStatusNm: string;
smallCnt: number;
// 08. 개인하수처리시설 (private - 기존 9번)
privateCharger: string;
privateCttpc: string;
privateStatusNm: string;
privateCnt: number;
// 09. 하수도재정 (finance - 기존 10번)
financeCharger: string;
financeCttpc: string;
financeStatusNm: string;
financeCnt: number;
// 10. 물재이용 (reuse - 기존 11번)
reuseCharger: string;
reuseCttpc: string;
reuseStatusNm: string;
reuseCnt: number;
// 11. 하수및분뇨찌꺼기 (sludge - 기존 12번)
sludgeCharger: string;
sludgeCttpc: string;
sludgeStatusNm: string;
sludgeCnt: number;
// 12. 하수및분뇨관련산업 (excreta - 기존 13번)
excretaCharger: string;
excretaCttpc: string;
excretaStatusNm: string;
excretaCnt: number;
// 13. 하수도서비스수준 (complaint - 기존 15번)
complaintCharger: string;
complaintCttpc: string;
complaintStatusNm: string;
complaintCnt: number;
ctpvNm?: string;
sggNm?: string;
sewerBzmnCd?: string;
status?: string;
statsNm?: string;
statsCd?: string;
statusNm?: string;
statusCd?: string;
chargerNm?: string;
chargerCttpc?: string;
}
export interface StatsInputStatusReviewResponseData {
@@ -43,9 +43,65 @@ export interface ImrRequest {
lae_sbmsn_stts_cd: string;
}
export interface SubmitExceptionRequest {
statsYr: string;
statsCd: string;
ctpvNm: string;
sggNm: string;
sewerBzmnCd: string;
userId: string;
superAdmin?: string;
laePicNm: string;
laePicTelNo: string;
laeKbrdrDeptNm: string;
laeKbrdrJbgdNm: string;
laeIdfrNm: string;
laeIdfrTelNo: string;
laeIdfrDeptNm: string;
laeIdfrJbgdNm: string
}
export interface SubmitExceptionResponse {
status: string;
message?: string;
}
export interface StatisticsLog {
statsLogSn?: number;
menuNm?: string;
tabNm?: string;
tblNm?: string;
dmndFwkNm?: string;
rgtrId?: string;
regDt?: string;
}
export interface StatisticsLogRequest {
page: number;
size: number;
statsCd?: string;
}
export interface StatisticsLogResponse {
status: string;
message?: string;
data?: {
statsLogList: StatisticsLog[];
pageInfo: {
totalElements: number;
totalPages: number;
currentPage: number;
size: number;
};
};
}
export interface StatisticsService {
getSidoList(): Promise<ApiResponse<SearchCondition>>;
getSggList(sidoName: string): Promise<ApiResponse<Sgg[]>>;
getSggAllList(sidoName: string): Promise<ApiResponse<Sgg[]>>;
cancelSubmit(param: cancelSubmitRequest): Promise<cancelSubmitResponse>;
callImrService(param: ImrRequest): Promise<null>;
submitException(param: SubmitExceptionRequest): Promise<SubmitExceptionResponse>;
getLogList(param: StatisticsLogRequest): Promise<StatisticsLogResponse>;
}