[st] - 입력단 메뉴 작업중

This commit is contained in:
2026-06-15 10:38:33 +09:00
parent 49e42757bc
commit e3eb542e59
43 changed files with 7637 additions and 947 deletions
@@ -43,8 +43,10 @@ function buildFieldsFromColumns(columns: { fieldName: string; valueType?: ValueT
const hasNumeratorDenominator = "numeratorFields" in col.computed && col.computed.numeratorFields.length > 0 && col.computed.denominatorFields.length > 0;
const hasMaxOfFieldAndSum = "baseField" in col.computed && "sumFields" in col.computed && col.computed.sumFields.length > 0;
const hasDifferenceOfSums = "minuendFields" in col.computed && "subtrahendFields" in col.computed && col.computed.minuendFields.length > 0;
const hasHalfSumOfSums = col.computed.type === "halfSumOfSums" && "firstSumFields" in col.computed && "secondSumFields" in col.computed && col.computed.firstSumFields.length > 0 && col.computed.secondSumFields.length > 0;
const hasSumOfTwoHalfSumOfSums = col.computed.type === "sumOfTwoHalfSumOfSums" && "firstHalfSum" in col.computed && "secondHalfSum" in col.computed && col.computed.firstHalfSum.firstSumFields.length > 0 && col.computed.firstHalfSum.secondSumFields.length > 0 && col.computed.secondHalfSum.firstSumFields.length > 0 && col.computed.secondHalfSum.secondSumFields.length > 0;
const hasSumOfMaxGroups = "groups" in col.computed && Array.isArray(col.computed.groups) && col.computed.groups.length > 0;
if (hasFields || hasNumeratorDenominator || hasMaxOfFieldAndSum || hasDifferenceOfSums || hasSumOfMaxGroups) {
if (hasFields || hasNumeratorDenominator || hasMaxOfFieldAndSum || hasDifferenceOfSums || hasHalfSumOfSums || hasSumOfTwoHalfSumOfSums || hasSumOfMaxGroups) {
field.valueCallback = getComputedValueCallback(col.computed) as (prod: unknown, dataRow: number, fieldName: string, fieldNames: string[], values: unknown[]) => number;
}
}
+84 -76
View File
@@ -21,6 +21,7 @@ import type {
import { getComputedValueCallback } from "@/components/util/grid/computedFieldCallbacks";
import {
getExcelImportColumnFieldNames,
prepareGridColumnsForExcelExport,
restoreGridColumnsAfterExcelExport,
shouldPrepareColumnsForExcelExport,
@@ -33,7 +34,7 @@ declare module "realgrid" {
setOptions(options: {
footer?: { visible?: boolean };
summaryMode?: "aggregate" | "statistical" | "none";
checkBar?: { visible?: boolean };
checkBar?: { visible?: boolean; exclusive?: boolean };
}): void;
}
}
@@ -461,8 +462,10 @@ function getDecimalPlacesFromDataFormat(dataFormat?: string): number | null {
if (!dataFormat) return null;
const match = dataFormat.match(/\.(0+|#+)/);
const decimalToken = match?.[1];
if (!decimalToken) return null;
return decimalToken.length;
if (decimalToken) return decimalToken.length;
// #,##0 등 정수 포맷 — 소수부 없음 → 0자리 절삭(내림)
if (/[#0]/.test(dataFormat)) return 0;
return null;
}
function truncateByDecimalPlaces(value: number, decimalPlaces: number): number {
@@ -527,6 +530,20 @@ function buildFieldsFromColumns(
"minuendFields" in col.computed &&
"subtrahendFields" in col.computed &&
col.computed.minuendFields.length > 0;
const hasHalfSumOfSums =
col.computed.type === "halfSumOfSums" &&
"firstSumFields" in col.computed &&
"secondSumFields" in col.computed &&
col.computed.firstSumFields.length > 0 &&
col.computed.secondSumFields.length > 0;
const hasSumOfTwoHalfSumOfSums =
col.computed.type === "sumOfTwoHalfSumOfSums" &&
"firstHalfSum" in col.computed &&
"secondHalfSum" in col.computed &&
col.computed.firstHalfSum.firstSumFields.length > 0 &&
col.computed.firstHalfSum.secondSumFields.length > 0 &&
col.computed.secondHalfSum.firstSumFields.length > 0 &&
col.computed.secondHalfSum.secondSumFields.length > 0;
const hasSumOfMaxGroups =
col.computed.type === "sumOfMaxOfFieldAndSumGroups" &&
"groups" in col.computed &&
@@ -536,13 +553,27 @@ function buildFieldsFromColumns(
col.computed.type === "pumpGrossPpqty" &&
"pumpGroups" in col.computed &&
col.computed.pumpGroups.length > 0;
const hasBodPrcsRt =
col.computed.type === "bodPrcsRt" &&
"inflwField" in col.computed &&
"dschgField" in col.computed;
const hasQuotientRatioPercent =
col.computed.type === "quotientRatioPercent" &&
"numeratorQuotient" in col.computed &&
"denominatorQuotient" in col.computed &&
col.computed.numeratorQuotient.length === 2 &&
col.computed.denominatorQuotient.length === 2;
if (
hasFields ||
hasNumeratorDenominator ||
hasMaxOfFieldAndSum ||
hasDifferenceOfSums ||
hasHalfSumOfSums ||
hasSumOfTwoHalfSumOfSums ||
hasSumOfMaxGroups ||
hasPumpGrossPpqty
hasPumpGrossPpqty ||
hasBodPrcsRt ||
hasQuotientRatioPercent
) {
const computedCallback = getComputedValueCallback(
col.computed,
@@ -629,6 +660,7 @@ const DataGrid = ({
const onItemAllCheckedRef =
useRef<typeof onItemAllChecked>(onItemAllChecked);
const isSyncingChecksFromRowDataRef = useRef(false);
const columnsFieldsSigRef = useRef("");
columnsRef.current = columns;
rowDataRef.current = rowData;
cellStyleCallbackRef.current = cellStyleCallback;
@@ -681,20 +713,6 @@ const DataGrid = ({
.find((s) => s.menuEngNm === activeTabId)?.menuKornNm ||
"export";
// 엑셀 import 시 매칭을 위해 헤더에 fieldName 포함 (예: "합계 (swgeProcZoneInnerSm)")
const originalHeaders: Map<string, string> = new Map();
columns.forEach((col) => {
if (col.fieldName && gridView.columnByName) {
const gridCol = gridView.columnByName(col.name);
if (gridCol && gridCol.header) {
const originalText = gridCol.header.text || "";
originalHeaders.set(col.name, originalText);
const headerWithFieldName = `${originalText} (${col.fieldName})`;
gridCol.header.text = headerWithFieldName;
}
}
});
// exportGrid 호출 전에 columnLayout 정규화 (col 속성 추가)
const normalizeLayoutForExport = (
layout: ColumnLayoutItem[],
@@ -788,20 +806,6 @@ const DataGrid = ({
excelExportVisibilitySnapshot,
);
}
// 헤더 원복
columns.forEach((col) => {
if (col.fieldName && gridView.columnByName) {
const gridCol = gridView.columnByName(col.name);
if (
gridCol &&
gridCol.header &&
originalHeaders.has(col.name)
) {
gridCol.header.text =
originalHeaders.get(col.name) || "";
}
}
});
// columnLayout 원복 (footerUserSpans 유지)
if (normalizedLayout && !layoutRestored) {
layoutRestored = true;
@@ -903,36 +907,11 @@ const DataGrid = ({
: headerStartRow;
const dataStartRow = maxHeaderRowIndex + 1;
// 엑셀 컬럼 인덱스 → 그리드 fieldName 매핑
const headerToFieldNameMap = new Map<string, string>();
headers.forEach((headerText, colIndex) => {
let fieldName: string | null = null;
// 헤더에 fieldName이 포함된 경우 (예: "합계 (swgeProcZoneInnerSm)")
const fieldNameMatch = headerText.match(
/\(([a-zA-Z_][a-zA-Z0-9_]*)\)$/,
);
if (fieldNameMatch && fieldNameMatch[1]) {
const extractedFieldName = fieldNameMatch[1];
if (
columns.find(
(c) => c.fieldName === extractedFieldName,
)
) {
fieldName = extractedFieldName;
}
}
// 매칭 실패 시 원본 헤더 텍스트 사용
if (!fieldName) {
console.warn(
`엑셀 컬럼 ${colIndex + 1} (헤더: "${headerText}") 매칭 실패 → 헤더 텍스트를 fieldName으로 사용`,
);
fieldName = headerText;
}
headerToFieldNameMap.set(headerText, fieldName);
});
// 엑셀 N번째 열 → 그리드 N번째 fieldName (보내기 컬럼 순서 기준)
const excelColumnFieldNames = getExcelImportColumnFieldNames(
columns,
columnLayout,
);
// 데이터 행 읽기
const dataRows: Record<string, string | number>[] = [];
@@ -950,12 +929,8 @@ const DataGrid = ({
worksheet[
XLSX.utils.encode_cell({ r: row, c: col })
];
const headerName =
headers[colIndex] || `Column${colIndex + 1}`;
// 헤더 텍스트를 fieldName으로 변환
const fieldName =
headerToFieldNameMap.get(headerName) ||
headerName;
const fieldName = excelColumnFieldNames[colIndex];
if (!fieldName) continue;
let cellValue: string | number = "";
if (
@@ -1039,6 +1014,21 @@ const DataGrid = ({
const dataProvider = gridRef.current.dataProvider;
applyDefaultGridEditOptions(gridView as GridViewWithEditOptions);
const columnsFieldsSig = columns.map((c) => c.fieldName).join("\u0001");
if (columnsFieldsSig !== columnsFieldsSigRef.current) {
columnsFieldsSigRef.current = columnsFieldsSig;
const dpForFields = dataProvider as {
setFields?: (f: unknown[]) => void;
setRows?: (r: Record<string, string | number>[]) => void;
};
try {
dpForFields.setFields?.(buildFieldsFromColumns(columns));
dpForFields.setRows?.(rowDataRef.current);
} catch (e) {
console.warn("setFields sync:", e);
}
}
// mergeCells: true인 컬럼이 있으면 onSorting에서 클라이언트 정렬로 처리
const hasMergeCells = columns.some((col) => col.mergeCells === true);
@@ -1105,6 +1095,11 @@ const DataGrid = ({
tooltip: resolveColumnTooltip(column),
}),
);
gv.setColumnProperty?.(
column.name,
"editable",
column.editable !== false,
);
applyColumnListEditorProperties(gv, column);
});
if (columns.some((c) => isDropDownEditorColumn(c))) {
@@ -1152,7 +1147,7 @@ const DataGrid = ({
// 헤더 체크박스 표시 여부 설정
gridView.setOptions({
checkBar: { visible: showHeaderCheckbox },
checkBar: { visible: showHeaderCheckbox, exclusive: false },
rowIndicator: { visible: false },
});
@@ -1758,6 +1753,22 @@ const DataGrid = ({
layout={columnLayout}
rows={rowData}
onRGDataColumnsChanged={(grid) => {
const dp = gridRef.current?.dataProvider as
| {
setFields?: (f: unknown[]) => void;
setRows?: (
r: Record<string, string | number>[],
) => void;
}
| undefined;
try {
dp?.setFields?.(
buildFieldsFromColumns(columnsRef.current),
);
dp?.setRows?.(rowDataRef.current);
} catch (e) {
console.warn("onRGDataColumnsChanged setFields:", e);
}
reapplyListEditorsOnGrid(
grid as GridViewWithSetColumnProperty,
columnsRef.current,
@@ -1797,6 +1808,9 @@ const DataGrid = ({
dataProvider.setFields(
buildFieldsFromColumns(columns),
);
columnsFieldsSigRef.current = columns
.map((c) => c.fieldName)
.join("\u0001");
}
if (dataProvider?.setRows)
dataProvider.setRows(rowData);
@@ -1978,15 +1992,9 @@ const DataGrid = ({
{columns.map((column) => {
const baseStyleName = column.styleName;
const columnReactKey =
column.values?.length &&
isDropDownEditorColumn(column)
? `${column.name}-dropdown-${column.values.length}`
: column.name;
return (
<RGDataColumn
key={columnReactKey}
key={column.name}
name={column.name}
fieldName={column.fieldName}
valueType={
@@ -116,7 +116,7 @@ const getSidoListFetcher = async (): Promise<ApiResponse<SearchCondition>> => {
const getSggAllListFetcher = async (
key: [string, string],
): Promise<ApiResponse<Sgg[]>> => {
return statisticsApi.getSggAllList(key[1]);
return statisticsApi.getSggList(key[1]);
};
type SidoSggFacilityNameSearchFormProps = {
@@ -292,17 +292,12 @@ const SidoSggFacilityNameSearchForm = ({
return;
}
const ctpvNm = String(row.ctpvNm ?? sido);
const sggNm = String(
row.sggNm ?? (gugun !== "ALL" && gugun ? gugun : ""),
);
const params: SeweragePipeFacilityAdminAreaListRequest = {
statsYr: statsYr.trim(),
swtrmtFcltCd,
sewerBzmnCd: initialSearchValues?.bsnmCd || '',
ctpvNm,
...(doNameList.includes(ctpvNm) ? { sggNm } : {}),
ctpvNm: initialSidoValue || '',
...(doNameList.includes(initialSidoValue) ? { sggNm: initialGugunValue } : {}),
};
try {
+11 -49
View File
@@ -17,6 +17,7 @@ import SplitDataGrid from "./SplitDataGrid";
import { getComputedValueCallback } from "@/components/util/grid/computedFieldCallbacks"
import {
getExcelImportColumnFieldNames,
prepareGridColumnsForExcelExport,
restoreGridColumnsAfterExcelExport,
shouldPrepareColumnsForExcelExport,
@@ -222,8 +223,10 @@ function buildFieldsFromColumns(columns: { fieldName: string; valueType?: ValueT
const hasNumeratorDenominator = "numeratorFields" in col.computed && col.computed.numeratorFields.length > 0 && col.computed.denominatorFields.length > 0;
const hasMaxOfFieldAndSum = "baseField" in col.computed && "sumFields" in col.computed && col.computed.sumFields.length > 0;
const hasDifferenceOfSums = "minuendFields" in col.computed && "subtrahendFields" in col.computed && col.computed.minuendFields.length > 0;
const hasHalfSumOfSums = col.computed.type === "halfSumOfSums" && "firstSumFields" in col.computed && "secondSumFields" in col.computed && col.computed.firstSumFields.length > 0 && col.computed.secondSumFields.length > 0;
const hasSumOfTwoHalfSumOfSums = col.computed.type === "sumOfTwoHalfSumOfSums" && "firstHalfSum" in col.computed && "secondHalfSum" in col.computed && col.computed.firstHalfSum.firstSumFields.length > 0 && col.computed.firstHalfSum.secondSumFields.length > 0 && col.computed.secondHalfSum.firstSumFields.length > 0 && col.computed.secondHalfSum.secondSumFields.length > 0;
const hasSumOfMaxGroups = "groups" in col.computed && Array.isArray(col.computed.groups) && col.computed.groups.length > 0;
if (hasFields || hasNumeratorDenominator || hasMaxOfFieldAndSum || hasDifferenceOfSums || hasSumOfMaxGroups) {
if (hasFields || hasNumeratorDenominator || hasMaxOfFieldAndSum || hasDifferenceOfSums || hasHalfSumOfSums || hasSumOfTwoHalfSumOfSums || hasSumOfMaxGroups) {
field.valueCallback = getComputedValueCallback(col.computed) as (prod: unknown, dataRow: number, fieldName: string, fieldNames: string[], values: unknown[]) => number;
}
}
@@ -373,20 +376,6 @@ const TabDataGrid = ({
const columns = columnInfoItem.columns || [];
const columnLayout = columnInfoItem.columnLayout || [];
// 엑셀 import 시 매칭을 위해 헤더에 fieldName 포함 (예: "합계 (swgeProcZoneInnerSm)")
const originalHeaders: Map<string, string> = new Map();
columns.forEach((col) => {
if (col.fieldName && gridView.columnByName) {
const gridCol = gridView.columnByName(col.name);
if (gridCol && gridCol.header) {
const originalText = gridCol.header.text || '';
originalHeaders.set(col.name, originalText);
const headerWithFieldName = `${originalText} (${col.fieldName})`;
gridCol.header.text = headerWithFieldName;
}
}
});
// exportGrid 호출 전에 columnLayout 정규화 (col 속성 추가)
const normalizeLayoutForExport = (layout: ColumnLayoutItem[]): ColumnLayoutItem[] => {
return layout.map(item => {
@@ -452,15 +441,6 @@ const TabDataGrid = ({
if (excelExportVisibilitySnapshot) {
restoreGridColumnsAfterExcelExport(gridView, excelExportVisibilitySnapshot);
}
// 헤더 원복
columns.forEach((col) => {
if (col.fieldName && gridView.columnByName) {
const gridCol = gridView.columnByName(col.name);
if (gridCol && gridCol.header && originalHeaders.has(col.name)) {
gridCol.header.text = originalHeaders.get(col.name) || '';
}
}
});
// columnLayout 원복 (footerUserSpans 유지)
if (normalizedLayout && !layoutRestored) {
layoutRestored = true;
@@ -546,29 +526,12 @@ const TabDataGrid = ({
const maxHeaderRowIndex = headerRowIndexes.length > 0 ? Math.max(...headerRowIndexes) : headerStartRow;
const dataStartRow = maxHeaderRowIndex + 1;
// 엑셀 컬럼 인덱스 → 그리드 fieldName 매핑
const headerToFieldNameMap = new Map<string, string>();
const columns = columnInfoItem.columns || [];
headers.forEach((headerText, colIndex) => {
let fieldName: string | null = null;
// 헤더에 fieldName이 포함된 경우 (예: "합계 (swgeProcZoneInnerSm)")
const fieldNameMatch = headerText.match(/\(([a-zA-Z_][a-zA-Z0-9_]*)\)$/);
if (fieldNameMatch && fieldNameMatch[1]) {
const extractedFieldName = fieldNameMatch[1];
if (columns.find(c => c.fieldName === extractedFieldName)) {
fieldName = extractedFieldName;
}
}
// 매칭 실패 시 원본 헤더 텍스트 사용
if (!fieldName) {
console.warn(`엑셀 컬럼 ${colIndex + 1} (헤더: "${headerText}") 매칭 실패 → 헤더 텍스트를 fieldName으로 사용`);
fieldName = headerText;
}
headerToFieldNameMap.set(headerText, fieldName);
});
const columnLayout = columnInfoItem.columnLayout || [];
const excelColumnFieldNames = getExcelImportColumnFieldNames(
columns,
columnLayout,
);
// 데이터 행 읽기
const dataRows: Record<string, string | number>[] = [];
@@ -579,9 +542,8 @@ const TabDataGrid = ({
for (let colIndex = 0; colIndex < headers.length; colIndex++) {
const col = range.s.c + colIndex;
const cell = worksheet[XLSX.utils.encode_cell({ r: row, c: col })];
const headerName = headers[colIndex] || `Column${colIndex + 1}`;
// 헤더 텍스트를 fieldName으로 변환
const fieldName = headerToFieldNameMap.get(headerName) || headerName;
const fieldName = excelColumnFieldNames[colIndex];
if (!fieldName) continue;
let cellValue: string | number = '';
if (cell && cell.v !== undefined && cell.v !== null) {
@@ -181,6 +181,8 @@ export interface VerificationResultHistoryDialogProps {
} | null;
title?: string;
emptyMessage?: string;
/** 예외 제출 취소 API 성공 후 부모(입력 화면)에서 메인 데이터를 다시 조회할 때 사용 */
onExceptionResetSubmitted?: () => void | Promise<void>;
}
export function VerificationResultHistoryDialog({
@@ -189,6 +191,7 @@ export function VerificationResultHistoryDialog({
queryContext,
title = "검증 결과 내역",
emptyMessage = "표시할 검증 결과가 없습니다.",
onExceptionResetSubmitted,
}: VerificationResultHistoryDialogProps) {
const stableQueryContext = useMemo(() => {
if (!queryContext) return null;
@@ -459,12 +462,28 @@ export function VerificationResultHistoryDialog({
alert("예외 제출 취소 처리되었습니다.");
setCheckedVrfcByTab((prev) => ({ ...prev, [tab]: new Map() }));
await loadTabPage(tab, tabPages[tab].pageIndex);
try {
await onExceptionResetSubmitted?.();
} catch {
alert(
"예외 제출 취소는 완료되었으나 화면 데이터를 다시 불러오지 못했습니다. 조회로 새로고침 해주세요.",
);
}
} catch {
alert("예외 제출 취소 처리 중 오류가 발생했습니다.");
} finally {
setResetSubmitting(false);
}
}, [activeTab, alert, checkedVrfcByTab, entriesByTab, loadTabPage, stableQueryContext, tabPages]);
}, [
activeTab,
alert,
checkedVrfcByTab,
entriesByTab,
loadTabPage,
onExceptionResetSubmitted,
stableQueryContext,
tabPages,
]);
const renderActiveTabPanel = (tab: VerificationHistoryTabKey) => {
const entries = entriesByTab[tab];
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -210,7 +210,7 @@ const PublicSewageFacilityInp = () => {
const currentData =
st60001GridGetRowsRef.current?.() ?? rowDataMap.get('ST00060001') ?? [];
if (!treatAsNoApplicable && (!currentData || currentData.length === 0)) {
alert('제출할 데이터가 없습니다.');
alert('해당사항 없음을 체크해주세요.');
return;
}
const verificationBlocked = getVerificationPeriodActionBlockedMessage(periodCheckList);
@@ -291,15 +291,15 @@ const PublicSewageFacilityInp = () => {
const currentData =
st60001GridGetRowsRef.current?.() ?? rowDataMap.get('ST00060001') ?? [];
if (!treatAsNoApplicable && (!currentData || currentData.length === 0)) {
alert('제출할 데이터가 없습니다.');
const submitList = treatAsNoApplicable ? [] : buildFacilitySaveList(currentData);
if (!treatAsNoApplicable && submitList.length === 0) {
alert('해당사항 없음을 체크해주세요.');
return;
}
try {
showGlobalProgress({ label: '서버에 요청 중...', progress: 0 });
const userInfo = await getUserInfo();
const submitList = treatAsNoApplicable ? [] : buildFacilitySaveList(currentData);
const activeTabStatus = tabStatusInfoMap.get(activeTab) ?? null;
const statsCdForTab = tabStatsCdMap.get(activeTab) ?? activeTab;
const requestData = {
@@ -530,19 +530,19 @@ const PublicSewageFacilityInp = () => {
if (!(await confirm('저장 하시겠습니까?'))) return;
if (activeTab === 'ST00060001') {
const noApplicable = getNoApplicableForRequest();
const treatAsNoApplicable = noApplicable || isNotApplicable;
const currentData =
st60001GridGetRowsRef.current?.() ?? rowDataMap.get('ST00060001') ?? [];
const saveList = treatAsNoApplicable ? [] : buildFacilitySaveList(currentData);
if (!treatAsNoApplicable && saveList.length === 0) {
alert('해당사항 없음을 체크해주세요.');
return;
}
try {
showGlobalProgress({ label: '저장 진행 중...', progress: 0 });
const userInfo = await getUserInfo();
const noApplicable = getNoApplicableForRequest();
const treatAsNoApplicable = noApplicable || isNotApplicable;
const currentData =
st60001GridGetRowsRef.current?.() ?? rowDataMap.get('ST00060001') ?? [];
if (!treatAsNoApplicable && (!currentData || currentData.length === 0)) {
alert('저장할 데이터가 없습니다.');
return;
}
const saveList = treatAsNoApplicable ? [] : buildFacilitySaveList(currentData);
const param: PublicSewageFacilitySaveRequest = {
statsYr: lastSearchedValues.year ?? '',
ctpvNm: lastSearchedValues.sido ?? '',
@@ -1309,6 +1309,11 @@ const PublicSewageFacilityInp = () => {
open={verificationHistoryOpen}
onOpenChange={setVerificationHistoryOpen}
queryContext={verificationQueryContext}
onExceptionResetSubmitted={async () => {
const ctx = lastSearchedValues ?? searchValues;
if (!ctx || !searchParamRef.current) return;
await loadPublicSewageFacilityActiveTabData(activeTab, searchParamRef.current, ctx);
}}
/>
<SubmitDailog
open={showDailog}
File diff suppressed because it is too large Load Diff
@@ -2119,6 +2119,12 @@ const SewagePipeInp = () => {
}
: null
}
onExceptionResetSubmitted={async () => {
const values = lastSearchedValues ?? searchValues;
if (!values?.year || !values?.sido || !values?.bsnmCd) return;
const refreshedParam = buildSewagePipeSearchParam(values);
await loadSewagePipeActiveTabData(activeTab, refreshedParam, values, true, false);
}}
/>
<SubmitDailog
open={showDailog}
@@ -80,6 +80,12 @@ const SewagePipeMaintainInpTabList = [
const firstTabKey = RepairStatusInpColumn.tabKey;
const SEWAGE_PIPE_MAINTAIN_COLUMN_GUIDE_TAB_KEYS = Array.from(
new Set(
SewagePipeMaintainInpTabList.map((tab) => tab.tabKey).filter(Boolean),
),
);
const toGridColumnInfo = (tab: (typeof SewagePipeMaintainInpTabList)[number]): ColumnInfo => ({
columns: tab.columns ?? [],
columnLayout: tab.columnLayout ?? [],
@@ -302,6 +308,9 @@ const SewagePipeMaintainInp = () => {
const [rowDataMap, setRowDataMap] = useState<
Map<string, Record<string, string | number>[]>
>(new Map());
const [columnGuidesByTab, setColumnGuidesByTab] = useState<
Record<string, Record<string, string>>
>({});
const searchDoneRef = useRef(false);
const periodEnterableRef = useRef(true);
@@ -328,6 +337,37 @@ const SewagePipeMaintainInp = () => {
}
}, [activeTabId, inputFocusNonce]);
useEffect(() => {
if (activeTabId !== STATISTICS_INPUT_URL_PATH.SEWAGE_PIPE_MAINTAIN) return;
let cancelled = false;
void (async () => {
const entries = await Promise.all(
SEWAGE_PIPE_MAINTAIN_COLUMN_GUIDE_TAB_KEYS.map(async (tabKey) => {
try {
const res = await statisticsApi.getColumnGuides(tabKey);
if (res.status !== "success" || !Array.isArray(res.data)) {
return [tabKey, {}] as const;
}
const next: Record<string, string> = {};
for (const item of res.data) {
const key = item.engColNm?.trim();
const guide = item.columnGuide?.trim();
if (key && guide) next[key] = guide;
}
return [tabKey, next] as const;
} catch {
return [tabKey, {}] as const;
}
}),
);
if (cancelled) return;
setColumnGuidesByTab(Object.fromEntries(entries));
})();
return () => {
cancelled = true;
};
}, [activeTabId]);
const commitToolbarSnapshotForLoadedTab = (
tabKey: string,
statusInfo: SewerageStatusInfo | null,
@@ -951,22 +991,22 @@ const SewagePipeMaintainInp = () => {
tabStatusInfoMap.get(tabKey) ?? null,
inputPeriodEnterable,
) || isAdminInfo;
const columns = (tab.columns ?? []).map((col: ColumnConfig) =>
canEdit
? col
: {
...col,
editable: false as const,
styleName: col.styleName ?? "rg-cell-readonly",
},
);
const rawColumns = tab.columns ?? [];
const base = canEdit
? rawColumns
: rawColumns.map((col) => ({ ...col, editable: false as const }));
const columns = base
.map((col) => {
const guide = columnGuidesByTab[tabKey]?.[col.fieldName]?.trim();
return guide ? { ...col, tooltip: guide } : col;
});
map.set(tabKey, {
columns,
columnLayout: tab.columnLayout ?? [],
});
}
return map;
}, [tabStatusInfoMap, inputPeriodEnterable, isAdminInfo]);
}, [tabStatusInfoMap, inputPeriodEnterable, isAdminInfo, columnGuidesByTab]);
const searchContextForToolbar = lastSearchedValues ?? searchValues;
const activeTabStatus = tabStatusInfoMap.get(activeTab) ?? null;
@@ -1120,7 +1160,7 @@ const SewagePipeMaintainInp = () => {
return (
<TabsContent key={tabKey} value={tabKey}>
<DataGrid
key={tabKey}
key={`${tabKey}-${tabEnterableForGrid ? "enterable" : "readonly"}`}
columnInfo={
columnInfoByTab.get(tabKey) ??
toGridColumnInfo(info)
@@ -1163,6 +1203,17 @@ const SewagePipeMaintainInp = () => {
open={verificationHistoryOpen}
onOpenChange={setVerificationHistoryOpen}
queryContext={verificationQueryContext}
onExceptionResetSubmitted={async () => {
const values = lastSearchedValues ?? searchValues;
if (!values || !searchParamRef.current) return;
await loadActiveTabData(
activeTabRef.current,
searchParamRef.current,
values,
true,
false,
);
}}
/>
<ConfirmDialog isOpen={false} />
<SubmitDailog
@@ -1176,6 +1227,17 @@ const SewagePipeMaintainInp = () => {
open={verificationResultDialogOpen}
onOpenChange={setVerificationResultDialogOpen}
queryContext={verificationQueryContext}
onExceptionSubmitted={async () => {
const values = lastSearchedValues ?? searchValues;
if (!values || !searchParamRef.current) return;
await loadActiveTabData(
activeTabRef.current,
searchParamRef.current,
values,
true,
false,
);
}}
/>
</>
);
@@ -1,5 +1,6 @@
import {
useCallback,
useEffect,
useMemo,
useRef,
useState,
@@ -25,8 +26,11 @@ import SaveButton from "@/components/button/SaveButton";
import SubmitButton from "@/components/button/SumitButton";
import VerificationResultButton from "@/components/button/VerificationResultButton";
import DataGrid, { applyDropDownColumnToGrid } from "@/components/module/DataGrid";
import { openVerificationResultDialogIfNeeded } from "@/components/module/post-submit-verification-results";
import SearchForm from "@/components/module/SearchForm";
import { VerificationResultDialog } from "@/components/module/VerificationResultDialog";
import { VerificationResultHistoryDialog } from "@/components/module/VerificationResultHistoryDialog";
import SubmitDailog from "./SubmitDailog";
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import type {
CellStyleCallbackParams,
@@ -45,22 +49,27 @@ import {
toCompactYmd,
} from "@/components/util/grid/ymdFormat";
import { statisticsInputSewageReservoirPumpApi } from "@/entities/statistics/api/input/statistics-input-sewage-reservoir-pump-service";
import { statisticsInputSewSupplyRateApi } from "@/entities/statistics/api/input/statistics-input-sew-supply-rate-service";
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
import { isInputPeriodCurrent } from "@/entities/statistics/model/input/statistics-sewage-supply-rate-input-interface";
import {
SEWAGE_RESERVOIR_STATS_CD,
type SewageReservoirItem,
type SewageReservoirRequestParams,
type SewageReservoirSaveItem,
type RainfallPumpStationItem,
type RainfallPumpStationSaveItem,
type SewageReservoirVerificationResult,
} from "@/entities/statistics/model/input/statistics-input-sewage-reservoir-pump-interface";
import type { StatisticsInputSubmitInfoFormData } from "@/entities/statistics/model/statistics-input-submit-info-schema";
import type {
ApiResponse,
cancelSubmitRequest,
ImrStatusResponse,
Sgg,
SewerageStatusInfo,
} from "@/entities/statistics/model/statistics-interface";
import { useAlertStore, useStatisticsInputToolbarGroupGate } from "@/shared/lib";
import { useAlertStore, useGlobalProgressStore, useStatisticsInputToolbarGroupGate } from "@/shared/lib";
import { ConfirmDialog } from "@/shared/ui/confirm-dialog";
import { useAdminStore } from "@/store/admin-store";
@@ -80,6 +89,12 @@ const firstTabKey = SewageReservoirPumpInpTabList[0]?.tabKey ?? SEWAGE_RESERVOIR
const rainfallPumpTabKey =
RainfallPumpStationInpColumn.tabKey ?? "rainfallPumpStationInp";
const SEWAGE_RESERVOIR_PUMP_COLUMN_GUIDE_TAB_KEYS = Array.from(
new Set(
SewageReservoirPumpInpTabList.map((tab) => tab.tabKey).filter(Boolean),
),
);
type GridRowWithVerification = Record<string, string | number> & {
verificationResult?: SewageReservoirVerificationResult;
};
@@ -210,11 +225,7 @@ function buildSggNmColumn(
sggOptions: Sgg[],
): ColumnConfig {
if (!canEdit) {
return {
...col,
editable: false as const,
styleName: col.styleName ?? "rg-cell-readonly",
};
return { ...col, editable: false as const };
}
if (sggOptions.length === 0) {
return { ...col, editable: true };
@@ -272,31 +283,7 @@ function toGridNumber(value: string | number | null | undefined): number {
return Number.isFinite(n) ? n : 0;
}
/** API 조회 항목 → 저장/삭제 API 형식 (조회 직후 스냅샷용) */
function buildSewageReservoirSaveItemFromApiItem(
item: SewageReservoirItem,
): SewageReservoirSaveItem {
return buildSewageReservoirSaveItem(
item as unknown as Record<string, string | number>,
);
}
function buildSewageReservoirDeleteList(
loadedSnapshot: SewageReservoirSaveItem[],
currentRows: Record<string, string | number>[],
): SewageReservoirSaveItem[] {
const currentFcltSnSet = new Set(
currentRows
.map((row) => String(row.fcltSn ?? "").trim())
.filter((sn) => sn !== ""),
);
return loadedSnapshot.filter((item) => {
const sn = String(item.fcltSn ?? "").trim();
return sn !== "" && !currentFcltSnSet.has(sn);
});
}
/** 시군구명(`sggNm`) 기준 행정구역 코드·명 */
/** 시군구명(`sggNm`) 기준 `admdstCd`(bsnmCd)·`admdstNm`(시군구명) */
function resolveSewageReservoirAdmdst(
sggNm: string,
sggOptions: Sgg[],
@@ -304,7 +291,7 @@ function resolveSewageReservoirAdmdst(
const trimmed = sggNm.trim();
const matched = sggOptions.find((s) => s.gugun === trimmed);
if (matched) {
return { admdstCd: matched.adstDstrCd, admdstNm: matched.gugun };
return { admdstCd: matched.bsnmCd, admdstNm: matched.gugun };
}
return { admdstCd: "", admdstNm: trimmed };
}
@@ -318,10 +305,12 @@ function buildSewageReservoirSaveItem(
String(row.sggNm ?? ""),
sggOptions,
);
const fcltSn = String(row.fcltSn ?? "").trim();
return {
admdstCd,
admdstNm,
fcltSn: String(row.fcltSn ?? ""),
// 항목 추가 직후 저장(신규) — 그리드 fcltSn 없음 → API에는 "1"
fcltSn: fcltSn,
fcltNm: String(row.fcltNm ?? ""),
fcltPstnCn: String(row.fcltPstnCn ?? ""),
oprtngStrtYmd: toCompactYmd(row.oprtngStrtYmd),
@@ -345,9 +334,9 @@ function buildSewageReservoirSaveItem(
pumpFcltN3PerPpqty: toGridNumber(row.pumpFcltN3PerPpqty),
pumpFcltN3PumpCnt: toGridNumber(row.pumpFcltN3PumpCnt),
pumpFcltN3RsrvPumpCnt: toGridNumber(row.pumpFcltN3RsrvPumpCnt),
prcsgFcltYn: String(row.prcsgFcltYn ?? ""),
ddrztnEqpmYn: String(row.ddrztnEqpmYn ?? ""),
oslfEtgtYn: String(row.oslfEtgtYn ?? ""),
prcsgFcltYn: String(row.prcsgFcltYn ? (row.prcsgFcltYn === '무' ? 'N' : 'Y') : ""),
ddrztnEqpmYn: String(row.ddrztnEqpmYn ? (row.prcsgFcltYn === '무' ? 'N' : 'Y') : ""),
oslfEtgtYn: String(row.oslfEtgtYn ? (row.prcsgFcltYn === '무' ? 'N' : 'Y') : ""),
mltvMthCn: nullableTrimmedString(row.mltvMthCn),
impdMthCn: nullableTrimmedString(row.impdMthCn),
impdPrcsMthdCn: nullableTrimmedString(row.impdPrcsMthdCn),
@@ -362,6 +351,56 @@ function buildSewageReservoirSaveItem(
};
}
/** 그리드 행 → ST00040002 저장/삭제 API 항목 */
function buildRainfallPumpStationSaveItem(
row: Record<string, string | number>,
sggOptions: Sgg[] = [],
): RainfallPumpStationSaveItem {
const { admdstCd, admdstNm } = resolveSewageReservoirAdmdst(
String(row.sggNm ?? ""),
sggOptions,
);
const fcltSn = String(row.fcltSn ?? "").trim();
return {
admdstCd,
admdstNm,
fcltSn: fcltSn,
fcltNm: String(row.fcltNm ?? ""),
fcltPstnCn: String(row.fcltPstnCn ?? ""),
fcltyKndCn: String(row.fcltyKndCn ?? ""),
oprtngStrtYmd: toCompactYmd(row.oprtngStrtYmd),
planDesignFreqCnt: toGridNumber(row.planDesignFreqCnt),
wtrStrgZoneArea: toGridNumber(row.wtrStrgZoneArea),
rsvrYn: String(row.rsvrYn ? (row.rsvrYn === "무" ? "N" : "Y") : ""),
rsvrImpndQnt: toGridNumber(row.rsvrImpndQnt),
abstnRgnImpdQnt: toGridNumber(row.abstnRgnImpdQnt),
grssPpqty: toGridNumber(row.grssPpqty),
pumpFcltN1DmtrExpln: String(row.pumpFcltN1DmtrExpln ?? ""),
pumpFcltN1PerPpqty: toGridNumber(row.pumpFcltN1PerPpqty),
pumpFcltN1PumpCnt: toGridNumber(row.pumpFcltN1PumpCnt),
pumpFcltN1RsrvPumpCnt: toGridNumber(row.pumpFcltN1RsrvPumpCnt),
pumpFcltN2DmtrExpln: String(row.pumpFcltN2DmtrExpln ?? ""),
pumpFcltN2PerPpqty: toGridNumber(row.pumpFcltN2PerPpqty),
pumpFcltN2PumpCnt: toGridNumber(row.pumpFcltN2PumpCnt),
pumpFcltN2RsrvPumpCnt: toGridNumber(row.pumpFcltN2RsrvPumpCnt),
pumpFcltN3DmtrExpln: String(row.pumpFcltN3DmtrExpln ?? ""),
pumpFcltN3PerPpqty: toGridNumber(row.pumpFcltN3PerPpqty),
pumpFcltN3PumpCnt: toGridNumber(row.pumpFcltN3PumpCnt),
pumpFcltN3RsrvPumpCnt: toGridNumber(row.pumpFcltN3RsrvPumpCnt),
prcsgFcltYn: String(row.prcsgFcltYn ? (row.prcsgFcltYn === "무" ? "N" : "Y") : ""),
ddrztnEqpmYn: String(row.ddrztnEqpmYn ? (row.prcsgFcltYn === "무" ? "N" : "Y") : ""),
oslfEtgtYn: String(row.oslfEtgtYn ? (row.prcsgFcltYn === "무" ? "N" : "Y") : ""),
dschgWtrsExpln: String(row.dschgWtrsExpln ?? ""),
prcptnLastDspsQnt: toGridNumber(row.prcptnLastDspsQnt),
ctmnLastDspsQnt: toGridNumber(row.ctmnLastDspsQnt),
prcptnLastDspsMthdExpln: String(row.prcptnLastDspsMthdExpln ?? ""),
ctmnLastDspsMthdExpln: String(row.ctmnLastDspsMthdExpln ?? ""),
oprtngDayCnt: toGridNumber(row.oprtngDayCnt),
grssMltvQnt: toGridNumber(row.grssMltvQnt),
rmrk: nullableTrimmedString(row.rmrk),
};
}
function mapRainfallPumpStationListToGridRows(
list: RainfallPumpStationItem[],
ctpvNm: string,
@@ -386,20 +425,72 @@ function mapRainfallPumpStationListToGridRows(
});
}
const sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));
async function waitForImrJobCompleted(
jobId: string,
getStatus: (id: string) => Promise<ImrStatusResponse>,
options?: {
pollIntervalMs?: number;
maxAttempts?: number;
onProgress?: (jobProgress: number) => void;
},
): Promise<void> {
const pollIntervalMs = options?.pollIntervalMs ?? 2000;
const maxAttempts = options?.maxAttempts ?? 150;
for (let i = 0; i < maxAttempts; i++) {
const res = await getStatus(jobId);
options?.onProgress?.(Math.max(0, Math.min(100, Number(res.job_prog) || 0)));
if (res.status === "COMPLETED" && Number(res.job_prog) === 100) return;
if (res.status === "FAILED" || res.status === "ERROR" || res.status === "CANCELLED") {
throw new Error(res.error_msg || `IMR 검증이 ${res.status} 상태로 종료되었습니다.`);
}
await sleep(pollIntervalMs);
}
throw new Error("IMR 검증 완료 대기 시간이 초과되었습니다.");
}
// 하수도 통계 관리 > 04. 하수저류 시설 및 빗물 펌프장
const SewageReservoirPumpInp = () => {
const { setMessage: alert } = useAlertStore((state) => state);
const { confirm } = useConfirm();
const { isAdminInfo } = useAdminStore();
const toolbarGroupAllowed = useStatisticsInputToolbarGroupGate();
const showGlobalProgress = useGlobalProgressStore((state) => state.show);
const setGlobalProgress = useGlobalProgressStore((state) => state.setProgress);
const setGlobalProgressLabel = useGlobalProgressStore((state) => state.setLabel);
const hideGlobalProgress = useGlobalProgressStore((state) => state.hide);
const submitFunc = () => {
console.log("submit");
void (async () => {
const tabKey = activeTabRef.current;
if (
tabKey !== SEWAGE_RESERVOIR_STATS_CD &&
tabKey !== rainfallPumpTabKey
) {
alert("제출할 수 없는 탭입니다.");
return;
}
if (!lastSearchedValues) {
alert("먼저 조회를 진행해주세요.");
return;
}
if (!buildGugunOkForToolbar(lastSearchedValues)) {
alert("시군구를 선택해주세요.");
return;
}
const noApplicable = isNotApplicableByTab.get(tabKey) ?? false;
const currentData = getCurrentRowsForTab(tabKey);
if (!noApplicable && currentData.length === 0) {
alert("해당사항 없음을 체크해주세요.");
return;
}
if (await confirm("제출 하시겠습니까?")) {
setShowSubmitDialog(true);
}
})();
};
/** 마지막 조회 시 서버에 있던 하수저류시설 목록 (deleteList 비교 기준) */
const loadedSewageReservoirSnapshotRef = useRef<SewageReservoirSaveItem[]>([]);
const cnacelSubmitFunc = () => {
void (async () => {
try {
@@ -530,24 +621,35 @@ const SewageReservoirPumpInp = () => {
.filter((idx) => idx >= 0),
);
const removedRows = currentRows.filter((_, idx) =>
checkedIndexSet.has(idx),
);
const nextRows = currentRows.filter((_, idx) => !checkedIndexSet.has(idx));
if (nextRows.length === currentRows.length) {
if (removedRows.length === 0) {
alert("삭제할 항목을 선택해주세요.");
return;
}
if (tabKey === SEWAGE_RESERVOIR_STATS_CD) {
setSewageReservoirDeleteList((prev) => [...prev, ...removedRows]);
} else if (tabKey === rainfallPumpTabKey) {
setRainfallPumpStationDeleteList((prev) => [...prev, ...removedRows]);
}
setRowDataMap((prev) => {
const next = new Map(prev);
next.set(tabKey, nextRows);
return next;
});
};
const saveFunc = () => {
void (async () => {
const tabKey = activeTabRef.current;
if (tabKey !== SEWAGE_RESERVOIR_STATS_CD) {
if (
tabKey !== SEWAGE_RESERVOIR_STATS_CD &&
tabKey !== rainfallPumpTabKey
) {
alert("저장할 수 없는 탭입니다.");
return;
}
@@ -564,41 +666,63 @@ const SewageReservoirPumpInp = () => {
commitGridForTab(tabKey);
const noApplicable = isNotApplicableByTab.get(tabKey) ?? false;
const currentData = getCurrentRowsForTab(tabKey);
const isReservoirTab = tabKey === SEWAGE_RESERVOIR_STATS_CD;
const saveList = noApplicable
? []
: currentData.map((row) =>
buildSewageReservoirSaveItem(row, sggOptions),
);
const deleteList = buildSewageReservoirDeleteList(
loadedSewageReservoirSnapshotRef.current,
currentData,
);
if (!noApplicable && saveList.length === 0 && deleteList.length === 0) {
alert("저장할 변경 사항이 없습니다.");
: isReservoirTab
? currentData.map((row) =>
buildSewageReservoirSaveItem(row, sggOptions),
)
: currentData.map((row) =>
buildRainfallPumpStationSaveItem(row, sggOptions),
);
const deleteList = isReservoirTab
? sewageReservoirDeleteList
.filter((row) => String(row.fcltSn ?? "").trim() !== "")
.map((row) => buildSewageReservoirSaveItem(row, sggOptions))
: rainfallPumpStationDeleteList
.filter((row) => String(row.fcltSn ?? "").trim() !== "")
.map((row) => buildRainfallPumpStationSaveItem(row, sggOptions));
if (!noApplicable && saveList.length === 0) {
alert("해당사항 없음을 체크해주세요.");
return;
}
const requestBase = {
statsYr: lastSearchedValues.year ?? "",
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...(isAdminInfo ? { superAdmin: "superAdmin" } : {}),
...(noApplicable ? { noApplicable: true } : {}),
};
try {
setIsLoading(true);
const userInfo = await getUserInfo();
const response =
await statisticsInputSewageReservoirPumpApi.saveSewageReservoir({
statsYr: lastSearchedValues.year ?? "",
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
const response = isReservoirTab
? await statisticsInputSewageReservoirPumpApi.saveSewageReservoir({
...requestBase,
userId: userInfo.userUnqId,
...(isAdminInfo ? { superAdmin: "superAdmin" } : {}),
...(noApplicable ? { noApplicable: true } : {}),
saveList,
deleteList,
saveList: saveList as SewageReservoirSaveItem[],
deleteList: deleteList as SewageReservoirSaveItem[],
})
: await statisticsInputSewageReservoirPumpApi.saveRainfallPumpStation({
...requestBase,
userId: userInfo.userUnqId,
saveList: saveList as RainfallPumpStationSaveItem[],
deleteList: deleteList as RainfallPumpStationSaveItem[],
});
if (response.status === "success") {
if (isReservoirTab) {
setSewageReservoirDeleteList([]);
} else {
setRainfallPumpStationDeleteList([]);
}
await loadActiveTabData(tabKey, lastSearchedValues, false, false);
alert("저장되었습니다.");
} else {
@@ -612,6 +736,180 @@ const SewageReservoirPumpInp = () => {
})();
};
const executeSubmit = async (submitInfo: StatisticsInputSubmitInfoFormData) => {
const tabKey = activeTabRef.current;
if (
tabKey !== SEWAGE_RESERVOIR_STATS_CD &&
tabKey !== rainfallPumpTabKey
) {
alert("제출할 수 없는 탭입니다.");
return;
}
if (!lastSearchedValues) {
alert("먼저 조회를 진행해주세요.");
return;
}
if (!buildGugunOkForToolbar(lastSearchedValues)) {
alert("시군구를 선택해주세요.");
return;
}
commitGridForTab(tabKey);
const noApplicable = isNotApplicableByTab.get(tabKey) ?? false;
const currentData = getCurrentRowsForTab(tabKey);
const isReservoirTab = tabKey === SEWAGE_RESERVOIR_STATS_CD;
const submitList = noApplicable
? []
: isReservoirTab
? currentData.map((row) => buildSewageReservoirSaveItem(row, sggOptions))
: currentData.map((row) => buildRainfallPumpStationSaveItem(row, sggOptions));
const deleteList = isReservoirTab
? sewageReservoirDeleteList
.filter((row) => String(row.fcltSn ?? "").trim() !== "")
.map((row) => buildSewageReservoirSaveItem(row, sggOptions))
: rainfallPumpStationDeleteList
.filter((row) => String(row.fcltSn ?? "").trim() !== "")
.map((row) => buildRainfallPumpStationSaveItem(row, sggOptions));
const activeTabStatus = tabStatusInfoMap.get(tabKey) ?? null;
const statsCdForTab = tabStatsCdMap.get(tabKey) ?? tabKey;
const verificationQuery = {
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
statsYr: lastSearchedValues.year ?? "",
statsCd: statsCdForTab,
};
const reloadSubmittedTab = async () => {
if (isReservoirTab) {
setSewageReservoirDeleteList([]);
} else {
setRainfallPumpStationDeleteList([]);
}
await loadActiveTabData(tabKey, lastSearchedValues, false, false);
};
if (!noApplicable && submitList.length === 0) {
alert("해당사항 없음을 체크해주세요.");
return;
}
const submitRequestBase = {
statsYr: lastSearchedValues.year ?? "",
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
...(noApplicable ? { noApplicable: true } : {}),
laePicNm: submitInfo.laePicNm,
laeKbrdrJbgdNm: submitInfo.laeKbrdrJbgdNm,
laeKbrdrDeptNm: submitInfo.laeKbrdrDeptNm,
laePicTelNo: submitInfo.laePicTelNo,
laeIdfrNm: submitInfo.laeIdfrNm,
laeIdfrJbgdNm: submitInfo.laeIdfrJbgdNm,
laeIdfrDeptNm: submitInfo.laeIdfrDeptNm,
laeIdfrTelNo: submitInfo.laeIdfrTelNo,
};
try {
showGlobalProgress({ label: "서버에 요청 중...", progress: 0 });
const userInfo = await getUserInfo();
setGlobalProgressLabel("일반 검증 진행 중...");
const response = isReservoirTab
? await statisticsInputSewageReservoirPumpApi.submitSewageReservoir({
...submitRequestBase,
userId: userInfo.userUnqId,
submitList: submitList as SewageReservoirSaveItem[],
deleteList: deleteList as SewageReservoirSaveItem[],
})
: await statisticsInputSewageReservoirPumpApi.submitRainfallPumpStation({
...submitRequestBase,
userId: userInfo.userUnqId,
submitList: submitList as RainfallPumpStationSaveItem[],
deleteList: deleteList as RainfallPumpStationSaveItem[],
});
if (response.status !== "success") {
alert(response.message ?? "데이터 제출 중 오류가 발생했습니다.");
return;
}
const finalStatus = response.data?.finalStatus;
const nvpassed = response.data?.nvpassed;
if (finalStatus === "5") {
setGlobalProgress(50);
setGlobalProgressLabel("IMR 검증 진행 중...");
const imrParam = {
user_id: userInfo.userUnqId,
sewer_bzmn_cd: lastSearchedValues.bsnmCd ?? "",
stats_cd: statsCdForTab,
stats_yr: lastSearchedValues.year ?? "",
lae_sbmsn_stts_cd:
activeTabStatus?.laeSbmsnSttsCd === "4" ? "4" : "2",
gen_verify_stts_cd: nvpassed ? "1" : "2",
};
try {
const imrResponse = await statisticsApi.callImrService(imrParam);
if (imrResponse.job_id) {
await waitForImrJobCompleted(
imrResponse.job_id,
(id) => statisticsApi.getImrStatus(id),
{
onProgress: (jobProgress) => {
setGlobalProgress(50 + Math.round(jobProgress / 2));
},
},
);
}
setGlobalProgress(100);
await reloadSubmittedTab();
await openVerificationResultDialogIfNeeded(
verificationQuery,
setVerificationResultDialogOpen,
{ notify: alert },
);
} catch (e) {
await reloadSubmittedTab();
await openVerificationResultDialogIfNeeded(
verificationQuery,
setVerificationResultDialogOpen,
{ notify: alert, notifySubmitComplete: false },
);
const msg =
e instanceof Error && e.message
? e.message
: "IMR 검증 중 오류가 발생했습니다.\n관리자에게 문의하세요.";
alert(msg);
}
} else {
setGlobalProgress(100);
await reloadSubmittedTab();
await openVerificationResultDialogIfNeeded(
verificationQuery,
setVerificationResultDialogOpen,
{ notify: alert },
);
}
} catch {
alert("데이터 제출 중 오류가 발생했습니다.");
} finally {
hideGlobalProgress();
}
};
const [sewageReservoirDeleteList, setSewageReservoirDeleteList] = useState<
Record<string, string | number>[]
>([]);
const [rainfallPumpStationDeleteList, setRainfallPumpStationDeleteList] =
useState<Record<string, string | number>[]>([]);
const [activeTab, setActiveTab] = useState(firstTabKey);
const [rowDataMap, setRowDataMap] = useState<
Map<string, Record<string, string | number>[]>
@@ -634,7 +932,13 @@ const SewageReservoirPumpInp = () => {
>(new Map());
const [tabStatsCdMap, setTabStatsCdMap] = useState<Map<string, string>>(new Map());
const [verificationHistoryOpen, setVerificationHistoryOpen] = useState(false);
const [verificationResultDialogOpen, setVerificationResultDialogOpen] =
useState(false);
const [showSubmitDialog, setShowSubmitDialog] = useState(false);
const [sggOptions, setSggOptions] = useState<Sgg[]>([]);
const [columnGuidesByTab, setColumnGuidesByTab] = useState<
Record<string, Record<string, string>>
>({});
const reservoirGridRef = useRef<RealGridReact | null>(null);
const rainfallPumpGridRef = useRef<RealGridReact | null>(null);
const reservoirGridGetRowsRef =
@@ -643,6 +947,36 @@ const SewageReservoirPumpInp = () => {
useRef<(() => Record<string, string | number>[]) | null>(null);
const sggOptionsSidoRef = useRef<string | null>(null);
useEffect(() => {
let cancelled = false;
void (async () => {
const entries = await Promise.all(
SEWAGE_RESERVOIR_PUMP_COLUMN_GUIDE_TAB_KEYS.map(async (tabKey) => {
try {
const res = await statisticsApi.getColumnGuides(tabKey);
if (res.status !== "success" || !Array.isArray(res.data)) {
return [tabKey, {}] as const;
}
const next: Record<string, string> = {};
for (const item of res.data) {
const key = item.engColNm?.trim();
const guide = item.columnGuide?.trim();
if (key && guide) next[key] = guide;
}
return [tabKey, next] as const;
} catch {
return [tabKey, {}] as const;
}
}),
);
if (cancelled) return;
setColumnGuidesByTab(Object.fromEntries(entries));
})();
return () => {
cancelled = true;
};
}, []);
const searchDoneRef = useRef(false);
const periodEnterableRef = useRef(true);
const activeTabRef = useRef(activeTab);
@@ -732,9 +1066,7 @@ const SewageReservoirPumpInp = () => {
const { commonInfo, statusInfo, sewageReservoirList } = response.data;
const list = sewageReservoirList ?? [];
loadedSewageReservoirSnapshotRef.current = list
.filter((item) => String(item.fcltSn ?? "").trim() !== "")
.map(buildSewageReservoirSaveItemFromApiItem);
setSewageReservoirDeleteList([]);
const gridRows = mapSewageReservoirListToGridRows(
list,
commonInfo.ctpvNm,
@@ -771,6 +1103,7 @@ const SewageReservoirPumpInp = () => {
const { commonInfo, statusInfo, rainfallPumpStationList } = response.data;
const list = rainfallPumpStationList ?? [];
setRainfallPumpStationDeleteList([]);
const gridRows = mapRainfallPumpStationListToGridRows(
list,
commonInfo.ctpvNm,
@@ -837,7 +1170,8 @@ const SewageReservoirPumpInp = () => {
}, []);
const handleReset = () => {
loadedSewageReservoirSnapshotRef.current = [];
setSewageReservoirDeleteList([]);
setRainfallPumpStationDeleteList([]);
setRowDataMap(new Map());
setTabStatusInfoMap(new Map());
setTabStatsCdMap(new Map());
@@ -860,6 +1194,25 @@ const SewageReservoirPumpInp = () => {
return;
}
try {
const periodResponse = await statisticsInputSewSupplyRateApi.checkInputPeriod(
values.year || "",
);
if (periodResponse.status !== "success") {
alert(periodResponse.message);
return;
}
const periods = Array.isArray(periodResponse.data)
? periodResponse.data
: [];
const periodOk = isInputPeriodCurrent(periods);
periodEnterableRef.current = periodOk;
setInputPeriodEnterable(periodOk);
} catch {
alert("입력 기간 확인 중 오류가 발생했습니다.");
return;
}
const yearChangedFromLastSearch =
lastSearchedValues != null &&
(lastSearchedValues.year ?? "") !== (values.year ?? "");
@@ -957,12 +1310,13 @@ const SewageReservoirPumpInp = () => {
baseShowEnterableToolbarButtons && toolbarGroupAllowed;
const showActionToolbarCard =
baseShowActionToolbarCard && toolbarGroupAllowed;
// 항목추가/제거 버튼은 "데이터가 있든 없든", "과거 데이터(입력 불가 상태)여도"
// 조회를 하고나면 항상 보여야 합니다.
const isReservoirOrRainfallTab =
activeTab === SEWAGE_RESERVOIR_STATS_CD || activeTab === rainfallPumpTabKey;
const showArticleToolbarCard =
hasSearched && isReservoirOrRainfallTab && toolbarGroupAllowed;
hasSearched &&
isReservoirOrRainfallTab &&
toolbarGroupAllowed &&
(showSaveInToolbar || showEnterableToolbarButtons);
const isNotApplicableForActiveTab = isNotApplicableByTab.get(activeTab) ?? false;
const showNotApplicableForActiveTab =
loadedTabKeysRef.current.has(activeTab) &&
@@ -1012,20 +1366,6 @@ const SewageReservoirPumpInp = () => {
const sggNm =
ctx.gugun && ctx.gugun !== "ALL" ? ctx.gugun : "";
if (isRainfallPumpTab) {
const newRow = createEmptyRainfallPumpStationRow(ctx.sido, sggNm);
setRowDataMap((prev) => {
const next = new Map(prev);
const current = next.get(rainfallPumpTabKey) ?? [];
next.set(rainfallPumpTabKey, [...current, newRow]);
return next;
});
const statusInfo = tabStatusInfoMap.get(rainfallPumpTabKey) ?? null;
commitToolbarSnapshotForLoadedTab(rainfallPumpTabKey, statusInfo, true, ctx);
return;
}
let list = sggOptions;
if (sggOptionsSidoRef.current !== ctx.sido || list.length === 0) {
list = await loadSggOptions(ctx.sido);
@@ -1036,24 +1376,23 @@ const SewageReservoirPumpInp = () => {
return;
}
const tabKey = SEWAGE_RESERVOIR_STATS_CD;
const labels = list.map((s) => s.gugun).filter(Boolean);
const newRow = createEmptySewageReservoirRow(ctx.sido, sggNm);
const tabKey = activeTabKey;
const newRow = isReservoirTab
? createEmptySewageReservoirRow(ctx.sido, sggNm)
: createEmptyRainfallPumpStationRow(ctx.sido, sggNm);
commitGridForTab(tabKey);
const currentRows = getCurrentRowsForTab(tabKey);
flushSync(() => {
setSggOptions(list);
setRowDataMap((prev) => {
const next = new Map(prev);
const current = next.get(tabKey) ?? [];
next.set(tabKey, [...current, newRow]);
next.set(tabKey, [...currentRows, newRow]);
return next;
});
});
// 드롭다운 컬럼은 columnInfoByTab(buildSggNmColumn) 렌더링으로 반영한다.
// 행 추가 직후 setColumnProperty를 강제 재적용하면 타이밍 이슈로
// `fieldIndex is out of bounds`가 발생할 수 있어 제거한다.
const statusInfo = tabStatusInfoMap.get(tabKey) ?? null;
commitToolbarSnapshotForLoadedTab(tabKey, statusInfo, true, ctx);
};
@@ -1068,24 +1407,29 @@ const SewageReservoirPumpInp = () => {
inputPeriodEnterable,
) || isAdminInfo;
const isReservoirTab = tabKey === SEWAGE_RESERVOIR_STATS_CD;
const columns = (tab.columns ?? []).map((col: ColumnConfig) => {
if (isReservoirTab && col.fieldName === "ctpvNm") {
return {
...col,
editable: false as const,
styleName: col.styleName ?? "rg-cell-readonly",
};
const isRainfallPumpTab = tabKey === rainfallPumpTabKey;
const rawColumns = tab.columns ?? [];
const base = canEdit
? rawColumns
: rawColumns.map((col) => ({ ...col, editable: false as const }));
const columns = base.map((col: ColumnConfig) => {
if (
(isReservoirTab || isRainfallPumpTab) &&
col.fieldName === "ctpvNm"
) {
return { ...col, editable: false as const };
}
if (isReservoirTab && col.fieldName === "sggNm") {
return buildSggNmColumn(col, canEdit, sggOptions);
if (
(isReservoirTab || isRainfallPumpTab) &&
col.fieldName === "sggNm" &&
canEdit
) {
return buildSggNmColumn(col, true, sggOptions);
}
return canEdit
? col
: {
...col,
editable: false as const,
styleName: col.styleName ?? "rg-cell-readonly",
};
return col;
}).map((col) => {
const guide = columnGuidesByTab[tabKey]?.[col.fieldName]?.trim();
return guide ? { ...col, tooltip: guide } : col;
});
map.set(tabKey, {
columns,
@@ -1093,7 +1437,7 @@ const SewageReservoirPumpInp = () => {
});
}
return map;
}, [tabStatusInfoMap, inputPeriodEnterable, isAdminInfo, sggOptions]);
}, [tabStatusInfoMap, inputPeriodEnterable, isAdminInfo, sggOptions, columnGuidesByTab]);
const verificationCellStyle = ({
fieldName,
@@ -1220,7 +1564,7 @@ const SewageReservoirPumpInp = () => {
return (
<TabsContent key={tabKey} value={tabKey}>
<DataGrid
key={tabKey}
key={`${tabKey}-${tabEnterableForGrid ? "enterable" : "readonly"}`}
getGridRef={
isReservoirTab
? reservoirGridRef
@@ -1265,7 +1609,7 @@ const SewageReservoirPumpInp = () => {
showHeaderCheckbox
statusInfo={tabStatusInfo}
isExcelImport={
tabRowDataExists(rowDataMap, tabKey) &&
loadedTabKeysRef.current.has(tabKey) &&
tabEnterableForGrid &&
gugunOk
}
@@ -1281,6 +1625,28 @@ const SewageReservoirPumpInp = () => {
open={verificationHistoryOpen}
onOpenChange={setVerificationHistoryOpen}
queryContext={verificationQueryContext}
onExceptionResetSubmitted={async () => {
const values = lastSearchedValues ?? searchValues;
if (!values) return;
await loadActiveTabData(activeTabRef.current, values, false, false);
}}
/>
<SubmitDailog
open={showSubmitDialog}
onOpenChange={setShowSubmitDialog}
statusInfo={activeTabStatus}
onConfirm={executeSubmit}
isLoading={isLoading}
/>
<VerificationResultDialog
open={verificationResultDialogOpen}
onOpenChange={setVerificationResultDialogOpen}
queryContext={verificationQueryContext}
onExceptionSubmitted={async () => {
const values = lastSearchedValues ?? searchValues;
if (!values) return;
await loadActiveTabData(activeTabRef.current, values, false, false);
}}
/>
<ConfirmDialog isOpen={false} />
</>
@@ -1,34 +1,539 @@
import { useState } from "react";
import { useEffect, useMemo, useRef, useState } from "react";
import SearchForm from "@/components/module/SearchForm";
import TabDataGrid from "@/components/module/TabDataGrid";
import SubmitButton from "@/components/button/SumitButton";
import SaveButton from "@/components/button/SaveButton";
import SubmitExceptionButton from "@/components/button/SubmitExceptionButton";
import { useConfirm } from "@frontend-opensource/use-react-hooks";
import { useAlertStore } from "@/shared/lib";
import { statisticsInputSewSupplyRateApi } from "@/entities/statistics/api/input/statistics-input-sew-supply-rate-service";
import { statisticsInputSewerageFinanceApi } from "@/entities/statistics/api/input/statistics-input-sewerage-finance-service";
import { statisticsApi } from "@/entities/statistics/api/statistics-service";
import type {
CauserBurdenItem,
SewerageChargeItem,
SewerageFinanceRequestParams,
SewerageFinanceSettleItem,
SewerageFinanceVerificationResult,
SewerageUsageFeeItem,
} from "@/entities/statistics/model/input/statistics-input-sewerage-finance-interface";
import { isInputPeriodCurrent } from "@/entities/statistics/model/input/statistics-sewage-supply-rate-input-interface";
import type { cancelSubmitRequest, SewerageStatusInfo } from "@/entities/statistics/model/statistics-interface";
import { LoadingWithAnimation, Tabs, TabsContent, TabsList, TabsTrigger } from "@common/components";
import { getUserInfo } from "../../../../../../packages/common/components/src/api";
import CancelSubmitButton from "@/components/button/CancelSubmitButton";
import LastYearDataButton from "@/components/button/LastYearDataButton";
import SaveButton from "@/components/button/SaveButton";
import SubmitButton from "@/components/button/SumitButton";
import VerificationResultButton from "@/components/button/VerificationResultButton";
import DataGrid from "@/components/module/DataGrid";
import SearchForm from "@/components/module/SearchForm";
import { VerificationResultDialog } from "@/components/module/VerificationResultDialog";
import { VerificationResultHistoryDialog } from "@/components/module/VerificationResultHistoryDialog";
import type { ColumnConfig, ColumnInfo } from "@/components/type/DataGrid.type";
import type { SearchFormValues } from "@/components/type/SearchForm.type";
import { doList } from "@/components/util/column/input/DoList";
import { CauserBurdenInpColumn } from "@/components/util/column/input/sewerageFinance/CauserBurdenInpColumn";
import { SewerageChargeInpColumn } from "@/components/util/column/input/sewerageFinance/SewerageChargeInpColumn";
import { SewerageFinanceSettleInpColumn } from "@/components/util/column/input/sewerageFinance/SewerageFinanceSettleInpColumn";
import { SewerageUsageFeeInpColumn } from "@/components/util/column/input/sewerageFinance/SewerageUsageFeeInpColumn";
import { toGridRows } from "@/components/util/grid/gridFunction";
import { useAdminStore } from "@/store/admin-store";
import { SewerageFinanceSettleInpColumn } from "@/components/util/column/input/sewerageFinance/SewerageFinanceSettleInpColumn"
import { SewerageChargeInpColumn } from "@/components/util/column/input/sewerageFinance/SewerageChargeInpColumn"
import { SewerageUsageFeeInpColumn } from "@/components/util/column/input/sewerageFinance/SewerageUsageFeeInpColumn"
import { CauserBurdenInpColumn } from "@/components/util/column/input/sewerageFinance/CauserBurdenInpColumn"
const SewerageFinanceInpTabList = [
SewerageFinanceSettleInpColumn,
SewerageChargeInpColumn,
SewerageUsageFeeInpColumn,
CauserBurdenInpColumn,
] as const;
const firstTabKey = SewerageFinanceSettleInpColumn.tabKey;
const SEWERAGE_FINANCE_COLUMN_GUIDE_TAB_KEYS = Array.from(
new Set(SewerageFinanceInpTabList.map((tab) => tab.tabKey).filter(Boolean)),
);
type GridRowWithVerification = Record<string, string | number> & {
verificationResult?: SewerageFinanceVerificationResult;
};
type TabToolbarSnapshot = {
showSaveInToolbar: boolean;
showEnterableToolbarButtons: boolean;
showActionToolbarCard: boolean;
};
const toGridColumnInfo = (tab: (typeof SewerageFinanceInpTabList)[number]): ColumnInfo => ({
columns: tab.columns ?? [],
columnLayout: tab.columnLayout ?? [],
});
function buildSewerageFinanceSearchParam(
values: SearchFormValues,
overrides?: { mode?: string },
): SewerageFinanceRequestParams {
return {
statsYr: values.year,
sewerBzmnCd: values.bsnmCd,
ctpvNm: values.sido,
sggNm: values.gugun === "ALL" ? "" : values.gugun,
...(overrides?.mode ? { mode: overrides.mode } : {}),
};
}
const computeTabEnterable = (
statusInfo: SewerageStatusInfo | null | undefined,
periodEnterable: boolean,
): boolean => {
if (statusInfo?.laeSbmsnSttsCd) {
if (["2", "3"].includes(statusInfo.laeSbmsnSttsCd)) {
return false;
}
return periodEnterable;
}
return periodEnterable;
};
function buildGugunOkForToolbar(values: SearchFormValues | null): boolean {
return doList.includes(values?.bsnmCd || "")
? Boolean(values?.gugun && values.gugun !== "ALL")
: true;
}
function computeTabToolbarSnapshot(
statusInfo: SewerageStatusInfo | null,
hasRowData: boolean,
treatAsSearched: boolean,
searchContext: SearchFormValues | null,
periodEnterable: boolean,
isAdminInfo: boolean,
): TabToolbarSnapshot {
const gugunOk = buildGugunOkForToolbar(searchContext);
const enterable = computeTabEnterable(statusInfo, periodEnterable);
const isSubmitted = statusInfo?.laeSbmsnSttsCd === "2";
const showSaveInToolbar = (enterable || isAdminInfo) && gugunOk;
const showEnterableToolbarButtons = enterable && gugunOk;
const showSubmittedInInputPeriodToolbar = isSubmitted && periodEnterable && gugunOk;
const showActionToolbarCard =
(hasRowData || treatAsSearched) &&
(showSaveInToolbar ||
showEnterableToolbarButtons ||
showSubmittedInInputPeriodToolbar ||
(isSubmitted && isAdminInfo));
return { showSaveInToolbar, showEnterableToolbarButtons, showActionToolbarCard };
}
function tabRowDataExists(
m: Map<string, Record<string, string | number>[]>,
tabKey: string,
): boolean {
const rows = m.get(tabKey);
return Array.isArray(rows) && rows.length > 0;
}
function mapRegionalListToGridRows<
T extends { verificationResult?: SewerageFinanceVerificationResult },
>(list: T[], ctpvNm: string): GridRowWithVerification[] {
return list.map((item) => {
const { verificationResult, ...rest } = item;
const row = toGridRows([{ ctpvNm, ...rest }])[0] as GridRowWithVerification;
if (verificationResult) row.verificationResult = verificationResult;
return row;
});
}
// 하수도 통계 관리 > 09. 하수도재정
const SewerageFinanceInp = () => {
const { setMessage: alert } = useAlertStore((state) => state);
const { confirm } = useConfirm();
const { isAdminInfo } = useAdminStore();
// tab list
const SewerageFinanceInpTabList = [
SewerageFinanceSettleInpColumn,
SewerageChargeInpColumn,
SewerageUsageFeeInpColumn,
CauserBurdenInpColumn
];
const [isLoading, setIsLoading] = useState(false);
const [activeTab, setActiveTab] = useState<string>(firstTabKey);
const [rowDataMap, setRowDataMap] = useState<Map<string, Record<string, string | number>[]>>(
new Map(),
);
const [searchValues, setSearchValues] = useState<SearchFormValues | null>(null);
const [lastSearchedValues, setLastSearchedValues] = useState<SearchFormValues | null>(null);
const [hasSearched, setHasSearched] = useState(false);
const [inputPeriodEnterable, setInputPeriodEnterable] = useState(true);
const [tabStatusInfoMap, setTabStatusInfoMap] = useState<
Map<string, SewerageStatusInfo | null>
>(new Map());
const [tabStatsCdMap, setTabStatsCdMap] = useState<Map<string, string>>(new Map());
const [toolbarSnapshotByTab, setToolbarSnapshotByTab] = useState<
Map<string, TabToolbarSnapshot>
>(new Map());
const [columnGuidesByTab, setColumnGuidesByTab] = useState<
Record<string, Record<string, string>>
>({});
const [verificationHistoryOpen, setVerificationHistoryOpen] = useState(false);
const [verificationResultDialogOpen, setVerificationResultDialogOpen] = useState(false);
const submitExceptionFunc = () => {
// 예외 제출 로직
const searchParamRef = useRef<SewerageFinanceRequestParams | null>(null);
const searchDoneRef = useRef(false);
const periodEnterableRef = useRef(true);
const activeTabRef = useRef(activeTab);
const getGridDataRef = useRef<(() => Record<string, string | number>[]) | null>(null);
activeTabRef.current = activeTab;
useEffect(() => {
let cancelled = false;
void (async () => {
const entries = await Promise.all(
SEWERAGE_FINANCE_COLUMN_GUIDE_TAB_KEYS.map(async (tabKey) => {
try {
const res = await statisticsApi.getColumnGuides(tabKey);
if (res.status !== "success" || !Array.isArray(res.data)) {
return [tabKey, {}] as const;
}
const next: Record<string, string> = {};
for (const item of res.data) {
const key = item.engColNm?.trim();
const guide = item.columnGuide?.trim();
if (key && guide) next[key] = guide;
}
return [tabKey, next] as const;
} catch {
return [tabKey, {}] as const;
}
}),
);
if (cancelled) return;
setColumnGuidesByTab(Object.fromEntries(entries));
})();
return () => {
cancelled = true;
};
}, []);
const commitToolbarSnapshotForLoadedTab = (
tabKey: string,
statusInfo: SewerageStatusInfo | null,
hasRowData: boolean,
searchContext: SearchFormValues,
) => {
setToolbarSnapshotByTab((prev) => {
const next = new Map(prev);
next.set(
tabKey,
computeTabToolbarSnapshot(
statusInfo,
hasRowData,
searchDoneRef.current,
searchContext,
periodEnterableRef.current,
isAdminInfo,
),
);
return next;
});
};
const lastYearDataFunc = () => {
// 지난연도 데이터 가져오기 로직
const applyRowMap = (
mergeIntoExistingMap: boolean,
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);
}
};
const loadActiveTabData = async (
tabKey: string,
param: SewerageFinanceRequestParams,
searchContext: SearchFormValues,
mergeIntoExistingMap: boolean,
updateLastSearchedValues: boolean,
overrides?: { mode?: string },
): Promise<boolean> => {
const requestParam = overrides?.mode ? { ...param, mode: overrides.mode } : param;
if (tabKey === SewerageFinanceSettleInpColumn.tabKey) {
const response =
await statisticsInputSewerageFinanceApi.getSewerageFinanceSettleList(requestParam);
if (response.status !== "success" || !response.data) {
alert(response.message || "데이터 조회 중 오류가 발생했습니다.");
return false;
}
const { commonInfo, statusInfo, sewerageFinanceSettleList } = response.data;
const gridRows = mapRegionalListToGridRows(
(sewerageFinanceSettleList ?? []) as SewerageFinanceSettleItem[],
commonInfo.ctpvNm,
);
applyRowMap(mergeIntoExistingMap, (m) => {
m.set(SewerageFinanceSettleInpColumn.tabKey, gridRows);
});
setTabStatusInfoMap((prev) => new Map(prev).set(tabKey, statusInfo));
setTabStatsCdMap((prev) => new Map(prev).set(tabKey, commonInfo.statsCd));
if (updateLastSearchedValues) setLastSearchedValues(searchContext);
commitToolbarSnapshotForLoadedTab(tabKey, statusInfo, gridRows.length > 0, searchContext);
return true;
}
if (tabKey === SewerageChargeInpColumn.tabKey) {
const response =
await statisticsInputSewerageFinanceApi.getSewerageChargeList(requestParam);
if (response.status !== "success" || !response.data) {
alert(response.message || "데이터 조회 중 오류가 발생했습니다.");
return false;
}
const { commonInfo, statusInfo, sewerageChargeList } = response.data;
const gridRows = mapRegionalListToGridRows(
(sewerageChargeList ?? []) as SewerageChargeItem[],
commonInfo.ctpvNm,
);
applyRowMap(mergeIntoExistingMap, (m) => {
m.set(SewerageChargeInpColumn.tabKey, gridRows);
});
setTabStatusInfoMap((prev) => new Map(prev).set(tabKey, statusInfo));
setTabStatsCdMap((prev) => new Map(prev).set(tabKey, commonInfo.statsCd));
if (updateLastSearchedValues) setLastSearchedValues(searchContext);
commitToolbarSnapshotForLoadedTab(tabKey, statusInfo, gridRows.length > 0, searchContext);
return true;
}
if (tabKey === SewerageUsageFeeInpColumn.tabKey) {
const response =
await statisticsInputSewerageFinanceApi.getSewerageUsageFeeList(requestParam);
if (response.status !== "success" || !response.data) {
alert(response.message || "데이터 조회 중 오류가 발생했습니다.");
return false;
}
const { commonInfo, statusInfo, sewerageUsageFeeList } = response.data;
const gridRows = mapRegionalListToGridRows(
(sewerageUsageFeeList ?? []) as SewerageUsageFeeItem[],
commonInfo.ctpvNm,
);
applyRowMap(mergeIntoExistingMap, (m) => {
m.set(SewerageUsageFeeInpColumn.tabKey, gridRows);
});
setTabStatusInfoMap((prev) => new Map(prev).set(tabKey, statusInfo));
setTabStatsCdMap((prev) => new Map(prev).set(tabKey, commonInfo.statsCd));
if (updateLastSearchedValues) setLastSearchedValues(searchContext);
commitToolbarSnapshotForLoadedTab(tabKey, statusInfo, gridRows.length > 0, searchContext);
return true;
}
if (tabKey === CauserBurdenInpColumn.tabKey) {
const response =
await statisticsInputSewerageFinanceApi.getCauserBurdenList(requestParam);
if (response.status !== "success" || !response.data) {
alert(response.message || "데이터 조회 중 오류가 발생했습니다.");
return false;
}
const { commonInfo, statusInfo, causerBurdenList } = response.data;
const gridRows = mapRegionalListToGridRows(
(causerBurdenList ?? []) as CauserBurdenItem[],
commonInfo.ctpvNm,
);
applyRowMap(mergeIntoExistingMap, (m) => {
m.set(CauserBurdenInpColumn.tabKey, gridRows);
});
setTabStatusInfoMap((prev) => new Map(prev).set(tabKey, statusInfo));
setTabStatsCdMap((prev) => new Map(prev).set(tabKey, commonInfo.statsCd));
if (updateLastSearchedValues) setLastSearchedValues(searchContext);
commitToolbarSnapshotForLoadedTab(tabKey, statusInfo, gridRows.length > 0, searchContext);
return true;
}
alert("해당 탭 조회 API가 없습니다.");
return false;
};
const getTabGridRows = (tabKey: string): Record<string, string | number>[] =>
getGridDataRef.current?.() ?? rowDataMap.get(tabKey) ?? [];
const handleReset = () => {
setRowDataMap(new Map());
setTabStatusInfoMap(new Map());
setTabStatsCdMap(new Map());
setToolbarSnapshotByTab(new Map());
setActiveTab(firstTabKey);
activeTabRef.current = firstTabKey;
setLastSearchedValues(null);
setHasSearched(false);
setInputPeriodEnterable(true);
searchParamRef.current = null;
searchDoneRef.current = false;
periodEnterableRef.current = true;
};
const handleSearch = async (values: SearchFormValues) => {
if (!values.sido || values.sido === "ALL") {
alert("시도를 선택해주세요.");
return;
}
try {
const periodResponse = await statisticsInputSewSupplyRateApi.checkInputPeriod(
values.year || "",
);
if (periodResponse.status !== "success") {
alert(periodResponse.message);
return;
}
const periods = Array.isArray(periodResponse.data) ? periodResponse.data : [];
const periodOk = isInputPeriodCurrent(periods);
periodEnterableRef.current = periodOk;
setInputPeriodEnterable(periodOk);
const yearChangedFromLastSearch =
lastSearchedValues != null &&
(lastSearchedValues.year ?? "") !== (values.year ?? "");
if (yearChangedFromLastSearch) {
setRowDataMap(new Map());
setTabStatusInfoMap(new Map());
setTabStatsCdMap(new Map());
setToolbarSnapshotByTab(new Map());
activeTabRef.current = firstTabKey;
setActiveTab(firstTabKey);
}
setIsLoading(true);
searchDoneRef.current = true;
setHasSearched(true);
const searchParam = buildSewerageFinanceSearchParam(values);
searchParamRef.current = searchParam;
await loadActiveTabData(
activeTabRef.current,
searchParam,
values,
!yearChangedFromLastSearch,
true,
);
} catch {
alert("데이터 조회 중 오류가 발생했습니다.");
} finally {
setIsLoading(false);
}
};
const handleActiveTabChange = async (tabKey: string) => {
const prevTabKey = activeTabRef.current;
const latestRowsInPrevTab = getTabGridRows(prevTabKey);
if (latestRowsInPrevTab.length > 0 || rowDataMap.has(prevTabKey)) {
setRowDataMap((prev) => {
const next = new Map(prev);
next.set(prevTabKey, latestRowsInPrevTab);
return next;
});
}
activeTabRef.current = tabKey;
setActiveTab(tabKey);
if (!hasSearched || !lastSearchedValues || !searchParamRef.current) return;
if (tabRowDataExists(rowDataMap, tabKey)) return;
try {
setIsLoading(true);
await loadActiveTabData(
tabKey,
searchParamRef.current,
lastSearchedValues,
true,
false,
);
} catch {
alert("데이터 조회 중 오류가 발생했습니다.");
} finally {
setIsLoading(false);
}
};
const lastYearDataFunc = async () => {
const values = lastSearchedValues ?? searchValues;
if (!values?.sido || values.sido === "ALL") {
alert("시도를 선택해주세요.");
return;
}
if (!hasSearched) {
alert("먼저 데이터를 조회해주세요.");
return;
}
try {
setIsLoading(true);
const param = buildSewerageFinanceSearchParam(values, { mode: "PRE" });
await loadActiveTabData(
activeTabRef.current,
param,
values,
true,
false,
{ mode: "PRE" },
);
} catch {
alert("데이터 조회 중 오류가 발생했습니다.");
} finally {
setIsLoading(false);
}
};
const cnacelSubmitFunc = () => {
void (async () => {
try {
if (!(await confirm("제출된 내용을 취소하시겠습니까?"))) return;
if (!lastSearchedValues || !searchParamRef.current) {
alert("먼저 조회를 진행해주세요.");
return;
}
const userInfo = await getUserInfo();
const tabKey = activeTabRef.current;
const statsCdForTab = tabStatsCdMap.get(tabKey);
if (!statsCdForTab) {
alert("먼저 조회를 진행해주세요.");
return;
}
const requestData: cancelSubmitRequest = {
sewerBzmnCd: lastSearchedValues.bsnmCd ?? "",
ctpvNm: lastSearchedValues.sido ?? "",
sggNm:
lastSearchedValues.gugun === "ALL"
? ""
: (lastSearchedValues.gugun ?? ""),
statsYr: lastSearchedValues.year ?? "",
mdfrId: userInfo.userUnqId,
statsCd: statsCdForTab,
};
const response = await statisticsApi.cancelSubmit(requestData);
if (response.status === "success") {
setIsLoading(true);
await loadActiveTabData(
tabKey,
searchParamRef.current,
lastSearchedValues,
false,
false,
);
alert("제출 취소되었습니다.");
} else {
alert(response.message ?? "제출 취소 중 오류가 발생했습니다.");
}
} catch {
alert("제출 취소 중 오류가 발생했습니다.");
} finally {
setIsLoading(false);
}
})();
};
const submitFunc = () => {
@@ -39,26 +544,249 @@ const SewerageFinanceInp = () => {
// 저장 로직
};
const [rowDataMap, setRowDataMap] = useState<Map<string, Record<string, string | number>[]>>(new Map());
const openVerificationHistoryDialog = () => {
const ctx = lastSearchedValues ?? searchValues;
const statsCd = tabStatsCdMap.get(activeTab);
if (!ctx?.year || !ctx?.sido || !ctx?.bsnmCd || !statsCd) {
alert("먼저 조회를 진행해주세요.");
return;
}
setVerificationHistoryOpen(true);
};
const verificationQueryContext = (() => {
const ctx = lastSearchedValues ?? searchValues;
const statsCd = tabStatsCdMap.get(activeTab);
if (!ctx?.year || !ctx?.sido || !ctx?.bsnmCd || !statsCd) return null;
return {
sewerBzmnCd: ctx.bsnmCd,
ctpvNm: ctx.sido,
sggNm: ctx.gugun === "ALL" ? "" : (ctx.gugun ?? ""),
statsYr: ctx.year,
statsCd,
};
})();
const columnInfoByTab = useMemo(() => {
const map = new Map<string, ColumnInfo>();
for (const tab of SewerageFinanceInpTabList) {
const tabKey = tab.tabKey;
const canEdit =
computeTabEnterable(
tabStatusInfoMap.get(tabKey) ?? null,
inputPeriodEnterable,
) || isAdminInfo;
const rawColumns = (tab.columns ?? []) as ColumnConfig[];
const base = canEdit
? rawColumns
: rawColumns.map((col) => ({ ...col, editable: false as const }));
const columns = base.map((col) => {
const guide = columnGuidesByTab[tabKey]?.[col.fieldName]?.trim();
return guide ? { ...col, tooltip: guide } : col;
});
map.set(tabKey, {
columns,
columnLayout: tab.columnLayout ?? [],
});
}
return map;
}, [tabStatusInfoMap, inputPeriodEnterable, isAdminInfo, columnGuidesByTab]);
const activeTabStatus = tabStatusInfoMap.get(activeTab) ?? null;
const activeTabToolbar =
toolbarSnapshotByTab.get(activeTab) ??
computeTabToolbarSnapshot(
activeTabStatus,
tabRowDataExists(rowDataMap, activeTab),
hasSearched,
lastSearchedValues,
inputPeriodEnterable,
isAdminInfo,
);
const { showSaveInToolbar, showEnterableToolbarButtons, showActionToolbarCard } =
activeTabToolbar;
const verificationCellStyle = ({
fieldName,
row,
}: {
fieldName: string;
row: Record<string, string | number>;
}): string | undefined => {
const verification = (row as GridRowWithVerification).verificationResult;
if (!verification) return;
const errors = verification[fieldName];
if (Array.isArray(errors) && errors.length > 0) {
const hasImr = errors.some((e) => e.vrfcCd === "IMR" || e.vrfcCd === "HB");
return hasImr ? "rg-cell-verify-error-imr" : "rg-cell-verify-error";
}
};
const verificationCellTooltip = ({
fieldName,
row,
}: {
fieldName: string;
row: Record<string, string | number>;
}): string | undefined => {
const verification = (row as GridRowWithVerification).verificationResult;
if (!verification) return;
const errors = verification[fieldName];
if (Array.isArray(errors) && errors.length > 0) {
return errors
.map((e) => {
const prefix =
e.vrfcCd === "IMR"
? "IMR검증 - "
: e.vrfcCd === "NV"
? "일반검증 - "
: "HB검증 - ";
return `${prefix}${e.rsnExpln}(검증 기준값: ${e.compVlCn} / 현재 입력값: ${e.nowVlCn})`;
})
.filter(Boolean)
.join("\n");
}
};
return (
<>
<SearchForm />
<div className="card !py-0">
<div className="flex justify-end items-center gap-[0.6rem] m-[0.6rem]">
<SubmitExceptionButton SubmitExceptionFunc={submitExceptionFunc} />
<LastYearDataButton getLastYearDataFunc={lastYearDataFunc} />
<SubmitButton submitFunc={submitFunc} />
<SaveButton saveFunc={saveFunc} />
<div className="relative">
<SearchForm
onSubmit={handleSearch}
onReset={handleReset}
onValuesChange={setSearchValues}
/>
{showActionToolbarCard && (
<div className="card !py-0">
<div className="flex justify-end items-center gap-[0.6rem] m-[0.6rem]">
<VerificationResultButton
VerificationResultButtonFunc={openVerificationHistoryDialog}
/>
{showEnterableToolbarButtons && (
<>
<LastYearDataButton
getLastYearDataFunc={() => void lastYearDataFunc()}
/>
<SubmitButton submitFunc={submitFunc} />
</>
)}
{activeTabStatus?.laeSbmsnSttsCd === "2" && (
<CancelSubmitButton cnacelSubmitFunc={cnacelSubmitFunc} />
)}
{showSaveInToolbar && <SaveButton saveFunc={saveFunc} />}
</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>
)}
<div className="flex flex-col gap-[0.6rem] mx-[0.6rem]">
<Tabs
value={activeTab}
onValueChange={(v) => void handleActiveTabChange(v)}
className="w-full"
variant="default"
>
<TabsList>
{SewerageFinanceInpTabList.map((info) => (
<TabsTrigger key={info.tabKey} value={info.tabKey}>
{info.tabLabel || info.tabKey}
</TabsTrigger>
))}
</TabsList>
{SewerageFinanceInpTabList.map((info) => {
const tabKey = info.tabKey;
const rowData = rowDataMap.get(tabKey) ?? [];
const tabEnterableForGrid =
computeTabEnterable(
tabStatusInfoMap.get(tabKey) ?? null,
inputPeriodEnterable,
) || isAdminInfo;
const gugunOk = buildGugunOkForToolbar(lastSearchedValues);
const tabStatusInfo = tabStatusInfoMap.get(tabKey) ?? null;
return (
<TabsContent key={tabKey} value={tabKey}>
<DataGrid
key={`${tabKey}-${tabEnterableForGrid ? "enterable" : "readonly"}`}
columnInfo={
columnInfoByTab.get(tabKey) ?? toGridColumnInfo(info)
}
rowStartNum={info.rowStartNum}
excelExportFileSuffix={info.tabLabel || tabKey}
rowData={rowData}
setRowData={(updater) => {
setRowDataMap((prev) => {
const next = new Map(prev);
const cur = next.get(tabKey) ?? [];
const n =
typeof updater === "function"
? updater(cur)
: updater;
next.set(tabKey, n);
return next;
});
}}
showHeaderCheckbox={false}
fixedColumns={info.fixedColumns}
enableSort={false}
statusInfo={tabStatusInfo}
isExcelImport={
tabRowDataExists(rowDataMap, tabKey) &&
tabEnterableForGrid &&
gugunOk
}
isExcelExport
cellStyleCallback={verificationCellStyle}
cellTooltipCallback={verificationCellTooltip}
getGridDataRef={
activeTab === tabKey ? getGridDataRef : undefined
}
/>
</TabsContent>
);
})}
</Tabs>
</div>
</div>
<TabDataGrid
columnInfo={SewerageFinanceInpTabList}
rowDataMap={rowDataMap}
setRowDataMap={setRowDataMap}
<VerificationResultHistoryDialog
open={verificationHistoryOpen}
onOpenChange={setVerificationHistoryOpen}
queryContext={verificationQueryContext}
onExceptionResetSubmitted={async () => {
const values = lastSearchedValues ?? searchValues;
if (!values || !searchParamRef.current) return;
await loadActiveTabData(
activeTabRef.current,
searchParamRef.current,
values,
true,
false,
);
}}
/>
<VerificationResultDialog
open={verificationResultDialogOpen}
onOpenChange={setVerificationResultDialogOpen}
queryContext={verificationQueryContext}
onExceptionSubmitted={async () => {
const values = lastSearchedValues ?? searchValues;
if (!values || !searchParamRef.current) return;
await loadActiveTabData(
activeTabRef.current,
searchParamRef.current,
values,
true,
false,
);
}}
/>
</>
);
};
export default SewerageFinanceInp;
export default SewerageFinanceInp;
@@ -288,7 +288,8 @@ const SeweragePenetrationRateInp = () => {
} else {
alert(response.message);
}
} catch {
} catch (error) {
console.log(error);
alert('데이터 조회 중 오류가 발생했습니다.');
setRowData([]);
setStatusInfo(null);
@@ -790,11 +791,13 @@ const SeweragePenetrationRateInp = () => {
</div>
</div>
)}
{isLoading && !globalProgressVisible && <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" />
{isLoading && !globalProgressVisible &&
<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>}
}
<DataGrid
key={(isEnterable || isAdminInfo) ? 'enterable' : 'readonly'}
columnInfo={columnInfo}
@@ -859,6 +862,11 @@ const SeweragePenetrationRateInp = () => {
}
: null
}
onExceptionResetSubmitted={async () => {
const values = lastSearchedValues ?? searchValues;
if (!values) return;
await handleSearch(values);
}}
/>
<VerificationResultDialog
open={verificationResultDialogOpen}
+22 -1
View File
@@ -116,12 +116,22 @@ export type DataFieldValueCallback = (
/** 계산 필드 설정 (computedFieldCallbacks.ts 참고) */
export type ComputedFieldConfig =
| { fields: string[]; type: "sum" | "avg" | "max" | "difference" }
| { fields: string[]; type: "sum" | "avg" | "max" | "difference" | "quotient" }
| {
minuendFields: string[];
subtrahendFields: string[];
type: "differenceOfSums";
}
| {
firstSumFields: string[];
secondSumFields: string[];
type: "halfSumOfSums";
}
| {
firstHalfSum: { firstSumFields: string[]; secondSumFields: string[] };
secondHalfSum: { firstSumFields: string[]; secondSumFields: string[] };
type: "sumOfTwoHalfSumOfSums";
}
| {
baseField: string;
sumFields: string[];
@@ -157,6 +167,17 @@ export type ComputedFieldConfig =
perPpqtyField: string;
}>;
type: "pumpGrossPpqty";
}
| {
inflwField: string;
dschgField: string;
type: "bodPrcsRt";
}
| {
/** (quotient[0]/quotient[1]) / (denominatorQuotient[0]/denominatorQuotient[1]) × 100 */
numeratorQuotient: [string, string];
denominatorQuotient: [string, string];
type: "quotientRatioPercent";
};
export type ColumnConfig = {
@@ -8,58 +8,83 @@ import { ValueType } from "realgrid";
export const ExcretaTreatFactInpColumn = {
columns: [
// 행정구역
{ name: 'sido', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'gugun', fieldName: '시군구', header: { text: '시군구' }, footer: { text: '합계' } },
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, footer: { text: '합계' }, editable: false },
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' } },
// 기본 정보
{ name: 'ecscs_nm', fieldName: '시설명', header: { text: '시설명' } },
{ name: 'locplcs', fieldName: '소재지', header: { text: '소재지' } },
{ name: 'telno', fieldName: '전화번호', header: { text: '전화번호' } },
{ name: 'fcltNm', fieldName: 'fcltNm', header: { text: '시설명' } },
{ name: 'fcltPstnCn', fieldName: 'fcltPstnCn', header: { text: '소재지' } },
{ name: 'telNo', fieldName: 'telNo', header: { text: '전화번호' } },
// 날짜 정보
{ name: 'comp_dt', fieldName: '준공일 yyyy-mm-dd', header: { text: '준공일 yyyy-mm-dd' } },
{ name: 'rn_dt', fieldName: '가동개시일 yyyy-mm-dd', header: { text: '가동개시일 yyyy-mm-dd' } },
{
name: 'cmptnStrtYmd',
fieldName: 'cmptnStrtYmd',
header: { text: '준공일 yyyy-mm-dd' },
datetimeFormat: 'yyyy-MM-dd',
editor: {
type: 'date' as const,
dropDownWhenClick: true,
datetimeFormat: 'yyyy-MM-dd',
},
},
{
name: 'oprtngStrtYmd',
fieldName: 'oprtngStrtYmd',
header: { text: '가동개시일 yyyy-mm-dd' },
datetimeFormat: 'yyyy-MM-dd',
editor: {
type: 'date' as const,
dropDownWhenClick: true,
datetimeFormat: 'yyyy-MM-dd',
},
},
// 사업비/용량 정보
{ name: 'wct', fieldName: '사업비(백만원)', header: { text: '사업비(백만원)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'ecscs_cpcty', fieldName: '시설용량(m³/일)', header: { text: '시설용량(m³/일)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'process_cpcty', fieldName: '처리량(m³/일)', header: { text: '처리량(m³/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'opr_daycnt', fieldName: '가동일수(일/년)', header: { text: '가동일수(일/년)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'wholPjtcoAmt', fieldName: 'wholPjtcoAmt', header: { text: '사업비(백만원)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'prcsFcltCpct', fieldName: 'prcsFcltCpct', header: { text: '시설용량(m³/일)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'cpct', fieldName: 'cpct', header: { text: '처리량(m³/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'oprtngDayCnt', fieldName: 'oprtngDayCnt', header: { text: '가동일수(일/년)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 처리 공법 및 연계
{ name: 'trt_msrc', fieldName: '처리공법', header: { text: '처리공법' } },
{ name: 'cntc_prcplt', fieldName: '연계처리장명', header: { text: '연계처리장명' } },
{ name: 'assct_trt_cpc', fieldName: '연계처리량(m³/일)', header: { text: '연계처리량(m³/일)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'prcsMthdExpln', fieldName: 'prcsMthdExpln', header: { text: '처리공법' } },
{ name: 'linkTmplntCn', fieldName: 'linkTmplntCn', header: { text: '연계처리장명' } },
{ name: 'linkPrcsQnt', fieldName: 'linkPrcsQnt', header: { text: '연계처리량(m³/일)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 유입수질
{ name: 'infl_wqlt_bod', fieldName: 'BOD', header: { text: 'BOD' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'infl_wqlt_toc', fieldName: 'TOC', header: { text: 'TOC' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'infl_wqlt_ss', fieldName: 'SS', header: { text: 'SS' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'infl_wqlt_tn', fieldName: 'T-N', header: { text: 'T-N' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' },
{ name: 'infl_wqlt_tp', fieldName: 'T-P', header: { text: 'T-P' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' },
{ name: 'infl_wqlt_clfgrpcnt', fieldName: '대장균군수', header: { text: '대장균군수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'inflwBodWqVl', fieldName: 'inflwBodWqVl', header: { text: 'BOD' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'inflwTocWqVl', fieldName: 'inflwTocWqVl', header: { text: 'TOC' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'inflwSsWqVl', fieldName: 'inflwSsWqVl', header: { text: 'SS' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'inflwTnWqVl', fieldName: 'inflwTnWqVl', header: { text: 'T-N' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' },
{ name: 'inflwTpWqVl', fieldName: 'inflwTpWqVl', header: { text: 'T-P' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' },
{ name: 'inflwClfgCnt', fieldName: 'inflwClfgCnt', header: { text: '대장균군수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 방류수질
{ name: 'effl_wqlt_bod', fieldName: 'BOD', header: { text: 'BOD' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'effl_wqlt_toc', fieldName: 'TOC', header: { text: 'TOC' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'effl_wqlt_ss', fieldName: 'SS', header: { text: 'SS' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'effl_wqlt_tn', fieldName: 'T-N', header: { text: 'T-N' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' },
{ name: 'effl_wqlt_tp', fieldName: 'T-P', header: { text: 'T-P' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' },
{ name: 'effl_wqlt_clfgrpcnt', fieldName: '대장균군수', header: { text: '대장균군수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'dschgBodWqVl', fieldName: 'dschgBodWqVl', header: { text: 'BOD' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'dschgTocWqVl', fieldName: 'dschgTocWqVl', header: { text: 'TOC' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'dschgSsWqVl', fieldName: 'dschgSsWqVl', header: { text: 'SS' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'dschgTnWqVl', fieldName: 'dschgTnWqVl', header: { text: 'T-N' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' },
{ name: 'dschgTpWqVl', fieldName: 'dschgTpWqVl', header: { text: 'T-P' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' },
{ name: 'dschgClfgCnt', fieldName: 'dschgClfgCnt', header: { text: '대장균군수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 방류수역
{ name: 'rvbsn', fieldName: '수계', header: { text: '수계' } },
{ name: 'bstrm', fieldName: '지류', header: { text: '지류' } },
{ name: 'lgbsn', fieldName: '중권역', header: { text: '중권역' } },
{ name: 'mdmbsn', fieldName: '소권역', header: { text: '소권역' } },
{ name: 'wtsysNm', fieldName: 'wtsysNm', header: { text: '수계' } },
{ name: 'trbtrNm', fieldName: 'trbtrNm', header: { text: '지류' } },
{ name: 'msirbCn', fieldName: 'msirbCn', header: { text: '중권역' } },
{ name: 'ssirbCn', fieldName: 'ssirbCn', header: { text: '소권역' } },
// 운영 정보
{ name: 'oper_mth', fieldName: '운영주체(자체/공기업/민간위탁)', header: { text: '운영주체(자체/공기업/민간위탁)' } },
{ name: 'cntc_prcplt_cpc', fieldName: '직원총수(명)', header: { text: '직원총수(명)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'safe_acdnt_co', fieldName: '안전사고건수(건/인/년)', header: { text: '안전사고건수(건/인/년)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'operMthdCdCn', fieldName: 'operMthdCdCn', header: { text: '운영주체(자체/공기업/민간위탁)' } },
{ name: 'wholNope', fieldName: 'wholNope', header: { text: '직원총수(명)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'sftyAcdntEvntcsCnt', fieldName: 'sftyAcdntEvntcsCnt', header: { text: '안전사고건수(건/인/년)' }, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 비고
{ name: 'rmk', fieldName: '비고', header: { text: '비고' } },
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
// Hidden Columns : 시스템 관리를 위한 값
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, },
{ name: 'fcltSn', fieldName: 'fcltSn', header: { text: '시설일련번호' }, visible: false, },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, visible: false },
],
columnLayout: [
@@ -67,41 +92,51 @@ export const ExcretaTreatFactInpColumn = {
name: 'adminGroup',
hideChildHeaders: false,
header: { text: '행정구역' },
items: ['sido', 'gugun'],
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
'ecscs_nm',
'locplcs',
'telno',
'comp_dt',
'rn_dt',
'wct',
'ecscs_cpcty',
'process_cpcty',
'opr_daycnt',
'trt_msrc',
'cntc_prcplt',
'assct_trt_cpc',
'fcltNm',
'fcltPstnCn',
'telNo',
'cmptnStrtYmd',
'oprtngStrtYmd',
'wholPjtcoAmt',
'prcsFcltCpct',
'cpct',
'oprtngDayCnt',
'prcsMthdExpln',
'linkTmplntCn',
'linkPrcsQnt',
{
name: 'influenceGroup',
hideChildHeaders: false,
header: { text: '유입수질(mg/L, 개/mL)' },
items: ['infl_wqlt_bod', 'infl_wqlt_toc', 'infl_wqlt_ss', 'infl_wqlt_tn', 'infl_wqlt_tp', 'infl_wqlt_clfgrpcnt'],
items: ['inflwBodWqVl', 'inflwTocWqVl', 'inflwSsWqVl', 'inflwTnWqVl', 'inflwTpWqVl', 'inflwClfgCnt'],
},
{
name: 'effluentGroup',
hideChildHeaders: false,
header: { text: '방류수질(mg/L, 개/mL)' },
items: ['effl_wqlt_bod', 'effl_wqlt_toc', 'effl_wqlt_ss', 'effl_wqlt_tn', 'effl_wqlt_tp', 'effl_wqlt_clfgrpcnt'],
items: ['dschgBodWqVl', 'dschgTocWqVl', 'dschgSsWqVl', 'dschgTnWqVl', 'dschgTpWqVl', 'dschgClfgCnt'],
},
{
name: 'dischargeGroup',
hideChildHeaders: false,
header: { text: '방류수역' },
items: ['rvbsn', 'bstrm', 'lgbsn', 'mdmbsn'],
items: ['wtsysNm', 'trbtrNm', 'msirbCn', 'ssirbCn'],
},
'oper_mth',
'cntc_prcplt_cpc',
'safe_acdnt_co',
'rmk',
]
'operMthdCdCn',
'wholNope',
'sftyAcdntEvntcsCnt',
'rmrk',
'admdstCd',
'fcltSn',
'admdstNm'
],
rowStartNum: 2,
fixedColumns: 2,
tabKey: 'ST00070001',
};
@@ -8,7 +8,7 @@ import { footerDecimal1, footerDecimal3,footerInt } from "@/components/util/grid
export const PublicSewageFactColumn = {
columns: [
// 행정구역
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, footer: { text: '합계' }, editable: false, styleName: 'rg-cell-readonly' },
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' }, editable: false, footer: { text: '합계' }, styleName: 'rg-cell-readonly' },
// 시설명
@@ -18,7 +18,20 @@ export const PublicSewageFactColumn = {
{ name: 'fcltPstnCn', fieldName: 'fcltPstnCn', header: { text: '소재지' }, editable: false, styleName: 'rg-cell-readonly' },
// 시설용량
{ name: 'prcsFcltCpct', fieldName: 'prcsFcltCpct', header: { text: '시설용량(500m³/일 이상/미만)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-total' },
{
name: 'prcsFcltCpct',
fieldName: 'prcsFcltCpct',
header: { text: '시설용량(500m³/일 이상/미만)' },
editable: false,
footer: footerDecimal1,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
fields: ['prcsFcltPhysCpct', 'prcsFcltBoilCpct', 'prcsFcltAltdCpct'],
type: 'sum' as const,
},
},
// (m³/일)
{ name: 'prcsFcltPhysCpct', fieldName: 'prcsFcltPhysCpct', header: { text: '물리적' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
@@ -56,8 +69,22 @@ export const PublicSewageFactColumn = {
{ name: 'dschgGrssClfgVl', fieldName: 'dschgGrssClfgVl', header: { text: '방류총대장균군수' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-readonly' },
// 처리특성
{ name: 'prcsRt', fieldName: 'prcsRt', header: { text: '처리효율(%)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-total' },
{ name: 'prcsLodQnt', fieldName: 'prcsLodQnt', header: { text: '처리부하량(kgBOD/D)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-total' },
{
name: 'prcsRt',
fieldName: 'prcsRt',
header: { text: '처리효율(%)' },
editable: false,
footer: footerDecimal1,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
inflwField: 'inflwBodVl',
dschgField: 'dschgBodVl',
type: 'bodPrcsRt' as const,
},
},
{ name: 'prcsLodQnt', fieldName: 'prcsLodQnt', header: { text: '처리부하량(kgBOD/D)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-readonly' },
{ name: 'prcsMthdExpln', fieldName: 'prcsMthdExpln', header: { text: '처리방법' }, editable: false, styleName: 'rg-cell-readonly' },
// 연계처리량
@@ -86,7 +113,20 @@ export const PublicSewageFactColumn = {
{ name: 'goalWqNo', fieldName: 'goalWqNo', header: { text: '목표수질(BOD)' }, editable: false, styleName: 'rg-cell-readonly' },
// 에너지 자립률
{ name: 'engySrlneRt', fieldName: 'engySrlneRt', header: { text: '에너지자립률(%)' }, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-readonly' },
{
name: 'engySrlneRt',
fieldName: 'engySrlneRt',
header: { text: '에너지자립률(%)' },
editable: false,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
numeratorFields: ['engyItslfPrdctnQnt'],
denominatorFields: ['anlEngyWholUseQnt'],
type: 'pblswrgPopltnSuprtRate' as const,
},
},
// 에너지 자체 생산량
{ name: 'engyItslfPrdctnQnt', fieldName: 'engyItslfPrdctnQnt', header: { text: '에너지자체생산량(TOE)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-readonly' },
@@ -97,7 +137,7 @@ export const PublicSewageFactColumn = {
{ name: 'itslfEngyUseQnt', fieldName: 'itslfEngyUseQnt', header: { text: '자체 에너지사용량(TOE)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0', styleName: 'rg-cell-readonly' },
// 하수처리량당 CO2배출량
{ name: 'swtrmtEmsnQnt', fieldName: 'swtrmtEmsnQnt', header: { text: '하수처리량당 CO2배출량(kgCO2/m³)' }, editable: false, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000', styleName: 'rg-cell-total' },
{ name: 'swtrmtEmsnQnt', fieldName: 'swtrmtEmsnQnt', header: { text: '하수처리량당 CO2배출량(kgCO2/m³)' }, editable: false, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000', styleName: 'rg-cell-readonly' },
// 운영주체
{ name: 'operMnbdCn', fieldName: 'operMnbdCn', header: { text: '운영주체(자체/공기업/민간대행)' }, editable: false, styleName: 'rg-cell-readonly' },
@@ -123,7 +163,10 @@ export const PublicSewageFactColumn = {
name: 'administrative',
hideChildHeaders: false,
header: { text: '행정구역' },
items: ['ctpvNm', 'sggNm'],
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
'swtrmtFcltNm',
'fcltPstnCn',
@@ -1,6 +1,33 @@
import type { ColumnConfig } from "@/components/type/DataGrid.type";
import { footerInt, footerDecimal1, footerDecimal3 } from "@/components/util/grid/footerSummary";
import { ValueType } from "realgrid";
/** 펌프장 구분(`pumpSeNm`) */
export const PUMPING_STATION_PUMP_SE_VALUES = ["합류식", "분류식"] as const;
function dropdownColumn(
col: Pick<ColumnConfig, "name" | "fieldName" | "header"> &
Partial<ColumnConfig>,
options: readonly string[],
): ColumnConfig {
const values = [...options];
return {
...col,
values,
labels: values,
lookupDisplay: true,
editor: {
type: "dropdown",
domainOnly: true,
textReadOnly: true,
commitOnSelect: true,
dropDownWhenClick: true,
values,
labels: values,
},
};
}
/**
* 하수도 통계 관리 > 05. 펌프장
* 단일 Tab
@@ -14,18 +41,70 @@ export const PumpingStationInpColumn = {
// 기본 정보
{ name: 'swtrmtFcltNm', fieldName: 'swtrmtFcltNm', header: { text: '처리장명' } },
{ name: 'fcltNm', fieldName: 'fcltNm', header: { text: '시설명' } },
{ name: 'pumpSeNm', fieldName: 'pumpSeNm', header: { text: '구분(합류식 분류식)' } },
dropdownColumn(
{ name: 'pumpSeNm', fieldName: 'pumpSeNm', header: { text: '구분(합류식 분류식)' } },
PUMPING_STATION_PUMP_SE_VALUES,
),
{ name: 'fcltPstnCn', fieldName: 'fcltPstnCn', header: { text: '위치' } },
{ name: 'oprtngStrtYmd', fieldName: 'oprtngStrtYmd', header: { text: '가동 년월일 yyyy-mm-dd' } },
{
name: 'oprtngStrtYmd',
fieldName: 'oprtngStrtYmd',
header: { text: '가동 년월일 yyyy-mm-dd' },
datetimeFormat: 'yyyy-MM-dd',
editor: {
type: 'date' as const,
dropDownWhenClick: true,
datetimeFormat: 'yyyy-MM-dd',
},
},
{ name: 'planMltvArea', fieldName: 'planMltvArea', header: { text: '계획배수면적(㎢)' }, footer: footerDecimal3, valueType: ValueType.NUMBER, dataFormat: '#,##0.000' },
// 계획시간 대 최대배수량
{ name: 'sndyArea', fieldName: 'sndyArea', header: { text: '청천시(A)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'rainArea', fieldName: 'rainArea', header: { text: '우천시(B)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'exlnQnt', fieldName: 'exlnQnt', header: { text: '우수량(B-A)' }, footer: footerDecimal1, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'exlnQnt',
fieldName: 'exlnQnt',
header: { text: '우수량(B-A)' },
footer: footerDecimal1,
editable: false,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
computed: { fields: ['rainArea', 'sndyArea'], type: 'difference' as const },
styleName: 'rg-cell-total',
},
// 배수능력 > 총 양수량 (오수)
{ name: 'drtwPumpGrssPpqty', fieldName: 'drtwPumpGrssPpqty', header: { text: '총양수량(m³/min)' }, footer: footerDecimal1, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'drtwPumpGrssPpqty',
fieldName: 'drtwPumpGrssPpqty',
header: { text: '총양수량(m³/min)' },
footer: footerDecimal1,
editable: false,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
type: 'pumpGrossPpqty' as const,
pumpGroups: [
{
pumpCntField: 'drtwPumpN1Cnt',
rsrvPumpCntField: 'drtwPumpN1RsrvCnt',
perPpqtyField: 'drtwPumpN1Ppqty',
},
{
pumpCntField: 'drtwPumpN2Cnt',
rsrvPumpCntField: 'drtwPumpN2RsrvCnt',
perPpqtyField: 'drtwPumpN2Ppqty',
},
{
pumpCntField: 'drtwPumpN3Cnt',
rsrvPumpCntField: 'drtwPumpN3RsrvCnt',
perPpqtyField: 'drtwPumpN3Ppqty',
},
],
},
},
// 배수능력 > 오수펌프(1)
{ name: 'drtwPumpN1DmtrArea', fieldName: 'drtwPumpN1DmtrArea', header: { text: '구경(mm)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
@@ -46,7 +125,36 @@ export const PumpingStationInpColumn = {
{ name: 'drtwPumpN3RsrvCnt', fieldName: 'drtwPumpN3RsrvCnt', header: { text: '예비펌프대수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 배수능력 > 총 양수량 (우수)
{ name: 'exlnPumpGrssPpqty', fieldName: 'exlnPumpGrssPpqty', header: { text: '총양수량(m³/min)' }, footer: footerDecimal1, editable: false, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'exlnPumpGrssPpqty',
fieldName: 'exlnPumpGrssPpqty',
header: { text: '총양수량(m³/min)' },
footer: footerDecimal1,
editable: false,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
type: 'pumpGrossPpqty' as const,
pumpGroups: [
{
pumpCntField: 'exlnPumpN1Cnt',
rsrvPumpCntField: 'exlnPumpN1RsrvCnt',
perPpqtyField: 'exlnPumpN1Ppqty',
},
{
pumpCntField: 'exlnPumpN2Cnt',
rsrvPumpCntField: 'exlnPumpN2RsrvCnt',
perPpqtyField: 'exlnPumpN2Ppqty',
},
{
pumpCntField: 'exlnPumpN3Cnt',
rsrvPumpCntField: 'exlnPumpN3RsrvCnt',
perPpqtyField: 'exlnPumpN3Ppqty',
},
],
},
},
// 배수능력 > 우수펌프(1)
{ name: 'exlnPumpN1DmtrArea', fieldName: 'exlnPumpN1DmtrArea', header: { text: '구경(mm)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
@@ -83,7 +191,10 @@ export const PumpingStationInpColumn = {
name: 'administrativeArea',
hideChildHeaders: false,
header: { text: '행정구역' },
items: ['ctpvNm', 'sggNm'],
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
'swtrmtFcltNm',
'fcltNm',
@@ -147,5 +258,8 @@ export const PumpingStationInpColumn = {
'admdstCd',
'fcltSn',
'admdstNm'
]
],
rowStartNum: 3,
fixedColumns: 3,
tabKey: 'ST00050001',
};
@@ -5,66 +5,64 @@ import { ValueType } from "realgrid";
export const PrivateSewageDesigStatusInpColumn = {
columns: [
// 행정구역
{ name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { 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: 'MGMT_AR', fieldName: '공고지역(개소)', header: { text: '공고지역(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'APPN_SCOPE', fieldName: '지정범위(㎢)', header: { text: '지정범위(㎢)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'indvSwagePbancRgnCnt', fieldName: 'indvSwagePbancRgnCnt', header: { text: '공고지역(개소)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'indvSwageDsgnScpLen', fieldName: 'indvSwageDsgnScpLen', header: { text: '지정범위(㎢)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 개인하수도관리지역공고현황 > 정화조
{ name: 'STTY_CNT', fieldName: '개소수', header: { text: '개소수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_CPC', fieldName: '처리용량(인/일)', header: { text: '처리용량(인/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'spctnkGrssFcltCnt', fieldName: 'spctnkGrssFcltCnt', header: { text: '개소수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'spctnkQnt', fieldName: 'spctnkQnt', header: { text: '처리용량(인/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 개인하수도관리지역공고현황 > 오수시설
{ name: 'WRRTN_CNT', fieldName: '개소수', header: { text: '개소수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'WRRTN_CPC', fieldName: '처리용량(m³/일)', header: { text: '처리용량(m³/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'drtwPrcsFcltGrssFcltCnt', fieldName: 'drtwPrcsFcltGrssFcltCnt', header: { text: '개소수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'drtwPrcsFcltQnt', fieldName: 'drtwPrcsFcltQnt', header: { text: '처리용량(m³/일)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 비고
{ name: 'RMK', fieldName: '비고', header: { text: '비고' } },
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
// 검증 필드 (hidden)
{ name: 'CHK_MGMT_AR', fieldName: 'CHK_MGMT_AR', header: { text: '' } },
{ name: 'CHK_APPN_SCOPE', fieldName: 'CHK_APPN_SCOPE', header: { text: '' } },
{ name: 'CHK_STTY_CNT', fieldName: 'CHK_STTY_CNT', header: { text: '' } },
{ name: 'CHK_STTY_CPC', fieldName: 'CHK_STTY_CPC', header: { text: '' } },
{ name: 'CHK_WRRTN_CNT', fieldName: 'CHK_WRRTN_CNT', header: { text: '' } },
{ name: 'CHK_WRRTN_CPC', fieldName: 'CHK_WRRTN_CPC', header: { text: '' } },
{ name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' } },
{ name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' } },
// hidden
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, visible: false },
],
columnLayout: [
{
name: 'group_admin',
header: { text: '행정구역' },
items: ['SIDO', 'GUGUN']
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
{
name: 'group_notice',
header: { text: '개인하수도관리지역공고현황 (하수도법제34조의2)' },
items: [
'MGMT_AR',
'APPN_SCOPE',
'indvSwagePbancRgnCnt',
'indvSwageDsgnScpLen',
{
name: 'group_septic',
header: { text: '정화조' },
items: [
'STTY_CNT',
'STTY_CPC'
'spctnkGrssFcltCnt',
'spctnkQnt'
]
},
{
name: 'group_sewage',
header: { text: '오수시설' },
items: [
'WRRTN_CNT',
'WRRTN_CPC'
'drtwPrcsFcltGrssFcltCnt',
'drtwPrcsFcltQnt'
]
}
]
},
'RMK'
'rmrk',
'admdstCd',
'admdstNm'
],
rowStartNum: 3,
fixedColumns: 1,
@@ -5,182 +5,316 @@ import { ValueType } from "realgrid";
export const SepticTankStatusInpColumn = {
columns: [
// 행정구역
{ name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { 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: 'TOT_FCLTY_CNT', fieldName: '총 시설 수 (A+D)', header: { text: '총 시설 수 (A+D)' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 총 시설 수 — (B+C)/2 + (E+F)/2 = A + D
{
name: 'grssFcltCnt',
fieldName: 'grssFcltCnt',
header: { text: '총 시설 수 (A+D)' },
editable: false,
footer: footerInt,
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
styleName: 'rg-cell-total',
computed: {
firstHalfSum: {
firstSumFields: [
'staaInsdSttkNprsflCnt',
'step1StaaInsdSttkFrcCnt',
'staaInsdSprktpFltrtbCnt',
'staaInsdAertMthdCnt',
'staaInsdCntcAertCnt',
'staaInsdEtcCnt',
],
secondSumFields: [
'step1StaaInsdNope',
'step2StaaInsdNope',
'step3StaaInsdNope',
'step4StaaInsdNope',
'step5StaaInsdNope',
'step6StaaInsdNope',
'step7StaaInsdNope',
'step8StaaInsdNope',
],
},
secondHalfSum: {
firstSumFields: [
'staaExcpSttkNprsflCnt',
'step1StaaExcpSttkFrcCnt',
'staaExcpSprktpFltrtbCnt',
'staaExcpAertMthdCnt',
'staaExcpCntcAertCnt',
'staaExcpEtcCnt',
],
secondSumFields: [
'step1StaaExcpNope',
'step2StaaExcpNope',
'step3StaaExcpNope',
'step4StaaExcpNope',
'step5StaaExcpNope',
'step6StaaExcpNope',
'step7StaaExcpNope',
'step8StaaExcpNope',
],
},
type: 'sumOfTwoHalfSumOfSums' as const,
},
},
// 하수처리구역 내 > 총계
{ name: 'SPZI_SM', fieldName: '총계 A=(B+C)/2', header: { text: '총계 A=(B+C)/2' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{
name: 'staaInsdWhol',
fieldName: 'staaInsdWhol',
header: { text: '총계 A=(B+C)/2' },
editable: false,
footer: footerInt,
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
styleName: 'rg-cell-total',
computed: {
firstSumFields: [
'staaInsdSttkNprsflCnt',
'step1StaaInsdSttkFrcCnt',
'staaInsdSprktpFltrtbCnt',
'staaInsdAertMthdCnt',
'staaInsdCntcAertCnt',
'staaInsdEtcCnt',
],
secondSumFields: [
'step1StaaInsdNope',
'step2StaaInsdNope',
'step3StaaInsdNope',
'step4StaaInsdNope',
'step5StaaInsdNope',
'step6StaaInsdNope',
'step7StaaInsdNope',
'step8StaaInsdNope',
],
type: 'halfSumOfSums' as const,
},
},
// 하수처리구역 내 > 처리방식별
{ name: 'SPZI_MTHD_SM', fieldName: '계(B)', header: { text: '계(B)' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{
name: 'staaInsdPrcsMthByWhol',
fieldName: 'staaInsdPrcsMthByWhol',
header: { text: '계(B)' },
editable: false,
footer: footerInt,
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
styleName: 'rg-cell-total',
computed: {
fields: [
'staaInsdSttkNprsflCnt',
'step1StaaInsdSttkFrcCnt',
'staaInsdSprktpFltrtbCnt',
'staaInsdAertMthdCnt',
'staaInsdCntcAertCnt',
'staaInsdEtcCnt',
],
type: 'sum' as const,
},
},
// 하수처리구역 내 > 처리방식별 > 부패탱크
{ name: 'SPZI_ROT_NPFL_CNT', fieldName: '자연유하', header: { text: '자연유하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZI_ROT_ENFCT_PUMP_1', fieldName: '강제펌핑식', header: { text: '강제펌핑식' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'staaInsdSttkNprsflCnt', fieldName: 'staaInsdSttkNprsflCnt', header: { text: '자연유하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step1StaaInsdSttkFrcCnt', fieldName: 'step1StaaInsdSttkFrcCnt', header: { text: '강제펌핑식' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 하수처리구역 내 > 처리방식별
{ name: 'SPZI_WTROT_NPFL_CNT', fieldName: '살수여상', header: { text: '살수여상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZI_TFM', fieldName: '폭기방법', header: { text: '폭기방법' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZI_ARTM', fieldName: '접촉폭기', header: { text: '접촉폭기' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZI_CTARM', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'staaInsdSprktpFltrtbCnt', fieldName: 'staaInsdSprktpFltrtbCnt', header: { text: '살수여상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'staaInsdAertMthdCnt', fieldName: 'staaInsdAertMthdCnt', header: { text: '폭기방법' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'staaInsdCntcAertCnt', fieldName: 'staaInsdCntcAertCnt', header: { text: '접촉폭기' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'staaInsdEtcCnt', fieldName: 'staaInsdEtcCnt', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 하수처리구역 내 > 처리용량별
{ name: 'SPZI_PROC_CPC_SM', fieldName: '계(C)', header: { text: '계(C)' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZI_PROC_CPC_1', fieldName: '10인조이하', header: { text: '10인조이하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZI_PROC_CPC_2', fieldName: '11~20', header: { text: '11~20' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZI_PROC_CPC_3', fieldName: '21~30', header: { text: '21~30' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZI_PROC_CPC_4', fieldName: '31~50', header: { text: '31~50' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZI_PROC_CPC_5', fieldName: '51~199', header: { text: '51~199' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZI_PROC_CPC_6', fieldName: '200~499', header: { text: '200~499' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZI_PROC_CPC_7', fieldName: '500~999', header: { text: '500~999' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZI_PROC_CPC_8', fieldName: '1,000 이상', header: { text: '1,000 이상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{
name: 'staaInsdPrcsCpctByWhol',
fieldName: 'staaInsdPrcsCpctByWhol',
header: { text: '계(C)' },
editable: false,
footer: footerInt,
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
styleName: 'rg-cell-total',
computed: {
fields: [
'step1StaaInsdNope',
'step2StaaInsdNope',
'step3StaaInsdNope',
'step4StaaInsdNope',
'step5StaaInsdNope',
'step6StaaInsdNope',
'step7StaaInsdNope',
'step8StaaInsdNope',
],
type: 'sum' as const,
},
},
{ name: 'step1StaaInsdNope', fieldName: 'step1StaaInsdNope', header: { text: '10인조이하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step2StaaInsdNope', fieldName: 'step2StaaInsdNope', header: { text: '11~20' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step3StaaInsdNope', fieldName: 'step3StaaInsdNope', header: { text: '21~30' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step4StaaInsdNope', fieldName: 'step4StaaInsdNope', header: { text: '31~50' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step5StaaInsdNope', fieldName: 'step5StaaInsdNope', header: { text: '51~199' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step6StaaInsdNope', fieldName: 'step6StaaInsdNope', header: { text: '200~499' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step7StaaInsdNope', fieldName: 'step7StaaInsdNope', header: { text: '500~999' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step8StaaInsdNope', fieldName: 'step8StaaInsdNope', header: { text: '1,000 이상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 하수처리구역 외 > 총계
{ name: 'SPZO_SM', fieldName: '총계 D=(E+F)/2', header: { text: '총계 D=(E+F)/2' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{
name: 'staaExcpWhol',
fieldName: 'staaExcpWhol',
header: { text: '총계 D=(E+F)/2' },
editable: false,
footer: footerInt,
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
styleName: 'rg-cell-total',
computed: {
firstSumFields: [
'staaExcpSttkNprsflCnt',
'step1StaaExcpSttkFrcCnt',
'staaExcpSprktpFltrtbCnt',
'staaExcpAertMthdCnt',
'staaExcpCntcAertCnt',
'staaExcpEtcCnt',
],
secondSumFields: [
'step1StaaExcpNope',
'step2StaaExcpNope',
'step3StaaExcpNope',
'step4StaaExcpNope',
'step5StaaExcpNope',
'step6StaaExcpNope',
'step7StaaExcpNope',
'step8StaaExcpNope',
],
type: 'halfSumOfSums' as const,
},
},
// 하수처리구역 외 > 처리방식별
{ name: 'SPZO_MTHD_SM', fieldName: '계(E)', header: { text: '계(E)' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{
name: 'staaExcpPrcsMthByWhol',
fieldName: 'staaExcpPrcsMthByWhol',
header: { text: '계(E)' },
editable: false,
footer: footerInt,
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
styleName: 'rg-cell-total',
computed: {
fields: [
'staaExcpSttkNprsflCnt',
'step1StaaExcpSttkFrcCnt',
'staaExcpSprktpFltrtbCnt',
'staaExcpAertMthdCnt',
'staaExcpCntcAertCnt',
'staaExcpEtcCnt',
],
type: 'sum' as const,
},
},
// 하수처리구역 외 > 처리방식별 > 부패탱크
{ name: 'SPZO_ROT_NPFL_CNT', fieldName: '자연유하', header: { text: '자연유하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZO_ROT_ENFCT_PUMP_1', fieldName: '강제펌핑식', header: { text: '강제펌핑식' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'staaExcpSttkNprsflCnt', fieldName: 'staaExcpSttkNprsflCnt', header: { text: '자연유하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step1StaaExcpSttkFrcCnt', fieldName: 'step1StaaExcpSttkFrcCnt', header: { text: '강제펌핑식' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 하수처리구역 외 > 처리방식별
{ name: 'SPZO_WTROT_NPFL_CNT', fieldName: '살수여상', header: { text: '살수여상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZO_TFM', fieldName: '폭기방법', header: { text: '폭기방법' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZO_ARTM', fieldName: '접촉폭기', header: { text: '접촉폭기' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZO_CTARM', fieldName: '기타', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'staaExcpSprktpFltrtbCnt', fieldName: 'staaExcpSprktpFltrtbCnt', header: { text: '살수여상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'staaExcpAertMthdCnt', fieldName: 'staaExcpAertMthdCnt', header: { text: '폭기방법' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'staaExcpCntcAertCnt', fieldName: 'staaExcpCntcAertCnt', header: { text: '접촉폭기' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'staaExcpEtcCnt', fieldName: 'staaExcpEtcCnt', header: { text: '기타' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 하수처리구역 외 > 처리용량별
{ name: 'SPZO_PROC_CPC_SM', fieldName: '계(F)', header: { text: '계(F)' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZO_PROC_CPC_1', fieldName: '10인조이하', header: { text: '10인조이하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZO_PROC_CPC_2', fieldName: '11~20', header: { text: '11~20' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZO_PROC_CPC_3', fieldName: '21~30', header: { text: '21~30' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZO_PROC_CPC_4', fieldName: '31~50', header: { text: '31~50' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZO_PROC_CPC_5', fieldName: '51~199', header: { text: '51~199' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZO_PROC_CPC_6', fieldName: '200~499', header: { text: '200~499' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZO_PROC_CPC_7', fieldName: '500~999', header: { text: '500~999' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SPZO_PROC_CPC_8', fieldName: '1,000 이상', header: { text: '1,000 이상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{
name: 'staaExcpPrcsCpctByWhol',
fieldName: 'staaExcpPrcsCpctByWhol',
header: { text: '계(F)' },
editable: false,
footer: footerInt,
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
styleName: 'rg-cell-total',
computed: {
fields: [
'step1StaaExcpNope',
'step2StaaExcpNope',
'step3StaaExcpNope',
'step4StaaExcpNope',
'step5StaaExcpNope',
'step6StaaExcpNope',
'step7StaaExcpNope',
'step8StaaExcpNope',
],
type: 'sum' as const,
},
},
{ name: 'step1StaaExcpNope', fieldName: 'step1StaaExcpNope', header: { text: '10인조이하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step2StaaExcpNope', fieldName: 'step2StaaExcpNope', header: { text: '11~20' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step3StaaExcpNope', fieldName: 'step3StaaExcpNope', header: { text: '21~30' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step4StaaExcpNope', fieldName: 'step4StaaExcpNope', header: { text: '31~50' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step5StaaExcpNope', fieldName: 'step5StaaExcpNope', header: { text: '51~199' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step6StaaExcpNope', fieldName: 'step6StaaExcpNope', header: { text: '200~499' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step7StaaExcpNope', fieldName: 'step7StaaExcpNope', header: { text: '500~999' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step8StaaExcpNope', fieldName: 'step8StaaExcpNope', header: { text: '1,000 이상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 비고
{ name: 'RMK', fieldName: '비고', header: { text: '비고' } },
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
// hidden
{ name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' } },
{ name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' } },
// 검증 필드 (검증 여부 표시용 - 빨간색 표시)
{ name: 'CHK_TOT_FCLTY_CNT', fieldName: 'CHK_TOT_FCLTY_CNT', header: { text: '' } },
{ name: 'CHK_SPZI_SM', fieldName: 'CHK_SPZI_SM', header: { text: '' } },
{ name: 'CHK_SPZI_MTHD_SM', fieldName: 'CHK_SPZI_MTHD_SM', header: { text: '' } },
{ name: 'CHK_SPZI_ROT_NPFL_CNT', fieldName: 'CHK_SPZI_ROT_NPFL_CNT', header: { text: '' } },
{ name: 'CHK_SPZI_ROT_ENFCT_PUMP_1', fieldName: 'CHK_SPZI_ROT_ENFCT_PUMP_1', header: { text: '' } },
{ name: 'CHK_SPZI_ROT_ENFCT_PUMP_2', fieldName: 'CHK_SPZI_ROT_ENFCT_PUMP_2', header: { text: '' } },
{ name: 'CHK_SPZI_ROT_ENFCT_PUMP_3', fieldName: 'CHK_SPZI_ROT_ENFCT_PUMP_3', header: { text: '' } },
{ name: 'CHK_SPZI_ROT_TANK_SM', fieldName: 'CHK_SPZI_ROT_TANK_SM', header: { text: '' } },
{ name: 'CHK_SPZI_IMHF_NPFL_CNT', fieldName: 'CHK_SPZI_IMHF_NPFL_CNT', header: { text: '' } },
{ name: 'CHK_SPZI_IMHF_ENFCT_PUMP_1', fieldName: 'CHK_SPZI_IMHF_ENFCT_PUMP_1', header: { text: '' } },
{ name: 'CHK_SPZI_IMHF_ENFCT_PUMP_2', fieldName: 'CHK_SPZI_IMHF_ENFCT_PUMP_2', header: { text: '' } },
{ name: 'CHK_SPZI_IMHF_ENFCT_PUMP_3', fieldName: 'CHK_SPZI_IMHF_ENFCT_PUMP_3', header: { text: '' } },
{ name: 'CHK_SPZI_IMHF_TANK_SM', fieldName: 'CHK_SPZI_IMHF_TANK_SM', header: { text: '' } },
{ name: 'CHK_SPZI_WTROT_NPFL_CNT', fieldName: 'CHK_SPZI_WTROT_NPFL_CNT', header: { text: '' } },
{ name: 'CHK_SPZI_WTROT_ENFCT_PUMP_1', fieldName: 'CHK_SPZI_WTROT_ENFCT_PUMP_1', header: { text: '' } },
{ name: 'CHK_SPZI_WTROT_ENFCT_PUMP_2', fieldName: 'CHK_SPZI_WTROT_ENFCT_PUMP_2', header: { text: '' } },
{ name: 'CHK_SPZI_WTROT_ENFCT_PUMP_3', fieldName: 'CHK_SPZI_WTROT_ENFCT_PUMP_3', header: { text: '' } },
{ name: 'CHK_SPZI_WTROT_TANK_SM', fieldName: 'CHK_SPZI_WTROT_TANK_SM', header: { text: '' } },
{ name: 'CHK_SPZI_TFM', fieldName: 'CHK_SPZI_TFM', header: { text: '' } },
{ name: 'CHK_SPZI_ARTM', fieldName: 'CHK_SPZI_ARTM', header: { text: '' } },
{ name: 'CHK_SPZI_CTARM', fieldName: 'CHK_SPZI_CTARM', header: { text: '' } },
{ name: 'CHK_SPZI_NPFL', fieldName: 'CHK_SPZI_NPFL', header: { text: '' } },
{ name: 'CHK_SPZI_ENFCT_PUMP', fieldName: 'CHK_SPZI_ENFCT_PUMP', header: { text: '' } },
{ name: 'CHK_SPZI_PROC_CPC_SM', fieldName: 'CHK_SPZI_PROC_CPC_SM', header: { text: '' } },
{ name: 'CHK_SPZI_PROC_CPC_1', fieldName: 'CHK_SPZI_PROC_CPC_1', header: { text: '' } },
{ name: 'CHK_SPZI_PROC_CPC_2', fieldName: 'CHK_SPZI_PROC_CPC_2', header: { text: '' } },
{ name: 'CHK_SPZI_PROC_CPC_3', fieldName: 'CHK_SPZI_PROC_CPC_3', header: { text: '' } },
{ name: 'CHK_SPZI_PROC_CPC_4', fieldName: 'CHK_SPZI_PROC_CPC_4', header: { text: '' } },
{ name: 'CHK_SPZI_PROC_CPC_5', fieldName: 'CHK_SPZI_PROC_CPC_5', header: { text: '' } },
{ name: 'CHK_SPZI_PROC_CPC_6', fieldName: 'CHK_SPZI_PROC_CPC_6', header: { text: '' } },
{ name: 'CHK_SPZI_PROC_CPC_7', fieldName: 'CHK_SPZI_PROC_CPC_7', header: { text: '' } },
{ name: 'CHK_SPZI_PROC_CPC_8', fieldName: 'CHK_SPZI_PROC_CPC_8', header: { text: '' } },
{ name: 'CHK_SPZO_SM', fieldName: 'CHK_SPZO_SM', header: { text: '' } },
{ name: 'CHK_SPZO_MTHD_SM', fieldName: 'CHK_SPZO_MTHD_SM', header: { text: '' } },
{ name: 'CHK_SPZO_ROT_NPFL_CNT', fieldName: 'CHK_SPZO_ROT_NPFL_CNT', header: { text: '' } },
{ name: 'CHK_SPZO_ROT_ENFCT_PUMP_1', fieldName: 'CHK_SPZO_ROT_ENFCT_PUMP_1', header: { text: '' } },
{ name: 'CHK_SPZO_ROT_ENFCT_PUMP_2', fieldName: 'CHK_SPZO_ROT_ENFCT_PUMP_2', header: { text: '' } },
{ name: 'CHK_SPZO_ROT_ENFCT_PUMP_3', fieldName: 'CHK_SPZO_ROT_ENFCT_PUMP_3', header: { text: '' } },
{ name: 'CHK_SPZO_ROT_TANK_SM', fieldName: 'CHK_SPZO_ROT_TANK_SM', header: { text: '' } },
{ name: 'CHK_SPZO_IMHF_NPFL_CNT', fieldName: 'CHK_SPZO_IMHF_NPFL_CNT', header: { text: '' } },
{ name: 'CHK_SPZO_IMHF_ENFCT_PUMP_1', fieldName: 'CHK_SPZO_IMHF_ENFCT_PUMP_1', header: { text: '' } },
{ name: 'CHK_SPZO_IMHF_ENFCT_PUMP_2', fieldName: 'CHK_SPZO_IMHF_ENFCT_PUMP_2', header: { text: '' } },
{ name: 'CHK_SPZO_IMHF_ENFCT_PUMP_3', fieldName: 'CHK_SPZO_IMHF_ENFCT_PUMP_3', header: { text: '' } },
{ name: 'CHK_SPZO_IMHF_TANK_SM', fieldName: 'CHK_SPZO_IMHF_TANK_SM', header: { text: '' } },
{ name: 'CHK_SPZO_WTROT_NPFL_CNT', fieldName: 'CHK_SPZO_WTROT_NPFL_CNT', header: { text: '' } },
{ name: 'CHK_SPZO_WTROT_ENFCT_PUMP_1', fieldName: 'CHK_SPZO_WTROT_ENFCT_PUMP_1', header: { text: '' } },
{ name: 'CHK_SPZO_WTROT_ENFCT_PUMP_2', fieldName: 'CHK_SPZO_WTROT_ENFCT_PUMP_2', header: { text: '' } },
{ name: 'CHK_SPZO_WTROT_ENFCT_PUMP_3', fieldName: 'CHK_SPZO_WTROT_ENFCT_PUMP_3', header: { text: '' } },
{ name: 'CHK_SPZO_WTROT_TANK_SM', fieldName: 'CHK_SPZO_WTROT_TANK_SM', header: { text: '' } },
{ name: 'CHK_SPZO_TFM', fieldName: 'CHK_SPZO_TFM', header: { text: '' } },
{ name: 'CHK_SPZO_ARTM', fieldName: 'CHK_SPZO_ARTM', header: { text: '' } },
{ name: 'CHK_SPZO_CTARM', fieldName: 'CHK_SPZO_CTARM', header: { text: '' } },
{ name: 'CHK_SPZO_NPFL', fieldName: 'CHK_SPZO_NPFL', header: { text: '' } },
{ name: 'CHK_SPZO_ENFCT_PUMP', fieldName: 'CHK_SPZO_ENFCT_PUMP', header: { text: '' } },
{ name: 'CHK_SPZO_PROC_CPC_SM', fieldName: 'CHK_SPZO_PROC_CPC_SM', header: { text: '' } },
{ name: 'CHK_SPZO_PROC_CPC_1', fieldName: 'CHK_SPZO_PROC_CPC_1', header: { text: '' } },
{ name: 'CHK_SPZO_PROC_CPC_2', fieldName: 'CHK_SPZO_PROC_CPC_2', header: { text: '' } },
{ name: 'CHK_SPZO_PROC_CPC_3', fieldName: 'CHK_SPZO_PROC_CPC_3', header: { text: '' } },
{ name: 'CHK_SPZO_PROC_CPC_4', fieldName: 'CHK_SPZO_PROC_CPC_4', header: { text: '' } },
{ name: 'CHK_SPZO_PROC_CPC_5', fieldName: 'CHK_SPZO_PROC_CPC_5', header: { text: '' } },
{ name: 'CHK_SPZO_PROC_CPC_6', fieldName: 'CHK_SPZO_PROC_CPC_6', header: { text: '' } },
{ name: 'CHK_SPZO_PROC_CPC_7', fieldName: 'CHK_SPZO_PROC_CPC_7', header: { text: '' } },
{ name: 'CHK_SPZO_PROC_CPC_8', fieldName: 'CHK_SPZO_PROC_CPC_8', header: { text: '' } },
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, visible: false },
],
columnLayout: [
{
name: 'group_admin',
header: { text: '행정구역' },
items: ['SIDO', 'GUGUN']
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
'TOT_FCLTY_CNT',
'grssFcltCnt',
{
name: 'group_spzi',
header: { text: '하수처리구역 내' },
items: [
'SPZI_SM',
'staaInsdWhol',
{
name: 'group_spzi_mthd',
header: { text: '처리방식별' },
items: [
'SPZI_MTHD_SM',
'staaInsdPrcsMthByWhol',
{
name: 'group_spzi_rot',
header: { text: '부패탱크' },
items: [
'SPZI_ROT_NPFL_CNT',
'SPZI_ROT_ENFCT_PUMP_1'
'staaInsdSttkNprsflCnt',
'step1StaaInsdSttkFrcCnt'
]
},
'SPZI_WTROT_NPFL_CNT',
'SPZI_TFM',
'SPZI_ARTM',
'SPZI_CTARM'
'staaInsdSprktpFltrtbCnt',
'staaInsdAertMthdCnt',
'staaInsdCntcAertCnt',
'staaInsdEtcCnt'
]
},
{
name: 'group_spzi_proc',
header: { text: '처리용량별' },
items: [
'SPZI_PROC_CPC_SM',
'SPZI_PROC_CPC_1',
'SPZI_PROC_CPC_2',
'SPZI_PROC_CPC_3',
'SPZI_PROC_CPC_4',
'SPZI_PROC_CPC_5',
'SPZI_PROC_CPC_6',
'SPZI_PROC_CPC_7',
'SPZI_PROC_CPC_8'
'staaInsdPrcsCpctByWhol',
'step1StaaInsdNope',
'step2StaaInsdNope',
'step3StaaInsdNope',
'step4StaaInsdNope',
'step5StaaInsdNope',
'step6StaaInsdNope',
'step7StaaInsdNope',
'step8StaaInsdNope'
]
}
]
@@ -189,47 +323,49 @@ export const SepticTankStatusInpColumn = {
name: 'group_spzo',
header: { text: '하수처리구역 외' },
items: [
'SPZO_SM',
'staaExcpWhol',
{
name: 'group_spzo_mthd',
header: { text: '처리방식별' },
items: [
'SPZO_MTHD_SM',
'staaExcpPrcsMthByWhol',
{
name: 'group_spzo_rot',
header: { text: '부패탱크' },
items: [
'SPZO_ROT_NPFL_CNT',
'SPZO_ROT_ENFCT_PUMP_1'
'staaExcpSttkNprsflCnt',
'step1StaaExcpSttkFrcCnt'
]
},
'SPZO_WTROT_NPFL_CNT',
'SPZO_TFM',
'SPZO_ARTM',
'SPZO_CTARM'
'staaExcpSprktpFltrtbCnt',
'staaExcpAertMthdCnt',
'staaExcpCntcAertCnt',
'staaExcpEtcCnt'
]
},
{
name: 'group_spzo_proc',
header: { text: '처리용량별' },
items: [
'SPZO_PROC_CPC_SM',
'SPZO_PROC_CPC_1',
'SPZO_PROC_CPC_2',
'SPZO_PROC_CPC_3',
'SPZO_PROC_CPC_4',
'SPZO_PROC_CPC_5',
'SPZO_PROC_CPC_6',
'SPZO_PROC_CPC_7',
'SPZO_PROC_CPC_8'
'staaExcpPrcsCpctByWhol',
'step1StaaExcpNope',
'step2StaaExcpNope',
'step3StaaExcpNope',
'step4StaaExcpNope',
'step5StaaExcpNope',
'step6StaaExcpNope',
'step7StaaExcpNope',
'step8StaaExcpNope'
]
}
]
},
'RMK'
'rmrk',
'admdstCd',
'admdstNm'
],
rowStartNum: 4,
fixedColumns: 1,
tabKey: 'septicTankStatusInp',
tabKey: 'ST00080002',
tabLabel: '정화조 현황'
};
@@ -5,65 +5,119 @@ import { ValueType } from "realgrid";
export const SewageTreatFactStatusInpColumn = {
columns: [
// 행정구역
{ name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { 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: 'STTY_SM', fieldName: '총계', header: { text: '총계' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{
name: 'wholDrtwPrcsFcltCnt',
fieldName: 'wholDrtwPrcsFcltCnt',
header: { text: '총계' },
editable: false,
footer: footerInt,
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
styleName: 'rg-cell-total',
computed: {
fields: [
'step1StaaPrcsQnt',
'step2StaaPrcsQnt',
'step3StaaPrcsQnt',
'step4StaaPrcsQnt',
'step5StaaPrcsQnt',
'step6StaaPrcsQnt',
'step7StaaPrcsQnt',
'step1StaaExcpPrcsQnt',
'step2StaaExcpPrcsQnt',
'step3StaaExcpPrcsQnt',
'step4StaaExcpPrcsQnt',
'step5StaaExcpPrcsQnt',
'step6StaaExcpPrcsQnt',
'step7StaaExcpPrcsQnt',
],
type: 'sum' as const,
},
},
// 오수처리시설 > 하수처리구역 내(m³/일)
{ name: 'STTY_ZONE_INNER_SM', fieldName: '계', header: { text: '계' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_ZONE_INNER_2', fieldName: '2이하', header: { text: '2이하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_ZONE_INNER_3', fieldName: '3~10', header: { text: '3~10' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_ZONE_INNER_11', fieldName: '11~20', header: { text: '11~20' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_ZONE_INNER_21', fieldName: '21~50', header: { text: '21~50' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_ZONE_INNER_51', fieldName: '51~100', header: { text: '51~100' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_ZONE_INNER_101', fieldName: '101~500', header: { text: '101~500' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_ZONE_INNER_501', fieldName: '501이상', header: { text: '501이상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{
name: 'wholStaaCnt',
fieldName: 'wholStaaCnt',
header: { text: '계' },
editable: false,
footer: footerInt,
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
styleName: 'rg-cell-total',
computed: {
fields: [
'step1StaaPrcsQnt',
'step2StaaPrcsQnt',
'step3StaaPrcsQnt',
'step4StaaPrcsQnt',
'step5StaaPrcsQnt',
'step6StaaPrcsQnt',
'step7StaaPrcsQnt',
],
type: 'sum' as const,
},
},
{ name: 'step1StaaPrcsQnt', fieldName: 'step1StaaPrcsQnt', header: { text: '2이하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step2StaaPrcsQnt', fieldName: 'step2StaaPrcsQnt', header: { text: '3~10' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step3StaaPrcsQnt', fieldName: 'step3StaaPrcsQnt', header: { text: '11~20' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step4StaaPrcsQnt', fieldName: 'step4StaaPrcsQnt', header: { text: '21~50' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step5StaaPrcsQnt', fieldName: 'step5StaaPrcsQnt', header: { text: '51~100' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step6StaaPrcsQnt', fieldName: 'step6StaaPrcsQnt', header: { text: '101~500' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step7StaaPrcsQnt', fieldName: 'step7StaaPrcsQnt', header: { text: '501이상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 오수처리시설 > 하수처리구역 외(m³/일)
{ name: 'STTY_ZONE_OUTER_SM', fieldName: '계', header: { text: '계' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_ZONE_OUTER_2', fieldName: '2이하', header: { text: '2이하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_ZONE_OUTER_3', fieldName: '3~10', header: { text: '3~10' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_ZONE_OUTER_11', fieldName: '11~20', header: { text: '11~20' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_ZONE_OUTER_21', fieldName: '21~50', header: { text: '21~50' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_ZONE_OUTER_51', fieldName: '51~100', header: { text: '51~100' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_ZONE_OUTER_101', fieldName: '101~500', header: { text: '101~500' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'STTY_ZONE_OUTER_501', fieldName: '501이상', header: { text: '501이상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{
name: 'staaExcpWhol',
fieldName: 'staaExcpWhol',
header: { text: '계' },
editable: false,
footer: footerInt,
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
styleName: 'rg-cell-total',
computed: {
fields: [
'step1StaaExcpPrcsQnt',
'step2StaaExcpPrcsQnt',
'step3StaaExcpPrcsQnt',
'step4StaaExcpPrcsQnt',
'step5StaaExcpPrcsQnt',
'step6StaaExcpPrcsQnt',
'step7StaaExcpPrcsQnt',
],
type: 'sum' as const,
},
},
{ name: 'step1StaaExcpPrcsQnt', fieldName: 'step1StaaExcpPrcsQnt', header: { text: '2이하' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step2StaaExcpPrcsQnt', fieldName: 'step2StaaExcpPrcsQnt', header: { text: '3~10' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step3StaaExcpPrcsQnt', fieldName: 'step3StaaExcpPrcsQnt', header: { text: '11~20' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step4StaaExcpPrcsQnt', fieldName: 'step4StaaExcpPrcsQnt', header: { text: '21~50' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step5StaaExcpPrcsQnt', fieldName: 'step5StaaExcpPrcsQnt', header: { text: '51~100' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step6StaaExcpPrcsQnt', fieldName: 'step6StaaExcpPrcsQnt', header: { text: '101~500' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'step7StaaExcpPrcsQnt', fieldName: 'step7StaaExcpPrcsQnt', header: { text: '501이상' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
// 비고
{ name: 'RMK', fieldName: '비고', header: { text: '비고' } },
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
// hidden
{ name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' } },
{ name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' } },
// 검증 필드 (검증 여부 표시용, 빨간색 표시)
{ name: 'CHK_STTY_SM', fieldName: 'CHK_STTY_SM', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_INNER_SM', fieldName: 'CHK_STTY_ZONE_INNER_SM', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_INNER_2', fieldName: 'CHK_STTY_ZONE_INNER_2', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_INNER_3', fieldName: 'CHK_STTY_ZONE_INNER_3', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_INNER_11', fieldName: 'CHK_STTY_ZONE_INNER_11', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_INNER_21', fieldName: 'CHK_STTY_ZONE_INNER_21', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_INNER_51', fieldName: 'CHK_STTY_ZONE_INNER_51', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_INNER_101', fieldName: 'CHK_STTY_ZONE_INNER_101', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_INNER_501', fieldName: 'CHK_STTY_ZONE_INNER_501', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_OUTER_SM', fieldName: 'CHK_STTY_ZONE_OUTER_SM', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_OUTER_2', fieldName: 'CHK_STTY_ZONE_OUTER_2', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_OUTER_3', fieldName: 'CHK_STTY_ZONE_OUTER_3', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_OUTER_11', fieldName: 'CHK_STTY_ZONE_OUTER_11', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_OUTER_21', fieldName: 'CHK_STTY_ZONE_OUTER_21', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_OUTER_51', fieldName: 'CHK_STTY_ZONE_OUTER_51', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_OUTER_101', fieldName: 'CHK_STTY_ZONE_OUTER_101', header: { text: '' } },
{ name: 'CHK_STTY_ZONE_OUTER_501', fieldName: 'CHK_STTY_ZONE_OUTER_501', header: { text: '' } },
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, visible: false },
],
columnLayout: [
{
name: 'group_admin',
header: { text: '행정구역' },
items: ['SIDO', 'GUGUN']
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
'STTY_SM',
'wholDrtwPrcsFcltCnt',
{
name: 'group_stty',
header: { text: '오수처리시설' },
@@ -72,36 +126,38 @@ export const SewageTreatFactStatusInpColumn = {
name: 'group_inner',
header: { text: '하수처리구역 내(m³/일)' },
items: [
'STTY_ZONE_INNER_SM',
'STTY_ZONE_INNER_2',
'STTY_ZONE_INNER_3',
'STTY_ZONE_INNER_11',
'STTY_ZONE_INNER_21',
'STTY_ZONE_INNER_51',
'STTY_ZONE_INNER_101',
'STTY_ZONE_INNER_501'
'wholStaaCnt',
'step1StaaPrcsQnt',
'step2StaaPrcsQnt',
'step3StaaPrcsQnt',
'step4StaaPrcsQnt',
'step5StaaPrcsQnt',
'step6StaaPrcsQnt',
'step7StaaPrcsQnt'
]
},
{
name: 'group_outer',
header: { text: '하수처리구역 외(m³/일)' },
items: [
'STTY_ZONE_OUTER_SM',
'STTY_ZONE_OUTER_2',
'STTY_ZONE_OUTER_3',
'STTY_ZONE_OUTER_11',
'STTY_ZONE_OUTER_21',
'STTY_ZONE_OUTER_51',
'STTY_ZONE_OUTER_101',
'STTY_ZONE_OUTER_501'
'staaExcpWhol',
'step1StaaExcpPrcsQnt',
'step2StaaExcpPrcsQnt',
'step3StaaExcpPrcsQnt',
'step4StaaExcpPrcsQnt',
'step5StaaExcpPrcsQnt',
'step6StaaExcpPrcsQnt',
'step7StaaExcpPrcsQnt'
]
}
]
},
'RMK'
'rmrk',
'admdstCd',
'admdstNm'
],
rowStartNum: 3,
fixedColumns: 1,
tabKey: 'sewageTreatFactStatusInp',
tabKey: 'ST00080001',
tabLabel: '처리용량별 오수처리시설 현황'
};
@@ -5,46 +5,72 @@ import { ValueType } from "realgrid";
export const CauserBurdenInpColumn = {
columns: [
// 행정구역
{ name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } },
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, footer: { text: '합계' }, editable: false },
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' } },
// 부과액
{ name: 'LEVY_CO', fieldName: '건수', header: { text: '건수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'LEVY_AMOUNT', fieldName: '금액(백만원)', header: { text: '금액(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'levyEvntcsCnt', fieldName: 'levyEvntcsCnt', header: { text: '건수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'levyAmt', fieldName: 'levyAmt', header: { text: '금액(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 대상별 징수현황 > 계
{ name: 'TRGET_LEV_STTUS_CO', fieldName: '건수', header: { text: '건수' }, editable: false, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'TRGET_LEV_STTUS_AMOUNT', fieldName: '금액(백만원)', header: { text: '금액(백만원)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'trgtByWholClctnEvntcsCnt',
fieldName: 'trgtByWholClctnEvntcsCnt',
header: { text: '건수' },
editable: false,
footer: footerInt,
styleName: 'rg-cell-total',
valueType: ValueType.NUMBER,
dataFormat: '#,##0',
computed: {
fields: ['mltvEqpmEvntcsCnt', 'othrCstrnOthrActrCnt'],
type: 'sum' as const,
},
},
{
name: 'trgtByWholClctnAmt',
fieldName: 'trgtByWholClctnAmt',
header: { text: '금액(백만원)' },
editable: false,
footer: footerDecimal1,
styleName: 'rg-cell-total',
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
computed: {
fields: ['mltvEqpmAmt', 'othrCstrnOthrActrAmt'],
type: 'sum' as const,
},
},
// 대상별 징수현황 > 배수설비설치자
{ name: 'DRNG_EQP_CO', fieldName: '건수', header: { text: '건수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'DRNG_EQP_AMOUNT', fieldName: '금액(백만원)', header: { text: '금액(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'mltvEqpmEvntcsCnt', fieldName: 'mltvEqpmEvntcsCnt', header: { text: '건수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'mltvEqpmAmt', fieldName: 'mltvEqpmAmt', header: { text: '금액(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 대상별 징수현황 > 타공사·타행위자
{ name: 'CNTRWK_CO', fieldName: '건수', header: { text: '건수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'CNTRWK_AMOUNT', fieldName: '금액(백만원)', header: { text: '금액(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'othrCstrnOthrActrCnt', fieldName: 'othrCstrnOthrActrCnt', header: { text: '건수' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'othrCstrnOthrActrAmt', fieldName: 'othrCstrnOthrActrAmt', header: { text: '금액(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 비고
{ name: 'RMK', fieldName: '비고', header: { text: '비고' } },
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
// hidden
{ name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' } },
{ name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' } },
// 참조 필드
{ name: 'AMNTBRNCSR', fieldName: 'AMNTBRNCSR', header: { text: 'AMNTBRNCSR' } },
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, visible: false },
],
columnLayout: [
{
name: 'group_admin',
header: { text: '행정구역' },
items: ['SIDO', 'GUGUN']
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
{
name: 'group_assess',
header: { text: '부과액' },
items: ['LEVY_CO', 'LEVY_AMOUNT']
items: ['levyEvntcsCnt', 'levyAmt']
},
{
name: 'group_collection',
@@ -53,25 +79,27 @@ export const CauserBurdenInpColumn = {
{
name: 'coll_group_total',
header: { text: '계' },
items: ['TRGET_LEV_STTUS_CO', 'TRGET_LEV_STTUS_AMOUNT']
items: ['trgtByWholClctnEvntcsCnt', 'trgtByWholClctnAmt']
},
{
name: 'coll_group_inst',
header: { text: '배수설비설치자(하수도법 제61조 제1항)' },
items: ['DRNG_EQP_CO', 'DRNG_EQP_AMOUNT']
items: ['mltvEqpmEvntcsCnt', 'mltvEqpmAmt']
},
{
name: 'coll_group_otr',
header: { text: '타공사·타행위자(하수도법 제61조 제2항)' },
items: ['CNTRWK_CO', 'CNTRWK_AMOUNT']
items: ['othrCstrnOthrActrCnt', 'othrCstrnOthrActrAmt']
}
]
},
'RMK'
'rmrk',
'admdstCd',
'admdstNm'
],
rowStartNum: 3,
fixedColumns: 1,
tabKey: 'causerBurdenInp',
tabKey: 'ST00090004',
tabLabel: '원인자 부담금'
};
@@ -5,45 +5,86 @@ import { ValueType } from "realgrid";
export const SewerageChargeInpColumn = {
columns: [
// 행정구역
{ name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } },
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, footer: { text: '합계' }, editable: false },
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' } },
// 요금
{ name: 'FYER_LEVY_QY', fieldName: '연간조정량(m³)(A)', header: { text: '연간조정량(m³)(A)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'LEVY_AMOUNT', fieldName: '하수도사용료수익(원)(B)', header: { text: '하수도사용료수익(원)(B)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'AVRG_UNTPC', fieldName: 'm³당요금(원/m³) C=(B/A)', header: { text: 'm³당요금(원/m³) C=(B/A)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'anlLevyQnt', fieldName: 'anlLevyQnt', header: { text: '연간조정량(m³)(A)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'levyAmt', fieldName: 'levyAmt', header: { text: '하수도사용료수익(원)(B)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{
name: 'avgUntprcAmt',
fieldName: 'avgUntprcAmt',
header: { text: 'm³당요금(원/m³) C=(B/A)' },
editable: false,
footer: footerDecimal1,
styleName: 'rg-cell-total',
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
computed: {
fields: ['levyAmt', 'anlLevyQnt'],
type: 'quotient' as const,
},
},
{ name: 'SMRIZE_PRMPC', fieldName: '총괄원가(원)(D)', header: { text: '총괄원가(원)(D)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{ name: 'SMRIZE_UNIT_PRMPC', fieldName: 'm³당원가(원/m³) E=(D/A)', header: { text: 'm³당원가(원/m³) E=(D/A)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'RLTY_RT', fieldName: '요금현실화율(%) F=(C/E*100)', header: { text: '요금현실화율(%) F=(C/E*100)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'grssPrmcoAmt', fieldName: 'grssPrmcoAmt', header: { text: '총괄원가(원)(D)' }, footer: footerInt, valueType: ValueType.NUMBER, dataFormat: '#,##0' },
{
name: 'grssUnitPrmcoAmt',
fieldName: 'grssUnitPrmcoAmt',
header: { text: 'm³당원가(원/m³) E=(D/A)' },
editable: false,
footer: footerDecimal1,
styleName: 'rg-cell-total',
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
computed: {
fields: ['grssPrmcoAmt', 'anlLevyQnt'],
type: 'quotient' as const,
},
},
{
name: 'rlztnRt',
fieldName: 'rlztnRt',
header: { text: '요금현실화율(%) F=(C/E*100)' },
editable: false,
footer: footerDecimal1,
styleName: 'rg-cell-total',
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
computed: {
numeratorQuotient: ['levyAmt', 'anlLevyQnt'] as [string, string],
denominatorQuotient: ['grssPrmcoAmt', 'anlLevyQnt'] as [string, string],
type: 'quotientRatioPercent' as const,
},
},
// 비고
{ name: 'RMK', fieldName: '비고', header: { text: '비고' } },
{ name: 'rmrk', fieldName: '비고', header: { text: '비고' } },
// hidden
{ name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 참조 필드
{ name: 'SEWER_RNTFEE', fieldName: 'SEWER_RNTFEE', header: { text: 'SEWER_RNTFEE' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'C_LEVY_AMOUNT', fieldName: 'C_LEVY_AMOUNT', header: { text: 'C_LEVY_AMOUNT' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, visible: false },
],
columnLayout: [
{
name: 'group_admin',
header: { text: '행정구역' },
items: ['SIDO', 'GUGUN']
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
'FYER_LEVY_QY',
'LEVY_AMOUNT',
'AVRG_UNTPC',
'SMRIZE_PRMPC',
'SMRIZE_UNIT_PRMPC',
'RLTY_RT',
'RMK'
'anlLevyQnt',
'levyAmt',
'avgUntprcAmt',
'grssPrmcoAmt',
'grssUnitPrmcoAmt',
'rlztnRt',
'rmrk',
'admdstCd',
'admdstNm'
],
rowStartNum: 2,
fixedColumns: 1,
tabKey: 'sewerageChargeInp',
tabKey: 'ST00090002',
tabLabel: '하수도 요금'
};
@@ -5,146 +5,238 @@ import { ValueType } from "realgrid";
export const SewerageFinanceSettleInpColumn = {
columns: [
// 행정구역
{ name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } },
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, footer: { text: '합계' }, editable: false },
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' } },
// 재정별 사업비
{ name: 'FNANC_WCT_SM', fieldName: '계', header: { text: '계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'NTNXPN', fieldName: '중앙정부지원액', header: { text: '중앙정부지원액' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'fnnByWholPjtcoAmt',
fieldName: 'fnnByWholPjtcoAmt',
header: { text: '계' },
editable: false,
footer: footerDecimal1,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
fields: [
'cnterGovSprtAmt',
'mctpvLcexpAmt',
'wareaSggLclAmt',
'sewerUseAmt',
'orgprBrdnAmt',
'etcIncmAmt',
'crfwAmt',
],
type: 'sum' as const,
},
},
{ name: 'cnterGovSprtAmt', fieldName: 'cnterGovSprtAmt', header: { text: '중앙정부지원액' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 재정별 사업비 > 지방비
{ name: 'LCLEXPNS', fieldName: '특·광역시, 도', header: { text: '특·광역시, 도' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'LCLTY_CT', fieldName: '시,군,구 지자체', header: { text: '시,군,구 지자체' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'mctpvLcexpAmt', fieldName: 'mctpvLcexpAmt', header: { text: '특·광역시, 도' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'wareaSggLclAmt', fieldName: 'wareaSggLclAmt', header: { text: '시,군,구 지자체' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 재정별 사업비
{ name: 'SEWER_RNTFEE', fieldName: '하수도사용료', header: { text: '하수도사용료' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'AMNTBRNCSR', fieldName: '원인자부담금', header: { text: '원인자부담금' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'ETC_INCME', fieldName: '기타잡수입', header: { text: '기타잡수입' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'CYFD_AMOUNT', fieldName: '이월액', header: { text: '이월액' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'sewerUseAmt', fieldName: 'sewerUseAmt', header: { text: '하수도사용료' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'orgprBrdnAmt', fieldName: 'orgprBrdnAmt', header: { text: '원인자부담금' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'etcIncmAmt', fieldName: 'etcIncmAmt', header: { text: '기타잡수입' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'crfwAmt', fieldName: 'crfwAmt', header: { text: '이월액' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 부채액
{ name: 'DEBT_AMOUNT', fieldName: '부채액(백만원)', header: { text: '부채액(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'lbltAmt', fieldName: 'lbltAmt', header: { text: '부채액(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 용도별사업비 > 계
{ name: 'PRPOS_WCT_SM', fieldName: '계', header: { text: '계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'usgByWholPjtcoAmt',
fieldName: 'usgByWholPjtcoAmt',
header: { text: '계' },
editable: false,
footer: footerDecimal1,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
},
// 용도별사업비 > 처리장
{ name: 'PRCPLT_SM', fieldName: '소계', header: { text: '소계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'PRCPLT_FCLTY', fieldName: '시설비', header: { text: '시설비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'PRCPLT_RPAIR', fieldName: '개보수비', header: { text: '개보수비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'PRCPLT_ORPNS', fieldName: '운영유지비', header: { text: '운영유지비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'PRCPLT_ETC', fieldName: '기타', header: { text: '기타' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'tmplntWholAmt',
fieldName: 'tmplntWholAmt',
header: { text: '소계' },
editable: false,
footer: footerDecimal1,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
fields: ['tmplntFcltAmt', 'tmplntMendngAmt', 'tmplntOperKeepAmt', 'tmplntEtcAmt'],
type: 'sum' as const,
},
},
{ name: 'tmplntFcltAmt', fieldName: 'tmplntFcltAmt', header: { text: '시설비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'tmplntMendngAmt', fieldName: 'tmplntMendngAmt', header: { text: '개보수비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'tmplntOperKeepAmt', fieldName: 'tmplntOperKeepAmt', header: { text: '운영유지비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'tmplntEtcAmt', fieldName: 'tmplntEtcAmt', header: { text: '기타' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 용도별사업비 > 하수관로
{ name: 'SEWAGE_SM', fieldName: '소계', header: { text: '소계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'SEWAGE_FCLTY', fieldName: '시설비', header: { text: '시설비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'SEWAGE_RPAIR', fieldName: '개보수비', header: { text: '개보수비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'SEWAGE_ORPNS', fieldName: '운영유지비', header: { text: '운영유지비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'SEWAGE_ETC', fieldName: '기타', header: { text: '기타' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'wholSwagePplAmt',
fieldName: 'wholSwagePplAmt',
header: { text: '소계' },
editable: false,
footer: footerDecimal1,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
fields: [
'swagePplFcltAmt',
'swagePplMendngAmt',
'swagePplOperKeepAmt',
'swagePplEtcAmt',
],
type: 'sum' as const,
},
},
{ name: 'swagePplFcltAmt', fieldName: 'swagePplFcltAmt', header: { text: '시설비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'swagePplMendngAmt', fieldName: 'swagePplMendngAmt', header: { text: '개보수비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'swagePplOperKeepAmt', fieldName: 'swagePplOperKeepAmt', header: { text: '운영유지비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'swagePplEtcAmt', fieldName: 'swagePplEtcAmt', header: { text: '기타' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 용도별사업비 > 하수저류시설
{ name: 'SWGE_STRGE_SM', fieldName: '소계', header: { text: '소계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'SWGE_STRGE_FCLTY', fieldName: '시설비', header: { text: '시설비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'SWGE_STRGE_RPAIR', fieldName: '개보수비', header: { text: '개보수비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'SWGE_STRGE_ORPNS', fieldName: '운영유지비', header: { text: '운영유지비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'SWGE_STRGE_ETC', fieldName: '기타', header: { text: '기타' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'wholSwageImpdFcltAmt',
fieldName: 'wholSwageImpdFcltAmt',
header: { text: '소계' },
editable: false,
footer: footerDecimal1,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
fields: [
'swageImpdFcltAmt',
'swageImpdMendngAmt',
'swageImpdOperKeepAmt',
'swageImpdFcltEtcAmt',
],
type: 'sum' as const,
},
},
{ name: 'swageImpdFcltAmt', fieldName: 'swageImpdFcltAmt', header: { text: '시설비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'swageImpdMendngAmt', fieldName: 'swageImpdMendngAmt', header: { text: '개보수비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'swageImpdOperKeepAmt', fieldName: 'swageImpdOperKeepAmt', header: { text: '운영유지비' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'swageImpdFcltEtcAmt', fieldName: 'swageImpdFcltEtcAmt', header: { text: '기타' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 용도별사업비 > 기타
{ name: 'ETC_SM', fieldName: '소계', header: { text: '소계' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'ACDNT_CYFD_AMOUNT', fieldName: '사고이월액', header: { text: '사고이월액' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'UNRCIV_AMOUNT', fieldName: '미수납액', header: { text: '미수납액' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'ETC_AMOUNT', fieldName: '기타', header: { text: '기타' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'etcWholAmt',
fieldName: 'etcWholAmt',
header: { text: '소계' },
editable: false,
footer: footerDecimal1,
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
styleName: 'rg-cell-total',
computed: {
fields: ['etcAcdntCrfwAmt', 'etcNrcpAmt', 'etcAmt'],
type: 'sum' as const,
},
},
{ name: 'etcAcdntCrfwAmt', fieldName: 'etcAcdntCrfwAmt', header: { text: '사고이월액' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'etcNrcpAmt', fieldName: 'etcNrcpAmt', header: { text: '미수납액' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'etcAmt', fieldName: 'etcAmt', header: { text: '기타' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 비고
{ name: 'RMK', fieldName: '비고', header: { text: '비고' } },
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
// hidden
{ name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 참조 필드
{ name: 'B_LEVY_AMOUNT', fieldName: 'B_LEVY_AMOUNT', header: { text: 'B_LEVY_AMOUNT' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'C_LEVY_AMOUNT', fieldName: 'C_LEVY_AMOUNT', header: { text: 'C_LEVY_AMOUNT' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'D_LEVY_AMOUNT', fieldName: 'D_LEVY_AMOUNT', header: { text: 'D_LEVY_AMOUNT' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, visible: false },
],
columnLayout: [
{
name: 'group_admin',
header: { text: '행정구역' },
items: ['SIDO', 'GUGUN']
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
{
name: 'group_finance',
header: { text: '재정별 사업비(백만원)' },
items: [
'FNANC_WCT_SM',
'NTNXPN',
'fnnByWholPjtcoAmt',
'cnterGovSprtAmt',
{
name: 'group_local_finance',
header: { text: '지방비' },
items: ['LCLEXPNS', 'LCLTY_CT']
items: ['mctpvLcexpAmt', 'wareaSggLclAmt']
},
'SEWER_RNTFEE',
'AMNTBRNCSR',
'ETC_INCME',
'CYFD_AMOUNT'
'sewerUseAmt',
'orgprBrdnAmt',
'etcIncmAmt',
'crfwAmt'
]
},
'DEBT_AMOUNT',
'lbltAmt',
{
name: 'group_purpose',
header: { text: '용도별사업비(백만원)' },
items: [
'PRPOS_WCT_SM',
'usgByWholPjtcoAmt',
{
name: 'group_treatment',
header: { text: '처리장' },
items: [
'PRCPLT_SM',
'PRCPLT_FCLTY',
'PRCPLT_RPAIR',
'PRCPLT_ORPNS',
'PRCPLT_ETC'
'tmplntWholAmt',
'tmplntFcltAmt',
'tmplntMendngAmt',
'tmplntOperKeepAmt',
'tmplntEtcAmt'
]
},
{
name: 'group_pipe',
header: { text: '하수관로' },
items: [
'SEWAGE_SM',
'SEWAGE_FCLTY',
'SEWAGE_RPAIR',
'SEWAGE_ORPNS',
'SEWAGE_ETC'
'wholSwagePplAmt',
'swagePplFcltAmt',
'swagePplMendngAmt',
'swagePplOperKeepAmt',
'swagePplEtcAmt'
]
},
{
name: 'group_storage',
header: { text: '하수저류시설' },
items: [
'SWGE_STRGE_SM',
'SWGE_STRGE_FCLTY',
'SWGE_STRGE_RPAIR',
'SWGE_STRGE_ORPNS',
'SWGE_STRGE_ETC'
'wholSwageImpdFcltAmt',
'swageImpdFcltAmt',
'swageImpdMendngAmt',
'swageImpdOperKeepAmt',
'swageImpdFcltEtcAmt'
]
},
{
name: 'group_etc_purpose',
header: { text: '기타' },
items: [
'ETC_SM',
'ACDNT_CYFD_AMOUNT',
'UNRCIV_AMOUNT',
'ETC_AMOUNT'
'etcWholAmt',
'etcAcdntCrfwAmt',
'etcNrcpAmt',
'etcAmt'
]
}
]
},
'RMK'
'rmrk',
'admdstCd',
'admdstNm'
],
rowStartNum: 3,
fixedColumns: 1,
tabKey: 'sewerageFinanceSettleInp',
tabKey: 'ST00090001',
tabLabel: '하수도재정 결산'
};
@@ -5,67 +5,108 @@ import { ValueType } from "realgrid";
export const SewerageUsageFeeInpColumn = {
columns: [
// 행정구역
{ name: 'SIDO', fieldName: '시도', header: { text: '시도' }, editable: false },
{ name: 'GUGUN', fieldName: '시군구', header: { text: '시군구' }, editable: false, footer: { text: '합계' } },
{ name: 'ctpvNm', fieldName: 'ctpvNm', header: { text: '시도' }, footer: { text: '합계' }, editable: false },
{ name: 'sggNm', fieldName: 'sggNm', header: { text: '시군구' } },
// 총계 > 부과액
{ name: 'LEVY_AMOUNT', fieldName: '(백만원)', header: { text: '(백만원)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'levyAmt',
fieldName: 'levyAmt',
header: { text: '(백만원)' },
editable: false,
footer: footerDecimal1,
styleName: 'rg-cell-total',
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
computed: {
fields: [
'hshusLevyAmt',
'taskFrLevyAmt',
'fbuLevyAmt',
'bthhsFrSpcs1LevyAmt',
'bthhsFrSpcs2LevyAmt',
'induLevyAmt',
'etcLevyAmt',
],
type: 'sum' as const,
},
},
// 총계 > 부과량
{ name: 'LEVY_QY', fieldName: '(천톤)', header: { text: '(천톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{
name: 'levyQnt',
fieldName: 'levyQnt',
header: { text: '(천톤)' },
editable: false,
footer: footerDecimal1,
styleName: 'rg-cell-total',
valueType: ValueType.NUMBER,
dataFormat: '#,##0.0',
computed: {
fields: [
'hshusLevyQnt',
'taskFrLevyQnt',
'fbuLevyQnt',
'bthhsFrSpcs1LevyQnt',
'bthhsFrSpcs2LevyQnt',
'induLevyQnt',
'etcLevyQnt',
],
type: 'sum' as const,
},
},
// 가정용
{ name: 'HOME_LEVY_AMOUNT', fieldName: '(백만원)', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'HOME_LEVY_QY', fieldName: '(천톤)', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'HOME_UNTPC', fieldName: '(원/톤)', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'hshusLevyAmt', fieldName: 'hshusLevyAmt', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'hshusLevyQnt', fieldName: 'hshusLevyQnt', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'hshusUntprcAmt', fieldName: 'hshusUntprcAmt', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 업무용
{ name: 'JOB_LEVY_AMOUNT', fieldName: '(백만원)', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'JOB_LEVY_QY', fieldName: '(천톤)', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'JOB_UNTPC', fieldName: '(원/톤)', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'taskFrLevyAmt', fieldName: 'taskFrLevyAmt', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'taskFrLevyQnt', fieldName: 'taskFrLevyQnt', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'taskFrUntprcAmt', fieldName: 'taskFrUntprcAmt', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 영업용(일반용)
{ name: 'BSN_LEVY_AMOUNT', fieldName: '(백만원)', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'BSN_LEVY_QY', fieldName: '(천톤)', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'BSN_UNTPC', fieldName: '(원/톤)', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'fbuLevyAmt', fieldName: 'fbuLevyAmt', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'fbuLevyQnt', fieldName: 'fbuLevyQnt', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'fbuUntprcAmt', fieldName: 'fbuUntprcAmt', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 욕탕1종(대중탕용)
{ name: 'BATHS1_LEVY_AMOUNT', fieldName: '(백만원)', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'BATHS1_LEVY_QY', fieldName: '(천톤)', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'BATHS1_UNTPC', fieldName: '(원/톤)', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'bthhsFrSpcs1LevyAmt', fieldName: 'bthhsFrSpcs1LevyAmt', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'bthhsFrSpcs1LevyQnt', fieldName: 'bthhsFrSpcs1LevyQnt', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'bthhsFrSpcs1UntprcAmt', fieldName: 'bthhsFrSpcs1UntprcAmt', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 욕탕2종
{ name: 'PBBATH_LEVY_AMOUNT', fieldName: '(백만원)', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'PBBATH_LEVY_QY', fieldName: '(천톤)', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'PBBATH_UNTPC', fieldName: '(원/톤)', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'bthhsFrSpcs2LevyAmt', fieldName: 'bthhsFrSpcs2LevyAmt', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'bthhsFrSpcs2LevyQnt', fieldName: 'bthhsFrSpcs2LevyQnt', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'bthhsFrSpcs2UntprcAmt', fieldName: 'bthhsFrSpcs2UntprcAmt', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 산업 및 공업용
{ name: 'INDUST_LEVY_AMOUNT', fieldName: '(백만원)', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'INDUST_LEVY_QY', fieldName: '(천톤)', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'INDUST_UNTPC', fieldName: '(원/톤)', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'induLevyAmt', fieldName: 'induLevyAmt', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'induLevyQnt', fieldName: 'induLevyQnt', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'induUntprcAmt', fieldName: 'induUntprcAmt', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 기타
{ name: 'ETC_LEVY_AMOUNT', fieldName: '(백만원)', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'ETC_LEVY_QY', fieldName: '(천톤)', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'ETC_UNTPC', fieldName: '(원/톤)', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'etcLevyAmt', fieldName: 'etcLevyAmt', header: { text: '(백만원)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'etcLevyQnt', fieldName: 'etcLevyQnt', header: { text: '(천톤)' }, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
{ name: 'etcLevyUntprcAmt', fieldName: 'etcLevyUntprcAmt', header: { text: '(원/톤)' }, editable: false, footer: footerDecimal1, valueType: ValueType.NUMBER, dataFormat: '#,##0.0' },
// 비고
{ name: 'RMK', fieldName: '비고', header: { text: '비고' } },
{ name: 'rmrk', fieldName: 'rmrk', header: { text: '비고' } },
// hidden
{ name: 'CHCKER_OPINION', fieldName: '검토자의견', header: { text: '검토자의견' } },
{ name: 'WRTER_OPINION', fieldName: '사용자의견', header: { text: '사용자의견' } },
// 참조 필드
{ name: 'SEWER_RNTFEE', fieldName: 'SEWER_RNTFEE', header: { text: 'SEWER_RNTFEE' } },
{ name: 'B_LEVY_AMOUNT', fieldName: 'B_LEVY_AMOUNT', header: { text: 'B_LEVY_AMOUNT' } },
{ name: 'admdstCd', fieldName: 'admdstCd', header: { text: '행정구역코드' }, visible: false, },
{ name: 'admdstNm', fieldName: 'admdstNm', header: { text: '행정구역명' }, visible: false },
],
columnLayout: [
{
name: 'group_admin',
header: { text: '행정구역' },
items: ['SIDO', 'GUGUN']
items: [
{ column: 'ctpvNm', footerUserSpans: [{ colspan: 2 }] },
'sggNm',
],
},
{
name: 'group_total',
@@ -74,12 +115,12 @@ export const SewerageUsageFeeInpColumn = {
{
name: 'total_amount',
header: { text: '부과액' },
items: ['LEVY_AMOUNT']
items: ['levyAmt']
},
{
name: 'total_qty',
header: { text: '부과량' },
items: ['LEVY_QY']
items: ['levyQnt']
}
]
},
@@ -90,17 +131,17 @@ export const SewerageUsageFeeInpColumn = {
{
name: 'home_amount',
header: { text: '부과액' },
items: ['HOME_LEVY_AMOUNT']
items: ['hshusLevyAmt']
},
{
name: 'home_qty',
header: { text: '부과량' },
items: ['HOME_LEVY_QY']
items: ['hshusLevyQnt']
},
{
name: 'home_price',
header: { text: '단가' },
items: ['HOME_UNTPC']
items: ['hshusUntprcAmt']
}
]
},
@@ -111,17 +152,17 @@ export const SewerageUsageFeeInpColumn = {
{
name: 'job_amount',
header: { text: '부과액' },
items: ['JOB_LEVY_AMOUNT']
items: ['taskFrLevyAmt']
},
{
name: 'job_qty',
header: { text: '부과량' },
items: ['JOB_LEVY_QY']
items: ['taskFrLevyQnt']
},
{
name: 'job_price',
header: { text: '단가' },
items: ['JOB_UNTPC']
items: ['taskFrUntprcAmt']
}
]
},
@@ -132,17 +173,17 @@ export const SewerageUsageFeeInpColumn = {
{
name: 'bsn_amount',
header: { text: '부과액' },
items: ['BSN_LEVY_AMOUNT']
items: ['fbuLevyAmt']
},
{
name: 'bsn_qty',
header: { text: '부과량' },
items: ['BSN_LEVY_QY']
items: ['fbuLevyQnt']
},
{
name: 'bsn_price',
header: { text: '단가' },
items: ['BSN_UNTPC']
items: ['fbuUntprcAmt']
}
]
},
@@ -153,17 +194,17 @@ export const SewerageUsageFeeInpColumn = {
{
name: 'baths1_amount',
header: { text: '부과액' },
items: ['BATHS1_LEVY_AMOUNT']
items: ['bthhsFrSpcs1LevyAmt']
},
{
name: 'baths1_qty',
header: { text: '부과량' },
items: ['BATHS1_LEVY_QY']
items: ['bthhsFrSpcs1LevyQnt']
},
{
name: 'baths1_price',
header: { text: '단가' },
items: ['BATHS1_UNTPC']
items: ['bthhsFrSpcs1UntprcAmt']
}
]
},
@@ -174,17 +215,17 @@ export const SewerageUsageFeeInpColumn = {
{
name: 'pbbath_amount',
header: { text: '부과액' },
items: ['PBBATH_LEVY_AMOUNT']
items: ['bthhsFrSpcs2LevyAmt']
},
{
name: 'pbbath_qty',
header: { text: '부과량' },
items: ['PBBATH_LEVY_QY']
items: ['bthhsFrSpcs2LevyQnt']
},
{
name: 'pbbath_price',
header: { text: '단가' },
items: ['PBBATH_UNTPC']
items: ['bthhsFrSpcs2UntprcAmt']
}
]
},
@@ -195,17 +236,17 @@ export const SewerageUsageFeeInpColumn = {
{
name: 'indust_amount',
header: { text: '부과액' },
items: ['INDUST_LEVY_AMOUNT']
items: ['induLevyAmt']
},
{
name: 'indust_qty',
header: { text: '부과량' },
items: ['INDUST_LEVY_QY']
items: ['induLevyQnt']
},
{
name: 'indust_price',
header: { text: '단가' },
items: ['INDUST_UNTPC']
items: ['induUntprcAmt']
}
]
},
@@ -216,25 +257,27 @@ export const SewerageUsageFeeInpColumn = {
{
name: 'etc_amount',
header: { text: '부과액' },
items: ['ETC_LEVY_AMOUNT']
items: ['etcLevyAmt']
},
{
name: 'etc_qty',
header: { text: '부과량' },
items: ['ETC_LEVY_QY']
items: ['etcLevyQnt']
},
{
name: 'etc_price',
header: { text: '단가' },
items: ['ETC_UNTPC']
items: ['etcLevyUntprcAmt']
}
]
},
'RMK'
'rmrk',
'admdstCd',
'admdstNm'
],
rowStartNum: 3,
fixedColumns: 1,
tabKey: 'sewerageUsageFeeInp',
tabKey: 'ST00090003',
tabLabel: '하수도 사용료'
};
@@ -43,6 +43,65 @@ export function createAvgValueCallback(fieldNames: string[]): DataFieldValueCall
};
}
function sumFieldValues(
fields: string[],
fieldNamesArr: string[],
values: unknown[],
): number {
let sum = 0;
for (const f of fields) {
const idx = fieldNamesArr.indexOf(f);
if (idx >= 0) sum += toNum(values[idx]);
}
return sum;
}
/** halfSumOfSums 한 건 — (sum(first) + sum(second)) / 2, 소수 내림 */
function computeHalfSumOfSums(
firstSumFields: string[],
secondSumFields: string[],
fieldNamesArr: string[],
values: unknown[],
): number {
const firstSum = sumFieldValues(firstSumFields, fieldNamesArr, values);
const secondSum = sumFieldValues(secondSumFields, fieldNamesArr, values);
return Math.floor((firstSum + secondSum) / 2);
}
export type HalfSumOfSumsGroup = {
firstSumFields: string[];
secondSumFields: string[];
};
/** 두 그룹 합계의 평균 — (sum(first) + sum(second)) / 2 */
export function createHalfSumOfSumsValueCallback(
firstSumFields: string[],
secondSumFields: string[],
): DataFieldValueCallback {
return (_prod, _dataRow, _fieldName, fieldNamesArr, values) =>
computeHalfSumOfSums(firstSumFields, secondSumFields, fieldNamesArr, values);
}
/** halfSumOfSums 2건의 합 — (B+C)/2 + (E+F)/2 */
export function createSumOfTwoHalfSumOfSumsValueCallback(
firstGroup: HalfSumOfSumsGroup,
secondGroup: HalfSumOfSumsGroup,
): DataFieldValueCallback {
return (_prod, _dataRow, _fieldName, fieldNamesArr, values) =>
computeHalfSumOfSums(
firstGroup.firstSumFields,
firstGroup.secondSumFields,
fieldNamesArr,
values,
) +
computeHalfSumOfSums(
secondGroup.firstSumFields,
secondGroup.secondSumFields,
fieldNamesArr,
values,
);
}
/** 차이값: fields[0] − fields[1] (예: 매설연장 − 철거연장) */
export function createDifferenceValueCallback(fields: string[]): DataFieldValueCallback {
const a = fields[0] ?? "";
@@ -179,6 +238,21 @@ export function createSwrgInstlrtRateValueCallback(
};
}
/** 처리효율: (유입BOD − 방류BOD) / 유입BOD */
export function createBodPrcsRtValueCallback(
inflwField: string,
dschgField: string,
): DataFieldValueCallback {
return (_prod, _dataRow, _fieldName, fieldNamesArr, values) => {
const inflwIdx = fieldNamesArr.indexOf(inflwField);
const dschgIdx = fieldNamesArr.indexOf(dschgField);
const inflw = inflwIdx >= 0 ? toNum(values[inflwIdx]) : 0;
const dschg = dschgIdx >= 0 ? toNum(values[dschgIdx]) : 0;
if (inflw === 0) return 0;
return (inflw - dschg) / inflw;
};
}
/** 총양수량: Σ(펌프대수 + 예비펌프대수) × 대당 양수량 */
export function createPumpGrossPpqtyValueCallback(
pumpGroups: Array<{
@@ -202,6 +276,44 @@ export function createPumpGrossPpqtyValueCallback(
};
}
/** (a/b) / (c/d) × 100 — 각 quotient 분모 0이면 0 */
export function createQuotientRatioPercentValueCallback(
numeratorQuotient: [string, string],
denominatorQuotient: [string, string],
): DataFieldValueCallback {
const [numField, numDenField] = numeratorQuotient;
const [denNumField, denDenField] = denominatorQuotient;
return (_prod, _dataRow, _fieldName, fieldNamesArr, values) => {
const getVal = (field: string) => {
const idx = fieldNamesArr.indexOf(field);
return idx >= 0 ? toNum(values[idx]) : 0;
};
const numDen = getVal(numDenField);
const denDen = getVal(denDenField);
const denNum = getVal(denNumField);
if (numDen === 0 || denDen === 0 || denNum === 0) return 0;
const numerator = getVal(numField) / numDen;
const denominator = denNum / denDen;
if (denominator === 0) return 0;
return (numerator / denominator) * 100;
};
}
/** 나눗셈: fields[0] / fields[1] (분모 0이면 0) */
export function createQuotientValueCallback(fields: string[]): DataFieldValueCallback {
const numeratorField = fields[0] ?? "";
const denominatorField = fields[1] ?? "";
return (_prod, _dataRow, _fieldName, fieldNamesArr, values) => {
if (!numeratorField || !denominatorField) return 0;
const numIdx = fieldNamesArr.indexOf(numeratorField);
const denIdx = fieldNamesArr.indexOf(denominatorField);
const numerator = numIdx >= 0 ? toNum(values[numIdx]) : 0;
const denominator = denIdx >= 0 ? toNum(values[denIdx]) : 0;
if (denominator === 0) return 0;
return numerator / denominator;
};
}
/** 연산 타입에 맞는 valueCallback 반환 */
export function getComputedValueCallback(config: ComputedFieldConfig): DataFieldValueCallback {
switch (config.type) {
@@ -213,11 +325,28 @@ export function getComputedValueCallback(config: ComputedFieldConfig): DataField
return createMaxValueCallback(config.fields);
case 'difference':
return createDifferenceValueCallback(config.fields);
case 'quotient':
return createQuotientValueCallback(config.fields);
case 'quotientRatioPercent':
return createQuotientRatioPercentValueCallback(
config.numeratorQuotient,
config.denominatorQuotient,
);
case 'differenceOfSums':
return createDifferenceOfSumsValueCallback(
config.minuendFields,
config.subtrahendFields,
);
case 'halfSumOfSums':
return createHalfSumOfSumsValueCallback(
config.firstSumFields,
config.secondSumFields,
);
case 'sumOfTwoHalfSumOfSums':
return createSumOfTwoHalfSumOfSumsValueCallback(
config.firstHalfSum,
config.secondHalfSum,
);
case 'maxOfFieldAndSum':
return createMaxOfFieldAndSumValueCallback(
config.baseField,
@@ -234,5 +363,7 @@ export function getComputedValueCallback(config: ComputedFieldConfig): DataField
return createSwrgInstlrtRateValueCallback(config.numeratorFields, config.denominatorFields, config.thirdFields);
case 'pumpGrossPpqty':
return createPumpGrossPpqtyValueCallback(config.pumpGroups);
case 'bodPrcsRt':
return createBodPrcsRtValueCallback(config.inflwField, config.dschgField);
}
}
+59 -1
View File
@@ -1,6 +1,64 @@
import type { ColumnConfig } from "@/components/type/DataGrid.type";
import type {
ColumnConfig,
ColumnLayoutItem,
} from "@/components/type/DataGrid.type";
import type { GridView } from "realgrid";
function flattenColumnLayoutNames(layout: ColumnLayoutItem[]): string[] {
const result: string[] = [];
for (const item of layout) {
if (typeof item === "string") {
result.push(item);
continue;
}
if (item && typeof item === "object") {
if ("column" in item && typeof item.column === "string") {
result.push(item.column);
continue;
}
if ("items" in item && Array.isArray(item.items)) {
result.push(
...flattenColumnLayoutNames(
item.items as ColumnLayoutItem[],
),
);
}
}
}
return result;
}
/** 엑셀 가져오기 — N번째 열을 그리드 N번째 fieldName에 매핑 (보내기 컬럼 순서와 동일) */
export function getExcelImportColumnFieldNames(
columns: ColumnConfig[],
columnLayout?: ColumnLayoutItem[],
): string[] {
const excludedNames = new Set(
columns.filter((c) => c.excludeFromExcelExport).map((c) => c.name),
);
const fieldByName = new Map(
columns
.filter((c) => c.fieldName)
.map((c) => [c.name, c.fieldName as string]),
);
const layoutNames =
columnLayout && columnLayout.length > 0
? flattenColumnLayoutNames(columnLayout)
: columns.map((c) => c.name);
const seen = new Set<string>();
const fieldNames: string[] = [];
for (const name of layoutNames) {
if (excludedNames.has(name) || seen.has(name)) continue;
const fieldName = fieldByName.get(name);
if (!fieldName) continue;
seen.add(name);
fieldNames.push(fieldName);
}
return fieldNames;
}
export type ExcelExportVisibilitySnapshot = Map<string, boolean>;
/** 엑셀보내기 직전: 제외 컬럼 숨김, 그리드용 숨김(visible:false) 컬럼은 export에만 표시 */