├── public ├── robots.txt ├── favicon.ico └── index.html ├── src ├── style │ └── init.scss ├── App.vue ├── assets │ └── logo.png ├── main.js ├── router │ └── index.js ├── components │ ├── modules │ │ ├── HeaderAttr.vue │ │ ├── ContextMenu.vue │ │ ├── MenuNode.vue │ │ ├── FlowNode.vue │ │ ├── FlowArea.vue │ │ └── HeaderOperate.vue │ ├── FlowAttr.vue │ ├── FlowMenu.vue │ └── FlowContent.vue ├── registerServiceWorker.js ├── utils │ └── index.js ├── config │ ├── flow.config.js │ └── icon.config.js ├── views │ └── Index.vue └── store │ └── index.js ├── img ├── index1.png └── index2.png ├── babel.config.js ├── .gitignore ├── package.json ├── README.md └── vue.config.js /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: 3 | -------------------------------------------------------------------------------- /src/style/init.scss: -------------------------------------------------------------------------------- 1 | .jtk-hover { 2 | cursor: auto; 3 | } 4 | -------------------------------------------------------------------------------- /img/index1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlluGitHub/vue-flow-topology/HEAD/img/index1.png -------------------------------------------------------------------------------- /img/index2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlluGitHub/vue-flow-topology/HEAD/img/index2.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlluGitHub/vue-flow-topology/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zlluGitHub/vue-flow-topology/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App.vue' 3 | import './registerServiceWorker' 4 | import router from './router' 5 | import store from './store' 6 | import ViewUI from 'view-design'; 7 | import 'view-design/dist/styles/iview.css'; 8 | import "./style/init.scss" 9 | 10 | import JsonViewer from 'vue-json-viewer' 11 | Vue.use(JsonViewer); 12 | 13 | import events from "./utils"; 14 | Vue.prototype.$events = events; 15 | 16 | Vue.config.productionTip = false; 17 | Vue.use(ViewUI); 18 | new Vue({ 19 | router, 20 | store, 21 | render: h => h(App) 22 | }).$mount('#app') 23 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import VueRouter from 'vue-router' 3 | import Index from '../views/Index.vue' 4 | 5 | Vue.use(VueRouter) 6 | 7 | const routes = [ 8 | { 9 | path: '/', 10 | name: 'Index', 11 | component: Index 12 | }, 13 | // { 14 | // path: '/about', 15 | // name: 'About', 16 | // // route level code-splitting 17 | // // this generates a separate chunk (about.[hash].js) for this route 18 | // // which is lazy-loaded when the route is visited. 19 | // component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') 20 | // } 21 | ] 22 | 23 | const router = new VueRouter({ 24 | mode: 'history', 25 | base: process.env.BASE_URL, 26 | routes 27 | }) 28 | 29 | export default router 30 | -------------------------------------------------------------------------------- /src/components/modules/HeaderAttr.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 33 | 41 | -------------------------------------------------------------------------------- /src/registerServiceWorker.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-console */ 2 | 3 | import { register } from 'register-service-worker' 4 | 5 | if (process.env.NODE_ENV === 'production') { 6 | register(`${process.env.BASE_URL}service-worker.js`, { 7 | ready () { 8 | console.log( 9 | 'App is being served from cache by a service worker.\n' + 10 | 'For more details, visit https://goo.gl/AFskqB' 11 | ) 12 | }, 13 | registered () { 14 | console.log('Service worker has been registered.') 15 | }, 16 | cached () { 17 | console.log('Content has been cached for offline use.') 18 | }, 19 | updatefound () { 20 | console.log('New content is downloading.') 21 | }, 22 | updated () { 23 | console.log('New content is available; please refresh.') 24 | }, 25 | offline () { 26 | console.log('No internet connection found. App is running in offline mode.') 27 | }, 28 | error (error) { 29 | console.error('Error during service worker registration:', error) 30 | } 31 | }) 32 | } 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "flow", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "core-js": "^3.6.5", 12 | "file-saver": "^2.0.5", 13 | "jsplumb": "^2.15.5", 14 | "register-service-worker": "^1.7.1", 15 | "view-design": "^4.4.0", 16 | "vue": "^2.6.11", 17 | "vue-json-viewer": "^2.2.18", 18 | "vue-router": "^3.2.0", 19 | "vuex": "^3.4.0" 20 | }, 21 | "devDependencies": { 22 | "@vue/cli-plugin-babel": "~4.5.0", 23 | "@vue/cli-plugin-eslint": "~4.5.0", 24 | "@vue/cli-plugin-pwa": "~4.5.0", 25 | "@vue/cli-plugin-router": "~4.5.0", 26 | "@vue/cli-plugin-vuex": "~4.5.0", 27 | "@vue/cli-service": "~4.5.0", 28 | "babel-eslint": "^10.1.0", 29 | "eslint": "^6.7.2", 30 | "eslint-plugin-vue": "^6.2.2", 31 | "node-sass": "^4.12.0", 32 | "sass-loader": "^8.0.2", 33 | "vue-template-compiler": "^2.6.11" 34 | }, 35 | "eslintConfig": { 36 | "root": true, 37 | "env": { 38 | "node": true 39 | }, 40 | "extends": [ 41 | "plugin:vue/essential", 42 | "eslint:recommended" 43 | ], 44 | "parserOptions": { 45 | "parser": "babel-eslint" 46 | }, 47 | "rules": {} 48 | }, 49 | "browserslist": [ 50 | "> 1%", 51 | "last 2 versions", 52 | "not dead" 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /src/components/modules/ContextMenu.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 36 | 37 | 60 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vue-flow-topology 2 | ## 介绍 3 | > vue-flow-topology 该项目可以看做是一个独立的 Vue 项目(大数据流水线拓展流程工作台),也可以嵌入到其他vue项目中使用,新版会作为优先版本持续迭代。 4 | - 版本一:基于 `Vue-cli3.0` + `view-design` + `JSPlumb` 开发( `master` 分支 )。 5 | - 版本二:基于 `Vue-cli3.0` + `Ant Design Vue` + `JSPlumb` 开发( `simple` 分支 )。 6 | 7 | ## 特性 8 | ✅ 支持画布重绘、拖拽、放大、缩小功能 9 | 10 | ✅ 支持鼠标滚轮缩放画布功能 11 | 12 | ✅ 支持拖拽、添加、删除节点功能 13 | 14 | ✅ 支持不同连线类型进行关联节点功能 15 | 16 | ✅ 支持删除连线、重绘连线功能 17 | 18 | ✅ 支持点击画布设置属性功能 19 | 20 | ✅ 支持点击节点设置属性功能 21 | 22 | ✅ 支持点击线进行设置属性功能 23 | 24 | ✅ 支持导入、导出指定数据文件功能 25 | 26 | ✅ 支持复制、粘贴节点功能 27 | 28 | ## 版本一( master 分支 ) 29 | ### 演示地址 30 | - → 在线 Demo 预览:[http://flow.zhenglinglu.cn](http://flow.zhenglinglu.cn) 31 | ### 快速使用 32 | ```bash 33 | # clone this project 34 | git clone https://github.com/zlluGitHub/vue-flow-topology.git 35 | 36 | # Project setup 37 | npm install 38 | 39 | # Compiles and hot-reloads for development 40 | npm run dev 41 | 42 | # Compiles and minifies for production 43 | npm run build 44 | 45 | # Lints and fixes files 46 | npm run lint 47 | ``` 48 | #### 示例图片 49 | ![](./img/index1.png) 50 | 51 | ## 版本二( simple 分支 ) 52 | ### 演示地址 53 | - → 在线 Demo 预览:[http://flow.zhenglinglu.cn/simple.html](http://flow.zhenglinglu.cn/simple.html) 54 | ### 快速使用 55 | ```bash 56 | # clone this project 57 | git clone -b simple https://github.com/zlluGitHub/vue-flow-topology.git 58 | 59 | # Project setup 60 | npm install 61 | 62 | # Compiles and hot-reloads for development 63 | npm run dev 64 | 65 | # Compiles and minifies for production 66 | npm run build 67 | 68 | # Lints and fixes files 69 | npm run lint 70 | ``` 71 | #### 示例图片 72 | ![](./img/index2.png) -------------------------------------------------------------------------------- /src/utils/index.js: -------------------------------------------------------------------------------- 1 | 2 | const S4 = () => (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); 3 | export const getUUID = () => { 4 | return (S4() + S4() + "-" + S4() + "-" + S4()); 5 | } 6 | export const deepClone = source => { 7 | const targetObj = source.constructor === Array ? [] : {}; // 判断复制的目标是数组还是对象 8 | for (let keys in source) { 9 | // 遍历目标 10 | if (source.hasOwnProperty(keys)) { 11 | if (source[keys] && typeof source[keys] === "object") { 12 | // 如果值是对象,就递归一下 13 | targetObj[keys] = source[keys].constructor === Array ? [] : {}; 14 | targetObj[keys] = deepClone(source[keys]); 15 | } else { 16 | // 如果不是,就直接赋值 17 | targetObj[keys] = source[keys]; 18 | } 19 | } 20 | } 21 | return targetObj; 22 | } 23 | export const uploadFile = (input, callBack) => { 24 | //支持chrome IE10 25 | if (window.FileReader) { 26 | let file = input.files[0], filename = file.name.split(".")[0]; 27 | let reader = new FileReader(); 28 | reader.onload = function () { 29 | // console.log(this.result); 30 | callBack(this.result, filename) 31 | } 32 | reader.readAsText(file); 33 | } 34 | //支持IE 7 8 9 10 35 | else if (typeof window.ActiveXObject != 'undefined') { 36 | let xmlDoc; 37 | xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); 38 | xmlDoc.async = false; 39 | xmlDoc.load(input.value); 40 | callBack(xmlDoc.xml) 41 | } 42 | //支持FF 43 | else if (document.implementation && document.implementation.createDocument) { 44 | let xmlDoc; 45 | xmlDoc = document.implementation.createDocument("", "", null); 46 | xmlDoc.async = false; 47 | xmlDoc.load(input.value); 48 | callBack(xmlDoc.xml) 49 | } else { 50 | console.error('文件读取失败!') 51 | } 52 | } 53 | export default { getUUID, uploadFile } -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | 2 | // const CompressionPlugin = require("compression-webpack-plugin") 3 | module.exports = { 4 | // configureWebpack: config => { 5 | // if (process.env.NODE_ENV === 'production') { // 去掉console.log 6 | // config.optimization.minimizer[0].options.terserOptions.compress.warnings = false 7 | // config.optimization.minimizer[0].options.terserOptions.compress.drop_console = true 8 | // config.optimization.minimizer[0].options.terserOptions.compress.drop_debugger = true 9 | // config.optimization.minimizer[0].options.terserOptions.compress.pure_funcs = ['console.log'] 10 | // } 11 | 12 | // if (process.env.NODE_ENV === 'production') { 13 | // return { 14 | // plugins: [ 15 | // new CompressionPlugin({ 16 | // test: /\.js$|\.html$|\.css/, //匹配文件名 17 | // threshold: 10240,//对超过10k的数据压缩 18 | // deleteOriginalAssets: false //不删除源文件 19 | // }) 20 | // ] 21 | // } 22 | // } 23 | // }, 24 | // 根域上下文目录 25 | // publicPath: '/topology', // publicPath:'/rm', 这里可以设置二级文件夹作为主页面 26 | 27 | // devServer: { 28 | // port: 8080, 29 | // // sockHost: 'http://127.0.0.1', 30 | // // disableHostCheck: true, 31 | // // open: true, 32 | // // host: "localhost", 33 | // proxy: { 34 | // '/piflow': { 35 | // target: ' ', 36 | // changeOrigin: true, 37 | // ws: true, // 是否启用websockets 38 | // secure: false, // 使用的是http协议则设置为false,https协议则设置为true 39 | // pathRewrite: { 40 | // '^/piflow': '/' 41 | // } 42 | // } 43 | // }, 44 | // }, 45 | productionSourceMap: process.env.NODE_ENV === 'production' ? false : true, 46 | lintOnSave: false, // 取消 eslint 验证 47 | 48 | } -------------------------------------------------------------------------------- /src/config/flow.config.js: -------------------------------------------------------------------------------- 1 | export const flowAnchors = ["Top", "Right", "Bottom", "Left"]; 2 | export const flowAnchor = "Continuous"; 3 | export const connectorType = ['Bezier', 'Flowchart', 'StateMachine', 'Straight']; 4 | export const jsPlumbConfig = { 5 | isSource: true,// 是否可以拖动(作为连线起点) 6 | isTarget: true,// 是否可以放置(连线终点) 7 | connector: [ 8 | "Straight",// 连接线的样式种类有[Bezier],[Flowchart],[StateMachine ],[Straight ] 9 | // { 10 | // gap: 5, 11 | // cornerRadius: 8, 12 | // alwaysRespectStubs: true, 13 | // }, 14 | ], 15 | // connectionsDetachable: false, 16 | // anchors: ["Top", "Right", "Bottom", "Left"],//"Continuous", "AutoDefault","Top" ,"Right", "Bottom", "Left","TopRight", "TopLeft","BottomRight","BottomLeft", "Center" 17 | // anchor: "Continuous",//"Continuous", "AutoDefault","Top" ,"Right", "Bottom", "Left","TopRight", "TopLeft","BottomRight","BottomLeft", "Center" 18 | // isDetachable: false, //连线是否可重新编辑 19 | maxConnections: -1, 20 | // endpoint: 'Rectangle', //Dot:{radius,cssClass,hoverClass }、Rectangle:{width,height,cssClass,hoverClass }、Image:{src,cssClass,hoverClass }、Blank 21 | 22 | //锚点形状 23 | endpoints: [ 24 | "Dot", 25 | { 26 | radius: 7, 27 | fill: "#000", 28 | }, 29 | ], 30 | 31 | //锚点样式 32 | paintStyle: { 33 | // fill: "#000", 34 | radius: 2, //锚点大小 35 | stroke: "#4caf50", 36 | strokeWidth: 1, 37 | }, 38 | hoverPaintStyle: { 39 | // fill: "#4caf50", 40 | stroke: "#4caf50", 41 | strokeWidth: 3 42 | }, 43 | 44 | //连线的样式 45 | connectorStyle: { 46 | // outlineStroke: "#000", 47 | strokeWidth: 1, 48 | // lineWidth: 1, 49 | stroke: "#4caf50", 50 | // joinstyle: "round", 51 | // fill: "pink", 52 | // outlineColor: "", 53 | // outlineWidth: "", 54 | }, 55 | connectorHoverStyle: { 56 | strokeWidth: 2, 57 | // lineWidth: 2, 58 | // strokeStyle: "red", 59 | // outlineWidth: 10, 60 | // outlineColor: "", 61 | }, 62 | 63 | // Arrow 一个可配置的箭头 64 | // Label 标签,可以在连接上显示文字信息 65 | // PlainArrow 原始类型的箭头 66 | // Diamond 菱形箭头 67 | // Custom 自定义类型 68 | connectorOverlays: [ 69 | [ 70 | "Arrow", 71 | { 72 | width: 10, 73 | length: 10, 74 | visible: true, 75 | location: 1, 76 | }, 77 | ], 78 | // [ 79 | // "Label", 80 | // { 81 | // label: "dfdf", 82 | // cssClass: "", 83 | // labelStyle: { 84 | // color: "red", 85 | // }, 86 | // events: { 87 | // click: function (labelOverlay, originalEvent) { 88 | // console.log("点击了label"); 89 | // }, 90 | // }, 91 | // }, 92 | // ], 93 | ], 94 | 95 | } 96 | // 节点默认参数 97 | export const nodePrame = { 98 | // data: null 99 | } 100 | // 连线默认参数 101 | export const linkPrame = { 102 | type: null 103 | } 104 | // 节点默认参数 105 | export const flowConfig = { 106 | offsetX: -3000, 107 | offsetY: -3000, 108 | title:"", 109 | nodes: [], 110 | links: [] 111 | } -------------------------------------------------------------------------------- /src/components/modules/MenuNode.vue: -------------------------------------------------------------------------------- 1 | 44 | 50 | 51 | 140 | -------------------------------------------------------------------------------- /src/views/Index.vue: -------------------------------------------------------------------------------- 1 | 44 | 45 | 115 | 157 | -------------------------------------------------------------------------------- /src/components/FlowAttr.vue: -------------------------------------------------------------------------------- 1 | 119 | 120 | 164 | 165 | 180 | -------------------------------------------------------------------------------- /src/components/modules/FlowNode.vue: -------------------------------------------------------------------------------- 1 | 50 | 90 | 91 | 182 | -------------------------------------------------------------------------------- /src/components/FlowMenu.vue: -------------------------------------------------------------------------------- 1 | 19 | 142 | 143 | 184 | -------------------------------------------------------------------------------- /src/store/index.js: -------------------------------------------------------------------------------- 1 | import { deepClone } from "@/utils"; 2 | import Vue from 'vue' 3 | import Vuex from 'vuex' 4 | Vue.use(Vuex) 5 | let storeData = { 6 | state: { 7 | jspInit: null, 8 | flowMenuObj: { 9 | type: 'drag-drop', 10 | connector: 'Bezier' 11 | }, 12 | // flowData:{ "offsetX": -2993, "offsetY": -2991,nodes:[],links:[]}, 13 | flowData: { "offsetX": -2993, "offsetY": -2991, "title": "数据处理流程图", "nodes": [{ "name": "终止节点", "type": "end-node", "id": "03b711a6-e730-7dc4", "x": 3369, "y": 3237 }, { "name": "输出", "type": "out-node", "id": "2f2f5c9c-effb-9846", "x": 4062, "y": 3233 }, { "name": "起始节点", "type": "start-node", "id": "efa686eb-c07b-4d92", "x": 3683, "y": 3088 }, { "name": "输入", "type": "in-node", "id": "5dbc5113-c54a-c119", "x": 3091, "y": 3302 }, { "name": "基础配置", "type": "cog-node", "icon": "md-cog", "id": "008a6541-ccfc-03b5", "x": 3548, "y": 3693 }, { "name": "抽离模块", "type": "codepen-node", "icon": "md-cube", "id": "9b593548-5b94-220c", "x": 3041, "y": 3567 }, { "name": "进程监听", "type": "pulse-node", "icon": "md-pulse", "id": "d59a2dbd-140a-80ff", "x": 4030, "y": 3536 }, { "name": "输出", "type": "out-node", "id": "7a42861e-6e6b-ad35", "x": 4213, "y": 3091 }, { "name": "抽离模块", "type": "codepen-node", "icon": "md-cube", "id": "b7184cb9-95ca-3b77", "x": 3580, "y": 3381 }, { "name": "进程监听", "type": "pulse-node", "icon": "md-pulse", "id": "c00fb55c-2ebf-22d6", "x": 3102, "y": 3072 }], "links": [{ "id": "93525b44-b67d-b02c", "connector": "Flowchart", "sourceId": "03b711a6-e730-7dc4", "targetId": "5dbc5113-c54a-c119" }, { "id": "dfbdad0e-baf2-45ae", "connector": "Flowchart", "sourceId": "03b711a6-e730-7dc4", "targetId": "efa686eb-c07b-4d92" }, { "id": "5b75278a-ce70-1339", "connector": "Flowchart", "sourceId": "efa686eb-c07b-4d92", "targetId": "2f2f5c9c-effb-9846" }, { "id": "8b78c9ed-b270-b66a", "connector": "Bezier", "sourceId": "03b711a6-e730-7dc4", "targetId": "9b593548-5b94-220c", "label": "开发" }, { "id": "ea20501e-c11a-7639", "connector": "Bezier", "sourceId": "2f2f5c9c-effb-9846", "targetId": "d59a2dbd-140a-80ff" }, { "id": "4d63a745-f096-f3dc", "connector": "Straight", "sourceId": "2f2f5c9c-effb-9846", "targetId": "008a6541-ccfc-03b5" }, { "id": "f3706e5b-404b-3552", "connector": "Straight", "sourceId": "03b711a6-e730-7dc4", "targetId": "008a6541-ccfc-03b5" }, { "id": "e5bb1821-f195-d534", "connector": "Flowchart", "sourceId": "008a6541-ccfc-03b5", "targetId": "d59a2dbd-140a-80ff" }, { "id": "3629f98f-fa50-a83e", "connector": "Flowchart", "sourceId": "9b593548-5b94-220c", "targetId": "008a6541-ccfc-03b5" }, { "id": "ad71eb33-0dea-8661", "connector": "Flowchart", "sourceId": "7a42861e-6e6b-ad35", "targetId": "d59a2dbd-140a-80ff" }, { "id": "16d48ac7-bfc8-c9ae", "connector": "Bezier", "sourceId": "7a42861e-6e6b-ad35", "targetId": "2f2f5c9c-effb-9846" }, { "id": "cb69aad0-5126-7b18", "connector": "Bezier", "sourceId": "efa686eb-c07b-4d92", "targetId": "b7184cb9-95ca-3b77" }, { "id": "e3e31972-0274-1e76", "connector": "Bezier", "sourceId": "b7184cb9-95ca-3b77", "targetId": "2f2f5c9c-effb-9846" }, { "id": "871a35d1-408d-baef", "connector": "Bezier", "sourceId": "b7184cb9-95ca-3b77", "targetId": "008a6541-ccfc-03b5" }, { "id": "b0ec12a6-1089-088d", "connector": "Bezier", "sourceId": "b7184cb9-95ca-3b77", "targetId": "03b711a6-e730-7dc4" }, { "id": "71032d91-8f54-91ec", "connector": "Bezier", "sourceId": "b7184cb9-95ca-3b77", "targetId": "b7184cb9-95ca-3b77" }, { "id": "59a2af00-5710-ce4e", "connector": "Bezier", "sourceId": "d59a2dbd-140a-80ff", "targetId": "d59a2dbd-140a-80ff" }, { "id": "ffb1c9ef-0ccb-874b", "connector": "Flowchart", "sourceId": "03b711a6-e730-7dc4", "targetId": "c00fb55c-2ebf-22d6" }] }, 14 | // 添加新节点 15 | newNode: { 16 | state: false, 17 | node: {} 18 | }, 19 | //选中的节点、连线数据 20 | selectContent: { 21 | type: "", 22 | data: {} 23 | }, 24 | //步骤缓存 25 | flowStepData: [], 26 | stepIndex: 0, 27 | }, 28 | mutations: { 29 | setSelectContent(state, data) { 30 | state.selectContent = { 31 | type: data.type ? data.type : '', 32 | data: data.data ? data.data : '' 33 | }; 34 | }, 35 | setJspInit(state, data) { 36 | state.jspInit = data; 37 | }, 38 | setFlowData(state, dataObj) { 39 | let data = deepClone(dataObj) 40 | if (data.method) { 41 | if (data.method === "add-node") { 42 | state.flowData.nodes.push(data.node); 43 | storeData.mutations.setFlowStepData(state, state.flowData); 44 | } else if (data.method === "delete-node") { 45 | let nodes = state.flowData.nodes.filter(node => data.node.id !== node.id) 46 | let links = state.flowData.links.filter(link => { 47 | if (link.sourceId === data.node.id || link.targetId === data.node.id) { 48 | return false 49 | } 50 | return true 51 | }); 52 | state.flowData = { ...state.flowData, ...{ nodes, links } }; 53 | storeData.mutations.setFlowStepData(state, state.flowData); 54 | } else if (data.method === "add-link") { 55 | state.flowData.links.push(data.link); 56 | storeData.mutations.setFlowStepData(state, state.flowData); 57 | } else if (data.method === "delete-link") { 58 | state.flowData.links = state.flowData.links.filter(item => { 59 | if (((item.sourceId === data.link.sourceId) && (item.targetId === data.link.targetId)) || ((item.sourceId === data.link.targetId) && (item.targetId === data.link.sourceId))) { 60 | return false 61 | } 62 | return true; 63 | }) 64 | storeData.mutations.setFlowStepData(state, state.flowData); 65 | } else if (data.method === "all-update") { 66 | state.flowData = data.data 67 | state.flowStepData = []; 68 | state.stepIndex = 0; 69 | storeData.mutations.setFlowStepData(state, state.flowData); 70 | } 71 | } else { 72 | state.flowData = { ...state.flowData, ...data }; 73 | } 74 | 75 | sessionStorage.setItem("flowData", JSON.stringify(state.flowData)); 76 | console.log(state.flowData); 77 | }, 78 | setFlowMenuObj(state, data) { 79 | state.flowMenuObj = { 80 | type: data.type, 81 | connector: data.connector 82 | }; 83 | }, 84 | setNewNode(state, data) { 85 | if (data.state) { 86 | state.newNode.state = data.state; 87 | } else { 88 | state.newNode.state = false 89 | } 90 | if (data.node) { 91 | state.newNode.node = data.node; 92 | } 93 | }, 94 | // 缓存flow步骤 95 | setFlowStepData(state, data) { 96 | if (!data) { 97 | state.flowStepData = []; 98 | state.stepIndex = 0; 99 | } else { 100 | let newArr = deepClone(state.flowStepData.slice(0, state.stepIndex + 1)) 101 | newArr.push(deepClone(data)); 102 | state.flowStepData = newArr; 103 | state.stepIndex = newArr.length - 1 104 | } 105 | }, 106 | setStepIndex(state, i) { 107 | state.stepIndex = i; 108 | }, 109 | }, 110 | actions: { 111 | }, 112 | modules: { 113 | } 114 | }; 115 | 116 | export default new Vuex.Store(storeData); 117 | -------------------------------------------------------------------------------- /src/components/modules/FlowArea.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 218 | 219 | 227 | -------------------------------------------------------------------------------- /src/config/icon.config.js: -------------------------------------------------------------------------------- 1 | export const FlowchartSvg = (color) => { 2 | return `` 3 | }; 4 | 5 | export const StraightSvg = (color) => { 6 | return `` 7 | }; 8 | 9 | 10 | export const StateMachineSvg = (color) => { 11 | return `` 12 | }; 13 | 14 | export const BezierSvg = (color) => { 15 | return `` 16 | }; 17 | export const EnlargeSvg = (color) => { 18 | return `` 19 | }; 20 | 21 | export const NarrowSvg = (color) => { 22 | return `` 23 | }; -------------------------------------------------------------------------------- /src/components/modules/HeaderOperate.vue: -------------------------------------------------------------------------------- 1 | 119 | 191 | 192 | 227 | -------------------------------------------------------------------------------- /src/components/FlowContent.vue: -------------------------------------------------------------------------------- 1 | 37 | 335 | 406 | --------------------------------------------------------------------------------