📚 更多文档目录

🚀 搭建教程 | 1 - 📑 前置教程 | 2 - 🎈 主题调整 | 3 - ✨ 魔改教程 | 4 - 🐈 重构自用数据记录


本篇教程基于 Hexo 6.3.0 & Butterfly 4.8.5 为博主的魔改教程记录,以防自己日后因魔改迷失所记录 📝

本小节魔改内容不包括 顶部banner栏,如有需要请移步至 ✨ 第二章 - 三小节 | 魔改页前置

最后更新于 2023 年 10 月 06 日

231006 更新:适配本地静态数据部署

效果预览

创建数据

  • 创建 [blogRoot]/source/project/index.md 页面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
---
title: 清单
date: 2023-04-20 11:56:42
type: project
top_img: false
aside: false
top_page: true
top_bg: https://img.meuicat.com/banner
top_item: 待办
top_title: 生活清单
top_tips: 原来我有这么多想要做的事情
comments: false
---

<!-- 页面内容 -->
  • 修改 [blogRoot]/themes/butterfly/layout/page.pug 来使页面匹配
    + 号直接删除 即是正常缩进)
1
2
3
4
5
6
      when 'categories'
include includes/page/categories.pug
+ when 'project'
+ include includes/page/project.pug
default
include includes/page/default-page.pug
  • 新建 [blogRoot]/themes/butterfly/layout/includes/page/project.pug 页面,并新增以下内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
if page.memos_url
#todolist
.todolist-bottom
p Powered by
a(target="_blank" href="https://github.com/usememos/memos" rel="noopener external nofollow") Memos

else
if site.data.project
#todolist
- let result = ""
each i in site.data.project
- let className = i.class_name
- let listResult = ""
- let state = ""
- let strikethrough = ""

each j in i.project_list
-
if (j.state) {
state = `fa-circle-check`
strikethrough = `class="achieve"`
} else {
state = `fa-circle`
strikethrough = ``
}
listResult += `
<li ${strikethrough}>
<i style="margin-right: 5px;" class="fa-regular ${state}"></i>${j.content}
</li>`
-

- result += `<div class="list_item"><h3>${className}</h3><ul>${listResult}</ul></div>`
!= result

.todolist-bottom
p Powered by
a(target="_blank" href="https://github.com/usememos/memos" rel="noopener external nofollow") Memos
  • 新建 [blogRoot]/source/css/project.css 样式文件,并新增以下内容
    (也可以在自建的css文件里新增内容)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#todolist {
display: flex;
flex-wrap: wrap;
margin-top: 1rem;
}
.list_item {
display: inline-block;
width: calc(33.333% - .5rem);
background: #ffe3dd;
border-radius: 12px;
border: var(--style-border-always);
box-shadow: var(--icat-shadow-border);
padding: 10px 1rem 1.2rem;
border: 2px dashed #f7a796;
--todo-border: 1px solid #f7a796;
margin-right: 1rem;
margin-bottom: 1rem;
}
.list_item h3 {
margin: 0;
color: #333;
border-bottom: var(--todo-border);
}
.list_item ul {
font-size: 17px;
padding: 0 !important;
margin: 0;
}
.list_item li{
margin: 0 !important;
color: #333;
border-bottom: var(--todo-border);
}
.list_item li::marker {
content: none;
}
li.achieve {
opacity: .8;
text-decoration: line-through;
}
.bottom {
line-height: 1.5;
text-align: right;
}
.bottom p {
margin: 0 !important;
}
.bottom a {
font-weight: 700;
color: var(--font-color) !important;
}
[data-theme="dark"] .list_item {
border: 2px solid var(--icat-card-border) !important;
}
@media screen and (max-width: 900px) {
div#todolist {
margin: 1rem 5px 0;
}
}
@media screen and (max-width: 768px) {
.list_item{
width: 100%;
}
.bottom {
text-align: center;
}
}

/* Memos清单页样式 */
  • 创建 [blogRoot]/source/js/memos/waterfall.min.js 文件,并新增以下内容,用来处理Memos清单页的瀑布流
    (在前几节的即刻短文和上一节的Memos动态相册教程里都有此js步骤,如果有了那么这一步就可以跳过)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
