/** 表单校验工具。 */ /** * 密码校验:按 **UTF-8 字节数** 而非字符数。 * * 后端 bcrypt 对超过 72 字节的密码直接拒绝;中文一个字符占 3 字节, * 24 个汉字即到顶 —— 按字符数校验(如 maxlength=20)根本拦不住, * 会出现"前端放行、后端 400"的错位体验。与后端 `PASSWORD_*_BYTES` 对齐。 */ export const PASSWORD_MIN_BYTES = 8 export const PASSWORD_MAX_BYTES = 72 const BYTE_HINT = '(中文一个字符约 3 字节)' /** * 金额形态正则(与后端 `schemas/business._check_amount` 同口径单点维护)。 * * 「DECIMAL 列 ↔ 前端放行位数」规则此前在业务/发货/回款/人员四页各写一份 * (businesses 与 shipments 逐字重复,receipts/personnel 又各自内联两位小数 * 正则),后端精度调整时漏改任何一页,就会出现「A 页面拦得住、B 页面放行 * 后端 400」或「带精度历史行永远无法编辑」(fba8cfc 的教训)。 */ /** 非负数字、最多两位小数(对齐 DECIMAL(14,2):金额/数量/成本/运费/工资) */ export const AMOUNT_PATTERN = /^(\d+)(\.\d{1,2})?$/ /** 非负数字、最多四位小数(对齐 DECIMAL(14,4):单价/指导价) */ export const PRICE_PATTERN = /^(\d+)(\.\d{1,4})?$/ /** 全零形态(数量「0 或 0.00」须拦:数量 >0 是明细行的硬约束) */ export const ZERO_PATTERN = /^0+(\.0+)?$/ /** 计算 UTF-8 字节数 */ export function passwordByteLength(value: string): number { return new TextEncoder().encode(value).length } /** Element Plus 表单校验器(用于 el-form 的 rules.validator) */ export function passwordRule( _rule: unknown, value: string, callback: (error?: Error) => void, ): void { if (!value) { callback(new Error('请输入密码')) return } const length = passwordByteLength(value) if (length < PASSWORD_MIN_BYTES) { callback(new Error(`密码不足 ${PASSWORD_MIN_BYTES} 字节${BYTE_HINT}`)) } else if (length > PASSWORD_MAX_BYTES) { callback(new Error(`密码超过 ${PASSWORD_MAX_BYTES} 字节上限${BYTE_HINT}`)) } else { callback() } }