/** * 金额格式化工具模块 * 提供千分位分隔、输入清洗、展示格式化、数值解析等功能 */ /** 字符串分段拼接千分位,禁止使用前瞻正则 */ export function addThousandSeparators(intStr: string): string { if (!intStr) return '' const isNegative = intStr.startsWith('-') const absStr = isNegative ? intStr.slice(1) : intStr const parts: string[] = [] const len = absStr.length for (let i = len; i > 0; i -= 3) { const start = Math.max(0, i - 3) parts.unshift(absStr.slice(start, i)) } const result = parts.join(',') return isNegative ? `-${result}` : result } /** 输入清洗:去逗号、仅保留数字与一个点、截断小数位 */ export function sanitizeMoneyInput(value: string, precision = 2): string { if (!value) return '' // 去逗号 let cleaned = value.replace(/,/g, '') // 去非数字非点字符 cleaned = cleaned.replace(/[^0-9.]/g, '') // 只保留第一个小数点 const dotIndex = cleaned.indexOf('.') if (dotIndex !== -1) { const intPart = cleaned.slice(0, dotIndex + 1) const decPart = cleaned.slice(dotIndex + 1).replace(/\./g, '') cleaned = intPart + decPart } // 截断小数到 precision 位 if (dotIndex !== -1) { const dotIdx = cleaned.indexOf('.') const intPart = cleaned.slice(0, dotIdx + 1) let decPart = cleaned.slice(dotIdx + 1) if (decPart.length > precision) { decPart = decPart.slice(0, precision) } cleaned = intPart + decPart } return cleaned } /** 展示:整数部分千分位,小数部分按实际位数展示,不补尾零 */ export function formatMoneyDisplay( value: string | number | null | undefined, precision = 2 ): string { if (value === null || value === undefined || value === '') return '' const num = typeof value === 'string' ? parseFloat(value) : value if (isNaN(num)) return '' const isNegative = num < 0 const absNum = Math.abs(num) const factor = Math.pow(10, precision) const rounded = Math.round(absNum * factor) / factor const raw = String(rounded) const dotIdx = raw.indexOf('.') if (dotIdx === -1) { const formatted = addThousandSeparators(raw) return isNegative ? `-${formatted}` : formatted } const intStr = raw.slice(0, dotIdx) const decStr = raw.slice(dotIdx) const formatted = addThousandSeparators(intStr) const result = `${formatted}${decStr}` return isNegative ? `-${result}` : result } /** 将已清洗的金额字符串加上千分位,保留用户输入的小数形态 */ export function formatMoneyInputDisplay(sanitized: string): string { if (!sanitized || sanitized === '.' || sanitized === '-' || sanitized === '-.') return '' const isNegative = sanitized.startsWith('-') const body = isNegative ? sanitized.slice(1) : sanitized const dotIdx = body.indexOf('.') if (dotIdx === -1) { const formatted = addThousandSeparators(body) return isNegative ? `-${formatted}` : formatted } const intStr = body.slice(0, dotIdx) const decStr = body.slice(dotIdx) const formatted = addThousandSeparators(intStr) const result = `${formatted}${decStr}` return isNegative ? `-${result}` : result } /** 提交:number | null;空串 / 仅 '.' → null */ export function parseMoneyToNumber(value: string, precision = 2): number | null { const sanitized = sanitizeMoneyInput(value, precision) if (!sanitized || sanitized === '.') return null const num = Number(sanitized) if (isNaN(num)) return null const factor = Math.pow(10, precision) return Math.round(num * factor) / factor }