best-unit 1.0.11 → 1.0.13

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.
@@ -21,6 +21,10 @@ export declare function initFundUnit(params: {
21
21
  env: Env;
22
22
  };
23
23
 
24
+ export declare function getBalanceData(): any;
25
+
26
+ export declare function refreshBalance(): void;
27
+
24
28
  // Vite 代理配置类型
25
29
  export declare const viteProxy: {
26
30
  "/api": {
@@ -44,6 +48,8 @@ declare global {
44
48
  theme?: Theme;
45
49
  env: Env;
46
50
  }) => void;
51
+ getBalanceData: () => any;
52
+ refreshBalance: () => void;
47
53
  };
48
54
  }
49
55
 
@@ -57,6 +63,7 @@ declare global {
57
63
  [key: string]: any;
58
64
  };
59
65
  "best-statistical-balance": any;
66
+ "best-refresh-button": any;
60
67
  }
61
68
  }
62
69
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "best-unit",
3
3
  "private": false,
4
- "version": "1.0.11",
4
+ "version": "1.0.13",
5
5
  "type": "module",
6
6
  "main": "dist/best-unit.cjs",
7
7
  "module": "dist/best-unit.js",
@@ -24,17 +24,17 @@ export function createAxiosInstance(options: CreateAxiosOptions = {}) {
24
24
 
25
25
  let apiUrl: string;
26
26
  switch (env) {
27
- case Env.PROD:
28
- case Env.PRODUCTION:
29
- apiUrl = "https://fund.bestfulfill.com/api/sdk";
27
+ case Env.DEV:
28
+ case Env.DEVELOPMENT:
29
+ apiUrl = "/api";
30
30
  break;
31
31
  case Env.TEST:
32
32
  apiUrl = "https://fund.bestfulfill.tech/api/sdk";
33
33
  break;
34
- case Env.DEV:
35
- case Env.DEVELOPMENT:
34
+ case Env.PROD:
35
+ case Env.PRODUCTION:
36
36
  default:
37
- apiUrl = "/api";
37
+ apiUrl = "https://fund.bestfulfill.com/api/sdk";
38
38
  break;
39
39
  }
40
40
 
package/src/api/index.ts CHANGED
@@ -15,11 +15,20 @@ export function getBalance() {
15
15
  })
16
16
  .then((res) => {
17
17
  const data = res.data;
18
- return {
19
- totalAmount: data.total_amount || 0,
20
- availableAmount: data.available_amount || 0,
21
- frozenAmount: data.frozen_amount || 0,
18
+ const balanceData = {
19
+ fundBalanceId: data.fund_balance_id,
20
+ merchantId: data.merchant_id,
21
+ bizType: data.biz_type,
22
+ currency: data.currency,
23
+ totalAmount: data.total_amount,
24
+ availableAmount: data.available_amount,
25
+ frozenAmount: data.frozen_amount,
26
+ pendingAmount: data.pending_amount,
27
+ status: data.status,
28
+ createdAt: data.created_at,
22
29
  };
30
+ sessionStorage.setItem("balanceData", JSON.stringify(balanceData));
31
+ return balanceData;
23
32
  });
24
33
  }
25
34
 
@@ -83,8 +92,6 @@ export const createOnlineRecharge = async (data: any) => {
83
92
  sessionStorage.getItem("fund_unit_params") || "{}"
84
93
  );
85
94
 
