vue form表单的封装--使用的是elementUI

该组件是一个动态生成表单的Vue组件,通过传入formList参数和插槽的方式实现表单项的定制化渲染

组件的封装

HCommonFormItem

根据传入的"formList"参数生成一组表单项,并通过插槽(slot)将表单项渲染出来。

  1. 在components下创建commonFormItem文件夹,在该文件夹下创建index.vue文件
<template>
  <div class="block-message-box">
    <template v-for="(item, index) in formList">

     <template v-if="item.display===undefined || item.display===true">
       <HCommonBlock :title="item.title" :key="index" class="form-box" :hasBorder="item.hasBorder">
         <template slot="title-button" v-if="item.slot">
           <slot :name="'title-button-'+ item.key"></slot>
         </template>
         <h-common-form
             @label-click="handleLabelClick"
             :hideRequiredAsterisk="hideRequiredAsterisk"
             :hasLabelImg="hasLabelImg"
             :ref="'myForm' + index + item.key"
             :formRules="item.formValidate"
             :formData="item.purchasingInformation"
             :fileList="item.fileList"
             @upload="handleImgInfo"
             @uploadLoad="handleImgLoad"
             @check="handleChecked"
             @detail="handleLookDetail"
             @addTableRow="handleAddTableRow"
             @tableEvent="handleTableEvent"
             @tableSelect="handleSelectTable"
             @address="handleSelectAddress"
             @operation="handleOperation"
             @input-blur="handleInputBlur"
             @select-change="handleSelectChange"
             @radio="handleRadioGroupChange"
             @querySearch="handleQuerySearch"
             @remote-method="handleRemoteMethod"
             @autocomplete-change="handleSelectAutocomplete"
         >
           <template v-for="item in slotNameList" >
             <div :slot="item" :key="item">
               <slot :name="item" ></slot>
             </div>
           </template>

         </h-common-form>

       </HCommonBlock>
     </template>
      
    </template>
    <slot></slot>
  </div>
</template>
<script>
import HCommonForm from '@/components/commonForm'
import HCommonBlock from '@/components/commonBlock'
export default {
  name: 'HCommonFormItem',
  props: {
    formList: {
      type: Array,
      default() {
        return []
      }
    },
    slotNameList:{
      type: Array,
      default() {
        return []
      }
    },
    hasLabelImg: {
      type: Boolean,
      default: true
    },
    hideRequiredAsterisk: {
      type: Boolean,
      default: false
    },
  },
  components: {
    HCommonForm,
    HCommonBlock
  },

  methods: {
    handleLabelClick(info,val) {
      this.$emit('label-click',info,val)
    },
    handleOperation (operationType, info, column) {
      this.$emit('operation', operationType, info, column)
    },
    handleInputBlur(val, field, parentField) {
      this.$emit('input-blur', val, field, parentField)
    },
    handleSelectChange (val, field, parentField,info) {
      this.$emit('select-change', val, field, parentField,info)
    },
    handleImgInfo (val, file, info) {
      this.$emit('upload', val, file, info)
    },
    handleImgLoad (val, file, info) {
      this.$emit('uploadLoad', val, file, info)
    },
    handleChecked (value, currtentVal) {
      this.$emit('check', value, currtentVal)
    },
    handleLookDetail (currtentVal) {
      this.$emit('detail', currtentVal)
    },
    handleAddTableRow (currtentVal) {
      this.$emit('addTableRow', currtentVal)
    },
    handleTableEvent (event, $index, info) {
      this.$emit('tableEvent', event, $index, info)
    },
    handleSelectTable (currentVal, currentLabel, column, $index, info) {
      this.$emit('tableSelect', currentVal, currentLabel, column, $index, info)
    },
    handleSelectAddress (val, label, row,districtLevel) {
      this.$emit('address', val, label, row,districtLevel)
    },
    handleRadioGroupChange (val, row) {
      this.$emit('radio', val, row)
    },
    handleQuerySearch(queryString, cb, info) {
      this.$emit('querySearch',queryString, cb, info)
    },
    handleSelectAutocomplete(val, field, parentField, info) {
      this.$emit('autocomplete-change', val, field, parentField, info)
    },
    handleRemoteMethod(val, field, parentField, info) {
      this.$emit("remote-method", val, field, parentField, info);
    },
  }
}
</script>
<style lang="scss" scoped>
.block-message-box {
  height: fit-content;
  width: 100%;
}
</style>

  • 组件中使用了两个子组件:
    • HCommonForm: 这是一个自定义的表单组件,用于展示和处理表单数据。
    • HCommonBlock: 这是一个自定义的块级元素组件,用于包裹表单项,并提供标题和边框等样式。

    组件的模板部分使用了Vue的指令和循环语法,通过v-for指令遍历formList数组,生成多个HcommonBlock组件。每个HcommonBlock组件中包含了一个HCommonForm组件,根据item对象的属性设置HCommonForm组件的props。

HCommonBlock

该组件是实现一个可定制样式的块级容器组件,可以根据需要显示标题和内容,并支持选择不同的选项卡。

  1. 在components下创建HCommonBlock文件夹,在该文件夹下创建index.vue文件
<template>
  <div
    class="block-box"
    :class="[
      noPadding ? 'block-box-no-padding' : '',
      hasBorder ? 'block-box-border' : '',
      noMargin ? 'block-box-no-margin' : ''
    ]"
  >
    <div class="block-title" v-if="title">
      {{ title }}
    </div>
    <div v-else-if="blockTitleList.length > 0" class="block-title-box">
      <div
        :class="
          chooseTab == index ? 'block-title' : 'block-title block-title-no-bg '
        "
        v-for="(item, index) in blockTitleList"
        :key="index"
        @click="handleChooseTab(item, index)"
      >
        {{ item.name }}
      </div>
    </div>
    <slot name="title-button"></slot>
    <div
      class="block-content-box"
      :class="[
        noOverFlow ? 'block-content-box-overflow-no' : '',
        isScroll ? 'common-scroll' : ''
      ]"
    >
      <slot></slot>
    </div>
  </div>
</template>

<script>
export default {
  data () {
    return {
      chooseTab: 0
    }
  },
  props: {
    title: String,
    noPadding: {
      type: Boolean,
      default: false
    },
    hasBorder: {
      type: Boolean,
      default: false
    },
    noMargin: {
      type: Boolean,
      default: false
    },
    blockTitleList: {
      type: Array,
      default () {
        return []
      }
    },
    noOverFlow: {
      type: Boolean,
      default: false
    },
    isScroll: {
      type: Boolean,
      default: false
    }
  },
  methods: {
    handleChooseTab (val, index) {
      if (this.chooseTab == index) return
      this.chooseTab = index
      this.$emit('choose', val)
    }
  },
  components: {}
}
</script>

