best-unit 1.2.19 → 1.2.21

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.
Files changed (49) hide show
  1. package/BEST_UNIT_USAGE.md +402 -0
  2. package/dist/best-unit.cjs +13 -13
  3. package/dist/best-unit.js +1352 -1328
  4. package/index.html +13 -0
  5. package/package.json +1 -4
  6. package/src/api/axiosInstance.ts +111 -0
  7. package/src/api/index.ts +136 -0
  8. package/src/api/proxy.ts +11 -0
  9. package/src/components/business/recharge-sdk/components/offline-transfer-form/index.tsx +158 -0
  10. package/src/components/business/recharge-sdk/components/offline-transfer-form/theme.tsx +238 -0
  11. package/src/components/business/recharge-sdk/components/online-recharge-form/index.tsx +199 -0
  12. package/src/components/business/recharge-sdk/components/online-recharge-form/theme.tsx +159 -0
  13. package/src/components/business/recharge-sdk/components/recharge/index.tsx +152 -0
  14. package/src/components/business/recharge-sdk/components/recharge/theme.tsx +68 -0
  15. package/src/components/business/recharge-sdk/index.tsx +37 -0
  16. package/src/components/business/refresh-button/index.tsx +99 -0
  17. package/src/components/business/refresh-button/theme.tsx +58 -0
  18. package/src/components/business/statistical-balance/index.tsx +190 -0
  19. package/src/components/business/statistical-balance/theme.tsx +117 -0
  20. package/src/components/common/button/index.tsx +17 -0
  21. package/src/components/common/button/theme.tsx +56 -0
  22. package/src/components/common/hover-popover/index.tsx +182 -0
  23. package/src/components/common/hover-popover/theme.tsx +39 -0
  24. package/src/components/common/message/index.tsx +321 -0
  25. package/src/components/common/message/theme.tsx +25 -0
  26. package/src/components/common/modal/index.tsx +99 -0
  27. package/src/components/common/modal/theme.tsx +99 -0
  28. package/src/components/common/select/index.tsx +229 -0
  29. package/src/components/common/select/theme.tsx +104 -0
  30. package/src/components/common/upload/index.tsx +140 -0
  31. package/src/components/common/upload/theme.tsx +95 -0
  32. package/src/demo/App.tsx +685 -0
  33. package/src/demo/index.tsx +4 -0
  34. package/src/demo/testBalanceData.tsx +79 -0
  35. package/src/demo/theme-config-example.tsx +1 -0
  36. package/src/local/en.ts +64 -0
  37. package/src/local/index.ts +36 -0
  38. package/src/local/zh.ts +63 -0
  39. package/src/main.ts +26 -0
  40. package/src/types/global.d.ts +146 -0
  41. package/src/types/index.ts +31 -0
  42. package/src/types/preact-custom-element.d.ts +1 -0
  43. package/src/utils/business/index.ts +132 -0
  44. package/src/utils/common/index.ts +8 -0
  45. package/src/vite-env.d.ts +8 -0
  46. package/tsconfig.app.json +33 -0
  47. package/tsconfig.json +15 -0
  48. package/tsconfig.node.json +24 -0
  49. package/vite.config.ts +24 -0
