当前位置: 首页 > news >正文

前端绘制流程节点数据

根据数据结构和节点的层级、子节点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>

相关文章:

前端绘制流程节点数据

根据数据结构和节点的层级、子节点id&#xff0c;前端自己绘制节点位置和关联关系、指向、已完成节点等 <template><div><div>通过后端节点和层级&#xff0c;绘制出节点以及关联关系等</div><div class"container" ref"container&…...

2024年顶级算法-黑翅鸢优化算法(BKA)-详细原理(附matlab代码)

黑翅鸢是一种上半身蓝灰色&#xff0c;下半身白色的小型鸟类。它们的显著特征包括迁徙和捕食行为。它们以小型哺乳动物、爬行动物、鸟类和昆虫为食&#xff0c;具有很强的悬停能力&#xff0c;能够取得非凡的狩猎成功。受其狩猎技能和迁徙习惯的启发&#xff0c;该算法作者建立…...

Linux 内核开发 28 内核模块文件ko文件介绍

Linux 内核开发 28 内核模块文件ko文件介绍 1. ELF格式简介 内核模块文件ko文件&#xff0c;格式为elf格式&#xff0c; ELF&#xff08;Executable and Linkable Format&#xff09;可执行链接格式&#xff0c;是一种用于存储可执行程序、目标代码、共享库和内核模块的标准文件…...

DDR5—新手入门学习(一)【1-5】

目录 1、DDR背景 &#xff08;1&#xff09;SDR SDRAM时代 &#xff1a; &#xff08;2&#xff09;DDR SDRAM的创新 &#xff1a; &#xff08;3&#xff09;DDR技术的演进 &#xff1a; &#xff08;4&#xff09;需求推动&#xff1a; 2、了解内存 &#xff08;1&…...

力扣HOT100 - 138. 随机链表的复制

解题思路&#xff1a; class Solution {public Node copyRandomList(Node head) {if(headnull) return null;Node p head;//第一步&#xff0c;在每个原节点后面创建一个新节点//1->1->2->2->3->3while(p!null) {Node newNode new Node(p.val);newNode.next …...

深入分析 Android Activity (五)

深入分析 Android Activity (五) 1. Activity 的进程和线程模型 在 Android 中&#xff0c;Activity 默认在主线程&#xff08;也称为 UI 线程&#xff09;中运行。理解进程和线程模型对于开发响应迅速且无阻塞的应用程序至关重要。 1.1 主线程与 UI 操作 所有 UI 操作必须…...

Kubernetes 应用滚动更新

Kubernetes 应用版本号 在 Kubernetes 里&#xff0c;版本更新使用的不是 API 对象&#xff0c;而是两个命令&#xff1a;kubectl apply 和 kubectl rollout&#xff0c;当然它们也要搭配部署应用所需要的 Deployment、DaemonSet 等 YAML 文件。 在 Kubernetes 里应用都是以 …...

五分钟”手撕“图书管理系统

前言&#xff1a; 图书馆管理系统需要结合JavaSE的绝大部分知识&#xff0c;是一个很好的训练项目。 为了让大家更加方便的查阅与学习&#xff0c;我把代码放开头&#xff0c;供大家查询。 还有对代码的分析&#xff0c;我将以类为单位分开讲解。 目录 全部代码 Main类 Us…...

8个实用网站和软件,收藏起来一定不后悔~

整理了8个日常生活中经常能用得到的网站和软件&#xff0c;收藏起来一定不会后悔~ 1.ZLibrary zh.zlibrary-be.se/这个网站收录了超千万的书籍和文章资源&#xff0c;国内外的各种电子书资源都可以在这里搜索&#xff0c;98%以上都可以在网站内找到&#xff0c;并且支持免费下…...

电商内卷时代,视频号小店凭借一己之力“脱颖而出”

大家好&#xff0c;我是电商笨笨熊 今年618各大电商平台花样百出&#xff1b; 某宝更是直接取消了“预售”&#xff0c;从5月就开始进入618预热期&#xff1b; 不少玩家既开心又难过&#xff0c;市场如此内卷&#xff0c;618确实是个爆发期&#xff0c;但更多的需要不断压低…...

【论文笔记】| 定制化生成PuLID

PuLID: Pure and Lightning ID Customization via Contrastive Alignment ByteDance, arXiv:2404.16022v1 Theme: Customized generation 原文链接&#xff1a;https://arxiv.org/pdf/2404.16022 Main Work 提出了 Pure 和 Lightning ID 定制 (PuLID)&#xff0c;这是一种用于…...

P1638 逛画展

题目描述 博览馆正在展出由世上最佳的 &#x1d45a; 位画家所画的图画。 游客在购买门票时必须说明两个数字&#xff0c;&#x1d44e; 和 &#x1d44f;&#xff0c;代表他要看展览中的第 &#x1d44e; 幅至第 &#x1d44f; 幅画&#xff08;包含 &#x1d44e;,&#x1…...

