From 506df4c76b0f8496bed674d97e3e1d498e400786 Mon Sep 17 00:00:00 2001 From: bailongsen Date: Sat, 22 Jul 2023 14:14:41 +0800 Subject: [PATCH 01/32] =?UTF-8?q?fix:=20=E6=B7=BB=E5=8A=A0tabs=E4=BB=A5?= =?UTF-8?q?=E5=8F=8A=E6=A0=B9=E6=8D=AE=E7=8E=B0=E6=9C=89=E4=BB=A3=E7=A0=81?= =?UTF-8?q?=E8=BF=9B=E8=A1=8C=E8=A1=A5=E5=85=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Sql/CodeEditor/EditMenu/index.less | 0 .../Sql/CodeEditor/EditMenu/index.tsx | 198 ++++++++++++ .../Sql/CodeEditor/Editor/index copy.tsx | 258 ++++++++++++++++ .../Artifact/Sql/CodeEditor/Editor/index.less | 41 +++ .../Artifact/Sql/CodeEditor/Editor/index.tsx | 104 +++++++ .../Sql/CodeEditor/TabComponents/index.less | 0 .../Sql/CodeEditor/TabComponents/index.tsx | 125 ++++++++ .../Artifact/Sql/CodeEditor/index.tsx | 284 ++---------------- 8 files changed, 757 insertions(+), 253 deletions(-) create mode 100644 scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditMenu/index.less create mode 100644 scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditMenu/index.tsx create mode 100644 scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/Editor/index copy.tsx create mode 100644 scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/Editor/index.less create mode 100644 scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/Editor/index.tsx create mode 100644 scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/TabComponents/index.less create mode 100644 scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/TabComponents/index.tsx diff --git a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditMenu/index.less b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditMenu/index.less new file mode 100644 index 000000000..e69de29bb diff --git a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditMenu/index.tsx b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditMenu/index.tsx new file mode 100644 index 000000000..2a9c7ea8e --- /dev/null +++ b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditMenu/index.tsx @@ -0,0 +1,198 @@ +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.listCatalogs(resSessionClusterId, resSqlGatewaySessionHandleId); + 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 <> + + + {/* TODO: Add search in the future */} + {/**/} + + + + ; +}; + +export default EditMenu; diff --git a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/Editor/index copy.tsx b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/Editor/index copy.tsx new file mode 100644 index 000000000..6efa53a3a --- /dev/null +++ b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/Editor/index copy.tsx @@ -0,0 +1,258 @@ +import Split from "react-split"; +import React, {useEffect, useState} from "react"; +import {Editor} from "@monaco-editor/react"; +import {Button, Col, Row, Space, Tag, Tree, TreeDataNode} from "antd"; +import "./index.less"; +import {useLocation} from "umi"; +import {WsFlinkArtifactSql} from "@/services/project/typings"; +import {FlinkArtifactSqlService} from "@/services/project/WsFlinkArtifactSqlService"; +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 CodeEditor: React.FC = () => { + const urlParams = useLocation(); + const [showLeft, setShowLeft] = useState(true); + const [sqlScript, setSqlScript] = useState(''); + const [horizontalSplitSizes, setHorizontalSplitSizes] = useState([ + 20, 80, + ]); + const flinkArtifactSql = urlParams.state as WsFlinkArtifactSql; + + 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.listCatalogs(resSessionClusterId, resSqlGatewaySessionHandleId); + setTreeNodes(catalogArray.map(catalog => ({ + title: catalog, + key: catalog, + name: catalog, + type: 'catalog', + isLeaf: false, + children: [], + parent: null + }))); + } catch (error) { + // 处理错误 + } + } + + fetchData(); + }, []); + + useEffect(() => { + setSqlScript(flinkArtifactSql.script); + }, []) + + const onSave = () => { + FlinkArtifactSqlService.updateScript({id: flinkArtifactSql.id, script: sqlScript}); + } + + 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 <> + { + if (sizes[0] <= 6) { + setShowLeft(false); + } else { + setShowLeft(true); + } + setHorizontalSplitSizes(sizes); + }} + > +
+ + + {/* TODO: Add search in the future */} + {/**/} + + + +
+
+ + + + + + + + + + + { + setSqlScript(value) + }} + /> + + + +
+
+ ; +}; + +export default CodeEditor; 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 new file mode 100644 index 000000000..1015a7515 --- /dev/null +++ b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/Editor/index.less @@ -0,0 +1,41 @@ +.split-horizontal { + height: calc(100vh - 175px); + overflow-x: hidden; + overflow-y: auto; + } + + .split-horizontal > div { + float: left; + height: 100%; + } + + .split-vertical { + height: calc(100vh - 56px); + overflow-x: hidden; + overflow-y: auto; + } + + .gutter { + background-color: rgba(5, 5, 5, 0.06); + background-repeat: no-repeat; + background-position: 50%; + } + + .gutter.gutter-horizontal { + // background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg=="); + cursor: col-resize; + } + + .gutter.gutter-vertical { + // background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAFAQMAAABo7865AAAABlBMVEVHcEzMzMzyAv2sAAAAAXRSTlMAQObYZgAAABBJREFUeF5jOAMEEAIEEFwAn3kMwcB6I2AAAAAASUVORK5CYII="); + cursor: row-resize; + } + + .container-left { + margin: 6px; + } + + .container-right { + margin-left: 6px; + margin-right: 6px; + } \ 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 new file mode 100644 index 000000000..0017fdc33 --- /dev/null +++ b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/Editor/index.tsx @@ -0,0 +1,104 @@ +import React, {useEffect, useState} from "react"; +import {Editor} from "@monaco-editor/react"; +import {Button, Col, Row, Space} from "antd"; +import "./index.less"; +import {useLocation} from "umi"; +import {WsFlinkArtifactSql} from "@/services/project/typings"; +import {FlinkArtifactSqlService} from "@/services/project/WsFlinkArtifactSqlService"; + +const CodeEditor: React.FC = () => { + const urlParams = useLocation(); + const [sqlScript, setSqlScript] = useState(''); + const flinkArtifactSql = urlParams.state||'' as WsFlinkArtifactSql; + + useEffect(() => { + setSqlScript(flinkArtifactSql.script); + }, []) + + const onSave = () => { + FlinkArtifactSqlService.updateScript({id: flinkArtifactSql.id, script: sqlScript}); + } + + const handleEditorDidMount = (editor, monaco) => { + // 在 editorDidMount 回调函数中对 Monaco Editor 进行配置 + monaco.languages.register({ id: "sql" }); + + monaco.languages.setMonarchTokensProvider("sql", { + // 设置 SQL 语言的自定义语法定义 + tokenizer: { + root: [ + // 自定义的标记规则 + ], + }, + }); + + monaco.languages.registerCompletionItemProvider("sql", { + provideCompletionItems: function (model, position) { + const textUntilPosition = model.getValueInRange({ + startLineNumber: position.lineNumber, + startColumn: 1, + endLineNumber: position.lineNumber, + endColumn: position.column, + }); + + // 根据输入的文本返回相应的代码补全建议项 + const suggestions = getSQLSuggestions(textUntilPosition); + + return { + suggestions: suggestions, + }; + }, + }); + }; + + const getSQLSuggestions = (text) => { + // 根据输入的文本生成相应的代码补全建议项 + // 可以根据自己的需求自定义补全逻辑 + // 返回的建议项应该包括 label、kind、insertText 等属性 + return [ + { + label: "SELECT", + kind: monaco.languages.CompletionItemKind.Keyword, + insertText: "SELECT ", + }, + { + label: "FROM", + kind: monaco.languages.CompletionItemKind.Keyword, + insertText: "FROM ", + }, + // 其他补全建议项... + ]; + }; + + + return <> +
+ + + + + + + + + + + { + setSqlScript(value) + }} + editorDidMount={handleEditorDidMount} + /> + + +
+ ; +}; + +export default CodeEditor; diff --git a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/TabComponents/index.less b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/TabComponents/index.less new file mode 100644 index 000000000..e69de29bb diff --git a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/TabComponents/index.tsx b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/TabComponents/index.tsx new file mode 100644 index 000000000..76e765aab --- /dev/null +++ b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/TabComponents/index.tsx @@ -0,0 +1,125 @@ +import { Tabs, Input } from 'antd'; +import React, { useRef, useState } from 'react'; +import Editor from '../Editor'; +import './index.less'; + +const { TabPane } = Tabs; + +interface TabItem { + label: string; + children: Element | string| JSX.Element; + key: string; + closable?: boolean; + isEditing?: boolean; +} + +const initialItems: TabItem[] = [ + { label: 'Tab 1', children: , key: '1' }, + { label: 'Tab 2', children: 'Content of Tab 2', key: '2' }, + { label: 'Tab 3', children: 'Content of Tab 3', key: '3' }, +]; + +const TabComponents: React.FC = () => { + const [activeKey, setActiveKey] = useState(initialItems[0].key); + const [items, setItems] = useState(initialItems); + const newTabIndex = useRef(0); + + const onChange = (newActiveKey: string) => { + setActiveKey(newActiveKey); + }; + + const add = () => { + const newActiveKey = `newTab${newTabIndex.current++}`; + const newPanes = [...items]; + newPanes.push({ label: 'New Tab', children: 'Content of new Tab', key: newActiveKey }); + setItems(newPanes); + setActiveKey(newActiveKey); + }; + + const remove = (targetKey: string) => { + let newActiveKey = activeKey; + let lastIndex = -1; + items.forEach((item, i) => { + if (item.key === targetKey) { + lastIndex = i - 1; + } + }); + const newPanes = items.filter((item) => item.key !== targetKey); + if (newPanes.length && newActiveKey === targetKey) { + if (lastIndex >= 0) { + newActiveKey = newPanes[lastIndex].key; + } else { + newActiveKey = newPanes[0].key; + } + } + setItems(newPanes); + setActiveKey(newActiveKey); + }; + + const onEdit = (targetKey: string, action: 'add' | 'remove') => { + if (action === 'add') { + add(); + } else { + remove(targetKey); + } + }; + + const handleTabLabelDoubleClick = (tabItem: TabItem) => { + const { key } = tabItem; + const newItems = items.map((item) => { + if (item.key === key) { + return { ...item, isEditing: true }; + } + return item; + }); + setItems(newItems); + }; + + const handleTabLabelChange = (e: React.ChangeEvent, tabItem: TabItem) => { + const { value } = e.target; + const { key } = tabItem; + const newItems = items.map((item) => { + if (item.key === key) { + return { ...item, label: value, isEditing: false }; + } + return item; + }); + setItems(newItems); + }; + + + return ( + + {items.map((item) => ( + handleTabLabelChange(e, item)} + onPressEnter={(e) => handleTabLabelChange(e, item)} + bordered={false} + style={{ boxShadow: 'none', width: '120px', height: '22px' }} + /> + ) : ( + handleTabLabelDoubleClick(item)}>{item.label} + ) + } + key={item.key} + closable={item.closable} + style={{ height: 'auto' }} + > + {item.children} + + ))} + + ); +}; + +export default TabComponents; \ No newline at end of file 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 90ebd8e57..4c373d61a 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 @@ -1,258 +1,36 @@ -import Split from "react-split"; -import React, {useEffect, useState} from "react"; -import {Editor} from "@monaco-editor/react"; -import {Button, Col, Row, Space, Tag, Tree, TreeDataNode} from "antd"; +import { useState } from 'react'; +import Split from 'react-split'; +import EditMenu from './EditMenu'; +import TabComponents from './TabComponents'; import "./index.less"; -import {useLocation} from "umi"; -import {WsFlinkArtifactSql} from "@/services/project/typings"; -import {FlinkArtifactSqlService} from "@/services/project/WsFlinkArtifactSqlService"; -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 -} +export default function index() { + const [horizontalSplitSizes, setHorizontalSplitSizes] = useState([20, 80]); -const CodeEditor: React.FC = () => { - const urlParams = useLocation(); const [showLeft, setShowLeft] = useState(true); - const [sqlScript, setSqlScript] = useState(''); - const [horizontalSplitSizes, setHorizontalSplitSizes] = useState([ - 20, 80, - ]); - const flinkArtifactSql = urlParams.state as WsFlinkArtifactSql; - - const projectId = localStorage.getItem(WORKSPACE_CONF.projectId); - const [sessionClusterId, setSessionClusterId] = useState(); - const [sqlGatewaySessionHandleId, setSqlGatewaySessionHandleId] = useState(); - const [treeNodes, setTreeNodes] = useState([]) - - useEffect(() => { - WsFlinkKubernetesSessionClusterService.getSqlGatewaySessionClusterId(projectId) - .then(resSessionClusterId => { - setSessionClusterId(resSessionClusterId); - WsFlinkSqlGatewayService.openSession(resSessionClusterId) - .then(resSqlGatewaySessionHandleId => { - setSqlGatewaySessionHandleId(resSqlGatewaySessionHandleId); - WsFlinkSqlGatewayService.listCatalogs(resSessionClusterId, resSqlGatewaySessionHandleId) - .then(catalogArray => { - setTreeNodes(catalogArray.map(catalog => { - return { - title: catalog, - key: catalog, - name: catalog, - type: 'catalog', - isLeaf: false, - children: [], - parent: null - } - })) - }) - }) - }); - }, []) - - useEffect(() => { - setSqlScript(flinkArtifactSql.script); - }, []) - - const onSave = () => { - FlinkArtifactSqlService.updateScript({id: flinkArtifactSql.id, script: sqlScript}); - } - 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) => { - return new Promise(async resolve => { - let children: CatalogNode[] = []; - switch (catalogNode.type) { - case 'catalog': - await WsFlinkSqlGatewayService.listDatabases(sessionClusterId, sqlGatewaySessionHandleId, catalogNode.name) - .then(databases => 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': - await WsFlinkSqlGatewayService.listTables(sessionClusterId, sqlGatewaySessionHandleId, catalogNode.parent?.parent?.name, catalogNode.name) - .then(tables => 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': - await WsFlinkSqlGatewayService.listViews(sessionClusterId, sqlGatewaySessionHandleId, catalogNode.parent?.parent?.name, catalogNode.name) - .then(views => 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': - await WsFlinkSqlGatewayService.listUserDefinedFunctions(sessionClusterId, sqlGatewaySessionHandleId, catalogNode.parent?.parent?.name, catalogNode.name) - .then(userDefinedFunctions => 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)) - resolve(); - return; - }); - } - - return <> - { - if (sizes[0] <= 6) { - setShowLeft(false); - } else { - setShowLeft(true); - } - setHorizontalSplitSizes(sizes); - }} - > -
- - - {/* TODO: Add search in the future */} - {/**/} - - - -
-
- - - - - - - - - - - { - setSqlScript(value) - }} - > - - - -
-
- ; -}; - -export default CodeEditor; + return ( + <> + { + if (sizes[0] <= 6) { + setShowLeft(false); + } else { + setShowLeft(true); + } + setHorizontalSplitSizes(sizes); + }} + > + + + + + ); +} From 0a4a67847a293dd0efdf61e8e6afdf8dfb5ead31 Mon Sep 17 00:00:00 2001 From: bailongsen Date: Sat, 22 Jul 2023 23:13:44 +0800 Subject: [PATCH 02/32] =?UTF-8?q?fix:=20=E6=B7=BB=E5=8A=A0=E4=BB=A3?= =?UTF-8?q?=E7=A0=81=E8=A1=A5=E5=85=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + index.d.ts | 5 + package-lock.json | 24 +++ package.json | 5 + .../Artifact/Sql/CodeEditor/Editor/index.tsx | 149 ++++++++---------- 5 files changed, 97 insertions(+), 87 deletions(-) create mode 100644 index.d.ts create mode 100644 package-lock.json create mode 100644 package.json diff --git a/.gitignore b/.gitignore index 8c6216e48..0e129e774 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ target/ .mvn/wrapper/maven-wrapper.jar !**/src/main/**/target/ !**/src/test/**/target/ +/node_modules ### STS ### .apt_generated diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 000000000..e60e076e6 --- /dev/null +++ b/index.d.ts @@ -0,0 +1,5 @@ +declare module 'monaco-editor/esm/vs/basic-languages/sql/sql'; +declare module 'monaco-editor/esm/vs/language/typescript/ts.worker.js'; +declare module 'monaco-editor/esm/vs/editor/editor.worker.js'; +declare module 'monaco-editor/esm/vs/language/sql/monaco.contribution' +declare module 'monaco-editor/esm/vs/editor/editor.worker' \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 000000000..4ed56b4cc --- /dev/null +++ b/package-lock.json @@ -0,0 +1,24 @@ +{ + "name": "scaleph-dev", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "dependencies": { + "monaco-editor": "^0.40.0" + } + }, + "node_modules/monaco-editor": { + "version": "0.40.0", + "resolved": "https://registry.npmmirror.com/monaco-editor/-/monaco-editor-0.40.0.tgz", + "integrity": "sha512-1wymccLEuFSMBvCk/jT1YDW/GuxMLYwnFwF9CDyYCxoTw2Pt379J3FUhwy9c43j51JdcxVPjwk0jm0EVDsBS2g==" + } + }, + "dependencies": { + "monaco-editor": { + "version": "0.40.0", + "resolved": "https://registry.npmmirror.com/monaco-editor/-/monaco-editor-0.40.0.tgz", + "integrity": "sha512-1wymccLEuFSMBvCk/jT1YDW/GuxMLYwnFwF9CDyYCxoTw2Pt379J3FUhwy9c43j51JdcxVPjwk0jm0EVDsBS2g==" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 000000000..47d48079c --- /dev/null +++ b/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "monaco-editor": "^0.40.0" + } +} 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 0017fdc33..ca12f8cb7 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,104 +1,79 @@ -import React, {useEffect, useState} from "react"; -import {Editor} from "@monaco-editor/react"; -import {Button, Col, Row, Space} from "antd"; -import "./index.less"; -import {useLocation} from "umi"; -import {WsFlinkArtifactSql} from "@/services/project/typings"; -import {FlinkArtifactSqlService} from "@/services/project/WsFlinkArtifactSqlService"; +import { Editor } from '@monaco-editor/react'; +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, useState } from 'react'; +const { keywords: SQLKeys } = language; + +import { WsFlinkArtifactSql } from '@/services/project/typings'; +import { FlinkArtifactSqlService } from '@/services/project/WsFlinkArtifactSqlService'; +import { Button, Col, Row, Space } from 'antd'; +import { useLocation } from 'react-router-dom'; +import './index.less'; const CodeEditor: React.FC = () => { const urlParams = useLocation(); const [sqlScript, setSqlScript] = useState(''); - const flinkArtifactSql = urlParams.state||'' as WsFlinkArtifactSql; + const flinkArtifactSql = urlParams.state || ('' as WsFlinkArtifactSql); useEffect(() => { setSqlScript(flinkArtifactSql.script); - }, []) + }, []); const onSave = () => { - FlinkArtifactSqlService.updateScript({id: flinkArtifactSql.id, script: sqlScript}); - } - - const handleEditorDidMount = (editor, monaco) => { - // 在 editorDidMount 回调函数中对 Monaco Editor 进行配置 - monaco.languages.register({ id: "sql" }); - - monaco.languages.setMonarchTokensProvider("sql", { - // 设置 SQL 语言的自定义语法定义 - tokenizer: { - root: [ - // 自定义的标记规则 - ], - }, - }); - - monaco.languages.registerCompletionItemProvider("sql", { - provideCompletionItems: function (model, position) { - const textUntilPosition = model.getValueInRange({ - startLineNumber: position.lineNumber, - startColumn: 1, - endLineNumber: position.lineNumber, - endColumn: position.column, - }); - - // 根据输入的文本返回相应的代码补全建议项 - const suggestions = getSQLSuggestions(textUntilPosition); - - return { - suggestions: suggestions, - }; - }, - }); + FlinkArtifactSqlService.updateScript({ id: flinkArtifactSql.id, script: sqlScript }); }; - const getSQLSuggestions = (text) => { - // 根据输入的文本生成相应的代码补全建议项 - // 可以根据自己的需求自定义补全逻辑 - // 返回的建议项应该包括 label、kind、insertText 等属性 - return [ - { - label: "SELECT", - kind: monaco.languages.CompletionItemKind.Keyword, - insertText: "SELECT ", - }, - { - label: "FROM", - kind: monaco.languages.CompletionItemKind.Keyword, - insertText: "FROM ", - }, - // 其他补全建议项... - ]; + // 获取 SQL 语法提示 + const getSQLSuggest = (): monaco.languages.CompletionItem[] => { + return SQLKeys.map((key: string) => ({ + label: key, + kind: monaco.languages.CompletionItemKind.Keyword, + insertText: key, + detail: '​', + })); }; + const provideCompletionItems = ( + model: monaco.editor.ITextModel, + position: monaco.Position, + ): monaco.languages.ProviderResult => { + return { + suggestions: getSQLSuggest(), + }; + }; - return <> -
- - - - - - - - - - - { - setSqlScript(value) - }} - editorDidMount={handleEditorDidMount} - /> - - -
- ; + return ( +
+ + + + + + + + + + { + // 注册代码补全提供者 + monaco.languages.registerCompletionItemProvider('sql', { + provideCompletionItems, + }); + }} + onChange={(value, event) => { + setSqlScript(value); + }} + /> + + +
+ ); }; export default CodeEditor; From 47c0b9129b94970ffba09d908824de1802240f01 Mon Sep 17 00:00:00 2001 From: bailongsen Date: Tue, 25 Jul 2023 16:18:56 +0800 Subject: [PATCH 03/32] =?UTF-8?q?fix:=20=E4=BF=AE=E6=94=B9=E7=BC=96?= =?UTF-8?q?=E8=BE=91=E5=99=A8=E5=B8=83=E5=B1=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Artifact/Sql/CodeEditor/Editor/index.less | 78 +++++------ .../Artifact/Sql/CodeEditor/Editor/index.tsx | 105 +++++++++------ .../{TabComponents => EditorLeft}/index.less | 0 .../Sql/CodeEditor/EditorLeft/index.tsx | 38 ++++++ .../Sql/CodeEditor/TabComponents/index.tsx | 125 ------------------ .../Artifact/Sql/CodeEditor/index.less | 8 +- .../Artifact/Sql/CodeEditor/index.tsx | 4 +- 7 files changed, 149 insertions(+), 209 deletions(-) rename scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/{TabComponents => EditorLeft}/index.less (100%) create mode 100644 scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorLeft/index.tsx delete mode 100644 scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/TabComponents/index.tsx 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 1015a7515..0219f7706 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 @@ -1,41 +1,43 @@ -.split-horizontal { - height: calc(100vh - 175px); - overflow-x: hidden; - overflow-y: auto; - } - - .split-horizontal > div { - float: left; - height: 100%; - } +.consoleOptionsWrapper{ + display: flex; + align-items: center; + justify-content: space-between; + position: absolute; + padding: 0 20px; + bottom: 0; + left: 0; + right: 0; + z-index: 1; + height: 40px; + background-color: #fff; - .split-vertical { - height: calc(100vh - 56px); - overflow-x: hidden; - overflow-y: auto; - } - - .gutter { - background-color: rgba(5, 5, 5, 0.06); - background-repeat: no-repeat; - background-position: 50%; - } - - .gutter.gutter-horizontal { - // background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAeCAYAAADkftS9AAAAIklEQVQoU2M4c+bMfxAGAgYYmwGrIIiDjrELjpo5aiZeMwF+yNnOs5KSvgAAAABJRU5ErkJggg=="); - cursor: col-resize; - } - - .gutter.gutter-vertical { - // background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAB4AAAAFAQMAAABo7865AAAABlBMVEVHcEzMzMzyAv2sAAAAAXRSTlMAQObYZgAAABBJREFUeF5jOAMEEAIEEFwAn3kMwcB6I2AAAAAASUVORK5CYII="); - cursor: row-resize; + .consoleOptionsLeft{ + display: flex; + align-items: center; } - .container-left { - margin: 6px; - } - - .container-right { - margin-left: 6px; - margin-right: 6px; - } \ No newline at end of file + .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 ca12f8cb7..ed9ff9a5e 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,30 +1,42 @@ -import { Editor } from '@monaco-editor/react'; +import { Editor, EditorConstructionOptions, CompletionItem, languages, Position, CompletionList } from '@monaco-editor/react'; +import { Button } from 'antd'; 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, useState } from 'react'; const { keywords: SQLKeys } = language; import { WsFlinkArtifactSql } from '@/services/project/typings'; -import { FlinkArtifactSqlService } from '@/services/project/WsFlinkArtifactSqlService'; -import { Button, Col, Row, Space } from 'antd'; import { useLocation } from 'react-router-dom'; -import './index.less'; +import styles from './index.less'; const CodeEditor: React.FC = () => { const urlParams = useLocation(); - const [sqlScript, setSqlScript] = useState(''); + const [sqlScript, setSqlScript] = useState('');// 内容 const flinkArtifactSql = urlParams.state || ('' as WsFlinkArtifactSql); useEffect(() => { setSqlScript(flinkArtifactSql.script); }, []); - const onSave = () => { - FlinkArtifactSqlService.updateScript({ id: flinkArtifactSql.id, script: sqlScript }); + // 点击运行获取选中或者全部值 + const onRun = (editor: monaco.editor.IStandaloneCodeEditor): void => { + const selection = editor.getSelection(); + if (selection && !selection.isEmpty()) { + const selectedValue = editor.getModel()?.getValueInRange(selection); + console.log("选中的值:", selectedValue); + } else { + const fullValue = editor.getModel()?.getValue(); + console.log("全部的值:", fullValue); + } }; + // 保存数据 + const onSave = (): void => { + FlinkArtifactSqlService.updateScript({ id: flinkArtifactSql.id, script: sqlScript }); + } + // 获取 SQL 语法提示 - const getSQLSuggest = (): monaco.languages.CompletionItem[] => { + const getSQLSuggest = (): CompletionItem[] => { return SQLKeys.map((key: string) => ({ label: key, kind: monaco.languages.CompletionItemKind.Keyword, @@ -35,45 +47,58 @@ const CodeEditor: React.FC = () => { const provideCompletionItems = ( model: monaco.editor.ITextModel, - position: monaco.Position, - ): monaco.languages.ProviderResult => { + position: Position, + ): ProviderResult => { return { suggestions: getSQLSuggest(), }; }; return ( -
- - - - - - - - - - { - // 注册代码补全提供者 - monaco.languages.registerCompletionItemProvider('sql', { - provideCompletionItems, - }); - }} - onChange={(value, event) => { - setSqlScript(value); - }} - /> - - +
+ { + // 注册代码补全提供者 + monaco.languages.registerCompletionItemProvider('sql', { + provideCompletionItems, + }); + }} + onChange={(value, event) => { + setSqlScript(value); + }} + onMount={(editor) => (window.editor = editor)} + /> +
+
+ + +
+ +
); }; -export default CodeEditor; +export default CodeEditor; \ No newline at end of file diff --git a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/TabComponents/index.less b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorLeft/index.less similarity index 100% rename from scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/TabComponents/index.less rename to scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorLeft/index.less diff --git a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorLeft/index.tsx b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorLeft/index.tsx new file mode 100644 index 000000000..416fe031c --- /dev/null +++ b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorLeft/index.tsx @@ -0,0 +1,38 @@ +import { Button } from 'antd'; +import { useState, useRef } from 'react'; +import Split from 'react-split'; +import Editor from '../Editor'; +import './index.less'; + +export default function EditorLeft() { + const [verticalSplitSizes, setVerticalSplitSizes] = useState([70, 30]); + const editorRef = useRef(); + + + + const handleDrag = (sizes: number[]) => { + setVerticalSplitSizes(sizes); + if (editorRef.current) { + const editorHeight = sizes[0]; + editorRef.current.layout({ height: `${editorHeight}%` }); + } + }; + + return ( + +
+ +
+
456
+
+ ); +} \ No newline at end of file diff --git a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/TabComponents/index.tsx b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/TabComponents/index.tsx deleted file mode 100644 index 76e765aab..000000000 --- a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/TabComponents/index.tsx +++ /dev/null @@ -1,125 +0,0 @@ -import { Tabs, Input } from 'antd'; -import React, { useRef, useState } from 'react'; -import Editor from '../Editor'; -import './index.less'; - -const { TabPane } = Tabs; - -interface TabItem { - label: string; - children: Element | string| JSX.Element; - key: string; - closable?: boolean; - isEditing?: boolean; -} - -const initialItems: TabItem[] = [ - { label: 'Tab 1', children: , key: '1' }, - { label: 'Tab 2', children: 'Content of Tab 2', key: '2' }, - { label: 'Tab 3', children: 'Content of Tab 3', key: '3' }, -]; - -const TabComponents: React.FC = () => { - const [activeKey, setActiveKey] = useState(initialItems[0].key); - const [items, setItems] = useState(initialItems); - const newTabIndex = useRef(0); - - const onChange = (newActiveKey: string) => { - setActiveKey(newActiveKey); - }; - - const add = () => { - const newActiveKey = `newTab${newTabIndex.current++}`; - const newPanes = [...items]; - newPanes.push({ label: 'New Tab', children: 'Content of new Tab', key: newActiveKey }); - setItems(newPanes); - setActiveKey(newActiveKey); - }; - - const remove = (targetKey: string) => { - let newActiveKey = activeKey; - let lastIndex = -1; - items.forEach((item, i) => { - if (item.key === targetKey) { - lastIndex = i - 1; - } - }); - const newPanes = items.filter((item) => item.key !== targetKey); - if (newPanes.length && newActiveKey === targetKey) { - if (lastIndex >= 0) { - newActiveKey = newPanes[lastIndex].key; - } else { - newActiveKey = newPanes[0].key; - } - } - setItems(newPanes); - setActiveKey(newActiveKey); - }; - - const onEdit = (targetKey: string, action: 'add' | 'remove') => { - if (action === 'add') { - add(); - } else { - remove(targetKey); - } - }; - - const handleTabLabelDoubleClick = (tabItem: TabItem) => { - const { key } = tabItem; - const newItems = items.map((item) => { - if (item.key === key) { - return { ...item, isEditing: true }; - } - return item; - }); - setItems(newItems); - }; - - const handleTabLabelChange = (e: React.ChangeEvent, tabItem: TabItem) => { - const { value } = e.target; - const { key } = tabItem; - const newItems = items.map((item) => { - if (item.key === key) { - return { ...item, label: value, isEditing: false }; - } - return item; - }); - setItems(newItems); - }; - - - return ( - - {items.map((item) => ( - handleTabLabelChange(e, item)} - onPressEnter={(e) => handleTabLabelChange(e, item)} - bordered={false} - style={{ boxShadow: 'none', width: '120px', height: '22px' }} - /> - ) : ( - handleTabLabelDoubleClick(item)}>{item.label} - ) - } - key={item.key} - closable={item.closable} - style={{ height: 'auto' }} - > - {item.children} - - ))} - - ); -}; - -export default TabComponents; \ No newline at end of file diff --git a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/index.less b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/index.less index 1015a7515..89001c2c4 100644 --- a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/index.less +++ b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/index.less @@ -1,7 +1,7 @@ .split-horizontal { height: calc(100vh - 175px); - overflow-x: hidden; - overflow-y: auto; + // overflow-x: hidden; + // overflow-y: auto; } .split-horizontal > div { @@ -11,8 +11,8 @@ .split-vertical { height: calc(100vh - 56px); - overflow-x: hidden; - overflow-y: auto; + // overflow-x: hidden; + // overflow-y: auto; } .gutter { 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 4c373d61a..6c9227f7c 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 @@ -1,7 +1,7 @@ import { useState } from 'react'; import Split from 'react-split'; import EditMenu from './EditMenu'; -import TabComponents from './TabComponents'; +import EditorLeft from './EditorLeft'; import "./index.less"; export default function index() { @@ -29,7 +29,7 @@ export default function index() { }} > - + ); From 5c47fda97a5f1e349b519aa7d521a306a729b3fc Mon Sep 17 00:00:00 2001 From: bailongsen Date: Thu, 27 Jul 2023 17:13:32 +0800 Subject: [PATCH 04/32] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=9B=BD?= =?UTF-8?q?=E5=AE=B6=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/locales/zh-CN/pages/resource.ts | 4 + .../Sql/CodeEditor/Editor/index copy.tsx | 258 ------------------ .../Artifact/Sql/CodeEditor/Editor/index.tsx | 10 +- .../{EditorLeft => EditorRight}/index.less | 0 .../{EditorLeft => EditorRight}/index.tsx | 0 .../Artifact/Sql/CodeEditor/index.tsx | 4 +- 6 files changed, 12 insertions(+), 264 deletions(-) delete mode 100644 scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/Editor/index copy.tsx rename scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/{EditorLeft => EditorRight}/index.less (100%) rename scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/{EditorLeft => EditorRight}/index.tsx (100%) diff --git a/scaleph-ui-react/src/locales/zh-CN/pages/resource.ts b/scaleph-ui-react/src/locales/zh-CN/pages/resource.ts index 9ba8a5ad3..32387021d 100644 --- a/scaleph-ui-react/src/locales/zh-CN/pages/resource.ts +++ b/scaleph-ui-react/src/locales/zh-CN/pages/resource.ts @@ -49,4 +49,8 @@ export default { 'pages.resource.credentialFile.blockSize': '所在块大小', 'pages.resource.credentialFile.accessTime': '访问时间', 'pages.resource.credentialFile.modificationTime': '修改时间', + + 'Run': '运行', + 'Save': '保存', + 'Format':'格式化' }; diff --git a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/Editor/index copy.tsx b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/Editor/index copy.tsx deleted file mode 100644 index 6efa53a3a..000000000 --- a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/Editor/index copy.tsx +++ /dev/null @@ -1,258 +0,0 @@ -import Split from "react-split"; -import React, {useEffect, useState} from "react"; -import {Editor} from "@monaco-editor/react"; -import {Button, Col, Row, Space, Tag, Tree, TreeDataNode} from "antd"; -import "./index.less"; -import {useLocation} from "umi"; -import {WsFlinkArtifactSql} from "@/services/project/typings"; -import {FlinkArtifactSqlService} from "@/services/project/WsFlinkArtifactSqlService"; -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 CodeEditor: React.FC = () => { - const urlParams = useLocation(); - const [showLeft, setShowLeft] = useState(true); - const [sqlScript, setSqlScript] = useState(''); - const [horizontalSplitSizes, setHorizontalSplitSizes] = useState([ - 20, 80, - ]); - const flinkArtifactSql = urlParams.state as WsFlinkArtifactSql; - - 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.listCatalogs(resSessionClusterId, resSqlGatewaySessionHandleId); - setTreeNodes(catalogArray.map(catalog => ({ - title: catalog, - key: catalog, - name: catalog, - type: 'catalog', - isLeaf: false, - children: [], - parent: null - }))); - } catch (error) { - // 处理错误 - } - } - - fetchData(); - }, []); - - useEffect(() => { - setSqlScript(flinkArtifactSql.script); - }, []) - - const onSave = () => { - FlinkArtifactSqlService.updateScript({id: flinkArtifactSql.id, script: sqlScript}); - } - - 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 <> - { - if (sizes[0] <= 6) { - setShowLeft(false); - } else { - setShowLeft(true); - } - setHorizontalSplitSizes(sizes); - }} - > -
- - - {/* TODO: Add search in the future */} - {/**/} - - - -
-
- - - - - - - - - - - { - setSqlScript(value) - }} - /> - - - -
-
- ; -}; - -export default CodeEditor; 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 ed9ff9a5e..59a2593ef 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,8 +1,9 @@ +import React, { useEffect, useState } from 'react'; import { Editor, EditorConstructionOptions, CompletionItem, languages, Position, CompletionList } from '@monaco-editor/react'; import { Button } 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, useState } from 'react'; const { keywords: SQLKeys } = language; import { WsFlinkArtifactSql } from '@/services/project/typings'; @@ -13,6 +14,7 @@ const CodeEditor: React.FC = () => { const urlParams = useLocation(); const [sqlScript, setSqlScript] = useState('');// 内容 const flinkArtifactSql = urlParams.state || ('' as WsFlinkArtifactSql); + const intl = useIntl();//语言切换 useEffect(() => { setSqlScript(flinkArtifactSql.script); @@ -77,14 +79,14 @@ const CodeEditor: React.FC = () => {
diff --git a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorLeft/index.less b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorRight/index.less similarity index 100% rename from scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorLeft/index.less rename to scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorRight/index.less diff --git a/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorLeft/index.tsx b/scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorRight/index.tsx similarity index 100% rename from scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorLeft/index.tsx rename to scaleph-ui-react/src/pages/Project/Workspace/Artifact/Sql/CodeEditor/EditorRight/index.tsx 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 6c9227f7c..3cbdfe9b6 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 @@ -1,7 +1,7 @@ import { useState } from 'react'; import Split from 'react-split'; import EditMenu from './EditMenu'; -import EditorLeft from './EditorLeft'; +import EditorRight from './EditorRight'; import "./index.less"; export default function index() { @@ -29,7 +29,7 @@ export default function index() { }} > - + ); From 0151f308c6d276504cf39a942d24e110a51657b4 Mon Sep 17 00:00:00 2001 From: bailongsen Date: Thu, 27 Jul 2023 17:14:21 +0800 Subject: [PATCH 05/32] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E5=9B=BD?= =?UTF-8?q?=E9=99=85=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Project/Workspace/Artifact/Sql/CodeEditor/Editor/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 59a2593ef..dc52dc3ed 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 @@ -86,7 +86,7 @@ const CodeEditor: React.FC = () => { className={styles.saveButton} onClick={onSave} > - {intl.formatMessage({id: 'Save'})} + {intl.formatMessage({id: 'Save'})}