|
|
@@ -1,69 +1,144 @@
|
|
|
-// TODO: 别忘加更改环境变量的 VITE_UPLOAD_BASEURL 地址。
|
|
|
+import { ref, Ref } from 'vue'
|
|
|
import { getEnvBaseUploadUrl } from '@/utils'
|
|
|
|
|
|
const VITE_UPLOAD_BASEURL = `${getEnvBaseUploadUrl()}`
|
|
|
|
|
|
-/**
|
|
|
- * useUpload 是一个定制化的请求钩子,用于处理上传图片。
|
|
|
- * @param formData 额外传递给后台的数据,如{name: '菲鸽'}。
|
|
|
- * @returns 返回一个对象{loading, error, data, run},包含请求的加载状态、错误信息、响应数据和手动触发请求的函数。
|
|
|
- */
|
|
|
-export default function useUpload<T = string>(formData: Record<string, any> = {}) {
|
|
|
+type TfileType = 'image' | 'file'
|
|
|
+type TImage = 'png' | 'jpg' | 'jpeg' | 'webp' | '*'
|
|
|
+type TFile = 'doc' | 'docx' | 'ppt' | 'zip' | 'xls' | 'xlsx' | 'txt' | TImage
|
|
|
+
|
|
|
+type TOptions<T extends TfileType> = {
|
|
|
+ formData?: Record<string, any>
|
|
|
+ maxSize?: number
|
|
|
+ accept?: T extends 'image' ? TImage[] : TFile[]
|
|
|
+ fileType?: T
|
|
|
+ success?: (params: any) => void
|
|
|
+ error?: (err: any) => void
|
|
|
+}
|
|
|
+
|
|
|
+export default function useUpload<T extends TfileType>(options: TOptions<T> = {} as TOptions<T>) {
|
|
|
+ const {
|
|
|
+ formData = {},
|
|
|
+ maxSize = 5 * 1024 * 1024,
|
|
|
+ accept = ['*'],
|
|
|
+ fileType = 'image',
|
|
|
+ success,
|
|
|
+ error: onError,
|
|
|
+ } = options
|
|
|
+
|
|
|
const loading = ref(false)
|
|
|
- const error = ref(false)
|
|
|
- const data = ref<T>()
|
|
|
+ const error = ref<Error | null>(null)
|
|
|
+ const data = ref<any>(null)
|
|
|
+
|
|
|
const run = () => {
|
|
|
// #ifdef MP-WEIXIN
|
|
|
// 微信小程序从基础库 2.21.0 开始, wx.chooseImage 停止维护,请使用 uni.chooseMedia 代替。
|
|
|
// 微信小程序在2023年10月17日之后,使用本API需要配置隐私协议
|
|
|
- uni.chooseMedia({
|
|
|
+ const chooseFileOptions = {
|
|
|
count: 1,
|
|
|
- mediaType: ['image'],
|
|
|
- success: (res) => {
|
|
|
- loading.value = true
|
|
|
- const tempFilePath = res.tempFiles[0].tempFilePath
|
|
|
- uploadFile<T>({ tempFilePath, formData, data, error, loading })
|
|
|
+ success: (res: any) => {
|
|
|
+ handleFileChoose({ tempFilePath: res.tempFilePaths[0], tempFiles: res.tempFiles[0] })
|
|
|
},
|
|
|
- fail: (err) => {
|
|
|
- console.error('uni.chooseMedia err->', err)
|
|
|
- error.value = true
|
|
|
+ fail: (err: any) => {
|
|
|
+ console.error('File selection failed:', err)
|
|
|
+ error.value = err
|
|
|
+ onError?.(err)
|
|
|
},
|
|
|
- })
|
|
|
- // #endif
|
|
|
- // #ifndef MP-WEIXIN
|
|
|
- uni.chooseImage({
|
|
|
- count: 1,
|
|
|
- success: (res) => {
|
|
|
- loading.value = true
|
|
|
- const tempFilePath = res.tempFilePaths[0]
|
|
|
- uploadFile<T>({ tempFilePath, formData, data, error, loading })
|
|
|
+ }
|
|
|
+
|
|
|
+ if (fileType === 'image') {
|
|
|
+ // #ifdef MP-WEIXIN
|
|
|
+ uni.chooseMedia({
|
|
|
+ ...chooseFileOptions,
|
|
|
+ mediaType: ['image'],
|
|
|
+ })
|
|
|
+ // #endif
|
|
|
+
|
|
|
+ // #ifndef MP-WEIXIN
|
|
|
+ uni.chooseImage(chooseFileOptions)
|
|
|
+ // #endif
|
|
|
+ } else {
|
|
|
+ uni.chooseFile({
|
|
|
+ ...chooseFileOptions,
|
|
|
+ type: 'all',
|
|
|
+ })
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ const handleFileChoose = (file: {
|
|
|
+ tempFilePath: string
|
|
|
+ tempFiles?: { size?: number; name?: string }
|
|
|
+ }) => {
|
|
|
+ if (file?.tempFiles?.size && file.tempFiles.size > maxSize) {
|
|
|
+ uni.showToast({
|
|
|
+ title: `文件大小不能超过 ${maxSize / 1024 / 1024}MB`,
|
|
|
+ icon: 'none',
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ const fileExtension = file?.tempFiles?.name?.split('.').pop()?.toLowerCase()
|
|
|
+ const isTypeValid = accept.some((type) => type === '*' || type.toLowerCase() === fileExtension)
|
|
|
+
|
|
|
+ if (!isTypeValid) {
|
|
|
+ uni.showToast({
|
|
|
+ title: `仅支持 ${accept.join(', ')} 格式的文件`,
|
|
|
+ icon: 'none',
|
|
|
+ })
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ loading.value = true
|
|
|
+ uploadFile({
|
|
|
+ tempFilePath: file.tempFilePath,
|
|
|
+ formData,
|
|
|
+ onSuccess: (res) => {
|
|
|
+ data.value = res
|
|
|
+ success?.(res)
|
|
|
},
|
|
|
- fail: (err) => {
|
|
|
- console.error('uni.chooseImage err->', err)
|
|
|
- error.value = true
|
|
|
+ onError: (err) => {
|
|
|
+ error.value = err
|
|
|
+ onError?.(err)
|
|
|
+ },
|
|
|
+ onComplete: () => {
|
|
|
+ loading.value = false
|
|
|
},
|
|
|
})
|
|
|
- // #endif
|
|
|
}
|
|
|
|
|
|
return { loading, error, data, run }
|
|
|
}
|
|
|
|
|
|
-function uploadFile<T>({ tempFilePath, formData, data, error, loading }) {
|
|
|
+async function uploadFile({
|
|
|
+ tempFilePath,
|
|
|
+ formData,
|
|
|
+ onSuccess,
|
|
|
+ onError,
|
|
|
+ onComplete,
|
|
|
+}: {
|
|
|
+ tempFilePath: string
|
|
|
+ formData: Record<string, any>
|
|
|
+ onSuccess: (data: any) => void
|
|
|
+ onError: (err: any) => void
|
|
|
+ onComplete: () => void
|
|
|
+}) {
|
|
|
uni.uploadFile({
|
|
|
url: VITE_UPLOAD_BASEURL,
|
|
|
filePath: tempFilePath,
|
|
|
name: 'file',
|
|
|
formData,
|
|
|
success: (uploadFileRes) => {
|
|
|
- data.value = uploadFileRes.data as T
|
|
|
+ try {
|
|
|
+ const data = JSON.parse(uploadFileRes.data)
|
|
|
+ onSuccess(data)
|
|
|
+ } catch (err) {
|
|
|
+ onError(err)
|
|
|
+ }
|
|
|
},
|
|
|
fail: (err) => {
|
|
|
- console.error('uni.uploadFile err->', err)
|
|
|
- error.value = true
|
|
|
- },
|
|
|
- complete: () => {
|
|
|
- loading.value = false
|
|
|
+ console.error('Upload failed:', err)
|
|
|
+ onError(err)
|
|
|
},
|
|
|
+ complete: onComplete,
|
|
|
})
|
|
|
}
|