MultiUpload.vue 3.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. <template>
  2. <section>
  3. <el-upload
  4. list-type="picture-card"
  5. accept="image/*"
  6. :action="uploadUrl"
  7. :on-preview="handlePictureCardPreview"
  8. :on-success="handleSuccess"
  9. :on-remove="handleRemove"
  10. v-model:file-list="fileList"
  11. @before-upload="beforeUpload"
  12. multiple
  13. >
  14. <el-icon>
  15. <Plus />
  16. </el-icon>
  17. <template #tip>
  18. <div class="el-upload__tip">
  19. <slot></slot>
  20. </div>
  21. </template>
  22. </el-upload>
  23. <ElImageViewer v-if="showPreview" :url-list="[previewUrl]" teleported @close="showPreview = false" />
  24. </section>
  25. </template>
  26. <script setup>
  27. import resolveUrl from 'resolve-url'
  28. import { ref, watch } from 'vue'
  29. import { ElMessage } from 'element-plus'
  30. import { Plus } from '@vicons/tabler'
  31. import { useImageSize } from '@/utils/imageSize'
  32. const props = defineProps({
  33. modelValue: Array,
  34. maxWidth: {
  35. type: Number,
  36. default: -1
  37. },
  38. maxHeight: {
  39. type: Number,
  40. default: -1
  41. },
  42. maxSize: {
  43. type: Number,
  44. default: 1024 * 1024
  45. }
  46. })
  47. const emit = defineEmits(['update:modelValue'])
  48. const uploadUrl = resolveUrl(import.meta.env.VITE_API_BASE_URL, '/api/file/upload')
  49. const fileList = ref([])
  50. const loading = ref(false)
  51. const showPreview = ref(false)
  52. const previewUrl = ref(null)
  53. watch(
  54. () => props.modelValue,
  55. (val) => {
  56. if (val && val.join() === fileList.value.map((i) => i.realUrl).join()) {
  57. return
  58. }
  59. updateFileList(val)
  60. }
  61. )
  62. updateFileList(props.modelValue || [])
  63. function updateFileList(list) {
  64. fileList.value = list.map((i) => {
  65. return {
  66. url: i,
  67. realUrl: i
  68. }
  69. })
  70. }
  71. async function beforeUpload(file) {
  72. if (!/^image/.test(file.type)) {
  73. this.$message.error('只能上传图片格式!')
  74. return false
  75. }
  76. if (this.maxSize > 0 && file.size > this.maxSize) {
  77. this.$message.error('上传图片大小不能超过 ' + this.maxSize / 1024 + 'KB!')
  78. return false
  79. }
  80. const { width, height } = await useImageSize(URL.createObjectURL(file))
  81. if (props.maxWidth > 0 && width > props.maxWidth) {
  82. this.$message.error('上传图片宽度不能超过 ' + props.maxWidth + 'px!')
  83. return false
  84. } else if (props.maxHeight > 0 && height > props.maxHeight) {
  85. this.$message.error('上传图片高度不能超过 ' + props.maxHeight + 'px!')
  86. return false
  87. } else {
  88. loading.value = true
  89. return true
  90. }
  91. }
  92. function handlePictureCardPreview(file) {
  93. previewUrl.value = file.url || file.realUrl
  94. showPreview.value = true
  95. }
  96. function handleSuccess(res, file, fileList) {
  97. file.realUrl = res.url
  98. emit(
  99. 'update:modelValue',
  100. fileList.map((i) => i.realUrl)
  101. )
  102. }
  103. function handleRemove(file, fileList) {
  104. emit(
  105. 'update:modelValue',
  106. fileList.map((i) => i.realUrl)
  107. )
  108. }
  109. </script>
  110. <style lang="less" scoped></style>