Featured image of post Hugo-stackの美化 III

Hugo-stackの美化 III

可能是今年最后一次装修啦

本文属于 Hugo 博客美化 系列:
  1. Hugo-stackの美化
  2. Hugo-stackの美化 II
  3. Hugo 短代码
  4. Hugo-stackの美化 III (本文)

按照惯例,如果没有特别说明,css样式都是在 assets/scss/custom.scss 文件里修改,短代码的html文件放在 layouts/shortcodes 文件夹里。

谷歌robots

hugo博客seo优化

【建站技术】在你的 Hugo 博客中使用 Google Analytics 来统计和分析流量数据

链接样式

偶然看到了一个自己很喜欢的链接样式: https://www.shirakii.com/post/latex-chinese-record/

但是作者没有公开它的配置,于是自己手搓了一版:

 1a.link {
 2  color: dodgerblue;
 3  font-weight: 600;
 4  padding: 0 2px;
 5  box-shadow: none;
 6  cursor: url('/img/link.png'), pointer !important;
 7  position: relative;
 8  display: inline;
 9  box-decoration-break: clone;
10  -webkit-box-decoration-break: clone;
11  
12  background-image: linear-gradient(to right, dodgerblue, dodgerblue);
13  background-size: 0 2px;
14  background-position: left 100%;
15  background-repeat: no-repeat;
16  transition: background-size 0.3s ease, background-position 0s ease 0.3s;
17  padding-bottom: 2px; /* 增加底部内边距 */
18}
19
20a.link:hover {
21  background-size: 100% 2px;
22  box-shadow: none;
23  text-decoration: none;
24  background-position: left 100%;
25  transition: background-size 0.3s ease;
26}
27
28a.link:not(:hover) {
29  background-position: right 100%;
30  background-size: 0 2px;
31  transition: background-size 0.3s ease;
32}
33
34[data-scheme="light"] a.link {
35  color: deepskyblue;
36  background-image: linear-gradient(to right, deepskyblue, deepskyblue);
37}

不过发现用这种跳转链接不会出现上面的scss效果:<a href="/shortcodes" target="_blank">点我看看</a>

点我看看。修改代码:

 1a[linkscss="true"],
 2a.link {
 3  color: dodgerblue;
 4  font-weight: 600;
 5  padding: 0 2px;
 6  box-shadow: none;
 7  cursor: url('/img/link.png'), pointer !important;
 8  position: relative;
 9  display: inline;
10  box-decoration-break: clone;
11  -webkit-box-decoration-break: clone;
12  
13  background-image: linear-gradient(to right, dodgerblue, dodgerblue);
14  background-size: 0 2px;
15  background-position: left 100%;
16  background-repeat: no-repeat;
17  transition: background-size 0.3s ease, background-position 0s ease 0.3s;
18  padding-bottom: 4px; /* 增加底部内边距 */
19}
20
21a[linkscss="true"]:hover,
22a.link:hover {
23  background-size: 100% 2px;
24  box-shadow: none;
25  text-decoration: none;
26  background-position: left 100%;
27  transition: background-size 0.3s ease;
28}
29
30a[linkscss="true"]:not(:hover),
31a.link:not(:hover) {
32  background-position: right 100%;
33  background-size: 0 2px;
34  transition: background-size 0.3s ease;
35}
36
37[data-scheme="light"] a[linkscss="true"],
38[data-scheme="light"] a.link {
39  color: deepskyblue;
40  background-image: linear-gradient(to right, deepskyblue, deepskyblue);
41}

于是 <a href="/shortcodes" target="_blank" linkscss="true">点这里</a> 就能出现scss效果了:点这里

文章添加点赞按钮

参考■■Loading:《hugo装修日志10》■■,说的很详细了。

系列文章

参考在 Hugo 里显示系列文章,新建 layouts/partials/series.html

 1{{ range $index, $series := .Params.series }}
 2<div class="article-list article-series">
 3    <header>
 4        <div class="series-section-title">
 5            {{ i18n "partOf" }}
 6            <a href="{{ "/series/" | relLangURL }}{{ . | urlize }}" linkscss="true">{{ $series }}</a>
 7            {{ i18n "series" }}:
 8        </div>
 9    </header>
