You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@dolphinscheduler.apache.org by le...@apache.org on 2021/09/06 08:15:10 UTC

[dolphinscheduler] branch json_split_two updated: [Feature-5498][JsonSplit-api] refactor of dolphin-scheduler-ui/dag (#6098)

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

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


The following commit(s) were added to refs/heads/json_split_two by this push:
     new d4f5012  [Feature-5498][JsonSplit-api] refactor of dolphin-scheduler-ui/dag (#6098)
d4f5012 is described below

commit d4f5012f422f9319e21e0c71852b2385a4985ad5
Author: Wangyizhi1 <87...@users.noreply.github.com>
AuthorDate: Mon Sep 6 16:14:48 2021 +0800

    [Feature-5498][JsonSplit-api] refactor of dolphin-scheduler-ui/dag (#6098)
    
    * refactor of dolphin-scheduler-ui/dag
    
    * copy task feature and code optimization
    
    * fix review & ut bugs
    
    * add newline at end of file
    
    * add license header
    
    Co-authored-by: chenxiwei <cx...@139.com>
    Co-authored-by: chenxiwei <ch...@cmss.chinamobile.com>
---
 dolphinscheduler-ui/package.json                   |    2 +
 .../{plugIn/dragZoom.js => canvas/canvas.scss}     |   52 +-
 .../conf/home/pages/dag/_source/canvas/canvas.vue  |  739 +++++++++++++
 .../dragZoom.js => canvas/contextMenu.scss}        |   42 +-
 .../home/pages/dag/_source/canvas/contextMenu.vue  |  155 +++
 .../dragZoom.js => canvas/draggableBox.vue}        |   54 +-
 .../pages/dag/_source/canvas/edgeEditModel.vue     |  114 ++
 .../{plugIn/dragZoom.js => canvas/menuItem.vue}    |   46 +-
 .../{plugIn/dragZoom.js => canvas/statusMenu.scss} |   24 +-
 .../home/pages/dag/_source/canvas/statusMenu.vue   |   70 ++
 .../home/pages/dag/_source/canvas/taskbar.scss     |  167 +++
 .../conf/home/pages/dag/_source/canvas/taskbar.vue |   64 ++
 .../home/pages/dag/_source/canvas/toolbar.scss     |  113 ++
 .../conf/home/pages/dag/_source/canvas/toolbar.vue |  199 ++++
 .../home/pages/dag/_source/canvas/x6-helper.js     |  323 ++++++
 .../{plugIn/dragZoom.js => canvas/x6-style.scss}   |   32 +-
 .../src/js/conf/home/pages/dag/_source/dag.js      |  195 ----
 .../src/js/conf/home/pages/dag/_source/dag.scss    |  563 +---------
 .../src/js/conf/home/pages/dag/_source/dag.vue     | 1143 +++++++-------------
 .../home/pages/dag/_source/formModel/formModel.vue |  220 ++--
 .../conf/home/pages/dag/_source/formModel/log.vue  |    1 +
 .../dag/_source/formModel/tasks/sub_process.vue    |   32 +-
 .../pages/dag/_source/images/full-screen-close.png |  Bin 0 -> 463 bytes
 .../dag/_source/images/full-screen-close_hover.png |  Bin 0 -> 652 bytes
 .../pages/dag/_source/images/full-screen-open.png  |  Bin 0 -> 575 bytes
 .../dag/_source/images/full-screen-open_hover.png  |  Bin 0 -> 575 bytes
 .../home/pages/dag/_source/images/graph-format.png |  Bin 0 -> 458 bytes
 .../dag/_source/images/graph-format_hover.png      |  Bin 0 -> 461 bytes
 .../pages/dag/_source/images/startup-params.png    |  Bin 0 -> 903 bytes
 .../dag/_source/images/startup-params_hover.png    |  Bin 0 -> 903 bytes
 .../dag/_source/images/task-icos/conditions.png    |  Bin 0 -> 812 bytes
 .../_source/images/task-icos/conditions_hover.png  |  Bin 0 -> 736 bytes
 .../pages/dag/_source/images/task-icos/datax.png   |  Bin 0 -> 1122 bytes
 .../dag/_source/images/task-icos/datax_hover.png   |  Bin 0 -> 1127 bytes
 .../dag/_source/images/task-icos/dependent.png     |  Bin 0 -> 743 bytes
 .../_source/images/task-icos/dependent_hover.png   |  Bin 0 -> 745 bytes
 .../pages/dag/_source/images/task-icos/flink.png   |  Bin 0 -> 1443 bytes
 .../dag/_source/images/task-icos/flink_hover.png   |  Bin 0 -> 1348 bytes
 .../pages/dag/_source/images/task-icos/http.png    |  Bin 0 -> 707 bytes
 .../dag/_source/images/task-icos/http_hover.png    |  Bin 0 -> 709 bytes
 .../home/pages/dag/_source/images/task-icos/mr.png |  Bin 0 -> 930 bytes
 .../dag/_source/images/task-icos/mr_hover.png      |  Bin 0 -> 862 bytes
 .../dag/_source/images/task-icos/procedure.png     |  Bin 0 -> 1555 bytes
 .../_source/images/task-icos/procedure_hover.png   |  Bin 0 -> 1436 bytes
 .../pages/dag/_source/images/task-icos/python.png  |  Bin 0 -> 1618 bytes
 .../dag/_source/images/task-icos/python_hover.png  |  Bin 0 -> 1233 bytes
 .../pages/dag/_source/images/task-icos/shell.png   |  Bin 0 -> 747 bytes
 .../dag/_source/images/task-icos/shell_hover.png   |  Bin 0 -> 745 bytes
 .../pages/dag/_source/images/task-icos/spark.png   |  Bin 0 -> 1067 bytes
 .../dag/_source/images/task-icos/spark_hover.png   |  Bin 0 -> 1066 bytes
 .../pages/dag/_source/images/task-icos/sql.png     |  Bin 0 -> 1317 bytes
 .../dag/_source/images/task-icos/sql_hover.png     |  Bin 0 -> 1262 bytes
 .../pages/dag/_source/images/task-icos/sqoop.png   |  Bin 0 -> 896 bytes
 .../dag/_source/images/task-icos/sqoop_hover.png   |  Bin 0 -> 897 bytes
 .../dag/_source/images/task-icos/sub_process.png   |  Bin 0 -> 692 bytes
 .../_source/images/task-icos/sub_process_hover.png |  Bin 0 -> 693 bytes
 .../dag/_source/images/task-icos/waterdrop.png     |  Bin 0 -> 1086 bytes
 .../_source/images/task-icos/waterdrop_hover.png   |  Bin 0 -> 1054 bytes
 .../pages/dag/_source/images/view-variables.png    |  Bin 0 -> 858 bytes
 .../dag/_source/images/view-variables_hover.png    |  Bin 0 -> 858 bytes
 .../home/pages/dag/_source/plugIn/downChart.js     |  122 ---
 .../home/pages/dag/_source/plugIn/jsPlumbHandle.js |  814 --------------
 .../home/pages/dag/_source/plugIn/multiDrag.js     |   67 --
 .../js/conf/home/pages/dag/_source/plugIn/util.js  |  171 ---
 .../pages/dag/_source/udp/_source/selectTenant.vue |    7 +-
 .../src/js/conf/home/pages/dag/_source/udp/udp.vue |   16 +-
 .../js/conf/home/pages/dag/definitionDetails.vue   |    8 +-
 .../pages/definition/pages/list/_source/list.vue   |   12 +-
 .../pages/definition/pages/list/_source/start.vue  |    4 +-
 .../src/js/conf/home/router/index.js               |   28 +-
 .../src/js/conf/home/store/dag/actions.js          |  221 ++--
 .../src/js/conf/home/store/dag/mutations.js        |   54 +-
 .../src/js/conf/home/store/dag/state.js            |    6 +-
 73 files changed, 2707 insertions(+), 3173 deletions(-)

diff --git a/dolphinscheduler-ui/package.json b/dolphinscheduler-ui/package.json
index f1810e6..fe21c59 100644
--- a/dolphinscheduler-ui/package.json
+++ b/dolphinscheduler-ui/package.json
@@ -15,6 +15,8 @@
     "build:release": "npm run clean && cross-env NODE_ENV=production PUBLIC_PATH=/dolphinscheduler/ui webpack --config ./build/webpack.config.release.js"
   },
   "dependencies": {
+    "@antv/layout": "^0.1.18",
+    "@antv/x6": "^1.25.5",
     "@form-create/element-ui": "^1.0.18",
     "@riophae/vue-treeselect": "^0.4.0",
     "axios": "^0.16.2",
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/dragZoom.js b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/canvas.scss
similarity index 58%
copy from dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/dragZoom.js
copy to dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/canvas.scss
index 009c82b..e02ca68 100644
--- a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/dragZoom.js
+++ b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/canvas.scss
@@ -1,4 +1,3 @@
-import d3 from 'd3'
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -15,24 +14,39 @@ import d3 from 'd3'
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+.dag-canvas {
+  display: flex;
+  overflow: hidden;
+  height: calc(100% - 50px);
+  padding: 10px 0 0 0;
+  box-sizing: border-box;
 
-const DragZoom = function () {
-  this.element = {}
-  this.zoom = {}
-  this.scale = 1
-}
+  .dag-container {
+    height: 100%;
+    flex: 1;
+    overflow: hidden;
+    position: relative;
+  
+    .paper {
+      width: 100%;
+      height: 100%;
+    }
+  
+    .minimap {
+      position: absolute;
+      width: 300px;
+      height: 200px;
+      right: 10px;
+      bottom: 10px;
+      border: dashed 1px #e4e4e4;
+      z-index: 9;
+    }
 
-DragZoom.prototype.init = function () {
-  const $canvas = $('#canvas')
-  this.element = d3.select('#canvas')
-  this.zoom = d3.behavior.zoom()
-    .scaleExtent([0.5, 2])
-    .on('zoom', () => {
-      this.scale = d3.event.scale
-      $canvas.css('transform', 'scale(' + this.scale + ')')
-      $canvas.css('transform-origin', '0 0')
-    })
-  this.element.call(this.zoom).on('dblclick.zoom', null)
+    .context-menu{
+      position: absolute;
+      left: 100px;
+      top: 100px;
+    }
+    
+  }
 }
-
-export default new DragZoom()
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/canvas.vue b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/canvas.vue
new file mode 100644
index 0000000..fd4109f
--- /dev/null
+++ b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/canvas.vue
@@ -0,0 +1,739 @@
+/*
+ * 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.
+ */
+<template>
+  <div class="dag-canvas">
+    <dag-taskbar @on-drag-start="_onDragStart" />
+    <div
+      class="dag-container"
+      ref="container"
+      @dragenter.prevent
+      @dragover.prevent
+      @dragleave.prevent
+      @drop.stop.prevent="_onDrop"
+    >
+      <div ref="paper" class="paper"></div>
+      <div ref="minimap" class="minimap"></div>
+      <context-menu ref="contextMenu" />
+      <status-menu ref="statusMenu" />
+    </div>
+  </div>
+</template>
+
+<script>
+  import { Graph, DataUri } from '@antv/x6'
+  import dagTaskbar from './taskbar.vue'
+  import contextMenu from './contextMenu.vue'
+  import statusMenu from './statusMenu.vue'
+  import {
+    NODE_PROPS,
+    EDGE_PROPS,
+    PORT_PROPS,
+    X6_NODE_NAME,
+    X6_PORT_OUT_NAME,
+    X6_PORT_IN_NAME,
+    X6_EDGE_NAME,
+    NODE_HIGHLIGHT_PROPS,
+    PORT_HIGHLIGHT_PROPS,
+    EDGE_HIGHLIGHT_PROPS
+  } from './x6-helper'
+  import { DagreLayout } from '@antv/layout'
+  import { tasksType } from '../config'
+  import _ from 'lodash'
+  import { mapActions, mapMutations } from 'vuex'
+
+  export default {
+    name: 'dag-canvas',
+    data () {
+      return {
+        graph: null,
+        // Used to calculate the context menu location
+        originalScrollPosition: {
+          left: 0,
+          top: 0
+        },
+        editable: true,
+        dragging: {
+          // Distance from the mouse to the top-left corner of the dragging element
+          x: 0,
+          y: 0,
+          type: ''
+        }
+      }
+    },
+    provide () {
+      return {
+        dagCanvas: this
+      }
+    },
+    inject: ['dagChart'],
+    components: {
+      dagTaskbar,
+      contextMenu,
+      statusMenu
+    },
+    methods: {
+      ...mapActions('dag', ['genTaskCodeList']),
+      ...mapMutations('dag', ['removeTask']),
+      /**
+       * Recalculate the paper width and height
+       */
+      paperResize () {
+        const w = this.$el.offsetWidth
+        const h = this.$el.offsetHeight
+        this.graph.resize(w, h)
+      },
+      /**
+       * Init graph
+       * This will be called in the dag-chart mounted event
+       * @param {boolean} uneditable
+       */
+      graphInit (editable) {
+        const self = this
+        this.editable = !!editable
+        const paper = this.$refs.paper
+        const minimap = this.$refs.minimap
+        const graph = (this.graph = new Graph({
+          container: paper,
+          selecting: {
+            enabled: true,
+            multiple: true,
+            rubberband: true,
+            rubberEdge: true,
+            movable: true,
+            showNodeSelectionBox: false
+          },
+          scroller: true,
+          grid: {
+            size: 10,
+            visible: true
+          },
+          snapline: true,
+          minimap: {
+            enabled: true,
+            container: minimap
+          },
+          interacting: {
+            edgeLabelMovable: false,
+            nodeMovable: !!editable,
+            magnetConnectable: !!editable
+          },
+          connecting: {
+            snap: {
+              radius: 30
+            },
+            // Whether multiple edges can be created between the same start node and end
+            allowMulti: false,
+            // Whether a point is allowed to connect to a blank position on the canvas
+            allowBlank: false,
+            // The start node and the end node are the same node
+            allowLoop: false,
+            // Whether an edge is allowed to link to another edge
+            allowEdge: false,
+            // Whether edges are allowed to link to nodes
+            allowNode: true,
+            // Whether to allow edge links to ports
+            allowPort: true,
+            // Whether all available ports or nodes are highlighted when you drag the edge
+            highlight: true,
+            createEdge () {
+              return graph.createEdge({ shape: X6_EDGE_NAME })
+            },
+            validateMagnet ({ magnet }) {
+              return magnet.getAttribute('port-group') !== X6_PORT_IN_NAME
+            },
+            validateConnection (data) {
+              const { sourceCell, targetCell, sourceMagnet, targetMagnet } = data
+              // Connections can only be created from the output link post
+              if (
+                !sourceMagnet ||
+                sourceMagnet.getAttribute('port-group') !== X6_PORT_OUT_NAME
+              ) {
+                return false
+              }
+
+              // Can only be connected to the input link post
+              if (
+                !targetMagnet ||
+                targetMagnet.getAttribute('port-group') !== X6_PORT_IN_NAME
+              ) {
+                return false
+              }
+
+              if (
+                sourceCell &&
+                targetCell &&
+                sourceCell.isNode() &&
+                targetCell.isNode()
+              ) {
+                const edgeData = {
+                  sourceId: Number(sourceCell.id),
+                  targetId: Number(targetCell.id)
+                }
+                if (!self.dagChart.edgeIsValid(edgeData)) {
+                  return false
+                }
+              }
+
+              return true
+            }
+          },
+          highlighting: {
+            nodeAvailable: {
+              name: 'className',
+              args: {
+                className: 'available'
+              }
+            },
+            magnetAvailable: {
+              name: 'className',
+              args: {
+                className: 'available'
+              }
+            },
+            magnetAdsorbed: {
+              name: 'className',
+              args: {
+                className: 'adsorbed'
+              }
+            }
+          }
+        }))
+        // TODO will be deleted
+        window._graph = graph
+        this.registerX6Shape()
+        this.bindGraphEvent()
+        this.originalScrollPosition = graph.getScrollbarPosition()
+      },
+      /**
+       * Register custom shapes
+       */
+      registerX6Shape () {
+        Graph.unregisterNode(X6_NODE_NAME)
+        Graph.unregisterEdge(X6_EDGE_NAME)
+        Graph.registerNode(X6_NODE_NAME, { ...NODE_PROPS })
+        Graph.registerEdge(X6_EDGE_NAME, { ...EDGE_PROPS })
+      },
+      /**
+       * Bind grap event
+       */
+      bindGraphEvent () {
+        // nodes and edges hover
+        this.graph.on('cell:mouseenter', (data) => {
+          const { cell, e } = data
+          const { left: cL, top: cT } =
+            this.$refs.container.getBoundingClientRect()
+          const cX = e.clientX - cL
+          const cY = e.clientY - cT
+          const isStatusIcon = (tagName) =>
+            tagName && tagName.toLocaleLowerCase() === 'i'
+          if (isStatusIcon(e.target.tagName)) {
+            this.$refs.statusMenu.show(cX, cY)
+            this.$refs.statusMenu.setCurrentTask({
+              name: cell.data.taskName,
+              type: cell.data.taskType,
+              code: Number(cell.id)
+            })
+          } else {
+            this.setHighlight(cell)
+          }
+        })
+        this.graph.on('cell:mouseleave', ({ cell }) => {
+          if (!this.graph.isSelected(cell)) {
+            this.resetHighlight(cell)
+          }
+          this.$refs.statusMenu.hide()
+        })
+        // select
+        this.graph.on('cell:selected', ({ cell }) => {
+          this.setHighlight(cell)
+        })
+        this.graph.on('cell:unselected', ({ cell }) => {
+          if (!this.graph.isSelected(cell)) {
+            this.resetHighlight(cell)
+          }
+        })
+        // right click
+        this.graph.on('node:contextmenu', ({ x, y, cell }) => {
+          const { left, top } = this.graph.getScrollbarPosition()
+          const o = this.originalScrollPosition
+          this.$refs.contextMenu.show(x + (o.left - left), y + (o.top - top))
+          this.$refs.contextMenu.setCurrentTask({
+            name: cell.data.taskName,
+            type: cell.data.taskType,
+            code: Number(cell.id)
+          })
+        })
+        // node double click
+        this.graph.on('node:dblclick', ({ cell }) => {
+          this.dagChart.openFormModel(Number(cell.id), cell.data.taskType)
+        })
+        // create edge label
+        this.graph.on('edge:dblclick', ({ cell }) => {
+          const labelName = this.getEdgeLabelName(cell)
+          this.dagChart.$refs.edgeEditModel.show({
+            id: cell.id,
+            label: labelName
+          })
+        })
+      },
+      /**
+       * @param {Edge|string} edge
+       */
+      getEdgeLabelName (edge) {
+        if (typeof edge === 'string') edge = this.graph.getCellById(edge)
+        const labels = edge.getLabels()
+        const labelName = _.get(labels, ['0', 'attrs', 'label', 'text'], '')
+        return labelName
+      },
+      /**
+       * Set edge label by id
+       * @param {string} id
+       * @param {string} label
+       */
+      setEdgeLabel (id, label) {
+        const edge = this.graph.getCellById(id)
+        edge.setLabels(label)
+        if (this.graph.isSelected(edge)) {
+          this.setEdgeHighlight(edge)
+        }
+      },
+      /**
+       * @param {number} limit
+       * @param {string} text
+       * Each Chinese character is equal to two chars
+       */
+      truncateText (text, n) {
+        const exp = /[\u4E00-\u9FA5]/
+        let res = ''
+        let len = text.length
+        let chinese = text.match(new RegExp(exp, 'g'))
+        if (chinese) {
+          len += chinese.length
+        }
+        if (len > n) {
+          let i = 0
+          let acc = 0
+          while (true) {
+            let char = text[i]
+            if (exp.test(char)) {
+              acc += 2
+            } else {
+              acc++
+            }
+            if (acc > n) break
+            res += char
+            i++
+          }
+          res += '...'
+        } else {
+          res = text
+        }
+        return res
+      },
+      /**
+       * Set node name by id
+       * @param {string|number} id
+       * @param {string} name
+       */
+      setNodeName (id, name) {
+        id += ''
+        const node = this.graph.getCellById(id)
+        if (node) {
+          const truncation = this.truncateText(name, 18)
+          node.attr('title/text', truncation)
+          node.setData({ taskName: name })
+        }
+      },
+      /**
+       * Set node highlight
+       * @param {Node} node
+       */
+      setNodeHighlight (node) {
+        const url = require(`../images/task-icos/${node.data.taskType.toLocaleLowerCase()}_hover.png`)
+        node.setAttrs(NODE_HIGHLIGHT_PROPS.attrs)
+        node.setAttrByPath('image/xlink:href', url)
+        node.setPortProp(
+          X6_PORT_OUT_NAME,
+          'attrs',
+          PORT_HIGHLIGHT_PROPS[X6_PORT_OUT_NAME].attrs
+        )
+      },
+      /**
+       * Reset node style
+       * @param {Node} node
+       */
+      resetNodeStyle (node) {
+        const url = require(`../images/task-icos/${node.data.taskType.toLocaleLowerCase()}.png`)
+        node.setAttrs(NODE_PROPS.attrs)
+        node.setAttrByPath('image/xlink:href', url)
+        node.setPortProp(
+          X6_PORT_OUT_NAME,
+          'attrs',
+          PORT_PROPS.groups[X6_PORT_OUT_NAME].attrs
+        )
+      },
+      /**
+       * Set edge highlight
+       * @param {Edge} edge
+       */
+      setEdgeHighlight (edge) {
+        edge.setAttrs(EDGE_HIGHLIGHT_PROPS.attrs)
+        const labelName = this.getEdgeLabelName(edge)
+        if (labelName) {
+          edge.setLabels([
+            _.merge(EDGE_HIGHLIGHT_PROPS.defaultLabel, {
+              attrs: {
+                label: {
+                  text: labelName
+                }
+              }
+            })
+          ])
+        }
+      },
+      /**
+       * Reset edge style
+       * @param {Edge} edge
+       */
+      resetEdgeStyle (edge) {
+        edge.setAttrs(EDGE_PROPS.attrs)
+        const labelName = this.getEdgeLabelName(edge)
+        if (labelName) {
+          edge.setLabels([
+            {
+              attrs: {
+                label: {
+                  fill: EDGE_PROPS.defaultLabel.attrs.label.fill,
+                  text: labelName
+                },
+                body: {
+                  stroke: EDGE_PROPS.defaultLabel.attrs.body.stroke
+                }
+              }
+            }
+          ])
+        }
+      },
+      /**
+       * Set cell highlight
+       * @param {Cell} cell
+       */
+      setHighlight (cell) {
+        if (cell.isEdge()) {
+          this.setEdgeHighlight(cell)
+        } else if (cell.isNode()) {
+          this.setNodeHighlight(cell)
+        }
+      },
+      /**
+       * Reset cell highlight
+       * @param {Cell} cell
+       */
+      resetHighlight (cell) {
+        if (cell.isEdge()) {
+          this.resetEdgeStyle(cell)
+        } else if (cell.isNode()) {
+          this.resetNodeStyle(cell)
+        }
+      },
+      /**
+       * Convert the graph to JSON
+       * @return {{cells:Cell[]}}
+       */
+      toJSON () {
+        return this.graph.toJSON()
+      },
+      /**
+       * Generate graph with JSON
+       */
+      fromJSON (json) {
+        this.graph.fromJSON(json)
+      },
+      /**
+       * getNodes
+       * @return {Node[]}
+       */
+      // interface Node {
+      //   id: number;
+      //   position: {x:number;y:number};
+      //   data: {taskType:string;taskName:string;}
+      // }
+      getNodes () {
+        const nodes = this.graph.getNodes()
+        return nodes.map((node) => {
+          const position = node.getPosition()
+          const data = node.getData()
+          return {
+            id: Number(node.id),
+            position: position,
+            data: data
+          }
+        })
+      },
+      /**
+       * getEdges
+       * @return {Edge[]} Edge is inherited from the Cell
+       */
+      // interface Edge {
+      //   label: string;
+      //   sourceId: number;
+      //   targetId: number;
+      // }
+      getEdges () {
+        const edges = this.graph.getEdges()
+        return edges.map((edge) => {
+          const labelData = edge.getLabelAt(0)
+          return {
+            label: _.get(labelData, ['attrs', 'label', 'text'], ''),
+            sourceId: Number(edge.getSourceCellId()),
+            targetId: Number(edge.getTargetCellId())
+          }
+        })
+      },
+      /**
+       * downloadPNG
+       * @param {string} filename
+       */
+      downloadPNG (fileName = 'chart') {
+        this.graph.toPNG(
+          (dataUri) => {
+            DataUri.downloadDataUri(dataUri, `${fileName}.png`)
+          },
+          {
+            padding: {
+              top: 50,
+              right: 50,
+              bottom: 50,
+              left: 50
+            },
+            backgroundColor: '#f2f3f7'
+          }
+        )
+      },
+      /**
+       * format
+       * @desc Auto layout use @antv/layout
+       */
+      format () {
+        const dagreLayout = new DagreLayout({
+          type: 'dagre',
+          rankdir: 'LR',
+          align: 'UL',
+          // Calculate the node spacing based on the edge label length
+          ranksepFunc: (d) => {
+            const edges = this.graph.getOutgoingEdges(d.id)
+            let max = 0
+            if (edges && edges.length > 0) {
+              edges.forEach((edge) => {
+                const edgeView = this.graph.findViewByCell(edge)
+                const labelWidth = +edgeView.findAttr(
+                  'width',
+                  _.get(edgeView, ['labelSelectors', '0', 'body'], null)
+                )
+                max = Math.max(max, labelWidth)
+              })
+            }
+            return 50 + max
+          },
+          nodesep: 50,
+          controlPoints: true
+        })
+        const json = this.toJSON()
+        const nodes = json.cells.filter((cell) => cell.shape === X6_NODE_NAME)
+        const edges = json.cells.filter((cell) => cell.shape === X6_EDGE_NAME)
+        const newModel = dagreLayout.layout({
+          nodes: nodes,
+          edges: edges
+        })
+        this.fromJSON(newModel)
+      },
+      /**
+       * add a node to the graph
+       * @param {string|number} id
+       * @param {string} taskType
+       * @param {{x:number;y:number}} coordinate Default is { x: 100, y: 100 }
+       */
+      addNode (id, taskType, coordinate = { x: 100, y: 100 }) {
+        id += ''
+        if (!tasksType[taskType]) {
+          console.warn(`taskType:${taskType} is invalid!`)
+          return
+        }
+        const node = this.genNodeJSON(id, taskType, '', coordinate)
+        this.graph.addNode(node)
+      },
+      /**
+       * generate node json
+       * @param {number|string} id
+       * @param {string} taskType
+       * @param {{x:number;y:number}} coordinate Default is { x: 100, y: 100 }
+       */
+      genNodeJSON (id, taskType, taskName, coordinate = { x: 100, y: 100 }) {
+        id += ''
+        const url = require(`../images/task-icos/${taskType.toLocaleLowerCase()}.png`)
+        const truncation = taskName ? this.truncateText(taskName, 18) : id
+        return {
+          id: id,
+          shape: X6_NODE_NAME,
+          x: coordinate.x,
+          y: coordinate.y,
+          data: {
+            taskType: taskType,
+            taskName: taskName
+          },
+          attrs: {
+            image: {
+              // Use href instead of xlink:href, you may lose the icon when downloadPNG
+              'xlink:href': url
+            },
+            title: {
+              text: truncation
+            }
+          }
+        }
+      },
+      /**
+       * generate edge json
+       * @param {number|string} sourceId
+       * @param {number|string} targetId
+       * @param {string} label
+       */
+      genEdgeJSON (sourceId, targetId, label = '') {
+        sourceId += ''
+        targetId += ''
+        return {
+          shape: X6_EDGE_NAME,
+          source: {
+            cell: sourceId,
+            port: X6_PORT_OUT_NAME
+          },
+          target: {
+            cell: targetId,
+            port: X6_PORT_IN_NAME
+          },
+          labels: label ? [label] : undefined
+        }
+      },
+      /**
+       * remove a node
+       * @param {string|number} id NodeId
+       */
+      removeNode (id) {
+        id += ''
+        this.graph.removeNode(id)
+        this.removeTask(id)
+      },
+      /**
+       * remove an edge
+       * @param {string|number} id EdgeId
+       */
+      removeEdge (id) {
+        id += ''
+        this.graph.removeEdge(id)
+      },
+      /**
+       * remove multiple cells
+       * @param {Cell[]} cells
+       */
+      removeCells (cells) {
+        this.graph.removeCells(cells)
+        cells.forEach((cell) => {
+          if (cell.isNode()) {
+            this.removeTask(cell.id)
+          }
+        })
+      },
+      /**
+       * Gets the current selections
+       * @return {Cell[]}
+       */
+      getSelections () {
+        return this.graph.getSelectedCells()
+      },
+      /**
+       * Lock scroller
+       */
+      lockScroller () {
+        this.graph.lockScroller()
+      },
+      /**
+       * Unlock scroller
+       */
+      unlockScroller () {
+        this.graph.unlockScroller()
+      },
+      /**
+       * Drag && Drop Event
+       */
+      _onDragStart (e, taskType) {
+        if (!this.editable) {
+          e.preventDefault()
+          return
+        }
+        this.dragging = {
+          x: e.offsetX,
+          y: e.offsetY,
+          type: taskType.name
+        }
+      },
+      calcGraphCoordinate (mClientX, mClientY) {
+        // Distance from the mouse to the top-left corner of the container;
+        const { left: cX, top: cY } =
+          this.$refs.container.getBoundingClientRect()
+        const mouseX = mClientX - cX
+        const mouseY = mClientY - cY
+
+        // The distance that paper has been scrolled
+        const { left: sLeft, top: sTop } = this.graph.getScrollbarPosition()
+        const { left: oLeft, top: oTop } = this.originalScrollPosition
+        const scrollX = sLeft - oLeft
+        const scrollY = sTop - oTop
+
+        // Distance from the mouse to the top-left corner of the dragging element;
+        const { x: eX, y: eY } = this.dragging
+
+        return {
+          x: mouseX + scrollX - eX,
+          y: mouseY + scrollY - eY
+        }
+      },
+      _onDrop (e) {
+        const { type } = this.dragging
+        const { x, y } = this.calcGraphCoordinate(e.clientX, e.clientY)
+        this.genTaskCodeList({
+          genNum: 1
+        })
+          .then((res) => {
+            const [code] = res
+            this.addNode(code, type, { x, y })
+            this.dagChart.openFormModel(code, type)
+          })
+          .catch((err) => {
+            console.error(err)
+          })
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+@import "./canvas";
+</style>
+
+<style lang="scss">
+@import "./x6-style";
+</style>
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/dragZoom.js b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/contextMenu.scss
similarity index 60%
copy from dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/dragZoom.js
copy to dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/contextMenu.scss
index 009c82b..6ea9a3c 100644
--- a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/dragZoom.js
+++ b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/contextMenu.scss
@@ -1,4 +1,3 @@
-import d3 from 'd3'
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -15,24 +14,29 @@ import d3 from 'd3'
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+.dag-context-menu{
+  position: absolute;
+  left: 0;
+  top: 0;
+  width: 100px;
+  background-color: #ffffff;
+  box-shadow: 0 2px 10px rgba(0,0,0,0.12);
 
-const DragZoom = function () {
-  this.element = {}
-  this.zoom = {}
-  this.scale = 1
-}
+  .menu-item{
+    padding: 5px 10px;
+    border-bottom: solid 1px #f2f3f7;
+    cursor: pointer;
+    color: rgb(89, 89, 89);
+    font-size: 12px;
 
-DragZoom.prototype.init = function () {
-  const $canvas = $('#canvas')
-  this.element = d3.select('#canvas')
-  this.zoom = d3.behavior.zoom()
-    .scaleExtent([0.5, 2])
-    .on('zoom', () => {
-      this.scale = d3.event.scale
-      $canvas.css('transform', 'scale(' + this.scale + ')')
-      $canvas.css('transform-origin', '0 0')
-    })
-  this.element.call(this.zoom).on('dblclick.zoom', null)
-}
+    &:hover:not(.disabled){
+      color: #262626;
+      background-color: #f5f5f5; 
+    }
 
-export default new DragZoom()
+    &.disabled{
+      cursor: not-allowed;
+      color: rgba(89, 89, 89, .4);
+    }
+  }
+}
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/contextMenu.vue b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/contextMenu.vue
new file mode 100644
index 0000000..dafd521
--- /dev/null
+++ b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/contextMenu.vue
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ */
+<template>
+  <div
+    class="dag-context-menu"
+    v-show="visible"
+    :style="{
+      left: `${left}px`,
+      top: `${top}px`,
+    }"
+  >
+    <menu-item :disabled="!startAvailable" @on-click="onStart">
+      {{ $t("Start") }}
+    </menu-item>
+    <menu-item :disabled="readOnly" @on-click="onEdit">
+      {{ $t("Edit") }}
+    </menu-item>
+    <menu-item :disabled="readOnly" @on-click="onCopy">
+      {{ $t("Copy") }}
+    </menu-item>
+    <menu-item :disabled="readOnly" @on-click="onDelete">
+      {{ $t("Delete") }}
+    </menu-item>
+  </div>
+</template>
+
+<script>
+  import { mapState, mapActions, mapMutations } from 'vuex'
+  import { findComponentDownward, uuid } from '@/module/util/'
+  import MenuItem from './menuItem.vue'
+
+  export default {
+    name: 'dag-context-menu',
+    inject: ['dagChart', 'dagCanvas'],
+    components: {
+      MenuItem
+    },
+    data () {
+      return {
+        visible: false,
+        left: 0,
+        top: 0,
+        canvasRef: null,
+        currentTask: {
+          code: 0,
+          name: '',
+          type: ''
+        }
+      }
+    },
+    computed: {
+      ...mapState('dag', ['isDetails', 'releaseState', 'tasks']),
+      startAvailable () {
+        return (
+          this.$route.name === 'projects-definition-details' &&
+          this.releaseState !== 'NOT_RELEASE'
+        )
+      },
+      readOnly () {
+        return this.isDetails
+      }
+    },
+    mounted () {
+      document.addEventListener('click', (e) => {
+        this.hide()
+      })
+    },
+    methods: {
+      ...mapActions('dag', ['genTaskCodeList']),
+      ...mapMutations('dag', ['addTask']),
+      getDagCanvasRef () {
+        if (this.canvasRef) {
+          return this.canvasRef
+        } else {
+          const canvas = findComponentDownward(this.dagChart, 'dag-canvas')
+          this.canvasRef = canvas
+          return canvas
+        }
+      },
+      setCurrentTask (task) {
+        this.currentTask = { ...this.currentTask, ...task }
+      },
+      onStart () {
+        this.dagChart.startRunning(this.currentTask.name)
+      },
+      onEdit () {
+        this.dagChart.openFormModel(this.currentTask.code, this.currentTask.type)
+      },
+      onCopy () {
+        const nodes = this.dagCanvas.getNodes()
+        const targetNode = nodes.find(
+          (node) => node.id === this.currentTask.code
+        )
+        const targetTask = this.tasks.find(
+          (task) => task.code === this.currentTask.code
+        )
+
+        if (!targetNode || !targetTask) return
+
+        this.genTaskCodeList({
+          genNum: 1
+        })
+          .then((res) => {
+            const [code] = res
+            const taskName = uuid(targetTask.name + '_')
+            const task = {
+              ...targetTask,
+              code,
+              name: taskName
+            }
+            this.dagCanvas.addNode(code, this.currentTask.type, {
+              x: targetNode.position.x + 100,
+              y: targetNode.position.y + 100
+            })
+            this.addTask(task)
+            this.dagCanvas.setNodeName(code, taskName)
+          })
+          .catch((err) => {
+            console.error(err)
+          })
+      },
+      onDelete () {
+        this.dagCanvas.removeNode(this.currentTask.code)
+      },
+      show (x = 0, y = 0) {
+        this.dagCanvas.lockScroller()
+        this.visible = true
+        this.left = x + 10
+        this.top = y + 10
+      },
+      hide () {
+        this.dagCanvas.unlockScroller()
+        this.visible = false
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+@import "./contextMenu";
+</style>
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/dragZoom.js b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/draggableBox.vue
similarity index 59%
copy from dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/dragZoom.js
copy to dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/draggableBox.vue
index 009c82b..cf824da 100644
--- a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/dragZoom.js
+++ b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/draggableBox.vue
@@ -1,4 +1,3 @@
-import d3 from 'd3'
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -15,24 +14,37 @@ import d3 from 'd3'
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+<template>
+  <div
+    class="draggable-box"
+    ref="draggable"
+    draggable="true"
+    @dragstart="onDragstart"
+    @drag="onDrag"
+    @dragend="onDragend"
+  >
+    <slot></slot>
+  </div>
+</template>
 
-const DragZoom = function () {
-  this.element = {}
-  this.zoom = {}
-  this.scale = 1
-}
-
-DragZoom.prototype.init = function () {
-  const $canvas = $('#canvas')
-  this.element = d3.select('#canvas')
-  this.zoom = d3.behavior.zoom()
-    .scaleExtent([0.5, 2])
-    .on('zoom', () => {
-      this.scale = d3.event.scale
-      $canvas.css('transform', 'scale(' + this.scale + ')')
-      $canvas.css('transform-origin', '0 0')
-    })
-  this.element.call(this.zoom).on('dblclick.zoom', null)
-}
-
-export default new DragZoom()
+<script>
+  export default {
+    name: 'draggable-box',
+    data () {
+      return {
+        tmp: null
+      }
+    },
+    methods: {
+      onDragstart (e) {
+        this.$emit('onDragstart', e)
+      },
+      onDrag (e) {
+        this.$emit('onDrag', e)
+      },
+      onDragend (e) {
+        this.$emit('onDragend', e)
+      }
+    }
+  }
+</script>
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/edgeEditModel.vue b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/edgeEditModel.vue
new file mode 100644
index 0000000..686ac4e
--- /dev/null
+++ b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/edgeEditModel.vue
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ */
+<template>
+  <el-drawer
+    :visible.sync="drawerVisible"
+    :wrapperClosable="false"
+    size=""
+    :with-header="false"
+  >
+    <div class="form-model-wrapper">
+      <div class="title-box">
+        <span class="name">{{ $t("Current connection settings") }}</span>
+      </div>
+      <div class="content-box">
+        <div class="form-model">
+          <!-- Node name -->
+          <div class="clearfix list">
+            <div class="text-box">
+              <span>{{ $t("Connection name") }}</span>
+            </div>
+            <div class="cont-box">
+              <label class="label-box">
+                <el-input
+                  type="text"
+                  size="small"
+                  v-model="label"
+                  :disabled="isDetails"
+                  :placeholder="$t('Please enter name')"
+                  maxlength="100"
+                >
+                </el-input>
+              </label>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div class="bottom-box">
+        <div class="submit" style="background: #fff">
+          <el-button type="text" size="small" @click="cancel()">
+            {{ $t("Cancel") }}
+          </el-button>
+          <el-button
+            type="primary"
+            size="small"
+            @click="ok()"
+            :disabled="isDetails"
+            >{{ $t("Confirm add") }}
+          </el-button>
+        </div>
+      </div>
+    </div>
+  </el-drawer>
+</template>
+<script>
+  import disabledState from '@/module/mixin/disabledState'
+  import { mapState } from 'vuex'
+  import { findComponentDownward } from '@/module/util/'
+
+  export default {
+    name: 'edge-edit-model',
+    data () {
+      return {
+        id: '',
+        label: '',
+        drawerVisible: false
+      }
+    },
+    inject: ['dagChart'],
+    mixins: [disabledState],
+    computed: {
+      ...mapState('dag', ['isDetails'])
+    },
+    methods: {
+      getDagCanvasRef () {
+        if (this.canvasRef) {
+          return this.canvasRef
+        } else {
+          const canvas = findComponentDownward(this.dagChart, 'dag-canvas')
+          this.canvasRef = canvas
+          return canvas
+        }
+      },
+      show ({ id, label }) {
+        this.id = id
+        this.label = label
+        this.drawerVisible = true
+      },
+      cancel () {
+        this.drawerVisible = false
+        this.id = ''
+        this.label = ''
+      },
+      ok () {
+        const canvas = this.getDagCanvasRef()
+        canvas.setEdgeLabel(this.id, this.label)
+        this.cancel()
+      }
+    }
+  }
+</script>
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/dragZoom.js b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/menuItem.vue
similarity index 59%
copy from dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/dragZoom.js
copy to dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/menuItem.vue
index 009c82b..127a025 100644
--- a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/dragZoom.js
+++ b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/menuItem.vue
@@ -1,4 +1,3 @@
-import d3 from 'd3'
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -15,24 +14,29 @@ import d3 from 'd3'
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+<template>
+  <div class="menu-item" :class="disabled ? 'disabled' : ''" @click="onClick">
+    <slot></slot>
+  </div>
+</template>
 
-const DragZoom = function () {
-  this.element = {}
-  this.zoom = {}
-  this.scale = 1
-}
-
-DragZoom.prototype.init = function () {
-  const $canvas = $('#canvas')
-  this.element = d3.select('#canvas')
-  this.zoom = d3.behavior.zoom()
-    .scaleExtent([0.5, 2])
-    .on('zoom', () => {
-      this.scale = d3.event.scale
-      $canvas.css('transform', 'scale(' + this.scale + ')')
-      $canvas.css('transform-origin', '0 0')
-    })
-  this.element.call(this.zoom).on('dblclick.zoom', null)
-}
-
-export default new DragZoom()
+<script>
+  export default {
+    name: 'dag-context-menu-item',
+    props: {
+      disabled: {
+        type: Boolean,
+        default: false
+      }
+    },
+    data () {
+      return {}
+    },
+    methods: {
+      onClick (e) {
+        if (this.disabled) return
+        this.$emit('on-click', e)
+      }
+    }
+  }
+</script>
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/dragZoom.js b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/statusMenu.scss
similarity index 59%
copy from dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/dragZoom.js
copy to dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/statusMenu.scss
index 009c82b..c6a5afe 100644
--- a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/dragZoom.js
+++ b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/statusMenu.scss
@@ -1,4 +1,3 @@
-import d3 from 'd3'
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -14,25 +13,4 @@ import d3 from 'd3'
  * 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.
- */
-
-const DragZoom = function () {
-  this.element = {}
-  this.zoom = {}
-  this.scale = 1
-}
-
-DragZoom.prototype.init = function () {
-  const $canvas = $('#canvas')
-  this.element = d3.select('#canvas')
-  this.zoom = d3.behavior.zoom()
-    .scaleExtent([0.5, 2])
-    .on('zoom', () => {
-      this.scale = d3.event.scale
-      $canvas.css('transform', 'scale(' + this.scale + ')')
-      $canvas.css('transform-origin', '0 0')
-    })
-  this.element.call(this.zoom).on('dblclick.zoom', null)
-}
-
-export default new DragZoom()
+ */
\ No newline at end of file
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/statusMenu.vue b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/statusMenu.vue
new file mode 100644
index 0000000..2bbfec4
--- /dev/null
+++ b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/statusMenu.vue
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+<template>
+  <div
+    class="dag-status-menu"
+    v-show="visible"
+    :style="{
+      left: `${left}px`,
+      top: `${top}px`,
+    }"
+  >
+  </div>
+</template>
+
+<script>
+  import { mapState } from 'vuex'
+
+  export default {
+    name: 'dag-status-menu',
+    inject: ['dagChart', 'dagCanvas'],
+    data () {
+      return {
+        visible: false,
+        left: 0,
+        top: 0,
+        currentTask: {
+          code: 0,
+          name: '',
+          type: ''
+        }
+      }
+    },
+    computed: {
+      ...mapState('dag', ['tasks'])
+    },
+    methods: {
+      setCurrentTask (task) {
+        this.currentTask = { ...this.currentTask, ...task }
+      },
+      show (x = 0, y = 0) {
+        this.dagCanvas.lockScroller()
+        this.visible = true
+        this.left = x
+        this.top = y
+      },
+      hide () {
+        this.dagCanvas.unlockScroller()
+        this.visible = false
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+@import "./contextMenu";
+</style>
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/taskbar.scss b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/taskbar.scss
new file mode 100644
index 0000000..3ad2416
--- /dev/null
+++ b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/taskbar.scss
@@ -0,0 +1,167 @@
+/*
+ * 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.
+ */
+.dag-taskbar {
+  width: 190px;
+  height: 100%;
+  background-color: #fff;
+  margin-right: 20px;
+
+  .taskbar-title {
+    display: flex;
+    border-bottom: dashed 1px #e5e5e5;
+    height: 42px;
+    padding: 0 20px;
+    align-items: center;
+
+    h4 {
+      color: #666;
+      font-size: 14px;
+    }
+  }
+
+  .tasks {
+    width: 100%;
+    padding: 10px 20px;
+    display: flex;
+    flex-wrap: wrap;
+    box-sizing: border-box;
+    max-height: calc(100% - 42px);
+    overflow: auto;
+
+    .draggable-box {
+      cursor: move;
+      width: 100%;
+      height: 32px;
+      margin-bottom: 10px;
+
+      .task-item {
+        width: 100%;
+        height: 100%;
+        display: flex;
+        align-items: center;
+        border: 1px dashed #e4e4e4;
+        padding: 0 10px;
+        border-radius: 4px;
+
+        em {
+          margin-right: 10px;
+          display: block;
+          width: 18px;
+          height: 18px;
+          background-size: 100% 100%;
+          &.icos-shell {
+            background-image: url("../images/task-icos/shell.png");
+          }
+          &.icos-sub_process {
+            background-image: url("../images/task-icos/sub_process.png");
+          }
+          &.icos-procedure {
+            background-image: url("../images/task-icos/procedure.png");
+          }
+          &.icos-sql {
+            background-image: url("../images/task-icos/sql.png");
+          }
+          &.icos-flink {
+            background-image: url("../images/task-icos/flink.png");
+          }
+          &.icos-mr {
+            background-image: url("../images/task-icos/mr.png");
+          }
+          &.icos-python {
+            background-image: url("../images/task-icos/python.png");
+          }
+          &.icos-dependent {
+            background-image: url("../images/task-icos/dependent.png");
+          }
+          &.icos-http {
+            background-image: url("../images/task-icos/http.png");
+          }
+          &.icos-datax {
+            background-image: url("../images/task-icos/datax.png");
+          }
+          &.icos-sqoop {
+            background-image: url("../images/task-icos/sqoop.png");
+          }
+          &.icos-conditions {
+            background-image: url("../images/task-icos/conditions.png");
+          }
+          &.icos-waterdrop {
+            background-image: url("../images/task-icos/waterdrop.png");
+          }
+          &.icos-spark {
+            background-image: url("../images/task-icos/spark.png");
+          }
+        }
+
+        span {
+          font-size: 12px;
+        }
+
+        &:hover {
+          color: #288fff;
+          border: 1px dashed #288fff;
+          background-color: rgba(40, 143, 255, 0.1);
+
+          em {
+            &.icos-shell {
+              background-image: url("../images/task-icos/shell_hover.png");
+            }
+            &.icos-sub_process {
+              background-image: url("../images/task-icos/sub_process_hover.png");
+            }
+            &.icos-procedure {
+              background-image: url("../images/task-icos/procedure_hover.png");
+            }
+            &.icos-sql {
+              background-image: url("../images/task-icos/sql_hover.png");
+            }
+            &.icos-flink {
+              background-image: url("../images/task-icos/flink_hover.png");
+            }
+            &.icos-mr {
+              background-image: url("../images/task-icos/mr_hover.png");
+            }
+            &.icos-python {
+              background-image: url("../images/task-icos/python_hover.png");
+            }
+            &.icos-dependent {
+              background-image: url("../images/task-icos/dependent_hover.png");
+            }
+            &.icos-http {
+              background-image: url("../images/task-icos/http_hover.png");
+            }
+            &.icos-datax {
+              background-image: url("../images/task-icos/datax_hover.png");
+            }
+            &.icos-sqoop {
+              background-image: url("../images/task-icos/sqoop_hover.png");
+            }
+            &.icos-conditions {
+              background-image: url("../images/task-icos/conditions_hover.png");
+            }
+            &.icos-waterdrop {
+              background-image: url("../images/task-icos/waterdrop_hover.png");
+            }
+            &.icos-spark {
+              background-image: url("../images/task-icos/spark_hover.png");
+            }
+          }
+        }
+      }
+    }
+  }
+}
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/taskbar.vue b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/taskbar.vue
new file mode 100644
index 0000000..0ffc40c
--- /dev/null
+++ b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/taskbar.vue
@@ -0,0 +1,64 @@
+/*
+ * 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.
+ */
+<template>
+  <div class="dag-taskbar">
+    <div class="taskbar-title">
+      <h4>{{$t('Toolbar')}}</h4>
+    </div>
+    <div class="tasks">
+      <template v-for="taskType in tasksTypeList">
+        <draggable-box
+          :key="taskType.name"
+          @onDragstart="(e) => $emit('on-drag-start', e, taskType)"
+        >
+          <div class="task-item">
+            <em :class="`icos-${taskType.name.toLocaleLowerCase()}`"></em>
+            <span>{{ taskType.name }}</span>
+          </div>
+        </draggable-box>
+      </template>
+    </div>
+  </div>
+</template>
+
+<script>
+  import draggableBox from './draggableBox.vue'
+  import { tasksType } from '../config.js'
+
+  export default {
+    name: 'dag-taskbar',
+    components: {
+      draggableBox
+    },
+    data () {
+      const tasksTypeList = Object.keys(tasksType).map((type) => {
+        return {
+          name: type,
+          desc: tasksType[type].desc
+        }
+      })
+
+      return {
+        tasksTypeList
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+@import "./taskbar";
+</style>
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/toolbar.scss b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/toolbar.scss
new file mode 100644
index 0000000..03578f3
--- /dev/null
+++ b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/toolbar.scss
@@ -0,0 +1,113 @@
+/*
+ * 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.
+ */
+.dag-toolbar {
+  width: 100%;
+  height: 50px;
+  background-color: #fff;
+  padding: 0 20px;
+  display: flex;
+  align-items: center;
+  box-sizing: border-box;
+
+  h3 {
+    font-size: 14px;
+    font-weight: bold;
+    margin: 0 10px 0 0;
+  }
+
+  .transparent {
+    width: 1px;
+    height: 1px;
+    opacity: 0;
+  }
+
+  .toolbar-operation {
+    font-size: 18px;
+    cursor: pointer;
+    margin-left: 20px;
+    color: #666666;
+    &:hover {
+      color: #288fff;
+    }
+    &.last{
+      margin-right: 10px;
+    }
+  }
+
+  .toolbar-btn {
+    font-size: 16px;
+  }
+
+  .toolbar-left {
+    flex: 1;
+    display: flex;
+    align-items: center;
+  }
+
+  .toolbar-right {
+    justify-self: flex-end;
+    display: flex;
+    align-items: center;
+  }
+
+  .toolbar-el-btn{
+    margin-right: 0;
+    margin-left: 10px;
+  }
+
+  .custom-ico{
+    display: block;
+    width: 18px;
+    height: 18px;
+    background-size: 100% 100%;
+
+    &.view-variables{
+      background-image: url('../images/view-variables.png');
+      &:hover{
+        background-image: url('../images/view-variables_hover.png');
+      }
+    }
+
+    &.startup-parameters{
+      background-image: url('../images/startup-params.png');
+      &:hover{
+        background-image: url('../images/startup-params_hover.png');
+      }
+    }
+
+    &.full-screen-open{
+      background-image: url('../images/full-screen-open.png');
+      &:hover{
+        background-image: url('../images/full-screen-open_hover.png');
+      }
+    }
+
+    &.full-screen-close{
+      background-image: url('../images/full-screen-close.png');
+      &:hover{
+        background-image: url('../images/full-screen-close_hover.png');
+      }
+    }
+
+    &.graph-format{
+      background-image: url('../images/graph-format.png');
+      &:hover{
+        background-image: url('../images/graph-format_hover.png');
+      }
+    }
+  }
+}
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/toolbar.vue b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/toolbar.vue
new file mode 100644
index 0000000..1a7e5ab
--- /dev/null
+++ b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/toolbar.vue
@@ -0,0 +1,199 @@
+/*
+ * 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.
+ */
+<template>
+  <div class="dag-toolbar">
+    <h3>{{ dagChart.name || $t("Create process") }}</h3>
+    <el-tooltip
+      v-if="dagChart.name"
+      class="toolbar-operation"
+      :content="$t('Copy name')"
+      placement="bottom"
+    >
+      <i class="el-icon-copy-document" @click="copyName"></i>
+    </el-tooltip>
+    <textarea ref="textarea" cols="30" rows="10" class="transparent"></textarea>
+    <div class="toolbar-left">
+      <el-tooltip
+        :content="$t('View variables')"
+        placement="bottom"
+        class="toolbar-operation"
+      >
+        <i
+          class="custom-ico view-variables"
+          v-if="$route.name === 'projects-instance-details'"
+          @click="toggleVariableView"
+        ></i>
+      </el-tooltip>
+      <el-tooltip
+        :content="$t('Startup parameter')"
+        placement="bottom"
+        class="toolbar-operation"
+      >
+        <i
+          class="custom-ico startup-parameters"
+          v-if="$route.name === 'projects-instance-details'"
+          @click="toggleParamView"
+        ></i>
+      </el-tooltip>
+    </div>
+    <div class="toolbar-right">
+      <el-tooltip
+        class="toolbar-operation"
+        :content="$t('Delete selected lines or nodes')"
+        placement="bottom"
+      >
+        <i class="el-icon-delete" @click="removeCells"></i>
+      </el-tooltip>
+      <el-tooltip
+        class="toolbar-operation"
+        :content="$t('Download')"
+        placement="bottom"
+      >
+        <i class="el-icon-download" @click="downloadPNG"></i>
+      </el-tooltip>
+      <el-tooltip
+        class="toolbar-operation"
+        :content="$t('Full Screen')"
+        placement="bottom"
+      >
+        <i
+          :class="[
+            'custom-ico',
+            dagChart.fullScreen
+              ? 'full-screen-close'
+              : 'full-screen-open',
+          ]"
+          @click="toggleFullScreen"
+        ></i>
+      </el-tooltip>
+      <el-tooltip
+        class="toolbar-operation last"
+        :content="$t('Format DAG')"
+        placement="bottom"
+      >
+        <i class="custom-ico graph-format" @click="chartFormat"></i>
+      </el-tooltip>
+      <el-button
+        class="toolbar-el-btn"
+        type="primary"
+        size="mini"
+        @click="saveProcess"
+        >{{ $t("Save") }}</el-button
+      >
+      <el-button
+        class="toolbar-el-btn"
+        v-if="$route.query.subProcessCodes"
+        type="primary"
+        size="mini"
+        icon="el-icon-back"
+        @click="dagChart.returnToPrevProcess"
+      >
+        {{ $t("Return_1") }}
+      </el-button>
+      <el-button
+        class="toolbar-el-btn"
+        type="primary"
+        icon="el-icon-switch-button"
+        size="mini"
+        v-if="type === 'instance' || 'definition'"
+        @click="returnToListPage"
+      >
+        {{ $t("Close") }}
+      </el-button>
+    </div>
+  </div>
+</template>
+
+<script>
+  import { findComponentDownward } from '@/module/util/'
+
+  export default {
+    name: 'dag-toolbar',
+    inject: ['dagChart'],
+    data () {
+      return {
+        canvasRef: null
+      }
+    },
+    methods: {
+      getDagCanvasRef () {
+        if (this.canvasRef) {
+          return this.canvasRef
+        } else {
+          const canvas = findComponentDownward(this.dagChart, 'dag-canvas')
+          this.canvasRef = canvas
+          return canvas
+        }
+      },
+      toggleVariableView () {
+        findComponentDownward(this.$root, 'assist-dag-index')._toggleView()
+      },
+      toggleParamView () {
+        findComponentDownward(
+          this.$root,
+          'starting-params-dag-index'
+        )._toggleParam()
+      },
+      toggleFullScreen () {
+        this.dagChart.toggleFullScreen()
+      },
+      saveProcess () {
+        const canvas = this.getDagCanvasRef()
+        const nodes = canvas.getNodes()
+        if (!nodes.length) {
+          this.$message.error(this.$t('Failed to create node to save'))
+          return
+        }
+        this.dagChart.toggleSaveDialog(true)
+      },
+      downloadPNG () {
+        const canvas = this.getDagCanvasRef()
+        canvas.downloadPNG(this.processName)
+      },
+      removeCells () {
+        const canvas = this.getDagCanvasRef()
+        const selections = canvas.getSelections()
+        canvas.removeCells(selections)
+      },
+      copyName () {
+        const textarea = this.$refs.textarea
+        textarea.value = this.dagChart.name
+        textarea.select()
+        document.execCommand('copy')
+        this.$message(this.$t('Copy success'))
+      },
+      chartFormat () {
+        const canvas = this.getDagCanvasRef()
+        canvas.format()
+      },
+      // TODO
+      refreshTaskState () {},
+      returnToListPage () {
+        let $name = this.$route.name
+        if ($name && $name.indexOf('definition') !== -1) {
+          this.$router.push({ name: 'projects-definition-list' })
+        } else {
+          this.$router.push({ name: 'projects-instance-list' })
+        }
+      }
+    }
+  }
+</script>
+
+<style lang="scss" scoped>
+@import "./toolbar";
+</style>
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/x6-helper.js b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/x6-helper.js
new file mode 100644
index 0000000..ec53090
--- /dev/null
+++ b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/x6-helper.js
@@ -0,0 +1,323 @@
+/*
+ * 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.
+ */
+export const X6_NODE_NAME = 'dag-task'
+export const X6_EDGE_NAME = 'dag-edge'
+export const X6_PORT_OUT_NAME = 'dag-port-out'
+export const X6_PORT_IN_NAME = 'dag-port-in'
+
+const EDGE = '#999999'
+const BG_BLUE = 'rgba(40, 143, 255, 0.1)'
+const BG_WHITE = '#FFFFFF'
+const NODE_BORDER = '#e4e4e4'
+const TITLE = '#333'
+const STROKE_BLUE = '#288FFF'
+
+export const PORT_PROPS = {
+  groups: {
+    [X6_PORT_OUT_NAME]: {
+      position: {
+        name: 'absolute',
+        args: {
+          x: 200,
+          y: 24
+        }
+      },
+      markup: [
+        {
+          tagName: 'g',
+          selector: 'body',
+          children: [
+            {
+              tagName: 'circle',
+              selector: 'circle-outer'
+            },
+            {
+              tagName: 'text',
+              selector: 'plus-text'
+            },
+            {
+              tagName: 'circle',
+              selector: 'circle-inner'
+            }
+          ]
+        }
+      ],
+      attrs: {
+        body: {
+          magnet: true
+        },
+        'plus-text': {
+          fontSize: 12,
+          fill: EDGE,
+          text: '+',
+          textAnchor: 'middle',
+          x: 0,
+          y: 3
+        },
+        'circle-outer': {
+          stroke: EDGE,
+          strokeWidth: 1,
+          r: 6,
+          fill: BG_WHITE
+        },
+        'circle-inner': {
+          r: 4,
+          fill: 'transparent'
+        }
+      }
+    },
+    [X6_PORT_IN_NAME]: {
+      position: {
+        name: 'absolute',
+        args: {
+          x: 0,
+          y: 24
+        }
+      },
+      markup: [
+        {
+          tagName: 'g',
+          selector: 'body',
+          className: 'in-port-body',
+          children: [{
+            tagName: 'circle',
+            selector: 'circle',
+            className: 'circle'
+          }]
+        }
+      ],
+      attrs: {
+        body: {
+          magnet: true
+        },
+        circle: {
+          r: 4,
+          strokeWidth: 0,
+          fill: 'transparent'
+        }
+      }
+    }
+  }
+}
+
+export const PORT_HIGHLIGHT_PROPS = {
+  [X6_PORT_OUT_NAME]: {
+    attrs: {
+      'circle-outer': {
+        stroke: STROKE_BLUE,
+        fill: BG_BLUE
+      },
+      'plus-text': {
+        fill: STROKE_BLUE
+      },
+      'circle-inner': {
+        fill: STROKE_BLUE
+      }
+    }
+  },
+  [X6_PORT_IN_NAME]: {}
+}
+
+export const NODE_PROPS = {
+  width: 220,
+  height: 48,
+  markup: [
+    {
+      tagName: 'rect',
+      selector: 'body'
+    },
+    {
+      tagName: 'image',
+      selector: 'image'
+    },
+    {
+      tagName: 'text',
+      selector: 'title'
+    }
+    // {
+    //   tagName: 'foreignObject',
+    //   selector: 'fo',
+    //   children: [
+    //     {
+    //       tagName: 'body',
+    //       selector: 'fo-body',
+    //       ns: 'http://www.w3.org/1999/xhtml',
+    //       children: [{
+    //         tagName: 'i',
+    //         selector: 'state',
+    //         className: 'state-icon el-icon-circle-check'
+    //       }]
+    //     }
+    //   ]
+    // }
+  ],
+  attrs: {
+    body: {
+      refWidth: '100%',
+      refHeight: '100%',
+      rx: 6,
+      ry: 6,
+      pointerEvents: 'visiblePainted',
+      fill: BG_WHITE,
+      stroke: NODE_BORDER,
+      strokeWidth: 1
+    },
+    image: {
+      width: 30,
+      height: 30,
+      refX: 12,
+      refY: 9
+    },
+    title: {
+      refX: 45,
+      refY: 18,
+      fontFamily: 'Microsoft Yahei',
+      fontSize: 12,
+      fontWeight: 'bold',
+      fill: TITLE,
+      strokeWidth: 0
+    },
+    fo: {
+      refX: '46%',
+      refY: -25,
+      width: 18,
+      height: 18
+    },
+    state: {
+      style: {
+        display: 'block',
+        width: '100%',
+        height: '100%',
+        fontSize: '18px'
+      }
+    }
+  },
+  ports: {
+    ...PORT_PROPS,
+    items: [
+      {
+        id: X6_PORT_OUT_NAME,
+        group: X6_PORT_OUT_NAME
+      },
+      {
+        id: X6_PORT_IN_NAME,
+        group: X6_PORT_IN_NAME
+      }
+    ]
+  }
+}
+
+export const NODE_HIGHLIGHT_PROPS = {
+  attrs: {
+    body: {
+      fill: BG_BLUE,
+      stroke: STROKE_BLUE,
+      strokeDasharray: '5,2'
+    },
+    title: {
+      fill: STROKE_BLUE
+    }
+  }
+}
+
+export const EDGE_PROPS = {
+  attrs: {
+    line: {
+      stroke: EDGE,
+      strokeWidth: 0.8,
+      targetMarker: {
+        tagName: 'path',
+        fill: EDGE,
+        strokeWidth: 0,
+        d: 'M 6 -3 0 0 6 3 Z'
+      }
+    }
+  },
+  connector: {
+    name: 'rounded'
+  },
+  router: {
+    name: 'er',
+    args: {
+      offset: 20,
+      min: 20,
+      direction: 'L'
+    }
+  },
+  defaultLabel: {
+    markup: [
+      {
+        tagName: 'rect',
+        selector: 'body'
+      },
+      {
+        tagName: 'text',
+        selector: 'label'
+      }
+    ],
+    attrs: {
+      label: {
+        fill: EDGE,
+        fontSize: 14,
+        textAnchor: 'middle',
+        textVerticalAnchor: 'middle',
+        pointerEvents: 'none'
+      },
+      body: {
+        ref: 'label',
+        fill: BG_WHITE,
+        stroke: EDGE,
+        strokeWidth: 1,
+        rx: 4,
+        ry: 4,
+        refWidth: '140%',
+        refHeight: '140%',
+        refX: '-20%',
+        refY: '-20%'
+      }
+    },
+    position: {
+      distance: 0.5,
+      options: {
+        absoluteDistance: true,
+        reverseDistance: true
+      }
+    }
+  }
+}
+
+export const EDGE_HIGHLIGHT_PROPS = {
+  attrs: {
+    line: {
+      stroke: STROKE_BLUE,
+      targetMarker: {
+        fill: STROKE_BLUE
+      }
+    }
+  },
+  defaultLabel: {
+    attrs: {
+      label: {
+        fill: STROKE_BLUE
+      },
+      body: {
+        fill: BG_WHITE,
+        stroke: STROKE_BLUE
+      }
+    }
+  }
+}
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/dragZoom.js b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/x6-style.scss
similarity index 60%
rename from dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/dragZoom.js
rename to dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/x6-style.scss
index 009c82b..b86b51a 100644
--- a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/dragZoom.js
+++ b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/canvas/x6-style.scss
@@ -1,4 +1,3 @@
-import d3 from 'd3'
 /*
  * Licensed to the Apache Software Foundation (ASF) under one or more
  * contributor license agreements.  See the NOTICE file distributed with
@@ -15,24 +14,17 @@ import d3 from 'd3'
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+$STROKE_BLUE: #288FFF;
+$BG_WHITE: #FFFFFF;
 
-const DragZoom = function () {
-  this.element = {}
-  this.zoom = {}
-  this.scale = 1
+.x6-node[data-shape="dag-task"]{
+  .in-port-body{
+    &.adsorbed,&.available{
+      .circle {
+        stroke: $STROKE_BLUE;
+        stroke-width: 4;
+        fill: $BG_WHITE;
+      }
+    }
+  }
 }
-
-DragZoom.prototype.init = function () {
-  const $canvas = $('#canvas')
-  this.element = d3.select('#canvas')
-  this.zoom = d3.behavior.zoom()
-    .scaleExtent([0.5, 2])
-    .on('zoom', () => {
-      this.scale = d3.event.scale
-      $canvas.css('transform', 'scale(' + this.scale + ')')
-      $canvas.css('transform-origin', '0 0')
-    })
-  this.element.call(this.zoom).on('dblclick.zoom', null)
-}
-
-export default new DragZoom()
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/dag.js b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/dag.js
deleted file mode 100644
index 66509a6..0000000
--- a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/dag.js
+++ /dev/null
@@ -1,195 +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 Vue from 'vue'
-import _ from 'lodash'
-import i18n from '@/module/i18n'
-import { jsPlumb } from 'jsplumb'
-import JSP from './plugIn/jsPlumbHandle'
-import DownChart from './plugIn/downChart'
-import store from '@/conf/home/store'
-import dagre from 'dagre'
-
-/**
- * Prototype method
- */
-const Dag = function () {
-  this.dag = {}
-  this.instance = {}
-}
-
-/**
- * init
- * @dag dag vue instance
- */
-Dag.prototype.init = function ({ dag, instance }) {
-  this.dag = dag
-  this.instance = instance
-}
-
-/**
- * set init config
- */
-Dag.prototype.setConfig = function (o) {
-  JSP.setConfig(o)
-}
-
-/**
- * create dag
- */
-Dag.prototype.create = function () {
-  const self = this
-  const plumbIns = jsPlumb.getInstance()
-  plumbIns.reset()
-  plumbIns.ready(() => {
-    JSP.init({
-      dag: this.dag,
-      instance: this.instance,
-      options: {
-        onRemoveNodes ($id) {
-          self.dag.removeEventModelById($id)
-        }
-      }
-    })
-
-    // init event
-    JSP.handleEvent()
-
-    // init draggable
-    JSP.draggable()
-  })
-}
-
-/**
- * Action event on the right side of the toolbar
- */
-Dag.prototype.toolbarEvent = function ({ item, code, is }) {
-  const self = this
-  switch (code) {
-    case 'pointer':
-      JSP.handleEventPointer(is)
-      break
-    case 'line':
-      JSP.handleEventLine(is)
-      break
-    case 'remove':
-      JSP.handleEventRemove()
-      break
-    case 'screen':
-      JSP.handleEventScreen({ item, is })
-      break
-    case 'download':
-      Vue.prototype.$confirm(`${i18n.$t('Please confirm whether the workflow has been saved before downloading')}`, `${i18n.$t('Download')}`, {
-        confirmButtonText: `${i18n.$t('Confirm')}`,
-        cancelButtonText: `${i18n.$t('Cancel')}`,
-        type: 'warning'
-      }).then(() => {
-        DownChart.download({
-          dagThis: self.dag
-        })
-      }).catch(() => {
-      })
-      break
-  }
-}
-
-/**
- * Echo data display
- */
-Dag.prototype.backfill = function (arg) {
-  const that = this
-  if (arg) {
-    const marginX = 100
-    const g = new dagre.graphlib.Graph()
-    g.setGraph({})
-    g.setDefaultEdgeLabel(function () { return {} })
-
-    for (const i in store.state.dag.locations) {
-      const location = store.state.dag.locations[i]
-      g.setNode(i, { label: i, width: Math.min(location.name.length * 7, 170), height: 150 })
-    }
-
-    for (const i in store.state.dag.connects) {
-      const connect = store.state.dag.connects[i]
-      g.setEdge(connect.endPointSourceId, connect.endPointTargetId)
-    }
-    dagre.layout(g)
-
-    const dataObject = {}
-    g.nodes().forEach(function (v) {
-      const node = g.node(v)
-      const location = store.state.dag.locations[node.label]
-      const obj = {}
-      obj.name = location.name
-      obj.x = node.x + marginX
-      obj.y = node.y
-      obj.targetarr = location.targetarr
-      dataObject[node.label] = obj
-    })
-    jsPlumb.ready(() => {
-      JSP.init({
-        dag: this.dag,
-        instance: this.instance,
-        options: {
-          onRemoveNodes ($id) {
-            that.dag.removeEventModelById($id)
-          }
-        }
-      })
-      // Backfill
-      JSP.jspBackfill({
-        // connects
-        connects: _.cloneDeep(store.state.dag.connects),
-        // Node location information
-        locations: _.cloneDeep(dataObject),
-        // Node data
-        largeJson: _.cloneDeep(store.state.dag.tasks)
-      })
-    })
-  } else {
-    const plumbIns = jsPlumb.getInstance()
-    plumbIns.reset()
-    plumbIns.ready(() => {
-      JSP.init({
-        dag: this.dag,
-        instance: this.instance,
-        options: {
-          onRemoveNodes ($id) {
-            that.dag.removeEventModelById($id)
-          }
-        }
-      })
-      // Backfill
-      JSP.jspBackfill({
-        // connects
-        connects: _.cloneDeep(store.state.dag.connects),
-        // Node location information
-        locations: _.cloneDeep(store.state.dag.locations),
-        // Node data
-        largeJson: _.cloneDeep(store.state.dag.tasks)
-      })
-    })
-  }
-}
-
-/**
- * Get dag storage format data
- */
-Dag.prototype.saveStore = function () {
-  return JSP.saveStore()
-}
-
-export default new Dag()
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/dag.scss b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/dag.scss
old mode 100755
new mode 100644
index 08f2fec..6ceccb0
--- a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/dag.scss
+++ b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/dag.scss
@@ -14,558 +14,19 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-.dag-model {
-  background: url("../img/dag_bg.png");
-  height: calc(100vh - 100px);
-  ::selection {
-    background:transparent;
-  }
-  ::-moz-selection {
-    background:transparent;
-  }
-  ::-webkit-selection {
-    background:transparent;
-  }
-  .jsplumb-connector {
-    z-index: 1;
-  }
-  .endpoint-tasks {
-    margin-top:22px;
-  }
-  .draggable {
-    > span {
-      text-align: center;
-      display: block;
-      margin-top: -4px;
-      padding: 0 4px;
-      width: 200px;
-      margin-left: -81px;
-      position: absolute;
-      left: 0;
-      bottom: -12px;
-    }
-    .fa {
-      display: inline-block;
-      position: absolute;
-      right: -8px;
-      top: -8px;
-      z-index: 2;
-      cursor: pointer;
-    }
-    .icos {
-      display: inline-block;
-      cursor: pointer;
-    }
-    &.active-tasks {
-      span {
-        color: #0296DF;
-      }
-    }
-  }
-  .icos {
-    width: 32px;
-    height: 32px;
-    margin: 2px;
-    border-radius: 3px;
-    position: relative;
-    z-index: 9;
-  }
-  .icos-SHELL {
-    background: url("../img/toolbar_SHELL.png") no-repeat 50% 50%;
-  }
-  .icos-WATERDROP {
-    background: url("../img/toolbar_WATERDROP.png") no-repeat 50% 50%;
-  }
-  .icos-SUB_PROCESS {
-    background: url("../img/toolbar_SUB_PROCESS.png") no-repeat 50% 50%;
-  }
-  .icos-PROCEDURE {
-    background: url("../img/toolbar_PROCEDURE.png") no-repeat 50% 50%;
-  }
-  .icos-SQL {
-    background: url("../img/toolbar_SQL.png") no-repeat 50% 50%;
-  }
-  .icos-SPARK {
-    background: url("../img/toolbar_SPARK.png") no-repeat 50% 50%;
-  }
-  .icos-FLINK {
-    background: url("../img/toolbar_FLINK.png") no-repeat 50% 50%;
-  }
-  .icos-MR {
-    background: url("../img/toolbar_MR.png") no-repeat 50% 50%;
-  }
-  .icos-PYTHON {
-    background: url("../img/toolbar_PYTHON.png") no-repeat 50% 50%;
-  }
-  .icos-DEPENDENT {
-    background: url("../img/toolbar_DEPENDENT.png") no-repeat 50% 50%;
-  }
-  .icos-HTTP {
-    background: url("../img/toolbar_HTTP.png") no-repeat 50% 50%;
-  }
-  .icos-DATAX {
-    background: url("../img/toolbar_DATAX.png") no-repeat 50% 50%;
-  }
-  .icos-SQOOP {
-    background: url("../img/toolbar_SQOOP.png") no-repeat 50% 50%;
-  }
-  .icos-CONDITIONS {
-    background: url("../img/toolbar_CONDITIONS.png") no-repeat 50% 50%;
-  }
-  .toolbar {
-    width: 60px;
-    height: 100%;
-    background: #F2F3F7;
-    float: left;
-    border-radius: 0 0 0 3px;
-    .title {
-      height: 40px;
-      line-height: 40px;
-      background: #40434C;
-      text-align: center;
-      border-radius: 3px 0 0 0;
-      span {
-        font-size: 14px;
-        color: #fff;
-        font-weight: bold;
-      }
-    }
-    .toolbar-btn {
-      overflow: hidden;
-      padding: 8px 11px 0 11px;
-      .bar-box {
-        width: 36px;
-        height: 36px;
-        float: left;
-        margin-bottom: 3px;
-        border-radius: 3px;
-        .disabled {
-          .icos {
-            opacity: .6;
-            -webkit-filter: grayscale(100%);
-            -moz-filter: grayscale(100%);
-            -ms-filter: grayscale(100%);
-            -o-filter: grayscale(100%);
-            filter: grayscale(100%);
-            filter: gray;
-          }
-        }
-        &:nth-child(odd) {
-          margin-right: 6px;
-        }
-        &.active {
-          background: #e1e2e3;
-        }
-      }
-    }
-  }
-  .dag-contect {
-    float: left;
-    width: calc(100% - 60px);
-    height: 100%;
-    .dag-toolbar {
-      height: 40px;
-      background: #F2F3F7;
-      position: relative;
-      border-radius: 0 3px 0 0;
-      .ans-btn-text {
-        color: #337ab7;
-        .ans-icon {
-          font-size: 16px;
-        }
-      }
-      .assist-btn {
-        position: absolute;
-        left: 10px;
-        top: 7px;
-        >.name {
-          padding-left: 6px;
-          vertical-align: middle;
-        }
-        >.copy-name {
-          cursor: pointer;
-          padding-left: 4px;
-          position: relative;
-          top: -2px;
-          &:hover {
-            i {
-              color: #47c3ff;
-            }
-          }
-          i {
-            color: #333;
-            font-size: 18px;
-            vertical-align: middle;
-          }
-        }
-      }
-      .save-btn {
-        position: absolute;
-        right: 8px;
-        top: 6px;
-        .operation {
-          overflow: hidden;
-          display: inline-block;
-          a {
-            float: left;
-            width: 28px;
-            height: 28px;
-            text-align: center;
-            line-height: 28px;
-            margin-left: 6px;
-            border-radius: 3px;
-            vertical-align: middle;
-            i {
-              color: #333;
-            }
-            &.active {
-              // background: #e1e2e3;
-              i {
-                color: #2d8cf0;
-              }
-            }
-            &.disable {
-              i {
-                color: #bbb;
-              }
-            }
-          }
-        }
-      }
-    }
-    .dag-container {
-      height: calc(100% - 40px);
-      overflow-x: auto;
-      &::-webkit-scrollbar{
-        width: 9px;
-      }
-    }
-  }
-  .tools-model {
-    height: 60px;
-    background: #F4F5F4;
-    border-radius: 3px 3px 0px 0px;
-  }
-}
-#screen {
-  margin-right: 5px;
-}
-.v-modal-custom-log {
-  z-index: 101;
-}
-
-svg path:hover {
-  cursor: pointer;
-}
-
-#chart-container .ui-selecting {
-  span {
-    color: #0296DF;
-  }
-}
-#chart-container .ui-selected {
-  span {
-    color: #0296DF;
-  }
-}
-
-.contextmenu {
-  position: fixed;
-  width: 90px;
-  background: #fff;
-  border-radius: 3px;
-  box-shadow: 0 2px 4px 1px rgba(0, 0, 0, 0.1);
-  padding: 4px 4px;
-  visibility:hidden;
-  z-index: 10000;
-  a {
-    height: 30px;
-    line-height: 28px;
-    display: block;
-    i {
-      font-size: 16px;
-      vertical-align: middle;
-      margin-left: 10px;
-    }
-    span {
-      vertical-align: middle;
-      font-size: 12px;
-      color: #666;
-      padding-left: 2px;
-    }
-    &:hover {
-      background: #f6faff;
-    }
-    &#startRunning {
-      i {
-        color: #35cd75;
-      }
-    }
-    &#editNodes {
-      i {
-        color: #0097e0;
-      }
-    }
-    &#removeNodes {
-      i {
-        color: #f04d4e;
-      }
-    }
-    &#copyNodes {
-      i {
-        color: #FABC05;
-      }
-    }
-    &.disbled {
-      i,span {
-        color: #aaa !important;
-      }
-    }
-  }
-}
-
-.jtk-demo {
-  //min-width: calc(100% - 220px);
-  width: 8000px;
-  height: 5000px;
-  svg:not(:root){
-    z-index: 11;
-  }
-}
-
-.jtk-demo-canvas {
-  position: relative;
-  height: 100%;
-  display: flex;
-}
-
-.jtk-bootstrap {
-  min-height: 100vh;
-  display: flex;
-  flex-direction: column;
-}
-
-.jtk-bootstrap .jtk-page-container {
-  display: flex;
-  width: 100vw;
-  justify-content: center;
-  flex: 1;
-}
-
-.jtk-bootstrap .jtk-container {
-  width: 60%;
-  max-width: 800px;
-}
-
-.jtk-bootstrap-wide .jtk-container {
-  width: 80%;
-  max-width: 1187px;
-}
-
-.jtk-demo-main {
-  position: relative;
-  margin-top: 98px;
-}
-
-.jtk-demo-main .description {
-  font-size: 13px;
-  margin-top: 25px;
-  padding: 13px;
-  margin-bottom: 22px;
-  background-color: #f4f5ef;
-}
-
-.jtk-demo-main .description li {
-  list-style-type: disc !important;
-}
-
-.canvas-wide {
-  padding-top: 10px;
-  margin-left: 0;
-  -ms-transition: all .1s ease-out;
-  -moz-transition: all .1s ease-out;
-  -webkit-transition: all .1s ease-out;
-  -o-transition: all .1s ease-out;
-}
-
-.jtk-demo-dataset {
-  text-align: left;
-  max-height: 600px;
-  overflow: auto;
-}
-
-.demo-title {
-  float: left;
-  font-size: 18px;
-}
-
-.controls {
-  top: 25px;
-  color: #FFF;
-  margin-right: 10px;
-  position: absolute;
-  left: 25px;
-  z-index: 1;
-}
-
-.controls i {
-  background-color: #3E7E9C;
-  border-radius: 4px;
-  cursor: pointer;
-  margin-right: 0;
-  padding: 4px;
-}
-.w {
-  position: absolute;
-  z-index: 4;
-  font-size: 11px;
-  -webkit-transition: background-color 0.25s ease-in;
-  -moz-transition: background-color 0.25s ease-in;
-  transition: background-color 0.25s ease-in;
-  border: 7px solid transparent;
-  border-bottom: 30px solid transparent;
-  .icos {
-    width: 50px;
-    height: 50px;
-    box-shadow: 2px 2px 19px #e0e0e0;
-    -o-box-shadow: 2px 2px 19px #e0e0e0;
-    -webkit-box-shadow: 2px 2px 19px #e0e0e0;
-    -moz-box-shadow: 2px 2px 19px #e0e0e0;
-    -moz-border-radius: 8px;
-    border-radius: 8px;
-    opacity: 0.8;
-    cursor: move;
-    background-color: #fff;
-  }
-  .name-p {
-    position: absolute;
-    left: 50%;
-    top: 58px;
-    width: 200px;
-    text-align: center;
-    margin-left: -100px;
-    word-break:break-all;
-  }
-  .ban-p {
-    position: absolute;
-    left: -4px;
-    top: 36px;
-    z-index: 21;
-    i {
-      font-size: 18px;
-      color: #ff0000;
-      cursor: pointer;
-    }
-  }
-  .state-p {
-    width: 20px;
-    height: 20px;
-    position: absolute;
-    top: -20px;
-    left: 18px;
-    text-align: center;
-    cursor: pointer;
-    b {
-      font-weight: normal;
-    }
-  }
-}
-
-.aLabel {
-  -webkit-transition: background-color 0.25s ease-in;
-  -moz-transition: background-color 0.25s ease-in;
-  transition: background-color 0.25s ease-in;
-  background-color: white;
-  opacity: 0.8;
-  padding: 0.3em;
-  border-radius: 0.5em;
-  border: 1px solid #346789;
-  cursor: pointer;
-}
-
-.aLabel.jtk-hover,
-.jtk-source-hover,
-.jtk-target-hover {
-  .icos {
-    background-color: #333;
-    color: #333;
-    -ms-transition: all 0.6s ease-out;
-    -moz-transition: all 0.6s ease-out;
-    -webkit-transition: all 0.6s ease-out;
-    -o-transition: all 0.6s ease-out;
-  }
-}
-
-.jtk-tasks-active {
-  .icos {
-    background-color: #2db7f5;
-    color: #0097e0;
-    -ms-transition: all 0.6s ease-out;
-    -moz-transition: all 0.6s ease-out;
-    -webkit-transition: all 0.6s ease-out;
-    -o-transition: all 0.6s ease-out;
-  }
-}
-
-
-.jtk-ep {
-  .ep {
-    display: block;
-  }
-}
-
-.ep {
-  position: absolute;
-  top: -4%;
-  right: -1px;
-  width: 1em;
-  height: 1em;
-  z-index: 12;
-  background-color: orange;
-  cursor: pointer;
-  box-shadow: 0 0 2px black;
-  -webkit-transition: -webkit-box-shadow 0.25s ease-in;
-  -moz-transition: -moz-box-shadow 0.25s ease-in;
-  transition: box-shadow 0.25s ease-in;
-  border-radius:100%;
-  display: none;
-}
-
-.ep:hover {
-  box-shadow: 0 0 6px black;
-}
-
-.statemachine-demo .jtk-endpoint {
-  z-index: 3;
-}
-
-#canvas {
-  .dot-style {
-    opacity: 0;
-  }
-}
-
-.form-mirror {
+ 
+.dag-chart {
   width: 100%;
-  position: relative;
-  z-index: 0;
-  .CodeMirror {
-    height:auto;
-    min-height: 72px;
-  }
+  height: calc(100vh - 100px);
+  padding: 10px;
+  background: #f2f3f7;
 
-  .CodeMirror-scroll {
-    height:auto;
-    min-height: 72px;
-    overflow-y: hidden;
-    overflow-x: auto;
+  &.full-screen {
+    position: fixed;
+    width: 100%;
+    height: 100%;
+    top: 0;
+    left: 0;
+    z-index: 9999;
   }
 }
-
-.ans-modal-box.ans-drawer.ans-drawer-right.dagMask.mask {
-    width: 628px;
-    left: auto;
-}
-
-
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/dag.vue b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/dag.vue
old mode 100755
new mode 100644
index b927a29..e7169b1
--- a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/dag.vue
+++ b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/dag.vue
@@ -15,890 +15,477 @@
  * limitations under the License.
  */
 <template>
-  <div class="clearfix dag-model" >
-    <div class="toolbar">
-      <div class="title"><span>{{$t('Toolbar')}}</span></div>
-      <div class="toolbar-btn">
-        <div class="bar-box roundedRect jtk-draggable jtk-droppable jtk-endpoint-anchor jtk-connected"
-             :class="v === dagBarId ? 'active' : ''"
-             :id="v"
-             :key="v"
-             v-for="(item,v) in tasksTypeList"
-             @mousedown="_getDagId(v)">
-          <div data-toggle="tooltip" :title="item.desc">
-            <div class="icos" :class="'icos-' + v" ></div>
-          </div>
-        </div>
-      </div>
-    </div>
-    <div class="dag-contect">
-      <div class="dag-toolbar">
-        <div class="assist-btn">
-          <el-tooltip :content="$t('View variables')" placement="top" :enterable="false">
-           <span>
-            <el-button
-              style="vertical-align: middle;"
-              type="primary"
-              size="mini"
-              :disabled="$route.name !== 'projects-instance-details'"
-              @click="_toggleView"
-              icon="el-icon-c-scale-to-original">
-            </el-button>
-           </span>
-          </el-tooltip>
-          <el-tooltip :content="$t('Startup parameter')" placement="top" :enterable="false">
-            <span>
-              <el-button
-                style="vertical-align: middle;"
-                type="primary"
-                size="mini"
-                :disabled="$route.name !== 'projects-instance-details'"
-                @click="_toggleParam"
-                icon="el-icon-arrow-right">
-              </el-button>
-            </span>
-          </el-tooltip>
-          <span class="name">{{name}}</span>
-          &nbsp;
-          <span v-if="name"  class="copy-name" @click="_copyName" :data-clipboard-text="name"><em class="el-icon-copy-document" data-container="body"  data-toggle="tooltip" :title="$t('Copy name')" ></em></span>
-        </div>
-        <div class="save-btn">
-          <div class="operation" style="vertical-align: middle;">
-            <a href="javascript:"
-               v-for="(item,$index) in toolOperList"
-               :class="_operationClass(item)"
-               :id="item.code"
-               :key="$index"
-               @click="_ckOperation(item,$event)">
-              <el-tooltip :content="item.desc" placement="top" :enterable="false">
-                <span><el-button type="text" class="operBtn" :icon="item.icon"></el-button></span>
-              </el-tooltip>
-            </a>
-          </div>
-          <el-tooltip :content="$t('Format DAG')" placement="top" :enterable="false">
-            <span>
-              <el-button
-                type="primary"
-                icon="el-icon-caret-right"
-                size="mini"
-                v-if="(type === 'instance' || 'definition') && urlParam.id !=undefined"
-                style="vertical-align: middle;"
-                @click="dagAutomaticLayout">
-              </el-button>
-            </span>
-          </el-tooltip>
-          <el-tooltip :content="$t('Refresh DAG status')" placement="top" :enterable="false">
-            <span>
-              <el-button
-                style="vertical-align: middle;"
-                icon="el-icon-refresh"
-                type="primary"
-                :loading="isRefresh"
-                v-if="type === 'instance'"
-                @click="!isRefresh && _refresh()"
-                size="mini" >
-              </el-button>
-            </span>
-          </el-tooltip>
-          <el-button
-                  v-if="isRtTasks"
-                  style="vertical-align: middle;"
-                  type="primary"
-                  size="mini"
-                  icon="el-icon-back"
-                  @click="_rtNodesDag" >
-            {{$t('Return_1')}}
-          </el-button>
-          <span>
-            <el-button
-              type="primary"
-              icon="el-icon-switch-button"
-              size="mini"
-              v-if="(type === 'instance' || 'definition') "
-              style="vertical-align: middle;"
-              @click="_closeDAG">
-              {{$t('Close')}}
-            </el-button>
-          </span>
-          <el-button
-                  style="vertical-align: middle;"
-                  type="primary"
-                  size="mini"
-                  :loading="spinnerLoading"
-                  @click="_saveChart"
-                  icon="el-icon-document-checked"
-                  >
-            {{spinnerLoading ? 'Loading...' : $t('Save')}}
-          </el-button>
-          <span>
-            <el-button
-              style="vertical-align: middle;"
-              type="primary"
-              size="mini"
-              :loading="spinnerLoading"
-              @click="_version"
-              :disabled="$route.params.id == null"
-              icon="el-icon-info">
-              {{spinnerLoading ? 'Loading...' : $t('Version Info')}}
-            </el-button>
-          </span>
-        </div>
-      </div>
-      <div class="scrollbar dag-container">
-        <div class="jtk-demo" id="jtk-demo">
-          <div class="jtk-demo-canvas canvas-wide statemachine-demo jtk-surface jtk-surface-nopan jtk-draggable" id="canvas" ></div>
-        </div>
-      </div>
-      <el-drawer
-        :visible.sync="drawer"
-        size=""
-        :with-header="false">
-        <m-versions :versionData = versionData :isInstance="type === 'instance'" @mVersionSwitchProcessDefinitionVersion="mVersionSwitchProcessDefinitionVersion" @mVersionGetProcessDefinitionVersionsPage="mVersionGetProcessDefinitionVersionsPage" @mVersionDeleteProcessDefinitionVersion="mVersionDeleteProcessDefinitionVersion" @closeVersion="closeVersion"></m-versions>
-      </el-drawer>
-      <el-drawer
-        :visible.sync="nodeDrawer"
-        size=""
-        :with-header="false">
-        <m-form-model v-if="nodeDrawer" :nodeData=nodeData @seeHistory="seeHistory" @addTaskInfo="addTaskInfo" @cacheTaskInfo="cacheTaskInfo" @close="close" @onSubProcess="onSubProcess"></m-form-model>
-      </el-drawer>
-      <el-drawer
-        :visible.sync="lineDrawer"
-        size=""
-        :wrapperClosable="false"
-        :with-header="false">
-        <m-form-line-model :lineData = lineData @addLineInfo="addLineInfo" @cancel="cancel"></m-form-line-model>
-      </el-drawer>
-      <el-drawer
-        :visible.sync="udpDrawer"
-        size=""
-        :wrapperClosable="false"
-        :with-header="false">
-        <m-udp></m-udp>
-      </el-drawer>
-      <el-dialog
-        :title="$t('Set the DAG diagram name')"
-        :visible.sync="dialogVisible"
-        width="auto">
-        <m-udp ref="mUdp" @onUdp="onUdpDialog" @close="closeDialog"></m-udp>
-      </el-dialog>
-      <el-dialog
-        :title="$t('Please set the parameters before starting')"
-        :visible.sync="startDialog"
-        width="auto">
-        <m-start :startData= "startData" :startNodeList="startNodeList" :sourceType="sourceType" @onUpdateStart="onUpdateStart" @closeStart="closeStart"></m-start>
-      </el-dialog>
-    </div>
+  <div :class="['dag-chart', fullScreen ? 'full-screen' : '']">
+    <dag-toolbar />
+    <dag-canvas ref="canvas" />
+    <el-drawer
+      :visible.sync="taskDrawer"
+      size=""
+      :with-header="false"
+      :wrapperClosable="false"
+    >
+      <m-form-model
+        v-if="taskDrawer"
+        :nodeData="nodeData"
+        @seeHistory="seeHistory"
+        @addTaskInfo="addTaskInfo"
+        @close="closeTaskDrawer"
+        @onSubProcess="toSubProcess"
+      ></m-form-model>
+    </el-drawer>
+    <el-dialog
+      :title="$t('Set the DAG diagram name')"
+      :visible.sync="saveDialog"
+      width="auto"
+    >
+      <m-udp ref="mUdp" @onUdp="onSave" @close="cancelSave"></m-udp>
+    </el-dialog>
+    <el-dialog
+      :title="$t('Please set the parameters before starting')"
+      :visible.sync="startDialog"
+      width="auto"
+    >
+      <m-start
+        :startData="{ code: definitionCode, name: name }"
+        :startNodeList="startTaskName"
+        :sourceType="'contextmenu'"
+        @onUpdateStart="onUpdateStart"
+        @closeStart="closeStart"
+      ></m-start>
+    </el-dialog>
+    <edge-edit-model ref="edgeEditModel" />
   </div>
 </template>
+
 <script>
-  import _ from 'lodash'
-  import Dag from './dag'
-  import mUdp from './udp/udp'
-  import i18n from '@/module/i18n'
-  import { jsPlumb } from 'jsplumb'
-  import Clipboard from 'clipboard'
-  import { allNodesId } from './plugIn/util'
-  import { toolOper, tasksType } from './config'
-  import mFormModel from './formModel/formModel'
-  import mFormLineModel from './formModel/formLineModel'
-  import { formatDate } from '@/module/filter/filter'
-  import { findComponentDownward } from '@/module/util/'
-  import disabledState from '@/module/mixin/disabledState'
+  import { debounce } from 'lodash'
+  import dagToolbar from './canvas/toolbar.vue'
+  import dagCanvas from './canvas/canvas.vue'
+  import mFormModel from '../_source/formModel/formModel.vue'
   import { mapActions, mapState, mapMutations } from 'vuex'
-  import mStart from '../../projects/pages/definition/pages/list/_source/start'
-  import mVersions from '../../projects/pages/definition/pages/list/_source/versions'
+  import mUdp from '../_source/udp/udp.vue'
+  import mStart from '../../projects/pages/definition/pages/list/_source/start.vue'
+  import edgeEditModel from './canvas/edgeEditModel.vue'
 
-  let eventModel
+  const DEFAULT_NODE_DATA = {
+    id: null,
+    taskType: '',
+    self: {},
+    preNode: [],
+    rearList: [],
+    instanceId: null
+  }
 
   export default {
     name: 'dag-chart',
-    data () {
+    components: {
+      dagCanvas,
+      dagToolbar,
+      mFormModel,
+      mUdp,
+      mStart,
+      edgeEditModel
+    },
+    provide () {
       return {
-        tasksTypeList: tasksType,
-        toolOperList: toolOper(this),
-        dagBarId: null,
-        toolOperCode: '',
-        spinnerLoading: false,
-        urlParam: {
-          id: this.$route.params.id || null
-        },
-        isRtTasks: false,
-        isRefresh: false,
-        isLoading: false,
-        taskId: null,
-        arg: false,
-        versionData: {
-          processDefinition: {
-            id: null,
-            version: '',
-            releaseState: ''
-          },
-          processDefinitionVersions: [],
-          total: null,
-          pageNo: null,
-          pageSize: null
-        },
-        drawer: false,
-        nodeData: {
-          id: null,
-          taskType: '',
-          self: {},
-          preNode: [],
-          rearList: [],
-          instanceId: null
-        },
-        nodeDrawer: false,
-        lineData: {
-          id: null,
-          sourceId: '',
-          targetId: ''
-        },
-        lineDrawer: false,
-        udpDrawer: false,
-        dialogVisible: false,
-        startDialog: false,
-        startData: {},
-        startNodeList: '',
-        sourceType: ''
+        dagChart: this
       }
     },
-    mixins: [disabledState],
     props: {
       type: String,
       releaseState: String
     },
-    methods: {
-      ...mapActions('dag', ['saveDAGchart', 'updateInstance', 'updateDefinition', 'getTaskState', 'switchProcessDefinitionVersion', 'getProcessDefinitionVersionsPage', 'deleteProcessDefinitionVersion']),
-      ...mapMutations('dag', ['addTasks', 'cacheTasks', 'resetParams', 'setIsEditDag', 'setName', 'addConnects', 'resetLocalParam']),
-      startRunning (item, startNodeList, sourceType) {
-        this.startData = item
-        this.startNodeList = startNodeList
-        this.sourceType = sourceType
-        this.startDialog = true
-      },
-      onUpdateStart () {
-        this.startDialog = false
-      },
-      closeStart () {
-        this.startDialog = false
-      },
-      // DAG automatic layout
-      dagAutomaticLayout () {
-        if (this.store.state.dag.isEditDag) {
-          this.$message.warning(`${i18n.$t('Please save the DAG before formatting')}`)
-          return false
-        }
-        $('#canvas').html('')
+    data () {
+      return {
+        fullScreen: false,
+        taskDrawer: false,
+        nodeData: { ...DEFAULT_NODE_DATA },
+        saveDialog: false,
+        isLoading: false,
+        definitionCode: 0,
+        startDialog: false,
+        startTaskName: ''
+      }
+    },
+    mounted () {
+      // TODO
+      window._debug = this
 
-        // Destroy round robin
-        Dag.init({
-          dag: this,
-          instance: jsPlumb.getInstance({
-            Endpoint: [
-              'Dot', { radius: 1, cssClass: 'dot-style' }
-            ],
-            Connector: 'Bezier',
-            PaintStyle: { lineWidth: 2, stroke: '#456' }, // Connection style
-            ConnectionOverlays: [
-              [
-                'Arrow',
-                {
-                  location: 1,
-                  id: 'arrow',
-                  length: 12,
-                  foldback: 0.8
-                }
-              ],
-              ['Label', {
-                location: 0.5,
-                id: 'label'
-              }]
-            ],
-            Container: 'canvas',
-            ConnectionsDetachable: true
-          })
-        })
-        if (this.tasks.length) {
-          Dag.backfill(true)
-          if (this.type === 'instance') {
-            this._getTaskState(false).then(res => {})
-          }
-        } else {
-          Dag.create()
-        }
-      },
+      if (this.type === 'instance') {
+        this.definitionCode = this.$route.query.id
+      } else if (this.type === 'definition') {
+        this.definitionCode = this.$route.params.code
+      }
 
-      init (args) {
-        if (this.tasks.length) {
-          Dag.backfill(args)
-          // Process instances can view status
-          if (this.type === 'instance') {
-            this._getTaskState(false).then(res => {})
-            // Round robin acquisition status
-            this.setIntervalP = setInterval(() => {
-              this._getTaskState(true).then(res => {})
-            }, 90000)
-          }
-        } else {
-          Dag.create()
-        }
-      },
+      this.resizeDebounceFunc = debounce(this.canvasResize, 200)
+      window.addEventListener('resize', this.resizeDebounceFunc)
+      this.$refs.canvas.graphInit(!this.isDetails)
+
+      this.backfill()
+    },
+    unmounted () {
+      window.removeEventListener('resize', this.resizeDebounceFunc)
+    },
+    computed: {
+      ...mapState('dag', [
+        'tasks',
+        'locations',
+        'connects',
+        'isEditDag',
+        'name',
+        'isDetails',
+        'projectCode'
+      ])
+    },
+    methods: {
+      ...mapActions('dag', [
+        'saveDAGchart',
+        'updateInstance',
+        'updateDefinition',
+        'getTaskState',
+        'getStartCheck',
+        'genTaskCodeList',
+        'switchProcessDefinitionVersion',
+        'getProcessDefinitionVersionsPage',
+        'deleteProcessDefinitionVersion'
+      ]),
+      ...mapMutations('dag', [
+        'addTask',
+        'setTasks',
+        'setConnects',
+        'resetParams',
+        'setIsEditDag',
+        'setName',
+        'setLocations',
+        'resetLocalParam'
+      ]),
       /**
-       * copy name
+       * Toggle full screen
        */
-      _copyName () {
-        let clipboard = new Clipboard('.copy-name')
-        clipboard.on('success', e => {
-          this.$message.success(`${i18n.$t('Copy success')}`)
-          // Free memory
-          clipboard.destroy()
-        })
-        clipboard.on('error', e => {
-          // Copy is not supported
-          this.$message.warning(`${i18n.$t('The browser does not support automatic copying')}`)
-          // Free memory
-          clipboard.destroy()
-        })
+      canvasResize () {
+        const canvas = this.$refs.canvas
+        canvas && canvas.paperResize()
+      },
+      toggleFullScreen () {
+        this.fullScreen = !this.fullScreen
+        this.$nextTick(this.canvasResize)
       },
       /**
-       * Get state interface
-       * @param isReset Whether to manually refresh
+       * Task Drawer
+       * @param {boolean} visible
        */
-      _getTaskState (isReset) {
-        return new Promise((resolve, reject) => {
-          this.getTaskState(this.urlParam.id).then(res => {
-            let data = res.list
-            let state = res.processInstanceState
-            let taskList = res.taskList
-            let idArr = allNodesId()
-            const titleTpl = (item, desc) => {
-              let $item = _.filter(taskList, v => v.name === item.name)[0]
-              return `<div style="text-align: left">${i18n.$t('Name')}:${$item.name}</br>${i18n.$t('State')}:${desc}</br>${i18n.$t('type')}:${$item.taskType}</br>${i18n.$t('host')}:${$item.host || '-'}</br>${i18n.$t('Retry Count')}:${$item.retryTimes}</br>${i18n.$t('Submit Time')}:${formatDate($item.submitTime)}</br>${i18n.$t('Start Time')}:${formatDate($item.startTime)}</br>${i18n.$t('End Time')}:${$item.endTime ? formatDate($item.endTime) : '-'}</br></div>`
-            }
-
-            // remove tip state dom
-            $('.w').find('.state-p').html('')
-            const newTask = []
-            data.forEach(v1 => {
-              idArr.forEach(v2 => {
-                if (v2.name === v1.name) {
-                  let dom = $(`#${v2.id}`)
-                  let state = dom.find('.state-p')
-                  let depState = ''
-                  taskList.forEach(item => {
-                    if (item.name === v1.name) {
-                      depState = item.state
-                      const params = item.taskJson ? JSON.parse(item.taskJson).params : ''
-                      let localParam = params.localParams || []
-                      newTask.push({
-                        id: v2.id,
-                        localParam
-                      })
-                    }
-                  })
-                  dom.attr('data-state-id', v1.stateId)
-                  dom.attr('data-dependent-result', v1.dependentResult || '')
-                  dom.attr('data-dependent-depState', depState)
-                  state.append(`<strong class="${v1.icoUnicode} ${v1.isSpin ? 'as as-spin' : ''}" style="color:${v1.color}" data-toggle="tooltip" data-html="true" data-container="body"></strong>`)
-                  state.find('strong').attr('title', titleTpl(v2, v1.desc))
-                }
-              })
-            })
-            if (state === 'PAUSE' || state === 'STOP' || state === 'FAILURE' || this.state === 'SUCCESS') {
-              // Manual refresh does not regain large json
-              if (isReset) {
-                findComponentDownward(this.$root, `${this.type}-details`)._reset()
-              }
-            }
-            if (!isReset) {
-              this.resetLocalParam(newTask)
-            }
-            resolve()
-          })
-        })
+      toggleTaskDrawer (visible) {
+        this.taskDrawer = visible
       },
       /**
-       * Get the action bar id
-       * @param item
+       * Set the current node data
        */
-      _getDagId (v) {
-        // if (this.isDetails) {
-        //   return
-        // }
-        this.dagBarId = v
+      setNodeData (nodeData) {
+        this.nodeData = Object.assign(DEFAULT_NODE_DATA, nodeData)
       },
       /**
-       * operating
+       * open form model
+       * @desc Edit task config
+       * @param {number} taskCode
+       * @param {string} taskType
        */
-      _ckOperation (item) {
-        let is = true
-        let code = ''
-
-        if (item.disable) {
-          return
-        }
-
-        if (this.toolOperCode === item.code) {
-          this.toolOperCode = ''
-          code = item.code
-          is = false
-        } else {
-          this.toolOperCode = item.code
-          code = this.toolOperCode
-          is = true
-        }
-
-        // event type
-        Dag.toolbarEvent({
-          item: item,
-          code: code,
-          is: is
+      openFormModel (taskCode, taskType) {
+        this.setNodeData({
+          id: taskCode,
+          taskType: taskType
         })
+        this.toggleTaskDrawer(true)
+      },
+      addTaskInfo ({ item, fromThis }) {
+        this.addTask(item)
+        this.$refs.canvas.setNodeName(item.code, item.name)
+        this.taskDrawer = false
       },
-      _operationClass (item) {
-        return this.toolOperCode === item.code ? 'active' : ''
-        // if (item.disable) {
-        //   return this.toolOperCode === item.code ? 'active' : ''
-        // } else {
-        //   return 'disable'
-        // }
+      closeTaskDrawer ({ item, flag, fromThis }) {
+        if (flag) {
+          const canvas = this.$refs.canvas
+          canvas.removeNode(this.nodeData.id)
+        }
+        this.taskDrawer = false
       },
       /**
-       * Storage interface
+       * Save dialog
        */
-      _save (sourceType) {
+      toggleSaveDialog (value) {
+        this.saveDialog = value
+      },
+      onSave (sourceType) {
+        this.toggleSaveDialog(false)
         return new Promise((resolve, reject) => {
-          this.spinnerLoading = true
-          // Storage store
-          Dag.saveStore().then(res => {
-            if (this._verifConditions(res.tasks)) {
-              if (this.urlParam.id) {
-                /**
-                 * Edit
-                 * @param saveInstanceEditDAGChart => Process instance editing
-                 * @param saveEditDAGChart => Process definition editing
-                 */
-                this[this.type === 'instance' ? 'updateInstance' : 'updateDefinition'](this.urlParam.id).then(res => {
+          this.isLoading = true
+
+          let tasks = this.tasks || []
+          const edges = this.$refs.canvas.getEdges()
+          const nodes = this.$refs.canvas.getNodes()
+
+          const connects = this.buildConnects(edges, tasks)
+
+          this.setConnects(connects)
+
+          const locations = nodes.map(node => {
+            return {
+              taskCode: node.id,
+              x: node.position.x,
+              y: node.position.y
+            }
+          })
+
+          this.setLocations(locations)
+
+          resolve({
+            connects: connects,
+            tasks: tasks,
+            locations: locations
+          })
+        }).then((res) => {
+          if (this._verifConditions(res.tasks)) {
+            const definitionCode = this.definitionCode
+            if (definitionCode) {
+              /**
+               * Edit
+               * @param saveInstanceEditDAGChart => Process instance editing
+               * @param saveEditDAGChart => Process definition editing
+               */
+              return this[
+                this.type === 'instance' ? 'updateInstance' : 'updateDefinition'
+              ](definitionCode)
+                .then((res) => {
                   // this.$message.success(res.msg)
                   this.$message({
                     message: res.msg,
                     type: 'success',
                     offset: 80
                   })
-                  this.spinnerLoading = false
+                  this.isLoading = false
                   // Jump process definition
                   if (this.type === 'instance') {
-                    this.$router.push({ path: `/projects/${this.projectId}/instance/list/${this.urlParam.id}` })
+                    this.$router.push({
+                      path: `/projects/${this.projectCode}/instance/list/${definitionCode}`
+                    })
                   } else {
-                    this.$router.push({ path: `/projects/${this.projectId}/definition/list/${this.urlParam.id}` })
+                    this.$router.push({
+                      path: `/projects/${this.projectCode}/definition/list/${definitionCode}`
+                    })
                   }
-                  resolve()
-                }).catch(e => {
+                })
+                .catch((e) => {
                   this.$message.error(e.msg || '')
-                  this.spinnerLoading = false
-                  reject(e)
+                  this.isLoading = false
                 })
-              } else {
-                // New
-                this.saveDAGchart().then(res => {
+            } else {
+              // New
+              return this.saveDAGchart()
+                .then((res) => {
                   this.$message.success(res.msg)
-                  this.spinnerLoading = false
+                  this.isLoading = false
                   // source @/conf/home/pages/dag/_source/editAffirmModel/index.js
                   if (sourceType !== 'affirm') {
                     // Jump process definition
                     this.$router.push({ name: 'projects-definition-list' })
                   }
-                  resolve()
-                }).catch(e => {
+                })
+                .catch((e) => {
                   this.$message.error(e.msg || '')
                   this.setName('')
-                  this.spinnerLoading = false
-                  reject(e)
+                  this.isLoading = false
                 })
-              }
             }
-          })
+          }
         })
       },
-      _closeDAG () {
-        let $name = this.$route.name
-        if ($name && $name.indexOf('definition') !== -1) {
-          this.$router.push({ name: 'projects-definition-list' })
-        } else {
-          this.$router.push({ name: 'projects-instance-list' })
-        }
-      },
       _verifConditions (value) {
         let tasks = value
         let bool = true
-        tasks.map(v => {
-          if (v.type === 'CONDITIONS' && (v.conditionResult.successNode[0] === '' || v.conditionResult.successNode[0] === null || v.conditionResult.failedNode[0] === '' || v.conditionResult.failedNode[0] === null)) {
+        tasks.map((v) => {
+          if (
+            v.taskType === 'CONDITIONS' &&
+            (v.taskParams.conditionResult.successNode[0] === '' ||
+              v.taskParams.conditionResult.successNode[0] === null ||
+              v.taskParams.conditionResult.failedNode[0] === '' ||
+              v.taskParams.conditionResult.failedNode[0] === null)
+          ) {
             bool = false
             return false
           }
         })
         if (!bool) {
-          this.$message.warning(`${i18n.$t('Successful branch flow and failed branch flow are required')}`)
-          this.spinnerLoading = false
+          this.$message.warning(
+            `${this.$t(
+              'Successful branch flow and failed branch flow are required'
+            )}`
+          )
+          this.isLoading = false
           return false
         }
         return true
       },
-      onUdpDialog () {
-        this._save()
-        this.dialogVisible = false
-      },
-      closeDialog () {
-        this.dialogVisible = false
+      cancelSave () {
+        this.toggleSaveDialog(false)
       },
       /**
-       * Save chart
+       * build graph json
        */
-      _saveChart () {
-        // Verify node
-        if (!this.tasks.length) {
-          this.$message.warning(`${i18n.$t('Failed to create node to save')}`)
-          return
-        }
-
-        this.dialogVisible = true
-        this.$nextTick(() => {
-          this.$refs.mUdp.reloadParam()
+      buildGraphJSON (tasks, locations, connects) {
+        const nodes = []
+        const edges = []
+        tasks.forEach((task) => {
+          const location = locations.find(l => l.taskCode === task.code) || {}
+          const node = this.$refs.canvas.genNodeJSON(
+            task.code,
+            task.taskType,
+            task.name,
+            {
+              x: location.x,
+              y: location.y
+            }
+          )
+          nodes.push(node)
+        })
+        connects.filter(r => !!r.preTaskCode).forEach((c) => {
+          const edge = this.$refs.canvas.genEdgeJSON(
+            c.preTaskCode,
+            c.postTaskCode,
+            c.name
+          )
+          edges.push(edge)
         })
+        return {
+          nodes,
+          edges
+        }
       },
       /**
-       * Return to the previous child node
+       * Build connects by edges and tasks
+       * @param {Edge[]} edges
+       * @param {Task[]} tasks
+       * @returns
        */
-      _rtNodesDag () {
-        let getIds = this.$route.query.subProcessIds
-        let idsArr = getIds.split(',')
-        let ids = idsArr.slice(0, idsArr.length - 1)
-        let id = idsArr[idsArr.length - 1]
-        let query = {}
+      buildConnects (edges, tasks) {
+        const preTaskMap = {}
+        const tasksMap = {}
 
-        if (id !== idsArr[0]) {
-          query = { subProcessIds: ids.join(',') }
-        }
-        let $name = this.$route.name.split('-')
-        this.$router.push({ path: `/${$name[0]}/${$name[1]}/list/${id}`, query: query })
+        edges.forEach((edge) => {
+          preTaskMap[edge.targetId] = {
+            sourceId: edge.sourceId,
+            edgeLabel: edge.label || ''
+          }
+        })
+        tasks.forEach(task => {
+          tasksMap[task.code] = task
+        })
+
+        return tasks.map(task => {
+          const preTask = preTaskMap[task.code]
+          return {
+            name: preTask ? preTask.edgeLabel : '',
+            preTaskCode: preTask ? preTask.sourceId : 0,
+            preTaskVersion: preTask ? tasksMap[preTask.sourceId].version : 0,
+            postTaskCode: task.code,
+            postTaskVersion: tasksMap[task.code].version || 0,
+            // conditionType and conditionParams are reserved
+            conditionType: 0,
+            conditionParams: {}
+          }
+        })
+      },
+      backfill () {
+        const tasks = this.tasks
+        const locations = this.locations
+        const connects = this.connects
+        const json = this.buildGraphJSON(tasks, locations, connects)
+        this.$refs.canvas.fromJSON(json)
       },
       /**
-       * Subprocess processing
-       * @param subProcessId Subprocess ID
+       * Return to the previous process
        */
-      _subProcessHandle (subProcessId) {
-        let subProcessIds = []
-        let getIds = this.$route.query.subProcessIds
+      returnToPrevProcess () {
+        let $name = this.$route.name.split('-')
+        let subProcessCodes = this.$route.query.subProcessCodes
+        let codes = subProcessCodes.split(',')
+        const last = codes.pop()
+        this.$router.push({
+          path: `/${$name[0]}/${this.projectId}/${$name[1]}/list/${last}`,
+          query: codes.length > 0 ? { subProcessCodes: codes.join(',') } : null
+        })
+      },
+      toSubProcess ({ subProcessCode, fromThis }) {
+        let subProcessCodes = []
+        let getIds = this.$route.query.subProcessCodes
         if (getIds) {
           let newId = getIds.split(',')
-          newId.push(this.urlParam.id)
-          subProcessIds = newId
+          newId.push(this.definitionCode)
+          subProcessCodes = newId
         } else {
-          subProcessIds.push(this.urlParam.id)
+          subProcessCodes.push(this.definitionCode)
         }
         let $name = this.$route.name.split('-')
-        this.$router.push({ path: `/${$name[0]}/${$name[1]}/list/${subProcessId}`, query: { subProcessIds: subProcessIds.join(',') } })
-      },
-      /**
-       * Refresh data
-       */
-      _refresh () {
-        this.isRefresh = true
-        this._getTaskState(false).then(res => {
-          setTimeout(() => {
-            this.isRefresh = false
-            this.$message.success(`${i18n.$t('Refresh status succeeded')}`)
-          }, 2200)
+        this.$router.push({
+          path: `/${$name[0]}/${this.projectCode}/${$name[1]}/list/${subProcessCode}`,
+          query: { subProcessCodes: subProcessCodes.join(',') }
         })
       },
-      /**
-       * View variables
-       */
-      _toggleView () {
-        findComponentDownward(this.$root, 'assist-dag-index')._toggleView()
-      },
-
-      /**
-       * Starting parameters
-       */
-      _toggleParam () {
-        findComponentDownward(this.$root, 'starting-params-dag-index')._toggleParam()
-      },
-      addLineInfo ({ item, fromThis }) {
-        this.addConnects(item)
-        this.lineDrawer = false
-      },
-      cancel ({ fromThis }) {
-        this.lineDrawer = false
-      },
-
-      /**
-       * Create a node popup layer
-       * @param Object id
-       */
-      _createLineLabel ({ id, sourceId, targetId }) {
-        this.lineData.id = id
-        this.lineData.sourceId = sourceId
-        this.lineData.targetId = targetId
-        this.lineDrawer = true
-      },
-
       seeHistory (taskName) {
-        this.nodeData.self.$router.push({
+        this.$router.push({
           name: 'task-instance',
           query: {
-            processInstanceId: this.nodeData.self.$route.params.id,
+            processInstanceId: this.$route.params.code,
             taskName: taskName
           }
         })
       },
-
-      addTaskInfo ({ item, fromThis }) {
-        this.addTasks(item)
-        this.nodeDrawer = false
-      },
-
-      cacheTaskInfo ({ item, fromThis }) {
-        this.cacheTasks(item)
-      },
-
-      close ({ item, flag, fromThis }) {
-        this.addTasks(item)
-        // Edit status does not allow deletion of nodes
-        if (flag) {
-          jsPlumb.remove(this.nodeData.id)
-        }
-        this.nodeDrawer = false
-      },
-      onSubProcess ({ subProcessId, fromThis }) {
-        this._subProcessHandle(subProcessId)
-      },
-
-      _createNodes ({ id, type }) {
-        let self = this
-        let preNode = []
-        let rearNode = []
-        let rearList = []
-        $('div[data-targetarr*="' + id + '"]').each(function () {
-          rearNode.push($(this).attr('id'))
-        })
-
-        if (rearNode.length > 0) {
-          rearNode.forEach(v => {
-            let rearobj = {}
-            rearobj.value = $(`#${v}`).find('.name-p').text()
-            rearobj.label = $(`#${v}`).find('.name-p').text()
-            rearList.push(rearobj)
-          })
-        } else {
-          rearList = []
-        }
-        let targetarr = $(`#${id}`).attr('data-targetarr')
-        if (targetarr) {
-          let nodearr = targetarr.split(',')
-          nodearr.forEach(v => {
-            let nodeobj = {}
-            nodeobj.value = $(`#${v}`).find('.name-p').text()
-            nodeobj.label = $(`#${v}`).find('.name-p').text()
-            preNode.push(nodeobj)
-          })
-        } else {
-          preNode = []
-        }
-
-        this.taskId = id
-        type = type || self.dagBarId
-
-        this.nodeData.id = id
-        this.nodeData.taskType = type
-        this.nodeData.self = self
-        this.nodeData.preNode = preNode
-        this.nodeData.rearList = rearList
-        this.nodeData.instanceId = this.$route.params.id
-
-        this.nodeDrawer = true
-      },
-      removeEventModelById ($id) {
-        if (eventModel && this.taskId === $id) {
-          eventModel.remove()
-        }
-      },
-
       /**
-        * switch version in process definition version list
-        *
-        * @param version the version user want to change
-        * @param processDefinitionId the process definition id
-        * @param fromThis fromThis
-      */
-      mVersionSwitchProcessDefinitionVersion ({ version, processDefinitionId, fromThis }) {
-        this.$store.state.dag.isSwitchVersion = true
-        this.switchProcessDefinitionVersion({
-          version: version,
-          processDefinitionId: processDefinitionId
-        }).then(res => {
-          this.$message.success($t('Switch Version Successfully'))
-          this.$router.push({ path: `/projects/${this.projectId}/definition/list/${processDefinitionId}` })
-        }).catch(e => {
-          this.$store.state.dag.isSwitchVersion = false
-          this.$message.error(e.msg || '')
-        })
+       * Start dialog
+       */
+      startRunning (taskName) {
+        this.startTaskName = taskName
+        this.getStartCheck({ processDefinitionCode: this.definitionCode }).then(
+          (res) => {
+            this.startDialog = true
+          }
+        )
       },
-
-      /**
-        * Paging event of process definition versions
-        *
-        * @param pageNo page number
-        * @param pageSize page size
-        * @param processDefinitionId the process definition id of page version
-        * @param fromThis fromThis
-      */
-      mVersionGetProcessDefinitionVersionsPage ({ pageNo, pageSize, processDefinitionCode, fromThis }) {
-        this.getProcessDefinitionVersionsPage({
-          pageNo: pageNo,
-          pageSize: pageSize,
-          processDefinitionCode: processDefinitionCode
-        }).then(res => {
-          this.versionData.processDefinitionVersions = res.data.totalList
-          this.versionData.total = res.data.total
-          this.versionData.pageSize = res.data.pageSize
-          this.versionData.pageNo = res.data.currentPage
-        }).catch(e => {
-          this.$message.error(e.msg || '')
-        })
+      onUpdateStart () {
+        this.startDialog = false
       },
-
-      /**
-       * delete one version of process definition
-       *
-       * @param version the version need to delete
-       * @param processDefinitionId the process definition id user want to delete
-       * @param fromThis fromThis
-       */
-      mVersionDeleteProcessDefinitionVersion ({ version, processDefinitionId, processDefinitionCode, fromThis }) {
-        this.deleteProcessDefinitionVersion({
-          version: version,
-          processDefinitionId: processDefinitionId
-        }).then(res => {
-          this.$message.success(res.msg || '')
-          this.mVersionGetProcessDefinitionVersionsPage({
-            pageNo: 1,
-            pageSize: 10,
-            processDefinitionCode: processDefinitionCode,
-            fromThis: fromThis
-          })
-        }).catch(e => {
-          this.$message.error(e.msg || '')
-        })
+      closeStart () {
+        this.startDialog = false
       },
       /**
-       * query the process definition pagination version
+       * Verify whether edge is valid
+       * The number of edges start with CONDITIONS task cannot be greater than 2
        */
-      _version (item) {
-        this.getProcessDefinitionVersionsPage({
-          pageNo: 1,
-          pageSize: 10,
-          processDefinitionCode: this.store.state.dag.code
-        }).then(res => {
-          let processDefinitionVersions = res.data.totalList
-          let total = res.data.total
-          let pageSize = res.data.pageSize
-          let pageNo = res.data.currentPage
-          this.versionData.processDefinition.id = this.urlParam.id
-          this.versionData.processDefinition.code = this.store.state.dag.code
-          this.versionData.processDefinition.version = this.$store.state.dag.version
-          this.versionData.processDefinition.releaseState = this.releaseState
-          this.versionData.processDefinitionVersions = processDefinitionVersions
-          this.versionData.total = total
-          this.versionData.pageNo = pageNo
-          this.versionData.pageSize = pageSize
-          this.drawer = true
-        }).catch(e => {
-          this.$message.error(e.msg || '')
-        })
-      },
-
-      closeVersion () {
-        this.drawer = false
-      }
-    },
-    watch: {
-      tasks: {
-        deep: true,
-        handler (o) {
-          // Edit state does not allow deletion of node a...
-          this.setIsEditDag(true)
+      edgeIsValid (edge) {
+        const { sourceId } = edge
+        const sourceTask = this.tasks.find(task => task.code === sourceId)
+        if (sourceTask.taskType === 'CONDITIONS') {
+          const edges = this.$refs.canvas.getEdges()
+          return edges.filter(e => e.sourceId === sourceTask.code).length <= 2
         }
+        return true
       }
-    },
-    created () {
-      // Edit state does not allow deletion of node a...
-      this.setIsEditDag(false)
-
-      if (this.$route.query.subProcessIds) {
-        this.isRtTasks = true
-      }
-
-      Dag.init({
-        dag: this,
-        instance: jsPlumb.getInstance({
-          Endpoint: [
-            'Dot', { radius: 1, cssClass: 'dot-style' }
-          ],
-          Connector: 'Bezier',
-          PaintStyle: { lineWidth: 2, stroke: '#456' }, // Connection style
-          ConnectionOverlays: [
-            [
-              'Arrow',
-              {
-                location: 1,
-                id: 'arrow',
-                length: 12,
-                foldback: 0.8
-              }
-            ],
-            ['Label', {
-              location: 0.5,
-              id: 'label'
-            }]
-          ],
-          Container: 'canvas',
-          ConnectionsDetachable: true
-        })
-      })
-    },
-    mounted () {
-      this.init(this.arg)
-    },
-    beforeDestroy () {
-      this.resetParams()
-
-      // Destroy round robin
-      clearInterval(this.setIntervalP)
-    },
-    destroyed () {
-      if (eventModel) {
-        eventModel.remove()
-      }
-    },
-    computed: {
-      ...mapState('dag', ['tasks', 'locations', 'connects', 'isEditDag', 'name', 'projectId'])
-    },
-    components: { mVersions, mFormModel, mFormLineModel, mUdp, mStart }
+    }
   }
 </script>
 
-<style lang="scss" rel="stylesheet/scss">
-  @import "./dag";
-  .operBtn {
-    padding: 8px 6px;
-  }
+<style lang="scss" scoped>
+.dag-chart {
+  width: 100%;
+  height: calc(100vh - 100px);
+  padding: 10px;
+  background: #f2f3f7;
 
-  .el-drawer__body {
-    ::selection {
-      background: #409EFF;
-      color: white;
-    }
+  &.full-screen {
+    position: fixed;
+    width: 100%;
+    height: 100%;
+    top: 0;
+    left: 0;
+    z-index: 9999;
   }
-
+}
 </style>
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/formModel.vue b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/formModel.vue
index 7dfcbc6..20f5187 100644
--- a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/formModel.vue
+++ b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/formModel.vue
@@ -50,8 +50,8 @@
           <div slot="text">{{$t('Run flag')}}</div>
           <div slot="content">
             <el-radio-group v-model="runFlag" size="small">
-              <el-radio :label="'NORMAL'" :disabled="isDetails">{{$t('Normal')}}</el-radio>
-              <el-radio :label="'FORBIDDEN'" :disabled="isDetails">{{$t('Prohibition execution')}}</el-radio>
+              <el-radio :label="'YES'" :disabled="isDetails">{{$t('Normal')}}</el-radio>
+              <el-radio :label="'NO'" :disabled="isDetails">{{$t('Prohibition execution')}}</el-radio>
             </el-radio-group>
           </div>
         </m-list-box>
@@ -259,11 +259,12 @@
           :pre-node="nodeData.preNode">
         </m-conditions>
         <!-- Pre-tasks in workflow -->
-        <m-pre-tasks
+        <!-- TODO -->
+        <!-- <m-pre-tasks
           v-if="['SHELL', 'SUB_PROCESS'].indexOf(nodeData.taskType) > -1"
           @on-pre-tasks="_onPreTasks"
           ref="PRE_TASK"
-          :backfill-item="backfillItem"></m-pre-tasks>
+          :backfill-item="backfillItem"></m-pre-tasks> -->
       </div>
     </div>
     <div class="bottom-box">
@@ -276,7 +277,7 @@
 </template>
 <script>
   import _ from 'lodash'
-  import { mapActions } from 'vuex'
+  import { mapActions, mapState } from 'vuex'
   import mLog from './log'
   import mMr from './tasks/mr'
   import mSql from './tasks/sql'
@@ -287,7 +288,6 @@
   import mSpark from './tasks/spark'
   import mFlink from './tasks/flink'
   import mPython from './tasks/python'
-  import JSP from './../plugIn/jsPlumbHandle'
   import mProcedure from './tasks/procedure'
   import mDependent from './tasks/dependent'
   import mHttp from './tasks/http'
@@ -299,10 +299,9 @@
   import mTimeoutAlarm from './_source/timeoutAlarm'
   import mDependentTimeout from './_source/dependentTimeout'
   import mWorkerGroups from './_source/workerGroups'
-  import mPreTasks from './tasks/pre_tasks'
+  // import mPreTasks from './tasks/pre_tasks'
   import clickoutside from '@/module/util/clickoutside'
   import disabledState from '@/module/mixin/disabledState'
-  import { isNameExDag, rtBantpl } from './../plugIn/util'
   import mPriority from '@/module/components/priority/priority'
 
   export default {
@@ -337,7 +336,7 @@
         // Current node params data
         params: {},
         // Running sign
-        runFlag: 'NORMAL',
+        runFlag: 'YES',
         // The second echo problem caused by the node data is specifically which node hook caused the unfinished special treatment
         isContentBox: false,
         // Number of failed retries
@@ -380,6 +379,31 @@
     },
     methods: {
       ...mapActions('dag', ['getTaskInstanceList']),
+      taskToBackfillItem (task) {
+        return {
+          code: task.code,
+          conditionResult: task.taskParams.conditionResult,
+          delayTime: task.delayTime,
+          dependence: task.taskParams.dependence,
+          desc: task.description,
+          id: task.id,
+          maxRetryTimes: task.failRetryTimes,
+          name: task.name,
+          params: _.omit(task.taskParams, ['conditionResult', 'dependence']),
+          preTasks: [],
+          retryInterval: task.failRetryInterval,
+          runFlag: task.flag,
+          taskInstancePriority: task.taskPriority,
+          timeout: {
+            interval: task.timeout,
+            strategy: task.timeoutNotifyStrategy,
+            enable: task.timeoutFlag === 'OPEN'
+          },
+          type: task.taskType,
+          waitStartTimeout: task.waitStartTimeout,
+          workerGroup: task.workerGroup
+        }
+      },
       /**
        * depend
        */
@@ -449,8 +473,10 @@
             this.$message.error(e.msg || '')
           })
         } else {
+          const processDefinitionId = this.backfillItem.params.processDefinitionId
+          const process = this.processListS.find(process => process.processDefinition.id === processDefinitionId)
           this.$emit('onSubProcess', {
-            subProcessId: this.backfillItem.params.processDefinitionId,
+            subProcessCode: process.processDefinition.code,
             fromThis: this
           })
         }
@@ -461,39 +487,7 @@
       _onParams (o) {
         this.params = Object.assign({}, o)
       },
-
-      _onCacheParams (o) {
-        this.params = Object.assign(this.params, {}, o)
-        this._cacheItem()
-      },
-
-      _cacheItem () {
-        this.conditionResult.successNode[0] = this.successBranch
-        this.conditionResult.failedNode[0] = this.failedBranch
-        this.$emit('cacheTaskInfo', {
-          item: {
-            type: this.nodeData.taskType,
-            id: this.nodeData.id,
-            name: this.name,
-            code: this.code,
-            params: this.params,
-            desc: this.desc,
-            runFlag: this.runFlag,
-            conditionResult: this.conditionResult,
-            dependence: this.cacheDependence,
-            maxRetryTimes: this.maxRetryTimes,
-            retryInterval: this.retryInterval,
-            delayTime: this.delayTime,
-            timeout: this.timeout,
-            waitStartTimeout: this.waitStartTimeout,
-            taskInstancePriority: this.taskInstancePriority,
-            workerGroup: this.workerGroup,
-            status: this.status,
-            branch: this.branch
-          },
-          fromThis: this
-        })
-      },
+      _onCacheParams (o) {},
       /**
        * verification name
        */
@@ -510,7 +504,9 @@
           return true
         }
         // Name repeat depends on dom backfill dependent store
-        if (isNameExDag(this.name, _.isEmpty(this.backfillItem) ? 'dom' : 'backfill')) {
+        const tasks = this.store.state.dag.tasks
+        const task = tasks.find(t => t.name === 'this.name')
+        if (task) {
           this.$message.warning(`${i18n.$t('Name already exists')}`)
           return false
         }
@@ -558,66 +554,35 @@
           if (!this.$refs.PRE_TASK._verification()) {
             return
           } else {
-            // Sync data-targetarr
-            $(`#${this.nodeData.id}`).attr(
-              'data-targetarr', this.preTaskIdsInWorkflow ? this.preTaskIdsInWorkflow.join(',') : '')
-
-            // Update JSP connections
-            let plumbIns = JSP.JspInstance
-            let targetId = this.nodeData.id
-
-            // Update new connections
-            this.preTasksToAdd.map(sourceId => {
-              plumbIns.connect({
-                source: sourceId,
-                target: targetId,
-                type: 'basic',
-                paintStyle: { strokeWidth: 2, stroke: '#2d8cf0' },
-                HoverPaintStyle: { stroke: '#ccc', strokeWidth: 3 }
-              })
-            })
+            // TODO sync preTasks to graph
 
-            // Update remove connections
-            let currentConnects = plumbIns.getAllConnections()
-            let len = currentConnects.length
-            for (let i = 0; i < len; i++) {
-              if (this.preTasksToDelete.indexOf(currentConnects[i].sourceId) > -1 && currentConnects[i].targetId === targetId) {
-                plumbIns.deleteConnection(currentConnects[i])
-                i -= 1
-                len -= 1
-              }
-            }
           }
         }
-
-        $(`#${this.nodeData.id}`).find('span').text(this.name)
         this.conditionResult.successNode[0] = this.successBranch
         this.conditionResult.failedNode[0] = this.failedBranch
-        // Store the corresponding node data structure
         this.$emit('addTaskInfo', {
           item: {
-            type: this.nodeData.taskType,
-            id: this.nodeData.id,
+            code: this.nodeData.id,
             name: this.name,
-            code: this.code,
-            params: this.params,
-            desc: this.desc,
-            runFlag: this.runFlag,
-            conditionResult: this.conditionResult,
-            dependence: this.dependence,
-            maxRetryTimes: this.maxRetryTimes,
-            retryInterval: this.retryInterval,
-            delayTime: this.delayTime,
-            timeout: this.timeout,
-            waitStartTimeout: this.waitStartTimeout,
-            taskInstancePriority: this.taskInstancePriority,
+            description: this.desc,
+            taskType: this.nodeData.taskType,
+            taskParams: {
+              ...this.params,
+              dependence: this.cacheDependence,
+              conditionResult: this.conditionResult
+            },
+            flag: this.runFlag,
+            taskPriority: this.taskInstancePriority,
             workerGroup: this.workerGroup,
-            status: this.status,
-            branch: this.branch
+            failRetryTimes: this.maxRetryTimes,
+            failRetryInterval: this.retryInterval,
+            timeoutFlag: this.timeout.enable ? 'OPEN' : 'CLOSE',
+            timeoutNotifyStrategy: this.timeout.strategy,
+            timeout: this.timeout.interval || 0,
+            delayTime: this.delayTime
           },
           fromThis: this
         })
-
         // set run flag
         this._setRunFlag()
       },
@@ -629,13 +594,10 @@
       },
       /**
        *  set run flag
+       *  TODO
        */
       _setRunFlag () {
-        let dom = $(`#${this.nodeData.id}`).find('.ban-p')
-        dom.html('')
-        if (this.runFlag === 'FORBIDDEN') {
-          dom.append(rtBantpl())
-        }
+
       },
       /**
        * Submit verification
@@ -661,42 +623,25 @@
         })
       }
     },
-    watch: {
-      /**
-       * Watch the item change, cache the value it changes
-       **/
-      _item (val) {
-        // this._cacheItem()
-      }
-    },
     created () {
-      // Unbind copy and paste events
-      JSP.removePaste()
       // Backfill data
       let taskList = this.store.state.dag.tasks
-
-      // fillback use cacheTasks
-      let cacheTasks = this.store.state.dag.cacheTasks
       let o = {}
-      if (cacheTasks[this.nodeData.id]) {
-        o = cacheTasks[this.nodeData.id]
-        this.backfillItem = cacheTasks[this.nodeData.id]
-      } else {
-        if (taskList.length) {
-          taskList.forEach(v => {
-            if (v.id === this.nodeData.id) {
-              o = v
-              this.backfillItem = v
-            }
-          })
-        }
+      if (taskList.length) {
+        taskList.forEach(task => {
+          if (task.code === this.nodeData.id) {
+            const backfillItem = this.taskToBackfillItem(task)
+            o = backfillItem
+            this.backfillItem = backfillItem
+          }
+        })
       }
       // Non-null objects represent backfill
       if (!_.isEmpty(o)) {
         this.code = o.code
         this.name = o.name
         this.taskInstancePriority = o.taskInstancePriority
-        this.runFlag = o.runFlag || 'NORMAL'
+        this.runFlag = o.runFlag || 'YES'
         this.desc = o.desc
         this.maxRetryTimes = o.maxRetryTimes
         this.retryInterval = o.retryInterval
@@ -755,33 +700,14 @@
     destroyed () {
     },
     computed: {
+      ...mapState('dag', [
+        'processListS'
+      ]),
       /**
        * Child workflow entry show/hide
        */
       _isGoSubProcess () {
         return this.nodeData.taskType === 'SUB_PROCESS' && this.name
-      },
-
-      // Define the item model
-      _item () {
-        return {
-          type: this.nodeData.taskType,
-          id: this.nodeData.id,
-          code: this.code,
-          name: this.name,
-          desc: this.desc,
-          runFlag: this.runFlag,
-          dependence: this.cacheDependence,
-          maxRetryTimes: this.maxRetryTimes,
-          retryInterval: this.retryInterval,
-          delayTime: this.delayTime,
-          timeout: this.timeout,
-          waitStartTimeout: this.waitStartTimeout,
-          taskInstancePriority: this.taskInstancePriority,
-          workerGroup: this.workerGroup,
-          successBranch: this.successBranch,
-          failedBranch: this.failedBranch
-        }
       }
     },
     components: {
@@ -805,8 +731,8 @@
       mTimeoutAlarm,
       mDependentTimeout,
       mPriority,
-      mWorkerGroups,
-      mPreTasks
+      mWorkerGroups
+      // mPreTasks
     }
   }
 </script>
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/log.vue b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/log.vue
index 81d59d8..4a0cf69 100644
--- a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/log.vue
+++ b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/log.vue
@@ -79,6 +79,7 @@
         store,
         router,
         isLog: false,
+        // TODO
         stateId: $(`#${this.item.id}`).attr('data-state-id') || null,
         isScreen: false,
         loadingIndex: 0,
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/tasks/sub_process.vue b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/tasks/sub_process.vue
index 71fce10..189f92f 100644
--- a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/tasks/sub_process.vue
+++ b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/formModel/tasks/sub_process.vue
@@ -30,7 +30,7 @@
                 v-for="city in processDefinitionList"
                 :key="city.code"
                 :value="city.id"
-                :label="city.code">
+                :label="city.name">
           </el-option>
         </el-select>
       </div>
@@ -74,14 +74,14 @@
       /**
        * The selected process defines the upper component name padding
        */
-      _handleWdiChanged (o) {
-        this.$emit('on-set-process-name', this._handleName(o))
+      _handleWdiChanged (id) {
+        this.$emit('on-set-process-name', this._handleName(id))
       },
       /**
        * Return the name according to the process definition id
        */
       _handleName (id) {
-        return _.filter(this.processDefinitionList, v => id === v.id)[0].code
+        return _.filter(this.processDefinitionList, v => id === v.id)[0].name
       }
     },
     watch: {
@@ -93,22 +93,20 @@
     },
     created () {
       let processListS = _.cloneDeep(this.store.state.dag.processListS)
-      let id = null
+      let code = null
       if (this.router.history.current.name === 'projects-instance-details') {
-        id = this.router.history.current.query.id || null
+        code = this.router.history.current.query.code || null
       } else {
-        id = this.router.history.current.params.id || null
+        code = this.router.history.current.params.code || null
       }
-      this.processDefinitionList = (() => {
-        let a = _.map(processListS, v => {
-          return {
-            id: v.id,
-            code: v.name,
-            disabled: false
-          }
-        })
-        return _.filter(a, v => +v.id !== +id)
-      })()
+      this.processDefinitionList = processListS.map(v => {
+        return {
+          id: v.processDefinition.id,
+          code: v.processDefinition.code,
+          name: v.processDefinition.name,
+          disabled: false
+        }
+      }).filter(a => (a.code + '') !== code)
 
       let o = this.backfillItem
       // Non-null objects represent backfill
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/full-screen-close.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/full-screen-close.png
new file mode 100755
index 0000000..316fff7
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/full-screen-close.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/full-screen-close_hover.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/full-screen-close_hover.png
new file mode 100755
index 0000000..5ad8804
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/full-screen-close_hover.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/full-screen-open.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/full-screen-open.png
new file mode 100755
index 0000000..2443cc7
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/full-screen-open.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/full-screen-open_hover.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/full-screen-open_hover.png
new file mode 100755
index 0000000..d33ba67
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/full-screen-open_hover.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/graph-format.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/graph-format.png
new file mode 100755
index 0000000..ce0ca79
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/graph-format.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/graph-format_hover.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/graph-format_hover.png
new file mode 100755
index 0000000..97d359a
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/graph-format_hover.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/startup-params.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/startup-params.png
new file mode 100755
index 0000000..987e1f9
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/startup-params.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/startup-params_hover.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/startup-params_hover.png
new file mode 100755
index 0000000..83b9a82
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/startup-params_hover.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/conditions.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/conditions.png
new file mode 100755
index 0000000..0849964
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/conditions.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/conditions_hover.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/conditions_hover.png
new file mode 100755
index 0000000..a0c4551
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/conditions_hover.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/datax.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/datax.png
new file mode 100755
index 0000000..22519a9
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/datax.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/datax_hover.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/datax_hover.png
new file mode 100755
index 0000000..986470d
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/datax_hover.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/dependent.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/dependent.png
new file mode 100755
index 0000000..3f0b732
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/dependent.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/dependent_hover.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/dependent_hover.png
new file mode 100755
index 0000000..4b4b866
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/dependent_hover.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/flink.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/flink.png
new file mode 100755
index 0000000..568efbe
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/flink.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/flink_hover.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/flink_hover.png
new file mode 100755
index 0000000..7c8e521
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/flink_hover.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/http.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/http.png
new file mode 100755
index 0000000..1d80cd0
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/http.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/http_hover.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/http_hover.png
new file mode 100755
index 0000000..31403be
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/http_hover.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/mr.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/mr.png
new file mode 100755
index 0000000..59d082f
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/mr.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/mr_hover.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/mr_hover.png
new file mode 100755
index 0000000..e4cfd96
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/mr_hover.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/procedure.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/procedure.png
new file mode 100755
index 0000000..e681c28
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/procedure.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/procedure_hover.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/procedure_hover.png
new file mode 100755
index 0000000..82ad43d
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/procedure_hover.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/python.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/python.png
new file mode 100755
index 0000000..8bc4d51
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/python.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/python_hover.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/python_hover.png
new file mode 100755
index 0000000..f94a4f7
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/python_hover.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/shell.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/shell.png
new file mode 100755
index 0000000..4e40b6e
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/shell.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/shell_hover.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/shell_hover.png
new file mode 100755
index 0000000..b615f55
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/shell_hover.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/spark.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/spark.png
new file mode 100755
index 0000000..e9ff012
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/spark.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/spark_hover.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/spark_hover.png
new file mode 100755
index 0000000..b0fbc56
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/spark_hover.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/sql.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/sql.png
new file mode 100755
index 0000000..3415486
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/sql.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/sql_hover.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/sql_hover.png
new file mode 100755
index 0000000..fabce22
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/sql_hover.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/sqoop.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/sqoop.png
new file mode 100755
index 0000000..2c754d3
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/sqoop.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/sqoop_hover.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/sqoop_hover.png
new file mode 100755
index 0000000..dd64890
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/sqoop_hover.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/sub_process.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/sub_process.png
new file mode 100755
index 0000000..fb12061
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/sub_process.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/sub_process_hover.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/sub_process_hover.png
new file mode 100755
index 0000000..d6b4abc
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/sub_process_hover.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/waterdrop.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/waterdrop.png
new file mode 100755
index 0000000..bf2f83e
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/waterdrop.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/waterdrop_hover.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/waterdrop_hover.png
new file mode 100755
index 0000000..e6affd6
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/task-icos/waterdrop_hover.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/view-variables.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/view-variables.png
new file mode 100755
index 0000000..44cbc90
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/view-variables.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/view-variables_hover.png b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/view-variables_hover.png
new file mode 100755
index 0000000..7f8e08c
Binary files /dev/null and b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/images/view-variables_hover.png differ
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/downChart.js b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/downChart.js
deleted file mode 100644
index 1c851a1..0000000
--- a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/downChart.js
+++ /dev/null
@@ -1,122 +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 _ from 'lodash'
-import canvg from 'canvg'
-import { tasksAll } from './util'
-import html2canvas from 'html2canvas'
-import { findComponentDownward } from '@/module/util/'
-
-const DownChart = function () {
-  this.dag = {}
-}
-
-/**
- * Get interception location information
- */
-DownChart.prototype.maxVal = function () {
-  return new Promise((resolve, reject) => {
-    // All nodes
-    const tasksAllList = tasksAll()
-    const dom = $('.dag-container')
-    const y = parseInt(_.maxBy(tasksAllList, 'y').y + 60)
-    const x = parseInt(_.maxBy(tasksAllList, 'x').x + 100)
-
-    resolve({
-      width: (x > 600 ? x : dom.width()) + 100,
-      height: (y > 500 ? y : dom.height()) + 100
-    })
-  })
-}
-
-/**
- * Download to image
- */
-DownChart.prototype.download = function ({ dagThis }) {
-  this.dag = dagThis
-
-  this.maxVal().then(({ width, height }) => {
-    // Dom to save
-    const copyDom = $('#canvas')
-    // gain
-    const scale = 1
-    // divReport is the id of the dom that needs to be intercepted into a picture
-    const svgElem = copyDom.find('svg')
-    svgElem.each((index, node) => {
-      // svg handle
-      const nodesToRecover = []
-      const nodesToRemove = []
-      const parentNode = node.parentNode
-      const svg = node.outerHTML.trim()
-      const canvas = document.createElement('canvas')
-      canvg(canvas, svg)
-      if (node.style.position) {
-        canvas.style.position += node.style.position
-        canvas.style.left += node.style.left
-        canvas.style.top += node.style.top
-      }
-      nodesToRecover.push({
-        parent: parentNode,
-        child: node
-      })
-      parentNode.removeChild(node)
-      nodesToRemove.push({
-        parent: parentNode,
-        child: canvas
-      })
-      parentNode.appendChild(canvas)
-    })
-
-    const canvas = document.createElement('canvas')
-    // canvas width
-    canvas.width = width * scale
-    // canvas height
-    canvas.height = height * scale
-
-    const content = canvas.getContext('2d')
-    content.scale(scale, scale)
-    // Get the offset of the element relative to the inspection
-    const rect = copyDom.get(0).getBoundingClientRect()
-    // Set the context position, the value is a negative value relative to the window offset, let the picture reset
-    content.translate(-rect.left, -rect.top)
-
-    html2canvas(copyDom[0], {
-      dpi: window.devicePixelRatio * 2,
-      scale: scale,
-      width: width,
-      canvas: canvas,
-      heigth: height,
-      useCORS: true // Enable cross-domain configuration
-    }).then((canvas) => {
-      const name = `${this.dag.name}.png`
-      const url = canvas.toDataURL('image/png', 1)
-      setTimeout(() => {
-        const triggerDownload = $('<a>').attr('href', url).attr('download', name).appendTo('body')
-        triggerDownload[0].click()
-        triggerDownload.remove()
-      }, 100)
-
-      // To refresh the dag instance, otherwise you can't re-plot
-      setTimeout(() => {
-        // Refresh current dag
-        findComponentDownward(this.dag.$root, `${this.dag.type}-details`).init()
-      }, 500)
-    })
-  })
-}
-
-export default new DownChart()
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/jsPlumbHandle.js b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/jsPlumbHandle.js
deleted file mode 100755
index fbc00b0..0000000
--- a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/jsPlumbHandle.js
+++ /dev/null
@@ -1,814 +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 'jquery-ui/ui/widgets/draggable'
-import 'jquery-ui/ui/widgets/droppable'
-import 'jquery-ui/ui/widgets/resizable'
-import _ from 'lodash'
-import i18n from '@/module/i18n'
-import { jsPlumb } from 'jsplumb'
-import DragZoom from './dragZoom'
-import store from '@/conf/home/store'
-import router from '@/conf/home/router'
-import { uuid, findComponentDownward } from '@/module/util/'
-
-import {
-  tasksAll,
-  rtTasksTpl,
-  setSvgColor,
-  saveTargetarr,
-  rtTargetarrArr,
-  computeScale
-} from './util'
-import multiDrag from './multiDrag'
-
-const JSP = function () {
-  this.dag = {}
-  this.selectedElement = {}
-
-  this.config = {
-    // Whether to drag
-    isDrag: true,
-    // Whether to allow connection
-    isAttachment: false,
-    // Whether to drag a new node
-    isNewNodes: true,
-    // Whether to support double-click node events
-    isDblclick: true,
-    // Whether to support right-click menu events
-    isContextmenu: true,
-    // Whether to allow click events
-    isClick: false
-  }
-}
-/**
- * dag init
- */
-JSP.prototype.init = function ({ dag, instance, options }) {
-  // Get the dag component instance
-  this.dag = dag
-  // Get jsplumb instance
-  this.JspInstance = instance
-  // Get JSP options
-  this.options = options || {}
-  // Register jsplumb connection type and configuration
-  this.JspInstance.registerConnectionType('basic', {
-    anchor: 'Continuous',
-    connector: 'Bezier' // Line type
-  })
-
-  // Initial configuration
-  this.setConfig({
-    isDrag: !store.state.dag.isDetails,
-    isAttachment: false,
-    isNewNodes: !store.state.dag.isDetails, // Permissions.getAuth() === false ? false : !store.state.dag.isDetails,
-    isDblclick: true,
-    isContextmenu: true,
-    isClick: false
-  })
-
-  // Monitor line click
-  this.JspInstance.bind('click', e => {
-    // Untie event
-    if (this.config.isClick) {
-      this.connectClick(e)
-    } else {
-      findComponentDownward(this.dag.$root, 'dag-chart')._createLineLabel({ id: e._jsPlumb.overlays.label.canvas.id, sourceId: e.sourceId, targetId: e.targetId })
-    }
-  })
-
-  // Drag and drop
-  if (this.config.isNewNodes) {
-    DragZoom.init()
-  }
-
-  // support multi drag
-  multiDrag()
-}
-
-/**
- * set config attribute
- */
-JSP.prototype.setConfig = function (o) {
-  this.config = Object.assign(this.config, {}, o)
-}
-
-/**
- * Node binding event
- */
-JSP.prototype.tasksEvent = function (selfId) {
-  const tasks = $(`#${selfId}`)
-  // Bind right event
-  tasks.on('contextmenu', e => {
-    this.tasksContextmenu(e)
-    return false
-  })
-
-  // Binding double click event
-  tasks.find('.icos').bind('dblclick', e => {
-    this.tasksDblclick(e)
-  })
-
-  // Binding click event
-  tasks.on('click', e => {
-    this.tasksClick(e)
-  })
-}
-
-/**
- * Dag node drag and drop processing
- */
-JSP.prototype.draggable = function () {
-  if (this.config.isNewNodes) {
-    let selfId
-    const self = this
-    $('.toolbar-btn .roundedRect').draggable({
-      scope: 'plant',
-      helper: 'clone',
-      containment: $('.dag-model'),
-      stop: function (e, ui) {
-      },
-      drag: function () {
-        $('body').find('.tooltip.fade.top.in').remove()
-      }
-    })
-
-    $('#canvas').droppable({
-      scope: 'plant',
-      drop: function (ev, ui) {
-        let id = 'tasks-' + Math.ceil(Math.random() * 100000) // eslint-disable-line
-
-        let scale = computeScale($(this))
-        scale = scale || 1
-
-        // Get mouse coordinates and after scale coordinate
-        const left = parseInt(ui.offset.left - $(this).offset().left) / scale
-        const top = parseInt(ui.offset.top - $(this).offset().top) / scale
-        // Generate template node
-        $('#canvas').append(rtTasksTpl({
-          id: id,
-          name: id,
-          x: left,
-          y: top,
-          isAttachment: self.config.isAttachment,
-          taskType: findComponentDownward(self.dag.$root, 'dag-chart').dagBarId
-        }))
-
-        // Get the generated node
-        const thisDom = jsPlumb.getSelector('.statemachine-demo .w')
-
-        // Generating a connection node
-        self.JspInstance.batch(() => {
-          self.initNode(thisDom[thisDom.length - 1])
-        })
-        selfId = id
-
-        self.tasksEvent(selfId)
-
-        // Dom structure is not generated without pop-up form form
-        if ($(`#${selfId}`).html()) {
-          // dag event
-          findComponentDownward(self.dag.$root, 'dag-chart')._createNodes({
-            id: selfId
-          })
-        }
-      }
-    })
-  }
-}
-
-/**
- * Echo json processing and old data structure processing
- */
-JSP.prototype.jsonHandle = function ({ largeJson, locations }) {
-  _.map(largeJson, v => {
-    // Generate template
-    $('#canvas').append(rtTasksTpl({
-      id: v.id,
-      name: v.name,
-      x: locations[v.id].x,
-      y: locations[v.id].y,
-      targetarr: locations[v.id].targetarr,
-      isAttachment: this.config.isAttachment,
-      taskType: v.type,
-      runFlag: v.runFlag,
-      nodenumber: locations[v.id].nodenumber,
-      successNode: v.conditionResult === undefined ? '' : v.conditionResult.successNode[0],
-      failedNode: v.conditionResult === undefined ? '' : v.conditionResult.failedNode[0]
-    }))
-
-    // contextmenu event
-    $(`#${v.id}`).on('contextmenu', e => {
-      this.tasksContextmenu(e)
-      return false
-    })
-
-    // dblclick event
-    $(`#${v.id}`).find('.icos').bind('dblclick', e => {
-      this.tasksDblclick(e)
-    })
-
-    // click event
-    $(`#${v.id}`).bind('click', e => {
-      this.tasksClick(e)
-    })
-  })
-}
-
-/**
- * Initialize a single node
- */
-JSP.prototype.initNode = function (el) {
-  // Whether to drag
-  if (this.config.isDrag) {
-    this.JspInstance.draggable(el, {
-      containment: 'dag-container'
-    })
-  }
-
-  // Node attribute configuration
-  this.JspInstance.makeSource(el, {
-    filter: '.ep',
-    anchor: 'Continuous',
-    connectorStyle: {
-      stroke: '#2d8cf0',
-      strokeWidth: 2,
-      outlineStroke: 'transparent',
-      outlineWidth: 4
-    },
-    // This place is leaking
-    // connectionType: "basic",
-    extract: {
-      action: 'the-action'
-    },
-    maxConnections: -1
-  })
-
-  // Node connection property configuration
-  this.JspInstance.makeTarget(el, {
-    dropOptions: { hoverClass: 'dragHover' },
-    anchor: 'Continuous',
-    allowLoopback: false // Forbid yourself to connect yourself
-  })
-  this.JspInstance.fire('jsPlumbDemoNodeAdded', el)
-}
-
-/**
- * Node right click menu
- */
-JSP.prototype.tasksContextmenu = function (event) {
-  if (this.config.isContextmenu) {
-    const routerName = router.history.current.name
-    // state
-    const isOne = routerName === 'projects-definition-details' && this.dag.releaseState !== 'NOT_RELEASE'
-    // hide
-    const isTwo = store.state.dag.isDetails
-
-    const html = [
-      `<a href="javascript:" id="startRunning" class="${isOne ? '' : 'disbled'}"><em class="el-icon-video-play"></em><span>${i18n.$t('Start')}</span></a>`,
-      `<a href="javascript:" id="editNodes" class="${isTwo ? 'disbled' : ''}"><em class="el-icon-edit-outline"></em><span>${i18n.$t('Edit')}</span></a>`,
-      `<a href="javascript:" id="copyNodes" class="${isTwo ? 'disbled' : ''}"><em class="el-icon-copy-document"></em><span>${i18n.$t('Copy')}</span></a>`,
-      `<a href="javascript:" id="removeNodes" class="${isTwo ? 'disbled' : ''}"><em class="el-icon-delete"></em><span>${i18n.$t('Delete')}</span></a>`
-    ]
-
-    const operationHtml = () => {
-      return html.splice(',')
-    }
-
-    const e = event
-    const $id = e.currentTarget.id
-    const $contextmenu = $('#contextmenu')
-    const $name = $(`#${$id}`).find('.name-p').text()
-    const $left = e.pageX + document.body.scrollLeft - 5
-    const $top = e.pageY + document.body.scrollTop - 5
-    $contextmenu.css({
-      left: $left,
-      top: $top,
-      visibility: 'visible'
-    })
-    // Action bar
-    $contextmenu.html('').append(operationHtml)
-
-    if (isOne) {
-      // start run
-      $('#startRunning').on('click', () => {
-        const name = store.state.dag.name
-        const id = router.history.current.params.id
-        store.dispatch('dag/getStartCheck', { processDefinitionId: id }).then(res => {
-          this.dag.startRunning({ id: id, name: name }, $name, 'contextmenu')
-        })
-      })
-    }
-    if (!isTwo) {
-      // edit node
-      $('#editNodes').click(ev => {
-        findComponentDownward(this.dag.$root, 'dag-chart')._createNodes({
-          id: $id,
-          type: $(`#${$id}`).attr('data-tasks-type')
-        })
-      })
-      // delete node
-      $('#removeNodes').click(ev => {
-        this.removeNodes($id)
-      })
-
-      // copy node
-      $('#copyNodes').click(res => {
-        this.copyNodes($id)
-      })
-    }
-  }
-}
-
-/**
- * Node double click event
- */
-JSP.prototype.tasksDblclick = function (e) {
-  // Untie event
-  if (this.config.isDblclick) {
-    const id = $(e.currentTarget.offsetParent).attr('id')
-    findComponentDownward(this.dag.$root, 'dag-chart')._createNodes({
-      id: id,
-      type: $(`#${id}`).attr('data-tasks-type')
-    })
-  }
-}
-
-/**
- * Node click event
- */
-JSP.prototype.tasksClick = function (e) {
-  let $id
-  const self = this
-  const $body = $('body')
-  if (this.config.isClick) {
-    const $connect = this.selectedElement.connect
-    $('.w').removeClass('jtk-tasks-active')
-    $(e.currentTarget).addClass('jtk-tasks-active')
-    if ($connect) {
-      setSvgColor($connect, '#2d8cf0')
-      this.selectedElement.connect = null
-    }
-    this.selectedElement.id = $(e.currentTarget).attr('id')
-
-    // Unbind copy and paste events
-    $body.unbind('copy').unbind('paste')
-    // Copy binding id
-    $id = self.selectedElement.id
-
-    $body.bind({
-      copy: function () {
-        $id = self.selectedElement.id
-      },
-      paste: function () {
-        $id && self.copyNodes($id)
-      }
-    })
-  }
-}
-
-/**
- * Remove binding events
- * paste
- */
-JSP.prototype.removePaste = function () {
-  const $body = $('body')
-  // Unbind copy and paste events
-  $body.unbind('copy').unbind('paste')
-  // Remove selected node parameters
-  this.selectedElement.id = null
-  // Remove node selection effect
-  $('.w').removeClass('jtk-tasks-active')
-}
-
-/**
- * Line click event
- */
-JSP.prototype.connectClick = function (e) {
-  // Set svg color
-  setSvgColor(e, '#0097e0')
-  const $id = this.selectedElement.id
-  if ($id) {
-    $(`#${$id}`).removeClass('jtk-tasks-active')
-    this.selectedElement.id = null
-  }
-  this.selectedElement.connect = e
-}
-
-/**
- * toolbarEvent
- * @param {Pointer}
- */
-JSP.prototype.handleEventPointer = function (is) {
-  this.setConfig({
-    isClick: is,
-    isAttachment: false
-  })
-}
-
-/**
- * toolbarEvent
- * @param {Line}
- */
-JSP.prototype.handleEventLine = function (is) {
-  const wDom = $('.w')
-  this.setConfig({
-    isAttachment: is
-  })
-  is ? wDom.addClass('jtk-ep') : wDom.removeClass('jtk-ep')
-}
-
-/**
- * toolbarEvent
- * @param {Remove}
- */
-JSP.prototype.handleEventRemove = function () {
-  const $id = this.selectedElement.id || null
-  const $connect = this.selectedElement.connect || null
-  if ($id) {
-    this.removeNodes(this.selectedElement.id)
-  } else {
-    this.removeConnect($connect)
-  }
-
-  // Monitor whether to edit DAG
-  store.commit('dag/setIsEditDag', true)
-}
-
-/**
- * Delete node
- */
-JSP.prototype.removeNodes = function ($id) {
-  // Delete node processing(data-targetarr)
-  _.map(tasksAll(), v => {
-    const targetarr = v.targetarr.split(',')
-    if (targetarr.length) {
-      const newArr = _.filter(targetarr, v1 => v1 !== $id)
-      $(`#${v.id}`).attr('data-targetarr', newArr.toString())
-    }
-  })
-  // delete node
-  this.JspInstance.remove($id)
-
-  // delete dom
-  $(`#${$id}`).remove()
-
-  // callback onRemoveNodes event
-  this.options && this.options.onRemoveNodes && this.options.onRemoveNodes($id)
-  const connects = []
-  _.map(this.JspInstance.getConnections(), v => {
-    connects.push({
-      endPointSourceId: v.sourceId,
-      endPointTargetId: v.targetId,
-      label: v._jsPlumb.overlays.label.canvas.innerText
-    })
-  })
-  // Storage line dependence
-  store.commit('dag/setConnects', connects)
-}
-
-/**
- * Delete connection
- */
-JSP.prototype.removeConnect = function ($connect) {
-  if (!$connect) {
-    return
-  }
-  // Remove connections and remove node and node dependencies
-  const targetId = $connect.targetId
-  const sourceId = $connect.sourceId
-  let targetarr = rtTargetarrArr(targetId)
-  if (targetarr.length) {
-    targetarr = _.filter(targetarr, v => v !== sourceId)
-    $(`#${targetId}`).attr('data-targetarr', targetarr.toString())
-  }
-  if ($(`#${sourceId}`).attr('data-tasks-type') === 'CONDITIONS') {
-    $(`#${sourceId}`).attr('data-nodenumber', Number($(`#${sourceId}`).attr('data-nodenumber')) - 1)
-  }
-  this.JspInstance.deleteConnection($connect)
-
-  this.selectedElement = {}
-}
-
-/**
- * Copy node
- */
-JSP.prototype.copyNodes = function ($id) {
-  let newNodeInfo = _.cloneDeep(_.find(store.state.dag.tasks, v => v.id === $id))
-  const newNodePors = store.state.dag.locations[$id]
-  // Unstored nodes do not allow replication
-  if (!newNodePors) {
-    return
-  }
-  // Generate random id
-  const newUuId = `${uuid() + uuid()}`
-  const id = newNodeInfo.id.length > 8 ? newNodeInfo.id.substr(0, 7) : newNodeInfo.id
-  const name = newNodeInfo.name.length > 8 ? newNodeInfo.name.substr(0, 7) : newNodeInfo.name
-
-  // new id
-  const newId = `${id || ''}-${newUuId}`
-  // new name
-  const newName = `${name || ''}-${newUuId}`
-  // coordinate x
-  const newX = newNodePors.x + 100
-  // coordinate y
-  const newY = newNodePors.y + 40
-
-  // Generate template node
-  $('#canvas').append(rtTasksTpl({
-    id: newId,
-    name: newName,
-    x: newX,
-    y: newY,
-    isAttachment: this.config.isAttachment,
-    taskType: newNodeInfo.type
-  }))
-
-  // Get the generated node
-  const thisDom = jsPlumb.getSelector('.statemachine-demo .w')
-
-  // Copy node information
-  newNodeInfo = Object.assign(newNodeInfo, {
-    id: newId,
-    name: newName
-  })
-
-  // Add new node
-  store.commit('dag/addTasks', newNodeInfo)
-  // Add node location information
-  store.commit('dag/addLocations', {
-    [newId]: {
-      name: newName,
-      targetarr: '',
-      nodenumber: 0,
-      x: newX,
-      y: newY
-    }
-  })
-
-  // Generating a connection node
-  this.JspInstance.batch(() => {
-    this.initNode(thisDom[thisDom.length - 1])
-    // Add events to nodes
-    this.tasksEvent(newId)
-  })
-}
-/**
- * toolbarEvent
- * @param {Screen}
- */
-JSP.prototype.handleEventScreen = function ({ item, is }) {
-  let screenOpen = true
-  if (is) {
-    item.icon = 'el-icon-aim'
-    screenOpen = true
-  } else {
-    item.icon = 'el-icon-full-screen'
-    screenOpen = false
-  }
-  const $mainLayoutModel = $('.main-layout-model')
-  if (screenOpen) {
-    $mainLayoutModel.addClass('dag-screen')
-  } else {
-    $mainLayoutModel.removeClass('dag-screen')
-  }
-}
-/**
- * save task
- * @param tasks
- * @param locations
- * @param connects
- */
-JSP.prototype.saveStore = function () {
-  return new Promise((resolve, reject) => {
-    const connects = []
-    const locations = {}
-    const tasks = []
-
-    const is = (id) => {
-      return !!_.filter(tasksAll(), v => v.id === id).length
-    }
-
-    // task
-    _.map(_.cloneDeep(store.state.dag.tasks), v => {
-      if (is(v.id)) {
-        const preTasks = []
-        const id = $(`#${v.id}`)
-        const tar = id.attr('data-targetarr')
-        const idDep = tar ? id.attr('data-targetarr').split(',') : []
-        if (idDep.length) {
-          _.map(idDep, v1 => {
-            preTasks.push($(`#${v1}`).find('.name-p').text())
-          })
-        }
-
-        let tasksParam = _.assign(v, {
-          preTasks: preTasks,
-          depList: null
-        })
-
-        // Sub-workflow has no retries and interval
-        if (v.type === 'SUB_PROCESS') {
-          tasksParam = _.omit(tasksParam, ['maxRetryTimes', 'retryInterval'])
-        }
-
-        tasks.push(tasksParam)
-      }
-    })
-
-    if (store.state.dag.connects.length === this.JspInstance.getConnections().length) {
-      _.map(store.state.dag.connects, u => {
-        connects.push({
-          endPointSourceId: u.endPointSourceId,
-          endPointTargetId: u.endPointTargetId,
-          label: u.label
-        })
-      })
-    } else if (store.state.dag.connects.length > 0 && store.state.dag.connects.length < this.JspInstance.getConnections().length) {
-      _.map(this.JspInstance.getConnections(), v => {
-        connects.push({
-          endPointSourceId: v.sourceId,
-          endPointTargetId: v.targetId,
-          label: v._jsPlumb.overlays.label.canvas.innerText
-        })
-      })
-      _.map(store.state.dag.connects, u => {
-        _.map(connects, v => {
-          if (u.label && u.endPointSourceId === v.endPointSourceId && u.endPointTargetId === v.endPointTargetId) {
-            v.label = u.label
-          }
-        })
-      })
-    } else if (store.state.dag.connects.length === 0) {
-      _.map(this.JspInstance.getConnections(), v => {
-        connects.push({
-          endPointSourceId: v.sourceId,
-          endPointTargetId: v.targetId,
-          label: v._jsPlumb.overlays.label.canvas.innerText
-        })
-      })
-    } else if (store.state.dag.connects.length > this.JspInstance.getConnections().length) {
-      _.map(this.JspInstance.getConnections(), v => {
-        connects.push({
-          endPointSourceId: v.sourceId,
-          endPointTargetId: v.targetId,
-          label: v._jsPlumb.overlays.label.canvas.innerText
-        })
-      })
-    }
-
-    _.map(tasksAll(), v => {
-      locations[v.id] = {
-        name: v.name,
-        targetarr: v.targetarr,
-        nodenumber: v.nodenumber,
-        x: v.x,
-        y: v.y
-      }
-    })
-
-    // Storage node
-    store.commit('dag/setTasks', tasks)
-    // Store coordinate information
-    store.commit('dag/setLocations', locations)
-    // Storage line dependence
-    store.commit('dag/setConnects', connects)
-
-    resolve({
-      connects: connects,
-      tasks: tasks,
-      locations: locations
-    })
-  })
-}
-/**
- * Event processing
- */
-
-JSP.prototype.handleEvent = function () {
-  this.JspInstance.bind('beforeDrop', function (info) {
-    const sourceId = info.sourceId// 出
-    const targetId = info.targetId// 入
-    /**
-     * Recursive search for nodes
-     */
-    let recursiveVal
-    const recursiveTargetarr = (arr, targetId) => {
-      for (const i in arr) {
-        if (arr[i] === targetId) {
-          recursiveVal = targetId
-        } else {
-          recursiveTargetarr(rtTargetarrArr(arr[i]), targetId)
-        }
-      }
-      return recursiveVal
-    }
-
-    // Connection to connected nodes is not allowed
-    if (_.findIndex(rtTargetarrArr(targetId), v => v === sourceId) !== -1) {
-      return false
-    }
-
-    // Recursive form to find if the target Targetarr has a sourceId
-    if (recursiveTargetarr(rtTargetarrArr(sourceId), targetId)) {
-      return false
-    }
-
-    if ($(`#${sourceId}`).attr('data-tasks-type') === 'CONDITIONS' && $(`#${sourceId}`).attr('data-nodenumber') === 2) {
-      return false
-    } else {
-      $(`#${sourceId}`).attr('data-nodenumber', Number($(`#${sourceId}`).attr('data-nodenumber')) + 1)
-    }
-
-    // Storage node dependency information
-    saveTargetarr(sourceId, targetId)
-
-    // Monitor whether to edit DAG
-    store.commit('dag/setIsEditDag', true)
-
-    return true
-  })
-}
-/**
- * Backfill data processing
- */
-JSP.prototype.jspBackfill = function ({ connects, locations, largeJson }) {
-  // Backfill nodes
-  this.jsonHandle({
-    largeJson: largeJson,
-    locations: locations
-  })
-
-  const wNodes = jsPlumb.getSelector('.statemachine-demo .w')
-
-  // Backfill line
-  this.JspInstance.batch(() => {
-    for (let i = 0; i < wNodes.length; i++) {
-      this.initNode(wNodes[i])
-    }
-    _.map(connects, v => {
-      let sourceId = v.endPointSourceId.split('-')
-      let targetId = v.endPointTargetId.split('-')
-      const labels = v.label
-      if (sourceId.length === 4 && targetId.length === 4) {
-        sourceId = `${sourceId[0]}-${sourceId[1]}-${sourceId[2]}`
-        targetId = `${targetId[0]}-${targetId[1]}-${targetId[2]}`
-      } else {
-        sourceId = v.endPointSourceId
-        targetId = v.endPointTargetId
-      }
-
-      if ($(`#${sourceId}`).attr('data-tasks-type') === 'CONDITIONS' && $(`#${sourceId}`).attr('data-successnode') === $(`#${targetId}`).find('.name-p').text()) {
-        this.JspInstance.connect({
-          source: sourceId,
-          target: targetId,
-          type: 'basic',
-          paintStyle: { strokeWidth: 2, stroke: '#4caf50' },
-          HoverPaintStyle: { stroke: '#ccc', strokeWidth: 3 },
-          overlays: [['Label', { label: labels }]]
-        })
-      } else if ($(`#${sourceId}`).attr('data-tasks-type') === 'CONDITIONS' && $(`#${sourceId}`).attr('data-failednode') === $(`#${targetId}`).find('.name-p').text()) {
-        this.JspInstance.connect({
-          source: sourceId,
-          target: targetId,
-          type: 'basic',
-          paintStyle: { strokeWidth: 2, stroke: '#252d39' },
-          HoverPaintStyle: { stroke: '#ccc', strokeWidth: 3 },
-          overlays: [['Label', { label: labels }]]
-        })
-      } else {
-        this.JspInstance.connect({
-          source: sourceId,
-          target: targetId,
-          type: 'basic',
-          paintStyle: { strokeWidth: 2, stroke: '#2d8cf0' },
-          HoverPaintStyle: { stroke: '#ccc', strokeWidth: 3 },
-          overlays: [['Label', { label: labels }]]
-        })
-      }
-    })
-  })
-
-  jsPlumb.fire('jsPlumbDemoLoaded', this.JspInstance)
-
-  // Connection monitoring
-  this.handleEvent()
-
-  // Drag and drop new nodes
-  this.draggable()
-}
-
-export default new JSP()
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/multiDrag.js b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/multiDrag.js
deleted file mode 100644
index a68c529..0000000
--- a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/multiDrag.js
+++ /dev/null
@@ -1,67 +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 JSP from './jsPlumbHandle'
-/**
- * when and only ctrl or meta key pressing, we can select one or more dags to drag
- */
-export default function () {
-  // init
-  let selectableObjects = []
-  JSP.JspInstance.clearDragSelection()
-  let ctrlPress = false
-
-  let nodes = null
-  const $window = $(window)
-
-  $window.bind('keydown', function (event) {
-    if (event.ctrlKey || event.metaKey) {
-      if (nodes) {
-        nodes.unbind('mousedown', select)
-      }
-      nodes = $('.jtk-draggable')
-      nodes.bind('mousedown', select)
-      ctrlPress = true
-    }
-  })
-  $window.bind('keyup', function (event) {
-    clear()
-  })
-
-  function select (event) {
-    if (ctrlPress && event.button === 0) {
-      let index = null
-      if ((index = selectableObjects.indexOf(this)) !== -1) {
-        selectableObjects.splice(index, 1)
-        JSP.JspInstance.removeFromDragSelection(this)
-        $(this).css('border-color', '')
-      } else {
-        selectableObjects.push(this)
-        JSP.JspInstance.addToDragSelection(this)
-        $(this).css('border-color', '#4af')
-      }
-    }
-  }
-
-  function clear () {
-    ctrlPress = false
-    selectableObjects.map(item => {
-      $(item).css('border-color', '')
-    })
-    selectableObjects = []
-    JSP.JspInstance.clearDragSelection()
-  }
-}
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/util.js b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/util.js
deleted file mode 100755
index 34a23e8..0000000
--- a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/plugIn/util.js
+++ /dev/null
@@ -1,171 +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 _ from 'lodash'
-import i18n from '@/module/i18n'
-import store from '@/conf/home/store'
-
-/**
- * Node, to array
- */
-const rtTargetarrArr = (id) => {
-  const ids = $(`#${id}`).attr('data-targetarr')
-  return ids ? ids.split(',') : []
-}
-
-/**
- * Store node id to targetarr
- */
-const saveTargetarr = (valId, domId) => {
-  const $target = $(`#${domId}`)
-  const targetStr = $target.attr('data-targetarr') ? $target.attr('data-targetarr') + `,${valId}` : `${valId}`
-  $target.attr('data-targetarr', targetStr)
-}
-
-const rtBantpl = () => {
-  return `<em class="ri-indeterminate-circle-line" data-toggle="tooltip" data-html="true" data-container="body" data-placement="left" title="${i18n.$t('Prohibition execution')}"></em>`
-}
-
-/**
- * return node html
- */
-const rtTasksTpl = ({ id, name, x, y, targetarr, isAttachment, taskType, runFlag, nodenumber, successNode, failedNode }) => {
-  let tpl = ''
-  tpl += `<div class="w jtk-draggable jtk-droppable jtk-endpoint-anchor jtk-connected ${isAttachment ? 'jtk-ep' : ''}" data-targetarr="${targetarr || ''}" data-successNode="${successNode || ''}" data-failedNode="${failedNode || ''}" data-nodenumber="${nodenumber || 0}" data-tasks-type="${taskType}" id="${id}" style="left: ${x}px; top: ${y}px;">`
-  tpl += '<div>'
-  tpl += '<div class="state-p"></div>'
-  tpl += `<div class="icos icos-${taskType}"></div>`
-  tpl += `<span class="name-p">${name}</span>`
-  tpl += '</div>'
-  tpl += '<div class="ep"></div>'
-  tpl += '<div class="ban-p">'
-  if (runFlag === 'FORBIDDEN') {
-    tpl += rtBantpl()
-  }
-  tpl += '</div>'
-  tpl += '</div>'
-
-  return tpl
-}
-
-/**
- * Get all tasks nodes
- */
-const tasksAll = () => {
-  const a = []
-  $('#canvas .w').each(function (idx, elem) {
-    const e = $(elem)
-    a.push({
-      id: e.attr('id'),
-      name: e.find('.name-p').text(),
-      targetarr: e.attr('data-targetarr') || '',
-      nodenumber: e.attr('data-nodenumber'),
-      x: parseInt(e.css('left'), 10),
-      y: parseInt(e.css('top'), 10)
-    })
-  })
-  return a
-}
-
-/**
- * Determine if name is in the current dag map
- * rely dom / backfill
- */
-const isNameExDag = (name, rely) => {
-  if (rely === 'dom') {
-    return _.findIndex(tasksAll(), v => v.name === name) !== -1
-  } else {
-    return _.findIndex(store.state.dag.tasks, v => v.name === name) !== -1
-  }
-}
-
-/**
- * Change svg line color
- */
-const setSvgColor = (e, color) => {
-  // Traverse clear all colors
-  $('.jtk-connector').each((i, o) => {
-    _.map($(o)[0].childNodes, v => {
-      if ($(v).attr('fill') === '#ccc') {
-        $(v).attr('fill', '#2d8cf0')
-      }
-      if ($(v).attr('fill') === '#4caf50') {
-        $(v).attr('fill', '#4caf50').attr('stroke', '#4caf50').attr('stroke-width', 2)
-        $(v).prev().attr('stroke', '#4caf50').attr('stroke-width', 2)
-      } else if ($(v).attr('fill') === '#252d39') {
-        $(v).attr('stroke', '#252d39').attr('stroke-width', 2)
-        $(v).prev().attr('stroke', '#252d39').attr('stroke-width', 2)
-      } else {
-        $(v).attr('stroke', '#2d8cf0').attr('stroke-width', 2)
-      }
-    })
-  })
-
-  // Add color to the selection
-  _.map($(e.canvas)[0].childNodes, (v, i) => {
-    if ($(v).attr('fill') === '#2d8cf0') {
-      $(v).attr('fill', '#ccc')
-    }
-    $(v).attr('stroke', '#ccc')
-    if ($(v).attr('class')) {
-      $(v).attr('stroke-width', 2)
-    }
-  })
-}
-
-/**
- * Get all node ids
- */
-const allNodesId = () => {
-  const idArr = []
-  $('.w').each((i, o) => {
-    const $obj = $(o)
-    const $span = $obj.find('.name-p').text()
-    if ($span) {
-      idArr.push({
-        id: $obj.attr('id'),
-        name: $span
-      })
-    }
-  })
-  return idArr
-}
-/**
- * compute scale,because it cant get from jquery directly
- * @param el element
- * @returns {boolean|number}
- */
-const computeScale = function (el) {
-  const matrix = el.css('transform')
-  if (!matrix || matrix === 'none') {
-    return false
-  }
-  const values = matrix.split('(')[1].split(')')[0].split(',')
-  return Math.sqrt(values[0] * values[0] + values[1] * values[1])
-}
-
-export {
-  rtTargetarrArr,
-  saveTargetarr,
-  rtTasksTpl,
-  tasksAll,
-  isNameExDag,
-  setSvgColor,
-  allNodesId,
-  rtBantpl,
-  computeScale
-}
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/udp/_source/selectTenant.vue b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/udp/_source/selectTenant.vue
index e3611ba..0a491f9 100644
--- a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/udp/_source/selectTenant.vue
+++ b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/udp/_source/selectTenant.vue
@@ -61,8 +61,11 @@
       }
     },
     methods: {
-      _onChange (o) {
-        this.$emit('tenantSelectEvent', o)
+      _onChange (id) {
+        const tenant = this.itemList.find(item => item.id === id)
+        if (tenant) {
+          this.$emit('tenantSelectEvent', tenant.tenantCode)
+        }
       }
     },
     watch: {
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/udp/udp.vue b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/udp/udp.vue
index 9196e96..2d5af38 100644
--- a/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/udp/udp.vue
+++ b/dolphinscheduler-ui/src/js/conf/home/pages/dag/_source/udp/udp.vue
@@ -42,7 +42,7 @@
 
       <div class="title" style="padding-top: 6px;">
         <span class="text-b">{{$t('select tenant')}}</span>
-        <form-tenant v-model="tenantId"></form-tenant>
+        <form-tenant v-model="tenantCode"></form-tenant>
       </div>
       <div class="title" style="padding-top: 6px;">
         <span class="text-b">{{$t('warning of timeout')}}</span>
@@ -117,8 +117,8 @@
         syncDefine: true,
         // Timeout alarm
         timeout: 0,
-
-        tenantId: -1,
+        // tenant code
+        tenantCode: 'default',
         // checked Timeout alarm
         checkedTimeout: true
       }
@@ -150,7 +150,7 @@
 
         this.store.commit('dag/setName', _.cloneDeep(this.name))
         this.store.commit('dag/setTimeout', _.cloneDeep(this.timeout))
-        this.store.commit('dag/setTenantId', _.cloneDeep(this.tenantId))
+        this.store.commit('dag/setTenantCode', _.cloneDeep(this.tenantCode))
         this.store.commit('dag/setDesc', _.cloneDeep(this.description))
         this.store.commit('dag/setSyncDefine', this.syncDefine)
         this.store.commit('dag/setReleaseState', this.releaseState)
@@ -257,10 +257,10 @@
       this.timeout = dag.timeout || 0
       this.checkedTimeout = this.timeout !== 0
       this.$nextTick(() => {
-        if (dag.tenantId > -1) {
-          this.tenantId = dag.tenantId
-        } else if (this.store.state.user.userInfo.tenantId) {
-          this.tenantId = this.store.state.user.userInfo.tenantId
+        if (dag.tenantCode) {
+          this.tenantCode = dag.tenantCode
+        } else {
+          this.tenantCode = this.store.state.user.userInfo.tenantCode || 'default'
         }
       })
     },
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/dag/definitionDetails.vue b/dolphinscheduler-ui/src/js/conf/home/pages/dag/definitionDetails.vue
index 65510bf..1a5cc63 100644
--- a/dolphinscheduler-ui/src/js/conf/home/pages/dag/definitionDetails.vue
+++ b/dolphinscheduler-ui/src/js/conf/home/pages/dag/definitionDetails.vue
@@ -53,7 +53,7 @@
         // Promise Get node needs data
         Promise.all([
           // Node details
-          this.getProcessDetails(this.$route.params.id),
+          this.getProcessDetails(this.$route.params.code),
           // get process definition
           this.getProcessList(),
           // get project
@@ -69,8 +69,8 @@
           this.getTenantList()
         ]).then((data) => {
           let item = data[0]
-          this.setIsDetails(item.releaseState === 'ONLINE')
-          this.releaseState = item.releaseState
+          this.setIsDetails(item.processDefinition.releaseState === 'ONLINE')
+          this.releaseState = item.processDefinition.releaseState
           this.isLoading = false
           // Whether to pop up the box?
           Affirm.init(this.$root)
@@ -82,7 +82,7 @@
        * Redraw (refresh operation)
        */
       _reset () {
-        this.getProcessDetails(this.$route.params.id).then(res => {
+        this.getProcessDetails(this.$route.params.code).then(res => {
           let item = res
           this.setIsDetails(item.releaseState === 'ONLINE')
           this.releaseState = item.releaseState
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/list.vue b/dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/list.vue
index bf4eefd..fe55531 100644
--- a/dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/list.vue
+++ b/dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/list.vue
@@ -25,7 +25,7 @@
             <el-popover trigger="hover" placement="top">
               <p>{{ scope.row.name }}</p>
               <div slot="reference" class="name-wrapper">
-                <router-link :to="{ path: `/projects/${projectId}/definition/list/${scope.row.id}` }" tag="a" class="links">
+                <router-link :to="{ path: `/projects/${projectCode}/definition/list/${scope.row.code}` }" tag="a" class="links">
                   <span class="ellipsis">{{scope.row.name}}</span>
                 </router-link>
               </div>
@@ -256,7 +256,7 @@
         }
         // remove one
         this.deleteDefinition({
-          processDefinitionId: item.id
+          code: item.code
         }).then(res => {
           this._onUpdate()
           this.$message.success(res.msg)
@@ -268,14 +268,14 @@
        * edit
        */
       _edit (item) {
-        this.$router.push({ path: `/projects/${this.projectId}/definition/list/${item.id}` })
+        this.$router.push({ path: `/projects/${this.projectCode}/definition/list/${item.code}` })
       },
       /**
        * Offline
        */
       _downline (item) {
         this._upProcessState({
-          processId: item.id,
+          ...item,
           releaseState: 'OFFLINE'
         })
       },
@@ -284,7 +284,7 @@
        */
       _poponline (item) {
         this._upProcessState({
-          processId: item.id,
+          ...item,
           releaseState: 'ONLINE'
         })
       },
@@ -520,7 +520,7 @@
     mounted () {
     },
     computed: {
-      ...mapState('dag', ['projectId'])
+      ...mapState('dag', ['projectId', 'projectCode'])
     },
     components: { mVersions, mStart, mTiming, mRelatedItems }
   }
diff --git a/dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/start.vue b/dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/start.vue
index 982a156..0454868 100644
--- a/dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/start.vue
+++ b/dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/start.vue
@@ -265,7 +265,7 @@
           }
         }
         let param = {
-          processDefinitionId: this.startData.id,
+          processDefinitionCode: this.startData.code,
           scheduleTime: this.scheduleTime.length && this.scheduleTime.join(',') || '',
           failureStrategy: this.failureStrategy,
           warningType: this.warningType,
@@ -319,7 +319,7 @@
         }
       },
       _getGlobalParams () {
-        this.store.dispatch('dag/getProcessDetails', this.startData.id).then(res => {
+        this.store.dispatch('dag/getProcessDetails', this.startData.code).then(res => {
           this.definitionGlobalParams = _.cloneDeep(this.store.state.dag.globalParams)
           this.udpList = _.cloneDeep(this.store.state.dag.globalParams)
         })
diff --git a/dolphinscheduler-ui/src/js/conf/home/router/index.js b/dolphinscheduler-ui/src/js/conf/home/router/index.js
index f73dba2..a0f7ce4 100644
--- a/dolphinscheduler-ui/src/js/conf/home/router/index.js
+++ b/dolphinscheduler-ui/src/js/conf/home/router/index.js
@@ -92,7 +92,7 @@ const router = new Router({
           }
         },
         {
-          path: '/projects/:projectId/kinship',
+          path: '/projects/:projectCode/kinship',
           name: 'projects-kinship',
           component: resolve => require(['../pages/projects/pages/kinship/index'], resolve),
           meta: {
@@ -101,7 +101,7 @@ const router = new Router({
           }
         },
         {
-          path: '/projects/:projectId/definition',
+          path: '/projects/:projectCode/definition',
           name: 'definition',
           component: resolve => require(['../pages/projects/pages/definition/index'], resolve),
           meta: {
@@ -113,7 +113,7 @@ const router = new Router({
           },
           children: [
             {
-              path: '/projects/:projectId/definition/list',
+              path: '/projects/:projectCode/definition/list',
               name: 'projects-definition-list',
               component: resolve => require(['../pages/projects/pages/definition/pages/list/index'], resolve),
               meta: {
@@ -122,7 +122,7 @@ const router = new Router({
               }
             },
             {
-              path: '/projects/:projectId/definition/list/:id',
+              path: '/projects/:projectCode/definition/list/:code',
               name: 'projects-definition-details',
               component: resolve => require(['../pages/projects/pages/definition/pages/details/index'], resolve),
               meta: {
@@ -131,7 +131,7 @@ const router = new Router({
               }
             },
             {
-              path: '/projects/:projectId/definition/create',
+              path: '/projects/:projectCode/definition/create',
               name: 'definition-create',
               component: resolve => require(['../pages/projects/pages/definition/pages/create/index'], resolve),
               meta: {
@@ -139,7 +139,7 @@ const router = new Router({
               }
             },
             {
-              path: '/projects/:projectId/definition/tree/:id',
+              path: '/projects/:projectCode/definition/tree/:id',
               name: 'definition-tree-view-index',
               component: resolve => require(['../pages/projects/pages/definition/pages/tree/index'], resolve),
               meta: {
@@ -148,7 +148,7 @@ const router = new Router({
               }
             },
             {
-              path: '/projects/:projectId/definition/list/timing/:code',
+              path: '/projects/:projectCode/definition/list/timing/:code',
               name: 'definition-timing-details',
               component: resolve => require(['../pages/projects/pages/definition/timing/index'], resolve),
               meta: {
@@ -159,7 +159,7 @@ const router = new Router({
           ]
         },
         {
-          path: '/projects/:projectId/instance',
+          path: '/projects/:projectCode/instance',
           name: 'instance',
           component: resolve => require(['../pages/projects/pages/instance/index'], resolve),
           meta: {
@@ -170,7 +170,7 @@ const router = new Router({
           },
           children: [
             {
-              path: '/projects/:projectId/instance/list',
+              path: '/projects/:projectCode/instance/list',
               name: 'projects-instance-list',
               component: resolve => require(['../pages/projects/pages/instance/pages/list/index'], resolve),
               meta: {
@@ -179,7 +179,7 @@ const router = new Router({
               }
             },
             {
-              path: '/projects/:projectId/instance/list/:id',
+              path: '/projects/:projectCode/instance/list/:id',
               name: 'projects-instance-details',
               component: resolve => require(['../pages/projects/pages/instance/pages/details/index'], resolve),
               meta: {
@@ -188,7 +188,7 @@ const router = new Router({
               }
             },
             {
-              path: '/projects/:projectId/instance/gantt/:id',
+              path: '/projects/:projectCode/instance/gantt/:id',
               name: 'instance-gantt-index',
               component: resolve => require(['../pages/projects/pages/instance/pages/gantt/index'], resolve),
               meta: {
@@ -199,7 +199,7 @@ const router = new Router({
           ]
         },
         {
-          path: '/projects/:projectId/task-instance',
+          path: '/projects/:projectCode/task-instance',
           name: 'task-instance',
           component: resolve => require(['../pages/projects/pages/taskInstance'], resolve),
           meta: {
@@ -209,7 +209,7 @@ const router = new Router({
 
         },
         {
-          path: '/projects/:projectId/task-record',
+          path: '/projects/:projectCode/task-record',
           name: 'task-record',
           component: resolve => require(['../pages/projects/pages/taskRecord'], resolve),
           meta: {
@@ -218,7 +218,7 @@ const router = new Router({
           }
         },
         {
-          path: '/projects/:projectId/history-task-record',
+          path: '/projects/:projectCode/history-task-record',
           name: 'history-task-record',
           component: resolve => require(['../pages/projects/pages/historyTaskRecord'], resolve),
           meta: {
diff --git a/dolphinscheduler-ui/src/js/conf/home/store/dag/actions.js b/dolphinscheduler-ui/src/js/conf/home/store/dag/actions.js
index 63bce50..9ac787d 100644
--- a/dolphinscheduler-ui/src/js/conf/home/store/dag/actions.js
+++ b/dolphinscheduler-ui/src/js/conf/home/store/dag/actions.js
@@ -19,32 +19,13 @@ import _ from 'lodash'
 import io from '@/module/io'
 import { tasksState } from '@/conf/home/pages/dag/_source/config'
 
-// delete 'definitionList' from tasks
-const deleteDefinitionList = (tasks) => {
-  const newTasks = []
-  tasks.forEach(item => {
-    const newItem = Object.assign({}, item)
-    if (newItem.dependence && newItem.dependence.dependTaskList) {
-      newItem.dependence.dependTaskList.forEach(dependTaskItem => {
-        if (dependTaskItem.dependItemList) {
-          dependTaskItem.dependItemList.forEach(dependItem => {
-            Reflect.deleteProperty(dependItem, 'definitionList')
-          })
-        }
-      })
-    }
-    newTasks.push(newItem)
-  })
-  return newTasks
-}
-
 export default {
   /**
    *  Task status acquisition
    */
   getTaskState ({ state }, payload) {
     return new Promise((resolve, reject) => {
-      io.get(`projects/${state.projectName}/instance/task-list-by-process-id`, {
+      io.get(`projects/${state.projectCode}/instance/task-list-by-process-id`, {
         processInstanceId: payload
       }, res => {
         const arr = _.map(res.data.taskList, v => {
@@ -69,8 +50,9 @@ export default {
    */
   editProcessState ({ state }, payload) {
     return new Promise((resolve, reject) => {
-      io.post(`projects/${state.projectName}/process/release`, {
-        processId: payload.processId,
+      io.post(`projects/${state.projectCode}/process/release`, {
+        code: payload.code,
+        name: payload.name,
         releaseState: payload.releaseState
       }, res => {
         resolve(res)
@@ -85,7 +67,7 @@ export default {
    */
   getProcessDefinitionVersionsPage ({ state }, payload) {
     return new Promise((resolve, reject) => {
-      io.get(`projects/${state.projectName}/process/versions`, payload, res => {
+      io.get(`projects/${state.projectCode}/process/versions`, payload, res => {
         resolve(res)
       }).catch(e => {
         reject(e)
@@ -98,7 +80,7 @@ export default {
    */
   switchProcessDefinitionVersion ({ state }, payload) {
     return new Promise((resolve, reject) => {
-      io.get(`projects/${state.projectName}/process/version/switch`, payload, res => {
+      io.get(`projects/${state.projectCode}/process/version/switch`, payload, res => {
         resolve(res)
       }).catch(e => {
         reject(e)
@@ -111,7 +93,7 @@ export default {
    */
   deleteProcessDefinitionVersion ({ state }, payload) {
     return new Promise((resolve, reject) => {
-      io.get(`projects/${state.projectName}/process/version/delete`, payload, res => {
+      io.get(`projects/${state.projectCode}/process/version/delete`, payload, res => {
         resolve(res)
       }).catch(e => {
         reject(e)
@@ -139,7 +121,7 @@ export default {
    */
   verifDAGName ({ state }, payload) {
     return new Promise((resolve, reject) => {
-      io.get(`projects/${state.projectName}/process/verify-name`, {
+      io.get(`projects/${state.projectCode}/process/verify-name`, {
         name: payload
       }, res => {
         state.name = payload
@@ -155,36 +137,45 @@ export default {
    */
   getProcessDetails ({ state }, payload) {
     return new Promise((resolve, reject) => {
-      io.get(`projects/${state.projectName}/process/select-by-id`, {
-        processId: payload
+      io.get(`projects/${state.projectCode}/process/select-by-code`, {
+        code: payload
       }, res => {
         // process definition code
-        state.code = res.data.code
+        state.code = res.data.processDefinition.code
         // version
-        state.version = res.data.version
+        state.version = res.data.processDefinition.version
         // name
-        state.name = res.data.name
+        state.name = res.data.processDefinition.name
         // description
-        state.description = res.data.description
-        // connects
-        state.connects = JSON.parse(res.data.connects)
+        state.description = res.data.processDefinition.description
+        // taskRelationJson
+        state.connects = res.data.processTaskRelationList
         // locations
-        state.locations = JSON.parse(res.data.locations)
-        // Process definition
-        const processDefinitionJson = JSON.parse(res.data.processDefinitionJson)
-        // tasks info
-        state.tasks = processDefinitionJson.tasks
-        // tasks cache
-        state.cacheTasks = {}
-        processDefinitionJson.tasks.forEach(v => {
-          state.cacheTasks[v.id] = v
-        })
+        state.locations = JSON.parse(res.data.processDefinition.locations)
         // global params
-        state.globalParams = processDefinitionJson.globalParams
+        state.globalParams = res.data.processDefinition.globalParamList
         // timeout
-        state.timeout = processDefinitionJson.timeout
-
-        state.tenantId = processDefinitionJson.tenantId
+        state.timeout = res.data.processDefinition.timeout
+        // tenantId
+        state.tenantCode = res.data.processDefinition.tenantCode
+        // tasks info
+        state.tasks = res.data.taskDefinitionList.map(task => _.pick(task, [
+          'code',
+          'name',
+          'version',
+          'description',
+          'delayTime',
+          'taskType',
+          'taskParams',
+          'flag',
+          'taskPriority',
+          'workerGroup',
+          'failRetryTimes',
+          'failRetryInterval',
+          'timeoutFlag',
+          'timeoutNotifyStrategy',
+          'timeout'
+        ]))
         resolve(res.data)
       }).catch(res => {
         reject(res)
@@ -197,7 +188,7 @@ export default {
    */
   copyProcess ({ state }, payload) {
     return new Promise((resolve, reject) => {
-      io.post(`projects/${state.projectName}/process/copy`, {
+      io.post(`projects/${state.projectCode}/process/copy`, {
         processDefinitionIds: payload.processDefinitionIds,
         targetProjectId: payload.targetProjectId
       }, res => {
@@ -213,7 +204,7 @@ export default {
    */
   moveProcess ({ state }, payload) {
     return new Promise((resolve, reject) => {
-      io.post(`projects/${state.projectName}/process/move`, {
+      io.post(`projects/${state.project}/process/move`, {
         processDefinitionIds: payload.processDefinitionIds,
         targetProjectId: payload.targetProjectId
       }, res => {
@@ -242,37 +233,46 @@ export default {
    */
   getInstancedetail ({ state }, payload) {
     return new Promise((resolve, reject) => {
-      io.get(`projects/${state.projectName}/instance/select-by-id`, {
+      io.get(`projects/${state.projectCode}/instance/select-by-id`, {
         processInstanceId: payload
       }, res => {
+        const { processDefinition, processTaskRelationList, taskDefinitionList } = res.data.dagData
         // code
-        state.code = res.data.processDefinitionCode
+        state.code = processDefinition.code
         // version
-        state.version = res.data.processDefinitionVersion
+        state.version = processDefinition.version
         // name
         state.name = res.data.name
         // desc
-        state.description = res.data.description
+        state.description = processDefinition.description
         // connects
-        state.connects = JSON.parse(res.data.connects)
+        state.connects = processTaskRelationList
         // locations
-        state.locations = JSON.parse(res.data.locations)
-        // process instance
-        const processInstanceJson = JSON.parse(res.data.processInstanceJson)
-        // tasks info
-        state.tasks = processInstanceJson.tasks
-        // tasks cache
-        state.cacheTasks = {}
-        processInstanceJson.tasks.forEach(v => {
-          state.cacheTasks[v.id] = v
-        })
+        state.locations = JSON.parse(processDefinition.locations)
         // global params
-        state.globalParams = processInstanceJson.globalParams
+        state.globalParams = processDefinition.globalParamList
         // timeout
-        state.timeout = processInstanceJson.timeout
-
-        state.tenantId = processInstanceJson.tenantId
-
+        state.timeout = processDefinition.timeout
+        // tenantCode
+        state.tenantCode = res.data.tenantCode
+        // tasks info
+        state.tasks = taskDefinitionList.map(task => _.pick(task, [
+          'code',
+          'name',
+          'version',
+          'description',
+          'delayTime',
+          'taskType',
+          'taskParams',
+          'flag',
+          'taskPriority',
+          'workerGroup',
+          'failRetryTimes',
+          'failRetryInterval',
+          'timeoutFlag',
+          'timeoutNotifyStrategy',
+          'timeout'
+        ]))
         // startup parameters
         state.startup = _.assign(state.startup, _.pick(res.data, ['commandType', 'failureStrategy', 'processInstancePriority', 'workerGroup', 'warningType', 'warningGroupId', 'receivers', 'receiversCc']))
         state.startup.commandParam = JSON.parse(res.data.commandParam)
@@ -288,18 +288,15 @@ export default {
    */
   saveDAGchart ({ state }, payload) {
     return new Promise((resolve, reject) => {
-      const data = {
-        globalParams: state.globalParams,
-        tasks: deleteDefinitionList(state.tasks),
-        tenantId: state.tenantId,
-        timeout: state.timeout
-      }
-      io.post(`projects/${state.projectName}/process/save`, {
-        processDefinitionJson: JSON.stringify(data),
+      io.post(`projects/${state.projectCode}/process/save`, {
+        locations: JSON.stringify(state.locations),
         name: _.trim(state.name),
+        taskDefinitionJson: JSON.stringify(state.tasks),
+        taskRelationJson: JSON.stringify(state.connects),
+        tenantCode: state.tenantCode,
         description: _.trim(state.description),
-        locations: JSON.stringify(state.locations),
-        connects: JSON.stringify(state.connects)
+        globalParams: JSON.stringify(state.globalParams),
+        timeout: state.timeout
       }, res => {
         resolve(res)
       }).catch(e => {
@@ -312,20 +309,17 @@ export default {
    */
   updateDefinition ({ state }, payload) {
     return new Promise((resolve, reject) => {
-      const data = {
-        globalParams: state.globalParams,
-        tasks: deleteDefinitionList(state.tasks),
-        tenantId: state.tenantId,
-        timeout: state.timeout
-      }
-      io.post(`projects/${state.projectName}/process/update`, {
-        processDefinitionJson: JSON.stringify(data),
+      io.post(`projects/${state.projectCode}/process/update`, {
         locations: JSON.stringify(state.locations),
-        connects: JSON.stringify(state.connects),
         name: _.trim(state.name),
+        taskDefinitionJson: JSON.stringify(state.tasks),
+        taskRelationJson: JSON.stringify(state.connects),
+        tenantCode: state.tenantCode,
         description: _.trim(state.description),
-        id: payload,
-        releaseState: state.releaseState
+        globalParams: JSON.stringify(state.globalParams),
+        timeout: state.timeout,
+        releaseState: state.releaseState,
+        code: payload
       }, res => {
         resolve(res)
         state.isEditDag = false
@@ -345,7 +339,7 @@ export default {
         tenantId: state.tenantId,
         timeout: state.timeout
       }
-      io.post(`projects/${state.projectName}/instance/update`, {
+      io.post(`projects/${state.projectCode}/instance/update`, {
         processInstanceJson: JSON.stringify(data),
         locations: JSON.stringify(state.locations),
         connects: JSON.stringify(state.connects),
@@ -368,7 +362,7 @@ export default {
         resolve()
         return
       }
-      io.get(`projects/${state.projectName}/process/list`, payload, res => {
+      io.get(`projects/${state.projectCode}/process/list`, payload, res => {
         state.processListS = res.data
         resolve(res.data)
       }).catch(res => {
@@ -381,7 +375,7 @@ export default {
    */
   getProcessListP ({ state }, payload) {
     return new Promise((resolve, reject) => {
-      io.get(`projects/${state.projectName}/process/list-paging`, payload, res => {
+      io.get(`projects/${state.projectCode}/process/list-paging`, payload, res => {
         resolve(res.data)
       }).catch(res => {
         reject(res)
@@ -410,7 +404,7 @@ export default {
    */
   getProcessByProjectId ({ state }, payload) {
     return new Promise((resolve, reject) => {
-      io.get(`projects/${state.projectName}/process/queryProcessDefinitionAllByProjectId`, payload, res => {
+      io.get(`projects/${state.projectCode}/process/queryProcessDefinitionAllByProjectId`, payload, res => {
         resolve(res.data)
       }).catch(res => {
         reject(res)
@@ -474,7 +468,7 @@ export default {
    */
   getProcessInstance ({ state }, payload) {
     return new Promise((resolve, reject) => {
-      io.get(`projects/${state.projectName}/instance/list-paging`, payload, res => {
+      io.get(`projects/${state.projectCode}/instance/list-paging`, payload, res => {
         state.instanceListS = res.data.totalList
         resolve(res.data)
       }).catch(res => {
@@ -531,7 +525,7 @@ export default {
    */
   getSubProcessId ({ state }, payload) {
     return new Promise((resolve, reject) => {
-      io.get(`projects/${state.projectName}/instance/select-sub-process`, payload, res => {
+      io.get(`projects/${state.projectCode}/instance/select-sub-process`, payload, res => {
         resolve(res)
       }).catch(e => {
         reject(e)
@@ -628,7 +622,7 @@ export default {
    */
   deleteInstance ({ state }, payload) {
     return new Promise((resolve, reject) => {
-      io.get(`projects/${state.projectName}/instance/delete`, payload, res => {
+      io.get(`projects/${state.projectCode}/instance/delete`, payload, res => {
         resolve(res)
       }).catch(e => {
         reject(e)
@@ -640,7 +634,7 @@ export default {
    */
   batchDeleteInstance ({ state }, payload) {
     return new Promise((resolve, reject) => {
-      io.get(`projects/${state.projectName}/instance/batch-delete`, payload, res => {
+      io.get(`projects/${state.projectCode}/instance/batch-delete`, payload, res => {
         resolve(res)
       }).catch(e => {
         reject(e)
@@ -652,7 +646,7 @@ export default {
    */
   deleteDefinition ({ state }, payload) {
     return new Promise((resolve, reject) => {
-      io.get(`projects/${state.projectName}/process/delete`, payload, res => {
+      io.get(`projects/${state.projectCode}/process/delete`, payload, res => {
         resolve(res)
       }).catch(e => {
         reject(e)
@@ -664,7 +658,7 @@ export default {
    */
   batchDeleteDefinition ({ state }, payload) {
     return new Promise((resolve, reject) => {
-      io.get(`projects/${state.projectName}/process/batch-delete`, payload, res => {
+      io.get(`projects/${state.projectCode}/process/batch-delete`, payload, res => {
         resolve(res)
       }).catch(e => {
         reject(e)
@@ -696,7 +690,7 @@ export default {
       }
     }
 
-    io.get(`projects/${state.projectName}/process/export`, { processDefinitionIds: payload.processDefinitionIds }, res => {
+    io.get(`projects/${state.projectCode}/process/export`, { processDefinitionIds: payload.processDefinitionIds }, res => {
       downloadBlob(res, payload.fileName)
     }, e => {
 
@@ -710,7 +704,7 @@ export default {
    */
   getViewvariables ({ state }, payload) {
     return new Promise((resolve, reject) => {
-      io.get(`projects/${state.projectName}/instance/view-variables`, payload, res => {
+      io.get(`projects/${state.projectCode}/instance/view-variables`, payload, res => {
         resolve(res)
       }).catch(e => {
         reject(e)
@@ -734,7 +728,7 @@ export default {
    */
   getTaskInstanceList ({ state }, payload) {
     return new Promise((resolve, reject) => {
-      io.get(`projects/${state.projectName}/task-instance/list-paging`, payload, res => {
+      io.get(`projects/${state.projectCode}/task-instance/list-paging`, payload, res => {
         resolve(res.data)
       }).catch(e => {
         reject(e)
@@ -746,7 +740,7 @@ export default {
    */
   forceTaskSuccess ({ state }, payload) {
     return new Promise((resolve, reject) => {
-      io.post(`projects/${state.projectName}/task-instance/force-success`, payload, res => {
+      io.post(`projects/${state.projectCode}/task-instance/force-success`, payload, res => {
         resolve(res)
       }).catch(e => {
         reject(e)
@@ -782,7 +776,7 @@ export default {
    */
   getViewTree ({ state }, payload) {
     return new Promise((resolve, reject) => {
-      io.get(`projects/${state.projectName}/process/view-tree`, payload, res => {
+      io.get(`projects/${state.projectCode}/process/view-tree`, payload, res => {
         resolve(res.data)
       }).catch(e => {
         reject(e)
@@ -794,7 +788,7 @@ export default {
    */
   getViewGantt ({ state }, payload) {
     return new Promise((resolve, reject) => {
-      io.get(`projects/${state.projectName}/instance/view-gantt`, payload, res => {
+      io.get(`projects/${state.projectCode}/instance/view-gantt`, payload, res => {
         resolve(res.data)
       }).catch(e => {
         reject(e)
@@ -806,7 +800,7 @@ export default {
    */
   getProcessTasksList ({ state }, payload) {
     return new Promise((resolve, reject) => {
-      io.get(`projects/${state.projectName}/process/gen-task-list`, payload, res => {
+      io.get(`projects/${state.projectCode}/process/gen-task-list`, payload, res => {
         resolve(res.data)
       }).catch(e => {
         reject(e)
@@ -815,7 +809,7 @@ export default {
   },
   getTaskListDefIdAll ({ state }, payload) {
     return new Promise((resolve, reject) => {
-      io.get(`projects/${state.projectName}/process/get-task-list`, payload, res => {
+      io.get(`projects/${state.projectCode}/process/get-task-list`, payload, res => {
         resolve(res.data)
       }).catch(e => {
         reject(e)
@@ -842,5 +836,14 @@ export default {
         reject(e)
       })
     })
+  },
+  genTaskCodeList ({ state }, payload) {
+    return new Promise((resolve, reject) => {
+      io.get(`projects/${state.projectCode}/task/gen-task-code-list`, payload, res => {
+        resolve(res.data)
+      }).catch(e => {
+        reject(e)
+      })
+    })
   }
 }
diff --git a/dolphinscheduler-ui/src/js/conf/home/store/dag/mutations.js b/dolphinscheduler-ui/src/js/conf/home/store/dag/mutations.js
index 010a860..b974fed 100755
--- a/dolphinscheduler-ui/src/js/conf/home/store/dag/mutations.js
+++ b/dolphinscheduler-ui/src/js/conf/home/store/dag/mutations.js
@@ -64,10 +64,10 @@ export default {
     state.timeout = payload
   },
   /**
-   * set tenantId
+   * set tenantCode
    */
-  setTenantId (state, payload) {
-    state.tenantId = payload
+  setTenantCode (state, payload) {
+    state.tenantCode = payload
   },
   /**
    * set global params
@@ -108,13 +108,12 @@ export default {
    * reset params
    */
   resetParams (state, payload) {
-    $('#canvas').html('')
     state.globalParams = (payload && payload.globalParams) || []
     state.tasks = (payload && payload.tasks) || []
     state.name = (payload && payload.name) || ''
     state.description = (payload && payload.description) || ''
     state.timeout = (payload && payload.timeout) || 0
-    state.tenantId = (payload && payload.tenantId) || -1
+    state.tenantCode = (payload && payload.tenantCode) || ''
     state.processListS = (payload && payload.processListS) || []
     state.resourcesListS = (payload && payload.resourcesListS) || []
     state.resourcesListJar = (payload && payload.resourcesListJar) || []
@@ -126,48 +125,23 @@ export default {
   },
   /**
    * add task
-   * object {}
+   * @param {Task} task
    */
-  addTasks (state, payload) {
-    const i = _.findIndex(state.tasks, v => v.id === payload.id)
+  addTask (state, task) {
+    const i = _.findIndex(state.tasks, v => v.code === task.code)
     if (i !== -1) {
-      state.tasks[i] = Object.assign(state.tasks[i], {}, payload)
+      state.tasks[i] = Object.assign(state.tasks[i], {}, task)
     } else {
-      state.tasks.push(payload)
+      state.tasks.push(task)
     }
-    if (state.cacheTasks[payload.id]) {
-      state.cacheTasks[payload.id] = Object.assign(state.cacheTasks[payload.id], {}, payload)
-    } else {
-      state.cacheTasks[payload.id] = payload
-    }
-    const dom = $(`#${payload.id}`)
-    state.locations[payload.id] = _.assign(state.locations[payload.id], {
-      name: dom.find('.name-p').text(),
-      targetarr: dom.attr('data-targetarr'),
-      nodenumber: dom.attr('data-nodenumber'),
-      x: parseInt(dom.css('left'), 10),
-      y: parseInt(dom.css('top'), 10)
-    })
-  },
-  addConnects (state, payload) {
-    state.connects = _.map(state.connects, v => {
-      if (v.endPointSourceId === payload.sourceId && v.endPointTargetId === payload.targetId) {
-        v.label = payload.labelName
-      }
-      return v
-    })
   },
   /**
-   * Cache the input
-   * @param state
-   * @param payload
+   * remove task
+   * @param {object} state
+   * @param {string} code
    */
-  cacheTasks (state, payload) {
-    if (state.cacheTasks[payload.id]) {
-      state.cacheTasks[payload.id] = Object.assign(state.cacheTasks[payload.id], {}, payload)
-    } else {
-      state.cacheTasks[payload.id] = payload
-    }
+  removeTask (state, code) {
+    state.tasks = state.tasks.filter(task => task.code !== code)
   },
   resetLocalParam (state, payload) {
     const tasks = state.tasks
diff --git a/dolphinscheduler-ui/src/js/conf/home/store/dag/state.js b/dolphinscheduler-ui/src/js/conf/home/store/dag/state.js
index 4843ad9..b04c54f 100644
--- a/dolphinscheduler-ui/src/js/conf/home/store/dag/state.js
+++ b/dolphinscheduler-ui/src/js/conf/home/store/dag/state.js
@@ -39,11 +39,11 @@ export default {
   cacheTasks: {},
   // Timeout alarm
   timeout: 0,
-  // tenant id
-  tenantId: -1,
+  // tenant code
+  tenantCode: '',
   // Node location information
   locations: {},
-  // Node-to-node connection
+  // Node relations
   connects: [],
   // Running sign
   runFlag: '',