element-ui组件库table组件优化封装/element-ui组件库form组件优化封装

一个后端管理系统,有大量的表格,表单,所以基于element ui的el-table,el-form里面的表单元素进行二次封装是很有必要的,也可以保证每个页面的统一
接下来的两个组件由我和另外两个同事在使用中不断优化,目前比较稳定
1.目录

2.案例

下面两个文件,是我如何使用组件完成基本的table页面

common-table.vue组件

<template>
  <div style="padding:30px">
    <!-- 3将组件写入到页面 -->
     <!-- 7如果由额外的参数也可以用extraFormData传入 -->
    <commonTable 
      border
      :column="versionManageColumn"
      :pagination="versionManagePagi" 
      :data-url="verifyManageUrl"
      :filter-btn="filterBtn"
      :extra-form-data="extraFormData" 
    >
      <template #operate>
         <el-button type="info">查看信息</el-button>
      </template>
      <template #packageName="{ scope }">
        <div style="color:red">{{scope.row.packageName}}(嘿嘿嘿,我要变红)</div>
      </template>
    </commonTable>
  </div>
</template>
<script>
// 4传入参数到组件里面,column必传,data-url必传,pagination必传,如果不需要分页,传versionManagePagi: {hide: true}
// 凡是定义的常量,都可以重新写一个js,引入进来使用,不然页面会很多常量,例如这里的versionManageColumn
import { versionManageColumn, filterBtn } from './commonTableOption'
// 1引入commonTable组件
import commonTable from '@/components/CommonTable'
export default {
  components: {
    commonTable, // 2注入组件
  },
  data() {
    return {
      versionManagePagi: { 
        pageSize: 20,
        currentPage: 1,
        total: 0,
      },
      versionManageColumn,
      verifyManageUrl: 'http://47.110.148.106:3030/mock/11/version/manage',
      filterBtn,
      extraFormData: {
        name: '花花',
      }
    }
  }
}
</script>

commonTableOption.js主要是定义常量

export const versionManageColumn = [
  {
    prop: 'versionName',
    label: '版本名称',
    filterOption: {
      type: 'input',
      label: '版本名称',
      prop: 'versionName',
      placeholder: '请输入',
      formChildWidth: '250px',
    },
  },
  {
    prop: 'packageName',
    label: '包名称',
  },
  {
    prop: 'versionDesc',
    label: '版本说明',
  },
  // 5比较强大的地方在于能够直接配置进行去搜索
  {
    prop: 'isForceUpdate',
    label: '是否强制更新',
    filterOption: {
      type: 'select',
      label: '是否强制更新',
      prop: 'isForceUpdate',
      placeholder: '请选择',
      disabled: false,
      multiple: false,
      filterable: true,
      clearable: false,
      selectData: [
        { label: '是', value: 0 }, { label: '否', value: 1 },
      ],
      formChildWidth: '250px',
    },
  },
  {
    prop: 'installEquipmentNum',
    label: '安装设备数',
  },
  {
    prop: 'proportion',
    label: '占比',
  },
  {
    prop: 'versionPublishTime',
    label: '版本发布时间',
  },
  {
    prop: 'operate',
    label: '操作',
  },
]
// 6按钮操作也可以进行配置
export const filterBtn = { type: 'button', children:
  [
    { type: 'search' },
    { type: 'reset' },
    {
      type: 'export',
      apiUrl: 'xxx',
      isLimit: true,
      fileName: '咕咕咕',
    },
  ],
}

3.组件代码 (结合目录哦)
文件1 CommonTable/index.vue

