要插入的代码:

```dataviewjs
const pages = dv.pages('"3. Resourses"').where(p => p.страница && p["всего страниц"]);
const container = this.container;

// === UI: Filter by categories ===
const filterWrapper = document.createElement("div");
filterWrapper.style.display = "flex";
filterWrapper.style.gap = "1rem";
filterWrapper.style.marginBottom = "1rem";

// Categories (tags)
const select = document.createElement("select");
const allOption = document.createElement("option");
allOption.textContent = "All categories";
allOption.value = "";
select.appendChild(allOption);

const allTags = new Set();
for (let p of pages) {
  const tags = Array.isArray(p.tags) ? p.tags : [p.tags];
  tags.forEach(t => t && allTags.add(t));
}
for (let tag of Array.from(allTags).sort()) {
  const opt = document.createElement("option");
  opt.textContent = tag;
  opt.value = tag;
  select.appendChild(opt);
}
filterWrapper.appendChild(select);

// === UI: Search field ===
const searchInput = document.createElement("input");
searchInput.type = "text";
searchInput.placeholder = "🔍 Search for a book...";
searchInput.style.flex = "1";
filterWrapper.appendChild(searchInput);

// === Rendering the table ===
container.appendChild(filterWrapper);

function renderTable(selectedTag = "", searchTerm = "") {
  const oldTable = container.querySelector("table");
  if (oldTable) oldTable.remove();

  const table = document.createElement("table");
  table.className = "dataview table-view-table";

  const header = table.insertRow();
  ["📕 Cover", "📘 Book", "✍️ Author", "📚 Category", "📖 Read", "📈 Progress bar"].forEach(title => {
    const th = document.createElement("th");
    th.textContent = title;
    header.appendChild(th);
  });

  for (let p of pages) {
    const tagMatch = !selectedTag || (Array.isArray(p.tags) ? p.tags.includes(selectedTag) : p.tags === selectedTag);
    const searchMatch = !searchTerm || p.file.name.toLowerCase().includes(searchTerm.toLowerCase());

    if (!(tagMatch && searchMatch)) continue;

    const current = Number(p.страница);
    const total = Number(p["всего страниц"]);
    const percent = Math.round((current / total) * 100);
    const color = percent === 100 ? 'green' : 'orange';

    const row = table.insertRow();

    const cellCover = row.insertCell();
    cellCover.innerHTML = p.cover ? `<img src="${p.cover}" width="100" style="border-radius: 8px;">` : "—";

    const cellTitle = row.insertCell();
    const link = document.createElement("a");
    link.href = p.file.path;
    link.textContent = p.file.name;
    link.className = "internal-link";
    cellTitle.appendChild(link);

    const cellAuthor = row.insertCell();
    cellAuthor.textContent = p.Автор || "–";

    const cellTags = row.insertCell();
    cellTags.textContent = Array.isArray(p.tags) ? p.tags.join(", ") : (p.tags || "–");

    const cellProgressText = row.insertCell();
    cellProgressText.textContent = `${current} / ${total}`;

    const cellBar = row.insertCell();
    const progressBarContainer = document.createElement("div");
    progressBarContainer.style.background = "#eee";
    progressBarContainer.style.borderRadius = "6px";
    progressBarContainer.style.height = "16px";
    progressBarContainer.style.width = "100%";
    const progressBar = document.createElement("div");
    progressBar.style.background = color;
    progressBar.style.height = "100%";
    progressBar.style.borderRadius = "6px";
    progressBar.style.width = `${percent}%`;
    progressBarContainer.appendChild(progressBar);
    const percentText = document.createElement("div");
    percentText.style.fontSize = "12px";
    percentText.style.textAlign = "right";
    percentText.textContent = `${percent}%`;
    cellBar.appendChild(progressBarContainer);
    cellBar.appendChild(percentText);
  }

  container.appendChild(table);
}

// === Listeners ===
select.onchange = () => renderTable(select.value, searchInput.value);
searchInput.oninput = () => renderTable(select.value, searchInput.value);

// Initialisation
renderTable();```

→ 把 “const pages = dv.pages(‘“3. Resourses”’)” 中的路径替换成你自己存放带 YAML 笔记的路径 → 把最后三个撇号字符移到新的一行


🧠 给想更深入理解这段代码的朋友的说明:

🔍 1. 从文件夹中加载书籍

const pages = dv.pages('"3. Resourses"')
  .where(p => p.страница && p["всего страниц"]);

这里我们:

  • 获取 "3. Resourses" 文件夹中的所有笔记(请替换成你自己的路径)。

  • 只筛选出带有 страница(当前页数)和 всего страниц(总页数)这两个字段的笔记。

📌 也就是说,你可以直接在 YAML 里记录每本书读到了哪里。


🧰 2. 创建界面——筛选和搜索

const filterWrapper = document.createElement("div");
...

这里创建了两个控件:

  • 一个按分类(依据 tags)筛选的下拉列表;

  • 一个按文件名搜索的搜索框(笔记名 = 书名)。

📌 一切都是通过 document.createElement 手动完成的,所以这个元素会直接嵌入到笔记中。


🧠 3. 收集不重复的分类

for (let p of pages) {
  const tags = Array.isArray(p.tags) ? p.tags : [p.tags];
  tags.forEach(t => t && allTags.add(t));
}

我们遍历所有书籍,把不重复的标签(分类)收集到一个列表中。


📋 4. 绘制书籍表格

function renderTable(selectedTag = "", searchTerm = "") {

renderTable 函数:

  • 按分类和搜索条件筛选书籍;

  • 创建包含封面、作者和进度的表格;

  • 每次筛选或搜索时重新生成表格。

📌 正是这个函数让你的表格既是动态的,又能实时更新。


🖼️ 5. 表格的每一行显示什么?

每一行有 6 个单元格:

列显示内容
📕 封面如果 YAML 中指定了 cover:,则显示该图片
📘 书名书名,以指向笔记的链接形式显示
✍️ 作者YAML 中的 Автор: 字段
📚 分类书籍的标签
📖 已读格式为 23 / 200
📈 进度可视化进度条

🌡️ 进度条的效果

const percent = Math.round((current / total) * 100);
const color = percent === 100 ? 'green' : 'orange';
  • 计算已读百分比;

  • 如果已经读完——显示绿色;

  • 如果还在阅读中——显示橙色。

就在 Obsidian 里,有一个直观的指示器,就像常见的阅读追踪器一样。


🎮 6. 加入”生命力”

select.onchange = () => renderTable(select.value, searchInput.value);
searchInput.oninput = () => renderTable(select.value, searchInput.value);

当用户更改分类或输入查询内容时,表格会根据筛选条件重新绘制。


🧩 运行它需要什么

  • 一个存放书籍笔记的文件夹;

  • 每篇笔记都应包含带有以下字段的 YAML:

cover: https://link.to/cover.jpg
Автор: Author Name
tags: [Productivity, Philosophy]
страница: 80
всего страниц: 200

💥 你将得到什么

  • 一个带筛选和搜索功能的书籍目录;

  • 漂亮的视觉效果——封面、进度、分类;

  • 它直接在 Obsidian 中运行,无需任何外部服务。