vue2/3 table表格中嵌套form表单验证解决方案

384 阅读2分钟

1.需求是什么 我的需求是在父组件点击编辑按钮,打开对话框,对话框中有一个table表格嵌套form表单进行特定列的非空校验,列可能是select,input,timePIcker,input等等,这个子组件多处需要使用到,所以需要把这个子组件进行封装,父组件只需要传递columns,dataSouces给子组件即可,下面是代码,大家可以根据自己需求,自行改动

贴上代码

<template>
  <div id="app">
    <el-form
      :model="form"
      :rules="rules"
      ref="form"
    >
      <el-table
        :data="form.datas"
        highlight-current-row
        style="width: 100%"
      >
        <!-- 排序列 -->
        <el-table-column
          prop="id"
          label="序号"
          width="60"
        ></el-table-column>

        <el-table-column
          :prop="item.dataIndex"
          v-for="(item, index) in columns"
          :key="index"
          :label="item.lable"
        >
          <template slot-scope="scope">
            <template>
              <el-form-item
                :prop="'datas.' + scope.$index + '.' + item.dataIndex"
                :rules="rules[item.dataIndex]"
              >
                <el-input
                  v-if="item.type === 'input'"
                  size="mini"
                  v-model.trim="scope.row[item.dataIndex]"
                  style="width: 120px"
                ></el-input>
                <el-select
                  v-if="item.type === 'select'"
                  v-model="scope.row[item.dataIndex]"
                  placeholder="请选择"
                >
                  <el-option
                    v-for="item1 in item.option"
                    :key="item1.value"
                    :label="item1.label"
                    :value="item1.value"
                  ></el-option>
                </el-select>
              </el-form-item>
            </template>
          </template>
        </el-table-column>
          prop="options"
          label="晚餐"
        >
          <template slot-scope="scope">
            <template>
              <el-form-item
                :prop="'datas.' + scope.$index + '.options'"
                :rules="rules.options"
              >
                <el-select
                  v-model="scope.row.options"
                  placeholder="请选择"
                >
                  <el-option
                    v-for="item in options"
                    :key="item.value"
                    :label="item.label"
                    :value="item.value"
                  ></el-option>
                </el-select>
              </el-form-item>
            </template>
          </template>
        </el-table-column>
        <el-table-column
          prop="operation"
          label="操作"
        >
          <template slot-scope="scope">
            <template v-if="scope.row.action == 'view'">
              <el-button
                size="mini"
                @click="click_edit(scope.row, scope.$index)"
              >
                编辑
              </el-button>
              <el-button
                size="mini"
                @click="click_delete(scope.row, scope.$index)"
              >
                删除
              </el-button>
            </template>
            <template v-else-if="scope.row.action == 'add'">
              <el-button
                size="mini"
                @click="click_add(scope.row, scope.$index)"
              >
                新增
              </el-button>
              <el-button
                size="mini"
                @click="click_reset(scope.row, scope.$index)"
              >
                重置
              </el-button>
            </template>
            <template v-else>
              <el-button
                size="mini"
                @click="click_save(scope.row, scope.$index)"
              >
                保存
              </el-button>
              <el-button
                size="mini"
                @click="click_cancle(scope.row, scope.$index)"
              >
                取消
              </el-button>
            </template>
          </template>
        </el-table-column> -->
      </el-table>
    </el-form>
  </div>
</template>

<script>
const option = [
  {
    value: '选项1',
    label: '黄金糕'
  },
  {
    value: '选项2',
    label: '双皮奶'
  },
  {
    value: '选项3',
    label: '蚵仔煎'
  },
  {
    value: '选项4',
    label: '龙须面'
  },
  {
    value: '选项5',
    label: '北京烤鸭'
  }
]
const validatePass = (rule, value, callback) => {
  if (!Number.isInteger(Number(value))) {
    console.log(typeof value)
    return callback(new Error('请输入数字值'))
  } else {
    callback()
  }
}
export default {
  watch: {
    form: {
      handler(value) {
        console.log(value, 'value')
      },
      deep: true
    }
  },
  data() {
    return {
      form: {
        datas: [
          {
            id: 0,
            name: '张三',
            age: 20,
            option: ''
          },
          {
            id: 1,
            name: '李四',
            age: 32,
            option: ''
          }
        ]
      },
      columns: [
        {
          type: 'input',
          dataIndex: 'name',
          width: '',
          lable: '姓名'
        },
        {
          type: 'input',
          dataIndex: 'age',
          width: '',
          lable: '年纪'
        },
        {
          type: 'select',
          dataIndex: 'option',
          option,
          width: '',
          lable: '晚餐'
        }
      ],

      //表单验证规则
      rules: {
        name: [
          {
            type: 'string',
            required: true,
            trigger: 'blur',
            message: '请输入姓名'
          }
        ],
        age: [
          {
            //  type: 'number',
            required: true,
            trigger: 'blur',
            message: '请输入年龄'
          },
          { validator: validatePass, trigger: 'blur' }
          // {
          //   type: 'number',
          //   trigger: 'blur',
          //   min: 0,
          //   max: 120,
          //   message: '年龄最小0,最大120'
          // }
        ],
        options: [
          {
            type: 'string',
            required: true,
            trigger: 'change',
            message: '请输入晚餐'
          }
        ]
      }
    }
  },
  created() {
    //处理数据,为已有数据添加action:'view'
    this.form.datas.map((item) => {
      this.$set(item, 'action', 'view')
      return item
    })
  },

  methods: {
    //对部分表单字段进行校验
    validateField(form, index) {
      let result = true
      for (let item of this.$refs[form].fields) {
        if (item.prop.split('.')[1] == index) {
          this.$refs[form].validateField(item.prop, (error) => {
            if (error != '') {
              result = false
            }
          })
        }
        if (!result) break
      }
      return result
    },

    //对部分表单字段进行重置
    resetField(form, index) {
      this.$refs[form].fields.forEach((item) => {
        if (item.prop.split('.')[1] == index) {
          item.resetField()
        }
      })
    },

    //新增操作
    click_add(item, index) {
      if (!this.validateField('form', index)) return
      //模拟新增一条数据
      let itemClone = JSON.parse(JSON.stringify(item))
      itemClone.id = this.form.datas.length
      itemClone.action = 'view'
      this.form.datas.push(itemClone)
      this.resetField('form', index)
    },

    //新增-重置操作
    click_reset(item, index) {
      this.resetField('form', index)
    },

    //编辑-保存操作
    click_save(item, index) {
      if (!this.validateField('form', index)) return
      item.action = 'view'
    },

    //编辑-取消操作
    click_cancle(item, index) {
      this.resetField('form', index)
      item.action = 'view'
    },

    //编辑操作
    click_edit(item, index) {
      item.action = 'edit'
    },

    //删除操作
    click_delete(item, index) {
      this.$confirm('确定删除该条数据(ID' + item.id + ')吗?', '提示', {
        confirmButtonText: '确定',
        cancelButtonText: '取消',
        type: 'warning'
      })
        .then(() => {
          //模拟删除一条数据
          this.form.datas.splice(index, 1)
        })
        .catch(() => {})
    }
  }
}
</script>

<style>
.el-table .cell {
  overflow: visible;
}
.el-form-item {
  margin-bottom: 0;
}
.el-form-item__error {
  padding-top: 0;
  margin-top: -3px;
}
</style>

3.这里面有一点bug,就是rules你定义的type: 'number',可能会失效,你可以使用自定义校验去规避这个问题。大家可以放心直接使用,如果是vue3的项目做一些小改动就可以了。