From 82394670ede7dc710bcfee030c4b84d01b0efac6 Mon Sep 17 00:00:00 2001 From: kris Date: Sat, 4 Apr 2026 11:45:58 +0800 Subject: [PATCH] feat: refocus project switching to dashboard workspace --- CHANGELOG.md | 6 ++++++ web/storyforge-web-v4/assets/app.js | 13 +++++++++++-- .../tests/workbench-pages.test.mjs | 3 +++ 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e5326eb..db985e7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -323,3 +323,9 @@ - `跟踪摘要 -> 标记已读` 完成后,会自动回到 `跟踪账号` 工作区,方便继续处理当天的下一条跟踪任务。 - `切换当前 Agent` 后,会自动回到 `Agent -> 当前 Agent 工作台`,并聚焦到当前选中的 Agent,而不是只在原地刷新一句提示。 - 前端回归新增了这两条断言,锁住“切换完成后继续工作”的落点体验。 + +### 项目切换回到总台工作区 + +- 切换当前项目后,现在会自动回到 `项目总台` 的首页工作区,并聚焦到 dashboard 主内容,而不是只留在原地刷新。 +- 项目切换的移动端 sheet 和桌面项目切换入口都共用这条回跳逻辑,方便切完项目后立刻继续推进当前项目。 +- 前端回归新增了 dashboard 工作区锚点和项目切换 refocus 断言,锁住这条落点体验。 diff --git a/web/storyforge-web-v4/assets/app.js b/web/storyforge-web-v4/assets/app.js index e73c410..7537eab 100644 --- a/web/storyforge-web-v4/assets/app.js +++ b/web/storyforge-web-v4/assets/app.js @@ -3302,7 +3302,7 @@ async function applySelectedProject(projectId = "") { setBusy(false, ""); } rememberAction("当前项目已切换", `已切换到「${getSelectedProject()?.name || "所选项目"}」,你现在看到的首页、Agent 和任务都会跟随更新。`, "green"); - renderAll(); + focusDashboardWorkspace("dashboard-workspace-anchor"); } function openDashboardProjectSwitcher() { @@ -5562,7 +5562,7 @@ function renderDashboardScreen() { "先做最能推进当前项目的一步,再按需看概览。", `${button("新建项目", "create-project")} ${button("导入主页", "open-import-homepage")} ${button("创建 Agent", "open-create-assistant", "primary")}`, dashboardHomeRenderer?.renderDashboardHome - ? dashboardHomeRenderer.renderDashboardHome(homeModel, { escapeHtml }) + ? `
${dashboardHomeRenderer.renderDashboardHome(homeModel, { escapeHtml })}
` : renderEmptyState("首页模块未加载", "请刷新页面后重试。") ); } @@ -5749,6 +5749,15 @@ function focusTrackingWorkspace() { }); } +function focusDashboardWorkspace(anchorId = "dashboard-workspace-anchor") { + setScreen("dashboard"); + renderAll(); + window.requestAnimationFrame(() => { + (document.getElementById(anchorId) || document.querySelector('[data-screen="dashboard"] .panel')) + ?.scrollIntoView({ behavior: "smooth", block: "start" }); + }); +} + function focusCreditsWorkspace(anchorId = "credits-quota-anchor") { setScreen("credits"); renderAll(); diff --git a/web/storyforge-web-v4/tests/workbench-pages.test.mjs b/web/storyforge-web-v4/tests/workbench-pages.test.mjs index c7960fe..9e6671a 100644 --- a/web/storyforge-web-v4/tests/workbench-pages.test.mjs +++ b/web/storyforge-web-v4/tests/workbench-pages.test.mjs @@ -137,6 +137,7 @@ test("mobile project sheets support direct project picking and zoom-safe form co const applySelectedProject = extractBetween(APP, "async function applySelectedProject(projectId = \"\")", "function openDashboardProjectSwitcher()"); const createProject = extractBetween(APP, "async function createProject()", "function openPreferredModelAction()"); assert.match(APP, /async function applySelectedProject\(projectId = ""\)/); + assert.match(APP, /function focusDashboardWorkspace\(anchorId = "dashboard-workspace-anchor"\)/); assert.match(projectSwitcher, /data-project-choice=/); assert.match(projectSwitcher, /hidden:\s*isMobileViewport/); assert.match(projectSwitcher, /onOpen:\s*\(/); @@ -148,6 +149,8 @@ test("mobile project sheets support direct project picking and zoom-safe form co assert.match(APP, /if \(field\.hidden\) \{\s*return "";/); assert.match(applySelectedProject, /loadStorageStatus\(appState\.selectedProjectId \|\| ""\)/); assert.match(applySelectedProject, /loadAgentControlSurfaces\(appState\.selectedProjectId \|\| ""\)/); + assert.match(applySelectedProject, /focusDashboardWorkspace\("dashboard-workspace-anchor"\)/); + assert.match(APP, /id="dashboard-workspace-anchor"/); assert.match(createProject, /onOpen:\s*\(/); assert.match(CSS, /@media \(max-width: 760px\)[\s\S]*\.field-stack input,\s*[\s\S]*\.field-stack textarea,\s*[\s\S]*\.field-stack select\s*\{[\s\S]*min-height:\s*46px/); assert.match(CSS, /@media \(max-width: 760px\)[\s\S]*\.field-stack input,\s*[\s\S]*\.field-stack textarea,\s*[\s\S]*\.field-stack select\s*\{[\s\S]*font-size:\s*16px/);