Browse Source

引入HJflow,不用npm了。

jiaxiaoqiang 3 months ago
parent
commit
67cab407cb
42 changed files with 2281 additions and 85 deletions
  1. 2 1
      package.json
  2. 147 70
      pnpm-lock.yaml
  3. 14 0
      src/components/hjflow/index.ts
  4. 18 0
      src/components/hjflow/src/Handle/CommonHandle.vue
  5. 13 0
      src/components/hjflow/src/background/DropzoneBackground.vue
  6. 59 0
      src/components/hjflow/src/edges/commonEdge.vue
  7. 97 0
      src/components/hjflow/src/flow.vue
  8. 191 0
      src/components/hjflow/src/hjflow/index.vue
  9. 145 0
      src/components/hjflow/src/hooks/useDnD.ts
  10. 0 0
      src/components/hjflow/src/hooks/useFlow.ts
  11. 11 0
      src/components/hjflow/src/information/index.vue
  12. 167 0
      src/components/hjflow/src/leftDrags/test/configs.ts
  13. 41 0
      src/components/hjflow/src/leftDrags/test/dragData.vue
  14. 47 0
      src/components/hjflow/src/leftDrags/workflow/configs.ts
  15. 41 0
      src/components/hjflow/src/leftDrags/workflow/dragData.vue
  16. 11 0
      src/components/hjflow/src/leftDrags/workflow/editInfo.vue
  17. 24 0
      src/components/hjflow/src/nodes/CommonNode.vue
  18. 18 0
      src/components/hjflow/src/nodes/NestNode.vue
  19. 7 0
      src/components/hjflow/src/nodes/NumberFunNode.vue
  20. 7 0
      src/components/hjflow/src/nodes/NumberInputNode.vue
  21. 7 0
      src/components/hjflow/src/nodes/NumberResultNode.vue
  22. 94 0
      src/components/hjflow/src/nodes/com/basic.vue
  23. 49 0
      src/components/hjflow/src/nodes/com/operationHeader.vue
  24. 40 0
      src/components/hjflow/src/nodes/universal/UniversalNode.vue
  25. 40 0
      src/components/hjflow/src/nodes/workflow/WorkFlowNode.vue
  26. 20 0
      src/components/hjflow/src/panel/btns/Back.vue
  27. 44 0
      src/components/hjflow/src/panel/btns/add-node.vue
  28. 27 0
      src/components/hjflow/src/panel/btns/layout-LR.vue
  29. 29 0
      src/components/hjflow/src/panel/btns/layout-TB.vue
  30. 37 0
      src/components/hjflow/src/panel/btns/reset.vue
  31. 32 0
      src/components/hjflow/src/panel/btns/snake.vue
  32. 79 0
      src/components/hjflow/src/panel/index.vue
  33. 45 0
      src/components/hjflow/src/styles/index.scss
  34. 173 0
      src/components/hjflow/src/testShenpiliu.vue
  35. 105 0
      src/components/hjflow/src/types/comTypes.ts
  36. 103 0
      src/components/hjflow/src/utils/index.ts
  37. 72 0
      src/components/hjflow/src/utils/useLayout.ts
  38. 116 0
      src/components/hjflow/src/utils/useSnake.ts
  39. 0 5
      src/main.ts
  40. 60 0
      src/views/modules/project-config/com/function-col.vue
  41. 4 8
      src/views/modules/project-config/configs/properites.ts
  42. 45 1
      src/views/modules/project-config/project-config.vue

+ 2 - 1
package.json

@@ -40,6 +40,7 @@
     ]
   },
   "dependencies": {
+    "@dagrejs/dagre": "^1.1.4",
     "@element-plus/icons-vue": "^2.3.1",
     "@smallwei/avue": "^3.3.3",
     "@types/smallwei__avue": "^3.0.5",
@@ -51,7 +52,7 @@
     "axios": "^1.6.7",
     "echarts": "^5.5.0",
     "element-plus": "2.8.0",
-    "jxq-ui": "^0.0.4",
+    "less": "^4.2.2",
     "lodash-es": "^4.17.21",
     "net": "^1.0.2",
     "nprogress": "^0.2.0",

+ 147 - 70
pnpm-lock.yaml

@@ -8,6 +8,9 @@ importers:
 
   .:
     dependencies:
+      '@dagrejs/dagre':
+        specifier: ^1.1.4
+        version: 1.1.4
       '@element-plus/icons-vue':
         specifier: ^2.3.1
         version: 2.3.1(vue@3.5.13(typescript@5.7.3))
@@ -41,9 +44,9 @@ importers:
       element-plus:
         specifier: 2.8.0
         version: 2.8.0(vue@3.5.13(typescript@5.7.3))
-      jxq-ui:
-        specifier: ^0.0.4
-        version: 0.0.4(@types/sortablejs@1.15.8)(typescript@5.7.3)
+      less:
+        specifier: ^4.2.2
+        version: 4.2.2
       lodash-es:
         specifier: ^4.17.21
         version: 4.17.21
@@ -134,10 +137,10 @@ importers:
         version: 7.18.0(eslint@8.57.1)(typescript@5.7.3)
       '@vitejs/plugin-vue':
         specifier: ^5.0.4
-        version: 5.2.1(vite@5.4.14(@types/node@20.17.19)(sass@1.77.0)(terser@5.39.0))(vue@3.5.13(typescript@5.7.3))
+        version: 5.2.1(vite@5.4.14(@types/node@20.17.19)(less@4.2.2)(sass@1.77.0)(terser@5.39.0))(vue@3.5.13(typescript@5.7.3))
       '@vitejs/plugin-vue-jsx':
         specifier: ^3.1.0
-        version: 3.1.0(vite@5.4.14(@types/node@20.17.19)(sass@1.77.0)(terser@5.39.0))(vue@3.5.13(typescript@5.7.3))
+        version: 3.1.0(vite@5.4.14(@types/node@20.17.19)(less@4.2.2)(sass@1.77.0)(terser@5.39.0))(vue@3.5.13(typescript@5.7.3))
       autoprefixer:
         specifier: ^10.4.18
         version: 10.4.20(postcss@8.5.2)
@@ -209,7 +212,7 @@ importers:
         version: 5.7.3
       unocss:
         specifier: ^0.58.5
-        version: 0.58.9(postcss@8.5.2)(rollup@4.34.8)(vite@5.4.14(@types/node@20.17.19)(sass@1.77.0)(terser@5.39.0))
+        version: 0.58.9(postcss@8.5.2)(rollup@4.34.8)(vite@5.4.14(@types/node@20.17.19)(less@4.2.2)(sass@1.77.0)(terser@5.39.0))
       unplugin-auto-import:
         specifier: ^0.17.5
         version: 0.17.8(@vueuse/core@10.11.1(vue@3.5.13(typescript@5.7.3)))(rollup@4.34.8)
@@ -221,13 +224,13 @@ importers:
         version: 0.26.0(@babel/parser@7.26.9)(rollup@4.34.8)(vue@3.5.13(typescript@5.7.3))
       vite:
         specifier: ^5.1.5
-        version: 5.4.14(@types/node@20.17.19)(sass@1.77.0)(terser@5.39.0)
+        version: 5.4.14(@types/node@20.17.19)(less@4.2.2)(sass@1.77.0)(terser@5.39.0)
       vite-plugin-mock-dev-server:
         specifier: ^1.4.7
-        version: 1.8.4(bufferutil@4.0.9)(esbuild@0.21.5)(rollup@4.34.8)(utf-8-validate@5.0.10)(vite@5.4.14(@types/node@20.17.19)(sass@1.77.0)(terser@5.39.0))
+        version: 1.8.4(bufferutil@4.0.9)(esbuild@0.21.5)(rollup@4.34.8)(utf-8-validate@5.0.10)(vite@5.4.14(@types/node@20.17.19)(less@4.2.2)(sass@1.77.0)(terser@5.39.0))
       vite-plugin-svg-icons:
         specifier: ^2.0.1
-        version: 2.0.1(vite@5.4.14(@types/node@20.17.19)(sass@1.77.0)(terser@5.39.0))
+        version: 2.0.1(vite@5.4.14(@types/node@20.17.19)(less@4.2.2)(sass@1.77.0)(terser@5.39.0))
       vue-tsc:
         specifier: ^2.0.4
         version: 2.2.2(typescript@5.7.3)
@@ -495,6 +498,13 @@ packages:
     resolution: {integrity: sha512-SITSV6aIXsuVNV3f3O0f2n/cgyEDWoSqtZMYiAmcsYHydcKrOz3gUxB/iXd/Qf08+IZX4KpgNbvUdMBmWz+kcA==}
     engines: {node: '>=10'}
 
+  '@dagrejs/dagre@1.1.4':
+    resolution: {integrity: sha512-QUTc54Cg/wvmlEUxB+uvoPVKFazM1H18kVHBQNmK2NbrDR5ihOCR6CXLnDSZzMcSQKJtabPUWridBOlJM3WkDg==}
+
+  '@dagrejs/graphlib@2.2.4':
+    resolution: {integrity: sha512-mepCf/e9+SKYy1d02/UkvSy6+6MoyXhVxP8lLDfA7BPE1X1d4dR0sZznmbM8/XVJ1GPM+Svnx7Xj6ZweByWUkw==}
+    engines: {node: '>17.0.0'}
+
   '@dual-bundle/import-meta-resolve@4.1.0':
     resolution: {integrity: sha512-+nxncfwHM5SgAtrVzgpzJOI1ol0PkumhVo469KCf9lUi21IGcY90G98VuHm9VRrUypmAzawAHO9bs6hqeADaVg==}
 
@@ -1789,6 +1799,9 @@ packages:
     resolution: {integrity: sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==}
     engines: {node: '>= 0.8'}
 
+  copy-anything@2.0.6:
+    resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==}
+
   copy-descriptor@0.1.1:
     resolution: {integrity: sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==}
     engines: {node: '>=0.10.0'}
@@ -2117,11 +2130,6 @@ packages:
     peerDependencies:
       vue: ^3.2.0
 
-  element-plus@2.9.5:
-    resolution: {integrity: sha512-r+X79oogLbYq8p9L5f9fHSHhUFNM0AL72aikqiZVxSc2/08mK6m/PotiB9e/D90QmWTIHIaFnFmW65AcXmneig==}
-    peerDependencies:
-      vue: ^3.2.0
-
   emoji-regex@10.4.0:
     resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==}
 
@@ -2150,6 +2158,10 @@ packages:
     resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==}
     engines: {node: '>=18'}
 
+  errno@0.1.8:
+    resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==}
+    hasBin: true
+
   error-ex@1.3.2:
     resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==}
 
@@ -2741,6 +2753,10 @@ packages:
     resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
     engines: {node: '>=0.10.0'}
 
+  iconv-lite@0.6.3:
+    resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==}
+    engines: {node: '>=0.10.0'}
+
   ieee754@1.2.1:
     resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
 
@@ -3002,6 +3018,9 @@ packages:
     resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==}
     engines: {node: '>= 0.4'}
 
+  is-what@3.14.1:
+    resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==}
+
   is-windows@1.0.2:
     resolution: {integrity: sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA==}
     engines: {node: '>=0.10.0'}
@@ -3080,9 +3099,6 @@ packages:
     resolution: {integrity: sha512-POQXvpdL69+CluYsillJ7SUhKvytYjW9vG/GKpnf+xP8UWgYEM/RaMzHHofbALDiKbbP1W8UEYmgGl39WkPZsg==}
     engines: {'0': node >= 0.2.0}
 
-  jxq-ui@0.0.4:
-    resolution: {integrity: sha512-WB83T3IyScRCnNOqQTFsAZPdBTyrM/5+znaPQY4kUJ2fEf0Y4Nla56YOz7b/S937QXhu4hLOO2l+yawzbqC4dQ==}
-
   keygrip@1.1.0:
     resolution: {integrity: sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==}
     engines: {node: '>= 0.6'}
@@ -3115,6 +3131,11 @@ packages:
   kolorist@1.8.0:
     resolution: {integrity: sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==}
 
+  less@4.2.2:
+    resolution: {integrity: sha512-tkuLHQlvWUTeQ3doAqnHbNn8T6WX1KA8yvbKG9x4VtKtIjHsVKQZCH11zRgAfbDAXC2UNIg/K9BYAAcEzUIrNg==}
+    engines: {node: '>=6'}
+    hasBin: true
+
   levn@0.4.1:
     resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
     engines: {node: '>= 0.8.0'}
