一、前言

一般来讲Memos均通过Docker部署,似乎还有两种方式但是我不了解。
我想表达的是:最好有服务器

2023-01-15更新:说说功能的图片匹配的正则漏写了一个,导致多图会出错,已在代码中标注位置,替换即可。
新增:首页说说轮播(忘了写了)

效果预览

第一次知道Memos是在@林木木的这篇文章里:Hi,Memos
除此之外木木大佬还有几篇关于Memos的文章,感兴趣的可以看一下。

之前因为没需求一直没使用,机缘巧合下接触了一下。一发不可收拾。memos原来这么好用。

二、部署

部署的话木木大佬的文章写的很清楚了,直接看就行了:Hi,Memos
里面那个版本更新的命令可以记一下,因为Memos更新速度还是挺快的。也就是下面这行命令:

1
docker-compose down && docker image rm neosmemo/memos:latest && docker-compose up -d

添加反代

木木大佬没说部署之后添加反代的事,在这里补充一下。
操作如下:宝塔面版新建网站 -> 设置 -> 反代 -> 添加代理 -> 设置名称和目标url即可。如下图:
添加反代

三、使用指南

其实很容易搞懂基础的使用,如下图所示:
更多的就自己研究研究吧。
memos

四、说说功能

首先说明,这一切只是我定的规则而已,且功能不多。
你可以随意修改代码来实现你想实现的效果。

实现

首先通过hexo n page xxx创建页面,相信都很熟悉了。
然后粘贴如下代码并按照注释修改内容。

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
<style>
/* 页面初始化 */
div#page {
background: none;
border: 0;
padding: 0;
}

[data-theme=dark] #twikoo .tk-content,
#twikoo .tk-content {
padding: 0;
background: transparent;
}

.talk_item,
.tk-expand,
.tk-comments-container>.tk-comment,
.tk-submit:nth-child(1){
background: var(--card-bg);
border: 1px solid #e0e3ed;
box-shadow: 0 5px 10px rgb(189 189 189 / 10%);
transition: all .3s ease-in-out;
border-radius: 12px;
}
.talk_item:hover,
.tk-comments-container>.tk-comment:hover,
.tk-submit:nth-child(1):hover {
border-color: #49b1f5;
}

.tk-submit {
padding: 20px 10px 0;
}

.tk-comments-container>.tk-comment {
padding: 15px;
}

/* 页面初始化结束 */

#talk{
margin-top: 1rem;
}

#talk .loading {
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
}

#talk .loading img {
width: 200px;
}

.talk_item {
display: flex;
flex-direction: column;
padding: 20px;
margin-bottom: 15px;
}

.avatar {
margin: 0 !important;
width: 60px;
height: 60px;
border-radius: 10px;
}


.talk_bottom,
.talk_meta {
display: flex;
align-items: center;
width: 100%;
line-height: 1.5;
}
.talk_bottom{
justify-content: space-between;
}
.info {
display: flex;
flex-direction: column;
margin-left: 10px;
}
span.talk_nick {
color: #6dbdc3;
font-size: 1.2rem;
}
svg.is-badge.icon {
width: 15px;
margin-left: 5px;
padding-top: 3px;
}
span.talk_date {
opacity: .6;
}

.talk_content {
line-height: 1.5;
margin-top: 10px;
}
.zone_imgbox {
display: flex;
flex-wrap: wrap;
--w: calc(25% - 8px);
gap: 10px;
margin-top: 5px;
}
.zone_imgbox a {
display: block;
border-radius: 12px;
width: var(--w);
aspect-ratio: 1/1;
position: relative;
}

.zone_imgbox img {
width: 100%;
height: 100%;
margin: 0 !important;
object-fit: cover;
}
/* 底部 */

.talk_bottom {
opacity: .9;
}
.talk_bottom .icon {
color: var(--font-color);
float: right;
transition: all .3s;
}

.talk_bottom .icon:hover {
color: #49b1f5;
}

span.talk_tag{
font-size: 14px;
}
.talk_content>a {
margin: 0 3px;
color: #ff7d73 !important;
}
.talk_content>a:hover{
text-decoration: none !important;
color: #ff5143 !important
}

/* 提醒 */

.limit {
transition: all .3s ease-in-out;
color: rgba(76, 73, 72, 0.6);
}

[data-theme=dark] .limit {
color: rgba(255, 255, 255, 0.5);
}

