145 lines
3.6 KiB
Vue
145 lines
3.6 KiB
Vue
<template>
|
||
<view class="banner-carousel" @click.stop>
|
||
<!-- 骨架屏:加载中或首次无数据时显示 -->
|
||
<view v-if="showSkeleton" class="banner-skeleton">
|
||
<view class="skeleton-banner"></view>
|
||
</view>
|
||
|
||
<swiper v-else class="banner-swiper" :autoplay="true" :interval="4000" :duration="400" :circular="true"
|
||
:indicator-dots="false" @change="onSwiperChange">
|
||
<!-- <swiper-item @click.stop="$emit('top3Click')" style="display: flex;
|
||
align-items: center;">
|
||
<BannerTop3 @dataLoaded="onTop3DataLoaded" @top3Click="$emit('top3Click')" />
|
||
</swiper-item> -->
|
||
<swiper-item v-for="item in bannerActivities" :key="item.id" @tap.stop="$emit('activityClick', item)">
|
||
<image class="banner-activity-img" :src="item.cover_image || '/static/avatar/1.jpeg'" mode="aspectFill" />
|
||
</swiper-item>
|
||
<swiper-item v-for="(banner, index) in banners" :key="banner.id || index" @click="emit('bannerClick', banner)">
|
||
<image class="banner-image" :src="banner.image_url" mode="widthFill"></image>
|
||
<!-- <view class="banner-overlay">
|
||
<text class="banner-title">{{ banner.title }}</text>
|
||
</view> -->
|
||
</swiper-item>
|
||
</swiper>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup>
|
||
import { ref, computed } from 'vue'
|
||
import BannerTop3 from '../../components/BannerTop3.vue'
|
||
|
||
const props = defineProps({
|
||
bannerActivities: { type: Array, default: () => [] },
|
||
// 运营轮播图列表
|
||
banners: {
|
||
type: Array,
|
||
default: () => []
|
||
},
|
||
// 是否处于加载态(父组件 useBanner 提供)
|
||
loading: { type: Boolean, default: false }
|
||
})
|
||
const emit = defineEmits(['activityClick', 'top3Click', 'bannerClick'])
|
||
|
||
const currentIndex = ref(0)
|
||
|
||
const onSwiperChange = (e) => {
|
||
currentIndex.value = e.detail.current
|
||
}
|
||
|
||
const onTop3DataLoaded = (items) => {
|
||
// 数据已由 BannerTop3 内部处理,这里可以做一些额外操作
|
||
console.log('[BannerCarousel] top3 data loaded', items)
|
||
}
|
||
|
||
// 骨架屏:加载中 或 首次加载结束但 banner 全部为空时显示
|
||
const showSkeleton = computed(() => {
|
||
if (props.loading) return true
|
||
const total = (props.bannerActivities?.length || 0) + (props.banners?.length || 0)
|
||
return total === 0
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.banner-carousel {
|
||
position: relative;
|
||
/* width: 100%; */
|
||
z-index: 100;
|
||
padding: 0 16rpx;
|
||
box-sizing: border-box;
|
||
/* top:16rpx; */
|
||
}
|
||
|
||
.banner-swiper {
|
||
width: 100%;
|
||
height: 392rpx;
|
||
border-radius: 24rpx;
|
||
position: relative;
|
||
/* bottom: 24rpx; */
|
||
}
|
||
|
||
.banner-activity-img {
|
||
width: 100%;
|
||
height: 344rpx;
|
||
display: block;
|
||
border-radius: 24rpx;
|
||
position: relative;
|
||
/* top: 24rpx; */
|
||
}
|
||
|
||
.banner-image {
|
||
width: 100%;
|
||
height: 376rpx;
|
||
display: block;
|
||
border-radius: 24rpx;
|
||
position: relative;
|
||
bottom: 16rpx;
|
||
}
|
||
|
||
.banner-overlay {
|
||
position: absolute;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
padding: 24rpx;
|
||
background: linear-gradient(to top, rgba(0, 0, 0, 0.6), transparent);
|
||
}
|
||
|
||
.banner-title {
|
||
font-size: 28rpx;
|
||
color: #fff;
|
||
font-weight: 600;
|
||
}
|
||
|
||
/* 骨架屏 */
|
||
.banner-skeleton {
|
||
width: 100%;
|
||
height: 392rpx;
|
||
border-radius: 24rpx;
|
||
overflow: hidden;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.skeleton-banner {
|
||
width: 100%;
|
||
height: 100%;
|
||
border-radius: 24rpx;
|
||
background: linear-gradient(
|
||
90deg,
|
||
rgba(255, 255, 255, 0.1) 25%,
|
||
rgba(255, 255, 255, 0.25) 50%,
|
||
rgba(255, 255, 255, 0.1) 75%
|
||
);
|
||
background-size: 200% 100%;
|
||
animation: banner-skeleton-shimmer 1.5s infinite;
|
||
}
|
||
|
||
@keyframes banner-skeleton-shimmer {
|
||
0% {
|
||
background-position: 200% 0;
|
||
}
|
||
100% {
|
||
background-position: -200% 0;
|
||
}
|
||
}
|
||
</style>
|