feat(SplitPanelLayout2): 추가 조인 테이블 기능 구현
- JoinTableConfig 타입 정의 (joinTable, joinType, mainColumn, joinColumn, selectColumns) - RightPanelConfig.joinTables 배열 추가로 다중 조인 지원 - loadJoinTableData(), mergeJoinData() 함수로 클라이언트 사이드 조인 처리 - JoinTableItem 컴포넌트로 조인 테이블 설정 UI 제공 - 표시 컬럼에 sourceTable 추가로 테이블별 컬럼 구분 - 메인+조인 테이블 컬럼 통합 로드 기능
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
||||
ColumnConfig,
|
||||
DataTransferField,
|
||||
ActionButtonConfig,
|
||||
JoinTableConfig,
|
||||
} from "./types";
|
||||
import { defaultConfig } from "./config";
|
||||
import { cn } from "@/lib/utils";
|
||||
@@ -128,6 +129,94 @@ export const SplitPanelLayout2Component: React.FC<SplitPanelLayout2ComponentProp
|
||||
}
|
||||
}, [config.leftPanel?.tableName, config.leftPanel?.hierarchyConfig, isDesignMode]);
|
||||
|
||||
// 조인 테이블 데이터 로드 (단일 테이블)
|
||||
const loadJoinTableData = useCallback(async (
|
||||
joinConfig: JoinTableConfig,
|
||||
mainData: any[]
|
||||
): Promise<Map<string, any>> => {
|
||||
const resultMap = new Map<string, any>();
|
||||
if (!joinConfig.joinTable || !joinConfig.mainColumn || !joinConfig.joinColumn || mainData.length === 0) {
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
// 메인 데이터에서 조인할 키 값들 추출
|
||||
const joinKeys = [...new Set(mainData.map((item) => item[joinConfig.mainColumn]).filter(Boolean))];
|
||||
if (joinKeys.length === 0) return resultMap;
|
||||
|
||||
try {
|
||||
console.log(`[SplitPanelLayout2] 조인 테이블 로드: ${joinConfig.joinTable}, 키: ${joinKeys.length}개`);
|
||||
|
||||
const response = await apiClient.post(`/table-management/tables/${joinConfig.joinTable}/data`, {
|
||||
page: 1,
|
||||
size: 1000,
|
||||
// 조인 키 값들로 필터링
|
||||
dataFilter: {
|
||||
enabled: true,
|
||||
matchType: "any", // OR 조건으로 여러 키 매칭
|
||||
filters: joinKeys.map((key, idx) => ({
|
||||
id: `join_key_${idx}`,
|
||||
columnName: joinConfig.joinColumn,
|
||||
operator: "equals",
|
||||
value: String(key),
|
||||
valueType: "static",
|
||||
})),
|
||||
},
|
||||
autoFilter: {
|
||||
enabled: true,
|
||||
filterColumn: "company_code",
|
||||
filterType: "company",
|
||||
},
|
||||
});
|
||||
|
||||
if (response.data.success) {
|
||||
const joinData = response.data.data?.data || [];
|
||||
// 조인 컬럼 값을 키로 하는 Map 생성
|
||||
joinData.forEach((item: any) => {
|
||||
const key = item[joinConfig.joinColumn];
|
||||
if (key) {
|
||||
resultMap.set(String(key), item);
|
||||
}
|
||||
});
|
||||
console.log(`[SplitPanelLayout2] 조인 테이블 로드 완료: ${joinData.length}건`);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`[SplitPanelLayout2] 조인 테이블 로드 실패 (${joinConfig.joinTable}):`, error);
|
||||
}
|
||||
|
||||
return resultMap;
|
||||
}, []);
|
||||
|
||||
// 메인 데이터에 조인 테이블 데이터 병합
|
||||
const mergeJoinData = useCallback((
|
||||
mainData: any[],
|
||||
joinConfig: JoinTableConfig,
|
||||
joinDataMap: Map<string, any>
|
||||
): any[] => {
|
||||
return mainData.map((item) => {
|
||||
const joinKey = item[joinConfig.mainColumn];
|
||||
const joinRow = joinDataMap.get(String(joinKey));
|
||||
|
||||
if (joinRow && joinConfig.selectColumns) {
|
||||
// 선택된 컬럼만 병합
|
||||
const mergedItem = { ...item };
|
||||
joinConfig.selectColumns.forEach((col) => {
|
||||
// alias가 있으면 alias_컬럼명, 없으면 그냥 컬럼명
|
||||
const targetKey = joinConfig.alias ? `${joinConfig.alias}_${col}` : col;
|
||||
// 메인 테이블에 같은 컬럼이 없으면 추가
|
||||
if (!(col in mergedItem)) {
|
||||
mergedItem[col] = joinRow[col];
|
||||
} else if (joinConfig.alias) {
|
||||
// 메인 테이블에 같은 컬럼이 있으면 alias로 추가
|
||||
mergedItem[targetKey] = joinRow[col];
|
||||
}
|
||||
});
|
||||
return mergedItem;
|
||||
}
|
||||
|
||||
return item;
|
||||
});
|
||||
}, []);
|
||||
|
||||
// 우측 데이터 로드 (좌측 선택 항목 기반)
|
||||
const loadRightData = useCallback(async (selectedItem: any) => {
|
||||
if (!config.rightPanel?.tableName || !config.joinConfig?.leftColumn || !config.joinConfig?.rightColumn || !selectedItem) {
|
||||
@@ -173,7 +262,24 @@ export const SplitPanelLayout2Component: React.FC<SplitPanelLayout2ComponentProp
|
||||
|
||||
if (response.data.success) {
|
||||
// API 응답 구조: { success: true, data: { data: [...], total, page, ... } }
|
||||
const data = response.data.data?.data || [];
|
||||
let data = response.data.data?.data || [];
|
||||
console.log(`[SplitPanelLayout2] 메인 데이터 로드 완료: ${data.length}건`);
|
||||
|
||||
// 추가 조인 테이블 처리
|
||||
const joinTables = config.rightPanel?.joinTables || [];
|
||||
if (joinTables.length > 0 && data.length > 0) {
|
||||
console.log(`[SplitPanelLayout2] 조인 테이블 처리 시작: ${joinTables.length}개`);
|
||||
|
||||
for (const joinTableConfig of joinTables) {
|
||||
const joinDataMap = await loadJoinTableData(joinTableConfig, data);
|
||||
if (joinDataMap.size > 0) {
|
||||
data = mergeJoinData(data, joinTableConfig, joinDataMap);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`[SplitPanelLayout2] 조인 데이터 병합 완료`);
|
||||
}
|
||||
|
||||
setRightData(data);
|
||||
console.log(`[SplitPanelLayout2] 우측 데이터 로드 완료: ${data.length}건`);
|
||||
} else {
|
||||
@@ -196,7 +302,7 @@ export const SplitPanelLayout2Component: React.FC<SplitPanelLayout2ComponentProp
|
||||
} finally {
|
||||
setRightLoading(false);
|
||||
}
|
||||
}, [config.rightPanel?.tableName, config.joinConfig]);
|
||||
}, [config.rightPanel?.tableName, config.rightPanel?.joinTables, config.joinConfig, loadJoinTableData, mergeJoinData]);
|
||||
|
||||
// 좌측 패널 추가 버튼 클릭
|
||||
const handleLeftAddClick = useCallback(() => {
|
||||
|
||||
Reference in New Issue
Block a user