├── 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 |
2 |
3 |
4 |
5 |
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 |
20 | We're sorry but JsonFlow doesn't work properly without JavaScript enabled. Please enable it to
21 | continue.
22 |
23 |
24 |
25 |
26 |
27 |
28 | 正在加载资源
29 |
30 |
31 | JsonFlow加载中,请稍后...
32 |
33 |
34 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/src/components/node-menu.vue:
--------------------------------------------------------------------------------
1 |
2 |
18 |
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 |
2 |
8 |
11 |
17 |
18 |
19 |
23 |
24 |
25 |
26 |
74 |
75 |
77 |
--------------------------------------------------------------------------------
/src/views/json-view.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
10 |
11 |
当前JSON数据:
12 |
17 |
18 |
暂存数据:
19 |
20 |
21 |
22 |
暂存
23 |
加载JSON数据
24 |
25 |
26 |
27 |
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 |
2 |
3 |
11 |
12 |
18 |
19 |
20 |
21 |
22 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | 登录
40 |
41 |
42 |
43 |
44 | Copyright © 2022 {{ info.author }}. All rights reserved.
45 |
JsonFlow {{ info.version }}
46 |
47 |
48 |
49 |
50 | 获取开源版本
51 |
52 |
53 |
54 |
55 |
56 |
128 |
129 |
162 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | 
2 |
3 | [](https://gitee.com/jackrolling/json-flow-ui/stargazers) [](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 | 
58 |
59 | #### 专业UI界面⭐-常用于专业用户操作️
60 | - 流程图设计:拖拽节点到绘图区,进行任意连线。可自由设置连线/路由/布局`改变连线形式`、画布内的节点/连线均`可拖拽和调整大小`,未来可自定义图形
61 |
62 | 
63 |
64 | - 查看流程图:流程图节点与连线根据状态显示不同颜色和图标,鼠标移动到节点可显示节点与审批信息,放到线上可显示条件信息(注:显示信息可自行调整)
65 |
66 | 
67 |
68 | #### 在线设计
69 | - 模型设计分三个步骤`表单设置、表单设计、流程设计`,一次托拉拽即可完成表结构设计、表单字段定义、表单设计、流程设计等设计功能。流程设计的`流程图`和表单设计的`表单`均支持`PC、Pad、H5(即APP端)`,一次设计多端显示
70 |
71 | 
72 |
73 | #### 在线办公
74 | ##### 发起审批
75 | - 支持在线一键发起办公申请工单、办理任务、WebSocket任务消息通知等
76 |
77 | 
78 |
79 | ##### 审批管理
80 | - 在待我审批菜单中点审批,可以查看/修改表单信息、查看审批过程、查看流程图等信息
81 |
82 | 
83 |
84 | #### 流程监控
85 |
86 | 
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 |
2 |
3 |
12 |
13 |
16 |
17 | 画布
18 |
19 |
26 |
27 |
28 |
35 |
36 |
37 | 连线
38 |
39 |
41 | 贝塞尔曲线
42 | 直线
43 | 流程图线
44 | 状态线
45 |
46 |
47 |
48 |
49 |
50 |
51 |
56 |
57 |
58 | 默认样式
59 |
60 |
65 |
66 |
67 |
73 |
74 |
75 |
81 |
82 |
83 |
87 |
88 |
89 |
90 |
91 |
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 |
2 |
18 |
19 |
20 |
{{ node.nodeName }}
21 |
22 |
23 |
{{ node.nodeName }}
24 |
25 |
26 |
39 |
40 | {{ node.nodeName }} {{ node.jobSize ? '(' + node.jobSize + ')' : '' }}
41 |
42 |
43 |
55 |
56 | {{ node.nodeName }}
57 |
58 |
59 |
69 |
74 | {{ node.nodeName }}
75 |
76 |
77 |
78 |
88 |
93 | {{ node.nodeName }}
94 |
95 |
96 |
97 |
98 |
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 |
2 |
4 |
5 |
6 |
7 | 流程属性
8 |
9 |
10 |
11 |
16 |
21 |
22 |
23 | 加载
24 |
25 |
26 |
27 |
28 |
31 |
32 |
33 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 | 节点属性
59 |
60 |
61 | 节点配置
62 | 注:当前节点没有单独配置【节点任务】时,作为节点默认的任务属性配置
63 |
64 | {{ currentSelect.type }}
65 |
66 |
67 |
68 |
69 |
70 |
74 |
75 |
76 |
77 |
79 |
80 |
81 |
86 |
87 |
88 |
89 |
90 |
91 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
118 |
119 |
120 |
121 |
122 |
123 |
129 |
130 |
131 |
132 |
138 |
139 |
140 |
141 |
147 |
148 |
149 |
150 |
156 |
157 |
158 |
159 |
160 |
161 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
185 |
186 |
187 |
188 |
190 |
191 | 节点监听配置
192 |
193 |
194 |
195 |
196 |
197 |
198 |
203 |
204 |
205 |
206 |
207 |
208 |
213 |
214 |
215 |
216 |
217 |
218 |
223 |
224 |
225 |
226 |
227 |
228 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 | 节点任务属性
250 |
251 |
254 |
255 | 注:当前节点单独配置【节点任务】时,则节点上配置的默认任务属性失效
256 |
257 |
258 |
259 |
260 |
261 |
262 |
267 |
268 |
269 |
270 |
271 |
272 |
277 |
278 |
279 |
280 |
281 |
282 |
287 |
288 |
289 |
290 |
291 |
292 |
297 |
298 |
299 |
300 |
301 |
302 |
307 |
308 |
309 |
310 |
311 |
312 |
317 |
318 |
319 |
320 |
321 |
322 |
327 |
328 |
329 |
330 |
331 |
337 |
338 |
339 |
340 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
361 |
362 |
363 |
364 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 | 连线属性
379 |
380 |
381 |
382 | 连线配置
383 |
384 |
385 |
386 |
387 |
388 |
389 |
390 |
391 |
392 |
393 |
394 |
395 | 连线条件配置
396 |
397 |
404 |
409 |
410 |
411 |
412 |
413 |
420 |
425 |
426 |
427 |
428 |
429 |
430 |
435 |
436 |
437 |
438 |
439 |
440 |
445 |
446 |
447 |
448 |
449 |
450 |
455 |
456 |
457 |
458 |
459 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
620 |
621 |
624 |
--------------------------------------------------------------------------------
/src/components/flow-area.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
17 |
65 |
66 |
67 | {{ container.scaleShow }}%
68 |
69 |
70 |
71 | x: {{ mouse.position.x }}, y: {{ mouse.position.y }}
72 |
73 |
86 |
87 |
93 |
94 |
95 |
96 |
97 |
839 |
840 |
843 |
--------------------------------------------------------------------------------
/src/views/flow-design.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 基础
7 |
12 |
13 |
14 | 高级
15 |
20 |
21 |
22 | 泳道
23 |
28 |
29 |
30 |
31 |
125 |
126 |
143 |
144 |
149 |
150 |
151 |
152 |
159 |
168 |
169 |
170 |
171 |
181 |
182 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
885 |
886 |
889 |
--------------------------------------------------------------------------------