diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx index d164996..2c6dde9 100644 --- a/frontend/src/components/Sidebar.tsx +++ b/frontend/src/components/Sidebar.tsx @@ -6,6 +6,8 @@ import { shouldClearSidebarActiveContextOnEmptySelect, getV2RailConnectionGroupBadgeText, isV2SidebarObjectNode, + resolveV2ObjectGroupTitle, + resolveSidebarTableNameForCopy, type V2ExplorerFilter, } from './sidebar/sidebarHelpers'; // 重新导出,保持外部测试文件的 `from './Sidebar'` 兼容 @@ -16,6 +18,8 @@ export { shouldClearSidebarActiveContextOnEmptySelect, getV2RailConnectionGroupBadgeText, isV2SidebarObjectNode, + resolveV2ObjectGroupTitle, + resolveSidebarTableNameForCopy, } from './sidebar/sidebarHelpers'; import React, { useEffect, useState, useMemo, useRef, useCallback, useDeferredValue } from 'react'; import { createPortal } from 'react-dom'; @@ -193,17 +197,7 @@ interface TreeNode { type?: 'connection' | 'database' | 'table' | 'view' | 'materialized-view' | 'db-trigger' | 'db-event' | 'routine' | 'object-group' | 'v2-table-section' | 'queries-folder' | 'saved-query' | 'all-saved-queries' | 'saved-query-group' | 'unmatched-saved-queries' | 'external-sql-root' | 'external-sql-directory' | 'external-sql-folder' | 'external-sql-file' | 'folder-columns' | 'folder-indexes' | 'folder-fks' | 'folder-triggers' | 'redis-db' | 'tag' | 'jvm-mode' | 'jvm-resource' | 'jvm-diagnostic' | 'jvm-monitoring'; } -export const resolveV2ObjectGroupTitle = (node: Pick | null | undefined): string | null => { - if (node?.type !== 'object-group') return null; - const groupKey = String(node?.dataRef?.groupKey || ''); - if (groupKey === 'tables') return t('sidebar.v2_table_group_menu.title'); - if (groupKey === 'views') return t('sidebar.object_group.views'); - if (groupKey === 'routines') return t('sidebar.object_group.routines'); - if (groupKey === 'triggers') return t('sidebar.object_group.triggers'); - if (groupKey === 'events') return t('sidebar.object_group.events'); - if (groupKey === 'materializedViews') return t('sidebar.object_group.materialized_views'); - return null; -}; +// resolveV2ObjectGroupTitle 已迁移到 ./sidebar/sidebarHelpers export type SQLFileExecutionStatus = 'running' | 'done' | 'cancelled' | 'error'; @@ -308,9 +302,7 @@ export const shouldLoadSidebarNodeOnExpand = ( || node.type === 'jvm-resource'; }; -export const resolveSidebarTableNameForCopy = (node: Pick | null | undefined): string => { - return String(node?.dataRef?.tableName || node?.dataRef?.viewName || node?.dataRef?.eventName || node?.title || '').trim(); -}; +// resolveSidebarTableNameForCopy 已迁移到 ./sidebar/sidebarHelpers type SidebarTableSortPreference = 'name' | 'frequency'; diff --git a/frontend/src/components/sidebar/sidebarHelpers.ts b/frontend/src/components/sidebar/sidebarHelpers.ts index e326adb..966e782 100644 --- a/frontend/src/components/sidebar/sidebarHelpers.ts +++ b/frontend/src/components/sidebar/sidebarHelpers.ts @@ -98,3 +98,45 @@ export const isV2SidebarObjectNode = ( || node?.type === 'db-event' || node?.type === 'routine'; }; + +// === 第二期:依赖 i18n 但不依赖 TreeNode 内部类型的工具函数 === + +/** + * SidebarNodeLike 是 TreeNode 的结构化子集,用于工具函数签名。 + * 让 sidebarHelpers 不依赖 Sidebar.tsx 内部的 TreeNode 定义,避免循环依赖。 + */ +export interface SidebarNodeLike { + type?: string; + dataRef?: any; + title?: string; + children?: SidebarNodeLike[]; + isLeaf?: boolean; +} + +/** + * resolveV2ObjectGroupTitle 解析 V2 资源管理器中"对象分组"节点的本地化标题。 + * 仅对 type === 'object-group' 的节点有效,其他返回 null。 + */ +export const resolveV2ObjectGroupTitle = ( + node: Pick | null | undefined, +): string | null => { + if (node?.type !== 'object-group') return null; + const groupKey = String(node?.dataRef?.groupKey || ''); + if (groupKey === 'tables') return t('sidebar.v2_table_group_menu.title'); + if (groupKey === 'views') return t('sidebar.object_group.views'); + if (groupKey === 'routines') return t('sidebar.object_group.routines'); + if (groupKey === 'triggers') return t('sidebar.object_group.triggers'); + if (groupKey === 'events') return t('sidebar.object_group.events'); + if (groupKey === 'materializedViews') return t('sidebar.object_group.materialized_views'); + return null; +}; + +/** + * resolveSidebarTableNameForCopy 从节点提取用于复制的表名。 + * 优先级:dataRef.tableName > dataRef.viewName > dataRef.eventName > title。 + */ +export const resolveSidebarTableNameForCopy = ( + node: Pick | null | undefined, +): string => { + return String(node?.dataRef?.tableName || node?.dataRef?.viewName || node?.dataRef?.eventName || node?.title || '').trim(); +};