From 5ce0d76154bf4c1d70543476cdfad9a30195b0f8 Mon Sep 17 00:00:00 2001
From: shinnosuke <tantao@1hoper.com>
Date: 星期六, 10 十月 2026 16:44:38 +0800
Subject: [PATCH] fix(miniapp): 调整首页圆角布局并隐藏滚动条
---
rencaika/pages/entertainment-benefits/entertainment-benefits.uvue | 37 +++++++++++++++++++++++++++----------
1 files changed, 27 insertions(+), 10 deletions(-)
diff --git a/rencaika/pages/entertainment-benefits/entertainment-benefits.uvue b/rencaika/pages/entertainment-benefits/entertainment-benefits.uvue
index 07baa49..ecd92aa 100644
--- a/rencaika/pages/entertainment-benefits/entertainment-benefits.uvue
+++ b/rencaika/pages/entertainment-benefits/entertainment-benefits.uvue
@@ -4,19 +4,24 @@
<view class="nav-bg"></view>
<view class="safe-space" :style="'height:' + navTop + 'px'"></view>
<view class="nav-row" :style="'height:' + navHeight + 'px;padding-right:' + navRight + 'px'">
- <view class="back-button" @tap="goBack"><image src="/static/food-assets/arrow-left-white.svg" mode="aspectFit"></image></view>
+ <view class="back-button" hover-class="back-button-active" @tap.stop="goBack"><image src="/static/food-assets/arrow-left-white.svg" mode="aspectFit"></image></view>
<text class="nav-title">娱—怡乐生活</text>
<view class="capsule-space" :style="'width:' + navWidth + 'px'"></view>
</view>
</view>
- <scroll-view class="page-scroll" scroll-y lower-threshold="80" @scroll="handleScroll" @scrolltolower="loadMore">
+ <scroll-view :show-scrollbar="false" class="page-scroll" scroll-y lower-threshold="80" @scroll="handleScroll" @scrolltolower="loadMore">
<view class="hero">
- <image class="hero-image" :src="currentScene.image" mode="aspectFill" @error="replaceHeroImage"></image>
+ <swiper v-if="scenes.length > 0" class="hero-swiper" :current="activeIndex < 0 ? 0 : activeIndex" :circular="scenes.length > 1" :duration="240" @change="handleHeroChange">
+ <swiper-item v-for="(item, index) in scenes" :key="item.id">
+ <image class="hero-image" :src="item.image" mode="aspectFill" @error="replaceSceneImage(index)"></image>
+ </swiper-item>
+ </swiper>
+ <image v-else class="hero-image" :src="currentScene.image" mode="aspectFill" @error="replaceHeroImage"></image>
<view class="hero-shade"></view>
<text v-if="currentScene.name != ''" class="scene-name">{{currentScene.name}}</text>
<text v-if="scenes.length == 0" class="no-gallery">暂无图册内容</text>
- <scroll-view v-else class="scene-scroll" scroll-x scroll-with-animation :scroll-left="sceneScrollLeft" @scrollend="handleSceneScrollEnd">
+ <scroll-view :show-scrollbar="false" v-else class="scene-scroll" scroll-x scroll-with-animation :scroll-left="sceneScrollLeft" @scrollend="handleSceneScrollEnd">
<view class="scene-row" :style="'width:' + sceneTrackWidth + 'rpx'"><view class="scene-pad"></view><view v-for="(item, index) in scenes" :key="item.id" class="scene-item" :class="index == activeIndex ? 'active' : ''" @tap="selectScene(index)"><image :src="item.image" mode="aspectFill" @error="replaceSceneImage(index)"></image></view><view class="scene-pad"></view></view>
</scroll-view>
</view>
@@ -26,7 +31,7 @@
<view v-if="initialLoading" class="list-state"><view class="loading-spinner"></view><text>正在加载观影服务</text></view>
<view v-else-if="initialError" class="list-state error-state"><text>观影服务加载失败</text><text class="retry-text" @tap="retryInitialLoad">重新加载</text></view>
<view v-else-if="hasLoaded && visibleServices.length == 0" class="list-state"><text>暂无相关观影服务</text></view>
- <view v-else-if="hasLoaded"><view class="service-item" :class="item.available ? '' : 'is-unavailable'" v-for="item in visibleServices" :key="item.id" @tap="openService(item)"><image class="service-image" :src="item.image" mode="aspectFill" @error="replaceServiceImage(item)"></image><view class="service-info"><text class="service-name">{{item.name}}</text><text class="discount-tag">{{item.available ? item.discount : '已下架'}}</text><view class="address"><image src="/static/benefits-assets/map-pin.svg" mode="aspectFit"></image><text>{{item.address}}</text></view></view></view></view>
+ <view v-else-if="hasLoaded"><view class="service-item" :class="item.available ? '' : 'is-unavailable'" v-for="item in visibleServices" :key="item.id" @tap="openService(item)"><image class="service-image" :src="item.image" mode="aspectFill" @error="replaceServiceImage(item)"></image><view class="service-info"><text class="service-name">{{item.name}}</text><text v-if="!item.available || item.hasCoupon" class="discount-tag">{{item.available ? '券' : '已下架'}}</text><view class="address"><image src="/static/benefits-assets/map-pin.svg" mode="aspectFit"></image><text>{{item.address}}</text></view></view></view></view>
<view v-if="loadingMore" class="list-state compact"><view class="loading-spinner"></view><text>正在加载</text></view>
<view v-else-if="loadMoreError" class="list-state compact load-error" @tap="retryLoadMore"><text>加载失败,点击重试</text></view>
<view v-else-if="hasLoaded && !hasMore && visibleServices.length > 0" class="no-more"><text>已经到底了</text></view>
@@ -48,7 +53,7 @@
const navRight = ref(10)
const isSolidNav = ref(false)
const windowWidth = ref(375)
- const fallbackImage = '/static/entertainment-assets/entertainment-hero.webp'
+ const fallbackImage = ''
const activeIndex = ref(-1)
const sceneScrollLeft = ref(0)
const sceneTrackWidth = ref(644)
@@ -126,6 +131,12 @@
if (index >= scenes.value.length) index = scenes.value.length - 1
selectScene(index)
}
+ /** 主图左右滑动:滑动落点同步到缩略图、名称与描述。 */
+ const handleHeroChange = (event: any) => {
+ const current = event != null && event.detail != null && event.detail.current != null ? Number(event.detail.current) : -1
+ if (current < 0) return
+ selectScene(current)
+ }
const handleScroll = (event: any) => { isSolidNav.value = event.detail.scrollTop > 64 }
const loadServices = (append: boolean) => {
if (!isApiConfigured()) { initialLoading.value = false; loadingMore.value = false; initialError.value = true; return }
@@ -138,7 +149,8 @@
const next = append ? visibleServices.value.slice() : []
for (let index = 0; index < records.length; index++) {
const item = records[index]
- next.push({ id: '' + item.id, merchantType: item.merchantType != null ? item.merchantType as string : 'cinema', name: item.merchantName != null ? item.merchantName as string : '观影服务', discount: item.discountText != null ? item.discountText as string : '人才专享', address: item.address != null ? item.address as string : '', image: item.coverUrl != null && item.coverUrl != '' ? resolveApiUrl(item.coverUrl as string) : fallbackImage, available: item.available !== false })
+ const hasCoupon = item.couponAvailable === true || Number(item.couponAvailable) == 1 || String(item.discountText).trim() == '券'
+ next.push({ id: '' + item.id, merchantType: item.merchantType != null ? item.merchantType as string : 'cinema', name: item.merchantName != null ? item.merchantName as string : '观影服务', discount: hasCoupon ? '券' : '', hasCoupon: hasCoupon, address: item.address != null ? item.address as string : '', image: item.coverUrl != null && item.coverUrl != '' ? resolveApiUrl(item.coverUrl as string) : fallbackImage, available: item.available !== false })
}
visibleServices.value = next
currentPage.value++
@@ -166,20 +178,25 @@
const goBack = () => safeBack('/pages/benefits/benefits')
const openService = (item: any) => {
if (!item.available) { uni.showToast({ title: '该服务已下架', icon: 'none' }); return }
- if (item.merchantType != 'cinema') { uni.showToast({ title: '剧院服务详情暂未开放', icon: 'none' }); return }
+ const cinemaTypes = ['cinema', 'theatre', '观影服务', '电影院', '影院', '影城', '剧院']
+ if (cinemaTypes.indexOf(item.merchantType) < 0) { uni.showToast({ title: '剧院服务详情暂未开放', icon: 'none' }); return }
uni.navigateTo({ url: '/pages/cinema-merchant/cinema-merchant?merchantId=' + encodeURIComponent(item.id) + '&name=' + encodeURIComponent(item.name) + '&image=' + encodeURIComponent(item.image) + '&address=' + encodeURIComponent(item.address) + '&discount=' + encodeURIComponent(item.discount) })
}
</script>
<style>
+ /* 顶部大图标题用行楷:优先 App.uvue 动态加载的服务器完整字体,回退包内子集与系统字体。 */
+ .scene-name { font-family: 'FoodCalligraphyFull', 'FoodCalligraphy'; }
.page, .page-scroll { width: 100%; height: 100vh; background-color: #FFFFFF; }
.custom-nav { position: fixed; z-index: 20; left: 0; right: 0; top: 0; padding-bottom: 20rpx; }
.nav-bg { position: absolute; left: 0; right: 0; top: 0; bottom: 0; opacity: 0; background-image: linear-gradient(180deg, #F26A38 0%, #EE542B 100%); transition: opacity 280ms ease-out; }
.custom-nav.is-solid .nav-bg { opacity: 1; }.safe-space, .nav-row { position: relative; z-index: 1; }.safe-space { height: 28px; }
- .nav-row { display: flex; height: 32px; box-sizing: border-box; padding-left: 24rpx; padding-right: 10px; flex-direction: row; align-items: center; justify-content: space-between; }.back-button { display: flex; width: 54rpx; height: 54rpx; border-radius: 50%; background-color: rgba(45,45,45,.52); justify-content: center; align-items: center; }.custom-nav.is-solid .back-button { background-color: rgba(45,45,45,0); }.back-button image { width: 38rpx; height: 38rpx; }.nav-title { position: absolute; left: 0; right: 0; color: #FFFFFF; font-size: 31rpx; font-weight: 500; text-align: center; text-shadow: 0 2rpx 8rpx rgba(0,0,0,.35); }.capsule-space { width: 88px; height: 100%; flex-shrink: 0; }
+ .nav-row { display: flex; height: 32px; box-sizing: border-box; padding-left: 20rpx; padding-right: 10px; position: relative; flex-direction: row; align-items: center; justify-content: space-between; }.back-button { display: flex; width: 72rpx; height: 64rpx; position: relative; z-index: 4; margin-left: -4rpx; border-radius: 50%; background-color: rgba(45,45,45,.52); justify-content: center; align-items: center; }.back-button-active { opacity: .55; }.custom-nav.is-solid .back-button { background-color: rgba(45,45,45,0); }.back-button image { width: 38rpx; height: 38rpx; pointer-events: none; }.nav-title { position: absolute; z-index: 2; left: 0; right: 0; color: #FFFFFF; font-size: 31rpx; font-weight: 500; text-align: center; text-shadow: 0 2rpx 8rpx rgba(0,0,0,.35); pointer-events: none; }.nav-bg { pointer-events: none; }.capsule-space { width: 88px; height: 100%; flex-shrink: 0; }
.hero { height: 730rpx; position: relative; overflow: visible; background-color: #241C2A; }.hero-image { position: absolute; left: 0; top: 0; width: 100%; height: 730rpx; }.hero-shade { position: absolute; left: 0; right: 0; top: 0; height: 360rpx; background-image: linear-gradient(180deg, rgba(0,0,0,.6) 0%, rgba(0,0,0,0) 100%); }.scene-name { position: absolute; z-index: 2; left: 38rpx; top: 300rpx; color: #FFFFFF; font-size: 47rpx; line-height: 62rpx; font-weight: 500; text-shadow: 0 3rpx 10rpx rgba(0,0,0,.55); }.no-gallery { position: absolute; z-index: 2; left: 0; right: 0; top: 480rpx; color: #FFFFFF; font-size: 25rpx; text-align: center; }
.scene-scroll { position: absolute; z-index: 4; left: 0; right: 0; top: 469rpx; width: 100%; height: 150rpx; white-space: nowrap; }.scene-row { display: flex; height: 150rpx; padding: 0; flex-direction: row; align-items: center; }.scene-pad { width: 322rpx; height: 1rpx; flex-shrink: 0; }.scene-item { display: flex; width: 106rpx; height: 106rpx; margin-right: 31rpx; box-sizing: border-box; overflow: hidden; position: relative; z-index: 1; border-width: 3rpx; border-style: solid; border-color: rgba(255,255,255,.9); border-radius: 8rpx; flex-shrink: 0; transition: transform 180ms ease-out; }.scene-item image { width: 100%; height: 100%; }.scene-item.active { z-index: 3; border-width: 4rpx; border-color: #FFD329; transform: scale(1.28); box-shadow: 0 8rpx 18rpx rgba(0,0,0,.28); }
.topic-summary { position: relative; z-index: 4; margin-top: -66rpx; }.intro-card { margin: 0 31rpx; box-sizing: border-box; padding: 25rpx 31rpx; border-radius: 26rpx; background-image: linear-gradient(90deg, #FF5320 0%, #FF8A22 100%); box-shadow: 0 10rpx 22rpx rgba(255,104,28,.24); }.intro-card text { color: #FFFFFF; font-size: 23rpx; line-height: 34rpx; }
- .service-section { padding: 48rpx 31rpx 0; }.section-heading { display: flex; width: 132rpx; height: 72rpx; flex-direction: column; align-items: center; }.section-heading text { display: block; color: #363636; font-size: 32rpx; font-weight: 700; }.section-heading view { width: 61rpx; height: 7rpx; margin: 12rpx 0 18rpx; border-radius: 4rpx; background-image: linear-gradient(90deg, #FF4D36 0%, #FFBF3A 100%); }.service-item { display: flex; min-height: 205rpx; margin-bottom: 20rpx; flex-direction: row; }.service-item.is-unavailable { opacity: .56; }.service-image { width: 184rpx; height: 184rpx; border-radius: 17rpx; flex-shrink: 0; background-color: #EEE; }.service-info { display: flex; min-width: 0; min-height: 184rpx; flex: 1; padding: 4rpx 0 4rpx 24rpx; flex-direction: column; justify-content: space-between; }.service-name { color: #292929; font-size: 29rpx; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }.discount-tag { display: block; width: fit-content; padding: 6rpx 9rpx; border-radius: 6rpx; background-color: #FFF0EC; color: #FF5D37; font-size: 21rpx; }.address { display: flex; min-width: 0; box-sizing: border-box; padding: 7rpx 10rpx; border-radius: 7rpx; background-image: linear-gradient(90deg, rgba(239,240,242,.98) 0%, rgba(242,243,245,.76) 58%, rgba(255,255,255,0) 100%); flex-direction: row; align-items: flex-start; }.address image { width: 27rpx; height: 27rpx; margin-right: 8rpx; flex-shrink: 0; }.address text { min-width: 0; flex: 1; color: #7E8490; font-size: 21rpx; line-height: 31rpx; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
+ .service-section { padding: 32rpx 31rpx 0; }.section-heading { display: flex; width: 132rpx; height: 72rpx; flex-direction: column; align-items: center; }.section-heading text { display: block; color: #363636; font-size: 32rpx; font-weight: 700; }.section-heading view { width: 61rpx; height: 7rpx; margin: 12rpx 0 18rpx; border-radius: 4rpx; background-image: linear-gradient(90deg, #FF4D36 0%, #FFBF3A 100%); }.service-item { display: flex; min-height: 205rpx; margin-bottom: 20rpx; flex-direction: row; }.service-item.is-unavailable { opacity: .56; }.service-image { width: 184rpx; height: 184rpx; border-radius: 17rpx; flex-shrink: 0; background-color: #EEE; }.service-info { display: flex; min-width: 0; min-height: 184rpx; flex: 1; padding: 4rpx 0 4rpx 24rpx; flex-direction: column; justify-content: space-between; }.service-name { color: #292929; font-size: 29rpx; font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }.discount-tag { display: block; width: fit-content; padding: 6rpx 9rpx; border-radius: 6rpx; background-color: #FFF0EC; color: #FF5D37; font-size: 21rpx; }.address { display: flex; min-width: 0; box-sizing: border-box; padding: 7rpx 10rpx; border-radius: 7rpx; background-image: linear-gradient(90deg, rgba(239,240,242,.98) 0%, rgba(242,243,245,.76) 58%, rgba(255,255,255,0) 100%); flex-direction: row; align-items: flex-start; }.address image { width: 27rpx; height: 27rpx; margin-right: 8rpx; flex-shrink: 0; }.address text { min-width: 0; flex: 1; color: #7E8490; font-size: 21rpx; line-height: 31rpx; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.list-state, .no-more { display: flex; height: 150rpx; color: #989DA5; font-size: 23rpx; justify-content: center; align-items: center; }.list-state.compact { height: 90rpx; }.error-state { flex-direction: column; }.retry-text, .load-error text { margin-top: 18rpx; color: #F26A38; }.loading-spinner { width: 26rpx; height: 26rpx; margin-right: 12rpx; box-sizing: border-box; border: 3rpx solid #FFD6C4; border-top-color: #FF6C2B; border-radius: 50%; animation: entertainment-loading 720ms linear infinite; }@keyframes entertainment-loading { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }.bottom-space { height: calc(30rpx + env(safe-area-inset-bottom)); }
+ /* 主图轮播:与下方缩略图带同高同位,盖在原来的静态主图上。 */
+ .hero-swiper { position: absolute; left: 0; top: 0; width: 100%; height: 730rpx; }
</style>
--
Gitblit v1.7.1