antv/g6 学习: 数据驱动节点编排和点击连线项目

g6版本 

"@antv/g6": "^4.6.15"

数据

// 列表数据
public class DataBack {
    private String userId;
    private String operateId;
    private String abilityType;
    private String abilityId;
    private String abilityName;
    private String modeType;
}
// 流程数据
public class DataBackShow {
    private String id;
    private String operateId;
    private String abilityId;
    private String depAbilityIds;
    private String status;
    private String abilityType;
    private String abilityName;
    private String currentX;
    private String currentY;
}

组件调用

根据用户,运营分类
<!--user-id:用户id; operate-id:运营id; drawingListBackKey:刷新组件;
    refreshListBack: 刷新数据,刷新组件方法; drawing-list: 节点和连线数据  -->
<draggable-back
                  :user-id="user.userId"
                  :operate-id="operateId"
                  :key="drawingListBackKey"
                  @refresh="refreshListBack"
                  :drawing-list="drawingListBack"
                  @dragover.prevent
                />


// 刷新数据方法
refreshListBack(drawingList) {
      this.drawingListBack = drawingList
      this.drawingListBackKey = Date.now()
    },

模型组件

<template>
  <el-row style="height: 100%;">
    <el-col style="border-left: 2px solid #DCDFE6;height: 100%;">
      <div id="container" style="height: 100%;position: relative;border: 1px solid black;">
      </div>
    </el-col>
  </el-row>
</template>

