From 6742346eb4bc8d42fbed7c39b9d3d8c3f3ce3511 Mon Sep 17 00:00:00 2001 From: fengxiangdebaozi <10845145+fengxiangdebaozi@user.noreply.gitee.com> Date: Tue, 23 Jun 2026 21:05:58 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=A3=E7=A0=81=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend-web/src/core/embed/embed-session.ts | 32 ++++++++++++++++++-- frontend-web/src/pages/PageRoutes.tsx | 18 +++++++++-- 2 files changed, 45 insertions(+), 5 deletions(-) diff --git a/frontend-web/src/core/embed/embed-session.ts b/frontend-web/src/core/embed/embed-session.ts index 6dcf0de1..8e0f8a32 100644 --- a/frontend-web/src/core/embed/embed-session.ts +++ b/frontend-web/src/core/embed/embed-session.ts @@ -39,6 +39,12 @@ export interface EmbedSession { theme: EmbedTheme; /** 隐藏深链底部「快捷跳转」栏(TaskJumpBar:底部按钮 + 右侧悬浮菜单)。 */ hideJump: boolean; + /** + * 仅去全局 chrome(Header 导航栏 + PageSidebar 侧边栏),**不**强制主题、**不**隔离鉴权。 + * 区别于 `embed`:嵌入态会额外强制主题 + 走 sessionStorage 隔离登录身份;`hideChrome` 只想要 + * 一个干净的全屏页面(如「大屏绘制」`?hideChrome=1`),其余行为与正常标签页完全一致。 + */ + hideChrome: boolean; /** 独立会话:鉴权身份按本标签页隔离(走 sessionStorage),用于同浏览器登录不同账号互不覆盖。 */ isolate: boolean; } @@ -69,6 +75,11 @@ export function readHideJumpParam(search: string): FlagParam { return readFlagParam(search, "hideJump"); } +/** 读 URL 里的 `hideChrome` 三态(仅去全局导航栏 + 侧边栏)。 */ +export function readHideChromeParam(search: string): FlagParam { + return readFlagParam(search, "hideChrome"); +} + /** 读 URL 里的 `isolate` 三态(独立会话登录)。 */ export function readIsolateParam(search: string): FlagParam { return readFlagParam(search, "isolate"); @@ -99,6 +110,7 @@ export function readEmbedSession(): EmbedSession | null { embed: p.embed === true, theme: p.theme === "dark" ? "dark" : "light", hideJump: p.hideJump === true, + hideChrome: p.hideChrome === true, isolate: p.isolate === true, }; } catch { @@ -109,7 +121,7 @@ export function readEmbedSession(): EmbedSession | null { /** 写会话;无任何开关置位时直接清除(保持 sessionStorage 干净)。隐私模式/quota 异常静默失败。 */ function writeEmbedSession(next: EmbedSession): void { try { - if (!next.embed && !next.hideJump && !next.isolate) { + if (!next.embed && !next.hideJump && !next.hideChrome && !next.isolate) { sessionStorage.removeItem(EMBED_SESSION_KEY); return; } @@ -129,6 +141,16 @@ export function isJumpBarHidden(): boolean { return readEmbedSession()?.hideJump === true; } +/** + * 本标签页是否要去全局 chrome(Header 导航栏 + PageSidebar 侧边栏)。 + * `embed` 已隐含去 chrome,故这里取「embed || hideChrome」之并——任一置位都隐藏。 + * 与 `isEmbedActive()` 的差别:`hideChrome` 不强制主题、不隔离鉴权,只去 chrome。 + */ +export function isChromeHidden(): boolean { + const s = readEmbedSession(); + return s?.embed === true || s?.hideChrome === true; +} + /** * 本标签页是否处于独立会话态:鉴权身份按标签页隔离(走 sessionStorage)。 * `embed` 已隐含同样的隔离,故 `authStorage()` 取「embed || isolate」之并;这里只判 `isolate`, @@ -146,7 +168,7 @@ export function isIsolatedSession(): boolean { export function syncEmbedSessionFromSearch(search: string): void { const cur = readEmbedSession() ?? - { embed: false, theme: "light" as EmbedTheme, hideJump: false, isolate: false }; + { embed: false, theme: "light" as EmbedTheme, hideJump: false, hideChrome: false, isolate: false }; const next: EmbedSession = { ...cur }; const embedParam = readEmbedParam(search); @@ -161,6 +183,10 @@ export function syncEmbedSessionFromSearch(search: string): void { if (hideJumpParam === "on") next.hideJump = true; else if (hideJumpParam === "off") next.hideJump = false; + const hideChromeParam = readHideChromeParam(search); + if (hideChromeParam === "on") next.hideChrome = true; + else if (hideChromeParam === "off") next.hideChrome = false; + const isolateParam = readIsolateParam(search); if (isolateParam === "on") next.isolate = true; else if (isolateParam === "off") next.isolate = false; @@ -208,6 +234,6 @@ export function resolveForcedEmbedTheme(search: string): EmbedTheme | undefined export function setIsolatedSession(on: boolean): void { const cur = readEmbedSession() ?? - { embed: false, theme: "light" as EmbedTheme, hideJump: false, isolate: false }; + { embed: false, theme: "light" as EmbedTheme, hideJump: false, hideChrome: false, isolate: false }; writeEmbedSession({ ...cur, isolate: on }); } diff --git a/frontend-web/src/pages/PageRoutes.tsx b/frontend-web/src/pages/PageRoutes.tsx index a49f984c..208f82bc 100644 --- a/frontend-web/src/pages/PageRoutes.tsx +++ b/frontend-web/src/pages/PageRoutes.tsx @@ -6,7 +6,12 @@ import { PageSidebarDark } from "@/components/page-sidebar-dark"; import { isChatsIframeWorkspacePath } from "@/core/page-layout/chats-iframe-route"; import { readSidebarEnabled } from "@/core/page-layout/sidebar-enabled"; import { isWujieEnv } from "@/core/embed/wujie"; -import { isEmbedActive, readEmbedParam } from "@/core/embed/embed-session"; +import { + isEmbedActive, + isChromeHidden, + readEmbedParam, + readHideChromeParam, +} from "@/core/embed/embed-session"; import { useTheme } from "@/contexts/ThemeContext"; import { useLocation } from "react-router-dom"; import { useActiveThirdLevel } from "@/core/page-layout/use-sidebar-menu"; @@ -44,6 +49,13 @@ function PageLayout() { () => isEmbedActive() || readEmbedParam(location.search) === "on", [location.pathname, location.search], ); + // `?hideChrome=1`:仅去全局导航栏 + 侧边栏(不强制主题/不隔离鉴权),用于「大屏绘制」等想要 + // 干净全屏的页面。与 embed 一样按标签页 sessionStorage 持久化,跨登录跳转/页内导航存活;非置位 + // 标签页恒为 false,不受影响。按 location 重算以跟随页内导航。 + const chromeHidden = useMemo( + () => isChromeHidden() || readHideChromeParam(location.search) === "on", + [location.pathname, location.search], + ); // 「大屏绘制」独立页(/page/strategy/dashboard-agent)本就是「带导航栏 + 侧边栏」的独立页, // 但它常被深链「上一步」从去 chrome 的工作区/新标签打开——那里 chromeByPreference 可能为 false // (界面模式非全量、或会话级 sidebar 未置位),导致整页没有顶栏/侧栏。故对该路由**强制全局 chrome** @@ -52,7 +64,9 @@ function PageLayout() { const pageSidebarAllowed = (forceChromeForDashboardAgent || chromeByPreference) && !isChatsIframeWorkspacePath(location.pathname) && - !embedActive; + !embedActive && + // `?hideChrome=1` 显式去 chrome:优先级高于上面对 dashboard-agent 的强制全局 chrome。 + !chromeHidden; // h-full(100%)而非 h-screen(100vh):配合 globals.css 的 html/body/#root height:100% // 高度链,根布局跟随容器高度。全屏下 100% 解析到视口,与 h-screen 等价;无界等嵌套