best-unit 1.0.0 → 1.0.3

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 (48) hide show
  1. package/README.md +1 -0
  2. package/dist/best-unit.cjs +60 -0
  3. package/dist/best-unit.js +3988 -0
  4. package/dist/types/global.d.ts +64 -0
  5. package/dist/types/index.ts +17 -0
  6. package/dist/types/preact-custom-element.d.ts +1 -0
  7. package/index.html +2 -2
  8. package/package.json +14 -7
  9. package/src/api/axiosInstance.ts +94 -0
  10. package/src/api/index.ts +118 -15
  11. package/src/api/proxy.ts +11 -0
  12. package/src/components/business/recharge-sdk/components/Button.tsx +42 -0
  13. package/src/components/business/recharge-sdk/components/OfflineTransferForm.tsx +365 -0
  14. package/src/components/business/recharge-sdk/components/OnlineRechargeForm.tsx +389 -0
  15. package/src/components/business/recharge-sdk/components/Recharge.tsx +288 -0
  16. package/src/components/business/recharge-sdk/index.tsx +39 -0
  17. package/src/components/business/statistical-balance/index.tsx +206 -0
  18. package/src/components/common/HoverPopover.tsx +215 -0
  19. package/src/components/common/Message.tsx +324 -0
  20. package/src/components/common/Select.tsx +278 -0
  21. package/src/components/common/Upload.tsx +207 -0
  22. package/src/demo/App.tsx +429 -0
  23. package/src/demo/index.tsx +4 -0
  24. package/src/local/en.ts +61 -0
  25. package/src/local/index.ts +36 -0
  26. package/src/local/zh.ts +60 -0
  27. package/src/main.ts +10 -0
  28. package/src/types/global.d.ts +64 -0
  29. package/src/types/index.ts +17 -0
  30. package/src/types/preact-custom-element.d.ts +1 -0
  31. package/src/utils/business/index.ts +48 -0
  32. package/src/utils/common/index.ts +8 -0
  33. package/src/vite-env.d.ts +1 -0
  34. package/tsconfig.app.json +31 -0
  35. package/tsconfig.json +15 -0
  36. package/tsconfig.node.json +24 -0
  37. package/vite.config.ts +18 -0
  38. package/example/index.html +0 -24
  39. package/public/vite.svg +0 -1
  40. package/src/App.jsx +0 -6
  41. package/src/api/define-api.ts +0 -49
  42. package/src/javascript.svg +0 -1
  43. package/src/main.jsx +0 -4
  44. package/src/sdk/index.ts +0 -33
  45. package/src/style.css +0 -96
  46. package/src/views/AccountModal.jsx +0 -125
  47. package/src/views/Home.jsx +0 -53
  48. package/vite.config.js +0 -31
