refactor(layout, i18n, admin): 优化布局结构与多语言支持
调整 AdminShell 组件的子组件顺序,提升代码可读性。更新 admin-breadcrumb 组件,简化导航标签翻译逻辑,确保多语言支持的一致性。重构 admin-language-switcher 组件,优化语言切换的用户体验,增强界面交互性。更新多语言配置,新增登录界面的副标题,提升用户体验。
This commit is contained in:
@@ -1,26 +1,21 @@
|
||||
"use client";
|
||||
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { AdminPermissionGate } from "@/components/admin/admin-permission-gate";
|
||||
import { PRD_RULES_ODDS_ACCESS_ANY } from "@/lib/admin-prd";
|
||||
import { ConfigDocPage } from "@/modules/config/config-doc-page";
|
||||
import { ConfigSection } from "@/modules/config/config-section";
|
||||
import { OddsConfigDocScreen } from "@/modules/config/doc/odds-config-doc-screen";
|
||||
import { RebateConfigDocScreen } from "@/modules/config/doc/rebate-config-doc-screen";
|
||||
import { useOddsConfigWorkspace } from "@/modules/config/use-odds-config-workspace";
|
||||
import { RulesPageShell } from "@/modules/rules/rules-page-shell";
|
||||
|
||||
/** 赔率与回水:共用赔率版本线,单页上下分区。 */
|
||||
/** 赔率与回水:共用赔率版本线,主栏三步骤 + 右侧配置摘要。 */
|
||||
export function RulesOddsConfigScreen() {
|
||||
const { t } = useTranslation("config");
|
||||
const [sharedVersionId, setSharedVersionId] = useState("");
|
||||
const workspace = useOddsConfigWorkspace(sharedVersionId, setSharedVersionId);
|
||||
const rebateSectionRef = useRef<HTMLDivElement>(null);
|
||||
const [rebateMounted, setRebateMounted] = useState(
|
||||
() => typeof window !== "undefined" && window.location.hash === "#rebate",
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
const scrollToRebate = () => {
|
||||
@@ -34,44 +29,26 @@ export function RulesOddsConfigScreen() {
|
||||
return () => window.removeEventListener("hashchange", scrollToRebate);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (rebateMounted) {
|
||||
return;
|
||||
}
|
||||
const node = rebateSectionRef.current;
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
const observer = new IntersectionObserver(
|
||||
([entry]) => {
|
||||
if (entry?.isIntersecting) {
|
||||
setRebateMounted(true);
|
||||
}
|
||||
},
|
||||
{ rootMargin: "240px 0px" },
|
||||
);
|
||||
observer.observe(node);
|
||||
return () => observer.disconnect();
|
||||
}, [rebateMounted]);
|
||||
const rebateSection = (
|
||||
<div id="rebate">
|
||||
<RebateConfigDocScreen embedded mergedSection workspace={workspace} />
|
||||
</div>
|
||||
);
|
||||
|
||||
return (
|
||||
<RulesPageShell>
|
||||
<AdminPermissionGate requiredAny={PRD_RULES_ODDS_ACCESS_ANY}>
|
||||
<ConfigDocPage title={t("nav.rulesOddsTitle")} contentClassName="space-y-8">
|
||||
<ConfigSection title={t("nav.items.odds")}>
|
||||
<OddsConfigDocScreen embedded workspace={workspace} />
|
||||
</ConfigSection>
|
||||
<ConfigSection id="rebate" title={t("nav.items.rebate")}>
|
||||
<div ref={rebateSectionRef}>
|
||||
{rebateMounted ? (
|
||||
<RebateConfigDocScreen embedded workspace={workspace} />
|
||||
) : (
|
||||
<p className="text-muted-foreground py-6 text-center text-sm">
|
||||
{t("rebate.lazyLoadHint", { ns: "config" })}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</ConfigSection>
|
||||
<ConfigDocPage
|
||||
title={t("nav.rulesOddsTitle")}
|
||||
description={t("nav.rulesOddsDescription")}
|
||||
contentClassName="pt-2"
|
||||
>
|
||||
<OddsConfigDocScreen
|
||||
embedded
|
||||
mergedLayout
|
||||
workspace={workspace}
|
||||
rebateSection={rebateSection}
|
||||
/>
|
||||
</ConfigDocPage>
|
||||
</AdminPermissionGate>
|
||||
</RulesPageShell>
|
||||
|
||||
Reference in New Issue
Block a user