01月16, 2020

React+Antd 表单控件组合组件之校验相关

这篇文章主要整理了我在工作中,使用Form表单组件,进行数据校验的一些问题. 其中印象比较深刻的就是一个固定电话和客户账期的组合组件.(固定电话是由三个文本框组成,分别为区号,固号,以及分机号码.客户账期是由一个下拉选择框和文本框组合),开发过程中遇到的相关表单自定义校验问题和错误提示信息:

下图是页面展示效果:

页面效果

之后项目上线,对之前的代码进行了优化修改.优化内容如下:

  • 固定电话是否必填进行了可控制的校验,并且只显示一个提示信息.
  • 客户账期下拉文本框组件,进行了修改.onChange的时候,清空了文本框框的值.

具体的展示效果是:

固定电话格式:区号和固话号码必填,(分机号非必填) 校验规则如下->

如果区号和固号都没填写,则提示:固号号码需必填 如果只填写了区号,或者只填写了固号,提交则提示:请输入完整的固话

只有当区号和固号都填写了,则验证通过!

页面效果

验证通过的情况:

验证通过

之前的应急处理方式是,Form.Item 做的嵌套,对每个字段进行单独的校验.(一个外层的Form.Item里面嵌套三个FormItem)

固定电话抽离做了个一个共用组件,客户基本信息是一个表单组件:

父组件:

 <FormItem label="固定电话" required style={{ margin: 0 }}>
   {getFieldDecorator("fixphoneNo", {
            initialValue: {
            areaNo: customerbasicInfo && customerbasicInfo.id ? customerbasicInfo.companyTelArea :'',
            phoneNo:customerbasicInfo && customerbasicInfo.id ? customerbasicInfo.companyTel :'',
            exNo: customerbasicInfo && customerbasicInfo.id ? customerbasicInfo.companyTelEx :'',
        },
        })(
        <FixedphoneInput
            form={form}
            areafieldKey="companyTelArea"
            phonefieldKey="companyTel"
            exfiledKey="companyTelEx"
        />
        )}
</FormItem>

固话文本框子组件:

import React from 'react';
import { Form, Input, Row, Col } from 'antd';
//固定电话文本框组合组件
class FixedPhoneInput extends React.Component {
    static getDerivedStateFromProps(props) {
        if ('value' in props) {
            return {
                ...(props.value || {}),
               };
        }
        return null;
    }

    constructor(props) {
        super(props);
        const value = props.value || {};
        this.state = {
            areaNo: value.areaNo || '', //区号
            phoneNo: value.phoneNo || '', //固话号码
            exNo: value.exNo || ''//分机号
        };
    }

    checkFixedphone = (rule, value, callback) => {
        const { form, areafieldKey, phonefieldKey, } = this.props;
        const { setFields } = form;
        const companyTelArea = form.getFieldValue('companyTelArea');
        const companyTel = form.getFieldValue('companyTel');

        if (!companyTelArea && !companyTel) {
            callback('固话号码需必填');
            setFields({
                [phonefieldKey]: { value:companyTel,errors: null },
            });
        } else if (companyTelArea && !companyTel) {
            callback('请输入完整的固话');
            setFields({
                [phonefieldKey]: {value:companyTel, errors: '' },
            });
        } else if (!companyTelArea && companyTel) {
            callback('请输入完整的固话');
            setFields({
                [phonefieldKey]: { value: companyTel, errors: null },
            });
        }
        else if (companyTelArea && companyTel) {
            setFields({
                [areafieldKey]: { value: companyTelArea, errors: null },
                [phonefieldKey]: { value: companyTel, errors: null },
            });
            callback();

        }
        callback();
    }


    render() {
        const { areaNo, phoneNo, exNo } = this.state;
        const { areafieldKey, phonefieldKey, exfiledKey, form: { getFieldDecorator } } = this.props;
        return (
            <Row gutter={10}>

                <Col span={7}>
                    <Form.Item>
                        {getFieldDecorator(areafieldKey, {
                            initialValue: areaNo || undefined,
                            rules: [{ validator: this.checkFixedphone }],
                            validateTrigger: 'onBlur'
                        })(
                            <Input placeholder="请输入区号" />
                        )}
                    </Form.Item>
                </Col>

                <Col span={10}>
                    <Form.Item>
                        {(getFieldDecorator(phonefieldKey, {
                            initialValue: phoneNo || undefined,
                            rules: [{ validator: this.checkFixedphone }],
                            validateTrigger: 'onBlur',
                            getValueFromEvent: (event) => {
                                return event.target.value.replace(/\D/g, '')
                            },
                        })(
                            <Input placeholder="请输入固定电话号码" />
                        ))}
                    </Form.Item>
                </Col>


                <Col span={7}>
                    <Form.Item>
                        {getFieldDecorator(exfiledKey, {
                            initialValue: exNo || undefined
                        })(
                            <Input placeholder="请输入分机号码" />
                        )}
                    </Form.Item>
                </Col>
            </Row>
        );
    }
}
export default FixedPhoneInput;

