Dataviewjs的奇技淫巧

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

目前是通过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内部函数或者属性来判断文件是否打过书签吗?

通过控制台里的代码检索,找到了相关的内部函数,

app.internalPlugins.getEnabledPluginById("bookmarks").getBookmarks()

image

基于这个就可以进一步处理了。其实这是没有办法的事情,因为Obsidian主旨还是处理markdown文件,但是在我的工作环境,大量信息交换的是基于doc、pdf等文件交换。

后续基于书签再完善一下我的附件管理的思路。

1. 书签插件使用

Obsidian提供了核心插件—书签(Bookmarks),让用户对关注的东西进行标记,可以进一步利用标签组(类似文件夹)进行组织,通过命令或者菜单可以标记:

  • 文件(含Markdown笔记文件和非Markdown附件文件)
  • 文件夹
  • Markdown笔记文件的标题(Heading)、段落(block)
  • Obsidian的核心插件-搜索(search)的搜索条件。

2. Dataview插件检索

Dataview插件在文件属性中给出了starred,这是一个Boolean值,如果文件被书签插件进行了关注,则为true

const files = dv.pages().filter(f=>f.file.starred)
dv.header(4,"被书签化标记的文件")
const files = dv.pages().filter(f=>f.file.starred)
dv.list(files.map(f=>f.file.path))

3. Bookmark插件检索

const bookmarkfiles = app.internalPlugins.getEnabledPluginById("bookmarks").getBookmarks()

通过书签插件获取的书签信息,包含了:

  1. 书签类型(type),包括:file、search、folder、group;
  2. 文件路径(path)
  3. 子路径(subpath)
  4. 标签时间(ctime),number类型的,应该可以使用moment进行转换。
  5. 标签标题(title),打标签时输入的名字,没有输入为空;
  6. 标签分组(group),在items中记录了标签的type、path、ctime、title等信息。

下面简单给出了文件类型的标签信息表。

const bookmarkfiles = app.internalPlugins.getEnabledPluginById("bookmarks").getBookmarks();

dv.header(4,"文件类型书签")
dv.table(["标签标题","标签类型","文件路径","打标时间"],bookmarkfiles.filter(bf=>{return (bf.type==='file')}).map(bf=>[bf.title,bf.type,bf.path,moment(bf.ctime).format("LLL")]))

吐槽:文件中已经删除了曾经打标的welcome.md文件,但是还是检索到了书签信息,这应该是一个 :beetle: bug :beetle: 吧。

1 个赞

佬,请问你这个视图是在 Ob 执行在控制台查看,还是直接在控制台输入?

console.log(dv.page("文件名"))

我在控制台摸了大半天,最后发现在 Ob 里用 DataviewJS 这么写然后直接在控制台接收就行… 想知道是不是有其他我一直没搞懂的方法。

是在一个笔记里写的 dv.page("文件名"),然后在控制台查看,其实也可以控制台输入 DataviewAPI.page("文件名"),一样的效果,怎么方便怎么来

1 个赞

1 个赞

@lazyloong @阳光灿烂小逗逗 感谢,又多学到一些了 :smile:

image

1、打开 DevTools

Ctrl + Shift + I,窗口边框样式为 Obsidian 风格还可以左上角 Ob 图标 - View - Toggle Developer Tools。

2、找到 Console

3、向下滑找到小三角

4、在小三角所在行输入代码,获取当前文件元数据

DataviewAPI.page(app.workspace.getActiveFile().basename)

5、回车,得到结果,点击实心小三角展开

1 个赞

当然,有的时候在笔记里用 console.log 会更方便,比如在文件里写好代码后的调试

1 个赞

佬,我写了一个类似点击翻页的效果的 dvjs,但是 Ob 一刷新就回第 1 页了,有没有办法储存结果,类似记录上次翻到第几页呢,不然多少有点图一乐了…

20231215_014800

