概述
X6 是 AntV 旗下的图编辑引擎,提供了一系列开箱即用的交互组件和简单易用的节点定制能力,方便我们快速搭建流程图、DAG 图、ER 图等图应用,下面记录几个使用过程中遇到的几个坑.
坑1
使用X6的时候一定要和官网示例所给的版本号保持一致,如果直接无脑下载最新版使用,会发现最后很多接口没有提供或者已经在高版本启用了,儿官方文档所给示例还是以前旧版本的,详情见使用 HTML/React/Vue/Angular 渲染中提供的vue结合使用的demon
坑2
流程图中除了官网所给的基础的节点和边,在实际项目中必然涉及到非常高的自定义扩展,特别是ui方面,因此需要使用对应x6所提供的扩展节点和自定义节点的相关方法,详情见:使用 HTML/React/Vue/Angular 渲染
示例
文章以vue2版本为例
- APP.vue
<template><div><el-button @click="addHTMLNode">追加HTML节点</el-button><el-button @click="addElButtonNode">追加ElButton节点</el-button><el-button @click="addELDatePickerNode">追加DatePicker节点</el-button><el-button @click="download">下载</el-button><div class="container"></div><div class="container-mini-map" ref="containerMiniMap"></div></div></template><script>import { Graph, DataUri } from "@antv/x6";import "@antv/x6-vue-shape";import Button from "./components/Button.vue";import DatePicker from "./components/DatePicker.vue";Graph.registerNode("el-button", {inherit: "vue-shape",x: 200,y: 150,width: 150,height: 100,component: {render: (h) => h(Button),},});Graph.registerNode("el-date-picker", {inherit: "vue-shape",x: 200,y: 150,width: 150,height: 100,component: {render: (h) => h(DatePicker),},});export default {data() {return {graph: null,};},mounted() {this.init();},methods: {addElButtonNode() {this.graph.addNode({x: 40,y: 40,width: 100,height: 40,shape: "el-button",});},addELDatePickerNode() {this.graph.addNode({x: 40,y: 40,width: 100,height: 40,shape: "el-date-picker",});},addHTMLNode() {const source = this.graph.addNode({x: 40,y: 40,width: 100,height: 40,shape: "html",html() {const wrap = document.createElement("div");wrap.style.width = "100%";wrap.style.height = "100%";wrap.style.background = "#008c8c";wrap.style.display = "flex";wrap.style.justifyContent = "center";wrap.style.alignItems = "center";wrap.innerText = "html节点";return wrap;},});},download() {this.graph.toSVG((dataUri) => {DataUri.downloadDataUri(DataUri.svgToDataUrl(dataUri), "chart.svg");});},init() {const data = {// 节点nodes: [{id: "node1", // String,可选,节点的唯一标识x: 40, // Number,必选,节点位置的 x 值y: 40, // Number,必选,节点位置的 y 值width: 80, // Number,可选,节点大小的 width 值height: 40, // Number,可选,节点大小的 height 值label: "hello", // String,节点标签},{id: "node2", // String,节点的唯一标识x: 160, // Number,必选,节点位置的 x 值y: 180, // Number,必选,节点位置的 y 值width: 80, // Number,可选,节点大小的 width 值height: 40, // Number,可选,节点大小的 height 值label: "world", // String,节点标签},],// 边edges: [{source: "node1", // String,必须,起始节点 idtarget: "node2", // String,必须,目标节点 id},],};this.graph = new Graph({container: document.querySelector(".container"),history: true,selecting: {enabled: true,rubberband: true, // 启用框选className: "my-selecting",},panning: {// enabled: true,},scroller: {enabled: true,},minimap: {enabled: true,container: this.$refs.containerMiniMap,},background: {color: "#fffbe6", // 设置画布背景颜色},grid: {size: 10, // 网格大小 10pxvisible: true, // 渲染网格背景},});this.graph.fromJSON(data);},},};</script><style >.container {width: 80% !important;min-height: 500px !important;}.container-mini-map {width: 300px;height: 300px;border: 1px solid red;}.my-selecting {border: 1px solid red;}</style><template> <div> <el-button @click="addHTMLNode">追加HTML节点</el-button> <el-button @click="addElButtonNode">追加ElButton节点</el-button> <el-button @click="addELDatePickerNode">追加DatePicker节点</el-button> <el-button @click="download">下载</el-button> <div class="container"></div> <div class="container-mini-map" ref="containerMiniMap"></div> </div> </template> <script> import { Graph, DataUri } from "@antv/x6"; import "@antv/x6-vue-shape"; import Button from "./components/Button.vue"; import DatePicker from "./components/DatePicker.vue"; Graph.registerNode("el-button", { inherit: "vue-shape", x: 200, y: 150, width: 150, height: 100, component: { render: (h) => h(Button), }, }); Graph.registerNode("el-date-picker", { inherit: "vue-shape", x: 200, y: 150, width: 150, height: 100, component: { render: (h) => h(DatePicker), }, }); export default { data() { return { graph: null, }; }, mounted() { this.init(); }, methods: { addElButtonNode() { this.graph.addNode({ x: 40, y: 40, width: 100, height: 40, shape: "el-button", }); }, addELDatePickerNode() { this.graph.addNode({ x: 40, y: 40, width: 100, height: 40, shape: "el-date-picker", }); }, addHTMLNode() { const source = this.graph.addNode({ x: 40, y: 40, width: 100, height: 40, shape: "html", html() { const wrap = document.createElement("div"); wrap.style.width = "100%"; wrap.style.height = "100%"; wrap.style.background = "#008c8c"; wrap.style.display = "flex"; wrap.style.justifyContent = "center"; wrap.style.alignItems = "center"; wrap.innerText = "html节点"; return wrap; }, }); }, download() { this.graph.toSVG((dataUri) => { DataUri.downloadDataUri(DataUri.svgToDataUrl(dataUri), "chart.svg"); }); }, init() { const data = { // 节点 nodes: [ { id: "node1", // String,可选,节点的唯一标识 x: 40, // Number,必选,节点位置的 x 值 y: 40, // Number,必选,节点位置的 y 值 width: 80, // Number,可选,节点大小的 width 值 height: 40, // Number,可选,节点大小的 height 值 label: "hello", // String,节点标签 }, { id: "node2", // String,节点的唯一标识 x: 160, // Number,必选,节点位置的 x 值 y: 180, // Number,必选,节点位置的 y 值 width: 80, // Number,可选,节点大小的 width 值 height: 40, // Number,可选,节点大小的 height 值 label: "world", // String,节点标签 }, ], // 边 edges: [ { source: "node1", // String,必须,起始节点 id target: "node2", // String,必须,目标节点 id }, ], }; this.graph = new Graph({ container: document.querySelector(".container"), history: true, selecting: { enabled: true, rubberband: true, // 启用框选 className: "my-selecting", }, panning: { // enabled: true, }, scroller: { enabled: true, }, minimap: { enabled: true, container: this.$refs.containerMiniMap, }, background: { color: "#fffbe6", // 设置画布背景颜色 }, grid: { size: 10, // 网格大小 10px visible: true, // 渲染网格背景 }, }); this.graph.fromJSON(data); }, }, }; </script> <style > .container { width: 80% !important; min-height: 500px !important; } .container-mini-map { width: 300px; height: 300px; border: 1px solid red; } .my-selecting { border: 1px solid red; } </style><template> <div> <el-button @click="addHTMLNode">追加HTML节点</el-button> <el-button @click="addElButtonNode">追加ElButton节点</el-button> <el-button @click="addELDatePickerNode">追加DatePicker节点</el-button> <el-button @click="download">下载</el-button> <div class="container"></div> <div class="container-mini-map" ref="containerMiniMap"></div> </div> </template> <script> import { Graph, DataUri } from "@antv/x6"; import "@antv/x6-vue-shape"; import Button from "./components/Button.vue"; import DatePicker from "./components/DatePicker.vue"; Graph.registerNode("el-button", { inherit: "vue-shape", x: 200, y: 150, width: 150, height: 100, component: { render: (h) => h(Button), }, }); Graph.registerNode("el-date-picker", { inherit: "vue-shape", x: 200, y: 150, width: 150, height: 100, component: { render: (h) => h(DatePicker), }, }); export default { data() { return { graph: null, }; }, mounted() { this.init(); }, methods: { addElButtonNode() { this.graph.addNode({ x: 40, y: 40, width: 100, height: 40, shape: "el-button", }); }, addELDatePickerNode() { this.graph.addNode({ x: 40, y: 40, width: 100, height: 40, shape: "el-date-picker", }); }, addHTMLNode() { const source = this.graph.addNode({ x: 40, y: 40, width: 100, height: 40, shape: "html", html() { const wrap = document.createElement("div"); wrap.style.width = "100%"; wrap.style.height = "100%"; wrap.style.background = "#008c8c"; wrap.style.display = "flex"; wrap.style.justifyContent = "center"; wrap.style.alignItems = "center"; wrap.innerText = "html节点"; return wrap; }, }); }, download() { this.graph.toSVG((dataUri) => { DataUri.downloadDataUri(DataUri.svgToDataUrl(dataUri), "chart.svg"); }); }, init() { const data = { // 节点 nodes: [ { id: "node1", // String,可选,节点的唯一标识 x: 40, // Number,必选,节点位置的 x 值 y: 40, // Number,必选,节点位置的 y 值 width: 80, // Number,可选,节点大小的 width 值 height: 40, // Number,可选,节点大小的 height 值 label: "hello", // String,节点标签 }, { id: "node2", // String,节点的唯一标识 x: 160, // Number,必选,节点位置的 x 值 y: 180, // Number,必选,节点位置的 y 值 width: 80, // Number,可选,节点大小的 width 值 height: 40, // Number,可选,节点大小的 height 值 label: "world", // String,节点标签 }, ], // 边 edges: [ { source: "node1", // String,必须,起始节点 id target: "node2", // String,必须,目标节点 id }, ], }; this.graph = new Graph({ container: document.querySelector(".container"), history: true, selecting: { enabled: true, rubberband: true, // 启用框选 className: "my-selecting", }, panning: { // enabled: true, }, scroller: { enabled: true, }, minimap: { enabled: true, container: this.$refs.containerMiniMap, }, background: { color: "#fffbe6", // 设置画布背景颜色 }, grid: { size: 10, // 网格大小 10px visible: true, // 渲染网格背景 }, }); this.graph.fromJSON(data); }, }, }; </script> <style > .container { width: 80% !important; min-height: 500px !important; } .container-mini-map { width: 300px; height: 300px; border: 1px solid red; } .my-selecting { border: 1px solid red; } </style>
- ./component/Button.vue
<template><div><el-button type="primary" @click="handlePrimaryClick">按钮</el-button></div></template><script>export default {data() {return {value: "",};},components: {},methods: {handlePrimaryClick() {console.log("primary-click");},},};</script><style lang='less'></style><template> <div> <el-button type="primary" @click="handlePrimaryClick">按钮</el-button> </div> </template> <script> export default { data() { return { value: "", }; }, components: {}, methods: { handlePrimaryClick() { console.log("primary-click"); }, }, }; </script> <style lang='less'> </style><template> <div> <el-button type="primary" @click="handlePrimaryClick">按钮</el-button> </div> </template> <script> export default { data() { return { value: "", }; }, components: {}, methods: { handlePrimaryClick() { console.log("primary-click"); }, }, }; </script> <style lang='less'> </style>
- ./component/DatePicker.vue
<template><div><el-date-pickertype="date"v-model="value"placeholder="请输入"></el-date-picker></div></template><script>export default {data() {return {value: "",};},components: {},methods: {handlePrimaryClick() {console.log("primary-click");},},};</script><style lang='less'></style><template> <div> <el-date-picker type="date" v-model="value" placeholder="请输入" ></el-date-picker> </div> </template> <script> export default { data() { return { value: "", }; }, components: {}, methods: { handlePrimaryClick() { console.log("primary-click"); }, }, }; </script> <style lang='less'> </style><template> <div> <el-date-picker type="date" v-model="value" placeholder="请输入" ></el-date-picker> </div> </template> <script> export default { data() { return { value: "", }; }, components: {}, methods: { handlePrimaryClick() { console.log("primary-click"); }, }, }; </script> <style lang='less'> </style>
- main.js
import Vue from 'vue'import App from './App.vue'import router from './router'import store from './store'import ElementUI from 'element-ui';import 'element-ui/lib/theme-chalk/index.css';Vue.config.productionTip = falseVue.use(ElementUI)new Vue({router,store,render: h => h(App)}).$mount('#app')import Vue from 'vue' import App from './App.vue' import router from './router' import store from './store' import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; Vue.config.productionTip = false Vue.use(ElementUI) new Vue({ router, store, render: h => h(App) }).$mount('#app')import Vue from 'vue' import App from './App.vue' import router from './router' import store from './store' import ElementUI from 'element-ui'; import 'element-ui/lib/theme-chalk/index.css'; Vue.config.productionTip = false Vue.use(ElementUI) new Vue({ router, store, render: h => h(App) }).$mount('#app')
- package.json
注意对应包的版本要包吃一致
{"name": "vue2-project-test","version": "0.1.0","private": true,"scripts": {"serve": "vue-cli-service serve","build": "vue-cli-service build"},"dependencies": {"@antv/x6": "1.12.14","@antv/x6-vue-shape": "1.1.4","element-ui": "2.15.0","core-js": "^3.8.3","vue": "^2.6.14","vue-grid-layout": "^2.4.0","vue-router": "^3.5.1","vuex": "^3.6.2"},"devDependencies": {"@vue/cli-plugin-babel": "~5.0.0","@vue/cli-plugin-router": "~5.0.0","@vue/cli-plugin-vuex": "~5.0.0","@vue/cli-service": "~5.0.0","less": "^4.0.0","less-loader": "^8.0.0","vue-template-compiler": "^2.6.14"}}{ "name": "vue2-project-test", "version": "0.1.0", "private": true, "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build" }, "dependencies": { "@antv/x6": "1.12.14", "@antv/x6-vue-shape": "1.1.4", "element-ui": "2.15.0", "core-js": "^3.8.3", "vue": "^2.6.14", "vue-grid-layout": "^2.4.0", "vue-router": "^3.5.1", "vuex": "^3.6.2" }, "devDependencies": { "@vue/cli-plugin-babel": "~5.0.0", "@vue/cli-plugin-router": "~5.0.0", "@vue/cli-plugin-vuex": "~5.0.0", "@vue/cli-service": "~5.0.0", "less": "^4.0.0", "less-loader": "^8.0.0", "vue-template-compiler": "^2.6.14" } }{ "name": "vue2-project-test", "version": "0.1.0", "private": true, "scripts": { "serve": "vue-cli-service serve", "build": "vue-cli-service build" }, "dependencies": { "@antv/x6": "1.12.14", "@antv/x6-vue-shape": "1.1.4", "element-ui": "2.15.0", "core-js": "^3.8.3", "vue": "^2.6.14", "vue-grid-layout": "^2.4.0", "vue-router": "^3.5.1", "vuex": "^3.6.2" }, "devDependencies": { "@vue/cli-plugin-babel": "~5.0.0", "@vue/cli-plugin-router": "~5.0.0", "@vue/cli-plugin-vuex": "~5.0.0", "@vue/cli-service": "~5.0.0", "less": "^4.0.0", "less-loader": "^8.0.0", "vue-template-compiler": "^2.6.14" } }
最终效果
© 版权声明
文章版权归作者所有,未经允许请勿转载,侵权请联系 admin@trc20.tw 删除。
THE END