UNPKG

safe-currency

Version:

currency tool

182 lines (181 loc) 6.96 kB
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;