소스 검색

feat: 实现商品收藏功能及优化购物车徽章更新

- 在商品详情页添加收藏/取消收藏功能,包括收藏状态图标切换
- 新增收藏相关API接口(获取收藏列表、添加收藏、取消收藏)
- 修改购物车数量获取接口从GET改为POST以保持一致
- 修复推荐组件中showDesc参数传递错误
- 优化收藏页面交互,点击书籍可跳转至商品详情页
- 更新购物车徽章在添加商品后自动刷新
- 改进书籍列表组件,支持编辑模式下点击切换选中状态
ylong 1 주 전
부모
커밋
33fced0503

+ 9 - 0
api/modules/mall.js

@@ -112,5 +112,14 @@ export const useMallApi = (Vue, vm) => {
 
         // 获取购物车数量
         getCartCountAjax: () => vm.$u.post('/token/shop/cart/getCount'),
+
+        // 获取收藏列表
+        getCollectListAjax: (params) => vm.$u.get('/token/shop/user/collectList', params),
+
+        // 添加收藏
+        addCollectAjax: (isbn) => vm.$u.post('/token/shop/user/addCollect', { isbn }),
+
+        // 取消收藏
+        removeCollectAjax: (isbnList) => vm.$u.post('/token/shop/user/removeCollect', { isbnList }),
 	}
 }

+ 13 - 28
pages-car/components/book-list-item.vue

@@ -1,20 +1,20 @@
 <template>
-    <view class="book-item">
+    <view class="book-item" @click="handleClick">
         <!-- 书籍封面 -->
         <image class="book-cover" :src="book.cover" mode="aspectFill" />
         <!-- 书籍详情 -->
         <view class="book-info">
-            <view class="book-title">{{ book.bookName }}</view>
+            <view class="book-title">{{ book.bookName || book.name }}</view>
             <view class="book-price">
                 <view class="sale-price">
                     <text class="symbol">¥</text>
-                    <text class="amount">{{ book.recycleMoney }}</text>
+                    <text class="amount">{{ book.price || book.recycleMoney }}</text>
                 </view>
-                <text class="origin-price">¥{{ book.recycleMoney }}</text>
+                <text class="origin-price" v-if="book.producePrice">¥{{ book.producePrice }}</text>
             </view>
         </view>
         <!-- 选择框 -->
-        <view class="checkbox-item" v-if="isEditMode">
+        <view class="checkbox-item" v-if="isEditMode" @click.stop>
             <u-checkbox v-show="isEditMode" class="checkbox-item" v-model="book.selected" :name="book.isbn"
                 shape="circle" label-size="0" active-color="#38C148" @change="updateCheckbox"></u-checkbox>
         </view>
@@ -34,33 +34,20 @@ export default {
             default: false
         }
     },
-    data() {
-        return {
-            statusMap: {
-                1: "added",
-                2: 'pending',
-                3: 'disabled'
-            }
-        }
-    },
     methods: {
-        getStatusText(status) {
-            //  1-已加入卖书清单 2-未加入 3-暂不回收
-            let key = this.statusMap[status]
-            const statusKey = {
-                pending: '加入卖书清单',
-                added: '已加入卖书清单',
-                disabled: '暂不回收'
-            }
-            return statusKey[key] || key
-        },
         updateCheckbox({ value }) {
             this.$nextTick(() => {
                 this.$emit('checked', { book: this.book, checked: value })
             })
         },
-        handleAction() {
-            this.$emit('action', this.book)
+        handleClick() {
+            if (this.isEditMode) {
+                // 编辑模式下点击整个卡片也切换选中状态
+                this.book.selected = !this.book.selected;
+                this.updateCheckbox({ value: this.book.selected });
+                return;
+            }
+            this.$emit('click', this.book);
         }
     }
 }
