feat: 优化大厅下注结果与风险提示展示,重构期号与订单详情样式

This commit is contained in:
2026-05-15 15:31:00 +08:00
parent f2c7f5e4f1
commit a83920aa2a
13 changed files with 1078 additions and 492 deletions

View File

@@ -0,0 +1,179 @@
"use client";
import { CheckCircle2, ChevronRight } from "lucide-react";
import { useTranslation } from "react-i18next";
import { Button } from "@/components/ui/button";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Separator } from "@/components/ui/separator";
import { formatMinorAsCurrency } from "@/lib/money";
import { playLabel } from "@/lib/play-labels";
import type { TicketPlaceData } from "@/types/api/ticket";
type HallBetResultDialogProps = {
open: boolean;
onOpenChange: (open: boolean) => void;
currencyCode: string;
data: TicketPlaceData | null;
};
export function HallBetResultDialog({
open,
onOpenChange,
currencyCode,
data,
}: HallBetResultDialogProps) {
const { t } = useTranslation("player");
const totalItems = data?.items.length ?? 0;
const totalSuccess = totalItems;
const totalFailure = 0;
return (
<Dialog open={open} onOpenChange={onOpenChange}>
<DialogContent className="max-h-[min(90vh,620px)] gap-0 overflow-hidden border-[#e8eef7] p-0 shadow-[0_12px_40px_rgba(15,23,42,0.12)] ring-[#e8eef7] sm:max-w-md">
<div className="p-4 pb-2">
<DialogHeader>
<DialogTitle className="text-lg font-black text-[#0b3f96]">
{t("hall.result.title", { defaultValue: "下注结果" })}
</DialogTitle>
<DialogDescription className="text-xs leading-relaxed text-slate-500">
{t("hall.result.description", {
defaultValue: "本次提交已完成,以下为本次结果明细。",
})}
</DialogDescription>
</DialogHeader>
</div>
<ScrollArea className="max-h-[min(56vh,400px)] border-y border-[#e8eef7] px-4">
<div className="space-y-4 py-3 pr-3">
{!data ? (
<p className="text-sm text-slate-500">
{t("hall.result.empty", { defaultValue: "暂无结果。" })}
</p>
) : (
<>
<div className="rounded-xl border border-[#e8eef7] bg-[#f8fbff] p-3 text-sm shadow-[0_4px_14px_rgba(15,23,42,0.04)]">
<div className="flex items-center gap-2">
<CheckCircle2 className="size-4 text-emerald-600" />
<span className="font-semibold text-slate-800">
{t("hall.result.orderNo", {
defaultValue: "订单号",
})}{" "}
<span className="font-mono font-black text-[#0b3f96]">{data.order_no}</span>
</span>
</div>
<p className="mt-2 text-xs text-slate-500">
{t("hall.result.draw", { defaultValue: "期号" })}{" "}
<span className="font-mono font-semibold text-[#32518d]">{data.draw.draw_id}</span>
</p>
<Separator className="my-3 bg-[#e8eef7]" />
<ul className="space-y-1 text-xs tabular-nums text-slate-700">
<li>
{t("hall.result.successCount", { defaultValue: "成功数" })}{" "}
<span className="font-semibold text-emerald-700">{totalSuccess}</span>
</li>
<li>
{t("hall.result.failureCount", { defaultValue: "失败数" })}{" "}
<span className="font-semibold text-rose-600">{totalFailure}</span>
</li>
<li>
{t("hall.result.totalDeduct", { defaultValue: "成功扣款" })}{" "}
<span className="font-bold text-[#0b3f96]">
{formatMinorAsCurrency(data.summary.total_actual_deduct, currencyCode)}
</span>
</li>
<li>
{t("hall.result.balanceAfter", { defaultValue: "剩余余额" })}{" "}
<span className="font-semibold text-slate-800">
{formatMinorAsCurrency(data.balance_after, currencyCode)}
</span>
</li>
</ul>
</div>
<div className="space-y-2">
<p className="text-[11px] font-semibold uppercase tracking-wide text-slate-400">
{t("hall.result.items", {
defaultValue: "每一注成功/失败详情",
})}
</p>
<ul className="space-y-2 text-sm">
{data.items.map((item, index) => (
<li
key={`${item.ticket_no}-${index}`}
className="rounded-xl border border-[#e6edf8] bg-white px-3 py-2 shadow-[0_4px_14px_rgba(15,23,42,0.04)]"
>
<div className="flex items-center justify-between gap-2">
<span className="font-mono text-xs text-slate-400">
#{index + 1}
</span>
<span className="text-xs font-semibold text-emerald-700">
{t("hall.result.success", { defaultValue: "成功" })}
</span>
</div>
<div className="mt-1 flex items-center justify-between gap-2">
<span className="font-semibold text-[#32518d]">{playLabel(item.play_code, t)}</span>
<span className="font-mono text-xs text-slate-500">
{item.ticket_no}
</span>
</div>
<div className="mt-2 grid grid-cols-2 gap-x-2 gap-y-1 text-xs tabular-nums">
<span className="text-slate-500">
{t("hall.result.number", { defaultValue: "号码" })}
</span>
<span className="text-right font-mono font-black text-[#0b3f96]">{item.number}</span>
<span className="text-slate-500">
{t("hall.result.comboCount", { defaultValue: "组合数" })}
</span>
<span className="text-right">{item.combination_count}</span>
<span className="text-slate-500">
{t("hall.result.actualDeduct", { defaultValue: "实扣" })}
</span>
<span className="text-right font-semibold text-[#0b3f96]">
{formatMinorAsCurrency(item.actual_deduct_amount, currencyCode)}
</span>
<span className="text-slate-500">
{t("hall.result.estimatedMax", { defaultValue: "最坏赔付" })}
</span>
<span className="text-right">
{formatMinorAsCurrency(item.estimated_max_payout, currencyCode)}
</span>
</div>
{item.combination_count > 1 ? (
<div className="mt-2 inline-flex items-center gap-1 rounded-full border border-[#dce7f7] bg-[#f8fbff] px-2 py-1 text-[11px] text-[#07459f]">
<ChevronRight className="size-3" />
{t("hall.result.comboHint", {
defaultValue: "已按展开组合分摊",
})}
</div>
) : null}
</li>
))}
</ul>
</div>
</>
)}
</div>
</ScrollArea>
<div className="flex justify-end border-t border-[#e8eef7] bg-[#f8fbff] p-4">
<Button
type="button"
onClick={() => onOpenChange(false)}
className="h-11 rounded-xl border-0 bg-[#07459f] px-6 text-sm font-bold text-white hover:bg-[#063b88]"
>
{t("actions.close", { defaultValue: "关闭" })}
</Button>
</div>
</DialogContent>
</Dialog>
);
}