金融格式化
为什么需要金融格式化
- 金额、大数字需要更清晰地展示给用户
- 例如 1234567.89 → 1,234,567.89
新建文件
- utils/moneyformat.ts
ts
/**
* 金融金额格式化工具
* 将数字金额转换为符合金融规范的格式:千分位分隔,保留两位小数
*/
/** 可接受的金额输入类型 */
type MoneyInput = number | string;
/** 非法输入时的默认返回值 */
const INVALID_RESULT = "0.00";
/**
* 校验输入是否为有效数字
* @param input 输入值
* @returns 合法则返回 number,否则返回 null
*/
function toValidNumber(input: MoneyInput): number | null {
const num = typeof input === "string" ? Number(input.trim()) : input;
if (typeof num !== "number" || Number.isNaN(num) || !Number.isFinite(num)) {
return null;
}
return num;
}
/**
* 方案一:正则表达式实现金额格式化
*
* @param amount - 金额,支持 number 或 string 类型(如 12345.678 / "12345.678")
* @returns 格式化后的字符串,如 "12,345.68";非法输入返回 "0.00"
*
* 使用场景:需要简洁实现的常规金额展示,依赖正则完成千分位分隔。
*/
export function formatByRegex(amount: MoneyInput): string {
const num = toValidNumber(amount);
if (num === null) return INVALID_RESULT;
// toFixed(2):自动补全两位小数、超长小数四舍五入
const fixed = num.toFixed(2);
// 正则:在整数部分从右往左每三位前插入逗号(不匹配负号和小数点后的位置)
return fixed.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
/**
* 方案二:JavaScript 原生 API 实现金额格式化(不使用正则)
*
* @param amount - 金额,支持 number 或 string 类型
* @returns 格式化后的字符串,如 "12,345.68";非法输入返回 "0.00"
*
* 使用场景:不希望依赖正则的场景,或需要清晰控制整数/小数处理逻辑的场景。
*/
export function formatByNative(amount: MoneyInput): string {
const num = toValidNumber(amount);
if (num === null) return INVALID_RESULT;
// 处理负号
const isNegative = num < 0;
const absStr = Math.abs(num).toFixed(2); // 自动补0 / 四舍五入到两位小数
// 拆分整数部分和小数部分
const dotIndex = absStr.indexOf(".");
const intPart = dotIndex === -1 ? absStr : absStr.slice(0, dotIndex);
const decimalPart = dotIndex === -1 ? "" : absStr.slice(dotIndex + 1);
// 手动遍历,从右往左每三位插入千分位分隔符
let formattedInt = "";
for (let i = intPart.length - 1, count = 0; i >= 0; i--, count++) {
if (count > 0 && count % 3 === 0) {
formattedInt = "," + formattedInt;
}
formattedInt = intPart[i] + formattedInt;
}
const sign = isNegative ? "-" : "";
return decimalPart
? `${sign}${formattedInt}.${decimalPart}`
: `${sign}${formattedInt}`;
}
/* ============================== 测试用例 ============================== */
/** 简单断言:两函数输出一致且等于期望值 */
function assertFormat(input: MoneyInput, expected: string): void {
const regexResult = formatByRegex(input);
const nativeResult = formatByNative(input);
console.assert(
regexResult === expected && nativeResult === expected,
`输入 ${input}: 期望 "${expected}",实际 regex="${regexResult}", native="${nativeResult}"`,
);
console.log(
`输入 ${String(input).padEnd(18)} => "${regexResult}" ${regexResult === expected && nativeResult === expected ? "✔" : "✘"}`,
);
}
// 普通正整数:1234567 → "1,234,567.00"
assertFormat(1234567, "1,234,567.00");
// 带小数的金额(四舍五入):12345.678 → "12,345.68"
assertFormat(12345.678, "12,345.68");
// 负数金额:-987654.32 → "-987,654.32"
assertFormat(-987654.32, "-987,654.32");
// 小于1000的小额金额(补0):123.4 → "123.40"
assertFormat(123.4, "123.40");
// 超大金额:1234567890123.45 → "1,234,567,890,123.45"
assertFormat(1234567890123.45, "1,234,567,890,123.45");
// 字符串输入
assertFormat("9876543210.5", "9,876,543,210.50");
// 字符串负数输入
assertFormat("-12345.1", "-12,345.10");
// 异常输入:NaN / Infinity / 非数字字符串
assertFormat(NaN, "0.00");
assertFormat(Infinity, "0.00");
assertFormat("abc", "0.00");