赛事报名使用优惠劵
This commit is contained in:
parent
70763194b4
commit
5294f3a077
@ -216,6 +216,9 @@ Page({
|
||||
projectGroups: [], // 分组后的展示数据
|
||||
couponCode: '',
|
||||
totalPrice: 0,
|
||||
originalPrice: 0,
|
||||
discountPrice: 0,
|
||||
usedCouponId: null,
|
||||
submitting: false,
|
||||
openidLoading: false,
|
||||
myAuth: null
|
||||
@ -503,7 +506,12 @@ Page({
|
||||
_recalcAmountNow() {
|
||||
const items = buildCalculateAmountItems(this.data.projectGroups);
|
||||
if (!items.length) {
|
||||
this.setData({ totalPrice: 0 });
|
||||
this.setData({
|
||||
totalPrice: 0,
|
||||
originalPrice: 0,
|
||||
discountPrice: 0,
|
||||
usedCouponId: null
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
@ -514,34 +522,58 @@ Page({
|
||||
needAuth: true,
|
||||
data: {
|
||||
eventId: Number(this.data.eventId),
|
||||
registrationMode: 2,
|
||||
registrationMode: 1,
|
||||
items
|
||||
},
|
||||
contentType: 'application/json'
|
||||
})
|
||||
.then((res) => {
|
||||
// 兼容:后端可能返回 number / string / {amount}
|
||||
if (seq !== this._calcSeq) return;
|
||||
let amount = null;
|
||||
if (typeof res === 'number') amount = res;
|
||||
else if (typeof res === 'string') {
|
||||
|
||||
let totalAmount = null;
|
||||
let originalPrice = 0;
|
||||
let discountPrice = 0;
|
||||
|
||||
if (typeof res === 'number') {
|
||||
totalAmount = res;
|
||||
originalPrice = res;
|
||||
} else if (typeof res === 'string') {
|
||||
const n = Number(res);
|
||||
amount = Number.isNaN(n) ? null : n;
|
||||
totalAmount = Number.isNaN(n) ? null : n;
|
||||
originalPrice = totalAmount || 0;
|
||||
} else if (res && typeof res === 'object') {
|
||||
const v = res.amount != null ? res.amount : res.payAmount != null ? res.payAmount : res.totalAmount;
|
||||
const n = Number(v);
|
||||
amount = Number.isNaN(n) ? null : n;
|
||||
// 从统一的 CalculateAmountOutput 中获取数据
|
||||
totalAmount = res.totalAmount != null ? Number(res.totalAmount) :
|
||||
(res.amount != null ? Number(res.amount) :
|
||||
(res.payAmount != null ? Number(res.payAmount) : null));
|
||||
originalPrice = res.originalAmount != null ? Number(res.originalAmount) : totalAmount || 0;
|
||||
discountPrice = res.discountAmount != null ? Number(res.discountAmount) : 0;
|
||||
}
|
||||
if (amount == null) {
|
||||
|
||||
if (totalAmount == null) {
|
||||
// fallback:仍然展示前端计算的费用,避免空白
|
||||
this.setData({ totalPrice: calcTotalPrice(this.data.projectGroups) });
|
||||
const fallbackPrice = calcTotalPrice(this.data.projectGroups);
|
||||
this.setData({
|
||||
totalPrice: fallbackPrice,
|
||||
originalPrice: fallbackPrice,
|
||||
discountPrice: 0
|
||||
});
|
||||
} else {
|
||||
this.setData({ totalPrice: amount });
|
||||
this.setData({
|
||||
totalPrice: totalAmount,
|
||||
originalPrice: originalPrice || totalAmount,
|
||||
discountPrice: discountPrice || 0
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
if (seq !== this._calcSeq) return;
|
||||
this.setData({ totalPrice: calcTotalPrice(this.data.projectGroups) });
|
||||
const fallbackPrice = calcTotalPrice(this.data.projectGroups);
|
||||
this.setData({
|
||||
totalPrice: fallbackPrice,
|
||||
originalPrice: fallbackPrice,
|
||||
discountPrice: 0
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
@ -549,6 +581,51 @@ Page({
|
||||
this.setData({ couponCode: (e.detail.value || '').trim() });
|
||||
},
|
||||
|
||||
/**
|
||||
* 验证并应用优惠券
|
||||
*/
|
||||
async onValidateCoupon() {
|
||||
const couponCode = this.data.couponCode;
|
||||
if (!couponCode) {
|
||||
wx.showToast({ title: '请输入优惠券兑换码', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
|
||||
wx.showLoading({ title: '验证中', mask: true });
|
||||
try {
|
||||
const res = await request({
|
||||
path: API.couponValidate,
|
||||
method: 'POST',
|
||||
needAuth: true,
|
||||
data: {
|
||||
eventId: Number(this.data.eventId),
|
||||
couponCode: couponCode,
|
||||
originalAmount: this.data.originalPrice || this.data.totalPrice
|
||||
},
|
||||
contentType: 'application/json'
|
||||
});
|
||||
|
||||
if (res && res.isValid) {
|
||||
this.setData({
|
||||
usedCouponId: res.couponId,
|
||||
totalPrice: res.finalAmount,
|
||||
discountPrice: res.discountAmount || 0,
|
||||
originalPrice: res.originalAmount || this.data.originalPrice
|
||||
});
|
||||
wx.showToast({ title: '优惠券应用成功', icon: 'success' });
|
||||
} else {
|
||||
wx.showToast({ title: res.invalidReason || '优惠券无效', icon: 'none' });
|
||||
}
|
||||
} catch (error) {
|
||||
wx.showToast({
|
||||
title: error && error.message ? error.message : '验证失败,请稍后重试',
|
||||
icon: 'none'
|
||||
});
|
||||
} finally {
|
||||
wx.hideLoading();
|
||||
}
|
||||
},
|
||||
|
||||
/** 收集已选的 projectId + groupId */
|
||||
_getSelectedItems() {
|
||||
const items = [];
|
||||
@ -717,12 +794,16 @@ Page({
|
||||
},
|
||||
|
||||
/** 获取支付信息 */
|
||||
_getPaymentInfo(registrationId, openId) {
|
||||
_getPaymentInfo(registrationId, openid) {
|
||||
return request({
|
||||
path: '/api/eventclient/payment-info',
|
||||
method: 'POST',
|
||||
needAuth: true,
|
||||
data: { registrationId, openId },
|
||||
data: {
|
||||
registrationId,
|
||||
openId: openid,
|
||||
couponId: this.data.usedCouponId
|
||||
},
|
||||
contentType: 'application/json'
|
||||
});
|
||||
},
|
||||
|
||||
@ -29,10 +29,25 @@
|
||||
<!-- 优惠券 -->
|
||||
<view class="coupon-row">
|
||||
<input class="coupon-input" placeholder="请输入优惠卷兑换码" placeholder-class="coupon-placeholder" value="{{couponCode}}" bindinput="onCouponInput" />
|
||||
<view class="coupon-icon">
|
||||
<text class="coupon-icon-text">⊙</text>
|
||||
<button class="coupon-btn" bindtap="onValidateCoupon">验证</button>
|
||||
</view>
|
||||
|
||||
<!-- 金额显示 -->
|
||||
<view class="price-section">
|
||||
<view wx:if="{{discountPrice > 0}}" class="price-row">
|
||||
<text class="price-label">原价:</text>
|
||||
<text class="original-price">¥{{originalPrice}}</text>
|
||||
</view>
|
||||
<view wx:if="{{discountPrice > 0}}" class="price-row">
|
||||
<text class="price-label">优惠:</text>
|
||||
<text class="discount-price">-¥{{discountPrice}}</text>
|
||||
</view>
|
||||
<view class="price-row total">
|
||||
<text class="price-label">合计:</text>
|
||||
<text class="total-price">¥{{totalPrice}}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 底部占位,避免被 bottom-bar 遮挡 -->
|
||||
<view style="height: 200rpx;"></view>
|
||||
</scroll-view>
|
||||
@ -40,7 +55,14 @@
|
||||
<view class="bottom-bar">
|
||||
<button class="btn-offline" bindtap="onPayOffline" loading="{{submitting}}">线下支付</button>
|
||||
<button class="btn-online" bindtap="onPayOnline" loading="{{submitting}}">
|
||||
¥{{totalPrice}} 线上支付
|
||||
<view wx:if="{{discountPrice > 0}}" class="price-display">
|
||||
<text class="small-price">¥{{originalPrice}}</text>
|
||||
<text class="main-price">¥{{totalPrice}}</text>
|
||||
</view>
|
||||
<view wx:else class="price-display">
|
||||
<text class="main-price">¥{{totalPrice}}</text>
|
||||
</view>
|
||||
<text class="btn-text">线上支付</text>
|
||||
</button>
|
||||
</view>
|
||||
</block>
|
||||
|
||||
@ -140,18 +140,64 @@
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
.coupon-icon {
|
||||
.coupon-btn {
|
||||
flex-shrink: 0;
|
||||
width: 56rpx;
|
||||
height: 56rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
background: #1a56db;
|
||||
color: #ffffff;
|
||||
font-size: 26rpx;
|
||||
padding: 0 32rpx;
|
||||
height: 72rpx;
|
||||
line-height: 72rpx;
|
||||
border-radius: 36rpx;
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
|
||||
.coupon-icon-text {
|
||||
font-size: 44rpx;
|
||||
color: #c0c4cc;
|
||||
.coupon-btn::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
/* ===== 金额显示区域 ===== */
|
||||
.price-section {
|
||||
background: #ffffff;
|
||||
border-radius: 24rpx;
|
||||
padding: 32rpx;
|
||||
margin-bottom: 24rpx;
|
||||
}
|
||||
|
||||
.price-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.price-row.total {
|
||||
margin-bottom: 0;
|
||||
padding-top: 20rpx;
|
||||
border-top: 1rpx solid #f0f0f0;
|
||||
}
|
||||
|
||||
.price-label {
|
||||
font-size: 28rpx;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.original-price {
|
||||
font-size: 28rpx;
|
||||
color: #999;
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.discount-price {
|
||||
font-size: 28rpx;
|
||||
color: #e53935;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.total-price {
|
||||
font-size: 36rpx;
|
||||
color: #1a56db;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* ===== 底部按钮栏 ===== */
|
||||
@ -177,6 +223,10 @@
|
||||
font-weight: 600;
|
||||
border: none;
|
||||
letter-spacing: 2rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.btn-offline {
|
||||
@ -197,19 +247,23 @@
|
||||
border: none;
|
||||
}
|
||||
|
||||
|
||||
.bottom-bar {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 18rpx 16rpx 24rpx;
|
||||
background: rgba(255, 255, 255, 0.95);
|
||||
border-top: 1rpx solid rgba(0, 0, 0, 0.06);
|
||||
.price-display {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
gap: 8rpx;
|
||||
}
|
||||
|
||||
.submit-btn {
|
||||
width: 100%;
|
||||
height: 86rpx;
|
||||
.small-price {
|
||||
font-size: 24rpx;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
.main-price {
|
||||
font-size: 34rpx;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.btn-text {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
|
||||
@ -13,6 +13,7 @@ const API = {
|
||||
apply: '/api/eventclient/apply',
|
||||
calculateAmount: '/api/eventclient/calculate-amount',
|
||||
cancelRegistrationRefund: '/api/eventclient/cancel-registration-refund',
|
||||
couponValidate: '/api/eventclient/coupon/validate',
|
||||
|
||||
wechatLogin: '/api/oauth/login',
|
||||
loginByCode: '/api/oauth/login',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user