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

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


The following commit(s) were added to refs/heads/dev by this push:
     new 45f6c41  [Feature][UI Next] Workflow create (#8123)
45f6c41 is described below

commit 45f6c4197b03566895008577de964f0e5f1c80c8
Author: wangyizhi <[email protected]>
AuthorDate: Wed Jan 19 18:46:54 2022 +0800

    [Feature][UI Next] Workflow create (#8123)
---
 dolphinscheduler-ui-next/package.json              |   1 +
 dolphinscheduler-ui-next/pnpm-lock.yaml            |  30 ++
 .../src/assets/images/task-icons/conditions.png    | Bin 0 -> 812 bytes
 .../assets/images/task-icons/conditions_hover.png  | Bin 0 -> 736 bytes
 .../src/assets/images/task-icons/datax.png         | Bin 0 -> 1122 bytes
 .../src/assets/images/task-icons/datax_hover.png   | Bin 0 -> 1127 bytes
 .../src/assets/images/task-icons/dependent.png     | Bin 0 -> 743 bytes
 .../assets/images/task-icons/dependent_hover.png   | Bin 0 -> 745 bytes
 .../src/assets/images/task-icons/flink.png         | Bin 0 -> 1443 bytes
 .../src/assets/images/task-icons/flink_hover.png   | Bin 0 -> 1348 bytes
 .../src/assets/images/task-icons/http.png          | Bin 0 -> 707 bytes
 .../src/assets/images/task-icons/http_hover.png    | Bin 0 -> 709 bytes
 .../src/assets/images/task-icons/mr.png            | Bin 0 -> 930 bytes
 .../src/assets/images/task-icons/mr_hover.png      | Bin 0 -> 862 bytes
 .../src/assets/images/task-icons/pigeon.png        | Bin 0 -> 1192 bytes
 .../src/assets/images/task-icons/pigeon_hover.png  | Bin 0 -> 1167 bytes
 .../src/assets/images/task-icons/procedure.png     | Bin 0 -> 1555 bytes
 .../assets/images/task-icons/procedure_hover.png   | Bin 0 -> 1436 bytes
 .../src/assets/images/task-icons/python.png        | Bin 0 -> 1618 bytes
 .../src/assets/images/task-icons/python_hover.png  | Bin 0 -> 1233 bytes
 .../src/assets/images/task-icons/seatunnel.png     | Bin 0 -> 1086 bytes
 .../assets/images/task-icons/seatunnel_hover.png   | Bin 0 -> 1054 bytes
 .../src/assets/images/task-icons/shell.png         | Bin 0 -> 747 bytes
 .../src/assets/images/task-icons/shell_hover.png   | Bin 0 -> 745 bytes
 .../src/assets/images/task-icons/spark.png         | Bin 0 -> 1067 bytes
 .../src/assets/images/task-icons/spark_hover.png   | Bin 0 -> 1066 bytes
 .../src/assets/images/task-icons/sql.png           | Bin 0 -> 1317 bytes
 .../src/assets/images/task-icons/sql_hover.png     | Bin 0 -> 1262 bytes
 .../src/assets/images/task-icons/sqoop.png         | Bin 0 -> 896 bytes
 .../src/assets/images/task-icons/sqoop_hover.png   | Bin 0 -> 897 bytes
 .../src/assets/images/task-icons/sub_process.png   | Bin 0 -> 692 bytes
 .../assets/images/task-icons/sub_process_hover.png | Bin 0 -> 693 bytes
 .../src/assets/images/task-icons/switch.png        | Bin 0 -> 885 bytes
 .../src/assets/images/task-icons/switch_hover.png  | Bin 0 -> 825 bytes
 .../src/locales/modules/en_US.ts                   |  11 +
 .../src/locales/modules/zh_CN.ts                   |  11 +
 .../src/router/modules/projects.ts                 |  16 +
 .../src/service/modules/task-definition/index.ts   |  13 +-
 .../src/service/modules/task-definition/types.ts   |   4 -
 dolphinscheduler-ui-next/src/utils/index.ts        |   4 +-
 .../src/utils/{index.ts => truncate-text.ts}       |  43 ++-
 .../{utils/index.ts => views/projects/index.tsx}   |  17 +-
 .../index.ts => views/projects/task/config.ts}     |  59 +++-
 .../projects/task/task-config.tsx}                 |  17 +-
 .../src/views/projects/workflow/dag-canvas.tsx     |  60 ++++
 .../src/views/projects/workflow/dag-config.ts      | 339 +++++++++++++++++++++
 .../projects/workflow/dag-hooks.ts}                |  22 +-
 .../src/views/projects/workflow/dag-sidebar.tsx    |  68 +++++
 .../src/views/projects/workflow/dag-toolbar.tsx    | 180 +++++++++++
 .../src/views/projects/workflow/dag.module.scss    | 217 +++++++++++++
 .../src/views/projects/workflow/dag.tsx            |  67 ++++
 .../projects/workflow/hook-demo.ts}                |  26 +-
 .../src/views/projects/workflow/use-canvas-drop.ts |  70 +++++
 .../src/views/projects/workflow/use-canvas-init.ts | 177 +++++++++++
 .../src/views/projects/workflow/use-cell-active.ts | 154 ++++++++++
 .../projects/workflow/use-graph-operations.ts      | 157 ++++++++++
 .../src/views/projects/workflow/use-node-search.ts |  60 ++++
 .../projects/workflow/use-sidebar-drag.ts}         |  34 ++-
 .../workflow-definition-create.module.scss}        |  14 +-
 .../workflow/workflow-definition-create.tsx}       |  26 +-
 .../workflow/workflow-definition-details.tsx}      |  17 +-
 .../workflow/workflow-definition-list.tsx}         |  17 +-
 .../workflow/workflow-instance-details.tsx}        |  17 +-
 .../projects/workflow/workflow-instance-list.tsx}  |  17 +-
 .../projects/workflow/x6-style.scss}               |  29 +-
 65 files changed, 1871 insertions(+), 123 deletions(-)

