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

fix: 修复购物车和订单确认页的价格计算与红包逻辑

修复购物车总价计算使用错误的字段名 `productPrice` 应为 `price`
在订单确认页动态显示快递费用,并修正红包选择逻辑
红包弹窗现在使用后端返回的优惠券列表,并支持空状态显示
计算最终总价时正确减去所选红包面额
ylong 2 недель назад
Родитель
Сommit
02420e5ca9
3 измененных файлов с 149 добавлено и 116 удалено
  1. 125 107
      pages-car/components/red-packet-popup.vue
  2. 23 8
      pages-car/pages/confirm-order.vue
  3. 1 1
      pages-car/pages/index.vue

+ 125 - 107
pages-car/components/red-packet-popup.vue

@@ -5,21 +5,27 @@
                 <text>选择红包</text>
                 <u-icon name="close" size="28" color="#999" class="close-icon" @click="close"></u-icon>
             </view>
-            
-            <scroll-view scroll-y class="packet-list">
+
+            <scroll-view scroll-y class="packet-list" v-if="list && list.length > 0">
                 <view class="packet-item" v-for="(item, index) in list" :key="index" @click="selectPacket(index)">
                     <view class="packet-info">
-                        <view class="packet-name">{{ item.name }}</view>
-                        <view class="packet-desc">{{ item.desc }}</view>
-                        <view class="packet-time">{{ item.time }}</view>
+                        <view class="packet-name">{{ item.couponName }} ¥{{ item.faceMoney }}</view>
+                        <view class="packet-desc">满{{ item.thresholdMoney }}元可用</view>
+                        <view class="packet-time">{{ item.effectStartTime }}至{{ item.effectEndTime }}</view>
                     </view>
                     <view class="packet-check">
-                        <u-icon name="checkmark-circle-fill" size="40" :color="selectedIndex === index ? '#ff4500' : '#ccc'"></u-icon>
+                        <u-icon name="checkmark-circle-fill" size="40"
+                            :color="selectedIndex === index ? '#ff4500' : '#ccc'"></u-icon>
                     </view>
                 </view>
             </scroll-view>
-            
-            <view class="popup-footer">
+            <view class="empty-state" v-else>
+                <image src="https://shuhi.oss-cn-qingdao.aliyuncs.com/mini/no-data.png"
+                    style="width: 260rpx; height: 260rpx" mode="aspectFit"></image>
+                <view class="empty-text">暂无可用红包</view>
+            </view>
+
+            <view class="popup-footer" v-if="list && list.length > 0">
                 <u-button type="primary" shape="circle" @click="confirm">确认</u-button>
             </view>
         </view>
@@ -27,117 +33,129 @@
 </template>
 
 <script>
