| | |
| | | |
| | | <scroll-view class="page-scroll" scroll-y lower-threshold="80" @scroll="handlePageScroll" @scrolltolower="loadMoreMerchants"> |
| | | <view class="hero"> |
| | | <image class="hero-image" :src="currentImage" mode="aspectFill" @error="replaceTopicImage"></image> |
| | | <!-- 主图跟着图册左右滑:滑动的主图与下方缩略图互相联动(点缩略图 = 主图滑过去)。 --> |
| | | <swiper v-if="gallery.length > 0" class="hero-swiper" :current="activeIndex < 0 ? 0 : activeIndex" :circular="gallery.length > 1" :duration="240" @change="handleHeroChange"> |
| | | <swiper-item v-for="(item, index) in gallery" :key="item.id"> |
| | | <image class="hero-image" :src="item.image" mode="aspectFill" @error="replaceGalleryImage(index)"></image> |
| | | </swiper-item> |
| | | </swiper> |
| | | <image v-else class="hero-image" :src="currentImage" mode="aspectFill" @error="replaceTopicImage"></image> |
| | | <view class="hero-top-shade"></view> |
| | | <view class="hero-bottom-shade"></view> |
| | | |
| | |
| | | <image class="merchant-image" :src="item.image" mode="aspectFill" @error="replaceMerchantImage(item)"></image> |
| | | <view class="merchant-info"> |
| | | <text class="merchant-name">{{item.name}}</text> |
| | | <view class="tag-row"><text class="coupon-tag">券</text><text class="discount-tag">{{item.available ? item.discount : '已下架'}}</text></view> |
| | | <view v-if="!item.available" class="tag-row"><text class="discount-tag">已下架</text></view> |
| | | <view v-else-if="item.tags.length > 0" class="tag-row"><text v-for="tag in item.tags" :key="tag" :class="tag == '券' ? 'coupon-tag' : 'discount-tag'">{{tag}}</text></view> |
| | | <view class="address-row"><image src="/static/benefits-assets/map-pin.svg"></image><text>{{item.address}}</text></view> |
| | | </view> |
| | | </view> |
| | |
| | | import { isApiConfigured } from '../../config/api.uts' |
| | | import { getFoodTopic, getFoodTopicMerchants } from '../../services/portal.uts' |
| | | import { resolveApiUrl } from '../../services/http.uts' |
| | | import { resolveTalentId } from '../../utils/talent-id.uts' |
| | | import { safeBack } from '../../utils/navigation.uts' |
| | | |
| | | const navTop = ref(28) |
| | |
| | | const activeIndex = ref(-1) |
| | | const windowWidth = ref(375) |
| | | const thumbScrollLeft = ref(0) |
| | | const fallbackFoodImage = '/static/food-assets/food-1.jpg' |
| | | const fallbackFoodImage = '/static/common/image-placeholder.png' |
| | | const topicDescription = ref('') |
| | | |
| | | const gallery = ref<any[]>([]) |
| | |
| | | const failLoadMoreOnce = ref(false) |
| | | const currentPage = ref(0) |
| | | const listRequestVersion = ref(0) |
| | | const talentId = ref('') |
| | | const readListTags = (item: any, hasCoupon: boolean, discount: string): string[] => { |
| | | const tags: string[] = [] |
| | | if (item != null && item.benefitTags instanceof Array) { |
| | | const records: any[] = item.benefitTags as any[] |
| | | for (let index = 0; index < records.length; index++) { |
| | | const tag = String(records[index]).trim() |
| | | if (tag != '' && tag != '人才专享' && tags.indexOf(tag) < 0) tags.push(tag) |
| | | } |
| | | return tags |
| | | } |
| | | if (hasCoupon) tags.push('券') |
| | | if (discount != '' && discount != '人才专享') tags.push(discount) |
| | | return tags |
| | | } |
| | | |
| | | onLoad((options: any) => { |
| | | // #ifdef MP-WEIXIN |
| | |
| | | failInitialOnce.value = previewState == 'initialError' |
| | | failLoadMoreOnce.value = previewState == 'loadMoreError' |
| | | } |
| | | talentId.value = resolveTalentId() |
| | | loadFoodTopic() |
| | | loadInitialMerchants() |
| | | }) |
| | |
| | | if (index >= gallery.value.length) index = gallery.value.length - 1 |
| | | selectGallery(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 |
| | | selectGallery(current) |
| | | } |
| | | const handlePageScroll = (event: any) => { |
| | | const shouldShow = event.detail.scrollTop > 64 |
| | | if (isSolidNav.value != shouldShow) isSolidNav.value = shouldShow |
| | |
| | | loadMoreError.value = true |
| | | return |
| | | } |
| | | getFoodTopicMerchants(currentPage.value + 1, 4, (data: any) => { |
| | | getFoodTopicMerchants(currentPage.value + 1, 4, talentId.value, (data: any) => { |
| | | if (requestVersion != listRequestVersion.value) return |
| | | const records: any[] = data != null && data.records instanceof Array ? data.records as any[] : [] |
| | | const next = append ? visibleMerchants.value.slice() : [] |
| | | for (let index = 0; index < records.length; index++) { |
| | | const item = records[index] |
| | | const hasCoupon = item.couponAvailable === true || Number(item.couponAvailable) == 1 |
| | | const discount = item.discountText != null && item.discountText != '人才专享' ? item.discountText as string : '' |
| | | next.push({ |
| | | id: '' + item.id, |
| | | name: item.merchantName != null ? item.merchantName as string : '餐饮商户', |
| | | merchantType: item.merchantType != null ? item.merchantType as string : 'food', |
| | | discount: item.discountText != null ? item.discountText as string : '人才专享', |
| | | discount: discount, |
| | | hasCoupon: hasCoupon, |
| | | tags: readListTags(item, hasCoupon, discount), |
| | | address: item.address != null ? item.address as string : '', |
| | | image: item.coverUrl != null && item.coverUrl != '' ? resolveApiUrl(item.coverUrl as string) : fallbackFoodImage, |
| | | available: item.available !== false |
| | |
| | | .page { width: 100%; height: 100vh; background-color: #FFFFFF; } |
| | | .page-scroll { width: 100%; height: 100vh; background-color: #FFFFFF; } |
| | | .hero { height: 730rpx; position: relative; z-index: 2; overflow: visible; background-color: #332F29; } |
| | | .hero-swiper { position: absolute; left: 0; top: 0; width: 100%; height: 730rpx; } |
| | | .hero-image { position: absolute; left: 0; top: 0; width: 100%; height: 730rpx; } |
| | | .hero-top-shade { position: absolute; left: 0; right: 0; top: 0; height: 230rpx; background-image: linear-gradient(180deg, rgba(0,0,0,.62) 0%, rgba(0,0,0,.18) 65%, rgba(0,0,0,0) 100%); } |
| | | .hero-bottom-shade { position: absolute; left: 0; right: 0; bottom: 0; height: 230rpx; background-image: linear-gradient(180deg, rgba(0,0,0,0) 0%, rgba(0,0,0,.45) 100%); } |
| | |
| | | .merchant-info { display: flex; min-width: 0; min-height: 184rpx; flex: 1; box-sizing: border-box; padding: 4rpx 0 4rpx 24rpx; flex-direction: column; justify-content: space-between; } |
| | | .merchant-name { color: #292929; font-size: 29rpx; line-height: 40rpx; font-weight: 700; } |
| | | .tag-row { display: flex; flex-direction: row; align-items: center; } |
| | | .tag-row text:first-child { margin-left: 0; } |
| | | .coupon-tag { padding: 6rpx 8rpx; border-radius: 6rpx; background-color: #FFF0EC; color: #FF5D37; font-size: 21rpx; } |
| | | .discount-tag { margin-left: 11rpx; padding: 6rpx 9rpx; border-radius: 6rpx; background-color: #FFF5E7; color: #FF7C2B; font-size: 21rpx; } |
| | | .address-row { 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; } |