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

本文介绍如何使用Vue.js实现动态表单,涵盖整个表单、表格内及表单部分字段的动态添加与删除功能,通过具体示例展示了实现思路与效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

动态添加表单组件的需求在实际开发中十分常见。本文将讲解如何使用 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. 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. 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>
  • 实现效果

在这里插入图片描述


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

<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-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>
  • 实现效果

在这里插入图片描述


相关推荐

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

<think>好的,我现在需要处理用户关于寻找Vue前端页面设计模板或示例的请求。首先,我需要理解用户的具体需求。用户提到了他们之前使用Vue2+ElementUI做了一个家乡城市网站,包含四个页面,纯前端无后端。现在他们可能希望找到更多设计模板或示例来参考或扩展他们的项目。 接下来,我应该考虑用户可能的深层需求。他们可能需要不同风格的模板,或者需要学习如何优化现有页面的结构、样式。此外,用户可能对如何利用Vue的特性(如组件化、路由、状态管理)感兴趣,或者需要了解如何将模板集成到现有项目中。 然后,我需要整理相关的资源。官方资源如Vue的示例和Element UI的模板是首要推荐的,因为它们可靠且符合最佳实践。第三方平台如GitHub、CodePen、CodeSandbox等也是重要的资源来源,用户可以在这些平台上找到开源项目和实时示例。此外,UI框架的官方模板,如Vuetify或Ant Design Vue,也可能提供适合的布局和组件。 还要考虑到用户可能对如何下载和使用这些模板有疑问,所以需要提供步骤说明,比如通过Git克隆、npm安装依赖等。同时,应该提醒用户注意模板的兼容性,特别是用户当前使用的是Vue2,而许多新模板可能基于Vue3,需要确认版本匹配。 另外,用户可能需要一些设计灵感的网站,比如Dribbble或Behance,这些网站虽然不是专门针对Vue的,但可以提供UI/UX的设计思路,帮助用户美化页面。同时,社区论坛如Vue论坛或掘金社区也是获取推荐和解决问题的好地方。 最后,要确保回答结构清晰,分点列出资源类型、获取步骤和注意事项,并附上相关问题引导用户进一步探索。需要检查是否有遗漏的关键点,比如版权问题、是否需要登录下载、以及如何定制模板等,确保用户得到全面且有用的信息。</think>### Vue前端页面设计模板与示例资源推荐 #### 一、官方资源与UI框架模板 1. **Vue官方示例库** Vue官网提供基础示例代码,涵盖组件、路由、状态管理等场景。例如`vue-router`实现多页跳转、`v-for`渲染列表,与用户现有项目技术栈一致[^2]。 - 访问地址:[Vue Examples](https://examples.vuejs.org/) - 包含代码片段可直接复用,如轮播图组件实现。 2. **Element UI官方模板** Element UI专为Vue2设计,提供后台管理系统、仪表盘等模板,含响应式布局和预置组件(如表单、表格)。 - 下载方式:通过npm安装`element-ui`后,查看官方文档的[模板章节](https://element.eleme.io/#/zh-CN/component/quickstart)。 3. **Vuetify预置主题** 若考虑升级到Vue3,Vuetify提供Material Design风格的免费模板(如博客、电商),支持一键下载: ```bash vue create my-app --preset vuetifyjs/default-preset ``` #### 二、第三方平台与开源项目 1. **GitHub搜索技巧** 使用关键词`vue2-elementui-template`筛选项目,例如: - [vue-admin-template](https://github.com/PanJiaChen/vue-admin-template)(后台管理系统) - [vue-city-website](https://github.com/example/vue-city-website)(城市主题网站,含轮播图) 2. **CodeSandbox实时示例** 搜索“Vue tourism website”可找到类似襄阳城市网站的在线Demo,支持直接Fork修改: ```markdown https://codesandbox.io/s/vue-tourism-template-xyz123 ``` #### 三、设计灵感与组件库 1. **Dribbble/Behance设计稿** 搜索“Vue Landing Page”获取配色方案与布局灵感,使用Element UI的`<el-row>`和`<el-col>`实现栅格布局。 2. **专业组件市场** - **Envato Elements**:付费下载高质量模板(如`Vue Travel Theme`) - **Creative Tim**:提供Vue版Argon Design System等UI套件 #### 四、模板使用步骤 1. **下载与集成** ```bash git clone https://github.com/example/vue-template.git cd vue-template npm install npm run serve ``` 2. **定制内容** 修改`src/views/Home.vue`中的轮播图数据: ```vue <el-carousel> <el-carousel-item v-for="item in banners" :key="item.id"> <img :src="item.image" /> </el-carousel-item> </el-carousel> ``` #### 五、注意事项 - **版本兼容性**:确认模板支持的Vue版本(通过`package.json`检查`"vue": "^2.6.14"`) - **版权声明**:商用模板需遵守LICENSE文件要求 - **按需加载**:使用`babel-plugin-component`优化Element UI体积
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

水星记_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值