.limit {
display: none;
text-align: center;
margin-top: 20px;
color: var(--font-color);
}
@media screen and (max-width: 900px) {
.zone_imgbox {
--w: calc(33% - 5px);
}
#talk{
margin: 10px 3px 0
}
#post-comment{
margin: 0 3px
}
}

@media screen and (max-width: 768px) {
.zone_imgbox {
gap: 6px;
}
.zone_imgbox {
--w: calc(50% - 3px);
}
span.talk_date {
font-size: 14px;
}
}
</style>

<div id="talk">
<div class='loading'><img src="/img/loading.svg" alt="加载中..."></div>
</div>

<div class="limit">- 只展示最近30条说说 -</div>
<script>
pageTalk();
// 页面说说
function pageTalk() {
fetch('https://你的memos地址/api/memo?creatorId=1&tag=说说&limit=30').then(res => res.json()).then(data => { // 注意修改域名
let items = [],
html = '',
icon = '<svg viewBox="0 0 512 512"xmlns="http://www.w3.org/2000/svg"class="is-badge icon"><path d="m512 268c0 17.9-4.3 34.5-12.9 49.7s-20.1 27.1-34.6 35.4c.4 2.7.6 6.9.6 12.6 0 27.1-9.1 50.1-27.1 69.1-18.1 19.1-39.9 28.6-65.4 28.6-11.4 0-22.3-2.1-32.6-6.3-8 16.4-19.5 29.6-34.6 39.7-15 10.2-31.5 15.2-49.4 15.2-18.3 0-34.9-4.9-49.7-14.9-14.9-9.9-26.3-23.2-34.3-40-10.3 4.2-21.1 6.3-32.6 6.3-25.5 0-47.4-9.5-65.7-28.6-18.3-19-27.4-42.1-27.4-69.1 0-3 .4-7.2 1.1-12.6-14.5-8.4-26-20.2-34.6-35.4-8.5-15.2-12.8-31.8-12.8-49.7 0-19 4.8-36.5 14.3-52.3s22.3-27.5 38.3-35.1c-4.2-11.4-6.3-22.9-6.3-34.3 0-27 9.1-50.1 27.4-69.1s40.2-28.6 65.7-28.6c11.4 0 22.3 2.1 32.6 6.3 8-16.4 19.5-29.6 34.6-39.7 15-10.1 31.5-15.2 49.4-15.2s34.4 5.1 49.4 15.1c15 10.1 26.6 23.3 34.6 39.7 10.3-4.2 21.1-6.3 32.6-6.3 25.5 0 47.3 9.5 65.4 28.6s27.1 42.1 27.1 69.1c0 12.6-1.9 24-5.7 34.3 16 7.6 28.8 19.3 38.3 35.1 9.5 15.9 14.3 33.4 14.3 52.4zm-266.9 77.1 105.7-158.3c2.7-4.2 3.5-8.8 2.6-13.7-1-4.9-3.5-8.8-7.7-11.4-4.2-2.7-8.8-3.6-13.7-2.9-5 .8-9 3.2-12 7.4l-93.1 140-42.9-42.8c-3.8-3.8-8.2-5.6-13.1-5.4-5 .2-9.3 2-13.1 5.4-3.4 3.4-5.1 7.7-5.1 12.9 0 5.1 1.7 9.4 5.1 12.9l58.9 58.9 2.9 2.3c3.4 2.3 6.9 3.4 10.3 3.4 6.7-.1 11.8-2.9 15.2-8.7z"fill="#1da1f2"></path></svg>';
data.data.forEach(item => { items.push(Format(item)) });
if (items.length == 30) document.querySelector('.limit').style.display = 'block';
items.forEach(item => {
html += `<div class="talk_item"><div class="talk_meta"><img class="no-lightbox avatar" src="https://q1.qlogo.cn/g?b=qq&nk=553344777&s=5"><div class="info"><span class="talk_nick">Leonus${icon}</span><span class="talk_date">${item.date}</span></div></div><div class="talk_content">${item.content}</div><div class="talk_bottom"><div><span class="talk_tag"># ${item.tag}</span></div><a href="javascript:;"onclick="goComment('${item.text}')"><span class="icon"><i class="fa-solid fa-message fa-fw"></i></span></a></div></div>` // 注意修改头像链接和名称
})
document.getElementById('talk').innerHTML = html
})
}
// 页面评论
function goComment(e) {
var n = document.querySelector(".el-textarea__inner")
n.value = `> ${e}\n\n`;
n.focus();
btf.snackbarShow("无需删除空行,直接输入评论即可", !1, 2e3);
}
// 页面内容格式化
function Format(item) {
let date = getTime(new Date(item.createdTs * 1000).toString()),
content = item.content,
tag = item.content.match(/(?<=\{)(.*?)(?=\})/g),
imgls = content.match(/(?<=!\[.*\]\()(.*?)(?=\))/g), // 2023-01-15更新
text = ''
text = content.replace(/#(.*?)\s/g, '').replace(/\!\[(.*?)\]\((.*?)\)/g, '').replace(/\{(.*?)\}/g, '')
content = text.replace(/(?<!!)\[(.*?)\]\((.*?)\)/g, `<a href="$2">@$1</a>`);
if (imgls) {
content += `<div class="zone_imgbox">`
imgls.forEach(e => content += `<a href="${e}" data-fancybox="gallery" class="fancybox" data-thumb="${e}"><img src="${e}"></a>`
)
content += '</div>'
}
return {
content: content,
tag: tag ? tag : '无标签',
date: date,
text: text.replace(/(?<!!)\[(.*?)\]\((.*?)\)/g, '[链接]' + `${imgls?'[图片]':''}`)
}
}
// 页面时间格式化
function getTime(time) {
let d = new Date(time),
ls = [d.getFullYear(), d.getMonth() + 1, d.getDate(), d.getHours(), d.getMinutes(), d.getSeconds()];
for (let i = 0; i < ls.length; i++) {
ls[i] = ls[i] <= 9 ? '0' + ls[i] : ls[i] + ''
}
if (new Date().getFullYear() == ls[0]) return ls[1] + '月' + ls[2] + '日 ' + ls[3] +':'+ ls[4]
else return ls[0] + '年' + ls[1] + '月' + ls[2] + '日 ' + ls[3] +':'+ ls[4]
}
</script>

使用

使用的格式如下:

1
#说说 {说说标签} 我是内容 [我是链接](链接地址) ![](图片链接)

注意,前面的#说说 是固定的。标签用大括号包起来。
使用格式

五、首页说说轮播

修改源码

修改如下源码:[blogroot]\themes\butterfly\layout\includes\layout.pug,在main(第23行左右)上面添加如下代码。
注意根据注释修改代码中的说说页面地址。

1
2
3
4
5
6
7
8
if (is_home())
#main_top
//- 注意修改下面的onclick,如果你没使用pjax,将pjax.loadUrl("/zone/")改成location.pathname="/zone/",zone是说说页面。,这行注释最好删掉。
#bber-talk.wow.animate__flipInX(onclick=`pjax.loadUrl("/zone/")`)
svg.icon(t='1660960757124', viewBox='0 0 1024 1024', version='1.1', xmlns='http://www.w3.org/2000/svg', p-id='3946', width='200', height='200')
path(d='M526.432 924.064c-20.96 0-44.16-12.576-68.96-37.344L274.752 704H192c-52.928 0-96-43.072-96-96V416c0-52.928 43.072-96 96-96h82.752l182.624-182.624c24.576-24.576 47.744-37.024 68.864-37.024C549.184 100.352 576 116 576 160v704c0 44.352-26.72 60.064-49.568 60.064zM192 384c-17.632 0-32 14.368-32 32v192c0 17.664 14.368 32 32 32h96c8.48 0 16.64 3.36 22.624 9.376l192.064 192.096c3.392 3.36 6.496 6.208 9.312 8.576V174.016a145.824 145.824 0 0 0-9.376 8.608l-192 192C304.64 380.64 296.48 384 288 384h-96zM687.584 730.368a31.898 31.898 0 0 1-18.656-6.016c-14.336-10.304-17.632-30.304-7.328-44.672l12.672-17.344C707.392 617.44 736 578.624 736 512c0-69.024-25.344-102.528-57.44-144.928-5.664-7.456-11.328-15.008-16.928-22.784-10.304-14.336-7.04-34.336 7.328-44.672 14.368-10.368 34.336-7.04 44.672 7.328 5.248 7.328 10.656 14.464 15.968 21.504C764.224 374.208 800 421.504 800 512c0 87.648-39.392 141.12-74.144 188.32l-12.224 16.736c-6.272 8.704-16.064 13.312-26.048 13.312z', p-id='3947')
path(d='M796.448 839.008a31.906 31.906 0 0 1-21.088-7.936c-13.28-11.648-14.624-31.872-2.976-45.152C836.608 712.672 896 628.864 896 512s-59.392-200.704-123.616-273.888c-11.648-13.312-10.304-33.504 2.976-45.184 13.216-11.648 33.44-10.336 45.152 2.944C889.472 274.56 960 373.6 960 512s-70.528 237.472-139.488 316.096c-6.368 7.232-15.2 10.912-24.064 10.912z', p-id='3948')
ul.talk-list 说说加载中。。。

如下图所示:
添加代码

添加css

在自定义css文件中添加如下代码,不会自定义css文件的看这篇:Hexo博客添加自定义css和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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
/* maintop */

div#main_top {
z-index: 1;
max-width: 1200px;
margin: 20px auto -15px;
width: 100%;
padding: 0 15px;
}

@media screen and (min-width: 2000px) {
div#main_top {
max-width: 1500px;
}
}

#bber-talk {
border-radius: 12px;
box-shadow: none;
border: 1px solid #e0e3ed;
box-sizing: border-box;
transition: all .3s ease-in-out;
cursor: pointer;
width: 100%;
min-height: 50px;
background: var(--card-bg);
padding: .5rem 1rem;
display: flex;
align-items: center;
overflow: hidden;
font-weight: 700;
}

#bber-talk:hover {
border-color: #49b1f5;
box-shadow: none;
}

#bber-talk,
#bber-talk a {
color: var(--font-color);
}

#bber-talk svg.icon {
width: 1em;
height: 1em;
vertical-align: -.15em;
fill: currentColor;
overflow: hidden;
font-size: 20px;
}

#bber-talk .item i {
margin-left: 5px;
}

#bber-talk>i {
font-size: 1.1rem;
}

#bber-talk .talk-list {
flex: 1;
max-height: 32px;
font-size: 16px;
padding: 0;
margin: 0;
overflow: hidden;
}

#bber-talk .talk-list :hover {
color: #49b1f5 !important;
transition: all .2s ease-in-out;
}

#bber-talk .talk-list li {
list-style: none;
width: 100%;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
margin-left: 10px;
}

