vue + g6 实现树级结构(compactBox 紧凑树)

news2024/12/31 7:15:22

G6文档
在这里插入图片描述

自定义节点

G6.registerNode(
  "dom-node",
  {
    draw: (cfg, group) => {
      let str = `
        <div class='item-box catalog-node ${
          cfg.isSelected ? "is-selected" : ""
        } ${cfg.status}-box' οnclick='handleDetail("${cfg.id}")' id="${
        cfg.id
      }" style="width: ${cfg.size[0] - 5}px;">
          ${
            cfg.status
              ? `<span class='status ${cfg.status}'>${getLabel(
                  ISSUE_STATUS,
                  cfg.status
                )}</span>`
              : ""
          }
          ${
            cfg?.manager?.name
              ? `<p class=''><span class="title-txt avatar-img" title='负责人'>
            <img 
              src="${cfg?.manager?.avatar}"
            />
            </span>${cfg.manager.name}
            </p>`
              : ""
          }
          <div class='title' οnclick='handleDetail("${cfg.id}")'><span ${
        cfg.typeName === "Bug" ? `class='tipText'` : ""
      }>${cfg.title}</span>
          </div>
        </div>
        `;

      return group.addShape("dom", {
        attrs: {
          width: cfg.size[0],
          height: nodeHeight(cfg),
          // 传入 DOM 的 html
          html: str,
        },
        draggable: true,
      });
    },
  },
  "single-node"
);

在pc端,自定义的节点,绑定的点击事件起作用,但是移动端模式,不会起作用;

解决方法:
文档中也说明了,节点的选中事件,需要将Mode切换到edit模式。graph.setMode("edit");
在这里插入图片描述
如:

graph.setMode("edit");
graph.on("nodeselectchange", (e) => {
  // 当前操作的 item
  alert("node");
  console.log(e.target);
  // 当前操作后,所有被选中的 items 集合
  console.log(e.selectedItems);
  // 当前操作时选中(true)还是取消选中(false)
  console.log(e.select);
});

此处,直接用自定节点的事件了。

全部代码

<template>
  <div id="container"></div>
</template>

<script>
// 引入antv-G6
import G6 from "@antv/g6";
import { ISSUE_STATUS } from "@/utils/constant";
import { getLabel } from "@/utils";

// G6的配置项
G6.registerNode(
  "icon-node",
  {
    options: {
      size: [60, 20], // 宽高
      stroke: "#91d5ff", // 变颜色
      fill: "#fff", // 填充色
    },
    // draw是绘制后的附加操作-节点的配置项  图形分组,节点中图形对象的容器
    draw(cfg, group) {
      // 获取节点的配置
      const styles = this.getShapeStyle(cfg);
      // 解构赋值
      const { labelCfg = {} } = cfg;

      const w = styles.width;
      const h = styles.height;
      // 向分组中添加新的图形 图形 配置 rect矩形 xy 代表左上角坐标 w h是宽高
      const keyShape = group.addShape("rect", {
        attrs: {
          ...styles,
          x: -w / 2,
          y: -h / 2,
        },
      });

      // 文本文字的配置
      if (cfg.title) {
        group.addShape("text", {
          attrs: {
            ...labelCfg.style,
            text: cfg.title,
            x: 50 - w / 2,
            y: 25 - h / 2,
          },
        });
      }

      return keyShape;
    },
    // 更新节点后的操作,一般同 afterDraw 配合使用
    update: undefined,
  },
  "rect"
);