86
- console.log(fundUnitParams, "fundUnitParams");
87
-
88
95
  const params = {
89
96
  merchant_id: Number(fundUnitParams.merchantId),
90
97
  biz_type: fundUnitParams.bizType,
@@ -0,0 +1,94 @@
1
+ import { useState } from "preact/hooks";
2
+ import register from "preact-custom-element";
3
+ import { getRefreshButtonTheme, getRefreshButtonSizeStyles } from "./theme";
4
+ import { refreshBalance } from "@/utils/business";
5
+ import { Button } from "@/components/common/button";
6
+
7
+ interface RefreshButtonProps {
8
+ color?: string;
9
+ size?: "small" | "medium" | "large";
10
+ children?: any; // slot 内容
11
+ }
12
+
13
+ function RefreshButton({
14
+ color,
15
+ size = "medium",
16
+ children,
17
+ }: RefreshButtonProps) {
18
+ const [isSpinning, setIsSpinning] = useState(false);
19
+ const theme = getRefreshButtonTheme(color);
20
+ const sizeStyles = getRefreshButtonSizeStyles(size);
21
+
22
+ const buttonStyle = {
23
+ ...theme,
24
+ ...sizeStyles,
25
+ display: "flex",
26
+ alignItems: "center",
27
+ };
28
+
29
+ // 内联 CSS 动画定义
30
+ const spinAnimation = `
31
+ @keyframes spin {
32
+ from {
33
+ transform: rotate(0deg);
34
+ }
35
+ to {
36
+ transform: rotate(360deg);
37
+ }
38
+ }
39
+
40
+ .refresh-icon {
41
+ transform-origin: center;
42
+ will-change: transform;
43
+ }
44
+ `;
45
+
46
+ const handleClick = async () => {
47
+ setIsSpinning(true);
48
+ try {
49
+ // 触发刷新事件
50
+ refreshBalance();
51
+ // 确保动画至少显示一段时间,给用户视觉反馈
52
+ await new Promise((resolve) => setTimeout(resolve, 300));
53
+ } finally {
54
+ setIsSpinning(false);
55
+ }
56
+ };
57
+
58
+ return (
59
+ <>
60
+ {/* 注入 CSS 动画 */}
61
+ <style>{spinAnimation}</style>
62
+
63
+ <Button onClick={handleClick} color={color}>
64
+ <div style={buttonStyle}>
65
+ <svg
66
+ className="refresh-icon"
67
+ width={size === "small" ? "14" : size === "large" ? "24" : "18"}
68
+ height={size === "small" ? "14" : size === "large" ? "24" : "18"}
69
+ viewBox="0 0 24 24"
70
+ fill="none"
71
+ stroke="currentColor"
72
+ strokeWidth="2"
73
+ strokeLinecap="round"
74
+ strokeLinejoin="round"
75
+ style={{
76
+ animation: isSpinning ? "spin 0.6s linear infinite" : "none",
77
+ transition: "all 0.2s ease",
78
+ }}
79
+ >
80
+ <path d="M21 2v6h-6"></path>
81
+ <path d="M3 12a9 9 0 0 1 15-6.7L21 8"></path>
82
+ <path d="M3 22v-6h6"></path>
83
+ <path d="M21 12a9 9 0 0 1-15 6.7L3 16"></path>
84
+ </svg>
85
+ {children && <span>{children}</span>}
86
+ </div>
87
+ </Button>
88
+ </>
89
+ );
90
+ }
91
+
92
+ register(RefreshButton, "best-refresh-button");
93
+
94
+ export default RefreshButton;
@@ -0,0 +1,58 @@
1
+ import { Theme } from "@/types";
2
+ import { getInitParams } from "@/utils/business";
3
+
4
+ export const refreshButtonThemes = {
5
+ white: {
6
+ background: "#1890ff",
7
+ color: "#fff",
8
+ border: "none",
9
+ borderRadius: 6,
10
+ cursor: "pointer",
11
+ fontWeight: 600,
12
+ transition: "all 0.2s ease",
13
+ },
14
+ dark: {
15
+ background: "#00E8C6",
16
+ color: "#fff",
17
+ border: "none",
18
+ borderRadius: 6,
19
+ cursor: "pointer",
20
+ fontWeight: 600,
21
+ transition: "all 0.2s ease",
22
+ },
23
+ };
24
+
25
+ export function getRefreshButtonTheme(color?: string) {
26
+ const theme = getInitParams<Theme>("theme");
27
+ const whiteTheme = theme === Theme.WHITE;
28
+
29
+ return whiteTheme
30
+ ? {
31
+ ...refreshButtonThemes.white,
32
+ background: color || refreshButtonThemes.white.background,
33
+ }
34
+ : {
35
+ ...refreshButtonThemes.dark,
36
+ background: color || refreshButtonThemes.dark.background,
37
+ };
38
+ }
39
+
40
+ export function getRefreshButtonSizeStyles(size: "small" | "medium" | "large") {
41
+ switch (size) {
42
+ case "small":
43
+ return {
44
+ fontSize: "12px",
45
+ gap: "2px",
46
+ };
47
+ case "large":
48
+ return {
49
+ fontSize: "18px",
50
+ gap: "6px",
51
+ };
52
+ default:
53
+ return {
54
+ fontSize: "14px",
55
+ gap: "4px",
56
+ };
57
+ }
58
+ }
@@ -26,48 +26,69 @@ function StatisticalBalance(props: { popoverPosition?: PopoverPosition }) {
26
26
  ],
27
27
  });
28
28
 