<style lang="scss" scoped>
.block-box {
  width: calc(100% - 3.54167vw);
  height: fit-content;
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-top: 60px;
  position: relative;
  &:first-child {
    margin-top: 0;
  }
  &:last-child {
    margin-bottom: 60px;
  }
  &.block-box-no-padding {
    .block-content-box {
      padding: 0;
      background: transparent;
    }
  }
  &.block-box-no-margin {
    margin-bottom: 0;
  }
  &.block-box-border {
    .block-content-box {
      border-radius: 10px;
      border: 1px solid #000000;
      background: transparent;
      overflow: hidden;
      &-overflow-no {
        overflow: inherit;
      }
      &.common-scroll {
        overflow-y: auto;
      }
    }
  }
  .block-title-box {
    display: flex;
    .block-title {
      cursor: pointer;
      border-radius: 0;
      &:first-child {
        border-radius: 20px 0 0px 0px;
      }
      &:last-child {
        border-radius: 0 20px 0px 0px;
      }
    }
  }

  .block-title {
    min-width: 360px;
    width: fit-content;
    height: 65px;
    background: #808c87;
    border-radius: 20px 20px 0px 0px;
    font-size: 32px;
    font-weight: 600;
    color: #ffffff;
    display: flex;
    align-items: center;
    justify-content: center;
    &.block-title-no-bg {
      background: #dee2e0;
    }
  }
  .block-content-box {
    min-height: 80px;
    height: fit-content;
    background: #ffffff;
    border-radius: 10px;
    width: 100%;
    padding: 40px 40px 35px 40px;
    box-sizing: border-box;
  }
}
</style>

  • 模板中的
    是整个容器的最外层元素,它使用flex布局,并且垂直居中对齐。它可以根据传入的props动态添加不同的类名,从而实现样式的变化。
  • 中会显示标题,如果没有传入标题,则会根据blockTitleList数组中的数据渲染多个标题选项,点击选项会触发handleChooseTab方法,并通过$emit将选中的选项值传递给父组件。
  • 用于显示内容,可以根据传入的props进行样式控制,如是否有边框、是否可滚动等。
HCommonForm

该组件实现了一个灵活配置的表单组件,可以根据传入的数据动态生成表单项,并支持各种类型的输入框、下拉框、日期选择等功能。同时也包含了上传文件、显示图片、查看PDF等功能。

  1. 在components下创建HCommonForm文件夹,在该文件夹下创建index.vue文件
