AntV G6自定义流程图Graph

AntV G6自定义流程图Graph

前言:自定义流程图节点样式、节点可拖动,节点文字过长省略,文字过长的节点悬浮展示tooltip插件悬浮框,修改tooltip悬浮框样式。

一、先看下我的效果图

整体效果
鼠标悬浮节点样式

二、参考官方图表示例地址

Dagre 流程图

三、代码已整合成vue组件

调用组件传值给mapData,这里只是自己演示设置了假数据a

<template>
  <div style="width: 100%;height: 100%;">
    <!-- 流程预览图画布 -->
    <div id="containerBox"
         style="background: #F7F8FA;border-radius: 4px;">
    </div>
  </div>
</template>
<script>
import G6 from '@antv/g6'
import insertCss from 'insert-css';
// 这个样式是作用于modes里的tooltip
insertCss(`
 .g6-tooltip {
    border-radius: 4px;
    font-size: 12px;
    color: #fff;
    background-color: #3C3D42;
    text-align: left;
    width: 240px;
    padding: 6px 12px;
  }
`);
insertCss(`
  .g6-component-tooltip {
    border-radius: 4px;
    font-size: 12px;
    color: #fff;
    background-color: #3C3D42;
    text-align: left;
    width: 240px;
    padding: 6px 12px;
  }
`);
export default {
  name: "previewProcess",
  data () {
    return {
      a: {
        nodes: [
          {
            id: '1',
            dataType: 'start',
            name: 'alps_file1',
          },
          {
            id: '2',
            dataType: 'condition',
            name: 'alps_file2二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二二',
          },
          {
            id: '3',
            dataType: 'examine',
            name: 'alps_file3三三三三三三三三三三三三三三三三三三三三三三三三三三三三三三三三三三三三三三三三三',
          },
          {
            id: '4',
            dataType: 'examine',
            name: 'sql_file4四四四四四四四四四四四四四四四四四四四四四四四四四四四四四四四四四四四四四四四四四四',
          },
          {
            id: '5',
            dataType: 'examine',
            name: 'sql_file5',
          },
          {
            id: '6',
            dataType: 'end',
            name: 'feature_etl_1',
          }
        ],
        edges: [
          {
            source: '1',
            target: '2',
          },
          {
            source: '1',
            target: '3',
          },
          {
            source: '2',
            target: '4',
          },
          {
            source: '3',
            target: '4',
          },
          {
            source: '4',
            target: '5',
          },
          {
            source: '5',
            target: '6',
          }
        ],
      }
    }
  },
  props: {
    mapData: { // 预览数据
      type: Object,
      default: () => {
        return {}
      }
    },
    mapHeight: { // 高度H
      type: String,
      default: '600'
    },
  },
  mounted () {
    this.customNode()
    this.createNode(this.a) // 参数名为a,后期正式使用a都改成mapData
  },
  activated () {
  },
  watch: {
  },
  computed: {
  },
  methods: {
    // 文字过长显示...
    fittingString (str, maxWidth, fontSize) {
      const ellipsis = '...'
      const ellipsisLength = G6.Util.getTextSize(ellipsis, fontSize)[0]
      let currentWidth = 0
      let res = str
      const pattern = new RegExp('[\u4E00-\u9FA5]+') // distinguish the Chinese charactors and letters
      str.split('').forEach((letter, i) => {
        if (currentWidth > maxWidth - ellipsisLength) return
        if (pattern.test(letter)) {
          // Chinese charactors
          currentWidth += fontSize
        } else {
          // get the width of single letter according to the fontSize
          currentWidth += G6.Util.getLetterWidth(letter, fontSize)
        }
        if (currentWidth > maxWidth - ellipsisLength) {
          res = `${str.substr(0, i)}${ellipsis}`
        }
      })
      return res
    },
    // 自定义节点方法
    customNode () {
      const _that = this

      G6.registerNode(
        'sql',
        {
          drawShape (cfg, group) {
            let index = _that.a.nodes.length - 1
            // 判断为开始、结束节点
            if (cfg.id === _that.a.nodes[0].id || cfg.id === _that.a.nodes[index].id) {
              const startEnd = group.addShape('rect', {
                attrs: {
                  x: -10,
                  y: -25,
                  width: 100,
                  height: 36,
                  stroke: '#C3C5D9',
                  radius: 18,
                  fill: '#fff',
                },
                name: 'main-box',
                draggable: true,
              });
              group.addShape('text', {
                attrs: {
                  text: cfg.id === _that.a.nodes[0].id ? '发起节点' : '结束',
                  x: 40,
                  y: -6,
                  textAlign: 'center',
                  textBaseline: 'middle',
                  fill: '#202340',
                  fontSize: 14,
                  fontWeight: 500,
                  fontFamily: 'PingFangSC-Regular, PingFang SC',
                  lineHeight: 20,
                },
                name: 'text-shape',
              });
              return startEnd;
            } else {
              // 判断是条件还是审核人展示不同样式
              if (cfg.dataType === 'condition') {
                // 条件:整个节点矩形
                const rect = group.addShape('rect', {
                  attrs: {
                    x: -75,
                    y: -25,
                    width: 230,
                    height: 86,
                    radius: 4,
                    stroke: '#C3C5D9',
                    fill: '#F7F8FA',
                    lineWidth: 1,
                  },
                  name: 'rect-shape',
                });
                // 条件:上面标题矩形
                const titleRect = group.addShape('rect', {
                  attrs: {
                    x: -74.5,
                    y: -24.5,
                    width: 229,
                    height: 35,
                    radius: [4, 4, 0, 0],
                    stroke: '#C3C5D9',
                    fill: '#E2E3EE',
                    lineWidth: 0,
                  },
                  name: 'rect-title',
                });
                // 条件:上面矩形标题文字
                const title = group.addShape('text', {
                  attrs: {
                    text: _that.fittingString(cfg.dataType + '标题', 198, 14),
                    x: -58,
                    y: -8,
                    textAlign: 'left',
                    textBaseline: 'middle',
                    fill: '#565B85',
                    fontSize: 14,
                    fontWeight: 500,
                    fontFamily: 'PingFangSC-Regular, PingFang SC',
                    lineHeight: 20,
                  },
                  name: 'title-box',
                });
                // 条件:矩形下面的文本文字
                const text = group.addShape('text', {
                  attrs: {
                    text: _that.fittingString(cfg.name + '文本', 198, 14),
                    x: -58,
                    y: 35,
                    textAlign: 'left',
                    textBaseline: 'middle',
                    fill: '#202340',
                    fontSize: 14,
                    fontWeight: 500,
                    fontFamily: 'PingFangSC-Regular, PingFang SC',
                    lineHeight: 20,
                  },
                  name: 'text-box',
                });
                return rect;
              } else { // 审核人展示的样式
                // 审核人:整个节点矩形
                const rect = group.addShape('rect', {
                  attrs: {
                    x: -75,
                    y: -25,
                    width: 230,
                    height: 86,
                    radius: 4,
                    stroke: '#FFCA80',
                    fill: '#FFF9F2',
                    lineWidth: 1,
                  },
                  name: 'rect-shape',
                });
                // 审核人:上面标题矩形
                const titleRect = group.addShape('rect', {
                  attrs: {
                    x: -74.5,
                    y: -24.5,
                    width: 229,
                    height: 35,
                    radius: [4, 4, 0, 0],
                    stroke: '#C3C5D9',
                    fill: '#FFDFB2',
                    lineWidth: 0,
                  },
                  name: 'rect-title',
                });
                // 审核人:上面矩形标题文字
                const title = group.addShape('text', {
                  attrs: {
                    text: _that.fittingString(cfg.dataType + '标题', 198, 14),
                    x: -58,
                    y: -8,
                    textAlign: 'left',
                    textBaseline: 'middle',
                    fill: '#D97F00',
                    fontSize: 14,
                    fontWeight: 500,
                    fontFamily: 'PingFangSC-Regular, PingFang SC',
                    lineHeight: 20,
                  },
                  name: 'title-box',
                });
                // 审核人:矩形下面的文本文字
                const text = group.addShape('text', {
                  attrs: {
                    text: _that.fittingString(cfg.name + '文本', 198, 14),
                    x: -58,
                    y: 35,
                    textAlign: 'left',
                    textBaseline: 'middle',
                    fill: '#202340',
                    fontSize: 14,
                    fontWeight: 500,
                    fontFamily: 'PingFangSC-Regular, PingFang SC',
                    lineHeight: 20,
                  },
                  name: 'text-box',
                });
                return rect;
              }

            }
          },
        },
        'single-node',
      );
    },
    // 根据数据创建树状图节点
    createNode (mapData) {
      let tooltip = new G6.Tooltip({
        offsetX: 0,
        offsetY: 30,
        // 固定tooltip位置
        // fixToNode: [0, 1],
        // 允许出现 tooltip 的 item 类型
        itemTypes: ['node'],
        // 自定义 tooltip 内容
        getContent: (e) => {
          console.log('e=======', e.item);
          const outDiv = document.createElement('div');
          outDiv.style.width = '100%';
          // outDiv.style.background = 'red';
          outDiv.innerHTML = `<div>${e.item._cfg.model.name}</div>`;
          return outDiv;
        },
        shouldBegin: (e) => {
          let str = this.fittingString(e.item._cfg.model.name, 198, 14)
          let index = str.indexOf('.')
          if (index > -1) {
            return true
          }
          return false
        },
      });
      
      const container = document.getElementById('containerBox');
      const width = container.scrollWidth;
      const height = container.scrollHeight || this.mapHeight;
      const graph = new G6.Graph({
        container: 'containerBox',
        width,
        height,
        plugins: [tooltip],
        layout: {
          type: 'dagre',
          nodesepFunc: (d) => { // 节点左右边距
            if (d.id === '3') {
              return 500;
            }
            return 50;
          },
          ranksep: 60,  // 节点上下边距
        },
        defaultNode: {
          type: 'sql',
          anchorPoints: [
            [0.5, 1],
            [0.5, 0]
          ]
        },
        defaultEdge: { // 连接线样式
          type: 'polyline',
          style: {
            radius: 0,
            offset: 45,
            // 这个字段默认是true
            // endArrow: true,
            lineWidth: 2,
            stroke: '#C3C5D9',
            endArrow: {
              path: 'M 0,0 L 8,4 L 8,-4 Z',
              fill: '#C3C5D9',
            },
          },
        },
        nodeStateStyles: {
          selected: {
            stroke: '#d9d9d9',
            fill: '#5394ef',
          },
        },
        modes: {
          default: [
            'drag-canvas',
            'zoom-canvas',
            'drag-node',
            // {
            //   type: 'tooltip',
            //   formatText (model) {
            //     const cfg = model.name;
            //     return cfg
            //   },
            //   shouldBegin: (e) => {
            //     let str = this.fittingString(e.item._cfg.model.name, 198, 14)
            //     let index = str.indexOf('.')
            //     if (index > -1) {
            //       return true
            //     }
            //     return false
            //   },
            //   offset: 30
            // },
          ],
        },
        fitView: true,
      });
      graph.data(mapData);
      graph.render();
    // 这里是用于判断连接线是否有箭头
    //  mapData.nodes.forEach((node) => {
    //    if (node.dataType === 'condition') {
    //      mapData.edges.forEach((edge) => {
    //        if (edge.target === node.id) {
    //          edge.style.endArrow = false;
    //       }
    //     })
    //   }
    // });
      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);
        };
    }
  }
}
</script>