10    
11    <div class="article-series-container">
12        <ol>
13            {{ range $ind, $post := $.Site.Pages.ByDate }}
14                {{ if in $post.Params.series $series }}
15                    <li class="article-series-item">
16                        {{ if eq $post.Permalink $.Page.Permalink }}
17                            <span class="article-series-active">{{ $post.Params.title }} ({{ i18n "currentArticle" }})</span>
18                        {{ else }}
19                            <a href="{{ $post.Permalink }}" linkscss="true">{{ $post.Params.title }}</a>
20                        {{ end }}
21                    </li>
22                {{ end }}
23            {{ end }}
24        </ol>
25    </div>
26</div>
27{{ end }}

layouts/_default/single.html 对应位置加入:

1{{ if .Params.links }}
2    {{ partial "article/components/links" . }}
3{{ end }}
4
5<!-- Add the series partial here -->
6{{ partial "series.html" . }}
7
8{{ partial "article/components/related-content" . }}

hugo.yaml 最后加入:

1taxonomies:
2    category: categories
3    tag: tags
4    series: series

在多语言配置文件 i18n/zh-cn.yaml 最后面加入:

1partOf:
2    other: "本文属于"
3
4series:
5    other: "系列"
6
7currentArticle:
8    other: "本文"

assets/scss/custom.scss 最后加入:

 1.article-series {
 2  margin: 2rem 0;
 3  padding: 1rem;
 4  gap: 0;
 5  color: var(--card-text-color-main);
 6  background-color: var(--card-background);
 7  border-radius: var(--card-border-radius);
 8  box-shadow: var(--shadow-l1);
 9}
10
11.article-series-item {
12  margin-bottom: 0.8rem;
13}
14
15.series-section-title {
16  margin-top: 8px;
17  margin-left: 8px;
18}
19
20.section-description {
21  color: var(--card-text-color-main);
22  font-weight: bold;
23}
24
25.article-series-container {
26  margin-bottom: -8px;
27}

新建 layouts/series/list.html

 1{{ define "body-class" }}template-series{{ end }}
 2{{ define "main" }}
 3    <header>
 4        {{/* Display the series title and description */}}
 5        <h2 class="section-title">{{ .Title }} {{ i18n "series" }}</h2>
 6        {{ if .Description }}
 7        <div class="section-description">
 8            {{ .Description }}
 9        </div>
10        {{ end }}
11    </header>
12
13    {{/* Get the pages associated with this specific series term */}}
14    {{ $pagesInSeries := .Pages }}
15
16    {{/* Check if there are any pages in this series */}}
17    {{ if $pagesInSeries }}
18        {{/* Group the pages in this series by year, ordered reverse chronologically (newest year first) */}}
19        {{ range ($pagesInSeries.GroupByDate "2006").Reverse }}
20            {{/* Create an ID for the year group, e.g., "2023" */}}
21            {{ $id := lower (replace .Key " " "-") }}
22            <div class="archives-group" id="{{ $id }}">
23                {{/* Display the year as a section title, linking to the ID */}}
24                <h2 class="archives-date section-title"><a href="{{ $.RelPermalink }}#{{ $id }}">{{ .Key }}</a></h2>
25                {{/* Use the compact article list style */}}
26                <div class="article-list--compact">
27                    {{/* Range through the pages within this year group (already sorted by date by Hugo) */}}
28                    {{ range .Pages }}
29                        {{/* Display each article using the compact partial */}}
30                        {{ partial "article-list/compact" . }}
31                    {{ end }}
32                </div>
33            </div>
34        {{ end }}
35    {{ else }}
36        <p>{{ i18n "noPostsInSeries" | default "There are no articles in this series yet." }}</p>
37        {{/* Optional: Add a default message or translation for when a series is empty */}}
38        {{/* You might need to add "noPostsInSeries" to your i18n files */}}
39    {{ end }}
40
41    {{/* Footer partial */}}
42    {{ partialCached "footer/footer" . }}
43{{ end }}

