diff --git a/demos/create-group.html b/demos/create-group.html new file mode 100644 index 0000000000..1cf44178a5 --- /dev/null +++ b/demos/create-group.html @@ -0,0 +1,177 @@ + + + + + Rect节点分组 + + +
+ + + + + diff --git a/package.json b/package.json index 4be9b3406a..0f054437a3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@antv/g6", - "version": "3.1.2", + "version": "3.1.3", "description": "graph visualization frame work", "main": "build/g6.js", "homepage": "https://github.com/antvis/g6", @@ -44,7 +44,6 @@ "babel-loader": "^8.0.0", "babel-plugin-module-resolver": "^3.1.1", "babel-plugin-transform-remove-strict-mode": "~0.0.2", - "babel-polyfill": "^6.26.0", "body-parser": "~1.18.2", "chai": "~4.1.2", "chai-almost": "^1.0.1", @@ -128,4 +127,4 @@ "engines": { "node": ">=8.9.0" } -} +} \ No newline at end of file diff --git a/src/global.js b/src/global.js index d5813a950a..6e99489a00 100755 --- a/src/global.js +++ b/src/global.js @@ -3,7 +3,7 @@ */ module.exports = { - version: '3.1.2', + version: '3.1.3', rootContainerClassName: 'root-container', nodeContainerClassName: 'node-container', edgeContainerClassName: 'edge-container', diff --git a/src/graph/controller/customGroup.js b/src/graph/controller/customGroup.js index 213089b041..8df7b3da46 100644 --- a/src/graph/controller/customGroup.js +++ b/src/graph/controller/customGroup.js @@ -77,11 +77,18 @@ class CustomGroup { * @param {string} type 群组类型,默认为circle,支持rect * @param {number} zIndex 群组层级,默认为0 * @param {boolean} updateDataModel 是否更新节点数据,默认为false,只有当手动创建group时才为true + * @param {object} title 分组标题配置 * @memberof ItemGroup + * @return {object} null */ - create(groupId, nodes, type = 'circle', zIndex = 0, updateDataModel = false) { + create(groupId, nodes, type = 'circle', zIndex = 0, updateDataModel = false, title = {}) { const graph = this.graph; const customGroup = graph.get('customGroup'); + const hasGroupIds = customGroup.get('children').map(data => data.get('id')); + if (hasGroupIds.indexOf(groupId) > -1) { + return console.warn(`已经存在ID为 ${groupId} 的分组,请重新设置分组ID!`); + } + const nodeGroup = customGroup.addGroup({ id: groupId, zIndex @@ -101,6 +108,30 @@ class CustomGroup { // 根据groupId获取group数据,判断是否需要添加title let groupTitle = null; + // 只有手动创建group时执行以下逻辑 + if (updateDataModel) { + const groups = graph.get('groups'); + // 如果是手动创建group,则原始数据中是没有groupId信息的,需要将groupId添加到node中 + nodes.forEach(nodeId => { + const node = graph.findById(nodeId); + const model = node.getModel(); + if (!model.groupId) { + model.groupId = groupId; + } + }); + + // 如果是手动创建 group,则将 group 也添加到 groups 中 + if (!groups.find(data => data.id === groupId)) { + groups.push({ + id: groupId, + title + }); + graph.set({ + groups + }); + } + } + const groupData = graph.get('groups').filter(data => data.id === groupId); if (groupData && groupData.length > 0) { @@ -169,6 +200,7 @@ class CustomGroup { const textShape = nodeGroup.addShape('text', { attrs: { text, + stroke: '#444', x: titleX + offsetX, y: titleY + offsetY, ...titleStyle @@ -183,18 +215,6 @@ class CustomGroup { // 设置graph中groupNodes的值 graph.get('groupNodes')[groupId] = nodes; - // 只有手动创建group时执行以下逻辑 - if (updateDataModel) { - // 如果是手动创建group,则原始数据中是没有groupId信息的,需要将groupId添加到node中 - nodes.forEach(nodeId => { - const node = graph.findById(nodeId); - const model = node.getModel(); - if (!model.groupId) { - model.groupId = groupId; - } - }); - } - graph.setAutoPaint(autoPaint); graph.paint(); } @@ -1007,7 +1027,6 @@ class CustomGroup { } groupKeyShape.attr(keyshapePosition); } - // 如果存在标题,则更新标题位置 this.updateGroupTitle(nodeGroup, id, titleX, titleY); }); @@ -1033,8 +1052,8 @@ class CustomGroup { let offsetX = 0; let offsetY = 0; if (titleConfig) { - offsetX = titleConfig.offsetX; - offsetY = titleConfig.offsetY; + offsetX = titleConfig.offsetX || 0; + offsetY = titleConfig.offsetY || 0; } groupTitleShape.attr({ x: x + offsetX, diff --git a/src/graph/graph.js b/src/graph/graph.js index 9bc96d1868..71bd375c44 100755 --- a/src/graph/graph.js +++ b/src/graph/graph.js @@ -5,7 +5,7 @@ * @LastEditTime: 2019-08-22 11:22:16 * @Description: Graph */ -const { groupBy } = require('lodash'); +const { groupBy, isString } = require('lodash'); const G = require('@antv/g/lib'); const EventEmitter = G.EventEmitter; const Util = require('../util'); @@ -359,8 +359,14 @@ class Graph extends EventEmitter { */ addItem(type, model) { if (type === 'group') { - const { groupId, nodes, type, zIndex } = model; - return this.get('customGroupControll').create(groupId, nodes, type, zIndex, true); + const { groupId, nodes, type, zIndex, title } = model; + let groupTitle = title; + if (isString(title)) { + groupTitle = { + text: title + }; + } + return this.get('customGroupControll').create(groupId, nodes, type, zIndex, true, groupTitle); } return this.get('itemController').addItem(type, model); } @@ -546,14 +552,25 @@ class Graph extends EventEmitter { // 存在单个群组 // 获取所有有groupID的node const nodeInGroup = nodes.filter(node => node.groupId); - + const groupsArr = []; // 根据groupID分组 const groupIds = groupBy(nodeInGroup, 'groupId'); for (const groupId in groupIds) { const nodeIds = groupIds[groupId].map(node => node.id); this.get('customGroupControll').create(groupId, nodeIds, groupType, groupIndex); groupIndex--; + // 获取所有不重复的 groupId + if (!groupsArr.find(data => data.id === groupId)) { + groupsArr.push({ + id: groupId + }); + } } + + this.set({ + groups: groupsArr + }); + } else { // 将groups的数据存到groups中 this.set({ groups }); diff --git a/src/index.js b/src/index.js index 73c556e9b3..5535a4779e 100755 --- a/src/index.js +++ b/src/index.js @@ -7,7 +7,6 @@ const G = require('@antv/g/lib'); const Shape = require('./shape'); const Layout = require('./layout'); const Behaviors = require('./behavior'); -require('babel-polyfill'); const G6 = { Graph: require('./graph/graph'), diff --git a/test/unit/graph/controller/custom-group-spec.js b/test/unit/graph/controller/custom-group-spec.js index 7d0281d9d2..74b048083d 100644 --- a/test/unit/graph/controller/custom-group-spec.js +++ b/test/unit/graph/controller/custom-group-spec.js @@ -609,7 +609,7 @@ describe.only('signle layer group', () => { expect(groupNodes.p1.length).eql(1); const groups = graph.get('groups'); - expect(groups.length).eql(0); + expect(groups.length).eql(4); // 删除group1 const customGroup = graph.get('customGroupControll'); @@ -945,3 +945,91 @@ describe.only('nesting layer group', () => { expect(graph.destroyed).to.be.true; }); }); + +// 手动创建分子 +describe.only('create node group', () => { + it('use addItem create group', () => { + const data = { + nodes: [ + { + id: 'node1', + label: 'fck', + groupId: 'group1', + x: 100, + y: 100 + }, + { + id: 'node2', + label: 'node2', + x: 150, + y: 200 + }, + { + id: 'node3', + label: 'node3', + x: 300, + y: 100 + } + ], + edges: [ + { + source: 'node1', + target: 'node2' + }, + { + source: 'node2', + target: 'node3' + } + ], + groups: [ + { + id: 'group1', + title: '1' + } + ] + }; + + const graph = new G6.Graph({ + container: div, + width: 1500, + height: 1000, + pixelRatio: 2, + modes: { + default: [ 'drag-group' ] + }, + defaultNode: { + shape: 'circleNode' + }, + defaultEdge: { + color: '#bae7ff' + } + }); + + graph.data(data); + graph.render(); + + graph.data(data); + graph.render(); + + let { groups } = graph.save(); + expect(groups.length).equal(1); + + graph.addItem('group', { + groupId: 'xxx', + nodes: [ 'node2', 'node3' ], + type: 'rect', + title: '自定义' + }); + + groups = graph.save().groups; + expect(groups.length).eql(2); + + const customGroup = graph.get('customGroup'); + const children = customGroup.get('children'); + expect(children.length).eql(2); + + const { nodes } = graph.save(); + const groupNodes = nodes.filter(node => node.groupId === 'xxx'); + expect(groupNodes.length).eql(2); + }); +});