diff --git a/assets/style.css b/assets/style.css index a8d5c1d..6464246 100644 --- a/assets/style.css +++ b/assets/style.css @@ -892,6 +892,205 @@ body .content.expanded { font-size: 1.3rem; } +/* 多层级嵌套样式 */ +/* 层级2: 子分类 */ +.category-level-2 { + margin: 1.5rem 0 1.5rem 2rem; + padding: 1.5rem; + background: linear-gradient(145deg, + rgba(var(--card-bg-rgb), 0.8), + rgba(var(--card-bg-rgb), 0.6)); + border-left: 3px solid var(--accent-color); + border-radius: 12px; + width: calc(100% - 3.5rem); +} + +.category-level-2 .category-header h3 { + font-size: 1.1rem; + margin-bottom: 1rem; + color: var(--text-bright); + display: flex; + align-items: center; + gap: 0.6rem; + letter-spacing: 0.2px; +} + +.category-level-2 .category-header h3 i { + color: var(--accent-color); + font-size: 1.2rem; +} + +/* 层级3: 分组 */ +.group-level-3, .category-level-3 { + margin: 1rem 0 1rem 1.5rem; + padding: 1rem; + background: rgba(var(--card-bg-rgb), 0.4); + border-left: 2px solid var(--secondary-color); + border-radius: 8px; + width: calc(100% - 2.5rem); +} + +.group-level-3 .group-header h4, +.category-level-3 .category-header h4 { + font-size: 1rem; + margin-bottom: 0.8rem; + color: var(--text-bright); + display: flex; + align-items: center; + gap: 0.5rem; + letter-spacing: 0.2px; +} + +.group-level-3 .group-header h4 i, +.category-level-3 .category-header h4 i { + color: var(--secondary-color); + font-size: 1.1rem; +} + +/* 层级4: 更深层次的分组 */ +.group-level-4, .category-level-4 { + margin: 0.8rem 0 0.8rem 1rem; + padding: 0.8rem; + background: rgba(var(--card-bg-rgb), 0.2); + border-left: 1px solid var(--tertiary-color); + border-radius: 6px; + width: calc(100% - 1.8rem); +} + +.group-level-4 .group-header h5, +.category-level-4 .category-header h5 { + font-size: 0.95rem; + margin-bottom: 0.6rem; + color: var(--text-bright); + display: flex; + align-items: center; + gap: 0.4rem; + letter-spacing: 0.1px; +} + +.group-level-4 .group-header h5 i, +.category-level-4 .category-header h5 i { + color: var(--tertiary-color); + font-size: 1rem; +} + +/* 切换图标样式 */ +.toggle-icon { + display: inline-block; + margin-left: auto; + transition: transform 0.3s ease; + color: var(--text-muted); + font-size: 0.9rem; +} + +.category-header:hover .toggle-icon, +.group-header:hover .toggle-icon { + color: var(--accent-color); +} + +/* 展开/折叠动画 */ +.category-content, .group-content { + overflow: hidden; + transition: max-height 0.3s ease, opacity 0.3s ease; + max-height: 9999px; + opacity: 1; +} + +.category.collapsed .category-content, +.group.collapsed .group-content { + max-height: 0; + opacity: 0; +} + +.category.collapsed .toggle-icon i, +.group.collapsed .toggle-icon i { + transform: rotate(-90deg); +} + +/* 空内容提示 */ +.empty-content { + color: var(--text-muted); + font-style: italic; + text-align: center; + padding: 1rem; + font-size: 0.9rem; +} + +/* 子容器样式 */ +.subcategories-container, +.groups-container { + width: 100%; +} + +/* 确保嵌套的网站网格正确显示 */ +.category-level-2 .sites-grid, +.group-level-3 .sites-grid, +.category-level-3 .sites-grid, +.group-level-4 .sites-grid, +.category-level-4 .sites-grid { + margin-top: 0.8rem; + gap: 0.8rem; + grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); +} + +/* 响应式设计 - 嵌套结构 */ +@media (max-width: 768px) { + .category-level-2 { + margin-left: 1rem; + padding: 1rem; + width: calc(100% - 2rem); + } + + .group-level-3, .category-level-3 { + margin-left: 0.5rem; + padding: 0.8rem; + width: calc(100% - 1.3rem); + } + + .group-level-4, .category-level-4 { + margin-left: 0.3rem; + padding: 0.6rem; + width: calc(100% - 0.9rem); + } + + .category-level-2 .sites-grid, + .group-level-3 .sites-grid, + .category-level-3 .sites-grid, + .group-level-4 .sites-grid, + .category-level-4 .sites-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0.6rem; + } +} + +@media (max-width: 480px) { + .category { + margin-left: 1rem; + padding: 1.5rem; + } + + .category-level-2, .group-level-3, .category-level-3 { + margin-left: 0.5rem; + padding: 0.8rem; + width: calc(100% - 1rem); + } + + .group-level-4, .category-level-4 { + margin-left: 0.3rem; + padding: 0.6rem; + width: calc(100% - 0.6rem); + } + + .category-level-2 .sites-grid, + .group-level-3 .sites-grid, + .category-level-3 .sites-grid, + .group-level-4 .sites-grid, + .category-level-4 .sites-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0.5rem; + } +} + /* 网站卡片网格 */ .sites-grid { display: grid; diff --git a/config/_default/pages/bookmarks-four-level.yml b/config/_default/pages/bookmarks-four-level.yml new file mode 100644 index 0000000..2b4333c --- /dev/null +++ b/config/_default/pages/bookmarks-four-level.yml @@ -0,0 +1,313 @@ +# 四层级嵌套书签配置示例 +# 展示分类 -> 子分类 -> 分组 -> 网站的四层结构 +# 说明:该页面通常由“书签导入工具”自动生成,手工修改时请保持字段结构一致。 +title: 我的书签 +subtitle: 从浏览器导入的书签收藏 + +# 指定使用的模板文件名,现有页面模板可见 templates/pages(不含 .hbs) +template: bookmarks + +categories: + - name: '技术资源' + icon: 'fas fa-laptop-code' + subcategories: + - name: '前端开发' + icon: 'fas fa-code' + groups: + - name: '框架库' + icon: 'fas fa-cube' + sites: + - name: 'React' + url: 'https://reactjs.org/' + icon: 'fab fa-react' + description: 'React官方文档' + - name: 'Vue.js' + url: 'https://vuejs.org/' + icon: 'fab fa-vuejs' + description: 'Vue.js官方文档' + - name: 'Angular' + url: 'https://angular.io/' + icon: 'fab fa-angular' + description: 'Angular官方文档' + - name: 'Svelte' + url: 'https://svelte.dev/' + icon: 'fas fa-fire' + description: 'Svelte官方文档' + - name: '状态管理' + icon: 'fas fa-database' + sites: + - name: 'Redux' + url: 'https://redux.js.org/' + icon: 'fas fa-database' + description: 'Redux状态管理' + - name: 'Vuex' + url: 'https://vuex.vuejs.org/' + icon: 'fas fa-database' + description: 'Vue状态管理' + - name: 'MobX' + url: 'https://mobx.js.org/' + icon: 'fas fa-react' + description: '响应式状态管理' + - name: '构建工具' + icon: 'fas fa-tools' + sites: + - name: 'Webpack' + url: 'https://webpack.js.org/' + icon: 'fas fa-cube' + description: '模块打包工具' + - name: 'Vite' + url: 'https://vitejs.dev/' + icon: 'fas fa-bolt' + description: '下一代前端构建工具' + - name: 'Rollup' + url: 'https://rollupjs.org/' + icon: 'fas fa-compress' + description: '模块打包器' + - name: '后端开发' + icon: 'fas fa-server' + groups: + - name: 'Node.js生态' + icon: 'fab fa-node-js' + sites: + - name: 'Express' + url: 'https://expressjs.com/' + icon: 'fas fa-server' + description: 'Node.js Web框架' + - name: 'Koa' + url: 'https://koajs.com/' + icon: 'fas fa-leaf' + description: '下一代Node.js框架' + - name: 'NestJS' + url: 'https://nestjs.com/' + icon: 'fas fa-home' + description: 'Node.js企业级框架' + - name: 'Python框架' + icon: 'fab fa-python' + sites: + - name: 'Django' + url: 'https://www.djangoproject.com/' + icon: 'fas fa-python' + description: 'Python Web框架' + - name: 'Flask' + url: 'https://flask.palletsprojects.com/' + icon: 'fas fa-flask' + description: 'Python微框架' + - name: 'FastAPI' + url: 'https://fastapi.tiangolo.com/' + icon: 'fas fa-bolt' + description: '现代Python Web框架' + - name: '数据库ORM' + icon: 'fas fa-database' + sites: + - name: 'Prisma' + url: 'https://www.prisma.io/' + icon: 'fas fa-database' + description: '下一代数据库工具' + - name: 'TypeORM' + url: 'https://typeorm.io/' + icon: 'fas fa-database' + description: 'TypeScript ORM' + - name: 'SQLAlchemy' + url: 'https://www.sqlalchemy.org/' + icon: 'fas fa-database' + description: 'Python SQL工具包' + + - name: '设计资源' + icon: 'fas fa-palette' + subcategories: + - name: 'UI设计工具' + icon: 'fas fa-paint-brush' + groups: + - name: '原型设计' + icon: 'fas fa-drafting-compass' + sites: + - name: 'Figma' + url: 'https://www.figma.com/' + icon: 'fab fa-figma' + description: '协作式UI设计工具' + - name: 'Sketch' + url: 'https://www.sketch.com/' + icon: 'fab fa-sketch' + description: 'Mac平台UI设计工具' + - name: 'Adobe XD' + url: 'https://www.adobe.com/products/xd.html' + icon: 'fab fa-adobe' + description: 'Adobe UI设计工具' + - name: '设计系统' + icon: 'fas fa-th-large' + sites: + - name: 'Ant Design' + url: 'https://ant.design/' + icon: 'fas fa-th' + description: '企业级UI设计语言' + - name: 'Material Design' + url: 'https://material.io/design' + icon: 'fas fa-cube' + description: 'Google设计系统' + - name: 'Bootstrap' + url: 'https://getbootstrap.com/' + icon: 'fab fa-bootstrap' + description: '响应式CSS框架' + - name: '交互设计' + icon: 'fas fa-hand-pointer' + sites: + - name: 'Principle' + url: 'https://principleformac.com/' + icon: 'fas fa-magic' + description: '交互设计工具' + - name: 'Framer' + url: 'https://www.framer.com/' + icon: 'fas fa-mobile-alt' + description: '交互式设计工具' + - name: 'ProtoPie' + url: 'https://www.protopie.io/' + icon: 'fas fa-chart-pie' + description: '原型制作工具' + - name: '视觉资源' + icon: 'fas fa-image' + groups: + - name: '图标库' + icon: 'fas fa-icons' + sites: + - name: 'Font Awesome' + url: 'https://fontawesome.com/' + icon: 'fab fa-font-awesome' + description: '图标库' + - name: 'Iconfont' + url: 'https://www.iconfont.cn/' + icon: 'fas fa-icons' + description: '阿里巴巴图标库' + - name: 'Feather Icons' + url: 'https://feathericons.com/' + icon: 'fas fa-feather' + description: '简洁的图标库' + - name: '插画资源' + icon: 'fas fa-paint-brush' + sites: + - name: 'UnDraw' + url: 'https://undraw.co/' + icon: 'fas fa-palette' + description: '开源插画库' + - name: 'Illustrations.co' + url: 'https://illustrations.co/' + icon: 'fas fa-image' + description: '插画资源' + - name: 'Storyset' + url: 'https://storyset.com/' + icon: 'fas fa-book' + description: '动画插画库' + - name: '配色方案' + icon: 'fas fa-palette' + sites: + - name: 'Coolors' + url: 'https://coolors.co/' + icon: 'fas fa-palette' + description: '在线配色方案生成器' + - name: 'Adobe Color' + url: 'https://color.adobe.com/' + icon: 'fab fa-adobe' + description: 'Adobe配色工具' + - name: 'Paletton' + url: 'https://paletton.com/' + icon: 'fas fa-palette' + description: '配色方案设计工具' + + - name: '开发工具' + icon: 'fas fa-tools' + subcategories: + - name: '编程工具' + icon: 'fas fa-code' + groups: + - name: '代码编辑器' + icon: 'fas fa-code' + sites: + - name: 'Visual Studio Code' + url: 'https://code.visualstudio.com/' + icon: 'fas fa-code' + description: '微软代码编辑器' + - name: 'Sublime Text' + url: 'https://www.sublimetext.com/' + icon: 'fas fa-file-code' + description: '轻量级代码编辑器' + - name: 'WebStorm' + url: 'https://www.jetbrains.com/webstorm/' + icon: 'fab fa-js' + description: 'JetBrains前端IDE' + - name: '版本控制' + icon: 'fas fa-code-branch' + sites: + - name: 'GitHub' + url: 'https://github.com/' + icon: 'fab fa-github' + description: '代码托管平台' + - name: 'GitLab' + url: 'https://gitlab.com/' + icon: 'fab fa-gitlab' + description: 'Git代码管理平台' + - name: 'Bitbucket' + url: 'https://bitbucket.org/' + icon: 'fab fa-bitbucket' + description: 'Atlassian代码托管' + - name: '调试工具' + icon: 'fas fa-bug' + sites: + - name: 'Chrome DevTools' + url: 'https://developers.google.com/web/tools/chrome-devtools' + icon: 'fab fa-chrome' + description: 'Chrome开发者工具' + - name: 'Firefox DevTools' + url: 'https://developer.mozilla.org/en-US/docs/Tools' + icon: 'fab fa-firefox' + description: 'Firefox开发者工具' + - name: 'Postman' + url: 'https://www.postman.com/' + icon: 'fas fa-paper-plane' + description: 'API测试工具' + - name: '部署运维' + icon: 'fas fa-server' + groups: + - name: '云服务' + icon: 'fas fa-cloud' + sites: + - name: 'AWS' + url: 'https://aws.amazon.com/' + icon: 'fab fa-aws' + description: '亚马逊云服务' + - name: 'Azure' + url: 'https://azure.microsoft.com/' + icon: 'fab fa-microsoft' + description: '微软云服务' + - name: 'Google Cloud' + url: 'https://cloud.google.com/' + icon: 'fab fa-google' + description: '谷歌云服务' + - name: '容器化' + icon: 'fas fa-cube' + sites: + - name: 'Docker' + url: 'https://www.docker.com/' + icon: 'fab fa-docker' + description: '容器化平台' + - name: 'Kubernetes' + url: 'https://kubernetes.io/' + icon: 'fas fa-dharmachakra' + description: '容器编排系统' + - name: 'Podman' + url: 'https://podman.io/' + icon: 'fas fa-cube' + description: '无守护进程容器引擎' + - name: 'CI/CD' + icon: 'fas fa-infinity' + sites: + - name: 'GitHub Actions' + url: 'https://github.com/features/actions' + icon: 'fab fa-github' + description: 'GitHub自动化工具' + - name: 'Jenkins' + url: 'https://www.jenkins.io/' + icon: 'fas fa-infinity' + description: '开源自动化服务器' + - name: 'GitLab CI' + url: 'https://docs.gitlab.com/ee/ci/' + icon: 'fab fa-gitlab' + description: 'GitLab CI/CD工具' \ No newline at end of file diff --git a/config/_default/site.yml b/config/_default/site.yml index d822cfd..fc119bb 100644 --- a/config/_default/site.yml +++ b/config/_default/site.yml @@ -69,3 +69,6 @@ navigation: - name: 书签 icon: fas fa-bookmark id: bookmarks + - name: 书签(多层级) + icon: fas fa-bookmark + id: bookmarks-four-level diff --git a/src/bookmark-processor.js b/src/bookmark-processor.js index 1ee33cd..e42853d 100644 --- a/src/bookmark-processor.js +++ b/src/bookmark-processor.js @@ -103,11 +103,10 @@ function getLatestBookmarkFile() { } } -// 解析书签HTML内容 +// 解析书签HTML内容,支持2-4层级嵌套结构 function parseBookmarks(htmlContent) { - // 简单的正则表达式匹配方法解析书签文件 - // 注意:这是一个简化实现,可能不适用于所有浏览器的书签格式 - const folderRegex = /
/gi); + const dlEnd = htmlContent.substring(pos).match(/<\/DL>
/gi);
+
+ if (dlStart && dlStart.index < (dlEnd ? dlEnd.index : htmlContent.length)) {
+ depth++;
+ pos += dlStart.index + dlStart[0].length;
+ } else if (dlEnd) {
+ depth--;
+ pos += dlEnd.index + dlEnd[0].length;
+ } else {
+ break;
+ }
+ }
+
+ folderContentEnd = pos;
+ const folderContent = htmlContent.substring(folderEnd, folderContentEnd);
+
+ // 解析文件夹内容
+ const folder = {
+ name: folderName,
+ icon: 'fas fa-folder',
+ path: [...parentPath, folderName]
+ };
+
+ // 检查是否包含子文件夹
+ const hasSubfolders = folderRegex.test(folderContent);
+ folderRegex.lastIndex = 0;
+
+ if (hasSubfolders && level < 4) {
+ // 递归解析子文件夹
+ const subfolders = parseNestedFolder(folderContent, folder.path, level + 1);
+
+ // 根据层级深度决定数据结构
+ if (level === 1) {
+ folder.subcategories = subfolders;
+ } else if (level === 2) {
+ folder.groups = subfolders;
+ } else if (level === 3) {
+ // 层级3直接解析书签
+ folder.sites = parseSitesInFolder(folderContent);
+ }
+ } else {
+ // 解析书签
+ folder.sites = parseSitesInFolder(folderContent);
+ }
+
+ // 只添加包含内容的文件夹
+ const hasContent = folder.sites && folder.sites.length > 0 ||
+ folder.subcategories && folder.subcategories.length > 0 ||
+ folder.groups && folder.groups.length > 0;
+
+ if (hasContent) {
+ folders.push(folder);
+ }
+ }
+
+ return folders;
}
- // 对每个文件夹,提取其中的书签
- for (let i = 0; i < folderMatches.length; i++) {
- const folder = folderMatches[i];
- const nextFolder = folderMatches[i + 1];
-
- // 确定当前文件夹的内容范围
- const folderContent = nextFolder
- ? htmlContent.substring(folder.end, nextFolder.index)
- : htmlContent.substring(folder.end);
-
- // 从文件夹内容中提取书签
+ // 解析文件夹中的书签
+ function parseSitesInFolder(folderContent) {
const sites = [];
let bookmarkMatch;
- bookmarkRegex.lastIndex = 0; // 重置regex索引
+ bookmarkRegex.lastIndex = 0;
while ((bookmarkMatch = bookmarkRegex.exec(folderContent)) !== null) {
const url = bookmarkMatch[1];
@@ -162,16 +219,12 @@ function parseBookmarks(htmlContent) {
});
}
- // 只添加包含书签的文件夹
- if (sites.length > 0) {
- bookmarks.categories.push({
- name: folder.name,
- icon: 'fas fa-folder', // 默认使用文件夹图标
- sites: sites
- });
- }
+ return sites;
}
+ // 开始解析
+ bookmarks.categories = parseNestedFolder(htmlContent);
+
return bookmarks;
}
diff --git a/src/generator.js b/src/generator.js
index 930a147..c6689b6 100644
--- a/src/generator.js
+++ b/src/generator.js
@@ -196,7 +196,21 @@ function safeLoadYamlConfig(filePath) {
try {
const fileContent = fs.readFileSync(filePath, 'utf8');
- return yaml.load(fileContent);
+ // 使用 loadAll 而不是 load 来支持多文档 YAML 文件
+ const docs = yaml.loadAll(fileContent);
+
+ // 如果只有一个文档,直接返回
+ if (docs.length === 1) {
+ return docs[0];
+ }
+
+ // 如果有多个文档,返回第一个文档(忽略后面的文档)
+ if (docs.length > 1) {
+ console.warn(`Warning: Multiple documents found in ${filePath}. Using the first document only.`);
+ return docs[0];
+ }
+
+ return null;
} catch (error) {
handleConfigLoadError(filePath, error);
return null;
diff --git a/src/helpers/utils.js b/src/helpers/utils.js
index 0bd9128..47ca280 100644
--- a/src/helpers/utils.js
+++ b/src/helpers/utils.js
@@ -181,6 +181,19 @@ function encodeURIComponentHelper(text) {
}
}
+/**
+ * 数学加法运算助手函数
+ * @param {number} a 第一个数
+ * @param {number} b 第二个数
+ * @returns {number} 两数之和
+ * @example {{add level 1}}
+ */
+function add(a, b) {
+ const numA = parseInt(a, 10) || 0;
+ const numB = parseInt(b, 10) || 0;
+ return numA + numB;
+}
+
// 导出所有工具类助手函数
module.exports = {
slice,
@@ -191,5 +204,6 @@ module.exports = {
range,
pick,
keys,
- encodeURIComponent: encodeURIComponentHelper
+ encodeURIComponent: encodeURIComponentHelper,
+ add
};
diff --git a/src/script.js b/src/script.js
index 7be3d4c..0ae6af9 100644
--- a/src/script.js
+++ b/src/script.js
@@ -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());
diff --git a/templates/components/category.hbs b/templates/components/category.hbs
index c624cfd..97e305d 100644
--- a/templates/components/category.hbs
+++ b/templates/components/category.hbs
@@ -1,12 +1,53 @@
- 暂无网站 暂无网站 暂无内容 暂无网站 暂无网站 {{name}}
-