feat(iframe): add iframe bridge for ragflow integration
- Implement Penpal-based iframe communication bridge between host and child apps - Add route handling for ragflow integration with '/route-ragflow' prefix - Update navigation hooks to support embedded mode via iframe bridge - Configure build and dependencies for new iframe-bridge package - Adjust nginx config for proper SPA routing in subpath deployments
This commit is contained in:
@@ -108,7 +108,7 @@ function AgentListPage() {
|
||||
onCreateAgent={() => setCreateOpen(true)}
|
||||
onEdit={(agent) => { setEditTarget(agent); setEditOpen(true); }}
|
||||
onView={(agent) => {
|
||||
navigate(`/agent/${agent.id}`);
|
||||
navigate(`/route-ragflow/agent/${agent.id}`);
|
||||
}}
|
||||
onDelete={async (agent) => {
|
||||
const confirmed = await dialog.confirm({
|
||||
|
||||
40
src/pages/ragflow/agent.tsx
Normal file
40
src/pages/ragflow/agent.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import { useEffect, useMemo, useRef } from 'react';
|
||||
import { useNavigate, useParams } from 'react-router-dom';
|
||||
import { createPenpalHostBridge } from '@teres/iframe-bridge';
|
||||
import logger from '@/utils/logger';
|
||||
|
||||
export default function RagflowAgentPage() {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
const navigate = useNavigate();
|
||||
const iframeRef = useRef<HTMLIFrameElement>(null);
|
||||
const src = useMemo(() => `/ragflow/agent/${id ?? ''}`, [id]);
|
||||
|
||||
logger.info('RagflowAgentPage', id, src);
|
||||
|
||||
useEffect(() => {
|
||||
const el = iframeRef.current;
|
||||
if (!el) return;
|
||||
const { destroy } = createPenpalHostBridge({
|
||||
iframe: el,
|
||||
methods: {
|
||||
navigate: (to: string) => navigate(to),
|
||||
close: () => navigate('/agents'),
|
||||
agentReady: () => {
|
||||
// 可选:在需要时记录或触发后续逻辑
|
||||
},
|
||||
},
|
||||
});
|
||||
return () => destroy();
|
||||
}, [navigate]);
|
||||
|
||||
// 如需兼容旧的 postMessage 事件,可保留以下监听;为了纯 Penpal,此处移除
|
||||
|
||||
return (
|
||||
<iframe
|
||||
ref={iframeRef}
|
||||
title="ragflow-agent"
|
||||
src={src}
|
||||
style={{ width: '100%', height: '100vh', border: 'none' }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
37
src/pages/ragflow/iframe.tsx
Normal file
37
src/pages/ragflow/iframe.tsx
Normal file
@@ -0,0 +1,37 @@
|
||||
import { useEffect, useRef } from 'react';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { createPenpalHostBridge, toChildPath } from '@teres/iframe-bridge';
|
||||
|
||||
export default function RagflowIframePage() {
|
||||
const location = useLocation();
|
||||
const navigate = useNavigate();
|
||||
// 将宿主的 "/route-ragflow/**" 路径转换为子应用的 "/ragflow/**" 路径
|
||||
const childPath = toChildPath(location.pathname);
|
||||
const src = `/ragflow${childPath}${location.search}${location.hash}`;
|
||||
const iframeRef = useRef<HTMLIFrameElement>(null);
|
||||
|
||||
useEffect(() => {
|
||||
const el = iframeRef.current;
|
||||
if (!el) return;
|
||||
const { destroy } = createPenpalHostBridge({
|
||||
iframe: el,
|
||||
methods: {
|
||||
navigate: (to: string) => navigate(to),
|
||||
close: () => navigate('/agents'),
|
||||
agentReady: () => {
|
||||
// 可选:记录或触发后续逻辑
|
||||
},
|
||||
},
|
||||
});
|
||||
return () => destroy();
|
||||
}, [navigate, src]);
|
||||
|
||||
return (
|
||||
<iframe
|
||||
ref={iframeRef}
|
||||
title="ragflow"
|
||||
src={src}
|
||||
style={{ width: '100%', height: '100vh', border: 'none' }}
|
||||
/>
|
||||
);
|
||||
}
|
||||
9
src/pages/ragflow/layout.tsx
Normal file
9
src/pages/ragflow/layout.tsx
Normal file
@@ -0,0 +1,9 @@
|
||||
import { Outlet } from 'react-router-dom';
|
||||
|
||||
export default function RagflowLayout() {
|
||||
return (
|
||||
<div style={{ width: '100%', height: '100vh' }}>
|
||||
<Outlet />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -27,6 +27,9 @@ import ChunkParsedResult from '@/pages/chunk/parsed-result';
|
||||
import DocumentPreview from '@/pages/chunk/document-preview';
|
||||
import AgentDetailPage from '@/pages/agent-mui/detail';
|
||||
// import AgentDetailPage from '@/pages/agent';
|
||||
import RagflowLayout from '@/pages/ragflow/layout';
|
||||
import RagflowIframePage from '@/pages/ragflow/iframe';
|
||||
import RagflowAgentPage from '@/pages/ragflow/agent';
|
||||
|
||||
const AppRoutes = () => {
|
||||
return (
|
||||
@@ -70,6 +73,13 @@ const AppRoutes = () => {
|
||||
<Route path="mcp" element={<MCPSetting />} />
|
||||
</Route>
|
||||
|
||||
{/* 通过 iframe 承载 ragflow 应用,避免路由冲突并保持同源 */}
|
||||
{/* ragflow 路由分组:layout 承载,agent 为特定页面,其余走通用 iframe */}
|
||||
<Route path="route-ragflow" element={<RagflowLayout />}>
|
||||
<Route path="agent/:id" element={<RagflowAgentPage />} />
|
||||
<Route path="*" element={<RagflowIframePage />} />
|
||||
</Route>
|
||||
|
||||
{/* 处理未匹配的路由 */}
|
||||
<Route path="*" element={<Navigate to="/knowledge" replace />} />
|
||||
</Routes>
|
||||
|
||||
Reference in New Issue
Block a user