解决问题
为了实现笔记库内相关笔记的快速跳转和汇集,参考 The Brain 类似软件,按照上级、下级、平级的三种不同方向的关联关系,经多方摸索,初步实现了以 dataview 支持下的分类显示方式。
优势是关系添加操作简单,可以显示“本笔记A”中标记的上、下、平三类笔记,也可以同时:
- 把“其他笔记B“中标记“本笔记A”为下级的,在“本笔记A”中关联显示到“本笔记A”的上级分类中
- 把“其他笔记C”中标记“本笔记A”为上级的,关联显示到“本笔记A”的下级分类中
- 把“其他笔记D“中标记“本笔记A”为平级的,在“本笔记A”中关联显示到“本笔记A”的平级分类中
尝试过但不太理想或玩不转的插件方案包括:
- go-up,快速跳转到父级页面
- breadcrumbs,可视化笔记层次结构,与juggl紧密关联
- juggl,结构化、可视化笔记关系
- class-relation-visualization,结构化分类?不太会用
实现方法
基础
已安装并打开 dataview(js) (以下简称dv)
标记关系
在笔记中写dv行内字段【这样好操作,写到属性区也可以】
up: [[公司战略]]
down: [[模块B]],[[模块C]]
links: [[项目X]],[[项目Y]]
与本笔记的关系:up(上级)、down(下级)、links(平级),三个关系词可以自定义,中文应该也可以。
后面的笔记名,可以从笔记左侧的文件列表目录中,直接拖拽过来,或者从其他笔记处复制过来,两个笔记名链接间,用逗号区手动隔开,操作比较简单。
备注:以上拖拽添加的方式,外面不再加[ ] " " 等外面的符号,上下三个点的代码包裹也不需要,这样笔记名修改时,可以同步修改。
汇集三类关系笔记链接
建立一个模板如下:
三向标记
up::
down::
links::
三向笔记关联
上级笔记
const cur = dv.current();
const curLk = cur.file.link;
const curNm = cur.file.name;
/* ① 当前笔记自身的 up 字段(兼容单条/数组/空值)*/
const selfup = cur.up
? (Array.isArray(cur.up) ? cur.up : [cur.up])
: [];
/* ② 下级笔记:down 字段里出现当前笔记名 */
const childLinks = dv.pages()
.filter(p => p.down && dv.func.contains(String(p.down), curNm))
.map(p => p.file.name)
.array();
/* 合并、去重、排序;剔除空值再剥括号 */
const allNames = [...new Set([
...selfup
.filter(Boolean) // 去掉 null/undefined
.map(n => String(n).replace(/^\[\[|\]\]$/g, '')), // 剥括号
...childLinks
])].sort();
/* 渲染:当前笔记 → ◆ [[名字1]], [[名字2]] */
if (allNames.length) {
dv.span(`${curLk} → ◆ ${allNames.map(n => `[[${n}]]`).join(', ')}`);
}
/* 无结果时保持空白 */
下级笔记
const cur = dv.current();
const curLk = cur.file.link;
const curNm = cur.file.name;
/* ① 当前笔记自身的 down 字段(兼容单条/数组/空值)*/
const selfDown = cur.down
? (Array.isArray(cur.down) ? cur.down : [cur.down])
: [];
/* ② 下级笔记:up 字段里出现当前笔记名 */
const childLinks = dv.pages()
.filter(p => p.up && dv.func.contains(String(p.up), curNm))
.map(p => p.file.name)
.array();
/* 合并、去重、排序;剔除空值再剥括号 */
const allNames = [...new Set([
...selfDown
.filter(Boolean) // 去掉 null/undefined
.map(n => String(n).replace(/^\[\[|\]\]$/g, '')), // 剥括号
...childLinks
])].sort();
/* 渲染:当前笔记 → ◆ [[名字1]], [[名字2]] */
if (allNames.length) {
dv.span(`${curLk} → ◆ ${allNames.map(n => `[[${n}]]`).join(', ')}`);
}
/* 无结果时保持空白 */
平级笔记
const cur = dv.current();
const curLk = cur.file.link;
/* ① 当前笔记自身的 links 字段(兼容单条/数组/空值)*/
const selfLinks = cur.links
? (Array.isArray(cur.links) ? cur.links : [cur.links])
: [];
/* ② 反向链:别人 links 里出现当前笔记名 */
const backLinks = dv.pages()
.filter(p => p.links && dv.func.contains(String(p.links), cur.file.name))
.map(p => p.file.name) // 只取文件名,本身无括号
.array();
/* 合并、去重、排序;剔除空值并剥掉多余括号 */
const allNames = [...new Set([
...selfLinks
.filter(Boolean)
.map(n => String(n).replace(/^\[\[|\]\]$/g, '')), // 剥括号
...backLinks
])].sort();
/* 渲染:当前笔记 → ★ [[名字1]], [[名字2]] */
if (allNames.length) {
dv.span(`${curLk} → ★ ${allNames.map(n => `[[${n}]]`).join(', ')}`);
}
/* 无结果时保持空白 */
以上模板是dataviewjs代码,由两部分合并组成,一个部分显示本笔记中的标记关系,另一部分汇集其他笔记中标记的与本笔记的关系(反推汇集),最后合并在本笔记对应上、下、平关系中显示。
实际效果如下:
最后说明
这种标注方式,并不排斥多个主题线的上级、下级、平级关系,并且标记什么显示什么,可以互为上下级,也可以蛇头吃蛇尾、蛇腹接蛇头。因此,是允许多线头、多主题逻辑关系(其实也就是混乱),相对比较包容,可以从混乱慢慢走向有序。
