vue+element大型表单解决方案(7)–表单形态

代码地址:gitee.com/wyh-19/supe…
上篇代码分支:essay-6
本篇代码分支:essay-7

系列文章:

前言

表单一般分为新增、编辑和详情(本系列还多一种自创需求的比对形态)等使用形态,根据不同的业务形态,表单可能会有不同的逻辑和状态,比如新增使用空的初始化数据,编辑和详情需要回显数据,详情表单项不可修改,比对表单需要获取新旧两份数据;且不同形态的表单,相应的按钮也可能不同。本解决方案将大型表单拆解为多个子表单组件,为了简化组件调用,页面层只负责主表单组件的调用和传入表单类型,剩下的所有逻辑都将在表单组件内实现,下面请看具体实现。

准备工作

之前所有的演示都是在add这个路由页面下完成的,页面代码如下:

<template>
  <div class="page">
    <the-form form-type="add" />
  </div>
</template>

<script>
import TheForm from '../components/the-form'
export default {
  components: {
    TheForm
  }
}
</script>

<style lang="scss" scoped>
.page {
  position: relative;
  width: 100%;
  height: 100%;
  overflow-y: auto;
}
</style>
复制代码

edit、detail、compare的页面代码文件使用和上面相同的代码,唯一的改动就是form-type相应改为editdetailcompare。由于我希望addeditdetailcompare成为解决方案的标准单词,因此没有定义常量,避免引入常量的麻烦。此时各页面都和新增页面一样,如下图所示:

image.png

具体实现

下面将修改表单组件,使其支持不同的使用形态。

form-type传参

由于各页面在使用表单组件时,都传递了form-type参数,因此主表单组件内,需要定义相应的props进行接收,这个表单类型不仅主表单要用,子表单同样需要获取到,因此每个子表单组件上都要增加form-type属性传递。有没有更优雅的传递方式呢?好在vue提供了provide-inject这种数据传递的方式,可以避免上面重复写属性传递的麻烦,而且子表单组件内部如果再有后代组件需要用到表单类型时,可以直接拿到。下面是主表单新增的代码:

props: {
    formType: {
      type: String,
      default: 'add'
    }
},
provide() {
    return {
      formType: this.formType
    }
}
复制代码

而各子表单内,只需要inject这个formType即可,由于已经抽取了子表单的mixin文件,修改mixin中实现:

// 新增inject配置项
inject: ['formType'],
复制代码

当业务规定detailcompare时,表单需要变成disabled状态,那么只需要在form1和form2的el-form组件内增加:disabled="formType === 'detail' || formType==='compare'",或者在mixin中定义formDisabled的计算属性,并应用到el-form的属性传递上,代码如下:

computed: {
    //...其他已存在代码
    formDisabled() {
      return this.formType === 'detail' || this.formType === 'compare'
    }
},
// form1.vue
// form2.vue
<el-form ref="form" :model="formData" :disabled="formDisabled" .../>
复制代码

此时发现详情页面和比对页面的表单全都变成不可用状态。
当业务规定不同表单形式需要显示不同的按钮,只需要在主表单中根据不同的formType进行不同按钮的隐藏显示即可,这里不做赘述。

表单数据

之前提到不同形式的表单,初始化的数据也是不一样的,而且本系列的表单方案主要是拆分,比如将示例的表单拆分成两个不同的子表单,但是后端接口并不会关心前端怎么实现,接口很可能依然是按完整的表单进行传递,比如:

// 接口传递的数据形式
{
    name:'xx',
    age:'xxx',
    compony:'xxxx'
}
// 我们拆分时用到的数据形式
{
    form1:{
        name:'xx',
        age:'xxx',
    },
    form2:{
        compony:'xxxx'
    },
}
复制代码

因此需要手动编写一个转换函数,代码如下:

resolveDataToMap(data) {
  const form1 = {
    name: data.name,
    age: data.age
  }
  const form2 = {
    company: data.company
  }
  return {
    form1,
    form2
  }
},
复制代码

实际业务中,数据肯定比这个复杂的多,不过套路是一样的。也不要担心文章里演示的都是简单的数据结构,为了文章的编写顺利,这里先处理最简单的场景,后面会专门介绍复杂子表单的形式,不过依然是这个套路。
此时编写两个模拟获取数据的方法,代码如下:

export function ajaxGetData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(
        {
          name: 'wyh',
          age: 30,
          company: 'aaa'
        }
      )
    }, 500)
  })
}

export function ajaxGetOldData() {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(
        {
          name: 'wyh19',
          age: 32,
          company: 'bbb'
        }
      )
    }, 1000)
  })
}
复制代码

在主表单中,修改data中初始的formDataMap定义,并增加created生命周期内增加获取数据的逻辑,代码如下:

// data中
data() {
    return {
      pageBlock: null,
      formDataMap: this.resolveDataToMap({
        name: '',
        age: null,
        company: ''
      }),
      oldFormDataMap: {}
    }
},
async created() {
    // 根据页面类型获取数据
    if (~['edit', 'detail', 'compare'].findIndex(i => i === this.formType)) {
      await this.getData()
    }
    if (~['compare'].findIndex(i => i === this.formType)) {
      await this.getOldData()
    }
},

// methods里相应的getData、getOldData如下:
async getData() {
  const data = await ajaxGetData()
  this.formDataMap = this.resolveDataToMap(data)
},
async getOldData() {
  const data = await ajaxGetOldData()
  this.oldFormDataMap = this.resolveDataToMap(data)
},
复制代码

到这里就完成了不同表单形式的数据获取和回显,结合表单拆分handleSave方法使用的Object.assign(formData, partFormData),又将各分组好的子表单数据还原成接口需要的结构。表单形态就介绍到这里,新旧两套数据也准备好了,下一篇将介绍如何做数据比对。谢谢您的阅读,欢迎提出指正意见!

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享