@media screen and (min-width: 768px) {
#bber-talk .talk-list {
text-align: center;
margin-right: 20px;
}
}

添加js

在自定义js文件中添加如下代码,不会自定义js文件的看这篇:Hexo博客添加自定义css和js文件
引入时放在inject的bottom里面,不要放在head里。按照注释修改代码。
为了方便没有添加动画,切换比较生硬(其实本人还挺喜欢的)。可以自行使用swiper等添加动画。

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
// 存数据
function saveData(name, data) { localStorage.setItem(name, JSON.stringify({ 'time': Date.now(), 'data': data })) };
// 取数据
function loadData(name, time) {
let d = JSON.parse(localStorage.getItem(name));
// 过期或有错误返回 0 否则返回数据
if (d) {
let t = Date.now() - d.time
if (-1 < t && t < (time * 60000)) return d.data;
}
return 0;
};

let talkTimer = null;
function indexTalk() {
if (talkTimer) {
clearInterval(talkTimer)
talkTimer = null;
}
if (!document.getElementById('bber-talk')) return

function toText(ls) {
let text = []
ls.forEach(item => {
text.push(item.content.replace(/#(.*?)\s/g, '').replace(/\{(.*?)\}/g, '').replace(/\!\[(.*?)\]\((.*?)\)/g, '<i class="fa-solid fa-image"></i>').replace(/(?<!!)\[(.*?)\]\((.*?)\)/g, '<i class="fa-solid fa-link"></i>'))
});
return text
}

function talk(ls) {
let html = ''
ls.forEach((item, i) => { html += `<li class="item item-${i + 1}">${item}</li>` });
let box = document.querySelector("#bber-talk .talk-list")
box.innerHTML = html;
talkTimer = setInterval(() => {
box.appendChild(box.children[0]);
}, 3000);
}

let d = loadData('talk', 10);
if (d) talk(d);
else {
fetch('https://你的memos地址/api/memo?creatorId=1&tag=说说&limit=10').then(res => res.json()).then(data => { // 更改地址
data = toText(data.data)
talk(data);
saveData('talk', data);
})
}
}
indexTalk();

// pjax注释掉上面的 indexTalk(); 使用如下方法:
// function whenDOMReady() {
// indexTalk();
// }

// whenDOMReady()
// document.addEventListener("pjax:complete", whenDOMReady)

六、清单功能

再次说明,这一切只是我定的规则而已,你可以随意修改代码来实现你想实现的效果。

实现

还是先使用hexo n page xxx创建页面。
然后粘贴如下代码并按照注释修改内容。

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
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
<style>
/* 页面初始化 */
div#page {
background: none;
border: 0;
padding: 0;
}
[data-theme=dark] #twikoo .tk-content,
#twikoo .tk-content {
padding: 0;
background: transparent;
}

