146 lines
2.6 KiB
Vue
146 lines
2.6 KiB
Vue
<template>
|
|
<div class="item">
|
|
<t-button variant="text" class="preview-trigger" @click="emit('preview')">
|
|
<div v-if="isVideoItem(item)" class="video-thumbnail">
|
|
<t-image
|
|
v-if="getMediaThumbnailURL(item)"
|
|
class="image"
|
|
:src="getMediaThumbnailURL(item)"
|
|
:alt="getAttachTypeText(item.attachType)"
|
|
fit="cover"
|
|
/>
|
|
<video
|
|
v-else
|
|
:src="getMediaPreviewURL(item)"
|
|
muted
|
|
playsinline
|
|
preload="metadata"
|
|
/>
|
|
<span class="video-play" aria-hidden="true">▶</span>
|
|
</div>
|
|
<t-image
|
|
v-else
|
|
class="image"
|
|
:src="item.url"
|
|
:alt="getAttachTypeText(item.attachType)"
|
|
fit="cover"
|
|
/>
|
|
</t-button>
|
|
<div
|
|
v-if="showTypeLabel"
|
|
class="type-label white glass-black clip-text"
|
|
v-text="getAttachTypeText(item.attachType)"
|
|
/>
|
|
<t-button
|
|
v-if="showRemove && removable"
|
|
theme="danger"
|
|
class="remove"
|
|
shape="circle"
|
|
size="extra-small"
|
|
@click="emit('remove')"
|
|
>
|
|
<t-icon name="close" size="1rem" />
|
|
</t-button>
|
|
</div>
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import type { PhotoItem } from "@/types/Photo.ts";
|
|
import {
|
|
getAttachTypeText,
|
|
getMediaPreviewURL,
|
|
getMediaThumbnailURL,
|
|
isVideoItem
|
|
} from "./utils";
|
|
|
|
interface Props {
|
|
item: PhotoItem;
|
|
removable: boolean;
|
|
showRemove: boolean;
|
|
showTypeLabel: boolean;
|
|
}
|
|
|
|
defineProps<Props>();
|
|
const emit = defineEmits<{
|
|
preview: [];
|
|
remove: [];
|
|
}>();
|
|
</script>
|
|
|
|
<style scoped lang="less">
|
|
.item {
|
|
width: 5.5rem;
|
|
height: 5.5rem;
|
|
flex: 0 0 5.5rem;
|
|
position: relative;
|
|
overflow: hidden;
|
|
border-radius: 1rem;
|
|
|
|
.preview-trigger {
|
|
padding: 0;
|
|
border: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
display: block;
|
|
cursor: pointer;
|
|
background: transparent;
|
|
|
|
.image {
|
|
height: 100%;
|
|
}
|
|
|
|
.video-thumbnail {
|
|
width: 100%;
|
|
height: 100%;
|
|
position: relative;
|
|
background: #0f172a;
|
|
|
|
video {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.video-play {
|
|
inset: 50% auto auto 50%;
|
|
width: 2rem;
|
|
height: 2rem;
|
|
color: #fff;
|
|
position: absolute;
|
|
display: flex;
|
|
font-size: 1.125rem;
|
|
align-items: center;
|
|
justify-content: center;
|
|
background: rgba(15, 23, 42, .68);
|
|
border-radius: 50%;
|
|
transform: translate(-50%, -50%);
|
|
}
|
|
}
|
|
}
|
|
|
|
.type-label {
|
|
width: 100%;
|
|
bottom: 0;
|
|
padding: .125rem .5rem;
|
|
position: absolute;
|
|
overflow: hidden;
|
|
font-size: .875rem;
|
|
text-align: center;
|
|
line-height: 1.2;
|
|
border-radius: 0 0 1rem 1rem;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.remove {
|
|
top: .375rem;
|
|
right: .375rem;
|
|
position: absolute;
|
|
|
|
:deep(.t-button__content) {
|
|
display: flex;
|
|
align-items: center;
|
|
}
|
|
}
|
|
}
|
|
</style>
|