From 4b06b760686931a201a2f1adccb14f7368525b27 Mon Sep 17 00:00:00 2001 From: wangyizhi Date: Sun, 13 Feb 2022 20:16:58 +0800 Subject: [PATCH] [Feature][UI Next] Create workflow (#8362) --- .../src/locales/modules/en_US.ts | 3 +- .../src/locales/modules/zh_CN.ts | 3 +- .../modules/process-definition/index.ts | 6 +- .../modules/process-definition/types.ts | 3 +- .../src/views/projects/node/detail-modal.tsx | 76 ++++++++--- .../src/views/projects/node/detail.tsx | 7 +- .../src/views/projects/node/types.ts | 22 +--- .../workflow/components/dag/dag-hooks.ts | 8 +- .../components/dag/dag-save-modal.tsx | 3 +- .../workflow/components/dag/dag-sidebar.tsx | 7 +- .../workflow/components/dag/index.tsx | 47 ++++++- .../projects/workflow/components/dag/types.ts | 69 +++++++--- .../components/dag/use-business-mapper.ts | 118 +++++++++++++++++ .../workflow/components/dag/use-cell-query.ts | 54 -------- .../components/dag/use-cell-update.ts | 5 +- .../components/dag/use-custom-cell-builder.ts | 6 +- .../components/dag/use-dag-drag-drop.ts | 21 ++-- .../components/dag/use-node-search.ts | 9 +- .../workflow/components/dag/use-task-edit.ts | 119 ++++++++++++++++++ .../workflow/definition/create/index.tsx | 58 ++++++++- .../workflow/definition/detail/index.tsx | 9 +- 21 files changed, 502 insertions(+), 151 deletions(-) create mode 100644 dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-business-mapper.ts delete mode 100644 dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-cell-query.ts create mode 100644 dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-task-edit.ts diff --git a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts index b308e793fb..d52c90ba06 100644 --- a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts +++ b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts @@ -545,7 +545,8 @@ const project = { basic_info: 'Basic Information', minute: 'Minute', key: 'Key', - value: 'Value' + value: 'Value', + success: 'Success' }, 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 fe1bd9c23b..4728ca4a33 100644 --- a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts +++ b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts @@ -543,7 +543,8 @@ const project = { basic_info: '基本信息', minute: '分', key: '键', - value: '值' + value: '值', + success: '成功' }, 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 a6d13135bb..4671fa7a4b 100644 --- a/dolphinscheduler-ui-next/src/service/modules/process-definition/index.ts +++ b/dolphinscheduler-ui-next/src/service/modules/process-definition/index.ts @@ -39,11 +39,11 @@ export function queryListPaging(params: PageReq & ListReq, code: number): any { } export function createProcessDefinition( - data: ProcessDefinitionReq & NameReq, - code: CodeReq + data: ProcessDefinitionReq, + projectCode: number ): any { return axios({ - url: `/projects/${code}/process-definition`, + url: `/projects/${projectCode}/process-definition`, method: 'post', data }) diff --git a/dolphinscheduler-ui-next/src/service/modules/process-definition/types.ts b/dolphinscheduler-ui-next/src/service/modules/process-definition/types.ts index 2e9949a25f..17cb4bc0d0 100644 --- a/dolphinscheduler-ui-next/src/service/modules/process-definition/types.ts +++ b/dolphinscheduler-ui-next/src/service/modules/process-definition/types.ts @@ -53,7 +53,8 @@ interface ListReq extends PageReq { userId?: number } -interface ProcessDefinitionReq extends NameReq { +interface ProcessDefinitionReq { + name: string locations: string taskDefinitionJson: string taskRelationJson: string diff --git a/dolphinscheduler-ui-next/src/views/projects/node/detail-modal.tsx b/dolphinscheduler-ui-next/src/views/projects/node/detail-modal.tsx index 51d9a1b3bb..dfc1e66a71 100644 --- a/dolphinscheduler-ui-next/src/views/projects/node/detail-modal.tsx +++ b/dolphinscheduler-ui-next/src/views/projects/node/detail-modal.tsx @@ -19,37 +19,71 @@ import { defineComponent, PropType, ref } from 'vue' import { useI18n } from 'vue-i18n' import Modal from '@/components/modal' import Detail from './detail' -import type { IDataNode, ITask } from './types' +import type { NodeData } from '@/views/projects/workflow/components/dag/types' const props = { show: { type: Boolean as PropType, default: false }, - nodeData: { - type: Object as PropType, - default: { - taskType: 'SHELL' - } + taskDefinition: { + type: Object as PropType, + default: { code: 0, taskType: 'SHELL', name: '' } }, - type: { - type: String as PropType, - default: '' + projectCode: { + type: Number as PropType, + required: true }, - taskDefinition: { - type: Object as PropType + readonly: { + type: Boolean as PropType, + default: false } } const NodeDetailModal = defineComponent({ name: 'NodeDetailModal', props, - emits: ['cancel', 'update'], + emits: ['cancel', 'submit'], setup(props, { emit }) { const { t } = useI18n() const detailRef = ref() + + // TODO + const mapFormToTaskDefinition = (form: any) => { + return { + // "code": form.code, + name: form.name, + description: form.desc, + taskType: 'SHELL', + taskParams: { + resourceList: [], + localParams: form.localParams, + rawScript: form.shell, + dependence: {}, + conditionResult: { + successNode: [], + failedNode: [] + }, + waitStartTimeout: {}, + switchResult: {} + }, + flag: form.runFlag, + taskPriority: 'MEDIUM', + workerGroup: form.workerGroup, + failRetryTimes: '0', + failRetryInterval: '1', + timeoutFlag: 'CLOSE', + timeoutNotifyStrategy: '', + timeout: 0, + delayTime: '0', + environmentCode: form.environmentCode + } + } const onConfirm = () => { - detailRef.value.onSubmit() + emit('submit', { + formRef: detailRef.value.formRef, + form: mapFormToTaskDefinition(detailRef.value.form) + }) } const onCancel = () => { emit('cancel') @@ -63,7 +97,15 @@ const NodeDetailModal = defineComponent({ } }, render() { - const { t, show, onConfirm, onCancel } = this + const { + t, + show, + onConfirm, + onCancel, + projectCode, + taskDefinition, + readonly + } = this return ( - + ) } diff --git a/dolphinscheduler-ui-next/src/views/projects/node/detail.tsx b/dolphinscheduler-ui-next/src/views/projects/node/detail.tsx index e62796db39..b833dcfd23 100644 --- a/dolphinscheduler-ui-next/src/views/projects/node/detail.tsx +++ b/dolphinscheduler-ui-next/src/views/projects/node/detail.tsx @@ -15,7 +15,7 @@ * limitations under the License. */ -import { defineComponent, PropType, ref, toRefs } from 'vue' +import { defineComponent, PropType, ref, toRef, toRefs } from 'vue' import Form from '@/components/form' import { useTask } from './use-task' import { useDetail } from './use-detail' @@ -40,14 +40,15 @@ const NodeDetail = defineComponent({ const { taskType, projectCode } = props const { json, model } = useTask({ taskType, projectCode }) - const { state, onSubmit } = useDetail() + const { state } = useDetail() const jsonRef = ref(json) const { rules, elements } = getElementByJson(jsonRef.value, model) expose({ - onSubmit: () => void onSubmit(model) + formRef: toRef(state, 'formRef'), + form: model }) return { rules, elements, model, ...toRefs(state) } diff --git a/dolphinscheduler-ui-next/src/views/projects/node/types.ts b/dolphinscheduler-ui-next/src/views/projects/node/types.ts index 68d5e9053b..e8ef7d8e8f 100644 --- a/dolphinscheduler-ui-next/src/views/projects/node/types.ts +++ b/dolphinscheduler-ui-next/src/views/projects/node/types.ts @@ -18,6 +18,7 @@ import { VNode } from 'vue' import type { SelectOption } from 'naive-ui' import type { IFormItem, IJsonItem } from '@/components/form/types' +import type { TaskType } from '@/views/projects/task/constants/task-type' interface ITaskPriorityOption extends SelectOption { icon: VNode @@ -46,27 +47,10 @@ interface ITimeout { timeout?: number strategy?: string } -type ITaskType = - | 'SHELL' - | 'SUB_PROCESS' - | 'PROCEDURE' - | 'SQL' - | 'SPARK' - | 'FLINK' - | 'MapReduce' - | 'PYTHON' - | 'DEPENDENT' - | 'HTTP' - | 'DataX' - | 'PIGEON' - | 'SQOOP' - | 'CONDITIONS' - | 'DATA_QUALITY' - | 'SWITCH' - | 'SEATUNNEL' +type ITaskType = TaskType interface ITask { - code?: string + code: number timeoutNotifyStrategy?: string taskParams: ITaskParams description?: string diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-hooks.ts b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-hooks.ts index 303ae1f712..3c691275d3 100644 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-hooks.ts +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-hooks.ts @@ -16,7 +16,7 @@ */ import { useCanvasInit } from './use-canvas-init' -import { useCellQuery } from './use-cell-query' +import { useBusinessMapper } from './use-business-mapper' import { useCellActive } from './use-cell-active' import { useCellUpdate } from './use-cell-update' import { useNodeSearch } from './use-node-search' @@ -25,10 +25,11 @@ import { useTextCopy } from './use-text-copy' import { useCustomCellBuilder } from './use-custom-cell-builder' import { useGraphBackfill } from './use-graph-backfill' import { useDagDragAndDrop } from './use-dag-drag-drop' +import { useTaskEdit } from './use-task-edit' export { useCanvasInit, - useCellQuery, + useBusinessMapper, useCellActive, useNodeSearch, useGraphAutoLayout, @@ -36,5 +37,6 @@ export { useCustomCellBuilder, useGraphBackfill, useCellUpdate, - useDagDragAndDrop + useDagDragAndDrop, + useTaskEdit } 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 be4b9abf5e..255ff444b2 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 @@ -28,6 +28,7 @@ import { NDynamicInput } from 'naive-ui' import { queryTenantList } from '@/service/modules/tenants' +import { SaveForm } from './types' import './x6-style.scss' const props = { @@ -67,7 +68,7 @@ export default defineComponent({ }) }) - const formValue = ref({ + const formValue = ref({ name: '', description: '', tenantCode: 'default', diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-sidebar.tsx b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-sidebar.tsx index 2ecd49866a..4dfe8c78e4 100644 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-sidebar.tsx +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/dag-sidebar.tsx @@ -16,7 +16,10 @@ */ import { defineComponent } from 'vue' -import { TASK_TYPES_MAP, TaskType } from '../../../task/constants/task-type' +import { + TaskType, + TASK_TYPES_MAP +} from '@/views/projects/task/constants/task-type' import Styles from './dag.module.scss' export default defineComponent({ @@ -35,7 +38,7 @@ export default defineComponent({ class={Styles.draggable} draggable='true' onDragstart={(e) => { - context.emit('dragStart', e, task.type) + context.emit('dragStart', e, task.type as TaskType) }} > , default: false + }, + projectCode: { + type: Number as PropType, + default: 0 } } export default defineComponent({ name: 'workflow-dag', props, - emits: ['refresh'], + emits: ['refresh', 'save'], setup(props, context) { const theme = useThemeStore() @@ -68,9 +75,20 @@ export default defineComponent({ cancel } = useGraphAutoLayout({ graph }) + // Edit task + const { + taskConfirm, + taskModalVisible, + currTask, + taskCancel, + appendTask, + taskDefinitions + } = useTaskEdit({ graph }) + const { onDragStart, onDrop } = useDagDragAndDrop({ graph, - readonly: toRef(props, 'readonly') + readonly: toRef(props, 'readonly'), + appendTask }) // backfill @@ -99,9 +117,19 @@ export default defineComponent({ saveModalShow.value = !versionModalShow.value } } - const onSave = (form: any) => { - // TODO - console.log(form) + const { getConnects, getLocations } = useBusinessMapper() + const onSave = (saveForm: any) => { + const edges = graph.value?.getEdges() || [] + const nodes = graph.value?.getNodes() || [] + const connects = getConnects(nodes, edges, taskDefinitions.value as any) + const locations = getLocations(nodes) + context.emit('save', { + taskDefinitions: taskDefinitions.value, + saveForm, + connects, + locations + }) + saveModelToggle(false) } return () => ( @@ -136,6 +164,13 @@ export default defineComponent({ /> )} + ) } 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 77f97db029..3e6b97f8b2 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 @@ -15,6 +15,8 @@ * limitations under the License. */ +import { TaskType } from '@/views/projects/task/constants/task-type' + export interface ProcessDefinition { id: number code: number @@ -41,37 +43,37 @@ export interface ProcessDefinition { warningGroupId: number } -export interface ProcessTaskRelationList { - id: number +export interface Connect { + id?: number name: string - processDefinitionVersion: number - projectCode: any - processDefinitionCode: any + processDefinitionVersion?: number + projectCode?: number + processDefinitionCode?: number preTaskCode: number preTaskVersion: number - postTaskCode: any + postTaskCode: number postTaskVersion: number conditionType: string conditionParams: any - createTime: string - updateTime: string + createTime?: string + updateTime?: string } -export interface TaskDefinitionList { +export interface TaskDefinition { id: number - code: any + code: number name: string version: number description: string projectCode: any userId: number - taskType: string + taskType: TaskType taskParams: any taskParamList: any[] taskParamMap: any flag: string taskPriority: string - userName?: any + userName: any projectName?: any workerGroup: string environmentCode: number @@ -84,12 +86,49 @@ export interface TaskDefinitionList { resourceIds: string createTime: string updateTime: string - modifyBy?: any + modifyBy: any dependence: string } +export type NodeData = { + code: number + taskType: TaskType + name: string +} & Partial + export interface WorkflowDefinition { processDefinition: ProcessDefinition - processTaskRelationList: ProcessTaskRelationList[] - taskDefinitionList: TaskDefinitionList[] + processTaskRelationList: Connect[] + taskDefinitionList: TaskDefinition[] +} + +export interface Dragged { + x: number + y: number + type: TaskType +} + +export interface Coordinate { + x: number + y: number +} + +export interface GlobalParam { + key: string + value: string +} + +export interface SaveForm { + name: string + description: string + tenantCode: string + timeoutFlag: boolean + timeout: number + globalParams: GlobalParam[] +} + +export interface Location { + taskCode: number + x: number + y: number } diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-business-mapper.ts b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-business-mapper.ts new file mode 100644 index 0000000000..d8cb125b27 --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-business-mapper.ts @@ -0,0 +1,118 @@ +/* + * 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 type { Node, Edge } from '@antv/x6' +import { Connect, Location, TaskDefinition } from './types' +import { get } from 'lodash' + +/** + * Handling business entity and x6 entity conversion + * @param {Options} options + */ +export function useBusinessMapper() { + /** + * Get connects, connects and processTaskRelationList are the same + * @param {Node[]} nodes + * @param {Edge[]} edges + * @param {TaskDefinition[]} taskDefinitions + * @returns {Connect[]} + */ + function getConnects( + nodes: Node[], + edges: Edge[], + taskDefinitions: TaskDefinition[] + ): Connect[] { + interface TailNodes { + [code: string]: boolean + } + // Nodes in DAG whose in-degree is not 0 + const tailNodes: TailNodes = {} + // If there is an edge target to a node, the node is tailNode + edges.forEach((edge) => { + const targetId = edge.getTargetCellId() + tailNodes[targetId] = true + }) + const isHeadNode = (code: string) => !tailNodes[code] + + interface TasksMap { + [code: string]: TaskDefinition + } + const tasksMap: TasksMap = {} + nodes.forEach((node) => { + const code = node.id + const task = taskDefinitions.find((t) => t.code === Number(code)) + if (task) { + tasksMap[code] = task + } + }) + + const headConnects: Connect[] = nodes + .filter((node) => isHeadNode(node.id)) + .map((node) => { + const task = tasksMap[node.id] + return { + name: '', + preTaskCode: 0, + preTaskVersion: 0, + postTaskCode: task.code, + postTaskVersion: task.version || 0, + // conditionType and conditionParams are reserved + conditionType: 'NONE', + conditionParams: {} + } + }) + + const tailConnects: Connect[] = edges.map((edge) => { + const labels = edge.getLabels() + const labelName = get(labels, ['0', 'attrs', 'label', 'text'], '') + const sourceId = edge.getSourceCellId() + const prevTask = tasksMap[sourceId] + const targetId = edge.getTargetCellId() + const task = tasksMap[targetId] + + return { + name: labelName, + preTaskCode: prevTask.code, + preTaskVersion: prevTask.version || 0, + postTaskCode: task.code, + postTaskVersion: task.version || 0, + // conditionType and conditionParams are reserved + conditionType: 'NONE', + conditionParams: {} + } + }) + + return headConnects.concat(tailConnects) + } + + function getLocations(nodes: Node[]): Location[] { + return nodes.map((node) => { + const code = +node.id + const { x, y } = node.getPosition() + return { + taskCode: code, + x, + y + } + }) + } + + return { + getLocations, + getConnects + } +} diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-cell-query.ts b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-cell-query.ts deleted file mode 100644 index 243e6c755f..0000000000 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-cell-query.ts +++ /dev/null @@ -1,54 +0,0 @@ -/* - * 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 type { Ref } from 'vue' -import type { Graph } from '@antv/x6' -import { TaskType } from '../../../task/constants/task-type' - -interface Options { - graph: Ref -} - -/** - * Expose some cell-related query methods and refs - * @param {Options} options - */ -export function useCellQuery(options: Options) { - const { graph } = options - - /** - * Get all nodes - */ - function getNodes() { - const nodes = graph.value?.getNodes() - if (!nodes) return [] - return nodes.map((node) => { - const position = node.getPosition() - const data = node.getData() - return { - code: node.id, - position: position, - name: data.taskName as string, - type: data.taskType as TaskType - } - }) - } - - return { - getNodes - } -} diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-cell-update.ts b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-cell-update.ts index b69033f0a7..faae315e3a 100644 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-cell-update.ts +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-cell-update.ts @@ -18,9 +18,9 @@ import type { Ref } from 'vue' import type { Graph } from '@antv/x6' import type { TaskType } from '@/views/projects/task/constants/task-type' +import type { Coordinate } from './types' import { TASK_TYPES_MAP } from '@/views/projects/task/constants/task-type' import { useCustomCellBuilder } from './dag-hooks' -import type { Coordinate } from './use-custom-cell-builder' import utils from '@/utils' interface Options { @@ -59,13 +59,14 @@ export function useCellUpdate(options: Options) { function addNode( id: string, type: string, + name: string, coordinate: Coordinate = { x: 100, y: 100 } ) { if (!TASK_TYPES_MAP[type as TaskType]) { console.warn(`taskType:${type} is invalid!`) return } - const node = buildNode(id, type, '', coordinate) + const node = buildNode(id, type, name, coordinate) graph.value?.addNode(node) } diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-custom-cell-builder.ts b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-custom-cell-builder.ts index 390b28cf13..7d9c5ed3ec 100644 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-custom-cell-builder.ts +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-custom-cell-builder.ts @@ -18,9 +18,7 @@ import type { Node, Edge } from '@antv/x6' import { X6_NODE_NAME, X6_EDGE_NAME } from './dag-config' import utils from '@/utils' -import { WorkflowDefinition } from './types' - -export type Coordinate = { x: number; y: number } +import { WorkflowDefinition, Coordinate } from './types' export function useCustomCellBuilder() { /** @@ -110,7 +108,7 @@ export function useCustomCellBuilder() { tasks.forEach((task) => { const location = locations.find((l) => l.taskCode === task.code) || {} - const node = buildNode(task.code, task.taskType, task.name, { + const node = buildNode(task.code + '', task.taskType, task.name, { x: location.x, y: location.y }) diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-dag-drag-drop.ts b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-dag-drag-drop.ts index 046c742a01..0cc22f3f47 100644 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-dag-drag-drop.ts +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-dag-drag-drop.ts @@ -19,39 +19,33 @@ import { ref } from 'vue' import type { Ref } from 'vue' import type { Graph } from '@antv/x6' import { genTaskCodeList } from '@/service/modules/task-definition' -import { useCellUpdate } from './dag-hooks' +import { Dragged } from './types' +import { TaskType } from '@/views/projects/task/constants/task-type' import { useRoute } from 'vue-router' interface Options { readonly: Ref graph: Ref -} - -interface Dragged { - x: number - y: number - type: string + appendTask: (code: number, type: TaskType, coor: Coordinate) => void } /** * Sidebar item drag && drop in canvas */ export function useDagDragAndDrop(options: Options) { - const { readonly, graph } = options + const { readonly, graph, appendTask } = options const route = useRoute() const projectCode = Number(route.params.projectCode) - const { addNode } = useCellUpdate({ graph }) - // The element currently being dragged up const dragged = ref({ x: 0, y: 0, - type: '' + type: 'SHELL' }) - function onDragStart(e: DragEvent, type: string) { + function onDragStart(e: DragEvent, type: TaskType) { if (readonly.value) { e.preventDefault() return @@ -75,8 +69,7 @@ export function useDagDragAndDrop(options: Options) { const genNums = 1 genTaskCodeList(genNums, projectCode).then((res) => { const [code] = res - addNode(code + '', type, { x: x - eX, y: y - eY }) - // openTaskConfigModel(code, type) + appendTask(code, type, { x: x - eX, y: y - eY }) }) } } diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-node-search.ts b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-node-search.ts index 27eae9f417..c5a81abc09 100644 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-node-search.ts +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-node-search.ts @@ -17,7 +17,6 @@ import type { Graph } from '@antv/x6' import { ref, Ref } from 'vue' -import { useCellQuery } from './dag-hooks' interface Options { graph: Ref @@ -40,12 +39,12 @@ export function useNodeSearch(options: Options) { /** * Search dropdown control */ - const { getNodes } = useCellQuery({ graph }) const nodesDropdown = ref<{ label: string; value: string }[]>([]) const reQueryNodes = () => { - nodesDropdown.value = getNodes().map((node) => ({ - label: node.name, - value: node.code + const nodes = graph.value?.getNodes() || [] + nodesDropdown.value = nodes.map((node) => ({ + label: node.getData().taskName, + value: node.id })) } 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 new file mode 100644 index 0000000000..9306e34f25 --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/use-task-edit.ts @@ -0,0 +1,119 @@ +/* + * 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 { ref, onMounted } 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 { useCellUpdate } from './dag-hooks' + +interface Options { + graph: Ref +} + +/** + * Edit task configuration when dbclick + * @param {Options} options + * @returns + */ +export function useTaskEdit(options: Options) { + const { graph } = options + + const { addNode, setNodeName } = useCellUpdate({ graph }) + + const taskDefinitions = ref([]) + const currTask = ref({ + taskType: 'SHELL', + code: 0, + name: '' + }) + const taskModalVisible = ref(false) + + /** + * Append a new task + */ + function appendTask(code: number, type: TaskType, coordinate: Coordinate) { + addNode(code + '', type, '', coordinate) + taskDefinitions.value.push({ + code, + taskType: type, + name: '' + }) + openTaskModal({ code, taskType: type, name: '' }) + } + + function openTaskModal(task: NodeData) { + currTask.value = task + taskModalVisible.value = true + } + + /** + * The confirm event in task config modal + * @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 + } + }) + } + + /** + * The cancel event in task config modal + */ + function taskCancel() { + taskModalVisible.value = false + } + + onMounted(() => { + if (graph.value) { + graph.value.on('cell:dblclick', ({ cell }) => { + const code = Number(cell.id) + const definition = taskDefinitions.value.find((t) => t.code === code) + if (definition) { + currTask.value = definition + } + taskModalVisible.value = true + }) + } + }) + + return { + currTask, + taskModalVisible, + taskConfirm, + taskCancel, + appendTask, + taskDefinitions + } +} diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/create/index.tsx b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/create/index.tsx index 7462b425ba..f2cc259848 100644 --- a/dolphinscheduler-ui-next/src/views/projects/workflow/definition/create/index.tsx +++ b/dolphinscheduler-ui-next/src/views/projects/workflow/definition/create/index.tsx @@ -16,15 +16,71 @@ */ import { defineComponent } from 'vue' +import { useMessage } from 'naive-ui' import Dag from '../../components/dag' import { useThemeStore } from '@/store/theme/theme' +import { useRoute, useRouter } from 'vue-router' +import { + SaveForm, + TaskDefinition, + Connect, + Location +} from '../../components/dag/types' +import { createProcessDefinition } from '@/service/modules/process-definition' +import { useI18n } from 'vue-i18n' import Styles from './index.module.scss' +interface SaveData { + saveForm: SaveForm + taskDefinitions: TaskDefinition[] + connects: Connect[] + locations: Location[] +} + export default defineComponent({ name: 'WorkflowDefinitionCreate', setup() { const theme = useThemeStore() + const message = useMessage() + const { t } = useI18n() + const route = useRoute() + const router = useRouter() + const projectCode = Number(route.params.projectCode) + + const onSave = ({ + taskDefinitions, + saveForm, + connects, + locations + }: SaveData) => { + const globalParams = saveForm.globalParams.map((p) => { + return { + prop: p.key, + value: p.value, + direct: 'IN', + type: 'VARCHAR' + } + }) + + createProcessDefinition( + { + 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 + }, + projectCode + ).then((res: any) => { + message.success(t('project.dag.success')) + router.push({ path: `/projects/${projectCode}/workflow-definition` }) + }) + } + return () => (
- +
) } 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 fd7200d3f8..1342cb9399 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 @@ -39,6 +39,8 @@ export default defineComponent({ }) } + const save = () => {} + onMounted(() => { if (!code || !projectCode) return refresh() @@ -51,7 +53,12 @@ export default defineComponent({ theme.darkTheme ? Styles['dark'] : Styles['light'] ]} > - + ) }