使用Hugo框架搭建博客的过程 - 功能拓展

前言

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

文章页面功能

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

二级菜单

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

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

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

  1. 调整下拉菜单的样式
/* 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*/
  1. 调用JS
$(".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;
}
  1. 修改菜单模板,详细代码参考header.html
<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 -}}
  1. 菜单配置,子菜单设置parent属性指向父菜单。
  [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添加以下代码:

{{- /*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。

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

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

{{- /* 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添加以下代码:

{{- /*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函数翻译并且根据不同语言传递参数。

[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帐号了)

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"

{
  "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
    
    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命令,确定服务器同步是否正常。

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

参考

posted @ 2021-01-16 14:34  朤尧  阅读(848)  评论(0编辑  收藏  举报