hexo-butterfly实现友链状态检测
创建友链信息
在博客根目录创建link.js(如果你在添加友链朋友圈创建过了可无视
添加以下代码:
1 | const YML = require('yamljs') |
然后在控制台输入
1 | npm i yamljs --save |
复刻github项目
友情链接检测*不要勾选仅复刻main*
- 修改main分支下的config.yml,更改以下内容
1
2# 友情链接数据源URL
url: "https://你的博客域名/friend.json" - 切换到page分支
删除status.json和error-count.json的数据cloudflare部署
- 点击左侧的
Workers 和 Pages,再点击创建程序 - 点击pages➡️导入现有git➡️选择刚刚fork的库➡️更换生产分支
- 绑定域名
使用你绑定后的域名就能使用了
将检测结果显示到友链页
- 创建
source\_data\styles.styl,并填入以下内容
因为我出过一期友链样式魔改,本站所使用的是volantis和butterfly两个款式友链,我将提供这两种形式的styl文件
1.butterfly版本:1
// 友链状态指示器样式 .site-card position: relative .flink-list-item position: relative .site-card-status position: absolute bottom: 0 right: 0 z-index: 10 padding: 3px 8px border-radius: 8px 0 8px 0 font-size: 12px font-weight: 500 color: #fff background-color: rgba(0, 0, 0, 0.6) backdrop-filter: blur(4px) transition: all 0.3s ease .site-card-status.status-loading background-color: rgba(100, 100, 100, 0.8) animation: pulse 1.5s infinite .site-card-status.status-normal background-color: rgba(82, 196, 26, 0.9) .site-card-status.status-slow background-color: rgba(250, 173, 20, 0.9) .site-card-status.status-error background-color: rgba(255, 77, 79, 0.9) @keyframes pulse 0%, 100% opacity: 1 50% opacity: 0.5 [data-theme="dark"] .site-card-status background-color: rgba(255, 255, 255, 0.1) color: #fff [data-theme="dark"] .site-card-status.status-loading background-color: rgba(100, 100, 100, 0.8) [data-theme="dark"] .site-card-status.status-normal background-color: rgba(82, 196, 26, 0.8) [data-theme="dark"] .site-card-status.status-slow background-color: rgba(250, 173, 20, 0.8) [data-theme="dark"] .site-card-status.status-error background-color: rgba(255, 77, 79, 0.8)
2.volantis版本
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// 友链状态指示器样式 - 只对volantis样式生效
.volantis-flink-list .site-card
position: relative
.volantis-flink-list .site-card-status
position: absolute
top: 4px // 减小距离
left: 4px // 减小距离
z-index: 10
padding: 2px 6px // 减小内边距
border-radius: 12px // 稍微减小圆角
font-size: 10px // 缩小字体
font-weight: 600
color: #fff
background-color: rgba(0, 0, 0, 0.35)
backdrop-filter: blur(8px)
-webkit-backdrop-filter: blur(8px)
border: 1px solid rgba(255, 255, 255, 0.1)
transition: all 0.3s ease
min-width: 24px // 确保最小宽度
text-align: center
box-sizing: border-box
line-height: 1.2
// 加载中状态
.volantis-flink-list .site-card-status.status-loading
background-color: rgba(100, 100, 100, 0.5)
animation: pulse 1.5s infinite
// 正常状态
.volantis-flink-list .site-card-status.status-normal
background-color: rgba(82, 196, 26, 0.5)
// 缓慢状态
.volantis-flink-list .site-card-status.status-slow
background-color: rgba(250, 173, 20, 0.5)
// 错误状态
.volantis-flink-list .site-card-status.status-error
background-color: rgba(255, 77, 79, 0.5)
// 加载动画
@keyframes pulse
0%, 100%
opacity: 1
50%
opacity: 0.7
// 深色主题适配
[data-theme="dark"] .volantis-flink-list .site-card-status
background-color: rgba(255, 255, 255, 0.15)
color: #fff
border: 1px solid rgba(255, 255, 255, 0.2)
[data-theme="dark"] .volantis-flink-list .site-card-status.status-loading
background-color: rgba(100, 100, 100, 0.6)
[data-theme="dark"] .volantis-flink-list .site-card-status.status-normal
background-color: rgba(82, 196, 26, 0.6)
[data-theme="dark"] .volantis-flink-list .site-card-status.status-slow
background-color: rgba(250, 173, 20, 0.6)
[data-theme="dark"] .volantis-flink-list .site-card-status.status-error
background-color: rgba(255, 77, 79, 0.6)- 在
source\js中创建links.js,填入以下代码
- 在
- 这里也提供两种版本
1.butterfly版本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
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350// Butterfly主题友链状态检测脚本 - 适配提供的CSS样式
class LinkStatusChecker {
constructor() {
this.retryCount = 0;
this.MAX_RETRIES = 3;
this.STATUS_URL = 'https://你给检测项目绑定的域名/status.json';
this.init();
}
init() {
// 等待Butterfly主题的友链卡片加载完成
if (this.hasLinkCards()) {
this.injectStatusIndicators();
this.fetchAndUpdateStatus();
} else {
// 如果友链容器还没加载,等待一下
setTimeout(() => this.init(), 100);
}
}
hasLinkCards() {
return document.querySelector('.flink-list') ||
document.querySelector('.site-card') ||
document.querySelector('.flink-list-item');
}
// 为每个友链卡片注入状态指示器
injectStatusIndicators() {
// 尝试多种选择器以适应不同版本的Butterfly主题
const linkSelectors = [
'.flink-list .flink-list-item',
'.flink-list-item',
'.site-card',
'.friend-link-item'
];
let linkCards = [];
for (const selector of linkSelectors) {
const cards = document.querySelectorAll(selector);
if (cards.length > 0) {
linkCards = cards;
break;
}
}
linkCards.forEach(card => {
// 检查是否已经添加了状态指示器
if (card.querySelector('.site-card-status')) {
return;
}
const linkElement = card.querySelector('a');
if (!linkElement) return;
// 获取链接名称 - 适配Butterfly主题的不同结构
let linkName = '';
const nameSelectors = [
'.flink-item-name',
'.site-name',
'.friend-name',
'h2', 'h3', 'h4',
'.card-title',
'span'
];
for (const selector of nameSelectors) {
const nameEl = card.querySelector(selector);
if (nameEl && nameEl.textContent.trim()) {
linkName = nameEl.textContent.trim();
break;
}
}
// 如果还是没找到,使用链接文本
if (!linkName) {
linkName = linkElement.textContent.trim() ||
linkElement.getAttribute('title') ||
'未知网站';
}
const linkUrl = linkElement.href;
// 创建状态指示器 - 使用你提供的CSS类名
const statusEl = document.createElement('div');
statusEl.className = 'site-card-status status-loading';
statusEl.setAttribute('data-name', linkName);
statusEl.setAttribute('data-url', linkUrl);
statusEl.textContent = '检测中...';
// 添加到卡片中
card.appendChild(statusEl);
// 确保卡片有相对定位
if (getComputedStyle(card).position === 'static') {
card.style.position = 'relative';
}
});
}
async fetchLinkStatus() {
const response = await fetch(this.STATUS_URL);
if (!response.ok) {
throw new Error('网络请求失败');
}
return await response.json();
}
updateLinkStatus(data) {
if (data && data.link_status) {
const statusMap = {};
data.link_status.forEach(link => {
statusMap[link.name] = link;
});
// 更新每个链接的状态
const statusElements = document.querySelectorAll('.site-card-status');
statusElements.forEach(el => {
const linkName = el.getAttribute('data-name');
if (statusMap[linkName]) {
const status = statusMap[linkName];
this.updateStatusElement(el, status);
} else {
// 如果没有找到对应的状态数据,标记为错误
this.updateStatusElement(el, { success: false, latency: -1 });
}
});
} else {
throw new Error('无效的状态数据');
}
}
updateStatusElement(element, status) {
let statusClass = '';
let statusText = '';
if (!status.success || status.latency === -1) {
statusClass = 'status-error';
const errorCount = status.error_count || 0;
statusText = "异常[" + errorCount + "]";
} else {
const latency = status.latency;
if (latency <= 3) {
statusClass = 'status-normal';
} else {
statusClass = 'status-slow';
}
statusText = latency + 's';
}
element.className = 'site-card-status ' + statusClass;
element.textContent = statusText;
}
handleError() {
this.retryCount++;
if (this.retryCount <= this.MAX_RETRIES) {
console.log(`状态检测失败,第${this.retryCount}次重试...`);
setTimeout(() => this.fetchAndUpdateStatus(), 2000 * this.retryCount);
} else {
// 将所有状态设为错误
document.querySelectorAll('.site-card-status.status-loading').forEach(el => {
el.className = 'site-card-status status-error';
el.textContent = '获取失败';
});
}
}
async fetchAndUpdateStatus() {
try {
const data = await this.fetchLinkStatus();
this.updateLinkStatus(data);
} catch (error) {
console.error('获取友链状态失败:', error);
this.handleError();
}
}
}
// 添加你提供的CSS样式
const addLinkStatusStyles = () => {
// 检查是否已经添加了样式
if (document.querySelector('#link-status-styles')) {
return;
}
const css = `
.site-card {
position: relative
}
.flink-list-item {
position: relative
}
.site-card-status {
position: absolute;
bottom: 0;
right: 0;
z-index: 10;
padding: 3px 8px;
border-radius: 8px 0 8px 0;
font-size: 12px;
font-weight: 500;
color: #fff;
background-color: rgba(0, 0, 0, 0.6);
backdrop-filter: blur(4px);
transition: all 0.3s ease
}
.site-card-status.status-loading {
background-color: rgba(100, 100, 100, 0.8);
animation: pulse 1.5s infinite
}
.site-card-status.status-normal {
background-color: rgba(82, 196, 26, 0.9)
}
.site-card-status.status-slow {
background-color: rgba(250, 173, 20, 0.9)
}
.site-card-status.status-error {
background-color: rgba(255, 77, 79, 0.9)
}
@keyframes pulse {
0%,
100% {
opacity: 1
}
50% {
opacity: 0.5
}
}
[data-theme="dark"] .site-card-status {
background-color: rgba(255, 255, 255, 0.1);
color: #fff
}
[data-theme="dark"] .site-card-status.status-loading {
background-color: rgba(100, 100, 100, 0.8)
}
[data-theme="dark"] .site-card-status.status-normal {
background-color: rgba(82, 196, 26, 0.8)
}
[data-theme="dark"] .site-card-status.status-slow {
background-color: rgba(250, 173, 20, 0.8)
}
[data-theme="dark"] .site-card-status.status-error {
background-color: rgba(255, 77, 79, 0.8)
}
.flink-templates {
margin-top: 2rem;
padding: 1.5rem;
background: var(--efu-card-bg);
border-radius: 12px;
border: var(--style-border-always);
box-shadow: var(--efu-shadow-border)
}
.flink-templates h3 {
margin-bottom: 1rem;
font-size: 1.2rem;
font-weight: 600;
color: var(--efu-fontcolor)
}
.template-buttons {
display: flex;
gap: 1rem;
flex-wrap: wrap
}
.template-btn {
display: flex;
align-items: center;
gap: 0.5rem;
padding: 0.75rem 1.5rem;
border: none;
border-radius: 8px;
font-size: 14px;
font-weight: 500;
cursor: pointer;
transition: all 0.3s ease;
background: var(--efu-theme);
color: #fff
}
.template-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15)
}
.template-btn i {
font-size: 16px
}
.template-btn.markdown-btn {
background: #10b981
}
.template-btn.markdown-btn:hover {
background: #059669
}
[data-theme="dark"] .flink-templates {
background: var(--efu-card-bg);
border-color: var(--efu-border)
}
[data-theme="dark"] .template-btn {
background: var(--efu-theme)
}
[data-theme="dark"] .template-btn.markdown-btn {
background: #10b981
}
`;
const style = document.createElement('style');
style.id = 'link-status-styles';
style.textContent = css;
document.head.appendChild(style);
};
// 初始化
document.addEventListener('DOMContentLoaded', () => {
addLinkStatusStyles();
new LinkStatusChecker();
});
// 支持PJAX重新加载
document.addEventListener('pjax:complete', () => {
new LinkStatusChecker();
});
// 监听主题切换事件(如果Butterfly主题支持动态主题切换)
document.addEventListener('themechange', () => {
// 重新应用样式以确保深色主题正确
addLinkStatusStyles();
});
2.volantis版本
1 | // 友链状态检测脚本 - 只对volantis样式生效,状态显示在左上角 |
大功告成
至此已全部修改完成,本站只使用volantis版,所以里面的小巧思更多点,用butterfly版本只能复制让ai完善个人的小巧思了。完成预览:
更新这篇文章主要是发现我在逐渐忘记这部分魔改,赶紧得保存下来🤣
如果要更新友链信息那就在控制台执行
1 | node link.js |
最后hexo三连看看效果
1 | hexo;cl;hexo g;hexo d |
下一期预告:魔改about页面,实现anzhiyu同款
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 mccsjs🍋の小家!
评论


