增加了删除图片的功能。
This commit is contained in:
@@ -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)
|
||||
|
||||
|
||||
@@ -71,3 +71,7 @@ TIME_SHORTCUTS = {
|
||||
'最近7天': 7,
|
||||
'最近30天': 30
|
||||
}
|
||||
|
||||
# ======================== 删除配置 ========================
|
||||
# 删除模式: 'remote' (仅删除MySQL生产库)
|
||||
DELETE_MODE = 'remote'
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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):
|
||||
"""查看大图"""
|
||||
# 创建预览窗口
|
||||
|
||||
Reference in New Issue
Block a user