feat: 优化多语言文案接入并升级大厅与钱包交互体验
- 补充大厅下注结果、快速填单、查中奖、分页与通用操作多语言文案 - 移除多处界面默认文案回退,统一改用 i18n 配置输出 - 优化大厅快速填单、开奖结果入口与注单筛选区域视觉交互 - 重构钱包转入转出弹窗与流水卡片样式,增强金额与状态信息展示
This commit is contained in:
@@ -152,12 +152,12 @@ export function WalletLogsBlock({
|
||||
onClick={onLoadMore}
|
||||
>
|
||||
{loadingMore
|
||||
? t("actions.loading", { defaultValue: "加载中..." })
|
||||
: t("actions.loadMore", { defaultValue: "加载更多" })}
|
||||
? t("actions.loading")
|
||||
: t("actions.loadMore")}
|
||||
</Button>
|
||||
) : (
|
||||
<p className="py-2 text-center text-xs text-slate-400">
|
||||
{t("wallet.noMoreLogs", { defaultValue: "没有更多流水" })}
|
||||
{t("wallet.noMoreLogs")}
|
||||
</p>
|
||||
)}
|
||||
</>
|
||||
@@ -179,29 +179,50 @@ export function LogRow({
|
||||
const { t } = useTranslation("player");
|
||||
const ccy = item.currency_code || currency;
|
||||
const isIn = item.direction === "in";
|
||||
const statusTone = item.status === "posted"
|
||||
? "border-emerald-200 bg-emerald-50 text-emerald-700"
|
||||
: item.status === "pending_reconcile"
|
||||
? "border-amber-200 bg-amber-50 text-amber-700"
|
||||
: item.status === "reversed"
|
||||
? "border-slate-200 bg-slate-50 text-slate-600"
|
||||
: "border-blue-200 bg-blue-50 text-blue-700";
|
||||
|
||||
return (
|
||||
<li className="rounded-xl border border-[#e5edf8] bg-white px-3 py-3 text-sm shadow-[0_8px_24px_rgba(15,23,42,0.05)]">
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<div>
|
||||
<span className="font-medium">
|
||||
{logTypeLabel(item.type, t)}{" "}
|
||||
<span className={isIn ? "font-black text-emerald-600" : "font-black text-[#0b3f96]"}>
|
||||
{isIn ? "+" : "−"}
|
||||
{formatMinorAsCurrency(item.amount_abs, ccy)}
|
||||
</span>
|
||||
</span>
|
||||
<p className="mt-1 text-xs text-muted-foreground">
|
||||
{formatLocalDateTime(item.created_at)}{" "}
|
||||
<span className="text-foreground/80">
|
||||
· {txnStatusLabel(item.status, t)}
|
||||
</span>
|
||||
<li className="rounded-2xl border border-[#e1eaf6] bg-white px-4 py-3.5 text-sm shadow-[0_10px_28px_rgba(15,23,42,0.06)]">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className={isIn ? "size-2.5 rounded-full bg-emerald-500" : "size-2.5 rounded-full bg-[#0b3f96]"} aria-hidden />
|
||||
<p className="truncate text-base font-black leading-tight text-[#101a33]">
|
||||
{logTypeLabel(item.type, t)}
|
||||
</p>
|
||||
</div>
|
||||
<p className="mt-1.5 text-xs font-medium text-slate-500">
|
||||
{formatLocalDateTime(item.created_at)}
|
||||
</p>
|
||||
{item.ref_id ? (
|
||||
<p className="mt-0.5 font-mono text-[11px] text-muted-foreground">
|
||||
<p className="mt-1 truncate font-mono text-[11px] text-slate-400">
|
||||
{item.ref_id}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<div className="flex shrink-0 flex-col items-end gap-2 text-right">
|
||||
<p className={isIn ? "text-lg font-black tabular-nums text-emerald-600" : "text-lg font-black tabular-nums text-[#0b3f96]"}>
|
||||
{isIn ? "+" : "−"}
|
||||
{formatMinorAsCurrency(item.amount_abs, ccy)}
|
||||
</p>
|
||||
<span className={`inline-flex items-center rounded-full border px-2.5 py-1 text-[11px] font-black ${statusTone}`}>
|
||||
{txnStatusLabel(item.status, t)}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-3 flex items-center justify-between rounded-xl bg-[#f8fbff] px-3 py-2 text-xs">
|
||||
<span className="font-semibold text-slate-500">{t("wallet.balanceAfter")}</span>
|
||||
<span className="font-mono font-black tabular-nums text-[#32518d]">
|
||||
{formatMinorAsCurrency(item.balance_after, ccy)}
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
);
|
||||
|
||||
@@ -194,8 +194,8 @@ export function WalletScreen() {
|
||||
lotteryMinor={Number(balance?.balance ?? 0)}
|
||||
onSuccess={refreshAll}
|
||||
triggerVariant="hall"
|
||||
triggerLabel={t("wallet.transferIn")}
|
||||
triggerClassName="h-12 rounded-lg text-base font-bold"
|
||||
triggerLabel={t("wallet.transferIn", { defaultValue: "Transfer In" })}
|
||||
triggerClassName="h-14 rounded-2xl text-base font-black"
|
||||
/>
|
||||
<TransferOutDialog
|
||||
idPrefix="wallet-"
|
||||
@@ -203,8 +203,8 @@ export function WalletScreen() {
|
||||
availableMinor={Number(balance?.available_balance ?? 0)}
|
||||
onSuccess={refreshAll}
|
||||
triggerVariant="hall"
|
||||
triggerLabel={t("wallet.transferOut")}
|
||||
triggerClassName="h-12 rounded-lg text-base font-bold"
|
||||
triggerLabel={t("wallet.transferOut", { defaultValue: "Transfer Out" })}
|
||||
triggerClassName="h-14 rounded-2xl text-base font-black"
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -26,15 +26,13 @@ type BaseProps = {
|
||||
};
|
||||
|
||||
const defaultInTrigger =
|
||||
"!bg-[#52c41a] !text-white shadow-none hover:!bg-[#52c41a]/90";
|
||||
"!bg-[#0b3f96] !text-white shadow-[0_8px_18px_rgba(11,63,150,0.18)] hover:!bg-[#08357f]";
|
||||
const defaultOutTrigger = "flex-1";
|
||||
|
||||
/** 高保真稿:蓝底白字 */
|
||||
const hallInTrigger =
|
||||
"rounded-lg border-0 !bg-[#1677ff] !text-white shadow-sm hover:!bg-[#1677ff]/90";
|
||||
/** 高保真稿:白底红框红字 */
|
||||
"rounded-2xl border border-[#d7e5fb] !bg-[#0b3f96] !text-white shadow-[0_10px_24px_rgba(11,63,150,0.22)] hover:!bg-[#08357f]";
|
||||
const hallOutTrigger =
|
||||
"rounded-lg !border-2 !border-[#ff4d4f] !bg-white !text-[#ff4d4f] shadow-sm hover:!bg-red-50 dark:!bg-card dark:hover:!bg-red-950/30";
|
||||
"rounded-2xl border border-[#ffc7d2] !bg-white !text-[#e5002c] shadow-[0_8px_22px_rgba(216,20,53,0.08)] hover:!bg-[#fff5f7]";
|
||||
|
||||
export function TransferInDialog({
|
||||
currency,
|
||||
@@ -71,24 +69,26 @@ export function TransferInDialog({
|
||||
<ArrowDownLeft className="size-4 shrink-0" />
|
||||
{resolvedTriggerLabel}
|
||||
</Button>
|
||||
<DialogContent showCloseButton>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t("wallet.transferInTitle")}</DialogTitle>
|
||||
<DialogDescription>
|
||||
<DialogContent showCloseButton className="overflow-hidden rounded-[28px] border border-[#dbe7fb] bg-white p-0 shadow-[0_24px_70px_rgba(11,63,150,0.18)] sm:max-w-[430px] [&_[data-slot=dialog-close]]:right-3 [&_[data-slot=dialog-close]]:top-3 [&_[data-slot=dialog-close]]:text-white [&_[data-slot=dialog-close]]:hover:bg-white/15 [&_[data-slot=dialog-close]]:hover:text-white">
|
||||
<DialogHeader className="bg-gradient-to-br from-[#0b3f96] via-[#1456bd] to-[#e5002c] px-5 pb-5 pt-6 text-white">
|
||||
<DialogTitle className="text-xl font-black text-white">{t("wallet.transferInTitle")}</DialogTitle>
|
||||
<DialogDescription className="text-sm leading-6 text-white/85">
|
||||
{t("wallet.dialogInDescription", { currency })}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<TransferInPanel
|
||||
variant="dialog"
|
||||
currency={currency}
|
||||
lotteryMinor={lotteryMinor}
|
||||
idPrefix={idPrefix}
|
||||
onCancel={() => setOpen(false)}
|
||||
onSuccess={async () => {
|
||||
await onSuccess();
|
||||
setOpen(false);
|
||||
}}
|
||||
/>
|
||||
<div className="px-5 pb-5 pt-4">
|
||||
<TransferInPanel
|
||||
variant="dialog"
|
||||
currency={currency}
|
||||
lotteryMinor={lotteryMinor}
|
||||
idPrefix={idPrefix}
|
||||
onCancel={() => setOpen(false)}
|
||||
onSuccess={async () => {
|
||||
await onSuccess();
|
||||
setOpen(false);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
@@ -134,24 +134,26 @@ export function TransferOutDialog({
|
||||
<ArrowUpRight className="size-4 shrink-0" />
|
||||
{resolvedTriggerLabel}
|
||||
</Button>
|
||||
<DialogContent showCloseButton>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t("wallet.transferOutTitle")}</DialogTitle>
|
||||
<DialogDescription>
|
||||
<DialogContent showCloseButton className="overflow-hidden rounded-[28px] border border-[#ffd7df] bg-white p-0 shadow-[0_24px_70px_rgba(216,20,53,0.16)] sm:max-w-[430px] [&_[data-slot=dialog-close]]:right-3 [&_[data-slot=dialog-close]]:top-3 [&_[data-slot=dialog-close]]:text-white [&_[data-slot=dialog-close]]:hover:bg-white/15 [&_[data-slot=dialog-close]]:hover:text-white">
|
||||
<DialogHeader className="bg-gradient-to-br from-[#e5002c] via-[#d81435] to-[#0b3f96] px-5 pb-5 pt-6 text-white">
|
||||
<DialogTitle className="text-xl font-black text-white">{t("wallet.transferOutTitle")}</DialogTitle>
|
||||
<DialogDescription className="text-sm leading-6 text-white/85">
|
||||
{t("wallet.dialogOutDescription")}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<TransferOutPanel
|
||||
variant="dialog"
|
||||
currency={currency}
|
||||
availableMinor={availableMinor}
|
||||
idPrefix={idPrefix}
|
||||
onCancel={() => setOpen(false)}
|
||||
onSuccess={async () => {
|
||||
await onSuccess();
|
||||
setOpen(false);
|
||||
}}
|
||||
/>
|
||||
<div className="px-5 pb-5 pt-4">
|
||||
<TransferOutPanel
|
||||
variant="dialog"
|
||||
currency={currency}
|
||||
availableMinor={availableMinor}
|
||||
idPrefix={idPrefix}
|
||||
onCancel={() => setOpen(false)}
|
||||
onSuccess={async () => {
|
||||
await onSuccess();
|
||||
setOpen(false);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
@@ -121,17 +121,10 @@ export function TransferInPanel({
|
||||
)}
|
||||
</Button>
|
||||
) : (
|
||||
<div className="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
disabled={submitting}
|
||||
onClick={onCancel}
|
||||
>
|
||||
{t("actions.cancel")}
|
||||
</Button>
|
||||
<div className="grid gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
className="h-12 w-full rounded-2xl bg-[#0b3f96] text-base font-black text-white shadow-[0_10px_22px_rgba(11,63,150,0.18)] hover:bg-[#08357f]"
|
||||
disabled={submitting}
|
||||
onClick={() => void submit()}
|
||||
>
|
||||
@@ -144,26 +137,35 @@ export function TransferInPanel({
|
||||
t("wallet.confirmIn")
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
className="h-11 rounded-2xl border-[#dce7f7] bg-white text-base font-bold text-[#32518d] hover:bg-[#f8fbff]"
|
||||
disabled={submitting}
|
||||
onClick={onCancel}
|
||||
>
|
||||
{t("actions.cancel")}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="grid gap-3 py-1">
|
||||
<div className="rounded-xl border border-[#e5edf8] bg-[#f8fbff] px-3 py-2 text-xs">
|
||||
<p>
|
||||
<div className="grid gap-4 py-1">
|
||||
<div className="rounded-2xl border border-[#dbe7fb] bg-gradient-to-br from-[#f8fbff] to-white px-4 py-3 text-sm shadow-[0_8px_22px_rgba(11,63,150,0.06)]">
|
||||
<p className="text-slate-500">
|
||||
{t("wallet.mainBalance")}{" "}
|
||||
<span className="text-muted-foreground">{t("wallet.mainPending")}</span>
|
||||
<span className="font-semibold text-slate-700">{t("wallet.mainPending")}</span>
|
||||
</p>
|
||||
<p className="mt-1">
|
||||
<p className="mt-2 text-slate-500">
|
||||
{t("wallet.lotteryBalance")}{" "}
|
||||
<span className="font-medium text-foreground">
|
||||
<span className="font-black tabular-nums text-[#0b3f96]">
|
||||
{formatMinorAsCurrency(lotteryMinor, currency)}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<Label htmlFor={tid}>{t("wallet.inAmount")}</Label>
|
||||
<div className="grid gap-2.5">
|
||||
<Label htmlFor={tid} className="text-sm font-black text-[#101a33]">{t("wallet.inAmount")}</Label>
|
||||
<Input
|
||||
id={tid}
|
||||
inputMode="decimal"
|
||||
@@ -172,16 +174,16 @@ export function TransferInPanel({
|
||||
onChange={(ev) => setAmountText(ev.target.value)}
|
||||
disabled={submitting}
|
||||
autoComplete="off"
|
||||
className="h-11 rounded-lg border-[#dce7f7] bg-white text-base"
|
||||
className="h-13 rounded-2xl border-[#d7e5fb] bg-white px-4 text-lg font-bold tabular-nums text-[#101a33] shadow-inner focus-visible:ring-[#0b3f96]/20"
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
<p className="rounded-2xl bg-[#f8fbff] px-3 py-2 text-xs font-semibold leading-5 text-[#32518d]">
|
||||
{t("wallet.afterInPreview", {
|
||||
amount: formatMinorAsCurrency(previewAfter, currency),
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
{localError ? (
|
||||
<p className="text-sm text-[#ff4d4f]">{localError}</p>
|
||||
<p className="rounded-2xl border border-red-200 bg-red-50 px-3 py-2 text-sm font-semibold text-[#d81435]">{localError}</p>
|
||||
) : null}
|
||||
</div>
|
||||
{footer}
|
||||
@@ -270,17 +272,10 @@ export function TransferOutPanel({
|
||||
)}
|
||||
</Button>
|
||||
) : (
|
||||
<div className="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
disabled={submitting}
|
||||
onClick={onCancel}
|
||||
>
|
||||
{t("actions.cancel")}
|
||||
</Button>
|
||||
<div className="grid gap-2">
|
||||
<Button
|
||||
type="button"
|
||||
className="h-12 w-full rounded-2xl bg-[#e5002c] text-base font-black text-white shadow-[0_10px_22px_rgba(229,0,44,0.18)] hover:bg-[#d10028]"
|
||||
disabled={submitting}
|
||||
onClick={() => void submit()}
|
||||
>
|
||||
@@ -293,27 +288,36 @@ export function TransferOutPanel({
|
||||
t("wallet.confirmOut")
|
||||
)}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
className="h-11 rounded-2xl border-[#ffd7df] bg-white text-base font-bold text-[#d81435] hover:bg-[#fff5f7]"
|
||||
disabled={submitting}
|
||||
onClick={onCancel}
|
||||
>
|
||||
{t("actions.cancel")}
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="grid gap-3 py-1">
|
||||
<div className="rounded-xl border border-[#e5edf8] bg-[#f8fbff] px-3 py-2 text-xs">
|
||||
<p>
|
||||
<div className="grid gap-4 py-1">
|
||||
<div className="rounded-2xl border border-[#ffd7df] bg-gradient-to-br from-[#fff7f8] to-white px-4 py-3 text-sm shadow-[0_8px_22px_rgba(216,20,53,0.06)]">
|
||||
<p className="text-slate-500">
|
||||
{t("wallet.lotteryAvailable")}{" "}
|
||||
<span className="font-medium text-foreground">
|
||||
<span className="font-black tabular-nums text-[#d81435]">
|
||||
{formatMinorAsCurrency(availableMinor, currency)}
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<div className="grid gap-2.5">
|
||||
<div className="flex items-end justify-between gap-2">
|
||||
<Label htmlFor={tid}>{t("wallet.outAmount")}</Label>
|
||||
<Label htmlFor={tid} className="text-sm font-black text-[#101a33]">{t("wallet.outAmount")}</Label>
|
||||
<Button
|
||||
type="button"
|
||||
variant="link"
|
||||
className="h-auto p-0 text-xs"
|
||||
className="h-auto p-0 text-xs font-black text-[#d81435] hover:text-[#b80f2b]"
|
||||
onClick={fillAll}
|
||||
disabled={submitting || availableMinor < 1}
|
||||
>
|
||||
@@ -330,16 +334,16 @@ export function TransferOutPanel({
|
||||
onChange={(ev) => setAmountText(ev.target.value)}
|
||||
disabled={submitting}
|
||||
autoComplete="off"
|
||||
className="h-11 rounded-lg border-[#dce7f7] bg-white text-base"
|
||||
className="h-13 rounded-2xl border-[#ffd7df] bg-white px-4 text-lg font-bold tabular-nums text-[#101a33] shadow-inner focus-visible:ring-[#e5002c]/20"
|
||||
/>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
<p className="rounded-2xl bg-[#fff7f8] px-3 py-2 text-xs font-semibold leading-5 text-[#9f1730]">
|
||||
{t("wallet.afterOutPreview", {
|
||||
amount: formatMinorAsCurrency(previewAfter, currency),
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
{localError ? (
|
||||
<p className="text-sm text-[#ff4d4f]">{localError}</p>
|
||||
<p className="rounded-2xl border border-red-200 bg-red-50 px-3 py-2 text-sm font-semibold text-[#d81435]">{localError}</p>
|
||||
) : null}
|
||||
</div>
|
||||
{footer}
|
||||
|
||||
Reference in New Issue
Block a user