diff --git a/platform_web/pages/ServerDetailPage.test.tsx b/platform_web/pages/ServerDetailPage.test.tsx index 64af2be..d1409f1 100644 --- a/platform_web/pages/ServerDetailPage.test.tsx +++ b/platform_web/pages/ServerDetailPage.test.tsx @@ -173,6 +173,8 @@ describe("ServerDetailPage config write approval", () => { it("routes plugin-declared pages into server detail tabs", () => { expect(serverDetailPageSource).toContain("serverDetailSectionEntries(readyPlugin)"); expect(serverDetailPageSource).toContain("plugin:${page.key}"); + expect(serverDetailPageSource).toContain("ServerDetailSectionNav"); + expect(serverDetailPageSource).toContain('aria-label="游戏运营功能"'); expect(serverDetailPageSource).toContain('section === "files"'); expect(serverDetailPageSource).toContain("PluginPageSection"); expect(serverDetailPageSource).toContain("PluginPageHostPage"); diff --git a/platform_web/pages/ServerDetailPage.tsx b/platform_web/pages/ServerDetailPage.tsx index 3019c53..7dd0958 100644 --- a/platform_web/pages/ServerDetailPage.tsx +++ b/platform_web/pages/ServerDetailPage.tsx @@ -166,19 +166,7 @@ export function ServerDetailPage(props: PageComponentProps) { )} {instance.status !== "ready" && ( - + )} {instance.status === "ready" && ( @@ -230,19 +218,7 @@ export function ServerDetailPage(props: PageComponentProps) { - + {pluginPageKeyFromSection(section) && readyPlugin && } {section === "manage" && } @@ -289,6 +265,30 @@ function serverDetailSectionEntries(plugin?: GamePluginResponse): Array<{ id: Se return [...pluginPages, ...serverDetailSections]; } +function ServerDetailSectionNav({ entries, section, onChange }: { entries: Array<{ id: ServerDetailSection; label: string }>; section: ServerDetailSection; onChange: (section: ServerDetailSection) => void }) { + const pluginEntries = entries.filter((entry) => entry.id.startsWith("plugin:")); + const platformEntries = entries.filter((entry) => !entry.id.startsWith("plugin:")); + const activePluginSection = section.startsWith("plugin:") ? section : ""; + return ( + + ); +} + function pluginPageKeyFromSection(section: ServerDetailSection): string | null { return section.startsWith("plugin:") ? section.slice("plugin:".length) : null; } diff --git a/platform_web/theme/base.css b/platform_web/theme/base.css index 7e00234..9ef45b4 100644 --- a/platform_web/theme/base.css +++ b/platform_web/theme/base.css @@ -493,6 +493,7 @@ to{transform:translate(-50%,-50%) rotate(calc(var(--construct-drift) + 360deg))} .server-detail-title-row>div:first-child{min-width:0} .server-detail-title-row h1{margin:0;font-size:24px;color:var(--ink);overflow-wrap:anywhere} .section-tabs{display:flex;gap:6px;flex-wrap:wrap;padding:8px;border:1px solid var(--line);border-radius:8px;background:var(--frosted-surface),color-mix(in srgb,var(--surface) 72%,transparent);box-shadow:inset 0 1px 0 var(--crystal-rim)} +.server-plugin-section-picker{display:flex;align-items:center;gap:8px;min-height:38px;padding:0 10px;border:1px solid var(--line-strong);border-radius:8px;background:var(--control-surface);color:var(--ink-soft);font-size:12px;font-weight:700;white-space:nowrap}.server-plugin-section-picker select{min-width:132px;height:30px;border:0;border-left:1px solid var(--line);border-radius:0;padding:0 24px 0 9px;background:transparent;color:var(--ink);font:inherit;outline:0}.server-plugin-section-picker:focus-within{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent-soft)} .section-tab{display:inline-flex;align-items:center;gap:6px;min-height:38px;padding:0 14px;border:1px solid var(--line-strong);border-radius:999px;background:var(--control-surface);color:var(--ink-soft);cursor:pointer;white-space:nowrap} .section-tab:focus-visible,.section-tab:hover{border-color:var(--accent);outline:0} .section-tab-active{background:var(--primary-command-surface);border-color:var(--accent-deep);color:#fff;font-weight:700;box-shadow:0 10px 24px var(--candy-glow),inset 0 1px 0 var(--crystal-rim),0 0 18px var(--moonbeam)}