feat: 优化多语言文案接入并升级大厅与钱包交互体验

- 补充大厅下注结果、快速填单、查中奖、分页与通用操作多语言文案
- 移除多处界面默认文案回退,统一改用 i18n 配置输出
- 优化大厅快速填单、开奖结果入口与注单筛选区域视觉交互
- 重构钱包转入转出弹窗与流水卡片样式,增强金额与状态信息展示
This commit is contained in:
2026-05-20 17:56:18 +08:00
parent a2a29107f8
commit 626914feb6
20 changed files with 578 additions and 359 deletions

View File

@@ -625,19 +625,13 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
removed
? t("hall.playConfig.playClosedDraftCleared", {
playCode: evt.play_code,
defaultValue: "{{playCode}} 已关闭,相关草稿金额已清除。",
})
: t("hall.playConfig.playClosed", {
playCode: evt.play_code,
defaultValue: "{{playCode}} 已关闭。",
}),
);
} else {
toast.message(
t("hall.playConfig.updated", {
defaultValue: "玩法配置已更新,已刷新下注表格。",
}),
);
toast.message(t("hall.playConfig.updated"));
}
};
@@ -645,12 +639,7 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
void loadCatalog();
setPreviewOpen(false);
setPreviewData(null);
toast.message(
evt.message ??
t("hall.playConfig.oddsUpdated", {
defaultValue: "赔率已更新,请重新预览注单。",
}),
);
toast.message(evt.message ?? t("hall.playConfig.oddsUpdated"));
};
channel.listen(".play.toggle", onPlayToggle);
@@ -840,7 +829,6 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
t("hall.placePartialFailed", {
success: data.summary.success_count ?? 0,
failed: data.summary.failure_count ?? 0,
defaultValue: "{{success}} 个成功,{{failed}} 个失败",
}),
);
}
@@ -947,7 +935,6 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
<p className="rounded-full bg-amber-100 px-3 py-1 text-xs font-bold text-amber-800">
{t("results.jackpotGap", {
count: jackpot.draws_since_last_burst,
defaultValue: "距上次爆池 {{count}} 期",
})}
</p>
) : null}
@@ -970,49 +957,74 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
</div>
) : (
<>
<div className="space-y-3 rounded-xl border border-[#e9eef7] bg-white p-4 shadow-[0_8px_28px_rgba(15,23,42,0.05)]">
<div className="flex flex-wrap items-center justify-between gap-2">
<div>
<p className="text-sm font-semibold text-slate-900">
{t("hall.quickFill.title", { defaultValue: "快速填单" })}
<div className="rounded-xl border border-[#e3ebf7] bg-white px-3 py-3 shadow-[0_8px_24px_rgba(15,23,42,0.045)]">
<div className="flex items-start justify-between gap-3">
<div className="min-w-0">
<p className="text-sm font-bold leading-5 text-slate-950">
{t("hall.quickFill.title")}
</p>
<p className="text-xs text-slate-500">
{t("hall.quickFill.description", {
defaultValue: "收藏号码、最近号码可一键填入当前行。",
})}
<p className="mt-0.5 text-xs leading-5 text-slate-500">
{t("hall.quickFill.description")}
</p>
</div>
<div className="flex flex-wrap gap-2">
<Button type="button" variant="outline" size="sm" onClick={clearAllRows}>
{t("hall.quickFill.clearAll", { defaultValue: "批量清空" })}
</Button>
<div className="flex shrink-0 items-center gap-1.5">
{activeRow?.number ? (
<Button
type="button"
variant="outline"
size="sm"
variant="ghost"
size="icon"
aria-label={
favorites.includes(activeRow.number)
? t("hall.quickFill.unfavorite")
: t("hall.quickFill.favorite")
}
aria-pressed={favorites.includes(activeRow.number)}
title={
favorites.includes(activeRow.number)
? t("hall.quickFill.unfavorite")
: t("hall.quickFill.favorite")
}
className={cn(
"size-8 rounded-full border border-[#ffd7db] bg-[#fff7f8] text-[#d81435] hover:bg-[#fff1f3] hover:text-[#b80f2b]",
favorites.includes(activeRow.number) &&
"border-[#d81435] bg-[#d81435] text-white hover:bg-[#c51230] hover:text-white",
)}
onClick={() => toggleFavoriteNumber(activeRow.number)}
>
<Star className="mr-1 size-4" />
{favorites.includes(activeRow.number)
? t("hall.quickFill.unfavorite", { defaultValue: "取消收藏" })
: t("hall.quickFill.favorite", { defaultValue: "收藏号码" })}
<Star
className={cn(
"size-4",
favorites.includes(activeRow.number) && "fill-current",
)}
aria-hidden
/>
</Button>
) : null}
<Button
type="button"
variant="ghost"
size="icon"
aria-label={t("hall.quickFill.clearAll")}
title={t("hall.quickFill.clearAll")}
className="size-8 rounded-full border border-[#dfe6f0] bg-[#f8fafc] text-slate-500 hover:bg-slate-100 hover:text-slate-900"
onClick={clearAllRows}
>
<Trash2 className="size-4" aria-hidden />
</Button>
</div>
</div>
<div className="space-y-2">
<p className="text-[11px] font-medium uppercase tracking-wide text-slate-400">
{t("hall.quickFill.favorites", { defaultValue: "收藏" })}
</p>
<div className="flex flex-wrap gap-2">
{favoriteChips.length > 0 ? (
favoriteChips.map((number) => (
<div className="mt-3 space-y-2">
{favoriteChips.length > 0 ? (
<div className="flex flex-wrap items-center gap-1.5">
<span className="mr-0.5 text-[11px] font-semibold text-[#d81435]">
{t("hall.quickFill.favorites")}
</span>
{favoriteChips.map((number) => (
<button
key={`fav-${number}`}
type="button"
className="inline-flex items-center gap-1 rounded-full border border-[#ffd7db] bg-[#fff3f5] px-3 py-1 text-xs font-medium text-[#d81435]"
className="inline-flex h-7 items-center gap-1 rounded-full border border-[#ffd7db] bg-[#fff3f5] px-2.5 text-xs font-semibold text-[#d81435] transition-colors hover:bg-[#ffe9ed]"
onPointerDown={() => {
const current = holdFavoriteRef.current;
current.number = number;
@@ -1044,38 +1056,32 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
}}
>
{number}
<span className="text-[10px] opacity-70">
{t("hall.quickFill.tapHold", { defaultValue: "长按取消" })}
<span className="text-[10px] font-medium opacity-70">
{t("hall.quickFill.tapHold")}
</span>
</button>
))
) : (
<span className="text-xs text-slate-400">
{t("hall.quickFill.emptyFavorites", { defaultValue: "暂无收藏" })}
</span>
)}
</div>
</div>
))}
</div>
) : null}
<div className="space-y-2">
<p className="text-[11px] font-medium uppercase tracking-wide text-slate-400">
{t("hall.quickFill.history", { defaultValue: "最近 20 个历史号码" })}
</p>
<div className="flex flex-wrap gap-2">
<div className="flex flex-wrap items-center gap-1.5">
<span className="mr-0.5 text-[11px] font-semibold text-slate-400">
{t("hall.quickFill.history")}
</span>
{historyChips.length > 0 ? (
historyChips.map((number) => (
<button
key={`his-${number}`}
type="button"
className="inline-flex items-center rounded-full border border-[#dce7f7] bg-[#f8fbff] px-3 py-1 text-xs font-medium text-[#07459f]"
className="inline-flex h-7 min-w-10 items-center justify-center rounded-full border border-[#d7e5f8] bg-[#f8fbff] px-3 text-xs font-bold text-[#07459f] transition-colors hover:border-[#b9d0f3] hover:bg-[#eef6ff]"
onClick={() => fillCurrentRow(number)}
>
{number}
</button>
))
) : (
<span className="text-xs text-slate-400">
{t("hall.quickFill.emptyHistory", { defaultValue: "暂无历史号码" })}
<span className="inline-flex h-7 items-center rounded-full border border-dashed border-slate-200 bg-slate-50 px-3 text-xs text-slate-400">
{t("hall.quickFill.emptyHistory")}
</span>
)}
</div>
@@ -1122,7 +1128,7 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
: ""}
</span>
<span className="block text-[9px] font-medium text-[#9aa8bd]">
{t("hall.table.amountPlaceholder", { defaultValue: "金额" })}
{t("hall.table.amountPlaceholder")}
</span>
</th>
))}
@@ -1177,7 +1183,7 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
inputMode="decimal"
placeholder={
status === "sold_out"
? t("hall.table.soldOut", { defaultValue: "售罄" })
? t("hall.table.soldOut")
: "-"
}
onFocus={() => setActiveRowId(row.id)}
@@ -1192,11 +1198,11 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
/>
{status === "sold_out" ? (
<p className="mt-0.5 text-center text-[9px] font-bold text-slate-500">
{t("hall.table.soldOut", { defaultValue: "售罄" })}
{t("hall.table.soldOut")}
</p>
) : status === "warning" ? (
<p className="mt-0.5 text-center text-[9px] font-bold text-amber-700">
{t("hall.table.warning", { defaultValue: "接近售罄" })}
{t("hall.table.warning")}
</p>
) : null}
</td>
@@ -1227,13 +1233,13 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
className="flex h-10 w-full items-center justify-center gap-1.5 border-t border-[#edf2f9] bg-white text-xs font-bold text-[#1d57b7] hover:bg-[#f7faff] disabled:text-slate-300"
>
<CirclePlus className="size-4" aria-hidden />
{t("hall.table.addRow", { defaultValue: "添加一行" })}
{t("hall.table.addRow")}
</button>
</div>
<div className="flex items-center justify-between rounded-xl border border-[#e9eef7] bg-[#f8fbff] px-4 py-3 text-sm shadow-[0_6px_20px_rgba(15,23,42,0.04)]">
<span className="text-xs font-medium text-slate-500">
{t("hall.table.draftTotal", { defaultValue: "投注金额" })}
{t("hall.table.draftTotal")}
</span>
<span className="text-base font-black tabular-nums text-[#0b3f96]">
{formatMinorAsCurrency(debouncedSummary.actual, currencyCode)}
@@ -1254,12 +1260,12 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
>
<Ticket className="size-5" aria-hidden />
{previewLoading
? t("hall.table.previewing", { defaultValue: "预览中..." })
? t("hall.table.previewing")
: !isBettable
? t("hall.closed.title")
: availableMinor < debouncedSummary.actual
? t("hall.table.insufficientBalance", { defaultValue: "余额不足" })
: t("hall.table.submitBet", { defaultValue: "提交下注" })}
? t("hall.table.insufficientBalance")
: t("hall.table.submitBet")}
</Button>
</>
)}