mirror of
				https://github.com/pure-admin/vue-pure-admin.git
				synced 2025-11-03 13:44:47 +08:00 
			
		
		
		
	Merge branch 'main' into gitee
This commit is contained in:
		
						commit
						caab9c9843
					
				@ -55,6 +55,8 @@
 | 
				
			|||||||
    "@pureadmin/descriptions": "^1.2.1",
 | 
					    "@pureadmin/descriptions": "^1.2.1",
 | 
				
			||||||
    "@pureadmin/table": "^3.1.2",
 | 
					    "@pureadmin/table": "^3.1.2",
 | 
				
			||||||
    "@pureadmin/utils": "^2.4.7",
 | 
					    "@pureadmin/utils": "^2.4.7",
 | 
				
			||||||
 | 
					    "@vue-flow/background": "^1.3.0",
 | 
				
			||||||
 | 
					    "@vue-flow/core": "^1.33.4",
 | 
				
			||||||
    "@vueuse/core": "^10.9.0",
 | 
					    "@vueuse/core": "^10.9.0",
 | 
				
			||||||
    "@vueuse/motion": "^2.1.0",
 | 
					    "@vueuse/motion": "^2.1.0",
 | 
				
			||||||
    "@wangeditor/editor": "^5.1.23",
 | 
					    "@wangeditor/editor": "^5.1.23",
 | 
				
			||||||
@ -114,6 +116,7 @@
 | 
				
			|||||||
    "@iconify/vue": "^4.1.1",
 | 
					    "@iconify/vue": "^4.1.1",
 | 
				
			||||||
    "@intlify/unplugin-vue-i18n": "^2.0.0",
 | 
					    "@intlify/unplugin-vue-i18n": "^2.0.0",
 | 
				
			||||||
    "@pureadmin/theme": "^3.2.0",
 | 
					    "@pureadmin/theme": "^3.2.0",
 | 
				
			||||||
 | 
					    "@types/dagre": "^0.7.52",
 | 
				
			||||||
    "@types/gradient-string": "^1.1.5",
 | 
					    "@types/gradient-string": "^1.1.5",
 | 
				
			||||||
    "@types/intro.js": "^5.1.5",
 | 
					    "@types/intro.js": "^5.1.5",
 | 
				
			||||||
    "@types/js-cookie": "^3.0.6",
 | 
					    "@types/js-cookie": "^3.0.6",
 | 
				
			||||||
@ -130,6 +133,7 @@
 | 
				
			|||||||
    "boxen": "^7.1.1",
 | 
					    "boxen": "^7.1.1",
 | 
				
			||||||
    "cloc": "^2.11.0",
 | 
					    "cloc": "^2.11.0",
 | 
				
			||||||
    "cssnano": "^6.1.0",
 | 
					    "cssnano": "^6.1.0",
 | 
				
			||||||
 | 
					    "dagre": "^0.8.5",
 | 
				
			||||||
    "eslint": "^8.57.0",
 | 
					    "eslint": "^8.57.0",
 | 
				
			||||||
    "eslint-config-prettier": "^9.1.0",
 | 
					    "eslint-config-prettier": "^9.1.0",
 | 
				
			||||||
    "eslint-define-config": "^2.1.0",
 | 
					    "eslint-define-config": "^2.1.0",
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										118
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										118
									
								
								pnpm-lock.yaml
									
									
									
										generated
									
									
									
								
							@ -26,6 +26,12 @@ dependencies:
 | 
				
			|||||||
  '@pureadmin/utils':
 | 
					  '@pureadmin/utils':
 | 
				
			||||||
    specifier: ^2.4.7
 | 
					    specifier: ^2.4.7
 | 
				
			||||||
    version: 2.4.7(echarts@5.5.0)(vue@3.4.21)
 | 
					    version: 2.4.7(echarts@5.5.0)(vue@3.4.21)
 | 
				
			||||||
 | 
					  '@vue-flow/background':
 | 
				
			||||||
 | 
					    specifier: ^1.3.0
 | 
				
			||||||
 | 
					    version: 1.3.0(@vue-flow/core@1.33.4)(vue@3.4.21)
 | 
				
			||||||
 | 
					  '@vue-flow/core':
 | 
				
			||||||
 | 
					    specifier: ^1.33.4
 | 
				
			||||||
 | 
					    version: 1.33.4(vue@3.4.21)
 | 
				
			||||||
  '@vueuse/core':
 | 
					  '@vueuse/core':
 | 
				
			||||||
    specifier: ^10.9.0
 | 
					    specifier: ^10.9.0
 | 
				
			||||||
    version: 10.9.0(vue@3.4.21)
 | 
					    version: 10.9.0(vue@3.4.21)
 | 
				
			||||||
@ -199,6 +205,9 @@ devDependencies:
 | 
				
			|||||||
  '@pureadmin/theme':
 | 
					  '@pureadmin/theme':
 | 
				
			||||||
    specifier: ^3.2.0
 | 
					    specifier: ^3.2.0
 | 
				
			||||||
    version: 3.2.0
 | 
					    version: 3.2.0
 | 
				
			||||||
 | 
					  '@types/dagre':
 | 
				
			||||||
 | 
					    specifier: ^0.7.52
 | 
				
			||||||
 | 
					    version: 0.7.52
 | 
				
			||||||
  '@types/gradient-string':
 | 
					  '@types/gradient-string':
 | 
				
			||||||
    specifier: ^1.1.5
 | 
					    specifier: ^1.1.5
 | 
				
			||||||
    version: 1.1.5
 | 
					    version: 1.1.5
 | 
				
			||||||
@ -247,6 +256,9 @@ devDependencies:
 | 
				
			|||||||
  cssnano:
 | 
					  cssnano:
 | 
				
			||||||
    specifier: ^6.1.0
 | 
					    specifier: ^6.1.0
 | 
				
			||||||
    version: 6.1.0(postcss@8.4.35)
 | 
					    version: 6.1.0(postcss@8.4.35)
 | 
				
			||||||
 | 
					  dagre:
 | 
				
			||||||
 | 
					    specifier: ^0.8.5
 | 
				
			||||||
 | 
					    version: 0.8.5
 | 
				
			||||||
  eslint:
 | 
					  eslint:
 | 
				
			||||||
    specifier: ^8.57.0
 | 
					    specifier: ^8.57.0
 | 
				
			||||||
    version: 8.57.0
 | 
					    version: 8.57.0
 | 
				
			||||||
