前言

以前一直在用微软的 OneNote 作为笔记软件,进行资料收集和文件整理,后来在经理了一次 OneDrive 误删文件倒是 OneNote 整个笔记本丢失以后,就对这些云端方案不是很感兴趣了。于是在搜寻本地化的解决方案,网上推荐最多是 notion 和 obsidian,调研了一番以后,选择了全面开源并免费的 obsidian 作为知识管理工具,同时也作为日常随笔记录的工具。并且基于 markdown 的写作,也方便快速通过 hexo 将内容转换为博文进行发布。

一、安装与插件

1.1 安装

Obsidian 的安装非常简单,参考 电脑重装纪实,直接一条 winget install Obsidian.Obsidian 命令即可安装。

1.2 需求分析与插件选择

Obsidian 支持第三方插件,并且在软件中内置了社区插件检索,在选项-第三方插件中,关闭安全模式即可使用 Obsidian 庞大的第三方插件库。

本人在日常记录方面有如下几个需求:

  1. 支持快速检索:格式统一,分类自由,创建便利,方便检索。
  2. 支持账单统计:方便高效的记录每一笔收支,方便控制消费欲望,避免盲目消费。
  3. 多端记录同步:台式机、笔记本、手机、平板等设备可以互相同步。

Obsidian 本身自带的日记、ZK 卡片功能就可以快速构建笔记,而其他功能大部分都可以找到满足需求的社区插件,总结列表如下:

  1. (官方)日记插件:满足日记的快速生成,同时利用模板可以统计格式。
  2. (官方)ZK 卡片插件:快速生成临时笔记,记录偶然的想法。
  3. (社区)Templater 插件:模板增强插件,提供了强大的语法支持,更方便在文档创建时规整格式。
  4. (社区)Calendar 插件:增强日历功能,在侧面板中提供日历视图,快速定位日记,同时支持周记的生成,进一步归纳总结思路。
  5. (社区)Checklist 插件:在侧面板中提供任务清单视图,可以快速检索未完成的任务。
  6. (社区)Dataview 插件:提供强大的数据查询功能,利用其提供的语法,可以快速整理计算每日账单总额。
  7. (社区)Remotely Save 插件:提供远程存储和同步。

