Dataviewjs的奇技淫巧

我在确认一下吧,其实我用getAllLoadedFiles 的目的是希望把文件和目录分离出来,分别统计多少文件发生变化,哪些目录(因为其中文件变化)而发生变化。

真是奇怪的需求啊 :face_with_spiral_eyes:,我还是有点晕,文件和文件夹是分两个表,分别按时间排序吗

是的,我的需求就是分成两个表来统计啊,不是在同一个表里的。

现在的统计文件的代码和效果图如下:

const afiles = await app.vault.getFiles();
const files = dv.array(afiles).filter(p=> p.extension != 'md').sort(p=>moment(p.stat.mtime),'desc');
dv.table(["文件名("+files.length+"个)","文件夹","文件链接","修改时间","距离今天"], files.map(p=>[p.name,p.parent.path,dv.fileLink(p.path,false,p.basename),moment(p.stat.mtime).format("LL"),new moment().diff(moment(p.stat.mtime),'days')]))

大神指导一下,文件夹监控怎么写?

我不知道哪里出了问题热力图不显示标记

注释dataviewjs loop下面一行的p.file.frontmatter.date换成p.date试试

这样看看行不行吧

let afiles = dv.array(app.vault.getFiles()).filter(p=> p.extension != 'md').sort(p=>moment(p.stat.mtime),'desc');
dv.table(["文件名("+afiles.length+"个)","文件夹","文件链接","修改时间","距离今天"], afiles.map(p=>[p.name,p.parent.path,dv.fileLink(p.path,false,p.basename),moment(p.stat.mtime).format("LL"),moment().diff(moment(p.stat.mtime),'days')]))
// dv.table(["文件名("+afiles.length+"个)","文件夹","修改时间","距离今天"], afiles.map(p=>[dv.fileLink(p.path,false,p.name),p.parent.path,moment(p.stat.mtime).format("LL"),moment().diff(moment(p.stat.mtime),'days')]))

let folders = [];
let time = [];
afiles.forEach(p=>{
	if(!folders.includes(p.parent.path)){
		folders.push(p.parent.path)
		time.push(p.stat.mtime)
	}
})
dv.table(["文件夹("+folders.length+"个)","修改时间","距离今天"],folders.map(p=>app.vault.getAbstractFileByPath(p)).map((p,i)=>[p.name,moment(time[i]).format("LL"),moment().diff(moment(time[i]),'days')]))

文件夹的时间用的是该文件夹里时间最近的文件,因为昨天看错了,文件夹确实没有.stat.mtime 属性,昨天金工实习眼睛都干花了 :joy:

1 个赞

感谢大佬回复,但这个方法不行。我又尝试了你以前的回复,问题解决了 :blush:

dv.span("😍")
const calendarData = { 
year: 2023, 
colors: { 
blue: ["#8cb9ff","#69a3ff","#428bff","#1872ff","#0058e2"], // first entry is considered default if supplied 
green: ["#c6e48b","#7bc96f","#49af5d","#2e8840","#196127"], },    showCurrentDayBorder: true, // (optional) defaults to true
    defaultEntryIntensity: 1,   // (optional) defaults to 4
    intensityScaleStart: 1,    // (optional) defaults to lowest value passed to entries.intensity
    intensityScaleEnd: 66,     // (optional) defaults to highest value passed to entries.intensity
    entries: [] } 
for(let page of dv.pages('').groupBy(p=>moment(Number(p.file.cday)).format('YYYY-MM-DD'))){ 
calendarData.entries.push({ 
date: page.key, 
color: "green", 
intensity: page.rows.length, 
content: page.rows.length }) } 
renderHeatmapCalendar(this.container, calendarData) 

嗯,确实是疏漏了,不能直接拿这玩意分组。
如果你想用yaml的自定义的时间属性可以用 moment(p.date.ts).format(‘YYYY-MM-DD’) 或 moment(Number(p.date)).format(‘YYYY-MM-DD’)

感谢感谢,这样显示的是对的。
如果要筛选指定二级标题下的tasks且不用正则,我一开始想的是用tasks.section.subpath去和.heading获取的所有level 2+标题比对,然后看比对到的标题是否是目标标题或它的次级标题。但是因为subpath只能拿到这个task直属section的标题,看不到更高level的父级标题,所以如果不同父标题下出现了同名的次级标题,筛选就会有误。大佬有更好的建议么

办法倒是有,但是好像会很麻烦

image

也许可以从它们的位置下手,判断一个task是不是在目标标题下面,如果你不希望有三级标题下的task,可能还要复杂一点

谢谢大佬的提议,我不太在意是不是在子标题下。按这个方向写了一版,目前看起来可以正常工作,就是啰嗦了点

let files = dv.pages('"02-Daily Notes" or "03-Meetings"')
.filter(p=>p.file.tasks.where(t => !t.completed).length!=0)
.sort(p => p.file.name)

