Files
OnboardXFront/src/utils/onboardingValidation.ts
T
2026-09-14 16:45:50 +08:00

118 lines
3.5 KiB
TypeScript

/**
* 入职登记表固定字段校验工具。
*
* 前端校验用于即时提示,后端仍会执行同样的核心校验,避免绕过页面直接提交非法数据。
*/
const mobilePhonePattern = /^1[3-9]\d{9}$/;
const landlinePhonePattern = /^0\d{2,3}-?\d{7,8}$/;
const emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const bankCardPattern = /^\d{12,19}$/;
const idCardWeights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
const idCardCheckCodes = ["1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"];
export function normalizeIdCardNumber(value: unknown) {
return String(value ?? "").trim().toUpperCase();
}
export function normalizeBankCardNumber(value: unknown) {
return String(value ?? "").replace(/[\s-]/g, "");
}
export function isMobilePhone(value: unknown) {
return mobilePhonePattern.test(String(value ?? "").trim());
}
export function isContactPhone(value: unknown) {
const phone = String(value ?? "").trim();
return mobilePhonePattern.test(phone) || landlinePhonePattern.test(phone);
}
export function isEmail(value: unknown) {
return emailPattern.test(String(value ?? "").trim());
}
export function isBankCardNumber(value: unknown) {
return bankCardPattern.test(normalizeBankCardNumber(value));
}
/**
* 校验中国大陆 18 位居民身份证,包括出生日期和校验位。
* 返回空字符串表示校验通过,否则返回适合直接展示给用户的错误信息。
*/
export function getIdCardValidationMessage(value: unknown) {
const idCardNumber = normalizeIdCardNumber(value);
if (!/^\d{17}[0-9X]$/.test(idCardNumber)) {
return "请输入正确的18位身份证号码";
}
const birthday = getIdCardBirthday(idCardNumber);
if (!birthday || !isValidDateText(birthday) || birthday > getTodayText()) {
return "身份证号码中的出生日期不正确";
}
const total = idCardWeights.reduce(
(sum, weight, index) => sum + Number(idCardNumber[index]) * weight,
0
);
if (idCardCheckCodes[total % 11] !== idCardNumber[17]) {
return "身份证号码校验位不正确";
}
return "";
}
export function getIdCardBirthday(value: unknown) {
const idCardNumber = normalizeIdCardNumber(value);
if (!/^\d{17}[0-9X]$/.test(idCardNumber)) {
return "";
}
const text = idCardNumber.slice(6, 14);
return `${text.slice(0, 4)}-${text.slice(4, 6)}-${text.slice(6, 8)}`;
}
export function getIdCardGender(value: unknown) {
const idCardNumber = normalizeIdCardNumber(value);
if (!/^\d{17}[0-9X]$/.test(idCardNumber)) {
return "";
}
const genderCode = Number(idCardNumber.charAt(16));
return genderCode % 2 === 1 ? "男" : "女";
}
export function isNumberInRange(value: unknown, min: number, max: number) {
const text = String(value ?? "").trim();
if (!/^\d+(\.\d+)?$/.test(text)) {
return false;
}
const number = Number(text);
return Number.isFinite(number) && number >= min && number <= max;
}
function isValidDateText(value: string) {
const match = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
if (!match) {
return false;
}
const year = Number(match[1]);
const month = Number(match[2]);
const day = Number(match[3]);
const date = new Date(year, month - 1, day);
return date.getFullYear() === year
&& date.getMonth() === month - 1
&& date.getDate() === day;
}
function getTodayText() {
const now = new Date();
const year = now.getFullYear();
const month = String(now.getMonth() + 1).padStart(2, "0");
const day = String(now.getDate()).padStart(2, "0");
return `${year}-${month}-${day}`;
}