feat: 增加项目一键整理、打包及自动上传夸克/百度网盘功能

This commit is contained in:
王鹏
2026-04-15 10:24:38 +08:00
parent 245f9cdf41
commit a6d79d9a14
9 changed files with 933 additions and 19 deletions

397
gui.py
View File

@@ -6,6 +6,7 @@ import threading
import sys
import os
import subprocess
import re
# 添加当前目录到路径
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
@@ -17,6 +18,8 @@ from editor_gui import open_editor
from markdown_editor import ThemeManager
from db_manager import get_db_manager, reset_db_manager, ProjectOrder
from project_screenshot import ProjectScreenshotAutomation, ProjectConfig
from quark_uploader import QuarkUploader
from baidu_uploader import BaiduUploader
class YidaimaGUI:
@@ -50,6 +53,7 @@ class YidaimaGUI:
self.page_size = 20
self.total_items = 0
self.search_keyword = ""
self.search_zlzt = "全部"
# 创建界面
self.create_widgets()
@@ -214,11 +218,22 @@ class YidaimaGUI:
ttk.Label(search_frame, text="搜索名称:").grid(row=0, column=0, sticky=tk.W)
self.search_var = tk.StringVar()
self.search_entry = ttk.Entry(search_frame, textvariable=self.search_var, width=40)
self.search_entry = ttk.Entry(search_frame, textvariable=self.search_var, width=30)
self.search_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(10, 0))
ttk.Button(search_frame, text="搜索", command=self._search_projects, width=10).grid(row=0, column=2, padx=(10, 0))
ttk.Button(search_frame, text="刷新", command=self._refresh_projects, width=10).grid(row=0, column=3, padx=(10, 0))
ttk.Label(search_frame, text="整理状态:").grid(row=0, column=2, sticky=tk.W, padx=(10, 0))
self.search_zlzt_var = tk.StringVar(value="全部")
self.zlzt_combo = ttk.Combobox(
search_frame,
textvariable=self.search_zlzt_var,
values=("全部", "已整理", "可整理"),
state="readonly",
width=10
)
self.zlzt_combo.grid(row=0, column=3, sticky=tk.W, padx=(5, 0))
ttk.Button(search_frame, text="搜索", command=self._search_projects, width=10).grid(row=0, column=4, padx=(10, 0))
ttk.Button(search_frame, text="刷新", command=self._refresh_projects, width=10).grid(row=0, column=5, padx=(10, 0))
# === 数据表格区域 ===
table_frame = ttk.LabelFrame(self.tab_manage, text="项目列表", padding="5")
@@ -370,6 +385,36 @@ class YidaimaGUI:
# 测试连接按钮
ttk.Button(db_frame, text="🧪 测试连接", command=self._test_db_connection).grid(row=5, column=1, sticky=tk.W, pady=(10, 0))
row += 1
# === 夸克网盘配置 ===
quark_frame = ttk.LabelFrame(self.settings_frame, text="夸克网盘配置", padding="10")
quark_frame.grid(row=row, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 15))
quark_frame.columnconfigure(1, weight=1)
ttk.Label(quark_frame, text="Cookie 目录:").grid(row=0, column=0, sticky=tk.W)
self.setting_quark_cookies_var = tk.StringVar(value=self.config.get("quark.cookies_dir", os.path.join(os.getcwd(), "data", "quark_cookies")))
ttk.Entry(quark_frame, textvariable=self.setting_quark_cookies_var, width=50).grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(10, 0))
ttk.Label(quark_frame, text="根目录名称:").grid(row=1, column=0, sticky=tk.W, pady=(10, 0))
self.setting_quark_root_var = tk.StringVar(value=self.config.get("quark.root_path", "精品项目整理"))
ttk.Entry(quark_frame, textvariable=self.setting_quark_root_var, width=40).grid(row=1, column=1, sticky=(tk.W, tk.E), padx=(10, 0), pady=(10, 0))
row += 1
# === 百度网盘配置 ===
baidu_frame = ttk.LabelFrame(self.settings_frame, text="百度网盘配置", padding="10")
baidu_frame.grid(row=row, column=0, columnspan=2, sticky=(tk.W, tk.E), pady=(0, 15))
baidu_frame.columnconfigure(1, weight=1)
ttk.Label(baidu_frame, text="Cookie 目录:").grid(row=0, column=0, sticky=tk.W)
self.setting_baidu_cookies_var = tk.StringVar(value=self.config.get("baidu.cookies_dir", os.path.join(os.getcwd(), "data", "baidu_cookies")))
ttk.Entry(baidu_frame, textvariable=self.setting_baidu_cookies_var, width=50).grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(10, 0))
ttk.Label(baidu_frame, text="根目录名称:").grid(row=1, column=0, sticky=tk.W, pady=(10, 0))
self.setting_baidu_root_var = tk.StringVar(value=self.config.get("baidu.root_path", "精品项目整理"))
ttk.Entry(baidu_frame, textvariable=self.setting_baidu_root_var, width=40).grid(row=1, column=1, sticky=(tk.W, tk.E), padx=(10, 0), pady=(10, 0))
row += 1
# === 保存按钮 ===
@@ -541,7 +586,8 @@ class YidaimaGUI:
projects, total = self.db_manager.get_projects(
page=self.current_page,
page_size=self.page_size,
search_name=self.search_keyword if self.search_keyword else None
search_name=self.search_keyword if self.search_keyword else None,
zlzt=self.search_zlzt if self.search_zlzt and self.search_zlzt != "全部" else None
)
self.total_items = total
@@ -573,6 +619,7 @@ class YidaimaGUI:
"""搜索项目"""
try:
self.search_keyword = self.search_var.get().strip()
self.search_zlzt = self.search_zlzt_var.get()
self.current_page = 1
self._load_projects()
except Exception as e:
@@ -582,6 +629,8 @@ class YidaimaGUI:
"""刷新项目列表"""
self.search_keyword = ""
self.search_var.set("")
self.search_zlzt = "全部"
self.search_zlzt_var.set("全部")
self.current_page = 1
self._load_projects()
@@ -621,7 +670,7 @@ class YidaimaGUI:
menu = tk.Menu(self.root, tearoff=0)
# 变更已整理状态选项
new_zlzt = "已整理" if current_zlzt != "已整理" else "整理"
new_zlzt = "已整理" if current_zlzt != "已整理" else "整理"
menu.add_command(
label=f"变更状态: {new_zlzt}",
command=lambda: self._update_project_zlzt(name, new_zlzt)
@@ -766,6 +815,14 @@ class YidaimaGUI:
"database": self.setting_db_name_var.get(),
"user": self.setting_db_user_var.get(),
"password": self.setting_db_pass_var.get()
},
"quark": {
"cookies_dir": self.setting_quark_cookies_var.get(),
"root_path": self.setting_quark_root_var.get()
},
"baidu": {
"cookies_dir": self.setting_baidu_cookies_var.get(),
"root_path": self.setting_baidu_root_var.get()
}
}
@@ -807,6 +864,10 @@ class YidaimaGUI:
self.setting_db_name_var.set(self.config.get("database.database", "test"))
self.setting_db_user_var.set(self.config.get("database.user", "root"))
self.setting_db_pass_var.set(self.config.get("database.password", "123456"))
self.setting_quark_cookies_var.set(self.config.get("quark.cookies_dir", os.path.join(os.getcwd(), "data", "quark_cookies")))
self.setting_quark_root_var.set(self.config.get("quark.root_path", "精品项目整理"))
self.setting_baidu_cookies_var.set(self.config.get("baidu.cookies_dir", os.path.join(os.getcwd(), "data", "baidu_cookies")))
self.setting_baidu_root_var.set(self.config.get("baidu.root_path", "精品项目整理"))
# ==================== 项目运行截图 Tab 方法 ====================
@@ -827,21 +888,31 @@ class YidaimaGUI:
# 项目路径
ttk.Label(path_frame, text="项目路径:").grid(row=0, column=0, sticky=tk.W)
self.ps_project_path_var = tk.StringVar(value=ps_config.get("project_path", ""))
ps_project_path_entry = ttk.Entry(path_frame, textvariable=self.ps_project_path_var, width=60)
ps_project_path_entry = ttk.Entry(path_frame, textvariable=self.ps_project_path_var, width=50)
ps_project_path_entry.grid(row=0, column=1, sticky=(tk.W, tk.E), padx=(10, 0))
ttk.Button(path_frame, text="浏览...", command=self._browse_project_path, width=10).grid(row=0, column=2, padx=(10, 0))
btn_path_frame = ttk.Frame(path_frame)
btn_path_frame.grid(row=0, column=2, padx=(10, 0))
ttk.Button(btn_path_frame, text="选择项目", command=self._show_project_selector, width=10).pack(side=tk.LEFT)
ttk.Button(btn_path_frame, text="浏览...", command=self._browse_project_path, width=10).pack(side=tk.LEFT, padx=(5, 0))
# 项目名称 (name2)
ttk.Label(path_frame, text="项目名称:").grid(row=1, column=0, sticky=tk.W, pady=(10, 0))
self.ps_project_name_var = tk.StringVar(value=ps_config.get("project_name2", ""))
ps_project_name_entry = ttk.Entry(path_frame, textvariable=self.ps_project_name_var, width=60)
ps_project_name_entry.grid(row=1, column=1, sticky=(tk.W, tk.E), padx=(10, 0), pady=(10, 0))
# 桌面路径
ttk.Label(path_frame, text="桌面路径:").grid(row=1, column=0, sticky=tk.W, pady=(10, 0))
ttk.Label(path_frame, text="桌面路径:").grid(row=2, column=0, sticky=tk.W, pady=(10, 0))
self.ps_desktop_path_var = tk.StringVar(value=ps_config.get("desktop_path", r"C:\Users\南音\Desktop"))
ps_desktop_path_entry = ttk.Entry(path_frame, textvariable=self.ps_desktop_path_var, width=60)
ps_desktop_path_entry.grid(row=1, column=1, sticky=(tk.W, tk.E), padx=(10, 0), pady=(10, 0))
ps_desktop_path_entry.grid(row=2, column=1, sticky=(tk.W, tk.E), padx=(10, 0), pady=(10, 0))
# bat 文件夹名称
ttk.Label(path_frame, text="bat文件夹:").grid(row=2, column=0, sticky=tk.W, pady=(10, 0))
ttk.Label(path_frame, text="bat文件夹:").grid(row=3, column=0, sticky=tk.W, pady=(10, 0))
self.ps_bat_folder_var = tk.StringVar(value=ps_config.get("bat_folder", r"C:\Users\南音\Desktop\yidaima\bat"))
ps_bat_folder_entry = ttk.Entry(path_frame, textvariable=self.ps_bat_folder_var, width=60)
ps_bat_folder_entry.grid(row=2, column=1, sticky=(tk.W, tk.E), padx=(10, 0), pady=(10, 0))
ps_bat_folder_entry.grid(row=3, column=1, sticky=(tk.W, tk.E), padx=(10, 0), pady=(10, 0))
# 代码目标路径
ttk.Label(path_frame, text="代码目标路径:").grid(row=3, column=0, sticky=tk.W, pady=(10, 0))
@@ -878,7 +949,7 @@ class YidaimaGUI:
# 截图按钮
screenshot_frame = ttk.Frame(button_frame)
screenshot_frame.pack(fill=tk.X)
screenshot_frame.pack(fill=tk.X, pady=(0, 10))
ttk.Button(screenshot_frame, text="后台截图", command=self._ps_capture_admin, width=15).pack(side=tk.LEFT, padx=(0, 5))
ttk.Button(screenshot_frame, text="前台截图", command=self._ps_capture_front, width=15).pack(side=tk.LEFT, padx=(5, 5))
@@ -886,6 +957,14 @@ class YidaimaGUI:
ttk.Button(screenshot_frame, text="一键完整流程", command=self._ps_run_full_flow, width=20).pack(side=tk.LEFT, padx=(5, 5))
ttk.Button(screenshot_frame, text="关闭所有CMD", command=self._ps_close_all_cmd, width=15).pack(side=tk.LEFT, padx=(5, 0))
# 上传按钮
upload_frame = ttk.Frame(button_frame)
upload_frame.pack(fill=tk.X)
ttk.Button(upload_frame, text="整理项目", command=self._ps_organize_project, width=15).pack(side=tk.LEFT, padx=(0, 5))
ttk.Button(upload_frame, text="上传夸克网盘", command=self._ps_upload_quark, width=15).pack(side=tk.LEFT, padx=(5, 5))
ttk.Button(upload_frame, text="上传百度网盘", command=self._ps_upload_baidu, width=15).pack(side=tk.LEFT, padx=(5, 0))
# === 日志输出区域 ===
log_frame = ttk.LabelFrame(self.tab_screenshot, text="运行日志", padding="10")
log_frame.grid(row=2, column=0, sticky=(tk.W, tk.E, tk.N, tk.S), pady=(10, 0))
@@ -1374,6 +1453,300 @@ class YidaimaGUI:
threading.Thread(target=run, daemon=True).start()
def _ps_organize_project(self):
"""仅执行项目整理和打包"""
def run():
try:
self._ps_set_running(True)
self._ps_log("=" * 50)
self._ps_log("开始执行项目整理并打包...")
config = self._get_ps_config()
automation = ProjectScreenshotAutomation(config, log_callback=self._ps_log)
project_name = self.ps_project_name_var.get().strip()
if not project_name:
project_name = None
else:
self._ps_log(f"使用项目名称: {project_name}")
zip_file = automation.organize_and_zip_project(override_name=project_name)
if zip_file:
self._ps_log("=" * 50)
self._ps_log(f"项目整理完成!压缩包已生成:{zip_file}")
self.root.after(0, lambda: messagebox.showinfo("成功", f"项目整理并打包成功!\n压缩包已生成在桌面。"))
else:
self._ps_log("=" * 50)
self._ps_log("项目整理失败,请检查日志。")
self.root.after(0, lambda: messagebox.showerror("错误", "项目整理并打包失败。"))
except Exception as e:
self._ps_log(f"错误: {str(e)}")
finally:
self._ps_set_running(False)
threading.Thread(target=run, daemon=True).start()
def _ps_upload_quark(self):
"""仅上传到夸克网盘"""
def run():
try:
self._ps_set_running(True)
self._ps_log("=" * 50)
self._ps_log("准备上传到夸克网盘...")
# 检查压缩包是否存在
project_name = self.ps_project_name_var.get().strip()
desktop_path = self.ps_desktop_path_var.get()
if not project_name:
# 如果没填,尝试从路径拿
project_name = os.path.basename(self.ps_project_path_var.get().rstrip('/\\')).strip()
zip_file = os.path.join(desktop_path, f"{project_name}.zip")
if not os.path.exists(zip_file):
self._ps_log(f"错误:未找到压缩包,请先执行‘整理项目’\n路径:{zip_file}")
self.root.after(0, lambda: messagebox.showwarning("提示", "未找到对应的项目压缩包,请先执行‘整理项目’。"))
return
chrome_path = self.config.get("chrome.path", "")
cookies_dir = self.config.get("quark.cookies_dir", os.path.join(os.getcwd(), "data", "quark_cookies"))
root_path = self.config.get("quark.root_path", "精品项目整理")
uploader = QuarkUploader(chrome_path, cookies_dir, log_callback=self._ps_log)
success = uploader.upload_file(zip_file, project_name, root_path)
if success:
self._ps_log("=" * 50)
self._ps_log("夸克网盘上传成功!")
self.root.after(0, lambda: messagebox.showinfo("成功", "上传夸克网盘成功!"))
else:
self._ps_log("=" * 50)
self._ps_log("夸克网盘上传失败,请查看日志。")
self.root.after(0, lambda: messagebox.showerror("错误", "上传夸克网盘失败。"))
except Exception as e:
self._ps_log(f"错误: {str(e)}")
finally:
self._ps_set_running(False)
threading.Thread(target=run, daemon=True).start()
def _ps_upload_baidu(self):
"""上传到百度网盘"""
def run():
try:
self._ps_set_running(True)
self._ps_log("=" * 50)
self._ps_log("准备上传到百度网盘...")
project_name = self.ps_project_name_var.get().strip()
desktop_path = self.ps_desktop_path_var.get()
if not project_name:
project_name = os.path.basename(self.ps_project_path_var.get().rstrip('/\\')).strip()
zip_file = os.path.join(desktop_path, f"{project_name}.zip")
if not os.path.exists(zip_file):
self._ps_log(f"错误:未找到压缩包,请先执行‘整理项目’\n路径:{zip_file}")
self.root.after(0, lambda: messagebox.showwarning("提示", "未找到对应的项目压缩包,请先执行‘整理项目’。"))
return
chrome_path = self.config.get("chrome.path", "")
cookies_dir = self.config.get("baidu.cookies_dir", os.path.join(os.getcwd(), "data", "baidu_cookies"))
root_path = self.config.get("baidu.root_path", "精品项目整理")
uploader = BaiduUploader(chrome_path, cookies_dir, log_callback=self._ps_log)
success = uploader.upload_file(zip_file, project_name, root_path)
if success:
self._ps_log("=" * 50)
self._ps_log("百度网盘上传成功!")
self.root.after(0, lambda: messagebox.showinfo("成功", "上传百度网盘成功!"))
else:
self._ps_log("=" * 50)
self._ps_log("百度网盘上传失败,请查看日志。")
self.root.after(0, lambda: messagebox.showerror("错误", "上传百度网盘失败。"))
except Exception as e:
self._ps_log(f"错误: {str(e)}")
finally:
self._ps_set_running(False)
threading.Thread(target=run, daemon=True).start()
def _ps_organize_upload_cloud(self):
"""整理项目并上传网盘"""
def run():
try:
self._ps_set_running(True)
self._ps_log("=" * 50)
self._ps_log("开始整理项目并打包...")
config = self._get_ps_config()
automation = ProjectScreenshotAutomation(config, log_callback=self._ps_log)
# 直接从输入框获取项目名称,不需要再查数据库
project_name = self.ps_project_name_var.get().strip()
original_project_name = project_name # 保持原始名称用于后续更新数据库
if not project_name:
# 如果为空,则让 automation 尝试从路径提取
project_name = None
else:
self._ps_log(f"使用项目名称: {project_name}")
# 1. 执行整理打包
zip_file = automation.organize_and_zip_project(override_name=project_name)
if zip_file:
self._ps_log("=" * 50)
self._ps_log(f"项目整理完成!压缩包已生成:{zip_file}")
# 2. 上传到夸克网盘
self._ps_log("\n开始准备上传到夸克网盘...")
chrome_path = self.config.get("chrome.path", "")
quark_cookies_dir = self.config.get("quark.cookies_dir", os.path.join(os.getcwd(), "data", "quark_cookies"))
quark_root_path = self.config.get("quark.root_path", "精品项目整理")
# 再次确认项目名称(如果之前是 Noneautomation 会生成一个)
if not project_name:
project_name = os.path.basename(zip_file).replace(".zip", "")
uploader = QuarkUploader(
chrome_path=chrome_path,
cookies_dir=quark_cookies_dir,
log_callback=self._ps_log
)
success = uploader.upload_file(
file_path=zip_file,
target_folder_name=project_name,
root_path=quark_root_path
)
if success:
self._ps_log("=" * 50)
self._ps_log("项目已成功整理并上传到夸克网盘!")
self.root.after(0, lambda: messagebox.showinfo("成功", "项目整理并上传网盘成功!"))
else:
self._ps_log("=" * 50)
self._ps_log("上传网盘失败,请检查日志。")
self.root.after(0, lambda: messagebox.showerror("错误", "上传网盘失败,请检查日志。"))
else:
self._ps_log("=" * 50)
self._ps_log("项目整理失败,请检查日志。")
self.root.after(0, lambda: messagebox.showerror("错误", "项目整理并打包失败,请检查日志。"))
except Exception as e:
self._ps_log(f"错误: {str(e)}")
self.root.after(0, lambda: messagebox.showerror("错误", f"执行异常: {str(e)}"))
finally:
self._ps_set_running(False)
threading.Thread(target=run, daemon=True).start()
def _show_project_selector(self):
"""显示项目选择对话框"""
if not self.db_manager:
messagebox.showwarning("警告", "数据库未连接")
return
selector = tk.Toplevel(self.root)
selector.title("选择项目")
selector.geometry("800x600")
selector.transient(self.root)
selector.grab_set()
# 搜索框
search_frame = ttk.Frame(selector, padding="10")
search_frame.pack(fill=tk.X)
ttk.Label(search_frame, text="项目名称:").pack(side=tk.LEFT)
search_var = tk.StringVar()
search_entry = ttk.Entry(search_frame, textvariable=search_var, width=40)
search_entry.pack(side=tk.LEFT, padx=10)
# 结果列表
table_frame = ttk.Frame(selector, padding="10")
table_frame.pack(fill=tk.BOTH, expand=True)
columns = ("name", "name2", "paths", "zlzt")
tree = ttk.Treeview(table_frame, columns=columns, show="headings")
tree.heading("name", text="项目名称")
tree.heading("name2", text="名称2")
tree.heading("paths", text="项目路径")
tree.heading("zlzt", text="状态")
tree.column("name", width=200)
tree.column("name2", width=150)
tree.column("paths", width=300)
tree.column("zlzt", width=80)
scrollbar = ttk.Scrollbar(table_frame, orient=tk.VERTICAL, command=tree.yview)
tree.configure(yscrollcommand=scrollbar.set)
tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
def load_data():
keyword = search_var.get().strip()
# 清空
for item in tree.get_children():
tree.delete(item)
# 简单查询前100条
projects, _ = self.db_manager.get_projects(page=1, page_size=100, search_name=keyword if keyword else None)
for p in projects:
tree.insert("", tk.END, values=(p.name, p.name2, p.paths, p.zlzt))
# 搜索按钮
ttk.Button(search_frame, text="搜索", command=load_data).pack(side=tk.LEFT)
# 确认按钮
def on_select():
selected = tree.selection()
if not selected:
messagebox.showwarning("提示", "请先选择一个项目")
return
values = tree.item(selected[0], "values")
project_name = values[0]
project_name2 = values[1]
project_path = values[2]
# 更新主界面变量
self.ps_project_path_var.set(project_path)
# 使用名称2填入新增的项目名称输入框
self.ps_project_name_var.set(project_name2 if project_name2 else project_name)
# 同时更新发布页面的项目名称,方便后续对应
if project_name2 and '' in project_name2:
self.project_name_var.set(project_name2)
else:
self.project_name_var.set(project_name)
self._ps_log(f"已选择项目: {project_name}")
self._ps_log(f"更新项目路径: {project_path}")
self._ps_log(f"更新项目名称: {self.ps_project_name_var.get()}")
selector.destroy()
footer_frame = ttk.Frame(selector, padding="10")
footer_frame.pack(fill=tk.X)
ttk.Button(footer_frame, text="确定选择", command=on_select, width=15).pack(side=tk.RIGHT)
ttk.Button(footer_frame, text="取消", command=selector.destroy, width=15).pack(side=tk.RIGHT, padx=10)
# 初始加载
load_data()
# 绑定回车搜索
search_entry.bind("<Return>", lambda e: load_data())
# 绑定双击选择
tree.bind("<Double-1>", lambda e: on_select())
def main():
"""启动 GUI"""