# Tree

## 何时使用

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

也可以将 `树控件` 作为树形菜单使用。

## 案例演示

### Tree 基本使用

---demo
```js
import { Tree } from 'amos-framework';

const TreeNode = Tree.TreeNode;

const onSelect = (selectedKeys, info) => {
  console.log('selected', selectedKeys, info);
};

const onCheck = (checkedKeys, info) => {
  console.log('onCheck', checkedKeys, info);
};

ReactDOM.render((
  <Tree
    checkable
    defaultExpandedKeys={['0-0-0', '0-0-1']}
    defaultSelectedKeys={['0-0-0', '0-0-1']}
    defaultCheckedKeys={['0-0-0', '0-0-1']}
    onSelect={onSelect}
    onCheck={onCheck}
  >
    <TreeNode title="parent 1" key="0-0">
      <TreeNode title="parent 1-0" key="0-0-0" disabled>
        <TreeNode title="leaf" key="0-0-0-0" disableCheckbox />
        <TreeNode title="leaf" key="0-0-0-1" />
      </TreeNode>
      <TreeNode title="parent 1-1" key="0-0-1">
        <TreeNode title={<span style={{ color: '#1890ff' }}>sss</span>} key="0-0-1-0" />
      </TreeNode>
    </TreeNode>
  </Tree>
), _react_runner_);
```
---demoend

### filterTreeNode 使用

---demo
```js
import { Tree } from 'amos-framework';

const TreeNode = Tree.TreeNode;

const onSelect = (selectedKeys, info) => {
  console.log('selected', selectedKeys, info);
};

const onCheck = (checkedKeys, info) => {
  console.log('onCheck', checkedKeys, info);
};

// 高亮指定的 key, 所有 `-1` 结尾的节点
function filterTreeNode(node){
  return node.props.eventKey.endsWith('-1');
}

ReactDOM.render((
  <Tree
    checkable
    defaultExpandedKeys={['0-0-0', '0-0-1']}
    defaultSelectedKeys={['0-0-0', '0-0-1']}
    defaultCheckedKeys={['0-0-0', '0-0-1']}
    onSelect={onSelect}
    onCheck={onCheck}
    filterTreeNode={filterTreeNode}
  >
    <TreeNode title="parent 1" key="0-0">
      <TreeNode title="parent 1-0" key="0-0-0" disabled>
        <TreeNode title="leaf" key="0-0-0-0" disableCheckbox />
        <TreeNode title="leaf" key="0-0-0-1" />
      </TreeNode>
      <TreeNode title="parent 1-1" key="0-0-1">
        <TreeNode title={<span style={{ color: '#1890ff' }}>sss</span>} key="0-0-1-0" />
      </TreeNode>
    </TreeNode>
  </Tree>
), _react_runner_);
```
---demoend

### Tree 额外参数

点击节点时，需要使用父节点 `key`。直接将父节点的 key 数据作为 `props` 传递给当前的 `TreeNode`

---demo
```js
import { Tree, Row, Col } from 'amos-framework';

const TreeNode = Tree.TreeNode;

const onSelect = (selectedKeys, info) => {
  console.log('selected', selectedKeys, info);
  console.log('selected parentKey:', info.node.props.parentKey);
};

const onCheck = (checkedKeys, info) => {
  console.log('onCheck', checkedKeys, info);
  console.log('onCheck parentKey:', info.node.props.parentKey);
};

const treeData = [{
  title: '0-0',
  key: '0-0',
  children: [{
    title: '0-0-0',
    key: '0-0-0',
    children: [
      { title: '0-0-0-0', key: '0-0-0-0' },
      { title: '0-0-0-1', key: '0-0-0-1' }
    ]
  }, {
    title: '0-0-1',
    key: '0-0-1',
    children: [
      { title: '0-0-1-0', key: '0-0-1-0' }
    ]
  }]
}];

function renderTree(data, parentKey = ''){
  return data.map((item) => {
    if (item.children) {
      return (
        <TreeNode title={item.title} key={item.key} parentKey={parentKey}>
          {renderTree(item.children, item.key)}
        </TreeNode>
      );
    }
    return <TreeNode key={item.key} parentKey={parentKey} {...item} />;
  });
}

const Basic = () => (
  <Tree
    checkable
    defaultExpandedKeys={['0-0-0', '0-0-1']}
    defaultSelectedKeys={['0-0-0', '0-0-1']}
    defaultCheckedKeys={['0-0-0', '0-0-1']}
    onSelect={onSelect}
    onCheck={onCheck}
  >
    <TreeNode title="parent 1" key="0-0">
      <TreeNode title="parent 1-0" key="0-0-0" parentKey="0-0" disabled>
        <TreeNode title="leaf" key="0-0-0-0" parentKey="0-0-0" disableCheckbox />
        <TreeNode title="leaf" key="0-0-0-1" parentKey="0-0-0" />
      </TreeNode>
      <TreeNode title="parent 1-1" key="0-0-1" parentKey="0-0">
        <TreeNode title={<span style={{ color: '#1890ff' }}>sss</span>} key="0-0-1-0" parentKey="0-0-1"/>
      </TreeNode>
    </TreeNode>
  </Tree>
);

const UseData = () => (
  <Tree
    checkable
    defaultExpandedKeys={['0-0-0', '0-0-1']}
    defaultSelectedKeys={['0-0-0', '0-0-1']}
    defaultCheckedKeys={['0-0-0', '0-0-1']}
    onSelect={onSelect}
    onCheck={onCheck}
  >
    {renderTree(treeData)}
  </Tree>
);

ReactDOM.render((
  <div>
    <Row>
      <Col span={6}>TreeNode 使用</Col>
      <Col span={6} style={{ marginLeft: 10 }}>treeData 使用</Col>
    </Row>
    <Row>
      <Col span={6}><Basic /></Col>
      <Col span={6} style={{ marginLeft: 10 }}><UseData /></Col>
    </Row>
  </div>
), _react_runner_);
```
---demoend

### Tree 宽度固定内容很长时使用

