当前位置:   article > 正文

vue 动态表单添加,让你的网站更具交互性_vue动态表单

vue动态表单

前言

动态添加表单组件的需求在实际开发中十分常见。本文将讲解如何使用 vue 实现动态添加表单的功能,让你轻松应对此类需求。


一、整个表单内容都是动态的

<template>
  <div class="addFormBox">
    <!-- 循环data中定义的数组 -->
    <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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96

实现思路

  1. data 中定义了一个名为 formLabelAlign 的数组,用于存储车辆信息的表单数据。初始时,数组中只有一个空对象,表示一个空的车辆信息表单;

  2. 在模板中使用 v-for 指令循环遍历 formLabelAlign 数组,生成多个车辆信息表单;

  3. 每个车辆信息表单包含三个输入框,分别是车牌号、车牌颜色和排放阶段。这些输入框使用 v-model 指令与 formLabelAlign 数组中的对应属性进行双向绑定,实现数据的同步更新;

  4. 每个车辆信息表单下方有两个操作按钮,一个是"添加车辆信息"按钮,另一个是"删除此条车辆信息"按钮。点击"添加车辆信息"按钮时,会检查当前表单是否填写完整,如果有任何一个输入框为空,则会弹出警告消息,要求完善信息。如果所有输入框都填写完整,则会在 formLabelAlign 数组末尾添加一个空的车辆信息对象,实现添加表单的功能;

  5. 点击"删除此条车辆信息"按钮时,会根据按钮所在的表单索引,从 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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184

实现思路

  1. data 中定义了 formLabelAlign 数组,用于存储表格中的数据。数组中的每个元素都是一个对象,包含了编号、燃油类型、油号、地址和油号选项等属性;
  2. template 中使用了 el-tableel-table-column 标签,分别表示表格和表格中的列。其中,el-table-column 标签中使用了 slot-scope 属性,用于定义列中的内容;
  3. el-table-column 标签中,使用了 prop 属性指定了列对应的数据属性,使用了 v-model 指令实现了数据的双向绑定;
  4. 在燃油类型列中,使用了 el-select 标签实现了下拉框,使用了 v-for 指令遍历 rylxOptions 数组生成选项;
  5. 在油号列中,使用了 el-select 标签实现了下拉框,使用了 v-for 指令遍历当前行的 yhOptions 数组生成选项;
  6. 在操作列中,使用了 i 标签实现了添加和删除按钮,使用了 @click 指令绑定了对应的方法;
  7. methods 中定义了一些方法,包括 tableRowClassName、rylxChange、addForm、isDataCompleteremoveIdx 等。其中,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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 实现效果

在这里插入图片描述


三、表单部分内容动态添加

<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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145

实现思路

  • 初始化数据,包括弹框的显隐状态、模板类型选项和表单数据;
  • 在模板中使用 el-dialog 组件来展示弹框,并绑定 dialogVisible 属性控制弹框的显隐;
  • el-form 中使用 el-select 组件来选择模板类型,并绑定 formData.type 属性;
  • 使用 v-for 指令遍历 formData.inputs 数组,动态生成 el-form-itemel-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>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 实现效果

在这里插入图片描述


相关推荐

element表单验证技巧:如何应对动态数据循环

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/Gausst松鼠会/article/detail/91396
推荐阅读
相关标签
  

闽ICP备14008679号