feat(structure): persist local column order

This commit is contained in:
zipg authored and GitHub committed 2026-07-12 12:06:49 +08:00
1 parent c3ec7fceef
commit 0650428d1c
13 files changed
+278 -9

No files matched your search

@@ -28,7 +28,9 @@ import { safeLocalStorageGet, safeLocalStorageSet } from "@/lib/backend/safeStor
import { type BuildTableStructureChangeSqlOptions, type EditableStructureColumn, type EditableStructureForeignKey, type EditableStructureIndex, type EditableStructureTrigger } from "@/lib/table/tableStructureEditorSql";
import { PRESET_FIELDS_TEMPLATE_ID, createTableColumnTemplateDrafts } from "@/lib/table/tableColumnTemplates";
import { getTableMetadataCapabilities, firstStructureMetadataTab, isStructureMetadataTabSupported } from "@/lib/table/tableMetadataCapabilities";
import { canAddTableStructureColumn, getTableStructureCapabilities } from "@/lib/table/tableStructureCapabilities";
import { canAddTableStructureColumn, getTableStructureCapabilities, hasLocalTableColumnOrderChange, isPhysicalTableColumnOrderChange, supportsLocalTableColumnReorder } from "@/lib/table/tableStructureCapabilities";
import { orderedColumnIndexes, uniqueDataGridColumnOrderKeys } from "@/lib/dataGrid/dataGridColumnOrder";
import { loadTableDataGridColumnOrder, notifyTableDataGridColumnOrderChanged, removeTableDataGridColumnOrder, saveTableDataGridColumnOrder, tableDataGridColumnOrderScopeKey } from "@/lib/dataGrid/dataGridColumnLayoutStorage";
import { connectionObjectTreeQuerySchema, tableStructureDatabaseTypeForConnection } from "@/lib/database/jdbcDialect";
import type { TableInfoTab, TableStructureEditorDraft, TableStructureEditorTarget, TableStructureEditorViewport } from "@/types/database";
import {
@@ -191,7 +193,7 @@ function columnChanged(column: EditableStructureColumn, index: number): boolean
if (!column.original || column.markedForDrop) return true;
const original = column.original;
return (
column.originalPosition !== index ||
isPhysicalTableColumnOrderChange(databaseType.value, connection.value?.db_type, column.originalPosition, index) ||
column.name !== original.name ||
column.dataType !== original.data_type ||
column.isNullable !== original.is_nullable ||
@@ -1152,6 +1154,7 @@ function resetState() {
highlightedIndexId.value = null;
appliedInitialTargetSearchKey = "";
appliedInitialTargetScrollKey = "";
localColumnOrderNoticeShown.value = false;
}
async function reloadStructureFromDatabase() {
@@ -1216,7 +1219,7 @@ async function loadStructure(silent = false, scope: StructureRefreshScope = FULL
// Load live charset/collation metadata from the MySQL server so the column
// editor shows the correct options for the server version.
void loadCharsetMetadata();
columns.value = createColumnDrafts(nextColumns, databaseType.value);
columns.value = applyStoredLocalColumnOrder(createColumnDrafts(nextColumns, databaseType.value));
}
const nextTableComment = await tableCommentPromise;
@@ -1325,6 +1328,7 @@ type ColumnDragState = {
};
const columnDragState = ref<ColumnDragState | null>(null);
const localColumnOrderNoticeShown = ref(false);
let columnDragPreviousBodyUserSelect = "";
let columnDragPreviousBodyCursor = "";
let columnDragTracking = false;
@@ -1350,7 +1354,53 @@ function canDropColumnAt(sourceIndex: number, insertionIndex: number): boolean {
return crossedColumns.every((column) => !column.original);
}
const canShowColumnDragControls = computed(() => isCreateMode.value || structureCapabilities.value.reorderColumn);
const usesLocalTableColumnOrder = computed(() => !isCreateMode.value && supportsLocalTableColumnReorder(databaseType.value, connection.value?.db_type));
const canShowColumnDragControls = computed(() => isCreateMode.value || structureCapabilities.value.reorderColumn || usesLocalTableColumnOrder.value);
function localTableColumnOrderScopeKey(): string {
return tableDataGridColumnOrderScopeKey({
connectionId: props.connectionId,
database: props.database,
schema: props.schema,
tableName: props.tableName,
});
}
function localColumnOrderKeys(items: readonly EditableStructureColumn[]): string[] {
return uniqueDataGridColumnOrderKeys(items.map((column) => column.name));
}
const hasLocalColumnOrderChange = computed(() => {
if (!usesLocalTableColumnOrder.value) return false;
return hasLocalTableColumnOrderChange(columns.value);
});
function applyStoredLocalColumnOrder(items: EditableStructureColumn[]): EditableStructureColumn[] {
if (!usesLocalTableColumnOrder.value) return items;
const orderedKeys = loadTableDataGridColumnOrder(localTableColumnOrderScopeKey());
if (!orderedKeys.length) return items;
const columnKeys = uniqueDataGridColumnOrderKeys(items.map((column) => column.name));
const indexes = orderedColumnIndexes({
availableIndexes: items.map((_, index) => index),
columnKeys,
orderedKeys,
});
return indexes.map((index) => items[index]).filter((column): column is EditableStructureColumn => !!column);
}
function persistLocalColumnOrder(showNotice = true) {
if (!usesLocalTableColumnOrder.value) return;
const scopeKey = localTableColumnOrderScopeKey();
if (hasLocalColumnOrderChange.value) {
saveTableDataGridColumnOrder(scopeKey, localColumnOrderKeys(columns.value));
} else {
removeTableDataGridColumnOrder(scopeKey);
}
notifyTableDataGridColumnOrderChanged(scopeKey);
if (!showNotice || localColumnOrderNoticeShown.value) return;
localColumnOrderNoticeShown.value = true;
toast(t("structureEditor.localColumnOrderNotice"), 4000);
}
function isSqlServerIdentityChecked(column: EditableStructureColumn): boolean {
return !!column.extra.autoIncrement || !!column.extra.identity;
@@ -1510,6 +1560,7 @@ function moveColumnTo(index: number, insertionIndex: number) {
const adjustedInsertionIndex = insertionIndex > index ? insertionIndex - 1 : insertionIndex;
nextColumns.splice(adjustedInsertionIndex, 0, column);
columns.value = nextColumns;
persistLocalColumnOrder();
}
function onColumnDragPointerDown(index: number, event: PointerEvent) {
@@ -2097,6 +2148,8 @@ async function applyChanges() {
emit("saved", tableComment.value !== originalTableComment.value);
emit("close");
} else {
// Refresh persisted keys after successful renames/additions before metadata reloads.
persistLocalColumnOrder(false);
saving.value = false;
postSaveRefreshing.value = true;
skipNextRefreshVersion = true;
@@ -2788,7 +2841,7 @@ watch([activeTab, ddlLoading], ([tab, loading]) => {
type="button"
variant="ghost"
size="icon"
:class="[structureActionButtonClass, canDragColumn(index) ? 'cursor-grab active:cursor-grabbing' : 'cursor-not-allowed']"
:class="[structureActionButtonClass, canDragColumn(index) ? 'cursor-grab active:cursor-grabbing' : 'cursor-not-allowed', hasLocalColumnOrderChange ? 'border-primary/30 bg-primary/10 text-primary hover:bg-primary/15 hover:text-primary' : '']"
:disabled="!canDragColumn(index)"
:title="t('structureEditor.dragColumn')"
:aria-label="t('structureEditor.dragColumn')"