博客也写了三年多了,一共用了三个主题,这里记录下碰到的问题以及相关主题优化,防止之后再换主题时碰到相同问题。

参考资料:

备注:

所有npm命令均在博客根目录下进行。

next

使用时间:2018/3 -> 2021/1

链接:https://github.com/theme-next/hexo-theme-next

第一款使用的主题,选择的原因是简洁加上使用者多,总体来说体验很好,不过当时一个数学公式显示的bug调了我半年,最后更换主题的原因是感觉过于简洁。

matery

使用时间:2021/1 -> 2021/6

链接:https://github.com/blinkfox/hexo-theme-matery

第二款使用的主题,确实比next好看不少,有一些方面有点小bug。

butterfly

使用时间:2021/16 -> 至今

链接:https://github.com/jerryc127/hexo-theme-butterfly

到目前为止碰到的最好看的主题,而且个性化配置较多,使用下来bug比较少,所以选择了这款,后续会记录下配置相关。

待配置项

问题

基本配置

menu:
  # 中文
  首页: / || fas fa-home
  时间轴: /archives/ || fas fa-archive
  标签: /tags/ || fas fa-tags
  分类: /categories/ || fas fa-folder-open
  博客统计||fas fa-chart-pie:
    文章统计: /charts/ || far fa-chart-bar
    访问统计: /census/ || fas fa-chart-area
  阅读排行榜: /top/ || fab fa-hotjar
  娱乐||fa fa-heartbeat:
    电影: /movies/ || fas fa-video
  关于我: /about/ || fas fa-heart

大部分都是基本的,这里介绍下博客统计以及阅读排行榜的配置。

博客统计

安装cheerio:

npm i cheerio --save
文章统计

参考资料:

https://blog.eurkon.com/post/1213ef82.html

https://blog.csdn.net/kebi007/article/details/68488694

https://stackoverflow.com/questions/32734591/pie-chart-overlaps-with-legends

https://cndrew.cn/2020/03/03/calender/#%E5%88%86%E7%B1%BB%E9%9B%B7%E8%BE%BE%E5%9B%BE

https://github.com/blinkfox/hexo-theme-matery

添加了日历以及雷达图,并对第一份资料做了一些微调,保证标签能完全显示。

路径:

blog\source\census\index.md:

内容:

---
title: 文章统计
date: 2021-06-09 02:04:50
---

<script src="https://cdn.jsdelivr.net/npm/echarts@4.7.0/dist/echarts.min.js"></script>

<!-- 文章发布日历 -->
<div id="post-calendar" style="border-radius: 8px; height: 250px; padding: 10px;"></div>
<!-- 文章发布时间统计图 -->
<div id="posts-chart" style="border-radius: 8px; height: 300px; padding: 10px;"></div>
<!-- 文章标签统计图 -->
<div id="tags-chart" data-length="10" style="border-radius: 8px; height: 300px; padding: 10px;"></div>
<!-- 文章分类统计图 -->
<div id="categories-chart" style="border-radius: 8px; height: 300px; padding: 10px;"></div>
<!-- 文章分类雷达图 -->
<div id="categories-radar" style="border-radius: 8px; height: 300px; padding: 10px;"></div>

新建的文件路径:

\blog\themes\butterfly\scripts\helpers\charts.js

在tagsChart中添加如下内容,

+ grid: {
+     left: '10%',
+     bottom:'25%'
+ },

最后得到:

function tagsChart (len) {
  const tagArr = []
  hexo.locals.get('tags').map(function (tag) {
    tagArr.push({ name: tag.name, value: tag.length })
  })
  tagArr.sort((a, b) => { return b.value - a.value })

  let dataLength = Math.min(tagArr.length, len) || tagArr.length
  const tagNameArr = []
  const tagCountArr = []
  for (let i = 0; i < dataLength; i++) {
    tagNameArr.push(tagArr[i].name)
    tagCountArr.push(tagArr[i].value)
  }
  const tagNameArrJson = JSON.stringify(tagNameArr)
  const tagCountArrJson = JSON.stringify(tagCountArr)

  return `
  <script id="tagsChart">
    var color = '#000'
    var tagsChart = echarts.init(document.getElementById('tags-chart'), 'light');
    var tagsOption = {
      textStyle: {
        color: color
      },
      title: {
        text: 'Top ${dataLength} 标签统计图',
        x: 'center',
        textStyle: {
          color: color
        }
      },
      tooltip: {},
      xAxis: {
        name: '标签',
        type: 'category',
        axisTick: {
          show: false
        },
        axisLine: {
          show: true,
          lineStyle: {
            color: color
          }
        },
        axisLabel: {
            interval:0,
            rotate:20
         },
        grid: {
          left: '10%',
          bottom:'25%'
          },
        data: ${tagNameArrJson}
      },
      yAxis: {
        name: '文章篇数',
        type: 'value',
        splitLine: {
          show: false
        },
        axisTick: {
          show: false
        },
        axisLine: {
          show: true,
          lineStyle: {
            color: color
          }
        }
      },
      series: [{
        name: '文章篇数',
        type: 'bar',
        data: ${tagCountArrJson},
        itemStyle: {
          opacity: 1,
          color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
            offset: 0,
            color: 'rgba(128, 255, 165)'
          },
          {
            offset: 1,
            color: 'rgba(1, 191, 236)'
          }])
        },
        emphasis: {
          itemStyle: {
            opacity: 1,
            color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
              offset: 0,
              color: 'rgba(128, 255, 195)'
            },
            {
              offset: 1,
              color: 'rgba(1, 211, 255)'
            }])
          }
        },
        markLine: {
          data: [{
            name: '平均值',
            type: 'average'
          }]
        }
      }]
    };
    tagsChart.setOption(tagsOption);
    window.addEventListener("resize", () => { 
      tagsChart.resize();
    });
    </script>`
}

