diff --git a/src/app/globals.css b/src/app/globals.css index 6747975..54e02ec 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -121,11 +121,14 @@ * { @apply border-border outline-ring/50; } - body { - @apply bg-background text-foreground; - } html { @apply font-sans; + -webkit-text-size-adjust: 100%; + } + body { + @apply bg-background text-foreground; + -webkit-tap-highlight-color: transparent; + -webkit-touch-callout: none; } html:lang(ne) { font-family: @@ -134,6 +137,9 @@ system-ui, sans-serif; } + button, a, [role="button"] { + touch-action: manipulation; + } } /* 玩家端 Toast:顶部居中、紧凑尺寸(位置见 components/ui/sonner.tsx) */ diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 0eede12..0cc0d2c 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -28,6 +28,8 @@ export const metadata: Metadata = { export const viewport = { width: "device-width", initialScale: 1, + maximumScale: 1, + userScalable: false, }; export default function RootLayout({ @@ -39,9 +41,9 @@ export default function RootLayout({ - + {children} diff --git a/src/components/layout/player-app-shell.tsx b/src/components/layout/player-app-shell.tsx index 0d7b427..38c9b60 100644 --- a/src/components/layout/player-app-shell.tsx +++ b/src/components/layout/player-app-shell.tsx @@ -4,6 +4,8 @@ import type { ReactNode } from "react"; import { NetworkStatusBanner } from "@/components/network-status-banner"; import { PlayerBottomNav } from "@/components/layout/player-bottom-nav"; +import { PullToRefreshIndicator } from "@/components/pull-to-refresh-indicator"; +import { usePullToRefresh } from "@/hooks/use-pull-to-refresh"; type PlayerAppShellProps = { children: ReactNode; @@ -11,19 +13,34 @@ type PlayerAppShellProps = { /** * 玩家端外壳:顶栏(品牌 + 会话)+ 主体 + **底部 Tab 导航**(H5)。 - * 底部栏留白:{@link PlayerBottomNav} 对应 `padding-bottom`. + * 底部栏留白:{@link PlayerBottomNav} 作为 sticky 元素放在底部,main 只需要基础 padding。 * * 注意:全局离线横幅和服务器错误覆盖层已在 ErrorProvider 中处理 * 这里的 NetworkStatusBanner 仅用于 WebSocket 状态显示 */ export function PlayerAppShell({ children }: PlayerAppShellProps): ReactNode { + const { pullDistance, isRefreshing } = usePullToRefresh({ + onRefresh: () => { + window.dispatchEvent(new CustomEvent("lottery-hall-refresh")); + window.dispatchEvent(new CustomEvent("lottery-wallet-refresh")); + }, + threshold: 70, + }); + return ( -
+
+ -
+
{children}
- +
+ +
); } diff --git a/src/components/layout/player-bottom-nav.tsx b/src/components/layout/player-bottom-nav.tsx index 259eabc..d0fa448 100644 --- a/src/components/layout/player-bottom-nav.tsx +++ b/src/components/layout/player-bottom-nav.tsx @@ -6,7 +6,6 @@ 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"; @@ -55,8 +54,7 @@ export function PlayerBottomNav() { return (