g6-思维链路图

前言

需求:集团公司风险链路图
首次接触@antv/g6,进行记录总结
点击框显示公司数据

在这里插入图片描述

实现步骤

安装"@antv/g6": "4.5.0",
 "insert-css": "^2.0.0",
安装
npm install --save @antv/g6

学习方式:
官网
https://antv.vision/zh/
蚂蚁金服 G6 文档
https://www.bookstack.cn/books/antv-g6
  • 页面基础的架构
// dom容器
<template>
   <div id="mindMapContainer" />
</template>
<style scoped lang='scss'>
#mindMapContainer {
 width: 100%;
 height: 100%;
 position: relative;
 // margin: 200px;
 ::v-deep canvas{
   position: absolute;
   left: 0;
   top:0;
   right:0;
   top: 0;
 }
}
.tooltipDirlog{
 padding: 0;
}
::v-deep .g6-component-contextmenu{
 z-index: 2022;
}
</style>
<script lang='ts' setup>
import { ElMessage } from 'element-plus';
import {
 ref, onMounted, watch, defineEmits,
} from 'vue'
import insertCss from 'insert-css';
import G6, {
 IG6GraphEvent, IGroup, INode, Item, ModelConfig, ShapeOptions, TreeGraph,
} from '@antv/g6';
onMounted(() => {
 insertCss(`
 .g6-component-tooltip {
   background-color: rgba(0,0,0, 0.65);
   padding: 10px;
   box-shadow: rgb(174, 174, 174) 0px 0px 10px;
   width: fit-content;
   color: #fff;
   border-radius = 4px;
 }
`);
})

// 定义树图的对象用于销毁重绘
const devicetopologyChart = ref<any>()
const destroy = () => {
 if (devicetopologyChart.value) {
   devicetopologyChart.value.clear();
   devicetopologyChart.value.destroy();
 }
};
defineExpose({
 initContainer,
 destroy,
});

在这里插入图片描述

  • initContainer初始化函数用于进入页面绘图调用
const initContainer = async (Data: any) => {
// 1、Data就是我们上面接口返回的数据
  const colors = {
    B: '#5B8FF9',
    R: '#F46649',
    L: '#F46649',
    Y: '#EEBC20',
    G: '#5BD8A6',
    DI: '#A7A7A7',
  };
  //  组件props
  const props = {
    data: Data,
    config: {
      padding: [20, 50],
      defaultLevel: 3,
      defaultZoom: 0.8,
      modes: { default: ['zoom-canvas', 'drag-canvas'] },
    },
  };

  const container = document.getElementById('mindMapContainer') as HTMLElement;
  const width = container.scrollWidth;
  const height = container.scrollHeight || 500;

	  const registerFn = () => {
	  // 2、自定义绘制边节点标题 见代码块2
	  // 3、自定义线段 见代码块3
	  };
	  registerFn();
	    const { data } = props;
  		let graph:TreeGraph; // 定义树图对象
	  const initGraph = (data: any) => {
		// 4、渲染 见代码块4
	}
	  initGraph(data);

	  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);
	    };
	  }
}
  • antv/g6自定义节点(代码块2)
使用g6的G6.registerNode('节点名称','节点配置')
绘制位置如下图所示
根据cfg中的label进行区别绘制,绘制矩形和文字以及进行颜色填充
设置name便于监听点击事件

在这里插入图片描述