<template>
  <div
    class="h-common-form"
  >
    <el-form
      ref="rulesForm"
      :inline="false"
      :model="formData.form"
      label-position="right"
      :rules="formRules"
      :hide-required-asterisk="hideRequiredAsterisk"
      :class="[
      !hideRequiredAsterisk&&hasLabelImg?'form-label-Required':'',
      !hideRequiredAsterisk && !hasLabelImg ? 'form-label-Required-no-image' : ''
      ]"
    >
      <el-form-item
        v-for="(info, formI) in formData.formLabel"
        :key="info.field + formI"
        :prop="info.field"
        :style="[
          {
            width: !info.widthIsNotNum
              ? getWidth(info.itemWidth)
              : info.itemWidth
          },
          {
            marginRight: !info.widthIsNotNum
              ? getWidth(info.marginRight)
              : info.marginRight
          }
        ]"
        v-show="info.display"
        :rules="info.display ? formRules[info.field] : [{ required: false }]"
        :class="[
          { 'common-form-item-font-weight': info.wordBold },
          { 'common-form-item-font-color': info.wordColor },
          { 'common-form-item-font-color-red': info.wordColorRed },
          { 'common-form-item-algin-right': info.align == 'right' },
          { 'common-form-item-algin-center': info.align == 'center' },
          { 'common-form-item-margin-bottom': info.marginBottom },
          { 'common-form-item-margin-bottom-middle': info.marginBottomMiddle },
          { 'common-form-item-content-width-fit': info.widthFit },
          {
            'common-form-item-content-height':
              (info.type == 'textarea' && !info.isSpecial) ||
              info.type == 'upload'
          },
          {
            'common-form-item-content-height-no':
              info.type == 'textarea' && info.isSpecial
          },
          { 'common-form-item-no-label': info.noLabel },
          {
            'common-form-select-placeholder-right':
              info.selectPlaceholderAlign == 'right'
          },
          {
            'common-form-disabled-label-algin-right':
              info.disabledAlign == 'right'
          },
          {
            'common-form-disabled-label-algin-center':
              info.disabledAlign == 'center'
          },
          {
            'common-form-input-placeholder-right':
              info.inputPlaceHolderAlign == 'right'
          },
          { 'common-form-input-label-right': info.inputAlign == 'right' },
          { 'common-form-input-label-center': info.inputAlign == 'center' },
          { 'common-form-item-border-line': info.type == 'border' },
          { 'common-form-item-disabled': info.disabled }
        ]"
      >
        <template v-if="info.isUseSlot">
          <div slot="label" class="common-form-label">
            <img
                :style="[
              { width: info.iconWidth ? getWidth(info.iconWidth) : '' },
              {
                marginRight: info.iconMarginRight
                  ? getWidth(info.iconMarginRight)
                  : ''
              },
              { height: info.iconHeight ? getWidth(info.iconHeight) : '' }
            ]"
                :src="require(`@/assets/harley/${info.icon}.png`)"
                v-if="info.icon"
            />
          </div>
          <slot :name="info.field"></slot>

        </template>
        <template v-else>
          <span slot="label" class="common-form-label">
            <img
              :style="[
                { width: info.iconWidth ? getWidth(info.iconWidth) : '' },
                {
                  marginRight: info.iconMarginRight
                    ? getWidth(info.iconMarginRight)
                    : ''
                },
                { height: info.iconHeight ? getWidth(info.iconHeight) : '' }
              ]"
              :src="require(`@/assets/harley/${info.icon}.png`)"
              v-if="info.icon"
            />
            <span>{{ info.label ? info.label + ":" : "" }}</span>
          </span>
          <div v-if="info.type == 'border'" class="common-form-underLine"></div>
          <div
            v-if="
              info.disabled &&
                judgeTypeVisi(info.type) &&
                info.dataType != 'multiple'
            "
            class="common-form-content-label"
            :class="[
              {
                'common-form-content-label-block-bg-normal': info.isBlockBg
              },
              {
                'common-form-content-label-block-bg-abnormal':
                  info.isBlockBg && Number(formData.form[info.field]) <= 0
              }
            ]"
          >
            <common-tooltip
              :twoLine="!(info.widthIsNotNum && info.itemWidth == '100%')"
              :disabled="false"
              :isClick="info.isClick"
              :twoRow="
                info.widthIsNotNum &&
                  info.itemWidth == '100%' &&
                  info.type == 'textarea'
              "
              :className="'ellipsisName' + info.field"
              :refName="'toolitipName' + info.field"
              :content="formData.form[info.field].toString()"
              :isCopy="info.isCopy"
              @label-click="handleLabelClick(info,formData.form[info.field])"
            >
            </common-tooltip>

            <span
              v-if="
                info.unit &&
                  formData.form[info.field] !== '' &&
                  formData.form[info.field] !== undefined
              "
              class="unit"
              >{{ info.unit }}</span
            >
          </div>

          <el-input
            v-if="
              (info.type === 'text' ||
                info.type === 'password' ||
                info.type === 'textarea') &&
                !info.disabled &&
                info.dataType != 'multiple'
            "
            :type="info.type"
            :placeholder="info.placeholder || ''"
            :disabled="info.disabled"
            v-model.trim="formData.form[info.field]"
            @blur="
              handleInputBlur(
                formData.form[info.field],
                info.field,
                info.parentField
              )
            "
            ><i
              v-show="
                formData.form[info.field] &&
                  formData.form[info.field].length > 0 &&
                  info.passwordType == 'password'
              "
              slot="suffix"
              :class="[
                info.type == 'password' ? 'el-icon-minus' : 'el-icon-view'
              ]"
              autocomplete="auto"
              @click="$emit('passwordClick', info)"
            />
            <template slot="append" v-if="info.unit">{{ info.unit }}</template>
          </el-input>
          <div
            class="is-multiple-textarea"
            v-if="info.type === 'textarea' && info.dataType == 'multiple'"
          >
            <div
              v-for="(textareaItem, textareaIndex) in formData.form[info.field]"
              :key="textareaIndex + 'textarea'"
              class="textarea-item"
            >
              <el-input
                :type="info.type"
                :autosize="{ minRows: 2, maxRows: 10 }"
                :placeholder="info.placeholder || ''"
                :disabled="formData.form[info.field][textareaIndex].disabled || info.disabled"
                v-model="formData.form[info.field][textareaIndex].value"
              >
              </el-input>
            </div>

            <div
              v-if="info.dataType == 'multiple' && !info.disabled"
              class="add-remark"
              @click="handleAddRemark(formData.form[info.field])"
            >
              <img src="@/assets/harley/add.png" alt="" />
            </div>
          </div>
          <el-autocomplete
            v-if="info.type === 'autocomplete'"
            class="inline-input"
            :fetch-suggestions="
              (queryString, cb) => handleQuerySearch(queryString, cb, info)
            "
            :value-key="info.prop.value"
            :placeholder="info.placeholder || ''"
            :disabled="info.disabled"
            v-model.trim="formData.form[info.field]"
            @select="
              handleSelectAutocomplete(
                $event,
                info.field,
                info.parentField,
                info
              )
            "
            @change="
              handleSelectAutocomplete(
                $event,
                info.field,
                info.parentField,
                info
              )
            "
            :clearable="info.clearable"
            :popper-class="
              $store.getters.windowWidth <= 1024
                ? 'el-autocomplete-suggestion-width'
                : ''
            "
          >
            <template slot-scope="{ item }">
              <div class="name">{{ item[info.prop.label] }}</div>
            </template>
          </el-autocomplete>
          <h-date-picker
            v-if="info.type === 'date'"
            v-model="formData.form[info.field]"
            :clearable="info.clearable"
            :start-placeholder="
              info.startPlaceholder ? info.startPlaceholder : ''
            "
            :end-placeholder="info.endPlaceholder ? info.endPlaceholder : ''"
            :placeholder="info.placeholder || ''"
            :type="info.dateType"
            :value-format="info.valueFormat"
            :prefix-icon="'el-icon-caret-bottom'"
            :disabled="info.disabled"
            :keyValue="info.field + info.dateType"
            :picker-options="info.pickerOptions"
          ></h-date-picker>
          <h-select
            v-if="info.type === 'select' && !info.multiple"
            v-model="formData.form[info.field]"
            :ref="info.field"
            :data-source="info.options"
            :clearable="info.clearable === undefined ? true : info.clearable"
            :align="info.align"
            :collapseTags="info.collapseTags"
            :placeholder="info.placeholder || ''"
            :disabled="info.disabled"
            :allowCreate="info.allowCreate"
            :props="info.props"
            :remote="info.remote"
            :reserve-keyword="info.reserveKeyword"
            :remote-method="
              $event =>
                handleRemoteMethod($event, info.field, info.parentField, info)
            "
            :multiple="info.multiple"
            :filterable="info.filterable"
            @change="
              handleSelectChange($event, info.field, info.parentField, info)
            "
            :selectSpecial="info.selectSpecial"
          >
            <template slot="option-content" v-if="info.multiple">
              <el-checkbox-group v-model="formData.form[info.field]">
                <el-option
                  v-for="(item, index) in info.options"
                  :key="index"
                  :label="
                    info.prop && info.prop.label
                      ? item[info.prop.label]
                      : item.name
                  "
                  :value="
                    info.prop && info.prop.value
                      ? item[info.prop.value]
                      : item.id
                  "
                >
                  <el-checkbox
                    style="pointer-events: none"
                    :label="
                      info.prop && info.prop.value
                        ? item[info.prop.value]
                        : item.id
                    "
                    >{{
                      info.prop && info.prop.label
                        ? item[info.prop.label]
                        : item.name
                    }}
                  </el-checkbox>
                </el-option>
              </el-checkbox-group>
            </template>
          </h-select>
          <HSelectMultiple
            v-if="info.type === 'select' && info.multiple"
            :select-info="info"
            v-model="formData.form[info.field]"
            :filter-val="info.filterVal"
            @input-search="dropDownSearchTop($event, info)"
            @change="changeSelect($event, info.field, info.parentField, info)"
          >
          </HSelectMultiple>
          <address-cascader
            v-if="info.type === 'district'"
            :selectedVal="formData.form[info.field]"
            @getVal="
              (districtVal, districtLabel, districtLevel) =>
                setDistrictId(districtVal, districtLabel, info, districtLevel)
            "
            :isNeedFilter="info.isNeedFilter"
            :checkStrictly="info.checkStrictly"
            ref="addressCascader"
            :disabled="info.disabled"
          ></address-cascader>
          <h-radio-group
            v-if="info.type === 'radio' && info.display"
            :value="formData.form[info.field]"
            :disabled="info.disabled"
            :data-source="info.options"
            :props="info.props"
            @change="val => handleRadioGroupChange(val, info)"
          >
          </h-radio-group>
          
          <div
            v-if="info.type === 'upload'"
            class="upload-box"
          >
            <el-upload
              :ref="info.isDynamic ? 'upload' + info.field : 'upload'"
              :before-upload="
                e => {
                  beforeAvatarUpload(e, info);
                }
              "
              action="#"
              class="file-upload-box"
              :disabled="info.disabled"
              :multiple="info.multiple"
              :limit="info.limit"
              :headers="header"
              :on-exceed="
                e => {
                  handleExceed(e, info.limit, info);
                }
              "
              :http-request="
                (e, file) => {
                  handleAvatarSuccess(e, file, info);
                }
              "
              :file-list="info.isDynamic ? info.fileList : fileList"
            >
              <el-button
                :disabled="info.disabled"
                class="specailButton"
                :loading="imgLoading.indexOf(info.field) != -1"
                type="info"
                >上传
              </el-button>
              <template slot="file" slot-scope="{ file }">
                <div class="upload-file-name-box">
                  <div class="file-icon">
                    <img src="@/assets/harley/uploadImgIcon.png" />
                  </div>
                  <div class="file-info">
                    <span class="el-upload-list__item-name">{{
                      file.name
                    }}</span>

                    <div class="file-btn-box">
                      <el-button
                        class="file-btn danger"
                        @click="handleRemove(file, info)"
                        v-if="!info.disabled"
                      >
                        删除
                      </el-button>
                      <el-button
                        class="file-btn"
                        @click="handleDownLoadEdit(file, info)"
                        v-if="file.url"
                        :loading="
                          downLoading.indexOf(file.code) != -1 ||
                            downLoading.indexOf(file.uid) != -1
                        "
                      >
                        下载
                      </el-button>
                      <el-button
                        class="file-btn"
                        @click="handleEditPreview(file, info)"
                        v-if="commonFunc.judgeImg(file.name) && file.url"
                        :loading="
                          canEditImgLoading.indexOf(file.code) != -1 ||
                            canEditImgLoading.indexOf(file.uid) != -1
                        "
                      >
                        查看
                      </el-button>
                      <el-button
                        class="file-btn"
                        @click="handleEditPreviewPdf(file, info)"
                        v-if="commonFunc.judgePdf(file.name) && file.url"
                        :loading="
                          openPdfEditLoading.indexOf(file.uid) != -1 ||
                            openPdfEditLoading.indexOf(file.code) != -1
                        "
                      >
                        查看
                      </el-button>
                    </div>

                    <viewer :images="formImgData" ref="viewer" v-show="false">
                      <img
                        :src="itemImg.url"
                        :alt="itemImg.name ? itemImg.name : ''"
                        v-for="(itemImg, indexi) in formImgData"
                        :key="indexi"
                        :class="
                          itemImg.code
                            ? 'viewerImg' + itemImg.code + info.field
                            : 'viewerImg' + itemImg.uid + info.field
                        "
                      />
                    </viewer>
                  </div>
                </div>
              </template>
            </el-upload>
            <el-popover
              placement="right"
              style="display: inline-block; margin-left: 0"
              v-if="info.question"
              :popper-class="
                $store.getters.windowWidth <= 1024
                  ? info.question && info.question.image
                    ? 'popover-toolip popover-toolip-wdith popover-toolip-upload-width'
                    : 'popover-toolip popover-toolip-wdith'
                  : info.question && info.question.image
                  ? 'popover-toolip popover-toolip-upload'
                  : 'popover-toolip'
              "
              ref="popover"
              trigger="hover"
              @click.stop="() => {}"
            >
              <span v-if="info.question.text">{{ info.question.text }}</span>
              <img
                :src="info.question.image"
                alt=""
                v-if="info.question.image"
                :style="{ width: getWidth(info.question.width) }"
              />
              <i class="el-icon-question mgl10 pointer" slot="reference"></i>
            </el-popover>
          </div>
        </template>
      </el-form-item>

      <slot></slot>
    </el-form>
  </div>