@@ -3249,6 +3270,10 @@ packages:
   magic-string@0.30.17:
     resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==}
 
+  make-dir@2.1.0:
+    resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==}
+    engines: {node: '>=6'}
+
   map-cache@0.2.2:
     resolution: {integrity: sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==}
     engines: {node: '>=0.10.0'}
@@ -3336,6 +3361,11 @@ packages:
     resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==}
     engines: {node: '>= 0.6'}
 
+  mime@1.6.0:
+    resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==}
+    engines: {node: '>=4'}
+    hasBin: true
+
   mimic-fn@2.1.0:
     resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==}
     engines: {node: '>=6'}
@@ -3407,6 +3437,11 @@ packages:
   natural-compare@1.4.0:
     resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
 
+  needle@3.3.1:
+    resolution: {integrity: sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==}
+    engines: {node: '>= 4.4.x'}
+    hasBin: true
+
   net@1.0.2:
     resolution: {integrity: sha512-kbhcj2SVVR4caaVnGLJKmlk2+f+oLkjqdKeQlmUtz6nGzOpbcobwVIeSURNgraV/v3tlmGIX82OcPCl0K6RbHQ==}
 
@@ -3560,6 +3595,10 @@ packages:
     resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==}
     engines: {node: '>=8'}
 
+  parse-node-version@1.0.1:
+    resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==}
+    engines: {node: '>= 0.10'}
+
   parse-passwd@1.0.0:
     resolution: {integrity: sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==}
     engines: {node: '>=0.10.0'}
@@ -3637,6 +3676,10 @@ packages:
     engines: {node: '>=0.10'}
     hasBin: true
 
+  pify@4.0.1:
+    resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==}
+    engines: {node: '>=6'}
+
   pinia@2.3.1:
     resolution: {integrity: sha512-khUlZSwt9xXCaTbbxFYBKDc/bWAGWJjOgvxETwkTN7KRm66EeT1ZdZj6i2ceh9sP2Pzqsbc704r2yngBrxBVug==}
     peerDependencies:
@@ -3754,6 +3797,9 @@ packages:
   proxy-from-env@1.1.0:
     resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==}
 
+  prr@1.0.1:
+    resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==}
+
   punycode@2.3.1:
     resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
     engines: {node: '>=6'}
@@ -3924,6 +3970,9 @@ packages:
     engines: {node: '>=14.0.0'}
     hasBin: true
 
+  sax@1.4.1:
+    resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==}
+
   scroll-into-view-if-needed@2.2.31:
     resolution: {integrity: sha512-dGCXy99wZQivjmjIqihaBQNjryrz5rueJY7eHfTdyWEiR4ttYpsajb14rn9s5d4DY4EcY6+4+U/maARBXJedkA==}
 
@@ -5191,6 +5240,12 @@ snapshots:
 
   '@ctrl/tinycolor@3.6.1': {}
 
+  '@dagrejs/dagre@1.1.4':
+    dependencies:
+      '@dagrejs/graphlib': 2.2.4
+
+  '@dagrejs/graphlib@2.2.4': {}
+
   '@dual-bundle/import-meta-resolve@4.1.0': {}
 
   '@element-plus/icons-vue@2.3.1(vue@3.5.13(typescript@5.7.3))':
@@ -5652,13 +5707,13 @@ snapshots:
 
   '@ungap/structured-clone@1.3.0': {}
 
-  '@unocss/astro@0.58.9(rollup@4.34.8)(vite@5.4.14(@types/node@20.17.19)(sass@1.77.0)(terser@5.39.0))':
+  '@unocss/astro@0.58.9(rollup@4.34.8)(vite@5.4.14(@types/node@20.17.19)(less@4.2.2)(sass@1.77.0)(terser@5.39.0))':
     dependencies:
       '@unocss/core': 0.58.9
       '@unocss/reset': 0.58.9
-      '@unocss/vite': 0.58.9(rollup@4.34.8)(vite@5.4.14(@types/node@20.17.19)(sass@1.77.0)(terser@5.39.0))
+      '@unocss/vite': 0.58.9(rollup@4.34.8)(vite@5.4.14(@types/node@20.17.19)(less@4.2.2)(sass@1.77.0)(terser@5.39.0))
     optionalDependencies:
-      vite: 5.4.14(@types/node@20.17.19)(sass@1.77.0)(terser@5.39.0)
+      vite: 5.4.14(@types/node@20.17.19)(less@4.2.2)(sass@1.77.0)(terser@5.39.0)
     transitivePeerDependencies:
       - rollup
 
@@ -5789,7 +5844,7 @@ snapshots:
     dependencies:
       '@unocss/core': 0.58.9
 
-  '@unocss/vite@0.58.9(rollup@4.34.8)(vite@5.4.14(@types/node@20.17.19)(sass@1.77.0)(terser@5.39.0))':
+  '@unocss/vite@0.58.9(rollup@4.34.8)(vite@5.4.14(@types/node@20.17.19)(less@4.2.2)(sass@1.77.0)(terser@5.39.0))':
     dependencies:
       '@ampproject/remapping': 2.3.0
       '@rollup/pluginutils': 5.1.4(rollup@4.34.8)
@@ -5801,7 +5856,7 @@ snapshots:
       chokidar: 3.6.0
       fast-glob: 3.3.3
       magic-string: 0.30.17
-      vite: 5.4.14(@types/node@20.17.19)(sass@1.77.0)(terser@5.39.0)
+      vite: 5.4.14(@types/node@20.17.19)(less@4.2.2)(sass@1.77.0)(terser@5.39.0)
     transitivePeerDependencies:
       - rollup
 
@@ -5834,19 +5889,19 @@ snapshots:
       '@uppy/utils': 4.1.3
       nanoid: 3.3.8
 
-  '@vitejs/plugin-vue-jsx@3.1.0(vite@5.4.14(@types/node@20.17.19)(sass@1.77.0)(terser@5.39.0))(vue@3.5.13(typescript@5.7.3))':
+  '@vitejs/plugin-vue-jsx@3.1.0(vite@5.4.14(@types/node@20.17.19)(less@4.2.2)(sass@1.77.0)(terser@5.39.0))(vue@3.5.13(typescript@5.7.3))':
     dependencies:
       '@babel/core': 7.26.9
       '@babel/plugin-transform-typescript': 7.26.8(@babel/core@7.26.9)
       '@vue/babel-plugin-jsx': 1.2.5(@babel/core@7.26.9)
-      vite: 5.4.14(@types/node@20.17.19)(sass@1.77.0)(terser@5.39.0)
+      vite: 5.4.14(@types/node@20.17.19)(less@4.2.2)(sass@1.77.0)(terser@5.39.0)
       vue: 3.5.13(typescript@5.7.3)
     transitivePeerDependencies:
       - supports-color
 
-  '@vitejs/plugin-vue@5.2.1(vite@5.4.14(@types/node@20.17.19)(sass@1.77.0)(terser@5.39.0))(vue@3.5.13(typescript@5.7.3))':
+  '@vitejs/plugin-vue@5.2.1(vite@5.4.14(@types/node@20.17.19)(less@4.2.2)(sass@1.77.0)(terser@5.39.0))(vue@3.5.13(typescript@5.7.3))':
     dependencies:
-      vite: 5.4.14(@types/node@20.17.19)(sass@1.77.0)(terser@5.39.0)
+      vite: 5.4.14(@types/node@20.17.19)(less@4.2.2)(sass@1.77.0)(terser@5.39.0)
       vue: 3.5.13(typescript@5.7.3)
 
   '@volar/language-core@2.4.11':
@@ -6597,6 +6652,10 @@ snapshots:
       depd: 2.0.0
       keygrip: 1.1.0
 
+  copy-anything@2.0.6:
+    dependencies:
+      is-what: 3.14.1
+
   copy-descriptor@0.1.1: {}
 
   cors@2.8.5:
@@ -6942,27 +7001,6 @@ snapshots:
     transitivePeerDependencies:
       - '@vue/composition-api'
 
-  element-plus@2.9.5(vue@3.5.13(typescript@5.7.3)):
-    dependencies:
-      '@ctrl/tinycolor': 3.6.1
-      '@element-plus/icons-vue': 2.3.1(vue@3.5.13(typescript@5.7.3))
-      '@floating-ui/dom': 1.6.13
-      '@popperjs/core': '@sxzz/popperjs-es@2.11.7'
-      '@types/lodash': 4.17.15
-      '@types/lodash-es': 4.17.12
-      '@vueuse/core': 9.13.0(vue@3.5.13(typescript@5.7.3))
-      async-validator: 4.2.5
-      dayjs: 1.11.13
-      escape-html: 1.0.3
-      lodash: 4.17.21
-      lodash-es: 4.17.21
-      lodash-unified: 1.0.3(@types/lodash-es@4.17.12)(lodash-es@4.17.21)(lodash@4.17.21)
-      memoize-one: 6.0.0
-      normalize-wheel-es: 1.2.0
-      vue: 3.5.13(typescript@5.7.3)
-    transitivePeerDependencies:
-      - '@vue/composition-api'
-
   emoji-regex@10.4.0: {}
 
   emoji-regex@8.0.0: {}
@@ -6979,6 +7017,11 @@ snapshots:
 
   environment@1.1.0: {}
 
+  errno@0.1.8:
+    dependencies:
+      prr: 1.0.1
+    optional: true
+
   error-ex@1.3.2:
     dependencies:
       is-arrayish: 0.2.1
@@ -7743,6 +7786,11 @@ snapshots:
     dependencies:
       safer-buffer: 2.1.2
 
+  iconv-lite@0.6.3:
+    dependencies:
+      safer-buffer: 2.1.2
+    optional: true
+
   ieee754@1.2.1: {}
 
   ignore@5.3.2: {}
@@ -7987,6 +8035,8 @@ snapshots:
       call-bound: 1.0.3
       get-intrinsic: 1.2.7
 
+  is-what@3.14.1: {}
+
   is-windows@1.0.2: {}
 
   isarray@1.0.0: {}
@@ -8042,20 +8092,6 @@ snapshots:
 
   jsonparse@1.3.1: {}
 
-  jxq-ui@0.0.4(@types/sortablejs@1.15.8)(typescript@5.7.3):
-    dependencies:
-      '@element-plus/icons-vue': 2.3.1(vue@3.5.13(typescript@5.7.3))
-      '@vue-flow/background': 1.3.2(@vue-flow/core@1.42.1(vue@3.5.13(typescript@5.7.3)))(vue@3.5.13(typescript@5.7.3))
-      '@vue-flow/core': 1.42.1(vue@3.5.13(typescript@5.7.3))
-      element-plus: 2.9.5(vue@3.5.13(typescript@5.7.3))
-      uuid: 11.1.0
-      vue: 3.5.13(typescript@5.7.3)
-      vue-draggable-plus: 0.6.0(@types/sortablejs@1.15.8)
-    transitivePeerDependencies:
-      - '@types/sortablejs'
-      - '@vue/composition-api'
-      - typescript
-
   keygrip@1.1.0:
     dependencies:
       tsscmp: 1.0.6
@@ -8084,6 +8120,20 @@ snapshots:
 
   kolorist@1.8.0: {}
 
+  less@4.2.2:
+    dependencies:
+      copy-anything: 2.0.6
+      parse-node-version: 1.0.1
+      tslib: 2.8.1
+    optionalDependencies:
+      errno: 0.1.8
+      graceful-fs: 4.2.11
+      image-size: 0.5.5
+      make-dir: 2.1.0
+      mime: 1.6.0
+      needle: 3.3.1
+      source-map: 0.6.1
+
   levn@0.4.1:
     dependencies:
       prelude-ls: 1.2.1
@@ -8216,6 +8266,12 @@ snapshots:
     dependencies:
       '@jridgewell/sourcemap-codec': 1.5.0
 
+  make-dir@2.1.0:
+    dependencies:
+      pify: 4.0.1
+      semver: 5.7.2
+    optional: true
+
   map-cache@0.2.2: {}
 
   map-obj@1.0.1: {}
@@ -8303,6 +8359,9 @@ snapshots:
     dependencies:
       mime-db: 1.52.0
 
+  mime@1.6.0:
+    optional: true
+
   mimic-fn@2.1.0: {}
 
   mimic-fn@4.0.0: {}
@@ -8373,6 +8432,12 @@ snapshots:
 
   natural-compare@1.4.0: {}
 
