From 320d1a9d92860bed763f8755c6b57165102b9a4d Mon Sep 17 00:00:00 2001
From: lixiaolong <702156524@qq.com>
Date: Wed, 29 Jul 2026 11:43:07 +0800
Subject: [PATCH] feat: migrate workflows to Vue Flow
---
README.md | 3 +-
package.json | 5 +-
pnpm-lock.yaml | 234 +++----
src/api/test/log.js | 2 +-
src/main.js | 3 +
src/router/index.js | 10 +-
src/utils/convertNodeRedToLogicFlow.js | 105 ----
src/utils/convertNodeRedToVueFlow.js | 76 +++
.../components/LogicFlow/CustomGroup.js | 103 ----
.../components/LogicFlow/customFlowNode.js | 134 ----
.../register/components/LogicFlow/index.vue | 294 ---------
.../{LogicFlow => VueFlow}/IPlayer/config.ts | 0
.../{LogicFlow => VueFlow}/IPlayer/index.vue | 0
.../components/VueFlow/RuntimeGroup.vue | 24 +
.../components/VueFlow/RuntimeNode.vue | 47 ++
.../{LogicFlow => VueFlow}/customFlow.vue | 0
.../register/components/VueFlow/index.vue | 206 +++++++
src/views/flow/components/NodeState.vue | 2 +-
src/views/flow/components/NodeTitle.vue | 4 +-
src/views/flow/config.js | 65 --
src/views/flow/index.vue | 537 ++++++++++------
src/views/flow/nodes/common/endNode.js | 196 ------
src/views/flow/nodes/common/index.js | 11 -
src/views/flow/nodes/common/selectAreaNode.js | 102 ----
src/views/flow/nodes/common/serviceNode.js | 213 -------
src/views/flow/nodes/common/serviceNode.vue | 2 +-
src/views/flow/nodes/common/startNode.js | 204 -------
src/views/flow/nodes/function/codeNode.js | 213 -------
src/views/flow/nodes/function/codeNode.vue | 2 +-
src/views/flow/nodes/function/currentLoop.vue | 2 +-
.../flow/nodes/function/currentLoopNode.js | 219 -------
src/views/flow/nodes/function/httpNode.js | 213 -------
src/views/flow/nodes/function/httpNode.vue | 2 +-
src/views/flow/nodes/function/index.js | 25 -
src/views/flow/nodes/function/loop.js | 294 ---------
src/views/flow/nodes/function/loop.vue | 87 ++-
src/views/flow/nodes/function/recognize.js | 213 -------
src/views/flow/nodes/function/recognize.vue | 2 +-
src/views/flow/nodes/function/sdAgent.js | 213 -------
src/views/flow/nodes/function/sdAgent.vue | 2 +-
src/views/flow/nodes/function/sleep.js | 213 -------
src/views/flow/nodes/function/sleep.vue | 2 +-
src/views/flow/nodes/function/stopLoop.js | 191 ------
src/views/flow/nodes/function/subEnd.js | 186 ------
src/views/flow/nodes/function/subStart.js | 185 ------
src/views/flow/nodes/function/switch.js | 318 ----------
src/views/flow/nodes/function/switch.vue | 4 +-
src/views/flow/vue-flow/FlowEdge.vue | 86 +++
src/views/flow/vue-flow/FlowNode.vue | 202 ++++++
src/views/flow/vue-flow/adapter.js | 578 ++++++++++++++++++
src/views/test/log/index.vue | 4 +-
src/views/vi/testProject/index.vue | 4 +-
52 files changed, 1789 insertions(+), 4253 deletions(-)
delete mode 100644 src/utils/convertNodeRedToLogicFlow.js
create mode 100644 src/utils/convertNodeRedToVueFlow.js
delete mode 100644 src/views/device/register/components/LogicFlow/CustomGroup.js
delete mode 100644 src/views/device/register/components/LogicFlow/customFlowNode.js
delete mode 100644 src/views/device/register/components/LogicFlow/index.vue
rename src/views/device/register/components/{LogicFlow => VueFlow}/IPlayer/config.ts (100%)
rename src/views/device/register/components/{LogicFlow => VueFlow}/IPlayer/index.vue (100%)
create mode 100644 src/views/device/register/components/VueFlow/RuntimeGroup.vue
create mode 100644 src/views/device/register/components/VueFlow/RuntimeNode.vue
rename src/views/device/register/components/{LogicFlow => VueFlow}/customFlow.vue (100%)
create mode 100644 src/views/device/register/components/VueFlow/index.vue
delete mode 100644 src/views/flow/nodes/common/endNode.js
delete mode 100644 src/views/flow/nodes/common/index.js
delete mode 100644 src/views/flow/nodes/common/selectAreaNode.js
delete mode 100644 src/views/flow/nodes/common/serviceNode.js
delete mode 100644 src/views/flow/nodes/common/startNode.js
delete mode 100644 src/views/flow/nodes/function/codeNode.js
delete mode 100644 src/views/flow/nodes/function/currentLoopNode.js
delete mode 100644 src/views/flow/nodes/function/httpNode.js
delete mode 100644 src/views/flow/nodes/function/index.js
delete mode 100644 src/views/flow/nodes/function/loop.js
delete mode 100644 src/views/flow/nodes/function/recognize.js
delete mode 100644 src/views/flow/nodes/function/sdAgent.js
delete mode 100644 src/views/flow/nodes/function/sleep.js
delete mode 100644 src/views/flow/nodes/function/stopLoop.js
delete mode 100644 src/views/flow/nodes/function/subEnd.js
delete mode 100644 src/views/flow/nodes/function/subStart.js
delete mode 100644 src/views/flow/nodes/function/switch.js
create mode 100644 src/views/flow/vue-flow/FlowEdge.vue
create mode 100644 src/views/flow/vue-flow/FlowNode.vue
create mode 100644 src/views/flow/vue-flow/adapter.js
diff --git a/README.md b/README.md
index c8ef97e..107a327 100644
--- a/README.md
+++ b/README.md
@@ -25,7 +25,7 @@
| Element Plus | 2.13.1 |
| Pinia | 3.0.4 |
| Vue Router | 4.6.4 |
-| 可视化 | ECharts、LogicFlow、Three.js、Monaco Editor、XGPlayer |
+| 可视化 | ECharts、Vue Flow、Three.js、Monaco Editor、XGPlayer |
## 环境要求
@@ -100,4 +100,3 @@ src/views/corpusManger 语料管理
- `src/settings.js`:布局、页签和版权配置。
生产密码、访问密钥和内部网络配置应通过部署环境注入,不要提交新的敏感信息。
-
diff --git a/package.json b/package.json
index 704c4cd..0648cb7 100644
--- a/package.json
+++ b/package.json
@@ -15,8 +15,9 @@
},
"dependencies": {
"@element-plus/icons-vue": "2.3.2",
- "@logicflow/core": "^2.0.13",
- "@logicflow/extension": "^2.0.18",
+ "@vue-flow/background": "^1.3.2",
+ "@vue-flow/controls": "^1.1.3",
+ "@vue-flow/core": "^1.48.2",
"@vueup/vue-quill": "1.2.0",
"@vueuse/core": "14.1.0",
"ace-builds": "^1.43.0",
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index f3fe99e..c0eac5f 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -14,12 +14,15 @@ importers:
'@element-plus/icons-vue':
specifier: 2.3.2
version: 2.3.2(vue@3.5.26)
- '@logicflow/core':
- specifier: ^2.0.13
- version: 2.2.4
- '@logicflow/extension':
- specifier: ^2.0.18
- version: 2.3.0(@logicflow/core@2.2.4)
+ '@vue-flow/background':
+ specifier: ^1.3.2
+ version: 1.3.2(@vue-flow/core@1.48.2(vue@3.5.26))(vue@3.5.26)
+ '@vue-flow/controls':
+ specifier: ^1.1.3
+ version: 1.1.3(@vue-flow/core@1.48.2(vue@3.5.26))(vue@3.5.26)
+ '@vue-flow/core':
+ specifier: ^1.48.2
+ version: 1.48.2(vue@3.5.26)
'@vueup/vue-quill':
specifier: 1.2.0
version: 1.2.0(vue@3.5.26)
@@ -159,9 +162,6 @@ packages:
'@antfu/utils@0.7.10':
resolution: {integrity: sha512-+562v9k4aI80m1+VuMHehNJWLOFjBnXn3tdOitzD0il5b7smkSBal4+a3oKiQTbrwMmN/TBUMDvbdoWDehgOww==}
- '@antv/hierarchy@0.6.14':
- resolution: {integrity: sha512-V3uknf7bhynOqQDw2sg+9r9DwZ9pc6k/EcqyTFdfXB1+ydr7urisP0MipIuimucvQKN+Qkd+d6w601r1UIroqQ==}
-
'@babel/helper-string-parser@7.29.7':
resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==}
engines: {node: '>=6.9.0'}
@@ -440,14 +440,6 @@ packages:
'@lezer/lr@1.4.10':
resolution: {integrity: sha512-rnCpTIBafOx4mRp43xOxDJbFipJm/c0cia/V5TiGlhmMa+wsSdoGmUN3w5Bqrks/09Q/D4tNAmWaT8p6NRi77A==}
- '@logicflow/core@2.2.4':
- resolution: {integrity: sha512-lupgH9pUJ5cv3JB1+U+ahVwgQJyp1QBK6t3TXGOczzL6FwE53z8DYBj91WQQ2dFgDGShdcp4oSY1ZHMDhRZvmQ==}
-
- '@logicflow/extension@2.3.0':
- resolution: {integrity: sha512-7PGiWlGJ8oqXvHkfrr7vaMnLZgp4Ue6+TD371p8IxK2UbDQ6uWcJOoyij0pRpseU3+rL9oVyPo8qtIODKiaENQ==}
- peerDependencies:
- '@logicflow/core': 2.2.4
-
'@marijn/find-cluster-break@1.0.3':
resolution: {integrity: sha512-FY+MKLBoTsLNJF/eLWaOsXGdz6uh3Iu1axjPf6TUq92IYumcTcXWHoS747JARLkcdlJ/Waiaxc5wQfFO8jC6NA==}
@@ -751,6 +743,23 @@ packages:
vite: ^5.0.0 || ^6.0.0
vue: ^3.2.25
+ '@vue-flow/background@1.3.2':
+ resolution: {integrity: sha512-eJPhDcLj1wEo45bBoqTXw1uhl0yK2RaQGnEINqvvBsAFKh/camHJd5NPmOdS1w+M9lggc9igUewxaEd3iCQX2w==}
+ peerDependencies:
+ '@vue-flow/core': ^1.23.0
+ vue: ^3.3.0
+
+ '@vue-flow/controls@1.1.3':
+ resolution: {integrity: sha512-XCf+G+jCvaWURdFlZmOjifZGw3XMhN5hHlfMGkWh9xot+9nH9gdTZtn+ldIJKtarg3B21iyHU8JjKDhYcB6JMw==}
+ peerDependencies:
+ '@vue-flow/core': ^1.23.0
+ vue: ^3.3.0
+
+ '@vue-flow/core@1.48.2':
+ resolution: {integrity: sha512-raxhgKWE+G/mcEvXJjGFUDYW9rAI3GOtiHR3ZkNpwBWuIaCC1EYiBmKGwJOoNzVFgwO7COgErnK7i08i287AFA==}
+ peerDependencies:
+ vue: ^3.3.0
+
'@vue/compiler-core@3.5.26':
resolution: {integrity: sha512-vXyI5GMfuoBCnv5ucIT7jhHKl55Y477yxP6fc4eUswjP8FG3FFVFd41eNDArR+Uk3QKn2Z85NavjaxLxOC19/w==}
@@ -999,9 +1008,6 @@ packages:
resolution: {integrity: sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==}
engines: {node: '>=0.10.0'}
- classnames@2.5.1:
- resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==}
-
clipboard@2.0.11:
resolution: {integrity: sha512-C+0bbOqkezLIsmWSvlsXS0Q0bmkugu7jcfMIACB+RDEntIzQIkdr148we28AfSloQLRdZlYL/QYyrq05j/3Faw==}
@@ -1102,6 +1108,44 @@ packages:
csstype@3.2.3:
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
+ d3-color@3.1.0:
+ resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
+ engines: {node: '>=12'}
+
+ d3-dispatch@3.0.1:
+ resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==}
+ engines: {node: '>=12'}
+
+ d3-drag@3.0.0:
+ resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==}
+ engines: {node: '>=12'}
+
+ d3-ease@3.0.1:
+ resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==}
+ engines: {node: '>=12'}
+
+ d3-interpolate@3.0.1:
+ resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
+ engines: {node: '>=12'}
+
+ d3-selection@3.0.0:
+ resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==}
+ engines: {node: '>=12'}
+
+ d3-timer@3.0.1:
+ resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
+ engines: {node: '>=12'}
+
+ d3-transition@3.0.1:
+ resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==}
+ engines: {node: '>=12'}
+ peerDependencies:
+ d3-selection: 2 - 3
+
+ d3-zoom@3.0.0:
+ resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==}
+ engines: {node: '>=12'}
+
d@1.0.2:
resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==}
engines: {node: '>=0.12'}
@@ -1582,9 +1626,6 @@ packages:
resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==}
hasBin: true
- hoist-non-react-statics@2.5.5:
- resolution: {integrity: sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw==}
-
hookable@5.5.3:
resolution: {integrity: sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==}
@@ -1927,9 +1968,6 @@ packages:
mdn-data@2.0.14:
resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==}
- medium-editor@5.23.3:
- resolution: {integrity: sha512-he9/TdjX8f8MGdXGfCs8AllrYnqXJJvjNkDKmPg3aPW/uoIrlRqtkFthrwvmd+u4QyzEiadhCCM0EwTiRdUCJw==}
-
memoize-one@6.0.0:
resolution: {integrity: sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw==}
@@ -1978,26 +2016,9 @@ packages:
mlly@1.8.2:
resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==}
- mobx-preact@3.0.0:
- resolution: {integrity: sha512-ijan/cBs3WmRye87E5+3JmoFBB00KDAwNA3pm7bMwYLPHBAXlN86aC3gdrXw8aKzM5RI8V3a993PphzPv6P4FA==}
- peerDependencies:
- mobx: 5.x
- preact: '>=8'
-
- mobx-utils@5.6.2:
- resolution: {integrity: sha512-a/WlXyGkp6F12b01sTarENpxbmlRgPHFyR1Xv2bsSjQBm5dcOtd16ONb40/vOqck8L99NHpI+C9MXQ+SZ8f+yw==}
- peerDependencies:
- mobx: ^4.13.1 || ^5.13.1
-
- mobx@5.15.7:
- resolution: {integrity: sha512-wyM3FghTkhmC+hQjyPGGFdpehrcX1KOXsDuERhfK2YbJemkUhEB+6wzEN639T21onxlfYBmriA1PFnvxTUhcKw==}
-
monaco-editor@0.55.1:
resolution: {integrity: sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==}
- mousetrap@1.6.5:
- resolution: {integrity: sha512-QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA==}
-
ms@2.0.0:
resolution: {integrity: sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==}
@@ -2162,14 +2183,6 @@ packages:
resolution: {integrity: sha512-spBB5sgC4cv2YcW03f/IAUN1pgDJWNWD8FzkyY4mArLUMJW+KlQhlmUdKAHQuPfb00Jl5xIfImeOsf6YL8QK7Q==}
engines: {node: '>=0.10.0'}
- preact@10.29.7:
- resolution: {integrity: sha512-DCHYrK/B10yUD3ZjLfhZ3WIE/9Vf9VFUODcRE2dRomTYDpJk6z6L9wecSfhfE6M9ZTHUdyQkoC46arIDhEV84Q==}
- peerDependencies:
- preact-render-to-string: '>=5'
- peerDependenciesMeta:
- preact-render-to-string:
- optional: true
-
proto-list@1.2.4:
resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==}
@@ -2200,9 +2213,6 @@ packages:
resolution: {integrity: sha512-QfazNrhMakEdRG57IoYFwffUIr04LWJxbS/ZkidRFXYCQt63c1gK6Z7IHUXMx/Vh25WgPBU42oBaNzQ0K1R/xw==}
engines: {npm: '>=8.2.3'}
- rangy@1.3.2:
- resolution: {integrity: sha512-fS1C4MOyk8T+ZJZdLcgrukPWxkyDXa+Hd2Kj+Zg4wIK71yrWgmjzHubzPMY1G+WD9EgGxMp3fIL0zQ1ickmSWA==}
-
readable-stream@3.6.2:
resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==}
engines: {node: '>= 6'}
@@ -2758,10 +2768,6 @@ packages:
resolution: {integrity: sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==}
hasBin: true
- uuid@9.0.1:
- resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==}
- hasBin: true
-
vanilla-jsoneditor@3.13.0:
resolution: {integrity: sha512-u9C+D8Ocgr39Gnsfu7USjilfKssqbDqZehMdE0qbwIFnZFdNwkU4iKeEvJxvYbmlVi/sMLxMTrU14PhtXVyiZg==}
@@ -2972,8 +2978,6 @@ snapshots:
'@antfu/utils@0.7.10': {}
- '@antv/hierarchy@0.6.14': {}
-
'@babel/helper-string-parser@7.29.7': {}
'@babel/helper-validator-identifier@7.29.7': {}
@@ -3199,33 +3203,6 @@ snapshots:
dependencies:
'@lezer/common': 1.5.2
- '@logicflow/core@2.2.4':
- dependencies:
- classnames: 2.5.1
- lodash-es: 4.18.1
- mobx: 5.15.7
- mobx-preact: 3.0.0(mobx@5.15.7)(preact@10.29.7)
- mobx-utils: 5.6.2(mobx@5.15.7)
- mousetrap: 1.6.5
- preact: 10.29.7
- uuid: 9.0.1
- transitivePeerDependencies:
- - preact-render-to-string
-
- '@logicflow/extension@2.3.0(@logicflow/core@2.2.4)':
- dependencies:
- '@antv/hierarchy': 0.6.14
- '@logicflow/core': 2.2.4
- classnames: 2.5.1
- lodash-es: 4.18.1
- medium-editor: 5.23.3
- mobx: 5.15.7
- preact: 10.29.7
- rangy: 1.3.2
- vanilla-picker: 2.12.3
- transitivePeerDependencies:
- - preact-render-to-string
-
'@marijn/find-cluster-break@1.0.3': {}
'@nodelib/fs.scandir@2.1.5':
@@ -3428,6 +3405,27 @@ snapshots:
vite: 6.4.1(@types/node@26.1.2)(sass-embedded@1.97.2)(sass@1.97.2)(yaml@2.9.0)
vue: 3.5.26
+ '@vue-flow/background@1.3.2(@vue-flow/core@1.48.2(vue@3.5.26))(vue@3.5.26)':
+ dependencies:
+ '@vue-flow/core': 1.48.2(vue@3.5.26)
+ vue: 3.5.26
+
+ '@vue-flow/controls@1.1.3(@vue-flow/core@1.48.2(vue@3.5.26))(vue@3.5.26)':
+ dependencies:
+ '@vue-flow/core': 1.48.2(vue@3.5.26)
+ vue: 3.5.26
+
+ '@vue-flow/core@1.48.2(vue@3.5.26)':
+ dependencies:
+ '@vueuse/core': 10.11.1(vue@3.5.26)
+ d3-drag: 3.0.0
+ d3-interpolate: 3.0.1
+ d3-selection: 3.0.0
+ d3-zoom: 3.0.0
+ vue: 3.5.26
+ transitivePeerDependencies:
+ - '@vue/composition-api'
+
'@vue/compiler-core@3.5.26':
dependencies:
'@babel/parser': 7.29.7
@@ -3749,8 +3747,6 @@ snapshots:
isobject: 3.0.1
static-extend: 0.1.2
- classnames@2.5.1: {}
-
clipboard@2.0.11:
dependencies:
good-listener: 1.2.2
@@ -3847,6 +3843,42 @@ snapshots:
csstype@3.2.3: {}
+ d3-color@3.1.0: {}
+
+ d3-dispatch@3.0.1: {}
+
+ d3-drag@3.0.0:
+ dependencies:
+ d3-dispatch: 3.0.1
+ d3-selection: 3.0.0
+
+ d3-ease@3.0.1: {}
+
+ d3-interpolate@3.0.1:
+ dependencies:
+ d3-color: 3.1.0
+
+ d3-selection@3.0.0: {}
+
+ d3-timer@3.0.1: {}
+
+ d3-transition@3.0.1(d3-selection@3.0.0):
+ dependencies:
+ d3-color: 3.1.0
+ d3-dispatch: 3.0.1
+ d3-ease: 3.0.1
+ d3-interpolate: 3.0.1
+ d3-selection: 3.0.0
+ d3-timer: 3.0.1
+
+ d3-zoom@3.0.0:
+ dependencies:
+ d3-dispatch: 3.0.1
+ d3-drag: 3.0.0
+ d3-interpolate: 3.0.1
+ d3-selection: 3.0.0
+ d3-transition: 3.0.1(d3-selection@3.0.0)
+
d@1.0.2:
dependencies:
es5-ext: 0.10.64
@@ -4443,8 +4475,6 @@ snapshots:
he@1.2.0: {}
- hoist-non-react-statics@2.5.5: {}
-
hookable@5.5.3: {}
htmlparser2@3.10.1:
@@ -4763,8 +4793,6 @@ snapshots:
mdn-data@2.0.14: {}
- medium-editor@5.23.3: {}
-
memoize-one@6.0.0: {}
merge-options@1.0.1:
@@ -4824,25 +4852,11 @@ snapshots:
pkg-types: 1.3.1
ufo: 1.6.4
- mobx-preact@3.0.0(mobx@5.15.7)(preact@10.29.7):
- dependencies:
- hoist-non-react-statics: 2.5.5
- mobx: 5.15.7
- preact: 10.29.7
-
- mobx-utils@5.6.2(mobx@5.15.7):
- dependencies:
- mobx: 5.15.7
-
- mobx@5.15.7: {}
-
monaco-editor@0.55.1:
dependencies:
dompurify: 3.2.7
marked: 14.0.0
- mousetrap@1.6.5: {}
-
ms@2.0.0: {}
ms@2.1.3: {}
@@ -5008,8 +5022,6 @@ snapshots:
posthtml-parser: 0.2.1
posthtml-render: 1.4.0
- preact@10.29.7: {}
-
proto-list@1.2.4: {}
proxy-from-env@1.1.0: {}
@@ -5044,8 +5056,6 @@ snapshots:
parchment: 3.0.0
quill-delta: 5.1.0
- rangy@1.3.2: {}
-
readable-stream@3.6.2:
dependencies:
inherits: 2.0.4
@@ -5732,8 +5742,6 @@ snapshots:
uuid@11.1.1: {}
- uuid@9.0.1: {}
-
vanilla-jsoneditor@3.13.0:
dependencies:
'@codemirror/autocomplete': 6.20.3
diff --git a/src/api/test/log.js b/src/api/test/log.js
index d357ed4..f1847ba 100644
--- a/src/api/test/log.js
+++ b/src/api/test/log.js
@@ -62,7 +62,7 @@ export function getInst(query) {
}
// 查询任务实例日志
-export function getLogicFlow(query) {
+export function getFlowRuntimeLog(query) {
return request({
url: '/test/inst/log/query',
method: 'get',
diff --git a/src/main.js b/src/main.js
index 6e99000..f281aea 100644
--- a/src/main.js
+++ b/src/main.js
@@ -44,6 +44,8 @@ import ImageUpload from "@/components/ImageUpload"
import ImagePreview from "@/components/ImagePreview"
// 字典标签组件
import DictTag from '@/components/DictTag'
+import { JsonViewer } from 'vue3-json-viewer'
+import 'vue3-json-viewer/dist/index.css'
import ace from 'ace-builds'
import modeJsonUrl from 'ace-builds/src-noconflict/mode-json?url'
@@ -71,6 +73,7 @@ app.component('ImageUpload', ImageUpload)
app.component('ImagePreview', ImagePreview)
app.component('RightToolbar', RightToolbar)
app.component('Editor', Editor)
+app.component('JsonViewer', JsonViewer)
app.use(router)
app.use(store)
diff --git a/src/router/index.js b/src/router/index.js
index 3d9b031..86950ba 100644
--- a/src/router/index.js
+++ b/src/router/index.js
@@ -135,18 +135,18 @@ export const constantRoutes = [
hidden: true,
},
{
- //logicFlow
+ // 流程运行状态
path: "/logic_flow",
component: () =>
- import("@/views/device/register/components/LogicFlow/index.vue"),
- meta: { title: "logicFlow", noLogin: true },
+ import("@/views/device/register/components/VueFlow/index.vue"),
+ meta: { title: "流程运行状态", noLogin: true },
hidden: true,
},
{
- //logicFlow
+ // 流程编排
path: "/flow",
component: () => import("@/views/flow/index.vue"),
- meta: { title: "flow", noLogin: true },
+ meta: { title: "流程编排", noLogin: true },
hidden: true,
},
{
diff --git a/src/utils/convertNodeRedToLogicFlow.js b/src/utils/convertNodeRedToLogicFlow.js
deleted file mode 100644
index b79ffbd..0000000
--- a/src/utils/convertNodeRedToLogicFlow.js
+++ /dev/null
@@ -1,105 +0,0 @@
-
-import { getFlowById } from '@/api/device/register'
-
-const filterNodeRedData = async (id) => {
- let nodeRedData = []
- const data = await getFlowById(id)
- if (data.nodes) {
- nodeRedData = data.nodes
- } else {
- console.error('请求失败')
- }
- return nodeRedData
-}
-
-const mapNodeType = (nodeRedType) => {
- const typeMap = {
- 'group': 'customGroup', // Node-RED的inject节点转为圆形
- // 'inject': 'circle', // Node-RED的inject节点转为圆形
- // 'debug': 'diamond', // debug节点转为菱形
- // 'function': 'rect' // 函数节点转为矩形
- };
- return typeMap[nodeRedType] || 'CustomNode'; // 默认矩形
-}
-
-export const convertNodeRedToLogicFlow = async (id) => {
- const nodeRedData = await filterNodeRedData(id)
- // 节点数据
- const nodes = nodeRedData.map((node) => {
- return {
- id: node.id, // 或生成新ID:`node-${index}`
- type: mapNodeType(node.type), // 映射节点类型
- x: node.x * 2,
- y: node.y * 2,
- text: node.name || node.type, // 使用节点名称或类型作为文本
- children: node.type === 'group' ? node.nodes : null,
- properties: {
- ...node,
- state: 'unexecuted',
- }
- };
- });
-
- const edges = []
- // 连线数据
- nodeRedData.forEach((item) => {
- if (item.wires && item.wires.length > 0) {
- item.wires.forEach((w, index) => {
- w.forEach((id) => {
- const obj = {
- id: `edge-${item.id}-${id}`, // 或使用唯一标识生成方式
- type: 'bezier',
- sourceNodeId: item.id,
- targetNodeId: id,
- sourceAnchorId: item.id + '_1',
- targetAnchorId: id + '_3'
- }
- edges.push(obj)
- })
- })
- }
- })
- return { nodes, edges }
-}
-
-export const recursiveFilter = (data, id, result = [], type = 'customGroup') => {
- data.forEach((item) => {
- if (item.type === type && item.properties.nodes && item.properties.nodes.includes(id)) {
- result.push(item)
- recursiveFilter(data, item.id, result)
- }
- });
- return result
-}
-
-export const changeOtherNodeState = (nodes, lf) => {
- const filterArr = ['inject', 'cmvrLoop', 'startCamera', 'getImage', 'stopCamera', 'startVideo', 'stopVideo', 'end']
- const loopNodes = nodes.filter(item => {
- return !filterArr.includes(item.properties.type)
- })
- loopNodes.forEach(l => {
- nodes.forEach(n => {
- if (l.properties.wires[0].includes(n.id)) {
- const { state } = lf.getProperties(n.id)
- if (state !== 'unexecuted') {
- lf.setProperties(l.id, {
- state: 'success'
- })
- }
- }
-
- if (l.properties?.wires[1]?.includes(n.id)) {
- const { state } = lf.getProperties(n.id)
- if (state !== 'failed') {
- lf.setProperties(l.id, {
- state: 'running'
- })
- } else {
- lf.setProperties(l.id, {
- state: 'failed'
- })
- }
- }
- })
- })
-}
\ No newline at end of file
diff --git a/src/utils/convertNodeRedToVueFlow.js b/src/utils/convertNodeRedToVueFlow.js
new file mode 100644
index 0000000..e00e304
--- /dev/null
+++ b/src/utils/convertNodeRedToVueFlow.js
@@ -0,0 +1,76 @@
+import { getFlowById } from "@/api/device/register";
+
+const loadNodeRedData = async (id) => {
+ const data = await getFlowById(id);
+ return Array.isArray(data?.nodes) ? data.nodes : [];
+};
+
+export const convertNodeRedToVueFlow = async (id) => {
+ const source = await loadNodeRedData(id);
+ const nodes = source.map((node) => {
+ const isGroup = node.type === "group";
+ const width = isGroup ? Number(node.w || 300) + 80 : 200;
+ const height = isGroup ? Number(node.h || 180) + 80 : 100;
+ const center = { x: Number(node.x || 0) * 2, y: Number(node.y || 0) * 2 };
+ return {
+ id: node.id,
+ type: isGroup ? "runtime-group" : "runtime-node",
+ position: { x: center.x - width / 2, y: center.y - height / 2 },
+ width,
+ height,
+ draggable: false,
+ selectable: true,
+ connectable: false,
+ deletable: false,
+ zIndex: isGroup ? -1 : 1,
+ data: {
+ text: node.name || node.type,
+ properties: { ...node, state: "unexecuted" },
+ children: isGroup ? node.nodes || [] : [],
+ },
+ };
+ });
+
+ const edges = [];
+ source.forEach((node) => {
+ (node.wires || []).forEach((targets) => {
+ (targets || []).forEach((targetId) => {
+ edges.push({
+ id: `edge-${node.id}-${targetId}`,
+ source: node.id,
+ target: targetId,
+ sourceHandle: `${node.id}_1`,
+ targetHandle: `${targetId}_3`,
+ type: "bezier",
+ animated: true,
+ selectable: true,
+ focusable: true,
+ interactionWidth: 32,
+ markerEnd: {
+ type: "arrowclosed",
+ color: "#2f80ed",
+ width: 20,
+ height: 20,
+ },
+ style: { stroke: "#2f80ed", strokeWidth: 2 },
+ });
+ });
+ });
+ });
+
+ return { nodes, edges };
+};
+
+export const updateDerivedNodeStates = (nodes, getProperties, setProperties) => {
+ const ignored = ["inject", "cmvrLoop", "startCamera", "getImage", "stopCamera", "startVideo", "stopVideo", "end"];
+ nodes.filter((node) => !ignored.includes(node.data.properties.type)).forEach((current) => {
+ nodes.forEach((target) => {
+ if (current.data.properties.wires?.[0]?.includes(target.id) && getProperties(target.id).state !== "unexecuted") {
+ setProperties(current.id, { state: "success" });
+ }
+ if (current.data.properties.wires?.[1]?.includes(target.id)) {
+ setProperties(current.id, { state: getProperties(target.id).state === "failed" ? "failed" : "running" });
+ }
+ });
+ });
+};
diff --git a/src/views/device/register/components/LogicFlow/CustomGroup.js b/src/views/device/register/components/LogicFlow/CustomGroup.js
deleted file mode 100644
index 7cc34cf..0000000
--- a/src/views/device/register/components/LogicFlow/CustomGroup.js
+++ /dev/null
@@ -1,103 +0,0 @@
-
-import { GroupNodeModel, GroupNode } from '@logicflow/extension'
-import { recursiveFilter } from "@/utils/convertNodeRedToLogicFlow";
-
-class CustomGroupModel extends GroupNodeModel {
- initNodeData(data) {
- super.initNodeData(data);
- this.children = new Set(data.children || [])
- this.zIndex = 0;
-
- this.width = data.w + 80; // 增加内边距
- this.height = data.h + 80;
- this.x = data.x;
- this.y = data.y;
-
- setTimeout(() => {
- this.updateSize()
- const nodes = this.graphModel.nodes;
- const arr = recursiveFilter(nodes, this.id)
- arr.forEach((item) => {
- item.updateSize()
- })
- }, 0)
- }
-
- setAttributes() {
- this.text.editable = false;
- }
-
- getTextStyle() {
- const style = super.getTextStyle();
- style.fill = this.properties.style?.color || '#333';
- style.fontSize = 16;
- return style;
- }
-
- updateSize() {
- const children = []
- this.children.forEach((id) => {
- children.push(this.graphModel.getNodeModelById(id))
- });
- if (children.length === 0) return;
-
- let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
- children.forEach((node) => {
- minX = Math.min(minX, node.x - node.width / 2);
- maxX = Math.max(maxX, node.x + node.width / 2);
- minY = Math.min(minY, node.y - node.height / 2);
- maxY = Math.max(maxY, node.y + node.height / 2);
- });
-
- this.width = maxX - minX + 80; // 增加内边距
- this.height = maxY - minY + 80;
- this.x = (minX + maxX) / 2;
- this.y = (minY + maxY) / 2;
- this.setTextCoordinate(this.width, this.height, this.x, this.y)
- }
-
- setTextCoordinate(width, height, x, y) {
- const strategies = {
- "n": () => {
- this.text.x = x
- this.text.y = y - height / 2 + 20
- },
- "ne": () => {
- this.text.x = x + width / 2 - 20
- this.text.y = y - height / 2 + 20
- },
- "sw": () => {
- this.text.x = x - width / 2 + 20
- this.text.y = y + height / 2 - 20
- },
- "s": () => {
- this.text.x = x
- this.text.y = y + height / 2 - 20
- },
- "se": () => {
- this.text.x = x + width / 2 - 20
- this.text.y = y + height / 2 - 20
- },
- "default": () => {
- this.text.x = x - width / 2 + 20
- this.text.y = y - height / 2 + 20
- }
- };
- const labelPosition = this.properties.style?.['label-position'] || 'default'
- const action = strategies[labelPosition]
- action();
- }
-
- removeChild(id) {
- this.children.delete(id) // 现在可安全调用
- }
-}
-
-// 导出方法注册
-export function registerCustomGroup(lf) {
- lf.register({
- type: "customGroup",
- view: GroupNode,
- model: CustomGroupModel
- })
-}
diff --git a/src/views/device/register/components/LogicFlow/customFlowNode.js b/src/views/device/register/components/LogicFlow/customFlowNode.js
deleted file mode 100644
index fa19f30..0000000
--- a/src/views/device/register/components/LogicFlow/customFlowNode.js
+++ /dev/null
@@ -1,134 +0,0 @@
-// 导入 HtmlNode 及其模型,为后续继承做准备
-import { HtmlNode, HtmlNodeModel } from '@logicflow/core';
-// 导入 Vue 相关方法,用于渲染组件
-import { createApp, h } from 'vue';
-import ElementPlus from 'element-plus'
-// 导入 Vue 组件
-import CustomFlow from './customFlow.vue';
-
-/**
- * 定义一个 元素的 HTML 节点类,继承自 HtmlNode
- * 该类负责在 HTML 中渲染 元素,并处理其交互逻辑
- */
-class CustomFlowHtmlNode extends HtmlNode {
- isMounted; // 标记组件是否已挂载
- r; // 渲染函数
- app; // Vue 应用实例
-
- /**
- * 构造函数
- * @param props 传递给节点的属性,包括模型、图模型等
- */
- constructor(props) {
- super(props);
- this.isMounted = false;
- // 创建 元素的渲染函数
- this.r = h(CustomFlow, {
- model: props.model,
- graphModel: props.graphModel,
- properties: {
- ...props.model.getProperties(),
- },
- text: props.model.text.value,
- });
-
- // 创建 Vue 应用实例,并指定渲染函数
- this.app = createApp({
- render: () => this.r
- });
- }
-
- /**
- * 将 HTML 内容设置到指定的根元素上
- * @param rootEl 根元素
- */
- setHtml(rootEl) {
- if (!this.isMounted) {
- this.isMounted = true;
- const node = document.createElement('div');
- node.style.width = '100%'
- node.style.height = '100%'
- rootEl.appendChild(node);
- this.app.use(ElementPlus) // 关键:单独注册ElementPlus
- this.app.mount(node);
- } else {
- this.r.component.props.properties = this.props.model.getProperties();
- }
- }
-
- /**
- * 获取节点文本内容
- * 对于元素,返回 null,因为其内容由特定组件渲染
- * @returns {null}
- */
- getText() {
- return null;
- }
-}
-
-/**
- * 定义一个元素的 HTML 模型类,继承自 HtmlNodeModel
- * 该类主要设置节点的属性和样式
- */
-class CustomFlowHtmlModel extends HtmlNodeModel {
- initNodeData(data) {
- super.initNodeData(data);
- // 仅允许从底部锚点连出
- this.sourceRules.push({
- message: '只能从输出锚点连线',
- validate: (sourceNode, targetNode, sourceAnchor) =>
- // sourceAnchor.name === 'right'
- sourceAnchor.properties.connectionType === 'source'
- });
-
- // 仅允许连接到顶部锚点
- this.targetRules.push({
- message: '只能连接到输入锚点',
- validate: (sourceNode, targetNode, targetAnchor) =>
- // targetAnchor.name === 'left'
- targetAnchor.properties.connectionType === 'target'
- });
- }
-
- /**
- * 设置节点属性
- * 包括宽度、高度、文本编辑属性等
- */
- setAttributes() {
- this.width = 200;
- this.height = 100;
- this.text.editable = false;
- }
-
- // 定义节点只有左右两个锚点. 锚点位置通过中心点和宽度算出来。
- getDefaultAnchor() {
- let _a = this, x = _a.x, y = _a.y, width = _a.width, height = _a.height;
- return [
- // { x: x, y: y - height / 2, id: "".concat(this.id, "_0") },
- { x: x + width / 2, y: y, name: 'left', id: "".concat(this.id, "_1"), properties: { connectionType: 'target' } },
- // { x: x, y: y + height / 2, id: "".concat(this.id, "_2") },
- { x: x - width / 2, y: y, name: 'right', id: "".concat(this.id, "_3"), properties: { connectionType: 'source' } },
- ];
- }
-
- /**
- * 获取节点轮廓样式
- * 覆盖父类方法,设置 stroke 属性为 none,以适应特定的视觉效果
- * @returns {object} 节点轮廓样式
- */
- getOutlineStyle() {
- const style = super.getOutlineStyle();
- style.stroke = 'none';
- // style.hover.stroke = 'none';
- return style;
- }
-}
-
-// 导出方法注册
-export function registerCustomNode(lf) {
- lf.register({
- type: "CustomNode",
- view: CustomFlowHtmlNode,
- model: CustomFlowHtmlModel
- })
-}
\ No newline at end of file
diff --git a/src/views/device/register/components/LogicFlow/index.vue b/src/views/device/register/components/LogicFlow/index.vue
deleted file mode 100644
index facfd2a..0000000
--- a/src/views/device/register/components/LogicFlow/index.vue
+++ /dev/null
@@ -1,294 +0,0 @@
-
-