<template>
  <div>
    <el-form
      ref="form"
      class="form-flex"
      :label-width="formLabelWidth"
      :disabled="formDisabled"
      :model="formData"
      :rules="formRules"
      :inline="inline"
    >
      <el-form-item
        v-for="(formItem, itemIndex) in formFields"
        v-show="!formItem.hidden"
        :key="itemIndex"
        :prop="formItem.prop"
        :label-width="formItem.type === 'button' ? '0px': formItem.labelWidth"
        :label="formItem.label"
        :class="formItem.class"
      >
        <div
          v-if="formItem.type === 'extraMention'"
          :class="formItem.class"
        >{{ formItem.prop }}</div>
        <!-- 输入框或者textarea-->
        <el-input
          v-if="formItem.type === 'input'"
          v-model="formData[formItem.prop]"
          :placeholder="formItem.placeholder"
          :disabled="formItem.disabled"
          :clearable="formItem.clearable"
          :style="{ formItem.formChildWidth}"
          :maxlength="formItem.maxlength"
        />
        <el-input
          v-if="formItem.type === 'textarea'"
          v-model="formData[formItem.prop]"
          :placeholder="formItem.placeholder"
          type="textarea"
          :disabled="formItem.disabled"
          :style="{ formItem.formChildWidth}"
          :clearable="formItem.clearable"
          :maxlength="formItem.maxlength"
          :autosize="formItem.autoSize"
          :rows="formItem.rows || 2"
        />
        <el-input
          v-if="formItem.type === 'number'"
          v-model.number="formData[formItem.prop]"
          :placeholder="formItem.placeholder"
          type="number"
          :disabled="formItem.disabled"
          :style="{ formItem.formChildWidth}"
          :clearable="formItem.clearable"
          :min="formItem.min"
          :max="formItem.max"
        />
        <!-- 下拉框 -->
        <el-select
          v-if="formItem.type === 'select'"
          v-model="formData[formItem.prop]"
          :placeholder="formItem.placeholder"
          :disabled="formItem.disabled"
          :style="{ formItem.formChildWidth}"
          :multiple="formItem.multiple"
          :filterable="formItem.filterable"
          :clearable="formItem.clearable"
          @change="val => selectChange(val, formItem.prop)"
        >
          <el-option
            v-for="(selectItem, index) in formItem.selectData"
            :key="index"
            :label="selectItem.label"
            :value="selectItem.value"
          />
        </el-select>
        <!-- 时间控件 -->
        <el-date-picker
          v-if="formItem.type === 'datePicker'"
          v-model="formData[formItem.prop]"
          :type="formItem.dateType"
          :value-format="formItem.valueFormat || 'yyyy-MM-dd HH:mm:ss'"
          :format="formItem.format || 'yyyy-MM-dd HH:mm:ss'"
          :placeholder="formItem.placeholder"
          :disabled="formItem.disabled"
          :style="{ formItem.formChildWidth}"
          range-separator="至"
          :start-placeholder="formItem.placeholderStart"
          :end-placeholder="formItem.placeholderEnd"
          :class="formItem.class"
          :picker-options="formItem.pickerOptions"
        />
        <el-time-picker
          v-if="formItem.type === 'timePicker'"
          v-model="formData[formItem.prop]"
          value-format="HH:mm:ss"
          :placeholder="formItem.placeholder"
          :disabled="formItem.disabled"
          :style="{ formItem.formChildWidth}"
        />
        <el-date-picker
          v-if="formItem.type === 'dateTimePiker'"
          v-model="formData[formItem.prop]"
          :value-format="formItem.valueFormat || 'yyyy-MM-dd HH:mm:ss'"
          :format="formItem.format || 'yyyy-MM-dd HH:mm:ss'"
          type="datetime"
          :disabled="formItem.disabled"
          :placeholder="formItem.placeholder"
          :style="{ formItem.formChildWidth}"
        />
        <div v-if="formItem.type === 'dateZones'">
          <div v-for="(dateItem, dateIndex) in formItem.children" :key="dateIndex" class="date-zone">
            <el-form-item :prop="dateItem.prop">
              <el-date-picker
                v-if="dateItem.type === 'dataPicker'"
                v-model="formData[dateItem.prop]"
                value-format="yyyy-MM-dd"
                type="date"
                :placeholder="dateItem.placeholder"
                :disabled="dateItem.disabled"
                :style="{ dateItem.formChildWidth}"
              />
            </el-form-item> <span v-if="dateIndex===0 && inline">至</span>
          </div>
        </div>
        <!-- switch切换 -->
        <el-switch
          v-if="formItem.type === 'switch'"
          v-model="formData[formItem.prop]"
          :disabled="formItem.disabled"
          :style="{ formItem.formChildWidth}"
          @change="val => switchChange(val, formItem.prop)"
        />
        <!-- checkbox -->
        <el-checkbox-group
          v-if="formItem.type === 'checkbox'"
          v-model="formData[formItem.prop]"
          :disabled="formItem.disabled"
          :style="{ formItem.formChildWidth}"
          @change="val => checkChange(val, formItem.prop)"
        >
          <el-checkbox
            v-for="(selectItem, index) in formItem.checkData"
            :key="index"
            :label="selectItem"
            name="type"
          />
        </el-checkbox-group>
        <!-- radio -->
        <el-radio-group
          v-if="formItem.type === 'radio'"
          v-model="formData[formItem.prop]"
          :disabled="formItem.disabled || false"
          :fill="formItem.fill || '#409EFF'"
          :text-color="formItem.textColor || '#ffffff'"
          :style="{ formItem.formChildWidth}"
          @change="val => checkChange(val, formItem.prop)"
        >
          <el-radio
            v-for="(radioItem, index) in formItem.radioData"
            :key="index"
            :label="radioItem.label"
            :disabled="radioItem.disabled || false"
            :border="radioItem.border || false"
            :size="radioItem.size"
            :name="radioItem.primevalName || ''"
          >{{ radioItem.name || radioItem.label }}</el-radio>
        </el-radio-group>
        <!-- 普通标签 -->
        <div v-if="formItem.type === 'normalTag'" class="tag-fields">

          <el-tag
            v-for="(tagItem) in formItem.tagData"
            :key="tagItem.id"
            :closable="tagItem.closable || formItem.closable"
            :disable-transitions="tagItem.transitions"
            :size="tagItem.size"
            :effect="tagItem.effect"
            :type="tagItem.type"
            @close="tagClose(tagItem.id,formItem.tagData)"
          >{{ tagItem.name }}
          </el-tag>
          <el-tag v-if="!!(Object.keys(formItem.addTagBtnOption).length)" :size="formItem.addTagBtnOption.size" class="add-tag-btn" @click="addTag(formItem.tagData)">{{ formItem.addTagBtnOption.name }}</el-tag>
        </div>
        <!-- 图片标签 -->
        <div v-if="formItem.type === 'imageTag'" class="image-tag-fields-box">
          <div v-for="tagItem in formItem.tagData" :key="tagItem.id" class="img-item-wrap">
            <i v-if="!formItem.noDeleteIcon" class="el-icon-error delete-icon" @click="imageTagClose(tagItem.id,formItem.tagData)" />
            <div class="img-item" @click="uploadPreview(tagItem)"><img :src="tagItem.url" alt="暂无图片"></div>
            <span>{{ tagItem.name }}</span>
          </div>
          <div class="img-item-wrap add-item" @click="addImgTag(formItem.tagData)">
            <div class="img-item"> <span><i class="el-icon-plus add-btn" /></span></div>
          </div>
        </div>
        <!-- 自定义表单域 -->
        <div v-if="formItem.type === 'customSlot'" class="custom-slot-fields">
          <slot :name="`${formItem.prop}Slot`" />
        </div>
        <!--  upload 上传  -->
        <el-upload
          v-if="formItem.type === 'upload'"
          :ref="'uploadref'"
          :action="formItem.action"
          :headers="formItem.header"
          :multiple="formItem.multiple"
          :data="formItem.data"
          :name="formItem.name || 'file'"
          :with-credentials="formItem.withCredentials || false"
          :show-file-list="formItem.showFileList || true"
          :drag="formItem.drag || false"
          :accept="formItem.accept"
          :on-preview="formItem.onPreview || uploadPreview"
          :on-remove="formItem.onRemove"
          :on-success="formItem.onSuccess"
          :on-error="formItem.onError"
          :on-progress="formItem.onProgress"
          :on-change="formItem.onChange"
          :before-upload="formItem.beforeUplaod"
          :before-remove="formItem.beforeRemove"
          :list-type="formItem.listType || 'picture-card'"
          :auto-upload="formItem.autoUpload || true"
          :file-list="formData[formItem.prop] || []"
          :http-request="formItem.httpRequest"
          :disabled="formItem.disabled || false"
          :limit="formItem.limit"
          :on-exceed="formItem.onExceed"
        >
          <i class="el-icon-plus" />
          <!-- upload 插槽 -->
          <template #trigger>
            <slot :name="`${formItem.prop}Trigger`" />
          </template>
          <template #tip>
            <slot :name="`${formItem.prop}Tip`" />
          </template>
        </el-upload>
        <!-- 按钮 -->
        <div v-if="formItem.type === 'button'" class="button-flex">
          <div v-for="(buttonItem, buttonIndex) in formItem.children" :key="buttonIndex" class="button-child-flex">
            <button-comp
              :button-item="buttonItem"
              v-on="{
                reset, confirm, cancel, search, exportFile
              }"
            />
          </div>
        </div>
      </el-form-item>
    </el-form>

    <!-- 图片预览弹窗 -->
    <el-dialog :visible.sync="uploadDialogVisible" top="7vh" :append-to-body="true">
      <img width="100%" :src="dialogImageUrl" alt="">
    </el-dialog>
  </div>
