엑셀 다운로드 문제 해결
This commit is contained in:
parent
82ff18e388
commit
b4cc844675
|
|
@ -44,6 +44,7 @@ export default function ScreenViewPage() {
|
|||
const [tableSortBy, setTableSortBy] = useState<string | undefined>();
|
||||
const [tableSortOrder, setTableSortOrder] = useState<"asc" | "desc">("asc");
|
||||
const [tableColumnOrder, setTableColumnOrder] = useState<string[] | undefined>();
|
||||
const [tableDisplayData, setTableDisplayData] = useState<any[]>([]); // 화면에 표시된 데이터 (컬럼 순서 포함)
|
||||
|
||||
// 플로우에서 선택된 데이터 (버튼 액션에 전달)
|
||||
const [flowSelectedData, setFlowSelectedData] = useState<any[]>([]);
|
||||
|
|
@ -433,13 +434,16 @@ export default function ScreenViewPage() {
|
|||
sortBy={tableSortBy}
|
||||
sortOrder={tableSortOrder}
|
||||
columnOrder={tableColumnOrder}
|
||||
onSelectedRowsChange={(_, selectedData, sortBy, sortOrder, columnOrder) => {
|
||||
tableDisplayData={tableDisplayData}
|
||||
onSelectedRowsChange={(_, selectedData, sortBy, sortOrder, columnOrder, tableDisplayData) => {
|
||||
console.log("🔍 화면에서 선택된 행 데이터:", selectedData);
|
||||
console.log("📊 정렬 정보:", { sortBy, sortOrder, columnOrder });
|
||||
console.log("📊 화면 표시 데이터:", { count: tableDisplayData?.length, firstRow: tableDisplayData?.[0] });
|
||||
setSelectedRowsData(selectedData);
|
||||
setTableSortBy(sortBy);
|
||||
setTableSortOrder(sortOrder || "asc");
|
||||
setTableColumnOrder(columnOrder);
|
||||
setTableDisplayData(tableDisplayData || []);
|
||||
}}
|
||||
flowSelectedData={flowSelectedData}
|
||||
flowSelectedStepId={flowSelectedStepId}
|
||||
|
|
@ -494,13 +498,16 @@ export default function ScreenViewPage() {
|
|||
sortBy={tableSortBy}
|
||||
sortOrder={tableSortOrder}
|
||||
columnOrder={tableColumnOrder}
|
||||
onSelectedRowsChange={(_, selectedData, sortBy, sortOrder, columnOrder) => {
|
||||
tableDisplayData={tableDisplayData}
|
||||
onSelectedRowsChange={(_, selectedData, sortBy, sortOrder, columnOrder, tableDisplayData) => {
|
||||
console.log("🔍 화면에서 선택된 행 데이터 (자식):", selectedData);
|
||||
console.log("📊 정렬 정보 (자식):", { sortBy, sortOrder, columnOrder });
|
||||
console.log("📊 화면 표시 데이터 (자식):", { count: tableDisplayData?.length, firstRow: tableDisplayData?.[0] });
|
||||
setSelectedRowsData(selectedData);
|
||||
setTableSortBy(sortBy);
|
||||
setTableSortOrder(sortOrder || "asc");
|
||||
setTableColumnOrder(columnOrder);
|
||||
setTableDisplayData(tableDisplayData || []);
|
||||
}}
|
||||
refreshKey={tableRefreshKey}
|
||||
onRefresh={() => {
|
||||
|
|
@ -631,6 +638,7 @@ export default function ScreenViewPage() {
|
|||
userId={user?.userId}
|
||||
userName={userName}
|
||||
companyCode={companyCode}
|
||||
tableDisplayData={tableDisplayData}
|
||||
selectedRowsData={selectedRowsData}
|
||||
sortBy={tableSortBy}
|
||||
sortOrder={tableSortOrder}
|
||||
|
|
|
|||
|
|
@ -66,6 +66,7 @@ interface RealtimePreviewProps {
|
|||
// 테이블 정렬 정보 전달용
|
||||
sortBy?: string;
|
||||
sortOrder?: "asc" | "desc";
|
||||
tableDisplayData?: any[]; // 🆕 화면 표시 데이터
|
||||
[key: string]: any; // 추가 props 허용
|
||||
}
|
||||
|
||||
|
|
@ -109,7 +110,14 @@ const renderArea = (component: ComponentData, children?: React.ReactNode) => {
|
|||
};
|
||||
|
||||
// 동적 웹 타입 위젯 렌더링 컴포넌트
|
||||
const WidgetRenderer: React.FC<{ component: ComponentData; isDesignMode?: boolean }> = ({ component, isDesignMode = false }) => {
|
||||
const WidgetRenderer: React.FC<{
|
||||
component: ComponentData;
|
||||
isDesignMode?: boolean;
|
||||
sortBy?: string;
|
||||
sortOrder?: "asc" | "desc";
|
||||
tableDisplayData?: any[];
|
||||
[key: string]: any;
|
||||
}> = ({ component, isDesignMode = false, sortBy, sortOrder, tableDisplayData, ...restProps }) => {
|
||||
// 위젯 컴포넌트가 아닌 경우 빈 div 반환
|
||||
if (!isWidgetComponent(component)) {
|
||||
return <div className="text-xs text-gray-500">위젯이 아닙니다</div>;
|
||||
|
|
@ -158,6 +166,9 @@ const WidgetRenderer: React.FC<{ component: ComponentData; isDesignMode?: boolea
|
|||
readonly: readonly,
|
||||
isDesignMode,
|
||||
isInteractive: !isDesignMode,
|
||||
sortBy, // 🆕 정렬 정보
|
||||
sortOrder, // 🆕 정렬 방향
|
||||
tableDisplayData, // 🆕 화면 표시 데이터
|
||||
}}
|
||||
config={widget.webTypeConfig}
|
||||
/>
|
||||
|
|
@ -231,6 +242,7 @@ export const RealtimePreviewDynamic: React.FC<RealtimePreviewProps> = ({
|
|||
onFlowSelectedDataChange,
|
||||
sortBy,
|
||||
sortOrder,
|
||||
tableDisplayData, // 🆕 화면 표시 데이터
|
||||
...restProps
|
||||
}) => {
|
||||
const { user } = useAuth();
|
||||
|
|
@ -557,6 +569,7 @@ export const RealtimePreviewDynamic: React.FC<RealtimePreviewProps> = ({
|
|||
isDesignMode={isDesignMode}
|
||||
sortBy={sortBy}
|
||||
sortOrder={sortOrder}
|
||||
tableDisplayData={tableDisplayData}
|
||||
{...restProps}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -29,10 +29,11 @@ export interface ComponentRenderer {
|
|||
// 테이블 선택된 행 정보 (다중 선택 액션용)
|
||||
selectedRows?: any[];
|
||||
selectedRowsData?: any[];
|
||||
onSelectedRowsChange?: (selectedRows: any[], selectedRowsData: any[], sortBy?: string, sortOrder?: "asc" | "desc", columnOrder?: string[]) => void;
|
||||
onSelectedRowsChange?: (selectedRows: any[], selectedRowsData: any[], sortBy?: string, sortOrder?: "asc" | "desc", columnOrder?: string[], tableDisplayData?: any[]) => void;
|
||||
// 테이블 정렬 정보 (엑셀 다운로드용)
|
||||
sortBy?: string;
|
||||
sortOrder?: "asc" | "desc";
|
||||
tableDisplayData?: any[]; // 🆕 화면 표시 데이터
|
||||
// 플로우 선택된 데이터 정보 (플로우 위젯 선택 액션용)
|
||||
flowSelectedData?: any[];
|
||||
flowSelectedStepId?: number | null;
|
||||
|
|
@ -104,11 +105,12 @@ export interface DynamicComponentRendererProps {
|
|||
// 테이블 선택된 행 정보 (다중 선택 액션용)
|
||||
selectedRows?: any[];
|
||||
selectedRowsData?: any[];
|
||||
onSelectedRowsChange?: (selectedRows: any[], selectedRowsData: any[], sortBy?: string, sortOrder?: "asc" | "desc", columnOrder?: string[]) => void;
|
||||
onSelectedRowsChange?: (selectedRows: any[], selectedRowsData: any[], sortBy?: string, sortOrder?: "asc" | "desc", columnOrder?: string[], tableDisplayData?: any[]) => void;
|
||||
// 테이블 정렬 정보 (엑셀 다운로드용)
|
||||
sortBy?: string;
|
||||
sortOrder?: "asc" | "desc";
|
||||
columnOrder?: string[];
|
||||
tableDisplayData?: any[]; // 🆕 화면 표시 데이터
|
||||
// 플로우 선택된 데이터 정보 (플로우 위젯 선택 액션용)
|
||||
flowSelectedData?: any[];
|
||||
flowSelectedStepId?: number | null;
|
||||
|
|
@ -200,6 +202,7 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
|
|||
onSelectedRowsChange,
|
||||
sortBy, // 🆕 정렬 컬럼
|
||||
sortOrder, // 🆕 정렬 방향
|
||||
tableDisplayData, // 🆕 화면 표시 데이터
|
||||
flowSelectedData,
|
||||
flowSelectedStepId,
|
||||
onFlowSelectedDataChange,
|
||||
|
|
@ -290,6 +293,7 @@ export const DynamicComponentRenderer: React.FC<DynamicComponentRendererProps> =
|
|||
// 테이블 정렬 정보 전달
|
||||
sortBy,
|
||||
sortOrder,
|
||||
tableDisplayData, // 🆕 화면 표시 데이터
|
||||
// 플로우 선택된 데이터 정보 전달
|
||||
flowSelectedData,
|
||||
flowSelectedStepId,
|
||||
|
|
|
|||
|
|
@ -47,6 +47,7 @@ export interface ButtonPrimaryComponentProps extends ComponentRendererProps {
|
|||
sortBy?: string;
|
||||
sortOrder?: "asc" | "desc";
|
||||
columnOrder?: string[];
|
||||
tableDisplayData?: any[]; // 화면에 표시된 데이터 (컬럼 순서 포함)
|
||||
|
||||
// 플로우 선택된 데이터 정보 (플로우 위젯 선택 액션용)
|
||||
flowSelectedData?: any[];
|
||||
|
|
@ -82,6 +83,7 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
|||
sortBy, // 🆕 정렬 컬럼
|
||||
sortOrder, // 🆕 정렬 방향
|
||||
columnOrder, // 🆕 컬럼 순서
|
||||
tableDisplayData, // 🆕 화면에 표시된 데이터
|
||||
selectedRows,
|
||||
selectedRowsData,
|
||||
flowSelectedData,
|
||||
|
|
@ -417,6 +419,7 @@ export const ButtonPrimaryComponent: React.FC<ButtonPrimaryComponentProps> = ({
|
|||
sortBy, // 🆕 정렬 컬럼
|
||||
sortOrder, // 🆕 정렬 방향
|
||||
columnOrder, // 🆕 컬럼 순서
|
||||
tableDisplayData, // 🆕 화면에 표시된 데이터
|
||||
// 플로우 선택된 데이터 정보 추가
|
||||
flowSelectedData,
|
||||
flowSelectedStepId,
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import {
|
|||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { toast } from "sonner";
|
||||
import { tableDisplayStore } from "@/stores/tableDisplayStore";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
|
|
@ -139,7 +140,7 @@ export interface TableListComponentProps {
|
|||
onClose?: () => void;
|
||||
screenId?: string;
|
||||
userId?: string; // 사용자 ID (컬럼 순서 저장용)
|
||||
onSelectedRowsChange?: (selectedRows: any[], selectedRowsData: any[], sortBy?: string, sortOrder?: "asc" | "desc", columnOrder?: string[]) => void;
|
||||
onSelectedRowsChange?: (selectedRows: any[], selectedRowsData: any[], sortBy?: string, sortOrder?: "asc" | "desc", columnOrder?: string[], tableDisplayData?: any[]) => void;
|
||||
onConfigChange?: (config: any) => void;
|
||||
refreshKey?: number;
|
||||
}
|
||||
|
|
@ -266,6 +267,62 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
const [groupByColumns, setGroupByColumns] = useState<string[]>([]);
|
||||
const [collapsedGroups, setCollapsedGroups] = useState<Set<string>>(new Set());
|
||||
|
||||
// 🆕 초기 로드 시 localStorage에서 컬럼 순서 불러오기
|
||||
useEffect(() => {
|
||||
if (!tableConfig.selectedTable || !userId) return;
|
||||
|
||||
const userKey = userId || 'guest';
|
||||
const storageKey = `table_column_order_${tableConfig.selectedTable}_${userKey}`;
|
||||
const savedOrder = localStorage.getItem(storageKey);
|
||||
|
||||
if (savedOrder) {
|
||||
try {
|
||||
const parsedOrder = JSON.parse(savedOrder);
|
||||
console.log("📂 localStorage에서 컬럼 순서 불러오기:", { storageKey, columnOrder: parsedOrder });
|
||||
setColumnOrder(parsedOrder);
|
||||
|
||||
// 부모 컴포넌트에 초기 컬럼 순서 전달
|
||||
if (onSelectedRowsChange && parsedOrder.length > 0) {
|
||||
console.log("✅ 초기 컬럼 순서 전달:", parsedOrder);
|
||||
|
||||
// 초기 데이터도 함께 전달 (컬럼 순서대로 재정렬)
|
||||
const initialData = data.map((row: any) => {
|
||||
const reordered: any = {};
|
||||
parsedOrder.forEach((colName: string) => {
|
||||
if (colName in row) {
|
||||
reordered[colName] = row[colName];
|
||||
}
|
||||
});
|
||||
// 나머지 컬럼 추가
|
||||
Object.keys(row).forEach((key) => {
|
||||
if (!(key in reordered)) {
|
||||
reordered[key] = row[key];
|
||||
}
|
||||
});
|
||||
return reordered;
|
||||
});
|
||||
|
||||
console.log("📊 초기 화면 표시 데이터 전달:", { count: initialData.length, firstRow: initialData[0] });
|
||||
|
||||
// 전역 저장소에 데이터 저장
|
||||
if (tableConfig.selectedTable) {
|
||||
tableDisplayStore.setTableData(
|
||||
tableConfig.selectedTable,
|
||||
initialData,
|
||||
parsedOrder.filter(col => col !== '__checkbox__'),
|
||||
sortColumn,
|
||||
sortDirection
|
||||
);
|
||||
}
|
||||
|
||||
onSelectedRowsChange([], [], sortColumn, sortDirection, parsedOrder, initialData);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("❌ 컬럼 순서 파싱 실패:", error);
|
||||
}
|
||||
}
|
||||
}, [tableConfig.selectedTable, userId, data.length]); // data.length 추가 (데이터 로드 후 실행)
|
||||
|
||||
const { optimizedConvertCode } = useEntityJoinOptimization(columnMeta, {
|
||||
enableBatchLoading: true,
|
||||
preloadCommonCodes: true,
|
||||
|
|
@ -499,20 +556,78 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
// 정렬 변경 시 선택 정보와 함께 정렬 정보도 전달
|
||||
if (onSelectedRowsChange) {
|
||||
const selectedRowsData = data.filter((row, index) => selectedRows.has(getRowKey(row, index)));
|
||||
|
||||
// 1단계: 데이터를 정렬
|
||||
const sortedData = [...data].sort((a, b) => {
|
||||
const aVal = a[newSortColumn];
|
||||
const bVal = b[newSortColumn];
|
||||
|
||||
// null/undefined 처리
|
||||
if (aVal == null && bVal == null) return 0;
|
||||
if (aVal == null) return 1;
|
||||
if (bVal == null) return -1;
|
||||
|
||||
// 숫자 비교
|
||||
const aNum = Number(aVal);
|
||||
const bNum = Number(bVal);
|
||||
if (!isNaN(aNum) && !isNaN(bNum)) {
|
||||
return newSortDirection === "desc" ? bNum - aNum : aNum - bNum;
|
||||
}
|
||||
|
||||
// 문자열 비교
|
||||
const aStr = String(aVal);
|
||||
const bStr = String(bVal);
|
||||
const comparison = aStr.localeCompare(bStr);
|
||||
return newSortDirection === "desc" ? -comparison : comparison;
|
||||
});
|
||||
|
||||
// 2단계: 정렬된 데이터를 컬럼 순서대로 재정렬
|
||||
const reorderedData = sortedData.map((row: any) => {
|
||||
const reordered: any = {};
|
||||
visibleColumns.forEach((col) => {
|
||||
if (col.columnName in row) {
|
||||
reordered[col.columnName] = row[col.columnName];
|
||||
}
|
||||
});
|
||||
// 나머지 컬럼 추가
|
||||
Object.keys(row).forEach((key) => {
|
||||
if (!(key in reordered)) {
|
||||
reordered[key] = row[key];
|
||||
}
|
||||
});
|
||||
return reordered;
|
||||
});
|
||||
|
||||
console.log("✅ 정렬 정보 전달:", {
|
||||
selectedRowsCount: selectedRows.size,
|
||||
selectedRowsDataCount: selectedRowsData.length,
|
||||
sortBy: newSortColumn,
|
||||
sortOrder: newSortDirection,
|
||||
columnOrder: columnOrder.length > 0 ? columnOrder : undefined
|
||||
columnOrder: columnOrder.length > 0 ? columnOrder : undefined,
|
||||
tableDisplayDataCount: reorderedData.length,
|
||||
firstRowAfterSort: reorderedData[0]?.[newSortColumn],
|
||||
lastRowAfterSort: reorderedData[reorderedData.length - 1]?.[newSortColumn]
|
||||
});
|
||||
onSelectedRowsChange(
|
||||
Array.from(selectedRows),
|
||||
selectedRowsData,
|
||||
newSortColumn,
|
||||
newSortDirection,
|
||||
columnOrder.length > 0 ? columnOrder : undefined
|
||||
columnOrder.length > 0 ? columnOrder : undefined,
|
||||
reorderedData
|
||||
);
|
||||
|
||||
// 전역 저장소에 정렬된 데이터 저장
|
||||
if (tableConfig.selectedTable) {
|
||||
const cleanColumnOrder = (columnOrder.length > 0 ? columnOrder : visibleColumns.map(c => c.columnName)).filter(col => col !== '__checkbox__');
|
||||
tableDisplayStore.setTableData(
|
||||
tableConfig.selectedTable,
|
||||
reorderedData,
|
||||
cleanColumnOrder,
|
||||
newSortColumn,
|
||||
newSortDirection
|
||||
);
|
||||
}
|
||||
} else {
|
||||
console.warn("⚠️ onSelectedRowsChange 콜백이 없습니다!");
|
||||
}
|
||||
|
|
@ -653,6 +768,55 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
setColumnOrder(newColumnOrder);
|
||||
console.log("🔄 columnOrder 상태 업데이트:", newColumnOrder);
|
||||
|
||||
// 컬럼 순서 변경을 부모 컴포넌트에 전달 (엑셀 다운로드용)
|
||||
if (onSelectedRowsChange) {
|
||||
const selectedRowsData = data.filter((row, index) => selectedRows.has(getRowKey(row, index)));
|
||||
|
||||
// 화면에 표시된 데이터를 새로운 컬럼 순서대로 재정렬
|
||||
const reorderedData = data.map((row: any) => {
|
||||
const reordered: any = {};
|
||||
newColumns.forEach((col) => {
|
||||
if (col.columnName in row) {
|
||||
reordered[col.columnName] = row[col.columnName];
|
||||
}
|
||||
});
|
||||
// 나머지 컬럼 추가
|
||||
Object.keys(row).forEach((key) => {
|
||||
if (!(key in reordered)) {
|
||||
reordered[key] = row[key];
|
||||
}
|
||||
});
|
||||
return reordered;
|
||||
});
|
||||
|
||||
console.log("✅ 컬럼 순서 변경 정보 전달:", {
|
||||
columnOrder: newColumnOrder,
|
||||
sortBy: sortColumn,
|
||||
sortOrder: sortDirection,
|
||||
reorderedDataCount: reorderedData.length
|
||||
});
|
||||
onSelectedRowsChange(
|
||||
Array.from(selectedRows),
|
||||
selectedRowsData,
|
||||
sortColumn,
|
||||
sortDirection,
|
||||
newColumnOrder,
|
||||
reorderedData
|
||||
);
|
||||
|
||||
// 전역 저장소에 컬럼 순서 변경된 데이터 저장
|
||||
if (tableConfig.selectedTable) {
|
||||
const cleanColumnOrder = newColumnOrder.filter(col => col !== '__checkbox__');
|
||||
tableDisplayStore.setTableData(
|
||||
tableConfig.selectedTable,
|
||||
reorderedData,
|
||||
cleanColumnOrder,
|
||||
sortColumn,
|
||||
sortDirection
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
setDraggedColumnIndex(null);
|
||||
setDragOverColumnIndex(null);
|
||||
};
|
||||
|
|
@ -714,6 +878,78 @@ export const TableListComponent: React.FC<TableListComponentProps> = ({
|
|||
return cols.sort((a, b) => (a.order || 0) - (b.order || 0));
|
||||
}, [tableConfig.columns, tableConfig.checkbox, columnOrder]);
|
||||
|
||||
// 🆕 visibleColumns가 변경될 때마다 현재 컬럼 순서를 부모에게 전달
|
||||
const lastColumnOrderRef = useRef<string>("");
|
||||
|
||||
useEffect(() => {
|
||||
console.log("🔍 [컬럼 순서 전달 useEffect] 실행됨:", {
|
||||
hasCallback: !!onSelectedRowsChange,
|
||||
visibleColumnsLength: visibleColumns.length,
|
||||
visibleColumnsNames: visibleColumns.map(c => c.columnName),
|
||||
});
|
||||
|
||||
if (!onSelectedRowsChange) {
|
||||
console.warn("⚠️ onSelectedRowsChange 콜백이 없습니다!");
|
||||
return;
|
||||
}
|
||||
|
||||
if (visibleColumns.length === 0) {
|
||||
console.warn("⚠️ visibleColumns가 비어있습니다!");
|
||||
return;
|
||||
}
|
||||
|
||||
const currentColumnOrder = visibleColumns
|
||||
.map(col => col.columnName)
|
||||
.filter(name => name !== "__checkbox__"); // 체크박스 컬럼 제외
|
||||
|
||||
console.log("🔍 [컬럼 순서] 체크박스 제외 후:", currentColumnOrder);
|
||||
|
||||
// 컬럼 순서가 실제로 변경되었을 때만 전달 (무한 루프 방지)
|
||||
const columnOrderString = currentColumnOrder.join(",");
|
||||
console.log("🔍 [컬럼 순서] 비교:", {
|
||||
current: columnOrderString,
|
||||
last: lastColumnOrderRef.current,
|
||||
isDifferent: columnOrderString !== lastColumnOrderRef.current,
|
||||
});
|
||||
|
||||
if (columnOrderString === lastColumnOrderRef.current) {
|
||||
console.log("⏭️ 컬럼 순서 변경 없음, 전달 스킵");
|
||||
return;
|
||||
}
|
||||
|
||||
lastColumnOrderRef.current = columnOrderString;
|
||||
console.log("📊 현재 화면 컬럼 순서 전달:", currentColumnOrder);
|
||||
|
||||
// 선택된 행 데이터 가져오기
|
||||
const selectedRowsData = data.filter((row, index) => selectedRows.has(getRowKey(row, index)));
|
||||
|
||||
// 화면에 표시된 데이터를 컬럼 순서대로 재정렬
|
||||
const reorderedData = data.map((row: any) => {
|
||||
const reordered: any = {};
|
||||
visibleColumns.forEach((col) => {
|
||||
if (col.columnName in row) {
|
||||
reordered[col.columnName] = row[col.columnName];
|
||||
}
|
||||
});
|
||||
// 나머지 컬럼 추가
|
||||
Object.keys(row).forEach((key) => {
|
||||
if (!(key in reordered)) {
|
||||
reordered[key] = row[key];
|
||||
}
|
||||
});
|
||||
return reordered;
|
||||
});
|
||||
|
||||
onSelectedRowsChange(
|
||||
Array.from(selectedRows),
|
||||
selectedRowsData,
|
||||
sortColumn,
|
||||
sortDirection,
|
||||
currentColumnOrder,
|
||||
reorderedData
|
||||
);
|
||||
}, [visibleColumns.length, visibleColumns.map(c => c.columnName).join(",")]); // 의존성 단순화
|
||||
|
||||
const getColumnWidth = (column: ColumnConfig) => {
|
||||
if (column.columnName === "__checkbox__") return 50;
|
||||
if (column.width) return column.width;
|
||||
|
|
|
|||
|
|
@ -1689,6 +1689,17 @@ export class ButtonActionExecutor {
|
|||
private static async handleExcelDownload(config: ButtonActionConfig, context: ButtonActionContext): Promise<boolean> {
|
||||
try {
|
||||
console.log("📥 엑셀 다운로드 시작:", { config, context });
|
||||
console.log("🔍 context.columnOrder 확인:", {
|
||||
hasColumnOrder: !!context.columnOrder,
|
||||
columnOrderLength: context.columnOrder?.length,
|
||||
columnOrder: context.columnOrder,
|
||||
});
|
||||
console.log("🔍 context.tableDisplayData 확인:", {
|
||||
hasTableDisplayData: !!context.tableDisplayData,
|
||||
tableDisplayDataLength: context.tableDisplayData?.length,
|
||||
tableDisplayDataFirstRow: context.tableDisplayData?.[0],
|
||||
tableDisplayDataColumns: context.tableDisplayData?.[0] ? Object.keys(context.tableDisplayData[0]) : [],
|
||||
});
|
||||
|
||||
// 동적 import로 엑셀 유틸리티 로드
|
||||
const { exportToExcel } = await import("@/lib/utils/excelExport");
|
||||
|
|
@ -1736,8 +1747,38 @@ export class ButtonActionExecutor {
|
|||
});
|
||||
}
|
||||
}
|
||||
// 2순위: 테이블 전체 데이터 (API 호출)
|
||||
// 2순위: 화면 표시 데이터 (컬럼 순서 포함, 정렬 적용됨)
|
||||
else if (context.tableDisplayData && context.tableDisplayData.length > 0) {
|
||||
dataToExport = context.tableDisplayData;
|
||||
console.log("✅ 화면 표시 데이터 사용 (context):", {
|
||||
count: dataToExport.length,
|
||||
firstRow: dataToExport[0],
|
||||
columns: Object.keys(dataToExport[0] || {}),
|
||||
});
|
||||
}
|
||||
// 2.5순위: 전역 저장소에서 화면 표시 데이터 조회
|
||||
else if (context.tableName) {
|
||||
const { tableDisplayStore } = await import("@/stores/tableDisplayStore");
|
||||
const storedData = tableDisplayStore.getTableData(context.tableName);
|
||||
|
||||
if (storedData && storedData.data.length > 0) {
|
||||
dataToExport = storedData.data;
|
||||
console.log("✅ 화면 표시 데이터 사용 (전역 저장소):", {
|
||||
tableName: context.tableName,
|
||||
count: dataToExport.length,
|
||||
firstRow: dataToExport[0],
|
||||
lastRow: dataToExport[dataToExport.length - 1],
|
||||
columns: Object.keys(dataToExport[0] || {}),
|
||||
columnOrder: storedData.columnOrder,
|
||||
sortBy: storedData.sortBy,
|
||||
sortOrder: storedData.sortOrder,
|
||||
// 정렬 컬럼의 첫/마지막 값 확인
|
||||
firstSortValue: storedData.sortBy ? dataToExport[0]?.[storedData.sortBy] : undefined,
|
||||
lastSortValue: storedData.sortBy ? dataToExport[dataToExport.length - 1]?.[storedData.sortBy] : undefined,
|
||||
});
|
||||
}
|
||||
// 3순위: 테이블 전체 데이터 (API 호출)
|
||||
else {
|
||||
console.log("🔄 테이블 전체 데이터 조회 중...", context.tableName);
|
||||
console.log("📊 정렬 정보:", {
|
||||
sortBy: context.sortBy,
|
||||
|
|
@ -1773,6 +1814,7 @@ export class ButtonActionExecutor {
|
|||
} catch (error) {
|
||||
console.error("❌ 테이블 데이터 조회 실패:", error);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 4순위: 폼 데이터
|
||||
else if (context.formData && Object.keys(context.formData).length > 0) {
|
||||
|
|
@ -1814,15 +1856,26 @@ export class ButtonActionExecutor {
|
|||
const sheetName = config.excelSheetName || "Sheet1";
|
||||
const includeHeaders = config.excelIncludeHeaders !== false;
|
||||
|
||||
// 🆕 컬럼 순서 재정렬 (사용자가 드래그앤드롭으로 변경한 순서 적용)
|
||||
if (context.columnOrder && context.columnOrder.length > 0 && dataToExport.length > 0) {
|
||||
console.log("🔄 컬럼 순서 재정렬:", context.columnOrder);
|
||||
// 🆕 컬럼 순서 재정렬 (화면에 표시된 순서대로)
|
||||
let columnOrder: string[] | undefined = context.columnOrder;
|
||||
|
||||
// columnOrder가 없으면 tableDisplayData에서 추출 시도
|
||||
if (!columnOrder && context.tableDisplayData && context.tableDisplayData.length > 0) {
|
||||
columnOrder = Object.keys(context.tableDisplayData[0]);
|
||||
console.log("📊 tableDisplayData에서 컬럼 순서 추출:", columnOrder);
|
||||
}
|
||||
|
||||
if (columnOrder && columnOrder.length > 0 && dataToExport.length > 0) {
|
||||
console.log("🔄 컬럼 순서 재정렬 시작:", {
|
||||
columnOrder,
|
||||
originalColumns: Object.keys(dataToExport[0] || {}),
|
||||
});
|
||||
|
||||
dataToExport = dataToExport.map((row: any) => {
|
||||
const reorderedRow: any = {};
|
||||
|
||||
// 1. columnOrder에 있는 컬럼들을 순서대로 추가
|
||||
context.columnOrder!.forEach((colName: string) => {
|
||||
columnOrder!.forEach((colName: string) => {
|
||||
if (colName in row) {
|
||||
reorderedRow[colName] = row[colName];
|
||||
}
|
||||
|
|
@ -1839,9 +1892,15 @@ export class ButtonActionExecutor {
|
|||
});
|
||||
|
||||
console.log("✅ 컬럼 순서 재정렬 완료:", {
|
||||
originalColumns: Object.keys(dataToExport[0] || {}),
|
||||
reorderedColumns: Object.keys(dataToExport[0] || {}),
|
||||
});
|
||||
} else {
|
||||
console.log("⏭️ 컬럼 순서 재정렬 스킵:", {
|
||||
hasColumnOrder: !!columnOrder,
|
||||
columnOrderLength: columnOrder?.length,
|
||||
hasTableDisplayData: !!context.tableDisplayData,
|
||||
dataToExportLength: dataToExport.length,
|
||||
});
|
||||
}
|
||||
|
||||
console.log("📥 엑셀 다운로드 실행:", {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,110 @@
|
|||
/**
|
||||
* 테이블 화면 표시 데이터 전역 저장소
|
||||
* 엑셀 다운로드 등에서 현재 화면에 표시된 데이터에 접근하기 위함
|
||||
*/
|
||||
|
||||
interface TableDisplayState {
|
||||
data: any[];
|
||||
columnOrder: string[];
|
||||
sortBy: string | null;
|
||||
sortOrder: "asc" | "desc";
|
||||
tableName: string;
|
||||
}
|
||||
|
||||
class TableDisplayStore {
|
||||
private state: Map<string, TableDisplayState> = new Map();
|
||||
private listeners: Set<() => void> = new Set();
|
||||
|
||||
/**
|
||||
* 테이블 표시 데이터 저장
|
||||
* @param tableName 테이블명
|
||||
* @param data 화면에 표시된 데이터
|
||||
* @param columnOrder 컬럼 순서
|
||||
* @param sortBy 정렬 컬럼
|
||||
* @param sortOrder 정렬 방향
|
||||
*/
|
||||
setTableData(
|
||||
tableName: string,
|
||||
data: any[],
|
||||
columnOrder: string[],
|
||||
sortBy: string | null,
|
||||
sortOrder: "asc" | "desc"
|
||||
) {
|
||||
this.state.set(tableName, {
|
||||
data,
|
||||
columnOrder,
|
||||
sortBy,
|
||||
sortOrder,
|
||||
tableName,
|
||||
});
|
||||
|
||||
console.log("📦 [TableDisplayStore] 데이터 저장:", {
|
||||
tableName,
|
||||
dataCount: data.length,
|
||||
columnOrderLength: columnOrder.length,
|
||||
sortBy,
|
||||
sortOrder,
|
||||
firstRow: data[0],
|
||||
});
|
||||
|
||||
this.notifyListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* 테이블 표시 데이터 조회
|
||||
* @param tableName 테이블명
|
||||
*/
|
||||
getTableData(tableName: string): TableDisplayState | undefined {
|
||||
const state = this.state.get(tableName);
|
||||
|
||||
console.log("📤 [TableDisplayStore] 데이터 조회:", {
|
||||
tableName,
|
||||
found: !!state,
|
||||
dataCount: state?.data.length,
|
||||
});
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
/**
|
||||
* 모든 테이블 데이터 조회
|
||||
*/
|
||||
getAllTableData(): Map<string, TableDisplayState> {
|
||||
return new Map(this.state);
|
||||
}
|
||||
|
||||
/**
|
||||
* 테이블 데이터 삭제
|
||||
* @param tableName 테이블명
|
||||
*/
|
||||
clearTableData(tableName: string) {
|
||||
this.state.delete(tableName);
|
||||
this.notifyListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* 모든 데이터 삭제
|
||||
*/
|
||||
clearAll() {
|
||||
this.state.clear();
|
||||
this.notifyListeners();
|
||||
}
|
||||
|
||||
/**
|
||||
* 변경 리스너 등록
|
||||
*/
|
||||
subscribe(listener: () => void) {
|
||||
this.listeners.add(listener);
|
||||
return () => {
|
||||
this.listeners.delete(listener);
|
||||
};
|
||||
}
|
||||
|
||||
private notifyListeners() {
|
||||
this.listeners.forEach((listener) => listener());
|
||||
}
|
||||
}
|
||||
|
||||
// 싱글톤 인스턴스
|
||||
export const tableDisplayStore = new TableDisplayStore();
|
||||
|
||||
Loading…
Reference in New Issue