feat: add material type management dialog with checkbox filter
- Add MaterialTypeManagementDialog for managing MaterialsTypeToBeDeleted table - Implement checkbox-based filtering by manager name with dynamic manager list - Support CRUD operations: create, read, update, delete with local caching - Add "类型管理" button in material validation tab to open the dialog - Include select all/deselect all functionality for manager filters - Track pending changes and show unsaved changes warning Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
704
gui/material_type_management_dialog.py
Normal file
704
gui/material_type_management_dialog.py
Normal file
@@ -0,0 +1,704 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
物料类型管理对话框
|
||||
|
||||
提供对 MaterialsTypeToBeDeleted 表的管理功能:
|
||||
- 查看所有记录
|
||||
- 按负责人筛选
|
||||
- 新增/删除/编辑记录
|
||||
- 保存更改到数据库
|
||||
"""
|
||||
|
||||
import tkinter as tk
|
||||
from tkinter import ttk, messagebox, simpledialog
|
||||
from typing import List, Dict, Any, Optional, Tuple
|
||||
|
||||
|
||||
class MaterialTypeManagementDialog(tk.Toplevel):
|
||||
"""物料类型管理对话框
|
||||
|
||||
管理 MaterialsTypeToBeDeleted 表的 CRUD 操作。
|
||||
"""
|
||||
|
||||
def __init__(self, parent, title: str = "类型管理"):
|
||||
"""初始化对话框
|
||||
|
||||
Args:
|
||||
parent: 父窗口
|
||||
title: 窗口标题
|
||||
"""
|
||||
super().__init__(parent)
|
||||
self.title(title)
|
||||
self.geometry("900x600")
|
||||
|
||||
# 使窗口模态
|
||||
self.transient(parent)
|
||||
self.grab_set()
|
||||
|
||||
# 数据缓存
|
||||
self.original_data: List[Dict[str, Any]] = [] # 原始数据(从数据库加载)
|
||||
self.current_data: List[Dict[str, Any]] = [] # 当前显示的数据(可能已筛选)
|
||||
self.pending_changes: Dict[str, List[Dict[str, Any]]] = {
|
||||
'insert': [], # 待插入的记录
|
||||
'delete': [], # 待删除的记录
|
||||
'update': [] # 待更新的记录 (old, new)
|
||||
}
|
||||
|
||||
# 筛选相关
|
||||
self.managers: List[str] = [] # 所有的负责人列表
|
||||
self.manager_checkboxes: Dict[str, tk.BooleanVar] = {} # 负责人 -> Checkbox变量
|
||||
self.select_all_var: tk.BooleanVar = tk.BooleanVar(value=True) # 全选复选框变量
|
||||
|
||||
# 创建界面
|
||||
self._create_widgets()
|
||||
|
||||
# 加载数据
|
||||
self._load_data()
|
||||
|
||||
# 等待窗口关闭
|
||||
self.wait_window()
|
||||
|
||||
def _create_widgets(self):
|
||||
"""创建界面组件"""
|
||||
# 主容器
|
||||
main_container = ttk.Frame(self, padding=10)
|
||||
main_container.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
# 顶部:筛选区域
|
||||
filter_frame = ttk.LabelFrame(main_container, text="筛选(按负责人)", padding=10)
|
||||
filter_frame.pack(fill=tk.X, pady=(0, 10))
|
||||
|
||||
# 创建复选框容器(使用Canvas实现滚动)
|
||||
canvas_container = ttk.Frame(filter_frame)
|
||||
canvas_container.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
# Canvas和滚动条
|
||||
self.filter_canvas = tk.Canvas(canvas_container, height=80)
|
||||
scrollbar = ttk.Scrollbar(canvas_container, orient="vertical", command=self.filter_canvas.yview)
|
||||
self.filter_frame = ttk.Frame(self.filter_canvas)
|
||||
|
||||
self.filter_frame.bind(
|
||||
"<Configure>",
|
||||
lambda e: self.filter_canvas.configure(scrollregion=self.filter_canvas.bbox("all"))
|
||||
)
|
||||
|
||||
self.filter_canvas.create_window((0, 0), window=self.filter_frame, anchor="nw")
|
||||
self.filter_canvas.configure(yscrollcommand=scrollbar.set)
|
||||
|
||||
# 布局
|
||||
self.filter_canvas.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
|
||||
scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
|
||||
|
||||
# 鼠标滚轮支持
|
||||
def _on_mousewheel(event):
|
||||
self.filter_canvas.yview_scroll(int(-1*(event.delta/120)), "units")
|
||||
self.filter_canvas.bind("<MouseWheel>", _on_mousewheel) # Windows
|
||||
|
||||
# 复选框将在加载数据后动态创建
|
||||
self._create_filter_checkboxes()
|
||||
|
||||
# 快捷按钮
|
||||
button_frame = ttk.Frame(filter_frame)
|
||||
button_frame.pack(fill=tk.X, pady=(5, 0))
|
||||
|
||||
ttk.Button(
|
||||
button_frame,
|
||||
text="全选",
|
||||
command=self._select_all_managers
|
||||
).pack(side=tk.LEFT, padx=5)
|
||||
|
||||
ttk.Button(
|
||||
button_frame,
|
||||
text="取消全选",
|
||||
command=self._deselect_all_managers
|
||||
).pack(side=tk.LEFT, padx=5)
|
||||
|
||||
# 中部:数据表格
|
||||
table_frame = ttk.LabelFrame(main_container, text="数据列表", padding=10)
|
||||
table_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 10))
|
||||
|
||||
# 创建 Treeview
|
||||
columns = ("material_name", "manager_name")
|
||||
self.tree = ttk.Treeview(
|
||||
table_frame,
|
||||
columns=columns,
|
||||
show="headings",
|
||||
selectmode="extended"
|
||||
)
|
||||
|
||||
# 设置列标题和宽度
|
||||
self.tree.heading("material_name", text="物料名称")
|
||||
self.tree.heading("manager_name", text="负责人")
|
||||
|
||||
self.tree.column("material_name", width=400)
|
||||
self.tree.column("manager_name", width=200)
|
||||
|
||||
# 添加滚动条
|
||||
scrollbar_y = ttk.Scrollbar(table_frame, orient=tk.VERTICAL, command=self.tree.yview)
|
||||
scrollbar_x = ttk.Scrollbar(table_frame, orient=tk.HORIZONTAL, command=self.tree.xview)
|
||||
|
||||
self.tree.configure(
|
||||
yscrollcommand=scrollbar_y.set,
|
||||
xscrollcommand=scrollbar_x.set
|
||||
)
|
||||
|
||||
# 布局
|
||||
self.tree.grid(row=0, column=0, sticky="nsew")
|
||||
scrollbar_y.grid(row=0, column=1, sticky="ns")
|
||||
scrollbar_x.grid(row=1, column=0, sticky="ew")
|
||||
|
||||
table_frame.rowconfigure(0, weight=1)
|
||||
table_frame.columnconfigure(0, weight=1)
|
||||
|
||||
# 绑定双击事件用于编辑
|
||||
self.tree.bind("<Double-1>", self._on_item_double_click)
|
||||
|
||||
# 底部:操作按钮
|
||||
button_frame = ttk.Frame(main_container)
|
||||
button_frame.pack(fill=tk.X)
|
||||
|
||||
ttk.Button(
|
||||
button_frame,
|
||||
text="新增",
|
||||
command=self._add_record
|
||||
).pack(side=tk.LEFT, padx=5)
|
||||
|
||||
ttk.Button(
|
||||
button_frame,
|
||||
text="删除",
|
||||
command=self._delete_records
|
||||
).pack(side=tk.LEFT, padx=5)
|
||||
|
||||
ttk.Button(
|
||||
button_frame,
|
||||
text="编辑",
|
||||
command=self._edit_selected_record
|
||||
).pack(side=tk.LEFT, padx=5)
|
||||
|
||||
ttk.Button(
|
||||
button_frame,
|
||||
text="刷新",
|
||||
command=self._load_data
|
||||
).pack(side=tk.LEFT, padx=5)
|
||||
|
||||
# 待保存提示
|
||||
self.status_label = ttk.Label(button_frame, text="")
|
||||
self.status_label.pack(side=tk.LEFT, padx=20)
|
||||
|
||||
# 右侧按钮
|
||||
right_frame = ttk.Frame(button_frame)
|
||||
right_frame.pack(side=tk.RIGHT)
|
||||
|
||||
ttk.Button(
|
||||
right_frame,
|
||||
text="保存",
|
||||
command=self._save_changes
|
||||
).pack(side=tk.LEFT, padx=5)
|
||||
|
||||
ttk.Button(
|
||||
right_frame,
|
||||
text="关闭",
|
||||
command=self._close_dialog
|
||||
).pack(side=tk.LEFT, padx=5)
|
||||
|
||||
def _create_filter_checkboxes(self):
|
||||
"""创建筛选复选框(在数据加载后调用)"""
|
||||
# 清空现有复选框
|
||||
for widget in self.filter_frame.winfo_children():
|
||||
widget.destroy()
|
||||
self.manager_checkboxes.clear()
|
||||
|
||||
# 获取所有负责人并排序
|
||||
from db.materials_to_be_deleted_dao import MaterialsTypeToBeDeletedDAO
|
||||
dao = MaterialsTypeToBeDeletedDAO()
|
||||
self.managers = dao.get_managers()
|
||||
|
||||
# 如果没有数据,稍后会在 _load_data 中重新创建
|
||||
if not self.managers:
|
||||
ttk.Label(self.filter_frame, text="正在加载数据...").pack(anchor="w")
|
||||
return
|
||||
|
||||
# 计算列数(每列最多显示4个负责人)
|
||||
items_per_column = 6
|
||||
num_columns = max(1, (len(self.managers) + items_per_column - 1) // items_per_column)
|
||||
|
||||
# 创建全选复选框
|
||||
select_all_cb = ttk.Checkbutton(
|
||||
self.filter_frame,
|
||||
text="全选",
|
||||
variable=self.select_all_var,
|
||||
command=self._on_select_all_toggle
|
||||
)
|
||||
select_all_cb.grid(row=0, column=0, sticky="w", padx=5, pady=2)
|
||||
|
||||
# 为每个负责人创建复选框
|
||||
for idx, manager in enumerate(self.managers):
|
||||
var = tk.BooleanVar(value=True)
|
||||
self.manager_checkboxes[manager] = var
|
||||
|
||||
row = (idx // items_per_column) + 1
|
||||
col = idx % items_per_column
|
||||
|
||||
cb = ttk.Checkbutton(
|
||||
self.filter_frame,
|
||||
text=manager,
|
||||
variable=var,
|
||||
command=self._on_manager_checkbox_change
|
||||
)
|
||||
cb.grid(row=row, column=col, sticky="w", padx=5, pady=2)
|
||||
|
||||
def _on_select_all_toggle(self):
|
||||
"""全选复选框状态改变"""
|
||||
select_all = self.select_all_var.get()
|
||||
for var in self.manager_checkboxes.values():
|
||||
var.set(select_all)
|
||||
self._apply_filter()
|
||||
|
||||
def _on_manager_checkbox_change(self):
|
||||
"""单个负责人复选框状态改变"""
|
||||
# 检查是否所有负责人都被选中
|
||||
all_selected = all(var.get() for var in self.manager_checkboxes.values())
|
||||
self.select_all_var.set(all_selected)
|
||||
self._apply_filter()
|
||||
|
||||
def _select_all_managers(self):
|
||||
"""全选所有负责人"""
|
||||
self.select_all_var.set(True)
|
||||
for var in self.manager_checkboxes.values():
|
||||
var.set(True)
|
||||
self._apply_filter()
|
||||
|
||||
def _deselect_all_managers(self):
|
||||
"""取消全选"""
|
||||
self.select_all_var.set(False)
|
||||
for var in self.manager_checkboxes.values():
|
||||
var.set(False)
|
||||
self._apply_filter()
|
||||
|
||||
def _get_selected_managers(self) -> List[str]:
|
||||
"""获取选中的负责人列表"""
|
||||
return [
|
||||
manager for manager, var in self.manager_checkboxes.items()
|
||||
if var.get()
|
||||
]
|
||||
|
||||
def _load_data(self):
|
||||
"""从数据库加载数据"""
|
||||
try:
|
||||
from db.materials_to_be_deleted_dao import MaterialsTypeToBeDeletedDAO
|
||||
|
||||
dao = MaterialsTypeToBeDeletedDAO()
|
||||
self.original_data = dao.get_all_materials()
|
||||
self.current_data = list(self.original_data)
|
||||
|
||||
# 清空待保存的更改
|
||||
self.pending_changes = {'insert': [], 'delete': [], 'update': []}
|
||||
|
||||
# 重新创建筛选复选框(负责人列表可能变化)
|
||||
self._create_filter_checkboxes()
|
||||
|
||||
# 刷新显示
|
||||
self._apply_filter() # 使用 _apply_filter 而不是 _refresh_tree
|
||||
self._update_status()
|
||||
|
||||
except Exception as e:
|
||||
messagebox.showerror("错误", f"加载数据失败:{str(e)}")
|
||||
|
||||
def _refresh_tree(self):
|
||||
"""刷新表格显示"""
|
||||
# 清空表格
|
||||
for item in self.tree.get_children():
|
||||
self.tree.delete(item)
|
||||
|
||||
# 填充数据
|
||||
for record in self.current_data:
|
||||
self.tree.insert(
|
||||
"",
|
||||
tk.END,
|
||||
values=(
|
||||
record.get('MaterialName', ''),
|
||||
record.get('ManagerName', '')
|
||||
)
|
||||
)
|
||||
|
||||
def _apply_filter(self):
|
||||
"""应用筛选条件(基于选中的负责人复选框)"""
|
||||
selected_managers = self._get_selected_managers()
|
||||
|
||||
if not selected_managers:
|
||||
# 没有选中任何负责人,显示空表格
|
||||
self.current_data = []
|
||||
else:
|
||||
# 获取当前有效数据(原始数据减去已删除的,加上新插入的)
|
||||
deleted_set = {
|
||||
(r['MaterialName'], r['ManagerName'])
|
||||
for r in self.pending_changes['delete']
|
||||
}
|
||||
inserted = [
|
||||
r for r in self.pending_changes['insert']
|
||||
if (r['MaterialName'], r['ManagerName']) not in deleted_set
|
||||
]
|
||||
|
||||
base_data = [
|
||||
r for r in self.original_data
|
||||
if (r['MaterialName'], r['ManagerName']) not in deleted_set
|
||||
] + inserted
|
||||
|
||||
# 按选中的负责人筛选
|
||||
self.current_data = [
|
||||
r for r in base_data
|
||||
if r.get('ManagerName') in selected_managers
|
||||
]
|
||||
|
||||
self._refresh_tree()
|
||||
|
||||
def _add_record(self):
|
||||
"""添加新记录"""
|
||||
# 弹出输入对话框
|
||||
dialog = tk.Toplevel(self)
|
||||
dialog.title("新增记录")
|
||||
dialog.geometry("400x200")
|
||||
dialog.transient(self)
|
||||
dialog.grab_set()
|
||||
|
||||
# 物料名称
|
||||
ttk.Label(dialog, text="物料名称:").pack(pady=5)
|
||||
material_entry = ttk.Entry(dialog, width=40)
|
||||
material_entry.pack(pady=5)
|
||||
|
||||
# 负责人
|
||||
ttk.Label(dialog, text="负责人:").pack(pady=5)
|
||||
manager_entry = ttk.Entry(dialog, width=40)
|
||||
manager_entry.pack(pady=5)
|
||||
|
||||
# 按钮框架
|
||||
button_frame = ttk.Frame(dialog)
|
||||
button_frame.pack(pady=20)
|
||||
|
||||
result = {'material': '', 'manager': '', 'confirmed': False}
|
||||
|
||||
def on_confirm():
|
||||
material = material_entry.get().strip()
|
||||
manager = manager_entry.get().strip()
|
||||
|
||||
if not material:
|
||||
messagebox.showwarning("警告", "请输入物料名称")
|
||||
return
|
||||
if not manager:
|
||||
messagebox.showwarning("警告", "请输入负责人")
|
||||
return
|
||||
|
||||
result['material'] = material
|
||||
result['manager'] = manager
|
||||
result['confirmed'] = True
|
||||
dialog.destroy()
|
||||
|
||||
def on_cancel():
|
||||
dialog.destroy()
|
||||
|
||||
ttk.Button(button_frame, text="确定", command=on_confirm).pack(side=tk.LEFT, padx=5)
|
||||
ttk.Button(button_frame, text="取消", command=on_cancel).pack(side=tk.LEFT, padx=5)
|
||||
|
||||
# 设置焦点到第一个输入框
|
||||
material_entry.focus_set()
|
||||
|
||||
# 等待对话框关闭
|
||||
dialog.wait_window()
|
||||
|
||||
if result['confirmed']:
|
||||
# 检查是否已存在
|
||||
for record in self.current_data:
|
||||
if (record['MaterialName'] == result['material'] and
|
||||
record['ManagerName'] == result['manager']):
|
||||
messagebox.showinfo("提示", "该记录已存在")
|
||||
return
|
||||
|
||||
# 添加到待插入列表
|
||||
new_record = {
|
||||
'MaterialName': result['material'],
|
||||
'ManagerName': result['manager']
|
||||
}
|
||||
self.pending_changes['insert'].append(new_record)
|
||||
|
||||
# 立即显示在表格中
|
||||
self.current_data.append(new_record)
|
||||
self._refresh_tree()
|
||||
self._update_status()
|
||||
|
||||
def _delete_records(self):
|
||||
"""删除选中的记录"""
|
||||
selection = self.tree.selection()
|
||||
if not selection:
|
||||
messagebox.showwarning("警告", "请先选择要删除的记录")
|
||||
return
|
||||
|
||||
if not messagebox.askyesno("确认", f"确定要删除选中的 {len(selection)} 条记录吗?"):
|
||||
return
|
||||
|
||||
indices_to_delete = []
|
||||
for item in selection:
|
||||
values = self.tree.item(item, 'values')
|
||||
material_name = values[0]
|
||||
manager_name = values[1]
|
||||
|
||||
# 查找在 current_data 中的索引
|
||||
for idx, record in enumerate(self.current_data):
|
||||
if (record['MaterialName'] == material_name and
|
||||
record['ManagerName'] == manager_name):
|
||||
indices_to_delete.append(idx)
|
||||
|
||||
# 检查是否是新插入的记录
|
||||
is_new_insert = any(
|
||||
r['MaterialName'] == material_name and r['ManagerName'] == manager_name
|
||||
for r in self.pending_changes['insert']
|
||||
)
|
||||
|
||||
if is_new_insert:
|
||||
# 从待插入列表中移除
|
||||
self.pending_changes['insert'] = [
|
||||
r for r in self.pending_changes['insert']
|
||||
if not (r['MaterialName'] == material_name and
|
||||
r['ManagerName'] == manager_name)
|
||||
]
|
||||
else:
|
||||
# 添加到待删除列表
|
||||
self.pending_changes['delete'].append({
|
||||
'MaterialName': material_name,
|
||||
'ManagerName': manager_name
|
||||
})
|
||||
break
|
||||
|
||||
# 从 current_data 中删除(倒序删除,避免索引问题)
|
||||
for idx in sorted(indices_to_delete, reverse=True):
|
||||
del self.current_data[idx]
|
||||
|
||||
self._refresh_tree()
|
||||
self._update_status()
|
||||
|
||||
def _edit_selected_record(self):
|
||||
"""编辑选中的记录"""
|
||||
selection = self.tree.selection()
|
||||
if not selection:
|
||||
messagebox.showwarning("警告", "请先选择要编辑的记录")
|
||||
return
|
||||
|
||||
if len(selection) > 1:
|
||||
messagebox.showwarning("警告", "请只选择一条记录进行编辑")
|
||||
return
|
||||
|
||||
item = selection[0]
|
||||
values = self.tree.item(item, 'values')
|
||||
old_material = values[0]
|
||||
old_manager = values[1]
|
||||
|
||||
# 弹出编辑对话框
|
||||
dialog = tk.Toplevel(self)
|
||||
dialog.title("编辑记录")
|
||||
dialog.geometry("400x200")
|
||||
dialog.transient(self)
|
||||
dialog.grab_set()
|
||||
|
||||
# 物料名称
|
||||
ttk.Label(dialog, text="物料名称:").pack(pady=5)
|
||||
material_entry = ttk.Entry(dialog, width=40)
|
||||
material_entry.insert(0, old_material)
|
||||
material_entry.pack(pady=5)
|
||||
|
||||
# 负责人
|
||||
ttk.Label(dialog, text="负责人:").pack(pady=5)
|
||||
manager_entry = ttk.Entry(dialog, width=40)
|
||||
manager_entry.insert(0, old_manager)
|
||||
manager_entry.pack(pady=5)
|
||||
|
||||
# 按钮框架
|
||||
button_frame = ttk.Frame(dialog)
|
||||
button_frame.pack(pady=20)
|
||||
|
||||
result = {'material': old_material, 'manager': old_manager, 'confirmed': False}
|
||||
|
||||
def on_confirm():
|
||||
material = material_entry.get().strip()
|
||||
manager = manager_entry.get().strip()
|
||||
|
||||
if not material:
|
||||
messagebox.showwarning("警告", "物料名称不能为空")
|
||||
return
|
||||
if not manager:
|
||||
messagebox.showwarning("警告", "负责人不能为空")
|
||||
return
|
||||
|
||||
result['material'] = material
|
||||
result['manager'] = manager
|
||||
result['confirmed'] = True
|
||||
dialog.destroy()
|
||||
|
||||
def on_cancel():
|
||||
dialog.destroy()
|
||||
|
||||
ttk.Button(button_frame, text="确定", command=on_confirm).pack(side=tk.LEFT, padx=5)
|
||||
ttk.Button(button_frame, text="取消", command=on_cancel).pack(side=tk.LEFT, padx=5)
|
||||
|
||||
# 等待对话框关闭
|
||||
dialog.wait_window()
|
||||
|
||||
if result['confirmed']:
|
||||
new_material = result['material']
|
||||
new_manager = result['manager']
|
||||
|
||||
# 如果值没有变化,不处理
|
||||
if new_material == old_material and new_manager == old_manager:
|
||||
return
|
||||
|
||||
# 更新 current_data
|
||||
for record in self.current_data:
|
||||
if (record['MaterialName'] == old_material and
|
||||
record['ManagerName'] == old_manager):
|
||||
record['MaterialName'] = new_material
|
||||
record['ManagerName'] = new_manager
|
||||
break
|
||||
|
||||
# 添加到待更新列表
|
||||
self.pending_changes['update'].append({
|
||||
'old': {'MaterialName': old_material, 'ManagerName': old_manager},
|
||||
'new': {'MaterialName': new_material, 'ManagerName': new_manager}
|
||||
})
|
||||
|
||||
self._refresh_tree()
|
||||
self._update_status()
|
||||
|
||||
def _on_item_double_click(self, event):
|
||||
"""处理双击事件,编辑记录"""
|
||||
self._edit_selected_record()
|
||||
|
||||
def _save_changes(self):
|
||||
"""保存更改到数据库"""
|
||||
total_changes = (
|
||||
len(self.pending_changes['insert']) +
|
||||
len(self.pending_changes['delete']) +
|
||||
len(self.pending_changes['update'])
|
||||
)
|
||||
|
||||
if total_changes == 0:
|
||||
messagebox.showinfo("提示", "没有需要保存的更改")
|
||||
return
|
||||
|
||||
# 确认保存
|
||||
msg_parts = []
|
||||
if self.pending_changes['insert']:
|
||||
msg_parts.append(f"新增 {len(self.pending_changes['insert'])} 条")
|
||||
if self.pending_changes['delete']:
|
||||
msg_parts.append(f"删除 {len(self.pending_changes['delete'])} 条")
|
||||
if self.pending_changes['update']:
|
||||
msg_parts.append(f"更新 {len(self.pending_changes['update'])} 条")
|
||||
|
||||
if not messagebox.askyesno(
|
||||
"确认保存",
|
||||
"确定要将以下更改保存到数据库吗?\n\n" + "\n".join(msg_parts)
|
||||
):
|
||||
return
|
||||
|
||||
try:
|
||||
from db.materials_to_be_deleted_dao import MaterialsTypeToBeDeletedDAO
|
||||
|
||||
dao = MaterialsTypeToBeDeletedDAO()
|
||||
|
||||
stats = {
|
||||
'insert_success': 0,
|
||||
'insert_failed': 0,
|
||||
'delete_success': 0,
|
||||
'delete_failed': 0,
|
||||
'update_success': 0,
|
||||
'update_failed': 0
|
||||
}
|
||||
|
||||
# 执行插入
|
||||
for record in self.pending_changes['insert']:
|
||||
if dao.insert_material(record['MaterialName'], record['ManagerName']):
|
||||
stats['insert_success'] += 1
|
||||
else:
|
||||
stats['insert_failed'] += 1
|
||||
|
||||
# 执行删除
|
||||
for record in self.pending_changes['delete']:
|
||||
if dao.delete_material(record['MaterialName'], record['ManagerName']):
|
||||
stats['delete_success'] += 1
|
||||
else:
|
||||
stats['delete_failed'] += 1
|
||||
|
||||
# 执行更新
|
||||
for update in self.pending_changes['update']:
|
||||
old = update['old']
|
||||
new = update['new']
|
||||
# 先删除旧记录,再插入新记录
|
||||
if dao.delete_material(old['MaterialName'], old['ManagerName']):
|
||||
if dao.insert_material(new['MaterialName'], new['ManagerName']):
|
||||
stats['update_success'] += 1
|
||||
else:
|
||||
# 删除成功但插入失败,尝试回滚
|
||||
dao.insert_material(old['MaterialName'], old['ManagerName'])
|
||||
stats['update_failed'] += 1
|
||||
else:
|
||||
stats['update_failed'] += 1
|
||||
|
||||
# 显示结果
|
||||
result_parts = []
|
||||
if stats['insert_success'] > 0:
|
||||
result_parts.append(f"新增成功:{stats['insert_success']} 条")
|
||||
if stats['insert_failed'] > 0:
|
||||
result_parts.append(f"新增失败:{stats['insert_failed']} 条")
|
||||
if stats['delete_success'] > 0:
|
||||
result_parts.append(f"删除成功:{stats['delete_success']} 条")
|
||||
if stats['delete_failed'] > 0:
|
||||
result_parts.append(f"删除失败:{stats['delete_failed']} 条")
|
||||
if stats['update_success'] > 0:
|
||||
result_parts.append(f"更新成功:{stats['update_success']} 条")
|
||||
if stats['update_failed'] > 0:
|
||||
result_parts.append(f"更新失败:{stats['update_failed']} 条")
|
||||
|
||||
result_msg = "保存完成!\n\n" + "\n".join(result_parts)
|
||||
|
||||
if (stats['insert_failed'] + stats['delete_failed'] + stats['update_failed']) > 0:
|
||||
messagebox.showwarning("完成(部分失败)", result_msg)
|
||||
else:
|
||||
messagebox.showinfo("成功", result_msg)
|
||||
|
||||
# 重新加载数据
|
||||
self._load_data()
|
||||
|
||||
except Exception as e:
|
||||
messagebox.showerror("错误", f"保存失败:{str(e)}")
|
||||
|
||||
def _update_status(self):
|
||||
"""更新状态标签"""
|
||||
total_changes = (
|
||||
len(self.pending_changes['insert']) +
|
||||
len(self.pending_changes['delete']) +
|
||||
len(self.pending_changes['update'])
|
||||
)
|
||||
|
||||
if total_changes > 0:
|
||||
self.status_label.config(
|
||||
text=f"有 {total_changes} 项待保存的更改",
|
||||
foreground="red"
|
||||
)
|
||||
else:
|
||||
self.status_label.config(text="")
|
||||
|
||||
def _close_dialog(self):
|
||||
"""关闭对话框"""
|
||||
# 检查是否有未保存的更改
|
||||
total_changes = (
|
||||
len(self.pending_changes['insert']) +
|
||||
len(self.pending_changes['delete']) +
|
||||
len(self.pending_changes['update'])
|
||||
)
|
||||
|
||||
if total_changes > 0:
|
||||
if not messagebox.askyesno(
|
||||
"警告",
|
||||
f"有 {total_changes} 项未保存的更改,确定要关闭吗?"
|
||||
):
|
||||
return
|
||||
|
||||
self.destroy()
|
||||
@@ -18,6 +18,7 @@ from io import StringIO
|
||||
from contextlib import redirect_stdout
|
||||
from gui.widgets import FileSelector, LogText
|
||||
from gui.config_manager import ConfigManager
|
||||
from gui.material_type_management_dialog import MaterialTypeManagementDialog
|
||||
import pandas as pd
|
||||
|
||||
|
||||
@@ -287,6 +288,14 @@ class MaterialValidationTab(ttk.Frame):
|
||||
)
|
||||
self.export_button.pack(side=tk.LEFT, padx=5)
|
||||
|
||||
# 类型管理按钮
|
||||
self.type_management_button = ttk.Button(
|
||||
button_frame,
|
||||
text="类型管理",
|
||||
command=self.open_type_management
|
||||
)
|
||||
self.type_management_button.pack(side=tk.LEFT, padx=5)
|
||||
|
||||
def _create_result_table(self, parent):
|
||||
"""创建结果表格"""
|
||||
# 新列定义:选择、材料名称、材料代码、规格、型号、负责人
|
||||
@@ -802,3 +811,7 @@ class MaterialValidationTab(ttk.Frame):
|
||||
except Exception as e:
|
||||
messagebox.showerror("错误", f"导出失败:{str(e)}")
|
||||
self.log_text.error(f"导出失败:{str(e)}")
|
||||
|
||||
def open_type_management(self):
|
||||
"""打开类型管理窗口"""
|
||||
dialog = MaterialTypeManagementDialog(self, title="物料类型管理")
|
||||
|
||||
Reference in New Issue
Block a user