feat: 添加文件上传进度和速度显示
- 新增 api.postFormDataWithProgress 方法支持上传进度回调 - 版本创建和编辑页面显示上传进度条和实时速度 - 按钮显示上传百分比 - 添加国际化翻译 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -37,6 +37,8 @@ const versionId = computed(() => route.params.id as string)
|
||||
const loading = ref(true)
|
||||
const saving = ref(false)
|
||||
const uploading = ref(false)
|
||||
const uploadProgress = ref(0)
|
||||
const uploadSpeed = ref('')
|
||||
const applications = ref<Application[]>([])
|
||||
|
||||
const formData = ref({
|
||||
@@ -168,11 +170,20 @@ async function uploadZipFile() {
|
||||
}
|
||||
|
||||
uploading.value = true
|
||||
uploadProgress.value = 0
|
||||
uploadSpeed.value = ''
|
||||
try {
|
||||
const formDataObj = new FormData()
|
||||
formDataObj.append('file', formData.value.file)
|
||||
|
||||
const response = await api.postFormData<UploadResponse>('/dev/versions/upload-zip', formDataObj)
|
||||
const response = await api.postFormDataWithProgress<UploadResponse>(
|
||||
'/dev/versions/upload-zip',
|
||||
formDataObj,
|
||||
(progress, speed) => {
|
||||
uploadProgress.value = progress
|
||||
uploadSpeed.value = speed
|
||||
}
|
||||
)
|
||||
uploadedData.value = response
|
||||
toast.success(t('admin.versions.createForm.uploadSuccess'))
|
||||
}
|
||||
@@ -182,6 +193,8 @@ async function uploadZipFile() {
|
||||
}
|
||||
finally {
|
||||
uploading.value = false
|
||||
uploadProgress.value = 0
|
||||
uploadSpeed.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
@@ -380,13 +393,26 @@ onMounted(() => {
|
||||
>
|
||||
<Loader2 v-if="uploading" class="h-4 w-4 mr-2 animate-spin" />
|
||||
<Upload v-else class="h-4 w-4 mr-2" />
|
||||
{{ t('admin.versions.createForm.uploadAndParse') }}
|
||||
{{ uploading ? `${uploadProgress}%` : t('admin.versions.createForm.uploadAndParse') }}
|
||||
</UiButton>
|
||||
<UiButton variant="ghost" size="icon" :disabled="saving" @click="removeFile">
|
||||
<X class="size-4" />
|
||||
</UiButton>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 上传进度条 -->
|
||||
<div v-if="uploading" class="space-y-1">
|
||||
<div class="w-full bg-muted rounded-full h-2 overflow-hidden">
|
||||
<div
|
||||
class="bg-primary h-full transition-all duration-300 ease-out"
|
||||
:style="{ width: `${uploadProgress}%` }"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex justify-between text-xs text-muted-foreground">
|
||||
<span>{{ t('admin.versions.createForm.uploading') }}</span>
|
||||
<span class="font-medium">{{ uploadSpeed }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="uploadedData && uploadedData.file_hash !== formData.file?.name" class="rounded-lg bg-muted/50 p-3">
|
||||
<div class="flex items-center gap-2 text-sm text-green-600 mb-2">
|
||||
<CheckCircle class="size-4" />
|
||||
|
||||
@@ -36,6 +36,8 @@ const router = useRouter()
|
||||
const loading = ref(false)
|
||||
const saving = ref(false)
|
||||
const uploading = ref(false)
|
||||
const uploadProgress = ref(0)
|
||||
const uploadSpeed = ref('')
|
||||
const applications = ref<Application[]>([])
|
||||
|
||||
const formData = ref({
|
||||
@@ -149,11 +151,20 @@ async function uploadZipFile() {
|
||||
}
|
||||
|
||||
uploading.value = true
|
||||
uploadProgress.value = 0
|
||||
uploadSpeed.value = ''
|
||||
try {
|
||||
const formDataObj = new FormData()
|
||||
formDataObj.append('file', formData.value.file)
|
||||
|
||||
const response = await api.postFormData<UploadResponse>('/dev/versions/upload-zip', formDataObj)
|
||||
const response = await api.postFormDataWithProgress<UploadResponse>(
|
||||
'/dev/versions/upload-zip',
|
||||
formDataObj,
|
||||
(progress, speed) => {
|
||||
uploadProgress.value = progress
|
||||
uploadSpeed.value = speed
|
||||
}
|
||||
)
|
||||
uploadedData.value = response
|
||||
toast.success(t('admin.versions.createForm.uploadSuccess'))
|
||||
}
|
||||
@@ -163,6 +174,8 @@ async function uploadZipFile() {
|
||||
}
|
||||
finally {
|
||||
uploading.value = false
|
||||
uploadProgress.value = 0
|
||||
uploadSpeed.value = ''
|
||||
}
|
||||
}
|
||||
|
||||
@@ -319,13 +332,26 @@ onMounted(() => {
|
||||
>
|
||||
<Loader2 v-if="uploading" class="h-4 w-4 mr-2 animate-spin" />
|
||||
<Upload v-else class="h-4 w-4 mr-2" />
|
||||
{{ t('admin.versions.createForm.uploadAndParse') }}
|
||||
{{ uploading ? `${uploadProgress}%` : t('admin.versions.createForm.uploadAndParse') }}
|
||||
</UiButton>
|
||||
<UiButton variant="ghost" size="icon" :disabled="saving" @click="removeFile">
|
||||
<X class="size-4" />
|
||||
</UiButton>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 上传进度条 -->
|
||||
<div v-if="uploading" class="space-y-1">
|
||||
<div class="w-full bg-muted rounded-full h-2 overflow-hidden">
|
||||
<div
|
||||
class="bg-primary h-full transition-all duration-300 ease-out"
|
||||
:style="{ width: `${uploadProgress}%` }"
|
||||
/>
|
||||
</div>
|
||||
<div class="flex justify-between text-xs text-muted-foreground">
|
||||
<span>{{ t('admin.versions.createForm.uploading') }}</span>
|
||||
<span class="font-medium">{{ uploadSpeed }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="uploadedData" class="rounded-lg bg-muted/50 p-3">
|
||||
<div class="flex items-center gap-2 text-sm text-green-600 mb-2">
|
||||
<CheckCircle class="size-4" />
|
||||
|
||||
Reference in New Issue
Block a user