best-unit 0.0.41 → 0.0.43

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.41",
4
+ "version": "0.0.43",
5
5
  "type": "module",
6
6
  "main": "dist/best-unit.cjs",
7
7
  "module": "dist/best-unit.js",
@@ -4,6 +4,7 @@ import { createOfflineRecharge } from "../../../../api";
4
4
  import { message } from "../../../common/Message";
5
5
  import { t } from "../../../../local";
6
6
  import { Theme } from "../../../../types";
7
+ import { Select } from "../../../common/Select";
7
8
 
8
9
  interface OfflineTransferFormProps {
9
10
  formState: {
@@ -281,28 +282,23 @@ export const OfflineTransferForm: FunctionalComponent<
281
282
  <div style={theme.label}>
282
283
  <span style={{ color: "#F53F3F" }}>*</span> {t("第三方支付平台")}
283
284
  </div>
284
- <select
285
- style={{
286
- ...theme.select,
287
- ...(formState.platformError ? theme.selectError : {}),
288
- }}
285
+ <Select
289
286
  value={formState.platform}
290
- onInput={(e) => {
291
- const value = (e.target as HTMLSelectElement).value;
287
+ onChange={(value) => {
292
288
  setFormState((state: any) => ({
293
289
  ...state,
294
290
  platform: value,
295
291
  platformError: value ? "" : state.platformError,
296
292
  }));
297
293
  }}
298
- >
299
- <option value="" disabled hidden>
300
- {t("请选择支付平台")}
301
- </option>
302
- {channelDict?.map((item: any) => (
303
- <option value={item.value}>{item.label}</option>
304
- ))}
305
- </select>
294
+ options={[
295
+ ...channelDict?.map((item: any) => ({
296
+ value: item.value,
297
+ label: item.label,
298
+ })),
299
+ ]}
300
+ placeholder={t("请选择支付平台")}
301
+ />
306
302
  {formState.platformError && (
307
303
  <div style={theme.error}>{formState.platformError}</div>
308
304
  )}
@@ -1,6 +1,7 @@
1
1
  import type { FunctionalComponent } from "preact";
2
2
  import { t } from "../../../../local";
3
3
  import { Theme } from "../../../../types";
4
+ import { Select } from "../../../common/Select";
4
5
 
5
6
  interface OnlineRechargeFormProps {
6
7
  formState: {
@@ -54,16 +55,6 @@ export const OnlineRechargeForm: FunctionalComponent<
54
55
  inputError: {
55
56
  border: "1px solid #ff4d4f",
56
57
  },
57
- select: {
58
- width: "100%",
59
- padding: "10px 12px",
60
- borderRadius: 6,
61
- border: "1px solid #E5E6EB",
62
- background: "#fff",
63
- color: "#222",
64
- fontSize: 15,
65
- outline: "none",
66
- },
67
58
  selectError: {
68
59
  border: "1px solid #ff4d4f",
69
60
  },
@@ -116,16 +107,6 @@ export const OnlineRechargeForm: FunctionalComponent<
116
107
  inputError: {
117
108
  border: "1px solid #ff4d4f",
118
109
  },
119
- select: {
120
- width: "100%",
121
- padding: "10px 12px",
122
- borderRadius: 6,
123
- border: "1px solid #23262F",
124
- background: "#23262F",
125
- color: "#fff",
126
- fontSize: 15,
127
- outline: "none",
128
- },
129
110
  selectError: {
130
111
  border: "1px solid #ff4d4f",
131
112
  },
@@ -233,27 +214,20 @@ export const OnlineRechargeForm: FunctionalComponent<
233
214
  <div style={theme.label}>
234
215
  <span style={{ color: "#F53F3F" }}>*</span> {t("充值币种")}
235
216
  </div>
236
- <select
237
- style={{
238
- ...theme.select,
239
- // 币种没有错误校验
240
- }}
217
+ <Select
241
218
  value={formState.currency}
242
- onInput={(e) => {
243
- const value = (e.target as HTMLSelectElement).value;
219
+ onChange={(value) => {
244
220
  setFormState((state: any) => ({
245
221
  ...state,
246
222
  currency: value,
247
223
  }));
248
224
  }}
249
- >
250
- <option value="" disabled hidden>
251
- {t("请选择充值币种")}
252
- </option>
253
- {currencyDict?.map((item: any) => (
254
- <option value={item.value}>{item.label}</option>
255
- ))}
256
- </select>
225
+ options={currencyDict?.map((item: any) => ({
226
+ value: item.value,
227
+ label: item.label,
228
+ }))}
229
+ placeholder={t("请选择充值币种")}
230
+ />
257
231
  </div>
258
232
  <div style={{ marginBottom: 18 }}>
259
233
  <div style={theme.label}>
@@ -284,28 +258,21 @@ export const OnlineRechargeForm: FunctionalComponent<
284
258
  <div style={theme.label}>
285
259
  <span style={{ color: "#F53F3F" }}>*</span> {t("支付平台")}
286
260
  </div>
287
- <select
288
- style={{
289
- ...theme.select,
290
- ...(formState.rechargeChannelError ? theme.selectError : {}),
291
- }}
261
+ <Select
292
262
  value={formState.rechargeChannel}
293
- onInput={(e) => {
294
- const value = (e.target as HTMLSelectElement).value;
263
+ onChange={(value) => {
295
264
  setFormState((state: any) => ({
296
265
  ...state,
297
266
  rechargeChannel: value,
298
267
  rechargeChannelError: value ? "" : state.rechargeChannelError,
299
268
  }));
300
269
  }}
301
- >
302
- <option value="" disabled hidden>
303
- {t("请选择支付平台")}
304
- </option>
305
- {channelDict?.map((item: any) => (
306
- <option value={item.value}>{item.label}</option>
307
- ))}
308
- </select>
270
+ options={channelDict?.map((item: any) => ({
271
+ value: item.value,
272
+ label: item.label,
273
+ }))}
274
+ placeholder={t("请选择支付平台")}
275
+ ></Select>
309
276
  {formState.rechargeChannelError && (
310
277
  <div style={theme.error}>{formState.rechargeChannelError}</div>
311
278
  )}
@@ -1,18 +1,26 @@
1
- import type { FunctionalComponent } from "preact";
1
+ import type { FunctionalComponent, JSX } from "preact";
2
2
  import { useState, useRef, useEffect } from "preact/hooks";
3
+ import { Theme } from "../../types";
4
+ import { t } from "../../local";
3
5
 
4
6
  interface Option {
5
7
  value: string;
6
8
  label: string;
9
+ disabled?: boolean;
7
10
  }
8
11
 
9
12
  interface SelectProps {
10
- value: string;
11
- onChange: (value: string) => void;
13
+ value?: string;
14
+ onChange?: (value: string) => void;
12
15
  options: Option[];
13
16
  placeholder?: string;
14
17
  style?: any;
15
18
  error?: boolean;
19
+ disabled?: boolean;
20
+ dropdownStyle?: any;
21
+ className?: string;
22
+ dropdownClassName?: string;
23
+ children?: JSX.Element;
16
24
  }
17
25
 
18
26
  export const Select: FunctionalComponent<SelectProps> = ({
@@ -22,11 +30,80 @@ export const Select: FunctionalComponent<SelectProps> = ({
22
30
  placeholder,
23
31
  style,
24
32
  error,
33
+ disabled,
34
+ dropdownStyle,
35
+ className,
36
+ dropdownClassName,
37
+ children,
25
38
  }) => {
26
39
  const [open, setOpen] = useState(false);
27
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
+ };
28
104
 
29
105
  useEffect(() => {
106
+ if (!open) return;
30
107
  const handleClick = (e: MouseEvent) => {
31
108
  if (ref.current && !ref.current.contains(e.target as Node)) {
32
109
  setOpen(false);
@@ -34,64 +111,160 @@ export const Select: FunctionalComponent<SelectProps> = ({
34
111
  };
35
112
  document.addEventListener("mousedown", handleClick);
36
113
  return () => document.removeEventListener("mousedown", handleClick);
37
- }, []);
114
+ }, [open]);
38
115
 
39
116
  const selected = options.find((opt) => opt.value === value);
40
117
 
41
118
  return (
42
- <div ref={ref} style={{ position: "relative", ...style }}>
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>
43
140
  <div
44
- onClick={() => setOpen((o) => !o)}
141
+ onClick={() => !disabled && setOpen((o) => !o)}
45
142
  style={{
46
143
  minHeight: 40,
47
- border: error ? "1px solid #ff4d4f" : "1px solid #d9d9d9",
48
144
  borderRadius: 6,
49
- background: "#fff",
50
145
  display: "flex",
51
146
  alignItems: "center",
52
147
  padding: "0 12px",
53
148
  fontSize: 15,
54
- color: value ? "#222" : "#bfbfbf",
55
- cursor: "pointer",
149
+ cursor: disabled ? "not-allowed" : "pointer",
56
150
  boxSizing: "border-box",
57
151
  transition: "border 0.2s",
152
+ opacity: disabled ? 0.6 : 1,
153
+ width: "100%",
154
+ ...selectTheme.trigger,
58
155
  ...style,
59
156
  }}
157
+ tabIndex={0}
60
158
  >
61
159
  <span style={{ flex: 1 }}>
62
- {selected ? selected.label : placeholder || "请选择"}
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>
63
184
  </span>
64
- <span style={{ marginLeft: 8, fontSize: 12, color: "#bfbfbf" }}>▼</span>
65
185
  </div>
66
- {open && (
186
+ {open && !disabled && (
67
187
  <div
188
+ className={`custom-select-dropdown${
189
+ dropdownClassName ? " " + dropdownClassName : ""
190
+ }`}
68
191
  style={{
69
192
  position: "absolute",
70
193
  left: 0,
71
194
  right: 0,
72
195
  top: 44,
73
196
  zIndex: 10,
74
- background: "#fff",
75
- border: "1px solid #d9d9d9",
76
197
  borderRadius: 6,
77
- boxShadow: "0 2px 8px rgba(0,0,0,0.08)",
198
+ boxShadow: whiteTheme
199
+ ? "0 2px 8px rgba(0,0,0,0.08)"
200
+ : "0 2px 8px rgba(0,0,0,0.32)",
78
201
  maxHeight: 220,
79
202
  overflowY: "auto",
203
+ ...selectTheme.dropdown,
204
+ ...dropdownStyle,
80
205
  }}
81
206
  >
82
- {options.map((opt) => (
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) => (
83
249
  <div
84
250
  key={opt.value}
85
251
  onClick={() => {
252
+ if (opt.disabled) return;
86
253
  setOpen(false);
87
- onChange(opt.value);
254
+ onChange?.(opt.value);
88
255
  }}
256
+ onMouseEnter={() => setHoveredIndex(idx)}
257
+ onMouseLeave={() => setHoveredIndex(null)}
89
258
  style={{
90
259
  padding: "10px 16px",
91
- cursor: "pointer",
92
- color: "#222",
93
- background: value === opt.value ? "#f5f5f5" : "#fff",
260
+ cursor: opt.disabled ? "not-allowed" : "pointer",
94
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
+ ),
95
268
  }}
96
269
  >
97
270
  {opt.label}
@@ -99,6 +272,7 @@ export const Select: FunctionalComponent<SelectProps> = ({
99
272
  ))}
100
273
  </div>
101
274
  )}
275
+ {children}
102
276
  </div>
103
277
  );
104
278
  };
package/src/local/en.ts CHANGED
@@ -5,6 +5,7 @@ export const en: Record<string, string> = {
5
5
  真实金额: "Real Amount",
6
6
  冻结金额: "Frozen Amount",
7
7
  总可用: "Total Available",
8
+ 暂无数据: "No Data",
8
9
 
9
10
  // 充值相关
10
11
  "充值 / 转账": "Recharge / Transfer",
package/src/local/zh.ts CHANGED
@@ -5,6 +5,7 @@ export const zh: Record<string, string> = {
5
5
  真实金额: "真实金额",
6
6
  冻结金额: "冻结金额",
7
7
  总可用: "总可用",
8
+ 暂无数据: "暂无数据",
8
9
 
9
10
  // 充值相关
10
11
  "充值 / 转账": "充值 / 转账",