</template>

<script>
import buttonComp from './buttonComp'
export default {
  components: {
    buttonComp,
  },
  props: {
    formLabelWidth: { // 表单label宽度
      type: String,
      default: '200px',
    },
    formData: { // 表单数据
      type: Object,
      default: null,
    },
    formDisabled: { // 是否禁用表单
      type: Boolean,
      default: false,
    },
    formRules: { // 表单验证规则
      type: Object,
      default: null,
    },
    // 设置json数据
    formFields: { // 表单项配置信息
      type: Array,
      default: () => [],
    },
    inline: { // 表单横向竖向控制
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      dialogImageUrl: '',
      uploadDialogVisible: false,
    }
  },
  methods: {
    reset() {
      this.$refs['form'].resetFields()
      this.$emit('reset')
    },
    exportFile(buttonItem) {
      this.$emit('exportFile', buttonItem)
    },
    cancel() {
      this.$emit('cancel')
    },
    confirm() {
      this.$refs['form'].validate((valid) => {
        if (valid) {
          // 传事件到父亲组件
          this.$emit('confirm')
        } else {
          return false
        }
      })
    },
    // 下拉
    selectChange(val, prop) {
      this.$emit(`select${prop}`, val)
    },
    // switch切换
    switchChange(val, prop) {
      this.$emit(`switch${prop}`, val)
    },
    // 选中
    checkChange(val, prop) {
      this.$emit(`check${prop}`, val)
    },
    // 标签关闭事件
    tagClose(id, tagData) {
      const curIndex = tagData.findIndex((tagItem, index) => tagItem.id === id)
      tagData.splice(curIndex, 1)
      this.$emit('tagClose', id)
    },
    // 图片标签关闭事件
    imageTagClose(id, tagData) {
      const curIndex = tagData.findIndex((tagItem, index) => tagItem.id === id)
      tagData.splice(curIndex, 1)
      this.$emit('imgTagClose', id)
    },
    addTag(tagData) {
      this.$emit('addTagBtn', tagData)
    },
    addImgTag(tagData) {
      this.$emit('addImgTagBtn', tagData)
    },
    // 查询
    search() {
      this.$emit('search')
    },
    // 上传图片预览
    uploadPreview(file) {
      this.dialogImageUrl = file.url
      this.uploadDialogVisible = true
    },
  },
}
</script>
<style lang="scss" scoped>
  .button-flex {
    display: flex;
    flex-wrap: wrap;
    text-align: center;
  }
  .button-child-flex {
    flex: 1;
    margin-right: 10px;
  }
  .date-zone {
    margin-bottom: 20px;
  }
  .tag-fields{
    display: flex;
    flex-wrap: wrap;
    /deep/.el-tag{
      margin: 0 10px 10px 0;
      &.add-tag-btn{
        color: #606266;
        background: #ffffff;
        border-color: #DCDFE6;
      }
      &.add-tag-btn:hover {
        color: #409EFF;
        border-color: #c6e2ff;
        background-color: #ecf5ff;
        cursor:pointer;
      }
    }
  }
