refactor: 重构奖池配置页面,移除冗余组件,优化加载体验与国际化支持

This commit is contained in:
2026-05-21 16:46:48 +08:00
parent 3ce84af39c
commit 26feed3c4f
29 changed files with 393 additions and 213 deletions

View File

@@ -0,0 +1,43 @@
"use client";
import { useEffect } from "react";
import { useTranslation } from "react-i18next";
import { ConfigDocPage } from "@/modules/config/config-doc-page";
import { ConfigSection } from "@/modules/config/config-section";
import { JackpotPoolsConsole } from "@/modules/jackpot/jackpot-pools-console";
import { JackpotRecordsConsole } from "@/modules/jackpot/jackpot-records-console";
/**
* 奖池:仅保留「侧栏 + 运营配置顶栏」两层导航;池参数与流水在同一页用分区展示。
*/
export function JackpotConfigScreen() {
const { t } = useTranslation("jackpot");
useEffect(() => {
const scrollToRecords = () => {
if (window.location.hash !== "#records") {
return;
}
document.getElementById("jackpot-records")?.scrollIntoView({ behavior: "smooth", block: "start" });
};
scrollToRecords();
window.addEventListener("hashchange", scrollToRecords);
return () => window.removeEventListener("hashchange", scrollToRecords);
}, []);
return (
<ConfigDocPage title={t("configTitle")} description={t("pageDescription")}>
<ConfigSection title={t("poolsSectionTitle")} description={t("poolsSectionDescription")}>
<JackpotPoolsConsole embedded />
</ConfigSection>
<ConfigSection
id="jackpot-records"
title={t("recordsSectionTitle")}
description={t("recordsSectionDescription")}
>
<JackpotRecordsConsole embedded />
</ConfigSection>
</ConfigDocPage>
);
}

View File

@@ -53,7 +53,12 @@ function toDraft(p: AdminJackpotPoolRow): Draft {
};
}
export function JackpotPoolsConsole() {
type JackpotPoolsConsoleProps = {
/** 嵌入运营配置单页时去掉外层脚手架与重复标题 */
embedded?: boolean;
};
export function JackpotPoolsConsole({ embedded = false }: JackpotPoolsConsoleProps) {
const { t } = useTranslation(["jackpot", "common"]);
const [items, setItems] = useState<AdminJackpotPoolRow[]>([]);
const [drafts, setDrafts] = useState<Record<number, Draft>>({});
@@ -146,13 +151,14 @@ export function JackpotPoolsConsole() {
}
};
return (
<ModuleScaffold>
<Card>
const body = (
<Card className={embedded ? "border-border/60 shadow-none" : undefined}>
{!embedded ? (
<CardHeader>
<CardTitle className="text-base">{t("configTitle")}</CardTitle>
</CardHeader>
<CardContent className="space-y-8">
) : null}
<CardContent className="space-y-8">
{loading ? <p className="text-muted-foreground text-sm">{t("states.loading", { ns: "common" })}</p> : null}
{!loading && items.length === 0 ? (
<p className="text-muted-foreground text-sm">{t("noPoolData")}</p>
@@ -288,8 +294,13 @@ export function JackpotPoolsConsole() {
</div>
);
})}
</CardContent>
</Card>
</ModuleScaffold>
</CardContent>
</Card>
);
if (embedded) {
return body;
}
return <ModuleScaffold>{body}</ModuleScaffold>;
}

View File

@@ -27,7 +27,11 @@ import type {
AdminJackpotPayoutLogsData,
} from "@/types/api/admin-jackpot";
export function JackpotRecordsConsole() {
type JackpotRecordsConsoleProps = {
embedded?: boolean;
};
export function JackpotRecordsConsole({ embedded = false }: JackpotRecordsConsoleProps) {
const { t } = useTranslation(["jackpot", "common"]);
const formatDt = useAdminDateTimeFormatter();
const [drawNo, setDrawNo] = useState("");
@@ -101,13 +105,8 @@ export function JackpotRecordsConsole() {
return translated === key ? value : translated;
};
return (
<ModuleScaffold>
<div className="mb-6">
<h1 className="text-lg font-semibold tracking-tight">{t("recordsPage.title")}</h1>
<p className="mt-1 text-sm text-muted-foreground">{t("recordsPage.description")}</p>
</div>
const content = (
<>
<Card className="mb-6">
<CardHeader className="pb-3">
<CardTitle className="text-base">{t("filter")}</CardTitle>
@@ -260,6 +259,12 @@ export function JackpotRecordsConsole() {
) : null}
</CardContent>
</Card>
</ModuleScaffold>
</>
);
if (embedded) {
return content;
}
return <ModuleScaffold>{content}</ModuleScaffold>;
}

View File

@@ -1,39 +0,0 @@
"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { useTranslation } from "react-i18next";
import { cn } from "@/lib/utils";
const LINKS: { href: string; label: string }[] = [
{ href: "/admin/config/jackpot", label: "subnavPools" },
{ href: "/admin/config/jackpot/records", label: "subnavRecords" },
];
export function JackpotSubNav() {
const { t } = useTranslation("jackpot");
const pathname = usePathname();
return (
<nav className="mb-6 flex flex-wrap gap-2 border-b border-border pb-3" aria-label={t("subnavLabel")}>
{LINKS.map(({ href, label }) => {
const active = pathname === href || pathname.startsWith(`${href}/`);
return (
<Link
key={href}
href={href}
className={cn(
"rounded-md px-3 py-1.5 text-sm transition-colors",
active
? "bg-primary text-primary-foreground"
: "bg-muted/60 text-muted-foreground hover:bg-muted hover:text-foreground",
)}
>
{t(label)}
</Link>
);
})}
</nav>
);
}

View File

@@ -1,4 +1,4 @@
export const jackpotModuleMeta = {
title: "奖池记录",
title: "奖池配置",
description: "",
} as const;