目录

LoveIt - 4功能拓展

前言

本文介绍一些拓展功能,如文章页面功能增加二级菜单,相关文章推荐和赞赏。另外,使用脚本会大大简化写作后的上传流程。

文章页面功能

这部分功能的拓展主要是用前端的JS和CSS,如果对前端不了解,可以参考放在Github上的网站源码

二级菜单

导航栏放都太多链接不分主次,不够简洁。尝试多种方案后,做出了现在的二级菜单。

  1. 使用bootstrap框架 引入了太多样式,界面被扰乱,不够友好。
  2. Select标签 样式单一,不能修改。
  3. Hover属性 多个二级菜单不能使用相同参数的绝对定位,拓展太麻烦。

最终方案是:引入Jquery,使用Jquery的slideToggle()方法。可拓展,不影响原有界面。步骤如下。

  1. 调整下拉菜单的样式

     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
    
    /* dropdown menus css*/
    .dropdown {
        position: relative;
        display: inline;
        margin: 0px;
    }
    
    .dropdown-menu {
        position: absolute;
        left: 0;
        z-index: 1000;
        float: left;
        min-width: auto;
        padding: 2px 1px;
        font-size: 14px;
        text-align: left;
        list-style: none;
        background-color: #fff;
        -webkit-background-clip: padding-box;
        background-clip: padding-box;
        border: 1px solid #ccc;
        border-radius: 4px;
        -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
        box-shadow: 0 6px 12px rgba(0, 0, 0, .175);
    }
    
    .dropdown-menu-mobile {
        width: 100%;
        position: relative;
        background: transparent;
        text-align: center;
    }
    
    .dropdown-menu.show {
        display: block;
    }
    
    .dropdown-item {
        display: block;
        margin: .4rem 0.5rem;
        clear: both;
        font-weight: 400;
        color: #212529;
        text-align: inherit;
        white-space: nowrap;
        background-color: transparent;
        line-height: 1.5rem;
    }
    
    .btn {
        vertical-align: inherit;
        font-weight: 400;
        color: #212529;
        text-align: center;
        -webkit-user-select: none;
        user-select: none;
        background-color: transparent;
        font-size: 1rem;
        border-radius: .25rem;
    }
    /* dropdown menus css*/
    
  2. 调用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
    
    $(".dropdown").each(function() {
        $(this).on("click", function(e) {
            // 收起其他菜单
            if (isMobile()) {
                $(".menu").find(".dropdown-menu").not($(this).children("div")).slideUp("fast");    
            } else {
                $(".menu-inner").find(".dropdown-menu").not($(this).children("div")).slideUp("fast");    
            }
            e.stopPropagation(); 
            var downmenu = $(this).children("div");
            // 展开菜单
            downmenu.slideToggle("fast");
            // 点击其他地方,隐藏菜单
            if (downmenu.is(":visible")) {
                $(document).one("click", function() {   
                    downmenu.slideUp("fast");
                });
            }
        });
    });
    
    # 移动端
    function isMobile(){
        return window.matchMedia("only screen and (max-width: 680px)").matches;
    }
    
  3. 修改菜单模板,详细代码参考header.html

     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
    
      <div class="menu" style="overflow: visible">
          <div class="menu-inner">
              {{- range .Site.Menus.main -}}
    
                  {{- /* MultiMenus */ -}}
                  {{ if .HasChildren }}
                      <div class="dropdown menu-item" style="display: inline;">
                          <a class="btn" href="javascript:void(0);" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">{{- .Pre | safeHTML }}{{ .Name }}{{ .Post | safeHTML -}}</a>
                          <div class="dropdown-menu" style="display: none;">
                          {{ range .Children }}
                              {{- $url := .URL | relLangURL -}}
                              {{- with .Page -}}
                                  {{- $url = .RelPermalink -}}
                              {{- end -}}
                              <a class="dropdown-item" href="{{ $url }}" {{ if (urls.Parse $url).Host }} rel="noopener noreffer" target="_blank"{{ end }}>{{- .Pre | safeHTML }} {{ .Name }} {{ .Post | safeHTML -}}</a>
                          {{ end }}
                          </div>
                      </div>                       
                  {{ else }}
                      {{- $url := .URL | relLangURL -}}
                      {{- with .Page -}}
                          {{- $url = .RelPermalink -}}
                      {{- end -}}
                      <a class="menu-item{{ if $.IsMenuCurrent `main` . | or ($.HasMenuCurrent `main` .) | or (eq $.RelPermalink $url) }} active {{ end }}" href="{{ $url }}"{{ with .Title }} title="{{ . }}"{{ end }}{{ if (urls.Parse $url).Host }} rel="noopener noreffer" target="_blank"{{ end }}>                            {{- .Pre | safeHTML }}{{ .Name }}{{ .Post | safeHTML -}}</a>
                  {{ end }}
              {{- end -}}
    
      {{- /* Mobile header */ -}}
      {{- range .Site.Menus.main -}}
          {{- /* MultiMenus */ -}}
          {{ if .HasChildren }}
              <div class="dropdown menu-item">
                  <a class="btn" href="javascript:void(0);" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">{{- .Pre | safeHTML }}{{ .Name }}{{ .Post | safeHTML -}}</a>
                  <div class="dropdown-menu dropdown-menu-mobile" style="display: none">
                  {{ range .Children }}
                      {{- $url := .URL | relLangURL -}}
                      {{- with .Page -}}
                          {{- $url = .RelPermalink -}}
                      {{- end -}}
                      <a class="dropdown-item" href="{{ $url }}" {{ if (urls.Parse $url).Host }} rel="noopener noreffer" target="_blank"{{ end }}>{{- .Pre | safeHTML }} {{ .Name }} {{ .Post | safeHTML -}}</a>
                  {{ end }}
                  </div>
              </div>                       
          {{ else }}
              {{- $url := .URL | relLangURL -}}
              {{- with .Page -}}
                  {{- $url = .RelPermalink -}}
              {{- end -}}
              <a class="menu-item" href="{{ $url }}" title="{{ .Title }}"{{ if (urls.Parse $url).Host }} rel="noopener noreffer" target="_blank"{{ end }}>
                  {{- .Pre | safeHTML }}{{ .Name }}{{ .Post | safeHTML -}}
              </a>
          {{ end }}
          {{- end -}}
    
  4. 菜单配置,子菜单设置parent属性指向父菜单。

      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
    
      [languages.en.menu]
          [[languages.en.menu.main]]
            identifier = "home"
            pre = "<i class='fas fa-home fa-fw'></i>"
            post = ""
            name = "Home"
            url = ""
            title = "Home"
            weight = 1
    
        [[languages.en.menu.main]]
            identifier = "posts"
            pre = "<i class='fas fa-archive fa-fw'></i>"
            post = ""
            name = "Posts"
            url = "posts/"
            title = "all articles"
            weight = 2
    
        [[languages.en.menu.main]]
            identifier = "memory"
            pre = "<i class='fas fa-database fa-fw'></i>"
            post = ""
            name = "Memory"
            url = "memories/"
            title = "left it"
            weight = 3
    
        [[languages.en.menu.main]]
            identifier = "tags"
            pre = "<i class='fas fa-tags fa-fw'></i>"
            post = ""
            name = " Tags"
            url = "tags/"
            title = ""
            weight = 4
    
        [[languages.en.menu.main]]
            identifier = "categories"
            pre = "<i class='fas fa-th-list fa-fw'></i>"
            post = ""
            name = "Categories"
            url = "categories/"
            title = ""
            weight = 5
    
        [[languages.en.menu.main]]
            identifier = "share"
            pre = "<i class='fas fa-fan fa-fw'></i>"
            post = ""
            name = "Share"
            title = ""
            weight = 7
    
        [[languages.en.menu.main]]
            identifier = "books"
            pre = "<i class='fas fa-file-alt fa-fw'></i>"
            post = ""
            name = "Book list"
            url = "booklist/"
            title = ""
            weight = 8
            parent = "share"
    
        [[languages.en.menu.main]]
            identifier = "websites"
            pre = "<i class='fas fa-globe fa-fw'></i>"
            post = ""
            name = "Websites"
            url = "websites/"
            title = ""
            weight = 8
            parent = "share"
    
        [[languages.en.menu.main]]
            identifier = "internal"
            pre = "<i class='fas fa-door-closed fa-fw'></i>"
            post = ""
            name = "Doors"
            title = ""
            weight = 8
    
        [[languages.en.menu.main]]
            identifier = "message-board"
            pre = "<i class='fas fa-comments fa-fw'></i>"
            post = ""
            name = "Message Board"
            url = "message-board/"
            title = ""
            weight = 8
            parent = "internal"
    
        [[languages.en.menu.main]]
            identifier = "favorites"
            pre = "<i class='fas fa-star fa-fw'></i>"
            post = ""
            name = "Favorites"
            url = "favorites/"
            title = ""
            weight = 8
            parent = "internal"
    
        [[languages.en.menu.main]]
            identifier = "milestone"
            pre = "<i class='fas fa-monument fa-fw'></i>"
            post = ""
            name = "Milestone"
            url = "milestone/"
            title = ""
            weight = 8
            parent = "internal"
    
        [[languages.en.menu.main]]
            identifier = "links"
            pre = "<i class='fas fa-user-friends fa-fw'></i>"
            post = ""
            name = "Links"
            url = "links/"
            title = ""
            weight = 9
            parent = "internal"
    
        [[languages.en.menu.main]]
            identifier = "about"
            pre = "<i class='fas fa-user-secret fa-fw'></i>"
            post = ""
            name = "About"
            url = "about/"
            title = ""
            weight = 10
            parent = "internal"
    

