ExtUpload.vue 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. <template>
  2. <el-upload
  3. class="avatar-uploader"
  4. :action="state.action"
  5. :headers="state.headers"
  6. :show-file-list="false"
  7. :multiple="multiple"
  8. :on-success="handleAvatarSuccess"
  9. :on-error="handleAvatarError"
  10. :before-upload="beforeAvatarUpload"
  11. >
  12. <template v-if="multiple">
  13. <div v-for="(img,idx) in showImageList" :key="idx" class="hp" style="display: block;">
  14. <SvgIcon name="ele-CircleCloseFilled" color="red" class="hc" style="position: absolute;top:2px;" @click.stop="handleRemove(idx)"></SvgIcon>
  15. <img :src="img" class="avatar"/>
  16. </div>
  17. <el-icon v-if="showImageList.length<max" class="avatar-uploader-icon">
  18. <Plus/>
  19. </el-icon>
  20. </template>
  21. <template v-else>
  22. <SvgIcon name="ele-Remove" class="hc"> </SvgIcon>
  23. <img v-if="showImageList[0]" :src="showImageList[0]" class="avatar"/>
  24. <el-icon v-else class="avatar-uploader-icon">
  25. <Plus/>
  26. </el-icon>
  27. </template>
  28. </el-upload>
  29. </template>
  30. <script lang="ts" setup>
  31. import {computed, onMounted, reactive, ref, watch} from 'vue'
  32. import type {UploadProps} from 'element-plus'
  33. import {ElMessage} from 'element-plus'
  34. import {Plus} from '@element-plus/icons-vue'
  35. import {Session} from "/@/utils/storage";
  36. import u from "/@/utils/u"
  37. import {Msg} from "/@/utils/message";
  38. const emit = defineEmits(['update:modelValue', 'on-change']);
  39. const props = defineProps({
  40. modelValue: {
  41. type: String
  42. },
  43. multiple: {
  44. type: Boolean,
  45. default: false
  46. },
  47. max: {
  48. type: Number,
  49. default: 9
  50. },
  51. splitor: {
  52. type: String,
  53. default: ";"
  54. }
  55. })
  56. const typeOf = (obj: any) => {
  57. return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase();
  58. }
  59. const state = reactive({
  60. action: '',
  61. headers: {},
  62. imageList: [] as Array<string>
  63. })
  64. const imageUrl = ref('')
  65. watch(() => props.modelValue, (nv, v) => {
  66. if (nv) {
  67. if (props.multiple) {
  68. if (Array.isArray(nv)) {
  69. state.imageList = nv;
  70. } else {
  71. state.imageList = nv.split(props.splitor);
  72. }
  73. } else {
  74. state.imageList = [nv]
  75. }
  76. } else {
  77. state.imageList = []
  78. }
  79. //console.log(state.imageList)
  80. // imageUrl.value = u.fmt.fmtUrl(o);
  81. }, {immediate: true})
  82. const handleRemove = (idx: number) => {
  83. state.imageList.splice(idx, 1);
  84. emitData()
  85. }
  86. const showImageList = computed(() => {
  87. return state.imageList.map((k: string) => u.fmt.fmtUrl(k))
  88. }
  89. )
  90. onMounted(() => {
  91. let url = import.meta.env.VITE_API_URL;
  92. if (!url) {
  93. url = `${location.origin}/admin/`;
  94. }
  95. state.action = `${url}/file/upload`
  96. state.headers = {"accessToken": Session.get("accessToken")}
  97. })
  98. const handleAvatarError = () => {
  99. Msg.hideLoading();
  100. }
  101. const handleAvatarSuccess: UploadProps['onSuccess'] = (
  102. response,
  103. uploadFile
  104. ) => {
  105. Msg.hideLoading();
  106. //console.log(uploadFile, response)
  107. let {url, uuid} = response.data;
  108. imageUrl.value = url;
  109. // imageUrl.value = URL.createObjectURL(uploadFile.raw!)
  110. if (props.multiple) {
  111. state.imageList.unshift(url)
  112. } else {
  113. state.imageList = [url]
  114. }
  115. emitData()
  116. }
  117. const emitData = () => {
  118. if (props.multiple) {
  119. emit("update:modelValue", state.imageList.join(props.splitor))
  120. emit("on-change", state.imageList.join(props.splitor))
  121. } else {
  122. if (state.imageList.length > 0) {
  123. emit("update:modelValue", state.imageList[0])
  124. emit("on-change", state.imageList[0])
  125. } else {
  126. emit("update:modelValue", null)
  127. emit("on-change", null)
  128. }
  129. }
  130. }
  131. const beforeAvatarUpload: UploadProps['beforeUpload'] = (rawFile) => {
  132. if (rawFile.type !== 'image/jpeg' && rawFile.type !== 'image/png') {
  133. ElMessage.error('Avatar picture must be JPG format!')
  134. return false
  135. } else if (rawFile.size / 1024 / 1024 > 2) {
  136. ElMessage.error('Avatar picture size can not exceed 2MB!')
  137. return false
  138. }
  139. Msg.showLoading("上传中...")
  140. return true
  141. }
  142. </script>
  143. <style scoped>
  144. .avatar-uploader .avatar {
  145. width: 60px;
  146. height: 60px;
  147. display: block;
  148. }
  149. </style>
  150. <style>
  151. .avatar-uploader .el-upload {
  152. border: 1px dashed var(--el-border-color);
  153. border-radius: 6px;
  154. cursor: pointer;
  155. position: relative;
  156. overflow: hidden;
  157. transition: var(--el-transition-duration-fast);
  158. }
  159. .avatar-uploader .el-upload:hover {
  160. border-color: var(--el-color-primary);
  161. }
  162. .el-icon.avatar-uploader-icon {
  163. font-size: 28px;
  164. color: #8c939d;
  165. width: 60px;
  166. height: 60px;
  167. text-align: center;
  168. margin-left: -5px;
  169. }
  170. </style>