|
|
<!-- 秒杀活动列表 --><template> <s-layout :bgStyle="{ color: 'rgb(245,28,19)' }" navbar="inner"> <!--顶部背景图--> <view class="page-bg" :style="[{ marginTop: '-' + Number(statusBarHeight + 88) + 'rpx' }]" ></view> <!-- 时间段轮播图 --> <view class="header" v-if="activeTimeConfig?.sliderPicUrls?.length > 0"> <swiper indicator-dots="true" autoplay="true" :circular="true" interval="3000" duration="1500" indicator-color="rgba(255,255,255,0.6)" indicator-active-color="#fff" > <block v-for="(picUrl, index) in activeTimeConfig.sliderPicUrls" :key="index"> <swiper-item class="borRadius14"> <image :src="picUrl" class="slide-image borRadius14" lazy-load /> </swiper-item> </block> </swiper> </view> <!-- 时间段列表 --> <view class="flex align-center justify-between ss-p-25"> <!-- 左侧图标 --> <view class="time-icon"> <!-- TODO 芋艿:图片统一维护 --> <image class="ss-w-100 ss-h-100" src="http://mall.yudao.iocoder.cn/static/images/priceTag.png" /> </view> <scroll-view class="time-list" :scroll-into-view="activeTimeElId" scroll-x scroll-with-animation > <view v-for="(config, index) in timeConfigList" :key="index" :class="['item', { active: activeTimeIndex === index }]" :id="`timeItem${index}`" @tap="handleChangeTimeConfig(index, config.id)" > <!-- 活动起始时间 --> <view class="time">{{ config.startTime }}</view> <!-- 活动状态 --> <view class="status">{{ config?.status }}</view> </view> </scroll-view> </view>
<!-- 内容区 --> <view class="list-content"> <!-- 活动倒计时 --> <view class="content-header ss-flex-col ss-col-center ss-row-center"> <view class="content-header-box ss-flex ss-row-center"> <view class="countdown-box ss-flex" v-if="activeTimeConfig?.status === TimeStatusEnum.STARTED" > <view class="countdown-title ss-m-r-12">距结束</view> <view class="ss-flex countdown-time"> <view class="ss-flex countdown-h">{{ countDown.h }}</view> <view class="ss-m-x-4">:</view> <view class="countdown-num ss-flex ss-row-center">{{ countDown.m }}</view> <view class="ss-m-x-4">:</view> <view class="countdown-num ss-flex ss-row-center">{{ countDown.s }}</view> </view> </view> <view v-else> {{ activeTimeConfig?.status }} </view> </view> </view>
<!-- 活动列表 --> <scroll-view class="scroll-box" :style="{ height: pageHeight + 'rpx' }" scroll-y="true" :scroll-with-animation="false" :enable-back-to-top="true" > <view class="goods-box ss-m-b-20" v-for="activity in activityList" :key="activity.id"> <s-goods-column size="lg" :data="{ ...activity, price: activity.seckillPrice }" :goodsFields="goodsFields" :seckillTag="true" > <!-- 抢购进度 --> <template #activity> <view class="limit"> 限量 <text class="ss-m-l-5">{{ activity.stock }} {{ activity.unitName }}</text> </view> <su-progress :percentage="activity.percent" strokeWidth="10" textInside isAnimate /> </template> <!-- 抢购按钮 --> <template #cart> <button :class="[ 'ss-reset-button cart-btn', { disabled: activeTimeConfig?.status === TimeStatusEnum.END }, ]" v-if="activeTimeConfig?.status === TimeStatusEnum.WAIT_START" > <span>未开始</span> </button> <button :class="[ 'ss-reset-button cart-btn', { disabled: activeTimeConfig?.status === TimeStatusEnum.END }, ]" @click="sheep.$router.go('/pages/goods/seckill', { id: activity.id })" v-else-if="activeTimeConfig?.status === TimeStatusEnum.STARTED" > <span>马上抢</span> </button> <button :class="[ 'ss-reset-button cart-btn', { disabled: activeTimeConfig?.status === TimeStatusEnum.END }, ]" v-else > <span>已结束</span> </button> </template> </s-goods-column> </view> <uni-load-more v-if="activityTotal > 0" :status="loadStatus" :content-text="{ contentdown: '上拉加载更多', }" @tap="loadMore" /> </scroll-view> </view> </s-layout></template><script setup> import { computed, nextTick, reactive, ref } from 'vue'; import { onLoad, onReachBottom } from '@dcloudio/uni-app'; import sheep from '@/sheep'; import { useDurationTime } from '@/sheep/hooks/useGoods'; import SeckillApi from '@/sheep/api/promotion/seckill'; import dayjs from 'dayjs'; import { TimeStatusEnum } from '@/sheep/util/const';
// 计算页面高度
const { safeAreaInsets, safeArea } = sheep.$platform.device; const statusBarHeight = sheep.$platform.device.statusBarHeight * 2; const pageHeight = (safeArea.height + safeAreaInsets.bottom) * 2 + statusBarHeight - sheep.$platform.navbar - 350; const headerBg = sheep.$url.css('/static/img/shop/goods/seckill-header.png');
// 商品控件显示的字段(不显示库存、销量。改为显示自定义的进度条)
const goodsFields = { name: { show: true, }, introduction: { show: true, }, price: { show: true, }, marketPrice: { show: true, }, };
//#region 时间段
// 时间段列表
const timeConfigList = ref([]); // 查询时间段
const getSeckillConfigList = async () => { const { data } = await SeckillApi.getSeckillConfigList(); const now = dayjs(); const today = now.format('YYYY-MM-DD'); const select = ref([]); // 判断时间段的状态
data.forEach((config, index) => { const startTime = dayjs(`${today} ${config.startTime}`); const endTime = dayjs(`${today} ${config.endTime}`); select.value[index] = config.id; if (now.isBefore(startTime)) { config.status = TimeStatusEnum.WAIT_START; } else if (now.isAfter(endTime)) { config.status = TimeStatusEnum.END; } else { config.status = TimeStatusEnum.STARTED; activeTimeIndex.value = index; } }); timeConfigList.value = data; // 默认选中进行中的活动
handleChangeTimeConfig(activeTimeIndex.value, select.value[activeTimeIndex.value]); // 滚动到进行中的时间段
scrollToTimeConfig(activeTimeIndex.value); };
// 滚动到指定时间段
const activeTimeElId = ref(''); // 当前选中的时间段的元素ID
const scrollToTimeConfig = (index) => { nextTick(() => (activeTimeElId.value = `timeItem${index}`)); };
// 切换时间段
const activeTimeIndex = ref(0); // 当前选中的时间段的索引
const activeTimeConfig = computed(() => timeConfigList.value[activeTimeIndex.value]); // 当前选中的时间段
const handleChangeTimeConfig = (index, id) => { activeTimeIndex.value = index;
// 查询活动列表
activityPageParams.pageNo = 1; activityPageParams.configId = id; activityList.value = []; getActivityList(); };
// 倒计时
const countDown = computed(() => { const endTime = activeTimeConfig.value?.endTime; if (endTime) { return useDurationTime(`${dayjs().format('YYYY-MM-DD')} ${endTime}`); } });
//#endregion
//#region 分页查询活动列表
// 查询活动列表
const activityPageParams = reactive({ configId: 0, // 时间段 ID
pageNo: 1, // 页码
pageSize: 5, // 每页数量
}); const activityTotal = ref(0); // 活动总数
const activityList = ref([]); // 活动列表
const loadStatus = ref(''); // 页面加载状态
async function getActivityList() { loadStatus.value = 'loading'; const { data } = await SeckillApi.getSeckillActivityPage(activityPageParams); data.list.forEach((activity) => { // 计算抢购进度
activity.percent = parseInt( (100 * (activity.totalStock - activity.stock)) / activity.totalStock, ); }); activityList.value = activityList.value.concat(...data.list); activityTotal.value = data.total;
loadStatus.value = activityList.value.length < activityTotal.value ? 'more' : 'noMore'; }
// 加载更多
function loadMore() { if (loadStatus.value !== 'noMore') { activityPageParams.pageNo += 1; getActivityList(); } } // 上拉加载更多
onReachBottom(() => loadMore());
//#endregion
// 页面初始化
onLoad(async () => { await getSeckillConfigList(); });</script><style lang="scss" scoped> // 顶部背景图
.page-bg { width: 100%; height: 458rpx; background: v-bind(headerBg) no-repeat; background-size: 100% 100%; }
// 时间段轮播图
.header { width: 710rpx; height: 330rpx; margin: -276rpx auto 0 auto; border-radius: 14rpx; overflow: hidden;
swiper { height: 330rpx !important; border-radius: 14rpx; overflow: hidden; }
image { width: 100%; height: 100%; border-radius: 14rpx; overflow: hidden;
img { border-radius: 14rpx; } } }
// 时间段列表:左侧图标
.time-icon { width: 75rpx; height: 70rpx; }
// 时间段列表
.time-list { width: 596rpx; white-space: nowrap;
// 时间段
.item { display: inline-block; font-size: 20rpx; color: #666; text-align: center; box-sizing: border-box; margin-right: 30rpx; width: 130rpx;
// 开始时间
.time { font-size: 36rpx; font-weight: 600; color: #333; }
// 选中的时间段
&.active { .time { color: var(--ui-BG-Main); }
// 状态
.status { height: 30rpx; line-height: 30rpx; border-radius: 15rpx; width: 128rpx; background: linear-gradient(90deg, var(--ui-BG-Main) 0%, var(--ui-BG-Main-gradient) 100%); color: #fff; } } } }
// 内容区
.list-content { position: relative; z-index: 3; margin: 0 20rpx 0 20rpx; background: #fff; border-radius: 20rpx 20rpx 0 0;
.content-header { width: 100%; border-radius: 20rpx 20rpx 0 0; height: 150rpx; background: linear-gradient(180deg, #fff4f7, #ffe6ec);
.content-header-box { width: 678rpx; height: 64rpx; background: rgba($color: #fff, $alpha: 0.66); border-radius: 32px;
// 场次倒计时内容
.countdown-title { font-size: 28rpx; font-weight: 500; color: #333333; line-height: 28rpx; }
// 场次倒计时
.countdown-time { font-size: 28rpx; color: rgba(#ed3c30, 0.23);
// 场次倒计时:小时部分
.countdown-h { font-size: 24rpx; font-family: OPPOSANS; font-weight: 500; color: #ffffff; padding: 0 4rpx; height: 40rpx; background: rgba(#ed3c30, 0.23); border-radius: 6rpx; }
// 场次倒计时:分钟、秒
.countdown-num { font-size: 24rpx; font-family: OPPOSANS; font-weight: 500; color: #ffffff; width: 40rpx; height: 40rpx; background: rgba(#ed3c30, 0.23); border-radius: 6rpx; } } } }
// 活动列表
.scroll-box { height: 900rpx;
// 活动
.goods-box { position: relative;
// 抢购按钮
.cart-btn { position: absolute; bottom: 10rpx; right: 20rpx; z-index: 11; height: 44rpx; line-height: 50rpx; padding: 0 20rpx; border-radius: 25rpx; font-size: 24rpx; color: #fff; background: linear-gradient(90deg, #ff6600 0%, #fe832a 100%);
&.disabled { background: $gray-b; color: #fff; } }
// 秒杀限量商品数
.limit { font-size: 22rpx; color: $dark-9; margin-bottom: 5rpx; } } } }</style>
|