import { useEffect, useRef, useState } from "react"; /** * 校验中国大陆18位身份证号 * 包含地址码、年份(18/19/20开头)、月份、日期、顺序码和校验位 * @param {string} idCard * @returns {boolean} */ export function isValidChineseIdCard(idCard) { if (!idCard || typeof idCard !== "string") { return false; } const value = idCard.trim(); if (!isValidChineseIdCardFormat(value)) { return false; } const weights = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]; const checkCodeMap = ["1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"]; const sum = value .substring(0, 17) .split("") .reduce((total, num, index) => total + Number(num) * weights[index], 0); return checkCodeMap[sum % 11] === value[17].toUpperCase(); } export function isValidChineseIdCardFormat(idCard) { if (!idCard || typeof idCard !== "string") { return false; } const value = idCard.trim(); const pattern = /^[1-9][0-9]{5}(18|19|20)[0-9]{2}(0[1-9]|1[0-2])(0[1-9]|[12][0-9]|3[01])[0-9]{3}[0-9Xx]$/; if (!pattern.test(value)) { return false; } const year = Number.parseInt(value.substring(6, 10), 10); const month = Number.parseInt(value.substring(10, 12), 10); const day = Number.parseInt(value.substring(12, 14), 10); const date = new Date(year, month - 1, day); return ( date.getFullYear() === year && date.getMonth() === month - 1 && date.getDate() === day ); } export function getChineseIdCardInputErrorMessage(idCard) { if (!idCard || typeof idCard !== "string") { return ""; } const value = idCard.trim(); if (!/^[0-9Xx]*$/.test(value)) { return "身份证号只能输入数字或X"; } if (value.length > 18) { return "身份证号长度不能超过18位"; } if (value.length < 18) { return ""; } if (!isValidChineseIdCardFormat(value)) { return "请输入正确的18位身份证号"; } return ""; } export function getChineseIdCardFullErrorMessage(idCard) { const inputErrorMessage = getChineseIdCardInputErrorMessage(idCard); if (inputErrorMessage) { return inputErrorMessage; } if (!idCard || typeof idCard !== "string") { return ""; } const value = idCard.trim(); if (value.length < 18) { return ""; } if (!isValidChineseIdCard(value)) { return "身份证校验位不正确"; } return ""; } /** * 根据身份证号计算年龄 * @param {string} idCard - 18位身份证号码 * @returns {number | null} 年龄(整数),无效身份证返回 null */ export function getAgeByIdCard(idCard) { // 非空 & 类型校验 if (!idCard || typeof idCard !== "string") { return null; } // 只处理18位身份证 const id = idCard.trim(); if (id.length !== 18) { return null; } // 提取出生年月日:第7~14位(索引6~13) const birthStr = id.substring(6, 14); // 如 "19900101" const year = Number.parseInt(birthStr.substring(0, 4), 10); const month = Number.parseInt(birthStr.substring(4, 6), 10); const day = Number.parseInt(birthStr.substring(6, 8), 10); // 简单校验日期合法性(可选增强) if ( year < 1900 || year > new Date().getFullYear() || month < 1 || month > 12 || day < 1 || day > 31 ) { return null; } // 构造出生日期 const birthDate = new Date(year, month - 1, day); // 月份从0开始 const today = new Date(); // 计算年龄 let age = today.getFullYear() - birthDate.getFullYear(); const monthDiff = today.getMonth() - birthDate.getMonth(); const dayDiff = today.getDate() - birthDate.getDate(); // 如果还没过生日,减1岁 if (monthDiff < 0 || (monthDiff === 0 && dayDiff < 0)) { age -= 1; } return age >= 0 ? age : null; // 年龄不能为负 } /** * 从18位身份证号中提取出生年月日 * @param {string} idCard - 身份证号码 * @returns {{ year: number; month: number; day: number } | null} 出生年月日对象,无效则返回 null */ export function getBirthDateFromIdCard(idCard) { if (!idCard || typeof idCard !== "string") { return null; } const id = idCard.trim(); if (id.length !== 18) { return null; // 仅支持18位身份证 } // 提取第7~14位:YYYYMMDD const birthStr = id.substring(6, 14); // 如 "19900307" const year = Number.parseInt(birthStr.substring(0, 4), 10); const month = Number.parseInt(birthStr.substring(4, 6), 10); const day = Number.parseInt(birthStr.substring(6, 8), 10); // 简单合法性校验 const currentYear = new Date().getFullYear(); if ( year < 1900 || year > currentYear + 1 // 允许未来1年(录入误差) || month < 1 || month > 12 || day < 1 || day > 31 ) { return null; } return `${year}-${String(month).padStart(2, "0")}-${String(day).padStart(2, "0")}`; } export function useDebounce(value, delay = 500) { const [debouncedValue, setDebouncedValue] = useState(value); const timerRef = useRef(null); useEffect(() => { // 清除上一次的定时器 if (timerRef.current) { clearTimeout(timerRef.current); } // 设置新的定时器 timerRef.current = setTimeout(() => { setDebouncedValue(value); }, delay); // 组件卸载或 delay/value 变化时清理 return () => { if (timerRef.current) { clearTimeout(timerRef.current); } }; }, [value, delay]); return debouncedValue; } // 手机号脱敏 export function maskPhone(phone) { if (!phone) return ""; const str = String(phone).replace(/\s+/g, ""); // 去除空格 if (!/^1[3-9]\d{9}$/.test(str)) { return phone; // 非标准手机号,原样返回 } return `${str.substring(0, 3)}****${str.substring(7)}`; } // 身份证号脱敏 export function maskIdCard(idCard) { if (!idCard) return ""; // 转为字符串并去除空格 const str = String(idCard).replace(/\s+/g, ""); // 判断是否为 18 位身份证(支持末尾 X/x) if (!/^\d{17}[\dX]$/i.test(str)) { return idCard; // 非标准身份证,原样返回 } return `${str.substring(0, 6)}********${str.substring(14)}`; } // 属地 export const getAreaNamePath = (item) => { const names = [ item.provinceName, item.cityName, item.countryName, item.streetName, item.villageName, ].filter(name => name != null && name !== ""); return names.join("/"); }; // 行业类型 export const getCorpTypeNamePath = (item) => { const names = [ item.corpTypeName, item.corpType2Name, item.corpType3Name, item.corpType4Name, ].filter(name => name != null && name !== ""); return names.join("/"); }; // 身份证解码 export const UseDecodeIdCard = (userIdCard) => { if (!userIdCard) return userIdCard; try { const decoded = atob(userIdCard); if (/^\d{17}[\dX]$/.test(decoded)) { return decoded; } } catch { console.warn("Not a valid Base64 string, keep as is:", userIdCard); } return userIdCard; // fallback };