react-util-tools 1.0.26 → 1.0.27
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 +87 -78
- package/dist/index.js +85 -76
- package/package.json +1 -1
- package/src/format/index.ts +263 -102
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: () =>
|
|
33
|
+
Decimal: () => import_decimal2.default,
|
|
34
34
|
XLSX: () => XLSX,
|
|
35
35
|
abs: () => abs,
|
|
36
36
|
add: () => add,
|
|
@@ -464,6 +464,7 @@ function getDeviceInfo() {
|
|
|
464
464
|
}
|
|
465
465
|
|
|
466
466
|
// src/format/index.ts
|
|
467
|
+
var import_decimal = __toESM(require("decimal.js"), 1);
|
|
467
468
|
function formatMoney(amount, options = {}) {
|
|
468
469
|
const {
|
|
469
470
|
decimals = 2,
|
|
@@ -471,20 +472,19 @@ function formatMoney(amount, options = {}) {
|
|
|
471
472
|
separator = ",",
|
|
472
473
|
decimalPoint = "."
|
|
473
474
|
} = options;
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
}
|
|
487
|
-
return isNegative ? `-${result}` : result;
|
|
475
|
+
return tryRun(() => {
|
|
476
|
+
const dec = new import_decimal.default(amount);
|
|
477
|
+
const isNegative = dec.isNegative();
|
|
478
|
+
const absDec = dec.abs();
|
|
479
|
+
const fixed = absDec.toFixed(decimals, import_decimal.default.ROUND_DOWN);
|
|
480
|
+
const [integerPart, decimalPart] = fixed.split(".");
|
|
481
|
+
const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, separator);
|
|
482
|
+
let result = symbol + formattedInteger;
|
|
483
|
+
if (decimals > 0 && decimalPart) {
|
|
484
|
+
result += decimalPoint + decimalPart;
|
|
485
|
+
}
|
|
486
|
+
return isNegative ? `-${result}` : result;
|
|
487
|
+
}) ?? `${symbol}0${decimalPoint}${"0".repeat(decimals)}`;
|
|
488
488
|
}
|
|
489
489
|
function parseMoney(formattedAmount) {
|
|
490
490
|
if (!formattedAmount || typeof formattedAmount !== "string") {
|
|
@@ -495,68 +495,70 @@ function parseMoney(formattedAmount) {
|
|
|
495
495
|
return isNaN(num) ? 0 : num;
|
|
496
496
|
}
|
|
497
497
|
function formatNumber(amount, decimals = 2) {
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
return decimals > 0 && decimalPart ? `${formattedInteger}.${decimalPart}` : formattedInteger;
|
|
498
|
+
return tryRun(() => {
|
|
499
|
+
const dec = new import_decimal.default(amount);
|
|
500
|
+
const fixed = dec.toFixed(decimals, import_decimal.default.ROUND_DOWN);
|
|
501
|
+
const [integerPart, decimalPart] = fixed.split(".");
|
|
502
|
+
const formattedInteger = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ",");
|
|
503
|
+
return decimals > 0 && decimalPart ? `${formattedInteger}.${decimalPart}` : formattedInteger;
|
|
504
|
+
}) ?? "0." + "0".repeat(decimals);
|
|
506
505
|
}
|
|
507
506
|
function formatMoneyToChinese(amount) {
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
result = "
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
507
|
+
return tryRun(() => {
|
|
508
|
+
const dec = new import_decimal.default(amount);
|
|
509
|
+
if (dec.isNegative()) {
|
|
510
|
+
return "\u96F6\u5143\u6574";
|
|
511
|
+
}
|
|
512
|
+
const num = dec.toNumber();
|
|
513
|
+
const digits = ["\u96F6", "\u58F9", "\u8D30", "\u53C1", "\u8086", "\u4F0D", "\u9646", "\u67D2", "\u634C", "\u7396"];
|
|
514
|
+
const units = ["", "\u62FE", "\u4F70", "\u4EDF"];
|
|
515
|
+
const bigUnits = ["", "\u4E07", "\u4EBF", "\u5146"];
|
|
516
|
+
const decimalUnits = ["\u89D2", "\u5206"];
|
|
517
|
+
const [integerPart, decimalPart] = num.toFixed(2).split(".");
|
|
518
|
+
let result = "";
|
|
519
|
+
if (integerPart === "0") {
|
|
520
|
+
result = "\u96F6\u5143";
|
|
521
|
+
} else {
|
|
522
|
+
const integerStr = integerPart;
|
|
523
|
+
const len = integerStr.length;
|
|
524
|
+
let zeroCount = 0;
|
|
525
|
+
for (let i = 0; i < len; i++) {
|
|
526
|
+
const digit = parseInt(integerStr[i]);
|
|
527
|
+
const unitIndex = (len - i - 1) % 4;
|
|
528
|
+
const bigUnitIndex = Math.floor((len - i - 1) / 4);
|
|
529
|
+
if (digit === 0) {
|
|
530
|
+
zeroCount++;
|
|
531
|
+
} else {
|
|
532
|
+
if (zeroCount > 0) {
|
|
533
|
+
result += "\u96F6";
|
|
534
|
+
}
|
|
535
|
+
result += digits[digit] + units[unitIndex];
|
|
536
|
+
zeroCount = 0;
|
|
533
537
|
}
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
if (result[result.length - 1] !== bigUnits[bigUnitIndex]) {
|
|
539
|
-
result += bigUnits[bigUnitIndex];
|
|
538
|
+
if (unitIndex === 0 && bigUnitIndex > 0) {
|
|
539
|
+
if (result[result.length - 1] !== bigUnits[bigUnitIndex]) {
|
|
540
|
+
result += bigUnits[bigUnitIndex];
|
|
541
|
+
}
|
|
540
542
|
}
|
|
541
543
|
}
|
|
544
|
+
result += "\u5143";
|
|
542
545
|
}
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
546
|
+
if (decimalPart && decimalPart !== "00") {
|
|
547
|
+
const jiao = parseInt(decimalPart[0]);
|
|
548
|
+
const fen = parseInt(decimalPart[1]);
|
|
549
|
+
if (jiao > 0) {
|
|
550
|
+
result += digits[jiao] + decimalUnits[0];
|
|
551
|
+
} else if (fen > 0) {
|
|
552
|
+
result += "\u96F6";
|
|
553
|
+
}
|
|
554
|
+
if (fen > 0) {
|
|
555
|
+
result += digits[fen] + decimalUnits[1];
|
|
556
|
+
}
|
|
557
|
+
} else {
|
|
558
|
+
result += "\u6574";
|
|
555
559
|
}
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
}
|
|
559
|
-
return result;
|
|
560
|
+
return result;
|
|
561
|
+
}) ?? "\u96F6\u5143\u6574";
|
|
560
562
|
}
|
|
561
563
|
function formatPercent(value, options = {}) {
|
|
562
564
|
const { decimals = 2, multiply: multiply2 = true } = options;
|
|
@@ -599,6 +601,13 @@ function unmaskEmail(maskedEmail, originalEmail) {
|
|
|
599
601
|
}
|
|
600
602
|
return maskedEmail;
|
|
601
603
|
}
|
|
604
|
+
function tryRun(fn) {
|
|
605
|
+
try {
|
|
606
|
+
return fn();
|
|
607
|
+
} catch (error) {
|
|
608
|
+
return null;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
602
611
|
|
|
603
612
|
// src/date/index.ts
|
|
604
613
|
var import_date_fns = require("date-fns");
|
|
@@ -862,22 +871,22 @@ function getUTCWeekNumber(date) {
|
|
|
862
871
|
}
|
|
863
872
|
|
|
864
873
|
// src/decimal/index.ts
|
|
865
|
-
var
|
|
874
|
+
var import_decimal2 = __toESM(require("decimal.js"), 1);
|
|
866
875
|
|
|
867
876
|
// src/decimal/utils/index.ts
|
|
868
|
-
var
|
|
877
|
+
var import_decimal3 = __toESM(require("decimal.js"), 1);
|
|
869
878
|
function safeDecimal(value) {
|
|
870
879
|
try {
|
|
871
|
-
if (value instanceof
|
|
880
|
+
if (value instanceof import_decimal3.default) {
|
|
872
881
|
return value;
|
|
873
882
|
}
|
|
874
|
-
const decimal = new
|
|
883
|
+
const decimal = new import_decimal3.default(value);
|
|
875
884
|
if (decimal.isNaN()) {
|
|
876
|
-
return new
|
|
885
|
+
return new import_decimal3.default(0);
|
|
877
886
|
}
|
|
878
887
|
return decimal;
|
|
879
888
|
} catch {
|
|
880
|
-
return new
|
|
889
|
+
return new import_decimal3.default(0);
|
|
881
890
|
}
|
|
882
891
|
}
|
|
883
892
|
function add(a, b) {
|
|
@@ -975,7 +984,7 @@ function round(value, decimalPlaces = 2) {
|
|
|
975
984
|
function ceil(value, decimalPlaces = 2) {
|
|
976
985
|
try {
|
|
977
986
|
const decimal = safeDecimal(value);
|
|
978
|
-
return decimal.toDecimalPlaces(decimalPlaces,
|
|
987
|
+
return decimal.toDecimalPlaces(decimalPlaces, import_decimal3.default.ROUND_CEIL).toNumber();
|
|
979
988
|
} catch {
|
|
980
989
|
return 0;
|
|
981
990
|
}
|
|
@@ -983,7 +992,7 @@ function ceil(value, decimalPlaces = 2) {
|
|
|
983
992
|
function floor(value, decimalPlaces = 2) {
|
|
984
993
|
try {
|
|
985
994
|
const decimal = safeDecimal(value);
|
|
986
|
-
return decimal.toDecimalPlaces(decimalPlaces,
|
|
995
|
+
return decimal.toDecimalPlaces(decimalPlaces, import_decimal3.default.ROUND_FLOOR).toNumber();
|
|
987
996
|
} catch {
|
|
988
997
|
return 0;
|
|
989
998
|
}
|
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
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
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
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
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
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
result = "
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
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
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
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
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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
|
-
|
|
353
|
-
|
|
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,13 @@ function unmaskEmail(maskedEmail, originalEmail) {
|
|
|
395
397
|
}
|
|
396
398
|
return maskedEmail;
|
|
397
399
|
}
|
|
400
|
+
function tryRun(fn) {
|
|
401
|
+
try {
|
|
402
|
+
return fn();
|
|
403
|
+
} catch (error) {
|
|
404
|
+
return null;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
398
407
|
|
|
399
408
|
// src/date/index.ts
|
|
400
409
|
import {
|
|
@@ -704,19 +713,19 @@ function getUTCWeekNumber(date) {
|
|
|
704
713
|
import { default as default2 } from "decimal.js";
|
|
705
714
|
|
|
706
715
|
// src/decimal/utils/index.ts
|
|
707
|
-
import
|
|
716
|
+
import Decimal2 from "decimal.js";
|
|
708
717
|
function safeDecimal(value) {
|
|
709
718
|
try {
|
|
710
|
-
if (value instanceof
|
|
719
|
+
if (value instanceof Decimal2) {
|
|
711
720
|
return value;
|
|
712
721
|
}
|
|
713
|
-
const decimal = new
|
|
722
|
+
const decimal = new Decimal2(value);
|
|
714
723
|
if (decimal.isNaN()) {
|
|
715
|
-
return new
|
|
724
|
+
return new Decimal2(0);
|
|
716
725
|
}
|
|
717
726
|
return decimal;
|
|
718
727
|
} catch {
|
|
719
|
-
return new
|
|
728
|
+
return new Decimal2(0);
|
|
720
729
|
}
|
|
721
730
|
}
|
|
722
731
|
function add(a, b) {
|
|
@@ -814,7 +823,7 @@ function round(value, decimalPlaces = 2) {
|
|
|
814
823
|
function ceil(value, decimalPlaces = 2) {
|
|
815
824
|
try {
|
|
816
825
|
const decimal = safeDecimal(value);
|
|
817
|
-
return decimal.toDecimalPlaces(decimalPlaces,
|
|
826
|
+
return decimal.toDecimalPlaces(decimalPlaces, Decimal2.ROUND_CEIL).toNumber();
|
|
818
827
|
} catch {
|
|
819
828
|
return 0;
|
|
820
829
|
}
|
|
@@ -822,7 +831,7 @@ function ceil(value, decimalPlaces = 2) {
|
|
|
822
831
|
function floor(value, decimalPlaces = 2) {
|
|
823
832
|
try {
|
|
824
833
|
const decimal = safeDecimal(value);
|
|
825
|
-
return decimal.toDecimalPlaces(decimalPlaces,
|
|
834
|
+
return decimal.toDecimalPlaces(decimalPlaces, Decimal2.ROUND_FLOOR).toNumber();
|
|
826
835
|
} catch {
|
|
827
836
|
return 0;
|
|
828
837
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-util-tools",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.27",
|
|
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",
|
package/src/format/index.ts
CHANGED
|
@@ -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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
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
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
result +=
|
|
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
|
-
|
|
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
|
+
}
|