封装进度条onUploadProgress+axios取消请求的上传组件

目录

定时模拟进度条

方法

A.axios

B.xhr

取消请求

完整代码

A.自定义上传组件

B.二次封装组件

情况

增加cancelToken不生效,刷新页面

进度条太快->设置浏览器网速


定时模拟进度条

javascript 复制代码
    startUpload() {
        if (!this.file) return;

        const totalSize = this.file.size;
        let uploadedSize = 0;

        const interval = setInterval(() => {
            if (uploadedSize >= totalSize) {
                clearInterval(interval);
                // this.state_tip = STATE_TIPS.get('上传成功');
            } else {
                uploadedSize += 1024;
                this.progress = Math.round((uploadedSize / totalSize) * 100);
            }
        }, 200);
    }

方法

A.axios

javascript 复制代码
 uploadQuery() {
        if (!this.file) return;
        this.state_tip = STATE_TIPS.get('上传中');
        this.progress = 0;
        // headers = {'Content-Type': 'multipart/form-data'}
        const formData = new FormData()
        formData.append('file', this.file)
        axios.post(this.uploadPath, formData, {
            headers: {
                "X-Requested-With": "XMLHttpRequest",
            },
            onUploadProgress: (progressEvent: ProgressEvent) => {
                console.log("onUploadProgress");
                if (progressEvent.lengthComputable) {
                    this.progress = Math.round(
                        (progressEvent.loaded / progressEvent.total) * 100
                    );
                    console.log(this.progress);
                }
            },
        }).then((res: any) => {
            if (res && res.code == 200) {
                this.uploadExel = res.data;
                this.state_tip = STATE_TIPS.get('上传成功');
                console.log(this.uploadExel);
                this.$emit("update:uploadExel", this.uploadExel);
            } else {
                this.state_tip = STATE_TIPS.get('其他');
                this.state_tip.tip = res.msg || '请取消上传,更换符合模板要求的文件';
            }
        }).catch((error: any) => {
            this.state_tip = STATE_TIPS.get('上传失败');
        }).finally(() => {
            this.uploaded = true;
            this.$emit("update:uploaded", this.uploaded);
        });
    }

B.xhr

javascript 复制代码
   uploadQuery(file: File) {
        // headers = {'Content-Type': 'multipart/form-data'}
        const formData = new FormData()
        formData.append('file', file)
        const xhr = new XMLHttpRequest();
        xhr.open("POST", this.uploadPath, true);

        xhr.upload.onprogress = (event) => {
            if (event.lengthComputable) {
                this.uprogress = Math.round((event.loaded / event.total) * 100);
            }
        };

        xhr.onload = () => {
            console.log(xhr);
            
            if (xhr.status === 200) {
                const res = JSON.parse(xhr.responseText);
                console.log(res);
                console.log(res.code);
                if (res.code === 200) {
                    this.uploadExel = res.data;
                    this.state_tip = "上传成功";
                    this.uploaded = true;
                    console.log(this.uploadExel);
                    this.$emit("update:uploaded", this.uploaded);
                    this.$emit("update:uploadExel", this.uploadExel);
                } else {
                    // 处理上传失败情况
                    this.state_tip = "上传失败";
                }
            }
        };

        xhr.onerror = () => {
            // 处理上传出错情况
            this.state_tip = "上传出错";
        };

        xhr.send(formData);
        // request.post(this.uploadPath, formData).then((res: any) => {

        //     if (res.code == 200) {
        //         this.uploadExel = res.data;
        //         this.state_tip = STATE_TIPS.get('上传成功');
        //         this.uploaded = true;
        //         this.$emit("update:uploaded", this.uploaded);
        //         this.$emit("update:uploadExel", this.uploadExel);
        //     } else {

        //     }
        // })
    }

取消请求​​​​​​​

完整代码