DataviewJS 代码:

async function extract(files) {
    let tdata = []
    for (let file of files) {
        let content = ( await app.vault.readRaw(file.file.path) ).split('\n\n')  // 分块
        content.filter(p=> p.includes("【**") && p.includes("**】") )  // 检索
            ?.forEach(p=> tdata.push([ '[❔](<' + file.file.path + '#' + p.trim().match( / (\^[a-z0-9]{6,})/ )?.[1] + '>)', p.trim() ]))  // 格式
    } // 翻页
    let sliceRange = [0, 8]
    function uptd() {
        dv.container.innerHTML = ''; // 清除旧数据
        dv.el("button", "-8").onclick = function() {
            if (sliceRange[0] > 0) { sliceRange[0] -= 8; sliceRange[1] -= 8; uptd () }
        }
        dv.el("button", "+8").onclick = function() { sliceRange[0] += 8; sliceRange[1] += 8; uptd () }
        dv.table( ["文件", "所在行"], tdata.slice(sliceRange[0], sliceRange[1]) )  // 输出
    }; uptd ()
}; extract(dv.pages(`"文件目录"`))

我想到了一个新办法,只要 ob 不关闭就能保持数据

async function extract(files) {
    let tdata = []
    for (let file of files) {
        let content = ( await app.vault.readRaw(file.file.path) ).split('\n\n')  // 分块
        tdata.push([file.file.path])  // 格式
    } // 翻页
    if(!global.page) global.page = 0

    dv.el("button", "-8").onclick = () => {
        if (global.page > 0) global.page-=1
        update()
    }
    dv.el("button", "+8").onclick = () => { 
	    global.page+=1
	    update()
	}
	let table = dv.el('div')
	function update() {
		table.empty()
        dv.api.table( ["文件", "所在行"], tdata.slice(global.page*8,(global.page+1)*8 ),table,dv.component)
	}
	update()
}
extract(dv.pages(`"300-系列笔记/320-读书笔记/图解逻辑学"`))
1 个赞

直接把关键数据放到 global,不过可能要注意一点变量名冲突的问题,像 page 这种简单的命名可能很容易冲突,毕竟不晓得或者说懒得看是不是原本就存在这个变量嘛,可以命名为 dvPage 之类的

1 个赞

可以请问一下您这个代码是怎么写的吗 :joy:

用复制粘贴不如直接保存下来得了,比如专门用个json文件保存这些脚本的数据,然后设置一个保存按钮。之前不爱用是因为频繁读写文件麻烦,现在用 global 暂时保存方便多了。

1 个赞

主要是不会嘛 :joy:,非计算机专业,谢谢建议,得空研究一下。

现在会啦 :blush: 原来不需要额外的操作,代码直接复制粘贴到外部 JS 文件,Ob DataviewJS dv.view(JS 路径) 就行。

完整内容已发布:

我当时用js写的,但现在obsidian不支持嵌套属性,很久没用了,代码大约如下:

```dataviewjs
class DayCost {
  constructor(date, cost) {
    this.date = date;
    this.cost = cost;
  }
}

var cost=dv.pages('#log/2022/05').consumption.values
var filename=dv.pages('#log/2022/05').file.link
var daycost = 0
var monthcost = 0
var costarray = new Array()

for(var i=0; i<cost.length; i++){
    for(var c in cost[i]){
        daycost=daycost+cost[i][c]
        monthcost=monthcost+cost[i][c]
    }
    costarray[i]=new DayCost(filename[i],daycost)
    daycost = 0
}
costarray[i]=new DayCost("本月总[[消费]]",monthcost)

dv.table(["日期", "Consumption"],costarray.map(cos => {
	return [cos.date,cos.cost]
})
)
```

这不就图个方便吗 :joy:,上了正则当然不需要按照别人定的格式来写了。dv 的 :: 还不能记录多行数据嘞,设想的
image
结果是两个 null