dataview三栏数据汇总视图

自己这块对于数据有一个特殊需求,根据不同的yaml属性分组汇总文件,方便自己根据属性,以及属性值快速筛选文件,正好摸鱼无聊,借助dataview试了一下,好像还不错
三栏

9 个赞

求分享代码。。。

1 个赞

求分享代码。。。

好厉害!!!支持

https://wwi.lanzoup.com/i7ct61xv02cf

这个:https://wwi.lanzoup.com/i7ct61xv02cf

其实 dv.view() 默认会传 dv,所以不用再自己的参数再传 dv

const { propertyTypesArg, folderArg } = input
dv.el(...) // 直接写就行

学到了 :nerd_face: ,这块对吧,当时看了半天没看懂这句话啥意思,就自己传了一个

超级感谢楼主:pray:

太好了,这个正是我所需要的,解决了我多年的困扰,我之前是用project,每次手动筛选来解决这个问题

怎么用呀,为什么我的没内容

同样问题,不太会用

1 个赞

image
目录下的文件需要有对应的yaml标签

多谢解答,搞懂怎么用了

非常喜欢,已经用上,实在是太棒了

特别感谢:heart_eyes:,已经用上了,还用chatgpt修改了一下让它更好地自适应页面宽度

1 个赞

可以求个css吗,我怎么调也调不动,没法自适应页面宽度,尤其在手机上更窄了

1 个赞

我改了一个合适的css,原理是变成了弹性布局,自动按比例分配空间
而且增加了一个button,可以在匹配文件夹新建一个具有相应yaml的文件

这里是代码
// 主视图函数,接收配置参数对象
function mainView({dvOpArg, propertyTypesArg, folderArg}) {
  const dvOp = dvOpArg;       // 获取Dataview API操作对象
  const folder = folderArg;   // 获取要查询的目标文件夹路径
  const propertyTypes = [     // 创建属性类型数组的副本(避免修改原始数据)
    ...propertyTypesArg       // 使用展开运算符进行浅拷贝
  ];

  // 创建顶层容器
  const container = dvOp.el("div", "", { 
    attr: { style: "display: flex; flex-direction: column; gap: 10px;" } 
  });

  // 新建按钮容器(单独放在最上方)
  const headerContainer = dvOp.el("div", "", { 
    container,
    attr: { style: "margin-bottom: 5px;" }
  });

  // 添加新建按钮
  const newButton = dvOp.el("button", "新建+", {
    container: headerContainer,
    attr: { 
      style: `
        background-color: var(--interactive-accent);
        color: white;
        padding: 5px 15px;
        Margin-left:25px;
        border: none;
        border-radius: 4px;
        cursor: pointer;
        font-weight: 500;`
    }
  });

  // 新建按钮点击事件
  newButton.addEventListener("click", async (evt) => {
    evt.preventDefault();
    
    // 生成Frontmatter
    const frontmatter = propertyTypesArg.map(prop => {
      const defaultValue = prop.type === "list" ? "[]" : '""';
      return `${prop.code}: ${defaultValue}`;
    }).join("\n");

    // 创建文件名
    const timestamp = new Date().getTime();
    const fileName = `新建笔记-${timestamp}.md`;
    
    try {
      // 创建文件
      const newFile = await dv.app.vault.create(
        `${folder}/${fileName}`, 
        `---\n${frontmatter}\n---\n`
      );
      
      // 在新标签页打开
      dv.app.workspace.openLinkText(newFile.basename, newFile.path, true, { active: false });
    } catch (error) {
      console.error("创建文件失败:", error);
      dvOp.el("div", "创建文件失败,请检查控制台", { container: headerContainer });
    }
  });

  // 创建三栏容器
  const listRoot = dvOp.el("div", "", {
    container,
    cls: "listRoot",
    attr: {style: `
      display: flex;
      height: 505px;
      gap: 15px;
      box-sizing: border-box;`}
  });

  // 左侧属性类型选择栏(1份)----------------------------------------
  const mainRoot = dvOp.el(
    "div",
    "",
    {
      container: listRoot,     // 指定父容器
      cls: "leftRoot",         // CSS类名
      attr: {
        style: `
          height: 500px;       /* 高度略小于父容器避免滚动条溢出 */
          overflow: scroll;    /* 内容溢出时显示滚动条 */
          padding: 5px;        /* 内边距 */
          flex: 0.6;             /* 占据1份弹性空间 */`
      }
    }
  );

  // 创建属性类型按钮列表---------------------------------------------
  propertyTypes.forEach(item => {           // 遍历每个属性类型
    item.btn = dvOp.el(                     // 创建按钮元素并保存到item对象
      "li",                                 // 列表项元素
      dvOp.el("button", item.name),         // 嵌套创建按钮元素
      {
        container: mainRoot,                // 指定父容器
        attr: {
          style: "margin: 3px"              // 按钮间距样式
        }
      }
    )
  })

  // 中间属性值列表栏(1份)------------------------------------------
  const leftRoot = dvOp.el(
    "div",
    "",
    {
      container: listRoot,     // 指定父容器
      cls: "leftRoot",         // CSS类名
      attr: {
        style: `
          height: 500px;
          overflow: scroll;
          padding: 5px;
          flex: 1.4;             /* 占据1份弹性空间 */`
      }
    }
  );

  // 右侧详情展示栏(3份)-------------------------------------------
  const rightRoot = dvOp.el(
    "div",
    "",
    {
      container: listRoot,     // 指定父容器
      cls: "rightRoot",        // CSS类名
      attr: {
        style: `
          height: 500px;
          overflow: scroll;
          padding: 5px;
          flex: 3;             /* 占据3份弹性空间 */`
      }
    }
  );

  // 状态管理变量---------------------------------------------------
  let isInit = true; // 初始化标记,用于首次加载时自动触发数据加载

  // 数据展示函数---------------------------------------------------
  function showData(a, code, type) {
    // 参数说明:
    // a - 选中的属性值
    // code - 属性类型代码
    // type - 属性值类型(list/single)
    
    rightRoot.replaceChildren(); // 清空右侧栏内容
    
    if (a && a !== "") {         // 有效值检查
      dvOp.pages(`"${folder}"`)  // 查询指定文件夹的页面
        .where(t =>              // 过滤条件
          type === "list" ?      // 根据属性类型选择过滤方式
          t[code]?.includes(a) : // 列表属性:检查是否包含
          t[code] === a          // 单值属性:精确匹配
        )
        .map(k => k.file.link)   // 提取文件链接
        .forEach(item => {       // 遍历结果
          dvOp.el(               // 创建列表项
            "li", 
            item, 
            {
              container: rightRoot,
              attr: {style: "margin: 5px"}
            }
          );
        });
    }
  }

  // 事件绑定逻辑---------------------------------------------------
  propertyTypes.forEach(typeItem => {  // 遍历每个属性类型
    typeItem.btn.addEventListener(     // 为按钮添加点击事件监听
      "click", 
      async (evt) => {                 // 异步事件处理函数
        evt.preventDefault();          // 阻止默认行为
        leftRoot.replaceChildren();    // 清空中间栏
        rightRoot.replaceChildren();   // 清空右侧栏
        
        const typeMap = [];            // 创建类型统计数组

        // 数据统计逻辑--------------------------------------------
        if (typeItem.type === 'list') {  // 处理列表类型属性
          for (const item of dvOp.pages(`"${folder}"`)) {  // 遍历所有页面
            if (item[typeItem.code]?.length > 0) {         // 检查有效属性
              for (const it of item[typeItem.code]) {      // 遍历属性值列表
                const typeObj = typeMap.findIndex(         // 查找已有统计项
                  value => value.name === it
                );
                if (typeObj > -1) {
                  typeMap[typeObj].count += 1;  // 计数增加
                } else {
                  typeMap.push({                // 新建统计项
                    name: it, 
                    count: 1
                  });
                }
              }
            }
          }
        } else {                              // 处理单值类型属性
          for (const item of dvOp.pages(`"${folder}"`)) {
            if (item[typeItem.code]) {        // 检查有效属性
              const it = item[typeItem.code];
              const typeObj = typeMap.findIndex(
                value => value.name === it
              );
              if (typeObj > -1) {
                typeMap[typeObj].count += 1;
              } else {
                typeMap.push({
                  name: it, 
                  count: 1
                });
              }
            }
          }
        }

        // 渲染中间栏列表------------------------------------------
        for (const item of typeMap) {        // 遍历统计结果
          const li = dvOp.el(                // 创建列表项
            "li", 
            "", 
            {
              container: leftRoot,
              attr: {style: "margin: 5px"}
            }
          );
          const button = dvOp.el(            // 创建带计数的按钮
            "button", 
            `${item.name}(${item.count})`, 
            { container: li }
          );
          
          button.addEventListener("click",   // 按钮点击事件
            async (evt) => {
              evt.preventDefault();
              showData(item.name, typeItem.code, typeItem.type);
            }
          );

          // 自动触发首次点击--------------------------------------
          if (isInit) {                      // 检查初始化标记
            button.click();                  // 模拟点击
            isInit = false;                  // 标记已初始化
          }
        }
      }
    );
  })

  // 初始化触发第一个属性类型的点击----------------------------------
  propertyTypes[0].btn.click();
}

// 执行主函数-------------------------------------------------------
mainView(input);
1 个赞

你好,感谢你的CSS代码,我把这个代码用以下方式使用:
20250212_080234_759
但是不知道为什么,好像前后没啥效果,中间还是有大段的空白,没有紧凑(第一个为未使用CSS,第二个为使用CSS):
无CSS 添加CSS
是我的使用方法有问题吗?还是哪里出问题了吗?

1 个赞

用法是直接替换原来的js文件(删去原来的),我是在dataviewjs代码中修改了css

1 个赞