From 94e1c923f3a41442427a78e3c83bf4e5a7e013a8 Mon Sep 17 00:00:00 2001 From: shanhexi Date: Tue, 12 Dec 2023 23:54:06 +0800 Subject: [PATCH 1/2] fix:delete table --- .../NewTree/TreeNodeRightClick/index.less | 9 - .../NewTree/TreeNodeRightClick/index.tsx | 336 --------- .../blocks/NewTree/functions/deleteTable.less | 21 - .../blocks/NewTree/functions/deleteTable.tsx | 64 -- .../blocks/NewTree/functions/openAsyncSql.ts | 133 ---- .../src/blocks/NewTree/functions/pinTable.ts | 15 - .../src/blocks/NewTree/functions/refresh.ts | 8 - .../src/blocks/NewTree/functions/viewDDL.less | 6 - .../src/blocks/NewTree/functions/viewDDL.tsx | 47 -- .../NewTree/hooks/useGetRightClickMenu.ts | 652 ------------------ .../blocks/NewTree/hooks/useTreeNodeFocus.ts | 6 - chat2db-client/src/blocks/NewTree/index.less | 188 ----- chat2db-client/src/blocks/NewTree/index.tsx | 337 --------- .../src/blocks/NewTree/treeConfig.tsx | 634 ----------------- .../src/blocks/NewTree/treeStore.ts | 21 - .../src/blocks/Tree/functions/deleteTable.tsx | 11 +- .../src/blocks/Tree/functions/openAsyncSql.ts | 222 +++--- .../blocks/Tree/hooks/useGetRightClickMenu.ts | 23 +- chat2db-client/src/blocks/Tree/index.less | 15 +- chat2db-client/src/blocks/Tree/index.tsx | 244 ++++--- chat2db-client/src/blocks/Tree/treeConfig.tsx | 2 +- .../src/components/Modal/BaseModal/index.tsx | 4 +- .../workspace/components/TableList/index.tsx | 5 +- chat2db-client/src/store/setting/index.ts | 2 +- 24 files changed, 282 insertions(+), 2723 deletions(-) delete mode 100644 chat2db-client/src/blocks/NewTree/TreeNodeRightClick/index.less delete mode 100644 chat2db-client/src/blocks/NewTree/TreeNodeRightClick/index.tsx delete mode 100644 chat2db-client/src/blocks/NewTree/functions/deleteTable.less delete mode 100644 chat2db-client/src/blocks/NewTree/functions/deleteTable.tsx delete mode 100644 chat2db-client/src/blocks/NewTree/functions/openAsyncSql.ts delete mode 100644 chat2db-client/src/blocks/NewTree/functions/pinTable.ts delete mode 100644 chat2db-client/src/blocks/NewTree/functions/refresh.ts delete mode 100644 chat2db-client/src/blocks/NewTree/functions/viewDDL.less delete mode 100644 chat2db-client/src/blocks/NewTree/functions/viewDDL.tsx delete mode 100644 chat2db-client/src/blocks/NewTree/hooks/useGetRightClickMenu.ts delete mode 100644 chat2db-client/src/blocks/NewTree/hooks/useTreeNodeFocus.ts delete mode 100644 chat2db-client/src/blocks/NewTree/index.less delete mode 100644 chat2db-client/src/blocks/NewTree/index.tsx delete mode 100644 chat2db-client/src/blocks/NewTree/treeConfig.tsx delete mode 100644 chat2db-client/src/blocks/NewTree/treeStore.ts diff --git a/chat2db-client/src/blocks/NewTree/TreeNodeRightClick/index.less b/chat2db-client/src/blocks/NewTree/TreeNodeRightClick/index.less deleted file mode 100644 index aa09fc090..000000000 --- a/chat2db-client/src/blocks/NewTree/TreeNodeRightClick/index.less +++ /dev/null @@ -1,9 +0,0 @@ -@import '../../../styles/var.less'; - -.monacoEditorBox { - margin-top: -15px; - height: 60vh; - border: 1px solid var(--color-border); - border-radius: 4px; - overflow: hidden; -} diff --git a/chat2db-client/src/blocks/NewTree/TreeNodeRightClick/index.tsx b/chat2db-client/src/blocks/NewTree/TreeNodeRightClick/index.tsx deleted file mode 100644 index 0131b154d..000000000 --- a/chat2db-client/src/blocks/NewTree/TreeNodeRightClick/index.tsx +++ /dev/null @@ -1,336 +0,0 @@ -import React, { memo, useMemo, useRef, useState } from 'react'; -import { message, Modal, Input, Dropdown, notification } from 'antd'; -// import classnames from 'classnames'; -import i18n from '@/i18n'; -import styles from './index.less'; - -// ----- components ----- -import { dataSourceFormConfigs } from '@/components/ConnectionEdit/config/dataSource'; -import { IConnectionConfig } from '@/components/ConnectionEdit/config/types'; -import MonacoEditor, { IExportRefFunction } from '@/components/MonacoEditor'; -import MenuLabel from '@/components/MenuLabel'; - -// ----- constants ----- -import { TreeNodeType, OperationColumn } from '@/constants'; -import { ITreeNode } from '@/typings'; - -// ----- service ----- -import connectionServer from '@/service/connection'; -// import mysqlServer from '@/service/sql'; - -// ----- config ----- -import { ITreeConfigItem, treeConfig } from '../treeConfig'; - -export type IProps = { - className?: string; - // setIsLoading: (value: boolean) => void; - data: ITreeNode; - children?: React.ReactNode; -}; - -export interface IOperationColumnConfigItem { - text: string; - icon: string; - handle: () => void; -} - -function TreeNodeRightClick(props: IProps) { - const { data } = props; - const [verifyDialog, setVerifyDialog] = useState(); - const [verifyTableName, setVerifyTableName] = useState(''); - const [, modelDom] = Modal.useModal(); - const [, notificationDom] = notification.useNotification(); - // 拿出当前节点的配置 - const treeNodeConfig: ITreeConfigItem = treeConfig[data.treeNodeType]; - const { getChildren, operationColumn } = treeNodeConfig; - - const [monacoVerifyDialog, setMonacoVerifyDialog] = useState(false); - - const dataSourceFormConfig = dataSourceFormConfigs.find((t: IConnectionConfig) => { - return t.type === data.extraParams?.databaseType; - })!; - const monacoEditorRef = useRef(null); - - const operationColumnConfig: { [key in OperationColumn]: (data: ITreeNode) => IOperationColumnConfigItem } = { - // 刷新 - [OperationColumn.Refresh]: () => { - return { - text: i18n('common.button.refresh'), - icon: '\uec08', - handle: () => { - refresh(); - }, - }; - }, - [OperationColumn.EditTableData]: () => { - return { - text: i18n('workspace.menu.editTableData'), - icon: '\ue7b5', - handle: () => { - openEditTableData(); - }, - }; - }, - [OperationColumn.ViewDDL]: () => { - return { - text: i18n('workspace.menu.ViewDDL'), - icon: '\ue665', - handle: () => { - // mysqlServer - // .exportCreateTableSql({ - // ...curWorkspaceParams, - // tableName: data.key, - // } as any) - // .then((res) => { - // setMonacoVerifyDialog(true); - // setTimeout(() => { - // monacoEditorRef.current?.setValue(res, 'cover'); - // }, 0); - // }); - }, - }; - }, - [OperationColumn.ShiftOut]: (_data) => { - return { - text: '移出', - icon: '\ue62a', - handle: () => { - connectionServer.remove({ id: +_data.key }).then(() => { - treeConfig[TreeNodeType.DATA_SOURCES]?.getChildren!({} as any).then(() => { - // setTreeData(res); - }); - }); - }, - }; - }, - [OperationColumn.CreateTable]: () => { - return { - text: '新建表', - icon: '\ue6b6', - handle: () => { - // const operationData = { - // type: 'new', - // nodeData: _data, - // }; - // setOperationDataDialog(operationData) - }, - }; - }, - [OperationColumn.CreateConsole]: () => { - return { - text: '新建查询', - icon: '\ue619', - handle: () => {}, - }; - }, - [OperationColumn.DeleteTable]: () => { - return { - text: i18n('workspace.menu.deleteTable'), - icon: '\ue6a7', - handle: () => { - setVerifyDialog(true); - }, - }; - }, - [OperationColumn.EditTable]: () => { - return { - text: i18n('workspace.menu.editTable'), - icon: '\ue602', - handle: () => { - // dispatch({ - // type: 'workspace/setCreateTabIntro', - // payload: { - // type: CreateTabIntroType.EditorTable, - // workspaceTabType: WorkspaceTabType.EditTable, - // treeNodeData: { - // ...data, - // name: data.key, - // }, - // }, - // }); - }, - }; - }, - [OperationColumn.EditSource]: () => { - return { - text: '编辑数据源', - icon: '\ue623', - handle: () => { - // setEditDataSourceData({ - // dataType: data.dataType as any, - // id: +data.key - // }) - }, - }; - }, - [OperationColumn.Top]: (_data) => { - return { - text: _data.pinned ? i18n('workspace.menu.unPin') : i18n('workspace.menu.pin'), - icon: _data.pinned ? '\ue61d' : '\ue627', - handle: handleTop, - }; - }, - [OperationColumn.CopyName]: (_data) => { - return { - text: i18n('common.button.copy'), - icon: '\uec7a', - handle: () => { - navigator.clipboard.writeText(_data.key.toString()); - }, - }; - }, - }; - - function handleTop() { - // const api = data.pinned ? 'deleteTablePin' : 'addTablePin'; - // mysqlServer[api]({ - // ...curWorkspaceParams, - // tableName: data.key, - // } as any).then(() => { - // dispatch({ - // type: 'workspace/fetchGetCurTableList', - // payload: { - // ...curWorkspaceParams, - // extraParams: curWorkspaceParams, - // refresh: true, - // }, - // callback: () => { - // message.success(i18n('common.text.submittedSuccessfully')); - // }, - // }); - // }); - } - - function refresh() { - // setIsLoading(true); - const params = { - ...data.extraParams, - extraParams:{ - ...data.extraParams - }, - refresh: true, - } - getChildren?.(params).then((res) => { - data.children = res as any; - }) - .finally(() => { - // setIsLoading(false); - }) - } - - function openEditTableData() { - // const payload = { - // type: CreateTabIntroType.EditTableData, - // workspaceTabType: WorkspaceTabType.EditTableData, - // treeNodeData: { - // ...data, - // name: data.key, - // }, - // }; - // dispatch({ - // type: 'workspace/setCreateTabIntro', - // payload, - // }); - } - - function handleOk() { - if (verifyTableName === data.key) { - // const p: any = { - // ...data.extraParams, - // tableName: data.key, - // }; - // mysqlServer.deleteTable(p).then(() => { - // message.success(i18n('common.text.successfullyDelete')); - // dispatch({ - // type: 'workspace/fetchGetCurTableList', - // payload: { - // ...curWorkspaceParams, - // extraParams: curWorkspaceParams, - // }, - // callback: () => { - // setVerifyDialog(false); - // setVerifyTableName(''); - // }, - // }); - // }); - } else { - message.error(i18n('workspace.tips.affirmDeleteTable')); - } - } - - // 有些数据库不支持的操作,需要排除掉 - function excludeSomeOperation() { - const excludes = dataSourceFormConfig.baseInfo.excludes; - const newOperationColumn: OperationColumn[] = []; - operationColumn?.map((item: OperationColumn) => { - let flag = false; - excludes?.map((t) => { - if (item === t) { - flag = true; - } - }); - if (!flag) { - newOperationColumn.push(item); - } - }); - return newOperationColumn; - } - - const dropdowns = useMemo(() => { - if (dataSourceFormConfig) { - return excludeSomeOperation().map((t, i) => { - const concrete = operationColumnConfig[t](data); - return { - key: i, - label: , - onClick: concrete.handle, - }; - }); - } - return []; - }, [dataSourceFormConfig]); - - return ( - <> - {modelDom} - {notificationDom} - { - setVerifyDialog(false); - }} - > - { - setVerifyTableName(e.target.value); - }} - /> - - {/* 这里后续肯定是要提出去的 */} - {monacoVerifyDialog && ( - { - setMonacoVerifyDialog(false); - }} - footer={false} - > -
- -
-
- )} - - ); -} - -export default memo(TreeNodeRightClick); diff --git a/chat2db-client/src/blocks/NewTree/functions/deleteTable.less b/chat2db-client/src/blocks/NewTree/functions/deleteTable.less deleted file mode 100644 index 028684100..000000000 --- a/chat2db-client/src/blocks/NewTree/functions/deleteTable.less +++ /dev/null @@ -1,21 +0,0 @@ -.deleteTableFooter{ - display: flex; - justify-content: center; - button{ - margin: 0px 15px; - } - -} - -.checkContainer{ - margin: 15px 0px 25px; -} - -.deleteModalContent{ - display: flex; - flex-direction: column; - justify-content: center; - font-size: 16px; - font-weight: 500; - text-align: center; -} \ No newline at end of file diff --git a/chat2db-client/src/blocks/NewTree/functions/deleteTable.tsx b/chat2db-client/src/blocks/NewTree/functions/deleteTable.tsx deleted file mode 100644 index d55d1507c..000000000 --- a/chat2db-client/src/blocks/NewTree/functions/deleteTable.tsx +++ /dev/null @@ -1,64 +0,0 @@ -// 置顶表格 -import React, { useState } from 'react'; -import mysqlService from '@/service/sql'; -import { Button, Checkbox } from 'antd'; -import { openModal } from '@/store/common/components'; -import styles from './deleteTable.less'; -import i18n from '@/i18n'; - -export const deleteTable = (treeNodeData) => { - openModal({ - width: '450px', - content: , - }); -}; - -export const DeleteModalContent = (params: { treeNodeData: any; openModal: any }) => { - const { treeNodeData } = params; - // 禁用确定按钮 - const [userChecked, setUserChecked] = useState(false); - - const onOk = () => { - const p: any = { - dataSourceId: treeNodeData.extraParams.dataSourceId, - databaseName: treeNodeData.extraParams.databaseName, - schemaName: treeNodeData.extraParams.schemaName, - tableName: treeNodeData.name, - }; - mysqlService.deleteTable(p).then(() => { - treeNodeData.parentNode.loadData({ - refresh: true, - }); - openModal(false); - }); - }; - - return ( -
-
{i18n('workspace.tree.delete.table.tip', `"${treeNodeData.name}"`)}
-
- { - setUserChecked(e.target.checked); - }} - > - {i18n('workspace.tree.delete.tip')} - -
-
- - -
-
- ); -}; diff --git a/chat2db-client/src/blocks/NewTree/functions/openAsyncSql.ts b/chat2db-client/src/blocks/NewTree/functions/openAsyncSql.ts deleted file mode 100644 index 98cf337ef..000000000 --- a/chat2db-client/src/blocks/NewTree/functions/openAsyncSql.ts +++ /dev/null @@ -1,133 +0,0 @@ -import { WorkspaceTabType } from '@/constants'; -import sqlService from '@/service/sql'; -import {createConsole} from '@/pages/main/workspace/store/console' - -export const openView = (props:{ - addWorkspaceTab: any; - treeNodeData: any; -}) => { - const { treeNodeData } = props; - createConsole({ - name: treeNodeData.name, - operationType: WorkspaceTabType.VIEW, - dataSourceId: treeNodeData.extraParams!.dataSourceId!, - dataSourceName: treeNodeData.extraParams!.dataSourceName!, - databaseType: treeNodeData.extraParams!.databaseType!, - databaseName: treeNodeData.extraParams?.databaseName, - schemaName: treeNodeData.extraParams?.schemaName, - loadSQL: ()=>{ - return new Promise((resolve) => { - sqlService - .getViewDetail({ - dataSourceId: treeNodeData.extraParams!.dataSourceId!, - databaseType: treeNodeData.extraParams!.databaseType!, - databaseName: treeNodeData.extraParams!.databaseName!, - schemaName: treeNodeData.extraParams?.schemaName, - tableName: treeNodeData.name - } as any) - .then((res) => { - // 更新ddl - resolve(res.ddl); - }); - }); - } - }) -} - -export const openFunction = (props:{ - treeNodeData: any; -}) => { - const { treeNodeData } = props; - createConsole({ - name: treeNodeData.name, - operationType: WorkspaceTabType.FUNCTION, - dataSourceId: treeNodeData.extraParams!.dataSourceId!, - dataSourceName: treeNodeData.extraParams!.dataSourceName!, - databaseType: treeNodeData.extraParams!.databaseType!, - databaseName: treeNodeData.extraParams?.databaseName, - schemaName: treeNodeData.extraParams?.schemaName, - loadSQL: ()=>{ - return new Promise((resolve) => { - sqlService - .getFunctionDetail({ - dataSourceId: treeNodeData.extraParams!.dataSourceId!, - databaseType: treeNodeData.extraParams!.databaseType!, - databaseName: treeNodeData.extraParams!.databaseName!, - schemaName: treeNodeData.extraParams?.schemaName, - functionName: treeNodeData.name - } as any) - .then((res) => { - // 更新ddl - resolve(res.functionBody); - }); - }); - } - }) -} - -export const openProcedure = (props:{ - treeNodeData: any; -}) => { - const { treeNodeData } = props; - createConsole({ - name: treeNodeData.name, - operationType: WorkspaceTabType.PROCEDURE, - dataSourceId: treeNodeData.extraParams!.dataSourceId!, - dataSourceName: treeNodeData.extraParams!.dataSourceName!, - databaseType: treeNodeData.extraParams!.databaseType!, - databaseName: treeNodeData.extraParams?.databaseName, - schemaName: treeNodeData.extraParams?.schemaName, - loadSQL: ()=>{ - return new Promise((resolve) => { - sqlService - .getProcedureDetail({ - dataSourceId: treeNodeData.extraParams!.dataSourceId!, - databaseType: treeNodeData.extraParams!.databaseType!, - databaseName: treeNodeData.extraParams!.databaseName!, - schemaName: treeNodeData.extraParams?.schemaName, - procedureName: treeNodeData.name - } as any) - .then((res) => { - // 更新ddl - resolve(res.procedureBody); - }); - }); - } - }) -} - -export const openTrigger = (props:{ - treeNodeData: any; -}) => { - const {treeNodeData } = props; - createConsole({ - name: treeNodeData.name, - operationType: WorkspaceTabType.TRIGGER, - dataSourceId: treeNodeData.extraParams!.dataSourceId!, - dataSourceName: treeNodeData.extraParams!.dataSourceName!, - databaseType: treeNodeData.extraParams!.databaseType!, - databaseName: treeNodeData.extraParams?.databaseName, - schemaName: treeNodeData.extraParams?.schemaName, - loadSQL: ()=>{ - return new Promise((resolve) => { - sqlService - .getTriggerDetail({ - dataSourceId: treeNodeData.extraParams!.dataSourceId!, - databaseType: treeNodeData.extraParams!.databaseType!, - databaseName: treeNodeData.extraParams!.databaseName!, - schemaName: treeNodeData.extraParams?.schemaName, - triggerName: treeNodeData.name - } as any) - .then((res) => { - // 更新ddl - resolve(res.triggerBody); - }); - }); - } - }) -} - - - - - diff --git a/chat2db-client/src/blocks/NewTree/functions/pinTable.ts b/chat2db-client/src/blocks/NewTree/functions/pinTable.ts deleted file mode 100644 index 8142548bd..000000000 --- a/chat2db-client/src/blocks/NewTree/functions/pinTable.ts +++ /dev/null @@ -1,15 +0,0 @@ -// 置顶表格 -import mysqlService from '@/service/sql'; -export const handelPinTable = ({ treeNodeData,loadData }) => { - const api = treeNodeData.pinned ? 'deleteTablePin' : 'addTablePin'; - mysqlService[api]({ - dataSourceId: treeNodeData.extraParams.dataSourceId, - databaseName: treeNodeData.extraParams.databaseName, - schemaName: treeNodeData.extraParams.schemaName, - tableName: treeNodeData.name, - }).then(()=>{ - loadData({ - refresh: true, - }) - }) -}; diff --git a/chat2db-client/src/blocks/NewTree/functions/refresh.ts b/chat2db-client/src/blocks/NewTree/functions/refresh.ts deleted file mode 100644 index d925800b2..000000000 --- a/chat2db-client/src/blocks/NewTree/functions/refresh.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { ITreeNode } from '@/typings'; - -export const refreshTreeNode = (props:{ - treeNodeData: ITreeNode; -}) => { - const { treeNodeData } = props; - console.log(treeNodeData) -} diff --git a/chat2db-client/src/blocks/NewTree/functions/viewDDL.less b/chat2db-client/src/blocks/NewTree/functions/viewDDL.less deleted file mode 100644 index 595b8ff8e..000000000 --- a/chat2db-client/src/blocks/NewTree/functions/viewDDL.less +++ /dev/null @@ -1,6 +0,0 @@ -.monacoEditorBox{ - border: 1px solid var(--color-border); - border-radius: 4px; - height: 60vh; - overflow: hidden; -} \ No newline at end of file diff --git a/chat2db-client/src/blocks/NewTree/functions/viewDDL.tsx b/chat2db-client/src/blocks/NewTree/functions/viewDDL.tsx deleted file mode 100644 index 21bedebac..000000000 --- a/chat2db-client/src/blocks/NewTree/functions/viewDDL.tsx +++ /dev/null @@ -1,47 +0,0 @@ -// 置顶表格 -import React from 'react'; -import mysqlService from '@/service/sql'; -import { v4 as uuid } from 'uuid'; -import styles from './viewDDL.less'; - -import { openModal } from '@/store/common/components'; - -import MonacoEditor from '@/components/MonacoEditor'; - -export const viewDDL = (treeNodeData) => { - const getSql = () => { - return new Promise((resolve) => { - mysqlService - .exportCreateTableSql({ - dataSourceId: treeNodeData.extraParams.dataSourceId, - databaseName: treeNodeData.extraParams.databaseName, - schemaName: treeNodeData.extraParams.schemaName, - tableName: treeNodeData.name, - }) - .then((res) => { - resolve(res); - }); - }); - }; - - openModal({ - title: `DDL-${treeNodeData.name}`, - width: '60%', - height: '60%', - footer: false, - content: ( -
- -
- ), - }); -}; - -export const MonacoEditorAsync = (params: { getSql: any }) => { - const { getSql } = params; - const monacoEditorRef = React.useRef(); - getSql().then((sql) => { - monacoEditorRef.current.setValue(sql); - }); - return ; -}; diff --git a/chat2db-client/src/blocks/NewTree/hooks/useGetRightClickMenu.ts b/chat2db-client/src/blocks/NewTree/hooks/useGetRightClickMenu.ts deleted file mode 100644 index d6527b2fb..000000000 --- a/chat2db-client/src/blocks/NewTree/hooks/useGetRightClickMenu.ts +++ /dev/null @@ -1,652 +0,0 @@ -import { ITreeNode } from '@/typings'; -import { OperationColumn, WorkspaceTabType, TreeNodeType } from '@/constants'; -import i18n from '@/i18n'; -import { v4 as uuid } from 'uuid'; - -// ----- components ----- -import { dataSourceFormConfigs } from '@/components/ConnectionEdit/config/dataSource'; -import { IConnectionConfig } from '@/components/ConnectionEdit/config/types'; - -// ----- config ----- -import { ITreeConfigItem, treeConfig } from '../treeConfig'; -import { useMemo } from 'react'; - -// ----- store ----- -import { createConsole, addWorkspaceTab } from '@/pages/main/workspace/store/console'; -import { useWorkspaceStore } from '@/pages/main/workspace/store'; - -// ---- functions ----- -import { openView, openFunction, openProcedure, openTrigger } from '../functions/openAsyncSql'; -import { handelPinTable } from '../functions/pinTable'; -import { viewDDL } from '../functions/viewDDL'; -import { deleteTable } from '../functions/deleteTable'; - -// ----- utils ----- -import { compatibleDataBaseName } from '@/utils/database'; - -interface IProps { - treeNodeData: ITreeNode; - loadData: any; -} - -interface IOperationColumnConfigItem { - text: string; - icon: string; - doubleClickTrigger?: boolean; - handle: (treeNodeData: ITreeNode) => void; - discard?: boolean; -} - -interface IRightClickMenu { - key: number; - onClick: (treeNodeData: ITreeNode) => void; - type: OperationColumn; - doubleClickTrigger?: boolean; - labelProps: { - icon: string; - label: string; - }; -} - -export const useGetRightClickMenu = (props: IProps) => { - const { treeNodeData, loadData } = props; - - const { openCreateDatabaseModal, currentConnectionDetails } = useWorkspaceStore((state) => { - return { - openCreateDatabaseModal: state.openCreateDatabaseModal, - currentConnectionDetails: state.currentConnectionDetails, - }; - }); - - const handelOpenCreateDatabaseModal = (type: 'database' | 'schema') => { - - const relyOnParams = { - databaseType: treeNodeData.extraParams!.databaseType, - dataSourceId: treeNodeData.extraParams!.dataSourceId!, - databaseName: treeNodeData.name, - } - - openCreateDatabaseModal?.({ - type, - relyOnParams, - executedCallback: () => { - loadData({ - refresh: true, - }); - }, - }); - }; - - const rightClickMenu = useMemo(() => { - // 拿出当前节点的配置 - const treeNodeConfig: ITreeConfigItem = treeConfig[treeNodeData.treeNodeType]; - const { operationColumn } = treeNodeConfig; - - const dataSourceFormConfig = dataSourceFormConfigs.find((t: IConnectionConfig) => { - return t.type === treeNodeData.extraParams?.databaseType; - })!; - - // 有些数据库不支持的操作,需要排除掉 - function excludeSomeOperation() { - const excludes = dataSourceFormConfig.baseInfo.excludes; - const newOperationColumn: OperationColumn[] = []; - operationColumn?.map((item: OperationColumn) => { - let flag = false; - excludes?.map((t) => { - if (item === t) { - flag = true; - } - }); - if (!flag) { - newOperationColumn.push(item); - } - }); - return newOperationColumn; - } - - const operationColumnConfig: { [key in string]: IOperationColumnConfigItem } = { - // 刷新 - [OperationColumn.Refresh]: { - text: i18n('common.button.refresh'), - icon: '\uec08', - handle: () => { - loadData?.({ - refresh: true, - }); - }, - }, - - // 创建console - [OperationColumn.CreateConsole]: { - text: i18n('workspace.menu.queryConsole'), - icon: '\ue619', - handle: () => { - createConsole({ - dataSourceId: treeNodeData.extraParams!.dataSourceId!, - dataSourceName: treeNodeData.extraParams!.dataSourceName!, - databaseType: treeNodeData.extraParams!.databaseType!, - databaseName: treeNodeData.extraParams?.databaseName, - schemaName: treeNodeData.extraParams?.schemaName, - }); - }, - }, - - // 查看所有表 - [OperationColumn.ViewAllTable]: { - text: i18n('workspace.menu.viewAllTable'), - icon: '\ue611', - handle: () => { - addWorkspaceTab({ - id: uuid(), - type: WorkspaceTabType.ViewAllTable, - title: `${treeNodeData.extraParams!.databaseName!}-tables`, - uniqueData: { - dataSourceId: treeNodeData.extraParams!.dataSourceId!, - dataSourceName: treeNodeData.extraParams!.dataSourceName!, - databaseType: treeNodeData.extraParams!.databaseType!, - databaseName: treeNodeData.extraParams?.databaseName, - schemaName: treeNodeData.extraParams?.schemaName, - }, - }) - - }, - }, - - // 创建表 - [OperationColumn.CreateTable]: { - text: i18n('editTable.button.createTable'), - icon: '\ue792', - handle: () => { - addWorkspaceTab({ - id: uuid(), - title: i18n('editTable.button.createTable'), - type: WorkspaceTabType.CreateTable, - uniqueData: { - dataSourceId: treeNodeData.extraParams!.dataSourceId!, - databaseType: treeNodeData.extraParams!.databaseType!, - databaseName: treeNodeData.extraParams?.databaseName, - schemaName: treeNodeData.extraParams?.schemaName, - submitCallback: () => {loadData?.({refresh: true})}, - }, - }); - }, - discard: (treeNodeData.treeNodeType === TreeNodeType.DATABASE && currentConnectionDetails?.supportSchema), - }, - - // 删除表 - [OperationColumn.DeleteTable]: { - text: i18n('workspace.menu.deleteTable'), - icon: '\ue6a7', - handle: () => { - deleteTable(treeNodeData); - }, - }, - - // 查看ddl - [OperationColumn.ViewDDL]: { - text: i18n('workspace.menu.ViewDDL'), - icon: '\ue665', - handle: () => { - viewDDL(treeNodeData) - }, - }, - - // 置顶 - [OperationColumn.Pin]: { - text: treeNodeData.pinned ? i18n('workspace.menu.unPin') : i18n('workspace.menu.pin'), - icon: treeNodeData.pinned ? '\ue61d' : '\ue627', - handle: () => { - handelPinTable({ - treeNodeData, - loadData: () => { - loadData({treeNodeData:treeNodeData.parentNode}) - } - }); - }, - }, - - // 编辑表 - [OperationColumn.EditTable]: { - text: i18n('workspace.menu.editTable'), - icon: '\ue602', - handle: () => { - addWorkspaceTab({ - id: `${OperationColumn.EditTable}-${treeNodeData.uuid}`, - title: treeNodeData?.name, - type: WorkspaceTabType.EditTable, - uniqueData: { - dataSourceId: treeNodeData.extraParams!.dataSourceId!, - databaseType: treeNodeData.extraParams!.databaseType!, - databaseName: treeNodeData.extraParams?.databaseName, - schemaName: treeNodeData.extraParams?.schemaName, - tableName: treeNodeData?.name, - submitCallback: () => { - - loadData({ - treeNodeData: treeNodeData.parentNode, - refresh: true - }) - }, - }, - }); - }, - }, - - // 复制名称 - [OperationColumn.CopyName]: { - text: i18n('common.button.copyName'), - icon: '\uec7a', - handle: () => { - navigator.clipboard.writeText(treeNodeData.name); - }, - }, - - // 打开表 - [OperationColumn.OpenTable]: { - text: i18n('workspace.menu.openTable'), - icon: '\ue618', - doubleClickTrigger: true, - handle: () => { - const databaseName = compatibleDataBaseName(treeNodeData.name!, treeNodeData.extraParams!.databaseType); - addWorkspaceTab({ - id: `${OperationColumn.OpenTable}-${treeNodeData.uuid}`, - title: treeNodeData.name, - type: WorkspaceTabType.EditTableData, - uniqueData: { - dataSourceId: treeNodeData.extraParams!.dataSourceId!, - databaseType: treeNodeData.extraParams!.databaseType!, - databaseName: treeNodeData.extraParams?.databaseName, - schemaName: treeNodeData.extraParams?.schemaName, - tableName: treeNodeData.name, - sql: 'select * from ' + databaseName, - }, - }); - }, - }, - - // 打开视图 - [OperationColumn.OpenView]: { - text: i18n('workspace.menu.view'), - icon: '\ue651', - doubleClickTrigger: true, - handle: () => { - openView({ - addWorkspaceTab, - treeNodeData, - }); - }, - }, - - // 打开函数 - [OperationColumn.OpenFunction]: { - text: i18n('workspace.menu.view'), - icon: '\ue651', - doubleClickTrigger: true, - handle: () => { - openFunction({ - addWorkspaceTab, - treeNodeData, - }); - }, - }, - - // 打开存储过程 - [OperationColumn.OpenProcedure]: { - text: i18n('workspace.menu.view'), - icon: '\ue651', - doubleClickTrigger: true, - handle: () => { - openProcedure({ - addWorkspaceTab, - treeNodeData, - }); - }, - }, - - // 打开触发器 - [OperationColumn.OpenTrigger]: { - text: i18n('workspace.menu.view'), - icon: '\ue651', - doubleClickTrigger: true, - handle: () => { - openTrigger({ - addWorkspaceTab, - treeNodeData, - }); - }, - }, - - // 创建数据库 - [OperationColumn.CreateDatabase]: { - text: i18n('workspace.menu.createDatabase'), - icon: '\ue816', - handle: () => { - handelOpenCreateDatabaseModal('database'); - }, - }, - - // 创建schema - [OperationColumn.CreateSchema]: { - text: i18n('workspace.menu.createSchema'), - icon: '\ue696', - handle: () => { - handelOpenCreateDatabaseModal('schema'); - }, - discard: !currentConnectionDetails?.supportSchema, - }, - }; - - // 根据配置生成右键菜单 - const finalList: IRightClickMenu[] = []; - excludeSomeOperation().forEach((t, i) => { - const concrete = operationColumnConfig[t]; - if (!concrete.discard) { - finalList.push({ - key: i, - onClick: concrete?.handle, - type: t, - doubleClickTrigger: concrete.doubleClickTrigger, - labelProps: { - icon: concrete?.icon, - label: concrete?.text, - }, - }); - } - }); - return finalList; - }, [treeNodeData]); - - return rightClickMenu; -}; - -export const getRightClickMenu = (props: IProps) => { - const { treeNodeData, loadData } = props; - - const openCreateDatabaseModal = useWorkspaceStore.getState().openCreateDatabaseModal; - const currentConnectionDetails = useWorkspaceStore.getState().currentConnectionDetails; - - const handelOpenCreateDatabaseModal = (type: 'database' | 'schema') => { - - const relyOnParams = { - databaseType: treeNodeData.extraParams!.databaseType, - dataSourceId: treeNodeData.extraParams!.dataSourceId!, - databaseName: treeNodeData.name, - } - - openCreateDatabaseModal?.({ - type, - relyOnParams, - executedCallback: () => { - loadData({ - refresh: true, - }); - }, - }); - }; - - // 拿出当前节点的配置 - const treeNodeConfig: ITreeConfigItem = treeConfig[treeNodeData.treeNodeType]; - const { operationColumn } = treeNodeConfig; - - const dataSourceFormConfig = dataSourceFormConfigs.find((t: IConnectionConfig) => { - return t.type === treeNodeData.extraParams?.databaseType; - })!; - - // 有些数据库不支持的操作,需要排除掉 - function excludeSomeOperation() { - const excludes = dataSourceFormConfig.baseInfo.excludes; - const newOperationColumn: OperationColumn[] = []; - operationColumn?.map((item: OperationColumn) => { - let flag = false; - excludes?.map((t) => { - if (item === t) { - flag = true; - } - }); - if (!flag) { - newOperationColumn.push(item); - } - }); - return newOperationColumn; - } - - const operationColumnConfig: { [key in string]: IOperationColumnConfigItem } = { - // 刷新 - [OperationColumn.Refresh]: { - text: i18n('common.button.refresh'), - icon: '\uec08', - handle: () => { - loadData?.({ - refresh: true, - }); - }, - }, - - // 创建console - [OperationColumn.CreateConsole]: { - text: i18n('workspace.menu.queryConsole'), - icon: '\ue619', - handle: () => { - createConsole({ - dataSourceId: treeNodeData.extraParams!.dataSourceId!, - dataSourceName: treeNodeData.extraParams!.dataSourceName!, - databaseType: treeNodeData.extraParams!.databaseType!, - databaseName: treeNodeData.extraParams?.databaseName, - schemaName: treeNodeData.extraParams?.schemaName, - }); - }, - }, - - // 查看所有表 - [OperationColumn.ViewAllTable]: { - text: i18n('workspace.menu.viewAllTable'), - icon: '\ue611', - handle: () => { - addWorkspaceTab({ - id: uuid(), - type: WorkspaceTabType.ViewAllTable, - title: `${treeNodeData.extraParams!.databaseName!}-tables`, - uniqueData: { - dataSourceId: treeNodeData.extraParams!.dataSourceId!, - dataSourceName: treeNodeData.extraParams!.dataSourceName!, - databaseType: treeNodeData.extraParams!.databaseType!, - databaseName: treeNodeData.extraParams?.databaseName, - schemaName: treeNodeData.extraParams?.schemaName, - }, - }) - - }, - }, - - // 创建表 - [OperationColumn.CreateTable]: { - text: i18n('editTable.button.createTable'), - icon: '\ue792', - handle: () => { - addWorkspaceTab({ - id: uuid(), - title: i18n('editTable.button.createTable'), - type: WorkspaceTabType.CreateTable, - uniqueData: { - dataSourceId: treeNodeData.extraParams!.dataSourceId!, - databaseType: treeNodeData.extraParams!.databaseType!, - databaseName: treeNodeData.extraParams?.databaseName, - schemaName: treeNodeData.extraParams?.schemaName, - submitCallback: () => {treeNodeData.loadData?.({refresh: true})}, - }, - }); - }, - discard: (treeNodeData.treeNodeType === TreeNodeType.DATABASE && currentConnectionDetails?.supportSchema), - }, - - // 删除表 - [OperationColumn.DeleteTable]: { - text: i18n('workspace.menu.deleteTable'), - icon: '\ue6a7', - handle: () => { - deleteTable(treeNodeData); - }, - }, - - // 查看ddl - [OperationColumn.ViewDDL]: { - text: i18n('workspace.menu.ViewDDL'), - icon: '\ue665', - handle: () => { - viewDDL(treeNodeData) - }, - }, - - // 置顶 - [OperationColumn.Pin]: { - text: treeNodeData.pinned ? i18n('workspace.menu.unPin') : i18n('workspace.menu.pin'), - icon: treeNodeData.pinned ? '\ue61d' : '\ue627', - handle: () => { - handelPinTable({treeNodeData, loadData: treeNodeData.parentNode!.loadData!}); - }, - }, - - // 编辑表 - [OperationColumn.EditTable]: { - text: i18n('workspace.menu.editTable'), - icon: '\ue602', - handle: () => { - addWorkspaceTab({ - id: `${OperationColumn.EditTable}-${treeNodeData.uuid}`, - title: treeNodeData?.name, - type: WorkspaceTabType.EditTable, - uniqueData: { - dataSourceId: treeNodeData.extraParams!.dataSourceId!, - databaseType: treeNodeData.extraParams!.databaseType!, - databaseName: treeNodeData.extraParams?.databaseName, - schemaName: treeNodeData.extraParams?.schemaName, - tableName: treeNodeData?.name, - submitCallback: () => {treeNodeData.parentNode?.loadData?.({refresh: true})}, - }, - }); - }, - }, - - // 复制名称 - [OperationColumn.CopyName]: { - text: i18n('common.button.copyName'), - icon: '\uec7a', - handle: () => { - navigator.clipboard.writeText(treeNodeData.name); - }, - }, - - // 打开表 - [OperationColumn.OpenTable]: { - text: i18n('workspace.menu.openTable'), - icon: '\ue618', - doubleClickTrigger: true, - handle: () => { - const databaseName = compatibleDataBaseName(treeNodeData.name!, treeNodeData.extraParams!.databaseType); - addWorkspaceTab({ - id: `${OperationColumn.OpenTable}-${treeNodeData.uuid}`, - title: treeNodeData.name, - type: WorkspaceTabType.EditTableData, - uniqueData: { - dataSourceId: treeNodeData.extraParams!.dataSourceId!, - databaseType: treeNodeData.extraParams!.databaseType!, - databaseName: treeNodeData.extraParams?.databaseName, - schemaName: treeNodeData.extraParams?.schemaName, - tableName: treeNodeData.name, - sql: 'select * from ' + databaseName, - }, - }); - }, - }, - - // 打开视图 - [OperationColumn.OpenView]: { - text: i18n('workspace.menu.view'), - icon: '\ue651', - doubleClickTrigger: true, - handle: () => { - openView({ - addWorkspaceTab, - treeNodeData, - }); - }, - }, - - // 打开函数 - [OperationColumn.OpenFunction]: { - text: i18n('workspace.menu.view'), - icon: '\ue651', - doubleClickTrigger: true, - handle: () => { - openFunction({ - addWorkspaceTab, - treeNodeData, - }); - }, - }, - - // 打开存储过程 - [OperationColumn.OpenProcedure]: { - text: i18n('workspace.menu.view'), - icon: '\ue651', - doubleClickTrigger: true, - handle: () => { - openProcedure({ - addWorkspaceTab, - treeNodeData, - }); - }, - }, - - // 打开触发器 - [OperationColumn.OpenTrigger]: { - text: i18n('workspace.menu.view'), - icon: '\ue651', - doubleClickTrigger: true, - handle: () => { - openTrigger({ - addWorkspaceTab, - treeNodeData, - }); - }, - }, - - // 创建数据库 - [OperationColumn.CreateDatabase]: { - text: i18n('workspace.menu.createDatabase'), - icon: '\ue816', - handle: () => { - handelOpenCreateDatabaseModal('database'); - }, - }, - - // 创建schema - [OperationColumn.CreateSchema]: { - text: i18n('workspace.menu.createSchema'), - icon: '\ue696', - handle: () => { - handelOpenCreateDatabaseModal('schema'); - }, - discard: !currentConnectionDetails?.supportSchema, - }, - }; - - // 根据配置生成右键菜单 - const finalList: IRightClickMenu[] = []; - excludeSomeOperation().forEach((t,i) => { - const concrete = operationColumnConfig[t]; - if (!concrete.discard) { - finalList.push({ - key: i, - onClick: concrete?.handle, - type: t, - doubleClickTrigger: concrete.doubleClickTrigger, - labelProps: { - icon: concrete?.icon, - label: concrete?.text, - }, - }); - } - }); - return finalList; -}; diff --git a/chat2db-client/src/blocks/NewTree/hooks/useTreeNodeFocus.ts b/chat2db-client/src/blocks/NewTree/hooks/useTreeNodeFocus.ts deleted file mode 100644 index a09f6dfe7..000000000 --- a/chat2db-client/src/blocks/NewTree/hooks/useTreeNodeFocus.ts +++ /dev/null @@ -1,6 +0,0 @@ -import { useTreeStore } from '../treeStore'; - -export const useTreeNodeFocus = (treeId) => { - const focusId = useTreeStore((state) => state.focusId); - return focusId === treeId; -} diff --git a/chat2db-client/src/blocks/NewTree/index.less b/chat2db-client/src/blocks/NewTree/index.less deleted file mode 100644 index 6f85f5ec2..000000000 --- a/chat2db-client/src/blocks/NewTree/index.less +++ /dev/null @@ -1,188 +0,0 @@ -@import '../../styles/var.less'; - -.scrollBox { - overflow-y: auto; - padding: 2px 6px 10px; - box-sizing: border-box; - height: 100%; -} - -.treeBox { - overflow: hidden; - height: 100%; -} - -.treeListHolder { - height: calc(var(--tree-node-count) * 26px); - min-height: 100%; -} - -// 如果treeBox滚动的高度>0那么久加一个上边框 -.treeBoxScroll { - border-top: 1px solid var(--color-border-secondary); -} - -.leftModuleTitleShadow { - border-top: 1px solid red; -} - -.treeNode { - display: flex; - align-items: center; - overflow: hidden; - height: 26px; - border-radius: 4px; - opacity: 1; - cursor: pointer; - transition: opacity 0.05s ease-in, height 0.1s ease-in; - user-select: none; - padding-right: 6px; - - &:hover { - } -} - -.treeNodeFocus { - // background-color: var(--color-hover-bg); - // background-color: var(--color-primary-bg-hover); - background-color: var(--color-primary-hover); - // background-color: var(--color-primary); - .right { - // color: var(--color-primary); - color: var(--color-bg-base); - } - .indent { - &::before { - opacity: 0; - } - } - .type { - color: var(--color-bg-base); - } -} - -.left { - display: flex; - align-items: center; - height: 100%; - flex-shrink: 0; -} - -.right { - flex: 1; - display: flex; - align-items: center; - height: 100%; - padding-left: 4px; - border-radius: 2px; - .moreBox { - width: 22px; - height: 100%; - display: flex; - justify-content: center; - align-items: center; - opacity: 0; - } - - .moreButton { - flex-shrink: 0; - transform: rotate(90deg); - } -} - -.arrows { - flex-shrink: 0; - height: 20px; - width: 18px; - display: flex; - align-items: center; - transform: rotate(0deg); - transition: transform 0.2s ease-in; -} - -.loadingArrows { - display: flex; -} - -.arrowsIcon { - display: inline-block; - transform: rotate(0deg); - font-size: 12px; -} - -.rotateArrowsIcon { - transform: rotate(90deg); -} - -.dblclickArea { - display: flex; - flex: 1; - height: 26px; -} - -.typeIcon { - height: 26px; - display: flex; - align-items: center; - width: 20px; -} - -.typeImg { - height: 20px; - width: 20px; - background-repeat: no-repeat; - background-size: 20px 20px; -} - -.contentText { - width: 0; - flex: 1; - display: flex; - align-items: center; -} - -.name { - .f-lines(1); - line-height: 20px; -} - -.type { - font-size: 11px; - flex-shrink: 0; - line-height: 20px; - color: var(--color-text-tertiary); - margin-left: 10px; -} - -.describe { - flex: 1; - font-size: 10px; - margin-left: 20px; - color: var(--color-text-tertiary); - .f-single-line(); -} - -.indent { - width: 20px; - height: 100%; - position: relative; - &::before { - position: absolute; - top: 0; - right: 9px; - bottom: -4px; - border-right: 1px solid var(--color-border); - content: ''; - } -} - -.hiddenTreeNode { - height: 0; - opacity: 0; - transition: opacity 0.2s ease-in, height 0.1s ease-in; - - .arrows { - transform: rotate(0deg); - transition: transform 0.2s ease-in; - } -} diff --git a/chat2db-client/src/blocks/NewTree/index.tsx b/chat2db-client/src/blocks/NewTree/index.tsx deleted file mode 100644 index d2ebc8f80..000000000 --- a/chat2db-client/src/blocks/NewTree/index.tsx +++ /dev/null @@ -1,337 +0,0 @@ -import React, { memo, useEffect, useMemo, useState, createContext, useContext, useRef } from 'react'; -import styles from './index.less'; -import classnames from 'classnames'; -import Iconfont from '@/components/Iconfont'; -import { Tooltip, Dropdown } from 'antd'; -import { ITreeNode } from '@/typings'; -import { TreeNodeType, databaseMap } from '@/constants'; -import { treeConfig, switchIcon, ITreeConfigItem } from './treeConfig'; -import { useCommonStore } from '@/store/common'; -import LoadingGracile from '@/components/Loading/LoadingGracile'; -import { setFocusId, useTreeStore } from './treeStore'; -import { useGetRightClickMenu } from './hooks/useGetRightClickMenu'; -import MenuLabel from '@/components/MenuLabel'; -import LoadingContent from '@/components/Loading/LoadingContent'; -import { cloneDeep } from 'lodash'; -// import { flushSync } from 'react-dom'; - -interface IProps { - className?: string; - treeData: ITreeNode[] | null; - searchValue: string; -} - -interface TreeNodeIProps { - data: ITreeNode; - level: number; -} - -interface IContext { - treeData: ITreeNode[]; - setTreeData: (value: ITreeNode[] | null) => void; -} - -export const Context = createContext({} as any); - -// 树转平级 -const smoothTree = (treeData: ITreeNode[], result: ITreeNode[] = [], parentNode?: ITreeNode) => { - treeData.forEach((item) => { - if (parentNode) { - item.parentNode = parentNode; - item.level = (parentNode.level || 0) + 1; - } - result.push(item); - if (item.children) { - smoothTree(item.children, result, item); - } - }); - return result; -}; - -const itemHeight = 26; // 每个 item 的高度 -const paddingCount = 2; - -const Tree = (props: IProps) => { - const { className, treeData: outerTreeData, searchValue } = props; - const [treeData, setTreeData] = useState(null); - const [smoothTreeData, setSmoothTreeData] = useState([]); - const [searchTreeData, setSearchTreeData] = useState(null); // 搜索结果 - const [scrollTop, setScrollTop] = useState(0); // 滚动位置 // 继续需要渲染的 item 索引有哪些 - - const startIdx = useMemo(() => { - let _startIdx = Math.floor(scrollTop / itemHeight); - _startIdx = Math.max(_startIdx - paddingCount, 0); // 处理越界情况 - return _startIdx; - }, [scrollTop]); - - const top = itemHeight * startIdx; // 第一个渲染的 item 到顶部距离 - - useEffect(() => { - setTreeData(outerTreeData); - setScrollTop(0); - }, [outerTreeData]); - - useEffect(() => { - if (treeData) { - const result: ITreeNode[] = []; - smoothTree(treeData, result); - setSmoothTreeData(result); - } else { - setSmoothTreeData([]); - } - }, [treeData]); - - const treeNodes = useMemo(() => { - const realNodeList = (searchTreeData || smoothTreeData).slice(startIdx, startIdx + 50); - return realNodeList.map((item) => { - return ; - }); - }, [smoothTreeData, searchTreeData, startIdx]); - - useEffect(() => { - if (searchValue) { - const ls = smoothTreeData.filter((item) => { - const reg = new RegExp(searchValue, 'i'); - return reg.test(item.name || ''); - }); - setSearchTreeData(ls); - } else { - setSearchTreeData(null); - } - setScrollTop(0); - }, [searchValue]); - - return ( - - -
{ - console.log(e.target.scrollTop); - setScrollTop(e.target.scrollTop); - }} - > -
-
- {treeNodes} -
-
- - - ); -}; - -const TreeNode = memo((props: TreeNodeIProps) => { - const { data: treeNodeData, level } = props; - const [isLoading, setIsLoading] = useState(false); - const indentArr = new Array(level).fill('indent'); - const { treeData, setTreeData } = useContext(Context); - // 当前节点是展开的 - const isExpandedRef = useRef(false); - - // 加载数据 - function loadData(_props?: { refresh: boolean; pageNo: number; treeNodeData?: ITreeNode }) { - const _treeNodeData = _props?.treeNodeData || props.data; - const treeNodeConfig: ITreeConfigItem = treeConfig[_treeNodeData.pretendNodeType || _treeNodeData.treeNodeType]; - setIsLoading(true); - if (_props?.pageNo === 1 || !_props?.pageNo) { - insertData(treeData!, _treeNodeData.uuid!, null); - } - - treeNodeConfig - .getChildren?.({ - ..._treeNodeData.extraParams, - extraParams: { - ..._treeNodeData.extraParams, - }, - refresh: _props?.refresh || false, - pageNo: _props?.pageNo || 1, - }) - .then((res: any) => { - if (res.length || res.data) { - if (res.data) { - insertData(treeData!, _treeNodeData.uuid!, res.data); - if (res.hasNextPage && isExpandedRef.current) { - loadData({ - refresh: _props?.refresh || false, - pageNo: res.pageNo + 1, - }); - } - } else { - insertData(treeData!, _treeNodeData.uuid!, res); - } - setIsLoading(false); - } else { - // 处理树可能出现不连续的情况 - if (treeNodeConfig.next) { - _treeNodeData.pretendNodeType = treeNodeConfig.next; - loadData(); - } else { - insertData(treeData!, _treeNodeData.uuid!, []); - setIsLoading(false); - } - } - }) - .catch(() => { - setIsLoading(false); - }); - } - - // 当前节点是否是focus - const isFocus = useTreeStore((state) => state.focusId) === treeNodeData.uuid; - - // 在treeData中找到对应的节点,插入数据 - const insertData = (_treeData: ITreeNode[], uuid: string, data: any): ITreeNode | null => { - let result: ITreeNode | null = null; - for (let i = 0; i < _treeData?.length; i++) { - if (_treeData[i].uuid === uuid) { - result = _treeData[i]; - if (data && isExpandedRef.current) { - result.children = [...(result.children || []), ...(data || [])]; - } else { - result.children = null; - } - result.expanded = !!data; - // 这里没写错 就是要改变treeData的引用 - setTreeData?.(cloneDeep(treeData || [])); - break; - } else { - if (_treeData[i].children) { - result = insertData(_treeData[i].children!, uuid, data); - if (result) { - break; - } - } - } - } - return result; - }; - - //展开-收起 - const handleClick = () => { - if (treeNodeData.expanded) { - isExpandedRef.current = false; - insertData(treeData!, treeNodeData.uuid!, null); - } else { - loadData(); - isExpandedRef.current = true; - } - }; - - // 找到对应的icon - const recognizeIcon = (treeNodeType: TreeNodeType) => { - if (treeNodeType === TreeNodeType.DATA_SOURCE) { - return databaseMap[treeNodeData.extraParams!.databaseType!]?.icon; - } else { - return ( - switchIcon[treeNodeType]?.[treeNodeData.children ? 'unfoldIcon' : 'icon'] || switchIcon[treeNodeType]?.icon - ); - } - }; - - // 点击节点 - const handelClickTreeNode = () => { - useCommonStore.setState({ - focusedContent: (treeNodeData.name || '') as any, - }); - setFocusId(treeNodeData.uuid || ''); - }; - - // 双击节点 - const handelDoubleClickTreeNode = () => { - if ( - treeNodeData.treeNodeType === TreeNodeType.TABLE || - treeNodeData.treeNodeType === TreeNodeType.VIEW || - treeNodeData.treeNodeType === TreeNodeType.PROCEDURE || - treeNodeData.treeNodeType === TreeNodeType.FUNCTION || - treeNodeData.treeNodeType === TreeNodeType.TRIGGER - ) { - rightClickMenu.find((item) => item.doubleClickTrigger)?.onClick(treeNodeData); - } else { - handleClick(); - } - }; - - const rightClickMenu = useGetRightClickMenu({ - treeNodeData, - loadData, - }); - - const treeNodeDom = useMemo(() => { - const dropdownsItems: any = rightClickMenu.map((item) => { - return { - key: item.key, - onClick: () => { - item.onClick(treeNodeData); - }, - label: , - }; - }); - - return ( - - -
-
- {indentArr.map((item, i) => { - return
; - })} -
-
- {!treeNodeData.isLeaf && ( -
- {isLoading ? ( - - ) : ( - - )} -
- )} -
-
- -
-
-
- {treeNodeData.treeNodeType === TreeNodeType.COLUMN && ( -
- {/* 转小写 */} - {treeNodeData.columnType?.toLowerCase()} -
- )} -
-
-
-
- - - ); - }, [isFocus, isLoading, rightClickMenu]); - - return treeNodeDom; -}); - -export default memo(Tree); diff --git a/chat2db-client/src/blocks/NewTree/treeConfig.tsx b/chat2db-client/src/blocks/NewTree/treeConfig.tsx deleted file mode 100644 index 07e908593..000000000 --- a/chat2db-client/src/blocks/NewTree/treeConfig.tsx +++ /dev/null @@ -1,634 +0,0 @@ -import { ITreeNode, IConnectionDetails } from '@/typings'; -import { TreeNodeType, OperationColumn } from '@/constants'; -import connectionService from '@/service/connection'; -import { v4 as uuid } from 'uuid'; - -import mysqlServer from '@/service/sql'; - -export type ITreeConfig = Partial<{ [key in TreeNodeType]: ITreeConfigItem }>; - -export const switchIcon: Partial<{ [key in TreeNodeType]: { icon: string; unfoldIcon?: string } }> = { - [TreeNodeType.DATABASE]: { - icon: '\ue669', - }, - [TreeNodeType.SCHEMAS]: { - icon: '\ue696', - }, - [TreeNodeType.TABLE]: { - icon: '\ue63e', - }, - [TreeNodeType.TABLES]: { - icon: '\ueabe', - unfoldIcon: '\ueabf', - }, - [TreeNodeType.COLUMNS]: { - icon: '\ueabe', - unfoldIcon: '\ueabf', - }, - [TreeNodeType.COLUMN]: { - icon: '\ue611', - }, - [TreeNodeType.KEYS]: { - icon: '\ueabe', - unfoldIcon: '\ueabf', - }, - [TreeNodeType.KEY]: { - icon: '\ue775', - }, - [TreeNodeType.INDEXES]: { - icon: '\ueabe', - unfoldIcon: '\ueabf', - }, - [TreeNodeType.INDEX]: { - icon: '\ue65b', - }, - [TreeNodeType.VIEW]: { - icon: '\ue70c', - }, - [TreeNodeType.FUNCTION]: { - icon: '\ue76a', - }, - [TreeNodeType.PROCEDURE]: { - icon: '\ue73c', - }, - [TreeNodeType.TRIGGER]: { - icon: '\ue64a', - }, - [TreeNodeType.VIEWCOLUMNS]: { - icon: '\ueabe', - unfoldIcon: '\ueabf', - }, - [TreeNodeType.VIEWCOLUMN]: { - icon: '\ue647', - }, - [TreeNodeType.FUNCTIONS]: { - icon: '\ueabe', - unfoldIcon: '\ueabf', - }, - [TreeNodeType.PROCEDURES]: { - icon: '\ueabe', - unfoldIcon: '\ueabf', - }, - [TreeNodeType.TRIGGERS]: { - icon: '\ueabe', - unfoldIcon: '\ueabf', - }, - [TreeNodeType.VIEWS]: { - icon: '\ueabe', - unfoldIcon: '\ueabf', - }, -}; - -export interface ITreeConfigItem { - icon?: string; - getChildren?: (params: any, options?: any) => Promise; - next?: TreeNodeType; - operationColumn?: OperationColumn[]; -} - -export const treeConfig: { [key in TreeNodeType]: ITreeConfigItem } = { - [TreeNodeType.DATA_SOURCES]: { - getChildren: () => { - return new Promise((r: (value: ITreeNode[]) => void, j) => { - const p = { - pageNo: 1, - pageSize: 1000, - }; - connectionService - .getList(p) - .then((res) => { - const data: ITreeNode[] = res.data.map((t: IConnectionDetails) => { - return { - uuid: uuid(), - key: t.id, - name: t.alias, - treeNodeType: TreeNodeType.DATA_SOURCE, - extraParams: { - databaseType: t.type, - dataSourceId: t.id, - dataSourceName: t.alias, - }, - }; - }); - r(data); - }) - .catch(() => { - j(); - }); - }); - }, - }, - - [TreeNodeType.DATA_SOURCE]: { - getChildren: (params: { dataSourceId: number; dataSourceName: string; extraParams: any }) => { - return new Promise((r, j) => { - const _extraParams = params.extraParams; - delete params.extraParams; - connectionService - .getDatabaseList(params) - .then((res) => { - const data: ITreeNode[] = res.map((t: any) => { - return { - uuid: uuid(), - key: t.name, - name: t.name, - treeNodeType: TreeNodeType.DATABASE, - extraParams: { - ..._extraParams, - databaseName: t.name, - }, - }; - }); - r(data); - }) - .catch(() => { - j(); - }); - }); - }, - operationColumn: [OperationColumn.EditSource, OperationColumn.Refresh, OperationColumn.ShiftOut], - next: TreeNodeType.DATABASE, - }, - - [TreeNodeType.DATABASE]: { - icon: '\ue62c', - getChildren: (params) => { - const _extraParams = params.extraParams; - delete params.extraParams; - return new Promise((r: (value: ITreeNode[], b?: any) => void, j) => { - connectionService - .getSchemaList(params) - .then((res) => { - const data: ITreeNode[] = res.map((t: any) => { - return { - uuid: uuid(), - key: t.name, - name: t.name, - treeNodeType: TreeNodeType.SCHEMAS, - schemaName: t.name, - extraParams: { - ..._extraParams, - schemaName: t.name, - }, - }; - }); - r(data); - }) - .catch(() => { - j(); - }); - }); - }, - operationColumn: [ - OperationColumn.CreateConsole, - OperationColumn.CreateSchema, - // OperationColumn.CreateTable, - OperationColumn.CopyName, - OperationColumn.Refresh, - ], - next: TreeNodeType.SCHEMAS, - }, - - [TreeNodeType.SCHEMAS]: { - icon: '\ue696', - getChildren: (parentData: ITreeNode) => { - const { dataSourceId, databaseName, schemaName } = parentData.extraParams!; - const preCode = [dataSourceId, databaseName, schemaName].join('-'); - return new Promise((r: (value: ITreeNode[]) => void) => { - const data = [ - { - uuid: uuid(), - key: `${preCode}-tables`, - name: 'tables', - treeNodeType: TreeNodeType.TABLES, - extraParams: parentData.extraParams, - }, - { - uuid: uuid(), - key: `${preCode}-views`, - name: 'view', - treeNodeType: TreeNodeType.VIEWS, - extraParams: parentData.extraParams, - }, - { - uuid: uuid(), - key: `${preCode}-functions`, - name: 'functions', - treeNodeType: TreeNodeType.FUNCTIONS, - extraParams: parentData.extraParams, - }, - { - uuid: uuid(), - key: `${preCode}-procedures`, - name: 'procedures', - treeNodeType: TreeNodeType.PROCEDURES, - extraParams: parentData.extraParams, - }, - { - uuid: uuid(), - key: `${preCode}-triggers`, - name: 'triggers', - treeNodeType: TreeNodeType.TRIGGERS, - extraParams: parentData.extraParams, - }, - ]; - r(data); - }); - }, - operationColumn: [OperationColumn.CreateConsole, OperationColumn.Refresh], - }, - - [TreeNodeType.TABLES]: { - icon: '\ueac5', - getChildren: (params, options) => { - const _extraParams = params.extraParams; - delete params.extraParams; - params.pageSize = 1000; - return new Promise((r, j) => { - mysqlServer - .getTableList(params, options) - .then((res) => { - const tableList: ITreeNode[] = res.data?.map((t: any) => { - return { - uuid: uuid(), - name: t.name, - treeNodeType: TreeNodeType.TABLE, - key: t.name, - pinned: t.pinned, - comment: t.comment, - extraParams: { - ..._extraParams, - tableName: t.name, - }, - }; - }); - r({ - data: tableList, - pageNo: res.pageNo, - pageSize: res.pageSize, - total: res.total, - hasNextPage: res.hasNextPage, - } as any); - }) - .catch((error) => { - j(error); - }); - }); - }, - operationColumn: [ - OperationColumn.CreateConsole, - OperationColumn.ViewAllTable, - OperationColumn.CreateTable, - OperationColumn.Refresh, - ], - }, - - [TreeNodeType.TABLE]: { - icon: '\ue63e', - getChildren: (params) => { - return new Promise((r: (value: ITreeNode[]) => void) => { - const { dataSourceId, databaseName, schemaName, tableName } = params.extraParams!; - const preCode = [dataSourceId, databaseName, schemaName, tableName].join('-'); - const list = [ - { - uuid: uuid(), - key: `${preCode}-columns`, - name: 'columns', - treeNodeType: TreeNodeType.COLUMNS, - extraParams: params.extraParams, - }, - { - uuid: uuid(), - key: `${preCode}-keys`, - name: 'keys', - treeNodeType: TreeNodeType.KEYS, - extraParams: params.extraParams, - }, - { - uuid: uuid(), - key: `${preCode}-indexs`, - name: 'indexs', - treeNodeType: TreeNodeType.INDEXES, - extraParams: params.extraParams, - }, - ]; - - r(list); - }); - }, - operationColumn: [ - OperationColumn.OpenTable, - OperationColumn.CreateConsole, - OperationColumn.Pin, - OperationColumn.ViewDDL, - OperationColumn.EditTable, - OperationColumn.CopyName, - OperationColumn.Refresh, - OperationColumn.DeleteTable, - ], - }, - - [TreeNodeType.VIEWS]: { - icon: '\ue70c', - getChildren: (params) => { - const _extraParams = params.extraParams; - delete params.extraParams; - return new Promise((r: (value: ITreeNode[]) => void, j) => { - mysqlServer - .getViewList(params) - .then((res) => { - const viewList: ITreeNode[] = res.data?.map((t: any) => { - return { - uuid: uuid(), - name: t.name, - treeNodeType: TreeNodeType.VIEW, - key: t.name, - pinned: t.pinned, - comment: t.comment, - extraParams: { - ..._extraParams, - tableName: t.name, - }, - }; - }); - r(viewList); - }) - .catch((error) => { - j(error); - }); - }); - }, - operationColumn: [OperationColumn.CreateConsole, OperationColumn.Refresh], - }, - - [TreeNodeType.FUNCTIONS]: { - icon: '\ue76a', - getChildren: (params) => { - const _extraParams = params.extraParams; - delete params.extraParams; - return new Promise((r: (value: ITreeNode[]) => void, j) => { - mysqlServer - .getFunctionList(params) - .then((res) => { - const list: ITreeNode[] = res.data?.map((t: any) => { - return { - uuid: uuid(), - name: t.functionName, - treeNodeType: TreeNodeType.FUNCTION, - key: t.name, - pinned: t.pinned, - comment: t.comment, - isLeaf: true, - extraParams: { - ..._extraParams, - functionName: t.functionName, - }, - }; - }); - r(list); - }) - .catch((error) => { - j(error); - }); - }); - }, - operationColumn: [OperationColumn.CreateConsole, OperationColumn.Refresh], - }, - - [TreeNodeType.FUNCTION]: { - icon: '\ue76a', - operationColumn: [OperationColumn.CreateConsole, OperationColumn.OpenFunction, OperationColumn.CopyName], - }, - - [TreeNodeType.PROCEDURES]: { - icon: '\ue73c', - getChildren: (params) => { - const _extraParams = params.extraParams; - delete params.extraParams; - return new Promise((r: (value: ITreeNode[]) => void, j) => { - mysqlServer - .getProcedureList(params) - .then((res) => { - const list: ITreeNode[] = res.data?.map((t: any) => { - return { - uuid: uuid(), - name: t.procedureName, - treeNodeType: TreeNodeType.PROCEDURE, - key: t.name, - pinned: t.pinned, - comment: t.comment, - isLeaf: true, - extraParams: { - ..._extraParams, - procedureName: t.procedureName, - }, - }; - }); - r(list); - }) - .catch((error) => { - j(error); - }); - }); - }, - operationColumn: [OperationColumn.CreateConsole, OperationColumn.Refresh], - }, - - [TreeNodeType.PROCEDURE]: { - icon: '\ue73c', - operationColumn: [OperationColumn.CreateConsole, OperationColumn.OpenProcedure, OperationColumn.CopyName], - }, - - [TreeNodeType.TRIGGERS]: { - icon: '\ue64a', - getChildren: (params) => { - const _extraParams = params.extraParams; - delete params.extraParams; - return new Promise((r: (value: ITreeNode[]) => void, j) => { - mysqlServer - .getTriggerList(params) - .then((res) => { - const list: ITreeNode[] = res.data?.map((t: any) => { - return { - uuid: uuid(), - name: t.triggerName, - treeNodeType: TreeNodeType.TRIGGER, - key: t.name, - pinned: t.pinned, - comment: t.comment, - isLeaf: true, - extraParams: { - ..._extraParams, - triggerName: t.triggerName, - }, - }; - }); - r(list); - }) - .catch((error) => { - j(error); - }); - }); - }, - operationColumn: [OperationColumn.CreateConsole, OperationColumn.Refresh], - }, - - [TreeNodeType.TRIGGER]: { - icon: '\ue64a', - operationColumn: [OperationColumn.CreateConsole, OperationColumn.OpenTrigger, OperationColumn.CopyName], - }, - - [TreeNodeType.VIEW]: { - icon: '\ue70c', - getChildren: (params) => { - return new Promise((r: (value: ITreeNode[]) => void) => { - const list = [ - { - uuid: uuid(), - name: 'columns', - treeNodeType: TreeNodeType.COLUMNS, - key: 'columns', - extraParams: params.extraParams, - }, - ]; - r(list); - }); - }, - operationColumn: [OperationColumn.CreateConsole, OperationColumn.OpenView, OperationColumn.CopyName], - }, - - [TreeNodeType.VIEWCOLUMNS]: { - icon: '\ue647', - getChildren: (params) => { - const _extraParams = params.extraParams; - delete params.extraParams; - return new Promise((r: (value: ITreeNode[]) => void, j) => { - mysqlServer - .getViewColumnList(params) - .then((res) => { - const list: ITreeNode[] = res.data?.map((t: any) => { - return { - uuid: uuid(), - name: t.name, - treeNodeType: TreeNodeType.VIEWCOLUMN, - key: t.name, - pinned: t.pinned, - comment: t.comment, - isLeaf: true, - extraParams: _extraParams, - }; - }); - r(list); - }) - .catch((error) => { - j(error); - }); - }); - }, - operationColumn: [OperationColumn.CreateConsole, OperationColumn.CopyName, OperationColumn.Refresh], - }, - - [TreeNodeType.VIEWCOLUMN]: { - icon: '\ue647', - operationColumn: [OperationColumn.CreateConsole, OperationColumn.CopyName], - }, - - [TreeNodeType.COLUMNS]: { - icon: '\ueac5', - getChildren: (params) => { - const _extraParams = params.extraParams; - delete params.extraParams; - return new Promise((r: (value: ITreeNode[]) => void, j) => { - mysqlServer - .getColumnList(params) - .then((res) => { - const tableList: ITreeNode[] = res?.map((item) => { - return { - uuid: uuid(), - name: item.name, - treeNodeType: TreeNodeType.COLUMN, - key: item.name, - isLeaf: true, - columnType: item.columnType, - comment: item.comment, - extraParams: _extraParams, - }; - }); - r(tableList); - }) - .catch(() => { - j(); - }); - }); - }, - operationColumn: [OperationColumn.CreateConsole, OperationColumn.Refresh], - }, - [TreeNodeType.COLUMN]: { - icon: '\ue611', - operationColumn: [OperationColumn.CreateConsole, OperationColumn.CopyName], - }, - [TreeNodeType.KEYS]: { - icon: '\ueac5', - getChildren: (params) => { - const _extraParams = params.extraParams; - delete params.extraParams; - return new Promise((r: (value: ITreeNode[]) => void, j) => { - mysqlServer - .getKeyList(params) - .then((res) => { - const tableList: ITreeNode[] = res?.map((item) => { - return { - uuid: uuid(), - name: item.name, - treeNodeType: TreeNodeType.KEY, - key: item.name, - isLeaf: true, - extraParams: _extraParams, - }; - }); - r(tableList); - }) - .catch(() => { - j(); - }); - }); - }, - operationColumn: [OperationColumn.CreateConsole, OperationColumn.CopyName, OperationColumn.Refresh], - }, - [TreeNodeType.KEY]: { - icon: '\ue775', - operationColumn: [OperationColumn.CreateConsole, OperationColumn.CopyName], - }, - [TreeNodeType.INDEXES]: { - icon: '\ueac5', - getChildren: (params) => { - const _extraParams = params.extraParams; - delete params.extraParams; - return new Promise((r: (value: ITreeNode[]) => void, j) => { - mysqlServer - .getIndexList(params) - .then((res) => { - const tableList: ITreeNode[] = res?.map((item) => { - return { - uuid: uuid(), - name: item.name, - treeNodeType: TreeNodeType.INDEX, - key: item.name, - isLeaf: true, - extraParams: _extraParams, - }; - }); - r(tableList); - }) - .catch(() => { - j(); - }); - }); - }, - operationColumn: [OperationColumn.CreateConsole, OperationColumn.CopyName, OperationColumn.Refresh], - }, - [TreeNodeType.INDEX]: { - icon: '\ue65b', - operationColumn: [OperationColumn.CreateConsole, OperationColumn.CopyName], - }, -}; diff --git a/chat2db-client/src/blocks/NewTree/treeStore.ts b/chat2db-client/src/blocks/NewTree/treeStore.ts deleted file mode 100644 index 276b524a3..000000000 --- a/chat2db-client/src/blocks/NewTree/treeStore.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 树的store - */ -import { create, UseBoundStore, StoreApi } from 'zustand'; -import { devtools } from 'zustand/middleware'; - -export interface ITreeStore { - focusId: number | string | null; -} - -const treeStore = { - focusId: null, -} - -export const useTreeStore: UseBoundStore> = create( - devtools(() => (treeStore)), -); - -export const setFocusId = (focusId: ITreeStore['focusId']) => { - useTreeStore.setState({ focusId }); -} diff --git a/chat2db-client/src/blocks/Tree/functions/deleteTable.tsx b/chat2db-client/src/blocks/Tree/functions/deleteTable.tsx index d55d1507c..11b1b37f6 100644 --- a/chat2db-client/src/blocks/Tree/functions/deleteTable.tsx +++ b/chat2db-client/src/blocks/Tree/functions/deleteTable.tsx @@ -6,15 +6,15 @@ import { openModal } from '@/store/common/components'; import styles from './deleteTable.less'; import i18n from '@/i18n'; -export const deleteTable = (treeNodeData) => { +export const deleteTable = (treeNodeData,loadData) => { openModal({ width: '450px', - content: , + content: , }); }; -export const DeleteModalContent = (params: { treeNodeData: any; openModal: any }) => { - const { treeNodeData } = params; +export const DeleteModalContent = (params: { treeNodeData: any; openModal: any; loadData: any }) => { + const { treeNodeData,loadData } = params; // 禁用确定按钮 const [userChecked, setUserChecked] = useState(false); @@ -26,8 +26,9 @@ export const DeleteModalContent = (params: { treeNodeData: any; openModal: any } tableName: treeNodeData.name, }; mysqlService.deleteTable(p).then(() => { - treeNodeData.parentNode.loadData({ + loadData({ refresh: true, + treeNodeData: treeNodeData.parentNode }); openModal(false); }); diff --git a/chat2db-client/src/blocks/Tree/functions/openAsyncSql.ts b/chat2db-client/src/blocks/Tree/functions/openAsyncSql.ts index 4cc51c6f6..98cf337ef 100644 --- a/chat2db-client/src/blocks/Tree/functions/openAsyncSql.ts +++ b/chat2db-client/src/blocks/Tree/functions/openAsyncSql.ts @@ -1,140 +1,130 @@ -import { OperationColumn, WorkspaceTabType } from '@/constants'; +import { WorkspaceTabType } from '@/constants'; import sqlService from '@/service/sql'; +import {createConsole} from '@/pages/main/workspace/store/console' export const openView = (props:{ addWorkspaceTab: any; treeNodeData: any; }) => { - const {addWorkspaceTab,treeNodeData} = props; - addWorkspaceTab({ - id: `${OperationColumn.OpenView}-${treeNodeData.uuid}` , - title: treeNodeData.name, - type: WorkspaceTabType.VIEW, - uniqueData: { - dataSourceId: treeNodeData.extraParams!.dataSourceId!, - databaseType: treeNodeData.extraParams!.databaseType!, - databaseName: treeNodeData.extraParams?.databaseName, - schemaName: treeNodeData.extraParams?.schemaName, - loadSQL: ()=>{ - return new Promise((resolve) => { - sqlService - .getViewDetail({ - dataSourceId: treeNodeData.extraParams!.dataSourceId!, - databaseType: treeNodeData.extraParams!.databaseType!, - databaseName: treeNodeData.extraParams!.databaseName!, - schemaName: treeNodeData.extraParams?.schemaName, - tableName: treeNodeData.name - } as any) - .then((res) => { - // 更新ddl - resolve(res.ddl); - }); - }); - } - }, - }); + const { treeNodeData } = props; + createConsole({ + name: treeNodeData.name, + operationType: WorkspaceTabType.VIEW, + dataSourceId: treeNodeData.extraParams!.dataSourceId!, + dataSourceName: treeNodeData.extraParams!.dataSourceName!, + databaseType: treeNodeData.extraParams!.databaseType!, + databaseName: treeNodeData.extraParams?.databaseName, + schemaName: treeNodeData.extraParams?.schemaName, + loadSQL: ()=>{ + return new Promise((resolve) => { + sqlService + .getViewDetail({ + dataSourceId: treeNodeData.extraParams!.dataSourceId!, + databaseType: treeNodeData.extraParams!.databaseType!, + databaseName: treeNodeData.extraParams!.databaseName!, + schemaName: treeNodeData.extraParams?.schemaName, + tableName: treeNodeData.name + } as any) + .then((res) => { + // 更新ddl + resolve(res.ddl); + }); + }); + } + }) } export const openFunction = (props:{ - addWorkspaceTab: any; treeNodeData: any; }) => { - const {addWorkspaceTab,treeNodeData} = props; - addWorkspaceTab({ - id: `${OperationColumn.OpenFunction}-${treeNodeData.uuid}` , - title: treeNodeData.name, - type: WorkspaceTabType.FUNCTION, - uniqueData: { - dataSourceId: treeNodeData.extraParams!.dataSourceId!, - databaseType: treeNodeData.extraParams!.databaseType!, - databaseName: treeNodeData.extraParams?.databaseName, - schemaName: treeNodeData.extraParams?.schemaName, - loadSQL: ()=>{ - return new Promise((resolve) => { - sqlService - .getFunctionDetail({ - dataSourceId: treeNodeData.extraParams!.dataSourceId!, - databaseType: treeNodeData.extraParams!.databaseType!, - databaseName: treeNodeData.extraParams!.databaseName!, - schemaName: treeNodeData.extraParams?.schemaName, - functionName: treeNodeData.name - } as any) - .then((res) => { - // 更新ddl - resolve(res.functionBody); - }); - }); - } - }, - }); + const { treeNodeData } = props; + createConsole({ + name: treeNodeData.name, + operationType: WorkspaceTabType.FUNCTION, + dataSourceId: treeNodeData.extraParams!.dataSourceId!, + dataSourceName: treeNodeData.extraParams!.dataSourceName!, + databaseType: treeNodeData.extraParams!.databaseType!, + databaseName: treeNodeData.extraParams?.databaseName, + schemaName: treeNodeData.extraParams?.schemaName, + loadSQL: ()=>{ + return new Promise((resolve) => { + sqlService + .getFunctionDetail({ + dataSourceId: treeNodeData.extraParams!.dataSourceId!, + databaseType: treeNodeData.extraParams!.databaseType!, + databaseName: treeNodeData.extraParams!.databaseName!, + schemaName: treeNodeData.extraParams?.schemaName, + functionName: treeNodeData.name + } as any) + .then((res) => { + // 更新ddl + resolve(res.functionBody); + }); + }); + } + }) } export const openProcedure = (props:{ - addWorkspaceTab: any; treeNodeData: any; }) => { - const {addWorkspaceTab,treeNodeData} = props; - addWorkspaceTab({ - id: `${OperationColumn.OpenProcedure}-${treeNodeData.uuid}` , - title: treeNodeData.name, - type: WorkspaceTabType.PROCEDURE, - uniqueData: { - dataSourceId: treeNodeData.extraParams!.dataSourceId!, - databaseType: treeNodeData.extraParams!.databaseType!, - databaseName: treeNodeData.extraParams?.databaseName, - schemaName: treeNodeData.extraParams?.schemaName, - loadSQL: ()=>{ - return new Promise((resolve) => { - sqlService - .getProcedureDetail({ - dataSourceId: treeNodeData.extraParams!.dataSourceId!, - databaseType: treeNodeData.extraParams!.databaseType!, - databaseName: treeNodeData.extraParams!.databaseName!, - schemaName: treeNodeData.extraParams?.schemaName, - procedureName: treeNodeData.name - } as any) - .then((res) => { - // 更新ddl - resolve(res.procedureBody); - }); - }); - } - }, - }); + const { treeNodeData } = props; + createConsole({ + name: treeNodeData.name, + operationType: WorkspaceTabType.PROCEDURE, + dataSourceId: treeNodeData.extraParams!.dataSourceId!, + dataSourceName: treeNodeData.extraParams!.dataSourceName!, + databaseType: treeNodeData.extraParams!.databaseType!, + databaseName: treeNodeData.extraParams?.databaseName, + schemaName: treeNodeData.extraParams?.schemaName, + loadSQL: ()=>{ + return new Promise((resolve) => { + sqlService + .getProcedureDetail({ + dataSourceId: treeNodeData.extraParams!.dataSourceId!, + databaseType: treeNodeData.extraParams!.databaseType!, + databaseName: treeNodeData.extraParams!.databaseName!, + schemaName: treeNodeData.extraParams?.schemaName, + procedureName: treeNodeData.name + } as any) + .then((res) => { + // 更新ddl + resolve(res.procedureBody); + }); + }); + } + }) } export const openTrigger = (props:{ - addWorkspaceTab: any; treeNodeData: any; }) => { - const {addWorkspaceTab,treeNodeData} = props; - addWorkspaceTab({ - id: `${OperationColumn.OpenTrigger}-${treeNodeData.uuid}` , - title: treeNodeData.name, - type: WorkspaceTabType.TRIGGER, - uniqueData: { - dataSourceId: treeNodeData.extraParams!.dataSourceId!, - databaseType: treeNodeData.extraParams!.databaseType!, - databaseName: treeNodeData.extraParams?.databaseName, - schemaName: treeNodeData.extraParams?.schemaName, - loadSQL: ()=>{ - return new Promise((resolve) => { - sqlService - .getTriggerDetail({ - dataSourceId: treeNodeData.extraParams!.dataSourceId!, - databaseType: treeNodeData.extraParams!.databaseType!, - databaseName: treeNodeData.extraParams!.databaseName!, - schemaName: treeNodeData.extraParams?.schemaName, - triggerName: treeNodeData.name - } as any) - .then((res) => { - // 更新ddl - resolve(res.triggerBody); - }); - }); - } - }, - }); + const {treeNodeData } = props; + createConsole({ + name: treeNodeData.name, + operationType: WorkspaceTabType.TRIGGER, + dataSourceId: treeNodeData.extraParams!.dataSourceId!, + dataSourceName: treeNodeData.extraParams!.dataSourceName!, + databaseType: treeNodeData.extraParams!.databaseType!, + databaseName: treeNodeData.extraParams?.databaseName, + schemaName: treeNodeData.extraParams?.schemaName, + loadSQL: ()=>{ + return new Promise((resolve) => { + sqlService + .getTriggerDetail({ + dataSourceId: treeNodeData.extraParams!.dataSourceId!, + databaseType: treeNodeData.extraParams!.databaseType!, + databaseName: treeNodeData.extraParams!.databaseName!, + schemaName: treeNodeData.extraParams?.schemaName, + triggerName: treeNodeData.name + } as any) + .then((res) => { + // 更新ddl + resolve(res.triggerBody); + }); + }); + } + }) } diff --git a/chat2db-client/src/blocks/Tree/hooks/useGetRightClickMenu.ts b/chat2db-client/src/blocks/Tree/hooks/useGetRightClickMenu.ts index 906d1f69b..9c7112ec0 100644 --- a/chat2db-client/src/blocks/Tree/hooks/useGetRightClickMenu.ts +++ b/chat2db-client/src/blocks/Tree/hooks/useGetRightClickMenu.ts @@ -110,7 +110,7 @@ export const useGetRightClickMenu = (props: IProps) => { text: i18n('common.button.refresh'), icon: '\uec08', handle: () => { - treeNodeData.loadData?.({ + loadData?.({ refresh: true, }); }, @@ -166,7 +166,7 @@ export const useGetRightClickMenu = (props: IProps) => { databaseType: treeNodeData.extraParams!.databaseType!, databaseName: treeNodeData.extraParams?.databaseName, schemaName: treeNodeData.extraParams?.schemaName, - submitCallback: () => {treeNodeData.loadData?.({refresh: true})}, + submitCallback: () => {loadData?.({refresh: true})}, }, }); }, @@ -178,7 +178,7 @@ export const useGetRightClickMenu = (props: IProps) => { text: i18n('workspace.menu.deleteTable'), icon: '\ue6a7', handle: () => { - deleteTable(treeNodeData); + deleteTable(treeNodeData,loadData); }, }, @@ -196,7 +196,12 @@ export const useGetRightClickMenu = (props: IProps) => { text: treeNodeData.pinned ? i18n('workspace.menu.unPin') : i18n('workspace.menu.pin'), icon: treeNodeData.pinned ? '\ue61d' : '\ue627', handle: () => { - handelPinTable({treeNodeData, loadData: treeNodeData.parentNode!.loadData!}); + handelPinTable({ + treeNodeData, + loadData: () => { + loadData({treeNodeData:treeNodeData.parentNode}) + } + }); }, }, @@ -215,7 +220,13 @@ export const useGetRightClickMenu = (props: IProps) => { databaseName: treeNodeData.extraParams?.databaseName, schemaName: treeNodeData.extraParams?.schemaName, tableName: treeNodeData?.name, - submitCallback: () => {treeNodeData.parentNode?.loadData?.({refresh: true})}, + submitCallback: () => { + + loadData({ + treeNodeData: treeNodeData.parentNode, + refresh: true + }) + }, }, }); }, @@ -405,7 +416,7 @@ export const getRightClickMenu = (props: IProps) => { text: i18n('common.button.refresh'), icon: '\uec08', handle: () => { - treeNodeData.loadData?.({ + loadData?.({ refresh: true, }); }, diff --git a/chat2db-client/src/blocks/Tree/index.less b/chat2db-client/src/blocks/Tree/index.less index 59b74c34a..6f85f5ec2 100644 --- a/chat2db-client/src/blocks/Tree/index.less +++ b/chat2db-client/src/blocks/Tree/index.less @@ -1,19 +1,28 @@ @import '../../styles/var.less'; -.treeBox { +.scrollBox { + overflow-y: auto; padding: 2px 6px 10px; box-sizing: border-box; + height: 100%; +} + +.treeBox { overflow: hidden; - overflow-y: auto; height: 100%; } +.treeListHolder { + height: calc(var(--tree-node-count) * 26px); + min-height: 100%; +} + // 如果treeBox滚动的高度>0那么久加一个上边框 .treeBoxScroll { border-top: 1px solid var(--color-border-secondary); } -.leftModuleTitleShadow{ +.leftModuleTitleShadow { border-top: 1px solid red; } diff --git a/chat2db-client/src/blocks/Tree/index.tsx b/chat2db-client/src/blocks/Tree/index.tsx index 4856a835b..ff76ba24d 100644 --- a/chat2db-client/src/blocks/Tree/index.tsx +++ b/chat2db-client/src/blocks/Tree/index.tsx @@ -1,4 +1,4 @@ -import React, { memo, useEffect, useMemo, useState, forwardRef, createContext, useContext, useCallback } from 'react'; +import React, { memo, useEffect, useMemo, useState, createContext, useContext, useRef } from 'react'; import styles from './index.less'; import classnames from 'classnames'; import Iconfont from '@/components/Iconfont'; @@ -12,112 +12,164 @@ import { setFocusId, useTreeStore } from './treeStore'; import { useGetRightClickMenu } from './hooks/useGetRightClickMenu'; import MenuLabel from '@/components/MenuLabel'; import LoadingContent from '@/components/Loading/LoadingContent'; +import { cloneDeep } from 'lodash'; +// import { flushSync } from 'react-dom'; interface IProps { className?: string; treeData: ITreeNode[] | null; searchValue: string; } + interface TreeNodeIProps { data: ITreeNode; level: number; - setShowParentNode?: (value: boolean) => void; } interface IContext { - searchValue?: string; + treeData: ITreeNode[]; + setTreeData: (value: ITreeNode[] | null) => void; } export const Context = createContext({} as any); +// 树转平级 +const smoothTree = (treeData: ITreeNode[], result: ITreeNode[] = [], parentNode?: ITreeNode) => { + treeData.forEach((item) => { + if (parentNode) { + item.parentNode = parentNode; + item.level = (parentNode.level || 0) + 1; + } + result.push(item); + if (item.children) { + smoothTree(item.children, result, item); + } + }); + return result; +}; + +const itemHeight = 26; // 每个 item 的高度 +const paddingCount = 2; + const Tree = (props: IProps) => { const { className, treeData: outerTreeData, searchValue } = props; const [treeData, setTreeData] = useState(null); + const [smoothTreeData, setSmoothTreeData] = useState([]); + const [searchTreeData, setSearchTreeData] = useState(null); // 搜索结果 + const [scrollTop, setScrollTop] = useState(0); // 滚动位置 // 继续需要渲染的 item 索引有哪些 + + const startIdx = useMemo(() => { + let _startIdx = Math.floor(scrollTop / itemHeight); + _startIdx = Math.max(_startIdx - paddingCount, 0); // 处理越界情况 + return _startIdx; + }, [scrollTop]); + + const top = itemHeight * startIdx; // 第一个渲染的 item 到顶部距离 useEffect(() => { setTreeData(outerTreeData); + setScrollTop(0); }, [outerTreeData]); + useEffect(() => { + if (treeData) { + const result: ITreeNode[] = []; + smoothTree(treeData, result); + setSmoothTreeData(result); + } else { + setSmoothTreeData([]); + } + }, [treeData]); + const treeNodes = useMemo(() => { - return treeData?.map((item, index) => { - return ; + const realNodeList = (searchTreeData || smoothTreeData).slice(startIdx, startIdx + 50); + return realNodeList.map((item) => { + return ; }); - }, [treeData]); - // 如果treeBox滚动的高度>0那么久加一个上边框 - const [treeBoxScrollTop, setTreeBoxScrollTop] = useState(0); - const handleScroll = (e: any) => { - setTreeBoxScrollTop(e.target.scrollTop); - }; + }, [smoothTreeData, searchTreeData, startIdx]); + + useEffect(() => { + if (searchValue) { + const ls = smoothTreeData.filter((item) => { + const reg = new RegExp(searchValue, 'i'); + return reg.test(item.name || ''); + }); + setSearchTreeData(ls); + } else { + setSearchTreeData(null); + } + setScrollTop(0); + }, [searchValue]); return ( - - + +
0 })} - onScroll={handleScroll} + className={classnames(styles.scrollBox)} + onScroll={(e: any) => { + console.log(e.target.scrollTop); + setScrollTop(e.target.scrollTop); + }} > - {treeNodes} +
+
+ {treeNodes} +
- - + + ); }; const TreeNode = memo((props: TreeNodeIProps) => { - const { data: initData, level, setShowParentNode: _setShowParentNode } = props; + const { data: treeNodeData, level } = props; const [isLoading, setIsLoading] = useState(false); const indentArr = new Array(level).fill('indent'); - const { searchValue } = useContext(Context); + const { treeData, setTreeData } = useContext(Context); // 加载数据 - function loadData(_props?: { refresh: boolean }) { - const treeNodeConfig: ITreeConfigItem = treeConfig[treeNodeData.pretendNodeType || treeNodeData.treeNodeType]; + function loadData(_props?: { refresh: boolean; pageNo: number; treeNodeData?: ITreeNode }) { + const _treeNodeData = _props?.treeNodeData || props.data; + const treeNodeConfig: ITreeConfigItem = treeConfig[_treeNodeData.pretendNodeType || _treeNodeData.treeNodeType]; setIsLoading(true); - setTreeNodeData({ - ...treeNodeData, - children: null, - }); + if (_props?.pageNo === 1 || !_props?.pageNo) { + insertData(treeData!, _treeNodeData.uuid!, null); + } + treeNodeConfig .getChildren?.({ - ...treeNodeData.extraParams, + ..._treeNodeData.extraParams, extraParams: { - ...treeNodeData.extraParams, + ..._treeNodeData.extraParams, }, refresh: _props?.refresh || false, + pageNo: _props?.pageNo || 1, }) .then((res: any) => { if (res.length || res.data) { - setTimeout(() => { - if (res.data) { - // res.data每次只插入50条数据,间隔50ms - const count = res.data.length / 50; - for (let i = 0; i <= count; i++) { - setTimeout(() => { - setTreeNodeData({ - ...treeNodeData, - children: res.data.slice(0, (i + 1) * 50), - total: res.total, - }); - }, 100 * i); - } - } else { - setTreeNodeData({ - ...treeNodeData, - children: res, + if (res.data) { + insertData(treeData!, _treeNodeData.uuid!, res.data); + // TODO: + if (res.hasNextPage) { + loadData({ + refresh: _props?.refresh || false, + pageNo: res.pageNo + 1, }); } - setIsLoading(false); - }, 200); + } else { + insertData(treeData!, _treeNodeData.uuid!, res); + } + setIsLoading(false); } else { // 处理树可能出现不连续的情况 if (treeNodeConfig.next) { - treeNodeData.pretendNodeType = treeNodeConfig.next; + _treeNodeData.pretendNodeType = treeNodeConfig.next; loadData(); } else { - setTreeNodeData({ - ...treeNodeData, - children: [], - }); + insertData(treeData!, _treeNodeData.uuid!, []); setIsLoading(false); } } @@ -127,46 +179,42 @@ const TreeNode = memo((props: TreeNodeIProps) => { }); } - // 当前节点数据 - const [treeNodeData, setTreeNodeData] = useState({ - ...initData, - loadData, - }); - // 当前节点是否是focus const isFocus = useTreeStore((state) => state.focusId) === treeNodeData.uuid; - const [showTreeNode, setShowTreeNode] = useState(true); - // 如果子节点是展开的,那么父节点也要展示 - const [showParentNode, setShowParentNode] = useState(false); - - const handelSetShowParentNode = useCallback(() => { - setShowParentNode(true); - }, []); - - useEffect(() => { - if (searchValue) { - const reg = new RegExp(searchValue, 'i'); - const res = reg.test(treeNodeData.name || ''); - setShowTreeNode(res); - _setShowParentNode?.(res); - } else { - setShowTreeNode(true); + // 在treeData中找到对应的节点,插入数据 + const insertData = (_treeData: ITreeNode[], uuid: string, data: any): ITreeNode | null => { + let result: ITreeNode | null = null; + for (let i = 0; i < _treeData?.length; i++) { + if (_treeData[i].uuid === uuid) { + result = _treeData[i]; + if (data) { + result.children = [...(result.children || []), ...(data || [])]; + } else { + result.children = null; + } + result.expanded = !!data; + // 这里没写错 就是要改变treeData的引用 + setTreeData?.(cloneDeep(treeData || [])); + break; + } else { + if (_treeData[i].children) { + result = insertData(_treeData[i].children!, uuid, data); + if (result) { + break; + } + } + } } - }, [searchValue]); + return result; + }; //展开-收起 const handleClick = () => { - if ( - treeConfig[treeNodeData.treeNodeType] && - (treeNodeData.children === null || treeNodeData.children === undefined) - ) { - loadData(); + if (treeNodeData.expanded) { + insertData(treeData!, treeNodeData.uuid!, null); } else { - setTreeNodeData({ - ...treeNodeData, - children: null, - }); + loadData(); } }; @@ -204,23 +252,6 @@ const TreeNode = memo((props: TreeNodeIProps) => { } }; - // 递归渲染 - const treeNodes = useMemo(() => { - return treeNodeData.children?.map((item: any, index: number) => { - return ( - - ); - }); - }, [treeNodeData]); - const rightClickMenu = useGetRightClickMenu({ treeNodeData, loadData, @@ -297,14 +328,7 @@ const TreeNode = memo((props: TreeNodeIProps) => { ); }, [isFocus, isLoading, rightClickMenu]); - console.log(showTreeNode, showParentNode, treeNodeData.name); - - return ( -
- {(showTreeNode || showParentNode) && treeNodeDom} - {treeNodes} -
- ); + return treeNodeDom; }); -export default memo(forwardRef(Tree)); +export default memo(Tree); diff --git a/chat2db-client/src/blocks/Tree/treeConfig.tsx b/chat2db-client/src/blocks/Tree/treeConfig.tsx index 2464449a2..07e908593 100644 --- a/chat2db-client/src/blocks/Tree/treeConfig.tsx +++ b/chat2db-client/src/blocks/Tree/treeConfig.tsx @@ -243,7 +243,6 @@ export const treeConfig: { [key in TreeNodeType]: ITreeConfigItem } = { getChildren: (params, options) => { const _extraParams = params.extraParams; delete params.extraParams; - params.pageNo = 1; params.pageSize = 1000; return new Promise((r, j) => { mysqlServer @@ -268,6 +267,7 @@ export const treeConfig: { [key in TreeNodeType]: ITreeConfigItem } = { pageNo: res.pageNo, pageSize: res.pageSize, total: res.total, + hasNextPage: res.hasNextPage, } as any); }) .catch((error) => { diff --git a/chat2db-client/src/components/Modal/BaseModal/index.tsx b/chat2db-client/src/components/Modal/BaseModal/index.tsx index ad3a82a54..351920654 100644 --- a/chat2db-client/src/components/Modal/BaseModal/index.tsx +++ b/chat2db-client/src/components/Modal/BaseModal/index.tsx @@ -37,7 +37,9 @@ const Modal = memo(() => { onOk: modalData.onOk, }; } else { - return {}; + return { + footer: false + }; } }, [modalData]); diff --git a/chat2db-client/src/pages/main/workspace/components/TableList/index.tsx b/chat2db-client/src/pages/main/workspace/components/TableList/index.tsx index da0457e33..4dfd6c2ff 100644 --- a/chat2db-client/src/pages/main/workspace/components/TableList/index.tsx +++ b/chat2db-client/src/pages/main/workspace/components/TableList/index.tsx @@ -7,8 +7,7 @@ import { useWorkspaceStore } from '@/pages/main/workspace/store'; // ----- components ----- import OperationLine from '../OperationLine'; -// import Tree from '@/blocks/Tree'; -import NewTree from '@/blocks/NewTree'; +import Tree from '@/blocks/Tree'; import { treeConfig } from '@/blocks/Tree/treeConfig'; import { ITreeNode } from '@/typings'; @@ -57,7 +56,7 @@ export default memo((props) => {
{/* */} - +
); }); diff --git a/chat2db-client/src/store/setting/index.ts b/chat2db-client/src/store/setting/index.ts index 9a0f0f4ce..af6a36291 100644 --- a/chat2db-client/src/store/setting/index.ts +++ b/chat2db-client/src/store/setting/index.ts @@ -65,7 +65,7 @@ export const updateAiWithWhite = (apiKey: string) => { export const getAiSystemConfig = () => { configService.getAiSystemConfig({}).then((res) => { setAiConfig(res); - if (res.aiSqlSource === AIType.CHAT2DBAI && res.apiKey) { + if (res?.aiSqlSource === AIType.CHAT2DBAI && res.apiKey) { updateAiWithWhite(res.apiKey); } }); From 2cb8a1184200c2fbba6d071978de08d97f11e30f Mon Sep 17 00:00:00 2001 From: shanhexi Date: Wed, 13 Dec 2023 00:05:02 +0800 Subject: [PATCH 2/2] changelog --- CHANGELOG.md | 4 ++-- CHANGELOG_CN.md | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index df2e8cea0..2e7f783a3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,9 +2,9 @@ `2023-12-12` -**更新日志** +**Changelog** -- 🔥🔥【Optimize】The first startup time has been increased by 60%, and the second startup time has been increased by 95% +- 🔥🔥【Optimize】The first startup time has been increased by 65% - 🔥🔥【Optimize】Changed the structure of the left tree - 🔥🔥【Optimize】Optimized the tab switchover problem - ⚡️ 【Optimize】All nodes are supported. The name of each node can be copied diff --git a/CHANGELOG_CN.md b/CHANGELOG_CN.md index 3713151dd..43e74d572 100644 --- a/CHANGELOG_CN.md +++ b/CHANGELOG_CN.md @@ -4,7 +4,7 @@ **更新日志** -- 🔥🔥【优化】首次启动时间时间提升了60%,第二次启动时间提升了95% +- 🔥🔥【优化】首次启动时间时间提升了65% - 🔥🔥【优化】更改了左侧树的结构 - 🔥🔥【优化】优化切换tab卡顿问题 - ⚡️【优化】所有节点都支持选中,可以复制每个节点的名称