Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
172 changes: 164 additions & 8 deletions src/data/extra/web/js/mindmap/features/outline/outline.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@ class OutlineFeature {
this.lastSize = null; // 记录最后的大小
this.COLLAPSE_THRESHOLD = 750; // 思维导图尺寸小于这个值时自动折叠
this.titleBarHeight = 45; // 标题栏高度

// 添加防抖和监听器管理
this.updateTimer = null;
this.mutationObserver = null;
this.isUpdating = false;
this.lastUpdateTime = 0;
this.UPDATE_DEBOUNCE_DELAY = 300; // 防抖延迟300ms
}

/**
Expand All @@ -34,6 +41,9 @@ class OutlineFeature {
*/
init() {
console.log('OutlineFeature: init called');
// 先清理之前的实例(如果有的话)
this.cleanupObservers();

// 先检查并删除已存在的大纲窗口
const existingWindow = document.getElementById('vx-outline-window');
if (existingWindow) {
Expand Down Expand Up @@ -581,13 +591,44 @@ class OutlineFeature {
}

/**
* 更新大纲窗口内容
* 更新大纲窗口内容(带防抖)
* 步骤:
* 1. 清空现有内容
* 2. 获取根节点数据
* 3. 递归渲染节点结构
*/
updateOutlineWindow() {
// 防抖处理 - 清除之前的定时器
if (this.updateTimer) {
clearTimeout(this.updateTimer);
}

// 如果正在更新,直接返回
if (this.isUpdating) {
return;
}

// 检查更新频率限制
const now = Date.now();
const timeSinceLastUpdate = now - this.lastUpdateTime;

if (timeSinceLastUpdate < 500) { // 500ms内不重复更新
this.updateTimer = setTimeout(() => {
this.doUpdateOutlineWindow();
}, this.UPDATE_DEBOUNCE_DELAY);
return;
}

// 设置延迟更新
this.updateTimer = setTimeout(() => {
this.doUpdateOutlineWindow();
}, 50); // 短延迟确保DOM更新完成
}

/**
* 实际执行大纲窗口更新
*/
doUpdateOutlineWindow() {
if (!this.outlineWindow) {
console.warn('OutlineFeature: outlineWindow not found');
return;
Expand All @@ -600,6 +641,9 @@ class OutlineFeature {
}

try {
this.isUpdating = true;
this.lastUpdateTime = Date.now();

// 获取MindElixir数据
const allData = this.core.mindElixir && this.core.mindElixir.getAllData();

Expand All @@ -614,6 +658,13 @@ class OutlineFeature {
} catch (error) {
console.error('OutlineFeature: Error updating outline window:', error);
content.innerHTML = '<div style="color: #e74c3c; text-align: center; padding: 20px;">数据加载失败</div>';
} finally {
this.isUpdating = false;
// 清除定时器
if (this.updateTimer) {
clearTimeout(this.updateTimer);
this.updateTimer = null;
}
}
}

Expand Down Expand Up @@ -948,15 +999,95 @@ class OutlineFeature {
* 监听思维导图变化并更新大纲
*/
setupDOMObserver() {
const observer = new MutationObserver(() => {
this.updateOutlineWindow();
});
// 清理之前的监听器
this.cleanupObservers();

// 监听 MindElixir 的 operation 事件
this.core.mindElixir.bus.addListener('operation', (operation) => {
console.log('OutlineFeature: Operation detected:', operation.name);

// 根据操作类型决定是否需要更新大纲
const outlineUpdateOperations = [
'addChild', // 添加子节点
'removeNode', // 删除节点
'moveNode', // 移动节点
'finishEdit' // 完成编辑(文本内容变化)
];

const immediateUpdateOperations = [
'addChild',
'removeNode',
'moveNode'
];

const delayedUpdateOperations = [
'finishEdit' // 编辑完成时再更新,避免输入过程中频繁更新
];

if (immediateUpdateOperations.includes(operation.name)) {
// 立即更新(有50ms防抖)
this.updateOutlineWindow();
} else if (delayedUpdateOperations.includes(operation.name)) {
// 延迟更新,给更多时间让用户完成编辑
if (this.updateTimer) {
clearTimeout(this.updateTimer);
}
this.updateTimer = setTimeout(() => {
this.doUpdateOutlineWindow();
}, this.UPDATE_DEBOUNCE_DELAY);
}

observer.observe(document.getElementById('vx-mindmap'), {
childList: true,
subtree: true,
characterData: true
// 不再监听 editStyle, editTags, editIcons 等,这些不影响大纲结构
});

// 创建单一的 MutationObserver 作为备用监听器
// 只监听结构性变化,不监听文本内容变化
const mindmapElement = document.getElementById('vx-mindmap');
if (mindmapElement) {
this.mutationObserver = new MutationObserver((mutations) => {
let needsUpdate = false;

mutations.forEach((mutation) => {
// 只关注子节点的添加/删除,忽略文本内容变化
if (mutation.type === 'childList' &&
(mutation.addedNodes.length > 0 || mutation.removedNodes.length > 0)) {
needsUpdate = true;
}
});

if (needsUpdate) {
console.log('OutlineFeature: DOM structure change detected via MutationObserver');
this.updateOutlineWindow();
}
});

// 只监听子节点变化,不监听characterData
this.mutationObserver.observe(mindmapElement, {
childList: true,
subtree: true
// 不包含 characterData: true,避免文本编辑时的频繁触发
});
}
}

/**
* 清理所有观察器和监听器
*/
cleanupObservers() {
// 清理定时器
if (this.updateTimer) {
clearTimeout(this.updateTimer);
this.updateTimer = null;
}

// 清理 MutationObserver
if (this.mutationObserver) {
this.mutationObserver.disconnect();
this.mutationObserver = null;
}

// 重置状态
this.isUpdating = false;
}

/**
Expand Down Expand Up @@ -999,4 +1130,29 @@ class OutlineFeature {
});
}
}

/**
* 销毁大纲功能,清理所有资源
*/
destroy() {
console.log('OutlineFeature: destroy called');

// 清理所有观察器和监听器
this.cleanupObservers();

// 移除大纲窗口
if (this.outlineWindow && this.outlineWindow.parentNode) {
this.outlineWindow.parentNode.removeChild(this.outlineWindow);
}

// 清理所有引用
this.outlineWindow = null;
this.core = null;
this.nodeDataMap.clear();
this.collapsedNodes = null;
this.lastPosition = null;
this.lastSize = null;

console.log('OutlineFeature: destroyed successfully');
}
}
Loading