四、这里提供了直接改官方示例数据,简单的参考

在官方图表示例找到Dagre 流程图

import G6 from '@antv/g6';
import insertCss from 'insert-css';

insertCss(`
  .g6-tooltip {
    border-radius: 6px;
    font-size: 12px;
    color: #fff;
    background-color: #000;
    padding: 2px 8px;
    text-align: center;
  }
`);

const data = {
  nodes: [
    {
      id: '1',
      dataType: 'alps',
      name: 'alps_file1',
      conf: [
        {
          label: 'conf',
          value: 'pai_graph.conf',
        },
        {
          label: 'dot',
          value: 'pai_graph.dot',
        },
        {
          label: 'init',
          value: 'init.rc',
        },
      ],
    },
    {
      id: '2',
      dataType: 'alps',
      name: 'alps_file2',
      conf: [
        {
          label: 'conf',
          value: 'pai_graph.conf',
        },
        {
          label: 'dot',
          value: 'pai_graph.dot',
        },
        {
          label: 'init',
          value: 'init.rc',
        },
      ],
    },
    {
      id: '3',
      dataType: 'alps',
      name: '11111',
      conf: [
        {
          label: 'conf',
          value: 'pai_graph.conf',
        },
        {
          label: 'dot',
          value: 'pai_graph.dot',
        },
        {
          label: 'init',
          value: 'init.rc',
        },
      ],
    },
    {
      id: '4',
      dataType: 'sql',
      name: 'sql_file1',
      conf: [
        {
          label: 'conf',
          value: 'pai_graph.conf',
        },
        {
          label: 'dot',
          value: 'pai_graph.dot',
        },
        {
          label: 'init',
          value: 'init.rc',
        },
      ],
    },
    {
      id: '5',
      dataType: 'sql',
      name: 'sql_file2',
      conf: [
        {
          label: 'conf',
          value: 'pai_graph.conf',
        },
        {
          label: 'dot',
          value: 'pai_graph.dot',
        },
        {
          label: 'init',
          value: 'init.rc',
        },
      ],
    },
    {
      id: '6',
      dataType: 'feature_etl',
      name: 'feature_etl_1',
      conf: [
        {
          label: 'conf',
          value: 'pai_graph.conf',
        },
        {
          label: 'dot',
          value: 'pai_graph.dot',
        },
        {
          label: 'init',
          value: 'init.rc',
        },
      ],
    }
  ],
  edges: [
    {
      source: '1',
      target: '2',
    },
    {
      source: '1',
      target: '3',
    },
    {
      source: '2',
      target: '4',
    },
    {
      source: '3',
      target: '4',
    },
    {
      source: '4',
      target: '5',
    },
    {
      source: '5',
      target: '6',
    }
  ],
};

