vue封装form表单组件拒绝重复写form表单

目录
  • 前言
  • 核心思想:
  • 实现重点:
    • 表单双向绑定的方式有两种:
      • 1.使用v-model进行双向绑定
      • 2.使用v-model的语法糖
  • 配置项
    • 整体字段:
  • 效果浏览
  • 源码放送
    • 1. baseForm组件
    • 2. 父组件
    • 3. 配置项
    • 4. 添加或编辑

前言

在日常工作中,当需要处理的form表单很多时,我们没有必要一遍又一遍地重复写form表单,直接封装一个组件去处理就好。其实很早之前就有涉猎通过使用类似配置json方法写form表单的文章,虽然当时也没怎么认真看...我们前端组也是使用这种思想配置的。

然而,这个思想和方法很早就有出现过,并不怎么新颖,还望不喜勿喷...在此我封装了一个最最最基础的form表单,离我们前端组封装的组件差距还很大,有兴趣朋友们的可以继续往下完善。

核心思想:

  • 通过配置js文件的变量,使用vueis属性动态切换组件,默认显示的组件为el-input
  • 通过element的分栏和栅格属性,对form表单进行响应式布局
  • baseForm在组件初始化时,需要动态添加校验规则请求接口以及初始化form的部分值
  • 正统思想是对element组件的各个组件进行二次封装,然后通过is属性切换二次封装后的组件,在此不做过多描述,有兴趣的朋友可以自行研究
  • 更好的思想是将页面请求、搜索项、表格、分页封装到一起,形成一个整体,这也是我们前端小组目前的处理思路

实现重点:

  • 任何标签或者组件都可以通过vue的is属性来动态切换组件。本组件中使用div,将它的宽度设置为100%,使得element组件能够完全撑开。(使用vue内置组件component会与el-radio-group相冲突,因为它底层就是用component实现的)
  • 表单上添加validate-on-rule-change="false"属性,防止在表单初始化时就校验表单
  • 当为对象添加不存在的字段属性时,需要使用$set实现数据的响应式
  • 如果form表单中只有一个输入框,在输入框中按下回车会提交表单,刷新页面。为了阻止这一默认行为,需要在el-form标签上添加@submit.native.prevent
  • 使用lodash中的get方法获取对象的属性值,如果属性值不存在,可以给一个默认值
  • baseForm子组件中可以传一个form对象给父组件,那么添加或者编辑form对象,就都可以在父组件中进行。

表单双向绑定的方式有两种:

1.使用v-model进行双向绑定

<div
  v-else
  clearable
  style="width: 100%"
  type="daterange"
  range-separator="至"
  start-placeholder="开始日期"
  end-placeholder="结束日期"
  v-model="form[column.prop]"
  :label-width="get(column, 'size', column || defaultFormSize)"
  :disabled="get(column, 'disabled', false)"
  :is="get(column, 'type', 'el-input')"
>

2.使用v-model的语法糖

(`:value以及@input`)进行双向绑定

<div
  v-else
  clearable
  style="width: 100%"
  type="daterange"
  range-separator="至"
  start-placeholder="开始日期"
  end-placeholder="结束日期"
  :value="form[column.prop]"
  :label-width="get(column, 'size', column || defaultFormSize)"
  :disabled="get(column, 'disabled', false)"
  :is="get(column, 'type', 'el-input')"
  @input="input($event,column.prop)"
>
methods: {
    input(e,prop) {
      this.$set(this.form, prop, e)
    }
}

配置项

(本组件写得比较基础,目前仅支持element的五个常用组件):

整体字段:

formSize (表单中各element组件的整体大小)

column数组中每一个对象对应的字段(非请求接口):

  • label (表单label的名称)
  • span (这个表单项占据的份数,一行为24,默认为12)
  • labelWidth (这个表单项的label宽度,默认为90px)
  • labelHeight (这个表单项占据的高度,默认为50px)
  • slotName (插槽名)
  • prop (这个表单项绑定的属性名称)
  • size (这个表单项组件的大小,默认为small)
  • disabled (是否禁用这个表单项)
  • type (使用的element组件,默认为el-input)
  • dic (非接口请求的静态表单数据,使用{label以及value字段}表示的数组形式)

column数组中每一个对象对应的字段(请求接口):

  • url (接口的api地址)
  • requestParams (非必填项,需要额外传入的传参)
  • requestLabel (接口返回对应的id)
  • requestValue (接口返回对应的value)

效果浏览

源码放送

1. baseForm组件

