refactor(aigc): update carousel and video components

- rewrite template carousel from custom scroll to uni swiper
- add video loading, error, and replay states to result preview
- update template card styles and video/poster handling logic
- clean up unused CSS vars and remove unnecessary min-height on detail page
- fix video file regex and add video error recovery
This commit is contained in:
duanshuwen
2026-07-18 15:30:06 +08:00
parent 05edc5666c
commit 2407a9d941
7 changed files with 361 additions and 126 deletions

View File

@@ -4,19 +4,22 @@
:class="{ 'is-active': active }"
>
<view class="template-media">
<image v-if="templatePosterUrl" class="template-image" :src="templatePosterUrl" mode="aspectFill" />
<video
v-if="templateMediaUrl && isVideoTemplate"
v-else-if="templateVideoUrl && playing && !videoLoadFailed"
class="template-video"
:src="templateMediaUrl"
: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"
@error="handleVideoError"
/>
<image v-else-if="templateMediaUrl" class="template-image" :src="templateMediaUrl" mode="aspectFill" />
<view class="template-media-shade" />
</view>
@@ -33,7 +36,7 @@
</template>
<script setup>
import { computed } from "vue";
import { computed, ref, watch } from "vue";
const props = defineProps({
template: {
@@ -44,11 +47,16 @@ const props = defineProps({
type: Boolean,
default: false,
},
playing: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["select"]);
const VIDEO_EXT_REGEXP = /\.(mp4|mov|webm|m4v)(\?.*)?$/i;
const VIDEO_EXT_REGEXP = /\.(mp4|mov|webm|m4v)(?:[?#].*)?$/i;
const videoLoadFailed = ref(false);
const templateMediaUrl = computed(() => {
return props.template.templateContentUrl || props.template.coverPhotoUrl || "";
@@ -57,6 +65,23 @@ const templateMediaUrl = computed(() => {
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 handleVideoError = () => {
videoLoadFailed.value = true;
};
watch(templateVideoUrl, () => {
videoLoadFailed.value = false;
});
</script>
<style scoped lang="scss">