feat(game): 更新游戏规则并优化充值提现界面
- 在 api.type.ts 中为支付渠道、存款配置和取款字段添加新的接口属性 - 替换桌面端规则模态框中的纯文本内容为支持 Markdown 格式的组件 - 重构桌面端充值组件的 UI 结构,使用自定义按钮替代原生选择器 - 为取款组件添加最低最高金额验证及支付方式选择功能 - 更新英文本地化文件中的游戏规则内容,提供详细的玩法指南
This commit is contained in:
@@ -1,7 +1,6 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import {
|
||||
DEFAULT_CURRENCY_CODE,
|
||||
DEFAULT_WITHDRAW_CONFIG,
|
||||
QUICK_FIAT_AMOUNTS,
|
||||
WITHDRAW_EMAIL_PATTERN,
|
||||
@@ -41,15 +40,34 @@ function getCurrencyExchangeRate(
|
||||
return configuredRate && configuredRate > 0 ? configuredRate : fallbackRate
|
||||
}
|
||||
|
||||
function getActiveCurrencyCode(
|
||||
currencies: DepositWithdrawConfig['currencies'],
|
||||
selectedCurrencyCode: string,
|
||||
function toOptionalNumber(value: string) {
|
||||
if (value.trim().length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
const numericValue = Number(value)
|
||||
|
||||
return Number.isFinite(numericValue) ? numericValue : null
|
||||
}
|
||||
|
||||
function getChannelCurrencies(
|
||||
config: DepositWithdrawConfig,
|
||||
channel: DepositWithdrawConfig['withdraw']['payChannels'][number] | null,
|
||||
) {
|
||||
return (
|
||||
currencies.find((item) => item.code === selectedCurrencyCode) ??
|
||||
currencies[0] ??
|
||||
DEFAULT_WITHDRAW_CONFIG.currencies[0]
|
||||
if (!channel || channel.currencyCodes.length === 0) {
|
||||
return []
|
||||
}
|
||||
|
||||
const currencyMap = new Map(
|
||||
config.currencies.map((currency) => [currency.code, currency]),
|
||||
)
|
||||
|
||||
return channel.currencyCodes
|
||||
.map((currencyCode) => currencyMap.get(currencyCode))
|
||||
.filter(
|
||||
(currency): currency is DepositWithdrawConfig['currencies'][number] =>
|
||||
Boolean(currency),
|
||||
)
|
||||
}
|
||||
|
||||
function getNormalizedConfig(
|
||||
@@ -105,53 +123,73 @@ export function useWithdrawVm() {
|
||||
|
||||
const [amount, setAmountState] = useState(0)
|
||||
const [hasInitializedAmount, setHasInitializedAmount] = useState(false)
|
||||
const [currencyCode, setCurrencyCode] = useState(
|
||||
config.currencies[0]?.code ?? DEFAULT_CURRENCY_CODE,
|
||||
)
|
||||
const [currencyCode, setCurrencyCode] = useState('')
|
||||
const [paymentChannelCode, setPaymentChannelCode] = useState('')
|
||||
const [paymentType, setPaymentType] = useState('')
|
||||
const [bankCode, setBankCode] = useState('')
|
||||
const [holderName, setHolderName] = useState('')
|
||||
const [bankAccount, setBankAccount] = useState('')
|
||||
const [receiverEmail, setReceiverEmail] = useState('')
|
||||
const [receiverPhone, setReceiverPhone] = useState('')
|
||||
|
||||
const selectedCurrency = getActiveCurrencyCode(
|
||||
config.currencies,
|
||||
currencyCode,
|
||||
)
|
||||
const selectedRate = selectedCurrency.withdrawCoinsPerFiatValue || 1
|
||||
const selectedExchangeRate = getCurrencyExchangeRate(
|
||||
config,
|
||||
selectedCurrency.code,
|
||||
selectedRate,
|
||||
)
|
||||
const sortedPayChannels = useMemo(
|
||||
() =>
|
||||
[
|
||||
...(config.withdraw.payChannels.length > 0
|
||||
? config.withdraw.payChannels
|
||||
: config.payChannels),
|
||||
]
|
||||
[...config.withdraw.payChannels]
|
||||
.filter((channel) => channel.status === 1)
|
||||
.sort((left, right) => left.sort - right.sort),
|
||||
[config.payChannels, config.withdraw.payChannels],
|
||||
)
|
||||
const sortedBanks = useMemo(
|
||||
() =>
|
||||
[...config.withdraw.banks]
|
||||
.filter(
|
||||
(bank) =>
|
||||
bank.status === 1 &&
|
||||
(!bank.currencyCode || bank.currencyCode === selectedCurrency.code),
|
||||
)
|
||||
.sort((left, right) => left.sort - right.sort),
|
||||
[config.withdraw.banks, selectedCurrency.code],
|
||||
[config.withdraw.payChannels],
|
||||
)
|
||||
const availableBalance = Number(currentUser?.coin ?? 0)
|
||||
const maxWithdrawAmount = Math.max(0, Math.floor(availableBalance))
|
||||
const selectedPaymentChannel =
|
||||
sortedPayChannels.find((channel) => channel.code === paymentChannelCode) ??
|
||||
null
|
||||
const availablePaymentMethods = useMemo(
|
||||
() => selectedPaymentChannel?.methods ?? [],
|
||||
[selectedPaymentChannel],
|
||||
)
|
||||
const selectedPaymentMethod =
|
||||
availablePaymentMethods.find((method) => method.code === paymentType) ??
|
||||
null
|
||||
const availableCurrencies = useMemo(
|
||||
() => getChannelCurrencies(config, selectedPaymentChannel),
|
||||
[config, selectedPaymentChannel],
|
||||
)
|
||||
const selectedCurrency =
|
||||
availableCurrencies.find((item) => item.code === currencyCode) ?? null
|
||||
const selectedCurrencyCode = selectedCurrency?.code ?? ''
|
||||
const selectedRate = selectedCurrency?.withdrawCoinsPerFiatValue ?? 0
|
||||
const selectedExchangeRate = selectedCurrency
|
||||
? getCurrencyExchangeRate(config, selectedCurrency.code, selectedRate || 1)
|
||||
: 0
|
||||
const convertedFiatAmount =
|
||||
selectedExchangeRate > 0 ? amount * selectedExchangeRate : 0
|
||||
const selectedPaymentMinimumAmount = selectedPaymentMethod
|
||||
? toOptionalNumber(selectedPaymentMethod.minimumAmount)
|
||||
: null
|
||||
const selectedPaymentMaximumAmount = selectedPaymentMethod
|
||||
? toOptionalNumber(selectedPaymentMethod.maximumAmount)
|
||||
: null
|
||||
const amountBelowPaymentMinimum =
|
||||
amount > 0 &&
|
||||
selectedPaymentMinimumAmount !== null &&
|
||||
convertedFiatAmount < selectedPaymentMinimumAmount
|
||||
const amountAbovePaymentMaximum =
|
||||
amount > 0 &&
|
||||
selectedPaymentMaximumAmount !== null &&
|
||||
convertedFiatAmount > selectedPaymentMaximumAmount
|
||||
const sortedBanks = useMemo(
|
||||
() =>
|
||||
[...config.withdraw.banks]
|
||||
.filter(
|
||||
(bank) =>
|
||||
bank.status === 1 &&
|
||||
selectedCurrencyCode.length > 0 &&
|
||||
(!bank.currencyCode || bank.currencyCode === selectedCurrencyCode),
|
||||
)
|
||||
.sort((left, right) => left.sort - right.sort),
|
||||
[config.withdraw.banks, selectedCurrencyCode],
|
||||
)
|
||||
const setAmount = useCallback(
|
||||
(nextAmount: number) => {
|
||||
setAmountState(
|
||||
@@ -161,20 +199,6 @@ export function useWithdrawVm() {
|
||||
[maxWithdrawAmount],
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
selectedCurrency &&
|
||||
selectedCurrency.code !== currencyCode &&
|
||||
config.currencies.some((item) => item.code === currencyCode)
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
if (selectedCurrency && selectedCurrency.code !== currencyCode) {
|
||||
setCurrencyCode(selectedCurrency.code)
|
||||
}
|
||||
}, [config.currencies, currencyCode, selectedCurrency])
|
||||
|
||||
useEffect(() => {
|
||||
const firstAvailablePayChannel = sortedPayChannels[0]
|
||||
|
||||
@@ -195,6 +219,42 @@ export function useWithdrawVm() {
|
||||
}
|
||||
}, [paymentChannelCode, sortedPayChannels])
|
||||
|
||||
useEffect(() => {
|
||||
if (availablePaymentMethods.length === 0) {
|
||||
if (paymentType) {
|
||||
setPaymentType('')
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const hasSelectedAvailablePaymentMethod = availablePaymentMethods.some(
|
||||
(method) => method.code === paymentType,
|
||||
)
|
||||
|
||||
if (!hasSelectedAvailablePaymentMethod) {
|
||||
setPaymentType(availablePaymentMethods[0].code)
|
||||
}
|
||||
}, [availablePaymentMethods, paymentType])
|
||||
|
||||
useEffect(() => {
|
||||
if (availableCurrencies.length === 0) {
|
||||
if (currencyCode) {
|
||||
setCurrencyCode('')
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
const hasSelectedAvailableCurrency = availableCurrencies.some(
|
||||
(currency) => currency.code === currencyCode,
|
||||
)
|
||||
|
||||
if (!hasSelectedAvailableCurrency) {
|
||||
setCurrencyCode(availableCurrencies[0].code)
|
||||
}
|
||||
}, [availableCurrencies, currencyCode])
|
||||
|
||||
useEffect(() => {
|
||||
if (sortedBanks.length === 0) {
|
||||
if (bankCode) {
|
||||
@@ -229,6 +289,10 @@ export function useWithdrawVm() {
|
||||
}, [amount, maxWithdrawAmount, setAmount])
|
||||
|
||||
const quickAmounts = useMemo(() => {
|
||||
if (!selectedCurrency || selectedExchangeRate <= 0) {
|
||||
return []
|
||||
}
|
||||
|
||||
return QUICK_FIAT_AMOUNTS.map((fiatAmount) => ({
|
||||
diamonds: Math.min(
|
||||
maxWithdrawAmount,
|
||||
@@ -237,26 +301,33 @@ export function useWithdrawVm() {
|
||||
id: `quick-${selectedCurrency.code}-${fiatAmount}`,
|
||||
preview: `${selectedCurrency.code} ${formatNumber(locale, fiatAmount)}`,
|
||||
}))
|
||||
}, [locale, maxWithdrawAmount, selectedCurrency.code, selectedExchangeRate])
|
||||
}, [locale, maxWithdrawAmount, selectedCurrency, selectedExchangeRate])
|
||||
|
||||
const resetForm = useCallback(() => {
|
||||
const nextCurrencyCode = config.currencies[0]?.code ?? DEFAULT_CURRENCY_CODE
|
||||
const nextCurrency = getActiveCurrencyCode(
|
||||
config.currencies,
|
||||
nextCurrencyCode,
|
||||
)
|
||||
const nextExchangeRate = getCurrencyExchangeRate(
|
||||
config,
|
||||
nextCurrency.code,
|
||||
nextCurrency.withdrawCoinsPerFiatValue || 1,
|
||||
)
|
||||
const nextPaymentChannelCode = sortedPayChannels[0]?.code ?? ''
|
||||
const nextPaymentChannel =
|
||||
sortedPayChannels.find(
|
||||
(channel) => channel.code === nextPaymentChannelCode,
|
||||
) ?? null
|
||||
const nextCurrency =
|
||||
getChannelCurrencies(config, nextPaymentChannel)[0] ?? null
|
||||
const nextExchangeRate = nextCurrency
|
||||
? getCurrencyExchangeRate(
|
||||
config,
|
||||
nextCurrency.code,
|
||||
nextCurrency.withdrawCoinsPerFiatValue || 1,
|
||||
)
|
||||
: 0
|
||||
|
||||
setAmountState(
|
||||
getInitialWithdrawAmount(nextExchangeRate, maxWithdrawAmount),
|
||||
nextExchangeRate > 0
|
||||
? getInitialWithdrawAmount(nextExchangeRate, maxWithdrawAmount)
|
||||
: 0,
|
||||
)
|
||||
setHasInitializedAmount(true)
|
||||
setCurrencyCode(nextCurrencyCode)
|
||||
setPaymentChannelCode(sortedPayChannels[0]?.code ?? '')
|
||||
setCurrencyCode(nextCurrency?.code ?? '')
|
||||
setPaymentChannelCode(nextPaymentChannelCode)
|
||||
setPaymentType(nextPaymentChannel?.methods[0]?.code ?? '')
|
||||
setBankCode('')
|
||||
setHolderName('')
|
||||
setBankAccount('')
|
||||
@@ -264,37 +335,41 @@ export function useWithdrawVm() {
|
||||
setReceiverPhone('')
|
||||
}, [config, maxWithdrawAmount, sortedPayChannels])
|
||||
|
||||
const selectedCurrencyPreview = useMemo(
|
||||
() => ({
|
||||
currencyCode: selectedCurrency.code,
|
||||
currencyLabel: selectedCurrency.label,
|
||||
const selectedCurrencyPreview = useMemo(() => {
|
||||
const previewCurrencyCode = selectedCurrency?.code ?? '--'
|
||||
|
||||
return {
|
||||
currencyCode: selectedCurrency?.code ?? '',
|
||||
currencyLabel: selectedCurrency?.label ?? '',
|
||||
exchangeRateLabel: t('gameDesktop.withdraw.preview.exchangeRate', {
|
||||
currency: selectedCurrency.code,
|
||||
currency: previewCurrencyCode,
|
||||
}),
|
||||
exchangeRateValue: `1:${formatNumber(locale, selectedExchangeRate)}`,
|
||||
exchangeRateValue:
|
||||
selectedCurrency && selectedExchangeRate > 0
|
||||
? `1:${formatNumber(locale, selectedExchangeRate)}`
|
||||
: '--',
|
||||
convertibleLabel: t('gameDesktop.withdraw.preview.convertible', {
|
||||
currency: selectedCurrency.code,
|
||||
currency: previewCurrencyCode,
|
||||
}),
|
||||
convertibleValue: `${formatNumber(
|
||||
locale,
|
||||
selectedExchangeRate > 0 ? amount * selectedExchangeRate : 0,
|
||||
)} ${selectedCurrency.code}`,
|
||||
}),
|
||||
[
|
||||
amount,
|
||||
locale,
|
||||
selectedCurrency.code,
|
||||
selectedCurrency.label,
|
||||
selectedExchangeRate,
|
||||
t,
|
||||
],
|
||||
)
|
||||
convertibleValue:
|
||||
selectedCurrency && selectedExchangeRate > 0
|
||||
? `${formatNumber(
|
||||
locale,
|
||||
amount * selectedExchangeRate,
|
||||
)} ${selectedCurrency.code}`
|
||||
: '--',
|
||||
}
|
||||
}, [amount, locale, selectedCurrency, selectedExchangeRate, t])
|
||||
|
||||
return {
|
||||
amount,
|
||||
amountAbovePaymentMaximum,
|
||||
amountBelowPaymentMinimum,
|
||||
amountExceedsBalance: amount > maxWithdrawAmount,
|
||||
amountRequiredError: amount <= 0,
|
||||
availableBalance,
|
||||
availableCurrencies,
|
||||
availablePaymentMethods,
|
||||
bankAccount,
|
||||
bankAccountError: bankAccount.trim().length === 0,
|
||||
bankCode,
|
||||
@@ -308,6 +383,20 @@ export function useWithdrawVm() {
|
||||
maxWithdrawAmount,
|
||||
paymentChannelCode,
|
||||
paymentChannelCodeError: paymentChannelCode.trim().length === 0,
|
||||
paymentMaximumAmountLabel:
|
||||
selectedPaymentMaximumAmount !== null && selectedCurrency
|
||||
? `${formatNumber(locale, selectedPaymentMaximumAmount)} ${
|
||||
selectedCurrency.code
|
||||
}`
|
||||
: '',
|
||||
paymentMinimumAmountLabel:
|
||||
selectedPaymentMinimumAmount !== null && selectedCurrency
|
||||
? `${formatNumber(locale, selectedPaymentMinimumAmount)} ${
|
||||
selectedCurrency.code
|
||||
}`
|
||||
: '',
|
||||
paymentType,
|
||||
paymentTypeError: paymentType.trim().length === 0,
|
||||
quickAmounts,
|
||||
receiverEmail,
|
||||
receiverEmailError: !isValidEmail(receiverEmail),
|
||||
@@ -316,6 +405,7 @@ export function useWithdrawVm() {
|
||||
selectedCurrency,
|
||||
selectedCurrencyPreview,
|
||||
selectedPaymentChannel,
|
||||
selectedPaymentMethod,
|
||||
selectedRate,
|
||||
resetForm,
|
||||
setAmount,
|
||||
@@ -324,6 +414,7 @@ export function useWithdrawVm() {
|
||||
setCurrencyCode,
|
||||
setHolderName,
|
||||
setPaymentChannelCode,
|
||||
setPaymentType,
|
||||
setReceiverEmail,
|
||||
setReceiverPhone,
|
||||
sortedBanks,
|
||||
|
||||
Reference in New Issue
Block a user