javascript 复制代码
<UploadComp :uploadPath="PATH" :fileLogPath="PATH.replace('uploadExcel?wpReleId=','getOtherIndexFileLog/')" :uploaded.sync="uploaded" :uploadExel.sync="uploadExel" @cancelUpload="cancelUpload" />
            <!-- <SingleUploadComp :uploadPath="PATH" :uploaded.sync="uploaded" :uploadExel.sync="uploadExel" @cancelUpload="cancelUpload" /> -->

A.自定义上传组件

javascript 复制代码
<template>
    <div class="upload-list-dragger" :uploadPath="uploadPath" :fileLogPath="fileLogPath">
        <div v-if="!file" @click="openFileInput" @dragenter="onDragEnter" @dragover="onDragOver" @drop="onDrop"
            :class="{ 'drag-over': isDragOver }">
            <input type="file" ref="fileInput" style="display: none;" @change="onFileChange" :accept="format" />
            <div class="custom-drag-style">
                <img src="@/assets/img/upload.png" class="upload-icon" />
                <div class="upload-click-drag">点击或拖拽上传文件</div>
                <!-- 使用正则表达式替换所有点号 -->
                <div class="upload-tip">请上传{{ format.replace(/\./g, "") }}格式文件,上传多份文件时以最后一次为准</div>
            </div>
        </div>
        <div v-else class="custom-upload-card">
            <img class="upload-card-icon" src="@/assets/img/excel.png" />
            <div class="upload-card-state">
                <div>
                    <span class="file-name">{{ file.name }}</span>
                    <span class="cancel-upload" @click="cancelUpload"><mds-icon type="line-close" /></span>
                </div>
                <div class="progress-bar" :style="{ width: progress + '%', backgroundColor: state_tip.color }"></div>
                <div class="span-container">
                    <span :style="{ color: state_tip.state === '上传中' ? '#A8ACB3' : state_tip.color }">{{
                        state_tip.tip
                    }}</span>
                    <span v-if="state_tip.state === '上传中'">{{ progress + '%' }}</span>
                    <span v-if="state_tip.state === '上传失败'" class="span-operate" underline
                        @click="restartUpload">重新上传</span>
                    <span v-if="state_tip.state === '上传成功'" class="span-operate" underline
                        @click="downloadQuery">下载结果明细</span>
                </div>
            </div>
        </div>
    </div>
</template>
  
<script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator';
import request from '@/utils/request'
import axios, { Canceler } from 'axios';
const STATE_TIPS = new Map([['其他', { state: '其他', color: 'orange', tip: '' }], ['上传中', { state: '上传中', color: '#1564FF', tip: '文件上传解析中...' }], ['上传失败', { state: '上传失败', color: '#DD2100', tip: '上传失败,请重新上传' }], ['上传成功', { state: '上传成功', color: '#00AF5B', tip: '上传成功!' }]])
@Component({
    components: {}
})
export default class UploadComp extends Vue {
    @Prop({ required: true }) private uploadPath!: string
    @Prop({ required: true }) private fileLogPath!: string
    @Prop({ default: '.xls' }) private format!: string //形如".xls,.csv,.xlsx"

    uploadExel: any = {
        succList: []
    }
    uploaded: boolean = false;

    file: File | null = null;
    source = axios.CancelToken.source();