</template>

<script>
import commonTooltip from "@/components/commonTooltip/index";
import HSelectMultiple from "@/common-ui/selectMultiple/index";
import HSelect from "@/common-ui/select/index";
import HDatePicker from "@/common-ui/datePicker";
import addressCascader from "@/components/addressCascader/index";
import { getToken } from "@/utils/auth";
import HRadioGroup from "@/common-ui/radioGroup";
import { uploadImg } from "@/api/district";
import { axiosGet } from "@/api/axios";
import { validateFileName } from "@/utils/validate";
import { downLoadFile } from "@/utils/download";

export default {
  components: {
    HSelect,
    HDatePicker,
    addressCascader,
    commonTooltip,
    HRadioGroup,
    HSelectMultiple
  },

  props: {
    formData: {
      type: Object,
      default() {
        return {};
      }
    },
    formRules: {
      type: Object,
      default() {
        return {};
      }
    },
    labelWidth: {
      type: String,
      default() {
        return "80px";
      }
    },
    uploadImg: {
      type: Boolean,
      default() {
        return true;
      }
    },

    disabled: {
      type: Boolean,
      default: false
    },

    hideRequiredAsterisk: {
      type: Boolean,
      default: false
    },
    hasLabelImg: {
      type: Boolean,
      default: true
    },
    fileList: {
      type: Array,
      default: () => {
        return [];
      }
    }
  },
  data() {
    return {
      imgLoading: [],
      openPdfEditLoading: [],
      canEditImgLoading: [],
      downLoading: [],
      imgUploadList: [],
      formImgData: [],
      checkText: "",
      startVal: ""
    };
  },
  watch: {},
  computed: {
    header() {
      return {
        Authorization: "YWTE#" + getToken(),
        "Content-Type": "multipart/form-data",
        AuthSysCode: "RSPV"
      };
    }
  },

  beforeDestroy() {},
  methods: {
    handleLabelClick(info,val) {
      this.$emit('label-click',info,val)
    },
    handleQuerySearch(queryString, cb, info) {
      if (queryString == "") {
        cb([]);
      } else {
        this.$emit("querySearch", queryString, cb, info);
      }
    },
    handleSelectAutocomplete(val, field, parentField, info) {
      this.$emit("autocomplete-change", val, field, parentField, info);
    },

    handleAddRemark(item) {
      item.push({
        date: "",
        userName: "",
        disabled: false,
        value: ""
      });
    },
    changeSelect(val, field, parentField, info) {
      console.log(val, "kkkkk");
      info.chooseSelectList = [];
      for (let i = 0; i < info.filterOptions.length; i++) {
        for (let j = 0; j < val.length; j++) {
          let value = info.prop && info.prop.value ? info.prop.value : "id";
          if (val[j] === info.filterOptions[i][value]) {
            info.chooseSelectList.push(info.filterOptions[i]);
          }
        }
      }

      this.$emit("change", val, field, parentField, this.parentIndex);
    },

    dropDownSearchTop(val, info) {
      info.filterVal = val;
      if (info.filterVal === "") {
        info.options = JSON.parse(JSON.stringify(info.filterOptions));
        return;
      }
      let list = [];
      if (info.chooseSelectList.length > 0) {
        list = info.filterOptions.filter(item => {
          let value = info.prop && info.prop.value ? info.prop.value : "id";
          return info.chooseSelectList.every(el => el[value] != item[value]);
        });
      } else {
        list = JSON.parse(JSON.stringify(info.filterOptions));
      }
      info.options = info.chooseSelectList.concat(
        list.filter(item => {
          let name = info.prop && info.prop.label ? info.prop.label : "name";
          return item[name].includes(info.filterVal);
        })
      );
    },

    handleRadioGroupChange(val, row) {
      this.$emit("radio", val, row);
    },
    judgeTypeVisi(type) {
      let typeList = ["text", "textarea"];
      return typeList.indexOf(type) != -1;
    },
    getWidth(val, column, row) {
      return val ? (val + 'px') : ''
    },
    getHeight(val) {
      return val ? (val + 'px') : ''
    },
    handleRemoteMethod(val, field, parentField, info) {
      this.$emit("remote-method", val, field, parentField, info);
    },
    handleSelectChange(val, field, parentField, info) {
      this.$emit("select-change", val, field, parentField, info);
    },
    setDistrictId(val, label, info, districtLevel) {
      this.$emit("address", val, label, info, districtLevel);
    },
    handleInputBlur(val, field, parentField) {
      this.$emit("input-blur", val, field, parentField);
    },
    beforeAvatarUpload(file, info) {
      let name = file.name.slice(0, file.name.lastIndexOf("."));
      let validate = validateFileName(name);
      if (validate) {
        this.$commonMessage.message({
          type: "warning",
          message: '上传的文件名称不能包含下列字符: \ / : * ? " < > |'
        });
        return false;
      }
      if (info.uploadType == "image") {
        // 获取不到dwg图标的类型,只能根据name截取后缀判断
        let fileTypes = file.name.split(".");
        let type = fileTypes[fileTypes.length - 1];
        const isJPG =
          file.type === "image/jpeg" ||
          file.type === "image/png" ||
          file.type === "application/pdf" ||
          type == "dwg";
        if (!isJPG) {
          this.$commonMessage.message({
            type: "warning",
            message: "请上传PNG、JPG、pdf或dwg格式的文件!"
          });
          return false;
        }
        if (file.type === "application/pdf" || type == "dwg") {
          this.imgUploadList.push({
            file: file,
            isUpload: false
          });

          this.$emit("uploadLoad", this.imgUploadList, info);
          return true;
        } else {
          const isLtM = file.size / 1024 / 1024 >= 0.6;

          return new Promise(resolve => {
            this.imgUploadList.push({
              file: file,
              isUpload: false
            });
            this.imgLoading.push(info.field);
            this.$emit("uploadLoad", this.imgUploadList, info);
            // 小于1M 不压缩
            if (!isLtM) {
              resolve(file);
            }
            // 压缩到400KB,这里的400就是要压缩的大小,可自定义
            this.imageConversion.compressAccurately(file, 400).then(res => {
              resolve(res);
            });
          });
        }
      } else {
        this.imgUploadList.push({
          file: file,
          isUpload: false
        });
        this.imgLoading.push(info.field);
        this.$emit("uploadLoad", this.imgUploadList, info);
        return true;
      }
    },
    handleExceed(e, limit, info) {
      let times = limit ? limit : 3;
      let msg = `最多可以上传${times}张${info.label}!`;
      if (info.uploadType == "file") {
        msg = `最多可以上传${times}个文件!`;
      }

      this.$commonMessage.message({
        type: "warning",
        message: msg
      });
    },
    handleAvatarSuccess(res, file, row) {
      // let urls = ['file1', 'file2', 'file3', 'file4', 'file5']
      // var element = urls[Math.floor(Math.random() * urls.length)]

      // 以下是向后端识别图片接口传递file文件
      var formData = new FormData();
      formData.append("file", res.file);
      uploadImg(formData)
        .then(response => {
          this.$emit("upload", response, file, row);
          this.$set(
            this.imgUploadList[
              this.imgUploadList.findIndex(el => el.file.uid == res.file.uid)
            ],
            "isUpload",
            true
          );
          this.imgLoading.splice(
            this.imgLoading.findIndex(el => el == row.field),
            1
          );
          this.$emit("uploadLoad", this.imgUploadList, row);
        })
        .catch(e => {
          this.imgLoading.splice(
            this.imgLoading.findIndex(el => el == row.field),
            1
          );
          this.handleRemove(res.file, row);
        });
    },
    handleRemove(file, info, type) {
      if (info.disabled) return;
      if (
        this.imgUploadList.length > 0 &&
        this.imgUploadList.findIndex(el => el.file.uid == file.uid) != -1
      ) {
        this.$set(
          this.imgUploadList[
            this.imgUploadList.findIndex(el => el.file.uid == file.uid)
          ],
          "isUpload",
          true
        );
        this.imgUploadList.splice(
          0,
          this.imgUploadList.findIndex(el => el.file.uid == file.uid)
        );
        this.$emit("uploadLoad", this.imgUploadList, info);
      }
      if (type != "btn") {
        if (info.isDynamic) {
          this.$refs["upload" + info.field][0].handleRemove(file);
        } else {
          this.$refs["upload"][0].handleRemove(file);
        }
      }

      this.$emit("upload", file, "delete", info);
    },
    handleClear() {
      this.imgUploadList = [];
    },
    handleEditPreviewPdf(file, info) {
      if (
        this.openPdfEditLoading.indexOf(file.code) != -1 &&
        this.openPdfEditLoading.indexOf(file.uid) != -1
      )
        return;
      let requestUrl = file.url
        ? file.url.substring(file.url.indexOf("/upload"), file.url.length)
        : "";
      if (!requestUrl) {
        return;
      }
      let code = file.code ? file.code : file.uid;

      this.openPdfEditLoading.push(code);
      axiosGet(requestUrl)
        .then(res => {
          downLoadFile(res, "", "pdf", "preview");
          this.hidePreviewLoading(file);
        })
        .catch(() => {
          this.hidePreviewLoading(file);
        });
    },
    hidePreviewLoading(file) {
      let code = file.code ? file.code : file.uid;
      this.openPdfEditLoading.splice(
        this.openPdfEditLoading.findIndex(el => el == code),
        1
      );
    },
    handleDownLoadEdit(file, info) {
      if (
        this.downLoading.indexOf(file.code) != -1 &&
        this.downLoading.indexOf(file.uid) != -1
      )
        return;

      let requestUrl = file.url
        ? file.url.substring(file.url.indexOf("/upload"), file.url.length)
        : "";
      if (!requestUrl) {
        return;
      }
      let code = file.code ? file.code : file.uid;

      this.downLoading.push(code);

      let name = file.name;
      axiosGet(requestUrl)
        .then(res => {
          downLoadFile(res, name);
          this.hideImgLoading(file);
        })
        .catch(() => {
          this.hideImgLoading(file);
        });
    },
    hideImgLoading(file) {
      let code = file.code ? file.code : file.uid;
      this.downLoading.splice(
        this.downLoading.findIndex(el => el == code),
        1
      );
    },
    handleEditPreview(file, info) {
      let code = file.code ? file.code : file.uid;

      this.canEditImgLoading.push(code);

      this.formImgData = [];
      for (let i = 0; i < info.fileList.length; i++) {
        let itemImg = info.fileList[i];
        if (this.commonFunc.judgeImg(itemImg.name)) {
          this.formImgData.push(info.fileList[i]);
        }
      }
      setTimeout(() => {
        this.canEditImgLoading.splice(this.canEditImgLoading.indexOf(code), 1);
        let a = document.querySelector(`.viewerImg${code}${info.field}`);
        a.click();
      }, 1000);
    },
    onDeleteKeyDown(e, field) {
      let length = field ? field.length : 0;
      if (length != 0) {
        let { selectionStart, selectionEnd } = e.target;
        // 如果包含不能删除的区域位置,阻止删除
        if (!(selectionStart > length || selectionEnd < 0)) {
          e.preventDefault();
        }
      }
    },
    onKeyDown(e, field) {
      let length = field ? field.length : 0;
      if (field) {
        // 如果包含不能删除的区域,禁止
        if (e.target.selectionStart < length || e.target.selectionEnd < 0) {
          e.preventDefault();
        }
      }
    },
    handleFocusTextarea(e, field) {
      if (!(e.target.value && field && field.length)) return;
      if (e.target.value.length == field.length) {
        setTimeout(() => {
          e.target.setSelectionRange(field.length + 1, field.length + 1);
        });
      }
    },
    // 输入法键盘字符下不可输入,记录输入的起始位置
    onCompositionStart(e, field) {
      if (field) {
        this.checkText = e.data; //记录选中的文字
        this.startVal = e.target.selectionStart;
      }
    },

    // 当在不可编辑的范围内输入是,结合输入文字开始位置this.startVal和输入结束
    // e.target.selectionEnd位置,通过字符串截取并接还原原来的字符。
    compositionend(e, field) {
      let length = field ? field.length : 0;
      if (field) {
        if (this.startVal !== null) {
          this.$nextTick(() => {
            if (this.startVal < length) {
              let targetVal = e.target.value;
              let startTarget = targetVal.substring(0, this.startVal);
              let endTarget = targetVal.substring(e.target.selectionEnd);
              e.target.value = startTarget + this.checkText + endTarget;
            }
          });
        }
      }
    }
  },
  mounted() {}
};
</script>

