12月04, 2019

React+Antd 自定义表单问题-1

最近在用React的UI框架Antd做试剂SAAS系统,由于是后台系统,所以有很多的表单数据需要提交.在使用Antd的表单过程中,发现会存在许多的问题.该博客会对我曾经遇到的问题做一个总结.由于问题较多.所以会分为3篇左右进行.

这是该总结的第一篇文章,主要介绍下,什么是什么是自定义表单,下面会以新增客户基本信息表单为例来进行说明.

新增客户表单

其中,客户账期,还有公司地址,抽离成了共用组件.然后整个客户基本信息是一个表单组件.上面的保存,保存并添加资质信息都是基础布局组件里的内容.提交按钮和表单内容是在不同组件之中的.

拆分组件为细粒度的表单有什么好处

  1. CustomerForm组件可以复用,只需要引入即可,而不是复制黏贴代码.如果有改动,只需改动组件即可
  2. 代码整洁,只负责表单相关的事情.收集用户信息数据.
  3. 语义、职责更加清晰,新增客户基本信息由一个「表单」和三个「按钮」组成,表单负责提供数据,页面组件不再像之前一样掺杂了许多无关的代码。

该表单包含表单的大部分控件,如Input,Select,TextArea,还有一些关联组件.其实Antd里面表单相关的东西还是挺多的.

下面是提交打印的信息截图:

提交信息

{
  "companyMobileCode": "86",
  "epChName": "khtest1204",
  "companyTelArea": "021",
  "companyTel": "2342424",
  "companyTelEx": "333",
  "companyType": "PT_2",
  "custPay": {
    "custPayTypeArr": [
      {
        "id": "",
        "dicCode": "PT_1",
        "dicValue": "货到付款",
        "status": "0"
      },
      {
        "id": "",
        "dicCode": "PT_2",
        "dicValue": "款到发货",
        "status": "0"
      },
      {
        "id": "",
        "dicCode": "PT_3",
        "dicValue": "票到付款",
        "status": "0"
      }
    ],
    "custPayType": "PT_1",
    "custPayDate": "33",
    "cycleUnit": ""
  },
  "companyWebsite": "http://www.901web.com",
  "companyAddress": {
    "officeCountry": 100000,
    "officeCountryName": "中国",
    "officeProvince": 210000,
    "officeProvinceName": "辽宁省",
    "officeCity": 210500,
    "officeCityName": "本溪市",
    "officeArea": 210505,
    "officeAreaName": "南芬区"
  },
  "officeAddress": "上南路1121号",
  "salesId": "651414743202009088",
  "salesName": "pybtest",
  "companyMobile": "13456788900",
  "custLevel": "PT_2",
  "custSource": "PT_2",
  "status": "0",
  "companyIntro": "测试信息"
}

请求示例

{
    "epChName": "",
    "companyType": "",
    "officeCountry": 0,
    "officeCountryName": "",
    "officeProvince": 0,
    "officeProvinceName": "",
    "officeCity": 0,
    "officeCityName": "",
    "officeArea": 0,
    "officeAreaName": "",
    "officeAddress": "",
    "custLevel": "",
    "custSource": "",
    "companyTelArea": "",
    "companyTel": "",
    "companyTelEx": "",
    "custPayType": "",
    "custPayDate": 0,
    "companyMobileCode": "",
    "companyMobile": "",
    "salesId": "",
    "salesName": "",
    "companyWebsite": "",
    "companyIntro": "",
    "status": ""
}

然后需要删除掉对应的多余的字段信息:

    delete postData.companyAddress;
    delete postData.custPay;

打印信息

具体的表单数据提交,先拿到提交的数据进行解构,然后在组装,删除无用数据.提交后台进行新增.

下面要说明一点的是,表单组件是子组件,提交事件在外面的父组件中.如何进行表单校验呢~ 这里有两种方法都可以实现.下面分别对应的贴上代码:

