点击查看更新记录

更新记录

2021-04-29:新增npm插件

  1. 适配冰老师最新的前端方案。
  2. 使用npm插件加载。
  3. 新增配置项,均为可选。
  4. 预留开发者接口,后续可能适配多主题样式。

2021-02-12:内测版v0.1

  1. 应援冰老师的友链朋友圈项目,提供Hexo的插件化魔改方案;
  2. 按照惯有思路,将API路径的配置添加到配置文件中。
  3. 在页面配置中使用type: fcircle来实现加载友链朋友圈页面。
  4. 适配样式UI,使得友链朋友圈文章样式同时间轴样式。
  5. 适配pjax。
点击查看参考教程
参考方向教程原贴
冰老师的友链朋友圈项目基于hexo的友链朋友圈 Beta1.0
冰老师编写的前端项目

写在最前

本帖为对冰老师的友链朋友圈项目的前端配置方案进行主题插件化适配的应援帖,并不包括关键的后端配置内容。后端配置请参看冰老师的原帖基于hexo的友链朋友圈 Beta1.0,本帖的内容对应原帖的步骤四:配置 HEXO 页面。考虑到冰老师的源项目也仍旧处于开发状态,故本帖也会尽可能的紧跟进度实时更新。

npm插件方案

新版npm插件方案
  1. 安装插件,在博客根目录[Blogroot]下打开终端,运行以下指令:
    1
    npm install hexo-butterfly-fcircle --save
  2. 添加配置信息
    在站点配置文件_config.yml或者主题配置文件_config.butterfly.yml中添加
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    # fcircle
    # see https://zfe.space/friendcircle/
    # see https://akilar.top/posts/8480b91c/
    fcircle:
    enable: true #控制开关
    apiurl: https://hexo-circle-of-friends-api.vercel.app/api #api地址
    maxnumber: 20 #【可选】页面展示文章数量
    addnumber: 10 #【可选】每次加载增加的篇数
    opentype: '_blank' #【可选】'_blank'打开新标签,'_self'本窗口打开,默认为'_blank'
    nofollow: true #【可选】开启禁止搜索引擎抓取,默认开启
    preload: #【可选】加载动画图片链接
    css: #【可选】开发者接口,自定义css链接
    js: #【可选】开发者接口,自定义js链接
    path: #【可选】fcircle的路径名称。默认为 fcircle,生成的页面为 fcircle/index.html
    front_matter: #【可选】fcircle页面的 front_matter 配置
    title: 朋友圈
    comments: false
  3. 参数释义
参数备选值/类型释义
enabletrue/false控制开关
apiurlURLapi链接,配置教程参看基于 hexo 的友链朋友圈
maxnumbernumber【可选】填写阿拉伯数字,页面展示文章数量,默认20
addnumbernumber【可选】填写阿拉伯数字,每次加载增加的篇数,默认10
opentype_blank_self【可选】’_blank’新标签打开,’_self’本窗口打开,默认为’_blank’
nofollowtrue/false【可选】开启禁止搜索引擎抓取,默认开启
preloadURL【可选】加载动画图片链接
cssURL【可选】开发者接口,自定义css链接
jsURL【可选】开发者接口,自定义js链接
pathstring【可选】字符串,fcircle的路径名称。默认为 fcircle,生成的页面为 fcircle/index.html
front_matterobject【可选】写法见上文示例,fcircle页面的 front_matter 配置

魔改源码方案

旧版修改源码方案

资源下载

由于本教程涉及的所有修改对缩进格式等有严格要求,担心自己控制不好的可以直接下载静态资源。参照教程进行修改。

魔改步骤