修改categoriesChart,删除lengend的原因是我的标签较多,图表会产生重叠:

+ showLegendSymbol: false,

- legend: {
- top: 'bottom',
- 	textStyle: {
- 	color: color
- 	}
- },
文章日历

代码修改自matery主题,路径为

hexo-theme-matery\layout\_widget

新增的文件路径:

\blog\themes\butterfly\scripts\helpers\calendar.js

内容:

const cheerio = require('cheerio')
const moment = require('moment')

hexo.extend.filter.register('after_render:html', function (locals) {
  const $ = cheerio.load(locals)
  const post = $('#post-calendar')
  let htmlEncode = false

  if (post.length > 0) {
    if (post.length > 0 && $('#postCalender').length === 0) {
      if (post.attr('data-encode') === 'true') htmlEncode = true
      post.after(postCalender())
    }

    if (htmlEncode) {
      return $.root().html().replace(/&amp;#/g, '&#')
    } else {
      return $.root().html()
    }
  } else {
    return locals
  }
}, 15)

function postCalender () {
    var nameMap = 'cn';
    var titleText = '文章日历';

    // calculate range.
    var startDate = moment().subtract(1, 'years');
    var endDate = moment();
    var rangeArr = '["' + startDate.format('YYYY-MM-DD') + '", "' + endDate.format('YYYY-MM-DD') + '"]';

    // post and count map.
    var dateMap = new Map();
    hexo.locals.get('posts').forEach(function (post) {
        var date = post.date.format('YYYY-MM-DD');
        var count = dateMap.get(date);
        dateMap.set(date, count == null || count == undefined ? 1 : count + 1);
    });

    // loop the data for the current year, generating the number of post per day
    var i = 0;
    var datePosts = '[';
    var dayTime = 3600 * 24 * 1000;
    for (var time = startDate; time <= endDate; time += dayTime) {
        var date = moment(time).format('YYYY-MM-DD');
        datePosts = (i === 0 ? datePosts + '["' : datePosts + ', ["') + date + '", '
                + (dateMap.has(date) ? dateMap.get(date) : 0) + ']';
        i++;
    }
    datePosts += ']';

    return `
    <script id="postCalender">
        var myChart = echarts.init(document.getElementById('post-calendar'));
        console.log(1);
        console.log('${titleText}');
        var option = {
            title: {
                top: 0,
                text: '${titleText}',
                left: 'center',
                textStyle: {
                    color: '#3C4858'
                }
            },
            tooltip: {
                padding: 10,
                backgroundColor: '#555',
                borderColor: '#777',
                borderWidth: 1,
                formatter: function (obj) {
                    var value = obj.value;
                    return '<div style="font-size: 14px;">' + value[0] + ':' + value[1] + '</div>';
                }
            },
            visualMap: {
                show: true,
                showLabel: true,
                categories: [0, 1, 2, 3, ">=4"],
                calculable: true,
                inRange: {
                    symbol: 'rect',
                    color: ['#ebedf0', '#c6e48b', '#7bc96f', '#239a3b', '#196127']
                },
                itemWidth: 12,
                itemHeight: 12,
                orient: 'horizontal',
                left: 'center',
                bottom: 40
            },
            calendar: [{
                left: 'center',
                range: ${rangeArr},
                cellSize: [13, 13],
                splitLine: {
                    show: false
                },
                itemStyle: {
                    color: '#196127',
                    borderColor: '#fff',
                    borderWidth: 2
                },
                yearLabel: {
                    show: false
                },
                monthLabel: {
                    nameMap: '${nameMap}',
                    fontSize: 11
                },
                dayLabel: {
                    formatter: '{start}  1st',
                    nameMap: '${nameMap}',
                    fontSize: 11
                }
            }],
            series: [{
                type: 'heatmap',
                coordinateSystem: 'calendar',
                calendarIndex: 0,
                data: ${datePosts}
            }]

        };
        myChart.setOption(option);
        window.addEventListener("resize", () => { 
            myChart.resize();
        });
        </script>`
}
文章分类雷达图

代码修改自matery主题,路径为

hexo-theme-matery\layout\_widget\category-radar.ejs

新增的文件路径:

\blog\themes\butterfly\scripts\helpers\radar.js

内容为:

const cheerio = require('cheerio')
const moment = require('moment')

hexo.extend.filter.register('after_render:html', function (locals) {
  const $ = cheerio.load(locals)
  const category = $('#categories-radar')
  let htmlEncode = false

  if (category.length > 0) {
    if (category.length > 0 && $('#categoriesRadar').length === 0) {
      if (category.attr('data-encode') === 'true') htmlEncode = true
      category.after(categoriesRadar())
    }

    if (htmlEncode) {
      return $.root().html().replace(/&amp;#/g, '&#')
    } else {
      return $.root().html()
    }
  } else {
    return locals
  }
}, 15)

function categoriesRadar () {
    var categories = hexo.locals.get('categories');

    // Find the maximum and average values of the post categories.
    var radarValueArr = [];
    categories.some(function(category) {
        radarValueArr.push(category.length);
    });

    var max = Math.max.apply(null, radarValueArr) + Math.min.apply(null, radarValueArr);

    // Calculate the data needed for the radar chart.
    var indicatorArr = [];
    categories.map(function(category) {
        indicatorArr.push({'name': category.name, 'max': max});
    });

    var indicatorData = JSON.stringify(indicatorArr);
    var radarValueData = JSON.stringify(radarValueArr);

    return `
    <script id="categoriesRadar">
        var radarChart = echarts.init(document.getElementById('categories-radar'));
        var option = {
            title: {
                left: 'center',
                text: '文章分类雷达图',
                textStyle: {
                    // fontWeight: 500,
                    // fontSize: 22
                    color: '#000'
                }
            },
            tooltip: {},
            radar: {
                name: {
                    textStyle: {
                        color: '#3C4858'
                    }
                },
                indicator: ${indicatorData},
                nameGap: 5,
                // center: ['50%','55%'],
                center: ['50%','50%'],
                radius: '66%',
                left: 'center'
            },
            series: [{
                type: 'radar',
                color: ['#3ecf8e'],
                itemStyle: {normal: {areaStyle: {type: 'default'}}},
                data : [
                    {
                        value : ${radarValueData},
                        name : 'test'
                    }
                ]
            }]
        };

        radarChart.setOption(option);
        </script>`
}
访问统计

参考资料:

https://blog.eurkon.com/post/ef1da941.html

https://akilar.top/posts/1f9c68c9/

基本按照上述资料进行了配置,增加了全球访客图,变更如下:

路径:

blog\source\census\index.md:

内容:

<script src="https://cdn.jsdelivr.net/npm/echarts@4.7.0/dist/echarts.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/echarts@4.7.0/map/js/china.js"></script> <!-- 中国地图 -->
<script src="https://cdn.jsdelivr.net/npm/echarts@4.7.0/map/js/world.js"></script> <!-- 世界地图 -->

<!-- 全球访问地图 -->
<div id="world-map-chart" style="border-radius: 8px; height: 500px; padding: 10px;"></div>
<!-- 全国访问地图 -->
<div id="map-chart" style="border-radius: 8px; height: 600px; padding: 10px;"></div>
<!-- 访问趋势 -->
<div id="trends-chart" style="border-radius: 8px; height: 300px; padding: 10px;"></div>
<!-- 访问来源 -->
<div id="sources-chart" style="border-radius: 8px; height: 300px; padding: 10px;"></div>

路径

blog\themes\butterfly\scripts\helpers\census.js

内容:

const cheerio = require('cheerio')
const moment = require('moment')
const fetch = require('node-fetch')

hexo.extend.filter.register('after_render:html', async function (locals) {
  const $ = cheerio.load(locals)
  const worldmap = $('#world-map-chart')
  const map = $('#map-chart')
  const trend = $('#trends-chart')
  const source = $('#sources-chart')
  let htmlEncode = false

  if (worldmap.length > 0 || map.length > 0 || trend.length > 0 || source.length > 0) {
    if (worldmap.length > 0 && $('#worldMapChart').length === 0) {
      worldmap.after(await worldMapChart())
    }
    if (map.length > 0 && $('#mapChart').length === 0) {
      map.after(await mapChart())
    }
    if (trend.length > 0 && $('#trendsChart').length === 0) {
      trend.after(await trendsChart())
    }
    if (source.length > 0 && $('#sourcesChart').length === 0) {
      source.after(await sourcesChart())
    }

    if (htmlEncode) {
      return $.root().html().replace(/&amp;#/g, '&#')
    } else {
      return $.root().html()
    }
  } else {
    return locals
  }
}, 15)

const startDate = '20000101' // 开始日期
const endDate = moment().format('YYYYMMDD') // 结束日期
const accessToken = '' // accessToken
const siteId = '' // 网址id
const dataUrl = 'https://openapi.baidu.com/rest/2.0/tongji/report/getData?access_token=' + accessToken + '&site_id=' + siteId;
// const dataUrl = 'https://baidu-tongji-api-lime.vercel.app/api?access_token=' + accessToken + '&site_id=' + siteId;
const metrics = 'pv_count' // 统计访问次数 PV 填写 'pv_count',统计访客数 UV 填写 'visitor_count',二选一
const metricsName = (metrics === 'pv_count' ? '访问次数' : (metrics === 'visitor_count' ? '访客数' : ''))

// 全球访问地图
function worldMapChart () {
  return new Promise(resolve => {
    const paramUrl = '&start_date=' + startDate + '&end_date=' + endDate + '&metrics=' + metrics + '&method=visit/world/a';
    fetch(dataUrl + paramUrl)
      .then(data => data.json())
      .then(data => {
        monthArr = [];
        let mapName = data.result.items[0]
        let mapValue = data.result.items[1]
        let mapArr = []
        let max = mapValue[0][0]
        for (let i = 0; i < mapName.length; i++) {
          mapArr.push({ name: mapName[i][0].name, value: mapValue[i][0] })
        }
        const mapArrJson = JSON.stringify(mapArr)
        resolve(`
          <script id="worldMapChart">
            var color = document.documentElement.getAttribute('data-theme') === 'light' ? '#4c4948' : 'rgba(255,255,255,0.7)'
            var worldmap = echarts.init(document.getElementById('world-map-chart'), 'light');
            var worldmapOption = {
              title: {
                text: '博客访问来源地图(全球)',
                x: 'center',
                textStyle: {
                  color: color
                }
              },
              tooltip: {
                trigger: 'item'
              },
              visualMap: {
                min: 0,
                max: ${max},
                // max: 1000,
                left: 'left',
                top: 'bottom',
                text: ['高','低'],
                color: ['#1E90FF', '#AAFAFA'],
                textStyle: {
                  color: color
                },
                calculable: true
              },
              series: [{
                name: '${metricsName}',
                type: 'map',
                mapType: 'world',
                showLegendSymbol: false,
                nameMap: {
                  "Afghanistan":"阿富汗",
                  "Albania":"阿尔巴尼亚",
                  "Algeria":"阿尔及利亚",
                  "Angola":"安哥拉",
                  "Argentina":"阿根廷",
                  "Armenia":"亚美尼亚",
                  "Australia":"澳大利亚",
                  "Austria":"奥地利",
                  "Azerbaijan":"阿塞拜疆",
                  "Bahamas":"巴哈马",
                  "Bahrain":"巴林",
                  "Bangladesh":"孟加拉国",
                  "Belarus":"白俄罗斯",
                  "Belgium":"比利时",
                  "Belize":"伯利兹",
                  "Benin":"贝宁",
                  "Bhutan":"不丹",
                  "Bolivia":"玻利维亚",
                  "Bosnia and Herz.":"波斯尼亚和黑塞哥维那",
                  "Botswana":"博茨瓦纳",
                  "Brazil":"巴西",
                  "British Virgin Islands":"英属维京群岛",
                  "Brunei":"文莱",
                  "Bulgaria":"保加利亚",
                  "Burkina Faso":"布基纳法索",
                  "Burundi":"布隆迪",
                  "Cambodia":"柬埔寨",
                  "Cameroon":"喀麦隆",
                  "Canada":"加拿大",
                  "Cape Verde":"佛得角",
                  "Cayman Islands":"开曼群岛",
                  "Central African Rep.":"中非共和国",
                  "Chad":"乍得",
                  "Chile":"智利",
                  "China":"中国",
                  "Colombia":"哥伦比亚",
                  "Comoros":"科摩罗",
                  "Congo":"刚果",
                  "Costa Rica":"哥斯达黎加",
                  "Croatia":"克罗地亚",
                  "Cuba":"古巴",
                  "Cyprus":"塞浦路斯",
                  "Czech Rep.":"捷克共和国",
                  "Côte d'Ivoire":"科特迪瓦",
                  "Dem. Rep. Congo":"刚果民主共和国",
                  "Dem. Rep. Korea":"朝鲜",
                  "Denmark":"丹麦",
                  "Djibouti":"吉布提",
                  "Dominican Rep.":"多米尼加共和国",
                  "Ecuador":"厄瓜多尔",
                  "Egypt":"埃及",
                  "El Salvador":"萨尔瓦多",
                  "Equatorial Guinea":"赤道几内亚",
                  "Eritrea":"厄立特里亚",
                  "Estonia":"爱沙尼亚",
                  "Ethiopia":"埃塞俄比亚",
                  "Falkland Is.":"福克兰群岛",
                  "Fiji":"斐济",
                  "Finland":"芬兰",
                  "Fr. S. Antarctic Lands":"所罗门群岛",
                  "France":"法国",
                  "Gabon":"加蓬",
                  "Gambia":"冈比亚",
                  "Georgia":"格鲁吉亚",
                  "Germany":"德国",
                  "Ghana":"加纳",
                  "Greece":"希腊",
                  "Greenland":"格陵兰",
                  "Guatemala":"危地马拉",
                  "Guinea":"几内亚",
                  "Guinea-Bissau":"几内亚比绍",
                  "Guyana":"圭亚那",
                  "Haiti":"海地",
                  "Honduras":"洪都拉斯",
                  "Hungary":"匈牙利",
                  "Iceland":"冰岛",
                  "India":"印度",
                  "Indonesia":"印度尼西亚",
                  "Iran":"伊朗",
                  "Iraq":"伊拉克",
                  "Ireland":"爱尔兰",
                  "Isle of Man":"马恩岛",
                  "Israel":"以色列",
                  "Italy":"意大利",
                  "Jamaica":"牙买加",
                  "Japan":"日本",
                  "Jordan":"约旦",
                  "Kazakhstan":"哈萨克斯坦",
                  "Kenya":"肯尼亚",
                  "Korea":"韩国",
                  "Kuwait":"科威特",
                  "Kyrgyzstan":"吉尔吉斯斯坦",
                  "Lao PDR":"老挝",
                  "Latvia":"拉脱维亚",
                  "Lebanon":"黎巴嫩",
                  "Lesotho":"莱索托",
                  "Liberia":"利比里亚",
                  "Libya":"利比亚",
                  "Lithuania":"立陶宛",
                  "Luxembourg":"卢森堡",
                  "Macedonia":"马其顿",
                  "Madagascar":"马达加斯加",
                  "Malawi":"马拉维",
                  "Malaysia":"马来西亚",
                  "Maldives":"马尔代夫",
                  "Mali":"马里",
                  "Malta":"马耳他",
                  "Mauritania":"毛利塔尼亚",
                  "Mauritius":"毛里求斯",
                  "Mexico":"墨西哥",
                  "Moldova":"摩尔多瓦",
                  "Monaco":"摩纳哥",
                  "Mongolia":"蒙古",
                  "Montenegro":"黑山共和国",
                  "Morocco":"摩洛哥",
                  "Mozambique":"莫桑比克",
                  "Myanmar":"缅甸",
                  "Namibia":"纳米比亚",
                  "Nepal":"尼泊尔",
                  "Netherlands":"荷兰",
                  "New Caledonia":"新喀里多尼亚",
                  "New Zealand":"新西兰",
                  "Nicaragua":"尼加拉瓜",
                  "Niger":"尼日尔",
                  "Nigeria":"尼日利亚",
                  "Norway":"挪威",
                  "Oman":"阿曼",
                  "Pakistan":"巴基斯坦",
                  "Panama":"巴拿马",
                  "Papua New Guinea":"巴布亚新几内亚",
                  "Paraguay":"巴拉圭",
                  "Peru":"秘鲁",
                  "Philippines":"菲律宾",
                  "Poland":"波兰",
                  "Portugal":"葡萄牙",
                  "Puerto Rico":"波多黎各",
                  "Qatar":"卡塔尔",
                  "Reunion":"留尼旺",
                  "Romania":"罗马尼亚",
                  "Russia":"俄罗斯",
                  "Rwanda":"卢旺达",
                  "S. Geo. and S. Sandw. Is.":"南乔治亚和南桑威奇群岛",
                  "S. Sudan":"南苏丹",
                  "San Marino":"圣马力诺",
                  "Saudi Arabia":"沙特阿拉伯",
                  "Senegal":"塞内加尔",
                  "Serbia":"塞尔维亚",
                  "Sierra Leone":"塞拉利昂",
                  "Singapore":"新加坡",
                  "Slovakia":"斯洛伐克",
                  "Slovenia":"斯洛文尼亚",
                  "Solomon Is.":"所罗门群岛",
                  "Somalia":"索马里",
                  "South Africa":"南非",
                  "Spain":"西班牙",
                  "Sri Lanka":"斯里兰卡",
                  "Sudan":"苏丹",
                  "Suriname":"苏里南",
                  "Swaziland":"斯威士兰",
                  "Sweden":"瑞典",
                  "Switzerland":"瑞士",
                  "Syria":"叙利亚",
                  "Tajikistan":"塔吉克斯坦",
                  "Tanzania":"坦桑尼亚",
                  "Thailand":"泰国",
                  "Togo":"多哥",
                  "Tonga":"汤加",
                  "Trinidad and Tobago":"特立尼达和多巴哥",
                  "Tunisia":"突尼斯",
                  "Turkey":"土耳其",
                  "Turkmenistan":"土库曼斯坦",
                  "U.S. Virgin Islands":"美属维尔京群岛",
                  "Uganda":"乌干达",
                  "Ukraine":"乌克兰",
                  "United Arab Emirates":"阿拉伯联合酋长国",
                  "United Kingdom":"英国",
                  "United States":"美国",
                  "Uruguay":"乌拉圭",
                  "Uzbekistan":"乌兹别克斯坦",
                  "Vanuatu":"瓦努阿图",
                  "Vatican City":"梵蒂冈城",
                  "Venezuela":"委内瑞拉",
                  "Vietnam":"越南",
                  "W. Sahara":"西撒哈拉",
                  "Yemen":"也门",
                  "Yugoslavia":"南斯拉夫",
                  "Zaire":"扎伊尔",
                  "Zambia":"赞比亚",
                  "Zimbabwe":"津巴布韦"
                },
                label: {
                  emphasis: {
                    show: false
                  }
                },
                itemStyle: {
                  normal: {
                    areaColor: 'rgba(255, 255, 255, 0.1)',
                    borderColor: '#20232a'
                  },
                  emphasis: {
                    areaColor: 'gold'
                  }
                },
                data: ${mapArrJson}
                }]
            };
            worldmap.setOption(worldmapOption);
            window.addEventListener("resize", () => { 
              worldmap.resize();
            });
          </script>`);
      }).catch(function (error) {
        console.log(error);
      });
  })
}

// 访问地图
function mapChart () {
  return new Promise(resolve => {
    const paramUrl = '&start_date=' + startDate + '&end_date=' + endDate + '&metrics=' + metrics + '&method=visit/district/a';
    fetch(dataUrl + paramUrl)
      .then(data => data.json())
      .then(data => {
        monthArr = [];
        let mapName = data.result.items[0]
        let mapValue = data.result.items[1]
        let mapArr = []
        let max = mapValue[0][0]
        for (let i = 0; i < mapName.length; i++) {
          mapArr.push({ name: mapName[i][0].name, value: mapValue[i][0] })
        }
        const mapArrJson = JSON.stringify(mapArr)
        resolve(`
          <script id="mapChart">
            var color = document.documentElement.getAttribute('data-theme') === 'light' ? '#4c4948' : 'rgba(255,255,255,0.7)'
            var mapChart = echarts.init(document.getElementById('map-chart'), 'light');
            var mapOption = {
              title: {
                text: '博客访问来源地图(全国)',
                x: 'center',
                textStyle: {
                  color: color
                }
              },
              tooltip: {
                trigger: 'item'
              },
              visualMap: {
                min: 0,
                max: ${max},
                left: 'left',
                top: 'bottom',
                text: ['高','低'],
                color: ['#1E90FF', '#AAFAFA'],
                textStyle: {
                  color: color
                },
                calculable: true
              },
              series: [{
                name: '${metricsName}',
                type: 'map',
                mapType: 'china',
                showLegendSymbol: false,
                label: {
                  emphasis: {
                    show: false
                  }
                },
                itemStyle: {
                  normal: {
                    areaColor: 'rgba(255, 255, 255, 0.1)',
                    borderColor: '#20232a'
                  },
                  emphasis: {
                    areaColor: 'gold'
                  }
                },
                data: ${mapArrJson}
                }]
            };
            mapChart.setOption(mapOption);
            window.addEventListener("resize", () => { 
              mapChart.resize();
            });
          </script>`);
      }).catch(function (error) {
        console.log(error);
      });
  })
}

// 访问趋势
function trendsChart () {
  return new Promise(resolve => {
    const paramUrl = '&start_date=' + startDate + '&end_date=' + endDate + '&metrics=' + metrics + '&method=trend/time/a&gran=month'
    fetch(dataUrl + paramUrl)
      .then(data => data.json())
      .then(data => {
        const monthArr = []
        const monthValueArr = []
        const monthName = data.result.items[0]
        const monthValue = data.result.items[1]
        for (let i = Math.min(monthName.length, 12) - 1; i >= 0; i--) {
          monthArr.push(monthName[i][0].substring(0, 7).replace('/', '-'))
          if (monthValue[i][0] !== '--') {
            monthValueArr.push(monthValue[i][0])
          } else {
            monthValueArr.push(null)
          }
        }
        const monthArrJson = JSON.stringify(monthArr)
        const monthValueArrJson = JSON.stringify(monthValueArr)
        resolve(`
          <script id="trendsChart">
            var color = document.documentElement.getAttribute('data-theme') === 'light' ? '#4c4948' : 'rgba(255,255,255,0.7)'
            var trendsChart = echarts.init(document.getElementById('trends-chart'), 'light');
            var trendsOption = {
              textStyle: {
                color: color
              },
              title: {
                text: '博客访问统计图',
                x: 'center',
                textStyle: {
                  color: color
                }
              },
              tooltip: {
                trigger: 'axis'
              },
              xAxis: {
                name: '日期',
                type: 'category',
                axisTick: {
                  show: false
                },
                axisLine: {
                  show: true,
                  lineStyle: {
                    color: color
                  }
                },
                data: ${monthArrJson}
              },
              yAxis: {
                name: '${metricsName}',
                type: 'value',
                splitLine: {
                  show: false
                },
                axisTick: {
                  show: false
                },
                axisLine: {
                  show: true,
                  lineStyle: {
                    color: color
                  }
                }
              },
              series: [{
                name: '${metricsName}',
                type: 'line',
                smooth: true,
                lineStyle: {
                    width: 0
                },
                showSymbol: false,
                itemStyle: {
                  opacity: 1,
                  color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
                    offset: 0,
                    color: 'rgba(128, 255, 165)'
                  },
                  {
                    offset: 1,
                    color: 'rgba(1, 191, 236)'
                  }])
                },
                areaStyle: {
                  opacity: 1,
                  color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
                    offset: 0,
                    color: 'rgba(128, 255, 165)'
                  }, {
                    offset: 1,
                    color: 'rgba(1, 191, 236)'
                  }])
                },
                data: ${monthValueArrJson},
                markLine: {
                  data: [{
                    name: '平均值',
                    type: 'average'
                  }]
                }
              }]
            };
            trendsChart.setOption(trendsOption);
            window.addEventListener("resize", () => { 
              trendsChart.resize();
            });
          </script>`)
      }).catch(function (error) {
        console.log(error);
      });
  })
}

// 访问来源
function sourcesChart () {
  return new Promise(resolve => {
    const paramUrl = '&start_date=' + startDate + '&end_date=' + endDate + '&metrics=' + metrics + '&method=source/all/a';
    fetch(dataUrl + paramUrl)
      .then(data => data.json())
      .then(data => {
        monthArr = [];
        let sourcesName = data.result.items[0]
        let sourcesValue = data.result.items[1]
        let sourcesArr = []
        for (let i = 0; i < sourcesName.length; i++) {
          sourcesArr.push({ name: sourcesName[i][0].name, value: sourcesValue[i][0] })
        }
        const sourcesArrJson = JSON.stringify(sourcesArr)
        resolve(`
          <script id="sourcesChart">
            var color = document.documentElement.getAttribute('data-theme') === 'light' ? '#4c4948' : 'rgba(255,255,255,0.7)'
            var sourcesChart = echarts.init(document.getElementById('sources-chart'), 'light');
            var sourcesOption = {
              textStyle: {
                color: color
              },
              title: {
                text: '博客访问来源统计图',
                x: 'center',
                textStyle: {
                  color: color
                }
              },
              legend: {
                top: 'bottom',
                textStyle: {
                  color: color
                }
              },
              tooltip: {
                trigger: 'item',
                formatter: "{a} <br/>{b} : {c} ({d}%)"
              },
              series: [{
                name: '${metricsName}',
                type: 'pie',
                radius: [30, 80],
                center: ['50%', '50%'],
                roseType: 'area',
                label: {
                  formatter: "{b} : {c} ({d}%)"
                },
                data: ${sourcesArrJson},
                itemStyle: {
                  emphasis: {
                    shadowBlur: 10,
                    shadowOffsetX: 0,
                    shadowColor: 'rgba(255, 255, 255, 0.5)'
                  }
                }
              }]
            };
            sourcesChart.setOption(sourcesOption);
            window.addEventListener("resize", () => { 
              sourcesChart.resize();
            });
          </script>`);
      }).catch(function (error) {
        console.log(error);
      });
  })
}

阅读排行榜

todo。

娱乐

电影

参考资料:

https://butterfly.js.org/posts/4073eda/#%E9%9B%BB%E5%BD%B1

https://github.com/jerryc127/butterfly-plugins/tree/main/hexo-butterfly-douban

首页增加字数总计,阅读时长

原生的butterfly首页无法显示字数总计,阅读时长,这里对其进行了修改,具体见配置方法

文章页无法显示标签

更新:

标签显示在文章底部,并不是没有显示,所以后续操作可以忽略。

在主题的3.7.8版本中,即使将主题配置文件中如下tags设置为true,依然没有显示标签

post_meta:
  post:
    tags: true # true or false 文章頁是否顯示標籤

检查后发现是缺少对应代码,配置文件为

\blog\themes\butterfly\layout\includes\header\post-info.pug

搜索

i.fas.fa-angle-right.post-meta-separator

在其后添加

//- add
if (theme.post_meta.page.tags && page.tags.data.length > 0)
  span.post-meta-categories
    span.post-meta-separator |
    i.fas.fa-tag
    each item, index in page.tags.data
      a(href=url_for(item.path)).article-meta__tags #[=item.name]
      if (index < page.tags.data.length - 1)
        span.article-meta__link #[='•']

注意该if要和

if (theme.post_meta.post.categories && page.categories.data.length > 0)

的缩进相同,最后的效果为:

if (theme.post_meta.post.categories && page.categories.data.length > 0)
  span.post-meta-categories
    if (theme.post_meta.post.date_type)
      span.post-meta-separator |

    each item, index in page.categories.data
      i.fas.fa-inbox.fa-fw.post-meta-icon
      a(href=url_for(item.path)).post-meta-categories #[=item.name]
      if (index < page.categories.data.length - 1)
        i.fas.fa-angle-right.post-meta-separator

//- add
if (theme.post_meta.page.tags && page.tags.data.length > 0)
  span.post-meta-categories
    span.post-meta-separator |
    i.fas.fa-tag
    each item, index in page.tags.data
      a(href=url_for(item.path)).article-meta__tags #[=item.name]
      if (index < page.tags.data.length - 1)
        span.article-meta__link #[='•']

访客地图

参考资料:

https://butterfly.js.org/posts/ea33ab97/#%E4%BE%8B%E5%AD%90

https://clustrmaps.com/

https://www.cnblogs.com/DHUtoBUAA/p/12283754.html

按照链接1配置,将代码替换为链接2中自己的代码即可。

数学公式

参考资料:

https://butterfly.js.org/posts/ceeb73f/#Math-%E6%95%B8%E5%AD%B8

https://ranmaosong.github.io/2017/11/29/hexo-support-mathjax/

选了mathjax,首先进行安装:

npm uninstall hexo-renderer-marked --save
npm install hexo-renderer-kramed --save
npm uninstall hexo-math --save
npm install hexo-renderer-mathjax --save

如果安装后部分数学公式显示不正确,可以按照如下方式调整。

文件位置:

D:\blog\node_modules\hexo-renderer-kramed\lib\renderer.js

修改内容:

// Change inline math rule
function formatText(text) {
    // Fit kramed's rule: $$ + \1 + $$
    - return text.replace(/`\$(.*?)\$`/g, '$$$$$1$$$$');
    + return text;
}

文件位置:

\blog\node_modules\kramed\lib\rules\inline.js

修改内容:

- escape: /^\\([\\`*{}\[\]()#$+\-.!_>])/,
+ escape: /^\\([`*\[\]()#$+\-.!_>])/,

- em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
+ em: /^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,

还有一些显示的问题,已经在issue中提出:

目前(截至20210612)作者已经在dev分支修复上述问题,修改的文件为

\blog\themes\butterfly\layout\includes\third-party\math\mathjax.pug

添加社交图标

b站

参考资料:

https://blog.imzjw.cn/posts/b74f504f/

https://www.iconfont.cn/

以b站为例,按照教程中的方法下载css,内容如下:

@font-face {
    font-family: "iconfont"; /* Project id 2604084 */
    src: url('//at.alicdn.com/t/font_2604084_fh638f3m5rm.woff2?t=1623256482370') format('woff2'),
         url('//at.alicdn.com/t/font_2604084_fh638f3m5rm.woff?t=1623256482370') format('woff'),
         url('//at.alicdn.com/t/font_2604084_fh638f3m5rm.ttf?t=1623256482370') format('truetype');
  }
  
  .iconfont {
    font-family: "iconfont" !important;
    /* font-size: 16px; */
    font-size: 18px;
    font-style: normal;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
  }
  
  .icon-bilibili:before {
    content: "\e601";
  }

在如下路径

\blog\themes\butterfly\source\css

新建bilibili.css,将这些内容复制到css文件中。

在主题的_config.yml中添加:

inject:
  head:
    - <link rel="stylesheet" href="/css/bilibili.css" media="defer" onload="this.media='all'">

在主题的_config.yml文件中social添加如下内容即可:

social:
	  iconfont icon-bilibili: b站主页 || Bilibili

其他的社交图标用同样方式添加即可。

rss

参考资料:

https://lilu.org.cn/2021/01/31/butterfly/

安装插件:

npm install hexo-generator-feed --save

配置:

social:
	  fa fa-rss: /atom.xml

文章meta信息

加载动画

网页加载动画:

# Loading Animation (加載動畫)
preloader: true

图片加载动画:

# Lazyload (圖片懶加載)
# https://github.com/verlok/vanilla-lazyload
lazyload:
  enable: true
  field: site # site/post
  placeholder: /img/loading.gif
  blur: false

loading.gif即为加载动画,路径如下

\blog\themes\butterfly\source\img

顶部图

配置命令如下:

# Disable all banner image
disable_top_img: false

# The banner image of home page
index_img: picture

美化页面

# Beautify (美化頁面顯示)
beautify:
  enable: true
  field: site # site/post
  title-prefix-icon: '\f0c1'
  title-prefix-icon-color: '#F47466'

评论系统

目前配置了valine以及来必力(遗留问题,早期的评论来自于来必力,目前来必力只通过qq浏览器成功登陆,chrome以及edge均无法登陆)。

配置如下:

comments:
  # Up to two comments system, the first will be shown as default
  # Choose: Disqus/Disqusjs/Livere/Gitalk/Valine/Waline/Utterances/Facebook Comments/Twikoo
  use:
  - Valine
  - Livere
  # - Disqus
  text: true # Display the comment name next to the button
  # lazyload: The comment system will be load when comment element enters the browser's viewport.
  # If you set it to true, the comment count will be invalid
  lazyload: false
  count: true # Display comment count in post's top_img
  card_post_count: true # Display comment count in Home Page

搜索

使用了hexo-generator-search,注意hexo c并清除浏览器缓存。

分析系统

目前配置了百度分析以及谷歌分析,谷歌分析的跟踪 ID获取方法见:

https://git.mrhao.xyz/blog/2020/04/25/GoogleAnalytics/

https://iamlay.com/2020/06/27/HexoGoogleAnalytics/

搜索引擎收录

参考资料如下:

https://github.com/cjh0613/hexo-submit-urls-to-search-engine

https://cjh0613.com/20200603HexoSubmitUrlsToSearchEngine.html

插件为hexo-submit-urls-to-search-engine