├── public ├── about │ ├── me.png │ └── group.png ├── favicon.ico ├── flow │ ├── 07.png │ ├── 08.png │ ├── 10.png │ ├── 60.png │ ├── img.png │ ├── img_1.png │ └── short.gif ├── usages │ ├── 04.png │ ├── 05.png │ ├── img.png │ └── img_1.png ├── svg │ └── loading-spin.svg ├── cdn │ └── style │ │ └── index.css └── index.html ├── src ├── assets │ ├── images │ │ ├── grid-bg.jpg │ │ ├── remark.png │ │ ├── search.png │ │ └── multip-pointer.png │ ├── svges │ │ ├── index.js │ │ ├── arrow-icon.svg │ │ ├── start-icon.svg │ │ ├── virtual-icon.svg │ │ ├── node-icon.svg │ │ └── job-icon.svg │ └── style │ │ ├── flow-attr.less │ │ ├── flow-designer.less │ │ ├── flow-area.less │ │ └── flow-node.less ├── App.vue ├── router │ ├── errorCode.js │ ├── index.js │ └── axios.js ├── config │ ├── type.js │ ├── attr-config.js │ ├── node-config.js │ └── flow-config.js ├── main.js ├── api │ ├── jsonflow │ │ └── index.js │ └── admin │ │ └── index.js ├── ele-ui.js ├── components │ ├── node-menu.vue │ ├── flow-node.vue │ ├── flow-attr.vue │ └── flow-area.vue ├── utils │ ├── dict-prop.js │ ├── store.js │ └── common.js └── views │ ├── shortcut.vue │ ├── json-view.vue │ ├── user-login.vue │ ├── setting.vue │ └── flow-design.vue ├── babel.config.js ├── .editorconfig ├── .gitignore ├── vue.config.js ├── package.json ├── README.md └── LICENSE /public/about/me.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackRolling/jsonflow-ui/HEAD/public/about/me.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackRolling/jsonflow-ui/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/flow/07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackRolling/jsonflow-ui/HEAD/public/flow/07.png -------------------------------------------------------------------------------- /public/flow/08.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackRolling/jsonflow-ui/HEAD/public/flow/08.png -------------------------------------------------------------------------------- /public/flow/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackRolling/jsonflow-ui/HEAD/public/flow/10.png -------------------------------------------------------------------------------- /public/flow/60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackRolling/jsonflow-ui/HEAD/public/flow/60.png -------------------------------------------------------------------------------- /public/flow/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackRolling/jsonflow-ui/HEAD/public/flow/img.png -------------------------------------------------------------------------------- /public/about/group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackRolling/jsonflow-ui/HEAD/public/about/group.png -------------------------------------------------------------------------------- /public/flow/img_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackRolling/jsonflow-ui/HEAD/public/flow/img_1.png -------------------------------------------------------------------------------- /public/flow/short.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackRolling/jsonflow-ui/HEAD/public/flow/short.gif -------------------------------------------------------------------------------- /public/usages/04.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackRolling/jsonflow-ui/HEAD/public/usages/04.png -------------------------------------------------------------------------------- /public/usages/05.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackRolling/jsonflow-ui/HEAD/public/usages/05.png -------------------------------------------------------------------------------- /public/usages/img.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackRolling/jsonflow-ui/HEAD/public/usages/img.png -------------------------------------------------------------------------------- /public/usages/img_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackRolling/jsonflow-ui/HEAD/public/usages/img_1.png -------------------------------------------------------------------------------- /src/assets/images/grid-bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackRolling/jsonflow-ui/HEAD/src/assets/images/grid-bg.jpg -------------------------------------------------------------------------------- /src/assets/images/remark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackRolling/jsonflow-ui/HEAD/src/assets/images/remark.png -------------------------------------------------------------------------------- /src/assets/images/search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackRolling/jsonflow-ui/HEAD/src/assets/images/search.png -------------------------------------------------------------------------------- /src/assets/images/multip-pointer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackRolling/jsonflow-ui/HEAD/src/assets/images/multip-pointer.png -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset', 4 | /* "env"*/ 5 | ], 6 | /* plugins: [ 7 | "transform-vue-jsx" 8 | ]*/ 9 | } 10 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 12 | 13 | 18 | -------------------------------------------------------------------------------- /src/assets/svges/index.js: -------------------------------------------------------------------------------- 1 | // SVG ICON图标 2 | export const nodeJobSvgIcons = { 3 | start: require('./start-icon.svg') 4 | ,arrow: require('./arrow-icon.svg') 5 | ,node: require('./node-icon.svg') 6 | ,job: require('./job-icon.svg') 7 | ,virtual: require('./virtual-icon.svg') 8 | } 9 | -------------------------------------------------------------------------------- /src/assets/svges/arrow-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # http://editorconfig.org 2 | 3 | root = true 4 | 5 | [*] 6 | charset = utf-8 7 | indent_style = space 8 | indent_size = 2 9 | end_of_line = lf 10 | insert_final_newline = true 11 | trim_trailing_whitespace = true 12 | 13 | [*.md] 14 | insert_final_newline = false 15 | trim_trailing_whitespace = false 16 | -------------------------------------------------------------------------------- /src/router/errorCode.js: -------------------------------------------------------------------------------- 1 | export default { 2 | '000': '操作太频繁,请勿重复请求', 3 | '401': '当前操作没有权限', 4 | '403': '当前操作没有权限', 5 | '404': '资源不存在', 6 | '417': '未绑定登录账号,请使用密码登录后绑定', 7 | '423': '演示环境不能操作,如需了解联系我们', 8 | '426': '用户名不存在或密码错误', 9 | '428': '验证码错误,请重新输入', 10 | '429': '请求过频繁', 11 | '479': '演示环境,没有权限操作', 12 | 'default': '系统未知错误,请反馈给管理员' 13 | } 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | /tests/e2e/videos/ 6 | /tests/e2e/screenshots/ 7 | 8 | # local env files 9 | .env.local 10 | .env.*.local 11 | 12 | # Log files 13 | npm-debug.log* 14 | yarn-debug.log* 15 | yarn-error.log* 16 | 17 | # Editor directories and files 18 | .idea 19 | .vscode 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw* 25 | -------------------------------------------------------------------------------- /src/assets/svges/start-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/svg/loading-spin.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/router/index.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import Router from 'vue-router' 3 | import FlowDesign from '../views/flow-design' 4 | import UserLogin from '../views/user-login' 5 | 6 | Vue.use(Router) 7 | 8 | export default new Router({ 9 | routes: [ 10 | { 11 | path: '/', 12 | name: 'UserLogin', 13 | component: UserLogin 14 | }, 15 | { 16 | path: '/flow-design', 17 | name: 'FlowDesign', 18 | component: FlowDesign 19 | } 20 | ] 21 | }) 22 | -------------------------------------------------------------------------------- /src/assets/style/flow-attr.less: -------------------------------------------------------------------------------- 1 | .flow-attr { 2 | /*属性面板*/ 3 | .el-form-item { 4 | margin: 0 15px 10px!important; 5 | } 6 | 7 | /*属性面板在一行*/ 8 | .el-form--label-top .el-form-item__label{ 9 | display: inline-block; 10 | text-align: left; 11 | padding: 0 0 10px!important; 12 | } 13 | 14 | /*tab选中样式*/ 15 | .el-tabs__active-bar { 16 | background-color: revert; 17 | } 18 | 19 | /*tab头居中*/ 20 | .el-tabs__header{ 21 | margin: 8px!important; 22 | } 23 | } 24 | 25 | -------------------------------------------------------------------------------- /src/config/type.js: -------------------------------------------------------------------------------- 1 | export const ToolsType = { 2 | DRAG : 'drag', 3 | CONNECTION : 'connection', 4 | FLOW : 'flow' 5 | } 6 | 7 | export const CommonNodeType = { 8 | START : 'start', 9 | SERIAL : 'serial', 10 | PARALLEL : 'parallel', 11 | END : 'end', 12 | LINK : 'link' 13 | } 14 | 15 | export const HighNodeType = { 16 | CHILD_FLOW : 'child_flow', 17 | VIRTUAL : 'virtual', 18 | JOB : 'job' 19 | } 20 | 21 | export const LaneNodeType = { 22 | X_LANE : 'x_lane', 23 | Y_LANE : 'y_lane' 24 | } 25 | -------------------------------------------------------------------------------- /vue.config.js: -------------------------------------------------------------------------------- 1 | const url = 'http://jsonflow-gateway:9999' 2 | const path = require("path"); 3 | 4 | module.exports = { 5 | lintOnSave: false, 6 | outputDir: "dist", 7 | // 开发环境显示报错位置 8 | productionSourceMap: true, 9 | runtimeCompiler: true, 10 | 11 | devServer: { 12 | disableHostCheck: true, 13 | port: 8080, 14 | proxy: { 15 | '/': { 16 | target: url, 17 | ws: false, 18 | pathRewrite: { 19 | '^/': '/' 20 | } 21 | } 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/assets/svges/virtual-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/main.js: -------------------------------------------------------------------------------- 1 | // The Vue build version to load with the `import` command 2 | // (runtime-only or standalone) has been set in webpack.base.conf with an alias. 3 | import Vue from 'vue' 4 | import App from './App' 5 | // 组件按需加载 6 | import './ele-ui.js' 7 | 8 | import router from './router' 9 | import VueContextMenu from 'vue-contextmenu' 10 | 11 | import 'element-ui/lib/theme-chalk/index.css'; 12 | 13 | Vue.config.productionTip = false 14 | 15 | Vue.use(VueContextMenu) 16 | 17 | /* eslint-disable no-new */ 18 | new Vue({ 19 | el: '#app', 20 | router, 21 | components: { App }, 22 | template: '' 23 | }) 24 | -------------------------------------------------------------------------------- /src/assets/svges/node-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /src/assets/svges/job-icon.svg: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /public/cdn/style/index.css: -------------------------------------------------------------------------------- 1 | html, 2 | body, 3 | #app { 4 | height: 100%; 5 | margin: 0; 6 | padding: 0; 7 | } 8 | 9 | .jsonflow-home { 10 | background-color: #303133; 11 | height: 100%; 12 | display: flex; 13 | flex-direction: column; 14 | } 15 | 16 | .jsonflow-home__main { 17 | user-select: none; 18 | width: 100%; 19 | flex-grow: 1; 20 | display: flex; 21 | justify-content: center; 22 | align-items: center; 23 | flex-direction: column; 24 | } 25 | 26 | .jsonflow-home__footer { 27 | width: 100%; 28 | flex-grow: 0; 29 | text-align: center; 30 | padding: 1em 0; 31 | } 32 | 33 | .jsonflow-home__footer > a { 34 | font-size: 12px; 35 | color: #ABABAB; 36 | text-decoration: none; 37 | } 38 | 39 | .jsonflow-home__loading { 40 | height: 32px; 41 | width: 32px; 42 | margin-bottom: 20px; 43 | } 44 | 45 | .jsonflow-home__title { 46 | color: #FFF; 47 | font-size: 14px; 48 | margin-bottom: 10px; 49 | } 50 | 51 | .jsonflow-home__sub-title { 52 | color: #ABABAB; 53 | font-size: 12px; 54 | } 55 | 56 | .jsonflow-tree__content { 57 | padding: 5px 0 0 5px !important; 58 | height: calc(100% - 32px); 59 | } 60 | -------------------------------------------------------------------------------- /src/api/jsonflow/index.js: -------------------------------------------------------------------------------- 1 | import request from '@/router/axios' 2 | 3 | export function listDefFlow() { 4 | return request({ 5 | url: '/jsonflow/def-flow/list', 6 | method: 'get' 7 | }) 8 | } 9 | 10 | export function addObj(obj) { 11 | return request({ 12 | url: '/jsonflow/def-flow', 13 | method: 'post', 14 | data: obj 15 | }) 16 | } 17 | 18 | export function putObj(obj) { 19 | return request({ 20 | url: '/jsonflow/def-flow', 21 | method: 'put', 22 | data: obj 23 | }) 24 | } 25 | 26 | export function listUserKey() { 27 | return request({ 28 | url: '/jsonflow/node-job/list/user-key', 29 | method: 'get' 30 | }) 31 | } 32 | 33 | export function listTabsOptions() { 34 | return request({ 35 | url: '/jsonflow/tabs-option/list', 36 | method: 'get' 37 | }) 38 | } 39 | 40 | export function listVarKey() { 41 | return request({ 42 | url: '/jsonflow/flow-node-rel/list/var-key', 43 | method: 'get' 44 | }) 45 | } 46 | 47 | export function listRole() { 48 | return request({ 49 | url: '/admin/role/list', 50 | method: 'get' 51 | }) 52 | } 53 | 54 | export function listUser() { 55 | return request({ 56 | url: '/admin/user/list', 57 | method: 'get' 58 | }) 59 | } 60 | -------------------------------------------------------------------------------- /src/ele-ui.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | 3 | import { 4 | Button, 5 | Badge, 6 | Checkbox, 7 | Divider, 8 | Drawer, 9 | Form, 10 | FormItem, 11 | Icon, 12 | Input, 13 | Container, 14 | Aside, 15 | Menu, 16 | MenuItem, 17 | Dialog, 18 | Popconfirm, 19 | Row, 20 | Select, 21 | Option, 22 | Switch, 23 | Slider, 24 | Tabs, 25 | TabPane, 26 | Tag, 27 | Table, 28 | TableColumn, 29 | Tooltip, 30 | Header, 31 | Main, 32 | Footer, 33 | InputNumber, 34 | Message, 35 | MessageBox, 36 | ColorPicker 37 | } from 'element-ui'; 38 | 39 | Vue.use(Button) 40 | Vue.use(Badge) 41 | Vue.use(Checkbox) 42 | Vue.use(Divider) 43 | Vue.use(Drawer) 44 | Vue.use(Form) 45 | Vue.use(FormItem) 46 | Vue.use(Icon) 47 | Vue.use(Input) 48 | Vue.use(Container) 49 | Vue.use(Aside) 50 | Vue.use(Menu) 51 | Vue.use(MenuItem) 52 | Vue.use(Dialog) 53 | Vue.use(Popconfirm) 54 | Vue.use(Row) 55 | Vue.use(Select) 56 | Vue.use(Option) 57 | Vue.use(Switch) 58 | Vue.use(Slider) 59 | Vue.use(Tabs) 60 | Vue.use(TabPane) 61 | Vue.use(Tag) 62 | Vue.use(Table) 63 | Vue.use(TableColumn) 64 | Vue.use(Tooltip) 65 | Vue.use(Header) 66 | Vue.use(Main) 67 | Vue.use(Footer) 68 | Vue.use(InputNumber) 69 | Vue.use(ColorPicker) 70 | 71 | Vue.prototype.$message = Message 72 | Vue.prototype.$confirm = MessageBox.confirm 73 | -------------------------------------------------------------------------------- /src/config/attr-config.js: -------------------------------------------------------------------------------- 1 | import {deepClone} from "@/utils/common"; 2 | 3 | export const clazzAttr = { 4 | clazz: null, 5 | methods: null, 6 | dynamicType: null, 7 | roleId: null, 8 | userKey: null, 9 | remark: null, 10 | sort: 1 11 | } 12 | 13 | export const commonAttr = { 14 | pcTodoUrl: null, 15 | pcFinishUrl: null, 16 | timeout: 0, 17 | sort: 1, 18 | isValid: '1' 19 | } 20 | 21 | export const nodeAttr = { 22 | ...deepClone(commonAttr), 23 | isWaitSibling: '1', 24 | isAutoNext: '1', 25 | rejectType: '0', 26 | isContinue: '0' 27 | } 28 | 29 | export const jobAttr = { 30 | jobName: '任务名称', 31 | userId: null, 32 | roleId: null, 33 | jobType: '0', 34 | userKey: null, 35 | dynamicType: '0', 36 | distUserKey: null, 37 | distDynType: null, 38 | isNowCall: '0', 39 | isNowRun: '0', 40 | timeout: 0, 41 | sort: 1, 42 | isSkip: '0', 43 | isValid: '1' 44 | } 45 | 46 | export const endAttr = { 47 | ...deepClone(commonAttr), 48 | isAutoEnd: '0' 49 | } 50 | 51 | export const virtualAttr = { 52 | ...deepClone(commonAttr), 53 | rejectType: '1' 54 | } 55 | 56 | export const highAttr = { 57 | childFlowKey: null, 58 | childOrderId: null 59 | } 60 | 61 | export const linkAttr = { 62 | varKey: null 63 | ,varVal: null 64 | ,valType: '1' 65 | ,operator: '1' 66 | ,operatorType: '0' 67 | ,isValid: '1' 68 | } 69 | 70 | export const laneAttr = { 71 | isValid: '1' 72 | } 73 | -------------------------------------------------------------------------------- /src/api/admin/index.js: -------------------------------------------------------------------------------- 1 | import request from '@/router/axios' 2 | import {setStore} from '@/utils/store' 3 | import qs from 'qs' 4 | import * as CryptoJS from "crypto-js"; 5 | 6 | export const loginByUsername = (username, password, code, randomStr) => { 7 | let grant_type = 'password' 8 | let dataObj = qs.stringify({'username': username, 'password': password}) 9 | let basicAuth = 'Basic ' + window.btoa('test:test') 10 | 11 | setStore({ 12 | name: 'basicAuth', 13 | content: basicAuth, 14 | type: 'session' 15 | }) 16 | 17 | return request({ 18 | url: '/auth/oauth/token', 19 | headers: { 20 | isToken: false, 21 | 'TENANT-ID': '1', 22 | 'Authorization': basicAuth 23 | }, 24 | method: 'post', 25 | params: {randomStr, code, grant_type}, 26 | data: dataObj 27 | }) 28 | } 29 | 30 | /** 31 | *加密处理 32 | */ 33 | export const encryption = params => { 34 | let { data, type, param, key } = params; 35 | const result = JSON.parse(JSON.stringify(data)); 36 | if (type === "Base64") { 37 | param.forEach(ele => { 38 | result[ele] = btoa(result[ele]); 39 | }); 40 | } else { 41 | param.forEach(ele => { 42 | var data = result[ele]; 43 | key = CryptoJS.enc.Latin1.parse(key); 44 | var iv = key; 45 | // 加密 46 | var encrypted = CryptoJS.AES.encrypt(data, key, { 47 | iv: iv, 48 | mode: CryptoJS.mode.CFB, 49 | padding: CryptoJS.pad.NoPadding 50 | }); 51 | result[ele] = encrypted.toString(); 52 | }); 53 | } 54 | return result; 55 | }; 56 | -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | JsonFlow工作流引擎 16 | 17 | 18 | 19 | 23 |
24 |
25 |
26 | loading 27 |
28 | 正在加载资源 29 |
30 |
31 | JsonFlow加载中,请稍后... 32 |
33 |
34 | 38 |
39 |
40 | 41 | 42 | -------------------------------------------------------------------------------- /src/components/node-menu.vue: -------------------------------------------------------------------------------- 1 | 19 | 51 | 59 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "JsonFlow", 3 | "version": "1.0.0", 4 | "author": "766488893@qq.com", 5 | "private": true, 6 | "scripts": { 7 | "dev": "vue-cli-service serve", 8 | "serve": "vue-cli-service serve", 9 | "build": "vue-cli-service build", 10 | "lint": "vue-cli-service lint" 11 | }, 12 | "dependencies": { 13 | "element-ui": "^2.15.7", 14 | "canvas": "^2.6.0", 15 | "canvg": "^2.0.0", 16 | "html2canvas": "^1.0.0-rc.3", 17 | "jsdom": "^13.2.0", 18 | "jsplumb": "^2.11.0", 19 | "resizable-dom": "^1.0.2", 20 | "vue-contextmenu": "^1.5.9", 21 | "vue-json-viewer": "^2.2.1", 22 | "xmldom": "^0.1.27", 23 | "axios": "^0.20.0", 24 | "codemirror": "^6.0.0", 25 | "core-js": "^3.6.5", 26 | "less": "^3.12.2", 27 | "less-loader": "^7.0.1", 28 | "vue": "^2.6.11", 29 | "vue-router": "^3.4.3", 30 | "vuex": "^3.5.1", 31 | "crypto-js": "^3.1.9-1" 32 | }, 33 | "devDependencies": { 34 | "@vue/cli-service": "~4.5.0", 35 | "babel-helper-vue-jsx-merge-props": "^2.0.3", 36 | "babel-plugin-syntax-jsx": "^6.18.0", 37 | "babel-plugin-transform-vue-jsx": "^3.7.0", 38 | "babel-preset-env": "^1.7.0", 39 | "nprogress": "^0.2.0" 40 | }, 41 | "eslintConfig": { 42 | "root": true, 43 | "env": { 44 | "node": true 45 | }, 46 | "extends": [ 47 | "plugin:vue/essential", 48 | "eslint:recommended" 49 | ], 50 | "parserOptions": { 51 | "parser": "babel-eslint" 52 | }, 53 | "rules": {} 54 | }, 55 | "browserslist": [ 56 | "> 1%", 57 | "last 2 versions", 58 | "not dead" 59 | ] 60 | } 61 | -------------------------------------------------------------------------------- /src/utils/dict-prop.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 字典属性常量 3 | * @author luolin 4 | */ 5 | export let DIC_PROP = { 6 | isNeedService: false, 7 | YES_OR_NO: [{ 8 | label: '否', 9 | value: '0' 10 | }, { 11 | label: '是', 12 | value: '1' 13 | }], 14 | JOB_TYPE: [{ 15 | label: '个人', 16 | value: '0' 17 | }, { 18 | label: '角色', 19 | value: '1' 20 | }, { 21 | label: '或签', 22 | value: '2' 23 | }], 24 | NODE_DEGREE: [{ 25 | label: '出度', 26 | value: '0' 27 | }, { 28 | label: '入度', 29 | value: '1' 30 | }], 31 | REJECT_TYPE: [{ 32 | label: '依次返回', 33 | value: '0' 34 | }, { 35 | label: '直接返回', 36 | value: '1' 37 | }], 38 | OPERATOR: [{ 39 | label: '小于', 40 | value: '0' 41 | }, { 42 | label: '等于', 43 | value: '1' 44 | }, { 45 | label: '大于', 46 | value: '2' 47 | }], 48 | OPERATOR_TYPE: [{ 49 | label: '并且', 50 | value: '0' 51 | }, { 52 | label: '或者', 53 | value: '1' 54 | }], 55 | VAL_TYPE: [{ 56 | label: '数字', 57 | value: '0' 58 | }, { 59 | label: '字符串', 60 | value: '1' 61 | }, { 62 | label: 'SPEL', 63 | value: '2' 64 | }], 65 | DYNAMIC_TYPE: [{ 66 | label: '正常分配', 67 | value: '0' 68 | }, { 69 | label: '动态分配', 70 | value: '1' 71 | }, { 72 | label: '动态计算', 73 | value: '2' 74 | }], 75 | METHODS: [{ 76 | label: '启动节点时拦截start', 77 | value: 'start' 78 | }, { 79 | label: '审批节点前拦截before', 80 | value: 'before' 81 | }, { 82 | label: '节点结束拦截complete', 83 | value: 'complete' 84 | }, { 85 | label: '审批节点后拦截after', 86 | value: 'after' 87 | }, { 88 | label: '驳回后拦截reject', 89 | value: 'reject' 90 | }, { 91 | label: '被驳回后拦截rejected', 92 | value: 'rejected' 93 | }] 94 | } 95 | -------------------------------------------------------------------------------- /src/views/shortcut.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 74 | 75 | 77 | -------------------------------------------------------------------------------- /src/views/json-view.vue: -------------------------------------------------------------------------------- 1 | 28 | 29 | 71 | 72 | 74 | -------------------------------------------------------------------------------- /src/assets/style/flow-designer.less: -------------------------------------------------------------------------------- 1 | @primary-color: #0960bd; 2 | 3 | .container { 4 | height: 100%; 5 | -moz-user-select: none; 6 | -webkit-user-select: none; 7 | -ms-user-select: none; 8 | -o-user-select: none; 9 | user-select: none; 10 | } 11 | 12 | .select-area { 13 | background-color: #001529; 14 | position: relative; 15 | z-index: 1001; 16 | box-shadow: 3px 0 10px #999; 17 | width: 54px!important; 18 | .tab { 19 | text-align: center; 20 | margin-bottom: 10px; 21 | font-size: 12px; 22 | height: 26px; 23 | line-height: 24px; 24 | color: #fff; 25 | margin-top: 7px; 26 | } 27 | /*左侧菜单栏*/ 28 | .el-row { 29 | padding-bottom: 10px!important; 30 | } 31 | } 32 | 33 | .header-option { 34 | background: #fff; 35 | height: 46px!important; 36 | line-height: 36px; 37 | padding: 0 10px!important; 38 | display: flex; 39 | justify-content: space-between; 40 | align-items: center; 41 | box-shadow: 0 3px 5px #ddd; 42 | position: relative; 43 | z-index: 1000; 44 | &__tools { 45 | span { 46 | margin-right: 6px; 47 | } 48 | } 49 | 50 | /*拖拽/连线按钮*/ 51 | .el-button--small, .el-button--small.is-round { 52 | padding: 8px 8px!important; 53 | border-radius: 16px!important; 54 | } 55 | } 56 | 57 | .header-option-button { 58 | border: 0!important; 59 | padding: 8px 8px!important; 60 | } 61 | 62 | .content { 63 | background: #fafafa; 64 | height: 100%; 65 | border: 1px dashed rgba(170, 170, 170, 0.7); 66 | padding: 0!important; 67 | } 68 | 69 | .tag { 70 | margin: 6px; 71 | } 72 | 73 | .node-item { 74 | height: 32px; 75 | width: 32px; 76 | color: #fff; 77 | border-radius: 5px; 78 | line-height: 32px; 79 | text-align: left; 80 | cursor: move; 81 | display: flex; 82 | align-items: center; 83 | justify-content: center; 84 | 85 | &:hover { 86 | color: @primary-color; 87 | outline: 1px dashed @primary-color; 88 | } 89 | } 90 | 91 | .link-label { 92 | background-color: white; 93 | padding: 1px; 94 | border: 1px solid #346789; 95 | border-radius: 5px; 96 | opacity: 0.8; 97 | z-index: 3; 98 | } 99 | 100 | .flow-drawer { 101 | /*抽屉内容靠上*/ 102 | .el-drawer__header{ 103 | margin-bottom: 0!important; 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/assets/style/flow-area.less: -------------------------------------------------------------------------------- 1 | @active-color: #409EFF; 2 | 3 | .btn-wrapper-simple { 4 | height: 24px !important; 5 | line-height: 24px !important; 6 | } 7 | 8 | .vue-contextmenu-listWrapper { 9 | padding-left: 1px !important; 10 | border-radius: 20px !important; 11 | background: black !important; 12 | color: white !important; 13 | .context-menu-list { 14 | margin: 3px 10px !important; 15 | background: black !important; 16 | } 17 | } 18 | 19 | .child-ul-wrapper { 20 | padding-left: 1px !important; 21 | } 22 | 23 | .flow-container { 24 | width: 3000px; 25 | height: 3000px; 26 | position: relative; 27 | transition: transform 0.5s ease 0s, transform-origin 0.5s ease 0s; 28 | 29 | &.grid { 30 | background: url(~@/assets/images/grid-bg.jpg) repeat left top; 31 | background-size: 60px 60px; 32 | } 33 | 34 | &.canScale { 35 | cursor: url(~@/assets/images/search.png), default; 36 | } 37 | 38 | &.canDrag { 39 | cursor: grab; 40 | } 41 | 42 | &.canMultiple { 43 | cursor: url(~@/assets/images/multip-pointer.png), default; 44 | } 45 | } 46 | 47 | .rectangle-multiple { 48 | position: absolute; 49 | border: 1px dashed #31676f; 50 | } 51 | 52 | .flow-container-active { 53 | background-color: #e4e4e4; 54 | cursor: crosshair; 55 | } 56 | 57 | .auxiliary-line-x { 58 | position: absolute; 59 | border: 0.5px solid @active-color; 60 | width: 100%; 61 | z-index: 9999; 62 | } 63 | 64 | .auxiliary-line-y { 65 | position: absolute; 66 | border: 0.5px solid @active-color; 67 | height: 100%; 68 | z-index: 9999; 69 | } 70 | 71 | .link-active { 72 | outline: 2px dashed @active-color; 73 | } 74 | 75 | .container-scale { 76 | position: absolute; 77 | top: 10px; 78 | right: 10px; 79 | 80 | > span { 81 | display: inline-block; 82 | width: 40px; 83 | text-align: center; 84 | font-size: 14px; 85 | color: rgba(0, 0, 0, .65); 86 | } 87 | 88 | /*缩放按钮*/ 89 | .el-button--small.is-circle { 90 | padding: 5px 5px!important; 91 | } 92 | } 93 | 94 | /*自适应底部*/ 95 | .mouse-position { 96 | position: fixed; 97 | bottom: 0; 98 | right: 10px; 99 | } 100 | 101 | .common-remarks { 102 | width: 100px; 103 | height: 150px; 104 | position: absolute; 105 | background-color: #ffffaa; 106 | } 107 | -------------------------------------------------------------------------------- /src/router/axios.js: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | import {serialize} from '@/utils/common' 3 | import {getStore} from '@/utils/store' 4 | import NProgress from 'nprogress' // progress bar 5 | import errorCode from '@/router/errorCode' 6 | import {Message, MessageBox} from 'element-ui' 7 | import 'nprogress/nprogress.css' 8 | import qs from 'qs' 9 | 10 | axios.defaults.timeout = 30000 11 | // 返回其他状态吗 12 | axios.defaults.validateStatus = function (status) { 13 | return status >= 200 && status <= 500 // 默认的 14 | } 15 | 16 | // 跨域请求,允许保存cookie 17 | axios.defaults.withCredentials = true 18 | NProgress.configure({ 19 | showSpinner: false 20 | }) 21 | 22 | // HTTP request拦截 23 | axios.interceptors.request.use(config => { 24 | NProgress.start() 25 | const TENANT_ID = '1' 26 | const isToken = (config.headers || {}).isToken === false 27 | const token = getStore({name: 'access_token'}) 28 | if (token && !isToken) { 29 | config.headers['Authorization'] = 'Bearer ' + token// token 30 | } 31 | if (TENANT_ID) { 32 | config.headers['TENANT-ID'] = TENANT_ID // 租户ID 33 | } 34 | 35 | // 开启序列化 36 | if (config.method === 'post' && config.headers.serialize) { 37 | config.data = serialize(config.data) 38 | delete config.data.serialize 39 | } 40 | 41 | if (config.method === 'get') { 42 | config.paramsSerializer = function (params) { 43 | return qs.stringify(params, {arrayFormat: 'repeat'}) 44 | } 45 | } 46 | 47 | return config 48 | }, error => { 49 | return Promise.reject(error) 50 | }) 51 | 52 | // HTT Presponse拦截 53 | axios.interceptors.response.use(res => { 54 | NProgress.done() 55 | const status = Number(res.status) || 200 56 | const message = res.data.msg || errorCode[status] || errorCode['default'] 57 | 58 | // 后台定义 424 针对令牌过去的特殊响应码 59 | if (status === 424) { 60 | MessageBox.confirm('令牌状态已过期,请点击重新登录', '系统提示', { 61 | confirmButtonText: '重新登录', 62 | cancelButtonText: '取消', 63 | type: 'warning' 64 | } 65 | ).then(() => { 66 | // 刷新登录页面,避免多次弹框 67 | window.location.reload() 68 | }).catch(() => { 69 | }); 70 | return 71 | } 72 | 73 | if (status !== 200 || res.data.code === 1) { 74 | Message({ 75 | message: message, 76 | type: 'error' 77 | }) 78 | return Promise.reject(new Error(message)) 79 | } 80 | 81 | return res 82 | }, error => { 83 | // 处理 503 网络异常 84 | if (error.response.status === 503) { 85 | Message({ 86 | message: error.response.data.msg, 87 | type: 'error' 88 | }) 89 | } 90 | NProgress.done() 91 | return Promise.reject(new Error(error)) 92 | }) 93 | 94 | export default axios 95 | -------------------------------------------------------------------------------- /src/config/node-config.js: -------------------------------------------------------------------------------- 1 | import { ToolsType, CommonNodeType, HighNodeType, LaneNodeType } from './type' 2 | import { 3 | clazzAttr, 4 | commonAttr, 5 | endAttr, 6 | highAttr, 7 | jobAttr, 8 | laneAttr, 9 | linkAttr, 10 | nodeAttr, 11 | virtualAttr 12 | } from "./attr-config"; 13 | import {deepClone} from "@/utils/common"; 14 | 15 | export const tools = [ 16 | { 17 | type: ToolsType.DRAG, 18 | icon: 'el-icon-rank', 19 | name: '拖拽' 20 | }, 21 | { 22 | type: ToolsType.CONNECTION, 23 | icon: 'el-icon-share', 24 | name: '连线' 25 | }, 26 | { 27 | type: ToolsType.FLOW, 28 | icon: 'el-icon-loading', 29 | name: '设置流程属性' 30 | } 31 | ]; 32 | 33 | export const commonNodes = [ 34 | { 35 | type: CommonNodeType.START, 36 | nodeName: '开始', 37 | icon: 'video-play', 38 | attrs: deepClone(commonAttr), 39 | clazz: deepClone(clazzAttr), 40 | defJob: deepClone(jobAttr), 41 | jobSize: 1 42 | }, 43 | { 44 | type: CommonNodeType.SERIAL, 45 | nodeName: '串行节点', 46 | icon: 'right', 47 | attrs: deepClone(nodeAttr), 48 | clazz: deepClone(clazzAttr), 49 | defJob: deepClone(jobAttr), 50 | jobSize: 1, 51 | status: null 52 | }, 53 | { 54 | type: CommonNodeType.PARALLEL, 55 | nodeName: '并行节点', 56 | icon: 'sort', 57 | attrs: deepClone(nodeAttr), 58 | clazz: deepClone(clazzAttr), 59 | defJob: deepClone(jobAttr), 60 | jobSize: 1, 61 | status: null 62 | }, 63 | { 64 | type: CommonNodeType.END, 65 | nodeName: '结束', 66 | icon: 'remove-outline', 67 | attrs: deepClone(endAttr), 68 | clazz: deepClone(clazzAttr), 69 | defJob: deepClone(jobAttr), 70 | jobSize: 1 71 | }, 72 | { 73 | type: CommonNodeType.LINK, 74 | label: '连线', 75 | sourceId: null, 76 | targetId: null, 77 | attrs: deepClone(linkAttr) 78 | } 79 | ]; 80 | 81 | export const highNodes = [ 82 | { 83 | type: HighNodeType.VIRTUAL, 84 | nodeName: '虚拟节点', 85 | icon: 'refresh', 86 | attrs: deepClone(virtualAttr), 87 | clazz: deepClone(clazzAttr), 88 | defJob: deepClone(jobAttr), 89 | jobSize: 1 90 | }, 91 | { 92 | type: HighNodeType.JOB, 93 | nodeName: '节点任务', 94 | icon: 's-check', 95 | defJob: deepClone(jobAttr), 96 | status: null 97 | }, 98 | { 99 | type: HighNodeType.CHILD_FLOW, 100 | nodeName: '子流程', 101 | icon: 'set-up', 102 | attrs: deepClone(highAttr) 103 | } 104 | ]; 105 | 106 | export const laneNodes = [ 107 | { 108 | type: LaneNodeType.X_LANE, 109 | nodeName: '横向泳道', 110 | icon: 'tickets', 111 | attrs: deepClone(laneAttr) 112 | }, 113 | { 114 | type: LaneNodeType.Y_LANE, 115 | nodeName: '纵向泳道', 116 | icon: 'c-scale-to-original', 117 | attrs: deepClone(laneAttr) 118 | } 119 | ]; 120 | -------------------------------------------------------------------------------- /src/utils/store.js: -------------------------------------------------------------------------------- 1 | import {validatenull} from '@/utils/common' 2 | const keyName = 'jsonflow' + '-' 3 | /** 4 | * 存储localStorage 5 | */ 6 | export const setStore = (params = {}) => { 7 | let { 8 | name, 9 | content, 10 | type 11 | } = params 12 | name = keyName + name 13 | const obj = { 14 | dataType: typeof (content), 15 | content: content, 16 | type: type, 17 | datetime: new Date().getTime() 18 | } 19 | 20 | if (type) { 21 | window.sessionStorage.setItem(name, JSON.stringify(obj)) 22 | } else { 23 | window.localStorage.setItem(name, JSON.stringify(obj)) 24 | } 25 | } 26 | /** 27 | * 获取localStorage 28 | */ 29 | 30 | export const getStore = (params = {}) => { 31 | let { 32 | name, 33 | debug 34 | } = params 35 | name = keyName + name 36 | let obj = {} 37 | let content 38 | obj = window.sessionStorage.getItem(name) 39 | if (validatenull(obj)) obj = window.localStorage.getItem(name) 40 | if (validatenull(obj)) return 41 | try { 42 | obj = JSON.parse(obj) 43 | } catch (e) { 44 | return obj 45 | } 46 | if (debug) { 47 | return obj 48 | } 49 | if (obj.dataType === 'string') { 50 | content = obj.content 51 | } else if (obj.dataType === 'number') { 52 | content = Number(obj.content) 53 | } else if (obj.dataType === 'boolean') { 54 | content = eval(obj.content) 55 | } else if (obj.dataType === 'object') { 56 | content = obj.content 57 | } 58 | return content 59 | } 60 | /** 61 | * 删除localStorage 62 | */ 63 | export const removeStore = (params = {}) => { 64 | let { 65 | name, 66 | type 67 | } = params 68 | name = keyName + name 69 | if (type) { 70 | window.sessionStorage.removeItem(name) 71 | } else { 72 | window.localStorage.removeItem(name) 73 | } 74 | } 75 | 76 | /** 77 | * 获取全部localStorage 78 | */ 79 | export const getAllStore = (params = {}) => { 80 | const list = [] 81 | const { 82 | type 83 | } = params 84 | if (type) { 85 | for (let i = 0; i <= window.sessionStorage.length; i++) { 86 | list.push({ 87 | name: window.sessionStorage.key(i), 88 | content: getStore({ 89 | name: window.sessionStorage.key(i), 90 | type: 'session' 91 | }) 92 | }) 93 | } 94 | } else { 95 | for (let i = 0; i <= window.localStorage.length; i++) { 96 | list.push({ 97 | name: window.localStorage.key(i), 98 | content: getStore({ 99 | name: window.localStorage.key(i) 100 | }) 101 | }) 102 | } 103 | } 104 | return list 105 | } 106 | 107 | /** 108 | * 清空全部localStorage 109 | */ 110 | export const clearStore = (params = {}) => { 111 | const {type} = params 112 | if (type) { 113 | window.sessionStorage.clear() 114 | } else { 115 | window.localStorage.clear() 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/assets/style/flow-node.less: -------------------------------------------------------------------------------- 1 | @common-node-bg: white; 2 | @common-node-bg-hover: #f4f6fcb0; 3 | @common-node-active: #409EFF; 4 | 5 | .common-circle-node { 6 | position: absolute; 7 | height: 30px; 8 | width: 80px; 9 | line-height: 30px; 10 | text-align: center; 11 | border: 1px solid #777; 12 | border-radius: 30px; 13 | background-color: @common-node-bg; 14 | white-space: nowrap; 15 | font-size: 10px; 16 | 17 | &:hover { 18 | background-color: @common-node-bg-hover; 19 | z-index: 2; 20 | } 21 | 22 | &.active { 23 | outline: 2px dashed @common-node-active; 24 | outline-offset: 0px; 25 | } 26 | 27 | &.isStart { 28 | background-color: #2c2c2c; 29 | color: #fff; 30 | } 31 | 32 | &.isEnd { 33 | background-color: #6e6e6e; 34 | color: #fff; 35 | } 36 | } 37 | 38 | .common-rectangle-node { 39 | position: absolute; 40 | height: 34px; 41 | width: 140px; 42 | line-height: 34px; 43 | text-align: center; 44 | border: 1px solid #409EFF; 45 | border-radius: 20px; 46 | background-color: @common-node-bg; 47 | white-space: nowrap; 48 | font-size: 10px; 49 | 50 | &:hover { 51 | background-color: @common-node-bg-hover; 52 | z-index: 2; 53 | } 54 | 55 | &.active { 56 | outline: 2px dashed @common-node-active; 57 | outline-offset: 0px; 58 | } 59 | } 60 | 61 | .common-job-node { 62 | position: absolute; 63 | height: 34px; 64 | width: 120px; 65 | line-height: 34px; 66 | text-align: center; 67 | border: 1px solid #409EFF; 68 | border-radius: 20px; 69 | background-color: @common-node-bg; 70 | white-space: nowrap; 71 | font-size: 10px; 72 | 73 | &:hover { 74 | background-color: @common-node-bg-hover; 75 | z-index: 2; 76 | } 77 | 78 | &.active { 79 | outline: 2px dashed @common-node-active; 80 | outline-offset: 0px; 81 | } 82 | } 83 | 84 | .common-x-lane-node { 85 | position: absolute; 86 | text-align: center; 87 | border: 1px solid #777; 88 | border-radius: 2px; 89 | z-index: -1; 90 | 91 | &.active { 92 | outline: 2px dashed @common-node-active; 93 | outline-offset: 0px; 94 | } 95 | 96 | .lane-text-div { 97 | width: 18px; 98 | height: 100%; 99 | position: absolute; 100 | display: table; 101 | border-right: 1px solid #777; 102 | background-color: @common-node-bg; 103 | 104 | &:hover { 105 | z-index: 2; 106 | } 107 | 108 | .lane-text { 109 | word-wrap: break-word; 110 | display: table-cell; 111 | vertical-align: middle; 112 | font-size: 0.8em; 113 | } 114 | } 115 | } 116 | 117 | .common-y-lane-node { 118 | position: absolute; 119 | text-align: center; 120 | border: 1px solid #777; 121 | border-radius: 2px; 122 | z-index: -1; 123 | 124 | &.active { 125 | outline: 2px dashed @common-node-active; 126 | outline-offset: 0px; 127 | } 128 | 129 | .lane-text-div { 130 | width: 100%; 131 | height: 18px; 132 | position: absolute; 133 | display: table; 134 | border-bottom: 1px solid #777; 135 | background-color: @common-node-bg; 136 | 137 | &:hover { 138 | z-index: 2; 139 | } 140 | 141 | .lane-text { 142 | word-wrap: break-word; 143 | display: table-cell; 144 | font-size: 0.8em; 145 | } 146 | } 147 | } 148 | 149 | .node-icon { 150 | position: absolute; 151 | height: 18px; 152 | top: 8px; 153 | left: 8px; 154 | } 155 | 156 | .start-icon { 157 | position: absolute; 158 | height: 15px; 159 | top: 7px; 160 | left: 8px; 161 | } 162 | 163 | .arrow-icon { 164 | position: absolute; 165 | top: 9px; 166 | left: 60px; 167 | } 168 | -------------------------------------------------------------------------------- /src/views/user-login.vue: -------------------------------------------------------------------------------- 1 | 55 | 56 | 128 | 129 | 162 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![08](public/favicon.ico) 2 | 3 | [![star](https://gitee.com/jackrolling/json-flow-ui/badge/star.svg?theme=dark)](https://gitee.com/jackrolling/json-flow-ui/stargazers) [![fork](https://gitee.com/jackrolling/json-flow-ui/badge/fork.svg?theme=dark)](https://gitee.com/jackrolling/json-flow-ui/members) 4 | 5 | ### 如果觉得不错,给作者一个⭐️小星星⭐️Star⭐️支持下️吧 6 | 7 | | 欢迎进群一起交流 | 8 | |:-------------------------------------------------:| 9 | | 🔥大家可扫码加入交流群,群已超200+人被限制了。可以加我微信 ll766488893 拉你入群 | 10 | 11 | #### 开源仅一个前期的流程设计器([借鉴vue-flow-design-plus,将Ant Design改为ElementUI并重构](https://gitee.com/zhangyeping/vue-flow-design-plus)),与商业版本(完全自研)技术栈不同! 12 | 13 | ### JsonFlow在线演示(请联系作者微信ll766488893) 14 | [点击预览](http://47.109.57.40/)(JsonFlow SpringBoot单体版本) 【备注:微服务版本整合PIGX】 15 | 16 | ### JsonFlow在线文档(VIP文档暂未开源-请联系作者) 17 | [点击预览](https://flow.pig4cloud.com/) 18 | 19 | #### 🎉🎉🎉JsonFlow系统:创新中式工作流引擎,解锁工作效率新境界 20 | 21 | ## 简要概述 22 | 23 | **🔥🔥已集成人工智能AI,支持AI助手发起工单,使工作流更智能(`需单独采购AI`)** 24 | 25 | 🔥🔥支持`Vue3技术栈`,一套代码同时支持 **`钉钉简单模式与专业模式`**,并提供`两套可选UI界面`,同时适配 **`PC、Pad、H5`** 多端显示,支持`移动端APP办公`(`含流程图回显`) 26 | 27 | 🔥🔥支持表结构设计、表单字段定义、表单设计、流程设计、打印模板设计等设计功能,表单设计器支持 **`自定义组件`** 即接入 **`本地Vue业务组件`**,接入非常简单高效 28 | 29 | ## 全网Top级功能🔥自研国产分布式流程引擎(`非开源BPM`) 30 | - 1、🔥🔥支持`前端流程设计器`设计流程图,同时也支持 **`后端代码`** 生成流程图,参与者支持`用户、角色、岗位、部门`等任意扩展 31 | - 4、🔥🔥支持对已发起的 **`流程图实例`** 与 **`表单实例`** 可再次可视化`单独编辑配置`,非常方便后期维护如`加减节点`(`在图中自动布局回显`)、`加减参与者`即加减签、更改页面布局等 32 | - 2、🔥🔥支持 **`全流程预测`** ,可根据表单数据与流程配置信息预测,包括`流转轨迹预测、节点参与者预测、连线条件预测、审批过程预测、监听事件预测、子流程预测`等 33 | - 3、🔥🔥支持 **`节点路由`** ,可根据接口返回值 或 表单数据的字段值,配置对应的路由动作(可自定义随意扩展)。如`暂停当前节点、开启下一节点、通过、驳回到节点、退回上一步、终止流程`等 34 | - 5、🔥🔥支持众多中国式特色审批操作,支持`达梦`等国产数据库,`自研报表设计器`支持`自定义报表格式`导出,流程图支持节点与任务分离自由`切换UI`显示效果 35 | - 6、🔥🔥流程引擎与业务表单系统`完全解耦`,支持配置`第三方系统`的Http接口或表单URL地址,支持 **`第三方系统`** 调用操作流程的所有接口 36 | - 7、🔥🔥支持 `Online 工作交接`,一键交接工作与任务,轻松搞定人员变动 37 | 38 | ### 🔥🔥🔥JsonFlow商业版Pro强大功能🔥🔥🔥 39 | #### 注:商业版流程设计器全新自研更简单强大,核心与开源版不同 40 | 41 | - 基于本系统开发的工作流系统已在生产上平稳运行良久,生产已验证 42 | 43 | - 商业授权与协议范围说明 - PIGX官方授权声明地址[点击预览](https://pig4cloud.com/data/doc/info/auth-intro.html) 44 | - 请点击查看商业版Pro强大功能详情[点击预览](https://flow.pig4cloud.com/home/function/) 45 | - 请点击查看对比传统BPM详情[点击预览](https://flow.pig4cloud.com/home/compare/) 46 | 47 | #### 系统介绍 48 | 49 | - 🎉🎉 JsonFlow工作流是一个`100%纯国产`自研的`分布式`流程引擎系统(`非开源BPM`),内置自动布局最优算法布局,前后端纯JSON交互格式简单,支持`信创国产化`专为中国式特色审批而生。支持在线零代码进行表单设计与流程设计,`前后端代码完全自研可控,非常容易进行二次开发`,致力于解决传统BPM流程复杂难扩展、后期维护闹心、数据统计烦心等痛点问题 50 | 51 | - 🔥🔥 此系统从 2019 开始发布生产环境使用,不断优化趋于稳定,于 2022 年正式发布商业版!经过作者不断迭代和优化,孵化出了简单易用易扩展稳定强大的国产工作流系统。 52 | 53 | #### 操作介绍 54 | - JsonFlow工作流支持表结构设计、表单字段定义、表单设计、流程设计、打印模板设计等操作,配置简单高效易理解,几分钟即可完成 55 | 56 | #### 钉钉UI界面⭐️-常用于普通用户操作 57 | ![01](public/usages/img_1.png) 58 | 59 | #### 专业UI界面⭐-常用于专业用户操作️ 60 | - 流程图设计:拖拽节点到绘图区,进行任意连线。可自由设置连线/路由/布局`改变连线形式`、画布内的节点/连线均`可拖拽和调整大小`,未来可自定义图形 61 | 62 | ![01](public/usages/img.png) 63 | 64 | - 查看流程图:流程图节点与连线根据状态显示不同颜色和图标,鼠标移动到节点可显示节点与审批信息,放到线上可显示条件信息(注:显示信息可自行调整) 65 | 66 | ![04](public/usages/04.png) 67 | 68 | #### 在线设计 69 | - 模型设计分三个步骤`表单设置、表单设计、流程设计`,一次托拉拽即可完成表结构设计、表单字段定义、表单设计、流程设计等设计功能。流程设计的`流程图`和表单设计的`表单`均支持`PC、Pad、H5(即APP端)`,一次设计多端显示 70 | 71 | ![04](public/flow/img_1.png) 72 | 73 | #### 在线办公 74 | ##### 发起审批 75 | - 支持在线一键发起办公申请工单、办理任务、WebSocket任务消息通知等 76 | 77 | ![05](public/usages/05.png) 78 | 79 | ##### 审批管理 80 | - 在待我审批菜单中点审批,可以查看/修改表单信息、查看审批过程、查看流程图等信息 81 | 82 | ![08](public/flow/08.png) 83 | 84 | #### 流程监控 85 | 86 | ![08](public/flow/60.png) 87 | 88 | - 更多功能期待你体验... 89 | 90 | #### 其他功能: 91 | - 节点分为五种:开始节点、串行节点、并行节点、结束节点、虚拟节点,以及一个节点任务 92 | - 更多功能期待你体验... 93 | 94 | #### 数据结构 95 | ```json 96 | { 97 | "nodeList": [ 98 | { 99 | "type": "start", 100 | "nodeName": "开始", 101 | "icon": null, 102 | "attrs": { 103 | "pcTodoUrl": null, 104 | "pcFinishUrl": null 105 | }, 106 | "clazz": { 107 | "clazz": null, 108 | "methods": null 109 | }, 110 | "defJob": { 111 | "jobName": "任务名称", 112 | "userId": null, 113 | "roleId": null 114 | }, 115 | "jobSize": 1, 116 | "id": "1661145449179000002", 117 | "height": 50, 118 | "x": 210, 119 | "width": 50, 120 | "y": 205 121 | } 122 | ], 123 | "linkList": [ 124 | { 125 | "type": "link", 126 | "label": "", 127 | "sourceId": "1661145449179000002", 128 | "targetId": "1661145452800000003", 129 | "attrs": { 130 | "varKey": null, 131 | "varVal": null 132 | }, 133 | "id": "1661145456855000004", 134 | "icon": null 135 | } 136 | ], 137 | "attrs": { 138 | "id": "1661145437059000001" 139 | }, 140 | "status": "0" 141 | } 142 | ``` 143 | 144 | #### 本地安装 145 | 146 | * 下载本项目:npm install 147 | 148 | * 启动项目:npm run serve 149 | 150 | * 构建项目dist:npm run build 151 | 152 | #### 关于作者&&微信群 153 | - 欢迎大家进群一起交流 🔥🔥🔥 如果群二维码失效了,可以直接加我微信拉群,感谢 154 | 155 | 156 | 157 | 158 | 159 | 160 |
161 | -------------------------------------------------------------------------------- /src/views/setting.vue: -------------------------------------------------------------------------------- 1 | 92 | 93 | 158 | 159 | 172 | -------------------------------------------------------------------------------- /src/utils/common.js: -------------------------------------------------------------------------------- 1 | import { flowConfig } from '@/config/flow-config.js' 2 | 3 | /** 4 | * 判断是否为空 5 | */ 6 | export function validatenull(val) { 7 | if (typeof val === 'boolean') { 8 | return false 9 | } 10 | if (typeof val === 'number') { 11 | return false 12 | } 13 | if (val instanceof Array) { 14 | if (val.length === 0) return true 15 | } else if (val instanceof Object) { 16 | if (JSON.stringify(val) === '{}') return true 17 | } else { 18 | if (val === 'null' || val == null || val === 'undefined' || val === undefined || val === '') return true 19 | return false 20 | } 21 | return false 22 | } 23 | 24 | // 获取浏览器类型 25 | export const getBrowserType = function(_this) { 26 | let userAgent = navigator.userAgent; 27 | let isOpera = userAgent.indexOf("Opera") > -1; 28 | if (isOpera) { 29 | return 1; 30 | } 31 | 32 | if (userAgent.indexOf("Firefox") > -1) { 33 | return 2; 34 | } 35 | if (userAgent.indexOf("Chrome") > -1) { 36 | return 3; 37 | } 38 | if (userAgent.indexOf("Safari") > -1) { 39 | return 4; 40 | } 41 | if ( 42 | userAgent.indexOf("compatible") > -1 && 43 | userAgent.indexOf("MSIE") > -1 && 44 | !isOpera 45 | ) { 46 | _this.$message.error("IE浏览器支持性较差,推荐使用Firefox或Chrome"); 47 | return 5; 48 | } 49 | if (userAgent.indexOf("Trident") > -1) { 50 | _this.$message.error("Edge浏览器支持性较差,推荐使用Firefox或Chrome"); 51 | return 6; 52 | } 53 | }; 54 | 55 | export let utils = { 56 | seqNo: 1, 57 | consoleLog: function(strArr) { 58 | let log = ''; 59 | for (let i = 0, len = strArr.length; i < len; i++) { 60 | log += strArr[i] + '\n'; 61 | } 62 | }, 63 | getId: function() { 64 | let idType = flowConfig.idType; 65 | if (typeof idType == 'string') { 66 | if (idType == 'uuid') { 67 | return this.getUUID(); 68 | } else if (idType == 'time_stamp') { 69 | return this.getTimeStamp(); 70 | } 71 | } else if (idType instanceof Array) { 72 | if (idType[0] == 'sequence') { 73 | return this.getSequence(idType[1]); 74 | } else if (idType[0] == 'time_stamp_and_sequence') { 75 | return this.getTimeStampAndSequence(idType[1]); 76 | } else if (idType[0] == 'custom') { 77 | return idType[1](); 78 | } 79 | } 80 | }, 81 | getUUID: function() { 82 | let s = []; 83 | let hexDigits = "0123456789abcdef"; 84 | for(let i = 0; i < 36; i++) { 85 | s[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1); 86 | } 87 | s[14] = "4"; 88 | s[19] = hexDigits.substr((s[19] & 0x3) | 0x8, 1); 89 | s[8] = s[13] = s[18] = s[23] = "-"; 90 | 91 | let uuid = s.join(""); 92 | return uuid.replace(/-/g, ''); 93 | }, 94 | getTimeStamp: function() { 95 | return new Date().getTime(); 96 | }, 97 | getSequence: function(seqNoLength) { 98 | let zeroStr = new Array(seqNoLength).fill('0').join(''); 99 | return (zeroStr + (this.seqNo++)).slice(-seqNoLength); 100 | }, 101 | getTimeStampAndSequence: function(seqNoLength) { 102 | return this.getTimeStamp() + this.getSequence(seqNoLength); 103 | }, 104 | add: function(a, b) { 105 | let c, d, e; 106 | try { 107 | c = a.toString().split(".")[1].length; 108 | } catch (f) { 109 | c = 0; 110 | } 111 | try { 112 | d = b.toString().split(".")[1].length; 113 | } catch (f) { 114 | d = 0; 115 | } 116 | return e = Math.pow(10, Math.max(c, d)), (this.mul(a, e) + this.mul(b, e)) / e; 117 | }, 118 | sub: function(a, b) { 119 | let c, d, e; 120 | try { 121 | c = a.toString().split(".")[1].length; 122 | } catch (f) { 123 | c = 0; 124 | } 125 | try { 126 | d = b.toString().split(".")[1].length; 127 | } catch (f) { 128 | d = 0; 129 | } 130 | return e = Math.pow(10, Math.max(c, d)), (this.mul(a, e) - this.mul(b, e)) / e; 131 | }, 132 | mul: function(a, b) { 133 | let c = 0, d = a.toString(), e = b.toString(); 134 | try { 135 | c += d.split(".")[1].length; 136 | } catch (f) {} 137 | try { 138 | c += e.split(".")[1].length; 139 | } catch (f) {} 140 | return Number(d.replace(".", "")) * Number(e.replace(".", "")) / Math.pow(10, c); 141 | }, 142 | div: function(a, b) { 143 | let c, d, e = 0, f = 0; 144 | try { 145 | e = a.toString().split(".")[1].length; 146 | } catch (g) {} 147 | try { 148 | f = b.toString().split(".")[1].length; 149 | } catch (g) {} 150 | return c = Number(a.toString().replace(".", "")), d = Number(b.toString().replace(".", "")), this.mul(c / d, Math.pow(10, f - e)); 151 | } 152 | }; 153 | 154 | export const getObjType = obj => { 155 | let toString = Object.prototype.toString; 156 | let map = { 157 | "[object Boolean]": "boolean", 158 | "[object Number]": "number", 159 | "[object String]": "string", 160 | "[object Function]": "function", 161 | "[object Array]": "array", 162 | "[object Date]": "date", 163 | "[object RegExp]": "regExp", 164 | "[object Undefined]": "undefined", 165 | "[object Null]": "null", 166 | "[object Object]": "object" 167 | }; 168 | if (obj instanceof Element) { 169 | return "element"; 170 | } 171 | return map[toString.call(obj)]; 172 | }; 173 | 174 | /** 175 | * 对象深拷贝 176 | */ 177 | export const deepClone = data => { 178 | let type = getObjType(data); 179 | let obj; 180 | if (type === "array") { 181 | obj = []; 182 | } else if (type === "object") { 183 | obj = {}; 184 | } else { 185 | // 不再具有下一层次 186 | return data; 187 | } 188 | if (type === "array") { 189 | for (let i = 0, len = data.length; i < len; i++) { 190 | obj.push(deepClone(data[i])); 191 | } 192 | } else if (type === "object") { 193 | for (let key in data) { 194 | obj[key] = deepClone(data[key]); 195 | } 196 | } 197 | return obj; 198 | }; 199 | 200 | // 表单序列化 201 | export const serialize = data => { 202 | const list = []; 203 | Object.keys(data).forEach(ele => { 204 | list.push(`${ele}=${data[ele]}`); 205 | }); 206 | return list.join("&"); 207 | }; 208 | -------------------------------------------------------------------------------- /src/config/flow-config.js: -------------------------------------------------------------------------------- 1 | export const flowAttr = { 2 | id: null, 3 | flowName: null, 4 | flowKey: null, 5 | photo: null, 6 | isValid: '1', 7 | remark: null, 8 | sort: 1 9 | } 10 | 11 | export let flowConfig = { 12 | // ID的生成类型 13 | // 1.uuid uuid 2.time_stamp 时间戳 3.sequence 序列 4.time_stamp_and_sequence 时间戳加序列 5.custom 自定义 14 | idType: ['time_stamp_and_sequence', 6], 15 | flowData: { 16 | nodeList: [], 17 | linkList: [], 18 | attrs: flowAttr, 19 | status: '0' 20 | }, 21 | flowStatus: { 22 | CREATE: '0', 23 | SAVE: '1', 24 | MODIFY: '2', 25 | LOADING: '3' 26 | }, 27 | gridConfig: { 28 | showGrid: false, 29 | showGridText: "隐藏网格", 30 | showGridIcon: "el-icon-aim" 31 | }, 32 | jsPlumbInsConfig: { 33 | Connector: [ 34 | 'Flowchart', 35 | { 36 | gap: 5, 37 | cornerRadius: 8, 38 | alwaysRespectStubs: true 39 | } 40 | ], 41 | ConnectionOverlays: [ 42 | [ 43 | 'Arrow', 44 | { 45 | width: 6, 46 | length: 6, 47 | location: 1 48 | } 49 | ] 50 | ], 51 | PaintStyle: { 52 | stroke: '#2a2929', 53 | strokeWidth: 1 54 | }, 55 | HoverPaintStyle: { 56 | stroke: '#409EFF', 57 | strokeWidth: 2 58 | }, 59 | EndpointStyle: { 60 | fill: '#456', 61 | stroke: '#2a2929', 62 | strokeWidth: 1, 63 | radius: 2 64 | }, 65 | EndpointHoverStyle: { 66 | fill: 'pink' 67 | } 68 | }, 69 | jsPlumbConfig: { 70 | anchor: { 71 | default: ['Bottom', 'Right', 'Top', 'Left'] 72 | }, 73 | conn: { 74 | isDetachable: false 75 | }, 76 | makeSourceConfig: { 77 | filter: 'a', 78 | filterExclude: true, 79 | maxConnections: -1, 80 | endpoint: ['Dot', { radius: 7 }], 81 | anchor: ['Bottom', 'Right', 'Top', 'Left'] 82 | }, 83 | makeTargetConfig: { 84 | filter: 'a', 85 | filterExclude: true, 86 | maxConnections: -1, 87 | endpoint: ['Dot', { radius: 7 }], 88 | anchor: ['Bottom', 'Right', 'Top', 'Left'] 89 | } 90 | }, 91 | defaultStyle: { 92 | dragOpacity: 0.7, 93 | alignGridPX: [5, 5], 94 | alignSpacing: { 95 | level: 100, 96 | vertical: 100 97 | }, 98 | alignDuration: 300, 99 | containerScale: { 100 | init: 1, 101 | min: 0.5, 102 | max: 3, 103 | onceNarrow: 0.1, 104 | onceEnlarge: 0.1 105 | }, 106 | isOpenAuxiliaryLine: true, 107 | showAuxiliaryLineDistance: 20, 108 | movePx: 5, 109 | photoBlankDistance: 200 110 | }, 111 | shortcut: { 112 | nodeAttr: { 113 | code: 111, 114 | codeName: '双击节点或连线设置属性', 115 | shortcutName: '节点属性配置' 116 | }, 117 | dragTool: { 118 | code: 68, 119 | codeName: 'D(注:可双击画布快速切换)', 120 | shortcutName: '拖拽工具' 121 | }, 122 | connTool: { 123 | code: 76, 124 | codeName: 'L(注:可双击画布快速切换)', 125 | shortcutName: '连线工具' 126 | }, 127 | dragContainer: { 128 | code: 32, 129 | codeName: 'SPACE', 130 | shortcutName: '画布拖拽' 131 | }, 132 | scaleContainer: { 133 | code: 18, 134 | codeName: 'ALT(firefox下为SHIFT)', 135 | shortcutName: '画布缩放' 136 | }, 137 | multiple: { 138 | code: 17, 139 | codeName: 'CTRL', 140 | shortcutName: '多选' 141 | }, 142 | settingModal: { 143 | code: 83, 144 | codeName: 'CTRL+ALT+S', 145 | shortcutName: '打开设置页面' 146 | }, 147 | jsonModal: { 148 | code: 84, 149 | codeName: 'CTRL+ALT+T', 150 | shortcutName: '打开数据页面' 151 | }, 152 | leftMove: { 153 | code: 37, 154 | codeName: '←', 155 | shortcutName: '左移' 156 | }, 157 | upMove: { 158 | code: 38, 159 | codeName: '↑', 160 | shortcutName: '上移' 161 | }, 162 | rightMove: { 163 | code: 39, 164 | codeName: '→', 165 | shortcutName: '右移' 166 | }, 167 | downMove: { 168 | code: 40, 169 | codeName: '↓', 170 | shortcutName: '下移' 171 | }, 172 | zoomInTool: { 173 | code: 190, 174 | codeName: '<', 175 | shortcutName: '放大工具' 176 | }, 177 | zoomOutTool: { 178 | code: 188, 179 | codeName: '>', 180 | shortcutName: '缩小工具' 181 | } 182 | }, 183 | contextMenu: { 184 | container: { 185 | menuName: 'flow-menu', 186 | axis: { 187 | x: null, 188 | y: null 189 | }, 190 | menulists: [ 191 | { 192 | fnHandler: 'flowInfo', 193 | icoName: 'edit', 194 | btnName: '流程图信息' 195 | }, 196 | { 197 | icoName: 'edit', 198 | btnName: '对齐方式', 199 | children: [ 200 | { 201 | icoName: 'edit', 202 | fnHandler: 'verticaLeft', 203 | btnName: '垂直左对齐' 204 | }, 205 | { 206 | icoName: 'edit', 207 | fnHandler: 'verticalCenter', 208 | btnName: '垂直居中' 209 | }, 210 | { 211 | icoName: 'edit', 212 | fnHandler: 'verticalRight', 213 | btnName: '垂直右对齐' 214 | }, 215 | { 216 | icoName: 'edit', 217 | fnHandler: 'levelUp', 218 | btnName: '水平上对齐' 219 | }, 220 | { 221 | icoName: 'edit', 222 | fnHandler: 'levelCenter', 223 | btnName: '水平居中' 224 | }, 225 | { 226 | icoName: 'edit', 227 | fnHandler: 'levelDown', 228 | btnName: '水平下对齐' 229 | } 230 | ] 231 | }, 232 | { 233 | fnHandler: 'selectAll', 234 | icoName: 'edit', 235 | btnName: '全选' 236 | }, 237 | { 238 | fnHandler: 'paste', 239 | icoName: 'edit', 240 | btnName: '粘贴' 241 | }, 242 | { 243 | fnHandler: 'publishFlow', 244 | icoName: 'edit', 245 | btnName: '发布流程' 246 | } 247 | ] 248 | }, 249 | node: { 250 | menuName: 'node-menu', 251 | axis: { 252 | x: null, 253 | y: null 254 | }, 255 | menulists: [ 256 | { 257 | fnHandler: 'setNodeAttr', 258 | icoName: 'el-icon-edit', 259 | btnName: '设置属性' 260 | }, 261 | { 262 | fnHandler: 'copyNode', 263 | icoName: 'el-icon-document-copy', 264 | btnName: '复制节点' 265 | }, 266 | { 267 | fnHandler: 'deleteNode', 268 | icoName: 'el-icon-delete', 269 | btnName: '删除节点' 270 | } 271 | ] 272 | }, 273 | link: { 274 | menuName: 'link-menu', 275 | axis: { 276 | x: null, 277 | y: null 278 | }, 279 | menulists: [ 280 | { 281 | fnHandler: 'setLinkAttr', 282 | icoName: 'el-icon-edit', 283 | btnName: '设置属性' 284 | }, 285 | { 286 | fnHandler: 'deleteLink', 287 | icoName: 'el-icon-delete', 288 | btnName: '删除连线' 289 | } 290 | ] 291 | } 292 | } 293 | } 294 | -------------------------------------------------------------------------------- /src/components/flow-node.vue: -------------------------------------------------------------------------------- 1 | 99 | 100 | 285 | 286 | 289 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [Jack luolin] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /src/components/flow-attr.vue: -------------------------------------------------------------------------------- 1 | 472 | 473 | 620 | 621 | 624 | -------------------------------------------------------------------------------- /src/components/flow-area.vue: -------------------------------------------------------------------------------- 1 | 96 | 97 | 839 | 840 | 843 | -------------------------------------------------------------------------------- /src/views/flow-design.vue: -------------------------------------------------------------------------------- 1 | 196 | 197 | 885 | 886 | 889 | --------------------------------------------------------------------------------