Hexo(Matery)自定义(二):增加文章统计
注意事项
页面预览:点我
想法:偶然看到,甚是喜欢,但奈何原博主没有分享资源
新建统计页面
hexo new page stat
然后在文件内容的双线内添加:
type: stat
layout: stat
新建统计页面模板文件
在主题目录的layout/
文件夹下新建stat.ejs
模板文件,然后用vim
也好,用其他工具也好,写入以下内容:
<style type="text/css">
/* don't remove. */
.about-cover {
height: 75vh;
}
</style>
<%- partial('_partial/bg-cover') %>
<main class="content">
<div class="container chip-container">
<div class="card">
<div class="card-content">
<div class="tag-title center-align">
<i class="<%= theme.stat.icon %>"></i> <%= theme.stat.title %>
</div>
</div>
</div>
<div class="card">
<div class="card-content">
<% if (theme.stat.text) { %>
<style>
#stat-text {
-webkit-text-size-adjust: 100%;
line-height: 1.5;
font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif;
font-weight: normal;
--fa-font-brands: normal 400 1em/1 "Font Awesome 6 Brands";
--fa-font-regular: normal 400 1em/1 "Font Awesome 6 Free";
--fa-font-solid: normal 900 1em/1 "Font Awesome 6 Free";
color: #34495e;
text-align: center;
float: left;
box-sizing: border-box;
padding: 0 .75rem;
min-height: 1px;
opacity: .6;
font-size: 1.1rem;
width: 83.3333333333%;
left: auto;
right: auto;
margin-left: 8.3333333333%;
}
</style>
<div id="stat-text">
<div class="row">
<div class="col l8 offset-l2 m10 offset-m1 s10 offset-s1 center-align text">
<%= theme.stat.text %>
</div>
</div>
</div>
<br>
<hr>
<% } %>
<%- page.content %>
<% if (theme.stat.show_post) { %>
<h1 id="<%= theme.stat.title %>"><a href="#<%= theme.stat.title %>" class="headerlink" title="<%= theme.stat.title %>"></a><%= theme.stat.title %></h1>
<%
var article_posts = [];
site.posts.forEach(post => {
article_posts.push(post);
});
for (var i = 0; i < article_posts.length - 1; i++) {
for (var j = 1; j < article_posts.length - i; j++) {
if (article_posts[j].date > article_posts[j-1].date) {
var tmp = article_posts[j];
article_posts[j] = article_posts[j-1];
article_posts[j-1] = tmp;
}
}
}
var post_date_index = null;
article_posts.forEach(post => {
pwd = post.password;
if ((!(pwd && pwd.length > 0))&&(post.hide != true)) {
if (date(post.date, 'YYYY-MM') != post_date_index) {
post_date_index = date(post.date, 'YYYY-MM');
%>
<h2 id="<%= post_date_index %>"><a href="#<%= post_date_index %>" class="headerlink" title="<%= post_date_index %>"></a><%= post_date_index %></h2>
<% } %>
<p>
<code><%= date(post.date, 'YYYY-MM-DD') %></code> -
<a href="<%- url_for(post.path) %>"><%= post.title %></a>
</p>
<% } %>
<% }); %>
<% } %>
</div>
</div>
<style type="text/css">
#posts-chart,
#categories-chart,
#tags-chart {
width: 100%;
height: 300px;
margin: 0.5rem auto;
padding: 0.5rem;
}
</style>
<div class="card">
<div class="card-content">
<div class="chart col s12 m6 l4" data-aos="zoom-in-up">
<div id="posts-chart"></div>
</div>
</div>
</div>
<div class="card">
<div class="card-content">
<div class="chart col s12 m6 l4" data-aos="zoom-in-up">
<div id="tags-chart"></div>
</div>
</div>
</div>
<div class="card">
<div class="card-content">
<div class="chart col s12 m6 l4" data-aos="zoom-in-up">
<div id="categories-chart"></div>
</div>
</div>
</div>
<!-- <div class="card">
<div class="card-content">
</div>
</div> -->
</div>
<% if (site.categories && site.categories.length > 0) { %>
<%- partial('_widget/category-radar') %>
<% } %>
<script type="text/javascript" src="<%- theme.jsDelivr.url %><%- url_for(theme.libs.js.echarts) %>"></script>
<script>
let postsChart = echarts.init(document.getElementById('posts-chart'));
let categoriesChart = echarts.init(document.getElementById('categories-chart'));
let tagsChart = echarts.init(document.getElementById('tags-chart'));
<%
/* calculate postsOption data. */
var startDate = moment().subtract(1, 'years').startOf('month');
var endDate = moment().endOf('month');
var monthMap = new Map();
var dayTime = 3600 * 24 * 1000;
for (var time = startDate; time <= endDate; time += dayTime) {
var month = moment(time).format('YYYY-MM');
if (!monthMap.has(month)) {
monthMap.set(month, 0);
}
}
// post and count map.
site.posts.forEach(function (post) {
var month = post.date.format('YYYY-MM');
if (monthMap.has(month)) {
monthMap.set(month, monthMap.get(month) + 1);
}
});
// xAxis data and yAxis data.
var monthArr = JSON.stringify([...monthMap.keys()]);
var monthValueArr = JSON.stringify([...monthMap.values()]);
%>
let postsOption = {
title: {
text: '<%- __("postPublishChart") %>',
top: -5,
x: 'center'
},
tooltip: {
trigger: 'axis'
},
xAxis: {
type: 'category',
data: <%- monthArr %>
},
yAxis: {
type: 'value',
},
series: [
{
name: '<%- __("postsNumberName") %>',
type: 'line',
color: ['#6772e5'],
data: <%- monthValueArr %>,
markPoint: {
symbolSize: 45,
color: ['#fa755a','#3ecf8e','#82d3f4'],
data: [{
type: 'max',
itemStyle: {color: ['#3ecf8e']},
name: '<%- __("maximum") %>'
}, {
type: 'min',
itemStyle: {color: ['#fa755a']},
name: '<%- __("minimum") %>'
}]
},
markLine: {
itemStyle: {color: ['#ab47bc']},
data: [
{type: 'average', name: '<%- __("average") %>'}
]
}
}
]
};
<%
/* calculate categoriesOption data. */
var categoryArr = [];
site.categories.map(function(category) {
categoryArr.push({'name': category.name, 'value': category.length})
});
var categoryArrJson = JSON.stringify(categoryArr);
%>
let categoriesOption = {
title: {
text: '<%- __("categoriesChart") %>',
top: -4,
x: 'center'
},
tooltip: {
trigger: 'item',
formatter: "{a} <br/>{b} : {c} ({d}%)"
},
series: [
{
name: '<%- __("categories") %>',
type: 'pie',
radius: '50%',
color: ['#6772e5', '#ff9e0f', '#fa755a', '#3ecf8e', '#82d3f4', '#ab47bc', '#525f7f', '#f51c47', '#26A69A'],
data: <%- categoryArrJson %>,
itemStyle: {
emphasis: {
shadowBlur: 10,
shadowOffsetX: 0,
shadowColor: 'rgba(0, 0, 0, 0.5)'
}
}
}
]
};
<%
/* calculate tagsOption data. */
// get all tags name and count,then order by length desc.
var tagArr = [];
site.tags.map(function(tag) {
tagArr.push({'name': tag.name, 'value': tag.length});
});
tagArr.sort((a, b) => {return b.value - a.value});
// get Top 10 tags name and count.
var tagNameArr = [];
var tagCountArr = [];
for (var i = 0, len = Math.min(tagArr.length, 10); i < len; i++) {
tagNameArr.push(tagArr[i].name);
tagCountArr.push(tagArr[i].value);
}
var tagNameArrJson = JSON.stringify(tagNameArr);
var tagCountArrJson = JSON.stringify(tagCountArr);
%>
let tagsOption = {
title: {
text: '<%- __("top10TagsChart") %>',
top: -5,
x: 'center'
},
tooltip: {},
xAxis: [
{
type: 'category',
data: <%- tagNameArrJson %>
}
],
yAxis: [
{
type: 'value'
}
],
series: [
{
type: 'bar',
color: ['#82d3f4'],
barWidth : 18,
data: <%- tagCountArrJson %>,
markPoint: {
symbolSize: 45,
data: [{
type: 'max',
itemStyle: {color: ['#3ecf8e']},
name: '<%- __("maximum") %>'
}, {
type: 'min',
itemStyle: {color: ['#fa755a']},
name: '<%- __("minimum") %>'
}],
},
markLine: {
itemStyle: {color: ['#ab47bc']},
data: [
{type: 'average', name: '<%- __("average") %>'}
]
}
}
]
};
// render the charts
postsChart.setOption(postsOption);
categoriesChart.setOption(categoriesOption);
tagsChart.setOption(tagsOption);
</script>
</main>
配置
在主题配置文件中添加:
# 统计
stat:
title: 文章统计 # 标题
text: 身处不幸的时候,更应该竭尽全力。 # 一言
icon: far fa-bar-chart # 图标
show_post: true # 是否显示文章统计
添加菜单导航,生成静态文件即可
解释
- 获取所有文章信息
var article_posts = [];
site.posts.forEach(post => {
article_posts.push(post);
});
- 按发布时间前后排序
我写的比较草率,你有更好的排序算法直接更改即可,应该可以提高生成效率
for (var i = 0; i < article_posts.length - 1; i++) {
for (var j = 1; j < article_posts.length - i; j++) {
if (article_posts[j].date > article_posts[j-1].date) {
var tmp = article_posts[j];
article_posts[j] = article_posts[j-1];
article_posts[j-1] = tmp;
}
}
}
- 遍历所有文章信息,忽略加密文章和隐藏文章,再根据文章日期分类
var post_date_index = null;
article_posts.forEach(post => {
pwd = post.password;
if ((!(pwd && pwd.length > 0))&&(post.hide != true)) {
if (date(post.date, 'YYYY-MM') != post_date_index) {
post_date_index = date(post.date, 'YYYY-MM');
Over!