方法1:

  //页面模板
    <div className={styles.addCustomerForm}>
            <CustomerForm {...this.props} />
    </div>
    //提交方法
    this.props.form.validateFields((err, values) => {
            let {
                officeCountry,
                officeCountryName,
                officeProvince,
                officeProvinceName,
                officeCity,
                officeCityName,
                officeArea,
                officeAreaName
            } = values.companyAddress;
            let { custPayDate, custPayType } = values.custPay || {};
            if (!err) {
            let postData = {
                ...values,
                custPayType,
                custPayDate,
                officeCountry,
                officeCountryName,
                officeProvince,
                officeProvinceName,
                officeCity,
                officeCityName,
                officeArea,
                officeAreaName
            };
            delete postData.companyAddress;
            delete postData.custPay;

            console.log(postData)

方法2:

  //页面模板
    <div className={styles.addCustomerForm}>
            <CustomerForm wrappedComponentRef={(basicInfo) => this.basicInfoForm = basicInfo} />
    </div>
//提交方法
this.basicInfoForm.props.form.validateFieldsAndScroll((err, values) => {
    if (!err) {
        console.log(values);
        const body = JSON.stringify(values, null, 2);
        console.log(body);
    }
})
return;

这两种方法都可以达到提交表单数据.父组件通过Connect去链接(Form.create()(addCustomer));创建高阶组件.

另外一点卡主自己,像固定电话,客户账期,地址选择这种由多个字段组成的数据情况进行提交,校验需要自己写相应的校验方法.还记得有个赋初始值的问题.就是地址下拉选择的问题,由于是接口返回的数据,接口是不同的后端提供,有个同事返回的id是字符串的格式,导致渲染Select组件一直显示的是字符串数字.困扰我好半天的~(最后同事帮忙看了下指正出来啦!!!)

另外说明一点,之后又对详细地址字段做了分离,单独的去获取. 我们在使用setFieldsValue方法的时候,要先用getFieldDecorator对表单字段进行注册才可以,否则控制台会报错.

例如我们在编辑和新增客户基本信息的时候,有可能就是调用同一个后端接口的,如果是新增操作的话,就不用传Id,如果是编辑操作,就需要传入Id字段.这时候我们就可以给一个隐藏域的字段值,进行判断一下. 例如如下:

 {customerbasicInfo && customerbasicInfo.id && (
 <FormItem label="客户ID" style={{ display: 'none' }}>
    {getFieldDecorator('id', {
        initialValue: customerbasicInfo && customerbasicInfo.id
    })(<Input placeholder="客户Id值" type="hidden"/>)}
 </FormItem>
)}

选择销售负责人的时候,需要同时传入负责人Id值和对应的name值:

    //采购负责人选择事件
    handleChangeSeller = (value, option) => {
        let { title } = { ...option.props };
        const { setFieldsValue } = this.props.form;
        this.setState(
            {
                salesName: title
            },
            () => {
                let { salesName } = this.state;
                setFieldsValue({
                    salesName
                });
            }
        );
    };
 <FormItem label="" style={{ display: 'none' }}>
    {getFieldDecorator('salesName', {
    initialValue: (customerbasicInfo && customerbasicInfo.salesName) || ''})(<Input type="hidden" />)}
</FormItem>

后面业务验收,又对客户账期进行了修改调整.

客户账期

当选择 货到付款或者票到付款的时候,后面对应的显示 货到 ** 天内付款,票到 ** 天内付款,选择款到发货时,后面的不需要显示.

于是对对应的Select事件的Onchange方法做判断:

客户账期1

客户账期2

其实交互效果很常见,也并不难.但是自己对React不太熟悉,导致自己遇到问题不知如何处理.在开发过程中遇到了不少的问题,还是要仔细认真的看看官方文档和API说明,不清楚的地方自己多尝试.

下期预告

接下来会对「客户基本信息表单」做详细的讲解,主要涉及到:

  • 是否需要作为自定义表单组件
  • 自定义表单的写法
  • 自定义表单取值的问题
  • 自定义表单校验的问题

本文链接:https://901web.com/post/Antd自定义表单问题1.html

-- EOF --

Comments

请在后台配置评论类型和相关的值。