refactor: 重构大厅组件以优化状态管理与数据加载
- 在 HallDrawPanel 组件中引入 useHallDrawLive 自定义 Hook,简化状态管理与数据获取逻辑 - 移除不必要的状态与副作用,提升组件性能 - 在 HallScreen 组件中替换 Card 组件为 HallBettingGrid,优化下注表格展示 - 在 HallWalletStrip 组件中添加事件监听以支持钱包刷新功能
This commit is contained in:
170
src/features/hall/hall-bet-preview-dialog.tsx
Normal file
170
src/features/hall/hall-bet-preview-dialog.tsx
Normal file
@@ -0,0 +1,170 @@
|
||||
"use client";
|
||||
|
||||
import { AlertTriangleIcon } from "lucide-react";
|
||||
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||
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 type { TicketPreviewData, TicketPreviewWarning } from "@/types/api/ticket";
|
||||
|
||||
type HallBetPreviewDialogProps = {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
currencyCode: string;
|
||||
data: TicketPreviewData | null;
|
||||
placing: boolean;
|
||||
onConfirmPlace: () => void;
|
||||
};
|
||||
|
||||
function WarningsBlock({ warnings }: { warnings: TicketPreviewWarning[] }) {
|
||||
if (warnings.length === 0) return null;
|
||||
return (
|
||||
<Alert className="border-amber-500/40 bg-amber-500/5 text-amber-950 dark:text-amber-100">
|
||||
<AlertTriangleIcon />
|
||||
<AlertTitle>赔付池预警</AlertTitle>
|
||||
<AlertDescription className="space-y-1">
|
||||
<p className="text-xs leading-relaxed">
|
||||
产品文档 §6.4:以下号码本期赔付池占用较高,仍允许下注;若实际占用不足将售罄拒单。
|
||||
</p>
|
||||
<ul className="list-inside list-disc text-xs">
|
||||
{warnings.map((w, i) => (
|
||||
<li key={`${w.number_4d}-${i}`}>
|
||||
<span className="font-mono">{w.number_4d}</span> — {w.message}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 预览弹窗 + 提交确认(产品文档 §10.1.2:预览不下单,确认后 place)。
|
||||
*/
|
||||
export function HallBetPreviewDialog({
|
||||
open,
|
||||
onOpenChange,
|
||||
currencyCode,
|
||||
data,
|
||||
placing,
|
||||
onConfirmPlace,
|
||||
}: HallBetPreviewDialogProps) {
|
||||
const summary = data?.summary;
|
||||
const lines = data?.lines ?? [];
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent className="max-h-[min(90vh,560px)] gap-0 overflow-hidden p-0 sm:max-w-md">
|
||||
<div className="p-4 pb-2">
|
||||
<DialogHeader>
|
||||
<DialogTitle>确认下注</DialogTitle>
|
||||
<DialogDescription>
|
||||
请核对号码、玩法与实扣金额;确认后将扣减彩票钱包且不可撤单(产品文档 §6.3)。
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
</div>
|
||||
|
||||
<ScrollArea className="max-h-[min(52vh,360px)] border-y px-4">
|
||||
<div className="space-y-4 py-3 pr-3">
|
||||
{!data ? (
|
||||
<p className="text-sm text-muted-foreground">暂无预览数据</p>
|
||||
) : (
|
||||
<>
|
||||
<div className="rounded-lg border bg-muted/30 p-3 text-xs">
|
||||
<p>
|
||||
期号{" "}
|
||||
<span className="font-mono font-semibold">{data.draw.draw_id}</span> · 状态{" "}
|
||||
<span className="font-medium">{data.draw.status}</span>
|
||||
</p>
|
||||
{summary ? (
|
||||
<ul className="mt-2 space-y-1 tabular-nums">
|
||||
<li>
|
||||
总下注{" "}
|
||||
<span className="font-medium">
|
||||
{formatMinorAsCurrency(summary.total_bet_amount, currencyCode)}
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
回水抵扣{" "}
|
||||
<span className="font-medium">
|
||||
{formatMinorAsCurrency(summary.total_rebate_amount, currencyCode)}
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
实扣金额{" "}
|
||||
<span className="font-semibold text-primary">
|
||||
{formatMinorAsCurrency(summary.total_actual_deduct, currencyCode)}
|
||||
</span>
|
||||
</li>
|
||||
<li>
|
||||
预估最高赔付{" "}
|
||||
<span className="font-medium">
|
||||
{formatMinorAsCurrency(summary.total_estimated_payout, currencyCode)}
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
<WarningsBlock warnings={data.warnings} />
|
||||
|
||||
<div className="space-y-2">
|
||||
<p className="text-xs font-medium text-muted-foreground">注项明细</p>
|
||||
<ul className="space-y-2 text-sm">
|
||||
{lines.map((ln) => (
|
||||
<li
|
||||
key={ln.client_line_no}
|
||||
className="rounded-md border border-border/80 bg-card px-2 py-2"
|
||||
>
|
||||
<div className="flex flex-wrap items-baseline justify-between gap-2">
|
||||
<span className="font-mono text-xs text-muted-foreground">
|
||||
#{ln.client_line_no}
|
||||
</span>
|
||||
<span className="font-mono font-medium">{ln.play_code}</span>
|
||||
</div>
|
||||
<p className="mt-1 font-mono text-base">{ln.number}</p>
|
||||
<Separator className="my-2" />
|
||||
<div className="grid grid-cols-2 gap-x-2 gap-y-1 text-xs tabular-nums">
|
||||
<span className="text-muted-foreground">归一号码</span>
|
||||
<span className="text-right font-mono">{ln.normalized_number}</span>
|
||||
<span className="text-muted-foreground">组合数</span>
|
||||
<span className="text-right">{ln.combination_count}</span>
|
||||
<span className="text-muted-foreground">实扣</span>
|
||||
<span className="text-right">
|
||||
{formatMinorAsCurrency(ln.actual_deduct_amount, currencyCode)}
|
||||
</span>
|
||||
<span className="text-muted-foreground">预估最高赔</span>
|
||||
<span className="text-right">
|
||||
{formatMinorAsCurrency(ln.estimated_max_payout, currencyCode)}
|
||||
</span>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
|
||||
<div className="flex flex-col-reverse gap-2 border-t bg-muted/30 p-4 sm:flex-row sm:justify-between">
|
||||
<Button type="button" variant="outline" onClick={() => onOpenChange(false)} disabled={placing}>
|
||||
返回修改
|
||||
</Button>
|
||||
<Button type="button" onClick={onConfirmPlace} disabled={!data || placing}>
|
||||
{placing ? "提交中…" : "确认提交"}
|
||||
</Button>
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user