feat: 所有页面支持1到4层级的嵌套结构

This commit is contained in:
coolzr
2025-10-24 00:40:43 +08:00
parent 8d4d76184d
commit ad3cba549b
9 changed files with 846 additions and 43 deletions

View File

@@ -342,6 +342,137 @@ window.MeNav = {
}
};
// 多层级嵌套书签功能
window.MeNav.expandAll = function() {
document.querySelectorAll('.category.collapsed, .group.collapsed').forEach(element => {
element.classList.remove('collapsed');
saveToggleState(element, 'expanded');
});
};
window.MeNav.collapseAll = function() {
document.querySelectorAll('.category:not(.collapsed), .group:not(.collapsed)').forEach(element => {
element.classList.add('collapsed');
saveToggleState(element, 'collapsed');
});
};
window.MeNav.toggleCategory = function(categoryName, subcategoryName = null, groupName = null) {
const selector = groupName
? `[data-name="${categoryName}"] [data-name="${subcategoryName}"] [data-name="${groupName}"]`
: subcategoryName
? `[data-name="${categoryName}"] [data-name="${subcategoryName}"]`
: `[data-name="${categoryName}"]`;
const element = document.querySelector(selector);
if (element) {
toggleNestedElement(element);
}
};
window.MeNav.getNestedStructure = function() {
// 返回完整的嵌套结构数据
const categories = [];
document.querySelectorAll('.category-level-1').forEach(cat => {
categories.push(extractNestedData(cat));
});
return categories;
};
// 切换嵌套元素
function toggleNestedElement(container) {
const isCollapsed = container.classList.contains('collapsed');
if (isCollapsed) {
container.classList.remove('collapsed');
saveToggleState(container, 'expanded');
} else {
container.classList.add('collapsed');
saveToggleState(container, 'collapsed');
}
// 触发自定义事件
const event = new CustomEvent('nestedToggle', {
detail: {
element: container,
type: container.dataset.type,
name: container.dataset.name,
isCollapsed: !isCollapsed
}
});
document.dispatchEvent(event);
}
// 保存切换状态
function saveToggleState(element, state) {
const type = element.dataset.type;
const name = element.dataset.name;
const level = element.dataset.level || '1';
const key = `menav-toggle-${type}-${level}-${name}`;
localStorage.setItem(key, state);
}
// 恢复切换状态
function restoreToggleState(element) {
const type = element.dataset.type;
const name = element.dataset.name;
const level = element.dataset.level || '1';
const key = `menav-toggle-${type}-${level}-${name}`;
const savedState = localStorage.getItem(key);
if (savedState === 'collapsed') {
element.classList.add('collapsed');
}
}
// 初始化嵌套分类
function initializeNestedCategories() {
// 为所有可折叠元素添加切换功能
document.querySelectorAll('[data-toggle="category"], [data-toggle="group"]').forEach(header => {
header.addEventListener('click', function(e) {
e.stopPropagation();
const container = this.parentElement;
toggleNestedElement(container);
});
// 恢复保存的状态
restoreToggleState(header.parentElement);
});
}
// 提取嵌套数据
function extractNestedData(element) {
const data = {
name: element.dataset.name,
type: element.dataset.type,
level: element.dataset.level,
isCollapsed: element.classList.contains('collapsed')
};
// 提取子元素数据
const subcategories = element.querySelectorAll(':scope > .category-content > .subcategories-container > .category');
if (subcategories.length > 0) {
data.subcategories = Array.from(subcategories).map(sub => extractNestedData(sub));
}
const groups = element.querySelectorAll(':scope > .category-content > .groups-container > .group');
if (groups.length > 0) {
data.groups = Array.from(groups).map(group => extractNestedData(group));
}
const sites = element.querySelectorAll(':scope > .category-content > .sites-grid > .site-card, :scope > .group-content > .sites-grid > .site-card');
if (sites.length > 0) {
data.sites = Array.from(sites).map(site => ({
name: site.dataset.name,
url: site.dataset.url,
icon: site.dataset.icon,
description: site.dataset.description
}));
}
return data;
}
document.addEventListener('DOMContentLoaded', () => {
// 先声明所有状态变量
let isSearchActive = false;
@@ -1312,6 +1443,9 @@ document.addEventListener('DOMContentLoaded', () => {
});
});
// 初始化嵌套分类功能
initializeNestedCategories();
// 初始化搜索索引使用requestIdleCallback或setTimeout延迟初始化避免影响页面加载
if ('requestIdleCallback' in window) {
requestIdleCallback(() => initSearchIndex());