轮播图可以预览

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">
import { computed, ref } from 'vue'
interface HomeBannerSlide {
title?: string
eyebrow?: string
@@ -12,14 +14,43 @@ interface Props {
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>
<template>
<section class="hero-board">
<el-carousel height="240px" indicator-position="outside" :interval="3600">
<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
v-if="slide.image_url"
:src="slide.image_url"
@@ -34,6 +65,15 @@ defineProps<Props>()
</div>
</el-carousel-item>
</el-carousel>
<el-image-viewer
v-if="previewVisible"
:url-list="previewUrls"
:initial-index="previewIndex"
teleported
hide-on-click-modal
@close="closePreview"
/>
</section>
</template>
@@ -69,6 +109,10 @@ defineProps<Props>()
background: linear-gradient(135deg, #eff6ff 0%, #ffffff 58%, #fff7ed 100%);
}
.hero-slide.is-previewable {
cursor: zoom-in;
}
.hero-slide.tone-blue {
background: linear-gradient(135deg, #eff6ff 0%, #dbeafe 56%, #fff7ed 100%);
}
@@ -250,6 +250,10 @@
color: #fff;
}
.mobile-banner.is-previewable {
cursor: zoom-in;
}
.mobile-banner.tone-warm {
background: #9a3412;
}
@@ -21,7 +21,7 @@ import {
type AWRecycleConfig,
type HomeBannerSlide,
} from '@/features/listings/api/homeConfig'
import { showToast } from 'vant'
import { showImagePreview, showToast } from 'vant'
import AuthImage from '@/shared/components/business/AuthImage.vue'
import MobileHomeFilterSheet, { type FilterSection } from './MobileHomeFilterSheet.vue'
import {
@@ -485,6 +485,21 @@ function openAWRecycle() {
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() {
refreshing.value = true
try {
@@ -830,7 +845,11 @@ syncMobileHomeQuery()
<van-swipe-item v-for="slide in bannerSlides" :key="slide.title || slide.image_url">
<div
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
v-if="slide.image_url"