Files
estate/src/utils/money.ts
2026-08-01 10:11:38 +08:00

242 lines
6.7 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;
}
/**
* 标准化为Big实例空/undefined/null转0
* @param num 数字 | 数字字符串
*/
export function toBig(num: string | number | undefined | null): Big {
if (num === null || num === undefined || num === '') return new Big(0);
return new Big(num);
}
/**
* 金额加法
* @param a 数值
* @param b 数值
* @returns 保留2位小数字符串
*/
export function moneyAdd(a: number | string = 0, b: number | string = 0): string {
return toBig(a).plus(b).toFixed(2);
}
// 数组高精度求和
export function sum(arr: Array<string | number>): number {
const total = arr.reduce((acc, cur) => acc.plus(toBig(cur)), new Big(0));
return Number(total);
}
// 取较大值
export function maxMoney(a: string | number, b: string | number): number {
const bigA = toBig(a);
const bigB = toBig(b);
return Number(bigA.gt(bigB) ? bigA : bigB);
}
// 取较小值
export function minMoney(a: string | number, b: string | number): number {
const bigA = toBig(a);
const bigB = toBig(b);
return Number(bigA.lt(bigB) ? bigA : bigB);
}
/**
* 减法 a - b
*/
export function sub(a: string | number, b: string | number): number {
return Number(toBig(a).minus(toBig(b)));
}
/**
* 加法 a + b
*/
export function add(a: string | number, b: string | number): number {
return Number(toBig(a).plus(toBig(b)));
}
/**
* 乘法 a * b
*/
export function mul(a: string | number, b: string | number, decimal = 2): number {
return Number(toBig(a).times(toBig(b)).round(decimal));
}
/**
* 除法 a / b
* @throws 除数为0抛异常业务try catch捕获
*/
export function div(a: string | number, b: string | number): number {
const bigB = toBig(b);
if (bigB.eq(0)) throw new Error('除数不能为0');
return Number(toBig(a).div(bigB));
}
/**
* a > b
*/
export function gtMoney(a: string | number, b: string | number): boolean {
return toBig(a).gt(toBig(b));
}
/**
* a >= b
*/
export function gteMoney(a: string | number, b: string | number): boolean {
return toBig(a).gte(toBig(b));
}
/**
* a < b
*/
export function ltMoney(a: string | number, b: string | number): boolean {
return toBig(a).lt(toBig(b));
}
/**
* a <= b
*/
export function lteMoney(a: string | number, b: string | number): boolean {
return toBig(a).lte(toBig(b));
}
/**
* a === b 金额相等
*/
export function eqMoney(a: string | number, b: string | number): boolean {
return toBig(a).eq(toBig(b));
}
/**
* a !== b 金额不相等
*/
export function neqMoney(a: string | number, b: string | number): boolean {
return !toBig(a).eq(toBig(b));
}
// 保留2位小数金额专用
export function toFixed2(val) {
return Number(val).toFixed(2);
}
// 判断 a 和 b 是否数值相等
export function eq(a: string | number, b: string | number): boolean {
return toBig(a).eq(toBig(b));
}
/**
* 金额保留指定位小数四舍五入默认2位
* @param num 计算结果
* @param digit 保留位数
*/
export function toFixedMoney(num: string | number, digit = 2): number {
return Number(toBig(num).toFixed(digit, Big.roundHalfUp));
}
/**
* 智能金额格式化(带单位:元/万元/亿元)
* 兼容数字、字符串、null、undefined
* @param {number|string} amount 金额
* @param {number} unitNumber 保留几个小数 默认 2位
* @returns {string} 格式化后带单位的金额
*/
export function formatMoneyWithUnit(amount: number | string, unitNumber: number = 2): string {
// 空值处理
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(unitNumber).replace(/\B(?=(\d{3})+(?!\d))/g, ',') + ' 亿元';
} else if (absNum >= 10000) {
// 万元
return (num / 10000).toFixed(unitNumber).replace(/\B(?=(\d{3})+(?!\d))/g, ',') + ' 万元';
} else {
// 元
return num.toFixed(unitNumber).replace(/\B(?=(\d{3})+(?!\d))/g, ',') + ' 元';
}
}
// 带货币符号(¥)版本
export function formatMoneyWithSymbol(amount) {
return '¥' + formatMoneyWithUnit(amount);
}