---demo
```js
import { Tree, OverFlowText } from 'amos-framework';

const TreeNode = Tree.TreeNode;

const onSelect = (selectedKeys, info) => {
  console.log('selected', selectedKeys, info);
};

const onCheck = (checkedKeys, info) => {
  console.log('onCheck', checkedKeys, info);
};

function renderTitle(text){
  if (text.length > 10){
    return (
      <div style={{ maxWidth: '50px', display: 'inline-block', verticalAlign: 'bottom' }}>
        <OverFlowText><div>{text}</div></OverFlowText>
      </div>
    );
  }
  return text;
}

ReactDOM.render((
  <Tree
    checkable
    defaultExpandedKeys={['0-0-0', '0-0-1']}
    defaultSelectedKeys={['0-0-0', '0-0-1']}
    defaultCheckedKeys={['0-0-0', '0-0-1']}
    onSelect={onSelect}
    onCheck={onCheck}
  >
    <TreeNode title="parent 1" key="0-0">
      <TreeNode title="parent 1-0" key="0-0-0" disabled>
        <TreeNode title={renderTitle('我是一段很长很长的内容contentcccccc')} key="0-0-0-0" disableCheckbox />
        <TreeNode title="leaf" key="0-0-0-1" />
      </TreeNode>
      <TreeNode title="parent 1-1" key="0-0-1">
        <TreeNode title={<span style={{ color: '#1890ff' }}>sss</span>} key="0-0-1-0" />
      </TreeNode>
    </TreeNode>
  </Tree>
), _react_runner_);
```
---demoend

### 基本使用: 受控

---demo
```js
import { Tree } from 'amos-framework';

const TreeNode = Tree.TreeNode;

const treeData = [{
  title: '0-0',
  key: '0-0',
  children: [{
    title: '0-0-0',
    key: '0-0-0',
    children: [
      { title: '0-0-0-0', key: '0-0-0-0' },
      { title: '0-0-0-1', key: '0-0-0-1' },
      { title: '0-0-0-2', key: '0-0-0-2' }
    ]
  }, {
    title: '0-0-1',
    key: '0-0-1',
    children: [
      { title: '0-0-1-0', key: '0-0-1-0' },
      { title: '0-0-1-1', key: '0-0-1-1' },
      { title: '0-0-1-2', key: '0-0-1-2' }
    ]
  }, {
    title: '0-0-2',
    key: '0-0-2'
  }]
}, {
  title: '0-1',
  key: '0-1',
  children: [
    { title: '0-1-0-0', key: '0-1-0-0' },
    { title: '0-1-0-1', key: '0-1-0-1' },
    { title: '0-1-0-2', key: '0-1-0-2' }
  ]
}, {
  title: '0-2',
  key: '0-2'
}];

class Demo extends Component {
  state = {
    expandedKeys: ['0-0-0', '0-0-1'],
    autoExpandParent: true,
    checkedKeys: ['0-0-0'],
    selectedKeys: []
  }
  onExpand = (expandedKeys, ...args) => {
    console.log('onExpand', args);
    // if not set autoExpandParent to false, if children expanded, parent can not collapse.
    // or, you can remove all expanded children keys.
    this.setState({
      expandedKeys,
      autoExpandParent: false
    });
  }
  onCheck = (checkedKeys) => {
    console.log('onCheck', checkedKeys);
    this.setState({ checkedKeys });
  }
  onSelect = (selectedKeys, info) => {
    console.log('onSelect', info);
    this.setState({ selectedKeys });
  }
  renderTreeNodes = (data) => {
    return data.map((item) => {
      if (item.children) {
        return (
          <TreeNode title={item.title} key={item.key} dataRef={item}>
            {this.renderTreeNodes(item.children)}
          </TreeNode>
        );
      }
      return <TreeNode key={item.key} {...item} />;
    });
  }
  render() {
    return (
      <Tree
        checkable
        onExpand={this.onExpand}
        expandedKeys={this.state.expandedKeys}
        autoExpandParent={this.state.autoExpandParent}
        onCheck={this.onCheck}
        checkedKeys={this.state.checkedKeys}
        onSelect={this.onSelect}
        selectedKeys={this.state.selectedKeys}
      >
        {this.renderTreeNodes(treeData)}
      </Tree>
    );
  }
}

ReactDOM.render(<Demo />, _react_runner_);
```
---demoend

### 外部控制选择、展开

---demo
```js
import { Tree, Button } from 'amos-framework';

const TreeNode = Tree.TreeNode;
const BarGroup = Button.BarGroup;

const treeData = [{
  title: '0-0',
  key: '0-0',
  children: [{
    title: '0-0-0',
    key: '0-0-0',
    children: [
      { title: '0-0-0-0', key: '0-0-0-0' },
      { title: '0-0-0-1', key: '0-0-0-1' },
      { title: '0-0-0-2', key: '0-0-0-2' }
    ]
  }, {
    title: '0-0-1',
    key: '0-0-1',
    children: [
      { title: '0-0-1-0', key: '0-0-1-0' },
      { title: '0-0-1-1', key: '0-0-1-1' },
      { title: '0-0-1-2', key: '0-0-1-2' }
    ]
  }, {
    title: '0-0-2',
    key: '0-0-2'
  }]
}, {
  title: '0-1',
  key: '0-1',
  children: [
    { title: '0-1-0-0', key: '0-1-0-0' },
    { title: '0-1-0-1', key: '0-1-0-1' },
    { title: '0-1-0-2', key: '0-1-0-2' }
  ]
}, {
  title: '0-2',
  key: '0-2'
}];

function getTreeAllKey(datas = [], keys = []){
  for (let i = 0, len = datas.length; i < len; i++) {
    const data = datas[i];
    if (data){
      if (data.key){
        keys.push(data.key);
      }
      if (data.children){
        getTreeAllKey(data.children, keys);
      }
    }
  }
  return keys;
}

class Demo extends Component {
  state = {
    expandedKeys: ['0-0-0', '0-0-1'],
    checkedKeys: ['0-0-0'],
    selectedKeys: []
  }
  onExpand = (expandedKeys, ...args) => {
    this.setState({
      expandedKeys
    });
  }
  onCheck = (checkedKeys) => {
    this.setState({ checkedKeys });
  }
  onSelect = (selectedKeys, info) => {
    console.log('onSelect', info);
    this.setState({ selectedKeys });
  }

  handleTree = (type) => {
    switch (type) {
      case 'all':
        this.setState({
          checkedKeys: getTreeAllKey(treeData)
        });
        break;
      case 'none':
        this.setState({
          checkedKeys: []
        });
        break;
      case 'expand':
        this.setState({
          expandedKeys: getTreeAllKey(treeData)
        });
        break;
      case 'unexpand':
        this.setState({
          expandedKeys: []
        });
        break;

      default:
        break;
    }
  }

  renderTreeNodes = (data) => {
    return data.map((item) => {
      if (item.children) {
        return (
          <TreeNode title={item.title} key={item.key} dataRef={item}>
            {this.renderTreeNodes(item.children)}
          </TreeNode>
        );
      }
      return <TreeNode key={item.key} {...item} />;
    });
  }

  render() {
    const { expandedKeys, checkedKeys } = this.state;
    return (
      <div>
        <div>
          <div>已勾选{checkedKeys.length}项</div>
          <BarGroup label="控制树" mode="none">
            <Button onClick={e => this.handleTree('all')}>全选</Button>
            <Button onClick={e => this.handleTree('none')}>反选</Button>
            <Button onClick={e => this.handleTree('expand')}>展开</Button>
            <Button onClick={e => this.handleTree('unexpand')}>合并</Button>
          </BarGroup>
        </div>
        <Tree
          checkable
          onExpand={this.onExpand}
          expandedKeys={expandedKeys}
          onCheck={this.onCheck}
          checkedKeys={checkedKeys}
        >
          {this.renderTreeNodes(treeData)}
        </Tree>
      </div>
    );
  }
}

ReactDOM.render(<Demo />, _react_runner_);
```
---demoend

### 自定义Icon

icon 支持传入 `ReactNode` 和 `() => ReactNode` 两种方式，可以在 `Tree 和 TreeNode` 中设置，优先使用 `TreeNode` 中的 `icon`

---demo
```js
import { Tree, Icon, Row, Col } from 'amos-framework';

const TreeNode = Tree.TreeNode;

const TNIcon = () => (
<Tree showIcon defaultExpandAll defaultSelectedKeys={['0-0-0']}>
  <TreeNode icon={<Icon icon="folder" />} title="parent 1" key="0-0">
    <TreeNode icon={<Icon icon="file-leaf" />} title="leaf" key="0-0-0" />
    <TreeNode icon={({ selected }) => <Icon icon={selected ? 'meho' : 'frown'} />} title="leaf" key="0-0-1" />
  </TreeNode>
</Tree>);

function getIcon(props){
  const { title, isLeaf, expanded } = props;
  if (title === 'leaf' || isLeaf) {
    const icon = props.eventKey === '0-0-1' ? 'meho' : 'file';
    return <Icon icon={icon} />;
  }
  return <Icon icon={expanded ? 'folder-open' : 'folder'} />;
}

const TIcon = () => (
<Tree showIcon defaultExpandAll defaultSelectedKeys={['0-0-0']} icon={getIcon}>
  <TreeNode title="parent 1" key="0-0">
    <TreeNode title="leaf" key="0-0-0" isLeaf />
    <TreeNode title="leaf" key="0-0-1" isLeaf />
    <TreeNode title="leaf" key="0-0-2" icon={({ selected }) => <Icon icon={selected ? 'meho' : 'frown'} />} />
  </TreeNode>
</Tree>);

const dataSource = [
  { key: 'p1', title: 'p1', img: 'contacts', children: [
    { key: 'p1-1', title: 'p1-1', isLeaf: true },
    { key: 'p1-2', title: 'p1-2', isLeaf: true },
  ] },
    { key: 'p2', title: 'p2', children: [
    { key: 'p2-1', title: 'p2-1', isLeaf: true },
    { key: 'p2-2', title: 'p2-2', isLeaf: true },
  ] }
];

ReactDOM.render((
  <div>
    <Row>
      <Col span={6}>TreeNode Icon 使用</Col>
      <Col span={6} style={{ marginLeft: 10 }}>Tree Icon 使用</Col>
      <Col span={6} style={{ marginLeft: 10 }}>dataSource 使用</Col>
    </Row>
    <Row>
      <Col span={6}><TNIcon /></Col>
      <Col span={6} style={{ marginLeft: 10 }}><TIcon /></Col>
      <Col span={6} style={{ marginLeft: 10 }}>
        <Tree
          showIcon
          dataSource={dataSource}
          icon={({ img, isLeaf, expanded }) => {
            if (img){
              return <Icon icon={img} />;
            }
            if (isLeaf) {
              return <Icon icon="file" />;
            }
            return <Icon icon={expanded ? 'folder-open' : 'folder'} />;
          }}
        />
      </Col>
    </Row>
  </div>
), _react_runner_);
```
---demoend

### 可拖拽

---demo
```js
import { Tree } from 'amos-framework';

const TreeNode = Tree.TreeNode;

const x = 3;
const y = 2;
const z = 1;
const gData = [];

const generateData = (_level, _preKey, _tns) => {
  const preKey = _preKey || '0';
  const tns = _tns || gData;

  const children = [];
  for (let i = 0; i < x; i++) {
    const key = `${preKey}-${i}`;
    tns.push({ title: key, key });
    if (i < y) {
      children.push(key);
    }
  }
  if (_level < 0) {
    return tns;
  }
  const level = _level - 1;
  children.forEach((key, index) => {
    tns[index].children = [];
    return generateData(level, key, tns[index].children);
  });
};

generateData(z);

class Demo extends Component {
  state = {
    gData,
    expandedKeys: ['0-0', '0-0-0', '0-0-0-0']
  };
  onDragEnter = info => {
    console.log(info);
    // expandedKeys 需要受控时设置
    // this.setState({
    //   expandedKeys: info.expandedKeys,
    // });
  };
  onDrop = info => {
    console.log(info);
    const dropKey = info.node.props.eventKey;
    const dragKey = info.dragNode.props.eventKey;
    const dropPos = info.node.props.pos.split('-');
    const dropPosition = info.dropPosition - Number(dropPos[dropPos.length - 1]);
    // const dragNodesKeys = info.dragNodesKeys;
    const loop = (data, key, callback) => {
      data.forEach((item, index, arr) => {
        if (item.key === key) {
          return callback(item, index, arr);
        }
        if (item.children) {
          return loop(item.children, key, callback);
        }
      });
    };
    const data = [...this.state.gData];
    let dragObj;
    loop(data, dragKey, (item, index, arr) => {
      arr.splice(index, 1);
      dragObj = item;
    });
    if (info.dropToGap) {
      let ar;
      let i;
      loop(data, dropKey, (item, index, arr) => {
        ar = arr;
        i = index;
      });
      if (dropPosition === -1) {
        ar.splice(i, 0, dragObj);
      } else {
        ar.splice(i + 1, 0, dragObj);
      }
    } else {
      loop(data, dropKey, item => {
        item.children = item.children || [];
        // where to insert 示例添加到尾部，可以是随意位置
        item.children.push(dragObj);
      });
    }
    this.setState({
      gData: data
    });
  };
  render() {
    const loop = data =>
      data.map(item => {
        if (item.children && item.children.length) {
          return (
            <TreeNode key={item.key} title={item.key}>
              {loop(item.children)}
            </TreeNode>
          );
        }
        return <TreeNode key={item.key} title={item.key} />;
      });
    return (
      <Tree className="draggable-tree" defaultExpandedKeys={this.state.expandedKeys} draggable onDragEnter={this.onDragEnter} onDrop={this.onDrop}>
        {loop(this.state.gData)}
      </Tree>
    );
  }
}

ReactDOM.render(<Demo />, _react_runner_);
```
---demoend

