From 3cdadc5a0ce99199e8c5e6b9b3d4e266cd9bb28d Mon Sep 17 00:00:00 2001 From: bailongsen1027 <132729499+bailongsen1027@users.noreply.github.com> Date: Sun, 1 Oct 2023 09:43:54 +0800 Subject: [PATCH] [Feature][scaleph-ui-react] upgrade flink sql web online editor (#621) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: 调试请求接口 * fix: 添加执行结果 * fix: 完善执行结果 * fix: 调试执行sql结果 --------- Co-authored-by: bailongsen --- scaleph-ui-react/package.json | 1 + .../src/models/executionResult.ts | 11 + .../Sql/CodeEditor/EditMenu/indexNew.tsx | 198 ---------------- .../Artifact/Sql/CodeEditor/Editor/index.less | 46 ++-- .../Artifact/Sql/CodeEditor/Editor/index.tsx | 36 ++- .../Sql/CodeEditor/EditorRight/index.tsx | 38 ++-- .../EditorRightResultTable.tsx | 211 ++++++++---------- .../CodeEditor/EditorRightResult/index.less | 10 + .../CodeEditor/EditorRightResult/index.tsx | 183 ++++++++++++--- .../Sql/CodeEditor/components/sort.ts | 22 ++ .../Artifact/Sql/CodeEditor/index.tsx | 10 +- .../project/WsFlinkSqlGatewayService.ts | 16 +- 12 files changed, 362 insertions(+), 420 deletions(-) create mode 100644 scaleph-ui-react/src/models/executionResult.ts delete mode 100644 scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditMenu/indexNew.tsx create mode 100644 scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/components/sort.ts diff --git a/scaleph-ui-react/package.json b/scaleph-ui-react/package.json index 77efa47bd..0213df0c2 100644 --- a/scaleph-ui-react/package.json +++ b/scaleph-ui-react/package.json @@ -53,6 +53,7 @@ "@fortawesome/react-fontawesome": "^0.2.0", "@monaco-editor/react": "^4.4.6", "@umijs/route-utils": "^2.0.0", + "ali-react-table": "^2.6.1", "antd": "^4.23.2", "classnames": "^2.3.0", "lodash": "^4.17.0", diff --git a/scaleph-ui-react/src/models/executionResult.ts b/scaleph-ui-react/src/models/executionResult.ts new file mode 100644 index 000000000..2ce56f03e --- /dev/null +++ b/scaleph-ui-react/src/models/executionResult.ts @@ -0,0 +1,11 @@ +import { useState } from 'react'; + +const useGlobalModel = () => { + const [executionData, setExecutionData] = useState(''); + + return { + executionData, + setExecutionData, + }; +}; +export default useGlobalModel; diff --git a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditMenu/indexNew.tsx b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditMenu/indexNew.tsx deleted file mode 100644 index 81ff2f13b..000000000 --- a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditMenu/indexNew.tsx +++ /dev/null @@ -1,198 +0,0 @@ -import React, {useEffect, useState} from "react"; -import {Col, Row, Space, Tag, Tree, TreeDataNode} from "antd"; -import "./index.less"; -import {useLocation} from "umi"; -import {WORKSPACE_CONF} from "@/constant"; -import {WsFlinkKubernetesSessionClusterService} from "@/services/project/WsFlinkKubernetesSessionClusterService"; -import {WsFlinkSqlGatewayService} from "@/services/project/WsFlinkSqlGatewayService"; -import { - BorderlessTableOutlined, - DatabaseOutlined, - FolderOutlined, - FunctionOutlined, - TableOutlined -} from "@ant-design/icons"; - -interface CatalogNode extends TreeDataNode { - name: string, - type?: string; - children: CatalogNode[], - parent?: CatalogNode | null -} - -const EditMenu: React.FC = ({showLeft}) => { - const urlParams = useLocation(); - const [sqlScript, setSqlScript] = useState(''); - - const projectId = localStorage.getItem(WORKSPACE_CONF.projectId); - const [sessionClusterId, setSessionClusterId] = useState(); - const [sqlGatewaySessionHandleId, setSqlGatewaySessionHandleId] = useState(); - const [treeNodes, setTreeNodes] = useState([]) - - useEffect(() => { - const fetchData = async () => { - try { - const resSessionClusterId = await WsFlinkKubernetesSessionClusterService.getSqlGatewaySessionClusterId(projectId); - setSessionClusterId(resSessionClusterId); - - const resSqlGatewaySessionHandleId = await WsFlinkSqlGatewayService.openSession(resSessionClusterId); - setSqlGatewaySessionHandleId(resSqlGatewaySessionHandleId); - - const catalogArray = await WsFlinkSqlGatewayService.leftMenuList(resSessionClusterId, resSqlGatewaySessionHandleId); - console.log(catalogArray,'catalogArray'); - - // setTreeNodes(catalogArray.map(catalog => ({ - // title: catalog, - // key: catalog, - // name: catalog, - // type: 'catalog', - // isLeaf: false, - // children: [], - // parent: null - // }))); - } catch (error) { - // 处理错误 - } - } - - fetchData(); - }, []); - - - - const updateTreeData = (list: CatalogNode[], key: string | number, children: CatalogNode[]): CatalogNode[] => - list.map((node) => { - if (node.key === key) { - return { - ...node, - children, - }; - } - if (node.children) { - return { - ...node, - children: updateTreeData(node.children, key, children), - }; - } - return node; - }); - - const onCatalogLoad = async (catalogNode: CatalogNode) => { - let children: CatalogNode[] = []; - switch (catalogNode.type) { - case 'catalog': - const databases = await WsFlinkSqlGatewayService.listDatabases(sessionClusterId, sqlGatewaySessionHandleId, catalogNode.name); - databases.forEach(database => children.push({ - name: database, - title: database, - key: `${catalogNode.key}-${database}`, - type: 'database', - children: [], - parent: catalogNode, - icon: - })); - break; - case 'database': - children.push( - { - title: 'TABLE', - key: `${catalogNode.key}-TABLE`, - type: 'table', - name: catalogNode.name, - children: [], - parent: catalogNode, - icon: - }, - { - title: 'VIEW', - key: `${catalogNode.key}-VIEW`, - type: 'view', - name: catalogNode.name, - children: [], - parent: catalogNode, - icon: - }, - { - title: 'FUNCTION', - key: `${catalogNode.key}-FUNCTION`, - type: 'function', - name: catalogNode.name, - children: [], - parent: catalogNode, - icon: - } - ); - break; - case 'table': - const tables = await WsFlinkSqlGatewayService.listTables(sessionClusterId, sqlGatewaySessionHandleId, catalogNode.parent?.parent?.name, catalogNode.name); - tables.forEach(table => children.push( - { - title: table, - key: `${catalogNode.key}-${table}`, - type: 'table-object', - name: table, - children: [], - parent: catalogNode, - isLeaf: true, - icon: - } - )); - break; - case 'view': - const views = await WsFlinkSqlGatewayService.listViews(sessionClusterId, sqlGatewaySessionHandleId, catalogNode.parent?.parent?.name, catalogNode.name); - views.forEach(view => children.push( - { - title: view, - key: `${catalogNode.key}-${view}`, - type: 'view-object', - name: view, - children: [], - parent: catalogNode, - isLeaf: true, - icon: - } - )); - break; - case 'function': - const userDefinedFunctions = await WsFlinkSqlGatewayService.listUserDefinedFunctions(sessionClusterId, sqlGatewaySessionHandleId, catalogNode.parent?.parent?.name, catalogNode.name); - userDefinedFunctions.forEach(udf => children.push( - { - title: ( - - {udf.functionName} - {udf.functionKind} - - ), - key: `${catalogNode.key}-${udf.functionName}`, - type: 'function-object', - name: udf.functionName, - children: [], - parent: catalogNode, - isLeaf: true, - icon: - } - )); - break; - default: - break; - } - setTreeNodes(updateTreeData(treeNodes, catalogNode.key, children)); - }; - - return <> - - - - - - ; -}; - -export default EditMenu; diff --git a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/Editor/index.less b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/Editor/index.less index 0219f7706..10e0cd114 100644 --- a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/Editor/index.less +++ b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/Editor/index.less @@ -16,28 +16,28 @@ align-items: center; } - .runButton { - display: flex; - align-items: center; - justify-content: space-between; - width: 70px; - height: 28px; - font-size: 12px; - color: #fff; - font-family:Arial; - border-radius: 5px; - margin-right: 20px; - >img{ - width: 14px; - height: 14px; - } - } - .saveButton { - display: flex; - align-items: center; - justify-content: center; - border-radius: 5px; - width: 70px; - height: 28px; + .runButton { + display: flex; + align-items: center; + justify-content: space-between; + width: 70px; + height: 28px; + font-size: 12px; + color: #fff; + font-family:Arial; + border-radius: 5px; + margin-right: 20px; + >img{ + width: 14px; + height: 14px; } + } + .saveButton { + display: flex; + align-items: center; + justify-content: center; + border-radius: 5px; + width: 70px; + height: 28px; + } } \ No newline at end of file diff --git a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/Editor/index.tsx b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/Editor/index.tsx index 0147bffe7..094555e1a 100644 --- a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/Editor/index.tsx +++ b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/Editor/index.tsx @@ -1,16 +1,16 @@ -import React, { useEffect, useState, useRef } from 'react'; -import { Editor, languages } from '@monaco-editor/react'; +import { Editor } from '@monaco-editor/react'; import { Button, message } from 'antd'; -import { useIntl } from 'umi'; import { language } from 'monaco-editor/esm/vs/basic-languages/sql/sql'; import * as monaco from 'monaco-editor/esm/vs/editor/editor.api'; +import React, { useEffect, useRef, useState } from 'react'; import * as sqlFormatter from 'sql-formatter'; +import { useIntl, useModel } from 'umi'; -import { WORKSPACE_CONF } from "@/constant"; +import { WORKSPACE_CONF } from '@/constant'; import { WsFlinkArtifactSql } from '@/services/project/typings'; -import { WsFlinkKubernetesSessionClusterService } from "@/services/project/WsFlinkKubernetesSessionClusterService"; -import { WsFlinkSqlGatewayService } from "@/services/project/WsFlinkSqlGatewayService"; -import { FlinkArtifactSqlService } from "@/services/project/WsFlinkArtifactSqlService"; +import { FlinkArtifactSqlService } from '@/services/project/WsFlinkArtifactSqlService'; +import { WsFlinkKubernetesSessionClusterService } from '@/services/project/WsFlinkKubernetesSessionClusterService'; +import { WsFlinkSqlGatewayService } from '@/services/project/WsFlinkSqlGatewayService'; import { useLocation } from 'react-router-dom'; import styles from './index.less'; @@ -24,14 +24,19 @@ const CodeEditor: React.FC = () => { const intl = useIntl(); //语言切换 const [sessionClusterId, setSessionClusterId] = useState(); const editorRef = useRef(null); + const { setExecutionData } = useModel('executionResult'); //存储执行结果 useEffect(() => { - setSqlScript(flinkArtifactSql.script); + setSqlScript(flinkArtifactSql?.script); const projectId = localStorage.getItem(WORKSPACE_CONF.projectId); (async () => { - const resSessionClusterId = await WsFlinkKubernetesSessionClusterService.getSqlGatewaySessionClusterId(projectId); + const resSessionClusterId = + await WsFlinkKubernetesSessionClusterService.getSqlGatewaySessionClusterId(projectId); setSessionClusterId(resSessionClusterId); })(); + return () => { + setExecutionData(''); + }; }, []); // 点击运行获取选中或者全部值 @@ -42,9 +47,13 @@ const CodeEditor: React.FC = () => { if (selection && !selection.isEmpty()) { const selectedValue = editorRef.current.getModel()?.getValueInRange(selection); // console.log("选中的值:", selectedValue); - const catalogArray = await WsFlinkSqlGatewayService.executeSqlList(sessionClusterId, { sql: selectedValue || '', configuration: {} }); + const catalogArray = await WsFlinkSqlGatewayService.executeSqlList(sessionClusterId, { + sql: selectedValue || '', + configuration: {}, + }); if (catalogArray) { message.success(`${intl.formatMessage({ id: 'RequestSuccessful' })}`, 1); + setExecutionData(catalogArray); } } else { const fullValue = editorRef.current.getModel()?.getValue(); @@ -54,7 +63,10 @@ const CodeEditor: React.FC = () => { // 保存数据 const onSave = async (): Promise => { - const resultData = await FlinkArtifactSqlService.updateScript({ id: flinkArtifactSql.id, script: sqlScript }); + const resultData = await FlinkArtifactSqlService.updateScript({ + id: flinkArtifactSql.id, + script: sqlScript, + }); if (resultData.success) { message.success(`${intl.formatMessage({ id: 'SaveSuccessful' })}`, 1); } @@ -123,4 +135,4 @@ const CodeEditor: React.FC = () => { ); }; -export default CodeEditor; \ No newline at end of file +export default CodeEditor; diff --git a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorRight/index.tsx b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorRight/index.tsx index 99ddaefd6..7ce7f3f58 100644 --- a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorRight/index.tsx +++ b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorRight/index.tsx @@ -17,26 +17,26 @@ export default function EditorLeft() { }; return ( - // - //
- // - //
- //
- // - //
- //
- + +
+ +
- +
+
+ + //
+ // + //
); } diff --git a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorRightResult/EditorRightResultTable.tsx b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorRightResult/EditorRightResultTable.tsx index c5efc00d7..95ef4906a 100644 --- a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorRightResult/EditorRightResultTable.tsx +++ b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorRightResult/EditorRightResultTable.tsx @@ -1,34 +1,33 @@ +import { compareStrings } from '@/pages/Project/Workspace/Artifact/Sql/CodeEditor/components/sort'; import { Editor } from '@monaco-editor/react'; -import { Button, message, Modal, Table } from 'antd'; -import type { ColumnsType } from 'antd/es/table'; -import React, { useState } from 'react'; +import { ArtColumn, BaseTable, features, useTablePipeline } from 'ali-react-table'; +import { Button, message, Modal } from 'antd'; +import React, { useEffect, useMemo, useState } from 'react'; import styles from './index.less'; -interface DataType { - key: React.Key; - name: string; - age: number; - address: string; -} - interface IViewTableCellData { name: string; value: any; } -const data: DataType[] = []; -for (let i = 0; i < 100; i++) { - data.push({ - key: i, - name: `Edward King ${i}`, - name1: `1${i}`, - age: 32, - address: `London, Park Lane no. ${i}`, - }); -} - -const EditorRightResultTable: React.FC = () => { +const EditorRightResultTable: React.FC = ({ result, lastOneData }: any) => { const [viewTableCellData, setViewTableCellData] = useState(null); + const [headerList, setHeaderList] = useState([]); + const [dataList, setDataList] = useState([]); + + useEffect(() => { + const data = result?.columns?.map((item: any) => ({ + dataType: item?.dataType, + name: item?.columnName, + })); + setHeaderList(data); + }, []); + + useEffect(() => { + if (result?.data && lastOneData) { + setDataList((prev) => prev.concat(result?.data)); + } + }, [result]); // 关闭弹窗 const handleCancel = () => { @@ -46,109 +45,72 @@ const EditorRightResultTable: React.FC = () => { setViewTableCellData(data); }; - const columns: ColumnsType = [ - { - title: 'Name', - dataIndex: 'name', - width: 130, - fixed: 'left', - render: (value: any, row: any, rowIndex: number) => { - return ( -
-
{value}
-
- 查看 { - viewTableCell({ name: value, value }); - }} - /> - 复制 { - copyTableCell({ name: value, value }); - }} - /> -
-
- ); - }, - }, - { - title: 'Name1', - dataIndex: 'name1', - width: 130, - render: (value: any, row: any, rowIndex: number) => { - return ( -
-
{value}
-
- 查看 { - viewTableCell({ name: value, value }); - }} - /> - 复制 { - copyTableCell({ name: value, value }); - }} - /> -
-
- ); - }, - }, - { - title: 'Name', - dataIndex: 'name', - width: 130, - render: (value: any, row: any, rowIndex: number) => { - return ( -
-
{value}
-
- 查看 { - viewTableCell({ name: value, value }); - }} - /> - 复制 { - copyTableCell({ name: value, value }); - }} - /> -
-
- ); - }, - }, - { - title: 'Address', - dataIndex: 'address', - }, - ]; + const columns: ArtColumn[] = useMemo( + () => + (headerList || []).map((item, index) => { + const { dataType, name } = item; + const isFirstLine = index === 0; + const isNumber = dataType === 'STRING'; + return { + code: name, + name: name, + key: name, + lock: isFirstLine, + width: 120, + render: (value: any, row: any, rowIndex: number) => { + return ( +
+
{value}
+
+ 查看 { + viewTableCell({ name, value }); + }} + /> + 复制 { + copyTableCell({ name, value }); + }} + /> +
+
+ ); + }, + features: { sortable: isNumber ? compareStrings : true }, + }; + }), + [headerList], + ); + + const pipeline = useTablePipeline() + .input({ dataSource: dataList, columns }) + .use( + features.sort({ + mode: 'single', + // defaultSorts, + highlightColumnWhenActive: true, + // sorts, + // onChangeSorts, + }), + ) + .use( + features.columnResize({ + fallbackSize: 120, + minSize: 80, + maxSize: 1080, + // handleBackground: '#ddd', + // handleHoverBackground: '#aaa', + // handleActiveBackground: '#89bff7', + }), + ); return (
- -
Result:执行成功. Time Consuming:25ms
+ { } >
+ {/* {viewTableCellData?.name} */} { ); }; -export default EditorRightResultTable; \ No newline at end of file +export default EditorRightResultTable; diff --git a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorRightResult/index.less b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorRightResult/index.less index 78ea2cb0a..39d90f348 100644 --- a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorRightResult/index.less +++ b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorRightResult/index.less @@ -97,6 +97,10 @@ .ant-table-column-sorters { display: initial; } + + .art-table-row>.art-table-cell{ + position: relative; + } } .resultContentBox,.resultContentWrapper { display: flex; @@ -123,6 +127,7 @@ align-items: center; justify-content: space-between; max-height: 120px; + overflow: hidden; .tableHoverBox { position: absolute; @@ -158,4 +163,9 @@ background-color: #f7f8f9; } } +} + +.tableContainer { + width: 100%; + overflow-x: auto; } \ No newline at end of file diff --git a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorRightResult/index.tsx b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorRightResult/index.tsx index c5884b35e..9d2df88d6 100644 --- a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorRightResult/index.tsx +++ b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorRightResult/index.tsx @@ -1,61 +1,172 @@ +import { WORKSPACE_CONF } from '@/constant'; +import { WsFlinkKubernetesSessionClusterService } from '@/services/project/WsFlinkKubernetesSessionClusterService'; +import { WsFlinkSqlGatewayService } from '@/services/project/WsFlinkSqlGatewayService'; import { Spin, Tabs } from 'antd'; -import React, { useState } from 'react'; +import React, { useEffect, useRef, useState } from 'react'; +import { useLocation } from 'react-router-dom'; +import { useModel } from 'umi'; import EditorRightResultTable from './EditorRightResultTable'; import styles from './index.less'; -// 定义每个标签页的类型 -interface PaneItem { +// 定义标签项的接口 +interface TabItem { label: React.ReactNode; children: React.ReactNode; - key: string; + key: string | number | null | undefined; } -const defaultPanes: PaneItem[] = new Array(4).fill(null).map((_, index) => { - const id = String(index + 1); - return { - label: ( -
- Success - Tab {id} -
- ), - children: , - key: id, +const EditorRightResult: React.FC = () => { + const [dataList, setDataList] = useState([]); // 数据列表 + const urlParams = useLocation(); // 当前url参数 + const [sqlScript, setSqlScript] = useState(''); // SQL脚本内容 + const [items, setItems] = useState([]); // 标签项列表 + const [isLoading, setIsLoading] = useState(false); // 加载状态标志 + const { executionData, setExecutionData } = useModel('executionResult'); // 执行结果和设置执行结果的model + const [sessionClusterId, setSessionClusterId] = useState(); // 会话集群id + const [activeKey, setActiveKey] = useState(); // 控制当前激活的tab + const flinkArtifactSql = urlParams.state; // Flink SQL参数对象 + let sqlData: string; + const executionDataString = useRef(); + const sessionClusterIdString = useRef(); + const clearTimeOut = useRef(); + + const stopSqlCarryOut = async () => { + if (sessionClusterIdString.current && executionDataString.current) { + // 调用后端接口停止SQL执行 + await WsFlinkSqlGatewayService.deleteSqlResults( + sessionClusterIdString.current, + executionDataString.current, + ); + } }; -}); -const EditorRightResult: React.FC = () => { - // 当前活动的标签页key - const [activeKey, setActiveKey] = useState(defaultPanes?.[0]?.key); - // 标签页列表 - const [items, setItems] = useState(defaultPanes); - // 加载状态 - const [isLoading, setIsLoading] = useState(false); + useEffect(() => { + const getResults = async (data: string) => { + if (sqlData !== data) { + setIsLoading(false); + clearTimeout(clearTimeOut.current); + stopSqlCarryOut(); + sqlData = data; + } + // 调用后端接口获取SQL结果 + const catalogArray = await WsFlinkSqlGatewayService.getSqlResults(sessionClusterId!, data); + if (catalogArray?.resultType === 'NOT_READY' || catalogArray?.resultType === 'PAYLOAD') { + setIsLoading(true); + clearTimeOut.current = setTimeout(() => { + getResults(data); + }, 5000); + if (catalogArray?.resultType === 'PAYLOAD') { + setDataList((prevDataList) => { + const lastItem = prevDataList[prevDataList?.length - 1]; + if (lastItem && lastItem?.jobID === catalogArray?.jobID) { + return [...prevDataList.slice(0, -1), catalogArray]; + } else { + setActiveKey(catalogArray?.jobID); + return [...prevDataList, catalogArray]; + } + }); + setIsLoading(false); + } + } else { + setIsLoading(false); + clearTimeout(clearTimeOut.current); + } + }; + + if (executionData) { + getResults(executionData); + executionDataString.current = executionData; + } + return () => { + clearTimeout(clearTimeOut.current); + }; + }, [executionData]); + + useEffect(() => { + return stopSqlCarryOut; + }, []); - // 切换标签页时的回调函数 - const onChange = (key: string) => { - setActiveKey(key); + useEffect(() => { + const handleTabs = (result: any[]) => { + if (!result || !dataList) return []; + + return dataList.map((item, index) => ({ + label: ( +
+ Success + {item?.jobID} +
+ ), + children: ( + + ), + key: item?.jobID, + })); + }; + + const tabs = handleTabs(dataList); + setItems(tabs); + }, [dataList]); + + const onEdit = (key: string | number | null | undefined) => { + let index = items.findIndex((item) => item?.key === key); + if (index === items?.length - 1) { + setIsLoading(false); + stopSqlCarryOut(); + clearTimeout(clearTimeOut.current); + setItems(items.filter((item) => item?.key !== key)); + } else { + setItems(items.filter((item) => item?.key !== key)); + } }; + useEffect(() => { + setSessionClusterId(undefined); + setItems([]); + const projectId = localStorage.getItem(WORKSPACE_CONF.projectId); + + const fetchSessionClusterId = async () => { + // 获取会话集群id + const resSessionClusterId = + await WsFlinkKubernetesSessionClusterService.getSqlGatewaySessionClusterId(projectId!); + setSessionClusterId(resSessionClusterId); + sessionClusterIdString.current = resSessionClusterId; + }; + if (flinkArtifactSql && flinkArtifactSql.script) { + setSqlScript(flinkArtifactSql.script); + fetchSessionClusterId(); + } + }, [flinkArtifactSql]); + return (
- {/* 标签页组件 */} {items.length ? ( { + setActiveKey(itemId); + }} + onEdit={onEdit} activeKey={activeKey} type="editable-card" - items={items} - /> + > + {items.map((item) => ( + + {item.children} + + ))} + ) : ( -
No Data ​
+
No Data
)} - {/* 加载中的旋转动画 */} {isLoading && }
); diff --git a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/components/sort.ts b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/components/sort.ts new file mode 100644 index 000000000..13dd5ee52 --- /dev/null +++ b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/components/sort.ts @@ -0,0 +1,22 @@ +/** + * 比较两个字符串数字的大小,包含大数情况 + * @param a + * @param b + * @returns + */ +export function compareStrings(a: string, b: string) { + // 比较字符串长度 + if (a.length !== b.length) { + return a.length - b.length; + } + + // 逐位比较字符的ASCII码值 + for (let i = 0; i < a.length; i++) { + if (a[i] !== b[i]) { + return a.charCodeAt(i) - b.charCodeAt(i); + } + } + + // 如果两个字符串完全相等,则返回0 + return 0; +} diff --git a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/index.tsx b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/index.tsx index b003a7737..f8c66186b 100644 --- a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/index.tsx +++ b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/index.tsx @@ -2,7 +2,7 @@ import { useState } from 'react'; import Split from 'react-split'; import EditMenu from './EditMenu'; import EditorRight from './EditorRight'; -import "./index.less"; +import './index.less'; const Index: React.FC = () => { const [horizontalSplitSizes, setHorizontalSplitSizes] = useState([15, 85]); // 水平分割条大小比例的状态 @@ -28,11 +28,11 @@ const Index: React.FC = () => { setHorizontalSplitSizes(sizes); }} > - - + + ); -} +}; -export default Index; \ No newline at end of file +export default Index; diff --git a/scaleph-ui-react/src/services/project/WsFlinkSqlGatewayService.ts b/scaleph-ui-react/src/services/project/WsFlinkSqlGatewayService.ts index ea0d06728..f8b7932b4 100644 --- a/scaleph-ui-react/src/services/project/WsFlinkSqlGatewayService.ts +++ b/scaleph-ui-react/src/services/project/WsFlinkSqlGatewayService.ts @@ -16,9 +16,7 @@ export const WsFlinkSqlGatewayService = { }, leftMenuList: async (sessionClusterId: string | null) => { - return request>( - `${url}/${sessionClusterId}/getCatalogInfo`, - ); + return request>(`${url}/${sessionClusterId}/getCatalogInfo`); }, executeSqlList: async (sessionClusterId: string | null, data: Object) => { @@ -28,6 +26,18 @@ export const WsFlinkSqlGatewayService = { }); }, + deleteSqlResults: async (sessionClusterId?: string | null, operationHandleId?: string) => { + return request>(`${url}/${sessionClusterId}/${operationHandleId}`, { + method: 'DELETE', + }); + }, + + getSqlResults: async (sessionClusterId?: string | null, operationHandleId?: string) => { + return request>(`${url}/${sessionClusterId}/${operationHandleId}/results`, { + method: 'GET', + }); + }, + listDatabases: async ( sessionClusterId?: string | null, sqlGatewaySessionHandleId?: string,