best-unit 2.0.1 → 2.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.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "best-unit",
3
3
  "private": false,
4
- "version": "2.0.1",
4
+ "version": "2.0.3",
5
5
  "type": "module",
6
6
  "main": "dist/best-unit.cjs",
7
7
  "module": "dist/best-unit.js",
package/src/api/index.ts CHANGED
@@ -156,7 +156,7 @@ export const getChannelInfo = async (data: { code: string }) => {
156
156
  });
157
157
  };
158
158
 
159
- // /sdk/offline/channel/map
159
+ // 获取币种对应渠道
160
160
  export const getOfflineChannelMap = async (data: { currency: string }) => {
161
161
  const fundUnitParams = JSON.parse(
162
162
  sessionStorage.getItem("fund_unit_params") || "{}"
@@ -186,5 +186,32 @@ export const getOfflineChannelMap = async (data: { currency: string }) => {
186
186
  });
187
187
  };
188
188
 
189
+ // /sdk/business_exchange_rate/calculate
190
+ // 计算业务汇率
191
+ export const calculateBusinessExchangeRate = async (data: {
192
+ currency: string;
193
+ amount: string;
194
+ }) => {
195
+ const fundUnitParams = JSON.parse(
196
+ sessionStorage.getItem("fund_unit_params") || "{}"
197
+ );
198
+ return http()
199
+ .post("/business_exchange_rate/calculate", {
200
+ fund_balance_id: fundUnitParams.fundBalanceId,
201
+ merchant_id: fundUnitParams.merchantId,
202
+ biz_type: fundUnitParams.bizType,
203
+ amount: data.amount,
204
+ payment_currency: data.currency,
205
+ })
206
+ .then((res) => {
207
+ return {
208
+ amount: res.data.amount,
209
+ exchangeRate: res.data.exchange_rate,
210
+ currency: res.data.currency,
211
+ consistent: res.data.consistent,
212
+ };
213
+ });
214
+ };
215
+
189
216
  // 示例用法:
190
217
  // getBalance({ merchant_id: '1128', biz_type: 'ad', token: 'xxx' }).then(res => console.log(res));