+  needle@3.3.1:
+    dependencies:
+      iconv-lite: 0.6.3
+      sax: 1.4.1
+    optional: true
+
   net@1.0.2: {}
 
   next-tick@1.1.0: {}
@@ -8549,6 +8614,8 @@ snapshots:
       json-parse-even-better-errors: 2.3.1
       lines-and-columns: 1.2.4
 
+  parse-node-version@1.0.1: {}
+
   parse-passwd@1.0.0: {}
 
   pascalcase@0.1.1: {}
@@ -8594,6 +8661,9 @@ snapshots:
 
   pidtree@0.6.0: {}
 
+  pify@4.0.1:
+    optional: true
+
   pinia@2.3.1(typescript@5.7.3)(vue@3.5.13(typescript@5.7.3)):
     dependencies:
       '@vue/devtools-api': 6.6.4
@@ -8707,6 +8777,9 @@ snapshots:
 
   proxy-from-env@1.1.0: {}
 
+  prr@1.0.1:
+    optional: true
+
   punycode@2.3.1: {}
 
   qs@6.14.0:
@@ -8905,6 +8978,9 @@ snapshots:
       immutable: 4.3.7
       source-map-js: 1.2.1
 
+  sax@1.4.1:
+    optional: true
+
   scroll-into-view-if-needed@2.2.31:
     dependencies:
       compute-scroll-into-view: 1.0.20
@@ -9560,9 +9636,9 @@ snapshots:
 
   universalify@2.0.1: {}
 
-  unocss@0.58.9(postcss@8.5.2)(rollup@4.34.8)(vite@5.4.14(@types/node@20.17.19)(sass@1.77.0)(terser@5.39.0)):
+  unocss@0.58.9(postcss@8.5.2)(rollup@4.34.8)(vite@5.4.14(@types/node@20.17.19)(less@4.2.2)(sass@1.77.0)(terser@5.39.0)):
     dependencies:
-      '@unocss/astro': 0.58.9(rollup@4.34.8)(vite@5.4.14(@types/node@20.17.19)(sass@1.77.0)(terser@5.39.0))
+      '@unocss/astro': 0.58.9(rollup@4.34.8)(vite@5.4.14(@types/node@20.17.19)(less@4.2.2)(sass@1.77.0)(terser@5.39.0))
       '@unocss/cli': 0.58.9(rollup@4.34.8)
       '@unocss/core': 0.58.9
       '@unocss/extractor-arbitrary-variants': 0.58.9
@@ -9581,9 +9657,9 @@ snapshots:
       '@unocss/transformer-compile-class': 0.58.9
       '@unocss/transformer-directives': 0.58.9
       '@unocss/transformer-variant-group': 0.58.9
-      '@unocss/vite': 0.58.9(rollup@4.34.8)(vite@5.4.14(@types/node@20.17.19)(sass@1.77.0)(terser@5.39.0))
+      '@unocss/vite': 0.58.9(rollup@4.34.8)(vite@5.4.14(@types/node@20.17.19)(less@4.2.2)(sass@1.77.0)(terser@5.39.0))
     optionalDependencies:
-      vite: 5.4.14(@types/node@20.17.19)(sass@1.77.0)(terser@5.39.0)
+      vite: 5.4.14(@types/node@20.17.19)(less@4.2.2)(sass@1.77.0)(terser@5.39.0)
     transitivePeerDependencies:
       - postcss
       - rollup
@@ -9684,7 +9760,7 @@ snapshots:
 
   vary@1.1.2: {}
 
-  vite-plugin-mock-dev-server@1.8.4(bufferutil@4.0.9)(esbuild@0.21.5)(rollup@4.34.8)(utf-8-validate@5.0.10)(vite@5.4.14(@types/node@20.17.19)(sass@1.77.0)(terser@5.39.0)):
+  vite-plugin-mock-dev-server@1.8.4(bufferutil@4.0.9)(esbuild@0.21.5)(rollup@4.34.8)(utf-8-validate@5.0.10)(vite@5.4.14(@types/node@20.17.19)(less@4.2.2)(sass@1.77.0)(terser@5.39.0)):
     dependencies:
       '@pengzhanbo/utils': 1.2.0
       '@rollup/pluginutils': 5.1.4(rollup@4.34.8)
@@ -9701,7 +9777,7 @@ snapshots:
       mime-types: 2.1.35
       path-to-regexp: 6.3.0
       picocolors: 1.1.1
-      vite: 5.4.14(@types/node@20.17.19)(sass@1.77.0)(terser@5.39.0)
+      vite: 5.4.14(@types/node@20.17.19)(less@4.2.2)(sass@1.77.0)(terser@5.39.0)
       ws: 8.18.0(bufferutil@4.0.9)(utf-8-validate@5.0.10)
     optionalDependencies:
       esbuild: 0.21.5
@@ -9711,7 +9787,7 @@ snapshots:
       - supports-color
       - utf-8-validate
 
-  vite-plugin-svg-icons@2.0.1(vite@5.4.14(@types/node@20.17.19)(sass@1.77.0)(terser@5.39.0)):
+  vite-plugin-svg-icons@2.0.1(vite@5.4.14(@types/node@20.17.19)(less@4.2.2)(sass@1.77.0)(terser@5.39.0)):
     dependencies:
       '@types/svgo': 2.6.4
       cors: 2.8.5
@@ -9721,11 +9797,11 @@ snapshots:
       pathe: 0.2.0
       svg-baker: 1.7.0
       svgo: 2.8.0
-      vite: 5.4.14(@types/node@20.17.19)(sass@1.77.0)(terser@5.39.0)
+      vite: 5.4.14(@types/node@20.17.19)(less@4.2.2)(sass@1.77.0)(terser@5.39.0)
     transitivePeerDependencies:
       - supports-color
 
-  vite@5.4.14(@types/node@20.17.19)(sass@1.77.0)(terser@5.39.0):
+  vite@5.4.14(@types/node@20.17.19)(less@4.2.2)(sass@1.77.0)(terser@5.39.0):
     dependencies:
       esbuild: 0.21.5
       postcss: 8.5.2
@@ -9733,6 +9809,7 @@ snapshots:
     optionalDependencies:
       '@types/node': 20.17.19
       fsevents: 2.3.3
+      less: 4.2.2
       sass: 1.77.0
       terser: 5.39.0
 

+ 14 - 0
src/components/hjflow/index.ts

@@ -0,0 +1,14 @@
+// 导入组件,组件必须声明 name
+// import JXQFlow from "./src/flow.vue";
+import { App} from "vue";
+
+import HJFlow from './src/hjflow/index.vue'
+
+
+HJFlow.install = function (app: App) {
+    // app.component(JXQFlow.name!, JXQFlow)
+    app.component(HJFlow.name!, HJFlow)
+    return app
+}
+
+export default HJFlow

+ 18 - 0
src/components/hjflow/src/Handle/CommonHandle.vue

@@ -0,0 +1,18 @@
+<script setup lang="ts"></script>
+
+<template>
+  <div class="common-handle" ></div>
+</template>
+
+<style scoped lang="less">
+//定义一个中空的样式
+.common-handle {
+  position: absolute;
+  width: 10px;
+  height: 10px;
+  border-radius: 50%;
+  background-color: #fff;
+  border: 1px solid #ccc;
+  cursor: pointer;
+}
+</style>

+ 13 - 0
src/components/hjflow/src/background/DropzoneBackground.vue

@@ -0,0 +1,13 @@
+<script lang="ts" setup>
+import { Background } from '@vue-flow/background'
+</script>
+
+<template>
+  <div class="dropzone-background">
+    <Background :size="2" :gap="20" pattern-color="#BDBDBD" />
+
+    <div class="overlay">
+      <slot />
+    </div>
+  </div>
+</template>

+ 59 - 0
src/components/hjflow/src/edges/commonEdge.vue

@@ -0,0 +1,59 @@
+<template>
+  <g>
+    <!-- 圆形markStart -->
+    <circle
+        :cx="props.sourceX"
+        :cy="props.sourceY - 4"
+        r="4"
+        :fill="status ? 'gray' : 'black'"
+    />
+    <!-- path -->
+    <path
+        :d="path"
+        fill="none"
+        :stroke="selectLine?.id === id ? 'blue' : 'gray'"
+        stroke-width="3"
+    />
+    <!-- 箭头markEnd -->
+    <path
+        :d="`M ${targetX} ${targetY + 2} L ${targetX - 5} ${targetY - 10} L ${targetX + 5} ${targetY - 10} Z`"
+        :fill="selectLine?.id === id ? 'blue' : 'gray'"
+        stroke="none"
+    />
+  </g>
+</template>
+
+<script lang="ts" setup>
+import { getSmoothStepPath, SmoothStepEdgeProps } from "@vue-flow/core";
+import { computed, inject, ref, watch } from "vue";
+import {CurrentSelectedEdgeProvideName} from "../types/comTypes";
+
+const selectLine = inject(CurrentSelectedEdgeProvideName);
+const status = ref(true);
+const props = defineProps<SmoothStepEdgeProps>();
+const path = computed(() => getSmoothStepPath(props)[0]);
+
+
+
+const getArrowTransform = (props: SmoothStepEdgeProps, hhh, dd ) => {
+  console.log(props, 'getArrowTransform');
+  const { targetPosition } = props;
+  if (targetPosition === "top") {
+    return `rotate(0 ${props.targetX} ${props.targetY})`;
+  }
+  if (targetPosition === "bottom") {
+    return `rotate(180 ${props.targetX} ${props.targetY})`;
+  }
+  if (targetPosition === "left") {
+    return `rotate(-90 ${props.targetX} ${props.targetY})`;
+  }
+  if (targetPosition === "right") {
+    return `rotate(90 ${props.targetX} ${props.targetY})`;
+  }
+}
+</script>
+<script lang="ts">
+export default {
+  name: "Custom",
+};
+</script>

+ 97 - 0
src/components/hjflow/src/flow.vue

@@ -0,0 +1,97 @@
+<script setup lang="ts">
+import {ref, onMounted, watch} from 'vue';
+
+import type {Node, Edge} from '@vue-flow/core';
+import {HJMethodName, HJNodeData} from './types/comTypes';
+import DragData from "../src/leftDrags/test/dragData.vue"
+import WorkFlowDrag from '../src/leftDrags/workflow/dragData.vue'
+import Infomation from './information/index.vue'
+import {CircleCloseFilled} from '@element-plus/icons-vue'
+
+import {initialEdges, initialNodes} from './utils/index'
+
+import HJFlow from './hjflow/index.vue'
+
+defineOptions({
+  name: 'JXQFlow'
+})
+
+const nodes = ref<HJNodeData[]>([]);
+const edges = ref<Edge[]>([]);
+
+
+onMounted(() => {
+  // nodes.value = initialNodes;
+  // edges.value = initialEdges;
+});
+
+watch(nodes, (newVal, oldVal) => {
+  console.log('nodes change', newVal, oldVal);
+});
+
+watch(edges, (newVal, oldVal) => {
+  console.log('edges change', newVal, oldVal);
+});
+
+// vue 的相关操作
+const onNodeOperation = (name: HJMethodName, node: HJNodeData) : void => {
+  console.log('onNodeOperation', name, node);
+}
+
+// ======= 信息展示 =======
+const infoVisible = ref(false)
+const doubleCNode = ref<HJNodeData | null>(null) //双击的节点数据
+const closeInfo = () => {
+  infoVisible.value = false
+  doubleCNode.value = null
+}
+</script>
+
+<template>
+
+  <div v-if="nodes.length > 0"> {{ nodes[0]?.jsonData ?? "==" }}</div>
+  <div class="dnd-flow out-box">
+    <WorkFlowDrag></WorkFlowDrag>
+    <HJFlow v-model:nodes-data="nodes" v-model:edges-data="edges" @hjMethod="onNodeOperation"></HJFlow>
+    <!--    双击node节点展示的弹窗-->
+    <el-drawer v-model="infoVisible" :show-close="false" :destroy-on-close="true" :close-on-click-modal="false"
+               :append-to-body="true">
+      <template #header="{ close, titleId, titleClass }">
+        <h4 :id="titleId" :class="titleClass">编辑信息</h4>
+        <el-button type="danger" @click="closeInfo">
+          <el-icon class="el-icon--left">
+            <CircleCloseFilled/>
+          </el-icon>
+          Close
+        </el-button>
+      </template>
+
+      <Infomation></Infomation>
+    </el-drawer>
+  </div>
+
+</template>
+
+<style scoped lang="less">
+
+.out-box {
+  width: 100%;
+  height: 80vh;
+  border: 1px solid #ccc;
+  padding: 10px;
+  display: flex;
+}
+
+.vue-flow-box {
+  flex: 1;
+  flex-shrink: 0;
+  height: 100%;
+  border: 3px solid red;
+}
+</style>
+
+<!--<style>-->
+<!--.vue-flow__node {-->
+<!--  display: inline-block !important;-->
+<!--}-->
+<!--</style>-->

