From 00b57153077445f2075939857f792fc042c334bf Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Wed, 1 Jul 2026 14:05:24 +0800 Subject: [PATCH] feat(settings): move settings into main tab --- apps/desktop/src/App.vue | 94 +++++++++++++------ .../editor/EditorSettingsDialog.vue | 91 +++++++++++------- .../src/components/layout/AppDialogs.vue | 7 -- .../src/components/layout/AppTabBar.vue | 68 ++++++++++++-- .../src/components/layout/AppToolbar.vue | 3 +- 5 files changed, 187 insertions(+), 76 deletions(-) diff --git a/apps/desktop/src/App.vue b/apps/desktop/src/App.vue index 13c16ed9d3..b4099a1529 100644 --- a/apps/desktop/src/App.vue +++ b/apps/desktop/src/App.vue @@ -88,6 +88,7 @@ const AiAssistant = defineAsyncComponent(() => import("@/components/editor/AiAss const QueryHistory = defineAsyncComponent(() => import("@/components/editor/QueryHistory.vue")); const SqlLibraryPanel = defineAsyncComponent(() => import("@/components/layout/SqlLibraryPanel.vue")); const DriverStorePage = defineAsyncComponent(() => import("@/components/config/DriverStoreDialog.vue")); +const EditorSettingsPage = defineAsyncComponent(() => import("@/components/editor/EditorSettingsDialog.vue")); const UpdateDialog = defineAsyncComponent(() => import("@/components/layout/UpdateDialog.vue")); const CloseActionPromptDialog = defineAsyncComponent(() => import("@/components/layout/CloseActionPromptDialog.vue")); const LoginPage = defineAsyncComponent(() => import("@/components/auth/LoginPage.vue")); @@ -117,13 +118,16 @@ const setupRequired = ref(false); const showConnectionDialog = ref(false); const connectionDialogPrefill = ref(null); const connectionDialogInitialTab = ref(undefined); -const showSettingsDialog = ref(false); +const settingsPageTabOpen = ref(false); +const settingsPageActive = ref(false); const settingsInitialTab = ref("editor"); const settingsInitialSection = ref(undefined); const showQueryEditorDdlDialog = ref(false); const driverStoreTabOpen = ref(false); const driverStoreActive = ref(false); +const settingsReturnSurface = ref<"query" | "driverStore" | "welcome">("welcome"); const showDriverStore = computed(() => driverStoreTabOpen.value && driverStoreActive.value); +const showSettingsPage = computed(() => settingsPageTabOpen.value && settingsPageActive.value); const showQuickOpen = ref(false); const agentDriverUpdateCount = ref(0); const showHistory = ref(false); @@ -259,7 +263,37 @@ const updateNotificationsEnabled = computed(() => settingsStore.editorSettings.u function openSettings(initialTab = "editor", initialSection?: string) { settingsInitialTab.value = initialTab; settingsInitialSection.value = initialSection; - showSettingsDialog.value = true; + if (!settingsPageActive.value) { + settingsReturnSurface.value = showDriverStore.value ? "driverStore" : activeTab.value ? "query" : "welcome"; + } + activateSettingsPage(); +} + +function activateSettingsPage() { + settingsPageTabOpen.value = true; + settingsPageActive.value = true; + driverStoreActive.value = false; +} + +function closeSettingsPage() { + settingsPageTabOpen.value = false; + settingsPageActive.value = false; + if (settingsReturnSurface.value === "driverStore" && driverStoreTabOpen.value) { + driverStoreActive.value = true; + return; + } + driverStoreActive.value = false; +} + +function openDriverStorePage() { + driverStoreTabOpen.value = true; + driverStoreActive.value = true; + settingsPageActive.value = false; +} + +function closeDriverStorePage() { + driverStoreTabOpen.value = false; + driverStoreActive.value = false; } const toolbarAgentDriverUpdateCount = computed(() => (updateNotificationsEnabled.value ? agentDriverUpdateCount.value : 0)); const toolbarHasUpdateAvailable = computed(() => updateNotificationsEnabled.value && hasUpdateAvailable.value); @@ -369,6 +403,7 @@ watch( } if (id) newQueryContextSource.value = "tab"; if (id && driverStoreActive.value) driverStoreActive.value = false; + if (id && settingsPageActive.value) settingsPageActive.value = false; selectedSql.value = ""; activeOutputView.value = "result"; if (id) queryStore.reloadEvictedTab(id); @@ -1186,6 +1221,7 @@ function activateQueryTab(tabId: string): boolean { dispatchBeforeTabSwitch(tabId); queryStore.activeTabId = tabId; driverStoreActive.value = false; + settingsPageActive.value = false; return true; } @@ -1269,9 +1305,10 @@ function handleKeydown(e: KeyboardEvent) { } if (isCloseTabShortcut(e, shortcuts)) { e.preventDefault(); - if (showDriverStore.value) { - driverStoreTabOpen.value = false; - driverStoreActive.value = false; + if (showSettingsPage.value) { + closeSettingsPage(); + } else if (showDriverStore.value) { + closeDriverStorePage(); } else if (queryStore.activeTabId) { queryStore.closeTab(queryStore.activeTabId); } @@ -1401,8 +1438,7 @@ function handleContextMenu(e: MouseEvent) { } function openDriverStoreFromEvent() { - driverStoreTabOpen.value = true; - driverStoreActive.value = true; + openDriverStorePage(); } function runUpdateNotificationChecks() { @@ -1516,6 +1552,7 @@ onUnmounted(() => { :show-history="showHistory" :show-sql-library="showSqlLibraryPanel" :show-driver-store="showDriverStore" + :show-settings-page="showSettingsPage" :checking-updates="checkingUpdates" :has-update-available="toolbarHasUpdateAvailable" :agent-driver-update-count="toolbarAgentDriverUpdateCount" @@ -1529,10 +1566,7 @@ onUnmounted(() => { @toggle-sql-library="toggleSqlLibrary" @open-github="openGitHub" @open-settings="openSettings()" - @open-driver-store=" - driverStoreTabOpen = true; - driverStoreActive = true; - " + @open-driver-store="openDriverStorePage" @check-updates="checkUpdates()" @open-transfer="dialogs.showTransferDialog.value = true" @open-sql-file="dialogs.showSqlFileDialog.value = true" @@ -1553,20 +1587,32 @@ onUnmounted(() => { -
+ +
{
{ :show-connection-dialog="showConnectionDialog" :connection-prefill="connectionDialogPrefill" :connection-initial-tab="connectionDialogInitialTab" - :show-settings-dialog="showSettingsDialog" - :settings-initial-tab="settingsInitialTab" - :settings-initial-section="settingsInitialSection" - :app-version="appVersion" :show-danger-dialog="showDangerDialog" :danger-sql="dangerSql" :suppress-danger-confirm="suppressDangerConfirm" @@ -1708,7 +1750,6 @@ onUnmounted(() => { :sql-parameter-source-sql="sqlParameterSourceSql" :sql-parameter-names="sqlParameterNames" @update:show-connection-dialog="setConnectionDialogOpen" - @update:show-settings-dialog="showSettingsDialog = $event" @update:show-danger-dialog="showDangerDialog = $event" @update:suppress-danger-confirm="suppressDangerConfirm = $event" @update:show-sql-parameter-dialog="showSqlParameterDialog = $event" @@ -1727,8 +1768,7 @@ onUnmounted(() => { " @open-driver-store=" setConnectionDialogOpen(false); - driverStoreTabOpen = true; - driverStoreActive = true; + openDriverStorePage(); " @open-lineage-target="openLineageTarget" @open-database-search-target="openDatabaseSearchTarget" diff --git a/apps/desktop/src/components/editor/EditorSettingsDialog.vue b/apps/desktop/src/components/editor/EditorSettingsDialog.vue index f3ebb35e71..eada838189 100644 --- a/apps/desktop/src/components/editor/EditorSettingsDialog.vue +++ b/apps/desktop/src/components/editor/EditorSettingsDialog.vue @@ -94,7 +94,8 @@ let cachedSystemFonts: string[] | null = null; let pendingSystemFonts: Promise | null = null; const props = defineProps<{ - open: boolean; + open?: boolean; + variant?: "dialog" | "page"; initialTab?: string; initialSection?: string; appVersion?: string; @@ -104,6 +105,23 @@ const emit = defineEmits<{ "update:open": [value: boolean]; }>(); +const isSettingsPage = computed(() => props.variant === "page"); +const settingsVisible = computed(() => isSettingsPage.value || props.open === true); +const settingsRootComponent = computed(() => (isSettingsPage.value ? "div" : Dialog)); +const settingsRootProps = computed(() => (isSettingsPage.value ? {} : { open: props.open === true })); +const settingsRootClass = computed(() => (isSettingsPage.value ? "h-full min-h-0 overflow-hidden bg-background" : "")); +const settingsContentComponent = computed(() => (isSettingsPage.value ? "div" : DialogContent)); +const settingsContentClass = computed(() => (isSettingsPage.value ? "flex h-full min-h-0 flex-col gap-4 overflow-hidden bg-background p-4" : "h-[min(660px,calc(100dvh-80px))] !max-w-[min(920px,calc(100vw-32px))] grid-rows-[auto_minmax(0,1fr)] gap-3 p-4 sm:!max-w-[min(920px,calc(100vw-48px))]")); +const settingsTitleComponent = computed(() => (isSettingsPage.value ? "h2" : DialogTitle)); + +function onSettingsRootOpenChange(value: boolean) { + if (!isSettingsPage.value) emit("update:open", value); +} + +function closeSettings() { + emit("update:open", false); +} + interface TableColumnTemplateOverrideRow { id: string; databaseType: DatabaseType; @@ -459,7 +477,7 @@ async function loadSystemFontOptions() { // Sync from store when dialog opens watch( - () => props.open, + () => settingsVisible.value, (open) => { if (open) { editFontFamily.value = settingsStore.editorSettings.fontFamily; @@ -641,7 +659,7 @@ async function applySettings() { async function applySettingsAndClose() { await persistSettings(); - emit("update:open", false); + closeSettings(); } function resetDefaultsForTab(tab: SettingsCategory) { @@ -1345,7 +1363,7 @@ async function scrollToInitialSettingsSection() { } watch( - () => props.open, + () => settingsVisible.value, async (open) => { if (open) { activeSettingsTab.value = props.initialTab || "editor"; @@ -1374,7 +1392,16 @@ watch( watch( () => props.initialSection, () => { - if (props.open) void scrollToInitialSettingsSection(); + if (settingsVisible.value) void scrollToInitialSettingsSection(); + }, +); + +watch( + () => props.initialTab, + (tab) => { + if (!settingsVisible.value || !tab) return; + activeSettingsTab.value = tab; + void scrollToInitialSettingsSection(); }, ); @@ -1848,14 +1875,19 @@ watch( let previewInitialized = false; +function cleanupPreviewEditor() { + if (!previewView.value) return; + previewView.value.destroy(); + previewView.value = null; + previewInitialized = false; + fontThemeComp = null; + themeComp = null; + editorViewModule = null; +} + watch(activeSettingsTab, (tab) => { if (tab !== "editor" && previewView.value) { - previewView.value.destroy(); - previewView.value = null; - previewInitialized = false; - fontThemeComp = null; - themeComp = null; - editorViewModule = null; + cleanupPreviewEditor(); } }); @@ -1882,28 +1914,23 @@ watch(previewRef, async (el) => { }); watch( - () => props.open, + () => settingsVisible.value, (open) => { - if (!open && previewView.value) { - previewView.value.destroy(); - previewView.value = null; - previewInitialized = false; - fontThemeComp = null; - themeComp = null; - editorViewModule = null; - } + if (!open) cleanupPreviewEditor(); }, ); + +onUnmounted(cleanupPreviewEditor); diff --git a/apps/desktop/src/components/layout/AppDialogs.vue b/apps/desktop/src/components/layout/AppDialogs.vue index 3f7250d45d..15cc6f0352 100644 --- a/apps/desktop/src/components/layout/AppDialogs.vue +++ b/apps/desktop/src/components/layout/AppDialogs.vue @@ -4,7 +4,6 @@ import { useI18n } from "vue-i18n"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog"; const ConnectionDialog = defineAsyncComponent(() => import("@/components/connection/ConnectionDialog.vue")); -const EditorSettingsDialog = defineAsyncComponent(() => import("@/components/editor/EditorSettingsDialog.vue")); const DangerConfirmDialog = defineAsyncComponent(() => import("@/components/editor/DangerConfirmDialog.vue")); const SqlParameterDialog = defineAsyncComponent(() => import("@/components/editor/SqlParameterDialog.vue")); const DataTransferDialog = defineAsyncComponent(() => import("@/components/transfer/DataTransferDialog.vue")); @@ -27,10 +26,6 @@ const props = defineProps<{ showConnectionDialog: boolean; connectionPrefill?: ConnectionDeepLinkDraft | null; connectionInitialTab?: ConfigTab; - showSettingsDialog: boolean; - settingsInitialTab?: string; - settingsInitialSection?: string; - appVersion?: string; showDangerDialog: boolean; dangerSql: string; suppressDangerConfirm: boolean; @@ -41,7 +36,6 @@ const props = defineProps<{ const emit = defineEmits<{ "update:showConnectionDialog": [value: boolean]; - "update:showSettingsDialog": [value: boolean]; "update:showDangerDialog": [value: boolean]; "update:suppressDangerConfirm": [value: boolean]; "update:showSqlParameterDialog": [value: boolean]; @@ -117,7 +111,6 @@ watch( @connect-failed="emit('connectFailed', $event)" @open-driver-store="emit('openDriverStore')" /> - (); @@ -28,6 +30,8 @@ const emit = defineEmits<{ "activate-tab": []; "activate-driver-store": []; "close-driver-store": []; + "activate-settings-page": []; + "close-settings-page": []; "save-tab": [tabId: string]; }>(); @@ -218,9 +222,25 @@ watch( }, ); +watch( + () => props.settingsPageActive, + (show) => { + if (!show) return; + nextTick(() => { + const container = tabsContainerRef.value; + if (!container) return; + const el = container.querySelector("[data-settings-page-tab]"); + if (el) { + el.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center" }); + } + updateAllScrollButtons(); + }); + }, +); + function tabColorStyle(tab: QueryTab) { const color = connectionColor(tab.connectionId); - const isActive = tab.id === queryStore.activeTabId && !props.driverStoreActive; + const isActive = tab.id === queryStore.activeTabId && !props.driverStoreActive && !props.settingsPageActive; const isClassic = isClassicLayout.value; if (!color) { if (isClassic) { @@ -357,7 +377,7 @@ function activateTab(tabId: string) {