feat(sidebar): 添加一键折叠所有节点功能

- 在侧边栏中添加了“折叠所有”按钮,用户可以一键折叠所有展开的节点。
- 更新了相关的国际化文本,支持多语言。
- 增加了 collapseAllGroups 和 collapseExpandedTreeNodes 函数以支持新功能。
This commit is contained in:
二丫讲梵 authored and GitHub committed 2026-07-02 18:50:01 +08:00
1 parent e7dffeca37
commit af9ad39ffb
19 files changed
+191 -10

No files matched your search

@@ -2,7 +2,7 @@
import { computed, ref } from "vue";
import { useI18n } from "vue-i18n";
import { translateBackendError } from "@/i18n/backend-errors";
import { Upload, Download, FolderPlus, RefreshCw, ChevronsLeft } from "@lucide/vue";
import { Upload, Download, FolderPlus, RefreshCw, ChevronsLeft, ChevronsUp } from "@lucide/vue";
import { Button } from "@/components/ui/button";
import { Tooltip, TooltipTrigger, TooltipContent } from "@/components/ui/tooltip";
import LightDropdown from "@/components/ui/LightDropdown.vue";
@@ -45,6 +45,10 @@ function createNewGroup() {
void connectionTreeRef.value?.createNewGroup();
}
function collapseAllTreeNodes() {
connectionTreeRef.value?.collapseAllTreeNodes();
}
function focusSearch(): boolean {
return connectionTreeRef.value?.focusSearch() ?? false;
}
@@ -88,6 +92,14 @@ defineExpose({ focusSearch });
</TooltipTrigger>
<TooltipContent>{{ t("sidebar.export") }}</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger as-child>
<Button variant="ghost" size="icon" class="h-5 w-5" @click="collapseAllTreeNodes">
<ChevronsUp class="h-3 w-3" />
</Button>
</TooltipTrigger>
<TooltipContent>{{ t("sidebar.collapseAll") }}</TooltipContent>
</Tooltip>
<Tooltip>
<TooltipTrigger as-child>
<Button variant="ghost" size="icon" class="h-5 w-5" @click="createNewGroup">
@@ -702,6 +702,13 @@ function onSearchToggle(node: TreeNode) {
searchCollapsedIds.value = next;
}
function collapseAllTreeNodes() {
store.collapseAllTreeNodes();
if (isSearching.value) {
searchCollapsedIds.value = new Set(flatNodes.value.filter((item) => item.node.children?.length).map((item) => item.id));
}
}
function currentTreeScroller(): HTMLElement | null {
return ((useVirtualTree.value ? treeScrollerRef.value?.$el : plainTreeScrollerRef.value) as HTMLElement | undefined) ?? null;
}
@@ -859,7 +866,7 @@ onUnmounted(() => {
window.clearTimeout(sidebarScrollingTimer);
});
defineExpose({ focusSearch, createNewGroup });
defineExpose({ focusSearch, createNewGroup, collapseAllTreeNodes });
</script>
<template>
@@ -920,7 +927,7 @@ defineExpose({ focusSearch, createNewGroup });
:prerender="SIDEBAR_TREE_PRERENDER_COUNT"
:skip-hover="true"
key-field="id"
type-field="type"
type-field="poolType"
flow-mode
>
<template #default="{ item }">
@@ -76,7 +76,7 @@ import { clearActiveTableReferencePayload, createTableReferencePayload, createTa
import { editableRowIdentifierColumns, usesSyntheticRowIdKey } from "@/lib/tableEditing";
import { tableOpenPageLimit } from "@/lib/tableOpenPageLimit";
import { supportsDatabaseCreation, supportsDatabaseSearch, supportsFieldLineage, supportsObjectBrowserTreeNode, supportsSchemaDiagram, supportsSqlFileExecution, supportsTableImport, supportsTableTruncate, supportsTableStructureEditing, usesTreeSchemaMode } from "@/lib/databaseCapabilities";
import { copyNameForTreeNode, objectSourceKindForTreeNode, sidebarSelectionCopyAction, treeNodeRowAction, treeNodeRowDoubleClickAction } from "@/lib/treeNodeClick";
import { copyNameForTreeNode, objectSourceKindForTreeNode, shouldRunTreeNodeRowAction, sidebarSelectionCopyAction, treeNodeRowAction, treeNodeRowDoubleClickAction } from "@/lib/treeNodeClick";
import { formatSqlInsert } from "@/lib/exportFormats";
import { joinExportedDdls } from "@/lib/ddlExport";
import { fetchTableDataForExport } from "@/lib/tableDataExport";
@@ -636,17 +636,20 @@ async function toggle() {
}
}
function runRowClickAction() {
function runRowClickAction(clickDetail: number) {
const node = props.node;
if (node.type === "load-more") {
if (clickDetail > 1) return;
void loadMoreObjectGroupChildren();
return;
}
if (node.type === "object-browser") {
if (clickDetail > 1) return;
void openObjectBrowser();
return;
}
const action = treeNodeRowAction(node.type, canExpand.value, settingsStore.editorSettings.sidebarActivation);
if (!shouldRunTreeNodeRowAction(action, clickDetail)) return;
if (action === "open-data") {
openData();
} else if (node.type === "mongo-collection") {
@@ -680,6 +683,18 @@ async function loadAllObjectGroupChildren() {
}
}
function onToggleClick() {
selectSingleTreeNode(props.node);
rowRef.value?.focus({ preventScroll: true });
void toggle();
}
function onToggleMouseDown(event: MouseEvent) {
if (event.button !== 0) return;
selectSingleTreeNode(props.node);
rowRef.value?.focus({ preventScroll: true });
}
function visibleTreeNodes(): TreeNode[] {
if (sidebarTreeContext) return sidebarTreeContext.getVisibleNodes();
return flattenTree(connectionStore.treeNodes).map((item) => item.node);
@@ -749,8 +764,7 @@ function onClick(event: MouseEvent) {
selectSingleTreeNode(props.node);
rowRef.value?.focus({ preventScroll: true });
if (settingsStore.editorSettings.sidebarActivation === "double") return;
if (event.detail > 1) return;
runRowClickAction();
runRowClickAction(event.detail);
}
function onTreeItemContextMenu(event: MouseEvent, openContextMenu: (event: MouseEvent) => void) {
@@ -4487,7 +4501,7 @@ function treeItemMenuItems(): ContextMenuItem[] {
<div v-if="showDropBefore" class="absolute right-2 top-0 h-0.5 bg-primary rounded-full pointer-events-none" :style="{ left: paddingLeft }" />
<div v-if="showDropAfter" class="absolute right-2 bottom-0 h-0.5 bg-primary rounded-full pointer-events-none" :style="{ left: paddingLeft }" />
<template v-if="canExpand">
<button type="button" class="-m-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded-sm text-muted-foreground hover:bg-muted hover:text-foreground" @click.stop="toggle">
<button type="button" class="-m-0.5 flex h-4 w-4 shrink-0 items-center justify-center rounded-sm text-muted-foreground hover:bg-muted hover:text-foreground" @mousedown.stop="onToggleMouseDown" @click.stop="onToggleClick">
<Loader2 v-if="node.isLoading" class="w-3.5 h-3.5 animate-spin" />
<ChevronDown v-else-if="node.isExpanded" class="w-3.5 h-3.5" />
<ChevronRight v-else class="w-3.5 h-3.5" />