|
@@ -237,16 +237,23 @@
|
|
|
<!-- Coupon Section -->
|
|
<!-- Coupon Section -->
|
|
|
<view v-if="!isSpecialCouponDisabled" class="section coupon-section" @click="openCouponPicker">
|
|
<view v-if="!isSpecialCouponDisabled" class="section coupon-section" @click="openCouponPicker">
|
|
|
<view class="info-row">
|
|
<view class="info-row">
|
|
|
- <text class="info-label">优惠券</text>
|
|
|
|
|
|
|
+ <text class="info-label">
|
|
|
|
|
+ 优惠券
|
|
|
|
|
+ <text v-if="!selectedCoupon && ownedCoupons.length > 0" class="coupon-count-hint">({{ ownedCoupons.length }}张)</text>
|
|
|
|
|
+ </text>
|
|
|
<view class="coupon-selected" v-if="selectedCoupon">
|
|
<view class="coupon-selected" v-if="selectedCoupon">
|
|
|
<text class="coupon-discount">-{{ formatPriceWithSymbol(couponDiscount) }}</text>
|
|
<text class="coupon-discount">-{{ formatPriceWithSymbol(couponDiscount) }}</text>
|
|
|
<text class="coupon-name-tag">{{ selectedCoupon.name }}</text>
|
|
<text class="coupon-name-tag">{{ selectedCoupon.name }}</text>
|
|
|
</view>
|
|
</view>
|
|
|
<view class="coupon-placeholder" v-else>
|
|
<view class="coupon-placeholder" v-else>
|
|
|
- <text class="placeholder-text">选择优惠券</text>
|
|
|
|
|
|
|
+ <text class="placeholder-text" :class="{ 'placeholder-available': ownedCoupons.length > 0 }">
|
|
|
|
|
+ {{ ownedCoupons.length > 0 ? '有 ' + ownedCoupons.length + ' 张优惠券可抵,点击选择' : '选择优惠券' }}
|
|
|
|
|
+ </text>
|
|
|
</view>
|
|
</view>
|
|
|
<text class="address-arrow">›</text>
|
|
<text class="address-arrow">›</text>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
+ <text v-if="_autoSelected && !selectedCoupon && ownedCoupons.length === 0" class="coupon-auto-hint">已按最优自动选择</text>
|
|
|
|
|
+ <text v-if="selectedCoupon" class="coupon-auto-hint">已按最优自动选择</text>
|
|
|
</view>
|
|
</view>
|
|
|
<view v-if="!isMember" class="section member-coupon-hint">
|
|
<view v-if="!isMember" class="section member-coupon-hint">
|
|
|
<view class="member-coupon-hint-content">
|
|
<view class="member-coupon-hint-content">
|
|
@@ -510,6 +517,10 @@ export default {
|
|
|
selectedCoupon: null,
|
|
selectedCoupon: null,
|
|
|
// 「我的优惠券」页跳转过来的券实例ID(from=coupon),用于自动选中
|
|
// 「我的优惠券」页跳转过来的券实例ID(from=coupon),用于自动选中
|
|
|
_autoCouponId: null,
|
|
_autoCouponId: null,
|
|
|
|
|
+ // 本次会话是否已自动选过最优券(避免重复触发)
|
|
|
|
|
+ _autoSelected: false,
|
|
|
|
|
+ // 本次会话用户是否手动取消过券(取消后不再自动选)
|
|
|
|
|
+ _couponDismissed: false,
|
|
|
couponDiscount: 0, // 服务端 /api/coupon/apply 返回的抵扣金额(分),前端只展示
|
|
couponDiscount: 0, // 服务端 /api/coupon/apply 返回的抵扣金额(分),前端只展示
|
|
|
couponApplying: false,
|
|
couponApplying: false,
|
|
|
showCouponPicker: false,
|
|
showCouponPicker: false,
|
|
@@ -721,20 +732,44 @@ export default {
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
autoSelectCoupon() {
|
|
autoSelectCoupon() {
|
|
|
- // 自动选中「我的优惠券」页透传的券(仅 from=coupon 场景,couponId=券实例ID):
|
|
|
|
|
- // 在 ownedCoupons(已按商品/金额过滤)中匹配,命中则选中并触发服务端重算折扣;
|
|
|
|
|
- // 未命中(minSpend 不满足 / 券已失效 / 券被过滤)静默忽略,不报错、不强制。
|
|
|
|
|
- if (!this._autoCouponId) return
|
|
|
|
|
- var matched = null
|
|
|
|
|
|
|
+ // 优先处理 from=coupon 深链场景:按 couponId 匹配选中的券。
|
|
|
|
|
+ if (this._autoCouponId) {
|
|
|
|
|
+ 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)
|
|
|
|
|
+ this._autoSelected = true
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ // 未命中则静默忽略,继续走下面的最优券逻辑
|
|
|
|
|
+ }
|
|
|
|
|
+ // 非深链场景:若用户本次尚未手动取消过券,则自动选最优券(value最大,同值取minSpend最小)。
|
|
|
|
|
+ if (this._couponDismissed || this._autoSelected) return
|
|
|
var list = this.ownedCoupons || []
|
|
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 (list.length === 0) return
|
|
|
|
|
+ var best = null
|
|
|
|
|
+ for (var j = 0; j < list.length; j++) {
|
|
|
|
|
+ var c = list[j]
|
|
|
|
|
+ if (!c) continue
|
|
|
|
|
+ if (!best) {
|
|
|
|
|
+ best = c
|
|
|
|
|
+ } else {
|
|
|
|
|
+ var bestValue = best.value || 0
|
|
|
|
|
+ var curValue = c.value || 0
|
|
|
|
|
+ if (curValue > bestValue || (curValue === bestValue && (c.minSpend || 0) < (best.minSpend || 0))) {
|
|
|
|
|
+ best = c
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
- if (matched) {
|
|
|
|
|
- this.selectCoupon(matched)
|
|
|
|
|
|
|
+ if (best) {
|
|
|
|
|
+ this.selectCoupon(best)
|
|
|
|
|
+ this._autoSelected = true
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
async loadExchangeableCoupons() {
|
|
async loadExchangeableCoupons() {
|
|
@@ -829,6 +864,7 @@ export default {
|
|
|
this.selectedCoupon = null
|
|
this.selectedCoupon = null
|
|
|
this.couponDiscount = 0
|
|
this.couponDiscount = 0
|
|
|
this.showCouponPicker = false
|
|
this.showCouponPicker = false
|
|
|
|
|
+ this._couponDismissed = true
|
|
|
},
|
|
},
|
|
|
// 优惠券抵扣统一由服务端计算(券类型/满减门槛/会员范围/折扣千分比),
|
|
// 优惠券抵扣统一由服务端计算(券类型/满减门槛/会员范围/折扣千分比),
|
|
|
// 前端不再用 selectedCoupon.value 本地减额,保证展示与下单落库一致。
|
|
// 前端不再用 selectedCoupon.value 本地减额,保证展示与下单落库一致。
|
|
@@ -1494,6 +1530,23 @@ showOnlineSlotPicker: function() {
|
|
|
font-size: calc(26rpx * var(--font-scale, 1));
|
|
font-size: calc(26rpx * var(--font-scale, 1));
|
|
|
color: $facade-text-ghost;
|
|
color: $facade-text-ghost;
|
|
|
}
|
|
}
|
|
|
|
|
+.placeholder-available {
|
|
|
|
|
+ color: $brand-primary;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+}
|
|
|
|
|
+.coupon-count-hint {
|
|
|
|
|
+ font-size: calc(22rpx * var(--font-scale, 1));
|
|
|
|
|
+ color: $brand-primary;
|
|
|
|
|
+ margin-left: 8rpx;
|
|
|
|
|
+ font-weight: 500;
|
|
|
|
|
+}
|
|
|
|
|
+.coupon-auto-hint {
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ font-size: calc(22rpx * var(--font-scale, 1));
|
|
|
|
|
+ color: $brand-primary;
|
|
|
|
|
+ margin-top: 8rpx;
|
|
|
|
|
+ opacity: 0.8;
|
|
|
|
|
+}
|
|
|
|
|
|
|
|
.total-original {
|
|
.total-original {
|
|
|
font-size: calc(22rpx * var(--font-scale, 1));
|
|
font-size: calc(22rpx * var(--font-scale, 1));
|