<style scoped lang="scss"></style>

页面引用

  • 在views下创建名为index.vue的文件
<template>
  <div
    class="common-pop-box common-scroll"
  >
    <h-common-pop :title="title" ref="popBox" :footerList="footerBtnList">
      <h-common-form-item
        :formList="formList"
        :hideRequiredAsterisk="detailInfo.type=='detail'"
        @select-change="handleSelectChange"
        @address="handleSelectAddress"
        @upload="handleImgInfo"
        @uploadLoad="handleImgLoad"
        ref="myFromItem"
      >
      </h-common-form-item>
    </h-common-pop>
  </div>
</template>
<script>
import HCommonPop from "@/components/commonPop/index";
import HCommonFormItem from "@/components/commonFormItem/index";
import commonFormMixin from "@/mixins/commonForm";
export default {
  mixins: [commonFormMixin],
  data () {
    return {
      needBottom: false,
      formList: [
        {
          title: '日期/创建人',
          key: 'first',
          formValidate: {},
          purchasingInformation: {
            form: {
              createTime: '',
              createUserName: ''
            },
            formLabel: [
              {
                type: 'text',
                field: 'createTime',
                label: '创建日期',
                display: true,
                disabled: true,
                wordBold: true,
                itemWidth: '48%',
                marginRight: '4%',
                icon: 'date',
                widthIsNotNum: true
              },

              {
                type: 'text',
                field: 'createUserName',
                label: '创建人',
                display: true,
                disabled: true,
                wordBold: true,
                align: 'right',
                widthFit: true,
                itemWidth: '48%',
                icon: 'operator',
                widthIsNotNum: true
              }
            ]
          }
        },
        {
          title: '基本信息',
          key: 'second',
          formValidate: {
            type: [
              { required: true, message: '请选择类型', trigger: 'change' }
            ],
            name: [
              { required: true, message: '请输入名称', trigger: 'blur' },
              {
                min: 1,
                max: 50,
                message: '名称长度在 1 到 50 个字符',
                trigger: 'blur'
              }
            ],
            contactName: [
              { required: true, message: '请输入联系人', trigger: 'blur' },
              {
                min: 1,
                max: 5,
                message: '联系人长度在 1 到 5 个字符',
                trigger: 'blur'
              }
            ],
            contactPhone: [
              { required: true, message: '请输入电话1', trigger: 'blur' },
              { trigger: 'blur', validator: this.validTelephone }
            ],
            contactPhoneTwo: [
              { required: false, message: '请输入电话2', trigger: 'blur' },

              { trigger: 'blur', validator: this.validTelephone2 }
            ],

            companyDistrictId: [
              { required: true, message: '请选择地址', trigger: 'change' }
            ],
            companyAddress: [
              {
                required: true,
                message: '请输入详细地址',
                trigger: 'blur'
              },
              {
                min: 1,
                max: 50,
                message: '详细地址长度在 1 到 50 个字符',
                trigger: 'blur'
              }
            ]
          },

          purchasingInformation: {
            form: {
              type: '',
              name: '',
              contactName: '',
              contactPhone: '',
              contactPhoneTwo: '',
              companyDistrictId: '',
              companyAddress: '',
              file: ''
            },
            formLabel: [
              {
                type: 'select',
                field: 'type',
                label: '类型',
                clearable: true,
                marginBottom: true,
                placeholder: '选择类型',
                display: true,
                selectUrl: '/core/select/type',
                props: {
                  label: 'name',
                  value: 'value'
                },
                disabled: false,
                itemWidth: 330,
                marginRight: 59,
                icon: 'clientType',
                options: []
              },
              {
                type: 'text',
                field: 'name',
                label: '名称',
                clearable: true,
                selectPlaceholderAlign: 'right',
                disabledAlign: 'right',
                display: true,
                disabled: false,
                itemWidth: 1038,
                icon: 'supplier'
              },
              {
                type: 'text',
                field: 'contactName',
                label: '联系人',
                marginBottom: true,
                display: true,
                disabled: false,
                itemWidth: 411,
                marginRight: 194,
                icon: 'contacts'
              },

              {
                type: 'text',
                field: 'contactPhone',
                label: '电话1',
                display: true,
                disabled: false,
                itemWidth: 339.5 * 2,
                marginRight: 262 * 2,
                icon: 'phone'
              },
              {
                type: 'text',
                field: 'contactPhoneTwo',
                label: '电话2',
                display: true,
                disabled: false,
                itemWidth: 340,
                marginRight: 0,
                icon: 'phone'
              },

              {
                type: 'district',
                field: 'companyDistrictId',
                label: '公司地址',
                display: true,
                disabled: false,
                marginBottom: true,
                itemWidth: 672.5,
                marginRight: 30,
                icon: 'position'
              },
              {
                type: 'text',
                field: 'companyAddress',
                label: '详细地址',
                noLabel: true,
                placeholder: '填写详细地址',
                display: true,
                disabled: false,
                inputPlaceHolderAlign: 'right',
                itemWidth: 847
              },
              {
                type: 'upload',
                field: 'file',
                uploadType: 'image',
                label: '文件',
                isDynamic: true,
                multiple: true,
                display: false,
                disabled: false,
                itemWidth: '100%',
                widthIsNotNum: true,
                icon: 'agreement',
                chooseImgListOrigin: [],
                uploadImgList: [],
                fileList: [],
                chooseImgList: []
              }
            ]
          }
        }
      ],
      agreementInfo:{}
    }
  },
  components: {
    HCommonPop,
    HCommonFormItem
  },
  computed: {
    footerBtnList () {
      return [
            {
              caption: '返回',
              type: 'warning',
              callback: this.back,
              btnClass: 'warningButton',
            },
            {
              caption: '保存',
              type: 'primary',
              callback: this.submit,
              btnClass: 'primaryButton',
            }
          ]
    }
  },

  mounted () {

  },

  methods: {
   

  }
}
</script>

