feat: 优化多语言文案接入并升级大厅与钱包交互体验
- 补充大厅下注结果、快速填单、查中奖、分页与通用操作多语言文案 - 移除多处界面默认文案回退,统一改用 i18n 配置输出 - 优化大厅快速填单、开奖结果入口与注单筛选区域视觉交互 - 重构钱包转入转出弹窗与流水卡片样式,增强金额与状态信息展示
This commit is contained in:
@@ -78,14 +78,14 @@ export function CheckWinningScreen() {
|
||||
setResult(next);
|
||||
setRecent((current) => [normalizedTicketNo, ...current.filter((x) => x !== normalizedTicketNo)].slice(0, 5));
|
||||
} catch {
|
||||
setError(t("results.check.loadFailed", { defaultValue: "查询失败,请稍后重试。" }));
|
||||
setError(t("results.check.loadFailed"));
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [latestDraw, normalizedTicketNo, t]);
|
||||
|
||||
return (
|
||||
<PlayerPanel title={t("results.check.title", { defaultValue: "查我的中奖" })} backHref="/results" backLabel={t("results.title")}>
|
||||
<PlayerPanel title={t("results.check.title")} backHref="/results" backLabel={t("results.title")}>
|
||||
<div className="space-y-4">
|
||||
<section className="overflow-hidden rounded-2xl border border-red-100 bg-white shadow-[0_12px_32px_rgba(15,23,42,0.06)]">
|
||||
<div className="bg-gradient-to-b from-red-50 to-white px-5 pb-5 pt-8 text-center">
|
||||
@@ -93,28 +93,28 @@ export function CheckWinningScreen() {
|
||||
<BriefcaseBusiness className="size-12" />
|
||||
</div>
|
||||
<h2 className="mt-5 text-lg font-black text-slate-950">
|
||||
{t("results.check.enterTicket", { defaultValue: "输入你的票号或号码" })}
|
||||
{t("results.check.enterTicket")}
|
||||
</h2>
|
||||
<p className="mx-auto mt-2 max-w-xs text-sm leading-relaxed text-slate-500">
|
||||
{t("results.check.description", { defaultValue: "系统会按最新已发布期号查询你的注单和中奖情况。" })}
|
||||
{t("results.check.description")}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3 px-4 pb-4">
|
||||
<label className="block space-y-1.5">
|
||||
<span className="text-xs font-black text-slate-700">
|
||||
{t("results.check.ticketNumber", { defaultValue: "票号 / 号码" })}
|
||||
{t("results.check.ticketNumber")}
|
||||
</span>
|
||||
<Input
|
||||
value={ticketNo}
|
||||
placeholder={t("results.check.placeholder", { defaultValue: "请输入票号或号码" })}
|
||||
placeholder={t("results.check.placeholder")}
|
||||
onChange={(e) => setTicketNo(e.target.value)}
|
||||
className="h-12 rounded-xl border-[#dce7f7] bg-white font-mono text-base font-bold"
|
||||
/>
|
||||
</label>
|
||||
{latestDraw ? (
|
||||
<p className="text-xs text-slate-500">
|
||||
{t("results.check.latestDraw", { drawNo: latestDraw.draw_no, defaultValue: "最新期号 {{drawNo}}" })}
|
||||
{t("results.check.latestDraw", { drawNo: latestDraw.draw_no })}
|
||||
</p>
|
||||
) : null}
|
||||
{error ? <p className="text-sm font-semibold text-[#e5002c]">{error}</p> : null}
|
||||
@@ -124,7 +124,7 @@ export function CheckWinningScreen() {
|
||||
onClick={() => void runCheck()}
|
||||
className="h-12 w-full rounded-xl bg-[#e5002c] text-base font-black text-white hover:bg-[#d10028]"
|
||||
>
|
||||
{loading ? t("actions.loading", { defaultValue: "查询中..." }) : t("results.check.submit", { defaultValue: "立即查询" })}
|
||||
{loading ? t("results.check.loading") : t("results.check.submit")}
|
||||
</Button>
|
||||
</div>
|
||||
</section>
|
||||
@@ -132,18 +132,18 @@ export function CheckWinningScreen() {
|
||||
<section className="rounded-2xl border border-[#dfe8f6] bg-white p-4 shadow-[0_10px_26px_rgba(15,23,42,0.05)]">
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="font-black text-slate-950">
|
||||
{t("results.check.recent", { defaultValue: "最近查询" })}
|
||||
{t("results.check.recent")}
|
||||
</h3>
|
||||
{recent.length > 0 ? (
|
||||
<button type="button" className="text-sm font-bold text-[#0b56b7]" onClick={() => setRecent([])}>
|
||||
{t("actions.clear", { defaultValue: "清空" })}
|
||||
{t("actions.clear")}
|
||||
</button>
|
||||
) : null}
|
||||
</div>
|
||||
<div className="mt-3 divide-y divide-[#edf2f9]">
|
||||
{recent.length === 0 ? (
|
||||
<p className="py-4 text-sm text-slate-500">
|
||||
{t("results.check.noRecent", { defaultValue: "暂无查询记录。" })}
|
||||
{t("results.check.noRecent")}
|
||||
</p>
|
||||
) : (
|
||||
recent.map((row) => (
|
||||
@@ -213,7 +213,7 @@ function WinningResultDialog({
|
||||
type="button"
|
||||
onClick={() => onOpenChange(false)}
|
||||
className="absolute right-3 top-3 z-10 inline-flex size-9 items-center justify-center rounded-full text-slate-500 hover:bg-slate-100"
|
||||
aria-label={t("actions.close", { defaultValue: "关闭" })}
|
||||
aria-label={t("actions.close")}
|
||||
>
|
||||
<XIcon className="size-5" />
|
||||
</button>
|
||||
@@ -224,11 +224,11 @@ function WinningResultDialog({
|
||||
</div>
|
||||
<DialogTitle className="mt-3 text-xl font-black text-slate-950">
|
||||
{isWon
|
||||
? t("results.check.winTitle", { defaultValue: "恭喜,你中奖了" })
|
||||
: t("results.check.noWinTitle", { defaultValue: "未查询到中奖" })}
|
||||
? t("results.check.winTitle")
|
||||
: t("results.check.noWinTitle")}
|
||||
</DialogTitle>
|
||||
<DialogDescription className="text-sm text-slate-500">
|
||||
{t("results.check.ticketNumber", { defaultValue: "票号 / 号码" })}
|
||||
{t("results.check.ticketNumber")}
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
|
||||
@@ -239,15 +239,15 @@ function WinningResultDialog({
|
||||
<div className="mt-5 grid grid-cols-2 overflow-hidden rounded-xl border border-emerald-100 bg-emerald-50 text-center">
|
||||
<div className="border-r border-emerald-100 px-3 py-4">
|
||||
<p className="text-xs font-medium text-slate-500">
|
||||
{t("results.check.match", { defaultValue: "匹配" })}
|
||||
{t("results.check.match")}
|
||||
</p>
|
||||
<p className="mt-2 text-lg font-black text-[#0a8f3e]">
|
||||
{firstTicket ? playLabel(firstTicket.play_code, t) : isWon ? t("orders.hit", { defaultValue: "命中" }) : "—"}
|
||||
{firstTicket ? playLabel(firstTicket.play_code, t) : isWon ? t("orders.hit") : "—"}
|
||||
</p>
|
||||
</div>
|
||||
<div className="px-3 py-4">
|
||||
<p className="text-xs font-medium text-slate-500">
|
||||
{t("results.check.amount", { defaultValue: "中奖金额" })}
|
||||
{t("results.check.amount")}
|
||||
</p>
|
||||
<p className="mt-2 font-mono text-lg font-black text-[#0a8f3e]">
|
||||
{formatMinorAsCurrency(totalWin, firstTicket?.currency_code ?? "NPR")}
|
||||
@@ -257,11 +257,11 @@ function WinningResultDialog({
|
||||
|
||||
<div className="mt-5 rounded-xl border border-[#e8eef7] bg-white p-4 text-sm">
|
||||
<p className="font-black text-slate-950">
|
||||
{t("results.check.drawInfo", { defaultValue: "开奖信息" })}
|
||||
{t("results.check.drawInfo")}
|
||||
</p>
|
||||
<div className="mt-3 grid grid-cols-2 gap-4 text-slate-500">
|
||||
<div>
|
||||
<p className="text-xs">{t("results.check.issueNo", { defaultValue: "期号" })}</p>
|
||||
<p className="text-xs">{t("results.check.issueNo")}</p>
|
||||
<p className="mt-1 font-mono font-black text-slate-900">{data.draw.draw_no}</p>
|
||||
</div>
|
||||
<div>
|
||||
@@ -283,7 +283,7 @@ function WinningResultDialog({
|
||||
className="h-12 rounded-xl bg-[#07459f] text-base font-black text-white hover:bg-[#063b88]"
|
||||
render={<Link href={`/orders?draw_no=${encodeURIComponent(data.draw.draw_no)}&number=${encodeURIComponent(query)}`} />}
|
||||
>
|
||||
{t("results.check.viewBetDetails", { defaultValue: "查看注单详情" })}
|
||||
{t("results.check.viewBetDetails")}
|
||||
</Button>
|
||||
<Button
|
||||
type="button"
|
||||
@@ -292,7 +292,7 @@ function WinningResultDialog({
|
||||
onClick={onCheckAnother}
|
||||
>
|
||||
<RefreshCw className="size-5" />
|
||||
{t("results.check.checkAnother", { defaultValue: "查询另一张票" })}
|
||||
{t("results.check.checkAnother")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -27,7 +27,7 @@ const RESULTS_PAGE_SIZE = 10;
|
||||
|
||||
const MONTH_OPTIONS = Array.from({ length: 12 }, (_, value) => ({
|
||||
value,
|
||||
label: `${value + 1}月`,
|
||||
labelKey: `calendar.months.${value + 1}`,
|
||||
}));
|
||||
|
||||
export function DrawResultsListScreen() {
|
||||
@@ -46,7 +46,6 @@ export function DrawResultsListScreen() {
|
||||
const businessDate = /^\d{4}-\d{2}-\d{2}$/.test(date) ? date : undefined;
|
||||
const quickYears = useMemo(() => buildYearOptions(calendarMonth), [calendarMonth]);
|
||||
const featured = items?.[0] ?? null;
|
||||
const olderDraw = items?.[1] ?? null;
|
||||
|
||||
const fetchList = useCallback(async (targetPage = 1, append = false) => {
|
||||
setError(null);
|
||||
@@ -121,7 +120,10 @@ export function DrawResultsListScreen() {
|
||||
className="h-10 flex-1 justify-start rounded-lg border-[#dce7f7] bg-white px-3 text-left text-sm font-semibold text-[#32518d] hover:bg-[#f8fbff]"
|
||||
>
|
||||
<CalendarIcon className="mr-2 size-4 text-[#7890b8]" />
|
||||
{date || t("results.selectBusinessDate", { defaultValue: "选择日期" })}
|
||||
{date ||
|
||||
t("results.selectBusinessDate", {
|
||||
defaultValue: "选择日期",
|
||||
})}
|
||||
</Button>
|
||||
}
|
||||
/>
|
||||
@@ -139,7 +141,7 @@ export function DrawResultsListScreen() {
|
||||
<SelectContent className="max-h-64 min-w-24">
|
||||
{MONTH_OPTIONS.map((month) => (
|
||||
<SelectItem key={month.value} value={String(month.value)}>
|
||||
{month.label}
|
||||
{t(month.labelKey)}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
@@ -183,7 +185,7 @@ export function DrawResultsListScreen() {
|
||||
size="icon"
|
||||
variant="outline"
|
||||
className="h-10 w-10 rounded-lg border-[#dce7f7] bg-white text-[#7890b8] hover:bg-[#f8fbff] hover:text-[#32518d]"
|
||||
aria-label={t("actions.clear", { defaultValue: "清除" })}
|
||||
aria-label={t("actions.clear")}
|
||||
onClick={() => setDate("")}
|
||||
>
|
||||
<XIcon className="size-4" />
|
||||
@@ -226,44 +228,28 @@ export function DrawResultsListScreen() {
|
||||
<div className="space-y-3">
|
||||
{featured ? (
|
||||
<div className="rounded-xl border border-[#e5edf8] bg-white p-3 shadow-[0_10px_28px_rgba(15,23,42,0.06)]">
|
||||
<div className="flex flex-wrap items-center justify-between gap-2 border-b border-[#edf2f9] pb-3">
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="min-w-[5rem] rounded-full border-[#e6edf8] bg-white text-slate-400"
|
||||
disabled
|
||||
>
|
||||
{t("results.next")}
|
||||
</Button>
|
||||
<div className="min-w-0 flex-1 text-center">
|
||||
<p className="font-mono text-lg font-black text-[#0b3f96]">
|
||||
<div className="flex flex-wrap items-start justify-between gap-3 border-b border-[#edf2f9] pb-3">
|
||||
<div className="min-w-0">
|
||||
<p className="text-[11px] font-black uppercase tracking-normal text-[#0b56b7]">
|
||||
{t("results.detailTitle")}
|
||||
</p>
|
||||
<p className="mt-1 font-mono text-lg font-black text-[#0b3f96]">
|
||||
{featured.draw_no}
|
||||
</p>
|
||||
<p className="mt-1 font-mono text-xs text-slate-500">
|
||||
{t("results.drawTime", {
|
||||
time: formatLotteryInstant(featured.draw_time_iso ?? featured.draw_time ?? null),
|
||||
time: formatLotteryInstant(
|
||||
featured.draw_time_iso ?? featured.draw_time ?? null,
|
||||
),
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
{olderDraw ? (
|
||||
<Link
|
||||
href={`/results/${encodeURIComponent(olderDraw.draw_no)}`}
|
||||
className="inline-flex h-7 min-w-[5rem] items-center justify-center rounded-full border border-[#dce7f7] bg-white px-2.5 text-[0.8rem] font-medium text-[#0b56b7] transition-colors hover:bg-[#f1f6ff]"
|
||||
>
|
||||
{t("results.previous")}
|
||||
</Link>
|
||||
) : (
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
className="min-w-[5rem] rounded-full border-[#e6edf8] bg-white text-slate-400"
|
||||
disabled
|
||||
>
|
||||
{t("results.previous")}
|
||||
</Button>
|
||||
)}
|
||||
<Link
|
||||
href={`/results/${encodeURIComponent(featured.draw_no)}`}
|
||||
className="inline-flex h-8 shrink-0 items-center justify-center rounded-full border border-[#dce7f7] bg-white px-3 text-sm font-semibold text-[#0b56b7] transition-colors hover:bg-[#f1f6ff]"
|
||||
>
|
||||
{t("results.openDetail", { defaultValue: "查看详情" })}
|
||||
</Link>
|
||||
</div>
|
||||
<div className="pt-4">
|
||||
<TwentyThreeResultsGrid numbers={featured.results} />
|
||||
@@ -323,12 +309,12 @@ export function DrawResultsListScreen() {
|
||||
onClick={() => void fetchList(page + 1, true)}
|
||||
>
|
||||
{loadingMore
|
||||
? t("actions.loading", { defaultValue: "加载中..." })
|
||||
: t("actions.loadMore", { defaultValue: "加载更多" })}
|
||||
? t("actions.loading")
|
||||
: t("actions.loadMore")}
|
||||
</Button>
|
||||
) : items && items.length > 0 ? (
|
||||
<p className="py-2 text-center text-xs text-slate-400">
|
||||
{t("results.noMore", { defaultValue: "没有更多开奖结果" })}
|
||||
{t("results.noMore")}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
@@ -56,7 +56,7 @@ export function JackpotResultsStrip({
|
||||
</p>
|
||||
{gap !== null ? (
|
||||
<p className="mt-1 text-xs font-semibold text-amber-800">
|
||||
{t("results.jackpotGap", { count: gap, defaultValue: "距上次爆池 {{count}} 期" })}
|
||||
{t("results.jackpotGap", { count: gap })}
|
||||
</p>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user