@ -2003,6 +2015,10 @@ packages:
 | 
				
			|||||||
      '@types/node': 20.11.27
 | 
					      '@types/node': 20.11.27
 | 
				
			||||||
    dev: true
 | 
					    dev: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /@types/dagre@0.7.52:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-XKJdy+OClLk3hketHi9Qg6gTfe1F3y+UFnHxKA2rn9Dw+oXa4Gb378Ztz9HlMgZKSxpPmn4BNVh9wgkpvrK1uw==}
 | 
				
			||||||
 | 
					    dev: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /@types/estree@1.0.5:
 | 
					  /@types/estree@1.0.5:
 | 
				
			||||||
    resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
 | 
					    resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -2352,6 +2368,30 @@ packages:
 | 
				
			|||||||
      path-browserify: 1.0.1
 | 
					      path-browserify: 1.0.1
 | 
				
			||||||
    dev: true
 | 
					    dev: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /@vue-flow/background@1.3.0(@vue-flow/core@1.33.4)(vue@3.4.21):
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-fu/8s9wzSOQIitnSTI10XT3bzTtagh4h8EF2SWwtlDklOZjAaKy75lqv4htHa3wigy/r4LGCOGwLw3Pk88/AxA==}
 | 
				
			||||||
 | 
					    peerDependencies:
 | 
				
			||||||
 | 
					      '@vue-flow/core': ^1.23.0
 | 
				
			||||||
 | 
					      vue: ^3.3.0
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      '@vue-flow/core': 1.33.4(vue@3.4.21)
 | 
				
			||||||
 | 
					      vue: 3.4.21(typescript@5.4.2)
 | 
				
			||||||
 | 
					    dev: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /@vue-flow/core@1.33.4(vue@3.4.21):
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-ryoamKfQ5pgtdv//Gjpyc4nsawMOwfI2jVzOPvZ92VQs78L4lidiWD7UybqeEkrGw6UPue1CGlzoy/4KlOWcSg==}
 | 
				
			||||||
 | 
					    peerDependencies:
 | 
				
			||||||
 | 
					      vue: ^3.3.0
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      '@vueuse/core': 10.9.0(vue@3.4.21)
 | 
				
			||||||
 | 
					      d3-drag: 3.0.0
 | 
				
			||||||
 | 
					      d3-selection: 3.0.0
 | 
				
			||||||
 | 
					      d3-zoom: 3.0.0
 | 
				
			||||||
 | 
					      vue: 3.4.21(typescript@5.4.2)
 | 
				
			||||||
 | 
					    transitivePeerDependencies:
 | 
				
			||||||
 | 
					      - '@vue/composition-api'
 | 
				
			||||||
 | 
					    dev: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /@vue/babel-helper-vue-transform-on@1.2.1:
 | 
					  /@vue/babel-helper-vue-transform-on@1.2.1:
 | 
				
			||||||
    resolution: {integrity: sha512-jtEXim+pfyHWwvheYwUwSXm43KwQo8nhOBDyjrUITV6X2tB7lJm6n/+4sqR8137UVZZul5hBzWHdZ2uStYpyRQ==}
 | 
					    resolution: {integrity: sha512-jtEXim+pfyHWwvheYwUwSXm43KwQo8nhOBDyjrUITV6X2tB7lJm6n/+4sqR8137UVZZul5hBzWHdZ2uStYpyRQ==}
 | 
				
			||||||
    dev: true
 | 
					    dev: true
 | 
				
			||||||
@ -3851,6 +3891,71 @@ packages:
 | 
				
			|||||||
  /csstype@3.1.3:
 | 
					  /csstype@3.1.3:
 | 
				
			||||||
    resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
 | 
					    resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /d3-color@3.1.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==}
 | 
				
			||||||
 | 
					    engines: {node: '>=12'}
 | 
				
			||||||
 | 
					    dev: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /d3-dispatch@3.0.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==}
 | 
				
			||||||
 | 
					    engines: {node: '>=12'}
 | 
				
			||||||
 | 
					    dev: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /d3-drag@3.0.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==}
 | 
				
			||||||
 | 
					    engines: {node: '>=12'}
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      d3-dispatch: 3.0.1
 | 
				
			||||||
 | 
					      d3-selection: 3.0.0
 | 
				
			||||||
 | 
					    dev: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /d3-ease@3.0.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==}
 | 
				
			||||||
 | 
					    engines: {node: '>=12'}
 | 
				
			||||||
 | 
					    dev: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /d3-interpolate@3.0.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==}
 | 
				
			||||||
 | 
					    engines: {node: '>=12'}
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      d3-color: 3.1.0
 | 
				
			||||||
 | 
					    dev: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /d3-selection@3.0.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==}
 | 
				
			||||||
 | 
					    engines: {node: '>=12'}
 | 
				
			||||||
 | 
					    dev: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /d3-timer@3.0.1:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==}
 | 
				
			||||||
 | 
					    engines: {node: '>=12'}
 | 
				
			||||||
 | 
					    dev: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /d3-transition@3.0.1(d3-selection@3.0.0):
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==}
 | 
				
			||||||
 | 
					    engines: {node: '>=12'}
 | 
				
			||||||
 | 
					    peerDependencies:
 | 
				
			||||||
 | 
					      d3-selection: 2 - 3
 | 
				
			||||||
 | 
					    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
 | 
				
			||||||
 | 
					    dev: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /d3-zoom@3.0.0:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==}
 | 
				
			||||||
 | 
					    engines: {node: '>=12'}
 | 
				
			||||||
 | 
					    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)
 | 
				
			||||||
 | 
					    dev: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /d@1.0.2:
 | 
					  /d@1.0.2:
 | 
				
			||||||
    resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==}
 | 
					    resolution: {integrity: sha512-MOqHvMWF9/9MX6nza0KgvFH4HpMU0EF5uUDXqX/BtxtU8NfB0QzRtJ8Oe/6SuS4kbhyzVJwjd97EA4PKrzJ8bw==}
 | 
				
			||||||
    engines: {node: '>=0.12'}
 | 
					    engines: {node: '>=0.12'}
 | 
				
			||||||
@ -3859,6 +3964,13 @@ packages:
 | 
				
			|||||||
      type: 2.7.2
 | 
					      type: 2.7.2
 | 
				
			||||||
    dev: false
 | 
					    dev: false
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /dagre@0.8.5:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-/aTqmnRta7x7MCCpExk7HQL2O4owCT2h8NT//9I1OQ9vt29Pa0BzSAkR5lwFUcQ7491yVi/3CXU9jQ5o0Mn2Sw==}
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      graphlib: 2.1.8
 | 
				
			||||||
 | 
					      lodash: 4.17.21
 | 
				
			||||||
 | 
					    dev: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /danmu.js@1.1.13:
 | 
					  /danmu.js@1.1.13:
 | 
				
			||||||
    resolution: {integrity: sha512-knFd0/cB2HA4FFWiA7eB2suc5vCvoHdqio33FyyCSfP7C+1A+zQcTvnvwfxaZhrxsGj4qaQI2I8XiTqedRaVmg==}
 | 
					    resolution: {integrity: sha512-knFd0/cB2HA4FFWiA7eB2suc5vCvoHdqio33FyyCSfP7C+1A+zQcTvnvwfxaZhrxsGj4qaQI2I8XiTqedRaVmg==}
 | 
				
			||||||
    dependencies:
 | 
					    dependencies:
 | 
				
			||||||
