前言
以前一直在用微软的 OneNote 作为笔记软件,进行资料收集和文件整理,后来在经理了一次 OneDrive 误删文件倒是 OneNote 整个笔记本丢失以后,就对这些云端方案不是很感兴趣了。于是在搜寻本地化的解决方案,网上推荐最多是 notion 和 obsidian,调研了一番以后,选择了全面开源并免费的 obsidian 作为知识管理工具,同时也作为日常随笔记录的工具。并且基于 markdown 的写作,也方便快速通过 hexo 将内容转换为博文进行发布。
一、安装与插件
1.1 安装
Obsidian 的安装非常简单,参考 电脑重装纪实,直接一条 winget install Obsidian.Obsidian
命令即可安装。
1.2 需求分析与插件选择
Obsidian 支持第三方插件,并且在软件中内置了社区插件检索,在选项-第三方插件
中,关闭安全模式
即可使用 Obsidian 庞大的第三方插件库。
本人在日常记录方面有如下几个需求:
- 支持快速检索:格式统一,分类自由,创建便利,方便检索。
- 支持账单统计:方便高效的记录每一笔收支,方便控制消费欲望,避免盲目消费。
- 多端记录同步:台式机、笔记本、手机、平板等设备可以互相同步。
Obsidian 本身自带的日记、ZK 卡片功能就可以快速构建笔记,而其他功能大部分都可以找到满足需求的社区插件,总结列表如下:
- (官方)日记插件:满足日记的快速生成,同时利用模板可以统计格式。
- (官方)ZK 卡片插件:快速生成临时笔记,记录偶然的想法。
- (社区)Templater 插件:模板增强插件,提供了强大的语法支持,更方便在文档创建时规整格式。
- (社区)Calendar 插件:增强日历功能,在侧面板中提供日历视图,快速定位日记,同时支持周记的生成,进一步归纳总结思路。
- (社区)Checklist 插件:在侧面板中提供任务清单视图,可以快速检索未完成的任务。
- (社区)Dataview 插件:提供强大的数据查询功能,利用其提供的语法,可以快速整理计算每日账单总额。
- (社区)Remotely Save 插件:提供远程存储和同步。
1.3 插件配置
-
Templater 插件:
-
首先构建一个用于存放模板的目录,由于目录管理默认安装字符排序,所以推荐直接在目录前加上数字,我这里选择
0-模板
作为模板目录。 -
将插件的
Syntax Highlighting
、Automatic jump to cursor
、Trigger Templater on new file creation
三个功能全部激活。 -
记账、日记、周记、ZK 卡片总共四个模板,具体代码如下,可以根据需求进行加减。
-
记账模板(转账通过一笔支出一笔收入实现)
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") %>]] -
日记模板(在生成模板的同时,链接到前一天后一天以及当周周报)
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) //收入表格
}
``` -
周记模板(汇总当周收支,并连接每天的日记)
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) //收入表格
}
``` -
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 %>
## 参考
-
-
-
-
日记插件:
- 将日期格式改为
YYYY-MM-DD ddd
。前一节的日记模板通过读取文件名称获取日期,需要时间格式在语法上一直,本文将时间格式设置为2022-06-05 周日
。 - 设置新日记保存目录,本文将其设置为
2-日记
,前一节账单目录为/2-日记/2.1-账单/
- 根据模板名称,设置日记模板位置,本文为
0-模板/日记模板
- 将日期格式改为
-
ZK 卡片插件:
- 设置新卡片保存目录,本文将其设置为
7-ZK卡片
。 - 根据模板名称,设置 ZK 卡片模板位置,本文为
0-模板/ZK模板
- 设置 ZK 卡 ID 格式,本文将其设置为
YYYYMMDDHHMMSS
。参考样例20220605160629
,精确到秒防止连续创建导致卡片重名。
- 设置新卡片保存目录,本文将其设置为
-
Calendar 插件:
- 设置每周开始时间,默认为周一。
- 打开
Show week number
,方便建立周报 - 在
Weekly Note Settings
中:- 将
Weekly note format
设置为gggg-[W]ww
。参考样例为2022-W23
。 - 将
Weekly note template
设置为0-模板/周记模板
。 - 将
Weekly note folder
设置为2-日记
。
- 将
-
Checklist 插件:
- 根据需求,在
Tag name
中添加检索用的 tag,默认只有todo
,可以再添加一个中文的待办事项
。
- 根据需求,在
-
Dataview 插件:
- 打开
Enable JavaScript Queries
。 - 将
Date Format
设置为yyyy-MM-dd
,参考样例为2022-06-05
. - 将
Date + Time Format
设置为yyyy-MM-dd t
,参考样例为2022-06-05 16:47
。
- 打开
-
Remotely Save 插件:(本文使用
webdav
作为远程存储方式,以下以坚果云为例。)- 注册坚果云账户,登录后,在安全选项页面中,添加应用,获取对应密码。坚果云默认的 WebDAV 地址为
https://dav.jianguoyun.com/dav/
. - 在插件的 WebDav 设置中,对应填写服务器地址,用户名,密码,随后检查连接是否可用。
- 在插件的基本设置中,修改运行条件,本文设置为
启动后30s运行一次
,每30分钟
运行一次。
- 注册坚果云账户,登录后,在安全选项页面中,添加应用,获取对应密码。坚果云默认的 WebDAV 地址为
二、运行
2.1 日记功能
在 Obsidian 主页面右侧找到日记插件界面,点击每日日期或左侧周数,即可创建对应笔记。
2.2 todo 列表
在 Obsidian 主页面右侧找到 Tode list 插件界面,快速确定每个任务完成情况。
2.3 可视化
在 Obsidian 的关系图谱页面中,可以清晰的看到每笔日记的关联关系。
三、总结
目前 Obsidian 完美的满足了我对应全部文档数据的掌控欲,终究还是将数据存在本地比较安心。后续 Obsidian 还有许多有意思的功能值得分享,比如文本导出 PDF、Word,地理地图,整合 zotero 等。