简单封装el-form实现动态添加表单,和表单校验
操作方式
全部代码
目录结构
使用
<template>
<div class="form">
<yu-form
ref="yuForm"
:basic-purchase-desc="basicPurchaseDesc"
:purchasing-information="purchasingInformation"
:is-show-add-btn="true"
@removeInquiry="removeInquiry"
@addInquiry="addInquiry"
/>
<el-button @click="next">提交</el-button>
</div>
</template>
<script>
import YuForm from './components/yuForm'
import {
basicPurchaseDesc,
purchasingInformation,
addStockOrder
} from './helper'
export default {
components: {
YuForm
},
data() {
return {
basicPurchaseDesc: basicPurchaseDesc(), // elm基础表单描述
purchasingInformation: purchasingInformation() // 采购表单信息数据
}
},
methods: {
// 调用ele-form内部校验,和封装yu-form表单校验
next() {
// 校验采购表单
this.$refs.yuForm.validate((valid) => {
// 校验eleForm表单
if (valid) {
console.log(this.purchasingInformation)
}
})
},
// 添加操作
addInquiry() {
// 添加表单
this.purchasingInformation.push({
material: '', // 物料名称
brand: '', // 品牌
specification: '', // 规格/型号
amount: '', // 采购量
unit: '', // 单位
remark: '' // 备注
})
// 向采购中添加描述渲染另一个表单
this.basicPurchaseDesc.push(addStockOrder())
},
// 删除操作
removeInquiry(formIndex) {
// 判断数据是否有多条
if (this.purchasingInformation.length > 1) {
// 把基础数据中的purchasingInformation的数组当前下标删除
this.purchasingInformation.splice(formIndex, 1)
// 向采购中删除当前下标描述
this.basicPurchaseDesc.splice(formIndex, 1)
this.$message({
message: '删除成功',
type: 'success'
})
} else {
this.$message({
message: '只剩一条了',
type: 'error'
})
}
}
}
}
</script>
引入的./hepler.js文件
// yu-form添加采购表单字段
const addStockOrder = () => {
return {
material: {
type: 'input',
field: 'material',
label: '物料名称',
rules: { required: true, message: '请输入物料信息', trigger: 'blur' }
},
brand: {
type: 'input',
field: 'brand',
label: '品牌',
rules: { required: true, message: '请输入品牌', trigger: 'blur' }
},
specification: {
type: 'input',
field: 'specification',
label: '规格/型号',
rules: { required: true, message: '请输入规格型号', trigger: 'blur' }
},
amount: {
type: 'input',
field: 'amount',
label: '采购量',
rules: { required: true, message: '请输入采购量', trigger: 'blur' }
},
unit: {
type: 'select',
field: 'unit',
label: '单位',
rules: { required: true, message: '请选择单位', trigger: 'change' },
options: [
{
label: '吨',
value: 0
},
{
label: '米',
value: 1
},
{
label: '件',
value: 2
}
]
},
remark: {
type: 'input',
label: '备注'
}
}
}
// 采购表单数据
const purchasingInformation = () => {
return [ // 采购信息
{
material: '', // 物料名称
brand: '', // 品牌
specification: '', // 规格/型号
amount: '', // 采购量
unit: '', // 单位
remark: '' // 备注
}
]
}
// 基础采购描述
const basicPurchaseDesc = () => {
return [{
material: {
type: 'input',
field: 'material',
label: '物料名称',
rules: { required: true, message: '请输入物料信息', trigger: 'blur' }
},
brand: {
type: 'input',
field: 'brand',
label: '品牌',
rules: { required: true, message: '请输入品牌', trigger: 'blur' }
},
specification: {
type: 'input',
field: 'specification',
label: '规格/型号',
rules: { required: true, message: '请输入规格型号', trigger: 'blur' }
},
amount: {
type: 'input',
field: 'amount',
label: '采购量',
rules: { required: true, message: '请输入采购量', trigger: 'blur' }
},
unit: {
type: 'select',
field: 'unit',
label: '单位',
rules: { required: true, message: '请选择单位', trigger: 'change' },
options: [
{
label: '吨',
value: 0
},
{
label: '米',
value: 1
},
{
label: '件',
value: 2
}
]
},
remark: {
type: 'input',
label: '备注'
}
}]
}
export {
basicPurchaseDesc,
addStockOrder,
purchasingInformation
}
yuForm封装的组件
<template>
<div class="yu-form">
<div class="add-inquiry-btn">
<el-button v-if="isShowAddBtn" @click="addInquiry">添加询价单</el-button>
</div>
<transition-group class="content" tag="form" name="fade-list">
<el-form
v-for="(formInfo, formIndex) in basicPurchaseDesc"
ref="rulesForm"
:key="formIndex"
:inline="true"
:model="purchasingInformation[formIndex]"
label-position="top"
>
<el-form-item
v-for="(info, infoIndex) in formInfo"
:key="info.field"
:label="info.label"
:prop="infoIndex"
:rules="info.rules"
>
<el-input
v-if="info.type === 'input'"
v-model="purchasingInformation[formIndex][infoIndex]"
:placeholder="'请输入'+info.label"
/>
<el-select
v-else
v-model="purchasingInformation[formIndex][infoIndex]"
:placeholder="'请选择'+info.label"
>
<el-option
v-for="option in info.options"
:key="option.value"
:label="option.label"
:value="option.value"
/>
</el-select>
</el-form-item>
<el-form-item class="delete">
<el-button type="danger" @click="removeInquiry(formIndex)">删除</el-button>
</el-form-item>
</el-form>
</transition-group>
</div>
</template>
<script>
// 对el-form二次封装
export default {
name: 'YuForm',
props: {
// 多组校验
basicPurchaseRules: {
type: Array,
default() {
return []
}
},
// 基础信息描述
basicPurchaseDesc: {
type: Array,
default() {
return []
}
},
// 基础数据
purchasingInformation: {
type: Array,
default() {
return {}
}
},
isShowAddBtn: {
type: Boolean,
default: false
}
},
data() {
return {
isValidate: null
}
},
methods: {
// 删除对应表单
removeInquiry(formIndex) {
this.$emit('removeInquiry', formIndex)
},
// 校验表单
validate(cb) {
this.isValidate = null
this.purchasingInformation.forEach((item, index) => {
this.$refs['rulesForm'][index].validate((valid) => {
this.isValidate += valid
if (this.purchasingInformation.length === this.isValidate) {
cb(valid)
}
})
})
},
// 添加询价单
addInquiry() {
this.$emit('addInquiry')
}
}
}
</script>
<style scoped lang="scss">
.add-inquiry-btn {
margin: 20px 0;
}
.delete {
margin: 0;
::v-deep .el-form-item__content {
height: 125px;
display: flex;
flex-direction: column;
justify-content: space-around;
}
}
.fade-list-enter-active {
opacity: 0;
transform: translateY(100%);
transition: all .6s;
}
.fade-list-enter-to {
opacity: 1;
transform: translateY(0);
}
.fade-list-leave-to {
opacity: 0;
transform: translateY(0);
transition: all .6s;
}
</style>
介绍
- 共有两个组件一个父组件,一个子组件,父组件中使用子组件中封装
- 父组件名称setp.vue,子组件yuForm.vue
下面是子组件yuForm.vue
- class=“add-inquiry-btn”,这个类名的标签是一个添加数据的form表单的标签,用来操作表单数据的
- 在el-form外层包裹了一层transition-group这个作用是用来让添加的数据有动画这个要配合样式使用
如何做到动态渲染的
-
首先对el-form使用v-for指令对basicPurchaseDesc数组进行循环,这个basicPurchaseDesc中的数据格式我会放在下方,这下方的这个格式是个数组对象,默认只有一条数据,当使用v-for对其循环时,会渲染出一个表单。我们再看内层的el-form-item,这里也是使用v-for对el-form遍历的之后的数据在进行遍历,(v-for不仅仅可以遍历数组也遍历对象,遍历对象的时候第一个值是返回的当前对象,第二个值就是当前对象的键),遍历之后可以通过type来判断是input还是select(我这里之处理了两种),这里的数据包括了校验规则。
// 表单描述信息 [{ material: { type: 'input', field: 'material', label: '物料名称', rules: { required: true, message: '请输入物料信息', trigger: 'blur' } }, brand: { type: 'input', field: 'brand', label: '品牌', rules: { required: true, message: '请输入品牌', trigger: 'blur' } }, specification: { type: 'input', field: 'specification', label: '规格/型号', rules: { required: true, message: '请输入规格型号', trigger: 'blur' } }, amount: { type: 'input', field: 'amount', label: '采购量', rules: { required: true, message: '请输入采购量', trigger: 'blur' } }, unit: { type: 'select', field: 'unit', label: '单位', rules: { required: true, message: '请选择单位', trigger: 'change' }, options: [ { label: '吨', value: 0 }, { label: '米', value: 1 }, { label: '件', value: 2 } ] }, remark: { type: 'input', label: '备注' } }]
-
在下面就是说purchasingInformation这个保存数据的问题,这个数据也是一个数组对象格式的,el-form和el-form-item中的mode就是绑定这个数据el-form通过遍历的返回的formIndex下标
<!-- 根据purchasingInformation[formIndex]这个下标返回对应的对象数据和其绑定 --> <el-form v-for="(formInfo, formIndex) in basicPurchaseDesc" ref="rulesForm" :key="formIndex" :inline="true" :model="purchasingInformation[formIndex]" label-position="top" ></el-form> // purchasingInformation数组内容 [ // 采购信息 { material: '', // 物料名称 brand: '', // 品牌 specification: '', // 规格/型号 amount: '', // 采购量 unit: '', // 单位 remark: '' // 备注 } ]
- 这个是el-form-item绑定数据和表单校验问题
<!-- 这里的数据v-for遍历formInfo, info返回的当前对象,infoIndex返回当前对象的key, 所以这里可以通过inforIndex作为下标, label的值就是上面那个描述信息数据自己添加的值, prop这个名称要和purchasingInformation这个数组中的字段对应才能触发必填校验, rules的校验也是,el-form有两种在标签中使用校验的方法第一种在el-form中使用, 第二种就是在el-form-item自身使用 el-input和el-select的判断也是通过type值进行判断,他们的v-mode的的key要和prop的名字 一一对应这个和elm是一样的 最后是一个删除的按钮不需要的表单可以直接删除,这个删除就是根据下标通过splice删除的 --> <el-form-item v-for="(info, infoIndex) in formInfo" :key="infoIndex" :label="info.label" :prop="infoIndex" :rules="info.rules" > <el-input v-if="info.type === 'input'" v-model="purchasingInformation[formIndex][infoIndex]" :placeholder="'请输入'+info.label" /> <el-select v-else v-model="purchasingInformation[formIndex][infoIndex]" :placeholder="'请选择'+info.label" > <el-option v-for="option in info.options" :key="option.value" :label="option.label" :value="option.value" /> </el-select> </el-form-item> <el-form-item class="delete"> <el-button type="danger" @click="removeInquiry(formIndex)">删除</el-button> </el-form-item>
-
下面这些是逻辑这块的yuForm.vue
<script>
// 对el-form二次封装
export default {
name: 'YuForm',
// 这里的数据是接收父组件传递的数据
props: {
// 基础信息描述
basicPurchaseDesc: {
type: Array,
default() {
return []
}
},
// 基础数据
purchasingInformation: {
type: Array,
default() {
return {}
}
},
// 是否显示添加按钮
isShowAddBtn: {
type: Boolean,
default: false
}
},
data() {
return {
isValidate: null
}
},
methods: {
// 删除对应表单
removeInquiry(formIndex) {
this.$emit('removeInquiry', formIndex)
},
// 校验表单
validate(cb) {
this.isValidate = null
// 由于有多个表单需要进行校验所以直接遍历数据让来校验是否有没有填写的字段
this.purchasingInformation.forEach((item, index) => {
this.$refs['rulesForm'][index].validate((valid) => {
// vaild这个返回的数据如果当前index下标的表单有校验字段的内容为空就会返回false反之true,
// 这里通过这个来进行累加判断如果返回的有一个是false都是校验不通过
this.isValidate += valid
if (this.purchasingInformation.length === this.isValidate) {
// 如果都是所有的字段都填写了这时候通过校验callback返回给父组件处理
cb(valid)
}
})
})
},
// 添加询价单
addInquiry() {
this.$emit('addInquiry')
}
}
}
</script>
父组件中
<template>
<!--
// 这两个都是从./hepler.js中返回的都是自定义的
// 描述信息用来渲染表单
basicPurchaseDesc
// 用来存储多个表单数据的
purchasingInformation
-->
<div class="form">
<yu-form
ref="yuForm"
:basic-purchase-desc="basicPurchaseDesc"
:purchasing-information="purchasingInformation"
:is-show-add-btn="true"
@removeInquiry="removeInquiry"
@addInquiry="addInquiry"
/>
<el-button @click="next">提交</el-button>
</div>
</template>
<script>
// 使用封装的组件
import YuForm from './components/yuForm'
// 从./hepler.js导出所需要的数据
import {
basicPurchaseDesc,
purchasingInformation,
addStockOrder
} from './helper'
export default {
components: {
YuForm
},
data() {
return {
basicPurchaseDesc: basicPurchaseDesc(), // elm基础表单描述
purchasingInformation: purchasingInformation() // 采购表单信息数据
}
},
methods: {
// 调用ele-form内部校验,和封装yu-form表单校验
next() {
// 校验采购表单
this.$refs.yuForm.validate((valid) => {
// 校验eleForm表单
if (valid) {
console.log(this.purchasingInformation)
}
})
},
// 添加操作
addInquiry() {
// 添加表单
this.purchasingInformation.push({
material: '', // 物料名称
brand: '', // 品牌
specification: '', // 规格/型号
amount: '', // 采购量
unit: '', // 单位
remark: '' // 备注
})
// 向采购中添加描述渲染另一个表单
this.basicPurchaseDesc.push(addStockOrder())
},
// 删除操作
removeInquiry(formIndex) {
// 判断数据是否有多条
if (this.purchasingInformation.length > 1) {
// 把基础数据中的purchasingInformation的数组当前下标删除
this.purchasingInformation.splice(formIndex, 1)
// 向采购中删除当前下标描述
this.basicPurchaseDesc.splice(formIndex, 1)
this.$message({
message: '删除成功',
type: 'success'
})
} else {
this.$message({
message: '只剩一条了',
type: 'error'
})
}
}
}
}
</script>