任务管理数据库模版分享

起因

最近看了《防弹笔记法》,书中所有任务都是在对应的项目笔记中,把任务都记录在笔记里,这样可以方便溯源整个任务的上下文,减少任务上下文的切换,我以前一直用滴答清单进行管理,但是项目笔记又在思源中,两个软件来回切换也是有点难受,所以干脆尝试 all in siyuan 了,下面模版就是成果。

模版介绍

目前这套模版我优化起码半个月,最麻烦的是各种模版列代码的逻辑优化,此处感谢 deepseek 以及 F 佬的帖子思源模板功能新人指南:模板语法 + 函数 + md 块语法 - 链滴,只需要把帖子里的语法喂给 AI 基本就能慢慢帮我写好。

以下是模版文档,下载导入即可使用,无需任何其他插件,只依赖思源本身的数据库。

任务管理数据库模版.sy.zip

具体用法还是看下面图片和视频,并且在模版内的每个字段上我都添加了备注,鼠标移动上去就能看到对应备注信息。

image.png

功能演示视频:

看完上面视频应该能基本了解用法,我是把任务块添加至数据库,然后任务块的状态可以通过模版列自动同步至数据库,这样的好处是任务块状态和数据库内保持一致,如果是单独在数据库新建复选框,那就要点两次完成了,非常不优雅,当然现在受制于思源的文档刷新,无法实现数据库模版列内点完成同步至任务,必须通过悬浮窗进去点击完成,但我觉得完全能接受,如果不喜欢可以自己换成复选框完成,不管笔记内任务的状态。

以下是模版列实现自动操作的步骤。

  • 自动获取任务列表所在的文档标题
  • 自动获取任务列表完成状态
  • 自动计算距离开始日、截止日还有多久
  • 自动显示未添加日期待定的任务

更快的任务添加方式之 Supertag

以上视频演示的是通过标签将任务添加至数据库,我认为这个方式比去块菜单里添加至数据库快许多,只需要添加以下 JS 代码即可实现,注意数据库名称要和标签名称完全一致。非常感谢 @qiancang 大佬的这个帖子的灵感 [Quicker] 思源笔记 SuperTag - 链滴 以及 @wilsons 大佬的 JS 代码编写和帮助(让大佬帮我改了 N 次代码),让这一切可以得以顺利实现。

