使用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命令,確定服務器同步是否正常。

代碼改變世界,現在,寫作多么簡單。

參考


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM