Просмотр исходного кода

feat: 完善红包业务功能,优化列表布局与分享配置

新增默认分享图片,调整分享链接携带红包编号参数
优化售卖页书籍列表布局,将分页大小从5调整为10
为红包页面添加错误状态页、活动规则弹窗,完善领取逻辑与现金红包确认流程
补充分享朋友圈配置,移除冗余的路由跳转逻辑
修复红包领取按钮的空指针判断,优化样式细节
ylong 1 месяц назад
Родитель
Сommit
0548bd4ee3

+ 0 - 34
App.vue

@@ -127,46 +127,12 @@ export default {
 			if (!Object.keys(params).length) return;
 
 			this.cacheEntryParams(params);
-			if (params.bianhao) {
-				this.routeToPacketIndex(params.bianhao);
-			}
 			if (this.globalData.isColdLaunch) return;
 
 			if (params.upsellCode || params.reduceCode) {
 				eventBus.emit('appShowActivity', { params });
 			}
 		},
-		routeToPacketIndex(bianhao) {
-			const targetUrl = '/packet/pages/index?bianhao=' + bianhao;
-			setTimeout(() => {
-				const pages = getCurrentPages();
-				const currentPage = pages[pages.length - 1];
-				const currentRoute = currentPage?.route || '';
-				const currentBianhao = currentPage?.options?.bianhao || '';
-
-				if (currentRoute === 'packet/pages/index' && currentBianhao === String(bianhao)) {
-					return;
-				}
-
-				if (!pages.length || this.globalData.isColdLaunch) {
-					uni.reLaunch({
-						url: targetUrl
-					});
-					return;
-				}
-
-				if (currentRoute === 'packet/pages/index') {
-					uni.redirectTo({
-						url: targetUrl
-					});
-					return;
-				}
-
-				uni.navigateTo({
-					url: targetUrl
-				});
-			}, 0);
-		},
 		slientLogin(params = {}) {
 			let that = this;
 			uni.login({

+ 10 - 2
packet/pages/get.vue

@@ -102,7 +102,15 @@ export default {
         return {
             title: '好友助力解锁最高' + (this.redBagData?.shareSumPrice) + '元红包',
             path: '/packet/pages/index?bianhao=' + this.bianhao,
-            imageUrl: '' // 使用默认分享图
+            imageUrl: '../static/index/share.png'
+        };
+    },
+    onShareTimeline() {
+        // 设置分享内容
+        return {
+            title: '好友助力解锁最高' + (this.redBagData?.shareSumPrice) + '元红包',
+            path: '/packet/pages/index?bianhao=' + this.bianhao,
+            imageUrl: '../static/index/share.png'
         };
     },
     methods: {
@@ -433,4 +441,4 @@ export default {
         }
     }
 }
-</style>
+</style>

+ 346 - 14
packet/pages/index.vue

@@ -1,10 +1,23 @@
 <template>
     <view class="packet-page">
         <!-- Background -->
-        <image src="/packet/static/index/background.png" class="bg-image" mode="widthFix"></image>
+        <image v-if="!isFailState" src="/packet/static/index/background.png" class="bg-image" mode="widthFix"></image>
 
         <!-- Main Content -->
-        <view class="content-wrapper">
+        <view v-if="isFailState" class="fail-wrapper">
+            <view class="fail-card">
+                <view class="fail-icon" :class="'fail-icon--' + failState.iconType">
+                    <text class="fail-icon__text">{{ failState.iconText }}</text>
+                </view>
+                <view class="fail-title">{{ failState.title }}</view>
+                <view class="fail-desc">{{ failState.desc }}</view>
+                <view class="fail-btn" @click="goHome">
+                    <text>进入首页</text>
+                </view>
+            </view>
+        </view>
+
+        <view v-else class="content-wrapper">
             <!-- Headline -->
             <image src="/packet/static/index/headline.png" class="headline-image" mode="widthFix"></image>
 
@@ -24,7 +37,8 @@
                 <!-- Receive Button -->
                 <view class="receive-btn-wrapper" @click="handleReceive">
                     <image src="/packet/static/index/btn_receive.png" class="btn-image" mode="widthFix"></image>
-                    <text class="btn-text" v-if="!redBagData.drawStatus">领{{ amount }}元红包 | 买书卖书都能用</text>
+                    <text class="btn-text" v-if="!redBagData || !redBagData.drawStatus">领{{ amount }}元红包 |
+                        买书卖书都能用</text>
                     <text class="btn-text" v-else>已领取</text>
                 </view>
             </view>
@@ -48,12 +62,29 @@
                             <text>无门槛使用</text>
                         </view>
                     </view>
-                    <view class="rule-footer">
+                    <view class="rule-footer" @click="openRulePopup">
                         <text class="rule-text">活动规则</text>
                     </view>
                 </view>
             </view>
         </view>
+
+        <u-popup v-model="showRulePopup" mode="center" border-radius="24" :mask-close-able="true">
+            <view class="rule-popup">
+                <view class="rule-popup__header">
+                    <text class="rule-popup__title">活动规则</text>
+                    <view class="rule-popup__close" @click="closeRulePopup">x</view>
+                </view>
+                <scroll-view scroll-y class="rule-popup__body">
+                    <view class="rule-popup__item" v-for="(item, index) in ruleList" :key="index">
+                        <text>{{ index + 1 }}.{{ item }}</text>
+                    </view>
+                </scroll-view>
+                <view class="rule-popup__footer" @click="closeRulePopup">
+                    <text class="rule-popup__btn">我知道了</text>
+                </view>
+            </view>
+        </u-popup>
     </view>
 </template>
 
@@ -65,7 +96,22 @@
             return {
                 amount: '1.58',
                 bianhao: '', // 红包编号
-                redBagData: null // 红包数据
+                redBagData: null, // 红包数据
+                failState: null, // 扫码失败态
+                showRulePopup: false,
+                ruleList: [
+                    '活动时间:2026年05月01日至2028年05月30日。',
+                    '活动期间,用户扫描书嗨专属红包码,均有机会获得书嗨无门槛红包或现金红包。',
+                    '每位用户每获得一个红包有一次机会成为发起者。每人最多可赠送3位好友,分享者和好友均可获得书嗨无门槛红包或现金红包。',
+                    '助力者仅限新用户。',
+                    '助力红包发放时间:活动时间内,点击领取红包券,会实时到账小程序红包。',
+                    '无门槛红包可用于买书或卖书,买书订单将抵扣消费金额,卖书订单将加于书款上。'
+                ]
+            }
+        },
+        computed: {
+            isFailState() {
+                return !!this.failState;
             }
         },
         onLoad(options) {
@@ -102,6 +148,92 @@
             }
         },
         methods: {
+            getCantTypeValue(source = {}) {
+                const rawCantType = source.cantType;
+                if (rawCantType === undefined || rawCantType === null || rawCantType === '') {
+                    return null;
+                }
+                const cantType = Number(rawCantType);
+                return Number.isNaN(cantType) ? null : cantType;
+            },
+            buildFailState(source = {}, fallbackMsg = '') {
+                const cantType = this.getCantTypeValue(source);
+                if (cantType === null) {
+                    return null;
+                }
+                const scanCount = source.scanNum || source.scanCount || source.usedCount || source.count || '';
+                const failStateMap = {
+                    0: {
+                        iconType: 'warn',
+                        iconText: '!',
+                        title: scanCount ? `此二维码已被扫 ${scanCount} 次` : '扫码次数超上限',
+                        desc: scanCount ? '该二维码可参与次数已用完,请更换二维码后重试。' : '该二维码可参与次数已达上限,请更换二维码后重试。'
+                    },
+                    1: {
+                        iconType: 'ban',
+                        iconText: 'x',
+                        title: '扫码禁用',
+                        desc: '您的扫码功能已被禁用,当前无法领取红包。'
+                    },
+                    3: {
+                        iconType: 'warn',
+                        iconText: '!',
+                        title: '系统繁忙',
+                        desc: fallbackMsg || '系统开小差了,请稍后再试。'
+                    },
+                    4: {
+                        iconType: 'warn',
+                        iconText: '!',
+                        title: '活动已结束',
+                        desc: '很抱歉,当前活动已结束,无法继续领取红包。'
+                    },
+                    5: {
+                        iconType: 'warn',
+                        iconText: '!',
+                        title: '您不是新用户',
+                        desc: '抱歉!您不是新用户,无法领取分享红包。'
+                    },
+                    6: {
+                        iconType: 'warn',
+                        iconText: '!',
+                        title: '分享超限',
+                        desc: '当前分享次数已达上限,无法继续领取分享红包。'
+                    },
+                    7: {
+                        iconType: 'warn',
+                        iconText: '!',
+                        title: '二维码已使用',
+                        desc: '当前二维码已被使用,不能重复领取。'
+                    }
+                };
+                return failStateMap[cantType] || {
+                    iconType: 'warn',
+                    iconText: '!',
+                    title: '领取失败',
+                    desc: fallbackMsg || '当前无法领取红包,请稍后再试。'
+                };
+            },
+            applyCantTypeState(source = {}, fallbackMsg = '') {
+                const failState = this.buildFailState(source, fallbackMsg);
+                if (!failState) {
+                    return false;
+                }
+                this.failState = failState;
+                this.redBagData = null;
+                uni.hideLoading();
+                return true;
+            },
+            goHome() {
+                uni.switchTab({
+                    url: '/pages/sell/index'
+                });
+            },
+            openRulePopup() {
+                this.showRulePopup = true;
+            },
+            closeRulePopup() {
+                this.showRulePopup = false;
+            },
             // 请求前确保 token 可用,避免扫码冷启动时出现 401
             async ensureTokenReady() {
                 const token = uni.getStorageSync('token')
@@ -136,7 +268,11 @@
 
                 this.$u.api.scanRedBagAjax(this.bianhao).then(res => {
                     console.log('扫码获取红包信息成功:', res);
+                    if (this.applyCantTypeState(res.data || res, res.msg)) {
+                        return;
+                    }
                     if (res.code === 200) {
+                        this.failState = null;
                         this.redBagData = res.data;
                         this.amount = res.data.redPrice;
                     } else {
@@ -149,15 +285,6 @@
             },
             // 领取红包
             async handleReceive() {
-                // 如果已领取,提示用户
-                if (this.redBagData && this.redBagData.drawStatus === 1) {
-                    uni.showToast({
-                        title: '红包已领取',
-                        icon: 'none'
-                    });
-                    return;
-                }
-
                 if (!this.bianhao) {
                     uni.showToast({
                         title: '红包编号缺失',
@@ -183,9 +310,19 @@
                 try {
                     const res = await this.$u.api.getRedBagAjax(this.bianhao);
                     uni.hideLoading();
+                    if (this.applyCantTypeState(res.data || res, res.msg)) {
+                        return;
+                    }
                     if (res.code === 200) {
                         // 更新红包数据
+                        this.failState = null;
                         this.redBagData = res.data;
+
+                        //如果是现金红包,执行确认收款操作
+                        if (res.data.redType == 1) {
+                            return this.handleConfirmReceipt(res.data)
+                        }
+
                         uni.showToast({
                             title: '领取成功',
                             icon: 'success'
@@ -211,6 +348,36 @@
                     });
                 }
             },
+            //执行微信确认收款操作
+            handleConfirmReceipt(data) {
+                if (wx.canIUse('requestMerchantTransfer')) {
+                    wx.requestMerchantTransfer({
+                        mchId: data.mchId,
+                        appId: data.appId,
+                        package: data.packageInfo,
+                        success: (res) => {
+                            // res.err_msg将在页面展示成功后返回应用时返回ok,并不代表付款成功
+                            uni.showToast({
+                                title: '确认收款成功',
+                                icon: 'none'
+                            });
+                            //跳转到首页
+                            uni.switchTab({
+                                url: '/pages/sell/index'
+                            });
+                        },
+                        fail: (res) => {
+                            console.log('fail:', res);
+                        }
+                    });
+                } else {
+                    wx.showModal({
+                        content: '你的微信版本过低,请更新至最新版本。',
+                        showCancel: false
+                    });
+                }
+            },
+
             handleShare() {
                 // Share logic
             }
@@ -226,6 +393,7 @@
         flex-direction: column;
         align-items: center;
         overflow: hidden;
+        background: #FFFFFF;
 
         .bg-image {
             position: absolute;
@@ -299,6 +467,7 @@
                     }
 
                     .btn-text {
+                        display: inline-block;
                         position: absolute;
                         top: 45%;
                         left: 50%;
@@ -307,6 +476,7 @@
                         color: #D73800;
                         font-weight: 500;
                         white-space: nowrap;
+                        word-break: keep-all;
                     }
                 }
             }
@@ -356,6 +526,7 @@
                         justify-content: center;
                         align-items: center;
                         margin-top: 30rpx;
+                        cursor: pointer;
 
                         .rule-text {
                             font-size: 26rpx;
@@ -392,4 +563,165 @@
             }
         }
     }
+
+    .fail-wrapper {
+        width: 100%;
+        min-height: 100vh;
+        padding: 120rpx 48rpx 80rpx;
+        box-sizing: border-box;
+        display: flex;
+        justify-content: center;
+        align-items: flex-start;
+        background: #FFFFFF;
+    }
+
+    .fail-card {
+        width: 100%;
+        background: transparent;
+        border-radius: 0;
+        padding: 0;
+        box-sizing: border-box;
+        display: flex;
+        flex-direction: column;
+        align-items: center;
+        box-shadow: none;
+    }
+
+    .fail-icon {
+        width: 144rpx;
+        height: 144rpx;
+        border-radius: 72rpx;
+        display: flex;
+        align-items: center;
+        justify-content: center;
+        margin-bottom: 36rpx;
+        border: 6rpx solid #FF5A5F;
+        box-sizing: border-box;
+
+        &--ban {
+            border-color: #FF9F1A;
+            background: rgba(255, 159, 26, 0.08);
+        }
+
+        &--warn {
+            border-color: #F34A45;
+            background: rgba(243, 74, 69, 0.06);
+        }
+
+        &__text {
+            font-size: 76rpx;
+            font-weight: 600;
+            line-height: 1;
+            color: #F34A45;
+            text-transform: uppercase;
+        }
+
+        &--ban &__text {
+            color: #FF9F1A;
+        }
+    }
+
+    .fail-title {
+        font-size: 40rpx;
+        line-height: 56rpx;
+        font-weight: 600;
+        color: #222222;
+        text-align: center;
+        margin-bottom: 24rpx;
+    }
+
+    .fail-desc {
+        font-size: 30rpx;
+        line-height: 46rpx;
+        color: #666666;
+        text-align: center;
+        margin-bottom: 72rpx;
+        white-space: pre-wrap;
+    }
+
+    .fail-btn {
+        width: 100%;
+        max-width: 520rpx;
+        height: 88rpx;
+        border-radius: 44rpx;
+        background: linear-gradient(90deg, #FF453A 0%, #F5221E 100%);
+        display: flex;
+        align-items: center;
+        justify-content: center;
+
+        text {
+            color: #FFFFFF;
+            font-size: 32rpx;
+            font-weight: 500;
+        }
+    }
+
+    .rule-popup {
+        width: 640rpx;
+        background: #FFFFFF;
+        border-radius: 24rpx;
+        padding: 36rpx 32rpx 30rpx;
+        box-sizing: border-box;
+
+        &__header {
+            position: relative;
+            display: flex;
+            justify-content: center;
+            align-items: center;
+            margin-bottom: 28rpx;
+        }
+
+        &__title {
+            font-size: 36rpx;
+            font-weight: 600;
+            color: #222222;
+        }
+
+        &__close {
+            position: absolute;
+            right: 0;
+            top: 50%;
+            transform: translateY(-50%);
+            width: 44rpx;
+            height: 44rpx;
+            line-height: 44rpx;
+            text-align: center;
+            border-radius: 22rpx;
+            background: #F2F3F5;
+            color: #666666;
+            font-size: 26rpx;
+        }
+
+        &__body {
+            max-height: 720rpx;
+        }
+
+        &__item {
+            font-size: 28rpx;
+            color: #333333;
+            line-height: 46rpx;
+            margin-bottom: 20rpx;
+            word-break: break-all;
+        }
+
+        &__footer {
+            display: flex;
+            justify-content: center;
+            margin-top: 16rpx;
+        }
+
+        &__btn {
+            min-width: 240rpx;
+            height: 72rpx;
+            line-height: 72rpx;
+            text-align: center;
+            background: linear-gradient(90deg, #6ADD83 0%, #38C148 100%);
+            border-radius: 36rpx;
+            color: #FFFFFF;
+            font-size: 30rpx;
+            font-weight: 500;
+            padding: 0 30rpx;
+            box-sizing: border-box;
+        }
+    }
 </style>

+ 2 - 2
packet/pages/share.vue

@@ -72,8 +72,8 @@ export default {
         // 设置分享内容 - 使用对应卡片的数据
         return {
             title: '邀请好友同享最高可得' + (item?.shareSumPrice) + '元红包',
-            path: '/packet/pages/share',
-            imageUrl: '' // 使用默认分享图
+            path: '/packet/pages/index?bianhao=' + item.bianhao,
+            imageUrl: '../static/index/share.png' // 使用默认分享图
         };
     },
     methods: {

BIN
packet/static/index/share.png


+ 3 - 2
pages-sell/components/sell-container/index.vue

@@ -894,12 +894,13 @@ export default {
 	.book-list {
 		display: flex;
 		flex-wrap: wrap;
-		justify-content: space-between;
+		justify-content: flex-start;
+		column-gap: 15rpx;
+		row-gap: 24rpx;
 	}
 
 	.book-item {
 		width: 200rpx;
-		margin-bottom: 24rpx;
 		display: flex;
 		flex-direction: column;
 

+ 1 - 1
pages-sell/pages/topic.vue

@@ -44,7 +44,7 @@
 			return {
 				cateId: '',
 				pageNum: 1,
-				pageSize: 5,
+				pageSize: 10,
 				total: 0,
 				bookList: [],
 				showName: '',