// 思源通过标签插入当前块到数据库(SuperTag)
// 功能:给块设置标签,将块添加到标签同名的数据库。
// 说明;
// 1、数据库名称需要与标签同名,名称需含前后#,如 #笔记软件#
// 2、如果有多个同名数据库,只会将块添加到其中一个,所以不要建立同名数据库
// 3、需要提前建立数据库才能添加成功 
// version:0.0.4
// 更新记录
// 0.0.2 增加数据库同名文档标签即可把文档添加到标签同名数据库中(文档标签这里指文档头部的添加标签)。
// 0.0.3 增加可删除数据库引用文本中的标签名选项isShowTagNameInAvCell
// 0.0.4 改进当标签在列表中时数据库插入列表项
// 根据qiancang大佬的帖子实现 https://ld246.com/article/1731945645865
(()=>{
    // 添加tag后多少毫秒添加当前块到数据库
    // 不宜设置过小,过小可能导致标签被插入一半
    const delay = 1000;

    // 是否开启文档标签插入同名数据库(文档标签这里指文档头部的添加标签),true开启,false不开启
    const enableDocTagToAv = true;

    // 是否在数据库列表中显示标签名(数据库名),true显示,false不显示(注意,文档块引用不会添加标签名,实现较麻烦暂不支持)
    const isShowTagNameInAvCell = false;
  
    // 发布服务立即返回
    if(siyuan.config.readonly) return;
  
    // 监听tag输入
    observeTagSpans(async (tagEl, tagType) => {
        // 如果未开启文档标签插入同名数据库,当为文档标签时返回
        if(!enableDocTagToAv && tagType === 'doc-tag') return;
        // 去掉零宽度字符&ZeroWithSpace;
        const tag = tagEl?.textContent?.replace(/[\u200B-\u200D\uFEFF]/g, '')?.trim();
        if(!tag) return;
        // 获取数据库信息
        const av = await getAvByName(tag);
        if(!av) return;
        const avId = av.avID;
        if(!avId) return;
        const avBlockID = av.blockID;
        if(!avBlockID) return;
        // 获取文档块信息
        let block;
        if(tagType === 'doc-tag') {
            // 如果头部标签,返回文档id
            const blockParent = tagEl.closest('div.protyle-top');
            if(!blockParent) return;
            block = blockParent.querySelector('.protyle-title');
        } else {
            // 如果块标签,返回块id(监听元素的临时块)
            block = tagEl.closest('div[data-node-id][data-type]');
            if(!block) return;
            // 获取文档中的block结点
            block = document.querySelector('div[data-node-id="'+(block?.dataset?.nodeId||'')+'"]');
            if(!block) return;
            // 判断是否在列表元素内,数据库插入列表项
            const listItemNode = block.closest('div[data-node-id][data-type="NodeListItem"]');
            if(listItemNode) block = listItemNode;
        }
        const blockId = block?.dataset?.nodeId;
        if(!blockId) return;
        // 添加块到数据库
        await sleep(delay || 500);
        addBlocksToAv(blockId, avId, avBlockID);
    });

    // 如果不在数据库中显示标签名则删除标签名(注意,文档块引用不会添加标签名,实现较麻烦暂不支持)
    if(!isShowTagNameInAvCell) {
        observeElementCreation(
            document.body,
            '.av__row:not(.av__row--header) [data-dtype="block"] [data-type="block-ref"]',
            async ref => {
                if(!/\s?#.*?#/i.test(ref.textContent)) return;
                ref.textContent = ref.textContent.replace(/\s?#.*?#/ig, '');
            }
        );
    }
  
    // 插入块到数据库
    async function addBlocksToAv(blockIds, avId, avBlockID) {
        blockIds = typeof blockIds === 'string' ? [blockIds] : blockIds;
        const srcs = blockIds.map(blockId => ({
            "id": blockId,
            "isDetached": false,
        }));
        const input = {
          "avID": avId,
          "blockID": avBlockID,
          'srcs': srcs
        }
        const result = await fetchSyncPost('/api/av/addAttributeViewBlocks', input);
        if(!result || result.code !== 0) console.error(result);
    }
    // 通过该tag查询数据库
    async function getAvByName(name) {
        const result = await fetchSyncPost('/api/av/searchAttributeView', {
            "keyword": name
        });
        if(!result || result.code !== 0 || !result?.data?.results || result?.data?.results?.length === 0) return null;
        for (const av of result.data.results) {
            if (av.avName === name || av.avName === `#${name}#`) {
                return av;
            }
        }
        return null;
    }
    // 请求api
    // returnType json返回json格式,text返回文本格式
    async function fetchSyncPost(url, data, returnType = 'json') {
        const init = {
            method: "POST",
        };
        if (data) {
            if (data instanceof FormData) {
                init.body = data;
            } else {
                init.body = JSON.stringify(data);
            }
        }
        try {
            const res = await fetch(url, init);
            const res2 = returnType === 'json' ? await res.json() : await res.text();
            return res2;
        } catch(e) {
            console.log(e);
            return returnType === 'json' ? {code:e.code||1, msg: e.message||"", data: null} : "";
        }
    }
    // 延迟执行
    function sleep(ms) {
        return new Promise(resolve => setTimeout(resolve, ms));
    }
    // 监听tag被添加
    function observeTagSpans(callback) {
        // 创建一个观察者实例并传入回调函数
        const observer = new MutationObserver((mutationsList, observer) => {
            for (const mutation of mutationsList) {
                if (mutation.type === 'childList') {
                    // 检查新增的节点
                    for (const node of mutation.addedNodes) {
                        if (node.nodeType === Node.ELEMENT_NODE && node.tagName.toLowerCase() === 'span' && node.getAttribute('data-type') === 'tag') {
                            // 块标签调用回调函数
                            callback(node, 'block-tag');
                        } else if(node.nodeType === Node.ELEMENT_NODE && node.tagName.toLowerCase() === 'div' && node.classList?.contains('b3-chip') && node.getAttribute('data-type') === 'open-search') {
                            // 文档头部标签调用回调函数
                            callback(node, 'doc-tag');
                        }
                    }
                }
            }
        });
        // 配置观察选项:
        const config = { 
            childList: true, // 观察子节点的变化(添加/删除)
            subtree: true // 观察所有后代节点
        };
        // 选择需要观察变动的节点
        const targetNode = document.body; // 或者选择更具体的父节点以减少性能消耗
        // 开始观察目标节点
        observer.observe(targetNode, config);
        // 返回一个取消观察的方法
        return () => observer.disconnect();
    }
    // 监听元素被创建
    function observeElementCreation(parentNode, selector, onElementCreated) {
        // 配置观察器选项
        const config = { 
            childList: true, // 观察直接子节点的添加和移除
            subtree: true    // 观察所有后代节点
        };
        // 当检测到变动时执行的回调函数
        const callback = function(mutationsList, observer) {
            for (let mutation of mutationsList) {
                if (mutation.type === 'childList') {
                    mutation.addedNodes.forEach(node => {
                        if (node.nodeType === Node.ELEMENT_NODE) {
                            // 使用 querySelectorAll 查找所有符合条件的新元素
                            const elements = node.querySelectorAll(selector);
                            elements.forEach(element => {
                                onElementCreated(element); // 调用外部提供的回调函数
                            });
                        }
                    });
                }
            }
        };
        // 创建一个观察器实例并传入回调函数
        const observer = new MutationObserver(callback);
        // 开始观察目标节点
        observer.observe(parentNode, config);
        // 返回一个函数来停止观察
        return () => observer.disconnect();
    }
})();

此为代码原始链接:思源/思源通过标签插入当前块到数据库 SuperTag.js · wish/mysoft - Gitee.com

未来展望

至此基本就介绍完了,剩下的遗憾主要是没有其他更直观好看的视图,比如看板视图、分组功能等,希望 D 大 V 姐加把劲,争取明年补上 😂

END

  • 数据库

    据说 99% 的性能瓶颈都在数据库。

    343 引用 • 723 回帖
  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    23137 引用 • 93179 回帖
  • 模版
    2 引用 • 15 回帖
  • 任务管理
    2 引用 • 16 回帖
3 操作
5kyfkr 在 2024-11-29 19:27:14 更新了该帖
5kyfkr 在 2024-11-28 08:36:34 更新了该帖
5kyfkr 在 2024-11-28 01:50:05 更新了该帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...
  • JeffreyChen 1 赞同 1 评论

    牛的。再发展发展都可以卖模板了 👍

    分享一下我的任务管理方法:

    1. 把事项都列在一个叫“最近任务”的文档,从来不关页签,每天打开思源首先看到待办事项。
    2. 简单的事项使用嵌套的列表,列表项内补充相关信息,然后折叠列表项有需要再展开看;复杂的事项或者是其他项目中的事项就单独建一个文档然后在“最近任务”中引用。
    3. 通过两种块背景颜色来标注事项的紧迫性;
    4. 通过直觉判断任务重要性,想做哪个就做哪个,不能再拖延的时候我肯定会做。

    image.png

    可以滴,这个方式也挺简洁,就是缺了日期属性,我主要用来管理工作项目任务更多,一天有十几条任务多的时候,对筛选时间需求就大点
    5kyfkr 1 赞同
  • wilsons 1 2 评论

    非常棒!👍

    思源从此有任务管理了 😄

    哈哈,感谢大佬支持,不然整不出来这么多功能
    5kyfkr
    @5kyfkr 不客气哈,你这个很实用,我不过锦上添花罢了
    wilsons 1
  • pakeh2866 1 赞同

    666

  • CYKONG2016 1 赞同

    很好用!感谢了!

  • lxfw2000 1 赞同

    厉害!我真的得好好学习下 👍

  • taobuyan 2 评论

    小白请教怎么把模板导入到思源里用啊?

    1 操作
    JeffreyChen 在 2024-11-28 09:09:41 更新了该回帖
    直接文档树里右击文件夹或文档,然后导入 siyuan.sy.zip
    5kyfkr
    已搞定,感谢
    taobuyan 1 赞同
  • powehi 2 赞同 1 评论

    好好牛,如果删除标签能够删除数据库记录就更好了

    感觉不错,我问问大佬有没可能实现
    5kyfkr
  • spj888 1 评论

    希望搞个模板一键发布到集市的功能,这样集市就有更多模板了

    确实,现在还要专门去 github 弄非常麻烦
    5kyfkr
  • crjcrjcrj 1 赞同 1 评论

    瞌睡来了送枕头!

    我正在自己研究怎么搞数据库同步我的待办事项

    简直神了

    给你点赞 👍👍👍👍👍

    不客气哈哈
    5kyfkr
  • qiancang 1 1 评论

    1 操作
    qiancang 在 2024-11-29 21:33:49 更新了该回帖
    感谢支持
    5kyfkr
  • FlyingY 1 评论

    视频里数据库名称没有加#也能自动添加

    是的,#标签#,数据库不用加#也能添加,保证标签两个#标签#中间的标签名字一样就行
    5kyfkr
  • tiewei 1 评论

    请问“状态”设置为“完成”,能否支持把原文档里的相应“任务”也打上勾?

    现在就两种方式,直接在文档里任务打勾,或者状态设置成完成,因为思源必须要文档打开的情况下才能把数据库的状态保存过去,状态里的完成其实是为了给数据库纯文本键或者其他不是任务块用的
    5kyfkr
  • noitejun 3 评论

    怎么修改,可以把任务列表默认都添加到数据库,而不需要添加标签?

    或者怎么自定义条件:例如自定义其他标签名称,自定义别名等?期待大佬更新
    noitejun
    默认添加会涉及到如果某些不需要添加到数据库里的任务就要删除了,而且如果任务想添加到多个任务数据库里也会混乱,比如我就有工作和生活两个任务库
    5kyfkr
    具体可以问问 wilsons 大佬,他说能实现但是场景不够通用,你这边再跟他沟通下好点
    5kyfkr
  • MasterYS 1 评论

    刚开始用思源。就卡在怎么做任务管理,就遇到这套模板了,首先谢谢 LZ,然后我换了个思路用这个管理器,不知道能不能解决下面各位的问题。

    不用主键管理任务块,而是直接用文本或者文档块替代,这样的话写作型任务开始的时候,就从文本新建块引来建文档块写作。完成后直接在数据库修改状态。非写作类的,就等处理完,直接修改状态。

    目前应该是足够我使用了,用一个月试一下

    图片.png

    当然可以这样用,这种就是传统数据库管理任务的办法,我这个主要解决了文档里任务块的状态与数据库同步
    5kyfkr
  • randomthought123 1 评论

    谢谢,找到这个帖子好开心!自己本来还在摸索发现已经有了!太感谢了。

    我有个小问题,有一行我加了标签然后我看不见标签了,但是每次改的时候对应的数据库哪一行还是会变动,是不是我需要别的插件来查开添加到数据库那几行的 tag,添加进去 tag 自动消失了。image.png

    标签打完会自动消失?能不能新建工作空间复现下,如果能直接把标签消失我觉得挺适合我的哈哈,现在我这是标签不会自动消失
    5kyfkr
请输入回帖内容 ...