diff --git a/dolphinscheduler-ui-next/package.json 
b/dolphinscheduler-ui-next/package.json
index c56b683..941d81f 100644
--- a/dolphinscheduler-ui-next/package.json
+++ b/dolphinscheduler-ui-next/package.json
@@ -10,6 +10,7 @@
     "prettier": "prettier --write \"src/**/*.{vue,ts,tsx}\""
   },
   "dependencies": {
+    "@antv/x6": "^1.29.5",
     "@vueuse/core": "^7.5.3",
     "axios": "^0.24.0",
     "date-fns": "^2.27.0",
diff --git a/dolphinscheduler-ui-next/pnpm-lock.yaml 
b/dolphinscheduler-ui-next/pnpm-lock.yaml
index 142dc18..e446f5a 100644
--- a/dolphinscheduler-ui-next/pnpm-lock.yaml
+++ b/dolphinscheduler-ui-next/pnpm-lock.yaml
@@ -18,6 +18,7 @@
 lockfileVersion: 5.3
 
 specifiers:
+  '@antv/x6': ^1.29.5
   '@types/node': ^16.11.19
   '@types/nprogress': ^0.2.0
   '@types/qs': ^6.9.7
@@ -56,6 +57,7 @@ specifiers:
   vue-tsc: ^0.28.10
 
 dependencies:
+  '@antv/x6': 1.29.5
   '@vueuse/core': [email protected]
   axios: 0.24.0
   date-fns: 2.28.0
@@ -96,6 +98,18 @@ devDependencies:
   vue-tsc: [email protected]
 
 packages:
+
+  /@antv/x6/1.29.5:
+    resolution: {integrity: 
sha512-U5gg40jo+UtzjdX/7QFenVZgGKOtDFDo60AMNGEvIEzGnixV+2zV0EBJ2f8We4Fp1ZVP0G2pm6uS7ajuuuLsvg==}
+    dependencies:
+      csstype: 3.0.10
+      jquery: 3.6.0
+      jquery-mousewheel: 3.1.13
+      lodash-es: 4.17.21
+      mousetrap: 1.6.5
+      utility-types: 3.10.0
+    dev: false
+
   /@babel/code-frame/7.16.7:
     resolution:
       {
@@ -2792,6 +2806,14 @@ packages:
     engines: { node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0 }
     dev: false
 
+  /jquery-mousewheel/3.1.13:
+    resolution: {integrity: sha1-BvAzXxbjU6aV5yBr9QUDy1I6buU=}
+    dev: false
+
+  /jquery/3.6.0:
+    resolution: {integrity: 
sha512-JVzAR/AjBvVt2BmYhxRCSYysDsPcssdmTFnzyLEts9qNwmjmu4JTAMYubEfwVOSwpQ1I1sKKFcxhZCI2buerfw==}
+    dev: false
+
   /js-stringify/1.0.2:
     resolution: { integrity: sha1-Fzb939lyTyijaCrcYjCufk6Weds= }
     dev: true
@@ -3064,6 +3086,9 @@ packages:
       {
         integrity: 
sha512-FYPwxGZAeP6mRRyrr5XTGHD9gRXVjy7GUzF4IPChnyt3fS5WrNxIkS8DNujWf6EQy0Zlzpxw8oTVE+mWI2/D1Q==
       }
+
+  /mousetrap/1.6.5:
+    resolution: {integrity: 
sha512-QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA==}
     dev: false
 
   /ms/2.0.0:
@@ -4117,6 +4142,11 @@ packages:
     deprecated: Please see https://github.com/lydell/urix#deprecated
     dev: true
 
+  /utility-types/3.10.0:
+    resolution: {integrity: 
sha512-O11mqxmi7wMKCo6HKFt5AhO4BwY3VV68YU07tgxfz8zJTIxr4BpsezN49Ffwy9j3ZpwwJp4fkRwjRzq3uWE6Rg==}
+    engines: {node: '>= 4'}
+    dev: false
+
   /v8-compile-cache/2.3.0:
     resolution:
       {
diff --git 
a/dolphinscheduler-ui-next/src/assets/images/task-icons/conditions.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/conditions.png
new file mode 100755
index 0000000..0849964
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/conditions.png differ
diff --git 
a/dolphinscheduler-ui-next/src/assets/images/task-icons/conditions_hover.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/conditions_hover.png
new file mode 100755
index 0000000..a0c4551
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/conditions_hover.png 
differ
diff --git a/dolphinscheduler-ui-next/src/assets/images/task-icons/datax.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/datax.png
new file mode 100755
index 0000000..22519a9
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/datax.png differ
diff --git 
a/dolphinscheduler-ui-next/src/assets/images/task-icons/datax_hover.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/datax_hover.png
new file mode 100755
index 0000000..986470d
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/datax_hover.png differ
diff --git 
a/dolphinscheduler-ui-next/src/assets/images/task-icons/dependent.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/dependent.png
new file mode 100755
index 0000000..3f0b732
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/dependent.png differ
diff --git 
a/dolphinscheduler-ui-next/src/assets/images/task-icons/dependent_hover.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/dependent_hover.png
new file mode 100755
index 0000000..4b4b866
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/dependent_hover.png 
differ
diff --git a/dolphinscheduler-ui-next/src/assets/images/task-icons/flink.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/flink.png
new file mode 100755
index 0000000..568efbe
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/flink.png differ
diff --git 
a/dolphinscheduler-ui-next/src/assets/images/task-icons/flink_hover.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/flink_hover.png
new file mode 100755
index 0000000..7c8e521
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/flink_hover.png differ
diff --git a/dolphinscheduler-ui-next/src/assets/images/task-icons/http.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/http.png
new file mode 100755
index 0000000..1d80cd0
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/http.png differ
diff --git 
a/dolphinscheduler-ui-next/src/assets/images/task-icons/http_hover.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/http_hover.png
new file mode 100755
index 0000000..31403be
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/http_hover.png differ
diff --git a/dolphinscheduler-ui-next/src/assets/images/task-icons/mr.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/mr.png
new file mode 100755
index 0000000..59d082f
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/mr.png differ
diff --git a/dolphinscheduler-ui-next/src/assets/images/task-icons/mr_hover.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/mr_hover.png
new file mode 100755
index 0000000..e4cfd96
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/mr_hover.png differ
diff --git a/dolphinscheduler-ui-next/src/assets/images/task-icons/pigeon.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/pigeon.png
new file mode 100644
index 0000000..6fe21d2
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/pigeon.png differ
diff --git 
a/dolphinscheduler-ui-next/src/assets/images/task-icons/pigeon_hover.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/pigeon_hover.png
new file mode 100644
index 0000000..d9d651b
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/pigeon_hover.png differ
diff --git 
a/dolphinscheduler-ui-next/src/assets/images/task-icons/procedure.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/procedure.png
new file mode 100755
index 0000000..e681c28
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/procedure.png differ
diff --git 
a/dolphinscheduler-ui-next/src/assets/images/task-icons/procedure_hover.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/procedure_hover.png
new file mode 100755
index 0000000..82ad43d
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/procedure_hover.png 
differ
diff --git a/dolphinscheduler-ui-next/src/assets/images/task-icons/python.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/python.png
new file mode 100755
index 0000000..8bc4d51
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/python.png differ
diff --git 
a/dolphinscheduler-ui-next/src/assets/images/task-icons/python_hover.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/python_hover.png
new file mode 100755
index 0000000..f94a4f7
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/python_hover.png differ
diff --git 
a/dolphinscheduler-ui-next/src/assets/images/task-icons/seatunnel.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/seatunnel.png
new file mode 100755
index 0000000..bf2f83e
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/seatunnel.png differ
diff --git 
a/dolphinscheduler-ui-next/src/assets/images/task-icons/seatunnel_hover.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/seatunnel_hover.png
new file mode 100755
index 0000000..e6affd6
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/seatunnel_hover.png 
differ
diff --git a/dolphinscheduler-ui-next/src/assets/images/task-icons/shell.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/shell.png
new file mode 100755
index 0000000..4e40b6e
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/shell.png differ
diff --git 
a/dolphinscheduler-ui-next/src/assets/images/task-icons/shell_hover.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/shell_hover.png
new file mode 100755
index 0000000..b615f55
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/shell_hover.png differ
diff --git a/dolphinscheduler-ui-next/src/assets/images/task-icons/spark.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/spark.png
new file mode 100755
index 0000000..e9ff012
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/spark.png differ
diff --git 
a/dolphinscheduler-ui-next/src/assets/images/task-icons/spark_hover.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/spark_hover.png
new file mode 100755
index 0000000..b0fbc56
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/spark_hover.png differ
diff --git a/dolphinscheduler-ui-next/src/assets/images/task-icons/sql.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/sql.png
new file mode 100755
index 0000000..3415486
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/sql.png differ
diff --git 
a/dolphinscheduler-ui-next/src/assets/images/task-icons/sql_hover.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/sql_hover.png
new file mode 100755
index 0000000..fabce22
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/sql_hover.png differ
diff --git a/dolphinscheduler-ui-next/src/assets/images/task-icons/sqoop.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/sqoop.png
new file mode 100755
index 0000000..2c754d3
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/sqoop.png differ
diff --git 
a/dolphinscheduler-ui-next/src/assets/images/task-icons/sqoop_hover.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/sqoop_hover.png
new file mode 100755
index 0000000..dd64890
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/sqoop_hover.png differ
diff --git 
a/dolphinscheduler-ui-next/src/assets/images/task-icons/sub_process.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/sub_process.png
new file mode 100755
index 0000000..fb12061
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/sub_process.png differ
diff --git 
a/dolphinscheduler-ui-next/src/assets/images/task-icons/sub_process_hover.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/sub_process_hover.png
new file mode 100755
index 0000000..d6b4abc
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/sub_process_hover.png 
differ
diff --git a/dolphinscheduler-ui-next/src/assets/images/task-icons/switch.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/switch.png
new file mode 100755
index 0000000..02717ea
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/switch.png differ
diff --git 
a/dolphinscheduler-ui-next/src/assets/images/task-icons/switch_hover.png 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/switch_hover.png
new file mode 100755
index 0000000..0f9f0c1
Binary files /dev/null and 
b/dolphinscheduler-ui-next/src/assets/images/task-icons/switch_hover.png differ
diff --git a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts 
b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts
index b26fd0d..f49adf8 100644
--- a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts
+++ b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts
@@ -286,6 +286,17 @@ const project = {
     confirm: 'Confirm',
     cancel: 'Cancel',
     delete_confirm: 'Delete?'
+  },
+  dag: {
+    createWorkflow: "Create Workflow",
+    search: 'Search',
+    download_png: 'Download PNG',
+    fullscreen_open: 'Open Fullscreen',
+    fullscreen_close: 'Close Fullscreen',
+    workflow_version: 'Workflow Version Info',
+    save: 'Save',
+    close: 'Close',
+    format: 'Format'
   }
 }
 
diff --git a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts 
b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts
index c5f4264..517f819 100644
--- a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts
+++ b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts
@@ -286,6 +286,17 @@ const project = {
     confirm: '确定',
     cancel: '取消',
     delete_confirm: '确定删除吗?'
+  },
+  dag: {
+    createWorkflow: "创建工作流",
+    search: '搜索',
+    download_png: '下载工作流图片',
+    fullscreen_open: '全屏',
+    fullscreen_close: '退出全屏',
+    workflow_version: '工作流版本信息',
+    save: '保存',
+    close: '关闭',
+    format: '格式化'
   }
 }
 
diff --git a/dolphinscheduler-ui-next/src/router/modules/projects.ts 
b/dolphinscheduler-ui-next/src/router/modules/projects.ts
index 5fcbba2..56454a5 100644
--- a/dolphinscheduler-ui-next/src/router/modules/projects.ts
+++ b/dolphinscheduler-ui-next/src/router/modules/projects.ts
@@ -46,6 +46,22 @@ export default {
         title: '工作流监控',
         showSide: true
       }
+    },
+    {
+      path: '/projects/:projectCode/workflow/definitions/create',
+      name: 'workflow-definition-create',
+      component: components['workflow-definition-create'],
+      meta: {
+        title: '创建工作流定义',
+      },
+    },
+    {
+      path: '/projects/:projectCode/workflow/definitions/:code',
+      name: 'workflow-definition-details',
+      component: components['workflow-definition-details'],
+      meta: {
+        title: '工作流定义详情',
+      },
     }
   ]
 }
diff --git 
a/dolphinscheduler-ui-next/src/service/modules/task-definition/index.ts 
b/dolphinscheduler-ui-next/src/service/modules/task-definition/index.ts
index c2c0020..0770c86 100644
--- a/dolphinscheduler-ui-next/src/service/modules/task-definition/index.ts
+++ b/dolphinscheduler-ui-next/src/service/modules/task-definition/index.ts
@@ -21,7 +21,6 @@ import {
   ProjectCodeReq,
   TaskDefinitionListReq,
   TaskDefinitionJsonReq,
-  GenNumReq,
   CodeReq,
   TaskDefinitionJsonObjReq,
   ReleaseStateReq,
@@ -51,13 +50,15 @@ export function save(
 }
 
 export function genTaskCodeList(
-  params: GenNumReq,
-  projectCode: ProjectCodeReq
-): any {
-  return axios({
+  num: number,
+  projectCode: number
+) {
+  return axios.request<unknown, number[]>({
     url: `/projects/${projectCode}/task-definition/gen-task-codes`,
     method: 'get',
-    params
+    params: {
+      genNum: num
+    }
   })
 }
 
diff --git 
a/dolphinscheduler-ui-next/src/service/modules/task-definition/types.ts 
b/dolphinscheduler-ui-next/src/service/modules/task-definition/types.ts
index e0d75f2..2207e54 100644
--- a/dolphinscheduler-ui-next/src/service/modules/task-definition/types.ts
+++ b/dolphinscheduler-ui-next/src/service/modules/task-definition/types.ts
@@ -37,10 +37,6 @@ interface TaskDefinitionJsonReq {
   taskDefinitionJson: string
 }
 
-interface GenNumReq {
-  genNum: number
-}
-
 interface CodeReq {
   code: number
 }
diff --git a/dolphinscheduler-ui-next/src/utils/index.ts 
b/dolphinscheduler-ui-next/src/utils/index.ts
index efca1e8..4639c2c 100644
--- a/dolphinscheduler-ui-next/src/utils/index.ts
+++ b/dolphinscheduler-ui-next/src/utils/index.ts
@@ -17,10 +17,12 @@
 
 import mapping from './mapping'
 import regex from './regex'
+import truncateText  from './truncate-text'
 
 const utils = {
   mapping,
-  regex
+  regex,
+  truncateText,
 }
 
 export default utils
diff --git a/dolphinscheduler-ui-next/src/utils/index.ts 
b/dolphinscheduler-ui-next/src/utils/truncate-text.ts
similarity index 54%
copy from dolphinscheduler-ui-next/src/utils/index.ts
copy to dolphinscheduler-ui-next/src/utils/truncate-text.ts
index efca1e8..2605ac0 100644
--- a/dolphinscheduler-ui-next/src/utils/index.ts
+++ b/dolphinscheduler-ui-next/src/utils/truncate-text.ts
@@ -15,12 +15,37 @@
  * limitations under the License.
  */
 
-import mapping from './mapping'
-import regex from './regex'
-
-const utils = {
-  mapping,
-  regex
-}
-
-export default utils
+/**
+* truncateText('ALongText', 4) => 'ALon...'
+* @param {number} limit
+* @param {string} text
+* Each Chinese character is equal to two chars
+*/
+export default function truncateText(text: string, n: number) {
+  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
+}
\ No newline at end of file
diff --git a/dolphinscheduler-ui-next/src/utils/index.ts 
b/dolphinscheduler-ui-next/src/views/projects/index.tsx
similarity index 83%
copy from dolphinscheduler-ui-next/src/utils/index.ts
copy to dolphinscheduler-ui-next/src/views/projects/index.tsx
index efca1e8..e6769c4 100644
--- a/dolphinscheduler-ui-next/src/utils/index.ts
+++ b/dolphinscheduler-ui-next/src/views/projects/index.tsx
@@ -15,12 +15,13 @@
  * limitations under the License.
  */
 
-import mapping from './mapping'
-import regex from './regex'
+import { defineComponent } from 'vue'
 
-const utils = {
-  mapping,
-  regex
-}
-
-export default utils
+export default defineComponent({
+  name: "Projects",
+  setup() {
+    return () => (
+      <div>Projects</div>
+    )
+  }
+})
\ No newline at end of file
diff --git a/dolphinscheduler-ui-next/src/utils/index.ts 
b/dolphinscheduler-ui-next/src/views/projects/task/config.ts
similarity index 54%
copy from dolphinscheduler-ui-next/src/utils/index.ts
copy to dolphinscheduler-ui-next/src/views/projects/task/config.ts
index efca1e8..f19efb7 100644
--- a/dolphinscheduler-ui-next/src/utils/index.ts
+++ b/dolphinscheduler-ui-next/src/views/projects/task/config.ts
@@ -15,12 +15,53 @@
  * limitations under the License.
  */
 
-import mapping from './mapping'
-import regex from './regex'
-
-const utils = {
-  mapping,
-  regex
-}
-
-export default utils
+export const ALL_TASK_TYPES:any = {
+  SHELL: {
+    alias: 'SHELL',
+  },
+  SUB_PROCESS: {
+    alias: 'SUB_PROCESS',
+  },
+  PROCEDURE: {
+    alias: 'PROCEDURE',
+  },
+  SQL: {
+    alias: 'SQL',
+  },
+  SPARK: {
+    alias: 'SPARK',
+  },
+  FLINK: {
+    alias: 'FLINK',
+  },
+  MR: {
+    alias: 'MapReduce',
+  },
+  PYTHON: {
+    alias: 'PYTHON',
+  },
+  DEPENDENT: {
+    alias: 'DEPENDENT',
+  },
+  HTTP: {
+    alias: 'HTTP',
+  },
+  DATAX: {
+    alias: 'DataX',
+  },
+  PIGEON: {
+    alias: 'PIGEON',
+  },
+  SQOOP: {
+    alias: 'SQOOP',
+  },
+  CONDITIONS: {
+    alias: 'CONDITIONS',
+  },
+  SWITCH: {
+    alias: 'SWITCH',
+  },
+  SEATUNNEL: {
+    alias: 'WATERDROP',
+  }
+};
\ No newline at end of file
diff --git a/dolphinscheduler-ui-next/src/utils/index.ts 
b/dolphinscheduler-ui-next/src/views/projects/task/task-config.tsx
similarity index 82%
copy from dolphinscheduler-ui-next/src/utils/index.ts
copy to dolphinscheduler-ui-next/src/views/projects/task/task-config.tsx
index efca1e8..3ae6873 100644
--- a/dolphinscheduler-ui-next/src/utils/index.ts
+++ b/dolphinscheduler-ui-next/src/views/projects/task/task-config.tsx
@@ -15,12 +15,13 @@
  * limitations under the License.
  */
 
-import mapping from './mapping'
-import regex from './regex'
+import { defineComponent } from 'vue'
 
-const utils = {
-  mapping,
-  regex
-}
-
-export default utils
+export default defineComponent({
+  name: "TaskConfigModal",
+  setup() {
+    return () => (
+      <div>TaskConfigModal</div>
+    )
+  }
+})
\ No newline at end of file
diff --git 
a/dolphinscheduler-ui-next/src/views/projects/workflow/dag-canvas.tsx 
b/dolphinscheduler-ui-next/src/views/projects/workflow/dag-canvas.tsx
new file mode 100644
index 0000000..797c876
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/dag-canvas.tsx
@@ -0,0 +1,60 @@
+/*
+ * 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 { defineComponent, ref, inject } from 'vue'
+import Styles from './dag.module.scss'
+import type { PropType, Ref } from 'vue'
+import type { Dragged } from './dag'
+import { useCanvasInit, useGraphOperations, useCellActive, useCanvasDrop } 
from './dag-hooks';
+import { useRoute } from 'vue-router'
+
+const props = {
+  dragged: {
+    type: Object as PropType<Ref<Dragged>>,
+    default: ref({
+      x: 0,
+      y: 0,
+      type: ''
+    }),
+  }
+}
+
+export default defineComponent({
+  name: "workflow-dag-canvas",
+  props,
+  setup(props, context) {
+    const readonly = inject('readonly', ref(false));
+    const graph = inject('graph', ref());
+    const route = useRoute();
+    const projectCode = route.params.projectCode as string;
+
+    const { paper, minimap, container } = useCanvasInit({ readonly, graph });
+
+    // Change the style on cell hover and select
+    useCellActive({ graph });
+
+    // Drop sidebar item in canvas
+    const { onDrop, onDragenter, onDragover, onDragleave } = useCanvasDrop({ 
readonly, dragged: props.dragged, graph, container, projectCode });
+
+    return () => (
+      <div ref={container} class={Styles.canvas} onDrop={onDrop} 
onDragenter={onDragenter} onDragover={onDragover} onDragleave={onDragleave}>
+        <div ref={paper} class={Styles.paper}></div>
+        <div ref={minimap} class={Styles.minimap}></div>
+      </div>
+    );
+  }
+})
\ No newline at end of file
diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/dag-config.ts 
b/dolphinscheduler-ui-next/src/views/projects/workflow/dag-config.ts
new file mode 100644
index 0000000..3f8238f
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/dag-config.ts
@@ -0,0 +1,339 @@
+/*
+ * 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'
+
+const EDGE_COLOR = '#999999'
+const BG_BLUE = '#DFE9F7'
+const BG_WHITE = '#FFFFFF'
+const NODE_BORDER = '#CCCCCC'
+const TITLE = '#333333'
+const STROKE_BLUE = '#288FFF'
+const NODE_SHADOW = 'drop-shadow(3px 3px 4px rgba(0, 0, 0, 0.2))'
+const EDGE_SHADOW = 'drop-shadow(3px 3px 2px rgba(0, 0, 0, 0.2))'
+
+export const PORT = {
+  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: NODE_BORDER,
+          text: '+',
+          textAnchor: 'middle',
+          x: 0,
+          y: 3
+        },
+        'circle-outer': {
+          stroke: NODE_BORDER,
+          strokeWidth: 1,
+          r: 6,
+          fill: BG_WHITE
+        },
+        'circle-inner': {
+          r: 4,
+          fill: 'transparent'
+        }
+      }
+    }
+  }
+}
+
+export const PORT_HOVER = {
+  groups: {
+    [X6_PORT_OUT_NAME]: {
+      attrs: {
+        'circle-outer': {
+          stroke: STROKE_BLUE,
+          fill: BG_BLUE,
+          r: 8
+        },
+        'circle-inner': {
+          fill: STROKE_BLUE,
+          r: 6
+        }
+      }
+    }
+  }
+}
+
+export const PORT_SELECTED = {
+  groups: {
+    [X6_PORT_OUT_NAME]: {
+      attrs: {
+        'plus-text': {
+          fill: STROKE_BLUE
+        },
+        'circle-outer': {
+          stroke: STROKE_BLUE,
+          fill: BG_WHITE
+        }
+      }
+    }
+  }
+}
+
+export const NODE_STATUS_MARKUP = [{
+  tagName: 'foreignObject',
+  selector: 'fo',
+  children: [
+    {
+      tagName: 'body',
+      selector: 'fo-body',
+      ns: 'http://www.w3.org/1999/xhtml',
+      children: [{
+        tagName: 'div',
+        selector: 'status'
+      }]
+    }
+  ]
+}]
+
+export const NODE = {
+  width: 220,
+  height: 48,
+  markup: [
+    {
+      tagName: 'rect',
+      selector: 'body',
+      className: 'dag-task-body'
+    },
+    {
+      tagName: 'image',
+      selector: 'image'
+    },
+    {
+      tagName: 'text',
+      selector: 'title'
+    }
+  ],
+  attrs: {
+    body: {
+      refWidth: '100%',
+      refHeight: '100%',
+      rx: 6,
+      ry: 6,
+      pointerEvents: 'visiblePainted',
+      fill: BG_WHITE,
+      stroke: NODE_BORDER,
+      strokeWidth: 1,
+      strokeDasharray: 'none',
+      filter: 'none'
+    },
+    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
+    }
+  },
+  ports: {
+    ...PORT,
+    items: [
+      {
+        id: X6_PORT_OUT_NAME,
+        group: X6_PORT_OUT_NAME
+      }
+    ]
+  }
+}
+
+export const NODE_HOVER = {
+  attrs: {
+    body: {
+      fill: BG_BLUE,
+      stroke: STROKE_BLUE,
+      strokeDasharray: '5,2'
+    },
+    title: {
+      fill: STROKE_BLUE
+    }
+  }
+}
+
+export const NODE_SELECTED = {
+  attrs: {
+    body: {
+      filter: NODE_SHADOW,
+      fill: BG_WHITE,
+      stroke: STROKE_BLUE,
+      strokeDasharray: '5,2',
+      strokeWidth: '1.5'
+    },
+    title: {
+      fill: STROKE_BLUE
+    }
+  }
+}
+
+export const EDGE = {
+  attrs: {
+    line: {
+      stroke: EDGE_COLOR,
+      strokeWidth: 1,
+      targetMarker: {
+        tagName: 'path',
+        fill: EDGE_COLOR,
+        strokeWidth: 0,
+        d: 'M 6 -3 0 0 6 3 Z'
+      },
+      filter: 'none'
+    }
+  },
+  connector: {
+    name: 'rounded'
+  },
+  router: {
+    name: 'manhattan',
+    args: {
+      endDirections: ['top', 'bottom', 'left']
+    }
+  },
+  defaultLabel: {
+    markup: [
+      {
+        tagName: 'rect',
+        selector: 'body'
+      },
+      {
+        tagName: 'text',
+        selector: 'label'
+      }
+    ],
+    attrs: {
+      label: {
+        fill: EDGE_COLOR,
+        fontSize: 14,
+        textAnchor: 'middle',
+        textVerticalAnchor: 'middle',
+        pointerEvents: 'none'
+      },
+      body: {
+        ref: 'label',
+        fill: BG_WHITE,
+        stroke: EDGE_COLOR,
+        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_HOVER = {
+  attrs: {
+    line: {
+      stroke: STROKE_BLUE,
+      targetMarker: {
+        fill: STROKE_BLUE
+      }
+    }
+  },
+  defaultLabel: {
+    attrs: {
+      label: {
+        fill: STROKE_BLUE
+      },
+      body: {
+        fill: BG_WHITE,
+        stroke: STROKE_BLUE
+      }
+    }
+  }
+}
+
+export const EDGE_SELECTED = {
+  attrs: {
+    line: {
+      stroke: STROKE_BLUE,
+      targetMarker: {
+        fill: STROKE_BLUE
+      },
+      strokeWidth: 2,
+      filter: EDGE_SHADOW
+    }
+  },
+  defaultLabel: {
+    attrs: {
+      label: {
+        fill: STROKE_BLUE
+      },
+      body: {
+        fill: BG_WHITE,
+        stroke: STROKE_BLUE
+      }
+    }
+  }
+}
diff --git a/dolphinscheduler-ui-next/src/utils/index.ts 
b/dolphinscheduler-ui-next/src/views/projects/workflow/dag-hooks.ts
similarity index 64%
copy from dolphinscheduler-ui-next/src/utils/index.ts
copy to dolphinscheduler-ui-next/src/views/projects/workflow/dag-hooks.ts
index efca1e8..0823ad3 100644
--- a/dolphinscheduler-ui-next/src/utils/index.ts
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/dag-hooks.ts
@@ -15,12 +15,18 @@
  * limitations under the License.
  */
 
-import mapping from './mapping'
-import regex from './regex'
+import { useCanvasInit } from './use-canvas-init';
+import { useGraphOperations } from './use-graph-operations';
+import { useCellActive } from './use-cell-active';
+import { useSidebarDrag } from './use-sidebar-drag';
+import { useCanvasDrop } from './use-canvas-drop';
+import { useNodeSearch } from './use-node-search';
 
-const utils = {
-  mapping,
-  regex
-}
-
-export default utils
+export {
+  useCanvasInit,
+  useGraphOperations,
+  useCellActive,
+  useSidebarDrag,
+  useCanvasDrop,
+  useNodeSearch,
+}
\ No newline at end of file
diff --git 
a/dolphinscheduler-ui-next/src/views/projects/workflow/dag-sidebar.tsx 
b/dolphinscheduler-ui-next/src/views/projects/workflow/dag-sidebar.tsx
new file mode 100644
index 0000000..c1c6b3d
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/dag-sidebar.tsx
@@ -0,0 +1,68 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type { PropType, Ref } from 'vue';
+import type { Dragged } from './dag';
+import { defineComponent, ref, inject } from 'vue'
+import { ALL_TASK_TYPES } from '../task/config';
+import { useSidebarDrag } from './dag-hooks';
+import Styles from './dag.module.scss';
+
+const props = {
+  dragged: {
+    type: Object as PropType<Ref<Dragged>>,
+    default: ref({
+      x: 0,
+      y: 0,
+      type: ''
+    }),
+  },
+}
+
+export default defineComponent({
+  name: "workflow-dag-sidebar",
+  props,
+  setup(props) {
+    const readonly = inject('readonly', ref(false))
+    const dragged = props.dragged;
+    const { onDragStart } = useSidebarDrag({
+      readonly,
+      dragged
+    });
+    const allTaskTypes = Object.keys(ALL_TASK_TYPES).map(type => ({
+      type,
+      ...ALL_TASK_TYPES[type]
+    }));
+
+    return () => (
+      <div class={Styles.sidebar}>
+        {
+          allTaskTypes.map(task => (
+            <div
+              class={Styles.draggable}
+              draggable="true"
+              onDragstart={(e) => onDragStart(e, task.type)}
+            >
+              <em class={`${Styles['sidebar-icon']} ${Styles['icon-' + 
task.type.toLocaleLowerCase()]}`}></em>
+              <span>{task.alias}</span>
+            </div>
+          ))
+        }
+      </div>
+    )
+  }
+})
\ No newline at end of file
diff --git 
a/dolphinscheduler-ui-next/src/views/projects/workflow/dag-toolbar.tsx 
b/dolphinscheduler-ui-next/src/views/projects/workflow/dag-toolbar.tsx
new file mode 100644
index 0000000..599664c
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/dag-toolbar.tsx
@@ -0,0 +1,180 @@
+/*
+ * 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 { defineComponent, ref, inject } from 'vue'
+import { useI18n } from 'vue-i18n'
+import Styles from './dag.module.scss'
+import { NTooltip, NIcon, NButton, NSelect } from 'naive-ui';
+import { SearchOutlined, DownloadOutlined, FullscreenOutlined, 
FullscreenExitOutlined, InfoCircleOutlined, FormatPainterOutlined } from 
'@vicons/antd';
+import { useNodeSearch } from './dag-hooks';
+import { DataUri } from '@antv/x6'
+import { useFullscreen } from '@vueuse/core';
+import { useRouter } from 'vue-router';
+
+export default defineComponent({
+  name: "workflow-dag-toolbar",
+  setup(props, context) {
+    const { t } = useI18n();
+    const graph = inject('graph', ref());
+    const router = useRouter();
+
+    /**
+     * Node search and navigate
+     */
+    const {
+      searchNode,
+      getAllNodes,
+      allNodes,
+      toggleSearchInput,
+      searchInputVisible
+    } = useNodeSearch({ graph });
+
+    /**
+     * Download Workflow Image
+     * @param {string} fileName
+     * @param {string} bgColor 
+     */
+    const downloadPNG = (options = { fileName: 'dag', bgColor: '#f2f3f7' }) => 
{
+      const { fileName, bgColor } = options;
+      graph.value?.toPNG(
+        (dataUri: string) => {
+          DataUri.downloadDataUri(dataUri, `${fileName}.png`)
+        },
+        {
+          padding: {
+            top: 50,
+            right: 50,
+            bottom: 50,
+            left: 50
+          },
+          backgroundColor: bgColor
+        }
+      )
+    }
+
+    /**
+     * Toggle fullscreen
+     */
+    const { isFullscreen, toggle } = useFullscreen();
+
+
+    /**
+     * Open workflow version modal
+     */
+    const openVersionModal = () => {
+      //TODO, same as the version popup in the workflow list page
+    }
+
+    /**
+     * Open DAG format modal
+     */
+    const openDagFormatModal = () => {
+
+    }
+
+    const onClose = () => {
+      router.go(-1)
+    }
+
+    return () => (
+      <div class={Styles.toolbar}>
+        <span 
class={Styles['workflow-name']}>{t("project.dag.createWorkflow")}</span>
+        <div class={Styles['toolbar-right-part']}>
+          {/* Search node */}
+          <NTooltip v-slots={{
+            trigger: () => (
+              <NButton class={Styles['toolbar-right-item']} strong secondary 
circle type="info" onClick={toggleSearchInput} v-slots={{
+                icon: () => (
+                  <NIcon>
+                    <SearchOutlined />
+                  </NIcon>
+                )
+              }} />
+            ),
+            default: () => t('project.dag.search')
+          }}>
+          </NTooltip>
+          <div
+            class={`${Styles['toolbar-right-item']} ${Styles['node-selector']} 
${searchInputVisible.value ? Styles['visible'] : ''}`}
+          >
+            <NSelect size="small" options={allNodes.value} 
onFocus={getAllNodes} onUpdateValue={searchNode} filterable />
+          </div>
+          {/* Download workflow PNG */}
+          <NTooltip v-slots={{
+            trigger: () => (
+              <NButton class={Styles['toolbar-right-item']} strong secondary 
circle type="info" onClick={() => downloadPNG()} v-slots={{
+                icon: () => (
+                  <NIcon>
+                    <DownloadOutlined />
+                  </NIcon>
+                )
+              }} />
+            ),
+            default: () => t('project.dag.download_png')
+          }}>
+          </NTooltip>
+          {/* Toggle fullscreen */}
+          <NTooltip v-slots={{
+            trigger: () => (
+              <NButton class={Styles['toolbar-right-item']} strong secondary 
circle type="info" onClick={toggle} v-slots={{
+                icon: () => (
+                  <NIcon>
+                    {isFullscreen.value ? <FullscreenExitOutlined /> : 
<FullscreenOutlined />}
+                  </NIcon>
+                )
+              }} />
+            ),
+            default: () => isFullscreen.value ? 
t('project.dag.fullscreen_close') : t('project.dag.fullscreen_open')
+          }}>
+          </NTooltip>
+          {/* DAG Format */}
+          <NTooltip v-slots={{
+            trigger: () => (
+              <NButton class={Styles['toolbar-right-item']} strong secondary 
circle type="info" onClick={openDagFormatModal} v-slots={{
+                icon: () => (
+                  <NIcon>
+                    <FormatPainterOutlined />
+                  </NIcon>
+                )
+              }} />
+            ),
+            default: () => t('project.dag.format')
+          }}>
+          </NTooltip>
+          {/* Version info */}
+          <NTooltip v-slots={{
+            trigger: () => (
+              <NButton class={Styles['toolbar-right-item']} strong secondary 
circle type="info" onClick={openVersionModal} v-slots={{
+                icon: () => (
+                  <NIcon>
+                    <InfoCircleOutlined />
+                  </NIcon>
+                )
+              }} />
+            ),
+            default: () => t('project.dag.workflow_version')
+          }}>
+          </NTooltip>
+          {/* Save workflow */}
+          <NButton class={Styles['toolbar-right-item']} type="info" secondary 
round>{t('project.dag.save')}</NButton>
+          {/* Return to previous page */}
+          <NButton secondary round 
onClick={onClose}>{t('project.dag.close')}</NButton>
+        </div>
+      </div>
+    )
+  }
+})
\ No newline at end of file
diff --git 
a/dolphinscheduler-ui-next/src/views/projects/workflow/dag.module.scss 
b/dolphinscheduler-ui-next/src/views/projects/workflow/dag.module.scss
new file mode 100644
index 0000000..2298363
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/dag.module.scss
@@ -0,0 +1,217 @@
+/*
+ * 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.
+ */
+
+$blue: #288fff;
+$blueBg: rgba(40, 143, 255, 0.1);
+$toolbarHeight: 50px;
+
+.dag {
+  height: 100%;
+}
+
+.content {
+  display: flex;
+  height: calc(100% - $toolbarHeight - 20px);
+  margin-top: 20px;
+}
+
+.toolbar {
+  height: $toolbarHeight;
+  display: flex;
+  align-items: center;
+  padding: 0 20px;
+  border: 1px solid var(--n-border-color);
+  border-radius: 4px;
+  justify-content: space-between;
+}
+
+.canvas {
+  width: 100%;
+  height: 100%;
+  position: relative;
+  overflow: hidden;
+  display: flex;
+}
+
+.paper {
+  width: 100%;
+  height: 100%;
+}
+
+.sidebar {
+  width: 190px;
+  height: 100%;
+  margin-right: 20px;
+}
+
+.workflow-name {
+  font-size: 14px;
+}
+
+.draggable {
+  display: flex;
+  width: 100%;
+  height: 32px;
+  margin-bottom: 10px;
+  align-items: center;
+  border: 1px solid var(--n-border-color);
+  padding: 0 10px;
+  border-radius: 4px;
+  transform: translate(0, 0);
+  box-sizing: border-box;
+  cursor: move;
+  font-size: 12px;
+
+  .sidebar-icon {
+    display: block;
+    width: 18px;
+    height: 18px;
+    background-size: 100% 100%;
+    margin-right: 10px;
+    &.icon-shell {
+      background-image: url("../../../assets/images/task-icons/shell.png");
+    }
+    &.icon-sub_process {
+      background-image: 
url("../../../assets/images/task-icons/sub_process.png");
+    }
+    &.icon-procedure {
+      background-image: url("../../../assets/images/task-icons/procedure.png");
+    }
+    &.icon-sql {
+      background-image: url("../../../assets/images/task-icons/sql.png");
+    }
+    &.icon-flink {
+      background-image: url("../../../assets/images/task-icons/flink.png");
+    }
+    &.icon-mr {
+      background-image: url("../../../assets/images/task-icons/mr.png");
+    }
+    &.icon-python {
+      background-image: url("../../../assets/images/task-icons/python.png");
+    }
+    &.icon-dependent {
+      background-image: url("../../../assets/images/task-icons/dependent.png");
+    }
+    &.icon-http {
+      background-image: url("../../../assets/images/task-icons/http.png");
+    }
+    &.icon-datax {
+      background-image: url("../../../assets/images/task-icons/datax.png");
+    }
+    &.icon-pigeon {
+      background-image: url("../../../assets/images/task-icons/pigeon.png");
+    }
+    &.icon-sqoop {
+      background-image: url("../../../assets/images/task-icons/sqoop.png");
+    }
+    &.icon-conditions {
+      background-image: 
url("../../../assets/images/task-icons/conditions.png");
+    }
+    &.icon-seatunnel {
+      background-image: url("../../../assets/images/task-icons/seatunnel.png");
+    }
+    &.icon-spark {
+      background-image: url("../../../assets/images/task-icons/spark.png");
+    }
+    &.icon-switch {
+      background-image: url("../../../assets/images/task-icons/switch.png");
+    }
+  }
+
+  &:hover {
+    color: $blue;
+    border: 1px dashed $blue;
+    background-color: $blueBg;
+    .sidebar-icon {
+      &.icon-shell {
+        background-image: 
url("../../../assets/images/task-icons/shell_hover.png");
+      }
+      &.icon-sub_process {
+        background-image: 
url("../../../assets/images/task-icons/sub_process_hover.png");
+      }
+      &.icon-procedure {
+        background-image: 
url("../../../assets/images/task-icons/procedure_hover.png");
+      }
+      &.icon-sql {
+        background-image: 
url("../../../assets/images/task-icons/sql_hover.png");
+      }
+      &.icon-flink {
+        background-image: 
url("../../../assets/images/task-icons/flink_hover.png");
+      }
+      &.icon-mr {
+        background-image: 
url("../../../assets/images/task-icons/mr_hover.png");
+      }
+      &.icon-python {
+        background-image: 
url("../../../assets/images/task-icons/python_hover.png");
+      }
+      &.icon-dependent {
+        background-image: 
url("../../../assets/images/task-icons/dependent_hover.png");
+      }
+      &.icon-http {
+        background-image: 
url("../../../assets/images/task-icons/http_hover.png");
+      }
+      &.icon-datax {
+        background-image: 
url("../../../assets/images/task-icons/datax_hover.png");
+      }
+      &.icon-pigeon {
+        background-image: 
url("../../../assets/images/task-icons/pigeon_hover.png");
+      }
+      &.icon-sqoop {
+        background-image: 
url("../../../assets/images/task-icons/sqoop_hover.png");
+      }
+      &.icon-conditions {
+        background-image: 
url("../../../assets/images/task-icons/conditions_hover.png");
+      }
+      &.icon-seatunnel {
+        background-image: 
url("../../../assets/images/task-icons/seatunnel_hover.png");
+      }
+      &.icon-spark {
+        background-image: 
url("../../../assets/images/task-icons/spark_hover.png");
+      }
+      &.icon-switch {
+        background-image: 
url("../../../assets/images/task-icons/switch_hover.png");
+      }
+    }
+  }
+}
+
+.minimap {
+  position: absolute;
+  right: 0px;
+  bottom: 0px;
+  border: dashed 1px #e4e4e4;
+  z-index: 9;
+}
+
+.toolbar-right-part {
+  display: flex;
+  align-items: center;
+  .toolbar-right-item {
+    margin-right: 10px;
+  }
+  .node-selector {
+    width: 0;
+    overflow: hidden;
+    transition: all 0.5s;
+    margin-right: 0;
+
+    &.visible {
+      width: 200px;
+      margin-right: 10px;
+    }
+  }
+}
diff --git a/dolphinscheduler-ui-next/src/views/projects/workflow/dag.tsx 
b/dolphinscheduler-ui-next/src/views/projects/workflow/dag.tsx
new file mode 100644
index 0000000..2f0ce4a
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/dag.tsx
@@ -0,0 +1,67 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type { Graph } from '@antv/x6';
+import { defineComponent, ref, provide } from 'vue'
+import DagToolbar from './dag-toolbar';
+import DagCanvas from './dag-canvas';
+import DagSidebar from './dag-sidebar';
+import Styles from './dag.module.scss';
+import "./x6-style.scss";
+
+
+export interface Dragged {
+  x: number;
+  y: number;
+  type: string;
+}
+
+export default defineComponent({
+  name: "workflow-dag",
+  setup(props, context) {
+
+    // Whether the graph can be operated
+    const readonly = ref(false);
+    provide('readonly', readonly);
+
+    const graph = ref<Graph>();
+    provide('graph', graph);
+
+    // The sidebar slots
+    const toolbarSlots = {
+      left: context.slots.toolbarLeft,
+      right: context.slots.toolbarRight
+    }
+
+    // The element currently being dragged up
+    const dragged = ref<Dragged>({
+      x: 0,
+      y: 0,
+      type: ''
+    });
+
+    return () => (
+      <div class={Styles.dag}>
+        <DagToolbar v-slots={toolbarSlots} />
+        <div class={Styles.content}>
+          <DagSidebar dragged={dragged} />
+          <DagCanvas dragged={dragged} />
+        </div>
+      </div>
+    )
+  }
+})
\ No newline at end of file
diff --git a/dolphinscheduler-ui-next/src/utils/index.ts 
b/dolphinscheduler-ui-next/src/views/projects/workflow/hook-demo.ts
similarity index 66%
copy from dolphinscheduler-ui-next/src/utils/index.ts
copy to dolphinscheduler-ui-next/src/views/projects/workflow/hook-demo.ts
index efca1e8..0e5f0e0 100644
--- a/dolphinscheduler-ui-next/src/utils/index.ts
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/hook-demo.ts
@@ -15,12 +15,26 @@
  * limitations under the License.
  */
 
