/** 个人信息页前端校验(与后端手机号规则对齐:^1[3-9]\\d{9}$) */ const PHONE_REG = /^1[3-9]\d{9}$/ const MIN_NAME_LEN = 2 const MAX_NAME_LEN = 8 const NAME_REG = new RegExp( `^[\\u4e00-\\u9fa5a-zA-Z·\\s]{${MIN_NAME_LEN},${MAX_NAME_LEN}}$` ) const MEASURE_REG = /^\d+(\.\d)?$/ const MIN_HEIGHT = 50 const MAX_HEIGHT = 250 const MIN_WEIGHT = 20 const MAX_WEIGHT = 300 const MIN_BIRTH_YEAR = 1900 const MIN_AGE = 14 const MAX_FITNESS_GOALS = 5 export function isMaskedPhone(phone) { return String(phone || '').includes('****') } export function validateName(name) { const value = String(name ?? '').trim() if (!value) { return { ok: false, message: '请输入姓名' } } if (!NAME_REG.test(value)) { return { ok: false, message: `姓名为${MIN_NAME_LEN}-${MAX_NAME_LEN}个汉字或字母` } } return { ok: true, value } } /** 保存时使用:允许保留已脱敏的旧手机号 */ export function validatePhone(phone, options = {}) { const { allowMasked = true } = options const raw = String(phone ?? '').trim() if (!raw) { return { ok: false, message: '请绑定手机号' } } if (allowMasked && isMaskedPhone(raw)) { const digits = raw.replace(/\D/g, '') if (digits.length >= 7) { return { ok: true, value: raw } } return { ok: false, message: '手机号格式不正确' } } const digits = raw.replace(/\D/g, '') if (!PHONE_REG.test(digits)) { return { ok: false, message: '请输入11位有效手机号' } } return { ok: true, value: digits } } /** 换绑时必须输入完整新号 */ export function validatePhoneForRebind(phone) { return validatePhone(phone, { allowMasked: false }) } function parseMeasure(value) { const str = String(value ?? '').trim() if (!str || !MEASURE_REG.test(str)) { return null } const num = Number(str) return Number.isFinite(num) ? num : null } function formatMeasure(num) { return Number.isInteger(num) ? String(num) : String(Number(num.toFixed(1))) } export function validateHeight(height) { const num = parseMeasure(height) if (num == null) { return { ok: false, message: '请输入有效身高(单位 cm)' } } if (num < MIN_HEIGHT || num > MAX_HEIGHT) { return { ok: false, message: `身高请在 ${MIN_HEIGHT}-${MAX_HEIGHT} cm 之间` } } return { ok: true, value: formatMeasure(num) } } export function validateWeight(weight) { const num = parseMeasure(weight) if (num == null) { return { ok: false, message: '请输入有效体重(单位 kg)' } } if (num < MIN_WEIGHT || num > MAX_WEIGHT) { return { ok: false, message: `体重请在 ${MIN_WEIGHT}-${MAX_WEIGHT} kg 之间` } } return { ok: true, value: formatMeasure(num) } } export function parseBirthdayChinese(birthday) { const match = String(birthday ?? '').match(/(\d{4})年(\d{2})月(\d{2})日/) if (!match) return null return { year: Number(match[1]), month: Number(match[2]), day: Number(match[3]) } } export function validateBirthday(birthday) { const parts = parseBirthdayChinese(birthday) if (!parts) { return { ok: false, message: '请选择生日' } } const { year, month, day } = parts if (year < MIN_BIRTH_YEAR) { return { ok: false, message: '生日年份不合理' } } const date = new Date(year, month - 1, day) if ( date.getFullYear() !== year || date.getMonth() !== month - 1 || date.getDate() !== day ) { return { ok: false, message: '生日日期无效' } } const today = new Date() today.setHours(0, 0, 0, 0) if (date > today) { return { ok: false, message: '生日不能晚于今天' } } const minBirth = new Date( today.getFullYear() - MIN_AGE, today.getMonth(), today.getDate() ) if (date > minBirth) { return { ok: false, message: `需年满 ${MIN_AGE} 周岁` } } return { ok: true, value: `${year}年${String(month).padStart(2, '0')}月${String(day).padStart(2, '0')}日` } } export function validateGender(gender) { if (gender === 'male' || gender === 'female') { return { ok: true, value: gender } } return { ok: false, message: '请选择性别' } } export function validateFitnessGoals(goals, options = []) { const list = Array.isArray(goals) ? goals : [] const allowed = new Set(options) const invalid = list.filter((g) => !allowed.has(g)) if (invalid.length) { return { ok: false, message: '健身目标选项无效' } } if (list.length > MAX_FITNESS_GOALS) { return { ok: false, message: `最多选择 ${MAX_FITNESS_GOALS} 个健身目标` } } return { ok: true, value: [...list] } } export function validateUserProfile(profile, goalOptions = []) { const nameResult = validateName(profile.name) if (!nameResult.ok) return nameResult const phoneResult = validatePhone(profile.phone) if (!phoneResult.ok) return phoneResult const genderResult = validateGender(profile.gender) if (!genderResult.ok) return genderResult const birthdayResult = validateBirthday(profile.birthday) if (!birthdayResult.ok) return birthdayResult const heightResult = validateHeight(profile.height) if (!heightResult.ok) return heightResult const weightResult = validateWeight(profile.weight) if (!weightResult.ok) return weightResult const goalsResult = validateFitnessGoals(profile.fitnessGoals, goalOptions) if (!goalsResult.ok) return goalsResult return { ok: true, value: { ...profile, name: nameResult.value, phone: phoneResult.value, gender: genderResult.value, birthday: birthdayResult.value, height: heightResult.value, weight: weightResult.value, fitnessGoals: goalsResult.value } } } export function showValidationError(message) { uni.showToast({ title: message, icon: 'none' }) }