封装上传文件组件(axios,onUploadProgress,取消请求)

news/2025/2/15 21:20:34/

目录

定时模拟进度条

方法

A.axios

B.xhr

取消请求​​​​​​​

完整代码

A.自定义上传组件

B.二次封装组件

情况

增加cancelToken不生效,刷新页面

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

定时模拟进度条

    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

 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

   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 {//     }// })}

取消请求​​​​​​​

完整代码

<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.自定义上传组件

<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].fileUrldocument.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.二次封装组件

<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= falseonBeforeUpload(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不生效,刷新页面

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


http://www.ppmy.cn/news/1013408.html

相关文章

0基础学习VR全景平台篇 第79篇:全景相机-泰科易如何直播推流

泰科易科技是中国的一家研发全景相机的高科技公司&#xff0c;前不久&#xff0c;在2020世界VR产业大会上发布了新一代5G VR直播影像采集终端--360starlight。以其出色的夜景成像效果和一“部”到位的直播方案重新定义了VR慢直播相机&#xff0c;对行业具有高度借鉴意义。 本文…

SpringCloud项目打包注意事项以及可能出错的几种情况

SpringCloud项目打包注意事项和可能出错的几种情况 1、检查子模块中的 parent的pom文件路径 \<relativePath/\>2、检查打包插件的位置3、检查module是否重复引用 欢迎访问我的个人博客&#xff1a;https://wk-blog.vip 1、检查子模块中的 parent的pom文件路径 <relat…

【果树农药喷洒机器人】Part2:机器人变量喷药系统硬件选型

本专栏介绍&#xff1a;付费专栏&#xff0c;持续更新机器人实战项目&#xff0c;欢迎各位订阅关注。 关注我&#xff0c;带你了解更多关于机器人、嵌入式、人工智能等方面的优质文章&#xff01; 文章目录 一、引言二、变量喷药系统总体要求2.1系统功能要求2.2系统技术要求 三…

Java课题笔记~ AOP编程术语(掌握)

&#xff08;1&#xff09; 切面&#xff08;Aspect&#xff09; 切面泛指交叉业务逻辑。上例中的事务处理、日志处理就可以理解为切面。常用的切面是通知&#xff08;Advice&#xff09;。实际就是对主业务逻辑的一种增强。 &#xff08;2&#xff09; 连接点&#xff08;Jo…

为Git仓库设置签名信息

前言 在首次使用git版本库或创建新的仓库时&#xff0c;需要为其仓库设定管理员和管理员邮箱。 在为仓库添加管理员和邮箱地址时&#xff0c;有以下两种情况&#xff1a; &#xff08;1&#xff09;全局模式&#xff1a;首次创建&#xff0c;后面做为默认使用&#xff0c;对当…

【Scheme】Scheme 编程学习 (四) —— 递归

【Scheme】Scheme 编程学习 (四) —— 递归 文章目录 【Scheme】Scheme 编程学习 (四) —— 递归1. Factorial 阶乘2. Fibonacci 斐波那契数列3. Countdown 倒计时4. Map5. Recursion "shapes" 递归的形式5.1 factorial model5.2 - fibonacci model5.3 - countdown m…

代码随想录算法训练营第五十八天| 739. 每日温度 496.下一个更大元素 I

代码随想录算法训练营第五十八天| 739. 每日温度 496.下一个更大元素 I 什么情况下使用单调栈&#xff1a;通常是一维数组&#xff0c;要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置&#xff0c;此时我们就要想到可以用单调栈了。时间复杂度为O(n)。 一、…

Navicat连接SQL Server报错:IM002 未发现数据源名称且未指定驱动

Navicat Premium连接SQL Server软件时&#xff1a;报IM002错误&#xff0c;未发现数据源名称且未指定驱动程&#xff1a; 解决办法&#xff1a;查找Navicat Premium的安装目录D:\Navicat Premium\&#xff0c;你会找到一个文件sqlncli_x64.msi&#xff08;D:\Navicat Premium\s…