新建 layouts/series/terms.html

 1{{ define "body-class" }}template-series{{ end }}
 2{{ define "main" }}
 3    <header>
 4        {{- $taxonomy := $.Site.GetPage "taxonomyTerm" "series" -}}
 5        {{- $terms := $taxonomy.Pages -}}
 6        {{ if $terms }}
 7        <h2 class="section-title">{{ i18n "series" }}</h2>
 8        <div class="subsection-list">
 9            <div class="article-list--tile" style="display: flex; flex-direction: column;">
10                {{ range $terms }}
11                    <div class="series-group" style="flex: 1 auto; margin: 10px;">
12                        <h3 class="series-title">
13                            <a href="{{ .Permalink }}">{{ .Title }}</a>
14                        </h3>
15                        {{ if .Description }}
16                        <div class="series-description">
17                            {{ .Description }}
18                        </div>
19                        {{ end }}
20                        <div class="article-list--horizontal" style="display: flex; overflow-x: auto;">
21                            {{ $articles := where .Site.RegularPages "Params.series" "intersect" (slice .Title) }}
22                            {{ range $index, $element := $articles }}
23                            <div class="article-tile" style="flex: 0 0 auto; margin: 5px;">
24                                {{ partial "article-list/tile" (dict "context" $element "size" "250x150" "Type" "taxonomy") }}
25                            </div>
26                            {{ end }}
27                        </div>
28                    </div>
29                {{ end }}
30            </div>
31        </div>
32        {{ end }}
33    </header>
34
35    {{ partialCached "footer/footer" . }}
36{{ end }}

然后只需要在文章头部加入:series: ["Hugo 博客搭建"] 就可以啦。

过期提醒

参考在 MemE 主题文章开头添加过时提醒。由于我不想每篇文章开头都加上这个提醒,所以把它写成了短代码:

 1<!-- layouts/shortcodes/expiration-notice.html -->
 2<div id="update-info" class="article-notice update-notice">
 3    <span class="notice-icon">
 4        {{ partial "helper/icon" "clock" }}
 5    </span>
 6    <span class="notice-content">
 7        <p id="update-text">上次更新于 {{ .Page.Lastmod.Format "2006-01-02" }},部分内容可能已经过期。</p>
 8    </span>
 9</div>
10
11<script>
12    document.addEventListener('DOMContentLoaded', function() {
13        const lastModified = new Date("{{ .Page.Lastmod.Format "2006-01-02T15:04:05-0700" }}");
14        const now = new Date();
15        const duration = now - lastModified;
16        const days = Math.floor(duration / (1000 * 60 * 60 * 24));
17        const years = Math.floor(days / 365);
18        const remainingDays = days % 365;
19        
20        let updateText = "";
21        if (years >= 1) {
22            updateText = `上次更新于 ${years}${remainingDays} 天前,部分内容可能已经过期。`;
23        } else {
24            updateText = `上次更新于 ${days} 天前,部分内容可能已经过期。`;
25        }
26        
27        document.getElementById("update-text").textContent = updateText;
28    });
29</script>

 1.article-notice {
 2  display: flex;
 3  align-items: center;
 4  padding: 0.75rem 1rem;
 5  margin: 1rem 0;
 6  border-radius: 0.8rem;
 7  background-color: var(--card-background);
 8  border-left: 3.5px solid var(--accent-color);
 9}
10
11.update-notice {
12  background-color: rgba(var(--primary-color-rgb), 0.1);
13  border-left-color: var(--primary-color);
14}
15
16.notice-icon {
17  margin-right: 0.75rem;
18  color: var(--primary-color);
19  margin-top: 0.75rem;
20}
21
22.notice-content p {
23  margin: 0;
24  color: crimson;
25  font-weight: bold;
26}
27
28[data-scheme=dark] .notice-content p {
29  color: gold;
30}

在文章开头或者任意地方加入:

1{{< expiration-notice >}}

