feat: 引入logicFlow插件

master
donghao 2 weeks ago
parent 56f94ed745
commit 391caa5ff1

@ -7,6 +7,7 @@
"EffectScope": true, "EffectScope": true,
"ElButton": true, "ElButton": true,
"ElDialog": true, "ElDialog": true,
"ElMessage": true,
"ExtractDefaultPropTypes": true, "ExtractDefaultPropTypes": true,
"ExtractPropTypes": true, "ExtractPropTypes": true,
"ExtractPublicPropTypes": true, "ExtractPublicPropTypes": true,

1
.gitignore vendored

@ -3,4 +3,5 @@ dist
release release
out out
.DS_Store .DS_Store
test
*.log* *.log*

152
package-lock.json generated

@ -11,12 +11,17 @@
"dependencies": { "dependencies": {
"@electron-toolkit/preload": "^3.0.0", "@electron-toolkit/preload": "^3.0.0",
"@electron-toolkit/utils": "^3.0.0", "@electron-toolkit/utils": "^3.0.0",
"@element-plus/icons-vue": "^2.3.1",
"@logicflow/core": "^2.0.16",
"@logicflow/extension": "^2.0.21",
"@types/sortablejs": "^1.15.8",
"axios": "^1.10.0", "axios": "^1.10.0",
"element-plus": "^2.10.2", "element-plus": "^2.10.2",
"mockjs": "^1.1.0", "mockjs": "^1.1.0",
"pinia": "^3.0.3", "pinia": "^3.0.3",
"vite-plugin-mock": "^3.0.2", "vite-plugin-mock": "^3.0.2",
"vue-router": "^4.5.1" "vue-router": "^4.5.1",
"vuedraggable": "^2.24.3"
}, },
"devDependencies": { "devDependencies": {
"@electron-toolkit/eslint-config": "^1.0.2", "@electron-toolkit/eslint-config": "^1.0.2",
@ -82,6 +87,11 @@
"node": ">=6.0.0" "node": ">=6.0.0"
} }
}, },
"node_modules/@antv/hierarchy": {
"version": "0.6.14",
"resolved": "https://registry.npmmirror.com/@antv/hierarchy/-/hierarchy-0.6.14.tgz",
"integrity": "sha512-V3uknf7bhynOqQDw2sg+9r9DwZ9pc6k/EcqyTFdfXB1+ydr7urisP0MipIuimucvQKN+Qkd+d6w601r1UIroqQ=="
},
"node_modules/@babel/code-frame": { "node_modules/@babel/code-frame": {
"version": "7.23.5", "version": "7.23.5",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.23.5.tgz",
@ -1577,6 +1587,40 @@
"@jridgewell/sourcemap-codec": "^1.4.14" "@jridgewell/sourcemap-codec": "^1.4.14"
} }
}, },
"node_modules/@logicflow/core": {
"version": "2.0.16",
"resolved": "https://registry.npmmirror.com/@logicflow/core/-/core-2.0.16.tgz",
"integrity": "sha512-KoNdY5g7WcAtfk7sMe+uOOso28mw6dwCHgLKmnzC0nenASD0HGWhFq+Yo7ktHP2asMXUISPb9hbQA221NcYZdg==",
"dependencies": {
"classnames": "^2.3.2",
"lodash-es": "^4.17.21",
"mobx": "^5.15.7",
"mobx-preact": "^3.0.0",
"mobx-utils": "^5.6.1",
"mousetrap": "^1.6.5",
"preact": "^10.17.1",
"uuid": "^9.0.0"
}
},
"node_modules/@logicflow/extension": {
"version": "2.0.21",
"resolved": "https://registry.npmmirror.com/@logicflow/extension/-/extension-2.0.21.tgz",
"integrity": "sha512-SdYBOnDlCEOEElScGFIprgxqH0fv39ur7suyYzhiWUaWjL/TpvIESgqcR/ujE9aolFNTtzv2USc6xPcrouc4PQ==",
"dependencies": {
"@antv/hierarchy": "^0.6.11",
"@logicflow/core": "2.0.16",
"classnames": "^2.3.2",
"lodash-es": "^4.17.21",
"medium-editor": "^5.23.3",
"mobx": "^5.15.7",
"preact": "^10.17.1",
"rangy": "^1.3.1",
"vanilla-picker": "^2.12.3"
},
"peerDependencies": {
"@logicflow/core": "2.0.16"
}
},
"node_modules/@malept/cross-spawn-promise": { "node_modules/@malept/cross-spawn-promise": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz",
@ -2175,6 +2219,11 @@
"url": "https://github.com/sindresorhus/is?sponsor=1" "url": "https://github.com/sindresorhus/is?sponsor=1"
} }
}, },
"node_modules/@sphinxxxx/color-conversion": {
"version": "2.2.2",
"resolved": "https://registry.npmmirror.com/@sphinxxxx/color-conversion/-/color-conversion-2.2.2.tgz",
"integrity": "sha512-XExJS3cLqgrmNBIP3bBw6+1oQ1ksGjFh0+oClDKFYpCCqx/hlqwWO5KO/S63fzUo67SxI9dMrF0y5T/Ey7h8Zw=="
},
"node_modules/@szmarczak/http-timer": { "node_modules/@szmarczak/http-timer": {
"version": "4.0.6", "version": "4.0.6",
"resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
@ -2653,6 +2702,11 @@
"integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==", "integrity": "sha512-dn1l8LaMea/IjDoHNd9J52uBbInB796CDffS6VdIxvqYCPSG0V0DzHp76GpaWnlhg88uYyPbXCDIowa86ybd5A==",
"dev": true "dev": true
}, },
"node_modules/@types/sortablejs": {
"version": "1.15.8",
"resolved": "https://registry.npmmirror.com/@types/sortablejs/-/sortablejs-1.15.8.tgz",
"integrity": "sha512-b79830lW+RZfwaztgs1aVPgbasJ8e7AXtZYHTELNXZPsERt4ymJdjV4OccDbHQAvHrCcFpbF78jkm0R6h/pZVg=="
},
"node_modules/@types/verror": { "node_modules/@types/verror": {
"version": "1.10.9", "version": "1.10.9",
"resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.9.tgz", "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.9.tgz",
@ -3917,6 +3971,11 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/classnames": {
"version": "2.5.1",
"resolved": "https://registry.npmmirror.com/classnames/-/classnames-2.5.1.tgz",
"integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow=="
},
"node_modules/cli-truncate": { "node_modules/cli-truncate": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz",
@ -5654,6 +5713,11 @@
"he": "bin/he" "he": "bin/he"
} }
}, },
"node_modules/hoist-non-react-statics": {
"version": "2.5.5",
"resolved": "https://registry.npmmirror.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
"integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw=="
},
"node_modules/hookable": { "node_modules/hookable": {
"version": "5.5.3", "version": "5.5.3",
"resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz", "resolved": "https://registry.npmmirror.com/hookable/-/hookable-5.5.3.tgz",
@ -6466,6 +6530,11 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/medium-editor": {
"version": "5.23.3",
"resolved": "https://registry.npmmirror.com/medium-editor/-/medium-editor-5.23.3.tgz",
"integrity": "sha512-he9/TdjX8f8MGdXGfCs8AllrYnqXJJvjNkDKmPg3aPW/uoIrlRqtkFthrwvmd+u4QyzEiadhCCM0EwTiRdUCJw=="
},
"node_modules/memoize-one": { "node_modules/memoize-one": {
"version": "6.0.0", "version": "6.0.0",
"resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz", "resolved": "https://registry.npmmirror.com/memoize-one/-/memoize-one-6.0.0.tgz",
@ -6640,6 +6709,35 @@
"pathe": "^2.0.1" "pathe": "^2.0.1"
} }
}, },
"node_modules/mobx": {
"version": "5.15.7",
"resolved": "https://registry.npmmirror.com/mobx/-/mobx-5.15.7.tgz",
"integrity": "sha512-wyM3FghTkhmC+hQjyPGGFdpehrcX1KOXsDuERhfK2YbJemkUhEB+6wzEN639T21onxlfYBmriA1PFnvxTUhcKw==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/mobx"
}
},
"node_modules/mobx-preact": {
"version": "3.0.0",
"resolved": "https://registry.npmmirror.com/mobx-preact/-/mobx-preact-3.0.0.tgz",
"integrity": "sha512-ijan/cBs3WmRye87E5+3JmoFBB00KDAwNA3pm7bMwYLPHBAXlN86aC3gdrXw8aKzM5RI8V3a993PphzPv6P4FA==",
"dependencies": {
"hoist-non-react-statics": "^2.3.1"
},
"peerDependencies": {
"mobx": "5.x",
"preact": ">=8"
}
},
"node_modules/mobx-utils": {
"version": "5.6.2",
"resolved": "https://registry.npmmirror.com/mobx-utils/-/mobx-utils-5.6.2.tgz",
"integrity": "sha512-a/WlXyGkp6F12b01sTarENpxbmlRgPHFyR1Xv2bsSjQBm5dcOtd16ONb40/vOqck8L99NHpI+C9MXQ+SZ8f+yw==",
"peerDependencies": {
"mobx": "^4.13.1 || ^5.13.1"
}
},
"node_modules/mockjs": { "node_modules/mockjs": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmmirror.com/mockjs/-/mockjs-1.1.0.tgz", "resolved": "https://registry.npmmirror.com/mockjs/-/mockjs-1.1.0.tgz",
@ -6651,6 +6749,11 @@
"random": "bin/random" "random": "bin/random"
} }
}, },
"node_modules/mousetrap": {
"version": "1.6.5",
"resolved": "https://registry.npmmirror.com/mousetrap/-/mousetrap-1.6.5.tgz",
"integrity": "sha512-QNo4kEepaIBwiT8CDhP98umTetp+JNfQYBWvC1pc6/OAibuXtRcxZ58Qz8skvEHYvURne/7R8T5VoOI7rDsEUA=="
},
"node_modules/ms": { "node_modules/ms": {
"version": "2.1.3", "version": "2.1.3",
"resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz",
@ -7230,6 +7333,15 @@
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
"dev": true "dev": true
}, },
"node_modules/preact": {
"version": "10.26.9",
"resolved": "https://registry.npmmirror.com/preact/-/preact-10.26.9.tgz",
"integrity": "sha512-SSjF9vcnF27mJK1XyFMNJzFd5u3pQiATFqoaDy03XuN00u4ziveVVEGt5RKJrDR8MHE/wJo9Nnad56RLzS2RMA==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/preact"
}
},
"node_modules/prelude-ls": { "node_modules/prelude-ls": {
"version": "1.2.1", "version": "1.2.1",
"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@ -7356,6 +7468,11 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/rangy": {
"version": "1.3.2",
"resolved": "https://registry.npmmirror.com/rangy/-/rangy-1.3.2.tgz",
"integrity": "sha512-fS1C4MOyk8T+ZJZdLcgrukPWxkyDXa+Hd2Kj+Zg4wIK71yrWgmjzHubzPMY1G+WD9EgGxMp3fIL0zQ1ickmSWA=="
},
"node_modules/read-cache": { "node_modules/read-cache": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmmirror.com/read-cache/-/read-cache-1.0.0.tgz", "resolved": "https://registry.npmmirror.com/read-cache/-/read-cache-1.0.0.tgz",
@ -7764,6 +7881,11 @@
"npm": ">= 3.0.0" "npm": ">= 3.0.0"
} }
}, },
"node_modules/sortablejs": {
"version": "1.10.2",
"resolved": "https://registry.npmmirror.com/sortablejs/-/sortablejs-1.10.2.tgz",
"integrity": "sha512-YkPGufevysvfwn5rfdlGyrGjt7/CRHwvRPogD/lC+TnvcN29jDpCifKP+rBqf+LRldfXSTh+0CGLcSg0VIxq3A=="
},
"node_modules/source-map": { "node_modules/source-map": {
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@ -8629,6 +8751,26 @@
"node": ">= 0.4.0" "node": ">= 0.4.0"
} }
}, },
"node_modules/uuid": {
"version": "9.0.1",
"resolved": "https://registry.npmmirror.com/uuid/-/uuid-9.0.1.tgz",
"integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
"funding": [
"https://github.com/sponsors/broofa",
"https://github.com/sponsors/ctavan"
],
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/vanilla-picker": {
"version": "2.12.3",
"resolved": "https://registry.npmmirror.com/vanilla-picker/-/vanilla-picker-2.12.3.tgz",
"integrity": "sha512-qVkT1E7yMbUsB2mmJNFmaXMWE2hF8ffqzMMwe9zdAikd8u2VfnsVY2HQcOUi2F38bgbxzlJBEdS1UUhOXdF9GQ==",
"dependencies": {
"@sphinxxxx/color-conversion": "^2.2.2"
}
},
"node_modules/verror": { "node_modules/verror": {
"version": "1.10.1", "version": "1.10.1",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz",
@ -8811,6 +8953,14 @@
"typescript": "*" "typescript": "*"
} }
}, },
"node_modules/vuedraggable": {
"version": "2.24.3",
"resolved": "https://registry.npmmirror.com/vuedraggable/-/vuedraggable-2.24.3.tgz",
"integrity": "sha512-6/HDXi92GzB+Hcs9fC6PAAozK1RLt1ewPTLjK0anTYguXLAeySDmcnqE8IC0xa7shvSzRjQXq3/+dsZ7ETGF3g==",
"dependencies": {
"sortablejs": "1.10.2"
}
},
"node_modules/webpack-virtual-modules": { "node_modules/webpack-virtual-modules": {
"version": "0.6.2", "version": "0.6.2",
"resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", "resolved": "https://registry.npmmirror.com/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz",

@ -56,12 +56,17 @@
"dependencies": { "dependencies": {
"@electron-toolkit/preload": "^3.0.0", "@electron-toolkit/preload": "^3.0.0",
"@electron-toolkit/utils": "^3.0.0", "@electron-toolkit/utils": "^3.0.0",
"@element-plus/icons-vue": "^2.3.1",
"@logicflow/core": "^2.0.16",
"@logicflow/extension": "^2.0.21",
"@types/sortablejs": "^1.15.8",
"axios": "^1.10.0", "axios": "^1.10.0",
"element-plus": "^2.10.2", "element-plus": "^2.10.2",
"mockjs": "^1.1.0", "mockjs": "^1.1.0",
"pinia": "^3.0.3", "pinia": "^3.0.3",
"vite-plugin-mock": "^3.0.2", "vite-plugin-mock": "^3.0.2",
"vue-router": "^4.5.1" "vue-router": "^4.5.1",
"vuedraggable": "^2.24.3"
}, },
"devDependencies": { "devDependencies": {
"@electron-toolkit/eslint-config": "^1.0.2", "@electron-toolkit/eslint-config": "^1.0.2",

@ -9,6 +9,7 @@ declare global {
const EffectScope: typeof import('vue')['EffectScope'] const EffectScope: typeof import('vue')['EffectScope']
const ElButton: typeof import('element-plus/es')['ElButton'] const ElButton: typeof import('element-plus/es')['ElButton']
const ElDialog: typeof import('element-plus/es')['ElDialog'] const ElDialog: typeof import('element-plus/es')['ElDialog']
const ElMessage: typeof import('element-plus/es')['ElMessage']
const computed: typeof import('vue')['computed'] const computed: typeof import('vue')['computed']
const createApp: typeof import('vue')['createApp'] const createApp: typeof import('vue')['createApp']
const customRef: typeof import('vue')['customRef'] const customRef: typeof import('vue')['customRef']

@ -0,0 +1,47 @@
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2025-07-14 14:30:34
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2025-07-14 16:12:13
* @FilePath: \Robot-Al-Platform-Web\src\renderer\src\config\designCanvas.ts
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/
export const logicFlowConf = {
background: {
backgroundColor: '#242529'
},
snapline: {
stroke: '#ff0000', // 对齐线颜色
strokeWidth: 1 // 对齐线宽度
}
}
export const logicFlowThemeConf = {
baseNode: {
fill: '#fff',
stroke: '#154DDD',
strokeWidth: 2
},
nodeText: {
color: '#ff0000',
overflowMode: 'default',
lineHeight: 1.2,
fontSize: 12
},
line: {
stroke: '#154DDD',
strokeWidth: 2
},
polyline: {
stroke: '#154DDD',
strokeWidth: 2
},
edgeText: {
textWidth: 100,
overflowMode: 'default',
fontSize: 12,
background: {
fill: 'transparent'
}
}
}

@ -0,0 +1,83 @@
import SaveIcon from '@/assets/images/navBar/save.png'
import PrevIcon from '@/assets/images/navBar/prev.png'
import NextIcon from '@/assets/images/navBar/next.png'
import LockIcon from '@/assets/images/navBar/lock.png'
import CameraIcon from '@/assets/images/navBar/camera.png'
import ControlIcon from '@/assets/images/navBar/control.png'
import GlobalIcon from '@/assets/images/navBar/global.png'
import CommIcon from '@/assets/images/navBar/comm.png'
import TriggerIcon from '@/assets/images/navBar/trigger.png'
import ScriptIcon from '@/assets/images/navBar/script.png'
import SingleExecIcon from '@/assets/images/navBar/single_execution.png'
import ContinuousExecIcon from '@/assets/images/navBar/continuous_execution.png'
import RunInterfaceIcon from '@/assets/images/navBar/run_interface.png'
import RunModelIcon from '@/assets/images/navBar/run_model.png'
export interface ControlsItemType {
icon: any
text: string
type: string
}
export const navControlsConf: ControlsItemType[] = [
{
icon: SaveIcon,
text: '保存',
type: 'save'
},
{
icon: PrevIcon,
text: '上一步',
type: 'undo'
},
{
icon: NextIcon,
text: '下一步',
type: 'redo'
},
{
icon: LockIcon,
text: '锁定',
type: 'lock'
},
{
icon: CameraIcon,
text: '相机管理'
},
{
icon: ControlIcon,
text: '控制管理'
},
{
icon: GlobalIcon,
text: '全局变量'
},
{
icon: CommIcon,
text: '通信管理'
},
{
icon: TriggerIcon,
text: '全局触发'
},
{
icon: ScriptIcon,
text: '全局脚本'
},
{
icon: SingleExecIcon,
text: '单次执行'
},
{
icon: ContinuousExecIcon,
text: '连续执行'
},
{
icon: RunInterfaceIcon,
text: '编辑运行界面'
},
{
icon: RunModelIcon,
text: '编辑运行界面'
}
]

@ -0,0 +1,51 @@
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2025-07-14 14:33:03
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2025-07-14 16:26:52
* @FilePath: \Robot-Al-Platform-Web\src\renderer\src\hooks\useLoginFlow.ts
* @Description:
*
*/
export const useLogicFlow = () => {
const graphData = {
nodes: [
{
id: 'node_id_1',
type: 'rect',
x: 100,
y: 100,
text: { x: 100, y: 100, value: '节点1' },
properties: {}
},
{
id: 'node_id_2',
type: 'circle',
x: 200,
y: 300,
text: { x: 200, y: 300, value: '节点2' },
properties: {}
}
],
edges: [
{
id: 'edge_id',
type: 'polyline',
sourceNodeId: 'node_id_1',
targetNodeId: 'node_id_2',
text: { x: 139, y: 200, value: '' },
startPoint: { x: 100, y: 140 },
endPoint: { x: 200, y: 250 },
pointsList: [
{ x: 100, y: 140 },
{ x: 100, y: 200 },
{ x: 200, y: 200 },
{ x: 200, y: 250 }
],
properties: {}
}
]
}
return { graphData }
}

@ -0,0 +1,222 @@
<template>
<div class="design-content-layout-wrap">
<div class="pane left-pane" :style="{ width: paneWidths.left + 'px' }">
<slot name="left"></slot>
</div>
<div class="resizer vertical-resizer" @mousedown="startResize('horizontal', $event)">
<!-- <div class="resizer-handle"></div> -->
</div>
<div class="pane right-pane">
<div class="pane top-pane" :style="{ height: paneHeights.top + 'px' }">
<slot name="top"></slot>
</div>
<div class="resizer horizontal-resizer" @mousedown="startResize('vertical', $event)">
<!-- <div class="resizer-handle"></div> -->
</div>
<div class="pane bottom-pane">
<slot name="bottom"></slot>
</div>
</div>
</div>
</template>
<script setup lang="ts">
//
const paneWidths = ref({
left: 880 + 64,
right: 716 // 1600 - 600 - 8 ()
})
const paneHeights = ref({
top: 400,
bottom: 396 // 800 - 400 - 8 ()
})
//
const resizing = ref(false)
const resizingDirection = ref('')
const startPosition = ref({ x: 0, y: 0 })
const initialSizes = ref({
left: 0,
right: 0,
top: 0,
bottom: 0
})
//
const startResize = (direction, event) => {
resizing.value = true
resizingDirection.value = direction
//
startPosition.value = {
x: event.clientX,
y: event.clientY
}
//
initialSizes.value = {
left: paneWidths.value.left,
right: paneWidths.value.right,
top: paneHeights.value.top,
bottom: paneHeights.value.bottom
}
document.addEventListener('mousemove', handleResize)
document.addEventListener('mouseup', stopResize)
}
//
const handleResize = (event) => {
if (!resizing.value) return
if (resizingDirection.value === 'horizontal') {
//
const deltaX = event.clientX - startPosition.value.x
const newLeft = initialSizes.value.left + deltaX
const newRight = initialSizes.value.right - deltaX
//
const minWidth = 100
if (newLeft >= minWidth && newRight >= minWidth) {
paneWidths.value.left = newLeft
paneWidths.value.right = newRight
}
} else {
//
const deltaY = event.clientY - startPosition.value.y
const newTop = initialSizes.value.top + deltaY
const newBottom = initialSizes.value.bottom - deltaY
//
const minHeight = 100
if (newTop >= minHeight && newBottom >= minHeight) {
paneHeights.value.top = newTop
paneHeights.value.bottom = newBottom
}
}
}
//
const stopResize = () => {
resizing.value = false
document.removeEventListener('mousemove', handleResize)
document.removeEventListener('mouseup', stopResize)
}
//
onMounted(() => {
document.addEventListener('mousemove', (e) => {
if (resizing.value) {
e.preventDefault()
}
})
})
//
onUnmounted(() => {
document.removeEventListener('mousemove', handleResize)
document.removeEventListener('mouseup', stopResize)
})
</script>
<style scoped>
.design-content-layout-wrap {
display: flex;
overflow: hidden;
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.5);
border: 1px solid #444;
}
.pane {
overflow: auto;
box-sizing: border-box;
position: relative;
}
.pane-content {
padding: 20px;
color: #d4d4d4;
height: calc(100% - 52px);
overflow-y: auto;
}
.left-pane {
background: #242529;
}
.right-pane {
display: flex;
flex-direction: column;
flex: 1;
}
.top-pane {
min-height: 100px;
background: #151515;
}
.bottom-pane {
min-height: 100px;
background: #474747;
display: flex;
flex: 1;
}
.resizer {
background: #333;
position: relative;
transition: background-color 0.2s;
z-index: 1;
}
.resizer:hover {
background: var(--ds-color-primary);
}
.resizer:active {
background: var(--ds-color-primary);
}
.vertical-resizer {
width: 2px;
cursor: col-resize;
}
.horizontal-resizer {
height: 2px;
cursor: row-resize;
width: 100%;
}
/* .resizer-handle {
position: absolute;
background: red;
border-radius: 4px;
transition: all 0.2s;
} */
/* .vertical-resizer .resizer-handle {
width: 2px;
height: 40px;
top: 50%;
left: 2px;
transform: translateY(-50%);
}
.horizontal-resizer .resizer-handle {
height: 4px;
width: 40px;
left: 50%;
top: 2px;
transform: translateX(-50%);
} */
/* .resizer:hover .resizer-handle {
background: #fff;
} */
</style>

@ -2,25 +2,22 @@
* @Author: donghao donghao@supervision.ltd * @Author: donghao donghao@supervision.ltd
* @Date: 2025-07-02 13:17:44 * @Date: 2025-07-02 13:17:44
* @LastEditors: donghao donghao@supervision.ltd * @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2025-07-09 12:42:56 * @LastEditTime: 2025-07-15 10:02:10
* @FilePath: \electron-project\Robot-Al\Robot-Al-Platform-Web\src\renderer\src\main.ts * @FilePath: \electron-project\Robot-Al\Robot-Al-Platform-Web\src\renderer\src\main.ts
* @Description: * @Description:
*/ */
import ElementPlus from 'element-plus'
import './styles/tailwind.scss' import './styles/tailwind.scss'
import './styles/main.scss' import './styles/main.scss'
import { createApp } from 'vue' import { createApp } from 'vue'
import App from './App.vue' import App from './App.vue'
import router from '@router' import router from '@router'
import { createPinia } from 'pinia' import { createPinia } from 'pinia'
import zhCn from '@/plugins/zhCnElement' // 引入中文语言包
const app = createApp(App) import setupElementPlus from '@/plugins/elementPlus'
app.use(router).use(createPinia()).use(ElementPlus, { const app = createApp(App)
locale: zhCn, setupElementPlus(app)
size: 'large', // 全局组件尺寸 app.use(router).use(createPinia())
zIndex: 3000 // 全局z-index
})
app.mount('#app') app.mount('#app')

@ -0,0 +1,30 @@
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2025-07-15 09:49:13
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2025-07-15 10:00:07
* @FilePath: \Robot-Al-Platform-Web\src\renderer\src\plugins\elementPlus.ts
* @Description: element-plus
*/
import ElementPlus from 'element-plus'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
import zhCn from 'element-plus/es/locale/lang/zh-cn'
export default function setupElementPlus(app: App) {
// 自定义分页器文案
zhCn.el.pagination = {
goto: '前往',
pageClassifier: '',
pagesize: '条/页',
total: '共 {total} 条'
}
// 全局注册所有图标
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
app.use(ElementPlus, {
locale: zhCn,
size: 'large', // 全局组件尺寸
zIndex: 3000 // 全局z-index
})
}

@ -1,19 +0,0 @@
/*
* @Author: donghao donghao@supervision.ltd
* @Date: 2025-03-11 11:09:39
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2025-07-08 16:50:31
* @FilePath: \5G-Loading-Bay-Web\src\plugins\zhCn.ts
* @Description: element-plus
*/
import zhCn from 'element-plus/es/locale/lang/zh-cn'
// 自定义分页器文案
zhCn.el.pagination = {
goto: '前往',
pageClassifier: '',
pagesize: '条/页',
total: '共 {total} 条'
}
export default zhCn

@ -2,16 +2,18 @@
* @Author: donghao donghao@supervision.ltd * @Author: donghao donghao@supervision.ltd
* @Date: 2025-07-02 13:17:44 * @Date: 2025-07-02 13:17:44
* @LastEditors: donghao donghao@supervision.ltd * @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2025-07-08 17:40:18 * @LastEditTime: 2025-07-10 14:04:12
* @FilePath: \Robot-Al-Platform-Web\src\renderer\src\router\index.ts * @FilePath: \Robot-Al-Platform-Web\src\renderer\src\router\index.ts
* @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: ,`customMade`, koroFileHeader : https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
*/ */
import { createRouter, createWebHashHistory } from 'vue-router' import { createRouter, createWebHashHistory } from 'vue-router'
import Home from '@views/Home/index.vue' import Home from '@views/Home/index.vue'
import Design from '@views/Design/index.vue' import Design from '@views/Design/index.vue'
import TestLF from '@views/Test/logicFlow.vue'
export default createRouter({ export default createRouter({
history: createWebHashHistory(), //hash模式 history: createWebHashHistory(), //hash模式
routes: [{ path: '/', component: Design }] //路由配置规则数组 routes: [{ path: '/', component: Design }, { path: '/testLF', component: TestLF }] //路由配置规则数组
}) })
/** 重置路由 */ /** 重置路由 */

@ -35,3 +35,29 @@ body {
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
//
::-webkit-scrollbar-track-piece {
-webkit-border-radius: 0;
}
::-webkit-scrollbar {
width: 5px;
height: 10px;
}
::-webkit-scrollbar-thumb {
height: 50px;
background-color: #ccc;
-webkit-border-radius: 6px;
outline-offset: -2px;
-moz-opacity: 0.5;
-khtml-opacity: 0.5;
opacity: 0.5;
}
::-webkit-scrollbar-thumb:hover {
height: 50px;
background-color: #878987;
-webkit-border-radius: 6px;
}

@ -1,10 +1,10 @@
@import url('./logic-flow-core.scss'); // logicflow
.design-wrap { .design-wrap {
.design-header { .design-header {
background-color: orange;
height: 80px; height: 80px;
} }
.design-content { .design-content {
// background-color: red;
height: calc(100vh - 80px); height: calc(100vh - 80px);
.design-panel-box { .design-panel-box {
background-color: #373737; background-color: #373737;
@ -12,11 +12,15 @@
} }
.design-logic-flow-box { .design-logic-flow-box {
background-color: #242529; background-color: #242529;
width: calc((100% - 64px) * 0.4745); width: 880px;
} }
.design-content-right { .design-content-right {
background-color: #151515; background-color: #151515;
width: calc((100% - 64px) * 0.5255); width: calc((100% - 64px) - 880px);
}
.design-main-container {
width: 100vw;
height: calc(100vh - 80px);
} }
} }
} }

@ -2,6 +2,7 @@
@import url('element-plus/theme-chalk/dark/css-vars.css'); // @import url('element-plus/theme-chalk/dark/css-vars.css'); //
@import url('@/styles/base.scss'); @import url('@/styles/base.scss');
@import url('@/styles/global.scss'); @import url('@/styles/global.scss');
// //
* { * {
--el-color-primary: var(--ds-color-primary); // --el-color-primary: var(--ds-color-primary); //

@ -0,0 +1,214 @@
.lf-graph {
position: relative;
z-index: 0;
width: 100%;
height: 100%;
background: #fff;
user-select: none;
}
.lf-element-text {
cursor: text;
}
.lf-text-disabled {
pointer-events: none;
}
.lf-text-draggable {
cursor: move;
}
*:focus {
outline: none;
}
.lf-node-anchor {
cursor: crosshair;
}
.lf-node-anchor-hover {
visibility: hidden;
}
.lf-anchor:hover .lf-node-anchor-hover {
visibility: visible;
}
.lf-edge.pointer-none {
pointer-events: none;
}
.lf-edge-append {
cursor: pointer;
}
.lf-edge-animation {
stroke-dashoffset: 100%;
animation: lf_animate_dash 5s linear infinite;
}
@keyframes lf_animate_dash {
to {
stroke-dashoffset: 0;
}
}
/* node */
.lf-node-not-allow {
cursor: not-allowed;
}
.lf-polyline-append-ns-resize {
cursor: ns-resize;
}
.lf-polyline-append-ew-resize {
cursor: ew-resize;
}
.lf-dragging {
cursor: move;
}
.lf-dragging .lf-element-text {
cursor: move;
}
.lf-draggable {
cursor: default;
}
.lf-bezier-adjust-anchor {
cursor: pointer;
}
/* background */
.lf-background,
.lf-grid {
position: absolute;
inset: 0;
z-index: -1;
}
.lf-background-area {
width: 100%;
height: 100%;
}
/* html-overlay */
.lf-html-overlay {
position: absolute;
inset: 0;
z-index: 1;
overflow: hidden;
user-select: none;
pointer-events: none;
}
.lf-html-overlay__transform > * {
pointer-events: all;
}
.lf-text-editable {
pointer-events: all;
}
.lf-text-input {
position: absolute;
box-sizing: border-box;
min-width: 100px;
min-height: 20px;
padding: 5px;
line-height: 1.2;
white-space: pre;
text-align: center;
color: var(--ds-clort-text-1);
background: #ccc;
border: 1px solid #edefed;
border-radius: 3px;
outline: none;
transform: translate(-50%, -50%);
resize: none;
}
.lf-get-text-height {
display: inline-block;
box-sizing: border-box;
word-break: break-all;
/* 为了跟输入效果保持一致,设置透明边框占位 */
border: 1px solid transparent;
}
.lf-node-text-auto-wrap {
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
/* border: 1px solid transparent; */
}
.lf-node-text-auto-wrap-content {
width: 100%;
line-height: 1.2;
text-align: center;
word-break: break-all;
background: transparent;
}
.lf-node-text-ellipsis-content {
width: 100%;
line-height: 1.2;
white-space: nowrap;
text-align: center;
background: transparent;
/* overflow: hidden;
text-overflow: ellipsis; */
}
.lf-node-text-ellipsis-content > div {
overflow: hidden;
text-overflow: ellipsis;
}
/* tool-overlay */
.lf-tool-overlay {
position: absolute;
inset: 0;
z-index: 2;
overflow: hidden;
pointer-events: none;
}
.lf-tool-overlay > * {
pointer-events: all;
}
/* modification-overlay */
.modification-overlay {
position: absolute;
inset: 0;
z-index: 1;
overflow: hidden;
pointer-events: none;
}
.modification-overlay > * {
pointer-events: all;
}
.lf-outline,
.lf-snapline {
pointer-events: none;
}
.lf-keyboard-tips {
float: right;
}
.lf-node-select-decorate {
position: absolute;
border: 1px dashed #343435;
transform: translate(-50%, -50%);
pointer-events: none;
}
.lf-multiple-select {
position: absolute;
border: 2px dashed #187dffcc;
box-shadow: 0 0 3px 0 #187dff80;
cursor: move;
}
.lf-edge-adjust-point {
cursor: move;
}
.lf-rotate-control {
cursor: grabbing;
}
.lf-resize-control-nw {
cursor: nw-resize;
}
.lf-resize-control-n {
cursor: n-resize;
}
.lf-resize-control-ne {
cursor: ne-resize;
}
.lf-resize-control-e {
cursor: e-resize;
}
.lf-resize-control-se {
cursor: se-resize;
}
.lf-resize-control-s {
cursor: s-resize;
}
.lf-resize-control-sw {
cursor: sw-resize;
}
.lf-resize-control-w {
cursor: w-resize;
}

@ -1,8 +1,16 @@
<!--
* @Author: donghao donghao@supervision.ltd
* @Date: 2025-07-03 10:36:38
* @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2025-07-14 14:06:04
* @FilePath: \Robot-Al-Platform-Web\src\renderer\src\views\Design\Controls\navCtrl.vue
* @Description: 顶部操作栏
-->
<template> <template>
<div class="nav-controls-wrap"> <div class="nav-controls-wrap">
<div class="nav-controls"> <div class="nav-controls">
<template v-for="(item, index) in btnInfo" :key="index"> <template v-for="(item, index) in navControlsConf" :key="index">
<el-button size="small" class="custom-btn"> <el-button size="small" class="custom-btn" @click="handleClick(item)">
<img class="nav-icon" :src="item.icon" alt="" /> <img class="nav-icon" :src="item.icon" alt="" />
<span>{{ item.text }}</span> <span>{{ item.text }}</span>
</el-button> </el-button>
@ -17,81 +25,23 @@
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { navControlsConf, ControlsItemType } from '@/config/designControl'
defineOptions({ defineOptions({
name: 'NavBar' name: 'NavBar'
}) })
import SaveIcon from '@/assets/images/navBar/save.png'
import PrevIcon from '@/assets/images/navBar/prev.png' const emit = defineEmits(['design-flow'])
import NextIcon from '@/assets/images/navBar/next.png'
import LockIcon from '@/assets/images/navBar/lock.png' const handleClick = (record: ControlsItemType) => {
import CameraIcon from '@/assets/images/navBar/camera.png' switch (record?.type) {
import ControlIcon from '@/assets/images/navBar/control.png' case 'lock': //
import GlobalIcon from '@/assets/images/navBar/global.png' emit('design-flow', { ...record })
import CommIcon from '@/assets/images/navBar/comm.png' break
import TriggerIcon from '@/assets/images/navBar/trigger.png' default:
import ScriptIcon from '@/assets/images/navBar/script.png' emit('design-flow', { ...record })
import SingleExecIcon from '@/assets/images/navBar/single_execution.png' break
import ContinuousExecIcon from '@/assets/images/navBar/continuous_execution.png'
import RunInterfaceIcon from '@/assets/images/navBar/run_interface.png'
import RunModelIcon from '@/assets/images/navBar/run_model.png'
const btnInfo = [
{
icon: SaveIcon,
text: '保存'
},
{
icon: PrevIcon,
text: '上一步'
},
{
icon: NextIcon,
text: '下一步'
},
{
icon: LockIcon,
text: '锁定'
},
{
icon: CameraIcon,
text: '相机管理'
},
{
icon: ControlIcon,
text: '控制管理'
},
{
icon: GlobalIcon,
text: '全局变量'
},
{
icon: CommIcon,
text: '通信管理'
},
{
icon: TriggerIcon,
text: '全局触发'
},
{
icon: ScriptIcon,
text: '全局脚本'
},
{
icon: SingleExecIcon,
text: '单次执行'
},
{
icon: ContinuousExecIcon,
text: '连续执行'
},
{
icon: RunInterfaceIcon,
text: '编辑运行界面'
},
{
icon: RunModelIcon,
text: '编辑运行界面'
} }
] }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.nav-controls-wrap { .nav-controls-wrap {

@ -0,0 +1,55 @@
.panel-controls-wrap {
width: 100%;
height: 100%;
display: flex;
justify-content: center;
.panel-btn-box {
margin-top: 12px;
width: 40px;
height: 392px;
background: #565656;
border-radius: 4px;
display: flex;
flex-direction: column;
justify-content: space-around;
.panel-btn {
position: relative;
width: 100%;
height: 32px;
background: #565656;
color: white;
border: none; /* 可选:移除边框 */
margin-left: 0;
padding: 5px 8px;
.panel-icon {
width: 24px;
height: 24px !important;
}
//
&::after {
content: '';
position: absolute;
right: 4px; //
bottom: 4px;
width: 0;
height: 0;
border-style: solid;
border-width: 0 0 4px 4px; //
border-color: transparent transparent #fff transparent; //
}
&:hover {
background: #121212;
}
}
}
}
.panel-controls-menu{
.node-content {
.node-icon {
background-color: #565656 !important;
width: 24px;
height: 24px !important;
}
}
}

@ -2,16 +2,33 @@
<div class="panel-controls-wrap"> <div class="panel-controls-wrap">
<!-- 循环渲染导航项 --> <!-- 循环渲染导航项 -->
<div class="panel-btn-box"> <div class="panel-btn-box">
<el-button size="small" class="panel-btn" v-for="(item, index) in btnInfo" :key="index"> <el-button size="small" class="panel-btn" v-for="(v, k) in nodeTypes" :key="k">
<img class="panel-icon" :src="item.icon" alt="" /> <el-dropdown trigger="click" placement="right-start">
<img class="panel-icon" :src="v.icon" alt="" />
<template #dropdown>
<el-dropdown-menu class="panel-controls-menu">
<el-dropdown-item
v-for="item in v?.children"
:key="item?.type"
class="node-item"
>
<div class="flex items-center node-content" draggable="true" @dragstart="handleDragStart($event, item?.type)">
<img class="mr-[8px] node-icon" :src="item?.icon" alt="" />
<div class="node-info">
<span class="node-label">{{ item?.label }}</span>
</div>
</div>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</el-button> </el-button>
</div> </div>
</div> </div>
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
defineOptions({ import draggable from 'vuedraggable'
name: 'PanelBar'
})
import GatherIcon from '@/assets/images/panelBar/gather.png' // import GatherIcon from '@/assets/images/panelBar/gather.png' //
import LocateIcon from '@/assets/images/panelBar/locate.png' // import LocateIcon from '@/assets/images/panelBar/locate.png' //
import IdentifyIcon from '@/assets/images/panelBar/identify.png' // import IdentifyIcon from '@/assets/images/panelBar/identify.png' //
@ -22,11 +39,39 @@ import ImageGenerationIcon from '@/assets/images/panelBar/image_generation.png'
import LogicalToolsIcon from '@/assets/images/panelBar/logical_tools.png' // import LogicalToolsIcon from '@/assets/images/panelBar/logical_tools.png' //
import CommunicationIcon from '@/assets/images/panelBar/communication.png' // import CommunicationIcon from '@/assets/images/panelBar/communication.png' //
import DeepLearningIcon from '@/assets/images/panelBar/deep_learning.png' // import DeepLearningIcon from '@/assets/images/panelBar/deep_learning.png' //
defineOptions({
const btnInfo = [ name: 'PanelControlBar'
})
const nodeTypes = [
{ {
icon: GatherIcon, icon: GatherIcon,
text: '采集' text: '采集',
children: [
{
type: 'image-node',
label: '图像源',
icon: LocateIcon,
style: { fill: '#F5F5F5', stroke: '#1890FF' }
},
{
type: 'setting',
label: '设置',
icon: IdentifyIcon,
style: { fill: '#F5F5F5', stroke: '#52C41A' }
},
{
type: 'view',
label: '视图',
icon: CalibrateIcon,
style: { fill: '#F5F5F5', stroke: '#FAAD14' }
},
{
type: 'picture',
label: '图片',
icon: ImageProcessingIcon,
style: { fill: '#F5F5F5', stroke: '#F5222D' }
}
]
}, },
{ {
icon: LocateIcon, icon: LocateIcon,
@ -61,51 +106,27 @@ const btnInfo = [
text: '深度学习' text: '深度学习'
} }
] ]
</script>
<style lang="scss" scoped> // const handleDragStart = (item: any) => {
.panel-controls-wrap { // ElMessage.info(`: ${item.label}`)
width: 100%; // // 使
height: 100%; // }
display: flex;
justify-content: center; // const startDrag = (item: any) => {
.panel-btn-box { // // mousedown
margin-top: 12px; // const event = window.event as DragEvent
width: 40px; // if (event && event.dataTransfer) {
height: 392px; // event.dataTransfer.setData('logicflow/node', item.type)
background: #565656; // }
border-radius: 4px; // }
display: flex;
flex-direction: column; const handleDragStart = (event: DragEvent, nodeType: string) => {
justify-content: space-around; if (event.dataTransfer) {
.panel-btn { event.dataTransfer.setData('application/node-type', nodeType);
position: relative; event.dataTransfer.effectAllowed = 'copy';
width: 100%;
height: 32px;
background: #565656;
color: white;
border: none; /* 可选:移除边框 */
margin-left: 0;
padding: 5px 8px;
.panel-icon {
width: 24px;
height: 24px !important;
}
//
&::after {
content: '';
position: absolute;
right: 4px; //
bottom: 4px;
width: 0;
height: 0;
border-style: solid;
border-width: 0 0 4px 4px; //
border-color: transparent transparent #fff transparent; //
}
&:hover {
background: #121212;
}
}
} }
} };
</script>
<style lang="scss">
@import url('./panelCtrl.scss');
</style> </style>

@ -0,0 +1,28 @@
.logic-flow-view-wrap {
font-size: 14px;
.design-panel-box {
position: fixed;
left: 0;
z-index: 2;
}
.flow-editor-container {
margin-left: 64px;
display: flex;
flex-direction: column;
height: 100%;
.empty-canvas{
height: 100%;
}
.canvas-wrapper {
flex: 1;
position: relative;
overflow: hidden;
// background-color: orange;
#lf-container {
width: 100%;
height: 100%;
}
}
}
}

@ -2,10 +2,173 @@
* @Author: donghao donghao@supervision.ltd * @Author: donghao donghao@supervision.ltd
* @Date: 2025-07-03 10:27:47 * @Date: 2025-07-03 10:27:47
* @LastEditors: donghao donghao@supervision.ltd * @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2025-07-03 13:44:43 * @LastEditTime: 2025-07-15 09:54:53
* @FilePath: \electron-project\Robot-Al\Robot-Al-Platform-Web\src\renderer\src\views\Design\Workflow\logicFlowView.vue * @FilePath: \electron-project\Robot-Al\Robot-Al-Platform-Web\src\renderer\src\views\Design\Workflow\logicFlowView.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 流程设计器面板
--> -->
<template> <template>
<div class="h-full logic-flow-view-wrap">画布</div> <div class="logic-flow-view-wrap">
<!-- 流程节点控制面板 -->
<ControlPanel class="design-panel-box" />
<div class="flex-1 w-full flow-editor-container">
<!-- 新建流程 -->
<div v-if="!isHasFlow" class="flex flex-col items-center justify-center empty-canvas">
<!-- // TODO -->
<img class="w-[63px] mb-[20px]" :src="CalibrateIcon" alt="" />
<p>暂无流程配置请添加流程</p>
<DSButton type="primary" size="default" @click="handleNewFlow" class="my-[24px]"
><div class="flex items-center gap-[8px]">
<el-icon class="text-[16px]"><Plus /></el-icon> <span ></span>
</div></DSButton
>
</div>
<!-- 画布区域 -->
<div class="canvas-wrapper" v-else>
<!-- LogicFlow 画布容器 -->
<div
id="lf-container"
ref="flowContainer"
@dragover="handleDragOver"
@drop="handleDrop"
></div>
</div>
</div>
</div>
</template> </template>
<script setup lang="ts">
import LogicFlow, { RectNode } from '@logicflow/core'
// import '@logicflow/core/dist/style/index.css';
import { Menu, SelectionSelect } from '@logicflow/extension'
import CalibrateIcon from '@/assets/images/panelBar/calibrate.png' //
// import '@logicflow/extension/lib/style/index.css'
import ControlPanel from '../Controls/panelCtrl.vue'
import { useLogicFlow } from '@/hooks/useLogicFlow'
import { logicFlowConf, logicFlowThemeConf } from '@/config/designCanvas'
import { DSButton } from '@/components/Button'
const isHasFlow = ref<boolean>(false)
const { graphData } = useLogicFlow()
// 1.
const flowContainer = ref<HTMLDivElement | null>(null)
const lf = ref<LogicFlow | null>(null)
const initDesignFlow = (record) => {
console.log('initDesignFlow_record', record)
if (!isHasFlow.value) {
ElMessage.error('请先创建新流程')
}
}
const registerNodeTypes = () => {
const types = [
'image-node',
'multi-image-node',
'output-image-node',
'cache-image-node',
'trend-node',
'user-node'
]
types.forEach((type) => {
lf.value?.register({
type,
view: RectNode.view,
model: {
...RectNode.model,
width: 120,
height: 50,
getAnchorStyle() {
return {
stroke: '#000'
}
},
getNodeStyle() {
const style = RectNode.model.getNodeStyle?.call(this) || {}
style.stroke = '#ddd'
style.fill = '#fff'
style.radius = 5
return style
}
}
})
})
}
// 2.
const initializeLogicFlow = () => {
lf.value = new LogicFlow({
container: flowContainer.value,
...logicFlowConf
})
lf.value.setTheme({ ...logicFlowThemeConf })
// registerNodeTypes()
lf.value.render(graphData)
}
const handleNewFlow = () => {
lf.value?.clearData()
ElMessage.success('已创建新流程')
isHasFlow.value = true
nextTick(() => initializeLogicFlow())
}
// TODO
//
const handleDragOver = (e: DragEvent) => {
e.preventDefault()
e.dataTransfer!.dropEffect = 'copy'
}
//
const handleDrop = (e: DragEvent) => {
e.preventDefault()
if (!lf.value) return
const nodeType = e.dataTransfer?.getData('application/node-type')
if (!nodeType) return
//
const containerRect = flowContainer.value!.getBoundingClientRect()
const x = e.clientX - containerRect.left
const y = e.clientY - containerRect.top
// LogicFlow
const position = lf.value.translateClientByContainer(x, y)
lf.value.addNode({
type: nodeType,
x: position.x,
y: position.y
})
}
const saveDiagram = () => {
if (lf.value) {
const data = lf.value.getData()
console.log('Diagram Data:', data)
alert('Diagram data saved to console!')
}
}
const undo = () => {
if (lf.value) {
lf.value.undo()
}
}
const redo = () => {
if (lf.value) {
lf.value.redo()
}
}
onMounted(() => {
// initLogicFlow()
})
defineExpose({ initDesignFlow })
</script>
<style lang="scss" scoped>
@import url('./logicFlowView.scss');
</style>

@ -2,7 +2,7 @@
* @Author: donghao donghao@supervision.ltd * @Author: donghao donghao@supervision.ltd
* @Date: 2025-07-02 16:17:29 * @Date: 2025-07-02 16:17:29
* @LastEditors: donghao donghao@supervision.ltd * @LastEditors: donghao donghao@supervision.ltd
* @LastEditTime: 2025-07-10 10:22:11 * @LastEditTime: 2025-07-14 11:42:26
* @FilePath: \electron-project\Robot-Al\Robot-Al-Platform-Web\src\renderer\src\views\Design\index.vue * @FilePath: \electron-project\Robot-Al\Robot-Al-Platform-Web\src\renderer\src\views\Design\index.vue
* @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
--> -->

Loading…
Cancel
Save