+ 191 - 0
src/components/hjflow/src/hjflow/index.vue

@@ -0,0 +1,191 @@
+<script setup lang="ts">
+import {ref, onMounted, InjectionKey, Ref, provide, inject, nextTick, markRaw} from 'vue';
+import {
+  HJMethodName,
+  HJNodeData,
+  HJNodeType,
+  nodeTypes,
+  edgeTypes,
+  HJMethodProvideName,
+  CurrentHeaderOperationProvideName, CurrentSelectedEdgeProvideName, HJFlowProps
+} from "../types/comTypes";
+import Panel from "../panel/index.vue";
+import CommonEdge from '../edges/commonEdge.vue'
+import DropzoneBackground from "../background/DropzoneBackground.vue";
+import {useVueFlow, VueFlow, Edge} from "@vue-flow/core";
+import useDragAndDrop from "../hooks/useDnD";
+import {useLayout} from '../utils/useLayout'
+import {useSnakeLayoutHook} from "../utils/useSnake";
+import {ElMessage} from "element-plus";
+
+
+defineOptions({
+  name: 'HJFlow'
+})
+
+const {onConnect, addEdges, fitView, updateNode, findNode} = useVueFlow()
+const {onDragOver, onDrop, onDragLeave, isDragOver} = useDragAndDrop()
+
+onConnect(addEdges)
+
+
+// ========== 外面传进来的参数  ===========
+const nodes = defineModel<HJNodeData[]>('nodesData')
+const edges = defineModel('edgesData')
+const props = defineProps<HJFlowProps>()
+
+
+// ==========vueflow 本身的方法操作 ===========
+const doubleClick = ({node}) => {
+  // 如果通过双击弹出额外信息编辑是可以拿到jsonData的,但是如果在自定义node中是拿不到的,非要在自定义中拿就得把额外的信息放到data中。
+  // 还要看 是否要根据 dragData中的数据生成具体 avue页面。 之后可以尝试通过useVueFlow的find能否找到
+  console.log('doubleClick', node);
+}
+
+const currentClickEdgeData = ref<Edge | null>(null)
+provide(CurrentSelectedEdgeProvideName, currentClickEdgeData)
+const onClickEdge = ({edge}) => {
+  currentClickEdgeData.value = JSON.parse(JSON.stringify(edge))
+}
+
+
+
+
+// ======= Emits =======
+const emits = defineEmits<{
+  hjMethod: [name: HJMethodName, node: HJNodeData]
+}>()
+
+// 提供一个孙子组件里面(比如自定义node里面) 调用本组件的方法
+// ======= Node操作 =======
+const currentHeaderOperationNodeData = ref<HJNodeData | null >(null) // 当前选中的header操作节点
+provide(CurrentHeaderOperationProvideName, currentHeaderOperationNodeData)
+provide(HJMethodProvideName, ( name: HJMethodName,node: HJNodeData) => {
+  currentHeaderOperationNodeData.value = JSON.parse(JSON.stringify(node)) // 复制一份数据
+  console.log('provide', name, node);
+  emits('hjMethod', name, node)
+})
+
+
+
+///// === 数据历史纪录 用于回退或者重置按钮 === /////
+let originData: any = null
+let historyList: any[]  =  []
+
+
+///// === 所有的node 和edge change 调用的地方 === /////
+const onFlowNodesChange = (data: any) => {
+  if (
+      data.length > 0 &&
+      data[0].type &&
+      (data[0].type === "add" || data[0].type === "remove")
+  ) {
+
+    historyList.unshift(JSON.stringify({nodes: markRaw(nodes.value), edges: markRaw(edges.value)}))
+    console.log('onFlowNodesChange historyList', historyList)
+  }
+}
+const onFlowEdgesChange = (data: any) => {
+  if (
+      data.length > 0 &&
+      data[0].type &&
+      (data[0].type === "add" || data[0].type === "remove")
+  ) {
+    historyList.unshift(JSON.stringify({nodes: markRaw(nodes.value), edges: markRaw(edges.value)}))
+  }
+}
+///// === 所有的node 和edge change 调用的地方 === /////
+
+///// === 初始化   === /////
+const initFlow = (nodes: HJNodeData[], edges: any[]) => {
+  originData = JSON.stringify({nodes, edges}) // 复制一份数据
+  historyList = [] // 初始化历史纪录
+}
+
+// 为父组件提供更新node中的data数据的操作 一般只更新information就行了 更新一个node的值
+const updateNodeData = (node: HJNodeData) => {
+  console.log('updateNodeData', node)
+
+  if (node.id) {
+    const findNodeData = findNode(node.id) as HJNodeData
+    findNodeData.data.information = node.data.information
+    console.log('findNodeData', findNodeData)
+    // updateNode(node.id, node)
+  }
+}
+
+defineExpose({
+  updateNodeData,
+  initFlow
+})
+
+// ======= Panel =======
+// 测试layout  点击切换布局时候的操作
+const {layout} = useLayout()
+
+async function layoutGraph(direction: 'LR' | 'TB') {
+  nodes.value = layout(nodes.value, edges.value, direction)
+
+  nextTick(() => {
+    fitView()
+  })
+}
+
+const pannelOperations = {
+  addNode: (nodeType: HJNodeType) => {
+    console.log('addNode', nodeType);
+  },
+  layout: (direction: 'LR' | 'TB') => {
+    layoutGraph(direction)
+  },
+  toSnake: () => {
+    console.log('toSnake');
+    nodes.value = useSnakeLayoutHook(nodes.value, edges.value)
+  },
+  reset: () => {
+    console.log('reset');
+    if (originData && JSON.parse(originData)) {
+      let data = JSON.parse(originData)
+      nodes.value = data.nodes;
+      edges.value = data.edges;
+      historyList = []
+    }
+  },
+  back: () => {
+    console.log('back');
+    let firstHistory = historyList.shift();
+    if (firstHistory) {
+      let historyData = JSON.parse(firstHistory)
+      nodes.value = historyData.nodes;
+      edges.value = historyData.edges;
+    } else {
+      ElMessage.warning("已是初始线路");
+    }
+  }
+}
+// ======= Panel ======= ↑↑↑↑↑↑↑↑↑↑↑↑↑↑
+
+</script>
+
+<template>
+  <VueFlow  v-model:nodes="nodes" v-model:edges="edges" :node-types="nodeTypes" :edgeTypes="edgeTypes"
+           @dragover="onDragOver" @dragleave="onDragLeave" @drop="onDrop" @nodeDoubleClick="doubleClick" @edge-click="onClickEdge" @nodes-change="onFlowNodesChange" @edges-change="onFlowEdgesChange">
+    <DropzoneBackground
+        :style="{
+            backgroundColor: isDragOver ? '#e7f3ff' : 'transparent',
+            transition: 'background-color 0.2s ease',
+          }"
+    >
+<!--      <p v-if="isDragOver">Drop here</p>-->
+    </DropzoneBackground>
+
+    <!--        右面上工具栏操作 -->
+    <Panel v-if="showPanel" v-on="pannelOperations" :fun-names="panelBtns"></Panel>
+
+
+  </VueFlow>
+</template>
+
+<style scoped lang="less">
+
+</style>

+ 145 - 0
src/components/hjflow/src/hooks/useDnD.ts

@@ -0,0 +1,145 @@
+import {Position, useVueFlow} from '@vue-flow/core'
+import { ref, watch } from 'vue'
+import type { Node, Edge } from '@vue-flow/core';
+import {HJInterNodeData, HJPosition} from "../types/comTypes";
+
+import { v4 as uuidv4 } from "uuid";
+
+/**
+ * @returns {string} - A unique id.
+ */
+function getId(): string {
+    return uuidv4();
+}
+
+/**
+ * In a real world scenario you'd want to avoid creating refs in a global scope like this as they might not be cleaned up properly.
+ * @type {{draggedType: Ref<string|null>, isDragOver: Ref<boolean>, isDragging: Ref<boolean>}}
+ */
+const state = {
+    /**
+     * The type of the node being dragged.
+     */
+    dragData: ref<HJInterNodeData | null>(null),
+    draggedType: ref<string | null>(null),
+    isDragOver: ref(false),
+    isDragging: ref(false),
+}
+
+export default function useDragAndDrop() {
+    const { draggedType, isDragOver, isDragging , dragData} = state
+
+    const { addNodes, screenToFlowCoordinate, onNodesInitialized, updateNode } = useVueFlow()
+
+    watch(isDragging, (dragging) => {
+        document.body.style.userSelect = dragging ? 'none' : ''
+    })
+
+    // 第二个类型是HJInterNodeData
+    function onDragStart(event: DragEvent, data: HJInterNodeData) {
+        if (event.dataTransfer) {
+            event.dataTransfer.setData('application/vueflow', data.type!)
+            event.dataTransfer.effectAllowed = 'move'
+        }
+
+        draggedType.value = data.type!
+        dragData.value = data
+        isDragging.value = true
+
+        document.addEventListener('drop', onDragEnd)
+    }
+
+    /**
+     * Handles the drag over event.
+     *
+     * @param {DragEvent} event
+     */
+    function onDragOver(event: DragEvent) {
+        event.preventDefault()
+
+        if (draggedType.value) {
+            isDragOver.value = true
+
+            if (event.dataTransfer) {
+                event.dataTransfer.dropEffect = 'move'
+            }
+        }
+    }
+
+    function onDragLeave() {
+        isDragOver.value = false
+    }
+
+    function onDragEnd() {
+        isDragging.value = false
+        isDragOver.value = false
+        draggedType.value = null
+        dragData.value = null
+        document.removeEventListener('drop', onDragEnd)
+    }
+
+    /**
+     * Handles the drop event.
+     *
+     * @param {DragEvent} event
+     */
+    function onDrop(event: DragEvent) {
+        const position = screenToFlowCoordinate({
+            x: event.clientX,
+            y: event.clientY,
+        })
+
+        const nodeId = getId()
+
+        const newNode: Node = {
+            id: nodeId,
+            position,
+            data: dragData.value!.data,
+            type: dragData.value!.type,
+            targetPosition: Position.Top,
+            sourcePosition:  Position.Bottom,
+        }
+
+        console.log("从左侧拖拽的添加操作",newNode)
+        // 如果没有给node的data设置handles属性,这默认上下结构
+        if (!newNode.data.handles) {
+            newNode.data.handles = [
+                {
+                    type: 'source',
+                    position: HJPosition.Bottom,
+
+                },
+                {
+                    type: 'target',
+                    position: HJPosition.Top,
+                }
+            ]
+        }
+
+
+        /**
+         * Align node position after drop, so it's centered to the mouse
+         *
+         * We can hook into events even in a callback, and we can remove the event listener after it's been called.
+         */
+        const { off } = onNodesInitialized(() => {
+            updateNode(nodeId, (node) => ({
+                position: { x: node.position.x - 100, y: node.position.y - node.dimensions.height / 2 },
+            }))
+
+            off()
+        })
+
+        addNodes(newNode)
+    }
+
+    return {
+        draggedType,
+        isDragOver,
+        isDragging,
+        onDragStart,
+        onDragLeave,
+        onDragOver,
+        onDrop,
+    }
+}

+ 0 - 0
src/components/hjflow/src/hooks/useFlow.ts


+ 11 - 0
src/components/hjflow/src/information/index.vue

@@ -0,0 +1,11 @@
+<script setup lang="ts">
+
+</script>
+
+<template>
+  <div>这里是额外信息,需要具体拟定需求之后在决定怎么写</div>
+</template>
+
+<style scoped lang="less">
+
+</style>

+ 167 - 0
src/components/hjflow/src/leftDrags/test/configs.ts

