Tree树形控件

何时使用

文件夹、组织架构、生物分类、国家地区等等,世间万物的大多数结构都是树形结构。使用树控件可以完整展现其中的层级关系,并具有展开收起选择等交互功能。

代码演示

Tree 树形控件 - 图1

基本

最简单的用法,展示可勾选,可选中,禁用,默认展开等功能。

  1. import { Tree } from 'antd';
  2. const { TreeNode } = Tree;
  3. class Demo extends React.Component {
  4. onSelect = (selectedKeys, info) => {
  5. console.log('selected', selectedKeys, info);
  6. };
  7. onCheck = (checkedKeys, info) => {
  8. console.log('onCheck', checkedKeys, info);
  9. };
  10. render() {
  11. return (
  12. <Tree
  13. checkable
  14. defaultExpandedKeys={['0-0-0', '0-0-1']}
  15. defaultSelectedKeys={['0-0-0', '0-0-1']}
  16. defaultCheckedKeys={['0-0-0', '0-0-1']}
  17. onSelect={this.onSelect}
  18. onCheck={this.onCheck}
  19. >
  20. <TreeNode title="parent 1" key="0-0">
  21. <TreeNode title="parent 1-0" key="0-0-0" disabled>
  22. <TreeNode title="leaf" key="0-0-0-0" disableCheckbox />
  23. <TreeNode title="leaf" key="0-0-0-1" />
  24. </TreeNode>
  25. <TreeNode title="parent 1-1" key="0-0-1">
  26. <TreeNode title={<span style={{ color: '#1890ff' }}>sss</span>} key="0-0-1-0" />
  27. </TreeNode>
  28. </TreeNode>
  29. </Tree>
  30. );
  31. }
  32. }
  33. ReactDOM.render(<Demo />, mountNode);

Tree 树形控件 - 图2

拖动示例

将节点拖拽到其他节点内部或前后。

  1. import { Tree } from 'antd';
  2. const { TreeNode } = Tree;
  3. const x = 3;
  4. const y = 2;
  5. const z = 1;
  6. const gData = [];
  7. const generateData = (_level, _preKey, _tns) => {
  8. const preKey = _preKey || '0';
  9. const tns = _tns || gData;
  10. const children = [];
  11. for (let i = 0; i < x; i++) {
  12. const key = `${preKey}-${i}`;
  13. tns.push({ title: key, key });
  14. if (i < y) {
  15. children.push(key);
  16. }
  17. }
  18. if (_level < 0) {
  19. return tns;
  20. }
  21. const level = _level - 1;
  22. children.forEach((key, index) => {
  23. tns[index].children = [];
  24. return generateData(level, key, tns[index].children);
  25. });
  26. };
  27. generateData(z);
  28. class Demo extends React.Component {
  29. state = {
  30. gData,
  31. expandedKeys: ['0-0', '0-0-0', '0-0-0-0'],
  32. };
  33. onDragEnter = info => {
  34. console.log(info);
  35. // expandedKeys 需要受控时设置
  36. // this.setState({
  37. // expandedKeys: info.expandedKeys,
  38. // });
  39. };
  40. onDrop = info => {
  41. console.log(info);
  42. const dropKey = info.node.props.eventKey;
  43. const dragKey = info.dragNode.props.eventKey;
  44. const dropPos = info.node.props.pos.split('-');
  45. const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1]);
  46. const loop = (data, key, callback) => {
  47. data.forEach((item, index, arr) => {
  48. if (item.key === key) {
  49. return callback(item, index, arr);
  50. }
  51. if (item.children) {
  52. return loop(item.children, key, callback);
  53. }
  54. });
  55. };
  56. const data = [...this.state.gData];
  57. // Find dragObject
  58. let dragObj;
  59. loop(data, dragKey, (item, index, arr) => {
  60. arr.splice(index, 1);
  61. dragObj = item;
  62. });
  63. if (!info.dropToGap) {
  64. // Drop on the content
  65. loop(data, dropKey, item => {
  66. item.children = item.children || [];
  67. // where to insert 示例添加到尾部,可以是随意位置
  68. item.children.push(dragObj);
  69. });
  70. } else if (
  71. (info.node.props.children || []).length > 0 && // Has children
  72. info.node.props.expanded && // Is expanded
  73. dropPosition === 1 // On the bottom gap
  74. ) {
  75. loop(data, dropKey, item => {
  76. item.children = item.children || [];
  77. // where to insert 示例添加到尾部,可以是随意位置
  78. item.children.unshift(dragObj);
  79. });
  80. } else {
  81. let ar;
  82. let i;
  83. loop(data, dropKey, (item, index, arr) => {
  84. ar = arr;
  85. i = index;
  86. });
  87. if (dropPosition === -1) {
  88. ar.splice(i, 0, dragObj);
  89. } else {
  90. ar.splice(i + 1, 0, dragObj);
  91. }
  92. }
  93. this.setState({
  94. gData: data,
  95. });
  96. };
  97. render() {
  98. const loop = data =>
  99. data.map(item => {
  100. if (item.children && item.children.length) {
  101. return (
  102. <TreeNode key={item.key} title={item.title}>
  103. {loop(item.children)}
  104. </TreeNode>
  105. );
  106. }
  107. return <TreeNode key={item.key} title={item.title} />;
  108. });
  109. return (
  110. <Tree
  111. className="draggable-tree"
  112. defaultExpandedKeys={this.state.expandedKeys}
  113. draggable
  114. blockNode
  115. onDragEnter={this.onDragEnter}
  116. onDrop={this.onDrop}
  117. >
  118. {loop(this.state.gData)}
  119. </Tree>
  120. );
  121. }
  122. }
  123. ReactDOM.render(<Demo />, mountNode);