### 异步加载数据

---demo
```js
import { Tree } from 'amos-framework';

const TreeNode = Tree.TreeNode;

class Demo extends Component {
  state = {
    treeData: [
      { title: 'Expand to load', key: '0' },
      { title: 'Expand to load', key: '1' },
      { title: 'Tree Node', key: '2', isLeaf: true }
    ]
  }
  onLoadData = (treeNode) => {
    return new Promise((resolve) => {
      if (treeNode.props.children) {
        resolve();
        return;
      }
      setTimeout(() => {
        treeNode.props.dataRef.children = [
          { title: 'Child Node', key: `${treeNode.props.eventKey}-0` },
          { title: 'Child Node', key: `${treeNode.props.eventKey}-1` }
        ];
        this.setState({
          treeData: [...this.state.treeData]
        });
        resolve();
      }, 1000);
    });
  }
  renderTreeNodes = (data) => {
    return data.map((item) => {
      if (item.children) {
        return (
          <TreeNode title={item.title} key={item.key} dataRef={item}>
            {this.renderTreeNodes(item.children)}
          </TreeNode>
        );
      }
      return <TreeNode {...item} key={item.key} dataRef={item} />;
    });
  }
  render() {
    return (
      <Tree loadData={this.onLoadData}>
        {this.renderTreeNodes(this.state.treeData)}
      </Tree>
    );
  }
}

ReactDOM.render(<Demo />, _react_runner_);
```
---demoend

### Tree 连接线

---demo
```js
import { Tree } from 'amos-framework';

const TreeNode = Tree.TreeNode;

const onSelect = (selectedKeys, info) => {
  console.log('selected', selectedKeys, info);
};

ReactDOM.render((
  <Tree showLine defaultExpandedKeys={['0-0-0']} onSelect={onSelect}>
    <TreeNode title="parent 1" key="0-0">
      <TreeNode title="parent 1-0" key="0-0-0">
        <TreeNode title="leaf" key="0-0-0-0" />
        <TreeNode title="leaf" key="0-0-0-1" />
        <TreeNode title="leaf" key="0-0-0-2" />
      </TreeNode>
      <TreeNode title="parent 1-1" key="0-0-1">
        <TreeNode title="leaf" key="0-0-1-0" />
      </TreeNode>
      <TreeNode title="parent 1-2" key="0-0-2">
        <TreeNode title="leaf" key="0-0-2-0" />
        <TreeNode title="leaf" key="0-0-2-1" />
      </TreeNode>
    </TreeNode>
  </Tree>
), _react_runner_);
```
---demoend

### Tree 显示虚线连接线

采用虚线的方式，连接所有的树节点，注意：仅从一个根节点进行连接。如果最外层节点有多个，则会进行各自连接。

---demo
```js
import { Tree } from 'amos-framework';

const TreeNode = Tree.TreeNode;

const onSelect = (selectedKeys, info) => {
  console.log('selected', selectedKeys, info);
};

ReactDOM.render((
  <Tree showDashLine defaultExpandedKeys={['0-0-0']} onSelect={onSelect}>
    <TreeNode title="parent 1" key="0-0">
      <TreeNode title="parent 1-0" key="0-0-0">
        <TreeNode title="leaf" key="0-0-0-0" />
        <TreeNode title="leaf" key="0-0-0-1" />
        <TreeNode title="leaf" key="0-0-0-2" />
      </TreeNode>
      <TreeNode title="parent 1-1" key="0-0-1">
        <TreeNode title="leaf" key="0-0-1-0" />
      </TreeNode>
      <TreeNode title="parent 1-2" key="0-0-2">
        <TreeNode title="leaf" key="0-0-2-0" />
        <TreeNode title="leaf" key="0-0-2-1" />
      </TreeNode>
    </TreeNode>
  </Tree>
), _react_runner_);
```
---demoend

### 可搜索

---demo
```js
import { Tree, Search } from 'amos-framework';

const TreeNode = Tree.TreeNode;

const x = 3;
const y = 2;
const z = 1;
const gData = [];

const generateData = (_level, _preKey, _tns) => {
  const preKey = _preKey || '0';
  const tns = _tns || gData;

  const children = [];
  for (let i = 0; i < x; i++) {
    const key = `${preKey}-${i}`;
    tns.push({ title: key, key });
    if (i < y) {
      children.push(key);
    }
  }
  if (_level < 0) {
    return tns;
  }
  const level = _level - 1;
  children.forEach((key, index) => {
    tns[index].children = [];
    return generateData(level, key, tns[index].children);
  });
};
generateData(z);

const dataList = [];
const generateList = data => {
  for (let i = 0; i < data.length; i++) {
    const node = data[i];
    const key = node.key;
    dataList.push({ key, title: key });
    if (node.children) {
      generateList(node.children, node.key);
    }
  }
};
generateList(gData);

const getParentKey = (key, tree) => {
  let parentKey;
  for (let i = 0; i < tree.length; i++) {
    const node = tree[i];
    if (node.children) {
      if (node.children.some(item => item.key === key)) {
        parentKey = node.key;
      } else if (getParentKey(key, node.children)) {
        parentKey = getParentKey(key, node.children);
      }
    }
  }
  return parentKey;
};

class Demo extends Component {
  state = {
    expandedKeys: [],
    searchValue: '',
    autoExpandParent: true
  };
  onExpand = expandedKeys => {
    this.setState({
      expandedKeys,
      autoExpandParent: false
    });
  };
  onChange = e => {
    const value = e.target.value;
    const expandedKeys = dataList
      .map(item => {
        if (item.key.indexOf(value) > -1) {
          return getParentKey(item.key, gData);
        }
        return null;
      })
      .filter((item, i, self) => item && self.indexOf(item) === i);
    this.setState({
      expandedKeys,
      searchValue: value,
      autoExpandParent: true
    });
  };
  render() {
    const { searchValue, expandedKeys, autoExpandParent } = this.state;
    const loop = data =>
      data.map(item => {
        const index = item.key.indexOf(searchValue);
        const beforeStr = item.key.substr(0, index);
        const afterStr = item.key.substr(index + searchValue.length);
        const title =
          index > -1 ? (
            <span>
              {beforeStr}
              <span style={{ color: '#f50' }}>{searchValue}</span>
              {afterStr}
            </span>
          ) : (
            <span>{item.key}</span>
          );
        if (item.children) {
          return (
            <TreeNode key={item.key} title={title}>
              {loop(item.children)}
            </TreeNode>
          );
        }
        return <TreeNode key={item.key} title={title} />;
      });
    return (
      <div style={{ width: '20em' }}>
        <Search style={{ marginBottom: 8 }} placeholder="Search" useInnerIcon icon="search" onChange={this.onChange} />
        <Tree onExpand={this.onExpand} expandedKeys={expandedKeys} autoExpandParent={autoExpandParent}>
          {loop(gData)}
        </Tree>
      </div>
    );
  }
}

ReactDOM.render(<Demo />, _react_runner_);
```
---demoend

### MenuTree 案例

仿照 `Menu` 方式，全行聚焦，点击父节点可进行展开合并。按住 `Ctrl` 键，可实现多选。

`showLine` 和 `showDashLine` 将会改变 `MenuTree` 的形态

---demo
```js
import { Tree, Row, Col } from 'amos-framework';

const MenuTree = Tree.MenuTree;
const TreeNode = Tree.TreeNode;

const onSelect = (selectedKeys) => {
  console.log('Select:', selectedKeys);
};

const onExpand = (expandedKeys) => {
  console.log('Expand：', expandedKeys);
};

const BasicTree = () =>  (
  <MenuTree multiple defaultExpandAll onSelect={onSelect} onExpand={onExpand}>
    <TreeNode title="parent 0" key="0-0">
      <TreeNode title="leaf 0-0" key="0-0-0" isLeaf />
      <TreeNode title="leaf 0-1" key="0-0-1" isLeaf />
    </TreeNode>
    <TreeNode title="parent 1" key="0-1">
      <TreeNode title="leaf 1-0" key="0-1-0" isLeaf />
      <TreeNode title="leaf 1-1" key="0-1-1" isLeaf />
    </TreeNode>
  </MenuTree>
);

const dataSource = [
  { key: 'p1', title: 'p1', children: [
    { key: 'p1-1', title: 'p1-1', isLeaf: true },
    { key: 'p1-2', title: 'p1-2', isLeaf: true },
  ] },
    { key: 'p2', title: 'p2', children: [
    { key: 'p2-1', title: 'p2-1', isLeaf: true },
    { key: 'p2-2', title: 'p2-2', isLeaf: true },
  ] }
];

const DataTree = () => (
  <MenuTree multiple defaultExpandAll onSelect={onSelect} onExpand={onExpand} dataSource={dataSource} />
);

const DataTree2 = () => (
  <MenuTree
    defaultExpandAll
    onSelect={onSelect}
    onExpand={onExpand}
    dataSource={dataSource}
    hideSwitcher
    tight
  />
);

const DataTree3 = () => (
  <MenuTree
    multiple
    showDashLine
    defaultExpandAll
    onSelect={onSelect}
    onExpand={onExpand}
    dataSource={dataSource}
  />
);

ReactDOM.render((
  <div>
    <Row>
      <Col span={5}>默认使用</Col>
      <Col span={5} style={{ marginLeft: 10 }}>使用 dataSource 创建</Col>
      <Col span={5} style={{ marginLeft: 10 }}>紧促模式</Col>
      <Col span={5} style={{ marginLeft: 10 }}>显示switcher</Col>
    </Row>
    <Row>
      <Col span={5}><BasicTree /></Col>
      <Col span={5} style={{ marginLeft: 10 }}><DataTree /></Col>
      <Col span={5} style={{ marginLeft: 10 }}><DataTree2 /></Col>
      <Col span={5} style={{ marginLeft: 10 }}><DataTree3 /></Col>
    </Row>
  </div>
), _react_runner_);
```
---demoend


### 拖拽限制&创建元素

---demo
```js
import { Tree, Icon } from 'amos-framework';

const TreeNode = Tree.TreeNode;

const dataSource = [
  {
    key: 'b1',
    title: '矩形',
    children: [
      { key: 'rect', title: '矩形' },
      { key: 'circle', title: '圆形' },
      { key: 'line', title: '直线' },
      { key: 'dash', title: <span>虚线 <Icon icon="meho" style={{ fontSize: 14 }} /></span> },
      { key: 'flow', title: '水流线' }
    ]
  }
];

const styles = {
  wrap: {
    display: 'flex',
    height: '300px'
  },
  drag: {
    background: '#eee'
  },
  drop: {
    width: '300px',
    border: '1px solid #eee',
    overflow: 'auto'
  },
  item: {
    display: 'inline-block',
    border: '1px solid #1839ff',
    margin: '5px',
    padding: '5px',
    borderRadius: '6px'
  }
};

let index = 0;

class Demo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      eles: [],
      selectedKeys: [],
      dragging: false
    };
    this.tar = '';
  }

  onDragStart = ({ event, node }) => {
    console.log(event, node);

    const { eventKey } = node.props;
    if (eventKey){
      this.tar = eventKey;
      this.setState({
        selectedKeys: [eventKey],
        dragging: true
      });
    }
  };

  // 拖拽结束处理，当无 drop 时，该事件可以进行元素恢复
  onDragEnd = ({ event, node }) => {
    this.setState({
      dragging: false
    });
  }

  handleDrop = (e) => {
    if (this.tar){
      index += 1;
      this.setState({
        eles: [
          ...this.state.eles,
          {
            id: index,
            name: `${this.tar}-${index}`
          }
        ],
        dragging: false
      });
    }
  }

  processDataProps(props){
    // 排除 默认组件方式
    if (React.isValidElement(props.title)){
      return props;
    }

    // hasChildren 字段，为注入的字段，也可以自行设置
    if (props.hasChildren){
      return props;
    }

    return {
      ...props,
      title: <span>{props.title} <Icon icon="star-hollow" style={{ fontSize: 14 }} /></span>
    };
  }

  render() {
    const { selectedKeys, dragging } = this.state;
    const dropStyle = dragging ? {
      ...styles.drop,
      border: '1px dashed blue'
    } : styles.drop;
    return (
      <div style={styles.wrap}>
        <div className="drag-container" style={styles.drag}>
          <Tree
            className="draggable-tree2"
            draggable
            defaultExpandAll
            dataSource={dataSource}
            onDragStart={this.onDragStart}
            onDragEnd={this.onDragEnd}
            noDropSelf
            onlyDragLeaf
            tight
            selectedKeys={selectedKeys}
            processDataProps={this.processDataProps}
          />
        </div>
        <div
          className="drop-container"
          style={dropStyle}
          onDragOver={evt => evt.preventDefault()}
          onDrop={this.handleDrop}
        >
          {this.state.eles.map(e => <span style={styles.item} key={e.id}>{e.name}</span>)}
        </div>
      </div>
    );
  }
}

ReactDOM.render(<Demo />, _react_runner_);
```
---demoend