@@ -0,0 +1,324 @@
1
+ import type { FunctionalComponent } from "preact";
2
+ import { useState, useEffect } from "preact/hooks";
3
+
4
+ export type MessageType = "success" | "error" | "warning" | "info";
5
+
6
+ interface MessageProps {
7
+ type: MessageType;
8
+ content: string;
9
+ duration?: number;
10
+ onClose?: () => void;
11
+ closable?: boolean;
12
+ }
13
+
14
+ const MessageItem: FunctionalComponent<MessageProps> = ({
15
+ type,
16
+ content,
17
+ duration = 3000,
18
+ onClose,
19
+ closable = false,
20
+ }) => {
21
+ const [visible, setVisible] = useState(true);
22
+
23
+ useEffect(() => {
24
+ if (duration > 0) {
25
+ const timer = setTimeout(() => {
26
+ setVisible(false);
27
+ setTimeout(() => onClose?.(), 300);
28
+ }, duration);
29
+ return () => clearTimeout(timer);
30
+ }
31
+ }, [duration, onClose]);
32
+
33
+ const getIcon = () => {
34
+ switch (type) {
35
+ case "error":
36
+ return (
37
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
38
+ <circle cx="8" cy="8" r="8" fill="#ff4d4f" />
39
+ <path
40
+ d="M10.5 5.5l-5 5m0-5l5 5"
41
+ stroke="white"
42
+ strokeWidth="1.5"
43
+ strokeLinecap="round"
44
+ />
45
+ </svg>
46
+ );
47
+ case "success":
48
+ return (
49
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
50
+ <circle cx="8" cy="8" r="8" fill="#52c41a" />
51
+ <path
52
+ d="M5 8.5l2.2 2.2 3.8-3.4"
53
+ stroke="white"
54
+ strokeWidth="1.8"
55
+ strokeLinecap="round"
56
+ strokeLinejoin="round"
57
+ fill="none"
58
+ />
59
+ </svg>
60
+ );
61
+ case "warning":
62
+ return (
63
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
64
+ <circle cx="8" cy="8" r="8" fill="#faad14" />
65
+ <path
66
+ d="M8 4.5v5"
67
+ stroke="white"
68
+ strokeWidth="1.5"
69
+ strokeLinecap="round"
70
+ />
71
+ <circle cx="8" cy="12.5" r="1" fill="white" />
72
+ </svg>
73
+ );
74
+ case "info":
75
+ return (
76
+ <svg width="16" height="16" viewBox="0 0 16 16" fill="none">
77
+ <circle cx="8" cy="8" r="8" fill="#1677ff" />
78
+ <rect
79
+ x="7.25"
80
+ y="7"
81
+ width="1.5"
82
+ height="6"
83
+ rx="0.75"
84
+ fill="white"
85
+ />
86
+ <rect
87
+ x="7.25"
88
+ y="5"
89
+ width="1.5"
90
+ height="1.5"
91
+ rx="0.75"
92
+ fill="white"
93
+ />
94
+ </svg>
95
+ );
96
+ default:
97
+ return null;
98
+ }
99
+ };
100
+
101
+ const getBackgroundColor = () => {
102
+ return "#fff"; // 统一使用白色背景
103
+ };
104
+
105
+ const getBorderColor = () => {
106
+ return "#d9d9d9"; // 统一使用灰色边框
107
+ };
108
+
109
+ return (
110
+ <div
111
+ style={{
112
+ position: "fixed",
113
+ top: "20px",
114
+ left: "50%",
115
+ zIndex: 9999,
116
+ background: getBackgroundColor(),
117
+ border: `1px solid ${getBorderColor()}`,
118
+ borderRadius: "12px", // 更圆润的圆角
119
+ padding: "8px 16px", // 减小上下padding
120
+ display: "flex",
121
+ alignItems: "center",
122
+ gap: "8px",
123
+ boxShadow: "0 6px 16px rgba(0, 0, 0, 0.12)", // 增强阴影效果
124
+ minWidth: content.length < 20 ? "auto" : "300px", // 短消息自适应宽度
125
+ maxWidth: "500px",
126
+ opacity: visible ? 1 : 0,
127
+ transform: visible
128
+ ? "translateX(-50%) translateY(0)"
129
+ : "translateX(-50%) translateY(-20px)",
130
+ transition: "all 0.3s ease",
131
+ }}
132
+ >
133
+ <div style={{ flexShrink: 0 }}>{getIcon()}</div>
134
+ <div
135
+ style={{
136
+ color: "#222",
137
+ fontSize: "14px",
138
+ lineHeight: "1.5",
139
+ flex: 1,
140
+ }}
141
+ >
142
+ {content}
143
+ </div>
144
+ {closable && (
145
+ <button
146
+ onClick={() => {
147
+ setVisible(false);
148
+ setTimeout(() => onClose?.(), 300);
149
+ }}
150
+ style={{
151
+ background: "none",
152
+ border: "none",
153
+ cursor: "pointer",
154
+ padding: "4px",
155
+ color: "#999",
156
+ fontSize: "12px",
157
+ flexShrink: 0,
158
+ }}
159
+ >
160
+
161
+ </button>
162
+ )}
163
+ </div>
164
+ );
165
+ };
166
+
167
+ // 支持对象参数
168
+ interface MessageOptions {
169
+ content: string;
170
+ duration?: number;
171
+ closable?: boolean;
172
+ }
173
+
174
+ type MessageArg = string | MessageOptions;
175
+
176
+ // 简单的消息管理器
177
+ class MessageManager {
178
+ private parseArg(arg: MessageArg): MessageOptions {
179
+ if (typeof arg === "string") {
180
+ return { content: arg };
181
+ }
182
+ return arg;
183
+ }
184
+
185
+ show(type: MessageType, arg: MessageArg) {
186
+ const { content, duration, closable } = this.parseArg(arg);
187
+ // 创建消息元素
188
+ const messageDiv = document.createElement("div");
189
+ messageDiv.style.cssText = `
190
+ position: fixed;
191
+ top: 20px;
192
+ left: 50%;
193
+ transform: translateX(-50%);
194
+ z-index: 9999;
195
+ background: #fff;
196
+ border: 1px solid #fff;
197
+ border-radius: 12px;
198
+ padding: 8px 16px;
199
+ box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
200
+ min-width: ${content.length < 20 ? "auto" : "300px"};
201
+ max-width: 500px;
202
+ display: flex;
203
+ align-items: center;
204
+ gap: 8px;
205
+ font-size: 14px;
206
+ color: #222;
207
+ `;
208
+
209
+ // 添加图标
210
+ if (type === "error") {
211
+ const iconSvg = document.createElementNS(
212
+ "http://www.w3.org/2000/svg",
213
+ "svg"
214
+ );
215
+ iconSvg.setAttribute("width", "16");
216
+ iconSvg.setAttribute("height", "16");
217
+ iconSvg.setAttribute("viewBox", "0 0 16 16");
218
+ iconSvg.innerHTML = `
219
+ <circle cx="8" cy="8" r="8" fill="#ff4d4f"/>
220
+ <path d="M10.5 5.5l-5 5m0-5l5 5" stroke="white" stroke-width="1.5" stroke-linecap="round"/>
221
+ `;
222
+ messageDiv.appendChild(iconSvg);
223
+ }
224
+ if (type === "success") {
225
+ const iconSvg = document.createElementNS(
226
+ "http://www.w3.org/2000/svg",
227
+ "svg"
228
+ );
229
+ iconSvg.setAttribute("width", "16");
230
+ iconSvg.setAttribute("height", "16");
231
+ iconSvg.setAttribute("viewBox", "0 0 16 16");
232
+ iconSvg.innerHTML = `
233
+ <circle cx="8" cy="8" r="8" fill="#52c41a"/>
234
+ <path d="M5 8.5l2.2 2.2 3.8-3.4" stroke="white" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" fill="none"/>
235
+ `;
236
+ messageDiv.appendChild(iconSvg);
237
+ }
238
+ if (type === "warning") {
239
+ const iconSvg = document.createElementNS(
240
+ "http://www.w3.org/2000/svg",
241
+ "svg"
242
+ );
243
+ iconSvg.setAttribute("width", "16");
244
+ iconSvg.setAttribute("height", "16");
245
+ iconSvg.setAttribute("viewBox", "0 0 16 16");
246
+ iconSvg.innerHTML = `
247
+ <circle cx="8" cy="8" r="8" fill="#faad14"/>
248
+ <path d="M8 4.5v5" stroke="white" stroke-width="1.5" stroke-linecap="round"/>
249
+ <circle cx="8" cy="12.5" r="1" fill="white"/>
250
+ `;
251
+ messageDiv.appendChild(iconSvg);
252
+ }
253
+ if (type === "info") {
254
+ const iconSvg = document.createElementNS(
255
+ "http://www.w3.org/2000/svg",
256
+ "svg"
257
+ );
258
+ iconSvg.setAttribute("width", "16");
259
+ iconSvg.setAttribute("height", "16");
260
+ iconSvg.setAttribute("viewBox", "0 0 16 16");
261
+ iconSvg.innerHTML = `
262
+ <circle cx="8" cy="8" r="8" fill="#1677ff"/>
263
+ <rect x="7.25" y="7" width="1.5" height="6" rx="0.75" fill="white"/>
264
+ <rect x="7.25" y="5" width="1.5" height="1.5" rx="0.75" fill="white"/>
265
+ `;
266
+ messageDiv.appendChild(iconSvg);
267
+ }
268
+
269
+ // 添加文本
270
+ const textDiv = document.createElement("div");
271
+ textDiv.textContent = content;
272
+ textDiv.style.flex = "1";
273
+ messageDiv.appendChild(textDiv);
274
+
275
+ // 添加关闭按钮(如果启用)
276
+ if (closable) {
277
+ const closeBtn = document.createElement("button");
278
+ closeBtn.textContent = "✕";
279
+ closeBtn.style.cssText = `
280
+ background: none;
281
+ border: none;
282
+ cursor: pointer;
283
+ padding: 4px;
284
+ color: #999;
285
+ font-size: 12px;
286
+ `;
287
+ closeBtn.onclick = () => {
288
+ document.body.removeChild(messageDiv);
289
+ };
290
+ messageDiv.appendChild(closeBtn);
291
+ }
292
+
293
+ // 添加到页面
294
+ document.body.appendChild(messageDiv);
295
+
296
+ // 自动关闭
297
+ if (duration !== 0) {
298
+ setTimeout(() => {
299
+ if (document.body.contains(messageDiv)) {
300
+ document.body.removeChild(messageDiv);
301
+ }
302
+ }, duration || 3000);
303
+ }
304
+ }
305
+
306
+ success(arg: MessageArg) {
307
+ this.show("success", arg);
308
+ }
309
+
310
+ error(arg: MessageArg) {
311
+ this.show("error", arg);
312
+ }
313
+
314
+ warning(arg: MessageArg) {
315
+ this.show("warning", arg);
316
+ }
317
+
318
+ info(arg: MessageArg) {
319
+ this.show("info", arg);
320
+ }
321
+ }
322
+
323
+ export const message = new MessageManager();
324
+ export default MessageItem;
@@ -0,0 +1,278 @@
1
+ import type { FunctionalComponent, JSX } from "preact";
2
+ import { useState, useRef, useEffect } from "preact/hooks";
3
+ import { Theme } from "../../types";
4
+ import { t } from "../../local";
5
+
6
+ interface Option {
7
+ value: string;
8
+ label: string;
9
+ disabled?: boolean;
10
+ }
11
+
12
+ interface SelectProps {
13
+ value?: string;
14
+ onChange?: (value: string) => void;
15
+ options: Option[];
16
+ placeholder?: string;
17
+ style?: any;
18
+ error?: boolean;
19
+ disabled?: boolean;
20
+ dropdownStyle?: any;
21
+ className?: string;
22
+ dropdownClassName?: string;
23
+ children?: JSX.Element;
24
+ }
25
+
26
+ export const Select: FunctionalComponent<SelectProps> = ({
27
+ value,
28
+ onChange,
29
+ options,
30
+ placeholder,
31
+ style,
32
+ error,
33
+ disabled,
34
+ dropdownStyle,
35
+ className,
36
+ dropdownClassName,
37
+ children,
38
+ }) => {
39
+ const [open, setOpen] = useState(false);
40
+ const ref = useRef<HTMLDivElement>(null);
41
+ const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
42
+
43
+ // 主题切换
44
+ const fundUnitParams = JSON.parse(
45
+ sessionStorage.getItem("fund_unit_params") || "{}"
46
+ );
47
+ const whiteTheme = fundUnitParams.theme === Theme.WHITE;
48
+ // hover灰色 #f5f5f5,选中蓝色 #e6f7ff
49
+ const selectTheme = whiteTheme
50
+ ? {
51
+ trigger: {
52
+ background: disabled ? "#f5f5f5" : "#fff",
53
+ color: disabled ? "#bfbfbf" : value ? "#222" : "#bfbfbf",
54
+ border: error
55
+ ? "1px solid #ff4d4f"
56
+ : disabled
57
+ ? "1px solid #f0f0f0"
58
+ : "1px solid #d9d9d9",
59
+ },
60
+ dropdown: {
61
+ background: "#fff",
62
+ border: "1px solid #d9d9d9",
63
+ color: "#222",
64
+ },
65
+ option: (active: boolean, optDisabled: boolean, hovered: boolean) => ({
66
+ color: optDisabled ? "#bfbfbf" : active ? "#1890ff" : "#222",
67
+ background: active
68
+ ? "#e6f7ff"
69
+ : hovered && !optDisabled
70
+ ? "#f5f5f5"
71
+ : optDisabled
72
+ ? "#f5f5f5"
73
+ : "#fff",
74
+ }),
75
+ placeholder: { color: "#bfbfbf" },
76
+ }
77
+ : {
78
+ trigger: {
79
+ background: disabled ? "#23262F" : "#23262F",
80
+ color: disabled ? "#666" : value ? "#fff" : "#666",
81
+ border: error
82
+ ? "1px solid #ff4d4f"
83
+ : disabled
84
+ ? "1px solid #23262F"
85
+ : "1px solid #23262F",
86
+ },
87
+ dropdown: {
88
+ background: "#23262F",
89
+ border: "1px solid #23262F",
90
+ color: "#fff",
91
+ },
92
+ option: (active: boolean, optDisabled: boolean, hovered: boolean) => ({
93
+ color: optDisabled ? "#666" : active ? "#00E8C6" : "#fff",
94
+ background: active
95
+ ? "#23262F"
96
+ : hovered && !optDisabled
97
+ ? "#333843"
98
+ : optDisabled
99
+ ? "#23262F"
100
+ : "#23262F",
101
+ }),
102
+ placeholder: { color: "#666" },
103
+ };
104
+
105
+ useEffect(() => {
106
+ if (!open) return;
107
+ const handleClick = (e: MouseEvent) => {
108
+ if (ref.current && !ref.current.contains(e.target as Node)) {
109
+ setOpen(false);
110
+ }
111
+ };
112
+ document.addEventListener("mousedown", handleClick);
113
+ return () => document.removeEventListener("mousedown", handleClick);
114
+ }, [open]);
115
+
116
+ const selected = options.find((opt) => opt.value === value);
117
+
118
+ return (
119
+ <div
120
+ ref={ref}
121
+ className={className}
122
+ style={{ position: "relative", ...style }}
123
+ >
124
+ <style>{`
125
+ .custom-select-dropdown {
126
+ scrollbar-width: thin;
127
+ scrollbar-color: #bfbfbf #f5f5f5;
128
+ }
129
+ .custom-select-dropdown::-webkit-scrollbar {
130
+ width: 8px;
131
+ }
132
+ .custom-select-dropdown::-webkit-scrollbar-thumb {
133
+ border-radius: 4px;
134
+ background: ${whiteTheme ? "#e5e5e5" : "#444C5C"};
135
+ }
136
+ .custom-select-dropdown::-webkit-scrollbar-track {
137
+ background: ${whiteTheme ? "#f5f5f5" : "#23262F"};
138
+ }
139
+ `}</style>
140
+ <div
141
+ onClick={() => !disabled && setOpen((o) => !o)}
142
+ style={{
143
+ minHeight: 40,
144
+ borderRadius: 6,
145
+ display: "flex",
146
+ alignItems: "center",
147
+ padding: "0 12px",
148
+ fontSize: 15,
149
+ cursor: disabled ? "not-allowed" : "pointer",
150
+ boxSizing: "border-box",
151
+ transition: "border 0.2s",
152
+ opacity: disabled ? 0.6 : 1,
153
+ width: "100%",
154
+ ...selectTheme.trigger,
155
+ ...style,
156
+ }}
157
+ tabIndex={0}
158
+ >
159
+ <span style={{ flex: 1 }}>
160
+ {selected ? (
161
+ selected.label
162
+ ) : (
163
+ <span style={selectTheme.placeholder}>
164
+ {placeholder || "请选择"}
165
+ </span>
166
+ )}
167
+ </span>
168
+ <span style={{ marginLeft: 8, display: "flex", alignItems: "center" }}>
169
+ <svg
170
+ width="20"
171
+ height="20"
172
+ viewBox="0 0 20 20"
173
+ fill="none"
174
+ xmlns="http://www.w3.org/2000/svg"
175
+ >
176
+ <path
177
+ d="M6 8L10 12L14 8"
178
+ stroke="#bfbfbf"
179
+ stroke-width="1.5"
180
+ stroke-linecap="round"
181
+ stroke-linejoin="round"
182
+ />
183
+ </svg>
184
+ </span>
185
+ </div>
186
+ {open && !disabled && (
187
+ <div
188
+ className={`custom-select-dropdown${
189
+ dropdownClassName ? " " + dropdownClassName : ""
190
+ }`}
191
+ style={{
192
+ position: "absolute",
193
+ left: 0,
194
+ right: 0,
195
+ top: 44,
196
+ zIndex: 10,
197
+ borderRadius: 6,
198
+ boxShadow: whiteTheme
199
+ ? "0 2px 8px rgba(0,0,0,0.08)"
200
+ : "0 2px 8px rgba(0,0,0,0.32)",
201
+ maxHeight: 220,
202
+ overflowY: "auto",
203
+ ...selectTheme.dropdown,
204
+ ...dropdownStyle,
205
+ }}
206
+ >
207
+ {options.length === 0 && (
208
+ <div
209
+ style={{
210
+ display: "flex",
211
+ flexDirection: "column",
212
+ alignItems: "center",
213
+ justifyContent: "center",
214
+ minHeight: 120,
215
+ width: "100%",
216
+ userSelect: "none",
217
+ }}
218
+ >
219
+ <svg
220
+ width="64"
221
+ height="41"
222
+ viewBox="0 0 64 41"
223
+ xmlns="http://www.w3.org/2000/svg"
224
+ >
225
+ <title>{t("暂无数据")}</title>
226
+ <g transform="translate(0 1)" fill="none" fillRule="evenodd">
227
+ <ellipse
228
+ fill="#f5f5f5"
229
+ cx="32"
230
+ cy="33"
231
+ rx="32"
232
+ ry="7"
233
+ ></ellipse>
234
+ <g fillRule="nonzero" stroke="#d9d9d9">
235
+ <path d="M55 12.76L44.854 1.258C44.367.474 43.656 0 42.907 0H21.093c-.749 0-1.46.474-1.947 1.257L9 12.761V22h46v-9.24z"></path>
236
+ <path
237
+ d="M41.613 15.931c0-1.605.994-2.93 2.227-2.931H55v18.137C55 33.26 53.68 35 52.05 35h-40.1C10.32 35 9 33.259 9 31.137V13h11.16c1.233 0 2.227 1.323 2.227 2.928v.022c0 1.605 1.005 2.901 2.237 2.901h14.752c1.232 0 2.237-1.308 2.237-2.913v-.007z"
238
+ fill="#fafafa"
239
+ ></path>
240
+ </g>
241
+ </g>
242
+ </svg>
243
+ <div style={{ marginTop: 8, color: "#bfbfbf", fontSize: 15 }}>
244
+ {t("暂无数据")}
245
+ </div>
246
+ </div>
247
+ )}
248
+ {options.map((opt, idx) => (
249
+ <div
250
+ key={opt.value}
251
+ onClick={() => {
252
+ if (opt.disabled) return;
253
+ setOpen(false);
254
+ onChange?.(opt.value);
255
+ }}
256
+ onMouseEnter={() => setHoveredIndex(idx)}
257
+ onMouseLeave={() => setHoveredIndex(null)}
258
+ style={{
259
+ padding: "10px 16px",
260
+ cursor: opt.disabled ? "not-allowed" : "pointer",
261
+ fontWeight: value === opt.value ? 600 : 400,
262
+ opacity: opt.disabled ? 0.6 : 1,
263
+ ...selectTheme.option(
264
+ value === opt.value,
265
+ !!opt.disabled,
266
+ hoveredIndex === idx
267
+ ),
268
+ }}
269
+ >
270
+ {opt.label}
271
+ </div>
272
+ ))}
273
+ </div>
274
+ )}
275
+ {children}
276
+ </div>
277
+ );
278
+ };