在vue3中浅尝antv X6 2.0 demo(三)

2023-11-02 05:30

本文主要是介绍在vue3中浅尝antv X6 2.0 demo(三),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

终于抽空把antv X6 2.0这个版本的demo抽出来了,原以为项目会一直使用vue3去做这个流程,结果最近项目经理说antv X6的菜单功能只有react才能用...然鹅...写到菜单模块的时候,发现都可以用的...

目前我项目里面react版本的多一些功能(如:新增节点时自动布局、右键菜单,如图)

这篇记一下这个小demo的一些功能和实现~~~

(附上demo仓库连接: https://github.com/Tipchak5/vue3_antv_X6_2.0.git)

  • 导入模版 (画布中的节点是点击插入模版直接形成的,且左侧目录树与模板的关系是相对应的,目录树的label我用的是节点id来显示的)

 代码实现:

1、先引入模版数据tsakMasterplate(后期应该是调后端接口选择模版导入)

2、直接调这个leadInMasterplate方法,导入模板前会先清空画布

const tsakMasterplate = ref(require('../../assets/masterplate.json')); // 引入模版数据/** 导入模版 */
const leadInMasterplate = async () => {// 清空所有单元格const cells = graph.getCells(); // 获取所有单元格graph.removeCells(cells); // 画布清空// 模版渲染await new Promise((resolve) => setTimeout(resolve, 100));graph.fromJSON(tsakMasterplate.value).centerContent(); // 引入模版数据 这里tsakMasterplate是我自己写的一个死数据// 对应的目录树treeInfo.value = [{label: 'node2',children: [{label: 'task2',children: [{label: 'task3',children: [],},{label: 'task5',children: [],},],},],},];
};

  • 导出json(其实导出json就相当于创建了一个模版,以json的格式将你要常用的节点模版传给后端,以便后期调用)

    代码实现:

// 导出json
function printNodeList() {console.log(graph.toJSON(), '导出数组');console.log(JSON.stringify(graph.toJSON({ diff: true })), 'JSON');graph.clearCells();
}

  • 新增节点

  • <el-button type="primary" :icon="Plus" :title="task" @mousedown="startDrag(task, $event)" >新增{{ task }}
    </el-button>const task = ref('任务');// 新增任务节点
    const startDrag = (type) => {const tree = startDragToGraph(graph, type); // graph就是初始化的画布 因为我是将startDragToGraph方法抽出来放在公共js中直接调用的 所以传了graph 如果你在当前页面就不需要传了id.value = tree.id;const label = { label: tree.id, children: [] };treeInfo.value[0].children.push(label); // 更新树目录
    };// 这里startDragToGraph方法如下
    let count = 0;
    const startDragToGraph = (graph, type) => {let node = null;node = graph.addNode({shape: 'custom-rect',attrs: {label: {text: type,// 自动换行textWrap: {width: '90%',height: '80%',ellipsis: true,breakWord: true,},},},x: -50,y: -50,id: `task${++count}`, // 设置节点id 方便后期根据id抓节点 进行一些操作});ElMessage.success(`添加任务节点${node.id}成功!`);return node;
    };

  •  节点连接时的一些操作

        1、首先,它节点中带有一个叫做port的连接桩(也就是节点边上的小圆点)

        2、什么时候显示port,并让节点之间相连接?案例告诉我们是鼠标移入节点时显示port,

                移出隐藏

 代码实现

// 连接桩
const ports = {groups: {top: {position: 'top',attrs: {circle: {r: 2,magnet: true,stroke: 'black',strokeWidth: 1,fill: '#fff',style: {visibility: 'hidden',},},},},right: {position: 'right',attrs: {circle: {r: 2,magnet: true,stroke: 'black',strokeWidth: 1,fill: '#fff',style: {visibility: 'hidden',},},},},bottom: {position: 'bottom',attrs: {circle: {r: 2,magnet: true,stroke: 'black',strokeWidth: 1,fill: '#fff',style: {visibility: 'hidden',},},},},left: {position: 'left',attrs: {circle: {r: 2,magnet: true,stroke: 'black',strokeWidth: 1,fill: '#fff',style: {visibility: 'hidden',},},},},},items: [{group: 'top',},{group: 'right',},{group: 'bottom',},{group: 'left',},],
};

port的显示与隐藏

	graph.on('node:mouseenter', () => {const container = document.getElementById('graph-container');const ports = container.querySelectorAll('.x6-port-body');showPorts(ports, true);});graph.on('node:mouseleave', () => {const container = document.getElementById('graph-container');const ports = container.querySelectorAll('.x6-port-body');showPorts(ports, false);}); // 以上代码需要放在onMounted周期中 或者画布初始化的方法中也可以// 函数showPorts我是单独抽成一个公共js的文件里面使用的 看个人实际情况function showPorts(ports, show) {for (let i = 0, len = ports.length; i < len; i = i + 1) {ports[i].style.visibility = show ? 'visible' : 'hidden';}}

3、连接成功后,根据连接的目标节点和源节点来判断 树目录 的数据结构要如何显示 (么错,递归要来了...)

拿到目标节点和源节点后,去 树目录 里面找这个两个id,有的话就让 树目录里面的目标节点 成为源节点子,然后你就可以拥有一个和节点相对应的目录树了

其中有一点很重要,当连接成功后,目录树里面 目标节点 会成为 源节点 的子后,要记得删除目录树原来的那个目标节点(顺便说一下: react里面的书组件是不支持整个目录树有相同的2个节点出现的,也就是一个子节点不能有两个源节点)

代码实现 

	// 节点连接成功时graph.on('edge:connected', ({ isNew, edge }) => {if (isNew) {const sourceId = edge.getSourceCell().id;const targetId = edge.getTargetCell().id;// findNodeById 是我写的一个公共方法 在树目录里找和节点相同的idconst sourceNode = findNodeById(treeInfo.value[0].children,sourceId); // 源节点idconst targetNode = findNodeById(treeInfo.value[0].children,targetId); // 目标节点idif (sourceNode && targetNode) {sourceNode.children.push(targetNode);// push之后删除原targetNodefor (let i = 0; i < treeInfo.value[0].children.length; i++) {const node = treeInfo.value[0].children[i];if (node.label === targetId) {treeInfo.value[0].children.splice(i, 1);}}}}});// 树形目录中添加节点
export const findNodeById = (nodes, id) => {for (let i = 0; i < nodes.length; i++) {const node = nodes[i];if (node.label === id) {return node;}if (node.children) {const result = findNodeById(node.children, id);if (result) {return result;}}}return null;
};

  • 删除功能

删除节点可以通过绑定键盘按钮操作,也可以使用官方的节点工具(想要实现下图的删除,可以参考官方文档,so easy)

我主要是通过键盘按钮('delete', 'backspace'都可以删除)来操作的,需要安装X6对应的插件@antv/x6-plugin-keyboard 来绑定删除事件,当然,树目录也要删除对应的节点数据(删除的时候,如果该节点有子节点会连带后续所有的子节点都删除

代码实现

/** 删除的一些操作 */graph.bindKey(['delete', 'backspace'], () => {const cells = graph.getSelectedCells();const cellsId = cells[0].id; // 获取删除节点的idif (cells.length) {const allChildrenNode = [];const nodes = cells.filter((cell) => cell.isNode());nodes.forEach((node) => {//  获取后继单元格const successors = graph.getSuccessors(node, { depth: 3 });allChildrenNode.push(...successors);console.log(successors, 'successors');});// 获取后继节点idlet keyArr = [];allChildrenNode.forEach((i) => {keyArr.push(i.id);});// 删除当前节点graph.removeCells(cells);removeNodes(keyArr);// 如果删除的节点和其他节点有共同子节点时 删除书目录中对应的数据let newArr = [...keyArr, cellsId]; // 要删除的节点id// 删除对应树目录数据deleteNodeById(treeInfo.value[0].children, newArr);console.log(newArr, 'newArr');}});// 删除后继节点
function removeNodes(keys) {if (keys && keys.length > 0) {keys.forEach((key) => {graph.removeNode(key, {deep: true,});});}
}// 树形目录中删除对应节点
export const deleteNodeById = (treeArr, keyArr) => {let deleted = false;for (let i = treeArr.length - 1; i >= 0; i--) {const node = treeArr[i];if (keyArr.includes(node.label)) {treeArr.splice(i, 1);deleted = true;} else if (node.children && node.children.length) {if (deleteNodeById(node.children, keyArr)) {deleted = true;if (node.children.length === 0) {node.children = [];}}}}return deleted;
};

以上差不多就是这个demo的所有功能了,还有文本编辑啥的,我还没测过,暂时就不管了,先这样吧~

期待与大家一起共同交流,共同进步,如果有地方写的烂,还望海涵,欢迎大佬指导,最近卡在react版的自定义工具注册上,react是真的不是很熟悉...头疼...

这篇关于在vue3中浅尝antv X6 2.0 demo(三)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/328435

相关文章

Vue3 的 shallowRef 和 shallowReactive:优化性能

大家对 Vue3 的 ref 和 reactive 都很熟悉,那么对 shallowRef 和 shallowReactive 是否了解呢? 在编程和数据结构中,“shallow”(浅层)通常指对数据结构的最外层进行操作,而不递归地处理其内部或嵌套的数据。这种处理方式关注的是数据结构的第一层属性或元素,而忽略更深层次的嵌套内容。 1. 浅层与深层的对比 1.1 浅层(Shallow) 定义

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

【 html+css 绚丽Loading 】000046 三才归元阵

前言:哈喽,大家好,今天给大家分享html+css 绚丽Loading!并提供具体代码帮助大家深入理解,彻底掌握!创作不易,如果能帮助到大家或者给大家一些灵感和启发,欢迎收藏+关注哦 💕 目录 📚一、效果📚二、信息💡1.简介:💡2.外观描述:💡3.使用方式:💡4.战斗方式:💡5.提升:💡6.传说: 📚三、源代码,上代码,可以直接复制使用🎥效果🗂️目录✍️

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

计算机毕业设计 大学志愿填报系统 Java+SpringBoot+Vue 前后端分离 文档报告 代码讲解 安装调试

🍊作者:计算机编程-吉哥 🍊简介:专业从事JavaWeb程序开发,微信小程序开发,定制化项目、 源码、代码讲解、文档撰写、ppt制作。做自己喜欢的事,生活就是快乐的。 🍊心愿:点赞 👍 收藏 ⭐评论 📝 🍅 文末获取源码联系 👇🏻 精彩专栏推荐订阅 👇🏻 不然下次找不到哟~Java毕业设计项目~热门选题推荐《1000套》 目录 1.技术选型 2.开发工具 3.功能

Vue3项目开发——新闻发布管理系统(六)

文章目录 八、首页设计开发1、页面设计2、登录访问拦截实现3、用户基本信息显示①封装用户基本信息获取接口②用户基本信息存储③用户基本信息调用④用户基本信息动态渲染 4、退出功能实现①注册点击事件②添加退出功能③数据清理 5、代码下载 八、首页设计开发 登录成功后,系统就进入了首页。接下来,也就进行首页的开发了。 1、页面设计 系统页面主要分为三部分,左侧为系统的菜单栏,右侧

【VUE】跨域问题的概念,以及解决方法。

目录 1.跨域概念 2.解决方法 2.1 配置网络请求代理 2.2 使用@CrossOrigin 注解 2.3 通过配置文件实现跨域 2.4 添加 CorsWebFilter 来解决跨域问题 1.跨域概念 跨域问题是由于浏览器实施了同源策略,该策略要求请求的域名、协议和端口必须与提供资源的服务相同。如果不相同,则需要服务器显式地允许这种跨域请求。一般在springbo

HTML提交表单给python

python 代码 from flask import Flask, request, render_template, redirect, url_forapp = Flask(__name__)@app.route('/')def form():# 渲染表单页面return render_template('./index.html')@app.route('/submit_form',

Java 后端接口入参 - 联合前端VUE 使用AES完成入参出参加密解密

加密效果: 解密后的数据就是正常数据: 后端:使用的是spring-cloud框架,在gateway模块进行操作 <dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>30.0-jre</version></dependency> 编写一个AES加密

vue2 组件通信

props + emits props:用于接收父组件传递给子组件的数据。可以定义期望从父组件接收的数据结构和类型。‘子组件不可更改该数据’emits:用于定义组件可以向父组件发出的事件。这允许父组件监听子组件的事件并作出响应。(比如数据更新) props检查属性 属性名类型描述默认值typeFunction指定 prop 应该是什么类型,如 String, Number, Boolean,