react基于antd Table组件实现行内可编辑表格

737 阅读1分钟
import React from 'react';
import { Table, Input, InputNumber, Form, Select } from 'antd';
import { OPTION_TYPE } from '../../constants/typeConstant';
import event, { EVENT_TYPE } from '../../utils/event';
import Common from '../../utils/common';

// 支持按行编辑的table
// 必传参数:columns、onSave、newRowKey、updateList
// column以editable标识是否可编辑, inputType标识编辑时为input还是select等, optionList标识为select时的可选值

const EditableContext = React.createContext();

class EditableCell extends React.Component {
  getInput = () => {
    const {inputType, mode, optionList} = this.props;
    switch(inputType){
        case 'input':
            return <Input style={{width: '100%'}}/>;  
        case 'number':
            return <InputNumber style={{width: '100%'}}/>; 
        case 'select':
            return <Select style={{width: '100%'}}
                showSearch
                allowClear
                mode={mode || 'multiple'}
                optionFilterProp="children"
                   >
                    {optionList?.map((item) => (
                        <Select.Option value={item.value} key={item.value}>
                            {item.label}
                        </Select.Option>
                    ))
                    }
                </Select>
                }
    return <Input/>;
  };

  renderCell = ({ getFieldDecorator }) => {
    const {
      editing,
      dataIndex,
      title,
      inputType,
      required,
      record,
      index,
      children,
      ...restProps
    } = this.props;
    return (
      <td {...restProps}>
        {editing ? (
          <Form.Item style={{ margin: 0 }}>
            {getFieldDecorator(dataIndex, {
              rules: [
                {
                  required: required == true || false,
                  message: required == true ? `请输入 ${title}!`: ''
                }
              ],
              initialValue: (record[dataIndex] && Array.isArray(record[dataIndex])) ? 
                record[dataIndex]?.map(item => Common.types(item).isObject ? item.value : item) : 
                (Common.types(record[dataIndex]).isObject ? record[dataIndex].value : record[dataIndex])
            })(this.getInput())}
          </Form.Item>
        ) : (
          children
        )}
      </td>
    );
  };

  render() {
    return <EditableContext.Consumer>{this.renderCell}</EditableContext.Consumer>;
  }
}

class EditableTable extends React.Component {
  constructor(props) {
    super(props);
    this.state = { editingKey: '' };
  }

  componentDidMount(){
    event.addListener(EVENT_TYPE.ADD_ROW_IN_EDITABLE_TABLE, this.editNewRow);
  }

  componentWillUnmount(){
    event.removeListener(EVENT_TYPE.ADD_ROW_IN_EDITABLE_TABLE, this.editNewRow);
  }

  isEditing = record => record.key === this.state.editingKey;

  // 取消
  cancel = (key) => {
    const {updateList} = this.props;
    updateList && updateList();
    this.setState({ editingKey: '' });
  };

  //保存
  save(form, oldRow, onSave) {
    const { updateList} = this.props;
    form.validateFields((error, row) => {
      if (error) {
        return;
      }

      //调用自定义保存方法
      const newRow = {...oldRow, ...row};
      onSave(newRow);
      updateList && updateList();
      this.setState({ editingKey: '' });
    });
  }

  // 切换为编辑状态
  edit(key) {
    this.setState({ editingKey: key });
  }

  // 新增行切换为编辑状态
  editNewRow = () =>{
    const {newRowKey} = this.props;
    this.setState({ editingKey: newRowKey || 'new_row' });
  }

  formatColumns = () => {
    const {columns, handleView, handleDelete} = this.props;
    const newColumns = [...columns];
    return newColumns?.map(column => {
        if(column?.dataIndex == 'operation'){
            const viewOption =  column.options?.find(option => option.type === OPTION_TYPE.VIEW);
            const editOption =  column.options?.find(option => option.type === OPTION_TYPE.EDIT);
            const deleteOption =  column.options?.find(option => option.type === OPTION_TYPE.DELETE);
            column.render = (text, record) => {
                const { editingKey } = this.state;
                const editable = this.isEditing(record);
                return <>
                {
                  viewOption && !editable &&
                  viewOption?.render(record)
                }
                {editOption &&(
                  editable ? (
                    <span>
                      <EditableContext.Consumer>
                        {form => (
                          <a
                              onClick={() => this.save(form, record, editOption.onSave)}
                              style={{ marginRight: 10 }}
                          >
                            保存
                          </a>
                        )}
                      </EditableContext.Consumer>
                      <a
                          onClick={() => this.cancel(record.key)}
                      >
                          取消
                       </a>
                    </span>
                  ) : (
                    <a disabled={editingKey !== ''} 
                        onClick={() => this.edit(record.key)}
                        style={{ marginRight: 10 }}
                    >
                      编辑
                    </a>
                  )
                )}
                {
                  deleteOption && !editable &&
                  deleteOption?.render(record)
                }
                </>
            }
        }
        if(!column.editable) {
          return column;
        }
        return {
        ...column,
        onCell: record => ({
            record,
            inputType: column.inputType,
            dataIndex: column.dataIndex,
            title: column.title,
            required: column.required,
            mode: column.mode, 
            optionList: column.optionList,
            editing: this.isEditing(record)
        })
        };
    });
  }

  render() {
    const {columns, ...restProps} = this.props;
    const components = {
      body: {
        cell: EditableCell
      }
    };

    return (
      <EditableContext.Provider value={this.props.form}>
        <Table
            components={components}
            columns={this.formatColumns()}
            {...restProps}
        />
      </EditableContext.Provider>
    );
  }
}

export default Form.create()(EditableTable);