上次更新于 2025-09-16,部分内容可能已经过期。


代码变化显示

参考实验田-diffcode,新建 /layouts/partials/diffcode.html

 1{{- $diffBlock := (. | replaceRE "(?m:^```\\w+)" "```diff") -}}
 2{{- $diffBlock := (index (split $diffBlock "```") 1) -}}
 3{{- $diffBlock := ($diffBlock | replaceRE "(?m:^([+\\-~])?.*$)" "$1") -}}
 4{{- $diffBlock := ($diffBlock | replaceRE "(?m:^$)" "." | replaceRE "\n" "") -}}
 5
 6{{- $lines := split $diffBlock "" -}}
 7{{- $mainBlock := (. | replaceRE "(?m:^[+\\-~])" "") | markdownify -}}
 8
 9{{- $lineCount := 0 -}}
10{{- $output := "" -}}
11
12{{- $diffBlock := (. | replaceRE "(?m:^```\\w+)" "```diff") -}}
13{{- $diffBlock := (index (split $diffBlock "```") 1) -}}
14{{- $diffBlock := ($diffBlock | replaceRE "(?m:^([+\\-~])?.*$)" "$1") -}}
15{{- $diffBlock := ($diffBlock | replaceRE "(?m:^$)" "." | replaceRE "\n" "") -}}
16
17{{- $lines := split $diffBlock "" -}}
18{{- $mainBlock := (. | replaceRE "(?m:^[+\\-~])" "") | markdownify -}}
19
20{{- $lineCount := 0 -}}
21{{- $output := "" -}}
22
23{{- range $i, $l := (split $mainBlock `<span class="line">`) -}}
24    {{- if gt $i 0 -}}
25        {{- $lineClass := "" -}}
26        {{- if and (gt $lineCount 0) (lt $lineCount (len $lines)) -}}
27            {{- if eq (index $lines (sub $lineCount -1)) "+" -}}
28                {{- $lineClass = "diff-add" -}}
29            {{- else if eq (index $lines (sub $lineCount -1)) "-" -}}
30                {{- $lineClass = "diff-remove" -}}
31            {{- else if eq (index $lines (sub $lineCount -1)) "~" -}}
32                {{- $lineClass = "diff-highlight" -}}
33            {{- end -}}
34        {{- end -}}
35        
36        {{- if $lineClass -}}
37            {{- $output = printf "%s<span class=\"line %s\">" $output $lineClass -}}
38        {{- else -}}
39            {{- $output = printf "%s<span class=\"line\">" $output -}}
40        {{- end -}}
41        
42        {{- $lineCount = add $lineCount 1 -}}
43    {{- else -}}
44        {{- $output = $l -}}
45    {{- end -}}
46    
47    {{- if gt $i 0 -}}
48        {{- $output = printf "%s%s" $output $l -}}
49    {{- end -}}
50{{- end -}}
51
52{{ $output | safeHTML }}

新建 /layouts/shortcodes/diffcode.html

1{{ partial "diffcode.html" .Inner }}

/assets/scss/custom.scss 中加入:

 1.highlight {
 2  .line {
 3      &.diff-add {
 4          background-color: rgba(0, 255, 0, 0.2);
 5          
 6          &::before {
 7              content: "+";
 8              color: green;
 9              position: absolute;
10              left: 0.5em;
11              user-select: none;
12          }
13      }
14      
15      &.diff-remove {
16          background-color: rgba(255, 0, 0, 0.2);
17          
18          &::before {
19              content: "-";
20              color: red;
21              position: absolute;
22              left: 1.1rem;
23              user-select: none;
24          }
25      }
26      
27      &.diff-highlight {
28          background-color: rgba(255, 255, 0, 0.2);
29          
30          &::before {
31              content: "~";
32              color: orange;
33              position: absolute;
34              left: 0.5em;
35              user-select: none;
36          }
37      }
38  }
39}
40
41// 确保行号正确对齐
42.highlight table td {
43  padding: 5px;
44}
45
46.highlight table pre {
47  margin: 0;
48}
49
50// 为行号留出足够空间
51.highlight .lnt, .highlight .ln {
52  margin-right: 0.8em;
53  padding: 0 0.4em 0 0.4em;
54}

