antd的Table组件实现增加一行可编辑的单元格

发布于:2025-02-11 ⋅ 阅读:(9) ⋅ 点赞:(0)

目录

1、效果图

2、代码

2.1 父页面:

2.2 表格子组件:

3、代码解释


1、效果图

效果图:

关注表格,表格是可以左右滑动的,可以新增一行,新增的一行都是可编辑的单元格

纯展示:

2、代码

需求:

一个页面上需要展示一个可编辑表格。这个页面就称它为父页面,表格为子页面。

多个页面都需要使用这个表格,把这个表格做成了公共组件的形式。

2.1 父页面:
// 父页面, react的函数组件的写法
import { connect } from '@cmbc/apollo'
import React from 'react'
import AddInputTable from './addInputTable'

const { memo } = React

const FeedDetailView = (props) => {

    const feedmation = () => (
    <div>
        <form>
            <AddInputTable 
                {...getFieldProps('supFeedbackWzList')} // 组件写在form里,子组件就可以拿到AddInputTable组件身上的value, onChange
                placeholder='请输入'
                feedBackId={feedDetailsPro.id}
                isEditTable={true} // 判断表格是可编辑状态还是纯展示数据状态
            />
        </form>
    </div>

    )



    return (
        <div>
            {feedmation()}
        </div>
    )
}

const mapStateToProps = ({ feedDetails }) => ({ ...feedDetails })
const mapDispatchToProps = dispatch => ({

})

export default connect(mapStateToProps, mapDispatchToProps)(createForm()(memo(FeedDetailView )))

2.2 表格子组件:
import React, { Component } from 'react'
import { Table, Input, InputNumber, Form, Select, Icon, DatePicker } from 'antd'
import { Toast } from 'antd-mobile'
import styles from './addInputTable.less'
import moment from 'moment'
import axios from 'axios'

const { TextArea } = Input
const Option = Select.Option
const EditableContext = React.createContext()

// 单元格组件
class EditableCell extends React.Component {
  renderCell = (form) => {
    const { editing, dataIndex, title, inputType, record, index, children, feedBackId, dictionaryList, ...restProps } = this.props
    const wzPunishList = dictionaryList && dictionaryList.length > 0 ? dictionaryList.map(i => <Option key={i.dataKey} value={i.dataValue}>{i.dataValue}</Option>) : null

    const onWzChange = (value, form, dataIndex, record, index) => {
      const newVal = this.props.value.map((i, ri) => {
        if(ri === index){
          let temp = value
          if(dataIndex === 'wzCompleteTime'){
            temp = value && value !== null ? moment(value).format('YYYY-MM-DD hh:mm:ss') : null
          }
          return {
            ...i,
            [dataIndex]: temp
          }
        }
        return i
      })
      this.props.onValChange(newVal) // 更新父组件的值,即更新页面展示的数据
    }

    const getInput = (form, record, dataIndex, index, title) => {
      const records = record && record.resultData
      const type = this.props.inputType()
      switch (type) {
        case 'TextArea':
          // 解释1:e.stopPropagation() 阻止冒泡
          view = <TextArea placeholder={`请输入${title}`} autoSize maxlength={200} onChange={(e) => { e.stopPropagation(); onWzChange(e && e.target && e.target.value, form, dataIndex, record, index, title) }} />
          break;
        case 'DatePicker':
          view = <DatePicker placeholder={`请输入${title}`}  onChange={(e) => onWzChange(e, form, dataIndex, record, index, title) } />
          break;
        case 'Select':
          view = <Select placeholder={`请输入${title}`} onChange={(e) => onWzChange(e, form, dataIndex, record, index, title) }>{wzPunishList}</Select>
          break;
        case 'InputNumber':
          view = <InputNumber placeholder={`请输入${title}`} precision={4} min={0} onChange={(e) => onWzChange(e, form, dataIndex, record, index, title) } />
          break;
        default:
          break;
      }
      return view;
    }

    return (
      <td {...restProps}>
        {editing ? (
          this.props.isEditTable ?
            <Form.Item style={{ margin: 0 }}>
              {
                form.getFieldDecorator(`${dataIndex}_${record && record.id}`, {
                  initialValue: inputType() === 'DatePicker' ? (dataIndex === 'wzCompleteTime' && record.wzCompleteTime !== null ? moment(record.wzCompleteTime) : null) : record[dataIndex] // 单独处理日期时间的回显
                })(
                  getInput(form, record, dataIndex, index, title)
                )
              }
            </Form.Item> : ''
        ) : (
          children
        )}
      </td>
    )
  }
  render() {
    return <EditableContext.Consumer>{this.renderCell}</EditableContext.Consumer>
  }
}


