Bläddra i källkod

feat(购物车): 实现买书订单预提交和确认流程

- 在购物车页面添加预提交接口调用,检查库存和商品状态
- 新增买书订单确认页面,展示地址、商品和价格信息
- 移除回收订单相关逻辑,调整为普通商品购买流程
- 添加订单备注功能,简化用户操作步骤
ylong 3 dagar sedan
förälder
incheckning
572a8368f8
3 ändrade filer med 165 tillägg och 263 borttagningar
  1. 6 0
      api/modules/mall.js
  2. 72 44
      pages-car/pages/index.vue
  3. 87 219
      pages-home/pages/book-order.vue

+ 6 - 0
api/modules/mall.js

@@ -177,5 +177,11 @@ export const useMallApi = (Vue, vm) => {
 		
 		// 清空购物车
 		clearCartAjax: () => vm.$u.post('/token/shop/cart/clearCart'),
+		
+		// 买书订单预览 (预提交)
+		preSubmitOrderAjax: (data) => vm.$u.post('/token/shop/order/preSubmit', data),
+		
+		// 确认提交订单 (买书)
+		submitShopOrderAjax: (data) => vm.$u.post('/token/shop/order/submitOrder', data),
 	}
 }

+ 72 - 44
pages-car/pages/index.vue

@@ -4,8 +4,8 @@
         <view class="cart-list">
             <u-swipe-action :show="item.show" :index="index" v-for="(item, index) in cartList" :key="item.id"
                 @click="clickAction" @open="openAction" :options="actionOptions">
-                <cart-item :item="item" @check="handleCheck" @changeNum="handleChangeNum"
-                    @reduce="handleReduce" @selectCondition="onSelectCondition"></cart-item>
+                <cart-item :item="item" @check="handleCheck" @changeNum="handleChangeNum" @reduce="handleReduce"
+                    @selectCondition="onSelectCondition"></cart-item>
             </u-swipe-action>
         </view>
 
@@ -262,10 +262,38 @@
                 }
             },
             onNext() {
-                // 提交订单 - 传递选中的 ID?
-                // 通常我们将 ID 传递给确认订单页面
-                const ids = this.selectedItems.map(item => item.id).join(',');
-                uni.navigateTo({ url: `/pages-car/pages/confirm-order?ids=${ids}` });
+                // 提交订单 - 传递选中的 ID
+                if (this.selectedItems.length === 0) {
+                    this.$u.toast('请选择商品');
+                    return;
+                }
+
+                const ids = this.selectedItems.map(item => item.id);
+
+                // 调用预提交接口检查
+                uni.showLoading({ title: '处理中' });
+                this.$u.api.preSubmitOrderAjax({ cartIdList: ids }).then(res => {
+                    uni.hideLoading();
+                    if (res.code === 200) {
+                        if (res.data.code === 1) {
+                            // 成功,跳转到确认订单页面
+                            uni.navigateTo({
+                                url: `/pages-home/pages/book-order?cartIds=${ids}`
+                            });
+                        } else if (res.data.code === 2) {
+                            // 失败,显示错误信息
+                            this.$u.toast('库存不足');
+                        } else if (res.data.code === 3) {
+                            // 失败,显示错误信息
+                            this.$u.toast('图书已下架');
+                        }
+                    } else {
+                        // 失败,显示错误信息
+                        this.$u.toast(res.msg || '无法提交订单');
+                    }
+                }).catch(() => {
+                    uni.hideLoading();
+                });
             },
             handleShare() {
                 console.log('share');
@@ -301,7 +329,7 @@
                     this.$u.toast('无法获取商品信息');
                     return;
                 }
-                
+
                 uni.showLoading({ title: '加载中' });
                 this.$u.http.get('/token/shop/bookDetail', { isbn: item.isbn }).then(res => {
                     uni.hideLoading();
@@ -315,43 +343,43 @@
                     this.$u.toast('网络请求失败');
                 });
             },
