react antd动态增减表单 react antd实现动态增减表单
月亮出来了 人气:0想了解react antd实现动态增减表单的相关内容吗,月亮出来了在本文为您仔细讲解react antd动态增减表单的相关知识和一些Code实例,欢迎阅读和指正,我们先划重点:react,antd动态增减表单,react,antd,动态表单,下面大家一起来学习吧。
之前写动态表单遇到过坑,就是用index下标做key会导致bug,而且很严重!
今天有空写下文章记录下:怎么处理和逻辑
我用的是antd3的版本,3和4的表单有点不一样,不过差别应该不大。
需求:
1、选择类型切换展示固定的模板
2、通过新增字段可以动态增减表单里面的每一行
3、控制每一行的字段是否需要必填
4、编辑时候回填参数
效果图:
部分关键代码:
import React, { Component } from 'react'; import styles from './index.less'; import { Table, Button, Select, Popconfirm, Modal, Form, Input, Radio, Row, Col, Tooltip, Icon, message, Pagination, InputNumber, } from 'antd'; const Option = Select.Option; const FormItem = Form.Item; let id = 0; @Form.create() class Index extends Component { marketId = 0; state = { selectType: '', orderType: 1, //文章1 地图2 typeLoading: false, isEdit: false, lookVisible: false, visible: false, pageSize: 10, pageNum: 1, keyWord: '', row: {}, typeList: {}, mock: {}, mapType: [{ 'fieldName': 'name', 'isImg': 0, 'order': 0, 'remarks': '名称', }, { 'fieldName': 'label', 'isImg': 0, 'order': 0, 'remarks': '标签', }, { 'fieldName': 'lon', 'isImg': 0, 'order': 0, 'remarks': '经度', }, { 'fieldName': 'lat', 'isImg': 0, 'order': 0, 'remarks': '纬度', }], articleType: [{ 'fieldName': 'name', 'isImg': 0, 'order': 0, 'remarks': '名称', }, { 'fieldName': 'label', 'isImg': 0, 'order': 0, 'remarks': '标签', }], }; /** * 将动表单态值生成需要的数据格式 * @param values * @returns {[]} */ createValues = (values) => { const { row } = this.state; const data = []; const newValues = { // 用新的对象承载提交的数据 ...values, }; const fieldNameData = []; // 保存fieldName值 const remarksData = []; // 保存remarks值 const isImgData = []; // 保存isImg值 const orderData = []; // 保存orderData值 const fieldName = RegExp(/fieldName/); const remarks = RegExp(/remarks/); const isImg = RegExp(/isImg/); for (const key in newValues) { if (fieldName.test(key)) { fieldNameData.push(newValues[key]); } } for (const key in newValues) { if (remarks.test(key)) { remarksData.push(newValues[key]); } } for (const key in newValues) { if (isImg.test(key)) { isImgData.push(newValues[key]); } } for (const key in newValues) { if (isImg.test(key)) { orderData.push(newValues[key]); } } fieldNameData.forEach((item, index) => { data.push({ fieldName: item, remarks: remarksData[index], isImg: isImgData[index], order: orderData[index], id: row.dataType ? row.dataType.id : '', }); }); return data; }; handleOk = e => { this.props.form.validateFields((err, values) => { if (!err) { const { row, isEdit } = this.state; const params = { dataType: { name: values.name, type: values.type, id: row.dataType ? row.dataType.id : '', }, typeFields: [], }; params.typeFields = this.createValues(values); if (isEdit) { editType(params).then(res => { if (res.code === 0) { message.info('修改成功'); this.setState({ visible: false, isEdit: false, }); this.fetchTypeList(); this.props.form.resetFields(); } }); } else { addType(params).then(res => { if (res.code === 0) { message.info('新增成功'); this.setState({ visible: false, isEdit: false, }); this.fetchTypeList(); this.props.form.resetFields(); } }); } } }); }; lookOrEditTypeModal = (flag, record) => { const { articleType, mapType } = this.state; if (flag === 'add') { //添加默认为文章模板 this.marketId = articleType.length + 1; //设置动态key标记长度 this.setState({ visible: true, row: { typeFields: articleType }, }); } else if (flag === 'edit') { this.setState({ visible: true, }); getType({ dataTypeId: record.id }).then(res => { if (res.code === 0) { this.marketId = res.data.typeFields.length + 1; //设置动态key标记长度 this.setState({ row: res.data, isEdit: flag === 'edit', }); } }); } else { this.setState({ lookVisible: true, }); getType({ dataTypeId: record.id }).then(res => { if (res.code === 0) { this.setState({ row: res.data, }); } }); } }; onChangeType = (value) => { const { form } = this.props; const { orderType, row, articleType, mapType } = this.state; this.props.form.resetFields(); const params = {}; if (value === 1) { //文章类型 params['typeFields'] = articleType; this.marketId = articleType.length + 1; } else { params['typeFields'] = mapType; this.marketId = mapType.length + 1; } this.setState({ row: params, orderType: value, }); }; //删除方法!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! removeFile = k => { const { form } = this.props; const keys = form.getFieldValue('keys'); if (keys.length === 1) { return; } form.setFieldsValue({ keys: keys.filter(key => key !== k), }); }; //添加方法!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! addFile = () => { const { form } = this.props; const keys = form.getFieldValue('keys'); const nextKeys = keys.concat(this.marketId++); form.setFieldsValue({ keys: nextKeys, }); }; judgeIsTemplet = (data) => { if (!data) { return false; } if ((data.fieldName === 'lat') || (data.fieldName === 'lon') || (data.fieldName === 'label') || (data.fieldName === 'name')) { return true; } }; handleValidator = (rule, val, callback) => { if (!val) { callback(); } let validateResult = /^[5A-Za-z0-9-\_]+$/.test(val); if (!validateResult) { callback('请输入正确表字段'); } callback(); }; columns = [ { title: '类型名称', dataIndex: 'name', key: 'name', width: 500, }, { title: '所属类型', dataIndex: 'type', key: 'type', render: (text) => { return text === 1 ? '文章' : '地图'; }, }, { title: '操作', dataIndex: 'address', key: 'address', render: (text, record) => { return <div> <Button type='link' onClick={() => this.lookOrEditTypeModal('look', record)}>查看</Button> <Button type='link' onClick={() => this.lookOrEditTypeModal('edit', record)}>编辑</Button> <Popconfirm title="确认删除?" onConfirm={() => this.deleteTypeClick(record)}> <Button type='link'>删除</Button> </Popconfirm> </div>; }, }, ]; render() { const { selectType, typeLoading, mock, row, isEdit, typeList, keyWord, lookVisible } = this.state; const { getFieldDecorator, getFieldValue } = this.props.form; let typeFields = row.typeFields || []; const initData = []; typeFields.forEach((item, index) => {//根据真实数据,设置默认keys数组 initData.push(index); }); getFieldDecorator('keys', { initialValue: initData }); //给表单增加keys字段,并设置默认值,这里编辑时候可以生成编辑回填的效果。 const keys = getFieldValue('keys'); const formItems = keys.map((k) => ( <Row gutter={12} key={k} className={styles.form_row}> <FormItem label="字段" key={`fieldName_${k}`}> {getFieldDecorator(`fieldName_${k}`, { initialValue: row.typeFields[k] ? row.typeFields[k].fieldName : '', validateTrigger: ['onChange', 'onBlur'], //校验子节点值的时机 rules: [{ required: true, message: '请输入英文字段!', }, { validator: this.handleValidator, }], })(<Input placeholder="请输入英文字段" max={30} disabled={this.judgeIsTemplet(row.typeFields[k])}/>)} </FormItem> <FormItem label="名称" key={`remarks_${k}`}> {getFieldDecorator(`remarks_${k}`, { initialValue: row.typeFields[k] ? row.typeFields[k].remarks : '', validateTrigger: ['onChange', 'onBlur'], rules: [{ required: true, message: '请输入中文名称!', }], })(<Input placeholder="请输入中文名称" disabled={this.judgeIsTemplet(row.typeFields[k])}/>)} </FormItem> <FormItem label="排序" key={`order_${k}`}> {getFieldDecorator(`order_${k}`, { initialValue: row.typeFields[k] ? row.typeFields[k].order : 0, })(<InputNumber style={{width:75}} placeholder="排序" />)} </FormItem> <FormItem label="图片" key={k}> {getFieldDecorator(`isImg_${k}`, { initialValue: row.typeFields[k] ? row.typeFields[k].isImg : 0, rules: [{ required: true, }], })(<Radio.Group disabled={this.judgeIsTemplet(row.typeFields[k])}> <Radio value={0}>否</Radio> <Radio value={1}>是</Radio> </Radio.Group>)} </FormItem> {!this.judgeIsTemplet(row.typeFields[k]) ? ( <Icon type="minus-circle" onClick={() => this.removeFile(k)} title='删除'/> ) : null} </Row> )); return ( <div className={styles.wrap_type}> <Modal title="类型管理" visible={this.state.visible} onOk={this.handleOk} onCancel={this.handleCancel} width={890} // className={styles.modal_type} maskClosable={false} > <Form layout='inline'> <Row style={{ textAlign: 'center', marginBottom: 14 }}> <FormItem label="选择类型"> {getFieldDecorator('type', { initialValue: row.dataType ? row.dataType.type : 1, rules: [{ required: true, }], })(<Select onChange={this.onChangeType} disabled={isEdit} style={{ width: 200 }}> <Option value={1}>文章类型</Option> <Option value={2}>地图类型</Option> <Option value={3} disabled={true}>文件类型</Option> </Select>)} </FormItem> <FormItem label="类型名称"> {getFieldDecorator('name', { initialValue: row.dataType ? row.dataType.name : '', rules: [{ required: true, message: '请输入类型名称!', }], })(<Input placeholder="请输入类型名称" style={{ width: 200 }}/>)} </FormItem> </Row> {formItems} <div style={{ margin: 'auto', textAlign: 'center' }}> <Button icon="plus" onClick={this.addFile} style={{ marginTop: 10 }}>新增字段</Button> </div> </Form> </Modal> </div> ); } } export default Index;
关键地方是设置一个marketID作为动态添加的key,然后用他的值作为动态key。(千万不要用数组的下标index来作为key)!
加载全部内容