最近一直在研究流程图相关的技术,一次在逛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> 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 = [4,2,1,2]; const interval = 9; G6.registerEdge('customEdge', { draw(cfg, group) { let sourceNode, targetNode, start, end if (typeof (cfg.souxrce) === 'string') { cfg.source = cfg.sourceNode } if(!cfg.start){ cfg.start={ x:0, y:17 } } if(!cfg.end){ cfg.end={ x:0, y:-17 } } if (!cfg.source.x) { sourceNode = cfg.source.getModel() start = { x: sourceNode.x + cfg.start.x, y: sourceNode.y + cfg.start.y } } else { start = cfg.source } if (typeof (cfg.target) === 'string') { cfg.target = cfg.targetNode } if (!cfg.target.x) { targetNode = cfg.target.getModel() end = { x: targetNode.x + cfg.end.x, y: targetNode.y + cfg.end.y } } else { end = cfg.target } let path = [] let hgap = Math.abs(end.x - start.x) if (end.x > start.x) { path = [ ['M', start.x, start.y], [ 'C', start.x, start.y + hgap / (hgap / 50), end.x, end.y - hgap / (hgap / 50), end.x, end.y - 4 ], [ 'L', end.x, end.y ] ] } else { path = [ ['M', start.x, start.y], [ 'C', start.x, start.y + hgap / (hgap / 50), end.x, end.y - hgap / (hgap / 50), end.x, end.y - 4 ], [ 'L', end.x, end.y ] ] } let lineWidth = 1; lineWidth = lineWidth > MIN_ARROW_SIZE ? lineWidth : MIN_ARROW_SIZE; const width = lineWidth * 10 / 3; const halfHeight = lineWidth * 4 / 3; const radius = lineWidth * 4; const endArrowPath = [ ['M', -width, halfHeight], ['L', 0, 0], ['L', -width, -halfHeight], ['A', radius, radius, 0, 0, 1, -width, halfHeight], ['Z'] ]; const keyShape = group.addShape('path', { attrs: { id: 'edge' + uniqueId(), path: path, stroke: '#b8c3ce', lineAppendWidth: 10, endArrow: { path: endArrowPath, } } });
//此处是我修改的,增加连线的样式即线上文本 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' } }) } return keyShape }, afterDraw(cfg, group) { if (cfg.source.getModel().isDoingStart && cfg.target.getModel().isDoingEnd) { const shape = group.get('children')[0]; const length = shape.getTotalLength(); // G 增加了 totalLength 的接口 let totalArray = []; for (var i = 0; i < length; i += interval) { totalArray = totalArray.concat(lineDash); } let index = 0; shape.animate({ onFrame() { const cfg = { lineDash: dashArray[index].concat(totalArray) }; index = (index + 1) % interval; return cfg; }, repeat: true }, 3000); } }, setState(name, value, item) { const group = item.getContainer(); const shape = group.get("children")[0]; const selectStyles = () => { shape.attr("stroke", "#6ab7ff"); }; const unSelectStyles = () => { shape.attr("stroke", "#b8c3ce"); }; switch (name) { case "selected": case "hover": if (value) { selectStyles() } else { unSelectStyles() } break; } } }); G6.registerEdge('link-edge', { draw(cfg, group) { let sourceNode, targetNode, start, end if (!cfg.source.x) { sourceNode = cfg.source.getModel() start = { x: sourceNode.x + cfg.start.x, y: sourceNode.y + cfg.start.y } } else { start = cfg.source } if (!cfg.target.x) { targetNode = cfg.target.getModel() end = { x: targetNode.x + cfg.end.x, y: targetNode.y + cfg.end.y } } else { end = cfg.target } let path = [] path = [ ['M', start.x, start.y], ['L', end.x, end.y] ] const keyShape = group.addShape('path', { attrs: { id: 'edge' + uniqueId(), path: path, stroke: '#1890FF', strokeOpacity: 0.9, lineDash: [5, 5] } }); return keyShape }, }); } } export default customEdge
三、发现的BUG
当删除文本框中的内容时,会发现连节点也删除了,解决办法就是修改src/behavior/keyboard.js