Files
xdd-uniapp-new/pkg-video/views/county-winners.vue
T

842 lines
21 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="winners-page">
<view class="custom-header" :style="{ paddingTop: `${statusBarHeight}px` }">
<view class="header-inner">
<view class="header-back" @click="goBack">
<text class="header-back-icon"></text>
</view>
<view class="header-title">中奖名单</view>
</view>
</view>
<view class="content-wrap" v-if="loaded">
<view v-if="!showHistory" class="draw-info-wrap" :style="{ backgroundImage: `url(${webUrl}/zhongjiang5.png)` }">
<view class="period-side left">
<text class="side-text v12-font-36"></text>
<text class="side-text v12-font-36">{{ periodLabel }}</text>
<text class="side-text v12-font-36"></text>
</view>
<view class="period-side right">
<view class="side-text side-text-count v12-font-26" @click="handleShowHistory">
<text></text>
<text></text>
<text></text>
<text></text>
<text></text>
<text></text>
</view>
</view>
<view class="draw-main">
<view class="draw-title">本期已开奖</view>
<view class="draw-time">开奖时间{{ drawTime }}</view>
</view>
</view>
<view v-else class="draw-info-wrap" style="padding: 0" :style="{ backgroundImage: `url(${webUrl}/zhongjiang01.png)` }">
<view
class="history-nav-btn left"
:class="{ disabled: !canSwitchPrev }"
@click="handleSwitchHistory('prev')"
>
</view>
<view
class="history-nav-btn right"
:class="{ disabled: !canSwitchNext }"
@click="handleSwitchHistory('next')"
>
</view>
<view class="top-wrap">
<view class="v12-font-bold v12-font-40">
{{ periodLabel }}
</view>
</view>
<view class="draw-main" style="padding: 0" >
<view class="draw-time" style="margin-top: 60rpx">开奖时间{{ drawTime }}</view>
</view>
</view>
<view class="win-banner" v-if="hasWon === 1 && userWinList.length > 0" :style="{ backgroundImage: `url(${webUrl}/zhongjiangn.png)` }">
<view class="win-banner-subtitle">
您的抽奖码 <text class="code">{{ userWinList[0].ticketCode }}</text> 获得了
</view>
<view class="prize-card" v-for="(prize, index) in userWinList" :key="index">
<view class="prize-img-wrap">
<image class="prize-img" :src="prize.prizeImage" mode="aspectFill"></image>
</view>
<view class="prize-info">
<view class="prize-name-wrap">
<text class="prize-level">{{ prize.levelName }}</text>
<text class="prize-name">{{ prize.prizeName }}</text>
</view>
<view class="prize-desc">{{ prize.prizeDesc || "默认" }}</view>
</view>
</view>
<button class="my-tickets-btn" @click="handleClaimNow">
<text class="btn-text">立即领取</text>
</button>
<view class="claim-tip">*请在7天内领取过期失效</view>
</view>
<view class="winners-list-wrap">
<view class="list-title-badge" :style="{ backgroundImage: `url(${webUrl}/zhongjiang6.png)` }">
<text class="gift-icon" style="opacity: 0;">🎁</text>
<text class="title-text" style="opacity: 0;">中奖名单</text>
</view>
<view class="list-content">
<view class="list-item" v-for="(item, index) in activityWinnerList" :key="index">
<view class="item-left">
<view class="item-prize">
<text class="level">{{ item.levelName }}</text>
<text class="name">{{ item.prizeName }}</text>
</view>
<view class="item-code">
中奖码<text class="code-text">{{ item.ticketCode }}</text>
</view>
</view>
<view class="item-right">
<view class="user-name">{{ item.realName }}</view>
<view class="user-phone">{{ item.phone }}</view>
</view>
</view>
<view class="empty-text" v-if="activityWinnerList.length === 0">暂无中奖名单</view>
</view>
</view>
</view>
<view class="coupon-popup-mask" v-if="showCouponSuccessPopup" @click="closeCouponSuccessPopup">
<view class="coupon-popup" @click.stop>
<view class="popup-close" @click="closeCouponSuccessPopup">×</view>
<view class="popup-icon-wrap">
<text class="popup-icon">🎉</text>
</view>
<view class="popup-title">领取成功</view>
<view class="popup-desc">您的奖品已经领取成功请前往我的优惠券查看详情</view>
<view class="popup-btn" @click="goMyCoupons">去查看我的优惠券</view>
</view>
</view>
</view>
</template>
<script>
import { getDrawActivityInfo, getActivityWinRecord, getDrawMyTickets, getDrawHistoryActivities } from '@/api/lottery'
export default {
computed: {
currentHistoryIndex() {
if (!this.historyList.length) return -1
return this.historyList.findIndex(item => Number(item.id) === Number(this.activityId))
},
canSwitchPrev() {
return this.currentHistoryIndex > -1 && this.currentHistoryIndex < this.historyList.length - 1
},
canSwitchNext() {
return this.currentHistoryIndex > 0
}
},
data() {
return {
webUrl: this.$VUE_APP_RESOURCES_URL,
statusBarHeight: 20,
activityId: 0,
periodLabel: "01",
drawTime: "--",
hasWon: 0,
userWinList: [],
activityWinnerList: [],
historyActivities: [],
loaded: false,
showCouponSuccessPopup: false,
showHistory: false,
historyList: []
}
},
onLoad(options) {
this.activityId = Number(options && options.activityId) || 0
const systemInfo = uni.getSystemInfoSync()
this.statusBarHeight = systemInfo.statusBarHeight || 20
this.fetchData()
},
methods: {
async handleShowHistory() {
uni.showLoading({
mask: true
})
const historyRes = await getDrawHistoryActivities()
const {data, success} = historyRes || {}
uni.hideLoading()
if(!success) return
this.historyList = (data || [])
.map(item => ({
id: Number(item.id || item.activityId || item.drawActivityId || 0),
periodNo: item.periodNo || '',
drawTime: item.drawTime || ''
}))
.filter(item => item.id > 0)
.sort((a, b) => this.getPeriodNoValue(b.periodNo) - this.getPeriodNoValue(a.periodNo))
this.showHistory = true
},
async handleSwitchHistory(direction) {
if (!this.historyList.length) {
uni.showToast({
title: "暂无可切换历史期",
icon: "none"
})
return
}
const currentIndex = this.currentHistoryIndex
if (currentIndex < 0) {
uni.showToast({
title: "当前期数不在历史列表中",
icon: "none"
})
return
}
const targetIndex = direction === 'prev' ? currentIndex + 1 : currentIndex - 1
if (targetIndex < 0 || targetIndex >= this.historyList.length) return
const target = this.historyList[targetIndex]
if (!target || Number(target.id) === Number(this.activityId)) return
this.activityId = Number(target.id)
this.loaded = false
await this.fetchData()
},
goBack() {
uni.navigateBack()
},
navToMyTickets() {
uni.navigateTo({
url: '/pkg-video/views/county-my-tickets'
})
},
handleClaimNow() {
if (!this.userWinList.length) return
if (this.userWinList.length > 1) {
this.navToMyTickets()
return
}
const allCouponPrize = this.userWinList.every((item) => this.isCouponPrize(item))
if (allCouponPrize) {
this.showCouponSuccessPopup = true
return
}
this.navToReceiveInfo(this.userWinList[0])
},
isCouponPrize(prize = {}) {
const prizeType = Number(prize.prizeType)
const prizeName = String(prize.prizeName || "")
return prizeType === 3 || prize.prizeType === "coupon" || /券/.test(prizeName)
},
navToReceiveInfo(prize = {}) {
const query = `?source=lottery&ticketCode=${encodeURIComponent(prize.ticketCode || "")}&prizeName=${encodeURIComponent(prize.prizeName || "")}`
uni.navigateTo({
url: `/pages/user/address/AddAddress/index${query}`
})
},
closeCouponSuccessPopup() {
this.showCouponSuccessPopup = false
},
goMyCoupons() {
this.showCouponSuccessPopup = false
uni.navigateTo({
url: "/pkg_user/views/myCoupons"
})
},
normalizeListData(res) {
if (!res) return []
if (Array.isArray(res)) return res
if (Array.isArray(res.data)) return res.data
if (Array.isArray(res.data && res.data.list)) return res.data.list
if (Array.isArray(res.list)) return res.list
return []
},
normalizeHistoryListData(res) {
if (!res) return []
if (Array.isArray(res)) return res
if (Array.isArray(res.data)) return res.data
if (Array.isArray(res.data && res.data.list)) return res.data.list
if (Array.isArray(res.list)) return res.list
return []
},
getPeriodNoValue(periodNo) {
const text = String(periodNo || '')
const match = text.match(/\d+/g)
if (!match || !match.length) return 0
return Number(match.join('')) || 0
},
formatDrawTime(time) {
if (!time) return "--"
const date = new Date(String(time).replace(/-/g, "/"))
if (Number.isNaN(date.getTime())) return String(time)
const y = date.getFullYear()
const m = String(date.getMonth() + 1).padStart(2, "0")
const d = String(date.getDate()).padStart(2, "0")
return `${y}${m}${d}日`
},
mapUserWinList(ticketList = []) {
return ticketList.map(item => ({
levelName: item.levelName || item.prizeLevelName || "中奖奖品",
prizeName: item.prizeName || item.awardName || "神秘奖品",
prizeDesc: item.prizeDesc || item.remark || "默认",
ticketCode: item.ticketCode || item.code || "--",
prizeType: item.prizeType,
prizeImage: item.prizeImage || item.image || ""
}))
},
async fetchData() {
try {
const [activityRes, winnerRes, myTicketsRes] = await Promise.all([
this.activityId ? getDrawActivityInfo(this.activityId) : Promise.resolve(null),
this.activityId ? getActivityWinRecord(this.activityId) : Promise.resolve(null),
getDrawMyTickets(1)
])
const activityData = (activityRes && activityRes.data) || {}
const winnerList = winnerRes.data.activityWinnerList || []
const myTicketList = this.normalizeListData(myTicketsRes)
const filteredMyTicketList = this.activityId
? myTicketList.filter(item => Number(item.activityId || item.drawActivityId || 0) === this.activityId)
: myTicketList
this.drawTime = this.formatDrawTime(activityData.drawTime || activityData.openTime || "")
this.activityWinnerList = winnerList
this.userWinList = this.mapUserWinList(filteredMyTicketList)
this.hasWon = this.userWinList.length > 0 ? 1 : 0
this.periodLabel = winnerRes.data.periodNo || "01"
} catch (e) {
uni.showToast({
title: "中奖数据加载失败",
icon: "none"
})
} finally {
this.loaded = true
}
}
}
}
</script>
<style lang="less" scoped>
.winners-page {
min-height: 100vh;
background: #f4f6f8;
padding-bottom: 48rpx;
font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', Helvetica, Segoe UI, Arial, Roboto, 'PingFang SC', 'miui', 'Hiragino Sans GB', 'Microsoft Yahei', sans-serif;
}
.custom-header {
position: sticky;
top: 0;
left: 0;
right: 0;
z-index: 99;
background-color: #fff;
.header-inner {
height: 44px;
display: flex;
align-items: center;
position: relative;
padding: 0 24rpx;
.header-back {
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8rpx;
.header-back-icon {
font-size: 60rpx;
color: #333;
margin-top: -4rpx;
}
}
.header-title {
flex: 1;
text-align: center;
font-size: 34rpx;
font-weight: 700;
color: #333;
}
.header-actions {
width: 120rpx;
height: 56rpx;
border-radius: 28rpx;
display: flex;
align-items: center;
justify-content: space-around;
padding: 0 16rpx;
.dot {
font-size: 22rpx;
color: #333;
font-weight: bold;
letter-spacing: 2rpx;
}
.circle-dot {
width: 20rpx;
height: 20rpx;
border: 3rpx solid #333;
border-radius: 50%;
position: relative;
&::after {
content: '';
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 6rpx;
height: 6rpx;
background: #333;
border-radius: 50%;
}
}
}
}
}
.content-wrap {
padding: 28rpx 24rpx 0;
}
.draw-info-wrap {
position: relative;
margin: 18rpx 0 38rpx;
padding: 24rpx 70rpx;
display: flex;
align-items: center;
min-height: 168rpx;
background-size: 100% 100%;
background-repeat: no-repeat;
background-color: transparent;
.period-side {
position: absolute;
top: 50%;
transform: translateY(-50%);
width: 54rpx;
height: 152rpx;
border-radius: 16rpx;
display: flex;
align-items: center;
justify-content: center;
background: #fff4d1;
padding-bottom: 15rpx;
.side-text {
font-size: 24rpx;
line-height: 1.1;
font-weight: 900;
letter-spacing: 2rpx;
color: #1f1f1f;
}
.side-text-count {
writing-mode: horizontal-tb;
height: 132rpx;
display: flex;
flex-direction: column;
justify-content: space-between;
align-items: center;
letter-spacing: 0;
line-height: 1;
}
&.left {
left: 8rpx;
background: transparent;
display: flex;
flex-direction: column;
align-items: center;
}
&.right {
right: 8rpx;
background: transparent;
}
}
.draw-main {
flex: 1;
border-radius: 16rpx;
text-align: center;
padding: 18rpx 12rpx;
}
.draw-title {
font-size: 58rpx;
line-height: 1.05;
color: #d73222;
font-weight: 900;
letter-spacing: 1rpx;
margin-bottom: 8rpx;
}
.draw-time {
font-size: 30rpx;
color: #222;
font-weight: 800;
}
}
.win-banner {
padding: 54rpx 20rpx 26rpx;
text-align: center;
margin-bottom: 62rpx;
position: relative;
background-size: 100%;
background-repeat: no-repeat;
background-color: transparent;
.win-badge {
position: absolute;
top: -34rpx;
left: 50%;
transform: translateX(-50%);
width: 390rpx;
height: 72rpx;
background: #ffca1c;
border: 4rpx solid #1f1f1f;
border-radius: 18rpx;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 5rpx 0 #d49400;
.win-badge-text {
font-size: 52rpx;
font-weight: 900;
color: #fff;
text-shadow: -2rpx -2rpx 0 #1f1f1f, 2rpx -2rpx 0 #1f1f1f, -2rpx 2rpx 0 #1f1f1f, 2rpx 2rpx 0 #1f1f1f, 0 4rpx 0 #1f1f1f;
letter-spacing: 1rpx;
}
}
.win-banner-subtitle {
font-size: 28rpx;
color: #fff;
margin-bottom: 20rpx;
margin-top: 8rpx;
font-weight: 700;
.code {
font-weight: 900;
font-size: 40rpx;
margin: 0 8rpx;
color: #ffe8db;
}
}
.prize-card {
background: #fffdf5;
border: 4rpx solid #1f1f1f;
border-radius: 20rpx;
padding: 12rpx;
display: flex;
align-items: flex-start;
margin-bottom: 18rpx;
text-align: left;
box-shadow: inset 0 0 0 2rpx #fff;
.prize-img-wrap {
width: 132rpx;
height: 132rpx;
border-radius: 12rpx;
border: 2rpx dashed #b3b3b3;
padding: 4rpx;
margin-right: 18rpx;
background: #fff;
display: flex;
align-items: center;
justify-content: center;
.prize-img {
width: 100%;
height: 100%;
border-radius: 8rpx;
}
}
.prize-info {
flex: 1;
padding-top: 8rpx;
.prize-name-wrap {
font-size: 40rpx;
color: #222;
margin-bottom: 10rpx;
line-height: 1.2;
.prize-level {
font-weight: 900;
}
.prize-name {
font-weight: 700;
}
}
.prize-desc {
font-size: 34rpx;
color: #8c8c8c;
font-weight: 600;
}
}
}
.my-tickets-btn {
background: linear-gradient(180deg, #ffd33d 0%, #ffb300 100%);
border: 4rpx solid #1f1f1f;
border-radius: 48rpx;
padding: 0;
height: 90rpx;
width: 92%;
margin: 0 auto;
display: flex;
align-items: center;
justify-content: center;
box-shadow: inset 0 -8rpx 0 rgba(0, 0, 0, 0.12), 0 8rpx 0 #1f1f1f;
transition: all 0.1s;
&:active {
transform: translateY(4rpx);
box-shadow: inset 0 -4rpx 0 rgba(0, 0, 0, 0.12), 0 4rpx 0 #1f1f1f;
}
.btn-text {
font-size: 50rpx;
font-weight: 900;
color: #fff;
text-shadow: -2rpx -2rpx 0 #1f1f1f, 2rpx -2rpx 0 #1f1f1f, -2rpx 2rpx 0 #1f1f1f, 2rpx 2rpx 0 #1f1f1f, 0 4rpx 0 #1f1f1f;
letter-spacing: 2rpx;
}
}
.claim-tip {
margin-top: 16rpx;
color: #fff0de;
font-size: 30rpx;
font-weight: 700;
}
}
.winners-list-wrap {
background: #fff;
border-radius: 28rpx;
padding: 46rpx 24rpx 10rpx;
position: relative;
box-shadow: 0 5rpx 0 #e6e8ec;
.list-title-badge {
position: absolute;
left: 50%;
top: -30rpx;
transform: translateX(-50%);
display: flex;
align-items: center;
padding: 14rpx 34rpx;
background-size: 100% 100%;
background-repeat: no-repeat;
background-color: transparent;
.gift-icon {
font-size: 28rpx;
margin-right: 8rpx;
}
.title-text {
font-size: 34rpx;
font-weight: 900;
color: #222;
}
}
.list-content {
.list-item {
display: flex;
justify-content: space-between;
align-items: flex-start;
padding: 24rpx 0;
border-bottom: 2rpx solid #f1f1f1;
&:last-child {
border-bottom: none;
}
.item-left {
flex: 1;
.item-prize {
font-size: 36rpx;
color: #333;
margin-bottom: 8rpx;
line-height: 1.25;
.level {
font-weight: 900;
}
.name {
font-weight: 700;
}
}
.item-code {
font-size: 32rpx;
color: #666;
font-weight: 700;
.code-text {
color: #d8392b;
font-weight: 900;
}
}
}
.item-right {
margin-left: 16rpx;
text-align: right;
.user-name {
font-size: 32rpx;
color: #666;
font-weight: 700;
margin-bottom: 8rpx;
}
.user-phone {
font-size: 30rpx;
color: #999;
font-weight: 600;
}
}
}
.empty-text {
text-align: center;
padding: 60rpx 0;
color: #999;
font-size: 28rpx;
}
}
}
.coupon-popup-mask {
position: fixed;
inset: 0;
z-index: 1000;
background: rgba(0, 0, 0, 0.55);
display: flex;
align-items: center;
justify-content: center;
padding: 24rpx;
}
.coupon-popup {
width: 100%;
max-width: 620rpx;
background: #fff;
border-radius: 32rpx;
padding: 26rpx 34rpx 42rpx;
position: relative;
}
.popup-close {
position: absolute;
right: 18rpx;
top: 18rpx;
width: 40rpx;
height: 40rpx;
border-radius: 50%;
border: 2rpx solid #333;
font-size: 40rpx;
font-weight: 700;
color: #333;
line-height: 40rpx;
text-align: center;
}
.popup-icon-wrap {
width: 112rpx;
height: 112rpx;
margin: 20rpx auto 8rpx;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
}
.popup-icon {
font-size: 70rpx;
}
.popup-title {
text-align: center;
font-size: 60rpx;
font-weight: 900;
color: #222;
margin-top: 12rpx;
}
.popup-desc {
margin: 38rpx auto 42rpx;
text-align: center;
font-size: 28rpx;
line-height: 1.6;
color: #9a9a9a;
width: 92%;
}
.popup-btn {
width: 86%;
height: 96rpx;
margin: 0 auto;
border-radius: 48rpx;
background: #c43124;
color: #fff;
font-size: 40rpx;
font-weight: 900;
text-align: center;
line-height: 96rpx;
letter-spacing: 2rpx;
}
.top-wrap{
position: absolute;
top: 6px;
left: 0;
right: 0;
margin: auto;
width: fit-content;
color: #333;
}
.history-nav-btn {
width: 52rpx;
height: 52rpx;
position: absolute;
top: 16rpx;
border-radius: 50%;
color: #333;
font-size: 44rpx;
line-height: 48rpx;
text-align: center;
font-weight: 700;
&.left {
left: 240rpx;
}
&.right {
right: 240rpx;
}
&.disabled {
opacity: 0.4;
}
}
</style>