### 节点扩展使用

---demo
```js
import { Tree, Fragment, DomHtml, Checkbox, AmosAlert, Button } from 'amos-framework';

const TreeNode = Tree.TreeNode;
const CheckboxGroup = Checkbox.Group;

const treeData = [
  {
    title: '0-0',
    key: '0-0',
    children: [
      {
        title: '0-0-0',
        key: '0-0-0',
        btns: [
          { title: '新增', key: 'add-000' },
          { title: '编辑', key: 'edit-000' },
          { title: '查看', key: 'info-000' }
        ]
      },
      {
        title: '0-0-1',
        key: '0-0-1',
        children: [
          { title: '0-0-1-0', key: '0-0-1-0' },
          { title: '0-0-1-1', key: '0-0-1-1' },
          {
            title: '0-0-1-2',
            key: '0-0-1-2',
            btns: [
              { title: '新增', key: 'add-0012' },
              { title: '编辑', key: 'edit-0012' },
              { title: '查看', key: 'info-0012' }
            ]
          }
        ]
      },
      {
        title: '0-0-2',
        key: '0-0-2'
      }
    ]
  },
  {
    title: '0-1',
    key: '0-1',
    btns: [
      { title: '新增', key: 'add-01' },
      { title: '编辑', key: 'edit-01' },
      { title: '发布', key: 'pub-01' },
      { title: '删除', key: 'del-01' },
      { title: '筛选', key: 'filter-01' },
      { title: '审核供应商资质', key: 'check-01' },
      { title: '查看', key: 'info-01' }
    ],
    children: [
      { title: '0-1-0-0', key: '0-1-0-0' },
      { title: '0-1-0-1', key: '0-1-0-1' },
      { title: '0-1-0-2', key: '0-1-0-2',
      btns: [...Array(15)].map((item, i) => ({ key: `btn${i}`, title: `按钮${i}` })) }
    ]
  },
  {
    title: '0-2',
    key: '0-2'
  }
];

const maxBtn = 6;

class Demo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      expandedKeys: ['0-0-0', '0-0-1'],
      checkedKeys: ['0-0-0'],
      btnSelects: {}
    }
  }

  onExpand = (expandedKeys, ...args) => {
    this.setState({
      expandedKeys
    });
  }

  onCheck = (checkedKeys) => {
    this.setState({ checkedKeys });
  }

  handleBtnBeforeChange = (evt, value) => {
    evt.stopPropagation();
  }

  handelBtnSelect = (parentKey, selects) => {
    const { btnSelects } = this.state;
    btnSelects[parentKey] = selects;

    this.setState({
      btnSelects
    });
  }

  submit = () => {
    const { checkedKeys, btnSelects } = this.state;
    AmosAlert.success('结果', JSON.stringify({
      checkedKeys,
      btnSelects
    }, null, 2));
  }

  renderChecks = (dataItem) => {
    const { btnSelects, checkedKeys } = this.state;
    const btnDisabled = (checkedKeys || []).includes(dataItem.key) ? false : true;
    const btns = dataItem.btns;
    if (btns){
      if(btns.length <= maxBtn){
        return (
          <span className="tree-node-has-btns">
            <CheckboxGroup
              selects={btnSelects[dataItem.key]}
              onBeforeChange={this.handleBtnBeforeChange}
              onChange={selects => this.handelBtnSelect(dataItem.key, selects)}
            >
              {
                btns.map(btn => <Checkbox key={btn.key} disabled={btnDisabled} value={btn.key} onClick={e => e.stopPropagation()}>{btn.title}</Checkbox>)
              }
            </CheckboxGroup>
          </span>
        );
      } else {
        const left = btns.slice(0, maxBtn);
        const right = btns.slice(maxBtn, btns.length);
        return (
          <span className="tree-node-has-btns">
            <CheckboxGroup
              selects={btnSelects[dataItem.key]}
              onBeforeChange={this.handleBtnBeforeChange}
              onChange={selects => this.handelBtnSelect(dataItem.key, selects)}
            >
              {
                left.map(btn => <Checkbox key={btn.key} disabled={btnDisabled} value={btn.key} onClick={e => e.stopPropagation()}>{btn.title}</Checkbox>)
              }
            </CheckboxGroup>
            <span className="gpt-common-tree-folder-extra">
              <Popover
                disabled={btnDisabled}
                content={(
                  <CheckboxGroup
                    selects={btnSelects[dataItem.key]}
                    onBeforeChange={this.handleBtnBeforeChange}
                    onChange={selects => this.handelBtnSelect(dataItem.key, selects)}
                  >
                    {
                      right.map(btn => <Checkbox key={btn.key} disabled={btnDisabled} value={btn.key} onClick={e => e.stopPropagation()}>{btn.title}</Checkbox>)
                    }
                  </CheckboxGroup>
                )}
                direction="down"
              >
                <Icon icon="ellipsis" color="#fdb90f" />
              </Popover>
            </span>
          </span>
        );
      }
    }
    // 超过7个，采用 更多 展示, 及: 6个 + more
    return null;
  }

  renderBtnNode = (dataItem) => {
    return (
      <span className="my-extra-tree-btn-parent-title">
        <span>{dataItem.title}</span>
        <span className="tree-node-has-btns">
          {this.renderChecks(dataItem)}
        </span>
      </span>
    );
  }

  render() {
    const { expandedKeys, checkedKeys, selectedKeys } = this.state;
    return (
      <Fragment>
        <DomHtml>
          <style type="text/css" data-dhtype="btnsearch">
          {
            `.my-extra-tree.amos-tree li .amos-tree-node-content-wrapper {
                width: calc(100% - 68px);
                height: 30px; /** checkbox height */
                line-height: 30px;
              }

              .my-extra-tree.amos-tree li .amos-tree-node-content-wrapper > .amos-tree-title {
                width: 100%;
              }

              .my-extra-tree-btn-parent-title {
                display: flex;
                align-items: center;
                gap: 16px;
                justify-content: space-between;
                width: 100%;
              }

              .tree-node-has-btns {
                display: flex;
                align-items: center;
                gap: 8px;
                font-weight: bold;
                color: #345fa6;
              }
            `
          }
          </style>
        </DomHtml>
        <Button onClick={this.submit}>提交</Button>
        <Tree
          className="my-extra-tree"
          checkable
          dataSource={treeData}
          onExpand={this.onExpand}
          expandedKeys={expandedKeys}
          onCheck={this.onCheck}
          checkedKeys={checkedKeys}
          processDataProps={(dataItem) => {
            const nodeProps = {
              key: dataItem.key,
              dataRef: dataItem,
              title: dataItem.btns ? this.renderBtnNode(dataItem) : dataItem.title
            };
            if (!dataItem.hasChildren) {
              nodeProps.isLeaf = true;
            }

            return nodeProps;
          }}
        />
      </Fragment>
    );
  }
}

ReactDOM.render(<Demo />, _react_runner_);
```
---demoend

