antvis/G6

Do you want to work on this issue?

You can request for a bounty in order to promote it!

[Bug]: Overlapping nodes after collapse issue - Indent tree layout does not recalculate node spacing correctly after collapse/expand operation #6751

TifaXu131 posted onGitHub

Describe the bug / 问题描述

在使用 G6 紧凑树(indent)布局时,当执行节点折叠操作后,部分节点出现重叠现象,有时候会前后颠倒

重现步骤: 点击折叠

G6 版本:5.0.42

Image

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

<script setup>
import { onMounted, onUnmounted } from 'vue'
import { Rect as GRect, Text as GText } from '@antv/g'
import { Graph, GraphEvent, register, treeToGraphData, Badge, CommonEvent, ExtensionCategory } from '@antv/g6'
import { getTaskRelationTree } from '@/service/apis/task'

// 自定义节点:只保留 label 和折叠按钮逻辑
class TreeNode extends GRect {
  // 获取 label 样式
  getLabelStyle(attributes) {
    const [width, height] = this.getSize(attributes)
    return {
      name: 'label',
      x: -width / 2 + 8,
      y: -height / 2 + 50,
      text: this.data?.label,
      fontSize: 12,
      fill: '#000',
      cursor: 'pointer'
    }
  }
  // 绘制 label
  drawLabelShape(attributes, container) {
    const labelStyle = this.getLabelStyle(attributes)
    const label = this.upsert('label', GText, labelStyle, container)
    if (label && !Reflect.has(label, '__bind__')) {
      Reflect.set(label, '__bind__', true)
      label.addEventListener(CommonEvent.CLICK, () => {
        // 点击 label 可执行跳转或其他操作
      })
    }
  }
  // 获取折叠按钮样式
  getCollapseStyle(attributes) {
    if (this.childrenData.length === 0) return false
    const { collapsed } = attributes
    const [width] = this.getSize(attributes)
    return {
      fill: '#D2D6DA',
      fontSize: 16,
      text: collapsed ? '+' : '-',
      x: width / 2,
      y: 0,
      cursor: 'pointer'
    }
  }
  // 绘制折叠按钮
  drawCollapseShape(attributes, container) {
    const collapseStyle = this.getCollapseStyle(attributes)
    const btn = this.upsert('collapse', Badge, collapseStyle, container)
    if (btn && !Reflect.has(btn, '__bind__')) {
      Reflect.set(btn, '__bind__', true)
      btn.addEventListener(CommonEvent.CLICK, () => {
        try {
          const { collapsed } = this.attributes
          const graph = this.context.graph
          if (collapsed) graph.expandElement(this.id)
          else graph.collapseElement(this.id)
        } catch (error) {
          console.error(error)
        }
      })
    }
  }
  // 重写 render,仅绘制 label 和折叠按钮
  render(attributes = this.parsedAttributes, container) {
    super.render(attributes, container)
    this.drawCollapseShape(attributes, container)
    this.drawLabelShape(attributes, container)
  }
}
register(ExtensionCategory.NODE, 'tree-node', TreeNode)

let graph
let mindMapData = []

const getData = async () => {
  // 此处替换为实际任务编码
  const data = await getTaskRelationTree('some-task-code')
  mindMapData = data
}

onMounted(async () => {
  await getData()

  graph = new Graph({
    container: 'container',
    data: treeToGraphData(mindMapData, {
      getNodeData: (datum) => {
        const nodeData = {
          ...datum,
          id: datum.nodeId,
          label: datum.name,
          style: { collapsed: false }
        }
        if (datum.children && datum.children.length) {
          nodeData.children = datum.children.map(child => child.nodeId)
        }
        return nodeData
      },
      getId: (d) => d.nodeId,
      getEdgeData: (source, target) => ({
        id: `${source.nodeId}-${target.nodeId}`,
        source: source.nodeId,
        target: target.nodeId
      })
    }),
    node: {
      type: 'tree-node',
      style: {
        size: [260, 80],
        radius: 4
      }
    },
    edge: {
      type: 'cubic-horizontal',
      style: {
        stroke: '#D2D6DA',
        lineWidth: 1,
        controlPoints: true
      }
    },
    layout: {
      type: 'indented',
      direction: 'LR',
      dropCap: false,
      indent: 340,
      controlPoints: true,
      getHeight: () => 60,
      nodesep: 40,
      ranksep: 120,
      preLayout: false
    },
    behaviors: ['zoom-canvas', 'drag-canvas', 'drag-element']
  })

  graph.render()
  graph.once(GraphEvent.AFTER_RENDER, () => {
    graph.fitCenter()
  })
})

onUnmounted(() => {
  if (graph) graph.destroy()
  mindMapData = null
  graph = null
})
</script>

<style scoped>
#container {
  position: relative;
  height: 100%;
}
</style>

No response

Steps to Reproduce the Bug or Issue / 重现步骤

点击折叠

G6 Version / G6 版本

Please select / 请选择

OS / 操作系统

  • macOS
  • Windows
  • Linux
  • Others / 其他

Browser / 浏览器

  • Chrome
  • Edge
  • Firefox
  • Safari (Limited support / 有限支持)
  • IE (Nonsupport / 不支持)
  • Others / 其他

可以复现的代码链接 点第二个元素折叠 就会出现元素重叠 https://stackblitz.com/edit/react-2lkvgqmu?file=index.js

posted by TifaXu131 3 months ago

Fund this Issue

$0.00
Funded
Only logged in users can fund an issue

Pull requests