Feat:抽奖2.0(部分接口未调通)

This commit is contained in:
LinCyJang
2026-04-27 08:47:33 +08:00
parent 0202f14073
commit d3602c010e
11 changed files with 2718 additions and 29 deletions
+550
View File
@@ -0,0 +1,550 @@
<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>
</template>
<script>
// import { getDrawMyTickets } from '@/api/lottery'
export default {
name: 'CountyMyTicketsPage',
data() {
return {
statusBarHeight: 20,
loading: false,
activeTab: 'all',
activityId: 1,
tabs: [
{ key: 'all', label: '全部' },
{ key: 'wait', label: '待开奖' },
{ key: 'opened', label: '已开奖' },
{ key: 'invalid', label: '已失效' }
],
list: [],
openedList: []
}
},
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) || 1
}
},
onShow() {
this.statusBarHeight = uni.getSystemInfoSync().statusBarHeight || 20
this.fetchList()
},
methods: {
mapTabToStatus(tab) {
const map = {
wait: 0,
invalid: -1
}
return map[tab]
},
onChangeTab(tab) {
if (this.activeTab === tab) return
this.activeTab = tab
this.fetchList()
},
goWinnersPage() {
uni.navigateTo({
url: `/pkg-video/views/county-winners?activityId=${this.activityId}`
})
},
fetchList() {
this.loading = true
// 使用模拟数据以匹配设计图所有状态
setTimeout(() => {
const mockData = [
{
id: 1,
ticketCode: 'A8F92E',
status: 0,
sourceOrderId: '12345678910',
drawTime: '2026-05-30 10:00:00',
},
{
id: 2,
ticketCode: 'A8F92E',
status: 1,
claimStatus: 0,
sourceOrderId: '12345678910',
drawTime: '2026-05-30 10:00:00',
prizeName: '一等奖ipone17 pro max手机'
},
{
id: 3,
ticketCode: 'A8F92E',
status: 1,
claimStatus: 1,
sourceOrderId: '12345678910',
drawTime: '2026-05-30 10:00:00',
prizeName: '一等奖ipone17 pro max手机'
},
{
id: 4,
ticketCode: 'A8F92E',
status: -1,
claimStatus: 0,
sourceOrderId: '12345678910',
drawTime: '2026-05-30 10:00:00',
},
{
id: 5,
ticketCode: 'A8F92E',
status: -1,
claimStatus: -1,
sourceOrderId: '12345678910',
drawTime: '2026-05-30 10:00:00',
prizeName: '一等奖ipone17 pro max手机'
}
];
let filteredData = mockData;
if (this.activeTab === 'wait') {
filteredData = mockData.filter(item => item.status === 0);
} else if (this.activeTab === 'opened') {
filteredData = mockData.filter(item => item.status === 1 || item.status === 2);
} else if (this.activeTab === 'invalid') {
filteredData = mockData.filter(item => item.status === -1);
}
this.list = filteredData;
this.openedList = mockData.filter(item => item.status === 1 || item.status === 2);
this.loading = false;
}, 300);
},
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 '订单取消'
},
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}`
},
onClaim(item) {
if (!item.id) return
uni.navigateTo({
url: `/pages/order/OrderSubmission/index?lotteryRecordId=${item.id}`
})
},
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;
}
</style>