From bd502bfa63b9b69d842e649c37a9367a69eb6b65 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Mon, 5 Oct 2026 15:02:37 +0800 Subject: [PATCH] =?UTF-8?q?fix(listings):=20=E6=89=8B=E6=9C=BA=E8=B6=85?= =?UTF-8?q?=E5=AE=BD=E8=BD=AE=E6=92=AD=E6=94=B9=E4=B8=BA=E5=8F=AF=E6=A8=AA?= =?UTF-8?q?=E6=BB=91=E5=B1=95=E7=A4=BA=E5=92=8C=E5=A4=A7=E5=9B=BE=E9=A2=84?= =?UTF-8?q?=E8=A7=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 分站海报比例很宽,整图塞进轮播会缩成一条,点开预览也看不清。首页按固定高度横滑查看,预览用大图灯箱代替默认图片预览。 Co-authored-by: Cursor --- .../listings/views/MobileHomeView.css | 132 +++++++++++++++- .../listings/views/MobileHomeView.vue | 143 ++++++++++++++++-- 2 files changed, 258 insertions(+), 17 deletions(-) diff --git a/frontend/src/features/listings/views/MobileHomeView.css b/frontend/src/features/listings/views/MobileHomeView.css index 19ae8d0..81be472 100644 --- a/frontend/src/features/listings/views/MobileHomeView.css +++ b/frontend/src/features/listings/views/MobileHomeView.css @@ -328,13 +328,135 @@ object-fit: cover; } -.mobile-banner.has-image:not(.has-copy) { - background: #0f172a; +.banner-swipe.is-poster { + height: 168px; + background: #eef3f8; } -.mobile-banner.has-image:not(.has-copy) .banner-image { - object-fit: contain; - background: #0f172a; +.mobile-banner.is-poster { + height: 168px; + min-height: 0; + padding: 0; + background: #eef3f8; +} + +.poster-scroller { + height: 100%; + overflow-x: auto; + overflow-y: hidden; + -webkit-overflow-scrolling: touch; + scrollbar-width: none; +} + +.poster-scroller::-webkit-scrollbar { + display: none; +} + +.mobile-banner.is-poster .banner-image { + position: static; + display: block; + width: auto; + max-width: none; + height: 168px; + object-fit: unset; + background: transparent; +} + +.mobile-banner.is-poster .banner-badge { + display: none; +} + +.banner-step-row { + display: flex; + justify-content: flex-end; + gap: 8px; + margin: -4px 0 12px; +} + +.banner-step-row button { + border: 0; + padding: 4px 10px; + border-radius: 999px; + background: #e8eef5; + color: #334155; + font-size: 12px; + font-weight: 700; +} + +.banner-lightbox { + position: fixed; + inset: 0; + z-index: 3000; + display: flex; + flex-direction: column; + justify-content: center; + background: rgba(8, 12, 20, 0.94); + color: #fff; +} + +.banner-lightbox-close, +.banner-lightbox-nav button { + border: 0; + background: rgba(255, 255, 255, 0.14); + color: #fff; + font-size: 14px; + font-weight: 700; +} + +.banner-lightbox-close { + position: absolute; + top: calc(12px + env(safe-area-inset-top)); + right: 12px; + z-index: 1; + padding: 8px 14px; + border-radius: 999px; +} + +.banner-lightbox-count, +.banner-lightbox-hint { + margin: 0; + text-align: center; +} + +.banner-lightbox-count { + position: absolute; + top: calc(18px + env(safe-area-inset-top)); + left: 0; + right: 0; + font-size: 14px; + font-weight: 700; +} + +.banner-lightbox-scroller { + overflow-x: auto; + overflow-y: hidden; + -webkit-overflow-scrolling: touch; +} + +.banner-lightbox-scroller img { + display: block; + width: auto; + max-width: none; + height: 58vh; + margin: 0 auto; +} + +.banner-lightbox-hint { + margin-top: 14px; + color: rgba(255, 255, 255, 0.72); + font-size: 12px; +} + +.banner-lightbox-nav { + display: flex; + justify-content: center; + gap: 12px; + margin-top: 14px; +} + +.banner-lightbox-nav button { + padding: 8px 16px; + border-radius: 999px; } .mobile-banner.has-image.has-copy::after { diff --git a/frontend/src/features/listings/views/MobileHomeView.vue b/frontend/src/features/listings/views/MobileHomeView.vue index 700b02e..0716a75 100644 --- a/frontend/src/features/listings/views/MobileHomeView.vue +++ b/frontend/src/features/listings/views/MobileHomeView.vue @@ -23,7 +23,7 @@ import { type CooperationFeedbackConfig, type HomeBannerSlide, } from '@/features/listings/api/homeConfig' -import { showImagePreview, showToast } from 'vant' +import { showToast } from 'vant' import AuthImage from '@/shared/components/business/AuthImage.vue' import MobileHomeFilterSheet, { type FilterSection } from './MobileHomeFilterSheet.vue' import { @@ -103,6 +103,15 @@ const displayBanners = computed(() => { } return bannerSlides.value }) +const bannerSwipeRef = ref<{ prev: () => void; next: () => void } | null>(null) +const bannerPreviewVisible = ref(false) +const bannerPreviewIndex = ref(0) +const bannerPreviewScroller = ref(null) +const bannerPreviewImages = computed(() => + displayBanners.value + .map(slide => slide.image_url?.trim() || '') + .filter((url): url is string => Boolean(url)) +) const awRecycle = ref({ ...defaultAWRecycleConfig }) const awRecyclePreviewVisible = ref(false) const cooperationFeedback = ref({ ...defaultCooperationFeedbackConfig }) @@ -396,6 +405,7 @@ onBeforeRouteLeave(() => { onBeforeUnmount(() => { persistScrollTop() + document.body.style.overflow = '' if (searchDebounceTimer) clearTimeout(searchDebounceTimer) }) @@ -510,21 +520,68 @@ function openCooperationFeedback() { cooperationFeedbackPreviewVisible.value = true } +let posterDragged = false + +function centerWideImage(event: Event) { + const img = event.target as HTMLImageElement + const scroller = img.parentElement + if (!scroller) return + scroller.scrollLeft = Math.max(0, (scroller.scrollWidth - scroller.clientWidth) / 2) +} + +function onPosterTouchStart() { + posterDragged = false +} + +function onPosterTouchMove() { + posterDragged = true +} + +function onPosterClick(slide: HomeBannerSlide) { + if (posterDragged) { + posterDragged = false + return + } + openBannerPreview(slide) +} + +function stepBanner(step: number) { + if (step < 0) bannerSwipeRef.value?.prev() + else bannerSwipeRef.value?.next() +} + +function centerBannerPreview() { + const scroller = bannerPreviewScroller.value + if (!scroller) return + scroller.scrollLeft = Math.max(0, (scroller.scrollWidth - scroller.clientWidth) / 2) +} + function openBannerPreview(slide: HomeBannerSlide) { const url = slide.image_url?.trim() if (!url) return - const images = displayBanners.value - .map(item => item.image_url?.trim() || '') - .filter((item): item is string => Boolean(item)) - const startPosition = images.indexOf(url) + const startPosition = bannerPreviewImages.value.indexOf(url) if (startPosition < 0) return - showImagePreview({ - images, - startPosition, - closeable: true, - }) + bannerPreviewIndex.value = startPosition + bannerPreviewVisible.value = true } +function closeBannerPreview() { + bannerPreviewVisible.value = false +} + +function shiftBannerPreview(step: number) { + const total = bannerPreviewImages.value.length + if (total <= 1) return + bannerPreviewIndex.value = (bannerPreviewIndex.value + step + total) % total +} + +watch([bannerPreviewVisible, bannerPreviewIndex], async ([open]) => { + document.body.style.overflow = open ? 'hidden' : '' + if (!open) return + await nextTick() + centerBannerPreview() +}) + function hasBannerCopy(slide: HomeBannerSlide) { return Boolean(slide.eyebrow?.trim() || slide.title?.trim() || slide.pill?.trim()) } @@ -915,7 +972,15 @@ if (!restoreHomeConfigFromCache()) { -