@@ -0,0 +1,167 @@
+import {HJInterNodeData, HJPosition} from "../../types/comTypes";
+import { ref } from 'vue'
+import { HJNodeType} from '../../types/comTypes'
+
+
+export const dragNodes = ref<HJInterNodeData[]>(
+    [
+        {
+            type: HJNodeType.custom,
+            data: {
+                label: "普通上1下1",
+                handles: [
+                    {
+                        type: 'source',
+                        position: HJPosition.Bottom,
+
+                    },
+                    {
+                        type: 'target',
+                        position: HJPosition.Top,
+                    }
+                ]
+            }
+        },
+        {
+            type: HJNodeType.nest,
+            data: {
+                label: "左2 右1",
+                handles: [
+                    {
+                        type: 'source',
+                        position: HJPosition.Left,
+                        style: {
+                            top: '10px'
+                        }
+                    },
+                    {
+                        type: 'source',
+                        position: HJPosition.Left,
+                        style: {
+                            top: 'auto',
+                            bottom: '10px'
+                        }
+                    },
+                    {
+                        type: 'target',
+                        position: HJPosition.Right,
+                    }
+                ]
+            }
+        },
+        {
+            type: HJNodeType.custom,
+            data: {
+                label: "上2 下3",
+                handles: [
+                    {
+                        type: 'source',
+                        position: HJPosition.Top,
+                        style: {
+                            left: '10px'
+                        }
+                    },
+                    {
+                        type: 'source',
+                        position: HJPosition.Top,
+                        style: {
+                            left: 'auto',
+                            right: '10px'
+                        }
+                    },
+                    {
+                        type: 'target',
+                        position: HJPosition.Bottom,
+                        style: {
+
+                             left: '10px'
+                        }
+                    },
+                    {
+                        type: 'target',
+                        position: HJPosition.Bottom,
+                        style: {
+                            left: '50%'
+                        }
+                    },
+                    {
+                        type: 'target',
+                        position: HJPosition.Bottom,
+                        style: {
+                            left: 'auto',
+                             right: '10px'
+                        }
+                    }
+
+
+                ]
+            }
+        },
+        {
+            type: HJNodeType.nest,
+            data: {
+                label: "左三右二",
+                handles: [
+                    {
+                        type: 'source',
+                        position: HJPosition.Left,
+                        style: {
+                            top: '10px'
+                        }
+                    },
+                    {
+                        type: 'source',
+                        position: HJPosition.Left,
+                        style: {
+                            top: 'auto',
+                            bottom: '10px'
+                        }
+                    },
+                    {
+                        type: 'source',
+                        position: HJPosition.Left,
+                        style: {
+                            top: '50%',
+                            // bottom: '10px'
+                        }
+                    },
+                    {
+                        type: 'target',
+                        position: HJPosition.Right,
+                        style: {
+                            top: '10px'
+                        }
+                    },
+                    {
+                        type: 'target',
+                        position: HJPosition.Right,
+                        style: {
+                            top: '50%',
+                            // bottom: '10px'
+                        }
+                    }
+
+
+                ]
+            }
+        },
+        {
+            type: HJNodeType.numberInput,
+            data: {
+                label: "Node 3",
+            }
+        },
+        {
+            type: HJNodeType.numberFunction,
+            data: {
+                label: "Node 4",
+            }
+        },
+        {
+            type: HJNodeType.numberResult,
+            data: {
+                label: "Node 5",
+            }
+        },
+    ]
+)

+ 41 - 0
src/components/hjflow/src/leftDrags/test/dragData.vue

@@ -0,0 +1,41 @@
+<script setup lang="ts">
+
+import {dragNodes} from "./configs";
+import { VueDraggable } from 'vue-draggable-plus'
+
+import useDragAndDrop from '../../hooks/useDnD'
+
+const { onDragStart } = useDragAndDrop()
+</script>
+
+<template>
+<div class="drag-container" >
+  <div class="drag-item" v-for="(dragData, index) in dragNodes" :key="index" v-text="dragData.data.label" :draggable="true" @dragstart="onDragStart($event, dragData)" />
+
+</div>
+</template>
+
+<style scoped lang="less">
+.drag-container {
+  display: flex;
+  flex-direction: column;
+  justify-content: start;
+  align-items: center;
+  height: 100%;
+  width: 200px;
+  background: #ccc;
+  padding: 8px 10px;
+  .drag-item {
+    background: #fff;
+    width: 100%;
+    font-size: 14px;
+    text-align: center;
+    height: 20px;
+    line-height: 20px;
+    border-radius: 4px;
+    border: 1px solid rgba(204, 204, 204, 0.9);
+    margin-bottom: 10px;
+    cursor: pointer;
+  }
+}
+</style>

+ 47 - 0
src/components/hjflow/src/leftDrags/workflow/configs.ts

@@ -0,0 +1,47 @@
+import {HJInterNodeData, HJPosition} from "../../types/comTypes";
+import { ref } from 'vue'
+import { HJNodeType} from '../../types/comTypes'
+
+
+export const dragNodes = ref<HJInterNodeData[]>(
+    [
+        {
+            type: HJNodeType.workFlow,
+            data: {
+                label: "发起人",
+                // handles: [
+                //     {
+                //         type: 'source',
+                //         position: HJPosition.Bottom,
+                //
+                //     },
+                //     {
+                //         type: 'target',
+                //         position: HJPosition.Top,
+                //     }
+                // ]
+            }
+        },
+        {
+            type: HJNodeType.workFlow,
+            data: {
+                label: "审核人",
+            }
+        },
+        {
+            type: HJNodeType.workFlow,
+            data: {
+                label: "抄送人",
+            }
+        },
+        {
+            type: HJNodeType.universal,
+            data: {
+                label: "测试系统",
+                information: {
+                    configName: 'ddd'
+                }
+            }
+        },
+    ]
+)

+ 41 - 0
src/components/hjflow/src/leftDrags/workflow/dragData.vue

@@ -0,0 +1,41 @@
+<script setup lang="ts">
+
+import {dragNodes} from "./configs";
+import { VueDraggable } from 'vue-draggable-plus'
+
+import useDragAndDrop from '../../hooks/useDnD'
+
+const { onDragStart } = useDragAndDrop()
+</script>
+
+<template>
+<div class="drag-container" >
+  <div class="drag-item" v-for="(dragData, index) in dragNodes" :key="index" v-text="dragData.data.label" :draggable="true" @dragstart="onDragStart($event, dragData)" />
+
+</div>
+</template>
+
+<style scoped lang="less">
+.drag-container {
+  display: flex;
+  flex-direction: column;
+  justify-content: start;
+  align-items: center;
+  height: 100%;
+  width: 150px;
+  background: #ccc;
+  padding: 8px 10px;
+  .drag-item {
+    background: #fff;
+    width: 100%;
+    font-size: 14px;
+    text-align: center;
+    height: 20px;
+    line-height: 20px;
+    border-radius: 4px;
+    border: 1px solid rgba(204, 204, 204, 0.9);
+    margin-bottom: 10px;
+    cursor: pointer;
+  }
+}
+</style>

+ 11 - 0
src/components/hjflow/src/leftDrags/workflow/editInfo.vue

@@ -0,0 +1,11 @@
+<script setup lang="ts">
+
+</script>
+
+<template>
+
+</template>
+
+<style scoped lang="less">
+
+</style>

+ 24 - 0
src/components/hjflow/src/nodes/CommonNode.vue

@@ -0,0 +1,24 @@
+<script setup lang="ts">
+import type { Node } from '@vue-flow/core';
+import { onMounted } from 'vue';
+import { HJInterNodeData, HJNodeData } from '../types/comTypes';
+import Basic from './com/basic.vue';
+
+// 即使外部定义的类型数据再多,传进来的也是Node类型。
+const props = defineProps<HJInterNodeData>();
+
+onMounted(() => {
+  console.log('mounted', props);
+});
+</script>
+
+<template>
+    <Basic v-bind="props" >
+      default-4
+      <template #header>
+        header-1
+      </template>
+    </Basic>
+</template>
+
+<style scoped lang="less"></style>

+ 18 - 0
src/components/hjflow/src/nodes/NestNode.vue

@@ -0,0 +1,18 @@
+<script setup lang="ts">
+import type { Node } from '@vue-flow/core';
+import { onMounted } from 'vue';
+import { HJInterNodeData, HJNodeData } from '../types/comTypes';
+import Basic from './com/basic.vue';
+
+const props = defineProps<HJInterNodeData>();
+
+onMounted(() => {
+  console.log('mounted', props);
+});
+</script>
+
+<template>
+  <Basic v-bind="props"> </Basic>
+</template>
+
+<style scoped lang="less"></style>

+ 7 - 0
src/components/hjflow/src/nodes/NumberFunNode.vue

@@ -0,0 +1,7 @@
+<script setup lang="ts"></script>
+
+<template>
+  <div>NumberFunNode.vue</div>
+</template>
+
+<style scoped lang="less"></style>

+ 7 - 0
src/components/hjflow/src/nodes/NumberInputNode.vue

@@ -0,0 +1,7 @@
+<script setup lang="ts"></script>
+
+<template>
+  <div>number input node</div>
+</template>
+
+<style scoped lang="less"></style>

+ 7 - 0
src/components/hjflow/src/nodes/NumberResultNode.vue

@@ -0,0 +1,7 @@
+<script setup lang="ts"></script>
+
+<template>
+  <div>NumberResultNode.vue</div>
+</template>
+
+<style scoped lang="less"></style>

+ 94 - 0
src/components/hjflow/src/nodes/com/basic.vue

@@ -0,0 +1,94 @@
+<script setup lang="ts">
+import {
+  CurrentHeaderOperationProvideName,
+  GrandparentMethod,
+  HJInterNodeData,
+  HJMethodName, HJNodeData,
+  HJPosition
+} from '../../types/comTypes';
+import {Handle} from '@vue-flow/core'
+import {Position} from '@vue-flow/core';
+import {inject, watch} from "vue";
+const props = defineProps<HJInterNodeData>();
+
+//  'basic-box--selected': props.data.isSelected || (currentHeaderOperationNode && currentHeaderOperationNode.id === props.id) 如果是选中或者正在通过header编辑节点信息的样式
+const currentHeaderOperationNode = inject<HJNodeData | null>(CurrentHeaderOperationProvideName)
+
+
+</script>
+
+<template>
+  <div
+      class="basic-box"
+      :class="{
+      [`basic-box--${props.type}`]: props.type,
+      'basic-box--selected': props.data.isSelected || (currentHeaderOperationNode && currentHeaderOperationNode.id === props.id),
+      'basic-box--dragging': props.data.isDragging,
+    }"
+  >
+
+    <slot name="header"></slot>
+
+    <slot name="default">
+    </slot>
+
+    <!--    这里是handle的操作-->
+    <div v-if="props.sourcePosition && props.targetPosition">
+      <Handle class="handle-style"  type="source" :position="props.sourcePosition"/>
+      <Handle class="handle-style"  type="target" :position="props.targetPosition"/>
+    </div>
+    <div v-else-if="props.data.handles && props.data.handles.length">
+      <Handle class="handle-style" v-for="(handle, index) in props.data.handles"  :type="handle.type"
+              :position="handle.position" :style="handle.style"/>
+    </div>
+    <div v-else>
+      <Handle class="handle-style"  type="target" :position="Position.Top"/>
+      <Handle class="handle-style"  type="source" :position="Position.Bottom"/>
+    </div>
+
+  </div>
+</template>
+
+<style scoped lang="less">
+.basic-box {
+  width: 200px;
+  height: 60px;
+  border: 2px solid #625454;
+  border-radius: 5px;
+  background-color: rgba(255, 255, 255);
+  overflow: hidden;
+}
+
+.handle-style {
+  width: 10px;
+  height: 10px;
+}
+
+
+//可以根据type类型来设置不同的Node的样式
+.basic-box--custom {
+  border-style: dashed;
+}
+
+.basic-box--nest {
+  border-style: double;
+  width: 300px;
+  height: 100px;
+  background: transparent;
+}
+
+.basic-box:hover {
+  //background-color: #30bfcb;
+  border-color: #2b48dd;
+}
+
+.basic-box--selected {
+  //background-color: #388d25;
+  border-color: rgb(184, 129.6, 48);
+  color: rgb(184, 129.6, 48);
+}
+
+.basic-box--dragging {
+  background-color: #da5565;
+}
+</style>

+ 49 - 0
src/components/hjflow/src/nodes/com/operationHeader.vue

