feat: 重构草稿箱功能

- 数据库添加 drafts 表存储草稿数据
- 草稿箱独立页面,侧边栏添加入口
- 自动保存间隔可配置(2/5/10/30/60秒)
- 草稿可编辑、发布为正式条目、删除
- 编辑时自动保存到服务器数据库
This commit is contained in:
2026-04-19 17:55:29 +08:00
parent 51c76ebd24
commit 7d3c5c2ae1
2 changed files with 442 additions and 107 deletions

View File

@@ -99,6 +99,26 @@ class Database:
)
""")
# 草稿表
cursor.execute("""
CREATE TABLE IF NOT EXISTS drafts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
type TEXT NOT NULL DEFAULT 'text',
title TEXT,
content TEXT,
url TEXT,
source TEXT,
status TEXT DEFAULT 'pending',
priority TEXT DEFAULT 'medium',
due_date TEXT,
note TEXT,
tags TEXT,
is_starred INTEGER DEFAULT 0,
created_at TEXT NOT NULL,
updated_at TEXT NOT NULL
)
""")
# 邮件发送记录表
cursor.execute("""
CREATE TABLE IF NOT EXISTS email_logs (
@@ -444,6 +464,109 @@ class Database:
conn.commit()
return cursor.rowcount > 0
# ============ Draft 草稿操作 ============
def save_draft(self, type: str = "text", title: str = None, content: str = None,
url: str = None, source: str = None, status: str = "pending",
priority: str = "medium", due_date: str = None, note: str = None,
tags: str = None, is_starred: bool = False) -> int:
"""保存草稿"""
self._ensure_init()
now = datetime.now().isoformat()
with self.get_conn() as conn:
cursor = conn.cursor()
cursor.execute("""
INSERT INTO drafts (type, title, content, url, source, status, priority, due_date, note, tags, is_starred, created_at, updated_at)
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
""", (type, title, content, url, source, status, priority, due_date, note, tags, 1 if is_starred else 0, now, now))
draft_id = cursor.lastrowid
conn.commit()
return draft_id
def update_draft(self, draft_id: int, **kwargs) -> bool:
"""更新草稿"""
allowed_fields = ['type', 'title', 'content', 'url', 'source', 'status', 'priority', 'due_date', 'note', 'tags', 'is_starred']
update_fields = {k: v for k, v in kwargs.items() if k in allowed_fields}
if not update_fields:
return False
now = datetime.now().isoformat()
with self.get_conn() as conn:
cursor = conn.cursor()
cursor.execute("SELECT id FROM drafts WHERE id = ?", (draft_id,))
if not cursor.fetchone():
return False
set_clause = ", ".join(f"{k} = ?" for k in update_fields.keys())
set_clause += ", updated_at = ?"
values = list(update_fields.values()) + [now, draft_id]
cursor.execute(f"UPDATE drafts SET {set_clause} WHERE id = ?", values)
conn.commit()
return True
def list_drafts(self, limit: int = 50, offset: int = 0) -> List[Dict[str, Any]]:
"""列出草稿"""
with self.get_conn() as conn:
cursor = conn.cursor()
cursor.execute("SELECT * FROM drafts ORDER BY updated_at DESC LIMIT ? OFFSET ?", (limit, offset))
return [dict(row) for row in cursor.fetchall()]
def count_drafts(self) -> int:
"""计算草稿总数"""
with self.get_conn() as conn:
cursor = conn.cursor()
cursor.execute("SELECT COUNT(*) as count FROM drafts")
return cursor.fetchone()['count']
def get_draft(self, draft_id: int) -> Optional[Dict[str, Any]]:
"""获取单个草稿"""
with self.get_conn() as conn:
cursor = conn.cursor()
cursor.execute("SELECT * FROM drafts WHERE id = ?", (draft_id,))
row = cursor.fetchone()
return dict(row) if row else None
def delete_draft(self, draft_id: int) -> bool:
"""删除草稿"""
with self.get_conn() as conn:
cursor = conn.cursor()
cursor.execute("DELETE FROM drafts WHERE id = ?", (draft_id,))
conn.commit()
return cursor.rowcount > 0
def draft_to_item(self, draft_id: int) -> Optional[int]:
"""将草稿转为正式条目"""
draft = self.get_draft(draft_id)
if not draft:
return None
# 创建条目
tags_list = draft['tags'].split(',') if draft['tags'] else []
tags_list = [t.strip() for t in tags_list if t.strip()]
item_id = self.create_item(
type=draft['type'],
title=draft['title'],
content=draft['content'],
url=draft['url'],
source=draft['source'],
status=draft['status'],
priority=draft['priority'],
due_date=draft['due_date'],
note=draft['note'],
tags=tags_list,
is_starred=draft['is_starred']
)
# 删除草稿
if item_id:
self.delete_draft(draft_id)
return item_id
# ============ Tag 操作 ============
def create_tag(self, name: str, color: str = "#3498db") -> int: