best-unit 0.0.8 → 0.0.9

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "best-unit",
3
3
  "private": false,
4
- "version": "0.0.8",
4
+ "version": "0.0.9",
5
5
  "type": "module",
6
6
  "main": "dist/best-unit.cjs",
7
7
  "module": "dist/best-unit.js",
@@ -1,4 +1,4 @@
1
- import type { ComponentChildren } from 'preact';
1
+ import type { ComponentChildren } from "preact";
2
2
 
3
3
  interface ButtonProps {
4
4
  onClick: () => void;
@@ -10,12 +10,12 @@ export function ThemedButton({ onClick, color, children }: ButtonProps) {
10
10
  return (
11
11
  <button
12
12
  style={{
13
- background: color || '#1890ff',
14
- color: '#fff',
15
- border: 'none',
13
+ background: color || "#1890ff",
14
+ color: "#fff",
15
+ border: "none",
16
16
  borderRadius: 4,
17
- padding: '8px 16px',
18
- cursor: 'pointer',
17
+ padding: "8px 16px",
18
+ cursor: "pointer",
19
19
  fontSize: 16,
20
20
  }}
21
21
  onClick={onClick}
@@ -23,4 +23,4 @@ export function ThemedButton({ onClick, color, children }: ButtonProps) {
23
23
  {children}
24
24
  </button>
25
25
  );
26
- }
26
+ }
@@ -0,0 +1,302 @@
1
+ import type { FunctionalComponent } from "preact";
2
+ import { useRef } from "preact/hooks";
3
+
4
+ interface OfflineTransferFormProps {
5
+ formState: {
6
+ platform: string;
7
+ transactionId: string;
8
+ files: File[];
9
+ platformError?: string;
10
+ transactionIdError?: string;
11
+ filesError?: string;
12
+ loading?: boolean;
13
+ };
14
+ setFormState: (fn: (state: any) => any) => void;
15
+ onClose: () => void;
16
+ loading: boolean;
17
+ }
18
+
19
+ export const OfflineTransferForm: FunctionalComponent<
20
+ OfflineTransferFormProps
21
+ > = ({ formState, setFormState, onClose, loading }) => {
22
+ const fileInputRef = useRef<HTMLInputElement>(null);
23
+
24
+ const handleFileChange = (e: any) => {
25
+ const files = Array.from(e.target.files).slice(0, 10);
26
+ setFormState((state: any) => ({ ...state, files, filesError: "" }));
27
+ };
28
+
29
+ const handleDrop = (e: any) => {
30
+ e.preventDefault();
31
+ const files = Array.from(e.dataTransfer.files).slice(0, 10);
32
+ setFormState((state: any) => ({ ...state, files, filesError: "" }));
33
+ };
34
+
35
+ const handleDragOver = (e: any) => {
36
+ e.preventDefault();
37
+ };
38
+
39
+ const handleRemoveFile = (idx: number) => {
40
+ setFormState((state: any) => ({
41
+ ...state,
42
+ files: state.files.filter((_: File, i: number) => i !== idx),
43
+ }));
44
+ };
45
+
46
+ const handleSubmit = (e: any) => {
47
+ e.preventDefault();
48
+ let valid = true;
49
+ setFormState((state: any) => ({
50
+ ...state,
51
+ platformError: "",
52
+ transactionIdError: "",
53
+ filesError: "",
54
+ }));
55
+ if (!formState.platform) {
56
+ setFormState((state: any) => ({
57
+ ...state,
58
+ platformError: "请选择支付平台",
59
+ }));
60
+ valid = false;
61
+ }
62
+ if (!formState.transactionId.trim()) {
63
+ setFormState((state: any) => ({
64
+ ...state,
65
+ transactionIdError: "请输入转账交易ID",
66
+ }));
67
+ valid = false;
68
+ }
69
+ if (!formState.files || formState.files.length === 0) {
70
+ setFormState((state: any) => ({
71
+ ...state,
72
+ filesError: "请上传转账凭证",
73
+ }));
74
+ valid = false;
75
+ }
76
+ if (!valid) return;
77
+ // 打印表单值
78
+ console.log("OfflineTransferForm values:", formState);
79
+ };
80
+
81
+ // 文件大小格式化
82
+ const formatSize = (size: number) => {
83
+ if (size > 1024 * 1024) return (size / 1024 / 1024).toFixed(2) + " MB";
84
+ if (size > 1024) return (size / 1024).toFixed(2) + " KB";
85
+ return size + " B";
86
+ };
87
+
88
+ return (
89
+ <form onSubmit={handleSubmit}>
90
+ <div style={{ marginBottom: 18 }}>
91
+ <div style={{ marginBottom: 8, fontSize: 14, color: "#222" }}>
92
+ <span style={{ color: "#F53F3F" }}>*</span> 第三方支付平台
93
+ </div>
94
+ <select
95
+ style={{
96
+ width: "100%",
97
+ padding: "10px 12px",
98
+ borderRadius: 6,
99
+ border: formState.platformError
100
+ ? "1px solid #ff4d4f"
101
+ : "1px solid #E5E6EB",
102
+ background: "#fff",
103
+ color: "#222",
104
+ fontSize: 15,
105
+ outline: "none",
106
+ }}
107
+ value={formState.platform}
108
+ onInput={(e) => {
109
+ const value = (e.target as HTMLSelectElement).value;
110
+ setFormState((state: any) => ({
111
+ ...state,
112
+ platform: value,
113
+ platformError: value ? "" : state.platformError,
114
+ }));
115
+ }}
116
+ >
117
+ <option value="paypal">PayPal</option>
118
+ <option value="alipay">支付宝</option>
119
+ <option value="wechat">微信</option>
120
+ </select>
121
+ {formState.platformError && (
122
+ <div style={{ color: "#ff4d4f", fontSize: 13, marginTop: 4 }}>
123
+ {formState.platformError}
124
+ </div>
125
+ )}
126
+ </div>
127
+ <div style={{ marginBottom: 18 }}>
128
+ <div style={{ marginBottom: 8, fontSize: 14, color: "#222" }}>
129
+ <span style={{ color: "#F53F3F" }}>*</span> 交易ID
130
+ </div>
131
+ <input
132
+ type="text"
133
+ placeholder="请输入转账交易ID"
134
+ value={formState.transactionId}
135
+ onInput={(e) => {
136
+ const value = (e.target as HTMLInputElement).value;
137
+ setFormState((state: any) => ({
138
+ ...state,
139
+ transactionId: value,
140
+ transactionIdError: value ? "" : state.transactionIdError,
141
+ }));
142
+ }}
143
+ style={{
144
+ width: "100%",
145
+ padding: "10px 12px",
146
+ borderRadius: 6,
147
+ boxSizing: "border-box",
148
+ border: formState.transactionIdError
149
+ ? "1px solid #ff4d4f"
150
+ : "1px solid #E5E6EB",
151
+ background: "#fff",
152
+ color: "#222",
153
+ fontSize: 15,
154
+ outline: "none",
155
+ marginBottom: 0,
156
+ }}
157
+ />
158
+ {formState.transactionIdError && (
159
+ <div style={{ color: "#ff4d4f", fontSize: 13, marginTop: 4 }}>
160
+ {formState.transactionIdError}
161
+ </div>
162
+ )}
163
+ </div>
164
+ <div style={{ marginBottom: 24 }}>
165
+ <div style={{ marginBottom: 8, fontSize: 14, color: "#222" }}>
166
+ <span style={{ color: "#F53F3F" }}>*</span> 上传文件
167
+ </div>
168
+ <div
169
+ style={{
170
+ border: formState.filesError
171
+ ? "1px dashed #ff4d4f"
172
+ : "1px dashed #E5E6EB",
173
+ borderRadius: 8,
174
+ background: "#FCFCFD",
175
+ padding: 24,
176
+ textAlign: "center",
177
+ cursor: "pointer",
178
+ minHeight: 120,
179
+ display: "flex",
180
+ flexDirection: "column",
181
+ alignItems: "center",
182
+ justifyContent: "center",
183
+ color: "#999",
184
+ fontSize: 15,
185
+ }}
186
+ onClick={() => fileInputRef.current?.click()}
187
+ onDrop={handleDrop}
188
+ onDragOver={handleDragOver}
189
+ >
190
+ <div style={{ fontSize: 48, marginBottom: 12 }}>
191
+ {/* 优化后的上传图标 */}
192
+ <svg width="64" height="64" viewBox="0 0 64 64" fill="none">
193
+ <rect x="8" y="8" width="48" height="48" rx="12" fill="#F4F6FA" />
194
+ <rect x="28" y="20" width="8" height="24" rx="4" fill="#E5E6EB" />
195
+ </svg>
196
+ </div>
197
+ <div style={{ color: "#222", fontSize: 15, marginBottom: 4 }}>
198
+ 点击或拖拽文件到此处上传
199
+ </div>
200
+ <div style={{ color: "#999", fontSize: 13 }}>
201
+ 支持 JPG、PNG、PDF 格式,单个文件不超过 20MB,最多上传 10 个文件
202
+ </div>
203
+ <input
204
+ ref={fileInputRef}
205
+ type="file"
206
+ multiple
207
+ accept=".jpg,.jpeg,.png,.pdf"
208
+ style={{ display: "none" }}
209
+ onChange={handleFileChange}
210
+ />
211
+ </div>
212
+ {formState.files && formState.files.length > 0 && (
213
+ <div style={{ marginTop: 12 }}>
214
+ {formState.files.map((file, idx) => (
215
+ <div
216
+ key={idx}
217
+ style={{
218
+ display: "flex",
219
+ alignItems: "center",
220
+ background: "#F7F8FA",
221
+ borderRadius: 8,
222
+ padding: "12px 16px",
223
+ marginBottom: 8,
224
+ fontSize: 15,
225
+ color: "#222",
226
+ justifyContent: "space-between",
227
+ }}
228
+ >
229
+ <div style={{ display: "flex", alignItems: "center", flex: 1 }}>
230
+ <span style={{ fontWeight: 500 }}>{file.name}</span>
231
+ <span
232
+ style={{ color: "#8C8F93", fontSize: 13, marginLeft: 8 }}
233
+ >
234
+ ({formatSize(file.size)})
235
+ </span>
236
+ </div>
237
+ <button
238
+ type="button"
239
+ onClick={(e) => {
240
+ e.stopPropagation();
241
+ handleRemoveFile(idx);
242
+ }}
243
+ style={{
244
+ background: "#FF4D4F",
245
+ color: "#fff",
246
+ border: "none",
247
+ borderRadius: 6,
248
+ padding: "4px 14px",
249
+ fontSize: 15,
250
+ marginLeft: 16,
251
+ cursor: "pointer",
252
+ }}
253
+ >
254
+ 移除
255
+ </button>
256
+ </div>
257
+ ))}
258
+ </div>
259
+ )}
260
+ {formState.filesError && (
261
+ <div style={{ color: "#ff4d4f", fontSize: 13, marginTop: 4 }}>
262
+ {formState.filesError}
263
+ </div>
264
+ )}
265
+ </div>
266
+ {/* 按钮区 */}
267
+ <div style={{ display: "flex", justifyContent: "flex-end", gap: 12 }}>
268
+ <button
269
+ type="button"
270
+ onClick={onClose}
271
+ style={{
272
+ background: "#fff",
273
+ color: "#222",
274
+ border: "1px solid #E5E6EB",
275
+ borderRadius: 6,
276
+ padding: "8px 24px",
277
+ fontSize: 15,
278
+ cursor: "pointer",
279
+ }}
280
+ >
281
+ 取消
282
+ </button>
283
+ <button
284
+ type="submit"
285
+ disabled={loading}
286
+ style={{
287
+ background: "#155EEF",
288
+ color: "#fff",
289
+ border: "none",
290
+ borderRadius: 6,
291
+ padding: "8px 24px",
292
+ fontSize: 15,
293
+ cursor: "pointer",
294
+ fontWeight: 600,
295
+ }}
296
+ >
297
+ 去支付
298
+ </button>
299
+ </div>
300
+ </form>
301
+ );
302
+ };
@@ -0,0 +1,227 @@
1
+ import type { FunctionalComponent } from "preact";
2
+
3
+ interface OnlineRechargeFormProps {
4
+ formState: {
5
+ amount: string;
6
+ rechargeChannel: string;
7
+ currency: string;
8
+ loading: boolean;
9
+ error: string;
10
+ amountError: string;
11
+ rechargeChannelError: string;
12
+ currencyError: string;
13
+ };
14
+ setFormState: (fn: (state: any) => any) => void;
15
+ onClose: () => void;
16
+ loading: boolean;
17
+ whiteTheme?: boolean;
18
+ }
19
+
20
+ export const OnlineRechargeForm: FunctionalComponent<
21
+ OnlineRechargeFormProps
22
+ > = ({ formState, setFormState, onClose, loading, whiteTheme = false }) => {
23
+ // 白色主题样式
24
+ const inputStyle = whiteTheme
25
+ ? {
26
+ width: "100%",
27
+ padding: "10px 12px",
28
+ borderRadius: 6,
29
+ boxSizing: "border-box",
30
+ border: formState.amountError
31
+ ? "1px solid #ff4d4f"
32
+ : "1px solid #E5E6EB",
33
+ background: "#fff",
34
+ color: "#222",
35
+ fontSize: 15,
36
+ outline: "none",
37
+ marginBottom: 0,
38
+ }
39
+ : {
40
+ width: "100%",
41
+ padding: "10px 12px",
42
+ borderRadius: 6,
43
+ boxSizing: "border-box",
44
+ border: formState.amountError
45
+ ? "1px solid #ff4d4f"
46
+ : "1px solid #23262F",
47
+ background: "#23262F",
48
+ color: "#fff",
49
+ fontSize: 15,
50
+ outline: "none",
51
+ marginBottom: 0,
52
+ };
53
+ const selectStyle = whiteTheme
54
+ ? {
55
+ width: "100%",
56
+ padding: "10px 12px",
57
+ borderRadius: 6,
58
+ border: formState.rechargeChannelError
59
+ ? "1px solid #ff4d4f"
60
+ : "1px solid #E5E6EB",
61
+ background: "#fff",
62
+ color: "#222",
63
+ fontSize: 15,
64
+ outline: "none",
65
+ }
66
+ : {
67
+ width: "100%",
68
+ padding: "10px 12px",
69
+ borderRadius: 6,
70
+ border: formState.rechargeChannelError
71
+ ? "1px solid #ff4d4f"
72
+ : "1px solid #23262F",
73
+ background: "#23262F",
74
+ color: "#fff",
75
+ fontSize: 15,
76
+ outline: "none",
77
+ };
78
+ const selectCurrencyStyle = whiteTheme
79
+ ? {
80
+ width: "100%",
81
+ padding: "10px 12px",
82
+ borderRadius: 6,
83
+ border: "1px solid #E5E6EB",
84
+ background: "#fff",
85
+ color: "#222",
86
+ fontSize: 15,
87
+ outline: "none",
88
+ }
89
+ : {
90
+ width: "100%",
91
+ padding: "10px 12px",
92
+ borderRadius: 6,
93
+ border: "1px solid #23262F",
94
+ background: "#23262F",
95
+ color: "#fff",
96
+ fontSize: 15,
97
+ outline: "none",
98
+ };
99
+ const labelStyle = whiteTheme
100
+ ? { marginBottom: 8, fontSize: 14, color: "#222" }
101
+ : { marginBottom: 8, fontSize: 14 };
102
+ const errorStyle = { color: "#ff4d4f", fontSize: 13, marginTop: 4 };
103
+ const buttonCancelStyle = whiteTheme
104
+ ? {
105
+ background: "#fff",
106
+ color: "#222",
107
+ border: "1px solid #E5E6EB",
108
+ borderRadius: 6,
109
+ padding: "8px 24px",
110
+ fontSize: 15,
111
+ cursor: "pointer",
112
+ }
113
+ : {
114
+ background: "#23262F",
115
+ color: "#fff",
116
+ border: "none",
117
+ borderRadius: 6,
118
+ padding: "8px 24px",
119
+ fontSize: 15,
120
+ cursor: "pointer",
121
+ };
122
+ const buttonSubmitStyle = whiteTheme
123
+ ? {
124
+ background: "#155EEF",
125
+ color: "#fff",
126
+ border: "none",
127
+ borderRadius: 6,
128
+ padding: "8px 24px",
129
+ fontSize: 15,
130
+ cursor: "pointer",
131
+ fontWeight: 600,
132
+ }
133
+ : {
134
+ background: "#00E8C6",
135
+ color: "#fff",
136
+ border: "none",
137
+ borderRadius: 6,
138
+ padding: "8px 24px",
139
+ fontSize: 15,
140
+ cursor: "pointer",
141
+ fontWeight: 600,
142
+ };
143
+
144
+ return (
145
+ <>
146
+ <div style={{ marginBottom: 18 }}>
147
+ <div style={labelStyle}>
148
+ <span style={{ color: "#F53F3F" }}>*</span> 充值币种
149
+ </div>
150
+ <select
151
+ style={selectCurrencyStyle}
152
+ value={formState.currency}
153
+ onInput={(e) => {
154
+ const value = (e.target as HTMLSelectElement).value;
155
+ setFormState((state: any) => ({
156
+ ...state,
157
+ currency: value,
158
+ }));
159
+ }}
160
+ >
161
+ <option value="USD">USD - 美元</option>
162
+ <option value="CNY">CNY - 人民币</option>
163
+ <option value="EUR">EUR - 欧元</option>
164
+ </select>
165
+ </div>
166
+ <div style={{ marginBottom: 18 }}>
167
+ <div style={labelStyle}>
168
+ <span style={{ color: "#F53F3F" }}>*</span> 充值金额
169
+ </div>
170
+ <input
171
+ type="text"
172
+ placeholder="请输入充值金额"
173
+ value={formState.amount}
174
+ onInput={(e) => {
175
+ const value = (e.target as HTMLInputElement).value;
176
+ setFormState((state: any) => ({
177
+ ...state,
178
+ amount: value,
179
+ amountError: value.trim() ? "" : state.amountError,
180
+ }));
181
+ }}
182
+ style={inputStyle}
183
+ />
184
+ {formState.amountError && (
185
+ <div style={errorStyle}>{formState.amountError}</div>
186
+ )}
187
+ </div>
188
+ <div style={{ marginBottom: 24 }}>
189
+ <div style={labelStyle}>
190
+ <span style={{ color: "#F53F3F" }}>*</span> 支付平台
191
+ </div>
192
+ <select
193
+ style={selectStyle}
194
+ value={formState.rechargeChannel}
195
+ onInput={(e) => {
196
+ const value = (e.target as HTMLSelectElement).value;
197
+ setFormState((state: any) => ({
198
+ ...state,
199
+ rechargeChannel: value,
200
+ rechargeChannelError: value ? "" : state.rechargeChannelError,
201
+ }));
202
+ }}
203
+ >
204
+ <option value="alipay">支付宝</option>
205
+ <option value="wechat">微信</option>
206
+ <option value="paypal">PayPal</option>
207
+ </select>
208
+ {formState.rechargeChannelError && (
209
+ <div style={errorStyle}>{formState.rechargeChannelError}</div>
210
+ )}
211
+ </div>
212
+ {formState.error && (
213
+ <div style={{ color: "#ff4d4f", marginBottom: 12 }}>
214
+ {formState.error}
215
+ </div>
216
+ )}
217
+ <div style={{ display: "flex", justifyContent: "flex-end", gap: 12 }}>
218
+ <button type="button" onClick={onClose} style={buttonCancelStyle}>
219
+ 取消
220
+ </button>
221
+ <button type="submit" disabled={loading} style={buttonSubmitStyle}>
222
+ {loading ? "提交中..." : "去支付"}
223
+ </button>
224
+ </div>
225
+ </>
226
+ );
227
+ };