时间管理示例库源码
模板插件好像用不着,好用没做过示例库有点生疏。
第一步:
下载 obsidian 并在插件市场下载下载如下插件:
- QuickAdd
- Calendar
- DataView
并打开核心插件--日记和模板。
第二步:
先配置日记和模板插件,再配置 Calendar 插件,最后配置QuickAdd 插件。
---
ONE: 1️⃣ 工作学习
ONE目标: 570
LearnTime: 0
TWO: 2️⃣ 运动健身
TWO目标: 90
ExerciseTime: 0
THREE: 3️⃣ 休闲娱乐
THREE目标: 120
PlayTime: 0
FOUR: 4️⃣ 生活杂事
FOUR目标: 330
DailyTime: 0
activateTime: "06:00:00"
currentMode: "DailyTime"
---
```dataview
list without id
"<div style='display: flex; justify-content: space-between; align-items: center; margin-bottom: -5px;'>" +
"<span>📖 " + ONE + " <span style='color: var(--text-muted);'>📈 " + round(LearnTime/ONE目标*100) + "%</span></span>" +
"<span style='display: flex;'>" +
"<span style='width: 100px;'>✅ " + LearnTime + " 分钟</span>" +
"<span style='width: 100px; color: var(--text-muted);'>⏳ " + round(ONE目标 - LearnTime) + " 分钟</span>" +
"</span>" +
"</div>" +
"<progress value='" + LearnTime + "' max='" + ONE目标 + "' style='width: 100%;'></progress>"
+
"<div style='display: flex; justify-content: space-between; align-items: center; margin-bottom: -5px; margin-top: 10px;'>" +
"<span>🏃 " + TWO + " <span style='color: var(--text-muted);'>📈 " + round(ExerciseTime/TWO目标*100) + "%</span></span>" +
"<span style='display: flex;'>" +
"<span style='width: 100px;'>✅ " + ExerciseTime + " 分钟</span>" +
"<span style='width: 100px; color: var(--text-muted);'>⏳ " + round(TWO目标 - ExerciseTime) + " 分钟</span>" +
"</span>" +
"</div>" +
"<progress value='" + ExerciseTime + "' max='" + TWO目标 + "' style='width: 100%;'></progress>"
+
"<div style='display: flex; justify-content: space-between; align-items: center; margin-bottom: -5px; margin-top: 10px;'>" +
"<span>🎮 " + THREE + " <span style='color: var(--text-muted);'>📈 " + round(PlayTime/THREE目标*100) + "%</span></span>" +
"<span style='display: flex;'>" +
"<span style='width: 100px;'>✅ " + PlayTime + " 分钟</span>" +
"<span style='width: 100px; color: var(--text-muted);'>⏳ " + round(THREE目标 - PlayTime) + " 分钟</span>" +
"</span>" +
"</div>" +
"<progress value='" + PlayTime + "' max='" + THREE目标 + "' style='width: 100%;'></progress>"
+
"<div style='display: flex; justify-content: space-between; align-items: center; margin-bottom: -5px; margin-top: 10px;'>" +
"<span>🏠 " + FOUR + " <span style='color: var(--text-muted);'>📈 " + round(DailyTime/FOUR目标*100) + "%</span></span>" +
"<span style='display: flex;'>" +
"<span style='width: 100px;'>✅ " + DailyTime + " 分钟</span>" +
"<span style='width: 100px; color: var(--text-muted);'>⏳ " + round(FOUR目标 - DailyTime) + " 分钟</span>" +
"</span>" +
"</div>" +
"<progress value='" + DailyTime + "' max='" + FOUR目标 + "' style='width: 100%;'></progress>"
where file.path = this.file.path
```
第三步:
QuickAdd 插件配置过程用到的代码
trackDaily¶
module.exports = async (params) => {
const { app } = params;
const file = app.workspace.getActiveFile();
if (!file) {
new Notice("没有检测到当前活动笔记");
return;
}
const LOCK_KEY = "__stateTrackLock__";
if (window[LOCK_KEY]) {
new Notice("有状态脚本正在执行,请稍后再点");
return;
}
window[LOCK_KEY] = true;
const allowedFields = ["LearnTime", "ExerciseTime", "PlayTime", "DailyTime"];
const modeLabelMap = {
LearnTime: "学习",
ExerciseTime: "锻炼",
PlayTime: "娱乐",
DailyTime: "日常",
sleep: "睡眠"
};
const nextMode = "DailyTime";
const nextModeLabel = modeLabelMap[nextMode];
const logTitle = "状态切换日志:";
const now = new Date();
function pad(n) {
return String(n).padStart(2, "0");
}
function formatTime(date) {
return `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
}
function parseActivateTime(timeStr, currentTime) {
if (typeof timeStr !== "string") return null;
const parts = timeStr.split(":").map(Number);
if (parts.length < 2 || parts.some(Number.isNaN)) return null;
const [h = 0, m = 0, s = 0] = parts;
const d = new Date(currentTime);
d.setHours(h, m, s, 0);
if (d.getTime() > currentTime.getTime()) {
d.setDate(d.getDate() - 1);
}
return d;
}
function normalizeNumber(value) {
if (typeof value === "number") return value;
if (typeof value === "string" && value.trim() !== "") {
const n = Number(value);
return Number.isNaN(n) ? 0 : n;
}
return 0;
}
function buildLogLine({ nowTime, previousLabel, addedMinutes, creditedLabel, nextLabel }) {
if (previousLabel === "睡眠") {
return `- ${nowTime}|切换为${nextLabel},上一状态为睡眠`;
}
return `- ${nowTime}|切换为${nextLabel},${creditedLabel}已记录 ${addedMinutes} 分钟~`;
}
async function appendLog(line) {
const content = await app.vault.read(file);
let newContent = content;
if (!content.includes(logTitle)) {
const trimmed = content.replace(/\s*$/, "");
newContent = `${trimmed}\n\n${logTitle}\n\n${line}\n`;
} else {
newContent = content.replace(new RegExp(`${logTitle}\n*`), `${logTitle}\n\n`);
newContent = newContent.replace(/\s*$/, "");
newContent += `\n${line}\n`;
}
await app.vault.modify(file, newContent);
}
let noticeMessage = "";
let logLine = "";
try {
await app.fileManager.processFrontMatter(file, (frontmatter) => {
for (const field of allowedFields) {
if (frontmatter[field] === undefined) {
frontmatter[field] = 0;
}
}
if (
frontmatter.activateTime === undefined ||
frontmatter.activateTime === null ||
String(frontmatter.activateTime).trim() === ""
) {
frontmatter.activateTime = "06:00:00";
}
if (
frontmatter.currentMode === undefined ||
frontmatter.currentMode === null ||
![...allowedFields, "sleep"].includes(String(frontmatter.currentMode))
) {
frontmatter.currentMode = "DailyTime";
}
const previousMode = String(frontmatter.currentMode);
const previousLabel = modeLabelMap[previousMode] || previousMode;
const activateDate = parseActivateTime(String(frontmatter.activateTime).trim(), now);
if (!activateDate) {
throw new Error("activateTime 格式错误,应为 HH:mm:ss 或 HH:mm");
}
let addedMinutes = Math.floor((now.getTime() - activateDate.getTime()) / 60000);
if (addedMinutes < 0) addedMinutes = 0;
let creditedLabel = "无";
if (allowedFields.includes(previousMode)) {
const oldValue = normalizeNumber(frontmatter[previousMode]);
frontmatter[previousMode] = oldValue + addedMinutes;
creditedLabel = modeLabelMap[previousMode];
noticeMessage = `已记录 ${creditedLabel} ${addedMinutes} 分钟,开始${nextModeLabel}`;
} else if (previousMode === "sleep") {
noticeMessage = `已从睡眠切换到${nextModeLabel}`;
} else {
noticeMessage = `检测到异常状态,已切换到${nextModeLabel}`;
}
logLine = buildLogLine({
nowTime: formatTime(now),
previousLabel,
addedMinutes,
creditedLabel,
nextLabel: nextModeLabel
});
frontmatter.currentMode = nextMode;
frontmatter.activateTime = formatTime(now);
});
await appendLog(logLine);
new Notice(noticeMessage);
} catch (error) {
console.error(error);
new Notice(`记录失败:${error.message}`);
} finally {
window[LOCK_KEY] = false;
}
};
trackExercise¶
module.exports = async (params) => {
const { app } = params;
const file = app.workspace.getActiveFile();
if (!file) {
new Notice("没有检测到当前活动笔记");
return;
}
const LOCK_KEY = "__stateTrackLock__";
if (window[LOCK_KEY]) {
new Notice("有状态脚本正在执行,请稍后再点");
return;
}
window[LOCK_KEY] = true;
const allowedFields = ["LearnTime", "ExerciseTime", "PlayTime", "DailyTime"];
const modeLabelMap = {
LearnTime: "学习",
ExerciseTime: "锻炼",
PlayTime: "娱乐",
DailyTime: "日常",
sleep: "睡眠"
};
const nextMode = "ExerciseTime";
const nextModeLabel = modeLabelMap[nextMode];
const logTitle = "状态切换日志:";
const now = new Date();
function pad(n) {
return String(n).padStart(2, "0");
}
function formatTime(date) {
return `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
}
function parseActivateTime(timeStr, currentTime) {
if (typeof timeStr !== "string") return null;
const parts = timeStr.split(":").map(Number);
if (parts.length < 2 || parts.some(Number.isNaN)) return null;
const [h = 0, m = 0, s = 0] = parts;
const d = new Date(currentTime);
d.setHours(h, m, s, 0);
if (d.getTime() > currentTime.getTime()) {
d.setDate(d.getDate() - 1);
}
return d;
}
function normalizeNumber(value) {
if (typeof value === "number") return value;
if (typeof value === "string" && value.trim() !== "") {
const n = Number(value);
return Number.isNaN(n) ? 0 : n;
}
return 0;
}
function buildLogLine({ nowTime, previousLabel, addedMinutes, creditedLabel, nextLabel }) {
if (previousLabel === "睡眠") {
return `- ${nowTime}|切换为${nextLabel},上一状态为睡眠`;
}
return `- ${nowTime}|切换为${nextLabel},${creditedLabel}已记录 ${addedMinutes} 分钟~`;
}
async function appendLog(line) {
const content = await app.vault.read(file);
let newContent = content;
if (!content.includes(logTitle)) {
const trimmed = content.replace(/\s*$/, "");
newContent = `${trimmed}\n\n${logTitle}\n\n${line}\n`;
} else {
newContent = content.replace(new RegExp(`${logTitle}\n*`), `${logTitle}\n\n`);
newContent = newContent.replace(/\s*$/, "");
newContent += `\n${line}\n`;
}
await app.vault.modify(file, newContent);
}
let noticeMessage = "";
let logLine = "";
try {
await app.fileManager.processFrontMatter(file, (frontmatter) => {
for (const field of allowedFields) {
if (frontmatter[field] === undefined) {
frontmatter[field] = 0;
}
}
if (
frontmatter.activateTime === undefined ||
frontmatter.activateTime === null ||
String(frontmatter.activateTime).trim() === ""
) {
frontmatter.activateTime = "06:00:00";
}
if (
frontmatter.currentMode === undefined ||
frontmatter.currentMode === null ||
![...allowedFields, "sleep"].includes(String(frontmatter.currentMode))
) {
frontmatter.currentMode = "DailyTime";
}
const previousMode = String(frontmatter.currentMode);
const previousLabel = modeLabelMap[previousMode] || previousMode;
const activateDate = parseActivateTime(String(frontmatter.activateTime).trim(), now);
if (!activateDate) {
throw new Error("activateTime 格式错误,应为 HH:mm:ss 或 HH:mm");
}
let addedMinutes = Math.floor((now.getTime() - activateDate.getTime()) / 60000);
if (addedMinutes < 0) addedMinutes = 0;
let creditedLabel = "无";
if (allowedFields.includes(previousMode)) {
const oldValue = normalizeNumber(frontmatter[previousMode]);
frontmatter[previousMode] = oldValue + addedMinutes;
creditedLabel = modeLabelMap[previousMode];
noticeMessage = `已记录 ${creditedLabel} ${addedMinutes} 分钟,开始${nextModeLabel}`;
} else if (previousMode === "sleep") {
noticeMessage = `已从睡眠切换到${nextModeLabel}`;
} else {
noticeMessage = `检测到异常状态,已切换到${nextModeLabel}`;
}
logLine = buildLogLine({
nowTime: formatTime(now),
previousLabel,
addedMinutes,
creditedLabel,
nextLabel: nextModeLabel
});
frontmatter.currentMode = nextMode;
frontmatter.activateTime = formatTime(now);
});
await appendLog(logLine);
new Notice(noticeMessage);
} catch (error) {
console.error(error);
new Notice(`记录失败:${error.message}`);
} finally {
window[LOCK_KEY] = false;
}
};
tracklearn¶
module.exports = async (params) => {
const { app } = params;
const file = app.workspace.getActiveFile();
if (!file) {
new Notice("没有检测到当前活动笔记");
return;
}
const LOCK_KEY = "__stateTrackLock__";
if (window[LOCK_KEY]) {
new Notice("有状态脚本正在执行,请稍后再点");
return;
}
window[LOCK_KEY] = true;
const allowedFields = ["LearnTime", "ExerciseTime", "PlayTime", "DailyTime"];
const modeLabelMap = {
LearnTime: "学习",
ExerciseTime: "锻炼",
PlayTime: "娱乐",
DailyTime: "日常",
sleep: "睡眠"
};
const nextMode = "LearnTime";
const nextModeLabel = modeLabelMap[nextMode];
const logTitle = "状态切换日志:";
const now = new Date();
function pad(n) {
return String(n).padStart(2, "0");
}
function formatTime(date) {
return `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
}
function parseActivateTime(timeStr, currentTime) {
if (typeof timeStr !== "string") return null;
const parts = timeStr.split(":").map(Number);
if (parts.length < 2 || parts.some(Number.isNaN)) return null;
const [h = 0, m = 0, s = 0] = parts;
const d = new Date(currentTime);
d.setHours(h, m, s, 0);
if (d.getTime() > currentTime.getTime()) {
d.setDate(d.getDate() - 1);
}
return d;
}
function normalizeNumber(value) {
if (typeof value === "number") return value;
if (typeof value === "string" && value.trim() !== "") {
const n = Number(value);
return Number.isNaN(n) ? 0 : n;
}
return 0;
}
function buildLogLine({ nowTime, previousLabel, addedMinutes, creditedLabel, nextLabel }) {
if (previousLabel === "睡眠") {
return `- ${nowTime}|切换为${nextLabel},上一状态为睡眠`;
}
return `- ${nowTime}|切换为${nextLabel},${creditedLabel}已记录 ${addedMinutes} 分钟~`;
}
async function appendLog(line) {
const content = await app.vault.read(file);
let newContent = content;
if (!content.includes(logTitle)) {
const trimmed = content.replace(/\s*$/, "");
newContent = `${trimmed}\n\n${logTitle}\n\n${line}\n`;
} else {
newContent = content.replace(new RegExp(`${logTitle}\n*`), `${logTitle}\n\n`);
newContent = newContent.replace(/\s*$/, "");
newContent += `\n${line}\n`;
}
await app.vault.modify(file, newContent);
}
let noticeMessage = "";
let logLine = "";
try {
await app.fileManager.processFrontMatter(file, (frontmatter) => {
for (const field of allowedFields) {
if (frontmatter[field] === undefined) {
frontmatter[field] = 0;
}
}
if (
frontmatter.activateTime === undefined ||
frontmatter.activateTime === null ||
String(frontmatter.activateTime).trim() === ""
) {
frontmatter.activateTime = "06:00:00";
}
if (
frontmatter.currentMode === undefined ||
frontmatter.currentMode === null ||
![...allowedFields, "sleep"].includes(String(frontmatter.currentMode))
) {
frontmatter.currentMode = "DailyTime";
}
const previousMode = String(frontmatter.currentMode);
const previousLabel = modeLabelMap[previousMode] || previousMode;
const activateDate = parseActivateTime(String(frontmatter.activateTime).trim(), now);
if (!activateDate) {
throw new Error("activateTime 格式错误,应为 HH:mm:ss 或 HH:mm");
}
let addedMinutes = Math.floor((now.getTime() - activateDate.getTime()) / 60000);
if (addedMinutes < 0) addedMinutes = 0;
let creditedLabel = "无";
if (allowedFields.includes(previousMode)) {
const oldValue = normalizeNumber(frontmatter[previousMode]);
frontmatter[previousMode] = oldValue + addedMinutes;
creditedLabel = modeLabelMap[previousMode];
noticeMessage = `已记录 ${creditedLabel} ${addedMinutes} 分钟,开始${nextModeLabel}`;
} else if (previousMode === "sleep") {
noticeMessage = `已从睡眠切换到${nextModeLabel}`;
} else {
noticeMessage = `检测到异常状态,已切换到${nextModeLabel}`;
}
logLine = buildLogLine({
nowTime: formatTime(now),
previousLabel,
addedMinutes,
creditedLabel,
nextLabel: nextModeLabel
});
frontmatter.currentMode = nextMode;
frontmatter.activateTime = formatTime(now);
});
await appendLog(logLine);
new Notice(noticeMessage);
} catch (error) {
console.error(error);
new Notice(`记录失败:${error.message}`);
} finally {
window[LOCK_KEY] = false;
}
};
trackPlay¶
module.exports = async (params) => {
const { app } = params;
const file = app.workspace.getActiveFile();
if (!file) {
new Notice("没有检测到当前活动笔记");
return;
}
const LOCK_KEY = "__stateTrackLock__";
if (window[LOCK_KEY]) {
new Notice("有状态脚本正在执行,请稍后再点");
return;
}
window[LOCK_KEY] = true;
const allowedFields = ["LearnTime", "ExerciseTime", "PlayTime", "DailyTime"];
const modeLabelMap = {
LearnTime: "学习",
ExerciseTime: "锻炼",
PlayTime: "娱乐",
DailyTime: "日常",
sleep: "睡眠"
};
const nextMode = "PlayTime";
const nextModeLabel = modeLabelMap[nextMode];
const logTitle = "状态切换日志:";
const now = new Date();
function pad(n) {
return String(n).padStart(2, "0");
}
function formatTime(date) {
return `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
}
function parseActivateTime(timeStr, currentTime) {
if (typeof timeStr !== "string") return null;
const parts = timeStr.split(":").map(Number);
if (parts.length < 2 || parts.some(Number.isNaN)) return null;
const [h = 0, m = 0, s = 0] = parts;
const d = new Date(currentTime);
d.setHours(h, m, s, 0);
if (d.getTime() > currentTime.getTime()) {
d.setDate(d.getDate() - 1);
}
return d;
}
function normalizeNumber(value) {
if (typeof value === "number") return value;
if (typeof value === "string" && value.trim() !== "") {
const n = Number(value);
return Number.isNaN(n) ? 0 : n;
}
return 0;
}
function buildLogLine({ nowTime, previousLabel, addedMinutes, creditedLabel, nextLabel }) {
if (previousLabel === "睡眠") {
return `- ${nowTime}|切换为${nextLabel},上一状态为睡眠`;
}
return `- ${nowTime}|切换为${nextLabel},${creditedLabel}已记录 ${addedMinutes} 分钟~`;
}
async function appendLog(line) {
const content = await app.vault.read(file);
let newContent = content;
if (!content.includes(logTitle)) {
const trimmed = content.replace(/\s*$/, "");
newContent = `${trimmed}\n\n${logTitle}\n\n${line}\n`;
} else {
newContent = content.replace(new RegExp(`${logTitle}\n*`), `${logTitle}\n\n`);
newContent = newContent.replace(/\s*$/, "");
newContent += `\n${line}\n`;
}
await app.vault.modify(file, newContent);
}
let noticeMessage = "";
let logLine = "";
try {
await app.fileManager.processFrontMatter(file, (frontmatter) => {
for (const field of allowedFields) {
if (frontmatter[field] === undefined) {
frontmatter[field] = 0;
}
}
if (
frontmatter.activateTime === undefined ||
frontmatter.activateTime === null ||
String(frontmatter.activateTime).trim() === ""
) {
frontmatter.activateTime = "06:00:00";
}
if (
frontmatter.currentMode === undefined ||
frontmatter.currentMode === null ||
![...allowedFields, "sleep"].includes(String(frontmatter.currentMode))
) {
frontmatter.currentMode = "DailyTime";
}
const previousMode = String(frontmatter.currentMode);
const previousLabel = modeLabelMap[previousMode] || previousMode;
const activateDate = parseActivateTime(String(frontmatter.activateTime).trim(), now);
if (!activateDate) {
throw new Error("activateTime 格式错误,应为 HH:mm:ss 或 HH:mm");
}
let addedMinutes = Math.floor((now.getTime() - activateDate.getTime()) / 60000);
if (addedMinutes < 0) addedMinutes = 0;
let creditedLabel = "无";
if (allowedFields.includes(previousMode)) {
const oldValue = normalizeNumber(frontmatter[previousMode]);
frontmatter[previousMode] = oldValue + addedMinutes;
creditedLabel = modeLabelMap[previousMode];
noticeMessage = `已记录 ${creditedLabel} ${addedMinutes} 分钟,开始${nextModeLabel}`;
} else if (previousMode === "sleep") {
noticeMessage = `已从睡眠切换到${nextModeLabel}`;
} else {
noticeMessage = `检测到异常状态,已切换到${nextModeLabel}`;
}
logLine = buildLogLine({
nowTime: formatTime(now),
previousLabel,
addedMinutes,
creditedLabel,
nextLabel: nextModeLabel
});
frontmatter.currentMode = nextMode;
frontmatter.activateTime = formatTime(now);
});
await appendLog(logLine);
new Notice(noticeMessage);
} catch (error) {
console.error(error);
new Notice(`记录失败:${error.message}`);
} finally {
window[LOCK_KEY] = false;
}
};
trackSleep¶
module.exports = async (params) => {
const { app } = params;
const file = app.workspace.getActiveFile();
if (!file) {
new Notice("没有检测到当前活动笔记");
return;
}
const LOCK_KEY = "__stateTrackLock__";
if (window[LOCK_KEY]) {
new Notice("有状态脚本正在执行,请稍后再点");
return;
}
window[LOCK_KEY] = true;
const allowedFields = ["LearnTime", "ExerciseTime", "PlayTime", "DailyTime"];
const modeLabelMap = {
LearnTime: "学习",
ExerciseTime: "锻炼",
PlayTime: "娱乐",
DailyTime: "日常",
sleep: "睡眠"
};
const nextMode = "sleep";
const nextModeLabel = modeLabelMap[nextMode];
const forceCreditMode = "DailyTime";
const logTitle = "状态切换日志:";
const now = new Date();
function pad(n) {
return String(n).padStart(2, "0");
}
function formatTime(date) {
return `${pad(date.getHours())}:${pad(date.getMinutes())}:${pad(date.getSeconds())}`;
}
function parseActivateTime(timeStr, currentTime) {
if (typeof timeStr !== "string") return null;
const parts = timeStr.split(":").map(Number);
if (parts.length < 2 || parts.some(Number.isNaN)) return null;
const [h = 0, m = 0, s = 0] = parts;
const d = new Date(currentTime);
d.setHours(h, m, s, 0);
if (d.getTime() > currentTime.getTime()) {
d.setDate(d.getDate() - 1);
}
return d;
}
function normalizeNumber(value) {
if (typeof value === "number") return value;
if (typeof value === "string" && value.trim() !== "") {
const n = Number(value);
return Number.isNaN(n) ? 0 : n;
}
return 0;
}
function buildLogLine({ nowTime, previousLabel, addedMinutes, creditedLabel, nextLabel }) {
if (previousLabel === "睡眠") {
return `- ${nowTime}|切换为${nextLabel},上一状态为睡眠`;
}
return `- ${nowTime}|切换为${nextLabel},${creditedLabel}已记录 ${addedMinutes} 分钟~`;
}
async function appendLog(line) {
const content = await app.vault.read(file);
let newContent = content;
if (!content.includes(logTitle)) {
const trimmed = content.replace(/\s*$/, "");
newContent = `${trimmed}\n\n${logTitle}\n\n${line}\n`;
} else {
newContent = content.replace(new RegExp(`${logTitle}\n*`), `${logTitle}\n\n`);
newContent = newContent.replace(/\s*$/, "");
newContent += `\n${line}\n`;
}
await app.vault.modify(file, newContent);
}
let noticeMessage = "";
let logLine = "";
try {
await app.fileManager.processFrontMatter(file, (frontmatter) => {
for (const field of allowedFields) {
if (frontmatter[field] === undefined) {
frontmatter[field] = 0;
}
}
if (
frontmatter.activateTime === undefined ||
frontmatter.activateTime === null ||
String(frontmatter.activateTime).trim() === ""
) {
frontmatter.activateTime = "06:00:00";
}
if (
frontmatter.currentMode === undefined ||
frontmatter.currentMode === null ||
![...allowedFields, "sleep"].includes(String(frontmatter.currentMode))
) {
frontmatter.currentMode = "DailyTime";
}
const previousMode = String(frontmatter.currentMode);
const previousLabel = modeLabelMap[previousMode] || previousMode;
const activateDate = parseActivateTime(String(frontmatter.activateTime).trim(), now);
if (!activateDate) {
throw new Error("activateTime 格式错误,应为 HH:mm:ss 或 HH:mm");
}
let addedMinutes = Math.floor((now.getTime() - activateDate.getTime()) / 60000);
if (addedMinutes < 0) addedMinutes = 0;
let creditedLabel = "无";
if (allowedFields.includes(previousMode)) {
const creditMode = forceCreditMode || previousMode;
const oldValue = normalizeNumber(frontmatter[creditMode]);
frontmatter[creditMode] = oldValue + addedMinutes;
creditedLabel = modeLabelMap[creditMode];
noticeMessage = `已记录 ${creditedLabel} ${addedMinutes} 分钟,开始${nextModeLabel}`;
} else if (previousMode === "sleep") {
noticeMessage = `已进入${nextModeLabel}`;
} else {
noticeMessage = `检测到异常状态,已切换到${nextModeLabel}`;
}
logLine = buildLogLine({
nowTime: formatTime(now),
previousLabel,
addedMinutes,
creditedLabel,
nextLabel: nextModeLabel
});
frontmatter.currentMode = nextMode;
frontmatter.activateTime = formatTime(now);
});
await appendLog(logLine);
new Notice(noticeMessage);
} catch (error) {
console.error(error);
new Notice(`记录失败:${error.message}`);
} finally {
window[LOCK_KEY] = false;
}
};