-export default {
-    props: {
-        value: {
-            type: Boolean,
-            default: false
-        }
-    },
-    data() {
-        return {
-            show: false,
-            selectedIndex: 0,
-            list: [
-                {
-                    name: '惊喜红包(满15减5)',
-                    desc: '书嗨所有商品(不包含邮费)',
-                    time: '2024-01-01 00:00:00至2024-12-31 23:59:59'
-                },
-                {
-                    name: '惊喜红包(满15减5)',
-                    desc: '书嗨所有商品(不包含邮费)',
-                    time: '2024-01-01 00:00:00至2024-12-31 23:59:59'
-                }
-            ]
-        }
-    },
-    watch: {
-        value(val) {
-            this.show = val;
+    export default {
+        props: {
+            value: {
+                type: Boolean,
+                default: false
+            },
+            list: {
+                type: Array,
+                default: () => []
+            }
         },
-        show(val) {
-            this.$emit('input', val);
-        }
-    },
-    methods: {
-        close() {
-            this.show = false;
+        data() {
+            return {
+                show: false,
+                selectedIndex: -1,
+            }
         },
-        selectPacket(index) {
-            this.selectedIndex = index;
+        watch: {
+            value(val) {
+                this.show = val;
+            },
+            show(val) {
+                this.$emit('input', val);
+            }
         },
-        confirm() {
-            this.$emit('confirm', this.list[this.selectedIndex]);
-            this.close();
+        methods: {
+            close() {
+                this.show = false;
+            },
+            selectPacket(index) {
+                this.selectedIndex = index;
+            },
+            confirm() {
+                if (this.list && this.list.length > 0 && this.selectedIndex !== -1) {
+                    this.$emit('confirm', this.list[this.selectedIndex]);
+                } else {
+                    this.$emit('confirm', null);
+                }
+                this.close();
+            }
         }
     }
-}
 </script>
 
 <style lang="scss" scoped>
-.red-packet-popup {
-    height: 100%;
-    display: flex;
-    flex-direction: column;
-    overflow: hidden;
-    
-    .popup-header {
-        padding: 30rpx;
-        text-align: center;
-        font-size: 32rpx;
-        font-weight: bold;
-        position: sticky;
-        top: 0;
-        background-color: #fff;
-        border-bottom: 1rpx solid #eee;
-        
-        .close-icon {
-            position: absolute;
-            right: 30rpx;
-            top: 40rpx;
+    .red-packet-popup {
+        height: 100%;
+        max-height: 80vh;
+        display: flex;
+        flex-direction: column;
+        overflow: hidden;
+
+        .popup-header {
+            padding: 30rpx;
+            text-align: center;
+            font-size: 32rpx;
+            font-weight: bold;
+            position: sticky;
+            top: 0;
+            background-color: #fff;
+            border-bottom: 1rpx solid #eee;
+
+            .close-icon {
+                position: absolute;
+                right: 30rpx;
+                top: 40rpx;
+            }
         }
-    }
-    
-    .packet-list {
-        flex: 1;
-        padding: 20rpx;
-        box-sizing: border-box;
-        max-height: 400px;
-        overflow-y: auto;
-        
-        .packet-item {
+
+        .packet-list {
+            flex: 1;
+            padding: 20rpx;
+            box-sizing: border-box;
+            height: 600rpx; // Give it a fixed height for scroll
+            overflow-y: auto;
+
+            .packet-item {
+                display: flex;
+                align-items: center;
+                padding: 20rpx 30rpx;
+                border-bottom: 1rpx solid #f5f5f5;
+
+                .packet-info {
+                    flex: 1;
+
+                    .packet-name {
+                        font-size: 30rpx;
+                        color: #ff4500;
+                        font-weight: bold;
+                        margin-bottom: 10rpx;
+                    }
+
+                    .packet-desc {
+                        font-size: 26rpx;
+                        color: #333;
+                        margin-bottom: 6rpx;
+                    }
+
+                    .packet-time {
+                        font-size: 22rpx;
+                        color: #999;
+                    }
+                }
+            }
+        }
+
+        .empty-state {
             display: flex;
+            flex-direction: column;
             align-items: center;
-            padding: 20rpx 30rpx;
-            
-            .packet-info {
-                flex: 1;
-                
-                .packet-name {
-                    font-size: 30rpx;
-                    color: #ff4500;
-                    font-weight: bold;
-                    margin-bottom: 10rpx;
-                }
-                
-                .packet-desc {
-                    font-size: 26rpx;
-                    color: #333;
-                    margin-bottom: 6rpx;
-                }
-                
-                .packet-time {
-                    font-size: 22rpx;
-                    color: #999;
-                }
+            justify-content: center;
+            padding: 60rpx 0;
+
+            .empty-text {
+                color: #999;
+                font-size: 28rpx;
+                margin-top: 20rpx;
             }
         }
+
+        .popup-footer {
+            padding: 20rpx 40rpx;
+            padding-bottom: env(safe-area-inset-bottom);
+        }
     }
-    
-    .popup-footer {
-        padding: 20rpx 40rpx;
-        padding-bottom: env(safe-area-inset-bottom);
-    }
-}
 </style>

+ 23 - 8
pages-car/pages/confirm-order.vue

@@ -36,9 +36,9 @@
         <!-- 订单信息 -->
         <view class="section-card mt-20" style="padding: 0; overflow: hidden;">
             <u-cell-group :border="false">
-                <u-cell-item title="配送服务" value="快递 免邮" :arrow="false" :title-style="{ color: '#333' }"
-                    :value-style="{ color: '#333' }"></u-cell-item>
-                <u-cell-item title="红包" :value="selectedPacket ? selectedPacket.name : ''" @click="showRedPacket = true"
+                <u-cell-item title="配送服务" :value="!preOrder.expressMoney ? '快递 免邮' : '快递费 ' + preOrder.expressMoney + '元'" :arrow="false"
+                    :title-style="{ color: '#333' }" :value-style="{ color: '#333' }"></u-cell-item>
+                <u-cell-item title="红包" :value="redPacketText" @click="showRedPacket = true"
                     :title-style="{ color: '#333' }"></u-cell-item>
                 <u-cell-item title="订单备注" :arrow="false" :title-style="{ color: '#333' }">
                     <u-input slot="right-icon" v-model="submitData.remark" type="text" placeholder="无备注"
@@ -56,14 +56,15 @@
                 <view class="total">
                     共<text class="num">{{ preOrder.totalQuantity }}</text>本
                     <text class="reduce-text" v-if="preOrder.discountMoney > 0">已降¥{{ preOrder.discountMoney }}</text>
-                    合计: <text class="price">¥{{ preOrder.totalMoney }}</text>
+                    合计: <text class="price">¥{{ finalTotalMoney }}</text>
                 </view>
                 <u-button type="primary" shape="circle" @click="submitOrder">提交订单</u-button>
             </view>
         </view>
 
         <submit-confirm ref="submitConfirmDialog" @confirm="handleConfirmSubmit"></submit-confirm>
-        <red-packet-popup v-model="showRedPacket" @confirm="onRedPacketConfirm"></red-packet-popup>
+        <red-packet-popup v-model="showRedPacket" :list="preOrder.couponList"
+            @confirm="onRedPacketConfirm"></red-packet-popup>
         <stock-shortage-popup v-model="showStockShortage" @confirm="onStockShortageConfirm"></stock-shortage-popup>
     </view>
 </template>
@@ -116,6 +117,21 @@
                 preOrder: {},// 预订单信息
             };
         },
+        computed: {
+            redPacketText() {
+                if (!this.preOrder.couponList || this.preOrder.couponList.length === 0) {
+                    return '暂无红包';
+                }
+                return this.selectedPacket ? this.selectedPacket.couponName : '请选择';
+            },
+            finalTotalMoney() {
+                let total = parseFloat(this.preOrder.totalMoney || 0);
+                if (this.selectedPacket && this.selectedPacket.faceMoney) {
+                    total -= parseFloat(this.selectedPacket.faceMoney);
+                }
+                return total < 0 ? '0.00' : total.toFixed(2);
+            }
+        },
         onLoad(options) {
             // 从本地存储获取提交订单数据
             const preSubmitOrderData = uni.getStorageSync('preSubmitOrderData');
@@ -142,12 +158,11 @@
 
             onRedPacketConfirm(packet) {
                 this.selectedPacket = packet;
-                if (packet && packet.id) {
-                    this.submitData.userCouponIds = [packet.id];
+                if (packet && packet.userCouponId) {
+                    this.submitData.userCouponIds = [packet.userCouponId];
                 } else {
                     this.submitData.userCouponIds = [];
                 }
-                // Recalculate price if needed
             },
 
             onStockShortageConfirm(option) {

+ 1 - 1
pages-car/pages/index.vue

@@ -154,7 +154,7 @@
                 return this.cartList.filter(item => item.checked && item.stockStatus !== 3 && item.availableStock > 0);
             },
             totalPrice() {
-                return this.selectedItems.reduce((sum, item) => sum + item.productPrice * item.quantity, 0).toFixed(2);
+                return this.selectedItems.reduce((sum, item) => sum + item.price * item.quantity, 0).toFixed(2);
             },
             totalReduced() {
                 return this.selectedItems.reduce((sum, item) => sum + (item.currReduceMoney || 0) * item.quantity, 0).toFixed(2);