给主题侧边栏添加最新评论

前言

本文将介绍如何给博客侧边栏添加一个最新评论,展示最新的Twikoo评论。效果可以看本站右侧的最新评论模块。

使用方法

1. 创建JS文件

在博客目录的 source 文件夹下创建 comments.js 文件(也可以在source文件夹下另外新建文件夹)。

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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
(() => {
const injectCSS = () => {
const style = document.createElement('style');
style.textContent = `
.card-latest-comments .item-headline i {
color: var(--anzhiyu-main);
}

.card-latest-comments .headline-right {
position: absolute;
right: 24px;
top: 20px;
transition: all 0.3s;
opacity: 0.6;
}

.card-latest-comments .headline-right:hover {
color: var(--anzhiyu-main);
opacity: 1;
transform: rotate(90deg);
}

.aside-list-author {
display: flex;
align-items: center;
font-weight: bold;
height: 22px;
gap: 5px;
}

.aside-list-date {
font-size: 0.7rem;
font-weight: normal;
margin-left: auto;
}

.aside-list-content {
font-size: 0.9rem;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
text-decoration: none;
line-height: 1.2;
}

.aside-list-item:last-child {
margin-bottom: 0!important;
}

[data-theme='dark'] .aside-list-item-right {
filter: brightness(0.95);
}
`;
document.head.appendChild(style);
};

const LatestComments = {
API_URL: 'https://twikoo.ruom.top',
ADMIN_EMAIL_MD5: 'f2c9c64c90a00afeed5ba410e5447a0d01aa294874bd662032a27c5385bcde1c',
PAGE_SIZE: 5,
LOADING_GIF: 'https://lib.bsgun.cn/Hexo-static/img/loading.gif',

async fetchComments() {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 5000);

try {
const response = await fetch(this.API_URL, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
event: 'GET_RECENT_COMMENTS',
includeReply: true,
pageSize: this.PAGE_SIZE
}),
signal: controller.signal
});

const { data } = await response.json();
return data;
} catch (error) {
console.error('获取评论出错:', error);
return null;
} finally {
clearTimeout(timeoutId);
}
},

formatTimeAgo(timestamp) {
const diff = Math.floor((Date.now() - new Date(timestamp)) / 1000);
if (diff < 60) return '刚刚';
if (diff < 3600) return `${Math.floor(diff / 60)}分钟前`;
if (diff < 86400) return `${Math.floor(diff / 3600)}小时前`;
if (diff < 604800) return `${Math.floor(diff / 86400)}天前`;

return new Date(timestamp).toLocaleDateString('zh-CN', { month: 'numeric', day: 'numeric' }) + '日';
},