</style>

文件2 CommonTable/pagination.vue

<template>
  <div>
    <el-pagination
      background
      layout="total, sizes, prev, pager, next, jumper"
      :total="pagination.total"
      :page-size="pagination.size"
      :current-page="pagination.page"
      :page-sizes="pagination.sizeZones"
      @current-change="handleCurrentChange"
      @size-change="handleSizeChange"
    />
  </div>
</template>

<script>
export default {
  props: {
    pagination: {
      type: Object,
      default: null
    }
  },
  data() {
    return {
    }
  },
  methods: {
    handleCurrentChange() {
    },
    handleSizeChange() {
    }
  }
}
</script>

文件3 CommonTable/components/buttonComp.vue

<template>
  <el-button
    :size="buttonItem.size || 'medium'"
    :type="buttonItem.btnType"
    :plain="buttonItem.plain || false"
    :round="buttonItem.round || false"
    :circle="buttonItem.circle || false"
    :loading="buttonItem.loading || false"
    :disabled="buttonItem.disabled || false"
    :icon="buttonItem.icon"
    @click="btnClick"
  >
    {{ buttonItem.btnText }}
  </el-button>
</template>

<script>
export default {
  name: 'ButtonComp',
  props: {
    buttonItem: {
      type: Object,
      required: true,
    },
  },
  methods: {
    btnClick() {
      this.$emit('clickFunc')
    },
  },
}
</script>

