Skip to content

Commit 6772580

Browse files
author
liyuan-meng
committed
✨[Tree]支持动态加载
1 parent ae8d741 commit 6772580

6 files changed

Lines changed: 207 additions & 21 deletions

File tree

src/components/tree/demos/basicSelector.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,6 @@ class TreeDemo extends React.Component {
253253
supportMenu={this.state.supportMenu}
254254
supportSearch={this.state.supportSearch}
255255
isAddFront={this.state.isAddFront}
256-
supportCheckbox={true}
257256
onAddNode={this.addNode}
258257
onDoubleClick={this.onDoubleClick}
259258
onRenameNode={this.renameNode}
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
---
2+
order: 1
3+
title: 基础用法
4+
desc: 支持搜索节点、右键菜单、最大层级为4、节点名称最大长度20、可搜索关键字长度最大为20个字符、新增节点加到当前节点的子节点末尾,展开全部节点
5+
---
6+
7+
```jsx
8+
9+
/**
10+
* title: 基础用法
11+
* desc: 支持搜索节点、右键菜单、最大层级为4、节点名称最大长度20、可搜索关键字长度最大为20个字符、新增节点加到当前节点的子节点末尾,展开全部节点
12+
*/
13+
import React from 'react';
14+
import { Tree, Modal } from 'cloud-react';
15+
16+
const treeData = [
17+
{
18+
id: 1,
19+
name: '所有基础',
20+
pId: null,
21+
disableRemove: true,
22+
disableRename: true,
23+
children: [
24+
{
25+
id: 11,
26+
name: '禁止删除节点',
27+
pId: 1,
28+
disableRemove: true,
29+
children: [],
30+
},
31+
{
32+
id: 12,
33+
name: '禁止新增节点',
34+
pId: 1,
35+
disableAdd: true,
36+
children: [],
37+
},
38+
{
39+
id: 13,
40+
name: '禁止重命名节点',
41+
pId: 1,
42+
disableRename: true,
43+
children: [],
44+
},
45+
{
46+
id: 14,
47+
name: '未分类',
48+
pId: 1,
49+
disableRemove: true,
50+
disableAdd: true,
51+
disableRename: true,
52+
isLeaf: true,
53+
}
54+
]
55+
}
56+
];
57+
58+
class TreeDemo extends React.Component {
59+
constructor(props) {
60+
super(props);
61+
this.state = { selectedValue: [] };
62+
}
63+
64+
selectedNode = (node, nodeList) => {
65+
console.log(node, nodeList);
66+
this.setState({ selectedValue: nodeList });
67+
}
68+
69+
render() {
70+
return (
71+
<Tree
72+
selectedValue={this.state.selectedValue}
73+
onSelectedNode={this.selectedNode}
74+
isDynamicLoad
75+
supportCheckbox
76+
supportSearch
77+
treeData={treeData}
78+
searchPlaceholder="动态加载节点"
79+
onLoadData={(data, node) => {
80+
return new Promise(resolve => {
81+
setTimeout(() => {
82+
node.children = [
83+
{
84+
id: `${node.id}1`,
85+
name: '动态加载一个',
86+
// title: <span style={{ color: 'red' }}>动态加载一个</span>,
87+
pId: node.id,
88+
level: node.level + 1,
89+
children: [],
90+
checked: node.checked,
91+
},
92+
{
93+
id: `${node.id}2`,
94+
name: '动态加载叶子节点',
95+
pId: node.id,
96+
isLeaf: true, // 无叶子节点需设置该属性
97+
level: node.level + 1,
98+
children: [],
99+
checked: node.checked, // 父节点选中,子节点也选中
100+
}
101+
];
102+
resolve(data)
103+
}, 2 * 1000);
104+
})
105+
}}
106+
/>
107+
);
108+
}
109+
}
110+
111+
export default TreeDemo;
112+
```

src/components/tree/index.js

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,8 @@ class Tree extends Component {
7373
onDragMoving: noop,
7474
onDragBefore: noop,
7575
onDragAfter: noop,
76+
customNodeTpl: noop,
77+
onLoadData: noop,
7678
};
7779

7880
static propsTypes = {
@@ -109,6 +111,8 @@ class Tree extends Component {
109111
onDragMoving: PropTypes.func,
110112
onDragBefore: PropTypes.func,
111113
onDragAfter: PropTypes.func,
114+
customNodeTpl: PropTypes.func,
115+
onLoadData: PropTypes.func,
112116
};
113117

114118
constructor(props) {
@@ -245,7 +249,7 @@ class Tree extends Component {
245249
treeData: [...backTree],
246250
maxLevel: null,
247251
selectedValue: [...allSelectedLowest],
248-
isUnfold,
252+
isUnfold: searchText ? true : undefined,
249253
disabled,
250254
}),
251255
preSelectedList: allSelectedLowest,
@@ -344,10 +348,41 @@ class Tree extends Component {
344348
* @param node
345349
*/
346350
onFoldNodeAction = (data, node) => {
347-
const backData = store.onFoldNode(data, node);
348-
this.setState({
349-
treeData: [...backData],
350-
});
351+
if (this.props.isDynamicLoad && !node?.children?.length) {
352+
node.isLoading = true;
353+
this.setState({
354+
treeData: [...this.state.treeData],
355+
}, () => {
356+
this.props.onLoadData(data, node).then(res => {
357+
node.isLoading = false
358+
const backData = store.onFoldNode(res, node);
359+
360+
// 如果点击的节点被选中,则其子节点也被选中,更新选中节点状态
361+
if (this.props.supportCheckbox && node.checked) {
362+
const selectedResult = store.getSelectedLowestNodeList(
363+
this.state.selectedValue,
364+
this.getSelectedMoreList(backData, node),
365+
node,
366+
).filter(item => item.id !== node.id);
367+
this.props.onSelectedNode(node, selectedResult);
368+
}
369+
370+
// 更新 allTreeData(用作筛选)
371+
const currentNode = store.findNodeById(this.state.allTreeData, node.id);
372+
Object.assign(currentNode, node);
373+
374+
this.setState({
375+
allTreeData: this.state.allTreeData,
376+
treeData: [...backData],
377+
});
378+
})
379+
});
380+
} else {
381+
const backData = store.onFoldNode(data, node);
382+
this.setState({
383+
treeData: [...backData],
384+
});
385+
}
351386
};
352387

353388
/**
@@ -679,6 +714,7 @@ class Tree extends Component {
679714
onDragMoving,
680715
onDragAfter,
681716
customNodeTpl,
717+
isDynamicLoad,
682718
} = this.props;
683719

684720
const {
@@ -732,6 +768,7 @@ class Tree extends Component {
732768
customNodeTpl,
733769
removeNode,
734770
addNode,
771+
isDynamicLoad,
735772
}}
736773
>
737774
<div className={`${selector} ${className}`} style={style}>

src/components/tree/index.less

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,33 @@
134134
}
135135
}
136136

137+
&.support-checkbox {
138+
.loading-spin {
139+
top: 0;
140+
}
141+
}
142+
143+
.loading-spin {
144+
position: relative;
145+
top: 3px;
146+
display: inline-block;
147+
width: 12px;
148+
height: 12px;
149+
margin: 0 9px;
150+
border-radius: 50%;
151+
animation: load 1.5s infinite linear;
152+
border: 1px solid @blue-4;
153+
border-right: 1px solid transparent;
154+
@keyframes load {
155+
from {
156+
transform: rotate(0deg);
157+
}
158+
to {
159+
transform: rotate(360deg);
160+
}
161+
}
162+
}
163+
137164
.toggle-icon {
138165
cursor: pointer;
139166
font-size: 16px;

src/components/tree/index.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,8 @@ group:
5252
| onDragAfter | 拖拽后调用 | Function | - |
5353
| supportTooltip | 是否启用tooltip,为true时将根据文本长度自动增加tooltip。如数据超大,建议设置为false以节省性能 | Boolean | true |
5454
| customNodeTpl | 自定义节点内容 | Function | - |
55+
| onLoadData | 动态加载数据 | Function | - |
56+
| isDynamicLoad | 动态加载数据 | bool | false |
5557

5658
### 数据属性
5759

@@ -183,3 +185,6 @@ group:
183185

184186
### 自定义节点
185187
<embed src="@components/tree/demos/custom-node.md" />
188+
189+
### 动态加载节点
190+
<embed src="@components/tree/demos/dynamicLoad.md" />

src/components/tree/node.js

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,7 @@ class Node extends Component {
172172
removeNode,
173173
addNode,
174174
customNodeTpl,
175+
isDynamicLoad,
175176
} = this.context;
176177
const { setInputValue, onSaveClick, onClickCancel } = this;
177178
// 将三个方法传递出去可以供外部调用
@@ -184,7 +185,7 @@ class Node extends Component {
184185
<div
185186
className={classNames(
186187
`${prefixCls}-list-node-area ${
187-
data.children && !data.children.length ? 'child-style' : null
188+
(data.children && !data.children.length && (isDynamicLoad && data.isLeaf || !isDynamicLoad)) ? 'child-style' : null
188189
}`,
189190
)}
190191
>
@@ -204,8 +205,9 @@ class Node extends Component {
204205

205206
{/* 折叠展开icon */}
206207
<ToggleFold
207-
hasChildren={data?.children.length > 0}
208-
showChildrenItem={data.isUnfold}
208+
isLoading={data.isLoading}
209+
hasChildren={data?.children.length > 0 || isDynamicLoad && !data.isLeaf}
210+
showChildrenItem={data.isUnfold && data?.children.length}
209211
toggle={(e) => this.toggle(e, data)}
210212
/>
211213
<div
@@ -288,19 +290,23 @@ class Node extends Component {
288290
* @returns {null|*}
289291
* @constructor
290292
*/
291-
function ToggleFold({ hasChildren, showChildrenItem, toggle }) {
293+
function ToggleFold({ hasChildren, showChildrenItem, toggle, isLoading }) {
292294
return (
293-
hasChildren && (
294-
<Icon
295-
className="toggle-icon"
296-
// type={!showChildrenItem ? 'down' : 'up'}
297-
type="down-solid"
298-
style={{
299-
transform: showChildrenItem ? 'rotate(0)' : 'rotate(-90deg)',
300-
}}
301-
onClick={toggle}
302-
/>
303-
)
295+
<>
296+
{isLoading && <span className="loading-spin" />}
297+
{hasChildren && (
298+
<Icon
299+
className="toggle-icon"
300+
type="down-solid"
301+
style={{
302+
zIndex: isLoading ? -100 : undefined,
303+
position: isLoading ? 'absolute' : undefined,
304+
transform: showChildrenItem ? 'rotate(0)' : 'rotate(-90deg)',
305+
}}
306+
onClick={toggle}
307+
/>
308+
)}
309+
</>
304310
);
305311
}
306312

0 commit comments

Comments
 (0)