前端绘制流程节点数据

根据数据结构和节点的层级、子节点id,前端自己绘制节点位置和关联关系、指向、已完成节点等
在这里插入图片描述

<template><div><div>通过后端节点和层级,绘制出节点以及关联关系等</div><div class="container" ref="container"><div v-for="(item, index) in nodeList" :key="index" class="point":style="{ position: 'absolute', top: item.y + 'px', left: item.x + 'px', }">{{ item.name }}-{{ item.isDone }}</div></div></div>
</template><script>export default {data() {return {// 节点数据 level代表层级 lastIds代表与其关联的下一级节点的idnodeList: [{ name: '点1', id: 1, level: 1, lastIds: [2, 3] },{ name: '点2', id: 2, level: 2, lastIds: [4] },{ name: '点3', id: 3, level: 2, lastIds: [5, 10, 6] },{ name: '点4', id: 4, level: 3, lastIds: [7] },{ name: '点5', id: 5, level: 3, lastIds: [7] },{ name: '点10', id: 10, level: 3, lastIds: [9] },{ name: '点6', id: 6, level: 3, lastIds: [8] },{ name: '点7', id: 7, level: 4, lastIds: [9] },{ name: '点8', id: 8, level: 4, lastIds: [9] },{ name: '点9', id: 9, level: 5, lastIds: [] },],lineList: [], // 两两连接线关系的数据项pathsList: [], // 首位相连的完整链路num: 7, // 当前节点idsSet: [], //当前节点及已路过的节点集合};},mounted() {const container = document.getElementsByClassName('container')[0]// 计算定位节点const addCoordinates = (nodes, width) => {// 按 level 分组节点const groupedNodes = nodes.reduce((acc, node) => {if (!acc[node.level]) {acc[node.level] = [];}acc[node.level].push(node);return acc;}, {});// 处理每个 level 的节点Object.keys(groupedNodes).forEach(level => {const group = groupedNodes[level];const count = group.length;const spacing = width / (count + 1); // 间距group.forEach((node, index) => {node.x = spacing * (index + 1);node.y = node.level * 66;});});// 返回处理后的节点数组return nodes;};// 查找存在连接关系的项,并将信息存入 lineList 数组const findConnectedNodes = (nodes) => {const lineList = [];nodes.forEach(node => {node.lastIds.forEach(lastId => {// 根据 id 找到相应的节点const connectedNode = nodes.find(item => item.id === lastId);// 将节点信息存入 lineList 数组,确保起点是当前节点,终点是连接的节点if (connectedNode) {lineList.push({x1: node.x,y1: node.y,x2: connectedNode.x,y2: connectedNode.y,lineAB: [node.id, connectedNode.id]});}});});return lineList;};this.nodeList = addCoordinates(this.nodeList, 600);console.log('nodeList 节点定位', this.nodeList);// 查找存在连接关系的项this.lineList = findConnectedNodes(this.nodeList);console.log('lineList 两两关联', this.lineList);// 绘制线段const drawLine = (x1, y1, x2, y2, isDone) => {// console.log(x1, y1, x2, y2, isDone);const length = Math.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2);const angle = Math.atan2(y2 - y1, x2 - x1) * (180 / Math.PI);const line = document.createElement('div');line.className = 'line';line.style.width = `${length}px`;line.style.transform = `rotate(${angle}deg)`;line.style.transformOrigin = '0 0';line.style.position = 'absolute';line.style.top = `${y1}px`;line.style.left = `${x1}px`;line.style.backgroundColor = 'black';line.style.height = '2px';line.style.backgroundColor = isDone ? '#1fff' : 'black';// 创建箭头const arrow = document.createElement('div');arrow.className = 'arrow';arrow.style.position = 'absolute';arrow.style.width = '0';arrow.style.height = '0';arrow.style.borderLeft = '5px solid transparent';arrow.style.borderRight = '5px solid transparent';arrow.style.borderTop = '15px solid black';arrow.style.borderTopColor = isDone ? '#1fff' : 'black';// 计算箭头位置arrow.style.top = `${y2 - 10}px`;arrow.style.left = `${x2 - 6}px`;arrow.style.transform = `rotate(${angle + 270}deg)`;arrow.style.transformOrigin = 'center center';if (container) {container.appendChild(line);container.appendChild(arrow);}};// 找到完整链路const getPath = (arr) => {let pathsList = [];// 构建图的邻接表表示let graph = {};arr.forEach(({ lineAB: [start, end] }) => {if (!graph[start]) {graph[start] = [];}graph[start].push(end);});// 深度优先搜索函数function dfs(node, path) {path.push(node);if (!graph[node] || graph[node].length === 0) {pathsList.push([...path]);} else {for (let neighbor of graph[node]) {dfs(neighbor, path);}}path.pop();}// 找到所有的起点(即那些不作为任何其他点的终点的点)let allPoints = new Set(arr.flatMap(({ lineAB }) => lineAB));let endPoints = new Set(arr.map(({ lineAB: [, end] }) => end));let startPoints = [...allPoints].filter(point => !endPoints.has(point));// 从每个起点开始搜索完整路径startPoints.forEach(start => {dfs(start, []);});return pathsList}this.pathsList = getPath(this.lineList)console.log('pathsList完整链路', this.pathsList);let that = thisfunction updateNodeListWithDoneStatus(nodeList, pathsList, num) {let idsSet = new Set();// 找到所有包含 num 的路径,并提取 num 之前的节点pathsList.forEach(path => {let index = path.indexOf(num);if (index !== -1) {for (let i = 0; i <= index; i++) {idsSet.add(path[i]);}}});idsSet.forEach(val => {that.idsSet.push(val)});console.log('当前及链路上的节点', that.idsSet);// 更新 nodeList,添加 isDone 属性nodeList.forEach(node => {if (idsSet.has(node.id)) {node.isDone = true;} else {node.isDone = false;}});// 更新 lineList 添加 isDone 属性that.lineList.forEach(line => {// 如果 lineAB 中的任意一个节点在 idsSet 中,则标记为已完成// line.isDone = idsSet.has(line.lineAB[0]) && idsSet.has(line.lineAB[1]);line.isDone = line.lineAB.every(item => that.idsSet.includes(item))});return nodeList;}// 示例:查找当前几点之前的链路ids集合并更新nodeListlet updatedNodeList = updateNodeListWithDoneStatus(this.nodeList, this.pathsList, this.num);this.nodeList = updatedNodeList// 遍历绘制线段for (let index = 0; index < this.lineList.length; index++) {let element = this.lineList[index]setTimeout(() => {drawLine(element.x1, element.y1, element.x2, element.y2, element.isDone)}, 110);}this.$forceUpdate()console.log('最终节点数据', this.nodeList);console.log('最终两两连接线关系的数据', this.lineList);},};
</script><style lang="less" scoped>
.container {position: relative;width: 600px;height: 600px;border: 1px solid #000;
}.point {background-color: red;
}.line {background-color: black;height: 2px;position: absolute;pointer-events: none; // 防止影响鼠标事件
}
</style>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/330612.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Numpy】深入解析numpy.mgrid()函数

numpy.mgrid()&#xff1a;多维网格生成与数值计算的利器 &#x1f308; 欢迎莅临我的个人主页&#x1f448;这里是我深耕Python编程、机器学习和自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;并乐于分享知识与经验的小天地&#xff01;&#x1f387; &#x1f393…

张大哥笔记:改变自己,才是改变一切的开始

人往往有一种惰性&#xff0c;总喜欢把希望寄托于别人&#xff01;比如会将注意力投向外部因素如环境、他人或命运从而期望为我们的生活带来突破和转机。但现实往往是残酷的&#xff0c;不会发生任何改变的&#xff01;真正的改变来自于自己&#xff0c;自我革新才是改变整个局…

开源实用!猫抓媒体嗅探浏览器插件

CatCatch&#xff1a;网络资源&#xff0c;一触即发 - 精选真开源&#xff0c;释放新价值。 概览 CatCatch是一个专为浏览器设计的资源嗅探扩展&#xff0c;旨在帮助用户轻松捕获和分析网页中的各种资源。无论是视频、音频还是其他类型的文件&#xff0c;猫爪都能提供直观的界…

AI - 各类AI针对Excel分析对比

一个水果销量表&#xff0c;Excel包含多个年份sheet&#xff0c;需要提取某个品种的水果每年的销量&#xff0c;看看几个AI的分析结果吧 1、文心一言3.5&#xff08;不支持Excel&#xff09; 不支持上传Excel文件 2、 通义千问2.5&#xff08;完成★&#xff09; 顺利完成…

虚拟机网络设置为桥接模式后未显示网络

本方法为&#xff0c;VMware配置正确&#xff0c;但在尝试其他办法后未能成功解决的人提供一种方法 本机的虚拟机使用NAT模式正常使用 但是使用桥接模式后重启&#xff0c;未发现虚拟机内网络设置,详见下图&#xff1a; 使用 ifconfig 查看网络详情 发现没有ens33接口 查看硬…

kubectl

陈述式资源管理方法 kubernetes 集群管理集群资源的唯一入口是通过相应的方法调用apiserver的接口 kubectl 是官方的CLI命令行工具&#xff0c;用于与apiserver进行通信&#xff0c;将用户在命令行输入的命令&#xff0c;组织转换成apiserver能识别的信息&#xff0c;进而实现…

当面试官问出“Unsafe”类时,我就知道这场面试废了,祖坟都能给你问出来!

一、写在开头 依稀记得多年以前的一场面试中&#xff0c;面试官从Java并发编程问到了锁&#xff0c;从锁问到了原子性&#xff0c;从原子性问到了Atomic类库&#xff08;对着JUC包进行了刨根问底&#xff09;&#xff0c;从Atomic问到了CAS算法&#xff0c;紧接着又有追问到了…

B站滑块登录之极验点选

滑块登录这些东西都不是很难&#xff0c;我个人的去处理的话一般会考虑三种方案&#xff0c;一个是自动化selenium 二是各类打码平台 三是ocr识别&#xff0c;本文是selenium接打码平台&#xff0c;也是个比较常规的操作。 先常规步骤跟着来吧&#xff0c;做登录的话把基本的模…

汇聚荣:新手做拼多多应该注意哪些事项?

新手在拼多多开店&#xff0c;面临的是竞争激烈的市场和复杂的运营规则。要想在这个平台上脱颖而出&#xff0c;必须注意以下几个关键事项。 一、市场调研与定位 深入了解市场需求和竞争对手情况是新手开店的首要步骤。选择有潜力的细分市场&#xff0c;并针对目标消费者群体进…

【C语言】指针(三)

目录 一、字符指针 1.1 ❥ 使用场景 1.2 ❥ 有关字符串笔试题 二、数组指针 2.1 ❥ 数组指针变量 2.2 ❥ 数组指针类型 2.3 ❥ 数组指针的初始化 三、数组指针的使用 3.1 ❥ 二维数组和数组名的理解 3.2 ❥ 二维数组传参 四、函数指针 4.1 ❥ 函数的地址 4.2 ❥ 函数…

知识分享:大数据信用花导致的评分不足多久能恢复

随着金融风控领域越来越科技化&#xff0c;基于大数据技术的金融风控成为了贷前风控不可或缺的重要环节&#xff0c;相信很多人在申贷的时候都听说过大数据信用和综合评分等词语&#xff0c;那大数据信用花导致的评分不足多久能恢复呢?本文带大家一起去了解一下。 首先&#x…

【面试干货】矩阵对角线元素之和

【面试干货】矩阵对角线元素之和 1、实现思想2、代码实现 &#x1f496;The Begin&#x1f496;点点关注&#xff0c;收藏不迷路&#x1f496; 1、实现思想 创建一个3x3的二维数组来表示输入的矩阵。通过嵌套循环读取输入的矩阵元素&#xff0c;并将其保存到数组中。再次嵌套循…

C++:vector基础讲解

hello&#xff0c;各位小伙伴&#xff0c;本篇文章跟大家一起学习《C&#xff1a;vector基础讲解》&#xff0c;感谢大家对我上一篇的支持&#xff0c;如有什么问题&#xff0c;还请多多指教 &#xff01; 如果本篇文章对你有帮助&#xff0c;还请各位点点赞&#xff01;&#…

抖音跳转微信卡片制作教程 小白也能搞

实测可以正常跳转&#xff0c;很牛逼&#xff0c;给大家分享一下~ 这是我做出来抖音发出去的效果&#xff0c;大家会制作了可以去卖钱&#xff0c;市场上一个这个卡片都要卖50-200&#xff0c;很不错的&#xff01;&#xff01; https://pan.baidu.com/s/1xPmGAWPcbAp7eXg7Dc…

若依 ruoyi-vue 用户账号前后端参数校验密码 手机号 邮箱

前端 <el-dialog :title"title" :visible.sync"open" width"800px" append-to-body><el-form ref"form" :model"form" :rules"rules" label-width"120px"><el-row><el-col :span…

【竞技宝】欧洲杯:吉鲁退出法国队,欧洲杯后主动让贤

吉鲁是法国队功勋中锋&#xff0c;为球队立下过赫赫战功。法国队能在2018年拿到久违的世界杯冠军&#xff0c;吉鲁身为主力锋霸功不可没。每当&#xff0c;法国队在比赛中遇到僵局&#xff0c;吉鲁总会站出来&#xff0c;为球队做出应有的贡献。吉鲁在法国队的作用不仅仅体现在…

Day48 Javascript详解

Day48 Javascript详解 文章目录 Day48 Javascript详解一、什么是javascript二、javascript特点三、 Javascript的历史四、Javascript vs Java五、JS的基本数据类型六、JS基本数据类型的特殊点七、数组 一、什么是javascript JavaScript是一种高级的、解释型的编程语言&#xf…

【论文速读】|探索ChatGPT在软件安全应用中的局限性

本次分享论文&#xff1a;Exploring the Limits of ChatGPT in Software Security Applications 基本信息 原文作者&#xff1a;Fangzhou Wu, Qingzhao Zhang, Ati Priya Bajaj, Tiffany Bao, Ning Zhang, Ruoyu "Fish" Wang, Chaowei Xiao 作者单位&#xff1a;威…

[Linux] 进程概念

目录 1.冯诺依曼硬件体系结构 2.操作系统&#xff08;OS&#xff09; 3.系统接口 4.进程的概念 5.进程状态 6.四个其他概念 7.环境变量 8.进程地址空间 1.冯诺依曼硬件体系结构 在冯诺依曼体系结构中&#xff0c;计算机是由输入、输出、存储设备和中央处理器cpu组成的。图中体结…

子比主题ACG美化插件[全开源]

WordPress插件是一种可以扩展和增强WordPress网站功能的应用程序。子比主题ACG美化插件听起来像是一个专门为ACG&#xff08;动画、漫画、游戏&#xff09;爱好者设计的美化插件&#xff0c;它可能包含多种功能来改善网站的外观和用户体验。 内置功能开关100意味着这个插件提供…