best-unit 0.0.7 → 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.
@@ -0,0 +1,244 @@
1
+ import { useState, useEffect } from "preact/hooks";
2
+ import { OnlineRechargeForm } from "./OnlineRechargeForm";
3
+ import { OfflineTransferForm } from "./OfflineTransferForm";
4
+
5
+ interface ModalFormProps {
6
+ visible: boolean;
7
+ onClose: () => void;
8
+ onSubmit: (form: {
9
+ amount: string;
10
+ rechargeChannel: string;
11
+ currency: string;
12
+ }) => Promise<void>;
13
+ color?: string;
14
+ }
15
+
16
+ export function ModalForm({
17
+ visible,
18
+ onClose,
19
+ onSubmit,
20
+ color,
21
+ }: ModalFormProps) {
22
+ console.log(1111);
23
+ const [formState, setFormState] = useState({
24
+ amount: "",
25
+ rechargeChannel: "paypal",
26
+ currency: "USD",
27
+ loading: false,
28
+ error: "",
29
+ amountError: "",
30
+ rechargeChannelError: "",
31
+ currencyError: "",
32
+ });
33
+ const [activeTab, setActiveTab] = useState<"online" | "offline">("online");
34
+ const [offlineFormState, setOfflineFormState] = useState({
35
+ platform: "paypal",
36
+ transactionId: "",
37
+ files: [],
38
+ platformError: "",
39
+ transactionIdError: "",
40
+ filesError: "",
41
+ loading: false,
42
+ });
43
+
44
+ // 每次关闭弹窗时重置内容
45
+ useEffect(() => {
46
+ if (!visible) {
47
+ setActiveTab("online");
48
+ setFormState({
49
+ amount: "",
50
+ rechargeChannel: "paypal",
51
+ currency: "USD",
52
+ loading: false,
53
+ error: "",
54
+ amountError: "",
55
+ rechargeChannelError: "",
56
+ currencyError: "",
57
+ });
58
+ setOfflineFormState({
59
+ platform: "paypal",
60
+ transactionId: "",
61
+ files: [],
62
+ platformError: "",
63
+ transactionIdError: "",
64
+ filesError: "",
65
+ loading: false,
66
+ });
67
+ }
68
+ }, [visible]);
69
+
70
+ if (!visible) return null;
71
+
72
+ const handleSubmit = async (e: Event) => {
73
+ e.preventDefault();
74
+ let valid = true;
75
+ setFormState((state) => ({
76
+ ...state,
77
+ amountError: "",
78
+ rechargeChannelError: "",
79
+ }));
80
+ if (!formState.amount.trim()) {
81
+ setFormState((state) => ({ ...state, amountError: "请输入充值金额" }));
82
+ valid = false;
83
+ }
84
+ if (!formState.rechargeChannel) {
85
+ setFormState((state) => ({
86
+ ...state,
87
+ rechargeChannelError: "请选择支付平台",
88
+ }));
89
+ valid = false;
90
+ }
91
+ if (!valid) return;
92
+ setFormState((state) => ({ ...state, loading: true, error: "" }));
93
+ try {
94
+ await onSubmit({
95
+ amount: formState.amount,
96
+ rechargeChannel: formState.rechargeChannel,
97
+ currency: formState.currency,
98
+ });
99
+ onClose();
100
+ } catch {
101
+ setFormState((state) => ({ ...state, error: "提交失败,请重试" }));
102
+ } finally {
103
+ setFormState((state) => ({ ...state, loading: false }));
104
+ }
105
+ };
106
+
107
+ // 点击弹窗外部关闭
108
+ const handleMaskClick = (e: any) => {
109
+ if (e.target === e.currentTarget) {
110
+ onClose();
111
+ }
112
+ };
113
+
114
+ return (
115
+ <div
116
+ style={{
117
+ position: "fixed",
118
+ top: 0,
119
+ left: 0,
120
+ right: 0,
121
+ bottom: 0,
122
+ background: "rgba(0,0,0,0.3)", // 更浅的遮罩
123
+ display: "flex",
124
+ alignItems: "center",
125
+ justifyContent: "center",
126
+ zIndex: 9999,
127
+ }}
128
+ onClick={handleMaskClick}
129
+ >
130
+ <form
131
+ onSubmit={handleSubmit}
132
+ style={{
133
+ background: "#fff", // 白色弹窗
134
+ padding: 32,
135
+ borderRadius: 12,
136
+ minWidth: 400,
137
+ color: color || "#222", // 深色字体
138
+ boxShadow: "0 4px 24px rgba(0,0,0,0.08)",
139
+ position: "relative",
140
+ }}
141
+ onClick={(e) => e.stopPropagation()}
142
+ >
143
+ {/* 关闭按钮 */}
144
+ <button
145
+ type="button"
146
+ onClick={onClose}
147
+ style={{
148
+ position: "absolute",
149
+ right: 16,
150
+ top: 16,
151
+ background: "none",
152
+ border: "none",
153
+ color: "#222",
154
+ fontSize: 22,
155
+ cursor: "pointer",
156
+ lineHeight: 1,
157
+ }}
158
+ aria-label="关闭"
159
+ >
160
+ ×
161
+ </button>
162
+ <div
163
+ style={{
164
+ fontWeight: 600,
165
+ fontSize: 20,
166
+ marginBottom: 24,
167
+ textAlign: "left",
168
+ color: "#222",
169
+ }}
170
+ >
171
+ 充值 / 转账
172
+ </div>
173
+ {/* tab 按钮区域 */}
174
+ <div style={{ display: "flex", marginBottom: 28 }}>
175
+ <button
176
+ type="button"
177
+ onClick={() => setActiveTab("online")}
178
+ style={{
179
+ flex: 1,
180
+ background: activeTab === "online" ? "#fff" : "#F7F8FA",
181
+ color: activeTab === "online" ? "#155EEF" : "#222",
182
+ border: "none",
183
+ borderRadius: "8px 0 0 8px",
184
+ fontWeight: activeTab === "online" ? 600 : 400,
185
+ fontSize: 16,
186
+ height: 48,
187
+ boxShadow:
188
+ activeTab === "online"
189
+ ? "0 2px 8px 0 rgba(20,20,20,0.04)"
190
+ : "none",
191
+ outline: "none",
192
+ cursor: "pointer",
193
+ borderRight: "1px solid #F0F1F3",
194
+ transition: "all 0.2s",
195
+ }}
196
+ >
197
+ 在线充值
198
+ </button>
199
+ <button
200
+ type="button"
201
+ onClick={() => setActiveTab("offline")}
202
+ style={{
203
+ flex: 1,
204
+ background: activeTab === "offline" ? "#fff" : "#F7F8FA",
205
+ color: activeTab === "offline" ? "#155EEF" : "#222",
206
+ border: "none",
207
+ borderRadius: "0 8px 8px 0",
208
+ fontWeight: activeTab === "offline" ? 600 : 400,
209
+ fontSize: 16,
210
+ height: 48,
211
+ boxShadow:
212
+ activeTab === "offline"
213
+ ? "0 2px 8px 0 rgba(20,20,20,0.04)"
214
+ : "none",
215
+ outline: "none",
216
+ cursor: "pointer",
217
+ borderLeft: "1px solid #F0F1F3",
218
+ transition: "all 0.2s",
219
+ }}
220
+ >
221
+ 线下转账
222
+ </button>
223
+ </div>
224
+ {/* tab 内容区域 */}
225
+ {activeTab === "online" ? (
226
+ <OnlineRechargeForm
227
+ formState={formState}
228
+ setFormState={setFormState}
229
+ onClose={onClose}
230
+ loading={formState.loading}
231
+ whiteTheme={true}
232
+ />
233
+ ) : (
234
+ <OfflineTransferForm
235
+ formState={offlineFormState}
236
+ setFormState={setOfflineFormState}
237
+ onClose={onClose}
238
+ loading={offlineFormState.loading}
239
+ />
240
+ )}
241
+ </form>
242
+ </div>
243
+ );
244
+ }
@@ -1,14 +1,18 @@
1
- import { useState } from 'preact/hooks';
2
- import { ThemedButton } from './components/Button';
3
- import { ModalForm } from './components/ModalForm';
4
- import register from 'preact-custom-element';
1
+ import { useState } from "preact/hooks";
2
+ import { ThemedButton } from "./components/Button";
3
+ import { ModalForm } from "./components/recharge";
4
+ import register from "preact-custom-element";
5
5
 
