话不多说,直入正题:
1、工具类:
import * as moment from 'moment';
import {isUndefined} from 'util';
import {AbstractControl, ValidatorFn} from '@angular/forms';
/**
* Created by yanchao lcj on 2020/06/28.
*/
export class StringUtils {
constructor() {
}
/**
* 判断是空字符串?
* @param str
*/
static isBlank(str: string) {
return isUndefined(str) || str === null || '' === str.trim();
}
/**
* 去空格
* @param str 处理字符串
* @return 结果字符串
*/
static trim(str: string) {
return str.replace('\n', '').replace(' ', '').trim();
}
/**
* 判断不是空字符串?
* @param str
*/
static isNotBlank(str: string) {
return !this.isBlank(str);
}
/**
* 判断是空?
* @param o
*/
static isEmpty(o) {
return o === null || o === 'null' || o === undefined || o === 'undefined' || o === '';
}
/**
* 判断不是空?
* @param o
*/
static isNotEmpty(o) {
return !this.isEmpty(o);
}
/**
* 校验身份证的
* @param code
*/
public static identityCodeValid(code): ProcessResult {
// 判断是否为空 长度是否合法
if (this.isBlank(code) || code.length !== 18) {
return new ProcessResult(false, '身份证长度不够');
}
code = this.trim(code);
// 身份证号格式错误
if (!code || !/^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$|^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/.test(code)) {
return new ProcessResult(false, '身份证不合法');
}
// 省份对应的省编码
const city = {
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: '国外'
};
if (!city[code.substr(0, 2)]) {
// 地址编码错误
return new ProcessResult(false, '身份证前2为输入有误');
} else {
// 18位身份证需要验证最后一位校验位
code = code.split('');
// 身份证最后一位
const lastOneCode = code[17];
// 加权因子 ∑(ai×Wi)(mod 11)
const factor = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2];
// 校验位
const parity = ['1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'];
let sum = 0;
let ai = 0;
let wi = 0;
// 取前17位,(每一位数字 * 每一位数字在加权因子数组的对应的数字) 之和
for (let i = 0; i < 17; i++) {
ai = code[i];
wi = factor[i];
sum += ai * wi;
}
// 算出来的和 mod 11 得到 最后一位,再把传进来的身份证的最后一位和算出来的最后一位对比
return new ProcessResult(parity[sum % 11] === lastOneCode, '身份证不合法');
}
}
/**
* 身份证自定义校验器
*/
public static idCardNoValidator(): ValidatorFn {
return (control: AbstractControl): { [key: string]: any } | null => {
const processResult = this.identityCodeValid(control.value);
return processResult.isSuccess() ? null : {idCardError: {value: control.value, message: processResult.errorMessage}};
};
}
/**
* 统一社会信用代码自定义校验器
*/
public static unifiedCreditCodeValidator(): ValidatorFn {
return (control: AbstractControl): { [key: string]: any } | null => {
const processResult = this.checkUnifiedCreditCode(control.value);
return processResult.isSuccess() ? null : {unifiedCreditCodeError: {value: control.value, message: processResult.errorMessage}};
};
}
/**
* 统一社会信用代码校验
* @param uniCode
*/
public static checkUnifiedCreditCode(uniCode: string): ProcessResult {
// 假如不是18位,错误
if (!uniCode || uniCode.length !== 18) {
return new ProcessResult(false, '统一社会信用代码长度不合法');
}
uniCode = this.trim(uniCode);
// 统一社会信用代码由18位阿拉伯数字或英文大写字母表示(不包括I,O,Z,S,V以防止和阿拉伯字母混淆)-->V:???关我毛事?
const upUniCode = uniCode.toUpperCase();
if (upUniCode.indexOf('I') !== -1 || upUniCode.indexOf('O') !== -1 || upUniCode.indexOf('Z') !== -1 || upUniCode.indexOf('S') !== -1 || upUniCode.indexOf('V') !== -1) {
return new ProcessResult(false, '统一社会信用代码不能含有(I,O,Z,S,V)');
}
// (组织机构代码)校验
const orgCheckCode = [
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T',
'U', 'V', 'W', 'X', 'Y', 'Z'
];
// 组织机构代码加权因子
const orgWeight = [3, 7, 9, 10, 5, 8, 4, 2];
const orgCode = uniCode.substr(8, 9);
let sumOrg = 0;
// 去前8位数 每一位数对应在orgWeight数组的下标 * 对应的加权因子 之和
for (let i = 0; i < 8; i++) {
const tmpAttr = orgCode[i] as any;
const tmpCode = orgCheckCode.indexOf(tmpAttr);
const tmpWeight = orgWeight[i];
sumOrg += (tmpCode * tmpWeight);
}
// 再用11 - 算出的合数 mod 11
const modOrg = 11 - sumOrg % 11;
// 再用算出来的数字转换成最后一位的数字 拿到最后一个字符
const modOrgLast = (modOrg === 10) ? 'X' : ((modOrg === 11) ? '0' : ('' + modOrg));
// 对比算出来的最后一位是否等于给我的组织机构代码的最后一位
if (modOrgLast !== orgCode[8]) {
return new ProcessResult(false, '统一社会信用代码中的组织机构代码不合法');
}
// 最后一位的校验 (A=10, B=11,以此类推)
const uniCheckCode = [
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q',
'R', 'T', 'U', 'W', 'X', 'Y'
];
// 统一社会信用代码加权因子
const uniCodeWeight = [1, 3, 9, 27, 19, 26, 16, 17, 20, 29, 25, 13, 8, 24, 10, 30, 28];
let sumUniCode = 0;
// 去前17位数 每一位数对应在uniCheckCode数组的下标 * 对应的加权因子 之和
for (let i = 0; i < 17; i++) {
const tmpAttr = uniCode[i] as any;
const tmpCode = uniCheckCode.indexOf(tmpAttr);
const tmpWeight = uniCodeWeight[i];
sumUniCode += (tmpCode * tmpWeight);
}
// 再用31 - 算出的合数 mod 31
const modOrgUni = 31 - sumUniCode % 31;
// 在uniCheckCode数组找出对应的字符就是最后一位
const modOrgUniLast = uniCheckCode[modOrgUni % 31];
// 对比算出来的最后一位是否等于给我的信用代码的最后一位
return new ProcessResult(modOrgUniLast + '' === uniCode[17] + '', '统一社会信用代码不合法');
}
/**
* 根据身份证号码获取生日年龄性别
*/
public static getIdCardInformation(idCardNo): IdCardInformation {
const processResult = this.identityCodeValid(idCardNo);
if (processResult.isFailure()) {
return new IdCardInformation(undefined, undefined, undefined);
}
// 区分二代身份证
const mark = idCardNo.length > 15;
// 获取身份证上的出生日期
const birthday = moment(idCardNo.substring(6, mark ? 14 : 11), 'YYYYMMDD').toDate();
// 算出年龄
const age = new Date().getFullYear() - birthday.getFullYear();
// 得出年龄
const gender = !(Number(idCardNo.charAt(mark ? 16 : 14)) % 2 === 0);
// 返回身份证信息
return new IdCardInformation(birthday, age, gender);
}
}
/**
* 身份证信息
*/
class IdCardInformation {
/**
* 出生日期
*/
birthday: Date;
/**
* 年龄
*/
age: number;
/**
* 性别
*/
gender: boolean;
constructor(birthday: Date, age: number, gender: boolean) {
this.birthday = birthday;
this.age = age;
this.gender = gender;
}
}
/**
* 处理结果
*/
class ProcessResult {
/**
* 成功?
*/
success: boolean;
/**
* 错误信息
*/
errorMessage: string;
constructor(success: boolean, errorMessage: string) {
this.success = success;
this.errorMessage = errorMessage;
}
/**
* 是成功的?
*/
isSuccess() {
return this.success;
}
/**
* 是失败的?
*/
isFailure() {
return !this.success;
}
}
2、表单对象 :
this.formGroup = this.fb.group({
// 证件号码
idCard: [null, [Validators.required, StringUtils.idCardNoValidator()]]
});
3、页面用管道显示错误信息:
<nz-form-item>
<nz-form-label [nzSm]="4" [nzXs]="12" nzRequired>证件号码</nz-form-label>
<nz-form-control [nzValidateStatus]="formGroup.get('idCard')"
nzHasFeedback [nzErrorTip]="formGroup.get('idCard').errors | errorMessage"
[nzSm]="7" [nzXs]="12">
<input nz-input formControlName="idCard" placeholder="请输入证件号码"/>
</nz-form-control>
</nz-form-item>
4、管道的类:
import {Pipe, PipeTransform} from '@angular/core';
import {ValidationErrors} from '@angular/forms';
@Pipe({
name: 'errorMessage'
})
export class ErrorMessagePipe implements PipeTransform {
constructor() {
}
errors: ValidationErrors;
formatError(key) {
switch (key) {
case 'required':
return '必填项';
case 'idCardError':
return this.errors[key].message;
case 'unifiedCreditCodeError':
return this.errors[key].message;
default:
return '';
}
}
transform(errors: ValidationErrors): any {
this.errors = errors;
// tslint:disable-next-line:forin
for (const key in errors) {
return this.formatError(key);
}
}
}
效果:
5、校验身份证获取身份证信息:
const value = "身份证号码";
// 获取身份证里面的信息如:出生日期 年龄 性别 当然还可以获得出生地,这里我没实现
const idCardInformation = StringUtils.getIdCardInformation(value);
// 身份证校验
const success = StringUtils.identityCodeValid(value).isSuccess();
// 统一社会信用代码校验
const success1 = StringUtils.checkUnifiedCreditCode(‘****’).isSuccess();