其他分享
首页 > 其他分享> > 封装文件导入组件,含导入进度条

封装文件导入组件,含导入进度条

作者:互联网

需求

系统中需要有多个文件导入的地方,因此需要把它封装为组件便于复用。

问题是:每次的导入 url 不同,每次封装的导入接口应该在主页面用呢?还是在组件页面用?

解决办法

分析:其实这里进入了一个误区———每个接口 url 都应该封装成一个接口。完全可以不同的接口地址都封装成一个接口,只要它们的请求方法和数据格式相同。

因此,解决办法就是:把不同的导入地址以字符串形式传给组件,组件调用相同的接口,只需在接口中设置不同的 url 即可。

主页面 HTML
        <el-col :span="1.5">
          <!-- 导入按钮 -->
          <import
            :para-name="paraName"
            :upload-url="uploadUrl"
            :show-file-list="showFileList"
            :file-type="fileType"
            :btn-permi="btnPermi"
            @refreshList="loadxxxxList"
            :progress-url="progressUrl"
          />
        </el-col>
主页面 data 声明
      // 是否显示文件列表
      showFileList: false,
      // 文件类型
      fileType: ['zip'],
      // 按钮权限
      btnPermi: ['xxxx:xxx:xxxx'],
      // 上传文件的参数名称
      paraName: 'zipFile',
      // 上传文件地址
      uploadUrl: "/xxxx/uploadFile",
      // 查询导入进度地址
      progressUrl: "/xxxx/progress",

导入组件 Import/index.vue HTML
  <div class="import">
    <!-- 导入进度组件  -->
    <el-progress
      v-if="progressUrl && isUploading"
      class="upload-progress"
      type="circle"
      :percentage="percentage"
      :status="percentage === 100 ? 'success' : undefined"
      :format="format"
    />
    <!-- 导入组件 -->
    <el-upload
      element-loading-text=""
      element-loading-spinner="null"
      v-loading.fullscreen.lock="progressUrl && isUploading"
      element-loading-background="rgba(0, 0, 0, 0.8)"
      class="upload-file"
      ref="upload"
      :action="uploadUrl"
      :http-request="uploadFile"
      :show-file-list="showFileList"
      multiple
      :accept="acceptText"
      :on-exceed="handleExceed"
      :before-upload="beforeUpload"
      :limit="limitNum"
      :file-list="fileList"
      >
        <el-button type="warning" plain icon="el-icon-upload2" size="mini">导入</el-button>
        <div slot="tip" class="el-upload__tip">请导入 <b>{{ fileType.join('、') }}</b> 文件</div>
    </el-upload>
  </div>
导入组件 Import/index.vue JS
import { importFile } from './importFile'
import { queryProgress } from './progress'

export default {
  name: 'Import',
  data() {
    return {
      // 导入的文件
      file: {},
      // 导入成功的文件列表
      fileList: [],
      // 正在导入状态
      isUploading: false,
      // 导入进度的百分比数字
      percentage: 0,
      // 轮询定时器
      loopTimer: ''
    }
  },
  props: {
    // 导入文件的参数名称
    paraName: {
      type: String,
      default: 'file'
    },
    // 导入文件的地址
    uploadUrl: {
      type: String,
      required: true
    },
    // 是否显示文件列表
    showFileList: {
      type: Boolean,
      default: true
    },
    // 文件类型
    fileType: {
      type: Array,
      default: ['zip']
    },
    // 文件大小
    fileSize: {
      type: Number,
      default: 10
    },
    // 导入文件数量
    limitNum: {
      type: Number,
      default: 20
    },
    // 按钮权限
    btnPermi: {
      type: Array
    },
    // 查询导入进度的地址:若无,表示不加载导入进度
    progressUrl: {
      type: String
    }
  },
  computed: {
    acceptText() {
      return this.fileType.map(ele => '.' + ele).join(',')
    }
  },
  methods: {
    // 进度条内文字
    format(percentage) {
      let text = percentage === 100 ? '导入成功' : '导入中'
      return percentage + '%\n' + text
    },
    /* 自定义导入文件 */
    async uploadFile(param) {
      this.file = param.file
      this.isUploading = true
      const formData = new FormData()
      formData.append(this.paraName, this.file)
      if (!this.uploadUrl) { return console.log('uploadUrl 为空') }
      const data = await importFile(formData, this.uploadUrl)
      if (data.code === 200) {
        // 轮询导入进度
        if (this.progressUrl) {
          await this.loopProgress()
        } else {
          this.$message.success('上传成功')
          this.isUploading = false
          this.percentage = 0
          this.fileChange(this.file)
          this.$emit('refreshList')
        }
      } else {
        this.isUploading = false
        this.percentage = 0
        this.$message.error('上传失败')
      }
    },
    /* 导入文件改变触发的函数 */
    fileChange(file){
      this.$refs.upload.clearFiles()
      this.fileList = [{name: file.name, zipFile: file}]
    },
    /* 校验导入文件的数量 */
    handleExceed(files, fileList) {
      this.$message.warning(`当前限制选择 ${this.limitNum} 个文件,本次选择了 ${files.length} 个文件,共选择了 ${files.length + fileList.length} 个文件`);
    },
    /* 导入文件前校验格式和大小 */
    beforeUpload(file) {
      const fileSuffix = file.name.substring(file.name.lastIndexOf('.') + 1)
      const isType = this.fileType.includes(fileSuffix)
      const isLimit = file.size / 1024 / 1024 <= this.fileSize
      if (!isType) {
        this.$message.error(`导入文件只能是 ${this.fileType.join('、')} 格式`);
      }
      if (!isLimit) {
        this.$message.error(`导入文件大小不能超过 ${this.fileSize}MB`);
      }
      return isType && isLimit
    },
    /* 查询导入进度 */
    async getProgress() {
      const data = await queryProgress(this.progressUrl)
      if (data.code === 200) {
        this.percentage = data.data.percent
      } else {
        this.$message.error('查询进度失败')
      }
    },
    /* 轮询导入进度 */
    loopProgress() {
      this.loopTimer = window.setInterval(() => {
        setTimeout(async () => {
           // 调用接口
          this.getProgress()
          if (this.percentage === 100) {
            clearInterval(this.loopTimer)
            this.$message.success('导入成功')
            await this.delay(1000)
            this.isUploading = false
            this.percentage = 0
            this.fileChange(this.file)
            this.$emit('refreshList')
          }
        }, 0)
      }, 100)

    },
    /* 延迟 */
    delay(num) {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve()
        }, num)
      })
    }
  }
}
导入组件 Import/importFile.js
import request from '@/utils/request'

// 导入文件
export const importFile = (data, importUrl) => {
  return request({
    url: importUrl,
    method: 'post',
    data,
    headers:{"Content-Type" : "multipart/form-data;boundary ="+ new Date().getTime()},
  })
}
导入组件 Import/progress.js

export const queryProgress = (progressUrl) => {
  return request({
    url: progressUrl,
    method: 'get'
  })
}

总结

  1. 传递接口地址给组件,提高封装组件的灵活性;
  2. el-upload 组件,采用 http-request 自定义上传方式。上传接口是数据格式是 multipart/form-data 时,要单独设置 Content-Type,传参也要转为 formdata 格式,formData.append(参数名, this.file)
  3. el-progress 自定义显示数据使用 format属性;
  4. 轮询导入进度:window.setInterval(() => {setTimeout(() => {// 调用接口}, 0)}, 1000)。清除定时器:clearInterval(定时器名称)

标签:文件,封装,进度条,导入,file,组件,percentage,type
来源: https://www.cnblogs.com/shayloyuki/p/16623692.html