// 代码块2
// 自定义节点、边
  const registerFn = () => {
  /**
 - 自定义节点
   */
    G6.registerNode(
      'flow-rect',
      {
        draw(cfg: any, group:IGroup) {
        // cfg每一个数据对象 group g6绘制对象
          const {
            title = '',
            collapsed,
            label,
            meta,
          } = cfg;
          // 设置基础宽高 颜色 线粗
          const rectConfig = {
            width: label === 'Downstream' || label === 'Upstream' ? 124 : 164,
            height: label === 'Downstream' || label === 'Upstream' ? 82 : 32,
            lineWidth: 1,
            fontSize: 14,
            fill: '#fff',
            radius: 4,
            stroke: '#26A6F5',
            opacity: 1,
          };
		// 基础坐标
          const nodeOrigin = {
            x: -rectConfig.width / 2,
            y: -rectConfig.height / 2,
          };

          let rect;
		// 绘制集团
          if (label === 'Enterprise') {
            rect = group.addShape('rect', {
            // 绘制矩形
              attrs: {
                x: nodeOrigin.x,
                y: nodeOrigin.y,
                fill: '#2878FF',
                fontSize: 14,
                lineWidth: 1,
                width: 164,
                height: 32,
                radius: 4,
              },
            });
            const base = group.addShape('text', {
            // 绘制文字
              attrs: {
                x: 5,
                y: 22 + nodeOrigin.y,// 进行位置移动
                text: (title as string).length > 12 ? `${(title as string).substring(0, 12)}...` : title,
                textAlign: 'center',
                fill: '#fff',
                fontSize: 14,
              },
            });
          } else if (label === 'Downstream') {
          // label 下游绘制下游
            rect = group.addShape('rect', {
              attrs: {
                x: nodeOrigin.x,
                y: nodeOrigin.y,
                width: 124,
                height: 82,
                lineWidth: 1,
                fontSize: 14,
                fill: '#fff',
                radius: 4,
                stroke: '#26A6F5',
                opacity: 1,
              },
            });
            group.addShape('rect', {
              attrs: {
                x: nodeOrigin.x,
                y: nodeOrigin.y,
                width: 124,
                height: 28,
                fill: '#26a6f51a',
                radius: [4, 4, 0, 0],
                opacity: 1,
              },
            });
            group.addShape('text', {
              attrs: {
                textAlign: 'left',
                textBaseline: 'bottom',
                x: 12 + nodeOrigin.x,
                y: 22 + nodeOrigin.y,
                text: (title as string).length > 12 ? `${(title as string).substring(0, 12)}...` : title,
                fontSize: 12,
                // opacity: 0.85,
                fill: '#26A6F5',
                cursor: 'pointer',
                opacity: 1,
              },
            });
            // left icon
            group.addShape('image', {
              attrs: {
                x: 12 + nodeOrigin.x,
                y: 39 + nodeOrigin.y,
                height: 10,
                width: 10,
                cursor: 'pointer',
                opacity: 1,
                img: 'https://huoxian-asr.oss-cn-beijing.aliyuncs.com/app/zichan.png',
              },
              name: 'node-icon',
            });
            group.addShape('text', {
              attrs: {
                textAlign: 'left',
                textBaseline: 'bottom',
                x: 28 + nodeOrigin.x,
                y: 50 + nodeOrigin.y,
                text: `资产:${mockData.value.children[0].meta.assets}`,
                fontSize: 12,
                // opacity: 0.85,
                opacity: 1,
                fill: '#333333',
                cursor: 'pointer',
              },
            });
            group.addShape('image', {
              attrs: {
                x: 12 + nodeOrigin.x,
                y: 59 + nodeOrigin.y,
                height: 10,
                width: 10,
                cursor: 'pointer',
                img: 'https://huoxian-asr.oss-cn-beijing.aliyuncs.com/app/fengxian.png',
              },
              name: 'node-icon',
            });
            group.addShape('text', {
              attrs: {
                textAlign: 'left',
                textBaseline: 'bottom',
                x: 28 + nodeOrigin.x,
                y: 70 + nodeOrigin.y,
                text: `风险:${mockData.value.children[0].meta.risk}`,
                fontSize: 12,
                opacity: 0.85,
                fill: '#333333',
                cursor: 'pointer',
              },
            });
          }else {
            rect = group.addShape('rect', {
              attrs: {
                x: nodeOrigin.x,
                y: nodeOrigin.y,
                ...rectConfig,
              },
            });
            // label title
            group.addShape('text', {
              attrs: {
                textAlign: 'left',
                textBaseline: 'bottom',
                x: 12 + nodeOrigin.x,
                y: 22 + nodeOrigin.y,
                text: (title as string).length > 12 ? `${(title as string).substring(0, 12)}...` : title,
                fontSize: 12,
                opacity: 0.85,
                fill: '#000',
                cursor: 'pointer',
              },
              name: 'name-shape',
            });
            if (cfg.meta && cfg.meta.top) {
              const top = group.addShape('text', {
                attrs: {
                  x: 60,
                  y: -18,
                  fill: cfg.meta.top <= 5 ? '#ED7B2F' : '#fff',
                  text: cfg.meta.top <= 5 ? `TOP${cfg.meta.top}` : '',
                  fontSize: 12,
                  lineWidth: cfg.meta.top <= 5 ? 1 : 0,
                },
              });

              const topBox = group.addShape('rect', {
                attrs: {
                  x: 56,
                  y: -34,
                  fill: cfg.meta.top <= 5 ? '#ed7b2f1a' : '#fff',
                  fontSize: 12,
                  lineWidth: 1,
                  width: 40,
                  height: 18,
                  radius: 2,
                },
              });
            }

            if (cfg.meta && cfg.meta.status) {
              const zhuangtai = group.addShape('text', {
                attrs: {
                  x: cfg.meta.top <= 5 ? 22 : 54,
                  y: -18,
                  fill: '#00A870',
                  text: cfg.meta.status.slice(0, 2),
                  fontSize: 12,
                  lineWidth: 1,
                },
              });

              const zhuangtaiBox = group.addShape('rect', {
                attrs: {
                  x: cfg.meta.top <= 5 ? 14 : 44,
                  y: -34,
                  fill: '#00a8701a',
                  fontSize: 12,
                  lineWidth: 1,
                  width: 40,
                  height: 18,
                  radius: 2,
                },
              });
            }
          }

          // collapse 点击收起展开的样式
          if (cfg.children && (cfg.children as Array<any>).length) {
            // console.log('kuaikuai', rectConfig)
            group.addShape('rect', {
              attrs: {
                x: title === '上游' ? -rectConfig.width / 2 - 28 : rectConfig.width / 2 + 8,
                y: -7,
                width: 12,
                height: 12,
                radius: [6, 6],
                stroke: 'rgba(0, 0, 0, 0.5)',
                cursor: 'pointer',
                fill: '#fff',
              },
              name: 'collapse-back',
              // 这里的name可以用于下面进行点击事件绑定
              modelId: cfg.id,
            });

            // collpase text
            group.addShape('text', {
              attrs: {
                x: title === '上游' ? -rectConfig.width / 2 - 22 : rectConfig.width / 2 + 14,
                y: -2,
                textAlign: 'center',
                textBaseline: 'middle',
                text: collapsed ? '+' : '-',
                fontSize: 12,
                cursor: 'pointer',
                fill: 'rgba(0, 0, 0, 0.7)',
              },
              name: 'collapse-text',
              modelId: cfg.id,
            });
          }

          this.drawLinkPoints(cfg, group);
          return rect;
        },
        setState(name, value, item) {
          // console.log(name, value, item)
          if (name === 'collapse') {
            const group = (item as any).getContainer();
            // console.log(11)
            const collapseText = group.find((e: any) => e.get('name') === 'collapse-text');
            if (collapseText) {
              if (!value) {
                collapseText.attr({
                  text: '-',
                });
              } else {
                collapseText.attr({
                  text: '+',
                });
              }
            }
          }
        },
        getAnchorPoints() {
          return [
            [0, 0.5],
            [1, 0.5],
          ];
        },
      } as ShapeOptions,
      'rect',
    );
  • antv/g6自定义边(代码块3)
使用g6的G6.registerEdge('flow-edge','options') 
参数:名称和数据项
绘制线段上的颜色、箭头以及线段上的提示
绘制的位置在下图所示

在这里插入图片描述

 /**
 - 自定义边
   */
    G6.registerEdge('flow-edge', {
      afterDraw(cfg:any, group:IGroup | undefined) {
        // 获取图形组中的第一个图形,在这里就是边的路径图形
        const shape = (group as IGroup).get('children')[0];
        // 获取路径图形的中点坐标 很重要 下面我们根据这个坐标值进行操作绘制矩形绘制文字等
        const midPoint = shape.getPoint(1);
        // 在中点增加一个矩形,注意矩形的原点在其左上角
        // 获取路径上的四分位点坐标
        const quatile = shape.getPoint(0.25);
        // add a circle on the quatile of the path
        // 在四分位点上放置一个圆形
        // 公司集团两边的两个小圆圈
        // eslint-disable-next-line no-underscore-dangle
        cfg.targetNode._cfg.model.label === 'Downstream' && (group as IGroup).addShape('circle', {
          attrs: {
            r: 5,
            fill: 'rgba(38, 166, 245, 1)',
            x: quatile.x,
            y: quatile.y,
          },
        });
        // eslint-disable-next-line no-underscore-dangle
        cfg.targetNode._cfg.model.label === 'Upstream' && (group as IGroup).addShape('circle', {
          attrs: {
            r: 5,
            fill: 'rgba(116, 115, 255, 1)',
            x: quatile.x,
            y: quatile.y,
          },
        });
        // eslint-disable-next-line no-underscore-dangle
        cfg.targetNode._cfg.model.meta?.edge && cfg.targetNode._cfg.model.label !== 'chainSupply' && (group as IGroup).addShape('rect', {
          // 招标信息展示
          attrs: {
            width: 48,
            height: 20,
            // eslint-disable-next-line no-underscore-dangle
            fill: cfg.targetNode._cfg.model.meta.edge === '其他' ? 'rgba(240, 242, 245, 1)' : cfg.targetNode._cfg.model.meta.edge === '招标' && 'rgba(242, 241, 255, 1)' || 'rgba(233, 246, 254, 1)',
            // x 和 y 分别减去 width / 2 与 height / 2,使矩形中心在 线段上
            x: midPoint.x > 0 ? midPoint.x / 2 + midPoint.x / 4 + 10 : midPoint.x / 2 - midPoint.x / 4,
            y: midPoint.y - 9,
          },
        });
        // eslint-disable-next-line no-underscore-dangle
        cfg.targetNode._cfg.model.meta?.edge && cfg.targetNode._cfg.model.label !== 'chainSupply' && (group as IGroup).addShape('text', {
          attrs: {
            width: 10,
            height: 10,
            // eslint-disable-next-line no-underscore-dangle
            fill: cfg.targetNode._cfg.model.meta.edge === '其他' ? 'rgba(101, 119, 151, 1)' : cfg.targetNode._cfg.model.meta.edge === '招标' && 'rgba(116, 115, 255, 1)' || 'rgba(38, 166, 245, 1)',
            // x 和 y 分别减去 width / 2 与 height / 2,使矩形中心在 midPoint 上
            x: midPoint.x > 0 ? midPoint.x / 2 + midPoint.x / 4 + 16 : midPoint.x / 2 - midPoint.x / 4 - 10,
            y: midPoint.y + 10,
            // eslint-disable-next-line no-underscore-dangle
            text: cfg.targetNode._cfg.model.meta.edge,
          },
        });
        // eslint-disable-next-line no-underscore-dangle
        cfg.targetNode._cfg.model.label === 'chainSupply' && (group as IGroup).addShape('rect', {
          // 招标信息展示
          attrs: {
            width: 42,
            height: 20,
            // eslint-disable-next-line no-underscore-dangle
            fill: cfg.targetNode._cfg.model.meta.edge === '其他' ? 'rgba(240, 242, 245, 1)' : cfg.targetNode._cfg.model.meta.edge === '招标' && 'rgba(242, 241, 255, 1)' || cfg.targetNode._cfg.model.meta.edge === '投标' && 'rgba(230, 249, 249, 1)' || 'rgba(254, 249, 237, 1)',
            // x 和 y 分别减去 width / 2 与 height / 2,使矩形中心在 midPoint 上
            // eslint-disable-next-line no-underscore-dangle
            x: cfg.targetNode._cfg.model.meta.form === 1 ? midPoint.x + 15 : midPoint.x - 66,
            y: midPoint.y - 9,
          },
        });
      update: undefined,
    }, 'polyline'); 
    // polyline 表示我们自定义边的类型 这里是直角线
  • antv/g6树图(代码块4 )
const initGraph = (data: any) => {
   if (!data) {
     return;
   }
   const menu = new G6.Menu({
   	// 点击显示公司信息 见代码块5
   })
   const tooltip = new G6.Tooltip({
   // 鼠标移上去显示信息 见代码块5
   })
   graph = new G6.TreeGraph({
     container: 'mindMapContainer',
     // 1、绑定dom
     width: container.scrollWidth,
     height: container.scrollHeight,
     modes: {
       default: ['zoom-canvas', 'drag-canvas'],
     },
     renderer: 'svg',
     fitView: false, // 开启后图自动适配画布大小
     autoPaint: false,
     // fitCenter: true, // 图适应画布时,指定四周的留白。
     // linkCenter: true, // 图的中心将对齐到画布中心,但不缩放。优先级低于 fitView。
     // fitViewPadding: 100, // 边是否连入节点的中心
     animate: true,
     defaultNode: {
       type: 'flow-rect', // 我们之前自定义的节点
     },
     // 默认线
     defaultEdge: {
       type: 'flow-edge', // 我们之前自定义的线
       color: '#87e8de',
       style: {
         offset: 60,
         lineAppendWidth: 80,
         endArrow: true,// 显示箭头 或可以使用下面自定义箭头
         // endArrow: {
         //   path: 'M 4,0 L -4,-4 L -4,4 Z',
         //   d: 4,
         //   fill: 'rgba(38, 166, 245, 1)',
         //   stroke: 'steelblue',
         //   opacity: 1,
         // },
         circle: 15,
         stroke: '#D9D9D9', // 优先级高于color
       },
     },
     layout: { // 布局相关 项之间的距离 线段之间长度
       type: 'mindmap',
       direction: 'H',
       getHeight: () => 60,
       getWidth: () => 50,
       getVGap: () => 20,
       getHGap: () => 120,
     },
     plugins: [tooltip, menu], 
     //点击显示new G6.Menu   鼠标移上去显示信息new G6.Tooltip   注册
   });
   // 点击收起和展开
   const handleCollapse = (e: IG6GraphEvent) => {
     const target = e.target;
     const id = target.get('modelId');
     const item = graph.findById(id);
     const nodeModel = item.getModel();
     nodeModel.collapsed = !nodeModel.collapsed;
     graph.layout();
     graph.setItemState(item, 'collapse', nodeModel.collapsed as string);
   };
   // 自定义事件 对每个节点位置点击展开事件监听
   // 这里的collapse-text是我们之前自定义节点块起的name
   graph.on('collapse-text:click', (e) => {
     handleCollapse(e);
   });
   graph.on('collapse-back:click', (e) => {
     handleCollapse(e);
   });
   graph.data(data);
   graph.render();
   graph.fitView(40); // 居中处理
   devicetopologyChart.value = graph; // 便于销毁、重绘
}
 
  • antv/g6提示组件(代码块5)
这一块是鼠标放上去调取接口,点击展示接口返回的信息
别问,问就是ts类型校验,内置自定义提示getContent函数不支持async
1、定义了三个参数
const companyDetail = ref<any>() // 用于接收接口返回信息
const companyDetailShow = ref<boolean>(false) // 鼠标移上去就调取接口,这个参数控制接口只执行一次
const companyDetailName = ref<any>()
// 鼠标移上去就调取接口,这个参数控制接口只执行一次
提示函数用于调取公司信息
const tooltip = new G6.Tooltip({
	// 相对鼠标项移动的位置
      offsetX: 20,
      offsetY: 30,
      className: 'tooltipDirlog',
      // 允许出现 tooltip 的 item 类型
      itemTypes: ['node'],
      fixToNode: [0.5, 0.5],
      // custom the tooltip's content
      // 自定义 tooltip 内容 
      // 这一块我最终只return''
      // 实际上这里执行的是自执行函数调取接口的操作
      getContent: (e?: IG6GraphEvent | undefined) => {
        const outDiv = document.createElement('div') as HTMLDivElement;
        const nodeName = ((e as IG6GraphEvent).item as INode).getModel().title as string;
          unicodeValue.value = ((e as IG6GraphEvent).item as INode).getModel().id
          // 根据id获取公司信息
        (async () => {
          if (companyDetailName.value == nodeName && companyDetailShow.value) {
            return false
          }
          const params = {
            unicode: unicodeValue.value,
            main_unicode: decode(queryUnicode.value),
          }
          const {
            code,
            msg,
            data,
          } = await (async (params: any) => {
                return await requestSupplyMembers(params);// 接口返回
            }
          })(params);
          companyDetail.value = data // 定义ref对象保存
        })()
        outDiv.innerHTML = `${formatedNodeName}`;
        return '';
      },
      // 判断哪些不用调用接口
      shouldBegin: (e?: IG6GraphEvent | undefined) => {
        companyDetailName.value = ((e as IG6GraphEvent).item as INode).getModel().title;
        companyDetailShow.value = true
        // 这里有个companyDetailShow是控制调取次数
       
        if ((e as IG6GraphEvent).target.get('name') === 'name-shape' || (e as IG6GraphEvent).target.get('name') === 'mask-label-shape') {
       	// 当我们点击线段或者节点时不需要调取接口
          companyDetailShow.value = true
          return true;
        }
        return false;
      },
    });
  • antv/g6菜单组件(代码块5.2)
通过上面我们调取接口获取到公司信息,点击展示弹出框显示公司信息
 const menu = new G6.Menu({
      offsetX: 6,
      offsetY: 10,
      itemTypes: ['node'],
      trigger: 'click',
      getContent: (e?: IG6GraphEvent | undefined) => {
        const outDiv = document.createElement('div') as HTMLDivElement;
        // outDiv.style.padding = '0px 0px 20px 0px';
        const nodeName = ((e as IG6GraphEvent).item as INode).getModel().title as string;
        // console.log('nodeName', nodeName)
        let formatedNodeName = '';
        for (let i = 0; i < nodeName.length; i++) {
          formatedNodeName = `${formatedNodeName}${nodeName[i]}`;
          if (i !== 0 && i % 20 === 0) formatedNodeName = `${formatedNodeName}<br/>`;
        }
        outDiv.innerHTML = `${formatedNodeName}`;
        return `<div style="width: 368px;height: 316px;padding: 16px;background: #fff;">
                  <div style="display: flex;align-items: center;">
                    <img src="${companyDetail?.value?.logo === '' ? '@/assets/images/logoDefault.png' : companyDetail?.value?.logo}" style="width: 40px;height: 40px;" alt="">
                    <span style="font-weight: 500;font-size: 14px;line-height: 16px;color: #000;margin-left: 8px;">${companyDetail?.value?.name}</span>
                  </div>
                  <div style="margin-top: 8px;font-size: 14px;line-height: 20px;color: #999999;padding-bottom: 6px;
    border-bottom: 1px solid #F0F0F0;">
                    来源:<a
                    href="${companyDetail?.value?.source_url}"
                    style="font-weight: 400;font-size: 14px;line-height: 20px;color: #5297FF;"
                    target="_blank"
                  >${companyDetail?.value?.name}</a>
                  </div>
                  <div style="margin-top: 16px;">
                    <div style="font-size: 14px;color: #181818;font-weight: 500;margin-bottom: 8px;">风险数量:</div>
                    <div style="font-weight: 400;font-size: 14px;line-height: 22px;color: #333333;display: flex;
    flex-wrap: wrap;">
                      <div style="width: 56px;height: 44px;text-align: center;">${companyDetail?.value?.risk.software}<br /><span style="color: #999999;">软件</span></div>
                      <div style="width: 56px;height: 44px;text-align: center;">${companyDetail?.value?.risk.app}<br /><span style="color: #999999;">合规</span></div>
                      <div style="width: 56px;height: 44px;text-align: center;">${companyDetail?.value?.risk.email}<br /><span style="color: #999999;">邮箱</span></div>
                      <div style="width: 56px;height: 44px;text-align: center;">${companyDetail?.value?.risk.port}<br /><span style="color: #999999;">端口</span></div>
                      <div style="width: 56px;height: 44px;text-align: center;">${companyDetail?.value?.risk.code_leak}<br /><span style="color: #999999;">代码</span></div>
                      <div style="width: 56px;height: 44px;text-align: center;">${companyDetail?.value?.risk.net_disk}<br /><span style="color: #999999;">网盘</span></div>
                    </div>
                  </div>
                   <div style="margin-top: 16px;">
                    <div style="font-size: 14px;color: #181818;font-weight: 500;margin-bottom: 8px;">资产数量:</div>
                    <div style="font-weight: 400;font-size: 14px;line-height: 22px;color: #333333;display: flex;
    flex-wrap: wrap;">
                      <div style="width: 56px;height: 44px;text-align: center;">${companyDetail?.value?.assert.domain}<br /><span style="color: #999999;">域名</span></div>
                      <div style="width: 56px;height: 44px;text-align: center;">${companyDetail?.value?.assert.subdomain}<br /><span style="color: #999999;">子域名</span></div>
                      <div style="width: 56px;height: 44px;text-align: center;">${companyDetail?.value?.assert.ip}<br /><span style="color: #999999;">IP</span></div>
                      <div style="width: 56px;height: 44px;text-align: center;">${companyDetail?.value?.assert.web}<br /><span style="color: #999999;">网站</span></div>
                      <div style="width: 56px;height: 44px;text-align: center;">${companyDetail?.value?.assert.email}<br /><span style="color: #999999;">泄露邮箱</span></div>
                      <div style="width: 56px;height: 44px;text-align: center;">${companyDetail?.value?.assert.certificate}<br /><span style="color: #999999;">证书</span></div>
                      <div style="width: 56px;height: 44px;text-align: center; margin-top: 4px;">${companyDetail?.value?.assert.app}<br /><span style="color: #999999;">APP应用</span></div>
                      <div style="width: 56px;height: 44px;text-align: center; margin-top: 4px;">${companyDetail?.value?.assert.cloud_product}<br /><span style="color: #999999;">云产品</span></div>
                    </div>
                  </div>
                </div>`;
      },
      shouldBegin: (e?: IG6GraphEvent | undefined) => {
      // 哪些点击不需要展示弹框
        companyDetailName.value = ((e as IG6GraphEvent).item as INode).getModel().title;
        // console.log('aa', ((e as IG6GraphEvent).item as INode).getModel())
        if (((e as IG6GraphEvent).item as INode).getModel().title === '上游') {
          return false;
        }
        if ((e as IG6GraphEvent).target.get('name') === 'name-shape' || (e as IG6GraphEvent).target.get('name') === 'mask-label-shape' || ((e as IG6GraphEvent).item as INode).getModel().label === 'chainSupply') {
          // companyDetailShow.value = true
          return true;
        }
        return false;
      },
    });

项目拓展

思路说明:当页面数据成千上万时,数据加载很慢,
所以后期做了一个树展示5条,通过点击更多加载下五条数据;
核心:updateChildren(data, parentId)
更新数据,差量更新子树中的所有子节点。data 是一个子树数据数组。
若希望更新或增加一个 parentId 节点的子节点,请使用 updateChild。

1、
新建一个对象,itemAddObj
后台数据遍历:每个对象item中mate中返回total字段判断是否需要更多显示,
total大于5时进行定义一个参数保存当前的父级id、下游list、当前页码、条数等
itemAddObj.value[`${item?.id}1`] = {
      page: 1,
      pageSize: 5,
      list: item.children,
      id: `${item?.id}1`,
      total: item.meta?.total,
      trend: item.title === '下游' ? -1 : 1,
      root: item.meta.root,
    }
2、当前的父级push一个自定义更多项
item.children.push({
         children: null,
         id: `${item.id}1`,
         label: 'moreOver',
         meta: {
           edge: '', status: '存续(在营、开业、在册)', top: 180, trend: item.title === '上游' ? 1 : -1,
         },
         title: '更多',
       })
3、自定义节点时 添加一个name:moreOver-text
group.addShape('rect', {
      attrs: {
         x: meta.trend === -1 ? nodeOrigin.x : nodeOrigin.x + 60,
         y: nodeOrigin.y,
         cursor: 'pointer',
         width: 104,
         height: 28,
         fill: '#26a6f51a',
         radius: [4, 4, 0, 0],
       },
       name: 'moreOver-text',
       modelId: cfg.id,
     });
4、监听它的点击事件
graph.on('moreOver-text:click', (e) => {
     moreOver(e);
   });
   // 执行接口调用
   const moreOver = (e: IG6GraphEvent) => {
     // 获取新的数据
     const target = e.target;
     const id = target.get('modelId');
     const item = graph.findById(id);
     itemAddObj.value[id].page++; // 页码加一
     // 父组件调取接口返回数据
     emit('moveList', itemAddObj.value[id])
     // 接口返回数据结构[{} ]见下图
   };
   5、watch监听参数变化
   // g6 的updateChildren('list','id') // 根据id更改下面子树数据
   watch(() => props.addListObj, () => {
     const obj = itemAddObj.value[props.addListObj.id]
     // 找到当前对象
     // 插入传递进来的数据
     DateList.forEach((item:any) => {
       obj.list.splice(obj.list.length - 1, 0, item)
     });
     const id = props.addListObj.id.slice(0, props.addListObj.id.length - 1)
     // 根据tatal和页码判断是否显示更多
     if (obj.page * obj.pageSize > obj.total) {
       obj.list = obj.list.slice(0, obj.list.length - 1)
     }
     // 更新数据
     devicetopologyChart.value.updateChildren(obj.list, id)
   });

在这里插入图片描述

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值