文件4 CommonTable/components/tbPagination.vue

<template>
  <div :class="`${pagination.className || ''} defaultPagination`">
    <div class="tip-box">
      <span>本页个数:{{ pagination.currentTotal }}</span>
      <span>总数:{{ pagination.total }}</span>
    </div>
    <!--  分页器前插槽  -->
    <slot name="beforePagination" />

    <el-pagination
      v-if="pagination.isShow || true"
      :small="pagination.small || false"
      :background="pagination.background || true"
      :page-size="pagination.pageSize || 20"
      :total="pagination.total"
      :page-count="pagination.pageCount"
      :pager-count="pagination.pagerCount || 7"
      :current-page="pagination.currentPage || 1"
      :layout="pagination.layout || 'sizes, prev, pager, next'"
      :page-sizes="pagination.pageSizes || [5, 10, 20, 30, 40, 50, 100]"
      :popper-class="pagination.popperClass"
      :prev-text="pagination.prevText"
      :next-text="pagination.nextText"
      :disabled="pagination.disabled || false"
      :hide-on-single-page="pagination.hideOnSinglePage || false"
      @size-change="val => $emit('size-change', val)"
      @current-change="val => $emit('page-change', val)"
    />

    <!--  分页器后插槽  -->
    <slot name="afterPagination" />
  </div>
</template>

<script>
export default {
  name: 'TbPagination',
  props: {
    pagination: { // 分页配置数据
      type: Object,
      required: true,
    },
  },
  methods: {
  },
}
</script>

<style lang="scss" scoped>
  .defaultPagination{
    margin: 20px;
    display: flex;
    justify-content: space-between;
    align-items: center;
    .tip-box{
      span{
        padding-right: 20px;
        font-size: 14px;
        color: #606266;
      }
    }
  }
</style>

以上是封装table的代码,下面是分装form
文件1
CommomForm/index.vue

