<template>
  <div ref="mountNode"></div>
</template>

<script>
import G6 from '@antv/g6';
export default {
  props: {
    name: {
      type: String,
      default: 'name'
    },
    label: {
      type: String,
      default: 'relation'
    },
    children: {
      type: String,
      default: 'children'
    },
    width: {
      type: Number,
      default: window.innerWidth
    },
    height: {
      type: Number,
      default: 0
    },
    format: {
      default: item => {
        return item[this.label];
      }
    },
    data: {
      type: Object,
      default: () => {
        return {};
      }
    }
  },
  data() {
    return {
      graph: {},
      // 收缩图标
      COLLAPSE_ICON: function COLLAPSE_ICON(x, y, r) {
        return [
          ['M', x, y],
          ['a', r, r, 0, 1, 0, r * 2, 0],
          ['a', r, r, 0, 1, 0, -r * 2, 0],
          ['M', x + 2, y],
          ['L', x + 2 * r - 2, y]
        ];
      },
      // 展开图标
      EXPAND_ICON: function EXPAND_ICON(x, y, r) {
        return [
          ['M', x, y],
          ['a', r, r, 0, 1, 0, r * 2, 0],
          ['a', r, r, 0, 1, 0, -r * 2, 0],
          ['M', x + 2, y],
          ['L', x + 2 * r - 2, y],
          ['M', x + r, y - r + 2],
          ['L', x + r, y + r - 2]
        ];
      },
      shapeOptions: [
        { fill: '#16aafd', textColor: '#fff' },
        { fill: '#ffb068', textColor: '#333' },
        { fill: '#ffeada', textColor: '#333' },
        { fill: '#dff4ff', textColor: '#333' }
      ]
    };
  },
  methods: {
    /**
     * @Description 自定义元素
     * @Author ZPFly
     * @Date 2021/11/17 16:19
     */
    registerCmp() {
      const COLLAPSE_ICON = this.COLLAPSE_ICON;
      const shapeOptions = this.shapeOptions;
      const name = this.name;
      const children = this.children;
      const label = this.label;
      const format = this.format;
      // 自定义节点
      G6.registerNode(
        'tree-node',
        {
          drawShape: function drawShape(cfg, group) {
            const opt = shapeOptions[(cfg?.depth || 0) % shapeOptions.length];
            // 矩形
            const rect = group.addShape('rect', {
              attrs: {
                fill: cfg?.fill || opt.fill,
                stroke: cfg?.stroke || opt.fill,
                radius: 4
              }
            });
            const hasChildren = cfg[children] && cfg[children].length > 0;
            // 矩形中的文本 超过20个字符换行
            let content = hasChildren ? cfg[name] : cfg[name]?.replace(/(.{20})/g, '$1\n');
            content = content || '';
            const text = group.addShape('text', {
              attrs: {
                text: content.endsWith('\n') ? content.substr(0, content.lastIndexOf('\n')) : content,
                x: 0,
                y: 0,
                textAlign: 'left',
                textBaseline: 'middle',
                fill: opt.textColor
              }
            });
            const bbox = text.getBBox();
            if (hasChildren) {
              // 右侧图标
              group.addShape('marker', {
                attrs: {
                  x: bbox.maxX + 8,
                  y: bbox.minX + bbox.height / 2 - 6,
                  r: 6,
                  symbol: COLLAPSE_ICON,
                  stroke: '#666',
                  lineWidth: 2
                },
                className: 'collapse-icon'
              });
            }
            rect.attr({
              x: bbox.minX - 10,
              y: bbox.minY - 10,
              width: bbox.width + (hasChildren ? 36 : 22),
              height: bbox.height + 20
            });
            return rect;
          }
        },
        'single-shape'
      );
      // 自定义连线
      G6.registerEdge('hvh', {
        draw(cfg, group) {
          const startPoint = cfg.startPoint;
          const endPoint = cfg.endPoint;
          const shape = group.addShape('path', {
            attrs: {
              stroke: '#558fff',
              path: [
                ['M', startPoint.x, startPoint.y],
                ['L', endPoint.x / 3 + (2 / 3) * startPoint.x, startPoint.y], // 三分之一处
                ['L', endPoint.x / 3 + (2 / 3) * startPoint.x, endPoint.y], // 三分之二处
                ['L', endPoint.x, endPoint.y]
              ]
            },
            name: 'path-shape'
          });
          const cfgModel = cfg?.targetNode._cfg.model;
          let text = '';
          if (cfgModel.labelShow) {
            text = format(cfgModel);
          }
          // 标签文本
          group.addShape('text', {
            attrs: {
              text: text,
              fill: '#595959',
              textAlign: 'end',
              textBaseline: 'middle',
              x: endPoint.x - 10,
              y: text.indexOf('\n') == -1 ? endPoint.y - 10 : endPoint.y + 1
            }
          });
          return shape;
        }
      });
    },
    initGraph() {
      let height = this.height;
      const EXPAND_ICON = this.EXPAND_ICON;
      const COLLAPSE_ICON = this.COLLAPSE_ICON;
      const mountNode = this.$refs.mountNode;
      if (!height) {
        const nodeRect = mountNode.getBoundingClientRect();
        height = document.body.clientHeight - Math.ceil(nodeRect.top) - 2;
      }
      const graph = new G6.TreeGraph({
        container: mountNode,
        width: this.width,
        height: height,
        modes: {
          default: [
            {
              type: 'collapse-expand',
              onChange: function onChange(item, collapsed) {
                const data = item.get('model');
                const icon = item.get('group').findByClassName('collapse-icon');
                if (collapsed) {
                  icon.attr('symbol', EXPAND_ICON);
                } else {
                  icon.attr('symbol', COLLAPSE_ICON);
                }
                data.collapsed = collapsed;
                return true;
              }
            },
            'drag-canvas',
            'zoom-canvas'
          ]
        },
        defaultNode: {
          type: 'tree-node',
          anchorPoints: [
            [0, 0.5],
            [1, 0.5]
          ]
        },
        defaultEdge: {
          type: 'hvh' // cubic-horizontal / polyline
        },
        /** direction
         * ● LR（根节点在左，往右布局）
         * ● RL（根节点在右，往左布局）
         * ● H（根节点在中间，水平对称布局）
         * ● TB（根节点在上，往下布局）
         * ● BT（根节点在下，往上布局）
         * ● V（根节点在中间，垂直对称布局）
         */
        layout: {
          type: 'compactBox',
          direction: 'LR',
          getId: function getId(d) {
            return d.id;
          },
          getHeight: function getHeight() {
            return 16;
          },
          getWidth: function getWidth() {
            return 16;
          },
          getVGap: function getVGap() {
            return 30; // 每个节点的垂直间隙
          },
          getHGap: function getHGap() {
            return 160; // 每个节点的水平间隙
          }
        }
      });
      this.graph = graph;
      this.renderGraph(this.data);
    },

    renderGraph(data) {
      const graph = this.graph;
      let index = 0;
      G6.Util.traverseTree(data, item => {
        item.id = item.id || item.name + '_' + index++;
        return true;
      });
      graph.data(data);
      graph.render();
      graph.fitView();
    }
  },

  watch: {
    data(newVal) {
      this.renderGraph(newVal);
    }
  },

  created() {
    this.registerCmp();
  },

  mounted() {
    this.$nextTick(() => {
      this.initGraph();
    });
  }
};
</script>

<style scoped></style>
