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

682 lines
16 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="county-my-tickets-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 class="header-right">
<view class="right-dot"></view>
<view class="right-ring"></view>
</view>
</view>
<view class="tabs">
<view
v-for="tab in tabs"
:key="tab.key"
class="tab-item"
:class="{ active: activeTab === tab.key }"
@click="onChangeTab(tab.key)"
>
{{ tab.label }}
</view>
</view>
</view>
<scroll-view class="page-body" scroll-y :style="{ paddingTop: `calc(${statusBarHeight}px + 168rpx)` }">
<view class="tip-bar" v-if="summaryText">
<view class="tip-text one-t">{{ summaryText }}</view>
<view class="tip-btn" @click="goWinnersPage">查看全部 <text class="icon-expand"></text></view>
</view>
<view class="ticket-list" v-if="displayList.length">
<view class="ticket-card" v-for="item in displayList" :key="item.id">
<view class="ticket-head">
<view class="ticket-code">
<text class="code-icon">🎟</text>
<text class="code-label">抽奖码:</text>
<text class="code-value">{{ item.ticketCode || '--' }}</text>
</view>
<view class="ticket-status" :class="[ `status-${item.status}`, item.status === 0 ? 'status-pill' : '', item.status === 1 && item.claimStatus === 1 ? 'status-claimed' : '' ]">{{ getStatusText(item) }}</view>
</view>
<view class="ticket-info-row">
<text class="label">来源订单</text>
<text class="value">{{ item.sourceOrderId || '--' }}</text>
</view>
<view class="ticket-info-row">
<text class="label">开奖时间</text>
<text class="value">{{ formatDrawTime(item.drawTime) }}</text>
</view>
<view class="prize-box" v-if="showPrize(item)">
恭喜获得{{ item.prizeName || '神秘奖品' }}
</view>
<view class="ticket-footer" v-if="item.status === 1">
<template v-if="item.claimStatus === 0">
<view class="footer-note">注意请在7天内领取过期不予发放</view>
<view class="footer-btn btn-claim" @click="onClaim(item)"> </view>
</template>
<template v-else-if="item.claimStatus === 1">
<view class="footer-note done">奖品已领取</view>
<view class="footer-btn btn-order" @click="goOrder(item)">查看订单</view>
</template>
</view>
<view class="invalid-reason" v-if="item.status === -1">
失效原因{{ getInvalidReason(item) }}
</view>
</view>
</view>
<view class="empty-wrap" v-else>
<view class="empty-text">{{ loading ? '加载中...' : '暂无抽奖码记录' }}</view>
</view>
</scroll-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 { claimCoupon, getDrawMyTickets } from '@/api/lottery'
export default {
name: 'CountyMyTicketsPage',
data() {
return {
statusBarHeight: 20,
loading: false,
activeTab: 'all',
activityId: 0,
tabs: [
{ key: 'all', label: '全部' },
{ key: 'wait', label: '待开奖' },
{ key: 'opened', label: '已开奖' },
{ key: 'invalid', label: '已失效' }
],
list: [],
openedList: [],
showCouponSuccessPopup: false,
claiming: false
}
},
computed: {
displayList() {
return this.activeTab === 'opened' ? this.openedList : this.list
},
summaryText() {
return `恭喜海*在第01期抽奖活动中获得 iPhone17 pro 手机`
}
},
onLoad(options) {
if (options && options.activityId) {
this.activityId = Number(options.activityId) || 0
}
},
onShow() {
this.statusBarHeight = uni.getSystemInfoSync().statusBarHeight || 20
this.fetchList()
},
methods: {
mapTabToStatus(tab) {
const map = {
wait: 0,
invalid: -1
}
return map[tab]
},
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 []
},
normalizeTicketItem(item = {}) {
const statusNum = Number(item.status)
const claimStatusNum = Number(item.claimStatus)
return {
...item,
id: item.id || item.lotteryRecordId || item.ticketId || item.recordId || '',
ticketCode: item.ticketCode || item.code || '',
status: Number.isNaN(statusNum) ? -999 : statusNum,
claimStatus: Number.isNaN(claimStatusNum) ? 0 : claimStatusNum,
sourceOrderId: item.sourceOrderId || item.orderId || item.orderNo || '',
drawTime: item.drawTime || item.openTime || '',
prizeName: item.prizeName || item.awardName || '',
prizeType: item.prizeType || item.awardType || '',
prizeTypeName: item.prizeTypeName || item.awardTypeName || ''
}
},
filterByActivity(list = []) {
if (!this.activityId) return list
return list.filter(item => Number(item.activityId || item.drawActivityId || 0) === this.activityId)
},
filterByTab(list = []) {
if (this.activeTab === 'wait') {
return list.filter(item => item.status === 0)
}
if (this.activeTab === 'opened') {
return list.filter(item => item.status === 1 || item.status === 2)
}
if (this.activeTab === 'invalid') {
return list.filter(item => item.status === -1)
}
return list
},
onChangeTab(tab) {
if (this.activeTab === tab) return
this.activeTab = tab
this.fetchList()
},
goWinnersPage() {
uni.navigateTo({
url: `/pkg-video/views/county-winners?activityId=${this.activityId}`
})
},
async fetchList() {
this.loading = true
try {
const status = this.mapTabToStatus(this.activeTab)
const res = await getDrawMyTickets(status)
const allList = this.filterByActivity(this.normalizeListData(res).map(this.normalizeTicketItem))
this.openedList = allList.filter(item => item.status === 1 || item.status === 2)
this.list = this.filterByTab(allList)
} catch (e) {
this.list = []
this.openedList = []
uni.showToast({
title: '抽奖码加载失败',
icon: 'none'
})
} finally {
this.loading = false
}
},
getStatusText(item) {
const status = Number(item.status)
if (status === 0) return '等待开奖'
if (status === 1) return item.claimStatus === 1 ? '已领取' : '已开奖'
if (status === 2) return '已开奖'
if (status === -1) return '已失效'
return '--'
},
showPrize(item) {
return [1, 2, -1].includes(Number(item.status)) && !!item.prizeName
},
getInvalidReason(item) {
if (Number(item.claimStatus) === -1) return '奖品过期未领取'
return '订单取消'
},
isCouponPrize(item = {}) {
const prizeType = String(item.prizeType || '').toLowerCase()
const prizeTypeName = String(item.prizeTypeName || '')
const prizeName = String(item.prizeName || '')
if (prizeType === 'coupon' || prizeType === '3') return true
return /券/.test(prizeTypeName) || /券/.test(prizeName)
},
formatDrawTime(time) {
if (!time) return '--'
const date = new Date(String(time).replace(/-/g, '/'))
if (Number.isNaN(date.getTime())) return time
const y = date.getFullYear()
const m = String(date.getMonth() + 1).padStart(2, '0')
const d = String(date.getDate()).padStart(2, '0')
const hh = String(date.getHours()).padStart(2, '0')
const mm = String(date.getMinutes()).padStart(2, '0')
return `${y}${m}${d}${hh}:${mm}`
},
async onClaim(item) {
if (!item.id) return
if (this.claiming) return
if (this.isCouponPrize(item)) {
this.claiming = true
uni.showLoading({ title: '领取中...' })
try {
await claimCoupon(Number(item.id))
this.showCouponSuccessPopup = true
this.fetchList()
} catch (e) {
uni.showToast({
title: (e && e.msg) || (e && e.message) || '领取失败',
icon: 'none'
})
} finally {
uni.hideLoading()
this.claiming = false
}
return
}
const query = [
`lotteryRecordId=${item.id}`,
`ticketCode=${encodeURIComponent(item.ticketCode || '')}`,
`prizeName=${encodeURIComponent(item.prizeName || '')}`,
`prizeType=${encodeURIComponent(item.prizeType || '')}`,
`prizeTypeName=${encodeURIComponent(item.prizeTypeName || '')}`
].join('&')
uni.navigateTo({
url: `/pkg-video/views/county-claim-address?${query}`
})
},
closeCouponSuccessPopup() {
this.showCouponSuccessPopup = false
},
goMyCoupons() {
this.showCouponSuccessPopup = false
uni.navigateTo({
url: '/pkg_user/views/myCoupons'
})
},
goOrder(item) {
if (!item.sourceOrderId) {
uni.showToast({
title: '缺少订单号',
icon: 'none'
})
return
}
uni.navigateTo({
url: `/pages/order/OrderDetails/index?id=${item.sourceOrderId}`
})
},
goBack() {
if (getCurrentPages().length > 1) {
uni.navigateBack()
return
}
uni.switchTab({
url: '/pages/home/index'
})
}
}
}
</script>
<style scoped lang="less">
.county-my-tickets-page {
min-height: 100vh;
background: #f5f5f5;
}
.custom-header {
position: fixed;
left: 0;
top: 0;
width: 100%;
z-index: 20;
background: #f5f5f5;
}
.header-inner {
height: 88rpx;
padding: 0 24rpx;
display: flex;
align-items: center;
}
.header-back {
width: 56rpx;
height: 56rpx;
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
}
.header-back-icon {
font-size: 60rpx;
color: #111;
line-height: 1;
margin-top: -4rpx;
}
.header-title {
flex: 1;
text-align: center;
margin-left: 56rpx;
font-size: 34rpx;
color: #333;
font-weight: 700;
}
.header-right {
width: 88rpx;
height: 44rpx;
border-radius: 24rpx;
border: 2rpx solid #d8d8d8;
background: #fff;
display: flex;
align-items: center;
justify-content: center;
}
.right-dot {
width: 8rpx;
height: 8rpx;
border-radius: 50%;
background: #111;
margin-right: 14rpx;
}
.right-ring {
width: 18rpx;
height: 18rpx;
border-radius: 50%;
border: 2rpx solid #111;
}
.tabs {
height: 80rpx;
padding: 0 24rpx;
display: flex;
align-items: center;
background: #f5f5f5;
}
.tab-item {
margin-right: 20rpx;
padding: 8rpx 24rpx;
border-radius: 30rpx;
color: #666;
font-size: 26rpx;
background: #fff;
}
.tab-item.active {
color: #BD3124;
background: rgba(220,60,53,0.39);
}
.page-body {
box-sizing: border-box;
height: 100vh;
}
.tip-bar {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10rpx 24rpx;
font-size: 24rpx;
color: #666;
background: #fff;
border-radius: 160rpx;
margin: 0 20rpx;
}
.tip-text {
flex: 1;
font-weight: 500;
color: #333;
}
.tip-btn {
background: #FCF9EA;
padding: 6rpx 16rpx;
border-radius: 20rpx;
display: flex;
align-items: center;
font-size: 22rpx;
color: #333;
margin-left: 10rpx;
}
.icon-expand {
font-size: 20rpx;
margin-left: 4rpx;
}
.ticket-list {
padding-top: 10rpx;
padding-bottom: 40rpx;
}
.ticket-card {
background: #fff;
border-radius: 16rpx;
margin: 0 24rpx 20rpx;
padding: 24rpx;
}
.ticket-head {
display: flex;
align-items: center;
justify-content: space-between;
padding-bottom: 20rpx;
border-bottom: 2rpx dashed #eee;
margin-bottom: 20rpx;
position: relative;
}
.ticket-code {
display: flex;
align-items: center;
color: #333;
}
.code-icon {
margin-right: 8rpx;
color: #333;
font-size: 28rpx;
}
.code-label {
font-size: 32rpx;
font-weight: 700;
margin-right: 12rpx;
}
.code-value {
font-size: 34rpx;
font-weight: 700;
letter-spacing: 1rpx;
}
.ticket-status {
font-size: 26rpx;
font-weight: bold;
}
.status-pill {
background: #BD3124;
color: #fff;
padding: 4rpx 16rpx;
border-radius: 20rpx 0 0 20rpx;
font-size: 24rpx;
font-weight: normal;
position: absolute;
right: -24rpx;
}
.status-0 {
/* handled by pill */
}
.status-1,
.status-2 {
color: #BD3124;
}
.status-claimed {
color: #999 !important;
}
.status--1 {
color: #999;
}
.ticket-info-row {
margin-top: 14rpx;
display: flex;
align-items: center;
justify-content: space-between;
}
.ticket-info-row .label {
color: #999;
font-size: 26rpx;
}
.ticket-info-row .value {
color: #333;
font-size: 26rpx;
}
.prize-box {
margin-top: 24rpx;
border: 2rpx solid #BD3124;
border-radius: 40rpx;
color: #BD3124;
font-size: 30rpx;
text-align: center;
padding: 16rpx 0;
font-weight: 600;
}
.ticket-footer {
margin-top: 20rpx;
display: flex;
align-items: center;
justify-content: space-between;
}
.footer-note {
flex: 1;
color: #BD3124;
font-size: 22rpx;
}
.footer-note.done {
color: #BD3124;
}
.footer-btn {
width: 170rpx;
height: 54rpx;
border-radius: 30rpx;
color: #fff;
font-size: 26rpx;
display: flex;
align-items: center;
justify-content: center;
}
.btn-claim {
background: #BD3124;
}
.btn-order {
background: #BD3124;
}
.invalid-reason {
margin-top: 14rpx;
font-size: 24rpx;
color: #BD3124;
}
.empty-wrap {
padding-top: 120rpx;
text-align: center;
}
.empty-text {
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;
}
</style>