feat: 实现对叶子节点的细分、优化ai追问

This commit is contained in:
lixinran 2025-09-08 16:51:12 +08:00
parent 0a64d46ea3
commit 7b6601e010
3 changed files with 298 additions and 17 deletions

Binary file not shown.

View File

@ -77,6 +77,22 @@
</div>
</div>
<!-- 历史记录 -->
<div class="section" v-if="history.length > 0">
<h4>📚 历史记录</h4>
<div class="history-list">
<div
v-for="(item, index) in history"
:key="index"
class="history-item"
@click="loadHistoryItem(item)"
>
<div class="history-title">{{ item.title }}</div>
<div class="history-time">{{ formatTime(item.timestamp) }}</div>
</div>
</div>
</div>
<!-- AI生成的Markdown结果 - 固定显示区域 -->
<div class="section">
<h4>📝 AI生成的Markdown结果</h4>
@ -121,16 +137,16 @@
</div>
</div>
<!-- 快速测试 -->
<!-- 快速测试
<div class="section">
<h4>🧪 快速测试</h4>
<div class="button-group">
<button @click="loadTestData" class="btn-test">📊 加载测试数据</button>
<button @click="clearAll" class="btn-clear">🗑 清空所有</button>
</div>
</div>
</div> -->
<!-- 历史记录 -->
<!-- 历史记录
<div class="section" v-if="history.length > 0">
<h4>📚 历史记录</h4>
<div class="history-list">
@ -144,7 +160,7 @@
<div class="history-time">{{ formatTime(item.timestamp) }}</div>
</div>
</div>
</div>
</div> -->
</div>
</div>
</div>
@ -788,12 +804,27 @@ const processContentIntelligently = (content, parentNode, nodeCounter) => {
}
}
//
//
if (remainingContent.length > 0) {
const finalContent = remainingContent.join('\n').trim();
if (finalContent) {
const formattedContent = formatMarkdownToText(finalContent);
parentNode.topic = parentNode.topic + '\n\n' + formattedContent;
//
const paragraphs = finalContent.split('\n\n').filter(p => p.trim());
paragraphs.forEach(paragraph => {
const cleanParagraph = formatMarkdownToText(paragraph.trim());
if (cleanParagraph) {
const paragraphNode = {
id: `node_${currentNodeCounter++}`,
topic: cleanParagraph,
children: [],
level: (parentNode.level || 0) + 1,
data: {}
};
parentNode.children.push(paragraphNode);
}
});
}
}

View File

