Skip to content

Commit 2604ff1

Browse files
authored
Merge pull request #1641 from future-architect/feature
Add /categories
2 parents e2e42c4 + 8d6b280 commit 2604ff1

File tree

5 files changed

+129
-13
lines changed

5 files changed

+129
-13
lines changed

_config.yml

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,6 @@ source_dir: source
2323
public_dir: public
2424
tag_dir: tags
2525
archive_dir: articles
26-
category_dir: categories
2726
author_dir: authors
2827
techcast_dir: techcasts
2928
code_dir: downloads/code
@@ -35,10 +34,6 @@ author_generator:
3534
per_page: 25
3635
url_map:
3736

38-
category_generator:
39-
per_page: 25
40-
order_by: -date
41-
4237
tag_generator:
4338
per_page: 25
4439
order_by: -date

scripts/category_chart_helpers.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
'use strict';
2+
3+
/**
4+
* サイトの全投稿を集計し、時間軸ごとのカテゴリ別投稿データを生成する
5+
* - 2018年以前は年ごと、2019年以降は四半期ごと
6+
* @returns {object} { quarters: string[], series: object[], categories: string[] }
7+
*/
8+
function getQuarterlyCategoryData() {
9+
const posts = this.site.posts.sort('date', 1); // 日付順にソート
10+
if (!posts.length) {
11+
return { quarters: [], series: [], categories: [] };
12+
}
13+
14+
const dataByTimeBucket = new Map();
15+
16+
// 1. 全投稿をループして、時間軸ごとにカテゴリ別投稿数を集計
17+
posts.forEach(post => {
18+
const year = post.date.year();
19+
let timeKey;
20+
21+
if (year >= 2019) {
22+
// 2019年以降は四半期ごと
23+
const quarter = Math.floor(post.date.month() / 3) + 1;
24+
timeKey = `${year}-Q${quarter}`;
25+
} else {
26+
// 2018年以前は年ごと
27+
timeKey = year.toString();
28+
}
29+
30+
if (!dataByTimeBucket.has(timeKey)) {
31+
dataByTimeBucket.set(timeKey, new Map());
32+
}
33+
34+
const bucketData = dataByTimeBucket.get(timeKey);
35+
const postCategories = post.categories.map(cat => cat.name);
36+
if (!postCategories.length) return;
37+
38+
postCategories.forEach(catName => {
39+
const currentCount = bucketData.get(catName) || 0;
40+
bucketData.set(catName, currentCount + 1);
41+
});
42+
});
43+
44+
// 2. サイトの全カテゴリを取得し、「合計記事数」で降順にソートする
45+
const sortedCategoryObjects = this.site.categories.toArray().sort((a, b) => b.length - a.length);
46+
const sortedCategoryNames = sortedCategoryObjects.map(cat => cat.name);
47+
48+
// 3. X軸のラベル(時間軸)を生成し、ソートする
49+
const sortedTimeKeys = Array.from(dataByTimeBucket.keys()).sort();
50+
51+
// 4. EChartsのseries形式にデータを整形
52+
const series = sortedCategoryObjects.map(category => {
53+
const catName = category.name;
54+
const data = sortedTimeKeys.map(timeKey => {
55+
const bucketData = dataByTimeBucket.get(timeKey);
56+
return bucketData.get(catName) || 0; // その期間に投稿がなければ0
57+
});
58+
59+
return {
60+
name: catName,
61+
type: 'line',
62+
stack: 'Total',
63+
areaStyle: {},
64+
emphasis: {
65+
focus: 'series'
66+
},
67+
data: data
68+
};
69+
});
70+
71+
return {
72+
quarters: sortedTimeKeys, // キー名はEJS側と合わせるため'quarters'のまま
73+
series: series,
74+
categories: sortedCategoryNames
75+
};
76+
}
77+
78+
// ヘルパーとして登録
79+
hexo.extend.helper.register('get_quarterly_category_data', getQuarterlyCategoryData);

source/categories/index.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
title: カテゴリ一覧
3+
layout: categories
4+
---

themes/future/layout/categories.ejs

Lines changed: 44 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,50 @@
33
<li><a href="/">Home</a></li>
44
<li class="active">Categories</li>
55
</ul>
6+
67
<section id="main" class="margin-top-30">
7-
<h2 class="list-page">カテゴリから記事を探す</h2>
8-
<ul class="summary">
9-
<li><span class="summary-count"><%= count_categories() %></span><br><span class="summary-label">カテゴリ</span></li>
10-
</ul>
11-
<%- list_categories() %>
8+
9+
<div class="widget">
10+
<h2 class="list-page">カテゴリ別 投稿数の推移(四半期ごと)</h2>
11+
<div id="category-chart" style="width: 100%; height: 400px;"></div>
12+
</div>
13+
14+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/echarts.min.js"></script>
15+
16+
<script type="text/javascript">
17+
const chartData = <%- JSON.stringify(get_quarterly_category_data()) %>;
18+
const myChart = echarts.init(document.getElementById('category-chart'));
19+
const option = {
20+
tooltip: { trigger: 'axis', axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985' } } },
21+
legend: { data: chartData.categories, type: 'scroll', orient: 'horizontal', left: 'center', top: 'bottom' },
22+
grid: { left: '3%', right: '4%', bottom: '15%', containLabel: true },
23+
xAxis: [ { type: 'category', boundaryGap: false, data: chartData.quarters } ],
24+
yAxis: [ { name: '投稿数', type: 'value' } ],
25+
series: chartData.series
26+
};
27+
myChart.setOption(option);
28+
</script>
29+
30+
<br>
31+
<div class="widget">
32+
<h2 class="list-page">カテゴリから記事を探す</h2>
33+
<div class="widget">
34+
<ul class="nav margin-bottom-40">
35+
<%
36+
const compareFunc = (a, b) => b.posts.length - a.posts.length;
37+
const categoryRanks = site.categories.data.sort(compareFunc)
38+
39+
let categoryListHTML = "";
40+
categoryRanks.forEach(function(c) {
41+
let className = "";
42+
if (is_category(c.name)) {
43+
className = "category-list-current"
44+
}
45+
categoryListHTML += `<li class="${className}"><a href="${url_for(c.path)}">${c.name} (${c.length})</a></li>\n`
46+
});
47+
%>
48+
<%- categoryListHTML %>
49+
</ul>
50+
</div>
1251
</section>
1352
</div>
14-

themes/future/layout/category.ejs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
<ul class="breadcrumb">
33
<li><a href="/">Home</a></li>
44
<% if (page.current === 1) { %>
5-
<li><a href="/articles">Categories</a></li>
5+
<li><a href="/categories">Categories</a></li>
66
<li class="active"><%- page.category %></li>
77
<% } else { %>
8-
<li><a href="/articles">Category</a></li>
8+
<li><a href="/categories">Category</a></li>
99
<li><a href="/categories/<%- page.category %>"><%- page.category %></a></li>
1010
<li class="active"><%= page.current %>ページ目</li>
1111
<% } %>

0 commit comments

Comments
 (0)