imgs.vue 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  1. <template>
  2. <div class="mb-3 color-666">共<strong class="color-primary"> {{ srcList.length }} </strong>张,还能上传 <strong class="color-primary">{{ (Number(limit) - Number(srcList.length)) }}</strong> 张</div>
  3. <div class="d-flex flex-wrap">
  4. <div style="position: relative;" v-for="val in srcList" :key="val" class="mr-3 mb-3">
  5. <v-icon color="error" class="close" @click="handleClose(val)">mdi-close-circle</v-icon>
  6. <v-img :src="val" width="100" height="100" rounded class="imgBox"></v-img>
  7. </div>
  8. <div v-if="srcList.length < limit" class="upload d-flex align-center justify-center flex-column" @click="openFileInput">
  9. <v-icon color="#ccc" :size="tips ? 30 : 50">mdi-plus</v-icon>
  10. <div class="font-size-12 color-999">{{ tips }}</div>
  11. <input
  12. type="file"
  13. ref="fileInput"
  14. accept="image/*"
  15. style="display: none;"
  16. @change="handleUploadFile"
  17. />
  18. </div>
  19. </div>
  20. </template>
  21. <script setup>
  22. // 图片组上传
  23. defineOptions({ name: 'upload-imgs'})
  24. import { ref, watch } from 'vue'
  25. import { uploadFile } from '@/api/common'
  26. import { useI18n } from '@/hooks/web/useI18n'
  27. import Snackbar from '@/plugins/snackbar'
  28. const props = defineProps({
  29. modelValue: Array,
  30. tips: {
  31. type: String,
  32. default: '上传图片'
  33. },
  34. showSnackbar: {
  35. type: Boolean,
  36. default: false
  37. },
  38. limit: {
  39. type: [Number, String],
  40. default: 9
  41. }
  42. })
  43. const { t } = useI18n()
  44. const srcList = ref([])
  45. watch(() => props.modelValue, (newVal) => {
  46. srcList.value = newVal
  47. }, { immediate: true }, { deep: true })
  48. // 选择文件
  49. const fileInput = ref()
  50. const clicked = ref(false)
  51. const openFileInput = () => {
  52. if (srcList.value.length >= props.limit) return Snackbar.warning(`最多可上传${props.limit}张图片`)
  53. if (clicked.value) return
  54. clicked.value = true
  55. fileInput.value.click()
  56. clicked.value = false
  57. }
  58. // 文件上传
  59. const accept = ['jpg', 'png', 'webp', 'jpeg']
  60. const handleUploadFile = async (e) => {
  61. const file = e.target.files[0]
  62. const size = file.size
  63. if (size / (1024*1024) > 20) {
  64. Snackbar.warning(t('common.fileSizeExceed'))
  65. return
  66. }
  67. const fileType = file.name.split('.')[1]
  68. if (!accept.includes(fileType)) return Snackbar.warning('请上传图片格式')
  69. const formData = new FormData()
  70. formData.append('file', file)
  71. formData.append('path', 'img')
  72. const { data } = await uploadFile(formData)
  73. if (!data) return Snackbar.error('上传失败')
  74. if (props.showSnackbar) Snackbar.success(t('common.uploadSucMsg'))
  75. srcList.value.push(data)
  76. }
  77. const handleClose = (src) => {
  78. const index = srcList.value.indexOf(src)
  79. if (index === -1) return
  80. srcList.value.splice(index, 1)
  81. }
  82. </script>
  83. <style scoped lang="scss">
  84. .upload {
  85. width: 100px;
  86. height: 100px;
  87. border: 1px solid #ccc;
  88. border-radius: 4px;
  89. cursor: pointer;
  90. }
  91. .imgBox {
  92. position: relative;
  93. border: 1px solid #ccc;
  94. border-radius: 4px;
  95. padding: 5px;
  96. }
  97. .close {
  98. position: absolute;
  99. top: -10px;
  100. right: -10px;
  101. cursor: pointer;
  102. z-index: 9;
  103. }
  104. </style>