best-unit 2.0.17 → 2.1.4
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/dist/best-unit.cjs +18 -18
- package/dist/best-unit.js +1745 -1489
- package/dist/types/global.d.ts +4 -4
- package/package.json +1 -1
- package/src/api/index.ts +84 -36
- package/src/components/business/payment-sdk/offline-detail/index.tsx +35 -6
- package/src/components/business/payment-sdk/offline-payment/fund-allocation.tsx +213 -0
- package/src/components/business/payment-sdk/offline-payment/index.tsx +31 -1
- package/src/components/business/payment-sdk/offline-payment/theme.tsx +39 -0
- package/src/demo/App.tsx +16 -15
- package/src/local/en.ts +11 -0
- package/src/local/zh.ts +9 -0
- package/src/types/global.d.ts +4 -4
- package/src/utils/business/index.ts +3 -3
package/dist/types/global.d.ts
CHANGED
|
@@ -49,7 +49,7 @@ export interface InitParams {
|
|
|
49
49
|
locale?: Locale;
|
|
50
50
|
env: Env;
|
|
51
51
|
size?: Size;
|
|
52
|
-
|
|
52
|
+
theme_config?: ThemeConfig;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
export interface ThemeConfig {
|
|
@@ -81,7 +81,7 @@ export declare function initFundUnit(params: {
|
|
|
81
81
|
locale?: Locale;
|
|
82
82
|
env: Env;
|
|
83
83
|
size?: Size;
|
|
84
|
-
|
|
84
|
+
theme_config?: ThemeConfig;
|
|
85
85
|
}): {
|
|
86
86
|
token: string;
|
|
87
87
|
merchantId?: string;
|
|
@@ -91,7 +91,7 @@ export declare function initFundUnit(params: {
|
|
|
91
91
|
locale: Locale;
|
|
92
92
|
env: Env;
|
|
93
93
|
size?: Size;
|
|
94
|
-
|
|
94
|
+
theme_config?: ThemeConfig;
|
|
95
95
|
};
|
|
96
96
|
|
|
97
97
|
export declare function getBalanceData(): Promise<BalanceData>;
|
|
@@ -120,7 +120,7 @@ declare global {
|
|
|
120
120
|
locale?: "zh" | "en";
|
|
121
121
|
theme?: Theme;
|
|
122
122
|
size?: Size;
|
|
123
|
-
|
|
123
|
+
theme_config?: ThemeConfig;
|
|
124
124
|
env: Env;
|
|
125
125
|
}) => void;
|
|
126
126
|
getBalanceData: () => any;
|
package/package.json
CHANGED
package/src/api/index.ts
CHANGED
|
@@ -1,39 +1,80 @@
|
|
|
1
1
|
import { http } from "./axiosInstance";
|
|
2
2
|
import type { AxiosProgressEvent } from "axios";
|
|
3
|
-
|
|
4
|
-
export
|
|
3
|
+
|
|
4
|
+
export interface MerchantBalanceItem {
|
|
5
|
+
fundBalanceId: string;
|
|
6
|
+
merchantId: number;
|
|
7
|
+
bizType: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export const getAllBalances = async () => {
|
|
5
11
|
const fundUnitParams = JSON.parse(
|
|
6
|
-
sessionStorage.getItem("fund_unit_params") || "{}"
|
|
12
|
+
sessionStorage.getItem("fund_unit_params") || "{}",
|
|
7
13
|
);
|
|
8
14
|
return http()
|
|
9
|
-
.
|
|
10
|
-
|
|
11
|
-
merchant_id: fundUnitParams.merchantId,
|
|
12
|
-
biz_type: fundUnitParams.bizType,
|
|
13
|
-
fund_balance_id: fundUnitParams.fundBalanceId,
|
|
14
|
-
},
|
|
15
|
+
.post("/merchant/all-balances", {
|
|
16
|
+
merchant_id: Number(fundUnitParams.merchantId),
|
|
15
17
|
})
|
|
16
18
|
.then((res) => {
|
|
17
|
-
const
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
isCredit: data.credit_config ? true : false,
|
|
28
|
-
availableAmount: data.available_amount,
|
|
29
|
-
currBalance: data.curr_balance,
|
|
30
|
-
pendingAmount: data.pending_amount,
|
|
31
|
-
status: data.status,
|
|
32
|
-
createdAt: data.created_at,
|
|
33
|
-
};
|
|
34
|
-
sessionStorage.setItem("balanceData", JSON.stringify(balanceData));
|
|
35
|
-
return balanceData;
|
|
19
|
+
const allBalances: MerchantBalanceItem[] = (
|
|
20
|
+
Array.isArray(res?.data) ? res.data : []
|
|
21
|
+
).map((item: any) => ({
|
|
22
|
+
fundBalanceId: String(item.fund_balance_id || ""),
|
|
23
|
+
merchantId: Number(item.merchant_id || 0),
|
|
24
|
+
bizType: String(item.biz_type || ""),
|
|
25
|
+
}));
|
|
26
|
+
|
|
27
|
+
sessionStorage.setItem("allBalances", JSON.stringify(allBalances));
|
|
28
|
+
return allBalances;
|
|
36
29
|
});
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// 获取余额
|
|
33
|
+
export async function getBalance() {
|
|
34
|
+
const fundUnitParams = JSON.parse(
|
|
35
|
+
sessionStorage.getItem("fund_unit_params") || "{}",
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const balanceRequest = http().get("/balance", {
|
|
39
|
+
params: {
|
|
40
|
+
merchant_id: fundUnitParams.merchantId,
|
|
41
|
+
biz_type: fundUnitParams.bizType,
|
|
42
|
+
fund_balance_id: fundUnitParams.fundBalanceId,
|
|
43
|
+
},
|
|
44
|
+
});
|
|
45
|
+
const [balanceRes, allBalancesRes] = await Promise.allSettled([
|
|
46
|
+
balanceRequest,
|
|
47
|
+
getAllBalances(),
|
|
48
|
+
]);
|
|
49
|
+
|
|
50
|
+
if (balanceRes.status !== "fulfilled") {
|
|
51
|
+
throw balanceRes.reason;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const data = balanceRes.value.data;
|
|
55
|
+
const balanceData = {
|
|
56
|
+
fundBalanceId: data.fund_balance_id,
|
|
57
|
+
merchantId: data.merchant_id,
|
|
58
|
+
bizType: data.biz_type,
|
|
59
|
+
currency: data.currency,
|
|
60
|
+
totalAmount: data.total_amount,
|
|
61
|
+
frozenAmount: data.frozen_amount,
|
|
62
|
+
creditLimit: data.credit_config?.credit_limit,
|
|
63
|
+
creditUsed: data.credit_used,
|
|
64
|
+
isCredit: data.credit_config ? true : false,
|
|
65
|
+
availableAmount: data.available_amount,
|
|
66
|
+
currBalance: data.curr_balance,
|
|
67
|
+
pendingAmount: data.pending_amount,
|
|
68
|
+
status: data.status,
|
|
69
|
+
createdAt: data.created_at,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
if (allBalancesRes.status !== "fulfilled") {
|
|
73
|
+
console.error("获取子业务账户列表失败:", allBalancesRes.reason);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
sessionStorage.setItem("balanceData", JSON.stringify(balanceData));
|
|
77
|
+
return balanceData;
|
|
37
78
|
}
|
|
38
79
|
|
|
39
80
|
// 获取所有字典
|
|
@@ -49,7 +90,7 @@ export const getAllDicts = async () => {
|
|
|
49
90
|
// 获取所有币种
|
|
50
91
|
export const getAllCurrencies = async () => {
|
|
51
92
|
const fundUnitParams = JSON.parse(
|
|
52
|
-
sessionStorage.getItem("fund_unit_params") || "{}"
|
|
93
|
+
sessionStorage.getItem("fund_unit_params") || "{}",
|
|
53
94
|
);
|
|
54
95
|
return http()
|
|
55
96
|
.get("/currency", {
|
|
@@ -70,7 +111,7 @@ export const getAllCurrencies = async () => {
|
|
|
70
111
|
// 上传文件
|
|
71
112
|
export const uploadFile = async (
|
|
72
113
|
file: any,
|
|
73
|
-
onProgress?: (percent: number) => void
|
|
114
|
+
onProgress?: (percent: number) => void,
|
|
74
115
|
) => {
|
|
75
116
|
return http()
|
|
76
117
|
.post("/oss/upload", file, {
|
|
@@ -80,7 +121,7 @@ export const uploadFile = async (
|
|
|
80
121
|
onUploadProgress: (progressEvent: AxiosProgressEvent) => {
|
|
81
122
|
if (progressEvent.total) {
|
|
82
123
|
const percent = Math.round(
|
|
83
|
-
(progressEvent.loaded * 100) / progressEvent.total
|
|
124
|
+
(progressEvent.loaded * 100) / progressEvent.total,
|
|
84
125
|
);
|
|
85
126
|
onProgress && onProgress(percent);
|
|
86
127
|
}
|
|
@@ -94,7 +135,7 @@ export const uploadFile = async (
|
|
|
94
135
|
// 创建离线充值
|
|
95
136
|
export const createOfflineRecharge = async (data: any) => {
|
|
96
137
|
const fundUnitParams = JSON.parse(
|
|
97
|
-
sessionStorage.getItem("fund_unit_params") || "{}"
|
|
138
|
+
sessionStorage.getItem("fund_unit_params") || "{}",
|
|
98
139
|
);
|
|
99
140
|
|
|
100
141
|
const params = {
|
|
@@ -111,6 +152,13 @@ export const createOfflineRecharge = async (data: any) => {
|
|
|
111
152
|
submit_amount: data.submitAmount,
|
|
112
153
|
submit_currency: data.submitCurrency,
|
|
113
154
|
voucher_urls: data.voucherUrls,
|
|
155
|
+
use_fund_allocation: Boolean(data.useFundAllocation),
|
|
156
|
+
allocation_ratios: Boolean(data.useFundAllocation)
|
|
157
|
+
? (data.allocationRatios || []).map((item: any) => ({
|
|
158
|
+
fund_balance_id: item.fundBalanceId,
|
|
159
|
+
ratio: String(item.ratio),
|
|
160
|
+
}))
|
|
161
|
+
: [],
|
|
114
162
|
metadata:
|
|
115
163
|
fundUnitParams.bizType === "ad"
|
|
116
164
|
? {
|
|
@@ -124,7 +172,7 @@ export const createOfflineRecharge = async (data: any) => {
|
|
|
124
172
|
// 创建在线充值
|
|
125
173
|
export const createOnlineRecharge = async (data: any) => {
|
|
126
174
|
const fundUnitParams = JSON.parse(
|
|
127
|
-
sessionStorage.getItem("fund_unit_params") || "{}"
|
|
175
|
+
sessionStorage.getItem("fund_unit_params") || "{}",
|
|
128
176
|
);
|
|
129
177
|
|
|
130
178
|
const params = {
|
|
@@ -177,7 +225,7 @@ export const calcPaymentAmount = async (data: CalcPaymentAmountParams) => {
|
|
|
177
225
|
// /sdk/channel/info
|
|
178
226
|
export const getChannelInfo = async (data: { code: string }) => {
|
|
179
227
|
const fundUnitParams = JSON.parse(
|
|
180
|
-
sessionStorage.getItem("fund_unit_params") || "{}"
|
|
228
|
+
sessionStorage.getItem("fund_unit_params") || "{}",
|
|
181
229
|
);
|
|
182
230
|
return http()
|
|
183
231
|
.get("/channel/info", {
|
|
@@ -194,7 +242,7 @@ export const getChannelInfo = async (data: { code: string }) => {
|
|
|
194
242
|
// 获取币种对应渠道
|
|
195
243
|
export const getOfflineChannelMap = async (data: { currency: string }) => {
|
|
196
244
|
const fundUnitParams = JSON.parse(
|
|
197
|
-
sessionStorage.getItem("fund_unit_params") || "{}"
|
|
245
|
+
sessionStorage.getItem("fund_unit_params") || "{}",
|
|
198
246
|
);
|
|
199
247
|
return http()
|
|
200
248
|
.get("/offline/channel/map", {
|
|
@@ -228,7 +276,7 @@ export const calculateBusinessExchangeRate = async (data: {
|
|
|
228
276
|
amount: string;
|
|
229
277
|
}) => {
|
|
230
278
|
const fundUnitParams = JSON.parse(
|
|
231
|
-
sessionStorage.getItem("fund_unit_params") || "{}"
|
|
279
|
+
sessionStorage.getItem("fund_unit_params") || "{}",
|
|
232
280
|
);
|
|
233
281
|
return http()
|
|
234
282
|
.post("/business_exchange_rate/calculate", {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
|
-
import { t } from "@/local";
|
|
1
|
+
import { getCurrentLocale, t } from "@/local";
|
|
2
2
|
import { getOfflineDetailTheme } from "./theme";
|
|
3
3
|
import { useEffect, useState } from "preact/hooks";
|
|
4
4
|
import { getChannelInfo } from "@/api";
|
|
5
|
+
import { Locale } from "@/types";
|
|
6
|
+
import { getInitParams } from "@/utils/business";
|
|
5
7
|
|
|
6
8
|
interface OfflineDetailProps {
|
|
7
9
|
onConfirm: () => void;
|
|
@@ -11,6 +13,8 @@ interface OfflineDetailProps {
|
|
|
11
13
|
export function OfflineDetail({ onConfirm, channelCode }: OfflineDetailProps) {
|
|
12
14
|
const theme = getOfflineDetailTheme();
|
|
13
15
|
const [detailLines, setDetailLines] = useState<string[]>([]);
|
|
16
|
+
const merchantId = getInitParams<string>("merchantId");
|
|
17
|
+
const reminderAccentStyle = { color: "#ff4d4f", fontWeight: 600 };
|
|
14
18
|
|
|
15
19
|
useEffect(() => {
|
|
16
20
|
if (!channelCode) return;
|
|
@@ -77,12 +81,37 @@ export function OfflineDetail({ onConfirm, channelCode }: OfflineDetailProps) {
|
|
|
77
81
|
|
|
78
82
|
<div style={theme.reminderTitle}>{t("温馨提示")}</div>
|
|
79
83
|
<ul style={theme.reminderList}>
|
|
80
|
-
<li>
|
|
81
|
-
|
|
84
|
+
<li>
|
|
85
|
+
{t("我们强烈建议,转账时在备注中输入您的ID:")}
|
|
86
|
+
<span style={reminderAccentStyle}>{merchantId}</span>
|
|
87
|
+
{t("。这可以")}
|
|
88
|
+
<span style={reminderAccentStyle}>{t("加速资金审核")}</span>
|
|
89
|
+
{getCurrentLocale() === Locale.ZH ? "。" : "."}
|
|
90
|
+
</li>
|
|
91
|
+
<li>
|
|
92
|
+
{t(
|
|
93
|
+
"创建付款时请勿填写敏感备注,例如:topup ads、order等、建议填写数字代码",
|
|
94
|
+
)}
|
|
95
|
+
</li>
|
|
96
|
+
<li>
|
|
97
|
+
{t("请填写正确的支付金额、币种、支付时间,相同的交易请勿重复提交。")}
|
|
98
|
+
</li>
|
|
82
99
|
<li>{t("请上传交易凭证,否则无法核验并及时添加余额。")}</li>
|
|
83
|
-
<li>
|
|
84
|
-
|
|
85
|
-
|
|
100
|
+
<li>
|
|
101
|
+
{t(
|
|
102
|
+
"提交成功后根据实际到账金额添加余额。工作时间提交的转账当日处理,非工作时间提交的转账将在下一个工作日处理。",
|
|
103
|
+
)}
|
|
104
|
+
</li>
|
|
105
|
+
<li>
|
|
106
|
+
{t(
|
|
107
|
+
"通过平台互转(例如payoneer转payoneer)付款方式可能会有10分钟延迟,请耐心等待。",
|
|
108
|
+
)}
|
|
109
|
+
</li>
|
|
110
|
+
<li>
|
|
111
|
+
{t(
|
|
112
|
+
"广告款项若入账金额为美金外其他币种额外扣除0.6%手续费,其中PayPal支付方式额外扣除2.5%手续费。",
|
|
113
|
+
)}
|
|
114
|
+
</li>
|
|
86
115
|
<li>{t("如有问题,请联系客服。")}</li>
|
|
87
116
|
</ul>
|
|
88
117
|
</div>
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { useEffect, useState } from "preact/hooks";
|
|
2
|
+
import { t } from "@/local";
|
|
3
|
+
import { getAllBalances, type MerchantBalanceItem } from "@/api";
|
|
4
|
+
|
|
5
|
+
interface FundAllocationSectionProps {
|
|
6
|
+
theme: any;
|
|
7
|
+
useFundAllocation: boolean;
|
|
8
|
+
subBusinesses: MerchantBalanceItem[];
|
|
9
|
+
subBizPercentages: Record<string, string>;
|
|
10
|
+
subBizError: string;
|
|
11
|
+
onUseFundAllocationChange: (value: boolean) => void;
|
|
12
|
+
onPercentageChange: (bizType: string, value: string) => void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getBizTypeLabel(bizType: string) {
|
|
16
|
+
if (bizType === "ad") return "Advertising";
|
|
17
|
+
if (bizType === "fulfill") return "Fulfillment";
|
|
18
|
+
if (bizType === "chargewipe") return "ChargeWipe";
|
|
19
|
+
return bizType;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function useFundAllocation() {
|
|
23
|
+
const [subBusinesses, setSubBusinesses] = useState<MerchantBalanceItem[]>([]);
|
|
24
|
+
const [useFundAllocation, setUseFundAllocation] = useState(false);
|
|
25
|
+
const [subBizPercentages, setSubBizPercentages] = useState<
|
|
26
|
+
Record<string, string>
|
|
27
|
+
>({});
|
|
28
|
+
const [subBizError, setSubBizError] = useState("");
|
|
29
|
+
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
let mounted = true;
|
|
32
|
+
|
|
33
|
+
const applySubBusinesses = (items: MerchantBalanceItem[]) => {
|
|
34
|
+
if (!mounted) return;
|
|
35
|
+
|
|
36
|
+
setSubBusinesses(items);
|
|
37
|
+
setSubBizPercentages((prev) =>
|
|
38
|
+
items.reduce(
|
|
39
|
+
(acc, item) => ({
|
|
40
|
+
...acc,
|
|
41
|
+
[item.bizType]: prev[item.bizType] || "",
|
|
42
|
+
}),
|
|
43
|
+
{} as Record<string, string>,
|
|
44
|
+
),
|
|
45
|
+
);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const cached = JSON.parse(sessionStorage.getItem("allBalances") || "[]");
|
|
49
|
+
if (Array.isArray(cached) && cached.length > 0) {
|
|
50
|
+
applySubBusinesses(cached);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
getAllBalances()
|
|
54
|
+
.then((items) => {
|
|
55
|
+
applySubBusinesses(items);
|
|
56
|
+
})
|
|
57
|
+
.catch(() => {
|
|
58
|
+
if (!Array.isArray(cached)) {
|
|
59
|
+
applySubBusinesses([]);
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
return () => {
|
|
64
|
+
mounted = false;
|
|
65
|
+
};
|
|
66
|
+
}, []);
|
|
67
|
+
|
|
68
|
+
const handleUseFundAllocationChange = (value: boolean) => {
|
|
69
|
+
setUseFundAllocation(value);
|
|
70
|
+
setSubBizError("");
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const handlePercentageChange = (bizType: string, value: string) => {
|
|
74
|
+
setSubBizPercentages((prev) => ({
|
|
75
|
+
...prev,
|
|
76
|
+
[bizType]: value,
|
|
77
|
+
}));
|
|
78
|
+
setSubBizError("");
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const validateFundAllocation = () => {
|
|
82
|
+
if (!useFundAllocation || subBusinesses.length === 0) {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const hasEmptyValue = subBusinesses.some(
|
|
87
|
+
(item) => !subBizPercentages[item.bizType]?.trim(),
|
|
88
|
+
);
|
|
89
|
+
const hasInvalidValue = subBusinesses.some((item) => {
|
|
90
|
+
const value = Number(subBizPercentages[item.bizType]);
|
|
91
|
+
return Number.isNaN(value) || value < 0 || value > 100;
|
|
92
|
+
});
|
|
93
|
+
const totalPercentage = subBusinesses.reduce(
|
|
94
|
+
(sum, item) => sum + Number(subBizPercentages[item.bizType] || 0),
|
|
95
|
+
0,
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
if (hasEmptyValue) {
|
|
99
|
+
setSubBizError(t("请输入子业务占比"));
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (hasInvalidValue || Math.abs(totalPercentage - 100) > 0.0001) {
|
|
104
|
+
setSubBizError(t("子业务占比总和必须为100%"));
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
setSubBizError("");
|
|
109
|
+
return true;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
const buildAllocationRatios = () => {
|
|
113
|
+
if (!useFundAllocation) {
|
|
114
|
+
return [];
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return subBusinesses.map((item) => ({
|
|
118
|
+
fundBalanceId: item.fundBalanceId,
|
|
119
|
+
ratio: String(subBizPercentages[item.bizType] || ""),
|
|
120
|
+
}));
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
return {
|
|
124
|
+
subBusinesses,
|
|
125
|
+
useFundAllocation,
|
|
126
|
+
subBizPercentages,
|
|
127
|
+
subBizError,
|
|
128
|
+
setUseFundAllocation: handleUseFundAllocationChange,
|
|
129
|
+
setSubBizPercentage: handlePercentageChange,
|
|
130
|
+
validateFundAllocation,
|
|
131
|
+
buildAllocationRatios,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export function FundAllocationSection({
|
|
136
|
+
theme,
|
|
137
|
+
useFundAllocation,
|
|
138
|
+
subBusinesses,
|
|
139
|
+
subBizPercentages,
|
|
140
|
+
subBizError,
|
|
141
|
+
onUseFundAllocationChange,
|
|
142
|
+
onPercentageChange,
|
|
143
|
+
}: FundAllocationSectionProps) {
|
|
144
|
+
return (
|
|
145
|
+
<>
|
|
146
|
+
<div style={theme.fieldSection}>
|
|
147
|
+
<label style={theme.fieldLabel}>{t("是否使用资金分配")}</label>
|
|
148
|
+
<div style={theme.radioGroup}>
|
|
149
|
+
<label style={theme.radioOption}>
|
|
150
|
+
<input
|
|
151
|
+
type="radio"
|
|
152
|
+
name="useFundAllocation"
|
|
153
|
+
checked={useFundAllocation}
|
|
154
|
+
onChange={() => onUseFundAllocationChange(true)}
|
|
155
|
+
/>
|
|
156
|
+
<span>{t("是")}</span>
|
|
157
|
+
</label>
|
|
158
|
+
<label style={theme.radioOption}>
|
|
159
|
+
<input
|
|
160
|
+
type="radio"
|
|
161
|
+
name="useFundAllocation"
|
|
162
|
+
checked={!useFundAllocation}
|
|
163
|
+
onChange={() => onUseFundAllocationChange(false)}
|
|
164
|
+
/>
|
|
165
|
+
<span>{t("否")}</span>
|
|
166
|
+
</label>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
|
|
170
|
+
{useFundAllocation && subBusinesses.length > 0 && (
|
|
171
|
+
<div style={theme.fieldSection}>
|
|
172
|
+
<div style={theme.subBizGrid}>
|
|
173
|
+
{subBusinesses.map((item) => (
|
|
174
|
+
<div
|
|
175
|
+
key={item.fundBalanceId || item.bizType}
|
|
176
|
+
style={theme.subBizField}
|
|
177
|
+
>
|
|
178
|
+
<label style={theme.fieldLabel}>
|
|
179
|
+
{getBizTypeLabel(item.bizType)}
|
|
180
|
+
{" (%)"}
|
|
181
|
+
</label>
|
|
182
|
+
<input
|
|
183
|
+
type="number"
|
|
184
|
+
min="0"
|
|
185
|
+
max="100"
|
|
186
|
+
step="0.01"
|
|
187
|
+
placeholder="0"
|
|
188
|
+
value={subBizPercentages[item.bizType] || ""}
|
|
189
|
+
onChange={(e) => {
|
|
190
|
+
onPercentageChange(
|
|
191
|
+
item.bizType,
|
|
192
|
+
(e.target as HTMLInputElement).value,
|
|
193
|
+
);
|
|
194
|
+
}}
|
|
195
|
+
style={{
|
|
196
|
+
...theme.input,
|
|
197
|
+
...(subBizError ? theme.inputError : {}),
|
|
198
|
+
}}
|
|
199
|
+
/>
|
|
200
|
+
</div>
|
|
201
|
+
))}
|
|
202
|
+
</div>
|
|
203
|
+
<div style={theme.subBizHint}>
|
|
204
|
+
{`${subBusinesses
|
|
205
|
+
.map((item) => getBizTypeLabel(item.bizType))
|
|
206
|
+
.join(" + ")} ${t("must total 100%")}`}
|
|
207
|
+
</div>
|
|
208
|
+
{subBizError && <div style={theme.error}>{subBizError}</div>}
|
|
209
|
+
</div>
|
|
210
|
+
)}
|
|
211
|
+
</>
|
|
212
|
+
);
|
|
213
|
+
}
|
|
@@ -5,6 +5,10 @@ import { DatePicker } from "@/components/common/date-picker";
|
|
|
5
5
|
import { Upload } from "@/components/common/upload";
|
|
6
6
|
import { createOfflineRecharge } from "@/api";
|
|
7
7
|
import { message } from "@/components/common/message";
|
|
8
|
+
import {
|
|
9
|
+
FundAllocationSection,
|
|
10
|
+
useFundAllocation,
|
|
11
|
+
} from "./fund-allocation";
|
|
8
12
|
|
|
9
13
|
interface OfflinePaymentProps {
|
|
10
14
|
onBack: () => void;
|
|
@@ -25,11 +29,21 @@ export function OfflinePayment({
|
|
|
25
29
|
}: OfflinePaymentProps) {
|
|
26
30
|
const theme = getOfflinePaymentTheme();
|
|
27
31
|
const isDark = theme.container.background === "#181A20";
|
|
32
|
+
const {
|
|
33
|
+
subBusinesses,
|
|
34
|
+
useFundAllocation: isUsingFundAllocation,
|
|
35
|
+
subBizPercentages,
|
|
36
|
+
subBizError,
|
|
37
|
+
setUseFundAllocation,
|
|
38
|
+
setSubBizPercentage,
|
|
39
|
+
validateFundAllocation,
|
|
40
|
+
buildAllocationRatios,
|
|
41
|
+
} = useFundAllocation();
|
|
28
42
|
|
|
29
43
|
// 默认选择当日日期,格式为 YYYY-MM-DD
|
|
30
44
|
const today = new Date();
|
|
31
45
|
const todayStr = `${today.getFullYear()}-${String(
|
|
32
|
-
today.getMonth() + 1
|
|
46
|
+
today.getMonth() + 1,
|
|
33
47
|
).padStart(2, "0")}-${String(today.getDate()).padStart(2, "0")}`;
|
|
34
48
|
|
|
35
49
|
const [formState, setFormState] = useState({
|
|
@@ -85,6 +99,10 @@ export function OfflinePayment({
|
|
|
85
99
|
}));
|
|
86
100
|
}
|
|
87
101
|
|
|
102
|
+
if (!validateFundAllocation()) {
|
|
103
|
+
valid = false;
|
|
104
|
+
}
|
|
105
|
+
|
|
88
106
|
if (!valid) return;
|
|
89
107
|
|
|
90
108
|
try {
|
|
@@ -96,6 +114,8 @@ export function OfflinePayment({
|
|
|
96
114
|
payerName: formState.payerName,
|
|
97
115
|
submitAmount: amount,
|
|
98
116
|
submitCurrency: currency,
|
|
117
|
+
useFundAllocation: isUsingFundAllocation,
|
|
118
|
+
allocationRatios: buildAllocationRatios(),
|
|
99
119
|
});
|
|
100
120
|
message.success(t("提交成功"));
|
|
101
121
|
onCancel();
|
|
@@ -226,6 +246,16 @@ export function OfflinePayment({
|
|
|
226
246
|
)}
|
|
227
247
|
</div>
|
|
228
248
|
|
|
249
|
+
<FundAllocationSection
|
|
250
|
+
theme={theme}
|
|
251
|
+
useFundAllocation={isUsingFundAllocation}
|
|
252
|
+
subBusinesses={subBusinesses}
|
|
253
|
+
subBizPercentages={subBizPercentages}
|
|
254
|
+
subBizError={subBizError}
|
|
255
|
+
onUseFundAllocationChange={setUseFundAllocation}
|
|
256
|
+
onPercentageChange={setSubBizPercentage}
|
|
257
|
+
/>
|
|
258
|
+
|
|
229
259
|
{/* 操作按钮 */}
|
|
230
260
|
<div style={theme.buttonSection}>
|
|
231
261
|
<button type="button" onClick={onCancel} style={theme.cancelButton}>
|
|
@@ -100,6 +100,37 @@ function createOfflinePaymentThemes() {
|
|
|
100
100
|
fieldSection: {
|
|
101
101
|
marginBottom: size === Size.SMALL ? 12 : 16,
|
|
102
102
|
},
|
|
103
|
+
radioGroup: {
|
|
104
|
+
display: "flex",
|
|
105
|
+
alignItems: "center",
|
|
106
|
+
gap: size === Size.SMALL ? 16 : 20,
|
|
107
|
+
flexWrap: "wrap",
|
|
108
|
+
},
|
|
109
|
+
radioOption: {
|
|
110
|
+
display: "inline-flex",
|
|
111
|
+
alignItems: "center",
|
|
112
|
+
gap: 6,
|
|
113
|
+
color: "#222",
|
|
114
|
+
fontSize: size === Size.SMALL ? 13 : 14,
|
|
115
|
+
cursor: "pointer",
|
|
116
|
+
},
|
|
117
|
+
subBizGrid: {
|
|
118
|
+
display: "grid",
|
|
119
|
+
gridTemplateColumns: "repeat(2, minmax(0, 1fr))",
|
|
120
|
+
gap: size === Size.SMALL ? 10 : 12,
|
|
121
|
+
width: "100%",
|
|
122
|
+
"@media (max-width: 600px)": {
|
|
123
|
+
gridTemplateColumns: "1fr",
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
subBizField: {
|
|
127
|
+
minWidth: 0,
|
|
128
|
+
},
|
|
129
|
+
subBizHint: {
|
|
130
|
+
marginTop: 8,
|
|
131
|
+
color: "#666",
|
|
132
|
+
fontSize: size === Size.SMALL ? 12 : 13,
|
|
133
|
+
},
|
|
103
134
|
fieldLabel: {
|
|
104
135
|
display: "block",
|
|
105
136
|
fontSize: size === Size.SMALL ? 13 : 14,
|
|
@@ -240,6 +271,14 @@ function createOfflinePaymentThemes() {
|
|
|
240
271
|
...base.fieldLabel,
|
|
241
272
|
color: "#fff",
|
|
242
273
|
},
|
|
274
|
+
radioOption: {
|
|
275
|
+
...base.radioOption,
|
|
276
|
+
color: "#fff",
|
|
277
|
+
},
|
|
278
|
+
subBizHint: {
|
|
279
|
+
...base.subBizHint,
|
|
280
|
+
color: "#B5B8BE",
|
|
281
|
+
},
|
|
243
282
|
input: {
|
|
244
283
|
...base.input,
|
|
245
284
|
border: "1px solid #374151",
|