feat: 精简玩家端页面结构并支持局域网开发热更新
Some checks failed
lotteryfront CI / build (push) Has been cancelled

- 合并钱包、订单、开奖、通知等独立 screen 到页面层,减少重复壳组件
- 优化订单列表/详情、钱包划转与待对账展示、开奖核对与结果详情交互
- 改进入口 gate、移动端视口与下拉刷新在窄屏下的表现
- dev 绑定 0.0.0.0 并默认放行 192.168/10 网段,修复局域网 HMR WebSocket
This commit is contained in:
2026-06-26 14:10:47 +08:00
parent dd1155978a
commit e5622c58cb
51 changed files with 1184 additions and 1335 deletions

View File

@@ -2,6 +2,7 @@
import { Wallet } from "lucide-react";
import Image from "next/image";
import { useRouter, useSearchParams } from "next/navigation";
import { useCallback, useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
@@ -13,7 +14,7 @@ import {
TransferInDialog,
TransferOutDialog,
} from "@/features/wallet/wallet-transfer-dialogs";
import { WalletPendingReconcileBanner } from "@/features/wallet/wallet-pending-reconcile-banner";
import { WalletPendingReconcileSection } from "@/features/wallet/wallet-pending-reconcile-section";
import { PlayerMoneyDisplay } from "@/components/player-money-display";
import { WalletLogsBlock } from "@/features/wallet/wallet-logs-block";
import { dispatchWalletLogsRefresh } from "@/hooks/use-pending-wallet-reconcile";
@@ -28,9 +29,18 @@ import { getWalletLogsLastPage, type WalletLogsData } from "@/types/api/wallet-l
const WALLET_LOGS_PAGE_SIZE = 10;
function scrollToWalletSection(id: string): void {
if (typeof document === "undefined") return;
document.getElementById(id)?.scrollIntoView({ behavior: "smooth", block: "start" });
}
export function WalletScreen() {
const router = useRouter();
const searchParams = useSearchParams();
const { activeCurrency: currency } = useActivePlayerCurrency();
const { t } = useTranslation("player");
const [transferInOpen, setTransferInOpen] = useState(false);
const [transferOutOpen, setTransferOutOpen] = useState(false);
const [balance, setBalance] = useState<WalletBalanceData | null>(null);
const [logs, setLogs] = useState<WalletLogsData | null>(null);
const [filter, setFilter] = useState("");
@@ -39,8 +49,47 @@ export function WalletScreen() {
const [loadingMore, setLoadingMore] = useState(false);
const [error, setError] = useState<string | null>(null);
const loadMoreRef = useRef<HTMLDivElement | null>(null);
const filterInitializedRef = useRef(false);
const prevFilterRef = useRef("");
const actionDeepLinkHandledRef = useRef(false);
const sectionDeepLinkHandledRef = useRef(false);
const profile = usePlayerSessionStore((s) => s.profile);
const isCreditPlayer = isCreditFundingPlayer(balance) || isCreditFundingPlayer(profile);
useEffect(() => {
if (actionDeepLinkHandledRef.current || loading) return;
const action = searchParams.get("action");
if (action !== "transfer-in" && action !== "transfer-out") return;
actionDeepLinkHandledRef.current = true;
if (!isCreditPlayer) {
if (action === "transfer-in") {
setTransferInOpen(true);
} else {
setTransferOutOpen(true);
}
}
router.replace("/wallet", { scroll: false });
}, [isCreditPlayer, loading, router, searchParams]);
useEffect(() => {
if (sectionDeepLinkHandledRef.current || loading) return;
const section = searchParams.get("section");
if (section !== "logs" && section !== "pending") return;
sectionDeepLinkHandledRef.current = true;
const targetId =
section === "logs" || (section === "pending" && isCreditPlayer)
? "wallet-logs"
: "wallet-pending";
const timer = window.setTimeout(() => {
scrollToWalletSection(targetId);
router.replace("/wallet", { scroll: false });
}, 120);
return () => window.clearTimeout(timer);
}, [isCreditPlayer, loading, router, searchParams]);
const loadLogs = useCallback(async (targetPage = 1, append = false) => {
const nextLogs = await getWalletLogs({
@@ -164,8 +213,6 @@ export function WalletScreen() {
const hasMore = logs ? logs.page < getWalletLogsLastPage(logs) : false;
const profile = usePlayerSessionStore((s) => s.profile);
const isCreditPlayer = isCreditFundingPlayer(balance) || isCreditFundingPlayer(profile);
const displayMinor = isCreditPlayer
? Number(balance?.available_balance ?? 0)
: Number(balance?.balance ?? 0);
@@ -261,17 +308,8 @@ export function WalletScreen() {
</div>
</section>
{isCreditPlayer ? (
<p className="rounded-xl border border-[#d6e4ff] bg-[#f5f9ff] px-3 py-3 text-sm text-[#0b3f96]/85">
{t("wallet.creditNoTransferHint", {
defaultValue:
"由代理授信,无需主站转入转出;中奖不即时派彩,盈亏在账期统一结算,额度调整请联系代理。",
})}
</p>
) : (
<>
<WalletPendingReconcileBanner />
<div className="grid grid-cols-2 gap-3">
{!isCreditPlayer ? (
<div className="grid grid-cols-2 gap-3">
<TransferInDialog
idPrefix="wallet-"
currency={currency}
@@ -285,6 +323,8 @@ export function WalletScreen() {
triggerVariant="hall"
triggerLabel={t("wallet.transferIn", { defaultValue: "Transfer In" })}
triggerClassName="h-14 rounded-2xl text-base font-black"
open={transferInOpen}
onOpenChange={setTransferInOpen}
/>
<TransferOutDialog
idPrefix="wallet-"
@@ -294,11 +334,17 @@ export function WalletScreen() {
triggerVariant="hall"
triggerLabel={t("wallet.transferOut", { defaultValue: "Transfer Out" })}
triggerClassName="h-14 rounded-2xl text-base font-black"
open={transferOutOpen}
onOpenChange={setTransferOutOpen}
/>
</div>
</>
)}
) : null}
<WalletPendingReconcileSection
onViewLogs={() => scrollToWalletSection("wallet-logs")}
/>
<div id="wallet-logs" className="scroll-mt-3">
<WalletLogsBlock
creditMode={isCreditPlayer}
logs={logs}
@@ -311,6 +357,7 @@ export function WalletScreen() {
onFilterChange={setFilter}
currency={currency}
/>
</div>
</div>
</PlayerPanel>
);