首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >魔改笔记八:外挂标签及侧边栏美化

魔改笔记八:外挂标签及侧边栏美化

作者头像
柳神
发布2026-03-10 17:21:56
发布2026-03-10 17:21:56
390
举报
文章被收录于专栏:清羽飞扬清羽飞扬

关于AI

清羽AI正在绞尽脑汁想思路ING···

清羽のAI摘要

KIMI-K2

碎碎念

这个月研究了很多关于Vllm框架的内容,感觉身体被掏空,不过好在总结出来了一些架构,下个周就要开始开发咯,将部分特性移到自己的代码上,难度个人感觉还是挺高的,看看下个周哥的指导咯,得亏是两个人干,一个人的话我不敢想我的进度会多慢……

上上个周有个朋友想要我的Note外挂标签和侧边栏的个人信息页面,但是由于工作的原因一直耽误,导致没写(其实就是周六周天懒得写,给我找了个借口而已),今天闲来无事,懒得出门,那就整理一下吧!

屋里一股臭味,找了好久没有找到原因,直到某一刻发现,原来是枕头里藏了发霉的梦,和我腐烂的理想,寻摸着床底下发现了一具尸体,原来是年少的自己。

前置信息

之前有些朋友不太理解外挂标签是什么,在评论区我简单解释了一下,不过不太详细,这里引用hexo的文档详细解释一下,也能更好的理解教程~

内容声明

这部分由于比较系统化,大部分由ChatGPT整理生成,纯粹是我懒得写,反正能理解就好啦~

外挂标签

Hexo 的外挂标签是一类能在Markdown中以特殊语法插入复杂内容的扩展指令。例如 {% link url text %} 可以快速生成带标题的链接,而 {% image src alt %} 则能插入带属性的图片。相比原生Markdown,外挂标签提供了更灵活的排版能力,让主题开发者和写作者能轻松加入按钮、提示框、视频、折叠内容等更丰富的组件。

除了标签,Hexo还支持在文章中注入 Script 脚本。例如使用 {% js src %} 或直接在 Markdown 中加入 <script> 标签,可以加载外部JavaScript或运行自定义逻辑。这类脚本通常用于统计、交互组件或主题扩展,但需要注意安全性与执行顺序,确保不会影响页面正常渲染。

外挂标签会直接在pug结构渲染完毕后,插入到html结构中,也就是纯静态的部分,所以不需要担心性能问题。

🙄引用站外地址,不保证站点的可用性和安全性

标签插件(Tag Plugins)

Hexo官方文档

当然以上的示例仅供参考啦,具体使用方式还是需要看主题是否支持~

主题结构

Butterfly主题中,整体结构通常围绕 layoutsource/csssource/js 三个主要目录展开。其中 layout 目录负责页面的主体框架,这里写入的是页面的 html 结构,但主题使用 pug 语法进行模板编写,所以在魔改时,我们首先需要规划整体布局,并在 pug 文件中写出需要呈现的结构。layout 是整个主题的主干,所有最终渲染到前端的结构都从这里生成。

pug 架构确定后,样式部分会放在 source/css 中,这里使用 stylus 编写样式。通常我们会在 stylus 中针对布局进行细化调整,包括颜色、间距、响应式等内容。由于 Butterfly 本身的 stylus 架构较为清晰,一般只需要新增或覆盖部分样式即可完成魔改。

如果魔改内容包含交互行为或动态效果,则需要在 source/js 中加入对应的脚本。主题支持直接在 html 中插入 script 标签,也可以在 source/js 中编写单独的 js 文件并通过主题配置或 layout 加载。对于依赖事件、监听、动画等操作的功能,通常推荐单独写成 js 文件,便于维护和复用。

所以在进行Butterfly魔改时,通常遵循的顺序是:先设计并实现 pug 的结构,再通过 stylus 完成风格上的精调,最后根据需求在适当位置插入或加载 js 逻辑,实现完整的功能扩展。

魔改教程

外挂标签

Note

Butterfly内置了一个Note外挂标签,但是我嫌他有点丑,并且参数太多了,用起来我自己都懵逼,所以我将其简化修改,并进行了美化。

改前须知

魔改可能会影响到之前已有的标签渲染,导致裂开,比如此次修改的note标签,如果之前已经使用了相关标签,请自行删除或者按照新版要求修改。

