[js] 让日记根据日期永久锁定

缘由

如题,自己常用思源做流水账日记,为防止误触或其他原因,增加日期锁定功能,其实就是拦截锁定的 api 并进行判断。

效果

如下图显示,打开非当前日期的指定日记时会拦截请求并提示。

PixPin20250506134555.png

用法

  1. 设置→外观→代码片段设置→js代码 中增加一个 js 代码块。
  2. 复制下面代码,将 targetBoxID 的值改为你想要应用日期锁定的笔记本
// 全局设置笔记本ID - 需要根据实际情况修改
const targetBoxID = ""; // 这里设置需要特殊处理的笔记本ID

// 保存原始的fetch方法
const originalFetch = window.fetch;

// 重写fetch方法进行API拦截
window.fetch = async function(input, init) {
    // 只拦截setBlockAttrs请求
    if (typeof input === 'string' && input.includes('/api/attr/setBlockAttrs')) {
        // 克隆请求参数,避免修改原始请求
        const clonedInit = { ...init };
    
        if (init && init.body) {
            // 解析请求体
            const requestBody = JSON.parse(init.body);
            const blockID = requestBody.id;
        
            // 检查此文档所属的笔记本是否为目标笔记本
            const boxInfo = await checkDocumentBox(blockID);
        
            if (boxInfo && boxInfo.box === targetBoxID) {
                // 提取文档ID中的日期部分 (前8位)
                const docDate = blockID.substring(0, 8);
                const today = getTodayDateString();
            
                // 如果不是当天的文档且正在尝试修改锁定状态
                if (docDate !== today && 
                    requestBody.attrs && 
                    "custom-sy-readonly" in requestBody.attrs) {
                
                    // 创建新的请求体,强制设置锁定状态为true
                    const newBody = { ...requestBody };
                    newBody.attrs["custom-sy-readonly"] = "true";
                
                    // 更新请求
                    clonedInit.body = JSON.stringify(newBody);
                    console.log("已锁定非当天文档:", blockID);
                
                    // 显示弹窗提示
                    showLockNotification(docDate);
                }
            }
        }
    
        // 使用可能修改过的参数继续请求
        return originalFetch(input, clonedInit);
    }
  
    // 非目标API,正常请求
    return originalFetch(input, init);
};

// 获取文档所属的笔记本ID
async function checkDocumentBox(blockID) {
    try {
        const response = await originalFetch("/api/block/getBlockInfo", {
            method: "POST",
            headers: {
                "Content-Type": "application/json"
            },
            body: JSON.stringify({ id: blockID })
        });
    
        const result = await response.json();
    
        if (result.code === 0 && result.data) {
            return result.data;
        }
        return null;
    } catch (error) {
        console.error("获取文档信息失败:", error);
        return null;
    }
}

// 获取当前日期字符串,格式为YYYYMMDD
function getTodayDateString() {
    const today = new Date();
    const year = today.getFullYear();
    const month = String(today.getMonth() + 1).padStart(2, '0');
    const day = String(today.getDate()).padStart(2, '0');
  
    return `${year}${month}${day}`;
}

// 显示锁定通知弹窗
function showLockNotification(docDate) {
    // 格式化日期显示
    const year = docDate.substring(0, 4);
    const month = docDate.substring(4, 6);
    const day = docDate.substring(6, 8);
    const formattedDate = `${year}-${month}-${day}`;
  
    // 创建弹窗元素
    const notification = document.createElement('div');
    notification.style.position = 'fixed';
    notification.style.top = '20px';
    notification.style.left = '50%';
    notification.style.transform = 'translateX(-50%)';
    notification.style.backgroundColor = '#ff4d4f';
    notification.style.color = 'white';
    notification.style.padding = '12px 24px';
    notification.style.borderRadius = '4px';
    notification.style.boxShadow = '0 4px 12px rgba(0, 0, 0, 0.15)';
    notification.style.zIndex = '9999';
    notification.style.fontWeight = 'bold';
    notification.style.fontSize = '14px';
    notification.style.display = 'flex';
    notification.style.alignItems = 'center';
    notification.style.gap = '8px';
  
    // 添加图标
    const icon = document.createElement('span');
    icon.innerHTML = '🔒';
    icon.style.fontSize = '16px';
    notification.appendChild(icon);
  
    // 添加文本
    const text = document.createElement('span');
    text.textContent = `该日记(${formattedDate})非当前时间,已永久锁定`;
    notification.appendChild(text);
  
    // 添加到页面
    document.body.appendChild(notification);
  
    // 3秒后自动移除
    setTimeout(() => {
        notification.style.opacity = '0';
        notification.style.transition = 'opacity 0.5s ease';
        setTimeout(() => notification.remove(), 500);
    }, 3000);
}

console.log("思源笔记API拦截器已加载,目标笔记本ID:", targetBoxID); 

获取笔记本 id

  1. 打开开发者工具
  2. 选择网络选项卡
  3. 清除已有的请求
  4. 打开任意一个待设置的笔记本下的文档
  5. 网络中找到 getBlockInfo 这个请求
  6. 在预览中复制 box 的值即为笔记本 id

PixPin20250506135813.webp

  • 思源笔记

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

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

    25641 引用 • 106049 回帖
  • 代码片段

    代码片段分为 CSS 与 JS 两种代码,添加在 [设置 - 外观 - 代码片段] 中,这些代码会在思源笔记加载时自动执行,用于改善笔记的样式或功能。

    用户在该标签下分享代码片段时需在帖子标题前添加 [css] [js] 用于区分代码片段类型。

    173 引用 • 1183 回帖

相关帖子

欢迎来到这里!

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

注册 关于
请输入回帖内容 ...
  • huaji 锁定?叶归以前可是按块锁定的,直到我出了一堆变成了 html 块的内容,我把自动锁定功能给去掉了

  • 其他回帖
  • 我经常把过去的日记反复编辑,严格说他并不是我的日记,而是我分散在不同日期反复编辑的收集箱。

  • 也可简单的这样

    .protyle-breadcrumb button[data-type="readonly"] {
        pointer-events: none; /* 禁止所有鼠标事件 */
        opacity: 0.5;
    }
    

    补充:

    你这个获取笔记本 id 的方法也太麻烦了,小白估计有难度,直接下面的操作就行

    image.png

    1 操作
    wilsons 在 2025-05-06 20:11:49 更新了该回帖
  • 其实也可以简单的把 锁定按钮 加个 disabled 属性,省得拦截 api 的麻烦了。

    1 回复
  • 查看全部回帖