注意版本 :框架 Hexo 7.3.0 |主题 Butterfly 5.4.3
需求 :构建一个文学列表栏目,根据不同类别(三行诗集、诗词歌赋等),每个栏目可以有多个作品展示(类似首页,每页展示10条,数据为标题、时间、描述、作者、图片等)。
具体效果可参照我的博客: ”文学 - 诗词歌赋“
注意事项
Front-matter 必须严格格式(md文件):
1 2 3 4 5 --- title: 春日偶得 type: poems date: 2025 -04 -01 ---
对于脚本的放置位置 :
放在项目根 scripts(推荐用于全局逻辑)
放在主题 scripts(推荐用于主题专属逻辑)
我是放在主题butterfly之下 themes/butterfly/scripts/custom/
对于模板文件的配置 :
三个地方必须完全一致:
Front-matter: layout: custom/literature
Generator: layout: ['custom/literature']
文件路径: layout/custom/literature.pug
如果没有加载到模板,会自动用 archive 模板(时间轴形式)
对于路径的配置 :
其他文件异常 :
1 2 3 if theme.post_meta .post .categories && page.categories && page.categories .data && page.categories .data .length > 0
项目结构 :
hexo-blog/
├── source/
│ ├── _posts/文学/
│ │ ├── 春日偶得.md ← 包含 type: three-line-poem
│ │ └── 夜读有感.md
│ └── literature/
│ └──poems/
│ └── index.md ← layout: custom/literature
├── themes/
│ └── butterfly/
│ ├── layout/custom/
│ │ └── literature.pug
│ ├── scripts/custom/
│ │ └──poems.js ← layout: custom/literature
│ ├── source/css/custom/
│ │ └──literature.css
└── _config.yml
前提条件
你已安装 Hexo 并创建了博客项目。
已安装 hexo-pagination 插件(用于分页逻辑):
1 npm install hexo-pagination --save
使用 Pug 作为模板引擎(默认是 EJS,需配置):
1 npm install hexo-renderer-pug --save
在 _config.yml 中设置主题:
创建自定义页面
注意 :框架会自动创建该文件。你也可以自己手动创建,指定相关信息。
创建页面文件(列表):在 source/literature/poems/index.md:
1 2 3 4 5 6 7 --- title: 三行诗集 layout: custom/literature pagination: true per_page: 5 type: three-line-poem ---
准备文章数据
source/_posts/文学/春日偶得.md
1 2 3 4 5 6 7 8 9 10 11 --- title: 春日偶得 date: 2025 -04 -01 categories: [文学, 诗集] tags: [三行情诗, 春日偶得] type: three-line-poem --- 春风拂面不须酒, 落花随步即成诗。 心静自然天地宽。
source/_posts/文学/夜读有感.md
1 2 3 4 5 6 7 8 9 10 11 --- title: 夜读有感 date: 2025 -04 -02 categories: [文学, 诗集] tags: [三行情诗, 夜读有感] type: three-line-poem --- 灯下翻书夜已深, 字里行间见古人。 合卷方知月照襟。
所有你想在 /literature/poems 页面展示的诗,都加上 type: three-line-poem。
路径可以随意,如: source/_posts/test/test.md
创建分页模板(pug)
在主题目录( themes/butterfly/layout/)下创建模板(模板一定要与 hexo-pagination 版本对应,否则会出错,目前是4.0.0)
themes/butterfly/layout/custom/literature.pug
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 extends ../includes/layout.pug block content style. @import url('/css/custom/literature.css'); .poetry-page .container .poetry-header h1= page.title || '诗集' - var poemPosts = page.posts || []; if poemPosts.length === 0 .no-poems p 📖 暂无诗歌,敬请期待... else .poem-container each post in poemPosts .poem-card .card-header h3.poem-title= post.title if post.author p.poem-author 【#{post.author}】 .poem-meta if post.date && post.address span #{new Date(post.date).toLocaleDateString('zh-CN', { year: 'numeric', month: 'long', day: 'numeric' })} • #{post.address} else if post.date span #{new Date(post.date).toLocaleDateString('zh-CN', { year: 'numeric', month: 'long', day: 'numeric' })} else if post.address span #{post.address} if post.description p.poem-description= post.description .poem-body .poem-content - var contentText = post.content || ''; var cleanText = contentText.replace(/<[^>]*>/g, '').replace(/\s*\n\s*/g, '\n').trim(); var lines = cleanText.split('\n').filter(line => line.trim() !== ''); if lines.length > 0 each line, i in lines - var cleanLine = line.trim().replace(/^(《[^》]*》\s*)/, ''); if cleanLine p.poem-line= cleanLine else p.poem-line(style='text-align: center; color: #999;') 无诗句内容 .card-footer a.read-more(href=url_for(post.path)) 📖 阅读全文 if page.total > 1 .pagination-wrap .pagination-title | 第 #{page.current} 页,共 #{page.total} 页 .pagination if page.prev && page.prev > 0 a.page-link.prev(href=url_for(page.prev_link)) span.page-nav « 上一页 else span.page-link.disabled span.page-nav « 上一页 - for (let i = 1; i <= page.total; i++) if i === page.current span.page-link.current= i else - var pageLink = i === 1 ? page.base : page.base + 'page/' + i; a.page-link(href=url_for(pageLink))= i if page.next && page.next > 0 a.page-link.next(href=url_for(page.next_link)) span.page-nav 下一页 » else span.page-link.disabled span.page-nav 下一页 »
如果你希望更精确控制“诗句预览”,可以在生成器或模板中预处理。
每首诗显示为一张精美卡片:
┌──────────────────────────────────────┐
│ 《春日偶得》 │ ← 渐变标题栏
│ 余一叶知秋尽 │
│ 己亥年正月 • 老家 ├
│ 春日散步偶得小诗,心随景动。 │ ← 描述(斜体灰色)
│ │
│ 春风拂面不须酒, │ ← 第一行(橙色)
│ 落花随步即成诗。 │ ← 第二行(蓝色)
│ 心静自然天地宽。 │ ← 第三行(绿色加粗)
├──────────────────────────────────────┤
│ 📖 阅读全文 │ ← 右下角按钮
└──────────────────────────────────────┘
网格自适应(PC 3列,平板 2列,手机 1列)
鼠标悬停轻微上浮 + 阴影加深
点击标题或“阅读全文”进入详情页
分页导航 + 页码跳转
如果显示:未提取到诗句内容
可能问题:你的Generator 与 模板中的数据不对应,获取数据的属性不同,或者缺少文章
添加样式(CSS)
在主题目录( themes/butterfly/source/)下创建样式 themes\butterfly\source\css\custom\literature.css
你可以根据自己的需求,随意更改样式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 .poetry-page { padding : 2rem 0 ; background : #f8f9fa ; } .poetry-header { text-align : center; margin : 2rem 0 ; padding : 1.5rem ; } .poetry-header h1 { font-size : 2.2rem ; color : #2c3e50 ; margin : 0 ; font-weight : 600 ; } .poem-grid { display : grid; grid-template-columns : repeat (2 , 1 fr); gap : 2rem ; padding : 0 2rem ; max-width : 1200px ; margin : 0 auto; } @media (max-width : 768px ) { .poem-grid { grid-template-columns : 1 fr; padding : 0 1rem ; } } .poem-card { background : white; border-radius : 12px ; box-shadow : 0 4px 12px rgba (0 , 0 , 0 , 0.05 ); overflow : hidden; transition : all 0.3s ease; border : 1px solid #eee ; } .poem-card :hover { box-shadow : 0 8px 24px rgba (0 , 0 , 0 , 0.1 ); transform : translateY (-2px ); } .card-header { padding : 1.5rem 1.5rem 1rem 1.5rem ; border-bottom : 1px solid #f0f0f0 ; } .poem-title { font-size : 1.3rem ; font-weight : 600 ; margin : 0 0 0.5rem 0 ; color : #2c3e50 ; line-height : 1.4 ; } .poem-meta { display : flex; flex-wrap : wrap; gap : 1rem ; font-size : 0.85rem ; color : #7f8c8d ; } .poem-body { padding : 1.5rem ; } .poem-content { margin : 1rem 0 ; } .poem-line { text-align : center; font-size : 1.1rem ; line-height : 1.8 ; margin : 0.4rem 0 ; padding : 0.3rem 0 ; font-family : "STKaiti" , "KaiTi" , serif; } .poem-line :nth-child (1 ) { color : #e74c3c ; } .poem-line :nth-child (2 ) { color : #3498db ; } .poem-line :nth-child (3 ) { color : #27ae60 ; font-weight : 500 ; } .card-footer { padding : 1rem 1.5rem ; background : #f8f9fa ; text-align : right; border-top : 1px solid #eee ; } .read-more { color : #5d8aa8 ; text-decoration : none; font-size : 0.9rem ; font-weight : 500 ; } .read-more :hover { color : #2c5aa0 ; text-decoration : underline; } .no-poems { text-align : center; padding : 3rem 2rem ; color : #666 ; font-size : 1.1rem ; }
然后在模板中引入即可: @import url('/css/custom/literature.css');
添加评论(giscus)
添加 hexo 默认提供的评论样板,在自定义模板下面添加:
1 2 3 4 // giscus 评论系统(从配置中读取参数) if page.comments !== false && theme.comments.use - var commentsJsLoad = true !=partial('../includes/third-party/comments/index', {}, {cache: true})
添加护眼模式(CSS)
在主题目录( themes/butterfly/source/)下创建样式 themes\butterfly\source\css\custom\dark_mode.css
你可以根据自己的需求,随意更改样式。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 .poetry-page { padding : 2.5rem 0 ; background : var (--card-bg); } .poetry-header h1 { color : var (--font-color); } .poem-container { column-count : 2 ; column-gap : 3rem ; padding : 0 4rem ; max-width : 1400px ; margin : 0 auto; } .poem-card { break-inside : avoid; background : var (--card-bg); border-radius : 18px ; box-shadow : var (--card-box-shadow); border : 1px solid var (--line-color); margin-bottom : 2.5rem ; } .poem-card :hover { box-shadow : 0 12px 30px rgba (0 , 0 , 0 , 0.12 ); } .card-header { padding : 2rem ; border-bottom : 1px solid var (--line-color); } .poem-title { font-size : 1.5rem ; font-weight : 600 ; margin : 0 0 1rem 0 ; color : var (--font-color); text-align : center; } .poem-description { font-style : italic; color : var (--font-second-color); margin : 0 0 1.2rem 0 ; padding : 1rem ; background : var (--code-bg); border-radius : 10px ; border-left : 4px solid var (--theme-color); text-align : center; } .poem-author { text-align : center; color : var (--font-color); font-weight : 500 ; margin : 0.8rem 0 0.5rem 0 ; } .poem-meta { text-align : right; color : var (--font-second-color); margin : 0 ; } .poem-line { text-align : center; font-size : 1.2rem ; line-height : 1.8 ; margin : 0.6rem 0 ; color : var (--font-color); } .card-footer { padding : 1.2rem 2rem ; background : var (--card-bg); text-align : right; border-top : 1px solid var (--line-color); } .read-more { color : var (--theme-color); text-decoration : none; font-weight : 500 ; padding : 0.5rem 1.2rem ; border : 1px solid var (--theme-color); border-radius : 25px ; } .read-more :hover { background : var (--theme-color); color : var (--white); } .pagination-wrap { text-align : center; margin : 4rem 0 3rem 0 ; padding : 2.5rem 0 ; background : var (--card-bg); border-top : 1px solid var (--line-color); border-bottom : 1px solid var (--line-color); } .pagination { background : var (--card-bg); box-shadow : var (--card-box-shadow); } .page-link { border : 2px solid var (--line-color); color : var (--font-color); background : var (--card-bg); } .page-link :hover :not (.disabled ):not (.current ) { background : var (--second-bg); border-color : var (--theme-color); color : var (--theme-color); } .page-link .current { background : var (--theme-color); color : var (--white); border-color : var (--theme-color); } .page-link .disabled { color : var (--font-third-color); background : var (--card-bg); border-color : var (--line-color); }
1、在模板中引入该样式: @import url('/css/custom/dark_mode.css');
2、自定义模板必须 正确继承 Butterfly 的 layout ,这样才能获得护眼模式的 CSS 和 JS 支持(extends ../includes/layout.pug)
注入分页数据(Generator)
你需要在主题的脚本或插件中为该页面生成分页器。在主题的 scripts/custom 目录创建 three-line-poem.js
注意 : 此处的 register generators名称 “three-line-poem”,必须保持唯一,否则会与其他冲突而不起效果。
插件的使用 参照 Git 文档 ,配置错误会导致加载不到模板,一直使用默认的。
你可以随意在 processedPosts 中添加任意返回值,然后在pug模板中获取展示。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 const pagination = require ('hexo-pagination' );hexo.extend .generator .register ('three-line-poem' , function (locals ) { const { config } = this ; const { posts } = locals; const poemPosts = posts .sort ('-date' ) .filter (post => post.type === 'three-line-poem' ); const processedPosts = poemPosts.map (post => { return { title : post.title , author : post.author , date : post.date , content : post.content , description : post.description , } }); const basePath = 'literature/poems/' ; const paginated = pagination (basePath, processedPosts, { perPage : config.theme .per_page || 10 , layout : ["custom/literature" ], format : 'page/%d/' , data : { title : "三行情诗" , tag : "三行情诗" , } }); return paginated; });
这个脚本会在生成时为 /literature/poems/ 创建分页,并注入 paginator 对象到模板上下文。
在脚本中加 console.log, 验证脚本是否被加载
在主题配置(themes/butterfly/_config.yml)中添加:per_page: 10,这样你可以统一控制每页数量。
生成并预览
1 2 3 4 5 6 7 8 9 10 # 执行 hexo clean & hexo g & hexo s # 运行生成后,检查输出文件: # public/literature/poems/index.html # public/literature/poems/page/2/index.html hexo g --debug > debug.log 2>&1 # 预览 http://localhost:4000/literature/poems/ http://localhost:4000/literature/poems/page/2/
隐藏文章
如果需要在首页或其他栏目隐藏,可以使用 插件 hexo-hide-posts ,支持在特定标签显示或隐藏指定文章。
详细教程参考 插件Github说明 或 博客
如我的配置,其他地方隐藏,只在 “文学-三行诗集” 栏目显示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 hide_posts: enable: true filter: hidden noindex: false allowlist_generators: ['three-line-poem' ] blocklist_generators: ['index' , 'tag' , 'archive' ]