From c7cf20391ef94639f57216b330d63ed42a9d9334 Mon Sep 17 00:00:00 2001 From: Seefs <40468931+seefs001@users.noreply.github.com> Date: Thu, 9 Apr 2026 14:35:31 +0800 Subject: [PATCH] fix: document render (#4153) --- .../common/DocumentRenderer/index.jsx | 49 ++++++------------- .../layout/headerbar/ThemeToggle.jsx | 16 +++--- 2 files changed, 23 insertions(+), 42 deletions(-) diff --git a/web/src/components/common/DocumentRenderer/index.jsx b/web/src/components/common/DocumentRenderer/index.jsx index 68e868c5..8c15c073 100644 --- a/web/src/components/common/DocumentRenderer/index.jsx +++ b/web/src/components/common/DocumentRenderer/index.jsx @@ -17,7 +17,7 @@ along with this program. If not, see . For commercial licensing, please contact support@quantumnous.com */ -import React, { useEffect, useState } from 'react'; +import React, { useEffect, useMemo, useState } from 'react'; import { API, showError } from '../../../helpers'; import { Empty, Card, Spin, Typography } from '@douyinfe/semi-ui'; const { Title } = Typography; @@ -28,7 +28,7 @@ import { import { useTranslation } from 'react-i18next'; import MarkdownRenderer from '../markdown/MarkdownRenderer'; -// 检查是否为 URL +// Check whether content is a URL. const isUrl = (content) => { try { new URL(content.trim()); @@ -38,27 +38,23 @@ const isUrl = (content) => { } }; -// 检查是否为 HTML 内容 +// Check whether content contains HTML. const isHtmlContent = (content) => { if (!content || typeof content !== 'string') return false; - // 检查是否包含HTML标签 const htmlTagRegex = /<\/?[a-z][\s\S]*>/i; return htmlTagRegex.test(content); }; -// 安全地渲染HTML内容 +// Parse HTML content and extract inline styles. const sanitizeHtml = (html) => { - // 创建一个临时元素来解析HTML const tempDiv = document.createElement('div'); tempDiv.innerHTML = html; - // 提取样式 const styles = Array.from(tempDiv.querySelectorAll('style')) .map((style) => style.innerHTML) .join('\n'); - // 提取body内容,如果没有body标签则使用全部内容 const bodyContent = tempDiv.querySelector('body'); const content = bodyContent ? bodyContent.innerHTML : html; @@ -76,15 +72,11 @@ const DocumentRenderer = ({ apiEndpoint, title, cacheKey, emptyMessage }) => { const { t } = useTranslation(); const [content, setContent] = useState(''); const [loading, setLoading] = useState(true); - const [htmlStyles, setHtmlStyles] = useState(''); - const [processedHtmlContent, setProcessedHtmlContent] = useState(''); const loadContent = async () => { - // 先从缓存中获取 const cachedContent = localStorage.getItem(cacheKey) || ''; if (cachedContent) { setContent(cachedContent); - processContent(cachedContent); setLoading(false); } @@ -93,7 +85,6 @@ const DocumentRenderer = ({ apiEndpoint, title, cacheKey, emptyMessage }) => { const { success, message, data } = res.data; if (success && data) { setContent(data); - processContent(data); localStorage.setItem(cacheKey, data); } else { if (!cachedContent) { @@ -111,16 +102,12 @@ const DocumentRenderer = ({ apiEndpoint, title, cacheKey, emptyMessage }) => { } }; - const processContent = (rawContent) => { - if (isHtmlContent(rawContent)) { - const { content: htmlContent, styles } = sanitizeHtml(rawContent); - setProcessedHtmlContent(htmlContent); - setHtmlStyles(styles); - } else { - setProcessedHtmlContent(''); - setHtmlStyles(''); + const htmlPayload = useMemo(() => { + if (!isHtmlContent(content)) { + return { content: '', styles: '' }; } - }; + return sanitizeHtml(content); + }, [content]); useEffect(() => { loadContent(); @@ -129,8 +116,9 @@ const DocumentRenderer = ({ apiEndpoint, title, cacheKey, emptyMessage }) => { // 处理HTML样式注入 useEffect(() => { const styleId = `document-renderer-styles-${cacheKey}`; + const { styles } = htmlPayload; - if (htmlStyles) { + if (styles) { let styleEl = document.getElementById(styleId); if (!styleEl) { styleEl = document.createElement('style'); @@ -138,7 +126,7 @@ const DocumentRenderer = ({ apiEndpoint, title, cacheKey, emptyMessage }) => { styleEl.type = 'text/css'; document.head.appendChild(styleEl); } - styleEl.innerHTML = htmlStyles; + styleEl.innerHTML = styles; } else { const el = document.getElementById(styleId); if (el) el.remove(); @@ -148,7 +136,7 @@ const DocumentRenderer = ({ apiEndpoint, title, cacheKey, emptyMessage }) => { const el = document.getElementById(styleId); if (el) el.remove(); }; - }, [htmlStyles, cacheKey]); + }, [cacheKey, htmlPayload]); // 显示加载状态 if (loading) { @@ -207,15 +195,6 @@ const DocumentRenderer = ({ apiEndpoint, title, cacheKey, emptyMessage }) => { // 如果是 HTML 内容,直接渲染 if (isHtmlContent(content)) { - const { content: htmlContent, styles } = sanitizeHtml(content); - - // 设置样式(如果有的话) - useEffect(() => { - if (styles && styles !== htmlStyles) { - setHtmlStyles(styles); - } - }, [content, styles, htmlStyles]); - return (
@@ -225,7 +204,7 @@ const DocumentRenderer = ({ apiEndpoint, title, cacheKey, emptyMessage }) => {
diff --git a/web/src/components/layout/headerbar/ThemeToggle.jsx b/web/src/components/layout/headerbar/ThemeToggle.jsx index 45e7918d..95cf8db5 100644 --- a/web/src/components/layout/headerbar/ThemeToggle.jsx +++ b/web/src/components/layout/headerbar/ThemeToggle.jsx @@ -95,13 +95,15 @@ const ThemeToggle = ({ theme, onThemeToggle, t }) => { } > -