可以通过setFields方法手动设置某个字段的报错信息为null

 setFields({
      [phonefieldKey]: { value:companyTel,errors: null },
   });

另外一个就是客户账期的,当下拉选择货到付款或者票到付款的时候,后面的文本框展示.下拉选择款到发货选项时候,影藏后面的文本框.

优化之后的账期组件: 只对Select下拉做onChange事件监听,文本框的值通过表单获取,父组件中引用传入form即可:

import React from 'react';
import { Input, Row, Col } from 'antd';
import BaseDictionary from 'components/BaseDictionary';
//客户账期下拉框,文本框组件
class AccountInput extends React.Component {
static getDerivedStateFromProps(props) {
        if ('value' in props) {
            return {
                ...(props.value || {}),
                addonBeforeText: props.value.custPayType == 'PT_1' ? '货到' : '票到',
            };
        }
        return null;
    }
 constructor(props) {
        super(props);
        const value = props.value || {};
        this.state = {
            custPayType: value.selectfieldKey || '', //账期方式列
            custPayDate: value.inputfieldKey || '', //账期天数
            addonBeforeText: ''
        };
    }

    handleChangeVal = (type, value) => {
        const {  inputfieldKey } = this.props;
        this.setState({ [type]: value }, () => {
            //回调函数里面设置表单字段的值
            value && value !== 'PT_2' && this.props.form.setFieldsValue({
                [inputfieldKey]: `${value && value !== 'PT_2' ? '' : value}`,
            });
        });

        this.triggerChange({
            [type]: value,
            addonBeforeText: value == 'PT_1' ? '货到' : '票到',
        });

    }

    triggerChange = changedValue => {
        const { onChange } = this.props;
        console.log('changedValue', changedValue);
        let accoutInfo = {
            ...this.state,
            ...changedValue
        }
        if (onChange) {

            if (accoutInfo.custPayType === "PT_2") {
                //选择款到发货,没有文本框的时候,返回对象没有custPayDate字段
                onChange({
                    custPayType: accoutInfo.custPayType,
                    addonBeforeText: accoutInfo.addonBeforeText
                })
            } else if (accoutInfo.custPayType === undefined) {
                //非必填项,清除操作,对应字段传入空值.
                onChange({
                    custPayType: '',
                    addonBeforeText: ''
                })
            } else {
                onChange({
                    custPayType: accoutInfo.custPayType,
                    custPayDate: accoutInfo.custPayDate,
                    addonBeforeText: accoutInfo.addonBeforeText
                })
            }

        }
    };
    render(){
        ...
        组件代码
        ...
    }
}

render()函数代码:

页面模板

每次切换账期方式的时候,后面文本框的值也进行了清空处理.(当然也可以进行必填的校验)

有时候,我们在页面一加载的时候,就要进行拿到异步的数据进行判断,然后进行设置改变setState操作. 这时候就需要我们在组件的componentDidMount生命周期方法里面去执行异步方法.然后在.then方法里面拿到异步返回的结果做判断: (因为我们无法知道何时异步操作请求完成)

例如判断客户账期,编辑操作的时候,(如果选中的是 货到付款 或者 票到付款 后面的天数必填),并且要添加必填标识:

新增(编辑)页面-->CustomerForm 组件->固话号码(客户账期)组件:

componentDidMount() {
        const { customerId } = this.props;
        customerId && this.getCustomerInfo(customerId);
    }

getCustomerInfo = async (customerId) => {
        let { code, data, msg } = await getCustomerDetail({ id:customerId });
        if (code === "0") {
            this.setState({
                customerInfo: Object.assign({}, data)
            }, () => {
                this.isaccountRequired(customerId && this.state.customerInfo.custPayType !== "PT_2" ? true : false);
            })
        } else {
            message.error(msg)
        }

    }


    //客户账期是否需要必填
    isaccountRequired = (value) => {
        this.setState({
            accountRequire: value
        })
    }

效果如下图:

新增操作

编辑操作


切换账期

父组件引用客户账期:

 <FormItem label="客户账期">
    {getFieldDecorator('custPay', {
        initialValue:
                     customerbasicInfo && customerbasicInfo.id
                             ? {
                        custPayDate: customerbasicInfo.custPayDate || '',
                        custPayType: customerbasicInfo.custPayType || undefined
                 }: ''
       })(<CustomerAccountInput form={form} selectfieldKey="custPayType" inputfieldKey="custPayDate" />)}

</FormItem>

另外,对于动态表单值的提交,个人建议使用Antd封装好的方法来获取,不建议自己写onChange事件去监听,然后将当前的value值合并到每一条的对象里面.

{
        title: '本次发货数量',
        dataIndex: 'pickList',
        width: 120,
        render: (text, record, index) => (
            <TableSellgoodsItem
                form={form}
                shipmentCount={record.unDeliverCount}
                fieldKey={`pickList[${index}].deliverCount`}
                placeholder="请输入发货数量"
            />
        )
}

本文链接:https://901web.com/post/React+Antd+Validator相关.html

-- EOF --

Comments

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