function waterfall(a) {
function b(a, b) {
var c = window.getComputedStyle(b);
return parseFloat(c["margin" + a]) || 0
}

function c(a) {
return a + "px"
}

function d(a) {
return parseFloat(a.style.top)
}

function e(a) {
return parseFloat(a.style.left)
}

function f(a) {
return a.clientWidth
}

function g(a) {
return a.clientHeight
}

function h(a) {
return d(a) + g(a) + b("Bottom", a)
}

function i(a) {
return e(a) + f(a) + b("Right", a)
}

function j(a) {
a = a.sort(function(a, b) {
return h(a) === h(b) ? e(b) - e(a) : h(b) - h(a)
})
}

function k(b) {
f(a) != t && (b.target.removeEventListener(b.type, arguments.callee), waterfall(a))
}
"string" == typeof a && (a = document.querySelector(a));
var l = [].map.call(a.children, function(a) {
return a.style.position = "absolute", a
});
a.style.position = "relative";
var m = [];
l.length && (l[0].style.top = "0px", l[0].style.left = c(b("Left", l[0])), m.push(l[0]));
for (var n = 1; n < l.length; n++) {
var o = l[n - 1],
p = l[n],
q = i(o) + f(p) <= f(a);
if (!q) break;
p.style.top = o.style.top, p.style.left = c(i(o) + b("Left", p)), m.push(p)
}
for (; n < l.length; n++) {
j(m);
var p = l[n],
r = m.pop();
p.style.top = c(h(r) + b("Top", p)), p.style.left = c(e(r)), m.push(p)
}
j(m);
var s = m[0];
a.style.height = c(h(s) + b("Bottom", s));
var t = f(a);
window.addEventListener ? window.addEventListener("resize", k) : document.body.onresize = k
}

// 瀑布流处理
  • _config.butterfly.yml 主题配置文件中 inject 下的 headbottom 分别引入 project.css waterfall.min.js
1
2
3
4
5
6
7
8
9
  ···

inject:
head:
- <link rel="stylesheet" href="/css/project.css"> # 清单页样式
bottom:
- <script type="text/javascript" src="/js/memos/waterfall.min.js"></script> # memos清单页 - waterfall瀑布流

···

部署数据文件

  • 创建 [blogRoot]/source/_data/project.yml 文件,并新增以下内容
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
- class_name: 近期计划
project_list:
- content: 工作项目顺利收尾完工
state: true
- content: 返深
state: true
- content: 抽空处理分享describe
state: true
- content: 考虑转行
state: false

- class_name: 要处理的项目
project_list:
- content: 做一款阅读类博客主题
state: false
- content: 导航页
state: false
- content: 个人主页
state: false
- content: 简历页
state: false
- content: 重构 iCat 布柒糖FM
state: true
- content: 重构 iCat 卡布奇诺
state: true
- content: 二次重构优化 iCat爱吃肉的猫 V2.0
state: true
- content: 布柒糖FM 项目铺展
state: false

- class_name: 自我提升
project_list:
- content: 每月至少阅读一本书
state: true
- content: 养成定点喝水的习惯
state: true
- content: 每日刮胡子
state: true
- content: 定期护肤
state: false
- content: 专升本
state: false
- content: 坚持健身
state: false

- class_name: 想学的技术
project_list:
- content: CSS进阶
state: true
- content: Javascript进阶
state: false
- content: 微信小程序
state: true
- content: 单片机
state: false
- content: Node
state: true
- content: Docker
state: true
- content: Webpack
state: false
- content: Photoshop
state: true
- content: Illustrator
state: false
- content: EJS
state: true

- class_name: 想买的东西
project_list:
- content: 奥林巴斯 EM-5 III
state: false
- content: SONY A7R3A
state: false
- content: DJI Mini 3 Pro
state: false
- content: MacBook Pro
state: true
- content: Apple Watch Series 7
state: true
- content: 换WD 移动硬盘 2TB
state: false
  • 创建 [blogRoot]/source/js/memos/project.js 文件,并新增以下内容,用来处理本地清单的瀑布流响应布局
    (或写在自建的公共 js 中也可以)
