fix(generator): 修复自定义页面配置不生效问题

- src/generator.js:
    - 修改 `generateHTML` 和 `processTemplate` 函数,根据 `navigation` 配置动态生成所有页面 HTML,不再硬编码特定页面。
    - 修改 `generateNavigation` 函数,添加通用子菜单处理逻辑,支持为任意包含 `categories` 的页面生成子菜单。
- templates/index.html:
    - 移除硬编码的页面 `div` 结构。
    - 使用 `{{ALL_PAGES}}` 占位符动态注入所有页面内容。
- README.md:
    - 更新文档配置说明

Closes #6
This commit is contained in:
Zuoling Rong
2025-05-04 20:23:23 +08:00
parent dc8f97439e
commit 297890315b
4 changed files with 312 additions and 157 deletions

20
.gitignore vendored
View File

@@ -25,4 +25,22 @@ test-bookmarks.ps1
test-bookmarks.sh
# 个人笔记
note.md
docs/*
# 自定义配置文件
config/user/*
config.user.yml
bookmarks.user.yml
# 测试相关文件
jest.config.js
tests/
coverage/
.nyc_output/
jest-*.json
jest.results.json
.eslintcache
tests/screenshots/
tests/fixtures/
*.test.js
*.spec.js

302
README.md
View File

@@ -393,131 +393,229 @@ MeNav支持两种配置方式双文件配置和模块化配置推荐
<details>
<summary>点击展开</summary>
以下是配置文件各部分的详细说明,适用于双文件配置和模块化配置两种方式。
以下是配置文件的详细说明,帮助您全面了解和定制您的个人导航站。本指南适用于双文件配置和模块化配置两种方式。
#### 配置文件结构
#### 1. 基础网站配置 (site)
配置文件使用YAML格式包含多个关键部分
网站的基本信息配置,这些设置会影响整个网站的呈现
```yaml
# 网站基本信息
site:
title: 网站标题
description: 网站描述
author: 作者名
favicon: favicon.ico # 网站图标,支持ico、png等格式
title: "我的导航站" # 网站标题,显示在浏览器标签和页面顶部
description: "个人网址导航" # 网站描述用于SEO和分享卡片
author: "张三" # 作者
favicon: "favicon.ico" # 网站图标支持ico、png等格式
```
# 字体设置
#### 2. 字体设置 (fonts)
MeNav支持自定义字体您可以分别为标题、副标题和正文设置不同的字体
```yaml
fonts:
title: # 标题字体
family: 字体名称
weight: 字重值
source: 字体来源
title: # 标题字体设置
family: "Roboto" # 字体名称
weight: 700 # 字重值400常规、500中等、700粗体
source: "google" # 字体来源google或system
subtitle: # 副标题字体
family: 字体名称
weight: 字重值
source: 字体来源
family: "Noto Sans SC"
weight: 500
source: "google"
body: # 正文字体
family: 字体名称
weight: 字重值
source: 字体来源
# 个人信息
profile:
title: 欢迎语
subtitle: 副标题
description: 描述
# 导航菜单
navigation:
- name: 菜单名称
icon: 图标类名
id: 页面ID
active: 是否激活
# 类别
categories:
- name: 分类名称
icon: 分类图标
sites:
- name: 网站名称
url: 网站地址
icon: 网站图标
description: 网站描述
# 更多配置...
```
#### 添加网站链接
在配置文件中的 categories 部分添加新站点:
```yaml
categories:
- name: 分类名称
icon: 分类图标
sites:
- name: 网站名称
url: 网站地址
icon: 网站图标
description: 网站描述
```
#### 设置网站字体
字体配置有三个关键属性:
- `family`: 字体名称支持Web安全字体或Google Fonts
- `weight`: 字体粗细常用值如400(常规)、500(中等)、600(粗体)等
- `source`: 字体来源,可选"google"或"system"
例如使用Google字体:
```yaml
fonts:
body:
family: "Noto Sans SC" # Google提供的中文字体
family: "Noto Sans SC"
weight: 400
source: "google"
```
#### 设置网站图标
> **📝 温馨提示**
> - **system**表示使用系统自带字体,无需额外加载,页面加载速度更快
> - **google**表示从Google Fonts加载字体选择更丰富但可能影响加载速度
> - 中文网站推荐使用"Noto Sans SC"、"Source Han Sans CN"等支持中文的字体
> - 设置字重时请确保所选字体支持该字重值,否则可能无法正确显示
网站图标设置提示:
#### 3. 个人资料配置 (profile)
- 支持.ico、.png等格式
- 建议尺寸为32x32或16x16像素
- 将图标文件放在assets目录下
- 在配置文件中设置:`favicon: favicon.ico`
设置首页顶部的个人欢迎区域:
```yaml
profile:
title: "欢迎来到我的导航站" # 主标题/欢迎语
subtitle: "收集实用网站和工具" # 副标题
description: "这里整理了我日常使用的网站和工具,方便快速访问。" # 详细描述
```
> **📝 温馨提示**description支持较长文本可以添加一些个性化的介绍或使用说明让您的导航站更具特色。
#### 4. 导航菜单配置 (navigation)
设置网站左侧的导航菜单,支持多个自定义页面:
```yaml
navigation:
- name: "首页" # 菜单项名称
icon: "fas fa-home" # 菜单项图标
id: "home" # 页面标识符(必须唯一)
active: true # 是否默认激活只能有一个为true
- name: "项目"
icon: "fas fa-project-diagram"
id: "projects"
active: false
# 更多导航项...
```
> **📝 温馨提示**
> - 每个导航项的`id`必须唯一,并且有对应的页面配置(使用模块化配置时,每个导航项的`id`必须与`pages/`文件夹中的页面配置文件名一致)
> - 只能设置一个导航项的`active`为`true`,作为默认显示页面
> - 图标使用Font Awesome 5图标库格式为`前缀 fa-图标名`
#### 5. 分类和网站配置 (categories)
这是MeNav的核心配置用于定义网站分类和链接
#### 使用Font Awesome图标
MeNav使用Font Awesome图标库为网站分类和链接提供图标
- **图标类名**使用Font Awesome 5的图标类名例如`fas fa-home``fab fa-github`
- **查找图标**:可在[Font Awesome官网](https://fontawesome.com/icons)浏览和搜索图标
- **常用图标前缀**
- `fas`:实心风格图标 (Font Awesome Solid)
- `far`:线框风格图标 (Font Awesome Regular)
- `fab`:品牌图标 (Font Awesome Brands)
示例:
```yaml
# 分类图标
categories:
- name: "常用工具"
icon: "fas fa-tools"
sites:
- name: "GitHub"
url: "https://github.com"
icon: "fab fa-github"
description: "全球最大的代码托管平台"
- name: "谷歌搜索"
- name: "常用工具" # 分类名称
icon: "fas fa-tools" # 分类图标
sites: # 该分类下的网站列表
- name: "GitHub" # 网站名称
url: "https://github.com" # 网站链接
icon: "fab fa-github" # 网站图标
description: "全球最大的代码托管平台" # 网站描述
- name: "Google"
url: "https://google.com"
icon: "fab fa-google"
description: "全球最大的搜索引擎"
# 更多网站...
# 更多分类...
```
**提示**书签导入功能会根据网站URL自动匹配适合的Font Awesome图标。如需自定义可在导入后编辑配置文件修改图标类名。
> **📝 温馨提示**
> - 每个页面可以拥有不同的分类和网站
> - 网站描述建议简洁明了不超过30个字符以确保显示美观
> - 合理组织分类和网站顺序,将常用网站放在前面,提升使用体验
#### 6. Font Awesome图标指南
MeNav使用Font Awesome 5图标库提供了丰富的图标选择
**常用图标前缀**
- `fas` - Font Awesome Solid (实心风格,最常用)
- `far` - Font Awesome Regular (线框风格)
- `fab` - Font Awesome Brands (品牌图标,用于各类网站品牌)
**如何选择合适的图标**
1. 访问[Font Awesome官网](https://fontawesome.com/icons)搜索图标
2. 复制图标名称,加上前缀使用
3. 例如:`fas fa-book``fab fa-youtube`
**网站图标匹配建议**
- 对于知名网站和平台,优先使用`fab`前缀的品牌图标
- 对于通用功能网站,选择能代表其功能的图标,如词典类用`fas fa-book`
- 对于工具类网站,可使用`fas fa-tools`或更具体的工具图标
#### 7. 自定义页面配置
MeNav支持创建任意数量的自定义页面每个页面可以有不同内容
**第一步:在导航配置中添加页面入口**
```yaml
# 在navigation.yml或config.user.yml的navigation部分添加
- name: "笔记"
icon: "fas fa-sticky-note"
id: "notes"
active: false
```
**第二步:创建页面内容配置**
模块化配置方式(在`config/user/pages/notes.yml`
```yaml
title: "我的笔记收藏" # 页面标题
subtitle: "学习和工作笔记资源" # 页面副标题
categories: # 该页面的分类和网站
- name: "编程笔记"
icon: "fas fa-code"
sites:
- name: "Python学习笔记"
url: "https://example.com/python-notes"
icon: "fab fa-python"
description: "Python编程技巧和案例"
# 更多网站...
```
双文件配置方式(在`config.user.yml`中):
```yaml
notes: # 与navigation中的id保持一致
title: "我的笔记收藏"
subtitle: "学习和工作笔记资源"
categories:
# 与上面模块化配置内容相同
```
> **📝 温馨提示**
> - 页面标识符(`id`)在导航和页面配置中必须保持一致
> - 每个页面支持独立的标题、副标题和分类设置
> - 不同页面可以特化用于不同用途,如"工作"、"学习"、"娱乐"等
> - 页面数量不限,但建议控制在合理范围内,避免导航过长
#### 8. 配置文件结构示例
**模块化配置示例**(推荐):
```
config/user/
├── site.yml # 包含site、fonts、profile配置
├── navigation.yml # 导航菜单配置
└── pages/
├── home.yml # 首页配置
├── projects.yml # 项目页配置
└── notes.yml # 自定义笔记页配置
```
**双文件配置示例**
```yaml
# config.user.yml
site:
# 网站基本信息...
fonts:
# 字体设置...
profile:
# 个人信息...
navigation:
# 导航菜单...
home:
# 首页设置...
projects:
# 项目页设置...
notes:
# 自定义笔记页设置...
```
#### 9. 常见配置问题解决
1. **配置后不生效**
- 检查YAML格式是否正确缩进是否一致
- 确认修改的是用户配置文件(`config.user.yml``config/user/`目录),而非默认配置文件
- 运行`npm run dev`重新构建网站并查看
2. **图标不显示**
- 确认图标名称和前缀是否正确(如`fas fa-home`
- 检查Font Awesome是否支持该图标在官网搜索确认
- 注意图标名称中的连字符,如`fa-user-circle`而非`fa-usercircle`
3. **字体加载问题**
- 确认Google字体名称拼写正确
- 使用system来源时确保使用的是通用系统字体
- 中文字体推荐使用Noto Sans SC、Source Han Sans等支持中文的Google字体
4. **配置文件优先级问题**
- 记住系统只会使用最高优先级的配置,不会合并不同配置
- 检查您是否在多个地方定义了相同的配置,造成覆盖
> - 首次设置时,建议先复制一份完整默认配置,然后逐步修改
</details>

View File

@@ -302,6 +302,18 @@ function generateNavigation(navigation, config) {
`).join('')}
</div>`;
}
// 通用处理:任意自定义页面的子菜单生成
else if (config[nav.id] && config[nav.id].categories && Array.isArray(config[nav.id].categories)) {
submenuItems = `
<div class="submenu">
${config[nav.id].categories.map(category => `
<a href="#${category.name}" class="submenu-item" data-page="${nav.id}" data-category="${category.name}">
<i class="${escapeHtml(category.icon)}"></i>
<span>${escapeHtml(category.name)}</span>
</a>
`).join('')}
</div>`;
}
return `
<div class="nav-item-wrapper">
@@ -491,32 +503,38 @@ function generateHTML(config) {
// 首页内容
pageContents.home = generateHomeContent(config);
// 如果配置了项目页面
if (config.projects) {
pageContents.projects = generatePageContent('projects', config.projects);
// 动态生成所有其他页面的内容
if (config.navigation && Array.isArray(config.navigation)) {
config.navigation.forEach(navItem => {
const pageId = navItem.id;
// 跳过已处理的首页和搜索结果页
if (pageId === 'home' || pageId === 'search-results') {
return;
}
// 如果配置中存在该页面的配置,则生成页面内容
if (config[pageId]) {
pageContents[pageId] = generatePageContent(pageId, config[pageId]);
}
});
}
// 如果配置了文章页面
if (config.articles) {
pageContents.articles = generatePageContent('articles', config.articles);
}
// 生成首页HTML
const homeHTML = `
<!-- home页 -->
<div class="page active" id="home">
${pageContents.home}
</div>`;
// 如果配置了友链页面
if (config.friends) {
pageContents.friends = generatePageContent('friends', config.friends);
}
// 如果配置了书签页面
if (config.bookmarks) {
pageContents.bookmarks = generatePageContent('bookmarks', config.bookmarks);
}
// 生成所有页面的HTML
const pagesHTML = Object.entries(pageContents).map(([id, content]) => `
// 生成其他页面的HTML
const dynamicPagesHTML = Object.entries(pageContents)
.filter(([id]) => id !== 'home') // 排除首页
.map(([id, content]) => `
<!-- ${id}页 -->
<div class="page${id === 'home' ? ' active' : ''}" id="${id}">
<div class="page" id="${id}">
${content}
</div>`).join('\n');
</div>`)
.join('\n');
// 生成搜索结果页面
const searchResultsHTML = generateSearchResultsPage(config);
@@ -586,7 +604,8 @@ ${generateSocialLinks(config.social)}
</div>
</div>
${pagesHTML}
${homeHTML}
${dynamicPagesHTML}
<!-- 搜索结果页 -->
<div class="page" id="search-results">
@@ -651,21 +670,55 @@ function processTemplate(template, config) {
const googleFontsLink = generateGoogleFontsLink(config);
const fontVariables = generateFontVariables(config);
// 生成所有页面的HTML
let allPagesHTML = '';
// 确保按照导航顺序生成页面
if (config.navigation && Array.isArray(config.navigation)) {
// 按照导航中的顺序生成页面
config.navigation.forEach(navItem => {
const pageId = navItem.id;
// 跳过搜索结果页
if (pageId === 'search-results') {
return;
}
let pageContent = '';
let isActive = pageId === 'home' ? ' active' : '';
// 根据页面ID生成对应内容
if (pageId === 'home') {
pageContent = generateHomeContent(config);
} else if (config[pageId]) {
pageContent = generatePageContent(pageId, config[pageId]);
} else {
pageContent = `<div class="welcome-section">
<h2>页面未配置</h2>
<p class="subtitle">请配置 ${pageId} 页面</p>
</div>`;
}
// 添加页面HTML
allPagesHTML += `
<!-- ${pageId}页 -->
<div class="page${isActive}" id="${pageId}">
${pageContent}
</div>`;
});
}
// 创建替换映射
const replacements = {
'{{SITE_TITLE}}': escapeHtml(config.site.title),
'{{SITE_LOGO_TEXT}}': escapeHtml(config.site.logo_text || '导航站'), // 从配置中获取,如果不存在则使用默认值
'{{SITE_LOGO_TEXT}}': escapeHtml(config.site.logo_text || '导航站'),
'{{GOOGLE_FONTS}}': googleFontsLink,
'{{{FONT_VARIABLES}}}': fontVariables,
'{{NAVIGATION}}': generateNavigation(config.navigation, config),
'{{SOCIAL_LINKS}}': generateSocialLinks(config.social),
'{{CURRENT_YEAR}}': currentYear,
'{{HOME_CONTENT}}': generateHomeContent(config),
'{{PROJECTS_CONTENT}}': generatePageContent('projects', config.projects),
'{{ARTICLES_CONTENT}}': generatePageContent('articles', config.articles),
'{{FRIENDS_CONTENT}}': generatePageContent('friends', config.friends),
'{{BOOKMARKS_CONTENT}}': generatePageContent('bookmarks', config.bookmarks),
'{{SEARCH_RESULTS}}': generateSearchResultsPage(config)
'{{SEARCH_RESULTS}}': generateSearchResultsPage(config),
'{{ALL_PAGES}}': allPagesHTML
};
// 执行替换
@@ -731,4 +784,13 @@ function main() {
}
}
main();
main();
// 导出供测试使用的函数
module.exports = {
loadConfig,
generateHTML,
copyStaticFiles,
generateNavigation,
generateCategories
};

View File

@@ -86,30 +86,7 @@
</div>
</div>
<!-- 首页 -->
<div class="page active" id="home">
{{HOME_CONTENT}}
</div>
<!-- 项目页 -->
<div class="page" id="projects">
{{PROJECTS_CONTENT}}
</div>
<!-- 文章页 -->
<div class="page" id="articles">
{{ARTICLES_CONTENT}}
</div>
<!-- 朋友页 -->
<div class="page" id="friends">
{{FRIENDS_CONTENT}}
</div>
<!-- 书签页 -->
<div class="page" id="bookmarks">
{{BOOKMARKS_CONTENT}}
</div>
{{ALL_PAGES}}
{{SEARCH_RESULTS}}
</main>