mirror of https://github.com/halo-dev/halo
				
				
				
			feat: add support for video thumbnail preview in the attachment library (#6265)
#### What type of PR is this? /kind feature #### What this PR does / why we need it: 附件页面视频文件展示封面 #### Which issue(s) this PR fixes: Fixes https://github.com/halo-dev/halo/issues/5945 #### Special notes for your reviewer:  #### Does this PR introduce a user-facing change? ```release-note 附件库支持预览视频封面。 ```pull/6310/head
							parent
							
								
									45d0a475b5
								
							
						
					
					
						commit
						66d4986531
					
				|  | @ -35,9 +35,12 @@ import AttachmentGroupList from "./components/AttachmentGroupList.vue"; | |||
| import AttachmentListItem from "./components/AttachmentListItem.vue"; | ||||
| import AttachmentPoliciesModal from "./components/AttachmentPoliciesModal.vue"; | ||||
| import AttachmentUploadModal from "./components/AttachmentUploadModal.vue"; | ||||
| import AttachmentLoading from "./components/AttachmentLoading.vue"; | ||||
| import AttachmentError from "./components/AttachmentError.vue"; | ||||
| import { useAttachmentControl } from "./composables/use-attachment"; | ||||
| import { useFetchAttachmentGroup } from "./composables/use-attachment-group"; | ||||
| import { useFetchAttachmentPolicy } from "./composables/use-attachment-policy"; | ||||
| import LazyVideo from "@/components/video/LazyVideo.vue"; | ||||
| 
 | ||||
| const { t } = useI18n(); | ||||
| 
 | ||||
|  | @ -526,24 +529,26 @@ onMounted(() => { | |||
|                         classes="pointer-events-none object-cover group-hover:opacity-75 transform-gpu" | ||||
|                       > | ||||
|                         <template #loading> | ||||
|                           <div | ||||
|                             class="flex h-full items-center justify-center object-cover" | ||||
|                           > | ||||
|                             <span class="text-xs text-gray-400"> | ||||
|                               {{ $t("core.common.status.loading") }}... | ||||
|                             </span> | ||||
|                           </div> | ||||
|                           <AttachmentLoading /> | ||||
|                         </template> | ||||
|                         <template #error> | ||||
|                           <div | ||||
|                             class="flex h-full items-center justify-center object-cover" | ||||
|                           > | ||||
|                             <span class="text-xs text-red-400"> | ||||
|                               {{ $t("core.common.status.loading_error") }} | ||||
|                             </span> | ||||
|                           </div> | ||||
|                           <AttachmentError /> | ||||
|                         </template> | ||||
|                       </LazyImage> | ||||
|                       <LazyVideo | ||||
|                         v-else-if=" | ||||
|                           attachment?.spec.mediaType?.startsWith('video/') | ||||
|                         " | ||||
|                         :src="attachment.status?.permalink" | ||||
|                         classes="object-cover group-hover:opacity-75" | ||||
|                       > | ||||
|                         <template #loading> | ||||
|                           <AttachmentLoading /> | ||||
|                         </template> | ||||
|                         <template #error> | ||||
|                           <AttachmentError /> | ||||
|                         </template> | ||||
|                       </LazyVideo> | ||||
|                       <AttachmentFileTypeIcon | ||||
|                         v-else | ||||
|                         :file-name="attachment.spec.displayName" | ||||
|  |  | |||
|  | @ -0,0 +1,7 @@ | |||
| <template> | ||||
|   <div class="flex h-full items-center justify-center object-cover"> | ||||
|     <span class="text-xs text-red-400"> | ||||
|       {{ $t("core.common.status.loading_error") }} | ||||
|     </span> | ||||
|   </div> | ||||
| </template> | ||||
|  | @ -0,0 +1,7 @@ | |||
| <template> | ||||
|   <div class="flex h-full items-center justify-center object-cover"> | ||||
|     <span class="text-xs text-gray-400"> | ||||
|       {{ $t("core.common.status.loading") }}... | ||||
|     </span> | ||||
|   </div> | ||||
| </template> | ||||
|  | @ -0,0 +1,47 @@ | |||
| <script lang="ts" setup> | ||||
| import { onMounted, ref } from "vue"; | ||||
| 
 | ||||
| const props = withDefaults( | ||||
|   defineProps<{ | ||||
|     src: string; | ||||
|     classes?: string | string[]; | ||||
|   }>(), | ||||
|   { | ||||
|     src: "", | ||||
|     classes: "", | ||||
|   } | ||||
| ); | ||||
| 
 | ||||
| const isLoading = ref(false); | ||||
| const error = ref(false); | ||||
| 
 | ||||
| const loadVideo = async () => { | ||||
|   const video = document.createElement("video"); | ||||
|   video.src = props.src; | ||||
|   return new Promise((resolve, reject) => { | ||||
|     video.onloadedmetadata = () => resolve(video); | ||||
|     video.onerror = (err) => reject(err); | ||||
|   }); | ||||
| }; | ||||
| 
 | ||||
| onMounted(async () => { | ||||
|   isLoading.value = true; | ||||
|   try { | ||||
|     await loadVideo(); | ||||
|   } catch (e) { | ||||
|     error.value = true; | ||||
|   } finally { | ||||
|     isLoading.value = false; | ||||
|   } | ||||
|   isLoading.value = false; | ||||
| }); | ||||
| </script> | ||||
| <template> | ||||
|   <template v-if="isLoading"> | ||||
|     <slot name="loading"> loading... </slot> | ||||
|   </template> | ||||
|   <template v-else-if="error"> | ||||
|     <slot name="error"> error </slot> | ||||
|   </template> | ||||
|   <video v-else :src="src" preload="metadata" :class="classes" /> | ||||
| </template> | ||||
		Loading…
	
		Reference in New Issue
	
	 LonelySnowman
						LonelySnowman