update:赛事详情&提示

This commit is contained in:
ltx 2026-04-14 16:17:38 +08:00
parent 00a157e7b5
commit a3dd704d29
5 changed files with 154 additions and 53 deletions

View File

@ -257,7 +257,7 @@ Page({
})
.then((detail) => {
const es = getEventStatus(detail);
console.log( 'es', es);
console.log('es', es);
if (es !== null && es !== 1) {
wx.showToast({ title: toastForBlockedEventStatus(es), icon: 'none' });
setTimeout(() => {
@ -292,16 +292,15 @@ Page({
.then((res) => {
const d = res && typeof res === 'object' ? res : null;
const birthDate = d ? (d.birthDate || d.birthday || '') : '';
console.log('birthDate', birthDate);
if (!birthDate) {
wx.showModal({
title: '需先完成认证',
content: '报名个人项目需要先完成身份认证(用于校验年龄与性别限制)。',
confirmText: '去个人中心',
cancelText: '取消',
success: (r) => {
content: '报名个人项目需要先完成身份认证。',
success(r) {
if (r.confirm) wx.switchTab({ url: '/pages/mine/mine' });
}
});
})
this.setData({ myAuth: null });
return null;
}
@ -312,7 +311,6 @@ Page({
wx.showModal({
title: '获取认证信息失败',
content: '请稍后重试,或前往个人中心完成认证。',
confirmText: '去个人中心',
cancelText: '取消',
success: (r) => {
if (r.confirm) wx.switchTab({ url: '/pages/mine/mine' });
@ -392,7 +390,7 @@ Page({
wx.showModal({
title: '换取 openid 失败',
content: `请确认后端接口路径:${API.wechatOpenidByJsCode}\n\n可临时复制 code 给服务端调试:\n${code}`,
confirmText: '复制 code',
confirmText: '复制',
cancelText: '关闭',
success: (r) => {
if (r.confirm) wx.setClipboardData({ data: code });
@ -412,8 +410,15 @@ Page({
onGroupTap(e) {
const { projectId, groupId } = e.currentTarget.dataset;
if (!this.data.myAuth) {
wx.showToast({ title: '请先完成身份认证', icon: 'none' });
wx.switchTab({ url: '/pages/mine/mine' });
console.log('myAuth', this.data.myAuth);
wx.showModal({
title: '需先完成认证',
content: '报名个人项目需要先完成身份认证。',
cancelText: '取消',
success: (r) => {
if (r.confirm) wx.switchTab({ url: '/pages/mine/mine' });
}
});
return;
}
@ -506,7 +511,7 @@ Page({
_recalcAmountNow() {
const items = buildCalculateAmountItems(this.data.projectGroups);
if (!items.length) {
this.setData({
this.setData({
totalPrice: 0,
originalPrice: 0,
discountPrice: 0,
@ -529,11 +534,11 @@ Page({
})
.then((res) => {
if (seq !== this._calcSeq) return;
let totalAmount = null;
let originalPrice = 0;
let discountPrice = 0;
if (typeof res === 'number') {
totalAmount = res;
originalPrice = res;
@ -543,23 +548,23 @@ Page({
originalPrice = totalAmount || 0;
} else if (res && typeof res === 'object') {
// 从统一的 CalculateAmountOutput 中获取数据
totalAmount = res.totalAmount != null ? Number(res.totalAmount) :
(res.amount != null ? Number(res.amount) :
(res.payAmount != null ? Number(res.payAmount) : null));
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 (totalAmount == null) {
// fallback:仍然展示前端计算的费用,避免空白
const fallbackPrice = calcTotalPrice(this.data.projectGroups);
this.setData({
this.setData({
totalPrice: fallbackPrice,
originalPrice: fallbackPrice,
discountPrice: 0
});
} else {
this.setData({
this.setData({
totalPrice: totalAmount,
originalPrice: originalPrice || totalAmount,
discountPrice: discountPrice || 0
@ -569,7 +574,7 @@ Page({
.catch(() => {
if (seq !== this._calcSeq) return;
const fallbackPrice = calcTotalPrice(this.data.projectGroups);
this.setData({
this.setData({
totalPrice: fallbackPrice,
originalPrice: fallbackPrice,
discountPrice: 0
@ -617,9 +622,9 @@ Page({
wx.showToast({ title: res.invalidReason || '优惠券无效', icon: 'none' });
}
} catch (error) {
wx.showToast({
title: error && error.message ? error.message : '验证失败,请稍后重试',
icon: 'none'
wx.showToast({
title: error && error.message ? error.message : '验证失败,请稍后重试',
icon: 'none'
});
} finally {
wx.hideLoading();
@ -699,21 +704,21 @@ Page({
// 2. 提交报名
this.setData({ submitting: true });
const registrationId = await this._submitApplyForPayment(payload);
// 3. 选择支付方式并获取支付参数
await this._selectPaymentMethod(registrationId, openid, 2);
// 4. 获取支付信息
const paymentInfo = await this._getPaymentInfo(registrationId, openid);
// 5. 调起微信支付
await this._invokeWeChatPay(paymentInfo.wechatPayParams, registrationId);
} catch (error) {
console.error('支付流程异常:', error);
wx.showToast({
title: error && error.message ? error.message : '支付失败,请稍后重试',
icon: 'none'
wx.showToast({
title: error && error.message ? error.message : '支付失败,请稍后重试',
icon: 'none'
});
} finally {
this.setData({ submitting: false });
@ -737,7 +742,7 @@ Page({
reject(new Error('未获取到 code'));
return;
}
request({
path: API.wechatOpenidByJsCode,
method: 'POST',
@ -799,8 +804,8 @@ Page({
path: '/api/eventclient/payment-info',
method: 'POST',
needAuth: true,
data: {
registrationId,
data: {
registrationId,
openId: openid,
couponId: this.data.usedCouponId
},
@ -823,7 +828,7 @@ Page({
data: {},
contentType: 'application/json'
});
wx.showToast({ title: '支付成功', icon: 'success' });
setTimeout(() => {
wx.switchTab({ url: '/pages/mine/mine' });
@ -847,9 +852,9 @@ Page({
if (err.errMsg && err.errMsg.includes('cancel')) {
wx.showToast({ title: '支付已取消', icon: 'none' });
} else {
wx.showToast({
title: err.errMsg || '支付失败,请稍后重试',
icon: 'none'
wx.showToast({
title: err.errMsg || '支付失败,请稍后重试',
icon: 'none'
});
}
reject(err);

View File

@ -61,6 +61,19 @@ function parseEventStatus(rawDetail) {
return Number.isNaN(n) ? null : n;
}
/**
* registrationType(产品定义)
* 0: 只允许团队报名
* 1: 只允许团队报名(需从用户后台报名,本端不可报名)
* 2: 个人/团队报名
*/
function parseRegistrationType(rawDetail) {
const v = pick(rawDetail, ['registrationType', 'RegistrationType']);
if (v == null || v === '') return null;
const n = Number(v);
return Number.isNaN(n) ? null : n;
}
function applyUiFromEventStatus(es) {
if (es == null) {
return { canApply: true, applyButtonText: '报名' };
@ -99,6 +112,14 @@ function registrationModeText(mode) {
return null;
}
function registrationTypeText(regType) {
const m = regType === undefined || regType === null ? null : Number(regType);
if (m === 0) return '只允许个人报名';
if (m === 1) return '只允许团队报名(后台报名)';
if (m === 2) return '个人/团队报名';
return null;
}
function formatRange(start, end) {
const a = formatDateTime(start);
const b = formatDateTime(end);
@ -203,15 +224,27 @@ function normalizeEventDetail(raw) {
const d = raw && typeof raw === 'object' ? raw : {};
const projects = Array.isArray(d.projects) ? d.projects : Array.isArray(d.Projects) ? d.Projects : [];
const registrationType = pick(d, ['registrationType', 'RegistrationType']);
const registrationMethodText =
pick(d, [
'registrationMethodText',
'RegistrationMethodText',
'registrationMethodDescription',
'RegistrationMethodDescription'
]) || registrationModeText(pick(d, ['registrationMode', 'RegistrationMode']));
]) ||
registrationTypeText(registrationType) ||
registrationModeText(pick(d, ['registrationMode', 'RegistrationMode']));
const feeModeLabel =d.chargeMode == 0 ? '免费' : d.chargeMode == 1 ? '定价模式(赛事)' : d.chargeMode == 2 ? '单价(赛事)' : '单价(项目)';
const chargeModeRaw = pick(d, ['chargeMode', 'ChargeMode']);
const chargeMode = chargeModeRaw == null || chargeModeRaw === '' ? null : Number(chargeModeRaw);
const feeModeLabel =
chargeMode === 0
? '免费'
: chargeMode === 1
? '定价模式(赛事)'
: chargeMode === 2
? '单价(赛事)'
: '单价(项目)';
let priceDisplay = d && d.feeConfig ? d.feeConfig.price : null;
if (priceDisplay == null && projects.length) {
const fees = projects.map((p) => p.fee).filter((f) => f != null && f !== '');
@ -221,6 +254,13 @@ function normalizeEventDetail(raw) {
}
}
// chargeMode=3 单价(项目):价格展示固定为提示文案
let priceDisplayText = null;
if (chargeMode === 3) {
priceDisplayText = '价格见下方项目详情';
priceDisplay = null;
}
const materialRangeText =
pick(d, ['materialUploadDeadlineText', 'MaterialUploadDeadlineText']) ||
formatRange(
@ -313,6 +353,7 @@ function normalizeEventDetail(raw) {
registrationMethodText,
feeModeLabel: feeModeLabel || '定价模式(赛事)',
priceDisplay,
priceDisplayText,
materialRangeText,
refundRulesText,
participationPointsLabel,
@ -340,6 +381,7 @@ Page({
registrationMethodText: '',
feeModeLabel: '',
priceDisplay: null,
priceDisplayText: '',
materialRangeText: '',
refundRulesText: '',
participationPointsLabel: '',
@ -357,7 +399,13 @@ Page({
unitGroups: [],
/** 0未开始 1报名中 2报名结束 3比赛中 4已结束;null 表示接口未返回 */
eventStatus: null,
canApply: true,
registrationType: null,
/** 是否灰态展示(不等同于 disabled) */
applyGrey: false,
/** 是否原生禁用(禁用后无法点击) */
applyDisabled: false,
/** 是否被“需后台报名”规则拦截(按钮灰但可点,点后弹窗引导) */
applyBlockedByAdmin: false,
applyButtonText: '报名'
},
@ -444,6 +492,7 @@ Page({
var registrationMethodText = n.registrationMethodText || fb.registrationMethodText;
var feeModeLabel = n.feeModeLabel || fb.feeModeLabel;
var priceDisplay = n.priceDisplay != null ? n.priceDisplay : fb.priceDisplay;
var priceDisplayText = n.priceDisplayText || '';
var materialRangeText = n.materialRangeText || fb.materialRangeText;
@ -496,6 +545,34 @@ Page({
const eventStatus = parseEventStatus(detail);
const { canApply, applyButtonText } = applyUiFromEventStatus(eventStatus);
const registrationType = parseRegistrationType(detail);
// 报名按钮逻辑:eventStatus 先拦截;再按 registrationType 决定是否可在小程序端报名
let applyDisabled = !canApply; // 原生禁用:不可点
let applyGrey = !canApply; // 灰态:视觉置灰
let applyBlockedByAdmin = false;
let finalApplyButtonText = applyButtonText;
// 只有 registrationType 为 0 或 2 才允许报名;为 1 时灰态但可点,点击引导去后台
if (canApply) {
if (registrationType === 1) {
applyDisabled = false;
applyGrey = true;
applyBlockedByAdmin = true;
finalApplyButtonText = '报名';
} else if (registrationType === 0 || registrationType === 2 || registrationType == null) {
applyDisabled = false;
applyGrey = false;
applyBlockedByAdmin = false;
finalApplyButtonText = '报名';
} else {
// 未知值:保守拦截
applyDisabled = true;
applyGrey = true;
applyBlockedByAdmin = false;
finalApplyButtonText = '暂不可报名';
}
}
this.setData({
eventDetail: detail,
@ -512,6 +589,7 @@ Page({
registrationMethodText,
feeModeLabel,
priceDisplay,
priceDisplayText,
materialRangeText,
uploadMaterialRemark,
refundRulesText,
@ -524,15 +602,20 @@ Page({
projectTableRows,
unitGroups,
eventStatus,
canApply,
applyButtonText
registrationType,
applyGrey,
applyDisabled,
applyBlockedByAdmin,
applyButtonText: finalApplyButtonText
});
})
.catch((err) => {
// wx.showToast({ title: err.Error, icon: 'none' });
// console.log('Detail fetchDetail error:', err);
this.setData({
canApply: false,
applyGrey: true,
applyDisabled: true,
applyBlockedByAdmin: false,
applyButtonText: '暂不可报名'
});
});
@ -541,7 +624,16 @@ Page({
onApplyTap() {
const id = this.data.id;
if (!id) return;
if (!this.data.canApply) {
if (this.data.applyBlockedByAdmin) {
wx.showModal({
title: '提示',
content: '该赛事需从用户后台进行报名',
showCancel: false,
confirmText: '我知道了'
});
return;
}
if (this.data.applyDisabled) {
wx.showToast({ title: toastForEventStatus(this.data.eventStatus), icon: 'none' });
return;
}

View File

@ -19,11 +19,14 @@
<text class="info-label">收费模式:</text>
<view class="info-value info-value--fee">
<text class="info-value-txt">{{feeModeLabel}}</text>
<view wx:if="{{priceDisplay != null}}" class="fee-inline">
<view wx:if="{{priceDisplayText}}" class="fee-inline">
<text class="fee-digits">{{priceDisplayText}}</text>
</view>
<view wx:elif="{{priceDisplay != null}}" class="fee-inline">
<text class="fee-currency">¥</text>
<text class="fee-digits">{{priceDisplay}}</text>
</view>
<view class="info-i" wx:if="{{priceDisplay != null}}">i</view>
<view class="info-i" wx:if="{{priceDisplay != null || priceDisplayText}}">i</view>
</view>
</view>
<view class="info-row">
@ -151,9 +154,9 @@
<view class="bottom-bar">
<button
class="apply-btn {{canApply ? '' : 'apply-btn--disabled'}}"
class="apply-btn {{applyGrey ? 'apply-btn--disabled' : ''}}"
type="primary"
disabled="{{!canApply}}"
disabled="{{applyDisabled}}"
bindtap="onApplyTap"
>{{applyButtonText}}</button>
</view>

View File

@ -215,7 +215,7 @@ Page({
name: (ocr && ocr.name) || '',
passportNumber: (ocr && ocr.passportNumber) || '',
nationality: (ocr && ocr.nationality) || '',
birthDate: (ocr && (ocr.birthDate || ocr.birthDateYmd)) || '',
birthDate: (ocr && (ocr.birthDateYmd || ocr.birthDate)) || '',
issueDate: (ocr && (ocr.issueDate || ocr.issueDateYmd)) || '',
expiryDate: (ocr && (ocr.expiryDate || ocr.validToDate)) || '',
genderIndex,
@ -292,7 +292,7 @@ Page({
const {
activeTab, idCardImageUrl, passportImageUrl,
name, idCardNumber, birthDate,
genderOptions, genderIndex, mobile, verificationCode,idCardBackImageUrl
genderOptions, genderIndex, mobile, verificationCode,idCardBackImageUrl,passportNumber
} = this.data;
if (!name) {
@ -347,7 +347,8 @@ Page({
smsCode: verificationCode,
gender: genderValue,
birthDate,
passportPhotoUrl: passportImageUrl
passportPhotoUrl: passportImageUrl,
idCardNumber:passportNumber
};
}

View File

@ -59,7 +59,7 @@
<text class="label">护照号码</text>
<input class="input readonly" value="{{passportNumber}}" placeholder="上传护照后自动识别" disabled="{{true}}" />
</view>
<view class="form-row">
<!-- <view class="form-row">
<text class="label">国籍</text>
<input class="input readonly" value="{{nationality}}" placeholder="上传护照后自动识别" disabled="{{true}}" />
</view>
@ -70,7 +70,7 @@
<view class="form-row">
<text class="label">有效期至</text>
<input class="input readonly" value="{{expiryDate}}" placeholder="上传护照后自动识别" disabled="{{true}}" />
</view>
</view> -->
</block>
<!-- 通用字段 -->
<view class="form-row">