LogicFlow是什么
LogicFlow是一款流程图编辑框架,提供了一系列流程图交互、编辑所必需的功能和灵活的节点自定义、插件等拓展机制。LogicFlow支持前端自定义开发各种逻辑编排场景,如流程图、ER图、BPMN流程等。在工作审批流配置、机器人逻辑编排、无代码平台流程配置都有较好的应用。
特性
- 可视化模型:通过 LogicFlow 提供的直观可视化界面,用户可以轻松创建、编辑和管理复杂的逻辑流程图。
- 高可定制性:用户可以根据自己的需要定制节点、连接器和样式,创建符合特定用例的定制逻辑流程图。
- 自执行引擎:执行引擎支持浏览器端执行流程图逻辑,为无代码执行提供新思路。
创建logicflow的vue项目
创建vue项目
vue create mylogicflow
我选择vue2
idea打开项目
执行安装依赖命令
npm install
执行启动命令
npm run serve
打开如上页面证明vue项目创建成功
安装logicflow依赖
npm install @logicflow/core --save
插件包(不使用插件时不需要引入)推荐引入
npm install @logicflow/extension --save
创建draw.vue
创建pages文件夹,并在pages下面创建draw.vue
内容如下
<template>
<div class="container" ref="container"></div>
</template>
<script>
import LogicFlow from "@logicflow/core";
import "@logicflow/core/lib/style/index.css";
// import "@logicflow/core/dist/style/index.css"; // 2.0版本前的引入方式
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: "draw",
mounted() {
this.lf = new LogicFlow({
container: this.$refs.container,
grid: true,
});
this.lf.render();
},
};
</script>
<style scoped>
.container {
display: flex;
height: 800px;
flex-grow: 1; /*铺满剩余空间*/
border: 3px solid #ababab;
overflow: hidden;
}
</style>
安装router
安装router
npm install vue-router@3.1.3 --force
配置 router
在 src
文件夹下创建一个 router
文件夹,并在其中创建一个 index.js
文件:
在index.js中添加如下代码
// 引入Vue核心库
import Vue from 'vue'
// 引入vue-router
import VueRouter from 'vue-router'
// 应用vue-router插件
Vue.use(VueRouter)
// 引入组件(方式一)
import Home from '../pages/draw.vue'
// 配置路由
const routes = [
{
// 路径,/ 表示首页
path: '/',
// 使用组件(方式一)
component: Home
}
// ,
// {
// // 路径
// path: '/about',
// // 引入并使用组件(方式二)
// component: () => import('../views/About.vue')
// }
]
// 创建路由器
const router = new VueRouter({
routes
})
// 导出路由器
export default router
在 main.js
中引入并使用 Vue Router
import Vue from 'vue'
import App from './App.vue'
import router from './router'; // 引入 router 配置
Vue.config.productionTip = false
new Vue({
router,
render: h => h(App),
}).$mount('#app')
修改app.vue
<template>
<router-view></router-view>
</template>
<script>
export default {
name: 'App',
components: {
}
}
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
安装element ui
npm install element-ui@2.15.7
在mian.js中使用
import Vue from 'vue'
import App from './App.vue'
import router from './router'; // 引入 router 配置
Vue.config.productionTip = false
import Element from 'element-ui';
import 'element-ui/lib/theme-chalk/index.css';
Vue.use(Element)
new Vue({
router,
render: h => h(App),
}).$mount('#app')
启动项目
logicflow实操
添加控件(render之前)
系统默认控件
<template>
<div class="container" ref="container"></div>
</template>
<script>
import LogicFlow from "@logicflow/core";
import "@logicflow/core/lib/style/index.css";
import { Control } from "@logicflow/extension";
import "@logicflow/extension/lib/index.css";
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: "draw",
mounted() {
this.lf = new LogicFlow({
container: this.$refs.container,
plugins: [Control],
grid: true,
});
this.lf.render();
},
};
</script>
<style scoped>
.container {
display: flex;
height: 800px;
flex-grow: 1; /*铺满剩余空间*/
border: 3px solid #ababab;
overflow: hidden;
}
</style>
为什么会有上面默认控件,查看源码
node_modules/@logicflow/extension/src/components/control/index.ts
自定义控件
<template>
<el-container>
<div class="container" ref="container"></div>
</el-container>
</template>
<script>
import LogicFlow from "@logicflow/core";
import "@logicflow/core/lib/style/index.css";
import { Control } from "@logicflow/extension";
import "@logicflow/extension/lib/index.css";
import '@logicflow/extension/lib/style/index.css';
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: "draw",
mounted() {
this.lf = new LogicFlow({
container: this.$refs.container,
plugins: [Control],
grid: true,
});
this.init()
},
methods: {
init(){//el-icon-delete是element图标 icon-delete是自定义属性
this.lf.extension.control.addItem({
key: 'clear',
iconClass: 'el-icon-delete icon-delete',
title: "清空画板",
text: "清空画板",
onClick: () => {
console.log('清空画板')
}
})
this.lf.render();
}
},
};
</script>
<style scoped>
.container {
display: flex;
height: 800px;
flex-grow: 1; /*铺满剩余空间*/
border: 3px solid #ababab;
overflow: hidden;
}
.icon-delete {
font-size: 30px;
}
</style>
删除控件 (render之前)
<template>
<el-container>
<div class="container" ref="container"></div>
</el-container>
</template>
<script>
import LogicFlow from "@logicflow/core";
import "@logicflow/core/lib/style/index.css";
import { Control } from "@logicflow/extension";
import "@logicflow/extension/lib/index.css";
import '@logicflow/extension/lib/style/index.css';
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: "draw",
mounted() {
this.lf = new LogicFlow({
container: this.$refs.container,
plugins: [Control],
grid: true,
});
this.init()
},
methods: {
init(){//el-icon-delete是element图标 icon-delete是自定义属性
this.lf.extension.control.addItem({
key: 'clear',
iconClass: 'el-icon-delete icon-delete',
title: "清空画板",
text: "清空画板",
onClick: () => {
console.log('清空画板')
}
})
//删除缩小按钮
this.lf.extension.control.removeItem('zoom-out')
this.lf.render();
}
},
};
</script>
<style scoped>
.container {
display: flex;
height: 800px;
flex-grow: 1; /*铺满剩余空间*/
border: 3px solid #ababab;
overflow: hidden;
}
.icon-delete {
font-size: 30px;
}
</style>
添加拖拽面板
<template>
<el-container>
<div class="container" ref="container"></div>
</el-container>
</template>
<script>
import LogicFlow from "@logicflow/core";
import "@logicflow/core/lib/style/index.css";
import { Control,DndPanel, SelectionSelect } from "@logicflow/extension";
import "@logicflow/extension/lib/index.css";
import '@logicflow/extension/lib/style/index.css';
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: "draw",
mounted() {
this.lf = new LogicFlow({
container: this.$refs.container,
plugins: [Control,DndPanel, SelectionSelect],
grid: true,
});
this.init()
},
methods: {
init(){
var lf = this.lf
lf.extension.dndPanel.setPatternItems([
{
label: '选区',
icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAAOVJREFUOBGtVMENwzAIjKP++2026ETdpv10iy7WFbqFyyW6GBywLCv5gI+Dw2Bluj1znuSjhb99Gkn6QILDY2imo60p8nsnc9bEo3+QJ+AKHfMdZHnl78wyTnyHZD53Zzx73MRSgYvnqgCUHj6gwdck7Zsp1VOrz0Uz8NbKunzAW+Gu4fYW28bUYutYlzSa7B84Fh7d1kjLwhcSdYAYrdkMQVpsBr5XgDGuXwQfQr0y9zwLda+DUYXLaGKdd2ZTtvbolaO87pdo24hP7ov16N0zArH1ur3iwJpXxm+v7oAJNR4JEP8DoAuSFEkYH7cAAAAASUVORK5CYII=',
callback: () => {
lf.extension.selectionSelect.openSelectionSelect();
lf.once('selection:selected', () => {
lf.extension.selectionSelect.closeSelectionSelect();
});
}
},
{
type: 'circle',
text: '开始',
label: '开始节点',
icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAAnBJREFUOBGdVL1rU1EcPfdGBddmaZLiEhdx1MHZQXApraCzQ7GKLgoRBxMfcRELuihWKcXFRcEWF8HBf0DdDCKYRZpnl7p0svLe9Zzbd29eQhTbC8nv+9zf130AT63jvooOGS8Vf9Nt5zxba7sXQwODfkWpkbjTQfCGUd9gIp3uuPP8bZ946g56dYQvnBg+b1HB8VIQmMFrazKcKSvFW2dQTxJnJdQ77urmXWOMBCmXM2Rke4S7UAW+/8ywwFoewmBps2tu7mbTdp8VMOkIRAkKfrVawalJTtIliclFbaOBqa0M2xImHeVIfd/nKAfVq/LGnPss5Kh00VEdSzfwnBXPUpmykNss4lUI9C1ga+8PNrBD5YeqRY2Zz8PhjooIbfJXjowvQJBqkmEkVnktWhwu2SM7SMx7Cj0N9IC0oQXRo8xwAGzQms+xrB/nNSUWVveI48ayrFGyC2+E2C+aWrZHXvOuz+CiV6iycWe1Rd1Q6+QUG07nb5SbPrL4426d+9E1axKjY3AoRrlEeSQo2Eu0T6BWAAr6COhTcWjRaYfKG5csnvytvUr/WY4rrPMB53Uo7jZRjXaG6/CFfNMaXEu75nG47X+oepU7PKJvvzGDY1YLSKHJrK7vFUwXKkaxwhCW3u+sDFMVrIju54RYYbFKpALZAo7sB6wcKyyrd+aBMryMT2gPyD6GsQoRFkGHr14TthZni9ck0z+Pnmee460mHXbRAypKNy3nuMdrWgVKj8YVV8E7PSzp1BZ9SJnJAsXdryw/h5ctboUVi4AFiCd+lQaYMw5z3LGTBKjLQOeUF35k89f58Vv/tGh+l+PE/wG0rgfIUbZK5AAAAABJRU5ErkJggg==',
},
{
type: 'rect',
label: '用户任务',
icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAEFVwZaAAAABGdBTUEAALGPC/xhBQAAAqlJREFUOBF9VM9rE0EUfrMJNUKLihGbpLGtaCOIR8VjQMGDePCgCCIiCNqzCAp2MyYUCXhUtF5E0D+g1t48qAd7CCLqQUQKEWkStcEfVGlLdp/fm3aW2QQdyLzf33zz5m2IsAZ9XhDpyaaIZkTS4ASzK41TFao88GuJ3hsr2pAbipHxuSYyKRugagICGANkfFnNh3HeE2N0b3nN2cgnpcictw5veJIzxmDamSlxxQZicq/mflxhbaH8BLRbuRwNtZp0JAhoplVRUdzmCe/vO27wFuuA3S5qXruGdboy5/PRGFsbFGKo/haRtQHIrM83bVeTrOgNhZReWaYGnE4aUQgTJNvijJFF4jQ8BxJE5xfKatZWmZcTQ+BVgh7s8SgPlCkcec4mGTmieTP4xd7PcpIEg1TX6gdeLW8rTVMVLVvb7ctXoH0Cydl2QOPJBG21STE5OsnbweVYzAnD3A7PVILuY0yiiyDwSm2g441r6rMSgp6iK42yqroI2QoXeJVeA+YeZSa47gZdXaZWQKTrG93rukk/l2Al6Kzh5AZEl7dDQy+JjgFahQjRopSxPbrbvK7GRe9ePWBo1wcU7sYrFZtavXALwGw/7Dnc50urrHJuTPSoO2IMV3gUQGNg87IbSOIY9BpiT9HV7FCZ94nPXb3MSnwHn/FFFE1vG6DTby+r31KAkUktB3Qf6ikUPWxW1BkXSPQeMHHiW0+HAd2GelJsZz1OJegCxqzl+CLVHa/IibuHeJ1HAKzhuDR+ymNaRFM+4jU6UWKXorRmbyqkq/D76FffevwdCp+jN3UAN/C9JRVTDuOxC/oh+EdMnqIOrlYteKSfadVRGLJFJPSB/ti/6K8f0CNymg/iH2gO/f0DwE0yjAFO6l8JaR5j0VPwPwfaYHqOqrCI319WzwhwzNW/aQAAAABJRU5ErkJggg==',
className: 'important-node'
},
{
type: 'rect',
label: '系统任务',
icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAEFVwZaAAAABGdBTUEAALGPC/xhBQAAAqlJREFUOBF9VM9rE0EUfrMJNUKLihGbpLGtaCOIR8VjQMGDePCgCCIiCNqzCAp2MyYUCXhUtF5E0D+g1t48qAd7CCLqQUQKEWkStcEfVGlLdp/fm3aW2QQdyLzf33zz5m2IsAZ9XhDpyaaIZkTS4ASzK41TFao88GuJ3hsr2pAbipHxuSYyKRugagICGANkfFnNh3HeE2N0b3nN2cgnpcictw5veJIzxmDamSlxxQZicq/mflxhbaH8BLRbuRwNtZp0JAhoplVRUdzmCe/vO27wFuuA3S5qXruGdboy5/PRGFsbFGKo/haRtQHIrM83bVeTrOgNhZReWaYGnE4aUQgTJNvijJFF4jQ8BxJE5xfKatZWmZcTQ+BVgh7s8SgPlCkcec4mGTmieTP4xd7PcpIEg1TX6gdeLW8rTVMVLVvb7ctXoH0Cydl2QOPJBG21STE5OsnbweVYzAnD3A7PVILuY0yiiyDwSm2g441r6rMSgp6iK42yqroI2QoXeJVeA+YeZSa47gZdXaZWQKTrG93rukk/l2Al6Kzh5AZEl7dDQy+JjgFahQjRopSxPbrbvK7GRe9ePWBo1wcU7sYrFZtavXALwGw/7Dnc50urrHJuTPSoO2IMV3gUQGNg87IbSOIY9BpiT9HV7FCZ94nPXb3MSnwHn/FFFE1vG6DTby+r31KAkUktB3Qf6ikUPWxW1BkXSPQeMHHiW0+HAd2GelJsZz1OJegCxqzl+CLVHa/IibuHeJ1HAKzhuDR+ymNaRFM+4jU6UWKXorRmbyqkq/D76FffevwdCp+jN3UAN/C9JRVTDuOxC/oh+EdMnqIOrlYteKSfadVRGLJFJPSB/ti/6K8f0CNymg/iH2gO/f0DwE0yjAFO6l8JaR5j0VPwPwfaYHqOqrCI319WzwhwzNW/aQAAAABJRU5ErkJggg==',
className: 'import_icon'
},
{
type: 'diamond',
label: '条件判断',
icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABUAAAAVCAYAAAHeEJUAAAAABGdBTUEAALGPC/xhBQAAAvVJREFUOBGNVEFrE0EU/mY3bQoiFlOkaUJrQUQoWMGePLX24EH0IIoHKQiCV0G8iE1covgLiqA/QTzVm1JPogc9tIJYFaQtlhQxqYjSpunu+L7JvmUTU3AgmTfvffPNN++9WSA1DO182f6xwILzD5btfAoQmwL5KJEwiQyVbSVZ0IgRyV6PTpIJ81E5ZvqfHQR0HUOBHW4L5Et2kQ6Zf7iAOhTFAA8s0pEP7AXO1uAA52SbqGk6h/6J45LaLhO64ByfcUzM39V7ZiAdS2yCePPEIQYvTUHqM/n7dgQNfBKWPjpF4ISk8q3J4nB11qw6X8l+FsF3EhlkEMfrjIer3wJTLwS2aCNcj4DbGxXTw00JmAuO+Ni6bBxVUCvS5d9aa04+so4pHW5jLTywuXAL7jJ+D06sl82Sgl2JuVBQn498zkc2bGKxULHjCnSMadBKYDYYHAtsby1EQ5lNGrQd4Y3v4Zo0XdGEmDno46yCM9Tk+RiJmUYHS/aXHPNTcjxcbTFna000PFJHIVZ5lFRqRpJWk9/+QtlOUYJj9HG5pVFEU7zqIYDVsw2s+AJaD8wTd2umgSCCyUxgGsS1Y6TBwXQQTFuZaHcd8gAGioE90hlsY+wMcs30RduYtxanjMGal8H5dMW67dmT1JFtYUEe8LiQLRsPZ6IIc7A4J5tqco3T0pnv/4u0kyzrYUq7gASuEyI8VXKvB9Odytv6jS/PNaZBln0nioJG/AVQRZvApOdhjj3Jt8QC8Im09SafwdBdvIpztpxWxpeKCC+EsFdS8DCyuCn2munFpL7ctHKp+Xc5cMybeIyMAN33SPL3ZR9QV1XVwLyzHm6Iv0/yeUuUb7PPlZC4D4HZkeu6dpF4v9j9MreGtMbxMMRLIcjJic9yHi7WQ3yVKzZVWUr5UrViJvn1FfUlwe/KYVfYyWRLSGNu16hR01U9IacajXPei0wx/5BqgInvJN+MMNtNme7ReU9SBbgntovn0kKHpFg7UogZvaZiOue/q1SBo9ktHzQAAAAASUVORK5CYII=',
},
{
type: 'circle',
text: '结束',
label: '结束节点',
icon: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAYAAAH6ji2bAAAABGdBTUEAALGPC/xhBQAAA1BJREFUOBFtVE1IVUEYPXOf+tq40Y3vPcmFIdSjIorWoRG0ERWUgnb5FwVhYQSl72oUoZAboxKNFtWiwKRN0M+jpfSzqJAQclHo001tKkjl3emc8V69igP3znzfnO/M9zcDcKT67azmjYWTwl9Vn7Vumeqzj1DVb6cleQY4oAVnIOPb+mKAGxQmKI5CWNJ2aLPatxWa3aB9K7/fB+/Z0jUF6TmMlFLQqrkECWQzOZxYGjTlOl8eeKaIY5yHnFn486xBustDjWT6dG7pmjHOJd+33t0iitTPkK6tEvjxq4h2MozQ6WFSX/LkDUGfFwfhEZj1Auz/U4pyAi5Sznd7uKzznXeVHlI/Aywmk6j7fsUsEuCGADrWARXXwjxWQsUbIupDHJI7kF5dRktg0eN81IbiZXiTESic50iwS+t1oJgL83jAiBupLDCQqwziaWSoAFSeIR3P5Xv5az00wyIn35QRYTwdSYbz8pH8fxUUAtxnFvYmEmgI0wYXUXcCCSpeEVpXlsRhBnCEATxWylL9+EKCAYhe1NGstUa6356kS9NVvt3DU2fd+Wtbm/+lSbylJqsqkSm9CRhvoJVlvKPvF1RKY/FcPn5j4UfIMLn8D4UYb54BNsilTDXKnF4CfTobA0FpoW/LSp306wkXM+XaOJhZaFkcNM82ASNAWMrhrUbRfmyeI1FvRBTpN06WKxa9BK0o2E4Pd3zfBBEwPsv9sQBnmLVbLEIZ/Xe9LYwJu/Er17W6HYVBc7vmuk0xUQ+pqxdom5Fnp55SiytXLPYoMXNM4u4SNSCFWnrVIzKG3EGyMXo6n/BQOe+bX3FClY4PwydVhthOZ9NnS+ntiLh0fxtlUJHAuGaFoVmttpVMeum0p3WEXbcll94l1wM/gZ0Ccczop77VvN2I7TlsZCsuXf1WHvWEhjO8DPtyOVg2/mvK9QqboEth+7pD6NUQC1HN/TwvydGBARi9MZSzLE4b8Ru3XhX2PBxf8E1er2A6516o0w4sIA+lwURhAON82Kwe2iDAC1Watq4XHaGQ7skLcFOtI5lDxuM2gZe6WFIotPAhbaeYlU4to5cuarF1QrcZ/lwrLaCJl66JBocYZnrNlvm2+MBCTmUymPrYZVbjdlr/BxlMjmNmNI3SAAAAAElFTkSuQmCC',
}
]);
this.lf.render();
},
},
};
</script>
<style scoped>
.container {
display: flex;
height: 800px;
flex-grow: 1; /*铺满剩余空间*/
border: 3px solid #ababab;
overflow: hidden;
}
</style>
自定义拖拽节点
自定义五个节点
CustomAnd.js
import {HtmlNode, HtmlNodeModel} from '@logicflow/core'
//自定义and节点
// 提供节点 svg dom
class CustomAndNode extends HtmlNode {
constructor(props){
props.model.text.y = props.model.y + 20
super(props)
}
setHtml(rootEl) {
const { properties } = this.props.model;
const el = document.createElement("div");
el.className = "uml-wrapper";
el.innerHTML = `
<img width="58" src='${properties.status === 'COMPLETED' ? require('./img/and1.png') : (properties.status === 'FAILED' ? require('./img/and0.png') : require('./img/and.png'))}' alt=""/>
`;
rootEl.innerHTML = "";
rootEl.appendChild(el);
}
}
// 提供节点的样式
class CustomAndModel extends HtmlNodeModel {
setAttributes() {
const width = 58;
const height = 80;
this.width = width;
this.height = height;
this.anchorsOffset = [
[width / 2, -15],
[0, height / 2 - 30],
[-width / 2, -15],
[0, -height / 2],
];
}
}
export default {
type: 'CustomAnd',
view: CustomAndNode,
model: CustomAndModel
}
CustomBusiness.js
import {HtmlNode, HtmlNodeModel} from '@logicflow/core'
//自定义业务节点
// 提供节点
class CustomBusinessNode extends HtmlNode {
constructor(props){
props.model.text.y = props.model.y + 20
super(props)
}
setHtml(rootEl) {
const { properties } = this.props.model;
const el = document.createElement("div");
el.className = "uml-wrapper";
el.innerHTML = `
<img width="58" src='${properties.status === 'COMPLETED' ? require('./img/node1.png') : (properties.status === 'FAILED' ? require('./img/node0.png') : require('./img/node.png'))}' alt=""/>
`;
rootEl.innerHTML = "";
rootEl.appendChild(el);
}
}
// 提供节点的属性
class CustomBusinessModel extends HtmlNodeModel {
setAttributes() {
const width = 58;
const height = 80;
this.width = width;
this.height = height;
this.anchorsOffset = [
[width / 2, -15],
[0, height / 2 - 30],
[-width / 2, -15],
[0, -height / 2],
];
// 业务节点文本不可编辑
this.text.editable=false
}
}
export default {
type: 'CustomBusiness',
view: CustomBusinessNode,
model: CustomBusinessModel
}
CustomEnd.js
// CustomRectNode.js
import {HtmlNode, HtmlNodeModel} from '@logicflow/core'
//自定义结束节点
// 提供节点 svg dom
class CustomEndNode extends HtmlNode {
constructor(props){
props.model.text.y = props.model.y + 20
super(props)
}
setHtml(rootEl) {
const { properties } = this.props.model;
const el = document.createElement("div");
el.className = "uml-wrapper";
el.innerHTML = `
<img width="58" src='${properties.status === 'COMPLETED' ? require('./img/end1.png') : (properties.status === 'FAILED' ? require('./img/end0.png') : require('./img/end.png'))}' alt=""/>
`;
rootEl.innerHTML = "";
rootEl.appendChild(el);
}
}
// 提供节点的样式
class CustomEndModel extends HtmlNodeModel {
setAttributes() {
const width = 58;
const height = 80;
this.width = width;
this.height = height;
this.anchorsOffset = [
[width / 2, -15],
[0, height / 2 - 30],
[-width / 2, -15],
[0, -height / 2],
];
}
}
export default {
type: 'CustomEnd',
view: CustomEndNode,
model: CustomEndModel
}
CustomIf.js
import {HtmlNode, HtmlNodeModel} from '@logicflow/core'
//自定义条件节点
// 提供节点
class CustomIfNode extends HtmlNode {
constructor(props){
props.model.text.y = props.model.y + 20
super(props)
}
setHtml(rootEl) {
const { properties } = this.props.model;
const el = document.createElement("div");
el.className = "uml-wrapper";
el.innerHTML = `
<img width="58" src='${properties.status === 'COMPLETED' ? require('./img/if1.png') : (properties.status === 'FAILED' ? require('./img/if0.png') : require('./img/if.png'))}' alt=""/>
`;
rootEl.innerHTML = "";
rootEl.appendChild(el);
}
}
// 提供节点的属性
class CustomIfModel extends HtmlNodeModel {
setAttributes() {
const width = 58;
const height = 80;
this.width = width;
this.height = height;
this.anchorsOffset = [
[width / 2, -15],
[0, height / 2 - 30],
[-width / 2, -15],
[0, -height / 2],
];
}
}
export default {
type: 'CustomIf',
view: CustomIfNode,
model: CustomIfModel
}
CustomStart.js
import {HtmlNode, HtmlNodeModel} from '@logicflow/core'
//自定义开始节点
// 提供节点 svg dom
class CustomStartNode extends HtmlNode {
constructor(props){
props.model.text.y = props.model.y + 20
super(props)
}
setHtml(rootEl) {
const { properties } = this.props.model;
const el = document.createElement("div");
el.className = "start";
el.innerHTML = `
<img width="58" src='${properties.status === 'COMPLETED' ? require('./img/start1.png') : (properties.status === 'FAILED' ? require('./img/start0.png') : require('./img/start.png'))}' alt=""/>
`;
rootEl.innerHTML = "";
rootEl.appendChild(el);
}
}
class CustomStartModel extends HtmlNodeModel {
setAttributes() {
const width = 58;
const height = 80;
this.width = width;
this.height = height;
this.anchorsOffset = [
[width / 2, -15],
[0, height / 2 - 30],
[-width / 2, -15],
[0, -height / 2],
];
}
}
export default {
type: 'CustomStart',
view: CustomStartNode,
model: CustomStartModel
}
把自定义节点存放到index.js
import CustomStart from './CustomStart'
import CustomEnd from './CustomEnd'
import CustomAnd from './CustomAnd'
import CustomIf from './CustomIf'
import CustomBusiness from './CustomBusiness'
const Nodes = {
CustomStart,
CustomEnd,
CustomAnd,
CustomIf,
CustomBusiness,
}
export default Nodes
自定义面板存放节点
CustomNodePanel.vue
<template>
<div class="node-panel" v-if="showPanel">
<div class="node-item" @mousedown="addStartNode()" title="开始节点只能有一个出口,不能有入口">
<img src="../registerNode/img/start.png" width="48px" :draggable="false" alt="" >
<span class="node-label">开始</span>
</div>
<div class="node-item" @mousedown="addBusinessNode()" title="业务节点至少有一个入口,至少有一个出口">
<img src="../registerNode/img/node.png" width="48px" :draggable="false" alt="" >
<span class="node-label">业务能力</span>
</div>
<div class="node-item" @mousedown="addIfNode()" title="分支条件节点只能有一个入口,可以有多个出口">
<img src="../registerNode/img/if.png" width="48px" :draggable="false" alt="" >
<span class="node-label">分支条件</span>
</div>
<div class="node-item" @mousedown="addAndNode()" title="并行汇聚节点只能有一个出口,可以有多个出口">
<img src="../registerNode/img/and.png" width="48px" :draggable="false" alt="" >
<span class="node-label">并行汇聚</span>
</div>
<div class="node-item" @mousedown="addEndNode()" title="结束节点至少有一个入口,不能有出口">
<img src="../registerNode/img/end.png" width="48px" :draggable="false" alt="" >
<span class="node-label">结束</span>
</div>
</div>
</template>
<script>
// 注册节点
import customNodeTypes from '../registerNode/index'
import CustomPolyline from '../registerLine/CustomPolyline'
export default {
name: 'CustomNodePanel',
data () {
return {
lf: null,
}
},
props: {
graphData: Object,
showPanel: Boolean
},
inject: ['drawThis'],
mounted () {
// 注册节点
this.registerNode()
if (this.graphData) {
this.lf.renderRawData(this.graphData)
}
},
methods: {
registerNode () {
//创建画板
this.drawThis.lf = this.drawThis.init()
//注册自定义节点类型
this.drawThis.lf.register(customNodeTypes.CustomAnd)
this.drawThis.lf.register(customNodeTypes.CustomEnd)
this.drawThis.lf.register(customNodeTypes.CustomBusiness)
this.drawThis.lf.register(customNodeTypes.CustomIf)
this.drawThis.lf.register(customNodeTypes.CustomStart)
this.drawThis.lf.register(CustomPolyline)
this.lf =this.drawThis.lf
this.drawThis.refreshGraph()
},
addStartNode () {
(this.lf).dnd.startDrag({
type: 'CustomStart',
text: '开始',
properties: { type: 'start' }
})
},
addBusinessNode () {
(this.lf).dnd.startDrag({
type: 'CustomBusiness',
text: '业务能力',
properties: { type: 'business' }
})
},
addIfNode () {
(this.lf).dnd.startDrag({
type: 'CustomIf',
text: '条件',
properties: { type: 'if' }
})
},
addEndNode () {
(this.lf).dnd.startDrag({
type: 'CustomEnd',
text: '结束',
properties: { type: 'end', referenceName:'end' }
})
},
addAndNode () {
(this.lf).dnd.startDrag({
type: 'CustomAnd',
text: '合并',
properties: { type: 'and' }
})
}
}
}
</script>
<style>
.node-panel {
position: absolute;
top: 54px;
left: 24px;
width: 50px;
padding: 10px;
background-color: white;
box-shadow: 0 0 10px 1px rgb(228, 224, 219);
border-radius: 6px;
text-align: center;
z-index: 101;
}
.node-item {
margin-bottom: 10px;
}
.node-label {
font-size: 12px;
margin-top: 5px;
user-select: none;
}
.lf-html-wrapper .custom-html {
width:96px;
height:96px;
border:2px solid #2987ff;
}
.lf-html-wrapper .custom-head {
color:red;
height:26px;
line-height:26px;
}
.lf-html-wrapper .custom-body {
color:#2987ff;
height:50px;
line-height:50px;
}
</style>
使用自定义节点
<template>
<el-container>
<div class="container" ref="container"></div>
<CustomNodePanel :graphData="graphData" :showPanel="showPanel" ref="drawThis"></CustomNodePanel>
</el-container>
</template>
<script>
import LogicFlow from "@logicflow/core";
import "@logicflow/core/lib/style/index.css";
import { Control,DndPanel, SelectionSelect } from "@logicflow/extension";
import "@logicflow/extension/lib/index.css";
import '@logicflow/extension/lib/style/index.css';
import CustomNodePanel from "./components/CustomNodePanel.vue";
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: "draw",
components: { CustomNodePanel },
provide() {
return {
drawThis: this
}
},
data() {
return {
lf: null,
graphData: {
},
showPanel: true
}
},
methods: {
init(){
return new LogicFlow({
container: this.$refs.container,
plugins: [Control,DndPanel, SelectionSelect],
grid: true,
});
},
refreshGraph(){
this.lf.render();
}
},
};
</script>
<style scoped>
.container {
display: flex;
height: 800px;
flex-grow: 1; /*铺满剩余空间*/
border: 3px solid #ababab;
overflow: hidden;
}
</style>
系统线条
系统自带的类型
// 直线
import { LineEdge, PolylineEdgeModel } from "@logicflow/core";
// 折线
import { PolylineEdge, PolylineEdgeModel } from "@logicflow/core";
// 贝塞尔曲线
import { BezierEdge, BezierEdgeModel } from "@logicflow/core";
对应类型名称
export type EdgeType = 'line' | 'polyline' | 'bezier' | string
使用系统线条
<template>
<el-container>
<div class="container" ref="container"></div>
<CustomNodePanel :graphData="graphData" :showPanel="showPanel" ref="drawThis"></CustomNodePanel>
</el-container>
</template>
<script>
import LogicFlow from "@logicflow/core";
import "@logicflow/core/lib/style/index.css";
import { Control,DndPanel, SelectionSelect } from "@logicflow/extension";
import "@logicflow/extension/lib/index.css";
import '@logicflow/extension/lib/style/index.css';
import CustomNodePanel from "./components/CustomNodePanel.vue";
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: "draw",
components: { CustomNodePanel },
provide() {
return {
drawThis: this
}
},
data() {
return {
lf: null,
graphData: {
},
showPanel: true
}
},
methods: {
init(){
return new LogicFlow({
container: this.$refs.container,
plugins: [Control,DndPanel, SelectionSelect],
grid: true,
edgeType: 'bezier'
});
},
refreshGraph(){
this.lf.render();
}
},
};
</script>
<style scoped>
.container {
display: flex;
height: 800px;
flex-grow: 1; /*铺满剩余空间*/
border: 3px solid #ababab;
overflow: hidden;
}
</style>
自定义线条
CustomPolyline.js
import {PolylineEdge, PolylineEdgeModel} from "@logicflow/core";
class CustomPolylineModel extends PolylineEdgeModel {
setAttributes() {
this.offset = 20;
this.text.editable=false
}
//设置线样式
getEdgeStyle() {
const style = super.getEdgeStyle();
const { properties } = this;
if (properties.isActived) {
style.strokeDasharray = "4 4";
}
style.stroke = "#d51b1b";
return style;
}
//设置文字样色
getTextStyle() {
//设置文字样色
const style = super.getTextStyle();
// style.color = "#3451F1";
// style.fontSize = 30;
// style.background.fill = "#F2F131";
// console.log(style)
return style;
}
//鼠标悬浮时样式
getOutlineStyle() {
const style = super.getOutlineStyle();
// style.stroke = "";
// style.hover.stroke = "red";
return style;
}
}
export default {
type: "CustomPolyline",
view: PolylineEdge,
model: CustomPolylineModel,
};
注册
import CustomPolyline from '../registerLine/CustomPolyline'
this.drawThis.lf.register(CustomPolyline)
使用
<template>
<el-container>
<div class="container" ref="container"></div>
<CustomNodePanel :graphData="graphData" :showPanel="showPanel" ref="drawThis"></CustomNodePanel>
</el-container>
</template>
<script>
import LogicFlow from "@logicflow/core";
import "@logicflow/core/lib/style/index.css";
import { Control,DndPanel, SelectionSelect } from "@logicflow/extension";
import "@logicflow/extension/lib/index.css";
import '@logicflow/extension/lib/style/index.css';
import CustomNodePanel from "./components/CustomNodePanel.vue";
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: "draw",
components: { CustomNodePanel },
provide() {
return {
drawThis: this
}
},
data() {
return {
lf: null,
graphData: {
},
showPanel: true
}
},
methods: {
init(){
return new LogicFlow({
container: this.$refs.container,
plugins: [Control,DndPanel, SelectionSelect],
grid: true,
edgeType: 'CustomPolyline'
});
},
refreshGraph(){
this.lf.render();
}
},
};
</script>
<style scoped>
.container {
display: flex;
height: 800px;
flex-grow: 1; /*铺满剩余空间*/
border: 3px solid #ababab;
overflow: hidden;
}
</style>
菜单
import LogicFlow from "@logicflow/core";
import { Menu } from "@logicflow/extension";
import "@logicflow/extension/lib/style/index.css";
LogicFlow.use(Menu);
添加默认菜单
<template>
<el-container>
<div class="container" ref="container"></div>
<CustomNodePanel :graphData="graphData" :showPanel="showPanel" ref="drawThis"></CustomNodePanel>
</el-container>
</template>
<script>
import LogicFlow from "@logicflow/core";
import "@logicflow/core/lib/style/index.css";
import { Control,DndPanel, SelectionSelect,Menu } from "@logicflow/extension";
import "@logicflow/extension/lib/index.css";
import '@logicflow/extension/lib/style/index.css';
import CustomNodePanel from "./components/CustomNodePanel.vue";
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: "draw",
components: { CustomNodePanel },
provide() {
return {
drawThis: this
}
},
data() {
return {
lf: null,
graphData: {
},
showPanel: true
}
},
methods: {
init(){
return new LogicFlow({
container: this.$refs.container,
plugins: [Control,DndPanel, SelectionSelect,Menu ],
grid: true,
edgeType: 'CustomPolyline'
});
},
refreshGraph(){
this.lf.render();
}
},
};
</script>
<style scoped>
.container {
display: flex;
height: 800px;
flex-grow: 1; /*铺满剩余空间*/
border: 3px solid #ababab;
overflow: hidden;
}
</style>
添加自定义菜单
<template>
<el-container>
<div class="container" ref="container"></div>
<CustomNodePanel :graphData="graphData" :showPanel="showPanel" ref="drawThis"></CustomNodePanel>
</el-container>
</template>
<script>
import LogicFlow from "@logicflow/core";
import "@logicflow/core/lib/style/index.css";
import { Control,DndPanel, SelectionSelect,Menu } from "@logicflow/extension";
import "@logicflow/extension/lib/index.css";
import '@logicflow/extension/lib/style/index.css';
import CustomNodePanel from "./components/CustomNodePanel.vue";
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: "draw",
components: { CustomNodePanel },
provide() {
return {
drawThis: this
}
},
data() {
return {
lf: null,
graphData: {
},
showPanel: true
}
},
methods: {
init(){
return new LogicFlow({
container: this.$refs.container,
plugins: [Control,DndPanel, SelectionSelect,Menu ],
grid: true,
edgeType: 'CustomPolyline'
});
},
refreshGraph(){
this.addMenu()
this.lf.render();
},
addMenu(){
this.lf.extension.menu.addMenuConfig({
nodeMenu: [
{
text: "分享",
callback() {
alert("分享成功!");
},
},
{
text: "属性",
callback(node) {
alert(`
节点id:${node.id}
节点类型:${node.type}
节点坐标:(x: ${node.x}, y: ${node.y})`);
},
},
],
edgeMenu: [
{
text: "属性",
callback(edge) {
alert(`
边id:${edge.id}
边类型:${edge.type}
边坐标:(x: ${edge.x}, y: ${edge.y})
源节点id:${edge.sourceNodeId}
目标节点id:${edge.targetNodeId}`);
},
},
],
graphMenu: [
{
text: "分享",
callback() {
alert("分享成功!");
},
},
],
});
}
},
};
</script>
<style scoped>
.container {
display: flex;
height: 800px;
flex-grow: 1; /*铺满剩余空间*/
border: 3px solid #ababab;
overflow: hidden;
}
</style>
事件
添加事件
<template>
<el-container>
<div class="container" ref="container"></div>
<CustomNodePanel :graphData="graphData" :showPanel="showPanel" ref="drawThis"></CustomNodePanel>
</el-container>
</template>
<script>
import LogicFlow from "@logicflow/core";
import "@logicflow/core/lib/style/index.css";
import { Control,DndPanel, SelectionSelect,Menu } from "@logicflow/extension";
import "@logicflow/extension/lib/index.css";
import '@logicflow/extension/lib/style/index.css';
import CustomNodePanel from "./components/CustomNodePanel.vue";
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: "draw",
components: { CustomNodePanel },
provide() {
return {
drawThis: this
}
},
data() {
return {
lf: null,
graphData: {
},
showPanel: true
}
},
methods: {
init(){
return new LogicFlow({
container: this.$refs.container,
plugins: [Control,DndPanel, SelectionSelect,Menu ],
grid: true,
edgeType: 'CustomPolyline'
});
},
refreshGraph(){
this.addEvent()
this.lf.render();
},
addEvent(){
this.lf.on("node:dnd-add", (data) => {
console.log(data)
});
}
},
};
</script>
<style scoped>
.container {
display: flex;
height: 800px;
flex-grow: 1; /*铺满剩余空间*/
border: 3px solid #ababab;
overflow: hidden;
}
</style>
线条鼠标悬浮展示文字
创建LineTipsDialog.vue
<template>
<div
v-if="isTooltipVisible"
:style="{ left: tooltipPosition.x + 'px', top: tooltipPosition.y + 'px' }"
class="tooltip">
<div v-html="showText"></div>
</div>
</template>
<script>
export default {
data() {
return {
showText: '',
isTooltipVisible: false, // 控制提示框的显示/隐藏
tooltipPosition: { x: 0, y: 0 }, // 提示框的位置
};
},
methods: {
// 处理鼠标移动事件
open(properties,event) {
this.showText = 'flag=>'+properties.flag+'<br>resultDesc=>'+properties.resultDesc;
this.tooltipPosition.x = event.pageX + 10; // 提示框水平位置,略微偏移
this.tooltipPosition.y = event.pageY + 10; // 提示框垂直位置,略微偏移
this.isTooltipVisible = true; // 鼠标移动时显示提示框
},
// 处理鼠标离开事件
close() {
this.showText = '';
this.isTooltipVisible = false; // 鼠标离开时隐藏提示框
},
}
};
</script>
<style scoped>
.tooltip {
position: absolute;
padding: 5px;
color: #6c7375;
border-radius: 5px;
pointer-events: none; /* 让鼠标事件透过悬浮框 */
}
</style>
引用
import LineTipsDialog from './components/LineTipsDialog.vue'
components: {
LineTipsDialog
},
<line-tips-dialog ref="lineTipsDialog"></line-tips-dialog>
使用
//鼠标进入线条显示文字
this.lf.on('edge:mouseenter', (data) => {
if (data.data.properties.flag&&data.data.properties.flag!==''){
this.$refs.lineTipsDialog.open(data.data.properties,data.e);
}
});
//鼠标离开线条关闭文字
this.lf.on('edge:mouseleave', (data) => {
if (data.data.properties.flag&&data.data.properties.flag!==''){
this.$refs.lineTipsDialog.close();
}
});
一键美化
安装插件
npm install @antv/layout@0.3.1
创建工具beauty.js
// 引入布局模块
const { DagreLayout } = require('@antv/layout');
class BeautyUtils {
static pluginName = 'dagre';
lf;
option;
upIndexMap = {};
downIndexMap ={};
constructor(lf) {
this.lf = lf;
}
getBytesLength(word) {
if (!word) {
return 0;
}
let totalLength = 0;
for (let i = 0; i < word.length; i++) {
const c = word.charCodeAt(i);
if ((word.match(/[A-Z]/))) {
totalLength += 1.5;
} else if ((c >= 0x0001 && c <= 0x007e) || (c >= 0xff60 && c <= 0xff9f)) {
totalLength += 1;
} else {
totalLength += 2;
}
}
return totalLength;
}
/**
* option: {
* rankdir: "TB", // layout 方向, 可选 TB, BT, LR, RL
* align: undefined, // 节点对齐方式,可选 UL, UR, DL, DR
* nodeSize: undefined, // 节点大小
* nodesepFunc: undefined, // 节点水平间距(px)
* ranksepFunc: undefined, // 每一层节点之间间距
* nodesep: 40, // 节点水平间距(px) 注意:如果有grid,需要保证nodesep为grid的偶数倍
* ranksep: 40, // 每一层节点之间间距 注意:如果有grid,需要保证ranksep为grid的偶数倍
* controlPoints: false, // 是否保留布局连线的控制点
* radial: false, // 是否基于 dagre 进行辐射布局
* focusNode: null, // radial 为 true 时生效,关注的节点
* };
*/
layout(option = {}) {
this.upIndexMap = {};
this.downIndexMap = {};
const { nodes, edges, gridSize } = this.lf.graphModel;
// 为了保证生成的节点在girdSize上,需要处理一下。
let nodesep = 40;
let ranksep = 40;
if (gridSize > 20) {
nodesep = gridSize * 2;
ranksep = gridSize * 2;
}
this.option = {
type: 'dagre',
rankdir: 'LR',
// align: 'UL',
align: 'UR',
// align: 'DR',
nodesep,
ranksep,
begin: [120, 120],
...option,
};
const layoutInstance = new DagreLayout(this.option);
const layoutData = layoutInstance.layout({
nodes: nodes.map((node) => ({
id: node.id,
size: {
width: node.width,
height: node.height,
},
model: node,
})),
edges: edges.map((edge) => ({
source: edge.sourceNodeId,
target: edge.targetNodeId,
model: edge,
})),
});
const newGraphData = {
nodes: [],
edges: [],
};
layoutData.nodes.forEach(node => {
const { model } = node;
const data = model.getData();
data.x = node.x;
data.y = node.y;
newGraphData.nodes.push(data);
});
layoutData.edges.forEach(edge => {
const { model } = edge;
const data = model.getData();
data.pointsList = this.calcPointsList(model, newGraphData.nodes);
if (data.pointsList) {
let first = data.pointsList[0];
const second = data.pointsList[1];
const third = data.pointsList[2];
let last = data.pointsList[3];
//调整线的出口和入口位置
if (first.y===second.y){
//如果是水平出线
if (first.x>second.x){
data.pointsList[0].x = data.pointsList[0].x - 29;
}else {
data.pointsList[0].x = data.pointsList[0].x + 29;
}
}else {
//如果是垂直出线
if (first.y>second.y){
data.pointsList[0].y = data.pointsList[0].y - 15;
}else {
data.pointsList[0].y = data.pointsList[0].y + 15;
}
}
if (third.y===last.y){
data.pointsList[2].y = data.pointsList[2].y - 14;
data.pointsList[3].y = data.pointsList[3].y - 14;
//如果是水平入线
if (third.x>last.x){
data.pointsList[3].x = data.pointsList[3].x + 29;
}else {
data.pointsList[3].x = data.pointsList[3].x - 29;
}
}else {
//如果是垂直出线
if (third.y>last.y){
data.pointsList[3].y = data.pointsList[3].y + 15;
}else {
data.pointsList[3].y = data.pointsList[3].y - 37;
}
}
first = data.pointsList[0];
last = data.pointsList[3];
// 计算文字的位置
let textX = second.x + (third.x - second.x) / 2;
if (second.x>=third.x){
textX = third.x + (second.x - third.x) / 2;
}
let textY = second.y + (third.y - second.y) / 2;
if (second.y>=third.y){
textY = third.y + (second.y - third.y) / 2;
}
data.startPoint = { x: first.x, y: first.y };
data.endPoint = { x: last.x, y: last.y };
if (data.text && data.text.value) {
data.text = {
x: textX - this.getBytesLength(data.text.value) * 6 - 10,
y: textY,
value: data.text.value,
};
}
} else {
data.startPoint = undefined;
data.endPoint = undefined;
if (data.text && data.text.value) {
data.text = data.text.value;
}
}
newGraphData.edges.push(data);
});
// 调整节点上文字的位置
newGraphData.nodes.forEach(node => {
node.text.x = node.x
node.text.y = node.y+20
})
this.lf.render(newGraphData);
}
// 在节点确认从左向右后,通过计算来保证节点连线清晰。
//线比点的y少15 x多29或少29
calcPointsList(model, nodes) {
const pointsList = [];
if (this.option.rankdir === 'LR' && model.modelType === 'polyline-edge') {
const newSourceNodeData = nodes.find(node => node.id === model.sourceNodeId);
const newTargetNodeData = nodes.find(node => node.id === model.targetNodeId);
const sourceX = newSourceNodeData.x;
const sourceY = newSourceNodeData.y;
const targetX = newTargetNodeData.x;
const targetY = newTargetNodeData.y;
//如果节点在同一行或者同一列,则直接连线
pointsList.push({
x: sourceX,
y: sourceY,
});
const offset = this.getOffset(targetX, targetY);
if (sourceY===targetY){
//偏移量
pointsList.push({
x: sourceX,
y: targetY+offset,
});
pointsList.push({
x: targetX,
y: targetY+offset,
});
}else {
pointsList.push({
x: targetX+offset,
y: sourceY,
});
pointsList.push({
x: targetX+offset,
y: targetY,
});
}
pointsList.push({
x: targetX,
y: targetY,
});
return pointsList;
}
return undefined;
}
//参数是目标节点的坐标
getOffset(x,y){
let key = x+','+y;
let offset = 0;
if (!this.upIndexMap[key]){
this.upIndexMap[key]=0
}
if (!this.downIndexMap[key]){
this.downIndexMap[key]=0
}
if (this.upIndexMap[key]>this.downIndexMap[key]){
this.downIndexMap[key]++
offset = -80*this.downIndexMap[key]
}else {
this.upIndexMap[key]++
offset = 80*this.upIndexMap[key]
}
return offset;
}
}
module.exports = BeautyUtils;
使用
<template>
<el-container>
<div class="container" ref="container"></div>
<CustomNodePanel :graphData="graphData" :showPanel="showPanel" ref="drawThis"></CustomNodePanel>
</el-container>
</template>
<script>
import LogicFlow from "@logicflow/core";
import "@logicflow/core/lib/style/index.css";
import { Control,DndPanel, SelectionSelect,Menu,MiniMap } from "@logicflow/extension";
import "@logicflow/extension/lib/index.css";
import '@logicflow/extension/lib/style/index.css';
import CustomNodePanel from "./components/CustomNodePanel.vue";
import beauty from './utils/beauty'
export default {
// eslint-disable-next-line vue/multi-word-component-names
name: "draw",
components: { CustomNodePanel },
provide() {
return {
drawThis: this
}
},
data() {
return {
lf: null,
graphData: {
nodes: [],
edges: []
},
showPanel: true
}
},
methods: {
init(){
return new LogicFlow({
container: this.$refs.container,
plugins: [Control,DndPanel, SelectionSelect,Menu,MiniMap ],
grid: true,
edgeType: 'CustomPolyline'
});
},
refreshGraph(){
this.lf.extension.control.addItem({
key: 'clear',
iconClass: 'el-icon-refresh icon-delete',
title: "一键美化",
text: "一键美化",
onClick: () => {
this.beauty()
}
})
this.lf.render();
},
beauty() {
console.log(this.lf.getGraphData())
new beauty(this.lf).layout()
},
},
};
</script>
<style scoped>
.container {
display: flex;
height: 800px;
flex-grow: 1; /*铺满剩余空间*/
border: 3px solid #ababab;
overflow: hidden;
}
</style>
测试
如果启动报错
先查看是否安装了该依赖
npm list @babel/plugin-transform-private-methods
不存在的话就安装
npm install @babel/plugin-transform-private-methods
在babel.config.js中添加插件
重新启动即可
无连线
有连线