el-upload组件一次上传多个文件只调一次后台接口

1、目前的element的上传组件el-uload支持同时上传多个文件的功能,但是是一个文件上传需要后台调一次接口,这样是严重消耗性能的,不符合科学性。

2、于是针对目前在做的项目,自行封装一个组件,达到只调一次接口就可以上传多个文件,前提是接口支持多文件同时上传到服务器。

3、封装的一个弹框组件,代码如下:

<!--支持多文件上传 -->
<template>
  <div>
    <el-dialog
      v-el-drag-dialog
      append-to-body
      :visible.sync="uploadVisible"
      title="Upload"
      width="600px"
      :before-close="cancelForm"
    >
      <el-form
        ref="form"
        :model="uploadPayload"
        :rules="rules"
        size="small"
        label-width="85px"
        label-position="right"
      >
        <el-row :gutter="20">
          <el-col :xs="24" :sm="24" :lg="24">
            <el-form-item :label="$t('steamship.selectFiles')+ ':'" prop="name">
              <span slot="label">
                <span style="color:red">*</span>
                <span>{{ $t('steamship.selectFiles')+ ':' }}</span>
              </span>
              <el-upload
                ref="upload"
                class="upload-demo"
                :action="`${urlPrefix}${uploadUrl}`"
                :on-change="handleUploadChange"
                :on-remove="remove"
                :headers="headers"
                :with-credentials="true"
                :file-list="fileList"
                :data="uploadPayload"
                :auto-upload="false"
                multiple
              >
                <el-button type="primary"><em class="el-icon-folder" />
                    {{ $t('steamship.selectFiles') }}
                </el-button>
                <div slot="tip" class="el-upload__tip">{{ uploadTip }}</div>
              </el-upload>
              <span v-if="showRemind" style="color:#ff4949">{{ remindTips }}</span>
            </el-form-item>
          </el-col>
        </el-row>
        <el-row :gutter="20">
          <template v-if="ifHaveFilter">
            <el-col :xs="24" :sm="24" :lg="12">
              <el-form-item 
                :label="$t('steamship.selectVesselType')+ ':'"
                prop="vesselType"
              >
                <el-select 
                    v-model="uploadPayload.vesselType" 
                    multiple 
                    placeholder="Select vessel type"
                >
                  <el-option
                    v-for="item in enums.vesselTypeEnum"
                    :key="item.code"
                    :label="item.name"
                    :value="item.code"
                  />
                </el-select>
              </el-form-item>
            </el-col>
            <el-col :xs="24" :sm="24" :lg="12">
              <el-form-item :label="$t('steamship.selectFileType')+ ':'" prop="type">
                <el-select v-model="uploadPayload.type" placeholder="Select report type">
                  <el-option
                    v-for="item in enums.reportTypeEnum"
                    :key="item.code"
                    :label="item.name"
                    :value="item.code"
                  />
                </el-select>
              </el-form-item>
            </el-col>
          </template>
          <el-col v-if="fromSource !== 'reportParsing'" :xs="24" :sm="24" :lg="24">
            <el-form-item label="备注信息:" prop="remark">
              <el-input
                v-model="uploadPayload.remark"
                type="textarea"
                placeholder="请输入备注信息"
                maxlength="300"
                :autosize="{ minRows: 3, maxRows: 5}"
                show-word-limit
              />
            </el-form-item>
          </el-col>
        </el-row>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button plain size="small" @click="cancelForm">取消</el-button>
        <el-button 
            :loading="loading" 
            type="primary" 
            style="margin-left: 10px;" 
            size="small" 
            @click="submitUpload"
        >确定</el-button>
      </div>
    </el-dialog>
  </div>
</template>

