8de41796fb
- maskPhone/maskContactPhone 支持座机(含区号/分机)脱敏展示,domain 补充单测 - 套餐「使用时间/其他说明」由单行 input 改为多行 textarea(admin/h5-partner/h5-shop) - 后台「套餐审核」标签文字恒为白色(不受 Badge 角标影响) - 新增 deploy/sync-prod-db-to-local.sh:线上库经 SSH 隧道同步至本地(含 @ 密码/base64 解析、--lock-tables=0 兼容受限账号) Co-Authored-By: WorkBuddy <workbuddy@tencent.com>
72 lines
2.8 KiB
TypeScript
72 lines
2.8 KiB
TypeScript
/** 11 位大陆手机号(登录凭证) */
|
||
export const MOBILE_PHONE_RE = /^1[3-9]\d{9}$/;
|
||
|
||
/**
|
||
* 国内座机:区号 0 开头(3~4 位),本地号 7~8 位,允许 `-`,可选分机。
|
||
* 例:0379-8888888、010-12345678、03798888888
|
||
*/
|
||
export const LANDLINE_PHONE_RE = /^0\d{2,3}-?\d{7,8}(-\d{1,6})?$/;
|
||
|
||
export const STORE_CONTACT_PHONE_HINT = '请输入正确的联系电话(手机号或座机,如 0379-8888888)';
|
||
|
||
export function normalizeContactPhone(raw: string): string {
|
||
return String(raw ?? '')
|
||
.trim()
|
||
.replace(/\s+/g, '');
|
||
}
|
||
|
||
export function isMobilePhone(raw: string): boolean {
|
||
return MOBILE_PHONE_RE.test(normalizeContactPhone(raw));
|
||
}
|
||
|
||
export function isLandlinePhone(raw: string): boolean {
|
||
return LANDLINE_PHONE_RE.test(normalizeContactPhone(raw));
|
||
}
|
||
|
||
/** 门店对外联系电话:手机号或座机 */
|
||
export function isStoreContactPhone(raw: string): boolean {
|
||
const s = normalizeContactPhone(raw);
|
||
return isMobilePhone(s) || isLandlinePhone(s);
|
||
}
|
||
|
||
/** 拨号用:去掉空格和横线 */
|
||
export function toDialablePhone(raw: string): string {
|
||
return String(raw ?? '').replace(/[\s-]/g, '');
|
||
}
|
||
|
||
/**
|
||
* 对外联系电话脱敏展示。
|
||
* - 手机:138****8000
|
||
* - 座机:保留区号,本地号中间打码,如 0379-****888 / 010-****5678
|
||
* - 带分机时保留分机后缀
|
||
*/
|
||
export function maskContactPhone(phone?: string | null): string {
|
||
const raw = String(phone ?? '').trim();
|
||
if (!raw) return '—';
|
||
const normalized = normalizeContactPhone(raw);
|
||
if (isMobilePhone(normalized)) {
|
||
const digits = normalized.replace(/\D/g, '');
|
||
return `${digits.slice(0, 3)}****${digits.slice(-4)}`;
|
||
}
|
||
if (isLandlinePhone(normalized)) {
|
||
const extMatch = normalized.match(/-(\d{1,6})$/);
|
||
const hasExt = !!extMatch && normalized.indexOf('-') !== normalized.lastIndexOf('-');
|
||
const ext = hasExt ? extMatch![1] : '';
|
||
const main = hasExt ? normalized.slice(0, -(ext.length + 1)) : normalized;
|
||
const digits = main.replace(/\D/g, '');
|
||
const areaLen = digits.startsWith('01') || digits.startsWith('02') ? 3 : 4;
|
||
const area = digits.slice(0, areaLen);
|
||
const local = digits.slice(areaLen);
|
||
const keepTail = Math.min(4, Math.max(2, local.length - 4));
|
||
const maskedLocal =
|
||
local.length <= 4 ? '*'.repeat(local.length) : `${'*'.repeat(local.length - keepTail)}${local.slice(-keepTail)}`;
|
||
const joiner = main.includes('-') ? '-' : '';
|
||
return ext ? `${area}${joiner}${maskedLocal}-${ext}` : `${area}${joiner}${maskedLocal}`;
|
||
}
|
||
const digits = normalized.replace(/\D/g, '');
|
||
if (digits.length >= 11) return `${digits.slice(0, 3)}****${digits.slice(-4)}`;
|
||
if (digits.length >= 7) return `${digits.slice(0, 3)}****${digits.slice(-2)}`;
|
||
if (digits.length > 0) return `${digits.slice(0, 1)}****`;
|
||
return '****';
|
||
}
|