242 lines
6.7 KiB
TypeScript
242 lines
6.7 KiB
TypeScript
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);
|
||
}
|