|
@@ -1,9 +1,4 @@
|
|
|
<template>
|
|
<template>
|
|
|
- <div class="upload-area" @click="handleUpload">
|
|
|
|
|
- <a-icon type="cloud-upload" style="font-size: 60px; color: #3ca9f5" />
|
|
|
|
|
- <p>点击上传</p>
|
|
|
|
|
- <p>按住Ctrl可同时多选,支持上传PPT/word/excel/pdf/mp4/zip/rar,单个文件不能超过2G</p>
|
|
|
|
|
- </div>
|
|
|
|
|
<!-- 上传组件 -->
|
|
<!-- 上传组件 -->
|
|
|
<uploader
|
|
<uploader
|
|
|
class="uploader-app"
|
|
class="uploader-app"
|
|
@@ -15,6 +10,7 @@
|
|
|
@file-success="handleFileSuccess"
|
|
@file-success="handleFileSuccess"
|
|
|
@file-error="handleFileError"
|
|
@file-error="handleFileError"
|
|
|
@dragleave="hideUploadMask"
|
|
@dragleave="hideUploadMask"
|
|
|
|
|
+ @file-progress="handleFileProgress"
|
|
|
>
|
|
>
|
|
|
<uploader-unsupport></uploader-unsupport>
|
|
<uploader-unsupport></uploader-unsupport>
|
|
|
<!-- 选择按钮 在这里隐藏 -->
|
|
<!-- 选择按钮 在这里隐藏 -->
|
|
@@ -22,15 +18,10 @@
|
|
|
<uploader-btn class="select-file-btn" :attrs="attrs" :directory="true" ref="uploadDirBtn"> 选择目录 </uploader-btn>
|
|
<uploader-btn class="select-file-btn" :attrs="attrs" :directory="true" ref="uploadDirBtn"> 选择目录 </uploader-btn>
|
|
|
|
|
|
|
|
<!-- 拖拽上传 -->
|
|
<!-- 拖拽上传 -->
|
|
|
- <uploader-drop class="drop-box" id="dropBox" @paste="handlePaste" v-show="dropBoxShow">
|
|
|
|
|
- <div class="paste-img-wrapper" v-show="pasteImg.src">
|
|
|
|
|
- <div class="paste-name">{{ pasteImg.name }}</div>
|
|
|
|
|
- <img class="paste-img" :src="pasteImg.src" :alt="pasteImg.name" v-if="pasteImg.src" />
|
|
|
|
|
- </div>
|
|
|
|
|
- <span class="text" v-show="!pasteImg.src"> 截图粘贴或将文件拖拽至此区域上传 </span>
|
|
|
|
|
|
|
+ <uploader-drop class="drop-box" id="dropBox" @paste="handlePaste" @click="handleUpload">
|
|
|
|
|
+ <span class="text"> 点击上传或者截图粘贴或将文件拖拽至此区域上传 </span>
|
|
|
|
|
+ <p class="text">按住Ctrl可同时多选,支持上传PPT/word/excel/pdf/mp4/zip/rar,单个文件不能超过2G</p>
|
|
|
<UploadOutlined class="upload-icon" v-show="pasteImg.src" @click="handleUploadPasteImg" />
|
|
<UploadOutlined class="upload-icon" v-show="pasteImg.src" @click="handleUploadPasteImg" />
|
|
|
- <DeleteOutlined class="delete-icon" @click="handleDeletePasteImg" />
|
|
|
|
|
- <CloseCircleOutlined class="close-icon" @click="dropBoxShow = false" />
|
|
|
|
|
</uploader-drop>
|
|
</uploader-drop>
|
|
|
|
|
|
|
|
<!-- 上传列表 -->
|
|
<!-- 上传列表 -->
|
|
@@ -49,10 +40,11 @@
|
|
|
v-for="file in props.fileList"
|
|
v-for="file in props.fileList"
|
|
|
:key="file.id"
|
|
:key="file.id"
|
|
|
class="file-item"
|
|
class="file-item"
|
|
|
- :class="{ 'custom-status-item': file.statusStr !== '' }"
|
|
|
|
|
- >
|
|
|
|
|
|
|
+ :class="{ 'custom-status-item': file.statusStr !== '' }">
|
|
|
<uploader-file ref="fileItem" :file="file" :list="true" />
|
|
<uploader-file ref="fileItem" :file="file" :list="true" />
|
|
|
<span class="custom-status">{{ file.statusStr }}</span>
|
|
<span class="custom-status">{{ file.statusStr }}</span>
|
|
|
|
|
+ <!-- 添加剩余时间显示 -->
|
|
|
|
|
+ <!-- <span class="remaining-time" v-if="file.remainingTime"> 剩余: {{ file.remainingTime }} </span> -->
|
|
|
</li>
|
|
</li>
|
|
|
<div class="no-file" v-if="!props.fileList.length"><FileExclamationOutlined /> 暂无待上传文件</div>
|
|
<div class="no-file" v-if="!props.fileList.length"><FileExclamationOutlined /> 暂无待上传文件</div>
|
|
|
</ul>
|
|
</ul>
|
|
@@ -223,12 +215,21 @@
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const handleFilesAdded = (filesSource) => {
|
|
const handleFilesAdded = (filesSource) => {
|
|
|
|
|
+ console.log('handleFilesAdded', filesSource)
|
|
|
const filesTotalSize = filesSource
|
|
const filesTotalSize = filesSource
|
|
|
- .map((item) => item.size)
|
|
|
|
|
|
|
+ .map((item) => {
|
|
|
|
|
+ console.log(item, 'itemitemitemitem')
|
|
|
|
|
+ // 为每个文件添加上传速度跟踪属性
|
|
|
|
|
+ item.speed = 0
|
|
|
|
|
+ item.lastLoaded = item.loaded || 0
|
|
|
|
|
+ item.lastTime = Date.now()
|
|
|
|
|
+ item.remainingTime = '计算中...'
|
|
|
|
|
+ return item
|
|
|
|
|
+ })
|
|
|
.reduce((pre, next) => {
|
|
.reduce((pre, next) => {
|
|
|
- return pre + next
|
|
|
|
|
|
|
+ return pre + next.size
|
|
|
}, 0)
|
|
}, 0)
|
|
|
-
|
|
|
|
|
|
|
+ console.log('handleFilesAdded', filesSource)
|
|
|
if (remainderStorageValue.value < filesTotalSize) {
|
|
if (remainderStorageValue.value < filesTotalSize) {
|
|
|
// 批量选择的文件超出剩余存储空间
|
|
// 批量选择的文件超出剩余存储空间
|
|
|
message.warning(`剩余存储空间不足,请重新选择${filesSource.length > 1 ? '批量' : ''}文件`)
|
|
message.warning(`剩余存储空间不足,请重新选择${filesSource.length > 1 ? '批量' : ''}文件`)
|
|
@@ -246,6 +247,85 @@
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ const handleFileProgress = (file) => {
|
|
|
|
|
+ const now = Date.now()
|
|
|
|
|
+ const duration = (now - file.lastTime) / 1000 // 秒
|
|
|
|
|
+ const loadedDiff = file.lastTime - file.lastLoaded
|
|
|
|
|
+ console.log('duration', now, file.lastTime, file.lastLoaded, duration, loadedDiff)
|
|
|
|
|
+ if (duration > 0) {
|
|
|
|
|
+ console.log('进来了', file)
|
|
|
|
|
+ // 计算当前上传速度 (bytes/sec)
|
|
|
|
|
+ file.speed = loadedDiff / duration
|
|
|
|
|
+
|
|
|
|
|
+ // 计算剩余时间
|
|
|
|
|
+ if (file.speed > 0) {
|
|
|
|
|
+ const remainingBytes = file.size - file.loaded
|
|
|
|
|
+ const remainingSeconds = remainingBytes / file.speed
|
|
|
|
|
+ console.log('进来了计算剩余时间', remainingBytes, remainingSeconds)
|
|
|
|
|
+ // 格式化剩余时间显示
|
|
|
|
|
+ if (remainingSeconds < 60) {
|
|
|
|
|
+ console.log('进来了', 60)
|
|
|
|
|
+ file.remainingTime = `${Math.round(remainingSeconds)}秒`
|
|
|
|
|
+ } else if (remainingSeconds < 3600) {
|
|
|
|
|
+ console.log('进来了', 600)
|
|
|
|
|
+ file.remainingTime = `${Math.round(remainingSeconds / 60)}分钟`
|
|
|
|
|
+ } else {
|
|
|
|
|
+ console.log('进来了', 3600)
|
|
|
|
|
+ file.remainingTime = `${Math.round(remainingSeconds / 3600)}小时`
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 更新最后记录的时间和已上传量
|
|
|
|
|
+ file.lastLoaded = file.loaded
|
|
|
|
|
+ file.lastTime = now
|
|
|
|
|
+ }
|
|
|
|
|
+ // const handleFileProgress = (file) => {
|
|
|
|
|
+ // console.log(file, '进来了filefilefile')
|
|
|
|
|
+ // const now = Date.now()
|
|
|
|
|
+
|
|
|
|
|
+ // // 确保文件对象有必要的属性
|
|
|
|
|
+ // if (!file.lastTime) file.lastTime = now
|
|
|
|
|
+ // if (file.lastLoaded === undefined) file.lastLoaded = 0
|
|
|
|
|
+
|
|
|
|
|
+ // const duration = (now - file.lastTime) / 1000 // 转换为秒
|
|
|
|
|
+ // console.log(duration, '进来了duration')
|
|
|
|
|
+ // // 只有当时间间隔足够大时才计算速度(至少0.1秒)
|
|
|
|
|
+ // if (duration > 0.1) {
|
|
|
|
|
+ // console.log(duration, '进来了duration')
|
|
|
|
|
+ // const loadedDiff = file.loaded - file.lastLoaded
|
|
|
|
|
+
|
|
|
|
|
+ // // 只有当有新的数据上传时才计算
|
|
|
|
|
+ // if (loadedDiff > 0) {
|
|
|
|
|
+ // console.log(loadedDiff, '进来了loadedDiff')
|
|
|
|
|
+
|
|
|
|
|
+ // file.speed = loadedDiff / duration
|
|
|
|
|
+ // // 计算剩余时间(确保speed是有效数字)
|
|
|
|
|
+ // if (file.speed > 0 && !isNaN(file.speed)) {
|
|
|
|
|
+ // const remainingBytes = file.size - file.loaded
|
|
|
|
|
+ // const remainingSeconds = remainingBytes / file.speed
|
|
|
|
|
+ // // 格式化剩余时间
|
|
|
|
|
+ // if (remainingSeconds < 60) {
|
|
|
|
|
+ // file.remainingTime = `${Math.round(remainingSeconds)}秒`
|
|
|
|
|
+ // } else if (remainingSeconds < 3600) {
|
|
|
|
|
+ // const mins = Math.floor(remainingSeconds / 60)
|
|
|
|
|
+ // const secs = Math.round(remainingSeconds % 60)
|
|
|
|
|
+ // file.remainingTime = `${mins}分${secs}秒`
|
|
|
|
|
+ // } else {
|
|
|
|
|
+ // const hours = Math.floor(remainingSeconds / 3600)
|
|
|
|
|
+ // const mins = Math.round((remainingSeconds % 3600) / 60)
|
|
|
|
|
+ // file.remainingTime = `${hours}小时${mins}分`
|
|
|
|
|
+ // }
|
|
|
|
|
+ // } else {
|
|
|
|
|
+ // file.remainingTime = '计算中...'
|
|
|
|
|
+ // }
|
|
|
|
|
+
|
|
|
|
|
+ // // 更新最后记录的时间和已上传量
|
|
|
|
|
+ // file.lastLoaded = file.loaded
|
|
|
|
|
+ // file.lastTime = now
|
|
|
|
|
+ // }
|
|
|
|
|
+ // }
|
|
|
|
|
+ // }
|
|
|
const handleFileSuccess = (rootFile, file, response) => {
|
|
const handleFileSuccess = (rootFile, file, response) => {
|
|
|
if (response === '') {
|
|
if (response === '') {
|
|
|
uploadStatus.value[file.id] = '上传失败'
|
|
uploadStatus.value[file.id] = '上传失败'
|
|
@@ -334,7 +414,7 @@
|
|
|
file.resume()
|
|
file.resume()
|
|
|
file.statusStr = ''
|
|
file.statusStr = ''
|
|
|
// 文件开始上传时,禁止关闭弹窗
|
|
// 文件开始上传时,禁止关闭弹窗
|
|
|
- canClose.value = false
|
|
|
|
|
|
|
+ // canClose.value = false
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
const handleCancel = () => {
|
|
const handleCancel = () => {
|
|
@@ -391,13 +471,16 @@
|
|
|
|
|
|
|
|
<style lang="less" scoped>
|
|
<style lang="less" scoped>
|
|
|
@import '@/style/myResource/varibles.less';
|
|
@import '@/style/myResource/varibles.less';
|
|
|
|
|
+
|
|
|
.upload-btn-wrapper {
|
|
.upload-btn-wrapper {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
align-items: center;
|
|
align-items: center;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
.uploader-file {
|
|
.uploader-file {
|
|
|
width: 560px;
|
|
width: 560px;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
.select-file-btn {
|
|
.select-file-btn {
|
|
|
display: none;
|
|
display: none;
|
|
|
}
|
|
}
|
|
@@ -416,7 +499,7 @@
|
|
|
margin-top: 10px;
|
|
margin-top: 10px;
|
|
|
|
|
|
|
|
.text {
|
|
.text {
|
|
|
- font-size: 16px;
|
|
|
|
|
|
|
+ font-size: 14px;
|
|
|
color: #999;
|
|
color: #999;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -466,9 +549,11 @@
|
|
|
color: #999;
|
|
color: #999;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
.uploader-app {
|
|
.uploader-app {
|
|
|
width: 560px;
|
|
width: 560px;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
.file-panel {
|
|
.file-panel {
|
|
|
width: 100%;
|
|
width: 100%;
|
|
|
margin-top: 15px;
|
|
margin-top: 15px;
|
|
@@ -503,6 +588,7 @@
|
|
|
background-color: #fff;
|
|
background-color: #fff;
|
|
|
font-size: 12px;
|
|
font-size: 12px;
|
|
|
list-style: none;
|
|
list-style: none;
|
|
|
|
|
+
|
|
|
&::-webkit-scrollbar {
|
|
&::-webkit-scrollbar {
|
|
|
width: 6px;
|
|
width: 6px;
|
|
|
}
|
|
}
|
|
@@ -515,51 +601,80 @@
|
|
|
&::-webkit-scrollbar-track {
|
|
&::-webkit-scrollbar-track {
|
|
|
background: @scrollbar-track-color;
|
|
background: @scrollbar-track-color;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
.file-item {
|
|
.file-item {
|
|
|
position: relative;
|
|
position: relative;
|
|
|
- padding: 8px 12px;
|
|
|
|
|
- width: 100%;
|
|
|
|
|
- border-bottom: 1px solid #f0f0f0;
|
|
|
|
|
-
|
|
|
|
|
- &:last-child {
|
|
|
|
|
- border-bottom: none;
|
|
|
|
|
|
|
+ background-color: #fff;
|
|
|
|
|
+
|
|
|
|
|
+ :deep(.uploader-file) {
|
|
|
|
|
+ height: 40px;
|
|
|
|
|
+ line-height: 40px;
|
|
|
|
|
+
|
|
|
|
|
+ .uploader-file-progress {
|
|
|
|
|
+ border: 1px solid @success-color;
|
|
|
|
|
+ border-right: none;
|
|
|
|
|
+ border-left: none;
|
|
|
|
|
+ background: #e1f3d8;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .uploader-file-name {
|
|
|
|
|
+ width: 44%;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .uploader-file-size {
|
|
|
|
|
+ width: 16%;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .uploader-file-meta {
|
|
|
|
|
+ display: none;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .uploader-file-status {
|
|
|
|
|
+ width: 30%;
|
|
|
|
|
+ text-indent: 0;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ .uploader-file-actions>span {
|
|
|
|
|
+ margin-top: 12px;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- .uploader-file-info {
|
|
|
|
|
- width: 540px;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .uploader-file-name {
|
|
|
|
|
- width: 44%;
|
|
|
|
|
- }
|
|
|
|
|
-
|
|
|
|
|
- .uploader-file-size {
|
|
|
|
|
- width: 16%;
|
|
|
|
|
- }
|
|
|
|
|
- .uploader-file-status {
|
|
|
|
|
- width: 30%;
|
|
|
|
|
- text-indent: 0;
|
|
|
|
|
|
|
+ :deep(.uploader-file[status='success']) {
|
|
|
|
|
+ .uploader-file-progress {
|
|
|
|
|
+ border: none;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- &.custom-status-item {
|
|
|
|
|
- padding-right: 100px;
|
|
|
|
|
|
|
+ .file-item.custom-status-item {
|
|
|
|
|
+ :deep(.uploader-file-status) {
|
|
|
|
|
+ visibility: hidden;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.custom-status {
|
|
.custom-status {
|
|
|
position: absolute;
|
|
position: absolute;
|
|
|
- right: 12px;
|
|
|
|
|
- top: 50%;
|
|
|
|
|
- transform: translateY(-50%);
|
|
|
|
|
- color: #1878ff;
|
|
|
|
|
|
|
+ top: 0;
|
|
|
|
|
+ right: 10%;
|
|
|
|
|
+ width: 24%;
|
|
|
|
|
+ height: 40px;
|
|
|
|
|
+ line-height: 40px;
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
.no-file {
|
|
.no-file {
|
|
|
- padding: 20px 0;
|
|
|
|
|
- text-align: center;
|
|
|
|
|
- color: #999;
|
|
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ top: 50%;
|
|
|
|
|
+ left: 50%;
|
|
|
|
|
+ transform: translate(-50%, -50%);
|
|
|
font-size: 14px;
|
|
font-size: 14px;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ :deep(.uploader-file-icon) {
|
|
|
|
|
+ display: none;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ :deep(.uploader-file-actions > span) {
|
|
|
|
|
+ margin-right: 6px;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
@@ -574,6 +689,7 @@
|
|
|
.collapse-leave-to {
|
|
.collapse-leave-to {
|
|
|
max-height: 0;
|
|
max-height: 0;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
.upload-area {
|
|
.upload-area {
|
|
|
border: 2px dashed #3ca9f5;
|
|
border: 2px dashed #3ca9f5;
|
|
|
padding: 40px;
|
|
padding: 40px;
|
|
@@ -599,6 +715,7 @@
|
|
|
.ant-form-item {
|
|
.ant-form-item {
|
|
|
margin-bottom: 16px;
|
|
margin-bottom: 16px;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
.public-status-buttons {
|
|
.public-status-buttons {
|
|
|
display: flex;
|
|
display: flex;
|
|
|
}
|
|
}
|
|
@@ -617,15 +734,26 @@
|
|
|
color: #fff;
|
|
color: #fff;
|
|
|
border-color: #40a9ff;
|
|
border-color: #40a9ff;
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
.upload-area {
|
|
.upload-area {
|
|
|
border: 2px dashed #3ca9f5;
|
|
border: 2px dashed #3ca9f5;
|
|
|
padding: 40px;
|
|
padding: 40px;
|
|
|
text-align: center;
|
|
text-align: center;
|
|
|
- transition: border-color 0.3s; /* 平滑过渡效果 */
|
|
|
|
|
|
|
+ transition: border-color 0.3s;
|
|
|
|
|
+ /* 平滑过渡效果 */
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.upload-area.drag-over {
|
|
.upload-area.drag-over {
|
|
|
border-color: #1890ff;
|
|
border-color: #1890ff;
|
|
|
background-color: rgba(24, 144, 255, 0.05);
|
|
background-color: rgba(24, 144, 255, 0.05);
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ .remaining-time {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ right: 120px;
|
|
|
|
|
+ top: 50%;
|
|
|
|
|
+ transform: translateY(-50%);
|
|
|
|
|
+ color: #666;
|
|
|
|
|
+ font-size: 12px;
|
|
|
|
|
+ }
|
|
|
</style>
|
|
</style>
|