.tk-comments-container>.tk-comment,
.tk-submit:nth-child(1){
background: var(--card-bg);
border: 1px rgba(188, 188, 188, 0.8) solid;
box-shadow: 0 5px 10px rgb(189 189 189 / 10%);
transition: all .3s ease-in-out;
border-radius: 12px;
}

.tk-comments-container>.tk-comment:hover,
.tk-submit:nth-child(1):hover {
border-color: #6dc3fd;
}

.tk-submit {
padding: 20px 10px 0;
}

.tk-comments-container>.tk-comment {
padding: 15px;
}

/* 页面初始化结束 */
div#todolist {
display: flex;
flex-wrap: wrap;
margin-top: 1rem;
}
.list_item {
display: inline-block;
width: calc(50% - .4rem);
background: #ffe3dd;
border-radius: 12px;
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;
border-bottom: var(--todo-border);
}
.list_item ul {
font-size: 17px;
padding: 0 !important;
margin: 0;
}
.list_item li{
margin: 0 !important;
border-bottom: var(--todo-border);
}
.list_item li::marker {
content: none;
}
li.achieve {
opacity: .8;
text-decoration: line-through;
}
@media screen and (max-width: 900px) {
div#todolist {
margin: 1rem 5px 0;
}
}
@media screen and (max-width: 768px) {
.list_item{
width: 100%;
}
}
</style>


<div id="todolist"></div>


<script>
// 瀑布流函数,不用管
function waterfall(t){function e(t,e){var n=window.getComputedStyle(e);return parseFloat(n["margin"+t])||0}function n(t){return t+"px"}function r(t){return parseFloat(t.style.left)}function o(t){return t.clientWidth}function l(t){return function(t){return parseFloat(t.style.top)}(t)+function(t){return t.clientHeight}(t)+e("Bottom",t)}function i(t){return r(t)+o(t)+e("Right",t)}function u(t){t=t.sort((function(t,e){return l(t)===l(e)?r(e)-r(t):l(e)-l(t)}))}function a(e){o(t)!=h&&(e.target.removeEventListener(e.type,arguments.callee),waterfall(t))}"string"==typeof t&&(t=document.querySelector(t));var s=[].map.call(t.children,(function(t){return t.style.position="absolute",t}));t.style.position="relative";var f=[];s.length&&(s[0].style.top="0px",s[0].style.left=n(e("Left",s[0])),f.push(s[0]));for(var p=1;p<s.length;p++){var c=s[p-1],y=s[p];if(!(i(c)+o(y)<=o(t)))break;y.style.top=c.style.top,y.style.left=n(i(c)+e("Left",y)),f.push(y)}for(;p<s.length;p++){u(f);y=s[p];var d=f.pop();y.style.top=n(l(d)+e("Top",y)),y.style.left=n(r(d)),f.push(y)}u(f);var v=f[0];t.style.height=n(l(v));var h=o(t);window.addEventListener?window.addEventListener("resize",a):document.body.onresize=a}

// 清单函数
todolist();
function todolist() {
fetch('https://你的memos地址/api/memo?creatorId=1&tag=清单').then(res => res.json()).then(data => { // 注意替换链接
// 获取并处理数据
data = data.data
let box = document.getElementById('todolist')
data.forEach(item => {
// 处理数据
let content = item.content,
title = content.match(/(?<=#.*\[)(.*?)(?=\])/g);
// 去掉多余内容,替换清单内容
content = content.replace(/#.*\s/g, '').replace(/(-\s\[\s\]\s)(.*)(?=\s*)/g, `<li><i style="margin-right: 5px;" class="fa-regular fa-circle"></i>$2</li>`).replace(/(-\s\[x\]\s)(.*)(?=\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()
}
</script>

使用

使用的格式如下:

1
2
3
#清单 [想去的地方]
- [ ] 轻笑的米奇妙妙屋(轻笑让我加的)
- [x] 已完成的清单

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