AntV X6有个问题暂时没解,可以见AntV X6的问题
看了其他人的一些文章,感觉antv x6 v1版本制作复杂节点会比较复杂(在vue上)v2版本会好很多,但是目前还存在一些问题,且我作为半拉前端暂时也还没有时间去转型到vue3+ts的组合。官网上的例子又都是react的,转换起来属实是费劲。被折磨了好久 /(ㄒoㄒ)/
找了一圈又发现了一个适合vue体制的流程图插件drawflow
内容基本上都在github中,配合作者的beautiful.css可以让节点更好看一些。这个插件对vue组件的支持性好了很多。
本文主要解决了使用VUE组件作为node后,数据传递的问题。
使用
安装
npm i drawflow
引入
import Drawflow from 'drawflow'
import styleDrawflow from 'drawflow/dist/drawflow.min.css'
创建画布容器
<div id="drawflow"></div>
初始化画布:
var id = document.getElementById("drawflow");
const editor = new Drawflow(id);
editor.start();
vue2可以采用这种方式:
重要!!!重要!!!原因请见问题一
import Vue from 'vue'
// Pass render Vue
this.editor = new Drawflow(id, Vue, this);
vue3采用以下方式:
import { h, getCurrentInstance, render } from 'vue'
const Vue = { version: 3, h, render };
this.editor = new Drawflow(id, Vue);
// Pass render Vue 3 Instance
const internalInstance = getCurrentInstance()
editor.value = new Drawflow(id, Vue, internalInstance.appContext.app._context);
editor可选配置
Parameter | Type | Default | Description |
---|---|---|---|
reroute | Boolean | false | Active reroute |
reroute_fix_curvature | Boolean | false | Fix adding points |
curvature | Number | 0.5 | Curvature |
reroute_curvature_start_end | Number | 0.5 | Curvature reroute first point and las point |
reroute_curvature | Number | 0.5 | Curvature reroute |
reroute_width | Number | 6 | Width of reroute |
line_path | Number | 5 | Width of line |
force_first_input | Boolean | false | Force the first input to drop the connection on top of the node |
editor_mode | Text | edit | edit for edit, fixed for nodes fixed but their input fields available, view for view only |
zoom | Number | 1 | Default zoom |
zoom_max | Number | 1.6 | Default zoom max |
zoom_min | Number | 0.5 | Default zoom min |
zoom_value | Number | 0.1 | Default zoom value update |
zoom_last_value | Number | 1 | Default zoom last value |
draggable_inputs | Boolean | true | Drag nodes on click inputs |
useuuid | Boolean | false | Use UUID as node ID instead of integer index. Only affect newly created nodes, do not affect imported nodes |
添加节点
editor.addNode(name, inputs, outputs, posx, posy, class, data, html);
Parameter | Type | Description |
---|---|---|
name | text | Name of module |
inputs | number | Number of de inputs |
outputs | number | Number of de outputs |
pos_x | number | Position on start node left |
pos_y | number | Position on start node top |
class | text | Added classname to de node. Multiple classnames separated by space |
data | json | Data passed to node |
html | text | HTML drawn on node or name of register node. |
typenode | boolean & text | Default false, true for Object HTML, vue for vue |
主要区分在html,typenode这边,如果是html节点,可以按以下方式:
var google = `
<div>
<div class="title-box"><i class="fab fa-google-drive"></i> Google Search </div>
</div>
`;
this.editor.addNode('google', 1, 1, pos_x, pos_y, 'google', {}, google);
第二和第三个参数是输入桩的数量和输出桩的数量,与antv x6不同的是,这里桩的配置比较少,不能自定义桩信息。他会默认均匀分布在节点左侧(input)和右侧(output)
如果是vue组件作为节点,可以按以下方式:
//先注册节点
this.editor.registerNode('startNode', startNode, {}, {});
//添加节点
this.editor.addNode('startNode', 0, 1, pos_x, pos_y, 'startNode', {}, 'startNode', 'vue');
数据传递
往节点传输数据很方便,并且自带有双向绑定的方式。
HTML节点
例如:
var template = `
<div>
<div class="title-box"><i class="fas fa-code"></i> Template</div>
<div class="box">
Ger Vars
<textarea df-template></textarea>
Output template with vars
</div>
</div>
`;
editor.addNode('template', 1, 1, pos_x, pos_y, 'template', { "template": 'Write your template'}, template );
这样会渲染出一个带有textarea的节点,在addNode时,倒数第三个参数传递了一个对象,里面有template属性,可以看到在textarea上有一个df-template,这样会形成双向绑定(输入框内容和节点配置进行绑定)
导出的数据将会是如下:
{
"drawflow": {
"Home": {
"data": {
"1": {
"id": 1,
"name": "template",
"data": {
"template": "Write your template"
},
"class": "template",
"html": "<div><div class='title-box'><i class='fas fa-code'></i> Template</div><div class='box'>Ger Vars<textarea df-template></textarea>Output template with var</div></div>",
"typenode": false,
"inputs": {
"input_1": {
"connections": []
}
},
"outputs": {
"output_1": {
"connections": []
}
},
"pos_x": 80,
"pos_y": 80
},
}
}
}
}
在节点的data区,有我们传入的数据。当然,如果直接在节点中对textarea的内容进行编辑,也会使导出的json发生变化(我原称之为双向绑定)。
VUE节点
vue节点同html节点,可以通过df-xxx的配置来获取addNode时的参数。但是这里有个问题,有一些复杂节点,需要编辑一些东西(数据可能是json格式给后端的),这里就不适合让用户自己输入了。df-xxx的形式只能支持input、textarea或select双向绑定,并且是用户输入行为使值发生变化才能真正改变导出的json中data块发生变化。
说人话就是,如果想要使用js改变input的value是不生效的。editor.export()后的数据还是初始的数据。
如果是这种情况对业务复杂的节点是不友好的,这里我们需要借助vue的事件总线来解决这个问题。
//在vue组件中声明
mounted() {
//节点基础通信
this.$nextTick(() => {
const id = this.$el.parentElement.parentElement.id;
//获得节点ID
this.nodeId = id;
bus.$emit('getData', id);
});
//发送了数据过来,保存数据。
bus.$on('sendData', (data) => {
if (data.id === this.nodeId) {
if (JSON.stringify(data.dataNode) != '{}') {
console.log('填充data')
this.dataNode = data.dataNode;
}
bus.$emit('refreshNode', this.nodeId);
}
});
},
//组件中保存数据
bus.$emit('saveData', this.nodeId, this.dataNode);
bus.$emit('refreshNode', this.nodeId);
//在画布中声明
mounted() {
//获取到节点ID,把节点目前的data发送过去
bus.$on('getData', (id) => {
const dataNode = this.editor.getNodeFromId(id.slice(5)).data;
bus.$emit('sendData', { id, dataNode })
})
//收到节点组件发来的保存数据请求
bus.$on('saveData', (id, data) => {
this.editor.drawflow.drawflow['Home'].data[id.slice(5)].data = data;
})
//重新渲染节点
bus.$on('refreshNode', (id) => {
this.editor.updateConnectionNodes(id);
})
},
遇到的问题
问题一 TypeError: Cannot read properties of null (reading ‘version’)
错误内容如下:
TypeError: Cannot read properties of null (reading 'version')
at i.addNode (drawflow.min.js:1:30657)
at VueComponent.loadNode (cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/views/dng/index.vue?vue&type=script&lang=js:366:19)
at VueComponent.initEditor (cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/views/dng/index.vue?vue&type=script&lang=js:358:12)
at VueComponent.mounted (cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/cache-loader/dist/cjs.js?!./node_modules/vue-loader/lib/index.js?!./src/views/dng/index.vue?vue&type=script&lang=js:348:10)
at invokeWithErrorHandling (vue.runtime.esm.js:1853:57)
at callHook (vue.runtime.esm.js:4213:7)
at Object.insert (vue.runtime.esm.js:3136:7)
at invokeInsertHook (vue.runtime.esm.js:6336:28)
at VueComponent.patch [as __patch__] (vue.runtime.esm.js:6555:5)
at Vue._update (vue.runtime.esm.js:3942:19)
解决这个问题的方案,请使用vue2的方式初始化画布!!!
如果缺省了new Drawflow后面的两个参数,会出现问题。
import Vue from 'vue'
// Pass render Vue
this.editor = new Drawflow(id, Vue, this);