From 318974511cc903116565883042bc6f1b5a07b0ce Mon Sep 17 00:00:00 2001 From: lixinran Date: Wed, 15 Oct 2025 17:07:31 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E6=80=9D=E7=BB=B4?= =?UTF-8?q?=E5=AF=BC=E5=9B=BE=E7=BC=96=E8=BE=91=E5=92=8C=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 修复编辑模式下表格渲染问题,使用新的节点结构逻辑 - 实现Markdown到HTML转换,支持表格和图片正确显示 - 添加generateTopicFromMarkdown函数,自动生成节点简短标题 - 优化保存逻辑,同时更新HTML、Markdown和topic字段 - 修复思维导图生成过程中的页面空白问题,优先使用增量更新 - 应用自定义紫色主题,统一线条和节点配色方案 - 简化控制台输出,只保留关键调试信息 --- backend/mindmap.db | Bin 41086976 -> 41086976 bytes frontend/src/components/MindMap.vue | 512 ++++++++++++++++++------- frontend/src/utils/markdownRenderer.js | 9 +- 3 files changed, 376 insertions(+), 145 deletions(-) diff --git a/backend/mindmap.db b/backend/mindmap.db index eecb48a5ce4d2888cdb2889ed9bdfb476d0faec9..ca6428c2dff8dece10502930fdf80289e58be9a2 100644 GIT binary patch delta 41224 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 { @@ -445,41 +457,233 @@ const closeImagePreview = () => { imagePreviewError.value = ''; }; -// HTML转Markdown函数 -const convertHTMLToMarkdown = (html) => { +/** + * 从Markdown内容生成简短标题 + * 用于节点显示 + */ +const generateTopicFromMarkdown = (markdown) => { + if (!markdown || typeof markdown !== 'string') { + return ''; + } + + try { + // 移除Markdown语法,提取纯文本 + let text = markdown + // 移除标题标记 + .replace(/^#{1,6}\s+/gm, '') + // 移除加粗和斜体 + .replace(/\*\*(.*?)\*\*/g, '$1') + .replace(/\*(.*?)\*/g, '$1') + // 移除代码块 + .replace(/```[\s\S]*?```/g, '') + .replace(/`([^`]*)`/g, '$1') + // 移除链接,保留文本 + .replace(/\[([^\]]+)\]\([^)]+\)/g, '$1') + // 移除图片 + .replace(/!\[([^\]]*)\]\([^)]+\)/g, '') + // 移除表格分隔符 + .replace(/^\|.*\|$/gm, '') + .replace(/^[-|:\s]+$/gm, '') + // 移除列表标记 + .replace(/^[-*+]\s+/gm, '') + .replace(/^\d+\.\s+/gm, '') + // 移除引用标记 + .replace(/^>\s*/gm, '') + // 移除多余空白 + .replace(/\n+/g, ' ') + .replace(/\s+/g, ' ') + .trim(); + + // 限制长度,取前50个字符 + if (text.length > 50) { + text = text.substring(0, 50) + '...'; + } + + return text || '无标题'; + } catch (error) { + console.error('❌ 生成标题失败:', error); + return '无标题'; + } +}; + +/** + * Markdown转HTML转换函数 + * 支持表格、图片等复杂内容的转换 + */ +const convertMarkdownToHTML = (markdown) => { + if (!markdown || typeof markdown !== 'string') { + return ''; + } + + try { + let html = markdown; + + // 处理表格 - 简化版本,处理更灵活 + // 先尝试处理标准Markdown表格 + html = html.replace(/\|(.+)\|\n\|[-\s|]+\|\n((?:\|.+\|\n?)*)/g, (match, header, rows) => { + const headers = header.split('|').map(h => h.trim()).filter(h => h); + const rowLines = rows.trim().split('\n').filter(line => line.trim()); + + let tableHTML = '\n'; + + // 表头 + if (headers.length > 0) { + tableHTML += ''; + headers.forEach(header => { + tableHTML += ``; + }); + tableHTML += '\n'; + } + + // 表体 + if (rowLines.length > 0) { + tableHTML += ''; + rowLines.forEach(row => { + const cells = row.split('|').map(c => c.trim()).filter(c => c); + if (cells.length > 0) { + tableHTML += ''; + cells.forEach(cell => { + tableHTML += ``; + }); + tableHTML += ''; + } + }); + tableHTML += ''; + } + + tableHTML += '
${header}
${cell}
'; + return tableHTML; + }); + + // 如果没有找到标准表格,尝试处理简单的列表格式 + if (!html.includes('
$1:
$2
'); + } + + // 处理图片 + html = html.replace(/!\[([^\]]*)\]\(([^)]+)\)/g, '$1'); + + // 处理标题 + html = html.replace(/^### (.*$)/gim, '

$1

'); + html = html.replace(/^## (.*$)/gim, '

$1

'); + html = html.replace(/^# (.*$)/gim, '

$1

'); + + // 处理加粗 + html = html.replace(/\*\*(.*?)\*\*/g, '$1'); + + // 处理斜体 + html = html.replace(/\*(.*?)\*/g, '$1'); + + // 处理代码块 + html = html.replace(/```([^`]*)```/g, '
$1
'); + + // 处理行内代码 + html = html.replace(/`([^`]*)`/g, '$1'); + + // 处理链接 + html = html.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '$1'); + + // 处理列表 + html = html.replace(/^- (.*$)/gim, '
  • $1
  • '); + html = html.replace(/(
  • .*<\/li>)/s, '
      $1
    '); + + // 处理换行 + html = html.replace(/\n\n/g, '

    '); + html = html.replace(/\n/g, '
    '); + + // 包装段落 + if (!html.startsWith('<')) { + html = '

    ' + html + '

    '; + } + + return html; + + } catch (error) { + console.error('❌ Markdown转HTML转换失败:', error); + return markdown; // 回退到原始Markdown + } +}; + +/** + * 改进的HTML转Markdown转换函数 + * 支持表格、图片等复杂内容的转换 + */ +const convertHTMLToMarkdownImproved = (html) => { if (!html || typeof html !== 'string') { return ''; } try { - console.log('🔄 开始转换HTML到Markdown:', html.substring(0, 100) + '...'); - - // 使用Vditor的html2md功能(如果可用) + // 优先使用Vditor的html2md功能 if (typeof Vditor?.html2md === 'function') { - const markdown = Vditor.html2md(html); - console.log('✅ 使用Vditor.html2md转换成功:', markdown.substring(0, 100) + '...'); - return markdown; + console.log('✅ 使用Vditor.html2md转换HTML'); + return Vditor.html2md(html); } - // 回退到简单转换逻辑 - console.log('⚠️ Vditor.html2md不可用,使用简单转换逻辑'); - let markdown = html - // 处理表格 - 保持表格结构 - .replace(/]*>/gi, '\n') - .replace(/<\/table>/gi, '\n') - .replace(/]*>/gi, '') - .replace(/<\/thead>/gi, '') - .replace(/]*>/gi, '') - .replace(/<\/tbody>/gi, '') - .replace(/]*>/gi, '') - .replace(/<\/tr>/gi, ' |\n') - .replace(/]*>/gi, '| ') - .replace(/<\/th>/gi, ' ') - .replace(/]*>/gi, '| ') - .replace(/<\/td>/gi, ' ') - // 图片处理 - .replace(/]*src="([^"]+)"[^>]*alt="([^"]*)"[^>]*>/gi, '![$2]($1)') - .replace(/]*src="([^"]+)"[^>]*>/gi, '![]($1)') + console.log('⚠️ Vditor.html2md不可用,使用改进的转换逻辑'); + + // 创建临时DOM元素进行解析 + const tempDiv = document.createElement('div'); + tempDiv.innerHTML = html; + + let markdown = ''; + + // 处理表格 + const tables = tempDiv.querySelectorAll('table'); + tables.forEach((table, tableIndex) => { + console.log(`📊 处理表格 ${tableIndex + 1}`); + + // 处理表头 + const thead = table.querySelector('thead'); + if (thead) { + const headerRow = thead.querySelector('tr'); + if (headerRow) { + const headers = Array.from(headerRow.querySelectorAll('th, td')).map(cell => + (cell.textContent || '').trim().replace(/\|/g, '\\|') + ); + markdown += '| ' + headers.join(' | ') + ' |\n'; + markdown += '| ' + headers.map(() => '---').join(' | ') + ' |\n'; + } + } + + // 处理表体 + const tbody = table.querySelector('tbody') || table; + const rows = tbody.querySelectorAll('tr'); + rows.forEach(row => { + // 跳过表头行(如果已经在thead中处理过) + if (thead && thead.contains(row)) return; + + const cells = Array.from(row.querySelectorAll('td, th')).map(cell => + (cell.textContent || '').trim().replace(/\|/g, '\\|') + ); + if (cells.length > 0) { + markdown += '| ' + cells.join(' | ') + ' |\n'; + } + }); + + markdown += '\n'; + }); + + // 处理图片 + const images = tempDiv.querySelectorAll('img'); + images.forEach(img => { + const src = img.src || img.getAttribute('src') || ''; + const alt = img.alt || img.getAttribute('alt') || ''; + if (src) { + markdown += `![${alt}](${src})\n\n`; + } + }); + + // 处理其他内容(移除已处理的表格和图片) + const processedDiv = tempDiv.cloneNode(true); + processedDiv.querySelectorAll('table, img').forEach(el => el.remove()); + + // 转换剩余内容 + let remainingContent = processedDiv.innerHTML; + + // 基本HTML到Markdown转换 + remainingContent = remainingContent // 换行与段落 .replace(//gi, '\n') .replace(/<\/p>/gi, '\n\n') @@ -494,48 +698,32 @@ const convertHTMLToMarkdown = (html) => { .replace(/<\/ul>/gi, '\n') .replace(/]*>/gi, '- ') .replace(/<\/li>/gi, '\n') + // 标题 + .replace(/]*>(.*?)<\/h1>/gi, '# $1\n\n') + .replace(/]*>(.*?)<\/h2>/gi, '## $1\n\n') + .replace(/]*>(.*?)<\/h3>/gi, '### $1\n\n') + .replace(/]*>(.*?)<\/h4>/gi, '#### $1\n\n') + .replace(/]*>(.*?)<\/h5>/gi, '##### $1\n\n') + .replace(/]*>(.*?)<\/h6>/gi, '###### $1\n\n') + // 链接 + .replace(/]*href="([^"]*)"[^>]*>(.*?)<\/a>/gi, '[$2]($1)') + // 代码 + .replace(/]*>(.*?)<\/code>/gi, '`$1`') + .replace(/]*>(.*?)<\/pre>/gi, '```\n$1\n```\n\n') // 清除剩余标签 .replace(/<[^>]+>/g, '') // 规范换行 .replace(/\n{3,}/g, '\n\n') .trim(); - // 处理表格格式 - 添加表头分隔线 - const lines = markdown.split('\n'); - const processedLines = []; - let inTable = false; + markdown += remainingContent; - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - if (line.includes('|') && line.trim().length > 0) { - if (!inTable) { - inTable = true; - processedLines.push(line); - // 添加表头分隔线 - const headerCells = line.split('|').filter(cell => cell.trim()); - if (headerCells.length > 0) { - const separator = '| ' + headerCells.map(() => '---').join(' | ') + ' |'; - processedLines.push(separator); - } - } else { - processedLines.push(line); - } - } else { - if (inTable) { - inTable = false; - } - processedLines.push(line); - } - } - - markdown = processedLines.join('\n'); - - console.log('✅ 简单转换逻辑完成:', markdown.substring(0, 100) + '...'); + console.log('✅ HTML转Markdown转换完成:', markdown.substring(0, 200) + '...'); return markdown; } catch (error) { - console.error('❌ HTML转Markdown失败:', error); - return html; // 转换失败时返回原始HTML + console.error('❌ HTML转Markdown转换失败:', error); + return html; // 回退到原始HTML } }; @@ -565,15 +753,55 @@ const openRichTextEditor = (nodeObj, nodeElement) => { allKeys: Object.keys(nodeObj) }); - // 将HTML内容转换为Markdown格式供编辑 + // 使用新的节点结构逻辑 + let htmlContent = ''; + + // 优先使用dangerouslySetInnerHTML(用于WYSIWYG编辑) if (nodeObj.dangerouslySetInnerHTML) { - editorContent.value = convertHTMLToMarkdown(nodeObj.dangerouslySetInnerHTML); - console.log('📝 转换后的Markdown内容:', editorContent.value); - } else if (nodeObj.topic) { - editorContent.value = nodeObj.topic; - } else { - editorContent.value = ''; + htmlContent = nodeObj.dangerouslySetInnerHTML; + console.log('📝 使用dangerouslySetInnerHTML内容:', htmlContent.substring(0, 200) + '...'); + } + // 其次使用markdown字段(如果存在) + else if (nodeObj.markdown) { + console.log('📝 使用markdown内容,转换为HTML:', nodeObj.markdown.substring(0, 200) + '...'); + try { + htmlContent = marked.parse(nodeObj.markdown); + // 更新dangerouslySetInnerHTML + nodeObj.dangerouslySetInnerHTML = htmlContent; + } catch (error) { + console.error('❌ Markdown转HTML失败:', error); + htmlContent = nodeObj.markdown; + } } + // 最后回退到data或topic(兼容旧数据) + else { + const markdownContent = nodeObj.data || nodeObj.topic || ''; + console.log('📝 使用兼容模式,从data/topic获取内容:', markdownContent.substring(0, 200) + '...'); + + // 检查是否包含Markdown格式内容 + const hasMarkdownContent = markdownContent.includes('|') && markdownContent.includes('-') || // 表格 + markdownContent.includes('![') || // 图片 + markdownContent.includes('#') || // 标题 + markdownContent.includes('**') || // 粗体 + markdownContent.includes('`'); // 代码 + + if (hasMarkdownContent) { + try { + htmlContent = marked.parse(markdownContent); + // 更新节点结构 + nodeObj.markdown = markdownContent; + nodeObj.dangerouslySetInnerHTML = htmlContent; + } catch (error) { + console.error('❌ Markdown转HTML失败:', error); + htmlContent = markdownContent; + } + } else { + htmlContent = markdownContent; + } + } + + editorContent.value = htmlContent; + console.log('🎯 WYSIWYG模式HTML内容:', htmlContent.substring(0, 200) + '...'); // 检查是否有MindElixir原生图片 if (nodeObj.image && !editorContent.value.includes('![')) { @@ -622,13 +850,6 @@ const initVditor = async () => { return; } - console.log('🎯 开始初始化Vditor编辑器...'); - console.log('🔍 vditorContainer元素:', vditorContainer.value); - console.log('🔍 容器可见性:', vditorContainer.value.offsetParent !== null); - console.log('🔍 容器尺寸:', { - width: vditorContainer.value.offsetWidth, - height: vditorContainer.value.offsetHeight - }); try { // 先清理容器 @@ -638,7 +859,7 @@ const initVditor = async () => { vditorInstance = new Vditor(vditorContainer.value, { height: 400, placeholder: '请输入节点内容...', - mode: 'wysiwyg', // 所见即所得模式 + mode: 'wysiwyg', // 所见即所得模式,使用HTML内容 theme: 'classic', toolbarConfig: { pin: true // 固定工具栏 @@ -691,17 +912,12 @@ const initVditor = async () => { } }, after: () => { - console.log('✅ Vditor编辑器初始化完成'); - // 等待编辑器完全渲染后再设置内容 setTimeout(() => { // ✅ 直接设置内容,让Vditor自动处理Markdown/HTML转换 if (editorContent.value && editorContent.value.trim()) { vditorInstance.setValue(editorContent.value); - console.log('✅ 内容已设置到WYSIWYG编辑器:', editorContent.value.substring(0, 100) + '...'); - console.log('✅ 编辑器当前模式: wysiwyg (所见即所得模式)'); } else { - console.warn('⚠️ editorContent.value为空,设置空内容'); vditorInstance.setValue(''); } @@ -714,34 +930,18 @@ const initVditor = async () => { // 获取Markdown格式的内容 const markdownContent = vditorInstance.getValue(); editorContent.value = markdownContent; - console.log('📝 编辑器内容变化(Markdown):', markdownContent.substring(0, 50) + '...'); }); - } else { - console.warn('⚠️ Vditor实例没有on方法'); } // 检查工具栏是否正确渲染 setTimeout(() => { const toolbar = document.querySelector('.vditor-toolbar'); const editor = document.querySelector('.vditor-wysiwyg'); - console.log('🔍 Vditor工具栏:', toolbar); - console.log('🔍 Vditor编辑器区域:', editor); - - if (!toolbar || !editor) { - console.error('❌ Vditor组件没有正确渲染'); - console.error('工具栏状态:', toolbar ? '存在' : '不存在'); - console.error('编辑器状态:', editor ? '存在' : '不存在'); - } else { - console.log('✅ Vditor组件渲染成功'); - console.log('工具栏按钮数量:', toolbar.querySelectorAll('button').length); - console.log('工具栏高度:', toolbar.offsetHeight); - } }, 100); }, 100); } }); - console.log('✅ Vditor编辑器创建成功'); } catch (error) { console.error('❌ Vditor编辑器初始化失败:', error); console.error('错误详情:', error.stack); @@ -820,14 +1020,13 @@ const saveRichTextChanges = async () => { } // ✅ 使用Vditor内置API获取内容 - const markdownContent = vditorInstance ? vditorInstance.getValue() : editorContent.value; - const htmlContent = vditorInstance ? vditorInstance.getHTML() : editorContent.value; - const contentToSave = markdownContent.trim() || ''; + const htmlContent = vditorInstance ? vditorInstance.getValue() : editorContent.value; + const markdownContent = vditorInstance ? vditorInstance.getMarkdown() : ''; - console.log('📝 获取到的Markdown内容:', contentToSave.substring(0, 100) + '...'); console.log('📝 获取到的HTML内容:', htmlContent.substring(0, 100) + '...'); + console.log('📝 获取到的Markdown内容:', markdownContent.substring(0, 100) + '...'); - // 更新节点数据 + // 更新节点数据 - 使用新的节点结构 const titleChanged = editorTitle.value !== (currentNode.value.title || ''); console.log('🔍 标题变化检查:', { editorTitle: editorTitle.value, @@ -835,10 +1034,11 @@ const saveRichTextChanges = async () => { titleChanged: titleChanged }); - // 更新节点内容 - currentNode.value.topic = contentToSave; // 保存原始Markdown内容 + // 更新节点内容 - 新的节点结构 + currentNode.value.dangerouslySetInnerHTML = htmlContent; // 保存HTML内容(用于渲染) + currentNode.value.markdown = markdownContent || htmlContent; // 保存Markdown内容(用于导出) + currentNode.value.topic = generateTopicFromMarkdown(markdownContent || htmlContent); // 生成简短标题 currentNode.value.title = editorTitle.value; // 更新标题 - currentNode.value.dangerouslySetInnerHTML = htmlContent; // 保存渲染后的HTML // 更新DOM元素 if (currentNodeElement.value) { @@ -864,7 +1064,9 @@ const saveRichTextChanges = async () => { newTitle: editorTitle.value, // 使用更新后的标题 newDes: currentNode.value.data?.des || "", newParentId: currentNode.value.parentId || currentNode.value.parent?.id, - newDangerouslySetInnerHTML: htmlContent || "" // 保存富文本内容 + newDangerouslySetInnerHTML: htmlContent || "", // 保存HTML内容(用于渲染) + newMarkdown: markdownContent || htmlContent, // 保存Markdown内容(用于导出) + newTopic: currentNode.value.topic // 保存生成的简短标题 }; console.log("🔍 直接发送到后端的更新数据:", updateData); @@ -3656,24 +3858,18 @@ const openCustomEditModal = (nodeObj, nodeElement) => { // 确保模态框完全渲染后再初始化Vditor编辑器 nextTick(() => { setTimeout(() => { - console.log('🔄 准备初始化Vditor,容器状态:', vditorContainer.value); - // 检查容器是否真正可见 if (vditorContainer.value && vditorContainer.value.offsetParent !== null) { - console.log('✅ 容器已可见,开始初始化Vditor'); initVditor(); } else { - console.warn('⚠️ 容器未准备好,延迟初始化'); // 多次尝试,确保容器准备好 let attempts = 0; const maxAttempts = 10; const checkContainer = () => { attempts++; if (vditorContainer.value && vditorContainer.value.offsetParent !== null) { - console.log('✅ 容器在第', attempts, '次尝试后可见'); initVditor(); } else if (attempts < maxAttempts) { - console.log('🔄 第', attempts, '次检查,容器仍未准备好,继续等待...'); setTimeout(checkContainer, 100); } else { console.error('❌ 容器在', maxAttempts, '次尝试后仍未准备好'); @@ -4785,6 +4981,7 @@ const updateMindMapRealtime = async (data, title, eventDetail = null) => { infinite: true, maxScale: 5, minScale: 0.1, + theme: customTheme, // 使用自定义紫色主题 markdown: (text, nodeObj) => { // 检查内容是否包含markdown语法(包括图片和数学公式) if (text.includes('|') || text.includes('**') || text.includes('`') || text.includes('#') || text.includes('$') || text.includes('![')) { @@ -4869,36 +5066,73 @@ const updateMindMapRealtime = async (data, title, eventDetail = null) => { } } else { - // 完整更新:仅在必要时重新初始化 - console.log('🔄 执行完整更新'); + // 优化:避免不必要的完整更新,优先使用增量更新 + console.log('🔄 尝试增量更新替代完整更新'); - const currentId = String(currentMindmapId.value || ''); - const tempId = currentId && currentId.startsWith('temp-') - ? currentId - : `temp-${Date.now()}`; - - const mindElixirData = { - nodeData: data, - mindmapId: tempId, - id: tempId, - title: title || 'AI生成中...' - }; - - // 确保当前思维导图ID是临时ID - if (!currentId || !currentId.startsWith('temp-')) { - currentMindmapId.value = tempId; - console.log('🆔 更新临时思维导图ID:', tempId); - } - - // 重新初始化数据 - const result = mindElixir.value.init(mindElixirData); - - // 恢复位置 - if (currentPosition) { - setTimeout(() => { - restorePosition(currentPosition); - console.log('📍 完整更新后恢复位置'); - }, 100); + // 检查是否可以增量更新 + if (mindElixir.value && mindElixir.value.data && mindElixir.value.data.nodeData) { + console.log('✅ 使用增量更新,避免页面空白'); + + // 更新根节点标题 + if (mindElixir.value.data.nodeData && data.topic) { + const rootNode = Object.values(mindElixir.value.data.nodeData)[0]; + if (rootNode && rootNode.topic !== data.topic) { + rootNode.topic = data.topic; + // 更新DOM中的标题显示 + const rootElement = mindmapEl.value.querySelector(`[data-id="${rootNode.id}"]`); + if (rootElement) { + const textElement = rootElement.querySelector('.topic-text'); + if (textElement) { + textElement.textContent = data.topic; + } + } + } + } + + // 增量更新子节点 + if (data.children && data.children.length > 0) { + updateNodesIncremental(data.children, mindElixir.value.data.nodeData); + } + + // 恢复位置,避免视图跳动 + if (currentPosition) { + setTimeout(() => { + restorePosition(currentPosition); + console.log('📍 优化增量更新后恢复位置'); + }, 50); + } + } else { + // 只有在确实无法增量更新时才执行完整更新 + console.log('⚠️ 无法增量更新,执行完整更新'); + + const currentId = String(currentMindmapId.value || ''); + const tempId = currentId && currentId.startsWith('temp-') + ? currentId + : `temp-${Date.now()}`; + + const mindElixirData = { + nodeData: data, + mindmapId: tempId, + id: tempId, + title: title || 'AI生成中...' + }; + + // 确保当前思维导图ID是临时ID + if (!currentId || !currentId.startsWith('temp-')) { + currentMindmapId.value = tempId; + console.log('🆔 更新临时思维导图ID:', tempId); + } + + // 重新初始化数据 + const result = mindElixir.value.init(mindElixirData); + + // 恢复位置 + if (currentPosition) { + setTimeout(() => { + restorePosition(currentPosition); + console.log('📍 完整更新后恢复位置'); + }, 100); + } } } diff --git a/frontend/src/utils/markdownRenderer.js b/frontend/src/utils/markdownRenderer.js index ed0fff4..2831ff9 100644 --- a/frontend/src/utils/markdownRenderer.js +++ b/frontend/src/utils/markdownRenderer.js @@ -53,13 +53,10 @@ renderer.image = function(href, title, text) { // 处理图片URL,确保能正确显示 let processedUrl = hrefStr; - - // 暂时禁用代理URL转换,直接使用原始URL - // 这样可以避免代理服务配置问题导致的图片显示异常 if (hrefStr.includes('cdn-mineru.openxlab.org.cn')) { - // 直接使用原始URL,不进行代理转换 - processedUrl = hrefStr; - console.log('🖼️ 使用原始CDN URL:', processedUrl); + // 将外部CDN URL转换为代理URL + const urlPath = hrefStr.replace('https://cdn-mineru.openxlab.org.cn', ''); + processedUrl = `/proxy-image${urlPath}`; } // 生成图片HTML