    progress = 0;
    isDragOver = false;
    data() {
        return {
            state_tip: {},
            
        }
    }
    created() {
        console.log(this.fileLogPath);
    }
    onFileChange(event: Event) {
        const target = event.target as HTMLInputElement;
        this.fileValidator(target.files);//可能为null
    }
    fileValidator(files: FileList | undefined | null) {
        if (files && files.length > 0) {
            // 上传多份文件时以最后一次为准
            const file = files[0];
            if (this.isValidFormat(file)) {
                this.file = file;
                console.log(this.file);
                this.uploadQuery();
            } else {
                alert(`请上传${this.format.replace(/\./g, "")}格式文件。`);
            }
        } else {
            alert(`请上传文件!`);
        }
    }
    uploadQuery() {
        if (!this.file) return;
        this.state_tip = STATE_TIPS.get('上传中');
        this.progress = 0;
        // headers = {'Content-Type': 'multipart/form-data'}
        const formData = new FormData()
        formData.append('file', this.file)
        // 在合适的地方定义取消令牌和取消函数
        const CancelToken = axios.CancelToken;

         // 判断上一次的请求是否还在继续,如果还在继续,则取消上一次的请求
       if(this.source.token._listeners!=undefined )
        {
            this.source.cancel("取消请求")
            this.source = axios.CancelToken.source()
        }

        request.post(this.uploadPath, formData, {
            onUploadProgress: (progressEvent: ProgressEvent) => {
                console.log("Upload progress:", progressEvent);
                this.progress = Math.round((progressEvent.loaded / progressEvent.total) * 100);
                console.log("进度:", this.progress);
            },
            cancelToken:this.source.token,
        }).then((res: any) => {
            if (res && res.code == 200) {
                this.uploadExel = res.data;
                this.state_tip = STATE_TIPS.get('上传成功');
                console.log(this.uploadExel);
                this.$emit("update:uploadExel", this.uploadExel);
                this.uploaded = true;
                this.$emit("update:uploaded", this.uploaded);
            } else {
                this.state_tip = STATE_TIPS.get('其他');
                this.state_tip.tip = res.msg || '请取消上传,更换符合模板要求的文件';
            }
        }).catch((error: any) => {
            this.state_tip = STATE_TIPS.get('上传失败');
        })
    }
    downloadQuery() {
        request.get(this.fileLogPath).then((res: any) => {
            var aLink = document.createElement("a");
            aLink.style.display = "none";
            aLink.href = res.data[0].fileUrl
            document.body.appendChild(aLink);
            aLink.click();
            document.body.removeChild(aLink);
        })
    }
    cancelUpload() {
        console.log("取消上传")
        this.state_tip = STATE_TIPS.get('其他');
        this.progress = 0;
        this.file = null;
        if (this.uploaded) {
            this.$emit('cancelUpload', this.uploadExel.fileLogId)
        }else{
            this.source.cancel("请求已被取消")
            this.source = axios.CancelToken.source()
        }
    }

    restartUpload() {
        this.uploadQuery();
    }
    openFileInput() {
        const fileInput = this.$refs.fileInput as HTMLInputElement;
        fileInput.click();
    }
    // 拖动文件进入上传区域
    onDragEnter(event: DragEvent) {
        // 防止浏览器默认的拖放行为
        event.preventDefault();
        this.isDragOver = true;
    }
    // 拖动文件在上传区域中移动
    onDragOver(event: DragEvent) {
        //防止浏览器默认的拖放行为
        event.preventDefault();
    }
    // 放置拖动的文件
    onDrop(event: DragEvent) {
        event.preventDefault();
        this.isDragOver = false;
        this.fileValidator(event.dataTransfer?.files)//可能为undefined
    }
    isValidFormat(file: File) {
        const supportedFormats: string[] = this.format.split(','); // 将 format 字符串拆分成数组
        const fileExtension = '.' + file.name.split('.').pop(); // 获取文件名的扩展名
        return supportedFormats.some((supportedFormat: string) => {
            return fileExtension === supportedFormat;
        });
    }
}
</script>
  
<style>
.upload-list-dragger {
    width: 800px;
    height: 160px;
    border: 1px dashed rgba(206, 212, 224, 1);
    border-radius: 4px;
    display: flex;
    align-items: center;
}

.upload-list-dragger:hover {
    background-color: #eef8ff;

}

.custom-drag-style {
    height: 140px;
    width: 780px;
    background-color: #fff;
    flex-wrap: wrap;
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    cursor: pointer;

    .upload-icon {
        width: 24px;
        height: 24px;
    }

    .upload-click-drag {
        width: 144px;
        height: 24px;
        font-family: PingFangSC-Regular;
        font-size: 16px;
        font-weight: 400;
        line-height: 24px;
        color: rgba(69, 71, 77, 1);
        text-align: left;
        display: block;
    }

    .upload-tip {
        height: 24px;
        font-family: PingFangSC-Regular;
        font-size: 14px;
        font-weight: 400;
        line-height: 24px;
        color: rgba(168, 172, 179, 1);
        text-align: left;
        display: block;
    }
}

