效果:
命令面板
生成后的归档文件
生成后的分类文件
操作方法
新建一个runjs脚本文档,比如在runjs目录新建“归档和分类.md”,输入下面两段代码
点击这里查看runjs代码👇
```js RunJS="生成归档"
// 归档文件名
const archiveFileName = '归档.md';
//排除的文件或文件夹
const excludes = [
//"demo.md",
//"demo/demo.md",
];
// 仅在下列文件夹中的文件才显示
const onlyFolers = [
//"开发笔记",
//"javascript",
];
// 格式 {yearmonth:[link, link]}
const files = {};
const dv = DataviewAPI;
dv.pages().sort(p => p.file.ctime, 'desc')
.map(p => {
// 排除的文件或文件夹则跳过
if (excludes.includes(p.file.path) ||
excludes.includes(p.file.folder) ||
(onlyFolers && onlyFolers.length && !onlyFolers.some(f=>new RegExp(`^${f}`).test(p.file.folder)))
) return;
// 初始化数组
const key = p.file.ctime.year+''+p.file.ctime.month;
if(!files[key]) files[key] = [];
// 把相同年月的放到一个数组中
files[key].push(p.file.link);
});
// 把年月倒序排序
const yearmonths = Object.keys(files)
.sort((a, b) => b - a);
// 输出年月及文件列表
let content = '';
yearmonths.forEach((year_month)=>{
const links = files[year_month]
content += "### " + year_month.substr(0, 4) + '年' + year_month.substr(4, 2) + '月' + "\n\n"
content += dv.markdownList(links) + "\n";
});
app.vault.adapter.write(archiveFileName, content);
```
```js RunJS="生成分类"
// 分类文件名
const cateFileName = '分类.md';
//排除的文件或文件夹
const excludes = [
//"demo.md",
//"demo/demo.md",
];
// 仅在下列文件夹中的文件才显示
const onlyFolers = [
//"开发笔记",
//"javascript",
];
//排序,仅支持文件夹排序
const sortBy = [
//"开发笔记",
//"javascript",
];
// 格式 {"folder":[link, link]}
const files = {};
const dv = DataviewAPI;
dv.pages()
.sort(p => p.file.ctime, 'desc')
.map(p => {
// 脚本自身及排除的文件或文件夹则跳过
if (excludes.includes(p.file.path) ||
excludes.includes(p.file.folder) ||
(onlyFolers && onlyFolers.length && !onlyFolers.some(f=>new RegExp(`^${f}`).test(p.file.folder)))
) return;
// 初始化数组
if(!files[p.file.folder]) files[p.file.folder] = [];
// 把相同的分类放到一个数组中
files[p.file.folder].push(p.file.link);
});
// 把文件夹按名称正序排序
let folders = Object.keys(files)
.sort((a, b) => a.localeCompare(b, undefined, { numeric: true }));
// 把未分类的文件放到最后
if(folders[0] === "") {
const shift = folders.shift()
folders.push(shift);
}
// 第一次把folders按sortby排序
const sortByIndex = sortBy.reduce((acc, curr, index) => {
acc[curr] = index;
return acc;
}, {});
folders.sort((a, b) => {
// 获取a和b在sortBy中的索引,如果不存在则默认为sortBy.length(即放在末尾)
const indexA = sortByIndex[a] !== undefined ? sortByIndex[a] : sortBy.length;
const indexB = sortByIndex[b] !== undefined ? sortByIndex[b] : sortBy.length;
// 按照索引值进行排序,索引越小越靠前
return indexA - indexB;
});
//第二次把folders按sortby前缀排序
let newFolders = [];
sortBy.forEach((item) => {
newFolders = [...newFolders, ...folders.filter(f=>f.startsWith(item))];
});
// 第三次,把folders中剩余的合并到newFolers
newFolders = [...newFolders, ...folders.filter(f=>!newFolders.includes(f))]
// 去重
folders = newFolders.filter((item, index, array) => array.indexOf(item) === index);
// 嵌套输出文件夹及文件列表
const tree = {};
let content = '';
folders.forEach(folder => {
let subs = folder.split('/');
let currentNode = tree;
let path = '';
subs.forEach((sub, index) => {
//path += (index ? '/' : '') + sub;
// 如果不在目录结构的列表才输出,防止重复输出父分类和祖先分类
if(!currentNode[sub]){
//输出标题
content += `${"\t".repeat(index)}- ${"#".repeat(index+2)} ${sub||"未分类"}\n\n`
// 输出链接
//content += `${"\t".repeat(index+1)}- list\n`
let links = files[folder]
links = dv.markdownList(links).replace(/- \[\[/g, `${"\t".repeat(index+1)}- [[`);
content += links + "\n"
}
// 实时计算目录结构
if (!currentNode[sub]) {
currentNode[sub] = {};
}
currentNode = currentNode[sub];
});
});
/* 不嵌套输出文件夹及文件列表
let content = '';
folders.forEach((folder)=>{
const links = files[folder]
content += "### " + (folder.replace(/\//g, " / ")||"未分类") + "\n\n"
content += dv.markdownList(links) + "\n";
});*/
app.vault.adapter.write(cateFileName, content);
然后,在runjs插件设置中,把生成归档和生成分类添加到命令中,如下图
这时候就已经完成了。
可以通过“RunJS: 生成归档”和“RunJS: 生成分类”命令生成相关文档了,如图
也可以通过左侧runjs面板里双击对应的名称生成,如图
但,也可以通过自定义一个常用命令的面板来执行,如果你需要请参考下面的步骤。
新建常用命令面板
新建一个常用命令面板,比如在panels目录新建“常用命令.md”,然后输入以下代码
点击这里查看常用命令代码👇
```dataviewjs
// 本脚本的文件的名字
const currFileName = dv.current()?.file?.name || "常用命令";
// 打开时设置为预览模式
const activeLeaf = this.app.workspace.activeLeaf;
if(activeLeaf?.view?.file?.basename === currFileName){
const state = activeLeaf?.view.getState();
state.mode = "preview"
activeLeaf?.setViewState({type: "markdown", state: state});
}
// 创建按钮
const createButton = function(name, callback, useStyle = '') {
const style = `float:left;margin-right:10px;margin-top:10px;${useStyle}`;
const button = dv.el('button', name || 'Button', {attr:{style:style}});
if(typeof callback === 'function') button.onclick = callback;
}
createButton('生成归档', function() {
//查看commandId
//app.commands.listCommands().filter(f=>f.id.contains('runjs'))
app.commands.executeCommandById('runjs:command-0');
this.firstChild.textContent = '已生成';
setTimeout(() => {
this.firstChild.textContent = '生成归档';
}, 1500);
});
createButton('生成分类', function() {
//查看commandId
//app.commands.listCommands().filter(f=>f.id.contains('runjs'))
app.commands.executeCommandById('runjs:command-1');
this.firstChild.textContent = '已生成';
setTimeout(() => {
this.firstChild.textContent = '生成分类';
}, 1500);
});
```
注意:这里的app.commands.executeCommandById('runjs:command-0');
和 app.commands.executeCommandById('runjs:command-1');
中的runjs:command-0和runjs:command-1就是刚才生成归档和生成分类的命令id,如果你不清楚这个id是什么(通常runjs命令列表,第一个就是runjs:command-0,第二个runjs:command-1,以此类推),可以在devtools控制面板中输入app.commands.listCommands().filter(f=>f.id.contains('runjs'))
查看,如图
最终效果图如下
修改侧边栏图标
如果你想修改常用命令面板的图标,可参考:dataview实现获取最近修改文件列表,支持搜索
最终效果如下
定时生成或外部执行
如果你想定时生成归档或分类文档或外部执行,可以在脚本中使用以下命令
// mac
open --background "obsidian://advanced-uri?vault=vault_name&commandid=runjs%253Acommand-0"
//linux
xdg-open "obsidian://advanced-uri?vault=vault_name&commandid=runjs%253Acommand-0"
//windows
start /b "obsidian://advanced-uri?vault=vault_name&commandid=runjs%253Acommand-0"
注意:这里的vault_name和unjs%253Acommand-0
根据你自己的情况修改。