// 表格组件
class EditableTable extends React.Component {
  constructor(props) {
    super(props)
    this.columns = [
      {
        title: '序号',
        key: 'number',
        dataIndex: 'number',
        align: 'center',
        render: (text, record, index) => {
          <span key={index}>{index + 1}</span>
        }
      },
      {
        title: '问责人员姓名',
        dataIndex: 'wzUserName',
        align: 'center',
        editable: true,
      },
      {
        title: '问责完成时间',
        dataIndex: 'wzCompleteTime',
        align: 'center',
        editable: true,
        render: (text) => <span>{text && moment(text).format('YYYY-MM-DD')}</span>
      },
      {
        title: '问责文号',
        dataIndex: 'wzNumber',
        align: 'center',
        editable: true,
      },
      {
        title: '问责主处分',
        dataIndex: 'wzPunish',
        align: 'center',
        editable: true,
      },
      {
        title: '问责附加处分',
        dataIndex: 'wzExtras',
        align: 'center',
        editable: true,
      },
      {
        title: '经济处分(万元)',
        dataIndex: 'wzFine',
        align: 'center',
        editable: true,
        render: (text) => <span>{text}</span>
      },
    ]
    this.columnsDisable = [
      {
        title: '序号',
        key: 'number',
        dataIndex: 'number',
        align: 'center',
        render: (text, record, index) => {
          <span key={index}>{index + 1}</span>
        }
      },
      {
        title: '问责人员姓名',
        dataIndex: 'wzUserName',
        align: 'center',
      },
      {
        title: '问责完成时间',
        dataIndex: 'wzCompleteTime',
        align: 'center',
        render: (text) => <span>{text && moment(text).format('YYYY-MM-DD')}</span>
      },
      {
        title: '问责文号',
        dataIndex: 'wzNumber',
        align: 'center',
      },
      {
        title: '问责主处分',
        dataIndex: 'wzPunish',
        align: 'center',
      },
      {
        title: '问责附加处分',
        dataIndex: 'wzExtras',
        align: 'center',
      },
      {
        title: '经济处分(万元)',
        dataIndex: 'wzFine',
        align: 'center',
        render: (text) => <span>{text}</span>
      },
    ]
  }

  componentWillMount() {
    this.columns.push({
      title: '操作',
      dataIndex: 'operation',
      width: 150,
      align: 'center',
      render: (text, record, index) => {
        return <span>
          <EditableContext.Consumer>
            {form => (
              <a style={{ color: 'red', marginLeft: '15px' }} onClick={() => this.deleteFun(index, record)}>删除</a>
            )}
          </EditableContext.Consumer>
        </span>
      }
    })
  }

  // 删除
  deleteFun = (index, record) => {
    axios.post('', {
      params: {
        id: record.id,
      },
      userId: localStorage.getItem('userId'),
      sysId: localStorage.getItem('sysId')
    }).then(res => {
      if (res.data && res.data.code !== 'SUP001') {
        Toast.error(res.data.message)
      }
      const valueList = this.props.value
      this.props.onChange(valueList.filter(i => i.id !== record.id))
    })
  }

  // 新增加一行
  addTableFun = () => {
    const { feedBackId } = this.props
    axios.post('', {
      params: {
        feedBackId,
      },
      userId: localStorage.getItem('userId'),
      sysId: localStorage.getItem('sysId')
    }).then(res => {
      if (res.data && res.data.code !== 'SUP001') {
        Toast.error(res.data.message)
      }
      const { resultData } = res.data
      this.props.onChange([...this.props.value, resultData])
    })
  }

