|
|
|
|
@ -0,0 +1,621 @@
|
|
|
|
|
import tkinter as tk
|
|
|
|
|
import customtkinter as ctk
|
|
|
|
|
from tkinter import messagebox
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
import tkinter.scrolledtext as scrolledtext
|
|
|
|
|
from base_manager import BaseManager
|
|
|
|
|
|
|
|
|
|
"""
|
|
|
|
|
待办事项管理模块
|
|
|
|
|
|
|
|
|
|
该模块提供了待办事项管理的UI界面,包括显示待办事项列表、查看待办详情、创建新待办、标记完成和删除待办等功能。
|
|
|
|
|
使用tkinter和customtkinter库实现现代化的用户界面。
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
class TodoManager(BaseManager):
|
|
|
|
|
"""待办事项管理类
|
|
|
|
|
|
|
|
|
|
负责待办事项管理界面的创建和交互逻辑,包括:
|
|
|
|
|
1. 显示用户待办事项列表
|
|
|
|
|
2. 查看待办事项详细信息
|
|
|
|
|
3. 创建新待办事项
|
|
|
|
|
4. 标记待办事项为完成
|
|
|
|
|
5. 删除待办事项
|
|
|
|
|
|
|
|
|
|
属性:
|
|
|
|
|
app: 主应用程序对象
|
|
|
|
|
root: 主窗口对象
|
|
|
|
|
auth: 用户认证对象
|
|
|
|
|
current_session_id: 当前会话ID
|
|
|
|
|
todo_listbox: 待办事项列表框控件
|
|
|
|
|
todo_detail_text: 待办事项详情文本框控件
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
def __init__(self, app):
|
|
|
|
|
"""初始化待办事项管理类
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
app: 主应用程序对象,包含root、auth等核心组件
|
|
|
|
|
"""
|
|
|
|
|
self.app = app
|
|
|
|
|
self.root = app.root # 主窗口
|
|
|
|
|
self.auth = app.auth # 用户认证对象
|
|
|
|
|
super().__init__(app, app.root) # 调用基类初始化
|
|
|
|
|
self.logger = getattr(app, 'logger_manager', None) # 日志记录对象(使用正确的属性名)
|
|
|
|
|
self.current_session_id = app.current_session_id # 当前会话ID
|
|
|
|
|
self.todo_listbox = None # 待办事项列表框
|
|
|
|
|
self.todo_detail_text = None # 待办事项详情文本框
|
|
|
|
|
|
|
|
|
|
def update_session_id(self, session_id):
|
|
|
|
|
"""更新当前会话ID
|
|
|
|
|
|
|
|
|
|
更新应用程序和当前类实例的会话ID,确保操作使用正确的用户上下文。
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
session_id (str): 新的会话ID
|
|
|
|
|
"""
|
|
|
|
|
self.app.current_session_id = session_id # 更新应用程序的会话ID
|
|
|
|
|
self.current_session_id = session_id # 更新当前实例的会话ID
|
|
|
|
|
if session_id and self.logger:
|
|
|
|
|
# 获取用户名
|
|
|
|
|
username = session_id
|
|
|
|
|
if self.auth:
|
|
|
|
|
try:
|
|
|
|
|
valid, username = self.auth.validate_session(session_id)
|
|
|
|
|
if not valid:
|
|
|
|
|
username = session_id.split("_")[0] if "_" in session_id else session_id
|
|
|
|
|
except Exception as e:
|
|
|
|
|
username = session_id.split("_")[0] if "_" in session_id else session_id
|
|
|
|
|
self.logger.log("info", f"更新待办事项管理器会话: {session_id}", username)
|
|
|
|
|
|
|
|
|
|
def show_todos(self, parent_frame=None):
|
|
|
|
|
"""显示我的待办事项窗口
|
|
|
|
|
|
|
|
|
|
创建并显示待办事项管理界面,包括:
|
|
|
|
|
1. 左侧待办事项列表
|
|
|
|
|
2. 右侧待办事项详情
|
|
|
|
|
3. 创建、标记完成和删除待办事项按钮
|
|
|
|
|
|
|
|
|
|
如果用户未登录,显示提示信息。
|
|
|
|
|
如果用户没有待办事项数据,添加示例数据。
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
parent_frame (CTkFrame, optional): 要显示待办的父框架。如果为None,则创建新窗口(兼容旧代码)。
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
# 调用基类的show方法保存聊天历史记录
|
|
|
|
|
super().show(parent_frame)
|
|
|
|
|
|
|
|
|
|
# 决定使用哪个框架作为父容器
|
|
|
|
|
if parent_frame:
|
|
|
|
|
# 清空父框架中的所有组件
|
|
|
|
|
for widget in parent_frame.winfo_children():
|
|
|
|
|
widget.destroy()
|
|
|
|
|
container = parent_frame
|
|
|
|
|
else:
|
|
|
|
|
# 兼容旧代码:创建新窗口
|
|
|
|
|
container = ctk.CTkToplevel(self.root)
|
|
|
|
|
container.title("我的待办事项")
|
|
|
|
|
container.geometry("800x600")
|
|
|
|
|
container.configure(fg_color="#1e1e1e")
|
|
|
|
|
|
|
|
|
|
# 检查用户是否登录
|
|
|
|
|
current_user_info = self.app.auth.get_current_user(self.app.current_session_id)
|
|
|
|
|
if not current_user_info:
|
|
|
|
|
ctk.CTkLabel(container, text="请先登录", text_color="#ffffff").pack(pady=20)
|
|
|
|
|
if not parent_frame:
|
|
|
|
|
container.destroy()
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 验证会话有效性
|
|
|
|
|
valid, username = self.app.auth.validate_session(self.app.current_session_id)
|
|
|
|
|
if not valid:
|
|
|
|
|
ctk.CTkLabel(container, text="请先登录", text_color="#ffffff").pack(pady=20)
|
|
|
|
|
if not parent_frame:
|
|
|
|
|
container.destroy()
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 更新当前会话ID
|
|
|
|
|
self.update_session_id(self.app.current_session_id)
|
|
|
|
|
|
|
|
|
|
# 加载用户待办事项
|
|
|
|
|
todos = self.app.auth.load_todos(self.app.current_session_id)
|
|
|
|
|
|
|
|
|
|
# 如果没有待办事项数据,添加一些示例数据
|
|
|
|
|
if not todos:
|
|
|
|
|
# 创建示例待办事项
|
|
|
|
|
example_todos = [
|
|
|
|
|
{
|
|
|
|
|
"id": "example-1",
|
|
|
|
|
"title": "完成项目报告",
|
|
|
|
|
"description": "完成季度项目进展报告",
|
|
|
|
|
"due_date": "2025-12-15",
|
|
|
|
|
"priority": "high",
|
|
|
|
|
"status": "pending",
|
|
|
|
|
"created_at": datetime.now().isoformat()
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"id": "example-2",
|
|
|
|
|
"title": "购买生活用品",
|
|
|
|
|
"description": "购买牛奶、鸡蛋、面包",
|
|
|
|
|
"due_date": "2025-12-14",
|
|
|
|
|
"priority": "medium",
|
|
|
|
|
"status": "pending",
|
|
|
|
|
"created_at": datetime.now().isoformat()
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"id": "example-3",
|
|
|
|
|
"title": "阅读技术文档",
|
|
|
|
|
"description": "阅读新的API文档",
|
|
|
|
|
"due_date": "2025-12-16",
|
|
|
|
|
"priority": "low",
|
|
|
|
|
"status": "pending",
|
|
|
|
|
"created_at": datetime.now().isoformat()
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
# 保存示例待办事项
|
|
|
|
|
self.app.auth.save_todos(self.app.current_session_id, example_todos)
|
|
|
|
|
todos = example_todos
|
|
|
|
|
|
|
|
|
|
# 按优先级排序(高->中->低)
|
|
|
|
|
priority_order = {"high": 0, "medium": 1, "low": 2}
|
|
|
|
|
todos.sort(key=lambda x: priority_order.get(x["priority"], 1))
|
|
|
|
|
|
|
|
|
|
# 创建左侧待办事项列表区域
|
|
|
|
|
left_frame = ctk.CTkFrame(container, width=300, corner_radius=0)
|
|
|
|
|
left_frame.pack(side="left", fill="y", padx=(0, 10), pady=10)
|
|
|
|
|
|
|
|
|
|
# 待办事项列表标题
|
|
|
|
|
list_title = ctk.CTkLabel(left_frame, text="我的待办事项", font=ctk.CTkFont(size=18, weight="bold"))
|
|
|
|
|
list_title.pack(pady=10)
|
|
|
|
|
|
|
|
|
|
# 创建待办事项列表
|
|
|
|
|
self.todo_listbox = tk.Listbox(left_frame, selectmode=tk.SINGLE, bg="#1e1e1e", fg="#ffffff",
|
|
|
|
|
selectbackground="#3a3a3a", selectforeground="#ffffff",
|
|
|
|
|
font=ctk.CTkFont(size=27))
|
|
|
|
|
self.todo_listbox.pack(fill="both", expand=True, padx=10, pady=(0, 10))
|
|
|
|
|
|
|
|
|
|
# 添加待办事项到列表
|
|
|
|
|
for i, todo in enumerate(todos):
|
|
|
|
|
status = "✅" if todo["status"] == "completed" else "⬜" # 状态图标
|
|
|
|
|
priority_color = "🔴" if todo["priority"] == "high" else "🟡" if todo["priority"] == "medium" else "🟢" # 优先级颜色
|
|
|
|
|
display_text = f"{status} {priority_color} {i+1}. {todo['title']}"
|
|
|
|
|
if todo['due_date']:
|
|
|
|
|
display_text += f" - {todo['due_date']}"
|
|
|
|
|
self.todo_listbox.insert(tk.END, display_text)
|
|
|
|
|
|
|
|
|
|
# 创建右侧待办事项详情区域
|
|
|
|
|
right_frame = ctk.CTkFrame(container, corner_radius=0)
|
|
|
|
|
right_frame.pack(side="right", fill="both", expand=True, padx=(0, 10), pady=10)
|
|
|
|
|
|
|
|
|
|
# 待办事项详情标题
|
|
|
|
|
detail_title = ctk.CTkLabel(right_frame, text="待办事项详情", font=ctk.CTkFont(size=18, weight="bold"))
|
|
|
|
|
detail_title.pack(pady=10)
|
|
|
|
|
except Exception as e:
|
|
|
|
|
# 记录错误日志
|
|
|
|
|
username = self._get_username()
|
|
|
|
|
if self.logger:
|
|
|
|
|
self.logger.log("error", f"显示待办事项界面失败: {e}", username)
|
|
|
|
|
messagebox.showerror("错误", f"显示待办事项界面失败: {str(e)}")
|
|
|
|
|
|
|
|
|
|
# 创建待办事项详情滚动框架(气泡式显示)
|
|
|
|
|
self.todo_detail_frame = ctk.CTkScrollableFrame(right_frame, bg_color="#252525", fg_color="#252525",
|
|
|
|
|
corner_radius=10)
|
|
|
|
|
self.todo_detail_frame.pack(fill="both", expand=True, padx=10, pady=(0, 10))
|
|
|
|
|
|
|
|
|
|
# 绑定列表选择事件
|
|
|
|
|
self.todo_listbox.bind("<<ListboxSelect>>", lambda e: self._on_todo_select(e, todos))
|
|
|
|
|
|
|
|
|
|
# 创建操作按钮框架
|
|
|
|
|
button_frame = ctk.CTkFrame(right_frame)
|
|
|
|
|
button_frame.pack(pady=10, padx=10, fill="x")
|
|
|
|
|
|
|
|
|
|
# 创建新待办按钮
|
|
|
|
|
create_button = ctk.CTkButton(button_frame, text="创建新待办",
|
|
|
|
|
command=lambda: self._create_new_todo(todo_window),
|
|
|
|
|
fg_color="#009688", hover_color="#00796b")
|
|
|
|
|
create_button.pack(side="left", expand=True, padx=5)
|
|
|
|
|
|
|
|
|
|
# 标记为完成按钮
|
|
|
|
|
complete_button = ctk.CTkButton(button_frame, text="标记为完成",
|
|
|
|
|
command=lambda: self._complete_todo(todos, todo_window),
|
|
|
|
|
fg_color="#34a853", hover_color="#2d9d48")
|
|
|
|
|
complete_button.pack(side="left", expand=True, padx=5)
|
|
|
|
|
|
|
|
|
|
# 编辑按钮
|
|
|
|
|
edit_button = ctk.CTkButton(button_frame, text="编辑待办",
|
|
|
|
|
command=lambda: self._edit_todo(todos, todo_window),
|
|
|
|
|
fg_color="#ff9800", hover_color="#f57c00")
|
|
|
|
|
edit_button.pack(side="left", expand=True, padx=5)
|
|
|
|
|
|
|
|
|
|
# 删除待办事项按钮
|
|
|
|
|
delete_button = ctk.CTkButton(button_frame, text="删除选中项",
|
|
|
|
|
command=lambda: self._delete_todo(todos, todo_window),
|
|
|
|
|
fg_color="#d13438", hover_color="#b32d30")
|
|
|
|
|
delete_button.pack(side="right", expand=True, padx=5)
|
|
|
|
|
|
|
|
|
|
def _get_username(self):
|
|
|
|
|
"""获取当前会话的用户名
|
|
|
|
|
|
|
|
|
|
Returns:
|
|
|
|
|
str: 用户名或会话ID
|
|
|
|
|
"""
|
|
|
|
|
if not self.current_session_id:
|
|
|
|
|
return "unknown"
|
|
|
|
|
|
|
|
|
|
username = self.current_session_id
|
|
|
|
|
if self.auth:
|
|
|
|
|
try:
|
|
|
|
|
valid, username = self.auth.validate_session(self.current_session_id)
|
|
|
|
|
if not valid:
|
|
|
|
|
username = self.current_session_id.split("_")[0] if "_" in self.current_session_id else self.current_session_id
|
|
|
|
|
except Exception as e:
|
|
|
|
|
username = self.current_session_id.split("_")[0] if "_" in self.current_session_id else self.current_session_id
|
|
|
|
|
|
|
|
|
|
return username
|
|
|
|
|
|
|
|
|
|
def _create_new_todo(self, parent_window):
|
|
|
|
|
"""创建新待办事项的弹窗
|
|
|
|
|
|
|
|
|
|
创建一个模态窗口,用于输入新待办事项的详细信息,包括:
|
|
|
|
|
- 标题
|
|
|
|
|
- 描述
|
|
|
|
|
- 截止日期
|
|
|
|
|
- 优先级
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
parent_window: 父窗口对象,用于在创建成功后刷新待办事项列表
|
|
|
|
|
"""
|
|
|
|
|
# 创建新窗口
|
|
|
|
|
create_window = ctk.CTkToplevel(parent_window)
|
|
|
|
|
create_window.title("创建新待办事项")
|
|
|
|
|
create_window.geometry("500x500")
|
|
|
|
|
create_window.configure(fg_color="#1e1e1e")
|
|
|
|
|
create_window.transient(parent_window) # 设置为父窗口的子窗口
|
|
|
|
|
create_window.grab_set() # 模态窗口,阻止操作其他窗口
|
|
|
|
|
|
|
|
|
|
# 标题标签和输入框
|
|
|
|
|
title_label = ctk.CTkLabel(create_window, text="标题:", font=ctk.CTkFont(size=14))
|
|
|
|
|
title_label.pack(pady=(20, 5), padx=20, anchor="w")
|
|
|
|
|
|
|
|
|
|
title_entry = ctk.CTkEntry(create_window, width=400, height=40, font=ctk.CTkFont(size=14))
|
|
|
|
|
title_entry.pack(pady=(0, 15), padx=20)
|
|
|
|
|
|
|
|
|
|
# 描述标签和文本框
|
|
|
|
|
desc_label = ctk.CTkLabel(create_window, text="描述:", font=ctk.CTkFont(size=14))
|
|
|
|
|
desc_label.pack(pady=(10, 5), padx=20, anchor="w")
|
|
|
|
|
|
|
|
|
|
desc_text = scrolledtext.ScrolledText(create_window, width=50, height=10,
|
|
|
|
|
bg="#252525", fg="#ffffff",
|
|
|
|
|
font=ctk.CTkFont(size=14), wrap="word")
|
|
|
|
|
desc_text.pack(pady=(0, 15), padx=20)
|
|
|
|
|
|
|
|
|
|
# 截止日期标签和输入框
|
|
|
|
|
due_label = ctk.CTkLabel(create_window, text="截止日期 (YYYY-MM-DD):", font=ctk.CTkFont(size=14))
|
|
|
|
|
due_label.pack(pady=(10, 5), padx=20, anchor="w")
|
|
|
|
|
|
|
|
|
|
due_entry = ctk.CTkEntry(create_window, width=400, height=40, font=ctk.CTkFont(size=14))
|
|
|
|
|
due_entry.insert(0, datetime.now().strftime("%Y-%m-%d")) # 默认当前日期
|
|
|
|
|
due_entry.pack(pady=(0, 15), padx=20)
|
|
|
|
|
|
|
|
|
|
# 优先级标签和下拉菜单
|
|
|
|
|
priority_label = ctk.CTkLabel(create_window, text="优先级:", font=ctk.CTkFont(size=14))
|
|
|
|
|
priority_label.pack(pady=(10, 5), padx=20, anchor="w")
|
|
|
|
|
|
|
|
|
|
priority_var = tk.StringVar(value="medium") # 默认中优先级
|
|
|
|
|
priority_option = ctk.CTkOptionMenu(create_window, width=400, height=40, font=ctk.CTkFont(size=14),
|
|
|
|
|
variable=priority_var, values=["high", "medium", "low"])
|
|
|
|
|
priority_option.pack(pady=(0, 25), padx=20)
|
|
|
|
|
|
|
|
|
|
# 保存按钮的回调函数
|
|
|
|
|
def save_todo():
|
|
|
|
|
# 获取输入数据
|
|
|
|
|
title = title_entry.get()
|
|
|
|
|
description = desc_text.get(1.0, tk.END).strip()
|
|
|
|
|
due_date = due_entry.get()
|
|
|
|
|
priority = priority_var.get()
|
|
|
|
|
|
|
|
|
|
# 验证必填字段
|
|
|
|
|
if not title:
|
|
|
|
|
messagebox.showerror("错误", "标题不能为空")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if due_date:
|
|
|
|
|
try:
|
|
|
|
|
# 验证日期格式
|
|
|
|
|
datetime.strptime(due_date, "%Y-%m-%d")
|
|
|
|
|
except ValueError:
|
|
|
|
|
messagebox.showerror("错误", "日期格式不正确,请使用YYYY-MM-DD格式")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 创建待办事项数据
|
|
|
|
|
todo_data = {
|
|
|
|
|
"title": title,
|
|
|
|
|
"description": description,
|
|
|
|
|
"due_date": due_date,
|
|
|
|
|
"priority": priority
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 保存待办事项
|
|
|
|
|
success, todo = self.auth.create_todo(self.current_session_id, todo_data)
|
|
|
|
|
|
|
|
|
|
if success:
|
|
|
|
|
messagebox.showinfo("成功", "待办事项创建成功")
|
|
|
|
|
create_window.destroy()
|
|
|
|
|
# 重新加载待办事项列表
|
|
|
|
|
parent_window.destroy()
|
|
|
|
|
self.show_todos()
|
|
|
|
|
else:
|
|
|
|
|
messagebox.showerror("错误", "待办事项创建失败")
|
|
|
|
|
|
|
|
|
|
# 创建保存按钮
|
|
|
|
|
save_button = ctk.CTkButton(create_window, text="保存待办事项",
|
|
|
|
|
command=save_todo, fg_color="#009688", hover_color="#00796b")
|
|
|
|
|
save_button.pack(pady=10, padx=20, fill="x")
|
|
|
|
|
|
|
|
|
|
def _edit_todo(self, todos, parent_window):
|
|
|
|
|
"""编辑待办事项的弹窗
|
|
|
|
|
|
|
|
|
|
创建一个模态窗口,用于编辑现有待办事项的详细信息,包括:
|
|
|
|
|
- 标题
|
|
|
|
|
- 描述
|
|
|
|
|
- 截止日期
|
|
|
|
|
- 优先级
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
todos: 待办事项列表数据
|
|
|
|
|
parent_window: 父窗口对象,用于在编辑成功后刷新待办事项列表
|
|
|
|
|
"""
|
|
|
|
|
# 获取选中项的索引
|
|
|
|
|
selection = self.todo_listbox.curselection()
|
|
|
|
|
if not selection:
|
|
|
|
|
messagebox.showinfo("提示", "请先选择一个要编辑的待办事项")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
index = selection[0]
|
|
|
|
|
todo = todos[index]
|
|
|
|
|
|
|
|
|
|
# 创建新窗口
|
|
|
|
|
edit_window = ctk.CTkToplevel(parent_window)
|
|
|
|
|
edit_window.title("编辑待办事项")
|
|
|
|
|
edit_window.geometry("500x500")
|
|
|
|
|
edit_window.configure(fg_color="#1e1e1e")
|
|
|
|
|
edit_window.transient(parent_window) # 设置为父窗口的子窗口
|
|
|
|
|
edit_window.grab_set() # 模态窗口,阻止操作其他窗口
|
|
|
|
|
|
|
|
|
|
# 标题标签和输入框
|
|
|
|
|
title_label = ctk.CTkLabel(edit_window, text="标题:", font=ctk.CTkFont(size=14))
|
|
|
|
|
title_label.pack(pady=(20, 5), padx=20, anchor="w")
|
|
|
|
|
|
|
|
|
|
title_entry = ctk.CTkEntry(edit_window, width=400, height=40, font=ctk.CTkFont(size=14))
|
|
|
|
|
title_entry.insert(0, todo["title"]) # 预先填充现有标题
|
|
|
|
|
title_entry.pack(pady=(0, 15), padx=20)
|
|
|
|
|
|
|
|
|
|
# 描述标签和文本框
|
|
|
|
|
desc_label = ctk.CTkLabel(edit_window, text="描述:", font=ctk.CTkFont(size=14))
|
|
|
|
|
desc_label.pack(pady=(10, 5), padx=20, anchor="w")
|
|
|
|
|
|
|
|
|
|
desc_text = scrolledtext.ScrolledText(edit_window, width=50, height=10,
|
|
|
|
|
bg="#252525", fg="#ffffff",
|
|
|
|
|
font=ctk.CTkFont(size=14), wrap="word")
|
|
|
|
|
desc_text.insert(1.0, todo["description"]) # 预先填充现有描述
|
|
|
|
|
desc_text.pack(pady=(0, 15), padx=20)
|
|
|
|
|
|
|
|
|
|
# 截止日期标签和输入框
|
|
|
|
|
due_label = ctk.CTkLabel(edit_window, text="截止日期 (YYYY-MM-DD):", font=ctk.CTkFont(size=14))
|
|
|
|
|
due_label.pack(pady=(10, 5), padx=20, anchor="w")
|
|
|
|
|
|
|
|
|
|
due_entry = ctk.CTkEntry(edit_window, width=400, height=40, font=ctk.CTkFont(size=14))
|
|
|
|
|
due_entry.insert(0, todo["due_date"] if todo["due_date"] else "") # 预先填充现有截止日期
|
|
|
|
|
due_entry.pack(pady=(0, 15), padx=20)
|
|
|
|
|
|
|
|
|
|
# 优先级标签和下拉菜单
|
|
|
|
|
priority_label = ctk.CTkLabel(edit_window, text="优先级:", font=ctk.CTkFont(size=14))
|
|
|
|
|
priority_label.pack(pady=(10, 5), padx=20, anchor="w")
|
|
|
|
|
|
|
|
|
|
priority_var = tk.StringVar(value=todo["priority"]) # 预先选择现有优先级
|
|
|
|
|
priority_option = ctk.CTkOptionMenu(edit_window, width=400, height=40, font=ctk.CTkFont(size=14),
|
|
|
|
|
variable=priority_var, values=["high", "medium", "low"])
|
|
|
|
|
priority_option.pack(pady=(0, 25), padx=20)
|
|
|
|
|
|
|
|
|
|
# 保存按钮的回调函数
|
|
|
|
|
def save_todo():
|
|
|
|
|
# 获取输入数据
|
|
|
|
|
title = title_entry.get()
|
|
|
|
|
description = desc_text.get(1.0, tk.END).strip()
|
|
|
|
|
due_date = due_entry.get()
|
|
|
|
|
priority = priority_var.get()
|
|
|
|
|
|
|
|
|
|
# 验证必填字段
|
|
|
|
|
if not title:
|
|
|
|
|
messagebox.showerror("错误", "标题不能为空")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if due_date:
|
|
|
|
|
try:
|
|
|
|
|
# 验证日期格式
|
|
|
|
|
datetime.strptime(due_date, "%Y-%m-%d")
|
|
|
|
|
except ValueError:
|
|
|
|
|
messagebox.showerror("错误", "日期格式不正确,请使用YYYY-MM-DD格式")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 创建待办事项数据
|
|
|
|
|
todo_data = {
|
|
|
|
|
"title": title,
|
|
|
|
|
"description": description,
|
|
|
|
|
"due_date": due_date,
|
|
|
|
|
"priority": priority
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
# 更新待办事项
|
|
|
|
|
success = self.auth.update_todo(self.current_session_id, todo["id"], todo_data)
|
|
|
|
|
|
|
|
|
|
if success:
|
|
|
|
|
messagebox.showinfo("成功", "待办事项编辑成功")
|
|
|
|
|
edit_window.destroy()
|
|
|
|
|
# 重新加载待办事项列表
|
|
|
|
|
if content_parent:
|
|
|
|
|
self.show_todos(content_parent)
|
|
|
|
|
else:
|
|
|
|
|
parent_window.destroy()
|
|
|
|
|
self.show_todos()
|
|
|
|
|
else:
|
|
|
|
|
messagebox.showerror("错误", "待办事项编辑失败")
|
|
|
|
|
|
|
|
|
|
# 创建保存按钮
|
|
|
|
|
save_button = ctk.CTkButton(edit_window, text="保存修改",
|
|
|
|
|
command=save_todo, fg_color="#ff9800", hover_color="#f57c00")
|
|
|
|
|
save_button.pack(pady=10, padx=20, fill="x")
|
|
|
|
|
|
|
|
|
|
def _on_todo_select(self, event, todos):
|
|
|
|
|
"""待办事项列表选择事件处理
|
|
|
|
|
|
|
|
|
|
当待办事项列表中的项被选中时,在右侧详情区域显示该待办事项的详细信息。
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
event: 事件对象
|
|
|
|
|
todos: 待办事项列表数据
|
|
|
|
|
"""
|
|
|
|
|
# 获取选中项的索引
|
|
|
|
|
selection = self.todo_listbox.curselection()
|
|
|
|
|
if selection:
|
|
|
|
|
index = selection[0]
|
|
|
|
|
todo = todos[index]
|
|
|
|
|
|
|
|
|
|
# 清空当前详情框架中的所有组件
|
|
|
|
|
for widget in self.todo_detail_frame.winfo_children():
|
|
|
|
|
widget.destroy()
|
|
|
|
|
|
|
|
|
|
# 转换状态和优先级为中文显示
|
|
|
|
|
status = "已完成" if todo["status"] == "completed" else "待处理"
|
|
|
|
|
priority = "高" if todo["priority"] == "high" else "中" if todo["priority"] == "medium" else "低"
|
|
|
|
|
|
|
|
|
|
# 待办事项参数列表
|
|
|
|
|
todo_params = [
|
|
|
|
|
("标题", todo['title']),
|
|
|
|
|
("状态", status),
|
|
|
|
|
("优先级", priority),
|
|
|
|
|
("截止日期", todo['due_date']),
|
|
|
|
|
("描述", todo['description']),
|
|
|
|
|
("创建时间", todo['created_at'])
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
# 创建气泡式显示
|
|
|
|
|
for param_name, param_value in todo_params:
|
|
|
|
|
if param_value: # 只显示有值的参数
|
|
|
|
|
# 创建参数名称气泡(左对齐,浅灰色)
|
|
|
|
|
param_name_frame = ctk.CTkFrame(self.todo_detail_frame, fg_color="#3a3a3a", corner_radius=10)
|
|
|
|
|
param_name_frame.pack(fill="x", padx=10, pady=(10, 0), anchor="w")
|
|
|
|
|
|
|
|
|
|
param_name_label = ctk.CTkLabel(param_name_frame, text=param_name, font=ctk.CTkFont(size=14, weight="bold"),
|
|
|
|
|
fg_color="transparent", text_color="#ffffff")
|
|
|
|
|
param_name_label.pack(padx=10, pady=5)
|
|
|
|
|
|
|
|
|
|
# 创建参数值气泡(右对齐,深灰色)
|
|
|
|
|
param_value_frame = ctk.CTkFrame(self.todo_detail_frame, fg_color="#4a4a4a", corner_radius=10)
|
|
|
|
|
param_value_frame.pack(fill="x", padx=10, pady=(0, 10), anchor="e")
|
|
|
|
|
|
|
|
|
|
param_value_label = ctk.CTkLabel(param_value_frame, text=param_value, font=ctk.CTkFont(size=14),
|
|
|
|
|
fg_color="transparent", text_color="#ffffff")
|
|
|
|
|
param_value_label.pack(padx=10, pady=5)
|
|
|
|
|
|
|
|
|
|
def _complete_todo(self, todos, window, content_parent=None):
|
|
|
|
|
"""标记待办事项为完成
|
|
|
|
|
|
|
|
|
|
将用户选中的待办事项标记为已完成状态,并重新加载待办事项列表。
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
todos: 待办事项列表数据
|
|
|
|
|
window: 父窗口对象
|
|
|
|
|
content_parent: 内容父框架,用于在操作完成后刷新待办事项列表
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取选中项的索引
|
|
|
|
|
selection = self.todo_listbox.curselection()
|
|
|
|
|
if not selection:
|
|
|
|
|
messagebox.showinfo("提示", "请先选择一个要标记为已完成的待办事项")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
index = selection[0]
|
|
|
|
|
todo = todos[index]
|
|
|
|
|
todo_id = todo['id']
|
|
|
|
|
|
|
|
|
|
# 确认标记为完成
|
|
|
|
|
if messagebox.askyesno("确认", f"确定要标记待办事项 '{todo['title']}' 为完成吗?"):
|
|
|
|
|
# 调用认证对象的标记完成方法
|
|
|
|
|
success = self.auth.complete_todo(self.app.current_session_id, todo_id)
|
|
|
|
|
if success:
|
|
|
|
|
# 记录日志
|
|
|
|
|
username = self._get_username()
|
|
|
|
|
if self.logger:
|
|
|
|
|
self.logger.log("info", f"标记待办事项为已完成: {todo['title']}", username)
|
|
|
|
|
messagebox.showinfo("成功", "待办事项已标记为完成")
|
|
|
|
|
# 重新加载待办事项列表
|
|
|
|
|
if content_parent:
|
|
|
|
|
self.show_todos(content_parent)
|
|
|
|
|
else:
|
|
|
|
|
window.destroy()
|
|
|
|
|
self.show_todos()
|
|
|
|
|
else:
|
|
|
|
|
# 记录日志
|
|
|
|
|
username = self._get_username()
|
|
|
|
|
if self.logger:
|
|
|
|
|
self.logger.log("error", f"标记待办事项为已完成失败: {todo['title']}", username)
|
|
|
|
|
messagebox.showerror("错误", "操作失败")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
# 记录错误日志
|
|
|
|
|
username = self._get_username()
|
|
|
|
|
if self.logger:
|
|
|
|
|
self.logger.log("error", f"标记待办事项为已完成时发生错误: {e}", username)
|
|
|
|
|
messagebox.showerror("错误", f"标记待办事项为已完成时发生错误: {str(e)}")
|
|
|
|
|
|
|
|
|
|
def _delete_todo(self, todos, window, content_parent=None):
|
|
|
|
|
"""删除选中的待办事项
|
|
|
|
|
|
|
|
|
|
删除用户选中的待办事项,并重新加载待办事项列表。
|
|
|
|
|
|
|
|
|
|
Args:
|
|
|
|
|
todos: 待办事项列表数据
|
|
|
|
|
window: 父窗口对象
|
|
|
|
|
content_parent: 内容父框架,用于在删除后刷新待办事项列表
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
# 获取选中项的索引
|
|
|
|
|
selection = self.todo_listbox.curselection()
|
|
|
|
|
if not selection:
|
|
|
|
|
messagebox.showinfo("提示", "请先选择一个要删除的待办事项")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
index = selection[0]
|
|
|
|
|
todo = todos[index]
|
|
|
|
|
todo_id = todo['id']
|
|
|
|
|
|
|
|
|
|
# 确认删除
|
|
|
|
|
if messagebox.askyesno("确认", f"确定要删除待办事项 '{todo['title']}' 吗?"):
|
|
|
|
|
# 调用认证对象的删除待办事项方法
|
|
|
|
|
success = self.auth.delete_todo(self.app.current_session_id, todo_id)
|
|
|
|
|
if success:
|
|
|
|
|
# 记录日志
|
|
|
|
|
username = self._get_username()
|
|
|
|
|
if self.logger:
|
|
|
|
|
self.logger.log("info", f"删除待办事项成功: {todo['title']}", username)
|
|
|
|
|
messagebox.showinfo("成功", "待办事项删除成功")
|
|
|
|
|
# 重新加载待办事项列表
|
|
|
|
|
if content_parent:
|
|
|
|
|
self.show_todos(content_parent)
|
|
|
|
|
else:
|
|
|
|
|
window.destroy()
|
|
|
|
|
self.show_todos()
|
|
|
|
|
else:
|
|
|
|
|
# 记录日志
|
|
|
|
|
username = self._get_username()
|
|
|
|
|
if self.logger:
|
|
|
|
|
self.logger.log("error", f"删除待办事项失败: {todo['title']}", username)
|
|
|
|
|
messagebox.showerror("错误", "删除失败")
|
|
|
|
|
except Exception as e:
|
|
|
|
|
# 记录错误日志
|
|
|
|
|
username = self._get_username()
|
|
|
|
|
if self.logger:
|
|
|
|
|
self.logger.log("error", f"删除待办事项时发生错误: {e}", username)
|
|
|
|
|
messagebox.showerror("错误", f"删除待办事项时发生错误: {str(e)}")
|