From e5622c58cb230dec918f7629bc0babf60e2f9174 Mon Sep 17 00:00:00 2001 From: kang Date: Fri, 26 Jun 2026 14:10:47 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E7=B2=BE=E7=AE=80=E7=8E=A9=E5=AE=B6?= =?UTF-8?q?=E7=AB=AF=E9=A1=B5=E9=9D=A2=E7=BB=93=E6=9E=84=E5=B9=B6=E6=94=AF?= =?UTF-8?q?=E6=8C=81=E5=B1=80=E5=9F=9F=E7=BD=91=E5=BC=80=E5=8F=91=E7=83=AD?= =?UTF-8?q?=E6=9B=B4=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 合并钱包、订单、开奖、通知等独立 screen 到页面层,减少重复壳组件 - 优化订单列表/详情、钱包划转与待对账展示、开奖核对与结果详情交互 - 改进入口 gate、移动端视口与下拉刷新在窄屏下的表现 - dev 绑定 0.0.0.0 并默认放行 192.168/10 网段,修复局域网 HMR WebSocket --- next.config.ts | 2 +- package.json | 2 +- src/api/ticket-items.ts | 2 + .../(player)/(main)/notifications/page.tsx | 9 +- .../(main)/orders/group/[groupKey]/page.tsx | 14 +- .../(player)/(main)/results/[drawNo]/page.tsx | 11 +- .../(player)/(main)/results/check/page.tsx | 6 +- src/app/(player)/(main)/wallet/logs/page.tsx | 9 +- src/app/(player)/(main)/wallet/page.tsx | 17 +- .../(main)/wallet/transfer-in/page.tsx | 9 +- .../(main)/wallet/transfer-out/page.tsx | 9 +- src/app/(player)/login/page.tsx | 20 +- src/app/(player)/page.tsx | 20 +- src/app/globals.css | 21 ++ src/app/layout.tsx | 1 + src/app/not-found.tsx | 4 +- src/components/error-provider.tsx | 7 - src/components/layout/player-app-shell.tsx | 7 +- .../layout/player-mobile-viewport.tsx | 10 +- src/components/pull-to-refresh-indicator.tsx | 5 +- src/components/ui/input.tsx | 2 +- src/components/ui/textarea.tsx | 2 +- src/features/hall/hall-bet-preview-dialog.tsx | 9 - src/features/hall/hall-betting-grid.tsx | 16 +- src/features/orders/group-ticket-items.ts | 58 +-- .../orders/ticket-order-detail-screen.tsx | 123 ++++-- .../orders/ticket-order-group-screen.tsx | 182 --------- .../orders/ticket-orders-list-screen.tsx | 259 +++++++------ src/features/player/entry-gate.tsx | 126 +++++-- src/features/player/notifications-screen.tsx | 145 ------- src/features/player/player-login-screen.tsx | 2 +- .../results/check-winning-redirect.tsx | 29 ++ src/features/results/check-winning-screen.tsx | 307 +-------------- .../results/draw-result-detail-screen.tsx | 28 +- .../results/draw-results-list-screen.tsx | 21 +- .../results/draw-winning-check-panel.tsx | 354 ++++++++++++++++++ src/features/wallet/transfer-in-screen.tsx | 57 --- src/features/wallet/transfer-out-screen.tsx | 52 --- src/features/wallet/wallet-logs-block.tsx | 26 +- src/features/wallet/wallet-logs-screen.tsx | 151 -------- .../wallet-pending-reconcile-banner.tsx | 45 --- .../wallet-pending-reconcile-section.tsx | 153 ++++++++ src/features/wallet/wallet-screen.tsx | 79 +++- .../wallet/wallet-transfer-credit-guard.tsx | 19 +- .../wallet/wallet-transfer-dialogs.tsx | 36 +- src/hooks/use-mobile.ts | 20 +- src/i18n/locales/en/player.json | 4 + src/i18n/locales/ne/player.json | 4 + src/i18n/locales/zh/player.json | 4 + src/lib/next-dev-origins.ts | 18 +- src/lib/player-viewport.ts | 3 + 51 files changed, 1184 insertions(+), 1335 deletions(-) delete mode 100644 src/features/orders/ticket-order-group-screen.tsx delete mode 100644 src/features/player/notifications-screen.tsx create mode 100644 src/features/results/check-winning-redirect.tsx create mode 100644 src/features/results/draw-winning-check-panel.tsx delete mode 100644 src/features/wallet/transfer-in-screen.tsx delete mode 100644 src/features/wallet/transfer-out-screen.tsx delete mode 100644 src/features/wallet/wallet-logs-screen.tsx delete mode 100644 src/features/wallet/wallet-pending-reconcile-banner.tsx create mode 100644 src/features/wallet/wallet-pending-reconcile-section.tsx diff --git a/next.config.ts b/next.config.ts index ed65cdd..3724fce 100644 --- a/next.config.ts +++ b/next.config.ts @@ -6,7 +6,7 @@ import { parseAllowedDevOrigins } from "./src/lib/next-dev-origins"; const allowedDevOrigins = parseAllowedDevOrigins(process.env.ALLOWED_DEV_ORIGINS); const nextConfig: NextConfig = { - ...(allowedDevOrigins.length > 0 ? { allowedDevOrigins } : {}), + allowedDevOrigins, reactCompiler: true, images: { diff --git a/package.json b/package.json index 1842470..b750168 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev --port 3800", + "dev": "next dev --port 3800 -H 0.0.0.0", "build": "next build", "start": "next start --port 3800", "lint": "eslint" diff --git a/src/api/ticket-items.ts b/src/api/ticket-items.ts index b3ee177..8f05cdb 100644 --- a/src/api/ticket-items.ts +++ b/src/api/ticket-items.ts @@ -9,6 +9,7 @@ export type GetTicketItemsParams = { page?: number; per_page?: number; draw_no?: string; + order_no?: string; number?: string; status?: string[]; start_date?: string; @@ -26,6 +27,7 @@ export function getTicketItems( page: params?.page, per_page: params?.per_page, draw_no: params?.draw_no, + order_no: params?.order_no, number: params?.number, status: params?.status, start_date: params?.start_date, diff --git a/src/app/(player)/(main)/notifications/page.tsx b/src/app/(player)/(main)/notifications/page.tsx index a068c7a..027dfa9 100644 --- a/src/app/(player)/(main)/notifications/page.tsx +++ b/src/app/(player)/(main)/notifications/page.tsx @@ -1,5 +1,6 @@ -import { NotificationsScreen } from "@/features/player/notifications-screen"; +import { redirect } from "next/navigation"; -export default function NotificationsPage() { - return ; -} +/** 待对账已内嵌在钱包页,旧路由锚点跳转 */ +export default function NotificationsRedirectPage() { + redirect("/wallet?section=pending"); +} \ No newline at end of file diff --git a/src/app/(player)/(main)/orders/group/[groupKey]/page.tsx b/src/app/(player)/(main)/orders/group/[groupKey]/page.tsx index 2b6f02a..841ad19 100644 --- a/src/app/(player)/(main)/orders/group/[groupKey]/page.tsx +++ b/src/app/(player)/(main)/orders/group/[groupKey]/page.tsx @@ -1,10 +1,6 @@ -import { TicketOrderGroupScreen } from "@/features/orders/ticket-order-group-screen"; +import { redirect } from "next/navigation"; -type PageProps = { - params: Promise<{ groupKey: string }>; -}; - -export default async function OrderGroupPage({ params }: PageProps) { - const { groupKey } = await params; - return ; -} +/** 旧「订单详情」中间层已移除,统一回到注单列表 */ +export default function OrderGroupRedirectPage() { + redirect("/orders"); +} \ No newline at end of file diff --git a/src/app/(player)/(main)/results/[drawNo]/page.tsx b/src/app/(player)/(main)/results/[drawNo]/page.tsx index f8c170b..37e7889 100644 --- a/src/app/(player)/(main)/results/[drawNo]/page.tsx +++ b/src/app/(player)/(main)/results/[drawNo]/page.tsx @@ -1,16 +1,25 @@ +import type { ReactNode } from "react"; +import { Suspense } from "react"; + import { DrawResultDetailScreen } from "@/features/results/draw-result-detail-screen"; type PageProps = { params: Promise<{ drawNo: string }>; }; +function DetailFallback(): ReactNode { + return
; +} + export default async function DrawResultByNoPage(props: PageProps) { const { drawNo: raw } = await props.params; const drawNo = decodeURIComponent(raw); return (
- + }> + +
); } diff --git a/src/app/(player)/(main)/results/check/page.tsx b/src/app/(player)/(main)/results/check/page.tsx index 31e0afe..5936d1f 100644 --- a/src/app/(player)/(main)/results/check/page.tsx +++ b/src/app/(player)/(main)/results/check/page.tsx @@ -1,5 +1,5 @@ -import { CheckWinningScreen } from "@/features/results/check-winning-screen"; +import { CheckWinningRedirect } from "@/features/results/check-winning-redirect"; export default function CheckWinningPage() { - return ; -} + return ; +} \ No newline at end of file diff --git a/src/app/(player)/(main)/wallet/logs/page.tsx b/src/app/(player)/(main)/wallet/logs/page.tsx index 27c2cc3..438a0f3 100644 --- a/src/app/(player)/(main)/wallet/logs/page.tsx +++ b/src/app/(player)/(main)/wallet/logs/page.tsx @@ -1,5 +1,6 @@ -import { WalletLogsScreen } from "@/features/wallet/wallet-logs-screen"; +import { redirect } from "next/navigation"; -export default function WalletLogsPage() { - return ; -} +/** 流水已内嵌在钱包页,旧路由锚点跳转 */ +export default function WalletLogsRedirectPage() { + redirect("/wallet?section=logs"); +} \ No newline at end of file diff --git a/src/app/(player)/(main)/wallet/page.tsx b/src/app/(player)/(main)/wallet/page.tsx index 3f123ea..f286728 100644 --- a/src/app/(player)/(main)/wallet/page.tsx +++ b/src/app/(player)/(main)/wallet/page.tsx @@ -1,6 +1,17 @@ +import type { ReactNode } from "react"; +import { Suspense } from "react"; + import { WalletScreen } from "@/features/wallet/wallet-screen"; -/** 界面文档 §4.9 彩票钱包:余额、转入/转出、流水 */ -export default function WalletPage() { - return ; +function WalletFallback(): ReactNode { + return
; } + +/** 界面文档 §4.9 彩票钱包:余额、转入/转出、待对账、流水 */ +export default function WalletPage() { + return ( + }> + + + ); +} \ No newline at end of file diff --git a/src/app/(player)/(main)/wallet/transfer-in/page.tsx b/src/app/(player)/(main)/wallet/transfer-in/page.tsx index e43be8a..e6407ab 100644 --- a/src/app/(player)/(main)/wallet/transfer-in/page.tsx +++ b/src/app/(player)/(main)/wallet/transfer-in/page.tsx @@ -1,5 +1,6 @@ -import { TransferInScreen } from "@/features/wallet/transfer-in-screen"; +import { redirect } from "next/navigation"; -export default function WalletTransferInPage() { - return ; -} +/** 深链统一进钱包弹窗,避免独立页与弹窗两套入口 */ +export default function WalletTransferInRedirectPage() { + redirect("/wallet?action=transfer-in"); +} \ No newline at end of file diff --git a/src/app/(player)/(main)/wallet/transfer-out/page.tsx b/src/app/(player)/(main)/wallet/transfer-out/page.tsx index e634d91..691c6c7 100644 --- a/src/app/(player)/(main)/wallet/transfer-out/page.tsx +++ b/src/app/(player)/(main)/wallet/transfer-out/page.tsx @@ -1,5 +1,6 @@ -import { TransferOutScreen } from "@/features/wallet/transfer-out-screen"; +import { redirect } from "next/navigation"; -export default function WalletTransferOutPage() { - return ; -} +/** 深链统一进钱包弹窗,避免独立页与弹窗两套入口 */ +export default function WalletTransferOutRedirectPage() { + redirect("/wallet?action=transfer-out"); +} \ No newline at end of file diff --git a/src/app/(player)/login/page.tsx b/src/app/(player)/login/page.tsx index 1282559..ca5f21d 100644 --- a/src/app/(player)/login/page.tsx +++ b/src/app/(player)/login/page.tsx @@ -1,16 +1,26 @@ import type { ReactNode } from "react"; import { Suspense } from "react"; +import { Loader2 } from "lucide-react"; import { PlayerLoginScreen } from "@/features/player/player-login-screen"; function LoginFallback(): ReactNode { - return
; + return ( +
+ +
+ ); } export default function PlayerLoginPage() { return ( - }> - - +
+ }> + + +
); -} +} \ No newline at end of file diff --git a/src/app/(player)/page.tsx b/src/app/(player)/page.tsx index b3bb5f8..0bc6d40 100644 --- a/src/app/(player)/page.tsx +++ b/src/app/(player)/page.tsx @@ -1,16 +1,26 @@ import type { ReactNode } from "react"; import { Suspense } from "react"; +import { Loader2 } from "lucide-react"; import { EntryGate } from "@/features/player/entry-gate"; function EntryFallback(): ReactNode { - return
; + return ( +
+ +
+ ); } export default function EntryPage() { return ( - }> - - +
+ }> + + +
); -} +} \ No newline at end of file diff --git a/src/app/globals.css b/src/app/globals.css index 54e02ec..ca7b4c4 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -142,6 +142,27 @@ } } +/* 下注表格横向滚动:右侧渐变提示可继续滑动 */ +.player-table-scroll { + -webkit-overflow-scrolling: touch; + scrollbar-width: thin; +} + +.player-table-scroll-wrap { + position: relative; +} + +.player-table-scroll-wrap::after { + content: ""; + position: absolute; + top: 0; + right: 0; + bottom: 0; + width: 1.25rem; + pointer-events: none; + background: linear-gradient(to left, rgba(255, 255, 255, 0.95), transparent); +} + /* 玩家端 Toast:顶部居中、紧凑尺寸(位置见 components/ui/sonner.tsx) */ [data-sonner-toaster] { --width: min(280px, calc(100vw - 24px)); diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 0cc0d2c..55a3079 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -30,6 +30,7 @@ export const viewport = { initialScale: 1, maximumScale: 1, userScalable: false, + viewportFit: "cover", }; export default function RootLayout({ diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx index 39c2f2e..b98c521 100644 --- a/src/app/not-found.tsx +++ b/src/app/not-found.tsx @@ -5,7 +5,6 @@ import { FileQuestion, Home } from "lucide-react"; import { useTranslation } from "react-i18next"; import { PlayerMobileViewport } from "@/components/layout/player-mobile-viewport"; -import { playerViewportColumnClass } from "@/lib/player-viewport"; import { cn } from "@/lib/utils"; import "@/i18n"; @@ -16,8 +15,7 @@ export default function NotFoundPage(): React.ReactElement {
diff --git a/src/components/error-provider.tsx b/src/components/error-provider.tsx index 16f881e..acdbce8 100644 --- a/src/components/error-provider.tsx +++ b/src/components/error-provider.tsx @@ -3,7 +3,6 @@ import type { ReactNode } from "react"; import { useNetworkStatus } from "@/hooks/use-network-status"; -import { OfflineBanner } from "@/components/offline-banner"; import { ServerError } from "@/components/server-error"; /** @@ -17,13 +16,7 @@ export function ErrorProvider({ children }: { children: ReactNode }): ReactNode return ( <> - {/* 全局离线状态横幅 - 显示在页面顶部 */} - - - {/* 服务器错误全屏覆盖 */} - - {/* 子内容 */} {children} ); diff --git a/src/components/layout/player-app-shell.tsx b/src/components/layout/player-app-shell.tsx index 38c9b60..74c2970 100644 --- a/src/components/layout/player-app-shell.tsx +++ b/src/components/layout/player-app-shell.tsx @@ -28,14 +28,17 @@ export function PlayerAppShell({ children }: PlayerAppShellProps): ReactNode { }); return ( -
+
-
+
{children}
diff --git a/src/components/layout/player-mobile-viewport.tsx b/src/components/layout/player-mobile-viewport.tsx index a09080a..5992d14 100644 --- a/src/components/layout/player-mobile-viewport.tsx +++ b/src/components/layout/player-mobile-viewport.tsx @@ -1,6 +1,10 @@ import type { ReactNode } from "react"; -import { playerViewportColumnClass } from "@/lib/player-viewport"; +import { OfflineBanner } from "@/components/offline-banner"; +import { + playerSafeAreaTopClass, + playerViewportColumnClass, +} from "@/lib/player-viewport"; import { cn } from "@/lib/utils"; type PlayerMobileViewportProps = { @@ -20,10 +24,12 @@ export function PlayerMobileViewport({
+ {children}
diff --git a/src/components/pull-to-refresh-indicator.tsx b/src/components/pull-to-refresh-indicator.tsx index 8223102..a557953 100644 --- a/src/components/pull-to-refresh-indicator.tsx +++ b/src/components/pull-to-refresh-indicator.tsx @@ -26,7 +26,10 @@ export function PullToRefreshIndicator({ return (
) { type={type} data-slot="input" className={cn( - "h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40", + "h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40", className )} {...props} diff --git a/src/components/ui/textarea.tsx b/src/components/ui/textarea.tsx index 04d27f7..aca4a8c 100644 --- a/src/components/ui/textarea.tsx +++ b/src/components/ui/textarea.tsx @@ -7,7 +7,7 @@ function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {