Browse Source

工作流插件参与进入项目本体

QM303176530 4 years ago
parent
commit
5253e43a5a
97 changed files with 6139 additions and 14 deletions
  1. 11 2
      web/package.json
  2. 1 6
      web/src/api/authority.js
  3. 19 0
      web/src/components/gva-wfd/assets/flow/end.svg
  4. 22 0
      web/src/components/gva-wfd/assets/flow/exclusive-gateway.svg
  5. 22 0
      web/src/components/gva-wfd/assets/flow/gateway.svg
  6. 19 0
      web/src/components/gva-wfd/assets/flow/inclusive-gateway.svg
  7. 21 0
      web/src/components/gva-wfd/assets/flow/java-task.svg
  8. 17 0
      web/src/components/gva-wfd/assets/flow/mail-task.svg
  9. 15 0
      web/src/components/gva-wfd/assets/flow/message-catch.svg
  10. 24 0
      web/src/components/gva-wfd/assets/flow/message-start.svg
  11. 22 0
      web/src/components/gva-wfd/assets/flow/parallel-gateway.svg
  12. 21 0
      web/src/components/gva-wfd/assets/flow/receive-task.svg
  13. 18 0
      web/src/components/gva-wfd/assets/flow/script-task.svg
  14. 14 0
      web/src/components/gva-wfd/assets/flow/signal-catch.svg
  15. 22 0
      web/src/components/gva-wfd/assets/flow/signal-start.svg
  16. 19 0
      web/src/components/gva-wfd/assets/flow/start.svg
  17. 13 0
      web/src/components/gva-wfd/assets/flow/timer-catch.svg
  18. 21 0
      web/src/components/gva-wfd/assets/flow/timer-start.svg
  19. 20 0
      web/src/components/gva-wfd/assets/flow/user-task.svg
  20. BIN
      web/src/components/gva-wfd/assets/iconfont/iconfont.eot
  21. 59 0
      web/src/components/gva-wfd/assets/iconfont/iconfont.svg
  22. BIN
      web/src/components/gva-wfd/assets/iconfont/iconfont.ttf
  23. BIN
      web/src/components/gva-wfd/assets/iconfont/iconfont.woff
  24. BIN
      web/src/components/gva-wfd/assets/iconfont/iconfont.woff2
  25. 1 0
      web/src/components/gva-wfd/assets/icons/flow/end.svg
  26. 1 0
      web/src/components/gva-wfd/assets/icons/flow/exclusive-gateway.svg
  27. 1 0
      web/src/components/gva-wfd/assets/icons/flow/icon_java.svg
  28. 1 0
      web/src/components/gva-wfd/assets/icons/flow/icon_mail.svg
  29. 1 0
      web/src/components/gva-wfd/assets/icons/flow/icon_message.svg
  30. 1 0
      web/src/components/gva-wfd/assets/icons/flow/icon_receive.svg
  31. 1 0
      web/src/components/gva-wfd/assets/icons/flow/icon_script.svg
  32. 0 0
      web/src/components/gva-wfd/assets/icons/flow/icon_setting.svg
  33. 1 0
      web/src/components/gva-wfd/assets/icons/flow/icon_signal.svg
  34. 1 0
      web/src/components/gva-wfd/assets/icons/flow/icon_timer.svg
  35. 1 0
      web/src/components/gva-wfd/assets/icons/flow/icon_user.svg
  36. 1 0
      web/src/components/gva-wfd/assets/icons/flow/inclusive-gateway.svg
  37. 1 0
      web/src/components/gva-wfd/assets/icons/flow/parallel-gateway.svg
  38. 1 0
      web/src/components/gva-wfd/assets/icons/flow/start.svg
  39. 167 0
      web/src/components/gva-wfd/assets/icons/model/icon_arch.svg
  40. 92 0
      web/src/components/gva-wfd/assets/icons/model/icon_cluster.svg
  41. 88 0
      web/src/components/gva-wfd/assets/icons/model/icon_data.svg
  42. 134 0
      web/src/components/gva-wfd/assets/icons/model/icon_decision.svg
  43. 138 0
      web/src/components/gva-wfd/assets/icons/model/icon_filtering.svg
  44. 77 0
      web/src/components/gva-wfd/assets/icons/model/icon_por-den.svg
  45. 101 0
      web/src/components/gva-wfd/assets/icons/model/icon_regression.svg
  46. 101 0
      web/src/components/gva-wfd/assets/icons/model/icon_rt-por.svg
  47. 105 0
      web/src/components/gva-wfd/assets/icons/model/icon_tree.svg
  48. 90 0
      web/src/components/gva-wfd/behavior/clickSelected.js
  49. 30 0
      web/src/components/gva-wfd/behavior/deleteItem.js
  50. 207 0
      web/src/components/gva-wfd/behavior/dragEdge.js
  51. 147 0
      web/src/components/gva-wfd/behavior/dragNode.js
  52. 149 0
      web/src/components/gva-wfd/behavior/dragPanelItemAddNode.js
  53. 97 0
      web/src/components/gva-wfd/behavior/dragPoint.js
  54. 23 0
      web/src/components/gva-wfd/behavior/hoverAnchorActived.js
  55. 29 0
      web/src/components/gva-wfd/behavior/hoverNodeActived.js
  56. 20 0
      web/src/components/gva-wfd/behavior/index.js
  57. 140 0
      web/src/components/gva-wfd/behavior/itemAlign.js
  58. 35 0
      web/src/components/gva-wfd/components/DetailPanel/DefaultDetail.vue
  59. 31 0
      web/src/components/gva-wfd/components/DetailPanel/EndEventDetail.vue
  60. 52 0
      web/src/components/gva-wfd/components/DetailPanel/FlowDetail.vue
  61. 32 0
      web/src/components/gva-wfd/components/DetailPanel/GatewayDetail.vue
  62. 38 0
      web/src/components/gva-wfd/components/DetailPanel/JavaTaskDetail.vue
  63. 54 0
      web/src/components/gva-wfd/components/DetailPanel/MailTaskDetail.vue
  64. 45 0
      web/src/components/gva-wfd/components/DetailPanel/MessageEventDetail.vue
  65. 80 0
      web/src/components/gva-wfd/components/DetailPanel/ProcessDetail.vue
  66. 45 0
      web/src/components/gva-wfd/components/DetailPanel/ReceiveTaskDetail.vue
  67. 40 0
      web/src/components/gva-wfd/components/DetailPanel/ScriptTaskDetail.vue
  68. 45 0
      web/src/components/gva-wfd/components/DetailPanel/SignalEventDetail.vue
  69. 31 0
      web/src/components/gva-wfd/components/DetailPanel/StartEventDetail.vue
  70. 49 0
      web/src/components/gva-wfd/components/DetailPanel/TimerEventDetail.vue
  71. 143 0
      web/src/components/gva-wfd/components/DetailPanel/UserTaskDetail.vue
  72. 122 0
      web/src/components/gva-wfd/components/DetailPanel/index.vue
  73. 133 0
      web/src/components/gva-wfd/components/ItemPanel.vue
  74. 121 0
      web/src/components/gva-wfd/components/ToolbarPanel.vue
  75. 262 0
      web/src/components/gva-wfd/components/Wfd.vue
  76. 12 0
      web/src/components/gva-wfd/index.js
  77. 47 0
      web/src/components/gva-wfd/item/anchor.js
  78. 20 0
      web/src/components/gva-wfd/item/controlPoint.js
  79. 76 0
      web/src/components/gva-wfd/locales/en-US.js
  80. 6 0
      web/src/components/gva-wfd/locales/index.js
  81. 76 0
      web/src/components/gva-wfd/locales/zh-CN.js
  82. 45 0
      web/src/components/gva-wfd/plugins/addItemPanel.js
  83. 36 0
      web/src/components/gva-wfd/plugins/canvasPanel.js
  84. 339 0
      web/src/components/gva-wfd/plugins/command.js
  85. 71 0
      web/src/components/gva-wfd/plugins/detailPanel.js
  86. 85 0
      web/src/components/gva-wfd/plugins/toolbar.js
  87. 34 0
      web/src/components/gva-wfd/shape/anchor.js
  88. 37 0
      web/src/components/gva-wfd/shape/controlPoint.js
  89. 328 0
      web/src/components/gva-wfd/shape/edge.js
  90. 375 0
      web/src/components/gva-wfd/shape/flowNode.js
  91. 15 0
      web/src/components/gva-wfd/shape/index.js
  92. 207 0
      web/src/components/gva-wfd/shape/node.js
  93. 291 0
      web/src/components/gva-wfd/shape/subProcess.js
  94. 225 0
      web/src/components/gva-wfd/util/bpmn.js
  95. 23 0
      web/src/components/gva-wfd/util/clazz.js
  96. 62 0
      web/src/components/gva-wfd/util/defaultStyle.js
  97. 23 6
      web/src/view/workflow/workflowCreate/workflowCreate.vue

+ 11 - 2
web/package.json

@@ -31,7 +31,11 @@
         "vuescroll": "^4.14.4",
         "vuex": "^3.1.1",
         "vuex-persist": "^2.1.0",
