RelationMap图谱--VUE,真实项目提供mock数据

RelationMap官网: 在线配置官网(可以把数据放进去,直接看效果)

VUE2 效果:左侧列表栏,点击右侧显示对应的图谱

代码:按照代码直接贴过去,直接出效果

relationMap/index.vue

<template>
  <div class="graphBody">
    <List :list="list" @select-Graph="getGraph" />
    <Graph v-if="showGraph" :key="graphItem.rootId" :item="graphItem" />
  </div>
</template>

<script lang="ts">
import { getUuid } from "@/utils/generateUuid";

import List from "./list/index.vue";
import Graph from "./graph/index.vue";
import mockInfo from "./mock.json";

export default {
  components: {
    List,
    Graph,
  },
  data() {
    return {
      list: [],
      showGraph: false,
      graphItem: {},
    };
  },
  methods: {
    initListData() {
      this.list = mockInfo.data;
    },
    getGraph(item) {
      const data = {
        id: getUuid(),
        name: item.globalVariable.name,
        DefineType: item.globalVariable.defineType,
        ActualType: item.globalVariable.actualType,
        children: [],
      };

      const graphMap = new Map();

      item.callFunction.forEach((item) => {
        item.call.forEach((call) => {
          const fileName = call.location.split("/").pop();
          const secondNode = {
            id: getUuid(),
            fileName: fileName,
            location: call.location,
            children: [],
          };

          const threeNode = {
            id: getUuid(),
            functionName: item.functionName,
            children: [
              {
                id: getUuid(),
                startRow: call.startRow,
                endRow: call.endRow,
                startColumn: call.startColumn,
                endColumn: call.endColumn,
                mode: call.mode,
              },
            ],
          };
          // 如果没有相同文件
          if (!graphMap.has(call.location)) {
            secondNode.children.push(threeNode);
            graphMap.set(call.location, secondNode);
          } else {
            const copy = graphMap.get(call.location);

            copy.children[0].children.push({
              id: getUuid(),
              startRow: call.startRow,
              endRow: call.endRow,
              startColumn: call.startColumn,
              endColumn: call.endColumn,
            });

            graphMap.set(call.location, copy);
          }
        });

        const valuesIterator = graphMap.values();
        const valuesArray = Array.from(valuesIterator);
        data.children.push(...valuesArray);
        graphMap.clear();
      });

      // 合并具有相同 location 属性的对象的 children 属性
      const mergedData = data.children.reduce((acc, obj) => {
        const existingObj = acc.find((item) => item.location === obj.location);
        if (existingObj) {
          existingObj.children = existingObj.children.concat(obj.children);
        } else {
          acc.push(obj);
        }
        return acc;
      }, []);
      const newValue = { ...data, children: mergedData };
      const { nodes, edges } = this.convertToNodesAndLines(newValue);
      const graphData = {
        rootId: newValue.id,
        nodes,
        lines: edges,
      };
      this.graphItem = graphData;
      if (this.graphItem) {
        this.showGraph = true;
      }
    },
    convertToNodesAndLines(data) {
      const treeToNode = (node, parentId) => {
        const result = [];
        const { children, ...nodeData } = node;
        result.push({
          id: node.id,
          text: node.id,
          data: nodeData,
        });
        if (node.children && node.children.length > 0) {
          node.children.forEach(
            (item) => {
              result.push(...treeToNode(item, node.id));
            }
          );
        }
        return result;
      };
      const nodeArray = treeToNode(data, data.id);
      const treeToEdge = (node) => {
        const links = [];
        if (node.children && node.children.length > 0) {
          node.children.forEach(
            (item) => {
              const to = item.id;
              const from = node.id;
              links.push({
                id: `${to}->${from}`,
                to,
                from,
              });
              links.push(...treeToEdge(item));
            }
          );
        }
        return links;
      };
      const edgeArray = treeToEdge(data);
      return { nodes: nodeArray, edges: edgeArray };
    },
  },
  mounted() {
    this.initListData();
  },
};
</script>

<style scoped>
.graphBody {
  height: 100%;
  border-radius: 4px;
  border: 1px solid #222529;
  background: #191c1f;
  display: flex;
}

::-webkit-scrollbar {
  display: block;
}

::-webkit-scrollbar-thumb {
  background: #393d45;
}
</style>

使用的方法util/generateUuid.js

import { v1 as uuidv1 } from 'uuid'

// 去除-携带时间戳-uuid
export function getUuid() {
  // const timestamp = new Date().getTime()
  // 生成UUID时去掉连字符
  const uuid = uuidv1().replace(/-/g, '')
  // 截取前8位作为8位UUID
  const eightDigitUuid = uuid.substring(0, 12)
  return `${eightDigitUuid}`
}