G6.registerNode(
  'sql',
  {
    drawShape(cfg, group) {
      const rect = group.addShape('rect', {
        attrs: {
          x: -75,
          y: -25,
          width: 150,
          height: 50,
          radius: 10,
          stroke: '#5B8FF9',
          fill: '#C6E5FF',
          lineWidth: 3,
        },
        name: 'rect-shape',
      });
      if (cfg.name) {
        group.addShape('text', {
          attrs: {
            text: cfg.name,
            x: 0,
            y: 0,
            fill: '#00287E',
            fontSize: 14,
            textAlign: 'center',
            textBaseline: 'middle',
            fontWeight: 'bold',
          },
          name: 'text-shape',
        });
      }
      return rect;
    },
  },
  'single-node',
);

const container = document.getElementById('container');
const width = container.scrollWidth;
const height = container.scrollHeight || 500;
const graph = new G6.Graph({
  container: 'container',
  width,
  height,
  layout: {
    type: 'dagre',
    nodesepFunc: (d) => {
      if (d.id === '3') {
        return 500;
      }
      return 50;
    },
    ranksep: 70,
  },
  defaultNode: {
    type: 'sql',
    anchorPoints: [
      [0.5, 1],
      [0.5, 0]
    ]
  },
  defaultEdge: {
    type: 'polyline',
    style: {
      radius: 0,
      offset: 45,
      endArrow: true,
      lineWidth: 2,
      stroke: '#C2C8D5',
	  endArrow: {
        path: 'M 0,0 L 8,4 L 8,-4 Z',
        fill: '#C2C8D5',
      },
    },
  },
  nodeStateStyles: {
    selected: {
      stroke: '#d9d9d9',
      fill: '#5394ef',
    },
  },
  modes: {
    default: [
      'drag-canvas',
      'zoom-canvas',
      'drag-node',
      // 'click-select',
      // {
      //   type: 'tooltip',
      //   formatText(model) {
      //     const cfg = model.conf;
      //     const text = [];
      //     cfg.forEach((row) => {
      //       text.push(row.label + ':' + row.value + '<br>');
      //     });
      //     return text.join('\n');
      //   },
      //   offset: 30,
      // },
    ],
  },
  fitView: true,
});
graph.data(data);
graph.render();

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);
  };

  • 2
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值