# MTree 树

- 来源：[E10 UI 组件库](https://www.e-cology.com.cn/ui/mobile/tree/freepass)
- 平台：移动端
- 维护者：王斌
- Issue：http://gitlab.ecology10.cn/FRONTEND/weapp-ui/-/issues?state=all&search=[MTree]

## 代码演示

### 基础用法

直接引入组件进行使用

```jsx
import React from 'react';
import { MTree } from '@weapp/ui';
import { ITreeOncheckHelpInfo, TreeRootIdArray } from '@weapp/ui';

const treeData = [{
  id: 1,
  content: '根节点',
  title:'这是根节点',
  isLeaf: false,
  children: [{
    id: 2,
    content: '一级-1',
    isLeaf:false,
    children: [{
      isLeaf: true,
      id: 3,
      content: '二级-1'
    }, {
      isLeaf: true,
      id: 4,
      content: '二级-2'
    }, {
      isLeaf: true,
      id: 5,
      content: '二级-3'
    }]
  }, {
    isLeaf: true,
    id: 6,
    content: '一级-2'
  }]
}];

export default class TreeDemo extends React.Component<any,{checkedKeys: TreeRootIdArray}> {
  constructor(props: any) {
    super(props);
    this.state = {
      checkedKeys: [],
    }
  }

  onTreeCheck = (v: TreeRootIdArray,b: ITreeOncheckHelpInfo, config:object) => {
    console.log('onCheck:', v, b, config)
    this.setState({checkedKeys: v})
  }

  render() {
    return(
      <div>
        <h2>Tree 树形控件</h2>
        <h5>树形控件，展示文件结构、组织架构、地理信息等分层数据的控件。</h5>
        <MTree
         
          data={treeData}
          checkable
          checkedKeys={this.state.checkedKeys}
          onCheck={this.onTreeCheck}
        />
      </div>
    );
  }
}
```

### 自定义图标展示

使用renderIcon渲染图标icon

```jsx
import React from 'react';
import { Icon, ITreeData, MTree } from '@weapp/ui';
import { ITreeOncheckHelpInfo, TreeRootIdArray } from '@weapp/ui';

const treeData = [{
  id: 1,
  content: '根节点',
  title:'这是根节点',
  isLeaf: false,
  children: [{
    id: 2,
    content: '一级-1',
    isLeaf:false,
    children: [{
      isLeaf: true,
      id: 3,
      content: '二级-1'
    }, {
      isLeaf: true,
      id: 4,
      content: '二级-2'
    }, {
      isLeaf: true,
      id: 5,
      content: '二级-3'
    }]
  }, {
    isLeaf: true,
    id: 6,
    content: '一级-2'
  }]
}];

export default class TreeDemo extends React.Component<any,{checkedKeys: TreeRootIdArray}> {
  constructor(props: any) {
    super(props);
    this.state = {
      checkedKeys: [],
    }
  }

  onTreeCheck = (v: TreeRootIdArray,b: ITreeOncheckHelpInfo, config:object) => {
    console.log('onCheck:', v, b, config)
    this.setState({checkedKeys: v})
  }
  renderIcon = (root: ITreeData, isParent: boolean, isExpanded: boolean) => {
    console.log(root, isParent, isExpanded)
    return <Icon name={isParent ? 'Icon-gateway-o' : 'Icon-customer-o'}  />
  }

  render() {
    return(
      <div>
        <h2>Tree 树形控件</h2>
        <h5>树形控件，展示文件结构、组织架构、地理信息等分层数据的控件。</h5>
        <MTree
         
          data={treeData}
          checkable
          checkedKeys={this.state.checkedKeys}
          onCheck={this.onTreeCheck}
          renderIcon={this.renderIcon}
        />
      </div>
    );
  }
}
```

### 展开受控

使用expandedKeys,onExpand属性控制是否展开

```jsx
import React from 'react'
import { ITreeData, MTree, TreeRootIdArray } from '@weapp/ui'

const treeData = [
  {
    id: 1,
    content: '根节点',
    title: '这是根节点',
    isLeaf: false,
    children: [
      {
        id: 2,
        content: '一级-1',
        isLeaf: false,
        children: [
          {
            isLeaf: false,
            id: 3,
            content: '二级-1',
            children: [
              {
                isLeaf: true,
                id: 31,
                content: '三级-11',
              },
              {
                isLeaf: true,
                id: 32,
                content: '三级-22',
              },
              {
                isLeaf: true,
                id: 33,
                content: '三级-33',
              },
            ],
          },
          {
            isLeaf: true,
            id: 4,
            content: '二级-2',
          },
          {
            isLeaf: true,
            id: 5,
            content: '二级-3',
          },
        ],
      },
      {
        isLeaf: true,
        id: 6,
        content: '一级-2',
      },
    ],
  },
]

export default class BasicDemo extends React.Component {
  state = {
    selectedKeys: [],
    expandedKeys: [],
  }
  onSelect = (data: ITreeData, target: HTMLSpanElement) => {
    this.setState({
      selectedKeys: [data.id],
    })
  }

  onExpand = (v: TreeRootIdArray, root: ITreeData, config: { isExpanded: boolean }) => {
    console.log('onExpand:', v, root, config.isExpanded)
    this.setState({ expandedKeys: v })
  }

  render() {
    return (
      <MTree
       
        data={treeData}
        onSelect={this.onSelect}
        selectedKeys={this.state.selectedKeys}
        expandedKeys={this.state.expandedKeys}
        onExpand={this.onExpand}
      />
    )
  }
}
```

### 选中受控

使用checkedKeys,checkedKeys属性控制选中状态

```jsx
import React from 'react'
import { MTree } from '@weapp/ui'
import { ITreeOncheckHelpInfo, TreeRootIdArray } from '@weapp/ui'

const treeData = [
  {
    id: 1,
    content: '根节点',
    title: '这是根节点',
    isLeaf: false,
    children: [
      {
        id: 2,
        content: '一级-1',
        isLeaf: false,
        children: [
          {
            isLeaf: true,
            id: 3,
            content: '二级-1',
          },
          {
            isLeaf: true,
            id: 4,
            content: '二级-2',
          },
          {
            isLeaf: true,
            id: 5,
            content: '二级-3',
          },
        ],
      },
      {
        isLeaf: true,
        id: 6,
        content: '一级-2',
      },
    ],
  },
]

export default class TreeDemo extends React.Component<
  any,
  { checkedKeys: TreeRootIdArray }
> {
  constructor(props: any) {
    super(props)
    this.state = {
      checkedKeys: [],
    }
  }

  onTreeCheck = (
    v: TreeRootIdArray,
    b: ITreeOncheckHelpInfo,
    config: object
  ) => {
    console.log('onCheck:', v, b, config)
    this.setState({ checkedKeys: v })
  }

  render() {
    return (
      <div>
        <h2>选中受控</h2>
        <MTree
         
          data={treeData}
          checkable
          checkedKeys={this.state.checkedKeys}
          onCheck={this.onTreeCheck}
        />
      </div>
    )
  }
}
```

### 自定义渲染

自定义渲染customRenderNode渲染树节点内容

```jsx
import React from 'react'
import { ITreeData, MTree } from '@weapp/ui'
import { ITreeOncheckHelpInfo, TreeRootIdArray } from '@weapp/ui'

const treeData = [
  {
    id: 1,
    content: '根节点',
    title: '这是根节点',
    isLeaf: false,
    children: [
      {
        id: 2,
        content: '一级-1',
        isLeaf: false,
        children: [
          {
            isLeaf: true,
            id: 3,
            content: '二级-1',
          },
          {
            isLeaf: true,
            id: 4,
            content: '二级-2',
          },
          {
            isLeaf: true,
            id: 5,
            content: '二级-3',
          },
        ],
      },
      {
        isLeaf: true,
        id: 6,
        content: '一级-2',
      },
    ],
  },
]

export default class TreeDemo extends React.Component<
  any,
  { checkedKeys: TreeRootIdArray }
> {
  constructor(props: any) {
    super(props)
    this.state = {
      checkedKeys: [],
    }
  }

  onTreeCheck = (
    v: TreeRootIdArray,
    b: ITreeOncheckHelpInfo,
    config: object
  ) => {
    console.log('onCheck:', v, b, config)
    this.setState({ checkedKeys: v })
  }

  customRender = (data: ITreeData) => <span>{data.content} -自定义内容</span>

  render() {
    return (
      <div>
        <h2>Tree 树形控件</h2>
        <h5>树形控件，展示文件结构、组织架构、地理信息等分层数据的控件。</h5>
        <MTree
         
          data={treeData}
          checkable
          checkedKeys={this.state.checkedKeys}
          onCheck={this.onTreeCheck}
          customRenderNode={this.customRender}
        />
      </div>
    )
  }
}
```

### 搜索

带搜索树形控件

```jsx
import React from 'react'
import { ITreeData, TreeRootIdArray, MTree } from '@weapp/ui'

const treeData = [
  {
    id: 1,
    content: '根节点',
    title: '这是根节点',
    isLeaf: false,
    children: [
      {
        id: 2,
        content: '一级-1',
        isLeaf: false,
        children: [
          {
            isLeaf: false,
            id: 3,
            content: '二级-1a',
            children: [
              {
                isLeaf: true,
                id: 31,
                content: '三级-11abc',
              },
              {
                isLeaf: true,
                id: 32,
                content: '三级-22',
              },
              {
                isLeaf: true,
                id: 33,
                content: '三级-33',
              },
            ],
          },
          {
            isLeaf: true,
            id: 4,
            content: '二级-2A',
          },
          {
            isLeaf: true,
            id: 5,
            content: '二级-3',
          },
        ],
      },
      {
        isLeaf: true,
        id: 6,
        content: '一级-2',
      },
    ],
  },
]

export default class SearchDemo extends React.Component {
  state = {
    searchValue: '',
  }

  onSearch = (
    searchValue: string | number,
    findRoot?: ITreeData[],
    expandedKeys?: TreeRootIdArray
  ) => {
    console.log(searchValue, findRoot, expandedKeys)
    this.setState({ expandedKeys, searchValue })
  }

  render() {
    return (
      <MTree
       
        data={treeData}
        needSearch={true}
        searchLabel='搜索'
        onSearch={this.onSearch}
        searchValue={this.state.searchValue}
        caseSensitive={false}
      />
    )
  }
}
```

### 异步加载

异步加载

```jsx
import React from 'react'
import { ITreeData, MTree, TreeRootIdArray } from '@weapp/ui'

interface DemoState {
  treeData: ITreeData[]
  expandedKeys?: TreeRootIdArray
  checkedKeys?: TreeRootIdArray
  checked?: boolean
  checkPosition?: 'default' | 'right'
}
const fetchData = (data: ITreeData, callback: any) => {
  setTimeout(() => {
    const mockAsyncDats = [
      {
        isLeaf: true,
        id: 13,
        content: '三级-1',
      },
      {
        isLeaf: true,
        id: 14,
        content: '三级-2',
      },
      {
        isLeaf: true,
        id: 15,
        content: '三级-3',
      },
    ]
    callback(mockAsyncDats)
  }, 200)
}

export default class AsyncDemo extends React.Component<{}, DemoState> {
  constructor(props: any) {
    super(props)
    this.state = {
      treeData: [
        {
          id: 1,
          content: '根节点',
          isLeaf: false,
          children: [
            {
              id: 2,
              content: '一级-1',
              isLeaf: false,
              children: [
                {
                  isLeaf: false,
                  id: 3,
                  content: '二级-1',
                },
                {
                  isLeaf: true,
                  id: 4,
                  content: '二级-2',
                },
                {
                  isLeaf: true,
                  id: 5,
                  content: '二级-3',
                },
              ],
            },
            {
              isLeaf: true,
              id: 6,
              content: '一级-2',
            },
          ],
        },
      ],
    }
  }

  loadMore = (data: ITreeData) =>
    new Promise((resolve) => {
      const { treeData } = this.state
      let [...newTreeData] = treeData
      fetchData(data, (resData: ITreeData) => {
        const childrenData = resData.map((item: any) => ({
          ...item,
          parentId: data.id,
        }))

        function getNewTreeData(datas: ITreeData[]) {
          datas.forEach((item) => {
            if (item.id === data.id) {
              item.children = childrenData
            } else if (item.children && item.children.length) {
              getNewTreeData(item.children)
            }
          })
        }

        getNewTreeData(newTreeData)
        this.setState({ treeData: [...newTreeData] })
        resolve(data)
      })
    })

  render() {
    const { treeData } = this.state

    return (
      <MTree
       
        data={treeData}
        isAsync
        loadMore={this.loadMore}
      />
    )
  }
}
```

### 定制化key

定制化key

```jsx
import React from 'react'
import { MTree } from '@weapp/ui'

export default class RenderKeyDemo extends React.Component {
  state = {
    treeData: [
      {
        key: 1,
        name: '根节点',
        isLeaf: false,
        childs: [
          {
            key: 2,
            name: '一级-1',
            isLeaf: false,
            childs: [
              {
                isLeaf: true,
                key: 3,
                name: '二级-1',
              },
              {
                isLeaf: true,
                key: 4,
                name: '二级-2',
              },
              {
                isLeaf: true,
                key: 5,
                name: '二级-3',
              },
            ],
          },
          {
            isLeaf: true,
            key: 6,
            name: '一级-2',
          },
        ],
      },
    ],
    renderKey: {
      dataKey: 'key',
      dataContent: 'name',
      dataChildren: 'childs',
    },
  }
  render() {
    return (
      <MTree
       
        data={this.state.treeData}
        checkable
        renderKey={this.state.renderKey}
      />
    )
  }
}
```

## API

### 参数说明如下：

| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
| --- | --- | --- | --- | --- |
| data | 数据 | ITreeData[] | 是 | - |
| dataType | 数据类型 | `tree` \| `plain` | 否 | `tree` |
| renderKey | 复杂数据类型 数据渲染 key 集合 | Partial | 否 | - |
| checkable | 是否为 checkbox 模式 | boolean | 否 | false |
| expandAll | 是否展开全部节点 | boolean | 否 | - |
| isShowCheckboxByDisabled | 禁用节点时是否显示,默认true | boolean | 否 | boolean |
| expandedKeys | 受控展开节点 | TreeRootIdArray | 否 | - |
| defaultExpandedKeys | 默认展开节点 | TreeRootIdArray | 否 | - |
| defaultExpandedLeavel | 默认展开层级 | number | 否 | - |
| defaultExpandedAutoByChecked | 默认展开已选数据 | boolean | 否 | - |
| checkedKeys | 受控选中节点 | TreeRootIdArray | 否 | - |
| selectedKeys | 受控点击节点 | Array | 否 | [] |
| showAllBranch | 是否包含下级 | boolean | 否 | false |
| disabledCheckedKeys | 默认禁用选中节点 | TreeRootIdArray | 否 | - |
| loadMore | 加载更多 | (data: ITreeData) => Promise | 否 | - |
| isRoot | 判断是否根节点 | (data: ITreeData) => boolean | 否 | - |
| customRenderNode | 节点名称自定义渲染 | (data: ITreeData, isExpanded?: boolean) => ReactNode; | 否 | - |
| operations | 操作 | ITreeOperation[] | 否 | - |
| foldable | 是否可折叠 | boolean | 否 | true |
| onCheck | check 回调 | (selected: TreeRootIdArray, info: ITreeOncheckHelpInfo, config: { isChecked: boolean }) => void | 否 | - |
| size | 大小 | `medium` \| `small` \| `large` | 否 | `medium` |
| style | 行内样式 | CSSProperties | 否 | - |
| searchPlaceholder | 搜索模式下Placeholder | string | 否 | `请输入内容` |
| className | 样式名 | string | 否 | - |
| onExpand | 展开回调 | (expandedKeys: TreeRootIdArray, data: ITreeData, config: { isExpanded: boolean }) => void | 否 | - |
| autoExpandOnSelect | 自动展开选中 | boolean | 否 | true |
| onSelect | select 回调 | (data: ITreeData, target: HTMLSpanElement) => void | 否 | - |
| isComplexDataByType | 是否为复杂数据类型 | boolean | 否 | - |
| isAsync | 是否为异步树形 | boolean | 否 | - |
| checkPosition | 选择框位置 | 'default' \| 'right' | 否 | default |
### 部分ts声明说明

#### ITreeRenderKey

```ts
interface ITreeRenderKey {
  dataKey: string;
  dataTitle: string;
  dataChildren: string;
  dataParentId: string;
  dataDisabled: string,
  dataCheckable: string,
  dataType: string,
  dataContent: string
}
```

#### TreeRootIdArray

```ts
type TreeRootIdArray = TArray<string | number>;
```