-import mapping from './mapping'
-import regex from './regex'
+import { ref, onMounted, Ref, onUnmounted } from 'vue'
 
-const utils = {
-  mapping,
-  regex
+interface Options {
+  // readonly: Ref<boolean>;
+  // canvas: Ref<HTMLElement | undefined>;
 }
 
-export default utils
+/**
+ * Canvas Init
+ * 1. Bind the graph to the dom
+ * 2. Redraw when the page is resized
+ * 3. Register custom graphics
+ */
+export function useCanvasInit(options: Options) {
+
+  // Whether the graph can be operated
+  const { } = options;
+
+
+  return {
+
+  }
+}
\ No newline at end of file
diff --git 
a/dolphinscheduler-ui-next/src/views/projects/workflow/use-canvas-drop.ts 
b/dolphinscheduler-ui-next/src/views/projects/workflow/use-canvas-drop.ts
new file mode 100644
index 0000000..32fb0d0
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/use-canvas-drop.ts
@@ -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.
+ */
+
+import type { Ref } from 'vue';
+import type { Graph } from '@antv/x6'
+import type { Dragged } from './dag'
+import { genTaskCodeList } from '@/service/modules/task-definition';
+import { useGraphOperations } from './dag-hooks';
+
+interface Options {
+  readonly: Ref<boolean>;
+  graph: Ref<Graph | undefined>;
+  container: Ref<HTMLElement | undefined>;
+  dragged: Ref<Dragged>;
+  projectCode: string;
+}
+
+/**
+ * Drop sidebar item in canvas
+ */
+export function useCanvasDrop(options: Options) {
+
+  const { readonly, graph, container, dragged, projectCode } = options;
+
+  const { addNode } = useGraphOperations({ graph });
+
+  const onDrop = (e: DragEvent) => {
+    e.stopPropagation();
+    e.preventDefault();
+    if (readonly.value) {
+      return;
+    }
+    if (dragged.value && graph.value && container.value && projectCode) {
+      const { type, x: eX, y: eY } = dragged.value;
+      const { x, y } = graph.value.clientToLocal(e.clientX, e.clientY);
+      const genNums = 1;
+      genTaskCodeList(genNums, Number(projectCode))
+        .then((res) => {
+          const [code] = res
+          addNode(code + '', type, { x: x - eX, y: y - eY })
+          // openTaskConfigModel(code, type)
+        })
+    }
+  }
+
+  const preventDefault = (e: DragEvent) => {
+    e.preventDefault();
+  }
+
+  return {
+    onDrop,
+    onDragenter: preventDefault,
+    onDragover: preventDefault,
+    onDragleave: preventDefault,
+  }
+}
\ No newline at end of file
diff --git 
a/dolphinscheduler-ui-next/src/views/projects/workflow/use-canvas-init.ts 
b/dolphinscheduler-ui-next/src/views/projects/workflow/use-canvas-init.ts
new file mode 100644
index 0000000..16c4479
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/use-canvas-init.ts
@@ -0,0 +1,177 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type { Node } from '@antv/x6';
+import { ref, onMounted, Ref, onUnmounted } from 'vue'
+import { Graph } from '@antv/x6'
+import {
+  NODE,
+  EDGE,
+  X6_NODE_NAME,
+  X6_EDGE_NAME,
+} from './dag-config'
+import { debounce } from 'lodash';
+
+interface Options {
+  readonly: Ref<boolean>;
+  graph: Ref<Graph | undefined>;
+}
+
+/**
+ * Canvas Init
+ * 1. Bind the graph to the dom
+ * 2. Redraw when the page is resized
+ * 3. Register custom graphics
+ */
+export function useCanvasInit(options: Options) {
+
+  // Whether the graph can be operated
+  const { readonly, graph } = options;
+
+  const paper = ref<HTMLElement>(); // The graph mount HTMLElement
+  const minimap = ref<HTMLElement>(); // The minimap mount HTMLElement
+  const container = ref<HTMLElement>(); // The container of paper and minimap
+
+  /**
+   * Graph Init, bind graph to the dom
+   */
+  function graphInit() {
+    return new Graph({
+      container: paper.value,
+      selecting: {
+        enabled: true,
+        multiple: true,
+        rubberband: true,
+        rubberEdge: true,
+        movable: true,
+        showNodeSelectionBox: false
+      },
+      scaling: {
+        min: 0.2,
+        max: 2
+      },
+      mousewheel: {
+        enabled: true,
+        modifiers: ['ctrl', 'meta']
+      },
+      scroller: true,
+      grid: {
+        size: 10,
+        visible: true
+      },
+      snapline: true,
+      minimap: {
+        enabled: true,
+        container: minimap.value,
+        scalable: false,
+        width: 200,
+        height: 120
+      },
+      interacting: {
+        edgeLabelMovable: false,
+        nodeMovable: !readonly.value,
+        magnetConnectable: !readonly.value
+      },
+      connecting: {
+        // 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: false,
+        // Whether all available ports or nodes are highlighted when you drag 
the edge
+        highlight: true,
+        createEdge() {
+          return graph.value?.createEdge({ shape: X6_EDGE_NAME })
+        }
+      },
+      highlighting: {
+        nodeAvailable: {
+          name: 'className',
+          args: {
+            className: 'available'
+          }
+        },
+        magnetAvailable: {
+          name: 'className',
+          args: {
+            className: 'available'
+          }
+        },
+        magnetAdsorbed: {
+          name: 'className',
+          args: {
+            className: 'adsorbed'
+          }
+        }
+      }
+    })
+  }
+
+  onMounted(() => {
+    graph.value = graphInit();
+    // Make sure the edge starts with node, not port
+    graph.value.on('edge:connected', ({ isNew, edge }) => {
+      if (isNew) {
+        const sourceNode = edge.getSourceNode() as Node
+        edge.setSource(sourceNode)
+      }
+    })
+  })
+
+  /**
+   * Redraw when the page is resized
+   */
+  const paperResize = debounce(() => {
+    if (!container.value) return;
+    const w = container.value.offsetWidth
+    const h = container.value.offsetHeight
+    graph.value?.resize(w, h);
+  }, 200)
+  onMounted(() => {
+    window.addEventListener('resize', paperResize)
+  })
+  onUnmounted(() => {
+    window.removeEventListener('resize', paperResize)
+  })
+
+  /**
+   * Register custom cells
+   */
+  function registerCustomCells() {
+    Graph.unregisterNode(X6_NODE_NAME)
+    Graph.unregisterEdge(X6_EDGE_NAME)
+    Graph.registerNode(X6_NODE_NAME, { ...NODE })
+    Graph.registerEdge(X6_EDGE_NAME, { ...EDGE })
+  }
+  onMounted(() => {
+    registerCustomCells()
+  })
+
+  return {
+    graph,
+    paper,
+    minimap,
+    container
+  }
+}
\ No newline at end of file
diff --git 
a/dolphinscheduler-ui-next/src/views/projects/workflow/use-cell-active.ts 
b/dolphinscheduler-ui-next/src/views/projects/workflow/use-cell-active.ts
new file mode 100644
index 0000000..341f3d0
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/use-cell-active.ts
@@ -0,0 +1,154 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type { Ref } from 'vue'
+import { onMounted, ref } from 'vue';
+import type { Node, Graph, Edge, Cell } from '@antv/x6'
+import _ from 'lodash';
+import {
+  X6_PORT_OUT_NAME,
+  PORT_HOVER,
+  PORT_SELECTED,
+  PORT,
+  NODE,
+  NODE_HOVER,
+  NODE_SELECTED,
+  EDGE,
+  EDGE_SELECTED,
+  EDGE_HOVER
+} from './dag-config';
+
+interface Options {
+  graph: Ref<Graph | undefined>
+}
+
+/**
+ * Change the style on cell hover and select
+ */
+export function useCellActive(options: Options) {
+
+  const { graph } = options;
+  const hoverCell = ref();
+
+  const isStatusIcon = (tagName: string) => {
+    if (!tagName) return false;
+    return tagName.toLocaleLowerCase() === 'em' || tagName.toLocaleLowerCase() 
=== 'body'
+  }
+
+  function setEdgeStyle(edge: Edge) {
+    const isHover = edge === hoverCell.value;
+    const isSelected = graph.value?.isSelected(edge)
+    // TODO
+    // const labelName = this.getEdgeLabelName ? this.getEdgeLabelName(edge) : 
''
+    let edgeProps = null
+
+    if (isHover) {
+      edgeProps = _.merge(_.cloneDeep(EDGE), EDGE_HOVER)
+    } else if (isSelected) {
+      edgeProps = _.merge(_.cloneDeep(EDGE), EDGE_SELECTED)
+    } else {
+      edgeProps = _.cloneDeep(EDGE)
+    }
+
+    edge.setAttrs(edgeProps.attrs)
+    edge.setLabels([
+      {
+        ..._.merge(
+          {
+            attrs: _.cloneDeep(edgeProps.defaultLabel.attrs)
+          },
+          // {
+          //   attrs: { label: { text: labelName } }
+          // }
+        )
+      }
+    ])
+  }
+
+  function setNodeStyle(node: Node) {
+    const isHover = node === hoverCell.value
+    const isSelected = graph.value?.isSelected(node)
+    const portHover = _.cloneDeep(PORT_HOVER.groups[X6_PORT_OUT_NAME].attrs)
+    const portSelected = 
_.cloneDeep(PORT_SELECTED.groups[X6_PORT_OUT_NAME].attrs)
+    const portDefault = _.cloneDeep(PORT.groups[X6_PORT_OUT_NAME].attrs)
+    const nodeHover = _.merge(_.cloneDeep(NODE.attrs), NODE_HOVER.attrs)
+    const nodeSelected = _.merge(_.cloneDeep(NODE.attrs), NODE_SELECTED.attrs)
+
+    let img = null
+    let nodeAttrs = null
+    let portAttrs = null
+
+    if (isHover || isSelected) {
+      img = 
`/src/assets/images/task-icons/${node.data.taskType.toLocaleLowerCase()}_hover.png`
+      if (isHover) {
+        nodeAttrs = nodeHover
+        portAttrs = _.merge(portDefault, portHover)
+      } else {
+        nodeAttrs = nodeSelected
+        portAttrs = _.merge(portDefault, portSelected)
+      }
+    } else {
+      img = 
`/src/assets/images/task-icons/${node.data.taskType.toLocaleLowerCase()}.png`
+      nodeAttrs = NODE.attrs
+      portAttrs = portDefault
+    }
+    node.setAttrByPath('image/xlink:href', img)
+    node.setAttrs(nodeAttrs)
+    node.setPortProp(
+      X6_PORT_OUT_NAME,
+      'attrs',
+      portAttrs
+    )
+  }
+
+  function updateCellStyle(cell: Cell) {
+    if (cell.isEdge()) {
+      setEdgeStyle(cell)
+    } else if (cell.isNode()) {
+      setNodeStyle(cell)
+    }
+  }
+
+  onMounted(() => {
+    if (graph.value) {
+      // hover
+      graph.value.on('cell:mouseenter', (data) => {
+        const { cell, e } = data
+        if (!isStatusIcon(e.target.tagName)) {
+          hoverCell.value = cell
+          updateCellStyle(cell)
+        }
+      })
+      graph.value.on('cell:mouseleave', ({ cell }) => {
+        hoverCell.value = undefined
+        updateCellStyle(cell)
+      })
+
+      // select
+      graph.value.on('cell:selected', ({ cell }) => {
+        updateCellStyle(cell)
+      })
+      graph.value.on('cell:unselected', ({ cell }) => {
+        updateCellStyle(cell)
+      })
+    }
+  })
+
+  return {
+    hoverCell
+  }
+}
\ No newline at end of file
diff --git 
a/dolphinscheduler-ui-next/src/views/projects/workflow/use-graph-operations.ts 
b/dolphinscheduler-ui-next/src/views/projects/workflow/use-graph-operations.ts
new file mode 100644
index 0000000..c48eb6a
--- /dev/null
+++ 
b/dolphinscheduler-ui-next/src/views/projects/workflow/use-graph-operations.ts
@@ -0,0 +1,157 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type { Ref } from 'vue'
+import type { Node, Graph, Edge } from '@antv/x6'
+import {
+  X6_NODE_NAME,
+  X6_EDGE_NAME,
+} from './dag-config'
+import { ALL_TASK_TYPES } from '../task/config';
+import utils from '@/utils';
+
+interface Options {
+  graph: Ref<Graph | undefined>
+}
+
+type Coordinate = { x: number; y: number; }
+
+/**
+ * Expose some graph operation methods
+ * @param {Options} options
+ */
+export function useGraphOperations(options: Options) {
+
+  const { graph } = options;
+
+
+  /**
+   * Build edge metadata
+   * @param {string} sourceId
+   * @param {string} targetId
+   * @param {string} label
+   */
+  function buildEdgeMetadata(sourceId: string, targetId: string, label: string 
= ''): Edge.Metadata {
+    return {
+      shape: X6_EDGE_NAME,
+      source: {
+        cell: sourceId
+      },
+      target: {
+        cell: targetId
+      },
+      labels: label ? [label] : undefined
+    }
+  }
+
+  /**
+   * Build node metadata
+   * @param {string} id
+   * @param {string} taskType
+   * @param {Coordinate} coordinate Default is { x: 100, y: 100 }
+   */
+  function buildNodeMetadata(id: string, type: string, taskName: string, 
coordinate: Coordinate = { x: 100, y: 100 }): Node.Metadata {
+    const truncation = taskName ? utils.truncateText(taskName, 18) : id;
+    return {
+      id: id,
+      shape: X6_NODE_NAME,
+      x: coordinate.x,
+      y: coordinate.y,
+      data: {
+        taskType: type,
+        taskName: taskName || id
+      },
+      attrs: {
+        image: {
+          // Use href instead of xlink:href, you may lose the icon when 
downloadPNG
+          'xlink:href': 
`/src/assets/images/task-icons/${type.toLocaleLowerCase()}.png`
+        },
+        title: {
+          text: truncation
+        }
+      }
+    }
+  }
+
+  /**
+   * Add a node to the graph
+   * @param {string} id
+   * @param {string} taskType
+   * @param {Coordinate} coordinate Default is { x: 100, y: 100 }
+   */
+  function addNode(id: string, type: string, coordinate: Coordinate = { x: 
100, y: 100 }) {
+    if (!ALL_TASK_TYPES[type]) {
+      console.warn(`taskType:${type} is invalid!`)
+      return
+    }
+    const node = buildNodeMetadata(id, type, '', coordinate)
+    graph.value?.addNode(node)
+  }
+
+  /**
+   * Set node name by id
+   * @param {string} id
+   * @param {string} name
+   */
+  function setNodeName(id: string, newName: string) {
+    const node = graph.value?.getCellById(id)
+    if (node) {
+      const truncation = utils.truncateText(newName, 18)
+      node.attr('title/text', truncation)
+      node.setData({ taskName: newName })
+    }
+  }
+
+  /**
+   * Get nodes
+   */
+  function getNodes() {
+    const nodes = graph.value?.getNodes()
+    if (!nodes) return []
+    return nodes.map((node) => {
+      const position = node.getPosition()
+      const data = node.getData()
+      return {
+        code: node.id,
+        position: position,
+        name: data.taskName,
+        type: data.taskType
+      }
+    })
+  }
+
+  /**
+   * Navigate to cell
+   * @param {string} code
+   */
+  function navigateTo(code: string) {
+    if (!graph.value) return;
+    const cell = graph.value.getCellById(code)
+    graph.value.scrollToCell(cell, { animation: { duration: 600 } })
+    graph.value.cleanSelection()
+    graph.value.select(cell)
+  };
+
+  return {
+    buildEdgeMetadata,
+    buildNodeMetadata,
+    addNode,
+    setNodeName,
+    getNodes,
+    navigateTo,
+  }
+}
\ No newline at end of file
diff --git 
a/dolphinscheduler-ui-next/src/views/projects/workflow/use-node-search.ts 
b/dolphinscheduler-ui-next/src/views/projects/workflow/use-node-search.ts
new file mode 100644
index 0000000..0b26f30
--- /dev/null
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/use-node-search.ts
@@ -0,0 +1,60 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import type { Graph } from '@antv/x6';
+import { ref, Ref } from 'vue'
+import { useGraphOperations } from './dag-hooks';
+
+
+interface Options {
+  graph: Ref<Graph | undefined>;
+}
+
+/**
+ * Node search and navigate
+ */
+export function useNodeSearch(options: Options) {
+
+  const { graph } = options;
+
+  const searchInputVisible = ref(false);
+  const allNodes = ref<any>([]);
+  const toggleSearchInput = () => {
+    searchInputVisible.value = !searchInputVisible.value;
+  }
+  const { getNodes, navigateTo } = useGraphOperations({ graph });
+  const searchNode = (val: string) => {
+    navigateTo(val)
+  }
+  const getAllNodes = () => {
+    const nodes = getNodes();
+    allNodes.value = nodes.map(node => {
+      return {
+        label: node.name,
+        value: node.code
+      }
+    })
+  }
+
+  return {
+    searchNode,
+    getAllNodes,
+    allNodes,
+    toggleSearchInput,
+    searchInputVisible,
+  }
+}
\ No newline at end of file
diff --git a/dolphinscheduler-ui-next/src/utils/index.ts 
b/dolphinscheduler-ui-next/src/views/projects/workflow/use-sidebar-drag.ts
similarity index 61%
copy from dolphinscheduler-ui-next/src/utils/index.ts
copy to dolphinscheduler-ui-next/src/views/projects/workflow/use-sidebar-drag.ts
index efca1e8..b501453 100644
--- a/dolphinscheduler-ui-next/src/utils/index.ts
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/use-sidebar-drag.ts
@@ -15,12 +15,34 @@
  * limitations under the License.
  */
 
-import mapping from './mapping'
-import regex from './regex'
+import type { Ref } from 'vue';
+import type { Dragged } from './dag';
 
-const utils = {
-  mapping,
-  regex
+interface Options {
+  readonly: Ref<boolean>;
+  dragged: Ref<Dragged>;
 }
 
-export default utils
+/**
+ * Sidebar drag
+ */
+export function useSidebarDrag(options: Options) {
+
+  const { readonly, dragged } = options;
+
+  const onDragStart = (e: DragEvent, type: string) => {
+    if (readonly.value) {
+      e.preventDefault()
+      return
+    }
+    dragged.value = {
+      x: e.offsetX,
+      y: e.offsetY,
+      type: type
+    }
+  }
+
+  return {
+    onDragStart
+  }
+}
\ No newline at end of file
diff --git a/dolphinscheduler-ui-next/src/utils/index.ts 
b/dolphinscheduler-ui-next/src/views/projects/workflow/workflow-definition-create.module.scss
similarity index 86%
copy from dolphinscheduler-ui-next/src/utils/index.ts
copy to 
dolphinscheduler-ui-next/src/views/projects/workflow/workflow-definition-create.module.scss
index efca1e8..182fdc3 100644
--- a/dolphinscheduler-ui-next/src/utils/index.ts
+++ 
b/dolphinscheduler-ui-next/src/views/projects/workflow/workflow-definition-create.module.scss
@@ -15,12 +15,8 @@
  * limitations under the License.
  */
 
-import mapping from './mapping'
-import regex from './regex'
-
-const utils = {
-  mapping,
-  regex
-}
-
-export default utils
+.container{
+  width: 100%;
+  box-sizing: border-box;
+  height: calc(100vh - 100px);
+ }
\ No newline at end of file
diff --git a/dolphinscheduler-ui-next/src/utils/index.ts 
b/dolphinscheduler-ui-next/src/views/projects/workflow/workflow-definition-create.tsx
similarity index 61%
copy from dolphinscheduler-ui-next/src/utils/index.ts
copy to 
dolphinscheduler-ui-next/src/views/projects/workflow/workflow-definition-create.tsx
index efca1e8..eff2932 100644
--- a/dolphinscheduler-ui-next/src/utils/index.ts
+++ 
b/dolphinscheduler-ui-next/src/views/projects/workflow/workflow-definition-create.tsx
@@ -15,12 +15,24 @@
  * limitations under the License.
  */
 
-import mapping from './mapping'
-import regex from './regex'
+import { defineComponent } from 'vue'
+import Dag from './dag';
+import { NCard } from 'naive-ui';
+import styles from './workflow-definition-create.module.scss';
 
-const utils = {
-  mapping,
-  regex
-}
+export default defineComponent({
+  name: "WorkflowDefinitionCreate",
+  setup() {
 
-export default utils
+    const slots = {
+      toolbarLeft: () => <span>left-operations</span>,
+      toolbarRight: () => <span>right-operations</span>
+    };
+
+    return () => (
+      <NCard class={styles.container}>
+        <Dag v-slots={slots} />
+      </NCard>
+    )
+  }
+})
\ No newline at end of file
diff --git a/dolphinscheduler-ui-next/src/utils/index.ts 
b/dolphinscheduler-ui-next/src/views/projects/workflow/workflow-definition-details.tsx
similarity index 80%
copy from dolphinscheduler-ui-next/src/utils/index.ts
copy to 
dolphinscheduler-ui-next/src/views/projects/workflow/workflow-definition-details.tsx
index efca1e8..ea9c726 100644
--- a/dolphinscheduler-ui-next/src/utils/index.ts
+++ 
b/dolphinscheduler-ui-next/src/views/projects/workflow/workflow-definition-details.tsx
@@ -15,12 +15,13 @@
  * limitations under the License.
  */
 
-import mapping from './mapping'
-import regex from './regex'
+import { defineComponent } from 'vue'
 
-const utils = {
-  mapping,
-  regex
-}
-
-export default utils
+export default defineComponent({
+  name: "WorkflowDefinitionDetails",
+  setup() {
+    return () => (
+      <div>WorkflowDefinitionDetails</div>
+    )
+  }
+})
\ No newline at end of file
diff --git a/dolphinscheduler-ui-next/src/utils/index.ts 
b/dolphinscheduler-ui-next/src/views/projects/workflow/workflow-definition-list.tsx
similarity index 81%
copy from dolphinscheduler-ui-next/src/utils/index.ts
copy to 
dolphinscheduler-ui-next/src/views/projects/workflow/workflow-definition-list.tsx
index efca1e8..2878ba5 100644
--- a/dolphinscheduler-ui-next/src/utils/index.ts
+++ 
b/dolphinscheduler-ui-next/src/views/projects/workflow/workflow-definition-list.tsx
@@ -15,12 +15,13 @@
  * limitations under the License.
  */
 
-import mapping from './mapping'
-import regex from './regex'
+import { defineComponent } from 'vue'
 
-const utils = {
-  mapping,
-  regex
-}
-
-export default utils
+export default defineComponent({
+  name: "WorkflowDefinitionList",
+  setup() {
+    return () => (
+      <div>WorkflowDefinitionList</div>
+    )
+  }
+})
\ No newline at end of file
diff --git a/dolphinscheduler-ui-next/src/utils/index.ts 
b/dolphinscheduler-ui-next/src/views/projects/workflow/workflow-instance-details.tsx
similarity index 80%
copy from dolphinscheduler-ui-next/src/utils/index.ts
copy to 
dolphinscheduler-ui-next/src/views/projects/workflow/workflow-instance-details.tsx
index efca1e8..137c442 100644
--- a/dolphinscheduler-ui-next/src/utils/index.ts
+++ 
b/dolphinscheduler-ui-next/src/views/projects/workflow/workflow-instance-details.tsx
@@ -15,12 +15,13 @@
  * limitations under the License.
  */
 
-import mapping from './mapping'
-import regex from './regex'
+import { defineComponent } from 'vue'
 
-const utils = {
-  mapping,
-  regex
-}
-
-export default utils
+export default defineComponent({
+  name: "WorkflowInstanceDetails",
+  setup() {
+    return () => (
+      <div>WorkflowInstanceDetails</div>
+    )
+  }
+})
\ No newline at end of file
diff --git a/dolphinscheduler-ui-next/src/utils/index.ts 
b/dolphinscheduler-ui-next/src/views/projects/workflow/workflow-instance-list.tsx
similarity index 81%
copy from dolphinscheduler-ui-next/src/utils/index.ts
copy to 
dolphinscheduler-ui-next/src/views/projects/workflow/workflow-instance-list.tsx
index efca1e8..619e6ca 100644
--- a/dolphinscheduler-ui-next/src/utils/index.ts
+++ 
b/dolphinscheduler-ui-next/src/views/projects/workflow/workflow-instance-list.tsx
@@ -15,12 +15,13 @@
  * limitations under the License.
  */
 
-import mapping from './mapping'
-import regex from './regex'
+import { defineComponent } from 'vue'
 
-const utils = {
-  mapping,
-  regex
-}
-
-export default utils
+export default defineComponent({
+  name: "WorkflowInstanceList",
+  setup() {
+    return () => (
+      <div>WorkflowInstanceList</div>
+    )
+  }
+})
\ No newline at end of file
diff --git a/dolphinscheduler-ui-next/src/utils/index.ts 
b/dolphinscheduler-ui-next/src/views/projects/workflow/x6-style.scss
similarity index 72%
copy from dolphinscheduler-ui-next/src/utils/index.ts
copy to dolphinscheduler-ui-next/src/views/projects/workflow/x6-style.scss
index efca1e8..549be67 100644
--- a/dolphinscheduler-ui-next/src/utils/index.ts
+++ b/dolphinscheduler-ui-next/src/views/projects/workflow/x6-style.scss
@@ -14,13 +14,22 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-
-import mapping from './mapping'
-import regex from './regex'
-
-const utils = {
-  mapping,
-  regex
-}
-
-export default utils
+ 
+ $STROKE_BLUE: #288fff;
+ $BG_WHITE: #ffffff;
+ 
+ .x6-node[data-shape="dag-task"] {
+   &.available {
+     .dag-task-body {
+       stroke: $STROKE_BLUE;
+       stroke-width: 1;
+       stroke-dasharray: 5, 2;
+     }
+     &.adsorbed {
+       .dag-task-body {
+         stroke-width: 3;
+       }
+     }
+   }
+ }
+ 
\ No newline at end of file

Reply via email to