best-unit 0.0.13 → 0.0.15

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.13",
4
+ "version": "0.0.15",
5
5
  "type": "module",
6
6
  "main": "dist/best-unit.cjs",
7
7
  "module": "dist/best-unit.js",
@@ -4,33 +4,28 @@ import type {
4
4
  AxiosResponse,
5
5
  InternalAxiosRequestConfig,
6
6
  } from "axios";
7
+ import { message } from "../components/common/Message";
7
8
 
8
9
  export interface CreateAxiosOptions {
9
10
  baseURL?: string;
10
11
  timeout?: number;
11
- getToken?: () => string | null;
12
12
  getLocale?: () => string | null;
13
13
  onError?: (msg: string, error: any) => void;
14
14
  }
15
15
 
16
16
  export function createAxiosInstance(options: CreateAxiosOptions = {}) {
17
- const {
18
- baseURL = "/api",
19
- timeout = 10000,
20
- getToken,
21
- getLocale,
22
- onError,
23
- } = options;
17
+ const { baseURL = "/api", timeout = 10000, getLocale, onError } = options;
24
18
 
25
19
  const instance: AxiosInstance = axios.create({ baseURL, timeout });
26
20
 
27
21
  // 请求拦截:加 token、国际化
28
22
  instance.interceptors.request.use((config: InternalAxiosRequestConfig) => {
29
- if (getToken) {
30
- const token = getToken();
31
- if (token)
32
- config.headers = { ...config.headers, Authorization: token } as any;
33
- }
23
+ const fundUnitParams = JSON.parse(
24
+ sessionStorage.getItem("fund_unit_params") || "{}"
25
+ );
26
+ const { token } = fundUnitParams;
27
+ config.headers = { ...config.headers, Authorization: token } as any;
28
+
34
29
  if (getLocale) {
35
30
  const locale = getLocale();
36
31
  if (locale)
@@ -48,17 +43,29 @@ export function createAxiosInstance(options: CreateAxiosOptions = {}) {
48
43
  if (response.data && response.data.code === 0) {
49
44
  return response.data;
50
45
  }
46
+ const errorMsg =
47
+ response.data?.msg || response.data?.message || "未知错误";
48
+
49
+ // 显示错误消息
50
+ message.error(errorMsg);
51
+
51
52
  if (onError) {
52
- onError(
53
- response.data?.msg || response.data?.message || "未知错误",
54
- response
55
- );
53
+ onError(errorMsg, response);
56
54
  }
57
55
  return Promise.reject(response.data || { message: "未知错误" });
58
56
  },
59
57
  (error) => {
58
+ const errorMsg =
59
+ error.response?.data?.msg ||
60
+ error.response?.data?.message ||
61
+ error.message ||
62
+ "网络请求失败";
63
+
64
+ // 显示错误消息
65
+ message.error(errorMsg);
66
+
60
67
  if (onError) {
61
- onError(error.message, error);
68
+ onError(errorMsg, error);
62
69
  }
63
70
  return Promise.reject(error);
64
71
  }
package/src/api/index.ts CHANGED
@@ -1,16 +1,97 @@
1
- import axiosInstance from "./axiosInstance";
2
-
3
- export function getBalance(params: {
4
- merchant_id: string;
5
- biz_type: string;
6
- token?: string;
7
- }) {
8
- const { token, ...rest } = params;
9
- return axiosInstance.get("/balance", {
10
- params: rest,
11
- headers: token ? { Authorization: token } : undefined,
12
- });
1
+ import http from "./axiosInstance";
2
+ import type { AxiosProgressEvent } from "axios";
3
+ // 获取余额
4
+ export function getBalance() {
5
+ const fundUnitParams = JSON.parse(
6
+ sessionStorage.getItem("fund_unit_params") || "{}"
7
+ );
8
+ return http
9
+ .get("/balance", {
10
+ params: {
11
+ merchant_id: fundUnitParams.merchant_id,
12
+ biz_type: fundUnitParams.biz_type,
13
+ },
14
+ })
15
+ .then((res) => {
16
+ const data = res.data;
17
+ return {
18
+ totalAmount: data.total_amount || 0,
19
+ availableAmount: data.available_amount || 0,
20
+ frozenAmount: data.frozen_amount || 0,
21
+ };
22
+ });
13
23
  }
14
24
 
25
+ // 获取所有字典
26
+ export const getAllDicts = async () => {
27
+ return http.get("/all-dicts", {}).then((res) => {
28
+ sessionStorage.setItem("all_dicts", JSON.stringify(res.data));
29
+ return res.data || {};
30
+ });
31
+ };
32
+
33
+ // 上传文件
34
+ export const uploadFile = async (
35
+ file: any,
36
+ onProgress?: (percent: number) => void
37
+ ) => {
38
+ return http
39
+ .post("/oss/upload", file, {
40
+ headers: {
41
+ "Content-Type": "multipart/form-data",
42
+ },
43
+ onUploadProgress: (progressEvent: AxiosProgressEvent) => {
44
+ if (progressEvent.total) {
45
+ const percent = Math.round(
46
+ (progressEvent.loaded * 100) / progressEvent.total
47
+ );
48
+ onProgress && onProgress(percent);
49
+ }
50
+ },
51
+ })
52
+ .then((res) => {
53
+ return res.data?.url || "";
54
+ });
55
+ };
56
+
57
+ // 创建离线充值
58
+ export const createOfflineRecharge = async (data: any) => {
59
+ const fundUnitParams = JSON.parse(
60
+ sessionStorage.getItem("fund_unit_params") || "{}"
61
+ );
62
+
63
+ const params = {
64
+ merchant_id: Number(fundUnitParams.merchantId),
65
+ biz_type: fundUnitParams.bizType,
66
+ source_operator: String(fundUnitParams.userId),
67
+ transfer_no: data.transferNo,
68
+ transfer_channel: data.transferChannel,
69
+ voucher_urls: data.voucherUrls,
70
+ };
71
+ return http.post("/offline/recharge/create", params, {});
72
+ };
73
+
74
+ // 创建在线充值
75
+ export const createOnlineRecharge = async (data: any) => {
76
+ const fundUnitParams = JSON.parse(
77
+ sessionStorage.getItem("fund_unit_params") || "{}"
78
+ );
79
+
80
+ console.log(fundUnitParams, "fundUnitParams");
81
+
82
+ const params = {
83
+ merchant_id: Number(fundUnitParams.merchantId),
84
+ biz_type: fundUnitParams.bizType,
85
+ source_operator: String(fundUnitParams.userId),
86
+ amount: data.amount,
87
+ currency: data.currency,
88
+ recharge_channel: data.rechargeChannel,
89
+ return_url: window.location.href,
90
+ };
91
+ return http.post("/online/recharge/create", params, {}).then((res) => {
92
+ return res.data.redirect_url;
93
+ });
94
+ };
95
+
15
96
  // 示例用法:
16
97
  // getBalance({ merchant_id: '1128', biz_type: 'ad', token: 'xxx' }).then(res => console.log(res));
@@ -1,29 +1,13 @@
1
1
  import type { ComponentChildren } from "preact";
2
- import { getBalance } from "../../../api";
3
2
 
4
3
  interface ButtonProps {
5
4
  onClick?: () => void;
6
5
  color?: string;
7
6
  children: ComponentChildren;
8
- merchantId?: string;
9
- bizType?: string;
10
- token?: string;
11
7
  }
12
8
 
13
- export function ThemedButton({
14
- onClick,
15
- color,
16
- children,
17
- merchantId,
18
- bizType,
19
- token,
20
- }: ButtonProps) {
9
+ export function ThemedButton({ onClick, color, children }: ButtonProps) {
21
10
  // 组件加载时直接调用
22
- if (merchantId && bizType) {
23
- getBalance({ merchant_id: merchantId, biz_type: bizType, token }).then(
24
- (res) => console.log(res)
25
- );
26
- }
27
11
  return (
28
12
  <button
29
13
  style={{
@@ -1,11 +1,13 @@
1
1
  import type { FunctionalComponent } from "preact";
2
- import { useRef } from "preact/hooks";
2
+ import { Upload } from "../../../common/Upload";
3
+ import { createOfflineRecharge } from "../../../../api";
4
+ import { message } from "../../../common/Message";
3
5
 
4
6
  interface OfflineTransferFormProps {
5
7
  formState: {
6
8
  platform: string;
7
9
  transactionId: string;
8
- files: File[];
10
+ files: string[]; // 绑定url数组
9
11
  platformError?: string;
10
12
  transactionIdError?: string;
11
13
  filesError?: string;
@@ -20,7 +22,9 @@ interface OfflineTransferFormProps {
20
22
  export const OfflineTransferForm: FunctionalComponent<
21
23
  OfflineTransferFormProps
22
24
  > = ({ formState, setFormState, onClose, loading, whiteTheme = false }) => {
23
- const fileInputRef = useRef<HTMLInputElement>(null);
25
+ const allDicts = JSON.parse(sessionStorage.getItem("all_dicts") || "{}");
26
+ console.log(allDicts, "allDicts");
27
+ const channelDict = allDicts.channel;
24
28
 
25
29
  // 样式对象
26
30
  const theme = whiteTheme
@@ -227,29 +231,7 @@ export const OfflineTransferForm: FunctionalComponent<
227
231
  },
228
232
  };
229
233
 
230
- const handleFileChange = (e: any) => {
231
- const files = Array.from(e.target.files).slice(0, 10);
232
- setFormState((state: any) => ({ ...state, files, filesError: "" }));
233
- };
234
-
235
- const handleDrop = (e: any) => {
236
- e.preventDefault();
237
- const files = Array.from(e.dataTransfer.files).slice(0, 10);
238
- setFormState((state: any) => ({ ...state, files, filesError: "" }));
239
- };
240
-
241
- const handleDragOver = (e: any) => {
242
- e.preventDefault();
243
- };
244
-
245
- const handleRemoveFile = (idx: number) => {
246
- setFormState((state: any) => ({
247
- ...state,
248
- files: state.files.filter((_: File, i: number) => i !== idx),
249
- }));
250
- };
251
-
252
- const handleSubmit = (e: any) => {
234
+ const handleSubmit = async (e: any) => {
253
235
  e.preventDefault();
254
236
  let valid = true;
255
237
  setFormState((state: any) => ({
@@ -280,15 +262,13 @@ export const OfflineTransferForm: FunctionalComponent<
280
262
  valid = false;
281
263
  }
282
264
  if (!valid) return;
283
- // 打印表单值
284
- console.log("OfflineTransferForm values:", formState);
285
- };
286
-
287
- // 文件大小格式化
288
- const formatSize = (size: number) => {
289
- if (size > 1024 * 1024) return (size / 1024 / 1024).toFixed(2) + " MB";
290
- if (size > 1024) return (size / 1024).toFixed(2) + " KB";
291
- return size + " B";
265
+ await createOfflineRecharge({
266
+ transferChannel: formState.platform,
267
+ transferNo: formState.transactionId,
268
+ voucherUrls: formState.files,
269
+ });
270
+ onClose();
271
+ message.success("离线充值创建成功");
292
272
  };
293
273
 
294
274
  return (
@@ -312,9 +292,12 @@ export const OfflineTransferForm: FunctionalComponent<
312
292
  }));
313
293
  }}
314
294
  >
315
- <option value="paypal">PayPal</option>
316
- <option value="alipay">支付宝</option>
317
- <option value="wechat">微信</option>
295
+ <option value="" disabled hidden>
296
+ 请选择支付平台
297
+ </option>
298
+ {channelDict.map((item: any) => (
299
+ <option value={item.value}>{item.label}</option>
300
+ ))}
318
301
  </select>
319
302
  {formState.platformError && (
320
303
  <div style={theme.error}>{formState.platformError}</div>
@@ -349,63 +332,19 @@ export const OfflineTransferForm: FunctionalComponent<
349
332
  <div style={theme.label}>
350
333
  <span style={{ color: "#F53F3F" }}>*</span> 上传文件
351
334
  </div>
352
- <div
353
- style={{
354
- ...theme.upload,
355
- ...(formState.filesError ? theme.uploadError : {}),
356
- }}
357
- onClick={() => fileInputRef.current?.click()}
358
- onDrop={handleDrop}
359
- onDragOver={handleDragOver}
360
- >
361
- <div style={{ fontSize: 48, marginBottom: 12 }}>
362
- {/* 优化后的上传图标 */}
363
- <svg width="64" height="64" viewBox="0 0 64 64" fill="none">
364
- <rect x="8" y="8" width="48" height="48" rx="12" fill="#F4F6FA" />
365
- <rect x="28" y="20" width="8" height="24" rx="4" fill="#E5E6EB" />
366
- </svg>
367
- </div>
368
- <div style={{ color: "#222", fontSize: 15, marginBottom: 4 }}>
369
- 点击或拖拽文件到此处上传
370
- </div>
371
- <div style={{ color: "#999", fontSize: 13 }}>
372
- 支持 JPG、PNG、PDF 格式,单个文件不超过 20MB,最多上传 10 个文件
373
- </div>
374
- <input
375
- ref={fileInputRef}
376
- type="file"
377
- multiple
378
- accept=".jpg,.jpeg,.png,.pdf"
379
- style={{ display: "none" }}
380
- onChange={handleFileChange}
381
- />
382
- </div>
383
- {formState.files && formState.files.length > 0 && (
384
- <div style={{ marginTop: 12 }}>
385
- {formState.files.map((file, idx) => (
386
- <div key={idx} style={theme.fileItem}>
387
- <div style={{ display: "flex", alignItems: "center", flex: 1 }}>
388
- <span style={{ fontWeight: 500 }}>{file.name}</span>
389
- <span
390
- style={{ color: "#8C8F93", fontSize: 13, marginLeft: 8 }}
391
- >
392
- ({formatSize(file.size)})
393
- </span>
394
- </div>
395
- <button
396
- type="button"
397
- onClick={(e) => {
398
- e.stopPropagation();
399
- handleRemoveFile(idx);
400
- }}
401
- style={theme.removeBtn}
402
- >
403
- 移除
404
- </button>
405
- </div>
406
- ))}
407
- </div>
408
- )}
335
+ <Upload
336
+ value={formState.files}
337
+ onChange={(urls) =>
338
+ setFormState((state: any) => ({
339
+ ...state,
340
+ files: urls,
341
+ filesError: "",
342
+ }))
343
+ }
344
+ maxCount={10}
345
+ accept={".jpg,.jpeg,.png,.pdf"}
346
+ multiple={true}
347
+ />
409
348
  {formState.filesError && (
410
349
  <div style={theme.error}>{formState.filesError}</div>
411
350
  )}
@@ -20,6 +20,11 @@ interface OnlineRechargeFormProps {
20
20
  export const OnlineRechargeForm: FunctionalComponent<
21
21
  OnlineRechargeFormProps
22
22
  > = ({ formState, setFormState, onClose, loading, whiteTheme = false }) => {
23
+ const allDicts = JSON.parse(sessionStorage.getItem("all_dicts") || "{}");
24
+ const currencyDict = allDicts.currency || [];
25
+ const channelDict =
26
+ allDicts.channel?.filter((item: any) => item.payment_support) || [];
27
+
23
28
  const theme = whiteTheme
24
29
  ? {
25
30
  label: {
@@ -237,9 +242,12 @@ export const OnlineRechargeForm: FunctionalComponent<
237
242
  }));
238
243
  }}
239
244
  >
240
- <option value="USD">USD - 美元</option>
241
- <option value="CNY">CNY - 人民币</option>
242
- <option value="EUR">EUR - 欧元</option>
245
+ <option value="" disabled hidden>
246
+ 请选择充值币种
247
+ </option>
248
+ {currencyDict.map((item: any) => (
249
+ <option value={item.value}>{item.label}</option>
250
+ ))}
243
251
  </select>
244
252
  </div>
245
253
  <div style={{ marginBottom: 18 }}>
@@ -286,9 +294,12 @@ export const OnlineRechargeForm: FunctionalComponent<
286
294
  }));
287
295
  }}
288
296
  >
289
- <option value="alipay">支付宝</option>
290
- <option value="wechat">微信</option>
291
- <option value="paypal">PayPal</option>
297
+ <option value="" disabled hidden>
298
+ 请选择支付平台
299
+ </option>
300
+ {channelDict.map((item: any) => (
301
+ <option value={item.value}>{item.label}</option>
302
+ ))}
292
303
  </select>
293
304
  {formState.rechargeChannelError && (
294
305
  <div style={theme.error}>{formState.rechargeChannelError}</div>
@@ -16,17 +16,16 @@ interface ModalFormProps {
16
16
  token?: string;
17
17
  }
18
18
 
19
- export function ModalForm({
19
+ export function Recharge({
20
20
  visible,
21
21
  onClose,
22
22
  onSubmit,
23
23
  color,
24
24
  whiteTheme = true,
25
25
  }: ModalFormProps & { whiteTheme?: boolean }) {
26
- console.log(1111);
27
26
  const [formState, setFormState] = useState({
28
27
  amount: "",
29
- rechargeChannel: "paypal",
28
+ rechargeChannel: "",
30
29
  currency: "USD",
31
30
  loading: false,
32
31
  error: "",
@@ -36,7 +35,7 @@ export function ModalForm({
36
35
  });
37
36
  const [activeTab, setActiveTab] = useState<"online" | "offline">("online");
38
37
  const [offlineFormState, setOfflineFormState] = useState({
39
- platform: "paypal",
38
+ platform: "",
40
39
  transactionId: "",
41
40
  files: [],
42
41
  platformError: "",
@@ -51,7 +50,7 @@ export function ModalForm({
51
50
  setActiveTab("online");
52
51
  setFormState({
53
52
  amount: "",
54
- rechargeChannel: "paypal",
53
+ rechargeChannel: "",
55
54
  currency: "USD",
56
55
  loading: false,
57
56
  error: "",
@@ -60,7 +59,7 @@ export function ModalForm({
60
59
  currencyError: "",
61
60
  });
62
61
  setOfflineFormState({
63
- platform: "paypal",
62
+ platform: "",
64
63
  transactionId: "",
65
64
  files: [],
66
65
  platformError: "",
@@ -220,6 +219,7 @@ export function ModalForm({
220
219
  padding: 32,
221
220
  borderRadius: 12,
222
221
  minWidth: 400,
222
+ maxWidth: 400,
223
223
  color: color || theme.modalColor,
224
224
  boxShadow: theme.modalBoxShadow,
225
225
  position: "relative",
@@ -1,33 +1,30 @@
1
1
  import { useState } from "preact/hooks";
2
2
  import { ThemedButton } from "./components/Button";
3
- import { ModalForm } from "./components/recharge";
3
+ import { Recharge } from "./components/Recharge";
4
4
  import register from "preact-custom-element";
5
+ import { createOnlineRecharge, getAllDicts } from "../../../api";
6
+ getAllDicts();
5
7
 
6
8
  export function BestUnit(props: any) {
7
9
  const [visible, setVisible] = useState(false);
8
10
  const [whiteTheme, setWhiteTheme] = useState(true);
9
11
  const color = props.theme?.primaryColor;
10
- const merchantId = props.merchant_id;
11
- const bizType = props.biz_type;
12
- const token = props.token;
13
-
14
12
  const handleSubmit = async (form: {
15
13
  amount: string;
16
14
  rechargeChannel: string;
17
15
  currency: string;
18
16
  }) => {
19
- console.log("submit", form);
17
+ const result = await createOnlineRecharge({
18
+ amount: form.amount,
19
+ currency: form.currency,
20
+ rechargeChannel: form.rechargeChannel,
21
+ });
22
+ window.open(result, "_blank");
20
23
  };
21
24
 
22
25
  return (
23
26
  <div>
24
- <ThemedButton
25
- color={color}
26
- onClick={() => setVisible(true)}
27
- merchantId={merchantId}
28
- bizType={bizType}
29
- token={token}
30
- >
27
+ <ThemedButton color={color} onClick={() => setVisible(true)}>
31
28
  打开表单
32
29
  </ThemedButton>
33
30
  <button
@@ -42,15 +39,12 @@ export function BestUnit(props: any) {
42
39
  >
43
40
  {whiteTheme ? "切换为暗黑主题" : "切换为白色主题"}
44
41
  </button>
45
- <ModalForm
42
+ <Recharge
46
43
  visible={visible}
47
44
  onClose={() => setVisible(false)}
48
45
  onSubmit={handleSubmit}
49
46
  color={color}
50
47
  whiteTheme={whiteTheme}
51
- merchantId={merchantId}
52
- bizType={bizType}
53
- token={token}
54
48
  />
55
49
  </div>
56
50
  );