mirror of
https://github.com/whyour/qinglong.git
synced 2025-11-10 00:26:09 +08:00
Implement visual workflow editor with React Flow and custom node types
Co-authored-by: whyour <22700758+whyour@users.noreply.github.com>
This commit is contained in:
parent
00b1bc71e0
commit
5ed2e5b809
47
package.json
47
package.json
|
|
@ -54,12 +54,15 @@
|
|||
}
|
||||
},
|
||||
"dependencies": {
|
||||
"@bufbuild/protobuf": "^2.10.0",
|
||||
"@grpc/grpc-js": "^1.14.0",
|
||||
"@grpc/proto-loader": "^0.8.0",
|
||||
"@keyv/sqlite": "^4.0.1",
|
||||
"@otplib/preset-default": "^12.0.1",
|
||||
"body-parser": "^1.20.3",
|
||||
"celebrate": "^15.0.3",
|
||||
"chokidar": "^4.0.1",
|
||||
"compression": "^1.7.4",
|
||||
"cors": "^2.8.5",
|
||||
"cron-parser": "^4.9.0",
|
||||
"cross-spawn": "^7.0.6",
|
||||
|
|
@ -69,54 +72,51 @@
|
|||
"express-jwt": "^8.4.1",
|
||||
"express-rate-limit": "^7.4.1",
|
||||
"express-urlrewrite": "^2.0.3",
|
||||
"undici": "^7.9.0",
|
||||
"helmet": "^8.1.0",
|
||||
"hpagent": "^1.2.0",
|
||||
"http-proxy-middleware": "^3.0.3",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"ip2region": "2.3.0",
|
||||
"js-yaml": "^4.1.0",
|
||||
"jsonwebtoken": "^9.0.2",
|
||||
"keyv": "^5.2.3",
|
||||
"lodash": "^4.17.21",
|
||||
"multer": "1.4.5-lts.1",
|
||||
"node-schedule": "^2.1.0",
|
||||
"nodemailer": "^6.9.16",
|
||||
"p-queue-cjs": "7.3.4",
|
||||
"@bufbuild/protobuf": "^2.10.0",
|
||||
"proper-lockfile": "^4.1.2",
|
||||
"ps-tree": "^1.2.0",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"request-ip": "3.3.0",
|
||||
"sequelize": "^6.37.5",
|
||||
"sockjs": "^0.3.24",
|
||||
"sqlite3": "git+https://github.com/whyour/node-sqlite3.git#v1.0.3",
|
||||
"toad-scheduler": "^3.0.1",
|
||||
"typedi": "^0.10.0",
|
||||
"undici": "^7.9.0",
|
||||
"uuid": "^11.0.3",
|
||||
"winston": "^3.17.0",
|
||||
"winston-daily-rotate-file": "^5.0.0",
|
||||
"request-ip": "3.3.0",
|
||||
"ip2region": "2.3.0",
|
||||
"keyv": "^5.2.3",
|
||||
"@keyv/sqlite": "^4.0.1",
|
||||
"proper-lockfile": "^4.1.2",
|
||||
"compression": "^1.7.4",
|
||||
"helmet": "^8.1.0"
|
||||
"winston-daily-rotate-file": "^5.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@flowgram.ai/free-layout-editor": "^1.0.2",
|
||||
"@flowgram.ai/core": "^1.0.2",
|
||||
"@flowgram.ai/reactive": "^1.0.2",
|
||||
"moment": "2.30.1",
|
||||
"@ant-design/icons": "^5.0.1",
|
||||
"@ant-design/pro-layout": "6.38.22",
|
||||
"@codemirror/view": "^6.34.1",
|
||||
"@codemirror/state": "^6.4.1",
|
||||
"@codemirror/view": "^6.34.1",
|
||||
"@flowgram.ai/core": "^1.0.2",
|
||||
"@flowgram.ai/free-layout-editor": "^1.0.2",
|
||||
"@flowgram.ai/reactive": "^1.0.2",
|
||||
"@monaco-editor/react": "4.2.1",
|
||||
"@react-hook/resize-observer": "^2.0.2",
|
||||
"react-router-dom": "6.26.1",
|
||||
"@types/body-parser": "^1.19.2",
|
||||
"@types/compression": "^1.7.2",
|
||||
"@types/cors": "^2.8.12",
|
||||
"@types/cross-spawn": "^6.0.2",
|
||||
"@types/express": "^4.17.13",
|
||||
"@types/express-jwt": "^6.0.4",
|
||||
"@types/file-saver": "2.0.2",
|
||||
"@types/helmet": "^4.0.0",
|
||||
"@types/js-yaml": "^4.0.5",
|
||||
"@types/jsonwebtoken": "^8.5.8",
|
||||
"@types/lodash": "^4.14.185",
|
||||
|
|
@ -124,17 +124,17 @@
|
|||
"@types/node": "^17.0.21",
|
||||
"@types/node-schedule": "^1.3.2",
|
||||
"@types/nodemailer": "^6.4.4",
|
||||
"@types/proper-lockfile": "^4.1.4",
|
||||
"@types/ps-tree": "^1.1.6",
|
||||
"@types/qrcode.react": "^1.0.2",
|
||||
"@types/react": "^18.0.20",
|
||||
"@types/react-copy-to-clipboard": "^5.0.4",
|
||||
"@types/react-dom": "^18.0.6",
|
||||
"@types/request-ip": "0.0.41",
|
||||
"@types/serve-handler": "^6.1.1",
|
||||
"@types/sockjs": "^0.3.33",
|
||||
"@types/sockjs-client": "^1.5.1",
|
||||
"@types/uuid": "^8.3.4",
|
||||
"@types/request-ip": "0.0.41",
|
||||
"@types/proper-lockfile": "^4.1.4",
|
||||
"@types/ps-tree": "^1.1.6",
|
||||
"@uiw/codemirror-extensions-langs": "^4.21.9",
|
||||
"@uiw/react-codemirror": "^4.21.9",
|
||||
"@umijs/max": "^4.4.4",
|
||||
|
|
@ -146,9 +146,9 @@
|
|||
"axios": "^1.4.0",
|
||||
"compression-webpack-plugin": "9.2.0",
|
||||
"concurrently": "^7.0.0",
|
||||
"react-hotkeys-hook": "^4.6.1",
|
||||
"file-saver": "2.0.2",
|
||||
"lint-staged": "^13.0.3",
|
||||
"moment": "2.30.1",
|
||||
"monaco-editor": "0.33.0",
|
||||
"nodemon": "^3.0.1",
|
||||
"prettier": "^2.5.1",
|
||||
|
|
@ -164,16 +164,17 @@
|
|||
"react-dnd": "^16.0.1",
|
||||
"react-dnd-html5-backend": "^16.0.1",
|
||||
"react-dom": "18.3.1",
|
||||
"react-hotkeys-hook": "^4.6.1",
|
||||
"react-intl-universal": "^2.12.0",
|
||||
"react-router-dom": "6.26.1",
|
||||
"react-split-pane": "^0.1.92",
|
||||
"reactflow": "^11.11.4",
|
||||
"sockjs-client": "^1.6.0",
|
||||
"ts-node": "^10.9.2",
|
||||
"ts-proto": "^2.6.1",
|
||||
"tslib": "^2.4.0",
|
||||
"typescript": "5.2.2",
|
||||
"vh-check": "^2.0.5",
|
||||
"virtualizedtableforantd4": "1.3.0",
|
||||
"@types/compression": "^1.7.2",
|
||||
"@types/helmet": "^4.0.0"
|
||||
"virtualizedtableforantd4": "1.3.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
461
pnpm-lock.yaml
461
pnpm-lock.yaml
|
|
@ -348,6 +348,9 @@ importers:
|
|||
react-split-pane:
|
||||
specifier: ^0.1.92
|
||||
version: 0.1.92(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
reactflow:
|
||||
specifier: ^11.11.4
|
||||
version: 11.11.4(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
sockjs-client:
|
||||
specifier: ^1.6.0
|
||||
version: 1.6.1
|
||||
|
|
@ -2030,6 +2033,42 @@ packages:
|
|||
peerDependencies:
|
||||
react: '>=18'
|
||||
|
||||
'@reactflow/background@11.3.14':
|
||||
resolution: {integrity: sha512-Gewd7blEVT5Lh6jqrvOgd4G6Qk17eGKQfsDXgyRSqM+CTwDqRldG2LsWN4sNeno6sbqVIC2fZ+rAUBFA9ZEUDA==}
|
||||
peerDependencies:
|
||||
react: '>=17'
|
||||
react-dom: '>=17'
|
||||
|
||||
'@reactflow/controls@11.2.14':
|
||||
resolution: {integrity: sha512-MiJp5VldFD7FrqaBNIrQ85dxChrG6ivuZ+dcFhPQUwOK3HfYgX2RHdBua+gx+40p5Vw5It3dVNp/my4Z3jF0dw==}
|
||||
peerDependencies:
|
||||
react: '>=17'
|
||||
react-dom: '>=17'
|
||||
|
||||
'@reactflow/core@11.11.4':
|
||||
resolution: {integrity: sha512-H4vODklsjAq3AMq6Np4LE12i1I4Ta9PrDHuBR9GmL8uzTt2l2jh4CiQbEMpvMDcp7xi4be0hgXj+Ysodde/i7Q==}
|
||||
peerDependencies:
|
||||
react: '>=17'
|
||||
react-dom: '>=17'
|
||||
|
||||
'@reactflow/minimap@11.7.14':
|
||||
resolution: {integrity: sha512-mpwLKKrEAofgFJdkhwR5UQ1JYWlcAAL/ZU/bctBkuNTT1yqV+y0buoNVImsRehVYhJwffSWeSHaBR5/GJjlCSQ==}
|
||||
peerDependencies:
|
||||
react: '>=17'
|
||||
react-dom: '>=17'
|
||||
|
||||
'@reactflow/node-resizer@2.2.14':
|
||||
resolution: {integrity: sha512-fwqnks83jUlYr6OHcdFEedumWKChTHRGw/kbCxj0oqBd+ekfs+SIp4ddyNU0pdx96JIm5iNFS0oNrmEiJbbSaA==}
|
||||
peerDependencies:
|
||||
react: '>=17'
|
||||
react-dom: '>=17'
|
||||
|
||||
'@reactflow/node-toolbar@1.3.14':
|
||||
resolution: {integrity: sha512-rbynXQnH/xFNu4P9H+hVqlEUafDCkEoCy0Dg9mG22Sg+rY/0ck6KkrAQrYrTgXusd+cEJOMK0uOOFCK2/5rSGQ==}
|
||||
peerDependencies:
|
||||
react: '>=17'
|
||||
react-dom: '>=17'
|
||||
|
||||
'@remix-run/router@1.19.1':
|
||||
resolution: {integrity: sha512-S45oynt/WH19bHbIXjtli6QmwNYvaz+vtnubvNpNDvUOoA/OWh6j1OikIP3G+v5GHdxyC6EXoChG3HgYGEUfcg==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
|
@ -2423,6 +2462,99 @@ packages:
|
|||
'@types/cross-spawn@6.0.6':
|
||||
resolution: {integrity: sha512-fXRhhUkG4H3TQk5dBhQ7m/JDdSNHKwR2BBia62lhwEIq9xGiQKLxd6LymNhn47SjXhsUEPmxi+PKw2OkW4LLjA==}
|
||||
|
||||
'@types/d3-array@3.2.2':
|
||||
resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==}
|
||||
|
||||
'@types/d3-axis@3.0.6':
|
||||
resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==}
|
||||
|
||||
'@types/d3-brush@3.0.6':
|
||||
resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==}
|
||||
|
||||
'@types/d3-chord@3.0.6':
|
||||
resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==}
|
||||
|
||||
'@types/d3-color@3.1.3':
|
||||
resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==}
|
||||
|
||||
'@types/d3-contour@3.0.6':
|
||||
resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==}
|
||||
|
||||
'@types/d3-delaunay@6.0.4':
|
||||
resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==}
|
||||
|
||||
'@types/d3-dispatch@3.0.7':
|
||||
resolution: {integrity: sha512-5o9OIAdKkhN1QItV2oqaE5KMIiXAvDWBDPrD85e58Qlz1c1kI/J0NcqbEG88CoTwJrYe7ntUCVfeUl2UJKbWgA==}
|
||||
|
||||
'@types/d3-drag@3.0.7':
|
||||
resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==}
|
||||
|
||||
'@types/d3-dsv@3.0.7':
|
||||
resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==}
|
||||
|
||||
'@types/d3-ease@3.0.2':
|
||||
resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==}
|
||||
|
||||
'@types/d3-fetch@3.0.7':
|
||||
resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==}
|
||||
|
||||
'@types/d3-force@3.0.10':
|
||||
resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==}
|
||||
|
||||
'@types/d3-format@3.0.4':
|
||||
resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==}
|
||||
|
||||
'@types/d3-geo@3.1.0':
|
||||
resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==}
|
||||
|
||||
'@types/d3-hierarchy@3.1.7':
|
||||
resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==}
|
||||
|
||||
'@types/d3-interpolate@3.0.4':
|
||||
resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==}
|
||||
|
||||
'@types/d3-path@3.1.1':
|
||||
resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==}
|
||||
|
||||
'@types/d3-polygon@3.0.2':
|
||||
resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==}
|
||||
|
||||
'@types/d3-quadtree@3.0.6':
|
||||
resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==}
|
||||
|
||||
'@types/d3-random@3.0.3':
|
||||
resolution: {integrity: sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==}
|
||||
|
||||
'@types/d3-scale-chromatic@3.1.0':
|
||||
resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==}
|
||||
|
||||
'@types/d3-scale@4.0.9':
|
||||
resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==}
|
||||
|
||||
'@types/d3-selection@3.0.11':
|
||||
resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==}
|
||||
|
||||
'@types/d3-shape@3.1.7':
|
||||
resolution: {integrity: sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==}
|
||||
|
||||
'@types/d3-time-format@4.0.3':
|
||||
resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==}
|
||||
|
||||
'@types/d3-time@3.0.4':
|
||||
resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==}
|
||||
|
||||
'@types/d3-timer@3.0.2':
|
||||
resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==}
|
||||
|
||||
'@types/d3-transition@3.0.9':
|
||||
resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==}
|
||||
|
||||
'@types/d3-zoom@3.0.8':
|
||||
resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==}
|
||||
|
||||
'@types/d3@7.4.3':
|
||||
resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==}
|
||||
|
||||
'@types/debug@4.1.12':
|
||||
resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==}
|
||||
|
||||
|
|
@ -2451,6 +2583,9 @@ packages:
|
|||
'@types/file-saver@2.0.2':
|
||||
resolution: {integrity: sha512-xbqnZmGrCEqi/KUzOkeUSe77p7APvLuyellGaAoeww3CHJ1AbjQWjPSCFtKIzZn8L7LpEax4NXnC+gfa6nM7IA==}
|
||||
|
||||
'@types/geojson@7946.0.16':
|
||||
resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==}
|
||||
|
||||
'@types/graceful-fs@4.1.9':
|
||||
resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==}
|
||||
|
||||
|
|
@ -3601,6 +3736,9 @@ packages:
|
|||
resolution: {integrity: sha512-Mz9QMT5fJe7bKI7MH31UilT5cEK5EHHRCccw/YRFsRY47AuNgaV6HY3rscp0/I4Q+tTW/5zoqpSeRRI54TkDWA==}
|
||||
engines: {node: '>= 0.10'}
|
||||
|
||||
classcat@5.0.5:
|
||||
resolution: {integrity: sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w==}
|
||||
|
||||
classnames@2.5.1:
|
||||
resolution: {integrity: sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==}
|
||||
|
||||
|
|
@ -3922,9 +4060,47 @@ packages:
|
|||
d3-array@1.2.4:
|
||||
resolution: {integrity: sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==}
|
||||
|
||||
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-polygon@1.0.6:
|
||||
resolution: {integrity: sha512-k+RF7WvI08PC8reEoXa/w2nSg5AUMTi+peBD9cmFc+0ixHfbs4QmxxkarVal1IkVkgxVuk9JSHhJURHiyHKAuQ==}
|
||||
|
||||
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'}
|
||||
|
|
@ -7175,6 +7351,12 @@ packages:
|
|||
peerDependencies:
|
||||
react: '*'
|
||||
|
||||
reactflow@11.11.4:
|
||||
resolution: {integrity: sha512-70FOtJkUWH3BAOsN+LU9lCrKoKbtOPnz2uq0CV2PLdNSwxTXOhCbsZr50GmZ+Rtw3jx8Uv7/vBFtCGixLfd4Og==}
|
||||
peerDependencies:
|
||||
react: '>=17'
|
||||
react-dom: '>=17'
|
||||
|
||||
read-pkg-up@7.0.1:
|
||||
resolution: {integrity: sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==}
|
||||
engines: {node: '>=8'}
|
||||
|
|
@ -8567,6 +8749,21 @@ packages:
|
|||
zod@3.25.76:
|
||||
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
|
||||
|
||||
zustand@4.5.7:
|
||||
resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==}
|
||||
engines: {node: '>=12.7.0'}
|
||||
peerDependencies:
|
||||
'@types/react': '>=16.8'
|
||||
immer: '>=9.0.6'
|
||||
react: '>=16.8'
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
immer:
|
||||
optional: true
|
||||
react:
|
||||
optional: true
|
||||
|
||||
snapshots:
|
||||
|
||||
'@ahooksjs/use-request@2.8.15(react@18.3.1)':
|
||||
|
|
@ -11033,6 +11230,84 @@ snapshots:
|
|||
'@react-hook/passive-layout-effect': 1.2.1(react@18.3.1)
|
||||
react: 18.3.1
|
||||
|
||||
'@reactflow/background@11.3.14(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@reactflow/core': 11.11.4(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
classcat: 5.0.5
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
zustand: 4.5.7(@types/react@18.3.26)(react@18.3.1)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- immer
|
||||
|
||||
'@reactflow/controls@11.2.14(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@reactflow/core': 11.11.4(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
classcat: 5.0.5
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
zustand: 4.5.7(@types/react@18.3.26)(react@18.3.1)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- immer
|
||||
|
||||
'@reactflow/core@11.11.4(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@types/d3': 7.4.3
|
||||
'@types/d3-drag': 3.0.7
|
||||
'@types/d3-selection': 3.0.11
|
||||
'@types/d3-zoom': 3.0.8
|
||||
classcat: 5.0.5
|
||||
d3-drag: 3.0.0
|
||||
d3-selection: 3.0.0
|
||||
d3-zoom: 3.0.0
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
zustand: 4.5.7(@types/react@18.3.26)(react@18.3.1)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- immer
|
||||
|
||||
'@reactflow/minimap@11.7.14(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@reactflow/core': 11.11.4(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@types/d3-selection': 3.0.11
|
||||
'@types/d3-zoom': 3.0.8
|
||||
classcat: 5.0.5
|
||||
d3-selection: 3.0.0
|
||||
d3-zoom: 3.0.0
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
zustand: 4.5.7(@types/react@18.3.26)(react@18.3.1)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- immer
|
||||
|
||||
'@reactflow/node-resizer@2.2.14(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@reactflow/core': 11.11.4(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
classcat: 5.0.5
|
||||
d3-drag: 3.0.0
|
||||
d3-selection: 3.0.0
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
zustand: 4.5.7(@types/react@18.3.26)(react@18.3.1)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- immer
|
||||
|
||||
'@reactflow/node-toolbar@1.3.14(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)':
|
||||
dependencies:
|
||||
'@reactflow/core': 11.11.4(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
classcat: 5.0.5
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
zustand: 4.5.7(@types/react@18.3.26)(react@18.3.1)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- immer
|
||||
|
||||
'@remix-run/router@1.19.1': {}
|
||||
|
||||
'@replit/codemirror-lang-nix@6.0.1(@codemirror/autocomplete@6.19.1)(@codemirror/language@6.11.3)(@codemirror/state@6.5.2)(@codemirror/view@6.38.6)(@lezer/common@1.3.0)(@lezer/highlight@1.2.3)(@lezer/lr@1.4.3)':
|
||||
|
|
@ -11534,6 +11809,123 @@ snapshots:
|
|||
dependencies:
|
||||
'@types/node': 17.0.45
|
||||
|
||||
'@types/d3-array@3.2.2': {}
|
||||
|
||||
'@types/d3-axis@3.0.6':
|
||||
dependencies:
|
||||
'@types/d3-selection': 3.0.11
|
||||
|
||||
'@types/d3-brush@3.0.6':
|
||||
dependencies:
|
||||
'@types/d3-selection': 3.0.11
|
||||
|
||||
'@types/d3-chord@3.0.6': {}
|
||||
|
||||
'@types/d3-color@3.1.3': {}
|
||||
|
||||
'@types/d3-contour@3.0.6':
|
||||
dependencies:
|
||||
'@types/d3-array': 3.2.2
|
||||
'@types/geojson': 7946.0.16
|
||||
|
||||
'@types/d3-delaunay@6.0.4': {}
|
||||
|
||||
'@types/d3-dispatch@3.0.7': {}
|
||||
|
||||
'@types/d3-drag@3.0.7':
|
||||
dependencies:
|
||||
'@types/d3-selection': 3.0.11
|
||||
|
||||
'@types/d3-dsv@3.0.7': {}
|
||||
|
||||
'@types/d3-ease@3.0.2': {}
|
||||
|
||||
'@types/d3-fetch@3.0.7':
|
||||
dependencies:
|
||||
'@types/d3-dsv': 3.0.7
|
||||
|
||||
'@types/d3-force@3.0.10': {}
|
||||
|
||||
'@types/d3-format@3.0.4': {}
|
||||
|
||||
'@types/d3-geo@3.1.0':
|
||||
dependencies:
|
||||
'@types/geojson': 7946.0.16
|
||||
|
||||
'@types/d3-hierarchy@3.1.7': {}
|
||||
|
||||
'@types/d3-interpolate@3.0.4':
|
||||
dependencies:
|
||||
'@types/d3-color': 3.1.3
|
||||
|
||||
'@types/d3-path@3.1.1': {}
|
||||
|
||||
'@types/d3-polygon@3.0.2': {}
|
||||
|
||||
'@types/d3-quadtree@3.0.6': {}
|
||||
|
||||
'@types/d3-random@3.0.3': {}
|
||||
|
||||
'@types/d3-scale-chromatic@3.1.0': {}
|
||||
|
||||
'@types/d3-scale@4.0.9':
|
||||
dependencies:
|
||||
'@types/d3-time': 3.0.4
|
||||
|
||||
'@types/d3-selection@3.0.11': {}
|
||||
|
||||
'@types/d3-shape@3.1.7':
|
||||
dependencies:
|
||||
'@types/d3-path': 3.1.1
|
||||
|
||||
'@types/d3-time-format@4.0.3': {}
|
||||
|
||||
'@types/d3-time@3.0.4': {}
|
||||
|
||||
'@types/d3-timer@3.0.2': {}
|
||||
|
||||
'@types/d3-transition@3.0.9':
|
||||
dependencies:
|
||||
'@types/d3-selection': 3.0.11
|
||||
|
||||
'@types/d3-zoom@3.0.8':
|
||||
dependencies:
|
||||
'@types/d3-interpolate': 3.0.4
|
||||
'@types/d3-selection': 3.0.11
|
||||
|
||||
'@types/d3@7.4.3':
|
||||
dependencies:
|
||||
'@types/d3-array': 3.2.2
|
||||
'@types/d3-axis': 3.0.6
|
||||
'@types/d3-brush': 3.0.6
|
||||
'@types/d3-chord': 3.0.6
|
||||
'@types/d3-color': 3.1.3
|
||||
'@types/d3-contour': 3.0.6
|
||||
'@types/d3-delaunay': 6.0.4
|
||||
'@types/d3-dispatch': 3.0.7
|
||||
'@types/d3-drag': 3.0.7
|
||||
'@types/d3-dsv': 3.0.7
|
||||
'@types/d3-ease': 3.0.2
|
||||
'@types/d3-fetch': 3.0.7
|
||||
'@types/d3-force': 3.0.10
|
||||
'@types/d3-format': 3.0.4
|
||||
'@types/d3-geo': 3.1.0
|
||||
'@types/d3-hierarchy': 3.1.7
|
||||
'@types/d3-interpolate': 3.0.4
|
||||
'@types/d3-path': 3.1.1
|
||||
'@types/d3-polygon': 3.0.2
|
||||
'@types/d3-quadtree': 3.0.6
|
||||
'@types/d3-random': 3.0.3
|
||||
'@types/d3-scale': 4.0.9
|
||||
'@types/d3-scale-chromatic': 3.1.0
|
||||
'@types/d3-selection': 3.0.11
|
||||
'@types/d3-shape': 3.1.7
|
||||
'@types/d3-time': 3.0.4
|
||||
'@types/d3-time-format': 4.0.3
|
||||
'@types/d3-timer': 3.0.2
|
||||
'@types/d3-transition': 3.0.9
|
||||
'@types/d3-zoom': 3.0.8
|
||||
|
||||
'@types/debug@4.1.12':
|
||||
dependencies:
|
||||
'@types/ms': 2.1.0
|
||||
|
|
@ -11575,6 +11967,8 @@ snapshots:
|
|||
|
||||
'@types/file-saver@2.0.2': {}
|
||||
|
||||
'@types/geojson@7946.0.16': {}
|
||||
|
||||
'@types/graceful-fs@4.1.9':
|
||||
dependencies:
|
||||
'@types/node': 17.0.45
|
||||
|
|
@ -12008,7 +12402,7 @@ snapshots:
|
|||
postcss-preset-env: 7.5.0(postcss@8.5.6)
|
||||
rollup-plugin-visualizer: 5.9.0(rollup@3.29.5)
|
||||
systemjs: 6.15.1
|
||||
vite: 4.5.2(@types/node@17.0.45)(less@4.4.2)(lightningcss@1.22.1)(sass@1.54.0)(terser@5.44.1)
|
||||
vite: 4.5.2(@types/node@17.0.45)(less@4.1.3)(lightningcss@1.22.1)(sass@1.54.0)(terser@5.44.1)
|
||||
transitivePeerDependencies:
|
||||
- '@types/node'
|
||||
- lightningcss
|
||||
|
|
@ -12523,7 +12917,7 @@ snapshots:
|
|||
'@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.28.5)
|
||||
'@babel/plugin-transform-react-jsx-source': 7.27.1(@babel/core@7.28.5)
|
||||
react-refresh: 0.14.2
|
||||
vite: 4.5.2(@types/node@17.0.45)(less@4.4.2)(lightningcss@1.22.1)(sass@1.54.0)(terser@5.44.1)
|
||||
vite: 4.5.2(@types/node@17.0.45)(less@4.1.3)(lightningcss@1.22.1)(sass@1.54.0)(terser@5.44.1)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
|
|
@ -13332,6 +13726,8 @@ snapshots:
|
|||
safe-buffer: 5.2.1
|
||||
to-buffer: 1.2.2
|
||||
|
||||
classcat@5.0.5: {}
|
||||
|
||||
classnames@2.5.1: {}
|
||||
|
||||
clean-css@5.3.3:
|
||||
|
|
@ -13681,8 +14077,44 @@ snapshots:
|
|||
|
||||
d3-array@1.2.4: {}
|
||||
|
||||
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-polygon@1.0.6: {}
|
||||
|
||||
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
|
||||
|
|
@ -17469,6 +17901,20 @@ snapshots:
|
|||
lodash: 4.17.21
|
||||
react: 18.3.1
|
||||
|
||||
reactflow@11.11.4(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1):
|
||||
dependencies:
|
||||
'@reactflow/background': 11.3.14(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@reactflow/controls': 11.2.14(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@reactflow/core': 11.11.4(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@reactflow/minimap': 11.7.14(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@reactflow/node-resizer': 2.2.14(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
'@reactflow/node-toolbar': 1.3.14(@types/react@18.3.26)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
|
||||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- immer
|
||||
|
||||
read-pkg-up@7.0.1:
|
||||
dependencies:
|
||||
find-up: 4.1.0
|
||||
|
|
@ -18760,7 +19206,7 @@ snapshots:
|
|||
react: 18.3.1
|
||||
react-dom: 18.3.1(react@18.3.1)
|
||||
|
||||
vite@4.5.2(@types/node@17.0.45)(less@4.4.2)(lightningcss@1.22.1)(sass@1.54.0)(terser@5.44.1):
|
||||
vite@4.5.2(@types/node@17.0.45)(less@4.1.3)(lightningcss@1.22.1)(sass@1.54.0)(terser@5.44.1):
|
||||
dependencies:
|
||||
esbuild: 0.18.20
|
||||
postcss: 8.5.6
|
||||
|
|
@ -18768,7 +19214,7 @@ snapshots:
|
|||
optionalDependencies:
|
||||
'@types/node': 17.0.45
|
||||
fsevents: 2.3.3
|
||||
less: 4.4.2
|
||||
less: 4.1.3
|
||||
lightningcss: 1.22.1
|
||||
sass: 1.54.0
|
||||
terser: 5.44.1
|
||||
|
|
@ -19003,3 +19449,10 @@ snapshots:
|
|||
zod: 3.25.76
|
||||
|
||||
zod@3.25.76: {}
|
||||
|
||||
zustand@4.5.7(@types/react@18.3.26)(react@18.3.1):
|
||||
dependencies:
|
||||
use-sync-external-store: 1.6.0(react@18.3.1)
|
||||
optionalDependencies:
|
||||
'@types/react': 18.3.26
|
||||
react: 18.3.1
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ import {
|
|||
} from '@ant-design/icons';
|
||||
import { request } from '@/utils/http';
|
||||
import intl from 'react-intl-universal';
|
||||
import ScenarioModal from './flowgramModal';
|
||||
import ScenarioModal from './visualWorkflowModal';
|
||||
import ScenarioLogModal from './logModal';
|
||||
import dayjs from 'dayjs';
|
||||
|
||||
|
|
|
|||
299
src/pages/scenario/nodeTypes.tsx
Normal file
299
src/pages/scenario/nodeTypes.tsx
Normal file
|
|
@ -0,0 +1,299 @@
|
|||
// Custom Node Type Definitions for Flowgram Workflow Editor
|
||||
import React from 'react';
|
||||
import { Tag } from 'antd';
|
||||
|
||||
export interface NodeData {
|
||||
label: string;
|
||||
[key: string]: any;
|
||||
}
|
||||
|
||||
export interface WorkflowNode {
|
||||
id: string;
|
||||
type: string;
|
||||
position: { x: number; y: number };
|
||||
data: NodeData;
|
||||
}
|
||||
|
||||
export interface WorkflowEdge {
|
||||
id: string;
|
||||
source: string;
|
||||
target: string;
|
||||
}
|
||||
|
||||
export interface WorkflowGraph {
|
||||
nodes: WorkflowNode[];
|
||||
edges: WorkflowEdge[];
|
||||
}
|
||||
|
||||
// Trigger Node Types
|
||||
export const TriggerNodeTypes = {
|
||||
TIME: 'time',
|
||||
WEBHOOK: 'webhook',
|
||||
VARIABLE: 'variable',
|
||||
TASK_STATUS: 'task_status',
|
||||
SYSTEM_EVENT: 'system_event',
|
||||
};
|
||||
|
||||
// Condition Node Types
|
||||
export const ConditionNodeTypes = {
|
||||
EQUALS: 'equals',
|
||||
NOT_EQUALS: 'not_equals',
|
||||
GREATER_THAN: 'greater_than',
|
||||
LESS_THAN: 'less_than',
|
||||
CONTAINS: 'contains',
|
||||
NOT_CONTAINS: 'not_contains',
|
||||
};
|
||||
|
||||
// Action Node Types
|
||||
export const ActionNodeTypes = {
|
||||
RUN_TASK: 'run_task',
|
||||
SET_VARIABLE: 'set_variable',
|
||||
EXECUTE_COMMAND: 'execute_command',
|
||||
SEND_NOTIFICATION: 'send_notification',
|
||||
};
|
||||
|
||||
// Control Flow Node Types
|
||||
export const ControlFlowNodeTypes = {
|
||||
DELAY: 'delay',
|
||||
RETRY: 'retry',
|
||||
CIRCUIT_BREAKER: 'circuit_breaker',
|
||||
AND_GATE: 'and_gate',
|
||||
OR_GATE: 'or_gate',
|
||||
};
|
||||
|
||||
// Node Renderer Components
|
||||
export const NodeRenderers = {
|
||||
trigger: (node: WorkflowNode) => (
|
||||
<div
|
||||
style={{
|
||||
padding: '12px 16px',
|
||||
background: '#1890ff',
|
||||
color: 'white',
|
||||
borderRadius: 6,
|
||||
minWidth: 160,
|
||||
boxShadow: '0 2px 8px rgba(24, 144, 255, 0.3)',
|
||||
}}
|
||||
>
|
||||
<div style={{ fontWeight: 600, marginBottom: 4 }}>{node.data.label}</div>
|
||||
<Tag
|
||||
style={{
|
||||
background: 'rgba(255, 255, 255, 0.2)',
|
||||
border: 'none',
|
||||
color: 'white',
|
||||
}}
|
||||
>
|
||||
{node.data.triggerType || 'trigger'}
|
||||
</Tag>
|
||||
</div>
|
||||
),
|
||||
|
||||
condition: (node: WorkflowNode) => (
|
||||
<div
|
||||
style={{
|
||||
padding: '12px 16px',
|
||||
background: '#52c41a',
|
||||
color: 'white',
|
||||
borderRadius: 6,
|
||||
minWidth: 160,
|
||||
boxShadow: '0 2px 8px rgba(82, 196, 26, 0.3)',
|
||||
}}
|
||||
>
|
||||
<div style={{ fontWeight: 600, marginBottom: 4 }}>{node.data.label}</div>
|
||||
<div style={{ fontSize: 12, opacity: 0.9 }}>
|
||||
{node.data.field} {node.data.operator} {node.data.value}
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
|
||||
action: (node: WorkflowNode) => (
|
||||
<div
|
||||
style={{
|
||||
padding: '12px 16px',
|
||||
background: '#fa8c16',
|
||||
color: 'white',
|
||||
borderRadius: 6,
|
||||
minWidth: 160,
|
||||
boxShadow: '0 2px 8px rgba(250, 140, 22, 0.3)',
|
||||
}}
|
||||
>
|
||||
<div style={{ fontWeight: 600, marginBottom: 4 }}>{node.data.label}</div>
|
||||
<Tag
|
||||
style={{
|
||||
background: 'rgba(255, 255, 255, 0.2)',
|
||||
border: 'none',
|
||||
color: 'white',
|
||||
}}
|
||||
>
|
||||
{node.data.actionType || 'action'}
|
||||
</Tag>
|
||||
</div>
|
||||
),
|
||||
|
||||
control: (node: WorkflowNode) => (
|
||||
<div
|
||||
style={{
|
||||
padding: '12px 16px',
|
||||
background: '#722ed1',
|
||||
color: 'white',
|
||||
borderRadius: 6,
|
||||
minWidth: 160,
|
||||
boxShadow: '0 2px 8px rgba(114, 46, 209, 0.3)',
|
||||
}}
|
||||
>
|
||||
<div style={{ fontWeight: 600, marginBottom: 4 }}>{node.data.label}</div>
|
||||
<div style={{ fontSize: 12, opacity: 0.9 }}>
|
||||
{node.data.controlType || 'control'}
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
|
||||
logic_gate: (node: WorkflowNode) => (
|
||||
<div
|
||||
style={{
|
||||
padding: '12px 16px',
|
||||
background: '#13c2c2',
|
||||
color: 'white',
|
||||
borderRadius: 6,
|
||||
minWidth: 120,
|
||||
textAlign: 'center',
|
||||
boxShadow: '0 2px 8px rgba(19, 194, 194, 0.3)',
|
||||
}}
|
||||
>
|
||||
<div style={{ fontWeight: 600, fontSize: 16 }}>
|
||||
{node.data.gateType || 'AND'}
|
||||
</div>
|
||||
</div>
|
||||
),
|
||||
};
|
||||
|
||||
// Helper function to create a new node
|
||||
export const createNode = (
|
||||
type: string,
|
||||
position: { x: number; y: number },
|
||||
data: Partial<NodeData>,
|
||||
): WorkflowNode => {
|
||||
return {
|
||||
id: `${type}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
|
||||
type,
|
||||
position,
|
||||
data: {
|
||||
label: data.label || type,
|
||||
...data,
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
// Helper function to create a new edge
|
||||
export const createEdge = (
|
||||
source: string,
|
||||
target: string,
|
||||
): WorkflowEdge => {
|
||||
return {
|
||||
id: `edge-${source}-${target}`,
|
||||
source,
|
||||
target,
|
||||
};
|
||||
};
|
||||
|
||||
// Node templates for quick creation
|
||||
export const NodeTemplates = {
|
||||
triggers: {
|
||||
time: {
|
||||
label: '时间触发',
|
||||
triggerType: 'time',
|
||||
config: { schedule: '0 0 * * *' },
|
||||
},
|
||||
webhook: {
|
||||
label: 'Webhook触发',
|
||||
triggerType: 'webhook',
|
||||
config: {},
|
||||
},
|
||||
variable: {
|
||||
label: '变量监听',
|
||||
triggerType: 'variable',
|
||||
config: { watchPath: '' },
|
||||
},
|
||||
task_status: {
|
||||
label: '任务状态',
|
||||
triggerType: 'task_status',
|
||||
config: { cronId: null },
|
||||
},
|
||||
system_event: {
|
||||
label: '系统事件',
|
||||
triggerType: 'system_event',
|
||||
config: { eventType: 'disk_space', threshold: 80 },
|
||||
},
|
||||
},
|
||||
conditions: {
|
||||
equals: {
|
||||
label: '等于判断',
|
||||
operator: 'equals',
|
||||
field: '',
|
||||
value: '',
|
||||
},
|
||||
greater_than: {
|
||||
label: '大于判断',
|
||||
operator: 'greater_than',
|
||||
field: '',
|
||||
value: 0,
|
||||
},
|
||||
contains: {
|
||||
label: '包含判断',
|
||||
operator: 'contains',
|
||||
field: '',
|
||||
value: '',
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
run_task: {
|
||||
label: '运行任务',
|
||||
actionType: 'run_task',
|
||||
cronId: null,
|
||||
},
|
||||
set_variable: {
|
||||
label: '设置变量',
|
||||
actionType: 'set_variable',
|
||||
name: '',
|
||||
value: '',
|
||||
},
|
||||
execute_command: {
|
||||
label: '执行命令',
|
||||
actionType: 'execute_command',
|
||||
command: '',
|
||||
},
|
||||
send_notification: {
|
||||
label: '发送通知',
|
||||
actionType: 'send_notification',
|
||||
message: '',
|
||||
},
|
||||
},
|
||||
controls: {
|
||||
delay: {
|
||||
label: '延迟执行',
|
||||
controlType: 'delay',
|
||||
delaySeconds: 60,
|
||||
},
|
||||
retry: {
|
||||
label: '重试策略',
|
||||
controlType: 'retry',
|
||||
maxRetries: 3,
|
||||
retryDelay: 5,
|
||||
backoffMultiplier: 2,
|
||||
},
|
||||
circuit_breaker: {
|
||||
label: '熔断器',
|
||||
controlType: 'circuit_breaker',
|
||||
failureThreshold: 3,
|
||||
},
|
||||
},
|
||||
logic_gates: {
|
||||
and: {
|
||||
label: 'AND',
|
||||
gateType: 'AND',
|
||||
},
|
||||
or: {
|
||||
label: 'OR',
|
||||
gateType: 'OR',
|
||||
},
|
||||
},
|
||||
};
|
||||
471
src/pages/scenario/visualWorkflowModal.tsx
Normal file
471
src/pages/scenario/visualWorkflowModal.tsx
Normal file
|
|
@ -0,0 +1,471 @@
|
|||
import React, { useState, useEffect, useCallback } from 'react';
|
||||
import { Modal, Form, Input, message, Button, Drawer, Select, InputNumber, Space } from 'antd';
|
||||
import { request } from '@/utils/http';
|
||||
import intl from 'react-intl-universal';
|
||||
import ReactFlow, {
|
||||
Node,
|
||||
Edge,
|
||||
Controls,
|
||||
Background,
|
||||
applyNodeChanges,
|
||||
applyEdgeChanges,
|
||||
addEdge,
|
||||
NodeChange,
|
||||
EdgeChange,
|
||||
Connection,
|
||||
BackgroundVariant,
|
||||
} from 'reactflow';
|
||||
import 'reactflow/dist/style.css';
|
||||
import {
|
||||
NodeRenderers,
|
||||
NodeTemplates,
|
||||
createNode,
|
||||
WorkflowGraph,
|
||||
} from './nodeTypes';
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
|
||||
const { TextArea } = Input;
|
||||
const { Option } = Select;
|
||||
|
||||
interface ScenarioModalProps {
|
||||
visible: boolean;
|
||||
scenario: any;
|
||||
onCancel: () => void;
|
||||
onSuccess: () => void;
|
||||
}
|
||||
|
||||
const VisualWorkflowModal: React.FC<ScenarioModalProps> = ({
|
||||
visible,
|
||||
scenario,
|
||||
onCancel,
|
||||
onSuccess,
|
||||
}) => {
|
||||
const [form] = Form.useForm();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [nodes, setNodes] = useState<Node[]>([]);
|
||||
const [edges, setEdges] = useState<Edge[]>([]);
|
||||
const [drawerVisible, setDrawerVisible] = useState(false);
|
||||
const [selectedNode, setSelectedNode] = useState<Node | null>(null);
|
||||
const [nodeConfigForm] = Form.useForm();
|
||||
|
||||
useEffect(() => {
|
||||
if (visible) {
|
||||
if (scenario) {
|
||||
form.setFieldsValue({
|
||||
name: scenario.name,
|
||||
description: scenario.description || '',
|
||||
});
|
||||
|
||||
if (scenario.workflowGraph) {
|
||||
setNodes(scenario.workflowGraph.nodes || []);
|
||||
setEdges(scenario.workflowGraph.edges || []);
|
||||
} else {
|
||||
initializeDefaultWorkflow();
|
||||
}
|
||||
} else {
|
||||
form.resetFields();
|
||||
initializeDefaultWorkflow();
|
||||
}
|
||||
}
|
||||
}, [visible, scenario, form]);
|
||||
|
||||
const initializeDefaultWorkflow = () => {
|
||||
const triggerNode = createNode('trigger', { x: 250, y: 100 }, {
|
||||
label: intl.get('时间触发'),
|
||||
triggerType: 'time',
|
||||
config: { schedule: '0 0 * * *' },
|
||||
});
|
||||
|
||||
setNodes([triggerNode]);
|
||||
setEdges([]);
|
||||
};
|
||||
|
||||
const onNodesChange = useCallback(
|
||||
(changes: NodeChange[]) => setNodes((nds) => applyNodeChanges(changes, nds)),
|
||||
[],
|
||||
);
|
||||
|
||||
const onEdgesChange = useCallback(
|
||||
(changes: EdgeChange[]) => setEdges((eds) => applyEdgeChanges(changes, eds)),
|
||||
[],
|
||||
);
|
||||
|
||||
const onConnect = useCallback(
|
||||
(connection: Connection) => setEdges((eds) => addEdge(connection, eds)),
|
||||
[],
|
||||
);
|
||||
|
||||
const handleAddNode = (type: string, template: any) => {
|
||||
const newNode = createNode(type, { x: Math.random() * 400 + 100, y: Math.random() * 300 + 100 }, template);
|
||||
setNodes((nds) => [...nds, newNode]);
|
||||
};
|
||||
|
||||
const handleNodeClick = (_event: React.MouseEvent, node: Node) => {
|
||||
setSelectedNode(node);
|
||||
nodeConfigForm.setFieldsValue(node.data);
|
||||
setDrawerVisible(true);
|
||||
};
|
||||
|
||||
const handleNodeConfigSave = async () => {
|
||||
try {
|
||||
const values = await nodeConfigForm.validateFields();
|
||||
setNodes((nds) =>
|
||||
nds.map((node) =>
|
||||
node.id === selectedNode?.id
|
||||
? { ...node, data: { ...node.data, ...values } }
|
||||
: node
|
||||
)
|
||||
);
|
||||
setDrawerVisible(false);
|
||||
message.success(intl.get('配置已保存'));
|
||||
} catch (error) {
|
||||
console.error('节点配置验证失败:', error);
|
||||
}
|
||||
};
|
||||
|
||||
const handleSubmit = async () => {
|
||||
try {
|
||||
const values = await form.validateFields();
|
||||
setLoading(true);
|
||||
|
||||
const workflowGraph: WorkflowGraph = {
|
||||
nodes: nodes.map(node => ({
|
||||
id: node.id,
|
||||
type: node.type || 'default',
|
||||
position: node.position,
|
||||
data: node.data,
|
||||
})),
|
||||
edges: edges.map(edge => ({
|
||||
id: edge.id,
|
||||
source: edge.source,
|
||||
target: edge.target,
|
||||
})),
|
||||
};
|
||||
|
||||
const endpoint = scenario ? '/api/scenarios' : '/api/scenarios';
|
||||
const method = scenario ? 'put' : 'post';
|
||||
const payload = {
|
||||
...values,
|
||||
workflowGraph,
|
||||
...(scenario ? { id: scenario.id } : {}),
|
||||
};
|
||||
|
||||
const { code } = await request[method](endpoint, payload);
|
||||
if (code === 200) {
|
||||
message.success(
|
||||
scenario ? intl.get('更新成功') : intl.get('创建成功'),
|
||||
);
|
||||
onSuccess();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Failed to save scenario:', error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const nodeTypes = {
|
||||
trigger: ({ data }: any) => NodeRenderers.trigger({ data, id: '', type: 'trigger', position: { x: 0, y: 0 } }),
|
||||
condition: ({ data }: any) => NodeRenderers.condition({ data, id: '', type: 'condition', position: { x: 0, y: 0 } }),
|
||||
action: ({ data }: any) => NodeRenderers.action({ data, id: '', type: 'action', position: { x: 0, y: 0 } }),
|
||||
control: ({ data }: any) => NodeRenderers.control({ data, id: '', type: 'control', position: { x: 0, y: 0 } }),
|
||||
logic_gate: ({ data }: any) => NodeRenderers.logic_gate({ data, id: '', type: 'logic_gate', position: { x: 0, y: 0 } }),
|
||||
};
|
||||
|
||||
const renderNodeConfig = () => {
|
||||
if (!selectedNode) return null;
|
||||
|
||||
switch (selectedNode.type) {
|
||||
case 'trigger':
|
||||
return (
|
||||
<>
|
||||
<Form.Item name="label" label={intl.get('名称')} rules={[{ required: true }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="triggerType" label={intl.get('触发类型')} rules={[{ required: true }]}>
|
||||
<Select>
|
||||
<Option value="time">{intl.get('时间触发')}</Option>
|
||||
<Option value="webhook">Webhook</Option>
|
||||
<Option value="variable">{intl.get('变量监听')}</Option>
|
||||
<Option value="task_status">{intl.get('任务状态')}</Option>
|
||||
<Option value="system_event">{intl.get('系统事件')}</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item noStyle shouldUpdate={(prev, curr) => prev.triggerType !== curr.triggerType}>
|
||||
{({ getFieldValue }) => {
|
||||
const triggerType = getFieldValue('triggerType');
|
||||
if (triggerType === 'time') {
|
||||
return (
|
||||
<Form.Item name={['config', 'schedule']} label="Cron 表达式">
|
||||
<Input placeholder="0 0 * * *" />
|
||||
</Form.Item>
|
||||
);
|
||||
} else if (triggerType === 'variable') {
|
||||
return (
|
||||
<Form.Item name={['config', 'watchPath']} label={intl.get('监听路径')}>
|
||||
<Input placeholder="/path/to/watch" />
|
||||
</Form.Item>
|
||||
);
|
||||
} else if (triggerType === 'system_event') {
|
||||
return (
|
||||
<>
|
||||
<Form.Item name={['config', 'eventType']} label={intl.get('事件类型')}>
|
||||
<Select>
|
||||
<Option value="disk_space">{intl.get('磁盘空间')}</Option>
|
||||
<Option value="memory">{intl.get('内存使用')}</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item name={['config', 'threshold']} label={intl.get('阈值')}>
|
||||
<InputNumber min={0} max={100} addonAfter="%" />
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}}
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
|
||||
case 'condition':
|
||||
return (
|
||||
<>
|
||||
<Form.Item name="label" label={intl.get('名称')} rules={[{ required: true }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="field" label={intl.get('字段名')} rules={[{ required: true }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="operator" label={intl.get('操作符')} rules={[{ required: true }]}>
|
||||
<Select>
|
||||
<Option value="equals">=</Option>
|
||||
<Option value="not_equals">!=</Option>
|
||||
<Option value="greater_than">></Option>
|
||||
<Option value="less_than"><</Option>
|
||||
<Option value="contains">{intl.get('包含')}</Option>
|
||||
<Option value="not_contains">{intl.get('不包含')}</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item name="value" label={intl.get('值')} rules={[{ required: true }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
|
||||
case 'action':
|
||||
return (
|
||||
<>
|
||||
<Form.Item name="label" label={intl.get('名称')} rules={[{ required: true }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="actionType" label={intl.get('动作类型')} rules={[{ required: true }]}>
|
||||
<Select>
|
||||
<Option value="run_task">{intl.get('运行任务')}</Option>
|
||||
<Option value="set_variable">{intl.get('设置变量')}</Option>
|
||||
<Option value="execute_command">{intl.get('执行命令')}</Option>
|
||||
<Option value="send_notification">{intl.get('发送通知')}</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item noStyle shouldUpdate={(prev, curr) => prev.actionType !== curr.actionType}>
|
||||
{({ getFieldValue }) => {
|
||||
const actionType = getFieldValue('actionType');
|
||||
if (actionType === 'run_task') {
|
||||
return (
|
||||
<Form.Item name="cronId" label={intl.get('任务 ID')}>
|
||||
<InputNumber min={1} style={{ width: '100%' }} />
|
||||
</Form.Item>
|
||||
);
|
||||
} else if (actionType === 'set_variable') {
|
||||
return (
|
||||
<>
|
||||
<Form.Item name="name" label={intl.get('变量名')}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="value" label={intl.get('变量值')}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
} else if (actionType === 'execute_command') {
|
||||
return (
|
||||
<Form.Item name="command" label={intl.get('命令')}>
|
||||
<TextArea rows={3} />
|
||||
</Form.Item>
|
||||
);
|
||||
} else if (actionType === 'send_notification') {
|
||||
return (
|
||||
<Form.Item name="message" label={intl.get('消息')}>
|
||||
<TextArea rows={3} />
|
||||
</Form.Item>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}}
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
|
||||
case 'control':
|
||||
return (
|
||||
<>
|
||||
<Form.Item name="label" label={intl.get('名称')} rules={[{ required: true }]}>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
<Form.Item name="controlType" label={intl.get('控制类型')} rules={[{ required: true }]}>
|
||||
<Select>
|
||||
<Option value="delay">{intl.get('延迟执行')}</Option>
|
||||
<Option value="retry">{intl.get('重试策略')}</Option>
|
||||
<Option value="circuit_breaker">{intl.get('熔断器')}</Option>
|
||||
</Select>
|
||||
</Form.Item>
|
||||
<Form.Item noStyle shouldUpdate={(prev, curr) => prev.controlType !== curr.controlType}>
|
||||
{({ getFieldValue }) => {
|
||||
const controlType = getFieldValue('controlType');
|
||||
if (controlType === 'delay') {
|
||||
return (
|
||||
<Form.Item name="delaySeconds" label={intl.get('延迟时间')}>
|
||||
<InputNumber min={1} addonAfter={intl.get('秒')} />
|
||||
</Form.Item>
|
||||
);
|
||||
} else if (controlType === 'retry') {
|
||||
return (
|
||||
<>
|
||||
<Form.Item name="maxRetries" label={intl.get('最大重试次数')}>
|
||||
<InputNumber min={1} max={10} />
|
||||
</Form.Item>
|
||||
<Form.Item name="retryDelay" label={intl.get('重试延迟')}>
|
||||
<InputNumber min={1} addonAfter={intl.get('秒')} />
|
||||
</Form.Item>
|
||||
<Form.Item name="backoffMultiplier" label={intl.get('退避倍数')}>
|
||||
<InputNumber min={1} step={0.5} />
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
} else if (controlType === 'circuit_breaker') {
|
||||
return (
|
||||
<Form.Item name="failureThreshold" label={intl.get('失败熔断阈值')}>
|
||||
<InputNumber min={1} />
|
||||
</Form.Item>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}}
|
||||
</Form.Item>
|
||||
</>
|
||||
);
|
||||
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal
|
||||
title={scenario ? intl.get('编辑场景') : intl.get('新建场景')}
|
||||
open={visible}
|
||||
onCancel={onCancel}
|
||||
onOk={handleSubmit}
|
||||
confirmLoading={loading}
|
||||
width={1400}
|
||||
destroyOnClose
|
||||
style={{ top: 20 }}
|
||||
>
|
||||
<Form form={form} layout="vertical">
|
||||
<Form.Item
|
||||
name="name"
|
||||
label={intl.get('名称')}
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Input />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item name="description" label={intl.get('描述')}>
|
||||
<TextArea rows={2} />
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item label={intl.get('工作流设计')}>
|
||||
<div style={{ marginBottom: 8 }}>
|
||||
<Space>
|
||||
<Button
|
||||
size="small"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => handleAddNode('trigger', NodeTemplates.triggers.time)}
|
||||
>
|
||||
{intl.get('触发器')}
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => handleAddNode('condition', NodeTemplates.conditions.equals)}
|
||||
>
|
||||
{intl.get('条件')}
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => handleAddNode('action', NodeTemplates.actions.run_task)}
|
||||
>
|
||||
{intl.get('动作')}
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => handleAddNode('control', NodeTemplates.controls.delay)}
|
||||
>
|
||||
{intl.get('控制流')}
|
||||
</Button>
|
||||
<Button
|
||||
size="small"
|
||||
icon={<PlusOutlined />}
|
||||
onClick={() => handleAddNode('logic_gate', NodeTemplates.logic_gates.and)}
|
||||
>
|
||||
{intl.get('逻辑门')}
|
||||
</Button>
|
||||
</Space>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
border: '1px solid #d9d9d9',
|
||||
borderRadius: 4,
|
||||
height: 500,
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
onNodesChange={onNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
onConnect={onConnect}
|
||||
onNodeClick={handleNodeClick}
|
||||
nodeTypes={nodeTypes}
|
||||
fitView
|
||||
>
|
||||
<Controls />
|
||||
<Background variant={BackgroundVariant.Dots} gap={12} size={1} />
|
||||
</ReactFlow>
|
||||
</div>
|
||||
</Form.Item>
|
||||
</Form>
|
||||
|
||||
<Drawer
|
||||
title={intl.get('节点配置')}
|
||||
placement="right"
|
||||
onClose={() => setDrawerVisible(false)}
|
||||
open={drawerVisible}
|
||||
width={400}
|
||||
extra={
|
||||
<Button type="primary" onClick={handleNodeConfigSave}>
|
||||
{intl.get('保存')}
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<Form form={nodeConfigForm} layout="vertical">
|
||||
{renderNodeConfig()}
|
||||
</Form>
|
||||
</Drawer>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default VisualWorkflowModal;
|
||||
Loading…
Reference in New Issue
Block a user