首先,通过外挂标签生成最基本的HTML结构,打开以下文件,并写入以下内容:

代码语言:javascript
复制
// blog\themes\liushen\scripts\tag\note.js

/**
 * note 外挂标签,分为警告、错误、问题、信息,比原版更加简单,样式更加漂亮
 * by: LiuShen
 * 样式参考: https://blog.zhilu.cyou/
 */

'use strict'

const postNote = (args, content) => {
  // 定义五种类型及其对应的 Font Awesome 图标
  const types = {
    warning: 'fa-circle-dot',          // 警告
    error: 'fa-circle-xmark',          // 错误
    question: 'fa-circle-question',    // 问题
    info: 'fa-circle-check',           // 信息
  };

  // 获取标签类型和标题
  const type = args[0] || 'info'; // 如果未提供类型,默认为 info
  const title = args.slice(1).join(' ') || '附加信息'; // 提取标题,默认为 "提示"
  const icon = types[type] || types.info; // 如果类型未定义,使用 info 类型的图标

  // 判断标签类型是否在 types 中定义
  if (!types[type]) {
    console.warn(`\`${type}\` 类型未定义,已自动切换为 \`info\` 类型`);
    type = 'info';
    title = '附加信息';
    icon = types.info;
  }

  // 返回 HTML 结构
  return `
    <div class="note note-${type}">
      <div class="note-header">
        <i class="note-icon fa-regular ${icon}"></i>
        <span class="note-title">${title}</span>
      </div>
      <div class="note-content">
        ${hexo.render.renderSync({ text: content, engine: 'markdown' })}
      </div>
    </div>
  `;
};

// 注册自定义标签
hexo.extend.tag.register('note', postNote, { ends: true });

有了HTML结构,就写入CSS样式,美化该标签,原来的Note标签的样式文件在:

代码语言:javascript
复制
blog\themes\liushen\source\css\_tags\note.styl

打开文件,覆盖以下内容:

