|
|
import tkinter as tk
|
|
|
import customtkinter as ctk
|
|
|
from tkinter import messagebox
|
|
|
import tkinter.scrolledtext as scrolledtext
|
|
|
from datetime import datetime
|
|
|
from base_manager import BaseManager
|
|
|
|
|
|
"""
|
|
|
日程管理模块
|
|
|
|
|
|
该模块提供了日程管理的UI界面,包括显示日程列表、查看日程详情、创建新日程和删除日程等功能。
|
|
|
使用tkinter和customtkinter库实现现代化的用户界面。
|
|
|
"""
|
|
|
|
|
|
class ScheduleManager(BaseManager):
|
|
|
"""日程管理类
|
|
|
|
|
|
负责日程管理界面的创建和交互逻辑,包括:
|
|
|
1. 显示用户日程列表
|
|
|
2. 查看日程详细信息
|
|
|
3. 创建新日程
|
|
|
4. 删除日程
|
|
|
|
|
|
属性:
|
|
|
app: 主应用程序对象
|
|
|
root: 主窗口对象
|
|
|
auth: 用户认证对象
|
|
|
current_session_id: 当前会话ID
|
|
|
schedule_listbox: 日程列表框控件
|
|
|
schedule_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.schedule_listbox = None # 日程列表框
|
|
|
self.schedule_detail_text = None # 日程详情文本框
|
|
|
self._original_schedules = [] # 原始日程数据
|
|
|
self._current_schedules = [] # 当前显示的日程数据
|
|
|
|
|
|
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:
|
|
|
valid, username = self.auth.validate_session(session_id)
|
|
|
if not valid:
|
|
|
username = session_id.split("_")[0] if "_" in session_id else session_id
|
|
|
self.logger.log("info", f"更新日程管理器会话: {session_id}", username)
|
|
|
|
|
|
def show_schedules(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:
|
|
|
if not parent_frame: # 只有在新窗口模式下才需要销毁
|
|
|
container.destroy()
|
|
|
return
|
|
|
|
|
|
# 验证会话有效性
|
|
|
valid, username = self.app.auth.validate_session(self.app.current_session_id)
|
|
|
if not valid:
|
|
|
if not parent_frame: # 只有在新窗口模式下才需要销毁
|
|
|
container.destroy()
|
|
|
return
|
|
|
|
|
|
# 更新当前会话ID
|
|
|
self.update_session_id(self.app.current_session_id)
|
|
|
|
|
|
# 加载用户日程
|
|
|
schedules = self.app.auth.load_schedules(self.app.current_session_id)
|
|
|
|
|
|
# 如果没有日程数据,添加一些示例数据
|
|
|
if not schedules:
|
|
|
# 创建示例日程
|
|
|
example_schedules = [
|
|
|
{
|
|
|
"id": "example-1",
|
|
|
"title": "项目会议",
|
|
|
"description": "讨论项目进展和下一步计划",
|
|
|
"start_time": "2025-12-15T14:00:00",
|
|
|
"end_time": "2025-12-15T15:30:00",
|
|
|
"location": "会议室A",
|
|
|
"reminder": None,
|
|
|
"created_at": datetime.now().isoformat()
|
|
|
},
|
|
|
{
|
|
|
"id": "example-2",
|
|
|
"title": "健身时间",
|
|
|
"description": "每周健身计划",
|
|
|
"start_time": "2025-12-16T18:00:00",
|
|
|
"end_time": "2025-12-16T19:30:00",
|
|
|
"location": "健身房",
|
|
|
"reminder": None,
|
|
|
"created_at": datetime.now().isoformat()
|
|
|
}
|
|
|
]
|
|
|
|
|
|
# 保存示例日程
|
|
|
self.app.auth.save_schedules(self.app.current_session_id, example_schedules)
|
|
|
schedules = example_schedules
|
|
|
|
|
|
# 按开始时间排序日程
|
|
|
schedules.sort(key=lambda x: x["start_time"])
|
|
|
|
|
|
# 创建左侧日程列表区域
|
|
|
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.search_var = tk.StringVar()
|
|
|
search_frame = ctk.CTkFrame(left_frame)
|
|
|
search_frame.pack(fill="x", padx=10, pady=5)
|
|
|
|
|
|
search_label = ctk.CTkLabel(search_frame, text="搜索:", font=ctk.CTkFont(size=12))
|
|
|
search_label.pack(side="left", padx=(0, 5))
|
|
|
|
|
|
search_entry = ctk.CTkEntry(search_frame, textvariable=self.search_var, placeholder_text="输入关键字")
|
|
|
search_entry.pack(side="left", fill="x", expand=True, padx=(0, 5))
|
|
|
|
|
|
search_button = ctk.CTkButton(search_frame, text="搜索", width=60, command=self._on_search)
|
|
|
search_button.pack(side="left")
|
|
|
|
|
|
# 添加搜索快捷键
|
|
|
search_entry.bind('<Return>', lambda event: self._on_search())
|
|
|
|
|
|
# 创建日程列表
|
|
|
self.schedule_listbox = tk.Listbox(left_frame, selectmode=tk.SINGLE, bg="#1e1e1e", fg="#ffffff",
|
|
|
selectbackground="#3a3a3a", selectforeground="#ffffff",
|
|
|
font=ctk.CTkFont(size=27))
|
|
|
self.schedule_listbox.pack(fill="both", expand=True, padx=10, pady=(0, 10))
|
|
|
|
|
|
# 保存原始日程数据
|
|
|
self._original_schedules = schedules.copy()
|
|
|
|
|
|
# 添加日程到列表
|
|
|
for i, schedule in enumerate(schedules):
|
|
|
# 优化时间显示格式
|
|
|
try:
|
|
|
start_dt = datetime.fromisoformat(schedule['start_time'])
|
|
|
display_time = start_dt.strftime('%Y-%m-%d %H:%M')
|
|
|
except ValueError:
|
|
|
display_time = schedule['start_time']
|
|
|
display_text = f"{i+1}. {schedule['title']} - {display_time}"
|
|
|
self.schedule_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.schedule_detail_frame = ctk.CTkScrollableFrame(right_frame, fg_color="#252525",
|
|
|
scrollbar_button_color="#333333",
|
|
|
scrollbar_button_hover_color="#444444")
|
|
|
self.schedule_detail_frame.pack(fill="both", expand=True, padx=10, pady=(0, 10))
|
|
|
|
|
|
# 绑定列表选择事件
|
|
|
self.schedule_listbox.bind("<<ListboxSelect>>", lambda e: self._on_schedule_select(e, schedules))
|
|
|
|
|
|
# 保存当前显示的日程列表
|
|
|
self._current_schedules = schedules
|
|
|
|
|
|
# 创建按钮框架
|
|
|
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_schedule(container, parent_frame),
|
|
|
fg_color="#009688", hover_color="#00796b")
|
|
|
create_button.pack(side="left", padx=(0, 5), fill="x", expand=True)
|
|
|
|
|
|
|
|
|
|
|
|
# 编辑日程按钮
|
|
|
edit_button = ctk.CTkButton(button_frame, text="编辑日程",
|
|
|
command=lambda: self._edit_schedule(schedules, container, parent_frame),
|
|
|
fg_color="#ff9800", hover_color="#f57c00")
|
|
|
edit_button.pack(side="left", padx=5, fill="x", expand=True)
|
|
|
|
|
|
# 删除日程按钮
|
|
|
delete_button = ctk.CTkButton(button_frame, text="删除选中日程",
|
|
|
command=lambda: self._delete_schedule(schedules, container, parent_frame),
|
|
|
fg_color="#d13438", hover_color="#b32d30")
|
|
|
delete_button.pack(side="right", padx=(5, 0), fill="x", expand=True)
|
|
|
|
|
|
|
|
|
|
|
|
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_schedule(self, parent_window, content_parent=None):
|
|
|
"""创建新日程的弹窗
|
|
|
|
|
|
创建一个模态窗口,用于输入新日程的详细信息
|
|
|
|
|
|
Args:
|
|
|
parent_window: 父窗口对象
|
|
|
content_parent: 内容父框架,用于在创建成功后刷新日程列表
|
|
|
"""
|
|
|
self._show_schedule_dialog(parent_window, content_parent=content_parent)
|
|
|
|
|
|
def _edit_schedule(self, schedules, parent_window, content_parent=None):
|
|
|
"""编辑日程的弹窗
|
|
|
|
|
|
创建一个模态窗口,用于编辑选中日程的详细信息
|
|
|
|
|
|
Args:
|
|
|
schedules: 日程列表数据
|
|
|
parent_window: 父窗口对象
|
|
|
content_parent: 内容父框架,用于在编辑成功后刷新日程列表
|
|
|
"""
|
|
|
# 获取选中项的索引
|
|
|
selection = self.schedule_listbox.curselection()
|
|
|
if selection:
|
|
|
index = selection[0]
|
|
|
schedule = schedules[index]
|
|
|
self._show_schedule_dialog(parent_window, schedule, content_parent=content_parent)
|
|
|
else:
|
|
|
messagebox.showinfo("提示", "请先选择一个日程")
|
|
|
|
|
|
def _show_schedule_dialog(self, parent_window, schedule=None, content_parent=None):
|
|
|
"""显示日程对话框(创建或编辑)
|
|
|
|
|
|
Args:
|
|
|
parent_window: 父窗口对象
|
|
|
schedule: 可选参数,要编辑的日程数据。如果为None,则创建新日程。
|
|
|
content_parent: 内容父框架,用于在操作成功后刷新日程列表
|
|
|
"""
|
|
|
is_edit = schedule is not None
|
|
|
|
|
|
# 创建新窗口
|
|
|
dialog_window = ctk.CTkToplevel(parent_window)
|
|
|
dialog_window.title("编辑日程" if is_edit else "创建新日程")
|
|
|
dialog_window.geometry("500x500")
|
|
|
dialog_window.configure(fg_color="#1e1e1e")
|
|
|
dialog_window.transient(parent_window) # 设置为父窗口的子窗口
|
|
|
dialog_window.grab_set() # 模态窗口,阻止操作其他窗口
|
|
|
|
|
|
# 标题标签和输入框
|
|
|
title_label = ctk.CTkLabel(dialog_window, text="标题:", font=ctk.CTkFont(size=14))
|
|
|
title_label.pack(pady=(20, 5), padx=20, anchor="w")
|
|
|
|
|
|
title_entry = ctk.CTkEntry(dialog_window, width=400, height=40, font=ctk.CTkFont(size=14))
|
|
|
title_entry.pack(pady=(0, 15), padx=20)
|
|
|
|
|
|
# 描述标签和文本框
|
|
|
desc_label = ctk.CTkLabel(dialog_window, text="描述:", font=ctk.CTkFont(size=14))
|
|
|
desc_label.pack(pady=(10, 5), padx=20, anchor="w")
|
|
|
|
|
|
desc_text = scrolledtext.ScrolledText(dialog_window, width=50, height=10,
|
|
|
bg="#252525", fg="#ffffff",
|
|
|
font=ctk.CTkFont(size=14), wrap="word")
|
|
|
desc_text.pack(pady=(0, 15), padx=20)
|
|
|
|
|
|
# 开始时间标签和输入框
|
|
|
start_label = ctk.CTkLabel(dialog_window, text="开始时间 (YYYY-MM-DDTHH:MM:SS):", font=ctk.CTkFont(size=14))
|
|
|
start_label.pack(pady=(10, 5), padx=20, anchor="w")
|
|
|
|
|
|
start_entry = ctk.CTkEntry(dialog_window, width=400, height=40, font=ctk.CTkFont(size=14))
|
|
|
start_entry.pack(pady=(0, 15), padx=20)
|
|
|
|
|
|
# 结束时间标签和输入框
|
|
|
end_label = ctk.CTkLabel(dialog_window, text="结束时间 (YYYY-MM-DDTHH:MM:SS):", font=ctk.CTkFont(size=14))
|
|
|
end_label.pack(pady=(10, 5), padx=20, anchor="w")
|
|
|
|
|
|
end_entry = ctk.CTkEntry(dialog_window, width=400, height=40, font=ctk.CTkFont(size=14))
|
|
|
end_entry.pack(pady=(0, 15), padx=20)
|
|
|
|
|
|
# 地点标签和输入框
|
|
|
location_label = ctk.CTkLabel(dialog_window, text="地点:", font=ctk.CTkFont(size=14))
|
|
|
location_label.pack(pady=(10, 5), padx=20, anchor="w")
|
|
|
|
|
|
location_entry = ctk.CTkEntry(dialog_window, width=400, height=40, font=ctk.CTkFont(size=14))
|
|
|
location_entry.pack(pady=(0, 25), padx=20)
|
|
|
|
|
|
# 如果是编辑模式,填充现有数据
|
|
|
if is_edit:
|
|
|
title_entry.insert(0, schedule['title'])
|
|
|
desc_text.insert(1.0, schedule['description'])
|
|
|
start_entry.insert(0, schedule['start_time'])
|
|
|
end_entry.insert(0, schedule['end_time'])
|
|
|
location_entry.insert(0, schedule['location'])
|
|
|
else:
|
|
|
# 创建模式,使用当前时间作为默认值
|
|
|
start_entry.insert(0, datetime.now().isoformat())
|
|
|
end_entry.insert(0, datetime.now().isoformat())
|
|
|
|
|
|
# 保存按钮的回调函数
|
|
|
def save_schedule():
|
|
|
# 获取输入数据
|
|
|
title = title_entry.get()
|
|
|
description = desc_text.get(1.0, tk.END).strip()
|
|
|
start_time = start_entry.get()
|
|
|
end_time = end_entry.get()
|
|
|
location = location_entry.get()
|
|
|
|
|
|
# 验证必填字段
|
|
|
if not title:
|
|
|
messagebox.showerror("错误", "标题不能为空")
|
|
|
return
|
|
|
|
|
|
if not start_time:
|
|
|
messagebox.showerror("错误", "开始时间不能为空")
|
|
|
return
|
|
|
|
|
|
if not end_time:
|
|
|
messagebox.showerror("错误", "结束时间不能为空")
|
|
|
return
|
|
|
|
|
|
try:
|
|
|
# 验证时间格式
|
|
|
datetime.fromisoformat(start_time)
|
|
|
datetime.fromisoformat(end_time)
|
|
|
except ValueError:
|
|
|
messagebox.showerror("错误", "时间格式不正确,请使用YYYY-MM-DDTHH:MM:SS格式")
|
|
|
return
|
|
|
|
|
|
# 创建日程数据
|
|
|
schedule_data = {
|
|
|
"title": title,
|
|
|
"description": description,
|
|
|
"start_time": start_time,
|
|
|
"end_time": end_time,
|
|
|
"location": location
|
|
|
}
|
|
|
|
|
|
# 保存日程
|
|
|
if is_edit:
|
|
|
success = self.auth.update_schedule(self.current_session_id, schedule['id'], schedule_data)
|
|
|
action = "更新"
|
|
|
else:
|
|
|
success, new_schedule = self.auth.create_schedule(self.current_session_id, schedule_data)
|
|
|
action = "创建"
|
|
|
|
|
|
if success:
|
|
|
messagebox.showinfo("成功", f"日程{action}成功")
|
|
|
dialog_window.destroy()
|
|
|
# 重新加载日程列表
|
|
|
if content_parent:
|
|
|
self.show_schedules(content_parent)
|
|
|
else:
|
|
|
parent_window.destroy()
|
|
|
self.show_schedules()
|
|
|
else:
|
|
|
messagebox.showerror("错误", f"日程{action}失败")
|
|
|
|
|
|
# 创建保存按钮
|
|
|
save_button = ctk.CTkButton(dialog_window, text="保存日程",
|
|
|
command=save_schedule, fg_color="#009688", hover_color="#00796b")
|
|
|
save_button.pack(pady=10, padx=20, fill="x")
|
|
|
|
|
|
def _on_search(self):
|
|
|
"""
|
|
|
搜索按钮点击事件处理
|
|
|
|
|
|
根据用户输入的关键字搜索日程,并更新日程列表。
|
|
|
"""
|
|
|
# 获取搜索关键字
|
|
|
keyword = self.search_var.get().strip()
|
|
|
|
|
|
# 如果没有关键字,显示所有日程
|
|
|
if not keyword:
|
|
|
search_results = self._original_schedules
|
|
|
else:
|
|
|
# 搜索日程
|
|
|
search_results = []
|
|
|
for schedule in self._original_schedules:
|
|
|
if (keyword.lower() in schedule['title'].lower() or
|
|
|
keyword.lower() in schedule.get('description', '').lower() or
|
|
|
keyword.lower() in schedule.get('location', '').lower()):
|
|
|
search_results.append(schedule)
|
|
|
|
|
|
# 清空列表
|
|
|
self.schedule_listbox.delete(0, tk.END)
|
|
|
|
|
|
# 添加搜索结果到列表
|
|
|
for i, schedule in enumerate(search_results):
|
|
|
# 优化时间显示格式
|
|
|
try:
|
|
|
start_dt = datetime.fromisoformat(schedule['start_time'])
|
|
|
display_time = start_dt.strftime('%Y-%m-%d %H:%M')
|
|
|
except ValueError:
|
|
|
display_time = schedule['start_time']
|
|
|
display_text = f"{i+1}. {schedule['title']} - {display_time}"
|
|
|
self.schedule_listbox.insert(tk.END, display_text)
|
|
|
|
|
|
# 更新当前显示的日程列表
|
|
|
self._current_schedules = search_results
|
|
|
|
|
|
# 清空详情区域
|
|
|
for widget in self.schedule_detail_frame.winfo_children():
|
|
|
widget.destroy()
|
|
|
|
|
|
def _on_schedule_select(self, event, schedules):
|
|
|
"""
|
|
|
日程列表选择事件处理
|
|
|
|
|
|
当日程列表中的项被选中时,在右侧详情区域以气泡样式显示该日程的详细信息。
|
|
|
|
|
|
Args:
|
|
|
event: 事件对象
|
|
|
schedules: 日程列表数据(仅用于向后兼容)
|
|
|
"""
|
|
|
# 获取选中项的索引
|
|
|
selection = self.schedule_listbox.curselection()
|
|
|
if selection:
|
|
|
index = selection[0]
|
|
|
|
|
|
# 获取当前显示的日程列表
|
|
|
current_schedules = getattr(self, '_current_schedules', schedules)
|
|
|
|
|
|
# 获取选中的日程
|
|
|
schedule = current_schedules[index]
|
|
|
|
|
|
# 清空详情区域
|
|
|
for widget in self.schedule_detail_frame.winfo_children():
|
|
|
widget.destroy()
|
|
|
|
|
|
# 优化时间显示格式
|
|
|
try:
|
|
|
start_dt = datetime.fromisoformat(schedule['start_time'])
|
|
|
formatted_start = start_dt.strftime('%Y-%m-%d %H:%M')
|
|
|
except ValueError:
|
|
|
formatted_start = schedule['start_time']
|
|
|
|
|
|
try:
|
|
|
if schedule.get('end_time'):
|
|
|
end_dt = datetime.fromisoformat(schedule['end_time'])
|
|
|
formatted_end = end_dt.strftime('%Y-%m-%d %H:%M')
|
|
|
else:
|
|
|
formatted_end = schedule.get('end_time', '')
|
|
|
except ValueError:
|
|
|
formatted_end = schedule.get('end_time', '')
|
|
|
|
|
|
try:
|
|
|
created_dt = datetime.fromisoformat(schedule['created_at'])
|
|
|
formatted_created = created_dt.strftime('%Y-%m-%d %H:%M:%S')
|
|
|
except ValueError:
|
|
|
formatted_created = schedule['created_at']
|
|
|
|
|
|
# 定义日程参数列表
|
|
|
schedule_params = [
|
|
|
("标题", schedule['title']),
|
|
|
("描述", schedule['description']),
|
|
|
("开始时间", formatted_start),
|
|
|
("结束时间", formatted_end),
|
|
|
("地点", schedule['location']),
|
|
|
("创建时间", formatted_created)
|
|
|
]
|
|
|
|
|
|
# 显示日程参数为气泡对
|
|
|
for param_name, param_value in schedule_params:
|
|
|
# 只显示有值的参数
|
|
|
if param_value:
|
|
|
# 创建参数名称气泡(左对齐)
|
|
|
name_frame = ctk.CTkFrame(self.schedule_detail_frame, fg_color="#333333", corner_radius=10)
|
|
|
name_frame.pack(fill="x", padx=10, pady=(5, 0), anchor="w")
|
|
|
|
|
|
name_label = ctk.CTkLabel(name_frame, text=param_name,
|
|
|
font=ctk.CTkFont(size=14, weight="bold"),
|
|
|
text_color="#ffffff")
|
|
|
name_label.pack(padx=10, pady=5)
|
|
|
|
|
|
# 创建参数值气泡(右对齐)
|
|
|
value_frame = ctk.CTkFrame(self.schedule_detail_frame, fg_color="#424242", corner_radius=10)
|
|
|
value_frame.pack(fill="x", padx=10, pady=(0, 10), anchor="e")
|
|
|
|
|
|
value_label = ctk.CTkLabel(value_frame, text=param_value,
|
|
|
font=ctk.CTkFont(size=14),
|
|
|
text_color="#ffffff",
|
|
|
wraplength=300, justify="left")
|
|
|
value_label.pack(padx=10, pady=5)
|
|
|
|
|
|
def _delete_schedule(self, schedules, window, content_parent=None):
|
|
|
"""删除选中的日程
|
|
|
|
|
|
删除用户选中的日程,并重新加载日程列表。
|
|
|
|
|
|
Args:
|
|
|
schedules: 日程列表数据
|
|
|
window: 父窗口对象
|
|
|
content_parent: 内容父框架,用于在删除后刷新日程列表
|
|
|
"""
|
|
|
try:
|
|
|
# 获取选中项的索引
|
|
|
selection = self.schedule_listbox.curselection()
|
|
|
if not selection:
|
|
|
messagebox.showinfo("提示", "请先选择一个要删除的日程")
|
|
|
return
|
|
|
|
|
|
index = selection[0]
|
|
|
schedule = schedules[index]
|
|
|
schedule_id = schedule['id']
|
|
|
|
|
|
# 确认删除
|
|
|
if messagebox.askyesno("确认", f"确定要删除日程 '{schedule['title']}' 吗?"):
|
|
|
# 调用认证对象的删除日程方法
|
|
|
success = self.auth.delete_schedule(self.app.current_session_id, schedule_id)
|
|
|
if success:
|
|
|
# 记录日志
|
|
|
username = self._get_username()
|
|
|
if self.logger:
|
|
|
self.logger.log("info", f"删除日程成功: {schedule['title']}", username)
|
|
|
messagebox.showinfo("成功", "日程删除成功")
|
|
|
# 重新加载日程列表
|
|
|
if content_parent:
|
|
|
self.show_schedules(content_parent)
|
|
|
else:
|
|
|
window.destroy()
|
|
|
self.show_schedules()
|
|
|
else:
|
|
|
# 记录日志
|
|
|
username = self._get_username()
|
|
|
if self.logger:
|
|
|
self.logger.log("error", f"删除日程失败: {schedule['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)}")
|