UNPKG

butterfly-dag

Version:

一个基于数据驱动的节点式编排组件库,让你有方便快捷定制可视化流程图表

412 lines (394 loc) 10.7 kB
'use strict'; import Canvas from "./baseCanvas"; const _ = require('lodash'); import TreeNode from '../node/treeNode'; import Node from '../node/baseNode'; import Hierarchy from '../utils/layout/hierarchy'; class TreeCanvas extends Canvas { constructor(options) { super(options); this._NodeClass = TreeNode; } _handleTreeNodes(data, isFlat) { if (isFlat) { return data; } else { let queue = [data]; let nodeList = []; while (queue.length > 0) { let node = queue.pop(); nodeList.push(node); if (node.children && node.children.length > 0) { node.children.forEach((child) => { child.parent = node.id; }); queue = queue.concat(node.children); } } return nodeList; } } _isExistNode(node) { let result = super._isExistNode(node); if (result && node.subCollapsed) { result = false; } return; } _addEventListener() { super._addEventListener(); this.on('InnerEvents', (data) => { if (data.type === 'node:collapse') { this.collapseNode(data.nodeId); } else if (data.type == 'node:expand') { this.expandNode(data.nodeId); } }); } findSubTree(item) { let queue = [item]; let result = []; while (queue.length > 0) { let _item = queue.pop(); result.push(_item); if (_item.children?.length > 0) { queue = queue.concat(_item.children); } } return result; } collapseNode (nodeId) { let collapseNodes = []; let collapseEdges = []; let targetNode = this.getNode(nodeId); targetNode.collapsed = true; targetNode.options.collapsed = true; if (!targetNode) { return; } let queue = [targetNode]; while (queue.length > 0) { let node = queue.pop(); collapseNodes.push(node); if (node.children && node.children.length > 0) { queue = queue.concat(node.children); } } // 先去掉target node做filter collapseNodes = collapseNodes.filter((item) => { return item.id !== targetNode.id; }); let tmp = {}; collapseNodes.forEach((item) => { tmp[item.id] = item; item.subCollapsed = true; }); collapseEdges = this.edges.filter((item) => { let _isCollapsed = false; if (item.type === 'endpoint') { _isCollapsed = !!tmp[item.sourceNode.id] || !!tmp[item.targetNode.id] } else { _isCollapsed = !!tmp[item.source.id] || !!tmp[item.target.id]; } if (item.sourceNode.id === targetNode.id) { _isCollapsed = true; } if (_isCollapsed) { item.collapsed = true; } return _isCollapsed; }); collapseNodes.unshift(targetNode); collapseNodes.forEach((item) => { if (item.subCollapsed) { item.destroy(true); } // 重置子节点的collapsed状态 if (item.id !== nodeId && item.collapsed) { delete item.collapsed; } }); collapseEdges.forEach((item) => { item.destroy(true); }); this.redraw(); this.emit('system.node.collapse', { target: targetNode, nodes: collapseNodes, edges: collapseEdges }); this.emit('events', { type: 'node.collapse', target: targetNode, nodes: collapseNodes, edges: collapseEdges }); return { nodes: collapseNodes, edges: collapseEdges } } expandNode(nodeId, nodes) { let targetNode = this.getNode(nodeId); let subNodes = []; let collapseEdges = []; let queue = [targetNode]; while (queue.length > 0) { let node = queue.pop(); subNodes.push(node); if (node.children && node.children.length > 0) { queue = queue.concat(node.children); } } // 先去掉target node做filter subNodes = subNodes.filter((item) => { return item.id !== targetNode.id; }); let tmp = {}; subNodes.forEach((item) => { tmp[item.id] = item; item.subCollapsed = true; }); collapseEdges = this.edges.filter((item) => { let _isCollapsed = false; if (item.type === 'endpoint') { _isCollapsed = !!tmp[item.sourceNode.id] || !!tmp[item.targetNode.id] } else { _isCollapsed = !!tmp[item.source.id] || !!tmp[item.target.id]; } if (item.sourceNode.id === targetNode.id) { _isCollapsed = true; } return _isCollapsed; }); this.nodes = _.differenceBy(this.nodes, subNodes, 'id'); let addNodes = this.addNodes(subNodes, true); this.edges = _.filter(this.edges, (a) => { if (a.type === 'endpoint') { return !_.some(collapseEdges, ((b) => { return a.sourceNode.id === b.sourceNode.id && a.targetNode.id === b.targetNode.id && a.source === b.source && a.target === b.target; })); } else { return !_.some(collapseEdges, ((b) => { return a.source === b.source && a.target === b.target; })); } }); let addEdges = this.addEdges(collapseEdges, true); subNodes.forEach((item) => { delete item.subCollapsed; }); delete targetNode.collapsed; collapseEdges.forEach((item) => { delete item.collapsed; }); this.redraw(); this.emit('system.node.expand', { target: targetNode, nodes: addNodes, edges: addEdges }); this.emit('events', { type: 'node.expand', target: targetNode, nodes: addNodes, edges: addEdges }); } redraw() { let rootNode = this.getRootNode(); let tree = []; let tmpTreeObj = {}; let queue = [rootNode]; while (queue.length > 0) { let node = queue.pop(); let obj = { id: node.id }; if (tmpTreeObj[node.id]) { obj = tmpTreeObj[node.id]; } else { tmpTreeObj[obj.id] = obj; } if (node.isRoot) { obj['isRoot'] = node.isRoot; } if (node.collapsed) { obj['collapsed'] = node.collapsed; } tree.push(obj); if (node.children && node.children.length > 0) { obj.children = []; node.children.forEach((child) => { let _childObj = { id: child.id }; if (tmpTreeObj[child.id]) { _childObj = tmpTreeObj[child.id]; } else { tmpTreeObj[child.id] = _childObj; } if (child.isRoot) { _childObj['isRoot'] = child.isRoot; } if (child.collapsed) { _childObj['collapsed'] = child.collapsed; } obj.children.push(_childObj); }); queue = queue.concat(node.children); } } let nodes = tree.filter((item) => { return true; }); this._autoLayout({ nodes: nodes, edges: [], groups: [] }); this.nodes.forEach((item) => { if (item.subCollapsed) { return; } item?.endpoints?.forEach(endpoint => { endpoint.updatePos(); }); let obj = tmpTreeObj[item.id]; if (item.top !== obj.top || item.left !== obj.left) { item.options.top = obj.top; item.options.left = obj.left; item.options.treePos = obj.treePos; item.moveTo(obj.left, obj.top); } }); this.edges.forEach((item) => { item.redraw(); }); this.emit('system.canvas.redraw'); this.emit('events', { type: 'canvas:redraw' }); } addNodes(data, isNotEventEmit) { let nodes = super.addNodes(data, isNotEventEmit); nodes.forEach((item) => { if(item.parent) { let parentNode = this.getNode(item.parent); if (!_.some(parentNode.children, ['id', item.id])) { !parentNode.children && (parentNode.children = []); parentNode.children.push(item); } } }); return nodes; } removeNodes(data, isNotDelEdge, isNotEventEmit) { let nodes = data.map((item) => { if (item instanceof Node) { return item; } else { return this.getNode(item); } }); let rmNodes = []; nodes.forEach((item) => { let _subTree = this.findSubTree(item); rmNodes = rmNodes.concat(_subTree); // 如果是某个节点的子节点,将此节点从父节点的children中移除 if (item.parent) { const parentNode = this.getNode(item.parent); if (parentNode) { parentNode.children = parentNode.children.filter((node) => node.id !== item.id); } } }); rmNodes = _.unionBy(rmNodes, 'id'); let result = super.removeNodes(rmNodes, isNotDelEdge, isNotEventEmit); return result; } getRootNode() { return this.nodes.filter((item) => { return item.isRoot; })[0]; } draw(opts, params, callback) { const nodes = this._handleTreeNodes(opts.nodes || [], _.get(params, 'isFlatNode', false)); // 需要过滤掉collapsed的 super.draw({ nodes: nodes, edges: opts.edges, groups: opts.groups }, () => { let tmp = {}; this.nodes.forEach((item) => { tmp[item.id] = item; item.children = []; }); this.nodes.forEach((item) => { if (item.isRoot || !item.parent) { return; } let parentNode = tmp[item.parent]; if (!parentNode) { return; } !parentNode.children && (parentNode.children = []); parentNode.children.unshift(item); }); callback && callback({ nodes: this.nodes, edges: this.edges, groups: this.groups }); }); } // getDataMap(isFlat) { // } _autoLayout(options) { let rootNode = options.nodes.filter((item) => { return item.isRoot; })[0]; if (!rootNode) { return; } // 这部分需要优化 let type = _.get(this, 'layout.type', 'compactBox'); if (Hierarchy[type]) { const NODE_SIZE = 16; const NODE_WIDTH = 30; const PEM = 5; Hierarchy[type](rootNode, _.assign({ direction: 'H', getId(d) { return d.id; }, getHeight(d) { if (d.isRoot) { return NODE_SIZE * 2; } return NODE_SIZE; }, getWidth(d) { if (d.isRoot) { return NODE_WIDTH * 2; } return NODE_WIDTH; }, getHGap(d) { return 100; }, getVGap(d) { return 30; }, getSubTreeSep(d) { if (!d.children || !d.children.length) { return 0; } return PEM; } }, _.get(this, 'layout.options', {}))); } } } export default TreeCanvas;