可以这样使用这个短代码:

1{{< diffcode >}}
2```python
3def hello_world():
4+    print("Hello, World!")
5-    print("Goodbye, World!")
6~    return None
7{{< /diffcode >}}

1def hello_world():
2    print("Hello, World!")
3    print("Goodbye, World!")
4    return None

代码块字体样式更改

代码块的字体还是用 ‘JetBrains Mono’ 更有味道。在自定义的scss里加入:

1.highlight *,
2.highlight pre,
3.highlight code,
4.chroma *,
5.chroma code,
6.chroma pre {
7  font-family: 'JetBrains Mono', "LXGW WenKai TC", "Gowun Batang", monospace;
8  font-size: 14px;
9}

个人主页

成品展示:ifantic.de

简单的小主页,原来的看够了,重新弄了一个
Vue

我用的是赛博菩萨 CloudFlare Pages 部署的。先把项目clone到本地,安装node,然后安装pnpm,安装依赖:

1npm install -g pnpm
2pnpm install
3// 预览
4pnpm dev
5// 构建
6pnpm build

然后把 .env.example 文件的名称修改为 .env,修改其中的配置。本地运行 pnpm dev 预览即可。

预览后部署到 Cloudflare Pages

  1. 创建项目:点击 创建应用程序 -> 选择 Pages 选项卡 -> 点击 上传资源
  2. 上传文件:给你的项目起个名字,然后将本地 dist 文件夹内的所有文件和子文件夹拖拽到上传区域,或者通过浏览文件选择 dist 文件夹。
  3. 部署站点:点击 部署站点Cloudflare 会上传你的文件并完成部署。

修复站点名称字体不一致

public/fontPacifico-Regular-all.ttf 替换原来的 Pacifico-Regular.ttf

修复默认的天气api

修改 src/api/index.js 第73行:

1// https://api.oioweb.cn/doc/weather/GetWeather
2export const getOtherWeather = async () => {
3  const res = await fetch("https://api.oioweb.cn/api/weather/GetWeather");
4  const res = await fetch("https://weather.sl.al/");
5  return await res.json();
6};

更改 src/components/Weather.vue 为以下内容:

  1<template>
  2  <div class="weather" v-if="weatherData.adCode.city && weatherData.weather.weather">
  3    <span v-if="!mainKey">{{ weatherData.adCode.region }}&nbsp;</span>
  4    <span>{{ weatherData.adCode.city }}&nbsp;</span>
  5    <span>{{ weatherData.weather.weather }}&nbsp;</span>
  6    <span>{{ weatherData.weather.temperature }}</span>
  7<!--
  8    <span class="sm-hidden">
  9      &nbsp;{{
 10        weatherData.weather.winddirection?.endsWith("风")
 11          ? weatherData.weather.winddirection
 12          : weatherData.weather.winddirection + "风"
 13      }}&nbsp;
 14    </span>
 15    <span class="sm-hidden">{{ weatherData.weather.windpower }}&nbsp;</span>
 16-->
 17    <template v-if="mainKey">
 18    <span class="sm-hidden">
 19        &nbsp;{{
 20            weatherData.weather.winddirection?.endsWith("风")
 21            ? weatherData.weather.winddirection
 22            : weatherData.weather.winddirection + "风"
 23        }}&nbsp;
 24    </span>
 25    <span class="sm-hidden">{{ weatherData.weather.windpower }}&nbsp;</span>
 26    </template>
 27  </div>
 28  <div class="weather" v-else>
 29    <span>天气数据获取失败</span>
 30  </div>
 31</template>
 32
 33
 34
 35
 36
 37<script setup>
 38import { getAdcode, getWeather, getOtherWeather } from "@/api";
 39import { Error } from "@icon-park/vue-next";
 40
 41// 高德开发者 Key
 42const mainKey = import.meta.env.VITE_WEATHER_KEY;
 43
 44// 天气数据
 45const weatherData = reactive({
 46  adCode: {
 47    city: null, // 城市
 48    adcode: null, // 城市编码
 49  },
 50  weather: {
 51    weather: null, // 天气现象
 52    temperature: null, // 实时气温
 53    winddirection: null, // 风向描述
 54    windpower: null, // 风力级别
 55  },
 56});
 57
 58// 取出天气平均值
 59const getTemperature = (min, max) => {
 60  try {
 61    // 计算平均值并四舍五入
 62    const average = (Number(min) + Number(max)) / 2;
 63    return Math.round(average);
 64  } catch (error) {
 65    console.error("计算温度出现错误:", error);
 66    return "NaN";
 67  }
 68};
 69
 70// 获取天气数据
 71const getWeatherData = async () => {
 72  try {
 73    // 获取地理位置信息
 74    if (!mainKey) {
 75      
 76/*
 77      console.log("未配置,使用备用天气接口");
 78      const result = await getOtherWeather();
 79      console.log(result);
 80      const data = result.result;
 81      weatherData.adCode = {
 82        city: data.city.City || "未知地区",
 83        // adcode: data.city.cityId,
 84      };
 85      weatherData.weather = {
 86        weather: data.condition.day_weather,
 87        temperature: getTemperature(data.condition.min_degree, data.condition.max_degree),
 88        winddirection: data.condition.day_wind_direction,
 89        windpower: data.condition.day_wind_power,
 90      };
 91*/
 92
 93      console.log("未配置,使用备用天气接口");
 94      const result = await getOtherWeather();
 95      console.log(result);
 96      const data = result;  // 不需要 .result 了,因为数据在顶层
 97      
 98      weatherData.adCode = {
 99          city: data.location?.city || "未知地区",
100          // 如果需要 region 也可以加上
101          region: data.location?.region
102      };
103      
104      weatherData.weather = {
105          weather: data.current?.description,        // 天气描述
106          temperature: data.current?.temperature,     // 温度
107          // 如果需要体感温度
108          // feelsLike: data.current?.feelsLike,
109          
110          // 如果新的数据结构中没有风向和风力数据,可以设置默认值
111          //winddirection: "暂无数据",
112          //windpower: "暂无数据",
113          
114          // 如果需要添加空气质量信息
115          //airQuality: {
116          //  category: data.current?.airQuality?.category,
117          //  statement: data.current?.airQuality?.statement
118          //}
119      };
120
121    } else {
122      // 获取 Adcode
123      const adCode = await getAdcode(mainKey);
124      console.log(adCode);
125      if (adCode.infocode !== "10000") {
126        throw "地区查询失败";
127      }
128      weatherData.adCode = {
129        city: adCode.city,
130        adcode: adCode.adcode,
131      };
132      // 获取天气信息
133      const result = await getWeather(mainKey, weatherData.adCode.adcode);
134      weatherData.weather = {
135        weather: result.lives[0].weather,
136        temperature: result.lives[0].temperature,
137        winddirection: result.lives[0].winddirection,
138        windpower: result.lives[0].windpower,
139      };
140    }
141  } catch (error) {
142    console.error("天气信息获取失败:" + error);
143    onError("天气信息获取失败");
144  }
145};
146
147// 报错信息
148const onError = (message) => {
149  ElMessage({
150    message,
151    icon: h(Error, {
152      theme: "filled",
153      fill: "#efefef",
154    }),
155  });
156  console.error(message);
157};
158
159onMounted(() => {
160  // 调用获取天气
161  getWeatherData();
162});
163</script>

小记

好像什么都没干一年就过去了。。。 sleep-1

Licensed under CC BY-NC-SA 4.0
最后更新于 2025-09-16 11:05 +0800
本文属于 Hugo 博客美化 系列:
  1. Hugo-stackの美化
  2. Hugo-stackの美化 II
  3. Hugo 短代码
  4. Hugo-stackの美化 III (本文)
给博主施舍一个赞吧(;へ:) ❤️