X6 是基于 HTML 和 SVG 的图编辑引擎,提供低成本的定制能力和开箱即用的内置扩展,方便我们快速搭建 DAG 图、ER 图、流程图、血缘图等应用。
最终效果图
1.安装
npm install @antv/x6 --save //x6主要包
npm install @antv/x6-vue-shape //使用vue组件画图插件
npm install @antv/x6-plugin-dnd //拖拽 添加元素图形插件
2.直接上代码
dom元素 form.vue
<template><!-- 新增、编辑弹窗--><el-dialog width="1200px" class="flow-dialog" top="5vh" title="实体编辑" :visible.sync="show" :append-to-body="true" :close-on-click-modal = "false" v-el-drag-dialog><div class="content"><!--左侧工具栏--><div class="stencil" ><p>流程编辑器</p><span @mousedown="startDragToGraph(item, $event)" v-for="item in list" :key="item" @click="test(item)" class="percentage-value"><i style="margin-right: 5px;cursor: pointer;" class="el-icon-setting"/><span class="percentage-content">{{ item }}</span></span></div><div class="panel"><!--流程图工具栏--><div class="toolbar"><el-button class="float-btn" icon="el-icon-s-claim" type="primary" @click="save()">保存</el-button><el-button class="float-btn" icon="el-icon-s-home" type="danger" @click="show=false">退出</el-button><el-button style="margin-left: 100px;" class="float-btn" icon="el-icon-s-tools" type="success" @click="">设置预览参数</el-button><el-button class="float-btn" icon="el-icon-notebook-2" type="info" @click="">预览节点数据</el-button><el-button class="float-btn" icon="el-icon-refresh" type="success" @click="">重算预览数据</el-button></div><!--流程图画板--><div id="containerShape" /></div></div></el-dialog></template>
js部分
<script>import elDragDialog from '@/directive/el-drag-dialog'import { register } from '@antv/x6-vue-shape'import { Graph,Shape} from "@antv/x6";import CustomNode from './CustomNode.vue'import { Dnd } from '@antv/x6-plugin-dnd'export default {directives: { elDragDialog },data() {return {show:false,list: ['数据查询', '横向连接', '追加合并', '分组汇总', '数据过滤','字段设置','输出'],graph:{}}},mounted() {const attrs = {circle: {r: 4,magnet: true,fill: '#fff',stroke: '#85A5FF',strokeWidth: 1,},};register({shape: 'custom-vue-node',component: CustomNode,width: 180,height: 40,// port默认不可见ports: {groups: {in: {position: {name:'left',args: {dx: -10,y: '50%',},},attrs: attrs},out: {position: {name:'right',args: {x: '100%',dx: 10,y: '50%',},},attrs: attrs},},},})},methods: {open() {this.show = true;this.$nextTick(() => {this.init("containerShape");});},init(id){const graph = new Graph({container: document.getElementById(id),width: 1000,height: 1000,connecting: {router: 'manhattan',anchor: 'center',connectionPoint: 'anchor',createEdge() {return new Shape.Edge({attrs: {line: {stroke: '#52c41a',strokeWidth: 1,strokeDasharray: 5,targetMarker: 'classic',style: {animation: 'ant-line 30s infinite linear',},},},zIndex: 0,})},}})graph.addNode({shape: 'custom-vue-node',x: 100,y: 100,ports: [{group: 'in'},{group: 'out'}],data:{percentage: 30}})this.graph = graph;},save() {this.graph.addNode({shape: 'custom-vue-node',x: 200,y: 300,ports: [{group: 'in'},{group: 'out'}],data:{percentage: '数据流程飒飒飒飒拉开阿斯兰的卡死了的科目来打开'}})},// 自定义一个拖拽方法,也可以单独封装成一个js文件(方便调用)// 这里直接写到vue文件的methods方法里了// 需求:未置灰的可以拖拽,置灰的无法拖拽即禁用状态startDragToGraph(item, e) {const node = this.graph.createNode({shape: 'custom-vue-node',x: 200,y: 300,ports: [{group: 'in'},{group: 'out'}],data:{percentage: item}});const dnd = new Dnd({target: this.graph,// ☆拖拽结束时,验证节点是否可以放置到目标画布中。validateNode: () => {console.log('成功拖拽至目标画布')},})dnd.start(node, e)},}}
</script>
css部分
<style type="text/css">@keyframes ant-line {to {stroke-dashoffset: -1000}}
</style><style type="text/css" scoped>.flow-dialog ::v-deep .el-dialog__body{max-height: 85vh;padding: 0;}.toolbar ::v-deep .el-button--small{padding: 5px 10px;}.content {width: 1180px;height: 85vh;display: flex;}.stencil {width: 230px;height: 100%;position: relative;margin-right: 10px;border-right: 1px solid rgba(0, 0, 0, 0.08);box-sizing: border-box;text-align: center;}.stencil p{margin: 0;line-height: 37px;border-bottom: 1px solid #00000008;background-color: #f7f9fb;font-size: 14px;padding-left: 5px;text-align: left;}.panel {width: calc(100% - 230px);height: 100%;}.panel .toolbar {width: 100%;height: 38px;display: flex;align-items: center;background-color: #f7f9fb;border-bottom: 1px solid rgba(0, 0, 0, 0.08);}.panel #containerShape {width: 100%;height: calc(100% - 10px) !important;}.percentage-value{display: inline-block;width: max-content;background-color: #fff;border: 1px solid #c2c8d5;border-left: 4px solid #5F95FF;border-radius: 4px;box-shadow: 0 2px 5px 1px rgba(0, 0, 0, 0.06);padding: 8px;font-size: 16px;margin-top: 10px;text-align: left;}.percentage-content{width: 100px;max-width: 150px;overflow: hidden;font-size: 12px;display: inline-block;}</style>
CustomNode.vue节点元素dom
<template><span class="percentage-value"><i style="margin-right: 5px;cursor: pointer;" @click="save()" class="el-icon-setting"/><span class="percentage-content">{{ percentage }}%</span><i @click="save()" class="el-icon-success data-start"/></span>
</template><script>export default {inject: ['getNode'],data() {return {percentage: 50,}},mounted() {const cell = this.getNode();this.percentage = cell.data.percentage;},methods: {save(){console.log("点击成功")}}}
</script>
<style type="text/css" scoped>.percentage-value{display: inline-block;width: max-content;background-color: #fff;border: 1px solid #c2c8d5;border-left: 4px solid #5F95FF;border-radius: 4px;box-shadow: 0 2px 5px 1px rgba(0, 0, 0, 0.06);padding: 10px;font-size: 16px;}.percentage-content{width: 100px;max-width: 150px;overflow: hidden;font-size: 12px;display: inline-block;}.data-start{margin-right: 5px;margin-left: 10px;cursor: pointer;color: #6bcc00;}
</style>
相关官方文档x6.antv官网