前言
之前写过一篇『Next 主题添加说说』,当时苦于 copy 不来洪哥的说说瀑布流样式,尝试一周未果,遂草草写了个结构,能够显示发布时间、外部链接和图片,但没有设计图片的排版样式,导致电脑上看一些图片很大,再加上样式本身也没什么特点,看久了就腻了。这两天突然兴起,又想再去试试,结果还是弄不来瀑布流,于是把方向转到其他有特点的设计上,正巧打开了微信,看到朋友的动态,灵光一闪,为何不模仿微信朋友圈样式呢?在 chatgpt 的帮助下捣鼓了一下午,于是有了这篇文章。
教程
创建 hexo/themes/next/layout/essay.njk
文件并写入以下内容
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
| {##################} {### ESSAY BLOCK ###} {##################}
<style> body { background: #f3f3f3; } .post-header { display: none; } .post-block:first-of-type { padding-top: 60px; } @media (max-width: 767px) { .post-block:first-of-type { padding-top: 40px; } } #waline blockquote { background: #fff; } .cover { position: relative; max-width: 800px; margin: 0 auto; } .cover img { width: 100%; height: 200px; object-fit: cover; } .profile { position: absolute; bottom: 10px; right: 20px; display: flex; align-items: center; gap: 10px; } .profile span { color: #fff; font-weight: bold; font-size: 16px; } .profile img { width: 50px; height: 50px; border-radius: 5px; } .moments { max-width: 800px; margin: 20px auto; } .card { background: #fff; border-radius: 12px; padding: 20px 25px; margin-bottom: 15px; } .card-header { display: flex; align-items: flex-start; gap: 10px; } .card-header img { width: 50px; height: 50px; border-radius: 5px; } .card-content { flex: 1; } .card-content .name { font-weight: bold; color: #333; } .card-content p { margin: 6px 0; color: #444; } .images-container { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; margin-top: 6px; } .thumb { width: 100%; aspect-ratio: 1 / 1; overflow: hidden; border-radius: 8px; } .thumb img { width: 100%; height: 100%; object-fit: cover; } .card-footer { display: flex; justify-content: space-between; margin-top: 8px; font-size: 14px; color: #777; } .card-footer button { background: none; border: none; color: #555; cursor: pointer; } .card-footer button:hover { color: #007aff; } .post-body *, .post-body *::before, .post-body *::after { box-sizing: border-box; } </style>
<!-- 顶部背景 --> {% set essay = site.data.essay %} <div class="cover"> <img src="{{ essay.profile.cover }}" style="border-radius: 12px;"> <a href="/about"><div class="profile" style="border: none;"> <span>{{ config.author }}</span> <img src="{{ theme.avatar.url }}" alt="头像" class="nofancybox" style="margin-bottom: 0;"> </div></a> </div>
<!-- 动态列表 --> <div class="moments"> {% for moment in essay.moments %} <div class="card"> <div class="card-header"> <a href="/about" style="border: none;"><img src="{{ theme.avatar.url }}" alt="头像" class="nofancybox" style="margin-bottom: 0;"></a> <div class="card-content"> <a href="/about"><div class="name">{{ config.author }}</div></a> <p>{{ moment.text }}</p> <div class="images-container"> {% for img in moment.images %} <div class="thumb"> <img src="{{ img }}"> </div> {% endfor %} </div> <div class="card-footer"> <time class="datetime" datetime="{{ date(moment.date, "YYYY/MM/DD") }}">{{ date(moment.date, "YYYY/MM/DD") }}</time> <div> <button class="essay-comment-btn" data-moment-text="{{ moment.text }}"><i class="fa-solid fa-message"></i></button> </div> </div> </div> </div> </div> {% endfor %} </div>
<script src="{{ url_for('/js/essay.js') }}" data-pjax></script>
{######################} {### END ESSAY BLOCK ###} {######################}
|
修改 hexo/themes/next/layout/page.njk
,添加 + 后的内容(不含 +)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| {%- if page.type === 'categories' and not page.title %} {{- __('title.category') + page_title_suffix }} {%- elif page.type === 'tags' and not page.title %} {{- __('title.tag') + page_title_suffix }} {%- elif page.type === 'schedule' and not page.title %} {{- __('title.schedule') + page_title_suffix }} +{%- elif page.type === 'essay' and not page.title %} + {{- __('title.essay') + page_title_suffix }} {%- else %}
...
{%- if page.type === 'tags' %} {%- include '_partials/page/tags.njk' -%} {% elif page.type === 'categories' %} {%- include '_partials/page/categories.njk' -%} {% elif page.type === 'schedule' %} {%- include '_partials/page/schedule.njk' -%} +{% elif page.type === 'essay' %} + {%- include 'essay.njk' -%} {% else %}
|
以下为方便复制的版本,请分别复制并粘贴到对应位置
1 2 3 4 5 6 7
| {%- elif page.type === 'essay' and not page.title %} {{- __('title.essay') + page_title_suffix }}
...
{% elif page.type === 'essay' %} {%- include 'essay.njk' -%}
|
创建 hexo/source/_data/essay.yml
并粘贴以下内容
1 2 3 4 5 6 7 8 9 10 11 12 13
| profile: cover: /images/essay-cover.webp
moments: - text: 最新说说 date: 2025-09-06
- text: 之前的说说 date: 2025-09-01 images: - /images/example1.webp - /images/example2.webp - /images/example3.webp
|
简单解释下参数
参数 | 说明 |
---|
cover | 头图,可填相对路径或绝对路径 |
text | 说说内容 |
date | 发布日期,格式:年-月-日,月日请采用两位数,比如「2025-09-06」而不是「2025-9-6」 |
images | 【可选】插入文章的图片,支持多张图片 |
发布新说说在 moments
下添加相同结构即可,注意缩进!
文件中说说的先后顺序与渲染出来的结果是一致的,即越写在上面的,在渲染结果中也就越排在上方。
创建 hexo/source/js/essay.js
并填入以下内容
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
| (function() { function formatRelativeTime(dateStr) { const now = new Date(); const past = new Date(dateStr); const diff = (now - past) / 1000;
if (diff < 60) return `${Math.floor(diff)}秒前`; if (diff < 3600) return `${Math.floor(diff / 60)}分钟前`; if (diff < 86400) return `${Math.floor(diff / 3600)}小时前`; if (diff < 604800) return `${Math.floor(diff / 86400)}天前`;
const year = past.getFullYear(); const month = past.getMonth() + 1; const day = past.getDate(); if (year !== now.getFullYear()) { return `${year}年${month}月${day}日`; } return `${month}月${day}日`; }
function updateRelativeTime(container = document) { container.querySelectorAll('.datetime').forEach(el => { const timeStr = el.getAttribute('datetime'); if (timeStr) el.textContent = formatRelativeTime(timeStr); }); }
function getWalineTextarea() { return document.querySelector('#waline textarea') || document.querySelector('#waline input[type="text"]'); }
function bindCommentButtons(container = document) { container.querySelectorAll('.essay-comment-btn').forEach(btn => { btn.addEventListener('click', () => { const momentText = btn.getAttribute('data-moment-text'); const textarea = getWalineTextarea(); if (!textarea) return;
textarea.scrollIntoView({ behavior: 'smooth', block: 'center' });
textarea.value = `> ${momentText}\n\n`; textarea.focus(); }); }); }
function init(container = document) { updateRelativeTime(container); bindCommentButtons(container); }
init(document);
document.addEventListener('pjax:success', () => { init(document); }); })();
|
运行 hexo new page "essay"
创建 hexo/source/essay/index.md
文件,设置标题日期,type 要设置为 "essay"
1 2 3
| title: 说说 date: 2025-09-06 11:18:28 type: "essay"
|
打开 _config.next.yml
,在 menu
中添加
1 2
| menu: essay: /essay/ || fas fa-pen
|
打开 hexo/themes/next/languages/zh-CN.yml
,在 menu
中添加
大功告成!
需要注意的是,如果要使用评论功能,必须安装并使用 waline,其他评论系统请自行修改 essay.js
中的选择器。
效果展示