relationMap/graph/index.vue

<template>
  <div>
    <div id="relation-graph-container" class="graph-wrapper">
      <RelationGraph ref="graphRef" :options="graphOptions">
        <template slot="node" slot-scope="{ node }">
          <div
            :class="`node-container node-type`"
            @click="nodeClick(node.data)"
          >
            <span
              v-if="node.data?.DefineType || node.data?.ActualType"
              class="type-style"
            >
              <div class="type-title">{{ node.data.name }}</div>
              <div class="content word-hidden type-content">
                <p>DefineType: {{ node.data.DefineType }}</p>
                <p>ActualType: {{ node.data.ActualType }}</p>
              </div>
            </span>
            <span v-if="node.data?.location" class="file-style">
              <div class="file-title">
                <div>{{ node.data.fileName || node.data.name }}</div>
              </div>
              <div>
                <span class="content word-hidden file-path"
                  >路径: {{ node.data?.location }}</span
                >
              </div>
            </span>
            <div v-if="node.data?.functionName" class="function-style">
              <div>
                <span clsss="content word-hidden1"
                  >函数名:{{ node.data?.functionName }}</span
                >
              </div>
            </div>
            <div v-if="node.data?.startRow" class="rowRolumn-style">
              <span clsss="content word-hidden"
                >行号:{{ node.data?.startRow }} 列号:{{
                  node.data?.startColumn
                }}-{{ node.data?.endColumn }}</span
              ><span
                ><span
                  >【{{ node.data.mode === "read" ? "读取" : "写入" }}】</span
                ></span
              >
            </div>
          </div>
        </template>
      </RelationGraph>
    </div>
  </div>
</template>

<script>
import RelationGraph from "relation-graph";
import { set } from "vue";
export default {
  name: "Graph",
  components: {
    RelationGraph,
  },
  props: {
    item: Object,
  },
  data() {
    return {
      graphOptions: {
        backgroundImageNoRepeat: true,
        moveToCenterWhenRefresh: false,
        zoomToFitWhenRefresh: false,
        defaultNodeBorderWidth: 0,
        defaultNodeShape: 1,
        layouts: [
          {
            label: "中心",
            layoutName: "tree",
            from: "left",
          },
        ],
      },
    };
  },
  mounted() {
    this.$refs.graphRef.setJsonData(this.item, (graphInstance) => {});
  },
};
</script>
<style scoped>
.node-container {
  width: 240px;
  min-height: 40px;
  border-radius: 4px;
  background-color: #484750;
}

.type-style {
  height: 120px;
}

.type-title {
  color: #4880ff;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  padding: 8px 12px;
  text-align: left;
  border-radius: 4px;
  border-bottom: 1px solid #383b3e;
}

.type-content {
  text-align: left;
  line-height: 22px;
  padding: 3px 6px;
}

.file-style {
  height: 120px;
  border-radius: 4px;
}

.file-title {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  background-color: #387dff;
  padding: 8px 12px;
  text-align: left;
  border-radius: 4px;
}

.file-path {
  text-align: left;
  line-height: 22px;
  padding: 3px 6px;
}

.function-style {
  height: 40px;
  line-height: 40px;
  text-align: left;
  background: #aabee3;
  border-radius: 4px;
  padding: 0 4px;
  color: #000;
}

.rowRolumn-style {
  height: 40px;
  line-height: 40px;
  text-align: left;
  padding: 0 4px;
  border-radius: 2px;
}

.content {
  padding: 4px 2px 2px;
  width: 240px;
  align-items: left;
}

.word-hidden {
  word-break: break-all;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  overflow: hidden;
}

.word-hidden1 {
  word-break: break-all;
  text-overflow: ellipsis;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 1;
  overflow: hidden;
}
</style>
<style>
.graph-wrapper {
  height: calc(100vh - 160px);
  width: 1200px;
}

.c-current-zoom {
  color: #999999 !important;
}

.relation-graph .rel-map {
  background-color: #191c1f !important;
}

.relation-graph .rel-toolbar .c-mb-button:hover {
  background-color: rgba(153, 153, 153, 0.5) !important;
}

.relation-graph .rel-node-checked {
  width: 100% !important;
  box-shadow: 0 0px 5px 2px #191c1f !important;
}
</style>

relationMap/list/index.vue

该页面使用了虚拟滚动RecycleScroller,需要安装一下,参考文档: 虚拟滚动

安装: npm i vue-virtual-scroller