-			handleConditionUpdate(sku) {
-				if (!this.currentItem) return;
-
-				uni.showLoading({
-					title: '更新中'
-				});
-				this.$u.api.updateCartConditionAjax({
-					id: this.currentItem.id,
-					conditionType: sku.conditionType
-				}).then(() => {
-					uni.hideLoading();
-					this.$u.toast('更新成功');
-					
-					// 在列表中查找并更新,确保视图刷新
-					const index = this.cartList.findIndex(i => i.id === this.currentItem.id);
-					if (index > -1) {
-						const targetItem = this.cartList[index];
-						// 确保转换为数字类型
-						const newType = Number(sku.conditionType);
-						
-						this.$set(targetItem, 'conditionType', newType);
-						this.$set(targetItem, 'productPrice', sku.price);
-						
-						if (sku.reduceMoney !== undefined) {
-							this.$set(targetItem, 'reduceMoney', sku.reduceMoney);
-						}
-						
-						// 强制更新 currentItem 引用(虽然它指向同一个对象,但为了保险)
-						this.currentItem = targetItem;
-					} else {
-						// 如果找不到(极少情况),重新加载
-						this.loadData();
-					}
-				}).catch(() => {
-					uni.hideLoading();
-				});
-			}
+            handleConditionUpdate(sku) {
+                if (!this.currentItem) return;
+
+                uni.showLoading({
+                    title: '更新中'
+                });
+                this.$u.api.updateCartConditionAjax({
+                    id: this.currentItem.id,
+                    conditionType: sku.conditionType
+                }).then(() => {
+                    uni.hideLoading();
+                    this.$u.toast('更新成功');
+
+                    // 在列表中查找并更新,确保视图刷新
+                    const index = this.cartList.findIndex(i => i.id === this.currentItem.id);
+                    if (index > -1) {
+                        const targetItem = this.cartList[index];
+                        // 确保转换为数字类型
+                        const newType = Number(sku.conditionType);
+
+                        this.$set(targetItem, 'conditionType', newType);
+                        this.$set(targetItem, 'productPrice', sku.price);
+
+                        if (sku.reduceMoney !== undefined) {
+                            this.$set(targetItem, 'reduceMoney', sku.reduceMoney);
+                        }
+
+                        // 强制更新 currentItem 引用(虽然它指向同一个对象,但为了保险)
+                        this.currentItem = targetItem;
+                    } else {
+                        // 如果找不到(极少情况),重新加载
+                        this.loadData();
+                    }
+                }).catch(() => {
+                    uni.hideLoading();
+                });
+            }
         }
     }
 </script>

+ 87 - 219
pages-home/pages/book-order.vue

@@ -6,7 +6,7 @@
                 <image src="/pages-mine/static/adderss.png" style="width: 40rpx; height: 40rpx"></image>
                 <view class="flex-d flex-1 ml-24" style="margin-right: 90rpx">
                     <view class="flex-a flex-j-b mb-10">
-                        <view :style="titleStyle">货人:{{ defaultAddr.name }}</view>
+                        <view :style="titleStyle">货人:{{ defaultAddr.name }}</view>
                         <view :style="titleStyle">{{ defaultAddr.mobile }}</view>
                     </view>
                     <view :style="titleStyle">地址:{{ defaultAddr.fullAddress }}</view>
@@ -17,7 +17,7 @@
             <view class="flex-a flex-j-b" @click="handleAddress" v-else>
                 <view class="flex-a">
                     <u-icon name="plus-circle-fill" :size="48" color="#38C148" top="2"></u-icon>
-                    <view :style="titleStyle" class="ml-10 font-30">货地址</view>
+                    <view :style="titleStyle" class="ml-10 font-30">货地址</view>
                     <text class="u-required">*</text>
                 </view>
 
@@ -26,43 +26,18 @@
                     <u-icon name="arrow-right" :size="28" color="#666" top="4"></u-icon>
                 </view>
             </view>
-
-            <view class="free-pickup mb-20 mt-20">
-                <view class="pickup-title">免费上门取货</view>
-                <view class="pickup-desc">书嗨将预约指定快递上门取件,邮费由书嗨承担</view>
-            </view>
-            <view class="flex-a flex-j-b time-card" style="padding: 0 10rpx" @click="showTimePicker = true">
-                <view class="flex-a">
-                    <view :style="titleStyle" class="ml-10 font-28">取件时间</view>
-                    <text class="u-required">*</text>
-                </view>
-
-                <view class="flex-a">
-                    <view v-if="selectedTime.day" :style="titleStyle" class="ml-10"
-                        >{{ selectedTime.day }}
-                        {{ selectedTime.time }}
-                    </view>
-                    <view v-else :style="titleStyle" class="ml-10">请选择快递上门取件时间</view>
-                    <u-icon name="arrow-right" :size="28" color="#666" top="4"></u-icon>
-                </view>
-            </view>
         </view>
 
