update:报名

This commit is contained in:
ltx 2026-04-07 17:54:54 +08:00
parent c4faf65187
commit c21de31c69
3 changed files with 133 additions and 9 deletions

View File

@ -101,6 +101,19 @@ function parseYmdDate(s) {
return Number.isNaN(dt.getTime()) ? null : dt;
}
function parseYmdDateAny(text) {
if (!text) return null;
const s = String(text);
const m = s.match(/(\d{4})-(\d{2})-(\d{2})/);
if (!m) return null;
const y = Number(m[1]);
const mo = Number(m[2]);
const d = Number(m[3]);
if ([y, mo, d].some((x) => Number.isNaN(x))) return null;
const dt = new Date(y, mo - 1, d);
return Number.isNaN(dt.getTime()) ? null : dt;
}
function calcAgeYears(birthDateStr, atDateStr) {
const b = parseYmdDate(birthDateStr);
if (!b) return null;
@ -111,7 +124,22 @@ function calcAgeYears(birthDateStr, atDateStr) {
return age >= 0 ? age : null;
}
function parseAgeRangeText(text) {
function parseBirthDateRangeText(text) {
// e.g. "2021-03-31 至 2026-04-29" / "2021-03-31~2026-04-29"
if (!text) return null;
const s = String(text);
const ms = s.match(/(\d{4}-\d{2}-\d{2})/g);
if (!ms || ms.length < 2) return null;
const a = parseYmdDate(ms[0]);
const b = parseYmdDate(ms[1]);
if (!a || !b) return null;
const start = a <= b ? a : b;
const end = a <= b ? b : a;
return { start, end, startText: ms[0], endText: ms[1] };
}
function parseAgeYearsRangeText(text) {
// 兼容旧格式:如 "6-12岁" / "6~12"
if (!text) return null;
const s = String(text);
const nums = s.match(/\d+/g);
@ -162,6 +190,23 @@ function countSelectedByType(projectGroups, rawProjects) {
return { individual, team, unknown };
}
function buildCalculateAmountItems(projectGroups) {
const map = new Map(); // projectId -> personCount
projectGroups.forEach((p) => {
const pid = Number(p.projectId);
if (Number.isNaN(pid)) return;
let cnt = 0;
p.groups.forEach((g) => {
if (g.selected) cnt += 1;
});
if (cnt > 0) map.set(pid, (map.get(pid) || 0) + cnt);
});
return Array.from(map.entries()).map(([projectId, personCount]) => ({
projectId,
personCount
}));
}
Page({
data: {
eventId: null,
@ -176,6 +221,9 @@ Page({
myAuth: null
},
_calcTimer: null,
_calcSeq: 0,
onLoad(options) {
const eventId = Number(options.eventId);
this.setData({ eventId });
@ -380,17 +428,34 @@ Page({
wx.showToast({ title: `该组别仅限${genderRequirementText(reqGender)}报名`, icon: 'none' });
return;
}
const range = parseAgeRangeText(clickedGroup.ageRequirement);
if (range) {
const age = calcAgeYears(birthDate, eventStart);
if (age == null) {
// 年龄限制:优先支持“出生日期范围”,兼容旧“岁数范围”
const birthRange = parseBirthDateRangeText(clickedGroup.ageRequirement);
if (birthRange) {
const bd = parseYmdDateAny(birthDate);
if (!bd) {
wx.showToast({ title: '无法解析出生日期,请先完善认证信息', icon: 'none' });
return;
}
if (age < range.min || age > range.max) {
wx.showToast({ title: `年龄需在${range.min}-${range.max}岁`, icon: 'none' });
if (bd < birthRange.start || bd > birthRange.end) {
wx.showToast({
title: `出生日期需在${birthRange.startText}至${birthRange.endText}`,
icon: 'none'
});
return;
}
} else {
const range = parseAgeYearsRangeText(clickedGroup.ageRequirement);
if (range) {
const age = calcAgeYears(birthDate, eventStart);
if (age == null) {
wx.showToast({ title: '无法解析出生日期,请先完善认证信息', icon: 'none' });
return;
}
if (age < range.min || age > range.max) {
wx.showToast({ title: `年龄需在${range.min}-${range.max}岁`, icon: 'none' });
return;
}
}
}
}
@ -419,8 +484,65 @@ Page({
return;
}
const totalPrice = calcTotalPrice(nextProjectGroups);
this.setData({ projectGroups: nextProjectGroups, totalPrice });
this.setData({ projectGroups: nextProjectGroups }, () => {
this._scheduleRecalcAmount();
});
},
_scheduleRecalcAmount() {
if (this._calcTimer) {
clearTimeout(this._calcTimer);
this._calcTimer = null;
}
this._calcTimer = setTimeout(() => {
this._calcTimer = null;
this._recalcAmountNow();
}, 250);
},
_recalcAmountNow() {
const items = buildCalculateAmountItems(this.data.projectGroups);
if (!items.length) {
this.setData({ totalPrice: 0 });
return;
}
const seq = (this._calcSeq += 1);
request({
path: API.calculateAmount,
method: 'POST',
needAuth: true,
data: {
eventId: Number(this.data.eventId),
registrationMode: 2,
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') {
const n = Number(res);
amount = Number.isNaN(n) ? null : n;
} 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;
}
if (amount == null) {
// fallback:仍然展示前端计算的费用,避免空白
this.setData({ totalPrice: calcTotalPrice(this.data.projectGroups) });
} else {
this.setData({ totalPrice: amount });
}
})
.catch(() => {
if (seq !== this._calcSeq) return;
this.setData({ totalPrice: calcTotalPrice(this.data.projectGroups) });
});
},
onCouponInput(e) {

View File

@ -9,6 +9,7 @@ const API = {
eventMyList: '/api/eventclient/my-events',
eventDetail: (id) => `/api/eventclient/${id}`,
apply: '/api/eventclient/apply',
calculateAmount: '/api/eventclient/calculate-amount',
wechatLogin: '/api/oauth/login',
loginByCode: '/api/oauth/login',

View File

@ -37,6 +37,7 @@ function getHeaderValue(headers, headerName) {
function tryUpdateAccessTokenFromResponseHeaders(res) {
// wx.request 的返回头在 res.header(部分环境也可能是 res.headers)
console.log( 'res', res);
const headers = (res && res.header) || (res && res.headers) || null;
const newAccessToken = getHeaderValue(headers, 'x-access-token');
if (!newAccessToken) return;