diff --git a/pages/apply/apply.js b/pages/apply/apply.js index 9893981..55f57ed 100644 --- a/pages/apply/apply.js +++ b/pages/apply/apply.js @@ -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' }); }, diff --git a/pages/apply/apply.wxml b/pages/apply/apply.wxml index 8160529..a5706f9 100644 --- a/pages/apply/apply.wxml +++ b/pages/apply/apply.wxml @@ -29,10 +29,25 @@ - - ⊙ + + + + + + + 原价: + ¥{{originalPrice}} + + + 优惠: + -¥{{discountPrice}} + + + 合计: + ¥{{totalPrice}} + @@ -40,7 +55,14 @@ diff --git a/pages/apply/apply.wxss b/pages/apply/apply.wxss index dcc962b..290ecf0 100644 --- a/pages/apply/apply.wxss +++ b/pages/apply/apply.wxss @@ -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; +} diff --git a/utils/constants.js b/utils/constants.js index 4840a52..73c8aff 100644 --- a/utils/constants.js +++ b/utils/constants.js @@ -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',