refactor: 更新管理端页面元数据,统一国际化支持,移除冗余代码

This commit is contained in:
2026-05-21 17:27:52 +08:00
parent 26feed3c4f
commit e8a5507411
77 changed files with 1669 additions and 732 deletions

105
src/lib/admin-page-title.ts Normal file
View File

@@ -0,0 +1,105 @@
export type PageTitleSpec = {
ns: string;
key: string;
params?: Record<string, string>;
};
const EXACT_ROUTES: Record<string, PageTitleSpec> = {
"/admin": { ns: "dashboard", key: "title" },
"/admin/players": { ns: "players", key: "title" },
"/admin/draws": { ns: "draws", key: "statusListTitle" },
"/admin/tickets": { ns: "tickets", key: "title" },
"/admin/settlement-batches": { ns: "settlement", key: "batchList" },
"/admin/reconcile": { ns: "reconcile", key: "title" },
"/admin/audit-logs": { ns: "audit", key: "title" },
"/admin/admin-users": { ns: "adminUsers", key: "title" },
"/admin/admin-roles": { ns: "adminRoles", key: "title" },
"/admin/wallet": { ns: "wallet", key: "title" },
"/admin/wallet/transactions": { ns: "wallet", key: "walletTransactions" },
"/admin/wallet/transfer-orders": { ns: "wallet", key: "transferOrders" },
"/admin/wallet/player": { ns: "wallet", key: "playerWalletQuery" },
"/admin/risk": { ns: "risk", key: "center" },
"/admin/settings": { ns: "common", key: "nav.settings" },
"/admin/settings/currencies": { ns: "config", key: "currencies.title" },
"/admin/currencies": { ns: "config", key: "currencies.title" },
"/admin/config": { ns: "config", key: "hub.title" },
"/admin/rules/plays": { ns: "config", key: "nav.rulesPlaysTitle" },
"/admin/rules/odds": { ns: "config", key: "nav.rulesOddsTitle" },
"/admin/jackpot": { ns: "jackpot", key: "configTitle" },
"/admin/risk/cap": { ns: "config", key: "nav.riskCapTitle" },
"/admin/login": { ns: "auth", key: "title" },
};
type RoutePattern = {
test: (pathname: string) => boolean;
resolve: (pathname: string) => PageTitleSpec;
};
const ROUTE_PATTERNS: RoutePattern[] = [
{
test: (p) => /^\/admin\/draws\/\d+\/finance$/.test(p),
resolve: () => ({ ns: "draws", key: "subnav.finance" }),
},
{
test: (p) => /^\/admin\/draws\/\d+\/results$/.test(p),
resolve: () => ({ ns: "draws", key: "subnav.results" }),
},
{
test: (p) => /^\/admin\/draws\/\d+\/review$/.test(p),
resolve: () => ({ ns: "draws", key: "subnav.review" }),
},
{
test: (p) => /^\/admin\/draws\/\d+\/publish\/\d+$/.test(p),
resolve: () => ({ ns: "draws", key: "publishTitle" }),
},
{
test: (p) => /^\/admin\/draws\/\d+\/risk\/occupancy$/.test(p) || /^\/admin\/risk\/draws\/\d+\/occupancy$/.test(p),
resolve: () => ({ ns: "draws", key: "subnav.riskOccupancy" }),
},
{
test: (p) => /^\/admin\/draws\/\d+\/risk\/hot$/.test(p) || /^\/admin\/risk\/draws\/\d+\/hot$/.test(p),
resolve: () => ({ ns: "draws", key: "subnav.riskHot" }),
},
{
test: (p) => /^\/admin\/draws\/\d+\/risk\/sold-out$/.test(p) || /^\/admin\/risk\/draws\/\d+\/sold-out$/.test(p),
resolve: () => ({ ns: "draws", key: "subnav.riskSoldOut" }),
},
{
test: (p) => /^\/admin\/draws\/\d+\/risk\/pools$/.test(p) || /^\/admin\/risk\/draws\/\d+\/pools$/.test(p),
resolve: () => ({ ns: "draws", key: "subnav.riskPools" }),
},
{
test: (p) => /^\/admin\/draws\/\d+\/risk\/pools\/[^/]+$/.test(p) || /^\/admin\/risk\/draws\/\d+\/pools\/[^/]+$/.test(p),
resolve: (p) => {
const number = decodeURIComponent(p.split("/").pop() ?? "");
return { ns: "risk", key: "numberTitle", params: { number } };
},
},
{
test: (p) => /^\/admin\/draws\/\d+\/risk$/.test(p),
resolve: () => ({ ns: "risk", key: "title" }),
},
{
test: (p) => /^\/admin\/draws\/\d+$/.test(p),
resolve: () => ({ ns: "draws", key: "drawDetail" }),
},
{
test: (p) => /^\/admin\/settlement-batches\/\d+\/details$/.test(p),
resolve: () => ({ ns: "settlement", key: "details" }),
},
];
/** 根据路径解析浏览器标题用的 i18n 规格。 */
export function resolveAdminPageTitle(pathname: string): PageTitleSpec | null {
if (EXACT_ROUTES[pathname]) {
return EXACT_ROUTES[pathname];
}
for (const pattern of ROUTE_PATTERNS) {
if (pattern.test(pathname)) {
return pattern.resolve(pathname);
}
}
return null;
}

57
src/lib/page-metadata.ts Normal file
View File

@@ -0,0 +1,57 @@
import type { Metadata } from "next";
import enAdminRoles from "@/i18n/locales/en/adminRoles.json";
import enAdminUsers from "@/i18n/locales/en/adminUsers.json";
import enAudit from "@/i18n/locales/en/audit.json";
import enAuth from "@/i18n/locales/en/auth.json";
import enConfig from "@/i18n/locales/en/config.json";
import enDashboard from "@/i18n/locales/en/dashboard.json";
import enDraws from "@/i18n/locales/en/draws.json";
import enJackpot from "@/i18n/locales/en/jackpot.json";
import enPlayers from "@/i18n/locales/en/players.json";
import enReconcile from "@/i18n/locales/en/reconcile.json";
import enRisk from "@/i18n/locales/en/risk.json";
import enSettlement from "@/i18n/locales/en/settlement.json";
import enCommon from "@/i18n/locales/en/common.json";
import enTickets from "@/i18n/locales/en/tickets.json";
import enWallet from "@/i18n/locales/en/wallet.json";
const EN_FLAT: Record<string, Record<string, unknown>> = {
dashboard: enDashboard,
players: enPlayers,
draws: enDraws,
tickets: enTickets,
settlement: enSettlement,
reconcile: enReconcile,
audit: enAudit,
adminUsers: enAdminUsers,
adminRoles: enAdminRoles,
wallet: enWallet,
risk: enRisk,
jackpot: enJackpot,
config: enConfig,
common: enCommon,
auth: enAuth,
};
function getByPath(obj: Record<string, unknown>, path: string): string | undefined {
const parts = path.split(".");
let cur: unknown = obj;
for (const part of parts) {
if (cur == null || typeof cur !== "object") {
return undefined;
}
cur = (cur as Record<string, unknown>)[part];
}
return typeof cur === "string" ? cur : undefined;
}
/** SSR 默认英文标题;客户端由 {@link AdminDocumentTitle} 按用户语言覆盖。 */
export function buildPageMetadata(ns: string, key: string): Metadata {
const bundle = EN_FLAT[ns];
const title = bundle ? getByPath(bundle as Record<string, unknown>, key) : undefined;
return {
title: title ?? key,
};
}