Răsfoiți Sursa

feat: 添加二维码跳转兼容处理和到货通知功能

- 在 App.vue 中添加二维码参数处理,支持商品详情和红包跳转
- 实现商品列表和热门推荐的到货通知功能
- 优化订单详情和我的订单页面的地址修改提示
- 添加优惠金额列表展示和状态显示优化
ylong 1 săptămână în urmă
părinte
comite
1781af333e

+ 9 - 0
App.vue

@@ -51,6 +51,15 @@ export default {
 					if (params.upsellCode) {
 						uni.setStorageSync('upsellCode', params.upsellCode)
 					}
+					// 兼容商品详情二维码跳转
+					if (params.isbn) {
+						// 将 isbn 存入全局或缓存,以便 detail 页面如果在 onLoad 没拿到时可以使用
+						uni.setStorageSync('scene_isbn', params.isbn);
+					}
+					// 兼容红包二维码跳转
+					if (params.bianhao) {
+						uni.setStorageSync('scene_bianhao', params.bianhao);
+					}
 					this.slientLogin(params);
 				} else {
 					this.slientLogin();

+ 22 - 3
packet/pages/index.vue

@@ -67,9 +67,28 @@ export default {
         }
     },
     onLoad(options) {
-        // 从二维码扫描进入时,获取编号参数
-        if (options.bianhao) {
-            this.bianhao = options.bianhao;
+        let bianhao = options.bianhao;
+        
+        // 兼容扫码进入场景,当前页的 scene 里带了 bianhao
+        if (!bianhao && options.scene) {
+            const decodeScene = decodeURIComponent(options.scene);
+            const paramPairs = decodeScene.split('&');
+            paramPairs.forEach((pair) => {
+                const [key, value] = pair.split('=');
+                if (key === 'bianhao') {
+                    bianhao = value;
+                }
+            });
+        }
+
+        // 兼容从 App.vue 缓存中获取 (因为如果是扫小程序码直接进入这个页面,App.vue的onLaunch会先执行并存入缓存,然后执行页面的onLoad)
+        if (!bianhao) {
+            bianhao = uni.getStorageSync('scene_bianhao');
+        }
+
+        if (bianhao) {
+            this.bianhao = bianhao;
+            uni.removeStorageSync('scene_bianhao');
             // 调用扫码接口获取红包信息
             this.scanRedBag();
         }

+ 7 - 1
pages-car/components/buy-order-item.vue

@@ -3,7 +3,9 @@
         <!-- 订单头部:订单号和状态 -->
         <view class="order-header">
             <text class="order-no">订单号:{{ order.orderId }}</text>
-            <text class="order-status">{{ getStatusText(order) }}</text>
+
+            <text class="order-status refund-status" v-if="order.refundStatus == 3">已退款</text>
+            <text class="order-status" v-else>{{ getStatusText(order) }}</text>
         </view>
 
         <!-- 商品信息 -->
@@ -228,6 +230,10 @@
             .order-status {
                 color: #999;
             }
+
+            .refund-status {
+                color: #FF4D4F;
+            }
         }
 
         .goods-list {

+ 24 - 6
pages-car/pages/my-order.vue

@@ -32,6 +32,11 @@
 
         <!-- 取消订单弹窗 -->
         <cancel-order-popup ref="cancelDialog" @success="loadOrders(true, params)"></cancel-order-popup>
+
+        <!-- 修改地址提示弹窗 -->
+        <common-dialog ref="addressDialog" title="温馨提示" :showCancel="false" confirmText="我知道了">
+            <view style="padding: 40rpx 0;">{{ addressDialogContent }}</view>
+        </common-dialog>
     </view>
 </template>
 
@@ -42,6 +47,7 @@ import pageScroll from '@/components/pageScroll/index.vue';
 import FastRefundDialog from '../components/fast-refund-dialog.vue';
 import UrgeDeliveryDialog from '../components/urge-delivery-dialog.vue';
 import CancelOrderPopup from '../components/cancel-order-popup.vue';
+import CommonDialog from '@/components/common-dialog.vue';
 
 export default {
     components: {
@@ -50,7 +56,8 @@ export default {
         pageScroll,
         FastRefundDialog,
         UrgeDeliveryDialog,
-        CancelOrderPopup
+        CancelOrderPopup,
+        CommonDialog
     },
     data() {
         return {
@@ -70,7 +77,8 @@ export default {
             showActionSheet: false,
             actionSheetList: [],
             currentOrder: null,
-            modifyingOrderId: null
+            modifyingOrderId: null,
+            addressDialogContent: ''
         };
     },
     onLoad(options) {
@@ -105,10 +113,20 @@ export default {
                 }).then(res => {
                     uni.hideLoading();
                     if (res.code == 200) {
-                        uni.showToast({ title: '修改成功', icon: 'success' });
-                        setTimeout(() => {
-                            this.loadOrders(true, this.params);
-                        }, 1000)
+                        if (res.data == 1) {
+                            uni.showToast({ title: '修改成功', icon: 'success' });
+                            setTimeout(() => {
+                                this.loadOrders(true, this.params);
+                            }, 1000)
+                        } else if (res.data == 2) {
+                            this.addressDialogContent = '当前区域暂时不支持购买';
+                            this.$refs.addressDialog.openPopup();
+                        } else if (res.data == 3) {
+                            this.addressDialogContent = '运费不一致,不支持修改到该地址';
+                            this.$refs.addressDialog.openPopup();
+                        } else {
+                            uni.showToast({ title: res.msg || '修改失败', icon: 'none' });
+                        }
                     }
                 }).finally(() => {
                     this.modifyingOrderId = null;

+ 36 - 9
pages-car/pages/order-detail.vue

@@ -93,10 +93,18 @@
                     <text class="goods-total">¥{{ (orderInfo.totalMoney - (orderInfo.expressMoney || 0)).toFixed(2)
                         }}</text>
                 </view>
-                <view class="row">
-                    <text>优惠金额</text>
-                    <text class="discount">-¥{{ orderInfo.discountMoney || '0.00' }}</text>
-                </view>
+                <template v-if="orderInfo.discountList && orderInfo.discountList.length > 0">
+                    <view class="row" v-for="(discount, index) in orderInfo.discountList" :key="'discount_' + index">
+                        <text>{{ discount.discountActivityMsg || '优惠金额' }}</text>
+                        <text class="discount">-¥{{ discount.discountMoney || '0.00' }}</text>
+                    </view>
+                </template>
+                <template v-else>
+                    <view class="row">
+                        <text>优惠金额</text>
+                        <text class="discount">-¥{{ orderInfo.discountMoney || '0.00' }}</text>
+                    </view>
+                </template>
                 <view class="divider"></view>
                 <view class="real-pay-row">
                     <text>实付款 ({{ orderInfo.payType == 1 ? '余额支付' : '微信支付' }})</text>
@@ -141,6 +149,11 @@
         <!-- 极速退款弹窗 -->
         <fast-refund-dialog ref="refundDialog" @refresh="loadOrderDetail(orderInfo.orderId)"></fast-refund-dialog>
 
+        <!-- 修改地址提示弹窗 -->
+        <common-dialog ref="addressDialog" title="温馨提示" :showCancel="false" confirmText="我知道了">
+            <view style="padding: 40rpx 0;">{{ addressDialogContent }}</view>
+        </common-dialog>
+
         <!-- 占位符,防止底部按钮遮挡内容 -->
         <view style="height: 120rpx;"></view>
     </view>
@@ -151,13 +164,15 @@ import OrderBottomBar from '../components/order-bottom-bar.vue';
 import CancelOrderPopup from '../components/cancel-order-popup.vue';
 import UrgeDeliveryDialog from '../components/urge-delivery-dialog.vue';
 import FastRefundDialog from '../components/fast-refund-dialog.vue';
+import CommonDialog from '@/components/common-dialog.vue';
 
 export default {
     components: {
         OrderBottomBar,
         CancelOrderPopup,
         UrgeDeliveryDialog,
-        FastRefundDialog
+        FastRefundDialog,
+        CommonDialog
     },
     data() {
         return {
@@ -172,11 +187,13 @@ export default {
                 expressMoney: 0,
                 totalMoney: 0,
                 discountMoney: 0,
+                discountList: [],
                 payMoney: 0,
                 createTime: ''
             },
             latestTrace: null,
             isModifyingAddress: false,
+            addressDialogContent: ''
         };
     },
     computed: {
@@ -226,10 +243,20 @@ export default {
                 }).then(res => {
                     uni.hideLoading();
                     if (res.code == 200) {
-                        uni.showToast({ title: '修改成功', icon: 'success' });
-                        setTimeout(() => {
-                            this.loadOrderDetail(this.orderInfo.orderId);
-                        }, 1000);
+                        if (res.data == 1) {
+                            uni.showToast({ title: '修改成功', icon: 'success' });
+                            setTimeout(() => {
+                                this.loadOrderDetail(this.orderInfo.orderId);
+                            }, 1000);
+                        } else if (res.data == 2) {
+                            this.addressDialogContent = '当前区域暂时不支持购买';
+                            this.$refs.addressDialog.openPopup();
+                        } else if (res.data == 3) {
+                            this.addressDialogContent = '运费不一致,不支持修改到该地址';
+                            this.$refs.addressDialog.openPopup();
+                        } else {
+                            uni.showToast({ title: res.msg || '修改失败', icon: 'none' });
+                        }
                     }
                 }).finally(() => {
                     this.isModifyingAddress = false;

+ 33 - 2
pages-sell/components/hot-recommend-item/index.vue

@@ -15,8 +15,10 @@
 						<text class="discount-desc">省{{ item.discountPrice }}元</text>
 					</view>
 
-					<view class="btn-cart" @click.stop="handleAddToCart">
-						<text>加入购物车</text>
+					<view class="btn-cart" :class="{ 'gray-btn': item.availableStock === 0 && item.hasArrivalNotice === 1 }" @click.stop="handleAction">
+						<text v-if="item.availableStock === 0 && item.hasArrivalNotice === 1">取消到货通知</text>
+						<text v-else-if="item.availableStock === 0">到货通知</text>
+						<text v-else>加入购物车</text>
 					</view>
 				</view>
 			</view>
@@ -48,6 +50,31 @@ export default {
 				url: '/pages-sell/pages/detail?isbn=' + this.item.isbn
 			});
 		},
+		handleAction() {
+			if (this.item.availableStock === 0) {
+				this.handleNotify();
+			} else {
+				this.handleAddToCart();
+			}
+		},
+		handleNotify() {
+			const isCancel = this.item.hasArrivalNotice === 1;
+			const apiUrl = isCancel ? '/token/shop/user/noticeArrivalCancel' : '/token/shop/user/noticeArrival';
+
+			uni.$u.http.post(apiUrl, {
+				isbn: this.item.isbn,
+			}).then(res => {
+				if (res.code === 200) {
+					const newValue = isCancel ? 0 : 1;
+					// Notify parent component to update the item
+					this.$emit('update-item', { ...this.item, hasArrivalNotice: newValue });
+					// Try updating local object properties directly (Vue reactivity)
+					this.$set(this.item, 'hasArrivalNotice', newValue);
+					this.item.hasArrivalNotice = newValue;
+					this.$u.toast(isCancel ? '已取消到货通知' : '到货通知设置成功');
+				}
+			});
+		},
 		handleAddToCart() {
 			// 加入购物车时,传递 sourceFrom 参数为 1
 			this.$refs.popup.open(this.item, 1);
@@ -146,6 +173,10 @@ export default {
 			justify-content: center;
 			padding: 0 20rpx;
 
+			&.gray-btn {
+				background: #cccccc;
+			}
+
 			text {
 				font-size: 28rpx;
 				color: #fff;

+ 33 - 2
pages-sell/components/recommend-item/index.vue

@@ -19,8 +19,10 @@
 						<text class="original">¥{{ item.originalPrice || item.price }}</text>
 					</view>
 
-					<view class="cart-btn" @click.stop="handleAddToCart">
-						<text>加入购物车</text>
+					<view class="cart-btn" :class="{ 'gray-btn': item.availableStock === 0 && item.hasArrivalNotice === 1 }" @click.stop="handleAction">
+						<text v-if="item.availableStock === 0 && item.hasArrivalNotice === 1">取消到货通知</text>
+						<text v-else-if="item.availableStock === 0">到货通知</text>
+						<text v-else>加入购物车</text>
 					</view>
 				</view>
 			</view>
@@ -74,6 +76,31 @@
 				if (!text) return '';
 				return text.replace(/<em>/g, '<span style="color: #38C148">').replace(/<\/em>/g, '</span>');
 			},
+			handleAction() {
+				if (this.item.availableStock === 0) {
+					this.handleNotify();
+				} else {
+					this.handleAddToCart();
+				}
+			},
+			handleNotify() {
+				const isCancel = this.item.hasArrivalNotice === 1;
+				const apiUrl = isCancel ? '/token/shop/user/noticeArrivalCancel' : '/token/shop/user/noticeArrival';
+
+				uni.$u.http.post(apiUrl, {
+					isbn: this.item.isbn,
+				}).then(res => {
+					if (res.code === 200) {
+						const newValue = isCancel ? 0 : 1;
+						// Notify parent component to update the item
+						this.$emit('update-item', { ...this.item, hasArrivalNotice: newValue });
+						// Try updating local object properties directly (Vue reactivity)
+						this.$set(this.item, 'hasArrivalNotice', newValue);
+						this.item.hasArrivalNotice = newValue;
+						this.$u.toast(isCancel ? '已取消到货通知' : '到货通知设置成功');
+					}
+				});
+			},
 			handleAddToCart() {
 				// Open the popup using ref
 				// 加入购物车时,传递 sourceFrom 参数为 2
@@ -166,6 +193,10 @@
 						line-height: 60rpx;
 						padding: 0 24rpx;
 
+						&.gray-btn {
+							background: #cccccc;
+						}
+
 						text {
 							font-size: 26rpx;
 							color: #fff;

+ 21 - 1
pages-sell/pages/detail.vue

@@ -134,8 +134,28 @@ export default {
         }
     },
     onLoad(options) {
-        const isbn = options.isbn || options.id; // Support both just in case
+        let isbn = options.isbn || options.id; // Support both just in case
+        
+        // 兼容扫码进入场景,App.vue 可能会把 isbn 存入全局,或者当前页的 scene 里带了 isbn
+        if (!isbn && options.scene) {
+            const decodeScene = decodeURIComponent(options.scene);
+            const paramPairs = decodeScene.split('&');
+            paramPairs.forEach((pair) => {
+                const [key, value] = pair.split('=');
+                if (key === 'isbn') {
+                    isbn = value;
+                }
+            });
+        }
+        
+        // 兼容从 App.vue 缓存中获取 (因为如果是扫小程序码直接进入这个页面,App.vue的onLaunch会先执行并存入缓存,然后执行页面的onLoad)
+        if (!isbn) {
+            isbn = uni.getStorageSync('scene_isbn');
+        }
+
         if (isbn) {
+            // 使用完后清除缓存,防止影响后续正常跳转
+            uni.removeStorageSync('scene_isbn');
             this.getBookDetail(isbn);
         } else {
             uni.showToast({

+ 8 - 2
pages-sell/pages/search-result.vue

@@ -22,7 +22,7 @@
             <!-- Everyone is watching -->
             <view class="section-block" v-if="hotBook && hotBook.isbn">
                 <text class="section-title">大家都在看</text>
-                <HotRecommendItem :item="hotBook" @add-cart="addToCart"></HotRecommendItem>
+                <HotRecommendItem :item="hotBook" @add-cart="addToCart" @update-item="updateHotBook"></HotRecommendItem>
             </view>
 
             <!-- Filter Bar -->
@@ -56,7 +56,7 @@
             <!-- Book List -->
             <view class="book-list">
                 <RecommendItem v-for="(item, index) in bookList" :key="index" :item="item" @add-cart="addToCart"
-                    :show-desc="false">
+                    @update-item="updateBookItem($event, index)" :show-desc="false">
                 </RecommendItem>
                 <u-loadmore :status="loadStatus" margin-top="30" margin-bottom="30"></u-loadmore>
             </view>
@@ -258,6 +258,12 @@ export default {
                 duration: 3000
             });
         },
+        updateBookItem(updatedItem, index) {
+            this.$set(this.bookList, index, { ...this.bookList[index], ...updatedItem });
+        },
+        updateHotBook(updatedItem) {
+            this.hotBook = { ...this.hotBook, ...updatedItem };
+        },
         openPacket(data) {
             this.$refs.packetDialog.open(data);
         },