video.vue 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201
  1. <template>
  2. <a-upload
  3. v-model:file-list="videoFile"
  4. name="file"
  5. :show-upload-list="false"
  6. accept=".mp4"
  7. :action="baseURL + '/takelook/upload/file'"
  8. :max-count="1"
  9. class="uploader"
  10. :headers="{
  11. authorization: 'authorization-text'
  12. }"
  13. :disabled="videoFile.length > 0"
  14. @change="handleAvatarChange"
  15. @preview="handleVideoPreview"
  16. >
  17. <div
  18. class="add-item-icon scene-sign"
  19. v-if="videoFile.length > 0 && videoFile[0].response"
  20. @click.stop="handleVideoPreview"
  21. >
  22. <video class="avatar" :src="videoFile[0].response.data" alt="video" />
  23. <span class="delete-scene" @click.stop="deleteAvatar(videoFile[0])">
  24. <close-outlined class="delete-scene-icon" />
  25. </span>
  26. </div>
  27. <div class="add-item-icon" v-else>
  28. <a-button shape="circle" class="button" type="primary">
  29. <plus-outlined class="add-room-icon" />
  30. </a-button>
  31. </div>
  32. <a-modal
  33. :visible="showVideoPreview"
  34. :footer="null"
  35. width="800px"
  36. @cancel="showVideoPreview = false"
  37. >
  38. <video controls id="previewVideo"></video>
  39. </a-modal>
  40. </a-upload>
  41. </template>
  42. <script lang="ts" setup>
  43. import { ref } from 'vue'
  44. import { message, type UploadChangeParam } from 'ant-design-vue'
  45. import type { FileItem } from './album-list.vue'
  46. import { baseURL } from '@/env'
  47. import { watchEffect } from 'vue'
  48. import { nextTick } from 'vue'
  49. const emit = defineEmits(['sync'])
  50. const videoFile = ref<any[]>([])
  51. const props = defineProps<{ data: string | undefined }>()
  52. const showVideoPreview = ref(false)
  53. const previewSrc = ref('')
  54. watchEffect(() => {
  55. if (props.data?.length) {
  56. const tempData = {} as FileItem
  57. tempData.uid = `data-0`
  58. tempData.response = {
  59. data: props.data,
  60. ok: 0
  61. }
  62. if (!videoFile.value?.length) {
  63. console.log('mapper', tempData)
  64. videoFile.value = [tempData]
  65. }
  66. } else {
  67. videoFile.value = []
  68. }
  69. })
  70. const handleAvatarChange = (info: UploadChangeParam) => {
  71. if (info.file.status === 'done') {
  72. const { code, data } = info.file.response
  73. if (code === 0) {
  74. console.log('syncVideo', data)
  75. previewSrc.value = data
  76. emit('sync', data || '')
  77. }
  78. } else if (info.file.status === 'error') {
  79. message.error(`${info.file.name} file upload failed.`)
  80. }
  81. }
  82. const deleteAvatar = (item: any) => {
  83. const index = videoFile.value.findIndex(i => i.uid === item.uid)
  84. if (index > -1) {
  85. videoFile.value.splice(index, 1)
  86. }
  87. emit('sync', '')
  88. }
  89. const handleVideoPreview = () => {
  90. showVideoPreview.value = true
  91. nextTick(() => {
  92. const video = document.getElementById('previewVideo')
  93. if (video) {
  94. // debugger
  95. const src = previewSrc.value || videoFile.value[0].response.data
  96. console.log('src', src)
  97. ;(video as any).src = src
  98. }
  99. })
  100. }
  101. </script>
  102. <style lang="scss" scoped>
  103. .add-item-icon {
  104. width: 120px;
  105. height: 120px;
  106. display: flex;
  107. justify-content: center;
  108. align-items: center;
  109. border: 1px solid #ebedf0;
  110. overflow: hidden;
  111. .avatar {
  112. max-width: 100%;
  113. // border-radius: 50%;
  114. }
  115. .button {
  116. background: linear-gradient(144deg, #00aefb 0%, #0076f6 100%);
  117. width: 40px;
  118. height: 40px;
  119. }
  120. }
  121. .scene-sign {
  122. border-radius: 4px;
  123. overflow: hidden;
  124. position: relative;
  125. z-index: 10;
  126. img {
  127. width: 100%;
  128. height: 100%;
  129. display: block;
  130. object-fit: cover;
  131. }
  132. .title {
  133. position: absolute;
  134. bottom: 0;
  135. left: 0;
  136. right: 0;
  137. height: 24px;
  138. background: rgba(0, 0, 0, 0.5);
  139. padding: 5px;
  140. font-size: 12px;
  141. color: #fff;
  142. margin: 0;
  143. overflow: hidden;
  144. text-overflow: ellipsis;
  145. white-space: nowrap;
  146. }
  147. .delete-scene {
  148. z-index: 2;
  149. position: absolute;
  150. right: 0;
  151. top: 0;
  152. width: 52px;
  153. height: 52px;
  154. background-color: rgba(0, 0, 0, 0.5);
  155. color: #fa5555;
  156. font-size: 14px;
  157. border-radius: 50%;
  158. display: flex;
  159. align-items: flex-end;
  160. transform: translate(100%, -100%);
  161. transition: all 0.3s ease;
  162. opacity: 0;
  163. cursor: pointer;
  164. .delete-scene-icon {
  165. padding: 10px;
  166. }
  167. }
  168. &:hover .delete-scene {
  169. transform: translate(50%, -50%);
  170. opacity: 1;
  171. }
  172. .status-cover {
  173. z-index: 1;
  174. position: absolute;
  175. left: 0;
  176. top: 0;
  177. right: 0;
  178. bottom: 0;
  179. background-color: rgba(0, 0, 0, 0.5);
  180. display: flex;
  181. align-items: center;
  182. justify-content: center;
  183. p {
  184. color: #fff;
  185. }
  186. }
  187. }
  188. </style>
  189. <style lang="scss">
  190. #previewVideo {
  191. width: 100%;
  192. min-height: 500px;
  193. }
  194. </style>