相关文章推荐

参考Related Content themes\LoveIt\layouts\posts\single.html添加以下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{{- /*see also*/ -}}           
# 显示tag分类相关的前5篇文章
{{ $related := .Site.RegularPages.RelatedIndices . "tags" | first 5 }}
{{ with $related }}
    <div id="related-article">
        <p>{{- T "seeAlso" -}}</p>
        <ul>
            {{ range . }}
            <li><a href="{{ .RelPermalink }}">{{ .Title }}</a></li>
            {{ end }}
        </ul>
    </div>
{{ end }}

赞赏

赞赏功能需要在data\imgURL.toml中添加图片的URL。

1
2
alipay = "https://img.xiaodejiyi.com/img/alipay.jpg"
weichatPay = "https://img.xiaodejiyi.com/img/wechat%20pay.jpg"

然后在模板文件themes\LoveIt\layouts\posts\single.html添加以下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
{{- /* sponsor */ -}}
<div style="text-align:center;margin-bottom:30px;">
    <h5 style="font-weight:600;margin-bottom:10px;">&nbsp;{{- T "thanksSponsor" -}}&nbsp;</h5>
    <button id="rewardButton"><span>{{- T "tips" -}}</span></button>
    <div id="QR" style="display: none;">
        <div id="wechat" style="display:inline-block">
            <a class="fancybox" rel="group">
                <img id="wechat_qr" src="{{ .Site.Data.imgURL.weichatPay }}" alt="WeChat Pay"></a>
            <h5 style="font-weight:600;margin-top:5px;">{{- T "weichatTip" -}}</h5>
        </div>
        <div id="alipay" style="display: inline-block">
            <a class="fancybox" rel="group">
                <img id="alipay_qr" src="{{ .Site.Data.imgURL.alipay }}" alt="Alipay"></a>
            <h5 style="font-weight:600;margin-top:5px;">{{- T "aliTip" -}}</h5>
        </div>
    </div>
