safe-currency
Version:
currency tool
182 lines (181 loc) • 6.96 kB
JavaScript
import JSBD, { Decimal } from "JSBD";
const defaultSetting = {
symbol: "¥",
separator: ",",
decimal: ".",
precision: 2,
pattern: "!#",
negativePattern: "-!#",
showTail0: true,
strict: false,
fromCents: false,
};
export class currency {
constructor(value, opts) {
this.settings = Object.assign({}, defaultSetting, opts);
this.JSBDVal = this.parseInput(value);
if (this.settings.fromCents)
this.JSBDVal = JSBD.divide(this.JSBDVal, JSBD.BigDecimal(100));
this.settings.fromCents = false;
this.value = this.JSBDVal.toFixed(this.settings.precision)
.replace(/0+$/, "")
.replace(/\.$/, "")
.replace(/^$/, "0");
}
static toChineseMoney(num) {
let ch = ["零", "壹", "贰", "叁", "肆", "伍", "陆", "柒", "捌", "玖"], ch_u = ["个", "拾", "佰", "仟", "万", "亿"];
let isNegative = num.charAt(0) === "-";
num = isNegative ? num.substring(1) : num;
let [int, decimal] = num.split(".");
let int_str = decimal === "00" ? "整" : "", decimal_str = "";
if (decimal === "00")
decimal_str = "";
else if (decimal.charAt(0) === "0")
decimal_str = "零" + ch[+decimal.charAt(1)] + "分";
else if (decimal.charAt(1) === "0")
decimal_str = ch[+decimal.charAt(0)] + "角";
else
decimal_str =
ch[+decimal.charAt(0)] + "角" + ch[+decimal.charAt(1)] + "分";
if (int != "0" || decimal_str === "") {
const int2ChineseNumber = (intStr) => {
intStr = intStr.replace(/^0+/, "").replace(/^$/, "0");
let int = "", strLen = intStr.length;
if (strLen == 1)
return ch[+intStr];
if (strLen <= 4) {
for (let i = 0, n = strLen - 1; n >= 0; n--, i++) {
let _num = +intStr.charAt(i);
int += ch[_num];
int += _num && n ? ch_u[n] : "";
}
}
else {
let d = (intStr.length / 4) >> 0, y = intStr.length % 4;
while (y == 0 || !ch_u[3 + d]) {
y += 4;
d--;
}
let left = intStr.substring(0, y), right = intStr.substring(y);
int =
int2ChineseNumber(left) +
ch_u[3 + d] +
(right.charAt(0) == "0" ? "零" : "") +
int2ChineseNumber(right);
}
return int.replace(/零+$/, "").replace(/零{2}/g, "零");
};
int_str = int2ChineseNumber(int) + "元" + int_str;
}
return (isNegative ? "负" : "") + int_str + decimal_str;
}
parseInput(val) {
if (!this.testValid(val))
throw "number format error";
if (typeof val === "undefined" || val === null)
return JSBD.BigDecimal(0);
if (val instanceof currency)
return JSBD.BigDecimal(val.JSBDVal);
if (val instanceof Decimal)
return JSBD.BigDecimal(val);
if (typeof val === "string") {
let regex = new RegExp("[^-\\d" + this.settings.decimal + "]", "g"), decimalString = new RegExp("\\" + this.settings.decimal, "g");
val = val
.replace(/\((.*)\)/, "-$1")
.replace(regex, "")
.replace(decimalString, ".")
.replace(/^0+/, "")
.replace(/^$/, "0");
return JSBD.BigDecimal(val);
}
return JSBD.BigDecimal(val);
}
testValid(val) {
if (this.settings.strict === false)
return true;
if (val === null || typeof val === "undefined")
return false;
if (typeof val === "string")
return /^[-]?[\.\d]+$/.test(val);
return true;
}
add(val) {
return new currency(JSBD.add(this.JSBDVal, this.parseInput(val)), this.settings);
}
subtract(val) {
return new currency(JSBD.subtract(this.JSBDVal, this.parseInput(val)), this.settings);
}
multiply(val) {
return new currency(JSBD.multiply(this.JSBDVal, this.parseInput(val)), this.settings);
}
divide(val) {
return new currency(JSBD.divide(this.JSBDVal, this.parseInput(val)), this.settings);
}
compareTo(b) {
if (JSBD.greaterThan(this.JSBDVal, b.JSBDVal))
return 1;
if (JSBD.equal(this.JSBDVal, b.JSBDVal))
return 0;
return -1;
}
distribute(count) {
count = Math.floor(count);
const arr = [];
const divideRes = JSBD.divide(this.JSBDVal, JSBD.BigDecimal(count), {
maximumFractionDigits: this.settings.precision,
roundingMode: "down",
});
const reminder = JSBD.subtract(this.JSBDVal, JSBD.multiply(divideRes, JSBD.BigDecimal(count)));
let minUnit = this.settings.precision === 0
? JSBD.BigDecimal(1)
: JSBD.pow(JSBD.BigDecimal(0.1), this.settings.precision);
if (JSBD.greaterThan(JSBD.BigDecimal(0), reminder))
minUnit = JSBD.multiply(minUnit, JSBD.BigDecimal(-1));
let num = +JSBD.divide(reminder, minUnit).toFixed();
for (let i = 0; i < count; i++, num--) {
if (num > 0)
arr[i] = new currency(JSBD.add(divideRes, minUnit), this.settings);
else
arr[i] = new currency(divideRes, this.settings);
}
return arr;
}
format(opts) {
let { pattern, negativePattern, symbol, separator, decimal, formatter } = Object.assign({}, this.settings, opts);
if (formatter)
return formatter(this.value);
let split = this.JSBDVal.toFixed(this.settings.precision)
.replace(/^-/, "")
.split("."), yuan = split[0], cents = split[1];
if (!this.settings.showTail0) {
cents = cents.replace(/0+$/, "");
if (cents.length === 0)
decimal = "";
}
return (JSBD.greaterThanOrEqual(this.JSBDVal, JSBD.BigDecimal(0))
? pattern
: negativePattern)
.replace("!", symbol)
.replace("#", yuan.replace(/(\d)(?=(\d{3})+\b)/g, "$1" + separator) +
(cents ? decimal + cents : ""));
}
toString() {
return this.value;
}
toNumber() {
return parseFloat(this.value);
}
toJSON() {
return this.value;
}
yuan() {
return this.value;
}
cents() {
return JSBD.multiply(this.JSBDVal, JSBD.BigDecimal(100)).toFixed();
}
chineseNumber() {
return currency.toChineseMoney(this.JSBDVal.toFixed(2));
}
}
export default currency;