vue中用Element-ui封装表单(form)组件动态渲染数据展示

1. 创建一个searchForm组件,将需要封装的searchForm组件全局注册,为了方便下次直接使用

main.js文件中全局注册

import SearchForm from './components/SearchForm'
Vue.component('SearchForm', SearchForm)

2. 在searchForm组件中创建基本结构

<template>
  <div>
    <el-form
      class="form"
      ref="refForm"
      :size="size"
      inline
      :label-width="labelWidth"
    >
        <el-form-item
        v-for="item in searchForm"
        :label="item.label"
        :key="item.prop"
        class="formItem"
      >
      </el-form-item>
    </el-form>
  </div>
</template>

<script>
export default {
  name: "SearchForm",
  props: {
    //控制文本框大小
    labelWidth: {
      type: String,
      default: "50px",
    },
    //控制每个控件的尺寸
    size: {
      type: String,
      default: "mini",
    },
    //渲染表单的表头和类型的数据
    searchForm: {
      type: Array,
      default: () => [],
    },
    //表单收集到的值
    searchData: {
      type: Object,
      default: () => {},
    },
  },
  data() {
    return {};
  },
  methods: {},
};
</script>

以上通过props传递的数据包含业务相关数据组件前端行为需要的数据

el-form-item会根据传递的searchForm数据进行遍历,每遍历一次会产生一个el-form-item,而我们只需要在遍历时拿着每一项进行判断,进而创建对应的表单组件

<el-form-item
        v-for="item in searchForm"
        :label="item.label"
        :key="item.prop"
        class="formItem"
      >
        <!-- 输入框 -->
        <el-input
          v-if="item.type === 'Input'"
          v-model="searchData[item.prop]"
          size="mini"
          :style="{ width: item.width }"
        >
        </el-input>

       <!-- 下拉框 -->
        <el-select
          v-if="item.type === 'Select'"
          v-model="searchData[item.prop]"
          size="mini"
          @change="item.change(searchData[item.prop])"
          :style="{ width: item.width }"
        >
          <el-option
            v-for="op in item.options"
            :label="op.label"
            :value="op.value"
            :key="op.value"
          >
              </el-option>   
              ...
              ...
              ...
</el-form-item>

3. 使用searchForm组件,并通过props传递数据

<template>
  <div class="ces-main">
    <searchForm
      label-width="50px"
      :searchData="searchData"
      :searchForm="searchForm"
    >
    </searchForm>
  </div>
</template>

export default {
  name: "home",
  data() {
    //表单查询一般分为input,下拉框,和日期,下面是下拉框时的设置下拉信息的情况,我的项目没有用到,但是留着方便看
    let sexs = [
      { label: "男", value: "1" },
      { label: "女", value: "2" },
    ];
    let sexProps = { label: "label", value: "value" };
    return {
      searchData: {
        //查询表单的对应的值
        name: "",
        sex: null,
      },
      searchForm: [
        //这里是渲染查询表单的表头和类型的数据
        {
          type: "Input",
          label: "姓名",
          prop: "name",
          width: "180px",
          placeholder: "请输入姓名...",
        },
        {
          type: "Select",
          label: "性别",
          prop: "sex",
          width: "180px",
          options: sexs,
          props: sexProps,
          change: (row) => console.log(row),
          placeholder: "请选择性别...",
        },
      ],
    };
  },

};
</script>

4. 效果图展示:

5. 总结

searchForm中配置一组组对象,对象中包含要生成的控件类型并且添加一个属性,该属性的名字和在父组件的data定义的名字同名,因为要保证收集到的数据是响应式的,然后将该对象通过props传递给searchForm组件,之后会对searchForm进行遍历,每遍历一次就会根据当前对象生成一个表单控件

 