Linux(centos)常用命令

Linux&#xff08;Centos&#xff09;常用命令使用说明文档 切换到/home目录下 使用cd命令切换目录&#xff0c;例如&#xff1a; cd /home列出/home目录下的所有文件 使用ls命令列出目录下的文件和子目录&#xff0c;例如&#xff1a; ls /home新建目录dir1 使用mkdir命…...

从入门到精通:掌握Scrapy框架的关键技巧

在当今信息爆炸的时代&#xff0c;获取并利用网络数据成为了许多行业的核心竞争力之一。而作为一名数据分析师、网络研究者或者是信息工作者&#xff0c;要想获取网络上的大量数据&#xff0c;离不开网络爬虫工具的帮助。而Scrapy框架作为Python语言中最为强大的网络爬虫框架之…...

Vue3按顺序调用新增和查询接口

Vue3按顺序调用新增和查询接口 一、前言1、代码 一、前言 如果你想将两个调用接口的操作封装在不同的方法中&#xff0c;你可以考虑将这两个方法分别定义为异步函数&#xff0c;并在需要时依次调用它们。以下是一个示例代码&#xff1a; 1、代码 <template><div>…...

sizeof的了解

32位编译器 qDebug() << "int:" << sizeof(int);qDebug() << "char:" << sizeof(char);qDebug() << "char*:" << sizeof(char*); 字节数&#xff1a; int: 4 char: 1 char*: 4 64位编译器 字节数&#…...

PostgreSQL 教程

## PostgreSQL 教程 ### 1. PostgreSQL 概述 PostgreSQL 是一个开源的对象关系型数据库管理系统&#xff08;ORDBMS&#xff09;&#xff0c;以其高扩展性和合规性闻名&#xff0c;支持 SQL 和 JSON 查询。 ### 2. 安装与配置 - **下载与安装**&#xff1a;从 PostgreSQL 官方…...

《基于Jmeter的性能测试框架搭建》改进一

《基于Jmeter的性能测试框架搭建》文末笔者提到了不少待改进之处&#xff0c;如下所示。 Grafana性能图表实时展现&#xff0c;测试过程中需实时截图形成测试报告&#xff0c;不够人性化。解决方案&#xff1a;自动生成测试报告并邮件通知。 Grafana性能图表需测试人员实时监控…...

计算机二进制表示和存储各种数据

目录 计算机二进制是什么 计算机中二进制数作用 不同数据的表示和存储 数字 文字 图片 音频 视频 计算机的中数据的显示和存储 计算机二进制是什么 计算机二进制数&#xff1a;计算机里存储的一切都是以二进制的0和1来表示。二进制是计算机使用的数字编码系统&#x…...

玩机社区 - 2024年最美社区源码开源

玩机社区 - 2024年最美社区源码开源 教程源码文档都内置到压缩包了 https://pan.baidu.com/s/1xwcscTne-JMbmKEntiuAuA?pwd78oi...

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…...

STM32+rt-thread判断是否联网

一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...

Opencv中的addweighted函数

一.addweighted函数作用 addweighted&#xff08;&#xff09;是OpenCV库中用于图像处理的函数&#xff0c;主要功能是将两个输入图像&#xff08;尺寸和类型相同&#xff09;按照指定的权重进行加权叠加&#xff08;图像融合&#xff09;&#xff0c;并添加一个标量值&#x…...

华为OD机试-食堂供餐-二分法

import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...

Keil 中设置 STM32 Flash 和 RAM 地址详解

文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

【Go】3、Go语言进阶与依赖管理

前言 本系列文章参考自稀土掘金上的 【字节内部课】公开课&#xff0c;做自我学习总结整理。 Go语言并发编程 Go语言原生支持并发编程&#xff0c;它的核心机制是 Goroutine 协程、Channel 通道&#xff0c;并基于CSP&#xff08;Communicating Sequential Processes&#xff0…...

2025年渗透测试面试题总结-腾讯[实习]科恩实验室-安全工程师(题目+回答)

安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 腾讯[实习]科恩实验室-安全工程师 一、网络与协议 1. TCP三次握手 2. SYN扫描原理 3. HTTPS证书机制 二…...

LabVIEW双光子成像系统技术

双光子成像技术的核心特性 双光子成像通过双低能量光子协同激发机制&#xff0c;展现出显著的技术优势&#xff1a; 深层组织穿透能力&#xff1a;适用于活体组织深度成像 高分辨率观测性能&#xff1a;满足微观结构的精细研究需求 低光毒性特点&#xff1a;减少对样本的损伤…...

为什么要创建 Vue 实例

核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...

MySQL:分区的基本使用

目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区&#xff08;Partitioning&#xff09;是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分&#xff08;分区&#xff09;可以独立存储、管理和优化&#xff0c;…...