refactor: 重构整体页面布局与样式,统一UI设计风格

- 重构PlayerAppShell,移除冗余头部导航与国际化依赖,统一页面背景与内边距
- 新增通用页面容器组件PlayerPanel,统一页面头部布局与样式
- 重构底部导航栏,调整图标、文案与样式,新增激活状态指示器
- 重构所有页面组件:大厅页、注单页、结果页、开奖面板等,统一使用新的UI组件与设计风格
- 优化状态标签、卡片、按钮等组件的视觉样式,统一配色与圆角规范
- 移除冗余依赖与注释代码,整理代码结构
This commit is contained in:
2026-05-14 11:18:08 +08:00
parent f777888940
commit ac612cb32c
11 changed files with 1054 additions and 735 deletions

View File

@@ -4,22 +4,14 @@ import Link from "next/link";
import { useCallback, useEffect, useState } from "react";
import { getDrawResults } from "@/api/draw";
import { JackpotResultsStrip } from "@/features/results/jackpot-results-strip";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Skeleton } from "@/components/ui/skeleton";
import { PlayerPanel } from "@/components/layout/player-panel";
import { JackpotResultsStrip } from "@/features/results/jackpot-results-strip";
import { formatLotteryInstant } from "@/lib/player-datetime";
import type { DrawResultListItem } from "@/types/api/draw-results";
/** §4.6 历史列表 + 默认最新一期入口 */
export function DrawResultsListScreen() {
const [items, setItems] = useState<DrawResultListItem[] | null>(null);
const [error, setError] = useState<string | null>(null);
@@ -51,92 +43,93 @@ export function DrawResultsListScreen() {
}, [fetchList]);
return (
<div className="flex flex-col gap-4">
<JackpotResultsStrip currencyCode="NPR" />
<PlayerPanel title="Results" subtitle="Latest draw history" eyebrow="N lotto">
<div className="space-y-4">
<JackpotResultsStrip currencyCode="NPR" />
<div className="flex flex-col gap-2 sm:flex-row sm:items-end">
<div className="flex flex-1 flex-col gap-1.5">
<Label htmlFor="biz-date"></Label>
<Input
id="biz-date"
type="date"
value={date}
onChange={(e) => setDate(e.target.value)}
className="max-w-xs"
/>
</div>
<Button type="button" variant="secondary" size="sm" onClick={() => void fetchList()}>
</Button>
</div>
{loading ? (
<Card>
<CardHeader>
<Skeleton className="h-6 w-32" />
<Skeleton className="h-4 w-48" />
</CardHeader>
<CardContent className="space-y-3">
<Skeleton className="h-24 w-full" />
<Skeleton className="h-24 w-full" />
</CardContent>
</Card>
) : error ? (
<Card className="border-destructive/40">
<CardHeader>
<CardTitle className="text-base"></CardTitle>
<CardDescription>{error}</CardDescription>
</CardHeader>
<CardContent>
<Button type="button" size="sm" onClick={() => void fetchList()}>
<div className="rounded-xl border border-[#e6edf8] bg-[#f8fbff] p-3">
<p className="mb-2 text-xs font-bold text-[#32518d]">Business Date</p>
<div className="flex gap-2">
<Input
type="date"
value={date}
onChange={(e) => setDate(e.target.value)}
className="h-10 rounded-lg border-[#dce7f7] bg-white text-sm"
/>
<Button
type="button"
size="sm"
className="h-10 rounded-lg bg-[#07459f] px-4 text-white hover:bg-[#063b88]"
onClick={() => void fetchList()}
>
Apply
</Button>
</CardContent>
</Card>
) : items && items.length === 0 ? (
<Card>
<CardHeader>
<CardTitle className="text-base"></CardTitle>
<CardDescription></CardDescription>
</CardHeader>
</Card>
) : (
<div className="flex flex-col gap-3">
{items?.map((row) => (
<Card key={row.draw_no}>
<CardHeader className="pb-2">
<div className="flex flex-wrap items-center justify-between gap-2">
<CardTitle className="font-mono text-base">{row.draw_no}</CardTitle>
<Link
href={`/results/${encodeURIComponent(row.draw_no)}`}
className="text-sm font-medium text-primary underline-offset-4 hover:underline"
>
</Link>
</div>
<CardDescription className="font-mono text-xs">
{formatLotteryInstant(row.draw_time_iso ?? row.draw_time ?? null)}
</CardDescription>
</CardHeader>
<CardContent className="grid grid-cols-3 gap-2 text-center font-mono text-sm">
<div className="rounded-md border bg-card py-2">
<div className="text-[10px] uppercase text-muted-foreground">1st</div>
<div className="font-semibold">{row.results["1st"]}</div>
</div>
<div className="rounded-md border bg-card py-2">
<div className="text-[10px] uppercase text-muted-foreground">2nd</div>
<div className="font-semibold">{row.results["2nd"]}</div>
</div>
<div className="rounded-md border bg-card py-2">
<div className="text-[10px] uppercase text-muted-foreground">3rd</div>
<div className="font-semibold">{row.results["3rd"]}</div>
</div>
</CardContent>
</Card>
))}
</div>
</div>
)}
</div>
{loading ? (
<div className="space-y-3">
<Skeleton className="h-28 rounded-xl" />
<Skeleton className="h-28 rounded-xl" />
<Skeleton className="h-28 rounded-xl" />
</div>
) : error ? (
<div className="rounded-xl border border-red-200 bg-red-50 px-4 py-4 text-sm text-red-700">
<p>{error}</p>
<Button
type="button"
size="sm"
className="mt-3 bg-[#e5002c] text-white hover:bg-[#d10028]"
onClick={() => void fetchList()}
>
Retry
</Button>
</div>
) : items && items.length === 0 ? (
<div className="rounded-xl border border-dashed border-[#dce7f7] bg-[#f8fbff] px-4 py-10 text-center text-sm text-slate-500">
No results yet.
</div>
) : (
<div className="space-y-3">
{items?.map((row) => (
<Link
key={row.draw_no}
href={`/results/${encodeURIComponent(row.draw_no)}`}
className="block rounded-xl border border-[#e5edf8] bg-white p-3 shadow-[0_8px_24px_rgba(15,23,42,0.05)] transition-colors hover:border-[#b9ccf6]"
>
<div className="flex items-start justify-between gap-3">
<div className="min-w-0">
<p className="truncate font-mono text-sm font-black text-[#0b3f96]">
{row.draw_no}
</p>
<p className="mt-1 text-[11px] text-slate-500">
{formatLotteryInstant(row.draw_time_iso ?? row.draw_time ?? null)}
</p>
</div>
<span className="shrink-0 rounded-full bg-[#f2f6ff] px-2.5 py-1 text-xs font-bold text-[#0b56b7]">
Detail
</span>
</div>
<div className="mt-3 grid grid-cols-3 gap-2 text-center">
{[
["1st", row.results["1st"]],
["2nd", row.results["2nd"]],
["3rd", row.results["3rd"]],
].map(([label, value]) => (
<div key={label} className="rounded-lg border border-[#edf2f8] bg-[#f8fbff] py-2">
<p className="text-[10px] font-bold uppercase text-[#7890b8]">{label}</p>
<p className="mt-1 font-mono text-lg font-black tabular-nums text-[#e5002c]">
{value}
</p>
</div>
))}
</div>
</Link>
))}
</div>
)}
</div>
</PlayerPanel>
);
}

View File

@@ -42,11 +42,11 @@ export function JackpotResultsStrip({
}
return (
<div className="rounded-lg border border-amber-500/30 bg-gradient-to-r from-amber-500/10 via-amber-400/5 to-transparent px-3 py-2">
<p className="text-[11px] font-medium uppercase tracking-wide text-amber-800/90 dark:text-amber-200/90">
<div className="rounded-xl border border-amber-200 bg-gradient-to-r from-amber-100 via-white to-[#f8fbff] px-3 py-3 shadow-[0_8px_20px_rgba(180,83,9,0.08)]">
<p className="text-[11px] font-black uppercase tracking-normal text-amber-700">
Jackpot
</p>
<p className="font-mono text-sm font-semibold tabular-nums text-amber-950 dark:text-amber-50">
<p className="font-mono text-lg font-black tabular-nums text-[#0b3f96]">
{formatMinorAsCurrency(minor, currencyCode.toUpperCase())}
</p>
</div>