<script>
import G6 from '@antv/g6'
export default {
  name: 'DraggableBack',
  props: [
    'userId',
    'drawingList',
    'operateId',
  ],
  data() {
    return {
      // 图上节点
      data: { nodes: [], edges: []},
      // 节点数据
      graph: undefined
    }
  },
  mounted() {
    this.initData() // 初始化数据
    this.initComponent() // 初始化图
  },
  methods: {
    // 边缘:增删; 删除节点 之后,回填值
    saveBackForm(drawingList = this.drawingList) {
      this.$emit('refresh', drawingList)
    },
    nodeDragend(model, currentX, currentY) {// 移动节点结束后,更新一下数据
      let { id } = model
      for (let i = 0; i < this.drawingList.length; i++) {
        let item = this.drawingList[i]
        if (item.id === id) {
          item.currentX = currentX.toFixed(1)
          item.currentY = currentY.toFixed(1)
        }
      }
      this.saveBackForm()
    },
    // 删除边
    delEdge(edge) {
      this.$confirm('请确认是否删除连线?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        let source = edge.getSource().getID()
        let target = edge.getTarget().getID()
        for (let i = 0; i < this.drawingList.length; i++) {
          let item = this.drawingList[i]
          if (item.id === target) {
            // 转成数组过滤删除后,转成字符串
            let dep = item.depAbilityIds.split(",").filter(Boolean)
            item.depAbilityIds = dep.filter(item => item !== source).toString()
          }
        }
        // this.graph.removeItem(edge) // 仅去除图上的线
        this.saveBackForm()
      }).catch(() => {
      });
    },
    // 新增边
    addEdge(source, target) {
      // 自己不能连自己
      if (source !== target) {
        for (let i = 0; i < this.drawingList.length; i++) {
          let item = this.drawingList[i]
          if (item.id === target) {
            // 转成数组添加后,转成字符串
            let dep = item.depAbilityIds.split(",").filter(Boolean)
            dep.push(source)
            item.depAbilityIds = dep.toString()
          }
        }
      }
      this.saveBackForm()
    },
    // 删除节点
    delModel(model) {
      this.$confirm('请确认是否删除节点?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      }).then(() => {
        let { id } = model
        // 去掉连接线: 循环查找,节点来源是否存在于其他节点依赖中,存在则去除
        for (let i = 0; i < this.drawingList.length; i++) {
          let item = this.drawingList[i]
          let dep = item.depAbilityIds.split(",").filter(Boolean) // 节点来源数组化
          if (dep.indexOf(id) > -1) { // 来源是否存在于这个数组
            // 存在,则过滤删除后,转成字符串
            item.depAbilityIds = dep.filter(item => item !== id).toString()
          }
        }
        // 去掉节点:
        let drawingList = this.drawingList.filter(item => item.id !== id)

        // this.graph.removeItem(model) // 仅去除图上的节点
        this.saveBackForm(drawingList)
      }).catch(() => {
      });
    },
    // 初始化数据
    initData(){
      for (let i = 0; i < this.drawingList.length; i++) {
        let item = this.drawingList[i]
        this.data.nodes.push({
          id: item.id,
          label: this.fittingString(item.abilityName, 80, 12),
          type: 'service',
          x: Number(item.currentX),
          y: Number(item.currentY),
        })
        let dep = item.depAbilityIds.split(",").filter(Boolean)
        for (let j = 0; j < dep.length; j++) {
          this.data.edges.push({ source: dep[j], target: item.id })
        }
      }
    },
    // 初始化图
    initComponent() {
      let container = document.getElementById('container')
      const width = container.scrollWidth
      const height = container.scrollHeight

      // 自定义节点
      G6.registerNode(
        'service',
        {
          // 绘制后的附加操作
          afterDraw(cfg, group) {
            // 添加删除按钮
            group.addShape('circle', {
              attrs: {
                x: 50,
                y: -20,
                r: 10,
                fill: 'red',
                cursor: 'pointer',
              },
              visible: false,
              name: 'custom-circle-shape',
            })
            group.addShape('text', { // 删除的X号
              attrs: {
                text: 'X',
                x: 45.5,
                y: -11.5,
                fontSize: 14,
                fill: '#fff',
                cursor: 'pointer',
              },
              visible: false,
              name: 'custom-text-shape',
            })
            // 添加锚点
            const bbox = group.getBBox();
            const anchorPoints = this.getAnchorPoints(cfg)
            anchorPoints.forEach((anchorPos, i) => {
              group.addShape('circle', {
                attrs: {
                  r: 5,
                  x: bbox.x + bbox.width * anchorPos[0],
                  y: bbox.y + bbox.height * anchorPos[1],
                  fill: '#fff',
                  stroke: '#5F95FF'
                },
                name: `anchor-point`, // the name, for searching by group.find(ele => ele.get('name') === 'anchor-point')
                anchorPointIdx: i, // flag the idx of the anchor-point circle
                links: 0, // cache the number of edges connected to this shape
                visible: false, // invisible by default, shows up when links > 1 or the node is in showAnchors state
                draggable: true // allow to catch the drag events on this shape
              })
            })
          },
          // 锚点的位置
          getAnchorPoints(cfg) {
            return cfg.anchorPoints || [[0, 0.5], [0.5, 1], [1, 0.5], [0.5, 0]];
          },
          // 触发事件
          setState(name, value, item) {
            if (name === 'show') {
              // 删除按钮
              const custom = item.getContainer().findAll(ele => ele.get('name').indexOf('custom') > -1)
              custom.forEach(point => {
                if (value) point.show()
                else point.hide()
              })
              // 锚点是否展示
              // const anchorPoints = item.getContainer().findAll(ele => ele.get('name') === 'anchor-point');
              // anchorPoints.forEach(point => {
              //   if (value || point.get('links') > 0) point.show()
              //   else point.hide()
              // })
            }
            // 点击节点高亮
            if (name === 'selected') {
              const service = item.getContainer().findAll(ele => ele.get('name') === 'service-keyShape')[0]
              if (value) {
                service.attr('stroke', '#5400fc');
              } else {
                service.attr('stroke', '');
              }
            }
          }
        }, 'rect')

      const SnapLine = new G6.SnapLine(); // G6插件:对齐辅助线

      // 没有的时候新增
      if (this.graph === undefined) {
        this.graph = new G6.Graph({
          container: 'container',
          width,
          height,
          fitCenter: false, // 开启后图将会被平移,图的中心将对齐到画布中心
          autoPaint: false,
          // layout: {
          //   // type: 'dagre',
          // },
          modes: {
            default: ['drag-node', {
              type: 'create-edge',
              trigger: 'click',
              shouldBegin: e => {
                this.graph.startNode = e.item
                return e.target && !(e.target.get('name').indexOf('custom') > -1)
              },
              shouldEnd(e) {
                  if (e.item === this.graph.startNode) {
                    return false;
                  }
                  this.graph.startNode = null;
                  return true;
                },
            }],
            snap: ['grid'] // 防止节点移动的时候,轻微偏移
          },
          grid: { // 防止节点移动的时候,轻微偏移
            forceAlign: true,   // 强制节点位置对齐到网格
            cell: 20            // 网格大小
          },
          defaultNode: {
            type: 'rect',
            style: {
              width: 150,
              height: 50,
              radius: 10,
              stroke: '',
              fill: 'l(90) 0:#fff 1:#f00' // 渐变色
            },
            labelCfg: {
              style: {
                fontSize: 12,
              }
            }
          },
          defaultEdge: {
            color: '#00ffff',
            style: {
              endArrow: {
                path: 'M 0,0 L 8,4 L 8,-4 Z',
                fill: '#00ffff',
              },
              cursor: 'pointer',
              lineWidth: 2,
            },
          },
          plugins: [SnapLine],
        })
      }
      this.graph.data(this.data) // 填充数据
      this.graph.render() // 渲染
      this.graphTriggerEvent() // 触发事件
    },
    fittingString(str, maxWidth, fontSize) {// 字符串换行
      let currentWidth = 0
      let res = ''
      let lineNum = []
      const pattern = new RegExp('[\u4E00-\u9FA5]+')
      str.split('').forEach((letter, i) => {// 根据字符串长度进行分割获取分割点
        if (pattern.test(letter)) {
          currentWidth += fontSize
        } else {
          currentWidth += G6.Util.getLetterWidth(letter, fontSize)
        }
        if (currentWidth > maxWidth) {
          lineNum.push(i)
          currentWidth = 0
        }
      })
      let length = lineNum.length
      if (length > 0) {// 超过长度循环切割,最后补上最后str
        for (let i = 0; i < length; i++) {
          res += str.substring(0, lineNum[i]) + '\n';
          str = str.substring(lineNum[i]);
        }
      }
      return res += str
    },
    // 图的触发事件
    graphTriggerEvent() {
      // 删除点击节点
      this.graph.on('custom-circle-shape:click', (evt) => {
        this.delModel(evt.item.getModel())
      })
      this.graph.on('custom-text-shape:click', (evt) => {
        this.delModel(evt.item.getModel())
      })
      // 移入边时高亮
      this.graph.on('edge:mouseenter', (e) => {
        const edge = e.item;
        this.graph.updateItem(edge, {
          style: {
            endArrow: {
              path: 'M 0,0 L 8,4 L 8,-4 Z',
              fill: 'red',
            },
            stroke: 'red',
          },
        });
      });
      // 移出边时还原
      this.graph.on('edge:mouseleave', (e) => {
        const edge = e.item;
        this.graph.updateItem(edge, {
          style: {
            endArrow: {
              path: 'M 0,0 L 8,4 L 8,-4 Z',
              fill: '#00ffff'
            },
            stroke: '#00ffff',
          },
        });
      });
      // 删除点击时触发边
      this.graph.on('edge:click', (e) => {
        this.delEdge(e.item)
      })
      // 点击新增边,更新一下样式
      this.graph.on('node:click', (e) => {
        this.graph.setItemState(e.item, 'selected', true)
      })
      this.graph.on('canvas:click', (e) => {
        let selected = this.graph.findAllByState('node', 'selected')[0]
        if (selected) {
          this.graph.setItemState(selected, 'selected', false)
        }
      })
      // 点击新增边,还原样式
      this.graph.on('aftercreateedge', (e) => {
        const { edge } = e;
        this.addEdge(edge.getSource().getID(), edge.getTarget().getID())
      })
      // 锚点和删除的展示
      this.graph.on('node:mouseenter', e => {
        this.graph.setItemState(e.item, 'show', true)
      })
      this.graph.on('node:mouseleave', e => {
        this.graph.setItemState(e.item, 'show', false)
      })
      // 拖拽节点时,保存节点位置
      this.graph.on('node:dragend', e => {
        const node = e.item;
        const model = node.getModel();
        const { x, y } = model;
        this.nodeDragend(model, x, y)
      })

    },
  }
}
</script>

<style scoped>
</style>

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值