最近有一个分单的需求,需要把一个二维数组通过表单提交传参给后台。

主要数据格式(数组[对象{对象,数组},对象{对象,数组}])示例 this.ruleList:

[{
            info: {
                name: '1',
                paymentTerm: '',
                stockPosition: '',
                purchaseGroup: '',
            },
            materialList: [{
                    a: '',
                    b: '',
                    deliveryDate: '2022-3-20'
                },
                {
                    a: '',
                    b: '',
                    deliveryDate: '2022-3-20'
                }
            ]
        },
        {
            info: {
                name: '2',
                paymentTerm: '',
                stockPosition: '',
                purchaseGroup: '',
            },
            materialList: [{
                a: '',
                b: '',
                deliveryDate: '2022-3-20'
            }]
        }
    ]

数据处理部分代码示例:

this.ruleDataList.forEach(item=>{
                    this.$set(item.info,'materialList',item.materialList);
                    item.materialList.forEach(ele=>{
                        this.$set(ele,'defaultDate',ele.deliveryDate);
                        this.$set(ele,'rule',[
                            {
                                required: true,
                                validator:(rule, value, callback)=>{
                                    this.deliveryDate(rule,callback,ele.defaultDate,ele.deliveryDate)
                                },
                                trigger: ['blur','change']
                            }
                        ])
                    })
                    this.validateList.push(item.info)
                })

首先将this.ruleDataList 里的对象和数组都合并到info里,materialList数组里新增了两个属性值rule和defaultDate并push到一个新数组validateList里,这样我们就得到this.validateList的数据格式为:

{
            name: '1',
            paymentTerm: '',
            stockPosition: '',
            purchaseGroup: '',
            materialList: [{
                    a: '',
                    b: '',
                    deliveryDate: '2022-3-20',
                    rule: '表格项校验规则'
                },
                {
                    a: '',
                    b: '',
                    deliveryDate: '2022-3-20',
                    rule: '表格项校验规则'
                }
            ]
        },
        {
            name: '2',
            paymentTerm: '',
            stockPosition: '',
            purchaseGroup: '',
        materialList: [{
            a: '',
            b: '',
            deliveryDate: '2022-3-20',
            rule: '表格项校验规则'
        }]
    }
]

这样我们渲染页面时就可以直接用处理后的数组,更方便表单的校验。页面通过v-for来循环validateList,获得多个表单,所以表单的ref拼接了索引,页面代码示例如下:

<div class="separateContent">
                <el-row class="soDetailRow" v-for="(item,index) in validateList" :key="index">
                    <div class="subTitle">
                        <span>{{item.supplierName}}</span>
                    </div>
                    <el-form :model="item" :ref="'ruleForm'+index" label-width="100px">
                        <el-form-item label="付款条件:" prop="paymentTerm"
                            :rules="[{ required: true, message: '付款条件不能为空'}]"
                            class="formRow">
                            <el-select placeholder="请选择"
                                filterable
                                style="width:200px;margin-right:10px"
                                v-model="item.paymentTerm">
                                <el-option v-for="ops in paymentClauseList"
                                    :key="ops.value"
                                    :label="ops.label"
                                    :value="ops.value">
                                </el-option>
                            </el-select>
                        </el-form-item>
                   
                        <el-table 
                            border 
                            :row-class-name="tableRowClassName"
                            :header-cell-style="{background: '#f7f7f7',padding:'5px 10px'}"
                            ref="multipleTable"
                            :data="item.materialList">
                            <el-table-column align="center" prop="orderUnitDesc" label="单位" ></el-table-column>
                            <el-table-column align="center" prop="deliveryDate" label="交货期" min-width="130px">
                                <template slot-scope="scope">
                                    <el-form-item 
                                        :prop="'materialList.'+scope.$index+'.deliveryDate'"
                                        :rules="scope.row.rule"
                                        class="costValue">
                                        <el-date-picker
                                            :clearable="false"
                                            style="width:130px"
                                            @change="handleChangeDate(scope.row,`materialList.${scope.$index}.deliveryDate`,`${index}`,)"
                                            v-model="scope.row.deliveryDate"
                                            type="date"
                                            value-format="yyyy-MM-dd"
                                            :placeholder="`选择日期${index}`">
                                            </el-date-picker>
                                    </el-form-item>
                                </template>
                            </el-table-column>
                        </el-table>
                    </el-form>
                </el-row>
            </div>
            <el-row class="crumbs btns printBtns" type="flex" justify="center">
                <el-button  class="el-btn" @click="hanldeCheckForm('1')"  v-preventReClick>存为待推送</el-button>
                <el-button type="primary"  class="el-btn" @click="hanldeCheckForm('2')"  v-preventReClick>确认分单</el-button>
            </el-row>

由于表单是多个,表单里嵌套的表格也是多个,根据prop定义的动态表单校验会有重复值,所以在处理数组时,set了每一项的自定义校验规则(每项的交货期日期组件选择都不能小于默认日期),规则代码如下:

// 自定义日期校验
        deliveryDate(rule, callback,defaultDate,deliveryDate){
            let index = Number(rule.field.split('.')[1])//获取当前验证项的index,对应数据的index
            if(!deliveryDate){
                callback(new Error('交货日期不能为空'))
            }else if(defaultDate > deliveryDate){
                callback(new Error('只能选择'+ defaultDate +'之后的日期'))
            }else{
                callback()
            }
        },

表单提交时,由于是多个表单,只有所有的表单校验通过之后再去请求接口,这里用到的是Promise.all的方法,代码示例如下:

// 表单校验
        hanldeCheckForm(type){
            let _self = this;
            let newArr = [];
            this.validateList.forEach((item,index)=>{
                let  result = new Promise(function(resolve, reject) {
                    _self.$refs['ruleForm'+index][0].validate(valid=>{
                            if (valid) {
                                resolve();
                            } else { reject() }
                        })
                    })
                newArr.push(result);
            })
            
            Promise.all(newArr).then(function() { //都通过了
                let paramsList = [];
                _self.validateList.forEach(item=>{
                    paramsList.push({info:item,materialList:item.materialList})
                })
                if(type == '1'){
                    _self.handlePushClick(paramsList)
                }else{
                    _self.handleConfirmClick(paramsList)
                }   
            }).catch(function(err) {
                console.log(err);
            })
        },
        // 待推送
        handlePushClick(params){
            saveWaitPush(params).then(res=>{
                if(res.code == '200'){
                    this.$message({
                        message: '操作成功',
                        type: 'success'
                    });
                    this.$emit('handleClose',false,true)
                }
            })
        },
        // 待确认
        handleConfirmClick(params){
            confirmSplit(params).then(res=>{
                if(res.code == '200'){
                    this.$message({
                        message: '操作成功',
                        type: 'success'
                    });
                    this.$emit('handleClose',false,true)
                }
            })
        },

由于后台要求入参格式跟未处理之前一致,所以在接口请求之前又重新将对象和数据剥离成 paramsList.push({info:item,materialList:item.materialList})。这里没有专门对info里的materialList做删除处理,可以自行删除。

解决需求痛点:

1.循环(多)表单动态校验。

2.自定义校验传参validator入参无法接收rule, value, callback 之外的参数,导致表格项校验时,无法直接通过索引值获取数组里的某一项做校验,曲线救国通过set以及在validator 通过自定义方法专门对入参做处理。