mediaComponents.vue 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. <template>
  2. <el-button type="primary" :loading="loading" v-if="!src">
  3. <label class="file" for="file"> 上传素材 </label>
  4. <input
  5. style="display: none"
  6. @change="chagne"
  7. type="file"
  8. name="file"
  9. accept="image/*,video/*"
  10. id="file"
  11. />
  12. </el-button>
  13. <!-- 文件上传 图片 视频 -->
  14. <div v-else class="media-components">
  15. <svg
  16. t="1732589883711"
  17. class="close"
  18. viewBox="0 0 1024 1024"
  19. version="1.1"
  20. xmlns="http://www.w3.org/2000/svg"
  21. @click="close"
  22. width="16"
  23. height="16"
  24. >
  25. <path
  26. d="M548.992 503.744L885.44 167.328a31.968 31.968 0 1 0-45.248-45.248L503.744 458.496 167.328 122.08a31.968 31.968 0 1 0-45.248 45.248l336.416 336.416L122.08 840.16a31.968 31.968 0 1 0 45.248 45.248l336.416-336.416L840.16 885.44a31.968 31.968 0 1 0 45.248-45.248L548.992 503.744z"
  27. p-id="4267"
  28. />
  29. </svg>
  30. <el-image style="width: 120px; height: 120px" :src="src" fit="contain" />
  31. <svg
  32. t="1732603337032"
  33. v-if="fileType === 'video'"
  34. class="play"
  35. viewBox="0 0 1024 1024"
  36. version="1.1"
  37. xmlns="http://www.w3.org/2000/svg"
  38. p-id="5375"
  39. width="32"
  40. height="32"
  41. @click="playVideo"
  42. >
  43. <path
  44. d="M512 0C230.4 0 0 230.4 0 512s230.4 512 512 512 512-230.4 512-512S793.6 0 512 0z m0 981.333333C253.866667 981.333333 42.666667 770.133333 42.666667 512S253.866667 42.666667 512 42.666667s469.333333 211.2 469.333333 469.333333-211.2 469.333333-469.333333 469.333333z"
  45. fill="#707070"
  46. p-id="5376"
  47. ></path>
  48. <path
  49. d="M672 441.6l-170.666667-113.066667c-57.6-38.4-106.666667-12.8-106.666666 57.6v256c0 70.4 46.933333 96 106.666666 57.6l170.666667-113.066666c57.6-42.666667 57.6-106.666667 0-145.066667z"
  50. fill="#707070"
  51. p-id="5377"
  52. ></path>
  53. </svg>
  54. </div>
  55. </template>
  56. <script setup lang="ts">
  57. import { uploadMaterial } from '@/api/index'
  58. // 获取组件传参
  59. import { ElMessage } from 'element-plus'
  60. import { defineProps, ref } from 'vue'
  61. // 传参父组件
  62. const emit = defineEmits(['change', 'getCalc'])
  63. const props = defineProps<{ src: string }>()
  64. const loading = ref(false)
  65. const src = ref(props.src)
  66. const fileType = ref('')
  67. const isImg = (url: string) => {
  68. return /\.(jpg|jpeg|png|gif)$/.test(url)
  69. }
  70. // 判定如果是图片则直接输出图片,如果是视频则输出第一帧图片
  71. const getImage = (file: File | string) => {
  72. const video = document.createElement('video')
  73. fileType.value = 'video'
  74. if (typeof file === 'string') {
  75. video.setAttribute('crossorigin', 'anonymous')
  76. video.src = file
  77. } else {
  78. if (file.type.indexOf('image') !== -1) {
  79. src.value = URL.createObjectURL(file)
  80. fileType.value = 'image'
  81. const img = new Image()
  82. img.onload = function () {
  83. emit('getCalc', img.width, img.height)
  84. }
  85. img.src = URL.createObjectURL(file)
  86. return
  87. }
  88. // 视频 获取第一帧
  89. // 播放本地视频文件
  90. video.src = URL.createObjectURL(file)
  91. }
  92. video.onloadeddata = function () {
  93. // 跳转到第二秒
  94. video.currentTime = 1
  95. // 当视频到达第二秒时,绘制帧
  96. video.addEventListener('seeked', () => {
  97. // 获取第一帧
  98. emit('getCalc', video.videoWidth, video.videoHeight)
  99. const canvas = document.createElement('canvas')
  100. canvas.width = video.videoWidth
  101. canvas.height = video.videoHeight
  102. const ctx = canvas.getContext('2d')
  103. ctx?.drawImage(video, 0, 0, video.videoWidth, video.videoHeight)
  104. src.value = canvas.toDataURL()
  105. })
  106. }
  107. }
  108. if (src.value) {
  109. const isImgUrl = isImg(src.value)
  110. fileType.value = isImgUrl ? 'image' : 'video'
  111. getImage(src.value)
  112. }
  113. let oriUrl = props.src ? props.src : ''
  114. let times = 0
  115. const close = () => {
  116. src.value = ''
  117. fileType.value = ''
  118. oriUrl = ''
  119. emit('getCalc', 0, 0)
  120. emit('change', '')
  121. }
  122. // 传参为文件
  123. const chagne = (e: Event) => {
  124. if (!e.target) return
  125. const inputElement = e.target as HTMLInputElement
  126. if (!inputElement.files || !inputElement.files.length) return
  127. loading.value = true
  128. // 上传文件获取地址后传出组件
  129. const file: File = inputElement.files[0]
  130. const formData = new FormData()
  131. formData.append('file', file)
  132. uploadMaterial(formData)
  133. .then(({ data }) => {
  134. ElMessage({
  135. message: '上传成功',
  136. type: 'success',
  137. })
  138. getImage(file)
  139. inputElement.value = ''
  140. times = 0
  141. oriUrl = data.url
  142. emit('change', oriUrl)
  143. loading.value = false
  144. })
  145. .catch(() => {
  146. if (times++ < 5) {
  147. ElMessage({
  148. message: '正在重新上传',
  149. type: 'info',
  150. })
  151. const t = setTimeout(function () {
  152. console.log(times)
  153. clearTimeout(t)
  154. chagne(e)
  155. }, 1000)
  156. } else {
  157. ElMessage({
  158. message: '网络有问题,请检查后重新上传',
  159. type: 'info',
  160. })
  161. inputElement.value = ''
  162. loading.value = false
  163. times = 0
  164. }
  165. })
  166. }
  167. const playVideo = () => {
  168. window.open(oriUrl)
  169. }
  170. </script>
  171. <style scoped>
  172. .media-components {
  173. border-radius: 4px;
  174. background: #fdfdfd;
  175. width: 120px;
  176. height: 120px;
  177. box-shadow: var(--el-box-shadow-lighter);
  178. position: relative;
  179. }
  180. .close {
  181. position: absolute;
  182. top: 10px;
  183. right: 10px;
  184. cursor: pointer;
  185. z-index: 10;
  186. }
  187. .play {
  188. position: absolute;
  189. top: 50%;
  190. left: 50%;
  191. transform: translate(-50%, -50%);
  192. z-index: 10;
  193. cursor: pointer;
  194. }
  195. </style>