fix(sidebar): 保持表列表滚动显示

This commit is contained in:
t8y2 committed 2026-05-11 17:15:36 +08:00
1 parent cab0e79dad
commit e4c1d6639b
6 files changed
+166 -74

No files matched your search

+11 -2
View File
@@ -5,7 +5,14 @@ import { Search, X, ListFilter, Check, FolderPlus } from "lucide-vue-next";
import { useConnectionStore } from "@/stores/connectionStore";
import type { TreeNode } from "@/types/database";
import { matchSidebarLabel } from "@/lib/sidebarSearch";
import { flattenTree, shouldVirtualizeFlatTree, type FlatTreeNode } from "@/composables/useFlatTree";
import {
SIDEBAR_TREE_ROW_HEIGHT,
SIDEBAR_TREE_PRERENDER_COUNT,
SIDEBAR_TREE_SCROLL_BUFFER,
flattenTree,
shouldVirtualizeFlatTree,
type FlatTreeNode,
} from "@/composables/useFlatTree";
import TreeItem from "./TreeItem.vue";
import DatabaseIcon from "@/components/icons/DatabaseIcon.vue";
import { RecycleScroller } from "vue-virtual-scroller";
@@ -242,7 +249,9 @@ function onSearchToggle(node: TreeNode) {
v-if="flatNodes.length > 0 && useVirtualTree"
class="min-h-0 flex-1 overflow-y-auto"
:items="flatNodes"
:item-size="28"
:item-size="SIDEBAR_TREE_ROW_HEIGHT"
:buffer="SIDEBAR_TREE_SCROLL_BUFFER"
:prerender="SIDEBAR_TREE_PRERENDER_COUNT"
key-field="id"
type-field="type"
flow-mode
+4
View File
@@ -1,5 +1,9 @@
import type { TreeNode, TreeNodeType } from "@/types/database";
export const SIDEBAR_TREE_ROW_HEIGHT = 28;
export const SIDEBAR_TREE_SCROLL_BUFFER = SIDEBAR_TREE_ROW_HEIGHT * 48;
export const SIDEBAR_TREE_PRERENDER_COUNT = 48;
export interface FlatTreeNode {
node: TreeNode;
depth: number;
+41
View File
@@ -0,0 +1,41 @@
import type { TableInfo, TreeNode } from "@/types/database";
export function buildTableTreeNodes({
nodeId,
connectionId,
database,
schema,
tables,
}: {
nodeId: string;
connectionId: string;
database: string;
schema?: string;
tables: TableInfo[];
}): TreeNode[] {
return tables.map((table) => ({
id: `${nodeId}:${table.name}`,
label: table.name,
type: table.table_type === "VIEW" ? "view" : "table",
connectionId,
database,
schema,
isExpanded: false,
children: [],
}));
}
export function expandCachedObjectBrowserNodes(nodes: TreeNode[]): TreeNode[] {
return nodes.flatMap((node) => {
if (node.type === "object-browser") return node.hiddenChildren ?? [];
if (!node.children) return [node];
return [
{
...node,
children: expandCachedObjectBrowserNodes(node.children),
},
];
});
}
+10 -71
View File
@@ -23,10 +23,10 @@ import { isTauriRuntime } from "@/lib/tauriRuntime";
import { isSchemaAware, TREE_SCHEMA_TYPES } from "@/lib/databaseCapabilities";
import { buildDatabaseTreeNodes } from "@/lib/databaseTree";
import { buildSqlServerDatabaseTreeNodes, SQLSERVER_DEFAULT_SCHEMA } from "@/lib/sqlServerTree";
import { buildTableTreeNodes, expandCachedObjectBrowserNodes } from "@/lib/tableTree";
import { useSavedSqlStore } from "@/stores/savedSqlStore";
const PINNED_TREE_NODES_STORAGE_KEY = "dbx-pinned-tree-nodes";
const SIDEBAR_TABLE_NODE_LIMIT = 15;
type ImportSource = "dbx" | "navicat" | "dbeaver";
interface LoadTreeOptions {
@@ -280,9 +280,12 @@ export const useConnectionStore = defineStore("connection", () => {
async function loadPersistedTreeChildren(node: TreeNode, cacheKey: string): Promise<boolean> {
const children = await api.loadSchemaCache<TreeNode[]>(cacheKey).catch(() => null);
if (!children) return false;
const normalizedChildren = expandCachedObjectBrowserNodes(children);
setChildren(
node,
node.type === "connection" && node.connectionId ? withSavedSqlRoot(node.connectionId, children, node) : children,
node.type === "connection" && node.connectionId
? withSavedSqlRoot(node.connectionId, normalizedChildren, node)
: normalizedChildren,
);
node.isExpanded = true;
return true;
@@ -292,24 +295,6 @@ export const useConnectionStore = defineStore("connection", () => {
await api.saveSchemaCache(cacheKey, children).catch(() => undefined);
}
function buildObjectBrowserNode(
nodeId: string,
connectionId: string,
database: string,
schema: string | undefined,
objectCount: number,
): TreeNode {
return {
id: `${nodeId}:__object_browser`,
label: "tree.objectBrowser",
type: "object-browser",
connectionId,
database,
schema,
objectCount,
};
}
function useCachedChildren(node: TreeNode, options?: LoadTreeOptions): boolean {
if (options?.force || !loadedTreeNodeChildrenIds.value.has(node.id)) return false;
node.isExpanded = true;
@@ -683,34 +668,7 @@ export const useConnectionStore = defineStore("connection", () => {
api.listSchemas(connectionId, database),
api.listTables(connectionId, database, SQLSERVER_DEFAULT_SCHEMA),
]);
let children = buildSqlServerDatabaseTreeNodes(connectionId, database, schemas, defaultSchemaTables);
if (defaultSchemaTables.length > SIDEBAR_TABLE_NODE_LIMIT) {
const previewTables = buildSqlServerDatabaseTreeNodes(
connectionId,
database,
[],
defaultSchemaTables.slice(0, SIDEBAR_TABLE_NODE_LIMIT),
);
const browserNode = buildObjectBrowserNode(
`${nodeId}:${SQLSERVER_DEFAULT_SCHEMA}`,
connectionId,
database,
SQLSERVER_DEFAULT_SCHEMA,
defaultSchemaTables.length,
);
const hiddenTables = defaultSchemaTables.slice(SIDEBAR_TABLE_NODE_LIMIT);
browserNode.hiddenChildren = hiddenTables.map((t) => ({
id: `${nodeId}:${SQLSERVER_DEFAULT_SCHEMA}:${t.name}`,
label: t.name,
type: (t.table_type === "VIEW" ? "view" : "table") as "view" | "table",
connectionId,
database,
schema: SQLSERVER_DEFAULT_SCHEMA,
isExpanded: false,
children: [],
}));
children = [...previewTables, browserNode, ...children.filter((child) => child.type === "schema")];
}
const children = buildSqlServerDatabaseTreeNodes(connectionId, database, schemas, defaultSchemaTables);
setChildren(node, children);
await savePersistedTreeChildren(cacheKey, children);
node.isExpanded = true;
@@ -734,32 +692,13 @@ export const useConnectionStore = defineStore("connection", () => {
const tables = await api.listTables(connectionId, database, querySchema);
const config = getConfig(connectionId);
const effectiveSchema = schema || (config?.db_type && isSchemaAware(config.db_type) ? database : undefined);
const visibleTables = tables.slice(0, SIDEBAR_TABLE_NODE_LIMIT);
const children: TreeNode[] = visibleTables.map((t) => ({
id: `${nodeId}:${t.name}`,
label: t.name,
type: (t.table_type === "VIEW" ? "view" : "table") as "view" | "table",
const children = buildTableTreeNodes({
nodeId,
connectionId,
database,
schema: effectiveSchema,
isExpanded: false,
children: [],
}));
if (tables.length > SIDEBAR_TABLE_NODE_LIMIT) {
const hiddenTables = tables.slice(SIDEBAR_TABLE_NODE_LIMIT);
const browserNode = buildObjectBrowserNode(nodeId, connectionId, database, effectiveSchema, tables.length);
browserNode.hiddenChildren = hiddenTables.map((t) => ({
id: `${nodeId}:${t.name}`,
label: t.name,
type: (t.table_type === "VIEW" ? "view" : "table") as "view" | "table",
connectionId,
database,
schema: effectiveSchema,
isExpanded: false,
children: [],
}));
children.push(browserNode);
}
tables,
});
setChildren(node, children);
await savePersistedTreeChildren(cacheKey, children);
node.isExpanded = true;