├── 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 |
2 |
3 |
4 |
5 |
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 |
2 |
5 |
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 |
2 |
15 |
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 | 
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 | 
--------------------------------------------------------------------------------
/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 |
2 |
3 |
6 |
9 |
12 |
15 |
22 |
29 |
41 |
无效节点
42 |
43 |
44 |
50 |
51 |
140 |
--------------------------------------------------------------------------------
/src/views/Index.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
16 |
24 |
25 |
28 |
29 |
30 |
31 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
115 |
157 |
--------------------------------------------------------------------------------
/src/components/FlowAttr.vue:
--------------------------------------------------------------------------------
1 |
2 |
118 |
119 |
120 |
164 |
165 |
180 |
--------------------------------------------------------------------------------
/src/components/modules/FlowNode.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
12 |
15 |
18 |
21 |
28 |
35 |
47 |
此节点无效
48 |
49 |
50 |
90 |
91 |
182 |
--------------------------------------------------------------------------------
/src/components/FlowMenu.vue:
--------------------------------------------------------------------------------
1 |
2 |
18 |
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 |
2 |
3 |
11 |
12 |
13 |
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 |
2 |
118 |
119 |
191 |
192 |
227 |
--------------------------------------------------------------------------------
/src/components/FlowContent.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 | 缩放:{{ parseInt(viewScale * 100) }}%
10 |
20 |
21 |
22 | x: {{ mousePosition.x }}, y: {{ mousePosition.y }}
23 |
24 |
25 |
26 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
335 |
406 |
--------------------------------------------------------------------------------