需求描述
用户收起某节点后要展示出当前节点的子节点数量,当节点展开后不显示子节点数量。
功能效果
分析
1、找到跟节点收起/展开相关的菜单按钮。
2、找到节点收起/展开的事件,添加一个childrenSize字段用于存储 收起状态下节点的子数量,当节点收起时添加一个图标显示该childrenSize的值。
节点收起时,设置childrenSize的值为当前节点的子节点数量
节点展开时,设置childrenSize的值为null
3、注册显示子节点数量的图标。当childrenSize有值时则显示该图标。
var BACK_PATH = 'M 0 10 A 1 1 0 0 0 22 10 A 1 1 0 0 0 0 10';
var MASK_PATH = 'M 0 10 A 1 1 0 0 0 22 10 A 1 1 0 0 0 0 10';
// 节点收缩时的图形 显示子节点数量
var ChildrenSizeIcon = kity.createClass('ChildrenSizeIcon', {
base: kity.Group,
constructor: function () {
this.callBase();
this.setSize(20);
this.create();
// this.setId(utils.uuid('node_priority'));
},
setSize: function (size) {
this.width = this.height = size;
},
create: function () {
var white, back, mask, number; // 4 layer
white = new kity.Path().setPathData(MASK_PATH).fill('white');
back = new kity.Path().setPathData(BACK_PATH).setTranslate(0.5, 0.5);
mask = new kity.Path().setPathData(MASK_PATH).setOpacity(0.8).setTranslate(0.5, 0.5);
number = new kity.Text()
.setX(this.width / 2 - 0.5).setY(this.height / 2)
.setTextAnchor('middle')
.setVerticalAlign('middle')
.setFontItalic(true)
.setFontSize(12)
.fill('white');
this.addShapes([back, mask, number]);
this.mask = mask;
this.back = back;
this.number = number;
},
setValue: function (value) {
var back = this.back,
mask = this.mask,
number = this.number;
var color = ['#000000', '#000000'];
if (color) {
back.fill(color[1]);
mask.fill(color[0]);
}
number.setContent(value);
}
});
var childrenSizeRenderer = kity.createClass('childrenSizeRenderer', {
base: Renderer,
create: function (node) {
return new ChildrenSizeIcon();
},
shouldRender: function (node) {
return node.getData('childrenSize');
},
update: function (icon, node, box) {
var data = node.getData('childrenSize');
var spaceLeft = node.getStyle('space-left'),
x, y;
icon.setValue(data);
x = box.left - icon.width - spaceLeft;
y = -icon.height / 2;
icon.setTranslate(x, y);
return new kity.Box({
x: x,
y: y,
width: icon.width,
height: icon.height
});
}
});
注册图标
完整代码
在kityminder.core.js文件中找到_p[46],将其中的代码照着expand.js的修改。
expand.js 中的完整代码
define(function (require, exports, module) {
var kity = require('../core/kity');
var utils = require('../core/utils');
var keymap = require('../core/keymap');
var MinderNode = require('../core/node');
var Command = require('../core/command');
var Module = require('../core/module');
var Renderer = require('../core/render');
Module.register('Expand', function () {
var minder = this;
var EXPAND_STATE_DATA = 'expandState',
STATE_EXPAND = 'expand',
STATE_COLLAPSE = 'collapse';
// 将展开的操作和状态读取接口拓展到 MinderNode 上
kity.extendClass(MinderNode, {
/**
* 展开节点
* @param {Policy} policy 展开的策略,默认为 KEEP_STATE
*/
expand: function () {
this.setData(EXPAND_STATE_DATA, STATE_EXPAND);
return this;
},
/**
* 收起节点
*/
collapse: function () {
this.setData(EXPAND_STATE_DATA, STATE_COLLAPSE);
return this;
},
/**
* 判断节点当前的状态是否为展开
*/
isExpanded: function () {
var expanded = this.getData(EXPAND_STATE_DATA) !== STATE_COLLAPSE;
return expanded && (this.isRoot() || this.parent.isExpanded());
},
/**
* 判断节点当前的状态是否为收起
*/
isCollapsed: function () {
return !this.isExpanded();
}
});
/**
* @command Expand
* @description 展开当前选中的节点,保证其可见
* @param {bool} justParents 是否只展开到父亲
* * `false` - (默认)保证选中的节点以及其子树可见
* * `true` - 只保证选中的节点可见,不展开其子树
* @state
* 0: 当前有选中的节点
* -1: 当前没有选中的节点
*/
var ExpandCommand = kity.createClass('ExpandCommand', {
base: Command,
execute: function (km, justParents) {
var node = km.getSelectedNode();
if (!node) return;
if (justParents) {
node = node.parent;
}
while (node.parent) {
node.expand();
node.setData('childrenSize', null)
node = node.parent;
}
node.renderTree();
km.layout(100);
},
queryState: function (km) {
var node = km.getSelectedNode();
return node && !node.isRoot() && !node.isExpanded() ? 0 : -1;
}
});
/**
* @command ExpandToLevel
* @description 展开脑图到指定的层级
* @param {number} level 指定展开到的层级,最少值为 1。
* @state
* 0: 一直可用
*/
var ExpandToLevelCommand = kity.createClass('ExpandToLevelCommand', {
base: Command,
execute: function (km, level) {
km.getRoot().traverse(function (node) {
if (node.getLevel() < level) {
node.expand();
node.setData('childrenSize', null);
}
if (node.getLevel() == level && !node.isLeaf()) {
node.collapse();
node.setData('childrenSize', node.children.length);
}
});
km.refresh(100);
},
enableReadOnly: true
});
/**
* @command Collapse
* @description 收起当前节点的子树
* @state
* 0: 当前有选中的节点
* -1: 当前没有选中的节点
*/
var CollapseCommand = kity.createClass('CollapseCommand', {
base: Command,
execute: function (km) {
var node = km.getSelectedNode();
if (!node) return;
node.collapse();
node.setData('childrenSize', node.children.length)
node.renderTree();
km.layout();
},
queryState: function (km) {
var node = km.getSelectedNode();
return node && !node.isRoot() && node.isExpanded() ? 0 : -1;
}
});
var Expander = kity.createClass('Expander', {
base: kity.Group,
constructor: function (node) {
this.callBase();
this.radius = 6;
this.outline = new kity.Circle(this.radius).stroke('gray').fill('white');
this.sign = new kity.Path().stroke('gray');
this.addShapes([this.outline, this.sign]);
this.initEvent(node);
this.setId(utils.uuid('node_expander'));
this.setStyle('cursor', 'pointer');
},
initEvent: function (node) {
this.on('mousedown', function (e) {
minder.select([node], true);
if (node.isExpanded()) {
node.collapse();
node.setData('childrenSize', node.children.length)
} else {
node.expand();
node.setData('childrenSize', null)
}
node.renderTree().getMinder().layout(100);
node.getMinder().fire('contentchange');
e.stopPropagation();
e.preventDefault();
});
this.on('dblclick click mouseup', function (e) {
e.stopPropagation();
e.preventDefault();
});
},
setState: function (state) {
if (state == 'hide') {
this.setVisible(false);
return;
}
this.setVisible(true);
var pathData = ['M', 1.5 - this.radius, 0, 'L', this.radius - 1.5, 0];
if (state == STATE_COLLAPSE) {
pathData.push(['M', 0, 1.5 - this.radius, 'L', 0, this.radius - 1.5]);
}
this.sign.setPathData(pathData);
}
});
var BACK_PATH = 'M 0 10 A 1 1 0 0 0 22 10 A 1 1 0 0 0 0 10';
var MASK_PATH = 'M 0 10 A 1 1 0 0 0 22 10 A 1 1 0 0 0 0 10';
// 节点收缩时的图形 显示子节点数量
var ChildrenSizeIcon = kity.createClass('ChildrenSizeIcon', {
base: kity.Group,
constructor: function () {
this.callBase();
this.setSize(20);
this.create();
// this.setId(utils.uuid('node_priority'));
},
setSize: function (size) {
this.width = this.height = size;
},
create: function () {
var white, back, mask, number; // 4 layer
white = new kity.Path().setPathData(MASK_PATH).fill('white');
back = new kity.Path().setPathData(BACK_PATH).setTranslate(0.5, 0.5);
mask = new kity.Path().setPathData(MASK_PATH).setOpacity(0.8).setTranslate(0.5, 0.5);
number = new kity.Text()
.setX(this.width / 2 - 0.5).setY(this.height / 2)
.setTextAnchor('middle')
.setVerticalAlign('middle')
.setFontItalic(true)
.setFontSize(12)
.fill('white');
this.addShapes([back, mask, number]);
this.mask = mask;
this.back = back;
this.number = number;
},
setValue: function (value) {
var back = this.back,
mask = this.mask,
number = this.number;
var color = ['#000000', '#000000'];
if (color) {
back.fill(color[1]);
mask.fill(color[0]);
}
number.setContent(value);
}
});
var childrenSizeRenderer = kity.createClass('childrenSizeRenderer', {
base: Renderer,
create: function (node) {
return new ChildrenSizeIcon();
},
shouldRender: function (node) {
return node.getData('childrenSize');
},
update: function (icon, node, box) {
var data = node.getData('childrenSize');
var spaceLeft = node.getStyle('space-left'),
x, y;
icon.setValue(data);
x = box.left - icon.width - spaceLeft;
y = -icon.height / 2;
icon.setTranslate(x, y);
return new kity.Box({
x: x,
y: y,
width: icon.width,
height: icon.height
});
}
});
var ExpanderRenderer = kity.createClass('ExpanderRenderer', {
base: Renderer,
create: function (node) {
if (node.isRoot()) return;
this.expander = new Expander(node);
node.getRenderContainer().prependShape(this.expander);
node.expanderRenderer = this;
this.node = node;
return this.expander;
},
shouldRender: function (node) {
return !node.isRoot();
},
update: function (expander, node, box) {
if (!node.parent) return;
var visible = node.parent.isExpanded();
expander.setState(visible && node.children.length ? node.getData(EXPAND_STATE_DATA) : 'hide');
var vector = node.getLayoutVectorIn().normalize(expander.radius + node.getStyle('stroke-width'));
var position = node.getVertexIn().offset(vector.reverse());
this.expander.setTranslate(position);
}
});
return {
commands: {
'expand': ExpandCommand,
'expandtolevel': ExpandToLevelCommand,
'collapse': CollapseCommand
},
events: {
'layoutapply': function (e) {
var r = e.node.getRenderer('ExpanderRenderer');
if (r.getRenderShape()) {
r.update(r.getRenderShape(), e.node);
}
},
'beforerender': function (e) {
var node = e.node;
var visible = !node.parent || node.parent.isExpanded();
var minder = this;
node.getRenderContainer().setVisible(visible);
if (!visible) e.stopPropagation();
},
'normal.keydown': function (e) {
if (this.getStatus() == 'textedit') return;
if (e.originEvent.keyCode == keymap['/']) {
var node = this.getSelectedNode();
if (!node || node == this.getRoot()) return;
var expanded = node.isExpanded();
this.getSelectedNodes().forEach(function (node) {
if (expanded) node.collapse();
else node.expand();
node.renderTree();
});
this.layout(100);
this.fire('contentchange');
e.preventDefault();
e.stopPropagationImmediately();
}
if (e.isShortcutKey('Alt+`')) {
this.execCommand('expandtolevel', 9999);
}
for (var i = 1; i < 6; i++) {
if (e.isShortcutKey('Alt+' + i)) {
this.execCommand('expandtolevel', i);
}
}
}
},
renderers: {
outside: ExpanderRenderer,
left: childrenSizeRenderer,
},
contextmenu: [{
command: 'expandtoleaf',
query: function () {
return !minder.getSelectedNode();
},
fn: function (minder) {
minder.execCommand('expandtolevel', 9999);
}
}, {
command: 'expandtolevel1',
query: function () {
return !minder.getSelectedNode();
},
fn: function (minder) {
minder.execCommand('expandtolevel', 1);
}
}, {
command: 'expandtolevel2',
query: function () {
return !minder.getSelectedNode();
},
fn: function (minder) {
minder.execCommand('expandtolevel', 2);
}
}, {
command: 'expandtolevel3',
query: function () {
return !minder.getSelectedNode();
},
fn: function (minder) {
minder.execCommand('expandtolevel', 3);
}
}, {
divider: true
}]
};
});
});