nutui-uniapp
Version:
京东风格的轻量级移动端 Uniapp、Vue3 组件库(支持小程序开发)
241 lines (221 loc) • 6.51 kB
text/typescript
import { omit } from '../_utils'
import type { AcceptType, FileType, SizeType } from './types'
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) - 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 ? Number(props.maximum) - 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 ? Number(props.maximum) - 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,
}
}