</div>

Ti18n函数是翻译函数,按照不同的语言,使用对应语言的字符串。参考i18n

版权声明

themes\LoveIt\layouts\posts\single.html添加以下代码:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
{{- /*copyright*/ -}}
<div id="copyright-container">
    <ul class="post-copyright">
        <li class="post-copyright-author">
            <strong>{{- T "articleAuthor" -}}:</strong><a href="{{ $.Site.Author.link | default .Site.Home.RelPermalink }}" target="_blank">{{ T "penname" }}</a>
        </li>
        <li class="post-copyright-link">
            <strong>{{- T "articleLink" -}}:</strong><a href="#" target="_blank" title="{{ .Title }}">{{- .Permalink | safeHTML -}}</a>
        </li>
        <li class="post-copyright-license">
            <strong>{{- T "copyRight" -}}:</strong>
            {{- $prestr := printf `<a href="%v" target="_blank" title="CC BY-NC-ND 4.0">%v</a>` .Site.Params.footer.license ( T "license" ) -}}
            {{- $laststr := printf `<a href="%v" target="_blank">%v</a>` ($.Site.Author.link | default .Site.Home.RelPermalink) ( T "penname" ) -}}
        {{- dict "preCopyRight" $prestr "afterCopyRight" $laststr | T "copyRightMsg" | safeHTML }}
        </li>
    </ul>
</div>

需要使用T函数翻译并且根据不同语言传递参数。

1
2
[copyRightMsg]
other = "本站所有文章除特别声明外,均采用 {{ .preCopyRight }} 转载请注明来自 {{ .afterCopyRight }} "

.Site.Params.footer.license在config.toml中设置:

license = 'https://creativecommons.org/licenses/by-nc-sa/4.0/'

同步文章到服务器

如果需要了解同步文章到对象存储平台,请参考Hugo 静态网站部署及免费 HTTPS 证书配置OSS常用工具汇总

