Files
rainblogweb/frontend/src/pages/Embed.jsx
T

100 lines
4.4 KiB
React
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import React, { useEffect, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import { useTheme } from '../theme.jsx';
import { getToken } from '../api/client.js';
/**
* 面板嵌入页(迁移自 embed.html):
* 入口参数 ?url=&title=&proxy=1 —— iframe 展示目标站点;
* proxy=1 或 HTTPS 页内嵌 HTTP 目标时走 /api/proxy/fetch?url=(代理剥 X-Frame-Options/CSP)。
* 工具栏:关闭(有来源则 history.back)、标题、原站(新标签)、主题切换。
*/
export default function Embed() {
const [searchParams] = useSearchParams();
const url = searchParams.get('url');
const title = searchParams.get('title') || url || '嵌入';
const useProxy = searchParams.get('proxy') === '1';
const { theme, toggleTheme } = useTheme();
const [showHint, setShowHint] = useState(false);
const isHttpsPage = window.location.protocol === 'https:';
const isHttpTarget = url ? url.startsWith('http:') : false;
const needsProxy = useProxy || (isHttpsPage && isHttpTarget);
// proxy 模式:iframe 无法携带 Authorization header,改用 ?token= 认证(后端 queryTokenAuth 转写)
const iframeSrc = url
? (needsProxy
? '/api/proxy/fetch?url=' + encodeURIComponent(url) + '&token=' + encodeURIComponent(getToken() || '')
: url)
: '';
// 非代理嵌入时 2s 后提示"无法嵌入?安装扩展"(照 v1)
useEffect(() => {
if (!url || needsProxy) return;
const t = setTimeout(() => setShowHint(true), 2000);
return () => clearTimeout(t);
}, [url, needsProxy]);
const handleClose = (e) => {
// 有来源页时返回上一页,否则跳首页
if (document.referrer && document.referrer !== window.location.href) {
e.preventDefault();
window.history.back();
}
};
return (
<div className="embed-page" style={{ height: 'calc(100vh - 56px)', display: 'flex', flexDirection: 'column', overflow: 'hidden' }}>
<h1 className="sr-only">嵌入页面</h1>
<div
className="embed-toolbar"
style={{ display: 'flex', alignItems: 'center', gap: 8, padding: '0 12px', height: 48, background: 'var(--md-ref-surface-container)', borderBottom: '1px solid var(--md-ref-outline-variant)', flexShrink: 0 }}
>
<a href="/" className="btn-icon" onClick={handleClose} title="关闭"><span className="material-icons">close</span></a>
<span className="embed-title" style={{ flex: 1, fontWeight: 500, fontSize: 15, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{title}</span>
{url && (
<a href={url} target="_blank" rel="noopener" className="btn btn-text btn-sm" title="在新标签页中打开">
<span className="material-icons">open_in_new</span> 原站
</a>
)}
<button className="btn-icon" onClick={toggleTheme} title="切换主题">
<span className="material-icons">{theme === 'dark' ? 'light_mode' : 'dark_mode'}</span>
</button>
</div>
{showHint && (
<div style={{ padding: '8px 16px', background: 'var(--md-ref-primary-container)', color: 'var(--md-ref-on-primary-container)', fontSize: 13, textAlign: 'center', flexShrink: 0 }}>
无法嵌入试试安装{' '}
<a href="https://chromewebstore.google.com/search/ignore%20x-frame" target="_blank" rel="noopener" style={{ color: 'inherit', fontWeight: 600, textDecoration: 'underline' }}>
Ignore X-Frame-Headers
</a>{' '}
扩展或点击右上角原站在新标签页打开
<button
type="button"
onClick={() => setShowHint(false)}
aria-label="关闭提示"
style={{ cursor: 'pointer', marginLeft: 8, fontWeight: 600, height: 'auto', padding: '2px 8px', fontSize: 'inherit', fontFamily: 'inherit', background: 'transparent', border: 'none', color: 'inherit' }}
>
</button>
</div>
)}
{iframeSrc ? (
<iframe
className="embed-container"
title={title}
style={{ flex: 1, width: '100%', border: 'none' }}
sandbox="allow-scripts allow-forms allow-same-origin allow-popups"
loading="lazy"
src={iframeSrc}
/>
) : (
<div className="empty-state" style={{ flex: 1 }}>
<div className="empty-icon">🔗</div>
<p>缺少 url 参数无法加载嵌入内容</p>
</div>
)}
</div>
);
}