From 266fcb2aedef0e0c98feeaf45784fb05169914f1 Mon Sep 17 00:00:00 2001 From: bisnsh Date: Wed, 13 Aug 2025 09:56:45 +0800 Subject: [PATCH] add echart --- themes/butterfly/layout/archive.pug | 1 + .../layout/includes/page/categories.pug | 4 +- .../butterfly/layout/includes/page/tags.pug | 4 +- themes/butterfly/scripts/helpers/chart.js | 445 ++++++++++++++++++ 4 files changed, 452 insertions(+), 2 deletions(-) create mode 100644 themes/butterfly/scripts/helpers/chart.js diff --git a/themes/butterfly/layout/archive.pug b/themes/butterfly/layout/archive.pug index 913dedc..50bda33 100644 --- a/themes/butterfly/layout/archive.pug +++ b/themes/butterfly/layout/archive.pug @@ -3,6 +3,7 @@ extends includes/layout.pug block content include ./includes/mixins/article-sort.pug #archive + #posts-chart(data-start="2021-01" style="height: 300px; padding: 10px;") .article-sort-title= `${_p('page.articles')} - ${getArchiveLength()}` +articleSort(page.posts) include includes/pagination.pug \ No newline at end of file diff --git a/themes/butterfly/layout/includes/page/categories.pug b/themes/butterfly/layout/includes/page/categories.pug index 79153c8..fe02784 100644 --- a/themes/butterfly/layout/includes/page/categories.pug +++ b/themes/butterfly/layout/includes/page/categories.pug @@ -1 +1,3 @@ -.category-lists!= list_categories() \ No newline at end of file + +.category-lists!= list_categories() +#categories-chart(data-parent="true" style="height: 300px; padding: 10px;") \ No newline at end of file diff --git a/themes/butterfly/layout/includes/page/tags.pug b/themes/butterfly/layout/includes/page/tags.pug index b5b62cd..ef56d4f 100644 --- a/themes/butterfly/layout/includes/page/tags.pug +++ b/themes/butterfly/layout/includes/page/tags.pug @@ -1,2 +1,4 @@ + .tag-cloud-list.text-center - !=cloudTags({source: site.tags, orderby: page.orderby || 'random', order: page.order || 1, minfontsize: 1.2, maxfontsize: 1.5, limit: 0, unit: 'em'}) \ No newline at end of file + !=cloudTags({source: site.tags, orderby: page.orderby || 'random', order: page.order || 1, minfontsize: 1.2, maxfontsize: 1.5, limit: 0, unit: 'em'}) +#tags-chart(data-length="10" style="height: 300px; padding: 10px;") \ No newline at end of file diff --git a/themes/butterfly/scripts/helpers/chart.js b/themes/butterfly/scripts/helpers/chart.js new file mode 100644 index 0000000..516e608 --- /dev/null +++ b/themes/butterfly/scripts/helpers/chart.js @@ -0,0 +1,445 @@ +const cheerio = require('cheerio') +const moment = require('moment') + +hexo.extend.filter.register('after_render:html', function (locals) { + const $ = cheerio.load(locals) + const post = $('#posts-chart') + const tag = $('#tags-chart') + const category = $('#categories-chart') + const htmlEncode = false + + if (post.length > 0 || tag.length > 0 || category.length > 0) { + if (post.length > 0 && $('#postsChart').length === 0) { + if (post.attr('data-encode') === 'true') htmlEncode = true + post.after(postsChart(post.attr('data-start'))) + } + if (tag.length > 0 && $('#tagsChart').length === 0) { + if (tag.attr('data-encode') === 'true') htmlEncode = true + tag.after(tagsChart(tag.attr('data-length'))) + } + if (category.length > 0 && $('#categoriesChart').length === 0) { + if (category.attr('data-encode') === 'true') htmlEncode = true + category.after(categoriesChart(category.attr('data-parent'))) + } + + if (htmlEncode) { + return $.root().html().replace(/&#/g, '&#') + } else { + return $.root().html() + } + } else { + return locals + } +}, 15) +function switchPostChart () { + // 这里为了统一颜色选取的是“明暗模式”下的两种字体颜色,也可以自己定义 + let color = document.documentElement.getAttribute('data-theme') === 'light' ? '#4C4948' : 'rgba(255,255,255,0.7)' + if (document.getElementById('posts-chart') && postsOption) { + try { + let postsOptionNew = postsOption + postsOptionNew.title.textStyle.color = color + postsOptionNew.xAxis.nameTextStyle.color = color + postsOptionNew.yAxis.nameTextStyle.color = color + postsOptionNew.xAxis.axisLabel.color = color + postsOptionNew.yAxis.axisLabel.color = color + postsOptionNew.xAxis.axisLine.lineStyle.color = color + postsOptionNew.yAxis.axisLine.lineStyle.color = color + postsOptionNew.series[0].markLine.data[0].label.color = color + postsChart.setOption(postsOptionNew) + } catch (error) { + console.log(error) + } + } + if (document.getElementById('tags-chart') && tagsOption) { + try { + let tagsOptionNew = tagsOption + tagsOptionNew.title.textStyle.color = color + tagsOptionNew.xAxis.nameTextStyle.color = color + tagsOptionNew.yAxis.nameTextStyle.color = color + tagsOptionNew.xAxis.axisLabel.color = color + tagsOptionNew.yAxis.axisLabel.color = color + tagsOptionNew.xAxis.axisLine.lineStyle.color = color + tagsOptionNew.yAxis.axisLine.lineStyle.color = color + tagsOptionNew.series[0].markLine.data[0].label.color = color + tagsChart.setOption(tagsOptionNew) + } catch (error) { + console.log(error) + } + } + if (document.getElementById('categories-chart') && categoriesOption) { + try { + let categoriesOptionNew = categoriesOption + categoriesOptionNew.title.textStyle.color = color + categoriesOptionNew.legend.textStyle.color = color + if (!categoryParentFlag) { categoriesOptionNew.series[0].label.color = color } + categoriesChart.setOption(categoriesOptionNew) + } catch (error) { + console.log(error) + } + } +} +document.getElementById("mode-button").addEventListener("click", function () { setTimeout(switchPostChart, 100) }) + +function postsChart (startMonth) { + const startDate = moment(startMonth || '2020-01') + const endDate = moment() + + const monthMap = new Map() + const dayTime = 3600 * 24 * 1000 + for (let time = startDate; time <= endDate; time += dayTime) { + const month = moment(time).format('YYYY-MM') + if (!monthMap.has(month)) { + monthMap.set(month, 0) + } + } + hexo.locals.get('posts').forEach(function (post) { + const month = post.date.format('YYYY-MM') + if (monthMap.has(month)) { + monthMap.set(month, monthMap.get(month) + 1) + } + }) + const monthArr = JSON.stringify([...monthMap.keys()]) + const monthValueArr = JSON.stringify([...monthMap.values()]) + + return ` + ` +} + +function tagsChart (len) { + const tagArr = [] + hexo.locals.get('tags').map(function (tag) { + tagArr.push({ name: tag.name, value: tag.length, path: tag.path }) + }) + tagArr.sort((a, b) => { return b.value - a.value }) + + const dataLength = Math.min(tagArr.length, len) || tagArr.length + const tagNameArr = [] + for (let i = 0; i < dataLength; i++) { + tagNameArr.push(tagArr[i].name) + } + const tagNameArrJson = JSON.stringify(tagNameArr) + const tagArrJson = JSON.stringify(tagArr) + + return ` + ` +} + +function categoriesChart (dataParent) { + const categoryArr = [] + let categoryParentFlag = false + hexo.locals.get('categories').map(function (category) { + if (category.parent) categoryParentFlag = true + categoryArr.push({ + name: category.name, + value: category.length, + path: category.path, + id: category._id, + parentId: category.parent || '0' + }) + }) + categoryParentFlag = categoryParentFlag && dataParent === 'true' + categoryArr.sort((a, b) => { return b.value - a.value }) + function translateListToTree (data, parent) { + let tree = [] + let temp + data.forEach((item, index) => { + if (data[index].parentId == parent) { + let obj = data[index]; + temp = translateListToTree(data, data[index].id); + if (temp.length > 0) { + obj.children = temp + } + if (tree.indexOf()) + tree.push(obj) + } + }) + return tree + } + const categoryNameJson = JSON.stringify(categoryArr.map(function (category) { return category.name })) + const categoryArrJson = JSON.stringify(categoryArr) + const categoryArrParentJson = JSON.stringify(translateListToTree(categoryArr, '0')) + + return ` + ` +}