Skip to content

Commit 49fe25a

Browse files
feat: ✨ 提供 useUpload hooks 用于便捷上传 (#969)
1 parent 460a838 commit 49fe25a

3 files changed

Lines changed: 222 additions & 88 deletions

File tree

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
import { isFunction } from '../common/util'
2+
import type { UploadFileItem, UploadMethod, UploadStatusType } from '../wd-upload/types'
3+
4+
export const UPLOAD_STATUS: Record<string, UploadStatusType> = {
5+
PENDING: 'pending',
6+
LOADING: 'loading',
7+
SUCCESS: 'success',
8+
FAIL: 'fail'
9+
}
10+
11+
export interface UseUploadReturn {
12+
// 开始上传文件
13+
startUpload: (file: UploadFileItem, options: UseUploadOptions) => UniApp.UploadTask | void | Promise<void>
14+
// 中断上传
15+
abort: (task?: UniApp.UploadTask) => void
16+
// 上传状态常量
17+
UPLOAD_STATUS: Record<string, UploadStatusType>
18+
}
19+
20+
export interface UseUploadOptions {
21+
// 上传地址
22+
action: string
23+
// 请求头
24+
header?: Record<string, any>
25+
// 文件对应的 key
26+
name?: string
27+
// 其它表单数据
28+
formData?: Record<string, any>
29+
// 文件类型
30+
fileType?: 'image' | 'video' | 'audio'
31+
// 成功状态码
32+
statusCode?: number
33+
// 文件状态的key
34+
statusKey?: string
35+
// 自定义上传方法
36+
uploadMethod?: UploadMethod
37+
// 上传成功回调
38+
onSuccess?: (res: UniApp.UploadFileSuccessCallbackResult, file: UploadFileItem, formData: Record<string, any>) => void
39+
// 上传失败回调
40+
onError?: (res: UniApp.GeneralCallbackResult, file: UploadFileItem, formData: Record<string, any>) => void
41+
// 上传进度回调
42+
onProgress?: (res: UniApp.OnProgressUpdateResult, file: UploadFileItem) => void
43+
// 是否自动中断之前的上传任务
44+
abortPrevious?: boolean
45+
}
46+
47+
export function useUpload(): UseUploadReturn {
48+
let currentTask: UniApp.UploadTask | null = null
49+
50+
// 中断上传
51+
const abort = (task?: UniApp.UploadTask) => {
52+
if (task) {
53+
task.abort()
54+
} else if (currentTask) {
55+
currentTask.abort()
56+
currentTask = null
57+
}
58+
}
59+
60+
/**
61+
* 默认上传方法
62+
*/
63+
const defaultUpload: UploadMethod = (file, formData, options) => {
64+
// 如果配置了自动中断,则中断之前的上传任务
65+
if (options.abortPrevious) {
66+
abort()
67+
}
68+
69+
const uploadTask = uni.uploadFile({
70+
url: options.action,
71+
header: options.header,
72+
name: options.name,
73+
fileName: options.name,
74+
fileType: options.fileType,
75+
formData,
76+
filePath: file.url,
77+
success(res) {
78+
if (res.statusCode === options.statusCode) {
79+
// 上传成功
80+
options.onSuccess(res, file, formData)
81+
} else {
82+
// 上传失败
83+
options.onError({ ...res, errMsg: res.errMsg || '' }, file, formData)
84+
}
85+
},
86+
fail(err) {
87+
// 上传失败
88+
options.onError(err, file, formData)
89+
}
90+
})
91+
92+
currentTask = uploadTask
93+
94+
// 获取当前文件加载的百分比
95+
uploadTask.onProgressUpdate((res) => {
96+
options.onProgress(res, file)
97+
})
98+
99+
// 返回上传任务实例,让外部可以控制上传过程
100+
return uploadTask
101+
}
102+
103+
/**
104+
* 开始上传文件
105+
*/
106+
const startUpload = (file: UploadFileItem, options: UseUploadOptions) => {
107+
const {
108+
uploadMethod,
109+
formData = {},
110+
action,
111+
name = 'file',
112+
header = {},
113+
fileType = 'image',
114+
statusCode = 200,
115+
statusKey = 'status',
116+
abortPrevious = false
117+
} = options
118+
119+
// 设置上传中状态
120+
file[statusKey] = UPLOAD_STATUS.LOADING
121+
122+
const uploadOptions = {
123+
action,
124+
header,
125+
name,
126+
fileName: name,
127+
fileType,
128+
statusCode,
129+
abortPrevious,
130+
onSuccess: (res: UniApp.UploadFileSuccessCallbackResult, file: UploadFileItem, formData: Record<string, any>) => {
131+
// 更新文件状态
132+
file[statusKey] = UPLOAD_STATUS.SUCCESS
133+
currentTask = null
134+
options.onSuccess?.(res, file, formData)
135+
},
136+
onError: (error: UniApp.GeneralCallbackResult, file: UploadFileItem, formData: Record<string, any>) => {
137+
// 更新文件状态和错误信息
138+
file[statusKey] = UPLOAD_STATUS.FAIL
139+
file.error = error.errMsg
140+
currentTask = null
141+
options.onError?.(error, file, formData)
142+
},
143+
onProgress: (res: UniApp.OnProgressUpdateResult, file: UploadFileItem) => {
144+
// 更新上传进度
145+
file.percent = res.progress
146+
options.onProgress?.(res, file)
147+
}
148+
}
149+
150+
// 返回上传任务实例,支持外部获取uploadTask进行操作
151+
if (isFunction(uploadMethod)) {
152+
return uploadMethod(file, formData, uploadOptions)
153+
} else {
154+
return defaultUpload(file, formData, uploadOptions)
155+
}
156+
}
157+
158+
return {
159+
startUpload,
160+
abort,
161+
UPLOAD_STATUS
162+
}
163+
}

src/uni_modules/wot-design-uni/components/wd-upload/types.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,11 +120,13 @@ export type UploadMethod = (
120120
fileName: string
121121
fileType: 'image' | 'video' | 'audio'
122122
statusCode: number
123+
// 添加是否自动中断之前上传的选项
124+
abortPrevious?: boolean
123125
onSuccess: (res: UniApp.UploadFileSuccessCallbackResult, file: UploadFileItem, formData: UploadFormData) => void
124126
onError: (res: UniApp.GeneralCallbackResult, file: UploadFileItem, formData: UploadFormData) => void
125127
onProgress: (res: UniApp.OnProgressUpdateResult, file: UploadFileItem) => void
126128
}
127-
) => void | Promise<void>
129+
) => UniApp.UploadTask | void | Promise<void> // 修改这里,支持返回 UploadTask 类型
128130

