Files
lilishop-uniapp/pages/promotion/seckill.vue
Yer11214 349ffa4f34 refactor: 重构多个组件以支持 Vue 3 语法和功能
- 将多个组件转换为 `<script setup>` 语法,提升可读性和性能
- 优化状态管理和事件处理逻辑,简化代码结构
- 更新样式和布局以适应新组件结构
- 添加新功能和修复已知问题,提升用户体验
2026-07-08 18:37:11 +08:00

248 lines
5.8 KiB
Vue

<template>
<view class="sale">
<u-navbar
title="限时抢购"
:fixed="true"
:placeholder="true"
left-icon="arrow-left"
:auto-back="false"
@left-click="back"
></u-navbar>
<view class="header-wraper">
<image class="header-banner" mode="aspectFit" src="/static/seckill.png"></image>
</view>
<scroll-view scroll-x>
<view class="index-navs">
<view class="index-nav-v">
<view class="index-nav" :class="{ 'index-nav-active': nav == index }"
@click="clickNavigateTime(index)" v-for="(item, index) in timeLine" :key="index">
{{ item.timeLine }}:00
<view class="index-nav-desc">{{ index === 0 && item.distanceStartTime === 0 ? '抢购中' : '即将开始' }}
</view>
</view>
</view>
</view>
</scroll-view>
<view class="sale-items" v-if="goodsList.length > 0">
<goodsTemplate :res="goodsList" />
</view>
<view v-else>
<view class="nodata">
<image style="height: 240rpx;width: 320rpx;" src="/static/nodata.png" alt="" />
<div>暂无商品</div>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { onShow, onUnload } from '@dcloudio/uni-app'
import { getSeckillTimeLine, getSeckillTimeGoods } from '@/api/promotions.js'
import Foundation from '@/utils/Foundation.js'
import goodsTemplate from '@/components/m-goods-list/promotion.vue'
const nav = ref(0)
const timeLine = ref<any[]>([])
const resTime = ref(0)
const time = ref(0)
const times = ref<any>({})
const onlyOne = ref(false)
const goodsList = ref<any[]>([])
const diffTime = ref(0)
const params = ref<{
pageNumber: number
pageSize: number
timeLine?: string | number
}>({
pageNumber: 1,
pageSize: 10,
})
let setTimeInterval: ReturnType<typeof setInterval> | null = null
onShow(async () => {
await getTimeLine()
if (!timeLine.value.length) {
await uni.showToast({
icon: 'none',
duration: 2000,
title: '今天没有活动,明天再来吧',
})
}
setTimeInterval = setInterval(() => {
if (time.value <= 0) {
if (setTimeInterval) clearInterval(setTimeInterval)
getGoodsList()
getTimeLine()
} else {
times.value = Foundation.countTimeDown(time.value)
time.value--
}
}, 1000)
})
onUnload(() => {
if (setTimeInterval) clearInterval(setTimeInterval)
})
function back() {
uni.navigateBack({
delta: 1,
fail: () => {
uni.switchTab({
url: '/pages/tabbar/home/index',
})
},
})
}
async function getTimeLine() {
const res = await getSeckillTimeLine()
if (res.data.success && res.data.result.length > 0) {
const sorted = res.data.result.sort(
(x: any, y: any) => Number(x.timeLine) - Number(y.timeLine)
)
timeLine.value = sorted.slice(0, 5)
resTime.value = parseInt(String(new Date().getTime() / 1000))
onlyOne.value = res.data.result.length === 1
diffTime.value = parseInt(String(new Date().getTime() / 1000)) - resTime.value
time.value =
timeLine.value[nav.value].distanceStartTime ||
(timeLine.value[nav.value + 1] &&
timeLine.value[nav.value + 1].distanceStartTime) ||
Foundation.theNextDayTime() - diffTime.value
times.value = Foundation.countTimeDown(time.value)
getGoodsList()
}
}
async function getGoodsList() {
if (!timeLine.value.length) return
params.value.timeLine = timeLine.value[nav.value].timeLine
const res = await getSeckillTimeGoods(params.value.timeLine)
if (res.data.success && res.data.result.length != 0) {
goodsList.value = res.data.result
} else {
goodsList.value = []
}
}
function clickNavigateTime(type: number) {
nav.value = type
goodsList.value = []
diffTime.value = parseInt(String(new Date().getTime() / 1000)) - resTime.value
time.value =
timeLine.value[nav.value].distanceStartTime ||
(timeLine.value[nav.value + 1] &&
timeLine.value[nav.value + 1].distanceStartTime) ||
Foundation.theNextDayTime() - diffTime.value
times.value = Foundation.countTimeDown(time.value)
getGoodsList()
}
</script>
<style lang="scss" scoped>
.sale {
width: 100%;
min-height: 100vh;
background-color: #f7f7f7;
}
.nodata {
flex-direction: column;
display: flex;
width: 100%;
justify-content: center;
align-items: center;
margin-top: 40rpx;
>div {
font-size: 24rpx;
margin-top: 20rpx;
color: #666;
}
}
.header-wraper {
background: url('/static/bg.png') no-repeat center center;
background-size: cover;
height: 200rpx;
display: flex;
align-items: center;
justify-content: center;
.header-banner {
width: 300rpx;
height: 100rpx;
display: block;
}
}
.sale-items {
padding-top: 20rpx;
}
.index-navs {
background: #fff;
background-color: #f7f7f7;
display: -webkit-box;
display: -webkit-flex;
display: flex;
align-items: center;
justify-content: space-between;
}
.index-nav-v {
display: -webkit-box;
display: -webkit-flex;
display: flex;
justify-content: center;
align-items: center;
position: relative;
}
.index-nav {
font-size: 28rpx;
display: -webkit-box;
display: -webkit-flex;
display: flex;
justify-content: center;
align-items: center;
width: 150rpx;
flex-direction: column;
color: #bababa;
height: 115rpx;
line-height: 1em;
position: relative;
font-size: 32rpx;
font-weight: bold;
&-active {
color: $main-color;
position: relative;
z-index: 30;
.index-nav-desc {
color: #fff;
font-weight: bold;
background: $main-color;
padding: 6rpx 16rpx;
border-radius: 50px;
}
}
}
.index-nav-desc {
margin-top: 8rpx;
font-size: 22rpx;
color: #bababa;
}
</style>