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 @@ - - - \ No newline at end of file diff --git a/src/views/device/register/components/LogicFlow/IPlayer/config.ts b/src/views/device/register/components/VueFlow/IPlayer/config.ts similarity index 100% rename from src/views/device/register/components/LogicFlow/IPlayer/config.ts rename to src/views/device/register/components/VueFlow/IPlayer/config.ts diff --git a/src/views/device/register/components/LogicFlow/IPlayer/index.vue b/src/views/device/register/components/VueFlow/IPlayer/index.vue similarity index 100% rename from src/views/device/register/components/LogicFlow/IPlayer/index.vue rename to src/views/device/register/components/VueFlow/IPlayer/index.vue diff --git a/src/views/device/register/components/VueFlow/RuntimeGroup.vue b/src/views/device/register/components/VueFlow/RuntimeGroup.vue new file mode 100644 index 0000000..dca777e --- /dev/null +++ b/src/views/device/register/components/VueFlow/RuntimeGroup.vue @@ -0,0 +1,24 @@ + + + + + diff --git a/src/views/device/register/components/VueFlow/RuntimeNode.vue b/src/views/device/register/components/VueFlow/RuntimeNode.vue new file mode 100644 index 0000000..6ab3e75 --- /dev/null +++ b/src/views/device/register/components/VueFlow/RuntimeNode.vue @@ -0,0 +1,47 @@ + + + + + diff --git a/src/views/device/register/components/LogicFlow/customFlow.vue b/src/views/device/register/components/VueFlow/customFlow.vue similarity index 100% rename from src/views/device/register/components/LogicFlow/customFlow.vue rename to src/views/device/register/components/VueFlow/customFlow.vue diff --git a/src/views/device/register/components/VueFlow/index.vue b/src/views/device/register/components/VueFlow/index.vue new file mode 100644 index 0000000..9ae8f5d --- /dev/null +++ b/src/views/device/register/components/VueFlow/index.vue @@ -0,0 +1,206 @@ + + + + + diff --git a/src/views/flow/components/NodeState.vue b/src/views/flow/components/NodeState.vue index 201e671..5d3b5b4 100644 --- a/src/views/flow/components/NodeState.vue +++ b/src/views/flow/components/NodeState.vue @@ -86,7 +86,7 @@ const showState = ref('NORMAL') watch(() => props.state, (newVal) => { showState.value = newVal; -}); +}, { immediate: true }); const closePopover = () => { showState.value = 'NORMAL'; diff --git a/src/views/flow/components/NodeTitle.vue b/src/views/flow/components/NodeTitle.vue index 82022b3..50f32f3 100644 --- a/src/views/flow/components/NodeTitle.vue +++ b/src/views/flow/components/NodeTitle.vue @@ -150,14 +150,14 @@ const execute = () => { } const handleInputFocus = (e) => { - // 阻止事件冒泡到 LogicFlow + // 阻止事件冒泡到流程画布 e.stopPropagation() e.preventDefault() } const handleInputKeydown = (e) => { - // 阻止事件冒泡到 Logic Flow 节点,避免被其事件拦截 + // 阻止事件冒泡到流程节点,避免被画布快捷键拦截 e.stopPropagation(); // 可选:明确放行 Ctrl+V(增强兼容性) if ((e.ctrlKey || e.metaKey) && e.key === "v") { diff --git a/src/views/flow/config.js b/src/views/flow/config.js index 68ce3a3..02cb59b 100644 --- a/src/views/flow/config.js +++ b/src/views/flow/config.js @@ -1,8 +1,3 @@ -import { Group, SelectionSelect } from "@logicflow/extension"; -import { registerCommon } from "./nodes/common/index"; - -import { registerFunction } from './nodes/function' - import cameraSvg from './icon/camera.svg' import videoSvg from './icon/video.svg' import loopSvg from './icon/loop.svg' @@ -19,66 +14,6 @@ import audioSvg from './icon/audio.svg' import touchSvg from './icon/touch.svg' import expressionSvg from './icon/expression.svg' -import { v4 as randomUUID } from 'uuid' - -export const lfConfig = { - idGenerator: () => { - // 生成标准UUID并移除连字符 - // return crypto.randomUUID().replace(/-/g, ''); - return randomUUID().replace(/-/g, '') - }, - background: { - backgroundColor: "#f6f8fa", - }, - grid: false, - plugins: [Group, SelectionSelect], - animation: true, - adjustEdgeStartAndEnd: false, - // multipleSelectKey: "ctrl", - // disabledTools: ["multipleSelect"], - partial: true, - // group: { - // foldable: true, // 启用折叠功能 - // foldSize: 30, // 折叠后显示的图标尺寸 - // }, - edgeType: "bezier", - zoomConfig: { - min: 0.05, // 最小缩放比例(支持更小值,如 0.01,但不建议过小) - max: 3, // 最大缩放比例(可自定义) - step: 0.1 // 每次滚轮缩放的步长(默认 0.1) - }, - style: { - anchor: { - show: true, // 强制全局锚点显示 - hoverOn: false, - visibility: "visible", - fill: "#FF5722", // 实心绿色 - stroke: "#FF5722", // 边框同色 - strokeWidth: 0, // 消除边框 - r: 7, // 锚点半径 - hover: { fill: "#FF5722" }, // 禁用悬停变色 - }, - anchorLine: { - stroke: "#FF5722", - strokeWidth: 2, - strokeDasharray: "3,2", - }, - bezier: { - stroke: "#FF5722", - strokeWidth: 2, - arrow: false, // 关闭箭头显示 - animation: true, // 开启动画效果 - animationSpeed: 3, - }, - }, -}; - -// 注册自定义节点 -export const registerCustomizeNode = (lf) => { - registerCommon(lf) - registerFunction(lf) -}; - const deviceOptions = () => { return [{ value: 'cam1', diff --git a/src/views/flow/index.vue b/src/views/flow/index.vue index 910ba7d..3910859 100644 --- a/src/views/flow/index.vue +++ b/src/views/flow/index.vue @@ -71,12 +71,45 @@