This is an automated email from the ASF dual-hosted git repository.

songjian pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/dolphinscheduler.git


The following commit(s) were added to refs/heads/dev by this push:
     new 4b06b76  [Feature][UI Next] Create workflow (#8362)
4b06b76 is described below

commit 4b06b760686931a201a2f1adccb14f7368525b27
Author: wangyizhi <[email protected]>
AuthorDate: Sun Feb 13 20:16:58 2022 +0800

    [Feature][UI Next] Create workflow (#8362)
---
 .../src/locales/modules/en_US.ts                   |   3 +-
 .../src/locales/modules/zh_CN.ts                   |   3 +-
 .../service/modules/process-definition/index.ts    |   6 +-
 .../service/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 +---
 .../projects/workflow/components/dag/dag-hooks.ts  |   8 +-
 .../workflow/components/dag/dag-save-modal.tsx     |   3 +-
 .../workflow/components/dag/dag-sidebar.tsx        |   7 +-
 .../projects/workflow/components/dag/index.tsx     |  47 ++++++--
 .../projects/workflow/components/dag/types.ts      |  69 +++++++++---
 .../workflow/components/dag/use-business-mapper.ts | 118 ++++++++++++++++++++
 .../workflow/components/dag/use-cell-query.ts      |  54 ----------
 .../workflow/components/dag/use-cell-update.ts     |   5 +-
 .../components/dag/use-custom-cell-builder.ts      |   6 +-
 .../workflow/components/dag/use-dag-drag-drop.ts   |  21 ++--
 .../workflow/components/dag/use-node-search.ts     |   9 +-
 .../workflow/components/dag/use-task-edit.ts       | 119 +++++++++++++++++++++
 .../projects/workflow/definition/create/index.tsx  |  58 +++++++++-
 .../projects/workflow/definition/detail/index.tsx  |   9 +-
 21 files changed, 502 insertions(+), 151 deletions(-)

diff --git a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts 
b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts
index b308e79..d52c90b 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 fe1bd9c..4728ca4 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 a6d1313..4671fa7 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 2e9949a..17cb4bc 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 51d9a1b..dfc1e66 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<boolean>,
     default: false
   },
-  nodeData: {
-    type: Object as PropType<IDataNode>,
-    default: {
-      taskType: 'SHELL'
-    }
+  taskDefinition: {
+    type: Object as PropType<NodeData>,
+    default: { code: 0, taskType: 'SHELL', name: '' }
   },
-  type: {
-    type: String as PropType<string>,
-    default: ''
+  projectCode: {
+    type: Number as PropType<number>,
+    required: true
   },
-  taskDefinition: {
-    type: Object as PropType<ITask>
+  readonly: {
+    type: Boolean as PropType<boolean>,
+    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 (
       <Modal
         show={show}
@@ -72,7 +114,11 @@ const NodeDetailModal = defineComponent({
         confirmLoading={false}
         onCancel={onCancel}
       >
-        <Detail ref='detailRef' taskType='SHELL' projectCode={111} />
+        <Detail
+          ref='detailRef'
+          taskType={taskDefinition.taskType}
+          projectCode={projectCode}
+        />
       </Modal>
     )
   }
diff --git a/dolphinscheduler-ui-next/src/views/projects/node/detail.tsx 
b/dolphinscheduler-ui-next/src/views/projects/node/detail.tsx
index e62796d..b833dcf 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 68d5e90..e8ef7d8 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 303ae1f..3c69127 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 be4b9ab..255ff44 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<SaveForm>({
       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 2ecd498..4dfe8c7 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)
             }}
           >
             <em
diff --git 
a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/index.tsx 
b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/index.tsx
index 32ceea1..1f56016 100644
--- 
a/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/index.tsx
+++ 
b/dolphinscheduler-ui-next/src/views/projects/workflow/components/dag/index.tsx
@@ -25,12 +25,15 @@ import DagAutoLayoutModal from './dag-auto-layout-modal'
 import {
   useGraphAutoLayout,
   useGraphBackfill,
-  useDagDragAndDrop
+  useDagDragAndDrop,
+  useTaskEdit,
+  useBusinessMapper
 } from './dag-hooks'
 import { useThemeStore } from '@/store/theme/theme'
 import VersionModal from '../../definition/components/version-modal'
 import { WorkflowDefinition } from './types'
 import DagSaveModal from './dag-save-modal'
+import TaskModal from '@/views/projects/node/detail-modal'
 import './x6-style.scss'
 
 const props = {
@@ -42,13 +45,17 @@ const props = {
   readonly: {
     type: Boolean as PropType<boolean>,
     default: false
+  },
+  projectCode: {
+    type: Number as PropType<number>,
+    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({
           />
         )}
         <DagSaveModal v-model:show={saveModalShow.value} onSave={onSave} />
+        <TaskModal
+          show={taskModalVisible.value}
+          projectCode={props.projectCode}
+          taskDefinition={currTask.value}
+          onSubmit={taskConfirm}
+          onCancel={taskCancel}
+        />
       </div>
     )
   }
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 77f97db..3e6b97f 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<TaskDefinition>
+
 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 0000000..d8cb125
--- /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 243e6c7..0000000
--- 
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<Graph | undefined>
-}
-
-/**
- * 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 b69033f..faae315 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 390b28c..7d9c5ed 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 046c742..0cc22f3 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<boolean>
   graph: Ref<Graph | undefined>
-}
-
-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<Dragged>({
     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 27eae9f..c5a81ab 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<Graph | undefined>
@@ -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 0000000..9306e34
--- /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<Graph | undefined>
+}
+
+/**
+ * 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<NodeData[]>([])
+  const currTask = ref<NodeData>({
+    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 7462b42..f2cc259 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 () => (
       <div
         class={[
@@ -32,7 +88,7 @@ export default defineComponent({
           theme.darkTheme ? Styles['dark'] : Styles['light']
         ]}
       >
-        <Dag />
+        <Dag projectCode={projectCode} onSave={onSave} />
       </div>
     )
   }
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 fd7200d..1342cb9 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']
         ]}
       >
-        <Dag definition={definition.value} onRefresh={refresh} />
+        <Dag
+          definition={definition.value}
+          onRefresh={refresh}
+          projectCode={projectCode}
+          onSave={save}
+        />
       </div>
     )
   }

Reply via email to