点击查看魔改正文
  1. 新建[Blogroot]\themes\butterfly\layout\includes\page\fcircle.pug;
    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
    #article-container
    if top_img === false
    h1.page-title= page.title
    if theme.fcircle.enable
    #friend_link_circle
    .article-sort-title 统计信息
    .article-sort
    #info_user_poor
    .chart
    span.friend_post_info_title 当前友链数:
    span.friend_post_info_number {{user_lenth}}个
    br
    span.friend_post_info_title 失败数:
    span.friend_post_info_number {{error}}个
    br
    .chart
    span.friend_post_info_title 活跃友链数:
    span.friend_post_info_number {{unique_live_link}}个
    br
    span.friend_post_info_title 当前库存:
    span.friend_post_info_number {{listlenth}}篇
    br
    .chart
    span.friend_post_info_title 今日更新:
    span.friend_post_info_number {{today_post}}篇
    br
    span.friend_post_info_title 最近更新:
    span.friend_post_info_number {{last_update_time}}
    div(v-for='datalist in datalist_slice')
    .article-sort-title(v-if='datalist[2]-maxnumber<0') {{datalist[0]}}
    .article-sort
    .article-sort-item(v-if='item[6]-maxnumber<0' v-for='(item,i) in datalist[1]')
    a.article-sort-item-img(:target='opentype' :href='item[2]' :title='item[0]')
    img.entered.loaded(onerror=`this.onerror=null;this.src='` + url_for(theme.error_img.flink) + `'` data-ll-status='loaded' :src='item[4]')
    .article-sort-item-info
    .article-sort-item-time
    i.far.fa-user
    span {{item[3]}}
    .friend_post_time
    i.far.fa-calendar-alt
    time.post-meta-date-created(:datetime='item[1]' :title='item[1]') {{item[1]}}
    a.article-sort-item-title(:target='opentype' :href='item[2]' :title='item[0]') {{item[0]}}
    div(style='text-align: center')
    button.load_button(v-if='loadmore_display' type='button' @click='addmaxnumber()') 加载更多...
    script.
    requests_url = '!{url_for(theme.fcircle.apiurl)}'
    script(defer data-pjax src=url_for(theme.CDN.fcircle))
    != page.content
  2. 新建[Blogroot]\themes\butterfly\source\css\_page\fcircle.styl,
    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
    if hexo-config('fcircle.enable')
    #info_user_poor
    display flex
    border-radius 2px
    .friend_post_info_title
    font-weight 700

    .friend_post_info_number
    float right

    .chart
    align-items flex-start
    flex 1
    width 100px
    height 60px
    margin 20px 0px
    span
    width 120px
    white-space nowrap
    overflow hidden
    text-overflow ellipsis

    .article-sort-item-time
    span
    padding-left 10px
    padding-right 10px
    a.article-sort-item-title
    -webkit-line-clamp: 1
    .load_button
    -webkit-transition-duration 0.4s
    transition-duration 0.4s
    text-align center
    border 1px solid #ededed
    border-radius .3em
    display inline-block
    background transparent
    color #555
    padding .5em 1.25em
    &:hover
    color #3090e4
    border-color #3090e4

    @media screen and (min-width: 500px)
    .friend_post_time
    float right

    @media screen and (max-width: 500px)
    #info_user_poor
    padding 10px
    flex-direction column
    max-height 200px
    .chart
    flex 0
    width 100%
    height 160px
    margin 0
  3. 新建[Blogroot]\themes\butterfly\source\js\fcircle.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
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    var friend_link_circle = new Vue({
    el: '#friend_link_circle',
    data: {
    datalist: [],
    datalist_slice:[],
    maxnumber:20,
    addnumber:10,
    display:true,
    loadmore_display:false,
    listlenth:0,
    today_post:0,
    last_update_time:'',
    user_lenth:'',
    error:0,
    unique_live_link:0,
    opentype:'_blank' //'_blank'打开新标签,'_self'本窗口打开
    },
    methods:{
    unique (arr) {
    return Array.from(new Set(arr))
    },
    formatDate(strDate) {
    try{
    let date = new Date(Date.parse(strDate.replace(/-/g, "/")));
    let gettimeoffset = 0
    if (new Date().getTimezoneOffset()){
    gettimeoffset = new Date().getTimezoneOffset();
    }
    else{
    gettimeoffset = 8;
    }
    let timeoffset = gettimeoffset * 60 * 1000;
    let len = date.getTime();
    let date2 = new Date(len - timeoffset);
    let sec = date2.getSeconds().toString();
    let min = date2.getMinutes().toString();
    if (sec.length === 1) {
    sec = "0" + sec;
    }
    if (min.length === 1) {
    min = "0" + min;
    }
    return date2.getFullYear().toString() + "/" + (date2.getMonth() + 1).toString() + "/" + date2.getDate().toString() + " " + date2.getHours().toString() + ":" + min + ":" + sec
    }catch(e){return ""}
    },
    timezoon(){
    let time = this.datalist_slice[0][1][0][5];
    return this.formatDate(time)
    },
    todaypost(){
    let date= new Date();
    let year = date.getFullYear();
    let month =(date.getMonth() + 1).toString();
    let day = (date.getDate()).toString();
    if (month.length === 1) {
    month = "0" + month;
    }
    if (day.length === 1) {
    day = "0" + day;
    }
    return year + "-" + month + "-" + day
    },
    addmaxnumber(){
    this.maxnumber = this.maxnumber + this.addnumber;
    if (this.maxnumber >= this.listlenth){
    this.loadmore_display=false;
    }
    },
    slice(data){
    let monthlist=[];
    let datalist=[];
    let data_slice = data;
    for (let item in data_slice) {
    data_slice[item].push(item);
    if (data_slice[item][1].lenth !== 10) {
    let list = data_slice[item][1].split('-')
    if (list[1].length < 2){
    list[1]="0" + list[1]
    }
    if (list[2].length < 2){
    list[2]="0" + list[2]
    }
    data_slice[item][1] = list.join('-')
    }
    let month=data_slice[item][1].slice(0,7);
    if(monthlist.indexOf(month) !== -1){
    console.log(month);
    datalist[monthlist.length-1][1].push(data_slice[item]);
    }
    else{
    monthlist.push(month);
    datalist.push([month,[data_slice[item]]]);
    }
    }

    for (let mounthgroup of datalist){
    mounthgroup.push(mounthgroup[1][0][6]);
    }
    console.log(datalist);
    return datalist
    }
    },
    mounted: function () {

    fetch(requests_url).then(
    data => data.json()
    ).then(
    data => {
    let today = this.todaypost();
    let Datetody = new Date(today);
    for (let item = 0; item <data[1].length ;item++){
    let Datedate = new Date(data[1][item][1])
    if (Datedate>Datetody){
    data[1].splice(item --, 1);
    console.log('穿越了');
    }
    }
    this.datalist = data[1];
    this.listlenth = data[1].length;
    this.user_lenth = data[0].length;
    this.datalist_slice = this.slice(data[1]);
    this.last_update_time =this.timezoon();
    this.loadmore_display = true;
    let link_list=[];
    for (let item of data[1]){
    if (item[1] === today ){
    this.today_post +=1;
    }
    link_list.push(item[3]);
    }
    let arr = this.unique(link_list);
    this.unique_live_link = arr.length;
    for (let item of data[0]){
    if (item[3] === 'true' ){
    this.error +=1;
    }
    }
    }
    )

    }
    })
  4. 修改[Blogroot]\themes\butterfly\layout\page.pug,添加朋友圈页面的选项:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
      extends includes/layout.pug

    block content
    #page
    case page.type
    when 'tags'
    include includes/page/tags.pug
    when 'link'
    include includes/page/flink.pug
    when 'categories'
    include includes/page/categories.pug
    when 'artitalk'
    include includes/page/artitalk.pug
    + when 'fcircle'
    + include includes/page/fcircle.pug
    default
    include includes/page/default-page.pug

    if page.comments !== false && theme.comments && theme.comments.use
    - var commentsJsLoad = true
    !=partial('includes/third-party/comments/index', {}, {cache:theme.fragment_cache})
  5. 修改[Blogroot]\themes\butterfly\layout\includes\additional-js.pug,添加vue.js依赖的配置项,butterfly_v3.4.0+移除了jquery,所以没有script(src=url_for(theme.CDN.jquery))这一行。
    1
    2
    3
    4
    5
    6
      div
    script(src=url_for(theme.CDN.utils))
    script(src=url_for(theme.CDN.main))
    + script(src=url_for(theme.CDN.vue))
    if theme.translate.enable
    script(src=url_for(theme.CDN.translate))
  6. 修改[Blogroot]\_config.butterfly.yml,添加友链页面菜单栏,CDN链接和配置项:
    • 添加菜单栏
      1
      2
      3
      4
      5
      6
      7
      8
        menu:
      博客: / || fas fa-home
      时间轴: /archives/ || fas fa-archive
      标签: /tags/ || fas fa-tags
      分类: /categories/ || fas fa-folder-open
      + 朋友圈: /fcircle/ || fa fa-puzzle-piece
      友人帐: /link/ || fas fa-link
      留言板: /comments/ || fas fa-comments
    • 添加CDN配置项:
      1
      2
      3
      4
      5
      6
      7
      8
        CDN:
      # main
      main_css: /css/index.css
      jquery: https://npm.elemecdn.com/jquery@latest/dist/jquery.min.js
      main: /js/main.js
      utils: /js/utils.js
      + vue: https://npm.elemecdn.com/vue@2.6.11 # vue.js依赖
      + fcircle: /js/fcircle.js # 友链朋友圈
    • 添加配置项:
      1
      2
      3
      4
      5
      #友链朋友圈
      fcircle:
      enable: true #开关友链
      apiurl: https://hexo-circle-of-friends-api.vercel.app/api
      # 改为冰老师原帖步骤三:配置 vercel api中获取的api链接
  7. 使用指令生成友链页面,并添加样式
    • [Blogroot]路径下打开终端,运行以下指令生成朋友圈页面:
      1
      hexo new page fcircle
    • 打开刚刚生成的朋友圈页面[Blogroot]\source\fcircle\index.md,添加页面类型,以引入友链朋友圈:
      1
      2
      3
      4
      5
      6
        ---
      title: Akilarの朋友圈
      date: 2021-02-11 18:09:00
      + type: "fcircle"
      comments: "true"
      ---
  8. 运行hexo cleanhexo generate以及hexo server三件套以后就可以看到友链预览了。

TO DO

前端配置魔改

前端配置npm插件化

多主题适配

重写统计部分UI