Obsidian 插件篇 -DataviewJS 参考示例
在上文 Obsidian插件篇-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(`"0.Atlas"`)
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
// 获取所有Markdown文件
const mdFiles = dv.pages('"4.Apply"').file
// 过滤未设置标签的文件
const untaggedFiles = mdFiles.filter(f => {
const page = dv.page(f.path)
// 检查是否存在tags字段,并验证是否有效
return !page?.tags ||
(Array.isArray(page.tags) && page.tags.length === 0) ||
(typeof page.tags === 'string' && page.tags.trim() === "")
})
// 显示结果
if (untaggedFiles.length > 0) {
dv.header(4, "未设置标签的文件 (" + untaggedFiles.length + "个)")
dv.list(untaggedFiles.map(f => dv.fileLink(f.path)))
} else {
dv.paragraph("🎉 所有Markdown文件都已设置标签!")
}
所有标签统计
dataviewjs
dv.header(2,"标签统计");
//let path="4.Apply"; // 设置根目录路径,请替换
let pages = dv.pages(``);// 设置根目录路径,请替换
//let pages = dv.pages(`"4.Apply"`);// 设置根目录路径,请替换
let publishPages=pages.filter(a => a.publish == true);//已发布,可替换 "publish" 为您实际yaml属性
let toPublishPages=pages.filter(a => a.publish == false);//未发布,可替换 "publish" 为您实际yaml属性
//数量统计部分
let i = [pages.length,
toPublishPages.length,
publishPages.length,
pages.file.etags.distinct().length];
dv.paragraph(`共有 **${i[0]}** 篇相关笔记,未发布**${i[1]}** 篇,已发布**${i[2]}** 篇,标签 **${i[3]}** 个`);
// ================== 标签云样式配置 ==================
dv.el('style', `
.dynamic-query-container {
position: relative;
min-height: 200px;
padding: 20px;
background: var(--background-primary);
border-radius: 8px;
box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}
`);
// ================== 标签云功能实现 ==================
const container = dv.el('div', '', { cls: 'dynamic-query-container' });
// 自动执行标签云查询
const dynamicQuery = async () => {
const resultDiv = dv.el('div', '', { cls: 'result' });
// 固定查询路径
await dv.view("5.Archives/1.resources/Js/tagcloud", {
values: pages.filter(p => p.file.tags)
}, resultDiv);
container.appendChild(resultDiv);
}
// 直接执行标签云查询
setTimeout(dynamicQuery, 100);
标签云-可指定文件夹
dataviewjs
// ================== 样式配置 ==================
const style = dv.el('style', `
.dynamic-query-container {
position: relative;
min-height: 200px;
}
.control-panel {
position: absolute;
top: 0;
right: 0;
display: flex;
gap: 12px; /* 增加间距 */
align-items: center;
background: var(--background-primary);
padding: 12px; /* 增加内边距 */
border-radius: 10px;
box-shadow: 0 4px 12px rgba(0,0,0,0.15);
z-index: 100;
}
.folder-select {
padding: 10px 16px; /* 加大内边距 */
border: 2px solid var(--interactive-accent); /* 加粗边框 */
border-radius: 8px;
background: var(--background-primary);
color: var(--text-normal);
font-size: 16px; /* 增大字号 */
width: 180px; /* 加宽容器 */
height: 44px; /* 固定高度 */
}
.query-btn {
padding: 12px 24px; /* 加大按钮尺寸 */
border-radius: 8px;
background: var(--interactive-accent);
color: var(--text-on-accent);
border: none;
cursor: pointer;
transition: all 0.2s ease;
font-size: 16px; /* 按钮字号 */
font-weight: 600; /* 加粗文字 */
letter-spacing: 0.5px;/* 字间距 */
}
.query-btn:hover {
transform: scale(1.05);
box-shadow: 0 4px 16px rgba(var(--interactive-accent-rgb), 0.3);
}
`);
// ================== 功能实现 ==================
// 创建容器
const container = dv.el('div', '', { cls: 'dynamic-query-container' });
// 创建控制面板
const controlPanel = dv.el('div', '', { cls: 'control-panel' });
// 路径选择器
const folderSelector = dv.el('select', '', { cls: 'folder-select' });
const FOLDER_OPTIONS = {
'"0.Atlas"': '📌 Atlas',
'"1.Acquire"': '🔍 Acquire',
'"2.Action"': '🚀 Action',
'"3.Atom"': '🧩 Atom',
'"4.Apply"': '⚡ Apply',
'"5.Archives"': '🗃️ Archives',
'"/"': 'Vault根目录'
};
Object.entries(FOLDER_OPTIONS).forEach(([path, name]) => {
const option = dv.el('option', name);
option.value = path;
if (path === '"4.Apply"') option.selected = true;
folderSelector.appendChild(option);
});
// 查询按钮
const queryBtn = dv.el('button', '🔎 执行查询', { cls: 'query-btn' });
// 动态查询函数
const dynamicQuery = async (folderPath) => {
container.querySelector('.result')?.remove();
const resultDiv = dv.el('div', '', { cls: 'result' });
await dv.view("5.Archives/1.resources/Js/tagcloud", {
values: dv.pages(folderPath).filter(p => p.file.tags)
}, resultDiv);
}
// 事件绑定
queryBtn.addEventListener('click', () => {
// 清除旧结果
while (dv.container.children.length > 2) {
dv.container.lastChild.remove();
}
// 执行新查询
dynamicQuery(folderSelector.value);
});
// 初始加载后自动查询
setTimeout(() => dynamicQuery('"4.Apply"'), 100);
// 组装界面
controlPanel.appendChild(folderSelector);
controlPanel.appendChild(queryBtn);
container.appendChild(controlPanel);
仓库统计
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);
}
文章统计-列表
dataviewjs
dv.header(2,"文章统计");
let path="AA";
let paths = dv.pagePaths(`"${path}"`);
let pages = dv.pages(`"${path}"`);
let s=["Todo","Organize","Rewrite","Explor","Apply","Done"];
let todoPages=pages.filter(a => a.status == s[0]);
let orangizePages=pages.filter(a => a.status == s[1]);
let rewritePages=pages.filter(a => a.status == s[2]);
let explorPages=pages.filter(a => a.status == s[3]);
let applyPages=pages.filter(a => a.status == s[4]);
let donePages=pages.filter(a => a.status == s[5]);
let cols=['文件名','id','标签','状态','创建天数'];
//.length统计数量
//数量统计
let i = [pages.length,
todoPages.length,
orangizePages.length,
rewritePages.length,
explorPages.length,
applyPages.length,
donePages.length,
pages.file.etags.distinct().length]
dv.paragraph(`共有 **${i[0]}** 篇笔记,${s[0]}**${i[1]}** 篇,${s[1]}**${i[2]}** 篇,${s[2]}**${i[3]}** 篇,${s[3]}**${i[4]}** 篇,${s[4]}**${i[5]}** 篇,${s[5]}**${i[6]}** 篇,标签 **${i[7]}** 个`)
// 所有的标签
dv.paragraph(pages.file.tags.distinct()
.map(t => {return `[${t}](${t})`})
.array().sort().join(" | "));
dv.table(cols,tableByDay(pages));
function tableByDay(pages) {
return pages.sort((a, b) => moment(b.created).unix() - moment(a.created).unix())
.map(p => ([
p.file.link,
p.id,
p.tags,
//p.file.folder,
p.status,
moment().diff(moment(p.created), 'days')
]))
}
文章统计-分组
dataviewjs
dv.header(2,"文章统计");
let path="2.Collection/5.Java/6.DB/MySQL";
//let path="3.Cards/MySQL";
let paths = dv.pagePaths(`"${path}"`);
let pages = dv.pages(`"${path}"`);
let filePaths=paths.groupBy(p=>p.split("/")[4]);
let todoPages=pages.filter(a => a.status == "Todo");
let donePages=pages.filter(a => a.status == "Done");
let cols=['文件名','id','标签', '状态','创建天数', '创建日期'];
//数量统计
let i = [pages.length,
pages.filter(a => a.status == "Todo").length,
pages.filter(a => a.status == "Done").length,
pages.file.etags.distinct().length]
dv.paragraph(`共有 **${i[0]}** 篇相关笔记,Todo**${i[1]}** 篇,Done**${i[2]}** 篇,标签 **${i[3]}** 个`)
// 所有的标签
dv.paragraph(pages.file.tags.distinct()
.map(t => {return `[${t}](${t})`})
.array().sort().join(" | "));
//TodoTable
dv.header(2,"TodoTable -- **"+todoPages.length+"**篇");
table(cols,todoPages);
//DoneTable
dv.header(2,"Table -- **"+pages.length+"**篇");
// 对文件夹进行分组并处理每个子集
for(let i of filePaths){
//.filter(p => p.status !== "Todo")
let a = dv.pages(`"${path}/${i.key}"`);
dv.paragraph(`### ${i.key} --**${a.length}**篇`);
table(cols,a);
}
function table(cols,pages) {
dv.table(cols,pages.sort((a, b) => moment(b.created).unix() - moment(a.created).unix())
.map(p => ([
p.file.link,
p.id,
p.tags,
p.status,
moment().diff(moment(p.created), 'days'),
p.created
])));
}
使用dataview分组查询所有已发布的笔记
dataviewjs
dv.header(2,"文章统计情况");
let path="4.Apply"; // 设置根目录路径,请替换
let paths = dv.pagePaths(`"4.Apply"`);// 设置根目录路径,请替换
let pages = dv.pages(`"4.Apply"`);// 设置根目录路径,请替换
let filePaths=paths.groupBy(p=>p.split("/")[1]); // 按第一级子文件夹名称分组路径,可替换 "1" 为您实际层次目录
let publishPages=pages.filter(a => a.publish == true);//已发布,可替换 "publish" 为您实际yaml属性
let toPublishPages=pages.filter(a => a.publish == false);//未发布,可替换 "publish" 为您实际yaml属性
let loopCount = 1;// 初始化循环计数器
let cols=['文件名','是否发布','标签', '状态','创建天数', '创建日期','发布日期'];//预设的输出table表头,可替换
//自定义排序部分
let letters = "*abcdefghjklmnopqrstwxyz".toLocaleUpperCase().split('');
let zh = "阿八嚓哒妸发旮哈讥咔垃痳拏噢妑七呥扨它穵夕丫帀".split('');
var isNumber = function(temp){
let re = /[0-9]/;
return re.test(temp);
};
var isChar = function(temp){
let re = /[a-zA-Z]/;
return re.test(temp);
};
var isChinese = function(temp){
let re = /[^\u4E00-\u9FA5]/;
return re.test(temp) ? false : true;
};
// mySort 函数,将文件按照文件名排序,优先数字,其次字母,最后汉字
function mySort(dvPages, letters, zh) {
let sortedPages = [];
letters.forEach((e,i) => {
let key = (e == "*") ? "0~9" : e;
let currData = new Array();
dvPages.forEach(b => {
let fileName = b.file.name;
if (fileName) {
let initial = fileName.charAt(0);
if(i == 0 && isNumber(initial)){
currData.push(b);
} else if (i != 0 && isChar(initial) && e == initial.toLocaleUpperCase()){
currData.push(b);
} else if (i != 0 && isChinese(initial)){
if(initial.localeCompare(zh[i]) == -1 && (!zh[i-1] || zh[i-1].localeCompare(initial) <= 0)){
currData.push(b);
}
}
}
});
if(currData.length > 0){
currData.sort((a, b) => { // 修改后的排序函数
let fileNameA = a.file.name;
let fileNameB = b.file.name;
// 提取文件名中的数字部分进行数值比较
let numA = parseInt(fileNameA.match(/^(\d+)/)?.[1]); // 正则提取文件名开头的数字
let numB = parseInt(fileNameB.match(/^(\d+)/)?.[1]); // 正则提取文件名开头的数字
// 如果都能提取到数字,则进行数值比较
if (!isNaN(numA) && !isNaN(numB)) {
return numA - numB; // 数值升序排序
} else {
// 如果无法提取数字,则 Fallback 到字符串排序 (字典序)
if (fileNameA < fileNameB) return -1;
if (fileNameA > fileNameB) return 1;
return 0;
}
});
sortedPages = sortedPages.concat(currData);
}
});
return sortedPages;
}
//数量统计部分
let i = [pages.length,
toPublishPages.length,
publishPages.length,
pages.file.etags.distinct().length];
dv.paragraph(`共有 **${i[0]}** 篇相关笔记,未发布**${i[1]}** 篇,已发布**${i[2]}** 篇,标签 **${i[3]}** 个`);
// 所有的标签
dv.paragraph(pages.file.tags.distinct()
.map(t => {return `[${t}](${t})`})
.array().sort().join(" | "));
// 按照层级循环遍历根目录下的子文件夹
for (const i of filePaths) {
// 输出三级标题,有序显示当前子文件夹名称
dv.header(3, `${loopCount} : ${i.key}`);
// 动态查询每个子文件夹下的已发布页面
let currentDvPages = dv.pages(`"${path}/${i.key}"`)
//.filter(a => a.publish == true);
// 调用 mySort 函数对当前子文件夹的页面进行排序
const sortedPages = mySort(currentDvPages, letters, zh);
// 输出当前子文件夹的排序结果
if (sortedPages.length > 0) {
table(cols,sortedPages);
} else {
dv.paragraph(` ${i.key} 下没有符合条件的文件`);
}
dv.paragraph("---"); // 添加分隔线,区分不同子文件夹的输出
// 循环计数器递增
loopCount++;
}
//输出表格,可替换成您实际yaml属性
function table(cols,pages) {
dv.table(cols,pages
.map(p => ([
p.file.link,
p.publish,
p.tags,
p.status,
moment().diff(moment(p.created), 'days'),
p.created,
p.date
]))
);
}
状态统计
dataviewjs
let allPaths = ["0.Atlas","1.Projects","2.Areas","3.Resources","4.Archives","5.a",]
let s=["Todo","Organize","Rewrite","Explor","Apply","Done"];
//let days=[0,1,3,7,30];
//let ftMd = dv.pages(`""`).file.sort(t => t.cday)[0];
//let noteCreateTime = new Date(ftMd.ctime);
//let total = parseInt([new Date() - noteCreateTime] / (60*60*24*1000))
//let paths = dv.pagePaths(`-"${allPaths[0]}" and -"${allPaths[3]}" and -"${allPaths[4]}"`);
//let pages = dv.pages(`-"${allPaths[0]}" and -"${allPaths[3]}" and -"${allPaths[4]}"`);
let paths = dv.pagePaths(`-"${allPaths[5]}"`);
let pages = dv.pages(`-"${allPaths[5]}"`);
dv.header(2,"文章统计");
let formattedToday = moment().format('YYYY年MM月DD日');
let firstDay = pages.sort((a, b) => moment(a.created).unix() - moment(b.created).unix())[0];
let total=moment().diff(moment(firstDay.created), 'days');
let totalDays = "今天是"+formattedToday+",最早的笔记:"+firstDay.file.link+",始于"+firstDay.created+",记录 "+"**"+total+"** 天!"
let todoPages=pages.filter(a => a.status == s[0]);
// 以下内容可按需开启查询
let orangizePages=pages.filter(a => a.status == s[1]);
let rewritePages=pages.filter(a => a.status == s[2]);
let explorPages=pages.filter(a => a.status == s[3]);
let applyPages=pages.filter(a => a.status == s[4]);
let donePages=pages.filter(a => a.status == s[5]);
let publishedPages=pages.filter(a => a.publish === true);
let noPages=pages.filter(a => a.publish !== true);
let cols=['文件名','标签','文件路径', '状态','创建天数', '创建日期'];
// 统计天数
dv.paragraph(totalDays);
//.length统计数量
//数量统计
let i = [pages.length,
todoPages.length,
orangizePages.length,
rewritePages.length,
explorPages.length,
applyPages.length,
donePages.length,
pages.file.etags.distinct().length]
dv.paragraph(`共有 **${i[0]}** 篇笔记,${s[0]}**${i[1]}** 篇,${s[1]}**${i[2]}** 篇,${s[2]}**${i[3]}** 篇,${s[3]}**${i[4]}** 篇,${s[4]}**${i[5]}** 篇,${s[5]}**${i[6]}** 篇,标签 **${i[7]}** 个`)
// 所有的标签
dv.paragraph(pages.file.tags.distinct()
.map(t => {return `[${t}](${t})`})
.array().sort().join(" | "));
//dv.header(3,s[0]);
//dv.table(cols,tableByDay(todoPages));
//dv.header(3,s[1]);
//dv.table(cols,tableByDay(orangizePages));
//dv.header(3,s[2]);
//dv.table(cols,tableByDay(rewritePages));
//dv.header(3,s[3]);
//dv.table(cols,tableByDay(explorPages));
//dv.header(3,s[4]);
//dv.table(cols,tableByDay(applyPages));
//dv.header(3,s[5]);
//dv.table(cols,tableByDay(donePages));
dv.header(3,'publish');
dv.table(cols,tableByDay(publishedPages));
function tableByDay(pages) {
return pages.sort((a, b) => moment(b.created).unix() - moment(a.created).unix())
.map(p => ([
p.file.link,
p.tags,
p.file.folder,
p.status,
moment().diff(moment(p.created), 'days'),
p.created
]))
}