【笔记】采集Hexo文章实现贡献日历

前言

编写Hexo插件,通过采集sitemap.txt站点地图中的文章创建日期作为数据,通过Echarts的日历热力图(Calendar Heatmap)渲染图表,实现贡献日历

准备工作

  • 已部署的站点可以访问站点地图(/sitemap.txt

创建贡献页面

1
hexo new page contribute

下载依赖

1
npm install ajax-for-node

编写脚本

  • hexo/scripts目录下创建.js文件作为Hexo脚本

site:这个常量要改成自己的站点域名
start_data:这个常量要改成自己的建站时间

hexo/scripts/GenerateContributeEchartsScript.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
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
const nodeAjax = require('ajax-for-node');
const fs = require('hexo-fs');

// 站点根目录
const site = `https://loli.fj.cn`;
// 站点地图路径
const site_map = `${site}/sitemap.txt`;
// 正则表达式
const reg = /\d{4}\/\d{2}\/\d{2}/;
// 建站时间
const start_data = new Date("2019-11-22");
// 所有日期
let data_list = [];
// 日期计数
let data_map = new Map();
// 分年度存储
let data_map_year = new Map();
// 文件内容
let file_content = ""

nodeAjax({
url: site_map,
type: "GET",
processData: false,
contentType: false,
success: function (result) {
const lines = result.split("\n");
// 过滤结果
for (let line of lines) {
if (reg.test(line)) {
line = line.substr(19, 10);
line = line.replaceAll("/", "-");
data_list.push(line);
}
}
// 遍历结果集,将所有得到的日期计数
for (const item of data_list) {
if (data_map.get(item)) {
// 能得到日期,将计数器自增1
data_map.set(item, data_map.get(item) + 1);
} else {
// 不能得到日期,将计数器置1
data_map.set(item, 1);
}
}
// 将所有计数的日期计为0
// 从建站那一年元旦遍历到明年元旦的时间
let current_data = new Date(`${start_data.getFullYear()}-01-01`);
// 明年元旦
const end_data = new Date(`${new Date().getFullYear() + 1}-01-01`);
while (current_data.getTime() < end_data.getTime()) {
// 遍历的时间的年月日
const year = current_data.getFullYear();
const month = current_data.getMonth() + 1 < 10 ? '0' + (current_data.getMonth() + 1) : current_data.getMonth() + 1;
const day = current_data.getDate().toString().length === 1 ? "0" + current_data.getDate() : current_data.getDate();
const current = `${year}-${month}-${day}`
// 不能得到日期,将计数器置0
if (data_map.get(current) === undefined) {
data_map.set(current, 0);
}
// 自增1天
current_data.setDate(current_data.getDate() + 1);
}
// 分年度存储
// 从建站那一年遍历到今年
for (let currentYear = start_data.getFullYear(); currentYear < end_data.getFullYear(); currentYear++) {
if (!data_map_year.get(currentYear)) {
// 如果还没有当前年度的数据,就新增一个数组
data_map_year.set(currentYear, [])
}
// 遍历所有处理后的数据,将当前年的数据全部转移
data_map.forEach((value, key) => {
if (key.indexOf(currentYear) !== -1) {
const data = [key, value];
data_map_year.get(currentYear).push(data);
}
})
// console.log(data_map_year.get(currentYear));
}
// 准备将要写入文件的内容
file_content += `---\ntitle: 贡献\n---\n`;
// 从建站那一年遍历到今年
for (let currentYear = start_data.getFullYear(); currentYear < end_data.getFullYear(); currentYear++) {
file_content += `\n## ${currentYear}年度\n\n{% echarts 700 100 %}\n{\n // 悬浮窗\n tooltip: {\n padding: 10,\n borderColor: "#FFF",\n borderWidth: 1,\n formatter: function (a) {\n var b = a.value;\n return \`<div style="font-size: 14px;">\${b[0]}: \${b[1]}</div>\`;\n }\n },\n visualMap: {\n show: false,\n min: 1,\n max: 5,\n minOpen: true,\n maxOpen: true,\n calculable: false,\n inRange: {\n symbol: "rect",\n color: ["#ebedf0", "#c6e48b", "#7bc96f", "#239a3b", "#196127"]\n },\n itemWidth: 12,\n itemHeight: 12,\n type: "piecewise",\n orient: "horizontal",\n left: "center",\n top: 0\n },\n // 主体\n calendar: {\n top: 0,\n range: "${currentYear}",\n left: "center",\n cellSize: [13, 13],\n splitLine: {\n // 月份分割线\n show: false\n },\n name: {\n textStyle: {\n color: "#3C4858"\n }\n },\n itemStyle: {\n borderColor: "#fff",\n borderWidth: 2\n },\n yearLabel: {\n // 左侧年份\n show: false\n },\n monthLabel: {\n // 上面月份\n show: false\n },\n dayLabel: {\n // 左侧星期\n show: false\n }\n },\n series: {\n type: "heatmap",\n coordinateSystem: "calendar",\n calendarIndex: 0,\n data: ${JSON.stringify(data_map_year.get(currentYear))}\n }\n}\n{% endecharts %}\n`;
}
// 写入文件
fs.writeFile("./source/contribute/index.md", file_content);
}
});

渲染后的结果

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
---
title: 貢獻
comments: false
---

## 2019年度

{% echarts 700 100 %}
{
// 悬浮窗
tooltip: {
padding: 10,
borderColor: "#FFF",
borderWidth: 1,
formatter: function (a) {
var b = a.value;
return `<div style="font-size: 14px;">${b[0]}: ${b[1]}</div>`;
}
},
visualMap: {
show: false,
min: 1,
max: 5,
minOpen: true,
maxOpen: true,
calculable: false,
inRange: {
symbol: "rect",
color: ["#ebedf0", "#c6e48b", "#7bc96f", "#239a3b", "#196127"]
},
itemWidth: 12,
itemHeight: 12,
type: "piecewise",
orient: "horizontal",
left: "center",
top: 0
},
// 主体
calendar: {
top: 0,
range: "2019",
left: "center",
cellSize: [13, 13],
splitLine: {
// 月份分割线
show: false
},
name: {
textStyle: {
color: "#3C4858"
}
},
itemStyle: {
borderColor: "#fff",
borderWidth: 2
},
yearLabel: {
// 左侧年份
show: false
},
monthLabel: {
// 上面月份
show: false
},
dayLabel: {
// 左侧星期
show: false
}
},
series: {
type: "heatmap",
coordinateSystem: "calendar",
calendarIndex: 0,
// 此处的数据以实际为准,此处只作为范例展示
data: [["2019-01-01",1],["2019-01-02",2]]
}
}
{% endecharts %}

完成

参考文献

小冰博客
Zfour/hexo-github-calendar
Kaiboshi的博客
HCLonely/hexo-calendar