轮播图可以预览

This commit is contained in:
yml2213
2026-07-23 18:28:35 +08:00
parent 9d74cbcf8d
commit 1ea01f7d46
3 changed files with 71 additions and 4 deletions
@@ -1,4 +1,6 @@
<script setup lang="ts"> <script setup lang="ts">
import { computed, ref } from 'vue'
interface HomeBannerSlide { interface HomeBannerSlide {
title?: string title?: string
eyebrow?: string eyebrow?: string
@@ -12,14 +14,43 @@ interface Props {
banners: HomeBannerSlide[] banners: HomeBannerSlide[]
} }
defineProps<Props>() const props = defineProps<Props>()
const previewVisible = ref(false)
const previewIndex = ref(0)
const previewUrls = computed(() =>
props.banners
.map(slide => slide.image_url?.trim() || '')
.filter((url): url is string => Boolean(url))
)
function openPreview(slide: HomeBannerSlide) {
const url = slide.image_url?.trim()
if (!url) return
const index = previewUrls.value.indexOf(url)
if (index < 0) return
previewIndex.value = index
previewVisible.value = true
}
function closePreview() {
previewVisible.value = false
}
</script> </script>
<template> <template>
<section class="hero-board"> <section class="hero-board">
<el-carousel height="240px" indicator-position="outside" :interval="3600"> <el-carousel height="240px" indicator-position="outside" :interval="3600">
<el-carousel-item v-for="slide in banners" :key="slide.title || slide.image_url"> <el-carousel-item v-for="slide in banners" :key="slide.title || slide.image_url">
<div class="hero-slide" :class="[`tone-${slide.tone}`, { 'has-image': slide.image_url }]"> <div
class="hero-slide"
:class="[
`tone-${slide.tone}`,
{ 'has-image': slide.image_url, 'is-previewable': !!slide.image_url },
]"
@click="openPreview(slide)"
>
<img <img
v-if="slide.image_url" v-if="slide.image_url"
:src="slide.image_url" :src="slide.image_url"
@@ -34,6 +65,15 @@ defineProps<Props>()
</div> </div>
</el-carousel-item> </el-carousel-item>
</el-carousel> </el-carousel>
<el-image-viewer
v-if="previewVisible"
:url-list="previewUrls"
:initial-index="previewIndex"
teleported
hide-on-click-modal
@close="closePreview"
/>
</section> </section>
</template> </template>
@@ -69,6 +109,10 @@ defineProps<Props>()
background: linear-gradient(135deg, #eff6ff 0%, #ffffff 58%, #fff7ed 100%); background: linear-gradient(135deg, #eff6ff 0%, #ffffff 58%, #fff7ed 100%);
} }
.hero-slide.is-previewable {
cursor: zoom-in;
}
.hero-slide.tone-blue { .hero-slide.tone-blue {
background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 56%, #fff7ed 100%); background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 56%, #fff7ed 100%);
} }
@@ -250,6 +250,10 @@
color: #fff; color: #fff;
} }
.mobile-banner.is-previewable {
cursor: zoom-in;
}
.mobile-banner.tone-warm { .mobile-banner.tone-warm {
background: #9a3412; background: #9a3412;
} }
@@ -21,7 +21,7 @@ import {
type AWRecycleConfig, type AWRecycleConfig,
type HomeBannerSlide, type HomeBannerSlide,
} from '@/features/listings/api/homeConfig' } from '@/features/listings/api/homeConfig'
import { showToast } from 'vant' import { showImagePreview, showToast } from 'vant'
import AuthImage from '@/shared/components/business/AuthImage.vue' import AuthImage from '@/shared/components/business/AuthImage.vue'
import MobileHomeFilterSheet, { type FilterSection } from './MobileHomeFilterSheet.vue' import MobileHomeFilterSheet, { type FilterSection } from './MobileHomeFilterSheet.vue'
import { import {
@@ -485,6 +485,21 @@ function openAWRecycle() {
awRecyclePreviewVisible.value = true awRecyclePreviewVisible.value = true
} }
function openBannerPreview(slide: HomeBannerSlide) {
const url = slide.image_url?.trim()
if (!url) return
const images = bannerSlides.value
.map(item => item.image_url?.trim() || '')
.filter((item): item is string => Boolean(item))
const startPosition = images.indexOf(url)
if (startPosition < 0) return
showImagePreview({
images,
startPosition,
closeable: true,
})
}
async function onRefresh() { async function onRefresh() {
refreshing.value = true refreshing.value = true
try { try {
@@ -830,7 +845,11 @@ syncMobileHomeQuery()
<van-swipe-item v-for="slide in bannerSlides" :key="slide.title || slide.image_url"> <van-swipe-item v-for="slide in bannerSlides" :key="slide.title || slide.image_url">
<div <div
class="mobile-banner" class="mobile-banner"
:class="[`tone-${slide.tone}`, { 'has-image': slide.image_url }]" :class="[
`tone-${slide.tone}`,
{ 'has-image': slide.image_url, 'is-previewable': !!slide.image_url },
]"
@click="openBannerPreview(slide)"
> >
<img <img
v-if="slide.image_url" v-if="slide.image_url"