@@ -0,0 +1,199 @@
1
+ import type { FunctionalComponent } from "preact";
2
+ import { useState, useEffect } from "preact/hooks";
3
+ import { t } from "@/local";
4
+ import { Select } from "@/components/common/select";
5
+ import { calcPaymentAmount } from "@/api";
6
+ import { getOnlineRechargeFormTheme } from "./theme";
7
+
8
+ interface OnlineRechargeFormProps {
9
+ formState: {
10
+ amount: string;
11
+ rechargeChannel: string;
12
+ currency: string;
13
+ loading: boolean;
14
+ error: string;
15
+ amountError: string;
16
+ rechargeChannelError: string;
17
+ currencyError: string;
18
+ };
19
+ setFormState: (fn: (state: any) => any) => void;
20
+ onClose: () => void;
21
+ loading: boolean;
22
+ }
23
+
24
+ // 辅助函数:只允许输入数字和小数点,且最多两位小数
25
+ function formatAmountInput(value: string) {
26
+ // 只保留数字和小数点
27
+ value = value.replace(/[^\d.]/g, "");
28
+ // 只保留第一个小数点
29
+ value = value.replace(/\.(?=.*\.)/g, "");
30
+ // 保证最多两位小数
31
+ value = value.replace(/^(\d+)(\.\d{0,2})?.*$/, "$1$2");
32
+ // 去除前导0(保留0.和0.xx)
33
+ value = value.replace(/^0+(\d)/, "$1");
34
+ if (value.startsWith(".")) value = "0" + value;
35
+ return value;
36
+ }
37
+ // 辅助函数:格式化为两位小数
38
+ function formatToTwoDecimal(value: string) {
39
+ if (!value) return "";
40
+ const num = parseFloat(value);
41
+ if (isNaN(num)) return "";
42
+ return num.toFixed(2);
43
+ }
44
+
45
+ export const OnlineRechargeForm: FunctionalComponent<
46
+ OnlineRechargeFormProps
47
+ > = ({ formState, setFormState, onClose, loading }) => {
48
+ const allDicts = JSON.parse(sessionStorage.getItem("all_dicts") || "{}");
49
+ const currencyDict = allDicts?.currency || [];
50
+ const channelDict =
51
+ allDicts?.channel?.filter((item: any) => item.payment_support) || [];
52
+ const [actualAmount, setActualAmount] = useState<string>("");
53
+ const [showFeeTip, setShowFeeTip] = useState(false);
54
+ const theme = getOnlineRechargeFormTheme();
55
+
56
+ // 当三个参数都填写完整时,计算实际支付金额
57
+ useEffect(() => {
58
+ if (formState.currency && formState.amount && formState.rechargeChannel) {
59
+ calcPaymentAmount({
60
+ channel: formState.rechargeChannel,
61
+ amount: formState.amount,
62
+ currency: formState.currency,
63
+ })
64
+ .then((paymentAmount) => {
65
+ setActualAmount(paymentAmount);
66
+ setShowFeeTip(true);
67
+ })
68
+ .catch((error) => {
69
+ console.error("计算支付金额失败:", error);
70
+ setShowFeeTip(false);
71
+ });
72
+ } else {
73
+ setShowFeeTip(false);
74
+ }
75
+ }, [formState.currency, formState.amount, formState.rechargeChannel]);
76
+
77
+ return (
78
+ <>
79
+ <div style={{ marginBottom: 18 }}>
80
+ <div style={theme.label}>
81
+ <span style={{ color: "#F53F3F" }}>*</span> {t("充值币种")}
82
+ </div>
83
+ <Select
84
+ value={formState.currency}
85
+ onChange={(value) => {
86
+ setFormState((state: any) => ({
87
+ ...state,
88
+ currency: value,
89
+ }));
90
+ }}
91
+ options={currencyDict?.map((item: any) => ({
92
+ value: item.value,
93
+ label: item.label,
94
+ }))}
95
+ placeholder={t("请选择充值币种")}
96
+ />
97
+ </div>
98
+ <div style={{ marginBottom: 18 }}>
99
+ <div style={theme.label}>
100
+ <span style={{ color: "#F53F3F" }}>*</span> {t("充值金额")}
101
+ </div>
102
+ <input
103
+ type="text"
104
+ placeholder={t("请输入充值金额")}
105
+ value={formState.amount}
106
+ onInput={(e) => {
107
+ let value = (e.target as HTMLInputElement).value;
108
+ value = formatAmountInput(value);
109
+ let amountError = "";
110
+ // 只有输入为合法数字且不以小数点结尾时才做区间修正
111
+ if (value && !value.endsWith(".")) {
112
+ let num = parseFloat(value);
113
+ if (!isNaN(num)) {
114
+ if (num < 1) num = 1;
115
+ if (num > 999999.99) num = 999999.99;
116
+ value = num.toString();
117
+ // 如果原始输入有小数点且小数点后有内容,保留小数部分
118
+ if (/\./.test((e.target as HTMLInputElement).value)) {
119
+ const decimalPart = (
120
+ e.target as HTMLInputElement
121
+ ).value.split(".")[1];
122
+ if (decimalPart !== undefined && decimalPart.length > 0) {
123
+ value = num.toFixed(Math.min(decimalPart.length, 2));
124
+ }
125
+ }
126
+ }
127
+ }
128
+ setFormState((state: any) => ({
129
+ ...state,
130
+ amount: value,
131
+ amountError,
132
+ }));
133
+ }}
134
+ onBlur={(e) => {
135
+ let value = (e.target as HTMLInputElement).value;
136
+ value = formatToTwoDecimal(value);
137
+ setFormState((state: any) => ({
138
+ ...state,
139
+ amount: value,
140
+ amountError: "",
141
+ }));
142
+ }}
143
+ style={{
144
+ ...theme.input,
145
+ ...(formState.amountError ? theme.inputError : {}),
146
+ }}
147
+ />
148
+ {formState.amountError && (
149
+ <div style={theme.error}>{formState.amountError}</div>
150
+ )}
151
+ </div>
152
+ <div style={{ marginBottom: 24 }}>
153
+ <div style={theme.label}>
154
+ <span style={{ color: "#F53F3F" }}>*</span> {t("支付平台")}
155
+ </div>
156
+ <Select
157
+ value={formState.rechargeChannel}
158
+ onChange={(value) => {
159
+ setFormState((state: any) => ({
160
+ ...state,
161
+ rechargeChannel: value,
162
+ rechargeChannelError: value ? "" : state.rechargeChannelError,
163
+ }));
164
+ }}
165
+ options={channelDict?.map((item: any) => ({
166
+ value: item.value,
167
+ label: item.label,
168
+ }))}
169
+ placeholder={t("请选择支付平台")}
170
+ ></Select>
171
+ {formState.rechargeChannelError && (
172
+ <div style={theme.error}>{formState.rechargeChannelError}</div>
173
+ )}
174
+ </div>
175
+ {/* 手续费提示 */}
176
+ {showFeeTip && actualAmount && (
177
+ <div style={theme.feeTip}>
178
+ {channelDict.find(
179
+ (item: any) => item.value === formState.rechargeChannel
180
+ )?.label || formState.rechargeChannel}
181
+ {t("需要收取手续费,实际支付金额约为:")}${actualAmount}
182
+ </div>
183
+ )}
184
+ {formState.error && (
185
+ <div style={{ color: "#ff4d4f", marginBottom: 12 }}>
186
+ {formState.error}
187
+ </div>
188
+ )}
189
+ <div style={{ display: "flex", justifyContent: "flex-end", gap: 12 }}>
190
+ <button type="button" onClick={onClose} style={theme.buttonCancel}>
191
+ {t("取消")}
192
+ </button>
193
+ <button type="submit" disabled={loading} style={theme.buttonSubmit}>
194
+ {loading ? t("提交中...") : t("去支付")}
195
+ </button>
196
+ </div>
197
+ </>
198
+ );
199
+ };
@@ -0,0 +1,159 @@
1
+ import { Size, Theme, type ThemeConfig } from "@/types";
2
+ import { getInitParams } from "@/utils/business";
3
+
4
+ // 动态生成主题配置的函数
5
+ function createOnlineRechargeFormThemes() {
6
+ const size = getInitParams<Size>("size"); // 每次调用时重新获取最新值
7
+
8
+ return {
9
+ white: {
10
+ label: {
11
+ marginBottom: size === Size.SMALL ? 6 : 8,
12
+ fontSize: size === Size.SMALL ? 12 : 14,
13
+ color: "#222",
14
+ textAlign: "left",
15
+ display: "block",
16
+ },
17
+ input: {
18
+ width: "100%",
19
+ padding: size === Size.SMALL ? "8px 10px" : "10px 12px",
20
+ borderRadius: 6,
21
+ boxSizing: "border-box",
22
+ border: "1px solid #E5E6EB",
23
+ background: "#fff",
24
+ color: "#222",
25
+ fontSize: size === Size.SMALL ? 12 : 15,
26
+ outline: "none",
27
+ marginBottom: 0,
28
+ },
29
+ inputError: {
30
+ border: "1px solid #ff4d4f",
31
+ },
32
+ selectError: {
33
+ border: "1px solid #ff4d4f",
34
+ },
35
+ error: {
36
+ color: "#ff4d4f",
37
+ fontSize: size === Size.SMALL ? 12 : 13,
38
+ marginTop: size === Size.SMALL ? 2 : 4,
39
+ textAlign: "left",
40
+ },
41
+ buttonCancel: {
42
+ background: "#fff",
43
+ color: "#222",
44
+ border: "1px solid #E5E6EB",
45
+ borderRadius: 6,
46
+ padding: size === Size.SMALL ? "6px 16px" : "8px 24px",
47
+ fontSize: size === Size.SMALL ? 12 : 15,
48
+ cursor: "pointer",
49
+ },
50
+ buttonSubmit: {
51
+ background: "#1890ff",
52
+ color: "#fff",
53
+ border: "none",
54
+ borderRadius: 6,
55
+ padding: size === Size.SMALL ? "6px 16px" : "8px 24px",
56
+ fontSize: size === Size.SMALL ? 12 : 15,
57
+ cursor: "pointer",
58
+ fontWeight: 600,
59
+ },
60
+ feeTip: {
61
+ marginBottom: 24,
62
+ padding: size === Size.SMALL ? "8px 10px" : "12px 16px",
63
+ background: "#f6ffed",
64
+ border: "1px solid #b7eb8f",
65
+ borderRadius: 6,
66
+ fontSize: size === Size.SMALL ? 12 : 14,
67
+ color: "#52c41a",
68
+ },
69
+ },
70
+ dark: {
71
+ label: {
72
+ marginBottom: size === Size.SMALL ? 6 : 8,
73
+ fontSize: size === Size.SMALL ? 12 : 14,
74
+ color: "#fff",
75
+ textAlign: "left",
76
+ display: "block",
77
+ },
78
+ input: {
79
+ width: "100%",
80
+ padding: size === Size.SMALL ? "8px 10px" : "10px 12px",
81
+ borderRadius: 6,
82
+ boxSizing: "border-box",
83
+ border: "1px solid #374151",
84
+ background: "#23262F",
85
+ color: "#fff",
86
+ fontSize: size === Size.SMALL ? 12 : 15,
87
+ outline: "none",
88
+ marginBottom: 0,
89
+ },
90
+ inputError: {
91
+ border: "1px solid #ff4d4f",
92
+ },
93
+ selectError: {
94
+ border: "1px solid #ff4d4f",
95
+ },
96
+ error: {
97
+ color: "#ff4d4f",
98
+ fontSize: size === Size.SMALL ? 12 : 13,
99
+ marginTop: size === Size.SMALL ? 2 : 4,
100
+ textAlign: "left",
101
+ },
102
+ buttonCancel: {
103
+ background: "#23262F",
104
+ color: "#fff",
105
+ border: "none",
106
+ borderRadius: 6,
107
+ padding: size === Size.SMALL ? "6px 16px" : "8px 24px",
108
+ fontSize: size === Size.SMALL ? 12 : 15,
109
+ cursor: "pointer",
110
+ },
111
+ buttonSubmit: {
112
+ background: "#00E8C6",
113
+ color: "#fff",
114
+ border: "none",
115
+ borderRadius: 6,
116
+ padding: size === Size.SMALL ? "6px 16px" : "8px 24px",
117
+ fontSize: size === Size.SMALL ? 12 : 15,
118
+ cursor: "pointer",
119
+ fontWeight: 600,
120
+ },
121
+ feeTip: {
122
+ marginBottom: size === Size.SMALL ? 16 : 24,
123
+ padding: size === Size.SMALL ? "8px 10px" : "12px 16px",
124
+ background: "#1a1a1a",
125
+ border: "1px solid #333",
126
+ borderRadius: 6,
127
+ fontSize: size === Size.SMALL ? 12 : 14,
128
+ color: "#52c41a",
129
+ },
130
+ },
131
+ };
132
+ }
133
+
134
+ export function getOnlineRechargeFormTheme() {
135
+ const theme = getInitParams<Theme>("theme");
136
+ const themeConfig = getInitParams<ThemeConfig>("themeConfig");
137
+ const whiteTheme = theme === Theme.WHITE;
138
+
139
+ // 每次调用时重新生成主题配置
140
+ const onlineRechargeFormThemes = createOnlineRechargeFormThemes();
141
+ const baseTheme = whiteTheme
142
+ ? onlineRechargeFormThemes.white
143
+ : onlineRechargeFormThemes.dark;
144
+ // 如果初始化时传入了按钮配置,则覆盖默认配置
145
+ if (themeConfig) {
146
+ const config = whiteTheme ? themeConfig.white : themeConfig.dark;
147
+ if (config?.color) {
148
+ return {
149
+ ...baseTheme,
150
+ buttonSubmit: {
151
+ ...baseTheme.buttonSubmit,
152
+ background: config.color,
153
+ },
154
+ };
155
+ }
156
+ }
157
+
158
+ return baseTheme;
159
+ }
@@ -0,0 +1,152 @@
1
+ import { useState, useEffect } from "preact/hooks";
2
+ import { OnlineRechargeForm } from "../online-recharge-form";
3
+ import { OfflineTransferForm } from "../offline-transfer-form";
4
+ import { t } from "@/local";
5
+ import { Modal } from "@/components/common/modal";
6
+ import { getRechargeTheme } from "./theme";
7
+ import { getInitParams } from "@/utils/business";
8
+ import { Size } from "@/types";
9
+ const size = getInitParams<Size>("size");
10
+ interface ModalFormProps {
11
+ visible: boolean;
12
+ onClose: () => void;
13
+ onSubmit: (form: {
14
+ amount: string;
15
+ rechargeChannel: string;
16
+ currency: string;
17
+ }) => Promise<void>;
18
+ color?: string;
19
+ merchantId?: string;
20
+ bizType?: string;
21
+ token?: string;
22
+ }
23
+
24
+ export function Recharge({ visible, onClose, onSubmit }: ModalFormProps) {
25
+ const [formState, setFormState] = useState({
26
+ amount: "",
27
+ rechargeChannel: "",
28
+ currency: "USD",
29
+ loading: false,
30
+ error: "",
31
+ amountError: "",
32
+ rechargeChannelError: "",
33
+ currencyError: "",
34
+ });
35
+ const [offlineFormState, setOfflineFormState] = useState({
36
+ platform: "",
37
+ transactionId: "",
38
+ files: [],
39
+ platformError: "",
40
+ transactionIdError: "",
41
+ filesError: "",
42
+ loading: false,
43
+ });
44
+ const [activeTab, setActiveTab] = useState<"online" | "offline">("online");
45
+ const theme = getRechargeTheme();
46
+
47
+ // 每次关闭弹窗时重置内容
48
+ useEffect(() => {
49
+ if (!visible) {
50
+ setActiveTab("online");
51
+ setFormState({
52
+ amount: "",
53
+ rechargeChannel: "",
54
+ currency: "USD",
55
+ loading: false,
56
+ error: "",
57
+ amountError: "",
58
+ rechargeChannelError: "",
59
+ currencyError: "",
60
+ });
61
+ setOfflineFormState({
62
+ platform: "",
63
+ transactionId: "",
64
+ files: [],
65
+ platformError: "",
66
+ transactionIdError: "",
67
+ filesError: "",
68
+ loading: false,
69
+ });
70
+ }
71
+ }, [visible]);
72
+
73
+ const handleSubmit = async (e: Event) => {
74
+ e.preventDefault();
75
+ let valid = true;
76
+ setFormState((state) => ({
77
+ ...state,
78
+ amountError: "",
79
+ rechargeChannelError: "",
80
+ }));
81
+ if (!formState.amount.trim()) {
82
+ setFormState((state) => ({ ...state, amountError: t("请输入充值金额") }));
83
+ valid = false;
84
+ }
85
+ if (!formState.rechargeChannel) {
86
+ setFormState((state) => ({
87
+ ...state,
88
+ rechargeChannelError: t("请选择支付平台"),
89
+ }));
90
+ valid = false;
91
+ }
92
+ if (!valid) return;
93
+ setFormState((state) => ({ ...state, loading: true, error: "" }));
94
+ try {
95
+ await onSubmit({
96
+ amount: formState.amount,
97
+ rechargeChannel: formState.rechargeChannel,
98
+ currency: formState.currency,
99
+ });
100
+ onClose();
101
+ } catch {
102
+ setFormState((state) => ({ ...state, error: t("提交失败,请重试") }));
103
+ } finally {
104
+ setFormState((state) => ({ ...state, loading: false }));
105
+ }
106
+ };
107
+
108
+ return (
109
+ <Modal visible={visible} onClose={onClose} title={t("充值 / 转账")}>
110
+ {/* tab 按钮区域 */}
111
+ <div
112
+ style={{
113
+ display: "flex",
114
+ marginBottom: size === Size.SMALL ? 16 : 28,
115
+ }}
116
+ >
117
+ <button
118
+ type="button"
119
+ onClick={() => setActiveTab("online")}
120
+ style={theme.tabBtn(activeTab === "online", true)}
121
+ >
122
+ {t("在线充值")}
123
+ </button>
124
+ <button
125
+ type="button"
126
+ onClick={() => setActiveTab("offline")}
127
+ style={theme.tabBtn(activeTab === "offline", false)}
128
+ >
129
+ {t("线下转账")}
130
+ </button>
131
+ </div>
132
+ {/* tab 内容区域 */}
133
+ {activeTab === "online" ? (
134
+ <form onSubmit={handleSubmit}>
135
+ <OnlineRechargeForm
136
+ formState={formState}
137
+ setFormState={setFormState}
138
+ onClose={onClose}
139
+ loading={formState.loading}
140
+ />
141
+ </form>
142
+ ) : (
143
+ <OfflineTransferForm
144
+ formState={offlineFormState}
145
+ setFormState={setOfflineFormState}
146
+ onClose={onClose}
147
+ loading={offlineFormState.loading}
148
+ />
149
+ )}
150
+ </Modal>
151
+ );
152
+ }
@@ -0,0 +1,68 @@
1
+ import { Size, Theme, type ThemeConfig } from "@/types";
2
+ import { getInitParams } from "@/utils/business";
3
+
4
+ // 动态生成主题配置的函数
5
+ function createRechargeThemes() {
6
+ const size = getInitParams<Size>("size"); // 每次调用时重新获取最新值
7
+
8
+ return {
9
+ white: {
10
+ tabBtn: (active: boolean, left: boolean) => ({
11
+ flex: 1,
12
+ background: active ? "#fff" : "#F7F8FA",
13
+ color: active ? "#1890ff" : "#222",
14
+ border: "1px solid #F0F1F3",
15
+ borderRadius: left ? "8px 0 0 8px" : "0 8px 8px 0",
16
+ fontWeight: active ? 600 : 400,
17
+ fontSize: size === Size.SMALL ? 12 : 16,
18
+ height: size === Size.SMALL ? 32 : 48,
19
+ boxShadow: active ? "0 2px 8px 0 rgba(20,20,20,0.04)" : "none",
20
+ outline: "none",
21
+ cursor: "pointer",
22
+ transition: "all 0.2s",
23
+ }),
24
+ },
25
+ dark: {
26
+ tabBtn: (active: boolean, left: boolean) => ({
27
+ flex: 1,
28
+ background: active ? "#23262F" : "#181A20",
29
+ color: active ? "#00E8C6" : "#fff",
30
+ border: "1px solid #444C5C",
31
+ borderRadius: left ? "8px 0 0 8px" : "0 8px 8px 0",
32
+ fontWeight: active ? 600 : 400,
33
+ fontSize: size === Size.SMALL ? 12 : 16,
34
+ height: size === Size.SMALL ? 32 : 48,
35
+ boxShadow: active ? "0 2px 8px 0 rgba(20,20,20,0.10)" : "none",
36
+ outline: "none",
37
+ cursor: "pointer",
38
+ transition: "all 0.2s",
39
+ }),
40
+ },
41
+ };
42
+ }
43
+
44
+ export function getRechargeTheme() {
45
+ const theme = getInitParams<Theme>("theme");
46
+ const themeConfig = getInitParams<ThemeConfig>("themeConfig");
47
+ const whiteTheme = theme === Theme.WHITE;
48
+
49
+ // 每次调用时重新生成主题配置
50
+ const rechargeThemes = createRechargeThemes();
51
+ const baseTheme = whiteTheme ? rechargeThemes.white : rechargeThemes.dark;
52
+
53
+ // 如果初始化时传入了主题配置,则覆盖默认配置
54
+ if (themeConfig) {
55
+ const config = whiteTheme ? themeConfig.white : themeConfig.dark;
56
+ if (config?.color) {
57
+ return {
58
+ ...baseTheme,
59
+ tabBtn: (active: boolean, left: boolean) => ({
60
+ ...baseTheme.tabBtn(active, left),
61
+ color: active ? config.color : baseTheme.tabBtn(active, left).color,
62
+ }),
63
+ };
64
+ }
65
+ }
66
+
67
+ return baseTheme;
68
+ }
@@ -0,0 +1,37 @@
1
+ import { useState } from "preact/hooks";
2
+ import { Button } from "@/components/common/button";
3
+ import { Recharge } from "./components/recharge";
4
+ import register from "preact-custom-element";
5
+ import { createOnlineRecharge } from "@/api";
6
+ import { t } from "@/local";
7
+
8
+ export function BestUnit() {
9
+ const [visible, setVisible] = useState(false);
10
+ const handleSubmit = async (form: {
11
+ amount: string;
12
+ rechargeChannel: string;
13
+ currency: string;
14
+ }) => {
15
+ const result = await createOnlineRecharge({
16
+ amount: form.amount,
17
+ currency: form.currency,
18
+ rechargeChannel: form.rechargeChannel,
19
+ });
20
+ window.open(result, "_blank");
21
+ };
22
+
23
+ return (
24
+ <div>
25
+ <Button onClick={() => setVisible(true)}>{t("充值/转账")}</Button>
26
+ <Recharge
27
+ visible={visible}
28
+ onClose={() => setVisible(false)}
29
+ onSubmit={handleSubmit}
30
+ />
31
+ </div>
32
+ );
33
+ }
34
+
35
+ register(BestUnit, "best-recharge", ["theme"], { shadow: false });
36
+
37
+ export default BestUnit;