<style lang="scss" scoped>
.common-pop-box {
  width: 100%;
  height: 100%;
  padding-bottom: 34px;
  box-sizing: border-box;
  &.common-pop-box-footer {
    ::v-deep .footer-button-group {
      margin-top: 200px;
    }
  }
}
</style>

  • 页面中使用了两个子组件,引入了一个mixins:
    • HCommonPop: 定义了一个包含标题和右侧插槽的div容器。
    • HCommonFormItem: 详见上文
    • commonFormMixin: 处理表单数据、上传文件、校验表单、获取下拉选项,提交表单;具体处理详见该篇文章vue 表单封装后数据的获取提交
HCommonPop

标题会根据传入的title属性进行显示,如果没有传入则显示空字符串。右侧插槽可以在使用该组件时插入其他内容。接着,使用了slot标签来插入组件的内容,即弹出窗口的主体部分。最后,通过引入HCommonFooter组件,将按钮列表传递给该组件,并在footerList有内容时显示通用底部组件

<template>
  <div>
    <div
      class="pop-title"
    >
      <slot name="pop-title">
        {{ title }}
        <slot name="pop-right"></slot>
      </slot>
    </div>
    <slot></slot>
    <h-common-footer :buttonList="footerList" v-if="footerList.length>0"> </h-common-footer>
  </div>
