fix(sidebar): preserve expanded tree on refresh

This commit is contained in:
t8y2 2026-05-09 16:21:45 +08:00
parent 9b4267ef7b
commit b41e548911
3 changed files with 88 additions and 14 deletions

View File

@ -5,6 +5,7 @@ import { Button } from "@/components/ui/button";
import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip";
import ConnectionTree from "@/components/sidebar/ConnectionTree.vue";
import { useConnectionStore } from "@/stores/connectionStore";
import { useToast } from "@/composables/useToast";
defineProps<{
sidebarWidth: number;
@ -18,6 +19,15 @@ const emit = defineEmits<{
const { t } = useI18n();
const connectionStore = useConnectionStore();
const { toast } = useToast();
async function refreshTree() {
try {
await connectionStore.refreshAllTree();
} catch (e: any) {
toast(t("connection.connectFailed", { message: e?.message || String(e) }), 5000);
}
}
</script>
<template>
@ -28,7 +38,7 @@ const connectionStore = useConnectionStore();
<span class="flex-1" />
<Tooltip>
<TooltipTrigger as-child>
<Button variant="ghost" size="icon" class="h-5 w-5" @click="connectionStore.refreshAllTree()">
<Button variant="ghost" size="icon" class="h-5 w-5" @click="refreshTree">
<RefreshCw class="h-3 w-3" />
</Button>
</TooltipTrigger>

View File

@ -335,10 +335,11 @@ async function clearNodeDefaultDatabase() {
}
async function refresh() {
const node = props.node;
node.isExpanded = false;
node.children = [];
await toggle();
try {
await connectionStore.refreshTreeNode(props.node);
} catch (e: any) {
toast(t("connection.connectFailed", { message: e?.message || String(e) }), 5000);
}
}
const showDeleteConfirm = ref(false);

View File

@ -20,7 +20,7 @@ import {
import type { SqlCompletionColumn, SqlCompletionTable } from "@/lib/sqlCompletion";
import * as api from "@/lib/api";
import { isTauriRuntime } from "@/lib/tauriRuntime";
import { isSchemaAware } from "@/lib/databaseCapabilities";
import { isSchemaAware, TREE_SCHEMA_TYPES } from "@/lib/databaseCapabilities";
import { buildDatabaseTreeNodes } from "@/lib/databaseTree";
const PINNED_TREE_NODES_STORAGE_KEY = "dbx-pinned-tree-nodes";
@ -677,6 +677,67 @@ export const useConnectionStore = defineStore("connection", () => {
}
}
function collectExpandedNodeIds(nodes: TreeNode[], ids = new Set<string>()): Set<string> {
for (const node of nodes) {
if (node.isExpanded) ids.add(node.id);
if (node.children) collectExpandedNodeIds(node.children, ids);
}
return ids;
}
async function loadTreeNodeChildren(node: TreeNode) {
if (node.type === "connection" && node.connectionId) {
const config = getConfig(node.connectionId);
if (config?.db_type === "redis") {
await loadRedisDatabases(node.connectionId);
} else if (config?.db_type === "mongodb" || config?.db_type === "elasticsearch") {
await loadMongoDatabases(node.connectionId);
} else {
await loadDatabases(node.connectionId);
}
} else if (node.type === "mongo-db" && node.connectionId && node.database) {
await loadMongoCollections(node.connectionId, node.database);
} else if (node.type === "database" && node.connectionId && node.database) {
const config = getConfig(node.connectionId);
if (config?.db_type && TREE_SCHEMA_TYPES.has(config.db_type)) {
await loadSchemas(node.connectionId, node.database);
} else {
await loadTables(node.connectionId, node.database);
}
} else if (node.type === "schema" && node.connectionId && node.database && node.schema) {
await loadTables(node.connectionId, node.database, node.schema);
} else if ((node.type === "table" || node.type === "view") && node.connectionId && node.database) {
await loadTableGroups(node.connectionId, node.database, node.label, node.schema);
} else if (node.type === "group-columns" && node.connectionId && node.database && node.tableName) {
await loadColumns(node.connectionId, node.database, node.tableName, node.schema);
} else if (node.type === "group-indexes" && node.connectionId && node.database && node.tableName) {
await loadIndexes(node.connectionId, node.database, node.tableName, node.schema);
} else if (node.type === "group-fkeys" && node.connectionId && node.database && node.tableName) {
await loadForeignKeys(node.connectionId, node.database, node.tableName, node.schema);
} else if (node.type === "group-triggers" && node.connectionId && node.database && node.tableName) {
await loadTriggers(node.connectionId, node.database, node.tableName, node.schema);
}
}
async function restoreExpandedChildren(node: TreeNode, expandedIds: Set<string>) {
if (!node.children) return;
for (const child of node.children) {
if (!expandedIds.has(child.id)) continue;
await loadTreeNodeChildren(child);
await restoreExpandedChildren(child, expandedIds);
}
}
async function refreshTreeNode(node: TreeNode) {
const expandedIds = collectExpandedNodeIds([node]);
expandedIds.add(node.id);
if (node.type !== "connection-group") {
node.children = [];
}
await loadTreeNodeChildren(node);
await restoreExpandedChildren(node, expandedIds);
}
function isSchemaAwareDatabase(connectionId: string): boolean {
return isSchemaAware(getConfig(connectionId)?.db_type);
}
@ -792,20 +853,21 @@ export const useConnectionStore = defineStore("connection", () => {
persistSidebarLayoutDebounced();
}
function refreshAllTree() {
const resetChildren = (nodes: TreeNode[]) => {
async function refreshAllTree() {
const expandedIds = collectExpandedNodeIds(treeNodes.value);
const refreshExpandedNodes = async (nodes: TreeNode[]) => {
for (const node of nodes) {
if (node.type === "connection-group") {
if (node.children) resetChildren(node.children);
if (node.children) await refreshExpandedNodes(node.children);
continue;
}
if (node.isExpanded) {
node.isExpanded = false;
node.children = [];
}
if (!expandedIds.has(node.id)) continue;
node.children = [];
await loadTreeNodeChildren(node);
await restoreExpandedChildren(node, expandedIds);
}
};
resetChildren(treeNodes.value);
await refreshExpandedNodes(treeNodes.value);
}
async function exportConnectionsToFile(passphrase: string) {
@ -955,6 +1017,7 @@ export const useConnectionStore = defineStore("connection", () => {
activeConnectionId,
treeNodes,
refreshAllTree,
refreshTreeNode,
connectedIds,
connectionErrors,
setConnectionError,