6
6
  export function BestUnit(props: any) {
7
7
  const [visible, setVisible] = useState(false);
8
8
  const color = props.theme?.primaryColor;
9
9
 
10
- const handleSubmit = async (form: { name: string; email: string }) => {
11
- console.log('submit', form);
10
+ const handleSubmit = async (form: {
11
+ amount: string;
12
+ rechargeChannel: string;
13
+ currency: string;
14
+ }) => {
15
+ console.log("submit", form);
12
16
  };
13
17
 
14
18
  return (
@@ -26,7 +30,6 @@ export function BestUnit(props: any) {
26
30
  );
27
31
  }
28
32
 
29
-
30
- register(BestUnit, 'x-best-modal-form', ['theme'], { shadow: false });
33
+ register(BestUnit, "x-best-modal-form", ["theme"], { shadow: false });
31
34
 
32
35
  export default BestUnit;
@@ -0,0 +1,19 @@
1
+ import { npmTest, printCurrentTime } from '../main';
2
+ import { BestUnit } from '../components/ModalForm';
3
+
4
+ export default function DemoApp() {
5
+ return (
6
+ <div>
7
+ <h2>组件库可视化测试</h2>
8
+ <div>
9
+ <h3>BestUnit 组件演示:</h3>
10
+ <BestUnit theme={{ primaryColor: '#6366f1' }} />
11
+ </div>
12
+ <div>
13
+ <h3>方法演示:</h3>
14
+ <button onClick={npmTest} style={{ marginRight: 10 }}>调用 npmTest()</button>
15
+ <button onClick={printCurrentTime}>调用 printCurrentTime()</button>
16
+ </div>
17
+ </div>
18
+ );
19
+ }
@@ -0,0 +1,4 @@
1
+ import { render } from 'preact';
2
+ import DemoApp from './App.tsx';
3
+
4
+ render(<DemoApp />, document.getElementById('app')!);
package/src/main.ts CHANGED
@@ -1,6 +1,5 @@
1
1
 
2
2
  import { npmTest, printCurrentTime } from './index.ts'
3
- import './app.tsx'
4
3
  import './components/ModalForm'
5
4
 
6
5
  // 可选:导出组件列表或版本信息
@@ -9,3 +8,7 @@ export {
9
8
  npmTest,
10
9
  printCurrentTime,
11
10
  }
11
+
12
+
13
+
14
+
package/tsconfig.json CHANGED
@@ -8,5 +8,8 @@
8
8
  "references": [
9
9
  { "path": "./tsconfig.app.json" },
10
10
  { "path": "./tsconfig.node.json" }
11
- ]
11
+ ],
12
+ "editor.codeActionsOnSave": {
13
+ "source.fixAll.eslint": true
14
+ }
12
15
  }