.custom-upload-card {
    display: flex;
    align-items: center;
    height: 71px;

    .upload-card-icon {
        width: 71px;

    }

    .upload-card-state {
        height: 100%;
        display: flex;
        flex-direction: column;
        justify-content: space-around;

        .file-name {
            font-family: PingFangSC-Regular;
            font-size: 16px;
            font-weight: 400;
            line-height: 16px;
            color: rgba(69, 71, 77, 1);
            text-align: left;
            margin-right: 12px;
        }

        .cancel-upload {
            cursor: pointer;
        }

        .progress-bar {
            height: 8px;
            border-radius: 8px;
        }

        /* 进度条看作是由两个嵌套的<div>元素构成,外部的.progress-bar元素和内部的<div> */
        .progress-bar div {
            width: 638px;
            height: 8px;
            background-color: rgba(228, 231, 237, 1);
            border-radius: 8px;
        }

        .span-container {
            width: 690px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            font-family: PingFangSC-Regular;
            font-size: 14px;
            font-weight: 400;
            line-height: 24px;

            .span-operate {
                color: #1564FF;
                cursor: pointer;
            }
        }
    }

}
</style>
  

B.二次封装组件

mds-upload内部取消上传,但组件会阻止Lits的改变,并呈现上传失败的样式,再次点击才能返回到上传界面

javascript 复制代码
<template>
  <mds-upload ref="uploadRef" :path="uploadPath" name="file" :beforeUpload="onBeforeUpload"
    :getUploadParams="getUploadParams" :disabled="false" :multiple="false" :accept="format" :onComplete="onUploadComplete"
    :onError="onUploadError" :onChange="onListChange" listType="imgCard" :limit="1" :dragable="true">
    <template v-slot:dragStyle>
      <div class="custom-drag-style">
        <img src="@/assets/img/upload.png" class="upload-icon" />
        <div class="upload-click-drag">点击或拖拽上传文件</div>
        <!-- 使用正则表达式替换所有点号 -->
        <div class="upload-tip" slot="tip">请上传{{ format.replace(/\./g, "") }}格式文件,上传多份文件时以最后一次为准</div>
      </div>
    </template>
  </mds-upload>
</template>
<script lang="ts">
import { Component, Vue, Prop } from 'vue-property-decorator'
@Component({
  components: {}
})
export default class SingleUploadComp extends Vue {
  @Prop({ required: true })  private uploadPath!: boolean
  @Prop({ default: '.xls' }) private format!: string //形如".xls,.csv,.xlsx"
  uploadExel: any = {
    succList: []
  }
  uploaded:boolean= false
   onBeforeUpload(files: File[], callback: (files: File[]) => void) {
    callback(files)
  }
  
