Prechádzať zdrojové kódy

Auto merge base into main

GitHub Actions 11 mesiacov pred
rodič
commit
57e81b20f7
1 zmenil súbory, kde vykonal 113 pridanie a 38 odobranie
  1. 113 38
      src/hooks/useUpload.ts

+ 113 - 38
src/hooks/useUpload.ts

@@ -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,
   })
 }