vue+antv g6+element-ui完整流程图

目录

最近一直在研究流程图相关的技术,一次在逛 GitHub 时发现了一个技术栈为 vue+g6+element-ui 的项目,基础功能完好,如 node 与 edge 的托拉拽,主界面如下:

GitHub 链接为:https://github.com/caoyu48/vue-g6-editor

线上访问地址为:http://62.234.69.136/

g6 官方 API 文档:https://antv-g6.gitee.io/zh/docs/manual/introduction

但由于作者没有写代码的说明文档,本文仅仅只是我本人对读该源码的一些理解,如有不同理解还希望各位朋友指出订正。

一、本地运行

首先从上面的 GitHub 网址下载该项目,下载该项目需要的依赖包。这里很多人下载完依赖包之后发现启动报错无法运行,需要注意的是这里

不要使用 cnpm install,一定要使用 npm install!!!具体为什么 cnpm 不行,我也不知道。

二、给连线加上文字 [本人自己增加]

 

1、修改 src/components/DetailPanel/index.vue

<template>
  <div class="detailpannel">
    <div>
      <div v-if="status=='node-selected'" class="pannel" id="node_detailpannel">
        <div class="pannel-title">模型详情</div>
        <div class="block-container">
          <el-row :gutter="10">
            <el-col :span="8">名称</el-col>
            <el-col :span="16">
              <el-input v-model="node.label" @change="handleChangeName" />
            </el-col>
            <el-col :span="8">任意属性</el-col>
            <el-col :span="16">
              <el-input v-model="node.xxx" />
            </el-col>
          </el-row>
        </div>
      </div>
      <div v-if="status==='canvas-selected'" class="pannel" id="canvas_detailpannel">
        <div class="pannel-title">画布</div>
        <div class="block-container">
          <el-checkbox v-model="showGrid" @change="changeGridState">网格对齐</el-checkbox>
        </div>
      </div>
   <!-- 我添加的 -->
<div v-if="status ==='edge-selected'" id="edge_detailpannel" class="pannel"> <div class="pannel-title">连线</div> <div class="block-container"> <el-col :span="8">内容</el-col> <el-col :span="16"> <el-input v-model="edge.label" @change="handleChange" /> </el-col> <el-col :span="8">文字颜色</el-col> <el-col :span="16"> <el-color-picker v-model="textColor" @change="handleChangeColor" /> </el-col> </div> </div> <!-- <div v-if="status==='group-selected'" class="pannel"id="node_detailpannel"> <div class="pannel-title"> 群组详情 </div> <div class="block-container"> <div class="p"> 名称: <el-input v-model="name" /> </div> <div class="p"> 任意属性: <el-input v-model="color" /> </div> </div> </div> --> </div> </div> </template>

<script>
import eventBus from
"@/utils/eventBus";
import Grid from
"@antv/g6/build/grid";
export
default {
data() {
return {
status:
"canvas-selected",
showGrid:
false,
page: {},
graph: {},
item: {},
node: {},
    //【我添加的】
edge:{},
grid:
null,
//【我添加的】
textColor:
'rgba(19, 206, 102, 0.8)'
};
},
created() {
this.init();
this.bindEvent();
},
methods: {
init(){},
bindEvent() {
let self
= this;
eventBus.$on(
"afterAddPage", page => {
self.page
= page;
self.graph
= self.page.graph;
eventBus.$on(
"nodeselectchange", item => {
if (item.select === true && item.target.getType() === "node") {
self.status
= "node-selected";
self.item
= item.target;
self.node
= item.target.getModel();
}
      //【我添加的】
      
else if (item.select === true && item.target.getType() === "edge") {
self.status
= "edge-selected";
self.item
= item.target;
self.edge
=
item.target.getModel();
}

      
else {
self.status
= "canvas-selected";
self.item
= null;
self.node
= null;
}
});
});
},
handleChangeName(e) {
const model
= {
label: e
};
this.graph.update(this.item, model);
},
changeGridState(value) {
if (value) {
this.grid = new Grid();
this.graph.addPlugin(this.grid);
}
else {
this.graph.removePlugin(this.grid);
}
},
  //【我添加的】
handleChange(e) {
const model
= {
label: e
};
console.log(model)
this.graph.update(this.item, model);
},
handleChangeColor(e) {
const model
= {
textColor: e
};
this.graph.update(this
.item, model);
}

}
};
</script>