<template>
  <el-form
    ref="form"
    :model="form"
    :rules="formRules"
    :size="get(option, 'formSize', defaultFormSize)"
    :validate-on-rule-change="false"
    @submit.native.prevent
  >
    <el-row :gutter="20" :span="24">
      <el-col
        v-for="column in formColumn"
        :key="column.label"
        :md="column.span || 12"
        :sm="12"
        :xs="24"
      >
        <el-form-item
          :label="`${column.label}:`"
          :prop="column.prop"
          :label-width="get(column, 'labelWidth', column.labelWidth || defaultLabelWidth)"
          :style="{
            height: get(column, 'labelHeight', column.labelHeight || defaultLabelHeight)
          }"
        >
          <slot
            v-if="column.slotName"
            :name="column.slotName"
            :form="form"
            :prop="column.prop"
            :value="form[column.prop]"
          ></slot>
          <div
            v-else
            clearable
            style="width: 100%"
            type="daterange"
            range-separator="-"
            start-placeholder="开始日期"
            end-placeholder="结束日期"
            v-model="form[column.prop]"
            :placeholder="getPlaceholder(column.type, column.label)"
            :label-width="get(column, 'size', column || defaultFormSize)"
            :disabled="get(column, 'disabled', false)"
            :is="get(column, 'type', 'el-input')"
          >
            <template v-if="column.type == 'el-select'">
              <el-option
                v-for="item in column.dic"
                :key="item.value"
                :label="item.label"
                :value="item.value"
              >
              </el-option>
            </template>
            <template v-if="column.type == 'el-radio-group'">
              <el-radio
                v-for="item in column.dic"
                :key="item.value"
                :label="item.label"
              >
                {{ item.value }}
              </el-radio>
            </template>
            <template v-if="column.type == 'el-checkbox-group'">
              <el-checkbox
                v-for="item in column.dic"
                :key="item.label"
                :label="item.value"
              >
                {{ item.label }}
              </el-checkbox>
            </template>
          </div>
        </el-form-item>
      </el-col>
    </el-row>
  </el-form>
</template>
<script>
import get from 'lodash/get'
import request from '@/service/request'
export default {
  props: {
    option: {
      type: Object,
      default: () => {}
    },
    form: {
      type: Object,
      default: () => {}
    }
  },
  data() {
    return {
      formRules: {},
      defaultFormSize: 'small',
      defaultLabelWidth: '90px',
      defaultLabelHeight: '50px'
    }
  },
  computed: {
    formColumn() {
      return this.option.column
    }
  },
  created() {
    this.initRules()
    this.initRequest()
    this.initValue()
  },
  methods: {
    get,
    getPlaceholder(type, label) {
      return type == 'el-select' ? `请选择${label}` : `请输入${label}`
    },
    initRequest() {
      if (!Array.isArray(this.formColumn)) return
      // 根据实际请求接口地址的前缀来判断
      const urls = this.formColumn?.filter((item) => item.url && item.url.indexOf('/emes') == 0) || []
      urls.forEach(async (item) => {
        const data = { page: { pageIndex: 1, pageSize: 0 }, ...item.requestParams }
        const { detail } = await request({
          url: item.url,
          method: 'post',
          data
        }) || []
        const finalResult = detail.map((result) => ({
          label: result[item.requestLabel],
          value: result[item.requestValue]
        }))
        this.$set(item, 'dic', finalResult)
      })
    },
    initRules() {
      if (!Array.isArray(this.formColumn)) return
      this.formColumn?.forEach((item) => {
        if (item.rules) {
          item.rules.map((rule, index) => {
            if (rule.required) {
              item.rules.splice(index, 1, {
                message: ['el-radio-group', 'el-checkbox-group'].includes(item.type) ? `${item.label}必选` : `${item.label}必填`,
                ...rule
              })
            }
          })
          this.$set(this.formRules, item.prop, item.rules)
        }
      })
    },
    initValue() {
      const selectList = this.formColumn.filter((item) => ['el-radio-group', 'el-checkbox-group'].includes(item.type))
      selectList.forEach((item) => {
        this.$set(this.form, item.prop, item.type == 'el-radio-group' ? item.dic[0].label : [item.dic[0].value])
      })
    }
  }
}
</script>

2. 父组件

<template>
  <div class="app-container">
    <myForm :option="option" :form="form">
      <template #usageSlot="{form, prop}">
        <el-input
          size="small"
          placeholder="请输入插槽使用"
          v-model="form[prop]"
          clearable
        >
        </el-input>
      </template>
    </myForm>
  </div>
</template>
<script>
import { option } from './const.js'
export default {
  data() {
    return {
      option,
      form: {}
    }
  }
}
</script>

3. 配置项