</template>

<script>
import HCommonFooter from '@/components/commonFooter/index'

export default {
  name: 'HCreatePop',
  props: {
    title: {
      type: String,
      default: () => {
        return ''
      }
    },
    footerList: {
      type: Array,
      default: () => {
        return []
      }
    }
  },
  components: { HCommonFooter },
  data () {
    return {
      visible: true
    }
  },
  created () {},
  mounted () {},
  methods: {
   
  },
  watch: {
    dialogVisible (val) {
      this.visible = this.dialogVisible
    },
    
  },
  computed: {
  }
}
</script>

<style lang="scss" scoped>
.pop-title {
  width: calc(100% - 3.54167vw);
  text-align: center;
  height: 48px;
  font-weight: 600;
  font-size: 40px;
  z-index: 0;
  color: #000;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 13px;
  position: relative;
  margin-bottom: 88px;
 
}
</style>

HCommonFooter

动态生成button的组件

<template>
  <div
      class="footer-button-group"
      :class="[
      isAllWidth ? 'footer-button-group-all' : '',
      isSmallBtn ? 'footer-button-group-small-bth': ''
    ]"
  >
    <template
            v-for="(
        { caption, display, permissionKey, icon, disabled, callback, action, type, btnClass, loading },
        index
      ) in buttonList"
    >
      <h-button v-if="getButtonDisplay(display)"
                :key="action ? index + action : index"
                :action="action"
                :btnClass="btnClass"
                :caption="caption"
                :icon="icon"
                :disabled="disabled"
                :type="type || 'primary'"
                :loading="loading"
                @click="callback"
                v-permission="permissionKey"
      ></h-button>
    </template>
  </div>
