241 lines
6.4 KiB
TypeScript
241 lines
6.4 KiB
TypeScript
import { omit } from '../_utils'
|
||
import type { AcceptType, FileType, SizeType } from './type'
|
||
import type { UploaderProps } from './uploader'
|
||
|
||
interface UniChooseFileSuccessCallbackResult {
|
||
/**
|
||
* 本地文件路径列表
|
||
*/
|
||
tempFilePaths?: string[]
|
||
/**
|
||
* 本地文件列表,每一项是一个 File 对象
|
||
*/
|
||
tempFiles: ({
|
||
path: string
|
||
size: number
|
||
name: string
|
||
type: string
|
||
} & File)[]
|
||
}
|
||
|
||
interface UniChooseImageSuccessCallbackResult extends UniChooseFileSuccessCallbackResult {
|
||
|
||
}
|
||
|
||
interface UniChooseVideoSuccessCallbackResult {
|
||
/**
|
||
* 本地文件路径
|
||
*/
|
||
tempFilePath?: string
|
||
/**
|
||
* 本地文件,一个 File 对象
|
||
*/
|
||
tempFile: ({
|
||
path: string
|
||
size: number
|
||
name: string
|
||
type: string
|
||
} & File)
|
||
/**
|
||
* 选定视频的时间长度,单位为s
|
||
*/
|
||
duration: number
|
||
/**
|
||
* 选定视频的数据量大小
|
||
*/
|
||
size: number
|
||
/**
|
||
* 返回选定视频的高
|
||
*/
|
||
height: number
|
||
/**
|
||
* 返回选定视频的宽
|
||
*/
|
||
width: number
|
||
/**
|
||
* 包含扩展名的文件名称
|
||
*/
|
||
name: string
|
||
}
|
||
|
||
export interface ChooseFile {
|
||
size: number
|
||
type?: FileType
|
||
fileType?: FileType
|
||
originalFileObj?: any
|
||
tempFilePath?: string
|
||
thumbTempFilePath?: string
|
||
path?: string
|
||
name: string
|
||
thumb?: string
|
||
url?: string
|
||
[k: string]: unknown
|
||
}
|
||
|
||
function omitProps<T>(obj: T, keys: string[]) {
|
||
if (!['[object Object]', '[object File]'].includes(Object.prototype.toString.call(obj)))
|
||
return {}
|
||
|
||
return omit(obj as unknown as Record<string, unknown>, keys)
|
||
}
|
||
|
||
function formatImage(res: UniChooseImageSuccessCallbackResult): ChooseFile[] {
|
||
return res.tempFiles.map(item => ({
|
||
...omitProps(item, ['path']),
|
||
type: 'image',
|
||
url: item.path,
|
||
thumb: item.path,
|
||
size: item.size,
|
||
name: item.name || 'image',
|
||
}))
|
||
}
|
||
|
||
function formatVideo(res: UniChooseVideoSuccessCallbackResult): ChooseFile[] {
|
||
return [{
|
||
...omitProps(res.tempFile, ['path']),
|
||
type: 'video',
|
||
url: res.tempFilePath,
|
||
thumb: res.tempFilePath,
|
||
size: res.tempFile.size,
|
||
name: res.tempFile.name || 'video',
|
||
}]
|
||
}
|
||
|
||
function formatMedia(res: UniApp.ChooseMediaSuccessCallbackResult & { name?: string }): ChooseFile[] {
|
||
return res.tempFiles.map(item => ({
|
||
...omitProps(item, ['fileType', 'thumbTempFilePath', 'tempFilePath']),
|
||
type: res.type as FileType,
|
||
url: item.tempFilePath,
|
||
thumb: res.type === 'video' ? item.thumbTempFilePath : item.tempFilePath,
|
||
size: item.size,
|
||
name: res?.name || 'media',
|
||
}))
|
||
}
|
||
|
||
export interface ChooseFileOptions {
|
||
accept: AcceptType
|
||
multiple: boolean
|
||
capture: boolean
|
||
maxDuration: number
|
||
sizeType: SizeType[]
|
||
camera?: 'back' | 'front'
|
||
maxCount: number
|
||
}
|
||
|
||
export function chooseFile({
|
||
accept,
|
||
multiple,
|
||
maxDuration,
|
||
sizeType,
|
||
camera,
|
||
}: ChooseFileOptions, props: UploaderProps, fileList: any[]): Promise<ChooseFile[] | ChooseFile> {
|
||
return new Promise((resolve, reject) => {
|
||
// chooseMedia 目前只支持微信小程序原生,其余端全部使用 chooseImage API
|
||
// #ifdef MP-WEIXIN
|
||
|
||
uni.chooseMedia({
|
||
/** 最多可以选择的文件个数 */
|
||
count: multiple ? Number(props.maximum) * 1 - fileList.length : 1,
|
||
/** 文件类型 */
|
||
mediaType: props.mediaType,
|
||
/** 图片和视频选择的来源 */
|
||
sourceType: props.sourceType,
|
||
/** 拍摄视频最长拍摄时间,单位秒。时间范围为 3s 至 30s 之间 */
|
||
maxDuration,
|
||
/** 仅对 mediaType 为 image 时有效,是否压缩所选文件 */
|
||
sizeType,
|
||
/** 仅在 sourceType 为 camera 时生效,使用前置或后置摄像头 */
|
||
camera,
|
||
/** 接口调用失败的回调函数 */
|
||
fail: reject,
|
||
/** 接口调用成功的回调函数 */
|
||
success: res => resolve(formatMedia(res)),
|
||
})
|
||
// #endif
|
||
|
||
// #ifndef MP-WEIXIN
|
||
if (accept === 'image') {
|
||
uni.chooseImage({
|
||
// 选择数量
|
||
count: props.multiple ? (props.maximum as number) * 1 - props.fileList.length : 1,
|
||
// 可以指定是原图还是压缩图,默认二者都有
|
||
sizeType,
|
||
sourceType: props.sourceType,
|
||
success: (res) => {
|
||
resolve(formatImage(res as UniChooseFileSuccessCallbackResult))
|
||
},
|
||
fail: reject,
|
||
})
|
||
}
|
||
else if (accept === 'video') {
|
||
uni.chooseVideo({
|
||
sourceType: props.sourceType,
|
||
success: (res) => {
|
||
resolve(formatVideo(res as UniChooseVideoSuccessCallbackResult))
|
||
},
|
||
fail: reject,
|
||
})
|
||
}
|
||
else if (accept === 'all') {
|
||
uni.chooseFile({
|
||
type: 'all',
|
||
// 选择数量
|
||
count: props.multiple ? (props.maximum as number) * 1 - props.fileList.length : 1,
|
||
// 可以指定是原图还是压缩图,默认二者都有
|
||
sizeType,
|
||
sourceType: props.sourceType,
|
||
success: (res) => {
|
||
resolve(formatImage(res as UniChooseFileSuccessCallbackResult))
|
||
},
|
||
fail: reject,
|
||
})
|
||
}
|
||
|
||
// #endif
|
||
})
|
||
}
|
||
|
||
export type OnProgressUpdateResult = UniApp.OnProgressUpdateResult
|
||
export type UploadFileSuccessCallbackResult = UniApp.UploadFileSuccessCallbackResult
|
||
export type GeneralCallbackResult = UniApp.GeneralCallbackResult
|
||
|
||
export type UploadOptions = UniNamespace.UploadFileOption & {
|
||
xhrState?: number
|
||
onStart?: (option: UploadOptions) => void
|
||
onProgress?: (result: UniApp.OnProgressUpdateResult, option: UploadOptions) => void
|
||
onSuccess?: (result: UniApp.UploadFileSuccessCallbackResult, option: UploadOptions) => void
|
||
onFailure?: (result: UniApp.GeneralCallbackResult | UniApp.UploadFileSuccessCallbackResult, option: UploadOptions) => void
|
||
}
|
||
|
||
export function createUploader(options: UploadOptions) {
|
||
const upload = () => {
|
||
const uploadTask = uni.uploadFile({
|
||
url: options.url,
|
||
fileType: options.fileType,
|
||
file: options.file,
|
||
filePath: options.filePath,
|
||
name: options.name,
|
||
header: options.header,
|
||
timeout: options.timeout,
|
||
formData: options.formData,
|
||
success: (result) => {
|
||
if (options.xhrState === result.statusCode)
|
||
options.onSuccess?.(result, options)
|
||
else
|
||
options.onFailure?.(result, options)
|
||
},
|
||
fail: (result) => {
|
||
options.onFailure?.(result, options)
|
||
},
|
||
})
|
||
|
||
options.onStart?.(options)
|
||
|
||
uploadTask.onProgressUpdate((event) => {
|
||
options.onProgress?.(event, options)
|
||
})
|
||
}
|
||
|
||
return { upload }
|
||
}
|