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

feat(product-gift): 小程序商品详情赠品选择区 + checkout 透传 giftItemIds

E2E Test Bot 2 недель назад
Родитель
Сommit
153100f91e

+ 128 - 0
cfc-frontend/pages/discover-detail/product-detail/product-detail.vue

@@ -25,6 +25,26 @@
           <view v-if="hasMemberPrice && !isMember" class="member-hint" @click="goUpgrade">加入会员享会员价 {{ formatPriceWithSymbol(memberPriceVal) }} ›</view>
         </view>
         <view v-if="product.giftLevelName" class="gift-tag">购买即送 {{ product.giftLevelName }} 会员</view>
+        <!-- 可选赠品选择区 -->
+        <view v-if="giftItems.length > 0" class="gift-select-section">
+          <text class="gift-select-title">选择赠品(可多选)</text>
+          <view class="gift-select-list">
+            <view
+              v-for="item in giftItems"
+              :key="getGiftItemKey(item)"
+              :class="isGiftSelected(item) ? 'gift-select-item gift-select-item-active' : 'gift-select-item'"
+              @click="toggleGift(item)"
+            >
+              <image v-if="item.giftType === 'product' && item.giftProductImage" class="gift-select-img" :src="getImageUrl(item.giftProductImage)" mode="aspectFill" />
+              <view v-else class="gift-select-cf">💎</view>
+              <view class="gift-select-info">
+                <text class="gift-select-name">{{ item.giftType === 'product' ? item.giftProductName : (item.cfValue + ' CF值') }}</text>
+                <text class="gift-select-type">{{ item.giftType === 'product' ? '赠品商品' : 'CF值赠品' }}</text>
+              </view>
+              <view v-if="isGiftSelected(item)" class="gift-select-check">✓</view>
+            </view>
+          </view>
+        </view>
         <view class="meta-row">
           <text class="meta-item" v-if="product.productType">{{ typeLabel }}</text>
           <text class="meta-item" v-if="product.domain">{{ domainLabel }}</text>
@@ -166,6 +186,8 @@ export default {
       isMember: false,
       memberLevel: 'FREE',
       bundleItems: []
+      // 赠品选择(giftConfigs 来自商品详情,当前用户等级匹配)
+      , selectedGiftIds: []
     }
   },
   computed: {
@@ -280,6 +302,20 @@ export default {
     },
     buyButtonText: function() {
       return '立即购买'
+    },
+    // 展平 giftConfigs 内所有赠品项(后端按当前等级返回,通常单配置)
+    giftItems: function() {
+      var list = []
+      var configs = this.product && this.product.giftConfigs
+      if (configs && configs.length > 0) {
+        for (var i = 0; i < configs.length; i++) {
+          var items = configs[i].items || []
+          for (var j = 0; j < items.length; j++) {
+            list.push(items[j])
+          }
+        }
+      }
+      return list
     }
   },
   onLoad(options) {
@@ -536,6 +572,20 @@ export default {
       var map = { 1: '快递配送', 2: '到店自提', 3: '快递/自提', 4: '线上服务', 5: '线上预约' }
       return map[method] || '快递配送'
     },
+    getGiftItemKey: function(item) {
+      return item.id
+    },
+    toggleGift: function(item) {
+      var idx = this.selectedGiftIds.indexOf(item.id)
+      if (idx > -1) {
+        this.selectedGiftIds.splice(idx, 1)
+      } else {
+        this.selectedGiftIds.push(item.id)
+      }
+    },
+    isGiftSelected: function(item) {
+      return this.selectedGiftIds.indexOf(item.id) > -1
+    },
 
     onBuy: function() {
       var userId = uni.getStorageSync('userId')
@@ -561,6 +611,8 @@ export default {
         productData.specDesc = this.buildSpecDesc()
       }
       uni.setStorageSync('checkoutItems', [productData])
+      // 透传赠品选择到 checkout
+      uni.setStorageSync('checkoutGiftItemIds', this.selectedGiftIds)
       var deliveryMethod = this.product.deliveryMethod || 1
       if (deliveryMethod > 1 && this.product.deliveryConfig) {
         productData.deliveryMethod = deliveryMethod
@@ -1063,4 +1115,80 @@ export default {
 .btn-share::after {
   border: none;
 }
+
+/* 赠品选择 */
+.gift-select-section {
+  background: #fff;
+  margin: 20rpx;
+  border-radius: 16rpx;
+  padding: 24rpx;
+}
+.gift-select-title {
+  font-size: 28rpx;
+  font-weight: bold;
+  color: #333;
+  margin-bottom: 16rpx;
+  display: block;
+}
+.gift-select-list {
+  display: flex;
+  flex-direction: column;
+}
+.gift-select-item {
+  display: flex;
+  align-items: center;
+  padding: 16rpx;
+  border: 2rpx solid #eee;
+  border-radius: 12rpx;
+  margin-bottom: 12rpx;
+  background: #fff;
+}
+.gift-select-item-active {
+  border-color: #F97316;
+  background: #FFF7ED;
+}
+.gift-select-img {
+  width: 80rpx;
+  height: 80rpx;
+  border-radius: 12rpx;
+  margin-right: 16rpx;
+  flex-shrink: 0;
+  background: #f7f7f7;
+}
+.gift-select-cf {
+  width: 80rpx;
+  height: 80rpx;
+  line-height: 80rpx;
+  text-align: center;
+  font-size: 40rpx;
+  margin-right: 16rpx;
+  flex-shrink: 0;
+  background: linear-gradient(135deg, #667eea22, #764ba222);
+  border-radius: 12rpx;
+}
+.gift-select-info {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  min-width: 0;
+}
+.gift-select-name {
+  font-size: 28rpx;
+  color: #333;
+  overflow: hidden;
+  text-overflow: ellipsis;
+  white-space: nowrap;
+}
+.gift-select-type {
+  font-size: 22rpx;
+  color: #999;
+  margin-top: 4rpx;
+}
+.gift-select-check {
+  color: #F97316;
+  font-size: 32rpx;
+  font-weight: bold;
+  margin-left: 16rpx;
+  flex-shrink: 0;
+}
 </style>

+ 6 - 1
cfc-frontend/pages/shop/checkout/checkout.vue

@@ -450,7 +450,8 @@ export default {
       showPickupLocationModal: false,
       showTimeSlotModal: false,
       showServicePersonModal: false,
-      showOnlineSlotModal: false
+       showOnlineSlotModal: false,
+       giftItemIds: []
     }
   },
   computed: {
@@ -508,6 +509,8 @@ export default {
   onLoad(options) {
     var that = this
     var stored = uni.getStorageSync('checkoutItems')
+    var giftIds = uni.getStorageSync('checkoutGiftItemIds')
+    this.giftItemIds = (giftIds && giftIds.length > 0) ? giftIds : []
     if (stored && stored.length > 0) {
       this.items = stored
       this.calcAmount()
@@ -862,6 +865,7 @@ export default {
         method: 'POST',
         data: {
           items: orderItems,
+          giftItemIds: that.giftItemIds,
           remark: this.remark,
           addressId: that.selectedAddress.id,
           purchaseInfo: that.purchaseForm,
@@ -881,6 +885,7 @@ export default {
             var order = res.data.data
             uni.showToast({ title: '下单成功', icon: 'success' })
             uni.removeStorageSync('checkoutItems')
+            uni.removeStorageSync('checkoutGiftItemIds')
             var moneyAmount = order.moneyAmount != null ? order.moneyAmount : order.totalAmount
             var pointsUsed = order.pointsUsed || 0
             setTimeout(function() {