话不多说,直入正题:
1、工具类:
import * as moment from 'moment';import {isUndefined} from 'util';import {AbstractControl, ValidatorFn} from '@angular/forms';/*** Created by yanchao lcj on /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 11const 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 31const 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:forinfor (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();
全网唯一正确身份证和社统一社会信用代码自定义表单校验器(身份证校验和社统一社会信用代码校验)