1
2
3
4
5
6
7
8
9
function whenDOMReady() {
window.addEventListener('load', function() {
if (location.pathname == '/project/') waterfall('#todolist');
});
}
whenDOMReady()
document.addEventListener("pjax:complete", whenDOMReady)

// 清单函数适配pjax
  • _config.butterfly.yml 主题配置文件中 inject 下的 bottom 引入 project.js 文件
1
2
3
4
5
6
7
8
9
  ···

inject:
head:
···
bottom:
- <script type="text/javascript" src="/js/memos/project.js"></script> # memos清单页 - 逻辑函数

···

如果没有服务器可以搭建memos,可以使用iCat自用的memos服务:iCat - Memos

  • 新增 [blogRoot]/source/project/index.md 页面的 Post Front-matter 配置项 memos_urltrue
1
2
3
4
5
6
7
---
···

memos_url: true
---

<!-- 文章内容 -->
  • 创建 [blogRoot]/source/js/memos/project.js 文件,并新增以下内容,用来处理Memos清单的函数
    (或写在自建的公共 js 中也可以)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
todolist();
function todolist() {
fetch('你的memos地址/api/v1/memo?creatorId=用户ID&tag=清单').then(res => res.json()).then(data => {
// 获取并处理数据
data.forEach(item => {
// 处理数据
let content = item.content,
title = content.match(/\[(.*?)\]/g)[0].replace(/\[(.*?)\]/, '$1');
// 去掉多余内容,替换清单内容
content = content.replace(/#.*\s/g, '').replace(/(-\s\[\s\]\s)(.*)/g, `<li><i style="margin-right: 5px;" class="fa-regular fa-circle"></i>$2</li>`).replace(/(-\s\[x\]\s)(.*)/g, `<li class="achieve"><i style="margin-right: 5px;" class="fa-regular fa-circle-check"></i>$2</li>`);
// 渲染数据
let div = document.createElement('div');
div.className = 'list_item';
div.innerHTML = `<h3>${title}</h3><ul>${content}</ul>`;
document.getElementById('todolist').appendChild(div);
});
waterfall('#todolist');
}).catch()
}

// 清单函数
Memos1.4.0以上版本
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
todolist();
function todolist() {
fetch('你的memos地址/api/memo?creatorId=用户ID&tag=清单').then(res => res.json()).then(data => { // 注意替换链接和ID
// 获取并处理数据
data = data.data
let box = document.getElementById('todolist')
data.forEach(item => {
// 处理数据
let content = item.content,
title = content.match(/\[(.*?)\]/g)[0].replace(/\[(.*?)\]/,'$1');
// 去掉多余内容,替换清单内容
content = content.replace(/#.*\s/g, '').replace(/(-\s\[\s\]\s)(.*)/g, `<li><i style="margin-right: 5px;" class="fa-regular fa-circle"></i>$2</li>`).replace(/(-\s\[x\]\s)(.*)/g, `<li class="achieve"><i style="margin-right: 5px;" class="fa-regular fa-circle-check"></i>$2</li>`);
// 渲染数据
let div = document.createElement('div');
div.className = 'list_item';
div.innerHTML = `<h3>${title}</h3><ul>${content}</ul>`;
box.appendChild(div);
});
waterfall('#todolist');
}).catch()
}

// 清单函数
  • _config.butterfly.yml 主题配置文件中 inject 下的 bottom 引入 project.js 文件
1
2
3
4
5
6
7
8
9
  ···

inject:
head:
···
bottom:
- <script type="text/javascript" src="/js/memos/project.js"></script> # memos清单页 - 逻辑函数

···

memos api地址格式如下所示:
https://memos地址/api/v1/memo?creatorId=用户ID&tag=标签名

memos地址就是首页地址,如:memos.meuicat.com

用户ID获取方式:

  • 点击个人头像,然后点击 RSS

  • 根据浏览器链接获取ID

如url是:https://memos.meuicat.com/u/1/rss.xml
则creatorId就是1
最后完整链接如下:
https://memos.meuicat.com/api/v1/memo?creatorId=1&tag=清单
能看到数据则为正确链接

  • Memos用法:
1
2
3
#清单 [我是标题栏]
- [ ] 我是内容1
- [x] 我是内容2

注意:
前面的 #清单 是固定的
标题用中括号包起来
已完成的将括号内的空格改成 x 即可