refactor: sync all rows with database on confirmation
Changed "确认删除" button to process ALL table rows (both checked and unchecked): - Checked rows (☑): Upsert MaterialCode and ManagerName to database - Unchecked rows (☐): Delete MaterialCode from database - Validation: Checked rows must have ManagerName This replaces the previous behavior that only processed checked items. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -29,16 +29,18 @@ class CheckboxTreeview(ttk.Treeview):
|
||||
- ☑ 选中
|
||||
"""
|
||||
|
||||
def __init__(self, parent, **kwargs):
|
||||
def __init__(self, parent, on_checkbox_change=None, **kwargs):
|
||||
"""初始化 CheckboxTreeview
|
||||
|
||||
Args:
|
||||
parent: 父容器
|
||||
on_checkbox_change: checkbox 状态改变时的回调函数
|
||||
**kwargs: 传递给 Treeview 的参数
|
||||
"""
|
||||
super().__init__(parent, **kwargs)
|
||||
self.checkboxes = {} # item_id -> bool
|
||||
self.checkbox_column = "选择"
|
||||
self.on_checkbox_change = on_checkbox_change # checkbox 状态改变回调
|
||||
|
||||
# 绑定点击事件
|
||||
self.bind("<Button-1>", self._on_click)
|
||||
@@ -56,7 +58,13 @@ class CheckboxTreeview(ttk.Treeview):
|
||||
if column == "#1" and item:
|
||||
# 切换 checkbox 状态
|
||||
current_state = self.checkboxes.get(item, False)
|
||||
self.set_checked(item, not current_state)
|
||||
new_state = not current_state
|
||||
self.set_checked(item, new_state)
|
||||
|
||||
# 通知父组件 checkbox 状态已改变
|
||||
if self.on_checkbox_change:
|
||||
self.on_checkbox_change(item, new_state)
|
||||
|
||||
return "break" # 阻止默认行为
|
||||
|
||||
def set_checked(self, item, checked: bool):
|
||||
@@ -283,7 +291,13 @@ class MaterialValidationTab(ttk.Frame):
|
||||
"""创建结果表格"""
|
||||
# 新列定义:选择、材料名称、材料代码、规格、型号、负责人
|
||||
columns = ("选择", "材料名称", "材料代码", "规格", "型号", "负责人")
|
||||
self.tree = CheckboxTreeview(parent, columns=columns, show="headings", height=10)
|
||||
self.tree = CheckboxTreeview(
|
||||
parent,
|
||||
columns=columns,
|
||||
show="headings",
|
||||
height=10,
|
||||
on_checkbox_change=self._sync_checkbox_by_material_code
|
||||
)
|
||||
|
||||
# 设置列标题和宽度
|
||||
self.tree.heading("选择", text="选择")
|
||||
@@ -369,6 +383,38 @@ class MaterialValidationTab(ttk.Frame):
|
||||
|
||||
self.log_text.info(f"已更新负责人: {current_value} -> {new_value}")
|
||||
|
||||
def _sync_checkbox_by_material_code(self, changed_item: str, new_state: bool):
|
||||
"""同步相同材料代码的所有记录的选择状态
|
||||
|
||||
当用户点击某个记录的 checkbox 时,自动将所有具有相同材料代码的记录
|
||||
的 checkbox 状态同步更新。
|
||||
|
||||
Args:
|
||||
changed_item: 被点击的 item ID
|
||||
new_state: 新的 checkbox 状态
|
||||
"""
|
||||
# 获取被点击行的材料代码
|
||||
values = self.tree.item(changed_item, "values")
|
||||
if not values or len(values) <= 2:
|
||||
return
|
||||
material_code = values[2] # 材料代码在第3列(索引2)
|
||||
|
||||
# 同步所有具有相同材料代码的记录
|
||||
synced_count = 0
|
||||
for item in self.tree.get_children():
|
||||
item_values = self.tree.item(item, "values")
|
||||
if item_values and len(item_values) > 2:
|
||||
if item_values[2] == material_code and item != changed_item:
|
||||
# 只更新状态不同的行,避免重复更新
|
||||
current_state = self.tree.checkboxes.get(item, False)
|
||||
if current_state != new_state:
|
||||
self.tree.set_checked(item, new_state)
|
||||
synced_count += 1
|
||||
|
||||
if synced_count > 0:
|
||||
action = "选中" if new_state else "取消选中"
|
||||
self.log_text.info(f"已同步 {synced_count} 条相同材料代码的记录{action}")
|
||||
|
||||
def _select_all(self, check: bool = True):
|
||||
"""全选或取消全选
|
||||
|
||||
@@ -558,87 +604,136 @@ class MaterialValidationTab(ttk.Frame):
|
||||
self._update_log(f"加载结果失败:{str(e)}", "ERROR")
|
||||
|
||||
def confirm_deletion(self):
|
||||
"""确认删除主流程"""
|
||||
# 获取选中的 item
|
||||
checked_items = self.tree.get_checked_items()
|
||||
"""确认删除主流程
|
||||
|
||||
if not checked_items:
|
||||
messagebox.showwarning("警告", "请先选择要删除的记录")
|
||||
处理所有行:
|
||||
- 已勾选的行:写入/更新到数据库(负责人不能为空)
|
||||
- 未勾选的行:从数据库中删除
|
||||
"""
|
||||
if not self.tree.get_children():
|
||||
messagebox.showwarning("警告", "没有可处理的数据")
|
||||
return
|
||||
|
||||
# 收集数据
|
||||
materials = []
|
||||
missing_manager = []
|
||||
# 收集所有数据
|
||||
to_upsert = [] # 需要写入/更新的记录
|
||||
to_delete = [] # 需要删除的记录
|
||||
missing_manager = [] # 缺少负责人的记录
|
||||
|
||||
for item in checked_items:
|
||||
for item in self.tree.get_children():
|
||||
values = self.tree.item(item, "values")
|
||||
is_checked = self.tree.checkboxes.get(item, False)
|
||||
material_code = values[2] if len(values) > 2 else ""
|
||||
manager_name = values[5] if len(values) > 5 else ""
|
||||
|
||||
# 跳过材料代码为空的行
|
||||
if not material_code or not material_code.strip():
|
||||
continue
|
||||
|
||||
if not manager_name or not manager_name.strip():
|
||||
missing_manager.append(material_code)
|
||||
else:
|
||||
materials.append({
|
||||
"material_code": material_code.strip(),
|
||||
"manager_name": manager_name.strip()
|
||||
})
|
||||
material_code = material_code.strip()
|
||||
|
||||
# 验证负责人
|
||||
if is_checked:
|
||||
# 已勾选:需要写入/更新
|
||||
if not manager_name or not manager_name.strip():
|
||||
missing_manager.append(material_code)
|
||||
else:
|
||||
to_upsert.append({
|
||||
"material_code": material_code,
|
||||
"manager_name": manager_name.strip()
|
||||
})
|
||||
else:
|
||||
# 未勾选:需要删除
|
||||
to_delete.append(material_code)
|
||||
|
||||
# 验证:已勾选的记录必须有负责人
|
||||
if missing_manager:
|
||||
msg = f"以下选中的记录缺少负责人信息,无法删除:\n\n" + \
|
||||
"\n".join(missing_manager[:10]) # 最多显示10个
|
||||
msg = f"以下已勾选的记录缺少负责人信息,无法保存:\n\n" + \
|
||||
"\n".join(missing_manager[:10])
|
||||
if len(missing_manager) > 10:
|
||||
msg += f"\n... 共 {len(missing_manager)} 条"
|
||||
messagebox.showwarning("警告", msg)
|
||||
return
|
||||
|
||||
if not materials:
|
||||
messagebox.showwarning("警告", "没有有效的记录可删除")
|
||||
# 构建确认消息
|
||||
confirm_parts = []
|
||||
if to_upsert:
|
||||
confirm_parts.append(f"写入/更新 {len(to_upsert)} 条记录")
|
||||
if to_delete:
|
||||
confirm_parts.append(f"删除 {len(to_delete)} 条记录")
|
||||
|
||||
if not to_upsert and not to_delete:
|
||||
messagebox.showinfo("提示", "没有需要处理的记录")
|
||||
return
|
||||
|
||||
# 显示确认对话框
|
||||
confirm_msg = f"确认要将 {len(materials)} 条记录写入删除表吗?\n\n"
|
||||
confirm_msg += "这些记录将被标记为待删除状态。"
|
||||
|
||||
if not messagebox.askyesno("确认删除", confirm_msg):
|
||||
confirm_msg = "确认以下操作吗?\n\n" + "\n".join(confirm_parts)
|
||||
if not messagebox.askyesno("确认操作", confirm_msg):
|
||||
return
|
||||
|
||||
# 执行删除操作(后台线程)
|
||||
self._update_log(f"开始处理 {len(materials)} 条记录...", "INFO")
|
||||
# 在后台线程中执行
|
||||
self._update_log(f"开始处理:写入/更新 {len(to_upsert)} 条,删除 {len(to_delete)} 条...", "INFO")
|
||||
|
||||
deletion_thread = threading.Thread(
|
||||
target=self._execute_deletion_in_background,
|
||||
args=(materials,),
|
||||
target=self._execute_sync_in_background,
|
||||
args=(to_upsert, to_delete),
|
||||
daemon=True,
|
||||
)
|
||||
deletion_thread.start()
|
||||
|
||||
def _execute_deletion_in_background(self, materials: list):
|
||||
"""后台线程执行删除操作"""
|
||||
def _execute_sync_in_background(self, to_upsert: list, to_delete: list):
|
||||
"""后台线程执行同步操作
|
||||
|
||||
Args:
|
||||
to_upsert: 需要写入/更新的记录列表
|
||||
to_delete: 需要删除的材料代码列表
|
||||
"""
|
||||
try:
|
||||
from db.materials_to_be_deleted_records_dao import MaterialsToBeDeletedDAO
|
||||
|
||||
dao = MaterialsToBeDeletedDAO()
|
||||
stats = dao.upsert_batch(materials)
|
||||
|
||||
self.after(0, lambda: self._deletion_complete(stats))
|
||||
# 执行写入/更新操作
|
||||
upsert_stats = {'total': 0, 'success': 0, 'failed': 0}
|
||||
if to_upsert:
|
||||
upsert_stats = dao.upsert_batch(to_upsert)
|
||||
|
||||
# 执行删除操作
|
||||
delete_count = 0
|
||||
if to_delete:
|
||||
delete_count = dao.delete_by_material_codes(to_delete)
|
||||
|
||||
# 组合统计信息
|
||||
combined_stats = {
|
||||
'upsert_success': upsert_stats['success'],
|
||||
'upsert_failed': upsert_stats['failed'],
|
||||
'deleted': delete_count
|
||||
}
|
||||
|
||||
self.after(0, lambda: self._sync_complete(combined_stats))
|
||||
|
||||
except Exception as e:
|
||||
self.after(0, lambda: self._update_log(f"删除操作失败:{str(e)}", "ERROR"))
|
||||
self.after(0, lambda: self._update_log(f"同步操作失败:{str(e)}", "ERROR"))
|
||||
|
||||
def _deletion_complete(self, stats: dict):
|
||||
"""删除完成回调,更新 UI"""
|
||||
total = stats.get('total', 0)
|
||||
success = stats.get('success', 0)
|
||||
failed = stats.get('failed', 0)
|
||||
def _sync_complete(self, stats: dict):
|
||||
"""同步完成回调,更新 UI
|
||||
|
||||
msg = f"删除操作完成!\n\n总计:{total} 条\n成功:{success} 条\n失败:{failed} 条"
|
||||
Args:
|
||||
stats: 包含 upsert_success, upsert_failed, deleted 的字典
|
||||
"""
|
||||
upsert_success = stats.get('upsert_success', 0)
|
||||
upsert_failed = stats.get('upsert_failed', 0)
|
||||
deleted = stats.get('deleted', 0)
|
||||
|
||||
if failed > 0:
|
||||
messagebox.showwarning("完成", msg)
|
||||
msg_parts = []
|
||||
if upsert_success > 0:
|
||||
msg_parts.append(f"写入/更新成功:{upsert_success} 条")
|
||||
if upsert_failed > 0:
|
||||
msg_parts.append(f"写入/更新失败:{upsert_failed} 条")
|
||||
if deleted > 0:
|
||||
msg_parts.append(f"删除成功:{deleted} 条")
|
||||
|
||||
msg = "同步操作完成!\n\n" + "\n".join(msg_parts)
|
||||
|
||||
if upsert_failed > 0:
|
||||
messagebox.showwarning("完成(部分失败)", msg)
|
||||
self._update_log(msg, "WARNING")
|
||||
else:
|
||||
messagebox.showinfo("成功", msg)
|
||||
|
||||
Reference in New Issue
Block a user