zhgdyun/src/components/GlobalUploader.vue

346 lines
13 KiB
Vue
Raw Normal View History

2022-06-08 14:51:11 +08:00
<template>
<div id="global-uploader">
<!-- 上传 -->
<uploader
ref="uploader"
:options="options"
:autoStart="false"
@file-added="filesAdded"
@file-success="onFileSuccess"
@file-progress="onFileProgress"
@file-error="onFileError"
class="uploader-app">
<uploader-unsupport></uploader-unsupport>
<uploader-btn id="global-uploader-btn" :attrs="attrs" ref="uploadBtn">选择文件</uploader-btn>
<uploader-list v-show="panelShow">
<div class="file-panel" slot-scope="props" :class="{'collapse': collapse}">
<div class="file-title">
<span class="title-span">上传列表</span>
<div class="operate">
<el-button @click="fileListShow" type="text" :title="collapse ? '展开':'折叠' ">
<i class="iconfont" :class="collapse ? 'el-icon-full-screen': 'el-icon-minus'"></i>
</el-button>
<el-button @click="close" type="text" title="关闭">
<i class="iconfont el-icon-close"></i>
</el-button>
</div>
</div>
<ul class="file-list">
<li v-for="file in props.fileList" :key="file.id">
<uploader-file :class="'file_' + file.id" ref="files" :file="file" :list="true"></uploader-file>
</li>
<div class="no-file" v-if="!props.fileList.length"><i class="iconfont icon-empty-file"></i> 暂无待上传文件</div>
</ul>
</div>
</uploader-list>
</uploader>
</div>
</template>
<script>
import SparkMD5 from 'spark-md5'
// import Cookies from 'js-cookie'
export default {
data() {
return {
options: {
target: this.$http.defaults.baseURL+'filetransfer/uploadfile?projectSn='+this.$store.state.projectSn, // 目标上传 URL
chunkSize: 1024 * 1024,
fileParameterName: 'file',
maxChunkRetries: 3,
testChunks: false, //是否开启服务器分片校验
// 服务器分片校验函数,秒传及断点续传基础
// checkChunkUploadedByResponse: function (chunk, message) {
// console.log('message',chunk)
// let objMessage = JSON.parse(message);
// let data = objMessage.result;
// if (data.skipUpload) {
// return true;
// }else{
// return false
// }
// //
// // return (data.uploaded || []).indexOf(chunk.offset + 1) >= 0
// },
// headers: {
// Authorization:'Bearer'+' '+this.$store.state.userInfo.token,
// // token: Cookies.get('token', { domain: '.qiwenshare.com' }),
// 'Content-Type':'application/x-www-form-urlencoded'
// },
// method:'post',
// query: {
// projectSn:this.$store.state.projectSn
// }
},
attrs: {
accept: '*'
},
panelShow: false, //选择文件后展示上传panel
collapse: false,
}
},
mounted() {
this.$EventBus.$on('openUploader', query => {
console.log('openUploader')
this.params = query || {};
// this.$refs.uploadBtn.click()
var e = document.createEvent('MouseEvent');
e.initEvent('click', false, false);
this.$refs.uploadBtn.$el.dispatchEvent(e);
});
},
computed: {
//Uploader实例
uploader() {
return this.$refs.uploader.uploader;
}
},
methods: {
// 添加文件的回调
filesAdded(file) {
console.log('filesAdded')
this.panelShow = true;
this.computeMD5(file);
},
onFileProgress(rootFile, file, chunk) {
console.log(`上传中 ${file.name}chunk${chunk.startByte / 1024 / 1024} ~ ${chunk.endByte / 1024 / 1024}`)
},
// 文件上传成功的回调
onFileSuccess(rootFile, file, response, chunk){
if (response == "") {
this.statusSet(file.id, 'failed');
return
}
let data = response.data
let result = JSON.parse(response)
if (result.success) {
this.$message.success(`${file.name} - 上传完毕`)
this.statusRemove(file.id);
this.$EventBus.$emit('refreshList', "")
this.$EventBus.$emit('refreshStorage', "")
} else {
this.$message.error(result.errorMessage)
this.statusSet(file.id, 'failed');
}
console.log(chunk)
},
onFileError(rootFile, file, response, chunk) {
this.$message({
message: response,
type: 'error'
})
},
/**
* 计算md5实现断点续传及秒传
* @param file
*/
computeMD5(file) {
let fileReader = new FileReader();
let time = new Date().getTime();
let blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
let currentChunk = 0;
const chunkSize = 1 * 1024 * 1024;
let chunks = Math.ceil(file.size / chunkSize);
let spark = new SparkMD5.ArrayBuffer();
// 文件状态设为"计算MD5"
this.statusSet(file.id, 'md5');
file.pause();
loadNext();
fileReader.onload = (e => {
spark.append(e.target.result);
if (currentChunk < chunks) {
currentChunk++;
loadNext();
// 实时展示MD5的计算进度
this.$nextTick(() => {
$(`.myStatus_${file.id}`).text('校验MD5 '+ ((currentChunk/chunks)*100).toFixed(0)+'%')
})
} else {
let md5 = spark.end();
this.computeMD5Success(md5, file);
console.log(`MD5计算完毕${file.name} \nMD5${md5} \n分片${chunks} 大小:${file.size} 用时:${new Date().getTime() - time} ms`);
}
});
fileReader.onerror = function () {
this.error(`文件${file.name}读取出错,请检查该文件`)
file.cancel();
};
function loadNext() {
let start = currentChunk * chunkSize;
let end = ((start + chunkSize) >= file.size) ? file.size : start + chunkSize;
fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end));
}
},
computeMD5Success(md5, file) {
// 将自定义参数直接加载uploader实例的opts上
Object.assign(this.uploader.opts, {
query: {
...this.params,
}
})
file.uniqueIdentifier = md5;
file.resume();
this.statusRemove(file.id);
},
fileListShow() {
let $list = $('#global-uploader .file-list');
if ($list.is(':visible')) {
$list.slideUp();
this.collapse = true;
} else {
$list.slideDown();
this.collapse = false;
}
},
close() {
this.uploader.cancel();
this.panelShow = false;
},
/**
* 新增的自定义的状态: 'md5''transcoding''failed'
* @param id
* @param status
*/
statusSet(id, status) {
//
let statusMap = {
md5: {
text: '校验MD5',
bgc: '#fff'
},
merging: {
text: '合并中',
bgc: '#e2eeff'
},
transcoding: {
text: '转码中',
bgc: '#e2eeff'
},
failed: {
text: '上传失败',
bgc: '#e2eeff'
}
}
this.$nextTick(() => {
$(`<p class="myStatus_${id}"></p>`).appendTo(`.file_${id} .uploader-file-status`).css({
'position': 'absolute',
'top': '0',
'left': '0',
'right': '0',
'bottom': '0',
'zIndex': '1',
'backgroundColor': statusMap[status].bgc
}).text(statusMap[status].text);
})
},
statusRemove(id) {
this.$nextTick(() => {
$(`.myStatus_${id}`).remove();
})
},
error(msg) {
this.$notify({
title: '错误',
message: msg,
type: 'error',
duration: 2000
})
}
},
watch: {},
destroyed() {
this.$off('openUploader');
},
components: {}
}
</script>
<style lang="stylus" scoped>
#global-uploader {
position: fixed;
z-index: 20;
right: 15px;
bottom: 15px;
.uploader-app {
width: 520px;
}
.file-panel {
background-color: #fff;
border: 1px solid #e2e2e2;
border-radius: 7px 7px 0 0;
box-shadow: 0 0 10px rgba(0, 0, 0, .2);
.file-title {
display: flex;
height: 40px;
line-height: 40px;
padding: 0 15px;
border-bottom: 1px solid #ddd;
.title-span {
padding-left: 0;
margin-bottom: 0;
font-size: 17px;
color: #303133;
}
.operate {
flex: 1;
text-align: right;
}
}
.file-list {
position: relative;
height: 240px;
overflow-x: hidden;
overflow-y: auto;
background-color: #fff;
> li {
background-color: #fff;
}
}
&.collapse {
.file-title {
background-color: #E7ECF2;
}
}
}
.no-file {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 16px;
}
/deep/.uploader-file-icon {
&:before {
content: '' !important;
}
&[icon=image] {
background: url(/assets/images/file/file_pic.png);
}
&[icon=video] {
background: url(/assets/images/file/file_video.png);
}
&[icon=document] {
background: url(/assets/images/file/file_txt.png);
}
}
/deep/.uploader-file-actions > span {
margin-right: 6px;
}
}
/* 隐藏上传按钮 */
#global-uploader-btn {
position: absolute;
clip: rect(0, 0, 0, 0);
}
</style>