增加了删除图片的功能。

This commit is contained in:
2025-11-27 16:42:13 +08:00
parent 970bf1c3ff
commit 88be46e79e
5 changed files with 216 additions and 1 deletions
+3 -1
View File
@@ -78,7 +78,9 @@ class DataManagementApp:
self.preview_panel = PreviewPanel(
main_container,
sqlite_manager=self.sqlite_manager,
on_download_callback=self._on_download
mysql_manager=self.mysql_manager,
on_download_callback=self._on_download,
on_delete_callback=self._on_refresh
)
self.preview_panel.pack(side="left", fill="both", expand=True, padx=5)
+4
View File
@@ -71,3 +71,7 @@ TIME_SHORTCUTS = {
'最近7天': 7,
'最近30天': 30
}
# ======================== 删除配置 ========================
# 删除模式: 'remote' (仅删除MySQL生产库)
DELETE_MODE = 'remote'
+33
View File
@@ -306,6 +306,39 @@ class MySQLManager:
finally:
session.close()
def delete_records(self, datasource: str, record_ids: List[int]) -> int:
"""
删除指定记录
Args:
datasource: 数据源
record_ids: 要删除的记录ID列表
Returns:
成功删除的记录数
"""
session: Session = self.Session()
try:
Model = self.get_model_by_datasource(datasource)
if Model is None:
return 0
# 批量删除
deleted_count = session.query(Model)\
.filter(Model.id.in_(record_ids))\
.delete(synchronize_session=False)
session.commit()
print(f"成功从MySQL删除 {deleted_count} 条记录 (数据源: {datasource})")
return deleted_count
except Exception as e:
session.rollback()
print(f"删除MySQL记录失败: {e}")
return 0
finally:
session.close()
def close(self):
"""关闭数据库连接"""
if self.engine:
@@ -224,6 +224,79 @@ class SQLiteManager:
print(f"获取下载统计失败: {e}")
return {}
def get_local_path(self, datasource: str, image_url: str) -> Optional[str]:
"""
获取图片的本地文件路径
Args:
datasource: 数据源
image_url: 图片URL
Returns:
本地文件路径,如果未下载则返回None
"""
try:
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute('''
SELECT local_path FROM download_history
WHERE datasource = ? AND image_url = ?
''', (datasource, image_url))
result = cursor.fetchone()
conn.close()
return result[0] if result else None
except Exception as e:
print(f"获取本地路径失败: {e}")
return None
def delete_download_record(self, datasource: str, image_url: str, delete_file: bool = True) -> bool:
"""
删除下载记录(可选删除物理文件)
Args:
datasource: 数据源
image_url: 图片URL
delete_file: 是否删除物理文件
Returns:
是否成功
"""
try:
# 如果需要删除文件,先获取文件路径
local_path = None
if delete_file:
local_path = self.get_local_path(datasource, image_url)
# 删除数据库记录
conn = sqlite3.connect(self.db_path)
cursor = conn.cursor()
cursor.execute('''
DELETE FROM download_history
WHERE datasource = ? AND image_url = ?
''', (datasource, image_url))
conn.commit()
conn.close()
# 删除物理文件
if delete_file and local_path and os.path.exists(local_path):
try:
os.remove(local_path)
print(f"已删除本地文件: {local_path}")
except Exception as e:
print(f"删除本地文件失败: {e}")
return True
except Exception as e:
print(f"删除下载记录失败: {e}")
return False
def clear_invalid_records(self) -> int:
"""清理文件不存在的下载记录"""
try:
+103
View File
@@ -151,12 +151,16 @@ class PreviewPanel(ctk.CTkFrame):
parent,
sqlite_manager,
on_download_callback: Callable,
mysql_manager=None,
on_delete_callback: Callable = None,
**kwargs
):
super().__init__(parent, **kwargs)
self.sqlite_manager = sqlite_manager
self.mysql_manager = mysql_manager
self.on_download_callback = on_download_callback
self.on_delete_callback = on_delete_callback
# 数据
self.current_data = []
@@ -235,6 +239,17 @@ class PreviewPanel(ctk.CTkFrame):
)
self.download_btn.pack(side="left", padx=5)
self.delete_btn = ctk.CTkButton(
left_buttons,
text="🗑️ 删除选中",
command=self._on_delete,
width=100,
height=30,
fg_color="red",
hover_color="darkred"
)
self.delete_btn.pack(side="left", padx=5)
# 分页控件
page_frame = ctk.CTkFrame(bottom_frame)
page_frame.pack(side="right", padx=10, pady=10)
@@ -383,6 +398,94 @@ class PreviewPanel(ctk.CTkFrame):
if self.on_download_callback:
self.on_download_callback(selected_data)
def _on_delete(self):
"""删除选中的图片"""
if not self.selected_ids:
messagebox.showwarning("提示", "请先选择要删除的图片")
return
# 获取选中的图片数据
selected_data = [
data for data in self.current_data
if data['id'] in self.selected_ids
]
# 二次确认(简化版,不显示删除范围选项)
count = len(selected_data)
confirm = messagebox.askyesno(
"确认删除",
f"确定要删除选中的 {count} 张图片吗?\n\n"
"此操作将:\n"
"1. 从MySQL数据库中删除记录\n"
"2. 删除已下载的本地文件(如有)\n\n"
"删除后无法恢复!",
icon='warning'
)
if not confirm:
return
# 在后台线程执行删除
def delete_thread():
try:
from config import DELETE_MODE
success_count = 0
failed_ids = []
for data in selected_data:
try:
# 模式B: 仅删除MySQL
if DELETE_MODE == 'remote':
# 删除MySQL记录
if self.mysql_manager:
deleted = self.mysql_manager.delete_records(
data['datasource'],
[data['id']]
)
if deleted > 0:
# 删除SQLite记录和本地文件
self.sqlite_manager.delete_download_record(
data['datasource'],
data['image_url'],
delete_file=True
)
success_count += 1
else:
failed_ids.append(data['id'])
else:
failed_ids.append(data['id'])
except Exception as e:
print(f"删除记录失败 (ID: {data['id']}): {e}")
failed_ids.append(data['id'])
# 更新UI(在主线程)
self.after(0, lambda: self._on_delete_complete(success_count, len(failed_ids)))
except Exception as e:
print(f"删除操作失败: {e}")
self.after(0, lambda: messagebox.showerror("错误", f"删除失败: {e}"))
threading.Thread(target=delete_thread, daemon=True).start()
def _on_delete_complete(self, success_count: int, failed_count: int):
"""删除完成回调"""
# 显示结果
if failed_count == 0:
messagebox.showinfo("删除成功", f"成功删除 {success_count} 张图片")
else:
messagebox.showwarning(
"部分删除失败",
f"成功删除 {success_count} 张图片\n失败 {failed_count}"
)
# 清空选中
self.selected_ids.clear()
# 调用删除回调,刷新数据
if self.on_delete_callback:
self.on_delete_callback()
def _on_preview(self, data: Dict):
"""查看大图"""
# 创建预览窗口