Files
2026-01-05 12:47:14 +08:00

241 lines
6.4 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 }
}