使用Hugo框架搭建博客的过程 - 功能拓展
前言
本文介绍一些拓展功能,如文章页面功能增加二级菜单,相关文章推荐和赞赏。另外,使用脚本会大大简化写作后的上传流程。
文章页面功能
这部分功能的拓展主要是用前端的JS和CSS,如果对前端不了解,可以参考放在Github上的网站源码。
二级菜单
导航栏放都太多链接不分主次,不够简洁。尝试多种方案后,做出了现在的二级菜单。
- 使用bootstrap框架
 引入了太多样式,界面被扰乱,不够友好。
- Select标签
 样式单一,不能修改。
- Hover属性
 多个二级菜单不能使用相同参数的绝对定位,拓展太麻烦。
最终方案是:引入Jquery,使用Jquery的slideToggle()方法。可拓展,不影响原有界面。步骤如下。
- 调整下拉菜单的样式
/* 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*/
- 调用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;
}
- 修改菜单模板,详细代码参考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 -}}
- 菜单配置,子菜单设置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;">「 {{- T "thanksSponsor" -}} 」</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>
T和i18n函数是翻译函数,按照不同的语言,使用对应语言的字符串。参考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。
文章同步到服务器的步骤如下。
- 本地创建博客文章
- 配置SSH免密登陆
- 使用Python脚本生成网站静态资源,提交变更
- 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命令,确定服务器同步是否正常。
代码改变世界,现在,写作多么简单。
参考
使用Hugo框架搭建博客的过程 - 功能拓展的更多相关文章
- 使用Hugo框架搭建博客的过程 - 页面模板
		前言 最初在制作友链界面时,没有学习Hugo框架,一头雾水.网上有关的教程甚少,只能去学一遍Hugo. 在学习Hugo的过程中,了解了列表模板,分类模板.开发了几个功能页面,如:留言板,友链,记忆分类 ... 
- 使用Hugo框架搭建博客的过程 - 前期准备
		前言 这篇教程介绍了如何搭建这样效果的博客. 所需步骤 可以从这样的角度出发: 注册域名. 使用CDN加快网站访问速度. 网站内容需要部署在服务器或对象存储平台上. 重要的是放什么内容.博客需要选择框 ... 
- 使用Hugo框架搭建博客的过程 - 主题配置
		前言 博客部署完成后,恭喜你可以发表第一篇:Hello world!但是LoveIt这么好用的主题,不配置一番可惜了. 基本功能配置 主题配置最好参考已有的配置,比如LoveIt作者写的介绍,还有主题 ... 
- 使用Hugo框架搭建博客的过程 - 部署
		前言 完成前期的准备工作后,在部署阶段需要配置服务器或对象存储服务. 对象存储和服务器对比 对象存储平台 国内有阿里云OSS.腾讯COS.又拍云.七牛云等.国外有Github Pages.Netlif ... 
- 用 Hugo 快速搭建博客
		用 Hugo 搭建博客 Hugo 是一个用 Go 编写的静态站点生成器,生成速度很快 下面是具体操作: 1.安装 Hugo Windows 用户 使用 Chocolatey 或者 Scoop 快速安装 ... 
- 在Windows下使用Hexo+GithubPage搭建博客的过程
		1.安装Node.js 下载地址:传送门 去 node.js 官网下载相应版本,进行安装即可. 可以通过node -v的命令来测试NodeJS是否安装成功 2.安装Git 下载地址:传送门 去 Git ... 
- 使用Hexo框架搭建博客,并部署到github上
		开发背景:年后回来公司业务不忙,闲暇时间了解一下node的使用场景,一篇文章吸引了我15个Nodejs应用场景,然后就被这个hexo框架吸引了,说时迟,那时快,赶紧动手搭建起来,网上找了好多资料一天时 ... 
- Hugo hexo 搭建博客系列1:自己的服务器
		hexo jekyll https://hexo.io/zh-cn/ http://theme-next.iissnan.com/getting-started.html Hexo 是高效的静态站点生 ... 
- hugo + nginx 搭建博客记录
		作为一个萌新Gopher,经常逛网站能看到那种极简的博客,引入眼帘的不是花里胡哨的图片和样式,而是黑白搭配,简简单单的文章标题,这种风格很吸引我.正好看到煎鱼佬也在用这种风格的博客,于是卸载了我的wo ... 
随机推荐
- 源码篇:Flutter Provider的另一面(万字图文+插件)
			前言 阅读此文的彦祖,亦菲们,附送一枚Provider模板代码生成插件! 我为啥要写这个插件呢? 此事说来话短,我这不准备写解析Provider源码的文章,肯定要写这框架的使用样例啊,然后再哔哔源码呀 ... 
- [Django高级之中间件、csrf跨站请求伪造]
			[Django高级之中间件.csrf跨站请求伪造] Django中间件 什么是中间件? Middleware is a framework of hooks into Django's request ... 
- Netty 框架学习 —— Netty 组件与设计
			Channel.EventLoop 和 ChannelFuture 这一节将对 Channel.EventLoop 和 ChannelFuture 类进行讨论,它们组合在一起,可以被认为是 Netty ... 
- 06.ElementUI 2.X 源码学习:源码剖析之工程化(一)
			0x.00 前言 在用了5章篇幅 ElementUI源码学习:从零开始搭建Vue组件库汇总 讲解了如何编写一个组件.发布npm以及生成展示文档之后.接下来将分析Element项目的代码结构,学习其工程 ... 
- 本地软件仓库配置及NFS安装
			[root@localhost ~]# mount /dev/cdrom /mnt mount: /dev/sr0 is write-protected, mounting read-only [ro ... 
- 【.NET 与树莓派】温度/湿度传感器——SHT30
			SHT3XX 系列的传感,常见的有三种:SHT 30.SHT 31.SHT 35.其中,比较便宜性价比较愉快的是 SHT 30. DHT 11 模块也是检测温度.湿度的,但SHT 11 使用的不是我们 ... 
- 作为一名双非本科毕业的Java程序员,我该如何在日益严重的内卷化中避免被裁?
			前言 对一个 Java 程序员而言,并发编程能否熟练掌握是判断他是不是优秀的重要标准之一.因为并发编程在 Java 语言中最为晦涩的知识点,它涉及内存.CPU.操作系统.编程语言等多方面的基础能力,更 ... 
- Spring源码分析-从@ComponentScan注解配置包扫描路径到IoC容器中的BeanDefinition,经历了什么(一)?
			阅前提醒 全文较长,建议沉下心来慢慢阅读,最好是打开Idea,点开Spring源码,跟着下文一步一步阅读,更加便于理解.由于笔者水平优先,编写时间仓促,文中难免会出现一些错误或者不准确的地方,恳请各位 ... 
- ApplicationListener接口,在spring容器初始化后执行的方法
			一.如果我们希望在Spring容器将所有的Bean都初始化完成之后,做一些操作,那么就可以使用ApplicationListener接口,实现ApplicationListener接口中的onAppl ... 
- Qt中的布局浅析与弹簧的使用,以及Qt居中的两种方法
			1. 布局 为什么要布局: 布局之后窗口的排列是有序的 布局之后窗口的大小发生变化, 控件的大小也会对应变化 如果不对控件布局, 窗口显示出来之后有些控件的看不到的 布局是可以嵌套使用 常用的布局方式 ... 