### 动态改变父子节点选择关联状态

可在执行过程中，动态改变父子联动状态。

使用场景：权限控制中，可以只设置菜单权限不设置按钮权限。

---demo
```js
import { Tree, Fragment, Checkbox, AmosAlert, Button } from 'amos-framework';

const TreeNode = Tree.TreeNode;

const treeData = [
  {
    title: '0-0',
    key: '0-0',
    children: [
      {
        title: '0-0-0',
        key: '0-0-0',
        children: [
          { title: '新增', key: 'add' },
          { title: '编辑', key: 'edit' },
          { title: '查看', key: 'info' }
        ]
      },
      {
        title: '0-0-1',
        key: '0-0-1',
        children: [
          { title: '0-0-1-0', key: '0-0-1-0' },
          { title: '0-0-1-1', key: '0-0-1-1' },
          {
            title: '0-0-1-2',
            key: '0-0-1-2',
            children: [
              { title: '新增', key: 'add' },
              { title: '编辑', key: 'edit' },
              { title: '查看', key: 'info' }
            ]
          }
        ]
      },
      {
        title: '0-0-2',
        key: '0-0-2'
      }
    ]
  },
  {
    title: '0-1',
    key: '0-1',
    children: [
      { title: '新增', key: 'add' },
      { title: '编辑', key: 'edit' },
      { title: '发布', key: 'pub' },
      { title: '删除', key: 'del' },
      { title: '筛选', key: 'filter' },
      { title: '审核供应商资质', key: 'check' },
      { title: '查看', key: 'info' }
    ],
    children: [
      { title: '0-1-0-0', key: '0-1-0-0' },
      { title: '0-1-0-1', key: '0-1-0-1' },
      { title: '0-1-0-2', key: '0-1-0-2',
      children: [...Array(15)].map((item, i) => ({ key: `btn${i}`, title: `按钮${i}` })) }
    ]
  },
  {
    title: '0-2',
    key: '0-2'
  }
];

const maxBtn = 6;

class Demo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      expandedKeys: ['0-0-0', '0-0-1'],
      checkedKeys: ['0-0-0'],
      checkLinked: true
    }
  }

  onExpand = (expandedKeys, ...args) => {
    this.setState({
      expandedKeys
    });
  }

  onCheck = (checkedKeys) => {
    this.setState({ checkedKeys });
  }

  submit = () => {
    const { checkedKeys, btnSelects } = this.state;
    AmosAlert.success('结果', JSON.stringify({
      checkedKeys,
      btnSelects
    }, null, 2));
  }

  render() {
    const { expandedKeys, checkedKeys, selectedKeys, checkLinked } = this.state;
    return (
      <Fragment>
        <Button onClick={this.submit}>提交</Button>
        <Checkbox style={{ marginLeft: 16 }} checked={checkLinked} onChange={e => this.setState({ checkLinked: e.target.checked })}>父子联动</Checkbox>
        <Tree
          className="my-extra-tree"
          checkable
          checkStrictly={!checkLinked}
          dataSource={treeData}
          onExpand={this.onExpand}
          expandedKeys={expandedKeys}
          onCheck={this.onCheck}
          checkedKeys={checkedKeys}
          processDataProps={(dataItem) => {
            const nodeProps = {
              key: dataItem.key,
              dataRef: dataItem,
              title: dataItem.title
            };
            if (!dataItem.hasChildren) {
              nodeProps.isLeaf = true;
            }

            return nodeProps;
          }}
        />
      </Fragment>
    );
  }
}

ReactDOM.render(<Demo />, _react_runner_);
```
---demoend

## API

### Tree props

