├── .DS_Store ├── Bullet Journal模版文件分享.zip ├── Minimalism Challenge Calender ├── Minimalism Game Calendar.md ├── minimalism_items.md ├── 挑战日历使用说明.md └── 极简挑战物品记录表及评分规则.md ├── README.md ├── bujo_calender_views V1.0.zip └── bujo_calender_views V1.0 ├── .DS_Store ├── calendar.css ├── markdown文件 ├── .DS_Store ├── 周视图.md ├── 日视图.md ├── 月视图.md └── 视图合集.md ├── 使用指南.md ├── 使用指南.pdf ├── 视图blue_dark_v1.0.png ├── 视图blue_light_v1.0.png ├── 视图green_dark_v1.0.png └── 视图green_lignt_v1.0.png /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YuriWg/Obsidian_Templates/1cbeab148121cfbd320b2fdf08ae89b527e23e1f/.DS_Store -------------------------------------------------------------------------------- /Bullet Journal模版文件分享.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YuriWg/Obsidian_Templates/1cbeab148121cfbd320b2fdf08ae89b527e23e1f/Bullet Journal模版文件分享.zip -------------------------------------------------------------------------------- /Minimalism Challenge Calender/Minimalism Game Calendar.md: -------------------------------------------------------------------------------- 1 | --- 2 | created: 2025-03-01T22:09:00 3 | updated: 2025-03-19T10:35 4 | challenge_month: 3 5 | life_stage: 0.8 6 | --- 7 | ```dataviewjs 8 | // 在最外层添加背景容器 9 | const mainContainer = document.createElement('div'); 10 | mainContainer.style.cssText = ` 11 | background: linear-gradient(135deg, #f5f5f5 0%, #fafafa 100%); 12 | border-radius: 0px; 13 | padding: 40px; 14 | margin: 20px 0; 15 | `; 16 | 17 | // 生成30天极简主义游戏的日历 18 | const currentDate = new Date(); 19 | const challengeMonth = this.challenge_month; 20 | const currentYear = currentDate.getFullYear(); 21 | 22 | // 添加环形进度条SVG生成函数 23 | function generateProgressRing(score) { 24 | const radius = 15; 25 | const circumference = 2 * Math.PI * radius; 26 | const progress = (score / 100) * circumference; 27 | const dashOffset = circumference - progress; 28 | 29 | return ` 30 | 31 | 36 | 44 | 49 | ${Math.round(score)} 50 | 51 | `; 52 | } 53 | 54 | // 添加处理方式映射 55 | const disposalMethods = { 56 | 0: { label: "立即处理", icon: "🗑️"}, 57 | 20: { label: "推荐舍弃", icon: "📤"}, 58 | 40: { label: "灵活处理", icon: "🔄"}, 59 | 60: { label: "建议保留", icon: "📥"}, 60 | 80: { label: "必须保留", icon: "📦"} 61 | }; 62 | 63 | // 获取处理方式 64 | function getDisposalMethod(score) { 65 | const thresholds = Object.keys(disposalMethods) 66 | .map(Number) 67 | .sort((a, b) => b - a); 68 | 69 | for (const threshold of thresholds) { 70 | if (score >= threshold) { 71 | return disposalMethods[threshold]; 72 | } 73 | } 74 | return disposalMethods[0]; 75 | } 76 | 77 | try { 78 | // 1. 直接读取文件内容 79 | const filePath = '0_Bullet Journal/Monthly Challenge/2025 Mar 30天极简主义挑战/minimalism_items.md'; 80 | const content = await dv.io.load(filePath); 81 | 82 | if (!content) { 83 | throw new Error('无法读取文件内容'); 84 | } 85 | 86 | // 2. 定位JSON数据 87 | const startIndex = content.indexOf('[{'); 88 | const endIndex = content.lastIndexOf('}]') + 2; 89 | 90 | if (startIndex === -1 || endIndex <= 1) { 91 | throw new Error('未找到有效的JSON数据'); 92 | } 93 | 94 | const jsonString = content.slice(startIndex, endIndex); 95 | const rawData = JSON.parse(jsonString); 96 | const itemCategories = { 97 | "衣物": {color: "#B4846C", icon: "🧣", order: 1}, 98 | "日用品": {color: "#A7B89D", icon: "🧴", order: 2}, 99 | "食物": {color: "#E1A692", icon: "🍕", order: 3}, 100 | "电子产品": {color: "#7895B2", icon: "📱", order: 4}, 101 | "文具": {color: "#BBAB8C", icon: "✏️", order: 5}, 102 | "书籍": {color: "#E5BA73", icon: "📖", order: 6}, 103 | "装饰品": {color: "#C17C74", icon: "🎀", order: 7}, 104 | "其它": {color: "#939B9F", icon: "🫙", order: 8}, 105 | "default": {color: "#e8e8e8", icon: "📎", order: 9} 106 | }; 107 | 108 | 109 | // 导出颜色映射 110 | const setcolors = Object.fromEntries( 111 | Object.entries(itemCategories).map(([key, value]) => [key, value.color]) 112 | ); 113 | 114 | // 导出图标映射 115 | const categoryIcons = Object.fromEntries( 116 | Object.entries(itemCategories).map(([key, value]) => [key, value.icon]) 117 | ); 118 | 119 | function getCategoryColor(category) { 120 | return itemCategories[category]?.color || itemCategories.default.color; 121 | } 122 | 123 | function getCategoryIcon(category) { 124 | return itemCategories[category]?.icon || itemCategories.default.icon; 125 | } 126 | 127 | function getSortedCategories() { 128 | return Object.entries(itemCategories) 129 | .sort((a, b) => a[1].order - b[1].order) 130 | .map(([key]) => key); 131 | } 132 | 133 | // 3. 从JSON数据中提取表格数据 134 | const tableData = rawData.map(row => ({ 135 | date: row.日期.replace(/-/g, ''), 136 | item: row.物品, 137 | category: row.分类 || 'default', 138 | freq: Number(row['使用频率']) || 0, 139 | necessity: Number(row['必要性']) || 0, 140 | irreplace: Number(row['不可替代性']) || 0, 141 | space: Number(row['空间负担']) || 0, 142 | multifunction: Number(row['多功能性']) || 0, 143 | emotion: Number(row['情感价值']) || 0, 144 | maintenance: Number(row['维护费用']) || 0, 145 | cost: Number(row['获取成本']) || 0 146 | })); 147 | 148 | // 添加生活阶段系数的计算函数 149 | function getLifeStageMultiplier(stage) { 150 | const multipliers = { 151 | 1: 0.8, 152 | 2: 1.0, 153 | 3: 1.3 154 | }; 155 | return multipliers[stage] || 1.0; 156 | } 157 | 158 | // 修改计算得分函数 159 | function calculateScore(item) { 160 | const scores = [ 161 | item.freq, 162 | item.necessity, 163 | item.irreplace, 164 | item.space, 165 | item.multifunction, 166 | item.emotion, 167 | item.maintenance, 168 | item.cost 169 | ]; 170 | 171 | const baseScore = scores.reduce((sum, score) => sum + score, 0); 172 | const lifeStageMultiplier = getLifeStageMultiplier(dv.current().life_stage); 173 | const finalScore = baseScore * lifeStageMultiplier; 174 | 175 | return Math.min(100, Math.max(0, finalScore)); 176 | } 177 | 178 | // 添加月度统计面板 179 | const monthStats = tableData.reduce((stats, item) => { 180 | const score = calculateScore(item); 181 | stats.totalScore += score; 182 | stats.itemCount++; 183 | stats.categories[item.category] = (stats.categories[item.category] || 0) + 1; 184 | return stats; 185 | }, { totalScore: 0, itemCount: 0, categories: {} }); 186 | 187 | // 修改统计面板的风格和内容 188 | const statsPanel = document.createElement('div'); 189 | statsPanel.style.cssText = ` 190 | display: flex; 191 | flex-direction: column; 192 | gap: 15px; 193 | margin: 20px 0; 194 | padding: 20px; 195 | `; 196 | 197 | // 更新统计面板的HTML结构 198 | statsPanel.innerHTML = ` 199 |
206 |
212 |
221 | 📦 222 |
223 |
224 |
${monthStats.itemCount}
230 |
待处理物品
234 |
235 |
236 | 237 |
248 | ${Object.entries(monthStats.categories) 249 | .sort((a, b) => { 250 | const orderA = itemCategories[a[0]]?.order || 999; 251 | const orderB = itemCategories[b[0]]?.order || 999; 252 | return orderA - orderB; 253 | }) 254 | .map(([category, count]) => ` 255 |
267 | ${categoryIcons[category] || categoryIcons.default} 270 |
275 | ${category} 279 | ${count} 284 |
285 |
286 | `).join('')} 287 |
288 |
289 | `; 290 | 291 | // 修改得分指导说明部分 292 | const scoreGuide = document.createElement('div'); 293 | scoreGuide.style.cssText = ` 294 | position: relative; 295 | padding: 8px; 296 | border-radius: 12px; 297 | font-size: 0.9em; 298 | margin-bottom: -15px; 299 | `; 300 | 301 | // 更新得分指导的HTML结构 302 | scoreGuide.innerHTML = ` 303 |
312 | ${[ 313 | {score: '80-100', label: '必须保留', icon: "🗂️"}, 314 | {score: '60-79', label: '建议保留', icon: "📥"}, 315 | {score: '40-59', label: '灵活处理', icon: "🔄"}, 316 | {score: '20-39', label: '推荐舍弃', icon: "📤"}, 317 | {score: '0-19', label: '立即处理', icon: "🗑️"} 318 | ].map(item => ` 319 |
327 | 333 | ${item.icon} 334 | ${item.score} 335 | ${item.label} 336 |
337 | `).join('')} 338 |
339 |
348 | `; 349 | 350 | statsPanel.appendChild(scoreGuide); 351 | 352 | // 生成表格布局 353 | let table = `
`; 359 | 360 | const totalDays = 30; 361 | 362 | for (let day = 1; day <= totalDays; day++) { 363 | const dateStr = `2025${String(3).padStart(2,'0')}${String(day).padStart(2,'0')}`; 364 | const dayRecord = tableData.find(item => item.date === dateStr); 365 | const dayLabel = `Day ${day}`; 366 | 367 | let itemInfo = dayRecord ? ` 368 |
378 |
${dayLabel}
388 |
${dayRecord.item}
399 |
411 | ${categoryIcons[dayRecord.category]} 417 | 426 |
${Math.round(calculateScore(dayRecord))}
430 | ${getDisposalMethod(calculateScore(dayRecord)).icon} 431 |
432 |
433 |
` : ''; 434 | 435 | const content = dayRecord ? ` 436 | ${itemInfo}` : ` 447 |
${dayLabel}
`; 454 | 455 | table += ` 456 |
${content}
`; 471 | } 472 | table += `
`; 473 | 474 | // 修改主容器的添加顺序 475 | mainContainer.innerHTML = ` 476 |
30 Days
486 |
Minimalism Game
496 | `; 497 | 498 | mainContainer.appendChild(statsPanel); 499 | 500 | const tableContainer = document.createElement('div'); 501 | tableContainer.innerHTML = table; 502 | tableContainer.style.cssText = ` 503 | width: 100%; 504 | //max-width: 1000px; 505 | margin: 30px 0px; 506 | padding: 0; 507 | `; 508 | 509 | mainContainer.appendChild(tableContainer); 510 | dv.container.appendChild(mainContainer); 511 | 512 | } catch (error) { 513 | console.error('数据处理错误:', error); 514 | dv.paragraph(`❌ 数据加载失败: ${error.message}`); 515 | } 516 | ``` -------------------------------------------------------------------------------- /Minimalism Challenge Calender/minimalism_items.md: -------------------------------------------------------------------------------- 1 | --- 2 | created: 2025-03-12T18:50 3 | updated: 2025-03-19T10:35 4 | life_stage: 1 5 | --- 6 | 7 | [{"日期":"2025-03-01","物品":"一个草莓熊支架","分类":"装饰品","使用频率":0,"必要性":8,"不可替代性":3,"空间负担":20,"多功能性":3,"情感价值":0,"维护费用":5,"获取成本":0},{"日期":"2025-03-02","物品":"一些明信片","分类":"文具","使用频率":0,"必要性":0,"不可替代性":3,"空间负担":20,"多功能性":0,"情感价值":5,"维护费用":5,"获取成本":0}] -------------------------------------------------------------------------------- /Minimalism Challenge Calender/挑战日历使用说明.md: -------------------------------------------------------------------------------- 1 | --- 2 | created: 2025-03-19T10:42 3 | updated: 2025-03-19T11:00 4 | --- 5 | 6 | # 30天极简主义挑战日历使用说明 7 | 8 | ## 1. 前置条件 9 | - 确保已安装并启用Dataview插件,并开启dataviewJS相关功能 10 | ![image.png|400](https://raw.githubusercontent.com/YuriWg/Obisidian_Pic/main/Picgo/dataviewjs.png) 11 | 12 | - 确保`minimalism_items.md`文件存在并包含有效的JSON数据,安装JSON table插件可一键实现markdown表格与JSON格式间的相互转换 13 | 14 | ## 2. 文件读取 15 | 当前文件读取路径设置如下: 16 | ``` 17 | 0_Bullet Journal/ 18 | └── Monthly Challenge/ 19 | └── 2025 Mar 30天极简主义挑战/ 20 | ├── Minimalism Game Calendar.md # 本日历文件 21 | └── minimalism_items.md # 物品数据文件 22 | ``` 23 | 需根据自己的路径调整的,找到如图代码位置,将filePath改为自己的文件路径即可 24 | ![image.png|400](https://raw.githubusercontent.com/YuriWg/Obisidian_Pic/main/Picgo/%E6%96%87%E4%BB%B6%E8%B7%AF%E5%BE%84.png) 25 | 26 | ## 3. 必要属性设置 27 | 在日历文件的YAML frontmatter中设置: 28 | ```yaml 29 | challenge_month: 3 # 挑战月份(1-12) 30 | life_stage: 1 # 生活阶段(1-3,分别对应不同系数0.8;1.0;1.3) 31 | ``` 32 | 33 | ## 4. 数据格式要求 34 | [[minimalism_items.md]] 文件需包含以下格式的JSON数据: 35 | ```json 36 | [ 37 | { 38 | "日期": "2025-03-01", 39 | "物品": "旧T恤", 40 | "分类": "衣物", 41 | "使用频率": 20, 42 | "必要性": 30, 43 | "不可替代性": 10, 44 | "空间负担": 40, 45 | "多功能性": 5, 46 | "情感价值": 15, 47 | "维护费用": 5, 48 | "获取成本": 10 49 | }, 50 | // ...更多物品数据 51 | ] 52 | ``` 53 | 54 | ## 5. 评分规则 55 | _详细见文档:[[极简挑战物品记录表及评分规则.md]]_ 56 | - 综合评分 = (各项评分之和) × 生活阶段系数 57 | - 评分范围:0-100分 58 | - 处理建议: 59 | - 80-100分:必须保留 60 | - 60-79分:建议保留 61 | - 40-59分:灵活处理 62 | - 20-39分:推荐舍弃 63 | - 0-19分:立即处理 64 | -------------------------------------------------------------------------------- /Minimalism Challenge Calender/极简挑战物品记录表及评分规则.md: -------------------------------------------------------------------------------- 1 | --- 2 | tags: 3 | - type/note 4 | - theme/life/minimalism 5 | - theme/challenge 6 | aliases: 7 | lead: 一份用于记录和评估个人物品价值的极简主义挑战表格,帮助系统化地整理和优化个人物品。 8 | visual: "![[image.jpg]]" 9 | template_type: Note 10 | updated: 2025-03-19T10:33 11 | created: 2025-03-06T11:09 12 | --- 13 | 14 | 15 | > [!Summary] 16 | > `= this.lead` 17 | 18 | ## 物品记录表格 19 | 20 | | date | item | catogory | freq | necessity | irreplace | space | multifunction | emotion | maintenance | cost | 21 | | :--------: | :-------------------: | :------: | :--------------------------: | :---------------------------: | :----------------------------: | :-----------------------------: | :-------------------------: | :-----------------------------: | :--------------------------: | :----------------------------: | 22 | | | *权重* | | *25%* | *15%* | *10%* | *20%* | *5%* | *15%* | *5%* | *5%* | 23 | | | *评分标准*
*(100x权重)* | | *每天用:25
每周用:18
半年未用:0* | *生存必需:15
提升质量:8
可有可无:0* | *特殊定制:10
普通替代品:3
随处可买:0* | *小体积高效用:20
中等:10
大件低效用:0* | *三用以上:5
双用途:3
单一功能:0* | *重大回忆载体:15
普通纪念品:8
无意义:0* | *无需维护:5
定期保养:3
持续耗材:1* | *限量版/绝版:5
高价购入:3
平价易得:0* | 24 | | 2025-03-01 | 一个草莓熊支架 | 装饰品 | 0 | 8 | 3 | 10 | 3 | 0 | 5 | 0 | 25 | | 2025-03-02 | 一些明信片 | 文具 | 0 | 0 | 3 | 10 | 0 | 5 | 5 | 0 | 26 | | 2025-03-03 | | | | | | | | | | | 27 | | 2025-03-04 | | | | | | | | | | | 28 | | 2025-03-05 | | | | | | | | | | | 29 | | 2025-03-06 | | | | | | | | | | | 30 | | 2025-03-07 | | | | | | | | | | | 31 | | 2025-03-08 | | | | | | | | | | | 32 | | 2025-03-09 | | | | | | | | | | | 33 | | 2025-03-10 | | | | | | | | | | | 34 | | 2025-03-11 | | | | | | | | | | | 35 | | 2025-03-12 | | | | | | | | | | | 36 | | 2025-03-13 | | | | | | | | | | | 37 | | 2025-03-14 | | | | | | | | | | | 38 | | 2025-03-15 | | | | | | | | | | | 39 | | 2025-03-16 | | | | | | | | | | | 40 | | 2025-03-17 | | | | | | | | | | | 41 | | 2025-03-18 | | | | | | | | | | | 42 | | 2025-03-19 | | | | | | | | | | | 43 | | 2025-03-20 | | | | | | | | | | | 44 | | 2025-03-21 | | | | | | | | | | | 45 | | 2025-03-22 | | | | | | | | | | | 46 | | 2025-03-23 | | | | | | | | | | | 47 | | 2025-03-24 | | | | | | | | | | | 48 | | 2025-03-25 | | | | | | | | | | | 49 | | 2025-03-26 | | | | | | | | | | | 50 | | 2025-03-27 | | | | | | | | | | | 51 | | 2025-03-28 | | | | | | | | | | | 52 | | 2025-03-29 | | | | | | | | | | | 53 | | 2025-03-30 | | | | | | | | | | | 54 | | 2025-03-31 | | | | | | | | | | | 55 | 56 | --- 57 | #### 日常物品舍弃评分规则 58 | (总分 = 各维度得分 × 生活阶段系数) 59 | 60 | | 评分维度 | 评分项 | 权重 | 评分标准 | 示例物品得分 | 61 | | :-------: | :---: | :-: | :----------------------------: | :-----------------: | 62 | | **功能性价值** | 使用频率 | 25% | 每天用(25)\| 每周用(15)\| 半年未用(0) | 牙刷(10)\| 帐篷(2) | 63 | | | 核心必要性 | 15% | 生存必需(15)\| 提升质量(8)\| 可有可无(0) | 药品(15)\| 香薰机(8) | 64 | | | 不可替代性 | 10% | 特殊定制(10)\| 普通替代品(3)\| 随处可买(0) | 义齿(10)\| 玻璃杯(0) | 65 | | **空间效率** | 占用体积 | 20% | 小体积高效用(20)\| 中等(10)\| 大件低效用(0) | 折叠伞(20)\| 沙发(5) | 66 | | | 多功能性 | 5% | 三用以上(5)\| 双用途(3)\| 单一功能(0) | 瑞士军刀(5)\| 擀面杖(0) | 67 | | **情感价值** | 纪念意义 | 15% | 重大回忆载体(15)\| 普通纪念品(8)\| 无意义(0) | 结婚戒指(15)\| 旅游明信片(3) | 68 | | **经济成本** | 维护费用 | 5% | 无需维护(5)\| 定期保养(3)\| 持续耗材(1) | 铸铁锅(3)\| 空气净化器滤芯(1) | 69 | | | 获取成本 | 5% | 限量版/绝版(5)\| 高价购入(3)\| 平价易得(0) | 绝版书(5)\| 超市碗碟(0) | 70 | 71 | #### 动态调整规则 72 | 1. **生活阶段系数** 73 | - 搬家/换季:总分 × 0.8(强化精简) 74 | - 稳定居住:总分 × 1.0 75 | - 空间充裕:总分 × 1.3(允许保留) 76 | 77 | 2. **职业/爱好修正** 78 | - 厨师保留专业刀具 +15% 79 | - 摄影师保留相机设备 +20% 80 | - 普通职员办公用品 +5% 81 | 82 | #### 决策阈值 83 | 84 | | **总分范围** | **行动建议** | **执行策略** | 85 | | -------- | -------- | ------------- | 86 | | 80-100 | **必须保留** | 设置专用收纳区 | 87 | | 60-79 | **建议保留** | 6个月观察期,定期复评 | 88 | | 40-59 | **灵活处理** | 转赠/二手出售/暂存储物间 | 89 | | 20-39 | **推荐舍弃** | 拍照留念后处理 | 90 | | 0-19 | **立即处理** | 直接回收/捐赠 | 91 | 92 | #### **风险对冲设计** 93 | 1. **暂存机制** 94 | - 设置「犹豫箱」:暂存3个月后再最终决定 95 | - 重要文件/照片:数字化备份后处理实体 96 | 97 | 2. **后悔防护** 98 | - 建立「舍弃日志」:记录物品信息和处理方式 99 | - 高价物品:优先二手平台寄售而非直接丢弃 100 | 101 | #### **示例计算** 102 | **物品:** 冬季厚外套(稳定居住期,普通职员) 103 | - 功能性:8(每周用) + 10(保暖必需) + 0(可替代) = **18** 104 | - 空间效率:5(大体积) + 0(单一功能) = **5** 105 | - 情感价值:0(无纪念) = **0** 106 | - 经济成本:3(需干洗) + 0(平价) = **3** 107 | - **原始总分**:18+5+0+3 = **26** 108 | - **动态调整**:26 × 1.0(稳定期) = **26** 109 | - **决策**:推荐舍弃(拍照留念后捐赠) 110 | 111 | **物品:** 老式机械手表(父亲遗物,稳定期) 112 | - 功能性:2(半年未用) + 3(非必需) + 10(不可替代) = **15** 113 | - 空间效率:8(小体积) + 0(单一功能) = **8** 114 | - 情感价值:10(重大回忆) + 5(定制刻字) = **15** 115 | - 经济成本:5(无需维护) + 5(绝版) = **10** 116 | - **总分**:15+8+15+10 = **48** → 调整后48 → **灵活处理** 117 | - **最终决策**:保留并放入纪念品收藏柜 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### 资料包说明 2 | #### 往期资料 3 | - 完整的Daily Log和Weekly Log: [Daily & Weekly Log模版](https://github.com/YuriWg/Obsidian_Templates/blob/main/Bullet%20Journal%E6%A8%A1%E7%89%88%E6%96%87%E4%BB%B6%E5%88%86%E4%BA%AB.zip) 4 | - 日、周、月打卡视图模版: [打卡小工具](https://github.com/YuriWg/Obsidian_Templates/tree/main/bujo_calender_views%20V1.0) 5 | #### 本次更新 6 | - 月度挑战日历模版: [30天极简主义挑战日历](https://github.com/YuriWg/Obsidian_Templates/tree/main/Minimalism%20Challenge%20Calender) 7 | 8 | ![21441742350919_ pic](https://github.com/user-attachments/assets/77f024bc-ce24-4360-9a15-e5e53e3686c2) 9 | -------------------------------------------------------------------------------- /bujo_calender_views V1.0.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YuriWg/Obsidian_Templates/1cbeab148121cfbd320b2fdf08ae89b527e23e1f/bujo_calender_views V1.0.zip -------------------------------------------------------------------------------- /bujo_calender_views V1.0/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YuriWg/Obsidian_Templates/1cbeab148121cfbd320b2fdf08ae89b527e23e1f/bujo_calender_views V1.0/.DS_Store -------------------------------------------------------------------------------- /bujo_calender_views V1.0/calendar.css: -------------------------------------------------------------------------------- 1 | /* 日历容器样式 */ 2 | .calendar-container { 3 | background-color: hsla(var(--interactive-accent-hsl), 0.05); 4 | border-radius: 12px; 5 | padding: 1.2rem; 6 | text-align: left; 7 | color: var(--text-normal); 8 | box-shadow: 0 2px 8px rgba(0,0,0,0.05); 9 | } 10 | 11 | /* 周数标题 */ 12 | .calendar-week-title { 13 | color: var(--interactive-accent); 14 | font-weight: 800; 15 | font-size: 1.5em; 16 | margin-bottom: 1rem; 17 | margin-top: 1rem; 18 | margin-left: 2rem; 19 | } 20 | 21 | /* 分割线 */ 22 | .calendar-divider { 23 | height: 1px; 24 | background: hsla(var(--interactive-accent-hsl), .1); 25 | margin: 0px 20px; 26 | } 27 | 28 | /* 日历网格 */ 29 | .calendar-grid { 30 | display: grid; 31 | grid-template-columns: repeat(7, minmax(0, 1fr)); 32 | gap: 0.2em; 33 | padding: 0em 1em; 34 | } 35 | 36 | /* 星期标题 */ 37 | .weekday-header { 38 | text-align: center; 39 | font-weight: bold; 40 | font-size: 15px; 41 | color: var(--interactive-accent); 42 | padding-top: 1em; 43 | } 44 | 45 | /* 日期单元格基础样式 */ 46 | .date-cell { 47 | text-align: center; 48 | border-radius: 20px; 49 | font-size: 15px; 50 | display: flex; 51 | justify-content: center; 52 | align-items: center; 53 | color: var(--text-muted); 54 | margin-left: 0 0.2em; 55 | min-width: 2em; 56 | } 57 | 58 | /* 当前日期高亮 */ 59 | .date-cell-current { 60 | background-color: var(--interactive-accent); 61 | color: white; 62 | font-weight: bold; 63 | padding: 0.5em 1em; 64 | margin: 1em 0.8em; 65 | } 66 | 67 | /* 有日记的日期样式 */ 68 | .date-cell-has-note { 69 | background-color: hsla(var(--interactive-accent-hsl), 0.3); 70 | } 71 | 72 | /* 引用区块容器 */ 73 | .quote-container { 74 | border-top: 1px solid hsla(var(--interactive-accent-hsl), 0.1); 75 | margin-top: 0.5em; 76 | padding-top: 2em; 77 | padding-bottom: 1.2em; 78 | font-size: 1em; 79 | color: var(--interactive-accent); 80 | position: relative; 81 | font-weight: bold; 82 | line-height: 2; 83 | margin-left: 20px; 84 | margin-right: 20px; 85 | } 86 | 87 | /* 引用图标 */ 88 | .quote-icon { 89 | position: absolute; 90 | left: 0px; 91 | top: -5px; 92 | font-size: 24px; 93 | color: var(--interactive-accent); 94 | opacity: 1; 95 | } 96 | 97 | /* 周视图容器 */ 98 | .week-view-container { 99 | display: flex; 100 | flex-direction: row; 101 | gap: 1rem; 102 | margin: 1em 0; 103 | } 104 | 105 | .week-view-container .calendar-grid { 106 | display: grid; 107 | grid-template-columns: repeat(7, 2.5em); 108 | gap: 0.5em; 109 | padding: 1.5em; 110 | margin: 0em 1em; 111 | justify-content: center; 112 | } 113 | 114 | .week-view-container .date-cell { 115 | width: 2.5em; 116 | height: 2.5em; 117 | display: flex; 118 | justify-content: center; 119 | align-items: center; 120 | border-radius: 50%; 121 | font-size: 0.9em; 122 | color: var(--text-normal); 123 | margin: 0; 124 | padding: 0; 125 | } 126 | 127 | .week-view-container .date-cell.date-cell-current { 128 | background-color: var(--interactive-accent); 129 | color: white !important; 130 | font-weight: bold; 131 | box-shadow: 0 2px 4px rgba(0,0,0,0.1); 132 | } 133 | 134 | .week-view-container .date-cell.date-cell-empty { 135 | background-color: hsla(var(--interactive-accent-hsl), 0.1); 136 | border: 1px solid var(--interactive-accent); 137 | color: var(--text-normal); 138 | } 139 | 140 | .week-view-container .date-cell.date-cell-other { 141 | color: var(--text-muted); 142 | opacity: 0.5; 143 | } 144 | 145 | .week-view-container .weekday-header { 146 | text-align: center; 147 | font-weight: bold; 148 | font-size: 15px; 149 | color: var(--interactive-accent); 150 | padding-top: 0em; 151 | padding-bottom: 1em; 152 | } 153 | 154 | /* 月历部分 */ 155 | .month-calendar { 156 | flex: 1; 157 | background-color: hsla(var(--interactive-accent-hsl), 0.05); 158 | border-radius: 12px; 159 | padding: 1.2rem; 160 | box-shadow: 0 2px 8px rgba(0,0,0,0.05); 161 | } 162 | 163 | .month-calendar-title { 164 | color: var(--interactive-accent); 165 | font-weight: 800; 166 | font-size: 1.5em; 167 | margin-top: 1.5rem; 168 | margin-bottom: 0.8rem; 169 | text-align: center; 170 | } 171 | 172 | /* 习惯跟踪部分 */ 173 | .habit-tracker { 174 | display: flex; 175 | background-color: hsla(var(--interactive-accent-hsl), 0.05); 176 | border-radius: 12px; 177 | padding: 1.2rem; 178 | height: 350px; 179 | box-shadow: 0 2px 8px rgba(0,0,0,0.05); 180 | flex: 2; 181 | overflow: hidden; 182 | } 183 | 184 | .habit-grid { 185 | display: grid; 186 | grid-template-columns: minmax(100px, 1fr) repeat(7, minmax(1.5rem, 1fr)); 187 | gap: 4px; 188 | width: 100%; 189 | text-align: center; 190 | } 191 | 192 | .habit-day-name { 193 | font-weight: bold; 194 | color: var(--interactive-accent); 195 | font-size: 1.1em; 196 | padding: 4px 0; 197 | } 198 | 199 | .habit-name { 200 | text-align: left; 201 | padding: 4px 8px; 202 | font-weight: 500; 203 | } 204 | 205 | .habit-status { 206 | width: 1.5rem; 207 | height: 1.5rem; 208 | margin: 2px auto; 209 | display: flex; 210 | align-items: center; 211 | justify-content: center; 212 | border-radius: 50%; 213 | } 214 | 215 | .habit-status-done { 216 | background-color: var(--interactive-accent); 217 | box-shadow: 0 2px 6px rgba(var(--interactive-accent-rgb), 0.5); 218 | } 219 | 220 | .habit-status-empty { 221 | background-color: hsla(var(--interactive-accent-hsl), 0.1); 222 | border: 1.5px dotted hsla(var(--interactive-accent-hsl), 0.8); 223 | } 224 | 225 | .habit-status-mood { 226 | background-color: hsla(var(--interactive-accent-hsl), 0.1); 227 | border: 1.5px dotted hsla(var(--interactive-accent-hsl), 0.8); 228 | font-size: 1.5rem; 229 | color: var(--interactive-accent); 230 | } 231 | 232 | /* 月视图专用样式 */ 233 | .month-view-container { 234 | display: flex; 235 | flex-direction: column; 236 | gap: 1rem; 237 | margin: 1em 0; 238 | } 239 | 240 | /* 年度概览部分 */ 241 | .year-overview { 242 | background-color: hsla(var(--interactive-accent-hsl), 0.05); 243 | border-radius: 12px; 244 | padding: 2rem; 245 | margin-bottom: 0; 246 | box-shadow: 0 2px 8px rgba(0,0,0,0.05); 247 | } 248 | 249 | .year-title { 250 | color: var(--interactive-accent); 251 | font-weight: 800; 252 | font-size: 2em; 253 | margin-left: 0.5rem; 254 | margin-bottom: 1rem; 255 | } 256 | 257 | .year-subtitle { 258 | font-size: 0.5em; 259 | color: hsla(var(--interactive-accent-hsl), 1); 260 | } 261 | 262 | .months-container { 263 | display: flex; 264 | gap: 1rem; 265 | overflow-x: auto; 266 | padding: 0 2px 2px; 267 | scrollbar-width: thin; 268 | } 269 | 270 | .month-button { 271 | flex: 100%; 272 | height: 2rem; 273 | border-radius: 50px; 274 | display: flex; 275 | align-items: center; 276 | justify-content: center; 277 | margin-top: 1rem; 278 | font-weight: lighter; 279 | color: var(--text-muted); 280 | transition: all 0.2s ease; 281 | background: hsla(var(--interactive-accent-hsl), 0.05); 282 | border: 1px solid hsla(var(--interactive-accent-hsl), 0.5); 283 | font-size: 1rem; 284 | cursor: pointer; 285 | } 286 | 287 | .month-button.current { 288 | background: var(--interactive-accent); 289 | color: white; 290 | transform: scale(1); 291 | box-shadow: 0 4px 12px rgba(var(--interactive-accent-rgb), 0.3); 292 | } 293 | 294 | .month-button.has-note { 295 | background: hsla(var(--interactive-accent-hsl), 0.2); 296 | border-color: var(--interactive-accent); 297 | } 298 | 299 | /* 月度跟踪网格 */ 300 | .month-tracker { 301 | display: grid; 302 | grid-template-columns: minmax(90px, 1fr) repeat(31, minmax(1.5rem, 1fr)) minmax(50px, 0.5fr); 303 | gap: 4px; 304 | width: 100%; 305 | max-width: 100%; 306 | text-align: center; 307 | padding: 20px; 308 | background-color: hsla(var(--interactive-accent-hsl), 0.05); 309 | border-radius: 5px; 310 | overflow-x: auto; 311 | scrollbar-width: thin; 312 | -webkit-overflow-scrolling: touch; 313 | } 314 | -------------------------------------------------------------------------------- /bujo_calender_views V1.0/markdown文件/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YuriWg/Obsidian_Templates/1cbeab148121cfbd320b2fdf08ae89b527e23e1f/bujo_calender_views V1.0/markdown文件/.DS_Store -------------------------------------------------------------------------------- /bujo_calender_views V1.0/markdown文件/周视图.md: -------------------------------------------------------------------------------- 1 | --- 2 | created: 2025-03-01T10:25:00 3 | updated: 2025-03-01T13:11 4 | cssclasses: 5 | - week-view 6 | - calendar-views 7 | --- 8 | ```dataviewjs 9 | // 日期相关函数 10 | function formatDate(date) { 11 | return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`; 12 | } 13 | 14 | function getWeekRange(baseDate) { 15 | const dayOfWeek = (baseDate.getDay() + 6) % 7; 16 | const startDate = new Date(baseDate); 17 | startDate.setDate(baseDate.getDate() - dayOfWeek); 18 | const endDate = new Date(startDate); 19 | endDate.setDate(startDate.getDate() + 6); 20 | return { startDate, endDate }; 21 | } 22 | 23 | // 优化日期处理函数 24 | function getDateInfo(date) { 25 | return { 26 | year: date.getFullYear(), 27 | month: date.getMonth(), 28 | day: date.getDate(), 29 | weekday: (date.getDay() + 6) % 7 30 | }; 31 | } 32 | 33 | // 简化页面查询逻辑 34 | function getPageForDate(dateStr) { 35 | return dv.pages('"0_Bullet Journal/Daily Notes"') 36 | .where(p => p.file.name.startsWith(dateStr)) 37 | .first(); 38 | } 39 | 40 | // 修改容器类名,使用共享样式 41 | let combinedHTML = '
'; 42 | 43 | try { 44 | const baseDate = dv.current().created ? new Date(dv.current().created) : new Date(); 45 | const { startDate, endDate } = getWeekRange(baseDate); 46 | const currentYear = startDate.getFullYear(); 47 | const currentMonth = startDate.getMonth(); 48 | const currentWeek = Math.ceil((baseDate - new Date(baseDate.getFullYear(), 0, 1)) / (1000 * 60 * 60 * 24 * 7)); 49 | 50 | // 生成月历HTML 51 | combinedHTML += ` 52 |
53 |
54 | ${currentYear} ${["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][currentMonth]} W${currentWeek} 55 |
56 |
57 | ${generateWeekGrid(startDate, endDate)} 58 |
59 |
60 | `; 61 | 62 | // 生成习惯跟踪器HTML 63 | combinedHTML += ` 64 |
65 | ${generateHabitTracker(startDate, endDate)} 66 |
67 | `; 68 | 69 | } catch (e) { 70 | console.error("Error in week view:", e); 71 | combinedHTML += '
Error generating week view
'; 72 | } 73 | 74 | combinedHTML += '
'; 75 | dv.paragraph(combinedHTML); 76 | 77 | // 辅助函数 78 | function generateWeekGrid(startDate, endDate) { 79 | const dateInfo = getDateInfo(startDate); 80 | let gridHTML = ''; 81 | 82 | // 添加星期标题 83 | ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"].forEach(day => { 84 | gridHTML += `
${day}
`; 85 | }); 86 | 87 | // 重置时间部分,只比较日期 88 | startDate.setHours(0, 0, 0, 0); 89 | endDate.setHours(0, 0, 0, 0); 90 | 91 | // 获取当前月的信息 92 | const currentYear = startDate.getFullYear(); 93 | const currentMonth = startDate.getMonth(); 94 | const firstDay = new Date(currentYear, currentMonth, 1); 95 | const lastDay = new Date(currentYear, currentMonth + 1, 0); 96 | const firstDayWeek = (firstDay.getDay() + 6) % 7; 97 | const prevMonthDays = new Date(currentYear, currentMonth, 0).getDate(); 98 | 99 | // 添加上月末尾日期 100 | for (let i = 0; i < firstDayWeek; i++) { 101 | const date = new Date(currentYear, currentMonth - 1, prevMonthDays - firstDayWeek + i + 1); 102 | const isCurrentWeek = date >= startDate && date <= endDate; 103 | 104 | gridHTML += ` 105 |
106 | ${prevMonthDays - firstDayWeek + i + 1} 107 |
`; 108 | } 109 | 110 | // 添加本月日期 111 | for (let i = 1; i <= lastDay.getDate(); i++) { 112 | const currentDate = new Date(currentYear, currentMonth, i); 113 | currentDate.setHours(0, 0, 0, 0); 114 | const dateStr = formatDate(currentDate); 115 | const weekDay = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][currentDate.getDay()]; 116 | // 修改:查找方式改为精确匹配日期前缀 117 | const pages = dv.pages() 118 | .where(p => p.file.name.startsWith(dateStr)) 119 | .values; 120 | const page = pages.length > 0; 121 | 122 | const isCurrentWeek = currentDate >= startDate && currentDate <= endDate; 123 | 124 | gridHTML += ` 125 |
126 | ${i} 127 |
`; 128 | } 129 | 130 | // 添加下月开始日期 131 | const lastDayWeek = (lastDay.getDay() + 6) % 7; 132 | for (let i = 1; i <= 6 - lastDayWeek; i++) { 133 | const nextDate = new Date(currentYear, currentMonth + 1, i); 134 | nextDate.setHours(0, 0, 0, 0); 135 | const dateStr = formatDate(nextDate); 136 | // 修改:对下月日期也使用相同的查找逻辑 137 | const pages = dv.pages() 138 | .where(p => p.file.name.startsWith(dateStr)) 139 | .values; 140 | const page = pages.length > 0; 141 | 142 | const isCurrentWeek = nextDate >= startDate && nextDate <= endDate; 143 | 144 | gridHTML += ` 145 |
146 | ${i} 147 |
`; 148 | } 149 | 150 | return gridHTML; 151 | } 152 | 153 | function generateHabitTracker(startDate, endDate) { 154 | const habits = ['🧘 拉伸', '📓 日记', '💻 笔记', '🏃 慢跑', '📖 阅读', '🤔 Stoic', '💢 心情']; 155 | const moodEmojiMap = { 156 | '-5': '😱', '-4': '😖', '-3': '😟', '-2': '😔', '-1': '😕', 157 | '0': '😐', '1': '🙂', '2': '😊', '3': '😄', '4': '🥰', '5': '😇' 158 | }; 159 | 160 | let trackerHTML = `
161 |
162 |
163 | ${["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"].map(day => 164 | `
${day}
` 165 | ).join('')}`; 166 | 167 | habits.forEach(habit => { 168 | trackerHTML += `
${habit}
`; 169 | 170 | for (let i = 0; i < 7; i++) { 171 | const currentDate = new Date(startDate); 172 | currentDate.setDate(startDate.getDate() + i); 173 | const dateStr = formatDate(currentDate); 174 | const page = getPageForDate(dateStr); 175 | 176 | const status = page ? { 177 | '🧘 拉伸': page.stretch || false, 178 | '📓 日记': page.journal || false, 179 | '💻 笔记': page.notes || false, 180 | '🏃 慢跑': page.running || false, 181 | '📖 阅读': page.reading || false, 182 | '🤔 Stoic': page.stoic || "", 183 | '💢 心情': page.mood ?? null 184 | }[habit] : null; 185 | 186 | const isMood = habit === '💢 心情'; 187 | 188 | trackerHTML += ` 189 |
190 | 194 | ${isMood ? (status !== null ? moodEmojiMap[status] : '') : (status ? '✓' : '')} 195 | 196 |
`; 197 | } 198 | }); 199 | 200 | trackerHTML += '
'; 201 | return trackerHTML; 202 | } -------------------------------------------------------------------------------- /bujo_calender_views V1.0/markdown文件/日视图.md: -------------------------------------------------------------------------------- 1 | --- 2 | created: 2025-03-01T10:23 3 | updated: 2025-03-01T13:23 4 | cssclasses: 5 | - calendar-views 6 | --- 7 | ```dataviewjs 8 | // 获取当前页面的日期(支持文件名和created属性) 9 | const getPageDate = () => { 10 | // 尝试从文件名获取日期 11 | const fileName = dv.current().file.name.split(" ")[0]; 12 | const dateMatch = fileName.match(/^\d{4}-\d{2}-\d{2}$/); 13 | 14 | if (dateMatch) { 15 | const [year, month, day] = fileName.split("-").map(Number); 16 | return new Date(year, month - 1, day); 17 | } 18 | 19 | // 如果文件名不是日期格式,使用created属性 20 | return new Date(dv.current().created); 21 | }; 22 | 23 | const currentDate = getPageDate(); 24 | const [year, month, day] = [ 25 | currentDate.getFullYear(), 26 | currentDate.getMonth() + 1, 27 | currentDate.getDate() 28 | ]; 29 | 30 | // 计算本周日期 31 | const currentDayOfWeek = (currentDate.getDay() + 6) % 7; 32 | const weekStart = new Date(currentDate); 33 | weekStart.setDate(currentDate.getDate() - currentDayOfWeek); 34 | const weekEnd = new Date(currentDate); 35 | weekEnd.setDate(currentDate.getDate() + (6 - currentDayOfWeek)); 36 | 37 | // 修改日期格式化函数 38 | const formatDateStr = (date) => { 39 | const y = date.getFullYear(); 40 | const m = String(date.getMonth() + 1).padStart(2, '0'); 41 | const d = String(date.getDate()).padStart(2, '0'); 42 | return `${y}-${m}-${d}`; 43 | }; 44 | 45 | // 简化周数计算 46 | const getWeekInfo = (date) => { 47 | const firstDayOfYear = new Date(date.getFullYear(), 0, 1); 48 | const pastDaysOfYear = (date - firstDayOfYear) / 86400000; 49 | return { 50 | week: 2 + Math.floor((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7), 51 | year: date.getFullYear() 52 | }; 53 | }; 54 | 55 | // 获取 ISO 周数 56 | const getISOWeek = (date) => { 57 | const target = new Date(date.valueOf()); 58 | const dayNr = (date.getDay() + 6) % 7; 59 | target.setDate(target.getDate() - dayNr + 3); 60 | const jan4 = new Date(target.getFullYear(), 0, 4); 61 | const dayDiff = (target - jan4) / 86400000; 62 | return 2 + Math.floor(dayDiff / 7); 63 | }; 64 | 65 | // 格式化标题 66 | const weekNumber = getISOWeek(currentDate); 67 | const formattedWeek = `${year} W${weekNumber}`; 68 | 69 | // 修改容器类名 70 | let calendarHTML = `
`; 71 | calendarHTML += `
${formattedWeek}
`; 72 | calendarHTML += `
`; 73 | calendarHTML += `
`; 74 | 75 | // 添加星期标题 76 | ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"].forEach(day => { 77 | calendarHTML += `
${day}
`; 78 | }); 79 | 80 | // 填充日期 81 | for (let i = 0; i < 7; i++) { 82 | const date = new Date(weekStart); 83 | date.setDate(weekStart.getDate() + i); 84 | const dateStr = formatDateStr(date); 85 | const page = dv.page(dateStr); 86 | 87 | let cellClasses = ["date-cell"]; 88 | if (date.toDateString() === currentDate.toDateString()) { 89 | cellClasses.push("date-cell-current"); 90 | } else if (page) { 91 | cellClasses.push("date-cell-has-note"); 92 | } 93 | 94 | calendarHTML += `
${date.getDate()}
`; 95 | } 96 | 97 | calendarHTML += `
`; // 结束 calendar-grid 98 | 99 | // 将引用区块移到日历网格外部 100 | //calendarHTML += `
`; 101 | calendarHTML += `
102 |
103 | ${dv.current().quote ? 104 | String(dv.current().quote) 105 | .replace(/^"+|"+$/g, '') 106 | .replace(/(\[\[.*?\]\])|(.*\.md\|?)/g, (match) => { 107 | const parts = match.split('|'); 108 | return parts.length > 1 ? parts.pop() : parts[0]; 109 | }) 110 | .replace(/\]\]/g, '') : 111 | '浊水变澄清,全在自流中。————种田山头火'} 112 |
`; 113 | 114 | calendarHTML += `
`; // 结束 calendar-container 115 | 116 | dv.paragraph(calendarHTML); 117 | 118 | -------------------------------------------------------------------------------- /bujo_calender_views V1.0/markdown文件/月视图.md: -------------------------------------------------------------------------------- 1 | --- 2 | created: 2025-02-28T10:28:00 3 | updated: 2025-03-01T12:47 4 | cssclasses: 5 | - month-view 6 | - calendar-views 7 | --- 8 | ```dataviewjs 9 | // 移除内联样式定义 10 | const mainContainerStyle = ``; 11 | const containerStyle = ``; 12 | 13 | // 初始化容器 14 | let combinedHTML = '
'; 15 | 16 | // 获取当前年份和月份 17 | const currentYear = new Date().getFullYear(); 18 | const currentMonth = new Date(dv.current().created).getMonth()+1; 19 | 20 | // 年度概览部分 21 | let yearCalendarHTML = ` 22 |
23 |
24 | ${currentYear} 25 | 正确的开始,微小的长进,然后持续。 26 |
27 |
`; 28 | 29 | // 生成月份按钮 30 | for (let month = 1; month <= 12; month++) { 31 | const monthStart = dv.date(`${currentYear}-${String(month).padStart(2,'0')}-01`); 32 | const monthName = monthStart.toFormat('MMM'); 33 | const hasMonthlyNote = dv.page(`Monthly/${currentYear}-${String(month).padStart(2,'0')}`); 34 | 35 | const classes = [ 36 | 'month-button', 37 | month === currentMonth ? 'current' : '', 38 | hasMonthlyNote ? 'has-note' : '' 39 | ].filter(Boolean).join(' '); 40 | 41 | yearCalendarHTML += ` 42 |
44 | ${monthName} 45 |
`; 46 | } 47 | 48 | yearCalendarHTML += '
'; 49 | 50 | // 创建标题和网格布局 51 | let habitHTML = ` 52 |
53 |
54 | `; 55 | // 确保日期框架稳定生成 56 | const buildCalendarGrid = () => { 57 | // 获取当前月份(动态) 58 | const currentDate = dv.date(dv.current().created); 59 | const monthStart = currentDate.startOf('month'); 60 | const daysInMonth = currentDate.daysInMonth; 61 | 62 | // 核心框架生成 63 | let habitHTML = ` 64 |
78 |
79 | `; 80 | 81 | // 生成日期列(强制生成空框架) 82 | Array.from({length: daysInMonth}).forEach((_, index) => { 83 | habitHTML += `
${index +1}
`; 84 | }); 85 | habitHTML += `
`; //统计列 86 | 87 | // 定义习惯列表 88 | const habits = ['🧘 拉伸', '📓 日记', '💻 笔记', '🏃 慢跑', '📖 阅读', '🤔 Stoic', '💢 心情']; 89 | 90 | // 获取数据(带空值保护) 91 | const safeGetPages = () => { 92 | try { 93 | return dv.pages('"0_Bullet Journal/Daily Notes"') 94 | .where(p => p?.file?.name?.startsWith(monthStart.toFormat('yyyy-MM'))) 95 | .sort(p => p.file.name, 'asc') || []; 96 | } catch { 97 | return []; 98 | } 99 | }; 100 | const pages = safeGetPages(); 101 | 102 | const moodEmojiMap = { 103 | [-5]: '😱', [-4]: '😖', [-3]: '😟', [-2]: '😔', [-1]: '😕', 104 | 0: '😐', 1: '🙂', 2: '😊', 3: '😄', 4: '🥰', 5: '😇' 105 | }; 106 | 107 | // 简化习惯状态获取逻辑 108 | const getHabitState = (page, habit) => { 109 | const stateMap = { 110 | '🧘 拉伸': 'stretch', 111 | '📓 日记': 'journal', 112 | '💻 笔记': 'notes', 113 | '🏃 慢跑': 'running', 114 | '📖 阅读': 'reading', 115 | '🤔 Stoic': 'stoic', 116 | '💢 心情': 'mood' 117 | }; 118 | return page?.[stateMap[habit]] ?? null; 119 | }; 120 | 121 | // 构建习惯行 122 | habits.forEach(habit => { 123 | let habitCount = 0; 124 | let moodSum = 0; 125 | let moodCount = 0; 126 | 127 | habitHTML += `
${habit}
`; 128 | 129 | for (let day = 1; day <= daysInMonth; day++) { 130 | const currentDate = monthStart.plus({days: day-1}); 131 | const page = pages.find(p => 132 | dv.date(p?.file?.name?.split(" ")[0])?.toISODate() === currentDate.toISODate() 133 | ) || {}; 134 | 135 | // 安全获取习惯状态 136 | const habitState = getHabitState(page, habit); 137 | 138 | // 更新统计值部分 139 | if (habit === '💢 心情') { 140 | if (habitState !== null) { 141 | moodSum += habitState; 142 | moodCount++; 143 | } 144 | } else if (habit === '🤔 Stoic') { 145 | if (page.file && habitState && habitState.trim() !== '') { 146 | habitCount++; 147 | } 148 | } else { 149 | if (page.file && habitState === true) { 150 | habitCount++; 151 | } 152 | } 153 | 154 | // 在渲染单元格时使用简化的逻辑 155 | const getCellStyle = (habit, state) => ` 156 | width: 1.5rem; 157 | height: 1.5rem; 158 | background-color: ${habit === '💢 心情' 159 | ? 'var(--background-modifier-hover)' 160 | : (state ? 'var(--interactive-accent)' : 'var(--background-modifier-hover)')}; 161 | border: 1.5px dotted ${habit === '💢 心情' 162 | ? 'var(--interactive-accent)' 163 | : (state ? 'transparent' : 'var(--interactive-accent)')}; 164 | `; 165 | 166 | // 渲染单元格(强制最小显示) 167 | habitHTML += ` 168 |
169 | 174 | ${habit === '💢 心情' ? (moodEmojiMap[habitState] || '') : (habitState ? '✓' : '')} 175 | 176 |
`; 177 | } 178 | 179 | const statValue = habit === '💢 心情' 180 | ? moodCount > 0 181 | ? moodEmojiMap[Math.round(moodSum / moodCount)] 182 | : '-' 183 | : `${Math.round((habitCount / daysInMonth) * 100)}%`; // 所有习惯都使用整月天数 184 | 185 | habitHTML += ` 186 |
198 | ${statValue} 199 |
200 | `; 201 | }); 202 | 203 | return habitHTML + '
'; 204 | } 205 | 206 | // 最终输出 207 | //dv.paragraph(buildCalendarGrid()); 208 | // 闭合容器 209 | habitHTML += `
`; 210 | 211 | // 组合输出 212 | combinedHTML += yearCalendarHTML; 213 | combinedHTML += buildCalendarGrid(); 214 | combinedHTML += '
'; 215 | 216 | // 最终渲染 217 | dv.paragraph(combinedHTML); 218 | ``` -------------------------------------------------------------------------------- /bujo_calender_views V1.0/markdown文件/视图合集.md: -------------------------------------------------------------------------------- 1 | --- 2 | created: 2025-02-28T09:16:00 3 | updated: 2025-03-01T13:14 4 | --- 5 | ### 日视图 6 | ```dataviewjs 7 | // 获取当前页面的日期(支持文件名和created属性) 8 | const getPageDate = () => { 9 | // 尝试从文件名获取日期 10 | const fileName = dv.current().file.name.split(" ")[0]; 11 | const dateMatch = fileName.match(/^\d{4}-\d{2}-\d{2}$/); 12 | 13 | if (dateMatch) { 14 | const [year, month, day] = fileName.split("-").map(Number); 15 | return new Date(year, month - 1, day); 16 | } 17 | 18 | // 如果文件名不是日期格式,使用created属性 19 | return new Date(dv.current().created); 20 | }; 21 | 22 | const currentDate = getPageDate(); 23 | const [year, month, day] = [ 24 | currentDate.getFullYear(), 25 | currentDate.getMonth() + 1, 26 | currentDate.getDate() 27 | ]; 28 | 29 | // 计算本周日期 30 | const currentDayOfWeek = (currentDate.getDay() + 6) % 7; 31 | const weekStart = new Date(currentDate); 32 | weekStart.setDate(currentDate.getDate() - currentDayOfWeek); 33 | const weekEnd = new Date(currentDate); 34 | weekEnd.setDate(currentDate.getDate() + (6 - currentDayOfWeek)); 35 | 36 | // 修改日期格式化函数 37 | const formatDateStr = (date) => { 38 | const y = date.getFullYear(); 39 | const m = String(date.getMonth() + 1).padStart(2, '0'); 40 | const d = String(date.getDate()).padStart(2, '0'); 41 | return `${y}-${m}-${d}`; 42 | }; 43 | 44 | // 简化周数计算 45 | const getWeekInfo = (date) => { 46 | const firstDayOfYear = new Date(date.getFullYear(), 0, 1); 47 | const pastDaysOfYear = (date - firstDayOfYear) / 86400000; 48 | return { 49 | week: 2 + Math.floor((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7), 50 | year: date.getFullYear() 51 | }; 52 | }; 53 | 54 | // 获取 ISO 周数 55 | const getISOWeek = (date) => { 56 | const target = new Date(date.valueOf()); 57 | const dayNr = (date.getDay() + 6) % 7; 58 | target.setDate(target.getDate() - dayNr + 3); 59 | const jan4 = new Date(target.getFullYear(), 0, 4); 60 | const dayDiff = (target - jan4) / 86400000; 61 | return 2 + Math.floor(dayDiff / 7); 62 | }; 63 | 64 | // 格式化标题 65 | const weekNumber = getISOWeek(currentDate); 66 | const formattedWeek = `${year} W${weekNumber}`; 67 | 68 | // 修改容器类名 69 | let calendarHTML = `
`; 70 | calendarHTML += `
${formattedWeek}
`; 71 | calendarHTML += `
`; 72 | calendarHTML += `
`; 73 | 74 | // 添加星期标题 75 | ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"].forEach(day => { 76 | calendarHTML += `
${day}
`; 77 | }); 78 | 79 | // 填充日期 80 | for (let i = 0; i < 7; i++) { 81 | const date = new Date(weekStart); 82 | date.setDate(weekStart.getDate() + i); 83 | const dateStr = formatDateStr(date); 84 | const page = dv.page(dateStr); 85 | 86 | let cellClasses = ["date-cell"]; 87 | if (date.toDateString() === currentDate.toDateString()) { 88 | cellClasses.push("date-cell-current"); 89 | } else if (page) { 90 | cellClasses.push("date-cell-has-note"); 91 | } 92 | 93 | calendarHTML += `
${date.getDate()}
`; 94 | } 95 | 96 | calendarHTML += `
`; // 结束 calendar-grid 97 | 98 | // 将引用区块移到日历网格外部 99 | //calendarHTML += `
`; 100 | calendarHTML += `
101 |
102 | ${dv.current().quote ? 103 | String(dv.current().quote) 104 | .replace(/^"+|"+$/g, '') 105 | .replace(/(\[\[.*?\]\])|(.*\.md\|?)/g, (match) => { 106 | const parts = match.split('|'); 107 | return parts.length > 1 ? parts.pop() : parts[0]; 108 | }) 109 | .replace(/\]\]/g, '') : 110 | '浊水变澄清,全在自流中。————种田山头火'} 111 |
`; 112 | 113 | calendarHTML += `
`; // 结束 calendar-container 114 | 115 | dv.paragraph(calendarHTML); 116 | 117 | ``` 118 | 119 | ### 周视图 120 | ```dataviewjs 121 | // 日期相关函数 122 | function formatDate(date) { 123 | return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`; 124 | } 125 | 126 | function getWeekRange(baseDate) { 127 | const dayOfWeek = (baseDate.getDay() + 6) % 7; 128 | const startDate = new Date(baseDate); 129 | startDate.setDate(baseDate.getDate() - dayOfWeek); 130 | const endDate = new Date(startDate); 131 | endDate.setDate(startDate.getDate() + 6); 132 | return { startDate, endDate }; 133 | } 134 | 135 | // 优化日期处理函数 136 | function getDateInfo(date) { 137 | return { 138 | year: date.getFullYear(), 139 | month: date.getMonth(), 140 | day: date.getDate(), 141 | weekday: (date.getDay() + 6) % 7 142 | }; 143 | } 144 | 145 | // 简化页面查询逻辑 146 | function getPageForDate(dateStr) { 147 | return dv.pages('"0_Bullet Journal/Daily Notes"') 148 | .where(p => p.file.name.startsWith(dateStr)) 149 | .first(); 150 | } 151 | 152 | // 修改容器类名,使用共享样式 153 | let combinedHTML = '
'; 154 | 155 | try { 156 | const baseDate = dv.current().created ? new Date(dv.current().created) : new Date(); 157 | const { startDate, endDate } = getWeekRange(baseDate); 158 | const currentYear = startDate.getFullYear(); 159 | const currentMonth = startDate.getMonth(); 160 | const currentWeek = Math.ceil((baseDate - new Date(baseDate.getFullYear(), 0, 1)) / (1000 * 60 * 60 * 24 * 7)); 161 | 162 | // 生成月历HTML 163 | combinedHTML += ` 164 |
165 |
166 | ${currentYear} ${["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][currentMonth]} W${currentWeek} 167 |
168 |
169 | ${generateWeekGrid(startDate, endDate)} 170 |
171 |
172 | `; 173 | 174 | // 生成习惯跟踪器HTML 175 | combinedHTML += ` 176 |
177 | ${generateHabitTracker(startDate, endDate)} 178 |
179 | `; 180 | 181 | } catch (e) { 182 | console.error("Error in week view:", e); 183 | combinedHTML += '
Error generating week view
'; 184 | } 185 | 186 | combinedHTML += '
'; 187 | dv.paragraph(combinedHTML); 188 | 189 | // 辅助函数 190 | function generateWeekGrid(startDate, endDate) { 191 | const dateInfo = getDateInfo(startDate); 192 | let gridHTML = ''; 193 | 194 | // 添加星期标题 195 | ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"].forEach(day => { 196 | gridHTML += `
${day}
`; 197 | }); 198 | 199 | // 重置时间部分,只比较日期 200 | startDate.setHours(0, 0, 0, 0); 201 | endDate.setHours(0, 0, 0, 0); 202 | 203 | // 获取当前月的信息 204 | const currentYear = startDate.getFullYear(); 205 | const currentMonth = startDate.getMonth(); 206 | const firstDay = new Date(currentYear, currentMonth, 1); 207 | const lastDay = new Date(currentYear, currentMonth + 1, 0); 208 | const firstDayWeek = (firstDay.getDay() + 6) % 7; 209 | const prevMonthDays = new Date(currentYear, currentMonth, 0).getDate(); 210 | 211 | // 添加上月末尾日期 212 | for (let i = 0; i < firstDayWeek; i++) { 213 | const date = new Date(currentYear, currentMonth - 1, prevMonthDays - firstDayWeek + i + 1); 214 | const isCurrentWeek = date >= startDate && date <= endDate; 215 | 216 | gridHTML += ` 217 |
218 | ${prevMonthDays - firstDayWeek + i + 1} 219 |
`; 220 | } 221 | 222 | // 添加本月日期 223 | for (let i = 1; i <= lastDay.getDate(); i++) { 224 | const currentDate = new Date(currentYear, currentMonth, i); 225 | currentDate.setHours(0, 0, 0, 0); 226 | const dateStr = formatDate(currentDate); 227 | const weekDay = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'][currentDate.getDay()]; 228 | // 修改:查找方式改为精确匹配日期前缀 229 | const pages = dv.pages() 230 | .where(p => p.file.name.startsWith(dateStr)) 231 | .values; 232 | const page = pages.length > 0; 233 | 234 | const isCurrentWeek = currentDate >= startDate && currentDate <= endDate; 235 | 236 | gridHTML += ` 237 |
238 | ${i} 239 |
`; 240 | } 241 | 242 | // 添加下月开始日期 243 | const lastDayWeek = (lastDay.getDay() + 6) % 7; 244 | for (let i = 1; i <= 6 - lastDayWeek; i++) { 245 | const nextDate = new Date(currentYear, currentMonth + 1, i); 246 | nextDate.setHours(0, 0, 0, 0); 247 | const dateStr = formatDate(nextDate); 248 | // 修改:对下月日期也使用相同的查找逻辑 249 | const pages = dv.pages() 250 | .where(p => p.file.name.startsWith(dateStr)) 251 | .values; 252 | const page = pages.length > 0; 253 | 254 | const isCurrentWeek = nextDate >= startDate && nextDate <= endDate; 255 | 256 | gridHTML += ` 257 |
258 | ${i} 259 |
`; 260 | } 261 | 262 | return gridHTML; 263 | } 264 | 265 | function generateHabitTracker(startDate, endDate) { 266 | const habits = ['🧘 拉伸', '📓 日记', '💻 笔记', '🏃 慢跑', '📖 阅读', '🤔 Stoic', '💢 心情']; 267 | const moodEmojiMap = { 268 | '-5': '😱', '-4': '😖', '-3': '😟', '-2': '😔', '-1': '😕', 269 | '0': '😐', '1': '🙂', '2': '😊', '3': '😄', '4': '🥰', '5': '😇' 270 | }; 271 | 272 | let trackerHTML = `
273 |
274 |
275 | ${["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"].map(day => 276 | `
${day}
` 277 | ).join('')}`; 278 | 279 | habits.forEach(habit => { 280 | trackerHTML += `
${habit}
`; 281 | 282 | for (let i = 0; i < 7; i++) { 283 | const currentDate = new Date(startDate); 284 | currentDate.setDate(startDate.getDate() + i); 285 | const dateStr = formatDate(currentDate); 286 | const page = getPageForDate(dateStr); 287 | 288 | const status = page ? { 289 | '🧘 拉伸': page.stretch || false, 290 | '📓 日记': page.journal || false, 291 | '💻 笔记': page.notes || false, 292 | '🏃 慢跑': page.running || false, 293 | '📖 阅读': page.reading || false, 294 | '🤔 Stoic': page.stoic || "", 295 | '💢 心情': page.mood ?? null 296 | }[habit] : null; 297 | 298 | const isMood = habit === '💢 心情'; 299 | 300 | trackerHTML += ` 301 |
302 | 306 | ${isMood ? (status !== null ? moodEmojiMap[status] : '') : (status ? '✓' : '')} 307 | 308 |
`; 309 | } 310 | }); 311 | 312 | trackerHTML += '
'; 313 | return trackerHTML; 314 | } 315 | ``` 316 | 317 | 318 | ### 月视图 319 | 320 | ```dataviewjs 321 | // 移除内联样式定义 322 | const mainContainerStyle = ``; 323 | const containerStyle = ``; 324 | 325 | // 初始化容器 326 | let combinedHTML = '
'; 327 | 328 | // 获取当前年份和月份 329 | const currentYear = new Date().getFullYear(); 330 | const currentMonth = new Date(dv.current().created).getMonth()+1; 331 | 332 | // 年度概览部分 333 | let yearCalendarHTML = ` 334 |
335 |
336 | ${currentYear} 337 | 正确的开始,微小的长进,然后持续。 338 |
339 |
`; 340 | 341 | // 生成月份按钮 342 | for (let month = 1; month <= 12; month++) { 343 | const monthStart = dv.date(`${currentYear}-${String(month).padStart(2,'0')}-01`); 344 | const monthName = monthStart.toFormat('MMM'); 345 | const hasMonthlyNote = dv.page(`Monthly/${currentYear}-${String(month).padStart(2,'0')}`); 346 | 347 | const classes = [ 348 | 'month-button', 349 | month === currentMonth ? 'current' : '', 350 | hasMonthlyNote ? 'has-note' : '' 351 | ].filter(Boolean).join(' '); 352 | 353 | yearCalendarHTML += ` 354 |
356 | ${monthName} 357 |
`; 358 | } 359 | 360 | yearCalendarHTML += '
'; 361 | 362 | // 创建标题和网格布局 363 | let habitHTML = ` 364 |
365 |
366 | `; 367 | // 确保日期框架稳定生成 368 | const buildCalendarGrid = () => { 369 | // 获取当前月份(动态) 370 | const currentDate = dv.date(dv.current().created); 371 | const monthStart = currentDate.startOf('month'); 372 | const daysInMonth = currentDate.daysInMonth; 373 | 374 | // 核心框架生成 375 | let habitHTML = ` 376 |
390 |
391 | `; 392 | 393 | // 生成日期列(强制生成空框架) 394 | Array.from({length: daysInMonth}).forEach((_, index) => { 395 | habitHTML += `
${index +1}
`; 396 | }); 397 | habitHTML += `
`; //统计列 398 | 399 | // 定义习惯列表 400 | const habits = ['🧘 拉伸', '📓 日记', '💻 笔记', '🏃 慢跑', '📖 阅读', '🤔 Stoic', '💢 心情']; 401 | 402 | // 获取数据(带空值保护) 403 | const safeGetPages = () => { 404 | try { 405 | return dv.pages('"0_Bullet Journal/Daily Notes"') 406 | .where(p => p?.file?.name?.startsWith(monthStart.toFormat('yyyy-MM'))) 407 | .sort(p => p.file.name, 'asc') || []; 408 | } catch { 409 | return []; 410 | } 411 | }; 412 | const pages = safeGetPages(); 413 | 414 | const moodEmojiMap = { 415 | [-5]: '😱', [-4]: '😖', [-3]: '😟', [-2]: '😔', [-1]: '😕', 416 | 0: '😐', 1: '🙂', 2: '😊', 3: '😄', 4: '🥰', 5: '😇' 417 | }; 418 | 419 | // 简化习惯状态获取逻辑 420 | const getHabitState = (page, habit) => { 421 | const stateMap = { 422 | '🧘 拉伸': 'stretch', 423 | '📓 日记': 'journal', 424 | '💻 笔记': 'notes', 425 | '🏃 慢跑': 'running', 426 | '📖 阅读': 'reading', 427 | '🤔 Stoic': 'stoic', 428 | '💢 心情': 'mood' 429 | }; 430 | return page?.[stateMap[habit]] ?? null; 431 | }; 432 | 433 | // 构建习惯行 434 | habits.forEach(habit => { 435 | let habitCount = 0; 436 | let moodSum = 0; 437 | let moodCount = 0; 438 | 439 | habitHTML += `
${habit}
`; 440 | 441 | for (let day = 1; day <= daysInMonth; day++) { 442 | const currentDate = monthStart.plus({days: day-1}); 443 | const page = pages.find(p => 444 | dv.date(p?.file?.name?.split(" ")[0])?.toISODate() === currentDate.toISODate() 445 | ) || {}; 446 | 447 | // 安全获取习惯状态 448 | const habitState = getHabitState(page, habit); 449 | 450 | // 更新统计值部分 451 | if (habit === '💢 心情') { 452 | if (habitState !== null) { 453 | moodSum += habitState; 454 | moodCount++; 455 | } 456 | } else if (habit === '🤔 Stoic') { 457 | if (page.file && habitState && habitState.trim() !== '') { 458 | habitCount++; 459 | } 460 | } else { 461 | if (page.file && habitState === true) { 462 | habitCount++; 463 | } 464 | } 465 | 466 | // 在渲染单元格时使用简化的逻辑 467 | const getCellStyle = (habit, state) => ` 468 | width: 1.5rem; 469 | height: 1.5rem; 470 | background-color: ${habit === '💢 心情' 471 | ? 'var(--background-modifier-hover)' 472 | : (state ? 'var(--interactive-accent)' : 'var(--background-modifier-hover)')}; 473 | border: 1.5px dotted ${habit === '💢 心情' 474 | ? 'var(--interactive-accent)' 475 | : (state ? 'transparent' : 'var(--interactive-accent)')}; 476 | `; 477 | 478 | // 渲染单元格(强制最小显示) 479 | habitHTML += ` 480 |
481 | 486 | ${habit === '💢 心情' ? (moodEmojiMap[habitState] || '') : (habitState ? '✓' : '')} 487 | 488 |
`; 489 | } 490 | 491 | const statValue = habit === '💢 心情' 492 | ? moodCount > 0 493 | ? moodEmojiMap[Math.round(moodSum / moodCount)] 494 | : '-' 495 | : `${Math.round((habitCount / daysInMonth) * 100)}%`; // 所有习惯都使用整月天数 496 | 497 | habitHTML += ` 498 |
510 | ${statValue} 511 |
512 | `; 513 | }); 514 | 515 | return habitHTML + '
'; 516 | } 517 | 518 | // 最终输出 519 | //dv.paragraph(buildCalendarGrid()); 520 | // 闭合容器 521 | habitHTML += `
`; 522 | 523 | // 组合输出 524 | combinedHTML += yearCalendarHTML; 525 | combinedHTML += buildCalendarGrid(); 526 | combinedHTML += '
'; 527 | 528 | // 最终渲染 529 | dv.paragraph(combinedHTML); 530 | ``` 531 | 532 | -------------------------------------------------------------------------------- /bujo_calender_views V1.0/使用指南.md: -------------------------------------------------------------------------------- 1 | --- 2 | created: 2025-03-01T12:23 3 | updated: 2025-03-01T12:36 4 | --- 5 | # 个人知识库视图系统使用指南 📖 6 | 7 | ## 1. 系统概述 8 | 9 | 这是一套为 Obsidian 设计的视图系统,包含日视图、周视图和月视图三个层级,帮助你更好地管理和展示个人知识与习惯追踪数据。 10 | 11 | ## 2. 安装步骤 12 | 13 | 1. 将 `calendar.css` 文件复制到 `.obsidian/snippets` 目录下 14 | 2. 在 Obsidian 设置中启用该 CSS 片段: 15 | - 打开设置 (Settings) 16 | - 进入外观 (Appearance) 17 | - 找到 CSS 片段 (CSS snippets) 18 | - 刷新并启用 `calendar.css` 19 | 3. 复制 `视图v1.0.md` 文件到你的知识库中 20 | 21 | ## 3. 视图说明 22 | 23 | ### 3.1 日视图功能 24 | - 显示当前周的日历 25 | - 高亮显示当前日期 26 | - 显示每日格言(需在日记中添加 `quote` 字段) 27 | - 标记已创建日记的日期 28 | 29 | ### 3.2 周视图功能 30 | - 显示当月日历,高亮当前周 31 | - 习惯追踪器,包含以下项目: 32 | - 🧘 拉伸 33 | - 📓 日记 34 | - 💻 笔记 35 | - 🏃 慢跑 36 | - 📖 阅读 37 | - 🤔 Stoic 38 | - 💢 心情 39 | 40 | ### 3.3 月视图功能 41 | - 年度概览(显示所有月份) 42 | - 当月习惯详细追踪 43 | - 自动计算习惯完成率 44 | - 心情平均值统计 45 | 46 | ## 4. 使用方法 47 | 48 | ### 4.1 创建日记文件 49 | - 文件名格式:`YYYY-MM-DD` 50 | - 需包含以下 YAML frontmatter: 51 | ```yaml 52 | --- 53 | quote: "每日格言" # 可选 54 | stretch: true/false # 拉伸记录 55 | journal: true/false # 日记记录 56 | notes: true/false # 笔记记录 57 | running: true/false # 慢跑记录 58 | reading: true/false # 阅读记录 59 | stoic: "内容" # Stoic 思考 60 | mood: -5到5的数值 # 心情值 61 | --- 62 | ``` 63 | 64 | ### 4.2 心情值对应表 65 | ``` 66 | -5: 😱 极度焦虑 67 | -4: 😖 非常难过 68 | -3: 😟 难过 69 | -2: 😔 有点难过 70 | -1: 😕 不太好 71 | 0: 😐 一般 72 | 1: 🙂 还行 73 | 2: 😊 不错 74 | 3: 😄 开心 75 | 4: 🥰 很开心 76 | 5: 😇 极度开心 77 | ``` 78 | 79 | ## 5. 注意事项 80 | 81 | 1. 确保你的 Obsidian 版本支持 DataviewJS 82 | 2. 日记文件需要按照指定格式命名和存储 83 | 3. YAML frontmatter 中的字段名称需要完全匹配 84 | 4. 心情值必须在 -5 到 5 的范围内 85 | 86 | ## 6. 自定义建议 87 | 88 | 1. 可以根据需要修改 `calendar.css` 中的颜色和样式 89 | 2. 可以在习惯追踪器中添加或删除项目 90 | 3. 可以调整视图的显示效果以适应个人偏好 91 | 92 | ## 7. 问题排查 93 | 94 | 如果视图显示不正常,请检查: 95 | 1. CSS 片段是否正确启用 96 | 2. 日记文件格式是否正确 97 | 3. YAML frontmatter 是否包含所需字段 98 | 4. Dataview 插件是否正常运行 99 | 100 | ## 8. 系统优化说明 101 | 102 | ### 8.1 整体架构优化 103 | - CSS样式完全分离:所有样式都统一在 `calendar.css` 中管理 104 | - 统一容器类名:使用 `calendar-container` 实现样式共享 105 | - 代码结构优化:提高可维护性和扩展性 106 | - 统一设计语言:保持视觉风格一致性 107 | 108 | ### 8.2 日视图优化 109 | - 改进日期获取逻辑:支持从文件名和created属性获取日期 110 | - 优化周数计算:采用 ISO 周数标准 111 | - 改进引用区块:优化显示位置和样式 112 | - 增强日期单元格:提供更好的视觉反馈 113 | 114 | ### 8.3 周视图优化 115 | - 改进日期范围计算 116 | - 优化习惯追踪器显示 117 | - 提升页面查询性能 118 | - 添加错误处理机制 119 | - 支持跨月份周视图 120 | 121 | ### 8.4 月视图优化 122 | - 添加年度概览功能 123 | - 改进月份按钮交互 124 | - 优化习惯统计算法 125 | - 改进数据可视化展示 126 | - 增加月度数据分析 127 | 128 | ### 8.5 功能增强 129 | - 完善心情追踪系统 130 | - 使用表情符号直观显示 131 | - 优化统计算法 132 | - 添加习惯完成率统计 133 | - 优化 Stoic 思考记录 134 | - 增加数据验证机制 135 | 136 | ### 8.6 性能优化 137 | - 优化数据查询逻辑 138 | - 减少DOM操作 139 | - 改进日期计算方法 140 | - 添加空值保护机制 141 | 142 | ### 8.7 用户体验优化 143 | - 统一视觉设计 144 | - 改进响应式布局 145 | - 优化移动端适配 146 | - 增强交互反馈 147 | 148 | ## 9. 更新日志 149 | 150 | 当前版本:v1.1 151 | - 优化系统架构 152 | - 改进性能表现 153 | - 增强用户体验 154 | - 完善文档说明 155 | -------------------------------------------------------------------------------- /bujo_calender_views V1.0/使用指南.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YuriWg/Obsidian_Templates/1cbeab148121cfbd320b2fdf08ae89b527e23e1f/bujo_calender_views V1.0/使用指南.pdf -------------------------------------------------------------------------------- /bujo_calender_views V1.0/视图blue_dark_v1.0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YuriWg/Obsidian_Templates/1cbeab148121cfbd320b2fdf08ae89b527e23e1f/bujo_calender_views V1.0/视图blue_dark_v1.0.png -------------------------------------------------------------------------------- /bujo_calender_views V1.0/视图blue_light_v1.0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YuriWg/Obsidian_Templates/1cbeab148121cfbd320b2fdf08ae89b527e23e1f/bujo_calender_views V1.0/视图blue_light_v1.0.png -------------------------------------------------------------------------------- /bujo_calender_views V1.0/视图green_dark_v1.0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YuriWg/Obsidian_Templates/1cbeab148121cfbd320b2fdf08ae89b527e23e1f/bujo_calender_views V1.0/视图green_dark_v1.0.png -------------------------------------------------------------------------------- /bujo_calender_views V1.0/视图green_lignt_v1.0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/YuriWg/Obsidian_Templates/1cbeab148121cfbd320b2fdf08ae89b527e23e1f/bujo_calender_views V1.0/视图green_lignt_v1.0.png --------------------------------------------------------------------------------