img.vue 2.7 KB

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