动态添加表单组件的需求在实际开发中十分常见。本文将讲解如何使用 vue 实现动态添加表单的功能,让你轻松应对此类需求。
<template>
<div class="addFormBox">
<div v-for="(item,index) in formLabelAlign" :key="index">
<div class="formOuterBox">
<div class="formCotantBox">
<h3>车辆信息 {{index+1}}h3>
<el-form label-width="80px">
<el-form-item label="车牌号">
<el-input v-model="item.carBoard">el-input>
el-form-item>
<el-form-item label="车牌颜色">
<el-input v-model="item.carColor">el-input>
el-form-item>
<el-form-item label="排放阶段">
<el-input v-model="item.discharge">el-input>
el-form-item>
el-form>
div>
<div>
<el-button @click="addForm" type="success">添加车辆信息el-button>
<el-button v-if="formLabelAlign.length > 1" @click="removeIdx(item, index)" type="danger">删除此条车辆信息el-button>
div>
div>
div>
div>
template>
<script>
export default {
data() {
return {
// 表单绑定数据
formLabelAlign: [
{
carBoard: "",
carColor: "",
discharge: "",
},
],
};
},
methods: {
// 添加操作
addForm() {
// 定义一个标识,通过标识判断是否能添加信息
let statusType = true;
this.formLabelAlign.forEach((item) => {
if (
item.carBoard == "" ||
item.carColor == "" ||
item.discharge == ""
) {
this.$message({
message: "请完善信息后在添加",
type: "warning",
});
statusType = false;
}
});
if (statusType) {
this.formLabelAlign.push({
carBoard: "",
carColor: "",
discharge: "",
});
}
},
// 删除操作
removeIdx(item, index) {
this.formLabelAlign.splice(index, 1);
this.$message({
message: "删除成功",
type: "success",
});
},
},
};
script>
<style scoped>
.addFormBox {
margin: 20px;
}
.formOuterBox {
margin-bottom: 20px;
padding: 30px 40px;
background: white;
border-radius: 30px;
}
h3 {
margin: 0px 0px 20px 0px;
}
style>
在 data
中定义了一个名为 formLabelAlign
的数组,用于存储车辆信息的表单数据。初始时,数组中只有一个空对象,表示一个空的车辆信息表单;
在模板中使用 v-for
指令循环遍历 formLabelAlign
数组,生成多个车辆信息表单;
每个车辆信息表单包含三个输入框,分别是车牌号、车牌颜色和排放阶段。这些输入框使用 v-model
指令与 formLabelAlign
数组中的对应属性进行双向绑定,实现数据的同步更新;
每个车辆信息表单下方有两个操作按钮,一个是"添加车辆信息"按钮,另一个是"删除此条车辆信息"按钮。点击"添加车辆信息"按钮时,会检查当前表单是否填写完整,如果有任何一个输入框为空,则会弹出警告消息,要求完善信息。如果所有输入框都填写完整,则会在 formLabelAlign
数组末尾添加一个空的车辆信息对象,实现添加表单的功能;
点击"删除此条车辆信息"按钮时,会根据按钮所在的表单索引,从 formLabelAlign
数组中删除对应的车辆信息对象,实现删除表单的功能。
提交后的数据
<template>
<div>
<el-table :row-class-name="tableRowClassName" :data="formLabelAlign" border style="width: 100%">
<el-table-column align="center" width="100px" type="index" label="序号">el-table-column>
<el-table-column align="center" prop="name" label="编号">
<template slot-scope="scope">
<el-input size="mini" v-model="scope.row.name">el-input>
template>
el-table-column>
<el-table-column align="center" prop="rylx" label="燃油类型">
<template slot-scope="scope">
<el-select @change="rylxChange(scope.row.rylx,scope.row.index)" size="mini" v-model="scope.row.rylx" placeholder="请选择燃油类型">
<el-option v-for="item in rylxOptions" :key="item.value" :label="item.label" :value="item.value">el-option>
el-select>
template>
el-table-column>
<el-table-column align="center" prop="yh" label="油号">
<template slot-scope="scope">
<el-select size="mini" v-model="scope.row.yh">
<el-option v-for="item in scope.row.yhOptions" :key="item.label" :label="item.label" :value="item.label">
el-option>
el-select>
template>
el-table-column>
<el-table-column align="center" prop="address" label="地址">
<template slot-scope="scope">
<el-input size="mini" v-model="scope.row.address">el-input>
template>
el-table-column>
<el-table-column align="center" prop="date" label="操作">
<template slot-scope="scope">
<i @click="addForm" class="el-icon-circle-plus">i>
<i v-if="formLabelAlign.length>1" @click="removeIdx(scope.row,scope.row.index)" style="color:rgb(216,30,6)"
class="el-icon-remove">i>
template>
el-table-column>
el-table>
div>
template>
<script>
export default {
data() {
return {
formLabelAlign: [
{
name: "",
rylx: "",
yh: "",
address: "",
yhOptions: [
{
value: "1",
label: "5",
},
{
value: "2",
label: "0",
},
{
value: "3",
label: "-10",
},
{
value: "4",
label: "-20",
},
{
value: "5",
label: "-35",
},
{
value: "6",
label: "-50",
},
],
},
],
rylxOptions: [
{
value: "0",
label: "汽油",
},
{
value: "1",
label: "柴油",
},
],
};
},
methods: {
// 添加index
tableRowClassName({ row, rowIndex }) {
row.index = rowIndex;
},
// 切换汽油柴油不同数据
rylxChange(e, index) {
// 柴油
if (e == "1") {
this.formLabelAlign[index].yhOptions = [
{
value: "1",
label: "5",
},
{
value: "2",
label: "0",
},
{
value: "3",
label: "-10",
},
{
value: "4",
label: "-20",
},
{
value: "5",
label: "-35",
},
{
value: "6",
label: "-50",
},
];
} else {
// 汽油
this.formLabelAlign[index].yhOptions = [
{
value: "1",
label: "89",
},
{
value: "2",
label: "92",
},
{
value: "3",
label: "95",
},
{
value: "4",
label: "98",
},
];
}
},
// 添加操作
addForm() {
if (this.isDataComplete()) {
this.formLabelAlign.push({
name: "",
rylx: "",
yh: "",
address: "",
});
} else {
this.$message({
message: "请完善信息后再添加",
type: "warning",
});
}
},
isDataComplete() {
return this.formLabelAlign.every(
(item) => item.name && item.rylx && item.yh && item.address
);
},
// 删除操作
removeIdx(item, index) {
this.formLabelAlign.splice(index, 1);
this.$message({
message: "删除成功",
type: "success",
});
},
},
};
script>
<style scoped>
i {
font-size: 24px;
cursor: pointer;
}
style>
data
中定义了 formLabelAlign
数组,用于存储表格中的数据。数组中的每个元素都是一个对象,包含了编号、燃油类型、油号、地址和油号选项等属性;template
中使用了 el-table
和 el-table-column
标签,分别表示表格和表格中的列。其中,el-table-column
标签中使用了 slot-scope
属性,用于定义列中的内容;el-table-column
标签中,使用了 prop
属性指定了列对应的数据属性,使用了 v-model
指令实现了数据的双向绑定;el-select
标签实现了下拉框,使用了 v-for
指令遍历 rylxOptions
数组生成选项;el-select
标签实现了下拉框,使用了 v-for
指令遍历当前行的 yhOptions
数组生成选项;i
标签实现了添加和删除按钮,使用了 @click
指令绑定了对应的方法;methods
中定义了一些方法,包括 tableRowClassName、rylxChange、addForm、isDataComplete
和 removeIdx
等。其中,tableRowClassName
方法用于给每一行数据添加一个 index
属性,rylxChange
方法用于根据燃addForm
方法用于添加一行数据,isDataComplete
方法用于判断数据是否完整,removeIdx
方法用于删除一行数据。当然你也可以将操作按钮改成下面这种交互方式
<el-table-column align="center" prop="date" label="操作">
<template slot-scope="scope">
<i @click="addForm(scope.row, scope.$index)" class="el-icon-circle-plus" v-if="formLabelAlign.length == scope.$index + 1">i>
<i v-if="formLabelAlign.length > 1" @click="removeIdx(scope.row, scope.$index)" style="color:rgb(216,30,6)"
class="el-icon-remove">i>
template>
el-table-column>
<template>
<el-dialog class="ui-dialog" :close-on-click-modal="false" title="配置模板字段" width="30%" :visible.sync="dialogVisible"
@close="$emit('update:dialogChild', false)">
<div>
<el-form ref="elForm" :model="formData" :rules="rules" size="medium" label-width="100px">
<el-form-item label="模板类型" prop="type">
<el-select v-model="formData.type" clearable placeholder="请选择模板类型">
<el-option v-for="item in typeOptions" :key="item.value" :label="item.label" :value="item.value">
el-option>
el-select>
el-form-item>
<div v-for="(input, index) in formData.inputs" :key="index">
<el-form-item :label="'字段名称' + (index+1)" :prop="'inputs[' + index + '].value'" :rules="getInputRules(index)">
<div class="inputRow"><el-input v-model="input.value" placeholder="请输入内容"
:disabled="index > 0 && !formData.inputs[index - 1].value">el-input>
<span v-if="index === formData.inputs.length - 1">
<el-button type="primary" icon="el-icon-plus" @click="addInput" :disabled="!input.value">el-button>
<el-button type="danger" icon="el-icon-minus" @click="removeInput(index)"
:disabled="formData.inputs.length === 1">el-button>
span>
div>
el-form-item>
div>
el-form>
<div class="bomBtn">
<el-button @click="$emit('update:dialogChild', false)">取消el-button>
<el-button type="primary" @click="submitForm">确定el-button>
div>
div>
el-dialog>
template>
<script>
export default {
data() {
return {
dialogVisible: true, //弹框显隐
typeOptions: [
{
label: "生产",
value: "0",
},
{
label: "存储",
value: "1",
},
{
label: "运输",
value: "2",
},
],
// 表单数据
formData: {
type: "0",
inputs: [{ value: "" }], // 初始只有一个输入框
},
rules: {
type: [
{
required: true,
message: "请选择模板类型",
trigger: "change",
},
],
inputs: [
{
required: true,
message: "请输入字段名称",
trigger: "change",
},
],
},
};
},
props: {
dialogChild: {
type: Boolean,
default: false,
},
},
watch: {
dialogChild: {
handler(newName, oldName) {
this.dialogVisible = newName;
},
deep: true,
},
},
methods: {
// 添加
addInput() {
const lastIndex = this.formData.inputs.length - 1;
if (this.formData.inputs[lastIndex].value) {
this.formData.inputs.push({ value: "" });
}
},
// 删除
removeInput(index) {
this.formData.inputs.splice(index, 1);
},
// 自定义校验
getInputRules(index) {
return [
{
required: true,
message: `请输入字段名称${index + 1}`,
trigger: "change",
},
];
},
// 提交
submitForm() {
this.$refs["elForm"].validate((valid) => {
if (valid) {
// 提交表单逻辑
console.log(this.formData);
} else {
console.log("表单验证失败!");
return false;
}
});
},
},
};
script>
<style lang="scss" scoped>
.bomBtn {
display: flex;
justify-content: right;
}
.el-select {
width: 100%;
}
.inputRow {
display: flex;
align-items: center;
}
.inputRow .el-input {
flex: 1;
margin-right: 10px;
}
style>
el-dialog
组件来展示弹框,并绑定 dialogVisible
属性控制弹框的显隐;el-form
中使用 el-select
组件来选择模板类型,并绑定 formData.type
属性;v-for
指令遍历 formData.inputs
数组,动态生成 el-form-item
和 el-input
组件,绑定对应的字段名称和值;el-input
组件中使用 v-model
指令绑定输入框的值,并根据条件判断是否禁用输入框;el-form-item
中使用 getInputRules
方法动态生成字段名称的校验规则;el-form
中定义取消和确定按钮,并绑定对应的点击事件;methods
中实现添加输入框、删除输入框、自定义校验和提交表单的逻辑。当然你也可以将操作按钮改成下面这种交互方式
<template>
<el-dialog class="ui-dialog" :close-on-click-modal="false" title="配置模板字段" width="30%" :visible.sync="dialogVisible"
@close="$emit('update:dialogChild', false)">
<div>
<el-form ref="elForm" :model="formData" :rules="rules" size="medium" label-width="100px">
<el-form-item label="模板类型" prop="type">
<el-select v-model="formData.type" clearable placeholder="请选择模板类型">
<el-option v-for="item in typeOptions" :key="item.value" :label="item.label" :value="item.value">
el-option>
el-select>
el-form-item>
<div v-for="(input, index) in formData.inputs" :key="index">
<el-form-item :label="'字段名称' + (index+1)" :prop="'inputs[' + index + '].value'" :rules="getInputRules(index)">
<div class="inputRow">
<el-input v-model="input.value" placeholder="请输入内容" :disabled="index > 0 && !formData.inputs[index - 1].value">el-input>
<span>
<el-button type="primary" icon="el-icon-plus" @click="addInput(index)" :disabled="!input.value">el-button>
<el-button type="danger" icon="el-icon-minus" @click="removeInput(index)"
:disabled="formData.inputs.length === 1">el-button>
span>
div>
el-form-item>
div>
el-form>
<div class="bomBtn">
<el-button @click="$emit('update:dialogChild', false)">取消el-button>
<el-button type="primary" @click="submitForm">确定el-button>
div>
div>
el-dialog>
template>
<script>
export default {
data() {
return {
dialogVisible: true, //弹框显隐
typeOptions: [
{
label: "生产",
value: "0",
},
{
label: "存储",
value: "1",
},
{
label: "运输",
value: "2",
},
],
// 表单数据
formData: {
type: "0",
inputs: [{ value: "" }], // 初始只有一个输入框
},
rules: {
type: [
{
required: true,
message: "请选择模板类型",
trigger: "change",
},
],
},
};
},
props: {
dialogChild: {
type: Boolean,
default: false,
},
},
watch: {
dialogChild: {
handler(newName, oldName) {
this.dialogVisible = newName;
},
deep: true,
},
},
methods: {
// 添加
addInput(index) {
if (this.formData.inputs[index].value) {
this.formData.inputs.splice(index + 1, 0, { value: "" });
}
},
// 删除
removeInput(index) {
this.formData.inputs.splice(index, 1);
},
// 自定义校验
getInputRules(index) {
return [
{
required: true,
message: `请输入字段名称${index + 1}`,
trigger: "change",
},
];
},
// 提交
submitForm() {
this.$refs["elForm"].validate((valid) => {
if (valid) {
console.log(this.formData);
// 提交表单逻辑
} else {
console.log("表单验证失败!");
return false;
}
});
},
},
};
script>
<style lang="scss" scoped>
.bomBtn {
display: flex;
justify-content: right;
}
.el-select {
width: 100%;
}
.inputRow {
display: flex;
align-items: center;
}
.inputRow .el-input {
flex: 1;
margin-right: 10px;
}
style>
⭐ element表单验证技巧:如何应对动态数据循环