formatContent(content) {
if (!content) return '';

return content
.replace(/<pre><code>[\s\S]*?<\/code><\/pre>/g, '[代码块]')
.replace(/<code>([^<]{4,})<\/code>/g, '[代码]')
.replace(/<code>([^<]{1,3})<\/code>/g, '$1')
.replace(/<img[^>]*>/g, '[图片]')
.replace(/<a[^>]*?>[\s\S]*?<\/a>/g, '[链接]')
.replace(/<[^>]+>/g, '')
.replace(/&(gt|lt|amp|quot|#39|nbsp);/g, m =>
({'>':'>', '<':'<', '&':'&', 'quot':'"', '#39':"'", 'nbsp':' '})[m.slice(1,-1)])
.replace(/\s+/g, ' ')
.trim();
},

generateCommentHTML(comment) {
const { created, comment: content, url, avatar, nick, mailMd5, id } = comment;
const timeAgo = this.formatTimeAgo(created);
const formattedContent = this.formatContent(content);
const adminBadge = mailMd5 === this.ADMIN_EMAIL_MD5 ? `
<svg t="1731283534336" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="29337" width="22" height="22"><path d="M512 0C230.4 0 0 230.4 0 512s230.4 512 512 512 512-230.4 512-512S793.6 0 512 0z m291.84 366.08c-46.08 0-79.36 23.04-92.16 66.56l-163.84 358.4h-66.56L312.32 435.2c-17.92-46.08-46.08-71.68-89.6-71.68v-35.84H512v35.84h-40.96c-25.6 2.56-30.72 23.04-12.8 61.44l102.4 225.28 89.6-199.68c25.6-56.32 2.56-84.48-71.68-89.6v-35.84h225.28v40.96z" fill="#06c013" p-id="29338" data-spm-anchor-id="a313x.search_index.0.i73.2b2d3a81BgxnVW" class=""></path></svg>` : '';

return `
<div class="aside-list-item" title="${formattedContent}" onclick="pjax.loadUrl('${url}#${id}')">
<div class="thumbnail">
<img class="aside-list-avatar" src="${avatar}" alt="avatar">
</div>
<div class="content">
<div class="aside-list-author">
${nick} ${adminBadge}
<span class="aside-list-date">${timeAgo}</span>
</div>
<div class="aside-list-content">${formattedContent}</div>
</div>
</div>
`;
},

getErrorTemplate(icon, message) {
return `
<div style="min-height: 346px;display: flex;padding: 20px;text-align: center;justify-content: center;align-items: center;flex-direction: column;">
<i class="fas fa-${icon}" style="font-size: 2rem; color: ${icon === 'exclamation-circle' ? '#ff6b6b' : '#999'}; margin-bottom: 10px;"></i>
<p style="color: #666;margin: 0;">${message}</p>
</div>
`;
},

async insertComponent() {
const container = document.getElementById("latest-comments");
if (!container) return;

container.innerHTML = `<img src="${this.LOADING_GIF}" style="display: flex;min-height: 346px;object-fit: cover;">`;

const comments = await this.fetchComments();
let content;

if (comments === null) {
content = this.getErrorTemplate('exclamation-circle', '评论加载失败,请稍后再试');
} else if (comments.length === 0) {
content = this.getErrorTemplate('comment-slash', '还没有评论呢~ 快来抢沙发吧!');
} else {
content = comments.map(this.generateCommentHTML.bind(this)).join('');
}

container.style.opacity = '0';
container.innerHTML = content;

requestAnimationFrame(() => {
container.style.transition = 'opacity 0.3s ease-in';
container.style.opacity = '1';
});
}
};

// 初始化时注入CSS并启动组件
['DOMContentLoaded', 'pjax:success'].forEach(event =>
document.addEventListener(event, () => {
injectCSS();
LatestComments.insertComponent();
})
);
})();

2. 添加组件配置

source/_data/widget.yml 中添加以下配置:

1
2
3
4
5
6
7
8
9
10
top:
- class_name: card-latest-comments
id_name:
name: 最新评论
icon: fas fa-comments
html: |
<a href="/messages/" class="headline-right" title="查看更多">
<i class="fas fa-angle-right"></i>
</a>
<div class="aside-list" id="latest-comments"></div>

3. 引入JS文件

_config.anzhiyu.yml 主题配置文件的 inject 配置项的 bottom 中引入 JS 文件:

仅供参考 static是我在source文件夹下创建的文件夹 具体根据实际情况修改!!

1
2
3
inject:
bottom:
- <script src="/static/comments.js"></script>

4. 配置说明

1
2
3
4
5
const LatestComments = {
API_URL: 'https://twikoo.ruom.top', // 这里填入你的Twikoo服务器地址
ADMIN_EMAIL_MD5: 'f2c9c64c90a00afeed5ba410e5447a0d01aa294874bd662032a27c5385bcde1c', // 这里填入你的邮箱md5值
PAGE_SIZE: 5, // 显示的评论数量
LOADING_GIF: 'https://lib.bsgun.cn/Hexo-static/img/loading.gif', // 加载动画图片地址

获取MD5

有小伙伴不知道怎么获取邮箱MD5

蓝色选中的就是邮箱md5

结语

如果文章内容有错误地方,欢迎下方评论!!!