useUpload.ts 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145
  1. import { ref, Ref } from 'vue'
  2. import { getEnvBaseUploadUrl } from '@/utils'
  3. const VITE_UPLOAD_BASEURL = `${getEnvBaseUploadUrl()}`
  4. type TfileType = 'image' | 'file'
  5. type TImage = 'png' | 'jpg' | 'jpeg' | 'webp' | '*'
  6. type TFile = 'doc' | 'docx' | 'ppt' | 'zip' | 'xls' | 'xlsx' | 'txt' | TImage
  7. type TOptions<T extends TfileType> = {
  8. formData?: Record<string, any>
  9. maxSize?: number
  10. accept?: T extends 'image' ? TImage[] : TFile[]
  11. fileType?: T
  12. success?: (params: any) => void
  13. error?: (err: any) => void
  14. }
  15. export default function useUpload<T extends TfileType>(options: TOptions<T> = {} as TOptions<T>) {
  16. const {
  17. formData = {},
  18. maxSize = 5 * 1024 * 1024,
  19. accept = ['*'],
  20. fileType = 'image',
  21. success,
  22. error: onError,
  23. } = options
  24. const loading = ref(false)
  25. const error = ref<Error | null>(null)
  26. const data = ref<any>(null)
  27. const run = () => {
  28. // #ifdef MP-WEIXIN
  29. // 微信小程序从基础库 2.21.0 开始, wx.chooseImage 停止维护,请使用 uni.chooseMedia 代替。
  30. // 微信小程序在2023年10月17日之后,使用本API需要配置隐私协议
  31. const chooseFileOptions = {
  32. count: 1,
  33. success: (res: any) => {
  34. handleFileChoose({ tempFilePath: res.tempFilePaths[0], tempFiles: res.tempFiles[0] })
  35. },
  36. fail: (err: any) => {
  37. console.error('File selection failed:', err)
  38. error.value = err
  39. onError?.(err)
  40. },
  41. }
  42. if (fileType === 'image') {
  43. // #ifdef MP-WEIXIN
  44. uni.chooseMedia({
  45. ...chooseFileOptions,
  46. mediaType: ['image'],
  47. })
  48. // #endif
  49. // #ifndef MP-WEIXIN
  50. uni.chooseImage(chooseFileOptions)
  51. // #endif
  52. } else {
  53. uni.chooseFile({
  54. ...chooseFileOptions,
  55. type: 'all',
  56. })
  57. }
  58. }
  59. const handleFileChoose = (file: {
  60. tempFilePath: string
  61. tempFiles?: { size?: number; name?: string }
  62. }) => {
  63. if (file?.tempFiles?.size && file.tempFiles.size > maxSize) {
  64. uni.showToast({
  65. title: `文件大小不能超过 ${maxSize / 1024 / 1024}MB`,
  66. icon: 'none',
  67. })
  68. return
  69. }
  70. const fileExtension = file?.tempFiles?.name?.split('.').pop()?.toLowerCase()
  71. const isTypeValid = accept.some((type) => type === '*' || type.toLowerCase() === fileExtension)
  72. if (!isTypeValid) {
  73. uni.showToast({
  74. title: `仅支持 ${accept.join(', ')} 格式的文件`,
  75. icon: 'none',
  76. })
  77. return
  78. }
  79. loading.value = true
  80. uploadFile({
  81. tempFilePath: file.tempFilePath,
  82. formData,
  83. onSuccess: (res) => {
  84. data.value = res
  85. success?.(res)
  86. },
  87. onError: (err) => {
  88. error.value = err
  89. onError?.(err)
  90. },
  91. onComplete: () => {
  92. loading.value = false
  93. },
  94. })
  95. }
  96. return { loading, error, data, run }
  97. }
  98. async function uploadFile({
  99. tempFilePath,
  100. formData,
  101. onSuccess,
  102. onError,
  103. onComplete,
  104. }: {
  105. tempFilePath: string
  106. formData: Record<string, any>
  107. onSuccess: (data: any) => void
  108. onError: (err: any) => void
  109. onComplete: () => void
  110. }) {
  111. uni.uploadFile({
  112. url: VITE_UPLOAD_BASEURL,
  113. filePath: tempFilePath,
  114. name: 'file',
  115. formData,
  116. success: (uploadFileRes) => {
  117. try {
  118. const data = JSON.parse(uploadFileRes.data)
  119. onSuccess(data)
  120. } catch (err) {
  121. onError(err)
  122. }
  123. },
  124. fail: (err) => {
  125. console.error('Upload failed:', err)
  126. onError(err)
  127. },
  128. complete: onComplete,
  129. })
  130. }