当前位置 : 主页 > 网络编程 > JavaScript >

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

来源:互联网 收集:自由互联 发布时间:2023-02-08
目录 前言 核心思想: 实现重点: 表单双向绑定的方式有两种: 1.使用v-model进行双向绑定 2.使用v-model的语法糖 配置项 整体字段: 效果浏览 源码放送 1. baseForm组件 2. 父组件 3. 配置项
目录
  • 前言
  • 核心思想:
  • 实现重点:
    • 表单双向绑定的方式有两种: 
      • 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表单组件的资料请关注易盾网络其它相关文章!

              网友评论