<think>我们正在处理一个关于Vue.jsElement-UI的问题,目标是实现一个父组件,该组件具有新增、修改、查看按钮,点击按钮弹出弹框(子组件),在弹框中填写必填项,点击保存后将数据以数组形式展示在父组件的列表页,关闭弹框。 根据引用内容,我们可以提取以下关键点: 1. 引用[1]提到:在父组件中通过按钮触发弹窗,在弹窗中调用子组件表单。这是一种常见的设计模式。 2. 引用[2]提到了封装弹框组件需要的参数: - title:弹框标题 - formConfig:表单配置,用于描述表单项(包括prop属性、组件类型等) - formData:传递给表单数据(新增时为空对象,修改时为当前行数据- visible:控制弹框显示与隐藏 - @confirm:弹框点击确定时触发的事件,将表单数据传递给父组件 3. 引用[3]提到了一个常见问题:当关闭弹框后再次打开时数据为空。解决方案是在父组件中监听弹框的visible属性,当关闭弹框(visible为false)时,将传递给子组件数据清空(或重置)。 因此,我们可以按照以下步骤实现: 步骤1:创建父组件(例如ParentComponent.vue-组件中有一个表格展示数据(数组形式) - 有新增、修改、查看按钮(修改查看按钮通常在表格每一行的操作列) - 使用一个子组件(弹框组件)用于表单展示输入 步骤2:创建子组件(例如FormDialog.vue-组件是一个弹框,内部是一个表单 - 根据组件传递的formConfig动态生成表单 - 表单数据使用父组件传递的formData - 有保存按钮(在确认时进行表单校验,校验通过则触发confirm事件传递表单数据,然后关闭弹框) 步骤3:父组件中的逻辑 - 定义数据数组(tableData)用于表格展示 - 定义控制弹框显示的变量(dialogVisible)弹框标题(dialogTitle) - 定义传递给子组件表单数据formData)表单配置(formConfig) - 定义操作类型(operationType:'add'、'edit'、'view') - 点击新增按钮:设置dialogTitle为“新增”,operationType为'add',formData为一个空对象(或根据表单配置的初始值),dialogVisible=true - 点击修改按钮:设置dialogTitle为“修改”,operationType为'edit',formData为当前行的数据(注意:这里需要深拷贝,避免直接修改表格数据),dialogVisible=true - 点击查看按钮:设置dialogTitle为“查看”,operationType为'view',formData为当前行的数据,dialogVisible=true(此时表单可能为只读) - 监听子组件的@confirm事件:当收到该事件时,根据操作类型处理数据- 如果是新增:将表单数据(子组件传递的数据)push到tableData中 - 如果是修改:根据索引(或唯一标识)找到tableData中对应的项,更新该项 - 如果是查看:无需处理(因为查看不需要保存) 然后关闭弹框(dialogVisible=false) - 另外,为了避免关闭弹框后再次打开时数据残留,可以监听dialogVisible,当其为false时,将formData重置为空对象(或根据引用[3]的做法,设置为空数组?但这里我们传递的是对象,所以可以设置为空对象或初始值) 步骤4:子组件的逻辑 - 接收父组件传递的props: title, formConfig, formData, visible - 在子组件内,使用formData初始化一个本地的表单数据对象(避免直接修改prop) - 使用formConfig动态生成表单(可以用v-for遍历formConfig,根据每一项的type渲染不同的表单组件- 表单验证:在保存时调用validate方法,验证通过则触发confirm事件($emit('confirm', 本地表单数据)),然后关闭弹框(通过触发父组件的update:visible事件来改变visible,或者由父组件控制关闭) - 注意:子组件关闭弹框时,不应该直接修改visible,而是通知父组件更改visible(.sync修饰符或自定义事件) 具体实现代码示例: 父组件(ParentComponent.vue): ```vue <template> <div> <el-button type="primary" @click="handleAdd">新增</el-button> <el-table :data="tableData" style="width:100%"> <!-- 表格列 --> <el-table-column prop="name" label="姓名"></el-table-column> <el-table-column prop="age" label="年龄"></el-table-column> <el-table-column label="操作"> <template slot-scope="scope"> <el-button size="mini" @click="handleEdit(scope.row)">修改</el-button> <el-button size="mini" @click="handleView(scope.row)">查看</el-button> </template> </el-table-column> </el-table> <!-- 弹框子组件 --> <form-dialog :title="dialogTitle" :formConfig="formConfig" :formData="tempFormData" :visible.sync="dialogVisible" @confirm="handleConfirm" ></form-dialog> </div> </template> <script> import FormDialog from './FormDialog.vue' export default { components: { FormDialog }, data() { return { tableData: [], // 表格数据 dialogVisible: false, // 控制弹框显示 dialogTitle: '', // 弹框标题 operationType: 'add', // 操作类型:add, edit, view tempFormData: {}, // 传递给子组件表单数据 formConfig: [ { prop: 'name', label: '姓名', type: 'input', required: true }, { prop: 'age', label: '年龄', type: 'input', required: true } // 其他表单项配置... ] } }, methods: { handleAdd() { this.dialogTitle = '新增' this.operationType = 'add' this.tempFormData = {} // 重置为空白表单 this.dialogVisible = true }, handleEdit(row) { this.dialogTitle = '修改' this.operationType = 'edit' // 深拷贝当前行数据,避免直接修改 this.tempFormData = Object.assign({}, row) this.dialogVisible = true }, handleView(row) { this.dialogTitle = '查看' this.operationType = 'view' this.tempFormData = Object.assign({}, row) this.dialogVisible = true }, handleConfirm(formData) { // 根据操作类型处理数据 if (this.operationType === 'add') { this.tableData.push(formData) } else if (this.operationType === 'edit') { // 根据唯一标识找到原数据(这里假设用name作为唯一标识,实际中应用id) const index = this.tableData.findIndex(item => item.name === formData.name) if (index !== -1) { this.$set(this.tableData, index, formData) } } // 如果是查看,则不需要处理 // 关闭弹框(通过.sync修饰符,dialogVisible会自动变为false) } }, watch: { // 引用[3]的做法:当弹框关闭时,重置tempFormData,避免再次打开时数据残留 dialogVisible(val) { if (!val) { this.tempFormData = {} } } } } </script> ``` 子组件FormDialog.vue): ```vue <template> <el-dialog :title="title" :visible.sync="visibleInternal" @close="closeDialog"> <el-form :model="localFormData" ref="form" :rules="formRules"> <el-form-item v-for="(item, index) in formConfig" :key="index" :label="item.label" :prop="item.prop" :rules="[{ required: item.required, message: `请输入${item.label}`, trigger: 'blur' }]" > <!-- 根据type渲染不同的表单组件 --> <el-input v-if="item.type === 'input'" v-model="localFormData[item.prop]" :disabled="isView"></el-input> <!-- 这里可以扩展其他类型的表单组件,如select等 --> </el-form-item> </el-form> <div slot="footer"> <el-button @click="closeDialog">取消</el-button> <el-button type="primary" @click="submitForm" v-if="!isView">保存</el-button> </div> </el-dialog> </template> <script> export default { props: { title: String, formConfig: Array, formData: Object, visible: Boolean }, data() { return { visibleInternal: this.visible, // 内部维护的visible,通过sync同步到父组件 localFormData: { ...this.formData }, // 本地表单数据,避免直接修改prop formRules: {} // 动态生成规则(也可以不用,直接在表单项上写规则) } }, computed: { isView() { // 根据组件传递的title判断是否是查看模式(或者父组件可以额外传递一个mode属性) return this.title === '查看' } }, watch: { // 监听父组件传递的visible visible(val) { this.visibleInternal = val }, // 监听父组件传递的formData,当变化时更新本地数据 formData: { immediate: true, handler(val) { this.localFormData = { ...val } } } }, methods: { closeDialog() { this.$emit('update:visible', false) }, submitForm() { this.$refs.form.validate(valid => { if (valid) { // 表单验证通过,触发confirm事件,将本地表单数据传递给父组件 this.$emit('confirm', { ...this.localFormData }) this.closeDialog() } }) } } } </script> ``` 注意: -组件通过.sync修饰符同步visible状态,子组件通过emit('update:visible')来改变父组件的visible。 -组件在关闭弹框时(比如点击取消或右上角的关闭按钮)需要触发update:visible事件。 - 在查看模式下,保存按钮不显示(通过isView计算属性判断)。 - 表单验证规则:这里为了简单,直接在表单项上写了规则(必填),也可以根据formConfig动态生成规则对象。 以上代码实现了基本功能,但实际项目中可能需要根据具体需求调整(例如表单配置的细节、验证规则、唯一标识等)。 相关问题: §§相关问题§§ 1.Vue中如何使用.sync修饰符实现父子组件的双向绑定? 2. Element-UI表单验证有哪些常用的规则? 3. 如何动态生成Element-UI表单? 4.Vue中,为什么有时候需要深拷贝数据?有哪些深拷贝的方法? 5. 如何根据不同的操作类型(新增、修改、查看)控制表单的显示模式(可编辑、只读)? 以上问题可以帮助用户深入理解实现细节。 </think>以下是实现父组件包含新增、修改、查看按钮,点击弹出弹框保存数据到列表的完整方案(基于Vue.js + Element UI): ### 解决方案 #### 1.组件实现 ```vue <template> <div> <!-- 操作按钮区域 --> <el-button type="primary" @click="openDialog('add')">新增</el-button> <!-- 数据表格展示 --> <el-table :data="tableData" style="width: 100%"> <el-table-column prop="name" label="姓名" /> <el-table-column prop="age" label="年龄" /> <el-table-column label="操作"> <template v-slot="scope"> <el-button @click="openDialog('edit', scope.row)">修改</el-button> <el-button @click="openDialog('view', scope.row)">查看</el-button> </template> </el-table-column> </el-table> <!-- 弹框子组件 --> <form-dialog :title="dialogTitle" :form-config="formConfig" :form-data="formData" :visible.sync="dialogVisible" :mode="dialogMode" @confirm="handleConfirm" /> </div> </template> <script> import FormDialog from './FormDialog.vue'; export default { components: { FormDialog }, data() { return { tableData: [], // 存放表格数据 dialogVisible: false, // 弹框显示控制 dialogMode: 'add', // 弹框模式: add/edit/view dialogTitle: '', formData: {}, // 传递给表单数据 // 表单配置项(可抽离到单独文件) formConfig: [ { prop: 'name', label: '姓名', type: 'input', required: true }, { prop: 'age', label: '年龄', type: 'input-number', required: true } ] }; }, methods: { // 打开弹框 openDialog(mode, row = {}) { this.dialogMode = mode; this.formData = {...row}; // 深拷贝数据避免污染原数据 // 设置弹框标题 switch(mode) { case 'add': this.dialogTitle = '新增数据'; break; case 'edit': this.dialogTitle = '修改数据'; break; case 'view': this.dialogTitle = '查看详情'; break; } this.dialogVisible = true; }, // 处理确认事件 handleConfirm(formData) { if (this.dialogMode === 'add') { this.tableData.push({...formData}); } else if (this.dialogMode === 'edit') { // 根据ID更新数据(实际项目中用唯一标识) const index = this.tableData.findIndex(item => item.name === formData.name); if (index !== -1) { this.$set(this.tableData, index, {...formData}); } } this.dialogVisible = false; this.formData = {}; // 清空表单数据 } }, watch: { // 监听弹框关闭事件(解决引用[3]的数据残留问题) dialogVisible(val) { if (!val) this.formData = {}; } } }; </script> ``` #### 2.组件实现 (`FormDialog.vue`) ```vue <template> <el-dialog :title="title" :visible.sync="internalVisible" @close="closeDialog"> <el-form ref="formRef" :model="form" :rules="rules" label-width="120px"> <!-- 动态生成表单--> <el-form-item v-for="item in formConfig" :key="item.prop" :label="item.label" :prop="item.prop" :rules="item.required ? [{ required: true, message: `请输入${item.label}`, trigger: 'blur' }] : []"> <!-- 根据类型渲染不同组件 --> <el-input v-if="item.type === 'input'" v-model="form[item.prop]" :disabled="mode === 'view'" clearable /> <el-input-number v-else-if="item.type === 'input-number'" v-model="form[item.prop]" :disabled="mode === 'view'" controls-position="right" /> </el-form-item> </el-form> <div slot="footer"> <el-button @click="closeDialog">取消</el-button> <el-button type="primary" @click="submitForm" v-if="mode !== 'view'"> 保存 </el-button> </div> </el-dialog> </template> <script> export default { props: { title: String, formConfig: Array, formData: Object, visible: Boolean, mode: String // add/edit/view }, data() { return { form: {...this.formData}, // 表单数据副本 internalVisible: this.visible, rules: {} }; }, watch: { visible(val) { this.internalVisible = val; if (val) this.form = {...this.formData}; // 重置表单 }, formData: { handler(val) { this.form = {...val}; }, deep: true } }, methods: { closeDialog() { this.$emit('update:visible', false); }, submitForm() { this.$refs.formRef.validate(valid => { if (valid) { this.$emit('confirm', {...this.form}); // 触发确认事件 this.closeDialog(); } }); } } }; </script> ``` ### 关键实现说明 1. **父子组件通信**: - 使用`props`传递表单配置/数据 - 使用`sync`修饰符同步弹窗状态 - 通过`$emit('confirm')`提交数据 2. **表单处理**: - 动态生成表单元素(支持扩展) - 必填项验证(Element UI表单验证) - 深拷贝数据避免引用污染 3. **三种模式处理**: - **新增模式**:表单为空,保存时push到数组 - **修改模式**:传入行数据,保存时更新数组 - **查看模式**:禁用表单输入,不显示保存按钮 4. **数据残留问题解决**(参考引用[3]): ```javascript watch: { dialogVisible(val) { if (!val) this.formData = {}; // 关闭时清空数据 } } ``` ### 使用示例 1. 点击**新增**按钮:弹出空表单 2. 填写必填项后点击**保存**: - 数据添加到表格 - 弹框自动关闭 3. 点击表格行的**修改**: - 带出当前行数据 - 保存后更新表格对应项 4. 点击**查看**:显示数据表单禁用) [^1]: 引用自表格与表单结合的设计模式描述 [^2]: 参考弹框组件参数设计 [^3]: 采用数据清空策略解决残留问题
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值