<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>