<template>
  <div>
    <el-form
      ref="form"
      class="form-flex"
      :label-width="formLabelWidth"
      :disabled="formDisabled"
      :model="formData"
      :rules="formRules"
      :inline="inline"
    >
      <el-form-item
        v-for="(formItem, itemIndex) in formFields"
        v-show="!formItem.hidden"
        :key="itemIndex"
        :prop="formItem.prop"
        :label-width="formItem.type === 'button' ? '0px': formItem.labelWidth"
        :label="formItem.label"
        :class="formItem.class"
      >
        <div
          v-if="formItem.type === 'extraMention'"
          :class="formItem.class"
        >{{ formItem.prop }}</div>
        <!-- 输入框或者textarea-->
        <el-input
          v-if="formItem.type === 'input'"
          v-model="formData[formItem.prop]"
          :placeholder="formItem.placeholder"
          :disabled="formItem.disabled"
          :clearable="formItem.clearable"
          :style="{ formItem.formChildWidth}"
          :maxlength="formItem.maxlength"
        />
        <el-input
          v-if="formItem.type === 'textarea'"
          v-model="formData[formItem.prop]"
          :placeholder="formItem.placeholder"
          type="textarea"
          :disabled="formItem.disabled"
          :style="{ formItem.formChildWidth}"
          :clearable="formItem.clearable"
          :maxlength="formItem.maxlength"
          :autosize="formItem.autoSize"
          :rows="formItem.rows || 2"
        />
        <el-input
          v-if="formItem.type === 'number'"
          v-model.number="formData[formItem.prop]"
          :placeholder="formItem.placeholder"
          type="number"
          :disabled="formItem.disabled"
          :style="{ formItem.formChildWidth}"
          :clearable="formItem.clearable"
          :min="formItem.min"
          :max="formItem.max"
        />
        <!-- 下拉框 -->
        <el-select
          v-if="formItem.type === 'select'"
          v-model="formData[formItem.prop]"
          :placeholder="formItem.placeholder"
          :disabled="formItem.disabled"
          :style="{ formItem.formChildWidth}"
          :multiple="formItem.multiple"
          :filterable="formItem.filterable"
          :clearable="formItem.clearable"
          @change="val => selectChange(val, formItem.prop)"
        >
          <el-option
            v-for="(selectItem, index) in formItem.selectData"
            :key="index"
            :label="selectItem.label"
            :value="selectItem.value"
          />
        </el-select>
        <!-- 时间控件 -->
        <el-date-picker
          v-if="formItem.type === 'datePicker'"
          v-model="formData[formItem.prop]"
          :type="formItem.dateType"
          :value-format="formItem.valueFormat || 'yyyy-MM-dd HH:mm:ss'"
          :format="formItem.format || 'yyyy-MM-dd HH:mm:ss'"
          :placeholder="formItem.placeholder"
          :disabled="formItem.disabled"
          :style="{ formItem.formChildWidth}"
          range-separator="至"
          :start-placeholder="formItem.placeholderStart"
          :end-placeholder="formItem.placeholderEnd"
          :class="formItem.class"
          :picker-options="formItem.pickerOptions"
        />
        <el-time-picker
          v-if="formItem.type === 'timePicker'"
          v-model="formData[formItem.prop]"
          value-format="HH:mm:ss"
          :placeholder="formItem.placeholder"
          :disabled="formItem.disabled"
          :style="{ formItem.formChildWidth}"
        />
        <el-date-picker
          v-if="formItem.type === 'dateTimePiker'"
          v-model="formData[formItem.prop]"
          :value-format="formItem.valueFormat || 'yyyy-MM-dd HH:mm:ss'"
          :format="formItem.format || 'yyyy-MM-dd HH:mm:ss'"
          type="datetime"
          :disabled="formItem.disabled"
          :placeholder="formItem.placeholder"
          :style="{ formItem.formChildWidth}"
        />
        <div v-if="formItem.type === 'dateZones'">
          <div v-for="(dateItem, dateIndex) in formItem.children" :key="dateIndex" class="date-zone">
            <el-form-item :prop="dateItem.prop">
              <el-date-picker
                v-if="dateItem.type === 'dataPicker'"
                v-model="formData[dateItem.prop]"
                value-format="yyyy-MM-dd"
                type="date"
                :placeholder="dateItem.placeholder"
                :disabled="dateItem.disabled"
                :style="{ dateItem.formChildWidth}"
              />
            </el-form-item> <span v-if="dateIndex===0 && inline">至</span>
          </div>
        </div>
        <!-- switch切换 -->
        <el-switch
          v-if="formItem.type === 'switch'"
          v-model="formData[formItem.prop]"
          :disabled="formItem.disabled"
          :style="{ formItem.formChildWidth}"
          @change="val => switchChange(val, formItem.prop)"
        />
        <!-- checkbox -->
        <el-checkbox-group
          v-if="formItem.type === 'checkbox'"
          v-model="formData[formItem.prop]"
          :disabled="formItem.disabled"
          :style="{ formItem.formChildWidth}"
          @change="val => checkChange(val, formItem.prop)"
        >
          <el-checkbox
            v-for="(selectItem, index) in formItem.checkData"
            :key="index"
            :label="selectItem"
            name="type"
          />
        </el-checkbox-group>
        <!-- radio -->
        <el-radio-group
          v-if="formItem.type === 'radio'"
          v-model="formData[formItem.prop]"
          :disabled="formItem.disabled || false"
          :fill="formItem.fill || '#409EFF'"
          :text-color="formItem.textColor || '#ffffff'"
          :style="{ formItem.formChildWidth}"
          @change="val => checkChange(val, formItem.prop)"
        >
          <el-radio
            v-for="(radioItem, index) in formItem.radioData"
            :key="index"
            :label="radioItem.label"
            :disabled="radioItem.disabled || false"
            :border="radioItem.border || false"
            :size="radioItem.size"
            :name="radioItem.primevalName || ''"
          >{{ radioItem.name || radioItem.label }}</el-radio>
        </el-radio-group>
        <!-- 普通标签 -->
        <div v-if="formItem.type === 'normalTag'" class="tag-fields">

          <el-tag
            v-for="(tagItem) in formItem.tagData"
            :key="tagItem.id"
            :closable="tagItem.closable || formItem.closable"
            :disable-transitions="tagItem.transitions"
            :size="tagItem.size"
            :effect="tagItem.effect"
            :type="tagItem.type"
            @close="tagClose(tagItem.id,formItem.tagData)"
          >{{ tagItem.name }}
          </el-tag>
          <el-tag v-if="!!(Object.keys(formItem.addTagBtnOption).length)" :size="formItem.addTagBtnOption.size" class="add-tag-btn" @click="addTag(formItem.tagData)">{{ formItem.addTagBtnOption.name }}</el-tag>
        </div>
        <!-- 图片标签 -->
        <div v-if="formItem.type === 'imageTag'" class="image-tag-fields-box">
          <div v-for="tagItem in formItem.tagData" :key="tagItem.id" class="img-item-wrap">
            <i v-if="!formItem.noDeleteIcon" class="el-icon-error delete-icon" @click="imageTagClose(tagItem.id,formItem.tagData)" />
            <div class="img-item" @click="uploadPreview(tagItem)"><img :src="tagItem.url" alt="暂无图片"></div>
            <span>{{ tagItem.name }}</span>
          </div>
          <div class="img-item-wrap add-item" @click="addImgTag(formItem.tagData)">
            <div class="img-item"> <span><i class="el-icon-plus add-btn" /></span></div>
          </div>
        </div>
        <!-- 自定义表单域 -->
        <div v-if="formItem.type === 'customSlot'" class="custom-slot-fields">
          <slot :name="`${formItem.prop}Slot`" />
        </div>
        <!--  upload 上传  -->
        <el-upload
          v-if="formItem.type === 'upload'"
          :ref="'uploadref'"
          :action="formItem.action"
          :headers="formItem.header"
          :multiple="formItem.multiple"
          :data="formItem.data"
          :name="formItem.name || 'file'"
          :with-credentials="formItem.withCredentials || false"
          :show-file-list="formItem.showFileList || true"
          :drag="formItem.drag || false"
          :accept="formItem.accept"
          :on-preview="formItem.onPreview || uploadPreview"
          :on-remove="formItem.onRemove"
          :on-success="formItem.onSuccess"
          :on-error="formItem.onError"
          :on-progress="formItem.onProgress"
          :on-change="formItem.onChange"
          :before-upload="formItem.beforeUplaod"
          :before-remove="formItem.beforeRemove"
          :list-type="formItem.listType || 'picture-card'"
          :auto-upload="formItem.autoUpload || true"
          :file-list="formData[formItem.prop] || []"
          :http-request="formItem.httpRequest"
          :disabled="formItem.disabled || false"
          :limit="formItem.limit"
          :on-exceed="formItem.onExceed"
        >
          <i class="el-icon-plus" />
          <!-- upload 插槽 -->
          <template #trigger>
            <slot :name="`${formItem.prop}Trigger`" />
          </template>
          <template #tip>
            <slot :name="`${formItem.prop}Tip`" />
          </template>
        </el-upload>
        <!-- 按钮 -->
        <div v-if="formItem.type === 'button'" class="button-flex">
          <div v-for="(buttonItem, buttonIndex) in formItem.children" :key="buttonIndex" class="button-child-flex">
            <button-comp
              :button-item="buttonItem"
              v-on="{
                reset, confirm, cancel, search, exportFile
              }"
            />
          </div>
        </div>
      </el-form-item>
    </el-form>

    <!-- 图片预览弹窗 -->
    <el-dialog :visible.sync="uploadDialogVisible" top="7vh" :append-to-body="true">
      <img width="100%" :src="dialogImageUrl" alt="">
    </el-dialog>
  </div>
