mirror of
https://github.com/maigamo/Daily-Task-Auto-Generator.git
synced 2026-07-22 12:40:26 +00:00
fix
This commit is contained in:
parent
e40c1161fc
commit
9fe90fb01e
8 changed files with 276 additions and 244 deletions
|
|
@ -1,10 +1,10 @@
|
|||
{
|
||||
"id": "daily-task-auto-generator",
|
||||
"name": "Daily Task Auto Generator",
|
||||
"version": "1.0.1",
|
||||
"version": "1.0.0",
|
||||
"minAppVersion": "0.15.0",
|
||||
"description": "Automatically generate daily tasks in specified folders with custom templates",
|
||||
"author": "maigamo",
|
||||
"authorUrl": "https://github.com/maigamo",
|
||||
"isDesktopOnly": false
|
||||
}
|
||||
}
|
||||
|
|
@ -40,6 +40,7 @@ export type TranslationKey =
|
|||
| 'notification.taskAdded'
|
||||
| 'notification.taskExists'
|
||||
| 'notification.error'
|
||||
| 'notification.generating'
|
||||
|
||||
// 星期
|
||||
| 'weekday.mon'
|
||||
|
|
@ -167,6 +168,7 @@ const translationsZH: Record<TranslationKey, string> = {
|
|||
'notification.taskAdded': '今日任务已添加',
|
||||
'notification.taskExists': '今日任务已存在',
|
||||
'notification.error': '错误:',
|
||||
'notification.generating': '正在生成今日任务...',
|
||||
|
||||
// 星期
|
||||
'weekday.mon': '星期一',
|
||||
|
|
@ -239,6 +241,7 @@ const translationsEN: Record<TranslationKey, string> = {
|
|||
'notification.taskAdded': 'Today\'s task has been added',
|
||||
'notification.taskExists': 'Today\'s task already exists',
|
||||
'notification.error': 'Error: ',
|
||||
'notification.generating': 'Generating today\'s tasks...',
|
||||
|
||||
// 星期
|
||||
'weekday.mon': 'Monday',
|
||||
|
|
|
|||
23
src/main.ts
23
src/main.ts
|
|
@ -4,11 +4,7 @@ import { DailyTaskSettingTab, SettingsManager } from './settings/settings';
|
|||
import { setCurrentLanguage } from './i18n/i18n';
|
||||
import { isWorkday } from './utils/dateUtils';
|
||||
import { TaskGenerator } from './taskGenerator';
|
||||
|
||||
// 定义插件图标
|
||||
const ICON = `<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/>
|
||||
</svg>`;
|
||||
import { DAILY_TASK_ICON } from './ui/icons';
|
||||
|
||||
/**
|
||||
* 每日任务自动生成器插件主类
|
||||
|
|
@ -24,10 +20,8 @@ export default class DailyTaskPlugin extends Plugin {
|
|||
* 插件加载时调用
|
||||
*/
|
||||
async onload() {
|
||||
console.log('Loading Daily Task Auto Generator plugin');
|
||||
|
||||
// 添加插件图标
|
||||
addIcon('daily-task', ICON);
|
||||
addIcon('daily-task', DAILY_TASK_ICON);
|
||||
|
||||
// 初始化设置管理器
|
||||
this.settingsManager = new SettingsManager(this);
|
||||
|
|
@ -53,9 +47,7 @@ export default class DailyTaskPlugin extends Plugin {
|
|||
|
||||
// 延迟10秒后检查是否需要自动生成任务
|
||||
// 这样可以确保Obsidian完全加载,避免与启动过程冲突
|
||||
console.log('计划在10秒后检查是否需要自动生成任务');
|
||||
setTimeout(async () => {
|
||||
console.log('开始检查是否需要自动生成任务');
|
||||
await this.checkAutoGenerate();
|
||||
}, 10000);
|
||||
}
|
||||
|
|
@ -64,7 +56,7 @@ export default class DailyTaskPlugin extends Plugin {
|
|||
* 插件卸载时调用
|
||||
*/
|
||||
onunload() {
|
||||
console.log('Unloading Daily Task Auto Generator plugin');
|
||||
// 插件卸载清理工作
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -75,25 +67,20 @@ export default class DailyTaskPlugin extends Plugin {
|
|||
|
||||
switch (settings.autoGenerateMode) {
|
||||
case AutoGenerateMode.DAILY:
|
||||
// 每天自动生成(静默模式,不打开文件,减少日志)
|
||||
console.log('根据设置,每天自动生成任务(静默模式)');
|
||||
// 每天自动生成(静默模式,不打开文件)
|
||||
await this.taskGenerator.generateDailyTask(false, true);
|
||||
break;
|
||||
|
||||
case AutoGenerateMode.WORKDAY:
|
||||
// 工作日自动生成(静默模式,不打开文件,减少日志)
|
||||
// 工作日自动生成(静默模式,不打开文件)
|
||||
if (isWorkday()) {
|
||||
console.log('根据设置,工作日自动生成任务(静默模式)');
|
||||
await this.taskGenerator.generateDailyTask(false, true);
|
||||
} else {
|
||||
console.log('今天不是工作日,跳过自动生成任务');
|
||||
}
|
||||
break;
|
||||
|
||||
case AutoGenerateMode.NONE:
|
||||
default:
|
||||
// 不自动生成
|
||||
console.log('已禁用自动生成任务功能');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ declare class DailyTaskPlugin {
|
|||
app: App;
|
||||
}
|
||||
|
||||
// CSS 相关代码
|
||||
// CSS 相关常量(class名称)
|
||||
const SettingsSectionCSS = "daily-task-settings-section";
|
||||
const ButtonCSS = "daily-task-button";
|
||||
const PreviewButtonCSS = "daily-task-preview-button";
|
||||
|
|
@ -21,71 +21,19 @@ const VerticalStackCSS = "daily-task-vertical-stack";
|
|||
const TextRightCSS = "daily-task-text-right";
|
||||
const TextCenterCSS = "daily-task-text-center";
|
||||
const ScrollbarSlimCSS = "daily-task-slim-scrollbar";
|
||||
const SaveIndicatorCSS = "daily-task-save-indicator";
|
||||
const SuccessIconCSS = "daily-task-success-icon";
|
||||
const SettingTopSpaceCSS = "daily-task-setting-top-space";
|
||||
const InputContainerCSS = "daily-task-input-container";
|
||||
const InputCSS = "daily-task-input";
|
||||
|
||||
/**
|
||||
* 添加插件自定义样式
|
||||
* 注意:样式内容现在已移至外部CSS文件
|
||||
*/
|
||||
function addCustomStyles() {
|
||||
const cssText = `
|
||||
.${SettingsSectionCSS} {
|
||||
margin-top: 24px;
|
||||
margin-bottom: 24px;
|
||||
padding: 12px 0;
|
||||
border-top: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.${ButtonCSS} {
|
||||
margin-top: 6px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.${PreviewButtonCSS}, .${ResetButtonCSS} {
|
||||
display: inline-block;
|
||||
text-align: center !important;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.${EditorCSS} {
|
||||
height: 200px;
|
||||
margin-top: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.${VerticalStackCSS} {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.${TextRightCSS} {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.${TextCenterCSS} {
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
.${ScrollbarSlimCSS} .CodeMirror-vscrollbar {
|
||||
width: 20% !important;
|
||||
}
|
||||
|
||||
/* 自定义通知样式 */
|
||||
.daily-task-success-notice {
|
||||
background-color: rgba(0, 255, 127, 0.2) !important;
|
||||
}
|
||||
|
||||
.daily-task-warning-notice {
|
||||
background-color: rgba(255, 165, 0, 0.2) !important;
|
||||
}
|
||||
|
||||
.daily-task-error-notice {
|
||||
background-color: rgba(255, 69, 0, 0.2) !important;
|
||||
}
|
||||
`;
|
||||
|
||||
// 添加自定义样式
|
||||
const styleElement = document.createElement('style');
|
||||
styleElement.textContent = cssText;
|
||||
document.head.appendChild(styleElement);
|
||||
// 样式已移至src/styles.css,无需在此处添加内联样式
|
||||
// 插件加载时会自动加载styles.css文件
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -127,7 +75,7 @@ export class DailyTaskSettingTab extends PluginSettingTab {
|
|||
|
||||
// 添加顶部间距(增加间距以改善界面美观)
|
||||
const topSpacing = containerEl.createEl('div');
|
||||
topSpacing.style.marginTop = '30px';
|
||||
topSpacing.classList.add(SettingTopSpaceCSS);
|
||||
|
||||
// 根目录设置
|
||||
const rootDirSetting = new Setting(containerEl)
|
||||
|
|
@ -136,9 +84,7 @@ export class DailyTaskSettingTab extends PluginSettingTab {
|
|||
|
||||
// 创建输入框容器,使其可以包含额外元素
|
||||
const inputContainer = document.createElement('div');
|
||||
inputContainer.style.display = 'flex';
|
||||
inputContainer.style.width = '100%';
|
||||
inputContainer.style.position = 'relative';
|
||||
inputContainer.classList.add(InputContainerCSS);
|
||||
rootDirSetting.controlEl.appendChild(inputContainer);
|
||||
|
||||
this.rootDirInput = new TextComponent(inputContainer)
|
||||
|
|
@ -153,37 +99,23 @@ export class DailyTaskSettingTab extends PluginSettingTab {
|
|||
}
|
||||
});
|
||||
|
||||
// 给input元素直接设置placeholder属性
|
||||
// 给input元素设置类和placeholder属性
|
||||
if (this.rootDirInput && this.rootDirInput.inputEl) {
|
||||
this.rootDirInput.inputEl.classList.add(InputCSS);
|
||||
this.rootDirInput.inputEl.placeholder = 'DailyTasks';
|
||||
// 增加宽度
|
||||
this.rootDirInput.inputEl.style.width = '100%';
|
||||
// 美化输入框样式
|
||||
this.rootDirInput.inputEl.style.borderRadius = '4px';
|
||||
this.rootDirInput.inputEl.style.padding = '8px 35px 8px 10px';
|
||||
this.rootDirInput.inputEl.style.transition = 'all 0.3s ease';
|
||||
}
|
||||
|
||||
// 添加自动保存指示器
|
||||
const saveIndicator = document.createElement('div');
|
||||
saveIndicator.classList.add('save-indicator');
|
||||
saveIndicator.style.position = 'absolute';
|
||||
saveIndicator.style.right = '10px';
|
||||
saveIndicator.style.top = '50%';
|
||||
saveIndicator.style.transform = 'translateY(-50%)';
|
||||
saveIndicator.style.opacity = '0';
|
||||
saveIndicator.style.transition = 'opacity 0.3s ease';
|
||||
saveIndicator.classList.add(SaveIndicatorCSS);
|
||||
inputContainer.appendChild(saveIndicator);
|
||||
|
||||
// 创建保存成功图标
|
||||
const saveSuccessIcon = createSpan({cls: 'svg-icon lucide-check'});
|
||||
saveSuccessIcon.style.color = '#4CAF50';
|
||||
saveSuccessIcon.style.width = '18px';
|
||||
saveSuccessIcon.style.height = '18px';
|
||||
const saveSuccessIcon = createSpan({cls: `svg-icon lucide-check ${SuccessIconCSS}`});
|
||||
saveIndicator.appendChild(saveSuccessIcon);
|
||||
|
||||
// 记录自动保存定时器
|
||||
let autoSaveTimer: number | null = null;
|
||||
let autoSaveTimer: NodeJS.Timeout | null = null;
|
||||
|
||||
// 自动保存方法
|
||||
this.autoSaveRootDir = async (value: string) => {
|
||||
|
|
@ -199,18 +131,17 @@ export class DailyTaskSettingTab extends PluginSettingTab {
|
|||
pathToSave = 'DailyTasks'; // 默认存放目录
|
||||
}
|
||||
|
||||
// 保存设置
|
||||
// 实际保存设置
|
||||
await this.settingsManager.updateSettings({ rootDir: pathToSave });
|
||||
this.dirtySettings = false;
|
||||
|
||||
// 显示保存成功指示器
|
||||
// 显示保存成功的视觉反馈
|
||||
saveIndicator.style.opacity = '1';
|
||||
|
||||
// 3秒后隐藏
|
||||
setTimeout(() => {
|
||||
saveIndicator.style.opacity = '0';
|
||||
}, 2000);
|
||||
}, 800) as unknown as number;
|
||||
}, 1500);
|
||||
|
||||
}, 800);
|
||||
};
|
||||
|
||||
// 自动生成模式
|
||||
|
|
|
|||
|
|
@ -88,4 +88,96 @@
|
|||
|
||||
.daily-task-slim-scrollbar::-webkit-scrollbar-thumb:hover {
|
||||
background: var(--interactive-accent-hover);
|
||||
}
|
||||
|
||||
/* 设置组件样式 */
|
||||
.daily-task-settings-section {
|
||||
margin-top: 24px;
|
||||
margin-bottom: 24px;
|
||||
padding: 12px 0;
|
||||
border-top: 1px solid var(--background-modifier-border);
|
||||
}
|
||||
|
||||
.daily-task-button {
|
||||
margin-top: 6px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.daily-task-preview-button, .daily-task-reset-button {
|
||||
display: inline-block;
|
||||
text-align: center !important;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.daily-task-editor {
|
||||
height: 200px;
|
||||
margin-top: 12px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.daily-task-vertical-stack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.daily-task-text-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.daily-task-text-center {
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
.daily-task-slim-scrollbar .CodeMirror-vscrollbar {
|
||||
width: 20% !important;
|
||||
}
|
||||
|
||||
/* 自定义通知样式 */
|
||||
.daily-task-success-notice {
|
||||
background-color: rgba(0, 255, 127, 0.2) !important;
|
||||
}
|
||||
|
||||
.daily-task-warning-notice {
|
||||
background-color: rgba(255, 165, 0, 0.2) !important;
|
||||
}
|
||||
|
||||
.daily-task-error-notice {
|
||||
background-color: rgba(255, 69, 0, 0.2) !important;
|
||||
}
|
||||
|
||||
/* 输入框样式 */
|
||||
.daily-task-input {
|
||||
width: 100%;
|
||||
border-radius: 4px;
|
||||
padding: 8px 35px 8px 10px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
/* 保存指示器样式 */
|
||||
.daily-task-save-indicator {
|
||||
position: absolute;
|
||||
right: 10px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
}
|
||||
|
||||
/* 成功图标样式 */
|
||||
.daily-task-success-icon {
|
||||
color: #4CAF50;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
|
||||
/* 设置页面顶部间距 */
|
||||
.daily-task-setting-top-space {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
/* 输入容器样式 */
|
||||
.daily-task-input-container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
import { App, Notice, TFile, Vault } from "obsidian";
|
||||
import { App, Notice, TAbstractFile, TFile, Vault } from "obsidian";
|
||||
import { getTranslation } from "./i18n/i18n";
|
||||
import { SettingsManager } from "./settings/settings";
|
||||
import { appendToFile, ensureFileExists, ensureFolderExists, getTaskFilePath, todayTaskExists, fileContains, getYesterdayDate, getYesterdayTaskFilePath, extractTasksForDate, analyzeTaskCompletion, yesterdayStatisticsExists, generateStatisticsContent } from "./utils/fileUtils";
|
||||
|
|
@ -33,7 +33,6 @@ export class TaskGenerator {
|
|||
|
||||
// 获取任务文件路径
|
||||
const filePath = getTaskFilePath(rootDir);
|
||||
if (!quietMode) console.log(`生成任务文件路径: ${filePath}`);
|
||||
|
||||
// 解析年份和月份
|
||||
const pathParts = filePath.split('/');
|
||||
|
|
@ -42,41 +41,39 @@ export class TaskGenerator {
|
|||
const yearFolder = `${rootDir}/${year}`;
|
||||
|
||||
// 确保根目录存在
|
||||
if (!quietMode) console.log(`正在确保根目录存在: ${rootDir}`);
|
||||
const rootCreated = await ensureFolderExists(this.vault, rootDir);
|
||||
if (!rootCreated) {
|
||||
console.error(`无法访问或创建根目录: ${rootDir}`);
|
||||
throw new Error(`无法访问根目录: ${rootDir},可能是存在同名文件或权限问题`);
|
||||
}
|
||||
if (!quietMode) console.log(`根目录确认: ${rootDir}`);
|
||||
|
||||
// 确保年份目录存在
|
||||
if (!quietMode) console.log(`正在确保年份目录存在: ${yearFolder}`);
|
||||
const yearCreated = await ensureFolderExists(this.vault, yearFolder);
|
||||
if (!yearCreated) {
|
||||
console.error(`无法访问或创建年份目录: ${yearFolder}`);
|
||||
throw new Error(`无法访问年份目录: ${yearFolder},可能是存在同名文件或权限问题`);
|
||||
}
|
||||
if (!quietMode) console.log(`年份目录确认: ${yearFolder}`);
|
||||
|
||||
// 确保月份文件存在
|
||||
if (!quietMode) console.log(`正在确保月份文件存在: ${filePath} (${monthName})`);
|
||||
const fileCreated = await ensureFileExists(this.vault, filePath);
|
||||
if (!fileCreated) {
|
||||
console.error(`无法访问或创建月份文件: ${filePath}`);
|
||||
throw new Error(`无法访问月份文件: ${filePath},请检查是否存在同名目录或权限问题`);
|
||||
}
|
||||
if (!quietMode) console.log(`月份文件确认: ${filePath}`);
|
||||
|
||||
// 检查今日任务是否已存在
|
||||
const date = getCurrentDate();
|
||||
// 更改检查方式:不仅检查纯日期,也检查带图标的日期格式
|
||||
const dateRegex = new RegExp(`## [^\\n]*${date}[^\\n]*\\n`);
|
||||
const fileContent = await this.vault.read(this.vault.getAbstractFileByPath(filePath) as TFile);
|
||||
|
||||
// 使用instanceof检查确保文件对象有效
|
||||
const abstractFile = this.vault.getAbstractFileByPath(filePath);
|
||||
if (!abstractFile || !(abstractFile instanceof TFile)) {
|
||||
throw new Error(`找不到有效的文件: ${filePath}`);
|
||||
}
|
||||
|
||||
const fileContent = await this.vault.read(abstractFile);
|
||||
const existingTaskCheck = dateRegex.test(fileContent);
|
||||
|
||||
if (existingTaskCheck) {
|
||||
console.log(`今日(${date})任务已存在于文件中,跳过创建`);
|
||||
if (!quietMode) console.log(`今日(${date})任务已存在于文件中,跳过创建`);
|
||||
|
||||
// 如果需要打开文件
|
||||
if (openFile) {
|
||||
|
|
@ -117,11 +114,10 @@ export class TaskGenerator {
|
|||
: renderedContent;
|
||||
|
||||
// 追加到文件
|
||||
if (!quietMode) console.log(`正在向文件追加内容`);
|
||||
const success = await appendToFile(this.vault, filePath, fullContent);
|
||||
|
||||
if (success) {
|
||||
console.log(`✅ 任务内容追加成功 ${date}`);
|
||||
if (!quietMode) console.log(`✅ 任务内容追加成功 ${date}`);
|
||||
|
||||
// 如果需要打开文件
|
||||
if (openFile) {
|
||||
|
|
@ -138,9 +134,6 @@ export class TaskGenerator {
|
|||
} else {
|
||||
throw new Error(`文件创建成功但无法打开: ${filePath}`);
|
||||
}
|
||||
} else {
|
||||
// 静默模式,只在控制台记录
|
||||
console.log(`✨ 今日(${date})任务已静默添加,无需打开文件`);
|
||||
}
|
||||
} else {
|
||||
throw new Error(`无法向文件追加内容: ${filePath}`);
|
||||
|
|
@ -171,14 +164,11 @@ export class TaskGenerator {
|
|||
const yesterdayDate = getYesterdayDate();
|
||||
const yesterdayFilePath = getYesterdayTaskFilePath(rootDir);
|
||||
|
||||
if (!quietMode) console.log(`正在获取昨日(${yesterdayDate})任务统计,文件路径: ${yesterdayFilePath}`);
|
||||
|
||||
// 检查今日的内容中是否已经包含昨日统计
|
||||
const todayFilePath = getTaskFilePath(rootDir);
|
||||
const statsExists = await yesterdayStatisticsExists(this.vault, todayFilePath, todayDate);
|
||||
|
||||
if (statsExists) {
|
||||
if (!quietMode) console.log('昨日统计信息已存在,跳过生成');
|
||||
return '';
|
||||
}
|
||||
|
||||
|
|
@ -187,55 +177,33 @@ export class TaskGenerator {
|
|||
|
||||
// 如果找不到昨日任务内容
|
||||
if (!yesterdayContent) {
|
||||
if (!quietMode) console.log(`找不到昨日(${yesterdayDate})任务内容,跳过统计`);
|
||||
return '';
|
||||
}
|
||||
|
||||
// 分析任务完成情况
|
||||
const taskStats = analyzeTaskCompletion(yesterdayContent);
|
||||
|
||||
if (!quietMode) {
|
||||
console.log(`昨日任务统计: 总数=${taskStats.totalTasks}, 已完成=${taskStats.completedTasks}`);
|
||||
console.log(`未完成任务: ${taskStats.unfinishedTasksList.length}个`);
|
||||
}
|
||||
|
||||
// 如果没有任务,不生成统计信息
|
||||
if (taskStats.totalTasks === 0) {
|
||||
if (!quietMode) console.log('昨日没有任务,跳过统计');
|
||||
return '';
|
||||
}
|
||||
|
||||
// 确保在所有语言环境下都调用generateStatisticsContent
|
||||
// 生成统计内容
|
||||
return generateStatisticsContent(taskStats, getTranslation);
|
||||
|
||||
} catch (error) {
|
||||
console.error(`生成昨日统计信息时出错:`, error);
|
||||
return ''; // 出错时返回空字符串,不影响今日任务生成
|
||||
if (!quietMode) console.error("生成昨日统计出错:", error);
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动添加今日任务
|
||||
* 手动添加任务(从命令面板或图标调用)
|
||||
* @returns 成功或失败
|
||||
*/
|
||||
async addTaskManually(): Promise<boolean> {
|
||||
try {
|
||||
const settings = this.settingsManager.getSettings();
|
||||
const rootDir = settings.rootDir.trim() || 'DailyTasks'; // 使用默认目录
|
||||
// 显示开始生成的通知
|
||||
this.showWarningNotice(`⏳ ${getTranslation('notification.generating')}`);
|
||||
|
||||
// 检查今日任务是否已存在
|
||||
const exists = await todayTaskExists(this.vault, rootDir);
|
||||
|
||||
if (exists) {
|
||||
// 任务已存在,显示提示
|
||||
this.showWarningNotice(`📌 ${getTranslation('notification.taskExists')}`);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 生成任务
|
||||
return await this.generateDailyTask();
|
||||
// 调用任务生成逻辑,打开文件
|
||||
return await this.generateDailyTask(true);
|
||||
} catch (error) {
|
||||
console.error("Error adding task manually:", error);
|
||||
console.error("手动添加任务失败:", error);
|
||||
|
||||
// 显示错误通知
|
||||
const errorMsg = error instanceof Error ? error.message : String(error);
|
||||
|
|
@ -247,49 +215,28 @@ export class TaskGenerator {
|
|||
|
||||
/**
|
||||
* 显示成功通知
|
||||
* @param message 通知内容
|
||||
*/
|
||||
private showSuccessNotice(message: string): void {
|
||||
const notice = new Notice(
|
||||
message,
|
||||
this.settingsManager.getSettings().successNotificationDuration
|
||||
);
|
||||
|
||||
// 添加成功样式
|
||||
if (notice.noticeEl) {
|
||||
notice.noticeEl.addClass('daily-task-success-notice');
|
||||
}
|
||||
const notice = new Notice(message, 4000);
|
||||
// 使用CSS类而不是内联样式
|
||||
notice.noticeEl.classList.add('daily-task-success-notice');
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示警告通知
|
||||
* @param message 通知内容
|
||||
*/
|
||||
private showWarningNotice(message: string): void {
|
||||
const notice = new Notice(
|
||||
message,
|
||||
this.settingsManager.getSettings().successNotificationDuration
|
||||
);
|
||||
|
||||
// 添加警告样式
|
||||
if (notice.noticeEl) {
|
||||
notice.noticeEl.addClass('daily-task-warning-notice');
|
||||
}
|
||||
const notice = new Notice(message, 3000);
|
||||
// 使用CSS类而不是内联样式
|
||||
notice.noticeEl.classList.add('daily-task-warning-notice');
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示错误通知
|
||||
* @param message 通知内容
|
||||
*/
|
||||
private showErrorNotice(message: string): void {
|
||||
const notice = new Notice(
|
||||
message,
|
||||
this.settingsManager.getSettings().successNotificationDuration
|
||||
);
|
||||
|
||||
// 添加错误样式
|
||||
if (notice.noticeEl) {
|
||||
notice.noticeEl.addClass('daily-task-error-notice');
|
||||
}
|
||||
const notice = new Notice(message, 5000);
|
||||
// 使用CSS类而不是内联样式
|
||||
notice.noticeEl.classList.add('daily-task-error-notice');
|
||||
}
|
||||
}
|
||||
57
src/ui/icons.ts
Normal file
57
src/ui/icons.ts
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
/**
|
||||
* 插件图标定义
|
||||
* 集中管理所有SVG图标,方便维护和重用
|
||||
*/
|
||||
|
||||
/**
|
||||
* 主插件图标 - 用于显示在侧边栏和设置中
|
||||
*/
|
||||
export const DAILY_TASK_ICON = `<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5"/>
|
||||
</svg>`;
|
||||
|
||||
/**
|
||||
* 设置图标 - 用于设置页面
|
||||
*/
|
||||
export const SETTINGS_ICON = `<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<circle cx="12" cy="12" r="3"></circle>
|
||||
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
|
||||
</svg>`;
|
||||
|
||||
/**
|
||||
* 添加任务图标 - 用于添加任务按钮
|
||||
*/
|
||||
export const ADD_TASK_ICON = `<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="12" y1="5" x2="12" y2="19"></line>
|
||||
<line x1="5" y1="12" x2="19" y2="12"></line>
|
||||
</svg>`;
|
||||
|
||||
/**
|
||||
* 统计图标 - 用于任务统计功能
|
||||
*/
|
||||
export const STATISTICS_ICON = `<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<line x1="18" y1="20" x2="18" y2="10"></line>
|
||||
<line x1="12" y1="20" x2="12" y2="4"></line>
|
||||
<line x1="6" y1="20" x2="6" y2="14"></line>
|
||||
</svg>`;
|
||||
|
||||
/**
|
||||
* 重置图标 - 用于重置设置按钮
|
||||
*/
|
||||
export const RESET_ICON = `<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M3 2v6h6"></path>
|
||||
<path d="M21 12A9 9 0 0 0 6 5.3L3 8"></path>
|
||||
<path d="M21 22v-6h-6"></path>
|
||||
<path d="M3 12a9 9 0 0 0 15 6.7l3-2.7"></path>
|
||||
</svg>`;
|
||||
|
||||
/**
|
||||
* 模板图标 - 用于模板设置
|
||||
*/
|
||||
export const TEMPLATE_ICON = `<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"></path>
|
||||
<polyline points="14 2 14 8 20 8"></polyline>
|
||||
<line x1="16" y1="13" x2="8" y2="13"></line>
|
||||
<line x1="16" y1="17" x2="8" y2="17"></line>
|
||||
<polyline points="10 9 9 9 8 9"></polyline>
|
||||
</svg>`;
|
||||
|
|
@ -173,10 +173,10 @@ export async function appendToFile(vault: Vault, path: string, content: string):
|
|||
*/
|
||||
export async function fileContains(vault: Vault, path: string, content: string): Promise<boolean> {
|
||||
try {
|
||||
const file = vault.getAbstractFileByPath(path);
|
||||
if (file && file instanceof TFile) {
|
||||
const abstractFile = vault.getAbstractFileByPath(path);
|
||||
if (abstractFile && abstractFile instanceof TFile) {
|
||||
try {
|
||||
const currentContent = await vault.read(file);
|
||||
const currentContent = await vault.read(abstractFile);
|
||||
return currentContent.includes(content);
|
||||
} catch (e) {
|
||||
console.error(`读取文件内容时出错: ${e}`);
|
||||
|
|
@ -215,22 +215,24 @@ export function getTaskFilePath(rootDir: string): string {
|
|||
* @returns 是否已存在
|
||||
*/
|
||||
export async function todayTaskExists(vault: Vault, rootDir: string): Promise<boolean> {
|
||||
const taskFilePath = getTaskFilePath(rootDir);
|
||||
const date = getCurrentDate();
|
||||
|
||||
// 查找文件是否存在以及是否包含今天的日期标题(包括带图标格式)
|
||||
try {
|
||||
console.log(`检查今日任务是否存在于: ${taskFilePath}`);
|
||||
// 更改检查方式:改用正则表达式匹配任何包含当前日期的标题行
|
||||
const file = vault.getAbstractFileByPath(taskFilePath);
|
||||
if (file && file instanceof TFile) {
|
||||
const content = await vault.read(file);
|
||||
const dateRegex = new RegExp(`## [^\\n]*${date}[^\\n]*\\n`);
|
||||
return dateRegex.test(content);
|
||||
const date = getCurrentDate();
|
||||
const filePath = getTaskFilePath(rootDir);
|
||||
|
||||
// 获取文件
|
||||
const abstractFile = vault.getAbstractFileByPath(filePath);
|
||||
if (!abstractFile || !(abstractFile instanceof TFile)) {
|
||||
return false; // 文件不存在
|
||||
}
|
||||
return false;
|
||||
|
||||
// 读取文件内容
|
||||
const content = await vault.read(abstractFile);
|
||||
|
||||
// 更改检查方式:不仅检查纯日期,也检查带图标的日期格式
|
||||
const dateRegex = new RegExp(`## [^\\n]*${date}[^\\n]*\\n`);
|
||||
return dateRegex.test(content);
|
||||
} catch (error) {
|
||||
console.error(`检查今日任务时出错:`, error);
|
||||
console.error("Error checking if today's task exists:", error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -306,36 +308,47 @@ function getMonthNameEN(monthIndex: number): string {
|
|||
}
|
||||
|
||||
/**
|
||||
* 从文件内容中提取特定日期的任务内容
|
||||
* @param vault Obsidian保险库
|
||||
* 提取特定日期的任务内容
|
||||
* @param vault Obsidian文件系统
|
||||
* @param filePath 文件路径
|
||||
* @param date 日期(YYYY-MM-DD格式)
|
||||
* @returns 该日期的任务内容或null
|
||||
* @param date 日期字符串 (YYYY-MM-DD)
|
||||
* @returns 任务内容或null
|
||||
*/
|
||||
export async function extractTasksForDate(vault: Vault, filePath: string, date: string): Promise<string | null> {
|
||||
try {
|
||||
// 检查文件是否存在
|
||||
const file = vault.getAbstractFileByPath(filePath);
|
||||
if (!file || !(file instanceof TFile)) {
|
||||
console.log(`找不到文件: ${filePath}`);
|
||||
return null;
|
||||
// 获取文件
|
||||
const abstractFile = vault.getAbstractFileByPath(filePath);
|
||||
if (!abstractFile || !(abstractFile instanceof TFile)) {
|
||||
return null; // 文件不存在
|
||||
}
|
||||
|
||||
|
||||
// 读取文件内容
|
||||
const content = await vault.read(file);
|
||||
const content = await vault.read(abstractFile);
|
||||
|
||||
// 寻找日期标题,支持带图标格式
|
||||
const dateHeaderRegex = new RegExp(`## [^\\n]*${date}[^\\n]*\\n(.*?)(?=\\n## |$)`, 's');
|
||||
const match = content.match(dateHeaderRegex);
|
||||
// 找到日期标题行的位置
|
||||
const dateRegex = new RegExp(`## [^\\n]*${date}[^\\n]*\\n`);
|
||||
const match = content.match(dateRegex);
|
||||
|
||||
if (match && match[1]) {
|
||||
return match[1].trim();
|
||||
if (!match || match.index === undefined) {
|
||||
return null; // 找不到日期
|
||||
}
|
||||
|
||||
console.log(`找不到日期 ${date} 的任务内容`);
|
||||
return null;
|
||||
// 找到本日期部分的结束位置(下一个二级标题或文件结尾)
|
||||
const startPos = match.index;
|
||||
const nextHeadingMatch = content.slice(startPos + match[0].length).match(/\n## /);
|
||||
let endPos: number;
|
||||
|
||||
if (nextHeadingMatch && nextHeadingMatch.index !== undefined) {
|
||||
endPos = startPos + match[0].length + nextHeadingMatch.index;
|
||||
} else {
|
||||
endPos = content.length;
|
||||
}
|
||||
|
||||
// 提取任务部分,包括标题
|
||||
return content.slice(startPos, endPos).trim();
|
||||
|
||||
} catch (error) {
|
||||
console.error(`提取任务内容时出错: ${error}`);
|
||||
console.error(`Error extracting tasks for date ${date}:`, error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -382,30 +395,32 @@ export function analyzeTaskCompletion(taskContent: string): {
|
|||
}
|
||||
|
||||
/**
|
||||
* 检查昨日统计信息是否已存在
|
||||
* @param vault Obsidian保险库
|
||||
* @param filePath 文件路径
|
||||
* @param date 今日日期(用于检查昨日统计)
|
||||
* @returns 是否存在昨日统计信息
|
||||
* 检查今日文件中是否已包含昨日统计信息
|
||||
* @param vault Obsidian文件系统
|
||||
* @param filePath 今日任务文件路径
|
||||
* @param date 今日日期
|
||||
* @returns 是否已包含昨日统计
|
||||
*/
|
||||
export async function yesterdayStatisticsExists(vault: Vault, filePath: string, date: string): Promise<boolean> {
|
||||
try {
|
||||
// 检查文件是否存在
|
||||
const file = vault.getAbstractFileByPath(filePath);
|
||||
if (!file || !(file instanceof TFile)) {
|
||||
console.log(`找不到文件: ${filePath}`);
|
||||
return false;
|
||||
// 获取文件
|
||||
const abstractFile = vault.getAbstractFileByPath(filePath);
|
||||
if (!abstractFile || !(abstractFile instanceof TFile)) {
|
||||
return false; // 文件不存在
|
||||
}
|
||||
|
||||
|
||||
// 读取文件内容
|
||||
const content = await vault.read(file);
|
||||
const content = await vault.read(abstractFile);
|
||||
|
||||
// 寻找今日日期下的昨日统计标题,支持带图标格式
|
||||
const statisticsTitleRegex = new RegExp(`## [^\\n]*${date}[^\\n]*\\n.*?${getTranslation('statistics.title')}`, 's');
|
||||
return statisticsTitleRegex.test(content);
|
||||
// 检查是否包含昨日统计标记
|
||||
const yesterdayDate = getYesterdayDate();
|
||||
const statsTitle = getTranslation('statistics.title');
|
||||
const statsRegex = new RegExp(`## [^\\n]*${date}[^\\n]*[\\s\\S]*?${statsTitle}`);
|
||||
|
||||
return statsRegex.test(content);
|
||||
} catch (error) {
|
||||
console.error(`检查昨日统计信息时出错: ${error}`);
|
||||
return false;
|
||||
console.error(`Error checking if yesterday statistics exists:`, error);
|
||||
return false; // 出错时假设不存在
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue