delete unused ellipsis.scss partial and its import, update all components to use truncate and line-clamp-2 utilities
92 lines
2.7 KiB
Vue
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>
|