Fix:保留表结构编辑未保存草稿
This commit is contained in:
5 files changed
+143
-4
No files matched your search
@@ -1262,6 +1262,8 @@ defineExpose({ focusSearch, refreshData, handleModRTarget, requestQueryEditorExe
|
||||
:database="activeTab.database"
|
||||
:schema="activeTab.schema"
|
||||
:table-name="activeTab.structureTableName || ''"
|
||||
:draft="activeTab.structureDraft"
|
||||
@update:draft="(draft) => (activeTab.structureDraft = draft)"
|
||||
@saved="(commentChanged) => emit('structureEditorSaved', commentChanged)"
|
||||
@close="emit('structureEditorClose')"
|
||||
@open-settings="(initialTab, initialSection) => emit('openSettings', initialTab, initialSection)"
|
||||
|
||||
@@ -27,6 +27,7 @@ import { PRESET_FIELDS_TEMPLATE_ID, createTableColumnTemplateDrafts } from "@/li
|
||||
import { getTableMetadataCapabilities } from "@/lib/tableMetadataCapabilities";
|
||||
import { canAddTableStructureColumn, getTableStructureCapabilities } from "@/lib/tableStructureCapabilities";
|
||||
import { connectionObjectTreeQuerySchema, tableStructureDatabaseTypeForConnection } from "@/lib/jdbcDialect";
|
||||
import type { TableStructureEditorDraft } from "@/types/database";
|
||||
import {
|
||||
buildStructureTargetLabel,
|
||||
combineDataTypeForDatabase,
|
||||
@@ -79,9 +80,11 @@ const props = defineProps<{
|
||||
database: string;
|
||||
schema?: string;
|
||||
tableName: string;
|
||||
draft?: TableStructureEditorDraft;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits<{
|
||||
"update:draft": [draft: TableStructureEditorDraft | undefined];
|
||||
saved: [commentChanged: boolean];
|
||||
close: [];
|
||||
openSettings: [initialTab?: string, initialSection?: string];
|
||||
@@ -597,6 +600,54 @@ let sqlPreviewDebounceTimer: ReturnType<typeof setTimeout> | undefined;
|
||||
let deferredSqlPreviewRefresh = false;
|
||||
let keydownListenerRegistered = false;
|
||||
let skipNextRefreshVersion = false;
|
||||
let restoringDraft = false;
|
||||
let syncingDraft = false;
|
||||
let draftHydrated = false;
|
||||
|
||||
function cloneDraftValue<T>(value: T): T {
|
||||
return JSON.parse(JSON.stringify(value)) as T;
|
||||
}
|
||||
|
||||
function createCurrentDraft(initialized = true): TableStructureEditorDraft {
|
||||
return {
|
||||
activeTab: activeTab.value as TableStructureEditorDraft["activeTab"],
|
||||
newTableName: newTableName.value,
|
||||
tableComment: tableComment.value,
|
||||
originalTableComment: originalTableComment.value,
|
||||
columns: cloneDraftValue(columns.value),
|
||||
indexes: cloneDraftValue(indexes.value),
|
||||
foreignKeys: cloneDraftValue(foreignKeys.value),
|
||||
triggers: cloneDraftValue(triggers.value),
|
||||
initialized,
|
||||
};
|
||||
}
|
||||
|
||||
function syncDraftToParent() {
|
||||
if (!draftHydrated) return;
|
||||
if (restoringDraft || syncingDraft) return;
|
||||
syncingDraft = true;
|
||||
emit("update:draft", createCurrentDraft());
|
||||
syncingDraft = false;
|
||||
}
|
||||
|
||||
function restoreDraft(draft: TableStructureEditorDraft) {
|
||||
restoringDraft = true;
|
||||
activeTab.value = draft.activeTab || "columns";
|
||||
newTableName.value = draft.newTableName || "";
|
||||
tableComment.value = draft.tableComment || "";
|
||||
originalTableComment.value = draft.originalTableComment || "";
|
||||
columns.value = cloneDraftValue(draft.columns || []);
|
||||
indexes.value = cloneDraftValue(draft.indexes || []);
|
||||
foreignKeys.value = cloneDraftValue(draft.foreignKeys || []);
|
||||
triggers.value = cloneDraftValue(draft.triggers || []);
|
||||
restoringDraft = false;
|
||||
draftHydrated = true;
|
||||
}
|
||||
|
||||
function markDraftHydratedAndSync() {
|
||||
draftHydrated = true;
|
||||
syncDraftToParent();
|
||||
}
|
||||
|
||||
function hasPendingStructureChanges(): boolean {
|
||||
if (isCreateMode.value) {
|
||||
@@ -729,6 +780,11 @@ const canApply = computed(
|
||||
() => !loading.value && !saving.value && !postSaveRefreshing.value && !secondaryMetadataLoading.value && !sqlPreviewLoading.value && pendingStatements.value.length > 0 && warnings.value.length === 0 && !!props.connectionId && (isCreateMode.value ? !!newTableName.value.trim() : !!props.tableName),
|
||||
);
|
||||
|
||||
function clearDraft() {
|
||||
draftHydrated = false;
|
||||
emit("update:draft", undefined);
|
||||
}
|
||||
|
||||
function resetState() {
|
||||
activeTab.value = "columns";
|
||||
loading.value = false;
|
||||
@@ -752,6 +808,12 @@ function resetState() {
|
||||
originalTableComment.value = "";
|
||||
}
|
||||
|
||||
async function reloadStructureFromDatabase() {
|
||||
if (isCreateMode.value) return;
|
||||
draftHydrated = false;
|
||||
await loadStructure(false, FULL_STRUCTURE_REFRESH_SCOPE, true, { blockSecondaryMetadata: true });
|
||||
}
|
||||
|
||||
function setSecondaryMetadataLoading(scope: StructureRefreshScope, value: boolean) {
|
||||
if (scope.indexes && tableMetadataCapabilities.value.indexes) indexesLoading.value = value;
|
||||
if (scope.foreignKeys && tableMetadataCapabilities.value.foreignKeys) foreignKeysLoading.value = value;
|
||||
@@ -772,7 +834,7 @@ async function fetchTableCommentValue(connectionId: string, database: string, sc
|
||||
}
|
||||
}
|
||||
|
||||
async function loadStructure(silent = false, scope: StructureRefreshScope = FULL_STRUCTURE_REFRESH_SCOPE, showErrors = true, options: { blockSecondaryMetadata?: boolean } = {}) {
|
||||
async function loadStructure(silent = false, scope: StructureRefreshScope = FULL_STRUCTURE_REFRESH_SCOPE, showErrors = true, options: { blockSecondaryMetadata?: boolean; preserveDraft?: boolean } = {}) {
|
||||
const connectionId = props.connectionId;
|
||||
const database = props.database;
|
||||
const schema = metadataSchema.value;
|
||||
@@ -783,6 +845,7 @@ async function loadStructure(silent = false, scope: StructureRefreshScope = FULL
|
||||
setSecondaryMetadataLoading(scope, true);
|
||||
errorMessage.value = "";
|
||||
let secondaryMetadataScheduled = false;
|
||||
let loadedSuccessfully = false;
|
||||
try {
|
||||
await store.ensureConnected(connectionId);
|
||||
|
||||
@@ -831,6 +894,7 @@ async function loadStructure(silent = false, scope: StructureRefreshScope = FULL
|
||||
if (options.blockSecondaryMetadata) {
|
||||
await secondaryMetadataPromise;
|
||||
}
|
||||
loadedSuccessfully = true;
|
||||
} catch (e: any) {
|
||||
if (showErrors) {
|
||||
errorMessage.value = e?.message || String(e);
|
||||
@@ -842,6 +906,9 @@ async function loadStructure(silent = false, scope: StructureRefreshScope = FULL
|
||||
setSecondaryMetadataLoading(scope, false);
|
||||
}
|
||||
if (!silent) loading.value = false;
|
||||
if (!options.preserveDraft && loadedSuccessfully && requestId === structureLoadRequestId) {
|
||||
markDraftHydratedAndSync();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1211,6 +1278,7 @@ async function applyChanges() {
|
||||
ddlFetched.value = false;
|
||||
ddlContent.value = "";
|
||||
if (isCreateMode.value) {
|
||||
clearDraft();
|
||||
emit("saved", tableComment.value !== originalTableComment.value);
|
||||
emit("close");
|
||||
} else {
|
||||
@@ -1280,13 +1348,21 @@ onMounted(() => {
|
||||
resetState();
|
||||
registerStructureEditorShortcuts();
|
||||
void loadDynamicDataTypeOptions();
|
||||
void loadStructure();
|
||||
if (props.draft?.initialized) {
|
||||
restoreDraft(props.draft);
|
||||
} else if (isCreateMode.value) {
|
||||
markDraftHydratedAndSync();
|
||||
} else {
|
||||
void loadStructure(false, FULL_STRUCTURE_REFRESH_SCOPE, true, { blockSecondaryMetadata: true });
|
||||
}
|
||||
});
|
||||
|
||||
onActivated(() => {
|
||||
registerStructureEditorShortcuts();
|
||||
void loadDynamicDataTypeOptions();
|
||||
if (!isCreateMode.value) void loadStructure(true);
|
||||
if (props.draft?.initialized && !draftHydrated) {
|
||||
restoreDraft(props.draft);
|
||||
}
|
||||
});
|
||||
onDeactivated(unregisterStructureEditorShortcuts);
|
||||
onBeforeUnmount(() => {
|
||||
@@ -1322,10 +1398,15 @@ watch(
|
||||
[isCreateMode, databaseType, () => props.schema, () => props.tableName, newTableName, tableComment, columns, indexes, foreignKeys, triggers],
|
||||
() => {
|
||||
scheduleSqlPreviewRefresh();
|
||||
syncDraftToParent();
|
||||
},
|
||||
{ deep: true, immediate: true },
|
||||
);
|
||||
|
||||
watch(activeTab, () => {
|
||||
syncDraftToParent();
|
||||
});
|
||||
|
||||
watch(secondaryMetadataLoading, (value) => {
|
||||
if (value || !deferredSqlPreviewRefresh) return;
|
||||
scheduleSqlPreviewRefresh();
|
||||
@@ -1359,7 +1440,7 @@ watch(activeTab, (tab) => {
|
||||
<Database :class="[structureIconClass, 'text-muted-foreground']" />
|
||||
<span class="min-w-0 flex-1 truncate font-medium">{{ targetLabel || t("editor.noDatabase") }}</span>
|
||||
<Badge variant="outline">{{ connection?.driver_label || databaseType }}</Badge>
|
||||
<Button v-if="!isCreateMode" variant="ghost" size="sm" :class="structureToolbarButtonClass" :disabled="loading || saving" @click="loadStructure()">
|
||||
<Button v-if="!isCreateMode" variant="ghost" size="sm" :class="structureToolbarButtonClass" :disabled="loading || saving" @click="reloadStructureFromDatabase">
|
||||
<RefreshCw :class="structureIconClass" />
|
||||
{{ t("structureEditor.refresh") }}
|
||||
</Button>
|
||||
|
||||
Reference in new issue
Block a user