472 lines
16 KiB
Vue
472 lines
16 KiB
Vue
<template>
|
||
<div>
|
||
<div class="container">
|
||
<div class="item">
|
||
<el-upload action="#"
|
||
ref="uploadFolder"
|
||
:on-change="onChange"
|
||
:on-remove="handleRemove"
|
||
:show-file-list="false"
|
||
:headers="headers"
|
||
:accept="accept"
|
||
:file-list="fileList"
|
||
:before-upload="beforeUploadsg"
|
||
:on-success="uploadSuccess"
|
||
:on-error="uploadError"
|
||
:on-exceed="exceed"
|
||
:auto-upload="true"
|
||
:http-request="uploadFile"
|
||
:multiple="true">
|
||
<el-button slot="trigger" id="myButton" size="small" type="primary" @click="uploadBtn.click()">选取文件夹</el-button>
|
||
</el-upload>
|
||
</div>
|
||
<div></div>
|
||
<div class="item">
|
||
<el-upload class="upload-demo" ref="upload" action="#"
|
||
:before-upload="beforeUpload"
|
||
:on-change="onChange"
|
||
:on-remove="handleRemove"
|
||
:multiple="isMultiple"
|
||
:on-exceed="handleExceed"
|
||
:accept="acceptType"
|
||
:limit="limit"
|
||
:file-list="fileList"
|
||
:auto-upload="true"
|
||
:http-request="uploadFile"
|
||
show-file-list>
|
||
<el-button id="myButton" slot="trigger" size="small" type="primary">选取文件</el-button>
|
||
<div slot="tip" class="el-upload__tip">只能上传{{acceptType}}文件</div>
|
||
</el-upload>
|
||
<div v-show="progressFlag" class="head-img">
|
||
<el-progress :text-inside="true" :stroke-width="14" :percentage="progressPercent" status="success"></el-progress>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script>
|
||
import axios from 'axios'
|
||
import sgUploadTray from "./sgUploadTray"
|
||
import {
|
||
getToken
|
||
} from "@/utils/auth";
|
||
|
||
export default {
|
||
|
||
components: {
|
||
sgUploadTray,
|
||
},
|
||
props: {
|
||
uploadUrl: {
|
||
type: String,
|
||
required: true
|
||
},
|
||
// 是否多选
|
||
isMultiple: {
|
||
type: Boolean,
|
||
default() {
|
||
return false
|
||
}
|
||
},
|
||
// 文件类型
|
||
type: {
|
||
type: Array,
|
||
default() {
|
||
return []
|
||
}
|
||
},
|
||
//上传类型
|
||
acceptType: {
|
||
type: String,
|
||
required: true
|
||
},
|
||
// 上传数量
|
||
limit: {
|
||
type: Number,
|
||
default() {
|
||
return 1
|
||
}
|
||
},
|
||
// 文件
|
||
dataFile: {
|
||
type: [Object, Array, String],
|
||
default() {
|
||
return ''
|
||
}
|
||
},
|
||
},
|
||
data() {
|
||
return {
|
||
fileList: [],
|
||
progress: 0,
|
||
isUploading: false,
|
||
progressFlag: false, //进度条初始值隐藏
|
||
progressPercent: 0, //进度条初始值
|
||
partSize: 5 * 1024 * 1024,
|
||
headers: { kkToken: localStorage.token, }, //获取token(注意仔细看后端接受token的字段名是不是叫做“token”)
|
||
accept:`.${["png", "jpg", "jpeg", "bmp", "gif"].join(",.")}`,//允许上传的后缀名
|
||
actionURL: process.env.VUE_APP_BASE_API + "/common/initUpload",
|
||
dur: 100,
|
||
percent: 100,
|
||
uploadBtn: null,//上传按钮
|
||
uploadList: [],
|
||
showUploadTray: false,
|
||
};
|
||
},
|
||
mounted(d) {
|
||
this.$nextTick(() => {
|
||
this.uploadBtn = this.$refs.uploadFolder.$children[0].$refs.input;
|
||
this.uploadBtn.webkitdirectory = true;//让el-upload支持上传文件夹
|
||
})
|
||
},
|
||
watch: {
|
||
dataFile: {
|
||
handler(newValue, oldValue) {
|
||
if (newValue) {
|
||
this.fileList = Array.isArray(newValue) ? newValue : [newValue]
|
||
} else {
|
||
this.fileList = []
|
||
}
|
||
},
|
||
immediate: true
|
||
}
|
||
},
|
||
methods: {
|
||
showFakeLoading(file) {
|
||
file = this.uploadList.find(v => v.uid == file.uid);
|
||
clearInterval(file.interval);
|
||
file.percent = 0;
|
||
file.interval = setInterval(() => {
|
||
file.percent++;
|
||
file.percent >= 99 && this.hideFakeLoading(file);
|
||
}, this.dur);
|
||
},
|
||
hideFakeLoading(file, { type, tip, color } = {}) {
|
||
console.log('file======>');
|
||
console.log(file)
|
||
file = this.uploadList.find(v => v.uid == file.uid);
|
||
clearInterval(file.interval);
|
||
switch (type) {
|
||
case 'error':
|
||
file.percent = 0;
|
||
break;
|
||
case 'success':
|
||
default:
|
||
file.percent = 100;
|
||
}
|
||
type && (file.type = type);
|
||
tip && (file.tip = tip);
|
||
color && (file.color = color);
|
||
},
|
||
exceed(file, fileList) {
|
||
this.$message.error("上传文件数量太大,分散上传吧!");
|
||
},
|
||
stopUpload(d) {
|
||
this.$refs.uploadFolder.abort();
|
||
//console.log(`取消上传`, d);
|
||
},
|
||
beforeUploadsg(file, id) {
|
||
console.log(this.uploadList)
|
||
this.uploadList = []
|
||
console.info("this.uploadList==", JSON.stringify(this.uploadList))
|
||
this.uploadList.unshift({
|
||
interval: false,
|
||
uid: file.uid,
|
||
percent: 0,//加载进度
|
||
name: file.name,
|
||
size: file.size,
|
||
type: file.type,
|
||
webkitRelativePath: file.webkitRelativePath,
|
||
tip: '',
|
||
color: '',
|
||
});
|
||
this.showUploadTray = true;
|
||
this.accept = ["png", "jpg", "jpeg", "bmp", "gif", "txt", "json", "pdf"]
|
||
// 判断是不是特定的格式________________________
|
||
let isFile = this.accept.includes(file.name.toLocaleLowerCase().split(".").pop());
|
||
const maxSize = 50; //限制大小
|
||
const isAllowSize = file.size / 1024 / 1024 <= maxSize;
|
||
isFile || this.$message.error("上传文件只能是" + this.accept+ "格式");
|
||
isAllowSize || this.$message.error("上传文件大小不能超过" + maxSize + "MB");
|
||
let allowUpload = isFile && isAllowSize;
|
||
return allowUpload; //若返回false则停止上传
|
||
},
|
||
|
||
uploadSuccess(response, file, fileList) {
|
||
if (response.data && response.data.key) {
|
||
// 下载失败原因的描述文件
|
||
this.$d.customer_downloadImportCustomerExcel({ key: response.data.key }, {
|
||
s: (d) => {
|
||
this.hideFakeLoading(file, { type: 'error', tip: "上传失败", color: "red" });
|
||
this.$g.downloadFile(d, `${file.name}-上传失败原因`, '.xls');
|
||
this.$message.error(`${file.name}-上传失败,请查看失败原因`);
|
||
// this.initList();//刷新列表
|
||
//console.log('上传失败', response, file, fileList);
|
||
}
|
||
});
|
||
} else if (response.success) {
|
||
// 上传成功了
|
||
this.hideFakeLoading(file, { type: 'success', tip: "上传成功", color: "green" });
|
||
this.$message.success(`“${file.name}上传成功`);
|
||
// this.initList();//刷新列表
|
||
//console.log('上传成功', response, file, fileList);
|
||
} else {
|
||
// 其他失败原因
|
||
this.hideFakeLoading(file, { type: 'error', tip: "上传失败", color: "red" });
|
||
this.$message.error(response.msg);
|
||
//console.log('上传失败', response, file, fileList);
|
||
}
|
||
},
|
||
//上传失败
|
||
uploadError(err, file, fileList) {
|
||
this.hideFakeLoading(file, { type: 'error', tip: "上传失败", color: "red" });
|
||
this.$message.error("上传失败");
|
||
//console.log('上传失败', err, file, fileList);
|
||
},
|
||
|
||
// 上传前
|
||
beforeUpload(file) {
|
||
/* const fileType = file.type.toLowerCase()
|
||
console.info("fileType=======", fileType)
|
||
const typeFlag = this.type.some(item => {
|
||
return fileType.indexOf(item) != -1
|
||
})
|
||
if (!typeFlag) {
|
||
this.$message.error('上传类型有误,请重新上传')
|
||
return false
|
||
}*/
|
||
},
|
||
//文件状态改变时的钩子,添加文件、上传成功和上传失败时都会被调用
|
||
onChange(file, fileList) {
|
||
this.fileList = fileList;
|
||
},
|
||
handleRemove(file, fileList) {
|
||
this.fileList = fileList;
|
||
},
|
||
handleExceed(files, fileList) {
|
||
this.$message.warning(
|
||
"最多之能上传"+ this.limit +"个附件"
|
||
);
|
||
},
|
||
async uploadFile({ data, file }) {
|
||
let self = this
|
||
//初始化参数
|
||
this.progress++
|
||
if (file.size < this.partSize) {
|
||
let formData = new FormData()
|
||
formData.append("file", file)
|
||
self.progressFlag = true;
|
||
axios({
|
||
url: self.uploadUrl,
|
||
method: 'post',
|
||
data: formData,
|
||
headers: {
|
||
'Authorization': 'Bearer ' + getToken(),
|
||
'Content-Type': 'multipart/form-data'
|
||
},
|
||
onUploadProgress: progressEvent => {
|
||
// progressEvent.loaded:已上传文件大小
|
||
// progressEvent.total:被上传文件的总大小
|
||
//进度条
|
||
self.progressPercent = ((progressEvent.loaded / progressEvent.total) * 100) | 0;
|
||
}
|
||
}).then(res => {
|
||
setTimeout(() => {
|
||
if (res.data.code == 200 && self.progressPercent === 100) {
|
||
setTimeout(function () {
|
||
self.$message({
|
||
message: '上传成功!',
|
||
type: 'success',
|
||
duration: '2000'
|
||
});
|
||
self.progressFlag = false;
|
||
self.progressPercent = 0
|
||
self.handleResult(res,file);
|
||
}, 500);
|
||
} else {
|
||
self.$message({
|
||
message: '上传失败!',
|
||
type: 'error',
|
||
duration: '2000'
|
||
});
|
||
}
|
||
}, 1000);
|
||
|
||
}).catch(error => {
|
||
console.error(error)
|
||
self.progressFlag = false;
|
||
self.progressPercent = 0
|
||
self.$refs.upload.clearFiles();
|
||
self.$message({
|
||
message: '上传失败!',
|
||
type: 'error',
|
||
duration: '2000'
|
||
});
|
||
})
|
||
} else {
|
||
file._shardCount = Math.ceil(file.size / this.partSize) //总片数
|
||
file.uploaded = 0
|
||
let formData = new FormData()
|
||
formData.append('fileName', file.name)
|
||
let self = this
|
||
|
||
//大文件上传初始化,返回uploadId initUpload(formData)
|
||
axios({
|
||
url: process.env.VUE_APP_BASE_API + "/common/initUpload",
|
||
method: 'post',
|
||
data: formData,
|
||
headers: {
|
||
'Authorization': 'Bearer ' + getToken(),
|
||
'Content-Type': 'multipart/form-data'
|
||
},
|
||
}).then(function (res) {
|
||
if (res.status == 200) {
|
||
//从第0块开始上传
|
||
file.uploadId = res.data.uploadId
|
||
file.objectKey = res.data
|
||
file.chunkId = 0
|
||
self.uploadByChunk(file)
|
||
} else {
|
||
this.progress--
|
||
self.$message.error(res.msg)
|
||
}
|
||
}).catch(error => {
|
||
self.progressPercent = 0
|
||
self.progressFlag = false;
|
||
console.error(error)
|
||
})
|
||
}
|
||
|
||
},
|
||
//分片上传大文件
|
||
uploadByChunk(file) {
|
||
this._start = file.chunkId * this.partSize
|
||
this._end = Math.min(file.size, this._start + this.partSize)//结束时总大小,和 开始的大小+之前的大小比较
|
||
let self = this
|
||
let fileData = file.slice(this._start, this._end)
|
||
//获取文件块MD5
|
||
let reader = new FileReader()
|
||
reader.readAsBinaryString(fileData)
|
||
let form1 = new FormData()//new一个form的实例,可以进行键值对的添加,
|
||
form1.append('chunkFile', fileData) //slice方法用于切出文件的一部分
|
||
form1.append('uploadId', file.uploadId)
|
||
form1.append('chunkId', (file.chunkId + 1).toString())
|
||
form1.append('shardCount', file._shardCount.toString()) //是否最后一片
|
||
self.progressFlag = true;
|
||
//上传大文件的Chunk 返回chunk的MD5 uploadChunk(form1)
|
||
axios({
|
||
url: process.env.VUE_APP_BASE_API + "/common/uploadChunk",
|
||
method: 'post',
|
||
data: form1,
|
||
headers: {
|
||
'Authorization': 'Bearer ' + getToken(),
|
||
'Content-Type': 'multipart/form-data'
|
||
},
|
||
onUploadProgress: progressEvent => {
|
||
// progressEvent.loaded:已上传文件大小
|
||
// progressEvent.total:被上传文件的总大小
|
||
//进度条
|
||
self.progressPercent = ((progressEvent.loaded / progressEvent.total) * 100) | 0;
|
||
}
|
||
}).then(response => {
|
||
if (response.status == 200) {
|
||
//判断返回的MD5值是否一致,一致继续传下一块,否则重传本块(这里目前前后端MD5一直不一致,暂时先注释掉,有时间了我再研究一下)
|
||
// self.md5Str[index] === response.msg ||
|
||
if (true) {
|
||
// this.$message.success('第' + (index + 1).toString() + '块文件上传成功')
|
||
file.uploaded++
|
||
self.percentageSend(file)
|
||
//如果没上传完成,继续上传一下块
|
||
file.chunkId++
|
||
if (file.chunkId < file._shardCount) {
|
||
this.uploadByChunk(file)
|
||
}
|
||
} else {
|
||
//不一致,重新传本块
|
||
this.$message.success('第' + (file.chunkId + 1).toString() + '块文件上传不成功,重新上传')
|
||
this.uploadByChunk(file.chunkId)
|
||
}
|
||
} else {
|
||
//出错,跳出循环显示错误
|
||
this.progress--
|
||
this.$message.error(response.msg)
|
||
}
|
||
})
|
||
},
|
||
//接收上传的百分值回调
|
||
percentageSend(file) {
|
||
let self = this
|
||
let perNum = Math.floor((file.uploaded / file._shardCount) * 100)
|
||
this.progressPercent = perNum
|
||
//如果上传完成,合并文件
|
||
if (perNum === 100) {
|
||
let form2 = new FormData()//new一个form的实例,可以进行键值对的添加,
|
||
form2.append('uploadId', file.uploadId)
|
||
form2.append('fileName', file.name)
|
||
// 大文件上传完成后合并
|
||
// 返回文件访问的URL
|
||
axios({
|
||
url: process.env.VUE_APP_BASE_API + "/common/mergeFile",
|
||
method: 'post',
|
||
data: form2,
|
||
headers: {
|
||
'Authorization': 'Bearer ' + getToken(),
|
||
'Content-Type': 'multipart/form-data'
|
||
},
|
||
}).then(res => {
|
||
if (res.status == 200) {
|
||
setTimeout(function () {
|
||
self.$message({
|
||
message: '上传成功!',
|
||
type: 'success',
|
||
duration: '2000'
|
||
});
|
||
self.progressFlag = false;
|
||
self.progressPercent = 0
|
||
self.handleResult(res,file);
|
||
}, 500);
|
||
} else {
|
||
self.$message({
|
||
message: '上传失败!',
|
||
type: 'error',
|
||
duration: '2000'
|
||
});
|
||
}
|
||
})
|
||
}
|
||
},
|
||
resetData() {
|
||
this.progress = 0;
|
||
},
|
||
handleResult(res, file) {
|
||
let self = this
|
||
const data = {
|
||
fileName: file.name,
|
||
fileSize: file.size,
|
||
fileUrl: res.data.url,
|
||
filePath: res.data.filePath,
|
||
fileOldName: res.data.originalFilename,
|
||
fileNewName: res.data.newFileName,
|
||
suffixType: res.data.suffixType
|
||
}
|
||
self.$emit("handleSuccess", data)
|
||
},
|
||
/** 清空文件 **/
|
||
clearFile(){
|
||
this.$refs.upload.clearFiles();
|
||
this.fileList = []
|
||
}
|
||
}
|
||
};
|
||
</script>
|
||
<style>
|
||
#myButton {
|
||
width: 90px; /* 设置按钮长度为200像素 */
|
||
}
|
||
.container {
|
||
display: flex; /* 设定为flex布局 */
|
||
}
|
||
</style>
|
||
|