fix: preserve pending row edits when switching tabs (#362)

Wrap ContentArea in KeepAlive to cache tab instances across switches.
Add a module-level pending-changes cache in useDataGridEditor so edits
survive DataGrid destruction caused by result eviction and reload.
This commit is contained in:
t8y2 committed 2026-05-20 16:22:17 +08:00
1 parent fe6fead2dc
commit 47ec339d5a
8 files changed
+135 -65

No files matched your search

@@ -12,28 +12,35 @@ const ROW_NUM_WIDTH = 48;
export interface UseDataGridColumnResizeOptions {
columns: ComputedRef<string[]>;
rows: ComputedRef<CellValue[][]>;
sourceRows: ComputedRef<CellValue[][]>;
columnIndexes: ComputedRef<number[]>;
gridRef: Ref<HTMLDivElement | undefined>;
}
export function useDataGridColumnResize(options: UseDataGridColumnResizeOptions) {
const { columns, rows, gridRef } = options;
const { columns, sourceRows, columnIndexes, gridRef } = options;
const columnWidths = ref<number[]>([]);
const { width: gridWidth } = useElementSize(gridRef);
let isResizing = false;
function sampleColumnValues(rowData: CellValue[][], colIdx: number): CellValue[] {
return rowData.slice(0, DATA_GRID_SAMPLE_ROWS).map((row) => row[colIdx] ?? null);
function sampleColumnValues(visibleColIdx: number): CellValue[] {
const actualColIdx = columnIndexes.value[visibleColIdx];
const rows = sourceRows.value;
const end = Math.min(rows.length, DATA_GRID_SAMPLE_ROWS);
const values: CellValue[] = [];
for (let i = 0; i < end; i++) {
values.push(rows[i][actualColIdx] ?? null);
}
return values;
}
function initColumnWidths() {
if (columnWidths.value.length !== columns.value.length) {
const rowData = rows.value;
columnWidths.value = columns.value.map((colName, colIdx) => {
return calculateDataGridColumnWidth({
columnName: colName,
sampleValues: sampleColumnValues(rowData, colIdx),
sampleValues: sampleColumnValues(colIdx),
});
});
}
@@ -61,10 +68,9 @@ export function useDataGridColumnResize(options: UseDataGridColumnResizeOptions)
function autoFitColumn(colIdx: number) {
const colName = columns.value[colIdx];
if (!colName) return;
const rowData = rows.value;
columnWidths.value[colIdx] = calculateDataGridColumnWidth({
columnName: colName,
sampleValues: sampleColumnValues(rowData, colIdx),
sampleValues: sampleColumnValues(colIdx),
});
}
@@ -1,4 +1,4 @@
import { ref, computed, nextTick, watch, type ComputedRef, type Ref } from "vue";
import { ref, computed, nextTick, watch, onBeforeUnmount, type ComputedRef, type Ref } from "vue";
import * as api from "@/lib/api";
import {
buildDataGridRollbackStatements,
@@ -73,6 +73,7 @@ export interface UseDataGridEditorOptions {
getRowItem: (rowId: number) => RowItem | undefined;
pageSize: Ref<number>;
currentPage: Ref<number>;
cacheKey?: ComputedRef<string | undefined>;
emit: {
(
event: "reload",
@@ -86,6 +87,15 @@ export interface UseDataGridEditorOptions {
};
}
interface PendingChangesSnapshot {
newRows: CellValue[][];
dirtyRows: Map<number, Map<number, CellValue>>;
deletedRows: Set<number>;
columnCount: number;
}
const pendingChangesCache = new Map<string, PendingChangesSnapshot>();
export function useDataGridEditor(options: UseDataGridEditorOptions) {
const connectionStore = useConnectionStore();
const historyStore = useHistoryStore();
@@ -110,6 +120,7 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
getRowItem,
pageSize,
currentPage,
cacheKey,
} = options;
const editingCell = ref<{ rowId: number; col: number } | null>(null);
@@ -119,6 +130,20 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
const newRows = ref<CellValue[][]>([]);
const deletedRows = ref<Set<number>>(new Set());
// Restore cached pending changes from a previous instance (e.g. after result eviction + reload)
const key = cacheKey?.value;
if (key) {
const cached = pendingChangesCache.get(key);
if (cached && cached.columnCount === result.value.columns.length) {
newRows.value = cached.newRows;
dirtyRows.value = cached.dirtyRows;
deletedRows.value = cached.deletedRows;
pendingChangesCache.delete(key);
} else {
pendingChangesCache.delete(key);
}
}
const dirtyRowCount = computed(() => dirtyRows.value.size);
const newRowCount = computed(() => newRows.value.length);
const deletedRowCount = computed(() => deletedRows.value.size);
@@ -137,6 +162,10 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
(!!customSave?.value || (!!connectionId.value && !!database.value && !!tableMeta.value)),
);
if (hasPendingChanges.value && useTransaction.value) {
transactionActive.value = true;
}
function enterTransaction() {
transactionActive.value = true;
}
@@ -714,6 +743,21 @@ export function useDataGridEditor(options: UseDataGridEditorOptions) {
},
);
// Save pending changes before the component is destroyed so they can be
// restored if a new DataGrid instance is created for the same tab
// (e.g. after result eviction + reload).
onBeforeUnmount(() => {
const k = cacheKey?.value;
if (k && hasPendingChanges.value) {
pendingChangesCache.set(k, {
newRows: [...newRows.value.map((r) => [...r])],
dirtyRows: new Map([...dirtyRows.value].map(([i, m]) => [i, new Map(m)])),
deletedRows: new Set(deletedRows.value),
columnCount: result.value.columns.length,
});
}
});
return {
editingCell,
editValue,