@ -4962,6 +5074,12 @@ packages:
 | 
				
			|||||||
    resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
 | 
					    resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==}
 | 
				
			||||||
    dev: true
 | 
					    dev: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /graphlib@2.1.8:
 | 
				
			||||||
 | 
					    resolution: {integrity: sha512-jcLLfkpoVGmH7/InMC/1hIvOPSUh38oJtGhvrOFGzioE1DZ+0YW16RgmOJhHiuWTvGiJQ9Z1Ik43JvkRPRvE+A==}
 | 
				
			||||||
 | 
					    dependencies:
 | 
				
			||||||
 | 
					      lodash: 4.17.21
 | 
				
			||||||
 | 
					    dev: true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /has-flag@3.0.0:
 | 
					  /has-flag@3.0.0:
 | 
				
			||||||
    resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
 | 
					    resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==}
 | 
				
			||||||
    engines: {node: '>=4'}
 | 
					    engines: {node: '>=4'}
 | 
				
			||||||
 | 
				
			|||||||
@ -1,29 +1,31 @@
 | 
				
			|||||||
// 完整版菜单比较多,将 rank 抽离出来,在此方便维护
 | 
					// 完整版菜单比较多,将 rank 抽离出来,在此方便维护
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const home = 0, // 平台规定只有 home 路由的 rank 才能为 0 ,所以后端在返回 rank 的时候需要从非 0 开始
 | 
					const home = 0, // 平台规定只有 home 路由的 rank 才能为 0 ,所以后端在返回 rank 的时候需要从非 0 开始
 | 
				
			||||||
  components = 1,
 | 
					  vueflow = 1,
 | 
				
			||||||
  able = 2,
 | 
					  components = 2,
 | 
				
			||||||
  table = 3,
 | 
					  able = 3,
 | 
				
			||||||
  list = 4,
 | 
					  table = 4,
 | 
				
			||||||
  result = 5,
 | 
					  list = 5,
 | 
				
			||||||
  error = 6,
 | 
					  result = 6,
 | 
				
			||||||
  frame = 7,
 | 
					  error = 7,
 | 
				
			||||||
  nested = 8,
 | 
					  frame = 8,
 | 
				
			||||||
  permission = 9,
 | 
					  nested = 9,
 | 
				
			||||||
  system = 10,
 | 
					  permission = 10,
 | 
				
			||||||
  monitor = 11,
 | 
					  system = 11,
 | 
				
			||||||
  tabs = 12,
 | 
					  monitor = 12,
 | 
				
			||||||
  about = 13,
 | 
					  tabs = 13,
 | 
				
			||||||
  editor = 14,
 | 
					  about = 14,
 | 
				
			||||||
  flowchart = 15,
 | 
					  editor = 15,
 | 
				
			||||||
  formdesign = 16,
 | 
					  flowchart = 16,
 | 
				
			||||||
  board = 17,
 | 
					  formdesign = 17,
 | 
				
			||||||
  ppt = 18,
 | 
					  board = 18,
 | 
				
			||||||
  guide = 19,
 | 
					  ppt = 19,
 | 
				
			||||||
  menuoverflow = 20;
 | 
					  guide = 20,
 | 
				
			||||||
 | 
					  menuoverflow = 21;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export {
 | 
					export {
 | 
				
			||||||
  home,
 | 
					  home,
 | 
				
			||||||
 | 
					  vueflow,
 | 
				
			||||||
  components,
 | 
					  components,
 | 
				
			||||||
  able,
 | 
					  able,
 | 
				
			||||||
  table,
 | 
					  table,
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										22
									
								
								src/router/modules/vueflow.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								src/router/modules/vueflow.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					import { vueflow } from "@/router/enums";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
 | 
					  path: "/vue-flow",
 | 
				
			||||||
 | 
					  redirect: "/vue-flow/index",
 | 
				
			||||||
 | 
					  meta: {
 | 
				
			||||||
 | 
					    icon: "ep:set-up",
 | 
				
			||||||
 | 
					    title: "vue-flow",
 | 
				
			||||||
 | 
					    rank: vueflow
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  children: [
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					      path: "/vue-flow/index",
 | 
				
			||||||
 | 
					      name: "VueFlow",
 | 
				
			||||||
 | 
					      component: () => import("@/views/vue-flow/layouting/index.vue"),
 | 
				
			||||||
 | 
					      meta: {
 | 
				
			||||||
 | 
					        title: "vue-flow",
 | 
				
			||||||
 | 
					        extraIcon: "IF-pure-iconfont-new svg"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  ]
 | 
				
			||||||
 | 
					} satisfies RouteConfigsTable;
 | 
				
			||||||
							
								
								
									
										214
									
								
								src/views/vue-flow/layouting/animationEdge.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								src/views/vue-flow/layouting/animationEdge.vue
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,214 @@
 | 
				
			|||||||
 | 
					<script lang="ts" setup>
 | 
				
			||||||
 | 
					import { computed, nextTick, ref, toRef, watch } from "vue";
 | 
				
			||||||
 | 
					import { TransitionPresets, executeTransition } from "@vueuse/core";
 | 
				
			||||||
 | 
					import {
 | 
				
			||||||
 | 
					  Position,
 | 
				
			||||||
 | 
					  BaseEdge,
 | 
				
			||||||
 | 
					  useVueFlow,
 | 
				
			||||||
 | 
					  useNodesData,
 | 
				
			||||||
 | 
					  getSmoothStepPath,
 | 
				
			||||||
 | 
					  EdgeLabelRenderer
 | 
				
			||||||
 | 
					} from "@vue-flow/core";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const props = defineProps({
 | 
				
			||||||
 | 
					  id: {
 | 
				
			||||||
 | 
					    type: String,
 | 
				
			||||||
 | 
					    required: true
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  source: {
 | 
				
			||||||
 | 
					    type: String,
 | 
				
			||||||
 | 
					    required: true
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  target: {
 | 
				
			||||||
 | 
					    type: String,
 | 
				
			||||||
 | 
					    required: true
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  sourceX: {
 | 
				
			||||||
 | 
					    type: Number,
 | 
				
			||||||
 | 
					    required: true
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  sourceY: {
 | 
				
			||||||
 | 
					    type: Number,
 | 
				
			||||||
 | 
					    required: true
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  targetX: {
 | 
				
			||||||
 | 
					    type: Number,
 | 
				
			||||||
 | 
					    required: true
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  targetY: {
 | 
				
			||||||
 | 
					    type: Number,
 | 
				
			||||||
 | 
					    required: true
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  sourcePosition: {
 | 
				
			||||||
 | 
					    type: String,
 | 
				
			||||||
 | 
					    default: Position.Right
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  targetPosition: {
 | 
				
			||||||
 | 
					    type: String,
 | 
				
			||||||
 | 
					    default: Position.Left
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const { findEdge } = useVueFlow();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const nodesData = useNodesData([props.target, props.source]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const edgePoint = ref(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const edgeRef = ref();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const labelPosition = ref({ x: 0, y: 0 });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const currentLength = ref(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const targetNodeData = toRef(() => nodesData.value[0].data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const sourceNodeData = toRef(() => nodesData.value[1].data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const isFinished = toRef(() => sourceNodeData.value.isFinished);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const isCancelled = toRef(() => targetNodeData.value.isCancelled);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const isAnimating = ref(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const edgeColor = toRef(() => {
 | 
				
			||||||
 | 
					  if (targetNodeData.value.hasError) {
 | 
				
			||||||
 | 
					    return "#f87171";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (targetNodeData.value.isFinished) {
 | 
				
			||||||
 | 
					    return "#42B983";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (targetNodeData.value.isCancelled || targetNodeData.value.isSkipped) {
 | 
				
			||||||
 | 
					    return "#fbbf24";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (targetNodeData.value.isRunning || isAnimating.value) {
 | 
				
			||||||
 | 
					    return "#2563eb";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return "#6b7280";
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// @ts-expect-error
 | 
				
			||||||
 | 
					const path = computed(() => getSmoothStepPath(props));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					watch(isCancelled, isCancelled => {
 | 
				
			||||||
 | 
					  if (isCancelled) {
 | 
				
			||||||
 | 
					    reset();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					watch(isAnimating, isAnimating => {
 | 
				
			||||||
 | 
					  const edge = findEdge(props.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (edge) {
 | 
				
			||||||
 | 
					    edge.data = {
 | 
				
			||||||
 | 
					      ...edge.data,
 | 
				
			||||||
 | 
					      isAnimating
 | 
				
			||||||
 | 
					    };
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					watch(edgePoint, point => {
 | 
				
			||||||
 | 
					  const pathEl = edgeRef.value?.pathEl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!pathEl || point === 0 || !isAnimating.value) {
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const nextLength = pathEl.getTotalLength();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (currentLength.value !== nextLength) {
 | 
				
			||||||
 | 
					    runAnimation();
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  labelPosition.value = pathEl.getPointAtLength(point);
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					watch(isFinished, isFinished => {
 | 
				
			||||||
 | 
					  if (isFinished) {
 | 
				
			||||||
 | 
					    runAnimation();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function runAnimation() {
 | 
				
			||||||
 | 
					  const pathEl = edgeRef.value?.pathEl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!pathEl) {
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const totalLength = pathEl.getTotalLength();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const from = edgePoint.value || 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  labelPosition.value = pathEl.getPointAtLength(from);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  isAnimating.value = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (currentLength.value !== totalLength) {
 | 
				
			||||||
 | 
					    currentLength.value = totalLength;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  await executeTransition(edgePoint, from, totalLength, {
 | 
				
			||||||
 | 
					    transition: TransitionPresets.easeInOutCubic,
 | 
				
			||||||
 | 
					    duration: Math.max(1500, totalLength / 2),
 | 
				
			||||||
 | 
					    abort: () => !isAnimating.value
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  reset();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function reset() {
 | 
				
			||||||
 | 
					  nextTick(() => {
 | 
				
			||||||
 | 
					    edgePoint.value = 0;
 | 
				
			||||||
 | 
					    currentLength.value = 0;
 | 
				
			||||||
 | 
					    labelPosition.value = { x: 0, y: 0 };
 | 
				
			||||||
 | 
					    isAnimating.value = false;
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <BaseEdge
 | 
				
			||||||
 | 
					    :id="id"
 | 
				
			||||||
 | 
					    ref="edgeRef"
 | 
				
			||||||
 | 
					    :path="path[0]"
 | 
				
			||||||
 | 
					    :style="{ stroke: edgeColor }"
 | 
				
			||||||
 | 
					  />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <EdgeLabelRenderer v-if="isAnimating">
 | 
				
			||||||
 | 
					    <div
 | 
				
			||||||
 | 
					      :style="{
 | 
				
			||||||
 | 
					        transform: `translate(-50%, -50%) translate(${labelPosition.x}px,${labelPosition.y}px)`
 | 
				
			||||||
 | 
					      }"
 | 
				
			||||||
 | 
					      class="nodrag nopan animated-edge-label"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <span class="truck">
 | 
				
			||||||
 | 
					        <span class="box">📦</span>
 | 
				
			||||||
 | 
					        🚚
 | 
				
			||||||
 | 
					      </span>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					  </EdgeLabelRenderer>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style scoped>
 | 
				
			||||||
 | 
					.animated-edge-label {
 | 
				
			||||||
 | 
					  position: absolute;
 | 
				
			||||||
 | 
					  z-index: 100;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.truck {
 | 
				
			||||||
 | 
					  position: relative;
 | 
				
			||||||
 | 
					  display: inline-block;
 | 
				
			||||||
 | 
					  transform: scaleX(-1);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.box {
 | 
				
			||||||
 | 
					  position: absolute;
 | 
				
			||||||
 | 
					  top: -10px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
							
								
								
									
										85
									
								
								src/views/vue-flow/layouting/icon.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										85
									
								
								src/views/vue-flow/layouting/icon.vue
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,85 @@
 | 
				
			|||||||
 | 
					<script lang="ts" setup>
 | 
				
			||||||
 | 
					defineProps({
 | 
				
			||||||
 | 
					  name: {
 | 
				
			||||||
 | 
					    type: String,
 | 
				
			||||||
 | 
					    required: true
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <svg
 | 
				
			||||||
 | 
					    v-if="name === 'play'"
 | 
				
			||||||
 | 
					    viewBox="0 0 24 24"
 | 
				
			||||||
 | 
					    height="24"
 | 
				
			||||||
 | 
					    xmlns="http://www.w3.org/2000/svg"
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					    <path d="M8 5v14l11-7z" fill="currentColor" />
 | 
				
			||||||
 | 
					  </svg>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <svg
 | 
				
			||||||
 | 
					    v-else-if="name === 'stop'"
 | 
				
			||||||
 | 
					    xmlns="http://www.w3.org/2000/svg"
 | 
				
			||||||
 | 
					    viewBox="0 0 24 24"
 | 
				
			||||||
 | 
					    height="24"
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					      fill="currentColor"
 | 
				
			||||||
 | 
					      d="M8 16h8V8H8zm4 6q-2.075 0-3.9-.788t-3.175-2.137q-1.35-1.35-2.137-3.175T2 12q0-2.075.788-3.9t2.137-3.175q1.35-1.35 3.175-2.137T12 2q2.075 0 3.9.788t3.175 2.137q1.35 1.35 2.138 3.175T22 12q0 2.075-.788 3.9t-2.137 3.175q-1.35 1.35-3.175 2.138T12 22"
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					  </svg>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <svg
 | 
				
			||||||
 | 
					    v-else-if="name === 'horizontal'"
 | 
				
			||||||
 | 
					    viewBox="0 0 24 24"
 | 
				
			||||||
 | 
					    height="24"
 | 
				
			||||||
 | 
					    xmlns="http://www.w3.org/2000/svg"
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					    <path d="M2,12 L22,12" stroke="currentColor" stroke-width="2" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					      d="M7,7 L2,12 L7,17"
 | 
				
			||||||
 | 
					      stroke="currentColor"
 | 
				
			||||||
 | 
					      stroke-width="2"
 | 
				
			||||||
 | 
					      fill="none"
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					      d="M17,7 L22,12 L17,17"
 | 
				
			||||||
 | 
					      stroke="currentColor"
 | 
				
			||||||
 | 
					      stroke-width="2"
 | 
				
			||||||
 | 
					      fill="none"
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					  </svg>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <svg
 | 
				
			||||||
 | 
					    v-else-if="name === 'vertical'"
 | 
				
			||||||
 | 
					    viewBox="0 0 24 24"
 | 
				
			||||||
 | 
					    height="24"
 | 
				
			||||||
 | 
					    xmlns="http://www.w3.org/2000/svg"
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					    <path d="M12,2 L12,22" stroke="currentColor" stroke-width="2" />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					      d="M7,7 L12,2 L17,7"
 | 
				
			||||||
 | 
					      stroke="currentColor"
 | 
				
			||||||
 | 
					      stroke-width="2"
 | 
				
			||||||
 | 
					      fill="none"
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					      d="M7,17 L12,22 L17,17"
 | 
				
			||||||
 | 
					      stroke="currentColor"
 | 
				
			||||||
 | 
					      stroke-width="2"
 | 
				
			||||||
 | 
					      fill="none"
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					  </svg>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  <svg
 | 
				
			||||||
 | 
					    v-else-if="name === 'shuffle'"
 | 
				
			||||||
 | 
					    xmlns="http://www.w3.org/2000/svg"
 | 
				
			||||||
 | 
					    viewBox="0 0 24 24"
 | 
				
			||||||
 | 
					    height="24"
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					    <path
 | 
				
			||||||
 | 
					      fill="currentColor"
 | 
				
			||||||
 | 
					      d="M14 20v-2h2.6l-3.175-3.175L14.85 13.4L18 16.55V14h2v6zm-8.6 0L4 18.6L16.6 6H14V4h6v6h-2V7.4zm3.775-9.425L4 5.4L5.4 4l5.175 5.175z"
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					  </svg>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
							
								
								
									
										214
									
								
								src/views/vue-flow/layouting/index.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										214
									
								
								src/views/vue-flow/layouting/index.vue
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,214 @@
 | 
				
			|||||||
 | 
					<script lang="ts" setup>
 | 
				
			||||||
 | 
					import "@vue-flow/core/dist/style.css";
 | 
				
			||||||
 | 
					import "@vue-flow/core/dist/theme-default.css";
 | 
				
			||||||
 | 
					import Icon from "./icon.vue";
 | 
				
			||||||
 | 
					import { nextTick, ref } from "vue";
 | 
				
			||||||
 | 
					import { useLayout } from "./useLayout";
 | 
				
			||||||
 | 
					import { useShuffle } from "./useShuffle";
 | 
				
			||||||
 | 
					import ProcessNode from "./processNode.vue";
 | 
				
			||||||
 | 
					import { useRunProcess } from "./useRunProcess";
 | 
				
			||||||
 | 
					import AnimationEdge from "./animationEdge.vue";
 | 
				
			||||||
 | 
					import { Background } from "@vue-flow/background";
 | 
				
			||||||
 | 
					import { Panel, VueFlow, useVueFlow } from "@vue-flow/core";
 | 
				
			||||||
 | 
					import { initialEdges, initialNodes } from "./initialElements";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const nodes = ref(initialNodes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const edges = ref(initialEdges);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const cancelOnError = ref(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const shuffle = useShuffle();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const { graph, layout, previousDirection } = useLayout();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// @ts-expect-error
 | 
				
			||||||
 | 
					const { run, stop, reset, isRunning } = useRunProcess({ graph, cancelOnError });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const { fitView } = useVueFlow();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function shuffleGraph() {
 | 
				
			||||||
 | 
					  await stop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  reset(nodes.value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  edges.value = shuffle(nodes.value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  nextTick(() => {
 | 
				
			||||||
 | 
					    layoutGraph(previousDirection.value);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function layoutGraph(direction) {
 | 
				
			||||||
 | 
					  await stop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  reset(nodes.value);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  nodes.value = layout(nodes.value, edges.value, direction);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  nextTick(() => {
 | 
				
			||||||
 | 
					    fitView();
 | 
				
			||||||
 | 
					    run(nodes.value);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <div class="layout-flow">
 | 
				
			||||||
 | 
					    <VueFlow
 | 
				
			||||||
 | 
					      :nodes="nodes"
 | 
				
			||||||
 | 
					      :edges="edges"
 | 
				
			||||||
 | 
					      @nodes-initialized="layoutGraph('LR')"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <template #node-process="props">
 | 
				
			||||||
 | 
					        <ProcessNode
 | 
				
			||||||
 | 
					          :data="props.data"
 | 
				
			||||||
 | 
					          :source-position="props.sourcePosition"
 | 
				
			||||||
 | 
					          :target-position="props.targetPosition"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					      </template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <template #edge-animation="edgeProps">
 | 
				
			||||||
 | 
					        <AnimationEdge
 | 
				
			||||||
 | 
					          :id="edgeProps.id"
 | 
				
			||||||
 | 
					          :source="edgeProps.source"
 | 
				
			||||||
 | 
					          :target="edgeProps.target"
 | 
				
			||||||
 | 
					          :source-x="edgeProps.sourceX"
 | 
				
			||||||
 | 
					          :source-y="edgeProps.sourceY"
 | 
				
			||||||
 | 
					          :targetX="edgeProps.targetX"
 | 
				
			||||||
 | 
					          :targetY="edgeProps.targetY"
 | 
				
			||||||
 | 
					          :source-position="edgeProps.sourcePosition"
 | 
				
			||||||
 | 
					          :target-position="edgeProps.targetPosition"
 | 
				
			||||||
 | 
					        />
 | 
				
			||||||
 | 
					      </template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <Background />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <Panel class="process-panel" position="top-left">
 | 
				
			||||||
 | 
					        <div class="layout-panel">
 | 
				
			||||||
 | 
					          <button v-if="isRunning" class="stop-btn" title="stop" @click="stop">
 | 
				
			||||||
 | 
					            <Icon name="stop" />
 | 
				
			||||||
 | 
					            <span class="spinner" />
 | 
				
			||||||
 | 
					          </button>
 | 
				
			||||||
 | 
					          <button v-else title="start" @click="run(nodes)">
 | 
				
			||||||
 | 
					            <Icon name="play" />
 | 
				
			||||||
 | 
					          </button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <button title="set horizontal layout" @click="layoutGraph('LR')">
 | 
				
			||||||
 | 
					            <Icon name="horizontal" />
 | 
				
			||||||
 | 
					          </button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <button title="set vertical layout" @click="layoutGraph('TB')">
 | 
				
			||||||
 | 
					            <Icon name="vertical" />
 | 
				
			||||||
 | 
					          </button>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          <button title="shuffle graph" @click="shuffleGraph">
 | 
				
			||||||
 | 
					            <Icon name="shuffle" />
 | 
				
			||||||
 | 
					          </button>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!-- <div class="checkbox-panel">
 | 
				
			||||||
 | 
					          <label>Cancel on error</label>
 | 
				
			||||||
 | 
					          <input v-model="cancelOnError" type="checkbox" />
 | 
				
			||||||
 | 
					        </div> -->
 | 
				
			||||||
 | 
					      </Panel>
 | 
				
			||||||
 | 
					    </VueFlow>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style scoped>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@keyframes spin {
 | 
				
			||||||
 | 
					  0% {
 | 
				
			||||||
 | 
					    transform: rotate(0deg);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  100% {
 | 
				
			||||||
 | 
					    transform: rotate(360deg);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*,
 | 
				
			||||||
 | 
					::before,
 | 
				
			||||||
 | 
					::after {
 | 
				
			||||||
 | 
					  box-sizing: content-box;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.main-content {
 | 
				
			||||||
 | 
					  margin: 0 !important;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.layout-flow {
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  height: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.process-panel,
 | 
				
			||||||
 | 
					.layout-panel {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  gap: 10px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.process-panel {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					  padding: 10px;
 | 
				
			||||||
 | 
					  background-color: #2d3748;
 | 
				
			||||||
 | 
					  border-radius: 8px;
 | 
				
			||||||
 | 
					  box-shadow: 0 0 10px rgb(0 0 0 / 50%);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.process-panel button {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					  width: 40px;
 | 
				
			||||||
 | 
					  height: 40px;
 | 
				
			||||||
 | 
					  font-size: 16px;
 | 
				
			||||||
 | 
					  color: white;
 | 
				
			||||||
 | 
					  cursor: pointer;
 | 
				
			||||||
 | 
					  background-color: #4a5568;
 | 
				
			||||||
 | 
					  border: none;
 | 
				
			||||||
 | 
					  border-radius: 8px;
 | 
				
			||||||
 | 
					  box-shadow: 0 0 10px rgb(0 0 0 / 50%);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* .checkbox-panel {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  gap: 10px;
 | 
				
			||||||
 | 
					} */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.process-panel button:hover,
 | 
				
			||||||
 | 
					.layout-panel button:hover {
 | 
				
			||||||
 | 
					  background-color: #2563eb;
 | 
				
			||||||
 | 
					  transition: background-color 0.2s;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.process-panel label {
 | 
				
			||||||
 | 
					  font-size: 12px;
 | 
				
			||||||
 | 
					  color: white;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.stop-btn svg {
 | 
				
			||||||
 | 
					  display: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.stop-btn:hover svg {
 | 
				
			||||||
 | 
					  display: block;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.stop-btn:hover .spinner {
 | 
				
			||||||
 | 
					  display: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.spinner {
 | 
				
			||||||
 | 
					  width: 20px;
 | 
				
			||||||
 | 
					  height: 20px;
 | 
				
			||||||
 | 
					  border: 3px solid #f3f3f3;
 | 
				
			||||||
 | 
					  border-top: 3px solid #2563eb;
 | 
				
			||||||
 | 
					  border-radius: 50%;
 | 
				
			||||||
 | 
					  animation: spin 1s linear infinite;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
							
								
								
									
										76
									
								
								src/views/vue-flow/layouting/initialElements.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										76
									
								
								src/views/vue-flow/layouting/initialElements.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,76 @@
 | 
				
			|||||||
 | 
					import type { Edge, Node } from "@vue-flow/core";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const position = { x: 0, y: 0 };
 | 
				
			||||||
 | 
					const nodeType = "process";
 | 
				
			||||||
 | 
					const edgeType = "animation";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const initialNodes: Node[] = [
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: "1",
 | 
				
			||||||
 | 
					    position,
 | 
				
			||||||
 | 
					    type: nodeType
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: "2",
 | 
				
			||||||
 | 
					    position,
 | 
				
			||||||
 | 
					    type: nodeType
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: "2a",
 | 
				
			||||||
 | 
					    position,
 | 
				
			||||||
 | 
					    type: nodeType
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: "2b",
 | 
				
			||||||
 | 
					    position,
 | 
				
			||||||
 | 
					    type: nodeType
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: "2c",
 | 
				
			||||||
 | 
					    position,
 | 
				
			||||||
 | 
					    type: nodeType
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: "2d",
 | 
				
			||||||
 | 
					    position,
 | 
				
			||||||
 | 
					    type: nodeType
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: "3",
 | 
				
			||||||
 | 
					    position,
 | 
				
			||||||
 | 
					    type: nodeType
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: "4",
 | 
				
			||||||
 | 
					    position,
 | 
				
			||||||
 | 
					    type: nodeType
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: "5",
 | 
				
			||||||
 | 
					    position,
 | 
				
			||||||
 | 
					    type: nodeType
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: "6",
 | 
				
			||||||
 | 
					    position,
 | 
				
			||||||
 | 
					    type: nodeType
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    id: "7",
 | 
				
			||||||
 | 
					    position,
 | 
				
			||||||
 | 
					    type: nodeType
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export const initialEdges: Edge[] = [
 | 
				
			||||||
 | 
					  { id: "e1-2", source: "1", target: "2", type: edgeType, animated: true },
 | 
				
			||||||
 | 
					  { id: "e1-3", source: "1", target: "3", type: edgeType, animated: true },
 | 
				
			||||||
 | 
					  { id: "e2-2a", source: "2", target: "2a", type: edgeType, animated: true },
 | 
				
			||||||
 | 
					  { id: "e2-2b", source: "2", target: "2b", type: edgeType, animated: true },
 | 
				
			||||||
 | 
					  { id: "e2-2c", source: "2", target: "2c", type: edgeType, animated: true },
 | 
				
			||||||
 | 
					  { id: "e2c-2d", source: "2c", target: "2d", type: edgeType, animated: true },
 | 
				
			||||||
 | 
					  { id: "e3-7", source: "3", target: "4", type: edgeType, animated: true },
 | 
				
			||||||
 | 
					  { id: "e4-5", source: "4", target: "5", type: edgeType, animated: true },
 | 
				
			||||||
 | 
					  { id: "e5-6", source: "5", target: "6", type: edgeType, animated: true },
 | 
				
			||||||
 | 
					  { id: "e5-7", source: "5", target: "7", type: edgeType, animated: true }
 | 
				
			||||||
 | 
					];
 | 
				
			||||||
							
								
								
									
										145
									
								
								src/views/vue-flow/layouting/processNode.vue
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										145
									
								
								src/views/vue-flow/layouting/processNode.vue
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,145 @@
 | 
				
			|||||||
 | 
					<script lang="ts" setup>
 | 
				
			||||||
 | 
					import { toRef } from "vue";
 | 
				
			||||||
 | 
					import { Handle, useHandleConnections } from "@vue-flow/core";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const props = defineProps({
 | 
				
			||||||
 | 
					  data: {
 | 
				
			||||||
 | 
					    type: Object,
 | 
				
			||||||
 | 
					    required: true
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  sourcePosition: {
 | 
				
			||||||
 | 
					    type: String
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  targetPosition: {
 | 
				
			||||||
 | 
					    type: String
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const sourceConnections = useHandleConnections({
 | 
				
			||||||
 | 
					  type: "target"
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const targetConnections = useHandleConnections({
 | 
				
			||||||
 | 
					  type: "source"
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const isSender = toRef(() => sourceConnections.value.length <= 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const isReceiver = toRef(() => targetConnections.value.length <= 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const bgColor = toRef(() => {
 | 
				
			||||||
 | 
					  if (isSender.value) {
 | 
				
			||||||
 | 
					    return "#2563eb";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (props.data.hasError) {
 | 
				
			||||||
 | 
					    return "#f87171";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (props.data.isFinished) {
 | 
				
			||||||
 | 
					    return "#42B983";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (props.data.isCancelled) {
 | 
				
			||||||
 | 
					    return "#fbbf24";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return "#4b5563";
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const processLabel = toRef(() => {
 | 
				
			||||||
 | 
					  if (props.data.hasError) {
 | 
				
			||||||
 | 
					    return "❌";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (props.data.isSkipped) {
 | 
				
			||||||
 | 
					    return "🚧";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (props.data.isCancelled) {
 | 
				
			||||||
 | 
					    return "🚫";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (isSender.value) {
 | 
				
			||||||
 | 
					    return "📦";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (props.data.isFinished) {
 | 
				
			||||||
 | 
					    return "😎";
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return "🏠";
 | 
				
			||||||
 | 
					});
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <div
 | 
				
			||||||
 | 
					    class="process-node"
 | 
				
			||||||
 | 
					    :style="{
 | 
				
			||||||
 | 
					      backgroundColor: bgColor,
 | 
				
			||||||
 | 
					      boxShadow: data.isRunning ? '0 0 10px rgba(0, 0, 0, 0.5)' : ''
 | 
				
			||||||
 | 
					    }"
 | 
				
			||||||
 | 
					  >
 | 
				
			||||||
 | 
					    <Handle v-if="!isSender" type="target" :position="targetPosition as any">
 | 
				
			||||||
 | 
					      <span
 | 
				
			||||||
 | 
					        v-if="
 | 
				
			||||||
 | 
					          !data.isRunning &&
 | 
				
			||||||
 | 
					          !data.isFinished &&
 | 
				
			||||||
 | 
					          !data.isCancelled &&
 | 
				
			||||||
 | 
					          !data.isSkipped &&
 | 
				
			||||||
 | 
					          !data.hasError
 | 
				
			||||||
 | 
					        "
 | 
				
			||||||
 | 
					        >📥
 | 
				
			||||||
 | 
					      </span>
 | 
				
			||||||
 | 
					    </Handle>
 | 
				
			||||||
 | 
					    <Handle
 | 
				
			||||||
 | 
					      v-if="!isReceiver"
 | 
				
			||||||
 | 
					      type="source"
 | 
				
			||||||
 | 
					      :position="sourcePosition as any"
 | 
				
			||||||
 | 
					    />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <div v-if="!isSender && data.isRunning" class="spinner" />
 | 
				
			||||||
 | 
					    <span v-else>
 | 
				
			||||||
 | 
					      {{ processLabel }}
 | 
				
			||||||
 | 
					    </span>
 | 
				
			||||||
 | 
					  </div>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style scoped>
 | 
				
			||||||
 | 
					@keyframes spin {
 | 
				
			||||||
 | 
					  0% {
 | 
				
			||||||
 | 
					    transform: rotate(0deg);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  100% {
 | 
				
			||||||
 | 
					    transform: rotate(360deg);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.process-node {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					  width: 24px;
 | 
				
			||||||
 | 
					  height: 24px;
 | 
				
			||||||
 | 
					  padding: 10px;
 | 
				
			||||||
 | 
					  border-radius: 99px;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.process-node .vue-flow__handle {
 | 
				
			||||||
 | 
					  width: unset;
 | 
				
			||||||
 | 
					  height: unset;
 | 
				
			||||||
 | 
					  font-size: 12px;
 | 
				
			||||||
 | 
					  background: transparent;
 | 
				
			||||||
 | 
					  border: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.spinner {
 | 
				
			||||||
 | 
					  width: 20px;
 | 
				
			||||||
 | 
					  height: 20px;
 | 
				
			||||||
 | 
					  border: 1px solid #f3f3f3;
 | 
				
			||||||
 | 
					  border-top: 1px solid #2563eb;
 | 
				
			||||||
 | 
					  border-radius: 50%;
 | 
				
			||||||
 | 
					  animation: spin 1s linear infinite;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
							
								
								
									
										52
									
								
								src/views/vue-flow/layouting/useLayout.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								src/views/vue-flow/layouting/useLayout.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,52 @@
 | 
				
			|||||||
 | 
					import dagre from "dagre";
 | 
				
			||||||
 | 
					import { ref } from "vue";
 | 
				
			||||||
 | 
					import { Position, useVueFlow } from "@vue-flow/core";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function useLayout() {
 | 
				
			||||||
 | 
					  const { findNode } = useVueFlow();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const graph = ref(new dagre.graphlib.Graph());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const previousDirection = ref("LR");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function layout(nodes, edges, direction) {
 | 
				
			||||||
 | 
					    const dagreGraph = new dagre.graphlib.Graph();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    graph.value = dagreGraph;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dagreGraph.setDefaultEdgeLabel(() => ({}));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const isHorizontal = direction === "LR";
 | 
				
			||||||
 | 
					    dagreGraph.setGraph({ rankdir: direction });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    previousDirection.value = direction;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (const node of nodes) {
 | 
				
			||||||
 | 
					      const graphNode = findNode(node.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      dagreGraph.setNode(node.id, {
 | 
				
			||||||
 | 
					        width: graphNode.dimensions.width || 150,
 | 
				
			||||||
 | 
					        height: graphNode.dimensions.height || 50
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (const edge of edges) {
 | 
				
			||||||
 | 
					      dagreGraph.setEdge(edge.source, edge.target);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dagre.layout(dagreGraph);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return nodes.map(node => {
 | 
				
			||||||
 | 
					      const nodeWithPosition = dagreGraph.node(node.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return {
 | 
				
			||||||
 | 
					        ...node,
 | 
				
			||||||
 | 
					        targetPosition: isHorizontal ? Position.Left : Position.Top,
 | 
				
			||||||
 | 
					        sourcePosition: isHorizontal ? Position.Right : Position.Bottom,
 | 
				
			||||||
 | 
					        position: { x: nodeWithPosition.x, y: nodeWithPosition.y }
 | 
				
			||||||
 | 
					      };
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return { graph, layout, previousDirection };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										181
									
								
								src/views/vue-flow/layouting/useRunProcess.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										181
									
								
								src/views/vue-flow/layouting/useRunProcess.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,181 @@
 | 
				
			|||||||
 | 
					import { ref, toRef, toValue } from "vue";
 | 
				
			||||||
 | 
					import { useVueFlow } from "@vue-flow/core";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function useRunProcess({ graph: dagreGraph, cancelOnError = true }) {
 | 
				
			||||||
 | 
					  const { updateNodeData, getConnectedEdges } = useVueFlow();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const graph = toRef(() => toValue(dagreGraph));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const isRunning = ref(false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const executedNodes = new Set();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const runningTasks = new Map();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  const upcomingTasks = new Set();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async function runNode(node, isStart = false) {
 | 
				
			||||||
 | 
					    if (executedNodes.has(node.id)) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    upcomingTasks.add(node.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const incomers = getConnectedEdges(node.id).filter(
 | 
				
			||||||
 | 
					      connection => connection.target === node.id
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await Promise.all(
 | 
				
			||||||
 | 
					      incomers.map(incomer => until(() => !incomer.data.isAnimating))
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    upcomingTasks.clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (!isRunning.value) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    executedNodes.add(node.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    updateNodeData(node.id, {
 | 
				
			||||||
 | 
					      isRunning: true,
 | 
				
			||||||
 | 
					      isFinished: false,
 | 
				
			||||||
 | 
					      hasError: false,
 | 
				
			||||||
 | 
					      isCancelled: false
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const delay = Math.floor(Math.random() * 2000) + 1000;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return new Promise(resolve => {
 | 
				
			||||||
 | 
					      const timeout = setTimeout(
 | 
				
			||||||
 | 
					        async () => {
 | 
				
			||||||
 | 
					          const children = graph.value.successors(node.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          const willThrowError = Math.random() < 0.15;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          if (!isStart && willThrowError) {
 | 
				
			||||||
 | 
					            updateNodeData(node.id, { isRunning: false, hasError: true });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if (toValue(cancelOnError)) {
 | 
				
			||||||
 | 
					              await skipDescendants(node.id);
 | 
				
			||||||
 | 
					              runningTasks.delete(node.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              // @ts-expect-error
 | 
				
			||||||
 | 
					              resolve();
 | 
				
			||||||
 | 
					              return;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          updateNodeData(node.id, { isRunning: false, isFinished: true });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          runningTasks.delete(node.id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          if (children.length > 0) {
 | 
				
			||||||
 | 
					            await Promise.all(children.map(id => runNode({ id })));
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // @ts-expect-error
 | 
				
			||||||
 | 
					          resolve();
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					        isStart ? 0 : delay
 | 
				
			||||||
 | 
					      );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      runningTasks.set(node.id, timeout);
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async function run(nodes) {
 | 
				
			||||||
 | 
					    if (isRunning.value) {
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    reset(nodes);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    isRunning.value = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const startingNodes = nodes.filter(
 | 
				
			||||||
 | 
					      node => graph.value.predecessors(node.id)?.length === 0
 | 
				
			||||||
 | 
					    );
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    await Promise.all(startingNodes.map(node => runNode(node, true)));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    clear();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function reset(nodes) {
 | 
				
			||||||
 | 
					    clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (const node of nodes) {
 | 
				
			||||||
 | 
					      updateNodeData(node.id, {
 | 
				
			||||||
 | 
					        isRunning: false,
 | 
				
			||||||
 | 
					        isFinished: false,
 | 
				
			||||||
 | 
					        hasError: false,
 | 
				
			||||||
 | 
					        isSkipped: false,
 | 
				
			||||||
 | 
					        isCancelled: false
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async function skipDescendants(nodeId) {
 | 
				
			||||||
 | 
					    const children = graph.value.successors(nodeId);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (const child of children) {
 | 
				
			||||||
 | 
					      updateNodeData(child, { isRunning: false, isSkipped: true });
 | 
				
			||||||
 | 
					      await skipDescendants(child);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  async function stop() {
 | 
				
			||||||
 | 
					    isRunning.value = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (const nodeId of upcomingTasks) {
 | 
				
			||||||
 | 
					      clearTimeout(runningTasks.get(nodeId));
 | 
				
			||||||
 | 
					      runningTasks.delete(nodeId);
 | 
				
			||||||
 | 
					      // @ts-expect-error
 | 
				
			||||||
 | 
					      updateNodeData(nodeId, {
 | 
				
			||||||
 | 
					        isRunning: false,
 | 
				
			||||||
 | 
					        isFinished: false,
 | 
				
			||||||
 | 
					        hasError: false,
 | 
				
			||||||
 | 
					        isSkipped: false,
 | 
				
			||||||
 | 
					        isCancelled: true
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      await skipDescendants(nodeId);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (const [nodeId, task] of runningTasks) {
 | 
				
			||||||
 | 
					      clearTimeout(task);
 | 
				
			||||||
 | 
					      runningTasks.delete(nodeId);
 | 
				
			||||||
 | 
					      updateNodeData(nodeId, {
 | 
				
			||||||
 | 
					        isRunning: false,
 | 
				
			||||||
 | 
					        isFinished: false,
 | 
				
			||||||
 | 
					        hasError: false,
 | 
				
			||||||
 | 
					        isSkipped: false,
 | 
				
			||||||
 | 
					        isCancelled: true
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      await skipDescendants(nodeId);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    executedNodes.clear();
 | 
				
			||||||
 | 
					    upcomingTasks.clear();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  function clear() {
 | 
				
			||||||
 | 
					    isRunning.value = false;
 | 
				
			||||||
 | 
					    executedNodes.clear();
 | 
				
			||||||
 | 
					    runningTasks.clear();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return { run, stop, reset, isRunning };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					async function until(condition) {
 | 
				
			||||||
 | 
					  return new Promise(resolve => {
 | 
				
			||||||
 | 
					    const interval = setInterval(() => {
 | 
				
			||||||
 | 
					      if (condition()) {
 | 
				
			||||||
 | 
					        clearInterval(interval);
 | 
				
			||||||
 | 
					        // @ts-expect-error
 | 
				
			||||||
 | 
					        resolve();
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }, 100);
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										50
									
								
								src/views/vue-flow/layouting/useShuffle.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/views/vue-flow/layouting/useShuffle.ts
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,50 @@
 | 
				
			|||||||
 | 
					function shuffleArray(array) {
 | 
				
			||||||
 | 
					  for (let i = array.length - 1; i > 0; i--) {
 | 
				
			||||||
 | 
					    const j = Math.floor(Math.random() * (i + 1));
 | 
				
			||||||
 | 
					    [array[i], array[j]] = [array[j], array[i]];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					function generatePossibleEdges(nodes) {
 | 
				
			||||||
 | 
					  const possibleEdges = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (const sourceNode of nodes) {
 | 
				
			||||||
 | 
					    for (const targetNode of nodes) {
 | 
				
			||||||
 | 
					      if (sourceNode.id !== targetNode.id) {
 | 
				
			||||||
 | 
					        const edgeId = `e${sourceNode.id}-${targetNode.id}`;
 | 
				
			||||||
 | 
					        possibleEdges.push({
 | 
				
			||||||
 | 
					          id: edgeId,
 | 
				
			||||||
 | 
					          source: sourceNode.id,
 | 
				
			||||||
 | 
					          target: targetNode.id,
 | 
				
			||||||
 | 
					          type: "animation",
 | 
				
			||||||
 | 
					          animated: true
 | 
				
			||||||
 | 
					        });
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return possibleEdges;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export function useShuffle() {
 | 
				
			||||||
 | 
					  return nodes => {
 | 
				
			||||||
 | 
					    const possibleEdges = generatePossibleEdges(nodes);
 | 
				
			||||||
 | 
					    shuffleArray(possibleEdges);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const usedNodes = new Set();
 | 
				
			||||||
 | 
					    const newEdges = [];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (const edge of possibleEdges) {
 | 
				
			||||||
 | 
					      if (
 | 
				
			||||||
 | 
					        !usedNodes.has(edge.target) &&
 | 
				
			||||||
 | 
					        (usedNodes.size === 0 || usedNodes.has(edge.source))
 | 
				
			||||||
 | 
					      ) {
 | 
				
			||||||
 | 
					        newEdges.push(edge);
 | 
				
			||||||
 | 
					        usedNodes.add(edge.source);
 | 
				
			||||||
 | 
					        usedNodes.add(edge.target);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return newEdges;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user