11 changed files with 1100 additions and 249 deletions
-
11src/api/prearchiveLibrary/prearchiveLibrary.js
-
11src/api/system/auth2.js
-
25src/utils/index.js
-
5src/views/collectReorganizi/collectionLibrary/anjuan/tableList.vue
-
5src/views/collectReorganizi/collectionLibrary/juannei/index.vue
-
4src/views/collectReorganizi/collectionLibrary/module/collectHeader.vue
-
13src/views/components/category/PreviewForm.vue
-
645src/views/components/category/preUpload4 -数组.vue
-
591src/views/components/category/preUpload4.vue
-
35src/views/prearchiveLibrary/module/detail.vue
-
4src/views/system/archivesCategory/form.vue
@ -0,0 +1,645 @@ |
|||
<template> |
|||
<div class="upload-container"> |
|||
<!-- 多文件选择框:添加multiple属性 --> |
|||
<input type="file" multiple @change="handleFileSelect"> |
|||
<!-- 遍历展示每个文件的上传状态 --> |
|||
<div v-for="(fileItem, index) in fileList" :key="index" class="file-item"> |
|||
<div class="file-name">{{ fileItem.file.name }}</div> |
|||
<!-- 上传进度条 --> |
|||
<div v-if="fileItem.uploading" class="progress-wrapper"> |
|||
<div class="progress-bar" :style="{ width: fileItem.progress + '%' }" /> |
|||
<span class="progress-text">上传进度: {{ fileItem.progress }}%</span> |
|||
</div> |
|||
<!-- 合并中状态 --> |
|||
<div v-if="fileItem.merging" class="merge-loading"> |
|||
<span>合并中...</span> |
|||
</div> |
|||
<!-- 验签中状态 --> |
|||
<div v-if="fileItem.verifying" class="verify-loading"> |
|||
<span>验签中...</span> |
|||
</div> |
|||
<!-- 上传错误信息 --> |
|||
<p v-if="fileItem.errorMsg" class="error">{{ fileItem.errorMsg }}</p> |
|||
<!-- 上传成功+验签结果 --> |
|||
<div v-if="fileItem.successMsg" class="success-section"> |
|||
<p class="success">{{ fileItem.successMsg }}</p> |
|||
<!-- 验签成功 --> |
|||
<p v-if="fileItem.verifySuccess" class="verify-success">✓ 验签成功</p> |
|||
<!-- 验签失败 --> |
|||
<p v-if="fileItem.verifyError" class="verify-error">✗ 验签失败: {{ fileItem.verifyError }}</p> |
|||
</div> |
|||
</div> |
|||
</div> |
|||
</template> |
|||
|
|||
<script> |
|||
import axios from 'axios' |
|||
import SparkMD5 from 'spark-md5' |
|||
import { getToken } from '@/utils/auth' |
|||
|
|||
// 模拟getCurrentTime方法(若项目中有该方法可直接导入) |
|||
const getCurrentTime = () => { |
|||
return new Date().toISOString().replace(/T/, ' ').replace(/\..+/, '') |
|||
} |
|||
|
|||
export default { |
|||
name: 'MinioMultiChunkUpload', |
|||
props: { |
|||
selectedDocument: { |
|||
type: Object, |
|||
default: () => ({}) |
|||
}, |
|||
arcId: { |
|||
type: String, |
|||
default: '' |
|||
}, |
|||
selectedCategory: { |
|||
type: Object, |
|||
default: () => ({}) |
|||
}, |
|||
isBatchMount: { |
|||
type: String, |
|||
default: '' |
|||
} |
|||
}, |
|||
|
|||
data() { |
|||
return { |
|||
fileList: [], // 多文件上传列表,存储每个文件的状态 |
|||
CHUNK_SIZE: 5 * 1024 * 1024, // 分片大小 (5MB) |
|||
totalMergeStartTime: null, // 整体批量合并开始时间 |
|||
totalMergeEndTime: null, // 整体批量合并结束时间 |
|||
baseApi: '', // 接口基础路径(根据项目实际配置) |
|||
allChunksUploaded: false // 所有文件分片是否上传完成 |
|||
} |
|||
}, |
|||
methods: { |
|||
/** |
|||
* 工具方法:格式化时间戳为易读的本地时间(带毫秒) |
|||
* @param {number} timestamp - 时间戳 |
|||
* @returns {string} 格式化后的时间字符串 |
|||
*/ |
|||
formatTime(timestamp) { |
|||
if (!timestamp) return '无' |
|||
return new Date(timestamp).toLocaleString('zh-CN', { |
|||
year: 'numeric', |
|||
month: '2-digit', |
|||
day: '2-digit', |
|||
hour: '2-digit', |
|||
minute: '2-digit', |
|||
second: '2-digit', |
|||
millisecond: '3-digit' |
|||
}) |
|||
}, |
|||
|
|||
/** |
|||
* 工具方法:计算两个时间戳的差值并格式化 |
|||
* @param {number} start - 开始时间戳 |
|||
* @param {number} end - 结束时间戳 |
|||
* @returns {string} 格式化的耗时字符串 |
|||
*/ |
|||
getTimeDiff(start, end) { |
|||
if (!start || !end) return '0ms' |
|||
const diff = end - start |
|||
if (diff < 1000) return `${diff}ms` |
|||
if (diff < 60000) return `${(diff / 1000).toFixed(2)}s` |
|||
return `${(diff / 60000).toFixed(2)}min` |
|||
}, |
|||
|
|||
/** |
|||
* 工具方法:获取图片分辨率 |
|||
* @param {string} base64 - 图片base64编码 |
|||
* @returns {Promise<{width: number, height: number}>} 图片宽高 |
|||
*/ |
|||
getImgPx(base64) { |
|||
return new Promise((resolve) => { |
|||
const img = new Image() |
|||
img.onload = () => { |
|||
resolve({ width: img.width, height: img.height }) |
|||
} |
|||
img.src = base64 |
|||
}) |
|||
}, |
|||
|
|||
/** |
|||
* 工具方法:将文件转为base64 |
|||
* @param {File} file - 文件对象 |
|||
* @returns {Promise<string>} base64编码 |
|||
*/ |
|||
getBase64(file) { |
|||
return new Promise((resolve, reject) => { |
|||
const reader = new FileReader() |
|||
reader.readAsDataURL(file) |
|||
reader.onload = () => resolve(reader.result) |
|||
reader.onerror = (err) => reject(err) |
|||
}) |
|||
}, |
|||
|
|||
/** |
|||
* 工具方法:显示提示信息(可替换为项目的Message组件) |
|||
* @param {string} msg - 提示内容 |
|||
* @param {string} type - 类型(success/error) |
|||
*/ |
|||
showMessage(msg, type) { |
|||
if (type === 'error') { |
|||
console.error(msg) |
|||
} else { |
|||
console.log(msg) |
|||
} |
|||
// 若项目有ElementUI/NaiveUI等组件,可替换为: |
|||
// this.$message[type](msg) |
|||
}, |
|||
|
|||
/** |
|||
* 重置上传状态 |
|||
*/ |
|||
resetUploadState() { |
|||
this.allChunksUploaded = false |
|||
// 可根据需求重置其他状态 |
|||
}, |
|||
|
|||
/** |
|||
* 多文件选择处理函数 |
|||
* @param {Event} e - 选择文件的事件对象 |
|||
*/ |
|||
async handleFileSelect(e) { |
|||
const selectedFiles = Array.from(e.target.files) // 转为数组,支持多文件 |
|||
if (selectedFiles.length === 0) return |
|||
|
|||
// 初始化每个文件的上传状态,加入文件列表(新增合并、验签相关状态) |
|||
const newFileList = selectedFiles.map(file => ({ |
|||
file, // 文件本身 |
|||
uploading: false, // 是否正在上传 |
|||
merging: false, // 是否正在合并 |
|||
progress: 0, // 上传进度 |
|||
errorMsg: '', // 上传/合并错误信息 |
|||
successMsg: '', // 上传成功信息 |
|||
verifying: false, // 是否正在验签 |
|||
verifySuccess: false, // 验签是否成功 |
|||
verifyError: '', // 验签失败信息 |
|||
md5: '', // 文件MD5(新增,用于合并参数) |
|||
// 新增:每个文件的时间记录 |
|||
md5StartTime: null, // MD5计算开始时间 |
|||
md5EndTime: null, // MD5计算结束时间 |
|||
chunkUploadStartTime: null, // 分片上传开始时间 |
|||
chunkUploadEndTime: null, // 分片上传结束时间 |
|||
mergeStartTime: null, // 合并开始时间 |
|||
mergeEndTime: null, // 合并结束时间 |
|||
verifyStartTime: null, // 验签开始时间 |
|||
verifyEndTime: null // 验签结束时间 |
|||
})) |
|||
this.fileList = [...this.fileList, ...newFileList] |
|||
|
|||
// 串行上传文件分片(避免同时上传过多) |
|||
for (const fileItem of newFileList) { |
|||
await this.uploadFileChunks(fileItem) |
|||
} |
|||
|
|||
// 所有文件分片上传完成后,标记状态并触发批量合并 |
|||
this.allChunksUploaded = true |
|||
this.handleUploadConfirm() |
|||
}, |
|||
|
|||
/** |
|||
* 单个文件的分片上传流程(仅上传分片,不立即合并) |
|||
* @param {Object} fileItem - 文件状态对象 |
|||
*/ |
|||
async uploadFileChunks(fileItem) { |
|||
const file = fileItem.file |
|||
fileItem.uploading = true |
|||
fileItem.progress = 0 |
|||
fileItem.errorMsg = '' |
|||
fileItem.successMsg = '' |
|||
|
|||
try { |
|||
// 1. 计算文件MD5 |
|||
const fileMd5 = await this.calculateFileMd5(file, fileItem) |
|||
fileItem.md5 = fileMd5 // 存储MD5用于后续合并 |
|||
console.log(`【${file.name}】文件MD5:`, fileMd5) |
|||
|
|||
// 2. 计算总分片数 |
|||
const totalChunks = Math.ceil(file.size / this.CHUNK_SIZE) |
|||
console.log(`【${file.name}】总分片数:`, totalChunks) |
|||
|
|||
// 3. 检查并上传所有分片(记录分片上传开始时间) |
|||
fileItem.chunkUploadStartTime = new Date().getTime() |
|||
console.log(`【${file.name}】分片上传开始时间:${this.formatTime(fileItem.chunkUploadStartTime)}`) |
|||
|
|||
const uploadedChunks = [] |
|||
for (let i = 0; i < totalChunks; i++) { |
|||
// 检查分片是否已存在(断点续传) |
|||
const checkResult = await this.checkChunkExists(fileMd5, i) |
|||
|
|||
if (!checkResult.exists) { |
|||
// 上传未存在的分片 |
|||
await this.uploadSingleChunk(file, fileMd5, i) |
|||
} |
|||
|
|||
uploadedChunks.push(i) |
|||
fileItem.progress = Math.round((uploadedChunks.length / totalChunks) * 100) |
|||
} |
|||
|
|||
// 记录分片上传结束时间 |
|||
fileItem.chunkUploadEndTime = new Date().getTime() |
|||
console.log(`【${file.name}】分片上传结束时间:${this.formatTime(fileItem.chunkUploadEndTime)},耗时:${this.getTimeDiff(fileItem.chunkUploadStartTime, fileItem.chunkUploadEndTime)}`) |
|||
fileItem.progress = 100 // 分片上传完成,进度置为100% |
|||
} catch (err) { |
|||
// 异常时补全各阶段时间记录并打印 |
|||
const fileName = file.name |
|||
if (fileItem.chunkUploadStartTime && !fileItem.chunkUploadEndTime) { |
|||
fileItem.chunkUploadEndTime = new Date().getTime() |
|||
console.log(`【${fileName}】分片上传异常结束时间:${this.formatTime(fileItem.chunkUploadEndTime)},耗时:${this.getTimeDiff(fileItem.chunkUploadStartTime, fileItem.chunkUploadEndTime)}`) |
|||
} |
|||
fileItem.errorMsg = '分片上传失败: ' + (err.message || '未知错误') |
|||
console.error(`【${fileName}】分片上传流程异常:`, err.message) |
|||
this.allChunksUploaded = false // 有文件分片上传失败,取消合并触发 |
|||
} finally { |
|||
fileItem.uploading = false |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* 计算文件MD5(用于分片唯一标识) |
|||
* @param {File} file - 待上传的文件 |
|||
* @param {Object} fileItem - 文件状态对象 |
|||
* @returns {Promise<string>} 文件的MD5值 |
|||
*/ |
|||
calculateFileMd5(file, fileItem) { |
|||
return new Promise((resolve, reject) => { |
|||
// 记录MD5计算开始时间 |
|||
fileItem.md5StartTime = new Date().getTime() |
|||
console.log(`【${file.name}】MD5计算开始时间:${this.formatTime(fileItem.md5StartTime)}`) |
|||
|
|||
const spark = new SparkMD5.ArrayBuffer() |
|||
const fileReader = new FileReader() |
|||
const chunkSize = 2 * 1024 * 1024 // 计算MD5的分片大小 |
|||
let offset = 0 |
|||
|
|||
const loadNextChunk = () => { |
|||
const blob = file.slice(offset, offset + chunkSize) |
|||
fileReader.readAsArrayBuffer(blob) |
|||
} |
|||
|
|||
fileReader.onload = (e) => { |
|||
spark.append(e.target.result) |
|||
offset += chunkSize |
|||
|
|||
if (offset < file.size) { |
|||
loadNextChunk() |
|||
} else { |
|||
// 记录MD5计算结束时间 |
|||
fileItem.md5EndTime = new Date().getTime() |
|||
console.log(`【${file.name}】MD5计算结束时间:${this.formatTime(fileItem.md5EndTime)},耗时:${this.getTimeDiff(fileItem.md5StartTime, fileItem.md5EndTime)}`) |
|||
resolve(spark.end()) |
|||
} |
|||
} |
|||
|
|||
fileReader.onerror = (err) => { |
|||
reject(err) |
|||
} |
|||
loadNextChunk() |
|||
}) |
|||
}, |
|||
|
|||
/** |
|||
* 校验所有分片是否存在(合并前的二次校验) |
|||
* @param {string} fileMd5 - 文件MD5 |
|||
* @param {number} totalChunks - 总分片数 |
|||
* @returns {Promise<boolean>} 所有分片是否都存在 |
|||
*/ |
|||
async checkAllChunksExist(fileMd5, totalChunks) { |
|||
for (let i = 0; i < totalChunks; i++) { |
|||
const result = await this.checkChunkExists(fileMd5, i) |
|||
if (!result.exists) { |
|||
console.warn(`【${fileMd5}】分片${i}未上传`) |
|||
return false |
|||
} |
|||
} |
|||
return true |
|||
}, |
|||
|
|||
/** |
|||
* 检查分片是否已存在(断点续传核心) |
|||
* @param {string} fileMd5 - 文件MD5 |
|||
* @param {number} chunkIndex - 分片索引 |
|||
* @returns {Promise<Object>} 分片存在状态 |
|||
*/ |
|||
async checkChunkExists(fileMd5, chunkIndex) { |
|||
const response = await axios.get('/api/minioUpload/chunk', { |
|||
params: { |
|||
fileMd5, |
|||
chunkIndex |
|||
}, |
|||
headers: { 'Authorization': getToken() } |
|||
}) |
|||
|
|||
if (response.data.code !== 200) { |
|||
throw new Error('检查分片失败: ' + response.data.msg) |
|||
} |
|||
|
|||
return response.data.data |
|||
}, |
|||
|
|||
/** |
|||
* 上传单个分片 |
|||
* @param {File} file - 待上传的文件 |
|||
* @param {string} fileMd5 - 文件MD5 |
|||
* @param {number} chunkIndex - 分片索引 |
|||
*/ |
|||
async uploadSingleChunk(file, fileMd5, chunkIndex) { |
|||
// 计算分片的起始和结束位置 |
|||
const start = chunkIndex * this.CHUNK_SIZE |
|||
const end = Math.min(start + this.CHUNK_SIZE, file.size) |
|||
const chunkBlob = file.slice(start, end) |
|||
|
|||
// 创建FormData |
|||
const formData = new FormData() |
|||
formData.append('file', chunkBlob, `${fileMd5}_${chunkIndex}`) |
|||
formData.append('fileMd5', fileMd5) |
|||
formData.append('chunkIndex', chunkIndex) |
|||
|
|||
const response = await axios.post('/api/minioUpload/chunk', formData, { |
|||
headers: { |
|||
'Content-Type': 'multipart/form-data', |
|||
'Authorization': getToken() |
|||
} |
|||
}) |
|||
|
|||
if (response.data.code !== 200) { |
|||
throw new Error(`分片${chunkIndex}上传失败: ` + response.data.msg) |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* 所有文件分片上传完成后自动执行批量合并(核心改造方法) |
|||
*/ |
|||
async handleUploadConfirm() { |
|||
if (this.fileList.length === 0 || !this.allChunksUploaded) { |
|||
this.showMessage('没有可处理的文件或分片上传未完成!', 'error') |
|||
return |
|||
} |
|||
|
|||
// 过滤出分片上传成功的文件 |
|||
const validFiles = this.fileList.filter(item => !item.errorMsg && item.progress === 100) |
|||
if (validFiles.length === 0) { |
|||
this.showMessage('无有效分片上传完成的文件!', 'error') |
|||
return |
|||
} |
|||
|
|||
// 记录整体合并开始时间 |
|||
this.totalMergeStartTime = new Date().getTime() |
|||
console.log(`【整体合并】所有文件分片上传完成,开始合并,合并开始时间:${this.formatTime(this.totalMergeStartTime)}`) |
|||
const nowDate = getCurrentTime() |
|||
const jsonArrayToSend = [] |
|||
|
|||
// 标记所有文件为合并中,并记录单个文件合并开始时间 |
|||
validFiles.forEach(fileItem => { |
|||
fileItem.merging = true |
|||
fileItem.mergeStartTime = new Date().getTime() |
|||
console.log(`【文件${fileItem.file.name}】合并开始时间:${this.formatTime(fileItem.mergeStartTime)}`) |
|||
}) |
|||
|
|||
try { |
|||
// 处理所有文件信息,构建合并参数 |
|||
const processFiles = validFiles.map(async(fileItem) => { |
|||
const file = fileItem.file |
|||
const json = {} |
|||
const jsonArray = [] |
|||
const jsonString = {} |
|||
|
|||
// 处理图片文件分辨率 |
|||
if (file.type.startsWith('image')) { |
|||
const fileBase64 = await this.getBase64(file) |
|||
const imgRes = await this.getImgPx(fileBase64) |
|||
jsonString.file_dpi = `${imgRes.width}px*${imgRes.height}px` |
|||
} else { |
|||
jsonString.file_dpi = '' |
|||
} |
|||
|
|||
// 构建文件信息(按handleUploadConfirm的参数结构) |
|||
jsonString.file_name = file.name |
|||
jsonString.file_size = file.size |
|||
jsonString.file_type = file.name.split('.').pop() || '' |
|||
json.last_modified = file.lastModified |
|||
jsonString.file_path = '' |
|||
jsonString.sequence = null |
|||
jsonString.archive_id = this.arcId |
|||
jsonString.create_time = nowDate |
|||
jsonString.id = null |
|||
jsonString.file_thumbnail = '' |
|||
jsonArray.push(jsonString) |
|||
|
|||
// 根据挂载类型设置不同参数 |
|||
if (this.isBatchMount === 'true') { |
|||
json.categoryId = this.selectedCategory.id |
|||
} else { |
|||
json.documentId = this.selectedDocument.id |
|||
} |
|||
|
|||
// 构建合并核心参数 |
|||
const totalChunks = Math.ceil(file.size / this.CHUNK_SIZE) |
|||
// 二次校验分片是否全部存在 |
|||
const chunksExist = await this.checkAllChunksExist(fileItem.md5, totalChunks) |
|||
if (!chunksExist) { |
|||
throw new Error(`【${file.name}】部分分片未上传完成,无法合并`) |
|||
} |
|||
|
|||
json.archivesId = this.arcId |
|||
json.identifier = fileItem.md5 |
|||
json.filename = file.name |
|||
json.totalChunks = totalChunks |
|||
json.totalSize = file.size |
|||
json.fileJsonString = JSON.stringify(jsonArray) |
|||
|
|||
jsonArrayToSend.push(json) |
|||
return json |
|||
}) |
|||
|
|||
// 等待所有文件参数构建完成 |
|||
const jsonArray = await Promise.all(processFiles) |
|||
|
|||
// 调用批量合并接口(保留原/api/minioUpload/merge接口) |
|||
const response = await axios.post('/api/minioUpload/merge', jsonArray, { |
|||
headers: { |
|||
'Authorization': getToken(), |
|||
'Content-Type': 'application/json' // 批量合并传JSON数组 |
|||
} |
|||
}) |
|||
|
|||
// 记录整体合并结束时间 |
|||
this.totalMergeEndTime = new Date().getTime() |
|||
const totalMergeDuration = this.getTimeDiff(this.totalMergeStartTime, this.totalMergeEndTime) |
|||
|
|||
if (response.data.code === 200) { |
|||
this.showMessage('所有文件合并成功', 'success') |
|||
// 更新每个文件的合并状态和时间 |
|||
validFiles.forEach((fileItem, index) => { |
|||
fileItem.mergeEndTime = new Date().getTime() |
|||
const mergeDuration = this.getTimeDiff(fileItem.mergeStartTime, fileItem.mergeEndTime) |
|||
console.log(`【文件${fileItem.file.name}】合并结束时间:${this.formatTime(fileItem.mergeEndTime)},合并耗时:${mergeDuration}`) |
|||
// 从合并结果中获取文件路径(需后端返回对应文件的路径) |
|||
const mergeResult = response.data.data[index] || {} |
|||
fileItem.successMsg = '上传成功! 路径: ' + (mergeResult.filePath || '未知路径') |
|||
fileItem.merging = false |
|||
|
|||
// 合并成功后立即触发验签 |
|||
this.verifySignature(fileItem, mergeResult) |
|||
}) |
|||
console.log(`【整体合并】所有文件合并完成,合并结束时间:${this.formatTime(this.totalMergeEndTime)},整体合并耗时:${totalMergeDuration}`) |
|||
this.$emit('onUploadSuccess', response.data.data, validFiles.map(f => f.file.name), jsonArrayToSend) |
|||
} else { |
|||
throw new Error(response.data.msg || '合并失败') |
|||
} |
|||
} catch (err) { |
|||
// 合并失败处理 |
|||
this.totalMergeEndTime = new Date().getTime() |
|||
const totalMergeDuration = this.getTimeDiff(this.totalMergeStartTime, this.totalMergeEndTime) |
|||
console.log(`【整体合并】合并失败,失败时间:${this.formatTime(this.totalMergeEndTime)},合并耗时:${totalMergeDuration},异常信息:`, err) |
|||
this.showMessage(`合并失败: ${err.message}`, 'error') |
|||
// 更新每个文件的合并错误状态 |
|||
validFiles.forEach(fileItem => { |
|||
fileItem.merging = false |
|||
fileItem.errorMsg = fileItem.errorMsg || `合并失败: ${err.message}` |
|||
}) |
|||
this.$emit('onUploadError', err) |
|||
} finally { |
|||
this.resetUploadState() |
|||
} |
|||
}, |
|||
|
|||
/** |
|||
* 调用验签接口验证签名 |
|||
* @param {Object} fileItem - 文件状态对象 |
|||
* @param {Object} mergeResult - 合并成功后的返回数据 |
|||
*/ |
|||
async verifySignature(fileItem, mergeResult) { |
|||
const file = fileItem.file |
|||
const verifyParams = { |
|||
certFingerprint: mergeResult.certFingerprint, |
|||
filePath: mergeResult.filePath, |
|||
signature: mergeResult.signature, |
|||
timestamp: mergeResult.timestamp |
|||
} |
|||
console.log(`【${file.name}】验签参数:`, verifyParams) |
|||
|
|||
fileItem.verifying = true |
|||
try { |
|||
const response = await axios.post( |
|||
'/api/minioUpload/verify-signature', |
|||
null, |
|||
{ |
|||
params: verifyParams, |
|||
headers: { 'Authorization': getToken() } |
|||
} |
|||
) |
|||
|
|||
console.log(`【${file.name}】验签接口响应:`, response.data) |
|||
|
|||
if (response.data.code === 200) { // 调整为实际的成功码 |
|||
fileItem.verifySuccess = true |
|||
} else { |
|||
fileItem.verifyError = response.data.error || '验签失败,原因未知' |
|||
} |
|||
} catch (err) { |
|||
fileItem.verifyError = `请求异常: ${err.message || '网络错误'}` |
|||
console.error(`【${file.name}】验签请求异常:`, err) |
|||
} finally { |
|||
// 记录验签结束时间 |
|||
fileItem.verifyEndTime = new Date().getTime() |
|||
fileItem.verifying = false |
|||
// 打印文件全流程耗时 |
|||
const totalTime = this.getTimeDiff(fileItem.md5StartTime, fileItem.verifyEndTime) |
|||
console.log(`【${file.name}】上传+合并+验签全流程总耗时:${totalTime}`) |
|||
} |
|||
} |
|||
} |
|||
} |
|||
</script> |
|||
|
|||
<style scoped> |
|||
.upload-container { |
|||
max-width: 800px; |
|||
margin: 20px auto; |
|||
padding: 20px; |
|||
border: 1px solid #e0e0e0; |
|||
border-radius: 8px; |
|||
} |
|||
|
|||
/* 单个文件项样式 */ |
|||
.file-item { |
|||
margin-top: 20px; |
|||
padding: 15px; |
|||
border: 1px solid #f0f0f0; |
|||
border-radius: 6px; |
|||
} |
|||
|
|||
.file-name { |
|||
font-weight: 500; |
|||
margin-bottom: 10px; |
|||
color: #333; |
|||
} |
|||
|
|||
/* 进度条容器 */ |
|||
.progress-wrapper { |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 5px; |
|||
} |
|||
|
|||
.progress-bar { |
|||
height: 20px; |
|||
background-color: #42b983; |
|||
transition: width 0.3s ease; |
|||
border-radius: 10px; |
|||
} |
|||
|
|||
.progress-text { |
|||
font-size: 14px; |
|||
color: #666; |
|||
} |
|||
|
|||
/* 合并中状态 */ |
|||
.merge-loading { |
|||
margin: 10px 0; |
|||
color: #1890ff; |
|||
font-size: 14px; |
|||
} |
|||
|
|||
/* 验签中状态 */ |
|||
.verify-loading { |
|||
margin: 10px 0; |
|||
color: #42b983; |
|||
font-size: 14px; |
|||
} |
|||
|
|||
/* 上传和验签提示样式 */ |
|||
.success-section { |
|||
display: flex; |
|||
flex-direction: column; |
|||
gap: 5px; |
|||
} |
|||
|
|||
.success { |
|||
color: #00C851; |
|||
margin: 0; |
|||
font-size: 14px; |
|||
} |
|||
|
|||
.error { |
|||
color: #ff4444; |
|||
margin: 10px 0 0 0; |
|||
font-size: 14px; |
|||
} |
|||
|
|||
.verify-success { |
|||
color: #00C851; |
|||
font-size: 14px; |
|||
margin: 0; |
|||
} |
|||
|
|||
.verify-error { |
|||
color: #ff4444; |
|||
font-size: 14px; |
|||
margin: 0; |
|||
} |
|||
</style> |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue