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

fix(profile): 我的优惠券已领取券改转赠+使用 + 使用按券类型跳转

- coupons.vue: 已领取券不再显示「立即领取」(原点击报优惠券不存在),改为「转赠」+「使用」两按钮;
  使用按 applicableTo 分派:指定商品券(PRODUCT)→确认订单页(自动选券),全品类(ALL)→商城,会员券(MEMBERSHIP)→开通会员,兜底→商城
- coupons.vue: 未登录点击使用先弹登录浮层(openLoginSheet);productId 为空兜底跳商城;import 补 productDetail,移除 claimCoupon
- checkout.vue: onLoad 读 options.couponId(券实例ID) 存 _autoCouponId;loadCoupons 后经 autoSelectCoupon 在 ownedCoupons 中匹配自动选中并重算折扣;未命中静默忽略
E2E Test Bot 6 дней назад
Родитель
Сommit
1392c7e0bb

+ 92 - 19
cfc-frontend/pages/profile-extra/coupons.vue

@@ -78,13 +78,9 @@
             <text class="card-condition">{{ getConditionText(coupon.minSpend) }}</text>
             <text class="card-scope">{{ getScopeText(coupon.applicableTo, isMember) }}</text>
             <text class="card-expiry">有效期至 {{ formatDate(coupon.validUntil) }}</text>
-            <button
-              v-if="canClaim(coupon)"
-              class="claim-btn"
-              @click="handleClaim(coupon)"
-            >立即领取</button>
-            <view v-else-if="coupon.applicableTo === 'MEMBERSHIP' && !isMember && !coupon.isGift && (!coupon.status || coupon.status === 'AVAILABLE')" class="member-guide-btn" @click="goToMembership">
-              <text>去开通会员</text>
+            <view v-if="tabIndex === 0 && (!coupon.status || coupon.status === 'AVAILABLE')" class="coupon-actions">
+              <button class="use-btn" @click="useCoupon(coupon)">使用</button>
+              <button class="gift-btn" @click="giftCoupon(coupon)">转赠</button>
             </view>
           </view>
         </view>
@@ -94,7 +90,7 @@
 </template>
 
 <script>
-import { getMyCoupons, claimCoupon, getExchangeableCoupons, exchangeCouponByCf, getMyMembership } from '../../utils/api.js'
+import { getMyCoupons, getExchangeableCoupons, exchangeCouponByCf, getMyMembership, productDetail } from '../../utils/api.js'
 import { parseDate } from '../../utils/format.js'
 import fontScaleMixin from '@/mixins/font-scale.js'
 
@@ -232,25 +228,54 @@ export default {
         }
       })
     },
-    canClaim(coupon) {
-      // 非会员也可领取会员券,但使用时需开通会员
-      return this.tabIndex === 0 && (!coupon.status || coupon.status === 'AVAILABLE')
-    },
     goGiftCoupon() {
       uni.navigateTo({ url: '/pages/membership/gift-coupon' })
     },
     goToCouponCenter() {
       uni.navigateTo({ url: '/pages/profile-extra/coupon-center' })
     },
-    async handleClaim(coupon) {
-      try {
-        var res = await claimCoupon(coupon.id)
-        uni.showToast({ title: res.message || '领取成功', icon: 'success' })
-        this.loadCoupons()
-      } catch (e) {
-        uni.showToast({ title: e.message || '领取失败', icon: 'none' })
+    async useCoupon(coupon) {
+      var userId = uni.getStorageSync('userId')
+      if (!userId) {
+        this.openLoginSheet()
+        return
+      }
+      if (coupon.applicableTo === 'PRODUCT') {
+        if (!coupon.productId) {
+          uni.navigateTo({ url: '/pages/shop/index/index' })
+          return
+        }
+        try {
+          var res = await productDetail({ id: coupon.productId })
+          var d = res && res.data
+          if (!d) {
+            uni.navigateTo({ url: '/pages/shop/index/index' })
+            return
+          }
+          uni.setStorageSync('checkoutItems', [{
+            productId: d.id,
+            productName: d.name || '商品',
+            coverImage: d.coverImage || '',
+            unitPrice: d.salePrice != null ? d.salePrice : d.price,
+            quantity: 1
+          }])
+          uni.navigateTo({
+            url: '/pages/shop/checkout/checkout?from=coupon&id=' + d.id + '&couponId=' + coupon.id
+          })
+        } catch (e) {
+          uni.navigateTo({ url: '/pages/shop/index/index' })
+        }
+      } else if (coupon.applicableTo === 'ALL') {
+        uni.navigateTo({ url: '/pages/shop/index/index' })
+      } else if (coupon.applicableTo === 'MEMBERSHIP') {
+        uni.navigateTo({ url: '/pages/membership/upgrade' })
+      } else {
+        uni.navigateTo({ url: '/pages/shop/index/index' })
       }
     },
+    giftCoupon(coupon) {
+      uni.navigateTo({ url: '/pages/membership/gift-coupon' })
+    },
     getConditionText(minSpend) {
       if (!minSpend || minSpend <= 0) return '无门槛'
       return '满' + (minSpend / 100).toFixed(2) + '元可用'
@@ -544,6 +569,54 @@ export default {
   opacity: 0.85;
 }
 
+/* 已领取券操作区:使用 + 转赠 */
+.coupon-actions {
+  position: absolute;
+  right: 24rpx;
+  bottom: 20rpx;
+  display: flex;
+  align-items: center;
+  gap: 12rpx;
+}
+
+.use-btn {
+  background: linear-gradient(135deg, $brand-primary, $brand-primary-light);
+  color: $app-surface;
+  font-size: calc(24rpx * var(--font-scale, 1));
+  padding: 10rpx 24rpx;
+  border-radius: 24rpx;
+  border: none;
+  line-height: 1.4;
+  height: auto;
+}
+
+.use-btn::after {
+  border: none;
+}
+
+.use-btn:active {
+  opacity: 0.85;
+}
+
+.gift-btn {
+  background: $facade-fill;
+  color: $facade-text-strong;
+  font-size: calc(24rpx * var(--font-scale, 1));
+  padding: 10rpx 24rpx;
+  border-radius: 24rpx;
+  border: none;
+  line-height: 1.4;
+  height: auto;
+}
+
+.gift-btn::after {
+  border: none;
+}
+
+.gift-btn:active {
+  opacity: 0.85;
+}
+
 .gift-entry {
   display: flex;
   align-items: center;

+ 22 - 0
cfc-frontend/pages/shop/checkout/checkout.vue

@@ -513,6 +513,8 @@ export default {
       exchangeableCoupons: [],
       cfBalance: 0,
       selectedCoupon: null,
+      // 「我的优惠券」页跳转过来的券实例ID(from=coupon),用于自动选中
+      _autoCouponId: null,
       couponDiscount: 0,        // 服务端 /api/coupon/apply 返回的抵扣金额(分),前端只展示
       couponApplying: false,
       showCouponPicker: false,
@@ -670,6 +672,8 @@ export default {
     this.loadDefaultAddress()
     // 购买任务打卡:接受 taskId 并透传至支付页
     this.taskId = options && options.taskId ? parseInt(options.taskId) : null
+    // 「我的优惠券」页指定商品券跳转:from=coupon&couponId=券实例ID → loadCoupons 后自动选中
+    this._autoCouponId = options && options.couponId ? parseInt(options.couponId) : null
     this.loadRequiredFields()
     this.loadCoupons()
     this.loadExchangeableCoupons()
@@ -716,10 +720,28 @@ export default {
       try {
         var res = await getCouponList()
         this.coupons = res.data || []
+        this.autoSelectCoupon()
       } catch (e) {
         this.coupons = []
       }
     },
+    autoSelectCoupon() {
+      // 自动选中「我的优惠券」页透传的券(仅 from=coupon 场景,couponId=券实例ID):
+      // 在 ownedCoupons(已按商品/金额过滤)中匹配,命中则选中并触发服务端重算折扣;
+      // 未命中(minSpend 不满足 / 券已失效 / 券被过滤)静默忽略,不报错、不强制。
+      if (!this._autoCouponId) return
+      var matched = null
+      var list = this.ownedCoupons || []
+      for (var i = 0; i < list.length; i++) {
+        if (list[i] && parseInt(list[i].id) === this._autoCouponId) {
+          matched = list[i]
+          break
+        }
+      }
+      if (matched) {
+        this.selectCoupon(matched)
+      }
+    },
     async loadExchangeableCoupons() {
       try {
         var productId = null