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

组件的封装

HCommonFormItem

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

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

     <template v-if="item.display===undefined || item.display===true"&gt;
       <HCommonBlock :title="item.title" :key="index" class="form-box" :hasBorder="item.hasBorder"&gt;
         <template slot="title-button" v-if="item.slot"&gt;
           <slot :name="'title-button-'+ item.key"&gt;</slot&gt;
         </template&gt;
         <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>

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;
  &amp;:first-child {
    margin-top: 0;
  }
  &amp;:last-child {
    margin-bottom: 60px;
  }
  &amp;.block-box-no-padding {
    .block-content-box {
      padding: 0;
      background: transparent;
    }
  }
  &amp;.block-box-no-margin {
    margin-bottom: 0;
  }
  &amp;.block-box-border {
    .block-content-box {
      border-radius: 10px;
      border: 1px solid #000000;
      background: transparent;
      overflow: hidden;
      &amp;-overflow-no {
        overflow: inherit;
      }
      &amp;.common-scroll {
        overflow-y: auto;
      }
    }
  }
  .block-title-box {
    display: flex;
    .block-title {
      cursor: pointer;
      border-radius: 0;
      &amp;:first-child {
        border-radius: 20px 0 0px 0px;
      }
      &amp;: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;
    &amp;.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>

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&amp;&amp;hasLabelImg?'form-label-Required':'',
      !hideRequiredAsterisk &amp;& !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、pdfdwg格式的文件!"
          });
          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>

页面引用

<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>

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>

原文地址:https://blog.csdn.net/weixin_46328739/article/details/134737607

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_24118.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱:suwngjj01@126.com进行投诉反馈,一经查实,立即删除!

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注