feat: Enhance Excel upload functionality with automatic numbering column detection

- Implemented automatic detection of numbering columns in the Excel upload modal, improving user experience by streamlining the upload process.
- Updated the master-detail Excel upload configuration to reflect changes in how numbering rules are applied, ensuring consistency across uploads.
- Refactored related components to remove deprecated properties and improve clarity in the configuration settings.
- Enhanced error handling and logging for better debugging during the upload process.
This commit is contained in:
kjs
2026-02-11 15:43:50 +09:00
parent eac2fa63b1
commit 2bbb5d7013
4 changed files with 362 additions and 305 deletions

View File

@@ -4984,7 +4984,7 @@ export class ButtonActionExecutor {
// visible이 true인 컬럼만 추출
visibleColumns = columns.filter((col: any) => col.visible !== false).map((col: any) => col.columnName);
// 🎯 column_labels 테이블에서 실제 라벨 가져오기
// column_labels 테이블에서 실제 라벨 가져오기
try {
const columnsResponse = await apiClient.get(`/table-management/tables/${context.tableName}/columns`, {
params: { page: 1, size: 9999 },
@@ -5021,19 +5021,77 @@ export class ButtonActionExecutor {
}
});
}
} else {
console.warn("⚠️ 화면 레이아웃에서 테이블 리스트 컴포넌트를 찾을 수 없습니다.");
}
}
} catch (error) {
console.error("❌ 화면 레이아웃 조회 실패:", error);
}
// 🎨 카테고리 값들 조회 (한 번만)
// Fallback: 레이아웃에서 컬럼 정보를 못 가져온 경우, table_type_columns에서 직접 조회
// 시스템 컬럼 제외 + 라벨 적용으로 raw 컬럼명 노출 방지
const SYSTEM_COLUMNS = ["id", "company_code", "created_date", "updated_date", "writer"];
if ((!visibleColumns || visibleColumns.length === 0) && context.tableName && dataToExport.length > 0) {
console.log("⚠️ 레이아웃에서 컬럼 설정을 찾지 못함 → table_type_columns에서 fallback 조회");
try {
const { apiClient } = await import("@/lib/api/client");
const columnsResponse = await apiClient.get(`/table-management/tables/${context.tableName}/columns`, {
params: { page: 1, size: 9999 },
});
if (columnsResponse.data?.success && columnsResponse.data?.data) {
let columnData = columnsResponse.data.data;
if (columnData.columns && Array.isArray(columnData.columns)) {
columnData = columnData.columns;
}
if (Array.isArray(columnData) && columnData.length > 0) {
// visible이 false가 아닌 컬럼만 + 시스템 컬럼 제외
const filteredCols = columnData.filter((col: any) => {
const colName = (col.column_name || col.columnName || "").toLowerCase();
if (SYSTEM_COLUMNS.includes(colName)) return false;
if (col.isVisible === false || col.is_visible === false) return false;
return true;
});
visibleColumns = filteredCols.map((col: any) => col.column_name || col.columnName);
columnLabels = {};
filteredCols.forEach((col: any) => {
const colName = col.column_name || col.columnName;
const labelValue = col.column_label || col.label || col.displayName || colName;
if (colName) {
columnLabels![colName] = labelValue;
}
});
console.log(`✅ Fallback 컬럼 ${visibleColumns.length}개 로드 완료`);
}
}
} catch (fallbackError) {
console.error("❌ Fallback 컬럼 조회 실패:", fallbackError);
}
}
// 최종 안전장치: 여전히 컬럼 정보가 없으면 데이터의 키에서 시스템 컬럼만 제외
if ((!visibleColumns || visibleColumns.length === 0) && dataToExport.length > 0) {
console.log("⚠️ 최종 fallback: 데이터 키에서 시스템 컬럼 제외");
const allKeys = Object.keys(dataToExport[0]);
visibleColumns = allKeys.filter((key) => {
const lowerKey = key.toLowerCase();
// 시스템 컬럼 제외
if (SYSTEM_COLUMNS.includes(lowerKey)) return false;
// _name, _label 등 조인된 보조 필드 제외
if (lowerKey.endsWith("_name") || lowerKey.endsWith("_label") || lowerKey.endsWith("_value_label")) return false;
return true;
});
// 라벨이 없으므로 최소한 column_labels 비워두지 않음 (컬럼명 그대로 표시되지만 시스템 컬럼은 제외됨)
if (!columnLabels) {
columnLabels = {};
}
}
// 카테고리 값들 조회 (한 번만)
const categoryMap: Record<string, Record<string, string>> = {};
let categoryColumns: string[] = [];
// 백엔드에서 카테고리 컬럼 정보 가져오기
if (context.tableName) {
try {
const { getCategoryColumns, getCategoryValues } = await import("@/lib/api/tableCategoryValue");
@@ -5072,7 +5130,7 @@ export class ButtonActionExecutor {
}
}
// 🎨 컬럼 필터링 및 라벨 적용 (항상 실행)
// 컬럼 필터링 및 라벨 적용
if (visibleColumns && visibleColumns.length > 0 && dataToExport.length > 0) {
dataToExport = dataToExport.map((row: any) => {
const filteredRow: Record<string, any> = {};
@@ -5165,6 +5223,8 @@ export class ButtonActionExecutor {
? config.excelAfterUploadFlows
: config.masterDetailExcel?.afterUploadFlows;
// masterDetailExcel 설정이 명시적으로 있을 때만 간단 모드 (디테일만 업로드)
// 설정이 없으면 기본 모드 (마스터+디테일 둘 다 업로드)
if (config.masterDetailExcel) {
masterDetailExcelConfig = {
...config.masterDetailExcel,
@@ -5173,25 +5233,13 @@ export class ButtonActionExecutor {
detailTable: relationResponse.data.detailTable,
masterKeyColumn: relationResponse.data.masterKeyColumn,
detailFkColumn: relationResponse.data.detailFkColumn,
// 채번 규칙 ID 추가 (excelNumberingRuleId를 numberingRuleId로 매핑)
numberingRuleId: config.masterDetailExcel.numberingRuleId || config.excelNumberingRuleId,
// 업로드 후 제어 설정 (통합: excelAfterUploadFlows 우선)
afterUploadFlows,
};
} else {
// 버튼 설정이 없으면 분할 패널 정보만 사용
masterDetailExcelConfig = {
masterTable: relationResponse.data.masterTable,
detailTable: relationResponse.data.detailTable,
masterKeyColumn: relationResponse.data.masterKeyColumn,
detailFkColumn: relationResponse.data.detailFkColumn,
simpleMode: true, // 기본값으로 간단 모드 사용
// 채번 규칙 ID 추가 (excelNumberingRuleId 사용)
numberingRuleId: config.excelNumberingRuleId,
// 채번은 ExcelUploadModal에서 마스터 테이블 기반 자동 감지
// 업로드 후 제어 설정 (통합: excelAfterUploadFlows 우선)
afterUploadFlows,
};
}
// masterDetailExcel 설정 없으면 masterDetailExcelConfig는 undefined 유지
// → ExcelUploadModal에서 기본 모드로 동작 (마스터+디테일 둘 다 매핑/업로드)
}
}
@@ -5233,9 +5281,7 @@ export class ButtonActionExecutor {
isMasterDetail,
masterDetailRelation,
masterDetailExcelConfig,
// 🆕 단일 테이블 채번 설정
numberingRuleId: config.excelNumberingRuleId,
numberingTargetColumn: config.excelNumberingTargetColumn,
// 채번은 ExcelUploadModal에서 테이블 타입 관리 기반 자동 감지
// 🆕 업로드 후 제어 실행 설정
afterUploadFlows: config.excelAfterUploadFlows,
onSuccess: () => {