文章最后的“阅读全文”可以绕过,方法是在Chrome控制台的Network中找到readmore.js,右键Block request URL

文章同步到服务器的步骤如下。

  1. 本地创建博客文章
  2. 配置SSH免密登陆
  3. 使用Python脚本生成网站静态资源,提交变更
  4. Github Action使用rsync同步网站静态资源到服务器,并使用atomic-algolia同步index.json到Algolia。

创建文章

archetypes目录下的创建模板文件,使用hugo new --kind post-bundle posts/my-post生成指定模板的样例文章,不需要每次复制文章头部参数。

archetypes\post-bundle\index.md文件内容

SSH免密登陆

注意!服务器端要配置authorized_keys。参考设置 SSH 通过密钥登录

注册Algolia

使用Algolia搜索引擎为博客提供搜索功能。参考搜索配置

本地同步与备份脚本

在博客目录下执行该脚本,先推送blog到Github的blog仓库,再本地备份。(相信看到这里的读者都有Github帐号了)

 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
import time
import os, sys

def main(msg):
	# 备份blog/
	print("*"*10+"push blog/"+"*"*10, end="\n\n")
	# 生成静态页面
	os.system('hugo')

    # 需要安装Git
	os.system('git add .')
	os.system('git commit -m "{}"'.format(msg))
	os.system('git push')

    local_back = input('\n本地备份?提示: y\n')
    if local_back == 'y':
        #7z、winrar压缩参数是一样的,只有winrar的话,7z换成winrar也可以运行
        os.system(r'7z a D:\src\xxxxx.rar D:\src\xxxxx')
        os.system(r'move D:\src\code\xxxxx.rar D:\backup\xxxxx')
        print('本地备份完成!')

    print('over...')

if __name__ == '__main__':
    msg = input('commit: ')
    main(msg)

如果需要安装Git,试试Scoop,它用来安装应用程序非常方便。

Github Action

blog/文件根目录下创建package.json文件,并在scripts模块中加入"algolia": "atomic-algolia"

1
2
3
4
5
6
7
8
9
{
  "name": "algolia",
  "version": "0.2.0",
  "description": "atomic-algolia package",
  "private": true,
  "scripts": {
    "algolia": "atomic-algolia"
  }
}

在Github的Blog仓库中添加Secrets变量 同步index.json到Algolia需要配置如下变量:

  • ALGOLIA_ADMIN_KEY
  • ALGOLIA_APP_ID
  • ALGOLIA_INDEX_NAME

rsync同步需要配置如下变量

  • HOST
  • PORT
  • USER
  • REMOTE_PATH Nginx中配置的网站根目录
  • SSH_KEY

使用Github Action,创建workflow,复制以下代码。

name: deploy_blog

on: push: branches: [ master ]

jobs: build: runs-on: ubuntu-latest

 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
steps:
  - name: Check Out
    uses: actions/checkout@v2

  #同步blog搜索
  - name: Use Node.js
    uses: actions/setup-node@v1
    with:
      node-version: '12.x'

  - name: Install automic-algolia
    env:
      ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }}
      ALGOLIA_ADMIN_KEY: ${{ secrets.ALGOLIA_ADMIN_KEY }}
      ALGOLIA_INDEX_NAME: zh-cn
      ALGOLIA_INDEX_FILE: "./public/index.json"
    run: | 
      npm install atomic-algolia
      npm run algolia

    # 同步英语文章的json
  - name: en json
    env:
      ALGOLIA_APP_ID: ${{ secrets.ALGOLIA_APP_ID }}
      ALGOLIA_ADMIN_KEY: ${{ secrets.ALGOLIA_ADMIN_KEY }}
      ALGOLIA_INDEX_NAME: en
      ALGOLIA_INDEX_FILE: "./public/en/index.json"
    run: | 
      npm run algolia

  - name: deploy to server
    uses: AEnterprise/rsync-deploy@v1.0
    env:
      DEPLOY_KEY: ${{ secrets.SSH_KEY }}
      # avzr参数,增量备份本地文件。-delete 删除目标地址中本地没有的文件
      ARGS: "-avzr --delete"
      SERVER_PORT: ${{ secrets.PORT }}
      FOLDER: "./public/"
      SERVER_IP: ${{ secrets.HOST }}
      USERNAME: ${{ secrets.USER }}
      SERVER_DESTINATION: ${{ secrets.REMOTE_PATH }}

如果出现问题,先在本地创建linux虚拟机,测试rsync命令,确定服务器同步是否正常。

代码改变世界,现在,写作多么简单。

参考