</template>

<script>
  import HButton from '@/harley-ui/button'

  export default {
    data() {
      return {}
    },
    components: {
      HButton
    },
    props: {
      isAllWidth: {
        default: false,
        type: Boolean
      },
      isSmallBtn: {
        default: false,
        type: Boolean
      },
      buttonList: {
        type: Array,
        caption: [String, Number],
        icon: String,
        disabled: {
          type: Boolean,
          default: false
        },
        callback: {
          type: Function,
          required: true
        },
        action: {
          type: String,
          validator(value) {
            return ['add', 'edit', 'delete', 'appointRow'].indexOf(value) !== -1
          }
        },
        loading: {
          type: Boolean,
          default: false
        }
      }
    },
    methods: {
      getButtonDisplay(display) {
        let result = true
        if (typeof display === 'boolean') {
          result = display
        } else if (typeof display === 'function') {
          result = display()
        }
        return result
      },
    }
  }
</script>

<style lang="scss" scoped>
  .footer-button-group {
    margin-top: 60px;
    box-sizing: border-box;
    width: calc(100% - 3.54167vw);
    display: flex;
    align-items: center;
    justify-content: center;

    &.footer-button-group-all {
      width: 100%;
    }
  }
</style>

  • 模板部分(template):
    • 通过v-for指令循环遍历buttonList数组,动态生成按钮组。根据buttonList中的数据来渲染h-button组件,实现按钮的动态生成。
    • 根据getButtonDisplay方法返回的布尔值来决定按钮是否显示。
  • 脚本部分(script):
    • 导入HButton组件,并在components属性中注册。
    • 定义了props属性,用于接收父组件传递的数据。其中isAllWidth和isSmallBtn是布尔类型的prop,buttonList是数组类型的prop,包含了按钮的各种属性和回调函数。
  • 方法部分:
    • getButtonDisplay方法用于根据按钮的display属性确定按钮是否显示。如果display是布尔值,则直接返回;如果是函数,则执行函数并返回结果。
  • 样式部分(style):
    • 使用了SCSS语法定义了.footer-button-group类的样式。设置了按钮组的样式,包括顶部间距、宽度、排列方式等。根据isAllWidth和isSmallBtn属性的值,可以动态改变按钮组的样式。
  • 22
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Vue ElementUI提供了丰富的表单组件,但是在实际开发中,我们可能需要对表单进行封装,以便于复用和统一管理。以下是一些封装表单的思路: 1. 将表单组件封装成一个单独的组件,包括表单项、校验规则、提交事件等。这样可以将表单的逻辑和UI分离,方便维护和修改。 2. 使用插槽(slot)来动态渲染表单项,可以根据需要添加、删除、修改表单项,提高表单的灵活性。 3. 将表单项的校验规则封装成一个单独的对象,可以在多个表单中复用,减少代码冗余。 4. 使用v-model来双向绑定表单数据,方便获取和提交表单数据。 5. 使用ElementUI提供的表单验证规则,可以快速实现表单的校验功能,减少开发时间和代码量。 总之,封装表单可以提高代码的复用性和可维护性,同时也可以提高开发效率和代码质量。 ### 回答2: Vue是一个流行的前端框架,Element UI则是一个基于Vue框架的UI库。在Vue应用程序中使用Element UI封装form表单,可以简化表单的开发流程,并且可以提供一致性的UI风格。 Element UI提供了大量的表单组件,如输入框、下拉框、日期选择等。在Vue使用Element UI表单组件,需要对其进行封装,以便可以更方便地使用。 首先,在Vue组件中引入ElementUI库,可以通过install方法进行注册。在组件中注册可以方便使用,同时也可以控制内部封装实现,让表单组件更加灵活地适应业务场景。 其次,需要定义表单数据模型,可以使用组件中的data对象来定义,每个数据字段对应一个表单输入框。将表单数据模型和Element UI组件双向绑定,可以实现对表单数据的自动更新。 然后,定义表单校验规则,可以使用Element UI提供的Validator组件,同时也可以自定义校验规则。在提交表单前进行表单校验,可以避免用户输入不合法数据造成数据异常。 最后,封装表单提交方法,可以使用Vue的methods方法进行实现。在提交表单时,需要将表单数据转换为JSON格式,以便可以进行后续处理。 综上所述,通过对Element UI表单组件进行封装,可以实现更加便捷、高效、可维护的表单开发。同时,也可以提高用户体验,降低用户输入错误的概率,提高数据的准确性和可靠性。 ### 回答3: Vue ElementUI是一套基于Vue.js的UI组件库,它的封装了丰富的组件和样式,使得我们在编写前端页面时可以快速搭建页面、提高开发效率。其中Form表单组件是最常用的之一。 在Vue Element UI中使用Form表单组件时,我们往往会发现它所提供的属性和方法已经足够满足我们大部分的需求。但是,在实际开发中,我们可能会遇到一些特殊的需求,需要封装一些自定义的逻辑和验证规则,比如异步校验、业务逻辑校验。这时候,我们就需要对Vue ELementUIForm表单组件进行封装封装Form表单组件的目的就是为了封装我们常用的数据校验、提交等逻辑,提高代码的重用性和可维护性。在封装时,我们需要考虑以下几个方面: 1.表单数据的绑定: 在封装时,我们需要考虑表单数据的双向绑定,通常我们会将表单数据绑定在组件的数据对象中,并且在数据对象中提供一个reset方法,用于重置表单数据。 2.验证规则的扩展: 在Vue Element UI中,每个表单控件都可以设置相应的验证规则,比如必填、邮箱、手机号等等,但是有时候我们会遇到一些特殊的需求,比如异步校验、业务逻辑校验等,我们需要对这些验证进行特殊处理,可以通过自定义的验证方法来实现。 3.表单提交: 封装表单的最终目的就是为了实现表单的提交,并处理提交的结果。我们可以将表单的提交和结果处理封装成方法,并以事件的形式发布出去,供父组件调用。 综上所述,封装Vue ElementUIForm表单组件可以帮助我们提高代码的重用性和可维护性,减少代码的重复编写。同时,也可以使得我们的前端页面开发更加高效,更好地实现页面交互效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值