# EditableTable 可编辑表格

- 来源：[E10 UI 组件库](https://www.e-cology.com.cn/ui/editable-table/freepass)
- 平台：PC
- 维护者：陈佳敏
- 设计稿：http://pd.e-cology.com.cn/pd/E10/interface/e10xiaoguotu/PC/E10zjys/#s41
- Issue：http://gitlab.ecology10.cn/FRONTEND/weapp-ui/-/issues?state=all&search=[EditableTable]

## 代码演示

### 基础用法

1、展示所有可编辑组件 2、展示必填校验 （注意：columns中各项加一起width需要尽量为100%）3.设置一行或多行的工具栏不显示

```jsx
import React, { Component } from 'react';
import { EditableTable, FormItemProps, Button, FormValue, EditableTableValidatorType, Help, Checkbox } from '@weapp/ui';

function bodyRender(data: any, pos: any, name: any) {
  const style = { width: 100 };
  if (data.id === '1') {
    return (
      <div style={style}>
        这是自定义显示1
      </div>
    )
  }
  return (
    <div style={style}>
      这是自定义显示2
    </div>
  )
}

function buildData(count = 20) {
  const arr: any[] = [];
  for (let i = 1; i <= count; i++) {
    arr.push({
      id: String(i),
      name: `小张__${i}`,
      order: 1,
      des: '简介1',
      group: ['g1'],
      sex: 'man',
      about: ["about1", "about2"],
      date: '2019-6-06',
      time: '12:13:14',
      check: true,
      level: { min: 1, max: 100 },
      color: '#ce1919',
      rate: 1,
      switch: true,
      text: '111',
    });
  }
  return arr;
}

const data = buildData(105)
const data2 = [
  {
    id: '1',
    name: '小张',
    order: 1,
    des: '简介1',
    group: ['g1'],
    sex: 'man',
    about: ["about1", "about2"],
    date: '2019-6-06',
    time: '12:13:14',
    check: true,
    level: { min: 1, max: 100 },
    color: '#ce1919',
    rate: 1,
    switch: true,
    text: '111',
  },
  {
    id: '2',
    name: '小王',
    order: 1,
    des: '简介2',
    group: ['g1'],
    sex: 'woman',
    about: ['about1'],
    date: '2021-3-04',
    time: '13:14:15',
    check: false,
    level: { min: 1, max: 99 },
    color: '#f1f2f3',
    rate: 3,
    switch: false,
    text: '222',
  },
  {
    id: '3',
    name: '小李',
    order: 1,
    des: '',
    group: ['g1'],
    sex: 'woman',
    about: ['about1'],
    date: '2021-3-04',
    time: '00.01',
    check: false,
    level: { min: 1, max: 99 },
    color: '#f1f2f3',
    rate: 3,
    switch: false,
    text: '222',
  },
  {
    id: '4',
    name: '小李',
    order: 1,
    des: '',
    group: ['g1'],
    sex: 'woman',
    about: ['about1'],
    date: '2021-3-04',
    time: '00.01',
    check: false,
    level: { min: 1, max: 99 },
    color: '#f1f2f3',
    rate: 3,
    switch: false,
    text: '222',
  },
  {
    id: '5',
    name: '小李',
    order: 1,
    des: '',
    group: ['g1'],
    sex: 'woman',
    about: ['about1'],
    date: '2021-3-04',
    time: '00.01',
    check: false,
    level: { min: 1, max: 99 },
    color: '#f1f2f3',
    rate: 3,
    switch: false,
    text: '222',
  },
  {
    id: '6',
    name: '小李',
    order: 1,
    des: '',
    group: ['g1'],
    sex: 'woman',
    about: ['about1'],
    date: '2021-3-04',
    time: '00.01',
    check: false,
    level: { min: 1, max: 99 },
    color: '#f1f2f3',
    rate: 3,
    switch: false,
    text: '222',
  },
  {
    id: '7',
    name: '小李',
    order: 1,
    des: '',
    group: ['g1'],
    sex: 'woman',
    about: ['about1'],
    date: '2021-3-04',
    time: '00.01',
    check: false,
    level: { min: 1, max: 99 },
    color: '#f1f2f3',
    rate: 3,
    switch: false,
    text: '222',
  },
  {
    id: '8',
    name: '小李',
    order: 1,
    des: '',
    group: ['g1'],
    sex: 'woman',
    about: ['about1'],
    date: '2021-3-04',
    time: '00.01',
    check: false,
    level: { min: 1, max: 99 },
    color: '#f1f2f3',
    rate: 3,
    switch: false,
    text: '222',
  },
  {
    id: '9',
    name: '小李',
    order: 1,
    des: '',
    group: ['g1'],
    sex: 'woman',
    about: ['about1'],
    date: '2021-3-04',
    time: '00.01',
    check: false,
    level: { min: 1, max: 99 },
    color: '#f1f2f3',
    rate: 3,
    switch: false,
    text: '222',
  },
  {
    id: '10',
    name: '小李',
    order: 1,
    des: '',
    group: ['g1'],
    sex: 'woman',
    about: ['about1'],
    date: '2021-3-04',
    time: '00.01',
    check: false,
    level: { min: 1, max: 99 },
    color: '#f1f2f3',
    rate: 3,
    switch: false,
    text: '222',
  },
  {
    id: '11',
    name: '小李',
    order: 1,
    des: '',
    group: ['g1'],
    sex: 'woman',
    about: ['about1'],
    date: '2021-3-04',
    time: '00.01',
    check: false,
    level: { min: 1, max: 99 },
    color: '#f1f2f3',
    rate: 3,
    switch: false,
    text: '222',
  },
  {
    id: '12',
    name: '小李',
    order: 1,
    des: '',
    group: ['g1'],
    sex: 'woman',
    about: ['about1'],
    date: '2021-3-04',
    time: '00.01',
    check: false,
    level: { min: 1, max: 99 },
    color: '#f1f2f3',
    rate: 3,
    switch: false,
    text: '222',
  },
  {
    id: '13',
    name: '小李',
    order: 1,
    des: '',
    group: ['g1'],
    sex: 'woman',
    about: ['about1'],
    date: '2021-3-04',
    time: '00.01',
    check: false,
    level: { min: 1, max: 99 },
    color: '#f1f2f3',
    rate: 3,
    switch: false,
    text: '222',
  },
  {
    id: '14',
    name: '小李',
    order: 1,
    des: '',
    group: ['g1'],
    sex: 'woman',
    about: ['about1'],
    date: '2021-3-04',
    time: '00.01',
    check: false,
    level: { min: 1, max: 99 },
    color: '#f1f2f3',
    rate: 3,
    switch: false,
    text: '222',
  }
];

const columns = [
  {
    title: '名称(Input)',
    dataIndex: 'name', // 列标识(cells联动设置需要）
    comKey: 'name', // 当前列组件的key, 和data中的数据对应, 和comProps对应（注意: comKey不能重复）
    showRequired: true, // 是否显示必填小红点
    width: '150',
  },
  {
    title: '编号(InputNumber)',
    dataIndex: 'order',
    comKey: 'order',
    showRequired: true, // 是否显示必填小红点
    width: '150',
  },
  {
    title: '简介(Textarea)',
    dataIndex: 'des',
    comKey: 'des',
    showRequired: true, // 是否显示必填小红点
    width: '150',
  },
  {
    title: '分组(Select)',
    dataIndex: 'group',
    comKey: 'group',
    showRequired: true, // 是否显示必填小红点
    width: '150',
  },
  {
    title: '性别(Radio)',
    dataIndex: 'sex',
    comKey: 'sex',
    width: '150',
    showRequired: true, // 是否显示必填小红点
  },
  {
    title: '关联(Checkbox)',
    dataIndex: 'about',
    comKey: 'about',
    showRequired: true, // 是否显示必填小红点
    width: '150',
  },
  {
    title: '列全选(Checkbox)',
    dataIndex: 'check',
    comKey: 'check',
    width: '150',
    showCheckAll: true,
    checkAllProps: {
      itemType: 'CHECKBOX'
    },
  },
  {
    title: '列全选(Switch)',
    dataIndex: 'switchCheck',
    comKey: 'switchCheck',
    width: '150',
    showCheckAll: true,
    checkAllProps: {
      itemType: 'SWITCH'
    },
  },
  {
    title: '日期(DatePicker)',
    dataIndex: 'date',
    comKey: 'date',
    width: '150',
    showRequired: true, // 是否显示必填小红点
  },
  {
    title: '时间(TimePicker)',
    dataIndex: 'time',
    comKey: 'time',
    width: '150',
    showRequired: true, // 是否显示必填小红点
  },
  {
    title: '附件(Upload)',
    dataIndex: 'file',
    comKey: 'file',
    width: '150',
    showRequired: false, // 是否显示必填小红点
  },
  {
    title: '颜色(ColorPicker)',
    dataIndex: 'color',
    comKey: 'color',
    width: '150',
  },
  {
    title: '安全级别(Scope)',
    dataIndex: 'level',
    comKey: 'level',
    width: '250',
    showRequired: true, // 是否显示必填小红点
  },
  {
    title: '成员(Browser)',
    dataIndex: 'member',
    comKey: 'member',
    width: '180',
    showRequired: false, // 是否显示必填小红点
  },
  {
    title: '评分(RATE)',
    dataIndex: 'rate',
    comKey: 'rate',
    width: '150',
  },
  {
    title: '启用(Switch)',
    dataIndex: 'switch',
    comKey: 'switch',
    width: '150',
  },
  {
    title: <span>纯文本<Help title="提示信息" /></span>,
    dataIndex: 'text',
    width: '150',
    comKey: '',
  },
  {
    title: 'bodyRender自定义渲染',
    dataIndex: 'custom',
    comKey: '',
    width: '150',
    bodyRender,
  },
];

const comProps: FormItemProps = {
  'name': { // 以下可配置 input组件 的所有属性 ('name' -- 对应columns的comKey)
    itemType: 'INPUT',  // 字段类型
    defaultValue: '默认值', // 默认值 （组件属性）
    style: {  // 样式（组件属性）
      // width: '100px'
    },
    required: true, // 是否必填
    // rules: 'required', // 必填校验规则: 如果是其他规则,可通过rules配置
  },
  'order': {
    itemType: 'INPUTNUMBER',
    required: true, // 是否必填
    style: {
      // width: '120px'
    }
  },
  'des': {
    itemType: 'TEXTAREA',
    required: true, // 是否必填
    rules: 'required|maxLen20', // 这里是maxLen20是通过customRegister注册的校验规则
  },
  'group': {
    itemType: 'SELECT',
    required: true, // 是否必填
    data: [
      { id: 'g1', content: '分组1' },
      { id: 'g2', content: '分组2' },
      { id: 'g3', content: '分组3' }
    ],
    defaultValue: 'g1',
    style: {
      // width: '100px'
    }
  },
  'sex': {
    itemType: 'RADIO',
    required: true, // 是否必填
    data: [
      { id: 'man', value: 'man', content: '男' },
      { id: 'woman', value: 'woman', content: '女' }
    ],
    defaultValue: 'man',
  },
  'about': {
    itemType: 'CHECKBOX',
    required: true, // 是否必填
    data: [
      { id: 'about1', value: 'about1', content: '关联1' },
      { id: 'about2', value: 'about2', content: '关联2' }
    ],
    defaultValue: ['about1'],
  },
  'check': {
    itemType: 'CHECKBOX',
    required: true, // 是否必填
  },
  'date': {
    itemType: 'DATEPICKER',
    required: true, // 是否必填
  },
  'time': {
    itemType: 'TIMEPICKER',
    required: true, // 是否必填
  },
  'file': {
    itemType: 'UPLOAD',
    required: false, // 是否必填 demo必填关闭 方便体现 校验定位
    showListUploading: false, // 不显示上传中文件列表  
  },
  'color': {
    itemType: 'COLORPICKER',
    // rules: 'color', // customRegister注册的校验规则
  },
  'level': {
    itemType: 'SCOPE',
    required: true, // 是否必填
    defaultVaue: { min: 1, max: 100 },
  },
  'member': {
    itemType: 'BROWSER',
    required: false, // 是否必填  demo必填关闭 方便体现 校验定位
    browserBean: {
      canSelectAllUser: false,
      defaultAccount: false,
      defaultCheckStrictly: true,
      defaultOpen: false,
      defaultStopDept: false,
      disabledTabCache: false,
      enableAddData: false,
      enableExtendButton: false,
      hasAdvanceSearch: true,
      hasLeftData: false,
      module: "hrm",
      multCheckbox: false,
      multType: false,
      multiple: true,
      showCheckStrictly: true,
      type: "role"
    }
  },
  'rate': {
    itemType: 'RATE',
    // width: '8%'
  },
  'switch': {
    itemType: 'SWITCH',
  },
  'switchCheck': {
    itemType: 'CHECKBOX',
  },
}

const customValidator: EditableTableValidatorType = {
  // message: { // 自定义校验提示信息
  //   'required': {
  //     string: "此项必填(自定义)"
  //   }
  // },
  customRegister: [ // 自定义校验规则(comProps的rules需要使用注册的规则才会生效)
    {
      name: "maxLen20",
      rules: (value: any, requirement: any, attribute: any) => {
        return value.length < 21;
      },
      errorMessage: "最大长度20"
    },
    // {
    //   name: "color",
    //   rules: (value: any, requirement: any, attribute: any) => {
    //     return /^#[0-9a-fA-F]{3, 6}$/.test(value);
    //   },
    //   errorMessage: ":attribute 不符合颜色值格式"
    // }
  ]
};

type EditableTableDemoState = {
  data: any,
  selectedRowKeys: string[],
  enableValidateTipPosition: boolean,
  value: number,
  pageSize: number,
}

const scroll = { x: 3000 }

class EditableTableDemo extends Component<{}, EditableTableDemoState> {
  constructor(props: {}) {
    super(props);
    this.state = {
      data,
      selectedRowKeys: [],
      enableValidateTipPosition: false,
      value: 1,
      pageSize: 5,
    }
  }

  editableTableRef = React.createRef();

  onItemBlur = (editKey: string, editValue: FormValue, rowId: string,data:any) => {
    console.log('onItemBlur', editKey, editValue, rowId,data)
  }
  onItemFocus= (editKey: string, editValue: FormValue, rowId: string,data:any) => {
    console.log('onItemFocus', editKey, editValue, rowId,data)
  }

  onItemEditBefore = (newData: any, editKey: string, editValue: FormValue) => {
    console.log('onItemEditBefore', newData, editKey, editValue);
    return newData; // 如果有自定义需求,可以根据需求更改newData
  }

  onAddBefore = (newData: any, rowData: any) => {
    console.log('onAddBefore', newData, rowData);
    return newData;
  }

  onDeleteBefore = (newData: any, ids: string[]) => {
    console.log('onDeleteBefore', newData, ids);
    return newData;
  }

  onRowOperateBefore = (newData: any, rowData: any, type: 'insertUp' | 'insertDown' | 'copy') => {
    console.log('onRowOperateBefore', newData, rowData, type);
    return newData;
  }

  onBatchEditBefore = (newData: any, ids: string[], editKey: string, editValue: FormValue) => {
    console.log('onBatchEditBefore', newData, ids, editKey, editValue);
    return newData;
  }

  onChange = (allData: any, data: any) => {
    console.log('allData', allData, 'data', data)
    this.setState({ data: allData });
  }

  onCheckAllChange = (key: string, value: boolean) => {
    console.log('onCheckAllChange', key, value);
  }

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

  validate = () => {
    const editableTableRef: any = this.editableTableRef && this.editableTableRef.current;
    console.log('editableTableRef', editableTableRef);
    if (editableTableRef) {
      editableTableRef.doValidate((msg: object) => {
        console.log('validateCallback', msg);
      });
    }
  }

  changeEnabelValidateTipPosition = () => this.setState({ enableValidateTipPosition: !this.state.enableValidateTipPosition }); 

  customAdd = () => {
    alert('customAdd');
  }

  getPageInfo = () => {
    const { value, pageSize } = this.state;
    return {
      value: value,
      pageSize: pageSize,
      total: data.length,
    }
  }

  onDrag = (newData: any[], evt: any) => {
    console.log('[前端分页] onDrag:', { newData, evt });
    this.setState({ data: newData });
  };

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

    const topTools = [
      {
        type: 'batchEdit',
        icon: 'Icon-Batch-modification',
        title: '批量编辑',
      },
      {
        type: 'batchDelete',
        icon: 'Icon-Batch-delete',
        title: '批量删除',
      },
      {
        type: 'add',
        icon: 'Icon-add-to03',
        title: '新增',
        // onClick: this.customAdd, // 通过onClick方法复写默认的新增事件
      },
      {
        type: 'custom',
        icon: <Button type="primary">必填校验</Button>,
        title: '必填校验',
        onClick: this.validate,
      },
      {
        type: 'custom',
        icon: <Checkbox onChange={this.changeEnabelValidateTipPosition}>启用校验信息定位功能</Checkbox>,
        title: '',
      },
      {
        type: 'maximize',
        icon: 'Icon-Global-zoom-in',
        title: '最大化',
      }
    ];

    const rowTools = [
      {
        type: 'delete',
        icon: 'Icon-delete',
        title: '删除',
      },
      {
        type: 'insertUp',
        icon: 'Icon-Insert-forward',
        title: '向前插入一行',
      },
      {
        type: 'insertDown',
        icon: 'Icon-Insert-after-line',
        title: '向后插入一行',
      },
      {
        type: 'copy',
        icon: 'Icon-copy',
        title: '复制',
      },
    ];

    return (
      <EditableTable
       
        ref={this.editableTableRef}
        title={<span>表格标题<Help title="提示信息" /></span>}
        columns={columns}
        data={data}
        topTools={topTools}
        enableSortableColumnEdit
        onDrag={this.onDrag}
        showPagination={true}
        pageInfo={this.getPageInfo()}
        // rowTools={rowTools}
        selectedRowKeys={selectedRowKeys}
        comProps={comProps}
        customValidator={customValidator} // 自定义必填校验配置
        deleteConfirm={true}
        scroll={scroll} // 因为这里内容过多,所以加了滚动处理
        sortable={true}
        sortableType="icon"
        onChange={this.onChange}
        onCheckAllChange={this.onCheckAllChange}
        onRowSelect={this.onRowSelect}
        onItemBlur={this.onItemBlur} // 单元格失去焦点的回调
        onItemFocus={this.onItemFocus} // 单元格获取焦点的回调
        onItemEditBefore={this.onItemEditBefore} // 不需要更新前做操作就不传
        onAddBefore={this.onAddBefore} // 不需要更新前做操作就不传
        onDeleteBefore={this.onDeleteBefore} // 不需要更新前做操作就不传
        onRowOperateBefore={this.onRowOperateBefore} // 不需要更新前做操作就不传
        onBatchEditBefore={this.onBatchEditBefore} // 不需要更新前做操作就不传
        enabelValidateTipPosition={this.state.enableValidateTipPosition}
        // disableRowTools={["1"]} //设置表格的第一行工具栏不显示
      />
    );
  }
}

export default EditableTableDemo;
```

### 启用分页

启用分页组件（注意：columns中各项加一起width需要尽量为100%）

```jsx
import { Component } from 'react';
import { Button, EditableTable, FormItemProps } from '@weapp/ui';
import { getHd } from '../../../../utils';

function bodyRender(data: any, pos: any, name: any) {
  const style = { width: 100 };
  if (data.id === '1') {
    return (
      <div style={style}>
        这是自定义显示1
      </div>
    )
  }
  return (
    <div style={style}>
      这是自定义显示2
    </div>
  )
}

const data = [
  {
    id: '1',
    name: '张天逸',
    order: 1,
    des: '简介1',
    group: ['g1'],
    sex: 'man',
    about: ["about1", "about2"],
    date: '2019-6-06',
    time: '12:13:14',
    check: true,
    level: { min: 1, max: 100 },
    color: '#ce1919',
    rate: 1,
    switch: true,
    text: '111',
  },
  {
    id: '2',
    name: '彭浩秋',
    order: 1,
    des: '简介2',
    group: ['g1'],
    sex: 'woman',
    about: ['about1'],
    date: '2021-3-04',
    time: '13:14:15',
    check: false,
    level: { min: 1, max: 99 },
    color: '#f1f2f3',
    rate: 3,
    switch: false,
    text: '222',
  }
];

const columns = [
  {
    title: '名称(Input)',
    dataIndex: 'name', // 列标识(cells联动设置需要）
    comKey: 'name', // 当前列组件的key, 和data中的数据对应, 和comProps对应（注意: comKey不能重复）
    showRequired: true, // 是否显示必填小红点
    width: '5.5%',
  },
  {
    title: '编号(InputNumber)',
    dataIndex: 'order',
    comKey: 'order',
    showRequired: true, // 是否显示必填小红点
    width: '5.5%',
  },
  {
    title: '简介(Textarea)',
    dataIndex: 'des',
    comKey: 'des',
    showRequired: true, // 是否显示必填小红点
    width: '5.5%',
  },
  {
    title: '分组(Select)',
    dataIndex: 'group',
    comKey: 'group',
    width: '5.5%',
  },
  {
    title: '性别(Radio)',
    dataIndex: 'sex',
    comKey: 'sex',
    width: '5.5%',
  },
  {
    title: '关联(Checkbox)',
    dataIndex: 'about',
    comKey: 'about',
    width: '5.5%',
  },
  {
    title: '列全选(Checkbox)',
    dataIndex: 'check',
    comKey: 'check',
    width: '5.5%',
    showCheckAll: true,
    checkAllProps: {
      itemType: 'CHECKBOX'
    },
  },
  {
    title: '列全选(Switch)',
    dataIndex: 'switchCheck',
    comKey: 'switchCheck',
    width: '5.5%',
    showCheckAll: true,
    checkAllProps: {
      itemType: 'SWITCH'
    },
  },
  {
    title: '日期(DatePicker)',
    dataIndex: 'date',
    comKey: 'date',
    width: '5.5%',
  },
  {
    title: '时间(TimePicker)',
    dataIndex: 'time',
    comKey: 'time',
    width: '5.5%',
  },
  {
    title: '附件(Upload)',
    dataIndex: 'file',
    comKey: 'file',
    width: '5.5%',
  },
  {
    title: '颜色(ColorPicker)',
    dataIndex: 'color',
    comKey: 'color',
    width: '5.5%',
  },
  {
    title: '安全级别(Scope)',
    dataIndex: 'level',
    comKey: 'level',
    width: '10%',
  },
  {
    title: '成员(Browser)',
    dataIndex: 'member',
    comKey: 'member',
    width: '5.5%',
  },
  {
    title: '评分(RATE)',
    dataIndex: 'rate',
    comKey: 'rate',
    width: '5.5%',
  },
  {
    title: '启用(Switch)',
    dataIndex: 'switch',
    comKey: 'switch',
    width: '5.5%',
  },
  {
    title: '纯文本',
    dataIndex: 'text',
    width: '5.5%',
    comKey: '',
  },
  {
    title: 'bodyRender自定义渲染',
    dataIndex: 'custom',
    comKey: '',
    width: '5.5%',
    bodyRender,
  },
];

const comProps: FormItemProps = {
  'name': { // 以下可配置 input组件 的所有属性 ('name' -- 对应columns的comKey)
    itemType: 'INPUT',  // 字段类型
    defaultValue: '默认值', // 默认值 （组件属性）
    style: {  // 样式（组件属性）
      width: '100px'
    }
  },
  'order': {
    itemType: 'INPUTNUMBER',
    style: {
      width: '120px'
    }
  },
  'des': {
    itemType: 'TEXTAREA',
  },
  'group': {
    itemType: 'SELECT',
    data: [
      { id: 'g1', content: '分组1' },
      { id: 'g2', content: '分组2' },
      { id: 'g3', content: '分组3' }
    ],
    defaultValue: 'g1',
    style: {
      width: '100px'
    }
  },
  'sex': {
    itemType: 'RADIO',
    data: [
      { id: 'man', value: 'man', content: '男' },
      { id: 'woman', value: 'woman', content: '女' }
    ],
    defaultValue: 'man',
  },
  'about': {
    itemType: 'CHECKBOX',
    data: [
      { id: 'about1', value: 'about1', content: '关联1' },
      { id: 'about2', value: 'about2', content: '关联2' }
    ],
    defaultValue: ['about1'],
  },
  'check': {
    itemType: 'CHECKBOX',
  },
  'date': {
    itemType: 'DATEPICKER',
  },
  'time': {
    itemType: 'TIMEPICKER',
  },
  'file': {
    itemType: 'UPLOAD',
    showListUploading: false, // 不显示上传中文件列表
  },
  'color': {
    itemType: 'COLORPICKER',
  },
  'level': {
    itemType: 'SCOPE',
    defaultVaue: { min: 1, max: 100 },
  },
  'member': {
    itemType: 'BROWSER',
  },
  'rate': {
    itemType: 'RATE',
    width: '8%'
  },
  'switch': {
    itemType: 'SWITCH',
  },
  'switchCheck': {
    itemType: 'CHECKBOX',
  },
}

type EditableTableDemoState = {
  data: any,
  selectedRowKeys: string[],
  page: number,
  pageSize: number,
}

class EditableTableDemo extends Component<{}, EditableTableDemoState> {
  constructor(props: {}) {
    super(props);
    this.state = {
      data,
      selectedRowKeys: [],
      page: 1,
      pageSize: 10,
    }
  }

  onChange = (data: any) => {
    this.setState({ data });
  }

  onCheckAllChange = (key: string, value: boolean) => {
    console.log('onCheckAllChange', key, value);
  }

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

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

  onPageSizeChange = (value: number, pageSize: number) => {
    this.setState({ page: value, pageSize });
  }

  customBtnClick = () => {
    alert('customBtnClick');
  }

  customAdd = () => {
    alert('customAdd');
  }

  render() {
    const { data, selectedRowKeys, page, pageSize } = this.state;

    const topTools = [
      {
        type: 'batchEdit',
        icon: 'Icon-Batch-modification',
        title: '批量编辑',
      },
      {
        type: 'batchDelete',
        icon: 'Icon-Batch-delete',
        title: '批量删除',
      },
      {
        type: 'add',
        icon: 'Icon-add-to03',
        title: '新增',
        // onClick: this.customAdd, // 通过onClick方法复写默认的新增事件
      },
      {
        type: 'custom',
        icon: <Button type="primary">自定义</Button>,
        title: '自定义',
        onClick: this.customBtnClick,
      },
      {
        type: 'maximize',
        icon: 'Icon-Global-zoom-in',
        title: '最大化',
      }
    ];

    const rowTools = [
      {
        type: 'delete',
        icon: 'Icon-delete',
        title: '删除',
      },
      {
        type: 'insertUp',
        icon: 'Icon-Insert-forward',
        title: '向前插入一行',
      },
      {
        type: 'insertDown',
        icon: 'Icon-Insert-after-line',
        title: '向后插入一行',
      },
      {
        type: 'copy',
        icon: 'Icon-copy',
        title: '复制',
      },
    ];

    return (
      <EditableTable
       
        title="表格标题"
        columns={columns}
        data={data}
        selectedRowKeys={selectedRowKeys}
        comProps={comProps}
        topTools={topTools}
        rowTools={rowTools}
        deleteConfirm={true}
        onChange={this.onChange}
        onCheckAllChange={this.onCheckAllChange}
        onRowSelect={this.onRowSelect}
        scroll={{ x: 3000 * getHd() }}
        sortable={true}
        sortableType="icon"
        showPagination={true}
        page={page}
        pageSize={pageSize}
        onPageChange={this.onPageChange}
        onPageSizeChange={this.onPageSizeChange}
      />
    );
  }
}

export default EditableTableDemo;
```

### 可折叠

按照折叠分组的效果显示（注意：columns中各项加一起width需要尽量为100%）

```jsx
import React, { Component } from 'react';
import { EditableTable, FormItemProps, Button, FormValue, EditableTableValidatorType } from '@weapp/ui';

function bodyRender(data: any, pos: any, name: any) {
  const style = { width: 100 };
  if (data.id === '1') {
    return (
      <div style={style}>
        这是自定义显示1
      </div>
    )
  }
  return (
    <div style={style}>
      这是自定义显示2
    </div>
  )
}

const data = [
  {
    id: '1',
    name: '张天逸',
    order: 1,
    des: '简介1',
    group: ['g1'],
    sex: 'man',
    about: ["about1", "about2"],
    date: '2019-6-06',
    time: '12:13:14',
    check: true,
    level: { min: 1, max: 100 },
    color: '#ce1919',
    rate: 1,
    switch: true,
    text: '111',
  },
  {
    id: '2',
    name: '彭浩秋',
    order: 1,
    des: '简介2',
    group: ['g1'],
    sex: 'woman',
    about: ['about1'],
    date: '2021-3-04',
    time: '13:14:15',
    check: false,
    level: { min: 1, max: 99 },
    color: '#f1f2f3',
    rate: 3,
    switch: false,
    text: '222',
  },
  {
    id: '3',
    name: '',
    order: 1,
    des: '',
    group: ['g1'],
    sex: 'woman',
    about: ['about1'],
    date: '2021-3-04',
    time: '00.01',
    check: false,
    level: { min: 1, max: 99 },
    color: '#f1f2f3',
    rate: 3,
    switch: false,
    text: '222',
  }
];

const columns = [
  {
    title: '名称(Input)',
    dataIndex: 'name', // 列标识(cells联动设置需要）
    comKey: 'name', // 当前列组件的key, 和data中的数据对应, 和comProps对应（注意: comKey不能重复）
    showRequired: true, // 是否显示必填小红点
    width: '5.5%',
  },
  {
    title: '编号(InputNumber)',
    dataIndex: 'order',
    comKey: 'order',
    width: '5.5%',
  },
  {
    title: '简介(Textarea)',
    dataIndex: 'des',
    comKey: 'des',
    showRequired: true, // 是否显示必填小红点
    width: '5.5%',
  },
  {
    title: '分组(Select)',
    dataIndex: 'group',
    comKey: 'group',
    width: '5.5%',
  },
  {
    title: '性别(Radio)',
    dataIndex: 'sex',
    comKey: 'sex',
    width: '5.5%',
  },
  {
    title: '关联(Checkbox)',
    dataIndex: 'about',
    comKey: 'about',
    width: '5.5%',
  },
  {
    title: '列全选(Checkbox)',
    dataIndex: 'check',
    comKey: 'check',
    width: '5.5%',
    showCheckAll: true,
    checkAllProps: {
      itemType: 'CHECKBOX'
    },
  },
  {
    title: '列全选(Switch)',
    dataIndex: 'switchCheck',
    comKey: 'switchCheck',
    width: '5.5%',
    showCheckAll: true,
    checkAllProps: {
      itemType: 'SWITCH'
    },
  },
  {
    title: '日期(DatePicker)',
    dataIndex: 'date',
    comKey: 'date',
    width: '5.5%',
  },
  {
    title: '时间(TimePicker)',
    dataIndex: 'time',
    comKey: 'time',
    width: '5.5%',
  },
  {
    title: '附件(Upload)',
    dataIndex: 'file',
    comKey: 'file',
    width: '5.5%',
  },
  {
    title: '颜色(ColorPicker)',
    dataIndex: 'color',
    comKey: 'color',
    width: '5.5%',
  },
  {
    title: '安全级别(Scope)',
    dataIndex: 'level',
    comKey: 'level',
    width: '10%',
  },
  {
    title: '成员(Browser)',
    dataIndex: 'member',
    comKey: 'member',
    width: '5.5%',
  },
  {
    title: '评分(RATE)',
    dataIndex: 'rate',
    comKey: 'rate',
    width: '5.5%',
  },
  {
    title: '启用(Switch)',
    dataIndex: 'switch',
    comKey: 'switch',
    width: '5.5%',
  },
  {
    title: '纯文本',
    dataIndex: 'text',
    width: '5.5%',
    comKey: '',
  },
  {
    title: 'bodyRender自定义渲染',
    dataIndex: 'custom',
    comKey: '',
    width: '5.5%',
    bodyRender,
  },
];

const comProps: FormItemProps = {
  'name': { // 以下可配置 input组件 的所有属性 ('name' -- 对应columns的comKey)
    itemType: 'INPUT',  // 字段类型
    defaultValue: '默认值', // 默认值 （组件属性）
    style: {  // 样式（组件属性）
      width: '100px'
    },
    required: true, // 是否必填
    // rules: 'required', // 必填校验规则: 如果是其他规则,可通过rules配置
  },
  'order': {
    itemType: 'INPUTNUMBER',
    style: {
      width: '120px'
    }
  },
  'des': {
    itemType: 'TEXTAREA',
    required: true, // 是否必填
  },
  'group': {
    itemType: 'SELECT',
    data: [
      { id: 'g1', content: '分组1' },
      { id: 'g2', content: '分组2' },
      { id: 'g3', content: '分组3' }
    ],
    defaultValue: 'g1',
    style: {
      width: '100px'
    }
  },
  'sex': {
    itemType: 'RADIO',
    data: [
      { id: 'man', value: 'man', content: '男' },
      { id: 'woman', value: 'woman', content: '女' }
    ],
    defaultValue: 'man',
  },
  'about': {
    itemType: 'CHECKBOX',
    data: [
      { id: 'about1', value: 'about1', content: '关联1' },
      { id: 'about2', value: 'about2', content: '关联2' }
    ],
    defaultValue: ['about1'],
  },
  'check': {
    itemType: 'CHECKBOX',
  },
  'date': {
    itemType: 'DATEPICKER',
  },
  'time': {
    itemType: 'TIMEPICKER',
  },
  'file': {
    itemType: 'UPLOAD',
    showListUploading: false, // 不显示上传中文件列表
  },
  'color': {
    itemType: 'COLORPICKER',
  },
  'level': {
    itemType: 'SCOPE',
    defaultVaue: { min: 1, max: 100 },
  },
  'member': {
    itemType: 'BROWSER',
  },
  'rate': {
    itemType: 'RATE',
    width: '8%'
  },
  'switch': {
    itemType: 'SWITCH',
  },
  'switchCheck': {
    itemType: 'CHECKBOX',
  },
}

type EditableTableDemoState = {
  data: any,
  selectedRowKeys: string[],
}
class EditableTableDemo extends Component<{}, EditableTableDemoState> {
  constructor(props: {}) {
    super(props);
    this.state = {
      data,
      selectedRowKeys: [],
    }
  }

  editableTableRef = React.createRef();

  onItemBlur = (editKey: string, editValue: FormValue, rowId: string) => {
    console.log('onItemBlur', editKey, editValue, rowId)
  }

  onItemEditBefore = (newData: any, editKey: string, editValue: FormValue) => {
    console.log('onItemEditBefore', newData, editKey, editValue);
    return newData; // 如果有自定义需求,可以根据需求更改newData
  }

  onAddBefore = (newData: any, rowData: any) => {
    console.log('onAddBefore', newData, rowData);
    return newData;
  }

  onDeleteBefore = (newData: any, ids: string[]) => {
    console.log('onDeleteBefore', newData, ids);
    return newData;
  }

  onRowOperateBefore = (newData: any, rowData: any, type: 'insertUp' | 'insertDown' | 'copy') => {
    console.log('onRowOperateBefore', newData, rowData, type);
    return newData;
  }

  onBatchEditBefore = (newData: any, ids: string[], editKey: string, editValue: FormValue) => {
    console.log('onBatchEditBefore', newData, ids, editKey, editValue);
    return newData;
  }

  onChange = (data: any) => {
    console.log('data', data)
    this.setState({ data });
  }

  onCheckAllChange = (key: string, value: boolean) => {
    console.log('onCheckAllChange', key, value);
  }

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

  onCollapseChange = (value: string) => {
    console.log('onCollapseChange', value)
  }

  validate = () => {
    const editableTableRef: any = this.editableTableRef && this.editableTableRef.current;
    console.log('editableTableRef', editableTableRef);
    if (editableTableRef) {
      editableTableRef.doValidate((msg: object) => {
        console.log('validateCallback', msg);
      });
    }
  }

  customAdd = () => {
    alert('customAdd');
  }

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

    const topTools = [
      {
        type: 'batchEdit',
        icon: 'Icon-Batch-modification',
        title: '批量编辑',
      },
      {
        type: 'batchDelete',
        icon: 'Icon-Batch-delete',
        title: '批量删除',
      },
      {
        type: 'add',
        icon: 'Icon-add-to03',
        title: '新增',
        // onClick: this.customAdd, // 通过onClick方法复写默认的新增事件
      },
      // {
      //   type: 'custom',
      //   icon: <Button type="primary">必填校验</Button>,
      //   title: '必填校验',
      //   onClick: this.validate,
      // },
      {
        type: 'maximize',
        icon: 'Icon-Global-zoom-in',
        title: '最大化',
      }
    ];

    const rowTools = [
      {
        type: 'delete',
        icon: 'Icon-delete',
        title: '删除',
      },
      {
        type: 'insertUp',
        icon: 'Icon-Insert-forward',
        title: '向前插入一行',
      },
      {
        type: 'insertDown',
        icon: 'Icon-Insert-after-line',
        title: '向后插入一行',
      },
      {
        type: 'copy',
        icon: 'Icon-copy',
        title: '复制',
      },
    ];

    return (
      <EditableTable
       
        ref={this.editableTableRef}
        title="表格标题"
        columns={columns}
        data={data}
        topTools={topTools}
        rowTools={rowTools}
        collapsed={true} // 是否可折叠
        collapsePanelId="collapsedDemo" // 自定义折叠面板的id
        defaultCollapsed={false} // 默认折叠
        selectedRowKeys={selectedRowKeys}
        comProps={comProps}
        // customValidator={customValidator} // 自定义必填校验配置
        deleteConfirm={true}
        scroll={{ x: 3000 }}
        sortable={true}
        sortableType="icon"
        onChange={this.onChange}
        onCollapseChange={this.onCollapseChange}
        onCheckAllChange={this.onCheckAllChange}
        onRowSelect={this.onRowSelect}
        onItemBlur={this.onItemBlur} // 单元格失去焦点的回调
        onItemEditBefore={this.onItemEditBefore} // 不需要更新前做操作就不传
        onAddBefore={this.onAddBefore} // 不需要更新前做操作就不传
        onDeleteBefore={this.onDeleteBefore} // 不需要更新前做操作就不传
        onRowOperateBefore={this.onRowOperateBefore} // 不需要更新前做操作就不传
        onBatchEditBefore={this.onBatchEditBefore} // 不需要更新前做操作就不传
      />
    );
  }
}

export default EditableTableDemo;
```

### 自定义联动

1、展示级联配置，2、展示跨行、跨列联动的配置（注意：columns中各项加一起width需要尽量为100%）

```jsx
import React, { Component } from 'react';
import { Button, EditableTable, FormItemProps, FormValue, EditableTableCellType, FormSingleItemProps, Checkbox, Switch, constants } from '@weapp/ui';
const { uiPcClsPrefix } = constants;

// 列配置
const columns = [
  {
    title: '级联',
    dataIndex: 'cascader',
    comKey: ['select', 'input'], // 默认渲染的组件id 
    width: '60%',
  },
  {
    title: '跨行列联动',
    dataIndex: 'cross',
    comKey: '',
    width: '20%',
  },
  {
    title: '被联动列',
    dataIndex: 'beCustomed',
    comKey: '',
    width: '20%',
  },
];

const data = [
  {
    id: '1',
    select: '1',
    input: 'test1',
    beCustomed: '111'
  },
  {
    id: '2',
    select: '2',
    cross: '默认显示纯文本',
    textarea: 'test2',
    beCustomed: '111'
  },
  {
    id: '3',
    select: '4',
    select2: '2',
    cross: '默认显示纯文本',
    input2: 'test3',
    beCustomed: '111'
  },
];

// 组件属性配置
const commonInputProps: FormSingleItemProps = {
  itemType: 'INPUT',
  required: true, // 是否必填
  style: {
    width: '100px'
  },
}
const commonTextareaProps: FormSingleItemProps = {
  itemType: 'TEXTAREA',
  style: {
    width: '150px'
  },
  required: true, // 是否必填
}
const comProps: FormItemProps = {
  'select': {
    itemType: 'SELECT',
    data: [
      { id: '1', content: '单行文本' },
      { id: '2', content: '多行文本' },
      { id: '3', content: '浏览按钮(有值联动下一级)' },
      { id: '4', content: '二级下拉框' }
    ],
    defaultValue: '1',
    style: {
      width: '180px',
    }
  },
  'select2': {
    itemType: 'SELECT',
    data: [
      { id: '1', content: '不联动' },
      { id: '2', content: '联动单行文本' },
    ],
    defaultValue: '1',
    style: {
      width: '150px',
    }
  },
  'select3': {
    itemType: 'SELECT',
    data: [
      { id: '1', content: '不联动' },
      { id: '2', content: '联动行id为1,列dataIndex为beCustomed的单元格显示单行文本' },
      { id: '3', content: '联动行id为2,列dataIndex为cross的单元格显示switch框' },
    ],
    defaultValue: '1',
    otherParams: {
      className: `${uiPcClsPrefix}-demo-select3`
    }
  },
  'input': commonInputProps,
  'input2': commonInputProps,
  'input3': commonInputProps,
  'textarea2': commonTextareaProps,
  'textarea': commonTextareaProps,
  'browser': {
    itemType: 'BROWSER',
    browserBean: {
      type: 'demo',
      module: 'common',
    }
  },
  'switch': {
    itemType: 'SWITCH',
  },
  'checkbox': {
    itemType: 'CHECKBOX',
  }
}

// 级联规则
const cascadeRules = {
  'select': { // 1、通过具体的值匹配规则: object （这里对应下拉框的值 '1' | '2' | '3' | '4'）
    '1': ['input'],
    '2': ['textarea'],
    '3': ['browser'],
    '4': ['select2'],
  },
  'select2': (editValue: any, rowData: any) => { // 2、通过方法匹配规则: function
    if (editValue === '2') {
      return ['input2']
    }
    return [];
  },
  'browser': ['input3'], // 3、 直接匹配规则, 表示有值就显示
};

// 自定义单元格规则 (初始化的显示) { [rowId: string]: { [dataIndex: string']: FormValue }
const initCells = {
  '1': {
    'cross': ['select3']
  },
  '2': {
    'cascader': ['select', 'textarea'],
  },
  '3': {
    'cascader': ['select', 'select2', 'input2'],
  }
}

type EditableTableDemoState = {
  data: any,
  selectedRowKeys: string[],
  cells: EditableTableCellType,
}

class EditableTableDemo extends Component<{}, EditableTableDemoState> {
  constructor(props: {}) {
    super(props);
    this.state = {
      data,
      selectedRowKeys: [],
      cells: initCells,
    }
  }

  editableTableRef = React.createRef();

  onItemEditBefore = (newData: any, editKey: string, editValue: FormValue, rowData: any) => {
    const { cells } = this.state;
    let newCells: EditableTableCellType = cells;
    console.log('onItemEditBefore', newData, editKey, editValue, rowData);
    // 通过更新cells: 自定义跨行、跨列联动等 (如果只是某一行的某个字段,可通过rowData进行判断)
    if (editKey === 'select3') {
      if (editValue === '2') { // 值为2,指定id为'1'的列,dataIndex为beCustomed的单元格，渲染成单行文本
        newCells = {
          ...newCells,
          '1': {
            ...(newCells['1'] || {}),
            'beCustomed': ['input3']
          }
        }
        this.setState({ cells: newCells });
      } else if (editValue === '3') { // 值为3,指定id为'2'的列,dataIndex为cross的单元格，渲染成switch框
        newCells = {
          ...newCells,
          '2': {
            ...(newCells['2'] || {}),
            'cross': ['switch']
          }
        }
        this.setState({ cells: newCells });
      }
    }
    return newData;
  }

  onChange = (data: any) => {
    this.setState({ data });
  }

  onCellsChange = (cells: EditableTableCellType) => {
    console.log('onCellsChange', cells);
    this.setState({ cells });
  }

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

  validate = () => {
    const editableTableRef: any = this.editableTableRef && this.editableTableRef.current;
    console.log('editableTableRef', editableTableRef);
    if (editableTableRef) {
      editableTableRef.doValidate((msg: object) => {
        console.log('validateCallback', msg);
      });
    }
  }

  render() {
    const { data, selectedRowKeys, cells } = this.state;

    const topTools = [
      {
        type: 'batchDelete',
        icon: 'Icon-Batch-delete',
        title: '批量删除',
      },
      {
        type: 'add',
        icon: 'Icon-add-to03',
        title: '新增',
        // onClick: this.customAdd, // 通过onClick方法复写默认的新增事件
      },
      {
        type: 'custom',
        icon: <Button type="primary">必填校验</Button>,
        title: '必填校验',
        onClick: this.validate,
      },
      {
        type: 'maximize',
        icon: 'Icon-Global-zoom-in',
        title: '最大化',
      }
    ];

    return (
      <EditableTable
        ref={this.editableTableRef}
        title="表格标题"
        columns={columns}
        comProps={comProps}
        cascadeRules={cascadeRules}
        cells={cells}
        data={data}
        topTools={topTools}
        selectedRowKeys={selectedRowKeys}
        deleteConfirm={true}
        sortable={true}
        sortableType="icon"
        onChange={this.onChange}
        onCellsChange={this.onCellsChange}
        onRowSelect={this.onRowSelect}
        onItemEditBefore={this.onItemEditBefore} // 可通过更新cells,自定义跨行跨列的联动
      />
    );
  }
}

export default EditableTableDemo;
```

### 多级数据

data数据结构和Table有所差异：数据铺开,通过isParent parentId区分数据（注意：columns中各项加一起width需要尽量为100%）

```jsx
import React, { Component } from 'react';
import { EditableTable, FormItemProps, Button, FormValue, Icon, Switch } from '@weapp/ui';

const data = [
  {
    id: '1222',
    name: '张天逸1(一级)',
    order: 0,
    switch: true,
  },
  {
    id: '1',
    isParent: true, // 是否有下级节点
    name: '张天逸2(一级)',
    order: 0,
    switch: true,
  },
  {
    id: 'child_1',
    isParent: true,  // 是否有下级节点
    parentId: '1', // 父节点id
    name: '小王(二级)',
    order: 1,
    switch: false,
  },
  {
    id: 'child_2',
    parentId: '1', // 父节点id
    name: '小李(二级)',
    order: 2,
    switch: false,
  },
  {
    id: 'secondChild_1',
    isParent: true,  // 是否有下级节点
    parentId: 'child_1', // 父节点id
    name: '小张(三级)',
    order: 3,
    switch: false,
  },
  {
    id: 'fourChild_1',
    parentId: 'secondChild_1', // 父节点id
    name: '小张(四级)',
    order: 3,
    switch: false,
  },
  {
    id: 'secondChild_2',
    parentId: 'child_1', // 父节点id
    name: '小速(三级)',
    order: 3,
    switch: false,
  },
];

const columns = [
  {
    title: '名称(Input)',
    dataIndex: 'name', // 列标识(cells联动设置需要）
    comKey: 'name', // 当前列组件的key, 和data中的数据对应, 和comProps对应（注意: comKey不能重复）
    showRequired: true, // 是否显示必填小红点
    width: '70%',
  },
  {
    title: '编号(InputNumber)',
    dataIndex: 'order',
    comKey: 'order',
    width: '10%',
  },
  {
    title: '选择(switch)',
    dataIndex: 'switch',
    comKey: 'switch',
    width: '20%',
  },
];

const comProps: FormItemProps = {
  'name': { // 以下可配置 input组件 的所有属性 ('name' -- 对应columns的comKey)
    itemType: 'INPUT',  // 字段类型
    style: {  // 样式（组件属性）
      width: '100px'
    },
    required: true, // 是否必填
  },
  'order': {
    itemType: 'INPUTNUMBER',
    style: {
      width: '120px'
    }
  },
  'switch': {
    itemType: 'SWITCH',
  },
}

type EditableTableDemoState = {
  data: any,
  selectedRowKeys: string[],
  expandRowKeys: string[],
  hideNoChildNodeLine: boolean;
  useExpandLineWithNum: boolean
}

class EditableTableDemo extends Component<{}, EditableTableDemoState> {
  constructor(props: {}) {
    super(props);
    this.state = {
      data,
      selectedRowKeys: [],
      expandRowKeys: ['1'],
      hideNoChildNodeLine: false,
      useExpandLineWithNum: false
    }
  }

  editableTableRef = React.createRef();

  onChange = (data: any) => {
    console.log('data', data)
    this.setState({ data });
  }

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

  onItemChange = (editKey: string, editValue: FormValue, rowId: string, rowData: any) => {
    console.log('onItemChange', editKey, editValue, rowId, rowData)
  }

  validate = () => {
    const editableTableRef: any = this.editableTableRef && this.editableTableRef.current;
    console.log('editableTableRef', editableTableRef);
    if (editableTableRef) {
      editableTableRef.doValidate();
    }
  }

  handleChange = (val: boolean) => {
    this.setState({ hideNoChildNodeLine: val })
  }

  handleNumChange = (val: boolean) => {
    this.setState({ useExpandLineWithNum: val })
  }

  onRowSelect = (selectedRowKeys: string[], selectedRows: any[], changeRow: any | any[]) => {
    console.log(selectedRowKeys, selectedRows, changeRow)
    this.setState({ selectedRowKeys });
  }

  render() {
    const { data, selectedRowKeys, expandRowKeys, hideNoChildNodeLine, useExpandLineWithNum } = this.state;
    const topTools = [
      {
        type: 'batchDelete',
        icon: 'Icon-Batch-delete',
        title: '批量删除',
      },
      {
        type: 'add',
        icon: 'Icon-add-to03',
        title: '新增',
      },
      {
        type: 'custom',
        icon: <Button type="primary">必填校验</Button>,
        title: '必填校验',
        onClick: this.validate,
      },
    ];

    return (
      <>
        <div style={{ display: 'flex' }}>
          <div style={{ display: 'flex' }}>
            <span>是否仅展示叶子节点的折线图标：</span>
            <Switch
             
              value={hideNoChildNodeLine}
              onChange={this.handleChange}
            />
          </div>
          <div style={{ display: 'flex', marginLeft: 15 }}>
            <span>是否展示子节点数量：</span>
            <Switch
             
              value={useExpandLineWithNum}
              onChange={this.handleNumChange}
            />
          </div>
        </div>
        <div style={{ color: 'red', margin: '10px 0' }}>注意📢：目前暂不支持父节点向子节点内拖拽场景</div>
        <EditableTable
         
          ref={this.editableTableRef}
          title="表格标题"
          columns={columns}
          data={data}
          topTools={topTools}
          selectedRowKeys={selectedRowKeys}
          comProps={comProps}
          needExpand={true}
          expandRowKeys={expandRowKeys}
          onExpand={this.onExpand}
          deleteConfirm={true}
          onChange={this.onChange}
          onItemChange={this.onItemChange}
          useExpandLine
          useExpandLineWithNum={useExpandLineWithNum}
          hideNoChildNodeLine={hideNoChildNodeLine}
          customExpandIcon={[
            <Icon size={'sm'} name={'Icon-Down-arrow04'} />,
            <Icon size={'sm'} name={'Icon-Right-arrow04'} />
          ]}
          onRowSelect={this.onRowSelect}
          selection={{ isLinkageBySelect: true }}
          sortable
          sortableType='icon'
        />
      </>
    );
  }
}

export default EditableTableDemo;
```

### 特定单元格配置(详见下方具体说明)

通过cells配置特定单元格

```jsx
import React, { Component } from 'react';
import { EditableTable, FormItemProps, Button, FormValue, EditableTableValidatorType, Help } from '@weapp/ui';

function bodyRender(data: any, pos: any, name: any) {
  const style = { width: 100 };
  if (data.id === '1') {
    return (
      <div style={style}>
        这是自定义显示1
      </div>
    )
  }
  return (
    <div style={style}>
      这是自定义显示2
    </div>
  )
}

const data = [
  {
    id: '1',
    name: '',
    order: 1,
    des: '简介1',
    group: ['g1'],
    sex: 'man',
    about: ["about1", "about2"],
    date: '2019-6-06',
    time: '12:13:14',
    check: true,
    level: { min: 1, max: 100 },
    color: '#ce1919',
    rate: 1,
    switch: true,
    text: '111',
  },
  {
    id: '2',
    name: '彭浩秋',
    order: 1,
    des: '简介2',
    group: ['g1'],
    sex: 'woman',
    about: ['about1'],
    date: '2021-3-04',
    time: '13:14:15',
    check: false,
    level: { min: 1, max: 99 },
    color: '#f1f2f3',
    rate: 3,
    switch: false,
    text: '222',
  },
  {
    id: '3',
    name: '',
    order: 1,
    des: '',
    group: ['g1'],
    sex: 'woman',
    about: ['about1'],
    date: '2021-3-04',
    time: '00.01',
    check: false,
    level: { min: 1, max: 99 },
    color: '#f1f2f3',
    rate: 3,
    switch: false,
    text: '222',
  }
];

const columns = [
  {
    title: '名称(Input)',
    dataIndex: 'name', // 列标识(cells联动设置需要）
    comKey: 'name', // 当前列组件的key, 和data中的数据对应, 和comProps对应（注意: comKey不能重复）
    showRequired: true, // 是否显示必填小红点
    width: '5.5%',
  },
  {
    title: '编号(InputNumber)',
    dataIndex: 'order',
    comKey: 'order',
    width: '5.5%',
  },
  {
    title: '简介(Textarea)',
    dataIndex: 'des',
    comKey: 'des',
    showRequired: true, // 是否显示必填小红点
    width: '5.5%',
  },
  {
    title: '分组(Select)',
    dataIndex: 'group',
    comKey: 'group',
    width: '5.5%',
  },
  {
    title: '性别(Radio)',
    dataIndex: 'sex',
    comKey: 'sex',
    width: '5.5%',
  },
  {
    title: '关联(Checkbox)',
    dataIndex: 'about',
    comKey: 'about',
    width: '5.5%',
  },
  {
    title: '列全选(Checkbox)',
    dataIndex: 'check',
    comKey: 'check',
    width: '5.5%',
    showCheckAll: true,
    checkAllProps: {
      itemType: 'CHECKBOX'
    },
  },
  {
    title: '列全选(Switch)',
    dataIndex: 'switchCheck',
    comKey: 'switchCheck',
    width: '5.5%',
    showCheckAll: true,
    checkAllProps: {
      itemType: 'SWITCH'
    },
  },
  {
    title: '日期(DatePicker)',
    dataIndex: 'date',
    comKey: 'date',
    width: '5.5%',
  },
  {
    title: '时间(TimePicker)',
    dataIndex: 'time',
    comKey: 'time',
    width: '5.5%',
  },
  {
    title: '附件(Upload)',
    dataIndex: 'file',
    comKey: 'file',
    width: '5.5%',
  },
  {
    title: '颜色(ColorPicker)',
    dataIndex: 'color',
    comKey: 'color',
    width: '5.5%',
  },
  {
    title: '安全级别(Scope)',
    dataIndex: 'level',
    comKey: 'level',
    width: '10%',
  },
  {
    title: '成员(Browser)',
    dataIndex: 'member',
    comKey: 'member',
    width: '5.5%',
  },
  {
    title: '评分(RATE)',
    dataIndex: 'rate',
    comKey: 'rate',
    width: '5.5%',
  },
  {
    title: '启用(Switch)',
    dataIndex: 'switch',
    comKey: 'switch',
    width: '5.5%',
  },
  {
    title: <span>纯文本<Help title="提示信息" /></span>,
    dataIndex: 'text',
    width: '5.5%',
    comKey: '',
  },
  {
    title: 'bodyRender自定义渲染',
    dataIndex: 'custom',
    comKey: '',
    width: '5.5%',
    bodyRender,
  },
];

const comProps: FormItemProps = {
  'name': { // 以下可配置 input组件 的所有属性 ('name' -- 对应columns的comKey)
    itemType: 'INPUT',  // 字段类型
    defaultValue: '默认值', // 默认值 （组件属性）
    style: {  // 样式（组件属性）
      width: '100px'
    },
    required: true, // 是否必填
    // rules: 'required', // 必填校验规则: 如果是其他规则,可通过rules配置
  },
  'order': {
    itemType: 'INPUTNUMBER',
    style: {
      width: '120px'
    }
  },
  'des': {
    itemType: 'TEXTAREA',
    // required: true, // 是否必填
    rules: 'required|maxLen20', // 这里是maxLen20是通过customRegister注册的校验规则
  },
  'group': {
    itemType: 'SELECT',
    data: [
      { id: 'g1', content: '分组1' },
      { id: 'g2', content: '分组2' },
      { id: 'g3', content: '分组3' }
    ],
    defaultValue: 'g1',
    style: {
      width: '100px'
    }
  },
  'sex': {
    itemType: 'RADIO',
    data: [
      { id: 'man', value: 'man', content: '男' },
      { id: 'woman', value: 'woman', content: '女' }
    ],
    defaultValue: 'man',
  },
  'about': {
    itemType: 'CHECKBOX',
    data: [
      { id: 'about1', value: 'about1', content: '关联1' },
      { id: 'about2', value: 'about2', content: '关联2' }
    ],
    defaultValue: ['about1'],
  },
  'check': {
    itemType: 'CHECKBOX',
  },
  'date': {
    itemType: 'DATEPICKER',
  },
  'time': {
    itemType: 'TIMEPICKER',
  },
  'file': {
    itemType: 'UPLOAD',
    showListUploading: false, // 不显示上传中文件列表
  },
  'color': {
    itemType: 'COLORPICKER',
    // rules: 'color', // customRegister注册的校验规则
  },
  'level': {
    itemType: 'SCOPE',
    defaultVaue: { min: 1, max: 100 },
  },
  'member': {
    itemType: 'BROWSER',
  },
  'rate': {
    itemType: 'RATE',
    width: '8%'
  },
  'switch': {
    itemType: 'SWITCH',
  },
  'switchCheck': {
    itemType: 'CHECKBOX',
  },
}

const customValidator: EditableTableValidatorType = {
  // message: { // 自定义校验提示信息
  //   'required': {
  //     string: "此项必填(自定义)"
  //   }
  // },
  customRegister: [ // 自定义校验规则(comProps的rules需要使用注册的规则才会生效)
    {
      name: "maxLen20",
      rules: (value: any, requirement: any, attribute: any) => {
        return value.length < 21;
      },
      errorMessage: "最大长度20"
    },
    // {
    //   name: "color",
    //   rules: (value: any, requirement: any, attribute: any) => {
    //     return /^#[0-9a-fA-F]{3, 6}$/.test(value);
    //   },
    //   errorMessage: ":attribute 不符合颜色值格式"
    // }
  ]
};

type EditableTableDemoState = {
  data: any,
  selectedRowKeys: string[],
}
class EditableTableDemo extends Component<{}, EditableTableDemoState> {
  constructor(props: {}) {
    super(props);
    this.state = {
      data,
      selectedRowKeys: [],
    }
  }

  editableTableRef = React.createRef();

  onItemBlur = (editKey: string, editValue: FormValue, rowId: string) => {
    console.log('onItemBlur', editKey, editValue, rowId)
  }

  onItemEditBefore = (newData: any, editKey: string, editValue: FormValue) => {
    console.log('onItemEditBefore', newData, editKey, editValue);
    return newData; // 如果有自定义需求,可以根据需求更改newData
  }

  onAddBefore = (newData: any, rowData: any) => {
    console.log('onAddBefore', newData, rowData);
    return newData;
  }

  onDeleteBefore = (newData: any, ids: string[]) => {
    console.log('onDeleteBefore', newData, ids);
    return newData;
  }

  onRowOperateBefore = (newData: any, rowData: any, type: 'insertUp' | 'insertDown' | 'copy') => {
    console.log('onRowOperateBefore', newData, rowData, type);
    return newData;
  }

  onBatchEditBefore = (newData: any, ids: string[], editKey: string, editValue: FormValue) => {
    console.log('onBatchEditBefore', newData, ids, editKey, editValue);
    return newData;
  }

  onChange = (data: any) => {
    console.log('data', data)
    this.setState({ data });
  }

  onCheckAllChange = (key: string, value: boolean) => {
    console.log('onCheckAllChange', key, value);
  }

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

  validate = () => {
    const editableTableRef: any = this.editableTableRef && this.editableTableRef.current;
    console.log('editableTableRef', editableTableRef);
    if (editableTableRef) {
      editableTableRef.doValidate((msg: object) => {
        console.log('validateCallback', msg);
      });
    }
  }

  customAdd = () => {
    alert('customAdd');
  }

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

    const topTools = [
      {
        type: 'batchEdit',
        icon: 'Icon-Batch-modification',
        title: '批量编辑',
      },
      {
        type: 'batchDelete',
        icon: 'Icon-Batch-delete',
        title: '批量删除',
      },
      {
        type: 'add',
        icon: 'Icon-add-to03',
        title: '新增',
        // onClick: this.customAdd, // 通过onClick方法复写默认的新增事件
      },
      {
        type: 'custom',
        icon: <Button type="primary">必填校验</Button>,
        title: '必填校验',
        onClick: this.validate,
      },
      {
        type: 'maximize',
        icon: 'Icon-Global-zoom-in',
        title: '最大化',
      }
    ];

    const rowTools = [
      {
        type: 'delete',
        icon: 'Icon-delete',
        title: '删除',
      },
      {
        type: 'insertUp',
        icon: 'Icon-Insert-forward',
        title: '向前插入一行',
      },
      {
        type: 'insertDown',
        icon: 'Icon-Insert-after-line',
        title: '向后插入一行',
      },
      {
        type: 'copy',
        icon: 'Icon-copy',
        title: '复制',
      },
    ];

    const cells: any = {
      '1': {
        'name': [],
        'des': ''
      },
      '2': {
        'name': [],
        'des': ''
      },
    }
    return (
      <EditableTable
       
        ref={this.editableTableRef}
        title={<span>表格标题<Help title="提示信息" /></span>}
        columns={columns}
        data={data}
        topTools={topTools}
        rowTools={rowTools}
        selectedRowKeys={selectedRowKeys}
        comProps={comProps}
        customValidator={customValidator} // 自定义必填校验配置
        deleteConfirm={true}
        scroll={{ x: 3000 }} // 因为这里内容过多,所以加了滚动处理
        sortable={true}
        sortableType="icon"
        onChange={this.onChange}
        onCheckAllChange={this.onCheckAllChange}
        onRowSelect={this.onRowSelect}
        onItemBlur={this.onItemBlur} // 单元格失去焦点的回调
        onItemEditBefore={this.onItemEditBefore} // 不需要更新前做操作就不传
        onAddBefore={this.onAddBefore} // 不需要更新前做操作就不传
        onDeleteBefore={this.onDeleteBefore} // 不需要更新前做操作就不传
        onRowOperateBefore={this.onRowOperateBefore} // 不需要更新前做操作就不传
        onBatchEditBefore={this.onBatchEditBefore} // 不需要更新前做操作就不传
        disableRowTools={["1"]} //设置表格的第一行工具栏不显示
        cells={cells}
        displayLocationForRequiredMark="afterFields"
        displayLocationForRequiredMarkPriority='props'
      />
    );
  }
}

export default EditableTableDemo;
```

## API

#### Tip：

1、columns 中各项加一起 width 需要尽量为 100%。否则会影响 check 框、拖拽按钮、浮动工具栏的效果

2、可编辑默认 ui 改为了顶部对齐,需要居中对齐通过 rowAlign 参数修改

### 参数说明如下：可编辑列表继承 Table 的所有属性

| 参数 | 说明 | 类型 | 是否必填 | 默认值 |  |
| --- | --- | --- | --- | --- | --- |
| className | class 名 | string | 否 |  |  |
| preficCls | 默认组件 class 名 | string | 否 |  |  |
| style | 样式 | CSSProperties | 否 |  |  |
| customRender | 自定义组件渲染 | (props: any, ele: ReactElement) => ReactElement | 否 |  |  |
| columns | 列配置 | TableColumn[] | 是 | [] |  |
| data | 数据 | any[] | 是 | [] |  |
| comProps | 属性配置(详见下方具体说明) | FormItemProps | 是 | {} |  |
| cascadeRules | 级联规则(详见下方具体说明) | any | 否 |  |  |
| cells | 特定单元格配置(详见下方具体说明) | EditableTableCellType | 否 |  |  |
| collapsed | 是否可折叠 | boolean | 否 | false |  |
| collapsePanelId | 折叠面板 id | string | 否 | 'tablePanel' |  |
| collapseProps | 折叠组件属性 | ICollapseProps | 否 |  |  |
| collapsePanelProps | 折叠面板属性 | ICollapsePanelProps | 否 |  |  |
| showSelection | 是否显示第一列的列选中 check 框 | boolean | 否 | true |  |
| selectedRowKeys | 选中的每行唯一标识组成的数组 | string[] | 否 |  |  |
| rowAlign | 行内对齐方式 | 'top' \| 'middle' | 否 | 'top' |  |
| title | 标题 | string \| React.ReactElement | 否 | '' |  |
| validateDelay | 必填校验显示的时长,单位 ms | number | 否 | 10000 |  |
| validateErrorType | 必填校验显示效果('normal'为在可视窗口内显示, 'popover'为弹层显示) | 'normal' \| 'popover' | 否 | 'normal' |  |
| maximizeDailogTitle | 最大化弹框的 title | string | 否 | '预览' |  |
| maximizeDailogProps | 最大化弹框的 props,继承 dialog 的 props | DialogProps | 否 |  |  |
| maximizing | 最大化弹框显影 | boolean | 否 | false |  |
| batchEditing | 批量编辑弹框显影 | boolean | 否 | false |  |
| deleteConfirm | 删除的时候是否弹框确认 | boolean | 否 | true |  |
| topTools | 头部工具配置 | ToolItemConfig[] | 否 |  |  |
| rowTools | 行内工具配置 | ToolItemConfig[] | 否 |  |  |
| showPagination | 是否显示分页 | boolean | 否 | false |  |
| page | 当前页数 | number | 否 | 1 |  |
| defaultPage | 默认当前页数 | number | 否 | 1 |  |
| pageSize | 每页数量 | number | 否 | 10 |  |
| defaultPageSize | 默认每页数量 | number | 否 | 10 |  |
| requiredMark | 自定义必填图标（和 Form 一致） | string \| IconNames | 否 |  |  |
| requiredMarkProps | 自定义Icon必填标识属性（和 Form 一致） | IconProps | 否 |  |  |
| customValidator | 自定义校验规则配置（基础用法 demo 中有参考代码） | EditableTableValidatorType | 否 |  |  |
| onPageChange | 页码改变的回调, 参数是改变后的页码及每页条数 | (value: number, pageSize: number) => void | 否 |  |  |
| onPageSizeChange | 每页数量变化的回调, 参数是改变后的页码及每页条数 | (value: number, pageSize: number) => void | 否 |  |  |
| onItemEditBefore | 编辑单元格,data 改变之前的回调 | (newData: any, editKey: string, editValue: FormValue, rowData: any) => void | 否 |  |  |
| onAddBefore | 新增行数据前的回调 | (newData: any, rowData: any) => void | 否 |  |  |
| onRowOperateBefore | 行数据操作,data 改变之前的回调 | (newData: any, rowData: any, type: 'insertUp' \| 'insertDown' \| 'copy') => void | 否 |  |  |
| onBatchEditBefore | 批量编辑, data 改变之前的回调 | (newData: any, ids: string[], editKey: string, editValue: FormValue) => void | 否 |  |  |
| onDeleteBefore | 删除之前的回调 | (newData: any, ids: string[]) => void | 否 |  |  |
| onChange | data 改变的回调 | (data: any) => void | 否 |  |  |
| onCellsChange | cells 改变的回调 | (cells: EditableTableCellType) => void | 否 |  |  |
| onCheckAllChange | 头部列全选框点击的回调 | (key: string, value: boolean) => void | 否 |  |  |
| onItemChange | 单元格 onChange 的回调 | (editKey: string, editValue: FormValue, rowId: string, rowData: any) => void | 否 |  |  |
| onItemBlur | 单元格 onBlur 的回调 | (editKey: string, editValue: FormValue, rowId: string, rowData: any) => void | 否 |  |  |
| onItemFocus | 单元格 onFocus 的回调 | (editKey: string, editValue: FormValue, rowId: string, rowData: any) => void | 否 |  |  |
| onAdd | 自定义新增事件,不走组件默认的新增 | (addRowData: any) => viod | 否 |  |  |
| onDelete | 自定义删除事件,不走组件默认的删除 | (ids: string[]) => viod | 否 |  |  |
| onMaximizeChange | 放大缩小的回调 | (maximize: boolean) => void | 否 |  |  |
| onRowSelect | 行选中变化回调 | (selectedRowKeys: string[], selectedRows: any[], changeRow: any | any[]) => void | 否 |  |
| onCollapseChange | 折叠点击回调 | (value: string) => void | 否 |  |  |
| keepOriginalData | 保留原数据(用于级联字段过滤数据规则) | boolean | 否 |  |  |
| parentRowKey | 自定义父节点的判断字段(parentId字段冲突时使用) | string | 否 |  |  |
| displayLocationForRequiredMark | 必填信息显示位置(和Form一致) | FormDisplayLocationForRequiredMark | 否 |  |  |
| displayLocationForRequiredMarkPriority | 必填信息显示位置设置优先级(global 全局设置；props：组件参数设置) | 'global' \| 'props' | 否 | 'global' |  |
| onBeforeRequiredChange | 开启必填信息填写后隐藏必填标记开关，头部必填标记显影前的回调 | (showRequiredMark: boolean, column: ITableColumn, data: any[]) => boolean | 否 |  |  |
| newValidateTipType | 新版校验提示效果 | boolean | 否 | false |  |
| enabelValidateTipPosition | 启用校验信息定位功能 | boolean | 否 | false |  |
| customJudgeEmpty | 自定义必填判空方法 | customJudgeEmpty?: (params: { item: FormItemProps, com: string[], itemType: FormItemFieldType, value: any, rowData: any, isEmptyResult: boolean }) => boolean; | 否 |  |  |
| customRowValidateFunc | 自定义行校验规则 | (params: { item: FormItemProps, com: string[], itemType: ItemType, value: any, rowData: any, isEmptyResult: boolean }) => boolean | 否 |  |  |
### 通过 ref 调用的方法

| 参数 | 说明 | 类型 |
| --- | --- | --- |
| doValidate | 校验方法 | Function(callbackFunc) => void |
| updateSelectRowKeys | 用来更新组件内部的selectRowKeys | Function(ids) => void |
#### ToolItemConfig 配置说明

| 参数 | 说明 | 类型 |
| --- | --- | --- |
| type | 按钮类型, 默认有 'batchEdit': 批量编辑 \| 'batchDelete': 批量删除 \| 'add': 新增 \| 'maximize': 放大 \| 'delete': 删除 \| 'insertUp': 向前插入一行 \| 'insertDown': 向后插入一行 \| 'copy': 复制 | string |
| title | title 信息 | string |
| icon | 图标或者自定义显示 | React.ReactNode |
| onClick | 自定义点击事件 | () => void |
#### TableColumn 配置说明,继承 table 的配置

| 参数 | 说明 | 类型 | 是否必填 |
| --- | --- | --- | --- |
| dataIndex | 唯一标识 | string | 是 |
| comKey | comProps 的索引, 结合配置当前列的显示效果。为''则不渲染成组件，直接渲染只读文本 | string | 是 |
| title | 头部显示 | React.ReactNode | 否 |
| showCheckAll | 是否显示头部列全选 check 框 | boolean | 否 |
| checkAllProps | 头部列全选组件的配置（同 comProps 中的单项配置） | { [key: string]: any } | 否 |
#### FormItemProps 配置说明 - 同 FormSwitch 的配置, 除 itemType，其他属性为当前组件的所有属性

| 参数 | 说明 | 类型 | 是否必填 |
| --- | --- | --- | --- |
| itemType | 字段类型 | string | 是 |
| required | 是否必填 | boolean | 否 |
| rules | 除了必填之外的校验规则 | string | 否 |
```
const columns = [
  {
    title: '姓名(Input)',
    dataIndex: 'name',
    comKey: 'input',
  },
]

const comProps = [
  'input': { // 以下可配置 input组件 的所有属性
    itemType: 'INPUT', // 字段类型
    defaultValue: '默认值' , // Input的属性
    customRender: (formItemInstance: ReactNode, content: any, rowData: any, data: any, comProps: any, pos: any)=>{
      return <div>{rowData.name}</div>
     }
  },
]
```

#### cascadeRules 配置说明 (提供以下三种配置规则)

```
const cascadeRules = {
  // 1、通过具体的值匹配规则: object （这里对应下拉框的值 '1' | '2' | '3' | '4'）
  'select': {
    '1': ['input'],
    '2': ['textarea'],
    '3': ['browser'],
    '4': ['select2'],
  },
  // 2、通过方法匹配规则: function
  'select2': (editValue: any, rowData: any) => {
    if (editValue === '2') {
      return ['input2']
    }
    return [];
  },
  // 3、 直接匹配规则, 表示有值就显示
  'browser': ['input3'],
};
```

#### cells 配置说明

通过行 id 和列的 dataIndex 匹配 comKey

```
EditableTableCellType 说明：
EditableTableCellType = { [rowId: string]: { [key: string]: ComKeyType } }

const cells: EditableTableCellType = {
  '1': {
    'cross': ['select3']
  },
  '2': {
    'cascader': ['select', 'textarea'],
  },
  '3': {
    'cascader': ['select', 'select2', 'input2'],
  }
}
```