</template>

<script>
import buttonComp from './buttonComp'
export default {
  components: {
    buttonComp,
  },
  props: {
    formLabelWidth: { // 表单label宽度
      type: String,
      default: '200px',
    },
    formData: { // 表单数据
      type: Object,
      default: null,
    },
    formDisabled: { // 是否禁用表单
      type: Boolean,
      default: false,
    },
    formRules: { // 表单验证规则
      type: Object,
      default: null,
    },
    // 设置json数据
    formFields: { // 表单项配置信息
      type: Array,
      default: () => [],
    },
    inline: { // 表单横向竖向控制
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      dialogImageUrl: '',
      uploadDialogVisible: false,
    }
  },
  methods: {
    reset() {
      this.$refs['form'].resetFields()
      this.$emit('reset')
    },
    exportFile(buttonItem) {
      this.$emit('exportFile', buttonItem)
    },
    cancel() {
      this.$emit('cancel')
    },
    confirm() {
      this.$refs['form'].validate((valid) => {
        if (valid) {
          // 传事件到父亲组件
          this.$emit('confirm')
        } else {
          return false
        }
      })
    },
    // 下拉
    selectChange(val, prop) {
      this.$emit(`select${prop}`, val)
    },
    // switch切换
    switchChange(val, prop) {
      this.$emit(`switch${prop}`, val)
    },
    // 选中
    checkChange(val, prop) {
      this.$emit(`check${prop}`, val)
    },
    // 标签关闭事件
    tagClose(id, tagData) {
      const curIndex = tagData.findIndex((tagItem, index) => tagItem.id === id)
      tagData.splice(curIndex, 1)
      this.$emit('tagClose', id)
    },
    // 图片标签关闭事件
    imageTagClose(id, tagData) {
      const curIndex = tagData.findIndex((tagItem, index) => tagItem.id === id)
      tagData.splice(curIndex, 1)
      this.$emit('imgTagClose', id)
    },
    addTag(tagData) {
      this.$emit('addTagBtn', tagData)
    },
    addImgTag(tagData) {
      this.$emit('addImgTagBtn', tagData)
    },
    // 查询
    search() {
      this.$emit('search')
    },
    // 上传图片预览
    uploadPreview(file) {
      this.dialogImageUrl = file.url
      this.uploadDialogVisible = true
    },
  },
}
</script>
<style lang="scss" scoped>
  .button-flex {
    display: flex;
    flex-wrap: wrap;
    text-align: center;
  }
  .button-child-flex {
    flex: 1;
    margin-right: 10px;
  }
  .date-zone {
    margin-bottom: 20px;
  }
  .tag-fields{
    display: flex;
    flex-wrap: wrap;
    /deep/.el-tag{
      margin: 0 10px 10px 0;
      &.add-tag-btn{
        color: #606266;
        background: #ffffff;
        border-color: #DCDFE6;
      }
      &.add-tag-btn:hover {
        color: #409EFF;
        border-color: #c6e2ff;
        background-color: #ecf5ff;
        cursor:pointer;
      }
    }
  }
