From 8094f4901da24b454e00e8885a6038de6444482d Mon Sep 17 00:00:00 2001 From: Abeautifulsnow Date: Mon, 29 Jun 2026 12:05:46 +0800 Subject: [PATCH] fix(browser): ensure connection before mount loads --- .../components/document/DocumentBrowser.vue | 11 +++++++++- .../src/components/kv/KvKeyBrowser.vue | 20 ++++++++++++++++-- .../src/components/mq/MqAdminConsole.vue | 9 +++++++- .../components/nacos/NacosAdminConsole.vue | 12 +++++++++++ .../src/components/objects/ObjectBrowser.vue | 7 ++++++- .../src/components/redis/RedisDashboard.vue | 5 +++++ .../src/components/redis/RedisKeyBrowser.vue | 21 +++++++++++++++++-- .../components/redis/RedisSlowlogPanel.vue | 5 +++-- .../src/components/sidebar/TreeItem.vue | 8 +++++++ 9 files changed, 89 insertions(+), 9 deletions(-) diff --git a/apps/desktop/src/components/document/DocumentBrowser.vue b/apps/desktop/src/components/document/DocumentBrowser.vue index 09010d42bb..ebb0451f39 100644 --- a/apps/desktop/src/components/document/DocumentBrowser.vue +++ b/apps/desktop/src/components/document/DocumentBrowser.vue @@ -13,6 +13,7 @@ import ErrorBanner from "@/components/ui/ErrorBanner.vue"; import DataGrid from "@/components/grid/DataGrid.vue"; import QueryLoadingState from "@/components/common/QueryLoadingState.vue"; import * as api from "@/lib/api"; +import { useConnectionStore } from "@/stores/connectionStore"; import { clampSearchSplitWidth } from "@/lib/dataGridSearchSplit"; import { documentViewerFontStyle } from "@/lib/documentViewerFontStyle"; import { buildDocumentFilterCondition, combineDocumentFilterConditions, currentDocumentFilterJson, defaultDocumentFilterRule, documentFilterModeNeedsValue, documentFilterModeOptions, documentStoreProviderFor, type DocumentFilterMode, type DocumentFilterRule } from "@/lib/documentStoreProvider"; @@ -28,6 +29,7 @@ import "splitpanes/dist/splitpanes.css"; const { t } = useI18n(); const settingsStore = useSettingsStore(); +const connectionStore = useConnectionStore(); const props = defineProps<{ connectionId: string; @@ -714,7 +716,14 @@ function highlightedJson(json: string): string { }); } -onMounted(load); +onMounted(async () => { + try { + await connectionStore.ensureConnected(props.connectionId); + } catch (e) { + console.warn("[DBX] ensureConnected failed for", props.connectionId, e); + } + load(); +}); onBeforeUnmount(() => { if (documentLoadExecutionId.value) void api.cancelQuery(documentLoadExecutionId.value); stopDocumentLoadingTimer(); diff --git a/apps/desktop/src/components/kv/KvKeyBrowser.vue b/apps/desktop/src/components/kv/KvKeyBrowser.vue index ee9de8c7b4..706656d1e7 100644 --- a/apps/desktop/src/components/kv/KvKeyBrowser.vue +++ b/apps/desktop/src/components/kv/KvKeyBrowser.vue @@ -1,6 +1,7 @@ diff --git a/apps/desktop/src/components/mq/MqAdminConsole.vue b/apps/desktop/src/components/mq/MqAdminConsole.vue index 6f8b2e8866..5f28c07f23 100644 --- a/apps/desktop/src/components/mq/MqAdminConsole.vue +++ b/apps/desktop/src/components/mq/MqAdminConsole.vue @@ -3,6 +3,7 @@ import { formatError } from "@/lib/errorUtils"; import { ref, computed, onMounted, watch } from "vue"; import type { MqClusterInfo, TopicInfo } from "@/types/mq"; import { mqTestConnection } from "@/lib/api"; +import { useConnectionStore } from "@/stores/connectionStore"; import { mqClusterOptionsFromExtra } from "@/lib/mqTenantForm"; import TenantsPanel from "./TenantsPanel.vue"; import NamespacesPanel from "./NamespacesPanel.vue"; @@ -21,6 +22,7 @@ interface Props { } const props = defineProps(); +const connectionStore = useConnectionStore(); type MqTab = "tenants" | "namespaces" | "topics" | "subscriptions" | "monitoring" | "clients" | "policies" | "permissions" | "raw"; @@ -168,7 +170,12 @@ watch( ); // Lifecycle -onMounted(() => { +onMounted(async () => { + try { + await connectionStore.ensureConnected(props.connectionId); + } catch (e) { + console.warn("[DBX] ensureConnected failed for", props.connectionId, e); + } loadClusterInfo(); }); diff --git a/apps/desktop/src/components/nacos/NacosAdminConsole.vue b/apps/desktop/src/components/nacos/NacosAdminConsole.vue index c56e5c2e9a..930929d530 100644 --- a/apps/desktop/src/components/nacos/NacosAdminConsole.vue +++ b/apps/desktop/src/components/nacos/NacosAdminConsole.vue @@ -13,6 +13,7 @@ import EditorSearchPanel from "@/components/editor/EditorSearchPanel.vue"; import NacosConfigDiffDialog from "@/components/nacos/NacosConfigDiffDialog.vue"; import NacosConfigHistoryDialog from "@/components/nacos/NacosConfigHistoryDialog.vue"; import { useToast } from "@/composables/useToast"; +import { useConnectionStore } from "@/stores/connectionStore"; import { useI18n } from "vue-i18n"; import * as api from "@/lib/api"; import { buildNacosConfigDeleteConfirm, buildNacosConfigExportFileName, buildNacosConfigHistoryRollbackConfirm, buildNacosInstanceConfirm, createNacosSaveAsCopy, resolveNacosConfigCopyText } from "@/lib/nacosAdmin"; @@ -39,6 +40,7 @@ type AdminTab = "configs" | "services"; const { toast } = useToast(); const { t } = useI18n(); const settingsStore = useSettingsStore(); +const connectionStore = useConnectionStore(); const { isDark } = useTheme(); const activeTab = ref("configs"); const connectionInfo = ref(null); @@ -839,12 +841,22 @@ watch( originalConfigContent.value = ""; destroyConfigEditor(); selectedService.value = null; + try { + await connectionStore.ensureConnected(props.connectionId); + } catch (e) { + console.warn("[DBX] ensureConnected failed for", props.connectionId, e); + } await loadInfo(); await Promise.all([loadConfigsWithRetry(1), loadServicesWithRetry(1)]); }, ); onMounted(async () => { + try { + await connectionStore.ensureConnected(props.connectionId); + } catch (e) { + console.warn("[DBX] ensureConnected failed for", props.connectionId, e); + } await loadInfo(); await Promise.all([loadConfigsWithRetry(1), loadServicesWithRetry(1)]); }); diff --git a/apps/desktop/src/components/objects/ObjectBrowser.vue b/apps/desktop/src/components/objects/ObjectBrowser.vue index da9fdf532c..a254f30b60 100644 --- a/apps/desktop/src/components/objects/ObjectBrowser.vue +++ b/apps/desktop/src/components/objects/ObjectBrowser.vue @@ -1236,11 +1236,16 @@ onBeforeUnmount(() => { watch( () => [props.connection.id, props.database, props.schema] as const, - () => { + async () => { selectedSchema.value = props.schema; userHasSelectedFilter.value = false; objectFilter.value = "all"; clearTableSelection(); + try { + await connectionStore.ensureConnected(props.connection.id); + } catch (e) { + console.warn("[DBX] ensureConnected failed for", props.connection.id, e); + } void reload(); }, { immediate: true }, diff --git a/apps/desktop/src/components/redis/RedisDashboard.vue b/apps/desktop/src/components/redis/RedisDashboard.vue index 42b20020d4..e51b47b111 100644 --- a/apps/desktop/src/components/redis/RedisDashboard.vue +++ b/apps/desktop/src/components/redis/RedisDashboard.vue @@ -255,6 +255,11 @@ watch(autoRefreshInterval, () => { // Lifecycle // --------------------------------------------------------------------------- onMounted(async () => { + try { + await connectionStore.ensureConnected(props.connectionId); + } catch (e) { + console.warn("[DBX] ensureConnected failed for", props.connectionId, e); + } await fetchClusterNodes(); await fetchInfo(); }); diff --git a/apps/desktop/src/components/redis/RedisKeyBrowser.vue b/apps/desktop/src/components/redis/RedisKeyBrowser.vue index ce80f1c7b6..6091931d75 100644 --- a/apps/desktop/src/components/redis/RedisKeyBrowser.vue +++ b/apps/desktop/src/components/redis/RedisKeyBrowser.vue @@ -921,12 +921,29 @@ function onCommandInputKeydown(event: KeyboardEvent) { } } -onMounted(() => { +onMounted(async () => { resumeRedisBrowserBackgroundWork(); + try { + await connectionStore.ensureConnected(props.connectionId); + } catch (e) { + console.warn("[DBX] ensureConnected failed for", props.connectionId, e); + } void loadKeys(); }); -onActivated(resumeRedisBrowserBackgroundWork); +onActivated(async () => { + resumeRedisBrowserBackgroundWork(); + // Ensure the connection is still alive after reactivation (e.g. tab switch). + // If keys failed to load previously (empty list), retry loading. + try { + await connectionStore.ensureConnected(props.connectionId); + } catch (e) { + console.warn("[DBX] ensureConnected failed for", props.connectionId, e); + } + if (flatKeys.value.length === 0 && !loading.value) { + void loadKeys(); + } +}); onDeactivated(pauseRedisBrowserBackgroundWork); diff --git a/apps/desktop/src/components/redis/RedisSlowlogPanel.vue b/apps/desktop/src/components/redis/RedisSlowlogPanel.vue index a8700237df..c3efbdb069 100644 --- a/apps/desktop/src/components/redis/RedisSlowlogPanel.vue +++ b/apps/desktop/src/components/redis/RedisSlowlogPanel.vue @@ -59,9 +59,10 @@ const showClientColumns = computed(() => { onMounted(async () => { if (connectionMode.value === "cluster") { try { + await connectionStore.ensureConnected(props.connectionId); nodes.value = await api.redisClusterMasterNodes(props.connectionId); - } catch { - // Silently fail — nodes list is best-effort + } catch (e) { + console.warn("[DBX] ensureConnected failed for", props.connectionId, e); } } }); diff --git a/apps/desktop/src/components/sidebar/TreeItem.vue b/apps/desktop/src/components/sidebar/TreeItem.vue index bd26335cf5..85c2bb3457 100644 --- a/apps/desktop/src/components/sidebar/TreeItem.vue +++ b/apps/desktop/src/components/sidebar/TreeItem.vue @@ -494,30 +494,38 @@ async function toggle() { await connectionStore.loadDatabases(node.connectionId); } } else if (node.type === "redis-db" && node.connectionId && node.database) { + await connectionStore.ensureConnected(node.connectionId); const tabTitle = `${connectionStore.getConfig(node.connectionId)?.name || "Redis"}:db${node.database}`; queryStore.createTab(node.connectionId, node.database, tabTitle, "redis"); } else if (node.type === "mq-tenant" && node.connectionId) { + await connectionStore.ensureConnected(node.connectionId); queryStore.openMqAdmin(node.connectionId, { tenant: node.mqTenant || node.label }); } else if (node.type === "nacos-namespace" && node.connectionId) { + await connectionStore.ensureConnected(node.connectionId); queryStore.openNacosAdmin(node.connectionId, { namespace: node.nacosNamespace || "", namespaceName: node.nacosNamespaceName || node.label }); } else if (node.type === "etcd-root" && node.connectionId) { + await connectionStore.ensureConnected(node.connectionId); const tabTitle = `${connectionStore.getConfig(node.connectionId)?.name || "etcd"}:keys`; queryStore.createTab(node.connectionId, "", tabTitle, "etcd"); refreshActiveKvBrowserAfterOpen("etcd", node.connectionId); } else if (node.type === "zookeeper-root" && node.connectionId) { + await connectionStore.ensureConnected(node.connectionId); const tabTitle = `${connectionStore.getConfig(node.connectionId)?.name || "ZooKeeper"}:keys`; queryStore.createTab(node.connectionId, "", tabTitle, "zookeeper"); refreshActiveKvBrowserAfterOpen("zookeeper", node.connectionId); } else if (node.type === "user-admin" && node.connectionId) { + await connectionStore.ensureConnected(node.connectionId); queryStore.openUserAdmin(node.connectionId); } else if (node.type === "mongo-db" && node.connectionId && node.database) { await connectionStore.loadMongoCollections(node.connectionId, node.database); } else if (node.type === "mongo-collection" && node.connectionId && node.database) { await connectionStore.loadTableGroups(node.connectionId, node.database, node.label, node.schema, node.id); } else if (node.type === "elasticsearch-index" && node.connectionId) { + await connectionStore.ensureConnected(node.connectionId); const tab = queryStore.createTab(node.connectionId, node.database || "default", node.label, "mongo"); queryStore.updateSql(tab, node.label); } else if (node.type === "vector-collection" && node.connectionId) { + await connectionStore.ensureConnected(node.connectionId); const collectionRef = node.id.includes("__vector_collection:") ? node.id.split("__vector_collection:").pop() || node.label : node.label; const tab = queryStore.createTab(node.connectionId, node.database || "default", node.label, "vector"); queryStore.updateSql(tab, collectionRef);