@@ -7,7 +7,7 @@ function createOfflinePaymentThemes() {
7
7
  container: {
8
8
  background: "#fff",
9
9
  borderRadius: 8,
10
- padding: size === Size.SMALL ? 16 : 20,
10
+ padding: size === Size.SMALL ? 14 : 16,
11
11
  width: "100%",
12
12
  maxWidth: "100%",
13
13
  boxSizing: "border-box",
@@ -17,7 +17,7 @@ function createOfflinePaymentThemes() {
17
17
  },
18
18
  },
19
19
  header: {
20
- marginBottom: size === Size.SMALL ? 20 : 24,
20
+ marginBottom: size === Size.SMALL ? 16 : 20,
21
21
  },
22
22
  backButton: {
23
23
  display: "flex",
@@ -27,19 +27,19 @@ function createOfflinePaymentThemes() {
27
27
  border: "none",
28
28
  color: "#1890ff",
29
29
  cursor: "pointer",
30
- fontSize: size === Size.SMALL ? 14 : 16,
30
+ fontSize: size === Size.SMALL ? 13 : 14,
31
31
  fontWeight: 500,
32
32
  },
33
33
  backIcon: {
34
34
  fontSize: size === Size.SMALL ? 16 : 18,
35
35
  },
36
36
  uploadSection: {
37
- marginBottom: size === Size.SMALL ? 24 : 32,
37
+ marginBottom: size === Size.SMALL ? 18 : 24,
38
38
  },
39
39
  uploadArea: {
40
40
  border: "2px dashed #D9D9D9",
41
41
  borderRadius: 8,
42
- padding: size === Size.SMALL ? "40px 20px" : "60px 30px",
42
+ padding: size === Size.SMALL ? "30px 16px" : "40px 20px",
43
43
  textAlign: "center",
44
44
  cursor: "pointer",
45
45
  background: "#FAFAFA",
@@ -52,36 +52,36 @@ function createOfflinePaymentThemes() {
52
52
  },
53
53
  },
54
54
  uploadIcon: {
55
- fontSize: size === Size.SMALL ? 48 : 64,
56
- marginBottom: size === Size.SMALL ? 12 : 16,
55
+ fontSize: size === Size.SMALL ? 40 : 52,
56
+ marginBottom: size === Size.SMALL ? 10 : 12,
57
57
  },
58
58
  uploadTitle: {
59
- fontSize: size === Size.SMALL ? 16 : 18,
59
+ fontSize: size === Size.SMALL ? 14 : 16,
60
60
  fontWeight: 600,
61
61
  color: "#222",
62
- marginBottom: size === Size.SMALL ? 8 : 12,
62
+ marginBottom: size === Size.SMALL ? 6 : 10,
63
63
  },
64
64
  uploadHint: {
65
- fontSize: size === Size.SMALL ? 13 : 14,
65
+ fontSize: size === Size.SMALL ? 12 : 13,
66
66
  color: "#999",
67
67
  },
68
68
  hiddenInput: {
69
69
  display: "none",
70
70
  },
71
71
  fileList: {
72
- marginTop: size === Size.SMALL ? 12 : 16,
72
+ marginTop: size === Size.SMALL ? 10 : 12,
73
73
  },
74
74
  fileItem: {
75
75
  display: "flex",
76
76
  alignItems: "center",
77
77
  justifyContent: "space-between",
78
- padding: size === Size.SMALL ? "8px 12px" : "10px 16px",
78
+ padding: size === Size.SMALL ? "6px 10px" : "8px 12px",
79
79
  background: "#F5F5F5",
80
80
  borderRadius: 6,
81
- marginBottom: 8,
81
+ marginBottom: 6,
82
82
  },
83
83
  fileName: {
84
- fontSize: size === Size.SMALL ? 13 : 14,
84
+ fontSize: size === Size.SMALL ? 12 : 13,
85
85
  color: "#222",
86
86
  },
87
87
  removeButton: {
@@ -98,14 +98,14 @@ function createOfflinePaymentThemes() {
98
98
  justifyContent: "center",
99
99
  },
100
100
  fieldSection: {
101
- marginBottom: size === Size.SMALL ? 20 : 24,
101
+ marginBottom: size === Size.SMALL ? 12 : 16,
102
102
  },
103
103
  fieldLabel: {
104
104
  display: "block",
105
- fontSize: size === Size.SMALL ? 14 : 16,
105
+ fontSize: size === Size.SMALL ? 13 : 14,
106
106
  fontWeight: 500,
107
107
  color: "#222",
108
- marginBottom: size === Size.SMALL ? 8 : 12,
108
+ marginBottom: size === Size.SMALL ? 6 : 10,
109
109
  },
110
110
  required: {
111
111
  color: "#ff4d4f",
@@ -116,11 +116,12 @@ function createOfflinePaymentThemes() {
116
116
  },
117
117
  input: {
118
118
  width: "100%",
119
- padding: size === Size.SMALL ? "10px 12px" : "12px 16px",
119
+ // 与左侧支付金额输入框保持一致高度
120
+ padding: size === Size.SMALL ? "8px 10px" : "10px 12px",
120
121
  borderRadius: 6,
121
122
  border: "1px solid #D9D9D9",
122
123
  outline: "none",
123
- fontSize: size === Size.SMALL ? 14 : 16,
124
+ fontSize: 14,
124
125
  boxSizing: "border-box",
125
126
  },
126
127
  inputError: {
@@ -139,25 +140,25 @@ function createOfflinePaymentThemes() {
139
140
  },
140
141
  calendarIcon: {
141
142
  position: "absolute",
142
- right: size === Size.SMALL ? 12 : 16,
143
+ right: size === Size.SMALL ? 8 : 10,
143
144
  top: "50%",
144
145
  transform: "translateY(-50%)",
145
- fontSize: size === Size.SMALL ? 16 : 18,
146
+ fontSize: size === Size.SMALL ? 12 : 14,
146
147
  pointerEvents: "none",
147
148
  },
148
149
  currencySuffix: {
149
150
  position: "absolute",
150
- right: size === Size.SMALL ? 12 : 16,
151
+ right: size === Size.SMALL ? 10 : 12,
151
152
  top: "50%",
152
153
  transform: "translateY(-50%)",
153
- fontSize: size === Size.SMALL ? 14 : 16,
154
+ fontSize: size === Size.SMALL ? 12 : 14,
154
155
  color: "#999",
155
156
  pointerEvents: "none",
156
157
  },
157
158
  buttonSection: {
158
159
  display: "flex",
159
- gap: size === Size.SMALL ? 12 : 16,
160
- marginTop: size === Size.SMALL ? 32 : 40,
160
+ gap: size === Size.SMALL ? 10 : 12,
161
+ marginTop: size === Size.SMALL ? 24 : 32,
161
162
  width: "100%",
162
163
  boxSizing: "border-box",
163
164
  // 响应式设计
@@ -168,12 +169,12 @@ function createOfflinePaymentThemes() {
168
169
  },
169
170
  cancelButton: {
170
171
  flex: 1,
171
- padding: size === Size.SMALL ? "10px 16px" : "12px 20px",
172
+ padding: size === Size.SMALL ? "8px 12px" : "10px 16px",
172
173
  borderRadius: 6,
173
174
  border: "1px solid #D9D9D9",
174
175
  background: "#fff",
175
176
  color: "#222",
176
- fontSize: size === Size.SMALL ? 14 : 16,
177
+ fontSize: size === Size.SMALL ? 12 : 14,
177
178
  fontWeight: 500,
178
179
  cursor: "pointer",
179
180
  minWidth: 0,
@@ -181,12 +182,12 @@ function createOfflinePaymentThemes() {
181
182
  },
182
183
  submitButton: {
183
184
  flex: 1,
184
- padding: size === Size.SMALL ? "10px 16px" : "12px 20px",
185
+ padding: size === Size.SMALL ? "8px 12px" : "10px 16px",
185
186
  borderRadius: 6,
186
187
  border: "none",
187
188
  background: "#1890ff",
188
189
  color: "#fff",
189
- fontSize: size === Size.SMALL ? 14 : 16,
190
+ fontSize: size === Size.SMALL ? 12 : 14,
190
191
  fontWeight: 600,
191
192
  cursor: "pointer",
192
193
  minWidth: 0,
@@ -146,13 +146,19 @@ export function OnlinePayment({
146
146
 
147
147
  <div style={theme.reminderTitle}>{t("温馨提示")}</div>
148
148
  <ul style={theme.reminderList}>
149
- <li>{t("支付金额须与申请金额一致,请勿多付、少付或分批提交。")}</li>
150
- <li>{t("请妥善保存回单并提供准确的支付时间,否则可能无法核验。")}</li>
151
- <li>
149
+ <li style={theme.listItem}>
150
+ {t("支付金额须与申请金额一致,请勿多付、少付或分批提交。")}
151
+ </li>
152
+ <li style={theme.listItem}>
153
+ {t("请妥善保存回单并提供准确的支付时间,否则可能无法核验。")}
154
+ </li>
155
+ <li style={theme.listItem}>
152
156
  {t("工作时间提交4小时内审核,非工作时间将顺延至下一个工作日。")}
153
157
  </li>
154
- <li>{t("如有问题,请联系平台客服。")}</li>
155
- <li>{t("预付充值将收取平台服务费,具体以官方为准。")}</li>
158
+ <li style={theme.listItem}>{t("如有问题,请联系平台客服。")}</li>
159
+ <li style={theme.listItem}>
160
+ {t("预付充值将收取平台服务费,具体以官方为准。")}
161
+ </li>
156
162
  </ul>
157
163
 
158
164
  {formState.error && <div style={theme.error}>{formState.error}</div>}
@@ -98,6 +98,12 @@ function createOnlinePaymentThemes() {
98
98
  color: "#6b7280",
99
99
  lineHeight: 1.6,
100
100
  textAlign: "left",
101
+ listStyleType: "disc",
102
+ listStylePosition: "outside",
103
+ },
104
+ listItem: {
105
+ listStyleType: "disc",
106
+ listStylePosition: "outside",
101
107
  },
102
108
  error: {
103
109
  color: "#ff4d4f",
@@ -1,7 +1,8 @@
1
1
  import { useState, useEffect } from "preact/hooks";
2
2
  import { t } from "@/local";
3
3
  import { getSelectPaymentTheme } from "./theme";
4
- import { getOfflineChannelMap } from "@/api";
4
+ import { getOfflineChannelMap, calculateBusinessExchangeRate } from "@/api";
5
+ import { useRef } from "preact/hooks";
5
6
 
6
7
  interface SelectPaymentProps {
7
8
  onChannelChange: (channel: string, paymentType: string) => void;
@@ -39,6 +40,34 @@ export function SelectPayment({
39
40
  amountError: "",
40
41
  });
41
42
 
43
+ const [bizRate, setBizRate] = useState<{
44
+ exchangeRate: number;
45
+ amount: number;
46
+ currency: string;
47
+ consistent?: boolean;
48
+ } | null>(null);
49
+
50
+ // 防抖定时器
51
+ const bizRateTimerRef = useRef<number | null>(null);
52
+
53
+ const triggerBizRate = () => {
54
+ if (!formState.amount || Number(formState.amount) <= 0) {
55
+ setBizRate(null);
56
+ return;
57
+ }
58
+ if (bizRateTimerRef.current) {
59
+ clearTimeout(bizRateTimerRef.current);
60
+ }
61
+ bizRateTimerRef.current = window.setTimeout(() => {
62
+ calculateBusinessExchangeRate({
63
+ currency: formState.currency,
64
+ amount: formState.amount,
65
+ })
66
+ .then((res) => setBizRate(res))
67
+ .catch(() => setBizRate(null));
68
+ }, 300);
69
+ };
70
+
42
71
  // 根据币种拉取线上/线下渠道映射,初始化默认选项
43
72
  useEffect(() => {
44
73
  getOfflineChannelMap({ currency: formState.currency }).then((res) => {
@@ -79,6 +108,7 @@ export function SelectPayment({
79
108
  const handleCurrencyChange = (currency: string) => {
80
109
  setFormState((s) => ({ ...s, currency }));
81
110
  onCurrencyChange(currency);
111
+ triggerBizRate();
82
112
  };
83
113
 
84
114
  // 处理金额变化
@@ -86,6 +116,8 @@ export function SelectPayment({
86
116
  const formattedAmount = formatAmountInput(amount);
87
117
  setFormState((s) => ({ ...s, amount: formattedAmount, amountError: "" }));
88
118
  onAmountChange(formattedAmount);
119
+ // 输入金额后触发汇率计算
120
+ triggerBizRate();
89
121
  };
90
122
 
91
123
  // 处理渠道变化
@@ -96,6 +128,8 @@ export function SelectPayment({
96
128
  paymentType,
97
129
  }));
98
130
  onChannelChange(channel, paymentType);
131
+ // 切换渠道也触发汇率计算
132
+ triggerBizRate();
99
133
  };
100
134
  return (
101
135
  <div style={theme.left}>
@@ -137,6 +171,22 @@ export function SelectPayment({
137
171
  <div style={theme.inputSuffixAbs}>{formState.currency}</div>
138
172
  </div>
139
173
  {amountError && <div style={theme.error}>{amountError}</div>}
174
+ {bizRate && bizRate.consistent === false && (
175
+ <div style={theme.bizRateBox}>
176
+ <div>{t("您的支付币种与默认币种不一致,将转换为默认币种入账")}</div>
177
+ <div>
178
+ {t("目前汇率为:")} {formState.currency}
179
+ {t(`→${bizRate.currency} = `)}
180
+ <span style={theme.bizRateStrong}>{bizRate.exchangeRate}</span>
181
+ </div>
182
+ <div>
183
+ {t("钱包预计增加")}
184
+ <span style={theme.bizRateStrong}>
185
+ {bizRate.currency} {bizRate.amount}
186
+ </span>
187
+ </div>
188
+ </div>
189
+ )}
140
190
 
141
191
  <div style={{ height: 20 }} />
142
192
  <div style={theme.fieldLabel}>{t("支付方式")}</div>
@@ -74,6 +74,14 @@ function createSelectPaymentThemes() {
74
74
  pointerEvents: "none",
75
75
  fontSize: size === Size.SMALL ? 12 : 13,
76
76
  },
77
+ bizRateBox: {
78
+ marginTop: 6,
79
+ color: "#666",
80
+ fontSize: size === Size.SMALL ? 12 : 13,
81
+ textAlign: "left",
82
+ lineHeight: 1.6,
83
+ },
84
+ bizRateStrong: { color: "#111", fontWeight: 600 },
77
85
  subTitle: {
78
86
  fontSize: size === Size.SMALL ? 12 : 14,
79
87
  marginTop: size === Size.SMALL ? 10 : 14,
@@ -5,6 +5,7 @@ import { t } from "@/local";
5
5
  import { getUploadTheme } from "./theme";
6
6
  import { getInitParams } from "@/utils/business";
7
7
  import { Size } from "@/types";
8
+ import uploadSvg from "@/image/upload.svg";
8
9
 
9
10
  interface UploadProps {
10
11
  value?: string[];
@@ -72,26 +73,26 @@ export const Upload: FunctionalComponent<UploadProps> = ({
72
73
  }}
73
74
  onClick={() => !disabled && fileInputRef.current?.click()}
74
75
  >
75
- <div
76
+ <img
77
+ src={uploadSvg}
78
+ alt="upload"
76
79
  style={{
77
- fontSize: size === Size.SMALL ? 32 : 48,
80
+ width: size === Size.SMALL ? 40 : 48,
81
+ height: size === Size.SMALL ? 40 : 48,
78
82
  marginBottom: size === Size.SMALL ? 8 : 12,
79
83
  }}
80
- >
81
- 📁
82
- </div>
84
+ />
83
85
  <div
84
86
  style={{
85
87
  color: "#222",
86
- fontSize: size === Size.SMALL ? 12 : 15,
88
+ fontSize: size === Size.SMALL ? 13 : 16,
87
89
  marginBottom: size === Size.SMALL ? 4 : 8,
88
90
  }}
89
91
  >
90
- {t("点击或拖拽文件到此处上传")}
92
+ {t("Please upload the bank transfer receipt")}
91
93
  </div>
92
94
  <div style={{ color: "#999", fontSize: size === Size.SMALL ? 12 : 13 }}>
93
- {t("支持 JPG、PNG、PDF 格式,单个文件不超过 20MB,最多上传")}{" "}
94
- {maxCount} {t("个文件")}
95
+ {t("Click or drag file to this area to upload")}
95
96
  </div>
96
97
  <input
97
98
  ref={fileInputRef}
@@ -0,0 +1 @@
1
+ <?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1760084950168" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="6759" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M932.579556 137.272889h-192V64.142222a9.159111 9.159111 0 0 0-9.130667-9.159111h-64a9.159111 9.159111 0 0 0-9.159111 9.159111v73.130667H365.738667V64.142222a9.159111 9.159111 0 0 0-9.159111-9.159111h-64a9.159111 9.159111 0 0 0-9.130667 9.159111v73.130667h-192c-20.224 0-36.579556 16.355556-36.579556 36.579555v758.869334c0 20.195556 16.355556 36.551111 36.579556 36.551111h841.130667c20.252444 0 36.579556-16.327111 36.579555-36.551111V173.852444c0-20.224-16.355556-36.579556-36.579555-36.579555zM886.897778 887.011556H137.159111V452.693333H886.897778v434.261334z m-749.710222-512V219.534222h146.289777v54.869334c0 5.034667 4.124444 9.130667 9.130667 9.130666h64a9.159111 9.159111 0 0 0 9.159111-9.130666V219.562667h292.551111v54.869333c0 5.034667 4.124444 9.130667 9.159111 9.130667h64a9.159111 9.159111 0 0 0 9.130667-9.130667V219.562667h146.289778v155.448889H137.159111z" p-id="6760"></path></svg>
@@ -0,0 +1 @@
1
+ <?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1760084964886" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7145" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M519.224889 451.498667a9.102222 9.102222 0 0 0-14.421333 0l-128 161.962666a9.102222 9.102222 0 0 0 7.224888 14.734223h84.451556v277.048888c0 5.006222 4.096 9.102222 9.130667 9.102223h68.551111a9.159111 9.159111 0 0 0 9.159111-9.102223v-276.935111H640a9.102222 9.102222 0 0 0 7.224889-14.734222l-128-162.076444z" p-id="7146" fill="#1677ff"></path><path d="M854.186667 345.742222C801.848889 207.644444 668.444444 109.511111 512.227556 109.511111S222.634667 207.559111 170.296889 345.6A228.693333 228.693333 0 0 0 0 566.670222a228.494222 228.494222 0 0 0 228.465778 228.579556h45.795555a9.159111 9.159111 0 0 0 9.159111-9.159111v-68.551111a9.159111 9.159111 0 0 0-9.130666-9.159112H228.465778a140.885333 140.885333 0 0 1-101.717334-43.064888 141.653333 141.653333 0 0 1-39.879111-103.537778 140.600889 140.600889 0 0 1 29.923556-82.403556 143.217778 143.217778 0 0 1 75.548444-49.948444l43.320889-11.320889 15.872-41.813333a280.632889 280.632889 0 0 1 100.693334-129.479112 276.707556 276.707556 0 0 1 160-50.545777c57.742222 0 113.038222 17.493333 160 50.517333a280.832 280.832 0 0 1 100.693333 129.479111l15.758222 41.728 43.207111 11.434667a142.364444 142.364444 0 0 1 105.244445 137.244444c0 37.831111-14.734222 73.500444-41.472 100.238222a140.572444 140.572444 0 0 1-100.124445 41.472h-45.795555a9.159111 9.159111 0 0 0-9.159111 9.159112v68.551111c0 5.034667 4.096 9.159111 9.130666 9.159111h45.824A228.494222 228.494222 0 0 0 1024 566.641778a228.608 228.608 0 0 0-169.813333-220.899556z" p-id="7147" fill="#1677ff"></path></svg>