清羽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结构中,也就是纯静态的部分,所以不需要担心性能问题。
当然以上的示例仅供参考啦,具体使用方式还是需要看主题是否支持~
在Butterfly主题中,整体结构通常围绕 layout、source/css 和 source/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 逻辑,实现完整的功能扩展。
Butterfly内置了一个Note外挂标签,但是我嫌他有点丑,并且参数太多了,用起来我自己都懵逼,所以我将其简化修改,并进行了美化。
改前须知
魔改可能会影响到之前已有的标签渲染,导致裂开,比如此次修改的note标签,如果之前已经使用了相关标签,请自行删除或者按照新版要求修改。
首先,通过外挂标签生成最基本的HTML结构,打开以下文件,并写入以下内容:
// 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标签的样式文件在:
blog\themes\liushen\source\css\_tags\note.styl打开文件,覆盖以下内容:
.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这样整体结构就完成了,使用方法如下:
{% note warning 改前须知 %}
魔改可能会影响到之前已有的标签渲染,导致裂开,比如此次修改的`note`标签,如果之前已经使用了相关标签,请自行删除或者按照新版要求修改。
{% endnote %}渲染效果如上“改前须知“,往上翻翻就能看到啦,除了warning还有error,question,info,下面是样式展示:
info标签
这是标题
这个是内容展示,里面可以正常使用Markdown的相关渲染格式
对应文本如下:
{% note info 这是标题 %}
这个是内容展示,里面可以正常使用`Markdown`的相关渲染格式
{% endnote %}warning标签
这是标题
这个是内容展示,里面可以正常使用Markdown的相关渲染格式
对应文本如下:
{% note warning 这是标题 %}
这个是内容展示,里面可以正常使用`Markdown`的相关渲染格式
{% endnote %}question标签
这是标题
这个是内容展示,里面可以正常使用Markdown的相关渲染格式
对应文本如下:
{% note question 这是标题 %}
这个是内容展示,里面可以正常使用`Markdown`的相关渲染格式
{% endnote %}error标签
这是标题
这个是内容展示,里面可以正常使用Markdown的相关渲染格式
对应文本如下:
{% note error 这是标题 %}
这个是内容展示,里面可以正常使用`Markdown`的相关渲染格式
{% endnote %}Note标签就完成啦!
在很久很久以前,意外捣鼓出了一个外挂标签,就是聊天记录,如下所示:

虽然很久没用,但是我感觉还是很实用的(薛定谔的实用属于是),其实很久很久以前好像是出了个教程,但是不在博客里面,所以了解的人不算很多,如下:
由于是全新的修改,所以需要自行创建文件:
blog\themes\liushen\scripts\tag\chat.js
blog\themes\liushen\source\css\_tags\chat.styl由于有一些图片链接可能无法使用,请自行修改成个人图床。
其中第一个chat.js文件内容如下:
/**
* 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文件中写入:
.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如果不出意外,就结束啦,使用方式如下:
{% chatBox Akilar的糖果屋 %}
{% chat 咕咕怪,搞什么跳转页面 %}
{% chat 咕咕怪,你说连接不安全,你自己加上去的还不安全嘛? %}
{% chat 咕咕怪,这么不自信呀 %}
{% chat 咕咕怪,咕咕 %}
{% chat me,呜呜呜 %}
{% endchatBox %}注意自行修改头像,我懒得写了,直接放在了代码内部硬编码。
由于第一个栏目我自己也忘了咋搞的了,目前懒得提取出来,所以这篇文章先介绍一下欢迎来访者和每日诗词的魔改教程,至于下面的能量榜,在前面的文章总已经单独写了一篇文章,这里就不解释啦。
按照惯例,我们先实现插入HTML结构,新建PUG文件,如下:
blog\themes\liushen\layout\includes\widget\card_welcome.pug写入以下内容:
.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找到对应结构,如下:
#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})插入位置可以自行继续调整,这里只是做个示范。
结构实现了,还需要实现其样式,新建以下文件:
blog\themes\liushen\source\css\_layout\card-welcome.styl写入以下内容:
#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文件中添加以下内容:
// 显示欢迎信息
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中,这一步就不再细讲:
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文件,如下:
!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,如下:
#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文件:
/* 古诗词卡片 */
#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最终应该就可以实现效果啦,文章就不展示效果了,可以直接在侧边栏观看哦~
魔改博客的记忆,像雾气般渐渐消散。彼时,那些灵光一现的改动,曾在我心底刻下清晰的印记。然而,随着时间的洪流向前,我迈入了繁忙的职场,那些曾经认为有趣至极的事物,便被工作中的琐碎与疑惑所取代,日益淡漠。修整博客本是一件多么令人愉悦的事情,如今却无暇顾及。周末,除了在床上虚度光阴,再无一丝活力。一想到两天后的工作,眉头便开始紧锁。或许,面部表情的转变,正是我们成长的方式吧——直到有一天,你的笑容被一丝严肃所取代,那份严肃里,带着岁月的痕迹,便意味着,你真正长大了。
后面我会尽量整理所有魔改的教程,尽量不要让自己淡忘这一部分,希望还能整理出来一些,不至于全部丢了。
门前一阵清风,吹散了积压多时的雾气,待到这一刻拂晓,方知是心底升起了崭新的月,与我未尽的诗和远方,抬眼望窗外看见一道流星,恰是醒来的灵魂。