memos 使用指南:自建一个备忘录中心服务

图片[1]-memos 使用指南:自建一个备忘录中心服务-大海博客

不知道小伙伴们平时有没有写日记的习惯?虽说这年头正经人都不写日记了,但我们每天要处理各种各样的事情,一本备忘录还是有必要的。

这类应用其实有很多,我自己也体验过一些。有的集成了许多花里胡哨的功能;有的则是充斥着各种广告。于是我寻思着,那还不如自建一个服务来得直接。

项目介绍

memos[1] 是一个开源、自托管的备忘录中心,项目基于 Golang 和 TypeScript 开发。本文将介绍 memos 的安装和使用方法。

官网:https://usememos.com/

可以说是支持 Docker 自部署的 flomo ,而且有 API 调取数据和发 Memos 。

部署及更新代码

推荐使用 docker-compose.yml 部署,方便制定数据储存位置及更新版本,其中使用 ${PWD} 指定路径为当前文件夹。

version: "3.0"
services:
  memos:
    image: neosmemo/memos:latest
    container_name: memos
    volumes:
      - ${PWD}/.memos/:/var/opt/memos
    ports:
      - 5230:5230
图片[2]-memos 使用指南:自建一个备忘录中心服务-大海博客

宝塔为例:新建网站,新建 yml,开终端,丢代码。

docker-compose up -d

版本更新也是 一行代码 搞定:

docker-compose pull && docker-compose up -d --force-recreate

当然,也可以官方的一句拉起 Docker。之后更新需要先去 docker 管理器里删除镜像,再输入下句拉起服务。

docker run -d --name memos -p 5230:5230 -v ${PWD}/.memos/:/var/opt/memos neosmemo/memos:latest

注意不定时备份网址目录下的 .memos 文件夹,所有数据都在这。

添加反代

操作如下:在宝塔面版 新建网站 -> 设置 -> 反代 -> 添加代理 -> 设置名称和目标url即可。如下图:

图片[3]-memos 使用指南:自建一个备忘录中心服务-大海博客

使用指南

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

图片[4]-memos 使用指南:自建一个备忘录中心服务-大海博客

获取api地址

下面节中所提到的memos api地址格式如下:

https://memos网址/api/memo?creatorId=用户ID&tag=标签名&limit=限制数量

其中memos地址就是首页地址,如:www.niexiaoliang.love
用户ID的获取方法如下:

  1. 发送一条公共memos
  2. 点击探索接着点击用户名
图片[5]-memos 使用指南:自建一个备忘录中心服务-大海博客
  1. 查看URL获取ID
    如url是:http://www.niexiaoliang.love/u/1
    则creatorId就是1
    最后完整链接如下:
    http://www.niexiaoliang.love/api/memo?creatorId=1&tag=说说&limit=10
    能看到数据则为正确链接

说说功能

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

懒加载问题

打开文件 [blogroot]\themes\butterfly\scripts\filters\post_lazyload.js,将第13行的

return htmlContent.replace(/(<img.*? src=)/ig, `$1 "${bg}" data-lazy-src=`)

修改为

return htmlContent.replace(/(<img(?!.*?class[\t]*=[\t]*['"].*?no-lazyload.*?['"]).*? src=)/gi, `$1 "${bg}" data-lazy-src=`);

实现

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

<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=你的ID&tag=说说&limit=30').then(res => res.json()).then(data => { // 注意修改域名和用户id
        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 no-lazyload avatar" src="https://q1.qlogo.cn/g?b=qq&nk=990320751&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-02-06更新
        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.map(item => { return item.replace(/!\[.*\]\((.*?)\)/, '$1') }).forEach(e => content += `<a href="${e}" data-fancybox="gallery" class="fancybox" data-thumb="${e}"><img class="no-lazyload" src="${e}"></a>` // 2023-02-06更新
        )
        content += '</div>'
    }
    return {
        content: content,
        tag: tag ? tag[0].replace(/\{(.*?)\}/,'$1') : '无标签',
        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>

使用

使用的格式如下:

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

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

首页说说轮播

修改源码

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

如下图所示:

图片[6]-memos 使用指南:自建一个备忘录中心服务-大海博客

添加css

在自定义css文件中添加如下代码,

/* 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文件中添加如下代码,
引入时放在inject的bottom里面,不要放在head里。按照注释修改代码。
为了方便没有添加动画,切换比较生硬(其实本人还挺喜欢的)。可以自行使用swiper等添加动画。

// 存数据
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=用户ID&tag=说说&limit=10').then(res => res.json()).then(data => { // 更改地址和ID
            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创建页面。
然后粘贴如下代码并按照注释修改内容。

<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=用户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()
}
</script>

使用

使用的格式如下:

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

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

图片[7]-memos 使用指南:自建一个备忘录中心服务-大海博客

使用心得

#tag 后面必须有个空格才能创建 tag

文章推荐:

© 版权声明
THE END
喜欢就支持一下吧
点赞15赞赏 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容