  render() {
    const { feedBackId, dictionaryList } = this.props
    const components = {
      body: {
        cell: EditableCell,
      }
    }

    const columns = this.columns.map(col => {
      if (!col.editable) {
        return col
      }
      return {
        ...col,
        onCell: (record, index) => {
          return {
            record,
            index,
            inputType: () => {
              let type = ''
              switch (col.dataIndex) {
                case 'wzUserName':
                  type = 'TextArea';
                  break;
                case 'wzCompleteTime':
                  type = 'DatePicker';
                  break;
                case 'wzNumber':
                  type = 'TextArea';
                  break;
                case 'wzPunish':
                  type = 'Select';
                  break;
                case 'wzExtras':
                  type = 'InputNumber';
                  break;
                default:
                  break;
              }
              return type
            },
            dataIndex: col.dataIndex,
            title: col.title,
            editing: true,
            value: this.props.value,
            onValChange: this.props.onValChange,
            feedBackId,
            isEditTable: this.props.isEditTable,
            dictionaryList,
          }
        }
      }
    })

    return (
      <div className={styles.supFeedbackWzStyle}>
        <EditableContext.Provider value={this.props.form}>
          <Table
            components={components}
            bordered
            dataSource={this.props.value} // 外面的父页面传过来的
            columns={this.props.isEditTable ? columns : this.columnsDisable} // 表格可编辑、表格纯展示
            rowClassName='editable-row'
            locale={{ emptyText: '暂无数据' }}
            pagination={false}
            scroll={{ x: '4000px' }} // 实现横向滚动
            footer={() => this.props.isEditTable ? <a onClick={() => { this.addTableFun() }}><Icon style={{ fontSize: '16px', color: '#08c' }} type='plus' /></a> : null}
          />
        </EditableContext.Provider>
      </div>
    )
  }
}

const EditableFormTable = Form.create()(EditableTable)


// 导出完整的可编辑输入框表格组件
export default class AddInputTable extends React.Component {
  constructor(props) {
    super(props)
    this.state = {
      dictionaryList: [], // 问责主处分选择框数据
    }
  }

  componentDidMount() {
    setTimeout(() => {
      this.searchWZ()
      this.queryDictionary()
    }, 1000)
  }

  // 查询问责列表
  searchWZ = () => {
    axios.post('', {
      params: {
        feedBackId: this.props.feedBackId ? this.props.feedBackId : ''
      },
      userId: localStorage.getItem('userId'),
      sysId: localStorage.getItem('sysId')
    }).then(res => {
      if (res.data && res.data.code !== 'SUP001') {
        Toast.error(res.data.message)
      }
      const { resultData } = res.data
      this.props.onChange(resultData)
    })
  }

  // 选择框枚举
  queryDictionary = () => {
    axios.post('', {
      params: {
        cataCode: 'wzPunish'
      },
      userId: localStorage.getItem('userId'),
      sysId: localStorage.getItem('sysId')
    }).then(res => {
      if (res.data && res.data.code !== 'SUP001') {
        Toast.error(res.data.message)
      }
      const { resultData } = res.data
      this.setState({
        dictionaryList: resultData,
      })
    })
  }

  render() {
    // 父页面使用了Form, 这里的value,onChange是父页面身上的,做到子页面更新,父页面更新
    const { value, onChange, isEditTable, feedBackId } = this.props;
    const { dictionaryList } = this.state;
    const propsObj = {
      feedBackId,
      value,
      isEditTable,
      dictionaryList,
      // onChange, 为什么不直接把onChange传过去,还要下称下面的形式把onChange传过去。因为InputNumber组件点击上下箭头,数字能正常显示,手动输入,就会报错(InputNumber组件的原生onChange,和这里传的onChange有冲突了)
      onValChange: (val) => { // 这里把要传的onChange换个名字为onValChange传过去,就不会和Input身上带的原生的onChange冲突了
        onChange(val)
      }
    }
    return (
      <div>
        <EditableFormTable {...propsObj} />
      </div>
    )
  }
}


3、代码解释

    // 解释1:为什么e.stopPropagation() 阻止冒泡。图片说明

图1-没写e.stopPropagation()的报错

图2-没写e.stopPropagation()报错的定位

图3-写e.stopPropagation()