Files
lotteryFront/src/components/layout/player-bottom-nav.tsx
kang 7c283627d3 docs: 新增 AI 代理学习偏好与信用盘术语规范
feat: 优化登录与入口页面加载体验

- 为登录页面添加 Suspense 边界,统一 fallback 样式
- 重构入口守卫逻辑,在布局阶段处理未登录重定向,避免闪烁
- 登录页面支持会话过期提示,并自动清理 URL 参数

feat: 信用盘与钱包盘文案差异化展示

- 底部导航「钱包」标签在信用盘模式下显示为「信用」
- 大厅余额条在信用盘模式下使用「可
2026-06-12 20:48:10 +08:00

96 lines
3.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { BarChart3, ClipboardList, Home, Wallet } from "lucide-react";
import { useTranslation } from "react-i18next";
import { playerViewportFixedBarClass } from "@/lib/player-viewport";
import { isCreditFundingPlayer } from "@/lib/player-funding-mode";
import { cn } from "@/lib/utils";
import { usePlayerSessionStore } from "@/stores/player-session-store";
const tabs = [
{
href: "/hall",
labelKey: "nav.home",
labelDefault: "首页",
icon: Home,
match: (p: string) => p === "/hall",
},
{
href: "/results",
labelKey: "nav.results",
labelDefault: "开奖结果",
icon: BarChart3,
match: (p: string) => p === "/results" || p.startsWith("/results/"),
},
{
href: "/orders",
labelKey: "nav.orders",
labelDefault: "我的注单",
icon: ClipboardList,
match: (p: string) => p === "/orders" || p.startsWith("/orders/"),
},
{
href: "/wallet",
labelKey: "nav.wallet",
creditLabelKey: "nav.credit",
labelDefault: "钱包",
creditLabelDefault: "信用",
icon: Wallet,
match: (p: string) => p === "/wallet" || p.startsWith("/wallet/"),
},
] as const;
/**
* 移动端 H5 主导航:底部 Tab界面文档 §1.1 以手机为主)。
*/
export function PlayerBottomNav() {
const pathname = usePathname() ?? "";
const { t } = useTranslation("player");
const creditMode = isCreditFundingPlayer(usePlayerSessionStore((state) => state.profile));
return (
<nav
className={cn(
playerViewportFixedBarClass,
"bottom-0 border-t border-[#e4ebf5] bg-white/96 pb-[env(safe-area-inset-bottom,0px)] shadow-[0_-10px_30px_rgba(15,23,42,0.08)] backdrop-blur-md",
)}
aria-label={t("nav.aria")}
>
<div className="grid h-14 w-full grid-cols-4 grid-rows-1">
{tabs.map(({ href, labelKey, labelDefault, icon: Icon, match, ...tab }) => {
const active = match(pathname);
const creditLabelKey = "creditLabelKey" in tab ? tab.creditLabelKey : undefined;
const creditLabelDefault = "creditLabelDefault" in tab ? tab.creditLabelDefault : undefined;
const label = creditMode && creditLabelKey
? t(creditLabelKey, { defaultValue: creditLabelDefault ?? labelDefault })
: t(labelKey, { defaultValue: labelDefault });
return (
<Link
key={href}
href={href}
prefetch
aria-current={active ? "page" : undefined}
className={cn(
"relative flex min-h-0 min-w-0 max-w-full flex-col items-center justify-center gap-1 px-0.5 text-center text-[10px] font-semibold leading-tight transition-colors sm:text-[11px]",
active
? "text-[#f10b32]"
: "text-[#6b7280] hover:text-[#1f2937] active:text-[#1f2937]",
)}
>
<Icon
aria-hidden
className={cn("size-5 shrink-0 sm:size-[22px]", active && "stroke-[2.5px]")}
/>
<span className="w-full truncate">{label}</span>
</Link>
);
})}
</div>
</nav>
);
}