1.3 插件配置

  1. Templater 插件:

    1. 首先构建一个用于存放模板的目录,由于目录管理默认安装字符排序,所以推荐直接在目录前加上数字,我这里选择0-模板作为模板目录。

    2. 将插件的Syntax HighlightingAutomatic jump to cursorTrigger Templater on new file creation三个功能全部激活。

    3. 记账、日记、周记、ZK 卡片总共四个模板,具体代码如下,可以根据需求进行加减。

      1. 记账模板(转账通过一笔支出一笔收入实现)

        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
        <%*
        //判断模板应用模式
        if(tp.config.run_mode==0){
        //如果通过新建笔记应用模板,则将笔记存放到指定位置
        tp.file.move("/2-日记/2.1-账单/"+tp.date.now("YYYYMMDDHHmmss"))
        }
        //账单类型(清账用于管理账户余额)
        const bill_type=[
        "支出",
        "收入",
        "清账"
        ]
        //消费账户(根据需求设置消费所使用的账户)
        const bill_account=[
        "微信",
        "支付宝",
        "其他"
        ]
        //消费渠道(根据需求设置消费渠道)
        const bill_source = [
        "线下支付",
        "在线支付",
        "淘宝",
        "京东"
        ]
        //消费类型(根据需求设置消费类型)
        const expense_type=[
        "食品酒水",
        "日常用品",
        "学习进修",
        "衣服饰品",
        "数码产品",
        "行车交通",
        "交流通讯",
        "寝室住宿",
        "休闲娱乐",
        "人情往来",
        "医疗保险",
        "转账",
        "其他"
        ]
        //收入类型(根据需求设置收入类型)
        const income_type=[
        "工资",
        "转账",
        "生活费",
        "红包"
        ]
        //首先选择账单类型
        billType= await tp.system.suggester(bill_type, bill_type,false,'请选择账单类型')
        //随后根据不同类型选择不同记录过程
        //支出
        if (billType == bill_type[0]) {
        billAccount = await tp.system.suggester(bill_account, bill_account,false,'请选择消费账户')
        billSource = await tp.system.suggester(bill_source, bill_source,false,'请选择消费渠道')
        type = await tp.system.suggester(expense_type,expense_type,false,'请选择账单类型')
        goods = await tp.system.prompt("请输入账单内容")
        price = await tp.system.prompt("请输入价格")
        date = await tp.system.prompt("请输入账单日期",tp.date.now("YYYY-MM-DD"))
        }
        //收入
        if (billType == bill_type[1]) {
        billAccount = await tp.system.suggester(bill_account, bill_account,false,'请选择入账账户')
        billSource = await tp.system.suggester(bill_source, bill_source,false,'请选择入账资金来源')
        type = await tp.system.suggester(income_type,income_type,false,'请选择收入类型')
        goods = await tp.system.prompt("请输入入账明细")
        price = await tp.system.prompt("请输入入账金额")
        date = await tp.system.prompt("请输入入账日期",tp.date.now("YYYY-MM-DD"))
        }
        //清账
        if (billType == bill_type[2]) {
        billAccount = await tp.system.suggester(bill_account, bill_account,false,'请选择清账账户')
        billSource = "手动输入"
        type = "资金清账"
        goods = "清账"
        price = await tp.system.prompt("请输入目前账户金额")
        date = await tp.system.prompt("请输入清账日期",tp.date.now("YYYY-MM-DD"))
        }

        -%>
        ---
        tags:
        - 账单
        date: <% date %>
        goods:
        - <% goods %><% tp.file.cursor(0) %>
        type: <% type %>
        price: <% price %>
        billSource: <% billSource %>
        billAccount: <% billAccount %>
        billType: <% billType %>
        ---
        [[<% tp.date.now("YYYY-MM-DD ddd", 0, date, "YYYY-MM-DD") %>]]
      2. 日记模板(在生成模板的同时,链接到前一天后一天以及当周周报)

        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
        ---
        title: tiger的生活小记 (<% tp.date.now("YYYY-MM-DD", 0, tp.file.title, "YYYY-MM-DD ddd") %>)
        author: tiger
        categories:
        - 生活
        location:
        - 地点
        locations:
        weather:
        - 天气
        tags:
        - 日记
        date: <% tp.date.now("YYYY-MM-DD ddd") %>
        ---
        <<[[<% tp.date.now("gggg-[W]ww", 0, tp.file.title, "YYYY-MM-DD ddd") %>|每周总结]]>>

        << [[<% tp.date.now("YYYY-MM-DD ddd", -1, tp.file.title, "YYYY-MM-DD ddd") %>]] | [[<% tp.date.now("YYYY-MM-DD ddd", 1, tp.file.title, "YYYY-MM-DD ddd") %>]] >>

        ## 生活记录

        ### 上午`(06:00-13:00)`

        ### 下午`(13:00-18:00)`

        ### 晚上`(18:00-24:00)`

        ### 熬夜`(24:00-06:00)`

        ## 学习记录
        ## 待办事项
        #todo
        - [ ]
        ## ZK卡片
        ```dataviewjs
        const data = dv.pages('"7-ZK卡片"') //查询ZK卡片目录下的笔记
        .where( b => dv.equal(b.date.split(" ")[0],"<% tp.date.now("YYYY-MM-DD", 0, tp.file.title, "YYYY-MM-DD ddd") %>"))//过滤出当天卡片

        const tableData = data.sort(b => b.date,'desc') //按日期倒序
        .map(b => [b.file.link, b.title,b.date]) //取出主要字段
        dv.table(["笔记", "标题","创建时间"], tableData) //收入表格
        ```
        ## 日常收支
        ```dataviewjs
        const data = dv.pages('"2-日记/2.1-账单"') //查询账单目录下的笔记
        .where(b=> dv.equal(b.date,dv.date("<% tp.date.now("YYYY-MM-DD", 0, tp.file.title, "YYYY-MM-DD ddd") %>")))//过滤出当天账单

        const indata = data.where(b => dv.equal(b.billType,"收入"))

        const outdata = data.where(b => dv.equal(b.billType,"支出"))

        const intableData = indata.sort(b => b.date,'desc') //按日期倒序
        .map(b => [b.file.link, b.billSource,b.billAccount, b.type, b.date, b.goods, b.price]) //取出主要字段

        const outtableData = outdata.sort(b => b.date,'desc') //按日期倒序
        .map(b => [b.file.link, b.billSource,b.billAccount, b.type, b.date, b.goods, b.price]) //取出主要字段

        var totalout = 0
        var totalin =0

        // 统计收入总计
        for(let price of indata.price.array()) {
        totalin += price
        }
        // 统计消费总计
        for(let price of outdata.price.array()) {
        totalout += price
        }
        dv.header(3, "汇总")
        const a='账单数量: '+ data.length //账单数量
        const b='消费汇总: ¥'+ Math.round(totalout*100-totalin*100)/100 //收支总计


        var num= data.length
        var sum=Math.round(totalin*100-totalout*100)/100
        dv.table(["账单数量", "结余汇总¥"], dv.array([[[num],[sum]]])) //收入表格

        if(totalout>0){
        dv.header(4, "支出明细")
        const c='支出总计: ¥'+ Math.round(totalout*100)/100 //消费总计
        dv.list([c])
        dv.table(["笔记", "来源","账户", "类型","日期", "商品", "价格¥"], outtableData) //支出表格
        }
        if(totalin>0){
        dv.header(4, "收入明细")
        const d='收入总计: ¥'+ Math.round(totalin*100)/100 //收入总计
        dv.list([d])
        dv.table(["笔记", "来源","账户", "类型","日期", "商品", "价格¥"], intableData) //收入表格
        }
        ```
      3. 周记模板(汇总当周收支,并连接每天的日记)

        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
        ---
        title: tiger的生活小记 (<% tp.date.now("YYYY-MM-DD", 0, tp.file.title, "gggg-[W]ww") %> - <% tp.date.now("YYYY-MM-DD", 6, tp.file.title, "gggg-[W]ww") %>)
        author: tiger
        categories:
        - 生活
        tags:
        - 周记
        ---
        << [[<% tp.date.now("YYYY-MM-DD ddd", 0, tp.file.title, "gggg-[W]ww") %>]] | [[<% tp.date.now("YYYY-MM-DD ddd", 1, tp.file.title, "gggg-[W]ww") %>]] | [[<% tp.date.now("YYYY-MM-DD ddd", 2, tp.file.title, "gggg-[W]ww") %>]] | [[<% tp.date.now("YYYY-MM-DD ddd", 3, tp.file.title, "gggg-[W]ww") %>]] | [[<% tp.date.now("YYYY-MM-DD ddd", 4, tp.file.title, "gggg-[W]ww") %>]] | [[<% tp.date.now("YYYY-MM-DD ddd", 5, tp.file.title, "gggg-[W]ww") %>]] | [[<% tp.date.now("YYYY-MM-DD ddd", 6, tp.file.title, "gggg-[W]ww") %>]]>>

        ## 每周总结
        ```dataviewjs
        const data = dv.pages('"7-ZK卡片"') //查询ZK卡片目录下的笔记
        .where(b=> dv.date(b.date.split(" ")[0]) >= dv.date("<% tp.date.now("YYYY-MM-DD", 0, tp.file.title, "gggg-[W]ww") %>"))//过滤出当周卡片
        .where(b=> dv.date(b.date.split(" ")[0]) <= dv.date("<% tp.date.now("YYYY-MM-DD", 6, tp.file.title, "gggg-[W]ww") %>"))//过滤出当周卡片
        dv.header(3, "卡片汇总")
        const tableData = data.sort(b => b.date,'desc') //按日期倒序
        .map(b => [b.file.link, b.title,b.date]) //取出主要字段
        dv.table(["笔记", "标题","创建时间"], tableData) //收入表格
        ```
        ```dataviewjs
        const data = dv.pages('"2-日记/2.1-账单"') //查询账单目录下的笔记
        .where(b=> b.date >= dv.date("<% tp.date.now("YYYY-MM-DD", 0, tp.file.title, "gggg-[W]ww") %>"))//过滤出当周账单
        .where(b=> b.date <= dv.date("<% tp.date.now("YYYY-MM-DD", 6, tp.file.title, "gggg-[W]ww") %>"))//过滤出当周账单

        const indata = data.where(b => dv.equal(b.billType,"收入"))

        const outdata = data.where(b => dv.equal(b.billType,"支出"))

        const intableData = indata.sort(b => b.date,'desc') //按日期倒序
        .map(b => [b.file.link, b.billSource,b.billAccount, b.type, b.date, b.goods, b.price]) //取出主要字段

        const outtableData = outdata.sort(b => b.date,'desc') //按日期倒序
        .map(b => [b.file.link, b.billSource,b.billAccount, b.type, b.date, b.goods, b.price]) //取出主要字段

        var totalout = 0
        var totalin =0

        // 统计收入总计
        for(let price of indata.price.array()) {
        totalin += price
        }
        // 统计消费总计
        for(let price of outdata.price.array()) {
        totalout += price
        }
        dv.header(3, "汇总")
        const a='账单数量: '+ data.length //账单数量
        const b='消费汇总: ¥'+ Math.round(totalout*100-totalin*100)/100 //收支总计


        var num= data.length
        var sum=Math.round(totalin*100-totalout*100)/100
        dv.table(["账单数量", "结余汇总¥"], dv.array([[[num],[sum]]])) //收入表格

        if(totalout>0){
        dv.header(4, "支出明细")
        const c='支出总计: ¥'+ Math.round(totalout*100)/100 //消费总计
        dv.list([c])
        dv.table(["笔记", "来源","账户", "类型","日期", "商品", "价格¥"], outtableData) //支出表格
        }
        if(totalin>0){
        dv.header(4, "收入明细")
        const d='收入总计: ¥'+ Math.round(totalin*100)/100 //收入总计
        dv.list([d])
        dv.table(["笔记", "来源","账户", "类型","日期", "商品", "价格¥"], intableData) //收入表格
        }
        ```
      4. ZK 模板

        1
        2
        3
        4
        5
        6
        7
        8
        9
        10
        11
        12
        13
        14
        ---
        title: <% title = await tp.system.prompt("请输入卡片标题") %>
        aliases: [<% title %>]
        author: tiger
        tags:
        -
        date: '<% tp.date.now("YYYY-MM-DD HH:MM:SS",0,tp.file.title,"YYYYMMDDHHMMSS") %>'
        <%tp.file.rename(tp.date.now("YYYYMMDD-",0,tp.file.title,"YYYYMMDDHHMMSS") +title)%>
        ---
        << [[<% tp.date.now("YYYY-MM-DD ddd") %>]] >>
        ## <% title %>

        ## 参考
        -
  2. 日记插件:

    1. 将日期格式改为YYYY-MM-DD ddd 。前一节的日记模板通过读取文件名称获取日期,需要时间格式在语法上一直,本文将时间格式设置为 2022-06-05 周日
    2. 设置新日记保存目录,本文将其设置为2-日记,前一节账单目录为/2-日记/2.1-账单/
    3. 根据模板名称,设置日记模板位置,本文为0-模板/日记模板
  3. ZK 卡片插件:

    1. 设置新卡片保存目录,本文将其设置为7-ZK卡片
    2. 根据模板名称,设置 ZK 卡片模板位置,本文为0-模板/ZK模板
    3. 设置 ZK 卡 ID 格式,本文将其设置为YYYYMMDDHHMMSS。参考样例20220605160629,精确到秒防止连续创建导致卡片重名。
  4. Calendar 插件:

    1. 设置每周开始时间,默认为周一。
    2. 打开Show week number,方便建立周报
    3. Weekly Note Settings中:
      1. Weekly note format设置为gggg-[W]ww。参考样例为2022-W23
      2. Weekly note template设置为0-模板/周记模板
      3. Weekly note folder设置为2-日记
  5. Checklist 插件:

    1. 根据需求,在Tag name中添加检索用的 tag,默认只有todo,可以再添加一个中文的待办事项
  6. Dataview 插件:

    1. 打开Enable JavaScript Queries
    2. Date Format设置为yyyy-MM-dd,参考样例为2022-06-05.
    3. Date + Time Format设置为yyyy-MM-dd t,参考样例为2022-06-05 16:47
  7. Remotely Save 插件:(本文使用webdav作为远程存储方式,以下以坚果云为例。)

    1. 注册坚果云账户,登录后,在安全选项页面中,添加应用,获取对应密码。坚果云默认的 WebDAV 地址为https://dav.jianguoyun.com/dav/.
    2. 在插件的 WebDav 设置中,对应填写服务器地址,用户名,密码,随后检查连接是否可用。
    3. 在插件的基本设置中,修改运行条件,本文设置为启动后30s运行一次每30分钟运行一次。

二、运行

2.1 日记功能

在 Obsidian 主页面右侧找到日记插件界面,点击每日日期或左侧周数,即可创建对应笔记。
日历插件

2.2 todo 列表

在 Obsidian 主页面右侧找到 Tode list 插件界面,快速确定每个任务完成情况。
todolist

2.3 可视化

在 Obsidian 的关系图谱页面中,可以清晰的看到每笔日记的关联关系。
关系图谱

三、总结

目前 Obsidian 完美的满足了我对应全部文档数据的掌控欲,终究还是将数据存在本地比较安心。后续 Obsidian 还有许多有意思的功能值得分享,比如文本导出 PDF、Word,地理地图,整合 zotero 等。

参考文档