29
- useEffect(() => {
30
- const fetchBalance = async () => {
31
- try {
32
- const balance = await getBalance();
29
+ const fetchBalance = async () => {
30
+ try {
31
+ const balance = await getBalance();
33
32
 
34
- // 根据 API 返回的数据构建 balanceData
35
- const newBalanceData = {
36
- available: balance.availableAmount,
37
- currency: "USD", // 可以根据实际 API 返回调整
38
- symbol: "$",
39
- details: [
40
- {
41
- label: t("真实金额"),
42
- value: balance.availableAmount,
43
- color: "#15b36b",
44
- dot: "#15b36b",
45
- },
46
- {
47
- label: t("冻结金额"),
48
- value: balance.frozenAmount,
49
- color: "#f59e0b",
50
- dot: "#f59e0b",
51
- },
52
- {
53
- label: t("总可用"),
54
- value: balance.totalAmount,
55
- color: "#1890ff",
56
- dot: "#15b36b",
57
- },
58
- ],
59
- };
33
+ // 根据 API 返回的数据构建 balanceData
34
+ const newBalanceData = {
35
+ available: balance.availableAmount,
36
+ currency: "USD", // 可以根据实际 API 返回调整
37
+ symbol: "$",
38
+ details: [
39
+ {
40
+ label: t("真实金额"),
41
+ value: balance.availableAmount,
42
+ color: "#15b36b",
43
+ dot: "#15b36b",
44
+ },
45
+ {
46
+ label: t("冻结金额"),
47
+ value: balance.frozenAmount,
48
+ color: "#f59e0b",
49
+ dot: "#f59e0b",
50
+ },
51
+ {
52
+ label: t("总可用"),
53
+ value: balance.totalAmount,
54
+ color: "#1890ff",
55
+ dot: "#15b36b",
56
+ },
57
+ ],
58
+ };
60
59
 
61
- setBalanceData(newBalanceData);
62
- } catch (err) {
63
- console.error("获取余额失败:", err);
64
- // 获取失败时保持默认的 $0 USD 显示
65
- }
66
- };
60
+ setBalanceData(newBalanceData);
61
+ } catch (err) {
62
+ console.error("获取余额失败:", err);
63
+ // 获取失败时保持默认的 $0 USD 显示
64
+ }
65
+ };
67
66
 
67
+ useEffect(() => {
68
68
  fetchBalance();
69
69
  }, []);
70
70
 
71
+ useEffect(() => {
72
+ // 监听refresh-balance事件
73
+ const handleRefreshBalance = () => {
74
+ fetchBalance();
75
+ };
76
+
77
+ // 添加事件监听器
78
+ document.addEventListener(
79
+ "refresh-balance",
80
+ handleRefreshBalance as EventListener
81
+ );
82
+
83
+ // 清理函数
84
+ return () => {
85
+ document.removeEventListener(
86
+ "refresh-balance",
87
+ handleRefreshBalance as EventListener
88
+ );
89
+ };
90
+ }, []);
91
+
71
92
  const theme = getStatisticalBalanceTheme();
72
93
 
73
94
  return (
package/src/demo/App.tsx CHANGED
@@ -1,10 +1,12 @@
1
1
  import { useState } from "preact/hooks";
2
2
  import { npmTest, printCurrentTime } from "@/main";
3
3
  import { BestUnit } from "@/components/business/recharge-sdk";
4
- import { initFundUnit } from "@/main";
4
+ import { initFundUnit, refreshBalance } from "@/main";
5
5
  import StatisticalBalance from "@/components/business/statistical-balance";
6
+ import RefreshButton from "@/components/business/refresh-button";
6
7
  import { t } from "@/local";
7
8
  import { Env, Locale, Theme } from "@/types";
9
+ import TestBalanceData from "./testBalanceData";
8
10
 
9
11
  export default function DemoApp() {
10
12
  const [currentLocale, setCurrentLocale] = useState<Locale>(Locale.ZH);
@@ -107,6 +109,9 @@ export default function DemoApp() {
107
109
  </p>
108
110
  </div>
109
111
 
112
+ {/* getBalanceData 测试区域 */}
113
+ <TestBalanceData isDark={isDark} />
114
+
110
115
  {/* 国际化切换区域 */}
111
116
  <div
112
117
  style={{
@@ -342,6 +347,148 @@ export default function DemoApp() {
342
347
  <StatisticalBalance />
343
348
  </div>
344
349
 
350
+ {/* Refresh Button 测试区域 */}
351
+ <div
352
+ style={{
353
+ marginTop: 20,
354
+ padding: 16,
355
+ border: "2px solid #722ed1",
356
+ borderRadius: 8,
357
+ backgroundColor: "#f9f0ff",
358
+ }}
359
+ >
360
+ <h3 style={{ marginTop: 0, marginBottom: 12, color: "#722ed1" }}>
361
+ 🧪 Refresh Button 组件测试:
362
+ </h3>
363
+
364
+ <div style={{ marginBottom: 16 }}>
365
+ <h4 style={{ color: isDark ? "#F5F6FA" : "#333", marginBottom: 8 }}>
366
+ 基本用法:
367
+ </h4>
368
+ <div
369
+ style={{
370
+ display: "flex",
371
+ gap: 8,
372
+ alignItems: "center",
373
+ flexWrap: "wrap",
374
+ }}
375
+ >
376
+ <RefreshButton>{t("刷新")}</RefreshButton>
377
+ <RefreshButton></RefreshButton>
378
+ <RefreshButton color="#ff6b6b"></RefreshButton>
379
+ <RefreshButton color="#52c41a"></RefreshButton>
380
+ </div>
381
+ </div>
382
+
383
+ <div style={{ marginBottom: 16 }}>
384
+ <h4 style={{ color: isDark ? "#F5F6FA" : "#333", marginBottom: 8 }}>
385
+ 不同尺寸:
386
+ </h4>
387
+ <div
388
+ style={{
389
+ display: "flex",
390
+ gap: 8,
391
+ alignItems: "center",
392
+ flexWrap: "wrap",
393
+ }}
394
+ >
395
+ <RefreshButton size="small"></RefreshButton>
396
+ <RefreshButton size="medium"></RefreshButton>
397
+ <RefreshButton size="large"></RefreshButton>
398
+ </div>
399
+ </div>
400
+
401
+ <div style={{ marginBottom: 16 }}>
402
+ <h4 style={{ color: isDark ? "#F5F6FA" : "#333", marginBottom: 8 }}>
403
+ 与余额组件配合使用:
404
+ </h4>
405
+ <div
406
+ style={{
407
+ display: "flex",
408
+ gap: 16,
409
+ alignItems: "center",
410
+ justifyContent: "center",
411
+ flexWrap: "wrap",
412
+ }}
413
+ >
414
+ <RefreshButton>{t("刷新")}</RefreshButton>
415
+ </div>
416
+ <p
417
+ style={{
418
+ marginTop: 8,
419
+ fontSize: "12px",
420
+ color: "#666",
421
+ textAlign: "center",
422
+ }}
423
+ >
424
+ 点击刷新按钮可以重新获取余额数据
425
+ </p>
426
+ </div>
427
+
428
+ <div style={{ marginBottom: 16 }}>
429
+ <h4 style={{ color: isDark ? "#F5F6FA" : "#333", marginBottom: 8 }}>
430
+ 自定义样式:
431
+ </h4>
432
+ <div
433
+ style={{
434
+ display: "flex",
435
+ gap: 8,
436
+ alignItems: "center",
437
+ flexWrap: "wrap",
438
+ }}
439
+ >
440
+ <RefreshButton color="#1890ff" size="medium">
441
+ {t("刷新")}
442
+ </RefreshButton>
443
+ <RefreshButton color="#52c41a" size="small"></RefreshButton>
444
+ <RefreshButton color="#faad14" size="large">
445
+ {t("刷新")}
446
+ </RefreshButton>
447
+ <RefreshButton color="#ff4d4f" size="medium"></RefreshButton>
448
+ </div>
449
+ </div>
450
+
451
+ <div style={{ marginBottom: 16 }}>
452
+ <h4 style={{ color: isDark ? "#F5F6FA" : "#333", marginBottom: 8 }}>
453
+ 自定义文案:
454
+ </h4>
455
+ <div
456
+ style={{
457
+ display: "flex",
458
+ gap: 8,
459
+ alignItems: "center",
460
+ flexWrap: "wrap",
461
+ }}
462
+ >
463
+ <RefreshButton>重新加载</RefreshButton>
464
+ <RefreshButton>更新</RefreshButton>
465
+ <RefreshButton>同步</RefreshButton>
466
+ <RefreshButton>刷新余额</RefreshButton>
467
+ </div>
468
+ </div>
469
+
470
+ <div
471
+ style={{
472
+ marginTop: 16,
473
+ padding: 12,
474
+ backgroundColor: isDark ? "#23262F" : "#fff",
475
+ border: "1px solid #d9d9d9",
476
+ borderRadius: 6,
477
+ fontSize: "12px",
478
+ }}
479
+ >
480
+ <strong>测试说明:</strong>
481
+ <ul style={{ margin: "8px 0", paddingLeft: "20px" }}>
482
+ <li>点击任意刷新按钮都会触发余额组件重新获取数据</li>
483
+ <li>鼠标悬停在按钮上会有缩放和透明度变化效果</li>
484
+ <li>鼠标悬停在刷新图标上会有旋转动画</li>
485
+ <li>支持不同尺寸和颜色自定义</li>
486
+ <li>可以通过 slot 传入自定义文案,不传则只显示图标</li>
487
+ <li>支持任意内容作为 slot,包括文本、图标等</li>
488
+ </ul>
489
+ </div>
490
+ </div>
491
+
345
492
  {/* 国际化文本测试区域 */}
346
493
  <div
347
494
  style={{
@@ -424,6 +571,106 @@ export default function DemoApp() {
424
571
  </div>
425
572
  </div>
426
573
  </div>
574
+
575
+ {/* Business Utils 测试区域 */}
576
+ <div
577
+ style={{
578
+ marginTop: 20,
579
+ padding: 16,
580
+ border: "2px solid #13c2c2",
581
+ borderRadius: 8,
582
+ backgroundColor: "#e6fffb",
583
+ }}
584
+ >
585
+ <h3 style={{ marginTop: 0, marginBottom: 12, color: "#13c2c2" }}>
586
+ 🧪 Business Utils 方法测试:
587
+ </h3>
588
+
589
+ <div style={{ marginBottom: 16 }}>
590
+ <h4 style={{ color: isDark ? "#F5F6FA" : "#333", marginBottom: 8 }}>
591
+ refreshBalanceEvent 方法测试:
592
+ </h4>
593
+ <div
594
+ style={{
595
+ display: "flex",
596
+ gap: 8,
597
+ alignItems: "center",
598
+ flexWrap: "wrap",
599
+ }}
600
+ >
601
+ <button
602
+ onClick={() => refreshBalance()}
603
+ style={{
604
+ padding: "8px 16px",
605
+ backgroundColor: "#13c2c2",
606
+ color: "#fff",
607
+ border: "none",
608
+ borderRadius: 6,
609
+ cursor: "pointer",
610
+ fontSize: "14px",
611
+ }}
612
+ >
613
+ 调用 refreshBalanceEvent()
614
+ </button>
615
+ <button
616
+ onClick={() => refreshBalance()}
617
+ style={{
618
+ padding: "8px 16px",
619
+ backgroundColor: "#722ed1",
620
+ color: "#fff",
621
+ border: "none",
622
+ borderRadius: 6,
623
+ cursor: "pointer",
624
+ fontSize: "14px",
625
+ }}
626
+ >
627
+ 静默刷新余额
628
+ </button>
629
+ <button
630
+ onClick={() => refreshBalance()}
631
+ style={{
632
+ padding: "8px 16px",
633
+ backgroundColor: "#52c41a",
634
+ color: "#fff",
635
+ border: "none",
636
+ borderRadius: 6,
637
+ cursor: "pointer",
638
+ fontSize: "14px",
639
+ }}
640
+ >
641
+ 自定义来源刷新
642
+ </button>
643
+ </div>
644
+ </div>
645
+
646
+ <div
647
+ style={{
648
+ marginTop: 16,
649
+ padding: 12,
650
+ backgroundColor: isDark ? "#23262F" : "#fff",
651
+ border: "1px solid #d9d9d9",
652
+ borderRadius: 6,
653
+ fontSize: "12px",
654
+ }}
655
+ >
656
+ <strong>Business Utils 方法说明:</strong>
657
+ <ul style={{ margin: "8px 0", paddingLeft: "20px" }}>
658
+ <li>
659
+ <code>refreshBalanceEvent()</code> -
660
+ 基础刷新方法,触发所有余额组件刷新
661
+ </li>
662
+ <li>
663
+ <code>refreshBalanceEvent(&#123; silent: true &#125;)</code> -
664
+ 静默刷新,不显示控制台日志
665
+ </li>
666
+ <li>
667
+ <code>refreshBalanceEvent(&#123; source: "custom" &#125;)</code> -
668
+ 自定义来源标识的刷新
669
+ </li>
670
+ <li>现在refresh-button组件内部也使用这个函数,实现了代码复用</li>
671
+ </ul>
672
+ </div>
673
+ </div>
427
674
  </div>
428
675
  );
429
676
  }