9 changed files with 715 additions and 60 deletions
-
13src/utils/upload.js
-
16src/views/collectReorganizi/batchConnection/index.vue
-
71src/views/collectReorganizi/batchConnection/module/form.vue
-
7src/views/collectReorganizi/collectionLibrary/module/collectHeader.vue
-
56src/views/collectReorganizi/collectionLibrary/module/uploadOriginal/bigUpload.vue
-
2src/views/collectReorganizi/collectionLibrary/module/uploadOriginal/index.vue
-
37src/views/components/category/PreviewForm.vue
-
549src/views/components/category/preUpload.vue
-
12src/views/prearchiveLibrary/index.vue
@ -0,0 +1,549 @@ |
|||||
|
<template> |
||||
|
<div class="uploader-big"> |
||||
|
<uploader |
||||
|
ref="uploader" |
||||
|
:options="initOptions" |
||||
|
:file-status-text="fileStatusText" |
||||
|
:auto-start="false" |
||||
|
|
||||
|
class="uploader-app" |
||||
|
@file-added="onFileAdded" |
||||
|
@file-success="onUploadSuccess" |
||||
|
@file-progress="onFileProgress" |
||||
|
@file-error="onFileError" |
||||
|
> |
||||
|
<uploader-unsupport /> |
||||
|
<!-- @click="clickUploader" --> |
||||
|
<uploader-drop class="custom_uploader_drop" element-loading-text="正在读取中"> |
||||
|
<uploader-btn ref="uploadBtn" :attrs="attrs"> |
||||
|
<slot> |
||||
|
<i class="iconfont icon-shangchuan" /> |
||||
|
上传 |
||||
|
</slot> |
||||
|
</uploader-btn> |
||||
|
</uploader-drop> |
||||
|
<div v-if="isUpload" class="upload_process_box"> |
||||
|
<div>文件名:{{ fileName }}</div> |
||||
|
<el-progress :percentage="uploadProcessNum" /> |
||||
|
<div v-if="isMd5Upload"> |
||||
|
正在读取文件中 - {{ md5ProgressText }} |
||||
|
</div> |
||||
|
<div v-if="!isSyncUpload&&!isMd5Upload"> |
||||
|
上传至服务器 - <span>{{ uploadSpeed }} M/s</span> |
||||
|
</div> |
||||
|
<div v-if="isSyncUpload"> |
||||
|
上传中,请稍后 |
||||
|
</div> |
||||
|
</div> |
||||
|
</uploader> |
||||
|
</div> |
||||
|
</template> |
||||
|
|
||||
|
<script> |
||||
|
import SparkMD5 from 'spark-md5' |
||||
|
import { mapGetters } from 'vuex' |
||||
|
import axios from 'axios' |
||||
|
import { getToken } from '@/utils/auth' |
||||
|
import { getCurrentTime } from '@/utils/index' |
||||
|
|
||||
|
export default { |
||||
|
props: { |
||||
|
selectedDocument: { |
||||
|
type: Object, |
||||
|
default: function() { |
||||
|
return {} |
||||
|
} |
||||
|
}, |
||||
|
arcId: { |
||||
|
type: String, |
||||
|
default: function() { |
||||
|
return '' |
||||
|
} |
||||
|
}, |
||||
|
selectedCategory: { |
||||
|
type: Object, |
||||
|
default: function() { |
||||
|
return {} |
||||
|
} |
||||
|
}, |
||||
|
isBatchMount: { |
||||
|
type: String, |
||||
|
default: function() { |
||||
|
return '' |
||||
|
} |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
data() { |
||||
|
return { |
||||
|
initOptions: { |
||||
|
target: null, |
||||
|
headers: { |
||||
|
Authorization: getToken() |
||||
|
}, |
||||
|
singleFile: true, // 启用单个文件上传 |
||||
|
uploadMethod: 'post', // 真正上传的时候使用的 HTTP 方法,默认 POST |
||||
|
maxChunkRetries: 3, // 最大自动失败重试上传次数 |
||||
|
testChunks: true, // 是否开启服务器分片校验 |
||||
|
parseTimeRemaining: function(timeRemaining, parsedTimeRemaining) { |
||||
|
return parsedTimeRemaining |
||||
|
.replace(/\syears?/, '年') |
||||
|
.replace(/\days?/, '天') |
||||
|
.replace(/\shours?/, '小时') |
||||
|
.replace(/\sminutes?/, '分钟') |
||||
|
.replace(/\sseconds?/, '秒') |
||||
|
}, |
||||
|
checkChunkUploadedByResponse: (chunk, message) => { |
||||
|
const result = JSON.parse(message) |
||||
|
if (result.data.skipUpload) { |
||||
|
this.skip = true |
||||
|
return true |
||||
|
} |
||||
|
return (result.data.uploaded || []).indexOf(chunk.offset + 1) >= 0 |
||||
|
} |
||||
|
}, |
||||
|
attrs: { |
||||
|
accept: '' |
||||
|
}, |
||||
|
fileStatusText: { |
||||
|
success: '上传成功', |
||||
|
error: '上传失败', |
||||
|
uploading: '上传中', |
||||
|
paused: '已暂停', |
||||
|
waiting: '等待上传' |
||||
|
}, |
||||
|
isStartUpload: false, // 开始上传 |
||||
|
md5ProgressText: 0, |
||||
|
isMd5Upload: false, // 计算md5状态 |
||||
|
isUpload: false, // 正在上传 |
||||
|
uploadProcessNum: 0, // 上传进度 |
||||
|
uploadSpeed: 0, // 上传速度 |
||||
|
fileName: '', // 文件名 |
||||
|
isSyncUpload: false, // 是否在同步远程数据 |
||||
|
syncUploadProcessNum: 0, // 同步远程数据 |
||||
|
response: null, // 上传成功 |
||||
|
queryTimer: null, // 轮询计时器 |
||||
|
socket: null |
||||
|
} |
||||
|
}, |
||||
|
computed: { |
||||
|
...mapGetters([ |
||||
|
'baseApi' |
||||
|
]), |
||||
|
// Uploader实例 |
||||
|
uploader() { |
||||
|
return this.$refs.uploader.uploader |
||||
|
} |
||||
|
}, |
||||
|
created() { |
||||
|
this.initOptions.target = this.baseApi + '/api/collect/upload' |
||||
|
if (this.isBatchMount === 'true') { |
||||
|
this.attrs.accept = '.zip' |
||||
|
} else { |
||||
|
this.attrs.accept = '' |
||||
|
} |
||||
|
}, |
||||
|
beforeDestroy() { |
||||
|
clearInterval(this.queryTimer) |
||||
|
}, |
||||
|
methods: { |
||||
|
clickUploader(e) { |
||||
|
this.$refs.uploadBtn.$el.click() |
||||
|
}, |
||||
|
// 上传前 |
||||
|
onFileAdded(file) { |
||||
|
this.uploadProcessNum = 0 |
||||
|
// 计算MD5 |
||||
|
this.computeMD5(file).then((result) => this.startUpload(result)) |
||||
|
}, |
||||
|
/** |
||||
|
* 计算md5值,以实现断点续传及秒传 |
||||
|
*/ |
||||
|
computeMD5(file) { |
||||
|
const maxMessage = '上传文件大小不能超过 10GB!' |
||||
|
const maxSize = 10 * 1024 * 1024 * 1024 |
||||
|
if (file && file.size > maxSize) { |
||||
|
this.$message.warning(maxMessage) |
||||
|
return false |
||||
|
} |
||||
|
const fileReader = new FileReader() |
||||
|
const time = new Date().getTime() |
||||
|
const blobSlice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice |
||||
|
|
||||
|
let currentChunk = 0 |
||||
|
// const chunkSize = this.initOptions.chunkSize |
||||
|
const chunkSize = 10 * 1024 * 1024 |
||||
|
|
||||
|
// 使用 Math.ceil 实现向上取整 |
||||
|
const chunksWithCeil = Math.ceil(file.size / chunkSize) |
||||
|
console.log('使用 Math.ceil 得到的 chunks 数量:', chunksWithCeil) |
||||
|
|
||||
|
// 手动实现向上取整 |
||||
|
let chunks = Math.floor(file.size / chunkSize) |
||||
|
if (file.size % chunkSize !== 0) { |
||||
|
chunks++ |
||||
|
} |
||||
|
console.log('chunksManual', chunks) |
||||
|
|
||||
|
const spark = new SparkMD5.ArrayBuffer() |
||||
|
this.fileName = file.name |
||||
|
|
||||
|
// 文件状态设为"计算MD5" |
||||
|
this.isMd5Upload = true |
||||
|
this.isUpload = true |
||||
|
file.pause() |
||||
|
loadNext() |
||||
|
|
||||
|
return new Promise((resolve, reject) => { |
||||
|
fileReader.onload = (e) => { |
||||
|
spark.append(e.target.result) |
||||
|
if (currentChunk < chunks) { |
||||
|
currentChunk++ |
||||
|
loadNext() |
||||
|
|
||||
|
// 实时展示MD5的计算进度 |
||||
|
this.$nextTick(() => { |
||||
|
this.md5ProgressText = ((currentChunk / chunks) * 100).toFixed(0) + '%' |
||||
|
}) |
||||
|
} else { |
||||
|
console.log('spark', spark) |
||||
|
const md5 = spark.end() |
||||
|
console.log('md5', md5) |
||||
|
// md5计算完毕 |
||||
|
resolve({ md5, file }) |
||||
|
console.log(file) |
||||
|
console.log( |
||||
|
`MD5计算完毕:${file.name} \nMD5:${md5} \n分片:${chunks} 大小:${file.size} 用时:${ |
||||
|
new Date().getTime() - time |
||||
|
} ms` |
||||
|
) |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
fileReader.onerror = function() { |
||||
|
this.$message({ message: `文件${file.name}读取出错,请检查该文件`, type: 'error', offset: 8 }) |
||||
|
file.cancel() |
||||
|
reject() |
||||
|
} |
||||
|
}) |
||||
|
|
||||
|
function loadNext() { |
||||
|
const start = currentChunk * chunkSize |
||||
|
const end = start + chunkSize >= file.size ? file.size : start + chunkSize |
||||
|
|
||||
|
fileReader.readAsArrayBuffer(blobSlice.call(file.file, start, end)) |
||||
|
} |
||||
|
}, |
||||
|
|
||||
|
// md5计算完毕,开始上传 |
||||
|
startUpload({ md5, file }) { |
||||
|
file.uniqueIdentifier = md5 |
||||
|
file.resume() |
||||
|
this.isMd5Upload = false |
||||
|
this.isStartUpload = true |
||||
|
}, |
||||
|
// 上传中 |
||||
|
onFileProgress(rootFile, file, chunk) { |
||||
|
const uploader = this.$refs.uploader.uploader |
||||
|
this.uploadProcessNum = Math.floor(uploader.progress() * 100) |
||||
|
// this.emit('onUploadProcess', uploader.progress()) |
||||
|
const averageSpeed = uploader.averageSpeed |
||||
|
const speed = averageSpeed / 1000 / 10 |
||||
|
this.uploadSpeed = speed.toFixed(2) |
||||
|
}, |
||||
|
// 上传中转站成功 |
||||
|
onUploadSuccess(rootFile, file, response, chunk) { |
||||
|
const res1 = JSON.parse(response) |
||||
|
if (res1.code === 200) { |
||||
|
// 开始merge |
||||
|
console.log('rootFile.uniqueIdentifier', rootFile.uniqueIdentifier) |
||||
|
const body = { |
||||
|
totalChunks: rootFile.chunks.length, |
||||
|
md5File: rootFile.uniqueIdentifier, |
||||
|
fileName: rootFile.name |
||||
|
} |
||||
|
console.log('body', body) |
||||
|
console.log(file) |
||||
|
this.handleUploadConfirm() |
||||
|
} else { |
||||
|
this.$message({ message: '上传失败!', type: 'error', offset: 8 }) |
||||
|
this.$emit('onUploadError', res1) |
||||
|
} |
||||
|
if (this.skip) { |
||||
|
this.skip = false |
||||
|
} |
||||
|
}, |
||||
|
handleUploadConfirm() { |
||||
|
if (this.$refs.uploader.fileList.length === 0) { |
||||
|
this.$message({ message: '请选择要上传的文件!', type: 'error', offset: 8 }) |
||||
|
return false |
||||
|
} |
||||
|
this.nowDate = getCurrentTime() |
||||
|
const jsonArrayToSend = [] |
||||
|
this.isSyncUpload = true |
||||
|
// 使用 Promise.all 确保所有异步操作完成 |
||||
|
Promise.all(this.$refs.uploader.fileList.map(async(item) => { |
||||
|
console.log('item', item) |
||||
|
const json = {} |
||||
|
const jsonArray = [] |
||||
|
const jsonString = {} |
||||
|
|
||||
|
if (item.file.type.substring(0, item.file.type.indexOf('/')) === 'image') { |
||||
|
const fileBase64 = await this.getBase64(item.file) |
||||
|
const imgRes = await this.getImgPx(fileBase64) |
||||
|
item.file.px = imgRes.width + 'px*' + imgRes.height + 'px' |
||||
|
} else { |
||||
|
item.file.px = '' |
||||
|
} |
||||
|
|
||||
|
jsonString.file_name = item.file.name |
||||
|
jsonString.file_size = item.file.size |
||||
|
jsonString.file_type = item.file.name.substring(item.name.lastIndexOf('.') + 1, item.file.name.length) |
||||
|
json.last_modified = item.file.lastModified |
||||
|
jsonString.file_path = '' |
||||
|
jsonString.sequence = null |
||||
|
jsonString.archive_id = this.arcId |
||||
|
jsonString.file_dpi = item.file.px |
||||
|
jsonString.file_thumbnail = '' |
||||
|
jsonString.create_time = this.nowDate |
||||
|
jsonString.id = null |
||||
|
jsonArray.push(jsonString) |
||||
|
console.log('isBatchMount', this.isBatchMount) |
||||
|
console.log('isBatchMount', typeof this.isBatchMount) |
||||
|
if (this.isBatchMount === 'true') { |
||||
|
json.categoryId = this.selectedCategory.id |
||||
|
} else { |
||||
|
json.documentId = this.selectedDocument.id |
||||
|
} |
||||
|
|
||||
|
json.archivesId = this.arcId |
||||
|
json.identifier = item.uniqueIdentifier |
||||
|
json.filename = item.name |
||||
|
json.totalChunks = item.chunks.length - 1 |
||||
|
json.totalSize = item.size |
||||
|
json.fileJsonString = JSON.stringify(jsonArray) |
||||
|
|
||||
|
jsonArrayToSend.push(json) |
||||
|
})).then(() => { |
||||
|
console.log('jsonArrayToSend', jsonArrayToSend) |
||||
|
|
||||
|
if (this.$refs.uploader.fileList.every(item => item.completed) && this.isUpload) { |
||||
|
if (this.isBatchMount === 'true') { |
||||
|
axios.post(this.baseApi + '/api/collect/merge', jsonArrayToSend, { |
||||
|
headers: { |
||||
|
'Authorization': getToken() |
||||
|
} |
||||
|
}).then((res) => { |
||||
|
console.log(res) |
||||
|
if (res.data.code === 200 && res.data.data[0] !== '') { |
||||
|
this.$message({ message: '文件上传成功', type: 'success', offset: 8 }) |
||||
|
this.$emit('onUploadSuccess', res.data.data, this.fileName, jsonArrayToSend) |
||||
|
} else { |
||||
|
this.$message({ message: '文件上传失败', type: 'error', offset: 8 }) |
||||
|
} |
||||
|
this.isSyncUpload = false |
||||
|
setTimeout(() => { |
||||
|
this.isUpload = false |
||||
|
}, 2000) |
||||
|
}).catch(err => { |
||||
|
this.isSyncUpload = false |
||||
|
setTimeout(() => { |
||||
|
this.isUpload = false |
||||
|
}, 2000) |
||||
|
this.$emit('onUploadError', err) |
||||
|
this.$message({ message: '上传服务器失败', type: 'error', offset: 8 }) |
||||
|
clearInterval(this.queryTimer) |
||||
|
}) |
||||
|
} else { |
||||
|
axios.post(this.baseApi + '/api/re-document/merge', jsonArrayToSend, { |
||||
|
headers: { |
||||
|
'Authorization': getToken() |
||||
|
} |
||||
|
}).then((res) => { |
||||
|
console.log(res) |
||||
|
if (res.data.code === 200 && res.data.data !== '') { |
||||
|
this.$message({ message: '文件上传成功', type: 'success', offset: 8 }) |
||||
|
this.$emit('onUploadSuccess', res.data.data, this.fileName, jsonArrayToSend) |
||||
|
} else { |
||||
|
this.$message({ message: '文件上传失败', type: 'error', offset: 8 }) |
||||
|
} |
||||
|
this.isSyncUpload = false |
||||
|
}).catch(err => { |
||||
|
this.isSyncUpload = false |
||||
|
setTimeout(() => { |
||||
|
this.isUpload = false |
||||
|
}, 2000) |
||||
|
this.$emit('onUploadError', err) |
||||
|
this.$message({ message: '上传服务器失败', type: 'error', offset: 8 }) |
||||
|
clearInterval(this.queryTimer) |
||||
|
}) |
||||
|
} |
||||
|
} else { |
||||
|
this.isSyncUpload = false |
||||
|
setTimeout(() => { |
||||
|
this.isUpload = false |
||||
|
}, 2000) |
||||
|
this.$message({ message: '请耐心等待文件上传完成后再保存!', type: 'error', offset: 8 }) |
||||
|
} |
||||
|
}) |
||||
|
}, |
||||
|
// 上传失败 |
||||
|
onFileError(rootFile, file, response, chunk) { |
||||
|
this.$message({ message: '上传失败', type: 'error', offset: 8 }) |
||||
|
this.$emit('onUploadError', response) |
||||
|
this.isUpload = false |
||||
|
}, |
||||
|
// 将上传的图片转为base64 |
||||
|
getBase64(file) { |
||||
|
const reader = new FileReader() |
||||
|
reader.readAsDataURL(file) |
||||
|
return new Promise((resolve) => { |
||||
|
reader.onload = () => { |
||||
|
resolve(reader.result) |
||||
|
} |
||||
|
}) |
||||
|
}, |
||||
|
// 获取图片的分辨率 |
||||
|
getImgPx(img) { |
||||
|
const image = new Image() |
||||
|
image.src = img |
||||
|
return new Promise((resolve) => { |
||||
|
image.onload = () => { |
||||
|
const width = image.width |
||||
|
const height = image.height |
||||
|
resolve({ width, height }) |
||||
|
} |
||||
|
}) |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
</script> |
||||
|
<style lang="scss" scoped> |
||||
|
.uploader-big{ |
||||
|
position: relative; |
||||
|
// width: 100%; |
||||
|
margin-left: 10px; |
||||
|
.uploader{ |
||||
|
position: relative; |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
justify-content: center; |
||||
|
width: 100%; |
||||
|
text-align: center; |
||||
|
margin-bottom: 8px; |
||||
|
.uploader-drop{ |
||||
|
display: flex; |
||||
|
flex-direction: column; |
||||
|
justify-content: center; |
||||
|
text-align: center; |
||||
|
width: 100px; |
||||
|
height: 34px; |
||||
|
padding: 0; |
||||
|
border: 1px solid #0348f3; |
||||
|
background-color: transparent; |
||||
|
border-radius: 4px; |
||||
|
.uploader-btn{ |
||||
|
width: 100%; |
||||
|
margin: 0; |
||||
|
padding: 0; |
||||
|
border: none; |
||||
|
color: #0348f3; |
||||
|
line-height: 36px; |
||||
|
i{ |
||||
|
font-size: 20px; |
||||
|
color: #1F55EB; |
||||
|
} |
||||
|
&:hover{ |
||||
|
background-color: transparent; |
||||
|
} |
||||
|
} |
||||
|
.el-upload__tip{ |
||||
|
font-size: 12px; |
||||
|
color: #A6ADB6; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.upload_process_box{ |
||||
|
position: absolute; |
||||
|
right: 0; |
||||
|
top: 44px; |
||||
|
width: 540px; |
||||
|
border-radius: 5px; |
||||
|
border: 1px dashed #409eff; |
||||
|
padding: 10px 0; |
||||
|
} |
||||
|
|
||||
|
.uploader-file[status="success"] .uploader-file-remove { |
||||
|
display:block |
||||
|
} |
||||
|
::v-deep .uploader-list{ |
||||
|
max-height: 344px; |
||||
|
overflow-y: scroll; |
||||
|
.uploader-file-size{ |
||||
|
text-align: right !important; |
||||
|
} |
||||
|
.uploader-file-icon:before{ |
||||
|
content: ""; |
||||
|
} |
||||
|
.uploader-file-name{ |
||||
|
text-align: left !important; |
||||
|
} |
||||
|
.uploader-file{ |
||||
|
.uploader-file-icon:before{ |
||||
|
background: url("~@/assets/images/fileIcon/attachment.png") no-repeat; |
||||
|
background-size: 100% 100%; |
||||
|
} |
||||
|
&.icon-image{ |
||||
|
.uploader-file-icon:before{ |
||||
|
background: url("~@/assets/images/fileIcon/image.png") no-repeat; |
||||
|
background-size: 100% 100%; |
||||
|
} |
||||
|
} |
||||
|
&.icon-excel,&.icon-xlsx,&.icon-xls{ |
||||
|
.uploader-file-icon:before{ |
||||
|
background: url("~@/assets/images/fileIcon/excel.png") no-repeat; |
||||
|
background-size: 100% 100%; |
||||
|
} |
||||
|
} |
||||
|
&.icon-pdf{ |
||||
|
.uploader-file-icon:before{ |
||||
|
background: url("~@/assets/images/fileIcon/pdf.png") no-repeat; |
||||
|
background-size: 100% 100%; |
||||
|
} |
||||
|
} |
||||
|
&.icon-ppt, &.icon-pptx{ |
||||
|
.uploader-file-icon:before{ |
||||
|
background: url("~@/assets/images/fileIcon/ppt.png") no-repeat; |
||||
|
background-size: 100% 100%; |
||||
|
} |
||||
|
} |
||||
|
&.icon-word,&.icon-docx,&.icon-doc{ |
||||
|
.uploader-file-icon:before{ |
||||
|
background: url("~@/assets/images/fileIcon/word.png") no-repeat; |
||||
|
background-size: 100% 100%; |
||||
|
} |
||||
|
} |
||||
|
&.icon-zip,&.icon-rar{ |
||||
|
.uploader-file-icon:before{ |
||||
|
background: url("~@/assets/images/fileIcon/zip.png") no-repeat; |
||||
|
background-size: 100% 100%; |
||||
|
} |
||||
|
} |
||||
|
&.icon-txt{ |
||||
|
.uploader-file-icon:before{ |
||||
|
background: url("~@/assets/images/fileIcon/txt.png") no-repeat; |
||||
|
background-size: 100% 100%; |
||||
|
} |
||||
|
} |
||||
|
&.icon-ofd{ |
||||
|
.uploader-file-icon:before{ |
||||
|
background: url("~@/assets/images/fileIcon/OFD.png") no-repeat; |
||||
|
background-size: 100% 100%; |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
</style> |
Write
Preview
Loading…
Cancel
Save
Reference in new issue