function isWithinRanges(num, arr) {
  for (let i = 0; i < arr.length; i += 2) {
    if (num >= arr[i] && num <= arr[i + 1]) {
      return true;
    }
  }
  return false;
}

let targetHeader = `[[${dv.current().file.name}]]`
let tks = files.map(p => {
		let tf = app.vault.getAbstractFileByPath(p.file.path)
		let header = app.metadataCache.getFileCache(tf)
			.headings
		if (!header) return
		let hRange = []  // 目标标题覆盖范围的始末行数
		let t = []  // 目标标题下的task
		let b = false
		for (let i of header) {
			if (b && i.level == 2) {
				hRange.push(i.position.start.line)
				b = false
			}
			if (i.heading == targetHeader && i.level == 2) {
				hRange.push(i.position.start.line)
				b = true
			}
		}
		if (hRange.length > 0) {
			if (hRange.length % 2 == 1) hRange.push(100000000)
			let tasks = p.file.tasks
			for (let j of tasks) {
				if (isWithinRanges(j.line, hRange)) t.push(j)
			}
		}
		return t.length == 0 ? false : [p.file.link, t]
	})
	.filter(p => p)
	
dv.table(['Date','Tasks'],files.map(p=>{
	let div = dv.container.createEl('div');
	if (tks.length > 0) {
		for (let t of tks) {
			if (t[0] == p.file.link) {
			    dv.api.taskList(t[1], 0, div, this.component, this.currentFilePath);
			    return [p.file.link,div];
			}
		}
	}
}))

能实现就很好了:ok_hand:t2:,越是定制化的功能,代码就越麻烦。

请问图片如果有进行大小调整需要怎么修改吗?
例如
![[99-Attachment/local_images/1.jpeg]]可以渲染出来
![[99-Attachment/local_images/1.jpeg|500]]就无法渲染了

噢噢我知晓了,根据gpt给的方案,将正则修改为
x = x.replace(/![[(.+?)(?:|(\d+))?]]/g, function(match, p1)
就可以解决原先提出的问题
同时修改返回值,加入 height="400",可以固定渲染出来的图片的高度(如果宽度width)

return "<img src=\""+app.vault.adapter.basePath+"/"+p1+"\" height=\"400\">"

是的是的,我之前没有放在格式框内,显示有问题。修改了一下。

目前是通过dv.paragraph进行渲染,请问这个可以和dv.table或者dv.list相结合吗?
类似dv.table(file.name,file.figure)的效果?或者包含其他YAML区中的信息?

例如,
文件1 文件1.infor 文件1.figure
文件2 文件2.infor 文件2.figure

目前的显示效果是一股脑的图片,没有文件信息
文件1.figure
文件2.figure

需要这个功能的场景:监控文件变化后,对于新接收的pdf文件,我会根据需要用acrobat打开文件并进行标注。

通过dataviewjs检索文件后,如何才能实现“使用默认应用中打开文件”这个功能呢?

就像在文件列表的文件上调用“使用默认应用中打开”。

image

测试一个折中做法,直接在文件链接上可以使用右键命令,和文件列表上使用效果是一样的。

PixPin_2023-12-02_01-42-22

let afiles = dv.array(app.vault.getFiles()).filter(p=> p.extension != 'md').sort(p=>moment(p.stat.mtime),'desc');
dv.table(["文件名("+afiles.length+"个)","文件夹","默认打开","修改时间","距离今天"], afiles.map(p=>{
let div = dv.container.createEl('div',{text:'打开'})
div.onclick=()=>app.openWithDefaultApp(p.path)
return [dv.fileLink(p.path,false,p.name),p.parent.path,div,moment(p.stat.mtime).format("LL"),moment().diff(moment(p.stat.mtime),'days')]
}))

let folders = [];
let time = [];
afiles.forEach(p=>{
	if(!folders.includes(p.parent.path)){
		folders.push(p.parent.path)
		time.push(p.stat.mtime)
	}
})
dv.table(["文件夹("+folders.length+"个)","修改时间","距离今天"],folders.map(p=>app.vault.getAbstractFileByPath(p)).map((p,i)=>[p.name,moment(time[i]).format("LL"),moment().diff(moment(time[i]),'days')]))

有点丑陋,你可以加点css进去,反正默认打开的方法是 app.openWithDefaultApp(p.path)

1 个赞

非常感谢。

我还利用Obsidian的书签功能给关注的文件进行了标记,Obsidian的 :rose: 书签功能 :rose: 还是挺好用的,可以标记文件(含MD和非markdown文件)、文件夹、markdown文件中的标题。

新的问题来了,如何筛选打过书签的文件呢?

通过查阅dataview提供的页面文件的元数据说明,通过dv.pages().filter(f=>f.file.starred)可以得到打过书签的笔记文件,但是附件文件(非markdown)是通过app.vault.getFiles().filter(f=>f.extension != 'md')得到的,他们没有相应属性或者方法。

请问,有Obsidian内部函数或者属性来判断文件是否打过书签吗?