<el-upload>自定义上传事件后,进度条不显了。。本文带你了解如何自定义进度条

4,364 阅读2分钟
「这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战

最近有个任务,写个上传页面,是个后台页面,所以首选用Element-UI中的<el-upload>

因为需要分片上传,所以自定义了上传事件http-request,但是跟后台联调的时候发现怎么file-list列表中的进度条怎么没有了...., 刚好UI给的效果图跟upload给出的有点区别,那就自定义一下好了。

1. 页面部分代码

  • 隐藏默认的file-list列表
  • 声明自定义上传事件http-request
<el-upload
    class="upload-demo"
    ref="upload"
    action="/"
    multiple //多文件上传
    :action="uploadUrl" //文件上传的地址(必填)
    :show-file-list="false" //自定义样式所以设置false不显示
    :file-list="fileArr"//文件列表
    :http-request="checkedFile" //自定义上传事件
    :before-upload="beforeUploadFile" //文件上传前
    :on-progress="uploadFileProcess" //文件传输过程(处理进度条)
    :on-success="handleFileSuccess" //文件成功>
        <el-button size="small" type="primary">上次文件</el-button>
</el-upload>

2.js部分代码

// 格式化文件大小显示文字
 getSize(size) {
    return size > 1024
      ? size / 1024 > 1024
        ? size / (1024 * 1024) > 1024
          ? (size / (1024 * 1024 * 1024)).toFixed(2) + 'GB'
          : (size / (1024 * 1024)).toFixed(2) + 'MB'
        : (size / 1024).toFixed(2) + 'KB'
      : size.toFixed(2) + 'B'
  },
async checkedFile(options) {
    const { maxSize, multiUploadSize, getSize, splitUpload, singleUpload } =
      this
    const { file, onProgress, onSuccess, onError } = options
    console.log('options', options, this)
    if (file.size > maxSize) {
      return this.$message({
        message: `您选择的文件大于${getSize(maxSize)}`,
        type: 'error',
      })
    }
    // 大于指定大小切片上传
    const uploadFunc = file.size > multiUploadSize
        ? splitUpload
        : singleUpload
    try {
      let info = await uploadFunc(file, onProgress)
      this.$message({
        message: info.msg || '上传成功',
        type: 'success',
      })
      onSuccess()
    } catch (e) {
      console.error('error', e)
      onError()
    }
    const prom = new Promise((resolve, reject) => {})
    prom.abort = () => {}
    return prom
  },
// 大文件分块上传
  splitUpload(file, onProgress) {
    return new Promise(async (resolve, reject) => {
      try {
        const { eachSize } = this
        const chunks = Math.ceil(file.size / eachSize)
        const fileChunks = await this.splitFile(file, eachSize, chunks)
        let currentChunk = 1
        let cbRes = []
        for (let i = 1; i <= fileChunks.length; i++) {
          // 服务端检测已经上传到第currentChunk块了,那就直接跳到第currentChunk块,实现断点续传
          console.log('before', currentChunk, i)
          if (currentChunk === i) {
            // 每块上传完后则返回需要提交的下一块的index
            let postRes = await this.postFile(
              {
                // chunked: true,
                chunk: i,
                chunks,
                // eachSize,
                name: file.name,
                totalSize: file.size,
                // uid: file.uid,
                model: this.model,
                file: fileChunks[i - 1],
              },
              onProgress
            )
            // 根据实际接口格式判断是否续传
            let status = postRes.key
            if (status === 'continue') {
              currentChunk = postRes.value
            } else if (status === 'hasValue') {
              currentChunk = postRes.value + 1
            } else if (status === 'success') {
              currentChunk = postRes.value
              cbRes = postRes
            } else {
              currentChunk = postRes.value
            }
            console.log('after currentChunk', currentChunk)
          }
        }
        resolve(cbRes)
      } catch (e) {
        reject(e)
      }
    })
  },
  // 文件分块,利用Array.prototype.slice方法
  splitFile(file, eachSize, chunks) {
    return new Promise((resolve, reject) => {
      try {
        setTimeout(() => {
          const fileChunk = []
          for (let chunk = 0; chunks > 0; chunks--) {
            fileChunk.push(file.slice(chunk, chunk + eachSize))
            chunk += eachSize
          }
          resolve(fileChunk)
        }, 0)
      } catch (e) {
        console.error(e)
        reject(new Error('文件切块发生错误'))
      }
    })
  },
// 提交文件方法,将参数转换为FormData, 然后通过axios发起请求
  postFile(param, onProgress) {
    const formData = new FormData()
    for (let p in param) {
      formData.append(p, param[p])
    }
    const { requestCancelQueue } = this
    const config = {
      cancelToken: new axios.CancelToken(function executor(cancel) {
        if (requestCancelQueue[param.uid]) {
          requestCancelQueue[param.uid]()
          delete requestCancelQueue[param.uid]
        }
        requestCancelQueue[param.uid] = cancel
      }),
      onUploadProgress: (e) => {
        console.log('onUploadProgress', e, param, this)
        if (param.chunked) {
          e.percent = Number(
            (
              ((param.chunk * (param.eachSize - 1) + e.loaded) /
                param.fullSize) *
              100
            ).toFixed(2)
          )
        } else {
          e.percent = Number(((e.loaded / e.total) * 100).toFixed(2))
        }
        onProgress(e)
      },
    }
    // 实际上传事件
    if (this.fileType === 0) {
      return uploadStructTree(formData, config).then((rs) => rs)
    } else if (this.fileType === 2) {
      return uploadTheory(formData, config).then((rs) => rs)
    } else if (this.fileType === 3) {
      return uploadDmo(formData, config).then((rs) => rs)
    } else {
      return upload3DToBos(formData, config).then((rs) => rs)
    }
  },