效果图
用了一年多antd组件,发现需要做动态表单的场景可真不少!今天就来整理一下vue版的动态表单该如何实现吧!~效果图如下:
支持的功能
官方案例
官方的案例如下,一行只有一个表单项且没有赋初始值。
封装组件
基于官网最朴实无华的例子,做了一下延伸。封装的组件,支持一下功能:
- 一行可有多个表单项
- 表单项可赋初始值(如:应用在编辑场景!)
- 可在一个页面上多次复用该组件!
代码演示
封装的组件
模板的部分和样式的部分按需进行调整,js部分可以完全照搬!
<template>
<div>
<div class="dynamic-wrap" :style="{ maxHeight: wrapHeight + 'px' }">
<div v-for="item in keysList" :key="item">
<a-row :gutter="24">
<a-col :span="6">
<a-form-item label="名称" :labelCol="{span: 8}" :wrapperCol="{span: 16}">
<a-input
placeholder="请填写名称"
v-decorator="[
`${title}Name[${item}]`,
{
initialValue: arr[item] ? arr[item].name : undefined,
rules: [{ required: true, message: '请填写名称!' }]
}
]"
/>
</a-form-item>
</a-col>
<a-col :span="6">
<a-form-item label="性别" :labelCol="{span: 9}" :wrapperCol="{span: 15}">
<a-select
placeholder="请选择性别"
v-decorator="[
`${title}Gender[${item}]`,
{
initialValue: arr[item] ? arr[item].gender : 'male',
rules: [{ required: true, message: '请选择性别!' }]
}
]"
>
<a-select-option value="male">男</a-select-option>
<a-select-option value="female">女</a-select-option>
</a-select>
</a-form-item>
</a-col>
<a-col :span="6">
<a-form-item label="年龄" :labelCol="{span: 8}" :wrapperCol="{span: 16}">
<a-input
placeholder="请填年龄"
v-decorator="[
`${title}Age[${item}]`,
{
initialValue: arr[item] ? arr[item].age : undefined,
rules: [{ required: true, message: '请填年龄' }]
}
]"
/>
</a-form-item>
</a-col>
<a-col :span="2" style="padding-left: 0px">
<a-form-item :labelCol="{span: 0}" :wrapperCol="{span: 24}">
<template v-if="keysList.length > 1">
<a-button type="dashed" icon="minus" @click="removeRow(item)" class="minusRowBtn"></a-button>
</template>
</a-form-item>
</a-col>
</a-row>
</div>
<a-button type="dashed" icon="plus" @click="addRow" class="addRowBtn">
新增一行
</a-button>
</div>
</div>
</template>
<script>
export default {
name: 'DynamicForm',
props: {
title: {
type: String,
default: ''
},
wrapHeight: { // 表单容器的高度
type: Number,
default: 120
},
arr: {
type: Array,
default: function () {
return []
}
}
},
data () {
return {
id: 0,
keysList: []
}
},
created () {
this.form = this.$form.createForm(this)
this.init()
},
methods: {
// 初始化
init () {
const arr = [0]
if (this.arr.length !== 0) {
for (let i = 1; i < (this.arr).length; i++) {
arr.push(i)
this.id = this.id + 1
}
}
this.keysList = arr
},
// 移除某行
removeRow (k) {
if (this.keysList.length === 1) { // 如果存在可以移除所有行的情况,把条件改为this.keysList.length === 0即可
return
}
this.keysList = this.keysList.filter(item => item !== k)
},
// 新增一行
addRow () {
this.id = this.id + 1
this.keysList = this.keysList.concat(this.id)
}
}
}
</script>
<style lang="less" scoped>
.dynamic-wrap {
padding-top: 10px;
background-color: white;
overflow-y: scroll;
overflow-x: hidden;
&::-webkit-scrollbar {
width: 7px;
}
&::-webkit-scrollbar-thumb {
background: #d8d8d8;
border-radius: 10px;
}
&::-webkit-scrollbar-track-piece {
background: transparent;
}
}
.minusRowBtn {
color: #f5222d;
background: #fff1f0;
border-color: #ffa39e;
padding-right: 7px;
padding-left: 7px;
height: 29px;
margin-left: 10px;
}
.addRowBtn {
width: 70%;
color: #1890ff;
border-color: #91d5ff;
margin: 0px 0px 20px 70px;
}
</style>
组件的使用
API
title:用于Form的字段绑定,如果多次使用了该组件,需要设置不同的值。
wrapHeight:容器的高度,超过多少px则显示滚动条
arr:数据源(如编辑场景从接口获取到的初始数据显示在表单项里)
<template>
<div class="padding: 20px;">
<a-form :form="form">
<a-divider>动态表单演示 -- author by Emily</a-divider>
<dynamic-form
:title="`${PARTONE}`"
:wrapHeight="360"
:arr="arr"
/>
</a-form>
<a-button style="margin-top: 25px;" type="primary" @click="handleSubmit">
提交
</a-button>
<div style="margin-top: 15px;">{{ param.field }}</div>
</div>
</template>
<script>
import DynamicForm from './DynamicForm'
const PARTONE = 'partOne'
export default {
name: 'DynamicFormWrap',
components: { DynamicForm },
data () {
return {
form: this.$form.createForm(this),
arr: [ // 模拟从接口获取到的数据(如编辑场景)
{ name: 'Tom', age: 20, gender: 'male' },
{ name: 'Emily', age: 22, gender: 'female' }
],
PARTONE,
param: {
field: '' // 模拟接口接收的参数
}
}
},
methods: {
// 提交
handleSubmit () {
const { form: { validateFields } } = this
validateFields((errors, values) => {
if (!errors) {
const partOneArr = [];
(values[`${PARTONE}Name`]).forEach((item, index) => {
const obj = {
name: item,
age: values[`${PARTONE}Age`][index],
gender: values[`${PARTONE}Gender`][index]
}
partOneArr.push(obj)
})
this.param.field = JSON.stringify(partOneArr)
}
})
}
}
}
</script>