修复图片渲染器href属性检查逻辑,支持href.href属性

- 修复markdownRenderer.js中图片渲染器的href属性检查
- 添加对href.href属性的支持,解决src='[object Object]'错误
- 修复saveRichTextChanges函数中的async/await语法错误
- 优化模态框标题显示逻辑,确保标题和内容区域文字一致
- 修复节点标题同步问题,直接更新MindElixir的topic字段
- 智能更新topic字段,保留图片Markdown语法的同时更新标题文本
This commit is contained in:
lixinran 2025-10-11 17:41:12 +08:00
parent 4f072de2ee
commit 3693899a2b
5 changed files with 702 additions and 83 deletions

Binary file not shown.

View File

@ -54,8 +54,8 @@ def convert_to_mindelixir_format(mindmap, nodes):
# 添加HTML内容如果存在
if node.html_content:
node_data["dangerouslySetInnerHTML"] = node.html_content
# 如果有HTML内容清空topic以避免冲突
node_data["topic"] = ""
# 保留topic内容因为可能需要markdown原始内容
# node_data["topic"] = ""
# 添加图片信息
if node.image_url:

View File

@ -168,44 +168,73 @@
<div v-if="showRichTextEditor" class="rich-text-editor-modal" @click="closeRichTextEditor">
<div class="rich-text-editor-content" @click.stop>
<div class="rich-text-editor-header">
<span class="rich-text-editor-title">{{ editorTitle }}</span>
<span
v-if="!isEditingTitle"
class="rich-text-editor-title editable-title"
@dblclick="startEditTitle"
>
{{ editorTitle }}
</span>
<input
v-else
ref="titleInput"
v-model="editingTitleValue"
class="rich-text-editor-title-input"
@blur="finishEditTitle"
@keyup.enter="finishEditTitle"
@keyup.esc="cancelEditTitle"
/>
<button @click="closeRichTextEditor" class="rich-text-editor-close">×</button>
</div>
<div class="rich-text-editor-body">
<div class="editor-toolbar">
<div class="file-input-wrapper">
<input
type="file"
@change="handleFileSelect"
accept="image/*"
id="richTextFileInput"
class="file-input"
/>
<label for="richTextFileInput" class="file-input-label">
📁 插入图片
</label>
</div>
<p class="file-hint">支持 JPGPNGGIF 格式最大 5MB</p>
</div>
<div class="editor-main">
<textarea
id="richTextEditor"
v-model="editorContent"
class="rich-text-textarea"
placeholder="输入Markdown格式内容...
<!-- 已有图片展示区域 -->
<div v-if="existingImages.length > 0" class="existing-images-section">
<div class="existing-images-grid">
<!-- 添加图片虚线框 - 放在左侧 -->
<div class="add-image-horizontal">
<div class="file-input-wrapper">
<input
type="file"
@change="handleFileSelect"
accept="image/*"
id="richTextFileInput"
class="file-input"
/>
<label for="richTextFileInput" class="add-image-box">
<div class="add-image-icon">+</div>
<div class="add-image-text">
<div class="add-image-title">添加图片</div>
<div class="add-image-subtitle">点击选择图片文件</div>
</div>
</label>
</div>
<p class="file-hint">支持 JPGPNGGIF 格式最大 5MB</p>
</div>
支持的功能
文本编辑
图片插入![描述](图片链接)
表格| 列1 | 列2 |
列表 项目1
标题# 标题
点击上方'插入图片'按钮可以添加本地图片"
></textarea>
<div class="editor-preview" v-if="editorContent">
<h5>预览</h5>
<div class="preview-content" v-html="renderMarkdownPreview(editorContent)"></div>
<div
v-for="(image, index) in existingImages"
:key="index"
class="existing-image-container"
>
<div
class="existing-image-item"
@click="previewExistingImage(image)"
>
<img :src="image.url" :alt="image.alt" class="existing-image-preview" />
<div class="existing-image-overlay">
<span class="insert-text">点击查看</span>
</div>
<button
class="delete-image-btn"
@click.stop="deleteExistingImage(index)"
title="删除图片"
>
×
</button>
</div>
</div>
</div>
</div>
</div>
</div>
@ -216,6 +245,23 @@
</div>
</div>
<!-- 删除确认弹窗 -->
<div v-if="showDeleteConfirm" class="delete-confirm-modal" @click="cancelDeleteImage">
<div class="delete-confirm-content" @click.stop>
<div class="delete-confirm-header">
<h3>确认删除</h3>
</div>
<div class="delete-confirm-body">
<p>确定要删除这张图片吗</p>
<p class="delete-warning">删除后无法恢复</p>
</div>
<div class="delete-confirm-footer">
<button @click="cancelDeleteImage" class="cancel-btn">取消</button>
<button @click="confirmDeleteImage" class="confirm-btn">确定删除</button>
</div>
</div>
</div>
</div>
</template>
@ -223,6 +269,7 @@
import { ref, onMounted, onUnmounted, nextTick } from 'vue';
import MindElixir from '../lib/mind-elixir/dist/MindElixir.js';
import '../lib/mind-elixir/dist/style.css';
import { renderMarkdownToHTML } from '../utils/markdownRenderer.js';
//
const customTheme = {
@ -273,8 +320,7 @@ const customTheme = {
import { mindmapAPI } from '../api/mindmap.js';
import {
smartRenderNodeContent,
hasMarkdownSyntax,
renderMarkdownToHTML
hasMarkdownSyntax
} from '../utils/markdownRenderer.js';
//
@ -313,6 +359,13 @@ const editorTitle = ref('');
const editorContent = ref('');
const currentNode = ref(null);
const currentNodeElement = ref(null);
const existingImages = ref([]); //
const showDeleteConfirm = ref(false); //
const deleteImageIndex = ref(-1); //
const sectionTitle = ref(''); //
const isEditingTitle = ref(false); //
const editingTitleValue = ref(''); //
const titleInput = ref(null); //
@ -404,7 +457,25 @@ const openRichTextEditor = (nodeObj, nodeElement) => {
currentNode.value = nodeObj;
currentNodeElement.value = nodeElement;
editorTitle.value = nodeObj.topic || '编辑节点内容';
// 使topic
let tempTitle = nodeObj.title;
if (!tempTitle && nodeObj.topic) {
// nodeObj.titlenodeObj.topic
// Markdown
tempTitle = nodeObj.topic.replace(/!\[.*?\]\(.*?\)/g, '').trim();
}
editorTitle.value = tempTitle || '编辑节点内容';
//
console.log('📝 节点完整数据:', {
title: nodeObj.title,
topic: nodeObj.topic,
dangerouslySetInnerHTML: nodeObj.dangerouslySetInnerHTML,
image: nodeObj.image,
hasImage: !!nodeObj.image,
htmlContent: nodeObj.htmlContent,
allKeys: Object.keys(nodeObj)
});
// HTMLMarkdown
if (nodeObj.dangerouslySetInnerHTML) {
@ -416,6 +487,18 @@ const openRichTextEditor = (nodeObj, nodeElement) => {
editorContent.value = '';
}
// MindElixir
if (nodeObj.image && !editorContent.value.includes('![')) {
console.log('📝 发现MindElixir原生图片添加到编辑器中');
const imageUrl = typeof nodeObj.image === 'string' ? nodeObj.image : nodeObj.image.url;
const imageAlt = nodeObj.image.alt || nodeObj.topic || '图片';
const imageMarkdown = `![${imageAlt}](${imageUrl})`;
editorContent.value = editorContent.value + '\n' + imageMarkdown;
}
//
parseExistingImages();
showRichTextEditor.value = true;
console.log('📝 富文本编辑器已打开');
};
@ -426,6 +509,150 @@ const closeRichTextEditor = () => {
editorContent.value = '';
currentNode.value = null;
currentNodeElement.value = null;
existingImages.value = [];
};
//
const parseExistingImages = () => {
existingImages.value = [];
if (!editorContent.value) return;
console.log('📝 开始解析图片,内容长度:', editorContent.value.length);
console.log('📝 内容前100字符:', editorContent.value.substring(0, 100));
// 使markdown ![alt](url)
const imageRegex = /!\[([^\]]*)\]\(([^)]+)\)/g;
let match;
while ((match = imageRegex.exec(editorContent.value)) !== null) {
const alt = match[1] || '图片';
const url = match[2];
console.log('📝 找到图片:', { alt, url: url.substring(0, 50) + '...' });
existingImages.value.push({
alt: alt,
url: url,
markdown: match[0] // markdown
});
}
// markdown
if (existingImages.value.length === 0) {
console.log('📝 未找到标准markdown图片格式尝试其他格式...');
// data:imagebase64
const base64Regex = /data:image\/[^;]+;base64,[A-Za-z0-9+/=]+/g;
let base64Match;
while ((base64Match = base64Regex.exec(editorContent.value)) !== null) {
const url = base64Match[0];
const alt = '图片';
console.log('📝 找到base64图片:', url.substring(0, 50) + '...');
existingImages.value.push({
alt: alt,
url: url,
markdown: `![${alt}](${url})`
});
}
// URL
const urlRegex = /https?:\/\/[^\s]+\.(jpg|jpeg|png|gif|webp)/gi;
let urlMatch;
while ((urlMatch = urlRegex.exec(editorContent.value)) !== null) {
const url = urlMatch[0];
const alt = '图片';
console.log('📝 找到URL图片:', url);
existingImages.value.push({
alt: alt,
url: url,
markdown: `![${alt}](${url})`
});
}
}
console.log('📝 解析到已有图片:', existingImages.value.length, '张');
};
//
const startEditTitle = () => {
editingTitleValue.value = editorTitle.value;
isEditingTitle.value = true;
// 使nextTickDOM
nextTick(() => {
if (titleInput.value) {
titleInput.value.focus();
titleInput.value.select();
}
});
};
//
const finishEditTitle = () => {
if (editingTitleValue.value.trim()) {
console.log('📝 完成编辑标题:', editingTitleValue.value.trim());
editorTitle.value = editingTitleValue.value.trim();
}
isEditingTitle.value = false;
};
//
const cancelEditTitle = () => {
isEditingTitle.value = false;
editingTitleValue.value = '';
};
//
const previewExistingImage = (image) => {
console.log('📝 预览已有图片:', image.alt);
openImagePreview(image.url, image.alt);
};
//
const deleteExistingImage = (index) => {
showDeleteConfirm.value = true;
deleteImageIndex.value = index;
};
//
const confirmDeleteImage = () => {
const index = deleteImageIndex.value;
//
existingImages.value.splice(index, 1);
// markdown
if (existingImages.value.length > 0) {
//
let newContent = '';
existingImages.value.forEach(img => {
newContent += img.markdown + '\n';
});
editorContent.value = newContent;
} else {
//
const textContent = currentNode.value?.topic || '';
editorContent.value = textContent;
}
console.log('📝 已删除图片,剩余图片数量:', existingImages.value.length);
//
showDeleteConfirm.value = false;
deleteImageIndex.value = -1;
};
//
const cancelDeleteImage = () => {
showDeleteConfirm.value = false;
deleteImageIndex.value = -1;
};
const handleImageFileSelect = (event) => {
@ -551,40 +778,98 @@ const handleFileSelect = (event) => {
const imageUrl = e.target.result;
const fileName = file.name || 'image';
// Markdown
const textarea = document.getElementById('richTextEditor');
const cursorPos = textarea.selectionStart;
//
const imageMarkdown = `![${fileName}](${imageUrl})\n`;
editorContent.value = editorContent.value + imageMarkdown;
const newContent = editorContent.value.slice(0, cursorPos) + imageMarkdown + editorContent.value.slice(cursorPos);
editorContent.value = newContent;
//
parseExistingImages();
//
setTimeout(() => {
textarea.focus();
textarea.setSelectionRange(cursorPos + imageMarkdown.length, cursorPos + imageMarkdown.length);
}, 0);
console.log('📝 图片已添加到编辑器:', fileName);
};
reader.readAsDataURL(file);
};
const saveRichTextChanges = () => {
if (!currentNode.value || !editorContent.value.trim()) {
alert('请输入内容');
const saveRichTextChanges = async () => {
console.log('🚀 开始保存富文本内容...');
console.log('📝 当前editorTitle:', editorTitle.value);
console.log('📝 当前节点:', currentNode.value);
if (!currentNode.value) {
alert('节点信息丢失');
return;
}
//
const contentToSave = editorContent.value.trim() || '';
// 使markdownMarkdownHTML
const { renderMarkdownToHTML } = require('../utils/markdownRenderer.js');
const htmlContent = renderMarkdownToHTML(editorContent.value);
const htmlContent = contentToSave ? renderMarkdownToHTML(contentToSave) : '';
//
currentNode.value.topic = editorContent.value; // Markdown
// MindElixir使topic
const titleChanged = editorTitle.value !== (currentNode.value.title || '');
console.log('🔍 标题变化检查:', {
editorTitle: editorTitle.value,
currentNodeTitle: currentNode.value.title || '',
titleChanged: titleChanged
});
if (titleChanged) {
// Markdown
// topicMarkdown
const originalTopic = currentNode.value.topic || '';
const imageMatches = originalTopic.match(/!\[.*?\]\(.*?\)/g);
const imageMarkdown = imageMatches ? imageMatches.join('\n') : '';
// topic + Markdown
const newTopic = editorTitle.value + (imageMarkdown ? '\n' + imageMarkdown : '');
currentNode.value.topic = newTopic;
currentNode.value.title = editorTitle.value;
console.log('📝 节点topic已更新:', newTopic);
} else {
//
currentNode.value.topic = contentToSave; // Markdown
}
currentNode.value.dangerouslySetInnerHTML = htmlContent; // HTML
// DOM
if (currentNodeElement.value) {
currentNodeElement.value.innerHTML = htmlContent;
//
if (titleChanged) {
//
const textElement = currentNodeElement.value.querySelector('.tpc');
if (textElement) {
textElement.textContent = editorTitle.value;
console.log('📝 DOM文本元素已更新:', editorTitle.value);
}
}
}
// APIoperation
try {
const updateData = {
newTitle: editorTitle.value, // 使
newDes: currentNode.value.data?.des || "",
newParentId: currentNode.value.parentId || currentNode.value.parent?.id,
newDangerouslySetInnerHTML: htmlContent || "" //
};
console.log("🔍 直接发送到后端的更新数据:", updateData);
const response = await mindmapAPI.updateNode(currentNode.value.id, updateData);
if (response.data && response.data.success) {
console.log("✅ 节点数据已保存到后端");
} else {
console.warn("⚠️ 保存节点到后端失败:", response);
}
} catch (error) {
console.error("❌ 保存节点到后端失败:", error);
}
//
@ -594,18 +879,39 @@ const saveRichTextChanges = () => {
origin: currentNode.value.topic || '',
});
//
mindElixir.value?.render();
//
if (titleChanged) {
mindElixir.value?.bus.fire('operation', {
name: 'updateNode',
obj: currentNode.value,
origin: editorTitle.value,
});
}
//
closeRichTextEditor();
//
if (mindElixir.value) {
// DOM
nextTick(() => {
mindElixir.value.refresh();
console.log('🔄 思维导图已刷新');
});
}
console.log('✅ 富文本内容已保存');
closeRichTextEditor();
};
// Markdown
const renderMarkdownPreview = (markdown) => {
if (!markdown) return '';
const { renderMarkdownToHTML } = require('../utils/markdownRenderer.js');
return renderMarkdownToHTML(markdown);
try {
return renderMarkdownToHTML(markdown);
} catch (error) {
console.error('渲染markdown失败:', error);
return markdown; // markdown
}
};
const onImageLoad = () => {
@ -5057,7 +5363,7 @@ const updateMindMapRealtime = async (data, title) => {
display: flex;
justify-content: center;
align-items: center;
z-index: 10000;
z-index: 10002;
animation: fadeIn 0.3s ease;
}
@ -5283,6 +5589,14 @@ const updateMindMapRealtime = async (data, title) => {
font-weight: 600;
}
.add-image-horizontal {
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 8px;
flex-shrink: 0;
}
.file-input-wrapper {
margin-bottom: 8px;
}
@ -5291,26 +5605,273 @@ const updateMindMapRealtime = async (data, title) => {
display: none;
}
.file-input-label {
display: inline-block;
padding: 10px 20px;
background: #660874;
color: white;
border-radius: 6px;
.add-image-box {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 15px 10px;
border: 2px dashed #660874;
border-radius: 8px;
background: #fafafa;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: background 0.2s;
border: none;
transition: all 0.3s ease;
min-height: 60px;
width: 160px;
margin: 0;
box-sizing: border-box;
}
.file-input-label:hover {
background: #4d0655;
.add-image-box:hover {
border-color: #4d0655;
background: #f5f5f5;
transform: translateY(-1px);
box-shadow: 0 2px 8px rgba(102, 8, 116, 0.15);
}
.add-image-icon {
font-size: 18px;
color: #660874;
margin-bottom: 4px;
font-weight: 300;
line-height: 1;
}
.add-image-text {
text-align: center;
}
.add-image-title {
font-size: 12px;
font-weight: 600;
color: #333;
margin-bottom: 1px;
}
.add-image-subtitle {
font-size: 10px;
color: #666;
font-weight: 400;
}
/* 已有图片展示样式 */
.existing-images-section {
margin-bottom: 16px;
}
.section-title {
font-size: 14px;
font-weight: 600;
color: #333;
margin: 0 0 8px 0;
padding: 0;
}
.editable-title {
cursor: pointer;
padding: 4px 8px;
border-radius: 4px;
transition: all 0.2s ease;
border: 1px solid transparent;
}
.editable-title:hover {
background-color: #f5f5f5;
border-color: #e0e0e0;
}
.editable-title:active {
background-color: #e8e8e8;
}
.existing-images-grid {
display: flex;
flex-wrap: wrap;
gap: 12px;
margin-bottom: 16px;
align-items: flex-start;
justify-content: flex-start;
}
.existing-image-container {
display: flex;
flex-direction: column;
gap: 6px;
width: 160px;
flex-shrink: 0;
}
.existing-image-item {
position: relative;
border-radius: 6px;
overflow: hidden;
cursor: pointer;
transition: all 0.2s ease;
border: 1px solid #e0e0e0;
}
.existing-image-item:hover {
transform: scale(1.05);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
}
.existing-image-preview {
width: 100%;
height: 90px;
object-fit: cover;
display: block;
}
.existing-image-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(102, 8, 116, 0.8);
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transition: opacity 0.2s ease;
}
.existing-image-item:hover .existing-image-overlay {
opacity: 1;
}
.insert-text {
color: white;
font-size: 11px;
font-weight: 500;
text-align: center;
}
.delete-image-btn {
position: absolute;
top: 2px;
right: 2px;
width: 18px;
height: 18px;
border-radius: 50%;
background: #ff4757;
color: white;
border: none;
font-size: 12px;
font-weight: bold;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
transition: all 0.2s ease;
z-index: 10;
line-height: 1;
}
.delete-image-btn:hover {
background: #ff3742;
transform: scale(1.1);
}
/* 删除确认弹窗样式 */
.delete-confirm-modal {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.6);
display: flex;
justify-content: center;
align-items: center;
z-index: 10003;
animation: fadeIn 0.3s ease;
}
.delete-confirm-content {
background: white;
border-radius: 12px;
width: 90%;
max-width: 400px;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
overflow: hidden;
}
.delete-confirm-header {
background: #fff5f5;
padding: 20px 24px 16px;
border-bottom: 1px solid #ffe6e6;
}
.delete-confirm-header h3 {
margin: 0;
font-size: 18px;
font-weight: 600;
color: #dc2626;
}
.delete-confirm-body {
padding: 24px;
}
.delete-confirm-body p {
margin: 0 0 8px 0;
font-size: 16px;
color: #374151;
}
.delete-warning {
font-size: 14px;
color: #ef4444;
font-weight: 500;
}
.delete-confirm-footer {
padding: 16px 24px 24px;
display: flex;
justify-content: flex-end;
gap: 12px;
}
.cancel-btn {
padding: 10px 20px;
border: 1px solid #d1d5db;
background: white;
color: #374151;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
}
.cancel-btn:hover {
background: #f9fafb;
border-color: #9ca3af;
}
.confirm-btn {
padding: 10px 20px;
border: none;
background: #dc2626;
color: white;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.2s ease;
}
.confirm-btn:hover {
background: #b91c1c;
}
.file-hint {
margin: 0;
font-size: 12px;
font-size: 10px;
color: #666;
}
@ -5403,7 +5964,7 @@ const updateMindMapRealtime = async (data, title) => {
background: white;
border-radius: 12px;
width: 95%;
max-width: 1000px;
max-width: 700px;
max-height: 85vh;
overflow: hidden;
box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3);
@ -5427,6 +5988,24 @@ const updateMindMapRealtime = async (data, title) => {
color: #333;
}
.rich-text-editor-title-input {
font-size: 16px;
font-weight: 600;
color: #333;
border: 2px solid #660874;
border-radius: 4px;
padding: 4px 8px;
background: white;
outline: none;
margin: 0;
min-width: 200px;
}
.rich-text-editor-title-input:focus {
border-color: #4d0655;
box-shadow: 0 0 0 2px rgba(102, 8, 116, 0.2);
}
.rich-text-editor-close {
background: none;
border: none;

View File

@ -54,10 +54,16 @@ export default function (mind: MindElixirInstance) {
const imageUrl = img.src
const altText = img.alt || img.title || ''
console.log('🖼️ 双击图片节点,准备预览:', { imageUrl, altText })
console.log('🖼️ 双击图片节点,准备编辑:', { imageUrl, altText })
// 双击图片触发预览事件
mind.bus.fire('showImagePreview', imageUrl, altText)
// 双击图片触发富文本编辑事件(可以编辑图片)
const topicElement = target.closest('.me-tpc') as Topic
if (topicElement && topicElement.nodeObj) {
console.log('🖼️ 找到图片所在的节点:', topicElement.nodeObj)
mind.bus.fire('showRichTextEditor', topicElement.nodeObj, topicElement)
} else {
console.warn('🖼️ 未找到图片所在的节点')
}
return
}
@ -77,8 +83,9 @@ export default function (mind: MindElixirInstance) {
// 检查节点是否有MindElixir原生图片
if (topic.nodeObj?.image) {
const imageUrl = typeof topic.nodeObj.image === 'string' ? topic.nodeObj.image : topic.nodeObj.image.url
console.log('🖼️ 双击包含原生图片的节点,准备预览:', imageUrl)
mind.bus.fire('showImagePreview', imageUrl, topic.nodeObj.topic || '')
console.log('🖼️ 双击包含原生图片的节点,准备编辑:', imageUrl)
// 双击包含原生图片的节点触发富文本编辑事件
mind.bus.fire('showRichTextEditor', topic.nodeObj, topic)
return
}

View File

@ -19,11 +19,42 @@ const renderer = new marked.Renderer();
// 自定义图片渲染器
renderer.image = function(href, title, text) {
console.log('🖼️ 图片渲染器被调用:', { href, title, text, hrefType: typeof href });
// 确保href是字符串类型处理各种异常情况
let hrefStr = '';
if (typeof href === 'string') {
hrefStr = href;
} else if (href === null || href === undefined) {
console.warn('图片href为null或undefined');
return `<div class="markdown-error">图片链接为空</div>`;
} else if (typeof href === 'object') {
// 如果是对象尝试提取URL
if (href.href) {
hrefStr = String(href.href);
} else if (href.url) {
hrefStr = String(href.url);
} else if (href.src) {
hrefStr = String(href.src);
} else {
console.warn('图片href是对象但没有href、url或src属性:', href);
return `<div class="markdown-error">图片链接格式错误</div>`;
}
} else if (typeof href.toString === 'function') {
hrefStr = href.toString();
} else {
console.warn('图片href无法转换为字符串:', typeof href, href);
return `<div class="markdown-error">图片链接格式错误</div>`;
}
console.log('🖼️ 处理后的hrefStr:', hrefStr);
// 处理图片URL确保能正确显示
let processedUrl = href;
if (href.includes('cdn-mineru.openxlab.org.cn')) {
let processedUrl = hrefStr;
if (hrefStr.includes('cdn-mineru.openxlab.org.cn')) {
// 将外部CDN URL转换为代理URL
const urlPath = href.replace('https://cdn-mineru.openxlab.org.cn', '');
const urlPath = hrefStr.replace('https://cdn-mineru.openxlab.org.cn', '');
processedUrl = `/proxy-image${urlPath}`;
}
@ -31,6 +62,8 @@ renderer.image = function(href, title, text) {
const altText = text || '图片';
const titleAttr = title ? ` title="${title}"` : '';
console.log('🖼️ 最终生成的图片HTML:', `<img src="${processedUrl}" alt="${altText}"${titleAttr} class="markdown-image" />`);
return `<img src="${processedUrl}" alt="${altText}"${titleAttr} class="markdown-image" style="max-width: 200px; max-height: 150px; object-fit: contain; display: block; margin: 4px auto; border-radius: 4px; box-shadow: 0 2px 4px rgba(0,0,0,0.1);" />`;
};