feat(universal-form-modal): 조건부 테이블, 동적 Select 옵션, 서브 테이블 수정 로드 기능 구현

조건부 테이블: 체크박스/탭으로 조건 선택 시 다른 테이블 데이터 관리
동적 Select 옵션: 소스 테이블에서 드롭다운 옵션 동적 로드
행 선택 모드: Select 값 변경 시 같은 소스 행의 연관 컬럼 자동 채움
수정 모드 서브 테이블 로드: loadOnEdit 옵션으로 반복 섹션 데이터 자동 로드
SplitPanelLayout2 메인 테이블 병합: 서브 테이블 수정 시 메인 데이터 함께 조회
연결 필드 그룹 표시 형식: subDisplayColumn 추가로 메인/서브 컬럼 분리 설정
UX 개선: 컬럼 선택 UI를 검색 가능한 Combobox로 전환
saveMainAsFirst 로직 개선: items 없어도 메인 데이터 저장 가능
This commit is contained in:
SeongHyun Kim
2025-12-28 19:32:13 +09:00
parent 486e5ee29b
commit 47b23d1aa3
13 changed files with 3461 additions and 241 deletions

View File

@@ -675,7 +675,7 @@ export const SplitPanelLayout2Component: React.FC<SplitPanelLayout2ComponentProp
// 우측 패널 수정 버튼 클릭
const handleEditItem = useCallback(
(item: any) => {
async (item: any) => {
// 수정용 모달 화면 ID 결정 (editModalScreenId 우선, 없으면 addModalScreenId 사용)
const modalScreenId = config.rightPanel?.editModalScreenId || config.rightPanel?.addModalScreenId;
@@ -684,13 +684,42 @@ export const SplitPanelLayout2Component: React.FC<SplitPanelLayout2ComponentProp
return;
}
// 메인 테이블 데이터 조회 (우측 패널이 서브 테이블인 경우)
let editData = { ...item };
// 연결 설정이 있고, 메인 테이블이 설정되어 있으면 메인 테이블 데이터도 조회
if (config.rightPanel?.mainTableForEdit) {
const { tableName, linkColumn } = config.rightPanel.mainTableForEdit;
const linkValue = item[linkColumn?.subColumn || ""];
if (tableName && linkValue) {
try {
const response = await apiClient.get(`/table-management/tables/${tableName}/data`, {
params: {
filters: JSON.stringify({ [linkColumn?.mainColumn || linkColumn?.subColumn || ""]: linkValue }),
page: 1,
pageSize: 1,
},
});
if (response.data?.success && response.data?.data?.items?.[0]) {
// 메인 테이블 데이터를 editData에 병합 (서브 테이블 데이터 우선)
editData = { ...response.data.data.items[0], ...item };
console.log("[SplitPanelLayout2] 메인 테이블 데이터 병합:", editData);
}
} catch (error) {
console.error("[SplitPanelLayout2] 메인 테이블 데이터 조회 실패:", error);
}
}
}
// EditModal 열기 이벤트 발생 (수정 모드)
const event = new CustomEvent("openEditModal", {
detail: {
screenId: modalScreenId,
title: "수정",
modalSize: "lg",
editData: item, // 기존 데이터 전달
editData: editData, // 병합된 데이터 전달
isCreateMode: false, // 수정 모드
onSave: () => {
if (selectedLeftItem) {
@@ -700,9 +729,9 @@ export const SplitPanelLayout2Component: React.FC<SplitPanelLayout2ComponentProp
},
});
window.dispatchEvent(event);
console.log("[SplitPanelLayout2] 우측 수정 모달 열기:", item);
console.log("[SplitPanelLayout2] 우측 수정 모달 열기:", editData);
},
[config.rightPanel?.editModalScreenId, config.rightPanel?.addModalScreenId, selectedLeftItem, loadRightData],
[config.rightPanel?.editModalScreenId, config.rightPanel?.addModalScreenId, config.rightPanel?.mainTableForEdit, selectedLeftItem, loadRightData],
);
// 좌측 패널 수정 버튼 클릭
@@ -791,11 +820,11 @@ export const SplitPanelLayout2Component: React.FC<SplitPanelLayout2ComponentProp
toast.success(`${itemsToDelete.length}개 항목이 삭제되었습니다.`);
setSelectedRightItems(new Set<string | number>());
} else if (itemToDelete) {
// 단일 삭제 - 해당 항목 데이터를 body로 전달
// 단일 삭제 - 해당 항목 데이터를 배열로 감싸서 body로 전달 (백엔드가 배열을 기대함)
console.log(`[SplitPanelLayout2] ${deleteTargetPanel === "left" ? "좌측" : "우측"} 단일 삭제:`, itemToDelete);
await apiClient.delete(`/table-management/tables/${tableName}/delete`, {
data: itemToDelete,
data: [itemToDelete],
});
toast.success("항목이 삭제되었습니다.");
}