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

feat: 为加入购物车功能添加来源追踪参数

在多个组件中添加 sourceFrom 参数以追踪用户从不同入口加入购物车的来源:
- hot-recommend-item 组件传递 sourceFrom=1
- recommend-item 组件传递 sourceFrom=2
- detail/footer-bar 组件传递 sourceFrom=3
- select-good-popup 组件接收并传递 sourceFrom 到后端 API
- detail.vue 页面适配新的参数传递方式

这有助于分析不同推荐位和页面入口的转化效果
ylong 2 недель назад
Родитель
Сommit
aba066afa0

+ 64 - 63
pages-sell/components/detail/footer-bar.vue

@@ -2,7 +2,8 @@
 	<view class="footer-bar safe-area-inset-bottom">
 		<view class="icons-area">
 			<view class="icon-item">
-				<image src="/pages-sell/static/goods/icon-home.png" class="bar-icon" mode="widthFix" style="width: 36rpx;"></image>
+				<image src="/pages-sell/static/goods/icon-home.png" class="bar-icon" mode="widthFix"
+					style="width: 36rpx;"></image>
 				<text>首页</text>
 			</view>
 			<view class="icon-item">
@@ -27,84 +28,84 @@
 </template>
 
 <script>
-export default {
-	name: 'FooterBar',
-	props: {
-		hasStock: {
-			type: Boolean,
-			default: true
-		}
-	},
-	methods: {
-		onAddCart() {
-			this.$emit('addCart');
+	export default {
+		name: 'FooterBar',
+		props: {
+			hasStock: {
+				type: Boolean,
+				default: true
+			}
 		},
-		onNotify() {
-			this.$emit('notify');
+		methods: {
+			onAddCart() {
+				this.$emit('addCart', 3);
+			},
+			onNotify() {
+				this.$emit('notify');
+			}
 		}
 	}
-}
 </script>
 
 <style lang="scss" scoped>
-.footer-bar {
-	position: fixed;
-	bottom: 0;
-	left: 0;
-	width: 100%;
-	height: 200rpx;
-	background: #fff;
-	display: flex;
-	align-items: center;
-	justify-content: space-between;
-	padding: 0 20rpx;
-	box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
-	z-index: 3;
-	padding-bottom: env(safe-area-inset-bottom);
-
-	.icons-area {
+	.footer-bar {
+		position: fixed;
+		bottom: 0;
+		left: 0;
+		width: 100%;
+		height: 200rpx;
+		background: #fff;
 		display: flex;
-		margin-right: 20rpx;
+		align-items: center;
+		justify-content: space-between;
+		padding: 0 20rpx;
+		box-shadow: 0 -2rpx 10rpx rgba(0, 0, 0, 0.05);
+		z-index: 3;
+		padding-bottom: env(safe-area-inset-bottom);
 
-		.icon-item {
+		.icons-area {
 			display: flex;
-			flex-direction: column;
-			align-items: center;
-			justify-content: center;
-			width: 80rpx;
-			margin-right: 10rpx;
+			margin-right: 20rpx;
 
-			.bar-icon {
-				width: 40rpx;
-				height: 40rpx;
-				margin-bottom: 6rpx;
-			}
+			.icon-item {
+				display: flex;
+				flex-direction: column;
+				align-items: center;
+				justify-content: center;
+				width: 80rpx;
+				margin-right: 10rpx;
+
+				.bar-icon {
+					width: 40rpx;
+					height: 40rpx;
+					margin-bottom: 6rpx;
+				}
 
-			text {
-				font-size: 20rpx;
-				color: #666;
-				text-align: center;
+				text {
+					font-size: 20rpx;
+					color: #666;
+					text-align: center;
+				}
 			}
 		}
-	}
-
-	.action-btn-area {
-		flex: 1;
-		display: flex;
-		height: 80rpx;
-		max-width: 360rpx;
 
-		.action-btn {
+		.action-btn-area {
 			flex: 1;
 			display: flex;
-			align-items: center;
-			justify-content: center;
-			border-radius: 40rpx;
-			font-size: 30rpx;
-			color: #fff;
-			font-weight: bold;
-			background: linear-gradient(0deg, #38C248 0%, #5FEA6F 100%);
+			height: 80rpx;
+			max-width: 360rpx;
+
+			.action-btn {
+				flex: 1;
+				display: flex;
+				align-items: center;
+				justify-content: center;
+				border-radius: 40rpx;
+				font-size: 30rpx;
+				color: #fff;
+				font-weight: bold;
+				background: linear-gradient(0deg, #38C248 0%, #5FEA6F 100%);
+			}
 		}
 	}
-}
 </style>

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

@@ -44,7 +44,8 @@ export default {
 	},
 	methods: {
 		handleAddToCart() {
-			this.$refs.popup.open(this.item);
+			// 加入购物车时,传递 sourceFrom 参数为 1
+			this.$refs.popup.open(this.item, 1);
 		},
 		onPopupConfirm(data) {
 			this.$emit('add-cart', data);

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

@@ -76,7 +76,8 @@ export default {
 		},
 		handleAddToCart() {
 			// Open the popup using ref
-			this.$refs.popup.open(this.item);
+			// 加入购物车时,传递 sourceFrom 参数为 2
+			this.$refs.popup.open(this.item, 2);
 		},
 		onPopupConfirm(data) {
 			// Emit the confirmed data

+ 5 - 2
pages-sell/components/select-good-popup/index.vue

@@ -87,6 +87,7 @@
 				currentQuality: 1,
 				currentProduct: {},
 				qualityOptions: [],
+				sourceFrom: 0,
 			};
 		},
 		computed: {
@@ -115,9 +116,10 @@
 			}
 		},
 		methods: {
-			open(product) {
+			open(product, sourceFrom) {
 				this.visible = true;
 				this.quantity = 1;
+				this.sourceFrom = sourceFrom || 0;
 				if (product.isbn) {
 					this.getGoodQualityInfo(product.isbn);
 				}
@@ -178,7 +180,8 @@
 				this.$u.api.addShopCartAjax({
 					isbn: this.currentProduct.isbn,
 					quantity: this.quantity,
-					conditionType: conditionType
+					conditionType: conditionType,
+					sourceFrom: this.sourceFrom
 				}).then(res => {
 					if (res.code === 200) {
 						this.$u.toast('加入购物车成功');

+ 245 - 246
pages-sell/pages/detail.vue

@@ -4,13 +4,14 @@
         <view class="header-bg"></view>
 
         <!-- Custom Navbar -->
-        <Navbar :title="product.bookName || '详情'" :titleSize="32" title-color="#fff" back-icon-color="#fff" background="transparent">
+        <Navbar :title="product.bookName || '详情'" :titleSize="32" title-color="#fff" back-icon-color="#fff"
+            background="transparent">
         </Navbar>
 
         <!-- Notification Bar -->
         <view class="notification-bar">
             <u-avatar size="40" src="https://img.yzcdn.cn/vant/cat.jpeg"></u-avatar>
-            
+
             <text class="notif-text">微 ** 用户 购买了 《苏菲的世界》</text>
         </view>
 
@@ -40,12 +41,14 @@
                 @position-change="handlePositionChange" :z-index="20">
                 <!-- #ifdef MP-ALIPAY -->
                 <button class="service-btn" @click="navigateToCustomerService">
-                    <image src="/pages-sell/static/goods/icon-kefu.png" class="cs-icon" style="width: 100rpx;" mode="widthFix"></image>
+                    <image src="/pages-sell/static/goods/icon-kefu.png" class="cs-icon" style="width: 100rpx;"
+                        mode="widthFix"></image>
                 </button>
                 <!-- #endif -->
                 <!-- #ifndef MP-ALIPAY -->
                 <button class="service-btn" open-type="contact">
-                    <image src="/pages-sell/static/goods/icon-kefu.png" class="cs-icon" style="width: 100rpx" mode="widthFix"></image>
+                    <image src="/pages-sell/static/goods/icon-kefu.png" class="cs-icon" style="width: 100rpx"
+                        mode="widthFix"></image>
                 </button>
                 <!-- #endif -->
             </FloatingDrag>
@@ -61,16 +64,12 @@
                     <view class="indicator" v-if="currentTab === 1"></view>
                 </view>
             </view>
-            
+
             <!-- Product Detail Content -->
-            <ProductContent 
-                :currentTab="currentTab" 
-                :product="product" 
-                :tipsContent="tipsContent" 
-                :relatedBooksList="relatedBooksList" 
-                @bookClick="onBookClick">
+            <ProductContent :currentTab="currentTab" :product="product" :tipsContent="tipsContent"
+                :relatedBooksList="relatedBooksList" @bookClick="onBookClick">
             </ProductContent>
-            
+
             <u-gap height="220"></u-gap>
         </view>
 
@@ -79,276 +78,276 @@
 
         <!-- Select Popup -->
         <SelectGoodPopup ref="selectPopup" @confirm="onPopupConfirm"></SelectGoodPopup>
-        
+
         <!-- Share Popup -->
         <SharePopup ref="sharePopup" :product="product"></SharePopup>
     </view>
 </template>
 
 <script>
-import Navbar from '@/components/navbar/navbar.vue'
-import SelectGoodPopup from '../components/select-good-popup/index.vue'
-import InfoCard from '../components/detail/info-card.vue'
-import ServiceCard from '../components/detail/service-card.vue'
-import ProductContent from '../components/detail/product-content.vue'
-import FooterBar from '../components/detail/footer-bar.vue'
-import FloatingDrag from "@/components/floating-drag.vue";
-import SharePopup from '../components/detail/share-popup.vue';
-
-export default {
-    components: {
-        Navbar,
-        SelectGoodPopup,
-        InfoCard,
-        ServiceCard,
-        ProductContent,
-        FooterBar,
-        FloatingDrag,
-        SharePopup
-    },
-    data() {
-        return {
-            currentTab: 0,
-            hasStock: false, // Toggle this to test stock status
-            tipsContent: [
-                '印刷版次较多,二手图书封面、版次、原价等信息可能与商品介绍有差异,具体以收到实物为准;',
-                '二手图书性质特殊,不保证随新书赠送的光盘、海报、卡片等内容,仅支持图书质量问题退款,否则将扣除运费成本;',
-                '收到的图书如有质量问题,请于七天内联系客服处理,超出售后时效,不予处理。'
-            ],
-            relatedBooksList: [],
-            product: {},
-            servicePosition: {
-                left: "auto",
-                right: 0,
-                bottom: "300rpx",
-            },
-        }
-    },
-	onLoad(options) {
-		const isbn = options.isbn || options.id; // Support both just in case
-		if (isbn) {
-			this.getBookDetail(isbn);
-		} else {
-			uni.showToast({
-				title: '参数错误',
-				icon: 'none'
-			});
-			setTimeout(() => {
-				uni.navigateBack();
-			}, 1500);
-		}
-	},
-    methods: {
-		getBookDetail(isbn) {
-			uni.showLoading({ title: '加载中' });
-			this.$u.api.getBookDetailAjax({ isbn }).then(res => {
-				uni.hideLoading();
-				if (res.code === 200) {
-					this.product = res.data;
-					this.hasStock = res.data.skuList.some(sku => sku.stockNum > 0);
-					if (res.data.recommendList) {
-						this.relatedBooksList = res.data.recommendList;
-					}
-				} else {
-					uni.showToast({
-						title: res.msg || '获取详情失败',
-						icon: 'none'
-					});
-				}
-			}).catch(() => {
-				uni.hideLoading();
-			});
-		},
-        switchTab(index) {
-            this.currentTab = index;
-        },
-        openSelectPopup() {
-            this.$refs.selectPopup.open(this.product);
+    import Navbar from '@/components/navbar/navbar.vue'
+    import SelectGoodPopup from '../components/select-good-popup/index.vue'
+    import InfoCard from '../components/detail/info-card.vue'
+    import ServiceCard from '../components/detail/service-card.vue'
+    import ProductContent from '../components/detail/product-content.vue'
+    import FooterBar from '../components/detail/footer-bar.vue'
+    import FloatingDrag from "@/components/floating-drag.vue";
+    import SharePopup from '../components/detail/share-popup.vue';
+
+    export default {
+        components: {
+            Navbar,
+            SelectGoodPopup,
+            InfoCard,
+            ServiceCard,
+            ProductContent,
+            FooterBar,
+            FloatingDrag,
+            SharePopup
         },
-        handleNotify() {
-            uni.showToast({
-                title: '已订阅到货通知',
-                icon: 'success'
-            });
-        },
-        onPopupConfirm(data) {
-            console.log('Added to cart:', data);
-            uni.showToast({
-                title: '已加入购物车',
-                icon: 'success'
-            });
-        },
-        showServicePopup() {
-            // Placeholder for service popup
-        },
-        onBookClick(book) {
-            console.log('Book clicked:', book);
-            uni.navigateTo({
-                url: '/pages-sell/pages/detail?id=' + encodeURIComponent(book.title)
-            });
-        },
-        openSharePopup() {
-            this.$refs.sharePopup.open();
-        },
-        // 处理位置变更
-        handlePositionChange(position) {
-            this.servicePosition = position;
+        data() {
+            return {
+                currentTab: 0,
+                hasStock: false, // Toggle this to test stock status
+                tipsContent: [
+                    '印刷版次较多,二手图书封面、版次、原价等信息可能与商品介绍有差异,具体以收到实物为准;',
+                    '二手图书性质特殊,不保证随新书赠送的光盘、海报、卡片等内容,仅支持图书质量问题退款,否则将扣除运费成本;',
+                    '收到的图书如有质量问题,请于七天内联系客服处理,超出售后时效,不予处理。'
+                ],
+                relatedBooksList: [],
+                product: {},
+                servicePosition: {
+                    left: "auto",
+                    right: 0,
+                    bottom: "300rpx",
+                },
+            }
         },
-        //支付宝小程序的客服
-        navigateToCustomerService() {
-            uni.navigateTo({
-                url: "/pages-mine/pages/customer-service",
-            });
+        onLoad(options) {
+            const isbn = options.isbn || options.id; // Support both just in case
+            if (isbn) {
+                this.getBookDetail(isbn);
+            } else {
+                uni.showToast({
+                    title: '参数错误',
+                    icon: 'none'
+                });
+                setTimeout(() => {
+                    uni.navigateBack();
+                }, 1500);
+            }
         },
+        methods: {
+            getBookDetail(isbn) {
+                uni.showLoading({ title: '加载中' });
+                this.$u.api.getBookDetailAjax({ isbn }).then(res => {
+                    uni.hideLoading();
+                    if (res.code === 200) {
+                        this.product = res.data;
+                        this.hasStock = res.data.skuList.some(sku => sku.stockNum > 0);
+                        if (res.data.recommendList) {
+                            this.relatedBooksList = res.data.recommendList;
+                        }
+                    } else {
+                        uni.showToast({
+                            title: res.msg || '获取详情失败',
+                            icon: 'none'
+                        });
+                    }
+                }).catch(() => {
+                    uni.hideLoading();
+                });
+            },
+            switchTab(index) {
+                this.currentTab = index;
+            },
+            openSelectPopup(sourceFrom) {
+                this.$refs.selectPopup.open(this.product, sourceFrom);
+            },
+            handleNotify() {
+                uni.showToast({
+                    title: '已订阅到货通知',
+                    icon: 'success'
+                });
+            },
+            onPopupConfirm(data) {
+                console.log('Added to cart:', data);
+                uni.showToast({
+                    title: '已加入购物车',
+                    icon: 'success'
+                });
+            },
+            showServicePopup() {
+                // Placeholder for service popup
+            },
+            onBookClick(book) {
+                console.log('Book clicked:', book);
+                uni.navigateTo({
+                    url: '/pages-sell/pages/detail?id=' + encodeURIComponent(book.title)
+                });
+            },
+            openSharePopup() {
+                this.$refs.sharePopup.open();
+            },
+            // 处理位置变更
+            handlePositionChange(position) {
+                this.servicePosition = position;
+            },
+            //支付宝小程序的客服
+            navigateToCustomerService() {
+                uni.navigateTo({
+                    url: "/pages-mine/pages/customer-service",
+                });
+            },
+        }
     }
-}
 </script>
 
 <style lang="scss" scoped>
-.detail-page {
-    min-height: 100vh;
-    background-color: #f5f5f5;
-    position: relative;
-    padding-bottom: 100rpx;
-}
-
-.header-bg {
-    position: absolute;
-    top: 0;
-    left: 0;
-    width: 100%;
-    height: 664rpx;
-    background: linear-gradient(0deg, #4ED868 0%, #D1FFE5 100%);
-    z-index: 0;
-}
-
-.notification-bar {
-    position: fixed;
-    top: 180rpx;
-    /* Adjust based on navbar height */
-    left: 30rpx;
-    z-index: 10;
-    background: rgba(0, 0, 0, 0.3);
-    border-radius: 30rpx;
-    padding: 6rpx 20rpx 6rpx 6rpx;
-    display: flex;
-    align-items: center;
-
-    .notif-text {
-        font-size: 24rpx;
-        color: #fff;
-        margin-left: 10rpx;
-    }
-}
-
-.content-scroll {
-    position: relative;
-    z-index: 1;
-    height: 100vh;
-}
-
-.cover-area {
-    position: relative;
-    display: flex;
-    justify-content: center;
-    padding-top: 60rpx;
-    padding-bottom: 40rpx;
-
-    .book-cover {
-        width: 360rpx;
-        height: 360rpx;
-        box-shadow: 0 10rpx 20rpx rgba(0, 0, 0, 0.1);
+    .detail-page {
+        min-height: 100vh;
+        background-color: #f5f5f5;
+        position: relative;
+        padding-bottom: 100rpx;
     }
 
-    .share-btn {
+    .header-bg {
         position: absolute;
-        right: 50rpx;
-        top: 50rpx;
+        top: 0;
+        left: 0;
+        width: 100%;
+        height: 664rpx;
+        background: linear-gradient(0deg, #4ED868 0%, #D1FFE5 100%);
+        z-index: 0;
+    }
+
+    .notification-bar {
+        position: fixed;
+        top: 180rpx;
+        /* Adjust based on navbar height */
+        left: 30rpx;
+        z-index: 10;
+        background: rgba(0, 0, 0, 0.3);
+        border-radius: 30rpx;
+        padding: 6rpx 20rpx 6rpx 6rpx;
         display: flex;
-        flex-direction: column;
         align-items: center;
-        z-index: 10; // Ensure it's clickable
 
-        .share-icon {
-            width: 40rpx;
-            height: 40rpx;
-            margin-bottom: 4rpx;
+        .notif-text {
+            font-size: 24rpx;
+            color: #fff;
+            margin-left: 10rpx;
         }
+    }
 
-        text {
-            font-size: 20rpx;
-            color: #333;
-        }
+    .content-scroll {
+        position: relative;
+        z-index: 1;
+        height: 100vh;
     }
-}
 
-.banner-area {
-    background: #f5f5f5;
-    padding: 20rpx;
+    .cover-area {
+        position: relative;
+        display: flex;
+        justify-content: center;
+        padding-top: 60rpx;
+        padding-bottom: 40rpx;
+
+        .book-cover {
+            width: 360rpx;
+            height: 360rpx;
+            box-shadow: 0 10rpx 20rpx rgba(0, 0, 0, 0.1);
+        }
 
-    .banner-img {
-        width: 100%;
-        border-radius: 20rpx;
+        .share-btn {
+            position: absolute;
+            right: 50rpx;
+            top: 50rpx;
+            display: flex;
+            flex-direction: column;
+            align-items: center;
+            z-index: 10; // Ensure it's clickable
+
+            .share-icon {
+                width: 40rpx;
+                height: 40rpx;
+                margin-bottom: 4rpx;
+            }
+
+            text {
+                font-size: 20rpx;
+                color: #333;
+            }
+        }
     }
-}
-
-.service-btn {
-    padding: 0;
-    margin: 0;
-    background-color: transparent;
-    line-height: 1;
-    border-radius: 0;
-    
-    &::after {
-        border: none;
+
+    .banner-area {
+        background: #f5f5f5;
+        padding: 20rpx;
+
+        .banner-img {
+            width: 100%;
+            border-radius: 20rpx;
+        }
     }
-}
-
-.tabs-header {
-    display: flex;
-    justify-content: center;
-    /* Center the tabs */
-    background: #F8F8F8;
-    padding: 20rpx 0;
-    /* Remove horizontal padding as we center items */
-
-    .tab-item {
-        padding: 0 30rpx;
-        /* Add internal padding for hit area */
-        position: relative;
 
-        text {
-            font-family: 'Source Han Sans SC';
-            font-size: 30rpx;
-            /* Assuming 30px from design tool = 30rpx */
-            color: #666666;
-            transition: all 0.3s;
+    .service-btn {
+        padding: 0;
+        margin: 0;
+        background-color: transparent;
+        line-height: 1;
+        border-radius: 0;
+
+        &::after {
+            border: none;
         }
+    }
+
+    .tabs-header {
+        display: flex;
+        justify-content: center;
+        /* Center the tabs */
+        background: #F8F8F8;
+        padding: 20rpx 0;
+        /* Remove horizontal padding as we center items */
+
+        .tab-item {
+            padding: 0 30rpx;
+            /* Add internal padding for hit area */
+            position: relative;
 
-        &.active {
             text {
+                font-family: 'Source Han Sans SC';
                 font-size: 30rpx;
-                /* Keep consistent size or adjust if 'big' effect needed, user said 30px */
-                font-weight: bold;
-                color: #333333;
+                /* Assuming 30px from design tool = 30rpx */
+                color: #666666;
+                transition: all 0.3s;
             }
 
-            .indicator {
-                position: absolute;
-                bottom: 2rpx;
-                /* Adjust vertical position */
-                left: 50%;
-                transform: translateX(-50%);
-                width: 120rpx;
-                /* Make it wider or relative to text? Usually fixed or text width. Let's try matching text width visually or a fixed width */
-                height: 8rpx;
-                /* Slightly thicker for gradient visibility */
-                background: linear-gradient(90deg, rgba(78, 217, 100, 0.1) 0%, #4ED964 100%);
-                border-radius: 4rpx;
+            &.active {
+                text {
+                    font-size: 30rpx;
+                    /* Keep consistent size or adjust if 'big' effect needed, user said 30px */
+                    font-weight: bold;
+                    color: #333333;
+                }
+
+                .indicator {
+                    position: absolute;
+                    bottom: 2rpx;
+                    /* Adjust vertical position */
+                    left: 50%;
+                    transform: translateX(-50%);
+                    width: 120rpx;
+                    /* Make it wider or relative to text? Usually fixed or text width. Let's try matching text width visually or a fixed width */
+                    height: 8rpx;
+                    /* Slightly thicker for gradient visibility */
+                    background: linear-gradient(90deg, rgba(78, 217, 100, 0.1) 0%, #4ED964 100%);
+                    border-radius: 4rpx;
+                }
             }
         }
     }
-}
 
 
 </style>