From 3cbe7ed73f6fbd1504988677f19aa7c829c14253 Mon Sep 17 00:00:00 2001 From: gaoyan Date: Sat, 14 Oct 2023 22:05:33 +0800 Subject: [PATCH] Optimize datastudio (#2387) * save job when submit * disable edit when task online * optimize job edit * optimize job diff modal * merge dev --- dinky-web/src/locales/en-US/pages.ts | 20 +++ dinky-web/src/locales/zh-CN/pages.ts | 20 ++- .../DataStudio/HeaderContainer/index.tsx | 47 ++++--- .../LeftContainer/Project/index.tsx | 34 +++-- .../MiddleContainer/Editor/DiffModal.tsx | 127 ++++++++++++++++++ .../MiddleContainer/Editor/constants.tsx | 53 ++++++++ .../MiddleContainer/Editor/index.less | 44 ++++++ .../MiddleContainer/Editor/index.tsx | 104 +++++++++----- .../DataStudio/MiddleContainer/index.tsx | 39 +++++- .../RightContainer/JobConfig/index.tsx | 2 +- dinky-web/src/pages/DataStudio/function.ts | 2 +- dinky-web/src/pages/DataStudio/index.tsx | 99 +------------- dinky-web/src/pages/DataStudio/model.ts | 3 +- dinky-web/src/services/endpoints.tsx | 5 +- 14 files changed, 419 insertions(+), 180 deletions(-) create mode 100644 dinky-web/src/pages/DataStudio/MiddleContainer/Editor/DiffModal.tsx create mode 100644 dinky-web/src/pages/DataStudio/MiddleContainer/Editor/constants.tsx create mode 100644 dinky-web/src/pages/DataStudio/MiddleContainer/Editor/index.less diff --git a/dinky-web/src/locales/en-US/pages.ts b/dinky-web/src/locales/en-US/pages.ts index f880ce1804..bcf5033482 100644 --- a/dinky-web/src/locales/en-US/pages.ts +++ b/dinky-web/src/locales/en-US/pages.ts @@ -309,6 +309,11 @@ export default { 'pages.datastudio.editor.stop.job': 'Stop job', 'pages.datastudio.editor.stop.jobConfirm': 'Are you sure to stop the job [{jobName}]? ', 'pages.datastudio.editor.submitting': 'The new task [{jobName}] is executing', + 'pages.datastudio.editor.onlyread': + 'Task has been published, modification is prohibited, please go offline first', + 'pages.datastudio.editor.notsave': 'Current changes are not saved! ', + 'pages.datastudio.editor.notsave.note': + 'Continue will discard the changes, are you sure to continue?', 'pages.datastudio.to.jobDetail': 'Operation Center', 'pages.datastudio.explain.validate': 'Validating...', 'pages.datastudio.explain.validate.allright': 'All Right', @@ -323,6 +328,21 @@ export default { 'pages.datastudio.footer.codeType': 'Code Type', 'pages.datastudio.footer.lineSeparator': 'Line Separator', 'pages.datastudio.footer.memDetails': 'Maximum heap size', + + 'pages.datastudio.sql.sqlChanged': 'Code recovery', + 'pages.datastudio.sql.sqlChangedPrompt': + 'The code you modified is not saved, it is inconsistent with the server, we cached the last modification for you, please select the version', + 'pages.datastudio.sql.sqldiff.title': 'Code comparison', + 'pages.datastudio.sql.paramdiff.title': 'Configuration comparison', + 'pages.datastudio.sql.nochange': 'No change in job content', + 'pages.datastudio.sql.useCache': 'Use cached version', + 'pages.datastudio.sql.useServer': 'Use server version', + 'pages.datastudio.sql.cacheVersion': 'Local cache code', + 'pages.datastudio.sql.serverVersion': 'server-side code', + 'pages.datastudio.sql.configItem': 'Configuration Item', + 'pages.datastudio.sql.cacheConfigItem': 'Local cache configuration', + 'pages.datastudio.sql.serverConfigItem': 'server-side configuration', + 'pages.datastudio.help.sqlChanged': 'Sql context or configure changed', 'pages.datastudio.help.sqlChangedPrompt': 'the remote context is not the same as the current page , will the data be flushed?', diff --git a/dinky-web/src/locales/zh-CN/pages.ts b/dinky-web/src/locales/zh-CN/pages.ts index 4b454cc305..9da8732a37 100644 --- a/dinky-web/src/locales/zh-CN/pages.ts +++ b/dinky-web/src/locales/zh-CN/pages.ts @@ -302,6 +302,9 @@ export default { 'pages.datastudio.editor.stop.job': '停止作业', 'pages.datastudio.editor.stop.jobConfirm': '确定停止作业【{jobName}】吗?', 'pages.datastudio.editor.submitting': '新任务【{jobName}】正在执行', + 'pages.datastudio.editor.onlyread': '任务已发布,禁止修改,请先下线任务', + 'pages.datastudio.editor.notsave': '当前修改内容未保存!', + 'pages.datastudio.editor.notsave.note': '继续将抛弃所修改内容,确定继续吗?', 'pages.datastudio.to.jobDetail': '运维', 'pages.datastudio.explain.validate': '正在校验中...', 'pages.datastudio.explain.validate.allright': '全部正确', @@ -316,8 +319,21 @@ export default { 'pages.datastudio.footer.codeType': '代码类型:', 'pages.datastudio.footer.lineSeparator': '行分隔符:', 'pages.datastudio.footer.memDetails': '最大堆大小:{max}\n已使用: {used}M', - 'pages.datastudio.help.sqlChanged': 'Sql内容或配置变更', - 'pages.datastudio.help.sqlChangedPrompt': '检测到当前页与远端不一致,是否刷新更新数据?', + + 'pages.datastudio.sql.sqlChanged': '代码恢复', + 'pages.datastudio.sql.sqlChangedPrompt': + '您修改的代码未保存,与服务端不一致,我们为您缓存了上次修改,请选择版本', + 'pages.datastudio.sql.sqldiff.title': '代码对比', + 'pages.datastudio.sql.paramdiff.title': '配置对比', + 'pages.datastudio.sql.nochange': '作业内容无变化', + 'pages.datastudio.sql.useCache': '使用缓存版本', + 'pages.datastudio.sql.useServer': '使用服务器版本', + 'pages.datastudio.sql.cacheVersion': '本地缓存代码', + 'pages.datastudio.sql.serverVersion': '服务端代码', + 'pages.datastudio.sql.configItem': '配置项', + 'pages.datastudio.sql.cacheConfigItem': '本地缓存配置', + 'pages.datastudio.sql.serverConfigItem': '服务端配置', + 'pages.datastudio.label.execConfig': '执行配置', 'pages.datastudio.label.execConfig.autostop': '自动停止', 'pages.datastudio.label.execConfig.autostop.tip': diff --git a/dinky-web/src/pages/DataStudio/HeaderContainer/index.tsx b/dinky-web/src/pages/DataStudio/HeaderContainer/index.tsx index 38d04b0708..2ca4e7f6a0 100644 --- a/dinky-web/src/pages/DataStudio/HeaderContainer/index.tsx +++ b/dinky-web/src/pages/DataStudio/HeaderContainer/index.tsx @@ -38,6 +38,7 @@ import { DataStudioTabsItemType, MetadataTabsItemType, StateType, + TabsPageSubType, TabsPageType, TaskDataType, VIEW @@ -126,8 +127,10 @@ const HeaderContainer = (props: any) => { }, [dsConfig]); const handleSave = async () => { - await handlePutDataJson('/api/task', currentData); + const saved = await handlePutDataJson('/api/task', currentData); saveTabs({ ...props.tabs }); + if (currentTab) currentTab.isModified = false; + return saved; }; const handlerStop = () => { @@ -151,23 +154,24 @@ const HeaderContainer = (props: any) => { const handlerSubmit = async () => { if (!currentData) return; + const saved = currentData.step == JOB_LIFE_CYCLE.ONLINE ? true : await handleSave(); + if (saved) { + const res = await executeSql( + l('pages.datastudio.editor.submitting', '', { jobName: currentData.name }), + currentData.id + ); + if (!res) return; - const res = await executeSql( - l('pages.datastudio.editor.submitting', '', { jobName: currentData.name }), - currentData.id - ); - if (!res) return; - - updateJobRunningMsg({ - taskId: currentData.id, - jobName: currentData.name, - jobState: res.datas.status, - runningLog: res.msg - }); - - messageApi.success(l('pages.datastudio.editor.exec.success')); - currentData.status = JOB_STATUS.RUNNING; - saveTabs({ ...props.tabs }); + updateJobRunningMsg({ + taskId: currentData.id, + jobName: currentData.name, + jobState: res.datas.status, + runningLog: res.msg + }); + messageApi.success(l('pages.datastudio.editor.exec.success')); + currentData.status = JOB_STATUS.RUNNING; + saveTabs({ ...props.tabs }); + } }; const handleChangeJobLife = async () => { @@ -176,8 +180,11 @@ const HeaderContainer = (props: any) => { await offLinelTask(currentData.id); currentData.step = JOB_LIFE_CYCLE.DEVELOP; } else { - await onLineTask(currentData.id); - currentData.step = JOB_LIFE_CYCLE.ONLINE; + const saved = await handleSave(); + if (saved) { + await onLineTask(currentData.id); + currentData.step = JOB_LIFE_CYCLE.ONLINE; + } } saveTabs({ ...props.tabs }); }; @@ -253,7 +260,7 @@ const HeaderContainer = (props: any) => { isShow: currentTab?.type == TabsPageType.project && currentData?.jobInstanceId && - currentTab.subType == 'flinksql', + currentTab.subType == TabsPageSubType.flinkSql, props: { href: `/#/devops/job-detail?id=${currentData?.jobInstanceId}`, target: '_blank' diff --git a/dinky-web/src/pages/DataStudio/LeftContainer/Project/index.tsx b/dinky-web/src/pages/DataStudio/LeftContainer/Project/index.tsx index 9a820b86e4..2710c32489 100644 --- a/dinky-web/src/pages/DataStudio/LeftContainer/Project/index.tsx +++ b/dinky-web/src/pages/DataStudio/LeftContainer/Project/index.tsx @@ -25,7 +25,6 @@ import { import FolderModal from '@/pages/DataStudio/LeftContainer/Project/FolderModal'; import JobModal from '@/pages/DataStudio/LeftContainer/Project/JobModal'; import JobTree from '@/pages/DataStudio/LeftContainer/Project/JobTree'; -import { getTaskDetails } from '@/pages/DataStudio/LeftContainer/Project/service'; import { StateType, STUDIO_MODEL, STUDIO_MODEL_ASYNC } from '@/pages/DataStudio/model'; import { handleAddOrUpdate, @@ -108,24 +107,21 @@ const Project: React.FC = (props: connect) => { return; } - getTaskDetails(taskId).then((res) => { - path.pop(); - dispatch({ - type: STUDIO_MODEL.addTab, - payload: { - icon: type, - id: parentId + name, - treeKey: key, - breadcrumbLabel: path.join('/'), - label: name, - params: { - taskId: taskId, - taskData: { ...res, useResult: true, maxRowNum: 100 } - }, - type: 'project', - subType: type.toLowerCase() - } - }); + path.pop(); + dispatch({ + type: STUDIO_MODEL.addTab, + payload: { + icon: type, + id: parentId + name, + treeKey: key, + breadcrumbLabel: path.join('/'), + label: name, + params: { + taskId: taskId + }, + type: 'project', + subType: type + } }); }; diff --git a/dinky-web/src/pages/DataStudio/MiddleContainer/Editor/DiffModal.tsx b/dinky-web/src/pages/DataStudio/MiddleContainer/Editor/DiffModal.tsx new file mode 100644 index 0000000000..ce841c707a --- /dev/null +++ b/dinky-web/src/pages/DataStudio/MiddleContainer/Editor/DiffModal.tsx @@ -0,0 +1,127 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { + DIFF_EDITOR_PARAMS, + PARAM_DIFF_TABLE_COL +} from '@/pages/DataStudio/MiddleContainer/Editor/constants'; +import { l } from '@/utils/intl'; +import { DiffEditor } from '@monaco-editor/react'; +import { Col, Modal, Row, Space, Table, Tabs, Typography } from 'antd'; +import React from 'react'; +import styles from './index.less'; + +const { Text, Link } = Typography; + +export type DiffModalProps = { + diffs: any[]; + open: boolean; + fileName?: string; + onUse: (server: boolean) => void; +}; + +const DiffModal: React.FC = (props) => { + const { diffs, open, fileName, onUse } = props; + // Find the diff object with key 'statement' + const statementDiff = diffs.find((diff) => diff.key === 'statement'); + // Filter out the diff objects with key other than 'statement' + const paramDiff = diffs.filter((diff) => diff.key != 'statement'); + + // Render the statement diff section + const renderStatementDiff = () => { + return ( + <> + {statementDiff ? ( +
+ + + {l('pages.datastudio.sql.serverVersion')} + + + {l('pages.datastudio.sql.cacheVersion')} + + + +
+ ) : ( + + {l('pages.datastudio.sql.nochange')} + + )} + + ); + }; + + // Render the parameter diff section + const renderParamDiff = () => { + return ( +
+ {paramDiff.length > 0 ? ( + + ) : ( + + {l('pages.datastudio.sql.nochange')} + + )} + + ); + }; + + return ( + + {l('pages.datastudio.sql.sqlChanged')}-{fileName} + + } + open={open} + footer={null} + width={'60%'} + > +
+ {l('pages.datastudio.sql.sqlChangedPrompt')} +
+ + onUse(false)}>{l('pages.datastudio.sql.useCache')} + onUse(true)}>{l('pages.datastudio.sql.useServer')} + + } + items={[ + { + key: '1', + label: l('pages.datastudio.sql.sqldiff.title'), + children: renderStatementDiff() + }, + { + key: '2', + label: l('pages.datastudio.sql.paramdiff.title'), + children: renderParamDiff() + } + ]} + /> +
+ ); +}; +export default DiffModal; diff --git a/dinky-web/src/pages/DataStudio/MiddleContainer/Editor/constants.tsx b/dinky-web/src/pages/DataStudio/MiddleContainer/Editor/constants.tsx new file mode 100644 index 0000000000..5b0c43b4f4 --- /dev/null +++ b/dinky-web/src/pages/DataStudio/MiddleContainer/Editor/constants.tsx @@ -0,0 +1,53 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +import { l } from '@/utils/intl'; + +export const PARAM_DIFF_TABLE_COL = [ + { title: l('pages.datastudio.sql.configItem'), key: 'key', dataIndex: 'key' }, + { title: l('pages.datastudio.sql.cacheConfigItem'), key: 'cache', dataIndex: 'cache' }, + { title: l('pages.datastudio.sql.serverConfigItem'), key: 'server', dataIndex: 'server' } +]; + +export const DIFF_EDITOR_PARAMS = { + width: '100%', + height: '90%', + options: { + readOnly: true, + selectOnLineNumbers: true, + lineDecorationsWidth: 20, + mouseWheelZoom: true, + automaticLayout: true + }, + language: 'sql', + theme: 'vs-dark' +}; + +export const TASK_VAR_FILTER = [ + 'updateTime', + 'createTime', + 'jobInstanceId', + 'useResult', + 'maxRowNum', + 'useChangeLog', + 'useAutoCancel', + 'status', + 'step', + 'jobConfig' +]; diff --git a/dinky-web/src/pages/DataStudio/MiddleContainer/Editor/index.less b/dinky-web/src/pages/DataStudio/MiddleContainer/Editor/index.less new file mode 100644 index 0000000000..7da2c31079 --- /dev/null +++ b/dinky-web/src/pages/DataStudio/MiddleContainer/Editor/index.less @@ -0,0 +1,44 @@ +/* + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +.no_diff_content { + display: flex; + align-items: center; + justify-content: center; + height: 50vh; + text-align: center; +} + +.diff_content { + height: 50vh; + padding: 10px; + border: 1px solid #fdfdfd1f; +} + +.header { + border-bottom: 1px dotted #1677ff; + border-left: 5px solid #1677ff; + border-radius: 0; + padding-inline-start: 5px; +} + +.use_version_btn { + float: right; + margin-right: 20px; +} diff --git a/dinky-web/src/pages/DataStudio/MiddleContainer/Editor/index.tsx b/dinky-web/src/pages/DataStudio/MiddleContainer/Editor/index.tsx index 4f633dfdf5..d5524f5db9 100644 --- a/dinky-web/src/pages/DataStudio/MiddleContainer/Editor/index.tsx +++ b/dinky-web/src/pages/DataStudio/MiddleContainer/Editor/index.tsx @@ -1,48 +1,93 @@ -import { getCurrentData } from '@/pages/DataStudio/function'; -import { StateType, STUDIO_MODEL } from '@/pages/DataStudio/model'; -import { connect } from '@@/exports'; +import { getCurrentTab } from '@/pages/DataStudio/function'; +import { TASK_VAR_FILTER } from '@/pages/DataStudio/MiddleContainer/Editor/constants'; +import DiffModal from '@/pages/DataStudio/MiddleContainer/Editor/DiffModal'; +import { + DataStudioTabsItemType, + StateType, + STUDIO_MODEL, + TaskDataType +} from '@/pages/DataStudio/model'; +import { JOB_LIFE_CYCLE } from '@/pages/DevOps/constants'; +import { API_CONSTANTS } from '@/services/endpoints'; +import { l } from '@/utils/intl'; +import { connect, useRequest } from '@@/exports'; import { Editor } from '@monaco-editor/react'; +import { Spin } from 'antd'; import { editor } from 'monaco-editor'; -import React from 'react'; +import React, { useState } from 'react'; export type EditorProps = { - statement: string; + taskId: number; }; const CodeEditor: React.FC = (props) => { const { - statement, + taskId, tabs: { panes, activeKey }, dispatch } = props; - const current = getCurrentData(panes, activeKey); + + const [isModalOpen, setIsModalOpen] = useState(false); + const [diff, setDiff] = useState([]); + + const currentTab = getCurrentTab(panes, activeKey) as DataStudioTabsItemType; + const currentData = currentTab.params.taskData; + + const loadTask = (cache: TaskDataType, serverParams: TaskDataType) => { + if (!cache) { + currentTab.params.taskData = { ...serverParams, useResult: true, maxRowNum: 100 }; + dispatch({ type: STUDIO_MODEL.saveTabs, payload: { ...props.tabs } }); + return; + } + const diff: any[] = []; + Object.keys(serverParams).forEach((key) => { + if (TASK_VAR_FILTER.includes(key)) { + cache[key] = serverParams[key]; + } else if (JSON.stringify(serverParams[key]) !== JSON.stringify(cache[key])) { + diff.push({ key: key, server: serverParams[key], cache: cache[key] }); + } + }); + if (diff.length > 0) { + setDiff(diff); + setIsModalOpen(true); + } + }; + + const { loading, data } = useRequest( + { url: API_CONSTANTS.TASK, params: { id: taskId } }, + { onSuccess: (data: any) => loadTask(currentTab.params.taskData, data) } + ); + + const upDateTask = (useServerVersion: boolean) => { + if (useServerVersion) { + currentTab.params.taskData = { ...data, useResult: true, maxRowNum: 100 }; + currentTab.isModified = false; + } else { + currentTab.isModified = true; + } + dispatch({ type: STUDIO_MODEL.saveTabs, payload: { ...props.tabs } }); + setIsModalOpen(false); + }; return ( - <> + + = (props) => { }); }} onChange={(v) => { - if (!current) { + if (!currentData || !currentTab) { return; } if (typeof v === 'string') { - current.statement = v; + currentData.statement = v; } - + currentTab.isModified = true; dispatch({ type: STUDIO_MODEL.saveTabs, payload: { ...props.tabs } @@ -79,18 +124,7 @@ const CodeEditor: React.FC = (props) => { }} theme={'vs-dark'} /> - - {/* {*/} - {/* current.statement = v;*/} - {/* dispatch({*/} - {/* type: STUDIO_MODEL.saveTabs,*/} - {/* payload: {...props.tabs},*/} - {/* });*/} - - {/* }}*/} - {/*/>*/} - + ); }; diff --git a/dinky-web/src/pages/DataStudio/MiddleContainer/index.tsx b/dinky-web/src/pages/DataStudio/MiddleContainer/index.tsx index da6c617eee..4eb1f8f599 100644 --- a/dinky-web/src/pages/DataStudio/MiddleContainer/index.tsx +++ b/dinky-web/src/pages/DataStudio/MiddleContainer/index.tsx @@ -18,7 +18,11 @@ import ContentScroll from '@/components/Scroll/ContentScroll'; import useThemeValue from '@/hooks/useThemeValue'; import { STUDIO_TAG_RIGHT_CONTEXT_MENU } from '@/pages/DataStudio/constants'; -import { isDataStudioTabsItemType, isMetadataTabsItemType } from '@/pages/DataStudio/function'; +import { + getCurrentTab, + isDataStudioTabsItemType, + isMetadataTabsItemType +} from '@/pages/DataStudio/function'; import Editor from '@/pages/DataStudio/MiddleContainer/Editor'; import { getTabIcon } from '@/pages/DataStudio/MiddleContainer/function'; import KeyBoard from '@/pages/DataStudio/MiddleContainer/KeyBoard'; @@ -26,11 +30,16 @@ import QuickGuide from '@/pages/DataStudio/MiddleContainer/QuickGuide'; import { StateType, STUDIO_MODEL, TabsItemType, TabsPageType } from '@/pages/DataStudio/model'; import { RightSide } from '@/pages/DataStudio/route'; import RightTagsRouter from '@/pages/RegCenter/DataSource/components/DataSourceDetail/RightTagsRouter'; +import { l } from '@/utils/intl'; import { connect } from '@@/exports'; -import { ConfigProvider, Divider, Dropdown, Space, Tabs } from 'antd'; +import { ExclamationCircleFilled } from '@ant-design/icons'; +import { ConfigProvider, Divider, Dropdown, Modal, Space, Tabs, Typography } from 'antd'; import { MenuInfo } from 'rc-menu/es/interface'; import React, { useState } from 'react'; +const { Text } = Typography; +const { confirm } = Modal; + type TargetKey = React.MouseEvent | React.KeyboardEvent | string; const MiddleContainer = (props: any) => { @@ -211,7 +220,7 @@ const MiddleContainer = (props: any) => { } const v = item.params; - return ; + return ; } if (isMetadataTabsItemType(item)) { @@ -232,7 +241,10 @@ const MiddleContainer = (props: any) => { key={item.key} > {getTabIcon(item.icon, 16)} - {item.label} + + {item.label} + {item.isModified ? '*' : ''} + ), children: ( @@ -247,7 +259,7 @@ const MiddleContainer = (props: any) => { * 关闭tab * @param {TargetKey} targetKey */ - const closeTab = (targetKey: TargetKey) => { + const handleCloseTab = (targetKey: string) => { if (panes.length === 1) { dispatch({ type: STUDIO_MODEL.updateSelectRightKey, @@ -260,6 +272,23 @@ const MiddleContainer = (props: any) => { payload: targetKey }); }; + const closeTab = (targetKey: TargetKey) => { + if (typeof targetKey == 'string') { + const tab = getCurrentTab(panes, targetKey); + if (tab?.isModified) { + confirm({ + title: l('pages.datastudio.editor.notsave'), + icon: , + content: l('pages.datastudio.editor.notsave.note'), + onOk() { + handleCloseTab(targetKey); + } + }); + } else { + handleCloseTab(targetKey); + } + } + }; /** * render middle content diff --git a/dinky-web/src/pages/DataStudio/RightContainer/JobConfig/index.tsx b/dinky-web/src/pages/DataStudio/RightContainer/JobConfig/index.tsx index 48257256ae..e2050a6ada 100644 --- a/dinky-web/src/pages/DataStudio/RightContainer/JobConfig/index.tsx +++ b/dinky-web/src/pages/DataStudio/RightContainer/JobConfig/index.tsx @@ -102,7 +102,7 @@ const JobConfig = (props: any) => { pane.params.taskData[key] = all[key]; } }); - + pane.isModified = true; dispatch({ type: STUDIO_MODEL.saveTabs, payload: { ...props.tabs } diff --git a/dinky-web/src/pages/DataStudio/function.ts b/dinky-web/src/pages/DataStudio/function.ts index cc7f044dab..a134c736a6 100644 --- a/dinky-web/src/pages/DataStudio/function.ts +++ b/dinky-web/src/pages/DataStudio/function.ts @@ -165,7 +165,7 @@ export const getFooterValue = (panes: any, activeKey: string): Partial { updateBottomConsole, saveSession, saveEnv, - saveTabs, updateCenterContentHeight, updateSelectLeftKey, updateSelectRightKey, @@ -80,8 +66,6 @@ const DataStudio = (props: any) => { } = props; const { token } = useToken(); const themeValue = useThemeValue(); - const [isModalUpdateTabContentOpen, setIsModalUpdateTabContentOpen] = useState(false); - const [newTabData, setNewTabData] = useState(); const app = getDvaApp(); // 获取dva的实例 const persist = app._store.persist; const bottomHeight = bottomContainer.selectKey === '' ? 0 : bottomContainer.height; @@ -112,23 +96,6 @@ const DataStudio = (props: any) => { return () => window.removeEventListener('resize', onResize); }, []); - useEffect(() => { - if (isModalUpdateTabContentOpen) { - Modal.confirm({ - title: l('pages.datastudio.help.sqlChanged'), - keyboard: true, - content: ( - <> - {' '} - {l('pages.datastudio.help.sqlChangedPrompt')} - - ), - onOk: updateTabContent, - onCancel: () => setIsModalUpdateTabContentOpen(false) - }); - } - }, [isModalUpdateTabContentOpen]); - const loadData = async () => { Promise.all([ getDataBase(), @@ -145,51 +112,6 @@ const DataStudio = (props: any) => { saveEnv(res[4]); saveClusterConfiguration(res[5]); }); - - // 判断是否需要更新tab内容 - if (!tabs.activeKey) { - return; - } - - const currentTab = getCurrentTab(tabs.panes, tabs.activeKey); - if (!isDataStudioTabsItemType(currentTab)) { - return; - } - - const params = currentTab.params; - const res = await getTaskDetails(params.taskId); - if (!res) { - return true; - } - - const info = res as { [key: string]: any }; - const changed = Object.keys(params.taskData).some((key) => { - // ignore this property - if ( - [ - 'updateTime', - 'createTime', - 'jobInstanceId', - 'useResult', - 'maxRowNum', - 'useChangeLog', - 'useAutoCancel' - ].includes(key) - ) { - return false; - } - - if (JSON.stringify(info[key]) !== JSON.stringify(params.taskData[key])) { - console.log('changed', key, info[key], params.taskData[key]); - return true; - } - return false; - }); - - if (changed) { - setIsModalUpdateTabContentOpen(true); - setNewTabData(res); - } }; useEffect(() => { @@ -197,19 +119,6 @@ const DataStudio = (props: any) => { onResize(); }, []); - const updateTabContent = () => { - const currentTab = getCurrentTab(tabs.panes, tabs.activeKey); - if (!isDataStudioTabsItemType(currentTab)) { - return; - } - - if (!newTabData) return; - - currentTab.params.taskData = newTabData; - saveTabs({ ...tabs }); - setIsModalUpdateTabContentOpen(false); - }; - const access = useAccess(); const LeftTopMenu = ( diff --git a/dinky-web/src/pages/DataStudio/model.ts b/dinky-web/src/pages/DataStudio/model.ts index 86c7e40821..423b62a406 100644 --- a/dinky-web/src/pages/DataStudio/model.ts +++ b/dinky-web/src/pages/DataStudio/model.ts @@ -103,6 +103,7 @@ export type TaskDataBaseType = { id: number; name: string; statement: string; + step: number; }; export type TaskDataType = TaskDataBaseType & Record; @@ -120,7 +121,7 @@ export enum TabsPageType { } export enum TabsPageSubType { - flinkSql = 'flinksql' + flinkSql = 'FlinkSql' } export interface TabsItemType { diff --git a/dinky-web/src/services/endpoints.tsx b/dinky-web/src/services/endpoints.tsx index e97e390c2b..31f434ad37 100644 --- a/dinky-web/src/services/endpoints.tsx +++ b/dinky-web/src/services/endpoints.tsx @@ -277,5 +277,8 @@ export enum API_CONSTANTS { SAVE_OR_UPDATE_TASK_URL = '/api/catalogue/saveOrUpdateCatalogueAndTask', SAVE_OR_UPDATE_CATALOGUE_URL = '/api/catalogue/saveOrUpdateCatalogue', COPY_TASK_URL = '/api/catalogue/copyTask', - MOVE_CATALOGUE_URL = '/api/catalogue/moveCatalogue' + MOVE_CATALOGUE_URL = '/api/catalogue/moveCatalogue', + + //task + TASK = '/api/task' }