-        <!-- 快递备注部分 -->
+        <!-- 备注部分 -->
         <view class="section-card">
-            <view class="flex-a flex-j-b mb-20" @click="showExpressPicker">
+            <view class="flex-a flex-j-b mb-20">
                 <view class="flex-a">
-                    <view :style="titleStyle" class="font-28">快递备注</view>
+                    <view :style="titleStyle" class="font-28">订单备注</view>
                 </view>
-
-                <view class="flex-a">
-                    <view :style="titleStyle" class="ml-10">{{ submitData.expressDelivery || "请选择" }}</view>
-                    <u-icon name="arrow-right" :size="28" color="#666" top="3"></u-icon>
+                <view class="flex-a" style="flex: 1; justify-content: flex-end;">
+                    <u-input v-model="submitData.remark" type="text" placeholder="选填,请先和商家协商一致" :custom-style="{textAlign: 'right'}" />
                 </view>
             </view>
-            <view class="express-desc"
-                >请从【顺丰】或【京东】中选择可正常收寄的快递,如您不选择,则由系统根据区域情况自动分配</view
-            >
         </view>
 
         <!-- 书籍列表 -->
@@ -72,48 +47,27 @@
 
         <!-- 底部栏 -->
         <view class="bottom-bar">
-            <view class="agreement">
-                <u-checkbox v-model="agreed" shape="circle">
-                    <text class="agreement-text">我已阅读并同意</text>
-                    <text class="agreement-link" @click="goToAgreement">《书嗨用户协议》</text>
-                </u-checkbox>
-            </view>
             <view class="order-summary">
                 <view class="total">
-                    共<text class="price">{{ totalNum }}</text
-                    >件 预估回收价
-                    <text class="price">¥{{ totalRecycleMoney }}</text>
-                    <u-icon name="question-circle-fill" color="#333333" size="36" @click="showRules"></u-icon>
+                    共<text class="price">{{ totalNum }}</text>件 
+                    合计: <text class="price">¥{{ totalMoney }}</text>
+                    <text v-if="totalDiscount > 0" style="font-size: 24rpx; color: #999; margin-left: 10rpx;">(已优惠 ¥{{ totalDiscount }})</text>
                 </view>
                 <u-button type="primary" @click="submitOrder" :custom-style="{ margin: 0 }">提交订单</u-button>
             </view>
         </view>
 
-        <!-- 添加快递选择器 -->
-        <u-picker
-            v-model="showPicker"
-            mode="selector"
-            :range="expressList"
-            @confirm="onExpressConfirm"
-            @cancel="showPicker = false"
-            range-key="name"
-        ></u-picker>
-
-        <pickup-time-picker :show.sync="showTimePicker" @confirm="onTimeConfirm"></pickup-time-picker>
-
-        <submit-confirm ref="submitConfirmDialog" @confirm="handleConfirmSubmit"></submit-confirm>
+        <submit-confirm ref="submitConfirmDialog" @confirm="handleConfirmSubmit" title="确认提交订单" content="请确认收货地址和商品信息无误"></submit-confirm>
     </view>
 </template>
 
 <script>
 import bookItem from "@/pages-home/components/BookItem.vue";
-import pickupTimePicker from "@/pages-home/components/PickupTimePicker.vue";
 import SubmitConfirm from "@/pages-home/components/SubmitConfirm.vue";
 
 export default {
     components: {
         bookItem,
-        pickupTimePicker,
         SubmitConfirm,
     },
     data() {
@@ -123,58 +77,34 @@ export default {
                 "font-weight": 400,
                 color: "#333333",
             },
-            agreed: false,
             books: [],
-
-            showPicker: false,
-            expressList: [],
-            showTimePicker: false,
-            selectedTime: {},
             defaultAddr: {},
-
+            cartIdList: [], // 接收参数
+            
             submitData: {
-                expressDelivery: "",
-                orderId: "",
+                cartIdList: [],
                 addressId: "",
-                schedulePickupStartTime: "",
-                schedulePickupEndTime: "",
-                expressDelivery: "",
                 remark: "",
+                userCouponIds: [],
             },
+            
             totalNum: 0,
-            totalRecycleMoney: 0,
+            totalMoney: 0,
+            totalDiscount: 0,
         };
     },