-        "wfd-gva": "^1.0.4"
+        "@antv/dom-util": "2.0.2",
+        "@antv/matrix-util": "2.0.7",
+        "@antv/g-canvas": "^0.4.12",
+        "@antv/g6": "3.5.2",
+        "@antv/util": "~2.0.9"
     },
     "devDependencies": {
         "@vue/cli-plugin-babel": "^4.5.6",
@@ -40,7 +44,12 @@
         "babel-eslint": "^10.1.0",
         "eslint": "^6.7.2",
         "eslint-plugin-vue": "^6.2.2",
-        "vue-template-compiler": "^2.6.10"
+        "vue-template-compiler": "^2.6.10",
+        "core-js": "^3.3.2",
+        "node-sass": "^4.12.0",
+        "numericjs": "^1.2.6",
+        "raw-loader": "^3.1.0",
+        "sass-loader": "^8.0.0"
     },
     "eslintConfig": {
         "root": true,

+ 1 - 6
web/src/api/authority.js

@@ -1,11 +1,6 @@
 import service from '@/utils/request'
 
-// @Summary 用户登录
-// @Produce  application/json
-// @Param {
-//  page     int
-//	pageSize int
-// }
+
 // @Router /authority/getAuthorityList [post]
 export const getAuthorityList = (data) => {
     return service({

+ 19 - 0
web/src/components/gva-wfd/assets/flow/end.svg

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 68 68" style="enable-background:new 0 0 68 68;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#FCE6E9;}
+	.st1{fill:#F6606B;}
+	.st2{fill:#F5222D;}
+</style>
+<title>end_1</title>
+<g id="图层_2">
+	<g id="图层_1-2">
+		<path class="st0" d="M34,67.5c-18.5,0-33.5-15-33.5-33.5S15.5,0.5,34,0.5s33.5,15,33.5,33.5S52.5,67.5,34,67.5z"/>
+		<path class="st1" d="M34,1c18.2,0,33,14.8,33,33S52.2,67,34,67S1,52.2,1,34S15.8,1,34,1 M34,0C15.2,0,0,15.2,0,34s15.2,34,34,34
+			s34-15.2,34-34S52.8,0,34,0z"/>
+	</g>
+	<rect x="22" y="23" class="st2" width="24" height="24"/>
+</g>
+</svg>

+ 22 - 0
web/src/components/gva-wfd/assets/flow/exclusive-gateway.svg

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 92.5 92.5" style="enable-background:new 0 0 92.5 92.5;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#E6F7F7;stroke:#5FD2D1;stroke-miterlimit:10;}
+	.st1{fill:none;}
+	.st2{fill:#13C2C2;}
+</style>
+<title>decision_1</title>
+<g id="图层_2">
+	<g id="图层_1-2">
+		<path class="st0" d="M51,2.4l39.1,39.1c2.6,2.6,2.6,6.8,0,9.4L50.9,90.1c-2.6,2.6-6.8,2.6-9.4,0L2.4,51c-2.6-2.6-2.6-6.8,0-9.4
+			L41.6,2.4C44.2-0.1,48.4-0.1,51,2.4z"/>
+	</g>
+</g>
+<line class="st1" x1="31" y1="31" x2="62" y2="62"/>
+<line class="st1" x1="144" y1="92" x2="178.5" y2="52.7"/>
+<path class="st2" d="M49.6,46.2l15,15.2c0.7,0.7,0.7,1.8,0,2.5c-0.7,0.7-1.8,0.7-2.5,0L47,48.7l-15.2,15c-0.7,0.7-1.8,0.7-2.5,0
+	c-0.7-0.7-0.7-1.8,0-2.5l15.2-15L29.5,31c-0.7-0.7-0.7-1.8,0-2.5c0.7-0.7,1.8-0.7,2.5,0l15,15.2l15.2-15c0.7-0.7,1.8-0.7,2.5,0
+	c0.7,0.7,0.7,1.8,0,2.5L49.6,46.2L49.6,46.2z"/>
+</svg>

+ 22 - 0
web/src/components/gva-wfd/assets/flow/gateway.svg

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 92.5 92.5" style="enable-background:new 0 0 92.5 92.5;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#E6F7F7;stroke:#5FD2D1;stroke-miterlimit:10;}
+	.st1{fill:none;}
+	.st2{fill:#13C2C2;}
+</style>
+<title>decision_1</title>
+<g id="图层_2">
+	<g id="图层_1-2">
+		<path class="st0" d="M51,2.4l39.1,39.1c2.6,2.6,2.6,6.8,0,9.4L50.9,90.1c-2.6,2.6-6.8,2.6-9.4,0L2.4,51c-2.6-2.6-2.6-6.8,0-9.4
+			L41.6,2.4C44.2-0.1,48.4-0.1,51,2.4z"/>
+	</g>
+</g>
+<line class="st1" x1="31" y1="31" x2="62" y2="62"/>
+<line class="st1" x1="144" y1="92" x2="178.5" y2="52.7"/>
+<path class="st2" d="M49.6,46.2l15,15.2c0.7,0.7,0.7,1.8,0,2.5c-0.7,0.7-1.8,0.7-2.5,0L47,48.7l-15.2,15c-0.7,0.7-1.8,0.7-2.5,0
+	c-0.7-0.7-0.7-1.8,0-2.5l15.2-15L29.5,31c-0.7-0.7-0.7-1.8,0-2.5c0.7-0.7,1.8-0.7,2.5,0l15,15.2l15.2-15c0.7-0.7,1.8-0.7,2.5,0
+	c0.7,0.7,0.7,1.8,0,2.5L49.6,46.2L49.6,46.2z"/>
+</svg>

+ 19 - 0
web/src/components/gva-wfd/assets/flow/inclusive-gateway.svg

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 92.5 92.5" style="enable-background:new 0 0 92.5 92.5;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#E6F7F7;stroke:#5FD2D1;stroke-miterlimit:10;}
+	.st1{fill:none;}
+	.st2{fill:none;stroke:#13C2C2;stroke-width:3;stroke-miterlimit:10;}
+</style>
+<title>decision_1</title>
+<g id="图层_2">
+	<g id="图层_1-2">
+		<path class="st0" d="M51,2.4l39.1,39.1c2.6,2.6,2.6,6.8,0,9.4L50.9,90.1c-2.6,2.6-6.8,2.6-9.4,0L2.4,51c-2.6-2.6-2.6-6.8,0-9.4
+			L41.6,2.4C44.2-0.1,48.4-0.1,51,2.4z"/>
+	</g>
+</g>
+<line class="st1" x1="31" y1="31" x2="62" y2="62"/>
+<circle class="st2" cx="46.2" cy="46.3" r="23.7"/>
+</svg>

+ 21 - 0
web/src/components/gva-wfd/assets/flow/java-task.svg

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 107 61" style="enable-background:new 0 0 107 61;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#FFF1F0;stroke:#FF4D4F;stroke-miterlimit:10;}
+	.st1{fill:#FF4D4F;}
+</style>
+<title>task_1</title>
+<g id="图层_1-2">
+	<path class="st0" d="M8.9,0.5h89.2c4.6,0,8.4,3.8,8.4,8.4v43.2c0,4.6-3.8,8.4-8.4,8.4H8.9c-4.6,0-8.4-3.8-8.4-8.4V8.9
+		C0.5,4.3,4.3,0.5,8.9,0.5z"/>
+	<path class="st1" d="M49.7,36.7l-0.8-2.5l0,0L48,36.7H49.7z M60.6,36.7l-0.8-2.5l0,0l-0.8,2.5H60.6z"/>
+	<path class="st1" d="M67.1,28.9h-2.4v-6.1l-7.2-7.4H41.4c-1,0-1.8,0.8-1.8,1.9l0,0v11.7h-2.2c-0.3,0-0.7,0.3-0.7,0.7l0,0v12.4
+		c0,0.3,0.3,0.7,0.7,0.7h2.2v2c0,1,0.8,1.9,1.8,1.9l0,0h21.5c1,0,1.8-0.8,1.8-1.9l0,0v-2.2h2.4c0.3,0,0.7-0.3,0.7-0.7V29.4
+		C67.6,29.2,67.4,28.9,67.1,28.9z M41.7,17.6h14.8v6.1h6.1v5.4H41.7V17.6z M62.4,38.7h-1L61,37.2h-2.4l-0.5,1.5h-1l2.2-5.9h1
+		L62.4,38.7z M56.8,32.8l-2,5.9h-1.2l-1.8-5.9h1l1.5,4.9l0,0l1.5-4.9H56.8z M51.4,38.7h-1l-0.5-1.5h-2.4l-0.5,1.5h-1l2.2-5.9h1
+		L51.4,38.7z M43.5,37.9c0.3,0,0.5-0.2,0.7-0.3c0.2-0.3,0.2-0.7,0.2-1v-3.7h1v4.1c0,0.5-0.2,1-0.3,1.5c-0.3,0.3-0.8,0.5-1.3,0.5
+		c-0.5,0-1-0.2-1.3-0.5C42,38,41.9,37.5,41.9,37v-0.2h1V37C42.7,37.5,43,37.9,43.5,37.9z M62.5,44.3H41.7v-1.9h20.8V44.3z"/>
+</g>
+</svg>

+ 17 - 0
web/src/components/gva-wfd/assets/flow/mail-task.svg

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 107 61" style="enable-background:new 0 0 107 61;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#F6FFED;stroke:#73D13D;stroke-miterlimit:10;}
+	.st1{fill:#73D13D;}
+</style>
+<title>task_1</title>
+<g id="图层_1-2">
+	<path class="st0" d="M8.9,0.5h89.2c4.6,0,8.4,3.8,8.4,8.4v43.2c0,4.6-3.8,8.4-8.4,8.4H8.9c-4.6,0-8.4-3.8-8.4-8.4V8.9
+		C0.5,4.3,4.3,0.5,8.9,0.5z"/>
+	<path class="st1" d="M66.2,18.6H40.8c-1.7,0-3,1.3-3,3v17.9c0,1.7,1.3,3,3,3h25.3c1.7,0,3-1.3,3-3V21.6
+		C69.1,19.9,67.8,18.6,66.2,18.6z M65.1,24.2L55.6,33c-1.1,1.1-2.8,1.1-4.1,0l-9.4-8.8c-0.4-0.4-0.4-0.9,0-1.5
+		c0.4-0.4,0.9-0.4,1.5,0l9.4,8.8c0.4,0.4,0.9,0.4,1.3,0l9.4-8.8c0.4-0.4,1.1-0.4,1.5,0C65.4,23.3,65.4,23.8,65.1,24.2z"/>
+</g>
+</svg>

+ 15 - 0
web/src/components/gva-wfd/assets/flow/message-catch.svg

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 92.5 56" style="enable-background:new 0 0 92.5 56;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#FEF7E8;stroke:#FA8C16;stroke-miterlimit:10;}
+	.st1{fill:#FA8C16;}
+</style>
+<polygon class="st0" points="46.2,56 91.2,1.4 1.1,1.4 "/>
+<path class="st1" d="M39.3,18.9h14c0.6,0,1,0.4,1,1s-0.4,1-1,1h-14c-0.6,0-1-0.4-1-1S38.7,18.9,39.3,18.9z M39.3,12.9h14
+	c0.6,0,1,0.4,1,1s-0.4,1-1,1h-14c-0.6,0-1-0.4-1-1S38.7,12.9,39.3,12.9z M39.8,30.8l5.8-4.5c0.4-0.3,0.8-0.4,1.2-0.4h11
+	c0.6,0,1-0.4,1-1v-16c0-0.6-0.4-1-1-1h-23c-0.6,0-1,0.4-1,1v16c0,0.6,0.4,1,1,1h3c1.1,0,2,0.9,2,2V30.8L39.8,30.8z M46.8,27.9
+	L40.2,33c-0.7,0.5-1.6,0.4-2.1-0.3c-0.2-0.3-0.3-0.6-0.3-0.9v-3.9h-3c-1.7,0-3-1.3-3-3v-16c0-1.7,1.3-3,3-3h23c1.7,0,3,1.3,3,3v16
+	c0,1.7-1.3,3-3,3C57.8,27.9,46.8,27.9,46.8,27.9z"/>
+</svg>

+ 24 - 0
web/src/components/gva-wfd/assets/flow/message-start.svg

@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 68 68" style="enable-background:new 0 0 68 68;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#FCF1E8;}
+	.st1{fill:#F9AA68;}
+	.st2{fill:#FA8C16;}
+</style>
+<title>start_1</title>
+<g id="图层_2">
+	<g id="图层_1-2">
+		<path class="st0" d="M34,67.5c-18.5,0-33.5-15-33.5-33.5S15.5,0.5,34,0.5s33.5,15,33.5,33.5S52.5,67.5,34,67.5z"/>
+		<path class="st1" d="M34,1c18.2,0,33,14.8,33,33S52.2,67,34,67S1,52.2,1,34S15.8,1,34,1 M34,0C15.2,0,0,15.2,0,34s15.2,34,34,34
+			s34-15.2,34-34S52.8,0,34,0z"/>
+	</g>
+</g>
+<path class="st2" d="M27,34.2h15.4c0.6,0,1.1,0.5,1.1,1.1c0,0.6-0.5,1.1-1.1,1.1H27c-0.6,0-1.1-0.5-1.1-1.1
+	C25.9,34.7,26.4,34.2,27,34.2z M27,27.9h15.4c0.6,0,1.1,0.5,1.1,1.1S43.1,30,42.5,30H27c-0.6,0-1.1-0.5-1.1-1.1S26.4,27.9,27,27.9z
+	 M27.5,47l6.5-4.9c0.3-0.3,0.8-0.5,1.4-0.5h12.1c0.6,0,1.1-0.5,1.1-1.1v-17c0-0.6-0.5-1.1-1.1-1.1H22c-0.6,0-1.1,0.5-1.1,1.1v17
+	c0,0.6,0.5,1.1,1.1,1.1h3.3c1.3,0,2.2,0.9,2.2,2.1V47L27.5,47z M35.2,43.8L28,49.3c-0.8,0.6-1.7,0.5-2.4-0.3
+	c-0.2-0.3-0.3-0.6-0.3-0.9v-4.3H22c-1.9,0-3.3-1.4-3.3-3.2v-17c0-1.8,1.4-3.2,3.3-3.2h25.4c1.9,0,3.3,1.4,3.3,3.2v17
+	c0,1.8-1.4,3.2-3.3,3.2C47.5,43.8,35.2,43.8,35.2,43.8z"/>
+</svg>

+ 22 - 0
web/src/components/gva-wfd/assets/flow/parallel-gateway.svg

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 92.5 92.5" style="enable-background:new 0 0 92.5 92.5;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#E6F7F7;stroke:#5FD2D1;stroke-miterlimit:10;}
+	.st1{fill:none;}
+	.st2{fill:#13C2C2;}
+</style>
+<title>decision_1</title>
+<g id="图层_2">
+	<g id="图层_1-2">
+		<path class="st0" d="M51,2.4l39.1,39.1c2.6,2.6,2.6,6.8,0,9.4L50.9,90.1c-2.6,2.6-6.8,2.6-9.4,0L2.4,51c-2.6-2.6-2.6-6.8,0-9.4
+			L41.6,2.4C44.2-0.1,48.4-0.1,51,2.4z"/>
+	</g>
+</g>
+<line class="st1" x1="31" y1="31" x2="62" y2="62"/>
+<line class="st1" x1="144" y1="92" x2="178.5" y2="52.7"/>
+<path class="st2" d="M48.9,48l0.3,21.4c0,1-0.7,1.8-1.7,1.8c-1,0-1.8-0.7-1.8-1.7L45.3,48l-21.4,0.3c-1,0-1.8-0.7-1.8-1.7
+	c0-1,0.7-1.8,1.7-1.8l21.4-0.3l-0.3-21.4c0-1,0.7-1.8,1.7-1.8c1,0,1.8,0.7,1.8,1.7l0.3,21.4l21.4-0.3c1,0,1.8,0.7,1.8,1.7
+	c0,1-0.7,1.8-1.7,1.8L48.9,48L48.9,48z"/>
+</svg>

+ 21 - 0
web/src/components/gva-wfd/assets/flow/receive-task.svg

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 107 61" style="enable-background:new 0 0 107 61;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#FFF0F6;stroke:#FF85C0;stroke-miterlimit:10;}
+	.st1{fill:#FF85C0;}
+</style>
+<title>task_1</title>
+<g id="图层_1-2">
+	<path class="st0" d="M8.9,0.5h89.2c4.6,0,8.4,3.8,8.4,8.4v43.2c0,4.6-3.8,8.4-8.4,8.4H8.9c-4.6,0-8.4-3.8-8.4-8.4V8.9
+		C0.5,4.3,4.3,0.5,8.9,0.5z"/>
+	<path class="st1" d="M47.2,23h8.6c0.7,0,1.2,0.5,1.2,1.2c0,0.7-0.5,1.2-1.2,1.2h-8.6c-0.7,0-1.2-0.5-1.2-1.2
+		C46.2,23.5,46.7,23,47.2,23z M47.2,18.7h4.3c0.7,0,1.2,0.5,1.2,1.2c0,0.7-0.5,1.2-1.2,1.2h-4.3c-0.7,0-1.2-0.5-1.2-1.2
+		C46.1,19.2,46.7,18.7,47.2,18.7z M44.1,29.4c3.3,1.3,7.4,2.2,9.8,2.2s6.5-0.8,9.8-2.2V17.5c-0.2-0.5-0.7-1-1.2-1H45.1
+		c-0.5,0-1,0.5-1,1V29.4L44.1,29.4z M66.3,28.3c0.3-0.3,0.5-0.7,0.5-1s-0.3-1-1.2-1.5v2.6C65.9,28.4,66.1,28.3,66.3,28.3z
+		 M38.6,27.3c0-1.5,1.2-3,3.3-4v-5.8c0-1.8,1.5-3.3,3.3-3.3h17.2c1.8,0,3.3,1.5,3.3,3.3v5.8c2,1.2,3.3,2.5,3.3,4v14.1
+		c0,1.8-1.5,3.3-3.3,3.3H41.9c-1.8,0-3.3-1.5-3.3-3.3V27.3z M41.3,28.3c0.2,0.2,0.3,0.2,0.7,0.3V26c-0.7,0.5-1.2,1-1.2,1.5
+		C40.8,27.6,41,27.9,41.3,28.3z"/>
+</g>
+</svg>

+ 18 - 0
web/src/components/gva-wfd/assets/flow/script-task.svg

@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 107 61" style="enable-background:new 0 0 107 61;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#FFF7E6;stroke:#FFA940;stroke-miterlimit:10;}
+	.st1{fill:#FFA940;}
+</style>
+<title>task_1</title>
+<g id="图层_1-2">
+	<path class="st0" d="M8.9,0.5h89.2c4.6,0,8.4,3.8,8.4,8.4v43.2c0,4.6-3.8,8.4-8.4,8.4H8.9c-4.6,0-8.4-3.8-8.4-8.4V8.9
+		C0.5,4.3,4.3,0.5,8.9,0.5z"/>
+	<path class="st1" d="M56.9,13.5l9.7,9.7h-8.8c-0.6,0-1.1-0.6-1.1-1.1v-8.6H56.9z M45.8,36.5c-0.6,0-1.1,0.6-1.1,1.1
+		c0,0.6,0.6,1.1,1.1,1.1H59c0.6,0,1.1-0.6,1.1-1.1c0-0.6-0.6-1.1-1.1-1.1H45.8z M45.8,31.1c-0.6,0-1.1,0.6-1.1,1.1
+		c0,0.6,0.6,1.1,1.1,1.1h6.6c0.6,0,1.1-0.6,1.1-1.1c0-0.6-0.6-1.1-1.1-1.1H45.8z M66.7,25.4v15.4c0,1.9-1.5,3.2-3.2,3.2H43.8
+		c-1.9,0-3.2-1.5-3.2-3.2v-24c0-1.9,1.5-3.2,3.2-3.2h10.9V23c0,1.5,1.1,2.6,2.6,2.6h9.4V25.4z"/>
+</g>
+</svg>

+ 14 - 0
web/src/components/gva-wfd/assets/flow/signal-catch.svg

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 92.5 56" style="enable-background:new 0 0 92.5 56;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#FEF7E8;stroke:#FA8C16;stroke-miterlimit:10;}
+	.st1{fill:#FA8C16;}
+</style>
+<polygon class="st0" points="46.2,56 91.2,1.4 1.1,1.4 "/>
+<path class="st1" d="M46.3,25.7c-1.7,0-3,1.4-3,3.1l0,0c0,1.7,1.4,3.1,3,3.1c1.7,0,3-1.4,3-3.1l0,0C49.4,27.1,48,25.7,46.3,25.7z
+	 M39.9,22.3l2.1,2.2c2.3-2.4,6.1-2.4,8.5-0.1c0,0,0,0,0.1,0.1l2.1-2.2C49.3,18.7,43.6,18.7,39.9,22.3C40,22.2,39.9,22.2,39.9,22.3z
+	 M35.6,17.9l2.1,2.2c4.7-4.7,12.3-4.8,17-0.1l0.1,0.1l2.1-2.2c-5.8-5.9-15.4-6-21.3-0.1C35.7,17.8,35.7,17.9,35.6,17.9z M31.3,13.6
+	l2.1,2.2c7-7.1,18.4-7.2,25.6-0.2c0.1,0.1,0.1,0.1,0.2,0.2l2.1-2.2c-8.2-8.3-21.5-8.4-29.8-0.2C31.5,13.5,31.4,13.5,31.3,13.6z"/>
+</svg>

+ 22 - 0
web/src/components/gva-wfd/assets/flow/signal-start.svg

@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 68 68" style="enable-background:new 0 0 68 68;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#FCF1E8;}
+	.st1{fill:#F9AA68;}
+	.st2{fill:#FA8C16;}
+</style>
+<title>start_1</title>
+<g id="图层_2">
+	<g id="图层_1-2">
+		<path class="st0" d="M34,67.5c-18.5,0-33.5-15-33.5-33.5S15.5,0.5,34,0.5s33.5,15,33.5,33.5S52.5,67.5,34,67.5z"/>
+		<path class="st1" d="M34,1c18.2,0,33,14.8,33,33S52.2,67,34,67S1,52.2,1,34S15.8,1,34,1 M34,0C15.2,0,0,15.2,0,34s15.2,34,34,34
+			s34-15.2,34-34S52.8,0,34,0z"/>
+	</g>
+</g>
+<path class="st2" d="M34.4,40c-1.8,0-3.3,1.5-3.3,3.4l0,0c0,1.9,1.5,3.4,3.3,3.4c1.8,0,3.3-1.5,3.3-3.4l0,0
+	C37.8,41.5,36.3,40,34.4,40z M27.4,36.1l2.4,2.4c2.5-2.7,6.7-2.7,9.3,0l0,0l2.4-2.4C37.6,32.3,31.4,32.3,27.4,36.1L27.4,36.1z
+	 M22.7,31.4l2.4,2.4c5.2-5.2,13.5-5.2,18.7-0.1l0.1,0.1l2.4-2.4c-6.4-6.4-16.9-6.5-23.4-0.1C22.8,31.4,22.8,31.4,22.7,31.4z
+	 M18.1,26.6l2.4,2.4c7.7-7.9,20.2-7.9,28-0.1l0.1,0.1l2.4-2.4c-8.9-9-23.6-9.2-32.6-0.3C18.2,26.5,18.1,26.6,18.1,26.6z"/>
+</svg>

+ 19 - 0
web/src/components/gva-wfd/assets/flow/start.svg

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 68 68" style="enable-background:new 0 0 68 68;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#FCF1E8;}
+	.st1{fill:#F9AA68;}
+	.st2{fill:#FA8C16;}
+</style>
+<title>start_1</title>
+<g id="图层_2">
+	<g id="图层_1-2">
+		<path class="st0" d="M34,67.5c-18.5,0-33.5-15-33.5-33.5S15.5,0.5,34,0.5s33.5,15,33.5,33.5C67.5,52.5,52.5,67.5,34,67.5z"/>
+		<path class="st1" d="M34,1c18.2,0,33,14.8,33,33S52.2,67,34,67S1,52.2,1,34S15.8,1,34,1 M34,0C15.2,0,0,15.2,0,34s15.2,34,34,34
+			s34-15.2,34-34S52.8,0,34,0z"/>
+	</g>
+	<polygon class="st2" points="48.2,32.6 28.2,17.5 28.2,47.8 	"/>
+</g>
+</svg>

+ 13 - 0
web/src/components/gva-wfd/assets/flow/timer-catch.svg

@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 92.5 56" style="enable-background:new 0 0 92.5 56;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#FEF7E8;stroke:#FA8C16;stroke-miterlimit:10;}
+	.st1{fill:#FA8C16;}
+</style>
+<polygon class="st0" points="46.2,56 91.2,1.4 1.1,1.4 "/>
+<path class="st1" d="M46.3,6.2c-7.4,0-13.4,6-13.4,13.4S38.9,33,46.3,33s13.4-6,13.4-13.4S53.7,6.2,46.3,6.2z M46.3,30.4
+	c-6,0-10.8-4.8-10.8-10.8S40.3,8.8,46.3,8.8s10.8,4.8,10.8,10.8S52.2,30.4,46.3,30.4z M49.6,24.7L45,21.4c-0.2-0.1-0.3-0.3-0.3-0.5
+	V12c0-0.4,0.3-0.6,0.6-0.6H47c0.4,0,0.6,0.3,0.6,0.6v7.6l3.6,2.6c0.3,0.2,0.4,0.6,0.1,0.9l-1,1.4C50.3,24.9,49.9,24.9,49.6,24.7z"/>
+</svg>

+ 21 - 0
web/src/components/gva-wfd/assets/flow/timer-start.svg

@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 68 68" style="enable-background:new 0 0 68 68;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#FCF1E8;}
+	.st1{fill:#F9AA68;}
+	.st2{fill:#FA8C16;}
+</style>
+<title>start_1</title>
+<g id="图层_2">
+	<g id="图层_1-2">
+		<path class="st0" d="M34,67.5c-18.5,0-33.5-15-33.5-33.5S15.5,0.5,34,0.5s33.5,15,33.5,33.5S52.5,67.5,34,67.5z"/>
+		<path class="st1" d="M34,1c18.2,0,33,14.8,33,33S52.2,67,34,67S1,52.2,1,34S15.8,1,34,1 M34,0C15.2,0,0,15.2,0,34s15.2,34,34,34
+			s34-15.2,34-34S52.8,0,34,0z"/>
+	</g>
+</g>
+<path class="st2" d="M35,17.6c-8.7,0-15.6,7-15.6,15.7S26.4,49,35,49s15.6-7,15.6-15.7S43.7,17.6,35,17.6z M35,46
+	c-7,0-12.6-5.7-12.6-12.7S28.1,20.6,35,20.6s12.6,5.7,12.6,12.7S42,46,35,46z M38.8,39.4l-5.5-4C33.1,35.2,33,35,33,34.8V24.4
+	c0-0.4,0.4-0.8,0.8-0.8h2.1c0.4,0,0.8,0.4,0.8,0.8v9.1l4.1,3c0.4,0.2,0.4,0.8,0.2,1.1l-0.9,1.5C39.7,39.5,39.2,39.7,38.8,39.4z"/>
+</svg>

+ 20 - 0
web/src/components/gva-wfd/assets/flow/user-task.svg

@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 107 61" style="enable-background:new 0 0 107 61;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#E6F2FC;stroke:#5CAFFB;stroke-miterlimit:10;}
+	.st1{fill:#1890FF;}
+</style>
+<title>task_1</title>
+<g id="图层_2">
+	<g id="图层_1-2">
+		<path class="st0" d="M8.9,0.5h89.2c4.6,0,8.4,3.8,8.4,8.4v43.2c0,4.6-3.8,8.4-8.4,8.4H8.9c-4.6,0-8.4-3.8-8.4-8.4V8.9
+			C0.5,4.3,4.3,0.5,8.9,0.5z"/>
+	</g>
+</g>
+<path class="st1" d="M47.3,21.5c0-3.3,0.3-5.8,3.5-7.4c0.7-0.3,2.2-0.5,2.8-0.5c0.8,0,0-0.7,1.8-0.7s4.1,0.8,5.3,3.1s1.3,5,1.3,5.6
+	c0,0.7,0.7,0.3,0.7,1.3c0,1-0.3,2.8-1.3,4c-1,1.3-1.3,1.5-2,2.5c-0.7,1-1,2-1,2.8c0,3.1,5.3,3.3,8.4,5.3c1.2,0.8,2,1.8,2.5,3
+	c0.5,1.2-0.2,2.3-1.2,2.6c-0.2,0.2-0.5,0.2-0.8,0.2H41.9c-1.2,0-2.2-1-2.2-2.2c0-0.3,0-0.5,0.2-0.8c0.5-1.2,1.3-2.2,2.5-3
+	c3.1-2,8.4-2.2,8.4-5.3c0-0.8-0.3-1.8-1-2.8s-1-1.3-2-2.5c-1-1.3-1.3-3.1-1.3-4C46.5,22,47.3,22.2,47.3,21.5z"/>
+</svg>

BIN
web/src/components/gva-wfd/assets/iconfont/iconfont.eot


+ 59 - 0
web/src/components/gva-wfd/assets/iconfont/iconfont.svg

@@ -0,0 +1,59 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
+<!--
+2013-9-30: Created.
+-->
+<svg>
+<metadata>
+Created by iconfont
+</metadata>
+<defs>
+
+<font id="iconfont" horiz-adv-x="1024" >
+  <font-face
+    font-family="iconfont"
+    font-weight="500"
+    font-stretch="normal"
+    units-per-em="1024"
+    ascent="896"
+    descent="-128"
+  />
+    <missing-glyph />
+    
+    <glyph glyph-name="copy-o" unicode="&#59554;" d="M998.4-128h-518.4c-12.8 0-25.6 12.8-25.6 25.6v582.4c0 12.8 12.8 25.6 32 25.6h352l192-192v-422.4c-6.4-6.4-19.2-19.2-32-19.2z m-166.4 576v-128h128l-128 128z m128-192h-192v192h-256v-512h448v320z m-576-64h-256v576h256v-192h256v64l-192 192h-352c-19.2 0-32-12.8-32-25.6v-646.4c0-12.8 12.8-25.6 25.6-25.6h294.4v57.6z m64 576l128-128h-128v128z"  horiz-adv-x="1030" />
+
+    
+    <glyph glyph-name="undo" unicode="&#59557;" d="M192 409.6c76.8 102.4 204.8 166.4 345.6 166.4 211.2 0 384-147.2 428.8-345.6-64 121.6-198.4 204.8-345.6 204.8-121.6 0-230.4-57.6-307.2-147.2l108.8-108.8c6.4-6.4 6.4-19.2 0-32-6.4-6.4-6.4-6.4-12.8-6.4h-313.6c-12.8 0-25.6 12.8-25.6 25.6v313.6c0 12.8 6.4 19.2 19.2 19.2 6.4 0 12.8 0 12.8-6.4l89.6-83.2z"  horiz-adv-x="1024" />
+
+    
+    <glyph glyph-name="zoom-in-o" unicode="&#59558;" d="M640 480c0-19.2-12.8-32-32-32h-96v-96c0-19.2-12.8-32-32-32s-32 12.8-32 32V448h-96c-19.2 0-32 12.8-32 32s12.8 32 32 32h96v96c0 19.2 12.8 32 32 32s32-12.8 32-32v-96h96c19.2 0 32-12.8 32-32z m288-505.6c-19.2-12.8-44.8-12.8-57.6 6.4l-185.6 211.2c-57.6-44.8-128-70.4-204.8-70.4-192 0-352 160-352 352s160 352 352 352 352-160 352-352c0-89.6-32-166.4-83.2-230.4l185.6-217.6c12.8-6.4 12.8-32-6.4-51.2z m-448 217.6c160 0 288 128 288 288s-128 288-288 288-288-128-288-288 128-288 288-288z"  horiz-adv-x="1024" />
+
+    
+    <glyph glyph-name="actual-size-o" unicode="&#59339;" d="M256 768h64v-768H256V768z m512 0h64v-768h-64V768z m-256-192h64v-64h-64v64z m0-256h64v-64h-64v64z"  horiz-adv-x="1024" />
+
+    
+    <glyph glyph-name="redo" unicode="&#59340;" d="M832 409.6C755.2 512 627.2 576 486.4 576c-211.2 0-384-147.2-428.8-345.6 64 121.6 198.4 204.8 345.6 204.8 121.6 0 230.4-57.6 307.2-147.2l-108.8-108.8c-6.4-6.4-6.4-19.2 0-32 6.4-6.4 6.4-6.4 12.8-6.4H928c12.8 0 25.6 12.8 25.6 25.6V480c0 12.8-6.4 19.2-19.2 19.2-6.4 0-12.8 0-12.8-6.4L832 409.6z"  horiz-adv-x="1024" />
+
+    
+    <glyph glyph-name="fit" unicode="&#59341;" d="M640 608V800c0 19.2 12.8 32 32 32s32-12.8 32-32v-160h160c19.2 0 32-12.8 32-32s-12.8-32-32-32h-192c-6.4 0-12.8 0-19.2 6.4h-6.4c0 12.8-6.4 19.2-6.4 25.6zM288 832c-19.2 0-32-12.8-32-32v-160H96c-19.2 0-32-12.8-32-32s12.8-32 32-32h192c6.4 0 12.8 0 19.2 6.4h6.4c6.4 6.4 6.4 12.8 6.4 19.2v192C320 819.2 307.2 832 288 832z m576-576h-192c-6.4 0-12.8 0-19.2-6.4h-6.4c-6.4-6.4-6.4-12.8-6.4-19.2v-192c0-19.2 12.8-32 32-32s32 12.8 32 32V192h160c19.2 0 32 12.8 32 32s-12.8 32-32 32z m-550.4-12.8c-6.4 6.4-6.4 6.4 0 0-12.8 6.4-19.2 12.8-25.6 12.8H96c-19.2 0-32-12.8-32-32s12.8-32 32-32h160v-160c0-19.2 12.8-32 32-32s32 12.8 32 32v192c0 6.4-6.4 12.8-6.4 19.2zM480 544c-70.4 0-128-57.6-128-128s57.6-128 128-128 128 57.6 128 128-57.6 128-128 128z"  horiz-adv-x="1024" />
+
+    
+    <glyph glyph-name="delete-o" unicode="&#59342;" d="M896 576v-576c0-38.4-19.2-64-57.6-64H256c-38.4 0-64 25.6-64 64V576H128c0 38.4 25.6 64 64 64h192V704c0 38.4 25.6 64 64 64h192c38.4 0 64-25.6 64-64v-64h185.6c38.4 0 64-25.6 64-64H896z m-256 128h-198.4v-64H640V704z m192-128H249.6v-576H832V576zM448 192h-57.6v256H448v-256z m256 0h-64v256h64v-256z"  horiz-adv-x="1024" />
+
+    
+    <glyph glyph-name="to-front" unicode="&#59343;" d="M768 300.8c-6.4 12.8-12.8 19.2-25.6 19.2l-102.4 12.8 115.2 102.4c6.4 6.4 12.8 19.2 6.4 25.6-6.4 12.8-12.8 19.2-25.6 19.2l-102.4 12.8 115.2 102.4c6.4 6.4 12.8 19.2 6.4 25.6-6.4 12.8-12.8 19.2-25.6 19.2L358.4 697.6c-6.4 0-19.2 0-25.6-6.4l-320-230.4c-6.4-6.4-12.8-19.2-12.8-25.6 0-12.8 12.8-19.2 19.2-19.2l115.2-25.6-121.6-89.6c-6.4-6.4-12.8-19.2-12.8-25.6 0-12.8 12.8-19.2 19.2-19.2l115.2-25.6-121.6-89.6c-6.4-6.4-12.8-19.2-12.8-25.6 0-12.8 12.8-19.2 19.2-19.2l390.4-96c6.4 0 19.2 0 25.6 6.4l307.2 268.8c25.6 6.4 25.6 19.2 25.6 25.6zM217.6 377.6l204.8-51.2c6.4 0 19.2 0 25.6 6.4l134.4 121.6 83.2-12.8L416 224 96 300.8l121.6 76.8z m204.8-320l-320 76.8 108.8 76.8L416 160c6.4 0 19.2 0 25.6 6.4L576 288l83.2-12.8-236.8-217.6z m473.6 448l140.8-179.2h-96c-6.4-115.2-76.8-211.2-179.2-262.4 51.2 70.4 83.2 153.6 83.2 249.6v12.8h-96l147.2 179.2z"  horiz-adv-x="1036" />
+
+    
+    <glyph glyph-name="to-back" unicode="&#59344;" d="M729.6 320l-96 12.8 115.2 102.4c6.4 6.4 12.8 19.2 6.4 25.6 0 12.8-12.8 19.2-25.6 19.2l-96 12.8 115.2 102.4c6.4 6.4 12.8 19.2 6.4 25.6 0 12.8-12.8 19.2-25.6 19.2L364.8 697.6c-6.4 0-12.8 0-19.2-6.4l-320-230.4c-6.4-6.4-12.8-19.2-12.8-25.6 0-12.8 12.8-19.2 19.2-19.2l108.8-25.6-121.6-89.6c-6.4-6.4-12.8-19.2-12.8-25.6s12.8-19.2 19.2-19.2l108.8-25.6-121.6-89.6c-6.4-6.4-12.8-19.2-12.8-25.6 0-12.8 12.8-19.2 19.2-19.2l384-89.6c6.4 0 19.2 0 25.6 6.4l300.8 268.8c6.4 6.4 12.8 19.2 6.4 25.6 12.8 6.4 6.4 12.8-6.4 12.8zM364.8 646.4l300.8-44.8L416 384l-313.6 76.8 262.4 185.6z m-147.2-268.8l204.8-51.2c6.4 0 19.2 0 25.6 6.4l134.4 121.6 83.2-12.8L416 224l-313.6 76.8 115.2 76.8z m723.2-134.4h96L896 64l-140.8 179.2h96V256c0 96-32 179.2-83.2 249.6 102.4-51.2 172.8-153.6 172.8-262.4z"  horiz-adv-x="1036" />
+
+    
+    <glyph glyph-name="paster-o" unicode="&#59345;" d="M774.4 646.4H704V704h102.4c12.8 0 25.6-12.8 25.6-25.6v-96h-57.6v64zM256-6.4H185.6V646.4H256V704H153.6c-12.8 0-25.6-12.8-25.6-25.6V-32c0-12.8 12.8-25.6 25.6-25.6h96l6.4 51.2zM640 576H320V704h64V774.4c0 32 25.6 57.6 57.6 57.6H512c32 0 57.6-25.6 57.6-57.6V704H640v-128z m-128 198.4h-70.4V704H512V774.4z m377.6-876.8V320L704 512H352c-19.2 0-32-12.8-32-25.6V-96c0-12.8 12.8-25.6 25.6-25.6h512c19.2-6.4 32 6.4 32 19.2z m-64 409.6h-128V448l128-140.8z m6.4-384H377.6V448H640v-198.4h192v-326.4z"  horiz-adv-x="1024" />
+
+    
+    <glyph glyph-name="zoom-out-o" unicode="&#59346;" d="M640 480c0-19.2-12.8-32-32-32h-256c-19.2 0-32 12.8-32 32s12.8 32 32 32h256c19.2 0 32-12.8 32-32z m288-505.6c-19.2-12.8-44.8-12.8-57.6 6.4L684.8 192c-57.6-44.8-128-70.4-204.8-70.4-192 0-352 160-352 352s160 352 352 352 352-160 352-352c0-89.6-32-166.4-83.2-230.4l185.6-217.6c12.8-6.4 12.8-32-6.4-51.2zM480 192c160 0 288 128 288 288s-128 288-288 288-288-128-288-288 128-288 288-288z"  horiz-adv-x="1024" />
+
+    
+
+
+  </font>
+</defs></svg>

BIN
web/src/components/gva-wfd/assets/iconfont/iconfont.ttf


BIN
web/src/components/gva-wfd/assets/iconfont/iconfont.woff


BIN
web/src/components/gva-wfd/assets/iconfont/iconfont.woff2


+ 1 - 0
web/src/components/gva-wfd/assets/icons/flow/end.svg

@@ -0,0 +1 @@
+<svg t="1602254403451" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3291" width="200" height="200"><path d="M243.611344 62.597687l535.403013 0c98.565876 0 178.464601 80.265068 178.464601 179.283246l0 537.849738c0 99.008968-79.898725 179.283246-178.464601 179.283246L243.611344 959.013917c-98.556667 0-178.455391-80.274278-178.455391-179.283246l0-537.849738C65.155952 142.862755 145.054677 62.597687 243.611344 62.597687z" p-id="3292" fill="#d81e06"></path></svg>

+ 1 - 0
web/src/components/gva-wfd/assets/icons/flow/exclusive-gateway.svg

@@ -0,0 +1 @@
+<svg t="1602254737585" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4833" width="200" height="200"><path d="M834.976 888.736C799.712 902.56 759.648 894.4 732.608 867.872L512.288 647.808 292.064 867.872C254.528 905.376 193.696 905.376 156.16 867.872 118.624 830.4 118.624 769.568 156.16 732.096L376.448 512 156.16 291.936C118.624 254.432 118.624 193.632 156.16 156.128 193.696 118.624 254.528 118.624 292.064 156.128L512.288 376.288 732.608 156.128C770.304 119.776 830.24 120.288 867.296 157.344 904.352 194.4 904.864 254.272 868.416 291.936L648.192 512 868.416 732.096C887.296 751.072 897.28 777.152 895.872 803.904 894.08 841.696 870.24 874.912 834.976 888.736" p-id="4834" fill="#1296db"></path></svg>

+ 1 - 0
web/src/components/gva-wfd/assets/icons/flow/icon_java.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1562576538887" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2055" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><defs><style type="text/css"></style></defs><path d="M431.4112 690.5856l-29.2864-85.4016h-1.024l-29.4912 85.4016h59.8016zM793.6 690.5856l-29.2864-85.4016h-1.024l-29.4912 85.4016h59.8016z" fill="#FF4D4F" p-id="2056"></path><path d="M1003.52 447.3856h-75.8784V245.76L688.64 0H154.4192a61.44 61.44 0 0 0-61.44 61.44v385.9456H20.48a20.48 20.48 0 0 0-20.48 20.48v404.48a20.48 20.48 0 0 0 20.48 20.48h72.704V962.56a61.44 61.44 0 0 0 61.44 61.44h711.5776a61.44 61.44 0 0 0 61.44-61.44v-69.7344H1003.52a20.48 20.48 0 0 0 20.48-20.48v-404.48a20.48 20.48 0 0 0-20.48-20.48zM164.6592 71.68H655.36v199.8848h200.6016v175.8208H164.6592z m687.616 693.8624H819.2L802.7136 716.8h-78.0288l-16.9984 48.8448h-32.5632l71.68-194.56h34.7136z m-185.0368-194.56l-66.2528 194.56H564.224l-66.2528-194.56h33.1776l51.2 158.8224h0.8192l51.2-158.8224z m-177.152 194.56h-32.5632L440.32 716.8h-77.824l-16.9984 48.8448h-32.5632l71.68-194.56h34.7136z m-261.9392-24.2688a26.0096 26.0096 0 0 0 22.4256-9.5232 51.2 51.2 0 0 0 7.0656-30.72V570.7776h30.72v131.7888a77.7216 77.7216 0 0 1-13.312 49.664 59.2896 59.2896 0 0 1-47.2064 17.2032 54.3744 54.3744 0 0 1-42.8032-16.384 64.4096 64.4096 0 0 1-14.848-45.056v-6.2464h30.72v6.0416q-0.4096 33.4848 27.2384 33.4848zM855.9616 952.32H164.6592v-59.4944h691.3024z" fill="#FF4D4F" p-id="2057"></path></svg>

+ 1 - 0
web/src/components/gva-wfd/assets/icons/flow/icon_mail.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1562576680973" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3906" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><defs><style type="text/css"></style></defs><path d="M924.444587 131.281999H99.555413c-52.566216 0-95.179756 42.61354-95.179756 95.179756v571.077513c0 52.566216 42.61354 95.179756 95.179756 95.179757h824.889174c52.566216 0 95.179756-42.61354 95.179756-95.179757V226.460732c0-52.565193-42.61354-95.178733-95.179756-95.178733z m-38.936801 180.405813L576.573691 592.536244c-36.614919 33.286104-92.532463 33.286104-129.147382 0L138.492214 311.687812c-13.07683-11.887748-14.039761-32.125675-2.152013-45.201482 11.887748-13.07683 32.124651-14.040784 45.201482-2.153036l308.934094 280.849456c12.204973 11.095709 30.843472 11.095709 43.048446 0L842.458317 264.333294c13.075807-11.887748 33.313733-10.923794 45.201482 2.153036 11.887748 13.075807 10.923794 33.313733-2.152013 45.201482z" p-id="3907" fill="#73D13D"></path></svg>

+ 1 - 0
web/src/components/gva-wfd/assets/icons/flow/icon_message.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1562816107305" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4997" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><defs><style type="text/css"></style></defs><path d="M288.010553 489.070778h447.978894c17.672498 0 31.998785 14.326287 31.998785 31.998785s-14.326287 31.998785-31.998785 31.998785H288.010553c-17.672498 0-31.998785-14.326287-31.998785-31.998785s14.326287-31.998785 31.998785-31.998785z m0-191.990662h447.978894c17.672498 0 31.998785 14.326287 31.998785 31.998785s-14.326287 31.998785-31.998785 31.998784H288.010553c-17.672498 0-31.998785-14.326287-31.998785-31.998784s14.326287-31.998785 31.998785-31.998785z m15.999392 573.115939l184.698583-143.654794a63.99757 63.99757 0 0 1 39.289841-13.481036h351.983563c17.672498 0 31.998785-14.326287 31.998785-31.998785V169.087023c0-17.672498-14.326287-31.998785-31.998785-31.998785H144.017044c-17.672498 0-31.998785 14.326287-31.998784 31.998785v511.975441c0 17.672498 14.326287 31.998785 31.998784 31.998784h95.995332c35.344996 0 63.996546 28.652574 63.996546 63.996547v93.13826z m223.989447-93.13826L317.478701 940.795996c-20.924565 16.274662-51.080376 12.504802-67.355037-8.419763a47.994084 47.994084 0 0 1-10.110265-29.468149V777.057795h-95.995331c-53.016471 0-95.995331-42.97886-95.995331-95.995331V169.087023c0-53.016471 42.97886-95.995331 95.995331-95.995331h735.964888c53.016471 0 95.995331 42.97886 95.995331 95.995331v511.975441c0 53.016471-42.97886 95.995331-95.995331 95.995331H527.999392z" p-id="4998" fill="#FA8C16"></path></svg>

+ 1 - 0
web/src/components/gva-wfd/assets/icons/flow/icon_receive.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1562577642275" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6190" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><defs><style type="text/css"></style></defs><path d="M320.009338 320.009338h255.987208c17.672498 0 31.998785 14.326287 31.998785 31.998784s-14.326287 31.998785-31.998785 31.998785H320.009338c-17.672498 0-31.998785-14.326287-31.998785-31.998785s14.326287-31.998785 31.998785-31.998784z m0-127.994116h127.994116c17.672498 0 31.998785 14.326287 31.998784 31.998785s-14.326287 31.998785-31.998784 31.998784H320.009338c-17.672498 0-31.998785-14.326287-31.998785-31.998784s14.326287-31.998785 31.998785-31.998785z m-95.995331 321.775564c99.272981 38.236859 220.397642 62.20576 287.985993 62.20576s188.713013-23.968901 287.985993-62.20576V160.016437c0-17.672498-14.326287-31.998785-31.998784-31.998785H256.011768c-17.672498 0-31.998785 14.326287-31.998785 31.998785v353.774349z m657.828296-38.262442c9.481955-10.229992 14.139022-19.690458 14.139022-27.52489 0-11.751648-10.475586-27.160593-31.998785-43.338041v80.656994a434.420087 434.420087 0 0 0 17.859763-9.794063zM64.021106 448.00243c0-44.844348 35.875069-86.097914 95.995331-118.776173V160.016437c0-53.016471 42.97886-95.995331 95.995331-95.995331h511.975441c53.016471 0 95.995331 42.97886 95.995331 95.995331v169.20982c60.120262 32.679283 95.995331 73.931826 95.995331 118.776173v415.98011c0 53.016471-42.97886 95.995331-95.995331 95.995331H160.016437c-53.016471 0-95.995331-42.97886-95.995331-95.995331V448.00243z m78.135568 27.525914a434.536744 434.536744 0 0 0 17.859763 9.79304v-80.656994c-21.523199 16.177448-31.998785 31.587416-31.998785 43.33804 0.001023 7.835456 4.657067 17.295921 14.139022 27.525914z" p-id="6191" fill="#ff85c0"></path></svg>

+ 1 - 0
web/src/components/gva-wfd/assets/icons/flow/icon_script.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1562576402250" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3677" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><defs><style type="text/css"></style></defs><path d="M607.995331 64.021106l287.985994 287.985993H639.994116c-17.672498 0-31.998785-14.326287-31.998785-31.998785V64.021106zM288.010553 735.989447c-17.672498 0-31.998785 14.326287-31.998785 31.998785s14.326287 31.998785 31.998785 31.998785h383.981325c17.672498 0 31.998785-14.326287 31.998784-31.998785s-14.326287-31.998785-31.998784-31.998785H288.010553z m0-159.992901c-17.672498 0-31.998785 14.326287-31.998785 31.998785s14.326287 31.998785 31.998785 31.998785h191.990662c17.672498 0 31.998785-14.326287 31.998785-31.998785s-14.326287-31.998785-31.998785-31.998785H288.010553z m607.970772-159.991877v447.978894c0 53.016471-42.97886 95.995331-95.995332 95.995331H224.014007c-53.016471 0-95.995331-42.97886-95.995332-95.995331V160.016437c0-53.016471 42.97886-95.995331 95.995332-95.995331h319.984778v273.765107c0 43.198871 35.019585 78.218456 78.218456 78.218456h273.764084z" p-id="3678" fill="#FFA940"></path></svg>

File diff suppressed because it is too large
+ 0 - 0
web/src/components/gva-wfd/assets/icons/flow/icon_setting.svg


+ 1 - 0
web/src/components/gva-wfd/assets/icons/flow/icon_signal.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1562816508671" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2780" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><defs><style type="text/css"></style></defs><path d="M512 716.8a101.888 101.888 0 0 0-101.4272 102.4c0 56.5248 45.4144 102.4 101.4272 102.4a101.888 101.888 0 0 0 101.376-102.4c0-56.576-45.3632-102.4-101.376-102.4z m-215.04-114.7904l71.68 72.3968a201.3696 201.3696 0 0 1 286.7712 0l71.6288-72.3968a301.9776 301.9776 0 0 0-430.08 0zM153.6 457.1136l71.68 72.3968a402.6368 402.6368 0 0 1 573.3888 0L870.4 457.1136a503.3472 503.3472 0 0 0-716.8 0zM10.1888 312.32l71.68 72.3968a604.0064 604.0064 0 0 1 860.2112 0L1013.76 312.32a704.6656 704.6656 0 0 0-1003.5712 0z" fill="#FA8C16" p-id="2781"></path></svg>

+ 1 - 0
web/src/components/gva-wfd/assets/icons/flow/icon_timer.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1562814915173" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1937" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><defs><style type="text/css"></style></defs><path d="M512 16C238 16 16 238 16 512s222 496 496 496 496-222 496-496S786 16 512 16z m0 896c-221 0-400-179-400-400S291 112 512 112s400 179 400 400-179 400-400 400z m123.6-208.8l-169.8-123.4c-6.2-4.6-9.8-11.8-9.8-19.4V232c0-13.2 10.8-24 24-24h64c13.2 0 24 10.8 24 24v283.4l133.6 97.2c10.8 7.8 13 22.8 5.2 33.6L669.2 698c-7.8 10.6-22.8 13-33.6 5.2z" fill="#FA8C16" p-id="1938"></path></svg>

+ 1 - 0
web/src/components/gva-wfd/assets/icons/flow/icon_user.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1562575958479" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3448" xmlns:xlink="http://www.w3.org/1999/xlink" width="64" height="64"><defs><style type="text/css"></style></defs><path d="M289.071721 313.25654c-1.350764-101.018741 12.281721-176.459944 106.60804-227.359194 19.306718-10.417257 65.041342-15.911388 86.877673-15.911388 23.923876-2.447748 0.23843-21.964244 53.016471-21.964245s126.176724 23.252587 161.259754 95.225805c35.08303 71.974241 38.052664 150.966317 38.052664 169.903622 0 18.938328 19.383466 9.468652 19.383466 37.875632 0 28.40698-8.361435 84.613096-38.10076 122.79265-29.740348 38.179554-39.432081 47.648207-58.815547 76.055187-19.383466 28.40698-29.075199 59.379389-29.075199 85.22094 0 94.689592 162.000628 101.68696 255.906368 161.484881 37.066197 23.603581 63.063291 54.19532 77.990258 91.774194 13.048177 32.848129-3.00238 70.053496-35.850509 83.101673a63.984267 63.984267 0 0 1-23.62507 4.520966l-781.415033 0.001024c-35.344996 0-63.99757-28.652574-63.997569-63.99757 0-8.118911 1.545192-16.163121 4.551666-23.704888 14.97097-37.543058 40.871873-68.107167 77.703732-91.694375 93.377714-59.798944 256.137635-66.796312 256.137635-161.484881 0-25.217335-9.691733-56.81396-29.075199-85.220941-19.383466-28.40698-29.075199-37.875632-58.149375-76.055186-29.075199-38.179554-38.766932-94.385671-38.766932-122.792651 0-28.40698 19.383466-18.938328 19.383466-37.771255z" p-id="3449" fill="#1890FF"></path></svg>

+ 1 - 0
web/src/components/gva-wfd/assets/icons/flow/inclusive-gateway.svg

@@ -0,0 +1 @@
+<svg t="1602254842777" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6767" width="200" height="200"><path d="M512.005117 958.708971C265.683035 958.708971 65.290005 758.316965 65.290005 511.99386c0-246.310825 200.39303-446.703855 446.715111-446.703855 246.310825 0 446.703855 200.39303 446.703855 446.703855C958.708971 758.316965 758.316965 958.708971 512.005117 958.708971zM512.005117 169.716356c-188.738595 0-342.289784 153.545048-342.289784 342.277504 0 188.738595 153.551188 342.289784 342.289784 342.289784 188.733479 0 342.278527-153.551188 342.278527-342.289784C854.283644 323.261405 700.738595 169.716356 512.005117 169.716356z" p-id="6768" fill="#1296db"></path></svg>

+ 1 - 0
web/src/components/gva-wfd/assets/icons/flow/parallel-gateway.svg

@@ -0,0 +1 @@
+<svg t="1602254810649" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5751" width="200" height="200"><path d="M925.696 384q19.456 0 37.376 7.68t30.72 20.48 20.48 30.72 7.68 37.376q0 20.48-7.68 37.888t-20.48 30.208-30.72 20.48-37.376 7.68l-287.744 0 0 287.744q0 20.48-7.68 37.888t-20.48 30.208-30.72 20.48-37.376 7.68q-20.48 0-37.888-7.68t-30.208-20.48-20.48-30.208-7.68-37.888l0-287.744-287.744 0q-20.48 0-37.888-7.68t-30.208-20.48-20.48-30.208-7.68-37.888q0-19.456 7.68-37.376t20.48-30.72 30.208-20.48 37.888-7.68l287.744 0 0-287.744q0-19.456 7.68-37.376t20.48-30.72 30.208-20.48 37.888-7.68q39.936 0 68.096 28.16t28.16 68.096l0 287.744 287.744 0z" p-id="5752" fill="#1296db"></path></svg>

+ 1 - 0
web/src/components/gva-wfd/assets/icons/flow/start.svg

@@ -0,0 +1 @@
+<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1602254224459" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2402" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M256 896V128l512 384z" p-id="2403" fill="#FA8C16"></path></svg>

+ 167 - 0
web/src/components/gva-wfd/assets/icons/model/icon_arch.svg

@@ -0,0 +1,167 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Icon_Set" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 64 64" style="enable-background:new 0 0 64 64;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#263238;}
+	.st1{fill:#FFD740;}
+	.st2{fill:#FF5252;}
+	.st3{fill:#40C4FF;}
+	.st4{fill:#4DB6AC;}
+	.st5{fill:#FFFFFF;}
+	.st6{fill:#FFD345;}
+</style>
+<g>
+	<g>
+		<path class="st1" d="M57.5,57h-12c-0.552,0-1-0.448-1-1V40c0-0.552,0.448-1,1-1h12c0.552,0,1,0.448,1,1v16
+			C58.5,56.552,58.052,57,57.5,57z"/>
+		<path class="st0" d="M57.5,57.5h-12c-0.827,0-1.5-0.673-1.5-1.5V40c0-0.827,0.673-1.5,1.5-1.5h12c0.827,0,1.5,0.673,1.5,1.5v16
+			C59,56.827,58.327,57.5,57.5,57.5z M45.5,39.5c-0.276,0-0.5,0.224-0.5,0.5v16c0,0.276,0.224,0.5,0.5,0.5h12
+			c0.276,0,0.5-0.224,0.5-0.5V40c0-0.276-0.224-0.5-0.5-0.5H45.5z"/>
+	</g>
+	<g>
+		<path class="st0" d="M23,54.003h-1c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h1c0.276,0,0.5,0.224,0.5,0.5
+			S23.276,54.003,23,54.003z"/>
+	</g>
+	<g>
+		<path class="st0" d="M20,54.003H9c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h11c0.276,0,0.5,0.224,0.5,0.5
+			S20.276,54.003,20,54.003z"/>
+	</g>
+	<g>
+		<path class="st0" d="M21.5,9.5h-4C17.224,9.5,17,9.276,17,9s0.224-0.5,0.5-0.5h4C21.776,8.5,22,8.724,22,9S21.776,9.5,21.5,9.5z"
+			/>
+	</g>
+	<g>
+		<path class="st0" d="M19.5,11.5c-0.276,0-0.5-0.224-0.5-0.5V7c0-0.276,0.224-0.5,0.5-0.5S20,6.724,20,7v4
+			C20,11.276,19.776,11.5,19.5,11.5z"/>
+	</g>
+	<g>
+		<path class="st0" d="M14.5,16h-4c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h4c0.276,0,0.5,0.224,0.5,0.5S14.776,16,14.5,16z"
+			/>
+	</g>
+	<g>
+		<path class="st0" d="M12.5,18c-0.276,0-0.5-0.224-0.5-0.5v-4c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v4
+			C13,17.776,12.776,18,12.5,18z"/>
+	</g>
+	<g>
+		<path class="st0" d="M55.25,50.5h-7.5c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h7.5c0.276,0,0.5,0.224,0.5,0.5
+			S55.526,50.5,55.25,50.5z"/>
+	</g>
+	<g>
+		<path class="st0" d="M51.5,53.5h-3.75c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h3.75c0.276,0,0.5,0.224,0.5,0.5
+			S51.776,53.5,51.5,53.5z"/>
+	</g>
+	<g>
+		<path class="st3" d="M25.52,31.5H6.48c-0.541,0-0.98-0.336-0.98-0.75v-4.5c0-0.414,0.439-0.75,0.98-0.75H25.52
+			c0.541,0,0.98,0.336,0.98,0.75v4.5C26.5,31.164,26.061,31.5,25.52,31.5z"/>
+		<path class="st0" d="M25.521,32H6.479C5.65,32,5,31.451,5,30.75v-4.5C5,25.549,5.65,25,6.479,25h19.041
+			C26.35,25,27,25.549,27,26.25v4.5C27,31.451,26.35,32,25.521,32z M6.479,26C6.182,26,6,26.162,6,26.25v4.5
+			C6,30.838,6.182,31,6.479,31h19.041C25.818,31,26,30.838,26,30.75v-4.5c0-0.088-0.182-0.25-0.479-0.25H6.479z"/>
+	</g>
+	<g>
+		<path class="st0" d="M22.75,29h-5.5c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h5.5c0.276,0,0.5,0.224,0.5,0.5
+			S23.026,29,22.75,29z"/>
+	</g>
+	<g>
+		<rect x="9.25" y="27.5" class="st5" width="4" height="2"/>
+		<path class="st0" d="M13.25,30h-4c-0.276,0-0.5-0.224-0.5-0.5v-2c0-0.276,0.224-0.5,0.5-0.5h4c0.276,0,0.5,0.224,0.5,0.5v2
+			C13.75,29.776,13.526,30,13.25,30z M9.75,29h3v-1h-3V29z"/>
+	</g>
+	<g>
+		<path class="st3" d="M25.52,37.5H6.48c-0.541,0-0.98-0.336-0.98-0.75v-4.5c0-0.414,0.439-0.75,0.98-0.75H25.52
+			c0.541,0,0.98,0.336,0.98,0.75v4.5C26.5,37.164,26.061,37.5,25.52,37.5z"/>
+		<path class="st0" d="M25.521,38H6.479C5.65,38,5,37.451,5,36.75v-4.5C5,31.549,5.65,31,6.479,31h19.041
+			C26.35,31,27,31.549,27,32.25v4.5C27,37.451,26.35,38,25.521,38z M6.479,32C6.182,32,6,32.162,6,32.25v4.5
+			C6,36.838,6.182,37,6.479,37h19.041C25.818,37,26,36.838,26,36.75v-4.5c0-0.088-0.182-0.25-0.479-0.25H6.479z"/>
+	</g>
+	<g>
+		<path class="st0" d="M22.75,35h-5.5c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h5.5c0.276,0,0.5,0.224,0.5,0.5
+			S23.026,35,22.75,35z"/>
+	</g>
+	<g>
+		<rect x="9.25" y="33.5" class="st5" width="4" height="2"/>
+		<path class="st0" d="M13.25,36h-4c-0.276,0-0.5-0.224-0.5-0.5v-2c0-0.276,0.224-0.5,0.5-0.5h4c0.276,0,0.5,0.224,0.5,0.5v2
+			C13.75,35.776,13.526,36,13.25,36z M9.75,35h3v-1h-3V35z"/>
+	</g>
+	<g>
+		<path class="st3" d="M25.52,43.5H6.48c-0.541,0-0.98-0.336-0.98-0.75v-4.5c0-0.414,0.439-0.75,0.98-0.75H25.52
+			c0.541,0,0.98,0.336,0.98,0.75v4.5C26.5,43.164,26.061,43.5,25.52,43.5z"/>
+		<path class="st0" d="M25.521,44H6.479C5.65,44,5,43.451,5,42.75v-4.5C5,37.549,5.65,37,6.479,37h19.041
+			C26.35,37,27,37.549,27,38.25v4.5C27,43.451,26.35,44,25.521,44z M6.479,38C6.182,38,6,38.162,6,38.25v4.5
+			C6,42.838,6.182,43,6.479,43h19.041C25.818,43,26,42.838,26,42.75v-4.5c0-0.088-0.182-0.25-0.479-0.25H6.479z"/>
+	</g>
+	<g>
+		<path class="st0" d="M22.75,41h-5.5c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h5.5c0.276,0,0.5,0.224,0.5,0.5
+			S23.026,41,22.75,41z"/>
+	</g>
+	<g>
+		<rect x="9.25" y="39.5" class="st5" width="4" height="2"/>
+		<path class="st0" d="M13.25,42h-4c-0.276,0-0.5-0.224-0.5-0.5v-2c0-0.276,0.224-0.5,0.5-0.5h4c0.276,0,0.5,0.224,0.5,0.5v2
+			C13.75,41.776,13.526,42,13.25,42z M9.75,41h3v-1h-3V41z"/>
+	</g>
+	<g>
+		<path class="st0" d="M25,49H7c-0.276,0-0.5-0.224-0.5-0.5S6.724,48,7,48h18c0.276,0,0.5,0.224,0.5,0.5S25.276,49,25,49z"/>
+	</g>
+	<g>
+		<path class="st0" d="M11,47c-0.276,0-0.5-0.224-0.5-0.5v-3c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v3
+			C11.5,46.776,11.276,47,11,47z"/>
+	</g>
+	<g>
+		<path class="st0" d="M21,47c-0.276,0-0.5-0.224-0.5-0.5v-3c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v3
+			C21.5,46.776,21.276,47,21,47z"/>
+	</g>
+	<g>
+		<path class="st1" d="M57.5,30h-12c-0.552,0-1-0.448-1-1V13c0-0.552,0.448-1,1-1h12c0.552,0,1,0.448,1,1v16
+			C58.5,29.552,58.052,30,57.5,30z"/>
+		<path class="st0" d="M57.5,30.5h-12c-0.827,0-1.5-0.673-1.5-1.5V13c0-0.827,0.673-1.5,1.5-1.5h12c0.827,0,1.5,0.673,1.5,1.5v16
+			C59,29.827,58.327,30.5,57.5,30.5z M45.5,12.5c-0.276,0-0.5,0.224-0.5,0.5v16c0,0.276,0.224,0.5,0.5,0.5h12
+			c0.276,0,0.5-0.224,0.5-0.5V13c0-0.276-0.224-0.5-0.5-0.5H45.5z"/>
+	</g>
+	<g>
+		<line class="st5" x1="51" y1="20" x2="52" y2="15"/>
+		<path class="st0" d="M51,20.5c-0.032,0-0.065-0.003-0.099-0.01c-0.271-0.054-0.446-0.317-0.392-0.588l1-5
+			c0.054-0.271,0.314-0.448,0.588-0.392c0.271,0.054,0.446,0.317,0.392,0.588l-1,5C51.443,20.336,51.234,20.5,51,20.5z"/>
+	</g>
+	<g>
+		<path class="st0" d="M55.25,23.5h-7.5c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h7.5c0.276,0,0.5,0.224,0.5,0.5
+			S55.526,23.5,55.25,23.5z"/>
+	</g>
+	<g>
+		<path class="st0" d="M51.5,26.5h-3.75c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h3.75c0.276,0,0.5,0.224,0.5,0.5
+			S51.776,26.5,51.5,26.5z"/>
+	</g>
+	<g>
+		<path class="st0" d="M42.5,48.5H37c-1.654,0-3-1.346-3-3v-22c0-1.654,1.346-3,3-3h5.5c0.276,0,0.5,0.224,0.5,0.5
+			s-0.224,0.5-0.5,0.5H37c-1.103,0-2,0.897-2,2v22c0,1.103,0.897,2,2,2h5.5c0.276,0,0.5,0.224,0.5,0.5S42.776,48.5,42.5,48.5z"/>
+	</g>
+	<g>
+		<path class="st0" d="M34.5,35h-6c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h6c0.276,0,0.5,0.224,0.5,0.5S34.776,35,34.5,35z"
+			/>
+	</g>
+	<g>
+		<path class="st0" d="M49.25,20c-0.128,0-0.256-0.049-0.354-0.146l-2-2c-0.195-0.195-0.195-0.512,0-0.707l2-2
+			c0.195-0.195,0.512-0.195,0.707,0s0.195,0.512,0,0.707L47.957,17.5l1.646,1.646c0.195,0.195,0.195,0.512,0,0.707
+			C49.506,19.951,49.378,20,49.25,20z"/>
+	</g>
+	<g>
+		<path class="st0" d="M53.75,20c-0.128,0-0.256-0.049-0.354-0.146c-0.195-0.195-0.195-0.512,0-0.707l1.646-1.646l-1.646-1.646
+			c-0.195-0.195-0.195-0.512,0-0.707s0.512-0.195,0.707,0l2,2c0.195,0.195,0.195,0.512,0,0.707l-2,2
+			C54.006,19.951,53.878,20,53.75,20z"/>
+	</g>
+	<g>
+		<line class="st5" x1="51" y1="47" x2="52" y2="42"/>
+		<path class="st0" d="M51,47.5c-0.032,0-0.065-0.003-0.099-0.01c-0.271-0.054-0.446-0.317-0.392-0.588l1-5
+			c0.054-0.271,0.314-0.45,0.588-0.392c0.271,0.054,0.446,0.317,0.392,0.588l-1,5C51.443,47.336,51.234,47.5,51,47.5z"/>
+	</g>
+	<g>
+		<path class="st0" d="M49.25,47c-0.128,0-0.256-0.049-0.354-0.146l-2-2c-0.195-0.195-0.195-0.512,0-0.707l2-2
+			c0.195-0.195,0.512-0.195,0.707,0s0.195,0.512,0,0.707L47.957,44.5l1.646,1.646c0.195,0.195,0.195,0.512,0,0.707
+			C49.506,46.951,49.378,47,49.25,47z"/>
+	</g>
+	<g>
+		<path class="st0" d="M53.75,47c-0.128,0-0.256-0.049-0.354-0.146c-0.195-0.195-0.195-0.512,0-0.707l1.646-1.646l-1.646-1.646
+			c-0.195-0.195-0.195-0.512,0-0.707s0.512-0.195,0.707,0l2,2c0.195,0.195,0.195,0.512,0,0.707l-2,2
+			C54.006,46.951,53.878,47,53.75,47z"/>
+	</g>
+</g>
+</svg>

+ 92 - 0
web/src/components/gva-wfd/assets/icons/model/icon_cluster.svg

@@ -0,0 +1,92 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Icon_Set" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 64 64" style="enable-background:new 0 0 64 64;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#263238;}
+	.st1{fill:#FFD740;}
+	.st2{fill:#FF5252;}
+	.st3{fill:#40C4FF;}
+	.st4{fill:#4DB6AC;}
+	.st5{fill:#FFFFFF;}
+	.st6{fill:#FFD345;}
+</style>
+<g>
+	<g>
+		<path class="st0" d="M60,56.75H4c-0.276,0-0.5-0.224-0.5-0.5v-51c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v50.5H60
+			c0.276,0,0.5,0.224,0.5,0.5S60.276,56.75,60,56.75z"/>
+	</g>
+	<g>
+		<path class="st0" d="M12,59.25c-0.276,0-0.5-0.224-0.5-0.5v-2.5c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v2.5
+			C12.5,59.026,12.276,59.25,12,59.25z"/>
+	</g>
+	<g>
+		<path class="st0" d="M22,59.25c-0.276,0-0.5-0.224-0.5-0.5v-2.5c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v2.5
+			C22.5,59.026,22.276,59.25,22,59.25z"/>
+	</g>
+	<g>
+		<path class="st0" d="M32,59.25c-0.276,0-0.5-0.224-0.5-0.5v-2.5c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v2.5
+			C32.5,59.026,32.276,59.25,32,59.25z"/>
+	</g>
+	<g>
+		<path class="st0" d="M42,59.25c-0.276,0-0.5-0.224-0.5-0.5v-2.5c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v2.5
+			C42.5,59.026,42.276,59.25,42,59.25z"/>
+	</g>
+	<g>
+		<path class="st0" d="M52,59.25c-0.276,0-0.5-0.224-0.5-0.5v-2.5c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v2.5
+			C52.5,59.026,52.276,59.25,52,59.25z"/>
+	</g>
+	<g>
+		<rect x="12" y="41.25" class="st1" width="5" height="5"/>
+		<path class="st0" d="M17,46.75h-5c-0.276,0-0.5-0.224-0.5-0.5v-5c0-0.276,0.224-0.5,0.5-0.5h5c0.276,0,0.5,0.224,0.5,0.5v5
+			C17.5,46.526,17.276,46.75,17,46.75z M12.5,45.75h4v-4h-4V45.75z"/>
+	</g>
+	<g>
+		<rect x="26" y="42.25" class="st1" width="5" height="5"/>
+		<path class="st0" d="M31,47.75h-5c-0.276,0-0.5-0.224-0.5-0.5v-5c0-0.276,0.224-0.5,0.5-0.5h5c0.276,0,0.5,0.224,0.5,0.5v5
+			C31.5,47.526,31.276,47.75,31,47.75z M26.5,46.75h4v-4h-4V46.75z"/>
+	</g>
+	<g>
+		<rect x="15" y="28.25" class="st1" width="5" height="5"/>
+		<path class="st0" d="M20,33.75h-5c-0.276,0-0.5-0.224-0.5-0.5v-5c0-0.276,0.224-0.5,0.5-0.5h5c0.276,0,0.5,0.224,0.5,0.5v5
+			C20.5,33.526,20.276,33.75,20,33.75z M15.5,32.75h4v-4h-4V32.75z"/>
+	</g>
+	<g>
+		<rect x="25" y="31.25" class="st1" width="5" height="5"/>
+		<path class="st0" d="M30,36.75h-5c-0.276,0-0.5-0.224-0.5-0.5v-5c0-0.276,0.224-0.5,0.5-0.5h5c0.276,0,0.5,0.224,0.5,0.5v5
+			C30.5,36.526,30.276,36.75,30,36.75z M25.5,35.75h4v-4h-4V35.75z"/>
+	</g>
+	<g>
+		<path class="st0" d="M18.414,13.269c-0.128,0-0.256-0.049-0.354-0.146l-1.414-1.414c-0.195-0.195-0.195-0.512,0-0.707
+			s0.512-0.195,0.707,0l1.414,1.414c0.195,0.195,0.195,0.512,0,0.707C18.67,13.22,18.542,13.269,18.414,13.269z"/>
+	</g>
+	<g>
+		<path class="st0" d="M46.172,41.026c-0.128,0-0.256-0.049-0.354-0.146L19.475,14.537c-0.195-0.195-0.195-0.512,0-0.707
+			s0.512-0.195,0.707,0l26.343,26.343c0.195,0.195,0.195,0.512,0,0.707C46.428,40.977,46.3,41.026,46.172,41.026z"/>
+	</g>
+	<g>
+		<path class="st0" d="M49,43.854c-0.128,0-0.256-0.049-0.354-0.146l-1.414-1.414c-0.195-0.195-0.195-0.512,0-0.707
+			s0.512-0.195,0.707,0l1.414,1.414c0.195,0.195,0.195,0.512,0,0.707C49.256,43.806,49.128,43.854,49,43.854z"/>
+	</g>
+	<g>
+		<circle class="st2" cx="30.5" cy="11.75" r="2.5"/>
+		<path class="st0" d="M30.5,14.75c-1.654,0-3-1.346-3-3s1.346-3,3-3s3,1.346,3,3S32.154,14.75,30.5,14.75z M30.5,9.75
+			c-1.103,0-2,0.897-2,2s0.897,2,2,2s2-0.897,2-2S31.603,9.75,30.5,9.75z"/>
+	</g>
+	<g>
+		<circle class="st2" cx="47.5" cy="29.25" r="2.5"/>
+		<path class="st0" d="M47.5,32.25c-1.654,0-3-1.346-3-3s1.346-3,3-3s3,1.346,3,3S49.154,32.25,47.5,32.25z M47.5,27.25
+			c-1.103,0-2,0.897-2,2s0.897,2,2,2s2-0.897,2-2S48.603,27.25,47.5,27.25z"/>
+	</g>
+	<g>
+		<circle class="st2" cx="47.5" cy="17.25" r="2.5"/>
+		<path class="st0" d="M47.5,20.25c-1.654,0-3-1.346-3-3s1.346-3,3-3s3,1.346,3,3S49.154,20.25,47.5,20.25z M47.5,15.25
+			c-1.103,0-2,0.897-2,2s0.897,2,2,2s2-0.897,2-2S48.603,15.25,47.5,15.25z"/>
+	</g>
+	<g>
+		<circle class="st2" cx="38.5" cy="17.25" r="2.5"/>
+		<path class="st0" d="M38.5,20.25c-1.654,0-3-1.346-3-3s1.346-3,3-3s3,1.346,3,3S40.154,20.25,38.5,20.25z M38.5,15.25
+			c-1.103,0-2,0.897-2,2s0.897,2,2,2s2-0.897,2-2S39.603,15.25,38.5,15.25z"/>
+	</g>
+</g>
+</svg>

+ 88 - 0
web/src/components/gva-wfd/assets/icons/model/icon_data.svg

@@ -0,0 +1,88 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Icon_Set" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 64 64" style="enable-background:new 0 0 64 64;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#263238;}
+	.st1{fill:#FFD740;}
+	.st2{fill:#FF5252;}
+	.st3{fill:#40C4FF;}
+	.st4{fill:#4DB6AC;}
+	.st5{fill:#FFFFFF;}
+	.st6{fill:#FFD345;}
+</style>
+<g>
+	<g>
+		<rect x="17" y="21" class="st3" width="8" height="8"/>
+		<path class="st0" d="M25,29.5h-8c-0.276,0-0.5-0.224-0.5-0.5v-8c0-0.276,0.224-0.5,0.5-0.5h8c0.276,0,0.5,0.224,0.5,0.5v8
+			C25.5,29.276,25.276,29.5,25,29.5z M17.5,28.5h7v-7h-7V28.5z"/>
+	</g>
+	<g>
+		<rect x="30" y="17" class="st1" width="8" height="8"/>
+		<path class="st0" d="M38,25.5h-8c-0.276,0-0.5-0.224-0.5-0.5v-8c0-0.276,0.224-0.5,0.5-0.5h8c0.276,0,0.5,0.224,0.5,0.5v8
+			C38.5,25.276,38.276,25.5,38,25.5z M30.5,24.5h7v-7h-7V24.5z"/>
+	</g>
+	<g>
+		<rect x="47.5" y="22" class="st1" width="8" height="8"/>
+		<path class="st0" d="M55.5,30.5h-8c-0.276,0-0.5-0.224-0.5-0.5v-8c0-0.276,0.224-0.5,0.5-0.5h8c0.276,0,0.5,0.224,0.5,0.5v8
+			C56,30.276,55.776,30.5,55.5,30.5z M48,29.5h7v-7h-7V29.5z"/>
+	</g>
+	<g>
+		<rect x="31" y="44" class="st1" width="8" height="8"/>
+		<path class="st0" d="M39,52.5h-8c-0.276,0-0.5-0.224-0.5-0.5v-8c0-0.276,0.224-0.5,0.5-0.5h8c0.276,0,0.5,0.224,0.5,0.5v8
+			C39.5,52.276,39.276,52.5,39,52.5z M31.5,51.5h7v-7h-7V51.5z"/>
+	</g>
+	<g>
+		<path class="st0" d="M30,41.5h-8c-0.276,0-0.5-0.224-0.5-0.5v-8c0-0.276,0.224-0.5,0.5-0.5h8c0.276,0,0.5,0.224,0.5,0.5v8
+			C30.5,41.276,30.276,41.5,30,41.5z M22.5,40.5h7v-7h-7V40.5z"/>
+	</g>
+	<g>
+		<rect x="35" y="29" class="st2" width="8" height="8"/>
+		<path class="st0" d="M43,37.5h-8c-0.276,0-0.5-0.224-0.5-0.5v-8c0-0.276,0.224-0.5,0.5-0.5h8c0.276,0,0.5,0.224,0.5,0.5v8
+			C43.5,37.276,43.276,37.5,43,37.5z M35.5,36.5h7v-7h-7V36.5z"/>
+	</g>
+	<g>
+		<rect x="43.5" y="39" class="st3" width="8" height="8"/>
+		<path class="st0" d="M51.5,47.5h-8c-0.276,0-0.5-0.224-0.5-0.5v-8c0-0.276,0.224-0.5,0.5-0.5h8c0.276,0,0.5,0.224,0.5,0.5v8
+			C52,47.276,51.776,47.5,51.5,47.5z M44,46.5h7v-7h-7V46.5z"/>
+	</g>
+	<g>
+		<path class="st0" d="M9.5,54.188H9c-1.654,0-3-1.346-3-3v-43c0-1.654,1.346-3,3-3h31c1.654,0,3,1.346,3,3v0.5
+			c0,0.276-0.224,0.5-0.5,0.5S42,8.964,42,8.688v-0.5c0-1.103-0.897-2-2-2H9c-1.103,0-2,0.897-2,2v43c0,1.103,0.897,2,2,2h0.5
+			c0.276,0,0.5,0.224,0.5,0.5S9.776,54.188,9.5,54.188z"/>
+	</g>
+	<g>
+		<path class="st0" d="M45,59.188H14c-1.654,0-3-1.346-3-3v-43c0-1.654,1.346-3,3-3h31c1.654,0,3,1.346,3,3v7
+			c0,0.276-0.224,0.5-0.5,0.5s-0.5-0.224-0.5-0.5v-7c0-1.103-0.897-2-2-2H14c-1.103,0-2,0.897-2,2v43c0,1.103,0.897,2,2,2h31
+			c1.103,0,2-0.897,2-2v-7c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v7C48,57.842,46.654,59.188,45,59.188z"/>
+	</g>
+	<g>
+		<path class="st0" d="M47.5,37.5c-0.276,0-0.5-0.224-0.5-0.5v-5c0-0.276,0.224-0.5,0.5-0.5S48,31.724,48,32v5
+			C48,37.276,47.776,37.5,47.5,37.5z"/>
+	</g>
+	<g>
+		<path class="st0" d="M17.5,62.5h-2c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h2c0.276,0,0.5,0.224,0.5,0.5
+			S17.776,62.5,17.5,62.5z"/>
+	</g>
+	<g>
+		<path class="st0" d="M39.5,62.5h-20c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h20c0.276,0,0.5,0.224,0.5,0.5
+			S39.776,62.5,39.5,62.5z"/>
+	</g>
+	<g>
+		<path class="st0" d="M50.5,4.5h-4C46.224,4.5,46,4.276,46,4s0.224-0.5,0.5-0.5h4C50.776,3.5,51,3.724,51,4S50.776,4.5,50.5,4.5z"
+			/>
+	</g>
+	<g>
+		<path class="st0" d="M48.5,6.5C48.224,6.5,48,6.276,48,6V2c0-0.276,0.224-0.5,0.5-0.5S49,1.724,49,2v4
+			C49,6.276,48.776,6.5,48.5,6.5z"/>
+	</g>
+	<g>
+		<path class="st0" d="M57.5,11h-4c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h4c0.276,0,0.5,0.224,0.5,0.5S57.776,11,57.5,11z"
+			/>
+	</g>
+	<g>
+		<path class="st0" d="M55.5,13c-0.276,0-0.5-0.224-0.5-0.5v-4C55,8.224,55.224,8,55.5,8S56,8.224,56,8.5v4
+			C56,12.776,55.776,13,55.5,13z"/>
+	</g>
+</g>
+</svg>

+ 134 - 0
web/src/components/gva-wfd/assets/icons/model/icon_decision.svg

@@ -0,0 +1,134 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Icon_Set" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 64 64" style="enable-background:new 0 0 64 64;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#263238;}
+	.st1{fill:#FFD740;}
+	.st2{fill:#FF5252;}
+	.st3{fill:#40C4FF;}
+	.st4{fill:#4DB6AC;}
+	.st5{fill:#FFFFFF;}
+	.st6{fill:#FFD345;}
+</style>
+<g>
+	<g>
+		<path class="st0" d="M44,61.5c-0.276,0-0.5-0.224-0.5-0.5v-9.75c0-1.777-1.049-3.392-2.673-4.112l-5.667-2.519
+			c-0.776,0.932-1.929,1.477-3.156,1.477c-0.002,0-0.004,0-0.005,0c-1.231-0.001-2.385-0.55-3.16-1.485l-5.671,2.525
+			c-1.62,0.723-2.668,2.336-2.668,4.11V61c0,0.276-0.224,0.5-0.5,0.5s-0.5-0.224-0.5-0.5v-9.754c0-2.169,1.28-4.141,3.262-5.024
+			l6.035-2.688c0.24-0.108,0.531-0.007,0.65,0.233c0.557,0.825,1.498,1.328,2.553,1.329c0.001,0,0.002,0,0.004,0
+			c1.053,0,1.993-0.5,2.581-1.374c0.137-0.2,0.398-0.275,0.618-0.178l6.03,2.68c1.984,0.881,3.267,2.854,3.267,5.026V61
+			C44.5,61.276,44.276,61.5,44,61.5z"/>
+	</g>
+	<g>
+		<path class="st0" d="M32,43.495c-4.687,0-8.5-3.813-8.5-8.5s3.813-8.5,8.5-8.5s8.5,3.813,8.5,8.5S36.687,43.495,32,43.495z
+			 M32,27.495c-4.136,0-7.5,3.364-7.5,7.5s3.364,7.5,7.5,7.5s7.5-3.364,7.5-7.5S36.136,27.495,32,27.495z"/>
+	</g>
+	<g>
+		<path class="st0" d="M24.755,35.495H24c-0.276,0-0.5-0.224-0.5-0.5c0-4.687,3.813-8.5,8.5-8.5c2.526,0,4.905,1.115,6.527,3.059
+			c0.085,0.102,0.126,0.233,0.114,0.366c-0.013,0.132-0.076,0.254-0.179,0.339l-0.597,0.495
+			C34.191,33.812,29.535,35.495,24.755,35.495z M24.517,34.495h0.238c4.547,0,8.977-1.602,12.473-4.511l0,0l0.198-0.164
+			c-1.414-1.483-3.364-2.325-5.426-2.325C28.032,27.495,24.774,30.592,24.517,34.495z"/>
+	</g>
+	<g>
+		<path class="st0" d="M25,61.5c-0.276,0-0.5-0.224-0.5-0.5v-7c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v7
+			C25.5,61.276,25.276,61.5,25,61.5z"/>
+	</g>
+	<g>
+		<path class="st0" d="M39,61.5c-0.276,0-0.5-0.224-0.5-0.5v-7c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v7
+			C39.5,61.276,39.276,61.5,39,61.5z"/>
+	</g>
+	<g>
+		<path class="st0" d="M32.004,46.096c-0.001,0-0.003,0-0.004,0c-1.373-0.001-2.648-0.683-3.413-1.823
+			c-0.056-0.082-0.087-0.182-0.087-0.281v-1.579c0-0.341,0.382-0.589,0.697-0.464c1.8,0.728,3.817,0.725,5.615-0.003
+			c0.152-0.064,0.328-0.045,0.467,0.048c0.138,0.093,0.221,0.249,0.221,0.415v1.593c0,0.1-0.029,0.196-0.085,0.279
+			C34.649,45.417,33.374,46.096,32.004,46.096z M29.5,43.833c0.592,0.805,1.495,1.262,2.501,1.263c0.001,0,0.002,0,0.003,0
+			c1.003,0,1.904-0.454,2.496-1.253v-0.726c-1.621,0.502-3.379,0.502-5,0V43.833z"/>
+	</g>
+	<g>
+		<path class="st1" d="M37.001,11.995c0-3.115-2.848-5.565-6.083-4.886c-1.908,0.4-3.437,1.958-3.816,3.87
+			c-0.336,1.696,0.191,3.263,1.214,4.372c0.725,0.786,1.185,1.778,1.185,2.848v0.297h5V18.2c0-1.075,0.47-2.068,1.196-2.861
+			C36.505,14.456,37.001,13.287,37.001,11.995z"/>
+		<path class="st0" d="M34.501,18.995h-5c-0.276,0-0.5-0.224-0.5-0.5v-0.297c0-0.881-0.374-1.771-1.053-2.509
+			c-1.198-1.299-1.685-3.052-1.337-4.808c0.418-2.11,2.107-3.823,4.204-4.262c1.661-0.349,3.356,0.052,4.65,1.104
+			c1.293,1.05,2.035,2.607,2.035,4.271c0,1.362-0.51,2.67-1.435,3.682c-0.486,0.53-1.065,1.398-1.065,2.523v0.295
+			C35.001,18.771,34.777,18.995,34.501,18.995z M29.996,17.995h4.01c0.052-1.071,0.506-2.103,1.322-2.994
+			c0.757-0.826,1.173-1.894,1.173-3.006c0-1.362-0.607-2.636-1.665-3.495c-1.06-0.859-2.45-1.187-3.815-0.901
+			c-1.71,0.358-3.087,1.755-3.429,3.478c-0.285,1.439,0.112,2.874,1.091,3.936C29.482,15.879,29.943,16.931,29.996,17.995z"/>
+	</g>
+	<g>
+		<path class="st0" d="M32.001,23.995c-1.654,0-3-1.346-3-3v-2.5c0-0.276,0.224-0.5,0.5-0.5h5c0.276,0,0.5,0.224,0.5,0.5v2.5
+			C35.001,22.649,33.655,23.995,32.001,23.995z M30.001,18.995v2c0,1.103,0.897,2,2,2s2-0.897,2-2v-2H30.001z"/>
+	</g>
+	<g>
+		<path class="st0" d="M34.501,21.495h-5c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h5c0.276,0,0.5,0.224,0.5,0.5
+			S34.777,21.495,34.501,21.495z"/>
+	</g>
+	<g>
+		<path class="st0" d="M32.001,18.995c-0.276,0-0.5-0.224-0.5-0.5v-5c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v5
+			C32.501,18.771,32.277,18.995,32.001,18.995z"/>
+	</g>
+	<g>
+		<circle class="st2" cx="10.5" cy="29" r="10"/>
+		<path class="st0" d="M10.5,39.5C4.71,39.5,0,34.79,0,29s4.71-10.5,10.5-10.5S21,23.21,21,29S16.29,39.5,10.5,39.5z M10.5,19.5
+			C5.262,19.5,1,23.762,1,29s4.262,9.5,9.5,9.5S20,34.238,20,29S15.738,19.5,10.5,19.5z"/>
+	</g>
+	<g>
+		<path class="st0" d="M10.5,31.5c-0.276,0-0.5-0.224-0.5-0.5v-1.5c0-0.276,0.224-0.5,0.5-0.5c1.379,0,2.5-1.121,2.5-2.5
+			S11.879,24,10.5,24S8,25.121,8,26.5C8,26.776,7.776,27,7.5,27S7,26.776,7,26.5c0-1.93,1.57-3.5,3.5-3.5s3.5,1.57,3.5,3.5
+			c0,1.76-1.306,3.221-3,3.465V31C11,31.276,10.776,31.5,10.5,31.5z"/>
+	</g>
+	<g>
+		<circle class="st0" cx="10.5" cy="33.75" r="0.75"/>
+	</g>
+	<g>
+		<ellipse transform="matrix(0.7071 -0.7071 0.7071 0.7071 -4.8363 46.3241)" class="st4" cx="53.5" cy="29" rx="10" ry="10"/>
+		<path class="st0" d="M53.5,39.495c-2.688,0-5.378-1.023-7.425-3.07c-4.094-4.094-4.094-10.756,0-14.85s10.756-4.094,14.85,0
+			s4.094,10.756,0,14.85C58.878,38.472,56.188,39.495,53.5,39.495z M53.5,19.504c-2.433,0-4.865,0.926-6.718,2.778
+			c-3.704,3.704-3.704,9.731,0,13.436c3.705,3.705,9.73,3.705,13.436,0c3.704-3.704,3.704-9.731,0-13.436
+			C58.365,20.43,55.933,19.504,53.5,19.504z"/>
+	</g>
+	<g>
+		<path class="st0" d="M51.732,33.035c-0.128,0-0.256-0.049-0.354-0.146l-3.536-3.535c-0.195-0.195-0.195-0.512,0-0.707
+			s0.512-0.195,0.707,0l3.183,3.182l6.718-6.717c0.195-0.195,0.512-0.195,0.707,0s0.195,0.512,0,0.707l-7.071,7.07
+			C51.988,32.986,51.86,33.035,51.732,33.035z"/>
+	</g>
+	<g>
+		<path class="st0" d="M11.25,16.995c-0.276,0-0.5-0.224-0.5-0.5v-2.5c0-0.827,0.673-1.5,1.5-1.5h11c0.276,0,0.5,0.224,0.5,0.5
+			s-0.224,0.5-0.5,0.5h-11c-0.275,0-0.5,0.225-0.5,0.5v2.5C11.75,16.771,11.526,16.995,11.25,16.995z"/>
+	</g>
+	<g>
+		<path class="st0" d="M53.5,16.995c-0.276,0-0.5-0.224-0.5-0.5v-2.5c0-0.275-0.225-0.5-0.5-0.5h-11c-0.276,0-0.5-0.224-0.5-0.5
+			s0.224-0.5,0.5-0.5h11c0.827,0,1.5,0.673,1.5,1.5v2.5C54,16.771,53.776,16.995,53.5,16.995z"/>
+	</g>
+	<g>
+		<path class="st0" d="M12,5.5H8C7.724,5.5,7.5,5.276,7.5,5S7.724,4.5,8,4.5h4c0.276,0,0.5,0.224,0.5,0.5S12.276,5.5,12,5.5z"/>
+	</g>
+	<g>
+		<path class="st0" d="M10,7.5C9.724,7.5,9.5,7.276,9.5,7V3c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v4
+			C10.5,7.276,10.276,7.5,10,7.5z"/>
+	</g>
+	<g>
+		<path class="st0" d="M5,12H1c-0.276,0-0.5-0.224-0.5-0.5S0.724,11,1,11h4c0.276,0,0.5,0.224,0.5,0.5S5.276,12,5,12z"/>
+	</g>
+	<g>
+		<path class="st0" d="M3,14c-0.276,0-0.5-0.224-0.5-0.5v-4C2.5,9.224,2.724,9,3,9s0.5,0.224,0.5,0.5v4C3.5,13.776,3.276,14,3,14z"
+			/>
+	</g>
+	<g>
+		<path class="st0" d="M61,53.5h-1c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h1c0.276,0,0.5,0.224,0.5,0.5S61.276,53.5,61,53.5z
+			"/>
+	</g>
+	<g>
+		<path class="st0" d="M58,53.5H47c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h11c0.276,0,0.5,0.224,0.5,0.5S58.276,53.5,58,53.5
+			z"/>
+	</g>
+	<g>
+		<path class="st0" d="M4,53.5H3c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h1c0.276,0,0.5,0.224,0.5,0.5S4.276,53.5,4,53.5z"/>
+	</g>
+	<g>
+		<path class="st0" d="M17,53.5H6c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h11c0.276,0,0.5,0.224,0.5,0.5S17.276,53.5,17,53.5z
+			"/>
+	</g>
+</g>
+</svg>

+ 138 - 0
web/src/components/gva-wfd/assets/icons/model/icon_filtering.svg

@@ -0,0 +1,138 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Icon_Set" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 64 64" style="enable-background:new 0 0 64 64;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#263238;}
+	.st1{fill:#FFD740;}
+	.st2{fill:#FF5252;}
+	.st3{fill:#40C4FF;}
+	.st4{fill:#4DB6AC;}
+	.st5{fill:#FFFFFF;}
+	.st6{fill:#FFD345;}
+</style>
+<g>
+	<g>
+		<path class="st0" d="M52.375,43.75c-2.927,0-4.506-5.925-4.506-11.5s1.579-11.5,4.506-11.5s4.506,5.925,4.506,11.5
+			S55.302,43.75,52.375,43.75z M52.375,21.75c-1.658,0-3.506,4.312-3.506,10.5s1.848,10.5,3.506,10.5s3.506-4.312,3.506-10.5
+			S54.033,21.75,52.375,21.75z"/>
+	</g>
+	<g>
+		<path class="st0" d="M59.407,39.267c-2.54,0-6.143-1.308-9.284-3.12c-4.171-2.404-7.265-5.448-7.884-7.755
+			c-0.195-0.729-0.141-1.366,0.163-1.892c1.463-2.531,7.39-0.936,12.226,1.853l0,0c4.171,2.404,7.265,5.448,7.884,7.755
+			c0.195,0.729,0.141,1.366-0.163,1.892C61.835,38.886,60.775,39.267,59.407,39.267z M45.354,26.25c-1.064,0-1.802,0.257-2.086,0.75
+			c-0.167,0.288-0.188,0.669-0.063,1.133c0.544,2.032,3.525,4.904,7.417,7.148c5.367,3.094,10.033,3.653,10.86,2.219
+			c0.167-0.288,0.188-0.669,0.063-1.133c-0.544-2.032-3.525-4.904-7.417-7.148l0,0C50.606,27.188,47.386,26.25,45.354,26.25z"/>
+	</g>
+	<g>
+		<path class="st0" d="M45.343,39.267c-1.369,0-2.429-0.38-2.941-1.267c-0.304-0.525-0.358-1.162-0.163-1.892
+			c0.619-2.307,3.713-5.351,7.884-7.755c4.834-2.788,10.761-4.386,12.226-1.853c0.304,0.525,0.358,1.162,0.163,1.892
+			c-0.619,2.307-3.713,5.351-7.884,7.755C51.486,37.958,47.883,39.267,45.343,39.267z M50.622,29.219
+			c-3.892,2.244-6.872,5.116-7.417,7.148c-0.125,0.464-0.104,0.845,0.063,1.133c0.829,1.434,5.494,0.875,10.86-2.219
+			c3.892-2.244,6.872-5.116,7.417-7.148c0.125-0.464,0.104-0.845-0.063-1.133C60.654,25.567,55.989,26.126,50.622,29.219
+			L50.622,29.219z M50.372,28.786h0.01H50.372z"/>
+	</g>
+	<g>
+		<path class="st3" d="M21.895,26.75H2.855c-0.541,0-0.98-0.336-0.98-0.75v-4.5c0-0.414,0.439-0.75,0.98-0.75h19.041
+			c0.541,0,0.98,0.336,0.98,0.75V26C22.875,26.414,22.436,26.75,21.895,26.75z"/>
+		<path class="st0" d="M21.896,27.25H2.854c-0.83,0-1.479-0.549-1.479-1.25v-4.5c0-0.701,0.65-1.25,1.479-1.25h19.041
+			c0.83,0,1.479,0.549,1.479,1.25V26C23.375,26.701,22.725,27.25,21.896,27.25z M2.854,21.25c-0.297,0-0.479,0.162-0.479,0.25V26
+			c0,0.088,0.182,0.25,0.479,0.25h19.041c0.297,0,0.479-0.162,0.479-0.25v-4.5c0-0.088-0.182-0.25-0.479-0.25H2.854z"/>
+	</g>
+	<g>
+		<path class="st0" d="M19.125,24.25h-5.5c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h5.5c0.276,0,0.5,0.224,0.5,0.5
+			S19.401,24.25,19.125,24.25z"/>
+	</g>
+	<g>
+		<rect x="5.625" y="22.75" class="st5" width="4" height="2"/>
+		<path class="st0" d="M9.625,25.25h-4c-0.276,0-0.5-0.224-0.5-0.5v-2c0-0.276,0.224-0.5,0.5-0.5h4c0.276,0,0.5,0.224,0.5,0.5v2
+			C10.125,25.026,9.901,25.25,9.625,25.25z M6.125,24.25h3v-1h-3V24.25z"/>
+	</g>
+	<g>
+		<path class="st3" d="M21.895,32.75H2.855c-0.541,0-0.98-0.336-0.98-0.75v-4.5c0-0.414,0.439-0.75,0.98-0.75h19.041
+			c0.541,0,0.98,0.336,0.98,0.75V32C22.875,32.414,22.436,32.75,21.895,32.75z"/>
+		<path class="st0" d="M21.896,33.25H2.854c-0.83,0-1.479-0.549-1.479-1.25v-4.5c0-0.701,0.65-1.25,1.479-1.25h19.041
+			c0.83,0,1.479,0.549,1.479,1.25V32C23.375,32.701,22.725,33.25,21.896,33.25z M2.854,27.25c-0.297,0-0.479,0.162-0.479,0.25V32
+			c0,0.088,0.182,0.25,0.479,0.25h19.041c0.297,0,0.479-0.162,0.479-0.25v-4.5c0-0.088-0.182-0.25-0.479-0.25H2.854z"/>
+	</g>
+	<g>
+		<path class="st0" d="M19.125,30.25h-5.5c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h5.5c0.276,0,0.5,0.224,0.5,0.5
+			S19.401,30.25,19.125,30.25z"/>
+	</g>
+	<g>
+		<rect x="5.625" y="28.75" class="st5" width="4" height="2"/>
+		<path class="st0" d="M9.625,31.25h-4c-0.276,0-0.5-0.224-0.5-0.5v-2c0-0.276,0.224-0.5,0.5-0.5h4c0.276,0,0.5,0.224,0.5,0.5v2
+			C10.125,31.026,9.901,31.25,9.625,31.25z M6.125,30.25h3v-1h-3V30.25z"/>
+	</g>
+	<g>
+		<path class="st3" d="M21.895,38.75H2.855c-0.541,0-0.98-0.336-0.98-0.75v-4.5c0-0.414,0.439-0.75,0.98-0.75h19.041
+			c0.541,0,0.98,0.336,0.98,0.75V38C22.875,38.414,22.436,38.75,21.895,38.75z"/>
+		<path class="st0" d="M21.896,39.25H2.854c-0.83,0-1.479-0.549-1.479-1.25v-4.5c0-0.701,0.65-1.25,1.479-1.25h19.041
+			c0.83,0,1.479,0.549,1.479,1.25V38C23.375,38.701,22.725,39.25,21.896,39.25z M2.854,33.25c-0.297,0-0.479,0.162-0.479,0.25V38
+			c0,0.088,0.182,0.25,0.479,0.25h19.041c0.297,0,0.479-0.162,0.479-0.25v-4.5c0-0.088-0.182-0.25-0.479-0.25H2.854z"/>
+	</g>
+	<g>
+		<path class="st0" d="M19.125,36.25h-5.5c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h5.5c0.276,0,0.5,0.224,0.5,0.5
+			S19.401,36.25,19.125,36.25z"/>
+	</g>
+	<g>
+		<rect x="5.625" y="34.75" class="st5" width="4" height="2"/>
+		<path class="st0" d="M9.625,37.25h-4c-0.276,0-0.5-0.224-0.5-0.5v-2c0-0.276,0.224-0.5,0.5-0.5h4c0.276,0,0.5,0.224,0.5,0.5v2
+			C10.125,37.026,9.901,37.25,9.625,37.25z M6.125,36.25h3v-1h-3V36.25z"/>
+	</g>
+	<g>
+		<path class="st0" d="M21.375,44.25h-18c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h18c0.276,0,0.5,0.224,0.5,0.5
+			S21.651,44.25,21.375,44.25z"/>
+	</g>
+	<g>
+		<path class="st0" d="M7.375,42.25c-0.276,0-0.5-0.224-0.5-0.5v-3c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v3
+			C7.875,42.026,7.651,42.25,7.375,42.25z"/>
+	</g>
+	<g>
+		<path class="st0" d="M17.375,42.25c-0.276,0-0.5-0.224-0.5-0.5v-3c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v3
+			C17.875,42.026,17.651,42.25,17.375,42.25z"/>
+	</g>
+	<g>
+		<path class="st0" d="M52.375,19.75c-0.276,0-0.5-0.224-0.5-0.5v-4.5c0-1.103-0.897-2-2-2h-35c-1.103,0-2,0.897-2,2v4
+			c0,0.276-0.224,0.5-0.5,0.5s-0.5-0.224-0.5-0.5v-4c0-1.654,1.346-3,3-3h35c1.654,0,3,1.346,3,3v4.5
+			C52.875,19.527,52.651,19.75,52.375,19.75z"/>
+	</g>
+	<g>
+		<path class="st0" d="M49.875,52.75h-35c-1.654,0-3-1.346-3-3v-4c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v4
+			c0,1.103,0.897,2,2,2h35c1.103,0,2-0.897,2-2v-4.5c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v4.5
+			C52.875,51.404,51.529,52.75,49.875,52.75z"/>
+	</g>
+	<g>
+		<circle class="st1" cx="32.375" cy="12.25" r="7.5"/>
+	</g>
+	<g>
+		<path class="st0" d="M30.875,15.75c-0.128,0-0.256-0.049-0.354-0.146c-0.195-0.195-0.195-0.512,0-0.707l2.646-2.646l-2.646-2.646
+			c-0.195-0.195-0.195-0.512,0-0.707s0.512-0.195,0.707,0l3,3c0.195,0.195,0.195,0.512,0,0.707l-3,3
+			C31.131,15.701,31.003,15.75,30.875,15.75z"/>
+	</g>
+	<g>
+		<circle class="st1" cx="32.375" cy="52.25" r="7.5"/>
+	</g>
+	<g>
+		<path class="st0" d="M33.875,55.75c-0.128,0-0.256-0.049-0.354-0.146l-3-3c-0.195-0.195-0.195-0.512,0-0.707l3-3
+			c0.195-0.195,0.512-0.195,0.707,0s0.195,0.512,0,0.707l-2.646,2.646l2.646,2.646c0.195,0.195,0.195,0.512,0,0.707
+			C34.131,55.701,34.003,55.75,33.875,55.75z"/>
+	</g>
+	<g>
+		<path class="st0" d="M54.375,6.75h-4c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h4c0.276,0,0.5,0.224,0.5,0.5
+			S54.651,6.75,54.375,6.75z"/>
+	</g>
+	<g>
+		<path class="st0" d="M52.375,8.75c-0.276,0-0.5-0.224-0.5-0.5v-4c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v4
+			C52.875,8.526,52.651,8.75,52.375,8.75z"/>
+	</g>
+	<g>
+		<path class="st0" d="M61.375,13.25h-4c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h4c0.276,0,0.5,0.224,0.5,0.5
+			S61.651,13.25,61.375,13.25z"/>
+	</g>
+	<g>
+		<path class="st0" d="M59.375,15.25c-0.276,0-0.5-0.224-0.5-0.5v-4c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v4
+			C59.875,15.026,59.651,15.25,59.375,15.25z"/>
+	</g>
+</g>
+</svg>

+ 77 - 0
web/src/components/gva-wfd/assets/icons/model/icon_por-den.svg

@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Icon_Set" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 64 64" style="enable-background:new 0 0 64 64;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#263238;}
+	.st1{fill:#FF5252;}
+	.st2{font-family:'AmericanTypewriter-Bold';}
+	.st3{font-size:8px;}
+	.st4{fill:#F5222D;}
+</style>
+<g>
+	<path class="st0" d="M60,56.8H4c-0.3,0-0.5-0.2-0.5-0.5v-51C3.5,5,3.7,4.8,4,4.8S4.5,5,4.5,5.2v50.5H60c0.3,0,0.5,0.2,0.5,0.5
+		S60.3,56.8,60,56.8z"/>
+</g>
+<g>
+	<path class="st0" d="M12,59.2c-0.3,0-0.5-0.2-0.5-0.5v-2.5c0-0.3,0.2-0.5,0.5-0.5s0.5,0.2,0.5,0.5v2.5C12.5,59,12.3,59.2,12,59.2z"
+		/>
+</g>
+<g>
+	<path class="st0" d="M22,59.2c-0.3,0-0.5-0.2-0.5-0.5v-2.5c0-0.3,0.2-0.5,0.5-0.5s0.5,0.2,0.5,0.5v2.5C22.5,59,22.3,59.2,22,59.2z"
+		/>
+</g>
+<g>
+	<path class="st0" d="M32,59.2c-0.3,0-0.5-0.2-0.5-0.5v-2.5c0-0.3,0.2-0.5,0.5-0.5c0.3,0,0.5,0.2,0.5,0.5v2.5
+		C32.5,59,32.3,59.2,32,59.2z"/>
+</g>
+<g>
+	<path class="st0" d="M42,59.2c-0.3,0-0.5-0.2-0.5-0.5v-2.5c0-0.3,0.2-0.5,0.5-0.5s0.5,0.2,0.5,0.5v2.5C42.5,59,42.3,59.2,42,59.2z"
+		/>
+</g>
+<g>
+	<path class="st0" d="M52,59.2c-0.3,0-0.5-0.2-0.5-0.5v-2.5c0-0.3,0.2-0.5,0.5-0.5s0.5,0.2,0.5,0.5v2.5C52.5,59,52.3,59.2,52,59.2z"
+		/>
+</g>
+<g>
+	<path class="st0" d="M12.2,43.4c0-0.1,0.1-0.3,0.2-0.4L47,16.3c0.3-0.2,0.7-0.2,0.9,0c0.3,0.2,0.3,0.5,0,0.7L13.4,43.7
+		c-0.3,0.2-0.7,0.2-0.9,0C12.3,43.6,12.2,43.5,12.2,43.4z"/>
+</g>
+<g>
+	<circle class="st1" cx="19.7" cy="40.5" r="2.5"/>
+	<path class="st0" d="M19.7,43.5c-1.7,0-3-1.3-3-3s1.3-3,3-3s3,1.3,3,3S21.4,43.5,19.7,43.5z M19.7,38.5c-1.1,0-2,0.9-2,2s0.9,2,2,2
+		s2-0.9,2-2S20.8,38.5,19.7,38.5z"/>
+</g>
+<g>
+	<circle class="st1" cx="25.2" cy="37.5" r="2.5"/>
+	<path class="st0" d="M25.2,40.5c-1.7,0-3-1.3-3-3s1.3-3,3-3s3,1.3,3,3S26.9,40.5,25.2,40.5z M25.2,35.5c-1.1,0-2,0.9-2,2s0.9,2,2,2
+		s2-0.9,2-2S26.3,35.5,25.2,35.5z"/>
+</g>
+<g>
+	<circle class="st1" cx="31" cy="31" r="2.5"/>
+	<path class="st0" d="M31,34c-1.7,0-3-1.3-3-3s1.3-3,3-3s3,1.3,3,3S32.7,34,31,34z M31,29c-1.1,0-2,0.9-2,2s0.9,2,2,2s2-0.9,2-2
+		S32.1,29,31,29z"/>
+</g>
+<g>
+	<circle class="st1" cx="26" cy="30.8" r="2.5"/>
+	<path class="st0" d="M26,33.8c-1.7,0-3-1.3-3-3s1.3-3,3-3s3,1.3,3,3S27.7,33.8,26,33.8z M26,28.8c-1.1,0-2,0.9-2,2s0.9,2,2,2
+		s2-0.9,2-2S27.1,28.8,26,28.8z"/>
+</g>
+<g>
+	<circle class="st1" cx="31.8" cy="25.8" r="2.5"/>
+	<path class="st0" d="M31.8,28.8c-1.7,0-3-1.3-3-3s1.3-3,3-3c1.7,0,3,1.3,3,3S33.4,28.8,31.8,28.8z M31.8,23.8c-1.1,0-2,0.9-2,2
+		s0.9,2,2,2c1.1,0,2-0.9,2-2S32.9,23.8,31.8,23.8z"/>
+</g>
+<g>
+	<circle class="st1" cx="35.7" cy="21.8" r="2.5"/>
+	<path class="st0" d="M35.7,24.8c-1.7,0-3-1.3-3-3c0-1.7,1.3-3,3-3s3,1.3,3,3C38.7,23.4,37.4,24.8,35.7,24.8z M35.7,19.8
+		c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2s2-0.9,2-2C37.7,20.7,36.8,19.8,35.7,19.8z"/>
+</g>
+<g>
+	<circle class="st1" cx="41.2" cy="21.6" r="2.5"/>
+	<path class="st0" d="M41.2,24.6c-1.7,0-3-1.3-3-3c0-1.7,1.3-3,3-3s3,1.3,3,3C44.2,23.3,42.8,24.6,41.2,24.6z M41.2,19.6
+		c-1.1,0-2,0.9-2,2c0,1.1,0.9,2,2,2s2-0.9,2-2C43.2,20.5,42.3,19.6,41.2,19.6z"/>
+</g>
+<text transform="matrix(1 0 0 1 44.125 53.625)" class="st2 st3">Den</text>
+<text transform="matrix(1 0 0 1 6.375 10.625)" class="st4 st2 st3">Por</text>
+</svg>

+ 101 - 0
web/src/components/gva-wfd/assets/icons/model/icon_regression.svg

@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Icon_Set" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 64 64" style="enable-background:new 0 0 64 64;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#263238;}
+	.st1{fill:#FFD740;}
+	.st2{fill:#FF5252;}
+	.st3{fill:#40C4FF;}
+	.st4{fill:#4DB6AC;}
+	.st5{fill:#FFFFFF;}
+	.st6{fill:#FFD345;}
+</style>
+<g>
+	<g>
+		<path class="st0" d="M60,59.25H4c-0.276,0-0.5-0.224-0.5-0.5v-51c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v50.5H60
+			c0.276,0,0.5,0.224,0.5,0.5S60.276,59.25,60,59.25z"/>
+	</g>
+	<g>
+		<path class="st0" d="M12,61.75c-0.276,0-0.5-0.224-0.5-0.5v-2.5c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v2.5
+			C12.5,61.526,12.276,61.75,12,61.75z"/>
+	</g>
+	<g>
+		<path class="st0" d="M22,61.75c-0.276,0-0.5-0.224-0.5-0.5v-2.5c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v2.5
+			C22.5,61.526,22.276,61.75,22,61.75z"/>
+	</g>
+	<g>
+		<path class="st0" d="M32,61.75c-0.276,0-0.5-0.224-0.5-0.5v-2.5c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v2.5
+			C32.5,61.526,32.276,61.75,32,61.75z"/>
+	</g>
+	<g>
+		<path class="st0" d="M42,61.75c-0.276,0-0.5-0.224-0.5-0.5v-2.5c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v2.5
+			C42.5,61.526,42.276,61.75,42,61.75z"/>
+	</g>
+	<g>
+		<path class="st0" d="M52,61.75c-0.276,0-0.5-0.224-0.5-0.5v-2.5c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v2.5
+			C52.5,61.526,52.276,61.75,52,61.75z"/>
+	</g>
+	<g>
+		<path class="st0" d="M23,5.25h-4c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h4c0.276,0,0.5,0.224,0.5,0.5S23.276,5.25,23,5.25z
+			"/>
+	</g>
+	<g>
+		<path class="st0" d="M21,7.25c-0.276,0-0.5-0.224-0.5-0.5v-4c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v4
+			C21.5,7.026,21.276,7.25,21,7.25z"/>
+	</g>
+	<g>
+		<path class="st0" d="M14,13.25h-4c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h4c0.276,0,0.5,0.224,0.5,0.5
+			S14.276,13.25,14,13.25z"/>
+	</g>
+	<g>
+		<path class="st0" d="M12,15.25c-0.276,0-0.5-0.224-0.5-0.5v-4c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v4
+			C12.5,15.026,12.276,15.25,12,15.25z"/>
+	</g>
+	<g>
+		<circle class="st2" cx="24.5" cy="23" r="2.5"/>
+		<path class="st0" d="M24.5,26c-1.654,0-3-1.346-3-3s1.346-3,3-3s3,1.346,3,3S26.154,26,24.5,26z M24.5,21c-1.103,0-2,0.897-2,2
+			s0.897,2,2,2s2-0.897,2-2S25.603,21,24.5,21z"/>
+	</g>
+	<g>
+		<circle class="st2" cx="29.5" cy="33" r="2.5"/>
+		<path class="st0" d="M29.5,36c-1.654,0-3-1.346-3-3s1.346-3,3-3s3,1.346,3,3S31.154,36,29.5,36z M29.5,31c-1.103,0-2,0.897-2,2
+			s0.897,2,2,2s2-0.897,2-2S30.603,31,29.5,31z"/>
+	</g>
+	<g>
+		<circle class="st2" cx="41" cy="40" r="2.5"/>
+		<path class="st0" d="M41,43c-1.654,0-3-1.346-3-3s1.346-3,3-3s3,1.346,3,3S42.654,43,41,43z M41,38c-1.103,0-2,0.897-2,2
+			s0.897,2,2,2s2-0.897,2-2S42.103,38,41,38z"/>
+	</g>
+	<g>
+		<circle class="st2" cx="17" cy="33" r="2.5"/>
+		<path class="st0" d="M17,36c-1.654,0-3-1.346-3-3s1.346-3,3-3s3,1.346,3,3S18.654,36,17,36z M17,31c-1.103,0-2,0.897-2,2
+			s0.897,2,2,2s2-0.897,2-2S18.103,31,17,31z"/>
+	</g>
+	<g>
+		<circle class="st2" cx="37" cy="23" r="2.5"/>
+		<path class="st0" d="M37,26c-1.654,0-3-1.346-3-3s1.346-3,3-3s3,1.346,3,3S38.654,26,37,26z M37,21c-1.103,0-2,0.897-2,2
+			s0.897,2,2,2s2-0.897,2-2S38.103,21,37,21z"/>
+	</g>
+	<g>
+		<circle class="st2" cx="44.5" cy="48" r="2.5"/>
+		<path class="st0" d="M44.5,51c-1.654,0-3-1.346-3-3s1.346-3,3-3s3,1.346,3,3S46.154,51,44.5,51z M44.5,46c-1.103,0-2,0.897-2,2
+			s0.897,2,2,2s2-0.897,2-2S45.603,46,44.5,46z"/>
+	</g>
+	<g>
+		<circle class="st2" cx="52" cy="38" r="2.5"/>
+		<path class="st0" d="M52,41c-1.654,0-3-1.346-3-3s1.346-3,3-3s3,1.346,3,3S53.654,41,52,41z M52,36c-1.103,0-2,0.897-2,2
+			s0.897,2,2,2s2-0.897,2-2S53.103,36,52,36z"/>
+	</g>
+	<g>
+		<circle class="st2" cx="32" cy="48" r="2.5"/>
+		<path class="st0" d="M32,51c-1.654,0-3-1.346-3-3s1.346-3,3-3s3,1.346,3,3S33.654,51,32,51z M32,46c-1.103,0-2,0.897-2,2
+			s0.897,2,2,2s2-0.897,2-2S33.103,46,32,46z"/>
+	</g>
+	<g>
+		<path class="st0" d="M13.999,45.5c-0.248,0-0.464-0.185-0.495-0.438c-0.034-0.274,0.16-0.524,0.434-0.558l0.596-0.075
+			c14.493-1.812,27.179-10.624,33.934-23.572l1.09-2.088c0.128-0.245,0.431-0.338,0.675-0.212c0.245,0.128,0.34,0.43,0.212,0.675
+			l-1.09,2.088c-6.907,13.239-19.878,22.25-34.695,24.102l-0.596,0.075C14.041,45.499,14.021,45.5,13.999,45.5z"/>
+	</g>
+</g>
+</svg>

+ 101 - 0
web/src/components/gva-wfd/assets/icons/model/icon_rt-por.svg

@@ -0,0 +1,101 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Icon_Set" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 64 64" style="enable-background:new 0 0 64 64;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#263238;}
+	.st1{fill:#FFD740;}
+	.st2{fill:#FF5252;}
+	.st3{fill:#40C4FF;}
+	.st4{font-family:'AmericanTypewriter-Bold';}
+	.st5{font-size:8px;}
+	.st6{fill:#F5222D;}
+</style>
+<g>
+	<path class="st0" d="M60,56.8H4c-0.3,0-0.5-0.2-0.5-0.5v-51C3.5,5,3.7,4.8,4,4.8S4.5,5,4.5,5.2v50.5H60c0.3,0,0.5,0.2,0.5,0.5
+		S60.3,56.8,60,56.8z"/>
+</g>
+<g>
+	<path class="st0" d="M12,59.2c-0.3,0-0.5-0.2-0.5-0.5v-2.5c0-0.3,0.2-0.5,0.5-0.5s0.5,0.2,0.5,0.5v2.5C12.5,59,12.3,59.2,12,59.2z"
+		/>
+</g>
+<g>
+	<path class="st0" d="M22,59.2c-0.3,0-0.5-0.2-0.5-0.5v-2.5c0-0.3,0.2-0.5,0.5-0.5s0.5,0.2,0.5,0.5v2.5C22.5,59,22.3,59.2,22,59.2z"
+		/>
+</g>
+<g>
+	<path class="st0" d="M32,59.2c-0.3,0-0.5-0.2-0.5-0.5v-2.5c0-0.3,0.2-0.5,0.5-0.5c0.3,0,0.5,0.2,0.5,0.5v2.5
+		C32.5,59,32.3,59.2,32,59.2z"/>
+</g>
+<g>
+	<path class="st0" d="M42,59.2c-0.3,0-0.5-0.2-0.5-0.5v-2.5c0-0.3,0.2-0.5,0.5-0.5s0.5,0.2,0.5,0.5v2.5C42.5,59,42.3,59.2,42,59.2z"
+		/>
+</g>
+<g>
+	<path class="st0" d="M52,59.2c-0.3,0-0.5-0.2-0.5-0.5v-2.5c0-0.3,0.2-0.5,0.5-0.5s0.5,0.2,0.5,0.5v2.5C52.5,59,52.3,59.2,52,59.2z"
+		/>
+</g>
+<g>
+	<rect x="10.8" y="25.9" class="st1" width="5" height="5"/>
+	<path class="st0" d="M15.8,31.4h-5c-0.3,0-0.5-0.2-0.5-0.5v-5c0-0.3,0.2-0.5,0.5-0.5h5c0.3,0,0.5,0.2,0.5,0.5v5
+		C16.2,31.1,16,31.4,15.8,31.4z M11.2,30.4h4v-4h-4V30.4z"/>
+</g>
+<rect x="23" y="25.9" class="st1" width="5" height="5"/>
+<path class="st0" d="M28,31.4h-5c-0.3,0-0.5-0.2-0.5-0.5v-5c0-0.3,0.2-0.5,0.5-0.5h5c0.3,0,0.5,0.2,0.5,0.5v5
+	C28.5,31.1,28.3,31.4,28,31.4z M23.5,30.4h4v-4h-4V30.4z"/>
+<g>
+	<rect x="18.6" y="16.9" class="st1" width="5" height="5"/>
+	<path class="st0" d="M23.6,22.4h-5c-0.3,0-0.5-0.2-0.5-0.5v-5c0-0.3,0.2-0.5,0.5-0.5h5c0.3,0,0.5,0.2,0.5,0.5v5
+		C24.1,22.1,23.9,22.4,23.6,22.4z M19.1,21.4h4v-4h-4V21.4z"/>
+</g>
+<g>
+	<path class="st0" d="M55.8,29.9c-0.1,0.1-0.2,0.2-0.4,0.2l-46.1,8c-0.3,0.1-0.7-0.1-0.8-0.4c-0.1-0.3,0.1-0.5,0.5-0.6l46.1-8
+		c0.3-0.1,0.7,0.1,0.8,0.4C55.9,29.7,55.9,29.8,55.8,29.9z"/>
+</g>
+<g>
+	<circle class="st2" cx="38.8" cy="18.9" r="2.5"/>
+	<path class="st0" d="M38.8,21.9c-1.7,0-3-1.3-3-3s1.3-3,3-3s3,1.3,3,3S40.4,21.9,38.8,21.9z M38.8,16.9c-1.1,0-2,0.9-2,2s0.9,2,2,2
+		s2-0.9,2-2S39.9,16.9,38.8,16.9z"/>
+</g>
+<g>
+	<circle class="st2" cx="49.8" cy="22.5" r="2.5"/>
+	<path class="st0" d="M49.8,25.5c-1.7,0-3-1.3-3-3s1.3-3,3-3s3,1.3,3,3S51.4,25.5,49.8,25.5z M49.8,20.5c-1.1,0-2,0.9-2,2s0.9,2,2,2
+		s2-0.9,2-2S50.9,20.5,49.8,20.5z"/>
+</g>
+<g>
+	<circle class="st2" cx="41.2" cy="27.4" r="2.5"/>
+	<path class="st0" d="M41.2,30.4c-1.7,0-3-1.3-3-3s1.3-3,3-3s3,1.3,3,3C44.2,29,42.9,30.4,41.2,30.4z M41.2,25.4c-1.1,0-2,0.9-2,2
+		s0.9,2,2,2s2-0.9,2-2S42.4,25.4,41.2,25.4z"/>
+</g>
+<g>
+	<path class="st0" d="M31.9,14.9c0.1,0.1,0.1,0.2,0.1,0.2l-0.1,18.7c0,0.1-0.2,0.2-0.5,0.1c-0.3-0.1-0.5-0.3-0.5-0.4l0.1-18.7
+		c0-0.1,0.2-0.2,0.5-0.1C31.7,14.8,31.8,14.9,31.9,14.9z"/>
+</g>
+<g>
+	<polygon class="st3" points="42.1,34.7 38.9,39.7 45.3,39.7 	"/>
+	<path class="st0" d="M45.3,40h-6.4c-0.1,0-0.2-0.1-0.3-0.2c-0.1-0.1-0.1-0.2,0-0.3l3.2-5c0.1-0.2,0.4-0.2,0.5,0l3.2,5
+		c0.1,0.1,0.1,0.2,0,0.3C45.5,39.9,45.4,40,45.3,40z M39.7,39.3h4.8l-2.4-3.7L39.7,39.3z"/>
+</g>
+<g>
+	<polygon class="st3" points="37.1,42.8 33.9,47.8 40.3,47.8 	"/>
+	<path class="st0" d="M40.3,48.1h-6.4c-0.1,0-0.2-0.1-0.3-0.2c-0.1-0.1-0.1-0.2,0-0.3l3.2-5c0.1-0.2,0.4-0.2,0.5,0l3.2,5
+		c0.1,0.1,0.1,0.2,0,0.3C40.5,48,40.4,48.1,40.3,48.1z M34.7,47.4h4.8l-2.4-3.7L34.7,47.4z"/>
+</g>
+<g>
+	<polygon class="st3" points="29,37.2 25.8,42.2 32.2,42.2 	"/>
+	<path class="st0" d="M32.2,42.5h-6.4c-0.1,0-0.2-0.1-0.3-0.2c-0.1-0.1-0.1-0.2,0-0.3l3.2-5c0.1-0.2,0.4-0.2,0.5,0l3.2,5
+		c0.1,0.1,0.1,0.2,0,0.3C32.4,42.4,32.3,42.5,32.2,42.5z M26.6,41.8h4.8L29,38.1L26.6,41.8z"/>
+</g>
+<g>
+	<polygon class="st3" points="18.6,40.3 15.4,45.3 21.8,45.3 	"/>
+	<path class="st0" d="M21.8,45.6h-6.4c-0.1,0-0.2-0.1-0.3-0.2c-0.1-0.1-0.1-0.2,0-0.3l3.2-5c0.1-0.2,0.4-0.2,0.5,0l3.2,5
+		c0.1,0.1,0.1,0.2,0,0.3C22,45.5,21.9,45.6,21.8,45.6z M16.2,44.9H21l-2.4-3.7L16.2,44.9z"/>
+</g>
+<g>
+	<polygon class="st3" points="49.7,40.3 46.5,45.3 53,45.3 	"/>
+	<path class="st0" d="M53,45.6h-6.4c-0.1,0-0.2-0.1-0.3-0.2c-0.1-0.1-0.1-0.2,0-0.3l3.2-5c0.1-0.2,0.4-0.2,0.5,0l3.2,5
+		c0.1,0.1,0.1,0.2,0,0.3C53.2,45.5,53.1,45.6,53,45.6z M47.3,44.9h4.8l-2.4-3.7L47.3,44.9z"/>
+</g>
+<text transform="matrix(1 0 0 1 46.3076 54.625)" class="st4 st5">Por</text>
+<text transform="matrix(1 0 0 1 6.125 10.5)" class="st6 st4 st5">Rt</text>
+</svg>

+ 105 - 0
web/src/components/gva-wfd/assets/icons/model/icon_tree.svg

@@ -0,0 +1,105 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 21.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg version="1.1" id="Icon_Set" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 viewBox="0 0 64 64" style="enable-background:new 0 0 64 64;" xml:space="preserve">
+<style type="text/css">
+	.st0{fill:#263238;}
+	.st1{fill:#FFD740;}
+	.st2{fill:#FF5252;}
+	.st3{fill:#40C4FF;}
+	.st4{fill:#4DB6AC;}
+	.st5{fill:#FFFFFF;}
+	.st6{fill:#FFD345;}
+</style>
+<g>
+	<g>
+		<path class="st2" d="M41,16H23c-0.552,0-1-0.448-1-1V9c0-0.552,0.448-1,1-1h18c0.552,0,1,0.448,1,1v6C42,15.552,41.552,16,41,16z"
+			/>
+		<path class="st0" d="M41,16.5H23c-0.827,0-1.5-0.673-1.5-1.5V9c0-0.827,0.673-1.5,1.5-1.5h18c0.827,0,1.5,0.673,1.5,1.5v6
+			C42.5,15.827,41.827,16.5,41,16.5z M23,8.5c-0.275,0-0.5,0.224-0.5,0.5v6c0,0.276,0.225,0.5,0.5,0.5h18c0.275,0,0.5-0.224,0.5-0.5
+			V9c0-0.276-0.225-0.5-0.5-0.5H23z"/>
+	</g>
+	<g>
+		<path class="st1" d="M24,36H10c-0.552,0-1-0.448-1-1v-6c0-0.552,0.448-1,1-1h14c0.552,0,1,0.448,1,1v6C25,35.552,24.552,36,24,36z
+			"/>
+		<path class="st0" d="M24,36.5H10c-0.827,0-1.5-0.673-1.5-1.5v-6c0-0.827,0.673-1.5,1.5-1.5h14c0.827,0,1.5,0.673,1.5,1.5v6
+			C25.5,35.827,24.827,36.5,24,36.5z M10,28.5c-0.275,0-0.5,0.224-0.5,0.5v6c0,0.276,0.225,0.5,0.5,0.5h14
+			c0.275,0,0.5-0.224,0.5-0.5v-6c0-0.276-0.225-0.5-0.5-0.5H10z"/>
+	</g>
+	<g>
+		<path class="st4" d="M13,56H5c-0.552,0-1-0.448-1-1v-6c0-0.552,0.448-1,1-1h8c0.552,0,1,0.448,1,1v6C14,55.552,13.552,56,13,56z"
+			/>
+		<path class="st0" d="M13,56.5H5c-0.827,0-1.5-0.673-1.5-1.5v-6c0-0.827,0.673-1.5,1.5-1.5h8c0.827,0,1.5,0.673,1.5,1.5v6
+			C14.5,55.827,13.827,56.5,13,56.5z M5,48.5c-0.275,0-0.5,0.224-0.5,0.5v6c0,0.276,0.225,0.5,0.5,0.5h8c0.275,0,0.5-0.224,0.5-0.5
+			v-6c0-0.276-0.225-0.5-0.5-0.5H5z"/>
+	</g>
+	<g>
+		<path class="st4" d="M29,56h-8c-0.552,0-1-0.448-1-1v-6c0-0.552,0.448-1,1-1h8c0.552,0,1,0.448,1,1v6C30,55.552,29.552,56,29,56z"
+			/>
+		<path class="st0" d="M29,56.5h-8c-0.827,0-1.5-0.673-1.5-1.5v-6c0-0.827,0.673-1.5,1.5-1.5h8c0.827,0,1.5,0.673,1.5,1.5v6
+			C30.5,55.827,29.827,56.5,29,56.5z M21,48.5c-0.275,0-0.5,0.224-0.5,0.5v6c0,0.276,0.225,0.5,0.5,0.5h8c0.275,0,0.5-0.224,0.5-0.5
+			v-6c0-0.276-0.225-0.5-0.5-0.5H21z"/>
+	</g>
+	<g>
+		<path class="st0" d="M32,22.5c-0.276,0-0.5-0.224-0.5-0.5v-6c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v6
+			C32.5,22.276,32.276,22.5,32,22.5z"/>
+	</g>
+	<g>
+		<path class="st0" d="M47,28.5c-0.276,0-0.5-0.224-0.5-0.5v-5.5h-29V28c0,0.276-0.224,0.5-0.5,0.5s-0.5-0.224-0.5-0.5v-6
+			c0-0.276,0.224-0.5,0.5-0.5h30c0.276,0,0.5,0.224,0.5,0.5v6C47.5,28.276,47.276,28.5,47,28.5z"/>
+	</g>
+	<g>
+		<path class="st0" d="M17,42.5c-0.276,0-0.5-0.224-0.5-0.5v-6c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v6
+			C17.5,42.276,17.276,42.5,17,42.5z"/>
+	</g>
+	<g>
+		<path class="st0" d="M25,48.5c-0.276,0-0.5-0.224-0.5-0.5v-5.5h-15V48c0,0.276-0.224,0.5-0.5,0.5S8.5,48.276,8.5,48v-6
+			c0-0.276,0.224-0.5,0.5-0.5h16c0.276,0,0.5,0.224,0.5,0.5v6C25.5,48.276,25.276,48.5,25,48.5z"/>
+	</g>
+	<g>
+		<path class="st1" d="M54,36H40c-0.552,0-1-0.448-1-1v-6c0-0.552,0.448-1,1-1h14c0.552,0,1,0.448,1,1v6C55,35.552,54.552,36,54,36z
+			"/>
+		<path class="st0" d="M54,36.5H40c-0.827,0-1.5-0.673-1.5-1.5v-6c0-0.827,0.673-1.5,1.5-1.5h14c0.827,0,1.5,0.673,1.5,1.5v6
+			C55.5,35.827,54.827,36.5,54,36.5z M40,28.5c-0.275,0-0.5,0.224-0.5,0.5v6c0,0.276,0.225,0.5,0.5,0.5h14
+			c0.275,0,0.5-0.224,0.5-0.5v-6c0-0.276-0.225-0.5-0.5-0.5H40z"/>
+	</g>
+	<g>
+		<path class="st4" d="M43,56h-8c-0.552,0-1-0.448-1-1v-6c0-0.552,0.448-1,1-1h8c0.552,0,1,0.448,1,1v6C44,55.552,43.552,56,43,56z"
+			/>
+		<path class="st0" d="M43,56.5h-8c-0.827,0-1.5-0.673-1.5-1.5v-6c0-0.827,0.673-1.5,1.5-1.5h8c0.827,0,1.5,0.673,1.5,1.5v6
+			C44.5,55.827,43.827,56.5,43,56.5z M35,48.5c-0.275,0-0.5,0.224-0.5,0.5v6c0,0.276,0.225,0.5,0.5,0.5h8c0.275,0,0.5-0.224,0.5-0.5
+			v-6c0-0.276-0.225-0.5-0.5-0.5H35z"/>
+	</g>
+	<g>
+		<path class="st4" d="M59,56h-8c-0.552,0-1-0.448-1-1v-6c0-0.552,0.448-1,1-1h8c0.552,0,1,0.448,1,1v6C60,55.552,59.552,56,59,56z"
+			/>
+		<path class="st0" d="M59,56.5h-8c-0.827,0-1.5-0.673-1.5-1.5v-6c0-0.827,0.673-1.5,1.5-1.5h8c0.827,0,1.5,0.673,1.5,1.5v6
+			C60.5,55.827,59.827,56.5,59,56.5z M51,48.5c-0.275,0-0.5,0.224-0.5,0.5v6c0,0.276,0.225,0.5,0.5,0.5h8c0.275,0,0.5-0.224,0.5-0.5
+			v-6c0-0.276-0.225-0.5-0.5-0.5H51z"/>
+	</g>
+	<g>
+		<path class="st0" d="M47,42.5c-0.276,0-0.5-0.224-0.5-0.5v-6c0-0.276,0.224-0.5,0.5-0.5s0.5,0.224,0.5,0.5v6
+			C47.5,42.276,47.276,42.5,47,42.5z"/>
+	</g>
+	<g>
+		<path class="st0" d="M55,48.5c-0.276,0-0.5-0.224-0.5-0.5v-5.5h-15V48c0,0.276-0.224,0.5-0.5,0.5s-0.5-0.224-0.5-0.5v-6
+			c0-0.276,0.224-0.5,0.5-0.5h16c0.276,0,0.5,0.224,0.5,0.5v6C55.5,48.276,55.276,48.5,55,48.5z"/>
+	</g>
+	<g>
+		<path class="st0" d="M16.5,12.5h-1c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h1c0.276,0,0.5,0.224,0.5,0.5
+			S16.776,12.5,16.5,12.5z"/>
+	</g>
+	<g>
+		<path class="st0" d="M13.5,12.5h-11C2.224,12.5,2,12.276,2,12s0.224-0.5,0.5-0.5h11c0.276,0,0.5,0.224,0.5,0.5
+			S13.776,12.5,13.5,12.5z"/>
+	</g>
+	<g>
+		<path class="st0" d="M48.5,12.5h-1c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h1c0.276,0,0.5,0.224,0.5,0.5
+			S48.776,12.5,48.5,12.5z"/>
+	</g>
+	<g>
+		<path class="st0" d="M61.5,12.5h-11c-0.276,0-0.5-0.224-0.5-0.5s0.224-0.5,0.5-0.5h11c0.276,0,0.5,0.224,0.5,0.5
+			S61.776,12.5,61.5,12.5z"/>
+	</g>
+</g>
+</svg>

+ 90 - 0
web/src/components/gva-wfd/behavior/clickSelected.js

@@ -0,0 +1,90 @@
+export default function(G6){
+  G6.registerBehavior('clickSelected', {
+    getDefaultCfg() {
+      return {
+        multiple: false,
+      }
+    },
+    getEvents() {
+      return {
+        'node:click': 'onClick',
+        'edge:click': 'onClick',
+        'edge:mouseover': 'onEdgeMouseOver',
+        'edge:mouseleave': 'onEdgeMouseLeave',
+        'canvas:click': 'onCanvasClick',
+        'node:mouseover': 'onNodeMouseOver',
+      }
+    },
+    onClick(e) {
+      this._clearSelected();
+      this.graph.setItemState(e.item, 'selected', true);
+      let selectedItems = this.graph.get('selectedItems');
+      if(!selectedItems)
+        selectedItems = [];
+      selectedItems = [e.item.get('id')];
+      this.graph.set('selectedItems',selectedItems);
+      this.graph.emit('afteritemselected',selectedItems);
+    },
+    onNodeMouseOver(e){
+      if(this.graph.getCurrentMode() === 'edit')
+        this.graph.setItemState(e.item, 'hover', true);
+      else
+        this.graph.setItemState(e.item, 'hover', false);
+    },
+    onEdgeMouseOver(e){
+      if(this.graph.getCurrentMode() === 'edit' && !e.item.hasState('selected'))
+        this.graph.setItemState(e.item, 'hover', true);
+    },
+    onEdgeMouseLeave(e){
+      if(this.graph.getCurrentMode() === 'edit' && !e.item.hasState('selected'))
+        this.graph.setItemState(e.item, 'hover', false);
+    },
+    onCanvasClick(){
+      this._clearSelected();
+      this.graph.emit('afteritemselected',[]);
+    },
+    _clearSubProcessSelected() {
+      const subProcessList = this.graph.findAll('node', (node) => {
+        if (node.get('model')) {
+          const clazz = node.get('model').clazz;
+          return clazz === 'subProcess';
+        } else {
+          return false;
+        }
+      });
+      subProcessList.forEach((node) => {
+        const group = node.getContainer();
+        const subGroup = group.subGroup;
+        this._clearGroupSelected(subGroup);
+      });
+    },
+    _clearGroupSelected(group) {
+      const selected = group.findAll((subGroup) => {
+        const node = subGroup.get('item');
+        if (node) {
+          return node.hasState('selected');
+        } else {
+          return false;
+        }
+      });
+      selected.forEach(subGroup => {
+        const node = subGroup.get('item');
+        if (node) {
+          node.setState('selected', false);
+        }
+      });
+    },
+    _clearSelected(){
+      let selected = this.graph.findAllByState('node', 'selected');
+      selected.forEach(node => {
+        this.graph.setItemState(node, 'selected', false);
+      });
+      selected = this.graph.findAllByState('edge', 'selected');
+      selected.forEach(edge => {
+        this.graph.setItemState(edge, 'selected', false);
+      });
+      this._clearSubProcessSelected();
+      this.graph.set('selectedItems', []);
+    }
+  });
+}

+ 30 - 0
web/src/components/gva-wfd/behavior/deleteItem.js

@@ -0,0 +1,30 @@
+export default function(G6){
+  G6.registerBehavior('deleteItem', {
+    getEvents() {
+      return {
+        'keydown': 'onKeydown',
+        'canvas:mouseleave': 'onCanvasLeave',
+        'canvas:mouseenter': 'onCanvasFocus',
+      }
+    },
+    onKeydown(e){
+      const items = this.graph.get('selectedItems');
+      const focus = this.graph.get('focusGraphWrapper');
+      if(e.keyCode === 8 && items && items.length > 0 && focus){
+        if(this.graph.executeCommand) {
+          this.graph.executeCommand('delete', {});
+        }else{
+          this.graph.remove(items[0]);
+        }
+        this.graph.set('selectedItems',[]);
+        this.graph.emit('afteritemselected',[]);
+      }
+    },
+    onCanvasLeave(e){
+      this.graph.set('focusGraphWrapper',false);
+    },
+    onCanvasFocus(){
+      this.graph.set('focusGraphWrapper',true);
+    }
+  });
+}

+ 207 - 0
web/src/components/gva-wfd/behavior/dragEdge.js

@@ -0,0 +1,207 @@
+import editorStyle from "../util/defaultStyle";
+import { Marker } from '@antv/g-canvas/lib/shape';
+
+export default function (G6) {
+  G6.registerBehavior('dragEdge', {
+    getDefaultCfg() {
+      return {
+        updateEdge: true,
+        delegate: true,
+        delegateStyle: {},
+        dragEdge: false,
+      };
+    },
+    getEvents() {
+      return {
+        'anchor:dragstart': 'onDragStart',
+        'anchor:drag': 'onDrag',
+        'anchor:dragend': 'onDragEnd',
+        'anchor:dragenter': 'onDragEnter',
+        'anchor:dragleave': 'onDragLeave',
+      };
+    },
+    onDragEnter(e) {
+      if (!this.origin) {
+        return;
+      }
+      if (!this.sameNode(e)) {
+        e.item.setHotspotActived(true);
+        this.origin.targetNode = e.target.getParent().getParent().get('item');
+        this.origin.targetAnchor = e.item.get('index');
+      }
+    },
+    onDragLeave(e) {
+      if (!this.origin) {
+        return;
+      }
+      if (!this.sameNode(e)) {
+        e.item.setHotspotActived(false);
+        this.origin.targetNode = null;
+        this.origin.targetAnchor = null;
+      }
+    },
+    onDragStart(e) {
+      const node = e.target.getParent().getParent().get('item');
+      const anchorIndex = e.item.get('index');
+      const point = node.getAnchorPoints()[anchorIndex];
+      this.target = e.item;
+      const groupId = node.get('groupId');
+      if (groupId) {
+        const subProcessNode = e.target.getParent().getParent().getParent().getParent().get('item');
+        const subProcessBBox = subProcessNode.getBBox();
+        this.origin = {
+          x: point.x + subProcessBBox.x + subProcessBBox.width / 2,
+          y: point.y + subProcessBBox.y + subProcessBBox.height / 2,
+          sourceNode: node,
+          sourceAnchor: anchorIndex
+        };
+        this.dragEdgeBeforeShowAnchorBySub(subProcessNode);
+      } else {
+        this.origin = {
+          x: point.x,
+          y: point.y,
+          sourceNode: node,
+          sourceAnchor: anchorIndex
+        };
+        this.dragEdgeBeforeShowAnchor(e);
+      }
+      this.graph.set('edgeDragging', true);
+    },
+    onDrag(e) {
+      if (!this.origin) {
+        return;
+      }
+      this._updateEdge(this.target, e);
+    },
+    onDragEnd(e) {
+      if (!this.origin) {
+        return;
+      }
+      const delegateShape = e.item.get('edgeDelegate');
+      if (delegateShape) {
+        delegateShape.remove();
+        this.target.set('edgeDelegate', null);
+      }
+      this._updateEdge(this.target, e, true);
+      this.graph.setItemState(this.origin.sourceNode, 'show-anchor', false);
+      this.target = null;
+      this.origin = null;
+      this.graph.set('edgeDragging', false);
+    },
+    sameNode(e) {
+      return e.target instanceof Marker && e.target.getParent() && e.target.getParent().getParent().get('item').get('id') === this.origin.sourceNode.get('id')
+    },
+    dragEdgeBeforeShowAnchorBySub(subProcessNode) {
+      const group = subProcessNode.getContainer();
+      group.nodes.forEach(a => {
+        const aGroup = a.getContainer();
+        aGroup.showAnchor();
+        aGroup.anchorShapes.forEach(b => b.get('item').showHotpot());
+      });
+    },
+    dragEdgeBeforeShowAnchor(e) {
+      const sourceGroupId = this.origin.sourceNode.getModel().groupId;
+      this.graph.getNodes().forEach(node => {
+        if (node.getModel().clazz === 'start'
+          || node.getModel().clazz === 'timerStart'
+          || node.getModel().clazz === 'messageStart')
+          return;
+        const targetGroupId = node.getModel().groupId;
+        if (!sourceGroupId && targetGroupId || sourceGroupId && !targetGroupId || sourceGroupId !== targetGroupId)
+          return;
+        const group = node.getContainer();
+        group.showAnchor();
+        group.anchorShapes.forEach(a => a.get('item').showHotpot())
+      });
+    },
+    _updateEdge(item, e, force) {
+      const x = e.x;
+      const y = e.y;
+      if (this.delegate && !force) {
+        this._updateEdgeDelegate(item, x, y);
+        return;
+      }
+      const node = e.target.getParent().getParent().get('item');
+      const groupId = node.get('groupId');
+      if (groupId) {
+        this._addSubProcessEdge(node, e);
+      } else {
+        this._addEdge(e);
+      }
+      this._clearAllAnchor();
+      this.graph.paint();
+    },
+    _updateEdgeDelegate(item, x, y) {
+      const self = this;
+      let edgeShape = item.get('edgeDelegate');
+      if (!edgeShape) {
+        const parent = self.graph.get('group');
+        edgeShape = parent.addShape('line', {
+          attrs: {
+            x1: this.origin.x,
+            y1: this.origin.y,
+            x2: x,
+            y2: y,
+            ...editorStyle.edgeDelegationStyle,
+          }
+        });
+        edgeShape.set('capture', false);
+        item.set('edgeDelegate', edgeShape);
+      }
+      edgeShape.attr({ x2: x, y2: y });
+      this.graph.paint();
+    },
+    _clearAllAnchor() {
+      this.graph.getNodes().forEach(node => {
+        const group = node.getContainer();
+        group.clearAnchor();
+      });
+    },
+    _addSubProcessEdge(node, e) {
+      if (this.origin.targetNode) {
+        const group = node.getContainer().getParent().getParent();
+        const subProcess = node.getContainer().getParent().getParent().get('item');
+        const sourceId = this.origin.sourceNode.get('id');
+        const targetId = this.origin.targetNode.get('id');
+        const addModel = {
+          id: sourceId + '_to_' + targetId,
+          clazz: 'flow',
+          source: sourceId,
+          target: targetId,
+          sourceAnchor: this.origin.sourceAnchor,
+          targetAnchor: this.origin.targetAnchor,
+        };
+        const resultModel = group.addEdgeModel(subProcess, addModel);
+        if (this.graph.executeCommand) {
+          this.graph.executeCommand('update', {
+            itemId: subProcess.get('id'),
+            updateModel: resultModel
+          });
+        } else {
+          this.graph.updateItem(node, resultModel);
+        }
+      }
+    },
+    _addEdge() {
+      if (this.origin.targetNode) {
+        const timestamp = new Date().getTime();
+        const addModel = {
+          id: 'flow' + timestamp,
+          clazz: 'flow',
+          source: this.origin.sourceNode.get('id'),
+          target: this.origin.targetNode.get('id'),
+          sourceAnchor: this.origin.sourceAnchor,
+          targetAnchor: this.origin.targetAnchor,
+        };
+        if (this.graph.executeCommand) {
+          this.graph.executeCommand('add', {
+            type: 'edge',
+            addModel: addModel
+          });
+        } else {
+          this.graph.add('edge', addModel);
+        }
+      }
+    }
+  });
+}

+ 147 - 0
web/src/components/gva-wfd/behavior/dragNode.js

@@ -0,0 +1,147 @@
+import editorStyle from "../util/defaultStyle";
+
+export default function(G6){
+  G6.registerBehavior('dragNode', {
+    getDefaultCfg() {
+      return {
+        updateEdge: true,
+        delegate: true,
+        delegateStyle: {},
+        align: true,
+      };
+    },
+    getEvents() {
+      return {
+        'node:dragstart': 'onDragStart',
+        'node:drag': 'onDrag',
+        'node:dragend': 'onDragEnd'
+      };
+    },
+    onDragStart(e) {
+      if (!this.shouldBegin.call(this, e)) {
+        return;
+      }
+      this.target = e.item;
+      this.origin = {
+        x: e.x,
+        y: e.y
+      };
+    },
+    onDrag(e) {
+      if (!this.origin) {
+        return;
+      }
+      if (!this.get('shouldUpdate').call(this, e)) {
+        return;
+      }
+      const origin = this.origin;
+      const groupId = this.target.get('groupId');
+      const model = this.target.get('model');
+      if (!this.point) {
+        this.point = {
+          x: model.x,
+          y: model.y
+        };
+      }
+      if (groupId) {
+        const subProcessNode = this.graph.findById(groupId);
+        const subProcessBBox = subProcessNode.getBBox();
+        const x = e.x - origin.x + this.point.x + subProcessBBox.x + subProcessBBox.width / 2;
+        const y = e.y - origin.y + this.point.y + subProcessBBox.y + subProcessBBox.height / 2;
+        this.origin = { x: e.x, y: e.y };
+        this.point = {
+          x: x - subProcessBBox.x - subProcessBBox.width / 2,
+          y: y - subProcessBBox.y - subProcessBBox.height / 2
+        };
+        if (this.delegate) {
+          this._updateDelegate(this.target, x, y);
+        }
+      } else {
+        const x = e.x - origin.x + this.point.x;
+        const y = e.y - origin.y + this.point.y;
+        this.origin = { x: e.x, y: e.y };
+        this.point = { x, y };
+        if (this.delegate) {
+          this._updateDelegate(this.target, x, y);
+        }
+      }
+    },
+    onDragEnd(e) {
+      if (!this.shouldEnd.call(this, e)) {
+        return;
+      }
+      if (!this.origin) {
+        return;
+      }
+      const delegateShape = e.item.get('delegateShape');
+      const groupId = this.target.get('groupId');
+      if (groupId) {
+        if (delegateShape) {
+          const subProcessNode = this.graph.findById(groupId);
+          const subProcessBBox = subProcessNode.getBBox();
+          const bbox = delegateShape.getBBox();
+          const x = bbox.x + bbox.width / 2 - subProcessBBox.x - subProcessBBox.width / 2;
+          const y = bbox.y + bbox.height / 2 - subProcessBBox.y - subProcessBBox.height / 2;
+          delegateShape.remove();
+          this.target.set('delegateShape', null);
+          const group = subProcessNode.getContainer();
+          const id = this.target.get('id');
+          const resultModel = group.updateNodeModel(subProcessNode, id, { x, y });
+          this._updateItem(subProcessNode, resultModel);
+        }
+      } else {
+        if (delegateShape) {
+          const bbox = delegateShape.getBBox();
+          const x = bbox.x + bbox.width / 2;
+          const y = bbox.y + bbox.height / 2;
+          delegateShape.remove();
+          this.target.set('delegateShape', null);
+          this._updateItem(this.target, { x, y });
+        }
+      }
+      this.point = null;
+      this.origin = null;
+      this.graph.emit('afternodedragend', this.target);
+    },
+    _updateItem(item, point) {
+      if(this.graph.executeCommand) {
+        this.graph.executeCommand('update', {
+          itemId: item.get('id'),
+          updateModel: point
+        });
+      }else {
+        if (this.get('updateEdge')) {
+          this.graph.updateItem(item, point);
+        } else {
+          item.updatePosition(point);
+          this.graph.paint();
+        }
+      }
+    },
+    _updateDelegate(item, x, y) {
+      const self = this;
+      let shape = item.get('delegateShape');
+      const bbox = item.get('keyShape').getBBox();
+      if (!shape) {
+        const parent = self.graph.get('group');
+        const attrs = editorStyle.nodeDelegationStyle;
+        // model上的x, y是相对于图形中心的,delegateShape是g实例,x,y是绝对坐标
+        shape = parent.addShape('rect', {
+          attrs: {
+            width: bbox.width,
+            height: bbox.height,
+            x: x - bbox.width / 2,
+            y: y - bbox.height / 2,
+            nodeId:item.get('id'),
+            ...attrs
+          }
+        });
+        shape.set('capture', false);
+        item.set('delegateShape', shape);
+      }
+      shape.attr({ x: x - bbox.width / 2, y: y - bbox.height / 2 });
+      this.graph.paint();
+      this.graph.emit('afternodedrag',shape);
+    },
+  });
+}

+ 149 - 0
web/src/components/gva-wfd/behavior/dragPanelItemAddNode.js

@@ -0,0 +1,149 @@
+import editorStyle from "../util/defaultStyle";
+import { getShapeName } from '../util/clazz';
+
+export default function(G6){
+  G6.registerBehavior('dragPanelItemAddNode', {
+    getDefaultCfg() {
+      return {
+
+      };
+    },
+    getEvents() {
+      return {
+        'canvas:mousemove': 'onMouseMove',
+        'canvas:mouseup': 'onMouseUp',
+        'canvas:mouseleave': 'onMouseLeave',
+      }
+    },
+    onMouseMove(e){
+      if(this.graph.get('addNodeDragging')){
+        let delegateShape = this.graph.get('addDelegateShape');
+        const addModel = this.graph.get('addModel');
+        const width = parseInt(addModel.size.split('*')[0]);
+        const height = parseInt(addModel.size.split('*')[1]);
+        const point = this.graph.getPointByClient(e.x,e.y);
+        const x = point.x;
+        const y = point.y;
+        if (!delegateShape) {
+          const parent = this.graph.get('group');
+          delegateShape = parent.addShape('rect', {
+            attrs: {
+              width,
+              height,
+              x: x - width / 2,
+              y: y - height / 2,
+              ...editorStyle.nodeDelegationStyle,
+            }
+          });
+          delegateShape.set('capture', false);
+          this.graph.set('addDelegateShape', delegateShape);
+        }
+        delegateShape.attr({ x: x - width / 2, y: y - height / 2 });
+        this.graph.paint();
+        this.graph.emit('afternodedrag',delegateShape);
+      }
+    },
+    onMouseUp(e){
+      if (this.graph.get('addNodeDragging')) {
+        const p = this.graph.getPointByClient(e.clientX, e.clientY);
+        const subProcessNode = this.graph.find('node', (node) => {
+          if (node.get('model')) {
+            const clazz = node.get('model').clazz;
+            if (clazz === 'subProcess') {
+              const bbox = node.getBBox();
+              return p.x > bbox.minX && bbox.maxX > p.x
+                && p.y > bbox.minY && bbox.maxY > p.y;
+            }
+          } else {
+            return false;
+          }
+        });
+        if (subProcessNode) {
+          if (p.x > 0 && p.y > 0) {
+            this._addNodeBySubProcess(p, subProcessNode);
+          }
+        } else {
+          if (p.x > 0 && p.y > 0) {
+            this._addNode(p);
+          }
+        }
+      }
+    },
+    _addNodeBySubProcess(p, node) {
+      if (this.graph.get('addNodeDragging')) {
+        const addModel = this.graph.get('addModel');
+        const { clazz = 'userTask' } = addModel;
+        addModel.shape = getShapeName(clazz);
+        addModel.size = addModel.size.split("*");
+        const timestamp = new Date().getTime();
+        const id = clazz + timestamp;
+        const bbox = node.getBBox();
+        const x = p.x - bbox.x - bbox.width / 2;
+        const y = p.y - bbox.y - bbox.height / 2;
+        const nodeCfg = {
+          ...addModel,
+          x,
+          y,
+          id
+        };
+        const group = node.getContainer();
+        const resultModel = group.addNodeModel(node, nodeCfg);
+        if (this.graph.executeCommand) {
+          this.graph.executeCommand('update', {
+            itemId: node.get('id'),
+            updateModel: resultModel,
+          });
+        } else {
+          this.graph.updateItem(node, resultModel);
+        }
+      }
+    },
+    onMouseLeave(e){
+      if (this.graph.get('addNodeDragging')) {
+        this._clearDelegate();
+        this.graph.emit('afternodedragend');
+      }
+    },
+    _clearDelegate(){
+      const delegateShape = this.graph.get('addDelegateShape');
+      if (delegateShape) {
+        delegateShape.remove();
+        this.graph.set('addDelegateShape', null);
+        this.graph.paint();
+      }
+      this.graph.emit('afternodedragend');
+    },
+    _addNode(p){
+      if (this.graph.get('addNodeDragging')) {
+        const addModel = this.graph.get('addModel');
+        const { clazz = 'userTask' } = addModel;
+        //type、shape属性同时存在即兼容之前版本的数据,又兼容g6的升级
+        addModel.type = getShapeName(clazz);
+        addModel.shape = getShapeName(clazz);
+        const timestamp = new Date().getTime();
+        const id = clazz + timestamp;
+        const x = p.x;
+        const y = p.y;
+        if (this.graph.executeCommand) {
+          this.graph.executeCommand('add', {
+            type: 'node',
+            addModel: {
+              ...addModel,
+              x: x,
+              y: y,
+              id: id,
+            },
+          });
+        } else {
+          this.graph.add('node', {
+            ...addModel,
+            x: x,
+            y: y,
+            id: id,
+          });
+        }
+        this._clearDelegate();
+      }
+    }
+  });
+}

+ 97 - 0
web/src/components/gva-wfd/behavior/dragPoint.js

@@ -0,0 +1,97 @@
+export default function(G6) {
+  G6.registerBehavior('dragPoint', {
+    getDefaultCfg() {
+      return {
+        updateEdge: true,
+        delegate: true,
+        delegateStyle: {},
+        dragEdge: false,
+      };
+    },
+    getEvents() {
+      return {
+        'controlPoint:dragstart': 'onDragStart',
+        'controlPoint:drag': 'onDrag',
+        'controlPoint:dragend': 'onDragEnd',
+      };
+    },
+    onDragStart(e) {
+      const node = e.target.getParent().getParent().get('item');
+      const anchorIndex = e.item.get('index');
+      this.target = e.item;
+      this.origin = {
+        x: e.x,
+        y: e.y,
+        sourceNode: node,
+        sourceAnchor: anchorIndex,
+      };
+      this.graph.set('edgeDragging', true);
+    },
+    onDrag(e) {
+      if (!this.origin) {
+        return;
+      }
+
+      const node = e.target.getParent().getParent().get('item');
+      const anchorIndex = e.item.get('index');
+
+      const model = node.getModel();
+      const currentWidth = model.size[0];
+      const currentHeight = model.size[1];
+      const addWidth = e.x - this.origin.x;
+      const addHeight = e.y - this.origin.y;
+      let width = currentWidth;
+      let height = currentHeight;
+      // 0,0 // 两个都是负的变大
+      // 1,0 // 一正 一负 变大
+      // 1,1 // 两个都是负变大
+      // 0,1 // 一负 一正 变大
+      const pointIndex = this.origin.sourceAnchor;
+      if (pointIndex === 0) {
+        width = currentWidth - addWidth;
+        height = currentHeight - addHeight;
+      } else if (pointIndex === 1) {
+        height = currentHeight - addHeight;
+      } else if (pointIndex === 2) {
+        width = currentWidth + addWidth;
+        height = currentHeight - addHeight;
+      } else if (pointIndex === 3) {
+        width = currentWidth + addWidth;
+      } else if (pointIndex === 4) {
+        width = currentWidth + addWidth;
+        height = currentHeight + addHeight;
+      } else if (pointIndex === 5) {
+        height = currentHeight + addHeight;
+      } else if (pointIndex === 6) {
+        width = currentWidth - addWidth;
+        height = currentHeight + addHeight;
+      } else if (pointIndex === 7) {
+        width = currentWidth - addWidth;
+      }
+      const group = node.getContainer();
+      // 移动过程中的控制点全部隐藏。
+      group.controlPointShapes.forEach(a => a.hide());
+
+      this.graph.updateItem(node, { size: [width, height], });
+      this.origin = {
+        x: e.x,
+        y: e.y,
+        sourceNode: node,
+        sourceAnchor: anchorIndex,
+      };
+
+    },
+    onDragEnd(e) {
+      if (!this.origin) {
+        return;
+      }
+      const node = e.target.getParent().getParent().get('item');
+      this.target = null;
+      this.origin = null;
+      const group = node.getContainer();
+      group.clearControlPoints(group);
+      group.showControlPoints(group);
+      this.graph.set('edgeDragging', false);
+    },
+  });
+}

+ 23 - 0
web/src/components/gva-wfd/behavior/hoverAnchorActived.js

@@ -0,0 +1,23 @@
+export default function(G6){
+  G6.registerBehavior('hoverAnchorActived', {
+    getEvents() {
+      return {
+        'anchor:mouseenter': 'onAnchorEnter',
+        'anchor:mousemove': 'onAnchorEnter',
+        'anchor:mouseleave': 'onAnchorLeave',
+      }
+    },
+    onAnchorEnter(e){
+      if(!this.graph.get('edgeDragging'))
+        this.graph.setItemState(e.item, 'active-anchor', true);
+    },
+    onAnchorLeave(e){
+      if(!this.graph.get('edgeDragging')) {
+        let node = e.item.getContainer().getParent();
+        if(node) {
+          this.graph.setItemState(e.item, 'active-anchor', false);
+        }
+      }
+    }
+  });
+}

+ 29 - 0
web/src/components/gva-wfd/behavior/hoverNodeActived.js

@@ -0,0 +1,29 @@
+import { Marker } from '@antv/g-canvas/lib/shape';
+export default function(G6){
+  G6.registerBehavior('hoverNodeActived', {
+    getEvents() {
+      return {
+        'node:mouseenter': 'onNodeEnter',
+        'node:mouseleave': 'onNodeLeave',
+        'anchor:mouseleave': 'onAnchorLeave',
+      }
+    },
+    onAnchorLeave(e){
+      let node = e.item.getContainer().getParent();
+      if(node && !this.graph.get('edgeDragging')) {
+        this.graph.setItemState(node.get('item'), 'show-anchor', false);
+      }
+    },
+    onNodeEnter(e){
+      const clazz = e.item.getModel().clazz;
+      if(clazz !== 'endEvent' && !this.graph.get('edgeDragging')){
+        this.graph.setItemState(e.item, 'show-anchor', true);
+      }
+    },
+    onNodeLeave(e){
+      if(!(e.target instanceof Marker) && !this.graph.get('edgeDragging')) {
+        this.graph.setItemState(e.item, 'show-anchor', false);
+      }
+    },
+  });
+}

+ 20 - 0
web/src/components/gva-wfd/behavior/index.js

@@ -0,0 +1,20 @@
+import clickSelected from './clickSelected'
+import deleteItem from './deleteItem'
+import dragNode from './dragNode'
+import dragEdge from './dragEdge'
+import dragPanelItemAddNode from './dragPanelItemAddNode'
+import hoverAnchorActived from './hoverAnchorActived'
+import hoverNodeActived from './hoverNodeActived'
+import itemAlign from './itemAlign'
+import dragPoint from "./dragPoint";
+export default function(G6){
+  clickSelected(G6);
+  deleteItem(G6);
+  dragNode(G6);
+  dragEdge(G6);
+  dragPanelItemAddNode(G6);
+  hoverAnchorActived(G6);
+  hoverNodeActived(G6);
+  itemAlign(G6);
+  dragPoint(G6);
+}

+ 140 - 0
web/src/components/gva-wfd/behavior/itemAlign.js

@@ -0,0 +1,140 @@
+import { each } from '@antv/util';
+import { vec2 } from '@antv/matrix-util';
+export default function(G6){
+  const {mix} = G6.Util;
+  G6.registerBehavior('itemAlign', {
+    getDefaultCfg() {
+      return {
+        alignLineStyle: { stroke: '#FA8C16', lineWidth: 1 },
+        tolerance: 5,
+        _alignLines: [],
+      };
+    },
+    getEvents() {
+      return {
+        'afternodedrag': 'onDrag',
+        'afternodedragend': 'onDragEnd',
+      };
+    },
+    onDrag(shape) {
+      this._clearAlignLine();
+      this._itemAlign(shape);
+    },
+    onDragEnd() {
+      this._clearAlignLine();
+    },
+    _itemAlign(item){
+      const bbox = item.getBBox();
+      const ct = { x: bbox.x + bbox.width / 2, y: bbox.y };
+      const cc = { x: bbox.x + bbox.width / 2, y: bbox.y + bbox.height / 2 };
+      const cb = { x: bbox.x + bbox.width / 2, y: bbox.y + bbox.height };
+      const lc = { x: bbox.x, y: bbox.y + bbox.height / 2 };
+      const rc = { x: bbox.x + bbox.width, y: bbox.y + bbox.height / 2 };
+      const nodes = this.graph.getNodes();
+      each(nodes, (node) => {
+        const horizontalLines = [];
+        const verticalLines = [];
+        let p = null;
+        const bbox1 = node.getBBox();
+        each(this.getHorizontalLines(bbox1), (line) => {
+          horizontalLines.push(this.getDistance(line,ct));
+          horizontalLines.push(this.getDistance(line,cc));
+          horizontalLines.push(this.getDistance(line,cb));
+        });
+        each(this.getVerticalLines(bbox1), (line) => {
+          verticalLines.push(this.getDistance(line,lc));
+          verticalLines.push(this.getDistance(line,cc));
+          verticalLines.push(this.getDistance(line,rc));
+        });
+        horizontalLines.sort((a,b)=> a.dis-b.dis);
+        verticalLines.sort((a,b)=> a.dis-b.dis);
+        if(horizontalLines.length > 0 && horizontalLines[0].dis < this.tolerance){
+          item.attr({y: horizontalLines[0].line[1] - horizontalLines[0].point.y + bbox.y });
+          p = { horizontals: [horizontalLines[0]] };
+          for (let i = 1; i < 3; i++) horizontalLines[0].dis === horizontalLines[i].dis && p.horizontals.push(horizontalLines[i]);
+        }
+        if(verticalLines.length > 0 && verticalLines[0].dis < this.tolerance){
+          item.attr({ x: verticalLines[0].line[0] - verticalLines[0].point.x + bbox.x });
+          p ? p.verticals = [verticalLines[0]] : p = { verticals: [verticalLines[0]] };
+          for (let i = 1; i < 3; i++) verticalLines[0].dis === verticalLines[i].dis && p.verticals.push(verticalLines[i]);
+        }
+        if(p){
+          p.bbox = bbox;
+          this._addAlignLine(p);
+        }
+      })
+    },
+    _addAlignLine(p){
+      const group = this.graph.get('group');
+      const bbox = p.bbox;
+      const lineStyle = this.alignLineStyle;
+      const lineArr = this._alignLines;
+      if(p.horizontals){
+        each(p.horizontals, function(lineObj) {
+          const line = lineObj.line;
+          const point = lineObj.point;
+          const lineHalf = (line[0] + line[2]) / 2;
+          let x1,x2;
+          if(point.x < lineHalf){
+            x1 = point.x - bbox.width / 2;
+            x2 = Math.max(line[0], line[2]);
+          }else{
+            x1 = point.x + bbox.width / 2;
+            x2 = Math.min(line[0], line[2]);
+          }
+          const shape = group.addShape('line', { attrs: mix({ x1, y1: line[1], x2, y2: line[1] }, lineStyle), capture: false });
+          lineArr.push(shape);
+        })
+      }
+      if(p.verticals){
+        each(p.verticals, function(lineObj) {
+          const line = lineObj.line;
+          const point = lineObj.point;
+          const lineHalf = (line[1] + line[3]) / 2;
+          let y1,y2;
+          if(point.y < lineHalf){
+            y1 = point.y - bbox.height / 2;
+            y2 = Math.max(line[1], line[3]);
+          }else{
+            y1 = point.y + bbox.height / 2;
+            y2 = Math.min(line[1], line[3]);
+          }
+          const shape = group.addShape('line', { attrs: mix({ x1: line[0], y1, x2: line[0], y2 }, lineStyle), capture: false });
+          lineArr.push(shape);
+        })
+      }
+    },
+    getHorizontalLines(bbox){
+      return [
+        [bbox.minX, bbox.minY, bbox.maxX, bbox.minY],       // tltr
+        [bbox.minX, bbox.centerY, bbox.maxX, bbox.centerY], // lcrc
+        [bbox.minX, bbox.maxY, bbox.maxX, bbox.maxY],       // blbr
+      ]
+    },
+    getVerticalLines(bbox){
+      return [
+        [bbox.minX, bbox.minY, bbox.minX, bbox.maxY],       // tlbl
+        [bbox.centerX, bbox.minY, bbox.centerX, bbox.maxY], // tcbc
+        [bbox.maxX, bbox.minY, bbox.maxX, bbox.maxY],       // trbr
+      ]
+    },
+    getDistance(line, point) {
+      return { line, point, dis: this.pointLineDistance(line[0], line[1], line[2], line[3], point.x, point.y) };
+    },
+    pointLineDistance: function(lineX1, lineY1, lineX2, lineY2, pointX, pointY) {
+      const lineLength = [lineX2 - lineX1, lineY2 - lineY1];
+      if (vec2.exactEquals(lineLength, [0, 0])) return NaN;
+      let s = [-lineLength[1], lineLength[0]];
+      vec2.normalize(s, s);
+      return Math.abs(vec2.dot([pointX - lineX1, pointY - lineY1], s));
+    },
+    _clearAlignLine(){
+      each(this._alignLines, (line) => {
+        line.remove();
+      });
+      this._alignLines = [];
+      this.graph.paint();
+    },
+
+  });
+}

+ 35 - 0
web/src/components/gva-wfd/components/DetailPanel/DefaultDetail.vue

@@ -0,0 +1,35 @@
+<template>
+    <div>
+        <div class="panelRow">
+            <div>{{i18n['label']}}:</div>
+            <el-input style="width:90%; font-size:12px"
+                      :disabled="readOnly"
+                      :value="model.label"
+                      @input="(value) => {onChange('label', value)}" />
+        </div>
+        <div class="panelRow">
+            <el-checkbox @change="(value) => onChange('hideIcon', value)"
+                         :disabled="readOnly"
+                         :value="!!model.hideIcon">{{i18n['hideIcon']}}</el-checkbox>
+        </div>
+    </div>
+</template>
+<script>
+  export default {
+    inject: ['i18n'],
+    props: {
+      model: {
+        type:Object,
+        default: ()=>({}),
+      },
+      onChange: {
+        type: Function,
+        default: ()=>{}
+      },
+      readOnly:{
+        type: Boolean,
+        default: false,
+      }
+    },
+  }
+</script>

+ 31 - 0
web/src/components/gva-wfd/components/DetailPanel/EndEventDetail.vue

@@ -0,0 +1,31 @@
+<template>
+    <div :data-clazz="model.clazz">
+        <div class="panelTitle">{{i18n['endEvent']}}</div>
+        <div class="panelBody">
+            <DefaultDetail :model="model" :onChange="onChange" :readOnly="readOnly" />
+        </div>
+    </div>
+</template>
+<script>
+  import DefaultDetail from "./DefaultDetail";
+  export default {
+    inject: ['i18n'],
+    components: {
+      DefaultDetail
+    },
+    props: {
+      model: {
+        type:Object,
+        default: ()=>({}),
+      },
+      onChange: {
+        type: Function,
+        default: ()=>{}
+      },
+      readOnly:{
+        type: Boolean,
+        default: false,
+      }
+    },
+  }
+</script>

+ 52 - 0
web/src/components/gva-wfd/components/DetailPanel/FlowDetail.vue

@@ -0,0 +1,52 @@
+<template>
+    <div :data-clazz="model.clazz">
+        <div class="panelTitle">{{i18n['sequenceFlow']}}</div>
+        <div class="panelBody">
+            <DefaultDetail :model="model" :onChange="onChange" :readOnly="readOnly" />
+            <div class="panelRow">
+                <div>{{i18n['sequenceFlow.expression']}}:</div>
+                <el-input style="width:90%; font-size:12px"
+                          type="textarea"
+                          :rows="4"
+                          :disabled="readOnly"
+                          :value="model.conditionExpression"
+                          @input="(value) => {onChange('conditionExpression', value)}" />
+            </div>
+            <div class="panelRow">
+                <div>{{i18n['sequenceFlow.seq']}}:</div>
+                <el-input style="width:90%; font-size:12px"
+                          :disabled="readOnly"
+                          :value="model.seq"
+                          @input="(value) => {onChange('seq', value)}" />
+            </div>
+            <div class="panelRow">
+                <el-checkbox @change="(value) => onChange('reverse', value)"
+                             :disabled="readOnly"
+                             :value="!!model.reverse">{{i18n['sequenceFlow.reverse']}}</el-checkbox>
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+  import DefaultDetail from "./DefaultDetail";
+  export default {
+    inject: ['i18n'],
+    components: {
+      DefaultDetail
+    },
+    props: {
+      model: {
+        type:Object,
+        default: ()=>({}),
+      },
+      onChange: {
+        type: Function,
+        default: ()=>{}
+      },
+      readOnly:{
+        type: Boolean,
+        default: false,
+      }
+    },
+  }
+</script>

+ 32 - 0
web/src/components/gva-wfd/components/DetailPanel/GatewayDetail.vue

@@ -0,0 +1,32 @@
+<template>
+    <div :data-clazz="model.clazz">
+        <div class="panelTitle">{{model.clazz === 'exclusiveGateway' || model.clazz === 'gateway' ? i18n['exclusiveGateway']
+            : model.clazz === 'parallelGateway' ? i18n['parallelGateway'] : i18n['inclusiveGateway']}}</div>
+        <div class="panelBody">
+            <DefaultDetail :model="model" :onChange="onChange" :readOnly="readOnly" />
+        </div>
+    </div>
+</template>
+<script>
+  import DefaultDetail from "./DefaultDetail";
+  export default {
+    inject: ['i18n'],
+    components: {
+      DefaultDetail
+    },
+    props: {
+      model: {
+        type:Object,
+        default: ()=>({}),
+      },
+      onChange: {
+        type: Function,
+        default: ()=>{}
+      },
+      readOnly:{
+        type: Boolean,
+        default: false,
+      }
+    },
+  }
+</script>

+ 38 - 0
web/src/components/gva-wfd/components/DetailPanel/JavaTaskDetail.vue

@@ -0,0 +1,38 @@
+<template>
+    <div :data-clazz="model.clazz">
+        <div class="panelTitle">{{i18n['javaTask']}}</div>
+        <div class="panelBody">
+            <DefaultDetail :model="model" :onChange="onChange" :readOnly="readOnly" />
+            <div class="panelRow">
+                <div>{{i18n['javaTask.javaClass']}}:</div>
+                <el-input style="width:90%; font-size:12px"
+                          :disabled="readOnly"
+                          :value="model.javaClass"
+                          @input="(value) => {onChange('javaClass', value)}" />
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+  import DefaultDetail from "./DefaultDetail";
+  export default {
+    inject: ['i18n'],
+    components: {
+      DefaultDetail
+    },
+    props: {
+      model: {
+        type:Object,
+        default: ()=>({}),
+      },
+      onChange: {
+        type: Function,
+        default: ()=>{}
+      },
+      readOnly:{
+        type: Boolean,
+        default: false,
+      }
+    },
+  }
+</script>

+ 54 - 0
web/src/components/gva-wfd/components/DetailPanel/MailTaskDetail.vue

@@ -0,0 +1,54 @@
+<template>
+    <div :data-clazz="model.clazz">
+        <div class="panelTitle">{{i18n['mailTask']}}</div>
+        <div class="panelBody">
+            <DefaultDetail :model="model" :onChange="onChange" :readOnly="readOnly" />
+            <div class="panelRow">
+                <div>{{i18n['mailTask.to']}}:</div>
+                <el-input style="width:90%; font-size:12px"
+                          :disabled="readOnly"
+                          :value="model.to"
+                          @input="(value) => {onChange('to', value)}" />
+            </div>
+            <div class="panelRow">
+                <div>{{i18n['mailTask.subject']}}:</div>
+                <el-input style="width:90%; font-size:12px"
+                          :disabled="readOnly"
+                          :value="model.subject"
+                          @input="(value) => {onChange('subject', value)}" />
+            </div>
+            <div class="panelRow">
+                <div>{{i18n['mailTask.content']}}:</div>
+                <el-input style="width:90%; font-size:12px"
+                          type="textarea"
+                          :rows="4"
+                          :disabled="readOnly"
+                          :value="model.content"
+                          @input="(value) => {onChange('content', value)}" />
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+  import DefaultDetail from "./DefaultDetail";
+  export default {
+    inject: ['i18n'],
+    components: {
+      DefaultDetail
+    },
+    props: {
+      model: {
+        type:Object,
+        default: ()=>({}),
+      },
+      onChange: {
+        type: Function,
+        default: ()=>{}
+      },
+      readOnly:{
+        type: Boolean,
+        default: false,
+      }
+    },
+  }
+</script>

+ 45 - 0
web/src/components/gva-wfd/components/DetailPanel/MessageEventDetail.vue

@@ -0,0 +1,45 @@
+<template>
+    <div :data-clazz="model.clazz">
+        <div class="panelTitle">{{i18n['messageEvent']}}</div>
+        <div class="panelBody">
+            <DefaultDetail :model="model" :onChange="onChange" :readOnly="readOnly" />
+            <div class="panelRow">
+                <div>{{i18n['messageEvent.message']}}:</div>
+                <el-select style="width:90%; font-size: 12px"
+                           :placeholder="i18n['messageEvent.message']"
+                           :value="model.message"
+                           :disabled="readOnly"
+                           @change="(e) => { onChange('message', e) }">
+                    <el-option v-for="message in messageDefs" :key="message.id" :label="message.name" :value="message.id" />
+                </el-select>
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+  import DefaultDetail from "./DefaultDetail";
+  export default {
+    inject: ['i18n'],
+    components: {
+      DefaultDetail
+    },
+    props: {
+      model: {
+        type:Object,
+        default: ()=>({}),
+      },
+      onChange: {
+        type: Function,
+        default: ()=>{}
+      },
+      readOnly:{
+        type: Boolean,
+        default: false,
+      },
+      messageDefs: {
+        type: Array,
+        default: ()=>([]),
+      },
+    },
+  }
+</script>

+ 80 - 0
web/src/components/gva-wfd/components/DetailPanel/ProcessDetail.vue

@@ -0,0 +1,80 @@
+<template>
+    <div :data-clazz="model.clazz">
+        <div class="panelTitle">{{i18n['process']}}</div>
+        <div class="panelBody">
+            <DefaultDetail :model="model" :onChange="onChange" :readOnly="readOnly" />
+            <div class="panelRow">
+                <div>{{i18n['process.category']}}:</div>
+                <el-select style="width:90%; font-size:12px"
+                           :disabled="readOnly"
+                           :value="model.category"
+                           allow-create
+                           :filterable="true"
+                           :filter-method="filterCategory"
+                           @change="(e) => onChange('category', e)">
+                    <el-option v-for="category in categoryCopy" :key="category.id" :label="category.name" :value="category.id" />
+                </el-select>
+            </div>
+            <div class="panelRow">
+                <div>{{i18n['process.id']}}:</div>
+                <el-input style="width:90%; font-size:12px"
+                          :disabled="readOnly"
+                          :value="model.id"
+                          @input="(value) => {onChange('id', value)}" />
+            </div>
+            <div class="panelRow">
+                <div>{{i18n['process.name']}}:</div>
+                <el-input style="width:90%; font-size:12px"
+                          :disabled="readOnly"
+                          :value="model.name"
+                          @input="(value) => {onChange('name', value)}" />
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+  import DefaultDetail from "./DefaultDetail";
+  export default {
+    inject: ['i18n'],
+    components: {
+      DefaultDetail
+    },
+    props: {
+      model: {
+        type:Object,
+        default: ()=>({}),
+      },
+      categorys: {
+        type: Array,
+        default: ()=>([]),
+      },
+      onChange: {
+        type: Function,
+        default: ()=>{}
+      },
+      readOnly:{
+        type: Boolean,
+        default: false,
+      }
+    },
+    data() {
+      return {
+        categoryCopy: this.categorys,
+      }
+    },
+    methods: {
+      filterCategory(input) {
+        if (input) {
+          this.categoryCopy = this.categorys.filter((item) => {
+            if (!!~item.name.indexOf(input) || !!~item.name.toLowerCase().indexOf(input.toLowerCase())) {
+              return true
+            }
+          })
+        } else {
+          this.categoryCopy = this.categorys;
+        }
+      }
+    }
+
+  }
+</script>

+ 45 - 0
web/src/components/gva-wfd/components/DetailPanel/ReceiveTaskDetail.vue

@@ -0,0 +1,45 @@
+<template>
+    <div :data-clazz="model.clazz">
+        <div class="panelTitle">{{i18n['receiveTask']}}</div>
+        <div class="panelBody">
+            <DefaultDetail :model="model" :onChange="onChange" :readOnly="readOnly" />
+            <div class="panelRow">
+                <div>{{i18n['receiveTask.waitState']}}:</div>
+                <el-input style="width:90%; font-size:12px"
+                          :disabled="readOnly"
+                          :value="model.waitState"
+                          @input="(value) => {onChange('waitState', value)}" />
+            </div>
+            <div class="panelRow">
+                <div>{{i18n['receiveTask.stateValue']}}:</div>
+                <el-input style="width:90%; font-size:12px"
+                          :disabled="readOnly"
+                          :value="model.stateValue"
+                          @input="(value) => {onChange('stateValue', value)}" />
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+  import DefaultDetail from "./DefaultDetail";
+  export default {
+    inject: ['i18n'],
+    components: {
+      DefaultDetail
+    },
+    props: {
+      model: {
+        type:Object,
+        default: ()=>({}),
+      },
+      onChange: {
+        type: Function,
+        default: ()=>{}
+      },
+      readOnly:{
+        type: Boolean,
+        default: false,
+      }
+    },
+  }
+</script>

+ 40 - 0
web/src/components/gva-wfd/components/DetailPanel/ScriptTaskDetail.vue

@@ -0,0 +1,40 @@
+<template>
+    <div :data-clazz="model.clazz">
+        <div class="panelTitle">{{i18n['scriptTask']}}</div>
+        <div class="panelBody">
+            <DefaultDetail :model="model" :onChange="onChange" :readOnly="readOnly" />
+            <div class="panelRow">
+                <div>{{i18n['scriptTask.script']}}:</div>
+                <el-input style="width:90%; font-size:12px"
+                          type="textarea"
+                          :rows="4"
+                          :disabled="readOnly"
+                          :value="model.script"
+                          @input="(value) => {onChange('script', value)}" />
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+  import DefaultDetail from "./DefaultDetail";
+  export default {
+    inject: ['i18n'],
+    components: {
+      DefaultDetail
+    },
+    props: {
+      model: {
+        type:Object,
+        default: ()=>({}),
+      },
+      onChange: {
+        type: Function,
+        default: ()=>{}
+      },
+      readOnly:{
+        type: Boolean,
+        default: false,
+      }
+    },
+  }
+</script>

+ 45 - 0
web/src/components/gva-wfd/components/DetailPanel/SignalEventDetail.vue

@@ -0,0 +1,45 @@
+<template>
+    <div :data-clazz="model.clazz">
+        <div class="panelTitle">{{i18n['signalEvent']}}</div>
+        <div class="panelBody">
+            <DefaultDetail :model="model" :onChange="onChange" :readOnly="readOnly" />
+            <div class="panelRow">
+                <div>{{i18n['signalEvent.signal']}}:</div>
+                <el-select style="width:90%; font-size: 12px"
+                           :placeholder="i18n['signalEvent.signal']"
+                           :value="model.signal"
+                           :disabled="readOnly"
+                           @change="(e) => { onChange('signal', e) }">
+                    <el-option v-for="signal in signalDefs" :key="signal.id" :label="signal.name" :value="signal.id" />
+                </el-select>
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+  import DefaultDetail from "./DefaultDetail";
+  export default {
+    inject: ['i18n'],
+    components: {
+      DefaultDetail
+    },
+    props: {
+      model: {
+        type:Object,
+        default: ()=>({}),
+      },
+      onChange: {
+        type: Function,
+        default: ()=>{}
+      },
+      readOnly:{
+        type: Boolean,
+        default: false,
+      },
+      signalDefs: {
+        type: Array,
+        default: ()=>([]),
+      },
+    },
+  }
+</script>

+ 31 - 0
web/src/components/gva-wfd/components/DetailPanel/StartEventDetail.vue

@@ -0,0 +1,31 @@
+<template>
+    <div :data-clazz="model.clazz">
+        <div class="panelTitle">{{i18n['startEvent']}}</div>
+        <div class="panelBody">
+            <DefaultDetail :model="model" :onChange="onChange" :readOnly="readOnly" />
+        </div>
+    </div>
+</template>
+<script>
+  import DefaultDetail from "./DefaultDetail";
+  export default {
+    inject: ['i18n'],
+    components: {
+      DefaultDetail
+    },
+    props: {
+      model: {
+        type:Object,
+        default: ()=>({}),
+      },
+      onChange: {
+        type: Function,
+        default: ()=>{}
+      },
+      readOnly:{
+        type: Boolean,
+        default: false,
+      }
+    },
+  }
+</script>

+ 49 - 0
web/src/components/gva-wfd/components/DetailPanel/TimerEventDetail.vue

@@ -0,0 +1,49 @@
+<template>
+    <div :data-clazz="model.clazz">
+        <div class="panelTitle">{{i18n['timerEvent']}}</div>
+        <div class="panelBody">
+            <DefaultDetail :model="model" :onChange="onChange" :readOnly="readOnly" />
+            <div class="panelRow">
+                <div>{{i18n['timerEvent.cycle']}}:</div>
+                <el-input style="width:90%; font-size:12px"
+                          type="textarea"
+                          :rows="4"
+                          :disabled="readOnly"
+                          :value="model.cycle"
+                          @input="(value) => {onChange('cycle', value)}" />
+            </div>
+            <div class="panelRow">
+                <div>{{i18n['timerEvent.duration']}}:</div>
+                <el-input style="width:90%; font-size:12px"
+                          type="textarea"
+                          :rows="4"
+                          :disabled="readOnly"
+                          :value="model.duration"
+                          @input="(value) => {onChange('duration', value)}" />
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+  import DefaultDetail from "./DefaultDetail";
+  export default {
+    inject: ['i18n'],
+    components: {
+      DefaultDetail
+    },
+    props: {
+      model: {
+        type:Object,
+        default: ()=>({}),
+      },
+      onChange: {
+        type: Function,
+        default: ()=>{}
+      },
+      readOnly:{
+        type: Boolean,
+        default: false,
+      }
+    },
+  }
+</script>

+ 143 - 0
web/src/components/gva-wfd/components/DetailPanel/UserTaskDetail.vue

@@ -0,0 +1,143 @@
+<template>
+    <div :data-clazz="model.clazz">
+        <div class="panelTitle">{{i18n['userTask']}}</div>
+        <div class="panelBody">
+            <DefaultDetail :model="model" :onChange="onChange" :readOnly="readOnly" />
+            <div class="panelRow">
+                <div>选择角色:</div>
+                <el-select style="width:90%; font-size: 12px"
+                           placeholder="请选择角色(与用户互斥)"
+                           :value="model.assignType"
+                           :disabled="readOnly"
+                           @change="(e) => { onChange('assignValue','');onChange('assignType', e) }">
+                    <el-option key="user" value="user" :label="'用户'"/>
+                    <el-option key="authority" value="authority" :label="'角色'"/>
+                </el-select>
+            </div>
+            <div v-if="model.assignType=='user'" class="panelRow">
+                <div>选择用户:</div>
+                <el-select style="width:90%; font-size:12px"
+                           :placeholder="'请选择用户'"
+                           :disabled="readOnly"
+                           :value="model.assignValue"
+                           clearable
+                           allow-create
+                           :filterable="true"
+                           :filter-method="filterUsers"
+                           @change="(e) => onChange('assignValue', e)">
+                    <el-option v-for="user in usersCopy" :key="user.id" :label="user.name" :value="user.id" />
+                </el-select>
+            </div>
+            <div v-if="model.assignType=='authority'"  class="panelRow">
+                <div>选择角色:</div>
+                <el-select style="width:90%; font-size:12px"
+                           :placeholder="'请选择角色'"
+                           :disabled="readOnly"
+                           :value="model.assignValue"
+                           allow-create
+                           clearable
+                           :filterable="true"
+                           :filter-method="authorities"
+                           @change="(e) => onChange('assignValue', e)">
+                    <el-option v-for="authority in authoritiesCopy" :key="authority.id" :label="authority.name" :value="authority.id" />
+                </el-select>
+            </div>
+            <div class="panelRow">
+                <div style="display:inline">{{i18n['userTask.dueDate']}}:</div>
+                <el-date-picker type="datetime"
+                                style="width:90%; min-width:null"
+                                :placeholder="i18n['userTask.dueDate.placeholder']"
+                                :disabled="readOnly"
+                                :value="model.dueDate"
+                                value-format="yyyy-MM-dd HH:mm:ss"
+                                @input="(value) => onChange('dueDate', value)" />
+            </div>
+            <div class="panelRow">
+                <el-checkbox @change="(value) => onChange('isSequential', value)"
+                             :disabled="readOnly"
+                             :value="!!model.isSequential">{{i18n['userTask.counterSign']}}</el-checkbox>
+            </div>
+        </div>
+    </div>
+</template>
+<script>
+  import DefaultDetail from "./DefaultDetail";
+  export default {
+    inject: ['i18n'],
+    components: {
+      DefaultDetail
+    },
+    props: {
+      model: {
+        type:Object,
+        default: ()=>({}),
+      },
+      users: {
+        type: Array,
+        default: ()=>([]),
+      },
+      authority: {
+        type: Array,
+        default: ()=>([]),
+      },
+      authorities: {
+        type: Array,
+        default: ()=>([]),
+      },
+      groups: {
+        type: Array,
+        default: ()=>([]),
+      },
+      onChange: {
+        type: Function,
+        default: ()=>{}
+      },
+      readOnly:{
+        type: Boolean,
+        default: false,
+      }
+    },
+    data() {
+      return {
+        usersCopy: this.users,
+        groupsCopy: this.groups,
+        authoritiesCopy:this.authorities
+      }
+    },
+    methods: {
+      filterUsers(input) {
+        if (input) {
+          this.usersCopy = this.users.filter((item) => {
+            if (!!~item.name.indexOf(input) || !!~item.name.toLowerCase().indexOf(input.toLowerCase())) {
+              return true
+            }
+          })
+        } else {
+          this.usersCopy = this.users;
+        }
+      },
+      filterAuthorities(input) {
+        if (input) {
+          this.authoritiesCopy = this.authorities.filter((item) => {
+            if (!!~item.name.indexOf(input) || !!~item.name.toLowerCase().indexOf(input.toLowerCase())) {
+              return true
+            }
+          })
+        } else {
+          this.authoritiesCopy = this.authorities;
+        }
+      },
+      filterGroups(input) {
+        if (input) {
+          this.groupsCopy = this.groups.filter((item) => {
+            if (!!~item.name.indexOf(input) || !!~item.name.toLowerCase().indexOf(input.toLowerCase())) {
+              return true
+            }
+          })
+        } else {
+          this.groupsCopy = this.groups;
+        }
+      }
+    }
+  }
+</script>

+ 122 - 0
web/src/components/gva-wfd/components/DetailPanel/index.vue

@@ -0,0 +1,122 @@
+<template>
+    <div class="detailPanel" :style="{'height':height+'px'}">
+        <UserTaskDetail v-if="model.clazz === 'userTask'" :model="model" :onChange="onChange" :readOnly="readOnly" :users="users" :authorities="authorities" :groups="groups" />
+        <ScriptTaskDetail v-else-if="model.clazz === 'scriptTask'" :model="model" :onChange="onChange" :readOnly="readOnly" />
+        <JavaTaskDetail v-else-if="model.clazz === 'javaTask'" :model="model" :onChange="onChange" :readOnly="readOnly" />
+        <ReceiveTaskDetail v-else-if="model.clazz === 'receiveTask'" :model="model" :onChange="onChange" :readOnly="readOnly" />
+        <MailTaskDetail v-else-if="model.clazz === 'mailTask'" :model="model" :onChange="onChange" :readOnly="readOnly" />
+        <TimerEventDetail v-else-if="model.clazz === 'timerStart' || model.clazz === 'timerCatch'" :model="model" :onChange="onChange" :readOnly="readOnly" />
+        <SignalEventDetail v-else-if="model.clazz === 'signalStart' || model.clazz === 'signalCatch'" :model="model" :onChange="onChange" :readOnly="readOnly" :signalDefs="signalDefs" />
+        <MessageEventDetail v-else-if="model.clazz === 'messageStart' || model.clazz === 'messageCatch'" :model="model" :onChange="onChange" :readOnly="readOnly" :messageDefs="messageDefs" />
+        <GatewayDetail v-else-if="model.clazz === 'gateway' || model.clazz === 'exclusiveGateway' || model.clazz === 'parallelGateway' || model.clazz === 'inclusiveGateway'" :model="model" :onChange="onChange" :readOnly="readOnly" />
+        <FlowDetail v-else-if="model.clazz === 'flow'" :model="model" :onChange="onChange" :readOnly="readOnly" />
+        <StartEventDetail v-else-if="model.clazz === 'start'" :model="model" :onChange="onChange" :readOnly="readOnly" />
+        <EndEventDetail v-else-if="model.clazz === 'end'" :model="model" :onChange="onChange" :readOnly="readOnly" />
+        <ProcessDetail v-else-if="model.clazz === 'process'" :model="model" :onChange="onChange" :readOnly="readOnly" :categorys="categorys" />
+    </div>
+</template>
+<script>
+  import UserTaskDetail from "./UserTaskDetail"
+  import JavaTaskDetail from "./JavaTaskDetail"
+  import ScriptTaskDetail from "./ScriptTaskDetail"
+  import ReceiveTaskDetail from "./ReceiveTaskDetail"
+  import MailTaskDetail from "./MailTaskDetail"
+  import TimerEventDetail from "./TimerEventDetail"
+  import SignalEventDetail from "./SignalEventDetail"
+  import MessageEventDetail from "./MessageEventDetail"
+  import GatewayDetail from "./GatewayDetail"
+  import FlowDetail from "./FlowDetail"
+  import StartEventDetail from "./StartEventDetail"
+  import EndEventDetail from "./EndEventDetail"
+  import ProcessDetail from "./ProcessDetail"
+  export default {
+    inject: ['i18n'],
+    components:{
+      UserTaskDetail,
+      ScriptTaskDetail,
+      JavaTaskDetail,
+      ReceiveTaskDetail,
+      MailTaskDetail,
+      TimerEventDetail,
+      SignalEventDetail,
+      MessageEventDetail,
+      GatewayDetail,
+      FlowDetail,
+      StartEventDetail,
+      EndEventDetail,
+      ProcessDetail,
+    },
+    props: {
+      height: {
+        type: Number,
+        default: 800,
+      },
+      model: {
+        type:Object,
+        default: ()=>({}),
+      },
+      users: {
+        type: Array,
+        default: ()=>([]),
+      },
+      authorities: {
+        type: Array,
+        default: ()=>([]),
+      },
+      groups: {
+        type: Array,
+        default: ()=>([]),
+      },
+      categorys: {
+        type: Array,
+        default: ()=>([]),
+      },
+      signalDefs: {
+        type: Array,
+        default: ()=>([]),
+      },
+      messageDefs: {
+        type: Array,
+        default: ()=>([]),
+      },
+      onChange: {
+        type: Function,
+        default: ()=>{}
+      },
+      readOnly:{
+        type: Boolean,
+        default: false,
+      }
+    },
+  }
+</script>
+<style lang="scss">
+    .detailPanel {
+        height: 100%;
+        background: #f0f2f5;
+        flex: 0 0 auto;
+        float: left;
+        width: 20%;
+        border-right: 1px solid #E9E9E9;
+        border-bottom: 1px solid #E9E9E9;
+        .panelTitle {
+            text-align: left;
+            height: 32px;
+            padding-left: 12px;
+            color: #000;
+            line-height: 28px;
+            background: #EBEEF2;
+            border-bottom: 1px solid #DCE3E8;
+        }
+
+        .panelBody {
+            .panelRow {
+                text-align: left;
+                display: inline-block;
+                font-size: 12px;
+                width: 100%;
+                padding: 5px 12px;
+            }
+        }
+    }
+</style>

+ 133 - 0
web/src/components/gva-wfd/components/ItemPanel.vue

@@ -0,0 +1,133 @@
+<template>
+    <div class="itemPanel" :style="{'height': height+'px'}">
+        <el-collapse v-model="activeNames" >
+            <el-collapse-item :title="i18n['start']" name="1">
+                <img data-item="{clazz:'start',size:'50*50',label:''}"
+                     :src="require('../assets/flow/start.svg')" style="width:42px;height:42px" />
+                <div>{{i18n['startEvent']}}</div>
+                <img data-item="{clazz:'timerStart',size:'50*50',label:''}"
+                     :src="require('../assets/flow/timer-start.svg')" style="width:42px;height:42px" />
+                <div>{{i18n['timerEvent']}}</div>
+                <img data-item="{clazz:'messageStart',size:'50*50',label:''}"
+                     :src="require('../assets/flow/message-start.svg')" style="width:42px;height:42px" />
+                <div>{{i18n['messageEvent']}}</div>
+                <img data-item="{clazz:'signalStart',size:'50*50',label:''}"
+                     :src="require('../assets/flow/signal-start.svg')" style="width:42px;height:42px" />
+                <div>{{i18n['signalEvent']}}</div>
+            </el-collapse-item>
+            <el-collapse-item :title="i18n['task']" name="2">
+                <img :data-item="userTaskData"
+                     :src="require('../assets/flow/user-task.svg')" style="width:80px;height:44px" />
+                <div>{{i18n['userTask']}}</div>
+                <img :data-item="scriptTaskData"
+                     :src="require('../assets/flow/script-task.svg')" style="width:80px;height:44px" />
+                <div>{{i18n['scriptTask']}}</div>
+                <img :data-item="javaTaskData"
+                     :src="require('../assets/flow/java-task.svg')" style="width:80px;height:44px" />
+                <div>{{i18n['javaTask']}}</div>
+                <img :data-item="mailTaskData"
+                     :src="require('../assets/flow/mail-task.svg')" style="width:80px;height:44px" />
+                <div>{{i18n['mailTask']}}</div>
+                <img :data-item="receiveTaskData"
+                     :src="require('../assets/flow/receive-task.svg')" style="width:80px;height:44px" />
+                <div>{{i18n['receiveTask']}}</div>
+            </el-collapse-item>
+            <el-collapse-item :title="i18n['gateway']" name="3">
+                <img data-item="{clazz:'exclusiveGateway',size:'60*60',label:''}"
+                     :src="require('../assets/flow/exclusive-gateway.svg')" style="width:48px;height:48px" />
+                <div>{{i18n['exclusiveGateway']}}</div>
+                <img data-item="{clazz:'parallelGateway',size:'60*60',label:''}"
+                     :src="require('../assets/flow/parallel-gateway.svg')" style="width:48px;height:48px" />
+                <div>{{i18n['parallelGateway']}}</div>
+                <img data-item="{clazz:'inclusiveGateway',size:'60*60',label:''}"
+                     :src="require('../assets/flow/inclusive-gateway.svg')" style="width:48px;height:48px" />
+                <div>{{i18n['inclusiveGateway']}}</div>
+            </el-collapse-item>
+            <el-collapse-item :title="i18n['catch']" name="4">
+                <img data-item="{clazz:'timerCatch',size:'60*35',label:''}"
+                     :src="require('../assets/flow/timer-catch.svg')" style="width:58px;height:38px" />
+                <div>{{i18n['timerEvent']}}</div>
+                <img data-item="{clazz:'messageCatch',size:'60*35',label:''}"
+                     :src="require('../assets/flow/message-catch.svg')" style="width:58px;height:38px" />
+                <div>{{i18n['messageEvent']}}</div>
+                <img data-item="{clazz:'signalCatch',size:'60*35',label:''}"
+                     :src="require('../assets/flow/signal-catch.svg')" style="width:58px;height:38px" />
+                <div>{{i18n['signalEvent']}}</div>
+            </el-collapse-item>
+            <el-collapse-item :title="i18n['end']" name="5">
+                <img data-item="{clazz:'end',size:'50*50',label:''}"
+                     :src="require('../assets/flow/end.svg')" style="width:42px;height:42px" />
+                <div>{{i18n['endEvent']}}</div>
+            </el-collapse-item>
+        </el-collapse>
+    </div>
+</template>
+<script>
+  export default {
+    inject: ['i18n'],
+    props: {
+      height: {
+        type: Number,
+        default: 800,
+      },
+    },
+    data() {
+      return {
+        activeNames: [],
+        userTaskData: "{clazz:'userTask',size:'80*44',label:'"+this.i18n['userTask']+"'}",
+        scriptTaskData: "{clazz:'scriptTask',size:'80*44',label:'"+this.i18n['scriptTask']+"'}",
+        javaTaskData: "{clazz:'javaTask',size:'80*44',label:'"+this.i18n['javaTask']+"'}",
+        mailTaskData: "{clazz:'mailTask',size:'80*44',label:'"+this.i18n['mailTask']+"'}",
+        receiveTaskData: "{clazz:'receiveTask',size:'80*44',label:'"+this.i18n['receiveTask']+"'}",
+      };
+    },
+  }
+</script>
+
+<style lang="scss" >
+    .itemPanel {
+        float: left;
+        width: 10%;
+        background: #f0f2f5;
+        overflow-y: auto;
+        border-left: 1px solid #E9E9E9;
+        border-bottom: 1px solid #E9E9E9;
+        img{
+            width: 92px;
+            height: 96px;
+            padding: 4px;
+            border: 1px solid rgba(0,0,0,0);
+            border-radius: 2px;
+            &:hover{
+                border: 1px solid #ccc;
+                cursor: move;
+            }
+        }
+        .el-collapse {
+            border: 0;
+            .el-collapse-item {
+                > div[role=tab] > div {
+                    padding-left: 10px;
+                    border: 1px solid #E9E9E9;
+                    border-left:0;
+                }
+                &:first-child{
+                    > div[role=tab] > div {
+                        border-top: 0;
+                    }
+                }
+                &:last-child{
+                    > div[role=tab] > div {
+                        border-bottom: 1px solid #E9E9E9;
+                    }
+                }
+                .el-collapse-item__wrap{
+                    border-top: 0;
+                    background: #f0f2f5;
+                    text-align: center;
+                }
+            }
+        }
+
+    }
+</style>

File diff suppressed because it is too large
+ 121 - 0
web/src/components/gva-wfd/components/ToolbarPanel.vue


+ 262 - 0
web/src/components/gva-wfd/components/Wfd.vue

@@ -0,0 +1,262 @@
+<template>
+  <div class="root">
+    <ToolbarPanel ref="toolbar" v-if="!isView" />
+    <div style="display: flex">
+      <ItemPanel ref="addItemPanel" v-if="!isView" :height="height"/>
+      <div ref="canvas" class="canvasPanel" :style="{'height':height+'px','width':isView?'100%':'70%','border-bottom':isView?0:null}"></div>
+      <DetailPanel ref="detailPanel"
+                   v-if="!isView"
+                   :height="height"
+                   :model="selectedModel"
+                   :readOnly="mode !== 'edit'"
+                   :users="users"
+                   :authorities="authorities"
+                   :groups="groups"
+                   :categorys="categorys"
+                   :signalDefs="processModel.signalDefs"
+                   :messageDefs="processModel.messageDefs"
+                   :onChange="(key,val)=>{onItemCfgChange(key,val)}" />
+    </div>
+  </div>
+</template>
+<script>
+  import G6 from '@antv/g6/lib';
+  import { getShapeName } from '../util/clazz'
+  import Command from '../plugins/command'
+  import Toolbar from '../plugins/toolbar'
+  import AddItemPanel from '../plugins/addItemPanel'
+  import CanvasPanel from '../plugins/canvasPanel'
+  import ToolbarPanel from '../components/ToolbarPanel'
+  import ItemPanel from '../components/ItemPanel'
+  import DetailPanel from '../components/DetailPanel'
+  import i18n from '../locales'
+  import {exportXML,exportImg} from "../util/bpmn"
+  import registerShape from '../shape'
+  import registerBehavior from '../behavior'
+  registerShape(G6);
+  registerBehavior(G6);
+  export default {
+    name: "wfd-vue",
+    components: {
+      ToolbarPanel,
+      ItemPanel,
+      DetailPanel
+    },
+    provide() {
+      return {
+        i18n: i18n[this.lang]
+      }
+    },
+    props: {
+      isView: {
+        type: Boolean,
+        default: false,
+      },
+      mode: {
+        type: String,
+        default: "edit"
+      },
+      height: {
+        type: Number,
+        default: 800,
+      },
+      lang: {
+        type: String,
+        default: "zh"
+      },
+      data: {
+        type: Object,
+        default: () => ({nodes:[],edges:[]})
+      },
+      users: {
+        type: Array,
+        default: () => ([])
+      },
+      authorities: {
+        type: Array,
+        default: ()=>([]),
+      },
+      groups: {
+        type: Array,
+        default: () => ([])
+      },
+      categorys: {
+        type: Array,
+        default: () => ([])
+      }
+    },
+    data() {
+      return {
+        resizeFunc: ()=>{},
+        selectedModel: {},
+        processModel: {
+          id: '',
+          name: '',
+          category: '',
+          clazz: 'process',
+          dataObjs: [],
+          signalDefs: [],
+          messageDefs: [],
+        },
+        graph:null,
+        cmdPlugin: null,
+      };
+    },
+    watch:{
+      data(oldData,newData){
+        if(oldData !== newData) {
+          if (this.graph) {
+            this.graph.changeData(this.initShape(newData));
+            this.graph.setMode(this.mode);
+            this.graph.emit('canvas:click');
+            if (this.cmdPlugin) {
+              this.cmdPlugin.initPlugin(this.graph);
+            }
+            if (this.isView) {
+              this.graph.fitView(5)
+            }
+          }
+        }
+      },
+    },
+    methods: {
+      initShape(data){
+        if(data && data.nodes){
+          return {
+            nodes: data.nodes.map(node => {
+              return {
+                shape: getShapeName(node.clazz),
+                ...node,
+              }
+            }),
+            edges: data.edges
+          }
+        }
+        return data;
+      },
+      initEvents(){
+        this.graph.on('afteritemselected',(items)=>{
+          if(items && items.length > 0) {
+            let item = this.graph.findById(items[0]);
+            if(!item){
+              item = this.getNodeInSubProcess(items[0])
+            }
+            this.selectedModel = {...item.getModel()};
+          } else {
+            this.selectedModel = this.processModel;
+          }
+        });
+        const page = this.$refs['canvas'];
+        const graph = this.graph;
+        const height = this.height-1;
+        this.resizeFunc = ()=>{
+          graph.changeSize(page.offsetWidth,height);
+        };
+        window.addEventListener("resize", this.resizeFunc);
+      },
+      onItemCfgChange(key,value){
+        const items = this.graph.get('selectedItems');
+        if(items && items.length > 0){
+          let item = this.graph.findById(items[0]);
+          if(!item){
+            item = this.getNodeInSubProcess(items[0])
+          }
+          if(this.graph.executeCommand) {
+            this.graph.executeCommand('update', {
+              itemId: items[0],
+              updateModel: {[key]: value}
+            });
+          }else {
+            this.graph.updateItem(item, {[key]: value});
+          }
+          this.selectedModel = {...item.getModel()};
+        } else {
+          const canvasModel = { ...this.processModel, [key]: value};
+          this.selectedModel = canvasModel;
+          this.processModel = canvasModel;
+        }
+      },
+      getNodeInSubProcess(itemId){
+        const subProcess = this.graph.find('node', (node) => {
+          if (node.get('model')) {
+            const clazz = node.get('model').clazz;
+            if (clazz === 'subProcess') {
+              const containerGroup = node.getContainer();
+              const subGroup = containerGroup.subGroup;
+              const item = subGroup.findById(itemId);
+              return subGroup.contain(item);
+            } else {
+              return false;
+            }
+          } else {
+            return false;
+          }
+        });
+        if(subProcess) {
+          const group = subProcess.getContainer();
+          return group.getItem(subProcess, itemId);
+        }
+        return null;
+      },
+    },
+    destroyed(){
+      window.removeEventListener("resize", this.resizeFunc);
+      this.graph.getNodes().forEach(node => {
+        node.getKeyShape().stopAnimate();
+      });
+    },
+    mounted() {
+      let plugins = [];
+      if(!this.isView){
+        this.cmdPlugin = new Command();
+        const toolbar = new Toolbar({container:this.$refs['toolbar'].$el});
+        const addItemPanel = new AddItemPanel({container:this.$refs['addItemPanel'].$el});
+        const canvasPanel = new CanvasPanel({container:this.$refs['canvas']});
+        plugins = [ this.cmdPlugin,toolbar,addItemPanel,canvasPanel ];
+      }
+      const width = this.$refs['canvas'].offsetWidth;
+      this.graph = new G6.Graph({
+        plugins: plugins,
+        container: this.$refs['canvas'],
+        height: this.height,
+        width: width,
+        modes: {
+          default: ['drag-canvas', 'clickSelected'],
+          view: [ ],
+          edit: [ 'drag-canvas', 'hoverNodeActived','hoverAnchorActived','dragNode','dragEdge',
+            'dragPanelItemAddNode','clickSelected','deleteItem','itemAlign','dragPoint','brush-select'],
+        },
+        defaultEdge: {
+          shape: 'flow-polyline-round',
+        },
+      });
+      this.graph.saveXML = (createFile = true) => exportXML(this.graph.save(),this.processModel,createFile);
+      this.graph.saveImg = (createFile = true) => exportImg(this.$refs['canvas'],this.processModel.name,createFile);
+      if(this.isView)
+        this.graph.setMode('view');
+      else
+        this.graph.setMode(this.mode);
+      this.graph.data(this.initShape(this.data));
+      this.graph.render();
+      if(this.isView && this.data && this.data.nodes){
+        this.graph.fitView(5)
+      }
+      this.initEvents();
+    }
+  };
+</script>
+<style lang="scss" scoped>
+    .root{
+        width: 100%;
+        height: 100%;
+        background-color: #fff;
+        display: block;
+    }
+    .canvasPanel {
+        flex: 0 0 auto;
+        float: left;
+        width:70%;
+        background-color: #fff;
+        border-bottom: 1px solid #E9E9E9;
+    }
+</style>

+ 12 - 0
web/src/components/gva-wfd/index.js

@@ -0,0 +1,12 @@
+import Wfd from './components/Wfd';
+
+const install = (Vue) => {
+  Vue.component(Wfd.name, Wfd);
+};
+
+if (typeof window !== 'undefined' && window.Vue) {
+  install(window.Vue);
+}
+
+
+export default Wfd;

+ 47 - 0
web/src/components/gva-wfd/item/anchor.js

@@ -0,0 +1,47 @@
+import editorStyle from '../util/defaultStyle';
+import Item from '@antv/g6/lib/item/item';
+import { deepMix } from '@antv/util';
+
+export default class Anchor extends Item {
+  constructor(cfg) {
+    super(deepMix(cfg,{
+      type: 'anchor',
+      isActived: false,
+      model: {
+        type: 'anchor',
+        style: {
+          ...editorStyle.anchorPointStyle,
+          cursor: editorStyle.cursor.hoverEffectiveAnchor,
+        }
+      },
+    }));
+    this.enableCapture(true);
+    this.isAnchor = true;
+    this.toFront();
+  }
+
+  showHotpot(){
+    this.hotpot = this.getContainer().addShape('marker', {
+      attrs: {
+        ...this.get('model').style,
+        ...editorStyle.anchorHotsoptStyle
+      },
+      name: 'hotpot-shape',
+      draggable: true,
+    });
+    this.hotpot.toFront();
+    this.getKeyShape().toFront();
+  }
+  setActived(){
+    this.update({style: {...editorStyle.anchorPointHoverStyle}});
+  }
+  clearActived(){
+    this.update({style: {...editorStyle.anchorPointStyle}});
+  }
+  setHotspotActived(act){
+    this.hotpot &&
+    (act ?
+      this.hotpot.attr(editorStyle.anchorHotsoptActivedStyle)
+      : this.hotpot.attr(editorStyle.anchorHotsoptStyle))
+  }
+}

+ 20 - 0
web/src/components/gva-wfd/item/controlPoint.js

@@ -0,0 +1,20 @@
+import editorStyle from '../util/defaultStyle';
+import Item from '@antv/g6/lib/item/item';
+import { deepMix } from '@antv/util';
+
+export default class ControlPoint extends Item {
+  constructor(cfg) {
+    super(deepMix(cfg,{
+      type: 'controlPoint',
+      isActived: false,
+      model: {
+        type: 'controlPoint',
+        style: {
+          ...editorStyle.anchorPointStyle,
+        },
+      },
+    }));
+    this.enableCapture(true);
+    this.toFront();
+  }
+}

+ 76 - 0
web/src/components/gva-wfd/locales/en-US.js

@@ -0,0 +1,76 @@
+export default {
+  'label': 'Label',
+  'hideIcon': 'Hide Icon',
+  'userTask': 'User Task',
+  'userTask.assignType': 'Assign Type',
+  'userTask.assignType.placeholder': 'Select a assign type',
+  'userTask.assignType.assignee': 'Assignee',
+  'userTask.assignType.assignee.title': 'Assignee',
+  'userTask.assignType.assignee.placeholder': 'Select Assignee',
+  'userTask.assignType.person': 'Candidate Users',
+  'userTask.assignType.person.title': 'Candidate Users',
+  'userTask.assignType.person.placeholder': 'Select Candidate Users',
+  'userTask.assignType.persongroup': 'Candidate Groups',
+  'userTask.assignType.persongroup.title': 'Candidate Groups',
+  'userTask.assignType.persongroup.placeholder': 'Select Candidate Groups',
+  'userTask.dueDate': 'Due Date',
+  'userTask.dueDate.placeholder': 'Select date',
+  'userTask.counterSign': 'CounterSign',
+  'scriptTask': 'Script Task',
+  'scriptTask.script': 'Script',
+  'javaTask': 'Java Task',
+  'javaTask.javaClass': 'Java Class Name',
+  'mailTask': 'Mail Task',
+  'mailTask.to': 'To',
+  'mailTask.subject': 'Subject',
+  'mailTask.content': 'Content',
+  'receiveTask': 'Receive Task',
+  'receiveTask.waitState': 'Wait State',
+  'receiveTask.stateValue': 'State Value',
+  'timerEvent': 'Timer Event',
+  'timerEvent.cycle': 'Cycle',
+  'timerEvent.cycle.placeholder': 'Select time',
+  'timerEvent.duration': 'Duration',
+  'messageEvent': 'Message Event',
+  'messageEvent.message': 'Message',
+  'signalEvent': 'Signal Event',
+  'signalEvent.signal': 'Signal',
+  'sequenceFlow': 'Sequence Flow',
+  'sequenceFlow.expression': 'Expression',
+  'sequenceFlow.seq': 'Sequence',
+  'sequenceFlow.reverse': 'Reverse',
+  'startEvent': 'Start Event',
+  'endEvent': 'End Event',
+  'start': 'Start Events',
+  'end': 'End Events',
+  'gateway': 'Gateway',
+  'exclusiveGateway': 'Exclusive Gateway',
+  'parallelGateway': 'Parallel Gateway',
+  'inclusiveGateway': 'Inclusive Gateway',
+  'task': 'Task',
+  'catch': 'Catching Event',
+  'tooltip.undo': 'Undo',
+  'tooltip.redo': 'Redo',
+  'tooltip.copy': 'Copy',
+  'tooltip.paste': 'Paste',
+  'tooltip.delete': 'Delete',
+  'tooltip.zoomIn': 'Zoom In',
+  'tooltip.zoomOut': 'Zoom Out',
+  'tooltip.zoomReset': 'Zoom Reset',
+  'tooltip.autoFit': 'Auto Fit',
+  'tooltip.toFront': 'To Front Layer',
+  'tooltip.toBack': 'To Back Layer',
+  'tooltip.edit': 'Edit',
+  'process': 'Workflow',
+  'process.category': 'Category',
+  'process.id': 'Workflow ID',
+  'process.name': 'Workflow Name',
+  'process.dataObjs': 'Data Objects',
+  'process.signalDefs': 'Signal Defs',
+  'process.messageDefs': 'Message Defs',
+  'process.dataObjs.id': 'Id',
+  'process.dataObjs.name': 'Name',
+  'process.dataObjs.type': 'Type',
+  'process.dataObjs.defaultValue': 'DefaultValue',
+  'process.signalDef.scope': 'Scope',
+};

+ 6 - 0
web/src/components/gva-wfd/locales/index.js

@@ -0,0 +1,6 @@
+import en from './en-US'
+import zh from './zh-CN'
+export default {
+  en,
+  zh,
+}

+ 76 - 0
web/src/components/gva-wfd/locales/zh-CN.js

@@ -0,0 +1,76 @@
+export default {
+  'label': '标题',
+  'hideIcon': '隐藏图标',
+  'userTask': '审批节点',
+  'userTask.assignType': '指派类型',
+  'userTask.assignType.placeholder': '选择一个类型',
+  'userTask.assignType.assignee': '受理人',
+  'userTask.assignType.assignee.title': '受理人',
+  'userTask.assignType.assignee.placeholder': '选择受理人',
+  'userTask.assignType.person': '候选人',
+  'userTask.assignType.person.title': '候选人',
+  'userTask.assignType.person.placeholder': '选择候选人',
+  'userTask.assignType.persongroup': '候选组',
+  'userTask.assignType.persongroup.title': '候选组',
+  'userTask.assignType.persongroup.placeholder': '选择候选组',
+  'userTask.dueDate': '到期时间',
+  'userTask.dueDate.placeholder': '请选择日期',
+  'userTask.counterSign': '会签',
+  'scriptTask': '脚本节点',
+  'scriptTask.script': '脚本',
+  'javaTask': '自定义类节点',
+  'javaTask.javaClass': '类名',
+  'mailTask': '邮件节点',
+  'mailTask.to': '收件人',
+  'mailTask.subject': '标题',
+  'mailTask.content': '内容',
+  'receiveTask': '接收节点',
+  'receiveTask.waitState': '等待属性',
+  'receiveTask.stateValue': '等待值',
+  'timerEvent': '定时节点',
+  'timerEvent.cycle': '循环时间',
+  'timerEvent.cycle.placeholder': '请选择时间',
+  'timerEvent.duration': '持续时间',
+  'messageEvent': '消息节点',
+  'messageEvent.message': '消息名',
+  'signalEvent': '信号节点',
+  'signalEvent.signal': '信号名',
+  'sequenceFlow': '连接线',
+  'sequenceFlow.expression': '条件表达式',
+  'sequenceFlow.seq': '序号',
+  'sequenceFlow.reverse': '反向',
+  'startEvent': '开始节点',
+  'endEvent': '结束节点',
+  'start': '开始事件',
+  'end': '结束事件',
+  'gateway': '网关',
+  'exclusiveGateway': '排他网关',
+  'parallelGateway': '并行网关',
+  'inclusiveGateway': '包容网关',
+  'task': '活动',
+  'catch': '捕获事件',
+  'tooltip.undo': '撤销',
+  'tooltip.redo': '重复',
+  'tooltip.copy': '复制',
+  'tooltip.paste': '粘贴',
+  'tooltip.delete': '删除',
+  'tooltip.zoomIn': '放大',
+  'tooltip.zoomOut': '缩小',
+  'tooltip.zoomReset': '实际大小',
+  'tooltip.autoFit': '适应屏幕',
+  'tooltip.toFront': '移到上一层',
+  'tooltip.toBack': '移到下一层',
+  'tooltip.edit': '编辑',
+  'process': '流程',
+  'process.category': '分类',
+  'process.id': '流程标识',
+  'process.name': '流程名称',
+  'process.dataObjs': '数据对象',
+  'process.signalDefs': '信号定义',
+  'process.messageDefs': '消息定义',
+  'process.dataObjs.id': 'Id',
+  'process.dataObjs.name': '名称',
+  'process.dataObjs.type': '类型',
+  'process.dataObjs.defaultValue': '默认值',
+  'process.signalDef.scope': '作用域',
+};

+ 45 - 0
web/src/components/gva-wfd/plugins/addItemPanel.js

@@ -0,0 +1,45 @@
+import { deepMix, each } from '@antv/util';
+import { createDom } from '@antv/dom-util';
+
+class AddItemPanel {
+  constructor(cfgs) {
+    this._cfgs = deepMix(this.getDefaultCfg(), cfgs);
+  }
+  getDefaultCfg() {
+    return { container: null };
+  }
+
+  get(key) {
+    return this._cfgs[key];
+  }
+  set(key, val) {
+    this._cfgs[key] = val;
+  }
+
+  initPlugin(graph) {
+    const parentNode = this.get('container');
+    const ghost = createDom('<img src="data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"'+' style="opacity:0"/>');
+    const children = parentNode.querySelectorAll('div > .el-collapse-item > .el-collapse-item__wrap > .el-collapse-item__content > img[data-item]');
+    each(children,(child,i)=>{
+      const addModel = (new Function("return " + child.getAttribute('data-item')))();
+      child.addEventListener('dragstart', e => {
+        e.dataTransfer.setDragImage(ghost, 0, 0);
+        graph.set('addNodeDragging',true);
+        graph.set('addModel',addModel);
+      });
+      child.addEventListener('dragend', e => {
+        graph.emit('canvas:mouseup',e);
+        graph.set('addNodeDragging',false);
+        graph.set('addModel',null);
+      });
+    })
+  }
+
+  destroy() {
+    this.get('canvas').destroy();
+    const container = this.get('container');
+    container.parentNode.removeChild(container);
+  }
+}
+
+export default AddItemPanel;

+ 36 - 0
web/src/components/gva-wfd/plugins/canvasPanel.js

@@ -0,0 +1,36 @@
+import { deepMix } from '@antv/util';
+
+class CanvasPanel {
+
+  constructor(cfgs) {
+    this._cfgs = deepMix(this.getDefaultCfg(), cfgs);
+  }
+  getDefaultCfg() {
+    return { container: null };
+  }
+
+  get(key) {
+    return this._cfgs[key];
+  }
+  set(key, val) {
+    this._cfgs[key] = val;
+  }
+
+  initPlugin(graph) {
+    const parentNode = this.get('container');
+    parentNode.addEventListener('dragover', e => {
+      graph.emit('canvas:mousemove',e);
+    });
+    parentNode.addEventListener('dragleave', e => {
+      graph.emit('canvas:mouseleave',e);
+    });
+  }
+
+  destroy() {
+    this.get('canvas').destroy();
+    const container = this.get('container');
+    container.parentNode.removeChild(container);
+  }
+}
+
+export default CanvasPanel;

+ 339 - 0
web/src/components/gva-wfd/plugins/command.js

@@ -0,0 +1,339 @@
+import { mix, clone, isString } from '@antv/util';
+
+class Command{
+
+  constructor() {
+    this._cfgs = this.getDefaultCfg();
+    this.list = [];
+    this.queue = [];
+  }
+
+  getDefaultCfg() {
+    return { _command: { zoomDelta: .1, queue: [], current: 0, clipboard: [] } };
+  }
+
+  get(key) {
+    return this._cfgs[key];
+  }
+  set(key, val) {
+    this._cfgs[key] = val;
+  }
+
+  initPlugin(graph) {
+    this.initCommands();
+    graph.getCommands = () => { return this.get('_command').queue };
+    graph.getCurrentCommand = () => {
+      const c = this.get('_command');
+      return c.queue[c.current - 1]
+    };
+    graph.executeCommand = (name,cfg) => { this.execute(name, graph, cfg) };
+    graph.commandEnable = (name) => { return this.enable(name, graph) };
+  }
+
+  registerCommand(name,cfg,){
+    if (this[name]){
+      mix(this[name], cfg);
+    }else {
+      const cmd = mix({},{
+        name: name,
+        shortcutCodes: [],
+        queue: true,
+        executeTimes: 1,
+        init(){},
+        enable() { return true },
+        execute(graph) {
+          this.snapShot = graph.save();
+          this.selectedItems = graph.get('selectedItems');
+          this.method && (isString(this.method) ? graph[this.method]() : this.method(graph));
+        },
+        back(graph) {
+          graph.read(this.snapShot);
+          graph.set('selectedItems',this.selectedItems);
+        }
+      },cfg);
+      this[name] = cmd;
+      this.list.push(cmd);
+    }
+  }
+
+  execute(name, graph, cfg) {
+    const cmd = mix({},this[name], cfg);
+    const manager = this.get('_command');
+    if(cmd.enable(graph)){
+      cmd.init();
+      if(cmd.queue){
+        manager.queue.splice(manager.current, manager.queue.length - manager.current, cmd);
+        manager.current++;
+      }
+    }
+    graph.emit('beforecommandexecute', { command: cmd });
+    cmd.execute(graph);
+    graph.emit('aftercommandexecute', { command: cmd });
+    return cmd;
+  }
+
+  enable(name, graph) {
+    return this[name].enable(graph);
+  }
+
+  destroyPlugin() {
+    this._events = null;
+    this._cfgs = null;
+    this.list = [];
+    this.queue = [];
+    this.destroyed = true;
+  }
+  _deleteSubProcessNode(graph, itemId) {
+    const subProcess = graph.find('node', (node) => {
+      if (node.get('model')) {
+        const clazz = node.get('model').clazz;
+        if (clazz === 'subProcess') {
+          const containerGroup = node.getContainer();
+          const subGroup = containerGroup.subGroup;
+          const item = subGroup.findById(itemId);
+          return subGroup.contain(item);
+        } else {
+          return false;
+        }
+      } else {
+        return false;
+      }
+    });
+    if(subProcess){
+      const group = subProcess.getContainer();
+      const resultModel = group.removeItem(subProcess, itemId);
+      graph.updateItem(subProcess, resultModel);
+    }
+  }
+  initCommands(){
+    const cmdPlugin = this;
+    cmdPlugin.registerCommand('add',{
+      enable: function() {
+        return this.type && this.addModel;
+      },
+      execute: function(graph) {
+        const item = graph.add(this.type, this.addModel);
+        if(this.executeTimes === 1)
+          this.addId = item.get('id');
+      },
+      back: function(graph) {
+        graph.remove(this.addId);
+      },
+    });
+    cmdPlugin.registerCommand('update', {
+      enable: function() {
+        return this.itemId && this.updateModel;
+      },
+      execute: function(graph) {
+        const item = graph.findById(this.itemId);
+        if(item) {
+          if(this.executeTimes === 1)
+            this.originModel = mix({}, item.getModel());
+          graph.update(item, this.updateModel);
+        }
+      },
+      back: function(graph) {
+        const item = graph.findById(this.itemId);
+        graph.update(item, this.originModel);
+      },
+    });
+    cmdPlugin.registerCommand('delete', {
+      enable: function(graph) {
+        const mode = graph.getCurrentMode();
+        const selectedItems = graph.get('selectedItems');
+        return mode === 'edit' && selectedItems && selectedItems.length > 0;
+      },
+      method: function(graph) {
+        const selectedItems = graph.get('selectedItems');
+        graph.emit('beforedelete', { items: selectedItems });
+        if(selectedItems && selectedItems.length > 0) {
+          selectedItems.forEach(i => {
+            const node = graph.findById(i);
+            if (node) {
+              graph.remove(i);
+            } else {
+              cmdPlugin._deleteSubProcessNode(graph, i);
+            }
+          });
+        }
+        graph.emit('afterdelete', { items: selectedItems });
+      },
+      shortcutCodes: ['Delete', 'Backspace'],
+    });
+    cmdPlugin.registerCommand('redo', {
+      queue: false,
+      enable: function(graph) {
+        const mode = graph.getCurrentMode();
+        const manager = cmdPlugin.get('_command');
+        return mode === 'edit' && manager.current < manager.queue.length;
+      },
+      execute: function(graph) {
+        const manager = cmdPlugin.get('_command');
+        const cmd = manager.queue[manager.current];
+        cmd && cmd.execute(graph);
+        manager.current++;
+      },
+      shortcutCodes: [['metaKey', 'shiftKey', 'z'], ['ctrlKey', 'shiftKey', 'z']],
+    });
+    cmdPlugin.registerCommand('undo', {
+      queue: false,
+      enable: function(graph) {
+        const mode = graph.getCurrentMode();
+        return mode === 'edit' && cmdPlugin.get('_command').current > 0;
+      },
+      execute: function(graph) {
+        const manager = cmdPlugin.get('_command');
+        const cmd = manager.queue[manager.current - 1];
+        if(cmd) {
+          cmd.executeTimes++;
+          cmd.back(graph);
+        }
+        manager.current--;
+      },
+      shortcutCodes: [['metaKey', 'z'], ['ctrlKey', 'z']],
+    });
+    cmdPlugin.registerCommand('copy', {
+      queue: false,
+      enable: function(graph){
+        const mode = graph.getCurrentMode();
+        const items = graph.get('selectedItems');
+        return mode === 'edit' && items && items.length > 0;
+      },
+      method: function(graph) {
+        const manager = cmdPlugin.get('_command');
+        manager.clipboard = [];
+        const items = graph.get('selectedItems');
+        if(items && items.length > 0){
+          const item = graph.findById(items[0]);
+          if(item){
+            manager.clipboard.push({ type: item.get('type'), model: item.getModel()});
+          }
+        }
+      },
+    });
+    cmdPlugin.registerCommand('paste', {
+      enable: function(graph) {
+        const mode = graph.getCurrentMode();
+        return mode === 'edit' && cmdPlugin.get('_command').clipboard.length > 0;
+      },
+      method: function(graph) {
+        const manager = cmdPlugin.get('_command');
+        this.pasteData = clone(manager.clipboard[0]);
+        const addModel = this.pasteData.model;
+        addModel.x && (addModel.x += 10);
+        addModel.y && (addModel.y += 10);
+        const { clazz = 'userTask' } = addModel;
+        const timestamp = new Date().getTime();
+        const id = clazz + timestamp;
+        addModel.id = id;
+        const item = graph.add(this.pasteData.type, addModel);
+        item.toFront();
+      },
+    });
+    cmdPlugin.registerCommand('zoomIn', {
+      queue: false,
+      enable: function(graph) {
+        const zoom = graph.getZoom();
+        const maxZoom = graph.get('maxZoom');
+        const minZoom = graph.get('minZoom');
+        return zoom <= maxZoom && zoom >= minZoom;
+      },
+      execute: function(graph) {
+        const manager = cmdPlugin.get('_command');
+        const maxZoom = graph.get('maxZoom');
+        const zoom = graph.getZoom();
+        this.originZoom = zoom;
+        let currentZoom = zoom + manager.zoomDelta;
+        if(currentZoom > maxZoom)
+          currentZoom = maxZoom;
+        graph.zoomTo(currentZoom);
+      },
+      back: function(graph) {
+        graph.zoomTo(this.originZoom);
+      },
+      shortcutCodes: [['metaKey', '='], ['ctrlKey', '=']],
+    });
+    cmdPlugin.registerCommand('zoomOut', {
+      queue: false,
+      enable: function(graph) {
+        const zoom = graph.getZoom();
+        const maxZoom = graph.get('maxZoom');
+        const minZoom = graph.get('minZoom');
+        return zoom <= maxZoom && zoom >= minZoom;
+      },
+      execute: function(graph) {
+        const manager = cmdPlugin.get('_command');
+        const minZoom = graph.get('minZoom');
+        const zoom = graph.getZoom();
+        this.originZoom = zoom;
+        let currentZoom = zoom - manager.zoomDelta;
+        if(currentZoom < minZoom)
+          currentZoom = minZoom;
+        graph.zoomTo(currentZoom);
+      },
+      back: function(graph) {
+        graph.zoomTo(this.originZoom);
+      },
+      shortcutCodes: [['metaKey', '-'], ['ctrlKey', '-']],
+    });
+    cmdPlugin.registerCommand('resetZoom', {
+      queue: false,
+      execute: function(graph) {
+        const zoom = graph.getZoom();
+        this.originZoom = zoom;
+        graph.zoomTo(1);
+      },
+      back: function(graph) {
+        graph.zoomTo(this.originZoom);
+      },
+    });
+    cmdPlugin.registerCommand('autoFit', {
+      queue: false,
+      execute: function(graph) {
+        const zoom = graph.getZoom();
+        this.originZoom = zoom;
+        graph.fitView(5);
+      },
+      back: function(graph) {
+        graph.zoomTo(this.originZoom);
+      },
+    });
+    cmdPlugin.registerCommand('toFront', {
+      queue: false,
+      enable: function(graph) {
+        const items = graph.get('selectedItems');
+        return items && items.length > 0;
+      },
+      execute: function(graph) {
+        const items = graph.get('selectedItems');
+        if(items && items.length > 0) {
+          const item = graph.findById(items[0]);
+          item.toFront();
+          graph.paint();
+        }
+      },
+      back: function(graph) {
+
+      },
+    });
+    cmdPlugin.registerCommand('toBack', {
+      queue: false,
+      enable: function(graph) {
+        const items = graph.get('selectedItems');
+        return items && items.length > 0;
+      },
+      execute: function(graph) {
+        const items = graph.get('selectedItems');
+        if(items && items.length > 0) {
+          const item = graph.findById(items[0]);
+          item.toBack();
+          graph.paint();
+        }
+      },
+      back: function(graph) {
+
+      },
+    });
+  }
+}
+export default Command;

+ 71 - 0
web/src/components/gva-wfd/plugins/detailPanel.js

@@ -0,0 +1,71 @@
+import { deepMix, each, wrapBehavior } from '@antv/util';
+import { modifyCSS } from '@antv/dom-util';
+
+class DetailPanel {
+
+  constructor(cfgs) {
+    this._cfgs = deepMix(this.getDefaultCfg(), cfgs);
+  }
+  getDefaultCfg() {
+    return { container: null };
+  }
+
+  get(key) {
+    return this._cfgs[key];
+  }
+  set(key, val) {
+    this._cfgs[key] = val;
+  }
+
+  initPlugin(graph) {
+    const self = this;
+    this.set('graph', graph);
+    const events = self.getEvents();
+    const bindEvents = {};
+    each(events, (v, k) => {
+      const event = wrapBehavior(self, v);
+      bindEvents[k] = event;
+      graph.on(k, event);
+    });
+    this._events = bindEvents;
+    this.updatePanel();
+  }
+
+  updatePanel(){
+    const graph = this.get('graph');
+    const parentNode = this.get('container');
+    const selectedItems = graph.get('selectedItems');
+    let selectedItem = null;
+    let clazz = null;
+    if(selectedItems && selectedItems.length > 0){
+      selectedItem = graph.findById(selectedItems[0]);
+      clazz = selectedItem.getModel().clazz;
+    }
+    each(parentNode.children,(child,i)=>{
+      if(child.hasAttribute('data-clazz')){
+        const clazzName = child.getAttribute('data-clazz');
+        if(clazz && clazz === clazzName){
+          modifyCSS(child,{
+            display: 'inline'
+          });
+        }else{
+          modifyCSS(child,{
+            display: 'none'
+          });
+        }
+      }
+    })
+  }
+
+  getEvents() {
+    return { afteritemselected: 'updatePanel' };
+  }
+
+  destroy() {
+    this.get('canvas').destroy();
+    const container = this.get('container');
+    container.parentNode.removeChild(container);
+  }
+}
+
+export default DetailPanel;

+ 85 - 0
web/src/components/gva-wfd/plugins/toolbar.js

@@ -0,0 +1,85 @@
+import { deepMix, each, wrapBehavior } from '@antv/util';
+import { modifyCSS } from '@antv/dom-util';
+
+class Toolbar {
+
+  constructor(cfgs) {
+    this._cfgs = deepMix(this.getDefaultCfg(), cfgs);
+  }
+  getDefaultCfg() {
+    return { container: null };
+  }
+
+  get(key) {
+    return this._cfgs[key];
+  }
+  set(key, val) {
+    this._cfgs[key] = val;
+  }
+
+  initPlugin(graph) {
+    const self = this;
+    this.set('graph', graph);
+    const events = self.getEvents();
+    const bindEvents = {};
+    each(events, (v, k) => {
+      const event = wrapBehavior(self, v);
+      bindEvents[k] = event;
+      graph.on(k, event);
+    });
+    this._events = bindEvents;
+
+    this.initEvents();
+    this.updateToolbar();
+  }
+
+  getEvents() {
+    return { afteritemselected: 'updateToolbar',aftercommandexecute: 'updateToolbar' };
+  }
+
+  initEvents() {
+    const graph = this.get('graph');
+    const parentNode = this.get('container');
+    const children = parentNode.querySelectorAll('div > span[data-command]');
+    each(children,(child,i)=>{
+      const cmdName = child.getAttribute('data-command');
+      child.addEventListener('click', e => {
+        graph.commandEnable(cmdName) && graph.executeCommand(cmdName);
+      });
+    })
+  }
+
+  updateToolbar(){
+    const graph = this.get('graph');
+    const parentNode = this.get('container');
+    const children = parentNode.querySelectorAll('div > span[data-command]');
+    each(children,(child,i)=>{
+      const cmdName = child.getAttribute('data-command');
+      if(graph.commandEnable(cmdName)){
+        modifyCSS(child,{
+          cursor:'pointer',
+        });
+        modifyCSS(child.children[0],{
+          color:'#666',
+        });
+        child.children[0].setAttribute('color','#666');
+      }else{
+        modifyCSS(child,{
+          cursor:'default',
+        });
+        modifyCSS(child.children[0],{
+          color:'#bfbfbf',
+        });
+        child.children[0].setAttribute('color','#bfbfbf');
+      }
+    })
+  }
+
+  destroyPlugin() {
+    this.get('canvas').destroy();
+    const container = this.get('container');
+    container.parentNode.removeChild(container);
+  }
+}
+
+export default Toolbar;

+ 34 - 0
web/src/components/gva-wfd/shape/anchor.js

@@ -0,0 +1,34 @@
+import editorStyle from "../util/defaultStyle";
+import { shapeBase } from '@antv/g6/lib/shape/shapeBase';
+import Shape from '@antv/g6/lib/shape/shape';
+
+export default function(G6){
+  Shape.registerFactory('anchor', {
+    defaultShapeType: 'marker',
+    getShape:(type)=> {
+      const shapeObj = Object.assign({}, shapeBase,{
+        type: 'marker',
+        itemType: type,
+        drawShape(cfg, group) {
+          const style = this.getShapeStyle(cfg);
+          const shape = group.addShape('marker', {
+            attrs: style,
+            name: 'anchor-shape',
+            draggable: true,
+          });
+          return shape;
+        },
+        setState(name, value, item) {
+          if(name === 'active-anchor') {
+            if(value) {
+              this.update({style: {...editorStyle.anchorPointHoverStyle}}, item);
+            }else{
+              this.update({style: {...editorStyle.anchorPointStyle}}, item);
+            }
+          }
+        }
+      });
+      return shapeObj;
+    },
+  });
+}

+ 37 - 0
web/src/components/gva-wfd/shape/controlPoint.js

@@ -0,0 +1,37 @@
+import editorStyle from "../util/defaultStyle";
+import { shapeBase } from '@antv/g6/lib/shape/shapeBase';
+import Shape from '@antv/g6/lib/shape/shape';
+
+export default function(G6) {
+  Shape.registerFactory('controlPoint', {
+    defaultShapeType: 'marker',
+    getShape: (type) => {
+      const shapeObj = Object.assign({}, shapeBase, {
+        type: 'marker',
+        itemType: type,
+        drawShape(cfg, group) {
+          const style = this.getShapeStyle(cfg);
+          const shape = group.addShape('marker', {
+            attrs: {
+              ...style,
+              symbol: 'square'
+            },
+            name: 'controlPoint-shape',
+            draggable: true,
+          });
+          return shape;
+        },
+        setState(name, value, item) {
+          if (name === 'active') {
+            if (value) {
+              this.update({ style: { ...editorStyle.pointPointHoverStyle } }, item);
+            } else {
+              this.update({ style: { ...editorStyle.pointPointStyle } }, item);
+            }
+          }
+        }
+      });
+      return shapeObj;
+    }
+  });
+}

+ 328 - 0
web/src/components/gva-wfd/shape/edge.js

@@ -0,0 +1,328 @@
+import editorStyle from "../util/defaultStyle";
+
+const uniqBy = (arr,key)=>{
+  const result = [];
+  arr.forEach( i => {
+    if(!result.find(r => r[key] === i[key]))
+      result.push(i)
+  });
+  return result;
+};
+
+export default function(G6){
+  G6.registerEdge('flow-polyline-round', {
+    options: {
+      style: {
+        ...editorStyle.edgeStyle
+      },
+      stateStyles: {
+        selected: {
+          lineWidth: editorStyle.edgeSelectedStyle.lineWidth,
+        },
+        hover: {
+          stroke: editorStyle.edgeActivedStyle.stroke,
+        }
+      }
+    },
+    setState(name, value, item) {
+      const group = item.getContainer();
+      const path = group.getChildByIndex(0);
+      if(name === 'selected'){
+        if(value) {
+          path.attr('lineWidth', this.options.stateStyles.selected.lineWidth);
+          path.attr('stroke', this.options.style.stroke);
+        }else {
+          path.attr('lineWidth', this.options.style.lineWidth);
+        }
+      }else if(name === 'hover'){
+        if(value)
+          path.attr('stroke', this.options.stateStyles.hover.stroke);
+        else
+          path.attr('stroke',this.options.style.stroke);
+      }
+    },
+    drawShape(cfg, group) {
+      this.group = group;
+      const shapeStyle = this.getShapeStyle(cfg);
+      const shape = group.addShape('path', {
+        className: 'edge-shape',
+        attrs: shapeStyle
+      });
+      return shape;
+    },
+    drawLabel(cfg, group) {
+      const labelCfg = cfg.labelCfg || {};
+      const labelStyle = this.getLabelStyle(cfg, labelCfg, group);
+      const label = group.addShape('text', {
+        attrs: labelStyle
+      });
+      const labelBBox = label.getBBox();
+      group.addShape('rect',{
+        className: 'edge-labelRect',
+        attrs: {
+          x:labelBBox.x-editorStyle.edgeLabelRectPadding/2,
+          y:labelBBox.y-editorStyle.edgeLabelRectPadding/2,
+          width: labelBBox.width+editorStyle.edgeLabelRectPadding,
+          height: labelBBox.height+editorStyle.edgeLabelRectPadding,
+          fill:'#fff',
+          stroke:'#fff',
+        }
+      });
+      group.toBack();
+      label.toFront();
+      return label;
+    },
+    afterUpdate(cfg, item){
+      const label = item.getContainer().findByClassName('edge-label');
+      const labelRect = item.getContainer().findByClassName('edge-labelRect');
+      if(label) {
+        const labelBBox = label.getBBox();
+        labelRect.attr({
+          x: labelBBox.x - editorStyle.edgeLabelRectPadding / 2,
+          y: labelBBox.y - editorStyle.edgeLabelRectPadding / 2,
+          width: labelBBox.width + editorStyle.edgeLabelRectPadding,
+          height: labelBBox.height + editorStyle.edgeLabelRectPadding,
+        });
+      }
+    },
+    getShapeStyle(cfg) {
+      cfg = this.getPathPoints(cfg);
+      const startPoint = cfg.startPoint;
+      const endPoint = cfg.endPoint;
+      const controlPoints = this.getControlPoints(cfg);
+      let points = [ startPoint ];
+      if (controlPoints) {
+        points = points.concat(controlPoints);
+      }
+      points.push(endPoint);
+      const path = this.getPath(points);
+      let style = this.options.style;
+      if(cfg.reverse)
+        style = {...style,lineDash:[1, 3]};
+      else
+        style = {...style,lineDash:null};
+      return {
+        path,
+        ...style,
+        endArrow: {
+          path: 'M 0,0 L 6,-2 Q 5 0,6 2 Z',
+          lineDash: [ 0, 0 ],
+          fill: editorStyle.edgeStyle.stroke,
+        }
+      }
+    },
+    getPath(points){
+      const path = [];
+      for(let i = 0; i < points.length; i++){
+        const point = points[i];
+        if (i === 0) {
+          path.push([ 'M', point.x, point.y ]);
+        } else if(i === points.length - 1) {
+          path.push([ 'L', point.x, point.y ]);
+        } else {
+          const prevPoint = points[i - 1];
+          let nextPoint = points[i + 1];
+          let cornerLen = 5;
+          if(Math.abs(point.y - prevPoint.y) > cornerLen || Math.abs(point.x - prevPoint.x) > cornerLen){
+            if(prevPoint.x === point.x) {
+              path.push(['L', point.x, point.y > prevPoint.y ? point.y - cornerLen : point.y + cornerLen]);
+            } else if(prevPoint.y === point.y) {
+              path.push(['L', point.x > prevPoint.x ? point.x - cornerLen : point.x + cornerLen, point.y]);
+            }
+          }
+          const yLen = Math.abs(point.y - nextPoint.y);
+          const xLen = Math.abs(point.x - nextPoint.x);
+          if(yLen > 0 && yLen < cornerLen){
+            cornerLen = yLen;
+          }else if(xLen > 0 && xLen < cornerLen){
+            cornerLen = xLen;
+          }
+          if(prevPoint.x !== nextPoint.x && nextPoint.x === point.x) {
+            path.push([ 'Q', point.x, point.y, point.x, point.y > nextPoint.y ? point.y - cornerLen : point.y + cornerLen]);
+          } else if(prevPoint.y !== nextPoint.y && nextPoint.y === point.y) {
+            path.push([ 'Q', point.x, point.y, point.x > nextPoint.x ? point.x - cornerLen : point.x + cornerLen, point.y]);
+          }
+        }
+      }
+      return path;
+    },
+    getControlPoints(cfg) {
+      if(!cfg.sourceNode){
+        return cfg.controlPoints;
+      }
+      return this.polylineFinding(cfg.sourceNode,cfg.targetNode,cfg.startPoint,cfg.endPoint,15);
+    },
+    getExpandedBBox(bbox, offset) {
+      return 0 === bbox.width && 0 === bbox.height ? bbox : {
+        centerX: bbox.centerX,
+        centerY: bbox.centerY,
+        minX: bbox.minX - offset,
+        minY: bbox.minY - offset,
+        maxX: bbox.maxX + offset,
+        maxY: bbox.maxY + offset,
+        height: bbox.height + 2 * offset,
+        width: bbox.width + 2 * offset,
+      };
+    },
+    getExpandedPort(bbox, point) {
+      return Math.abs(point.x - bbox.centerX) / bbox.width > Math.abs(point.y - bbox.centerY) / bbox.height
+        ? { x: point.x > bbox.centerX ? bbox.maxX : bbox.minX, y: point.y }
+        : { x: point.x, y: point.y > bbox.centerY ? bbox.maxY : bbox.minY };
+    },
+    combineBBoxes(sBBox, tBBox) {
+      const minX = Math.min(sBBox.minX, tBBox.minX), minY = Math.min(sBBox.minY, tBBox.minY),
+        maxX = Math.max(sBBox.maxX, tBBox.maxX), maxY = Math.max(sBBox.maxY, tBBox.maxY);
+      return {
+        centerX: (minX + maxX) / 2,
+        centerY: (minY + maxY) / 2,
+        minX: minX,
+        minY: minY,
+        maxX: maxX,
+        maxY: maxY,
+        height: maxY - minY,
+        width: maxX - minX,
+      };
+    },
+    getBBoxFromVertexes(sPoint, tPoint){
+      const minX = Math.min(sPoint.x,tPoint.x), maxX = Math.max(sPoint.x,tPoint.x),
+        minY = Math.min(sPoint.y,tPoint.y), maxY = Math.max(sPoint.y,tPoint.y);
+      return {
+        centerX: (minX + maxX) / 2,
+        centerY: (minY + maxY) / 2,
+        maxX: maxX,
+        maxY: maxY,
+        minX: minX,
+        minY: minY,
+        height: maxY - minY,
+        width: maxX - minX,
+      };
+    },
+    vertexOfBBox(bbox){
+      return [{ x: bbox.minX, y: bbox.minY }, { x: bbox.maxX, y: bbox.minY }, { x: bbox.maxX, y: bbox.maxY }, { x: bbox.minX, y: bbox.maxY }];
+    },
+    crossPointsByLineAndBBox(bbox,centerPoint){
+      let crossPoints = [];
+      if(!(centerPoint.x < bbox.minX || centerPoint.x > bbox.maxX))
+        crossPoints = crossPoints.concat([{ x: centerPoint.x, y: bbox.minY }, { x: centerPoint.x, y: bbox.maxY }]);
+      if(!(centerPoint.y < bbox.minY || centerPoint.y > bbox.maxY))
+        crossPoints = crossPoints.concat([{ x: bbox.minX, y: centerPoint.y }, { x: bbox.maxX, y: centerPoint.y }]);
+      return crossPoints;
+    },
+    getConnectablePoints(sBBox, tBBox, sPoint, tPoint){
+      const lineBBox = this.getBBoxFromVertexes(sPoint, tPoint);
+      const outerBBox = this.combineBBoxes(sBBox, tBBox);
+      const sLineBBox = this.combineBBoxes(sBBox, lineBBox);
+      const tLineBBox = this.combineBBoxes(tBBox, lineBBox);
+      let points = [];
+      points = points.concat(this.vertexOfBBox(sLineBBox),this.vertexOfBBox(tLineBBox),this.vertexOfBBox(outerBBox));
+      const centerPoint = { x: outerBBox.centerX, y: outerBBox.centerY };
+      [ outerBBox, sLineBBox, tLineBBox, lineBBox ].forEach(bbox => {
+        points = points.concat(this.crossPointsByLineAndBBox(bbox, centerPoint))
+      });
+      points.push({ x: sPoint.x, y: tPoint.y });
+      points.push({ x: tPoint.x, y: sPoint.y });
+      return points
+    },
+    filterConnectablePoints(points,bbox){
+      return points.filter(point => point.x <= bbox.minX || point.x >= bbox.maxX || point.y <= bbox.minY || point.y >= bbox.maxY)
+    },
+    AStar(points, sPoint, tPoint, sBBox, tBBox){
+      const openList = [sPoint];
+      const closeList = [];
+      points = uniqBy(this.fillId(points),'id');
+      points.push(tPoint);
+      let endPoint;
+      while(openList.length > 0){
+        let minCostPoint;
+        openList.forEach((p,i) => {
+          if(!p.parent)
+            p.f = 0;
+          if(!minCostPoint)
+            minCostPoint = p;
+          if(p.f < minCostPoint.f)
+            minCostPoint = p;
+        });
+        if(minCostPoint.x === tPoint.x && minCostPoint.y === tPoint.y) {
+          endPoint = minCostPoint;
+          break;
+        }
+        openList.splice(openList.findIndex(o => o.x === minCostPoint.x && o.y === minCostPoint.y),1);
+        closeList.push(minCostPoint);
+        const neighbor = points.filter(p => (p.x === minCostPoint.x || p.y === minCostPoint.y)
+          && !(p.x === minCostPoint.x && p.y === minCostPoint.y)
+          && !this.crossBBox([sBBox,tBBox],minCostPoint,p));
+        neighbor.forEach(p => {
+          const inOpen = openList.find(o => o.x === p.x && o.y === p.y);
+          const currentG = this.getCost(p,minCostPoint);
+          if(closeList.find(o => o.x === p.x && o.y === p.y)){
+
+          }else if(inOpen) {
+            if(p.g > currentG) {
+              p.parent = minCostPoint;
+              p.g = currentG;
+              p.f = p.g + p.h;
+            }
+          }else {
+            p.parent = minCostPoint;
+            p.g = currentG;
+            let h = this.getCost(p,tPoint);
+            if(this.crossBBox([tBBox],p,tPoint)){
+              h += (tBBox.width/2+tBBox.height/2); //如果穿过bbox则增加该点的预估代价为bbox周长的一半
+            }
+            p.h = h;
+            p.f = p.g + p.h;
+            openList.push(p)
+          }
+        });
+      }
+      if(endPoint){
+        const result = [];
+        result.push({x:endPoint.x,y:endPoint.y});
+        while(endPoint.parent){
+          endPoint = endPoint.parent;
+          result.push({x:endPoint.x,y:endPoint.y});
+        }
+        return result.reverse();
+      }
+      return [];
+    },
+    crossBBox(bboxes,p1,p2){
+      for(let i=0; i < bboxes.length; i++) {
+        const bbox = bboxes[i];
+        if (p1.x === p2.x && bbox.minX < p1.x && bbox.maxX > p1.x) {
+          if (p1.y < bbox.maxY && p2.y >= bbox.maxY || p2.y < bbox.maxY && p1.y >= bbox.maxY)
+            return true
+        } else if (p1.y === p2.y && bbox.minY < p1.y && bbox.maxY > p1.y) {
+          if (p1.x < bbox.maxX && p2.x >= bbox.maxX || p2.x < bbox.maxX && p1.x >= bbox.maxX)
+            return true
+        }
+      }
+      return false;
+    },
+    getCost(p1,p2){
+      return Math.abs(p1.x - p2.x) + Math.abs(p1.y - p2.y);
+    },
+    getPointBBox(t) {
+      return { centerX: t.x, centerY: t.y, minX: t.x, minY: t.y, maxX: t.x, maxY: t.y, height: 0, width: 0 };
+    },
+    fillId(points) {
+      points.forEach(p => {
+        p.id = p.x + '-' + p.y;
+      });
+      return points;
+    },
+    polylineFinding(sNode, tNode, sPort, tPort, offset) {
+      const sourceBBox = sNode && sNode.getBBox() ? sNode.getBBox() : this.getPointBBox(sPort);
+      const targetBBox = tNode && tNode.getBBox() ? tNode.getBBox() : this.getPointBBox(tPort);
+      const sBBox = this.getExpandedBBox(sourceBBox,offset);
+      const tBBox = this.getExpandedBBox(targetBBox,offset);
+      const sPoint = this.getExpandedPort(sBBox,sPort);
+      const tPoint = this.getExpandedPort(tBBox,tPort);
+      let points = this.getConnectablePoints(sBBox, tBBox, sPoint, tPoint);
+      points = this.filterConnectablePoints(points, sBBox);
+      points = this.filterConnectablePoints(points, tBBox);
+      const polylinePoints = this.AStar(points, sPoint, tPoint, sBBox, tBBox);
+      return polylinePoints;
+    },
+  },'polyline');
+}

+ 375 - 0
web/src/components/gva-wfd/shape/flowNode.js

@@ -0,0 +1,375 @@
+import editorStyle from "../util/defaultStyle";
+
+const taskDefaultOptions = {
+  icon: null,
+  iconStyle: {
+    width: 12,
+    height: 12,
+    left: 2,
+    top: 2,
+  },
+  style:{
+    ...editorStyle.nodeStyle,
+    fill: '#E7F7FE',
+    stroke:'#1890FF',
+    cursor: 'default',
+  },
+  stateStyles: {
+    selected: {
+      fill: '#95D6FB',
+    },
+    hover: {
+      cursor: editorStyle.cursor.hoverNode,
+    }
+  }
+};
+
+const gatewayDefaultOptions = {
+  icon: null,
+  iconStyle: {
+    width: 20,
+    height: 20,
+    left: -10,
+    top: -10,
+  },
+  style:{
+    ...editorStyle.nodeStyle,
+    fill: '#E8FEFA',
+    stroke:'#13C2C2',
+    cursor: 'default',
+  },
+  stateStyles: {
+    selected: {
+      fill: '#8CE8DE',
+    },
+    hover: {
+      cursor: editorStyle.cursor.hoverNode,
+    }
+  }
+};
+
+const startDefaultOptions = {
+  icon: null,
+  iconStyle: {
+    width: 23,
+    height: 23,
+    left: 16,
+    top: 16,
+  },
+  style:{
+    ...editorStyle.nodeStyle,
+    fill: '#FEF7E8',
+    stroke:'#FA8C16',
+    cursor: 'default',
+  },
+  stateStyles: {
+    selected: {
+      fill: '#FCD49A',
+    },
+    hover: {
+      cursor: editorStyle.cursor.hoverNode,
+    }
+  }
+};
+
+const endDefaultOptions = {
+  icon: null,
+  iconStyle: {
+    width: 23,
+    height: 23,
+    left: 16,
+    top: 16,
+  },
+  style:{
+    ...editorStyle.nodeStyle,
+    fill: '#EFF7E8',
+    stroke:'#F5222D',
+    cursor: 'default',
+  },
+  stateStyles: {
+    selected: {
+      fill: '#CFD49A',
+    },
+    hover: {
+      cursor: editorStyle.cursor.hoverNode,
+    }
+  }
+};
+
+const catchDefaultOptions = {
+  icon: null,
+  iconStyle: {
+    width: 20,
+    height: 20,
+    left: -10,
+    top: -8,
+  },
+  style:{
+    ...editorStyle.nodeStyle,
+    fill: '#FEF7E8',
+    stroke:'#FA8C16',
+    cursor: 'default',
+  },
+  stateStyles: {
+    selected: {
+      fill: '#FCD49A',
+    },
+    hover: {
+      cursor: editorStyle.cursor.hoverNode,
+    }
+  }
+};
+
+export default function(G6) {
+  G6.registerNode('task-node', {
+    shapeType: 'rect',
+    options:{
+      ...taskDefaultOptions
+    },
+    getShapeStyle(cfg) {
+      cfg.size = [100, 55];
+      const width = cfg.size[0];
+      const height = cfg.size[1];
+      const style = {
+        x: 0 - width / 2,
+        y: 0 - height / 2,
+        width,
+        height,
+        ...this.options.style,
+      };
+      return style;
+    }
+  }, 'base-node');
+  G6.registerNode('gateway-node', {
+    shapeType: 'path',
+    labelPosition: 'bottom',
+    options:{
+      ...gatewayDefaultOptions
+    },
+    getShapeStyle(cfg) {
+      cfg.size = [55, 55];
+      const width = cfg.size[0];
+      const height = cfg.size[1];
+      const gap = 4;
+      const style = {
+        path: [
+          ['M', 0 - gap, 0 - height / 2 + gap],
+          ['Q', 0, 0 - height / 2, gap, 0 - height / 2 + gap],
+          ['L', width / 2 - gap, 0 - gap],
+          ['Q', width / 2, 0, width / 2 - gap, gap],
+          ['L', gap, height / 2 - gap],
+          ['Q', 0, height / 2, 0 - gap, height / 2 - gap],
+          ['L', -width / 2 + gap, gap],
+          ['Q', -width / 2, 0, -width / 2 + gap, 0 - gap],
+          ['Z']
+        ],
+        ...this.options.style,
+      };
+      return style;
+    },
+  }, 'base-node');
+  G6.registerNode('exclusive-gateway-node', {
+    options:  G6.Util.deepMix({},gatewayDefaultOptions,{icon: require('../assets/icons/flow/exclusive-gateway.svg')}),
+  }, 'gateway-node');
+  G6.registerNode('parallel-gateway-node', {
+    options:  G6.Util.deepMix({},gatewayDefaultOptions,{icon: require('../assets/icons/flow/parallel-gateway.svg')}),
+  }, 'gateway-node');
+  G6.registerNode('inclusive-gateway-node', {
+    options:  G6.Util.deepMix({},gatewayDefaultOptions,{icon: require('../assets/icons/flow/inclusive-gateway.svg')}),
+  }, 'gateway-node');
+  G6.registerNode('start-node', {
+    shapeType: 'circle',
+    labelPosition: 'bottom',
+    options:  G6.Util.deepMix({},startDefaultOptions,{icon: require('../assets/icons/flow/start.svg')}),
+    getShapeStyle(cfg) {
+      cfg.size = [55, 55];
+      const width = cfg.size[0];
+      const style = {
+        x: 0,
+        y: 0,
+        r: width / 2,
+        ...this.options.style,
+      };
+      if(cfg.hasOwnProperty('color')){
+        style.fill = cfg.color
+      }
+      return style;
+    },
+    afterDraw(cfg, group) {
+      if(cfg.active) {
+        const shape = group.get('children')[0];
+        shape.animate({
+          repeat: true,
+          onFrame(ratio) {
+            const diff = ratio <=0.5 ? ratio * 10 : (1 - ratio) * 10;
+            let radius = cfg.size;
+            if (isNaN(radius)) radius = radius[0];
+            return {
+              r: radius / 2 + diff
+            }
+          }
+        }, 3000, 'easeCubic');
+      }
+    },
+    getAnchorPoints() {
+      return [
+        [0.5, 0], // top
+        [1, 0.5], // right
+        [0.5, 1], // bottom
+      ]
+    }
+  }, 'base-node');
+  G6.registerNode('end-node', {
+    shapeType: 'circle',
+    labelPosition: 'bottom',
+    options:  G6.Util.deepMix({},endDefaultOptions,{icon: require('../assets/icons/flow/end.svg')}),
+    getShapeStyle(cfg) {
+      cfg.size = [55, 55];
+      const width = cfg.size[0];
+      const style = {
+        x: 0,
+        y: 0,
+        r: width / 2,
+        ...this.options.style,
+      };
+      if(cfg.hasOwnProperty('color')){
+        style.fill = cfg.color
+      }
+      return style;
+    },
+    afterDraw(cfg, group) {
+      if(cfg.active) {
+
+      }
+    },
+    getAnchorPoints() {
+      return [
+        [0.5, 0], // top
+        [0.5, 1], // bottom
+        [0, 0.5], // left
+      ]
+    }
+  }, 'base-node');
+  G6.registerNode('catch-node', {
+    shapeType: 'path',
+    labelPosition: 'bottom',
+    options: {
+      ...catchDefaultOptions
+    },
+    getShapeStyle(cfg) {
+      cfg.size = [70, 55];
+      const width = cfg.size[0];
+      const height = cfg.size[1];
+      const style = {
+        path: [
+          ['M', 0 , -height/3],
+          ['L', width/2, -height/3],
+          ['L', 0, height/3*2],
+          ['L', -width/2, -height/3],
+          ['Z'] // close
+        ],
+        ...this.options.style,
+      };
+      return style;
+    },
+    getAnchorPoints() {
+      return [
+        [0.5, 0], // top
+        [0.8, 0.38], // right
+        [0.5, 1], // bottom
+        [0.2, 0.38], // left
+      ]
+    }
+  }, 'base-node');
+  G6.registerNode('user-task-node', {
+    options:  G6.Util.deepMix({},taskDefaultOptions,{
+      icon: require('../assets/icons/flow/icon_user.svg'),
+      style: {
+        fill: '#E7F7FE',
+        stroke: '#1890FF',
+      },
+      stateStyles: {
+        selected: {
+          fill: '#95D6FB',
+        },
+      }
+    }),
+  }, 'task-node');
+  G6.registerNode('script-task-node', {
+    options:  G6.Util.deepMix({},taskDefaultOptions,{
+      icon: require('../assets/icons/flow/icon_script.svg'),
+      style: {
+        fill: '#FFF7E6',
+        stroke: '#FFA940',
+      },
+      stateStyles: {
+        selected: {
+          fill: '#FFE7BA',
+        },
+      }
+    }),
+  }, 'task-node');
+  G6.registerNode('java-task-node', {
+    options:  G6.Util.deepMix({},taskDefaultOptions,{
+      icon: require('../assets/icons/flow/icon_java.svg'),
+      style: {
+        fill: '#FFF1F0',
+        stroke: '#FF4D4F',
+      },
+      stateStyles: {
+        selected: {
+          fill: '#FFCCC7',
+        },
+      }
+    }),
+  }, 'task-node');
+  G6.registerNode('mail-task-node', {
+    options:  G6.Util.deepMix({},taskDefaultOptions,{
+      icon: require('../assets/icons/flow/icon_mail.svg'),
+      style: {
+        fill: '#F6FFED',
+        stroke: '#73D13D',
+      },
+      stateStyles: {
+        selected: {
+          fill: '#D9F7BE',
+        },
+      }
+    }),
+  }, 'task-node');
+  G6.registerNode('receive-task-node', {
+    options:  G6.Util.deepMix({},taskDefaultOptions,{
+      icon: require('../assets/icons/flow/icon_receive.svg'),
+      style: {
+        fill: '#FFF0F6',
+        stroke: '#FF85C0',
+      },
+      stateStyles: {
+        selected: {
+          fill: '#FFD6E7',
+        },
+      }
+    }),
+  }, 'task-node');
+  G6.registerNode('timer-start-node', {
+    options:  G6.Util.deepMix({},startDefaultOptions,{icon: require('../assets/icons/flow/icon_timer.svg')}),
+    afterDraw(cfg, group) { this.runAnimate(cfg,group) },
+  }, 'start-node');
+  G6.registerNode('message-start-node', {
+    options:  G6.Util.deepMix({},startDefaultOptions,{icon: require('../assets/icons/flow/icon_message.svg')}),
+    afterDraw(cfg, group) { this.runAnimate(cfg,group) },
+  }, 'start-node');
+  G6.registerNode('signal-start-node', {
+    options:  G6.Util.deepMix({},startDefaultOptions,{icon: require('../assets/icons/flow/icon_signal.svg')}),
+    afterDraw(cfg, group) { this.runAnimate(cfg,group) },
+  }, 'start-node');
+  G6.registerNode('timer-catch-node', {
+    options:  G6.Util.deepMix({},catchDefaultOptions,{icon: require('../assets/icons/flow/icon_timer.svg')}),
+  }, 'catch-node');
+  G6.registerNode('signal-catch-node', {
+    options:  G6.Util.deepMix({},catchDefaultOptions,{icon: require('../assets/icons/flow/icon_signal.svg')}),
+  }, 'catch-node');
+  G6.registerNode('message-catch-node', {
+    options:  G6.Util.deepMix({},catchDefaultOptions,{icon: require('../assets/icons/flow/icon_message.svg')}),
+  }, 'catch-node');
+}

+ 15 - 0
web/src/components/gva-wfd/shape/index.js

@@ -0,0 +1,15 @@
+import registerAnchor from './anchor'
+import registerControlPoint from './controlPoint'
+import registerNode from './node'
+import registerFlowNode from './flowNode'
+import registerEdge from './edge'
+import registerSubProcess from './subProcess'
+
+export default function(G6){
+  registerAnchor(G6);
+  registerControlPoint(G6);
+  registerNode(G6);
+  registerFlowNode(G6);
+  registerEdge(G6);
+  registerSubProcess(G6)
+}

+ 207 - 0
web/src/components/gva-wfd/shape/node.js

@@ -0,0 +1,207 @@
+import editorStyle from "../util/defaultStyle";
+import Anchor from '../item/anchor';
+const dashArray = [
+  [0,1],
+  [0,2],
+  [1,2],
+  [0,1,1,2],
+  [0,2,1,2],
+  [1,2,1,2],
+  [2,2,1,2],
+  [3,2,1,2],
+  [4,2,1,2]
+];
+const interval = 9;
+const lineDash = [4, 2, 1, 2];
+
+const nodeDefinition = {
+  options:{
+    icon: null,
+    iconStyle: {
+      width: 14,
+      height: 14,
+      left: 0,
+      top: 0,
+    },
+    style:{
+      fill: '#f9f9f9',
+      stroke:'#bbb',
+      cursor: 'default',
+    },
+    stateStyles: {
+      selected: {
+        fill: '#eee',
+      },
+      hover: {
+        cursor: editorStyle.cursor.hoverNode,
+      }
+    }
+  },
+  drawAnchor(group) {
+    const bbox = group.get('children')[0].getBBox();
+    this.getAnchorPoints().forEach((p, i) => {
+      const anchorContainer = group.addGroup();
+      const anchor = new Anchor({
+        group: anchorContainer,
+        index: i,
+        model:{
+          style:{
+            x: bbox.minX + bbox.width * p[0],
+            y: bbox.minY + bbox.height * p[1]
+          }
+        }
+      });
+      group.anchorShapes.push(anchorContainer);
+    });
+  },
+  initAnchor(group){
+    group.anchorShapes = [];
+    group.showAnchor = () => {
+      this.drawAnchor(group);
+    };
+    group.getAllAnchors = () => {
+      return group.anchorShapes.map(c => {
+        c.filter(a => a.isAnchor)
+      })
+    };
+    group.getAnchor = (i) => {
+      return group.anchorShapes.filter(a => a.get('index') === i)
+    };
+    group.clearAnchor = () => {
+      group.anchorShapes && group.anchorShapes.forEach(a => a.remove());
+      group.anchorShapes = [];
+    };
+    group.clearHotpotActived = () => {
+      group.anchorShapes && group.anchorShapes.forEach(a => {
+        if (a.isAnchor)
+          a.setHotspotActived(false);
+      });
+    };
+  },
+  drawShape(cfg, group) {
+    const shapeType = this.shapeType;
+    let style = this.getShapeStyle(cfg);
+    const shape = group.addShape(shapeType, {
+      attrs: {
+        ...style,
+      }
+    });
+    this.drawIcon(cfg,group);
+    this.initAnchor(group);
+    return shape;
+  },
+  drawIcon(cfg,group){
+    let style = this.getShapeStyle(cfg);
+    if(this.options.icon){
+      let attrs = {
+        x: style.x + this.options.iconStyle.left,
+        y: style.y + this.options.iconStyle.top,
+        width: this.options.iconStyle.width,
+        height: this.options.iconStyle.height,
+      };
+      if(this.shapeType === 'circle'){
+        attrs = {
+          x: style.x- style.r + this.options.iconStyle.left,
+          y: style.y - style.r + this.options.iconStyle.top,
+          width: this.options.iconStyle.width,
+          height: this.options.iconStyle.height,
+        }
+      }else if(this.shapeType === 'path'){
+        attrs = {
+          x: this.options.iconStyle.left,
+          y: this.options.iconStyle.top,
+          width: this.options.iconStyle.width,
+          height: this.options.iconStyle.height,
+        }
+      }
+      group.icon = group.addShape('image', {
+        attrs: {
+          img:this.options.icon,
+          ...attrs,
+        },
+        draggable: true,
+      });
+      if(cfg.hideIcon){
+        group.icon.hide();
+      }
+    }
+  },
+  setState(name, value, item) {
+    const group = item.getContainer();
+    if (name === 'show-anchor') {
+      if (value) {
+        group.showAnchor();
+      } else {
+        group.clearAnchor();
+      }
+    } else if (name === 'selected') {
+      const rect = group.getChildByIndex(0);
+      if (value) {
+        rect.attr('fill', this.options.stateStyles.selected.fill);
+      } else {
+        rect.attr('fill', this.options.style.fill);
+      }
+    } else if (name === 'hover') {
+      const rect = group.getChildByIndex(0);
+      const text = group.getChildByIndex(1);
+      if (value) {
+        rect.attr('cursor', this.options.stateStyles.hover.cursor);
+        if(text)
+          text.attr('cursor', this.options.stateStyles.hover.cursor);
+      } else {
+        rect.attr('cursor', this.options.style.cursor);
+        if(text)
+          text.attr('cursor', this.options.style.cursor);
+      }
+    }
+    this.setCustomState(name, value, item);
+  },
+  setCustomState(name, value, item){
+
+  },
+  getAnchorPoints() {
+    return [
+      [0.5, 0], // top
+      [1, 0.5], // right
+      [0.5, 1], // bottom
+      [0, 0.5], // left
+    ]
+  },
+  runAnimate(cfg, group){
+    if(cfg.active){
+      let totalArray = [];
+      let index = 0;
+      const shape = group.getFirst();
+      shape.animate(
+        (ratio)=>{
+          for (let i = 0; i < 9; i += interval) {
+            totalArray = totalArray.concat(lineDash);
+          }
+          const cfg = {
+            lineDash: dashArray[index].concat(totalArray)
+          };
+          index = (index + 1) % interval;
+          return cfg;
+        },
+        {
+          repeat: true,
+          duration: 5000
+        });
+    }
+  },
+  afterDraw(cfg, group) {
+    this.runAnimate(cfg,group);
+  },
+  afterUpdate(cfg, group) {
+    const icon = group.get('group').icon;
+    if(cfg.hideIcon && icon && icon.get('visible')){
+      icon.hide();
+    }else if(!cfg.hideIcon && icon && !icon.get('visible')){
+      icon.show();
+    }
+  },
+};
+
+export default function(G6) {
+  G6.registerNode('base-node', nodeDefinition, 'single-node');
+}

+ 291 - 0
web/src/components/gva-wfd/shape/subProcess.js

@@ -0,0 +1,291 @@
+import editorStyle from '../util/defaultStyle';
+import { getShapeName } from '../util/clazz';
+import ControlPoint from '../item/controlPoint';
+const Node = require('@antv/g6/lib/item/node');
+const Edge = require('@antv/g6/lib/item/edge');
+
+export default function(G6) {
+  G6.registerNode('sub-process-node', {
+    shapeType: 'rect',
+    options: {
+      icon: null,
+      iconStyle: {
+        width: 12,
+        height: 12,
+        left: 2,
+        top: 2,
+      },
+      style: {
+        ...editorStyle.nodeStyle,
+        fill: '#FFFFFF',
+        stroke: '#1890FF',
+        cursor: 'default',
+      },
+      stateStyles: {
+        selected: {
+          fill: '#E7F7FE',
+        },
+        addNode: {
+          fill: '#E7F7FE',
+        },
+        hover: {
+          cursor: editorStyle.cursor.hoverNode,
+        },
+      },
+    },
+    drawControlPoints(group) {
+      const bbox = group.get('children')[0].getBBox();
+      this.getControlPoints().forEach((p, i) => {
+        const anchorContainer = group.addGroup();
+        let cursor = 'default';
+        if (p[0] === 0 && p[1] === 0) {
+          cursor = 'nwse-resize';
+        } else if (p[0] === 0 && p[1] === 0.5) {
+          cursor = 'ew-resize';
+        } else if (p[0] === 1 && p[1] === 0) {
+          cursor = 'nesw-resize';
+        } else if (p[0] === 1 && p[1] === 0.5) {
+          cursor = 'ew-resize';
+        } else if (p[0] === 1 && p[1] === 1) {
+          cursor = 'nwse-resize';
+        } else if (p[0] === 0.5 && p[1] === 1) {
+          cursor = 'ns-resize';
+        } else if (p[0] === 0 && p[1] === 1) {
+          cursor = 'nesw-resize';
+        } else if (p[0] === 0.5 && p[1] === 0) {
+          cursor = 'ns-resize';
+        }
+        const anchor = new ControlPoint({
+          group: anchorContainer,
+          index: i,
+          model:{
+            style:{
+              x: bbox.minX + bbox.width * p[0],
+              y: bbox.minY + bbox.height * p[1],
+              cursor,
+            }
+          }
+        });
+        // fuck 坑了好几天,全局注册的东西,在Item内部调用 Shape.getFactory('anchor') 查询不到。
+        // 导致 item 初始化不成功。 只能暂时手工在外部把item 初始化了。 有点坑。
+        // const shapeFactory = G6.Shape.getFactory('anchor');
+        // anchor.set('shapeFactory', shapeFactory);
+        // anchor.draw();
+        anchor.toFront();
+        group.controlPointShapes.push(anchorContainer);
+        group.getAllAnchors = () => {
+          return group.controlPointShapes;
+        };
+        group.getAnchor = (j) => {
+          return group.controlPointShapes.filter(a => a.get('index') === j);
+        };
+      });
+    },
+    drawNodes(cfg, group) {
+      const nodes = [];
+      if (cfg.content && cfg.content.nodes) {
+        cfg.content.nodes.forEach(nodeCfg => {
+          const nodeContainer = group.addGroup();
+          const node = new Node({
+            group: nodeContainer,
+            capture: false,
+            id: nodeCfg.id,
+            groupId: cfg.id,
+            model: {
+              ...nodeCfg,
+              shape: getShapeName(nodeCfg.clazz),
+            },
+          });
+          node.toFront();
+          nodeCfg.shape = getShapeName(nodeCfg.clazz);
+          nodes.push(node);
+        });
+      }
+      return nodes;
+    },
+    drawEdges(cfg, group) {
+      const edges = [];
+      if (cfg.content && cfg.content.edges) {
+        cfg.content.edges.forEach(edgeCfg => {
+          let source = edgeCfg.source;
+          let target = edgeCfg.target;
+          if (source && G6.Util.isString(source)) {
+            source = group.findById(source);
+            source = source.get('item');
+          }
+          if (target && G6.Util.isString(target)) {
+            target = group.findById(target);
+            target = target.get('item');
+          }
+          const edgeContainer = group.addGroup();
+          const edge = new Edge({
+            group: edgeContainer,
+            capture: false,
+            source,
+            target,
+            id: edgeCfg.id,
+            model: {
+              ...edgeCfg,
+              shape: 'flow-polyline-round',
+            },
+          });
+          edge.toFront();
+          const model = edge.get('model');
+          edgeCfg.startPoint = model.startPoint;
+          edgeCfg.endPoint = model.endPoint;
+          edgeCfg.shape = 'flow-polyline-round';
+          edges.push(edge);
+        });
+      }
+      return edges;
+    },
+    _addNode(node, cfg) {
+      const nodeModel = node.get('model');
+      const nodes = nodeModel.content ? nodeModel.content.nodes : [];
+      const edges = nodeModel.content ? nodeModel.content.edges : [];
+      nodes.push(cfg);
+      return { content: { nodes, edges } };
+    },
+    _addEdge(node, cfg) {
+      const nodeModel = node.get('model');
+      const nodes = nodeModel.content ? nodeModel.content.nodes : [];
+      const edges = nodeModel.content ? nodeModel.content.edges : [];
+      edges.push(cfg);
+      return { content: { nodes, edges } };
+    },
+    _updateNode(node, nodeId, cfg) {
+      const nodeModel = node.get('model');
+      const nodes = nodeModel.content ? nodeModel.content.nodes : [];
+      const edges = nodeModel.content ? nodeModel.content.edges : [];
+      const tempNode = nodes.find(a => a.id === nodeId);
+      Object.assign(tempNode, cfg);
+      return { content: { nodes, edges } };
+    },
+    _getItem(node, itemId) {
+      const containerGroup = node.getContainer();
+      return containerGroup.nodes.find(item => item.get('id') === itemId);
+    },
+    _removeItem(node, itemId) {
+      const nodeModel = node.get('model');
+      if (nodeModel && nodeModel.content) {
+        let index = nodeModel.content.nodes.findIndex(item => item.id === itemId);
+        if (index !== -1) {
+          nodeModel.content.nodes.splice(index, 1);
+          return { content: nodeModel.content };
+        } else {
+          index = nodeModel.content.edges.findIndex(item => item.id === itemId);
+          if (index !== -1) {
+            nodeModel.content.edges.splice(index, 1);
+            return { content: nodeModel.content };
+          }
+        }
+      }
+      return {};
+    },
+    drawShape(cfg, group) {
+      const shapeType = this.shapeType;
+      const style = this.getShapeStyle(cfg);
+      const shape = group.addShape(shapeType, {
+        attrs: {
+          ...style,
+        },
+      });
+
+      this.drawIcon(cfg,group);
+      this.initAnchor(group);
+
+      const subGroup = group.addGroup({ id: 'sub_' + cfg.id });
+      group.subGroup = subGroup;
+      // 渲染内部Nodes
+      const nodes = this.drawNodes(cfg, subGroup);
+      // 渲染内部edges
+      const edges = this.drawEdges(cfg, subGroup);
+
+      group.nodes = nodes;
+      group.edges = edges;
+      group.controlPointShapes = [];
+      group.showControlPoints = (_group) => {
+        this.drawControlPoints(_group);
+      };
+      group.clearControlPoints = (_group) => {
+        if (_group.controlPointShapes) {
+          _group.controlPointShapes.forEach(a => a.remove());
+        }
+        _group.controlPointShapes = [];
+      };
+      group.addNodeModel = (_node, _cfg) => {
+        return this._addNode(_node, _cfg);
+      };
+      group.addEdgeModel = (_node, _cfg) => {
+        return this._addEdge(_node, _cfg);
+      };
+      group.updateNodeModel = (_node, _itemId, _cfg) => {
+        return this._updateNode(_node, _itemId, _cfg);
+      };
+      group.getItem = (_node, _itemId) => {
+        return this._getItem(_node, _itemId);
+      };
+      group.removeItem = (_node, _itemId) => {
+        return this._removeItem(_node, _itemId);
+      };
+
+      return shape;
+    },
+    setCustomState(name, value, item){
+      const group = item.getContainer();
+      if (name === 'selected') {
+        const rect = group.getChildByIndex(0);
+        if (value) {
+          group.clearAnchor(group);
+          group.showControlPoints(group);
+          rect.attr('fill', this.options.stateStyles.selected.fill);
+        } else {
+          group.clearControlPoints(group);
+          rect.attr('fill', this.options.style.fill);
+        }
+      }
+    },
+    getControlPoints() {
+      return [
+        [0, 0],
+        [0.5, 0],
+        [1, 0],
+        [1, 0.5],
+        [1, 1],
+        [0.5, 1],
+        [0, 1],
+        [0, 0.5],
+      ];
+    },
+    getShapeStyle(cfg) {
+      if (!cfg.size || !Array.isArray(cfg.size)) {
+        cfg.size = [80, 44];
+      }
+      const width = cfg.size[0];
+      const height = cfg.size[1];
+      const style = {
+        x: 0 - width / 2,
+        y: 0 - height / 2,
+        width,
+        height,
+        ...this.options.style,
+      };
+      return style;
+    },
+    afterUpdate(cfg, node) {
+      // TODO 因为没有改变 shape 所以不会更新
+      const bbox = node.getBBox();
+      const group = node.getContainer();
+      const subGroup = group.subGroup;
+      if (subGroup) {
+        subGroup.clear();
+        // 重新渲染内部Nodes
+        const nodes = this.drawNodes(cfg, subGroup);
+        // 重新渲染内部edges
+        const edges = this.drawEdges(cfg, subGroup);
+        group.nodes = nodes;
+        group.edges = edges;
+      }
+    },
+  }, 'base-node');
+}

+ 225 - 0
web/src/components/gva-wfd/util/bpmn.js

@@ -0,0 +1,225 @@
+function tab(len){
+  return [...Array(len)].map(a => " ").join('')
+}
+export function exportXML(json,canvas,createFile = true) {
+  const id = canvas.id || "flow";
+  const name = canvas.name || "flow";
+  let dataObjs = "";
+  canvas.dataObjs.forEach(s => {
+    dataObjs += `${tab(4)}<dataObject id="${s.id}" name="${s.name}" itemSubjectRef="xsd:${s.type}"></dataObject>\n`;
+  });
+  let signals = "";
+  canvas.signalDefs.forEach(s => {
+    signals += `${tab(2)}<signal id="${s.id}" name="${s.name}" flowable:scope="${s.scope}"></signal>\n`;
+  });
+  let messages = "";
+  canvas.messageDefs.forEach(s => {
+    messages += `${tab(2)}<message id="${s.id}" name="${s.name}"></message>\n`;
+  });
+  let BPMNShape = ``;
+  let BPMNEdge = ``;
+
+  let processXML = `${tab(2)}<process id="${id}" name="${name}">\n`;
+  processXML += dataObjs;
+  json.nodes.forEach(node => {
+    BPMNShape += `${tab(6)}<bpmndi:BPMNShape bpmnElement="${node.id}" id="BPMNShape_${node.id}">\n`+
+      `${tab(8)}<omgdc:Bounds width="${node.size[0]}" height="${node.size[1]}" x="${node.x}" y="${node.y}"></omgdc:Bounds>\n`+
+      `${tab(6)}</bpmndi:BPMNShape>\n`;
+    switch (node.clazz) {
+      case 'start':
+        processXML += `${tab(4)}<startEvent id="${node.id}"></startEvent>\n`;
+        break;
+      case 'end':
+        processXML += `${tab(4)}<endEvent id="${node.id}"></endEvent>\n`;
+        break;
+      case 'userTask': {
+        let assignments = "";
+        if(node.assignValue && node.assignValue.length > 0){
+          if(node.assignType === 'person'){
+            assignments += `flowable:candidateUsers="${node.assignValue.join(',')}"`;
+          }else if(node.assignType === 'assignee'){
+            assignments += `flowable:assignee="${node.assignValue[0]}"`;
+          }else if(node.assignType === 'persongroup'){
+            assignments += `flowable:candidateGroups="${node.assignValue.join(',')}"`;
+          }
+        }
+        processXML += `${tab(4)}<userTask id="${node.id}" name="${node.label}" ${assignments}></userTask>\n`;
+        break;
+      }
+      case 'javaTask': {
+        let javaClass = "";
+        if(node.javaClass){
+          javaClass = `flowable:class="${node.javaClass}"`;
+        }
+        processXML += `${tab(4)}<serviceTask id="${node.id}" name="${node.label}" ${javaClass}></serviceTask>\n`;
+        break;
+      }
+      case 'scriptTask': {
+        let script = "";
+        if(node.script) {
+          script = `${tab(6)}<script><![CDATA[${node.script}]]></script>\n`;
+        }
+        processXML += `${tab(4)}<scriptTask id="${node.id}" name="${node.label}">\n${script}${tab(4)}</scriptTask>\n`;
+        break;
+      }
+      case 'receiveTask':
+        processXML += `${tab(4)}<receiveTask id="${node.id}" name="${node.label}"></receiveTask>\n`;
+        break;
+      case 'mailTask': {
+        let to = `${tab(8)}<flowable:field name="to">\n`;
+        to += `${tab(10)}<flowable:string><![CDATA[${node.to}]]></flowable:string>\n`;
+        to += `${tab(8)}</flowable:field>\n`;
+        let subject = `${tab(8)}<flowable:field name="subject">\n`;
+        subject += `${tab(10)}<flowable:string><![CDATA[${node.subject}]]></flowable:string>\n`;
+        subject += `${tab(8)}</flowable:field>\n`;
+        let text = `${tab(8)}<flowable:field name="text">\n`;
+        text += `${tab(10)}<flowable:string><![CDATA[${node.content}]]></flowable:string>\n`;
+        text += `${tab(8)}</flowable:field>\n`;
+        let extension = `${tab(6)}<extensionElements>\n${to}${subject}${text}${tab(6)}</extensionElements>\n`;
+        processXML += `${tab(4)}<serviceTask id="${node.id}" name="${node.label}" flowable:type="mail">\n${extension}${tab(4)}</serviceTask>\n`;
+        break;
+      }
+      case 'timerStart': {
+        const timer = `${tab(6)}<timerEventDefinition>\n${tab(8)}<timeCycle>${node.cycle}</timeCycle>\n${tab(6)}</timerEventDefinition>\n`;
+        processXML += `${tab(4)}<startEvent id="${node.id}" isInterrupting="false">\n${timer}${tab(4)}</startEvent>\n`;
+        break;
+      }
+      case 'timerCatch': {
+        const timer = `${tab(6)}<timerEventDefinition>\n${tab(8)}<timeCycle>${node.cycle}</timeCycle>\n${tab(6)}</timerEventDefinition>\n`;
+        processXML += `${tab(4)}<intermediateCatchEvent id="${node.id}">\n${timer}${tab(4)}</intermediateCatchEvent>\n`;
+        break;
+      }
+      case 'signalStart': {
+        const signal = `${tab(6)}<signalEventDefinition signalRef="${node.signal}"></signalEventDefinition>\n`;
+        processXML += `${tab(4)}<startEvent id="${node.id}" isInterrupting="true">\n${signal}${tab(4)}</startEvent>\n`;
+        break;
+      }
+      case 'signalCatch': {
+        const signal = `${tab(6)}<signalEventDefinition signalRef="${node.signal}"></signalEventDefinition>\n`;
+        processXML += `${tab(4)}<intermediateCatchEvent id="${node.id}">\n${signal}${tab(4)}</intermediateCatchEvent>\n`;
+        break;
+      }
+      case 'messageStart': {
+        const message = `${tab(6)}<messageEventDefinition messageRef="${node.message}"></messageEventDefinition>\n`;
+        processXML += `${tab(4)}<startEvent id="${node.id}" isInterrupting="true">\n${message}${tab(4)}</startEvent>\n`;
+        break;
+      }
+      case 'messageCatch': {
+        const message = `${tab(6)}<messageEventDefinition messageRef="${node.message}"></messageEventDefinition>\n`;
+        processXML += `${tab(4)}<intermediateCatchEvent id="${node.id}">\n${message}${tab(4)}</intermediateCatchEvent>\n`;
+        break;
+      }
+      case 'gateway':
+        processXML += `${tab(4)}<exclusiveGateway id="${node.id}" name="${node.label}"></exclusiveGateway>\n`;
+        break;
+      case 'exclusiveGateway':
+        processXML += `${tab(4)}<exclusiveGateway id="${node.id}" name="${node.label}"></exclusiveGateway>\n`;
+        break;
+      case 'parallelGateway':
+        processXML += `${tab(4)}<parallelGateway id="${node.id}" name="${node.label}"></parallelGateway>\n`;
+        break;
+      case 'inclusiveGateway':
+        processXML += `${tab(4)}<inclusiveGateway id="${node.id}" name="${node.label}"></inclusiveGateway>\n`;
+        break;
+      default:
+        break;
+    }
+  });
+  json.edges.forEach(edge => {
+    BPMNEdge += `${tab(6)}<bpmndi:BPMNEdge bpmnElement="${edge.source}_${edge.sourceAnchor}-${edge.target}_${edge.targetAnchor}" `+
+      `id="BPMNEdge_${edge.source}_${edge.sourceAnchor}-${edge.target}_${edge.targetAnchor}">\n`+
+        `${tab(8)}<omgdi:waypoint x="${edge.startPoint.x}" y="${edge.startPoint.y}"></omgdi:waypoint>\n`+
+        `${tab(8)}<omgdi:waypoint x="${edge.endPoint.x}" y="${edge.endPoint.y}"></omgdi:waypoint>\n`+
+      `${tab(6)}</bpmndi:BPMNEdge>\n`;
+    let condition = "";
+    if(edge.conditionExpression){
+      condition = `${tab(6)}<conditionExpression xsi:type="tFormalExpression"><![CDATA[${edge.conditionExpression}]]></conditionExpression>\n`;
+    }
+    processXML += `${tab(4)}<sequenceFlow id="${edge.source}_${edge.sourceAnchor}-${edge.target}_${edge.targetAnchor}" sourceRef="${edge.source}" targetRef="${edge.target}">${condition}</sequenceFlow>\n`;
+  });
+  processXML += `${tab(2)}</process>\n`;
+
+  let BPMNDiagram = `${tab(2)}<bpmndi:BPMNDiagram id="BPMNDiagram_${id}">\n`+
+      `${tab(4)}<bpmndi:BPMNPlane bpmnElement="${id}" id="BPMNPlane_${id}">\n${BPMNShape}${BPMNEdge}${tab(4)}</bpmndi:BPMNPlane>\n`+
+      `${tab(2)}</bpmndi:BPMNDiagram>\n`;
+
+  let xml = `<?xml version="1.0" encoding="UTF-8"?>\n`;
+  xml += `<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL" `+
+    `xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" `+
+    `xmlns:xsd="http://www.w3.org/2001/XMLSchema" `+
+    `xmlns:flowable="http://flowable.org/bpmn" `+
+    `xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI" `+
+    `xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" `+
+    `xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI" `+
+    `typeLanguage="http://www.w3.org/2001/XMLSchema" `+
+    `expressionLanguage="http://www.w3.org/1999/XPath" `+
+    `targetNamespace="http://www.flowable.org/processdef">\n`;
+  xml += signals;
+  xml += messages;
+  xml += processXML;
+  xml += BPMNDiagram;
+  xml += `</definitions>`;
+  if(createFile) {
+    downloadFile(xml, 'application/xml;charset=utf-8;', `${name}.bpmn20.xml`)
+  }
+  return xml;
+}
+
+export function exportImg(canvasPanel,filename,createFile = true) {
+  filename = filename || 'flow'
+  let canvas = canvasPanel.querySelector('canvas')
+  let context = canvas.getContext('2d')
+
+  let imgData = context.getImageData(0, 0, canvas.width, canvas.height).data
+  let left = canvas.width;
+  let right = 0;
+  let top = canvas.height;
+  let bottom = 0
+  for (let i = 0; i < canvas.width; i++) {
+    for (let j = 0; j < canvas.height; j++) {
+      let pos = (i + canvas.width * j) * 4
+      if (imgData[pos] > 0 || imgData[pos + 1] > 0 || imgData[pos + 2] || imgData[pos + 3] > 0) {
+        bottom = Math.max(j, bottom) // 找到有色彩的最下端
+        right = Math.max(i, right) // 找到有色彩的最右端
+        top = Math.min(j, top) // 找到有色彩的最上端
+        left = Math.min(i, left) // 找到有色彩的最左端
+      }
+    }
+  }
+  let c = document.createElement('canvas')
+  // 四周空白余量
+  let blankWidth = 60
+  c.width = right - left + blankWidth*2
+  c.height = bottom - top + blankWidth*2
+  let ctx = c.getContext('2d')
+  // 设置白底
+  ctx.fillStyle = '#fff';
+  ctx.fillRect(0, 0, c.width, c.height);
+  ctx.drawImage(canvas, left-blankWidth, top-blankWidth, c.width, c.height, 0, 0, c.width, c.height)
+  let data = c.toDataURL("image/jpeg")
+  if(createFile) {
+    let parts = data.split(';base64,');
+    let contentType = parts[0].split(':')[1];
+    let raw = window.atob(parts[1]);
+    let uInt8Array = new Uint8Array(raw.length);
+    for (let i = 0; i < raw.length; ++i) {
+      uInt8Array[i] = raw.charCodeAt(i);
+    }
+    downloadFile(uInt8Array, contentType, `${filename}.jpg`)
+  }
+  return data
+}
+
+function downloadFile(data, type, filename) {
+  const blob = new Blob([data], { type });
+  let link = document.createElement('a');
+  if (link.download !== undefined) {
+    let url = URL.createObjectURL(blob);
+    link.setAttribute('href', url);
+    link.setAttribute('download', filename);
+    link.style.visibility = 'hidden';
+    document.body.appendChild(link);
+    link.click();
+    document.body.removeChild(link);
+  }
+}

+ 23 - 0
web/src/components/gva-wfd/util/clazz.js

@@ -0,0 +1,23 @@
+export function getShapeName(clazz) {
+  switch (clazz) {
+    case 'start': return 'start-node';
+    case 'end': return 'end-node';
+    case 'gateway': return 'gateway-node';
+    case 'exclusiveGateway': return 'exclusive-gateway-node';
+    case 'parallelGateway': return 'parallel-gateway-node';
+    case 'inclusiveGateway': return 'inclusive-gateway-node';
+    case 'timerStart': return 'timer-start-node';
+    case 'messageStart': return 'message-start-node';
+    case 'signalStart': return 'signal-start-node';
+    case 'userTask': return 'user-task-node';
+    case 'scriptTask': return 'script-task-node';
+    case 'mailTask': return 'mail-task-node';
+    case 'javaTask': return 'java-task-node';
+    case 'receiveTask': return 'receive-task-node';
+    case 'timerCatch': return 'timer-catch-node';
+    case 'messageCatch': return 'message-catch-node';
+    case 'signalCatch': return 'signal-catch-node';
+    case 'subProcess': return 'sub-process-node';
+    default: return 'task-node';
+  }
+}

+ 62 - 0
web/src/components/gva-wfd/util/defaultStyle.js

@@ -0,0 +1,62 @@
+export default {
+  nodeActivedOutterStyle: { lineWidth: 0 },
+  groupSelectedOutterStyle: { stroke: '#E0F0FF', lineWidth: 2 },
+  nodeSelectedOutterStyle: { stroke: '#E0F0FF', lineWidth: 2 },
+  edgeActivedStyle: { stroke: '#1890FF', strokeOpacity: .92 },
+  nodeActivedStyle: { fill: '#F3F9FF', stroke: '#1890FF' },
+  groupActivedStyle: { stroke: '#1890FF' },
+  edgeSelectedStyle: { lineWidth: 2, strokeOpacity: .92, stroke: '#A3B1BF' },
+  nodeSelectedStyle: { fill: '#F3F9FF', stroke: '#1890FF',fillOpacity: .4 },
+  groupSelectedStyle: { stroke: '#1890FF', fillOpacity: .92 },
+  nodeStyle: {
+    stroke: '#CED4D9',
+    fill: '#FFFFFF',
+    shadowOffsetX: 0,
+    shadowOffsetY: 4,
+    shadowBlur: 10,
+    shadowColor: 'rgba(13, 26, 38, 0.08)',
+    lineWidth: 1,
+    radius: 4,
+    strokeOpacity: .7,
+  },
+  edgeStyle: { stroke: '#A3B1BF', strokeOpacity: .92, lineWidth: 1, lineAppendWidth: 8, endArrow: true},
+  groupBackgroundPadding: [40, 10, 10, 10],
+  groupLabelOffsetX: 10,
+  groupLabelOffsetY: 10,
+  edgeLabelStyle: { fill: '#666', textAlign: 'center', textBaseline: 'middle' },
+  edgeLabelRectPadding: 4,
+  edgeLabelRectStyle: { fill: 'white' },
+  nodeLabelStyle: { fill: '#666', textAlign: 'center', textBaseline: 'middle' },
+  groupStyle: { stroke: '#CED4D9', radius: 4 },
+  groupLabelStyle: { fill: '#666', textAlign: 'left', textBaseline: 'top' },
+  multiSelectRectStyle: { fill: '#1890FF', fillOpacity: .08, stroke: '#1890FF', opacity: .1 },
+  dragNodeHoverToGroupStyle: { stroke: '#1890FF', lineWidth: 2 },
+  dragNodeLeaveFromGroupStyle: { stroke: '#BAE7FF', lineWidth: 2 },
+  anchorPointStyle: { r: 3.5, fill: '#fff', stroke: '#1890FF', lineAppendWidth: 12 },
+  anchorHotsoptStyle: { r: 12, fill: '#1890FF', fillOpacity: .25 },
+  anchorHotsoptActivedStyle: { r: 14 },
+  anchorPointHoverStyle: { r: 4, fill: '#1890FF', fillOpacity: 1, stroke: '#1890FF' },
+  nodeControlPointStyle: { radius: 4, fill: '#fff', shadowBlur: 4, shadowColor: '#666' },
+  edgeControlPointStyle: { radius: 6, symbol: 'square', lineAppendWidth: 6, fillOpacity: 0, strokeOpacity: 0 },
+  nodeSelectedBoxStyle: { stroke: '#C2C2C2' },
+  cursor: {
+    panningCanvas: '-webkit-grabbing',
+    beforePanCanvas: '-webkit-grab',
+    hoverNode: 'move',
+    hoverEffectiveAnchor: 'crosshair',
+    hoverEdge: 'default',
+    hoverGroup: 'move',
+    hoverUnEffectiveAnchor: 'default',
+    hoverEdgeControllPoint: 'crosshair',
+    multiSelect: 'crosshair',
+  },
+  nodeDelegationStyle: {
+    stroke: '#1890FF',
+    fill: '#1890FF',
+    fillOpacity: .08,
+    lineDash: [4, 4],
+    radius: 4,
+    lineWidth: 1,
+  },
+  edgeDelegationStyle: { stroke: '#1890FF', lineDash: [4, 4], lineWidth: 1 },
+};

+ 23 - 6
web/src/view/workflow/workflowCreate/workflowCreate.vue

@@ -3,25 +3,28 @@
     <el-button size="small" style="float:right;margin-top:6px;margin-right:6px;" @click="saveXML">导出XML</el-button>
     <el-button size="small" style="float:right;margin-top:6px;margin-right:6px;" @click="saveImg">导出图片</el-button>
     <el-button size="small" style="float:right;margin-top:6px;margin-right:6px;" @click="save">保存流程</el-button>
-    <wfd-gva ref="wfd" :data="demoData" :height="600" :users="users" :groups="groups" :categorys="categorys" :lang="lang" />
+    <gva-wfd ref="wfd" :data="demoData" :height="600" :users="users" :authorities="authorities" :groups="groups" :categorys="categorys" :lang="lang" />
   </div>
 </template>
 <script>
 
 
-import wfdGva from 'wfd-gva'
+import gvaWfd from '@/components/gva-wfd'
+import   {getUserList}     from '@/api/user'
+import   {getAuthorityList}     from '@/api/authority'
 export default {
   name: 'Workflow',
   components:{
-    wfdGva
+    gvaWfd
   },
   data () {
     return {
       lang: "zh",
       demoData: {},
-      users: [{id:'1',name:'审批人1'},{id:'2',name:'审批人2'},{id:'3',name:'审批人3'}],
+      users: [],
+      authorities:[],
       groups: [{id:'1',name:'组1'},{id:'2',name:'组2'},{id:'3',name:'组3'}],
-      categorys:[{id:'1',name:'分类1'},{id:'2',name:'分类2'},{id:'3',name:'分类3'}]
+      categorys:[{id:'1',name:'分类1'},{id:'2',name:'分类2'},{id:'3',name:'分类3'},{id:'4',分组:'分组4'}]
     }
   },
   methods:{
@@ -34,6 +37,20 @@ export default {
     saveImg(){
       console.log(this.$refs['wfd'].graph.saveImg())
     }
-  }
+  },
+  async created(){
+   const userRes = await getUserList({page:1,pageSize:9999999})
+   if(userRes.code == 0){
+     userRes.data.list.map(item=>{
+       this.users.push({id:item.ID,name:item.nickName})
+     })
+   }
+   const authorityRes = await getAuthorityList({page:1,pageSize:9999999})
+   if(authorityRes.code == 0){
+     authorityRes.data.list.map(item=>{
+       this.authorities.push({id:item.authorityId,name:item.authorityName})
+     })
+   }
+  },
 }
 </script>

Some files were not shown because too many files changed in this diff