export const option = {
  column: [
    {
      label: '姓名',
      prop: 'name',
      span: 8,
      rules: [
        {
          required: true
        }
      ]
    },
    {
      label: '职业',
      prop: 'job',
      type: 'el-select',
      span: 8,
      dic: [
        {
          label: '教师',
          value: 0
        },
        {
          label: '程序猿',
          value: 1
        },
        {
          label: '作家',
          value: 2
        }
      ],
      rules: [
        {
          required: true
        }
      ]
    },
    {
      label: '性别',
      prop: 'sex',
      span: 8,
      type: 'el-radio-group',
      dic: [
        {
          label: 0,
          value: '男'
        },
        {
          label: 1,
          value: '女'
        }
      ],
      rules: [
        {
          required: true
        }
      ]
    },
    {
      label: '城市',
      prop: 'city',
      type: 'el-checkbox-group',
      span: 8,
      dic: [
        {
          label: '仙桃',
          value: 0
        },
        {
          label: '泉州',
          value: 1
        },
        {
          label: '武汉',
          value: 2
        }
      ],
      rules: [
        {
          required: true
        }
      ]
    },
    {
      label: '出生日期',
      prop: 'data',
      type: 'el-date-picker',
      span: 8,
      rules: [
        {
          required: true
        }
      ]
    },
    {
      label: '测试',
      prop: 'test',
      type: 'el-select',
      span: 8,
      url:'/emes/factoryOrderService/warehouse/list',
      requestLabel: 'warehouseName',
      requestValue: 'id',
      rules: [
        {
          required: true
        }
      ]
    },
    {
      label: '插槽使用',
      prop: 'usage',
      slotName: 'usageSlot',
      span: 8,
      rules: [
        {
          required: true
        }
      ]
    }
  ]
}

4. 添加或编辑

  • 添加: 如果是添加状态,直接在父组件中引入就好。在点击确定按钮时,拿到子组件的ref并进行表单校验。校验通过后,使用后端定义好的接口进行传参;
  • 编辑: 如果是编辑状态,则需要在父组件页面初始化时,将后端返回的数据使用$set进行初始赋值,其余操作同添加状态。

以上就是vue封装form表单组件拒绝重复写form表单的详细内容,更多关于vue封装form表单组件的资料请关注我们其它相关文章!

(0)