const nodeHeight = (obj) => {
  // if (obj.depth == 0) {
  //   return 100;
  // }
  const l = ["manager", "title"];

  const arr = l.filter((item) => {
    return obj[item];
  });
  return arr.length * 25 + 50;
};
G6.registerNode(
  "dom-node",
  {
    draw: (cfg, group) => {
      let str = `
        <div class='item-box catalog-node ${
          cfg.isSelected ? "is-selected" : ""
        } ${cfg.status}-box' οnclick='handleDetail("${cfg.id}")' id="${
        cfg.id
      }" style="width: ${cfg.size[0] - 5}px;">
          ${
            cfg.status
              ? `<span class='status ${cfg.status}'>${getLabel( ISSUE_STATUS, cfg.status )}</span>`
              : ""
          }
          ${
            cfg?.manager?.name
              ? `<p class=''><span class="title-txt avatar-img" title='负责人'> <img src="${cfg?.manager?.avatar}" /> </span>${cfg.manager.name} </p>`
              : ""
          }
          <div class='title' οnclick='handleDetail("${cfg.id}")'><span ${
        cfg.typeName === "Bug" ? `class='tipText'` : ""
      }>${cfg.title}</span>
          </div>
        </div>
        `;

      return group.addShape("dom", {
        attrs: {
          width: cfg.size[0],
          height: nodeHeight(cfg),
          // 传入 DOM 的 html
          html: str,
        },
        draggable: true,
      });
    },
  },
  "single-node"
);

// 绘制层级之间的连接线
G6.registerEdge("flow-line", {
  // 绘制后的附加操作
  draw(cfg, group) {
    // 边两端与起始节点和结束节点的交点;
    const startPoint = cfg.startPoint;
    const endPoint = cfg.endPoint;
    // 边的配置
    const { style } = cfg;
    const shape = group.addShape("path", {
      attrs: {
        stroke: style.stroke, // 边框的样式
        endArrow: style.endArrow, // 结束箭头
        // 路径
        path: [
          ["M", startPoint.x, startPoint.y],
          ["L", startPoint.x, (startPoint.y + endPoint.y) / 2],
          ["L", endPoint.x, (startPoint.y + endPoint.y) / 2],
          ["L", endPoint.x, endPoint.y],
        ],
      },
    });

    return shape;
  },
});

// 默认连接边线的颜色 末尾箭头
const defaultEdgeStyle = {
  stroke: "#ccc",
};

// 默认布局
// compactBox 紧凑树布局
// 从根节点开始,同一深度的节点在同一层,并且布局时会将节点大小考虑进去。
const defaultLayout = {
  type: "compactBox", // 布局类型树
  direction: "TB", // TB 根节点在上,往下布局
  getId: function getId(d) {
    // 节点 id 的回调函数
    return d.id;
  },
  getHeight: function getHeight() {
    // 节点高度的回调函数
    return 16;
  },
  getWidth: function getWidth() {
    // 节点宽度的回调函数
    return 16;
  },
  getVGap: function getVGap(d) {
    // 节点纵向间距的回调函数
    if (d.parId === "0") return 70;
    return 80;
  },
  getHGap: function getHGap(d) {
    // 节点横向间距的回调函数
    if (d.parId === "0") return 100;
    return 150;
  },
};
let graph;