129131
export const uploadProps = {
130132
...baseProps,
@@ -338,6 +340,11 @@ export type UploadExpose = {
338340
* 手动触发上传
339341
*/
340342
submit: () => void
343+
/**
344+
* 取消上传
345+
* @param task 上传任务
346+
*/
347+
abort: (task?: UniApp.UploadTask) => void
341348
}
342349

343350
export type UploadErrorEvent = {

src/uni_modules/wot-design-uni/components/wd-upload/wd-upload.vue

Lines changed: 51 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -101,9 +101,10 @@ import wdVideoPreview from '../wd-video-preview/wd-video-preview.vue'
101101
import wdLoading from '../wd-loading/wd-loading.vue'
102102
103103
import { computed, ref, watch } from 'vue'
104-
import { context, getType, isEqual, isImageUrl, isVideoUrl, isFunction, isDef, deepClone } from '../common/util'
104+
import { context, isEqual, isImageUrl, isVideoUrl, isFunction, isDef, deepClone } from '../common/util'
105105
import { chooseFile } from './utils'
106106
import { useTranslate } from '../composables/useTranslate'
107+
import { useUpload } from '../composables/useUpload'
107108
import {
108109
uploadProps,
109110
type UploadFileItem,
@@ -133,7 +134,8 @@ const emit = defineEmits<{
133134
}>()
134135
135136
defineExpose<UploadExpose>({
136-
submit: () => startUploadFiles()
137+
submit: () => startUploadFiles(),
138+
abort: () => abort()
137139
})
138140
139141
const { translate } = useTranslate('upload')
@@ -144,6 +146,8 @@ const showUpload = computed(() => !props.limit || uploadFiles.value.length < pro
144146
145147
const videoPreview = ref<VideoPreviewInstance>()
146148
149+
const { startUpload, abort, UPLOAD_STATUS } = useUpload()
150+
147151
watch(
148152
() => props.fileList,
149153
(val) => {
@@ -258,48 +262,55 @@ function emitFileList() {
258262
}
259263
260264
/**
261-
* 组件内部上传方法
262-
* @param file 文件
263-
* @param formData
264-
* @param options
265+
* 开始上传文件
265266
*/
266-
const upload: UploadMethod = (file, formData, options) => {
267-
const uploadTask = uni.uploadFile({
268-
url: options.action,
269-
header: options.header,
270-
name: options.name,
271-
fileName: options.name,
272-
fileType: options.fileType as 'image' | 'video' | 'audio',
273-
formData,
274-
filePath: file.url,
275-
success(res) {
276-
if (res.statusCode === options.statusCode) {
277-
// 上传成功进行文件列表拼接
278-
options.onSuccess(res, file, formData)
267+
function startUploadFiles() {
268+
const { buildFormData, formData = {}, statusKey } = props
269+
const { action, name, header = {}, accept, successStatus, uploadMethod } = props
270+
const statusCode = isDef(successStatus) ? successStatus : 200
271+
272+
for (const uploadFile of uploadFiles.value) {
273+
// 仅开始未上传的文件
274+
if (uploadFile[statusKey] === UPLOAD_STATUS.PENDING) {
275+
if (buildFormData) {
276+
buildFormData({
277+
file: uploadFile,
278+
formData,
279+
resolve: (formData: Record<string, any>) => {
280+
formData &&
281+
startUpload(uploadFile, {
282+
action,
283+
header,
284+
name,
285+
formData,
286+
fileType: accept as 'image' | 'video' | 'audio',
287+
statusCode,
288+
statusKey,
289+
uploadMethod,
290+
onSuccess: handleSuccess,
291+
onError: handleError,
292+
onProgress: handleProgress,
293+
abortPrevious: true // 自动中断之前的上传
294+
})
295+
}
296+
})
279297
} else {
280-
// 上传失败处理
281-
options.onError({ ...res, errMsg: res.errMsg || '' }, file, formData)
298+
startUpload(uploadFile, {
299+
action,
300+
header,
301+
name,
302+
formData,
303+
fileType: accept as 'image' | 'video' | 'audio',
304+
statusCode,
305+
statusKey,
306+
uploadMethod,
307+
onSuccess: handleSuccess,
308+
onError: handleError,
309+
onProgress: handleProgress,
310+
abortPrevious: true
311+
})
282312
}
283-
},
284-
fail(err) {
285-
// 上传失败处理
286-
options.onError(err, file, formData)
287313
}
288-
})
289-
// 获取当前文件加载的百分比
290-
uploadTask.onProgressUpdate((res) => {
291-
options.onProgress(res, file)
292-
})
293-
}
294-
295-
const startUpload: UploadMethod = (file, formData, options) => {
296-
const { statusKey, uploadMethod } = props
297-
// 设置上传中,防止重复发起上传
298-
file[statusKey] = 'loading'
299-
if (isFunction(uploadMethod)) {
300-
uploadMethod(file, formData, options)
301-
} else {
302-
upload(file, formData, options)
303314
}
304315
}
305316
@@ -348,53 +359,6 @@ function initFile(file: ChooseFile, currentIndex?: number) {
348359
}
349360
}
350361
351-
/**
352-
* 开始上传文件
353-
*/
354-
function startUploadFiles() {
355-
const { buildFormData, formData = {}, statusKey } = props
356-
const { action, name, header = {}, accept, successStatus } = props
357-
const statusCode = isDef(successStatus) ? successStatus : 200
358-
359-
for (const uploadFile of uploadFiles.value) {
360-
// 仅开始未上传的文件
361-
if (uploadFile[statusKey] == 'pending') {
362-
if (buildFormData) {
363-
buildFormData({
364-
file: uploadFile,
365-
formData,
366-
resolve: (formData: Record<string, any>) => {
367-
formData &&
368-
startUpload(uploadFile, formData, {
369-
onSuccess: handleSuccess,
370-
onError: handleError,
371-
onProgress: handleProgress,
372-
action,
373-
header,
374-
name,
375-
fileName: name,
376-
fileType: accept as 'image' | 'video' | 'audio',
377-
statusCode
378-
})
379-
}
380-
})
381-
} else {
382-
startUpload(uploadFile, formData, {
383-
onSuccess: handleSuccess,
384-
onError: handleError,
385-
onProgress: handleProgress,
386-
action,
387-
header,
388-
name,
389-
fileName: name,
390-
fileType: accept as 'image' | 'video' | 'audio',
391-
statusCode
392-
})
393-
}
394-
}
395-
}
396-
}
397-
398362
/**
399363
* @description 上传失败捕获
400364
* @param {Object} err 错误返回信息

0 commit comments

Comments
 (0)