# Table 表格

- 来源：[E10 UI 组件库](https://www.e-cology.com.cn/ui/table/freepass)
- 平台：PC
- 维护者：王斌
- 设计稿：http://pd.e-cology.com.cn/pd/E10/interface/e10xiaoguotu/PC/zms/zujian-list/#s1
- Issue：http://gitlab.ecology10.cn/FRONTEND/weapp-ui/-/issues?state=all&search=[Table]

## 代码演示

### 基础用法

直接引入组件进行使用。表格型列表控件，用于展示多条结构类似的数据，可对数据进行排序、筛选、对比或其他自定义操作。

```jsx
import React from 'react';
import { Table, Avatar, Dialog, ITableColumn, Switch } from '@weapp/ui';
import imgSrc from "../../../../assets/images/ODTLcjxAfvqbxHnVXCYX.png"
const { message } = Dialog;

const columns: ITableColumn[] = [
  {
    title: '姓名',
    dataIndex: 'name',
    isPrimaryKey: true,
    bodyRender: getName,
  }, {
    title: '联系方式',
    dataIndex: 'tel'
  }, {
    title: '部门',
    dataIndex: 'depart',
  },{
    title: '多行文本',
    dataIndex: 'textarea',
  },{
    title: '上级',
    dataIndex: 'leader',
    bodyRender: getLeader
  },{
    title: '入职时间',
    dataIndex: 'join_time',
  }
];

function getName(data: any, pos: any, name: any) {
  const url = imgSrc;
  return (
    <span className={'ui-table-demo-name'}>
      <Avatar size='small' url={url} ></Avatar>
      <a style={{ marginLeft: '10px', color: '#5D9CEC' }}>{data[name]}</a>
    </span>
  )
}

function getLeader(data: any, pos: any, name: any) {
  return <a>{data[name]}</a>
}

const data = [{
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
},{
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
},{
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
},{
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
},{
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
},{
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
},{
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
},{
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
},{
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
},{
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
},{
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}];

export default class BasicDemo extends React.Component<{}, any> {
  state = {
    current: 1,
    pageSize: 5,
    data: data,
    isShowHead: true,
    loading: false,
    isShowIndex: true,
    bordered: false
  };

  onRowClick = (data: any, index: any, event: any) => {
    console.log('onRowClick', data, index, event.target)
  }

  onRowMouseEnter = (a: number, b: object) => {
    console.log('onRowMouseEnter', a, b)
  }

  handleChange = (v: boolean) => {
    this.setState({
      isShowHead: v
    })
  }

  handleLoadingChange = (v: boolean) => {
    this.setState({
      loading: v
    })
  }

  handleIndexChange = (v: boolean) => {
    this.setState({
      isShowIndex: v
    })
  }

  onChange = (value: number, pageSize: number) => {
    this.setState({ current: value, pageSize });
    message({
      type: 'success', // type: 'info', 'error', 'success'
      content: `value: ${value}, pageSize: ${pageSize}`,
    });
  }

  onSizeChange = (value: number, pageSize: number) => {
    this.setState({ current: value, pageSize })
    message({
      type: 'success', // type: 'info', 'error', 'success'
      content: `value: ${value}, pageSize: ${pageSize}`,
    });
  }

  getPageInfo = () => {
    const { current, pageSize, data } = this.state

    return {
      value: current,
      pageSize: pageSize,
      total: data.length,
      showSizeChanger: true,
      showTotal: false,
      showJumper: false,
      pageSizeOptions: [5, 10],
      onChange: this.onChange,
      onSizeChange: this.onSizeChange
    }
  }

  handleBorderedChange = (v: boolean) => {
    this.setState({ bordered: v })
  }

  render() {
    const { data, isShowHead, loading, isShowIndex, bordered } = this.state
    return (
      <div>
        <span style={{ margin: '15px 10px', display: 'inline-block', alignItems: 'center' }}>
          <Switch value={isShowHead} onChange={this.handleChange} />&nbsp;&nbsp;&nbsp;是否显示表头
        </span>
        <span>
          <Switch value={loading} onChange={this.handleLoadingChange} />&nbsp;&nbsp;&nbsp;是否loading
        </span>
        <span style={{ margin: '15px 10px', display: 'inline-block', alignItems: 'center' }}>
          <Switch value={isShowIndex} onChange={this.handleIndexChange} />&nbsp;&nbsp;&nbsp;是否显示序号
        </span>
        <span style={{ margin: '15px 10px', display: 'inline-block', alignItems: 'center' }}>
          <Switch value={bordered} onChange={this.handleBorderedChange} />&nbsp;&nbsp;&nbsp;是否显示边框
        </span>
        <Table
         
          columns={columns}
          data={data}
          pageInfo={this.getPageInfo()}
          onRowClick={this.onRowClick}
          isShowHead={isShowHead}
          loading={loading}
          isShowIndex={isShowIndex}
          bordered={bordered}
        />
      </div>
    )
  }
}
```

### 单选

```jsx
import React from 'react';
import { Avatar, Table } from '@weapp/ui';
import imgSrc from "../../../../assets/images/ODTLcjxAfvqbxHnVXCYX.png"

const columns = [
  {
    title: '姓名',
    dataIndex: 'name',
    bodyRender: getName
  }, {
    title: '联系方式',
    dataIndex: 'tel'
  }, {
    title: '部门',
    dataIndex: 'depart',
  }, {
    title: '多行文本',
    dataIndex: 'textarea',
  }, {
    title: '上级',
    dataIndex: 'leader',
    bodyRender: getLeader
  }, {
    title: '入职时间',
    dataIndex: 'join_time',
  }
];

function getName(data: any, pos: any, name: any) {
  const url = imgSrc;
  return (
    <span className={'ui-table-demo-name'}>
      <Avatar size='small' url={url} ></Avatar>
      <a style={{ marginLeft: '10px', color: '#5D9CEC' }}>{data[name]}</a>
    </span>
  )
}

function getLeader(data: any, pos: any, name: any) {
  return <span>{data[name]}</span>
}

const data = [{
  id: '1',
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}, {
  id: '2',
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
}, {
  id: '3',
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}, {
  id: '4',
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
}, {
  id: '5',
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}, {
  id: '6',
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
}, {
  id: '7',
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}, {
  id: '8',
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
}, {
  id: '9',
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}, {
  id: '10',
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
}];

export default class RadioCheckDemo extends React.Component<{}, any> {
  state = {
    selectedRowKeys: ['1'],
    data: data,
  };

  getRowClassName = (data: any, index: any) => {
    return `${(index + 1) % 2 === 0 ? '' : 'gray-row'}`
  }

  render() {
    return (
      <Table
        columns={columns}
        data={this.state.data}
        selection={{
          isSingleSelection: true,
          selectedRowKeys: this.state.selectedRowKeys,
          onSelect: (selectedRowKeys, selectedRows, currentRow) => {
            console.log('onSelect', selectedRowKeys, selectedRows, currentRow)
            this.setState({
              selectedRowKeys,
            })
          },
        }}
      />
    )
  }
}
```

### 多选

```jsx
import React from 'react';
import { Avatar, Table, Switch } from '@weapp/ui';
import imgSrc from "../../../../assets/images/ODTLcjxAfvqbxHnVXCYX.png"

const columns = [
  {
    title: '姓名',
    dataIndex: 'name',
    bodyRender: getName
  }, {
    title: '联系方式',
    dataIndex: 'tel'
  }, {
    title: '部门',
    dataIndex: 'depart',
  }, {
    title: '多行文本',
    dataIndex: 'textarea',
  }, {
    title: '上级',
    dataIndex: 'leader',
    bodyRender: getLeader
  }, {
    title: '入职时间',
    dataIndex: 'join_time',
  }
];

function getName(data: any, pos: any, name: any) {
  const url = imgSrc;
  return (
    <span className={'ui-table-demo-name'}>
      <Avatar size='small' url={url} ></Avatar>
      <a style={{ marginLeft: '10px', color: '#5D9CEC' }}>{data[name]}</a>
    </span>
  )
}

function getLeader(data: any, pos: any, name: any) {
  return <span>{data[name]}</span>
}

const data = [{
  id: '1',
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}, {
  id: '2',
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
}, {
  id: '3',
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}, {
  id: '4',
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
}, {
  id: '5',
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}, {
  id: '6',
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
}, {
  id: '7',
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}, {
  id: '8',
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
}, {
  id: '9',
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}, {
  id: '10',
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
}];

export default class CheckedDemo extends React.Component<{}, any> {
  state = {
    selectedRowKeys: ['1'],
    data: data,
    isShowIndexInCheck: true
  };

  getRowClassName = (data: any, index: any) => {
    return `${(index + 1) % 2 === 0 ? '' : 'gray-row'}`
  }

  onSelect = (selectedRowKeys: string[], selectedRows: any, currentRow: any, type: any) => {
    console.log('onSelect', selectedRowKeys, selectedRows, type)
    this.setState({
      selectedRowKeys,
    });
  }

  handleChange = (v: boolean) => {
    this.setState({ isShowIndexInCheck: v })
  }

  render() {
    const { isShowIndexInCheck } = this.state
    return (
      <div>
        <span style={{ margin: '15px 10px', display: 'inline-block', alignItems: 'center' }}>
          <Switch value={isShowIndexInCheck} onChange={this.handleChange} />&nbsp;&nbsp;&nbsp;选择列是否显示序号
        </span>
        <Table
          columns={columns}
          data={this.state.data}
               bordered={true}
          selection={{
            // selectedRowKeys: this.state.selectedRowKeys,
            isBatchSelect: true,
            getSelectionProps: (data: object, rowIndex: number | string) => {
              return {
                visible: false,
                disabled: rowIndex == 4,
              }
            },
            onSelect: this.onSelect
          }}
          isShowIndexInCheck={isShowIndexInCheck}
               sortable
               sortableType={'icon'}
        />
      </div>
    )
  }
}
```

### 滑动多选

先选择某一行（勾选或取消勾选），基于该行按住shift点击，进行滑动多选。或按住shift点击另外一行，将两行之间可选节点批量选择

```jsx
import React from 'react';
import { Avatar, Table, Switch } from '@weapp/ui';
import imgSrc from "../../../../assets/images/ODTLcjxAfvqbxHnVXCYX.png"

const columns = [
  {
    title: '姓名',
    dataIndex: 'name',
    bodyRender: getName
  }, {
    title: '联系方式',
    dataIndex: 'tel'
  }, {
    title: '部门',
    dataIndex: 'depart',
  }, {
    title: '多行文本',
    dataIndex: 'textarea',
  }, {
    title: '上级',
    dataIndex: 'leader',
    bodyRender: getLeader
  }, {
    title: '入职时间',
    dataIndex: 'join_time',
  }
];

function getName(data: any, pos: any, name: any) {
  const url = imgSrc;
  return (
    <span className={'ui-table-demo-name'}>
      <Avatar size='small' url={url} ></Avatar>
      <a style={{ marginLeft: '10px', color: '#5D9CEC' }}>{data[name]}</a>
    </span>
  )
}

function getLeader(data: any, pos: any, name: any) {
  return <span>{data[name]}</span>
}

const data = [{
  id: 'id-1',
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}, {
  id: 'id-2',
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
}, {
  id: 'id-3',
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}, {
  id: 'id-4',
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
}, {
  id: 'id-5',
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}, {
  id: 'id-6',
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
}, {
  id: 'id-7',
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}, {
  id: 'id-8',
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
}, {
  id: 'id-9',
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}, {
  id: 'id-10',
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
}];

export default class CheckedDemo extends React.Component<{}, any> {
  state = {
    selectedRowKeys: ['1'],
    data: data,
  };

  getRowClassName = (data: any, index: any) => {
    return `${(index + 1) % 2 === 0 ? '' : 'gray-row'}`
  }

  onSelect = (selectedRowKeys: string[], selectedRows: any, currentRow: any, type: any) => {
    console.log('onSelect', selectedRowKeys, selectedRows, type)
    this.setState({
      selectedRowKeys,
    });
  }

  render() {
    return (
      <div>
        
        <Table
          columns={columns}
          data={this.state.data}
          bordered={true}
          selection={{
            enableShiftSelect: true, // 开启shift快速批量选择
            getSelectionProps: (data: object, rowIndex: number | string) => {
              return {
                visible: false,
                disabled: rowIndex == 4,
              }
            },
            onSelect: this.onSelect
          }}
        />
      </div>
    )
  }
}
```

### 分页

```jsx
import React from 'react';
import { Table, Avatar, Dialog, ITableColumn, RadioDataType, Radio, RadioValueType, ITablePaginationType } from '@weapp/ui';
import imgSrc from "../../../../assets/images/ODTLcjxAfvqbxHnVXCYX.png"
const { message } = Dialog;

const columns: ITableColumn[] = [
  {
    title: '姓名',
    dataIndex: 'name',
    width: '15%',
    bodyRender: getName
  }, {
    title: '联系方式',
    width: '15%',
    dataIndex: 'tel'
  }, {
    title: '部门',
    width: '15%',
    dataIndex: 'depart',
  }, {
    title: '多行文本',
    width: '15%',
    dataIndex: 'textarea',
  }, {
    title: '上级',
    width: '15%',
    dataIndex: 'leader',
    bodyRender: getLeader
  }, {
    title: '入职时间',
    dataIndex: 'join_time',
  }
];

const options: RadioDataType = [
  {
    id: 'part',
    title: 'Pagination分页',
    content: 'Pagination分页',
  },
  {
    id: 'more',
    title: '点击加载更多',
    content: '点击加载更多',
  },
  {
    id: 'scroll',
    title: '滚动分页',
    content: '滚动分页',
  },
];

function getName(data: any, pos: any, name: any) {
  const url = imgSrc;
  return (
    <span className={'ui-table-demo-name'}>
      <Avatar size='small' url={url} ></Avatar>
      <a style={{ marginLeft: '10px', color: '#5D9CEC' }}>{data[name]}</a>
    </span>
  )
}

function getLeader(data: any, pos: any, name: any) {
  return <span>{data[name]}</span>
}

const data: any[] = [];
for (let i = 1; i < 147; i++) {
  data.push({
    key: i,
    name: `李大嘴${i}`,
    tel: 32,
    depart: `开发${i}部`,
    textarea: '设计师',
    leader: '王紫博',
    join_time: '2021-06-23'
  });
}

export default class PaginationModeDemo extends React.Component<{}, any> {
  state = {
    current: 1,
    pageSize: 20,
    data: data,
    paginationType: 'more',
    hasMore: true
  };

  getRowClassName = (data: any, index: any) => {
    return `${(index + 1) % 2 === 0 ? '' : 'gray-row'}`
  }

  handleChange = (v: RadioValueType) => {
    this.setState({ paginationType: v })
  }

  onPageChange = (value: number, pageSize: number) => {
    const { paginationType } = this.state

    this.setState({
      current: value,
      hasMore: (paginationType != 'part' && value * pageSize >= data.length) ? false : true,
      pageSize
    });

    message({
      type: 'success', // type: 'info', 'error', 'success'
      content: `value: ${value}, pageSize: ${pageSize}`,
    });
  }

  getPageInfo = () => {
    const { current, pageSize, data, paginationType } = this.state

    return {
      paginationType: paginationType as ITablePaginationType,
      value: current,
      pageSize: pageSize,
      total: data.length,
      showJumper: false,
      onChange: this.onPageChange,
      useFlat: true
    }
  }

  render() {
    const { data, paginationType, hasMore } = this.state
    return (
      <div>
        <Radio
         
          data={options}
          value={paginationType}
          onChange={this.handleChange}
        />
        <div style={{ margin: '15px 0' }}>
          <Table
            columns={columns}
            data={data}
            scroll={{ y: 260 }}
            pageInfo={this.getPageInfo()}
            hasMore={hasMore}
            scrollBarInBody={true}
            isShowIndex
            isRememberPageSize
          />
        </div>
      </div>
    )
  }
}
```

### 空数据

```jsx
import React from 'react';
import { Table, Empty, Icon, Button } from '@weapp/ui';

const columns = [
  {
    title: '全部',
    dataIndex: 'name',
  }, {
    title: '待处理',
    dataIndex: 'age'
  }, {
    title: '转发',
    dataIndex: 'address',
  }, {
    title: '抄送',
    dataIndex: 'send',
  }
];

const data: readonly any[] = [];

export default class EmptyDemo extends React.Component<{}, any> {
  render() {
    return (
      <Table
        columns={columns}
        data={data}
      />
    )
  }
}
```

### 骨架屏效果

```jsx
import React from 'react';
import { Table, Avatar, Dialog, ITableColumn, Switch } from '@weapp/ui';
import imgSrc from "../../../../assets/images/ODTLcjxAfvqbxHnVXCYX.png"
const { message } = Dialog;

const columns: ITableColumn[] = [
  {
    title: '姓名',
    dataIndex: 'name',
    isPrimaryKey: true,
    bodyRender: getName,
  }, {
    title: '联系方式',
    dataIndex: 'tel'
  }, {
    title: '部门',
    dataIndex: 'depart',
  },{
    title: '多行文本',
    dataIndex: 'textarea',
  },{
    title: '上级',
    dataIndex: 'leader',
    bodyRender: getLeader
  },{
    title: '入职时间',
    dataIndex: 'join_time',
  }
];

function getName(data: any, pos: any, name: any) {
  const url = imgSrc;
  return (
    <span className={'ui-table-demo-name'}>
      <Avatar size='small' url={url} ></Avatar>
      <a style={{ marginLeft: '10px', color: '#5D9CEC' }}>{data[name]}</a>
    </span>
  )
}

function getLeader(data: any, pos: any, name: any) {
  return <a>{data[name]}</a>
}

const data = [{
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
},{
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
},{
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
},{
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
},{
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}];

export default class SkeletonDemo extends React.Component<{}, any> {
  state = {
    data: data,
    useSkeleton: false,
  };

  handleLoadingChange = (v: boolean) => {
    this.setState({ useSkeleton: v })
  }

  render() {
    const { data, useSkeleton } = this.state
    return (
      <div>
        <span style={{ margin: '15px 0', display: 'inline-block', alignItems: 'center' }}>
          <Switch value={useSkeleton} onChange={this.handleLoadingChange} />&nbsp;&nbsp;&nbsp;是否开启骨架屏
        </span>
        <Table
         
          columns={columns}
          data={data}
          loading={useSkeleton}
          useSkeleton={useSkeleton}
          defaultSkeletonRow={6}
        />
      </div>
    )
  }
}
```

### 字段统计

通过getSummaryProps配置尾行统计功能，可配置是否开启合计（isShowSummary）、合计行数（summaryCount）

```jsx
import React from 'react';
import { Table, ITableColumn } from '@weapp/ui';

function getIndex(data: any, pos: any, name: any) {
  return <span>{pos.row + 1}</span>
}

function getName(data: any, pos: any, name: any) {
  return <a style={{ color: '#5D9CEC' }}>{data[name]}</a>
}

const columns: ITableColumn[] = [
  {
    title: '序号',
    dataIndex: 'index',
    bodyRender: getIndex,
    textAlign: 'center',
    align: 'center',
    summaryRender: (data: any, column: any) => {
      return '合计'
    }
  }, {
    title: '项目名称',
    dataIndex: 'name',
    bodyRender: getName
  }, {
    title: '开始时间',
    dataIndex: 'start_time',
  }, {
    title: '结束时间',
    dataIndex: 'end_time',
  }, {
    title: '负责人',
    dataIndex: 'person',
    bodyRender: getName
  }, {
    title: '状态',
    dataIndex: 'status',
    bodyRender: (data: readonly [], pos: any, name: any) => {
      if (data[name] === 1) {
        return '包信'
      }
    }
  }, {
    title: '进度',
    dataIndex: 'precess',
  }, {
    title: '待完成',
    dataIndex: 'doing',
    summaryRender: (data: readonly any[]) => {
      // @ts-ignore
      return data.reduce((pre: object, cur: object) => pre + cur['doing'], 0)
    }
  }, {
    title: '已延期',
    dataIndex: 'over',
    summaryRender: (data: readonly any[]) => {
      // @ts-ignore
      return data.reduce((pre: object, cur: object) => pre + cur['over'], 0)
    }
  }, {
    title: '已完成',
    dataIndex: 'done',
    summaryRender: (data: readonly any[]) => {
      // @ts-ignore
      return data.reduce((pre: object, cur: object) => pre + cur['done'], 0)
    }
  }
];

const data = [{
  name: '产品运维部招聘项目',
  start_time: '2016-03-29',
  end_time: '2016-03-29',
  person: '李杰',
  status: 1,
  precess: '10%',
  doing: 2,
  over: 1,
  done: 5
}, {
  name: '家乐福二期开发项目',
  start_time: '2016-03-29',
  end_time: '2016-03-29',
  person: '李杰',
  status: 1,
  precess: '26%',
  doing: 7,
  over: 2,
  done: 9
}, {
  name: '产品运维部招聘项目',
  start_time: '2016-03-29',
  end_time: '2016-03-29',
  person: '李杰',
  status: 1,
  precess: '10%',
  doing: 2,
  over: 1,
  done: 5
}, {
  name: '家乐福二期开发项目',
  start_time: '2016-03-29',
  end_time: '2016-03-29',
  person: '李杰',
  status: 1,
  precess: '26%',
  doing: 7,
  over: 2,
  done: 9
}, {
  name: '产品运维部招聘项目',
  start_time: '2016-03-29',
  end_time: '2016-03-29',
  person: '李杰',
  status: 1,
  precess: '10%',
  doing: 2,
  over: 1,
  done: 5
}, {
  name: '家乐福二期开发项目',
  start_time: '2016-03-29',
  end_time: '2016-03-29',
  person: '李杰',
  status: 1,
  precess: '26%',
  doing: 7,
  over: 2,
  done: 9
}, {
  name: '产品运维部招聘项目',
  start_time: '2016-03-29',
  end_time: '2016-03-29',
  person: '李杰',
  status: 1,
  precess: '10%',
  doing: 2,
  over: 1,
  done: 5
}, {
  name: '家乐福二期开发项目',
  start_time: '2016-03-29',
  end_time: '2016-03-29',
  person: '李杰',
  status: 1,
  precess: '26%',
  doing: 7,
  over: 2,
  done: 9
}, {
  name: '产品运维部招聘项目',
  start_time: '2016-03-29',
  end_time: '2016-03-29',
  person: '李杰',
  status: 1,
  precess: '10%',
  doing: 2,
  over: 1,
  done: 5
}, {
  name: '家乐福二期开发项目',
  start_time: '2016-03-29',
  end_time: '2016-03-29',
  person: '李杰',
  status: 1,
  precess: '26%',
  doing: 7,
  over: 2,
  done: 9
},];

export default class TextareaDemo extends React.Component<{}, any> {
  getRowClassName = (data: any, index: any) => {
    return `${(index + 1) % 2 === 0 ? '' : 'gray-row'}`
  }

  onRowClick = (data: any, index: any, event: any, isCtrlPress: boolean, rowType: string) => {
    console.log('onRowClick', data, index, event.target, isCtrlPress, rowType)
  }

  getSummaryProps = () => {
    return {
      isShowSummary: true,
      summaryCount: 1
    }
  }

  render() {
    return (
      <Table
        columns={columns}
        data={data}
        onRowClick={this.onRowClick}
        getSummaryProps={this.getSummaryProps()}
      />
    )
  }
}
```

### 合并单元格

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

const columns = [
  {
    title: '时间',
    dataIndex: 'time',
    width: '200px',
    bodyRender: getTime
  }, {
    title: '具体',
    dataIndex: 'sub',
    width: '200px',
    bodyRender: getSub
  }, {
    title: '工作内容',
    dataIndex: 'work',
    width: '200px',
    bodyRender: (data: string, pos: any, name: any) => {
      return pos.row < 4 ? '会议' : ''
    }
  }
];

function getSub(data: any, pos: any, name: any) {
  return <div style={{ textAlign:'center' }}>{data.sub}</div>
}

function getTime(data: string, pos: any, name: any){
  const { row }  = pos;
  if (row % 2 === 0) {
    return {
      props: {
        rowSpan: 2
      },
      children: (
        <div style={{ textAlign:'center' }}>
          {data[name]} <br/>
          {/*{data['week']}*/}
        </div>
      )
    }
  }
  if (row % 2 !== 0) {
    return {
      props: {
        rowSpan: 0
      }
    }
  }
  return (
    <div>
      {data[name]} <br/>
      {/*{data['week']}*/}
    </div>
  )
}

const datas = [
  {
    time: '2021-04-22',
    week: '星期一',
    sub: '上午',
    work: [
      {
        start_time: '09:00',
        person: '李杰',
        content: '会议'
      },
      {
        start_time: '09:30',
        person: '李杰',
        content: '会议'
      },
    ]
  },
  {
    time: '2021-04-22',
    week: '星期一',
    sub: '下午',
    work: []
  },
  {
    time: '2021-04-23',
    week: '星期二',
    sub: '上午',
    work: [
      {
        start_time: '09:00',
        person: '李杰',
        content: '会议'
      },
      {
        start_time: '10:30',
        person: '',
        content: '拜访客户'
      },
    ]
  },
  {
    time: '2021-04-23',
    week: '星期二',
    sub: '下午',
    work: [
      {
        start_time: '15:30',
        person: '',
        content: '拜访客户'
      },
    ]
  },
  {
    time: '2021-04-24',
    week: '星期三',
    sub: '上午',
    work: [
      {
        start_time: '09:30',
        person: '',
        content: '项目会议'
      },
    ]
  },
  {
    time: '2021-04-24',
    week: '星期三',
    sub: '下午',
    work: [
      {
        start_time: '15:30',
        person: '',
        content: '拜访客户'
      },
    ]
  },
  {
    time: '2021-04-25',
    week: '星期四',
    sub: '上午',
    work: []
  },
  {
    time: '2021-04-25',
    week: '星期四',
    sub: '下午',
    work: []
  },
  {
    time: '2021-04-26',
    week: '星期五',
    sub: '上午',
    work: []
  },
  {
    time: '2021-04-26',
    week: '星期五',
    sub: '下午',
    work: []
  },
  {
    time: '2021-04-27',
    week: '星期六',
    sub: '上午',
    work: []
  },
  {
    time: '2021-04-27',
    week: '星期六',
    sub: '下午',
    work: []
  },
  {
    time: '2021-04-28',
    week: '星期天',
    sub: '上午',
    work: []
  },
  {
    time: '2021-04-28',
    week: '星期天',
    sub: '下午',
    work: []
  },
]

export default class MergeCellDemo extends React.Component<{}, any> {
  state = {
    data: datas,
  };

  getRowClassName = (data: any, index: any) => {
    let mergeNum = (index + 1) % 2 === 0 ? (index + 1) * 2 - 1 : (index + 1) * 2 + 1
    return `${(mergeNum + 1) / 4 % 2 === 0 ? 'gray-row' : ''}`
  }

  render() {
    return (
      <Table
        disableHoverHighlight
        bordered
        columns={columns}
        data={this.state.data}
        rowKey='key'
      />
    )
  }
}
```

### 排序

```jsx
import React from 'react';
import { Table, ITableColumn } from '@weapp/ui';

const columns: ITableColumn[] = [
  {
    title: '商品名',
    dataIndex: 'name',
    orderKey: 'name',
    orderType: ''
  }, {
    title: <span className='test-dom'>库存</span>,
    dataIndex: 'stock',
    orderKey: 'name',
    orderType: '',
    isCustomTitle: true
  }
];

const data: any[] = []

for (let i = 0; i < 10; i++) {
  data.push({
    id: i + 1,
    name: `商品_${i + 1}`,
    stock: parseInt(String(Math.random() * 100), 10)
  })
}

interface SortDemoStateType<Data = any> {
  current: number,
  pageSize: number,
  data: Data[],
  columns: ITableColumn[]
}

export default class SortDemo extends React.Component<{}, SortDemoStateType> {
  state: SortDemoStateType = {
    current: 1,
    pageSize: 5,
    data: data,
    columns
  };

  onSort = (columns: ITableColumn[], sortColumn: ITableColumn) => {
    console.log('onSort', columns, sortColumn)
    const { data } = this.state;

    // 排序逻辑处理 mock
    const _d = JSON.parse(JSON.stringify(data))
    _d.sort((a: any, b: any) => {
      if (sortColumn.orderType === 'asc') {
        return Number(b.stock) - Number(a.stock)
      } else {
        return Number(a.stock) - Number(b.stock)
      }
    })

    this.setState({ columns, data: _d })
  }

  getRowClassName = (data: any, index: any) => {
    return `${(index + 1) % 2 === 0 ? '' : 'gray-row'}`
  }

  render() {
    const { data, columns } = this.state

    return (
      <Table
        columns={columns}
        data={data}
        onSort={this.onSort}
        ellipsis={false}
      />
    )
  }
}
```

### 拖拽

启用拖拽时请设置标识字段 id 或使用 rowKey指定标识字段，否则将会默认使用index作为标识，导致拖拽后数据更新异常

```jsx
import React from 'react';
import { Table, ITableColumn, Avatar, Switch, Icon } from '@weapp/ui';
import imgSrc from "../../../../assets/images/ODTLcjxAfvqbxHnVXCYX.png"

const columns: ITableColumn[] = [
  {
    title: '姓名',
    dataIndex: 'name',
    bodyRender: getName
  }, {
    title: '联系方式',
    dataIndex: 'tel'
  }, {
    title: '部门',
    dataIndex: 'depart',
  }, {
    title: '多行文本',
    dataIndex: 'textarea',
  }, {
    title: '上级',
    dataIndex: 'leader',
    bodyRender: getLeader
  }, {
    title: '入职时间',
    dataIndex: 'join_time',
  }
];

function getName(data: any, pos: any, name: any) {
  const url = imgSrc;
  return (
    <span className={'ui-table-demo-name'}>
      <Avatar size='small' url={url} ></Avatar>
      <a style={{ marginLeft: '10px', color: '#5D9CEC' }}>{data[name]}</a>
    </span>
  )
}

function getLeader(data: any, pos: any, name: any) {
  return <span>{data[name]}</span>
}

const data: any[] = [];
for (let i = 0; i < 7; i++) {
  data.push({
    id: i + 10,
    name: `李大嘴${i}`,
    tel: 32,
    depart: `西湖区湖底公园${i}号`,
    textarea: '设计师',
    leader: '王紫博',
    join_time: '2021-06-23'
  });
}

interface SortDemoStateType<Data = any> {
  selectedRowKeys: string[]
  data: Data[],
  sortableType: 'row' | 'icon'
}

export default class SortableDemo extends React.Component<{}, SortDemoStateType> {
  state: SortDemoStateType = {
    selectedRowKeys: [],
    data: data,
    sortableType: 'icon'
  };

  handleChange = (v: boolean) => {
    this.setState({
      sortableType: v ? 'row' : 'icon'
    })
  }

  onDrag = (data: any[], evt: any) => {
    console.log(data, evt)
    this.setState({ data })
  }

  onSelect = (selectedRowKeys: string[], selectedRows: any, currentRow: any) => {
    console.log('onSelect', selectedRowKeys, selectedRows, currentRow)
    this.setState({
      selectedRowKeys,
    });
  }

  getRowClassName = (data: any, index: any) => {
    return `${index < 3 ? 'ui-table-grid-tr__disable-sortable' : 'gray-row'}`
  }

  render() {
    const { data, sortableType } = this.state
    return (
      <div>
        <span style={{ margin: '15px 10px', display: 'inline-block', alignItems: 'center' }}>
          <Switch value={sortableType === 'row'} onChange={this.handleChange} />&nbsp;&nbsp;&nbsp;拖拽方式
        </span>
        <Table
         
          key={sortableType}
          columns={columns}
          data={data}
          sortable
          sortableType={sortableType}
          onDrag={this.onDrag}
          rowClassName={this.getRowClassName}
        />
      </div>
    )
  }
}
```

### 编辑拖拽排序

通过enableSortableColumnEdit参数开启

```jsx
import React from 'react';
import { Table, Dialog, ITableColumn } from '@weapp/ui';
const { message } = Dialog;

// ─── 公共列定义 ────────────────────────────────────────────────────────────────
const columns: ITableColumn[] = [
  { title: '姓名', dataIndex: 'name', width: '25%', orderKey: 'name' },
  { title: '部门', dataIndex: 'depart', width: '35%', orderKey: 'depart' },
  { title: '职位', dataIndex: 'position', width: '20%', orderKey: 'position' },
  { title: '入职时间', dataIndex: 'join_time', orderKey: 'join_time' },
];

// ─── 公共 mock 数据（20 条，覆盖 4 页 × 5 条） ──────────────────────────────
const DEPTS = ['研发部', '产品部', '设计部', '测试部', '运营部'];
const JOBS  = ['工程师', '经理', '总监', '专员', '助理'];
function buildData(count = 20) {
  const arr: any[] = [];
  for (let i = 1; i <= count; i++) {
    arr.push({
      id: String(i),
      name: `员工${i}`,
      depart: DEPTS[(i - 1) % DEPTS.length],
      position: JOBS[(i - 1) % JOBS.length],
      join_time: `2022-0${(i % 9) + 1}-01`,
    });
  }
  return arr;
}

// ─── 样式辅助 ─────────────────────────────────────────────────────────────────
const blockStyle: React.CSSProperties = {
  marginBottom: 40,
  padding: '20px 24px',
  background: '#fff',
  borderRadius: 6,
  boxShadow: '0 1px 4px rgba(0,0,0,.08)',
};
const titleStyle: React.CSSProperties = {
  fontSize: 16,
  fontWeight: 600,
  marginBottom: 8,
  color: '#1a1a1a',
};
const descStyle: React.CSSProperties = {
  fontSize: 13,
  color: '#666',
  lineHeight: 1.8,
  marginBottom: 16,
  background: '#f7f8fa',
  padding: '12px 16px',
  borderRadius: 4,
  borderLeft: '3px solid #5D9CEC',
};

// ═══════════════════════════════════════════════════════════════════════════════
// 示例一：前端分页
// ═══════════════════════════════════════════════════════════════════════════════
interface FrontendState {
  data: any[];
  current: number;
  pageSize: number;
}

class FrontendPaginationDemo extends React.Component<any, FrontendState> {
  state: FrontendState = {
    data: buildData(20),
    current: 1,
    pageSize: 5,
  };

  onPageChange = (current: number, pageSize: number) => {
    this.setState({ current, pageSize });
  };

  onDrag = (newData: any[], evt: any) => {
    /**
     * 前端分页场景下 onDrag 返回值说明：
     *
     * newData  — 重排后的完整数据数组（已在组件内部完成排序），
     *            直接用于 setState({ data: newData }) 即可刷新视图。
     *
     * evt.type    = 'edit-sortable-column'
     * evt.hasCrossPage = false（前端分页所有数据均在本地，无跨页概念）
     * evt.changes = [{ key, oldIndex, newIndex, data }]
     *   key      — 行的 rowKey 值
     *   oldIndex — 修改前在全量数组中的 0-based 索引
     *   newIndex — 用户填写序号对应的内部排序值（用户输入值 - 1，可为小数或负数）
     *   data     — 该行原始数据
     *
     * 技术实现：前端持有全量数据，组件直接排好序交回，
     * 外部只需 setState({ data: newData }) 完成刷新，无需自行计算排序逻辑。
     */
    console.log('[前端分页] onDrag:', { newData, evt });
    this.setState({ data: newData });
  };

  render() {
    const { data, current, pageSize } = this.state;
    return (
      <div style={blockStyle}>
        <div style={titleStyle}>前端分页（本地排序）</div>
        <div style={descStyle}>
          <b>onDrag 参数说明：</b><br />
          • <b>newData</b>（第一个参数）：组件已在内部完成排序的<b>完整数据数组</b>，直接用于更新 state。<br />
          • <b>evt.type</b>：<code>'edit-sortable-column'</code><br />
          • <b>evt.hasCrossPage</b>：<code>false</code>（前端持有全量数据，无需区分跨页）<br />
          • <b>evt.changes</b>：<code>{'[{ key, oldIndex, newIndex, data }]'}</code>，其中 <code>newIndex</code> 为用户输入序号减 1 后的排序值，可为小数或负数<br /><br />
          <b>技术实现：</b>前端持有所有数据，排序计算在组件内完成，
          外部只需 <code>setState({'({ data: newData })'  })</code> 即可，极简集成。<br />
          拖动图标或点击编辑图标修改序号后点击"保存"，查看控制台日志。
        </div>
        <Table
         
          columns={columns}
          data={data}
          sortable
          sortableType="icon"
          onDrag={this.onDrag}
          pageInfo={{
            value: current,
            pageSize,
            total: data.length,
            showJumper: true,
            useFlat: true,
            onChange: this.onPageChange,
            pageSizeOptions: [5, 10, 20, 50]
          }}
          scroll={{ y: 300 }}
          enableSortableColumnEdit
        />
      </div>
    );
  }
}

// ═══════════════════════════════════════════════════════════════════════════════
// 示例二：后端分页（模拟）
// ═══════════════════════════════════════════════════════════════════════════════
const ALL_SERVER_DATA = buildData(20);

interface BackendState {
  pageData: any[];
  current: number;
  pageSize: number;
  total: number;
  loading: boolean;
}

class BackendPaginationDemo extends React.Component<any, BackendState> {
  state: BackendState = {
    pageData: ALL_SERVER_DATA.slice(0, 5),
    current: 1,
    pageSize: 5,
    total: ALL_SERVER_DATA.length,
    loading: false,
  };

  /** 模拟后端请求 */
  fetchPage = (page: number, size: number) => {
    this.setState({ loading: true });
    setTimeout(() => {
      const start = (page - 1) * size;
      this.setState({
        pageData: ALL_SERVER_DATA.slice(start, start + size),
        current: page,
        loading: false,
      });
    }, 300);
  };

  onPageChange = (current: number, pageSize: number) => {
    this.fetchPage(current, pageSize);
  };

  onDrag = (newData: any[], evt: any) => {
    /**
     * 后端分页场景下 onDrag 返回值说明：
     *
     * ① 未跨页（hasCrossPage = false）
     *   newData  — 当前页重排后的数据，直接更新当前页视图即可。
     *   evt.type = 'edit-sortable-column'
     *   evt.changes — 同前端分页，告知后端哪些行需要调整顺序。
     *   后端只需根据 changes 更新数据库中的 sort 字段，再返回当前页数据。
     *
     * ② 跨页（hasCrossPage = true）
     *   newData  — 当前页原始数据（未排序），后端需自行处理。
     *   evt.type = 'edit-sortable-column-cross-page'
     *   evt.changes — 含目标页超出当前页的变更，需传给后端做跨页调整。
     *   后端处理完成后，前端重新请求当前页数据（fetchPage）。
     *
     * 与前端分页的区别：
     *   前端分页时组件持有全量数据，可直接在本地排序并返回完整结果；
     *   后端分页时组件只有当前页数据，无法感知其他页的数据，
     *   一旦用户将某行移到其他页（跨页），必须把变更交给后端统一处理。
     *   这样设计保证了"后端为唯一数据源"的原则，避免前端猜测全量顺序。
     */
    console.log('[后端分页] onDrag:', { newData, evt });
    if (evt.hasCrossPage) {
      message({ type: 'info', content: `跨页变更，已将 ${evt.changes?.length} 条变更发送至后端（模拟）` });
      // 模拟后端处理完成后重新拉取当前页
      setTimeout(() => this.fetchPage(this.state.current, this.state.pageSize), 800);
    } else {
      this.setState({ pageData: newData });
    }
  };

  render() {
    const { pageData, current, pageSize, total, loading } = this.state;
    return (
      <div style={blockStyle}>
        <div style={titleStyle}>后端分页（模拟跨页）</div>
        <div style={descStyle}>
          <b>onDrag 参数说明（后端分页）：</b><br />
          • <b>未跨页</b>：<code>evt.hasCrossPage = false</code>，newData 为当前页重排结果，将 changes 同步后端即可。<br />
          • <b>跨页</b>：<code>evt.hasCrossPage = true</code>，<code>evt.type = 'edit-sortable-column-cross-page'</code>，
          newData 为当前页原数据，需将全部 changes 发给后端统一调整顺序，前端再重新拉取当前页。<br /><br />
          <b>与前端分页的区别：</b>前端分页组件持有全量数据可直接本地排序；
          后端分页仅有当页数据，跨页时无法自行重排，必须依赖后端作为唯一数据源处理。<br /><br />
          尝试将某行序号改为其他页的编号（如当前第1页每页5条，将某行改为 6~20），触发跨页逻辑。详情见控制台。
        </div>
        <Table
         
          columns={columns}
          data={pageData}
          loading={loading}
          sortable
          sortableType="icon"
          onDrag={this.onDrag}
          pageInfo={{
            value: current,
            pageSize,
            total,
            showJumper: true,
            useFlat: true,
            paginationNoLocal: true,
            onChange: this.onPageChange,
            pageSizeOptions: [5, 10, 20, 50]
          } as any}
          scroll={{ y: 300 }}
          enableSortableColumnEdit
        />
      </div>
    );
  }
}

// ═══════════════════════════════════════════════════════════════════════════════
// 示例三：编辑中操作拦截
// ═══════════════════════════════════════════════════════════════════════════════
interface BlockState {
  data: any[];
  selectedRowKeys: string[];
  current: number;
  pageSize: number;
}

class BlockDemo extends React.Component<any, BlockState> {
  state: BlockState = {
    data: buildData(15),
    selectedRowKeys: [],
    current: 1,
    pageSize: 5,
  };

  onDrag = (newData: any[]) => {
    this.setState({ data: newData });
    message({ type: 'success', content: '保存成功' });
  };

  onPageChange = (current: number, pageSize: number) => {
    this.setState({ current, pageSize });
  };

  onSelect = (selectedRowKeys: string[]) => {
    this.setState({ selectedRowKeys });
  };

  render() {
    const { data, selectedRowKeys, current, pageSize } = this.state;
    return (
      <div style={blockStyle}>
        <div style={titleStyle}>序号编辑中的操作拦截</div>
        <div style={descStyle}>
          点击表头的编辑图标进入序号编辑状态后，以下操作会被拦截并弹出提示：<br />
          • <b>列宽拖拽</b>：拖动列分隔线调整宽度<br />
          • <b>分页</b>：点击分页器切换页码<br />
          • <b>勾选</b>：点击 checkbox 勾选行，或按住 Shift 滑动选择<br />
          • <b>排序</b>：点击列标题进行升序/降序排序<br /><br />
          所有拦截均提示：<code>序号编辑中，请先保存后再操作！(部分为 操作不显示)</code>
        </div>
        <Table
         
          columns={columns.map(c => ({ ...c, sorter: true }))}
          data={data}
          sortable
          sortableType="icon"
          onDrag={this.onDrag}
          selection={{
            selectedRowKeys,
            onSelect: this.onSelect,
            enableShiftSelect: true,
          }}
          pageInfo={{
            value: current,
            pageSize,
            total: data.length,
            useFlat: true,
            onChange: this.onPageChange,
            pageSizeOptions: [5, 10, 20, 50]
          }}
          scroll={{ y: 300 }}
          resizable
          enableSortableColumnEdit
        />
      </div>
    );
  }
}

// ═══════════════════════════════════════════════════════════════════════════════
// 示例四：子列表展开的编辑拖拽排序
// ═══════════════════════════════════════════════════════════════════════════════
const expandedData = [
  {
    id: '1',
    name: '部门主管',
    depart: '研发部',
    position: '总监',
    join_time: '2020-01-15',
    children: [
      { id: '1-1', name: '前端工程师A', depart: '研发部', position: '工程师', join_time: '2021-03-10' },
      { id: '1-2', name: '前端工程师B', depart: '研发部', position: '工程师', join_time: '2021-06-20' },
      { id: '1-3', name: '后端工程师A', depart: '研发部', position: '工程师', join_time: '2021-09-05' },
    ],
  },
  {
    id: '2',
    name: '产品经理',
    depart: '产品部',
    position: '经理',
    join_time: '2020-05-20',
    children: [
      { id: '2-1', name: '产品助理A', depart: '产品部', position: '助理', join_time: '2022-02-14' },
      { id: '2-2', name: '产品助理B', depart: '产品部', position: '助理', join_time: '2022-07-30' },
    ],
  },
  {
    id: '3',
    name: '设计主管',
    depart: '设计部',
    position: '经理',
    join_time: '2019-12-01',
    children: [
      { id: '3-1', name: 'UI设计师', depart: '设计部', position: '专员', join_time: '2021-11-18' },
    ],
  },
];

interface ExpandedDemoState {
  data: any[];
  expandRowKeys: string[];
}

class ExpandedDemo extends React.Component<any, ExpandedDemoState> {
  state: ExpandedDemoState = {
    data: expandedData,
    expandRowKeys: ['1', '2'], // 默认展开前两个
  };

  onExpand = (config: { expandRowKeys: string[]; expanded: boolean; data: any; event: React.MouseEvent<HTMLSpanElement>; index: number }) => {
    console.log('[子列表展开] onExpand:', config);
    this.setState({ expandRowKeys: config.expandRowKeys });
  };

  onDrag = (newData: any[], evt: any, _pageInfo?: any, nestedInfo?: any) => {
    console.log('[子列表拖拽] onDrag:', { newData, evt, nestedInfo });

    // 检查是否跨层级拖拽
    if (nestedInfo?.related?.floor !== nestedInfo?.dragged?.floor) {
      console.warn('跨层级拖拽，恢复原位置');
      // 跨层级拖拽时，恢复原位置（参考 expanded.tsx）
      evt.item.parentNode.insertBefore(evt.item, evt.item.parentNode.children[evt.oldIndex + 1]);
      return;
    }

    this.setState({ data: newData });
  };

  render() {
    const { data, expandRowKeys } = this.state;
    return (
      <div style={blockStyle}>
        <div style={titleStyle}>子列表展开的编辑排序</div>
        <div style={descStyle}>
          <b>功能特性：</b><br />
          • 支持主列表和子列表的序号编辑拖拽排序<br />
          • 子列表展开/收起状态保持<br />
          • 跨层级拖拽自动恢复（避免数据结构混乱）<br />
          • 同层级内可正常编辑序号和拖拽排序<br /><br />
          <b>操作说明：</b>点击展开图标可展开/收起子列表，
          在同一层级内可编辑序号或拖拽排序，跨层级拖拽会被阻止。详情见控制台。
        </div>
        <Table
         
          columns={columns}
          data={data}
          rowKey="id"
          needExpand
          expandRowKeys={expandRowKeys}
          onExpand={this.onExpand}
          sortable
          sortableType="icon"
          enableSortableColumnEdit
          onDrag={this.onDrag}
          scroll={{ y: 400 }}
        />
      </div>
    );
  }
}

// ═══════════════════════════════════════════════════════════════════════════════
// 汇总导出
// ═══════════════════════════════════════════════════════════════════════════════
export default class SortableEditDemo extends React.Component<any, {}> {
  render() {
    const weId = this.props.weId || '';
    return (
      <div style={{ padding: '20px', background: '#f0f2f5', minHeight: '100vh' }}>
        <FrontendPaginationDemo />
        <BackendPaginationDemo  />
        <BlockDemo             /> 
        {/* <ExpandedDemo          /> */}
      </div>
    );
  }
}
```

### 子列表展开

若节点为父级节点，isParent属性 需为 true

```jsx
import React from 'react';
import { Table, ITableOnChangeConfig } from '@weapp/ui';

const datasets = [{
  item_id: '5024217',
  bro_uvpv: '0/0',
  stock_num: '60',
  sold_num: 0,
  isParent: true,
  // rowSpan: 2,
  children: [
    {
      item_id: '1',
      bro_uvpv: '1',
      stock_num: '1',
      sold_num: 1,
      // isParent: true,
      // rowSpan: 2,
      // children: [
      //   {
      //     item_id: '1-1',
      //     bro_uvpv: '1-1',
      //     stock_num: '1-1',
      //     sold_num: 2,
      //     // rowSpan: 2,
      //   },
      //   {
      //     item_id: '1-2',
      //     bro_uvpv: '1-1',
      //     stock_num: '1-1',
      //     sold_num: 2,
      //     // rowSpan: 0,
      //     isParent: true,
      //   },
      // ]
    },
    {
      item_id: '2',
      rowSpan: 0,
      bro_uvpv: '2',
      stock_num: '2',
      sold_num: 2,
    }
  ]
}, {
  item_id: '502427722',
  bro_uvpv: '0/02',
  stock_num: 519,
  sold_num: 12,
  // rowSpan: 0,
},{
  item_id: '5024277',
  bro_uvpv: '0/0',
  stock_num: 59,
  sold_num: 0,
}, {
  item_id: '13213123',
  bro_uvpv: '0/0',
  stock_num: 159,
  sold_num: 0,
}];

const columns = [{
  title: 'Product',
  dataIndex: 'item_id',
  bodyRender: (data: any) => {
    return (
      <div>{data.item_id}</div>
    );
  }
}, {
  title: 'PV',
  dataIndex: 'bro_uvpv',
  width: '100px'
}, {
  title: 'Stock',
  dataIndex: 'stock_num',
  width: '100px'
}, {
  title: 'Sales',
  dataIndex: 'sold_num',
  bodyRender: (data: any, pos: any) => {
    // if (data.item_id === '5024217' || data.item_id === '1' || data.item_id === '1-1') {
    //   return {
    //     props: {
    //       rowSpan: 2
    //     },
    //     children: (
    //       <div>{data.sold_num}</div>
    //     )
    //   }
    // }
    // if (data.item_id === '502427722' || data.item_id === '2' || data.item_id === '1-2') {
    //   return {
    //     props: {
    //       rowSpan: 0
    //     }
    //   }
    // }
    return (
      <div>{data.sold_num}</div>
    );
  }
}];

const fetchData = (data: any, callback: any) => {
  setTimeout(() => {
    const mockAsyncDats = [{
      item_id: '1-1-1',
      bro_uvpv: '1-1-1',
      stock_num: '1-1-1',
      sold_num: 3,
    }];
    callback(mockAsyncDats);
  }, 200);
}

export default class ExpandedDemo extends React.Component<{}, any> {
  state = {
    datasets,
    limit: 10,
    current: 0,
    total: 101,
    expandRowKeys: [],
  };

  onChange = (conf: ITableOnChangeConfig) => {
    this.setState(conf);
  }

  onExpand = (conf: any) => {
    this.setState({
      expandRowKeys: conf.expandRowKeys
    })
  }

  loadMore = (data: any) => new Promise((resolve) => {
    fetchData(data, (resData: readonly []) => {
      this.updateDataSets(data, resData)
      resolve(resData);
    })
  })

  updateDataSets = (data: any, resData: readonly []) => {
    const { datasets } = this.state
    let [...newData] = datasets;

    function getNewTreeData(datas: any[]) {
      datas.forEach((item) => {
        if (item.item_id === data.item_id) {
          item.children = resData
        } else if (item.children && item.children.length) {
          getNewTreeData(item.children)
        }
      })
    }

    getNewTreeData(newData)

    this.setState({ datasets: [...newData] })
  }

  getRowClassName = (data: any, index: any) => {
    return `${(index + 1) % 2 === 0 || data.floor != 1 ? '' : 'gray-row'}`
  }

  onDrag = (data: any [], evt: any, pageInfo?: any, nestedInfo?: any) => {
    if (nestedInfo.related?.floor != nestedInfo?.dragged?.floor) {
      evt.item.parentNode.insertBefore(evt.item, evt.item.parentNode.children[evt.oldIndex + 1])
    } else {
      this.setState({ datasets: data })
    }
  }

  render() {
    return (
      <Table
        columns={columns}
        data={this.state.datasets}
        onChange={this.onChange}
        rowKey="item_id"
        expandRowKeys={this.state.expandRowKeys}
        onExpand={this.onExpand}
        needExpand={true}
        // isExpandAsync={true}
        loadMore={this.loadMore}
             sortable
             onDrag={this.onDrag}
             sortableType={'icon'}
      />
    )
  }
}
```

### 带选择的子列表

当 selection.isLinkageBySelect 为 false，父子节点勾选不联动 互相独立； 当 selection.isLinkageBySelect 为 true，父子节点勾选状态联动（自上向下联动），父节点被选中会连带其下级一并被勾选，子节点被选中不影响其父级节点的勾选状态（子节点作为独立数据存在）

```jsx
import React from 'react';
import { Table, ITableOnChangeConfig, Icon, Radio, RadioDataType } from '@weapp/ui';

const datasets = [{
  item_id: '1',
  bro_uvpv: '0/0',
  stock_num: '60',
  sold_num: 0,
  isParent: true,
  children: [
    {
      item_id: '1-1',
      bro_uvpv: '1',
      stock_num: '1',
      sold_num: 1,
      isParent: true,
      children: [
        {
          item_id: '1-1-1',
          bro_uvpv: '1-1',
          stock_num: '1-1',
          sold_num: 2,
        },
        {
          item_id: '1-1-2',
          bro_uvpv: '1-1',
          stock_num: '1-1',
          sold_num: 2,
          isParent: true,
          children: [
            {
              item_id: '1-1-1-1',
              bro_uvpv: '1-1',
              stock_num: '1-1',
              sold_num: 2,
            },
          ]
        },
      ]
    },
    {
      item_id: '1-2',
      rowSpan: 0,
      bro_uvpv: '2',
      stock_num: '2',
      sold_num: 2,
    },
    {
      item_id: '1-3',
      rowSpan: 0,
      bro_uvpv: '2',
      stock_num: '2',
      sold_num: 2,
    }
  ]
}, {
  item_id: '2',
  bro_uvpv: '0/02',
  stock_num: 519,
  sold_num: 12,
  // rowSpan: 0,
},{
  item_id: '3',
  bro_uvpv: '0/0',
  stock_num: 59,
  sold_num: 0,
}, {
  item_id: '4',
  bro_uvpv: '0/0',
  stock_num: 159,
  sold_num: 0,
}];

const columns = [{
  title: 'Product',
  dataIndex: 'item_id',
  bodyRender: (data: any) => {
    return (
      <div>{data.item_id}</div>
    );
  }
}, {
  title: 'PV',
  dataIndex: 'bro_uvpv',
  width: '100px'
}, {
  title: 'Stock',
  dataIndex: 'stock_num',
  width: '100px'
}, {
  title: 'Sales',
  dataIndex: 'sold_num',
  bodyRender: (data: any, pos: any) => {
    // if (data.item_id === '5024217' || data.item_id === '1' || data.item_id === '1-1') {
    //   return {
    //     props: {
    //       rowSpan: 2
    //     },
    //     children: (
    //       <div>{data.sold_num}</div>
    //     )
    //   }
    // }
    // if (data.item_id === '502427722' || data.item_id === '2' || data.item_id === '1-2') {
    //   return {
    //     props: {
    //       rowSpan: 0
    //     }
    //   }
    // }
    return (
      <div>{data.sold_num}</div>
    );
  }
}];

const optionsData: RadioDataType = [
  {
    id: 'notLinkage',
    title: '不联动',
    content: '不联动',
    color: '#5D9CEC',
    tips: '父子节点勾选不联动 互相独立',
  },
  {
    id: 'linkageByTop',
    title: '自上向下联动(单向)',
    content: '自上向下联动(单向)',
    color: '#5D9CEC',
    tips: '父子节点勾选状态联动（自上向下联动），父节点被选中会连带其下级一并被勾选，子节点被选中不影响其父级节点的勾选状态（子节点作为独立数据存在）',
  },
  {
    id: 'linkageByBoth',
    title: '父子节点相互联动（双向）',
    content: '父子节点相互联动（双向）',
    color: '#5D9CEC',
    tips: '当所有子节点都被选中时，父节点也会被自动勾选（子节点勾选状态会影响父节点勾选）',
  },
];

export default class ExpandedDemo extends React.Component<{}, any> {
  state = {
    selectedRowKeys: [],
    datasets,
    expandRowKeys: [],
    linkageRule: 'notLinkage'
  };

  onChange = (conf: ITableOnChangeConfig) => {
    this.setState(conf);
  }

  onExpand = (conf: any) => {
    this.setState({ expandRowKeys: conf.expandRowKeys })
  }

  onSelect = (selectedRowKeys: string[], selectedRows: any, currentRow: any, type: any) => {
    console.log('onSelect', selectedRowKeys, selectedRows, currentRow, type)
    this.setState({
      selectedRowKeys,
    });
  }

  handleRadioChange = (value: any) => {
    if (!value) return;
    if (value !== this.state.linkageRule) {
      this.setState({ linkageRule: value, selectedRowKeys: [] });
    }
  }

  render() {
    const { selectedRowKeys, linkageRule } = this.state
    return (
      <div>
        <span style={{ margin: '15px 10px', display: 'inline-block', alignItems: 'center' }}>
          <Radio
           
            data={optionsData}
            optionType={'tag'}
            value={linkageRule}
            onChange={this.handleRadioChange}
          />
        </span>
        <Table
               columns={columns}
               data={this.state.datasets}
               onChange={this.onChange}
               rowKey="item_id"
               expandRowKeys={this.state.expandRowKeys}
               onExpand={this.onExpand}
               needExpand={true}
               useExpandLine={true}
               useExpandLineWithNum={false}
               customExpandIcon={[
                 <Icon size={'sm'} name={'Icon-Down-arrow04'} />,
                 <Icon size={'sm'} name={'Icon-Right-arrow04'} />
               ]}
               isShowIndexInCheck={false}
               selection={{
                 enableShiftSelect: true,
                 selectedRowKeys,
                 isLinkageBySelect: linkageRule !== 'notLinkage',
                 linkageRule: linkageRule === 'linkageByTop' ? 'top' : 'both',
                 onSelect: this.onSelect,
                 getSelectionProps: (data: any, rowIndex: string | number) => {
                  return {
                    visible: false,
                    disabled: data.item_id === '1-1-1' 
                  }
                 }
               }}
        />
      </div>
    )
  }
}
```

### 带操作菜单

```jsx
import React, { PureComponent } from 'react';
import { Table, ITableColumn, ITableCellPos, OperatesType, MenuItemData, Menu, Avatar } from '@weapp/ui';
import imgSrc from "../../../../assets/images/ODTLcjxAfvqbxHnVXCYX.png"

const columns = [
  {
    title: '姓名',
    dataIndex: 'name',
    bodyRender: getName
  }, {
    title: '联系方式',
    dataIndex: 'tel'
  }, {
    title: '部门',
    dataIndex: 'depart',
  }, {
    title: '多行文本',
    dataIndex: 'textarea',
  }, {
    title: '上级',
    dataIndex: 'leader',
    bodyRender: getLeader
  }, {
    title: '入职时间',
    dataIndex: 'join_time',
  }
];

function getName(data: any, pos: any, name: any) {
  const url = imgSrc;
  return (
    <span className={'ui-table-demo-name'}>
      <Avatar size='small' url={url} ></Avatar>
      <a style={{ marginLeft: '10px', color: '#5D9CEC' }}>{data[name]}</a>
    </span>
  )
}

function getLeader(data: any, pos: any, name: any) {
  return <span>{data[name]}</span>
}
const data = [{
  id: '1',
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}, {
  id: '2',
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
}, {
  id: '3',
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}, {
  id: '4',
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
}, {
  id: '5',
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}, {
  id: '6',
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
}, {
  id: '7',
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}, {
  id: '8',
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
}, {
  id: '9',
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}, {
  id: '10',
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
}];

interface OperateMenuProps {
  menuData: MenuItemData[];
  pos: ITableCellPos,
  data: object,
  onOperateClick?: (item: OperatesType, data: object, pos: ITableCellPos) => void
}

class OperateMenu extends PureComponent<OperateMenuProps, {}> {
  onItemClick = (id: string, item: any, e: React.MouseEvent) => {
    const { pos, onOperateClick, data } = this.props

    e.stopPropagation()
    e.preventDefault()

    onOperateClick?.(item, data, pos)
  }

  render() {
    const { menuData } = this.props
    return (
      <Menu
       
        value={''}
        data={menuData}
        type="select"
        // action={['click']}
        selectType="iconOverlay"
        bindTriggerContainer={false}
        triggerProps={{
          popupPlacement: 'bottomRight',
        }}
        onItemClick={this.onItemClick}
      />
    );
  }
}

export default class OperateDemo extends React.Component<{}, any> {
  state = {
    data: data,
  };

  getRowClassName = (data: any, index: any) => {
    let mergeNum = (index + 1) % 2 === 0 ? (index + 1) * 2 - 1 : (index + 1) * 2 + 1
    return `${(mergeNum + 1) / 4 % 2 === 0 ? 'gray-row' : ''}`
  }

  onOperateMenuWarpClick = (e: React.MouseEvent) => {
    e.stopPropagation()
    e.preventDefault()
  }

  getColumns = (columns: ITableColumn[]) => {
    // 操作菜单列
    const operatesColumn: ITableColumn = {
      align: "left",
      dataIndex: "operates",
      fixed: false,
      hide: false,
      newLine: true,
      orderKey: null,
      rowSpan: 0,
      textAlign: "left",
      title: '',
      isCustomTitle: true,
      width: '64px',
      className: 'demo-operate',
      bodyRender: (data: object, pos: ITableCellPos, name: string) => {
        const operates: MenuItemData[] = [{
          id: '1',
          content: "增加",
        },{
          id: '2',
          content: "删除",
        }]

        return (
          <span onClick={this.onOperateMenuWarpClick} style={{
            display: 'flex',
            alignItems: 'center',
            justifyContent: 'center'
          }}>
            <OperateMenu
             
              menuData={operates}
              pos={pos}
              data={data}
            />
          </span>
        )
      }
    }

    return [ ...columns, operatesColumn ]
  }

  render() {
    return (
      <Table
             disableHoverHighlight
             columns={this.getColumns(columns)}
             data={this.state.data}
             rowKey='key'
             className={'demo-table-grid'}
      />
    )
  }
}
```

### 列宽拖拽

提供两种拖拽交互方式 通过autoScrollInResize配置

```jsx
import React from 'react';
import { Table, ITableColumn } from '@weapp/ui';

interface TableState {
  columns: ITableColumn[];
  scrollX: number;
}

interface ResizeDemoState {
  tableStates: Record<number, TableState>;
  needSetScroll: boolean;
}

const columns: ITableColumn[] = [
  { title: '全部', dataIndex: 'name', width: 240 },
  { title: '待处理', dataIndex: 'age', width: 340 },
  { title: '转发', dataIndex: 'address' },
  { title: '抄送', dataIndex: 'send' }
];

const data = [
  { name: '12', age: '6', address: '4', send: '2' },
  { name: '32', age: '20', address: '8', send: '4' },
  { name: '12', age: '6', address: '4', send: '2' }
];

export default class ResizeDemo extends React.Component<{}, ResizeDemoState> {
  state: ResizeDemoState = {
    tableStates: {
      1: { columns: [...columns], scrollX: 1600 },
      2: { columns: [...columns], scrollX: 0 },
      3: { columns: [...columns], scrollX: 1600 }
    },
    needSetScroll: false
  };

  handleResizeEnd = (index: number) => (
    columns: ITableColumn[],
    _dragIndex: number,
    needSetScroll: boolean,
    widthAccumulate: number
  ): void => {
    this.setState(prevState => ({
      tableStates: {
        ...prevState.tableStates,
        [index]: { columns, scrollX: widthAccumulate }
      },
      needSetScroll
    }));
  };

  onHandleResizeChange = (index: number) => (
    columns: ITableColumn[],
    _dragIndex: number,
    widthAccumulate: number,
  ): void => {
    console.log('onHandleResizeChange', columns, _dragIndex, widthAccumulate);
  };

  renderTable = (index: number, autoScrollInResize: boolean, hasScrollX: boolean): JSX.Element => {
    const { tableStates } = this.state;
    const { columns, scrollX } = tableStates[index];

    return (
      <Table
        data={data}
        resizable
        columns={columns}
        onHandleResizeEnd={this.handleResizeEnd(index)}
        onHandleResizeChange={this.onHandleResizeChange(index)}
        autoScrollInResize={autoScrollInResize}
        scroll={{ x: hasScrollX ? scrollX : undefined, y: 160 }}
      />
    );
  };

  render(): JSX.Element {
    return (
      <div>
        <h4>autoScrollInResize 默认为true，列宽拖拽 自适应撑开横向滚动 模式</h4>
        {this.renderTable(1, true, true)}
        <h4>autoScrollInResize 设置为false，列宽拖拽 互相挤占 模式</h4>
        {this.renderTable(2, false, false)}
        <h4>autoScrollInResize 设置为false，列宽拖拽 互相挤占 模式（带scroll.x）</h4>
        {this.renderTable(3, false, true)}
      </div>
    );
  }
}
```

### 表头分组拖拽

表头分组拖拽场景需要每列width设置固宽（比如：100 或者 '100px'）

```jsx
import React from 'react';
import { Table, Empty, Icon, ITableColumn } from '@weapp/ui';

const datasets: any[] = [];

for (let i = 0; i < 6; i++) {
  datasets.push({
    id: `id-${i}`,
    name: `商品 ${i}`,
    type: `type-${i}`,
    company: `company-${i}`,
    phone: `123342345${i}`,
    stock: 5,
    createdTime: '2018-12-11'
  });
}

interface TableState {
  columns: ITableColumn[];
  scrollX: number;
}

interface ResizeDemoState {
  tableStates: Record<number, TableState>;
}

const defaultState = {
  scrollX: 1200,
  columns: [
    {
      title: '商品名',
      dataIndex: 'name',
      className: 'name',
      width: 100,
      fixed: true,
    },
    {
      title: '商品信息',
      dataIndex: 'productInfo',
      width: 800,
      children: [
        {
          title: '类型',
          dataIndex: 'type',
          width: 200,
        },
        {
          title: '供货商',
          dataIndex: 'supplier',
          width: 600,
          children: [
            {
              title: '公司',
              dataIndex: 'company',
              width: 300,
            },
            {
              title: '电话',
              dataIndex: 'phone',
              width: 300,
            },
          ]
        },
      ],
    },
    {
      title: '库存',
      dataIndex: 'stock',
      defaultText: 0,
      width: 200,
    },
    {
      title: '创建时间',
      dataIndex: 'createdTime',
      width: 100,
    }
  ]
}

export default class HeaderChildrenDemo extends React.Component<{}, ResizeDemoState> {
  state: ResizeDemoState = {
    tableStates: {
      1: defaultState,
      2: defaultState,
    },
  }

  handleResizeEnd = (index: number) => (
    columns: ITableColumn[],
    _dragIndex: number,
    needSetScroll: boolean,
    widthAccumulate: number
  ): void => {
    console.log('handleResizeEnd', { columns, scrollX: widthAccumulate })
    this.setState(prevState => ({
      tableStates: {
        ...prevState.tableStates,
        [index]: { columns, scrollX: widthAccumulate }
      },
    }));
  };

  onHandleResizeChange = (index: number) => (
    columns: ITableColumn[],
    _dragIndex: number,
    widthAccumulate: number,
  ): void => {
    console.log('onHandleResizeChange', { columns, _dragIndex, widthAccumulate });
  };

  renderTable = (index: number, autoScrollInResize: boolean): JSX.Element => {
    const { tableStates } = this.state;
    const { columns, scrollX } = tableStates[index];

    return (
      <Table
             data={datasets}
             resizable
             columns={columns}
             onHandleResizeEnd={this.handleResizeEnd(index)}
             onHandleResizeChange={this.onHandleResizeChange(index)}
             autoScrollInResize={autoScrollInResize}
             scroll={{ x: scrollX }}
             bordered
      />
    );
  };

  render() {
    return (
      <div>
        <h4>autoScrollInResize 默认为true，列宽拖拽 自适应撑开横向滚动 模式</h4>
        {this.renderTable(1, true)}
        <h4>autoScrollInResize 设置为false，列宽拖拽 互相挤占 模式</h4>
        {this.renderTable(2, false)}
      </div>
    )
  }
}
```

### 水平滚动

建议指定 scroll.x 为大于表格宽度的固定值。注意，且非固定列宽度之和不要超过 scroll.x。若列头与内容不对齐或出现列重复，请指定列的宽度 width。

```jsx
import React from 'react';
import { Table, ITableColumn } from '@weapp/ui';

const columns: ITableColumn[] = [
  {
    title: '会议',
    dataIndex: 'name',
    width: 240
  }, {
    title: '会议人数',
    dataIndex: 'num',
    width: 120
  },{
    title: '会议详情',
    dataIndex: 'content',
  }, {
    title: '签到数',
    dataIndex: 'sold_num',
  }, {
    title: '通知时间',
    dataIndex: 'sent_time',
  }, {
    title: '会议时间',
    dataIndex: 'created_time',
  }
];

const data: any[] = [];

for (let i = 0; i < 5; i++) {
  data.push({
    id: i,
    name: `前端组件周例会_${i}`,
    content: 'xxxxxxxxxxx',
    num: 12,
    sold_num: 12,
    sent_time: '2021-03-31',
    created_time: '2021-04-01'
  })
}

export default class ScrollXDemo extends React.Component<{}, any> {
  onScroll = (e: any) => {
    console.log(e, e.target.scrollLeft)
  }

  getRowClassName = (data: any, index: any) => {
    return `${(index + 1) % 2 === 0 ? '' : 'gray-row'}`
  }

  render() {
    return (
      <Table
             columns={columns}
             data={data}
             scroll={{ x: 2160 }}
             onScroll={this.onScroll}
             bordered={true}
      />
    )
  }
}
```

### 垂直滚动

若列头与内容不对齐或出现列重复，请指定列的宽度 width。如果指定 width 不生效或出现白色垂直空隙，请尝试建议留一列不设宽度以适应弹性布局，或者检查是否有超长连续字段破坏布局。

```jsx
import React from 'react';
import { Table, ITableColumn } from '@weapp/ui';

const columns: ITableColumn[] = [
  {
    title: '会议',
    dataIndex: 'name',
    width: '18%'
  }, {
    title: '会议人数',
    dataIndex: 'num',
    width: 180
  },{
    title: '会议详情',
    dataIndex: 'content',
    width: '15%'
  }, {
    title: '签到数',
    dataIndex: 'sold_num',
    width: '15%'
  }, {
    title: '通知时间',
    dataIndex: 'sent_time',
  }, {
    title: '会议时间',
    dataIndex: 'created_time',
    width: '15%'
  }
];

const data: any[] = [];

for (let i = 0; i < 26; i++) {
  data.push({
    id: i,
    name: `前端组件周例会_${i}`,
    content: 'xxxxxxxxxxx',
    num: 12,
    sold_num: 12,
    sent_time: '2021-03-31',
    created_time: '2021-04-01'
  })
}

export default class ScrollYDemo extends React.Component<{}, any> {
  onScroll = (e: any) => {
    // console.log(e, e.target.scrollTop)
  }

  getRowClassName = (data: any, index: any) => {
    return `${(index + 1) % 2 === 0 ? '' : 'gray-row'}`
  }

  render() {
    return (
      <Table
             columns={columns}
             data={data}
             scroll={{ y: 260 }}
             onScroll={this.onScroll}
             bordered={true}
      />
    )
  }
}
```

### 水平垂直滚动

```jsx
import React from 'react';
import { Table, ITableColumn } from '@weapp/ui';

const columns: ITableColumn[] = [
  {
    title: '会议',
    dataIndex: 'name',
    width: '18%'
  }, {
    title: '会议人数',
    dataIndex: 'num',
    width: '15%'
  },{
    title: '会议详情',
    dataIndex: 'content',
    width: '15%'
  }, {
    title: '签到数',
    dataIndex: 'sold_num',
    width: '15%'
  }, {
    title: '通知时间',
    dataIndex: 'sent_time',
    width: '15%'
  }, {
    title: '会议时间',
    dataIndex: 'created_time',
  }
];

const data: any[] = [];

for (let i = 0; i < 16; i++) {
  data.push({
    id: i,
    name: `前端组件周例会_${i}`,
    content: 'xxxxxxxxxxx',
    num: 12,
    sold_num: 12,
    sent_time: '2021-03-31',
    created_time: '2021-04-01'
  })
}

export default class ScrollDemo extends React.Component<{}, any> {
  onScroll = (e: any) => {
    console.log(e, e.target.scrollLeft, e.target.scrollTop)
  }

  getRowClassName = (data: any, index: any) => {
    return `${(index + 1) % 2 === 0 ? '' : 'gray-row'}`
  }

  render() {
    return (
      <Table
             columns={columns}
             data={data}
             scroll={{ x: 1890, y: 360 }}
             onScroll={this.onScroll}
             bordered={true}
      />
    )
  }
}
```

### 指定滚动

支持纵向和横向滚动，并根据index 或 offset 指定滚动距离

```jsx
import React from 'react';
import { Table, ITableColumn, Button } from '@weapp/ui';

const columns: ITableColumn[] = [
  {
    title: '会议',
    dataIndex: 'name',
    width: '18%'
  }, {
    title: '会议人数',
    dataIndex: 'num',
    width: '15%'
  },{
    title: '会议详情',
    dataIndex: 'content',
    width: '15%'
  }, {
    title: '签到数',
    dataIndex: 'sold_num',
    width: '15%'
  }, {
    title: '通知时间',
    dataIndex: 'sent_time',
    width: '15%'
  }, {
    title: '会议时间',
    dataIndex: 'created_time',
  }
];

const data: any[] = [];

for (let i = 0; i < 16; i++) {
  data.push({
    id: i,
    name: `前端组件周例会_${i}`,
    content: 'xxxxxxxxxxx',
    num: 12,
    sold_num: 12,
    sent_time: '2021-03-31',
    created_time: '2021-04-01'
  })
}

export default class ScrollDemo extends React.Component<{}, any> {
  scrollTable: any

  onScroll = (e: any) => {
    console.log(e, e.target.scrollLeft, e.target.scrollTop)
  }

  getRowClassName = (data: any, index: any) => {
    return `${(index + 1) % 2 === 0 ? '' : 'gray-row'}`
  }

  getScrollTableRef = (ref: any) => {
    this.scrollTable = ref
  }

  scrollToIndex3 = () => {
    this.scrollTable.scrollToTablePosition({ index: 3, direction: 'Top' })
  }

  scrollToDataIndex = () => {
    this.scrollTable.scrollToTablePosition({ index: 'content', direction: 'Left' })
  }

  scrollTo220 = () => {
    this.scrollTable.scrollToTablePosition({ offset: 220, direction: 'Top' })
  }

  render() {
    return (
      <div>
        <div style={{ padding: '10px 0'}}>
          <Button onClick={this.scrollToIndex3}>纵向滚动到第三行</Button>
          <Button style={{ marginLeft: 10 }} onClick={this.scrollToDataIndex} >横向滚动到“会议详情”列</Button>
          <Button style={{ marginLeft: 10 }} onClick={this.scrollTo220}>纵向滚动220px</Button>
        </div>
        <Table
               ref={this.getScrollTableRef}
               columns={columns}
               data={data}
               scroll={{ x: 1890, y: 360 }}
               onScroll={this.onScroll}
               bordered={true}
        />
      </div>
    )
  }
}
```

### 懒加载

指定滚动监听容器，将对列表进行懒加载处理，优化首屏渲染性能

```jsx
import React from 'react';
import { Table, Avatar, Dialog, ITableColumn, Switch } from '@weapp/ui';
import imgSrc from "../../../../assets/images/ODTLcjxAfvqbxHnVXCYX.png"
const { message } = Dialog;

const columns: ITableColumn[] = [
  {
    title: '姓名',
    dataIndex: 'name',
    isPrimaryKey: true,
    bodyRender: getName,
  }, {
    title: '联系方式',
    dataIndex: 'tel'
  }, {
    title: '部门',
    dataIndex: 'depart',
  },{
    title: '多行文本',
    dataIndex: 'textarea',
  },{
    title: '上级',
    dataIndex: 'leader',
    bodyRender: getLeader
  },{
    title: '入职时间',
    dataIndex: 'join_time',
  }
];

function getName(data: any, pos: any, name: any) {
  const url = imgSrc;
  return (
    <span className={'ui-table-demo-name'}>
      <Avatar size='small' url={url} ></Avatar>
      <a style={{ marginLeft: '10px', color: '#5D9CEC' }}>{data[name]}</a>
    </span>
  )
}

function getLeader(data: any, pos: any, name: any) {
  return <a>{data[name]}</a>
}

const data = [{
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
},{
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
},{
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
},{
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
},{
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
},{
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
},{
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
},{
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
},{
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
},{
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
},{
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}];

export default class BasicDemo extends React.Component<{}, any> {
  state = {
    current: 1,
    pageSize: 5,
    data: data,
    isShowHead: true,
    loading: false,
    isShowIndex: true,
    bordered: false
  };

  onRowClick = (data: any, index: any, event: any) => {
    console.log('onRowClick', data, index, event.target)
  }

  onRowMouseEnter = (a: number, b: object) => {
    console.log('onRowMouseEnter', a, b)
  }

  onChange = (value: number, pageSize: number) => {
    this.setState({ current: value, pageSize });
    message({
      type: 'success', // type: 'info', 'error', 'success'
      content: `value: ${value}, pageSize: ${pageSize}`,
    });
  }

  onSizeChange = (value: number, pageSize: number) => {
    this.setState({ current: value, pageSize })
    message({
      type: 'success', // type: 'info', 'error', 'success'
      content: `value: ${value}, pageSize: ${pageSize}`,
    });
  }

  getPageInfo = () => {
    const { current, pageSize, data } = this.state

    return {
      value: current,
      pageSize: pageSize,
      total: data.length,
      showSizeChanger: true,
      showTotal: false,
      showJumper: false,
      pageSizeOptions: [5, 10],
      onChange: this.onChange,
      onSizeChange: this.onSizeChange
    }
  }

  getLazyLoadContainer = () => {
    return document.getElementById('demo_content_scroll');
  }

  render() {
    const { data, isShowHead, loading, isShowIndex, bordered } = this.state
    return (
      <div>
        <Table
         
          columns={columns}
          data={data}
          pageInfo={this.getPageInfo()}
          onRowClick={this.onRowClick}
          isShowHead={isShowHead}
          loading={loading}
          isShowIndex={isShowIndex}
          bordered={bordered}
          lazyLoad={true}
          getLazyLoadContainer={this.getLazyLoadContainer}
        />
      </div>
    )
  }
}
```

### 固定列

被设置固定fixed的列，须指定固宽。除此之外，若设置scroll滚动，则在出现列头与内容不对齐或出现列重复的现象，也请指定非固定列的宽度 width。

```jsx
import React from 'react';
import { Table, Avatar, ITableColumn } from '@weapp/ui';
import imgSrc from "../../../../assets/images/ODTLcjxAfvqbxHnVXCYX.png"

const columns: ITableColumn[] = [
  {
    title: '姓名',
    dataIndex: 'name',
    fixed: 'left',
    width: 120,
    bodyRender: getName
  }, {
    title: '联系方式',
    width: 120,
    fixed: 'left',
    dataIndex: 'tel'
  }, {
    title: '部门',
    width: 120,
    dataIndex: 'depart',
  },{
    title: '多行文本',
    width: 120,
    dataIndex: 'textarea',
  },{
    title: '上级',
    dataIndex: 'leader',
    // fixed: 'right',
    width: 120,
    bodyRender: getLeader
  },{
    title: '入职时间',
    fixed: 'right',
    width: '320',
    dataIndex: 'join_time',
  }
];

function getName(data: any, pos: any, name: any) {
  const url = imgSrc;
  return (
    <span className={'ui-table-demo-name'}>
      <Avatar size='small' url={url} ></Avatar>
      <a style={{ marginLeft: '10px', color: '#5D9CEC' }}>{data[name]}</a>
    </span>
  )
}

function getLeader(data: any, pos: any, name: any) {
  return <a>{data[name]}</a>
}

const data = [{
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
},{
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
},{
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
},{
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
},{
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
},{
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
},{
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
},{
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
},{
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
},{
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
},{
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}];

export default class FixedDemo extends React.Component<{}, any> {

  getRowClassName = (data: any, index: any) => {
    return `${(index + 1) % 2 === 0 ? '' : 'gray-row'}`
  }

  render() {
    return (
      <Table
             columns={columns}
             data={data}
        scroll={{ x: 920, y: 200 }}
             bordered={true}
      />
    )
  }
}
```

### 表头吸顶+横向滚动条吸底

1、sticky的使用有一定要求。基本逻辑：使用了sticky 属性的元素，会向上寻找最近的滚动组件并根据它进行固定。如果它中途的父级元素存在，overflow:hidden/scroll/auto/overlay时，sticky的行为将会被抑制。得不到页面效果,2、表头吸顶：浏览器支持sticky方案的场景下采用css sticky 属性的方案处理，其余场景采用js方案处理，滚动监听计算将表头fixed定位吸顶。,3、横向滚动条吸底：只支持sticky属性的浏览器场景。暂不考虑js方案

```jsx
import React from 'react';
import { Avatar, Table, Switch } from '@weapp/ui';
import imgSrc from "../../../../assets/images/ODTLcjxAfvqbxHnVXCYX.png"

const columns = [
  {
    title: '姓名',
    dataIndex: 'name',
    bodyRender: getName
  }, {
    title: '联系方式',
    dataIndex: 'tel'
  }, {
    title: '部门',
    dataIndex: 'depart',
  }, {
    title: '多行文本',
    dataIndex: 'textarea',
  }, {
    title: '上级',
    dataIndex: 'leader',
    bodyRender: getLeader
  }, {
    title: '入职时间',
    dataIndex: 'join_time',
  }
];

function getName(data: any, pos: any, name: any) {
  const url = imgSrc;
  return (
    <span className={'ui-table-demo-name'}>
      <Avatar size='small' url={url} ></Avatar>
      <a style={{ marginLeft: '10px', color: '#5D9CEC' }}>{data[name]}</a>
    </span>
  )
}

function getLeader(data: any, pos: any, name: any) {
  return <span>{data[name]}</span>
}

const data = [{
  id: '1',
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}, {
  id: '2',
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
}, {
  id: '3',
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}, {
  id: '4',
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
}, {
  id: '5',
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}, {
  id: '6',
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
}, {
  id: '7',
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}, {
  id: '8',
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
}, {
  id: '9',
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}, {
  id: '10',
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
}, {
  id: '16',
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
}, {
  id: '17',
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}, {
  id: '18',
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
}, {
  id: '9',
  name: '张天逸',
  tel: '15199398787',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '王紫博',
  join_time: '2021-06-23'
}, {
  id: '110',
  name: '彭浩秋',
  tel: '16179993456',
  depart: '泛微-北方大区',
  textarea: '设计师',
  leader: '张华',
  join_time: '2021-08-21'
}];

export default class StickyDemo extends React.Component<{}, any> {
  state = {
    selectedRowKeys: ['1'],
    data: data,
  };

  getRowClassName = (data: any, index: any) => {
    return `${(index + 1) % 2 === 0 ? '' : 'gray-row'}`
  }

  onSelect = (selectedRowKeys: string[], selectedRows: any, currentRow: any, type: any) => {
    this.setState({ selectedRowKeys });
  }

  getAutoStickyContainer = () => {
    return document.querySelector('#demo_content_scroll')
  }

  render() {
    return (
      <div>
        <Table
               columns={columns}
               data={this.state.data}
               selection={{
                 selectedRowKeys: this.state.selectedRowKeys,
                 isBatchSelect: true,
                 getSelectionProps: (data: object, rowIndex: number | string) => {
                   return {
                     visible: true,
                     disabled: rowIndex == 4,
                   }
                 },
                 onSelect: this.onSelect
               }}
               scroll={{ x: 1890, y: 380 }}
               autoHeaderStickyContainer={this.getAutoStickyContainer}
               autoHeaderSticky
               autoScrollerSticky
        />
      </div>
    )
  }
}
```

### 虚拟列表

基于大数据量下的复杂渲染列表，支持同时渲染10w+数据

```jsx
import React from 'react';
import { Table, Avatar, Dialog, ITableColumn, RadioValueType, ITablePaginationType } from '@weapp/ui';
import imgSrc from "../../../../assets/images/ODTLcjxAfvqbxHnVXCYX.png"
const { message } = Dialog;

const columns: ITableColumn[] = [
  {
    title: '姓名',
    dataIndex: 'name',
    width: '300px',
    fixed: true,
    bodyRender: getName,
  }, {
    title: '联系方式',
    width: '320px',
    bodyRender: getTime,
    dataIndex: 'tel'
  }, {
    title: '部门',
    width: 250,
    dataIndex: 'depart',
  }, {
    title: '多行文本',
    width: 450,
    dataIndex: 'textarea',
  }, {
    title: '上级',
    dataIndex: 'leader',
    width: 150,
    bodyRender: getLeader
  }, {
    title: '入职时间',
    width: 250,
    dataIndex: 'join_time',
  }
];

function getTime(data: string, pos: any, name: any){
  const { row }  = pos;
  if (row === 0 || row % 5 === 0) {
    return {
      props: {
        rowSpan: 5,
        rowSpanIndex: 0,
        rowSpanNum: 5
      },
      children: (
        <div style={{ textAlign:'center' }}>
          合并内容
        </div>
      )
    }
  } else {
    return {
      props: {
        rowSpan: 0,
        rowSpanIndex: row > 5 ? row % 5 : row,
        rowSpanNum: 5
      },
      // 不管被不被合并 都传一下当前应该显示的内容
      children: (
        <div style={{ textAlign:'center' }}>
          合并内容
        </div>
      )
    }
  }
}

function getName(data: any, pos: any, name: any) {
  const url = imgSrc;
  return (
    <span className={'ui-table-demo-name'}>
      <Avatar size='small' url={url} ></Avatar>
      <a style={{ marginLeft: '10px', color: '#5D9CEC' }}>{data[name]}</a>
    </span>
  )
}

function getLeader(data: any, pos: any, name: any) {
  return <span>{data[name]}</span>
}

const data: any[] = [];
for (let i = 0; i < 500000; i++) {
  data.push({
    key: i,
    id: i,
    name: `李大嘴__${i+1}`,
    tel: i+1,
    depart: `开发${i+1}部`,
    textarea: '设计师',
    leader: '王紫博',
    join_time: '2021-06-23'
  });
}

export default class VirtualizedModeDemo extends React.Component<{}, any> {
  state = {
    current: 1,
    pageSize: 50000,
    data: data,
    paginationType: 'part',
    hasMore: true
  };

  getRowClassName = (data: any, index: any) => {
    return `${(index + 1) % 2 === 0 ? '' : 'gray-row'}`
  }

  handleChange = (v: RadioValueType) => {
    this.setState({ paginationType: v })
  }

  onPageChange = (value: number, pageSize: number) => {
    const { paginationType } = this.state

    this.setState({
      current: value,
      hasMore: (paginationType != 'part' && value * pageSize >= data.length) ? false : true,
      pageSize
    });

    message({
      type: 'success', // type: 'info', 'error', 'success'
      content: `value: ${value}, pageSize: ${pageSize}`,
    });
  }

  getPageInfo = () => {
    const { current, pageSize, data, paginationType } = this.state

    return {
      paginationType: paginationType as ITablePaginationType,
      value: current,
      pageSize: pageSize,
      total: data.length,
      showJumper: false,
      onChange: this.onPageChange,
      pageSizeOptions: [10000, 50000, 100000, 200000]  //  分页最大20w条数据
    }
  }

  onRowClick = (data: any, index: number, event: React.MouseEvent<HTMLTableRowElement>, isCtrlPress: boolean) => {
    console.log(data, index, event, isCtrlPress)
  }

  render() {
    const { data, hasMore } = this.state
    return (
      <div>
        <div style={{ margin: '15px 0' }}>
          <Table
                 columns={columns}
                 data={data}
                 scroll={{ x: 1720, y: 560 }}
                 pageInfo={this.getPageInfo()}
                 hasMore={hasMore}
                 virtualMode
                 bordered
                 scrollBarInBody
                 onRowClick={this.onRowClick}
          />
        </div>
      </div>
    )
  }
}
```

## API

### 基础参数说明：

| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
| --- | --- | --- | --- | --- |
| columns | 列配置 | ITableColumn[] | 是 | - |
| data | 数据 | ReadonlyArray | 是 | - |
| rowKey | 每一行的 key | string | 否 | 'id' |
| className | 样式名 | string | 否 | - |
| style | 行内样式 | CSSProperties | 否 | - |
| size | 大小 | 'medium' \| 'small' \| 'large' | 否 | 'medium' |
| rowClassName | 表格行的类名 | string | 否 | - |
| emptyLabel | 列表为空时提示的文案 | React.ReactNode | 否 | '没有更多数据了' |
| scroll | 滚动条配置 | `{x?: number;y?: number;}` | 否 | - |
| needScroll | 是否组件内自适应计算 横向滚动（固定列、分组表头下无法使用） | boolean | 否 | false |
| autoUseSticky | 根据浏览器自动识别使用固定列sticky方案，可设置为false 强制关闭 | boolean | 否 | true |
| loading | 显示 loading | boolean | 否 | - |
| ellipsis | 是否需要文字超出宽度后省略号显示 (需配置 columns 中的 newLine) | boolean | 否 | - |
| bordered | 是否有边框 | boolean | 否 | - |
| selection | 选择框配置 | ITableSelection | 否 | - |
| pageInfo | 分页配置 | PaginationProps & { paginationType?: ITablePaginationType} | 否 | - |
| hasMore | more 分页模式下是否还有更多 | boolean | 否 | - |
| isRememberPageSize | pageSize 切换时是否存储本地缓存 | boolean | 否 | - |
| moreBtnTextCustomRender | 自定义渲染加载更多文本内容 | (type: 'ready' \| 'pending' \| 'finish') => React.ReactNode | 否 | - |
| onRowClick | 行点击事件 | ITableRowClickHandler: (data: Data, index: number,event: React.MouseEvent, isCtrlPress: boolean) => any | 否 | - |
| onExpand | 展开事件 | ITableOnExpandHandler: (expanded: boolean, data: Data, event: React.MouseEvent, index: number,) => any | 否 | - |
| expandRowKeys | 展开 key 受控 | string[] | 否 | - |
| needExpand | 是否开启展开功能 | boolean | 否 | false |
| convertToTree | 是否转换成树形结构 （支持外部传入平铺结构） | boolean | 否 | false |
| useExpandLine | 树形子列表展开采用连线模式（注意！！首列必须使用bodyRender渲染处理） | boolean | 否 | false |
| expandRender | 自定义渲染展开内容 | (data: any) => React.ReactNode | 否 | - |
| expandBodyRenderProps | 自定义配置展开列参数 | (data: Data,pos: ITableCellPos,name: string \| undefined,renderIndex?: number) => object | 否 | - |
| customExpandIcon | 自定义渲染展开图标 第一项：展开图标 第二项：收缩图标 | React.ReactNode[] | 否 | - |
| isExpandAsync | 是否存在异步展开 | boolean | 否 | false |
| isShowExpandColumn | 展开下是否需要显示展开列 | boolean | 否 | true |
| indentWidth | 展开列 宽度 (异步数据时用) | number | 否 | - |
| onRowMouseEnter | 鼠标移入行回调事件 | (mouseOverRowIndex: number, config: object) => any | 否 | - |
| sortable | 是否启用拖拽 | boolean | 否 | false |
| sortableType | 拖拽类型 | 'row' \| 'icon' | 否 | - |
| sortableIcon | 拖拽图标自定义 | ReactNode | 否 | - |
| sortableProps | 拖拽配置参数（可覆盖其他拖拽配置） | object(详情见 sortable 组件 api 说明) | 否 | - |
| onDrag | 拖拽回调事件 | (data: any[], evt: any) => any | 否 | - |
| customMove | 在不改变原有拖拽onMove方法的基础上，增加自定义事件 | (evt: any) => boolean | 否 | — |
| onScroll | 滚动回调事件 | (e: any) => void | 否 | - |
| getScrollContainer | 指定滚动容器 | () => HTMLDivElement \| Element \| HTMLElement | 否 | - |
| onSort | 排序回调事件 | (conf: ITableOnSortConfig) => any | 否 | - |
| isShowHead | 是否显示表头 | boolean | 否 | true |
| isShowIndex | 是否显示前列序号 | boolean | 否 | false |
| indexColumnWidth | 序号列宽度自定义 | React.CSSProperties['width'] | 否 | - |
| getSummaryProps | 尾行合计配置 | SummaryProps | 否 | - |
| isShowHeaderInCheck | 多选时是否显示表头标题 | boolean | 否 | true |
| isShowIndexInCheck | 多选时是否显示序号 | boolean | 否 | true |
| indexColumnBodyRender | 序号列自定义渲染函数 | ITableColumnBodyRenderFunc | 否 | - |
| customFooterRender | 表格底部自定义渲染内容 | React.ReactNode | 否 | - |
| scrollBarInBody | 滚动条是否放置滚动 body 内（只有当垂直滚动且非固定列场景下生效） | boolean | 否 | false |
| onHandleResizeEnd | 拖拽列宽后的回调函数 | (columns: ITableColumn[], dragIndex: number, needSetScroll: boolean, widthAccumulate: number) => void | 否 | - |
| resizable | 是否开启列宽拖拽 | boolean | 否 | false |
| autoScrollInResize | 是否开启自动拖拽撑开 横向滚动(默认开启) | boolean | 否 | true |
| virtualMode | 是否开启虚拟列表模式 | boolean | 否 | false |
| virtualModeProps | 虚拟列表配置参数 | IVirtualModeProps | 否 | - |
| useSkeleton | 是否开启骨架屏 | boolean | 否 | false |
| skeletonProps | 骨架屏配置参数 | SkeletonProps | 否 | - |
| defaultSkeletonRow | 骨架屏默认渲染行数 | number | 否 | 10 |
| autoScrollerSticky | 是否横向滚动条吸底 | boolean | 否 | false |
| autoScrollerStickyOffsetTop | 自定义滚动条吸底的 offsetTop | number | 否 | 0 |
| autoHeaderSticky | 是否自动将 head stick 到窗口 | boolean | 否 | false |
| autoHeaderStickyContainer | 指定吸顶父级容器 默认为window对象 | () => HTMLDivElement \| Element \| HTMLElement | 否 | false |
| autoHeaderStickyOffsetTop | 自定义表头吸顶的 offsetTop | number | 否 | 0 |
| lazyLoad | 是否开启懒加载 | boolean | 否 | false |
| getLazyLoadContainer | 指定懒加载监听滚动容器 默认为window对象 | () => HTMLDivElement \| Element \| HTMLElement | 否 | false |
| disabledHTMLDisplay | 是否禁用单元格html片段显示 | boolean | 否 | false |
| enableSortableColumnEdit | 是否开启编辑排序功能 | boolean | 否 | false |
### columns 列配置 参数说明：

| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
| --- | --- | --- | --- | --- |
| title | 列头显示文字 | React.ReactNode | 是 | - |
| dataIndex | 列数据在数据项中对应的 key | string | 否 | - |
| width | 列宽度 | React.CSSProperties['width'] | 否 | - |
| bodyRender | 生成复杂数据的渲染函数 | ITableColumnBodyRenderFunc | 否 | - |
| disableBodyRenderSpan | bodyRender/customBodyRender 场景下是否移除 td 内部默认 span 包裹 | boolean | 否 | false |
| summaryRender | 自定义渲染合计内容 | (data: readonly Data[], column: ITableInnerColumn) => React.ReactNode> | 否 | - |
| className | 列的 className | string | 否 | - |
| colSpan | 表头列合并,设置为 0 时，不渲染 | number | 否 | - |
| fixed | 列是否固定，可选 true(等效于 left) 'left' 'right' | TableFixedType | 否 | - |
| textAlign | 文本对齐方式 | TableTextAlign | 否 | - |
| align | 表头文本对齐方式 | TableTextAlign | 否 | - |
| newLine | 是否换行 默认换行 | boolean | 否 | true |
| defaultText | 默认显示文字 | React.ReactNode | 否 | - |
| children | 渲染分组表头 | Array<ITableColumn> | 否 | - |
| orderKey | 排序字段 | string | 否 | - |
| hide | 是否隐藏 | boolean | 否 | - |
| isPrimaryKey | 是否为主标题字段（color: #333333, 非主标题字段 color: #666666） | boolean | 否 | false |
| isCustomTitle | 是否自定义单元格内容标题 | boolean | 否 | false |
| disabledResize | 是否禁用拖拽列宽 | boolean | 否 | false |
| customRenderTh | 自定义标题内容 | (columns: ITableColumn[], dom:React.ReactNode, headerProps: ITableHeaderProps) => void | 否 | - |
| isLocaleDate | 是否根据服务器时间 转换为 客户端时间 | boolean | 否 | true |
### selection 选择框配置 参数说明：

| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
| --- | --- | --- | --- | --- |
| isSingleSelection | 是否单选 | boolean | 否 | - |
| isBatchSelect | 是否开启批量全选 | boolean | 否 | - |
| enableShiftSelect | 是否开启滑动多选 | boolean | 否 | false |
| isLinkageBySelect | 是否开启选择节点上下级联动 | boolean | 否 | false |
| linkageRule | 上下级联动规则（默认单向 自上向下） | 'top' \| 'both' | 否 | - |
| selectionWidth | 选择列宽度 | string | 否 | - |
| selectedRowKeys | 选中受控 key | string[] | 否 | - |
| onSelect | check 触发回调事件 | (selectedkeys: string[],selectedRows: Data[],changeRow: Data \| Data[]， type: 'normal' \| 'batchSelectAll' \| 'batchRemoveAll' ) => any | 否 | - |
| getSelectionProps | 选择框/单选框 属性配置 | (data: Data, rowIndex: number \| string) => any \| { visible: boolean, disabled: boolean }[] | 否 | - |
| customRenderSelectionTitle | 自定义渲染选择列标题 | (title: ReactNode, dataIndex: string) => ReactNode | 否 | - |
| customRenderSelectionColumn | 自定义渲染选择列内容 | (columnBody: ReactNode, data: Data, pos: ITableCellPos) => ReactNode | 否 | - |
### pageInfo 分页配置 参数说明：

| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
| --- | --- | --- | --- | --- |
| PaginationProps | 分页器配置，详情见 组件 Pagination API 说明 | PaginationProps | 否 |  |
| paginationType | 分页模式 ITablePaginationType('part'/'more'/'scroll') | paginationType | 否 | 'part' |
### bodyRender 列复写函数 说明：

##### 使用方式 ITableColumnBodyRenderFunc  : (data: Data, pos: ITableCellPos, name?: string ) => React.ReactNode

| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
| --- | --- | --- | --- | --- |
| data | 当前行数据 |  |  |  |
| pos | 位置信息 ITableCellPos（row、column、fixed） |  |  |  |
| name | 列 key 值 对应 dataIndex |  |  |  |
### SummaryProps 尾行合计配置 参数说明：

| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
| --- | --- | --- | --- | --- |
| isShowSummary | 是否开启尾行合计 | boolean |  |  |
| summaryCount | 插入尾行合计数（当前页合计、总合计） | number |  | 1 |
### scrollToTablePosition 滚动行数 说明：

| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| index | 目标行号（横向从0开始 纵向选取对应列的dataIndex）当同时设置 `index` 和 `offset` 时，优先使用 `index` | number \| string | - |
| offset | 滚动距离（像素） | number | - |
| direction | 滚动方向 | 'Left' \| 'Top' |  |
### IVirtualModeProps 虚拟列表配置 参数说明：

| 参数 | 说明 | 类型 | 是否必填 | 默认值 |
| --- | --- | --- | --- | --- |
| defaultColumnWidth | 默认列宽宽度 | number |  | 150 |
| defaultRowHeight | 默认行高 | number |  | 40 |
| theadVirtualMode | 列表头部是否开启虚拟化（表头存在分组场景 不建议开启） | boolean |  | false |
| overscanRowCount | 预扫描行数（不建议设置太大 影响性能） | number |  | 10 |
| overscanColumnCount | 预扫描列数（不建议设置太大 影响性能） | number |  | 10 |
### 列表全局统一基础配置对接说明：

#### Table底层列表组件提供一个 `getTableUserConfig` promise 方法，用于外部获取全局列表统一配置。

```js
import { getTableUserConfig } from '@weapp/ui';

// 1. 获取默认分页条数 - pageSize
getBaseConfigs('pageSize').then(size => {
  console.log('pageSize:', size); // number
});

// 2. 获取列宽拖拽模式 - autoScroll
getBaseConfigs('autoScrollInResize').then(autoScrollInResize => {
  console.log('autoScrollInResize:', autoScrollInResize); // boolean
});
```
