目录
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()