• Tree树形控件
    • 何时使用
    • 代码演示
    • API
      • Tree props
      • TreeNode props
      • DirectoryTree props
    • 注意
    • FAQ
      • 在 showLine 时,如何隐藏子节点图标?

    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