react-util-tools 1.0.26 → 1.0.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -30,7 +30,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/index.ts
31
31
  var index_exports = {};
32
32
  __export(index_exports, {
33
- Decimal: () => import_decimal.default,
33
+ Decimal: () => import_decimal2.default,
34
34
  XLSX: () => XLSX,
35
35
  abs: () => abs,
36
36
  add: () => add,
@@ -64,6 +64,9 @@ __export(index_exports, {
64
64
  formatUTC: () => formatUTC,
65
65
  formatUTCDateOnly: () => formatUTCDateOnly,
66
66
  formatUTCTimeOnly: () => formatUTCTimeOnly,
67
+ formateAmount: () => formateAmount,
68
+ formateFaitAmount: () => formateFaitAmount,
69
+ formatePrecision: () => formatePrecision,
67
70
  fromBase64: () => fromBase64,
68
71
  fromUTC: () => fromUTC,
69
72
  getAllCookies: () => getAllCookies,
@@ -120,6 +123,7 @@ __export(index_exports, {
120
123
  greaterThanOrEqual: () => greaterThanOrEqual,
121
124
  hasCookie: () => hasCookie,
122
125
  includes: () => includes,
126
+ integerTokenArr: () => integerTokenArr,
123
127
  isAfterDate: () => isAfterDate,
124
128
  isAndroid: () => isAndroid,
125
129
  isBeforeDate: () => isBeforeDate,
@@ -161,6 +165,7 @@ __export(index_exports, {
161
165
  readExcelToJSON: () => readExcelToJSON,
162
166
  readFile: () => readFile,
163
167
  removeCookie: () => removeCookie,
168
+ removeInvalidZero: () => removeInvalidZero,
164
169
  removeSpaces: () => removeSpaces,
165
170
  repeat: () => repeat,
166
171
  replaceAll: () => replaceAll,
@@ -184,6 +189,7 @@ __export(index_exports, {
184
189
  titleCase: () => titleCase,
185
190
  toBase64: () => toBase64,
186
191
  toISOString: () => toISOString,
192
+ toLocalString: () => toLocalString,
187
193
  toLowerCase: () => toLowerCase,
188
194
  toUTC: () => toUTC,
189
195
  toUpperCase: () => toUpperCase,
@@ -191,6 +197,7 @@ __export(index_exports, {
191
197
  trimEnd: () => trimEnd,
192
198
  trimStart: () => trimStart,
193
199
  truncate: () => truncate,
200
+ tryRun: () => tryRun,
194
201
  unescapeHtml: () => unescapeHtml,
195
202
  unmaskEmail: () => unmaskEmail,
196
203
  utils: () => utils2,
@@ -464,6 +471,7 @@ function getDeviceInfo() {
464
471
  }
465
472
 
466
473
  // src/format/index.ts
474
+ var import_decimal = __toESM(require("decimal.js"), 1);
467
475
  function formatMoney(amount, options = {}) {
468
476
  const {
469
477
  decimals = 2,
@@ -471,20 +479,19 @@ function formatMoney(amount, options = {}) {
471
479
  separator = ",",
472
480
  decimalPoint = "."
473
481
  } = options;
474
- const num = typeof amount === "string" ? parseFloat(amount) : amount;
475
- if (isNaN(num)) {
476
- return `${symbol}0${decimalPoint}${"0".repeat(decimals)}`;
477
- }
478
- const isNegative = num < 0;
479
- const absNum = Math.abs(num);
480
- const fixed = absNum.toFixed(decimals);
481
- const [integerPart, decimalPart] = fixed.split(".");
482
- const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, separator);
483
- let result = symbol + formattedInteger;
484
- if (decimals > 0 && decimalPart) {
485
- result += decimalPoint + decimalPart;
486
- }
487
- return isNegative ? `-${result}` : result;
482
+ return tryRun(() => {
483
+ const dec = new import_decimal.default(amount);
484
+ const isNegative = dec.isNegative();
485
+ const absDec = dec.abs();
486
+ const fixed = absDec.toFixed(decimals, import_decimal.default.ROUND_DOWN);
487
+ const [integerPart, decimalPart] = fixed.split(".");
488
+ const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, separator);
489
+ let result = symbol + formattedInteger;
490
+ if (decimals > 0 && decimalPart) {
491
+ result += decimalPoint + decimalPart;
492
+ }
493
+ return isNegative ? `-${result}` : result;
494
+ }) ?? `${symbol}0${decimalPoint}${"0".repeat(decimals)}`;
488
495
  }
489
496
  function parseMoney(formattedAmount) {
490
497
  if (!formattedAmount || typeof formattedAmount !== "string") {
@@ -495,68 +502,70 @@ function parseMoney(formattedAmount) {
495
502
  return isNaN(num) ? 0 : num;
496
503
  }
497
504
  function formatNumber(amount, decimals = 2) {
498
- const num = typeof amount === "string" ? parseFloat(amount) : amount;
499
- if (isNaN(num)) {
500
- return "0." + "0".repeat(decimals);
501
- }
502
- const fixed = num.toFixed(decimals);
503
- const [integerPart, decimalPart] = fixed.split(".");
504
- const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
505
- return decimals > 0 && decimalPart ? `${formattedInteger}.${decimalPart}` : formattedInteger;
505
+ return tryRun(() => {
506
+ const dec = new import_decimal.default(amount);
507
+ const fixed = dec.toFixed(decimals, import_decimal.default.ROUND_DOWN);
508
+ const [integerPart, decimalPart] = fixed.split(".");
509
+ const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
510
+ return decimals > 0 && decimalPart ? `${formattedInteger}.${decimalPart}` : formattedInteger;
511
+ }) ?? "0." + "0".repeat(decimals);
506
512
  }
507
513
  function formatMoneyToChinese(amount) {
508
- const num = typeof amount === "string" ? parseFloat(amount) : amount;
509
- if (isNaN(num) || num < 0) {
510
- return "\u96F6\u5143\u6574";
511
- }
512
- const digits = ["\u96F6", "\u58F9", "\u8D30", "\u53C1", "\u8086", "\u4F0D", "\u9646", "\u67D2", "\u634C", "\u7396"];
513
- const units = ["", "\u62FE", "\u4F70", "\u4EDF"];
514
- const bigUnits = ["", "\u4E07", "\u4EBF", "\u5146"];
515
- const decimalUnits = ["\u89D2", "\u5206"];
516
- const [integerPart, decimalPart] = num.toFixed(2).split(".");
517
- let result = "";
518
- if (integerPart === "0") {
519
- result = "\u96F6\u5143";
520
- } else {
521
- const integerStr = integerPart;
522
- const len = integerStr.length;
523
- let zeroCount = 0;
524
- for (let i = 0; i < len; i++) {
525
- const digit = parseInt(integerStr[i]);
526
- const unitIndex = (len - i - 1) % 4;
527
- const bigUnitIndex = Math.floor((len - i - 1) / 4);
528
- if (digit === 0) {
529
- zeroCount++;
530
- } else {
531
- if (zeroCount > 0) {
532
- result += "\u96F6";
514
+ return tryRun(() => {
515
+ const dec = new import_decimal.default(amount);
516
+ if (dec.isNegative()) {
517
+ return "\u96F6\u5143\u6574";
518
+ }
519
+ const num = dec.toNumber();
520
+ const digits = ["\u96F6", "\u58F9", "\u8D30", "\u53C1", "\u8086", "\u4F0D", "\u9646", "\u67D2", "\u634C", "\u7396"];
521
+ const units = ["", "\u62FE", "\u4F70", "\u4EDF"];
522
+ const bigUnits = ["", "\u4E07", "\u4EBF", "\u5146"];
523
+ const decimalUnits = ["\u89D2", "\u5206"];
524
+ const [integerPart, decimalPart] = num.toFixed(2).split(".");
525
+ let result = "";
526
+ if (integerPart === "0") {
527
+ result = "\u96F6\u5143";
528
+ } else {
529
+ const integerStr = integerPart;
530
+ const len = integerStr.length;
531
+ let zeroCount = 0;
532
+ for (let i = 0; i < len; i++) {
533
+ const digit = parseInt(integerStr[i]);
534
+ const unitIndex = (len - i - 1) % 4;
535
+ const bigUnitIndex = Math.floor((len - i - 1) / 4);
536
+ if (digit === 0) {
537
+ zeroCount++;
538
+ } else {
539
+ if (zeroCount > 0) {
540
+ result += "\u96F6";
541
+ }
542
+ result += digits[digit] + units[unitIndex];
543
+ zeroCount = 0;
533
544
  }
534
- result += digits[digit] + units[unitIndex];
535
- zeroCount = 0;
536
- }
537
- if (unitIndex === 0 && bigUnitIndex > 0) {
538
- if (result[result.length - 1] !== bigUnits[bigUnitIndex]) {
539
- result += bigUnits[bigUnitIndex];
545
+ if (unitIndex === 0 && bigUnitIndex > 0) {
546
+ if (result[result.length - 1] !== bigUnits[bigUnitIndex]) {
547
+ result += bigUnits[bigUnitIndex];
548
+ }
540
549
  }
541
550
  }
551
+ result += "\u5143";
542
552
  }
543
- result += "\u5143";
544
- }
545
- if (decimalPart && decimalPart !== "00") {
546
- const jiao = parseInt(decimalPart[0]);
547
- const fen = parseInt(decimalPart[1]);
548
- if (jiao > 0) {
549
- result += digits[jiao] + decimalUnits[0];
550
- } else if (fen > 0) {
551
- result += "\u96F6";
552
- }
553
- if (fen > 0) {
554
- result += digits[fen] + decimalUnits[1];
553
+ if (decimalPart && decimalPart !== "00") {
554
+ const jiao = parseInt(decimalPart[0]);
555
+ const fen = parseInt(decimalPart[1]);
556
+ if (jiao > 0) {
557
+ result += digits[jiao] + decimalUnits[0];
558
+ } else if (fen > 0) {
559
+ result += "\u96F6";
560
+ }
561
+ if (fen > 0) {
562
+ result += digits[fen] + decimalUnits[1];
563
+ }
564
+ } else {
565
+ result += "\u6574";
555
566
  }
556
- } else {
557
- result += "\u6574";
558
- }
559
- return result;
567
+ return result;
568
+ }) ?? "\u96F6\u5143\u6574";
560
569
  }
561
570
  function formatPercent(value, options = {}) {
562
571
  const { decimals = 2, multiply: multiply2 = true } = options;
@@ -599,6 +608,118 @@ function unmaskEmail(maskedEmail, originalEmail) {
599
608
  }
600
609
  return maskedEmail;
601
610
  }
611
+ function toLocalString(value) {
612
+ let result = value;
613
+ if (Number(value) >= 1e3) {
614
+ result = Number(value).toLocaleString("en-US");
615
+ } else {
616
+ result = value;
617
+ }
618
+ return result;
619
+ }
620
+ var integerTokenArr = ["SATS"];
621
+ function removeInvalidZero(num) {
622
+ let result = num;
623
+ if (num.includes(".")) {
624
+ result = result.replace(/\.?0+$/, "");
625
+ }
626
+ return result;
627
+ }
628
+ function formatePrecision(n, precision, tokenSymbol) {
629
+ if (!isNaN(Number(n))) {
630
+ let prec = precision && precision <= 8 ? precision : 8;
631
+ if (tokenSymbol && integerTokenArr.includes(tokenSymbol)) {
632
+ prec = 0;
633
+ }
634
+ return new import_decimal.default(Number(n)).toFixed(prec, import_decimal.default.ROUND_DOWN);
635
+ }
636
+ const num = new import_decimal.default(n).toNumber();
637
+ return num;
638
+ }
639
+ function formateAmount({
640
+ num = 0,
641
+ precision = 8,
642
+ tokenSymbol = "",
643
+ type = "0"
644
+ }) {
645
+ return tryRun(() => {
646
+ const dec = new import_decimal.default(num);
647
+ const { length: length2 } = dec.abs().floor().toString();
648
+ const pres = precision;
649
+ precision = precision > 8 ? 9 : precision + 1;
650
+ if (length2 >= precision) {
651
+ let value = "";
652
+ if (dec.greaterThan(1)) {
653
+ value = dec.toFixed(8, import_decimal.default.ROUND_DOWN);
654
+ } else {
655
+ value = dec.toFixed(precision - length2, import_decimal.default.ROUND_DOWN);
656
+ }
657
+ value = formatePrecision(value, pres).toString();
658
+ if (integerTokenArr.includes(tokenSymbol)) {
659
+ value = new import_decimal.default(value).toFixed(0, import_decimal.default.ROUND_CEIL);
660
+ }
661
+ let result = "";
662
+ if (typeof value === "string") {
663
+ if (value.includes(".")) {
664
+ result = value.replace(/\d(?=(\d{3})+\.)/g, "$&,");
665
+ } else {
666
+ result = toLocalString(value);
667
+ }
668
+ } else {
669
+ result = value;
670
+ }
671
+ if (Number(num) === 0) {
672
+ return result;
673
+ } else {
674
+ return dec.greaterThan(new import_decimal.default(0)) ? removeInvalidZero(result) : type === "0" ? "-" + removeInvalidZero(result) : removeInvalidZero(result);
675
+ }
676
+ } else {
677
+ let value = "";
678
+ if (dec.greaterThan(1)) {
679
+ value = dec.toFixed(8, import_decimal.default.ROUND_DOWN);
680
+ } else {
681
+ value = dec.toFixed(precision - length2, import_decimal.default.ROUND_DOWN);
682
+ }
683
+ value = formatePrecision(value, pres).toString();
684
+ if (integerTokenArr.includes(tokenSymbol)) {
685
+ value = new import_decimal.default(value).toFixed(0, import_decimal.default.ROUND_CEIL);
686
+ }
687
+ let result = "";
688
+ if (typeof value === "string") {
689
+ if (value.includes(".")) {
690
+ result = value.replace(/\d(?=(\d{3})+\.)/g, "$&,");
691
+ } else {
692
+ result = toLocalString(value);
693
+ }
694
+ } else {
695
+ result = value;
696
+ }
697
+ return removeInvalidZero(result);
698
+ }
699
+ }) ?? "0.00";
700
+ }
701
+ function formateFaitAmount(num = 0) {
702
+ return tryRun(() => {
703
+ const dec = new import_decimal.default(num);
704
+ const fnum = dec.toFixed(2, import_decimal.default.ROUND_DOWN);
705
+ if (Number(num) >= 1e3) {
706
+ let result = toLocalString(fnum);
707
+ if (!result.includes(".")) {
708
+ result = result + ".00";
709
+ }
710
+ return result;
711
+ } else {
712
+ return fnum;
713
+ }
714
+ }) ?? "0.00";
715
+ }
716
+ function tryRun(fn) {
717
+ try {
718
+ return fn();
719
+ } catch (error) {
720
+ return null;
721
+ }
722
+ }
602
723
 
603
724
  // src/date/index.ts
604
725
  var import_date_fns = require("date-fns");
@@ -862,22 +983,22 @@ function getUTCWeekNumber(date) {
862
983
  }
863
984
 
864
985
  // src/decimal/index.ts
865
- var import_decimal = __toESM(require("decimal.js"), 1);
986
+ var import_decimal2 = __toESM(require("decimal.js"), 1);
866
987
 
867
988
  // src/decimal/utils/index.ts
868
- var import_decimal2 = __toESM(require("decimal.js"), 1);
989
+ var import_decimal3 = __toESM(require("decimal.js"), 1);
869
990
  function safeDecimal(value) {
870
991
  try {
871
- if (value instanceof import_decimal2.default) {
992
+ if (value instanceof import_decimal3.default) {
872
993
  return value;
873
994
  }
874
- const decimal = new import_decimal2.default(value);
995
+ const decimal = new import_decimal3.default(value);
875
996
  if (decimal.isNaN()) {
876
- return new import_decimal2.default(0);
997
+ return new import_decimal3.default(0);
877
998
  }
878
999
  return decimal;
879
1000
  } catch {
880
- return new import_decimal2.default(0);
1001
+ return new import_decimal3.default(0);
881
1002
  }
882
1003
  }
883
1004
  function add(a, b) {
@@ -975,7 +1096,7 @@ function round(value, decimalPlaces = 2) {
975
1096
  function ceil(value, decimalPlaces = 2) {
976
1097
  try {
977
1098
  const decimal = safeDecimal(value);
978
- return decimal.toDecimalPlaces(decimalPlaces, import_decimal2.default.ROUND_CEIL).toNumber();
1099
+ return decimal.toDecimalPlaces(decimalPlaces, import_decimal3.default.ROUND_CEIL).toNumber();
979
1100
  } catch {
980
1101
  return 0;
981
1102
  }
@@ -983,7 +1104,7 @@ function ceil(value, decimalPlaces = 2) {
983
1104
  function floor(value, decimalPlaces = 2) {
984
1105
  try {
985
1106
  const decimal = safeDecimal(value);
986
- return decimal.toDecimalPlaces(decimalPlaces, import_decimal2.default.ROUND_FLOOR).toNumber();
1107
+ return decimal.toDecimalPlaces(decimalPlaces, import_decimal3.default.ROUND_FLOOR).toNumber();
987
1108
  } catch {
988
1109
  return 0;
989
1110
  }
@@ -1349,6 +1470,9 @@ function fromBase64(base64) {
1349
1470
  formatUTC,
1350
1471
  formatUTCDateOnly,
1351
1472
  formatUTCTimeOnly,
1473
+ formateAmount,
1474
+ formateFaitAmount,
1475
+ formatePrecision,
1352
1476
  fromBase64,
1353
1477
  fromUTC,
1354
1478
  getAllCookies,
@@ -1405,6 +1529,7 @@ function fromBase64(base64) {
1405
1529
  greaterThanOrEqual,
1406
1530
  hasCookie,
1407
1531
  includes,
1532
+ integerTokenArr,
1408
1533
  isAfterDate,
1409
1534
  isAndroid,
1410
1535
  isBeforeDate,
@@ -1446,6 +1571,7 @@ function fromBase64(base64) {
1446
1571
  readExcelToJSON,
1447
1572
  readFile,
1448
1573
  removeCookie,
1574
+ removeInvalidZero,
1449
1575
  removeSpaces,
1450
1576
  repeat,
1451
1577
  replaceAll,
@@ -1469,6 +1595,7 @@ function fromBase64(base64) {
1469
1595
  titleCase,
1470
1596
  toBase64,
1471
1597
  toISOString,
1598
+ toLocalString,
1472
1599
  toLowerCase,
1473
1600
  toUTC,
1474
1601
  toUpperCase,
@@ -1476,6 +1603,7 @@ function fromBase64(base64) {
1476
1603
  trimEnd,
1477
1604
  trimStart,
1478
1605
  truncate,
1606
+ tryRun,
1479
1607
  unescapeHtml,
1480
1608
  unmaskEmail,
1481
1609
  utils,
package/dist/index.d.cts CHANGED
@@ -89,6 +89,19 @@ declare function formatPercent(value: number, options?: {
89
89
  }): string;
90
90
  declare function maskEmail(email: string): string;
91
91
  declare function unmaskEmail(maskedEmail: string, originalEmail: string): string;
92
+ declare function toLocalString(value: string): string;
93
+ declare const integerTokenArr: string[];
94
+ type AmountNum = string | number | Decimal;
95
+ declare function removeInvalidZero(num: string): string;
96
+ declare function formatePrecision(n: AmountNum, precision?: number, tokenSymbol?: string): string | number;
97
+ declare function formateAmount({ num, precision, tokenSymbol, type, }: {
98
+ num: AmountNum;
99
+ precision?: number;
100
+ tokenSymbol?: string;
101
+ type?: string;
102
+ }): any;
103
+ declare function formateFaitAmount(num?: AmountNum): any;
104
+ declare function tryRun(fn: () => any): any;
92
105
 
93
106
  declare function formatDate(date: Date | number | string, formatStr?: string): string;
94
107
  declare function formatDateOnly(date: Date | number | string): string;
@@ -234,4 +247,4 @@ declare function isValidIdCard(idCard: string): boolean;
234
247
  declare function toBase64(str: string): string;
235
248
  declare function fromBase64(base64: string): string;
236
249
 
237
- export { type CookieOptions, abs, add, addDaysToDate, addDaysUTC, addMonthsToDate, addMonthsUTC, aoaToSheet, camelCase, capitalize, ceil, clearAllCookies, countOccurrences, debounceFn as debounce, divide, endsWith, equals, escapeHtml, exportExcelFile, exportJSONToExcel, extractNumbers, floor, formatDate, formatDateOnly, formatMoney, formatMoneyToChinese, formatNumber, formatPercent, formatRelativeTime, formatTimeOnly, formatUTC, formatUTCDateOnly, formatUTCTimeOnly, fromBase64, fromUTC, getAllCookies, getAllQueryParams, getBrowser, getBrowserEngine, getBrowserVersion, getCookie, getDaysDiff, getDeviceInfo, getDevicePixelRatio, getDeviceType, getEndOfDay, getEndOfMonth, getEndOfWeek, getEndOfYear, getHoursDiff, getMinutesDiff, getOS, getQueryParam, getQueryParamAll, getScreenResolution, getSheet, getSheetNames, getStartOfDay, getStartOfMonth, getStartOfWeek, getStartOfYear, getTimestamp, getTimestampInSeconds, getTimezoneOffset, getTimezoneOffsetHours, getUTCAllWeeksInYear, getUTCDaysDiff, getUTCEndOfDay, getUTCEndOfMonth, getUTCHoursDiff, getUTCMinutesDiff, getUTCNow, getUTCStartOfDay, getUTCStartOfMonth, getUTCTimestamp, getUTCTimestampInSeconds, getUTCWeekEnd, getUTCWeekNumber, getUTCWeekStart, getUTCWeeksInYear, getUTCYearEnd, getUTCYearEndTimestamp, getUTCYearStart, getUTCYearStartTimestamp, getViewportSize, greaterThan, greaterThanOrEqual, hasCookie, includes, isAfterDate, isAndroid, isBeforeDate, isDesktop, isEmpty, isIOS, isMobile, isNotEmpty, isSameDayDate, isTablet, isTouchDevice, isValidDate, isValidEmail, isValidIdCard, isValidPhone, isValidUrl, isWeChat, jsonToWorkbook, kebabCase, length, lessThan, lessThanOrEqual, maskBankCard, maskEmail, maskIdCard, maskName, maskPhone, multiply, negate, normalizeSpaces, padEnd, padStart, parseDate, parseMoney, pascalCase, randomString, read, readExcelFile, readExcelToJSON, readFile, removeCookie, removeSpaces, repeat, replaceAll, reverse, round, setCookie, sheetToAOA, sheetToCSV, sheetToHTML, snakeCase, split, startsWith, stripHtml, subDaysFromDate, subDaysUTC, subMonthsFromDate, subMonthsUTC, subtract, tableToSheet, throttleFn as throttle, titleCase, toBase64, toISOString, toLowerCase, toUTC, toUpperCase, trim, trimEnd, trimStart, truncate, unescapeHtml, unmaskEmail, utils, uuid, workbookToJSON, write, writeFile, writeFileXLSX };
250
+ export { type AmountNum, type CookieOptions, abs, add, addDaysToDate, addDaysUTC, addMonthsToDate, addMonthsUTC, aoaToSheet, camelCase, capitalize, ceil, clearAllCookies, countOccurrences, debounceFn as debounce, divide, endsWith, equals, escapeHtml, exportExcelFile, exportJSONToExcel, extractNumbers, floor, formatDate, formatDateOnly, formatMoney, formatMoneyToChinese, formatNumber, formatPercent, formatRelativeTime, formatTimeOnly, formatUTC, formatUTCDateOnly, formatUTCTimeOnly, formateAmount, formateFaitAmount, formatePrecision, fromBase64, fromUTC, getAllCookies, getAllQueryParams, getBrowser, getBrowserEngine, getBrowserVersion, getCookie, getDaysDiff, getDeviceInfo, getDevicePixelRatio, getDeviceType, getEndOfDay, getEndOfMonth, getEndOfWeek, getEndOfYear, getHoursDiff, getMinutesDiff, getOS, getQueryParam, getQueryParamAll, getScreenResolution, getSheet, getSheetNames, getStartOfDay, getStartOfMonth, getStartOfWeek, getStartOfYear, getTimestamp, getTimestampInSeconds, getTimezoneOffset, getTimezoneOffsetHours, getUTCAllWeeksInYear, getUTCDaysDiff, getUTCEndOfDay, getUTCEndOfMonth, getUTCHoursDiff, getUTCMinutesDiff, getUTCNow, getUTCStartOfDay, getUTCStartOfMonth, getUTCTimestamp, getUTCTimestampInSeconds, getUTCWeekEnd, getUTCWeekNumber, getUTCWeekStart, getUTCWeeksInYear, getUTCYearEnd, getUTCYearEndTimestamp, getUTCYearStart, getUTCYearStartTimestamp, getViewportSize, greaterThan, greaterThanOrEqual, hasCookie, includes, integerTokenArr, isAfterDate, isAndroid, isBeforeDate, isDesktop, isEmpty, isIOS, isMobile, isNotEmpty, isSameDayDate, isTablet, isTouchDevice, isValidDate, isValidEmail, isValidIdCard, isValidPhone, isValidUrl, isWeChat, jsonToWorkbook, kebabCase, length, lessThan, lessThanOrEqual, maskBankCard, maskEmail, maskIdCard, maskName, maskPhone, multiply, negate, normalizeSpaces, padEnd, padStart, parseDate, parseMoney, pascalCase, randomString, read, readExcelFile, readExcelToJSON, readFile, removeCookie, removeInvalidZero, removeSpaces, repeat, replaceAll, reverse, round, setCookie, sheetToAOA, sheetToCSV, sheetToHTML, snakeCase, split, startsWith, stripHtml, subDaysFromDate, subDaysUTC, subMonthsFromDate, subMonthsUTC, subtract, tableToSheet, throttleFn as throttle, titleCase, toBase64, toISOString, toLocalString, toLowerCase, toUTC, toUpperCase, trim, trimEnd, trimStart, truncate, tryRun, unescapeHtml, unmaskEmail, utils, uuid, workbookToJSON, write, writeFile, writeFileXLSX };
package/dist/index.d.ts CHANGED
@@ -89,6 +89,19 @@ declare function formatPercent(value: number, options?: {
89
89
  }): string;
90
90
  declare function maskEmail(email: string): string;
91
91
  declare function unmaskEmail(maskedEmail: string, originalEmail: string): string;
92
+ declare function toLocalString(value: string): string;
93
+ declare const integerTokenArr: string[];
94
+ type AmountNum = string | number | Decimal;
95
+ declare function removeInvalidZero(num: string): string;
96
+ declare function formatePrecision(n: AmountNum, precision?: number, tokenSymbol?: string): string | number;
97
+ declare function formateAmount({ num, precision, tokenSymbol, type, }: {
98
+ num: AmountNum;
99
+ precision?: number;
100
+ tokenSymbol?: string;
101
+ type?: string;
102
+ }): any;
103
+ declare function formateFaitAmount(num?: AmountNum): any;
104
+ declare function tryRun(fn: () => any): any;
92
105
 
93
106
  declare function formatDate(date: Date | number | string, formatStr?: string): string;
94
107
  declare function formatDateOnly(date: Date | number | string): string;
@@ -234,4 +247,4 @@ declare function isValidIdCard(idCard: string): boolean;
234
247
  declare function toBase64(str: string): string;
235
248
  declare function fromBase64(base64: string): string;
236
249
 
237
- export { type CookieOptions, abs, add, addDaysToDate, addDaysUTC, addMonthsToDate, addMonthsUTC, aoaToSheet, camelCase, capitalize, ceil, clearAllCookies, countOccurrences, debounceFn as debounce, divide, endsWith, equals, escapeHtml, exportExcelFile, exportJSONToExcel, extractNumbers, floor, formatDate, formatDateOnly, formatMoney, formatMoneyToChinese, formatNumber, formatPercent, formatRelativeTime, formatTimeOnly, formatUTC, formatUTCDateOnly, formatUTCTimeOnly, fromBase64, fromUTC, getAllCookies, getAllQueryParams, getBrowser, getBrowserEngine, getBrowserVersion, getCookie, getDaysDiff, getDeviceInfo, getDevicePixelRatio, getDeviceType, getEndOfDay, getEndOfMonth, getEndOfWeek, getEndOfYear, getHoursDiff, getMinutesDiff, getOS, getQueryParam, getQueryParamAll, getScreenResolution, getSheet, getSheetNames, getStartOfDay, getStartOfMonth, getStartOfWeek, getStartOfYear, getTimestamp, getTimestampInSeconds, getTimezoneOffset, getTimezoneOffsetHours, getUTCAllWeeksInYear, getUTCDaysDiff, getUTCEndOfDay, getUTCEndOfMonth, getUTCHoursDiff, getUTCMinutesDiff, getUTCNow, getUTCStartOfDay, getUTCStartOfMonth, getUTCTimestamp, getUTCTimestampInSeconds, getUTCWeekEnd, getUTCWeekNumber, getUTCWeekStart, getUTCWeeksInYear, getUTCYearEnd, getUTCYearEndTimestamp, getUTCYearStart, getUTCYearStartTimestamp, getViewportSize, greaterThan, greaterThanOrEqual, hasCookie, includes, isAfterDate, isAndroid, isBeforeDate, isDesktop, isEmpty, isIOS, isMobile, isNotEmpty, isSameDayDate, isTablet, isTouchDevice, isValidDate, isValidEmail, isValidIdCard, isValidPhone, isValidUrl, isWeChat, jsonToWorkbook, kebabCase, length, lessThan, lessThanOrEqual, maskBankCard, maskEmail, maskIdCard, maskName, maskPhone, multiply, negate, normalizeSpaces, padEnd, padStart, parseDate, parseMoney, pascalCase, randomString, read, readExcelFile, readExcelToJSON, readFile, removeCookie, removeSpaces, repeat, replaceAll, reverse, round, setCookie, sheetToAOA, sheetToCSV, sheetToHTML, snakeCase, split, startsWith, stripHtml, subDaysFromDate, subDaysUTC, subMonthsFromDate, subMonthsUTC, subtract, tableToSheet, throttleFn as throttle, titleCase, toBase64, toISOString, toLowerCase, toUTC, toUpperCase, trim, trimEnd, trimStart, truncate, unescapeHtml, unmaskEmail, utils, uuid, workbookToJSON, write, writeFile, writeFileXLSX };
250
+ export { type AmountNum, type CookieOptions, abs, add, addDaysToDate, addDaysUTC, addMonthsToDate, addMonthsUTC, aoaToSheet, camelCase, capitalize, ceil, clearAllCookies, countOccurrences, debounceFn as debounce, divide, endsWith, equals, escapeHtml, exportExcelFile, exportJSONToExcel, extractNumbers, floor, formatDate, formatDateOnly, formatMoney, formatMoneyToChinese, formatNumber, formatPercent, formatRelativeTime, formatTimeOnly, formatUTC, formatUTCDateOnly, formatUTCTimeOnly, formateAmount, formateFaitAmount, formatePrecision, fromBase64, fromUTC, getAllCookies, getAllQueryParams, getBrowser, getBrowserEngine, getBrowserVersion, getCookie, getDaysDiff, getDeviceInfo, getDevicePixelRatio, getDeviceType, getEndOfDay, getEndOfMonth, getEndOfWeek, getEndOfYear, getHoursDiff, getMinutesDiff, getOS, getQueryParam, getQueryParamAll, getScreenResolution, getSheet, getSheetNames, getStartOfDay, getStartOfMonth, getStartOfWeek, getStartOfYear, getTimestamp, getTimestampInSeconds, getTimezoneOffset, getTimezoneOffsetHours, getUTCAllWeeksInYear, getUTCDaysDiff, getUTCEndOfDay, getUTCEndOfMonth, getUTCHoursDiff, getUTCMinutesDiff, getUTCNow, getUTCStartOfDay, getUTCStartOfMonth, getUTCTimestamp, getUTCTimestampInSeconds, getUTCWeekEnd, getUTCWeekNumber, getUTCWeekStart, getUTCWeeksInYear, getUTCYearEnd, getUTCYearEndTimestamp, getUTCYearStart, getUTCYearStartTimestamp, getViewportSize, greaterThan, greaterThanOrEqual, hasCookie, includes, integerTokenArr, isAfterDate, isAndroid, isBeforeDate, isDesktop, isEmpty, isIOS, isMobile, isNotEmpty, isSameDayDate, isTablet, isTouchDevice, isValidDate, isValidEmail, isValidIdCard, isValidPhone, isValidUrl, isWeChat, jsonToWorkbook, kebabCase, length, lessThan, lessThanOrEqual, maskBankCard, maskEmail, maskIdCard, maskName, maskPhone, multiply, negate, normalizeSpaces, padEnd, padStart, parseDate, parseMoney, pascalCase, randomString, read, readExcelFile, readExcelToJSON, readFile, removeCookie, removeInvalidZero, removeSpaces, repeat, replaceAll, reverse, round, setCookie, sheetToAOA, sheetToCSV, sheetToHTML, snakeCase, split, startsWith, stripHtml, subDaysFromDate, subDaysUTC, subMonthsFromDate, subMonthsUTC, subtract, tableToSheet, throttleFn as throttle, titleCase, toBase64, toISOString, toLocalString, toLowerCase, toUTC, toUpperCase, trim, trimEnd, trimStart, truncate, tryRun, unescapeHtml, unmaskEmail, utils, uuid, workbookToJSON, write, writeFile, writeFileXLSX };
package/dist/index.js CHANGED
@@ -260,6 +260,7 @@ function getDeviceInfo() {
260
260
  }
261
261
 
262
262
  // src/format/index.ts
263
+ import Decimal from "decimal.js";
263
264
  function formatMoney(amount, options = {}) {
264
265
  const {
265
266
  decimals = 2,
@@ -267,20 +268,19 @@ function formatMoney(amount, options = {}) {
267
268
  separator = ",",
268
269
  decimalPoint = "."
269
270
  } = options;
270
- const num = typeof amount === "string" ? parseFloat(amount) : amount;
271
- if (isNaN(num)) {
272
- return `${symbol}0${decimalPoint}${"0".repeat(decimals)}`;
273
- }
274
- const isNegative = num < 0;
275
- const absNum = Math.abs(num);
276
- const fixed = absNum.toFixed(decimals);
277
- const [integerPart, decimalPart] = fixed.split(".");
278
- const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, separator);
279
- let result = symbol + formattedInteger;
280
- if (decimals > 0 && decimalPart) {
281
- result += decimalPoint + decimalPart;
282
- }
283
- return isNegative ? `-${result}` : result;
271
+ return tryRun(() => {
272
+ const dec = new Decimal(amount);
273
+ const isNegative = dec.isNegative();
274
+ const absDec = dec.abs();
275
+ const fixed = absDec.toFixed(decimals, Decimal.ROUND_DOWN);
276
+ const [integerPart, decimalPart] = fixed.split(".");
277
+ const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, separator);
278
+ let result = symbol + formattedInteger;
279
+ if (decimals > 0 && decimalPart) {
280
+ result += decimalPoint + decimalPart;
281
+ }
282
+ return isNegative ? `-${result}` : result;
283
+ }) ?? `${symbol}0${decimalPoint}${"0".repeat(decimals)}`;
284
284
  }
285
285
  function parseMoney(formattedAmount) {
286
286
  if (!formattedAmount || typeof formattedAmount !== "string") {
@@ -291,68 +291,70 @@ function parseMoney(formattedAmount) {
291
291
  return isNaN(num) ? 0 : num;
292
292
  }
293
293
  function formatNumber(amount, decimals = 2) {
294
- const num = typeof amount === "string" ? parseFloat(amount) : amount;
295
- if (isNaN(num)) {
296
- return "0." + "0".repeat(decimals);
297
- }
298
- const fixed = num.toFixed(decimals);
299
- const [integerPart, decimalPart] = fixed.split(".");
300
- const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
301
- return decimals > 0 && decimalPart ? `${formattedInteger}.${decimalPart}` : formattedInteger;
294
+ return tryRun(() => {
295
+ const dec = new Decimal(amount);
296
+ const fixed = dec.toFixed(decimals, Decimal.ROUND_DOWN);
297
+ const [integerPart, decimalPart] = fixed.split(".");
298
+ const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
299
+ return decimals > 0 && decimalPart ? `${formattedInteger}.${decimalPart}` : formattedInteger;
300
+ }) ?? "0." + "0".repeat(decimals);
302
301
  }
303
302
  function formatMoneyToChinese(amount) {
304
- const num = typeof amount === "string" ? parseFloat(amount) : amount;
305
- if (isNaN(num) || num < 0) {
306
- return "\u96F6\u5143\u6574";
307
- }
308
- const digits = ["\u96F6", "\u58F9", "\u8D30", "\u53C1", "\u8086", "\u4F0D", "\u9646", "\u67D2", "\u634C", "\u7396"];
309
- const units = ["", "\u62FE", "\u4F70", "\u4EDF"];
310
- const bigUnits = ["", "\u4E07", "\u4EBF", "\u5146"];
311
- const decimalUnits = ["\u89D2", "\u5206"];
312
- const [integerPart, decimalPart] = num.toFixed(2).split(".");
313
- let result = "";
314
- if (integerPart === "0") {
315
- result = "\u96F6\u5143";
316
- } else {
317
- const integerStr = integerPart;
318
- const len = integerStr.length;
319
- let zeroCount = 0;
320
- for (let i = 0; i < len; i++) {
321
- const digit = parseInt(integerStr[i]);
322
- const unitIndex = (len - i - 1) % 4;
323
- const bigUnitIndex = Math.floor((len - i - 1) / 4);
324
- if (digit === 0) {
325
- zeroCount++;
326
- } else {
327
- if (zeroCount > 0) {
328
- result += "\u96F6";
303
+ return tryRun(() => {
304
+ const dec = new Decimal(amount);
305
+ if (dec.isNegative()) {
306
+ return "\u96F6\u5143\u6574";
307
+ }
308
+ const num = dec.toNumber();
309
+ const digits = ["\u96F6", "\u58F9", "\u8D30", "\u53C1", "\u8086", "\u4F0D", "\u9646", "\u67D2", "\u634C", "\u7396"];
310
+ const units = ["", "\u62FE", "\u4F70", "\u4EDF"];
311
+ const bigUnits = ["", "\u4E07", "\u4EBF", "\u5146"];
312
+ const decimalUnits = ["\u89D2", "\u5206"];
313
+ const [integerPart, decimalPart] = num.toFixed(2).split(".");
314
+ let result = "";
315
+ if (integerPart === "0") {
316
+ result = "\u96F6\u5143";
317
+ } else {
318
+ const integerStr = integerPart;
319
+ const len = integerStr.length;
320
+ let zeroCount = 0;
321
+ for (let i = 0; i < len; i++) {
322
+ const digit = parseInt(integerStr[i]);
323
+ const unitIndex = (len - i - 1) % 4;
324
+ const bigUnitIndex = Math.floor((len - i - 1) / 4);
325
+ if (digit === 0) {
326
+ zeroCount++;
327
+ } else {
328
+ if (zeroCount > 0) {
329
+ result += "\u96F6";
330
+ }
331
+ result += digits[digit] + units[unitIndex];
332
+ zeroCount = 0;
329
333
  }
330
- result += digits[digit] + units[unitIndex];
331
- zeroCount = 0;
332
- }
333
- if (unitIndex === 0 && bigUnitIndex > 0) {
334
- if (result[result.length - 1] !== bigUnits[bigUnitIndex]) {
335
- result += bigUnits[bigUnitIndex];
334
+ if (unitIndex === 0 && bigUnitIndex > 0) {
335
+ if (result[result.length - 1] !== bigUnits[bigUnitIndex]) {
336
+ result += bigUnits[bigUnitIndex];
337
+ }
336
338
  }
337
339
  }
340
+ result += "\u5143";
338
341
  }
339
- result += "\u5143";
340
- }
341
- if (decimalPart && decimalPart !== "00") {
342
- const jiao = parseInt(decimalPart[0]);
343
- const fen = parseInt(decimalPart[1]);
344
- if (jiao > 0) {
345
- result += digits[jiao] + decimalUnits[0];
346
- } else if (fen > 0) {
347
- result += "\u96F6";
348
- }
349
- if (fen > 0) {
350
- result += digits[fen] + decimalUnits[1];
342
+ if (decimalPart && decimalPart !== "00") {
343
+ const jiao = parseInt(decimalPart[0]);
344
+ const fen = parseInt(decimalPart[1]);
345
+ if (jiao > 0) {
346
+ result += digits[jiao] + decimalUnits[0];
347
+ } else if (fen > 0) {
348
+ result += "\u96F6";
349
+ }
350
+ if (fen > 0) {
351
+ result += digits[fen] + decimalUnits[1];
352
+ }
353
+ } else {
354
+ result += "\u6574";
351
355
  }
352
- } else {
353
- result += "\u6574";
354
- }
355
- return result;
356
+ return result;
357
+ }) ?? "\u96F6\u5143\u6574";
356
358
  }
357
359
  function formatPercent(value, options = {}) {
358
360
  const { decimals = 2, multiply: multiply2 = true } = options;
@@ -395,6 +397,118 @@ function unmaskEmail(maskedEmail, originalEmail) {
395
397
  }
396
398
  return maskedEmail;
397
399
  }
400
+ function toLocalString(value) {
401
+ let result = value;
402
+ if (Number(value) >= 1e3) {
403
+ result = Number(value).toLocaleString("en-US");
404
+ } else {
405
+ result = value;
406
+ }
407
+ return result;
408
+ }
409
+ var integerTokenArr = ["SATS"];
410
+ function removeInvalidZero(num) {
411
+ let result = num;
412
+ if (num.includes(".")) {
413
+ result = result.replace(/\.?0+$/, "");
414
+ }
415
+ return result;
416
+ }
417
+ function formatePrecision(n, precision, tokenSymbol) {
418
+ if (!isNaN(Number(n))) {
419
+ let prec = precision && precision <= 8 ? precision : 8;
420
+ if (tokenSymbol && integerTokenArr.includes(tokenSymbol)) {
421
+ prec = 0;
422
+ }
423
+ return new Decimal(Number(n)).toFixed(prec, Decimal.ROUND_DOWN);
424
+ }
425
+ const num = new Decimal(n).toNumber();
426
+ return num;
427
+ }
428
+ function formateAmount({
429
+ num = 0,
430
+ precision = 8,
431
+ tokenSymbol = "",
432
+ type = "0"
433
+ }) {
434
+ return tryRun(() => {
435
+ const dec = new Decimal(num);
436
+ const { length: length2 } = dec.abs().floor().toString();
437
+ const pres = precision;
438
+ precision = precision > 8 ? 9 : precision + 1;
439
+ if (length2 >= precision) {
440
+ let value = "";
441
+ if (dec.greaterThan(1)) {
442
+ value = dec.toFixed(8, Decimal.ROUND_DOWN);
443
+ } else {
444
+ value = dec.toFixed(precision - length2, Decimal.ROUND_DOWN);
445
+ }
446
+ value = formatePrecision(value, pres).toString();
447
+ if (integerTokenArr.includes(tokenSymbol)) {
448
+ value = new Decimal(value).toFixed(0, Decimal.ROUND_CEIL);
449
+ }
450
+ let result = "";
451
+ if (typeof value === "string") {
452
+ if (value.includes(".")) {
453
+ result = value.replace(/\d(?=(\d{3})+\.)/g, "$&,");
454
+ } else {
455
+ result = toLocalString(value);
456
+ }
457
+ } else {
458
+ result = value;
459
+ }
460
+ if (Number(num) === 0) {
461
+ return result;
462
+ } else {
463
+ return dec.greaterThan(new Decimal(0)) ? removeInvalidZero(result) : type === "0" ? "-" + removeInvalidZero(result) : removeInvalidZero(result);
464
+ }
465
+ } else {
466
+ let value = "";
467
+ if (dec.greaterThan(1)) {
468
+ value = dec.toFixed(8, Decimal.ROUND_DOWN);
469
+ } else {
470
+ value = dec.toFixed(precision - length2, Decimal.ROUND_DOWN);
471
+ }
472
+ value = formatePrecision(value, pres).toString();
473
+ if (integerTokenArr.includes(tokenSymbol)) {
474
+ value = new Decimal(value).toFixed(0, Decimal.ROUND_CEIL);
475
+ }
476
+ let result = "";
477
+ if (typeof value === "string") {
478
+ if (value.includes(".")) {
479
+ result = value.replace(/\d(?=(\d{3})+\.)/g, "$&,");
480
+ } else {
481
+ result = toLocalString(value);
482
+ }
483
+ } else {
484
+ result = value;
485
+ }
486
+ return removeInvalidZero(result);
487
+ }
488
+ }) ?? "0.00";
489
+ }
490
+ function formateFaitAmount(num = 0) {
491
+ return tryRun(() => {
492
+ const dec = new Decimal(num);
493
+ const fnum = dec.toFixed(2, Decimal.ROUND_DOWN);
494
+ if (Number(num) >= 1e3) {
495
+ let result = toLocalString(fnum);
496
+ if (!result.includes(".")) {
497
+ result = result + ".00";
498
+ }
499
+ return result;
500
+ } else {
501
+ return fnum;
502
+ }
503
+ }) ?? "0.00";
504
+ }
505
+ function tryRun(fn) {
506
+ try {
507
+ return fn();
508
+ } catch (error) {
509
+ return null;
510
+ }
511
+ }
398
512
 
399
513
  // src/date/index.ts
400
514
  import {
@@ -704,19 +818,19 @@ function getUTCWeekNumber(date) {
704
818
  import { default as default2 } from "decimal.js";
705
819
 
706
820
  // src/decimal/utils/index.ts
707
- import Decimal from "decimal.js";
821
+ import Decimal2 from "decimal.js";
708
822
  function safeDecimal(value) {
709
823
  try {
710
- if (value instanceof Decimal) {
824
+ if (value instanceof Decimal2) {
711
825
  return value;
712
826
  }
713
- const decimal = new Decimal(value);
827
+ const decimal = new Decimal2(value);
714
828
  if (decimal.isNaN()) {
715
- return new Decimal(0);
829
+ return new Decimal2(0);
716
830
  }
717
831
  return decimal;
718
832
  } catch {
719
- return new Decimal(0);
833
+ return new Decimal2(0);
720
834
  }
721
835
  }
722
836
  function add(a, b) {
@@ -814,7 +928,7 @@ function round(value, decimalPlaces = 2) {
814
928
  function ceil(value, decimalPlaces = 2) {
815
929
  try {
816
930
  const decimal = safeDecimal(value);
817
- return decimal.toDecimalPlaces(decimalPlaces, Decimal.ROUND_CEIL).toNumber();
931
+ return decimal.toDecimalPlaces(decimalPlaces, Decimal2.ROUND_CEIL).toNumber();
818
932
  } catch {
819
933
  return 0;
820
934
  }
@@ -822,7 +936,7 @@ function ceil(value, decimalPlaces = 2) {
822
936
  function floor(value, decimalPlaces = 2) {
823
937
  try {
824
938
  const decimal = safeDecimal(value);
825
- return decimal.toDecimalPlaces(decimalPlaces, Decimal.ROUND_FLOOR).toNumber();
939
+ return decimal.toDecimalPlaces(decimalPlaces, Decimal2.ROUND_FLOOR).toNumber();
826
940
  } catch {
827
941
  return 0;
828
942
  }
@@ -1187,6 +1301,9 @@ export {
1187
1301
  formatUTC,
1188
1302
  formatUTCDateOnly,
1189
1303
  formatUTCTimeOnly,
1304
+ formateAmount,
1305
+ formateFaitAmount,
1306
+ formatePrecision,
1190
1307
  fromBase64,
1191
1308
  fromUTC,
1192
1309
  getAllCookies,
@@ -1243,6 +1360,7 @@ export {
1243
1360
  greaterThanOrEqual,
1244
1361
  hasCookie,
1245
1362
  includes,
1363
+ integerTokenArr,
1246
1364
  isAfterDate,
1247
1365
  isAndroid,
1248
1366
  isBeforeDate,
@@ -1284,6 +1402,7 @@ export {
1284
1402
  readExcelToJSON,
1285
1403
  readFile,
1286
1404
  removeCookie,
1405
+ removeInvalidZero,
1287
1406
  removeSpaces,
1288
1407
  repeat,
1289
1408
  replaceAll,
@@ -1307,6 +1426,7 @@ export {
1307
1426
  titleCase,
1308
1427
  toBase64,
1309
1428
  toISOString,
1429
+ toLocalString,
1310
1430
  toLowerCase,
1311
1431
  toUTC,
1312
1432
  toUpperCase,
@@ -1314,6 +1434,7 @@ export {
1314
1434
  trimEnd,
1315
1435
  trimStart,
1316
1436
  truncate,
1437
+ tryRun,
1317
1438
  unescapeHtml,
1318
1439
  unmaskEmail,
1319
1440
  utils2 as utils,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "react-util-tools",
3
- "version": "1.0.26",
3
+ "version": "1.0.28",
4
4
  "description": "A collection of useful utilities: throttle, debounce, date formatting, device detection, money formatting, decimal calculations, Excel processing and more",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -1,3 +1,5 @@
1
+ import Decimal from "decimal.js"
2
+
1
3
  /**
2
4
  * 金额格式化:将数字格式化为金额字符串
3
5
  * @param amount 金额数字
@@ -20,32 +22,29 @@ export function formatMoney(
20
22
  decimalPoint = '.'
21
23
  } = options
22
24
 
23
- // 转换为数字
24
- const num = typeof amount === 'string' ? parseFloat(amount) : amount
25
-
26
- // 处理无效数字
27
- if (isNaN(num)) {
28
- return `${symbol}0${decimalPoint}${'0'.repeat(decimals)}`
29
- }
30
-
31
- // 处理负数
32
- const isNegative = num < 0
33
- const absNum = Math.abs(num)
34
-
35
- // 固定小数位
36
- const fixed = absNum.toFixed(decimals)
37
- const [integerPart, decimalPart] = fixed.split('.')
38
-
39
- // 添加千分位分隔符
40
- const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, separator)
41
-
42
- // 组合结果
43
- let result = symbol + formattedInteger
44
- if (decimals > 0 && decimalPart) {
45
- result += decimalPoint + decimalPart
46
- }
47
-
48
- return isNegative ? `-${result}` : result
25
+ return tryRun(() => {
26
+ // 使用 Decimal 处理,避免精度问题
27
+ const dec = new Decimal(amount)
28
+
29
+ // 处理负数
30
+ const isNegative = dec.isNegative()
31
+ const absDec = dec.abs()
32
+
33
+ // 固定小数位(向下取整)
34
+ const fixed = absDec.toFixed(decimals, Decimal.ROUND_DOWN)
35
+ const [integerPart, decimalPart] = fixed.split('.')
36
+
37
+ // 添加千分位分隔符
38
+ const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, separator)
39
+
40
+ // 组合结果
41
+ let result = symbol + formattedInteger
42
+ if (decimals > 0 && decimalPart) {
43
+ result += decimalPoint + decimalPart
44
+ }
45
+
46
+ return isNegative ? `-${result}` : result
47
+ }) ?? `${symbol}0${decimalPoint}${'0'.repeat(decimals)}`
49
48
  }
50
49
 
51
50
  /**
@@ -74,20 +73,18 @@ export function parseMoney(formattedAmount: string): number {
74
73
  * @returns 格式化后的金额字符串(不含货币符号)
75
74
  */
76
75
  export function formatNumber(amount: number | string, decimals = 2): string {
77
- const num = typeof amount === 'string' ? parseFloat(amount) : amount
78
-
79
- if (isNaN(num)) {
80
- return '0.' + '0'.repeat(decimals)
81
- }
82
-
83
- const fixed = num.toFixed(decimals)
84
- const [integerPart, decimalPart] = fixed.split('.')
85
-
86
- const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
87
-
88
- return decimals > 0 && decimalPart
89
- ? `${formattedInteger}.${decimalPart}`
90
- : formattedInteger
76
+ return tryRun(() => {
77
+ // 使用 Decimal 处理,避免精度问题
78
+ const dec = new Decimal(amount)
79
+ const fixed = dec.toFixed(decimals, Decimal.ROUND_DOWN)
80
+ const [integerPart, decimalPart] = fixed.split('.')
81
+
82
+ const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
83
+
84
+ return decimals > 0 && decimalPart
85
+ ? `${formattedInteger}.${decimalPart}`
86
+ : formattedInteger
87
+ }) ?? '0.' + '0'.repeat(decimals)
91
88
  }
92
89
 
93
90
  /**
@@ -96,73 +93,76 @@ export function formatNumber(amount: number | string, decimals = 2): string {
96
93
  * @returns 中文大写金额
97
94
  */
98
95
  export function formatMoneyToChinese(amount: number | string): string {
99
- const num = typeof amount === 'string' ? parseFloat(amount) : amount
100
-
101
- if (isNaN(num) || num < 0) {
102
- return '零元整'
103
- }
104
-
105
- const digits = ['零', '壹', '贰', '叁', '肆', '伍', '陆', '柒', '捌', '玖']
106
- const units = ['', '拾', '佰', '仟']
107
- const bigUnits = ['', '', '亿', '']
108
- const decimalUnits = ['', '']
109
-
110
- // 分离整数和小数部分
111
- const [integerPart, decimalPart] = num.toFixed(2).split('.')
112
- let result = ''
113
-
114
- // 处理整数部分
115
- if (integerPart === '0') {
116
- result = '零元'
117
- } else {
118
- const integerStr = integerPart
119
- const len = integerStr.length
120
- let zeroCount = 0
121
-
122
- for (let i = 0; i < len; i++) {
123
- const digit = parseInt(integerStr[i])
124
- const unitIndex = (len - i - 1) % 4
125
- const bigUnitIndex = Math.floor((len - i - 1) / 4)
126
-
127
- if (digit === 0) {
128
- zeroCount++
129
- } else {
130
- if (zeroCount > 0) {
131
- result += '零'
96
+ return tryRun(() => {
97
+ const dec = new Decimal(amount)
98
+
99
+ if (dec.isNegative()) {
100
+ return '零元整'
101
+ }
102
+
103
+ const num = dec.toNumber()
104
+ const digits = ['', '', '', '', '肆', '伍', '陆', '柒', '捌', '玖']
105
+ const units = ['', '', '佰', '仟']
106
+ const bigUnits = ['', '万', '亿', '兆']
107
+ const decimalUnits = ['角', '分']
108
+
109
+ // 分离整数和小数部分
110
+ const [integerPart, decimalPart] = num.toFixed(2).split('.')
111
+ let result = ''
112
+
113
+ // 处理整数部分
114
+ if (integerPart === '0') {
115
+ result = '零元'
116
+ } else {
117
+ const integerStr = integerPart
118
+ const len = integerStr.length
119
+ let zeroCount = 0
120
+
121
+ for (let i = 0; i < len; i++) {
122
+ const digit = parseInt(integerStr[i])
123
+ const unitIndex = (len - i - 1) % 4
124
+ const bigUnitIndex = Math.floor((len - i - 1) / 4)
125
+
126
+ if (digit === 0) {
127
+ zeroCount++
128
+ } else {
129
+ if (zeroCount > 0) {
130
+ result += '零'
131
+ }
132
+ result += digits[digit] + units[unitIndex]
133
+ zeroCount = 0
132
134
  }
133
- result += digits[digit] + units[unitIndex]
134
- zeroCount = 0
135
- }
136
-
137
- if (unitIndex === 0 && bigUnitIndex > 0) {
138
- if (result[result.length - 1] !== bigUnits[bigUnitIndex]) {
139
- result += bigUnits[bigUnitIndex]
135
+
136
+ if (unitIndex === 0 && bigUnitIndex > 0) {
137
+ if (result[result.length - 1] !== bigUnits[bigUnitIndex]) {
138
+ result += bigUnits[bigUnitIndex]
139
+ }
140
140
  }
141
141
  }
142
+
143
+ result += '元'
142
144
  }
143
-
144
- result += '元'
145
- }
146
-
147
- // 处理小数部分
148
- if (decimalPart && decimalPart !== '00') {
149
- const jiao = parseInt(decimalPart[0])
150
- const fen = parseInt(decimalPart[1])
151
-
152
- if (jiao > 0) {
153
- result += digits[jiao] + decimalUnits[0]
154
- } else if (fen > 0) {
155
- result += '零'
156
- }
157
-
158
- if (fen > 0) {
159
- result += digits[fen] + decimalUnits[1]
145
+
146
+ // 处理小数部分
147
+ if (decimalPart && decimalPart !== '00') {
148
+ const jiao = parseInt(decimalPart[0])
149
+ const fen = parseInt(decimalPart[1])
150
+
151
+ if (jiao > 0) {
152
+ result += digits[jiao] + decimalUnits[0]
153
+ } else if (fen > 0) {
154
+ result += '零'
155
+ }
156
+
157
+ if (fen > 0) {
158
+ result += digits[fen] + decimalUnits[1]
159
+ }
160
+ } else {
161
+ result += '整'
160
162
  }
161
- } else {
162
- result += '整'
163
- }
164
-
165
- return result
163
+
164
+ return result
165
+ }) ?? '零元整'
166
166
  }
167
167
 
168
168
  /**
@@ -252,3 +252,164 @@ export function unmaskEmail(maskedEmail: string, originalEmail: string): string
252
252
 
253
253
  return maskedEmail // 不匹配,返回脱敏邮箱
254
254
  }
255
+
256
+ /* thousandths processing
257
+ ** value The coin string to be processed
258
+ */
259
+ export function toLocalString(value: string) {
260
+ // Do thousandths
261
+ let result = value
262
+ if (Number(value) >= 1000) {
263
+ result = Number(value).toLocaleString('en-US')
264
+ } else {
265
+ result = value
266
+ }
267
+ return result
268
+ }
269
+
270
+ export const integerTokenArr = ['SATS']
271
+ export type AmountNum = string | number | Decimal
272
+
273
+ export function removeInvalidZero(num: string) {
274
+ let result = num
275
+ if (num.includes('.')) {
276
+ result = result.replace(/\.?0+$/, '')
277
+ }
278
+ return result
279
+ }
280
+
281
+ //(<= 8)Output precision control, if it is greater than 8 digits, 8 digits will be reserved, if it is less than 8 digits, the corresponding digits will be displayed, and the default is 8 digits
282
+ export function formatePrecision(
283
+ n: AmountNum,
284
+ precision?: number,
285
+ tokenSymbol?: string
286
+ ) {
287
+ if (!isNaN(Number(n))) {
288
+ let prec = precision && precision <= 8 ? precision : 8
289
+ if (tokenSymbol && integerTokenArr.includes(tokenSymbol)) {
290
+ prec = 0
291
+ }
292
+ return new Decimal(Number(n)).toFixed(prec, Decimal.ROUND_DOWN)
293
+ }
294
+ const num = new Decimal(n).toNumber()
295
+ return num
296
+ }
297
+
298
+ /**
299
+ * (>= 8) formatted amount, number of tokens
300
+ * Keep 8 digits after the decimal point, a total of 10 digits
301
+ */
302
+ export function formateAmount({
303
+ num = 0,
304
+ precision = 8,
305
+ tokenSymbol = '',
306
+ type = '0',
307
+ }: {
308
+ num: AmountNum
309
+ precision?: number
310
+ tokenSymbol?: string
311
+ type?: string
312
+ }) {
313
+ return (
314
+ tryRun(() => {
315
+ const dec = new Decimal(num)
316
+ const { length } = dec.abs().floor().toString()
317
+ const pres = precision
318
+ precision = precision > 8 ? 9 : precision + 1
319
+ if (length >= precision) {
320
+ let value = ''
321
+ // Here it is necessary to judge whether it is greater than 1, and if it is greater than 1, directly retain 8 decimal places
322
+ if (dec.greaterThan(1)) {
323
+ value = dec.toFixed(8, Decimal.ROUND_DOWN)
324
+ } else {
325
+ value = dec.toFixed(precision - length, Decimal.ROUND_DOWN)
326
+ }
327
+ value = formatePrecision(value, pres).toString()
328
+ // If you round up here, the decimal place will be removed
329
+ // If it is a sats token, round up
330
+ if (integerTokenArr.includes(tokenSymbol)) {
331
+ value = new Decimal(value).toFixed(0, Decimal.ROUND_CEIL)
332
+ }
333
+ // Do thousandths
334
+ let result = ''
335
+ if (typeof value === 'string') {
336
+ if (value.includes('.')) {
337
+ result = value.replace(/\d(?=(\d{3})+\.)/g, '$&,')
338
+ } else {
339
+ result = toLocalString(value)
340
+ }
341
+ } else {
342
+ result = value
343
+ }
344
+
345
+ if (Number(num) === 0) {
346
+ return result
347
+ } else {
348
+ return dec.greaterThan(new Decimal(0))
349
+ ? removeInvalidZero(result)
350
+ : type === '0'
351
+ ? '-' + removeInvalidZero(result)
352
+ : removeInvalidZero(result)
353
+ }
354
+ } else {
355
+ let value = ''
356
+ // Here it is necessary to judge whether it is greater than 1, and if it is greater than 1, directly retain 8 decimal places
357
+ if (dec.greaterThan(1)) {
358
+ value = dec.toFixed(8, Decimal.ROUND_DOWN)
359
+ } else {
360
+ value = dec.toFixed(precision - length, Decimal.ROUND_DOWN)
361
+ }
362
+
363
+ value = formatePrecision(value, pres).toString()
364
+ // If it is a sats token, round up
365
+ if (integerTokenArr.includes(tokenSymbol)) {
366
+ value = new Decimal(value).toFixed(0, Decimal.ROUND_CEIL)
367
+ }
368
+
369
+ // Do thousandths
370
+ let result = ''
371
+ if (typeof value === 'string') {
372
+ if (value.includes('.')) {
373
+ result = value.replace(/\d(?=(\d{3})+\.)/g, '$&,')
374
+ } else {
375
+ result = toLocalString(value)
376
+ }
377
+ } else {
378
+ result = value
379
+ }
380
+
381
+ return removeInvalidZero(result)
382
+ }
383
+ }) ?? '0.00'
384
+ )
385
+ }
386
+
387
+ /**
388
+ * Format the local fiat currency, with two decimal places by default
389
+ */
390
+ export function formateFaitAmount(num: AmountNum = 0) {
391
+ return (
392
+ tryRun(() => {
393
+ const dec = new Decimal(num)
394
+ const fnum = dec.toFixed(2, Decimal.ROUND_DOWN)
395
+ // If it is greater than 1000, perform thousandths processing
396
+ if (Number(num) >= 1000) {
397
+ let result = toLocalString(fnum)
398
+ if (!result.includes('.')) {
399
+ result = result + '.00'
400
+ }
401
+ return result
402
+ } else {
403
+ return fnum
404
+ }
405
+ }) ?? '0.00'
406
+ )
407
+ }
408
+
409
+ export function tryRun(fn: () => any) {
410
+ try {
411
+ return fn()
412
+ } catch (error: any) {
413
+ return null
414
+ }
415
+ }
package/src/index.ts CHANGED
@@ -35,8 +35,16 @@ export {
35
35
  formatMoneyToChinese,
36
36
  formatPercent,
37
37
  maskEmail,
38
- unmaskEmail
38
+ unmaskEmail,
39
+ toLocalString,
40
+ integerTokenArr,
41
+ removeInvalidZero,
42
+ formatePrecision,
43
+ formateAmount,
44
+ formateFaitAmount,
45
+ tryRun
39
46
  } from './format/index'
47
+ export type { AmountNum } from './format/index'
40
48
  export {
41
49
  formatDate,
42
50
  formatDateOnly,