diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx index 2c6dde9..5679f67 100644 --- a/frontend/src/components/Sidebar.tsx +++ b/frontend/src/components/Sidebar.tsx @@ -4,11 +4,15 @@ import { formatSidebarRowCount, hasSidebarLazyChildren, shouldClearSidebarActiveContextOnEmptySelect, + shouldLoadSidebarNodeOnExpand, getV2RailConnectionGroupBadgeText, isV2SidebarObjectNode, resolveV2ObjectGroupTitle, resolveSidebarTableNameForCopy, + parseV2CommandSearchQuery, type V2ExplorerFilter, + type V2CommandSearchMode, + type V2CommandSearchQuery, } from './sidebar/sidebarHelpers'; // 重新导出,保持外部测试文件的 `from './Sidebar'` 兼容 export { @@ -16,10 +20,12 @@ export { formatSidebarRowCount, hasSidebarLazyChildren, shouldClearSidebarActiveContextOnEmptySelect, + shouldLoadSidebarNodeOnExpand, getV2RailConnectionGroupBadgeText, isV2SidebarObjectNode, resolveV2ObjectGroupTitle, resolveSidebarTableNameForCopy, + parseV2CommandSearchQuery, } from './sidebar/sidebarHelpers'; import React, { useEffect, useState, useMemo, useRef, useCallback, useDeferredValue } from 'react'; import { createPortal } from 'react-dom'; @@ -290,17 +296,7 @@ export const SQLFileExecutionProgressContent: React.FC ); -export const shouldLoadSidebarNodeOnExpand = ( - node: Pick | null | undefined, -): boolean => { - if (!node || node.isLeaf === true || hasSidebarLazyChildren(node.children)) return false; - return node.type === 'connection' - || node.type === 'database' - || node.type === 'external-sql-root' - || node.type === 'table' - || node.type === 'jvm-mode' - || node.type === 'jvm-resource'; -}; +// shouldLoadSidebarNodeOnExpand 已迁移到 ./sidebar/sidebarHelpers // resolveSidebarTableNameForCopy 已迁移到 ./sidebar/sidebarHelpers @@ -597,51 +593,7 @@ export type V2CommandSearchItem = dbName?: string; }; -export type V2CommandSearchMode = 'default' | 'object' | 'ai'; - -export interface V2CommandSearchQuery { - mode: V2CommandSearchMode; - rawValue: string; - keyword: string; - normalizedKeyword: string; - aiPrompt: string; -} - -export const parseV2CommandSearchQuery = (value: unknown): V2CommandSearchQuery => { - const rawValue = String(value ?? ''); - const trimmedValue = rawValue.trim(); - const firstChar = trimmedValue.charAt(0); - - if (firstChar === '@' || firstChar === '@') { - const keyword = trimmedValue.slice(1).trim(); - return { - mode: 'object', - rawValue, - keyword, - normalizedKeyword: keyword.toLowerCase(), - aiPrompt: '', - }; - } - - if (firstChar === '?' || firstChar === '?') { - const aiPrompt = trimmedValue.slice(1).trim(); - return { - mode: 'ai', - rawValue, - keyword: aiPrompt, - normalizedKeyword: aiPrompt.toLowerCase(), - aiPrompt, - }; - } - - return { - mode: 'default', - rawValue, - keyword: trimmedValue, - normalizedKeyword: trimmedValue.toLowerCase(), - aiPrompt: '', - }; -}; +// V2CommandSearchMode / V2CommandSearchQuery / parseV2CommandSearchQuery 已迁移到 ./sidebar/sidebarHelpers export const resolveSidebarConnectionIdFromKey = ( key: unknown, diff --git a/frontend/src/components/sidebar/sidebarHelpers.ts b/frontend/src/components/sidebar/sidebarHelpers.ts index 966e782..a5ba099 100644 --- a/frontend/src/components/sidebar/sidebarHelpers.ts +++ b/frontend/src/components/sidebar/sidebarHelpers.ts @@ -106,6 +106,7 @@ export const isV2SidebarObjectNode = ( * 让 sidebarHelpers 不依赖 Sidebar.tsx 内部的 TreeNode 定义,避免循环依赖。 */ export interface SidebarNodeLike { + key?: string; type?: string; dataRef?: any; title?: string; @@ -140,3 +141,75 @@ export const resolveSidebarTableNameForCopy = ( ): string => { return String(node?.dataRef?.tableName || node?.dataRef?.viewName || node?.dataRef?.eventName || node?.title || '').trim(); }; + +// === 命令搜索相关类型与解析(V2 Command Search)=== + +/** 命令搜索模式:default(默认)/ object(@前缀,对象搜索)/ ai(?或?前缀,AI 提问) */ +export type V2CommandSearchMode = 'default' | 'object' | 'ai'; + +/** 命令搜索查询解析结果 */ +export interface V2CommandSearchQuery { + mode: V2CommandSearchMode; + rawValue: string; + keyword: string; + normalizedKeyword: string; + aiPrompt: string; +} + +/** + * parseV2CommandSearchQuery 解析命令搜索框的输入。 + * - "@" 或 "@" 前缀:对象搜索模式 + * - "?" 或 "?" 前缀:AI 提问模式 + * - 无前缀:默认模式 + */ +export const parseV2CommandSearchQuery = (value: unknown): V2CommandSearchQuery => { + const rawValue = String(value ?? ''); + const trimmedValue = rawValue.trim(); + const firstChar = trimmedValue.charAt(0); + + if (firstChar === '@' || firstChar === '@') { + const keyword = trimmedValue.slice(1).trim(); + return { + mode: 'object', + rawValue, + keyword, + normalizedKeyword: keyword.toLowerCase(), + aiPrompt: '', + }; + } + + if (firstChar === '?' || firstChar === '?') { + const aiPrompt = trimmedValue.slice(1).trim(); + return { + mode: 'ai', + rawValue, + keyword: aiPrompt, + normalizedKeyword: aiPrompt.toLowerCase(), + aiPrompt, + }; + } + + return { + mode: 'default', + rawValue, + keyword: trimmedValue, + normalizedKeyword: trimmedValue.toLowerCase(), + aiPrompt: '', + }; +}; + +/** + * shouldLoadSidebarNodeOnExpand 判断节点展开时是否需要懒加载子节点。 + * 仅 connection/database/external-sql-root/table/jvm-mode/jvm-resource 类型且无已加载 children 时返回 true。 + */ +export const shouldLoadSidebarNodeOnExpand = ( + node: Pick | null | undefined, +): boolean => { + if (!node || node.isLeaf === true || hasSidebarLazyChildren(node.children)) return false; + return node.type === 'connection' + || node.type === 'database' + || node.type === 'external-sql-root' + || node.type === 'table' + || node.type === 'jvm-mode' + || node.type === 'jvm-resource'; +};