export default {
  name: "Home",
  props: {
    treeListData: {
      type: Array,
      default: () => [],
    },
    options: {
      type: Object,
      default: () => {
        return {};
      },
    },
  },
  emits: ["handleSelected"],
  data() {
    return {
      listData: [],
      selectedId: "", // 选中的节点Id
      initOptions: {
        isFitView: true, // 是否默认适应全局
        isFitCenter: true, // 是否居中
        isHiddenRoot: true, // 是否显示根元素
      },
    };
  },
  methods: {
    G6init() {
      if (typeof window !== "undefined") {
        window.onresize = () => {
          if (!graph || graph.get("destroyed")) return;
          if (!container || !container.scrollWidth || !container.scrollHeight)
            return;
          graph.changeSize(container.scrollWidth, container.scrollHeight);
        };
      }
      // 获取容器
      const container = document.getElementById("container");
      // 获取容器的宽高
      const width = container.scrollWidth;
      const height = container.scrollHeight - 30 || 500;

      // Graph 是 G6 图表的载体-实例化
      graph = new G6.TreeGraph({
        container: "container", // 图的 DOM 容器
        width,
        height,
        linkCenter: true, // 指定边是否连入节点的中心
        modes: {
          // default 模式中包含点击选中节点行为和拖拽画布行为;
          default: [
            {
              // 这个是可以展开可以收起
              type: "collapse-expand",
              onChange: function onChange(item, collapsed) {
                const data = item.get("model");
                data.collapsed = collapsed;
                return true;
              },
            },
            "drag-canvas",
            "zoom-canvas",
            "click-select",
          ],
          edit: ["click-select"],
        },
        // 默认状态下节点的配置
        defaultNode: {
          type: "dom-node", // 'icon-node',
          size: [250, 60],
        },
        // 默认状态下边线的配置,
        defaultEdge: {
          type: "flow-line",
          style: defaultEdgeStyle,
        },
        // 布局配置项
        layout: defaultLayout,
        renderer: "svg",
      });
      graph.data([...this.listData][0]);

      graph.render();
      // 让画布内容适应视口。
      if (this.initOptions.isFitView) {
        graph.fitView();
      }
      if (this.initOptions.isFitCenter) {
        graph.fitCenter();
      }
      if (!this.initOptions.isHiddenRoot) {
        // 是否要移除根节点
        const item = graph.findById([...this.listData][0].id);
        graph.removeItem(item);
      }
      // 改变视口的缩放比例,在当前画布比例下缩放,是相对比例。
      graph.zoom(1);
      graph.setMode("edit");
    },

    async init() {
      let _this = this;
      if (graph) {
        // 如果原来有画布,需要先清除
        graph.destroy();
      }
      this.initOptions = Object.assign(this.initOptions, this.options);
      this.listData = [...this.treeListData];
      function setSelectFalse(obj) {
        obj.forEach((element) => {
          element.isSelected = false;
          if (element.children) {
            setSelectFalse(element.children);
          }
        });
      }
      window.nodeClick = function (id) {
        const item = graph.findById(id);
        setSelectFalse(_this.listData);
        item._cfg.model.isSelected = true;
        graph.changeData(_this.listData[0]);
        graph.refresh();
        graph.fitCenter();
      };

      window.handleDetail = (id) => {
        const item = graph.findById(id);
        if (item?._cfg?.parent) {
          _this.$emit("handleSelected", id);
        }
      };
      this.G6init();
    },
  },
  beforeDestroy() {
    console.log("推出");
  },
};
</script>
<style lang="scss" scoped>
@import "@/assets/styles/common.scss";
#container {
  height: 100%;
  width: 100%;
  border: 1px solid #efefef;
  ::v-deep .title {
    font-size: 15px;
    display: block;
    // text-align: center;
    position: relative;
    margin: 10px 0;
    padding-left: 15px;
    color: #1199ff;
    cursor: pointer;
  }
  ::v-deep .item-box {
    background-color: #fff;
    border-radius: 5px;
    padding: 5px;
    // height: 100%;
    border: 1px solid;
    position: relative;
    p {
      margin-bottom: 2px;
      display: flex;
      align-items: center;
      color: #333;
    }
    &.is-selected {
      border: 1px solid #1199ff;
    }
    .tipText {
      color: red;
    }
    .logs {
      height: 70px;
      overflow: hidden;
      display: -webkit-box;
      -webkit-box-orient: vertical;
      -webkit-line-clamp: 3;
    }
    .title-txt {
      display: inline-block;
      width: 80px;
      color: rgb(169, 169, 169);
    }
    .avatar-img {
      width: 40px;
      height: 40px;
      margin-right: 15px;
      img {
        width: 100%;
        height: 100%;
        border-radius: 100%;
      }
    }
    .status {
      position: absolute;
      right: 15px;
      top: 15px;
      border: 1px solid;
      padding: 0 5px;
      font-size: 12px;
      border-radius: 4px;
    }
  }
}
::v-deep g g g:not(:first-child) foreignObject {
  font-size: 14px;
}
foreignObject {
  overflow: initial !important;
}
</style>

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JMeter压测如何分配业务比例?

在进行综合场景压测时&#xff0c;由于不同的请求&#xff0c;要求所占比例不同&#xff0c;那如何实现呢&#xff1f; 有人说将这些请求分别放到单独的线程组下&#xff0c;然后将线程组的线程数按照比例进行配置&#xff0c;这种方法不是很好&#xff0c;想想&#xff0c;不…

5G是如何提升通行能力的?5G毫米波到底有多快?