@@ -0,0 +1,49 @@
+<script setup lang="ts">
+
+import {Edit} from "@element-plus/icons-vue";
+import {GrandparentMethod, HJInterNodeData, HJMethodName, HJMethodProvideName} from "../../types/comTypes";
+import {inject, markRaw} from "vue";
+const props = defineProps<HJInterNodeData>();
+
+
+const editMethod = inject<GrandparentMethod>(HJMethodProvideName)
+
+const editClick = () => {
+  editMethod && editMethod( HJMethodName.EditNode,JSON.parse(JSON.stringify(markRaw(props))))
+}
+
+</script>
+
+<template>
+  <div class="box-header">
+    <div> {{ props?.data?.label ?? "" }}</div>
+    <div class="right-btns">
+      <el-icon :size="18" class="right-icon">
+        <Edit  @click.prevent="editClick"/>
+      </el-icon>
+    </div>
+  </div>
+</template>
+
+<style scoped lang="less">
+.box-header {
+  height: 25px;
+  line-height: 25px;
+  font-size: 12px;
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  padding: 0 8px;
+  background-color: #ccc;
+
+  .right-btns {
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+
+  .right-icon {
+    cursor: pointer;
+  }
+}
+</style>

+ 40 - 0
src/components/hjflow/src/nodes/universal/UniversalNode.vue

@@ -0,0 +1,40 @@
+<script setup lang="ts">
+import type {Node} from '@vue-flow/core';
+import {inject, markRaw, onMounted} from 'vue';
+import {GrandparentMethod, HJInterNodeData, HJMethodName, HJNodeData} from '../../types/comTypes';
+import Basic from '../com/basic.vue';
+import {CirclePlus} from "@element-plus/icons-vue";
+import OperationHeader from '../com/operationHeader.vue'
+
+// 即使外部定义的类型数据再多,传进来的也是Node类型。
+const props = defineProps<HJInterNodeData>();
+
+onMounted(() => {
+  console.log('mounted', props);
+});
+
+</script>
+
+<template>
+  <Basic v-bind="props">
+    <template #header>
+      <OperationHeader v-bind="props"></OperationHeader>
+    </template>
+    <template #default>
+      <div class="content-text">
+        {{ props.data?.information?.configName ?? '请配置名称' }}
+      </div>
+    </template>
+
+  </Basic>
+
+</template>
+
+<style scoped lang="less">
+.content-text {
+  height: 36px;
+  line-height: 36px;
+  font-size: 14px;
+  text-align: center;
+}
+</style>

+ 40 - 0
src/components/hjflow/src/nodes/workflow/WorkFlowNode.vue

@@ -0,0 +1,40 @@
+<script setup lang="ts">
+import type {Node} from '@vue-flow/core';
+import {inject, markRaw, onMounted} from 'vue';
+import {GrandparentMethod, HJInterNodeData, HJMethodName, HJNodeData} from '../../types/comTypes';
+import Basic from '../com/basic.vue';
+import {CirclePlus} from "@element-plus/icons-vue";
+import OperationHeader from '../com/operationHeader.vue'
+
+// 即使外部定义的类型数据再多,传进来的也是Node类型。
+const props = defineProps<HJInterNodeData>();
+
+onMounted(() => {
+  console.log('mounted', props);
+});
+
+</script>
+
+<template>
+  <Basic v-bind="props">
+    <template #header>
+      <OperationHeader v-bind="props"></OperationHeader>
+    </template>
+    <template #default>
+      <div class="content-text">
+        {{ props.data?.information?.desc ?? '暂无描述' }}
+      </div>
+    </template>
+
+  </Basic>
+
+</template>
+
+<style scoped lang="less">
+.content-text {
+  height: 36px;
+  line-height: 36px;
+  font-size: 14px;
+  text-align: center;
+}
+</style>

File diff suppressed because it is too large
+ 20 - 0
src/components/hjflow/src/panel/btns/Back.vue


+ 44 - 0
src/components/hjflow/src/panel/btns/add-node.vue

@@ -0,0 +1,44 @@
+<script setup lang="ts">
+import { HJNodeType } from '../../types/comTypes';
+import {
+    Plus,
+} from '@element-plus/icons-vue'
+import { ArrowDown } from '@element-plus/icons-vue'
+
+const emits = defineEmits(['addNode']);
+
+const options = [
+  { content: '数字输入', value: HJNodeType.numberInput },
+  { content: '数字计算', value: HJNodeType.numberFunction },
+  { content: '数字输出', value: HJNodeType.numberResult },
+];
+
+const clickHandler = (data) => {
+  console.log('clickHandler');
+  emits('addNode', data.value);
+};
+
+const changeHandler = (data) => {
+  console.log('changeHandler');
+//   这个不起作用 也不打印 不知道为什么
+
+}
+
+</script>
+
+<template>
+  <el-dropdown  @click="clickHandler" >
+    <el-button type="primary" :icon="Plus" circle />
+    <template #dropdown>
+      <el-dropdown-menu @change="changeHandler">
+        <el-dropdown-item>Action 1</el-dropdown-item>
+        <el-dropdown-item>Action 2</el-dropdown-item>
+        <el-dropdown-item>Action 3</el-dropdown-item>
+        <el-dropdown-item>Action 4</el-dropdown-item>
+        <el-dropdown-item>Action 5</el-dropdown-item>
+      </el-dropdown-menu>
+    </template>
+  </el-dropdown>
+</template>
+
+<style scoped lang="less"></style>

+ 27 - 0
src/components/hjflow/src/panel/btns/layout-LR.vue

@@ -0,0 +1,27 @@
+<script setup lang="ts">
+</script>
+
+<template>
+  <button title="LR">
+    <svg
+        viewBox="0 0 24 24"
+        xmlns="http://www.w3.org/2000/svg"
+    >
+      <path d="M2,12 L22,12" stroke="currentColor" stroke-width="2" />
+      <path
+          d="M7,7 L2,12 L7,17"
+          stroke="currentColor"
+          stroke-width="2"
+          fill="none"
+      />
+      <path
+          d="M17,7 L22,12 L17,17"
+          stroke="currentColor"
+          stroke-width="2"
+          fill="none"
+      />
+    </svg>
+  </button>
+</template>
+
+<style scoped lang="less"></style>

+ 29 - 0
src/components/hjflow/src/panel/btns/layout-TB.vue

@@ -0,0 +1,29 @@
+<script setup lang="ts">
+</script>
+
+<template>
+  <button title="TB">
+    <svg
+        width="24"
+        height="24"
+        viewBox="0 0 24 24"
+        xmlns="http://www.w3.org/2000/svg"
+    >
+      <path d="M12,2 L12,22" stroke="currentColor" stroke-width="2" />
+      <path
+          d="M7,7 L12,2 L17,7"
+          stroke="currentColor"
+          stroke-width="2"
+          fill="none"
+      />
+      <path
+          d="M7,17 L12,22 L17,17"
+          stroke="currentColor"
+          stroke-width="2"
+          fill="none"
+      />
+    </svg>
+  </button>
+</template>
+
+<style scoped lang="less"></style>

+ 37 - 0
src/components/hjflow/src/panel/btns/reset.vue

@@ -0,0 +1,37 @@
+<script setup lang="ts"></script>
+
+<template>
+  <button title="重置">
+    <svg
+        width="48"
+        height="48"
+        viewBox="0 0 48 48"
+        fill="none"
+        xmlns="http://www.w3.org/2000/svg"
+    >
+      <path
+          d="M4 12C4 7.58172 7.58172 4 12 4H36C40.4183 4 44 7.58172 44 12V36C44 40.4183 40.4183 44 36 44H12C7.58172 44 4 40.4183 4 36V12Z"
+      />
+      <path
+          fill-rule="evenodd"
+          clip-rule="evenodd"
+          d="M36.75 12.5V25.5H33.75V12.5H36.75Z"
+          fill="white"
+      />
+      <path
+          fill-rule="evenodd"
+          clip-rule="evenodd"
+          d="M14.25 22.5V35.5H11.25V22.5H14.25Z"
+          fill="white"
+      />
+      <path
+          fill-rule="evenodd"
+          clip-rule="evenodd"
+          d="M14.8259 15.1457C17.1428 12.7456 20.398 11.25 24 11.25C31.0416 11.25 36.75 16.9584 36.75 24V25.5H33.75V24C33.75 18.6152 29.3848 14.25 24 14.25C21.2451 14.25 18.7592 15.3906 16.9843 17.2293L15.9425 18.3085L13.7841 16.2249L14.8259 15.1457ZM14.25 22.5V24C14.25 29.3848 18.6152 33.75 24 33.75C26.6307 33.75 29.0155 32.7101 30.7707 31.0157L31.8499 29.9739L33.9335 32.1324L32.8543 33.1741C30.5623 35.3866 27.4388 36.75 24 36.75C16.9584 36.75 11.25 31.0416 11.25 24V22.5H14.25Z"
+          fill="white"
+      />
+    </svg>
+  </button>
+</template>
+
+<style scoped lang="less"></style>

File diff suppressed because it is too large
+ 32 - 0
src/components/hjflow/src/panel/btns/snake.vue


+ 79 - 0
src/components/hjflow/src/panel/index.vue

@@ -0,0 +1,79 @@
+<script setup lang="ts">
+import { Panel } from '@vue-flow/core';
+import Back from './btns/Back.vue';
+import AddNode from '../panel/btns/add-node.vue';
+import LayoutLR from './btns/layout-LR.vue'
+import LayoutTB from './btns/layout-TB.vue'
+import SnakeBtn from './btns/snake.vue'
+import ResetBtn from './btns/reset.vue'
+import {HJPanelFunName} from "../types/comTypes";
+
+// 定义一个props,参数是数组,而且数组里面的值只能是 HJPannelFunName 的值
+const props = defineProps<{
+  funNames: HJPanelFunName[];
+}>()
+
+const emits = defineEmits(['addNode', 'reset', 'layout', 'toSnake', 'back']);
+
+const onAddNode = (type: string) => {
+  emits('addNode', type);
+};
+</script>
+
+<template>
+  <Panel position="top-right" class="process-panel">
+    <Back @click="() => emits('back')"  v-if="props.funNames.includes('back')"/>
+    <add-node @addNode="onAddNode" v-if="props.funNames.includes('addNode')"></add-node>
+    <LayoutLR @click="() => emits('layout', 'LR')" v-if="props.funNames.includes('layoutLR')"/>
+    <LayoutTB @click="() => emits('layout', 'TB')" v-if="props.funNames.includes('layoutTB')"/>
+    <SnakeBtn @click="() => emits('toSnake')" v-if="props.funNames.includes('toSnake')"/>
+    <ResetBtn @click="() => emits('reset')" v-if="props.funNames.includes('reset')"/>
+  </Panel>
+</template>
+
+<style scoped lang="less">
+.process-panel {
+  background-color: #2d3748;
+  padding: 4px;
+  border-radius: 8px;
+  box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
+  display: flex;
+  flex-direction: row-reverse;
+  gap: 8px;
+}
+
+.process-panel button {
+  border: none;
+  cursor: pointer;
+  background-color: #4a5568;
+  border-radius: 8px;
+  color: white;
+  box-shadow: 0 0 10px rgba(0, 0, 0, 0.5);
+  font-size: 16px;
+  width: 30px;
+  height: 30px;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  white-space: nowrap;
+  overflow: hidden;
+  text-overflow: ellipsis;
+}
+
+.checkbox-panel {
+  display: flex;
+  align-items: center;
+  gap: 4px;
+}
+
+.process-panel button:hover,
+.layout-panel button:hover {
+  background-color: #2563eb;
+  transition: background-color 0.2s;
+}
+
+.process-panel label {
+  color: white;
+  font-size: 12px;
+}
+</style>

+ 45 - 0
src/components/hjflow/src/styles/index.scss

@@ -0,0 +1,45 @@
+
+
+.common-node {
+  background-color: #c86c6c;
+}
+
+.nest-node {
+  min-width: 200px;
+  min-height: 200px;
+  background-color: rgba(8, 169, 117, 0.1);
+}
+
+
+.vue-flow__minimap {
+  transform: scale(75%);
+  transform-origin: bottom right;
+}
+
+
+
+.dnd-flow .vue-flow-wrapper {
+  flex-grow:1;
+  height:100%
+}
+
+
+
+.dropzone-background {
+  position:relative;
+  height:100%;
+  width:100%
+}
+
+.dropzone-background .overlay {
+  position:absolute;
+  top:0;
+  left:0;
+  height:100%;
+  width:100%;
+  display:flex;
+  align-items:center;
+  justify-content:center;
+  z-index:1;
+  pointer-events:none
+}

+ 173 - 0
src/components/hjflow/src/testShenpiliu.vue

@@ -0,0 +1,173 @@
+<script setup lang="ts">
+import {ref, onMounted, watch, reactive, markRaw} from 'vue';
+
+import type {Node, Edge} from '@vue-flow/core';
+import {HJMethodName, HJNodeData, HJPanelFunNameArray} from './types/comTypes';
+import WorkFlowDrag from '../src/leftDrags/workflow/dragData.vue'
+import HJFlow from './hjflow/index.vue'
+
+import type { ComponentSize, FormInstance, FormRules } from 'element-plus'
+
+
+interface HJShenPiliuData {
+  name: string //节点名称
+  desc: string //节点描述
+}
+
+const formSize = ref<ComponentSize>('default')
+const ruleFormRef = ref<FormInstance>()
+const formData = ref<HJShenPiliuData>({
+  name: '',
+  desc: '',
+})
+
+const rules = reactive<FormRules<HJShenPiliuData>>({
+  name: [
+    { required: true, message: 'Please input Activity name', trigger: 'blur' },
+   ],
+  desc: [
+    { required: true, message: 'Please input activity form', trigger: 'blur' },
+  ],
+})
+
+const submitForm = async (formEl: FormInstance | undefined) => {
+  // if (!formEl) return
+  // await formEl.validate((valid, fields) => {
+  //   if (valid) {
+  //     console.log('submit!')
+  //   } else {
+  //     console.log('error submit!', fields)
+  //   }
+  // })
+
+  if (flowRef.value && flowRef.value.updateNodeData && selectedNode.value) {
+    selectedNode.value.data.information = JSON.parse(JSON.stringify(formData.value))
+    flowRef.value.updateNodeData(JSON.parse(JSON.stringify(markRaw(selectedNode.value))))
+    ruleFormRef.value && ruleFormRef.value.resetFields()
+    selectedNode.value = null
+
+  }
+}
+
+const cancel = () => {
+  ruleFormRef.value && ruleFormRef.value.resetFields()
+}
+
+const flowRef = ref()
+const nodes = ref<HJNodeData[]>([]);
+const edges = ref<Edge[]>([]);
+
+
+onMounted(() => {
+  // 接口调用完成要初始化 ,为的是保存一个最初的数据
+  flowRef.value && flowRef.value.initFlow([], [])
+});
+
+
+
+
+// ======= 信息展示 =======
+const infoVisible = ref(false)
+const selectedNode = ref<HJNodeData | null>(null) //双击的节点数据
+const closeInfo = () => {
+  infoVisible.value = false
+  selectedNode.value = null
+}
+
+// vue 的相关操作
+const onNodeOperation = (name: HJMethodName, node: HJNodeData) : void => {
+  selectedNode.value = null
+  ruleFormRef.value && ruleFormRef.value.resetFields()
+  selectedNode.value = JSON.parse(JSON.stringify(node))
+  console.log("selectedNode",selectedNode.value)
+  if (selectedNode.value && selectedNode.value.data.information) {
+    formData.value = JSON.parse(JSON.stringify(selectedNode.value.data.information))
+  }
+
+}
+
+const testGetCurrentData = () => {
+  let p = {
+    nodes: markRaw(nodes.value),
+    edges:  markRaw(edges.value),
+  }
+  console.log("=========当前数据======", JSON.stringify(p) ,p )
+}
+
+</script>
+
+<template>
+  <div class="out-box">
+    <WorkFlowDrag></WorkFlowDrag>
+    <HJFlow class="vue-flow-box" ref="flowRef" v-model:nodes-data="nodes" v-model:edges-data="edges" @hjMethod="onNodeOperation" :show-panel="true" :panel-btns="HJPanelFunNameArray"></HJFlow>
+    <div class="right-message">
+      <div class="flow-header-name" @click="testGetCurrentData">当前工作流名称</div>
+      <el-form
+
+          ref="ruleFormRef"
+          style="max-width: 600px"
+          :model="formData"
+          label-width="auto"
+          class="demo-ruleForm"
+          :size="formSize"
+          status-icon
+      >
+        <el-form-item label="Activity name" prop="name">
+          <el-input v-model="formData.name" />
+        </el-form-item>
+        <el-form-item label="Activity form" prop="desc">
+          <el-input v-model="formData.desc" type="textarea" />
+        </el-form-item>
+        <el-form-item>
+          <el-button type="primary" @click="submitForm(ruleFormRef)">
+            保存
+          </el-button>
+          <el-button @click="cancel">取消</el-button>
+        </el-form-item>
+      </el-form>
+    </div>
+  </div>
+
+</template>
+
+<style scoped lang="less">
+
+.out-box {
+  width: 100%;
+  height: 80vh;
+  border: 1px solid #ccc;
+  display: flex;
+}
+
+.vue-flow-box {
+  flex: 1;
+  flex-shrink: 0;
+  height: 100%;
+  border: 3px solid red;
+  //width: 300px;
+  //height: 500px;
+}
+
+.right-message {
+  width: 400px;
+  height: 100%;
+  border: 1px solid #ccc;
+  background-color: white;
+  padding: 10px;
+  display: flex;
+  flex-direction: column;
+  justify-content: start;
+  align-items: start;
+
+  .flow-header-name {
+    font-size: 18px;
+    font-weight: bold;
+    margin-top: 10px;
+    margin-bottom: 10px;
+  }
+
+}
+
+</style>
+
+

+ 105 - 0
src/components/hjflow/src/types/comTypes.ts

@@ -0,0 +1,105 @@
+// 这里是 vue-flow 自定义的组件的types集合
+import { markRaw } from 'vue';
+import type { Node, Edge } from '@vue-flow/core';
+
+import CommonEdge from '../edges/commonEdge.vue'
+
+import CommonNode from '../nodes/CommonNode.vue';
+import NestNode from '../nodes/NestNode.vue';
+import NumberInputNode from '../nodes/NumberInputNode.vue';
+import NumberFunctionNode from '../nodes/NumberFunNode.vue';
+import NumberResultNode from '../nodes/NumberResultNode.vue';
+
+import UniversalNode from '../nodes/universal/UniversalNode.vue';
+// 工作流相关
+import WorkFlowNode from '../nodes/workflow/WorkFlowNode.vue';
+
+
+export  enum HJNodeType {
+  custom = 'custom',
+  nest = 'nest',
+  //   算数相关
+  numberInput = 'number-input',
+  numberFunction = 'number-function',
+  numberResult = 'number-result',
+//   工作流相关
+  workFlow = 'work-flow',
+//   公共 通用 上面是header 下面是content
+  universal = 'universal',
+}
+
+export const nodeTypes = {
+  [HJNodeType.custom]: markRaw(CommonNode),
+  [HJNodeType.nest]: markRaw(NestNode),
+  //   算数相关
+  [HJNodeType.numberInput]: markRaw(NumberInputNode),
+  [HJNodeType.numberFunction]: markRaw(NumberFunctionNode),
+  [HJNodeType.numberResult]: markRaw(NumberResultNode),
+  [HJNodeType.workFlow]: markRaw(WorkFlowNode),
+
+  [HJNodeType.universal]: markRaw(UniversalNode),
+};
+
+export const edgeTypes = {
+  default: markRaw(CommonEdge),
+}
+
+// 用于Handle
+export  enum HJPosition {
+  Left = 'left',
+  Top = 'top',
+  Right = 'right',
+  Bottom = 'bottom',
+}
+
+export interface HJHandle {
+  type: 'source' | 'target';
+  position: HJPosition;
+  style?: any; //如果同一侧有多个handle,可以用style调整位置
+  other?: string; //预留字段,以后看可能不同的node类型需要不同的handle样式,通过这个引入不同的Handlevue 文件
+}
+
+// 用于Node 类型中的data,方便拓展选中 拖拽等数据
+export  interface HJNodeDataModel {
+  label: string;
+  isSelected?: boolean;
+  isDragging?: boolean;
+  handles?: HJHandle[];
+  information?: any  //点击node节点 弹出drawer编辑的数据
+}
+
+// 用于<VueFlow>组件的nodes属性的类型定义 从外部导入数据需要使用
+export  interface HJNodeData extends Partial<Node> {
+  jsonData?: string; //经实验发现nodeTypes这种方法无法传递 Node之外的key,给props扩展也没有用, 也就是自定义的nodes里面拿不到这个key
+  data: HJNodeDataModel;
+}
+
+// 这个类型用于自定义node节点内部的数据结构,忽略一些数据能提升渲染时候的性能,
+export  type HJInterNodeData = Omit<HJNodeData, 'jsonData'>;
+
+
+// 在顶层组件订一个方法,通过provide方法提供给其他组件使用
+export const HJMethodProvideName = 'hjMethodProvideName'
+export const CurrentHeaderOperationProvideName = 'hjCurrentHeaderOperationProvideName'
+export const CurrentSelectedEdgeProvideName = 'hjCurrentSelectedEdgeProvideName'
+export class HJMethodName {
+  static AddNode = 'hjAddNode'
+  static EditNode = 'hjEditNode'
+}
+export type GrandparentMethod = (name: HJMethodName, param: HJNodeData) => void;
+
+// Panel相关 HJPanelFunName添加一个  就在数组种写一个
+export type HJPanelFunName = 'addNode' | 'reset' | 'layoutLR' | 'layoutTB' | 'toSnake' | 'back';
+export const HJPanelFunNameArray = ['addNode','reset', 'layoutLR', 'layoutTB', 'toSnake', 'back'];
+
+// 外面会传给HJFlow的props
+export interface HJFlowProps {
+  // Pannel相关
+  panelBtns?: HJPanelFunName[]; //自定义面板按钮 默认空
+  showPanel?: boolean;  // 默认为false,不显示面板
+}
+
+export interface HJFlowInstance {
+  updateNodeData: (node: HJNodeData) => void;
+  initFlow: (nodes: HJNodeData[], edges: any[]) => void;
+}

+ 103 - 0
src/components/hjflow/src/utils/index.ts

@@ -0,0 +1,103 @@
+const position = { x: 0, y: 0 }
+
+export const initialNodes = [
+    {
+        id: '1',
+        position,
+        type: 'custom',
+        data: {
+            label: 'Node 1',
+        },
+    },
+    {
+        id: '2',
+        type: 'custom',
+        position,
+        data: {
+            label: 'Node 2',
+        },
+    },
+    {
+        id: '2a',
+        type: 'custom',
+        position,
+        data: {
+            label: 'Node 2a',
+        },
+    },
+    {
+        id: '2b',
+        type: 'custom',
+        position,
+        data: {
+            label: 'Node 2b',
+        },
+    },
+    {
+        id: '2c',
+        position,
+        type: 'custom',
+        data: {
+            label: 'Node 2c',
+        },
+    },
+    {
+        id: '2d',
+        type: 'custom',
+        position,
+        data: {
+            label: 'Node 2d',
+        },
+    },
+    {
+        id: '3',
+        type: 'custom',
+        position,
+        data: {
+            label: 'Node 3',
+        },
+    },
+    {
+        id: '4',
+        type: 'custom',
+        position,
+        data: {
+            label: 'Node 4',
+        },
+    },
+    {
+        id: '5',
+        type: 'custom',
+        position,
+        data: {
+            label: 'Node 5',
+        },
+    },
+    {
+        id: '6',
+        position,
+        data: {
+            label: 'Node 6',
+        },
+    },
+    {
+        id: '7',
+        position,
+        data: {
+            label: 'Node 7',
+        },
+    },
+]
+
+export const initialEdges = [
+    { id: 'e1-2', source: '1', target: '2' },
+    { id: 'e1-3', source: '1', target: '3' },
+    { id: 'e2-2a', source: '2', target: '2a' },
+    { id: 'e2-2b', source: '2', target: '2b' },
+    { id: 'e2-2c', source: '2', target: '2c' },
+    { id: 'e2c-2d', source: '2c', target: '2d' },
+    { id: 'e3-7', source: '3', target: '4' },
+    { id: 'e4-5', source: '4', target: '5' },
+    { id: 'e5-6', source: '5', target: '6' },
+    { id: 'e5-7', source: '5', target: '7' },
+]

+ 72 - 0
src/components/hjflow/src/utils/useLayout.ts

@@ -0,0 +1,72 @@
+import dagre from '@dagrejs/dagre'
+import { Position, useVueFlow, Node, Edge } from '@vue-flow/core'
+import { ref } from 'vue'
+import {HJNodeData} from "@/packages/components/flow/src/types/comTypes";
+
+
+
+/**
+ * Composable to run the layout algorithm on the graph.
+ * It uses the `dagre` library to calculate the layout of the nodes and edges.
+ */
+export function useLayout() {
+    const { findNode } = useVueFlow()
+
+    const graph = ref(new dagre.graphlib.Graph())
+
+    const previousDirection = ref('LR')
+
+    function layout(nodes: HJNodeData[], edges: Edge[], direction: 'LR' | 'TB' = 'LR') {
+        // we create a new graph instance, in case some nodes/edges were removed, otherwise dagre would act as if they were still there
+        const dagreGraph = new dagre.graphlib.Graph()
+
+        graph.value = dagreGraph
+
+        dagreGraph.setDefaultEdgeLabel(() => ({}))
+
+        const isHorizontal = direction === 'LR'
+        dagreGraph.setGraph({ rankdir: direction })
+
+        previousDirection.value = direction
+
+        for (const node of nodes) {
+            // if you need width+height of nodes for your layout, you can use the dimensions property of the internal node (`GraphNode` type)
+            const graphNode: any = findNode(node.id)
+
+            dagreGraph.setNode(node?.id ?? "", { width: graphNode.dimensions.width || 150, height: graphNode.dimensions.height || 50 })
+        }
+
+        for (const edge of edges) {
+            dagreGraph.setEdge(edge.source, edge.target)
+        }
+
+        dagre.layout(dagreGraph)
+
+        // set nodes with updated positions
+        return nodes.map((node) => {
+            const nodeWithPosition = dagreGraph.node(node?.id ?? "")
+
+            return {
+                ...node,
+                targetPosition: isHorizontal ? Position.Left : Position.Top,
+                sourcePosition: isHorizontal ? Position.Right : Position.Bottom,
+                position: { x: nodeWithPosition.x, y: nodeWithPosition.y },
+                data: {
+                    ...node.data,
+                    handles: [
+                        {
+                            type: 'target',
+                            position: isHorizontal ? Position.Left : Position.Top,
+                        },
+                        {
+                            type:'source',
+                            position: isHorizontal ? Position.Right : Position.Bottom,
+                        }
+                    ]
+                }
+            }
+        })
+    }
+
+    return { graph, layout, previousDirection }
+}

+ 116 - 0
src/components/hjflow/src/utils/useSnake.ts

@@ -0,0 +1,116 @@
+import {ElMessage} from "element-plus";
+
+export const isStraightFlow = (nodes: any[], edges: any[]) => {
+    let isSerial = false;
+    let message = "非标准直线型流程图";
+
+    // 如果nodes的数量小于1 说明没有节点 直接返回false
+    if (nodes && nodes.length < 2) {
+        return [isSerial, "节点数量少于2"];
+    }
+
+    // 如果edges的数量小于1 说明没有边 直接返回false
+    if (edges && edges.length < 1) {
+        return [isSerial, "边数量少于1"];
+    }
+
+    // 如果nodes的数量不等于edges的数量+1 说明没有完全连线或者是不是直线
+    if (nodes?.length !== edges?.length + 1) {
+        return [isSerial, message];
+    }
+
+    // 每一个边的源节点id组成一个set,如果set的数量和edges的数量不相等,说明不是标准直线型流程图
+    let sourceNodeIdsSet = new Set(
+        edges.map((edge) => {
+            return edge.source;
+        })
+    );
+    let targetNodeIdsSet = new Set(
+        edges.map((edge) => {
+            return edge.target;
+        })
+    );
+    if (
+        sourceNodeIdsSet.size !== edges.length ||
+        targetNodeIdsSet.size !== edges.length
+    ) {
+        return [isSerial, message];
+    }
+
+    // 能到这里说明是直线
+    return [true, "是直线"];
+};
+
+// 按照edges的顺序重新给nodes排序
+export const sortNodesByEdges = (nodes: any[], edges: any[]) => {
+    // 先获取所有边的node的id
+    let edgeIds = [edges[0].sourceNode.id];
+    for (let i = 1; i < edges.length; i++) {
+        edgeIds.push(edges[i].targetNode.id);
+    }
+
+    // 创建一个ids到对应的索引的映射
+    const idIndexMap = edgeIds.reduce((map, id, index) => {
+        map[id] = index;
+        return map;
+    }, {});
+
+    // 根据ids数组中的顺序对对象数组进行排序
+    return nodes.sort((a, b) => {
+        return idIndexMap[a.id] - idIndexMap[b.id];
+    });
+};
+
+export const useSnakeLayoutHook = (
+    nodes: any[],
+    edges: any[],
+    spacingY: number = 150,
+    spacingX: number = 230
+) => {
+    let [isStraight, msg] = isStraightFlow(nodes, edges);
+    if (!isStraight) {
+        ElMessage.warning(msg);
+        return nodes; //这里要返回nodes, 因为外面要用到
+    }
+
+    console.log("dddd", nodes, edges);
+
+    // 后端数据做了排序了  就不用排序了
+    // nodes = sortNodesByEdges(nodes, edges);
+
+    const snakeNodes = [];
+
+    // 内边距
+    const paddingTop = 35;
+    const paddingLeft = 15;
+
+    // 这是每一个个列的数量  列数量
+    const nodesPerColumn = 6; //Math.floor(canvasHeight / spacingY);
+
+    // console.log(canvasHeight, canvasHeight / spacingY);
+
+    nodes.forEach((node, index) => {
+        const col = Math.floor(index / nodesPerColumn);
+        const row = index % nodesPerColumn;
+
+        // 根据行号计算位置,奇数列从下到上,偶数列从上到下
+        const adjustedRow = row;
+        // const adjustedRow = col % 2 === 0 ? row : nodesPerColumn - 1 - row;
+
+        node.position = {
+            x: col * spacingX + paddingLeft,
+            y: adjustedRow * spacingY + paddingTop,
+        };
+
+        // 更新奇数列的 handle 处理
+        // if (col % 2 !== 0) {
+        //   // 奇数列的节点调整 handle 位置
+        //   node.data.handleDirection = 'toTop'; //如果是第二列 也就是奇数列 调整箭头向上
+        // } else {
+        //   node.data.handleDirection = 'toDown';
+        // }
+
+        snakeNodes.push(node);
+    });
+    return snakeNodes;
+};

+ 0 - 5
src/main.ts

@@ -14,9 +14,6 @@ import "element-plus/dist/index.css";
 import "@/styles/index.scss";
 import "uno.css";
 
-import jxqui from 'jxq-ui'
-import 'jxq-ui/dist/style.css'
-
 // avue
 import { setupEleAvue } from "@/plugins";
 
@@ -34,6 +31,4 @@ setupPermission();
 
 setupEleAvue(app);
 
-app.use(jxqui)
-
 app.use(router).mount("#app");

+ 60 - 0
src/views/modules/project-config/com/function-col.vue

@@ -1,12 +1,50 @@
 <script setup lang="ts">
+// import useDragAndDrop from "../configs/useDnD";
+
+// import useDragAndDrop from "jxq-ui";
+
+import useDragAndDrop from "@/components/hjflow/src/hooks/useDnD";
+
+const { onDragStart } = useDragAndDrop();
+
 import TitleHeader from "./titleHeader.vue";
 import { propertyData } from "../configs/properites";
+
+const activeNames = ref(["1"]);
+const handleChange = (val: any) => {
+  console.log(val);
+};
+
+const test = (data) => {
+  console.log("test", data);
+};
 </script>
 
 <template>
   <div class="function-col">
     <TitleHeader> 添加功能模块</TitleHeader>
     {{ propertyData }}
+    <el-collapse v-model="activeNames" @change="handleChange">
+      <el-collapse-item
+        title="Consistency"
+        name="1"
+        v-for="(funType, index) in propertyData"
+        :key="funType.id"
+      >
+        <div class="drag-container">
+          <div
+            class="drag-item"
+            v-for="(dragData, index) in funType.functions"
+            :key="index"
+            :draggable="true"
+            @dragstart="onDragStart($event, dragData)"
+            @click="test(dragData)"
+          >
+            {{ dragData?.data?.information?.functionName ?? "-" }}
+          </div>
+        </div>
+      </el-collapse-item>
+    </el-collapse>
   </div>
 </template>
 
@@ -16,4 +54,26 @@ import { propertyData } from "../configs/properites";
   height: calc(100vh - $main-header-height);
   background-color: $hj-black-2;
 }
