大表单拆分的问题记录

1,369 阅读2分钟

原因

在业务开发过程中,发现表单有点长,修改一点点,滚动条就不够用,受不了,索性把一个表单直接拆为零散的子表单

图示

在主表单中,在postForm定义表单的所有字段通过v-model分发给子表单,子表单接受,每个子表单定义有validate()方法和getData(), 前者用于验证表单数据,后者用于把数据处理以后返回给主表单

子表单

  • 接受postForm, 子表单通过props接收postForm
  props: {
    value: Object,
    isEdit: {
      type: Boolean,
      default: false
    }
  }
  • validate方法 验证本身组件的表单项,并返回一个promise对象
  // 返回`elementUI`表单验证的结果(为`promise`对象)
  validate() {
    return this.$refs.goodsInfoFormRef.validate()
  }
  • getData方法 接受父组件传递进来主表单副本,对副本中的部分字段进行二次处理,返回处理后的主表单副本
  getData(postFormCopy) {
    // 接收主表单传递的表单副本,进行一系列数据处理
    postFormCopy.frequency = parseInt(postFormCopy.frequency)
    return postFormCopy
  }

主表单

<template>
  <div class="app-container">
    <el-form ref="postFormRef" :model="postForm" :rules="postFormRules" label-width="120px" label-suffix=":">
      <edit-goods-info-detail ref="infoFormRef" v-model="postForm" :is-edit="isEdit" />
      <edit-goods-brand-detail ref="brandFormRef" v-model="postForm" :is-edit="isEdit" />
      <edit-goods-fav-detail ref="favFormRef" v-model="postForm" :is-edit="isEdit" />

      <el-form-item>
        <el-button v-loading="loading" type="primary" @click="handleSumbit">提 交</el-button>
        <el-button @click="handleCancel">取 消</el-button>
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
import EditGoodsInfoDetail from './EditGoodsInfoDetail'
import EditGoodsBrandDetail from './EditGoodsBrandDetail'
import EditGoodsFavDetail from './EditGoodsFavDetail'
// 省略...

export default {
  // 省略...
  methods: {
  	handleSumbit() {
      Promise.all([
        this.$refs.postFormRef.validate(),
        this.$refs.infoFormRef.validate(),
        this.$refs.brandFormRef.validate(),
        this.$refs.favFormRef.validate()
      ]).then(() => {
        // 全部验证通过
        const requestApi = this.isEdit ? updateGoods : createGoods
        let postFormCopy = _cloneDeep(this.postForm)
        postFormCopy = this.$refs.infoFormRef.getData(postFormCopy)
        postFormCopy = this.$refs.brandFormRef.getData(postFormCopy)
        postFormCopy = this.$refs.favFormRef.getData(postFormCopy)

        requestApi(JSON.stringify(postFormCopy))
          .then(res => {
            this.$message.success(`${this.isEdit ? '编辑成功' : '新增成功'}`)
            this.$router.push({ name: 'GoodsList', query: { flush: true }})
          })
      })
      .catch(err => {
        console.log(err)
        return false
      })
    }
  }
}
</script>

点击提交按钮后,表单会经过如下流程

  • 验证主表单和所有子表单的字段,验证失败不提交表单
  • 验证通过,复制一份表单,将副本传递给每个子表单,让子表单对数据做最后一次处理
  • 处理完数据,发起提交表单的请求

编辑表单

一般流程是

  • 获取表单详情
  • 获取详情之后,子表单对详情中的数据进行处理,完成页面回显

主表单中

  created() {
    if (this.isEdit) {
      const id = this.$route.query.id
      this.postForm.id = id
      this.getDetail(this.postForm.id)
        .then(() => {
          this.$refs.infoFormRef.initEdit()
        })
        .catch(err => {
          console.log(err)
        })
    }
  },
  getDetail(id) {
    return new Promise((resolve, reject) => {
      getGoodsDetail(id).then(res => {
        const data = res.data
        this.postForm = data
        resolve(data)
      }).catch(err => {
        console.log(err)
        reject(err)
      })
    })
  }

子表单

// 省略...
initEdit() {
  const detail = this.value
  if(detail.gender) {
    this.setGender(detail.gender)
    this.value.gender = JSON.parse(detail.gender)
  }
},
setGender(v) {
  let parseRs = JSON.parse(v)
  if(Array.isArray(parseRs)) {
    if(parseRs.length === 0) {
      this.selectedSexLimit = sexLimitStatus.UNLIMI
      return
    }
    if(parseRs.length > 0) {
      this.selectedSexLimit = sexLimitStatus.LIMIT
      return
    }
  }
  this.selectedSexLimit = ''
  return
}

具有关联的子表单

业务需求中,有两个select是强耦合关系,前面select重新选择了,后面的select会被重置,并且发起请求,获取最新的options选项列表,两个select不在一个子表单中,是在两个子表单中。

新增数据

解决方法是,监听前面select的变化,变化的时候,置空后面一个select的已选值,并且发起请求

computed: {
  adxId() {
    return this.value.adx_id
  }
},
watch: {
  adxId: {
    handler: function(newVal, oldVal) {
	  this.value.resource_id = ''
      // 为空时,不发起请求
      if(isEmpty(newVal)) return
      this.getResourcesOptions(this.postForm.adx_id)
    },
    immediate: true
  }
}

编辑时

在编辑时候,会涉及重新赋值问题,如果有二次处理,监听到多次变化,导致编辑时,触发监听,把原来的值冲掉情况

所以需要识别出这种场景出来--编辑赋值

  • 编辑模式
  • 表单的属性值会经历由默认值(一般为空值)变化为有值
watch: {
  adxId: {
    handler: function(newVal, oldVal) {
      const isEmptyToVal = !isEmpty(newVal) && isEmpty(oldVal)
      // 编辑状态下,首次服务端赋值时,不进行置空
      if(!(this.isEdit && isEmptyToVal)) {
        this.value.resource_id = ''
      }
      if(isEmpty(newVal)) return
      this.getResourcesOptions(this.postForm.adx_id)
    },
    immediate: true
  }
}

代码

可以参考git仓库代码