feat(embed): iframe 嵌入模式(去 chrome + 主题隔离 / hideJump / 登录用户隔离)
外部系统用 iframe 套用深链单智能体问答/多智能体会商时,三个按标签页(sessionStorage) 开关,由深链 URL 参数开启、跨登录跳转/刷新/页内导航存活,绝不跨标签污染正常系统页: - embed=1(可带 theme=):去全局导航栏 + 侧边栏;按外部主题色强制明/暗,走 next-themes forcedTheme(不写 localStorage),嵌入侧不调 setTheme/persistSidebarEnabled - hideJump=1:隐藏深链底部「快捷跳转」栏 TaskJumpBar(底部「下一步」按钮 + 右侧悬浮菜单) - 登录隔离:嵌入态鉴权/身份键(deerflow.auth/userInfo/login.*/tools_token 等)改走 sessionStorage(core/auth/scoped-storage.ts 的 authStorage),iframe 登录用户与正常 登录用户完全隔离、互不覆盖;token 读取统一过 getStoredAuth,改中心函数即全体生效; 跨标签账号变更监听 AuthSyncWatcher 嵌入态禁用 嵌入会话在 main.tsx 渲染前同步启用(早于任何鉴权写入)。正常标签页 isXxx() 恒 false, 行为零变化。新增 core/embed/embed-session.ts、core/auth/scoped-storage.ts。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
1 parent
2b58fe6c8b
commit
31f9838095
16 files changed
+337
-58
No files matched your search
@@ -20,6 +20,7 @@ import {
|
||||
} from '@/core/auth/task-deeplink';
|
||||
import { resolveUsernameByOnlineTicket } from '@/core/auth/wujie-ticket-login';
|
||||
import { isWujieEnv, useWujieProps } from '@/core/embed/wujie';
|
||||
import { readEmbedParam } from '@/core/embed/embed-session';
|
||||
import {
|
||||
GENERAL_QA_ROUTE,
|
||||
loginByYUserId,
|
||||
@@ -48,6 +49,13 @@ export default function LoginPage() {
|
||||
() => isSidebarEnabledFromSearch(searchParams.toString()),
|
||||
[searchParams],
|
||||
);
|
||||
// iframe 嵌入模式(外部系统 `&embed=1`):去 chrome + 按标签页强制主题。嵌入侧**不**走
|
||||
// setTheme / persistSidebarEnabled(那俩落 localStorage、会跨标签污染正常系统页),改用
|
||||
// sessionStorage 嵌入会话(见 embed-session.ts)。`embed=0` 显式关闭。
|
||||
const isEmbed = useMemo(
|
||||
() => readEmbedParam(searchParams.toString()) === 'on',
|
||||
[searchParams],
|
||||
);
|
||||
const redirectPath = useMemo(
|
||||
() =>
|
||||
searchParams.get('from') === IFRAME_LOGIN_SOURCE
|
||||
@@ -105,11 +113,14 @@ export default function LoginPage() {
|
||||
|
||||
// 主题切换:无界 props.theme 优先,其次 URL query theme。两者取值约定一致
|
||||
// (dark-blue → 深色;red → 浅色;blue 暂无该主题,先用浅色代替),统一走 resolveThemeFromQuery。
|
||||
// **嵌入模式跳过**:setTheme 会写 localStorage `strategy-theme`,跨标签污染正常系统页;
|
||||
// 嵌入主题改由 forcedTheme(按标签页、不持久化)接管。
|
||||
useEffect(() => {
|
||||
if (isEmbed) return;
|
||||
const themeValue = wujieProps.theme || externalParams.theme;
|
||||
if (!themeValue) return;
|
||||
setTheme(resolveThemeFromQuery(themeValue));
|
||||
}, [wujieProps.theme, externalParams.theme, setTheme]);
|
||||
}, [isEmbed, wujieProps.theme, externalParams.theme, setTheme]);
|
||||
|
||||
// ticket / userId / env 持久化(与 login 请求并行做,互不阻塞)
|
||||
useEffect(() => {
|
||||
@@ -154,7 +165,9 @@ export default function LoginPage() {
|
||||
// 内网透传的 access_token 合并进 userInfo(在 loginByYUserId 覆盖之后),
|
||||
// XK/PG 中任务 iframe 据此取 accessToken
|
||||
persistAccessToken(accessToken);
|
||||
persistSidebarEnabled(sidebarEnabled);
|
||||
// 嵌入模式不写 sidebarEnabled(localStorage 的「简洁/全量」偏好会跨标签污染正常页);
|
||||
// 去 chrome 由按标签页的嵌入会话(isEmbedActive)驱动,见 PageLayout。
|
||||
if (!isEmbed) persistSidebarEnabled(sidebarEnabled);
|
||||
setStatus('success');
|
||||
|
||||
// 任务深链:带 taskId + goPath 时拉任务详情并直送对应问答/会商页;
|
||||
@@ -209,6 +222,7 @@ export default function LoginPage() {
|
||||
gatePassword,
|
||||
taskId,
|
||||
goPath,
|
||||
isEmbed,
|
||||
]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -6,6 +6,7 @@ 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 { useTheme } from "@/contexts/ThemeContext";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { useActiveThirdLevel } from "@/core/page-layout/use-sidebar-menu";
|
||||
@@ -37,8 +38,14 @@ function PageLayout() {
|
||||
const [layoutMode] = useLayoutMode();
|
||||
const chromeByPreference =
|
||||
layoutMode === "full" ? true : layoutMode === "compact" ? false : sidebarEnabled;
|
||||
// iframe 嵌入模式(外部系统 `&embed=1`,按标签页 sessionStorage):去掉全局导航栏 + 侧边栏,
|
||||
// 只留问答/会商工作区本体。按 location 重算以跟随页内导航;非嵌入标签页恒为 false,不受影响。
|
||||
const embedActive = useMemo(
|
||||
() => isEmbedActive() || readEmbedParam(location.search) === "on",
|
||||
[location.pathname, location.search],
|
||||
);
|
||||
const pageSidebarAllowed =
|
||||
chromeByPreference && !isChatsIframeWorkspacePath(location.pathname);
|
||||
chromeByPreference && !isChatsIframeWorkspacePath(location.pathname) && !embedActive;
|
||||
|
||||
// h-full(100%)而非 h-screen(100vh):配合 globals.css 的 html/body/#root height:100%
|
||||
// 高度链,根布局跟随容器高度。全屏下 100% 解析到视口,与 h-screen 等价;无界等嵌套
|
||||
|
||||
Reference in new issue
Block a user