ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2022-08-25 11:30:29  阅读:136  来源: 互联网

标签:文件 封装 进度条 导入 file 组件 percentage type


需求

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

问题是:每次的导入 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

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有