feat: 分片上传断点续传、临时目录后台配置与清扫、宇恒云账号管理
- 管理端大文件分片上传与 sessionStorage 续传;Nginx 大请求体/超时 - .chunk-uploads 定期清扫;system_config 后台配置保留时长与扫描间隔 - 宇恒云 POST /register 对接与 yuheng_cloud_register_records 留痕;yuheng_cloud:manage 权限 Made-with: Cursor
This commit is contained in:
@@ -57,6 +57,7 @@
|
||||
|
||||
<!-- 上传前选择是否可下载 -->
|
||||
<el-dialog v-model="uploadDialogVisible" title="上传文件" width="440px" :close-on-click-modal="false">
|
||||
<p class="upload-resume-hint">≥8MB 将自动分片上传;中断后<strong>同一文件</strong>再次选择上传可续传(勿改文件名/大小)。</p>
|
||||
<el-form label-width="112px">
|
||||
<el-form-item label="当前目录">
|
||||
<span>{{ currentPath || '根目录' }}</span>
|
||||
@@ -68,6 +69,9 @@
|
||||
<el-form-item label="允许下载">
|
||||
<el-switch v-model="uploadDownloadable" />
|
||||
</el-form-item>
|
||||
<el-form-item v-if="uploading && uploadPercent > 0" label="进度">
|
||||
<el-progress :percentage="uploadPercent" :stroke-width="16" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="uploadDialogVisible = false">取消</el-button>
|
||||
@@ -93,7 +97,8 @@
|
||||
<script setup>
|
||||
import { ref, computed, watch, onMounted } from 'vue'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { getSites, getSiteAssets, uploadSiteAsset, deleteSiteAsset, createSiteFolder } from '../../api/admin'
|
||||
import { getSites, getSiteAssets, deleteSiteAsset, createSiteFolder } from '../../api/admin'
|
||||
import { uploadSiteAssetWithResume } from '../../utils/siteAssetResumableUpload'
|
||||
|
||||
const activeTab = ref('module')
|
||||
const siteId = ref('')
|
||||
@@ -103,6 +108,7 @@ const subDirs = ref([])
|
||||
const loading = ref(false)
|
||||
const currentPath = ref('')
|
||||
const uploading = ref(false)
|
||||
const uploadPercent = ref(0)
|
||||
const uploadDialogVisible = ref(false)
|
||||
const uploadDownloadable = ref(false)
|
||||
const uploadPreserveFilename = ref(false)
|
||||
@@ -167,12 +173,22 @@ const beforeUpload = (file) => {
|
||||
const doUpload = async () => {
|
||||
if (!pendingFile.value || !siteId.value) return
|
||||
uploading.value = true
|
||||
uploadPercent.value = 0
|
||||
try {
|
||||
await uploadSiteAsset(siteId.value, pendingFile.value, {
|
||||
folder: currentPath.value || undefined,
|
||||
downloadable: uploadDownloadable.value,
|
||||
preserveFilename: uploadPreserveFilename.value
|
||||
})
|
||||
await uploadSiteAssetWithResume(
|
||||
siteId.value,
|
||||
pendingFile.value,
|
||||
{
|
||||
folder: currentPath.value || undefined,
|
||||
downloadable: uploadDownloadable.value,
|
||||
preserveFilename: uploadPreserveFilename.value
|
||||
},
|
||||
{
|
||||
onProgress: ({ percent }) => {
|
||||
uploadPercent.value = percent
|
||||
}
|
||||
}
|
||||
)
|
||||
ElMessage.success('上传成功')
|
||||
uploadDialogVisible.value = false
|
||||
pendingFile.value = null
|
||||
@@ -181,6 +197,7 @@ const doUpload = async () => {
|
||||
ElMessage.error(e.response?.data?.error || e.message || '上传失败')
|
||||
} finally {
|
||||
uploading.value = false
|
||||
uploadPercent.value = 0
|
||||
}
|
||||
}
|
||||
|
||||
@@ -230,4 +247,10 @@ onMounted(() => fetchSites().then(() => fetchList()))
|
||||
.breadcrumb-wrap { margin-top: 12px; }
|
||||
.subdirs { margin-top: 8px; font-size: 13px; color: #666; }
|
||||
.subdirs .label { margin-right: 8px; }
|
||||
.upload-resume-hint {
|
||||
margin: 0 0 12px;
|
||||
font-size: 12px;
|
||||
color: #606266;
|
||||
line-height: 1.5;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user