feat(admin): 仪表盘重构、代理线路修复与后台体验优化
Some checks failed
lotteryadmin CI / build (push) Has been cancelled

- 各角色仪表盘:KPI 可点击跳转、快捷入口、去重冗余区块,代理/站点降级 analytics
- 代理线路:创建玩家权限、侧栏搜索、深链 URL、档案保存与删除预检等修复
- 统一密码最短 6 位;代理模块表格/侧栏背景色一致(admin-table-inset)
- 报表、钱包、对账、开奖、结算、配置等模块结构与 i18n 同步更新
This commit is contained in:
2026-06-26 14:39:22 +08:00
parent 7d075ceb62
commit 0d984b00f0
126 changed files with 7423 additions and 7084 deletions

View File

@@ -1,24 +1,5 @@
import type { Metadata } from "next";
import { redirect } from "next/navigation";
import { ModuleScaffold } from "@/components/admin/module-scaffold";
import { AdminPermissionGate } from "@/components/admin/admin-permission-gate";
import { AgentsDirectoryConsole } from "@/modules/agents/agents-directory-console";
import {
PRD_AGENT_LINE_PROVISION_ACCESS_ANY,
PRD_AGENTS_ACCESS_ANY,
} from "@/lib/admin-prd";
import { buildPageMetadata } from "@/lib/page-metadata";
export const metadata: Metadata = buildPageMetadata("agents", "listTitle");
export default function AgentsListPage() {
return (
<ModuleScaffold embedded>
<AdminPermissionGate
requiredAny={[...PRD_AGENTS_ACCESS_ANY, ...PRD_AGENT_LINE_PROVISION_ACCESS_ANY]}
>
<AgentsDirectoryConsole />
</AdminPermissionGate>
</ModuleScaffold>
);
}
export default function AgentsListRedirectPage() {
redirect("/admin/agents?view=list");
}

View File

@@ -1,6 +1,6 @@
import { ModuleScaffold } from "@/components/admin/module-scaffold";
import { AdminPermissionGate } from "@/components/admin/admin-permission-gate";
import { AgentsConsole } from "@/modules/agents/agents-console";
import { AgentsManagementScreen } from "@/modules/agents/agents-management-screen";
import {
PRD_AGENT_LINE_PROVISION_ACCESS_ANY,
PRD_AGENTS_ACCESS_ANY,
@@ -16,8 +16,8 @@ export default function AgentsPage() {
<AdminPermissionGate
requiredAny={[...PRD_AGENTS_ACCESS_ANY, ...PRD_AGENT_LINE_PROVISION_ACCESS_ANY]}
>
<AgentsConsole />
<AgentsManagementScreen />
</AdminPermissionGate>
</ModuleScaffold>
);
}
}

View File

@@ -15,4 +15,4 @@ export default function AdminAuditLogsPage() {
</AdminPermissionGate>
</ModuleScaffold>
);
}
}

View File

@@ -1,5 +1,5 @@
import { ModuleScaffold } from "@/components/admin/module-scaffold";
import { DrawSubnav } from "@/modules/draws/draw-subnav";
import { DrawDetailShell } from "@/modules/draws/draw-detail-shell";
export default async function AdminDrawSegmentLayout(props: {
children: React.ReactNode;
@@ -9,8 +9,7 @@ export default async function AdminDrawSegmentLayout(props: {
return (
<ModuleScaffold>
<DrawSubnav drawId={drawId} />
{props.children}
<DrawDetailShell drawId={drawId}>{props.children}</DrawDetailShell>
</ModuleScaffold>
);
}

View File

@@ -1,17 +1,33 @@
import { notFound, redirect } from "next/navigation";
import { buildPageMetadata } from "@/lib/page-metadata";
import { notFound } from "next/navigation";
import { Suspense } from "react";
import type { Metadata } from "next";
import { AdminPermissionGate } from "@/components/admin/admin-permission-gate";
import { AdminLoadingState } from "@/components/admin/admin-loading-state";
import { PRD_REPORTS_PAGE_ACCESS_ANY } from "@/lib/admin-prd";
import { buildPageMetadata } from "@/lib/page-metadata";
import { ReportsConsole } from "@/modules/reports/reports-console";
import type { ReportCategory } from "@/modules/reports/reports-definitions";
export const metadata: Metadata = buildPageMetadata("reports", "title");
const VALID_CATEGORIES = new Set<ReportCategory>(["profit", "wallet", "risk", "audit"]);
export default async function AdminReportsCategoryPage({
params,
}: {
params: Promise<{ category: string }>;
}) {
const { category } = await params;
if (!["profit", "wallet", "risk", "audit"].includes(category)) {
if (!VALID_CATEGORIES.has(category as ReportCategory)) {
notFound();
}
redirect("/admin/reports");
}
return (
<AdminPermissionGate requiredAny={PRD_REPORTS_PAGE_ACCESS_ANY}>
<Suspense fallback={<AdminLoadingState minHeight="12rem" />}>
<ReportsConsole initialCategory={category as ReportCategory} />
</Suspense>
</AdminPermissionGate>
);
}

View File

@@ -1,15 +1,5 @@
import { AdminPermissionGate } from "@/components/admin/admin-permission-gate";
import { AdminLoadingState } from "@/components/admin/admin-loading-state";
import { PRD_REPORTS_VIEW_ACCESS_ANY } from "@/lib/admin-prd";
import { ReportsConsole } from "@/modules/reports/reports-console";
import { Suspense } from "react";
import { redirect } from "next/navigation";
export default function AdminReportsPage() {
return (
<AdminPermissionGate requiredAny={PRD_REPORTS_VIEW_ACCESS_ANY}>
<Suspense fallback={<AdminLoadingState minHeight="12rem" />}>
<ReportsConsole />
</Suspense>
</AdminPermissionGate>
);
}
redirect("/admin/reports/profit");
}

View File

@@ -1,14 +1,12 @@
import type { ReactNode } from "react";
import { WalletScopeHint } from "@/modules/wallet/wallet-scope-hint";
import { WalletSubnav } from "@/modules/wallet/wallet-subnav";
export default function AdminWalletLayout({ children }: { children: ReactNode }) {
return (
<div className="mx-auto flex w-full max-w-[1680px] min-w-0 flex-col gap-6 px-4 py-5 sm:px-6 lg:px-8 lg:py-6">
<div className="sticky top-14 z-20 space-y-2 bg-background/95 pb-2 backdrop-blur supports-[backdrop-filter]:bg-background/80">
<div className="sticky top-14 z-20 bg-background/95 pb-2 backdrop-blur supports-[backdrop-filter]:bg-background/80">
<WalletSubnav />
<WalletScopeHint />
</div>
{children}
</div>

View File

@@ -168,6 +168,24 @@
@apply overflow-x-auto rounded-2xl border border-border/80 bg-card shadow-sm;
}
/* Flat table inside an existing card — same white surface, no nested panel or blue header tint */
.admin-table-inset {
@apply overflow-x-auto rounded-xl border border-border/80;
}
.admin-table-inset [data-slot="table-header"] {
@apply bg-card;
}
.admin-table-inset [data-slot="table-head"][class*="sticky"],
.admin-table-inset [data-slot="table-cell"][class*="sticky"] {
background-color: var(--card);
}
.admin-table-inset [data-slot="table-row"]:hover > [data-slot="table-cell"][class*="sticky"] {
@apply bg-muted/20;
}
/* Sticky columns need an opaque background so scrolled cells/headers do not show through */
[data-slot="table-head"][class*="sticky"] {
@apply bg-muted;