package/src/app.tsx DELETED
@@ -1,9 +0,0 @@
1
- import register from 'preact-custom-element';
2
-
3
- const Greeting = ({ name = 'World' }) => (
4
- <p>Hello, {name}!</p>
5
- );
6
-
7
- register(Greeting, 'x-greeting', ['name'], { shadow: false });
8
-
9
- export default Greeting;
@@ -1,80 +0,0 @@
1
- import { useState } from 'preact/hooks';
2
-
3
- interface ModalFormProps {
4
- visible: boolean;
5
- onClose: () => void;
6
- onSubmit: (form: { name: string; email: string }) => Promise<void>;
7
- color?: string;
8
- }
9
-
10
- export function ModalForm({ visible, onClose, onSubmit, color }: ModalFormProps) {
11
- const [name, setName] = useState('');
12
- const [email, setEmail] = useState('');
13
- const [loading, setLoading] = useState(false);
14
- const [error, setError] = useState('');
15
-
16
- if (!visible) return null;
17
-
18
- const handleSubmit = async (e: Event) => {
19
- e.preventDefault();
20
- setLoading(true);
21
- setError('');
22
- try {
23
- await onSubmit({ name, email });
24
- onClose();
25
- } catch {
26
- setError('提交失败,请重试');
27
- } finally {
28
- setLoading(false);
29
- }
30
- };
31
-
32
- return (
33
- <div style={{
34
- position: 'fixed', top: 0, left: 0, right: 0, bottom: 0,
35
- background: 'rgba(0,0,0,0.3)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 9999
36
- }}>
37
- <form
38
- onSubmit={handleSubmit}
39
- style={{ background: '#fff', padding: 24, borderRadius: 8, minWidth: 320 }}
40
- >
41
- <h2 style={{ color: color || '#1890ff' }}>表单</h2>
42
- <div style={{ marginBottom: 12 }}>
43
- <input
44
- type="text"
45
- placeholder="姓名"
46
- value={name}
47
- onInput={e => setName((e.target as HTMLInputElement).value)}
48
- required
49
- style={{ width: '100%', padding: 8, marginBottom: 8 }}
50
- />
51
- <input
52
- type="email"
53
- placeholder="邮箱"
54
- value={email}
55
- onInput={e => setEmail((e.target as HTMLInputElement).value)}
56
- required
57
- style={{ width: '100%', padding: 8 }}
58
- />
59
- </div>
60
- {error && <div style={{ color: 'red', marginBottom: 8 }}>{error}</div>}
61
- <button
62
- type="submit"
63
- disabled={loading}
64
- style={{
65
- background: color || '#1890ff', color: '#fff', border: 'none', borderRadius: 4, padding: '8px 16px', cursor: 'pointer', fontSize: 16
66
- }}
67
- >
68
- {loading ? '提交中...' : '提交'}
69
- </button>
70
- <button
71
- type="button"
72
- onClick={onClose}
73
- style={{ marginLeft: 12, background: '#eee', color: '#333', border: 'none', borderRadius: 4, padding: '8px 16px', cursor: 'pointer', fontSize: 16 }}
74
- >
75
- 取消
76
- </button>
77
- </form>
78
- </div>
79
- );
80
- }