高速公路&#xff0c;可以通过多层交通、多条车道、车道方向、车辆容量、货物包装、驾驶司机等多个因素&#xff0c;提升通行能力。 我们把5G比作高速公路&#xff0c;那么&#xff0c;5G是如何提升自身通行能力的呢&#xff1f;5G毫米波&#xff0c;到底能有多快呢&#xff1f…

跨越时空的教育:在线培训系统的全球化

随着全球化的发展&#xff0c;跨越时空的教育已经成为现实。在线培训系统可以打破地域限制&#xff0c;让学生能够接受来自世界各地的教育资源。这种新型教育模式具有巨大的潜力和优势。 在线培训系统是指通过互联网提供的远程教育服务。它可以通过网络平台、视频教育、虚拟课…

如何优雅地使用Low Code提高开发效率

2023年&#xff0c;低代码热度有&#xff0c;但是在企业内部核心场景的落地比例不高&#xff0c;推进进展也没有想象中快。就算是这样&#xff0c;低代码赛道也在“暗流涌动”。 数字化趋势下&#xff0c;很多企业想要以数字化的手段进行降本增效。很多企业希望以低代码的模式…

【Turfjs的java版本JTS】前面讲了Turfjs可以实现几何计算,空间计算的功能,如果后端要做这项功能也有类似的类库,JTS

JTS Java Topology Suite 几何计算&#xff1a; 1. 前端js就用这个 Turfjs的类库。参考网站&#xff1a; 计算两线段相交点 | Turf.js中文网 2. 后端java语言就可以用 JTS这个类库&#xff0c;参考网站&#xff1a; JTS参考网站&#xff1a; 1. https://github.com/locatio…

【机器学习】神经网络代价函数和反向传播算法

神经网络的代价函数 接下来我会再规定若干符号代表的含义&#xff1a; L L L表示神经网络的总层数 s i s_i si​表示的是第i层的神经元数量 如果神经网络处理的是一个二元分类问题&#xff0c;那么他的第L层就只会有一个节点&#xff1b;如果处理的是一个多元分类问题&…

不知不觉创作一年了,谈谈我的创作经历

前言 大家好&#xff0c;我是小刘在C站&#xff0c;不知不觉创作1年啦&#xff0c;本次文章呢分享一下我这一路走来的经历吧 目录 前言 1.为什么写博客 2.第一篇文章 3.怎么坚持创作的&#xff1f; 4.自我介绍 5.收获 6.认识了哪些大佬呢&#xff1f; 7.未来规划 8.分…

【C++】STL的string容器介绍

目录 1、string容器 1.1声明一个c字符串 1.2string和c字符数组的比较 1.3string类操作函数介绍 1.3.1赋值操作 1.3.2字符串拼接 1.3.3字符串查找 1.3.4字符串替换 1.3.5字符串比较 1.3.6字符存取 1.3.7字符串插入 1.3.8字符串删除 1.3.9子串获取 1、string容器 在…

测试4年外包已上岸 , 我只能说这类公司能不去尽量别去···

我大学学的是计算机专业&#xff0c;毕业的时候&#xff0c;对于找工作比较迷茫&#xff0c;也不知道当时怎么想的&#xff0c;一头就扎进了一家外包公司&#xff0c;一干就是4年。现在终于跳槽到了互联网公司了&#xff0c;我想说的是&#xff0c;但凡有点机会&#xff0c;千万…

从零开始Vue项目中使用MapboxGL开发三维地图教程(五)实现框选要素功能、可拖动点展示坐标以及地图上实时更新要素

文章目录 1、实现框选要素功能1.1、添加点数据的图层&#xff1a;1.2、增加绘图插件&#xff08;mapbox-draw&#xff09;1.3、实现框选并让选择的目标数据高亮 2、实现地图上可拖动点2.1、实现功能&#xff1a;2.2、实现思路&#xff1a;2.3、代码示例&#xff1a; 3、实时更新…

已安装过PageOfiice,谷歌浏览器反复提示PageOffice安装

