代码地址:gitee.com/wyh-19/supe…
上篇代码分支:essay-6
本篇代码分支:essay-7
系列文章:
- vue+element大型表单解决方案(1)–概览
- vue+element大型表单解决方案(2)–表单拆分
- vue+element大型表单解决方案(3)–锚点组件(上)
- vue+element大型表单解决方案(4)–锚点组件(下)
- vue+element大型表单解决方案(5)–校验标识
- vue+element大型表单解决方案(6)–自动标识
前言
表单一般分为新增、编辑和详情(本系列还多一种自创需求的比对形态)等使用形态,根据不同的业务形态,表单可能会有不同的逻辑和状态,比如新增使用空的初始化数据,编辑和详情需要回显数据,详情表单项不可修改,比对表单需要获取新旧两份数据;且不同形态的表单,相应的按钮也可能不同。本解决方案将大型表单拆解为多个子表单组件,为了简化组件调用,页面层只负责主表单组件的调用和传入表单类型,剩下的所有逻辑都将在表单组件内实现,下面请看具体实现。
准备工作
之前所有的演示都是在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
相应改为edit
、detail
和compare
。由于我希望add
、edit
、detail
和compare
成为解决方案的标准单词,因此没有定义常量,避免引入常量的麻烦。此时各页面都和新增页面一样,如下图所示:
具体实现
下面将修改表单组件,使其支持不同的使用形态。
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'],
复制代码
当业务规定detail
和compare
时,表单需要变成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)
,又将各分组好的子表单数据还原成接口需要的结构。表单形态就介绍到这里,新旧两套数据也准备好了,下一篇将介绍如何做数据比对。谢谢您的阅读,欢迎提出指正意见!