Tree 树形控件 - 图3

可搜索

可搜索的树。

  1. import { Tree, Input } from 'antd';
  2. const { TreeNode } = Tree;
  3. const Search = Input.Search;
  4. const x = 3;
  5. const y = 2;
  6. const z = 1;
  7. const gData = [];
  8. const generateData = (_level, _preKey, _tns) => {
  9. const preKey = _preKey || '0';
  10. const tns = _tns || gData;
  11. const children = [];
  12. for (let i = 0; i < x; i++) {
  13. const key = `${preKey}-${i}`;
  14. tns.push({ title: key, key });
  15. if (i < y) {
  16. children.push(key);
  17. }
  18. }
  19. if (_level < 0) {
  20. return tns;
  21. }
  22. const level = _level - 1;
  23. children.forEach((key, index) => {
  24. tns[index].children = [];
  25. return generateData(level, key, tns[index].children);
  26. });
  27. };
  28. generateData(z);
  29. const dataList = [];
  30. const generateList = data => {
  31. for (let i = 0; i < data.length; i++) {
  32. const node = data[i];
  33. const key = node.key;
  34. dataList.push({ key, title: key });
  35. if (node.children) {
  36. generateList(node.children);
  37. }
  38. }
  39. };
  40. generateList(gData);
  41. const getParentKey = (key, tree) => {
  42. let parentKey;
  43. for (let i = 0; i < tree.length; i++) {
  44. const node = tree[i];
  45. if (node.children) {
  46. if (node.children.some(item => item.key === key)) {
  47. parentKey = node.key;
  48. } else if (getParentKey(key, node.children)) {
  49. parentKey = getParentKey(key, node.children);
  50. }
  51. }
  52. }
  53. return parentKey;
  54. };
  55. class SearchTree extends React.Component {
  56. state = {
  57. expandedKeys: [],
  58. searchValue: '',
  59. autoExpandParent: true,
  60. };
  61. onExpand = expandedKeys => {
  62. this.setState({
  63. expandedKeys,
  64. autoExpandParent: false,
  65. });
  66. };
  67. onChange = e => {
  68. const value = e.target.value;
  69. const expandedKeys = dataList
  70. .map(item => {
  71. if (item.title.indexOf(value) > -1) {
  72. return getParentKey(item.key, gData);
  73. }
  74. return null;
  75. })
  76. .filter((item, i, self) => item && self.indexOf(item) === i);
  77. this.setState({
  78. expandedKeys,
  79. searchValue: value,
  80. autoExpandParent: true,
  81. });
  82. };
  83. render() {
  84. const { searchValue, expandedKeys, autoExpandParent } = this.state;
  85. const loop = data =>
  86. data.map(item => {
  87. const index = item.title.indexOf(searchValue);
  88. const beforeStr = item.title.substr(0, index);
  89. const afterStr = item.title.substr(index + searchValue.length);
  90. const title =
  91. index > -1 ? (
  92. <span>
  93. {beforeStr}
  94. <span style={{ color: '#f50' }}>{searchValue}</span>
  95. {afterStr}
  96. </span>
  97. ) : (
  98. <span>{item.title}</span>
  99. );
  100. if (item.children) {
  101. return (
  102. <TreeNode key={item.key} title={title}>
  103. {loop(item.children)}
  104. </TreeNode>
  105. );
  106. }
  107. return <TreeNode key={item.key} title={title} />;
  108. });
  109. return (
  110. <div>
  111. <Search style={{ marginBottom: 8 }} placeholder="Search" onChange={this.onChange} />
  112. <Tree
  113. onExpand={this.onExpand}
  114. expandedKeys={expandedKeys}
  115. autoExpandParent={autoExpandParent}
  116. >
  117. {loop(gData)}
  118. </Tree>
  119. </div>
  120. );
  121. }
  122. }
  123. ReactDOM.render(<SearchTree />, mountNode);