+    onLoad(options) {
+        if (options.cartIds) {
+            this.cartIdList = options.cartIds.split(',').map(Number);
+            this.submitData.cartIdList = this.cartIdList;
+            this.getPreSubmitOrder();
+        } else {
+            this.$u.toast('参数错误');
+            setTimeout(() => uni.navigateBack(), 1500);
+        }
+    },
     methods: {
-        showRules() {
-            uni.navigateTo({
-                url: '/pages-mine/pages/rules-for-sellbooks'
-            })
-        },
-        //时间选择
-        onTimeConfirm(data) {
-            this.selectedTime = data;
-            //格式化提交数据的时间
-            let date = this.$u.timeFormat(data.date, "yyyy-mm-dd");
-            let times = data.time.split("-");
-
-            this.submitData.schedulePickupStartTime = `${date} ${times[0]}`;
-            this.submitData.schedulePickupEndTime = `${date} ${times[1]}`;
-            console.log(date, this.submitData, "data");
-        },
-
-        //打开快递选择器
-        showExpressPicker() {
-            this.showPicker = true;
-        },
-        //确认选择快递
-        onExpressConfirm(e) {
-            if (!e.length) return;
-            let item = this.expressList[e[0]];
-            this.submitData.expressDelivery = item.name;
-            this.showPicker = false;
-        },
-        //添加或者选择地址
+        // 添加或者选择地址
         handleAddress() {
             uni.navigateTo({
                 url: `/pages-mine/pages/address/list?id=${this.defaultAddr.id}&isSelect=1`,
@@ -183,15 +113,7 @@ export default {
         
         submitOrder() {
             if (!this.submitData.addressId) {
-                this.$u.toast("请选择取货地址");
-                return;
-            }
-            if (!this.selectedTime.day) {
-                this.$u.toast("请选择取件时间");
-                return;
-            }
-            if (!this.agreed) {
-                this.$u.toast("请先同意用户协议");
+                this.$u.toast("请选择收货地址");
                 return;
             }
             // 显示确认弹窗
@@ -200,88 +122,65 @@ export default {
 
         // 确认提交订单
         handleConfirmSubmit() {
-            // 处理订单提交
-            uni.$u.http.post("/token/order/submitOrder", this.submitData).then((res) => {
+            uni.showLoading({ title: '提交中' });
+            this.$u.api.submitShopOrderAjax(this.submitData).then((res) => {
+                uni.hideLoading();
                 if (res.code == 200) {
-                    uni.navigateTo({
-                        url: "/pages-home/pages/order-success",
-                    });
+                    // 支付逻辑待定,暂时跳转成功页或提示
+                    this.$u.toast('下单成功');
+                    setTimeout(() => {
+                        uni.redirectTo({
+                            url: "/pages-home/pages/order-success",
+                        });
+                    }, 1000);
                     uni.removeStorageSync("selectAddr");
                 } else {
-                    uni.showToast({
-                        title: res.msg,
-                        icon: "none",
-                    });
+                    this.$u.toast(res.msg || '下单失败');
                 }
+            }).catch(() => {
+                uni.hideLoading();
             });
         },
-        //获取默认地址 /api/token/user/address/getDefault
-        getDefaultAddress() {
-            uni.$u.http.get("/token/user/address/getDefault").then((res) => {
-                if (res.code == 200) {
-                    this.defaultAddr = res.data;
-                    this.submitData.addressId = this.defaultAddr.id;
-                }
-            });
-        },
-        //获取当前用户未提交订单 /api/token/order/lastOrder
-        getLastOrder() {
-            uni.$u.http.get("/token/order/lastOrder").then((res) => {
-                if (res.code == 200) {
-                    this.books = res.data?.orderDetailList
-                        ? res.data.orderDetailList?.map((v) => {
-                              v.orderId = res.data.orderId;
-                              return v;
-                          })
-                        : [];
-
-                    this.submitData.orderId = this.books[0].orderId;
-                }
-            });
-        },
-        //获取预提交订单
+        
+        // 获取预提交订单信息
         getPreSubmitOrder() {
-            let orderId = uni.getStorageSync("orderId");
-            uni.$u.http.get("/token/order/preSubmit?orderId=" + orderId).then((res) => {
-                this.submitData.orderId = orderId;
-                this.defaultAddr = res.data.defaultAddress;
-                this.submitData.addressId = this.defaultAddr.id;
-
-                this.books = res.data.orderDetailList;
-                this.expressList = res.data.expressTypes.map((v) => ({
-                    name: v,
-                }));
-
-                this.totalRecycleMoney = res.data.totalRecycleMoney;
-                this.totalNum = res.data.totalNum;
-            });
-        },
-        goToAgreement() {
-            uni.navigateTo({
-                url: "/pages-home/pages/user-agreement",
+            uni.showLoading({ title: '加载中' });
+            this.$u.api.preSubmitOrderAjax({ 
+                cartIdList: this.cartIdList 
+            }).then((res) => {
+                uni.hideLoading();
+                if (res.code === 200) {
+                    const data = res.data;
+                    
+                    // 地址信息
+                    if (data.defaultAddress) {
+                        this.defaultAddr = data.defaultAddress;
+                        this.submitData.addressId = this.defaultAddr.id;
+                    }
+                    
+                    // 商品列表
+                    this.books = data.orderDetailList || [];
+                    
+                    // 价格汇总
+                    this.totalMoney = data.totalMoney;
+                    this.totalDiscount = data.discountMoney || 0;
+                    this.totalNum = data.totalQuantity;
+                    
+                } else {
+                    this.$u.toast(res.msg || '获取订单信息失败');
+                }
+            }).catch(() => {
+                uni.hideLoading();
             });
         },
     },
-    mounted() {
-        this.getPreSubmitOrder();
-    },
-
+    
     onShow() {
         let selectAddr = uni.getStorageSync("selectAddr");
         if (selectAddr) {
             this.defaultAddr = selectAddr;
             this.submitData.addressId = selectAddr.id;
         }
-
-        //处理从/pages-home/pages/order-success页面返回的情况,在执行返回一级页面
-        let pages = getCurrentPages();
-        let currentPage = pages[pages.length - 1];
-        console.log(pages, currentPage, "currentPage");
-        if (currentPage.route == "pages-home/pages/order-success") {
-            uni.navigateBack({
-                delta: 1,
-            });
-        }
     },
 };
 </script>
@@ -291,7 +190,7 @@ export default {
     min-height: 100vh;
     background: #f5f5f5;
     padding: 20rpx 30rpx;
-    padding-bottom: calc(env(safe-area-inset-bottom) + 190rpx);
+    padding-bottom: calc(env(safe-area-inset-bottom) + 120rpx);
 }
 
 .section-card {
@@ -307,32 +206,8 @@ export default {
     margin-left: 8rpx;
 }
 
-.time-card {
-    height: 80rpx;
-    background: #f8f8f8;
-    border-radius: 10rpx;
-}
-
-.free-pickup {
-    .pickup-title {
-        font-weight: bold;
-        font-size: 28rpx;
-        color: #333333;
-        margin-bottom: 10rpx;
-    }
-
-    .pickup-desc {
-        font-family: PingFang SC;
-        font-weight: 400;
-        font-size: 28rpx;
-        color: #999999;
-    }
-}
-
-.express-desc {
-    font-size: 24rpx;
-    color: #666;
-    line-height: 1.4;
+.book-list {
+    margin-bottom: 20rpx;
 }
 
 .bottom-bar {
@@ -343,30 +218,23 @@ export default {
     background: #fff;
     padding: 20rpx 30rpx;
     padding-bottom: env(safe-area-inset-bottom);
-
-    .agreement {
-        font-family: PingFang SC;
-        font-weight: 500;
-        font-size: 28rpx;
-        color: #333333;
-
-        &-link {
-            color: #07c160;
-        }
-    }
+    box-shadow: 0 -2rpx 10rpx rgba(0,0,0,0.05);
 
     .order-summary {
         display: flex;
         justify-content: space-between;
         align-items: center;
-        font-family: PingFang SC;
-        font-weight: 500;
-        font-size: 28rpx;
-        color: #999999;
 
-        .price {
-            color: #ff0000;
-            margin: 0 10rpx;
+        .total {
+            font-size: 28rpx;
+            color: #333;
+            
+            .price {
+                color: #ff0000;
+                font-size: 32rpx;
+                font-weight: bold;
+                margin: 0 8rpx;
+            }
         }
     }
 }