feat(player): PC 侧栏布局、顶栏与页内间距优化
Some checks failed
lotteryfront CI / build (push) Has been cancelled

- 桌面端侧栏 + 全局顶栏(Logo 在侧栏顶、激活态对齐后台红底白字)
- 移除各页重复页内顶栏,统一主区与卡片顶部留白
- 视口遮罩/弹层约束到主内容区,移动端保留底栏与顶栏 Logo
This commit is contained in:
2026-06-26 18:02:19 +08:00
parent e5622c58cb
commit 7d03ffa30b
24 changed files with 394 additions and 181 deletions

View File

@@ -1,21 +1,16 @@
"use client";
import { BookOpen } from "lucide-react";
import Image from "next/image";
import Link from "next/link";
import { useTranslation } from "react-i18next";
import { LanguageSwitcher } from "@/components/language-switcher";
import { HallBettingGrid } from "@/features/hall/hall-betting-grid";
import { usePlayerStickyTopStyle } from "@/hooks/use-player-sticky-top-style";
import { useActivePlayerCurrency } from "@/hooks/use-active-player-currency";
import { HallDrawPanel } from "@/features/hall/hall-draw-panel";
import { HallWalletStrip } from "@/features/hall/hall-wallet-strip";
import { JackpotBurstOverlay } from "@/features/hall/jackpot-burst-overlay";
import { useHallDrawLive } from "@/features/hall/use-hall-draw-live";
import { useJackpotBurstLive } from "@/features/hall/use-jackpot-burst-live";
import { playerHeaderControl, playerPageHeader, playerPageInset } from "@/lib/player-spacing";
import { playerViewportColumnClass } from "@/lib/player-viewport";
import { playerPageInset } from "@/lib/player-spacing";
import { playerContentColumnClass } from "@/lib/player-viewport";
import { cn } from "@/lib/utils";
/**
@@ -26,52 +21,15 @@ export function HallScreen() {
const drawLive = useHallDrawLive();
const { activeCurrency } = useActivePlayerCurrency();
const { burstEvent, clearBurstEvent } = useJackpotBurstLive(tp);
const stickyTopStyle = usePlayerStickyTopStyle();
return (
<div className={playerViewportColumnClass}>
<section className={cn("bg-white text-slate-900", playerPageInset)}>
<header
className={cn(
playerPageHeader,
"sticky z-20 mb-2 flex min-h-9 items-center gap-2 overflow-visible bg-white pt-2 pb-2 -mx-3 px-3",
)}
style={stickyTopStyle}
>
<div className="flex min-w-0 flex-1 items-center">
<Image
src="/logo.png"
alt="Nlotto"
width={243}
height={84}
className="h-7 w-auto max-w-[min(100%,160px)] object-contain object-left"
priority
/>
</div>
<div className="flex shrink-0 items-center gap-1">
<LanguageSwitcher
variant="minimal"
menuAlign="end"
showFlag={false}
className={cn(
playerHeaderControl,
"rounded-full border border-[#e4eaf4] bg-[#f8fafc] [&_button]:h-8 [&_button]:gap-1 [&_button]:px-2 [&_button]:py-0 [&_button]:text-xs",
)}
/>
<Link
href="/rules"
className={cn(
playerHeaderControl,
"rounded-full border border-[#e4eaf4] bg-[#f8fafc] px-2.5 text-xs font-bold text-[#0b3f96] hover:bg-[#f1f6ff]",
)}
aria-label={tp("nav.rules")}
>
<BookOpen className="size-3.5 shrink-0" aria-hidden />
<span>{tp("nav.rules")}</span>
</Link>
</div>
</header>
<div className={playerContentColumnClass}>
<section
className={cn(
"bg-white text-slate-900 lg:rounded-2xl lg:border lg:border-[#e4ebf5] lg:shadow-sm",
playerPageInset,
)}
>
<HallDrawPanel drawLive={drawLive} />
<HallWalletStrip />
@@ -81,4 +39,4 @@ export function HallScreen() {
<JackpotBurstOverlay event={burstEvent} onClose={clearBurstEvent} />
</div>
);
}
}

View File

@@ -7,6 +7,7 @@ import { useTranslation } from "react-i18next";
import { getDrawResultByNo } from "@/api/draw";
import { Button } from "@/components/ui/button";
import { formatMinorAsCurrency } from "@/lib/money";
import { playerViewportOverlayClass } from "@/lib/player-viewport";
import { cn } from "@/lib/utils";
export type JackpotBurstEvent = {
@@ -214,7 +215,10 @@ function JackpotBurstOverlayContent({
return (
<div
className="fixed inset-0 z-[90] flex items-center justify-center bg-white/88 px-4 py-6 text-slate-900 backdrop-blur-[2px] animate-[jackpot-backdrop-in_0.35s_ease-out]"
className={cn(
playerViewportOverlayClass,
"z-[90] flex items-center justify-center bg-white/88 px-4 py-6 text-slate-900 backdrop-blur-[2px] animate-[jackpot-backdrop-in_0.35s_ease-out]",
)}
style={{
backgroundImage:
"radial-gradient(ellipse 75% 55% at 50% 38%, rgba(245,197,66,0.14) 0%, transparent 60%)",

View File

@@ -450,7 +450,7 @@ export function EntryGate() {
<div className="flex flex-1 flex-col px-3 py-5">
{phase === "loading" || waitingForEmbeddedToken ? (
<div className="mx-auto w-full max-w-md">
<div className="mx-auto w-full max-w-md lg:max-w-lg">
<div className="mb-6 flex items-center gap-2">
<div className="flex size-8 items-center justify-center rounded bg-red-600 text-white">
<Globe className="size-5" aria-hidden />
@@ -525,7 +525,7 @@ export function EntryGate() {
) : null}
{phase === "failed" ? (
<div className="mx-auto w-full max-w-md">
<div className="mx-auto w-full max-w-md lg:max-w-lg">
<div className="mb-6 text-center">
<div className="mx-auto mb-4 flex size-16 items-center justify-center rounded-full bg-red-100 text-red-600">
<AlertTriangle className="size-8" aria-hidden />
@@ -601,7 +601,7 @@ export function EntryGate() {
) : null}
{phase === "success" ? (
<div className="mx-auto w-full max-w-md text-center">
<div className="mx-auto w-full max-w-md text-center lg:max-w-lg">
<div className="mb-6">
<div className="mx-auto mb-4 flex size-16 items-center justify-center rounded-full bg-green-100 text-green-600">
<CheckCircle2 className="size-8" aria-hidden />

View File

@@ -160,7 +160,7 @@ export function PlayerLoginScreen(): React.ReactElement {
</div>
</div>
<div className="mx-auto w-full max-w-md flex-1 px-4 py-8">
<div className="mx-auto w-full max-w-md flex-1 px-4 py-8 lg:max-w-lg lg:px-8">
<h1 className="text-xl font-bold text-gray-900">
{t("login.title")}
</h1>

View File

@@ -117,7 +117,8 @@ export function DrawResultsListScreen() {
<p className="mb-2 text-xs font-bold text-[#32518d]">
{t("results.businessDate")}
</p>
<div className="flex gap-2">
<div className="flex min-w-0 gap-2">
<div className="min-w-0 flex-1">
<Popover open={datePickerOpen} onOpenChange={setDatePickerOpen}>
<PopoverTrigger
render={
@@ -186,6 +187,7 @@ export function DrawResultsListScreen() {
/>
</PopoverContent>
</Popover>
</div>
{date ? (
<Button
type="button"
@@ -201,7 +203,7 @@ export function DrawResultsListScreen() {
<Button
type="button"
size="sm"
className="h-10 rounded-lg bg-[#07459f] px-4 text-white hover:bg-[#063b88]"
className="h-10 shrink-0 rounded-lg bg-[#07459f] px-4 text-white hover:bg-[#063b88]"
onClick={() => void fetchList(1, false)}
>
{t("actions.apply")}