首先,在进行折叠操作的时候就用数组记录下被折叠的节点,因为setState函数只要改变了状态(节点展开和折叠)就会执行,所以在数据更新后渲染历史节点状态时也会执行,要注意 不要重复push
setState(name, value, item) {
let flag = false; // 用来判断是否是历史数据用来初始化的,如果是历史数据就不push进数组
if (name === 'collapsed') {
if (value === true) {
str.forEach((x) => {
if (x._cfg.id === item._cfg.id) {
flag = true;
}
});
if (!flag) {
str.push(item);
flag = false;
}
} else {
// 展开时如果历史数据有此item,则str不再记录此item
str = str.filter(x => x._cfg.id !== item._cfg.id);
}
const marker = item.get('group').find(ele => ele.get('name') === 'collapse-icon');
const icon = value ? G6.Marker.expand : G6.Marker.collapse;
marker.attr('symbol', icon);
}
},
节点折叠状态的触发函数,str储存着被折叠的节点
// 历史折叠节点初始化
graph.getNodes().forEach((e) => {
for (let i = 0; i < str.length; i += 1) {
if (e._cfg.id === str[i]?._cfg.id) {
e.getModel().collapsed = !e.getModel().collapsed;
graph.setItemState(e, 'collapsed', e.getModel().collapsed);
graph.layout();
}
}
});
完整代码:
// 渲染树
renderTree = () => {
// 复制/新增/修改/删除 弹出框
const that = this;
const contextMenu = new G6.Menu({
getContent() {
const outDiv = document.createElement('div');
outDiv.style.width = '100px';
outDiv.style.textAlign = 'center';
outDiv.innerHTML = `<div>
<li><a code="copy">复制文本</a></li>
<li><a code="add">新增节点</a></li>
<li><a code="fix">修改节点</a></li>
<li><a code="delete">删除节点</a></li>
<textarea id="store">这是幕后黑手</textarea>
</div>`;
return outDiv;
},
handleMenuClick: (target, item) => {
const code = target.getAttribute('code');
// 复制
if (code === 'copy') {
const store = document.getElementById('store');
store.value = item._cfg.id;
store.select();
document.execCommand('copy');
}
// 新增
if (code === 'add') {
// 初始化为空
this.props.form.setFieldsValue({
EnumerateCode: '',
EnumerateValue: '',
});
this.setState({ visible: true, isAdd: true, modelTitle: '新增' });
this.props.form.setFieldsValue({
EnumerateType: getQuery('EnumerateType'),
EnumerateTypeName: getQuery('EnumerateTypeName'),
Up_EnumerateCode: item._cfg.model.EnumerateLevel === 0
? 0 : item._cfg.model.EnumerateCode,
EnumerateLevel: item._cfg.model.EnumerateLevel + 1,
});
}
// 删除
if (code === 'delete') {
confirm({
title: '请确认是否删除该节点',
okText: '确定',
okType: 'danger',
cancelText: '取消',
onOk() {
that.deleteNode(item._cfg.model.EnumerateCode);
},
onCancel() {
},
});
}
// 修改
if (code === 'fix') {
this.setState({ visible: true, modelTitle: '修改', oldCode: item._cfg.model.EnumerateCode, isAdd: false });
const obj = {
EnumerateType: getQuery('EnumerateType'),
EnumerateTypeName: getQuery('EnumerateTypeName'),
// 修改节点的Up_EnumerateCode为它自身的Up_EnumerateCode
Up_EnumerateCode: item._cfg.model.Up_EnumerateCode,
EnumerateLevel: item._cfg.model.EnumerateLevel,
EnumerateCode: item._cfg.model.EnumerateCode,
EnumerateValue: item._cfg.model.EnumerateValue,
};
// 保存原始编辑数据
this.setState({ oldEditValue: obj });
this.props.form.setFieldsValue({
...obj,
});
}
},
// offsetX and offsetY include the padding of the parent container
// 需要加上父级容器的 padding-left 16 与自身偏移量 10
offsetX: 16 + 10,
// 需要加上父级容器的 padding-top 24 、画布兄弟元素高度、与自身偏移量 10
offsetY: 0,
// the types of items that allow the menu show up
// 在哪些类型的元素上响应
itemTypes: ['node'],
});
// 自定义节点
G6.registerNode('card-node', {
draw: function drawShape(cfg, group) {
const r = 2;
const color = '#5B8FF9';
const w = cfg.size[0];
const h = cfg.size[1];
const shape = group.addShape('rect', {
attrs: {
x: -w / 2,
y: -h / 2,
width: w, // 200,
height: h, // 60
stroke: color,
radius: r,
fill: '#fff',
},
name: 'main-box',
draggable: true,
});
// 节点的内容
group.addShape('text', {
attrs: {
textAlign: 'center',
textBaseline: 'middle',
x: 0,
y: 0,
lineHeight: 20,
text: cfg.id,
fill: '#666',
},
name: 'title',
});
if (cfg.children && group) {
group.addShape('marker', {
attrs: {
x: w / 2,
y: 0,
r: 6,
cursor: 'pointer',
symbol: cfg.collapsed ? G6.Marker.expand : G6.Marker.collapse,
stroke: '#666',
lineWidth: 1,
fill: '#fff',
},
name: 'collapse-icon',
});
}
return shape;
},
setState(name, value, item) {
let flag = false; // 用来判断是否是历史数据用来初始化的,如果是历史数据就不push进数组
if (name === 'collapsed') {
if (value === true) {
str.forEach((x) => {
if (x._cfg.id === item._cfg.id) {
flag = true;
}
});
if (!flag) {
str.push(item);
flag = false;
}
} else {
// 展开时如果历史数据有此item,则str不再记录此item
str = str.filter(x => x._cfg.id !== item._cfg.id);
}
const marker = item.get('group').find(ele => ele.get('name') === 'collapse-icon');
const icon = value ? G6.Marker.expand : G6.Marker.collapse;
marker.attr('symbol', icon);
}
},
});
const container = this.mountNode.current;
const width = container.scrollWidth;
const height = container.scrollHeight || 500;
const minimap = new G6.Minimap({
size: [150, 100],
});
const graph = new G6.TreeGraph({
plugins: [contextMenu, minimap],
container: this.mountNode.current,
width,
height,
modes: {
default: ['drag-canvas', 'zoom-canvas'],
},
defaultNode: {
type: 'card-node',
size: [200, 40],
},
defaultEdge: {
type: 'cubic-horizontal',
style: {
endArrow: true,
},
},
layout: {
type: 'indented',
direction: 'LR',
dropCap: false,
indent: 250,
getHeight: () => {
return 60;
},
},
});
this.state.contorlGraph = graph;
// 渲染
graph.data(this.state.treeData);
graph.render();
// 节点定位
graph.focusItem(this.state.id, true, {
easing: 'easeLinear',
duration: 500,
});
// 历史折叠节点初始化
graph.getNodes().forEach((e) => {
for (let i = 0; i < str.length; i += 1) {
if (e._cfg.id === str[i]?._cfg.id) {
e.getModel().collapsed = !e.getModel().collapsed;
graph.setItemState(e, 'collapsed', e.getModel().collapsed);
graph.layout();
}
}
});
// 事件响应
graph.on('node:click', (e) => {
if (e.target.get('name') === 'collapse-icon') {
e.item.getModel().collapsed = !e.item.getModel().collapsed;
graph.setItemState(e.item, 'collapsed', e.item.getModel().collapsed);
graph.layout();
}
});
}