239 lines
5.6 KiB
JavaScript
239 lines
5.6 KiB
JavaScript
import dayjs from "./moment";
|
|
|
|
|
|
const PARAMS = {
|
|
/* 省,直辖市代码表 */
|
|
provinceAndCitys: {
|
|
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: '澳门',
|
|
91: '国外'
|
|
},
|
|
|
|
/* 每位加权因子 */
|
|
powers: [
|
|
'7',
|
|
'9',
|
|
'10',
|
|
'5',
|
|
'8',
|
|
'4',
|
|
'2',
|
|
'1',
|
|
'6',
|
|
'3',
|
|
'7',
|
|
'9',
|
|
'10',
|
|
'5',
|
|
'8',
|
|
'4',
|
|
'2'
|
|
],
|
|
|
|
/* 第18位校检码 */
|
|
parityBit: ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'],
|
|
|
|
/* 性别 */
|
|
genders: {1: '男', 0: '女'},
|
|
}
|
|
|
|
const Check = {
|
|
/* 校验地址码 */
|
|
checkAddressCode(addressCode) {
|
|
const check = /^[1-9]\d{5}$/.test(addressCode)
|
|
if (!check) return false
|
|
return !!PARAMS.provinceAndCitys[parseInt(addressCode.substring(0, 2))]
|
|
},
|
|
/* 校验日期码 */
|
|
checkBirthDayCode(birDayCode) {
|
|
const check = /^[1-9]\d{3}((0[1-9])|(1[0-2]))((0[1-9])|([1-2][0-9])|(3[0-1]))$/.test(
|
|
birDayCode
|
|
)
|
|
if (!check) return false
|
|
const yyyy = parseInt(birDayCode.substring(0, 4), 10)
|
|
const mm = parseInt(birDayCode.substring(4, 6), 10)
|
|
const dd = parseInt(birDayCode.substring(6), 10)
|
|
const xdata = new Date(yyyy, mm - 1, dd)
|
|
if (xdata > new Date()) {
|
|
return false // 生日不能大于当前日期
|
|
} else {
|
|
return (
|
|
xdata.getFullYear() == yyyy &&
|
|
xdata.getMonth() == mm - 1 &&
|
|
xdata.getDate() == dd
|
|
)
|
|
}
|
|
},
|
|
/* 验证校检码 */
|
|
checkParityBit(idCardNo) {
|
|
const parityBit = idCardNo.charAt(17).toUpperCase()
|
|
return this.getParityBit(idCardNo) == parityBit
|
|
},
|
|
// 校验15位的身份证号码
|
|
check15IdCardNo(idCardNo) {
|
|
// 15位身份证号码的基本校验
|
|
let check = /^[1-9]\d{7}((0[1-9])|(1[0-2]))((0[1-9])|([1-2][0-9])|(3[0-1]))\d{3}$/.test(
|
|
idCardNo
|
|
)
|
|
if (!check) return false
|
|
// 校验地址码
|
|
const addressCode = idCardNo.substring(0, 6)
|
|
check = this.checkAddressCode(addressCode)
|
|
if (!check) return false
|
|
const birDayCode = '19' + idCardNo.substring(6, 12)
|
|
// 校验日期码
|
|
return this.checkBirthDayCode(birDayCode)
|
|
},
|
|
|
|
// 校验18位的身份证号码
|
|
check18IdCardNo(idCardNo) {
|
|
// 18位身份证号码的基本格式校验
|
|
let check = /^[1-9]\d{5}[1-9]\d{3}((0[1-9])|(1[0-2]))((0[1-9])|([1-2][0-9])|(3[0-1]))\d{3}(\d|x|X)$/.test(
|
|
idCardNo
|
|
)
|
|
if (!check) return false
|
|
// 校验地址码
|
|
const addressCode = idCardNo.substring(0, 6)
|
|
check = this.checkAddressCode(addressCode)
|
|
if (!check) return false
|
|
// 校验日期码
|
|
const birDayCode = idCardNo.substring(6, 14)
|
|
check = this.checkBirthDayCode(birDayCode)
|
|
if (!check) return false
|
|
// 验证校检码
|
|
return this.checkParityBit(idCardNo)
|
|
},
|
|
/* 计算校检码 */
|
|
getParityBit(idCardNo) {
|
|
const id17 = idCardNo.substring(0, 17)
|
|
/* 加权 */
|
|
let power = 0
|
|
for (let i = 0; i < 17; i++) {
|
|
power += parseInt(id17.charAt(i), 10) * parseInt(PARAMS.powers[i])
|
|
}
|
|
/* 取模 */
|
|
const mod = power % 11
|
|
return PARAMS.parityBit[mod]
|
|
}
|
|
}
|
|
|
|
export default class Identity {
|
|
constructor(code) {
|
|
this.code = this.getId18(code)
|
|
this.init()
|
|
}
|
|
|
|
init() {
|
|
const {code} = this
|
|
if (!!code) {
|
|
this.hideCode = Identity.hideId(code)
|
|
this.getIdCardInfo(code)
|
|
}
|
|
}
|
|
|
|
static hideId(code) {
|
|
return code?.replace(/^(\d{10})\d{4}(.{4}$)/g, `$1${Array(5).join('*')}$2`)
|
|
}
|
|
|
|
getId18(code) {
|
|
if (code.length == 15) {
|
|
const id17 = code.substring(0, 6) + '19' + code.substring(6)
|
|
const parityBit = Check.getParityBit(id17)
|
|
return id17 + parityBit
|
|
} else if (code.length == 18) {
|
|
return code
|
|
} else {
|
|
return null
|
|
}
|
|
}
|
|
|
|
getIdCardInfo(idCardNo) {
|
|
this.birthday = Identity.getBirthday(idCardNo)
|
|
this.sex = Identity.getSex(idCardNo)
|
|
this.gender = PARAMS.genders[this.sex]
|
|
this.age = Identity.getAge(idCardNo)
|
|
this.areaId = Identity.getArea(idCardNo)
|
|
}
|
|
|
|
/**
|
|
* 获取性别
|
|
* @param code
|
|
* @returns {string}
|
|
*/
|
|
static getSex(code) {
|
|
return (Number(code.substring(16, 17)) % 2).toString()
|
|
}
|
|
|
|
/**
|
|
* 获取年龄
|
|
* @param code
|
|
* @returns {number}
|
|
*/
|
|
static getAge(code) {
|
|
const birthday = dayjs(code.substring(6, 14), 'YYYYMMDD')
|
|
return Math.ceil(dayjs.duration(dayjs().unix() - dayjs(birthday).unix(), 's').asYears())
|
|
}
|
|
|
|
/**
|
|
* 获取地区编码
|
|
* @param code
|
|
*/
|
|
static getArea(code) {
|
|
return code.substring(0, 6) + new Array(7).join(0)
|
|
}
|
|
|
|
/**
|
|
* 获取生日
|
|
*/
|
|
static getBirthday(code) {
|
|
return dayjs(code.substring(6, 14), 'YYYYMMDD').format("YYYY-MM-DD").replace('Invalid Date', '')
|
|
}
|
|
|
|
/* 校验15位或18位的身份证号码 */
|
|
static check(idCardNo) {
|
|
// 15位和18位身份证号码的基本校验
|
|
const check = /^\d{15}|(\d{17}(\d|x|X))$/.test(idCardNo)
|
|
if (!check) return false
|
|
// 判断长度为15位或18位
|
|
if (idCardNo.length == 15) {
|
|
return Check.check15IdCardNo(idCardNo)
|
|
} else if (idCardNo.length == 18) {
|
|
return Check.check18IdCardNo(idCardNo)
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
}
|