feat: 更新钱包日志和资金模式相关的国际化文本,增强用户体验
Some checks failed
lotteryfront CI / build (push) Has been cancelled
Some checks failed
lotteryfront CI / build (push) Has been cancelled
This commit is contained in:
@@ -24,7 +24,8 @@ export const WALLET_FLOW_FILTERS: { value: string; labelKey: string }[] = [
|
||||
export const CREDIT_FLOW_FILTERS: { value: string; labelKey: string }[] = [
|
||||
{ value: "", labelKey: "wallet.creditFlow.all" },
|
||||
{ value: "bet", labelKey: "wallet.creditFlow.bet" },
|
||||
{ value: "credit_release", labelKey: "wallet.creditFlow.credit_release" },
|
||||
{ value: "game_settlement", labelKey: "wallet.creditFlow.game_settlement" },
|
||||
{ value: "rebate", labelKey: "wallet.creditFlow.rebate" },
|
||||
{ value: "bill_settlement", labelKey: "wallet.creditFlow.bill_settlement" },
|
||||
];
|
||||
|
||||
@@ -37,7 +38,9 @@ const FLOW_LABEL_FALLBACKS: Record<string, string> = {
|
||||
"wallet.flow.refund": "退款",
|
||||
"wallet.flow.reversal": "冲正",
|
||||
"wallet.creditFlow.all": "全部",
|
||||
"wallet.creditFlow.bet": "占用额度",
|
||||
"wallet.creditFlow.bet": "下注冻结",
|
||||
"wallet.creditFlow.game_settlement": "开奖结算",
|
||||
"wallet.creditFlow.rebate": "账期回水",
|
||||
"wallet.creditFlow.credit_release": "释额",
|
||||
"wallet.creditFlow.bill_settlement": "账期收付",
|
||||
"wallet.creditFlow.win_credit": "中奖释额",
|
||||
@@ -59,7 +62,14 @@ function txnStatusLabel(
|
||||
status: string,
|
||||
t: (key: string, options?: { defaultValue?: string }) => string,
|
||||
): string {
|
||||
return t(`wallet.txnStatus.${status}`, { defaultValue: status });
|
||||
const fallback: Record<string, string> = {
|
||||
accrued: "已计提",
|
||||
in_bill: "已入账单",
|
||||
settled: "已结清",
|
||||
reversed: "已冲正",
|
||||
};
|
||||
|
||||
return t(`wallet.txnStatus.${status}`, { defaultValue: fallback[status] ?? status });
|
||||
}
|
||||
|
||||
type WalletLogsBlockProps = {
|
||||
@@ -125,6 +135,7 @@ export function WalletLogsBlock({
|
||||
? "h-8 rounded-full bg-[#07459f] px-3 text-xs font-bold text-white hover:bg-[#063b88]"
|
||||
: "h-8 rounded-full border-[#dce7f7] bg-white px-3 text-xs font-bold text-[#32518d] hover:bg-[#f8fbff]"
|
||||
}
|
||||
disabled={logsLoading && filter === f.value}
|
||||
onClick={() => onFilterChange(f.value)}
|
||||
>
|
||||
{f.label}
|
||||
@@ -142,7 +153,7 @@ export function WalletLogsBlock({
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("wallet.totalRecords", { total: logs.total })}
|
||||
</p>
|
||||
<ul className="space-y-2">
|
||||
<ul className={logsLoading ? "space-y-2 opacity-60" : "space-y-2"}>
|
||||
{logs.items.length === 0 ? (
|
||||
<li className="rounded-lg border border-dashed py-8 text-center text-sm text-muted-foreground">
|
||||
{t(creditMode ? "wallet.emptyCreditLogs" : "wallet.emptyLogs", {
|
||||
|
||||
@@ -24,14 +24,31 @@ import { formatWalletClientError } from "@/lib/wallet-api-error";
|
||||
import { usePlayerSessionStore } from "@/stores/player-session-store";
|
||||
import type { WalletBalanceData } from "@/types/api/wallet-balance";
|
||||
import { getWalletLogsLastPage, type WalletLogsData } from "@/types/api/wallet-logs";
|
||||
import type { PlayerMeData } from "@/types/api/player-me";
|
||||
|
||||
const WALLET_LOGS_PAGE_SIZE = 10;
|
||||
type FundingModeView = "credit" | "wallet" | "unknown";
|
||||
|
||||
function scrollToWalletSection(id: string): void {
|
||||
if (typeof document === "undefined") return;
|
||||
document.getElementById(id)?.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
}
|
||||
|
||||
function resolveFundingModeView(
|
||||
balance: WalletBalanceData | null,
|
||||
profile: PlayerMeData | null,
|
||||
): FundingModeView {
|
||||
if (isCreditFundingPlayer(balance) || isCreditFundingPlayer(profile)) {
|
||||
return "credit";
|
||||
}
|
||||
|
||||
if (balance?.funding_mode === "wallet" || profile?.funding_mode === "wallet") {
|
||||
return "wallet";
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
export function WalletScreen() {
|
||||
const router = useRouter();
|
||||
const searchParams = useSearchParams();
|
||||
@@ -54,7 +71,9 @@ export function WalletScreen() {
|
||||
const sectionDeepLinkHandledRef = useRef(false);
|
||||
|
||||
const profile = usePlayerSessionStore((s) => s.profile);
|
||||
const isCreditPlayer = isCreditFundingPlayer(balance) || isCreditFundingPlayer(profile);
|
||||
const fundingModeView = resolveFundingModeView(balance, profile);
|
||||
const fundingModeKnown = fundingModeView !== "unknown";
|
||||
const isCreditPlayer = fundingModeView === "credit";
|
||||
|
||||
useEffect(() => {
|
||||
if (actionDeepLinkHandledRef.current || loading) return;
|
||||
@@ -181,6 +200,16 @@ export function WalletScreen() {
|
||||
};
|
||||
}, [currency, filter, t]);
|
||||
|
||||
const handleFilterChange = useCallback((value: string) => {
|
||||
if (value === filter) {
|
||||
return;
|
||||
}
|
||||
|
||||
setLogs(null);
|
||||
setLogsLoading(true);
|
||||
setFilter(value);
|
||||
}, [filter]);
|
||||
|
||||
const refreshAll = useCallback(async () => {
|
||||
setError(null);
|
||||
setLogsLoading(true);
|
||||
@@ -210,6 +239,11 @@ export function WalletScreen() {
|
||||
const displayMinor = isCreditPlayer
|
||||
? Number(balance?.available_balance ?? 0)
|
||||
: Number(balance?.balance ?? 0);
|
||||
const panelTitle = !fundingModeKnown
|
||||
? t("wallet.loadingTitle", { defaultValue: "账户资金" })
|
||||
: isCreditPlayer
|
||||
? t("wallet.creditTitle", { defaultValue: "信用" })
|
||||
: t("wallet.title");
|
||||
|
||||
const loadMore = useCallback(() => {
|
||||
if (!logs || !hasMore || loadingMore) return;
|
||||
@@ -243,9 +277,7 @@ export function WalletScreen() {
|
||||
}, [hasMore, loadMore, loading, loadingMore, logsLoading]);
|
||||
|
||||
return (
|
||||
<PlayerPanel
|
||||
title={isCreditPlayer ? t("wallet.creditTitle", { defaultValue: "信用" }) : t("wallet.title")}
|
||||
>
|
||||
<PlayerPanel title={panelTitle}>
|
||||
<div className="space-y-3">
|
||||
{error ? (
|
||||
<div className="rounded-xl border border-red-200 bg-red-50 px-3 py-3 text-sm text-red-700">
|
||||
@@ -274,11 +306,13 @@ export function WalletScreen() {
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="text-sm font-semibold text-white/90">
|
||||
{isCreditPlayer
|
||||
{fundingModeKnown && isCreditPlayer
|
||||
? t("wallet.creditAvailable", { defaultValue: "可用信用" })
|
||||
: t("wallet.balance")}
|
||||
: fundingModeKnown
|
||||
? t("wallet.balance")
|
||||
: t("wallet.loadingBalance", { defaultValue: "正在加载" })}
|
||||
</p>
|
||||
{loading ? (
|
||||
{loading || !fundingModeKnown ? (
|
||||
<Skeleton className="mt-2 h-8 w-44 rounded-md bg-white/25" />
|
||||
) : (
|
||||
<PlayerMoneyDisplay
|
||||
@@ -288,21 +322,23 @@ export function WalletScreen() {
|
||||
/>
|
||||
)}
|
||||
<p className="mt-2 text-xs text-white/75">
|
||||
{isCreditPlayer
|
||||
? t("wallet.creditSummary", {
|
||||
defaultValue: "授信 {{limit}} · 已用 {{used}}",
|
||||
limit: formatMinorAsCurrency(balance?.credit_limit ?? 0, currency),
|
||||
used: formatMinorAsCurrency(balance?.used_credit ?? 0, currency),
|
||||
})
|
||||
: t("wallet.available", {
|
||||
amount: formatMinorAsCurrency(balance?.available_balance ?? 0, currency),
|
||||
})}
|
||||
{!fundingModeKnown
|
||||
? t("wallet.loadingAccountMode", { defaultValue: "正在确认资金模式…" })
|
||||
: isCreditPlayer
|
||||
? t("wallet.creditSummary", {
|
||||
defaultValue: "授信 {{limit}} · 已用 {{used}}",
|
||||
limit: formatMinorAsCurrency(balance?.credit_limit ?? 0, currency),
|
||||
used: formatMinorAsCurrency(balance?.used_credit ?? 0, currency),
|
||||
})
|
||||
: t("wallet.available", {
|
||||
amount: formatMinorAsCurrency(balance?.available_balance ?? 0, currency),
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{!isCreditPlayer ? (
|
||||
{fundingModeKnown && !isCreditPlayer ? (
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<TransferInDialog
|
||||
idPrefix="wallet-"
|
||||
@@ -334,7 +370,7 @@ export function WalletScreen() {
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{!isCreditPlayer && (logs?.pending_reconcile?.length ?? 0) > 0 ? (
|
||||
{fundingModeKnown && !isCreditPlayer && (logs?.pending_reconcile?.length ?? 0) > 0 ? (
|
||||
<section
|
||||
id="wallet-pending"
|
||||
className="scroll-mt-3 rounded-2xl border border-amber-200 bg-amber-50 px-3 py-3 text-sm text-amber-900"
|
||||
@@ -371,18 +407,22 @@ export function WalletScreen() {
|
||||
) : null}
|
||||
|
||||
<div id="wallet-logs" className="scroll-mt-3">
|
||||
<WalletLogsBlock
|
||||
creditMode={isCreditPlayer}
|
||||
logs={logs}
|
||||
logsLoading={loading || logsLoading}
|
||||
loadingMore={loadingMore}
|
||||
hasMore={hasMore}
|
||||
onLoadMore={loadMore}
|
||||
loadMoreRef={loadMoreRef}
|
||||
filter={filter}
|
||||
onFilterChange={setFilter}
|
||||
currency={currency}
|
||||
/>
|
||||
{fundingModeKnown ? (
|
||||
<WalletLogsBlock
|
||||
creditMode={isCreditPlayer}
|
||||
logs={logs}
|
||||
logsLoading={loading || logsLoading}
|
||||
loadingMore={loadingMore}
|
||||
hasMore={hasMore}
|
||||
onLoadMore={loadMore}
|
||||
loadMoreRef={loadMoreRef}
|
||||
filter={filter}
|
||||
onFilterChange={handleFilterChange}
|
||||
currency={currency}
|
||||
/>
|
||||
) : (
|
||||
<Skeleton className="h-40 w-full rounded-lg" />
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</PlayerPanel>
|
||||
|
||||
Reference in New Issue
Block a user