227 lines
6.8 KiB
JavaScript
227 lines
6.8 KiB
JavaScript
|
|
// 使用示例:
|
|||
|
|
// import IDCardParser from '@/utils/IDCardParser';
|
|||
|
|
|
|||
|
|
// const handleCheck = (idStr) => {
|
|||
|
|
// const parser = new IDCardParser(idStr);
|
|||
|
|
// const result = parser.getInfo();
|
|||
|
|
// if(result.valid) {
|
|||
|
|
// // 填充表单
|
|||
|
|
// console.log(result.age, result.gender);
|
|||
|
|
// } else {
|
|||
|
|
// alert(result.message);
|
|||
|
|
// }
|
|||
|
|
// }
|
|||
|
|
|
|||
|
|
class IDCardParser {
|
|||
|
|
constructor(idNumber) {
|
|||
|
|
this.idNumber = idNumber ? idNumber.trim().toUpperCase() : "";
|
|||
|
|
this.provinceMap = {
|
|||
|
|
11: "北京市",
|
|||
|
|
12: "天津市",
|
|||
|
|
13: "河北省",
|
|||
|
|
14: "山西省",
|
|||
|
|
15: "内蒙古自治区",
|
|||
|
|
21: "辽宁省",
|
|||
|
|
22: "吉林省",
|
|||
|
|
23: "黑龙江省",
|
|||
|
|
31: "上海市",
|
|||
|
|
32: "江苏省",
|
|||
|
|
33: "浙江省",
|
|||
|
|
34: "安徽省",
|
|||
|
|
35: "福建省",
|
|||
|
|
36: "江西省",
|
|||
|
|
37: "山东省",
|
|||
|
|
41: "河南省",
|
|||
|
|
42: "湖北省",
|
|||
|
|
43: "湖南省",
|
|||
|
|
44: "广东省",
|
|||
|
|
45: "广西壮族自治区",
|
|||
|
|
46: "海南省",
|
|||
|
|
50: "重庆市",
|
|||
|
|
51: "四川省",
|
|||
|
|
52: "贵州省",
|
|||
|
|
53: "云南省",
|
|||
|
|
54: "西藏自治区",
|
|||
|
|
61: "陕西省",
|
|||
|
|
62: "甘肃省",
|
|||
|
|
63: "青海省",
|
|||
|
|
64: "宁夏回族自治区",
|
|||
|
|
65: "新疆维吾尔自治区",
|
|||
|
|
71: "台湾省",
|
|||
|
|
81: "香港特别行政区",
|
|||
|
|
82: "澳门特别行政区"
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 初始化验证结果
|
|||
|
|
const validation = this._validate();
|
|||
|
|
this.isValid = validation.isValid;
|
|||
|
|
this.errorMsg = validation.msg;
|
|||
|
|
|
|||
|
|
// 如果合法,预先解析数据
|
|||
|
|
if (this.isValid) {
|
|||
|
|
this._parseData();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 核心校验逻辑
|
|||
|
|
*/
|
|||
|
|
_validate() {
|
|||
|
|
// 1. 基础正则 (18位,末位数字或X)
|
|||
|
|
if (!/^\d{17}[\d|X]$/.test(this.idNumber)) {
|
|||
|
|
return {
|
|||
|
|
isValid: false,
|
|||
|
|
msg: "格式错误:必须是18位,末位为数字或X"
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 2. 省份校验
|
|||
|
|
const provinceCode = parseInt(this.idNumber.substring(0, 2));
|
|||
|
|
if (!this.provinceMap[provinceCode]) {
|
|||
|
|
return {
|
|||
|
|
isValid: false,
|
|||
|
|
msg: "省份代码错误"
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 3. 日期合法性严格校验
|
|||
|
|
const year = parseInt(this.idNumber.substring(6, 10));
|
|||
|
|
const month = parseInt(this.idNumber.substring(10, 12));
|
|||
|
|
const day = parseInt(this.idNumber.substring(12, 14));
|
|||
|
|
|
|||
|
|
const date = new Date(year, month - 1, day);
|
|||
|
|
// JS Date会自动纠错(例如2月30会变成3月),所以必须反向比对
|
|||
|
|
if (
|
|||
|
|
date.getFullYear() !== year ||
|
|||
|
|
date.getMonth() + 1 !== month ||
|
|||
|
|
date.getDate() !== day
|
|||
|
|
) {
|
|||
|
|
return {
|
|||
|
|
isValid: false,
|
|||
|
|
msg: "出生日期无效"
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 检查年份范围(可选,例如限制在1900年以后)
|
|||
|
|
if (year < 1900 || date > new Date()) {
|
|||
|
|
return {
|
|||
|
|
isValid: false,
|
|||
|
|
msg: "出生日期不在合理范围内"
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 4. 校验码计算 (ISO 7064:1983.MOD 11-2)
|
|||
|
|
if (!this._checkSum()) {
|
|||
|
|
return {
|
|||
|
|
isValid: false,
|
|||
|
|
msg: "校验码错误(身份证可能无效)"
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
isValid: true,
|
|||
|
|
msg: "验证通过"
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 校验码算法
|
|||
|
|
*/
|
|||
|
|
_checkSum() {
|
|||
|
|
const factors = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
|
|||
|
|
const checkCodes = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
|
|||
|
|
|
|||
|
|
let sum = 0;
|
|||
|
|
for (let i = 0; i < 17; i++) {
|
|||
|
|
sum += parseInt(this.idNumber[i]) * factors[i];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const mod = sum % 11;
|
|||
|
|
return checkCodes[mod] === this.idNumber[17];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 解析内部数据
|
|||
|
|
*/
|
|||
|
|
_parseData() {
|
|||
|
|
const year = parseInt(this.idNumber.substring(6, 10));
|
|||
|
|
const month = parseInt(this.idNumber.substring(10, 12));
|
|||
|
|
const day = parseInt(this.idNumber.substring(12, 14));
|
|||
|
|
|
|||
|
|
this.birthDate = new Date(year, month - 1, day);
|
|||
|
|
this.province = this.provinceMap[parseInt(this.idNumber.substring(0, 2))];
|
|||
|
|
|
|||
|
|
// 性别:第17位,奇数男,偶数女
|
|||
|
|
const genderCode = parseInt(this.idNumber.substring(16, 17));
|
|||
|
|
this.gender = genderCode % 2 !== 0 ? "男" : "女";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取所有信息
|
|||
|
|
*/
|
|||
|
|
getInfo() {
|
|||
|
|
if (!this.isValid) {
|
|||
|
|
return {
|
|||
|
|
valid: false,
|
|||
|
|
message: this.errorMsg
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
valid: true,
|
|||
|
|
idNumber: this.idNumber,
|
|||
|
|
province: this.province,
|
|||
|
|
birthday: this._formatDate(this.birthDate),
|
|||
|
|
gender: this.gender,
|
|||
|
|
age: this._calculateAge(),
|
|||
|
|
zodiac: this._getZodiac(), // 生肖
|
|||
|
|
constellation: this._getConstellation() // 星座
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_formatDate(date) {
|
|||
|
|
const y = date.getFullYear();
|
|||
|
|
const m = String(date.getMonth() + 1).padStart(2, '0');
|
|||
|
|
const d = String(date.getDate()).padStart(2, '0');
|
|||
|
|
return `${y}-${m}-${d}`;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_calculateAge() {
|
|||
|
|
const today = new Date();
|
|||
|
|
let age = today.getFullYear() - this.birthDate.getFullYear();
|
|||
|
|
const m = today.getMonth() - this.birthDate.getMonth();
|
|||
|
|
|
|||
|
|
// 如果没过生日,年龄减1
|
|||
|
|
if (m < 0 || (m === 0 && today.getDate() < this.birthDate.getDate())) {
|
|||
|
|
age--;
|
|||
|
|
}
|
|||
|
|
return age;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_getZodiac() {
|
|||
|
|
// 猴鸡狗猪鼠牛虎兔龙蛇马羊 (对应余数 0-11)
|
|||
|
|
const zodiacs = "猴鸡狗猪鼠牛虎兔龙蛇马羊";
|
|||
|
|
return zodiacs.charAt(this.birthDate.getFullYear() % 12);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
_getConstellation() {
|
|||
|
|
const month = this.birthDate.getMonth() + 1;
|
|||
|
|
const day = this.birthDate.getDate();
|
|||
|
|
|
|||
|
|
// 星座分割日
|
|||
|
|
const dates = [20, 19, 21, 20, 21, 22, 23, 23, 23, 24, 23, 22];
|
|||
|
|
const constellations = [
|
|||
|
|
"摩羯座", "水瓶座", "双鱼座", "白羊座", "金牛座", "双子座",
|
|||
|
|
"巨蟹座", "狮子座", "处女座", "天秤座", "天蝎座", "射手座", "摩羯座"
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
if (day < dates[month - 1]) {
|
|||
|
|
return constellations[month - 1];
|
|||
|
|
} else {
|
|||
|
|
return constellations[month];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
export default IDCardParser;
|