代码语言:javascript
复制
.note
  addBorderRadius()
  border var(--liushen-card-border)
  position: relative
  margin: 0 0 20px
  padding: 15px 15px 7px 15px
  background-color: var(--liushen-card-bg)
  transition: transform 0.3s ease

  &:hover
    transform: translateY(-3px)

  &.note-warning
    background-image: radial-gradient(circle at 4em -25em, #f0ad4e, transparent 30em),
                linear-gradient(#f0ad4e -2000%, transparent)
    .note-header
      color: #f0ad4e

  &.note-info
    background-image: radial-gradient(circle at 4em -25em, #5cb85c, transparent 30em),
                linear-gradient(#5cb85c -2000%, transparent)
    .note-header
      color: #5cb85c

  &.note-error
    background-image: radial-gradient(circle at 4em -25em, #d9534f, transparent 30em),
                linear-gradient(#d9534f -2000%, transparent)
    .note-header
      color: #d9534f

  &.note-question
    background-image: radial-gradient(circle at 4em -25em, #5bc0de, transparent 30em),
                linear-gradient(#5bc0de -2000%, transparent)
    .note-header
      color: #5bc0de

  .note-header
    display: flex
    align-items: center
    font-weight: bold
    margin-bottom: 10px
    font-size: 1.0em
    i.note-icon
      margin-right: 10px
      color: inherit

这样整体结构就完成了,使用方法如下:

代码语言:javascript
复制
{% note warning 改前须知 %}
魔改可能会影响到之前已有的标签渲染,导致裂开,比如此次修改的`note`标签,如果之前已经使用了相关标签,请自行删除或者按照新版要求修改。
{% endnote %}

渲染效果如上“改前须知“,往上翻翻就能看到啦,除了warning还有errorquestioninfo,下面是样式展示:

info标签

这是标题

这个是内容展示,里面可以正常使用Markdown的相关渲染格式

对应文本如下:

代码语言:javascript
复制
{% note info 这是标题 %}
这个是内容展示,里面可以正常使用`Markdown`的相关渲染格式
{% endnote %}

warning标签

这是标题

这个是内容展示,里面可以正常使用Markdown的相关渲染格式

对应文本如下:

代码语言:javascript
复制
{% note warning 这是标题 %}
这个是内容展示,里面可以正常使用`Markdown`的相关渲染格式
{% endnote %}

question标签

这是标题

这个是内容展示,里面可以正常使用Markdown的相关渲染格式

对应文本如下:

代码语言:javascript
复制
{% note question 这是标题 %}
这个是内容展示,里面可以正常使用`Markdown`的相关渲染格式
{% endnote %}

error标签

这是标题

这个是内容展示,里面可以正常使用Markdown的相关渲染格式

对应文本如下:

代码语言:javascript
复制
{% note error 这是标题 %}
这个是内容展示,里面可以正常使用`Markdown`的相关渲染格式
{% endnote %}

Note标签就完成啦!

Chat

在很久很久以前,意外捣鼓出了一个外挂标签,就是聊天记录,如下所示:

虽然很久没用,但是我感觉还是很实用的(薛定谔的实用属于是),其实很久很久以前好像是出了个教程,但是不在博客里面,所以了解的人不算很多,如下:

😃来自本站,本站可确保其安全性,请放心点击跳转

清羽飞扬の代码片段

LiuShen's Blog

由于是全新的修改,所以需要自行创建文件:

代码语言:javascript
复制
blog\themes\liushen\scripts\tag\chat.js
blog\themes\liushen\source\css\_tags\chat.styl

由于有一些图片链接可能无法使用,请自行修改成个人图床。

其中第一个chat.js文件内容如下:

代码语言:javascript
复制
/**
 * Chat
 */

"use strict";

// 预定义头像数组
const avatars = [
  "https://i.p-i.vip/30/20240920-66ed9a608c2cf.png",
  "https://i.p-i.vip/30/20240920-66ed9b0655cba.png",
  "https://i.p-i.vip/30/20240920-66ed9b18a56ee.png",
  "https://i.p-i.vip/30/20240920-66ed9b2c199bf.png",
  "https://i.p-i.vip/30/20240920-66ed9b3350ed1.png",
  "https://i.p-i.vip/30/20240920-66ed9b5181630.png",
  // 可以继续添加更多头像
];

// 用来记录已经分配头像的用户
const userAvatarMap = new Map();
let avatarIndex = 0;

// 生成聊天框的整体结构
function postChatBox(args, content) {
  const title = args[0] ? args[0].trim() : "群聊的聊天记录"; // 获取标题
  const titleHtml = title ? `<div class="chatBoxTitle"><i class="fa-solid fa-angle-left"></i><span class="chatTitleText">${title}</span><div class="chatBoxIcons"><i class="fa-solid fa-user"></i><i class="fa-solid fa-bars"></i></div></div>` : ""; // 生成标题 HTML
  const contentHtml = `<div class="chatBox">${content}</div>`
  return `<div class="chatContainer">${titleHtml}${contentHtml}</div>`;
}

// 生成单条聊天内容
function postChat(args) {
  if (!args || args.length === 0) {
    return ""; // 如果参数为空,返回空字符串
  }

  // 合并并拆分参数
  args = args.join(" ").split(",");

  // 确保 args[0] 存在
  let name = args[0] ? args[0].trim() : "未知";
  let content = args[1] ? args[1].trim() : "无内容";

  // 判断名字是否包含 QQ 号 (例如 June@3526514925)
  let qqNumber = null;
  if (name.includes("@")) {
    [name, qqNumber] = name.split("@"); // 分割名字和 QQ 号
  }

  // 判断是否是我的消息
  const isMe = name.toLowerCase() === "me";
  const chatName = isMe ? hexo.config.author : name;
  const chatClass = isMe ? "me" : "";

  // 固定的头像链接
  const myAvatar = "https://p.liiiu.cn/i/2024/03/29/66061417537af.png";
  let avatarUrl;

  if (isMe) {
    avatarUrl = myAvatar;
  } else if (qqNumber) {
    // 如果有 QQ 号,拼接头像 URL
    avatarUrl = `https://q1.qlogo.cn/g?b=qq&nk=${qqNumber}&s=100`;
  } else {
    // 如果没有 QQ 号,从预定义的头像数组中分配
    if (!userAvatarMap.has(name)) {
      userAvatarMap.set(name, avatars[avatarIndex % avatars.length]);
      avatarIndex++;
    }
    avatarUrl = userAvatarMap.get(name);
  }

  // 生成 HTML 布局
  let result = "";
  result += `<div class="chatItem ${chatClass}">`;
  result += `<img class="chatAvatar no-lightbox" src="${avatarUrl}">`; // 添加头像
  result += `<div class="chatContentWrapper">`;
  result += `<b class="chatName">${chatName}</b>`;
  result += `<div class="chatContent">${content}</div>`;
  result += `</div>`;
  result += `</div>`;

  return result;
}

// 注册自定义标签
hexo.extend.tag.register("chat", postChat);
hexo.extend.tag.register("chatBox", postChatBox, { ends: true });

同样,外挂标签作用为注入html,真正的灵魂属于样式文件,在chat.styl文件中写入:

代码语言:javascript
复制
.chatContainer
    background: var(--card-bg)
    addBorderRadius()
    border: var(--liushen-card-border)
    overflow: hidden
    margin: 0 0 16px
    width: 100%

    .chatBoxTitle
        display: flex
        justify-content: flex-start
        background: var(--default-bg-color)
        padding: 10px 10px
        color: var(--white)
        display: flex
        align-items: center
        justify-content: space-between

        i
            font-size: 16px
            margin: 0 10px
        
        .chatTitleText
            flex: 1
            font-size: 16px
            white-space: nowrap; /* 不换行 */
            overflow: hidden; /* 超出部分隐藏 */
            text-overflow: ellipsis; /* 显示省略号 */

.chatBox
    padding: 20px 30px
    box-sizing: border-box
    max-height: 600px
    overflow-y: auto
    
    +maxWidth768()
        padding: 20px 10px
        max-height: 400px

.chatItem
    display: flex
    flex-direction: row
    justify-content: flex-start
    margin: 20px 0

    .chatAvatar
        width: 40px
        height: 40px
        border-radius: 50% !important
        margin: 5px 10px 0 0 !important
    
    .chatContentWrapper
        display: flex
        flex-direction: column
        width: calc(100% - 110px)

        .chatContent
            background: var(--liushen-card-secondbg)
            border: var(--liushen-card-border)
            padding: 10px
            border-radius: 0 10px 10px 10px
            width: fit-content
            max-width: 100%
            word-wrap: break-word

    &.me
        flex-direction: row-reverse

        .chatAvatar
            margin: 5px 0 0 10px !important

        .chatContentWrapper
            align-items: flex-end

            .chatContent
                background: var(--liushen-card-bg)
                border-radius: 10px 0 10px 10px

如果不出意外,就结束啦,使用方式如下:

代码语言:javascript
复制
{% chatBox Akilar的糖果屋 %}

{% chat 咕咕怪,搞什么跳转页面 %}
{% chat 咕咕怪,你说连接不安全,你自己加上去的还不安全嘛? %}
{% chat 咕咕怪,这么不自信呀 %}
{% chat 咕咕怪,咕咕 %}
{% chat me,呜呜呜 %}

{% endchatBox %}

注意自行修改头像,我懒得写了,直接放在了代码内部硬编码。

侧边栏

由于第一个栏目我自己也忘了咋搞的了,目前懒得提取出来,所以这篇文章先介绍一下欢迎来访者和每日诗词的魔改教程,至于下面的能量榜,在前面的文章总已经单独写了一篇文章,这里就不解释啦。

欢迎来访者

按照惯例,我们先实现插入HTML结构,新建PUG文件,如下:

代码语言:javascript
复制
blog\themes\liushen\layout\includes\widget\card_welcome.pug

写入以下内容:

代码语言:javascript
复制
.card-widget.card-welcome
  .item-headline
    i.fa.fa-user
    span 欢迎来访者!
  .item-content
    p 👋🏻我是清扬,一个
      span(style="font-weight: bold; color: #3498db") 热爱编程
      | 的技术爱好者,喜欢分享经验。😊 
    p ❓有问题欢迎提问,确保内容有意义,详情请见
      a(href="https://how-to-ask.liushen.fun/" target="_blank" style="font-weight: bold; color: #2980b9") 提问的智慧
      | 。如需联系我,欢迎通过
      a(href="mailto:01@liushen.fun" style="font-weight: bold; color: #9b59b6") 邮箱
      | 联系我!📧
  #welcome-info
    .error-message(style="height: 200px; display: flex; justify-content: center; align-items: center;")
      p(style="text-align: center;")
        span(style="font-size: 40px;") 😥 
        br
        span(style="font-size: 16px;") 由于网络问题
        br
        span(style="font-size: 16px;") 位置API请求错误
        br
        span(style="font-size: 16px;") 请刷新重试呀🤗~

结构实现了,下面将该结构插入到对应的位置,可以在当前目录下的index.pug找到对应结构,如下:

代码语言:javascript
复制
#aside-content.aside-content
  //- post
  if is_post()
    - const tocStyle = page.toc_style_simple
    - const tocStyleVal = tocStyle === true || tocStyle === false ? tocStyle : theme.toc.style_simple
    if showToc && tocStyleVal
      .sticky_layout
        include ./card_post_toc.pug
    else
      !=partial('includes/widget/card_author', {}, {cache: true})
      !=partial('includes/widget/card_poem', {}, {cache: true})
      !=partial('includes/widget/card_rewards', {}, {cache: true})
      !=partial('includes/widget/card_announcement', {}, {cache: true})
      !=partial('includes/widget/card_top_self', {}, {cache: true})
      .sticky_layout
        if showToc
          include ./card_post_toc.pug
        if page.series
          include ./card_post_series.pug
        !=partial('includes/widget/card_recent_post', {}, {cache: true})
        !=partial('includes/widget/card_ad', {}, {cache: true})
  else
    //- page
    !=partial('includes/widget/card_author', {}, {cache: true})
+   !=partial('includes/widget/card_welcome', {}, {cache: true})
    !=partial('includes/widget/card_poem', {}, {cache: true})
    !=partial('includes/widget/card_rewards', {}, {cache: true})
    !=partial('includes/widget/card_announcement', {}, {cache: true})
    !=partial('includes/widget/card_top_self', {}, {cache: true})

    .sticky_layout
      if showToc
        include ./card_post_toc.pug
      !=partial('includes/widget/card_recent_post', {}, {cache: true})
      !=partial('includes/widget/card_ad', {}, {cache: true})
      !=partial('includes/widget/card_newest_comment', {}, {cache: true})
      !=partial('includes/widget/card_categories', {}, {cache: true})
      !=partial('includes/widget/card_tags', {}, {cache: true})
      !=partial('includes/widget/card_archives', {}, {cache: true})
      !=partial('includes/widget/card_webinfo', {}, {cache: true})
      !=partial('includes/widget/card_bottom_self', {}, {cache: true})

插入位置可以自行继续调整,这里只是做个示范。

结构实现了,还需要实现其样式,新建以下文件:

代码语言:javascript
复制
blog\themes\liushen\source\css\_layout\card-welcome.styl

写入以下内容:

代码语言:javascript
复制
#aside-content
    .card-widget.card-welcome
        padding: 8px
        .item-headline
            margin: 12px 16px 0 16px
        .item-content
            margin: 0 16px 10px 16px
        #welcome-info
            text-align center
            background-color: var(--liushen-card-bg)
            border-radius: 12px
            padding: 16px
            margin: 8px
            // border: 2px solid var(--liushen-text)
            .ip-address
                // 模糊效果
                filter: blur(5px)
                color: var(--default-bg-color)
                transition: filter 0.5s
                &:hover
                    filter: none

经过渲染应该就可以看到结果了。

不过由于我们的访客卡片需要调用api,所以还需要js的配合,在任意自定义js文件中添加以下内容:

代码语言:javascript
复制
// 显示欢迎信息
function showWelcome(ipLocation) {
    if (!ipLocation || !ipLocation.data) {
        console.error('ipLocation data is not available.');
        return;
    }

    let dist = getDistance(121.476, 31.224, ipLocation.data.lng, ipLocation.data.lat);
    let pos = ipLocation.data.country;
    let ip = ipLocation.ip;
    let posdesc;

    // 以下的代码需要根据新API返回的结果进行相应的调整
    switch (ipLocation.data.country) {
        case "日本":
            posdesc = "よろしく,一起去看樱花吗";
            break;
        case "美国":
            posdesc = "Let us live in peace!";
            break;
        case "英国":
            posdesc = "想同你一起夜乘伦敦眼";
            break;
        case "俄罗斯":
            posdesc = "干了这瓶伏特加!";
            break;
        case "法国":
            posdesc = "C'est La Vie";
            break;
        case "德国":
            posdesc = "Die Zeit verging im Fluge.";
            break;
        case "澳大利亚":
            posdesc = "一起去大堡礁吧!";
            break;
        case "加拿大":
            posdesc = "拾起一片枫叶赠予你";
            break;
        case "中国":
            pos = ipLocation.data.prov + " " + ipLocation.data.city + " " + ipLocation.data.district;
            switch (ipLocation.data.prov) {
                case "北京市":
                    posdesc = "北——京——欢迎你~~~";
                    break;
                case "天津市":
                    posdesc = "讲段相声吧";
                    break;
                case "河北省":
                    posdesc = "山势巍巍成壁垒,天下雄关铁马金戈由此向,无限江山";
                    break;
                case "山西省":
                    posdesc = "展开坐具长三尺,已占山河五百余";
                    break;
                case "内蒙古自治区":
                    posdesc = "天苍苍,野茫茫,风吹草低见牛羊";
                    break;
                case "辽宁省":
                    posdesc = "我想吃烤鸡架!";
                    break;
                case "吉林省":
                    posdesc = "状元阁就是东北烧烤之王";
                    break;
                case "黑龙江省":
                    posdesc = "很喜欢哈尔滨大剧院";
                    break;
                case "上海市":
                    posdesc = "众所周知,中国只有两个城市";
                    break;
                case "江苏省":
                    switch (ipLocation.data.city) {
                        case "南京市":
                            posdesc = "这是我挺想去的城市啦";
                            break;
                        case "苏州市":
                            posdesc = "上有天堂,下有苏杭";
                            break;
                        default:
                            posdesc = "散装是必须要散装的";
                            break;
                    }
                    break;
                case "浙江省":
                    switch (ipLocation.data.city) {
                        case "杭州市":
                            posdesc = "东风渐绿西湖柳,雁已还人未南归";
                            break;
                        default:
                            posdesc = "望海楼明照曙霞,护江堤白蹋晴沙";
                            break;
                    }
                    break;
                case "河南省":
                    switch (ipLocation.data.city) {
                        case "郑州市":
                            posdesc = "豫州之域,天地之中";
                            break;
                        case "信阳市":
                            posdesc = "品信阳毛尖,悟人间芳华";
                            break;
                        case "南阳市":
                            posdesc = "臣本布衣,躬耕于南阳此南阳非彼南阳!";
                            break;
                        case "驻马店市":
                            posdesc = "峰峰有奇石,石石挟仙气嵖岈山的花很美哦!";
                            break;
                        case "开封市":
                            posdesc = "刚正不阿包青天";
                            break;
                        case "洛阳市":
                            posdesc = "洛阳牡丹甲天下";
                            break;
                        default:
                            posdesc = "可否带我品尝河南烩面啦?";
                            break;
                    }
                    break;
                case "安徽省":
                    posdesc = "蚌埠住了,芜湖起飞";
                    break;
                case "福建省":
                    posdesc = "井邑白云间,岩城远带山";
                    break;
                case "江西省":
                    posdesc = "落霞与孤鹜齐飞,秋水共长天一色";
                    break;
                case "山东省":
                    posdesc = "遥望齐州九点烟,一泓海水杯中泻";
                    break;
                case "湖北省":
                    switch (ipLocation.data.city) {
                        case "黄冈市":
                            posdesc = "红安将军县!辈出将才!";
                            break;
                        case "武汉市":
                            posdesc = "你想去长江游泳嘛?";
                            break;
                        default:
                            posdesc = "来碗热干面~";
                            break;
                    }
                    break;
                case "湖南省":
                    posdesc = "74751,长沙斯塔克";
                    break;
                case "广东省":
                    switch (ipLocation.data.city) {
                        case "广州市":
                            posdesc = "看小蛮腰,喝早茶了嘛~";
                            break;
                        case "深圳市":
                            posdesc = "今天你逛商场了嘛~";
                            break;
                        case "阳江市":
                            posdesc = "阳春合水!博主家乡~ 欢迎来玩~";
                            break;
                        default:
                            posdesc = "来两斤福建人~";
                            break;
                    }
                    break;
                case "广西壮族自治区":
                    posdesc = "桂林山水甲天下";
                    break;
                case "海南省":
                    posdesc = "朝观日出逐白浪,夕看云起收霞光";
                    break;
                case "四川省":
                    posdesc = "康康川妹子";
                    break;
                case "贵州省":
                    posdesc = "茅台,学生,再塞200";
                    break;
                case "云南省":
                    posdesc = "玉龙飞舞云缠绕,万仞冰川直耸天";
                    break;
                case "西藏自治区":
                    posdesc = "躺在茫茫草原上,仰望蓝天";
                    break;
                case "陕西省":
                    posdesc = "来份臊子面加馍";
                    break;
                case "甘肃省":
                    posdesc = "羌笛何须怨杨柳,春风不度玉门关";
                    break;
                case "青海省":
                    posdesc = "牛肉干和老酸奶都好好吃";
                    break;
                case "宁夏回族自治区":
                    posdesc = "大漠孤烟直,长河落日圆";
                    break;
                case "新疆维吾尔自治区":
                    posdesc = "驼铃古道丝绸路,胡马犹闻唐汉风";
                    break;
                case "台湾省":
                    posdesc = "我在这头,大陆在那头";
                    break;
                case "香港特别行政区":
                    posdesc = "永定贼有残留地鬼嚎,迎击光非岁玉";
                    break;
                case "澳门特别行政区":
                    posdesc = "性感荷官,在线发牌";
                    break;
                default:
                    posdesc = "带我去你的城市逛逛吧!";
                    break;
            }
            break;
        default:
            posdesc = "带我去你的国家逛逛吧";
            break;
    }

    // 根据本地时间切换欢迎语
    let timeChange;
    let date = new Date();
    if (date.getHours() >= 5 && date.getHours() < 11) timeChange = "<span>🌤️ 早上好,快趁机多睡点懒觉!</span>";
    else if (date.getHours() >= 11 && date.getHours() < 13) timeChange = "<span>☀️ 中午好,记得午休喔~</span>";
    else if (date.getHours() >= 13 && date.getHours() < 17) timeChange = "<span>🕞 下午好,饮茶先啦!</span>";
    else if (date.getHours() >= 17 && date.getHours() < 19) timeChange = "<span>🚶‍♂️ 下班啦!主打一个不听老板话~</span>";
    else if (date.getHours() >= 19 && date.getHours() < 24) timeChange = "<span>🌙 晚上好,来一起熬夜吧呜😭</span>";
    else timeChange = "夜深了,早点休息,少熬夜";

    let welcomeInfoElement = document.getElementById("welcome-info");

    if (welcomeInfoElement) {
        welcomeInfoElement.innerHTML =
            `嗷嗷!热烈欢迎🤪!来自<br><b><span style="color: var(--default-bg-color)">${pos}</span></b><br> 的铁铁,你好呀!😝<br>${posdesc}🍂<br>你目前距博主约 <b><span style="color: var(--default-bg-color)">${dist}</span></b> 公里!<br>你的网络IP为:<b><span class="ip-address" style="font-size: 15px;">${ip}</span></b><br>${timeChange} <br>`;
    } else {
        console.log("Pjax无法获取元素");
    }
}

// 判断是否存在 "welcome-info" 元素
function isWelcomeInfoAvailable() {
    let welcomeInfoElement = document.getElementById("welcome-info");
    return welcomeInfoElement !== null;
}

// Pjax 完成后调用的处理函数
function handlePjaxComplete(ipLocation) {
    if (isWelcomeInfoAvailable()) {
        showWelcome(ipLocation);
    }
}

// 加载时调用
function onLoad() {
    fetchIpLocation().then(ipLocation => {
        if (isWelcomeInfoAvailable()) {
            showWelcome(ipLocation);
        }
        document.addEventListener("pjax:complete", () => handlePjaxComplete(ipLocation));
    });
}

// 绑定 window.onload 事件
window.onload = onLoad;

欢迎来访者卡片就实现完成啦!

今日诗词

这个比较简单,主要就是以下三个文件,当然仍然需要按照上面的welcome卡片的方法注册到index.pug中,这一步就不再细讲:

代码语言:javascript
复制
blog\themes\liushen\source\js\jinrishici.js
blog\themes\liushen\layout\includes\widget\card_poem.pug
blog\themes\liushen\source\css\_layout\card-jinrishici.styl

首先是第一个jinrishici.js文件,如下:

代码语言:javascript
复制
!function(e){var n,t={},o="jinrishici-token";function i(){return document.getElementById("jinrishici-sentence")||0!=document.getElementsByClassName("jinrishici-sentence").length}function c(){t.load(function(e){var n=document.getElementById("jinrishici-sentence"),t=document.getElementsByClassName("jinrishici-sentence");if(n&&(n.innerText=e.data.content),0!==t.length)for(var o=0;o<t.length;o++)t[o].innerText=e.data.content})}function r(e,n){var t=new XMLHttpRequest;t.open("get",n),t.withCredentials=!0,t.send(),t.onreadystatechange=function(n){if(4===t.readyState){var o=JSON.parse(t.responseText);"success"===o.status?e(o):console.error("今日诗词API加载失败,错误原因:"+o.errMessage)}}}t.load=function(n){return e.localStorage&&e.localStorage.getItem(o)?function(e,n){return r(e,"https://v2.jinrishici.com/one.json?client=browser-sdk/1.2&X-User-Token="+encodeURIComponent(n))}(n,e.localStorage.getItem(o)):function(n){return r(function(t){e.localStorage.setItem(o,t.token),n(t)},"https://v2.jinrishici.com/one.json?client=browser-sdk/1.2")}(n)},e.jinrishici=t,i()?c():(n=function(){i()&&c()},"loading"!=document.readyState?n():document.addEventListener?document.addEventListener("DOMContentLoaded",n):document.attachEvent("onreadystatechange",function(){"complete"==document.readyState&&n()}))}(window);

下面是card_poem.pug,如下:

代码语言:javascript
复制
#card-poem.card-widget
    #poem_sentence
    #poem_info
        #poem_dynasty
        #poem_author
script(src='/js/jinrishici.js', charset='utf-8')
script(type='text/javascript').
  jinrishici.load(function(result) {
  var sentence = document.querySelector("#poem_sentence")
  var author = document.querySelector("#poem_author")
  var dynasty = document.querySelector("#poem_dynasty")

  var sentenceText = result.data.content
  sentenceText = sentenceText.substr(0, sentenceText.length - 1);
  sentence.innerHTML = sentenceText
  dynasty.innerHTML = result.data.origin.dynasty
  author.innerHTML = result.data.origin.author + '《' + result.data.origin.title + '》'
  });

注意创建了card_poem.pug文件后,自行修改目录下的index.pug实现注入。

下面是css文件:

代码语言:javascript
复制
/* 古诗词卡片 */
#card-poem
    display: flex
    flex-direction: column
    padding: 0.5rem !important

div#poem_sentence
    text-align: center
    font-family: serif, cursive
    line-height: 1.4
    margin-bottom: 0.5rem
    padding: 1rem
    border-radius: 8px
    background: var(--liushen-card-bg)
    min-height: 62px

div#poem_info
    display: flex
    color: var(--liushen-secondtext)
    font-size: 0.5rem
    justify-content: center
    flex-wrap: wrap

div#poem_author
    order: 1
    padding: 2px
    margin-left: 8px

div#poem_dynasty
    order: 0
    padding: 2px 4px 2px 6px
    background: var(--liushen-button-bg)
    color: var(--liushen-text)
    border-radius: 8px

最终应该就可以实现效果啦,文章就不展示效果了,可以直接在侧边栏观看哦~

总结

魔改博客的记忆,像雾气般渐渐消散。彼时,那些灵光一现的改动,曾在我心底刻下清晰的印记。然而,随着时间的洪流向前,我迈入了繁忙的职场,那些曾经认为有趣至极的事物,便被工作中的琐碎与疑惑所取代,日益淡漠。修整博客本是一件多么令人愉悦的事情,如今却无暇顾及。周末,除了在床上虚度光阴,再无一丝活力。一想到两天后的工作,眉头便开始紧锁。或许,面部表情的转变,正是我们成长的方式吧——直到有一天,你的笑容被一丝严肃所取代,那份严肃里,带着岁月的痕迹,便意味着,你真正长大了。

后面我会尽量整理所有魔改的教程,尽量不要让自己淡忘这一部分,希望还能整理出来一些,不至于全部丢了。

门前一阵清风,吹散了积压多时的雾气,待到这一刻拂晓,方知是心底升起了崭新的月,与我未尽的诗和远方,抬眼望窗外看见一道流星,恰是醒来的灵魂。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2025-11-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 碎碎念
  • 前置信息
    • 外挂标签
    • 主题结构
  • 魔改教程
    • 外挂标签
      • Note
      • Chat
    • 侧边栏
      • 欢迎来访者
      • 今日诗词
  • 总结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档