antv g6 4.x vue 决策树
原型:https://g6-v4.antv.vision/examples/case/treeDemos/#decisionTree
<template> <div class="main-content-box"> <div id="container"></div> </div> </template> <script> import api from '@/api'; import G6 from '@antv/g6'; import insertCss from 'insert-css'; const colors = { B: '#5B8FF9', R: '#F46649', Y: '#EEBC20', G: '#5BD8A6', DI: '#A7A7A7' }; export default { indexName: 'SubjectDependenceTree', components: {}, props: { isTreeDialog: Boolean }, data() { return { list: [], //扁平结构的数据(从接口获取) noRepeatList: [], //存放剔除重复值之后的数据 mockData: {}, nodeChildren: [], //单个node子级数据 childrenNode: {} //存放子节点点击的node }; }, mounted() { let params = { indexCode: 'POLICY_AWARDS_PUNISH_MANAGE', reportTheme: 0, isFirst: true }; this.searchQueryIndexTree(params); }, methods: { getInit() { let vm = this; G6.registerNode( 'flow-rect', { shapeType: 'flow-rect', draw(cfg, group) { const { indexName = '', indexCode, isChild, collapsed, indexCategory, status } = cfg; const grey = '#CED4D9'; // background: #0092ee; // color: white; const rectConfig = { width: 202, height: 60, lineWidth: 1, fontSize: 12, fill: '#fff', radius: 4, stroke: grey, opacity: 1 }; const nodeOrigin = { x: -rectConfig.width / 2, y: -rectConfig.height / 2 }; const textConfig = { textAlign: 'left', textBaseline: 'bottom' }; const rect = group.addShape('rect', { attrs: { x: nodeOrigin.x, y: nodeOrigin.y, ...rectConfig } }); const rectBBox = rect.getBBox(); //科目编码 group.addShape('text', { attrs: { ...textConfig, x: 8 + nodeOrigin.x, y: 23 + nodeOrigin.y, text: indexCode.length > 23 ? indexCode.substr(0, 23) + '...' : indexCode, fontSize: 12, fill: '#000', opacity: 0.85, cursor: 'pointer' }, name: 'name-shape' }); // 科目名称 const price = group.addShape('text', { attrs: { ...textConfig, x: 12 + nodeOrigin.x, y: rectBBox.maxY - 12, text: indexName.length > 10 ? indexName.substr(0, 10) + '...' : indexName, fontSize: 12, opacity: 0.85, fill: '#000' } }); // 科目类型 group.addShape('text', { attrs: { ...textConfig, x: price.getBBox().maxX + 5, y: rectBBox.maxY - 12, text: indexCategory == 1 ? '离线指标' : indexCategory == 2 ? '指标填报' : '自定义', fontSize: 10, fill: '#0092ee', opacity: 0.75 } }); // rect 展开节点( + -边框) if (cfg.isChild) { group.addShape('rect', { attrs: { x: rectConfig.width / 2 - 8, y: -8, width: 16, height: 16, stroke: 'rgba(0, 0, 0, 0.25)', cursor: 'pointer', radius: 8, fill: '#fff' }, // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type name: 'collapse-back', modelId: cfg.id }); // collpase text(+ -) group.addShape('text', { attrs: { x: rectConfig.width / 2, y: 1, textAlign: 'center', textBaseline: 'middle', text: collapsed ? '+' : '-', fontSize: 16, cursor: 'pointer', fill: 'rgba(0, 0, 0, 0.25)' }, // must be assigned in G6 3.3 and later versions. it can be any string you want, but should be unique in a custom item type name: 'collapse-text', modelId: cfg.id }); } this.drawLinkPoints(cfg, group); return rect; }, // * 响应节点的状态变化。 // * 在需要使用动画来响应状态变化时需要被复写 setState(name, value, item) { if (name === 'collapse') { const group = item.getContainer(); const collapseText = group.find((e) => e.get('name') === 'collapse-text'); if (collapseText) { console.log(value); if (!value) { collapseText.attr({ text: '-' }); } else { collapseText.attr({ text: '+' }); } } } }, getAnchorPoints() { return [ [0, 0.5], [1, 0.5] ]; } }, 'rect' ); G6.registerEdge( 'flow-cubic', { getControlPoints(cfg) { let controlPoints = cfg.controlPoints; // 指定controlPoints if (!controlPoints || !controlPoints.length) { const { startPoint, endPoint, sourceNode, targetNode } = cfg; const { x: startX, y: startY, coefficientX, coefficientY } = sourceNode ? sourceNode.getModel() : startPoint; const { x: endX, y: endY } = targetNode ? targetNode.getModel() : endPoint; let curveStart = (endX - startX) * coefficientX; let curveEnd = (endY - startY) * coefficientY; curveStart = curveStart > 40 ? 40 : curveStart; curveEnd = curveEnd < -30 ? curveEnd : -30; controlPoints = [ { x: startPoint.x + curveStart, y: startPoint.y }, { x: endPoint.x + curveEnd, y: endPoint.y } ]; } return controlPoints; }, getPath(points) { const path = []; path.push(['M', points[0].x, points[0].y]); path.push(['C', points[1].x, points[1].y, points[2].x, points[2].y, points[3].x, points[3].y]); return path; } }, 'single-line' ); const container = document.getElementById('container'); const width = container.scrollWidth; const height = container.scrollHeight || 500; const graph = new G6.TreeGraph({ container: 'container', width, height, padding: [20, 50], // defaultLevel: 3, defaultZoom: 0.3, // minZoom: 0.3, // maxZoom: 1.5, modes: { default: ['zoom-canvas', 'drag-canvas'] }, fitView: false, //是否启用画布自适应,适配画布大小 animate: false, //是否启用全部动画 defaultNode: { type: 'flow-rect' }, // 设置边的参数 defaultEdge: { type: 'cubic-horizontal', style: { stroke: '#CED4D9' } }, layout: { type: 'indented', direction: 'LR', dropCap: false, preventOverlap: true, // 防止节点重叠 indent: 300, getHeight: () => { return 40; } } }); graph.data(this.mockData[0]); graph.render(); graph.fitView(100); //适配视图,自动缩放和平移以适应内容大小,默认留白为 30px graph.zoom(0.6); // 设置缩放级别为50% const handleCollapse = async (e) => { this.childrenNode = []; const target = e.target; const id = target.get('modelId'); const item = graph.findById(id); const nodeModel = item.getModel(); const children = nodeModel.children; if (!children || children.length === 0) { this.childrenNode = nodeModel; let params = { indexCode: nodeModel.indexCode, //科目编码 reportTheme: 0, //主题 isFirst: false //初始化三层依赖树true,后面展开第四层或往后 false }; this.addTree(params); if (!nodeModel.children) { nodeModel.children = []; } // 如果childData是一个数组,则直接赋值给parentData.children // 如果是一个对象,则使用parentData.children.push(obj) setTimeout(() => { if (this.nodeChildren && this.nodeChildren.length > 0) { nodeModel.isChild = true; nodeModel.children = this.nodeChildren; nodeModel.collapsed = false; } else { //子级存在单个重复数据 nodeModel.isChild = false; nodeModel.children = []; nodeModel.collapsed = true; } item.update(nodeModel); }, 300); } else { nodeModel.collapsed = !nodeModel.collapsed; // graph.updateItem(id, nodeModel); item.update(nodeModel); } setTimeout(() => { graph.render(); // graph.refresh(); // graph.layout(); item.updatePosition(nodeModel); graph.focusItem(item); //将当前的节点设置为焦点 }, 500); }; graph.on('collapse-text:click', (e) => { handleCollapse(e); }); graph.on('collapse-back:click', (e) => { handleCollapse(e); }); this.$nextTick(() => { 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); }; }); }, // 展开子结构 async addTree(params) { this.list = []; this.nodeChildren = []; const res = await api.queryIndexTree(params); if (res.success) { this.list = res.data; this.nodeChildren = this.comparativeData(); //子级剔除重复数据 this.noRepeatList = this.noRepeatList.concat(this.comparativeData()); //剔除重复数据 } else { this.$message.error(`${res.message}`); } }, // 查询结构树初始化前三层 async searchQueryIndexTree(params) { this.list = []; const res = await api.queryIndexTree(params); if (res.success) { this.list = res.data; this.noRepeatList = this.noRepeatList.concat(this.comparativeData()); //剔除重复数据 this.mockData = this.toTree(this.noRepeatList); //将扁平化数据转换为树结构 this.$nextTick(() => { setTimeout(() => { this.getInit(); }, 500); }); } else { this.$message.error(`${res.message}`); } }, //对比是否存在重复数据,同层级存在重复数据后点击的节点isChild=false,collapsed=false comparativeData() { let arr = []; let hasUsefulNode = false; let result = this.list.map((i) => { let one = this.noRepeatList.findIndex((k) => k.indexCode == i.indexCode); if (one < 0) { hasUsefulNode = true; arr.push({ id: i.id, indexCode: i.indexCode, indexName: i.indexName, parentId: i.parentId, indexCategory: i.indexCategory, //类型 collapsed: !i.isOpen, //控制是否展开 isChild: i.isChild // 控制是否有子级 }); } }); if (!hasUsefulNode) { let one = this.noRepeatList.find((k) => k.indexCode == this.childrenNode.indexCode); if (one) { one.isChild = false; one.collapsed = true; } } return arr; }, //打平数据转换为树结构 toTree(items) { const tree = []; //存放最终的树状结构 const itemMap = {}; //存放每个节点数据 for (const item of items) { const { indexCode } = item; itemMap[indexCode] = { ...item, children: [] }; //每个节点增加一个children属性,用来存放子节点 } // 遍历所有节点,将每个节点放到其父节点的children数组中 for (const item of items) { const { indexCode, parentId } = item; // 如果是根节点,则直接放入结果数组中 if (indexCode == parentId) { //if (parentId == null || parentId == 0) { tree.push(itemMap[indexCode]); } else { // 如果不是根节点,则将当前节点放入其父节点的children数组中 // 子元素的parentId 等于 父节点的id itemMap[parentId] 父节点 //itemMap[indexCode] 当前节点 if (itemMap[parentId]) itemMap[parentId].children.push(itemMap[indexCode]); } } return tree; } } }; </script> <style lang="scss" scoped> .g6-component-tooltip { background-color: rgba(0, 0, 0, 0.65); padding: 10px; box-shadow: rgb(174, 174, 174) 0px 0px 10px; width: fit-content; color: #fff; border-radius: 4px; } </style>
 
                    
                     
                    
                 
                    
                 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号