├── _config.yml
├── .npmrc
├── babel.config.js
├── public
├── favicon.ico
└── index.html
├── resources
├── sample.png
└── svg.xml
├── .eslintignore
├── .editorconfig
├── src
└── components
│ ├── PipelineHighlightNode.vue
│ ├── PipelineNodeEnd.vue
│ ├── PipelineNodeStart.vue
│ ├── PipelineLine.vue
│ ├── line.js
│ ├── Pipeline.vue
│ ├── PipelineNode.vue
│ └── service.js
├── .gitignore
├── .travis.yml
├── vue.config.js
├── demo
├── main.js
├── App.vue
└── data.js
├── .eslintrc.js
├── LICENSE
├── .github
└── workflows
│ └── npmpublish.yml
├── index.js
├── package.json
├── docs
└── cn.md
├── README.md
└── test
└── service.test.js
/_config.yml:
--------------------------------------------------------------------------------
1 | theme: jekyll-theme-minimal
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | registry=https://registry.npmjs.org
--------------------------------------------------------------------------------
/babel.config.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | presets: [
3 | '@vue/app'
4 | ]
5 | }
6 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinfang134/vue-pipeline/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/resources/sample.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jinfang134/vue-pipeline/HEAD/resources/sample.png
--------------------------------------------------------------------------------
/.eslintignore:
--------------------------------------------------------------------------------
1 | /build/
2 | /config/
3 | /dist/
4 | /test/unit/coverage/
5 | /nodes_modules/
6 | /demo/nodes_modules/
7 |
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | root = true
2 |
3 | [*]
4 | charset = utf-8
5 | indent_style = space
6 | indent_size = 2
7 | end_of_line = lf
8 | insert_final_newline = true
9 | trim_trailing_whitespace = true
10 |
--------------------------------------------------------------------------------
/src/components/PipelineHighlightNode.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | node_modules
3 | /dist
4 |
5 | .eslintcache
6 |
7 | # local env files
8 | .env.local
9 | .env.*.local
10 |
11 | # Log files
12 | npm-debug.log*
13 | yarn-debug.log*
14 | yarn-error.log*
15 |
16 | # Editor directories and files
17 | .idea
18 | .vscode
19 | *.suo
20 | *.ntvs*
21 | *.njsproj
22 | *.sln
23 | *.sw?
24 | yarn.lock
25 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 | language: node_js
3 | node_js:
4 | - 10 # use nodejs v10 LTS
5 | cache: npm
6 | branches:
7 | only:
8 | - master # build master branch only
9 | script:
10 | - npm run build # generate static files
11 | deploy:
12 | provider: pages
13 | skip-cleanup: true
14 | github-token: $GH_TOKEN
15 | keep-history: true
16 | on:
17 | branch: master
18 | local-dir: dist
--------------------------------------------------------------------------------
/vue.config.js:
--------------------------------------------------------------------------------
1 | // vue.config.js
2 | module.exports = {
3 | publicPath: process.env.NODE_ENV === 'production'
4 | ? '/vue-pipeline/'
5 | : '/',
6 | configureWebpack: {
7 | output: {
8 | path: __dirname + '/dist'
9 | },
10 | resolve: {
11 | alias: {
12 | '@': __dirname + '/demo'
13 | }
14 | },
15 | entry: {
16 | app: './demo/main.js'
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/demo/main.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import App from './App.vue'
3 | import VuePipeline from '../index.js'
4 | // import VuePipeline from 'vue-pipeline'
5 |
6 | import ElementUI from 'element-ui'
7 | import 'element-ui/lib/theme-chalk/index.css'
8 |
9 | Vue.config.productionTip = false
10 |
11 | Vue.use(VuePipeline)
12 | Vue.use(ElementUI, {
13 | size: 'small'
14 | })
15 |
16 | new Vue({
17 | render: h => h(App),
18 | }).$mount('#app')
19 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | // https://eslint.org/docs/user-guide/configuring
2 |
3 | module.exports = {
4 | root: true,
5 | env: {
6 | node: true
7 | },
8 | extends: [
9 | "plugin:vue/essential",
10 | "eslint:recommended"
11 | ],
12 | parserOptions: {
13 | parser: "babel-eslint"
14 | },
15 | // add your custom rules here
16 | rules: {
17 | },
18 | overrides: [{
19 | "files": ["*.vue"],
20 | "rules": {
21 | // 'no-console': "off",
22 | }
23 | }]
24 | }
25 |
--------------------------------------------------------------------------------
/src/components/PipelineNodeEnd.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{label}}
5 |
6 |
7 |
16 |
17 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | vue-hello
9 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/components/PipelineNodeStart.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | {{label}}
5 |
6 |
7 |
16 |
17 |
30 |
--------------------------------------------------------------------------------
/resources/svg.xml:
--------------------------------------------------------------------------------
1 |
15 |
--------------------------------------------------------------------------------
/src/components/PipelineLine.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
49 |
54 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | (The MIT License)
3 |
4 | Copyright (c) 2012-2019 Zuo Jinfang
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in
14 | all copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 | THE SOFTWARE.
23 |
--------------------------------------------------------------------------------
/.github/workflows/npmpublish.yml:
--------------------------------------------------------------------------------
1 | name: Node.js Package
2 |
3 | on:
4 | release:
5 | types: [created]
6 |
7 | jobs:
8 | build:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v1
12 | - uses: actions/setup-node@v1
13 | with:
14 | node-version: 12
15 | - run: npm ci
16 | - run: npm test
17 |
18 | publish-npm:
19 | needs: build
20 | runs-on: ubuntu-latest
21 | steps:
22 | - uses: actions/checkout@v1
23 | - uses: actions/setup-node@v1
24 | with:
25 | node-version: 12
26 | registry-url: https://registry.npmjs.org/
27 | - run: npm ci
28 | - run: npm publish
29 | env:
30 | NODE_AUTH_TOKEN: ${{secrets.npm_token}}
31 |
32 | publish-gpr:
33 | needs: build
34 | runs-on: ubuntu-latest
35 | steps:
36 | - uses: actions/checkout@v1
37 | - uses: actions/setup-node@v1
38 | with:
39 | node-version: 12
40 | registry-url: https://npm.pkg.github.com/
41 | scope: '@your-github-username'
42 | - run: npm ci
43 | - run: npm publish
44 | env:
45 | NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}
46 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | /**************************************************************************
2 | * IMPORTS
3 | ***************************************************************************/
4 |
5 | // BASE COMPONENTS
6 | import VuePipeline from "./src/components/Pipeline.vue"
7 |
8 | /**************************************************************************
9 | * ENVIRONMENT CONFIGURATIONS
10 | ***************************************************************************/
11 |
12 | // install function executed by Vue.use()
13 | function install(Vue, options) {
14 | if (install.installed) {
15 | return
16 | } else {
17 | install.installed = true
18 | }
19 |
20 | // Declare the component
21 | Vue.component("vue-pipeline", VuePipeline)
22 | }
23 |
24 | // Create module definition for Vue.use()
25 | const plugin = {
26 | install
27 | }
28 |
29 | // To auto-install when vue is found
30 | /* global window global */
31 | let GlobalVue = null
32 |
33 | if (typeof window !== "undefined") {
34 | GlobalVue = window.Vue
35 | } else if (typeof global !== "undefined") {
36 | GlobalVue = global.Vue
37 | }
38 |
39 | if (GlobalVue) {
40 | GlobalVue.use(plugin)
41 | }
42 |
43 | // Default export is library as a whole, registered via Vue.use()
44 | export default plugin
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vue-pipeline",
3 | "version": "1.0.12",
4 | "private": false,
5 | "author": "Zuo Jinfang ",
6 | "license": "MIT",
7 | "description": "One easy-to-use component to show beautiful responsive timeline like jenkins blue ocean plugin.",
8 | "repository": {
9 | "type": "git",
10 | "url": "https://github.com/jinfang134/vue-pipeline"
11 | },
12 | "keywords": [
13 | "vue",
14 | "components",
15 | "timeline"
16 | ],
17 | "main": "index.js",
18 | "scripts": {
19 | "serve": "vue-cli-service serve",
20 | "build": "vue-cli-service build",
21 | "test": "ava test/*.test.js --verbose",
22 | "lint": "eslint --quiet --cache --ext .vue,.js, src/** ./demo/*.{js,vue}",
23 | "preversion": "npm test && npm run lint",
24 | "version": "git add .",
25 | "postversion": "git push origin master && git push --tags && npm publish --registry=https://registry.npmjs.org/"
26 | },
27 | "husky": {
28 | "hooks": {
29 | "pre-commit": "yarn lint && yarn test",
30 | "pre-push": "yarn lint"
31 | }
32 | },
33 | "ava": {
34 | "require": [
35 | "@babel/register"
36 | ],
37 | "babel": {
38 | "testOptions": {
39 | "babelrc": false,
40 | "configFile": false
41 | }
42 | }
43 | },
44 | "dependencies": {
45 | "string-width": "^4.2.0"
46 | },
47 | "devDependencies": {
48 | "@babel/register": "^7.7.4",
49 | "@vue/babel-preset-app": "^4.1.1",
50 | "@vue/cli-plugin-babel": "^3.0.1",
51 | "@vue/cli-plugin-eslint": "^3.0.1",
52 | "@vue/cli-service": "^3.0.1",
53 | "ava": "^2.4.0",
54 | "element-ui": "^2.13.0",
55 | "vue": "^2.6.10",
56 | "babel-eslint": "^10.0.1",
57 | "core-js": "^3.4.5",
58 | "eslint": "^5.16.0",
59 | "eslint-plugin-vue": "^6.0.1",
60 | "vue-template-compiler": "^2.6.10",
61 | "husky": "^3.1.0"
62 | },
63 | "postcss": {
64 | "plugins": {
65 | "autoprefixer": {}
66 | }
67 | },
68 | "browserslist": [
69 | "> 1%",
70 | "last 2 versions"
71 | ]
72 | }
73 |
--------------------------------------------------------------------------------
/docs/cn.md:
--------------------------------------------------------------------------------
1 | # vue-pipeline
2 |
3 | 仿照 jenkins blue ocean 的一个 pipeline 组件,可以根据数据结构动态的渲染出复杂的流程图,进度图等树型图或者有向无环图.
4 |
5 | [github 主页](/https://github.com/jinfang134/vue-pipeline)
6 |
7 | 
8 |
9 | 快速开始:
10 |
11 | - [Demo](https://jinfang134.github.io/vue-pipeline/)
12 |
13 | ## LICENSE
14 |
15 | **NOTE:** Vue Pipeline 是基于 [The MIT License](https://github.com/jinfang134/vue-pipeline/blob/master/LICENSE) 协议完全开源. 如果您觉得这个插件对您有用,请赏个 star,谢谢!
16 |
17 | ## Features
18 |
19 | - 根据源数据动态创建图形
20 | - 响应式设计
21 | - 基于 svg
22 | - 可配置的
23 | - 显示/隐藏箭头
24 | - 支持 3 种不同种类的线型
25 | - 支持树和有向无环图
26 | - 节点事件触发
27 | - 每个节点显示不同的状态
28 | - 不同颜色和权重的边
29 | - 线条样式,节点颜色可以根据 css 进行配置
30 |
31 | ## 安装
32 |
33 | ```
34 | npm install vue-pipeline
35 | ```
36 |
37 | ```
38 | import Vue from 'vue'
39 | import VuePipeline from 'vue-pipeline'
40 |
41 | Vue.use(VuePipeline)
42 |
43 | ```
44 |
45 | ## 属性
46 |
47 | ### Pipeline 的属性
48 |
49 | | Name | Type | Default | Description |
50 | | --------- | ------- | ------- | -------------------------------------------------------- |
51 | | x | number | 50 | 第一个节点的 x 坐标 |
52 | | y | number | 55 | 第一个节点的 y 坐标 |
53 | | xstep | number | 120 | 相邻两个节点之间 x 轴上的间隔 |
54 | | ystep | number | 50 | 相邻两个节点 y 轴上的间隔 |
55 | | data | Array | [] | 节点数据 |
56 | | lineStyle | string | default | 线型,目前支持三种不同的线型: ' default',' bessel','line' |
57 | | showArrow | boolean | false | 是否显示箭头 |
58 | | | | | |
59 |
60 | ### 各个节点的属性
61 |
62 | | Name | Type | Default | Description |
63 | | ------------ | ------ | ------- | ------------------------------------------------------------------------------------ |
64 | | name | string | null | 各节点的标题 |
65 | | hint | string | null | 各个节点的提示 |
66 | | status | string | null | 各个节点的状态,包括:`start`,`succeed`,`running`,`failure`,`paused`,`unstable`,`end` |
67 | | next | Array | [] | 邻接表(表示与当前节点相连的节点的列表) |
68 | | next: index | number | null | 相邻节点的索引 |
69 | | next: weight | number | null | 权重(表现为不同的颜色) |
70 |
71 | **范例:**
72 |
73 | ```javascript
74 | let data = [
75 | {
76 | name: "Start",
77 | hint: "1m23s",
78 | status: "start",
79 | next: [{ index: 1, weight: 2 }]
80 | },
81 | {
82 | name: "Ammouncement Import",
83 | hint: "1m23s",
84 | status: "success",
85 | next: [
86 | { index: 2, weight: 0 },
87 | { index: 4, weight: 2 }
88 | ]
89 | },
90 | {
91 | name: "Employee ID to Onboarding",
92 | hint: "2m23s",
93 | status: "failure",
94 | next: [{ index: 3, weight: 0 }]
95 | },
96 | {
97 | name: "Personal Basic Info",
98 | hint: "2m23s",
99 | status: "paused",
100 | next: [{ index: 4, weight: 0 }]
101 | },
102 | { name: "End ", hint: "2m23s", status: "end", next: [] }
103 | ];
104 | ```
105 |
106 | ## 事件
107 |
108 | | Name | Params | Description |
109 | | ------ | ------ | ---------------------- |
110 | | @click | node | 当节点被点击的时候触发 |
111 |
112 | ## 方法
113 |
114 | | Name | params | Description |
115 | | ------ | ------ | ----------------------------------- |
116 | | render | Node | 重新渲染整个图,当参数变化时可以调用 |
117 |
118 | ## Contributing
119 |
120 | 如果发现有任何 bug 或者建议,欢迎创建新的 issue 或者发 PR.
121 |
122 | Thanks!
123 |
124 | ## 本地开发
125 |
126 | ```
127 | cd demo
128 | yarn install
129 |
130 | // Compiles and hot-reloads for development
131 | yarn run serve
132 | ```
133 |
134 | ### Lints and fixes files
135 |
136 | ```
137 | yarn run lint
138 | ```
139 |
--------------------------------------------------------------------------------
/src/components/line.js:
--------------------------------------------------------------------------------
1 | export class EdgeService {
2 | constructor(xstep, ystep) {
3 | this.xstep = xstep;
4 | this.ystep = ystep;
5 | }
6 |
7 | static getDrawEdgeService(lineStyle, step) {
8 | switch (lineStyle) {
9 | case "default":
10 | return new DefaultStyleService(step.x, step.y);
11 | case "bessel":
12 | return new BesselStyleService(step.x, step.y);
13 | case "line":
14 | return new LineStyleService(step.x, step.y);
15 | default:
16 | break;
17 | }
18 | }
19 |
20 | // eslint-disable-next-line no-unused-vars
21 | drawEdge(start, end) {}
22 |
23 | drawVerticalEdge(start, end) {
24 | const radius=14
25 | return `M ${start.x},${start.y + radius} L${start.x},${start.y + radius} ${end.x},${
26 | end.y - radius
27 | }`;
28 | }
29 |
30 | drawHEdge(start, end) {
31 | if (end.x > start.x + this.xstep) {
32 | if (!start) {
33 | // console.log(start, end);
34 | }
35 | // let start = start.x + 10;
36 | let number = parseInt((end.x - start.x) / this.xstep);
37 |
38 | let control1 = this.xstep / 2 + 40;
39 | let control2 = this.xstep / 2 + 30;
40 |
41 | let d = `M ${start.x + 10} ${start.y} \
42 | l 20 0\
43 | C ${start.x + control1},${start.y} \
44 | ${start.x + control2},${start.y + 30} \
45 | ${start.x + this.xstep},${start.y + 30}`;
46 | if (number > 2) {
47 | d += `l ${this.xstep * (number - 2)} 0`;
48 | }
49 |
50 | d += `C ${end.x - control2},${start.y + 30} \
51 | ${end.x - control1},${start.y} \
52 | ${end.x - 10 - 20},${end.y} \
53 | l 20 0`;
54 |
55 | return d;
56 | }
57 | return this.getStraightLinePath(start, end);
58 | }
59 |
60 | /**
61 | * 生成直线的指令
62 | * @param {*} start
63 | * @param {*} end
64 | */
65 | getStraightLinePath(start, end) {
66 | return `M ${start.x + 12},${start.y} L${start.x + 12},${start.y} ${
67 | end.x - 15
68 | },${end.y}`;
69 | }
70 | }
71 |
72 | class DefaultStyleService extends EdgeService {
73 | constructor(xstep, ystep) {
74 | super(xstep, ystep);
75 | }
76 |
77 | drawEdge(start, end) {
78 | if (start.y == end.y) {
79 | return this.drawHEdge(start, end);
80 | }
81 | if (start.x == end.x) {
82 | return this.drawVerticalEdge(start, end);
83 | }
84 | const lb = "c 0 12 12 12 12 12";
85 | const rb = "c 12 0 12 -12 12 -12";
86 | const rt = "c 12 0 12 12 12 12";
87 | const lt = "c 0 -12 12 -12 12 -12";
88 | let midy = Math.abs(end.y - start.y);
89 | if (end.y > start.y) {
90 | // 左上到右下
91 | let firstCorner = end.x - start.x - 50;
92 | const d = `M ${start.x + 10} ${start.y}\
93 | l ${20} 0\
94 | ${rt} \
95 | l 0 ${midy - 24} \
96 | ${lb} \
97 | l ${firstCorner - 20} 0
98 | `;
99 | return d;
100 | } else {
101 | let lastCorner = end.x - start.x - 50;
102 | const d = `M ${start.x + 14} ${start.y}\
103 | l ${lastCorner - 20} 0\
104 | ${rb} \
105 | l 0 -${midy - 24} \
106 | ${lt} \
107 | l ${20} 0
108 | `;
109 | // console.log(d)
110 | return d;
111 | }
112 | }
113 | }
114 |
115 | class BesselStyleService extends EdgeService {
116 | drawEdge(start, end) {
117 | if (start.y == end.y) {
118 | return this.drawHEdge(start, end);
119 | }
120 | if (start.x == end.x) {
121 | return this.drawVerticalEdge(start, end);
122 | }
123 | if (end.y > start.y) {
124 | let path = `M ${start.x + 12},${start.y}\
125 | C ${end.x},${start.y}\
126 | ${start.x + 50},${end.y}\
127 | ${end.x - 15},${end.y}
128 | `;
129 | return path;
130 | } else {
131 | let path = `M ${start.x},${start.y}\
132 | C ${end.x - 50},${start.y}\
133 | ${start.x},${end.y}\
134 | ${end.x - 12},${end.y}
135 | `;
136 | return path;
137 | }
138 | }
139 | }
140 |
141 | class LineStyleService extends EdgeService {
142 | drawEdge(start, end) {
143 | if (start.y == end.y) {
144 | return this.drawHEdge(start, end);
145 | }
146 | return this.getStraightLinePath(start, end);
147 | }
148 | }
149 |
--------------------------------------------------------------------------------
/src/components/Pipeline.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
17 |
18 |
121 |
122 |
183 |
--------------------------------------------------------------------------------
/demo/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
Vue Pipeline
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 | Default
45 | Bessel
46 | line
47 |
48 |
49 |
50 |
51 | HUE 1
52 | HUE 2
53 | Tree
54 | Sample 4
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 | Add
63 |
64 |
65 |
66 | Update
67 |
68 |
69 |
70 |
71 |
You selected :{{msg}}
72 |
73 |
74 |
76 |
77 |
78 | {{JSON.stringify(this.data)}}
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
156 |
157 |
194 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # vue-pipeline
2 |
3 | [](https://www.npmjs.com/package/vue-pipeline)
4 | [](https://www.npmjs.com/package/vue-pipeline)
5 | [](https://github.com/jinfang134/vue-pipeline/stargazers)
6 | [](https://github.com/jinfang134/vue-pipeline/network)
7 | [](https://github.com/jinfang134/vue-pipeline/blob/master/LICENSE)
8 | [](https://travis-ci.com/jinfang134/vue-pipeline)
9 |
10 | One easy-to-use component to show beautiful responsive timeline like jenkins blue ocean plugin.
11 |
12 | [中文](https://github.com/jinfang134/vue-pipeline/blob/master/docs/cn.md)
13 |
14 | 
15 |
16 | To get started, check out:
17 |
18 | - [Demo](https://jinfang134.github.io/vue-pipeline/)
19 |
20 | ## LICENSE
21 |
22 | **NOTE:** Vue Pipeline is licensed under [The MIT License](https://github.com/jinfang134/vue-pipeline/blob/master/LICENSE). Completely free, you can arbitrarily use and modify this plugin. If this plugin is useful to you, you can **Star** this repo, your support is my biggest motive force, thanks.
23 |
24 | ## Features
25 |
26 | - Created Graph according your data dynamiclly
27 | - Responsive web design
28 | - svg component
29 | - Fully configurable
30 | - Via data attributes
31 | - Show/Hide arrow
32 | - 3 kinds of lines
33 | - support graph and tree view
34 | - Single node selection
35 | - Different status for each node
36 | - Different weight for each edge
37 | - Different color for each node and edge
38 |
39 | ## Install
40 |
41 | ```
42 | npm install vue-pipeline
43 | ```
44 |
45 | ```
46 | import Vue from 'vue'
47 | import VuePipeline from 'vue-pipeline'
48 |
49 | Vue.use(VuePipeline)
50 |
51 | ```
52 |
53 | ## Props
54 |
55 | ### Props of Pipeline
56 |
57 | | Name | Type | Default | Description |
58 | | --------- | ------- | ------- | ------------------------------------------------------ |
59 | | x | number | 50 | The x coordinate of the starting point of the graph |
60 | | y | number | 55 | The y coordinate of the starting point of the graph |
61 | | xstep | number | 120 | The position horizontally from a previous node. |
62 | | ystep | number | 50 | The position vertically from a previous node. |
63 | | data | Array | [] | data |
64 | | lineStyle | string | default | There are 3 types of line: ' default',' bessel','line' |
65 | | showArrow | boolean | false | whether show arrow for each line. |
66 | | | | | |
67 |
68 | ### Props for each node
69 |
70 | | Name | Type | Default | Description |
71 | | ------------ | ------ | ------- | ---------------------------------------------------------------------------------------------------------------- |
72 | | name | string | null | The title of each node |
73 | | hint | string | null | The hint of each node |
74 | | status | string | null | Status of each node, There are 6 type of status: `start`,`succeed`,`running`,`failure`,`paused`,`unstable`,`end` |
75 | | next | Array | [] | The edge connected with this node |
76 | | next: index | number | null | The index of another node of this edge |
77 | | next: weight | number | null | The weight of this edge |
78 |
79 | **Sample Data:**
80 |
81 | ```javascript
82 | let data = [
83 | {
84 | name: "Start",
85 | hint: "1m23s",
86 | status: "start",
87 | next: [{ index: 1, weight: 2 }]
88 | },
89 | {
90 | name: "Ammouncement Import",
91 | hint: "1m23s",
92 | status: "success",
93 | next: [
94 | { index: 2, weight: 0 },
95 | { index: 4, weight: 2 }
96 | ]
97 | },
98 | {
99 | name: "Employee ID to Onboarding",
100 | hint: "2m23s",
101 | status: "failure",
102 | next: [{ index: 3, weight: 0 }]
103 | },
104 | {
105 | name: "Personal Basic Info",
106 | hint: "2m23s",
107 | status: "paused",
108 | next: [{ index: 4, weight: 0 }]
109 | },
110 | { name: "End ", hint: "2m23s", status: "end", next: [] }
111 | ];
112 | ```
113 |
114 | ## Events
115 |
116 | | Name | Params | Description |
117 | | ------ | ------ | --------------------------- |
118 | | @click | node | Occurs when node is clicked |
119 |
120 | ## Function
121 |
122 | | Name | params | Description |
123 | | ------ | ------ | ---------------------------- |
124 | | render | Node | render the whole graph again |
125 |
126 | ## Contributing
127 |
128 | If you find any bugs and/or want to contribute, feel free to create issues or submit pull requests.
129 |
130 | Thanks!
131 |
132 | ## Local Development
133 |
134 | ```
135 | yarn install
136 | // Compiles and hot-reloads for development
137 | yarn run serve
138 | ```
139 |
140 | ### publish a new version
141 |
142 | ```
143 | npm version patch
144 | npm version minor
145 | npm version major
146 | ```
147 |
148 | ### Lints and fixes files
149 |
150 | ```
151 | yarn run lint
152 | ```
153 |
--------------------------------------------------------------------------------
/src/components/PipelineNode.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | {{getText().text}}
10 | {{label}}
11 |
12 |
13 |
14 |
15 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
35 |
36 |
37 |
38 |
39 | {{hint}}
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
134 |
188 |
--------------------------------------------------------------------------------
/test/service.test.js:
--------------------------------------------------------------------------------
1 | import test from 'ava';
2 | import { Pipeline } from "../src/components/service";
3 |
4 | const sample = {
5 | nodes: [
6 | { name: "test0", hint: '1m23s', status: 'success', next: [{ index: 1, weight: 2 }] },
7 | {
8 | name: "test1 long", hint: '1m23s', status: 'success', next: [
9 | { index: 2, weight: 2 }, { index: 3, weight: 2 }, { index: 4, weight: 2 }
10 | ]
11 | },
12 | { name: "test2 hello world", hint: '2m23s', status: 'unstable', next: [{ index: 5 }] },
13 | { name: "test3", hint: '2m23s', status: 'success', next: [{ index: 7, weight: 1 }] },
14 | { name: "test4 ha", hint: '2m23s', status: 'failure', next: [{ index: 8 }, { index: 9 }] },//4
15 | { name: "test5", hint: '2m23s', status: 'failure', next: [{ index: 6 }] },
16 | { name: "test6", hint: '2m23s', status: 'success', next: [{ index: 10 }] },//6
17 | { name: "test7 hello", hint: '2m23s', status: 'paused', next: [{ index: 10 }] },
18 | { name: "test8 2344", hint: '2m23s', status: 'paused', next: [{ index: 10 }] },//8
19 | { name: "test9", hint: '2m23s', status: 'failure', next: [{ index: 10 }] },
20 | { name: "test10", hint: '2m23s', status: 'failure', next: [{ index: 11 }] }, //10
21 | { name: "test11", hint: '2m23s', status: 'failure' },
22 | ],
23 | }
24 |
25 | test('find the longest way from start position', t => {
26 | let service = new Pipeline(sample.nodes, 50, 55, 120, 60)
27 | let list = service.findLongestWay(0)
28 | t.deepEqual(list, [0, 1, 2, 5, 6, 10, 11], 'longest way is different!!');
29 | });
30 |
31 | test('findParents', async t => {
32 | let service = new Pipeline(sample.nodes, 50, 55, 120, 60)
33 | let list = service.findParents(10)
34 | t.deepEqual(list, [6, 7, 8, 9], 'Parents is wrong!');
35 | });
36 |
37 |
38 | test('DFS', async t => {
39 | let service = new Pipeline(sample.nodes, 50, 55, 120, 60)
40 | let list = service.dfs(0)
41 | t.deepEqual(list.length, sample.nodes.length, 'DFS is wrong!');
42 | });
43 |
44 |
45 | test('topologicalSorting', t => {
46 | const testdata = [
47 | { name: "test0", hint: '1m23s', status: 'success', next: [{ index: 1, weight: 2 }] },
48 | {
49 | name: "test1 long", hint: '1m23s', status: 'success', next: [
50 | { index: 2, weight: 2 }, { index: 3, weight: 2 }, { index: 5, weight: 2 }
51 | ]
52 | },
53 | { name: "test2 hello world", hint: '2m23s', status: 'unstable', next: [{ index: 3 }, { index: 4 }] },
54 | { name: "test3", hint: '2m23s', status: 'success', next: [{ index: 4 }] },
55 | { name: "test4 ha", hint: '2m23s', status: 'failure', next: [] },//4
56 | { name: "test5", hint: '2m23s', status: 'failure', next: [{ index: 4 }] },
57 | ]
58 |
59 | let service = new Pipeline(testdata, 50, 55, 120, 60)
60 | let list = service.topologicalSorting()
61 | t.deepEqual(list, [0, 1, 2, 3, 5, 4])
62 | })
63 |
64 | test('hasCircle', t => {
65 | const test2 = [
66 | { name: "test0", hint: '1m23s', status: 'success', next: [{ index: 1, weight: 2 }] },
67 | { name: "test0", hint: '1m23s', status: 'success', next: [{ index: 2, weight: 2 }] },
68 | { name: "test0", hint: '1m23s', status: 'success', next: [{ index: 1, weight: 2 }] },
69 | ]
70 |
71 | let service = new Pipeline(test2, 50, 55, 120, 60)
72 | let result = service.hasCircle()
73 | t.deepEqual(result, true, 'this graph has circle.')
74 | })
75 |
76 |
77 | test('isTree', t => {
78 | const test2 = [
79 | { name: "test0", hint: '1m23s', status: 'success', next: [{ index: 1, weight: 2 }] },
80 | { name: "test0", hint: '1m23s', status: 'success', next: [{ index: 2, weight: 2 }] },
81 | { name: "test0", hint: '1m23s', status: 'success', next: [{ index: 1, weight: 2 }] },
82 | ]
83 | let service = new Pipeline(test2, 50, 55, 120, 60)
84 | let result = service.isTree()
85 | t.deepEqual(result, false, 'this graph is not a tree.')
86 |
87 | const data = [
88 | { name: "test0", hint: '1m23s', status: 'success', next: [{ index: 1, weight: 2 }, { index: 2, weight: 2 }] },
89 | { name: "test0", hint: '1m23s', status: 'success', next: [{ index: 3, weight: 2 }] },
90 | { name: "test0", hint: '1m23s', status: 'success', next: [] },
91 | { name: "test0", hint: '1m23s', status: 'success', next: [] },
92 | ]
93 | let service2 = new Pipeline(data, 50, 55, 120, 60)
94 | let result2 = service2.isTree()
95 | t.deepEqual(result2, true, 'this graph is a tree.')
96 |
97 | })
98 |
99 |
100 | test('assignNodeForTree',t=>{
101 | const data = [
102 | { name: "test0", hint: '1m23s', status: 'success', next: [{ index: 1, weight: 2 }] },
103 | {
104 | name: "test1 long", hint: '1m23s', status: 'success', next: [
105 | { index: 2, weight: 2 }, { index: 3, weight: 2 }, { index: 4, weight: 2 }
106 | ]
107 | },
108 | { name: "test2 hello world", hint: '2m23s', status: 'unstable', next: [{ index: 5 }] },
109 | { name: "test3", hint: '2m23s', status: 'success', next: [{ index: 7, weight: 1 }] },
110 | { name: "test4 ha", hint: '2m23s', status: 'failure', next: [{ index: 8 }, { index: 9 }] },//4
111 | { name: "test5", hint: '2m23s', status: 'failure', next: [{ index: 6 }] },
112 | { name: "test6", hint: '2m23s', status: 'success', next: [{ index: 10 }] },//6
113 | { name: "test7 hello", hint: '2m23s', status: 'paused', next: [{ index: 12 }, { index: 13 }] },
114 | { name: "test8 2344", hint: '2m23s', status: 'paused', next: [{ index: 14 }, { index: 15 }] },//8
115 | { name: "test9", hint: '2m23s', status: 'failure', next: [{ index: 16 }] },
116 | { name: "test10", hint: '2m23s', status: 'failure', next: [{ index: 11 }] }, //10
117 | { name: "test11", hint: '2m23s', status: 'failure' },
118 | { name: "test12", hint: '2m23s', status: 'failure' },
119 | { name: "test13", hint: '2m23s', status: 'failure' },
120 | { name: "test14", hint: '2m23s', status: 'failure' },
121 | { name: "test15", hint: '2m23s', status: 'failure' },
122 | { name: "test16", hint: '2m23s', status: 'failure' },
123 | ]
124 | let service2 = new Pipeline(data, 50, 55, 120, 60)
125 | service2.assignNodeForTree(0,0,0)
126 | t.deepEqual(service2.matrix[0], [0,1,2,5,6,10,11], 'this graph is a tree.')
127 | t.deepEqual(service2.matrix[1], [undefined,undefined,3,7,12], 'this graph is a tree.')
128 | })
129 |
--------------------------------------------------------------------------------
/src/components/service.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable no-console */
2 |
3 | // const data = require('./data.js')
4 | import { EdgeService } from "./line";
5 |
6 | class Pipeline {
7 | /**
8 | *
9 | * @param {Array} nodes
10 | * @param {Number} startx
11 | * @param {*} starty
12 | * @param {*} xstep
13 | * @param {*} ystep
14 | * @param {*} lineStyle 线型,目前支持三种线型: default(默认),line(直线),bessel(贝塞尔曲线)
15 | */
16 | constructor(nodes, startx, starty, xstep, ystep, lineStyle = "default") {
17 | this.nodes = nodes;
18 | this.startx = startx;
19 | this.starty = starty;
20 | this.xstep = xstep;
21 | this.ystep = ystep;
22 | this.positionList = new Set();
23 | this.solvedList = [];
24 | this.lineStyle = lineStyle;
25 | this.sortedList = this.topologicalSorting();
26 | this.matrix = []; //存放各个顶点的相对坐标
27 | for (let i = 0; i < nodes.length; i++) {
28 | this.matrix[i] = [];
29 | }
30 | this.width = 0;
31 | this.height = 0;
32 | }
33 |
34 | /**
35 | * 判断当前的图是否是一棵树
36 | */
37 | isTree() {
38 | let set = new Set();
39 | for (let i = 0; i < this.nodes.length; i++) {
40 | if (this.nodes[i].next) {
41 | if (this.nodes[i].next.some((it) => set.has(it.index))) {
42 | return false;
43 | }
44 | this.nodes[i].next.forEach((it) => set.add(it.index));
45 | }
46 | }
47 | return true;
48 | }
49 |
50 | /**
51 | * 计算一个树要占的宽度
52 | * @param {*} index
53 | */
54 | getWidthOfTree(index) {
55 | let node = this.nodes[index];
56 | if (!node.next || node.next.length == 0) {
57 | return 1;
58 | }
59 |
60 | let width = 0;
61 | for (let i = 0; i < node.next.length; i++) {
62 | width += this.getWidthOfTree(node.next[i].index);
63 | }
64 | return width;
65 | }
66 |
67 | /**
68 | * 为树分配节点的位置
69 | * @param {*} index
70 | * @param {*} x
71 | * @param {*} y
72 | */
73 | assignNodeForTree(index, x, y) {
74 | this.matrix[y][x] = index;
75 | let node = this.nodes[index];
76 | if (!node.next || node.next.length == 0) {
77 | return;
78 | }
79 |
80 | let xx = x + 1;
81 | let yy = y;
82 |
83 | for (let i = 0; i < node.next.length; i++) {
84 | let width = this.getWidthOfTree(node.next[i].index);
85 | this.assignNodeForTree(node.next[i].index, xx, yy);
86 | yy += width;
87 | }
88 | }
89 |
90 | getLines() {
91 | let list = [];
92 | var drawService = EdgeService.getDrawEdgeService(this.lineStyle, {
93 | x: this.xstep,
94 | y: this.ystep,
95 | });
96 | for (let i = 0; i < this.nodes.length; i++) {
97 | let node = this.nodes[i];
98 | if (!node.next) {
99 | continue;
100 | }
101 | for (let j = 0; j < node.next.length; j++) {
102 | let edge = node.next[j];
103 | let child = this.nodes[edge.index];
104 | list.push({
105 | path: drawService.drawEdge(node, child),
106 | weight: edge.weight,
107 | });
108 | }
109 | }
110 | list.sort((a, b) => a.weight - b.weight);
111 | return list;
112 | }
113 |
114 |
115 | getPositionInMatrix(index) {
116 | for (let i = 0; i < this.matrix.length; i++) {
117 | for (let j = 0; j < this.matrix[i].length; j++) {
118 | if (this.matrix[i][j] == index) {
119 | return [i, j];
120 | }
121 | }
122 | }
123 | return [];
124 | }
125 |
126 | /**
127 | * 计算每个点的坐标
128 | */
129 | calculateAllPosition() {
130 | if (this.isTree()) {
131 | this.assignNodeForTree(0, 0, 0);
132 | } else {
133 | this.assignNodeForGraph();
134 | }
135 | this.calCoordinateForMatrix();
136 | }
137 |
138 | /**
139 | * 为图的每个节点计算坐标位置
140 | */
141 | assignNodeForGraph() {
142 | // 查找最长的路径,并为其分配坐标
143 | let list = this.findLongestWay(0);
144 | list.forEach((it, index) => {
145 | this.matrix[0][index] = it;
146 | this.solvedList[it] = true;
147 | });
148 |
149 | for (let i = 0; i < this.sortedList.length; i++) {
150 | let sindex = this.sortedList[i];
151 | if (!this.solvedList[sindex]) {
152 | let fatherIndex = this.findSolvedFather(sindex);
153 | let [y, x] = this.getPositionInMatrix(fatherIndex); //找到父节点在矩阵中的坐标
154 | let list = this.findLongestWay(sindex);
155 | let startx = x + 1;
156 |
157 | let starty = y;
158 | while (this.matrix[starty][startx]) {
159 | starty++;
160 | }
161 | // starty-=1;
162 | list.forEach((it) => {
163 | this.matrix[starty][startx++] = it;
164 | this.solvedList[it] = true;
165 | });
166 | }
167 | }
168 | }
169 |
170 | calCoordinateForMatrix() {
171 | for (let i = 0; i < this.matrix.length; i++) {
172 | for (let j = 0; j < this.matrix.length; j++) {
173 | let index = this.matrix[i][j];
174 | if (index != undefined) {
175 | this.nodes[index].x = this.startx + this.xstep * j;
176 | this.nodes[index].y = this.starty + this.ystep * i;
177 | this.width = Math.max(this.width, this.nodes[index].x + this.startx);
178 | this.height = Math.max(
179 | this.height,
180 | this.nodes[index].y + this.starty
181 | );
182 | }
183 | }
184 | }
185 | }
186 |
187 | /**
188 | * 优化节点的位置,使其在x轴上左右居中,线的处理上还有bug
189 | */
190 | optimize() {
191 | for (let i = 0; i < this.nodes.length; i++) {
192 | let node = this.nodes[i];
193 | if (node.y == this.starty) {
194 | // 第一行不变
195 | continue;
196 | }
197 | let parents = this.findParents(i);
198 | let children = this.findChildren(i);
199 | // eslint-disable-next-line no-console
200 | console.log(parents, children);
201 | let startx = Math.max(...parents.map((item) => this.nodes[item].x));
202 | let endx = Math.min(...children.map((item) => this.nodes[item].x));
203 | node.x = (startx + endx) / 2;
204 | this.nodes[i] = node;
205 | }
206 | }
207 |
208 | /**
209 | * 图的拓扑排序
210 | */
211 | topologicalSorting() {
212 | let visited = [];
213 | let result = [];
214 | for (let i = 0; i < this.nodes.length; i++) {
215 | if (visited[i] == true) {
216 | continue;
217 | }
218 | let list = this.findParents(i);
219 | if (list.length == 0 || list.every((it) => visited[it] == true)) {
220 | visited[i] = true;
221 | result.push(i);
222 | i = 0;
223 | }
224 | }
225 | return result;
226 | }
227 |
228 | /**
229 | * 判断是否有环
230 | * 如果有环,返回true
231 | */
232 | hasCircle() {
233 | let list = this.topologicalSorting();
234 | return list.length < this.nodes.length;
235 | }
236 |
237 | /**
238 | * 往前找到第一个解决的父节点
239 | * @param {*} index
240 | */
241 | findSolvedFather(index) {
242 | let list = this.findParents(index);
243 | if (list.length == 0) {
244 | return null;
245 | }
246 | for (let i = 0; i < list.length; i++) {
247 | if (this.solvedList[list[i]]) {
248 | return list[i];
249 | } else {
250 | return this.findSolvedFather(list[i]);
251 | }
252 | }
253 | }
254 |
255 | /**
256 | * 查找某个顶点的父顶点
257 | * @param {*} nodes
258 | * @param {*} index
259 | */
260 | findParents(index) {
261 | let arr = [];
262 | for (let i = 0; i < this.nodes.length; i++) {
263 | if (
264 | this.nodes[i].next &&
265 | this.nodes[i].next.some((it) => it.index == index)
266 | ) {
267 | arr.push(i);
268 | }
269 | }
270 | return arr;
271 | }
272 |
273 | findChildren(index) {
274 | if (!this.nodes[index].next) {
275 | return [];
276 | }
277 | return this.nodes[index].next.map((it) => it.index);
278 | }
279 |
280 | /**
281 | * 查找从第{index}个节点开始的最长路径,返回经过的未被计算位置的节点,
282 | * @param {*} index
283 | */
284 | findLongestWay(index) {
285 | let children = this.findChildren(index);
286 | if (children.length == 0) {
287 | return [index];
288 | }
289 | let arr = [],
290 | maxLength = 0;
291 | for (let i = 0; i < children.length; i++) {
292 | if (this.solvedList[children[i]]) {
293 | continue;
294 | }
295 | let list = this.findLongestWay(children[i]);
296 | if (list.length > maxLength) {
297 | maxLength = list.length;
298 | arr = list.slice();
299 | }
300 | }
301 | return [index].concat(arr);
302 | }
303 |
304 | /**
305 | * 从第{index}个节点出发,深度优先搜索图
306 | * @param {*} nodes
307 | * @param {*} index
308 | */
309 | dfs(index) {
310 | const queue = [];
311 | const visited = [];
312 | const result = [];
313 | visited[index] = true;
314 | queue.push(index);
315 |
316 | while (queue.length > 0) {
317 | let first = queue.pop();
318 | visited[first] = true;
319 | console.log(first);
320 | result.push(first);
321 | let children = this.findChildren(first);
322 | for (let i = 0; i < children.length; i++) {
323 | let j = children[i];
324 | if (!visited[j]) {
325 | queue.push(j);
326 | visited[j] = true;
327 | }
328 | }
329 | }
330 | return result;
331 | }
332 | }
333 |
334 | export { Pipeline };
335 |
--------------------------------------------------------------------------------
/demo/data.js:
--------------------------------------------------------------------------------
1 | const hue3 = {
2 | nodes: [
3 | {
4 | name: "Start",
5 | hint: "1m23s",
6 | status: "start",
7 | next: [{ index: 1, weight: 2 }]
8 | },
9 | {
10 | name: "Ammouncement Import",
11 | hint: "1m23s",
12 | status: "success",
13 | next: [
14 | { index: 2, weight: 0 },
15 | { index: 3, weight: 2 }
16 | ]
17 | },
18 | {
19 | name: "Employee ID to Onboarding",
20 | hint: "2m23s",
21 | status: "failure",
22 | next: [{ index: 3, weight: 0 }]
23 | },
24 | {
25 | name: "Personal Basic Info",
26 | hint: "2m23s",
27 | status: "paused",
28 | next: [{ index: 4, weight: 0 }]
29 | },
30 | { name: "End ", hint: "2m23s", status: "end", next: [] }
31 | ]
32 | };
33 |
34 | const hue1 = {
35 | nodes: [
36 | {
37 | name: "Start",
38 | hint: "1m23s",
39 | status: "start",
40 | next: [{ index: 1, weight: 2 }]
41 | },
42 | {
43 | name: "Ammouncement Import",
44 | hint: "1m23s",
45 | status: "success",
46 | next: [{ index: 2, weight: 2 }]
47 | },
48 | {
49 | name: "Employee ID to Onboarding",
50 | hint: "2m23s",
51 | status: "success",
52 | next: [
53 | { index: 3, weight: 2 },
54 | { index: 4, weight: 2 },
55 | { index: 5, weight: 2 },
56 | { index: 6, weight: 0 },
57 | { index: 7, weight: 1 },
58 | { index: 8, weight: 2 },
59 | { index: 9, weight: 0 }
60 | ]
61 | },
62 | {
63 | name: "Personal Basic Info",
64 | hint: "2m23s",
65 | status: "unstable",
66 | next: [{ index: 10, weight: 2 }]
67 | },
68 | {
69 | name: "地址信息",
70 | hint: "2m23s",
71 | status: "success",
72 | next: [{ index: 10, weight: 0 }]
73 | },
74 | {
75 | name: "Family Info",
76 | hint: "2m23s",
77 | status: "failure",
78 | next: [{ index: 10, weight: 0 }]
79 | },
80 | {
81 | name: "Education Info",
82 | hint: "2m23s",
83 | status: "running",
84 | next: [{ index: 10, weight: 0 }]
85 | },
86 | {
87 | name: "Degree Info",
88 | hint: "2m23s",
89 | status: "failure",
90 | next: [{ index: 10, weight: 0 }]
91 | },
92 | {
93 | name: "Career Info",
94 | hint: "2m23s",
95 | status: "failure",
96 | next: [{ index: 10, weight: 0 }]
97 | },
98 | {
99 | name: "Qualification Info",
100 | hint: "2m23s",
101 | status: "failure",
102 | next: [{ index: 10, weight: 0 }]
103 | },
104 | { name: "End", hint: "2m23s", status: "end", next: [] }
105 | ]
106 | };
107 |
108 | const hue2 = {
109 | nodes: [
110 | {
111 | name: "Start",
112 | hint: "1m23s",
113 | status: "success",
114 | next: [
115 | {
116 | index: 1,
117 | weight: 0
118 | }
119 | ]
120 | },
121 | {
122 | name: "Ammouncement Import",
123 | hint: "1m23s",
124 | status: "success",
125 | next: [2, 3, 4, 5, 6]
126 | },
127 | {
128 | name: "Personal Basic Info",
129 | hint: "2m23s",
130 | status: "unstable",
131 | next: [4]
132 | },
133 | { name: "Address Info", hint: "2m23s", status: "success", next: [7] },
134 | { name: "Family Info", hint: "2m23s", status: "failure", next: [7] },
135 | { name: "Family In", hint: "2m23s", status: "failure", next: [7] },
136 | { name: "Education Info", hint: "2m23s", status: "success", next: [7] },
137 | { name: "Degree Info", hint: "2m23s", status: "paused", next: [8] },
138 | { name: "End", hint: "2m23s", status: "failure" }
139 | ]
140 | };
141 |
142 | const sample = {
143 | nodes: [
144 | {
145 | name: "test0",
146 | hint: "1m23s",
147 | status: "success",
148 | next: [{ index: 1, weight: 2 }]
149 | },
150 | {
151 | name: "test1 long",
152 | hint: "1m23s",
153 | status: "success",
154 | next: [
155 | { index: 2, weight: 2 },
156 | { index: 3, weight: 2 },
157 | { index: 4, weight: 2 }
158 | ]
159 | },
160 | {
161 | name: "test2 hello world",
162 | hint: "2m23s",
163 | status: "unstable",
164 | next: [{ index: 5 }]
165 | },
166 | {
167 | name: "test3",
168 | hint: "2m23s",
169 | status: "success",
170 | next: [{ index: 7, weight: 1 }]
171 | },
172 | {
173 | name: "test4 ha",
174 | hint: "2m23s",
175 | status: "running",
176 | next: [{ index: 8 }, { index: 9 }]
177 | }, //4
178 | { name: "test5", hint: "2m23s", status: "failure", next: [{ index: 6 }] },
179 | { name: "test6", hint: "2m23s", status: "success", next: [{ index: 10 }] }, //6
180 | {
181 | name: "test7 hello",
182 | hint: "2m23s",
183 | status: "paused",
184 | next: [{ index: 12 }, { index: 13 }]
185 | },
186 | {
187 | name: "test8 2344",
188 | hint: "2m23s",
189 | status: "paused",
190 | next: [{ index: 14 }, { index: 15 }]
191 | }, //8
192 | { name: "test9", hint: "2m23s", status: "failure", next: [{ index: 16 }] },
193 | { name: "test10", hint: "2m23s", status: "failure", next: [{ index: 11 }] }, //10
194 | { name: "test11", hint: "2m23s", status: "failure" },
195 | { name: "test12", hint: "2m23s", status: "failure" },
196 | { name: "test13", hint: "2m23s", status: "failure" },
197 | { name: "test14", hint: "2m23s", status: "failure" },
198 | { name: "test15", hint: "2m23s", status: "failure" },
199 | { name: "test16", hint: "2m23s", status: "failure" }
200 | ]
201 | };
202 |
203 | const sample3 = {
204 | nodes: [
205 | {
206 | name: "test0",
207 | hint: "1m23s",
208 | status: "success",
209 | next: [{ index: 1, weight: 2 }]
210 | },
211 | {
212 | name: "test1 long",
213 | hint: "1m23s",
214 | status: "success",
215 | next: [
216 | { index: 2, weight: 2 },
217 | { index: 3, weight: 2 },
218 | { index: 4, weight: 2 }
219 | ]
220 | },
221 | {
222 | name: "test2 hello world",
223 | hint: "2m23s",
224 | status: "unstable",
225 | next: [{ index: 5 }]
226 | },
227 | {
228 | name: "test3",
229 | hint: "2m23s",
230 | status: "success",
231 | next: [{ index: 7, weight: 1 }]
232 | },
233 | {
234 | name: "test4 ha",
235 | hint: "2m23s",
236 | status: "failure",
237 | next: [{ index: 8 }, { index: 9 }]
238 | }, //4
239 | { name: "test5", hint: "2m23s", status: "failure", next: [{ index: 6 }] },
240 | { name: "test6", hint: "2m23s", status: "success", next: [{ index: 10 }] }, //6
241 | {
242 | name: "test7 hello",
243 | hint: "2m23s",
244 | status: "paused",
245 | next: [{ index: 10 }]
246 | },
247 | {
248 | name: "test8 2344",
249 | hint: "2m23s",
250 | status: "paused",
251 | next: [{ index: 10 }]
252 | }, //8
253 | { name: "test9", hint: "2m23s", status: "failure", next: [{ index: 10 }] },
254 | { name: "test10", hint: "2m23s", status: "failure", next: [{ index: 11 }] }, //10
255 | { name: "test11", hint: "2m23s", status: "failure" }
256 | ]
257 | };
258 | const sample2 = {
259 | nodes: [
260 | {
261 | name: "test0",
262 | hint: "1m23s",
263 | status: "success",
264 | next: [{ index: 1, weight: 2 }],
265 | x: 50,
266 | y: 55
267 | },
268 | {
269 | name: "test1 long",
270 | hint: "1m23s",
271 | status: "success",
272 | next: [
273 | { index: 2, weight: 2 },
274 | { index: 3, weight: 2 },
275 | { index: 4, weight: 2 }
276 | ],
277 | x: 170,
278 | y: 55
279 | },
280 | {
281 | name: "test2 hello world",
282 | hint: "2m23s",
283 | status: "unstable",
284 | next: [{ index: 5 }],
285 | x: 290,
286 | y: 55
287 | },
288 | {
289 | name: "test3",
290 | hint: "2m23s",
291 | status: "success",
292 | next: [{ index: 7, weight: 1 }],
293 | x: 290,
294 | y: 125
295 | },
296 | {
297 | name: "test4 ha",
298 | hint: "2m23s",
299 | status: "failure",
300 | next: [{ index: 8 }, { index: 9 }],
301 | x: 290,
302 | y: 55
303 | },
304 | {
305 | name: "test5",
306 | hint: "2m23s",
307 | status: "failure",
308 | next: [{ index: 6 }],
309 | x: 410,
310 | y: 55
311 | },
312 | {
313 | name: "test6",
314 | hint: "2m23s",
315 | status: "running",
316 | next: [{ index: 10 }],
317 | x: 530,
318 | y: 55
319 | },
320 | {
321 | name: "test7 hello",
322 | hint: "2m23s",
323 | status: "paused",
324 | next: [{ index: 12 }, { index: 13 }, { index: "10" }],
325 | x: 650,
326 | y: 55
327 | },
328 | {
329 | name: "test8 2344",
330 | hint: "2m23s",
331 | status: "paused",
332 | next: [{ index: 14 }, { index: 15 }],
333 | x: 410,
334 | y: 55
335 | },
336 | {
337 | name: "test9",
338 | hint: "2m23s",
339 | status: "failure",
340 | next: [{ index: 16 }],
341 | x: 410,
342 | y: 265
343 | },
344 | {
345 | name: "test10",
346 | hint: "2m23s",
347 | status: "failure",
348 | next: [{ index: 11 }, { index: "11" }],
349 | x: 770,
350 | y: 55
351 | },
352 | { name: "test11", hint: "2m23s", status: "failure", x: 890, y: 55 },
353 | { name: "test12", hint: "2m23s", status: "failure", x: 530, y: 125 },
354 | { name: "test13", hint: "2m23s", status: "failure", x: 530, y: 195 },
355 | {
356 | name: "test14",
357 | hint: "2m23s",
358 | status: "failure",
359 | x: 530,
360 | y: 55,
361 | next: [{ index: "7" }]
362 | },
363 | {
364 | name: "test15",
365 | hint: "2m23s",
366 | status: "failure",
367 | x: 530,
368 | y: 335,
369 | next: [{ index: "7" }]
370 | },
371 | { name: "test16", hint: "2m23s", status: "failure", x: 530, y: 405 }
372 | ]
373 | };
374 |
375 | module.exports = {
376 | hue3,
377 | hue2,
378 | hue1,
379 | sample,
380 | sample2,
381 | sample3,
382 | bug: [
383 | { name: "0", next: [{ index: 1 }, { index: 3 }] },
384 | { name: "1", next: [{ index: 2 }, { index: 3 }] },
385 | { name: "2", next: [] },
386 | { name: "3", next: [] }
387 | ]
388 | };
389 | // export default hue1
390 |
--------------------------------------------------------------------------------