Tree 树形控件 - 图4

自定义图标

可以针对不同的节点定制图标。

  1. import { Tree, Icon } from 'antd';
  2. const { TreeNode } = Tree;
  3. ReactDOM.render(
  4. <Tree
  5. showIcon
  6. defaultExpandAll
  7. defaultSelectedKeys={['0-0-0']}
  8. switcherIcon={<Icon type="down" />}
  9. >
  10. <TreeNode icon={<Icon type="smile-o" />} title="parent 1" key="0-0">
  11. <TreeNode icon={<Icon type="meh-o" />} title="leaf" key="0-0-0" />
  12. <TreeNode
  13. icon={({ selected }) => <Icon type={selected ? 'frown' : 'frown-o'} />}
  14. title="leaf"
  15. key="0-0-1"
  16. />
  17. </TreeNode>
  18. </Tree>,
  19. mountNode,
  20. );

Tree 树形控件 - 图5

受控操作示例

受控操作示例

  1. import { Tree } from 'antd';
  2. const { TreeNode } = Tree;
  3. const treeData = [
  4. {
  5. title: '0-0',
  6. key: '0-0',
  7. children: [
  8. {
  9. title: '0-0-0',
  10. key: '0-0-0',
  11. children: [
  12. { title: '0-0-0-0', key: '0-0-0-0' },
  13. { title: '0-0-0-1', key: '0-0-0-1' },
  14. { title: '0-0-0-2', key: '0-0-0-2' },
  15. ],
  16. },
  17. {
  18. title: '0-0-1',
  19. key: '0-0-1',
  20. children: [
  21. { title: '0-0-1-0', key: '0-0-1-0' },
  22. { title: '0-0-1-1', key: '0-0-1-1' },
  23. { title: '0-0-1-2', key: '0-0-1-2' },
  24. ],
  25. },
  26. {
  27. title: '0-0-2',
  28. key: '0-0-2',
  29. },
  30. ],
  31. },
  32. {
  33. title: '0-1',
  34. key: '0-1',
  35. children: [
  36. { title: '0-1-0-0', key: '0-1-0-0' },
  37. { title: '0-1-0-1', key: '0-1-0-1' },
  38. { title: '0-1-0-2', key: '0-1-0-2' },
  39. ],
  40. },
  41. {
  42. title: '0-2',
  43. key: '0-2',
  44. },
  45. ];
  46. class Demo extends React.Component {
  47. state = {
  48. expandedKeys: ['0-0-0', '0-0-1'],
  49. autoExpandParent: true,
  50. checkedKeys: ['0-0-0'],
  51. selectedKeys: [],
  52. };
  53. onExpand = expandedKeys => {
  54. console.log('onExpand', expandedKeys);
  55. // if not set autoExpandParent to false, if children expanded, parent can not collapse.
  56. // or, you can remove all expanded children keys.
  57. this.setState({
  58. expandedKeys,
  59. autoExpandParent: false,
  60. });
  61. };
  62. onCheck = checkedKeys => {
  63. console.log('onCheck', checkedKeys);
  64. this.setState({ checkedKeys });
  65. };
  66. onSelect = (selectedKeys, info) => {
  67. console.log('onSelect', info);
  68. this.setState({ selectedKeys });
  69. };
  70. renderTreeNodes = data =>
  71. data.map(item => {
  72. if (item.children) {
  73. return (
  74. <TreeNode title={item.title} key={item.key} dataRef={item}>
  75. {this.renderTreeNodes(item.children)}
  76. </TreeNode>
  77. );
  78. }
  79. return <TreeNode {...item} />;
  80. });
  81. render() {
  82. return (
  83. <Tree
  84. checkable
  85. onExpand={this.onExpand}
  86. expandedKeys={this.state.expandedKeys}
  87. autoExpandParent={this.state.autoExpandParent}
  88. onCheck={this.onCheck}
  89. checkedKeys={this.state.checkedKeys}
  90. onSelect={this.onSelect}
  91. selectedKeys={this.state.selectedKeys}
  92. >
  93. {this.renderTreeNodes(treeData)}
  94. </Tree>
  95. );
  96. }
  97. }
  98. ReactDOM.render(<Demo />, mountNode);

