118 lines
3.5 KiB
TypeScript
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}`;
|
|
}
|