+.drag-container {
+  display: flex;
+  flex-direction: column;
+  justify-content: start;
+  align-items: center;
+  height: 100%;
+  width: 150px;
+  background: #ccc;
+  padding: 8px 10px;
+  .drag-item {
+    background: #fff;
+    width: 100%;
+    font-size: 14px;
+    text-align: center;
+    height: 20px;
+    line-height: 20px;
+    border-radius: 4px;
+    border: 1px solid rgba(204, 204, 204, 0.9);
+    margin-bottom: 10px;
+    cursor: pointer;
+  }
+}
 </style>

+ 4 - 8
src/views/modules/project-config/configs/properites.ts

@@ -1,9 +1,5 @@
 import { Node } from "@vue-flow/core";
-import type {
-  HJHandle,
-  HJNodeData,
-  HJNodeDataModel,
-} from "jxq-ui/dist/components/flow/src/types/comTypes";
+import { HJNodeDataModel } from "@/components/hjflow/src/types/comTypes";
 
 // 功能块名称	属性名称	初始值	数值类型	下限	上限	输入/输出
 
@@ -31,7 +27,7 @@ export interface AutoTestNodeData extends Partial<Node> {
   data: HJNodeData2;
 }
 
-export const propertyData: FunctionTypeModel[] = [
+export const propertyData = ref<FunctionTypeModel[]>([
   {
     id: "1",
     name: "信号源",
@@ -39,7 +35,7 @@ export const propertyData: FunctionTypeModel[] = [
       {
         type: "universal",
         data: {
-          label: "",
+          label: "信号源",
           information: {
             functionType: "功能块类别",
             functionTypeId: "功能块类别ID",
@@ -49,4 +45,4 @@ export const propertyData: FunctionTypeModel[] = [
       },
     ],
   },
-];
+]);

+ 45 - 1
src/views/modules/project-config/project-config.vue

@@ -1,13 +1,57 @@
 <script setup lang="ts">
 import ProjectMessage from "./com/project-message.vue";
 import FunctionModule from "./com/function-col.vue";
+import HJFlow from "@/components/hjflow/src/hjflow/index.vue";
+import {
+  HJFlowInstance,
+  HJMethodName,
+  HJNodeData,
+} from "@/components/hjflow/src/types/comTypes";
+import { Edge } from "@vue-flow/core";
+
+const flowRef = ref<HJFlowInstance>();
+const nodes = ref<HJNodeData[]>([]);
+const edges = ref<Edge[]>([]);
+// vue 的相关操作
+const onNodeOperation = (name: HJMethodName, node: HJNodeData): void => {
+  console.log("onNodeOperation", name, node);
+  // selectedNode.value = null;
+  // ruleFormRef.value && ruleFormRef.value.resetFields();
+  // selectedNode.value = JSON.parse(JSON.stringify(node));
+  console.log("selectedNode", selectedNode.value);
+  // if (selectedNode.value && selectedNode.value.data.information) {
+  //   formData.value = JSON.parse(
+  //     JSON.stringify(selectedNode.value.data.information)
+  //   );
+  // }
+};
+
+// 右侧信息
+// ======= 信息展示 =======
+const infoVisible = ref(false);
+const selectedNode = ref<HJNodeData | null>(null); //双击的节点数据
+const closeInfo = () => {
+  infoVisible.value = false;
+  selectedNode.value = null;
+};
+
+onMounted(() => {
+  // 接口调用完成要初始化 ,为的是保存一个最初的数据
+  flowRef.value && flowRef.value.initFlow([], []);
+});
 </script>
 
 <template>
   <div class="project-config-container">
     <ProjectMessage />
     <FunctionModule />
-    <HJFlow class="hjflow-box" />
+    <HJFlow
+      class="hjflow-box"
+      ref="flowRef"
+      v-model:nodes-data="nodes"
+      v-model:edges-data="edges"
+      @hjMethod="onNodeOperation"
+    />
   </div>
 </template>