feat: 提供 useUpload hooks 用于便捷上传 (#969)

This commit is contained in:
不如摸鱼去 2025-03-24 21:26:25 +08:00 committed by GitHub
parent 460a83802b
commit 49fe25a99b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 224 additions and 90 deletions

View File

@ -0,0 +1,163 @@
import { isFunction } from '../common/util'
import type { UploadFileItem, UploadMethod, UploadStatusType } from '../wd-upload/types'
export const UPLOAD_STATUS: Record<string, UploadStatusType> = {
PENDING: 'pending',
LOADING: 'loading',
SUCCESS: 'success',
FAIL: 'fail'
}
export interface UseUploadReturn {
// 开始上传文件
startUpload: (file: UploadFileItem, options: UseUploadOptions) => UniApp.UploadTask | void | Promise<void>
// 中断上传
abort: (task?: UniApp.UploadTask) => void
// 上传状态常量
UPLOAD_STATUS: Record<string, UploadStatusType>
}
export interface UseUploadOptions {
// 上传地址
action: string
// 请求头
header?: Record<string, any>
// 文件对应的 key
name?: string
// 其它表单数据
formData?: Record<string, any>
// 文件类型
fileType?: 'image' | 'video' | 'audio'
// 成功状态码
statusCode?: number
// 文件状态的key
statusKey?: string
// 自定义上传方法
uploadMethod?: UploadMethod
// 上传成功回调
onSuccess?: (res: UniApp.UploadFileSuccessCallbackResult, file: UploadFileItem, formData: Record<string, any>) => void
// 上传失败回调
onError?: (res: UniApp.GeneralCallbackResult, file: UploadFileItem, formData: Record<string, any>) => void
// 上传进度回调
onProgress?: (res: UniApp.OnProgressUpdateResult, file: UploadFileItem) => void
// 是否自动中断之前的上传任务
abortPrevious?: boolean
}
export function useUpload(): UseUploadReturn {
let currentTask: UniApp.UploadTask | null = null
// 中断上传
const abort = (task?: UniApp.UploadTask) => {
if (task) {
task.abort()
} else if (currentTask) {
currentTask.abort()
currentTask = null
}
}
/**
*
*/
const defaultUpload: UploadMethod = (file, formData, options) => {
// 如果配置了自动中断,则中断之前的上传任务
if (options.abortPrevious) {
abort()
}
const uploadTask = uni.uploadFile({
url: options.action,
header: options.header,
name: options.name,
fileName: options.name,
fileType: options.fileType,
formData,
filePath: file.url,
success(res) {
if (res.statusCode === options.statusCode) {
// 上传成功
options.onSuccess(res, file, formData)
} else {
// 上传失败
options.onError({ ...res, errMsg: res.errMsg || '' }, file, formData)
}
},
fail(err) {
// 上传失败
options.onError(err, file, formData)
}
})
currentTask = uploadTask
// 获取当前文件加载的百分比
uploadTask.onProgressUpdate((res) => {
options.onProgress(res, file)
})
// 返回上传任务实例,让外部可以控制上传过程
return uploadTask
}
/**
*
*/
const startUpload = (file: UploadFileItem, options: UseUploadOptions) => {
const {
uploadMethod,
formData = {},
action,
name = 'file',
header = {},
fileType = 'image',
statusCode = 200,
statusKey = 'status',
abortPrevious = false
} = options
// 设置上传中状态
file[statusKey] = UPLOAD_STATUS.LOADING
const uploadOptions = {
action,
header,
name,
fileName: name,
fileType,
statusCode,
abortPrevious,
onSuccess: (res: UniApp.UploadFileSuccessCallbackResult, file: UploadFileItem, formData: Record<string, any>) => {
// 更新文件状态
file[statusKey] = UPLOAD_STATUS.SUCCESS
currentTask = null
options.onSuccess?.(res, file, formData)
},
onError: (error: UniApp.GeneralCallbackResult, file: UploadFileItem, formData: Record<string, any>) => {
// 更新文件状态和错误信息
file[statusKey] = UPLOAD_STATUS.FAIL
file.error = error.errMsg
currentTask = null
options.onError?.(error, file, formData)
},
onProgress: (res: UniApp.OnProgressUpdateResult, file: UploadFileItem) => {
// 更新上传进度
file.percent = res.progress
options.onProgress?.(res, file)
}
}
// 返回上传任务实例,支持外部获取uploadTask进行操作
if (isFunction(uploadMethod)) {
return uploadMethod(file, formData, uploadOptions)
} else {
return defaultUpload(file, formData, uploadOptions)
}
}
return {
startUpload,
abort,
UPLOAD_STATUS
}
}

View File

@ -120,11 +120,13 @@ export type UploadMethod = (
fileName: string
fileType: 'image' | 'video' | 'audio'
statusCode: number
// 添加是否自动中断之前上传的选项
abortPrevious?: boolean
onSuccess: (res: UniApp.UploadFileSuccessCallbackResult, file: UploadFileItem, formData: UploadFormData) => void
onError: (res: UniApp.GeneralCallbackResult, file: UploadFileItem, formData: UploadFormData) => void
onProgress: (res: UniApp.OnProgressUpdateResult, file: UploadFileItem) => void
}
) => void | Promise<void>
) => UniApp.UploadTask | void | Promise<void> // 修改这里,支持返回 UploadTask 类型
export const uploadProps = {
...baseProps,
@ -338,6 +340,11 @@ export type UploadExpose = {
*
*/
submit: () => void
/**
*
* @param task
*/
abort: (task?: UniApp.UploadTask) => void
}
export type UploadErrorEvent = {

View File

@ -101,9 +101,10 @@ import wdVideoPreview from '../wd-video-preview/wd-video-preview.vue'
import wdLoading from '../wd-loading/wd-loading.vue'
import { computed, ref, watch } from 'vue'
import { context, getType, isEqual, isImageUrl, isVideoUrl, isFunction, isDef, deepClone } from '../common/util'
import { context, isEqual, isImageUrl, isVideoUrl, isFunction, isDef, deepClone } from '../common/util'
import { chooseFile } from './utils'
import { useTranslate } from '../composables/useTranslate'
import { useUpload } from '../composables/useUpload'
import {
uploadProps,
type UploadFileItem,
@ -133,7 +134,8 @@ const emit = defineEmits<{
}>()
defineExpose<UploadExpose>({
submit: () => startUploadFiles()
submit: () => startUploadFiles(),
abort: () => abort()
})
const { translate } = useTranslate('upload')
@ -144,6 +146,8 @@ const showUpload = computed(() => !props.limit || uploadFiles.value.length < pro
const videoPreview = ref<VideoPreviewInstance>()
const { startUpload, abort, UPLOAD_STATUS } = useUpload()
watch(
() => props.fileList,
(val) => {
@ -258,48 +262,55 @@ function emitFileList() {
}
/**
* 组件内部上传方法
* @param file 文件
* @param formData
* @param options
* 开始上传文件
*/
const upload: UploadMethod = (file, formData, options) => {
const uploadTask = uni.uploadFile({
url: options.action,
header: options.header,
name: options.name,
fileName: options.name,
fileType: options.fileType as 'image' | 'video' | 'audio',
formData,
filePath: file.url,
success(res) {
if (res.statusCode === options.statusCode) {
//
options.onSuccess(res, file, formData)
} else {
//
options.onError({ ...res, errMsg: res.errMsg || '' }, file, formData)
}
},
fail(err) {
//
options.onError(err, file, formData)
}
})
//
uploadTask.onProgressUpdate((res) => {
options.onProgress(res, file)
})
}
function startUploadFiles() {
const { buildFormData, formData = {}, statusKey } = props
const { action, name, header = {}, accept, successStatus, uploadMethod } = props
const statusCode = isDef(successStatus) ? successStatus : 200
const startUpload: UploadMethod = (file, formData, options) => {
const { statusKey, uploadMethod } = props
//
file[statusKey] = 'loading'
if (isFunction(uploadMethod)) {
uploadMethod(file, formData, options)
} else {
upload(file, formData, options)
for (const uploadFile of uploadFiles.value) {
//
if (uploadFile[statusKey] === UPLOAD_STATUS.PENDING) {
if (buildFormData) {
buildFormData({
file: uploadFile,
formData,
resolve: (formData: Record<string, any>) => {
formData &&
startUpload(uploadFile, {
action,
header,
name,
formData,
fileType: accept as 'image' | 'video' | 'audio',
statusCode,
statusKey,
uploadMethod,
onSuccess: handleSuccess,
onError: handleError,
onProgress: handleProgress,
abortPrevious: true //
})
}
})
} else {
startUpload(uploadFile, {
action,
header,
name,
formData,
fileType: accept as 'image' | 'video' | 'audio',
statusCode,
statusKey,
uploadMethod,
onSuccess: handleSuccess,
onError: handleError,
onProgress: handleProgress,
abortPrevious: true
})
}
}
}
}
@ -348,53 +359,6 @@ function initFile(file: ChooseFile, currentIndex?: number) {
}
}
/**
* 开始上传文件
*/
function startUploadFiles() {
const { buildFormData, formData = {}, statusKey } = props
const { action, name, header = {}, accept, successStatus } = props
const statusCode = isDef(successStatus) ? successStatus : 200
for (const uploadFile of uploadFiles.value) {
//
if (uploadFile[statusKey] == 'pending') {
if (buildFormData) {
buildFormData({
file: uploadFile,
formData,
resolve: (formData: Record<string, any>) => {
formData &&
startUpload(uploadFile, formData, {
onSuccess: handleSuccess,
onError: handleError,
onProgress: handleProgress,
action,
header,
name,
fileName: name,
fileType: accept as 'image' | 'video' | 'audio',
statusCode
})
}
})
} else {
startUpload(uploadFile, formData, {
onSuccess: handleSuccess,
onError: handleError,
onProgress: handleProgress,
action,
header,
name,
fileName: name,
fileType: accept as 'image' | 'video' | 'audio',
statusCode
})
}
}
}
}
/**
* @description 上传失败捕获
* @param {Object} err 错误返回信息