Files
sunProduct/src/utils/money.ts
2026-06-16 15:09:09 +08:00

178 lines
4.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Big from 'big.js';
/**
* 数字金额转中文大写(支持整数、小数)
* @param num - 待转换的金额(支持数字或字符串格式)
* @returns 中文大写金额字符串
*/
export function convertToChineseCapital(num: number | string): string {
// 中文数字映射
const digits: readonly string[] = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖'];
// 基本单位(个、拾、佰、仟)
const units: readonly string[] = ['', '拾', '佰', '仟'];
// 大单位(万、亿、万亿)
const bigUnits: readonly string[] = ['', '万', '亿', '万亿'];
// 1. 标准化输入:保留4位小数,分割整数与小数部分
const [intPartRaw, decPartRaw] = Number(num).toFixed(4).split('.');
// 去除整数前导零,若全零则保留一个 '0'
const intPart: string = intPartRaw.replace(/^0+/, '') || '0';
let result: string = '';
// 2. 处理整数部分
if (intPart === '0') {
result = '零元';
} else {
// 从右向左,每4位分为一组(处理万、亿等大单位)
const groups: string[] = [];
let tempInt: string = intPart;
while (tempInt.length > 0) {
groups.unshift(tempInt.slice(-4));
tempInt = tempInt.slice(0, -4);
}
// 遍历每一组进行转换
groups.forEach((group: string, gIndex: number) => {
let groupStr: string = '';
let needZero: boolean = false; // 标记是否需要补零
for (let i = 0; i < group.length; i++) {
const digit: number = parseInt(group[i], 10);
const unitPos: number = group.length - 1 - i; // 当前数字对应的单位位置
if (digit === 0) {
needZero = true;
} else {
// 补零(如果前面有0)
if (needZero) {
groupStr += '零';
needZero = false;
}
groupStr += digits[digit] + units[unitPos];
}
}
// 拼接大单位(万、亿)
if (groupStr) {
result += groupStr + bigUnits[groups.length - 1 - gIndex];
}
});
result += '元';
}
// 3. 处理小数部分(仅保留角、分,忽略毫、厘)
const jiao: number = parseInt(decPartRaw[0], 10); // 角(第1位小数)
const fen: number = parseInt(decPartRaw[1], 10); // 分(第2位小数)
if (jiao === 0 && fen === 0) {
result += '整';
} else {
// 处理“角”
if (jiao > 0) {
result += digits[jiao] + '角';
} else if (jiao === 0 && fen > 0) {
result += '零';
}
// 处理“分”
if (fen > 0) {
result += digits[fen] + '分';
}
}
return result;
}
/**
* 金额加法
* @param a 数值
* @param b 数值
* @returns 保留2位小数字符串
*/
export function moneyAdd(a: number | string = 0, b: number | string = 0): string {
return new Big(a).plus(b).toFixed(2);
}
// 金额减法
export function sub(a, b) {
let r1, r2;
try {
r1 = a.toString().split('.')[1].length;
} catch (e) {
r1 = 0;
}
try {
r2 = b.toString().split('.')[1].length;
} catch (e) {
r2 = 0;
}
const m = Math.pow(10, Math.max(r1, r2));
return (a * m - b * m) / m;
}
// 金额乘法
export function mul(a, b) {
let m = 0;
const s1 = a.toString(),
s2 = b.toString();
try {
m += s1.split('.')[1].length;
} catch (e) {}
try {
m += s2.split('.')[1].length;
} catch (e) {}
return (Number(s1.replace('.', '')) * Number(s2.replace('.', ''))) / Math.pow(10, m);
}
// 金额除法
export function div(a, b) {
let t1 = 0,
t2 = 0;
try {
t1 = a.toString().split('.')[1].length;
} catch (e) {}
try {
t2 = b.toString().split('.')[1].length;
} catch (e) {}
const x = Number(a.toString().replace('.', ''));
const y = Number(b.toString().replace('.', ''));
return (x / y) * Math.pow(10, t2 - t1);
}
// 保留2位小数(金额专用)
export function toFixed2(val) {
return Number(val).toFixed(2);
}
/**
* 智能金额格式化(带单位:元/万元/亿元)
* 兼容:数字、字符串、null、undefined
* @param {number|string} amount 金额
* @returns {string} 格式化后带单位的金额
*/
export function formatMoneyWithUnit(amount) {
// 空值处理
if (amount == null || amount === '') return '0.00 元';
const num = Number(amount);
if (isNaN(num)) return '0.00 元';
// 绝对值判断单位
const absNum = Math.abs(num);
if (absNum >= 100000000) {
// 亿元
return (num / 100000000).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',') + ' 亿元';
} else if (absNum >= 10000) {
// 万元
return (num / 10000).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',') + ' 万元';
} else {
// 元
return num.toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g, ',') + ' 元';
}
}
// 带货币符号(¥)版本
export function formatMoneyWithSymbol(amount) {
return '¥' + formatMoneyWithUnit(amount);
}