From 9ffa4c41d95b97e75f8a4fa538c47e2b190d2391 Mon Sep 17 00:00:00 2001 From: t8y2 <1156263951@qq.com> Date: Wed, 10 Jun 2026 02:46:10 +0800 Subject: [PATCH] feat: add SQL library panel with folder/file management, rename, and sync support --- apps/desktop/src/App.vue | 53 +++- .../src/components/editor/QueryHistory.vue | 27 +-- .../src/components/layout/AppToolbar.vue | 18 ++ .../src/components/sidebar/TreeItem.vue | 224 +---------------- .../desktop/src/composables/usePanelResize.ts | 4 + apps/desktop/src/i18n/locales/en.ts | 26 ++ apps/desktop/src/i18n/locales/es.ts | 26 ++ apps/desktop/src/i18n/locales/it.ts | 26 ++ apps/desktop/src/i18n/locales/pt-BR.ts | 26 ++ apps/desktop/src/i18n/locales/zh-CN.ts | 27 +++ apps/desktop/src/i18n/locales/zh-TW.ts | 26 ++ apps/desktop/src/lib/api.ts | 3 + apps/desktop/src/lib/http.ts | 15 +- .../desktop/src/lib/sidebarActiveTabTarget.ts | 12 - apps/desktop/src/lib/sidebarNodeOrdering.ts | 7 +- apps/desktop/src/lib/sidebarTreeItemLayout.ts | 12 +- apps/desktop/src/lib/tauri.ts | 24 ++ apps/desktop/src/lib/treeNodeClick.ts | 1 - apps/desktop/src/stores/connectionStore.ts | 80 +----- apps/desktop/src/stores/queryStore.ts | 2 +- apps/desktop/src/stores/savedSqlStore.ts | 228 +++++++++++++++++- apps/desktop/src/stores/settingsStore.ts | 3 + apps/desktop/src/types/database.ts | 2 + crates/dbx-core/src/saved_sql.rs | 4 + src-tauri/src/commands/saved_sql.rs | 183 ++++++++++++++ src-tauri/src/lib.rs | 4 + 26 files changed, 711 insertions(+), 352 deletions(-) diff --git a/apps/desktop/src/App.vue b/apps/desktop/src/App.vue index c8e4db963..b56c3aae6 100644 --- a/apps/desktop/src/App.vue +++ b/apps/desktop/src/App.vue @@ -70,6 +70,7 @@ import type { AiAction } from "@/lib/ai"; const AiAssistant = defineAsyncComponent(() => import("@/components/editor/AiAssistant.vue")); 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 UpdateDialog = defineAsyncComponent(() => import("@/components/layout/UpdateDialog.vue")); const LoginPage = defineAsyncComponent(() => import("@/components/auth/LoginPage.vue")); @@ -116,10 +117,19 @@ const showDriverStore = ref(false); const agentDriverUpdateCount = ref(0); const showHistory = ref(false); const showAiPanel = ref(safeLocalStorageGet("dbx-ai-panel-open") === "true"); +const showSqlLibraryPanel = ref(safeLocalStorageGet("dbx-sql-library-open") === "true"); const sidebarOpen = ref(safeLocalStorageGet("dbx-sidebar-open") !== "false"); const aiPanelReady = ref(false); -const { sidebarWidth, aiPanelWidth, historyWidth, startSidebarResize, startAiPanelResize, startHistoryResize } = - usePanelResize(); +const { + sidebarWidth, + aiPanelWidth, + historyWidth, + sqlLibraryWidth, + startSidebarResize, + startAiPanelResize, + startHistoryResize, + startSqlLibraryResize, +} = usePanelResize(); const aiAssistantRef = ref(null); const appSidebarRef = ref | null>(null); const contentAreaRef = ref | null>(null); @@ -245,8 +255,7 @@ const connectionStats = computed(() => ({ })); const recentConnections = computed(() => connectionStore.connections.slice(0, 5)); const saveSqlFolders = computed(() => { - const tab = activeTab.value; - return tab ? savedSqlStore.listFolders(tab.connectionId) : []; + return savedSqlStore.allFolders; }); async function applyUiScale(scale: number) { @@ -325,6 +334,11 @@ function toggleAiPanel() { safeLocalStorageSet("dbx-ai-panel-open", String(showAiPanel.value)); } +function toggleSqlLibrary() { + showSqlLibraryPanel.value = !showSqlLibraryPanel.value; + safeLocalStorageSet("dbx-sql-library-open", String(showSqlLibraryPanel.value)); +} + function fixWithAi(errorMessage: string) { if (!showAiPanel.value) { showAiPanel.value = true; @@ -371,8 +385,9 @@ function formatActiveSql() { } function defaultSavedSqlName(title: string) { - const trimmed = title.trim() || "Query"; - return trimmed.endsWith(".sql") ? trimmed : `${trimmed}.sql`; + const trimmed = title.trim() || "query"; + const normalized = trimmed.replace(/\s+/g, "_"); + return normalized.endsWith(".sql") ? normalized : `${normalized}.sql`; } async function openSaveSqlDialog() { @@ -394,7 +409,6 @@ async function openSaveSqlDialog() { sql: tab.sql, }); queryStore.linkSavedSql(tab.id, updated.id, updated.name); - connectionStore.refreshSavedSqlTree(tab.connectionId); toast(t("savedSql.saved"), 2000); return; } @@ -445,7 +459,6 @@ async function confirmSaveSqlToLibrary() { sql: tab.sql, }); queryStore.linkSavedSql(tab.id, saved.id, saved.name); - connectionStore.refreshSavedSqlTree(tab.connectionId); showSaveSqlDialog.value = false; toast(t("savedSql.saved"), 2000); } catch (e: any) { @@ -868,9 +881,10 @@ function onLoginSuccess() { function initApp() { const t0 = performance.now(); console.log("[STARTUP] initApp begin"); - settingsStore.initDesktopSettings().catch(() => {}); - savedSqlStore - .initFromStorage() + settingsStore + .initDesktopSettings() + .catch(() => {}) + .then(() => savedSqlStore.initFromStorage()) .then(() => { console.log(`[STARTUP] savedSqlStore.initFromStorage: ${(performance.now() - t0).toFixed(0)}ms`); return connectionStore.initFromDisk(); @@ -1010,6 +1024,7 @@ onUnmounted(() => { :theme-mode="themeMode" :show-ai-panel="showAiPanel" :show-history="showHistory" + :show-sql-library="showSqlLibraryPanel" :show-driver-store="showDriverStore" :checking-updates="checkingUpdates" :has-update-available="toolbarHasUpdateAvailable" @@ -1021,6 +1036,7 @@ onUnmounted(() => { @set-theme-mode="setThemeMode" @toggle-ai="toggleAiPanel" @toggle-history="showHistory = !showHistory" + @toggle-sql-library="toggleSqlLibrary" @open-github="openGitHub" @open-settings="showSettingsDialog = true" @open-driver-store="showDriverStore = !showDriverStore" @@ -1235,6 +1251,21 @@ onUnmounted(() => { @close="showHistory = false" /> + +
+
+
+ +
+
store.load());