Files
YGChatCS/src/pages-aigc/home/components/TemplateCard/index.vue
duanshuwen 2b12398f2a refactor: replace custom ellipsis classes with native utilities
delete unused ellipsis.scss partial and its import, update all components to use truncate and line-clamp-2 utilities
2026-07-24 22:19:35 +08:00

92 lines
2.7 KiB
Vue

<template>
<view class="template-card" :class="{ 'is-active': active }">
<view class="template-media">
<view class="template-media-placeholder" />
<image v-if="templatePosterUrl" class="template-image" :src="templatePosterUrl" mode="aspectFill" />
<video v-if="templateVideoUrl && playing && !videoLoadFailed" class="template-video"
:class="{ 'is-ready': videoReady }" :src="templateVideoUrl" autoplay muted loop :controls="false"
:show-play-btn="false" :show-center-play-btn="false" :show-fullscreen-btn="false"
:enable-progress-gesture="false" object-fit="cover" @play="handleVideoReady" @timeupdate="handleVideoReady"
@error="handleVideoError" />
<view class="template-media-shade" />
</view>
<view class="template-copy">
<text class="template-badge">{{ template.templateTag }}</text>
<text class="template-title truncate">{{ template.templateTitle || template.templateName }}</text>
<text class="template-desc truncate">{{ template.templateSubTitle }}</text>
<text class="template-note truncate">{{ template.templateDescription }}</text>
<button class="template-select" type="default" @tap.stop="emit('select')">
选这个模板
</button>
</view>
</view>
</template>
<script setup>
import { computed, ref, watch } from "vue";
const props = defineProps({
template: {
type: Object,
default: () => ({}),
},
active: {
type: Boolean,
default: false,
},
playing: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["select"]);
const VIDEO_EXT_REGEXP = /\.(mp4|mov|webm|m4v)(?:[?#].*)?$/i;
const videoLoadFailed = ref(false);
const videoReady = ref(false);
const templateMediaUrl = computed(() => {
return props.template.templateContentUrl || props.template.coverPhotoUrl || "";
});
const isVideoTemplate = computed(() => {
return VIDEO_EXT_REGEXP.test(templateMediaUrl.value);
});
const templateVideoUrl = computed(() => (isVideoTemplate.value ? templateMediaUrl.value : ""));
const templatePosterUrl = computed(() => {
const coverUrl = props.template.coverPhotoUrl || "";
if (isVideoTemplate.value) {
return coverUrl && !VIDEO_EXT_REGEXP.test(coverUrl) ? coverUrl : "";
}
return templateMediaUrl.value;
});
const handleVideoReady = () => {
videoReady.value = true;
};
const handleVideoError = () => {
videoReady.value = false;
videoLoadFailed.value = true;
};
watch(templateVideoUrl, () => {
videoReady.value = false;
videoLoadFailed.value = false;
});
watch(() => props.playing, (playing) => {
videoReady.value = false;
if (playing) {
videoLoadFailed.value = false;
}
});
</script>
<style scoped lang="scss">
@import "./styles/index.scss";
</style>