Mindmap NextGen+DataviewJS 的文件文件夹导图树

点击在文件列表显示对应文件/文件夹。

效果 GIF,点击展开(240419 更新)

20240321_153758

20240419_173130

DataviewJS 代码
const genSele = (inpu, items)=> {
  const sele = createEl('datalist', {attr: {id: 'demo'}})
  inpu.setAttribute('list', 'demo')
  for (const item of items)
    sele.createEl('option', {value: item.path})
  return sele
}
const import_fdTree = (app)=> {
  return (dv, def)=> {
    let para = dv.el(), isFile = !1, folder, vs = [], r = []
    dv.span(' 现在是 ')
    const btn = dv.el('button'); btn.onclick = ()=> { isFile = !isFile; tree(!0) }
    dv.span(' 是否多选 ')

    const check = createEl('input', {type: 'checkbox'})
    const inpu = createEl('input', {attr: {style: 'width: 100px;'}})
    const folders = app.vault.getAllLoadedFiles().filter(p=> !p.extension)
    const sele = genSele(inpu, folders)

    inpu.onchange = ()=> {
      folder = folders.find(p=> p.path == inpu.value)
      if (vs.includes(folder)) { inpu.placeholder = '已选啦'; inpu.value = ''; return }
      folder && tree()
    }
    const dd = createEl('span'); dv.container.append(check, inpu, sele, dd)

    const attachFd = app.vault.config.attachmentFolderPath.slice(2)
    const add = p=> {
      const el = createEl('span', {text: ` ${p.name||'全库'}`, attr: {style: 'cursor: pointer;'}})
      el.ondblclick = ()=> { vs = vs.filter(item=> item !== p); el.remove(); tree(!0) }
      vs.push(p); dd.append(el)
    }

    const sorti = (items)=> items.sort((a, b)=> a.name.localeCompare(b.name, 'zh'))
    const genList = (shortPath, p)=> `${' '.repeat((shortPath.split('/').length-1)*4)}- [[${p.path}|${p.name}]]`
    const tra = items=> sorti(items).map(p=> {
      r.push(genList(p.path, p))
      isFile ? ((p.extension || p.name == attachFd) || tra(p.children)) : tra(p.children?.filter(p=> !p.extension))
    })

    const genMkList = (r)=> '```markmap\n---\nmarkmap:\n  height: 900\n---\n' + r.join('\n') + '\n```'
    const tree = async isClick=> {
      para.empty(); r = []; inpu.placeholder = ''; btn.textContent = isFile ? '文档' : '文件夹'
      if (check.checked) { isClick || add(folder); tra(vs) }
      else {
        dd.empty(); vs = []
        folder ? add(folder) : def.map(path=> add(app.vault.getAbstractFileByPath(path)))
        tra(vs)
      }
      para = dv.paragraph(genMkList(r)); inpu.value = ''
    }; tree()
  }
}
const fdTree = import_fdTree(dv.app); fdTree(dv, ['默认目录'])
  • 可自行修改默认目录为 ['文件目录']['目录1', '目录2'] 等。
  • 切换到文档层级时可能会卡,需等待。
旧方法:修改 main.js

1、下载 Mindmap NextGen 插件,找到 .obsidian/plugins/obsidian-mindmap-nextgen/main.js

2、修改 main.js

const link = `<a href=\"${url}\">${linkText}</a>`;

改为

const bta = path=> app.internalPlugins.getEnabledPluginById('file-explorer').revealInFolder(app.vault.getAbstractFileByPath(path))
, link = `<button onclick="(${bta})('${linkPath}')">${linkText}</button>`

3、保存 main.js,重启 Ob。

240728 更新: 也可以用 可编辑的浮窗与可导出的 Markdown Mindmap 导图 里的插件,就不用自己改 main.js 了。

5 个赞

大佬,这种实现方式太优雅了,恰好我刚好前几天也有尝试类似的,方法比较笨重,哈哈哈:

1 个赞

一个小问题,我没改 mindmap nextgen插件的main.js代码,因为我使用的folder note,而不用左侧的文件列表,不需要点击定位到左侧文件列表

此时有一个问题,我希望点击跳转到对应的folder note,也就是说我需要的路径是AAA/BBB/BBB.md,现在只有AAA/BBB

改了下代码,可以实现了
去掉了文件/文件夹选择,只显示文件夹树状结构,因为切换到文件太卡了
现在有两个问题,一个是查找功能,必须打全文件夹名字,而且要把dv设置里的刷新间隔调大点才好用
一个是会莫名其妙多一个分割线,不知道为什么

```dataviewjs
let para = dv.el(),
  r = [];
const el1 = dv.el("input");
el1.style.width = "100px";
dv.el("button", "查找").onclick = () => mk();
const tra = (items) =>
    items.map((p) => {
    if (p.name !== "assets" && p.name !== "待整理" && !p.extension) {
      r.push([`${p.path}/${p.name}.md`, p.name]);
        tra(p.children?.filter((p) => !p.extension));
    }}),
  mk = () => {
    para.empty();
    r = [];
    tra(
      (el1.value ? [el1.value] : ["/"]).map((path) =>
        app.vault.getAbstractFileByPath(path)
      )
    );
    para = dv.paragraph(
      "```markmap\n---\nmarkmap:\n  height: 900\n---\n" +
        r
          .map(
            (p) =>
              `${" ".repeat((p[0].split("/").length - 2) * 4)}- [[${p[0]}|${
                p[1]
              }]]`
          )
          .join("\n") +
        "\n```"
    );
  };
mk();
```
1 个赞

感觉这个更符合我的期望

您好,我更改了main.js


也复制过代码了,但是还是报错了,请问您能看出来问题原因么?
dataviewjs

下报错
Evaluation Error: TypeError: Cannot read properties of null (reading ‘name’)
at add (eval at (plugin:dataview), :7:49)
at eval (eval at (plugin:dataview), :21:46)
at Array.map ()
at mk (eval at (plugin:dataview), :21:35)
at eval (eval at (plugin:dataview), :27:4)
at DataviewInlineApi.eval (plugin:dataview:18869:16)
at evalInContext (plugin:dataview:18870:7)
at asyncEvalInContext (plugin:dataview:18880:32)
at DataviewJSRenderer.render (plugin:dataview:18906:19)
at DataviewJSRenderer.onload (plugin:dataview:18448:14)

dataview

下报错

Dataview: Error:
– PARSING FAILED --------------------------------------------------

1 | let para = dv.el(), isFile = false, folder, vs = , r =
| ^
2 | const dc = str=> document.createElement(str), check = dc(‘input’), inpu = dc(‘input’), sele = dc(‘datalist’), dd = dc(‘span’)
3 | , folders = app.vault.getAllLoadedFiles().filter(p=> !p.extension).map(p=> {

Expected one of the following:

Not a comment, TABLE or LIST or TASK or CALENDAR, whitespace

调整了一下,你可以再复制一下 DataviewJS 代码,记得 Ctrl Shift V 纯文本复制,我已经在沙箱试过了没问题。

1 个赞