main.ts: 

  1. // vue virtual scroller

  2. import "vue-virtual-scroller/dist/vue-virtual-scroller.css" // 引入它的 css

  3. import VueVirtualScroller from "vue-virtual-scroller" // 引入它

  4. Vue.use(VueVirtualScroller) //

<template>
  <RecycleScroller
    class="scroller"
    :items="list"
    :item-size="36"
    key-field="id"
    v-slot="{ item }"
  >
    <div class="user" @click="selectGraph(item)">
      <span style="margin-left: 16px"> {{ item.globalVariable.name }}</span>
    </div>
  </RecycleScroller>
</template>

<script>
export default {
  props: {
    list: Array,
  },
  methods: {
    selectGraph(item) {
      this.$emit("select-Graph", item);
    },
  },
};
</script>

<style scoped>
.scroller {
  height: 800px;
  width: 240px;
}

.user {
  height: 36px;
  position: relative;
  display: flex;
  align-items: center;
  color: #fff;
  overflow: hidden;
  text-overflow: ellipsis;
  font-family: "Source Han Sans CN";
  font-size: 14px;
  font-style: normal;
  font-weight: 700;
  line-height: 20px; /* 142.857% */
}
.user:hover {
  background: #222529;
}
/* 在:hover状态下添加before伪类的样式 */
.user:hover::before {
  content: ""; /* 必须有content属性才能显示 */
  display: block;
  width: 3px;
  height: 36px;
  position: absolute;
  left: 0;
  top: 0;
  background-color: #4880ff;
}
</style>

mock.json 数据量太大了,这个页面放不下了,看这里:mock.json

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【小白的大模型之路】基础篇:Transformer细节

基础篇&#xff1a;Transformer 引言模型基础架构原论文架构图EmbeddingPostional EncodingMulti-Head AttentionLayerNormEncoderDecoder其他 引言 此文作者本身对transformer有一些基础的了解,此处主要用于记录一些关于transformer模型的细节部分用于进一步理解其具体的实现机…

《Mybatis》系列文章目录

什么是 MyBatis&#xff1f; MyBatis 是一款优秀的持久层框架&#xff0c;它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO&#xff…

Kubernetes学习-集群搭建篇(一) 搭建Master结点

&#x1f3f7;️个人主页&#xff1a;牵着猫散步的鼠鼠 &#x1f3f7;️系列专栏&#xff1a;Kubernetes渐进式学习-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 目录 1. 前言 2. 集群搭建方式 3. 环境说明 4. 利用kubeadm初始化Ma…

树莓派|采集视频并实时显示画面

1、使用SSH远程连接到树莓派 2、新建存放代码的目录 mkdir /home/pi/my_code_directory 3、进入存放代码的目录 cd /home/pi/my_code_directory 4、新建py文件 nano cv2test.py 5、输入代码 import cv2# 打开摄像头 cap cv2.VideoCapture(0)while True:# 读取视频帧ret…

最短路径[floyd算法]-----视频讲解+代码实现

求最短路径&#xff0c;一般有三种方法&#xff1a; 单源最短路径--Dijkstra算法 此算法只能求不带负权值的有向无环图 单源最短路径--Bellman-Ford算法&#xff08;少考&#xff09; 此算法优点在于&#xff1a;可以求带权值的有向无环图 但只是缺点明显&#xff0c;时间复杂度…

nacos在没有指定数据源的情况下默认使用什么数据库?

在没有特别指定数据源的情况下&#xff0c;Nacos 默认使用内嵌的数据库 Derby 来存储其数据。Derby 是一个轻量级的、基于 Java 的数据库管理系统&#xff0c;适合于开发和测试环境&#xff0c;因为它简单易部署且无需额外的数据库服务器。然而&#xff0c;对于生产环境&#x…

服务攻防——数据库安全

第一步: 端口扫描&#xff1a;nmap 扫不到端口&#xff1a;端口被修改&#xff0c;防护软件&#xff0c;放在内网环境 mysql 内置端口3306 第一种官方漏洞 第一步:先扫描有什么端口开发 用这个错误密码一直访问&#xff0c;最终就进去了 弱口令猜解 不可以直接猜解&#x…

C++(week2):C语言中高级

文章目录 (八) 指针0.概念1.指针基础(1)指针的声明(2)指针的两个基本操作①取地址运算符 &②解引用运算符 * (3)野指针①野指针②空指针③指针变量的赋值 vs 指针变量指向对象的赋值 (4)指针的应用①指针作为参数进行传递②指针作为返回值③拓展&#xff1a;栈帧 (5)常量指…

使用java远程提交flink任务到yarn集群