Tree 树形控件 - 图6

异步数据加载

点击展开节点,动态加载数据。

  1. import { Tree } from 'antd';
  2. const { TreeNode } = Tree;
  3. class Demo extends React.Component {
  4. state = {
  5. treeData: [
  6. { title: 'Expand to load', key: '0' },
  7. { title: 'Expand to load', key: '1' },
  8. { title: 'Tree Node', key: '2', isLeaf: true },
  9. ],
  10. };
  11. onLoadData = treeNode =>
  12. new Promise(resolve => {
  13. if (treeNode.props.children) {
  14. resolve();
  15. return;
  16. }
  17. setTimeout(() => {
  18. treeNode.props.dataRef.children = [
  19. { title: 'Child Node', key: `${treeNode.props.eventKey}-0` },
  20. { title: 'Child Node', key: `${treeNode.props.eventKey}-1` },
  21. ];
  22. this.setState({
  23. treeData: [...this.state.treeData],
  24. });
  25. resolve();
  26. }, 1000);
  27. });
  28. renderTreeNodes = data =>
  29. data.map(item => {
  30. if (item.children) {
  31. return (
  32. <TreeNode title={item.title} key={item.key} dataRef={item}>
  33. {this.renderTreeNodes(item.children)}
  34. </TreeNode>
  35. );
  36. }
  37. return <TreeNode {...item} dataRef={item} />;
  38. });
  39. render() {
  40. return <Tree loadData={this.onLoadData}>{this.renderTreeNodes(this.state.treeData)}</Tree>;
  41. }
  42. }
  43. ReactDOM.render(<Demo />, mountNode);

Tree 树形控件 - 图7

连接线

带连接线的树。

  1. import { Tree } from 'antd';
  2. const { TreeNode } = Tree;
  3. class Demo extends React.Component {
  4. onSelect = (selectedKeys, info) => {
  5. console.log('selected', selectedKeys, info);
  6. };
  7. render() {
  8. return (
  9. <Tree showLine defaultExpandedKeys={['0-0-0']} onSelect={this.onSelect}>
  10. <TreeNode title="parent 1" key="0-0">
  11. <TreeNode title="parent 1-0" key="0-0-0">
  12. <TreeNode title="leaf" key="0-0-0-0" />
  13. <TreeNode title="leaf" key="0-0-0-1" />
  14. <TreeNode title="leaf" key="0-0-0-2" />
  15. </TreeNode>
  16. <TreeNode title="parent 1-1" key="0-0-1">
  17. <TreeNode title="leaf" key="0-0-1-0" />
  18. </TreeNode>
  19. <TreeNode title="parent 1-2" key="0-0-2">
  20. <TreeNode title="leaf" key="0-0-2-0" />
  21. <TreeNode title="leaf" key="0-0-2-1" />
  22. </TreeNode>
  23. </TreeNode>
  24. </Tree>
  25. );
  26. }
  27. }
  28. ReactDOM.render(<Demo />, mountNode);

Tree 树形控件 - 图8

目录

内置的目录树,multiple 模式支持 ctrl(Windows) / command(Mac) 复选。

  1. import { Tree } from 'antd';
  2. const DirectoryTree = Tree.DirectoryTree;
  3. const { TreeNode } = Tree;
  4. class Demo extends React.Component {
  5. onSelect = (keys, event) => {
  6. console.log('Trigger Select', keys, event);
  7. };
  8. onExpand = () => {
  9. console.log('Trigger Expand');
  10. };
  11. render() {
  12. return (
  13. <DirectoryTree multiple defaultExpandAll onSelect={this.onSelect} onExpand={this.onExpand}>
  14. <TreeNode title="parent 0" key="0-0">
  15. <TreeNode title="leaf 0-0" key="0-0-0" isLeaf />
  16. <TreeNode title="leaf 0-1" key="0-0-1" isLeaf />
  17. </TreeNode>
  18. <TreeNode title="parent 1" key="0-1">
  19. <TreeNode title="leaf 1-0" key="0-1-0" isLeaf />
  20. <TreeNode title="leaf 1-1" key="0-1-1" isLeaf />
  21. </TreeNode>
  22. </DirectoryTree>
  23. );
  24. }
  25. }
  26. ReactDOM.render(<Demo />, mountNode);