<style scoped>
.detailpannel
{
height
: 100%;
position
: absolute;
right
: 0px;
z-index
: 2;
background
: #f7f9fb;
width
: 200px;
border-left
: 1px solid #e6e9ed;
}
.detailpannel .block-container
{
padding
: 16px 8px;
}
.block-container .el-col
{
height
: 28px;
display
: flex;
align-items
: center;
margin-bottom
: 10px;
}
.pannel-title
{
height
: 32px;
border-top
: 1px solid #dce3e8;
border-bottom
: 1px solid #dce3e8;
background
: #ebeef2;
color
: #000;
line-height
: 28px;
padding-left
: 12px;
}
</style>

2、修改 src/components/Flow/customEdge.js

import G6 from "@antv/g6/build/g6";
import {uniqueId} from '@/utils'
const MIN_ARROW_SIZE = 3

const customEdge = {
init() {
const dashArray
= [
[
0, 1],
[
0, 2],
[
1, 2],
[
0, 1, 1, 2],
[
0, 2, 1, 2],
[
1, 2, 1, 2],
[
2, 2, 1, 2],
[
3, 2, 1, 2],
[
4, 2, 1, 2]
];

const lineDash </span>= [4,2,1,2<span style="color: rgba(0, 0, 0, 1)">];
const interval </span>= 9<span style="color: rgba(0, 0, 0, 1)">;
G6.registerEdge(</span>'customEdge'<span style="color: rgba(0, 0, 0, 1)">, {
  draw(cfg, group) {
    let sourceNode, targetNode, start, end
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">typeof</span> (cfg.souxrce) === 'string'<span style="color: rgba(0, 0, 0, 1)">) {
      cfg.source </span>=<span style="color: rgba(0, 0, 0, 1)"> cfg.sourceNode
    }
    </span><span style="color: rgba(0, 0, 255, 1)">if</span>(!<span style="color: rgba(0, 0, 0, 1)">cfg.start){
      cfg.start</span>=<span style="color: rgba(0, 0, 0, 1)">{
        x:</span>0<span style="color: rgba(0, 0, 0, 1)">,
        y:</span>17<span style="color: rgba(0, 0, 0, 1)">
      }
    }
    </span><span style="color: rgba(0, 0, 255, 1)">if</span>(!<span style="color: rgba(0, 0, 0, 1)">cfg.end){
      cfg.end</span>=<span style="color: rgba(0, 0, 0, 1)">{
        x:</span>0<span style="color: rgba(0, 0, 0, 1)">,
        y:</span>-17<span style="color: rgba(0, 0, 0, 1)">
      }
    }
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">cfg.source.x) {
      sourceNode </span>=<span style="color: rgba(0, 0, 0, 1)"> cfg.source.getModel()
      start </span>= { x: sourceNode.x + cfg.start.x, y: sourceNode.y +<span style="color: rgba(0, 0, 0, 1)"> cfg.start.y }
    } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
      start </span>=<span style="color: rgba(0, 0, 0, 1)"> cfg.source
    }
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (<span style="color: rgba(0, 0, 255, 1)">typeof</span> (cfg.target) === 'string'<span style="color: rgba(0, 0, 0, 1)">) {
      cfg.target </span>=<span style="color: rgba(0, 0, 0, 1)"> cfg.targetNode
    }
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">cfg.target.x) {

      targetNode </span>=<span style="color: rgba(0, 0, 0, 1)"> cfg.target.getModel()
      end </span>= { x: targetNode.x + cfg.end.x, y: targetNode.y +<span style="color: rgba(0, 0, 0, 1)">  cfg.end.y }
    } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
      end </span>=<span style="color: rgba(0, 0, 0, 1)"> cfg.target
    }

    let path </span>=<span style="color: rgba(0, 0, 0, 1)"> []
    let hgap </span>= Math.abs(end.x -<span style="color: rgba(0, 0, 0, 1)"> start.x)
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (end.x &gt;<span style="color: rgba(0, 0, 0, 1)"> start.x) {
      path </span>=<span style="color: rgba(0, 0, 0, 1)"> [
        [</span>'M'<span style="color: rgba(0, 0, 0, 1)">, start.x, start.y],
        [
          </span>'C'<span style="color: rgba(0, 0, 0, 1)">,
          start.x,
          start.y </span>+ hgap / (hgap / 50<span style="color: rgba(0, 0, 0, 1)">),
          end.x,
          end.y </span>- hgap / (hgap / 50<span style="color: rgba(0, 0, 0, 1)">),
          end.x,
          end.y </span>- 4<span style="color: rgba(0, 0, 0, 1)">
        ],
        [
          </span>'L'<span style="color: rgba(0, 0, 0, 1)">,
          end.x,
          end.y
        ]
      ]
    } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
      path </span>=<span style="color: rgba(0, 0, 0, 1)"> [
        [</span>'M'<span style="color: rgba(0, 0, 0, 1)">, start.x, start.y],
        [
          </span>'C'<span style="color: rgba(0, 0, 0, 1)">,
          start.x,
          start.y </span>+ hgap / (hgap / 50<span style="color: rgba(0, 0, 0, 1)">),
          end.x,
          end.y </span>- hgap / (hgap / 50<span style="color: rgba(0, 0, 0, 1)">),
          end.x,
          end.y </span>- 4<span style="color: rgba(0, 0, 0, 1)">
        ],
        [
          </span>'L'<span style="color: rgba(0, 0, 0, 1)">,
          end.x,
          end.y
        ]
      ]
    }
    let lineWidth </span>= 1<span style="color: rgba(0, 0, 0, 1)">;
    lineWidth </span>= lineWidth &gt; MIN_ARROW_SIZE ?<span style="color: rgba(0, 0, 0, 1)"> lineWidth : MIN_ARROW_SIZE;
    const width </span>= lineWidth * 10 / 3<span style="color: rgba(0, 0, 0, 1)">;
    const halfHeight </span>= lineWidth * 4 / 3<span style="color: rgba(0, 0, 0, 1)">;
    const radius </span>= lineWidth * 4<span style="color: rgba(0, 0, 0, 1)">;
    const endArrowPath </span>=<span style="color: rgba(0, 0, 0, 1)"> [
      [</span>'M', -<span style="color: rgba(0, 0, 0, 1)">width, halfHeight],
      [</span>'L', 0, 0<span style="color: rgba(0, 0, 0, 1)">],
      [</span>'L', -width, -<span style="color: rgba(0, 0, 0, 1)">halfHeight],
      [</span>'A', radius, radius, 0, 0, 1, -<span style="color: rgba(0, 0, 0, 1)">width, halfHeight],
      [</span>'Z'<span style="color: rgba(0, 0, 0, 1)">]
    ];
    const keyShape </span>= group.addShape('path'<span style="color: rgba(0, 0, 0, 1)">, {
      attrs: {
        id: </span>'edge' +<span style="color: rgba(0, 0, 0, 1)"> uniqueId(),
        path: path,
        stroke: </span>'#b8c3ce'<span style="color: rgba(0, 0, 0, 1)">,
        lineAppendWidth: </span>10<span style="color: rgba(0, 0, 0, 1)">,
        endArrow: {
          path: endArrowPath,
        }
      }
    });<br>     <strong><span style="color: rgba(51, 153, 102, 1)">//此处是我修改的,增加连线的样式即线上文本
    </span></strong></span><strong><span style="color: rgba(255, 0, 0, 1)">if (cfg.label) {
      group.addShape('text', {
        attrs: {
          id: 'edgeText' + uniqueId(),
          x: end.x - (end.x - start.x) / 2,
          y: end.y - (end.y - start.y) / 2,
          text: cfg.label,
          fill: cfg.textColor ? cfg.textColor : '#000000'
        }
      })
    }
    </span></strong><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> keyShape
  },
  afterDraw(cfg, group) {
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (cfg.source.getModel().isDoingStart &amp;&amp;<span style="color: rgba(0, 0, 0, 1)"> cfg.target.getModel().isDoingEnd) {
      const shape </span>= group.get('children')[0<span style="color: rgba(0, 0, 0, 1)">];
      const length </span>= shape.getTotalLength(); <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> G 增加了 totalLength 的接口</span>
      let totalArray =<span style="color: rgba(0, 0, 0, 1)"> [];
      </span><span style="color: rgba(0, 0, 255, 1)">for</span> (<span style="color: rgba(0, 0, 255, 1)">var</span> i = 0; i &lt; length; i +=<span style="color: rgba(0, 0, 0, 1)"> interval) {
        totalArray </span>=<span style="color: rgba(0, 0, 0, 1)"> totalArray.concat(lineDash);
      }
      let index </span>= 0<span style="color: rgba(0, 0, 0, 1)">;
      shape.animate({
        onFrame() {
          const cfg </span>=<span style="color: rgba(0, 0, 0, 1)"> {
            lineDash: dashArray[index].concat(totalArray)
          };
          index </span>= (index + 1) %<span style="color: rgba(0, 0, 0, 1)"> interval;
          </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> cfg;
        },
        repeat: </span><span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">
      }, </span>3000<span style="color: rgba(0, 0, 0, 1)">);
    }
  },
  setState(name, value, item) {
    const group </span>=<span style="color: rgba(0, 0, 0, 1)"> item.getContainer();
    const shape </span>= group.get("children")[0<span style="color: rgba(0, 0, 0, 1)">];
    const selectStyles </span>= () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      shape.attr(</span>"stroke", "#6ab7ff"<span style="color: rgba(0, 0, 0, 1)">);
    };
    const unSelectStyles </span>= () =&gt;<span style="color: rgba(0, 0, 0, 1)"> {
      shape.attr(</span>"stroke", "#b8c3ce"<span style="color: rgba(0, 0, 0, 1)">);
    };

    </span><span style="color: rgba(0, 0, 255, 1)">switch</span><span style="color: rgba(0, 0, 0, 1)"> (name) {
      </span><span style="color: rgba(0, 0, 255, 1)">case</span> "selected"<span style="color: rgba(0, 0, 0, 1)">:
      </span><span style="color: rgba(0, 0, 255, 1)">case</span> "hover"<span style="color: rgba(0, 0, 0, 1)">:
        </span><span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (value) {
          selectStyles()
        } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
          unSelectStyles()
        }
        </span><span style="color: rgba(0, 0, 255, 1)">break</span><span style="color: rgba(0, 0, 0, 1)">;
    }
  }
});
G6.registerEdge(</span>'link-edge'<span style="color: rgba(0, 0, 0, 1)">, {
  draw(cfg, group) {
    let sourceNode, targetNode, start, end
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">cfg.source.x) {
      sourceNode </span>=<span style="color: rgba(0, 0, 0, 1)"> cfg.source.getModel()
      start </span>= { x: sourceNode.x + cfg.start.x, y: sourceNode.y +<span style="color: rgba(0, 0, 0, 1)"> cfg.start.y }
    } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
      start </span>=<span style="color: rgba(0, 0, 0, 1)"> cfg.source
    }
    </span><span style="color: rgba(0, 0, 255, 1)">if</span> (!<span style="color: rgba(0, 0, 0, 1)">cfg.target.x) {
      targetNode </span>=<span style="color: rgba(0, 0, 0, 1)"> cfg.target.getModel()
      end </span>= { x: targetNode.x + cfg.end.x, y: targetNode.y +<span style="color: rgba(0, 0, 0, 1)"> cfg.end.y }
    } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
      end </span>=<span style="color: rgba(0, 0, 0, 1)"> cfg.target
    }

    let path </span>=<span style="color: rgba(0, 0, 0, 1)"> []
    path </span>=<span style="color: rgba(0, 0, 0, 1)"> [
      [</span>'M'<span style="color: rgba(0, 0, 0, 1)">, start.x, start.y],
      [</span>'L'<span style="color: rgba(0, 0, 0, 1)">, end.x, end.y]
    ]
    const keyShape </span>= group.addShape('path'<span style="color: rgba(0, 0, 0, 1)">, {
      attrs: {
        id: </span>'edge' +<span style="color: rgba(0, 0, 0, 1)"> uniqueId(),
        path: path,
        stroke: </span>'#1890FF'<span style="color: rgba(0, 0, 0, 1)">,
        strokeOpacity: </span>0.9<span style="color: rgba(0, 0, 0, 1)">,
        lineDash: [</span>5, 5<span style="color: rgba(0, 0, 0, 1)">]
      }
    });
    </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> keyShape
  },
});

}
}

export default customEdge

三、发现的 BUG

 

 

当删除文本框中的内容时,会发现连节点也删除了,解决办法就是修改 src/behavior/keyboard.js

 

作者:不若为止
欢迎任何形式的转载,但请务必注明出处。
限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。