uview + uniapp 小程序 表单 v-for 动态验证解决方案

809 阅读2分钟

参考文档 blog.csdn.net/csdn_zuiren…

场景 表单中需要循环显示车牌列表 同时对不同的车牌进行校验

<u--form
:rules="rules"
labelWidth="145rpx"
:model="model"
ref="form"
>
    <view v-for="(item,index) in model.carNumberList" :key="index">
        <u-form-item
        label="车牌号"
        :prop="`carNumberList.${index}.car_number`"
        borderBottom
        labelWidth="120rpx"
        >
            <view slot="right" style="display: flex;">
                <view style="width: 465rpx;">
                    <car-number-input v-model="item.car_number"  @numberInputResult="numberInputResult($event,index)" :defaultStr="item.car_number"></car-number-input>
                </view>
                <text style="width: 32rpx;"></text>
            </view> 
        </u-form-item>
​
        <u-form-item
        label="进出口凭证(卡号/ETC/扫码)"
        borderBottom
        labelWidth="220rpx"
        >
            <view slot="right" style="display: flex;">
                <u--input
                v-model="item.etc_no"
                placeholder="请输入进出口凭证"
                border="none"
                ></u--input>
                <!-- <span style="color: #ccc;">个</span> -->
                <text style="width: 32rpx;"></text>
            </view>
        </u-form-item>
​
        <u-form-item
        label="备注"
        borderBottom
        >
            <u--textarea
            placeholder="请输入备注"
            v-model="item.remark"
            border="none"
            count
            ></u--textarea>
        </u-form-item>
    </view>
</u--form>

解决方案

  1. 绑定的校验规则rules和表单model下面放置一个同名数组,确保u-form能找到

    如下为 model 和 rules 中的内容 guigerules为后续往rules.carNumberList中新增的规则

model: {
    carNumberList:[
        {
            car_number: '浙',
            etc_no:'',
            remark:'',
        },
    ],
},
    
rules: {
    'carNumberList': [{
        car_number: {
            type: 'string',
                validator: (rule, value, callback) => {
                return uni.$u.test.carNo(value.replace(/\s*/g,""))
            },
            message: '请规范填写车牌号',
            required: true,
            trigger: ['blur', 'change']
        },
    }],
},
​
guigerules: {
    car_number: {
        type: 'string',
        validator: (rule, value, callback) => {
            return uni.$u.test.carNo(value.replace(/\s*/g,""))
        },
        message: '请规范填写车牌号',
        required: true,
        trigger: ['blur', 'change']
    },
},
  1. u-form-item中的表单必须改为 :prop="carNumberList.${index}.car_number"
 <u-form-item
 label="车牌号"
 :prop="`carNumberList.${index}.car_number`"
 >
    ......
 </u-form-item>
  1. 修改uview下的u-form(node_modules/uview-ui/components/u-form)的源码

    修改后的validateField函数:

// 对部分表单字段进行校验
async validateField(value, callback, event = null) {
    // $nextTick是必须的,否则model的变更,可能会延后于此方法的执行
    this.$nextTick(() => {
        // 校验错误信息,返回给回调方法,用于存放所有form-item的错误信息
        const errorsRes = [];
        // 如果为字符串,转为数组
        value = [].concat(value);
        // 历遍children所有子form-item
        this.children.map((child) => {
            // 用于存放form-item的错误信息
            const childErrors = [];
            if (value.includes(child.prop)) {
                // 获取对应的属性,通过类似'a.b.c'的形式
                const propertyVal = uni.$u.getProperty(
                    this.model,
                    child.prop
                );
                // 属性链数组
                const propertyChain = child.prop.split(".");
                const propertyName =
                    propertyChain[propertyChain.length - 1];
                //修改 将const 改为let -----------------------
                // const rule = this.formRules[child.prop];
                let rule = this.formRules[child.prop];      
                // 修改: 链式是无法通过上面的方式获取的,改为下面的方式------------
                if(!rule){
                    rule=uni.$u.getProperty(
                    this.formRules,
                    child.prop
                    );
                }
                // 如果不存在对应的规则,直接返回,否则校验器会报错
                if (!rule) return;
                // rule规则可为数组形式,也可为对象形式,此处拼接成为数组
                const rules = [].concat(rule);
​
                // 对rules数组进行校验
                for (let i = 0; i < rules.length; i++) {
                    const ruleItem = rules[i];
                    // 将u-form-item的触发器转为数组形式
                    const trigger = [].concat(ruleItem?.trigger);
                    // 如果是有传入触发事件,但是此form-item却没有配置此触发器的话,不执行校验操作
                    if (event && !trigger.includes(event)) continue;
                    // 实例化校验对象,传入构造规则
                    const validator = new Schema({
                        [propertyName]: ruleItem,
                    });
                    validator.validate({
                            [propertyName]: propertyVal,
                        },
                        (errors, fields) => {
                            if (uni.$u.test.array(errors)) {
                                errorsRes.push(...errors);
                                childErrors.push(...errors);
                            }
                            child.message =
                                childErrors[0]?.message ?? null;
                        }
                    );
                }
            }
        });
        // 执行回调函数
        typeof callback === "function" && callback(errorsRes);
    });
},
​

同时还要修改watch中对rules的监听 如果不关也不影响 就忽视这一步

    watch: {
        // 监听规则的变化
        rules: {
        immediate: true,
        handler(n) {
            // 此处监听会导致validator失效
            // 小程序建议手动绑定this.$refs.form.setRules(this.rules)
            // this.setRules(n);
        },
    },
}
  1. 动态添加 添加字段
// 添加车牌
addCarNumber(){
    this.model.carNumberList.push({
        car_number: '浙',
        etc_no:'',
        remark:'',
    })
    this.rules.carNumberList.push(this.guigerules);
    // 如果需要兼容微信小程序,并且校验规则中含有方法等,只能通过setRules方法设置规则
    //微信小程序:动态设置才能生效,每次添加都需要,动态绑定无效
    this.$refs.form.setRules(this.rules)
},
​
  1. 然后就可以每个对象里的车牌单独校验了