feat: 初始化福州港日报管理系统
- 添加日报生成功能 (report_generator.py) - 添加 GUI 界面 (daily_report_gui.py) - 添加班次交接报告功能 (shift_report.py) - 集成飞书 API 获取排班信息 - 集成 Metabase 查询作业数据 - 生成 AGENTS.md 文档
This commit is contained in:
372
daily_report_gui.py
Normal file
372
daily_report_gui.py
Normal file
@@ -0,0 +1,372 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
日报展示和复制工具
|
||||
基于tkinter的GUI应用,用于展示日报信息和一键复制
|
||||
"""
|
||||
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, messagebox, scrolledtext
|
||||
from datetime import datetime, timedelta
|
||||
import sys
|
||||
import os
|
||||
import threading
|
||||
|
||||
# 添加项目根目录到路径
|
||||
project_root = os.path.dirname(os.path.abspath(__file__))
|
||||
if project_root not in sys.path:
|
||||
sys.path.insert(0, project_root)
|
||||
|
||||
|
||||
class DailyReportApp:
|
||||
"""日报展示和复制应用"""
|
||||
|
||||
def __init__(self, root):
|
||||
self.root = root
|
||||
self.root.title("日报管理系统")
|
||||
self.root.geometry("1400x900")
|
||||
self.root.minsize(1200, 700)
|
||||
|
||||
# 设置默认字体大小
|
||||
self.default_font_size = 14
|
||||
self.title_font_size = 16
|
||||
|
||||
# 创建主布局
|
||||
self.create_main_layout()
|
||||
|
||||
# 初始化数据
|
||||
self.current_report_date = None
|
||||
self.report_content = ""
|
||||
self.is_generating = False
|
||||
|
||||
def create_main_layout(self):
|
||||
"""创建主布局"""
|
||||
# 确保能导入项目模块
|
||||
project_root = os.path.dirname(os.path.abspath(__file__))
|
||||
if project_root not in sys.path:
|
||||
sys.path.insert(0, project_root)
|
||||
|
||||
# 主容器
|
||||
main_container = tk.Frame(self.root, padx=10, pady=10)
|
||||
main_container.pack(fill="both", expand=True)
|
||||
|
||||
# 配置权重
|
||||
main_container.columnconfigure(1, weight=3) # 中间区域
|
||||
main_container.columnconfigure(2, weight=1) # 右侧区域
|
||||
main_container.rowconfigure(1, weight=1)
|
||||
|
||||
# 1. 顶部提示区域
|
||||
self.create_notice_area(main_container)
|
||||
|
||||
# 2. 左侧日期选择区域
|
||||
self.create_left_panel(main_container)
|
||||
|
||||
# 3. 中间日报展示区域
|
||||
self.create_center_panel(main_container)
|
||||
|
||||
# 4. 右侧复制按钮区域
|
||||
self.create_right_panel(main_container)
|
||||
|
||||
def create_notice_area(self, parent):
|
||||
"""创建提示区域"""
|
||||
notice_frame = tk.Frame(parent, bd=2, relief="solid", bg="#FFF3E0")
|
||||
notice_frame.grid(row=0, column=0, columnspan=3, sticky="ew", pady=(0, 10))
|
||||
|
||||
# 静态提示标签
|
||||
self.notice_label = tk.Label(
|
||||
notice_frame,
|
||||
text="重要提示:本程序需在8:00过后运行,确保最后一条船的指令结束时间超过8点,以保证日报数据完整性!",
|
||||
fg="#FF6B6B",
|
||||
bg="#FFF3E0",
|
||||
font=("", self.title_font_size, "bold"),
|
||||
padx=10,
|
||||
pady=10,
|
||||
)
|
||||
self.notice_label.pack(fill="x", expand=True)
|
||||
|
||||
def create_left_panel(self, parent):
|
||||
"""创建左侧控制面板"""
|
||||
left_frame = tk.LabelFrame(
|
||||
parent,
|
||||
text="日期选择",
|
||||
padx=10,
|
||||
pady=10,
|
||||
font=("", self.title_font_size, "bold"),
|
||||
)
|
||||
left_frame.grid(row=1, column=0, sticky="nsew", padx=(0, 10))
|
||||
|
||||
# 昨日汇总按钮
|
||||
self.yesterday_btn = tk.Button(
|
||||
left_frame,
|
||||
text="昨日汇总",
|
||||
command=self.select_yesterday,
|
||||
font=("", self.default_font_size),
|
||||
padx=10,
|
||||
pady=5,
|
||||
bg="#4CAF50",
|
||||
fg="white",
|
||||
)
|
||||
self.yesterday_btn.pack(fill="x", pady=(0, 10))
|
||||
|
||||
# 日期选择器
|
||||
tk.Label(left_frame, text="选择日期:", font=("", self.default_font_size)).pack(
|
||||
anchor="w", pady=(10, 5)
|
||||
)
|
||||
|
||||
# 日期输入框
|
||||
self.date_var = tk.StringVar()
|
||||
self.date_entry = tk.Entry(
|
||||
left_frame, textvariable=self.date_var, font=("", self.default_font_size)
|
||||
)
|
||||
self.date_entry.pack(fill="x", pady=(0, 5))
|
||||
|
||||
# 日期提示
|
||||
tk.Label(
|
||||
left_frame,
|
||||
text="格式:YYYY-MM-DD",
|
||||
fg="gray",
|
||||
font=("", self.default_font_size - 2),
|
||||
).pack(anchor="w")
|
||||
|
||||
# 生成日报按钮
|
||||
self.generate_btn = tk.Button(
|
||||
left_frame,
|
||||
text="生成日报",
|
||||
command=self.generate_report_async,
|
||||
font=("", self.default_font_size),
|
||||
padx=10,
|
||||
pady=5,
|
||||
bg="#2196F3",
|
||||
fg="white",
|
||||
)
|
||||
self.generate_btn.pack(fill="x", pady=(20, 0))
|
||||
|
||||
# 当前选中日期标签
|
||||
self.selected_date_label = tk.Label(
|
||||
left_frame,
|
||||
text="当前选中:无",
|
||||
fg="#2196F3",
|
||||
font=("", self.default_font_size, "bold"),
|
||||
)
|
||||
self.selected_date_label.pack(anchor="w", pady=(20, 0))
|
||||
|
||||
def create_center_panel(self, parent):
|
||||
"""创建中间日报展示区域"""
|
||||
center_frame = tk.LabelFrame(
|
||||
parent,
|
||||
text="日报内容",
|
||||
padx=10,
|
||||
pady=10,
|
||||
font=("", self.title_font_size, "bold"),
|
||||
)
|
||||
center_frame.grid(row=1, column=1, sticky="nsew", padx=(0, 10))
|
||||
|
||||
# 日报信息文本框
|
||||
self.report_text = scrolledtext.ScrolledText(
|
||||
center_frame,
|
||||
wrap=tk.WORD,
|
||||
padx=10,
|
||||
pady=10,
|
||||
height=30,
|
||||
font=("", self.default_font_size),
|
||||
)
|
||||
self.report_text.pack(fill="both", expand=True)
|
||||
|
||||
# 默认提示文本
|
||||
self.report_text.insert(
|
||||
tk.END, '请点击左侧"昨日汇总"按钮,或选择日期后点击"生成日报"按钮...'
|
||||
)
|
||||
self.report_text.config(state=tk.DISABLED)
|
||||
|
||||
def create_right_panel(self, parent):
|
||||
"""创建右侧控制面板"""
|
||||
right_frame = tk.LabelFrame(
|
||||
parent,
|
||||
text="操作",
|
||||
padx=10,
|
||||
pady=10,
|
||||
font=("", self.title_font_size, "bold"),
|
||||
)
|
||||
right_frame.grid(row=1, column=2, sticky="nsew")
|
||||
|
||||
# 复制按钮
|
||||
self.copy_btn = tk.Button(
|
||||
right_frame,
|
||||
text="复制日报",
|
||||
command=self.copy_report,
|
||||
font=("", self.default_font_size),
|
||||
padx=10,
|
||||
pady=5,
|
||||
bg="#4CAF50",
|
||||
fg="white",
|
||||
)
|
||||
self.copy_btn.pack(fill="x", pady=(0, 20))
|
||||
|
||||
# 复制状态标签
|
||||
self.copy_status = tk.Label(
|
||||
right_frame, text="", font=("", self.default_font_size)
|
||||
)
|
||||
self.copy_status.pack(fill="x")
|
||||
|
||||
# 分隔线
|
||||
tk.Frame(right_frame, height=2, bg="gray").pack(fill="x", pady=20)
|
||||
|
||||
# 清空按钮
|
||||
self.clear_btn = tk.Button(
|
||||
right_frame,
|
||||
text="清空",
|
||||
command=self.clear_report,
|
||||
font=("", self.default_font_size),
|
||||
padx=10,
|
||||
pady=5,
|
||||
)
|
||||
self.clear_btn.pack(fill="x", pady=(0, 10))
|
||||
|
||||
# 退出按钮
|
||||
self.exit_btn = tk.Button(
|
||||
right_frame,
|
||||
text="退出",
|
||||
command=self.root.quit,
|
||||
font=("", self.default_font_size),
|
||||
padx=10,
|
||||
pady=5,
|
||||
)
|
||||
self.exit_btn.pack(fill="x")
|
||||
|
||||
def select_yesterday(self):
|
||||
"""选择昨天"""
|
||||
yesterday = datetime.now() - timedelta(days=1)
|
||||
self.date_var.set(yesterday.strftime("%Y-%m-%d"))
|
||||
self.selected_date_label.config(
|
||||
text=f"当前选中:昨天 ({yesterday.strftime('%Y-%m-%d')})"
|
||||
)
|
||||
self.generate_report_async()
|
||||
|
||||
def generate_report_async(self):
|
||||
"""异步生成日报(避免界面卡死)"""
|
||||
if self.is_generating:
|
||||
return
|
||||
|
||||
self.is_generating = True
|
||||
self.generate_btn.config(state=tk.DISABLED, text="生成中...")
|
||||
|
||||
# 在后台线程生成日报
|
||||
thread = threading.Thread(target=self._generate_report_thread)
|
||||
thread.daemon = True
|
||||
thread.start()
|
||||
|
||||
def _generate_report_thread(self):
|
||||
"""在后台线程中生成日报"""
|
||||
try:
|
||||
date_str = self.date_var.get().strip()
|
||||
|
||||
if not date_str:
|
||||
self.root.after(0, lambda: self._on_generate_error("请先选择日期"))
|
||||
return
|
||||
|
||||
try:
|
||||
report_date = datetime.strptime(date_str, "%Y-%m-%d")
|
||||
except ValueError:
|
||||
self.root.after(
|
||||
0,
|
||||
lambda: self._on_generate_error(
|
||||
"日期格式错误!请使用 YYYY-MM-DD 格式"
|
||||
),
|
||||
)
|
||||
return
|
||||
|
||||
# 更新UI
|
||||
self.root.after(
|
||||
0,
|
||||
lambda: self.selected_date_label.config(
|
||||
text=f"当前选中:{report_date.strftime('%Y-%m-%d')}"
|
||||
),
|
||||
)
|
||||
|
||||
# 生成日报
|
||||
try:
|
||||
from report_generator import DailyReportGenerator
|
||||
|
||||
generator = DailyReportGenerator()
|
||||
report = generator.generate_daily_report(report_date)
|
||||
self.root.after(0, lambda: self._on_generate_success(report))
|
||||
except Exception as e:
|
||||
error_msg = f"生成日报时出错:{str(e)}\n\n请确保:\n1. 环境变量配置正确\n2. Metabase 和飞书服务可访问\n3. 在8:00过后运行程序"
|
||||
self.root.after(0, lambda: self._on_generate_error(error_msg))
|
||||
|
||||
except Exception as e:
|
||||
self.root.after(0, lambda: self._on_generate_error(str(e)))
|
||||
|
||||
def _on_generate_success(self, report):
|
||||
"""生成成功回调"""
|
||||
self.report_content = report
|
||||
self.report_text.config(state=tk.NORMAL)
|
||||
self.report_text.delete(1.0, tk.END)
|
||||
self.report_text.insert(tk.END, report)
|
||||
self.report_text.config(state=tk.DISABLED)
|
||||
self._reset_generate_button()
|
||||
|
||||
def _on_generate_error(self, error_msg):
|
||||
"""生成失败回调"""
|
||||
self.report_content = error_msg
|
||||
self.report_text.config(state=tk.NORMAL)
|
||||
self.report_text.delete(1.0, tk.END)
|
||||
self.report_text.insert(tk.END, error_msg)
|
||||
self.report_text.config(state=tk.DISABLED)
|
||||
self._reset_generate_button()
|
||||
messagebox.showerror("错误", error_msg)
|
||||
|
||||
def _reset_generate_button(self):
|
||||
"""重置生成按钮状态"""
|
||||
self.is_generating = False
|
||||
self.generate_btn.config(state=tk.NORMAL, text="生成日报")
|
||||
|
||||
def copy_report(self):
|
||||
"""复制日报到剪贴板"""
|
||||
if (
|
||||
not self.report_content
|
||||
or self.report_content
|
||||
== '请点击左侧"昨日汇总"按钮,或选择日期后点击"生成日报"按钮...'
|
||||
):
|
||||
messagebox.showwarning("提示", "请先生成日报!")
|
||||
return
|
||||
|
||||
try:
|
||||
# 复制到剪贴板
|
||||
self.root.clipboard_clear()
|
||||
self.root.clipboard_append(self.report_content)
|
||||
|
||||
# 显示成功提示
|
||||
self.copy_status.config(text="已复制到剪贴板!", fg="#4CAF50")
|
||||
self.root.after(3000, lambda: self.copy_status.config(text=""))
|
||||
|
||||
except Exception as e:
|
||||
messagebox.showerror("错误", f"复制失败:{str(e)}")
|
||||
|
||||
def clear_report(self):
|
||||
"""清空日报内容"""
|
||||
self.report_content = ""
|
||||
self.report_text.config(state=tk.NORMAL)
|
||||
self.report_text.delete(1.0, tk.END)
|
||||
self.report_text.insert(
|
||||
tk.END, '请点击左侧"昨日汇总"按钮,或选择日期后点击"生成日报"按钮...'
|
||||
)
|
||||
self.report_text.config(state=tk.DISABLED)
|
||||
self.date_var.set("")
|
||||
self.selected_date_label.config(text="当前选中:无")
|
||||
self.copy_status.config(text="")
|
||||
|
||||
|
||||
def main():
|
||||
"""主函数"""
|
||||
# 创建主窗口
|
||||
root = tk.Tk()
|
||||
|
||||
# 创建应用
|
||||
app = DailyReportApp(root)
|
||||
|
||||
# 运行主循环
|
||||
root.mainloop()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user