   getUploadParams(file: File, callback: (data: any) => void) {
    const formData = new FormData()
    formData.append('file', file)
    const cbData = {
      data: formData,
      withCredentials: true
    }
    callback(cbData)
    this.$refs.uploadRef.$el.querySelector('.upload-list-dragger').style.display = "none";
  }
  /**
   * @param res 响应结果
   * @param oriFile 原始文件
   */
   onUploadComplete(res: any, oriFile: File) {
    const errEle = this.$refs.uploadRef.$el.querySelector('.mds-upload-card-data-error')
    if (res.data.code == 200) {
      this.uploadExel = res.data.data;
      this.$emit("update:uploadExel", this.uploadExel); 
      errEle.innerHTML = "上传成功!";
      this.uploaded = true;
      this.$emit("update:uploaded", this.uploaded); 
    } else {
      errEle.innerHTML = res.data.msg;
      errEle.style.color = "orange";
    }
  }
   onUploadError(err: any, oriFile: File) {
    const errEle = this.$refs.uploadRef.$el.querySelector('.mds-upload-card-data-erro')
    errEle.innerHTML = "上传失败,请重新上传";
  }
   onListChange(uploadList: any[]) {
    console.log('on list change')
    if (uploadList.length == 0) {
      if (this.uploaded) {
        console.log("取消上传")
        this.$emit('cancelUpload', this.uploadExel.fileLogId)
      }
      this.$refs.uploadRef.$el.querySelector('.upload-list-dragger').style.display = "block";
    }
  }
}  
</script>
<style lang="scss" scoped>
::v-deep .upload-list-dragger {
  // position: relative;
  width: 800px;
  height: 160px;
  border: 1px dashed rgba(206, 212, 224, 1);
  border-radius: 4px;

  .custom-drag-style:hover {
    background-color: #eef8ff;
  }

  .custom-drag-style {
    height: 140px;
    background-color: #fff;
    flex-wrap: wrap;
    display: flex;
    justify-content: center;
    align-items: center;
    flex-direction: column;
    cursor: pointer;

    .upload-icon {
      width: 24px;
      height: 24px;
    }

    .upload-click-drag {
      width: 144px;
      height: 24px;
      font-family: PingFangSC-Regular;
      font-size: 16px;
      font-weight: 400;
      line-height: 24px;
      color: rgba(69, 71, 77, 1);
      text-align: left;
      display: block;
    }

    .upload-tip {
      width: 326px;
      height: 24px;
      font-family: PingFangSC-Regular;
      font-size: 14px;
      font-weight: 400;
      line-height: 24px;
      color: rgba(168, 172, 179, 1);
      text-align: left;
      display: block;
    }
  }
}

::v-deep .mds-upload-card {
  position: relative;
  width: 800px;
  height: 160px;
  border: 1px dashed rgba(206, 212, 224, 1) !important;
  border-radius: 4px;
}

::v-deep .mds-upload-card:hover .mds-upload-card-eyes {
  display: none;
}

::v-deep .mds-upload-card-icon {
  width: 71px;
  height: 71px;
  display: block;

  &::before {
    content: '';
    display: block;
    width: 71px;
    height: 71px;
    background: url('../../../assets/img/excel.png');
    background-size: 71px 71px;
    z-index: 9999;
  }
}

::v-deep .mds-upload-card-data-name {
  width: 114px;
  height: 24px;
  font-family: PingFangSC-Regular;
  font-size: 16px;
  font-weight: 400;
  line-height: 24px;
  color: rgba(69, 71, 77, 1);
  text-align: left;
}

::v-deep .mds-upload-card-data {
  .mds-upload-card-data-error {
    color: #00AF5B;
    height: 24px;
    font-family: PingFangSC-Regular;
    font-size: 14px;
    font-weight: 400;
    line-height: 24px;
    text-align: left;
  }

  .mds-upload-card-data-size {

    height: 24px;
    font-family: PingFangSC-Regular;
    font-size: 14px;
    font-weight: 400;
    line-height: 24px;
    text-align: left;
  }
}
</style>

情况

增加cancelToken不生效,刷新页面

进度条太快->设置浏览器网速

相关推荐
心仪悦悦3 分钟前
Scala的Array(2)
开发语言·后端·scala
沉默璇年20 分钟前
react中useMemo的使用场景
前端·react.js·前端框架
yqcoder26 分钟前
reactflow 中 useNodesState 模块作用
开发语言·前端·javascript
2401_8827275735 分钟前
BY组态-低代码web可视化组件
前端·后端·物联网·低代码·数学建模·前端框架
baivfhpwxf202336 分钟前
C# 5000 转16进制 字节(激光器串口通讯生成指定格式命令)
开发语言·c#
许嵩6639 分钟前
IC脚本之perl
开发语言·perl
长亭外的少年1 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
直裾1 小时前
Scala全文单词统计
开发语言·c#·scala
心仪悦悦1 小时前
Scala中的集合复习(1)
开发语言·后端·scala
JIAY_WX1 小时前
kotlin
开发语言·kotlin