绿满眶商城微信小程序-uniapp
peng
10 小时以前 c6bddd39c91f2d411316a78ed27b466488c2a39e
店铺扫码领取抽奖机会
2个文件已修改
1个文件已添加
443 ■■■■■ 已修改文件
api/store-coupon.js 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages.json 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pages/storeClaim/storePrizeClaim.vue 401 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
api/store-coupon.js
@@ -24,4 +24,29 @@
    method: Method.POST,
    needToken: true,
  });
}
/**
 * 根据店铺优惠券关联ID获取优惠券详情
 * @param {string|number} storeCoupRef - 店铺优惠券关联ID
 * @returns {Promise} 返回优惠券详情
 */
export function getStorePrize(storeCoupRef) {
  return http.request({
    url: `/lmk/store/prize/${storeCoupRef}`,
    method: Method.GET,
    needToken: true,
  });
}
/**
 * 领取店铺优惠券
 * @param {string|number} storeCoupRef - 店铺优惠券关联ID
 * @returns {Promise} 返回领取结果
 */
export function claimPrize(storeCoupRef) {
  return http.request({
    url: `/lmk/store/prize/${storeCoupRef}`,
    method: Method.POST,
    needToken: true,
  });
}
pages.json
@@ -2272,6 +2272,23 @@
                
            }
          }
      },
      {
          "path" : "storePrizeClaim",
          "style" :
          {
              "navigationBarTitleText" : "领取抽奖机会",
            "componentPlaceholder":{
                "u-card": "view",
                "u-navbar": "view",
                "u-tag": "view",
                "u-icon": "view",
                "u-button": "view",
                "u-empty": "view",
                "u-image":"view"
            }
          }
      }]
    }
    
pages/storeClaim/storePrizeClaim.vue
New file
@@ -0,0 +1,401 @@
<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>