@@ -119,8 +106,6 @@ export default {
             margin-bottom: 12rpx;
             font-family: Source Han Sans CN;
             display: flex;
-            justify-content: space-between;
-            align-items: center;
             align-items: baseline;
 
             .sale-price {

+ 24 - 33
pages-car/pages/my-fav.vue

@@ -1,5 +1,5 @@
 <template>
-    <view class="scan-history">
+    <view class="my-fav">
         <!-- 顶部操作栏 -->
         <view class="header" v-if="bookList.length">
             <view class="left">
@@ -14,16 +14,16 @@
         </view>
 
         <!-- 书籍列表 -->
-        <page-scroll :page-size="12" @updateList="handleUpdateList" ref="pageRef" slotEmpty emptyText="您暂未扫过书籍">
+        <page-scroll :page-size="12" url="/token/shop/user/collectList" @updateList="handleUpdateList" ref="pageRef" slotEmpty emptyText="您暂未收藏书籍">
             <view class="book-list">
                 <BookListItem v-for="book in bookList" :key="book.isbn" :book="book" :isEditMode="isEditMode"
-                    @action="handleAction" @checked="handleChecked" />
+                    @click="handleBookClick" @checked="handleChecked" />
             </view>
         </page-scroll>
 
         <!-- 删除确认弹窗 -->
         <common-dialog ref="deleteDialog" title="温馨提示" @confirm="confirmDelete">
-            <text>确定删除选中图书吗?</text>
+            <text>确定移除选中商品吗?</text>
         </common-dialog>
     </view>
 </template>
@@ -52,7 +52,7 @@ export default {
         this.$refs.pageRef?.loadData(true)
     },
     // #endif
-    mounted() {
+    onShow() {
         this.$refs.pageRef?.loadData(true)
     },
     methods: {
@@ -60,36 +60,25 @@ export default {
             this.$nextTick(() => {
                 let item = this.bookList.find(item => item.isbn === book.isbn)
                 let index = this.bookList.findIndex(item => item.isbn === book.isbn)
-                item.selected = checked
-                this.$set(this.bookList, index, item)
-
-                this.isAllSelected = this.bookList.every(item => item.selected)
-                console.log(this.bookList, 'this.isAllSelected')
+                if (item) {
+                    item.selected = checked
+                    this.$set(this.bookList, index, item)
+                    this.isAllSelected = this.bookList.every(item => item.selected)
+                }
             })
         },
-        handleAction(book) {
-            if (book.status == 2) {
-                uni.$u.http.get('/token/order/addScanToOrder?isbn=' + book.isbn).then(res => {
-                    if (res.code === 200) {
-                        uni.showToast({
-                            title: '加入成功',
-                            icon: 'success'
-                        })
-                        this.$refs.pageRef?.loadData(true)
-                    } else {
-                        uni.showToast({
-                            title: res.msg,
-                            icon: 'none'
-                        })
-                    }
-                })
-            }
+        handleBookClick(book) {
+             uni.navigateTo({
+                url: '/pages-sell/pages/detail?isbn=' + book.isbn
+            });
         },
         handleUpdateList(data) {
             this.bookList = data.map(v => {
                 v.selected = false
                 return v
             })
+            // 重置全选状态
+            this.isAllSelected = false;
         },
 
         // 切换编辑模式
@@ -98,8 +87,9 @@ export default {
             if (!this.isEditMode) {
                 this.bookList.forEach(book => book.selected = false)
             } else {
-                this.isAllSelected = true
-                this.bookList.forEach(book => book.selected = true)
+                // 进入编辑模式不默认全选
+                this.isAllSelected = false
+                this.bookList.forEach(book => book.selected = false)
             }
         },
 
@@ -114,7 +104,7 @@ export default {
             let deleteIds = this.bookList.filter(book => book.selected)
             if (deleteIds.length === 0) {
                 uni.showToast({
-                    title: '请选择要删除的记录',
+                    title: '请选择要移除的商品',
                     icon: 'none'
                 })
                 return
@@ -125,13 +115,14 @@ export default {
         // 确认删除
         confirmDelete() {
             let deleteIds = this.bookList.filter(book => book.selected).map(v => v.isbn)
-            uni.$u.http.post('/token/order/removeScanLogs', deleteIds).then(res => {
+            this.$u.api.removeCollectAjax(deleteIds).then(res => {
                 if (res.code === 200) {
                     uni.showToast({
-                        title: '除成功',
+                        title: '除成功',
                         icon: 'success'
                     })
                     this.$refs.pageRef?.loadData(true)
+                    this.isEditMode = false;
                 }
             })
         }
@@ -140,7 +131,7 @@ export default {
 </script>
 
 <style lang="scss">
-.scan-history {
+.my-fav {
     min-height: 100vh;
 
     ::v-deep .checkbox-item {

+ 28 - 5
pages-sell/components/detail/footer-bar.vue

@@ -1,16 +1,17 @@
 <template>
 	<view class="footer-bar safe-area-inset-bottom">
 		<view class="icons-area">
-			<view class="icon-item">
+			<view class="icon-item" @click="onHome">
 				<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">
-				<image src="/pages-sell/static/goods/icon-star.png" class="bar-icon" mode="aspectFit"></image>
-				<text>收藏</text>
+			<view class="icon-item" @click="onCollect">
+				<image v-if="isCollect" src="/pages-sell/static/goods/icon-star-active.png" class="bar-icon" mode="aspectFit"></image>
+				<image v-else src="/pages-sell/static/goods/icon-star.png" class="bar-icon" mode="aspectFit"></image>
+				<text :style="{ color: isCollect ? '#FFC107' : '#666' }">{{ isCollect ? '已收藏' : '收藏' }}</text>
 			</view>
-			<view class="icon-item">
+			<view class="icon-item" @click="onCart">
 				<image src="/pages-sell/static/goods/icon-car.png" class="bar-icon" mode="aspectFit"></image>
 				<text>购物车</text>
 			</view>
@@ -34,9 +35,26 @@
 			hasStock: {
 				type: Boolean,
 				default: true
+			},
+			isCollect: {
+				type: Boolean,
+				default: false
 			}
 		},
 		methods: {
+			onHome() {
+				uni.switchTab({
+					url: '/pages/home/index'
+				})
+			},
+			onCart() {
+				uni.switchTab({
+					url: '/pages-car/pages/index'
+				})
+			},
+			onCollect() {
+				this.$emit('collect');
+			},
 			onAddCart() {
 				this.$emit('addCart', 3);
 			},
@@ -81,6 +99,11 @@
 					margin-bottom: 6rpx;
 				}
 
+				.bar-icon-2 {
+					width: 50rpx;
+					height: 50rpx;
+				}
+
 				text {
 					font-size: 20rpx;
 					color: #666;

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

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

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

@@ -74,7 +74,7 @@
         </view>
 
         <!-- Footer -->
-        <FooterBar :hasStock="hasStock" @addCart="openSelectPopup" @notify="handleNotify"></FooterBar>
+        <FooterBar :hasStock="hasStock" :isCollect="product.isCollect" @addCart="openSelectPopup" @notify="handleNotify" @collect="handleCollect"></FooterBar>
 
         <!-- Select Popup -->
         <SelectGoodPopup ref="selectPopup" @confirm="onPopupConfirm"></SelectGoodPopup>
@@ -176,6 +176,28 @@
                     title: '已加入购物车',
                     icon: 'success'
                 });
+                this.$updateCartBadge();
+            },
+            handleCollect() {
+                if (!this.product || !this.product.isbn) {
+                    return this.$u.toast('商品信息不完整');
+                }
+                const isCollect = this.product.isCollect;
+                const api = isCollect ? this.$u.api.removeCollectAjax : this.$u.api.addCollectAjax;
+                const params = isCollect ? [this.product.isbn] : this.product.isbn;
+
+                uni.showLoading({ mask: true });
+                api(params).then(res => {
+                    uni.hideLoading();
+                    if (res.code === 200) {
+                        this.$set(this.product, 'isCollect', !isCollect);
+                        this.$u.toast(isCollect ? '已取消收藏' : '收藏成功');
+                    } else {
+                        this.$u.toast(res.msg);
+                    }
+                }).catch(() => {
+                    uni.hideLoading();
+                });
             },
             showServicePopup() {
                 // Placeholder for service popup

BIN
pages-sell/static/goods/icon-star.png


+ 1 - 1
utils/uniapp-api.js

@@ -32,7 +32,7 @@ export const updateCartBadge = () => {
     
     // 确保 uView http 可用
     if (uni.$u && uni.$u.http) {
-        uni.$u.http.get('/token/shop/cart/getCount').then(res => {
+        uni.$u.http.post('/token/shop/cart/getCount').then(res => {
             if (res.code == 200) {
                 const count = res.data;
                 if (count > 0) {