</style>

文件2 CommomForm/buttonComp.vue

<template>
  <el-button
    v-if="haveBtn(buttonItem.type)"
    v-show="!buttonItem.hidden"
    :size="buttonItem.size || 'medium'"
    :type="buttonItem.btnType || itemOption.type"
    :plain="buttonItem.plain || false"
    :round="buttonItem.round || false"
    :circle="buttonItem.circle || false"
    :loading="buttonItem.loading || false"
    :disabled="buttonItem.disabled || false"
    :icon="buttonItem.icon || itemOption.icon"
    @click="btnClick"
  >
    {{ buttonItem.btnText || itemOption.text }}
  </el-button>
</template>

<script>
export default {
  name: 'ButtonComp',
  props: {
    buttonItem: {
      type: Object,
      required: true,
    },
  },
  data() {
    return {
      defaultOption: { // 按钮默认配置
        reset: {
          icon: 'el-icon-refresh-left', type: 'warning', text: '重 置', emit: 'reset',
        },
        confirm: {
          icon: 'el-icon-circle-check', type: 'primary', text: '确 定', emit: 'confirm',
        },
        cancel: {
          icon: 'el-icon-circle-close', type: '', text: '取 消', emit: 'cancel',
        },
        search: {
          icon: 'el-icon-search', type: 'primary', text: '查 询', emit: 'search',
        },
        export: {
          icon: 'el-icon-folder-opened', type: 'info', text: '导 出', emit: 'exportFile',
        },
      },
    }
  },
  computed: {
    itemOption() {
      return this.defaultOption[this.buttonItem.type]
    },
  },
  methods: {
    haveBtn(type) {
      return this.defaultOption.hasOwnProperty(type)
    },
    btnClick() {
      this.$emit(this.itemOption.emit, this.buttonItem)
    },
  },
}
</script>

api/commonTable.js

import request from '@/utils/request'
import qs from 'qs'

export function apiRequest(url, params, method) {
  return request({
    url: url,
    method: method || 'get',
    params,
  })
}

export function apiQsRequest(url, params, method) {
  console.log(params, 7878);
  return request({
    url: url,
    method: method || 'get',
    params: {
      query: params,
    },
  })
}

export function exportFile(url, params) {
  return request({
    responseType: 'blob',
    headers: {
      'Content-Type': 'application/json',
    },
    timeout: 1000 * 60,
    url: url,
    method: 'get',
    params: {
      query: qs.stringify(params),
    },
  })
}

记得npm install qs --save哦,因为传过去可能是数组,所以直接qs过去拉~
任何不会用,在下面留言吧~ 467015242,我的微信,备注: table组件使用,这样我就来看看你留言了什么,一般不会通过

原文地址:https://www.cnblogs.com/antyhouse/p/13448558.html