Merge branch 'iipbs/st/dev' of https://www.onasys.co.kr:3000/ONAANDALL/frontend into iipbs/st/dev
This commit is contained in:
@@ -25,6 +25,8 @@ import type {
|
||||
import { getComputedValueCallback } from "@/components/util/grid/computedFieldCallbacks";
|
||||
import {
|
||||
getExcelImportColumnFieldNames,
|
||||
getNonEditableColumnFieldNames,
|
||||
mergeExcelImportWithExistingRows,
|
||||
prepareGridColumnsForExcelExport,
|
||||
restoreGridColumnsAfterExcelExport,
|
||||
shouldPrepareColumnsForExcelExport,
|
||||
@@ -727,6 +729,7 @@ const DataGrid = ({
|
||||
rowStartNum,
|
||||
excelExportFileSuffix,
|
||||
isExcelImport = true,
|
||||
excelImportSkipNonEditable = true,
|
||||
isExcelExport = true,
|
||||
fixedColumns,
|
||||
showHeaderCheckbox = false,
|
||||
@@ -1070,6 +1073,7 @@ useEffect(() => {
|
||||
const excelColumnFieldNames = getExcelImportColumnFieldNames(
|
||||
columns,
|
||||
columnLayout,
|
||||
{ skipNonEditable: excelImportSkipNonEditable },
|
||||
);
|
||||
|
||||
// 데이터 행 읽기
|
||||
@@ -1137,7 +1141,14 @@ useEffect(() => {
|
||||
dataRows.push(rowData);
|
||||
}
|
||||
}
|
||||
setRowData(dataRows);
|
||||
const preserveFieldNames = excelImportSkipNonEditable
|
||||
? getNonEditableColumnFieldNames(columns, columnLayout)
|
||||
: [];
|
||||
setRowData((prev) =>
|
||||
mergeExcelImportWithExistingRows(prev, dataRows, {
|
||||
preserveFieldNames,
|
||||
}),
|
||||
);
|
||||
} catch (error) {
|
||||
console.error("엑셀 파일 파싱 오류:", error);
|
||||
alert("엑셀 파일 파싱 중 오류가 발생했습니다.");
|
||||
@@ -1263,6 +1274,11 @@ useEffect(() => {
|
||||
"editable",
|
||||
column.editable !== false,
|
||||
);
|
||||
gv.setColumnProperty?.(
|
||||
column.name,
|
||||
"styleName",
|
||||
column.styleName ?? "",
|
||||
);
|
||||
applyColumnListEditorProperties(gv, column);
|
||||
});
|
||||
if (columns.some((c) => isDropDownEditorColumn(c))) {
|
||||
@@ -1481,12 +1497,10 @@ useEffect(() => {
|
||||
fieldName: col.fieldName,
|
||||
header: realGridHeaderWithTooltip(col.header),
|
||||
editable: col.editable,
|
||||
styleName: col.styleName ?? "",
|
||||
valueType: col.valueType,
|
||||
dataFormat: col.dataFormat,
|
||||
...(col.width != null && { width: col.width }),
|
||||
...(col.styleName != null && {
|
||||
styleName: col.styleName,
|
||||
}),
|
||||
...(col.mergeCells && {
|
||||
mergeRule: { criteria: "value" as const },
|
||||
}),
|
||||
|
||||
@@ -36,6 +36,8 @@ import SplitDataGrid from "./SplitDataGrid";
|
||||
import { getComputedValueCallback } from "@/components/util/grid/computedFieldCallbacks"
|
||||
import {
|
||||
getExcelImportColumnFieldNames,
|
||||
getNonEditableColumnFieldNames,
|
||||
mergeExcelImportWithExistingRows,
|
||||
prepareGridColumnsForExcelExport,
|
||||
restoreGridColumnsAfterExcelExport,
|
||||
shouldPrepareColumnsForExcelExport,
|
||||
@@ -277,6 +279,7 @@ function createNumberComparer(dp: RealGridDataProviderComparer): (field: string
|
||||
const TabDataGrid = ({
|
||||
columnInfo,
|
||||
isExcelImport = true,
|
||||
excelImportSkipNonEditable = true,
|
||||
isExcelExport = true,
|
||||
rows = [],
|
||||
showHeaderCheckbox = false,
|
||||
@@ -552,6 +555,7 @@ const TabDataGrid = ({
|
||||
const excelColumnFieldNames = getExcelImportColumnFieldNames(
|
||||
columns,
|
||||
columnLayout,
|
||||
{ skipNonEditable: excelImportSkipNonEditable },
|
||||
);
|
||||
|
||||
// 데이터 행 읽기
|
||||
@@ -596,9 +600,19 @@ const TabDataGrid = ({
|
||||
}
|
||||
}
|
||||
|
||||
const preserveFieldNames = excelImportSkipNonEditable
|
||||
? getNonEditableColumnFieldNames(columns, columnLayout)
|
||||
: [];
|
||||
|
||||
setRowDataMap(prev => {
|
||||
const newMap = new Map(prev);
|
||||
newMap.set(tabKey, dataRows);
|
||||
const existingRows = prev.get(tabKey) ?? [];
|
||||
newMap.set(
|
||||
tabKey,
|
||||
mergeExcelImportWithExistingRows(existingRows, dataRows, {
|
||||
preserveFieldNames,
|
||||
}),
|
||||
);
|
||||
return newMap;
|
||||
});
|
||||
} catch (error) {
|
||||
|
||||
@@ -555,7 +555,7 @@ const PublicSewageFacilityInp = () => {
|
||||
sggNm: lastSearchedValues.gugun === 'ALL' ? '' : lastSearchedValues.gugun ?? '',
|
||||
sewerBzmnCd: lastSearchedValues.bsnmCd ?? '',
|
||||
userId: userInfo.userUnqId,
|
||||
// superAdmin: isAdminInfo ? 'superAdmin' : '',
|
||||
superAdmin: isAdminInfo ? 'superAdmin' : '',
|
||||
noApplicable,
|
||||
saveList,
|
||||
};
|
||||
|
||||
@@ -108,6 +108,48 @@ const getPipeTypeLastDataYear = () => {
|
||||
return Math.max(1995, lastDataYear);
|
||||
};
|
||||
|
||||
const PIPE_TYPE_STATUS_TAB_KEY = "ST00020003";
|
||||
|
||||
/** 조회 연도(statsYr)에 해당하는 연도별 매설연장 필드명 (y{year}LyngPrlg) */
|
||||
const resolvePipeTypeEditableLyngField = (statsYr: string | null | undefined): string | null => {
|
||||
if (!statsYr) return null;
|
||||
const year = Number(statsYr);
|
||||
if (!Number.isFinite(year)) return null;
|
||||
const clamped = Math.min(getPipeTypeLastDataYear(), Math.max(1995, year));
|
||||
return `y${clamped}LyngPrlg`;
|
||||
};
|
||||
|
||||
/** ST00020003: 입력기간 통과 시 조회 연도의 매설연장(y{year}LyngPrlg)만 편집 가능 */
|
||||
const applyPipeTypeStatusEditableColumns = (
|
||||
cols: ColumnConfig[] | undefined,
|
||||
canEdit: boolean,
|
||||
periodEnterable: boolean,
|
||||
statsYr: string | null,
|
||||
): ColumnConfig[] =>
|
||||
(cols ?? []).map((col) => {
|
||||
if (!canEdit) {
|
||||
return { ...col, editable: false as const, styleName: col.styleName ?? "rg-cell-readonly" };
|
||||
}
|
||||
|
||||
const fieldName = col.fieldName ?? col.name;
|
||||
const isYearLyngField =
|
||||
fieldName !== "lyngPrlg" && typeof fieldName === "string" && fieldName.endsWith("LyngPrlg");
|
||||
if (fieldName === "lyngPrlg" || isYearLyngField) {
|
||||
const editableLyngField = periodEnterable ? resolvePipeTypeEditableLyngField(statsYr) : null;
|
||||
const isTargetYear = editableLyngField !== null && fieldName === editableLyngField;
|
||||
if (isTargetYear) {
|
||||
const { styleName: _readonlyStyle, ...rest } = col;
|
||||
return {
|
||||
...rest,
|
||||
editable: true as const,
|
||||
};
|
||||
}
|
||||
return { ...col, editable: false as const, styleName: col.styleName ?? "rg-cell-readonly" };
|
||||
}
|
||||
|
||||
return col;
|
||||
});
|
||||
|
||||
const getManholeLastDataYear = () => {
|
||||
const lastDataYear = new Date().getFullYear() - 1;
|
||||
return Math.max(2014, lastDataYear);
|
||||
@@ -507,11 +549,22 @@ const SewagePipeInp = () => {
|
||||
const canEdit =
|
||||
computeTabEnterable(tabStatusInfoMap.get(tab.tabKey) ?? null, inputPeriodEnterable) || isAdminInfo;
|
||||
const applyEditable = (cols: ColumnConfig[] | undefined) =>
|
||||
(cols ?? []).map((col: ColumnConfig) =>
|
||||
canEdit
|
||||
? col
|
||||
: { ...col, editable: false as const, styleName: col.styleName ?? "rg-cell-readonly" }
|
||||
);
|
||||
tab.tabKey === PIPE_TYPE_STATUS_TAB_KEY
|
||||
? applyPipeTypeStatusEditableColumns(
|
||||
cols,
|
||||
canEdit,
|
||||
inputPeriodEnterable,
|
||||
lastSearchedYear,
|
||||
)
|
||||
: (cols ?? []).map((col: ColumnConfig) =>
|
||||
canEdit
|
||||
? col
|
||||
: {
|
||||
...col,
|
||||
editable: false as const,
|
||||
styleName: col.styleName ?? "rg-cell-readonly",
|
||||
},
|
||||
);
|
||||
|
||||
if (tab.left && tab.right) {
|
||||
return {
|
||||
@@ -525,7 +578,7 @@ const SewagePipeInp = () => {
|
||||
columns: applyColumnGuides(applyEditable(tab.columns), tab.tabKey),
|
||||
};
|
||||
});
|
||||
}, [tabStatusInfoMap, isAdminInfo, inputPeriodEnterable, columnGuidesByTab]);
|
||||
}, [tabStatusInfoMap, isAdminInfo, inputPeriodEnterable, columnGuidesByTab, lastSearchedYear]);
|
||||
|
||||
const commitToolbarSnapshotForLoadedTab = (
|
||||
tabKey: string,
|
||||
@@ -1546,7 +1599,7 @@ const SewagePipeInp = () => {
|
||||
sggNm: values.gugun === "ALL" ? "" : (values.gugun ?? ""),
|
||||
sewerBzmnCd: values.bsnmCd ?? "",
|
||||
userId: userInfo.userUnqId,
|
||||
// superAdmin: isAdminInfo ? "superAdmin" : "",
|
||||
superAdmin: isAdminInfo ? "superAdmin" : "",
|
||||
saveList,
|
||||
};
|
||||
|
||||
@@ -1626,7 +1679,7 @@ const SewagePipeInp = () => {
|
||||
sggNm: values.gugun === "ALL" ? "" : (values.gugun ?? ""),
|
||||
sewerBzmnCd: values.bsnmCd ?? "",
|
||||
userId: userInfo.userUnqId,
|
||||
// superAdmin: isAdminInfo ? "superAdmin" : "",
|
||||
superAdmin: isAdminInfo ? "superAdmin" : "",
|
||||
saveList,
|
||||
};
|
||||
|
||||
@@ -1711,7 +1764,7 @@ const SewagePipeInp = () => {
|
||||
sggNm: values.gugun === "ALL" ? "" : (values.gugun ?? ""),
|
||||
sewerBzmnCd: values.bsnmCd ?? "",
|
||||
userId: userInfo.userUnqId,
|
||||
// superAdmin: isAdminInfo ? "superAdmin" : "",
|
||||
superAdmin: isAdminInfo ? "superAdmin" : "",
|
||||
saveList,
|
||||
};
|
||||
|
||||
@@ -1774,7 +1827,7 @@ const SewagePipeInp = () => {
|
||||
sggNm: values.gugun === "ALL" ? "" : (values.gugun ?? ""),
|
||||
sewerBzmnCd: values.bsnmCd ?? "",
|
||||
userId: userInfo.userUnqId,
|
||||
// superAdmin: isAdminInfo ? "superAdmin" : "",
|
||||
superAdmin: isAdminInfo ? "superAdmin" : "",
|
||||
saveList,
|
||||
};
|
||||
|
||||
@@ -1853,7 +1906,7 @@ const SewagePipeInp = () => {
|
||||
sggNm: values.gugun === "ALL" ? "" : (values.gugun ?? ""),
|
||||
sewerBzmnCd: values.bsnmCd ?? "",
|
||||
userId: userInfo.userUnqId,
|
||||
// superAdmin: isAdminInfo ? "superAdmin" : "",
|
||||
superAdmin: isAdminInfo ? "superAdmin" : "",
|
||||
saveList,
|
||||
};
|
||||
|
||||
@@ -2080,6 +2133,9 @@ const SewagePipeInp = () => {
|
||||
});
|
||||
}}
|
||||
isExcelImport={excelToolbarEnabledForTab}
|
||||
excelImportSkipNonEditable={
|
||||
tabKey === PIPE_TYPE_STATUS_TAB_KEY ? !isAdminInfo : true
|
||||
}
|
||||
isExcelExport
|
||||
showHeaderCheckbox={false}
|
||||
fixedColumns={info.fixedColumns}
|
||||
|
||||
@@ -353,9 +353,9 @@ const SeweragePenetrationRateInp = () => {
|
||||
statsCd: statsCd,
|
||||
};
|
||||
|
||||
// if (isAdminInfo) {
|
||||
// params.superAdmin = 'superAdmin';
|
||||
// }
|
||||
if (isAdminInfo) {
|
||||
params.superAdmin = 'superAdmin';
|
||||
}
|
||||
|
||||
try {
|
||||
setIsLoading(true);
|
||||
@@ -682,9 +682,9 @@ const SeweragePenetrationRateInp = () => {
|
||||
sewerBzmnFrgnrWholPpltnCnt: footerFromApi?.sewerBzmnFrgnrWholPpltnCnt // 행정참고자료 외국인 합계
|
||||
};
|
||||
|
||||
// if (isAdminInfo) {
|
||||
// requestData.superAdmin = 'superAdmin';
|
||||
// }
|
||||
if (isAdminInfo) {
|
||||
requestData.superAdmin = 'superAdmin';
|
||||
}
|
||||
|
||||
const response = await statisticsInputSewSupplyRateApi.saveSewerageSupplyRate(requestData);
|
||||
|
||||
|
||||
@@ -380,6 +380,8 @@ export type DataGridProps = {
|
||||
/** 있으면보내기 파일명: `{메뉴명}_{suffix}.xlsx` (TabDataGrid의 탭 라벨 접미사와 동일) */
|
||||
excelExportFileSuffix?: string;
|
||||
isExcelImport?: boolean; // 엑셀 가져오기 유무
|
||||
/** 엑셀 가져오기 시 editable:false 컬럼 값 무시 (기본 true) */
|
||||
excelImportSkipNonEditable?: boolean;
|
||||
isExcelExport?: boolean;
|
||||
onRowsChange?: (rows: Record<string, string | number>[]) => void;
|
||||
fixedColumns?: number; // 고정할 컬럼 개수 (좌측부터)
|
||||
@@ -432,6 +434,8 @@ export type TabDataGridProps = {
|
||||
columnInfo: ColumnInfoList[];
|
||||
rows?: Record<string, string | number>[];
|
||||
isExcelImport?: boolean; // 엑셀 가져오기 유무
|
||||
/** 엑셀 가져오기 시 editable:false 컬럼 값 무시 (기본 true) */
|
||||
excelImportSkipNonEditable?: boolean;
|
||||
isExcelExport?: boolean;
|
||||
onRowsChange?: (rows: Record<string, string | number>[]) => void;
|
||||
showHeaderCheckbox?: boolean; // 헤더 체크박스 표시 여부 (기본값: true)
|
||||
|
||||
@@ -28,14 +28,22 @@ function flattenColumnLayoutNames(layout: ColumnLayoutItem[]): string[] {
|
||||
return result;
|
||||
}
|
||||
|
||||
export type ExcelImportColumnFieldNamesOptions = {
|
||||
/** false면 editable:false 컬럼도 import (기본 true — 읽기전용·계산 컬럼은 엑셀 값 무시) */
|
||||
skipNonEditable?: boolean;
|
||||
};
|
||||
|
||||
/** 엑셀 가져오기 — N번째 열을 그리드 N번째 fieldName에 매핑 (보내기 컬럼 순서와 동일) */
|
||||
export function getExcelImportColumnFieldNames(
|
||||
columns: ColumnConfig[],
|
||||
columnLayout?: ColumnLayoutItem[],
|
||||
options?: ExcelImportColumnFieldNamesOptions,
|
||||
): string[] {
|
||||
const skipNonEditable = options?.skipNonEditable !== false;
|
||||
const excludedNames = new Set(
|
||||
columns.filter((c) => c.excludeFromExcelExport).map((c) => c.name),
|
||||
);
|
||||
const columnByName = new Map(columns.map((c) => [c.name, c]));
|
||||
const fieldByName = new Map(
|
||||
columns
|
||||
.filter((c) => c.fieldName)
|
||||
@@ -54,11 +62,72 @@ export function getExcelImportColumnFieldNames(
|
||||
const fieldName = fieldByName.get(name);
|
||||
if (!fieldName) continue;
|
||||
seen.add(name);
|
||||
const col = columnByName.get(name);
|
||||
if (skipNonEditable && col?.editable === false) {
|
||||
fieldNames.push("");
|
||||
continue;
|
||||
}
|
||||
fieldNames.push(fieldName);
|
||||
}
|
||||
return fieldNames;
|
||||
}
|
||||
|
||||
/** 엑셀 import 시 기존 값을 유지할 fieldName 목록 (editable:false) */
|
||||
export function getNonEditableColumnFieldNames(
|
||||
columns: ColumnConfig[],
|
||||
columnLayout?: ColumnLayoutItem[],
|
||||
): string[] {
|
||||
const excludedNames = new Set(
|
||||
columns.filter((c) => c.excludeFromExcelExport).map((c) => c.name),
|
||||
);
|
||||
const columnByName = new Map(columns.map((c) => [c.name, c]));
|
||||
|
||||
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 col = columnByName.get(name);
|
||||
const fieldName = col?.fieldName;
|
||||
if (!fieldName || col.editable !== false) continue;
|
||||
seen.add(name);
|
||||
fieldNames.push(fieldName);
|
||||
}
|
||||
return fieldNames;
|
||||
}
|
||||
|
||||
/** 엑셀 import 결과를 기존 행과 병합 — preserveFieldNames는 기존 그리드 값 유지 */
|
||||
export function mergeExcelImportWithExistingRows(
|
||||
existingRows: Record<string, string | number>[],
|
||||
importedRows: Record<string, string | number>[],
|
||||
options?: { preserveFieldNames?: readonly string[] },
|
||||
): Record<string, string | number>[] {
|
||||
const preserveSet =
|
||||
options?.preserveFieldNames && options.preserveFieldNames.length > 0
|
||||
? new Set(options.preserveFieldNames)
|
||||
: null;
|
||||
|
||||
return importedRows.map((importedRow, index) => {
|
||||
const existingRow = existingRows[index];
|
||||
if (!existingRow) return { ...importedRow };
|
||||
|
||||
const merged: Record<string, string | number> = { ...existingRow, ...importedRow };
|
||||
if (preserveSet) {
|
||||
for (const fieldName of preserveSet) {
|
||||
const preserved = existingRow[fieldName];
|
||||
if (preserved !== undefined) {
|
||||
merged[fieldName] = preserved;
|
||||
}
|
||||
}
|
||||
}
|
||||
return merged;
|
||||
});
|
||||
}
|
||||
|
||||
export type ExcelExportVisibilitySnapshot = Map<string, boolean>;
|
||||
|
||||
/** 엑셀보내기 직전: 제외 컬럼 숨김, 그리드용 숨김(visible:false) 컬럼은 export에만 표시 */
|
||||
|
||||
Reference in New Issue
Block a user