相关推荐

  • vue项目中form data形式传参方式

    目录 vue中form data形式传参 vue文件提交 FormData方式 前驱知识 FormDate FormData的使用 URL 实操 vue中form data形式传参 vue项目中form data形式传参,需要在headers中添加如下代码 headers: { 'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8' } 接口请求如下: export function subFaq (params) {

  • vue上传文件formData上传的解决全流程

    目录 vue上传文件formData上传解决 axios实例的配置关键配置 整体流程解决跨域上传文件跨域上传FormData格式等问题 vue 批量上传文件 简单说明一个实现思路 vue上传文件formData上传解决 axios实例的配置关键配置 FormData 上传文件类型有变项目中封装的公共方法需要更改头部请求信息设置为设为 “multipart/form-data” 整体流程解决跨域上传文件跨域上传FormData格式等问题 vue中Dom结构 <input type="file

  • 详解Vue里循环form表单项实例

    有的时候我们可能会遇到这种需求,用户点击某个按钮就可以增加一个同样的表单出来,点击一次增加一次.然后要用到深拷贝,Vue.js+ElementUI等等.效果大概如下,就是一个表单有下拉框和两个输入框,现在点击"添加表单"按钮之后就会多一个表单出来,点击"提交表单"后就同时提交两个表单的value值. 代码如下: <template> <div> <div style="margin: 10px 0"> <

  • vue form表单使用resetFields函数出现的问题

    目录 form表单使用resetFields函数的问题 resetFields失效,死活不起作用的解决 分析 解决办法 form表单使用resetFields函数的问题 今天想使用新增和删除使用一个dialog,这就涉及到点击添加按钮需要清空表单数据的问题,我第一个想到的是调用resetFields方法. 我直接写了之后是这样的 this.$refs[formName].resetFields() 结果直接报错,于是我就上网上百度了后,发现改成这样就不报错了. this.$nextTick(fu

  • vue form表单post请求结合Servlet实现文件上传功能

    前端测试页面代码: <template> <div> <input type="file" name="file" @change="change($event)"> </div> </template> <script> export default { created(){ this.path = this.$route.query; for (let i in this

  • vue中项目如何提交form格式数据的表单

    目录 vue提交form格式数据的表单 先将数据处理 数据上传 封装文件 vue form表单最简写法 vue提交form格式数据的表单 先将数据处理 let formData = new FormData(); for(let key in this.telForm){      formData.append(key,this.telForm[key]); } 数据上传 //采用封装的post方法上传 this.postRequest('web/login/mobile',formData)

  • vue封装form表单组件拒绝重复写form表单

    目录 前言 核心思想: 实现重点: 表单双向绑定的方式有两种: 1.使用v-model进行双向绑定 2.使用v-model的语法糖 配置项 整体字段: 效果浏览 源码放送 1. baseForm组件 2. 父组件 3. 配置项 4. 添加或编辑 前言 在日常工作中,当需要处理的form表单很多时,我们没有必要一遍又一遍地重复写form表单,直接封装一个组件去处理就好.其实很早之前就有涉猎通过使用类似配置json方法写form表单的文章,虽然当时也没怎么认真看...我们前端组也是使用这种思想配置的

  • 微信小程序form表单组件示例代码

    表单,将组件内的用户输入的<switch/> <input/> <checkbox/> <slider/> <radio/> <picker/> 提交. 当点击<form/>表单中 formType 为 submit 的<button/>组件时,会将表单组件中的 value 值进行提交,需要在表单组件中加上 name 来作为 key. 属性名 类型 说明 report-submit Boolean 是否返回fo

  • 解决layui的使用以及针对select、radio等表单组件不显示的问题

    layui是国内一款界面比较整洁大方的ui框架,里面封装了很多前端开发常用的组件,通常我们直接复制代码过去就可以实现效果,可以提高我们的开发效率. 使用步骤: 1.下载layui:https://www.layui.com 2.将layui文件夹复制到自己的项目里: 3.在html页面引入相关的css和js: <link rel="stylesheet" type="text/css" href="/layui/css/layui.css"

  • jQuery Mobile框架中的表单组件基础使用教程

    一.表单组件基础 1.组件简介 jQuery Mobile 中的表单组件是基于标准 HTML ,然后在此基础上增强样式,因此即使浏览器不支持 jQuery Mobile 表单仍可正常使用.需要注意的是, jQuery Mobile 会把表单元素增强为触摸设备很容易使用的形式,因此对于 iphone/ipad 与 Android 使用 Web 表单将会变得非常方便. jQuery Mobile 的表单组件有以下几种: (1)文本输入框, type="text" 标记的 input 元素会

  • YII2.0之Activeform表单组件用法实例

    本文实例讲述了YII2.0之Activeform表单组件用法.分享给大家供大家参考,具体如下: Activeform 文本框:textInput(); 密码框:passwordInput(); 单选框:radio(),radioList(); 复选框:checkbox(),checkboxList(); 下拉框:dropDownList(); 隐藏域:hiddenInput(); 文本域:textarea(['rows'=>3]); 文件上传:fileInput(); 提交按钮:submitBu

  • Bootstrap表单组件教程详解

    表单常见的元素主要包括:文本输入框.下拉选择框.单选框.复选框.文本域.按钮等.下面是不同的bootstrap版本: LESS: forms.less SASS: _forms.scss bootstrap仅对表单内的fieldset.legend.label标签进行了定制 fieldset { min-width: 0; padding: 0; margin: 0; border: 0; } legend { display: block; width: 100%; padding: 0; m

  • 关于element的表单组件整理笔记

    element表单及代码的展示 详细可以看element表单官方网址 结构.功能分析 通过介绍以及源码可以发现,表单是具有收集.校验.提交数据三个功能的. 他的基本结构如下: <el-form :model="ruleForm" :rules="rules" ref="ruleForm"> <el-form-item label="账号" prop="name"> <el-in

  • 微信小程序常用表单组件的使用详解

    目录 1.常用表单组件 1.1button 1.2checkbox 1.3input 1.4label 1.5form 1.6radio 1.7slider 1.8switch 1.9textarea 2.实训小案例–问卷调查 1.常用表单组件 1.1 button <button>为按钮组件,是常用的表单组件之一,用于事件的触发以及表单的提交.其属性表如下所示. 代码示例: <view class="demo-box"> <view class=&quo

  • 为JQuery EasyUI 表单组件增加焦点切换功能的方法

    1.背景说明 在使用 JQuery  EasyUI 各表单组件时,实际客户端页面元素是由 JQuery EasyUI 生成的,元素的焦点切换,虽然 Tab 键可以正常用,但顺序控制属性 tabindex 不起作用,因为页面看到的元素,是生成的,没有tabindex 属性,而真实的元素被隐藏了.本文通过一个自定义函数,实现Tab 和 回车键的焦点切换功能. 2.函数定义 通过捕获窗口按件,对回车和Tab键进行了热点切换处理.先根据当前焦点,获取需要tabindex 属性,加1后为下一焦点无素的属性

  • 微信小程序防止多次点击跳转和防止表单组件输入内容多次验证功能(函数防抖)

    一.函数节流(throttle) **函数节流:一个函数执行一次后,只有大于设定的执行周期后才会执行第二次**.有个需要频繁触发函数,出于优化性能角度,在规定时间内,只让函数触发的第一次生效,后面不生效. ### 1.如何实现 其原理是用时间戳来判断是否已到回调该执行时间,记录上次执行的时间戳,然后每次触发 scroll 事件执行回调,回调中判断当前时间戳距离上次执行时间戳的间隔是否已经到达 规定时间段,如果是,则执行,并更新上次执行的时间戳,如此循环: function throttle(fn

随机推荐