API

Tree props

参数说明类型默认值
autoExpandParent是否自动展开父节点booleantrue
blockNode是否节点占据一行booleanfalse
checkable节点前添加 Checkbox 复选框booleanfalse
checkedKeys(受控)选中复选框的树节点(注意:父子节点有关联,如果传入父节点 key,则子节点自动选中;相应当子节点 key 都传入,父节点也自动选中。当设置checkablecheckStrictly,它是一个有checkedhalfChecked属性的对象,并且父子节点的选中与否不再关联string[] | {checked: string[], halfChecked: string[]}[]
checkStrictlycheckable 状态下节点选择完全受控(父子节点选中状态不再关联)booleanfalse
defaultCheckedKeys默认选中复选框的树节点string[][]
defaultExpandAll默认展开所有树节点booleanfalse
defaultExpandedKeys默认展开指定的树节点string[][]
defaultExpandParent默认展开父节点booltrue
defaultSelectedKeys默认选中的树节点string[][]
disabled将树禁用boolfalse
draggable设置节点可拖拽(IE>8)booleanfalse
expandedKeys(受控)展开指定的树节点string[][]
filterTreeNode按需筛选树节点(高亮),返回 truefunction(node)-
loadData异步加载数据function(node)-
loadedKeys(受控)已经加载的节点,需要配合 loadData 使用string[][]
multiple支持点选多个节点(节点本身)booleanfalse
selectedKeys(受控)设置选中的树节点string[]-
showIcon是否展示 TreeNode title 前的图标,没有默认样式,如设置为 true,需要自行定义图标相关样式booleanfalse
switcherIcon自定义树节点的展开/折叠图标React.ReactElement-
showLine是否展示连接线booleanfalse
onCheck点击复选框触发function(checkedKeys, e:{checked: bool, checkedNodes, node, event})-
onDragEnddragend 触发时调用function({event, node})-
onDragEnterdragenter 触发时调用function({event, node, expandedKeys})-
onDragLeavedragleave 触发时调用function({event, node})-
onDragOverdragover 触发时调用function({event, node})-
onDragStart开始拖拽时调用function({event, node})-
onDropdrop 触发时调用function({event, node, dragNode, dragNodesKeys})-
onExpand展开/收起节点时触发function(expandedKeys, {expanded: bool, node})-
onLoad节点加载完毕时触发function(loadedKeys, {event, node})-
onRightClick响应右键点击function({event, node})-
onSelect点击树节点触发function(selectedKeys, e:{selected: bool, selectedNodes, node, event})-

TreeNode props

参数说明类型默认值版本
checkable当树为 checkable 时,设置独立节点是否展示 Checkboxboolean-3.17.0
disableCheckbox禁掉 checkboxbooleanfalse
disabled禁掉响应booleanfalse
icon自定义图标。可接收组件,props 为当前节点 propsReactNode/Function(props):ReactNode-
isLeaf设置为叶子节点(设置了loadData时有效)booleanfalse
key被树的 (default)ExpandedKeys / (default)CheckedKeys / (default)SelectedKeys 属性所用。注意:整个树范围内的所有节点的 key 值不能重复!string内部计算出的节点位置
selectable设置节点是否可被选中booleantrue
title标题string|ReactNode'—-'

DirectoryTree props

参数说明类型默认值
expandAction目录展开逻辑,可选 false 'click' 'doubleClick'stringclick

注意

3.4.0 之前:树节点可以有很多,但在设置checkable时,将会花费更多的计算时间,因此我们缓存了一些计算结果(this.treeNodesStates)来复用,避免多次重复计算,以此提高性能。但这也带来了一些限制,当你异步加载树节点时,你需要这样渲染树:

  1. {
  2. this.state.treeData.length ? (
  3. <Tree>
  4. {this.state.treeData.map(data => (
  5. <TreeNode />
  6. ))}
  7. </Tree>
  8. ) : (
  9. 'loading tree'
  10. );
  11. }

FAQ

在 showLine 时,如何隐藏子节点图标?

文件图标通过 switcherIcon 来实现,如果不需要你可以覆盖对应的样式:https://codesandbox.io/s/883vo47xp8