使用java远程提交flink任务到yarn集群 背景 由于业务需要&#xff0c;使用命令行的方式提交flink任务比较麻烦&#xff0c;要么将后端任务部署到大数据集群&#xff0c;要么弄一个提交机&#xff0c;感觉都不是很离线。经过一些调研&#xff0c;发现可以实现远程的任务发布。…

为什么3d重制变换模型会变形?---模大狮模型网

3D建模和渲染过程中&#xff0c;设计师经常会遇到一个让人头疼的问题&#xff0c;那就是模型在进行重制变换后出现的意外变形。这种变形不仅影响了模型的外观和质量&#xff0c;也给设计工作带来了额外的麻烦。本文将深入探讨3D模型进行重制变换后出现变形的原因&#xff0c;帮…

Hystrix服务熔断

服务熔断 熔断机制是应对雪崩效应的一种微服务链路保护机制。当某个微服务不可用或者响应时间太长时&#xff0c; 会进行服务降级&#xff0c;进而熔断该节点微服务的调用&#xff0c;快速返回“错误”的响应信息。当检测到该节点微 服务调用响应正常后恢复调用链路。 在Spri…

【Java】HOT100+代码随想录 动态规划(上)背包问题

目录 理论基础 一、基础题目 LeetCode509&#xff1a;斐波那契数 LeetCode70&#xff1a;爬楼梯 LeetCode746&#xff1a;使用最小花费爬楼梯 LeetCode62&#xff1a;不同路径 LeetCode63&#xff1a;不同路径ii LeetCode343&#xff1a;整数拆分 LeetCode96&#xff1a;不…

海外动态IP:揭秘其背后的技术与应用

在数字化时代&#xff0c;网络技术的发展日新月异&#xff0c;其中海外动态IP作为网络通信技术的重要一环&#xff0c;逐渐走进公众视野。海外动态IP不仅为跨国企业提供了灵活的网络接入方案&#xff0c;还为个人用户带来了更多样化的网络体验。本文将深入探讨海外动态IP的技术…

【Docker学习】重启容器的docker restart

命令&#xff1a; docker container restart 描述&#xff1a; 重启一个或多个容器 用法&#xff1a; docker container restart [OPTIONS] CONTAINER [CONTAINER...] 别名&#xff1a; docker restart(docker的一些命令可以简写&#xff0c;docker restart就等同于docker cont…

树莓派|连接CSI接口摄像头+opencv

CSI&#xff08;Camera Serial Interface&#xff09;接口摄像头是一种常见的嵌入式系统或移动设备中使用的摄像头接口。它通常用于与处理器或图像传感器进行直接连接&#xff0c;实现高速的图像数据传输。 CSI接口摄像头具有以下特点&#xff1a; 高速传输&#xff1a;CSI接口…

免翻,剪映出品的AI作图和AI视频官网免费体验!

哈喽&#xff0c;各位小伙伴们好&#xff0c;我是给大家带来各类黑科技与前沿资讯的小武。 近日&#xff0c;据剪映 Dreamina 官方消息&#xff0c;Deramina正式更名为即梦&#xff0c;同时宣布其AI作图和AI视频生成功能已全量上线。 ▲ 官网主页面 AI作图 1、通过文字描述或…

华中科大:感谢大家,我的春招之旅结束了

今天在论坛上看到一个帖子&#xff0c;一位华中科大的同学&#xff0c;因为家中父亲突然病倒&#xff0c;发求助帖&#xff1a; 请问大家&#xff0c;春招走哪个方向能最快找到工作&#xff1f;还是说继续读研呢&#xff0c;但是家里急需钱…… 当时这个帖子直接热榜第一&…

Python练习04

目录 制作一个简易的注册登陆系统 实现过程 声明需要用到的库 构造一个判断用户文件是否存在的函数 构造一个存储用户文件的函数 制作UI 制作系统主体 运行效果 制作一个简易的注册登陆系统 通过所学知识制作一个简易的注册登陆系统&#xff0c;要求可以存储账户及密码&#…

省级生活垃圾无害化处理率面板数据(2004-2022年)

01、数据简介 生活垃圾无害化处理率是指经过处理的生活垃圾中&#xff0c;达到无害化标准的垃圾所占的比例。这一指标是衡量城市垃圾处理水平的重要标准&#xff0c;反映了城市对垃圾进行有效管理和处理的能力。 生活垃圾无害化处理的主要方式包括生活垃圾焚烧、生活垃圾卫生…

2024生日快乐祝福HTNL源码修复版

源码介绍 2024生日快乐祝福HTNL源码&#xff0c;源码由HTMLCSSJS组成&#xff0c;记事本打开源码文件可以进行内容文字之类的修改&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器里面&#xff0c; 源码截图 源码下载 2024生日快乐祝福HTNL源码