| params  | type | default | description |
| --- | --- | --- | --- |
| autoExpandParent | boolean | true | 是否自动展开父节点 |
| checkable | boolean | false | 节点前添加 Checkbox 复选框 |
| checkedKeys | `string[] 或 {checked: string[], halfChecked: string[]}` | `[]` | （受控）选中复选框的树节点（注意：父子节点有关联，如果传入父节点key，则子节点自动选中；相应当子节点key都传入，父节点也自动选中。当设置`checkable`和`checkStrictly`，它是一个有`checked`和`halfChecked`属性的对象，并且父子节点的选中与否不再关联 |
| checkStrictly | boolean | false | checkable状态下节点选择完全受控（父子节点选中状态不再关联） |
| defaultCheckedKeys | `string[]` | `[]` | 默认选中复选框的树节点 |
| defaultExpandAll | boolean | false | 默认展开所有树节点 |
| defaultExpandedKeys | `string[]` | `[]` | 默认展开指定的树节点 |
| defaultExpandParent | bool | true | 默认展开父节点 |
| defaultSelectedKeys | `string[]` | `[]` | 默认选中的树节点 |
| disabled | bool | false | 将树禁用 |
| draggable | boolean | false | 设置节点可拖拽（IE>8） |
| expandedKeys | `string[]` | `[]` | （受控）展开指定的树节点 |
| filterTreeNode | function(node) | - | 按需筛选树节点（高亮），返回true。会给值为 ture 的节点添加 `tree-node-matched` 样式名 |
| loadData | function(node) | - | 异步加载数据 |
| multiple | boolean | false | 支持点选多个节点（节点本身） |
| selectedKeys | `string[]` | - | （受控）设置选中的树节点 |
| showIcon | boolean | false | 是否展示 TreeNode title 前的图标，没有默认样式，如设置为 true，需要自行定义图标相关样式 |
| showLine | boolean | false | 是否展示连接线 |
| showDashLine | boolean | false | 是否使用虚线连接线。和 showline 只能选其一，如果设置 showline 时，则 showDashLine 不起效 |
| onCheck | `function(checkedKeys, e:{checked: bool, checkedNodes, node, event})` | - | 点击复选框触发 |
| onDragStart | `function({event, node})` | - | 开始拖拽时调用 |
| onDragEnd | `function({event, node})` | - | dragend 触发时调用 |
| onDragEnter | `function({event, node, expandedKeys})` | - | dragenter 触发时调用 |
| onDragLeave | `function({event, node})` | - | dragleave 触发时调用 |
| onDragOver | `function({event, node})` | - | dragover 触发时调用 |
| onDrop | `function({event, node, dragNode, dragNodesKeys})` | - | drop 触发时调用 |
| onExpand | `function(expandedKeys, {expanded: bool, node})` | - | 展开/收起节点时触发 |
| onRightClick | `function({event, node})` | - | 响应右键点击 |
| onSelect | `function(selectedKeys, e:{selected: bool, selectedNodes, node, event})` | - | 点击树节点触发 |
| tight | boolean | - | 紧促模式 |
| hideSwitcher | boolean | - | 隐藏父节点的 `switcher`，注意，如果非 `MenuTree` 模式下隐藏时，则无法进行树的展开合并，只能通过外部事件控制 |
| noDropSelf | boolean | - | 不能拖拽放在自身上 |
| onlyDragLeaf | boolean | - | 只能拖拽叶子结点。用户也可以在 `onDragStart` 中进行拖拽节点配置 |
| dataSource | `Array or Object` | - | 数据源，用于渲染树 |
| processDataProps | `func: props => props` | - | 处理 `dataSource` 中生成 `Tree` 时的 `props（具体为 data item 中排除 children 属性）`, 返回值为 `TreeNode props`，如果有 children 节点，则添加 `hasChildren=true` |

> 注意，回调事件中，参数 `node` 中，如果需要获取当前数据的 key 值，可以采用 `node.eventKey` or `node.props.key or node.props.eventkey` 等方式来获取。
>
> 如果使用 `dataSource` 创建树 [案例](#自定义icon)，如需自定义 icon ，此时需要传入 `Icon` 组件或者采用 `img` 字段进行标识，然后再传入 `icon` props 进行动态创建。其中组件 `Icon` 支持图片路径。
> 或者采用 `processDataProps` props 将 `dataSource` 中传入的 `String icon` 转化为 `Icon` 组件也可。
> 在 `processDataProps(props)` 方法体中，可以通过 `props.hasChildren` 来判叶子结点。

设置 `checkStrictly=true` 时，onCheck 回调参数为 `(checkedKeys: {checked: string[], halfChecked: string[]}, evtObj)` 此时，evtObj 会新增 tmpHalfCheckedKeys 参数，用于存储子节点选中时，父节点半选key，部分场景下，选择子节点需要同步选择父节点，此时可以用该参数进行处理。


### TreeNode props

| params  | type | default | description |
| --- | --- | --- | --- |
| disableCheckbox | boolean | false | 禁掉 checkbox |
| disabled | boolean | false | 禁掉响应 |
| icon | `ReactNode/Function(props):ReactNode` | - | 自定义图标。可接收组件，参数 props 为当前节点 props |
| isLeaf | boolean | false | 设置为叶子节点(设置了`loadData`时有效) |
| key | string | 内部计算出的节点位置 | 被树的 (default)ExpandedKeys / (default)CheckedKeys / (default)SelectedKeys 属性所用。注意：整个树范围内的所有节点的 key 值不能重复！ |
| selectable | boolean | true | 设置节点是否可被选中 |
| title | `string或ReactNode` | '---' | 标题 |

如果点击节点时，需要获取到父节点的 `key`，可以自行给 `TreeNode` 中设置相应的 `props`，用于标识 父节点 `key`, 如：

```js
// 通过事件 `onSelect(selectedKeys, info){}` 的第二个参数获取 `info.node.props.parentKey`
<Tree
  checkable
  defaultExpandedKeys={['0-0-0', '0-0-1']}
  defaultSelectedKeys={['0-0-0', '0-0-1']}
  defaultCheckedKeys={['0-0-0', '0-0-1']}
  onSelect={onSelect}
  onCheck={onCheck}
>
  <TreeNode title="parent 1" key="0-0">
    <TreeNode title="parent 1-0" key="0-0-0" disabled>
      <TreeNode title="leaf" key="0-0-0-0" disableCheckbox parentKey="0-0-0" />
      <TreeNode title="leaf" key="0-0-0-1" parentKey="0-0-0" />
    </TreeNode>
    <TreeNode title="parent 1-1" key="0-0-1">
      <TreeNode title={<span style={{ color: '#1890ff' }}>sss</span>} key="0-0-1-0" />
    </TreeNode>
  </TreeNode>
</Tree>
// 如果传入的是 treeData 数据，自行给数据中添加类似 `parentKey` 属性即可。
```

### MenuTree 新增 props

| params  | type | default | description |
| --- | --- | --- | --- |
| expandAction | `click、doubleClick（dbclick）、null、false` | click | 展开合并触发方式，默认为 click，即：单击树节点时，进行展开合并操作 |

使用 MenuTree 时，如果需要屏蔽点击事件切换展开合并，此时可以设置 `expandAction={false}` or `expandAction={null}`

## 注意

* 大量树节点时，在设置`checkable`，当你异步加载树节点时，需要这样渲染树：

```js
{this.state.treeData.length
  ? <Tree>{this.state.treeData.map(data => <TreeNode />)}</Tree>
  : 'loading tree'}
```

* 树节点的展开合并以及 加载等icon，`unicode` 字符为以下字符，默认添加在在内置icon中， 如果自行下载或者设置 `iconfont` 时，请确保不要占用固定`unicode`字符。

```scss
$tree-default-open-icon: '\e900';
$tree-showline-open-icon: '\e905';
$tree-showline-close-icon: '\e904';
$tree-doc-icon: '\e902';
$tree-loading-icon: '\e903';
```
