Obsidian 插件篇 -DataviewJS 参考示例
在上文 Obsidian插件篇-DataviewJS基础知识 的基础上,本篇文章主要提供一些验证后的 DataviewJS 脚本供大家学习并仿写实践。
以下 DataviewJS 脚本,可以直接复制使用,标题即是脚本实现的功能。
汇总当前文件标题生成目录(文本)
dataviewjs
const startHeadingLevel = 0 // 起始标题级别
const file = app.workspace.getActiveFile()
const { headings } = app.metadataCache.getFileCache(file)
const lists = headings.map(p=>
`${' '.repeat((p.level - startHeadingLevel) * 4)}- ${p.heading}`
) // 前导空格 + 列表格式
dv.paragraph(lists.join('\n')) // 输出
汇总指定文件夹位置生成目录
dataviewjs
const dFiles = dv.pages(`"3.Cards/Sh成长系统"`)
const startHeadingLevel = 0
dFiles.map(async dFile=> {
const file = app.vault.getAbstractFileByPath(dFile.file.path)
const { headings } = app.metadataCache.getFileCache(file)
const lists = headings.filter(p=> p).map(p=>
`${' '.repeat((p.level - startHeadingLevel) * 4)}- ${p.heading}`
)
dv.header(6, dFile.file.link)
dv.paragraph(lists.join('\n'))
})
根据标题生成点击可跳转的目录
原文见:使用DV创建动态更新的笔记目录大纲(TOC) - 经验分享 - Obsidian 中文论坛。
dataviewjs
dv.header(2,"目录");
// 标题级别,按需修改
const startHeadinglevel = 2;
const file = app.workspace.getActiveFile();
const { headings } = app.metadataCache.getFileCache(file);
// 全列表的形式
const raws = headings.map( p => {
let repeatCount = Math.max((p.level - startHeadinglevel) * 4, 0);
let spacesPrefix = ' '.repeat( repeatCount + 4 );
let listSign = repeatCount > 0 ? '- ' : '';
let linkText = `[[#${p.heading}]]`;
let headingList = (p.level < startHeadinglevel) ? `- ${linkText}` : `${spacesPrefix}- ${linkText}`;
return headingList;
}
)
let result = raws.join('\n');
// 添加行距
dv.container.style.lineHeight = "1.5em";
dv.paragraph(result)
dataviewjs
// 一级列表单独显示的形式
// 标题级别,按需修改
const startHeadinglevel = 2;
const file = app.workspace.getActiveFile();
const { headings } = app.metadataCache.getFileCache(file);
let output = '';
const additionalAttr = {attr: {style: 'margin-block-start: 0 !important; margin-block-end: 0 !important;'}};
// 整个 dv 容器的样式调整
dv.container.style.marginBlockStart = "0em";
headings.forEach ( h => {
if (h.level < startHeadinglevel) {
// 先输出之前的子列表内容
if (output) dv.paragraph(output, additionalAttr);
// 将一级标题输出成单独的段落
output = `[[#${h.heading}]]\n`;
dv.paragraph(output);
output = '';
} else {
output += `${' '.repeat((h.level - startHeadinglevel) * 4)}- [[#${h.heading}]]\n`;
}
})
// 把最后的残余内容输出出来
if (output) dv.paragraph(output, additionalAttr);
按钮修改 yaml
dataviewjs
// 这个是按钮的点击函数
const {update,autoprop} = this.app.plugins.plugins["metaedit"].api;
const buttonMaker = (pn, pv, fpath) => {
// 选项,值,路径
const btn = this.container.createEl('button', {"text": pv});
btn.addEventListener('click', async (evt) => {
evt.preventDefault();
//"新内容"要在Auto Properties中事先配置
const newtext = await autoprop("新内容")
await update(pn, newtext, fpath);
});
return btn;
}
dv.table(["文件","路径", "tags", "status"],
dv.pages("#Linux")
.map(t=>[t.file.link, t.file.folder,buttonMaker('tags',t.tags,t.file.path), buttonMaker('status', t.status, t.file.path)])
)
所有标签列表
dataviewjs
// 生成所有的标签且形成列表
dv.list(dv.pages("").file.tags.distinct())
dataviewjs
// 生成所有的标签且以 | 分割,修改时只需要修改 join(" | ") 里面的内容。
dv.paragraph(
dv.pages("").file.tags.distinct().map(t => {return `[${t}](${t})`}).array().join(" | ")
)
dataviewjs
// 基于文件夹聚类所有的标签。
for (let group of dv.pages("").filter(p => p.file.folder != "").groupBy(p => p.file.folder.split("/")[0])) {
dv.paragraph(`### ${group.key}`);
dv.paragraph(
dv.pages(`"${group.key}"`).file.tags.distinct().map(t => {return `[${t}](${t})`}).array().sort().join(" | "));
}
仓库统计
dataviewjs
dv.header(2,"文章统计");
let ftMd = dv.pages("").file.sort(t => t.cday)[0]
let total = parseInt([new Date() - ftMd.ctime] / (60*60*24*1000))
let totalDays = "使用 *Obsidian* "+"**"+total+"**天,"
dv.paragraph(totalDays)
//.length统计数量
let i = [dv.pages('!"4.Extra/Template"').length,dv.pages(`"2.Calendar"`).length,dv.pages(`"4.Extra"`).length,
dv.pages().file.etags.distinct().length]
// console.log(dv.pages())
dv.paragraph(`共有 **${i[0]}** 篇笔记,Calendar笔记 **${i[1]}** 篇,附件 **${i[2]}** 个,标签 **${i[3]}** 个`)
dataviewjs
let ftMd = dv.pages("").file.sort(t => t.cday)[0]
let total = parseInt([new Date() - ftMd.ctime] / (60*60*24*1000))
let totalDays = "您已使用 ***Obsidian*** "+total+" 天,"
let nofold = '!"misc/templates"'
let allFile = dv.pages(nofold).file
let totalMd = "共创建 "+ allFile.length+" 篇笔记"
let totalTag = allFile.etags.distinct().length+" 个标签"
let totalTask = allFile.tasks.length+"个待办。 "
dv.paragraph(
totalDays + totalMd + "、" + totalTag + "、" + totalTask
)
所有带关键词的行
dataviewjs
//输出所有带有关键词的行
//使用时修改关键词即可
const term = "ACID"
const files = app.vault.getMarkdownFiles()
const arr = files.map(async ( file) => {
const content = await app.vault.cachedRead(file)
const lines = content.split("\n").filter(line => line.contains(term))
return lines
})
Promise.all(arr).then(values => dv.list(values.flat()))
倒计时
dataviewjs
// 修改其中的时间,可以输出当前离倒计时的时间差。
const setTime = new Date("2024/6/15 08:00:00");
const nowTime = new Date();
const restSec = setTime.getTime() - nowTime.getTime();
const day = parseInt(restSec / (60*60*24*1000));
const str = day + "天"
dv.paragraph(str);
Todo 文件一行输出
dataviewjs
//控制台调试
console.log(dv.pages("#Todo"))
//dv.paragraph生成段落
dv.paragraph(`${dv.pages("#Todo").file.link}`)
任务列表
dataviewjs
//dv.taskList生成任务列表
//console.log(dv.pages("#Task"))
dv.taskList(dv.pages("#Task").file.tasks)
待办列表
dataviewjs
dv.header(3,"待办列表");
//dv.list生成列表
dv.list(dv.pages("#Todo").map(p=>p.file.link));
最近一周文件列表
dataviewjs
//查询结果有误
dv.header(5,"最近一周");
dv.list(
dv.pages(``)
.filter(p=>moment(p.created).diff(moment(),'days')<=7)
.map(p=>p.file.link+' - '+p.created));
2024 年 8 月文件列表
dataviewjs
dv.header(3,"2024年7月文件列表");
//2024年文件
//dv.list()生成List
dv.list(
//获取符合条件的文件数组
dv.pages(``)
//筛选符合条件的子数组
.filter(p=>moment(Number(p.file.cday)).get("year")==2024
&& moment(Number(p.file.cday)).get("month")==7)
//降序排序
.sort(p=>p.file.cday,'desc')
//.map()类似for循环
.map(p=>moment(Number(p.file.cday)).format('yyyy-MM-DD')+' >> '+p.file.link)
)
某年某月文件列表
dataviewjs
dv.header(3,"2024年8月文件列表");
dv.list(
// 获取符合条件的文件数组
dv.pages("")
// 筛选符合条件的子数组(将 'file.cday' 转换为日期格式)
.filter(p => {
const date = moment(p.created);
// 筛选符合条件的子数组(将 'file.cday' 转换为日期格式)
// 月份比较不准,总是大一,问题未知(24年1月)
return date.year() ==2024 && date.month() ==0;
})
// 降序排序
.sort(p => p.created, 'desc')
// 使用 map 转换输出格式并处理日期(确保使用 `format` 方法正确解析)
.map(p => {
const formattedDate = moment(p.created).format('YYYY-MM-DD');
return `${formattedDate} >> ${p.file.link}`;
})
)
文件夹下文件列表
dataviewjs
//写法1
//dv.pagePaths()获取文件目录数组
for(let i of dv.pagePaths(`"1.Atlas"`).groupBy(p=>p.split("/")[1])){
let a = dv.pages(`"1.Atlas/${i.key}"`).length
dv.paragraph(`### ${i.key} --**${a}**篇`);
dv.list(
dv.pages(`"1.Atlas"`)
.filter(p=>p.file.folder.split("/")[1]==i.key)
//cday写法 .map(p=>moment(Number(p.file.cday)).format('yyyy-MM-DD')+' >> '+p.file.link)
.map(p=> p.file.link + '>>' +p.created +' - '+moment().diff(moment(p.created),'days')+'天')
);
}
dataviewjs
//写法2,优先使用
let pageGroups = dv.pagePaths(`"1.Atlas"`).groupBy(p => p.split("/")[1]);
for (let i of pageGroups) {
let pagesInGroup = dv.pages(`"1.Atlas/${i.key}"`);
// 使用 pagesInGroup 替换重复的获取操作
dv.paragraph(`### ${i.key} --**${pagesInGroup.length}**篇`);
dv.list(pagesInGroup.map(p => {
let daysSince = moment(p.created).diff(moment(), 'days');
return p.file.link + '>>' +p.created + ' - ' + `${daysSince}` + '天';
}));
}
MySQL 文件夹列表
dataviewjs
//MySQL文件夹总览
let pageGroups = dv.pagePaths(`"2.Calendar/7.todo/1.Study/MySQL"`).groupBy(p => p.split("/")[4]);
console.log(pageGroups);
for (let i of pageGroups) {
let pagesInGroup = dv.pages(`"2.Calendar/7.todo/1.Study/MySQL/${i.key}"`);
// 使用 pagesInGroup 替换重复的获取操作
dv.paragraph(`### ${i.key} --**${pagesInGroup.length}**篇`);
dv.list(pagesInGroup.map(p => {
let daysSince = moment(p.created).diff(moment(), 'days');
return p.file.link + '>>' +p.created + ' - ' + `${daysSince}` + '天';
}));
}
Atlas 文件夹 Table
dataviewjs
dv.header(1,"table任务");
// 首先获取所有页面的路径
let allPaths = dv.pagePaths(`"1.Atlas"`);
// 对文件夹进行分组并处理每个子集
for(let i of allPaths.groupBy(p=>p.split("/")[1])){
let a = dv.pages(`"1.Atlas/${i.key}"`).length;
dv.paragraph(`### ${i.key} --**${a}**篇`);
// 过滤当前文件夹下的页面,并计算从创建以来的天数和获取实际创建日期
let pageData = dv.pages(`"1.Atlas"`)
.filter(p => p.file.folder.split("/")[1] == i.key)
.map(p => ({
title: i.key,
pageName: p.file.link,
sinceCreated: moment().diff(moment(p.created), 'days'),
created: p.created
}));
// 创建并展示一个表格,用于当前文件夹下的页面数据
dv.table(['文件夹', '文件名', '创建天数', '创建日期'],
[[pageData.title,pageData.pageName,pageData.sinceCreated,pageData.created]]);
}
指定多个文件夹 Table
dataviewjs
{
dv.header(1,"指定多个文件夹Table");
// 首先获取所有页面的路径
let allPaths = [
{ key: "1.Atlas" },
{ key: "4.Extra" }
];
// 对文件夹进行分组并处理每个子集
for(let i of allPaths){
let a = dv.pages(`"${allPaths[0].key}"`).length;
dv.paragraph(`### ${i.key} --**${a}**篇`);
// 过滤当前文件夹下的页面,并计算从创建以来的天数和获取实际创建日期
let pageData = dv.pages(``)
.filter(p => p.file.folder.split("/")[0] == i.key)
.map(p => ({
pageName: p.file.link,
path: p.file.folder,
sinceCreated: moment().diff(moment(p.created), 'days'),
created: p.created
}));
// 创建并展示一个表格,用于当前文件夹下的页面数据
dv.table(['文件名','文件路径','创建天数', '创建日期'],
[[pageData.pageName,pageData.path,pageData.sinceCreated,pageData.created]]);
}}
仓库文件夹 Table
dataviewjs
dv.header(1,"仓库文件夹Table");
// 首先获取所有页面的路径
let allPaths = dv.pagePaths(``);
// 对文件夹进行分组并处理每个子集
for(let i of allPaths.groupBy(p=>p.split("/")[0])){
let a = dv.pages(`"${i.key}"`).length;
dv.paragraph(`### ${i.key} --**${a}**篇`);
// 过滤当前文件夹下的页面,并计算从创建以来的天数和获取实际创建日期
let pageData = dv.pages(``)
.filter(p => p.file.folder.split("/")[0] == i.key)
.map(p => ({
pageName: p.file.link,
path: p.file.folder,
sinceCreated: moment().diff(moment(p.created), 'days'),
created: p.created
}));
// 创建并展示一个表格,用于当前文件夹下的页面数据
dv.table(['文件名', '文件路径','创建天数', '创建日期'],
[[pageData.pageName,pageData.path,pageData.sinceCreated,pageData.created]]);
}