From e49ce611d59ee6f7acca95130aa3ee2ded28ac3a Mon Sep 17 00:00:00 2001 From: wangyizhi Date: Sat, 19 Feb 2022 02:42:54 +0800 Subject: [PATCH] [Feature][UI Next] Workflow editing and cells deletion capabilities (#8435) * [Feature][UI Next] Workflow editing and cells deletion capabilities * [Feature][UI Next] Code format --- .../src/components/modal/index.tsx | 19 +++--- .../src/locales/modules/en_US.ts | 4 +- .../src/locales/modules/zh_CN.ts | 4 +- .../modules/process-definition/index.ts | 10 +-- .../modules/process-instances/index.ts | 7 ++- .../task/components/node/detail-modal.tsx | 15 ++++- .../projects/task/components/node/detail.tsx | 7 ++- .../projects/task/components/node/types.ts | 2 +- .../components/dag/dag-save-modal.tsx | 41 ++++++++++-- .../workflow/components/dag/dag-toolbar.tsx | 49 +++++++++++++-- .../workflow/components/dag/index.tsx | 14 +++-- .../projects/workflow/components/dag/types.ts | 3 +- .../workflow/components/dag/use-task-edit.ts | 54 ++++++++++------ .../workflow/definition/detail/index.tsx | 62 +++++++++++++++++-- .../instance/detail/index.module.scss | 40 ++++++++++++ .../workflow/instance/detail/index.tsx | 47 +++++++++++++- .../security/yarn-queue-manage/index.tsx | 13 +++- 17 files changed, 328 insertions(+), 63 deletions(-) create mode 100644 dolphinscheduler-ui-next/src/views/projects/workflow/instance/detail/index.module.scss diff --git a/dolphinscheduler-ui-next/src/components/modal/index.tsx b/dolphinscheduler-ui-next/src/components/modal/index.tsx index 33cd1ce838..1910d39e97 100644 --- a/dolphinscheduler-ui-next/src/components/modal/index.tsx +++ b/dolphinscheduler-ui-next/src/components/modal/index.tsx @@ -89,8 +89,15 @@ const Modal = defineComponent({ return { t, onCancel, onConfirm, onJumpLink } }, render() { - const { $slots, t, onCancel, onConfirm, confirmDisabled, confirmLoading, onJumpLink } = - this + const { + $slots, + t, + onCancel, + onConfirm, + confirmDisabled, + confirmLoading, + onJumpLink + } = this return ( ( {this.linkEventShow && ( - + {this.linkEventText} )} - ) - , + ), footer: () => ( {this.cancelShow && ( diff --git a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts index 6ce08e52d6..47b38d2eda 100644 --- a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts +++ b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts @@ -547,7 +547,9 @@ const project = { minute: 'Minute', key: 'Key', value: 'Value', - success: 'Success' + success: 'Success', + delete_cell: 'Delete selected edges and nodes', + online_directly: 'Whether to go online the process definition' }, node: { current_node_settings: 'Current node settings', diff --git a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts index 96892b9bea..6296344bd0 100644 --- a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts +++ b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts @@ -545,7 +545,9 @@ const project = { minute: '分', key: '键', value: '值', - success: '成功' + success: '成功', + delete_cell: '删除选中的线或节点', + online_directly: '是否上线流程定义' }, node: { current_node_settings: '当前节点设置', diff --git a/dolphinscheduler-ui-next/src/service/modules/process-definition/index.ts b/dolphinscheduler-ui-next/src/service/modules/process-definition/index.ts index 4671fa7a4b..6b3d3bccb5 100644 --- a/dolphinscheduler-ui-next/src/service/modules/process-definition/index.ts +++ b/dolphinscheduler-ui-next/src/service/modules/process-definition/index.ts @@ -157,13 +157,13 @@ export function queryProcessDefinitionByCode( }) } -export function update( - data: ProcessDefinitionReq & NameReq & ReleaseStateReq, - code: CodeReq, - processCode: CodeReq +export function updateProcessDefinition( + data: ProcessDefinitionReq & ReleaseStateReq, + code: number, + projectCode: number ): any { return axios({ - url: `/projects/${code}/process-definition/${processCode}`, + url: `/projects/${projectCode}/process-definition/${code}`, method: 'put', data }) diff --git a/dolphinscheduler-ui-next/src/service/modules/process-instances/index.ts b/dolphinscheduler-ui-next/src/service/modules/process-instances/index.ts index bc403b92f7..1910c8def1 100644 --- a/dolphinscheduler-ui-next/src/service/modules/process-instances/index.ts +++ b/dolphinscheduler-ui-next/src/service/modules/process-instances/index.ts @@ -82,9 +82,12 @@ export function queryTopNLongestRunningProcessInstance( }) } -export function queryProcessInstanceById(id: IdReq, code: CodeReq): any { +export function queryProcessInstanceById( + instanceId: number, + projectCode: number +): any { return axios({ - url: `/projects/${code}/process-instances/${id}`, + url: `/projects/${projectCode}/process-instances/${instanceId}`, method: 'get' }) } diff --git a/dolphinscheduler-ui-next/src/views/projects/task/components/node/detail-modal.tsx b/dolphinscheduler-ui-next/src/views/projects/task/components/node/detail-modal.tsx index 3cc2ee3505..c69c56bfbf 100644 --- a/dolphinscheduler-ui-next/src/views/projects/task/components/node/detail-modal.tsx +++ b/dolphinscheduler-ui-next/src/views/projects/task/components/node/detail-modal.tsx @@ -64,7 +64,7 @@ const NodeDetailModal = defineComponent({ detailRef: ref(), linkEventShowRef: ref(), linkEventTextRef: ref(), - linkUrlRef: ref(), + linkUrlRef: ref() }) const onConfirm = async () => { @@ -103,8 +103,17 @@ const NodeDetailModal = defineComponent({ } }, render() { - const { t, show, onConfirm, onCancel, projectCode, data, readonly, from, onJumpLink } = - this + const { + t, + show, + onConfirm, + onCancel, + projectCode, + data, + readonly, + from, + onJumpLink + } = this return ( { name?: string processName?: number - taskPriority?: number + taskPriority?: string timeoutFlag: 'OPEN' | 'CLOSE' timeoutNotifyStrategy?: string | [] taskParams: { diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-save-modal.tsx b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-save-modal.tsx index 255ff444b2..bef3fdf096 100644 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-save-modal.tsx +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-save-modal.tsx @@ -15,7 +15,7 @@ * limitations under the License. */ -import { defineComponent, PropType, ref, computed, onMounted } from 'vue' +import { defineComponent, PropType, ref, computed, onMounted, watch } from 'vue' import Modal from '@/components/modal' import { useI18n } from 'vue-i18n' import { @@ -25,16 +25,22 @@ import { NSelect, NSwitch, NInputNumber, - NDynamicInput + NDynamicInput, + NCheckbox } from 'naive-ui' import { queryTenantList } from '@/service/modules/tenants' -import { SaveForm } from './types' +import { SaveForm, WorkflowDefinition } from './types' import './x6-style.scss' const props = { visible: { type: Boolean as PropType, default: false + }, + // If this prop is passed, it means from definition detail + definition: { + type: Object as PropType, + default: undefined } } @@ -74,7 +80,8 @@ export default defineComponent({ tenantCode: 'default', timeoutFlag: false, timeout: 0, - globalParams: [] + globalParams: [], + release: false }) const formRef = ref() const rule = { @@ -89,6 +96,25 @@ export default defineComponent({ context.emit('update:show', false) } + watch( + () => props.definition, + () => { + const process = props.definition?.processDefinition + if (process) { + formValue.value.name = process.name + formValue.value.description = process.description + formValue.value.tenantCode = process.tenantCode + if (process.timeout && process.timeout > 0) { + formValue.value.timeoutFlag = true + formValue.value.timeout = process.timeout + } + formValue.value.globalParams = process.globalParamList.map( + (param) => ({ key: param.prop, value: param.value }) + ) + } + } + ) + return () => ( + {props.definition && ( + + + {t('project.dag.online_directly')} + + + )} ) diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-toolbar.tsx b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-toolbar.tsx index 93ed86afbf..0c97dfef1d 100644 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-toolbar.tsx +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-toolbar.tsx @@ -15,7 +15,7 @@ * limitations under the License. */ -import { defineComponent, ref, inject, PropType } from 'vue' +import { defineComponent, ref, inject, PropType, Ref } from 'vue' import { useI18n } from 'vue-i18n' import Styles from './dag.module.scss' import { NTooltip, NIcon, NButton, NSelect } from 'naive-ui' @@ -26,13 +26,15 @@ import { FullscreenExitOutlined, InfoCircleOutlined, FormatPainterOutlined, - CopyOutlined + CopyOutlined, + DeleteOutlined } from '@vicons/antd' import { useNodeSearch, useTextCopy } from './dag-hooks' import { DataUri } from '@antv/x6' import { useFullscreen } from '@vueuse/core' import { useRouter } from 'vue-router' import { useThemeStore } from '@/store/theme/theme' +import type { Graph } from '@antv/x6' const props = { layoutToggle: { @@ -50,13 +52,13 @@ const props = { export default defineComponent({ name: 'workflow-dag-toolbar', props, - emits: ['versionToggle', 'saveModelToggle'], + emits: ['versionToggle', 'saveModelToggle', 'removeTasks'], setup(props, context) { const { t } = useI18n() const themeStore = useThemeStore() - const graph = inject('graph', ref()) + const graph = inject>('graph', ref()) const router = useRouter() /** @@ -124,6 +126,22 @@ export default defineComponent({ */ const { copy } = useTextCopy() + /** + * Delete selected edges and nodes + */ + const removeCells = () => { + if (graph.value) { + const cells = graph.value.getSelectedCells() + if (cells) { + graph.value?.removeCells(cells) + const codes = cells + .filter((cell) => cell.isNode()) + .map((cell) => +cell.id) + context.emit('removeTasks', codes) + } + } + } + return () => (
t('project.dag.download_png') }} > + {/* Delete */} + ( + removeCells()} + v-slots={{ + icon: () => ( + + + + ) + }} + /> + ), + default: () => t('project.dag.delete_cell') + }} + > {/* Toggle fullscreen */}
@@ -163,11 +165,15 @@ export default defineComponent({ onUpdateList={refreshDetail} /> )} - + diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/types.ts b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/types.ts index 3e6b97f8b2..c6dbf1065c 100644 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/types.ts +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/types.ts @@ -79,7 +79,7 @@ export interface TaskDefinition { environmentCode: number failRetryTimes: number failRetryInterval: number - timeoutFlag: string + timeoutFlag: 'OPEN' | 'CLOSE' timeoutNotifyStrategy: string timeout: number delayTime: number @@ -125,6 +125,7 @@ export interface SaveForm { timeoutFlag: boolean timeout: number globalParams: GlobalParam[] + release: boolean } export interface Location { diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-task-edit.ts b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-task-edit.ts index 9306e34f25..d4f91c09f2 100644 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-task-edit.ts +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-task-edit.ts @@ -15,15 +15,18 @@ * limitations under the License. */ -import { ref, onMounted } from 'vue' +import { ref, onMounted, watch } from 'vue' import type { Ref } from 'vue' import type { Graph } from '@antv/x6' import type { Coordinate, NodeData } from './types' import { TaskType } from '@/views/projects/task/constants/task-type' +import { formatParams } from '@/views/projects/task/components/node/format-data' import { useCellUpdate } from './dag-hooks' +import { WorkflowDefinition } from './types' interface Options { graph: Ref + definition: Ref } /** @@ -32,7 +35,7 @@ interface Options { * @returns */ export function useTaskEdit(options: Options) { - const { graph } = options + const { graph, definition } = options const { addNode, setNodeName } = useCellUpdate({ graph }) @@ -57,6 +60,16 @@ export function useTaskEdit(options: Options) { openTaskModal({ code, taskType: type, name: '' }) } + /** + * Remove task + * @param {number} code + */ + function removeTasks(codes: number[]) { + taskDefinitions.value = taskDefinitions.value.filter( + (task) => !codes.includes(task.code) + ) + } + function openTaskModal(task: NodeData) { currTask.value = task taskModalVisible.value = true @@ -67,25 +80,21 @@ export function useTaskEdit(options: Options) { * @param formRef * @param from */ - function taskConfirm({ formRef, form }: any) { - formRef.validate((errors: any) => { - if (!errors) { - // override target config - taskDefinitions.value = taskDefinitions.value.map((task) => { - if (task.code === currTask.value?.code) { - setNodeName(task.code + '', form.name) - console.log(form) - console.log(JSON.stringify(form)) - return { - code: task.code, - ...form - } - } - return task - }) - taskModalVisible.value = false + function taskConfirm({ data }: any) { + const taskDef = formatParams(data).taskDefinitionJsonObj as NodeData + // override target config + taskDefinitions.value = taskDefinitions.value.map((task) => { + if (task.code === currTask.value?.code) { + setNodeName(task.code + '', taskDef.name) + return { + ...taskDef, + code: task.code, + taskType: currTask.value.taskType + } } + return task }) + taskModalVisible.value = false } /** @@ -108,12 +117,17 @@ export function useTaskEdit(options: Options) { } }) + watch(definition, () => { + taskDefinitions.value = definition.value?.taskDefinitionList || [] + }) + return { currTask, taskModalVisible, taskConfirm, taskCancel, appendTask, - taskDefinitions + taskDefinitions, + removeTasks } } diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/detail/index.tsx b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/detail/index.tsx index 1342cb9399..daf9bbfd9c 100644 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/detail/index.tsx +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/detail/index.tsx @@ -16,18 +16,39 @@ */ import { defineComponent, onMounted, ref } from 'vue' -import { useRoute } from 'vue-router' +import { useRoute, useRouter } from 'vue-router' import { useThemeStore } from '@/store/theme/theme' +import { useMessage } from 'naive-ui' +import { useI18n } from 'vue-i18n' import Dag from '../../components/dag' -import { queryProcessDefinitionByCode } from '@/service/modules/process-definition' -import { WorkflowDefinition } from '../../components/dag/types' +import { + queryProcessDefinitionByCode, + updateProcessDefinition +} from '@/service/modules/process-definition' +import { + WorkflowDefinition, + SaveForm, + TaskDefinition, + Connect, + Location +} from '../../components/dag/types' import Styles from './index.module.scss' +interface SaveData { + saveForm: SaveForm + taskDefinitions: TaskDefinition[] + connects: Connect[] + locations: Location[] +} + export default defineComponent({ name: 'WorkflowDefinitionDetails', setup() { const theme = useThemeStore() const route = useRoute() + const router = useRouter() + const message = useMessage() + const { t } = useI18n() const projectCode = Number(route.params.projectCode) const code = Number(route.params.code) @@ -39,7 +60,40 @@ export default defineComponent({ }) } - const save = () => {} + const save = ({ + taskDefinitions, + saveForm, + connects, + locations + }: SaveData) => { + const globalParams = saveForm.globalParams.map((p) => { + return { + prop: p.key, + value: p.value, + direct: 'IN', + type: 'VARCHAR' + } + }) + + updateProcessDefinition( + { + taskDefinitionJson: JSON.stringify(taskDefinitions), + taskRelationJson: JSON.stringify(connects), + locations: JSON.stringify(locations), + name: saveForm.name, + tenantCode: saveForm.tenantCode, + description: saveForm.description, + globalParams: JSON.stringify(globalParams), + timeout: saveForm.timeoutFlag ? saveForm.timeout : 0, + releaseState: saveForm.release ? 'ONLINE' : 'OFFLINE' + }, + code, + projectCode + ).then((res: any) => { + message.success(t('project.dag.success')) + router.push({ path: `/projects/${projectCode}/workflow-definition` }) + }) + } onMounted(() => { if (!code || !projectCode) return diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/instance/detail/index.module.scss b/dolphinscheduler-ui-next/src/views/projects/workflow/instance/detail/index.module.scss new file mode 100644 index 0000000000..38be4c83e8 --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/instance/detail/index.module.scss @@ -0,0 +1,40 @@ +/* + * 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. + */ + +$borderDark: rgba(255, 255, 255, 0.09); +$borderLight: rgb(239, 239, 245); +$bgDark: rgb(24, 24, 28); +$bgLight: #ffffff; + +.container { + width: 100%; + padding: 20px; + box-sizing: border-box; + height: calc(100vh - 100px); + overflow: hidden; + display: block; +} + +.dark { + border: solid 1px $borderDark; + background-color: $bgDark; +} + +.light { + border: solid 1px $borderLight; + background-color: $bgLight; +} diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/instance/detail/index.tsx b/dolphinscheduler-ui-next/src/views/projects/workflow/instance/detail/index.tsx index 01d107e883..3f19885984 100644 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/instance/detail/index.tsx +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/instance/detail/index.tsx @@ -15,11 +15,54 @@ * limitations under the License. */ -import { defineComponent } from 'vue' +import { defineComponent, onMounted, ref } from 'vue' +import { useRoute } from 'vue-router' +import { useThemeStore } from '@/store/theme/theme' +import Dag from '../../components/dag' +// import { queryProcessDefinitionByCode } from '@/service/modules/process-definition' +import { queryProcessInstanceById } from '@/service/modules/process-instances' +import { WorkflowDefinition } from '../../components/dag/types' +import Styles from './index.module.scss' export default defineComponent({ name: 'WorkflowInstanceDetails', setup() { - return () =>
WorkflowInstanceDetails
+ const theme = useThemeStore() + const route = useRoute() + const projectCode = Number(route.params.projectCode) + const id = Number(route.params.id) + + const definition = ref() + + const refresh = () => { + queryProcessInstanceById(id, projectCode).then((res: any) => { + if (res.dagData) { + definition.value = res.dagData + } + }) + } + + const save = () => {} + + onMounted(() => { + if (!id || !projectCode) return + refresh() + }) + + return () => ( +
+ +
+ ) } }) diff --git a/dolphinscheduler-ui-next/src/views/security/yarn-queue-manage/index.tsx b/dolphinscheduler-ui-next/src/views/security/yarn-queue-manage/index.tsx index 95abe1386d..9b9456be1f 100644 --- a/dolphinscheduler-ui-next/src/views/security/yarn-queue-manage/index.tsx +++ b/dolphinscheduler-ui-next/src/views/security/yarn-queue-manage/index.tsx @@ -106,7 +106,12 @@ const yarnQueueManage = defineComponent({
- + {t('security.yarn_queue.create_queue')}
@@ -130,7 +135,11 @@ const yarnQueueManage = defineComponent({
- +