原因&#xff1a;Chrome开发团队以网络安全为由&#xff0c;强推ssl证书&#xff0c;希望所有部署在公网的网站&#xff0c;全部改用https访问&#xff0c;所以最新的谷歌和edge升级到94版本后对公网上的http请求下的非同域的http请求进行了拦截&#xff0c;于是就出现了目前遇…

火灾发生时如何实时地选择逃生路线

安科瑞虞佳豪 南京大学无菌动物房改造项目&#xff0c;位于位于南京江北新区学府路 12 号。改造面积约为 1100m2&#xff0c;均在原有建筑底层。其中&#xff0c;动物房区域含饲养室 6 间&#xff0c;层高 4.9m。功能实验区域含实验室 4间、手术室 1 间、暂养室 2 间、内外准备…

Linux进程信号 | 信号产生

前面的文章中我们讲述了进程间通信的部分内容&#xff0c;在本文中我们继续来学习进程信号相关的知识点。 信号入门 生活角度的信号 在我们的日常生活中&#xff0c;就有着各种各样的信号&#xff0c;它会给我们传递各种各样的信息&#xff0c;可以知道的是一个信号例如&…

webpack提升开发体验SourceMap

一、开发场景介绍 开发中我们不可避免的会写一些bug出来&#xff0c;这时候要调试&#xff0c;快速定位到bug到底出现在哪尤为关键。 例如我故意在sum函数中写一个错误代码如下&#xff1a; 这时我们用前面章节已经写好的开发模式的webpack.dev.js运行&#xff0c;控制台会出…

【Spring】— MyBatis与Spring的整合

目录 1.整合环境1.1准备所需的JAR包1&#xff0e;所需Spring框架的JAR包2&#xff0e;所需MyBatis框架的JAR包3&#xff0e;MyBatis与Spring整合所需的中间JAR包4&#xff0e;数据库驱动JAR包5&#xff0e;数据源所需JAR包 1.2 编写配置文件 2.整合2.1 传统DAO方式的开发整合1&…

龙蜥社区第 17 次运营委员会会议顺利召开

5 月 26 日&#xff0c;龙蜥社区走进 Arm 北京办公室召开了第 17 次运营委员会会议。本次会议由龙蜥社区运营委员会副主席金美琴主持。来自 Arm、阿里云、电信、红旗软件、飞腾、海光、Intel、浪潮信息、联通软研院、龙芯、凝思软件、麒麟软件、普华基础软件、申泰、统信软件、…

Vue-Element-Admin项目学习笔记(7)用Node.js写一个简单后端接口

前情回顾&#xff1a; vue-element-admin项目学习笔记&#xff08;1&#xff09;安装、配置、启动项目 vue-element-admin项目学习笔记&#xff08;2&#xff09;main.js 文件分析 vue-element-admin项目学习笔记&#xff08;3&#xff09;路由分析一:静态路由 vue-element-adm…

Opencv-C++笔记 (3) : opencv的库介绍以及和C++对接转换

文章目录 一、Opencv库的介绍calib3dcontribcoreimgprocfeatures2dflannhighguilegacymlnonfreeobjdetectoclphotostitchingsuperrestsvideoVideostab 二、C和MAT 转换方式2.1、一维Vector2.2、二维vector2.3 数组2.4、类型转换 ——一维转 数组2.5、类型转换 -------- 一维MAT…

抖音同城榜相关介绍来啦,这篇文不若与众带你去详细了解它

一、抖音同城榜是什么&#xff1f; 抖音同城榜是抖音平台上的一个功能&#xff0c;它可以根据用户所在的地理位置&#xff0c;推荐附近热门的视频和达人。用户可以通过同城榜来了解当地的热门话题、美食、景点等信息&#xff0c;也可以通过同城榜认识志同道合的朋友和达人&…

DTD怎样进行元素类型定义?【语法格式】

Bootstrap常用组件包括按钮、导航、菜单和表单等。使用Bootstrap不需要编写复杂的样式代码&#xff0c;只需要使用Bootstrap组件就可以实现复杂的页面架构。下面将对Boolstrap按钮组件进行详细讲解。 Bootstrap提供了多种样式的按钮&#xff0c;每个样式的按钮都有自己的语义用…