New file |
| | |
| | | <template> |
| | | <view class="container"> |
| | | <!-- 抽奖机会卡片 --> |
| | | <view class="prize-card" v-if="prizeInfo.id"> |
| | | <u-card :border="false" :head-style="{ padding: '30rpx' }" :body-style="{ padding: '0 30rpx 30rpx' }"> |
| | | <!-- 头部:店铺信息 --> |
| | | <view slot="head" class="card-head"> |
| | | <view class="store-info"> |
| | | <u-icon name="home" size="36" color="#999"></u-icon> |
| | | <text class="store-name">{{ prizeInfo.storeName || '默认店铺' }}</text> |
| | | </view> |
| | | <u-tag v-if="prizeInfo.claimStatus === 'NOT_CLAIM'" text="未领取" type="warning" mode="plain" size="mini" /> |
| | | <u-tag v-else-if="prizeInfo.claimStatus === 'CLAIM'" text="已领取" type="success" mode="plain" size="mini" /> |
| | | <u-tag v-else text="已过期" type="info" mode="plain" size="mini" /> |
| | | </view> |
| | | |
| | | <!-- 主体:抽奖机会信息 --> |
| | | <view slot="body" class="card-body"> |
| | | <!-- 封面图片 --> |
| | | <view class="cover-image" v-if="prizeInfo.activityCover"> |
| | | <u-image |
| | | :src="prizeInfo.activityCover" |
| | | width="100%" |
| | | height="300rpx" |
| | | mode="aspectFill" |
| | | border-radius="10" |
| | | :fade="true" |
| | | duration="450" |
| | | ></u-image> |
| | | </view> |
| | | |
| | | <view class="prize-title"> |
| | | <text class="title">{{ prizeInfo.activityName || prizeInfo.prizeName || '抽奖机会' }}</text> |
| | | </view> |
| | | |
| | | <view class="prize-desc" v-if="prizeInfo.activityDes || prizeInfo.prizeDesc"> |
| | | <text class="desc">{{ prizeInfo.activityDes || prizeInfo.prizeDesc }}</text> |
| | | </view> |
| | | |
| | | <view class="prize-rule" v-if="prizeInfo.prizeRule"> |
| | | <text class="rule-title">使用说明:</text> |
| | | <text class="rule-content">{{ prizeInfo.prizeRule }}</text> |
| | | </view> |
| | | |
| | | <view class="prize-time" v-if="prizeInfo.beginTime && prizeInfo.endTime"> |
| | | <u-icon name="clock" size="28" color="#999"></u-icon> |
| | | <text class="time-text">有效期:{{ formatDate(prizeInfo.beginTime) }} 至 {{ formatDate(prizeInfo.endTime) }}</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 底部:操作按钮 --> |
| | | <view slot="foot" class="card-foot"> |
| | | <u-button |
| | | :type="prizeInfo.claimStatus === 'NOT_CLAIM' ? 'primary' : 'default'" |
| | | :disabled="prizeInfo.claimStatus !== 'NOT_CLAIM'" |
| | | :loading="loading" |
| | | @click="claimPrize" |
| | | :ripple="true" |
| | | :hair-line="false" |
| | | > |
| | | {{ prizeInfo.claimStatus === 'NOT_CLAIM' ? '立即领取' : prizeInfo.claimStatus === 'CLAIM' ? '已领取' : '已过期' }} |
| | | </u-button> |
| | | </view> |
| | | </u-card> |
| | | </view> |
| | | |
| | | <!-- 空状态 --> |
| | | <view class="empty-state" v-else> |
| | | <u-empty text="抽奖机会信息不存在" mode="coupon" :icon-size="200"></u-empty> |
| | | </view> |
| | | |
| | | <!-- 使用说明 --> |
| | | <view class="instructions"> |
| | | <view class="instructions-title"> |
| | | <u-icon name="info-circle" size="28" color="#999"></u-icon> |
| | | <text class="title-text">使用说明</text> |
| | | </view> |
| | | <view class="instructions-content"> |
| | | <text class="content-text">1. 抽奖机会仅限在指定店铺使用\n2. 抽奖机会不可转让\n3. 请在有效期内使用</text> |
| | | </view> |
| | | </view> |
| | | |
| | | <!-- 空白占位 --> |
| | | <view class="placeholder"></view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script> |
| | | import { getStorePrize, claimPrize } from '@/api/store-coupon.js'; |
| | | |
| | | export default { |
| | | data() { |
| | | return { |
| | | loading: false, |
| | | prizeId: '', // 抽奖机会ID |
| | | prizeInfo: { |
| | | id: "", |
| | | storeId: "", |
| | | storeName: "", |
| | | activityName: "", // 活动名称 |
| | | prizeName: "抽奖机会", |
| | | prizeNo: "", |
| | | activityDes: "", // 活动描述 |
| | | prizeDesc: "获得一次抽奖机会,可在指定活动中使用", // 抽奖机会描述 |
| | | prizeRule: "每次抽奖消耗一次抽奖机会,抽中奖品后需及时领取", // 使用规则 |
| | | beginTime: "", // 开始时间 |
| | | endTime: "", // 结束时间 |
| | | claimStatus: "NOT_CLAIM", // 领取状态 |
| | | activityCover: "", // 封面图片 |
| | | enableStatus: "ON" // 启用状态 |
| | | } |
| | | } |
| | | }, |
| | | onShow() { |
| | | if (this.prizeId) { |
| | | this.getPrizeDetail(this.prizeId); |
| | | } |
| | | }, |
| | | onLoad(options) { |
| | | console.log('页面参数:', JSON.stringify(options)); |
| | | // 获取传递的抽奖机会ID |
| | | if (options.id) { |
| | | this.prizeId = options.id; |
| | | this.getPrizeDetail(options.id); |
| | | } else if(options.q){ |
| | | // 双重解码:微信对URL进行了两次编码 |
| | | const decodedUrl = decodeURIComponent(decodeURIComponent(options.q)); |
| | | console.log('原始URL:', decodedUrl); |
| | | |
| | | // 解析URL中的查询参数 |
| | | const params = this.parseUrlParams(decodedUrl); |
| | | this.prizeId = params.id; |
| | | this.getPrizeDetail(this.prizeId); |
| | | } else { |
| | | this.$u.toast('参数错误'); |
| | | } |
| | | }, |
| | | methods: { |
| | | // 解析URL参数 |
| | | parseUrlParams(url) { |
| | | const params = {}; |
| | | // 处理可能存在的hash(如果有的话) |
| | | const cleanUrl = url.split('#')[0]; |
| | | const queryStr = cleanUrl.split('?')[1] || ''; |
| | | |
| | | queryStr.split('&').forEach(pair => { |
| | | const [key, value] = pair.split('='); |
| | | if (key) { |
| | | // 如果值存在,则解码,否则设为空字符串 |
| | | params[key] = value ? decodeURIComponent(value) : ''; |
| | | } |
| | | }); |
| | | |
| | | return params; |
| | | }, |
| | | |
| | | // 格式化日期 |
| | | formatDate(dateStr) { |
| | | if (!dateStr) return ''; |
| | | // 处理带时区的时间格式 |
| | | const date = new Date(dateStr); |
| | | if (isNaN(date.getTime())) { |
| | | // 如果日期解析失败,返回原始字符串的日期部分 |
| | | return dateStr.split('T')[0]; |
| | | } |
| | | // 格式化为 YYYY-MM-DD |
| | | const year = date.getFullYear(); |
| | | const month = String(date.getMonth() + 1).padStart(2, '0'); |
| | | const day = String(date.getDate()).padStart(2, '0'); |
| | | return `${year}-${month}-${day}`; |
| | | }, |
| | | |
| | | // 获取抽奖机会详情 |
| | | async getPrizeDetail(prizeId) { |
| | | uni.showLoading({ |
| | | title: '加载中...' |
| | | }); |
| | | |
| | | try { |
| | | console.log('请求奖品详情,ID:', prizeId); |
| | | const res = await getStorePrize(prizeId); |
| | | console.log('接口返回数据:', JSON.stringify(res)); |
| | | |
| | | if (res.statusCode === 200) { |
| | | // 根据你提供的接口数据结构处理 |
| | | if (res.data && res.data.code === 200 && res.data.data) { |
| | | this.prizeInfo = { |
| | | ...this.prizeInfo, |
| | | ...res.data.data, |
| | | // 确保字段映射正确 |
| | | id: res.data.data.id || "", |
| | | storeId: res.data.data.storeId || "", |
| | | storeName: res.data.data.storeName || "默认店铺", |
| | | activityName: res.data.data.activityName || "抽奖活动", |
| | | prizeName: res.data.data.prizeName || "抽奖机会", |
| | | prizeNo: res.data.data.no || "", |
| | | activityDes: res.data.data.activityDes || "获得一次抽奖机会,可在指定活动中使用", |
| | | prizeDesc: res.data.data.prizeDesc || "获得一次抽奖机会,可在指定活动中使用", |
| | | beginTime: res.data.data.beginTime || res.data.data.startTime || "", |
| | | endTime: res.data.data.endTime || "", |
| | | activityCover: res.data.data.activityCover || "", |
| | | enableStatus: res.data.data.enableStatus || "ON", |
| | | claimStatus: "NOT_CLAIM" // 默认为未领取状态 |
| | | }; |
| | | console.log('设置后的prizeInfo:', JSON.stringify(this.prizeInfo)); |
| | | } else { |
| | | this.$u.toast(res.data.message || '获取抽奖机会详情失败'); |
| | | } |
| | | } else { |
| | | this.$u.toast(res.data.message); |
| | | } |
| | | } catch (err) { |
| | | this.$u.toast('获取抽奖机会详情失败,请稍后重试'); |
| | | console.error('获取抽奖机会详情失败:', err.message); |
| | | } finally { |
| | | uni.hideLoading(); |
| | | } |
| | | }, |
| | | |
| | | // 领取抽奖机会 |
| | | async claimPrize() { |
| | | if (this.prizeInfo.claimStatus !== 'NOT_CLAIM') { |
| | | this.$u.toast('该抽奖机会无法领取'); |
| | | return; |
| | | } |
| | | |
| | | // 确认领取 |
| | | uni.showModal({ |
| | | title: '提示', |
| | | content: `确定要领取"${this.prizeInfo.activityName || this.prizeInfo.prizeName || '本次抽奖机会'}"吗?`, |
| | | success: (res) => { |
| | | if (res.confirm) { |
| | | this.doClaimPrize(); |
| | | } |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | // 执行领取抽奖机会操作 |
| | | async doClaimPrize() { |
| | | this.loading = true; |
| | | try { |
| | | // 调用领取抽奖机会接口 |
| | | const res = await claimPrize(this.prizeId); |
| | | console.log('领取接口返回:', JSON.stringify(res)); |
| | | |
| | | // 根据接口返回处理结果 |
| | | if (res.statusCode === 200) { |
| | | // 检查返回的数据结构 |
| | | if (res.data && res.data.code === 200) { |
| | | this.$u.toast('领取成功'); |
| | | this.prizeInfo.claimStatus = 'CLAIM'; |
| | | |
| | | // 延迟返回上一页,让用户看到领取成功的提示 |
| | | setTimeout(() => { |
| | | uni.navigateBack(); |
| | | }, 1500); |
| | | } else { |
| | | this.$u.toast(res.data.message || '领取失败'); |
| | | } |
| | | } else { |
| | | this.$u.toast('领取失败: ' + (res.data.message || '未知错误')); |
| | | } |
| | | } catch (err) { |
| | | this.$u.toast('领取失败,请稍后重试'); |
| | | console.error('领取抽奖机会失败:', err.message); |
| | | } finally { |
| | | this.loading = false; |
| | | } |
| | | } |
| | | } |
| | | } |
| | | </script> |
| | | |
| | | <style lang="scss" scoped> |
| | | .container { |
| | | background-color: #f5f5f5; |
| | | min-height: 100vh; |
| | | } |
| | | |
| | | .prize-card { |
| | | margin: 20rpx; |
| | | } |
| | | |
| | | .card-head { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .store-info { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .store-name { |
| | | margin-left: 10rpx; |
| | | font-size: 32rpx; |
| | | font-weight: bold; |
| | | color: #333; |
| | | } |
| | | |
| | | .card-body { |
| | | padding-top: 20rpx; |
| | | } |
| | | |
| | | .cover-image { |
| | | margin-bottom: 30rpx; |
| | | } |
| | | |
| | | .prize-title { |
| | | margin-bottom: 30rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | flex-wrap: wrap; |
| | | } |
| | | |
| | | .title { |
| | | font-size: 36rpx; |
| | | font-weight: bold; |
| | | color: #333; |
| | | } |
| | | |
| | | .prize-desc { |
| | | margin-bottom: 30rpx; |
| | | } |
| | | |
| | | .desc { |
| | | font-size: 28rpx; |
| | | color: #666; |
| | | } |
| | | |
| | | .prize-rule { |
| | | margin-bottom: 30rpx; |
| | | } |
| | | |
| | | .rule-title { |
| | | font-size: 28rpx; |
| | | color: #333; |
| | | font-weight: bold; |
| | | } |
| | | |
| | | .rule-content { |
| | | font-size: 26rpx; |
| | | color: #666; |
| | | } |
| | | |
| | | .prize-time { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | .time-text { |
| | | font-size: 24rpx; |
| | | color: #999; |
| | | margin-left: 10rpx; |
| | | } |
| | | |
| | | .card-foot { |
| | | padding: 20rpx 0; |
| | | } |
| | | |
| | | .instructions { |
| | | background-color: #ffffff; |
| | | margin: 20rpx; |
| | | padding: 30rpx; |
| | | border-radius: 16rpx; |
| | | } |
| | | |
| | | .instructions-title { |
| | | display: flex; |
| | | align-items: center; |
| | | margin-bottom: 20rpx; |
| | | } |
| | | |
| | | .title-text { |
| | | margin-left: 10rpx; |
| | | font-size: 30rpx; |
| | | font-weight: bold; |
| | | color: #333; |
| | | } |
| | | |
| | | .instructions-content { |
| | | padding-left: 40rpx; |
| | | } |
| | | |
| | | .content-text { |
| | | font-size: 26rpx; |
| | | color: #666; |
| | | line-height: 1.6; |
| | | } |
| | | |
| | | .placeholder { |
| | | height: 40rpx; |
| | | } |
| | | |
| | | .empty-state { |
| | | margin-top: 200rpx; |
| | | } |
| | | </style> |