FileImportUploader.vue 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. <script setup lang="ts">
  2. import { ref, watch } from 'vue'
  3. import { Plus, Upload } from '@element-plus/icons-vue'
  4. import { ElButton, ElUpload, } from 'element-plus'
  5. import type {UploadProgressEvent, UploadFile, UploadFiles } from 'element-plus'
  6. import { toast } from 'vue-sonner'
  7. import { BASE_URL } from '@/api/index'
  8. const props = withDefaults(defineProps<{
  9. modelValue?: UploadFile | null | undefined
  10. action?: string
  11. method?: string
  12. headers?: Headers | Record<string, any>
  13. data?: Record<string, any>
  14. name?: string
  15. afterUpload?: (response: any) => string | Promise<string>
  16. multiple?: boolean
  17. ext?: string[]
  18. max?: number
  19. width?: number
  20. height?: number
  21. dimension?: {
  22. width: number
  23. height: number
  24. }
  25. size?: number
  26. hideTips?: boolean
  27. disabled?: boolean
  28. }>(), {
  29. method: 'post',
  30. headers: () => {
  31. const userStore = useUserStore()
  32. if (userStore.isLogin) {
  33. return {
  34. accessToken: userStore.token,
  35. Apptag: 'app.anycall'
  36. }
  37. }
  38. return {}
  39. },
  40. data: () => ({}),
  41. name: 'file',
  42. multiple: false,
  43. ext: () => [],
  44. max: 1,
  45. width: 100,
  46. height: 100,
  47. size: 5 * 1024 * 1024,
  48. hideTips: false,
  49. disabled: false,
  50. })
  51. const emit = defineEmits<{
  52. // 上传成功时 emit 事件
  53. 'success': [response: any, file: UploadFile, fileList: UploadFiles]
  54. // 更新 v-model: 必须命名为 'update:modelValue' 以支持 v-model
  55. 'update:modelValue': [value: UploadFile | null]
  56. }>()
  57. const internalFileList = ref<UploadFiles>(props.modelValue ? [props.modelValue] : [])
  58. watch(
  59. () => props.modelValue,
  60. (newVal) => {
  61. // 如果父组件的 modelValue 变为 null/undefined,清空内部列表
  62. if (!newVal) {
  63. internalFileList.value = []
  64. } else {
  65. // 如果有新值,更新(避免重复添加)
  66. if (internalFileList.value[0]?.uid !== newVal.uid) {
  67. internalFileList.value = [newVal]
  68. }
  69. }
  70. }
  71. )
  72. const loading = ref(false)
  73. function handleUploadSuccess(response: any, uploadFile: UploadFile, uploadFiles: UploadFiles) {
  74. console.log('【上传成功】响应:', response)
  75. console.log('【上传成功】文件对象:', uploadFile) // 这是最新的文件对象 (包含 url 等)
  76. loading.value = false
  77. emit('update:modelValue', uploadFile)
  78. // 7. 触发成功事件
  79. emit('success', response, uploadFile, uploadFiles)
  80. }
  81. // 8. 上传失败的回调
  82. function handleUploadError(error: Error, uploadFile: UploadFile, uploadFiles: UploadFiles) {
  83. console.error('【上传失败】:', error, uploadFile)
  84. emit('update:modelValue', null) // 失败时也通知父组件
  85. toast.error('文件上传失败')
  86. }
  87. function handleProgress (evt: UploadProgressEvent, uploadFile: UploadFile, uploadFiles: UploadFiles) {
  88. loading.value = true
  89. console.log(evt, uploadFile, uploadFiles)
  90. }
  91. // 添加文件选择变化的处理
  92. function handleFileChange(file: UploadFile, fileList: UploadFiles) {
  93. console.log(file, fileList)
  94. // 当有新文件被选择时,清空现有列表,允许新文件上传
  95. if (file.status === 'success') {
  96. internalFileList.value = [];
  97. }
  98. }
  99. </script>
  100. <template>
  101. <ElUpload
  102. v-model:file-list="internalFileList"
  103. :action="`${BASE_URL}/anycall/adm_clone/import`"
  104. :show-file-list="false"
  105. :limit="1"
  106. :headers="props.headers"
  107. @success="handleUploadSuccess"
  108. @progress="handleProgress"
  109. @change="handleFileChange"
  110. @error="handleUploadError"
  111. :auto-upload="true"
  112. >
  113. <ElButton :loading="loading" type="primary" :icon="Upload">Import</ElButton>
  114. </ElUpload>
  115. </template>