@ -1228,6 +1228,216 @@ const submitAIQuestion = async () => {
};
// AI
// Markdown
const formatMarkdownToText = (markdown) => {
return markdown
//
.replace(/^### (.*$)/gim, '📋 $1') //
.replace(/^## (.*$)/gim, '📌 $1') //
.replace(/^# (.*$)/gim, '🎯 $1') //
// -
.replace(/\*\*(.*?)\*\*/g, (match, content) => {
//
if (content.includes(':')) {
const parts = content.split(':');
if (parts.length > 1) {
return `${parts[0]}】: ${parts.slice(1).join(':')}`;
}
}
return `${content}`;
})
//
.replace(/\*(.*?)\*/g, '《$1》')
//
.replace(/^- (.*$)/gim, ' • $1')
.replace(/^\d+\. (.*$)/gim, ' $&')
//
.replace(/```(.*?)```/gims, '💻 $1')
//
.replace(/`(.*?)`/g, '「$1」')
//
.replace(/\[([^\]]+)\]\([^)]+\)/g, '🔗 $1')
//
.replace(/\n\n/g, '\n')
.replace(/\n/g, '\n ');
};
//
const processContentIntelligently = (content, parentNode, nodeCounter) => {
const lines = content.split('\n');
let currentNodeCounter = nodeCounter;
let remainingContent = [];
for (let i = 0; i < lines.length; i++) {
const line = lines[i];
const trimmed = line.trim();
//
const leafMatch = trimmed.match(/^[-*+]\s*【(.+)】/);
if (leafMatch) {
//
const leafTitle = leafMatch[1].trim();
const leafNode = {
id: `node_${currentNodeCounter++}`,
topic: leafTitle,
children: [],
level: (parentNode.level || 0) + 1,
data: {}
};
//
let leafContent = [];
let j = i + 1;
while (j < lines.length) {
const nextLine = lines[j].trim();
const nextLeafMatch = nextLine.match(/^[-*+]\s*【(.+)】/);
if (nextLeafMatch) {
//
break;
}
//
const listMatch = nextLine.match(/^[-*+]\s+(.+)$/);
if (listMatch) {
const listItem = listMatch[1].trim();
const cleanListItem = formatMarkdownToText(listItem);
leafContent.push('• ' + cleanListItem);
}
j++;
}
//
if (leafContent.length > 0) {
const formattedContent = formatMarkdownToText(leafContent.join('\n'));
leafNode.topic = leafNode.topic + '\n\n' + formattedContent;
}
parentNode.children.push(leafNode);
//
i = j - 1;
} else if (trimmed) {
//
remainingContent.push(trimmed);
}
}
//
if (remainingContent.length > 0) {
const finalContent = remainingContent.join('\n').trim();
if (finalContent) {
//
const paragraphs = finalContent.split('\n\n').filter(p => p.trim());
paragraphs.forEach(paragraph => {
const cleanParagraph = formatMarkdownToText(paragraph.trim());
if (cleanParagraph) {
const paragraphNode = {
id: `node_${currentNodeCounter++}`,
topic: cleanParagraph,
children: [],
level: (parentNode.level || 0) + 1,
data: {}
};
parentNode.children.push(paragraphNode);
}
});
}
}
return { nodeCounter: currentNodeCounter };
};
// MarkdownJSON -
const markdownToJSON = (markdown) => {
const lines = markdown.split('\n');
let root = null;
const stack = [];
let nodeCounter = 0;
let currentContent = [];
lines.forEach((line, index) => {
const trimmed = line.trim();
//
const match = trimmed.match(/^(#{1,6})\s+(.+)$/);
if (match) {
//
if (currentContent.length > 0 && stack.length > 0) {
const content = currentContent.join('\n').trim();
if (content) {
//
const processedContent = processContentIntelligently(content, stack[stack.length - 1], nodeCounter);
nodeCounter = processedContent.nodeCounter;
}
currentContent = [];
}
const level = match[1].length;
const title = match[2].trim();
// Markdown
const cleanTitle = formatMarkdownToText(title);
//
const node = {
id: `node_${nodeCounter++}`,
topic: cleanTitle,
children: [],
level: level,
data: {}
};
//
if (level === 1 && !root) {
root = node;
stack.length = 0; //
stack.push(root);
} else {
//
while (stack.length > 1 && stack[stack.length - 1].level >= level) {
stack.pop();
}
//
if (stack.length > 0) {
stack[stack.length - 1].children.push(node);
}
//
stack.push(node);
}
} else if (trimmed) {
//
currentContent.push(trimmed);
}
});
//
if (currentContent.length > 0 && stack.length > 0) {
const content = currentContent.join('\n').trim();
if (content) {
const processedContent = processContentIntelligently(content, stack[stack.length - 1], nodeCounter);
nodeCounter = processedContent.nodeCounter;
}
}
//
if (!root) {
root = {
id: 'root',
topic: '根节点',
children: [],
data: {}
};
}
return root;
};
const createAINode = async (parentNode, question, answer) => {
try {
console.log('开始创建AI节点...', { parentNode, question, answer });
@ -1245,15 +1455,33 @@ const createAINode = async (parentNode, question, answer) => {
const formattedAnswer = formatAnswer(answer);
// 使API
const nodeData = {
title: `问题:${question}\n\n回答${formattedAnswer}`, // title
des: `AI追问产生的节点 - ${new Date().toLocaleString()}`, // AI
// 使MarkdownJSONAI
const aiMarkdown = `# ${question}\n\n${formattedAnswer}`;
const aiJSON = markdownToJSON(aiMarkdown);
// AI
const aiParentNode = {
title: question,
des: `AI追问产生的节点 - ${new Date().toLocaleString()}`,
parentId: parentNode.id,
isRoot: false
};
console.log('准备创建节点数据:', nodeData);
//
const childNodes = [];
if (aiJSON.children && aiJSON.children.length > 0) {
aiJSON.children.forEach(child => {
childNodes.push({
title: child.topic,
des: '',
parentId: null, //
isRoot: false
});
});
}
console.log('准备创建AI父节点:', aiParentNode);
console.log('准备创建AI子节点:', childNodes);
console.log('当前思维导图ID:', currentMindmapId.value);
// ID
@ -1261,11 +1489,33 @@ const createAINode = async (parentNode, question, answer) => {
throw new Error('没有找到当前思维导图ID无法创建节点');
}
// API
const response = await mindmapAPI.addNodes(currentMindmapId.value, [nodeData]);
console.log('API创建节点响应:', response);
//
const parentResponse = await mindmapAPI.addNodes(currentMindmapId.value, [aiParentNode]);
console.log('AI父节点创建响应:', parentResponse);
if (response.data && response.data.success) {
if (!parentResponse.data || !parentResponse.data.success) {
throw new Error('AI父节点创建失败');
}
// ID
const createdParentId = parentResponse.data.data?.nodes?.[0]?.id;
if (!createdParentId) {
throw new Error('无法获取创建的父节点ID');
}
// ID
childNodes.forEach(child => {
child.parentId = createdParentId;
});
//
let childResponse = null;
if (childNodes.length > 0) {
childResponse = await mindmapAPI.addNodes(currentMindmapId.value, childNodes);
console.log('AI子节点创建响应:', childResponse);
}
if (parentResponse.data && parentResponse.data.success) {
console.log('✅ AI节点创建成功开始刷新思维导图...');
//
@ -1273,7 +1523,7 @@ const createAINode = async (parentNode, question, answer) => {
console.log('✅ AI节点创建并刷新完成');
} else {
throw new Error('API创建节点失败');
throw new Error('AI父节点创建失败');
}
} catch (error) {