• 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',
           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={{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={{  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={{  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)!

  • 相关阅读:
    mysql备份与binlog
    linux释放cached
    linux下mysql迁移到其他分区
    java分析jvm常用指令
    Mac下安装WebStrom
    Final
    Spring 复习
    ubuntu 14.4安装java环境
    php复习
    java 重难点
  • 原文地址:https://www.cnblogs.com/seemoon/p/13594502.html
Copyright © 2020-2023  润新知