wot-design-uni
Version:
一个基于Vue3+TS开发的uni-app组件库,提供70+高质量组件,支持暗黑模式、国际化和自定义主题。
365 lines (344 loc) • 10 kB
text/typescript
import { isArray, isDef, isFunction } from '../common/util'
import type { ChooseFile, ChooseFileOption, 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>
// 选择文件
chooseFile: (options: ChooseFileOption) => Promise<ChooseFile[]>
}
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
// 根据文件拓展名过滤(H5支持全部类型过滤,微信小程序支持all和file时过滤,其余平台不支持)
extension?: string[]
}
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)
}
}
/**
* 格式化图片信息
*/
function formatImage(res: UniApp.ChooseImageSuccessCallbackResult): ChooseFile[] {
// #ifdef MP-DINGTALK
// 钉钉文件在files中
res.tempFiles = isDef((res as any).files) ? (res as any).files : res.tempFiles
// #endif
if (isArray(res.tempFiles)) {
return res.tempFiles.map((item: any) => ({
path: item.path || '',
name: item.name || '',
size: item.size,
type: 'image',
thumb: item.path || ''
}))
}
return [
{
path: (res.tempFiles as any).path || '',
name: (res.tempFiles as any).name || '',
size: (res.tempFiles as any).size,
type: 'image',
thumb: (res.tempFiles as any).path || ''
}
]
}
/**
* 格式化视频信息
*/
function formatVideo(res: UniApp.ChooseVideoSuccess): ChooseFile[] {
return [
{
path: res.tempFilePath || (res as any).filePath || '',
name: res.name || '',
size: res.size,
type: 'video',
thumb: (res as any).thumbTempFilePath || '',
duration: res.duration
}
]
}
/**
* 格式化媒体信息
*/
function formatMedia(res: UniApp.ChooseMediaSuccessCallbackResult): ChooseFile[] {
return res.tempFiles.map((item) => ({
type: item.fileType,
path: item.tempFilePath,
thumb: item.fileType === 'video' ? item.thumbTempFilePath : item.tempFilePath,
size: item.size,
duration: item.duration
}))
}
/**
* 选择文件
*/
function chooseFile({
multiple,
sizeType,
sourceType,
maxCount,
accept,
compressed,
maxDuration,
camera,
extension
}: ChooseFileOption): Promise<ChooseFile[]> {
return new Promise((resolve, reject) => {
switch (accept) {
case 'image':
// #ifdef MP-WEIXIN
uni.chooseMedia({
count: multiple ? maxCount : 1,
mediaType: ['image'],
sourceType,
sizeType,
camera,
success: (res) => resolve(formatMedia(res)),
fail: reject
})
// #endif
// #ifndef MP-WEIXIN
uni.chooseImage({
count: multiple ? maxCount : 1,
sizeType,
sourceType,
// #ifdef H5
extension,
// #endif
success: (res) => resolve(formatImage(res)),
fail: reject
})
// #endif
break
case 'video':
// #ifdef MP-WEIXIN
uni.chooseMedia({
count: multiple ? maxCount : 1,
mediaType: ['video'],
sourceType,
camera,
maxDuration,
success: (res) => resolve(formatMedia(res)),
fail: reject
})
// #endif
// #ifndef MP-WEIXIN
uni.chooseVideo({
sourceType,
compressed,
maxDuration,
camera,
// #ifdef H5
extension,
// #endif
success: (res) => resolve(formatVideo(res)),
fail: reject
})
// #endif
break
// #ifdef MP-WEIXIN
case 'media':
uni.chooseMedia({
count: multiple ? maxCount : 1,
sourceType,
sizeType,
camera,
maxDuration,
success: (res) => resolve(formatMedia(res)),
fail: reject
})
break
case 'file':
uni.chooseMessageFile({
count: multiple ? (isDef(maxCount) ? maxCount : 100) : 1,
type: accept,
extension,
success: (res) => resolve(res.tempFiles),
fail: reject
})
break
// #endif
case 'all':
// #ifdef H5
uni.chooseFile({
count: multiple ? maxCount : 1,
type: accept,
extension,
success: (res) => resolve(res.tempFiles as ChooseFile[]),
fail: reject
})
// #endif
// #ifdef MP-WEIXIN
uni.chooseMessageFile({
count: multiple ? Number(maxCount) : 1,
type: accept,
extension,
success: (res) => resolve(res.tempFiles),
fail: reject
})
// #endif
break
default:
// #ifdef MP-WEIXIN
uni.chooseMedia({
count: multiple ? maxCount : 1,
mediaType: ['image'],
sourceType,
sizeType,
camera,
success: (res) => resolve(formatMedia(res)),
fail: reject
})
// #endif
// #ifndef MP-WEIXIN
uni.chooseImage({
count: multiple ? maxCount : 1,
sizeType,
sourceType,
// #ifdef H5
extension,
// #endif
success: (res) => resolve(formatImage(res)),
fail: reject
})
// #endif
break
}
})
}
return {
startUpload,
abort,
UPLOAD_STATUS,
chooseFile
}
}