mirror of
https://gitee.com/wot-design-uni/wot-design-uni.git
synced 2025-12-06 17:18:40 +08:00
feat: ✨ 提供 useUpload hooks 用于便捷上传 (#969)
This commit is contained in:
parent
460a83802b
commit
49fe25a99b
@ -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
|
||||
}
|
||||
}
|
||||
@ -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 = {
|
||||
|
||||
@ -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 错误返回信息
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user