From 976561d385961653deacfbdbbcfdd6515d8614ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=BA=94=E9=A6=99=E4=B8=B8=E5=AD=90?= <15518898337@163.com> Date: Sun, 21 Jun 2026 19:39:52 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E7=95=8C=E9=9D=A2=E6=97=A0?= =?UTF-8?q?=E6=B3=95=E8=B7=B3=E8=BD=AC=E7=9A=84=E9=97=AE=E9=A2=98=E4=BF=AE?= =?UTF-8?q?=E5=A4=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 让 /project 复用创作主页桌面导航壳 为项目页补充保留左侧导航并切回推荐页的回归测试 同步项目入口文档约束 --- ...作】创作主页与项目入口改版计划-2026-06-18.md | 1 + .../PlatformEntryFlowShellImpl.tsx | 51 +++++++++++-------- ...gEntryFlowShell.agent.interaction.test.tsx | 38 ++++++++++++++ src/components/rpg-entry/RpgEntryHomeView.tsx | 9 ++-- 4 files changed, 73 insertions(+), 26 deletions(-) diff --git a/docs/【玩法创作】创作主页与项目入口改版计划-2026-06-18.md b/docs/【玩法创作】创作主页与项目入口改版计划-2026-06-18.md index 91c0ae281..805937126 100644 --- a/docs/【玩法创作】创作主页与项目入口改版计划-2026-06-18.md +++ b/docs/【玩法创作】创作主页与项目入口改版计划-2026-06-18.md @@ -115,6 +115,7 @@ Tab: - `/creation/` 现有玩法工作台路由保持原有 stage 映射。 - 桌面端点击“创作”时调用 `pushAppHistoryPath('/creation')`。 - 桌面端原“草稿”入口改为“项目”,点击 `pushAppHistoryPath('/project')`。 +- 桌面端 `/project` 必须复用 `/creation` 的平台桌面 chrome,保留左侧完整导航栏和右侧顶部栏;`ProjectGalleryView` 只替换右侧主内容区,不能作为脱离导航的独立全屏页渲染,避免用户进入项目页后无法返回或切换页签。 - 移动端导航列表不展示“创作”和“项目”。 - 移动端不拦截用户直接访问 `/creation` 或 `/project`。 diff --git a/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx b/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx index 965ae2e13..9997573c7 100644 --- a/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx +++ b/src/components/platform-entry/PlatformEntryFlowShellImpl.tsx @@ -15251,10 +15251,18 @@ export function PlatformEntryFlowShellImpl({ /> ); + const projectGalleryContent = ( + }> + + + ); const handlePlatformHomeTabChange = useCallback( (tab: PlatformHomeTab) => { platformBootstrap.setPlatformTab(tab); - if (selectionStage === 'creation-home' && tab !== 'create') { + if ( + (selectionStage === 'creation-home' && tab !== 'create') || + selectionStage === 'project' + ) { setSelectionStage('platform'); pushAppHistoryPath('/'); } @@ -15264,11 +15272,14 @@ export function PlatformEntryFlowShellImpl({ const platformHomeView = ( )} - {selectionStage === 'project' && ( - - }> - - - - )} {selectionStage === 'creation-home' && ( )} + {selectionStage === 'project' && ( + + {platformHomeView} + + )} {selectionStage === 'platform' && ( ({ saveBabyObjectMatchDraft: vi.fn(), })); +vi.mock('../../services/image-editor/editorProjectClient', () => ({ + createEditorProject: vi.fn(), + deleteEditorProject: vi.fn(), + listEditorProjects: vi.fn(), + loadEditorAssetLibrary: vi.fn(async () => ({ assets: [] })), + renameEditorProject: vi.fn(), +})); + vi.mock('../../services/wooden-fish/woodenFishClient', () => ({ woodenFishClient: { checkpointRun: vi.fn(), @@ -2717,6 +2729,11 @@ beforeEach(() => { }); vi.mocked(listRpgEntryWorldLibrary).mockResolvedValue([]); vi.mocked(listRpgEntryWorldGallery).mockResolvedValue([]); + vi.mocked(listEditorProjects).mockResolvedValue([]); + vi.mocked(loadEditorAssetLibrary).mockResolvedValue({ + assets: [], + folders: [], + }); vi.mocked(getRpgEntryWorldGalleryDetail).mockImplementation( async (ownerUserId, profileId) => buildMockRpgGalleryDetail({ @@ -9169,6 +9186,27 @@ test('creation home desktop rail can return to platform tabs', async () => { expect(window.location.pathname).toBe('/'); }); +test('project page keeps desktop rail and can return to platform tabs', async () => { + const user = userEvent.setup(); + mockDesktopPlatformLayout(); + window.history.replaceState(null, '', '/project'); + const { container } = render(); + + expect(await screen.findByRole('main', { name: '项目' })).toBeTruthy(); + const nav = container.querySelector('.platform-desktop-rail'); + expect(nav).toBeTruthy(); + expect( + within(nav as HTMLElement).getByRole('button', { name: '项目' }), + ).toBeTruthy(); + + await user.click(within(nav as HTMLElement).getByRole('button', { name: '推荐' })); + + await waitFor(() => { + expect(screen.queryByRole('main', { name: '项目' })).toBeNull(); + }); + expect(window.location.pathname).toBe('/'); +}); + test('direct mobile creation home keeps landing content without mobile nav entry', async () => { window.history.replaceState(null, '', '/creation'); const { container } = render(); diff --git a/src/components/rpg-entry/RpgEntryHomeView.tsx b/src/components/rpg-entry/RpgEntryHomeView.tsx index 92b5c184e..7eb20db9f 100644 --- a/src/components/rpg-entry/RpgEntryHomeView.tsx +++ b/src/components/rpg-entry/RpgEntryHomeView.tsx @@ -228,6 +228,7 @@ export type PlatformHomeTab = type PlatformNavTab = PlatformHomeTab | 'projects'; export interface RpgEntryHomeViewProps { activeTab: PlatformHomeTab; + activeDesktopNavTab?: PlatformNavTab; isDesktopLayout?: boolean; onTabChange: (tab: PlatformHomeTab) => void; hasSavedGame: boolean; @@ -1939,10 +1940,6 @@ function DesktopTabButton({ ); } -function isPlatformHomeTab(tab: PlatformNavTab): tab is PlatformHomeTab { - return tab !== 'projects'; -} - function PlatformTabPanel({ tab, activeTab, @@ -2584,6 +2581,7 @@ function RechargePaymentConfirmationMask({ orderId }: { orderId: string }) { export function RpgEntryHomeView({ activeTab, + activeDesktopNavTab, isDesktopLayout: isDesktopLayoutProp, onTabChange, saveEntries, @@ -2973,6 +2971,7 @@ export function RpgEntryHomeView({ saves: '草稿', profile: '我的', } as const satisfies Record; + const resolvedDesktopNavTab = activeDesktopNavTab ?? activeTab; useEffect(() => { if (allowHiddenActiveTab) { @@ -5034,7 +5033,7 @@ export function RpgEntryHomeView({ {visibleDesktopNavTabs.map((tab) => (