├── .browserslistrc ├── src ├── eventBus.js ├── assets │ ├── logo.png │ └── svg │ │ ├── arrow.svg │ │ └── defaultImg.svg ├── views │ ├── demo3 │ │ ├── components │ │ │ ├── config.js │ │ │ ├── nodeTheme │ │ │ │ └── common.vue │ │ │ ├── menuBar.vue │ │ │ ├── dialog │ │ │ │ ├── mysql.vue │ │ │ │ └── condition.vue │ │ │ └── drawer.vue │ │ ├── graphMixin.js │ │ └── index.vue │ ├── test.vue │ ├── dragDemo │ │ ├── components │ │ │ ├── config.js │ │ │ ├── menuBar.vue │ │ │ ├── dialog │ │ │ │ ├── mysql.vue │ │ │ │ └── condition.vue │ │ │ ├── data.json │ │ │ ├── nodeTheme │ │ │ │ ├── onlyIn.vue │ │ │ │ ├── onlyOut.vue │ │ │ │ ├── condition.vue │ │ │ │ └── database.vue │ │ │ └── drawer.vue │ │ └── index.vue │ └── dargeDemo │ │ ├── components │ │ ├── formVue.vue │ │ ├── node │ │ │ ├── exnode.vue │ │ │ └── common.vue │ │ └── drawer.vue │ │ ├── config.js │ │ ├── graphMixin.js │ │ └── index.vue ├── store │ └── index.js ├── style │ └── dargeLess.less ├── utils │ └── index.js ├── App.vue ├── main.js ├── router │ └── index.js └── data.json ├── babel.config.js ├── public ├── favicon.ico └── index.html ├── vue.config.js ├── .gitignore ├── jsconfig.json ├── .eslintrc.js ├── package.json └── README.md /.browserslistrc: -------------------------------------------------------------------------------- 1 | > 1% 2 | last 2 versions 3 | not dead 4 | -------------------------------------------------------------------------------- /src/eventBus.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | 3 | export const EventBus = new Vue(); 4 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ["@vue/cli-plugin-babel/preset"], 3 | }; 4 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyButifullyLife/vue-x6-flow/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MyButifullyLife/vue-x6-flow/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require("@vue/cli-service"); 2 | module.exports = defineConfig({ 3 | transpileDependencies: true, 4 | runtimeCompiler: true, 5 | }); 6 | -------------------------------------------------------------------------------- /src/views/demo3/components/config.js: -------------------------------------------------------------------------------- 1 | const config = [ 2 | { 3 | type: "common", 4 | height: 100, 5 | width: 200, 6 | data: { 7 | name: "common节点", 8 | }, 9 | }, 10 | ]; 11 | 12 | export default config; 13 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import Vuex from "vuex"; 3 | 4 | Vue.use(Vuex); 5 | 6 | export default new Vuex.Store({ 7 | state: {}, 8 | getters: {}, 9 | mutations: {}, 10 | actions: {}, 11 | modules: {}, 12 | }); 13 | -------------------------------------------------------------------------------- /src/style/dargeLess.less: -------------------------------------------------------------------------------- 1 | 2 | 3 | .success{ 4 | //background-color: green ; 5 | border-color: green !important; 6 | } 7 | 8 | 9 | 10 | .error{ 11 | //background-color: green ; 12 | border-color: red !important; 13 | } 14 | 15 | .normal{ 16 | //background-color: green ; 17 | border-color: gray !important; 18 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | 6 | # local env files 7 | .env.local 8 | .env.*.local 9 | 10 | # Log files 11 | npm-debug.log* 12 | yarn-debug.log* 13 | yarn-error.log* 14 | pnpm-debug.log* 15 | 16 | # Editor directories and files 17 | .idea 18 | .vscode 19 | *.suo 20 | *.ntvs* 21 | *.njsproj 22 | *.sln 23 | *.sw? 24 | -------------------------------------------------------------------------------- /jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "esnext", 5 | "baseUrl": "./", 6 | "moduleResolution": "node", 7 | "paths": { 8 | "@/*": [ 9 | "src/*" 10 | ] 11 | }, 12 | "lib": [ 13 | "esnext", 14 | "dom", 15 | "dom.iterable", 16 | "scripthost" 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/utils/index.js: -------------------------------------------------------------------------------- 1 | // 根据自己的需求生成唯一ID 2 | export function generateUniqueId() { 3 | // 例如组合时间戳、随机数和计数器 4 | const timestamp = Date.now().toString(36); 5 | const randomNumber = Math.random().toString(36).substr(2, 5); 6 | const counter = generateUniqueId.counter 7 | ? ++generateUniqueId.counter 8 | : (generateUniqueId.counter = 1); 9 | return `${timestamp}-${randomNumber}-${counter}`; 10 | } 11 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 20 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | node: true, 5 | }, 6 | extends: [ 7 | "plugin:vue/essential", 8 | "eslint:recommended", 9 | "plugin:prettier/recommended", 10 | ], 11 | parserOptions: { 12 | parser: "@babel/eslint-parser", 13 | }, 14 | rules: { 15 | "no-console": process.env.NODE_ENV === "production" ? "warn" : "off", 16 | "no-debugger": process.env.NODE_ENV === "production" ? "warn" : "off", 17 | }, 18 | }; 19 | -------------------------------------------------------------------------------- /src/assets/svg/arrow.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import App from "./App.vue"; 3 | import router from "./router"; 4 | import store from "./store"; 5 | import ElementUI from "element-ui"; 6 | import "element-ui/lib/theme-chalk/index.css"; 7 | 8 | import { EventBus } from "./eventBus"; 9 | Vue.prototype.$EventBus = EventBus; 10 | 11 | Vue.use(ElementUI); 12 | 13 | Vue.config.productionTip = false; 14 | 15 | new Vue({ 16 | router, 17 | store, 18 | render: (h) => h(App), 19 | }).$mount("#app"); 20 | -------------------------------------------------------------------------------- /src/views/test.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 22 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/views/dragDemo/components/config.js: -------------------------------------------------------------------------------- 1 | const config = [ 2 | { 3 | type: "output", 4 | label: "只允许输出", 5 | name: "输出", 6 | data: { 7 | name: "输出", 8 | }, 9 | }, 10 | 11 | { 12 | type: "database", 13 | label: "数据库", 14 | name: "mySql", 15 | data: { 16 | name: "mysql", 17 | }, 18 | }, 19 | { 20 | type: "onlyIn", 21 | label: "只允许输入", 22 | name: "只允许输入", 23 | data: { 24 | name: "只允许输入", 25 | }, 26 | }, 27 | { 28 | type: "condition", 29 | label: "条件", 30 | name: "条件", 31 | data: { 32 | name: "condition", 33 | t: "", 34 | f: "", 35 | }, 36 | }, 37 | ]; 38 | 39 | export default config; 40 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from "vue"; 2 | import VueRouter from "vue-router"; 3 | 4 | Vue.use(VueRouter); 5 | 6 | const routes = [ 7 | { 8 | path: "/", 9 | name: "Test", 10 | component: () => import(/* webpackChunkName: "about" */ "@/views/test"), 11 | }, 12 | { 13 | path: "/dragDemo", 14 | name: "dragDemo", 15 | component: () => import(/* webpackChunkName: "about" */ "@/views/dragDemo"), 16 | }, 17 | { 18 | path: "/dargeDemo", 19 | name: "dargeDemo", 20 | component: () => import("@/views/dargeDemo"), 21 | }, 22 | { 23 | path: "/demo3", 24 | name: "demo3", 25 | component: () => import("@/views/demo3"), 26 | }, 27 | ]; 28 | 29 | const router = new VueRouter({ 30 | mode: "hash", 31 | base: process.env.BASE_URL, 32 | routes, 33 | }); 34 | 35 | export default router; 36 | -------------------------------------------------------------------------------- /src/assets/svg/defaultImg.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/views/demo3/components/nodeTheme/common.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 31 | 41 | -------------------------------------------------------------------------------- /src/views/dargeDemo/components/formVue.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 47 | 57 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue-x6-flow", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "dev": "vue-cli-service serve", 8 | "build": "vue-cli-service build", 9 | "lint": "vue-cli-service lint" 10 | }, 11 | "dependencies": { 12 | "@antv/layout": "^0.3.23", 13 | "@antv/x6": "^1.34.14", 14 | "@antv/x6-vue-shape": "^1.5.4", 15 | "@vue/composition-api": "^1.7.1", 16 | "core-js": "^3.8.3", 17 | "element-ui": "^2.15.6", 18 | "vue": "^2.6.14", 19 | "vue-router": "^3.5.1", 20 | "vuex": "^3.6.2" 21 | }, 22 | "devDependencies": { 23 | "@babel/core": "^7.12.16", 24 | "@babel/eslint-parser": "^7.12.16", 25 | "@vue/cli-plugin-babel": "~5.0.8", 26 | "@vue/cli-plugin-eslint": "~5.0.8", 27 | "@vue/cli-plugin-router": "~5.0.8", 28 | "@vue/cli-plugin-vuex": "~5.0.8", 29 | "@vue/cli-service": "~5.0.8", 30 | "eslint": "^7.32.0", 31 | "eslint-config-prettier": "^8.3.0", 32 | "eslint-plugin-prettier": "^4.0.0", 33 | "eslint-plugin-vue": "^8.0.3", 34 | "less": "^4.0.0", 35 | "less-loader": "^8.0.0", 36 | "prettier": "^2.4.1", 37 | "vue-template-compiler": "^2.6.14" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/views/demo3/components/menuBar.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 48 | 76 | -------------------------------------------------------------------------------- /src/views/dragDemo/components/menuBar.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 48 | 76 | -------------------------------------------------------------------------------- /src/views/demo3/components/dialog/mysql.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 48 | 62 | -------------------------------------------------------------------------------- /src/views/dragDemo/components/dialog/mysql.vue: -------------------------------------------------------------------------------- 1 | 17 | 18 | 48 | 62 | -------------------------------------------------------------------------------- /src/views/demo3/components/dialog/condition.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 64 | 77 | -------------------------------------------------------------------------------- /src/views/dragDemo/components/dialog/condition.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 64 | 77 | -------------------------------------------------------------------------------- /src/views/dargeDemo/components/node/exnode.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 38 | 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | # vue3 和 vue2 两个项目的demo是不同的,可以都下载下来运行查看是否有符合自身业务的demo 4 | 5 | # vue-x6-flow 6 | 7 | **基于antv-x6 vue版本** 8 | vue2.x+ element +antv-x6 9 | 适用于流程图-dag图 10 | 11 | 12 | # vue3版本地址 13 | https://github.com/MyButifullyLife/vue-x6-flow-3.0 14 | 15 | 有以下功能内容: 16 | 1、拖拽布局 17 | 2、右键菜单 18 | 3、保存 19 | 20 | 4、缩放 21 | 5、多选 22 | 6、快捷键 cv cx cz(重做) 23 | 7、连线判断 24 | 8、配置属性 25 | 9、动态节点流向动画 26 | 其余功能可在antv-x6文档自行添加 27 | 28 | ## 文档地址 29 | https://x6.antv.vision/zh/docs/api/graph/graph 30 | 31 | ## 本项目效果图 32 | ![image](https://user-images.githubusercontent.com/16436933/149902393-4a9fd58f-eadd-4a5f-af71-81076d0364d0.png) 33 | 34 | ## 流向动画 35 | ![GIF 2022-1-18 16-51-01](https://user-images.githubusercontent.com/16436933/149902899-b630b119-c39f-45e9-b576-da8d571386be.gif) 36 | 37 | ## 树状图 现成源码 38 | ![1679362448522](https://user-images.githubusercontent.com/16436933/226499607-5ad1b21d-79c5-45d1-9d31-b59841889eaa.jpg) 39 | 40 | ![企业微信截图_d2f74ba5-4202-43eb-90e8-9f95bfc3ff2f](https://user-images.githubusercontent.com/16436933/226512037-53dcc958-6112-402d-92b8-cf530b2f531d.png) 41 | 42 | 43 | ## x6 对于流程图 dag图 思维导图均可实现 44 | ![image](https://user-images.githubusercontent.com/16436933/153532050-5bd5372b-e3a0-43dd-8407-9f0a05b7f59a.png) 45 | 46 | 47 | ## 如果文章对你有帮助,那么5块、10块也是爱 48 | ![image](https://user-images.githubusercontent.com/16436933/153533288-32647686-344f-4933-a692-5f4451fe24f6.png) 49 | 50 | 51 | ## 有疑问可加QQ 52 | 冠希:微信转账30可以让你少掉、少白几根头发永远跟我一样帅 53 | QQ:251275985 wx:gjh251275985 54 | 55 | 56 | ## antv在vue使用所需要的依赖 57 | ··· 58 | 59 | "@antv/x6": "^1.29.5" 60 | 61 | "@antv/x6-vue-shape": "^1.3.0" 62 | 63 | "@vue/composition-api": "^1.4.3" 64 | 65 | 66 | 67 | vue.config.js 配置 68 | runtimeCompiler: true 69 | 70 | 71 | ··· 72 | 73 | ## Project setup 74 | ``` 75 | 因为项目是由最新vue-cli创建,运行环境必须要有 yarn 76 | 本地环境没有yarn 可以通过 npm install yarn 下载 77 | 78 | yarn install 79 | ``` 80 | 81 | ### Compiles and hot-reloads for development 82 | ``` 83 | yarn serve 84 | ``` 85 | 86 | ### Compiles and minifies for production 87 | ``` 88 | yarn build 89 | ``` 90 | 91 | ### Lints and fixes files 92 | ``` 93 | yarn lint 94 | 95 | ``` 96 | 97 | ### Customize configuration 98 | See [Configuration Reference](https://cli.vuejs.org/config/). 99 | -------------------------------------------------------------------------------- /src/data.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "1", 4 | "shape": "dag-node", 5 | "x": 290, 6 | "y": 110, 7 | "data": { 8 | "label": "读数据", 9 | "status": "success" 10 | }, 11 | "ports": [ 12 | { 13 | "id": "1-1", 14 | "group": "bottom" 15 | } 16 | ] 17 | }, 18 | { 19 | "id": "2", 20 | "shape": "dag-node", 21 | "x": 290, 22 | "y": 225, 23 | "data": { 24 | "label": "逻辑回归", 25 | "status": "success" 26 | }, 27 | "ports": [ 28 | { 29 | "id": "2-1", 30 | "group": "top" 31 | }, 32 | { 33 | "id": "2-2", 34 | "group": "bottom" 35 | }, 36 | { 37 | "id": "2-3", 38 | "group": "bottom" 39 | } 40 | ] 41 | }, 42 | { 43 | "id": "3", 44 | "shape": "dag-node", 45 | "x": 170, 46 | "y": 350, 47 | "data": { 48 | "label": "模型预测", 49 | "status": "success" 50 | }, 51 | "ports": [ 52 | { 53 | "id": "3-1", 54 | "group": "top" 55 | }, 56 | { 57 | "id": "3-2", 58 | "group": "bottom" 59 | } 60 | ] 61 | }, 62 | { 63 | "id": "4", 64 | "shape": "dag-node", 65 | "x": 450, 66 | "y": 350, 67 | "data": { 68 | "label": "读取参数", 69 | "status": "success" 70 | }, 71 | "ports": [ 72 | { 73 | "id": "4-1", 74 | "group": "top" 75 | }, 76 | { 77 | "id": "4-2", 78 | "group": "bottom" 79 | } 80 | ] 81 | }, 82 | { 83 | "id": "5", 84 | "shape": "dag-edge", 85 | "source": { 86 | "cell": "1", 87 | "port": "1-1" 88 | }, 89 | "target": { 90 | "cell": "2", 91 | "port": "2-1" 92 | }, 93 | "zIndex": 0 94 | }, 95 | { 96 | "id": "6", 97 | "shape": "dag-edge", 98 | "source": { 99 | "cell": "2", 100 | "port": "2-2" 101 | }, 102 | "target": { 103 | "cell": "3", 104 | "port": "3-1" 105 | }, 106 | "zIndex": 0 107 | }, 108 | { 109 | "id": "7", 110 | "shape": "dag-edge", 111 | "source": { 112 | "cell": "2", 113 | "port": "2-3" 114 | }, 115 | "target": { 116 | "cell": "4", 117 | "port": "4-1" 118 | }, 119 | "zIndex": 0 120 | } 121 | ] 122 | -------------------------------------------------------------------------------- /src/views/dragDemo/components/data.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "id": "1", 4 | "shape": "dag-node", 5 | "x": 290, 6 | "y": 110, 7 | "data": { 8 | "label": "读数据", 9 | "status": "success" 10 | }, 11 | "ports": [ 12 | { 13 | "id": "1-1", 14 | "group": "bottom" 15 | } 16 | ] 17 | }, 18 | { 19 | "id": "2", 20 | "shape": "dag-node", 21 | "x": 290, 22 | "y": 225, 23 | "data": { 24 | "label": "逻辑回归", 25 | "status": "success" 26 | }, 27 | "ports": [ 28 | { 29 | "id": "2-1", 30 | "group": "top" 31 | }, 32 | { 33 | "id": "2-2", 34 | "group": "bottom" 35 | }, 36 | { 37 | "id": "2-3", 38 | "group": "bottom" 39 | } 40 | ] 41 | }, 42 | { 43 | "id": "3", 44 | "shape": "dag-node", 45 | "x": 170, 46 | "y": 350, 47 | "data": { 48 | "label": "模型预测", 49 | "status": "success" 50 | }, 51 | "ports": [ 52 | { 53 | "id": "3-1", 54 | "group": "top" 55 | }, 56 | { 57 | "id": "3-2", 58 | "group": "bottom" 59 | } 60 | ] 61 | }, 62 | { 63 | "id": "4", 64 | "shape": "dag-node", 65 | "x": 450, 66 | "y": 350, 67 | "data": { 68 | "label": "读取参数", 69 | "status": "success" 70 | }, 71 | "ports": [ 72 | { 73 | "id": "4-1", 74 | "group": "top" 75 | }, 76 | { 77 | "id": "4-2", 78 | "group": "bottom" 79 | } 80 | ] 81 | }, 82 | { 83 | "id": "5", 84 | "shape": "dag-edge", 85 | "source": { 86 | "cell": "1", 87 | "port": "1-1" 88 | }, 89 | "target": { 90 | "cell": "2", 91 | "port": "2-1" 92 | }, 93 | "zIndex": 0 94 | }, 95 | { 96 | "id": "6", 97 | "shape": "dag-edge", 98 | "source": { 99 | "cell": "2", 100 | "port": "2-2" 101 | }, 102 | "target": { 103 | "cell": "3", 104 | "port": "3-1" 105 | }, 106 | "zIndex": 0 107 | }, 108 | { 109 | "id": "7", 110 | "shape": "dag-edge", 111 | "source": { 112 | "cell": "2", 113 | "port": "2-3" 114 | }, 115 | "target": { 116 | "cell": "4", 117 | "port": "4-1" 118 | }, 119 | "zIndex": 0 120 | } 121 | ] 122 | -------------------------------------------------------------------------------- /src/views/dargeDemo/config.js: -------------------------------------------------------------------------------- 1 | export const shapeObj = { 2 | "dag-node": { 3 | id: "1", 4 | shape: "dag-node", 5 | x: 290, 6 | y: 110, 7 | data: { 8 | id: "1", 9 | label: "读数据", 10 | status: "success", 11 | }, 12 | }, 13 | }; 14 | 15 | export const statusColor = { 16 | success: "#52c41a", 17 | error: "#c42027", 18 | normal: "#b1b4b1", 19 | }; 20 | 21 | // 结构 22 | export const dataJson = [ 23 | { 24 | type: "common", 25 | data: { 26 | status: "success", 27 | label: "流程开始1", 28 | }, 29 | children: [ 30 | { 31 | type: "common", 32 | data: { 33 | status: "normal", 34 | label: "流程开始2", 35 | form: { 36 | text1: "", 37 | text2: "", 38 | text3: "", 39 | text4: "", 40 | }, 41 | }, 42 | children: [ 43 | { 44 | type: "exnode", 45 | data: { 46 | status: "error", 47 | label: "流程开始3", 48 | }, 49 | }, 50 | ], 51 | }, 52 | { 53 | type: "common", 54 | data: { 55 | status: "normal", 56 | label: "流程开始4", 57 | }, 58 | }, 59 | ], 60 | }, 61 | ]; 62 | 63 | // 自定义json 64 | export const customJson = [ 65 | // 自定义1 66 | { 67 | type: "common", 68 | data: { 69 | status: "success", 70 | label: "自定义1", 71 | }, 72 | children: [ 73 | { 74 | type: "exnode", 75 | data: { 76 | status: "normal", 77 | label: "1-1", 78 | }, 79 | }, 80 | { 81 | type: "exnode", 82 | data: { 83 | status: "normal", 84 | label: "1-2", 85 | }, 86 | }, 87 | ], 88 | }, 89 | // 自定义2 90 | { 91 | type: "common", 92 | data: { 93 | status: "success", 94 | label: "自定义2", 95 | }, 96 | children: [ 97 | { 98 | type: "common", 99 | data: { 100 | status: "normal", 101 | label: "2-1", 102 | form: { 103 | text1: "", 104 | text2: "", 105 | text3: "", 106 | text4: "", 107 | }, 108 | }, 109 | }, 110 | { 111 | type: "common", 112 | data: { 113 | status: "normal", 114 | label: "2-2", 115 | form: { 116 | text1: "", 117 | text2: "", 118 | text3: "", 119 | text4: "", 120 | }, 121 | }, 122 | }, 123 | ], 124 | }, 125 | ]; 126 | 127 | export function generateUniqueId() { 128 | // 根据自己的需求生成唯一ID 129 | // 例如组合时间戳、随机数和计数器 130 | const timestamp = Date.now().toString(36); 131 | const randomNumber = Math.random().toString(36).substr(2, 5); 132 | const counter = generateUniqueId.counter 133 | ? ++generateUniqueId.counter 134 | : (generateUniqueId.counter = 1); 135 | return `${timestamp}-${randomNumber}-${counter}`; 136 | } 137 | -------------------------------------------------------------------------------- /src/views/dragDemo/components/nodeTheme/onlyIn.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 43 | 131 | -------------------------------------------------------------------------------- /src/views/dragDemo/components/nodeTheme/onlyOut.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 43 | 131 | -------------------------------------------------------------------------------- /src/views/dragDemo/components/nodeTheme/condition.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 44 | 132 | -------------------------------------------------------------------------------- /src/views/dragDemo/components/nodeTheme/database.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 44 | 132 | -------------------------------------------------------------------------------- /src/views/dargeDemo/components/node/common.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 81 | 128 | -------------------------------------------------------------------------------- /src/views/demo3/components/drawer.vue: -------------------------------------------------------------------------------- 1 | 42 | 43 | 143 | 205 | -------------------------------------------------------------------------------- /src/views/dargeDemo/components/drawer.vue: -------------------------------------------------------------------------------- 1 | 92 | 93 | 189 | 190 | 197 | -------------------------------------------------------------------------------- /src/views/dargeDemo/graphMixin.js: -------------------------------------------------------------------------------- 1 | import { Graph } from "@antv/x6"; 2 | import common from "@/views/dargeDemo/components/node/common"; 3 | import exnode from "@/views/dargeDemo/components/node/exnode"; 4 | // import { DagreLayout } from "@antv/layout"; 5 | export default { 6 | watch: {}, 7 | beforeMount() {}, 8 | beforeDestroy() {}, 9 | mounted() { 10 | this.initGraph(); 11 | this.keyBindFn(); 12 | 13 | // 禁止拖动 14 | this.graph.enableKeyboard(); 15 | }, 16 | methods: { 17 | layoutFn() {}, 18 | keyBindFn() { 19 | // copy cut paste 20 | // this.graph.bindKey(["meta+c", "ctrl+c"], () => { 21 | // const cells = this.graph.getSelectedCells(); 22 | // if (cells.length) { 23 | // this.graph.copy(cells); 24 | // } 25 | // return false; 26 | // }); 27 | // this.graph.bindKey(["meta+x", "ctrl+x"], () => { 28 | // const cells = this.graph.getSelectedCells(); 29 | // if (cells.length) { 30 | // this.graph.cut(cells); 31 | // } 32 | // return false; 33 | // }); 34 | // this.graph.bindKey(["meta+v", "ctrl+v"], () => { 35 | // if (!this.graph.isClipboardEmpty()) { 36 | // const cells = this.graph.paste({ offset: 32 }); 37 | // this.graph.cleanSelection(); 38 | // this.graph.select(cells); 39 | // } 40 | // return false; 41 | // }); 42 | // 43 | // // undo redo 44 | // this.graph.bindKey(["meta+z", "ctrl+z"], () => { 45 | // if (this.graph.history.canUndo()) { 46 | // this.graph.history.undo(); 47 | // } 48 | // return false; 49 | // }); 50 | }, 51 | registerNode() { 52 | // 注册节点 53 | Graph.registerNode( 54 | "dag-common", 55 | { 56 | inherit: "vue-shape", 57 | component: { 58 | template: ``, 59 | components: { 60 | common, 61 | }, 62 | }, 63 | ports: { 64 | groups: { 65 | left: { 66 | position: "left", 67 | }, 68 | right: { 69 | position: "right", 70 | }, 71 | }, 72 | }, 73 | }, 74 | true 75 | ); 76 | Graph.registerNode( 77 | "dag-exnode", 78 | { 79 | inherit: "vue-shape", 80 | component: { 81 | template: ``, 82 | components: { 83 | exnode, 84 | }, 85 | }, 86 | ports: { 87 | groups: { 88 | left: { 89 | position: "left", 90 | }, 91 | right: { 92 | position: "right", 93 | }, 94 | }, 95 | }, 96 | }, 97 | true 98 | ); 99 | }, 100 | initGraph() { 101 | this.registerNode(); 102 | // 注册节点 103 | const graph = new Graph({ 104 | grid: { 105 | size: 10, 106 | visible: true, 107 | type: "dot", // 'dot' | 'fixedDot' | 'mesh' 108 | args: { 109 | color: "#0a2327", // 网格线/点颜色 110 | thickness: 1, // 网格线宽度/网格点大小 111 | }, 112 | }, 113 | background: { 114 | color: "#eef3fb", // 设置画布背景颜色 115 | }, 116 | interacting: { 117 | nodeMovable: false, // 禁止节点和边移动 118 | edgeMovable: false, 119 | }, 120 | container: document.getElementById("draw-cot"), 121 | panning: { 122 | enabled: true, 123 | eventTypes: ["leftMouseDown", "mouseWheel"], 124 | }, 125 | mousewheel: { 126 | enabled: true, 127 | modifiers: "ctrl", 128 | factor: 1.1, 129 | maxScale: 1.5, 130 | minScale: 0.5, 131 | }, 132 | highlighting: { 133 | magnetAdsorbed: { 134 | name: "stroke", 135 | args: { 136 | attrs: { 137 | fill: "#fff", 138 | stroke: "#31d0c6", 139 | strokeWidth: 4, 140 | }, 141 | }, 142 | }, 143 | }, 144 | connecting: { 145 | snap: true, 146 | allowBlank: false, 147 | allowLoop: false, 148 | highlight: true, 149 | 150 | connectionPoint: "anchor", 151 | anchor: "center", 152 | validateMagnet() { 153 | // return magnet.getAttribute('port-group') !== 'top' 154 | 155 | // 限制连线配置 156 | return true; 157 | }, 158 | createEdge() { 159 | return graph.createEdge({ 160 | attrs: { 161 | line: { 162 | strokeDasharray: "5 5", 163 | targetMarker: { 164 | name: "block", 165 | width: 12, 166 | height: 8, 167 | }, 168 | }, 169 | }, 170 | zIndex: -1, 171 | }); 172 | }, 173 | }, 174 | selecting: { 175 | enabled: true, 176 | multiple: true, 177 | rubberEdge: true, 178 | rubberNode: true, 179 | modifiers: "shift", 180 | rubberband: true, 181 | }, 182 | keyboard: true, 183 | clipboard: true, 184 | history: true, 185 | }); 186 | this.graph = graph; 187 | 188 | graph.on("edge:contextmenu", ({ e, x, y, edge, view }) => { 189 | console.log(x, y, view); 190 | this.showContextMenu = true; 191 | this.$nextTick(() => { 192 | this.$refs.menuBar.initFn(e.offsetX, e.offsetY, { 193 | type: "edge", 194 | item: edge, 195 | }); 196 | }); 197 | }); 198 | 199 | graph.on("node:contextmenu", ({ e, x, y, node, view }) => { 200 | console.log(e, x, y, view); 201 | this.showContextMenu = true; 202 | 203 | this.$nextTick(() => { 204 | // this.$refs.menuBar.setItem({ type: 'node', item: node }) 205 | const p = graph.localToPage(x, y); 206 | this.$refs.menuBar.initFn(p.x, p.y, { type: "node", item: node }); 207 | }); 208 | }); 209 | 210 | graph.on("edge:connected", ({ edge }) => { 211 | edge.attr({ 212 | line: { 213 | strokeDasharray: "", 214 | }, 215 | }); 216 | }); 217 | }, 218 | }, 219 | }; 220 | -------------------------------------------------------------------------------- /src/views/demo3/graphMixin.js: -------------------------------------------------------------------------------- 1 | import { Graph, Path } from "@antv/x6"; 2 | import "@antv/x6-vue-shape"; 3 | import common from "./components/nodeTheme/common.vue"; 4 | export default { 5 | watch: {}, 6 | beforeMount() {}, 7 | beforeDestroy() {}, 8 | methods: { 9 | layoutFn() {}, 10 | keyBindFn() { 11 | // copy cut paste 12 | // this.graph.bindKey(["meta+c", "ctrl+c"], () => { 13 | // const cells = this.graph.getSelectedCells(); 14 | // if (cells.length) { 15 | // this.graph.copy(cells); 16 | // } 17 | // return false; 18 | // }); 19 | // this.graph.bindKey(["meta+x", "ctrl+x"], () => { 20 | // const cells = this.graph.getSelectedCells(); 21 | // if (cells.length) { 22 | // this.graph.cut(cells); 23 | // } 24 | // return false; 25 | // }); 26 | // this.graph.bindKey(["meta+v", "ctrl+v"], () => { 27 | // if (!this.graph.isClipboardEmpty()) { 28 | // const cells = this.graph.paste({ offset: 32 }); 29 | // this.graph.cleanSelection(); 30 | // this.graph.select(cells); 31 | // } 32 | // return false; 33 | // }); 34 | // 35 | // // undo redo 36 | // this.graph.bindKey(["meta+z", "ctrl+z"], () => { 37 | // if (this.graph.history.canUndo()) { 38 | // this.graph.history.undo(); 39 | // } 40 | // return false; 41 | // }); 42 | }, 43 | registerNode() { 44 | Graph.registerPortLayout( 45 | "erPortPosition", 46 | (portsPositionArgs, elemBBox) => { 47 | return portsPositionArgs.map((_, index) => { 48 | return { 49 | position: { 50 | x: index * 35 + 12, 51 | y: elemBBox.height - 30, 52 | }, 53 | angle: 0, 54 | }; 55 | }); 56 | }, 57 | true 58 | ); 59 | 60 | Graph.registerNode( 61 | `node-common`, 62 | { 63 | inherit: "vue-shape", 64 | width: 180, 65 | height: 36, 66 | component: { 67 | template: ``, 68 | components: { 69 | common, 70 | }, 71 | }, 72 | ports: { 73 | groups: { 74 | top: { 75 | position: "top", 76 | attrs: { 77 | circle: { 78 | r: 4, 79 | magnet: true, 80 | strokeWidth: 0, 81 | fill: "transparent", 82 | }, 83 | }, 84 | }, 85 | list: { 86 | label: { 87 | position: "center", 88 | }, 89 | markup: [ 90 | { 91 | tagName: "rect", 92 | selector: "portBody", 93 | }, 94 | { 95 | tagName: "text", 96 | selector: "portNameLabel", 97 | }, 98 | { 99 | tagName: "text", 100 | selector: "portTypeLabel", 101 | }, 102 | ], 103 | attrs: { 104 | portBody: { 105 | magnet: true, 106 | }, 107 | portNameLabel: { 108 | ref: "portBody", 109 | fontSize: 10, 110 | text: "未启动", 111 | }, 112 | }, 113 | position: "erPortPosition", 114 | }, 115 | }, 116 | }, 117 | }, 118 | true 119 | ); 120 | 121 | Graph.registerEdge( 122 | "dag-edge", 123 | { 124 | inherit: "edge", 125 | attrs: { 126 | line: { 127 | stroke: "#C2C8D5", 128 | strokeWidth: 2, 129 | targetMarker: { 130 | name: "block", 131 | width: 12, 132 | height: 8, 133 | }, 134 | }, 135 | }, 136 | }, 137 | true 138 | ); 139 | Graph.registerConnector( 140 | "algo-connector", 141 | (s, e) => { 142 | const offset = 4; 143 | const deltaY = Math.abs(e.y - s.y); 144 | const control = Math.floor((deltaY / 3) * 2); 145 | 146 | const v1 = { x: s.x, y: s.y + offset + control }; 147 | const v2 = { x: e.x, y: e.y - offset - control }; 148 | 149 | return Path.normalize( 150 | `M ${s.x} ${s.y} 151 | L ${s.x} ${s.y + offset} 152 | C ${v1.x} ${v1.y} ${v2.x} ${v2.y} ${e.x} ${e.y - offset} 153 | L ${e.x} ${e.y} 154 | ` 155 | ); 156 | }, 157 | true 158 | ); 159 | }, 160 | initGraph() { 161 | this.registerNode(); 162 | 163 | const graph = new Graph({ 164 | grid: { 165 | size: 10, 166 | visible: true, 167 | type: "dot", // 'dot' | 'fixedDot' | 'mesh' 168 | args: { 169 | color: "#a05410", // 网格线/点颜色 170 | thickness: 1, // 网格线宽度/网格点大小 171 | }, 172 | }, 173 | background: { 174 | color: "#fffbe6", // 设置画布背景颜色 175 | }, 176 | container: document.getElementById("draw-cot"), 177 | panning: { 178 | enabled: true, 179 | eventTypes: ["leftMouseDown", "mouseWheel"], 180 | }, 181 | mousewheel: { 182 | enabled: true, 183 | modifiers: "ctrl", 184 | factor: 1.1, 185 | maxScale: 1.5, 186 | minScale: 0.5, 187 | }, 188 | highlighting: { 189 | magnetAdsorbed: { 190 | name: "stroke", 191 | args: { 192 | attrs: { 193 | fill: "#fff", 194 | stroke: "#31d0c6", 195 | strokeWidth: 4, 196 | }, 197 | }, 198 | }, 199 | }, 200 | connecting: { 201 | snap: true, 202 | allowBlank: false, 203 | allowLoop: false, 204 | highlight: true, 205 | connector: "algo-connector", 206 | connectionPoint: "anchor", 207 | anchor: "center", 208 | validateMagnet() { 209 | // return magnet.getAttribute('port-group') !== 'top' 210 | 211 | // 限制连线配置 212 | return true; 213 | }, 214 | createEdge() { 215 | return graph.createEdge({ 216 | shape: "dag-edge", 217 | attrs: { 218 | line: { 219 | strokeDasharray: "5 5", 220 | targetMarker: { 221 | name: "block", 222 | width: 12, 223 | height: 8, 224 | }, 225 | }, 226 | }, 227 | zIndex: -1, 228 | }); 229 | }, 230 | }, 231 | selecting: { 232 | enabled: true, 233 | multiple: true, 234 | rubberEdge: true, 235 | rubberNode: true, 236 | modifiers: "shift", 237 | rubberband: true, 238 | }, 239 | keyboard: true, 240 | clipboard: true, 241 | history: true, 242 | }); 243 | 244 | graph.on("edge:contextmenu", ({ e, x, y, edge, view }) => { 245 | console.log(x, y, view); 246 | this.showContextMenu = true; 247 | this.$nextTick(() => { 248 | this.$refs.menuBar.initFn(e.offsetX, e.offsetY, { 249 | type: "edge", 250 | item: edge, 251 | }); 252 | }); 253 | }); 254 | 255 | graph.on("edge:connected", ({ edge }) => { 256 | // const source = graph.getCellById(edge.source.cell); 257 | const target = graph.getCellById(edge.target.cell); 258 | 259 | // console.log (target); 260 | // console.log(edge); 261 | // 262 | graph.getEdges().forEach((e) => { 263 | if ( 264 | edge.id !== e.id && 265 | e.getTargetPortId() === edge.getTargetPortId() && 266 | e.getSourcePortId() === edge.getSourcePortId() 267 | ) { 268 | return graph.removeEdge(edge.id); 269 | } 270 | }); 271 | 272 | if (!target.isNode()) { 273 | return graph.removeEdge(edge.id); 274 | } 275 | 276 | let stroke = "blue"; 277 | try { 278 | stroke = edge.getSourceCell().getPort(edge.getSourcePortId()).attrs 279 | .portBody.fill; 280 | } catch (e) { 281 | stroke = "blue"; 282 | } 283 | 284 | edge.attr({ 285 | line: { 286 | strokeDasharray: "", 287 | stroke: stroke, 288 | }, 289 | }); 290 | }); 291 | 292 | return graph; 293 | }, 294 | }, 295 | }; 296 | -------------------------------------------------------------------------------- /src/views/demo3/index.vue: -------------------------------------------------------------------------------- 1 | 84 | 85 | 248 | 249 | 308 | -------------------------------------------------------------------------------- /src/views/dragDemo/components/drawer.vue: -------------------------------------------------------------------------------- 1 | 43 | 44 | 277 | 339 | -------------------------------------------------------------------------------- /src/views/dargeDemo/index.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 285 | 286 | 345 | -------------------------------------------------------------------------------- /src/views/dragDemo/index.vue: -------------------------------------------------------------------------------- 1 | 95 | 96 | 729 | 730 | 789 | --------------------------------------------------------------------------------