<script>
import { upload } from '@/api/upload'
import { getDictionary, Es6duplicate } from '@/utils/commonTools'
import { getToken } from '@/utils/auth'
import store from '@/store'
import { getWXReportList, getRepParsList } from '@/api/system/report'
const reportParsingTip = 'Only .xls / .xlsx / .csv files can be uploaded, and no more than 20MB'
const otherTip = 'Only PDF / Excel / Docoment files can be uploaded, and no more than 20MB'
export default {
  props: {
    total: {
      type: Number,
      default: 10
    },
    fromSource: {
      type: String,
      default: ''
    },
    openDialog: {
      type: Boolean,
      default: false
    },
    ifHaveFilter: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      loading: false,
      uploadTip: '',
      uploadUrl: '',
      showRemind: false,
      remindTips: '',
      uploadVisible: false,
      headers: {
        Authorization: getToken()
      },
      urlPrefix: process.env.VUE_APP_BASE_API,
      fileList: [],
      uploadPayload: {
        vesselType: '',
        type: '',
        remark: ''
      },
      rules: {
        vesselType: [{ required: true, message: '请选择船型', trigger: 'blur' }],
        type: [{ required: true, message: '请选择报表类型', trigger: 'blur' }]
      },
      enums: {
        vesselTypeEnum: [],
        reportTypeEnum: []
      },
      totalList: [],
      timer: null
    }
  },
  watch: {
    fromSource: {
      handler(newVal) {
        if (newVal === 'reportParsing') {
          this.uploadTip = reportParsingTip
          this.uploadUrl = '/parse/fileUpload'
          this.uploadPayload = {
            file: ''
          }
        } else {
          this.uploadTip = otherTip
          this.uploadUrl = '/report/wxupload'
          this.uploadPayload = {
            vesselType: '',
            type: '',
            remark: ''
          }
        }
      }, immediate: true
    },
    openDialog(visible) {
      if (visible) {
        this.uploadVisible = visible
      }
    }
  },
  created() {
    this.getEnums()
  },
  methods: {
    getEnums() {
      const enums = getDictionary(
        { 
        vesselTypeEnum: 'vessel type', reportTypeEnum: 'report type' 
        }
      )
      Object.assign(this.enums, enums)
    //这里是项目中会用的字典映射
    },
    // 上传弹框确认按钮
    async submitUpload() {
      console.log('fileList', this.fileList)
      this.showRemind = false
      // 如果没有选择文件时
      if (this.fileList.length === 0) {
        this.showRemind = true
        this.remindTips = '请选择附件'
        return
      }
      // 如果上传的文件格式或者文件大小不符合要求时
      if (!this.beforeAvatarUpload()) return
      // 如果上传的文件已经存在于服务器时
      if (await this.ifFileNameExist(this.fileList)) return
      this.$refs.form.validate(valid => {
        if (valid) {
          const formData = new FormData() //  用FormData存放上传文件
          if (this.fromSource === 'reportParsing') {
            this.fileList.forEach(file => {
              formData.append('part', file.raw)
              formData.append('file', file.name)
            })
          } else {
            this.fileList.forEach(file => {
              formData.append('part', file.raw)
            })
            if (!this.ifHaveFilter) {
              this.uploadPayload.type = 2
              this.uploadPayload.vesselType = store.state.settings.vesselType
            }
            formData.append('vesselType', this.uploadPayload.vesselType)
            formData.append('type', this.uploadPayload.type)
            formData.append('remark', this.uploadPayload.remark)
          }
          // this.$refs.upload.submit()
          this.loading = true
          upload(this.uploadUrl, formData).then(res => {
            this.onSuccess(res)
            this.loading = false
          }).catch(errs => {
            this.onFail(errs)
            this.loading = false
          })
          this.$refs.form.resetFields()
        }
      })
    },
    // 如果上传的文件已经存在于服务器时
    async ifFileNameExist(files) {
      const params = {
        pageNo: 1,
        pageSize: this.total
      }
      const fn = this.fromSource === 'reportParsing' ? getRepParsList : getWXReportList
      this.loading = true
      await fn(params).then(res => {
        this.totalList = res.data.dataList
        this.loading = false
      }).catch(errs => {
        this.loading = false
      })
      const arr = []
      files.map(item => {
        if (this.totalList.some(sonItem => sonItem.fileName === item.name)) {
          arr.push(item.name)
        }
      })
      if (arr.length) {
        this.showRemind = true
        const str = arr.join('、')
        this.remindTips = `${str} 在服务器中已经存在!`
      }
      return arr.length
    },
    // 上传弹框取消按钮
    cancelForm() {
      this.uploadVisible = false
      this.showRemind = false
      this.$refs.upload.clearFiles()
      this.$refs.form.resetFields()
      this.$emit('childComCallBack', false, 'cancel')
    },
    // 文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用
    handleUploadChange(file, fileList) {
      this.showRemind = false
      this.fileList = fileList
      // 上传文件去重
      if (this.fileList.length > 1) {
        this.fileList = Es6duplicate(this.fileList, 'name')
      }
    },
    // 文件列表移除文件时的钩子
    remove(file, fileList) {
      this.fileList = fileList
    },
    // 上传失败
    onFail() {
      this.$message.error('Upload failed')
      return false
    },
    // 上传成功
    onSuccess(result, file, fileList) {
      let type = ''
      if (result.code === 0) { // 上传文件全部成功
        this.uploadVisible = false
        this.$message({
          message: result.message,
          type: 'success'
        })
        this.$refs.upload.clearFiles()
        this.fileList = []
        type = 'success'
      } else if (result.code === 200) { // 同时上传多个文件,出现有成功的、有失败的情况
        this.uploadVisible = false
        this.$message({
          message: result.message,
          type: 'warning'
        })
        this.$refs.upload.clearFiles()
        this.fileList = []
        type = 'success'
      } else { // 上传文件全部失败
        this.$message({
          message: result.message,
          type: 'error'
        })
        this.fileList = []
        type = 'error'
      }
      this.$emit('childComCallBack', false, type)
    },
    // 上传文件之前的钩子,参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传。
    beforeAvatarUpload() {
      const typeArr = []
      const sizeArr = []
      this.fileList.forEach(item => {
        if (item.name.endsWith('.pdf')) {
          typeArr.push('isPDF')
        } else if (item.name.endsWith('.csv') || item.name.endsWith('.CSV')) {
          typeArr.push('isCSV')
        } else if (item.name.endsWith('.xlsx') || item.name.endsWith('.xls')) {
          typeArr.push('isExcel')
        } else if (item.name.endsWith('.docx') || item.name.endsWith('.doc')) {
          typeArr.push('isWord')
        } else {
          typeArr.push('other')
        }
        sizeArr.push(item.size / 1024 / 1024 < 20)
      })
      let ifHasTrue = false
      let isCheckOK = true
      if (this.fromSource === 'reportParsing') { // 文件解析只支持xsl,xslx,csv,CSV
        ifHasTrue = typeArr.every(item => {
          return ['isExcel', 'isCSV'].some(e => e === item)
        })
      } else { // 文件管理除了csv,CSV外,其他的都支持
        ifHasTrue = typeArr.every(item => {
          return ['isPDF', 'isExcel', 'isWord'].some(e => e === item)
        })
      }
      // 上传的文件格式不符合要求时
      if (!ifHasTrue) {
        this.showRemind = true
        this.remindTips = this.fromSource === 'reportParsing' ? reportParsingTip : otherTip
        isCheckOK = false
      }
      if (sizeArr.some(item => !item)) {
        this.showRemind = true
        this.remindTips = 'The size of the uploaded file cannot exceed 20MB!'
        isCheckOK = false
      }
      return isCheckOK
    }
  }
}
</script>

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

4、用到了我所在项目的数据字典映射,这个可以依情况是否需要使用,接口也是根据自己项目而定,其中的逻辑可以用来参考。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值