feat: add Production ID input widget and UI configuration
- Add ProductionIdInput widget with placeholder and multi-line support - Add UIConfig class for font family, font size, and input width settings - Refactor data extraction tab to use horizontal PanedWindow layout - Left panel: Production ID text input (draggable width) - Right panel: control panel and log output - Share Production IDs between data extraction and material validation tabs - Add UI settings group in settings page (font selection, size, input width) - For User users: automatically use shared Production IDs, simplified UI - Apply font settings to input and log widgets - Use sashpos() to set initial pane width correctly Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -131,7 +131,7 @@ class CheckboxTreeview(ttk.Treeview):
|
||||
class MaterialValidationTab(ttk.Frame):
|
||||
"""物料校验标签页"""
|
||||
|
||||
def __init__(self, parent, config: ConfigManager, session_manager):
|
||||
def __init__(self, parent, config: ConfigManager, session_manager, main_window=None):
|
||||
"""
|
||||
初始化物料校验标签页
|
||||
|
||||
@@ -139,13 +139,16 @@ class MaterialValidationTab(ttk.Frame):
|
||||
parent: 父容器
|
||||
config: 配置管理器
|
||||
session_manager: SessionManager 实例,用于权限控制
|
||||
main_window: 主窗口引用,用于获取共享的 Production ID
|
||||
"""
|
||||
super().__init__(parent)
|
||||
self.config = config
|
||||
self.session_manager = session_manager
|
||||
self.main_window = main_window
|
||||
self.validating = False
|
||||
self.validation_results = None
|
||||
self.material_records_cache = None # 缓存完整的物料记录
|
||||
self.shared_production_ids = [] # 共享的 Production ID 列表
|
||||
|
||||
# 负责人筛选相关
|
||||
self.managers: List[str] = [] # 可用负责人列表
|
||||
@@ -244,14 +247,72 @@ class MaterialValidationTab(ttk.Frame):
|
||||
|
||||
# 数据库过滤模式 - 需要 ProductionID 文件
|
||||
self.db_filtered_frame = ttk.Frame(file_group)
|
||||
self.db_filtered_production_id_selector = FileSelector(
|
||||
self.db_filtered_frame,
|
||||
label_text="ProductionID 文件:",
|
||||
file_type="file",
|
||||
file_types=[("文本文件", "*.txt"), ("所有文件", "*.*")],
|
||||
initial_dir="D:/python/playwrite/",
|
||||
)
|
||||
self.db_filtered_production_id_selector.pack(fill=tk.X)
|
||||
|
||||
# 判断用户类型
|
||||
is_user_only = not self.session_manager.is_admin()
|
||||
|
||||
if is_user_only:
|
||||
# ========== User 模式:直接使用共享的 Production ID ==========
|
||||
# 自动设置为使用共享数据源
|
||||
self.production_id_source_var = tk.StringVar(value="shared")
|
||||
|
||||
# 创建共享 Production ID 显示标签(但不显示任何内容,只是用于更新)
|
||||
self.shared_ids_info_label = ttk.Label(
|
||||
self.db_filtered_frame,
|
||||
text="",
|
||||
foreground="gray"
|
||||
)
|
||||
# 不 pack,保持隐藏状态
|
||||
|
||||
else:
|
||||
# ========== Admin 模式:可选择文件或共享数据 ==========
|
||||
# 共享 Production ID 提示标签
|
||||
self.shared_ids_label = ttk.Label(
|
||||
self.db_filtered_frame,
|
||||
text="💡 数据提取页面输入了 Production ID,可直接使用",
|
||||
foreground="green",
|
||||
font=("", 9)
|
||||
)
|
||||
# 初始时不显示,等待 Production ID 更新
|
||||
|
||||
# Production ID 数据源选择
|
||||
self.production_id_source_var = tk.StringVar(value="file")
|
||||
source_frame = ttk.Frame(self.db_filtered_frame)
|
||||
source_frame.pack(fill=tk.X, pady=5)
|
||||
|
||||
ttk.Radiobutton(
|
||||
source_frame,
|
||||
text="使用文件:",
|
||||
variable=self.production_id_source_var,
|
||||
value="file",
|
||||
command=self._on_production_id_source_changed
|
||||
).pack(side=tk.LEFT)
|
||||
|
||||
ttk.Radiobutton(
|
||||
source_frame,
|
||||
text="使用数据提取页面的 Production ID:",
|
||||
variable=self.production_id_source_var,
|
||||
value="shared",
|
||||
command=self._on_production_id_source_changed
|
||||
).pack(side=tk.LEFT, padx=10)
|
||||
|
||||
# 文件选择器
|
||||
self.db_filtered_production_id_selector = FileSelector(
|
||||
self.db_filtered_frame,
|
||||
label_text="",
|
||||
file_type="file",
|
||||
file_types=[("文本文件", "*.txt"), ("所有文件", "*.*")],
|
||||
initial_dir="D:/python/playwrite/",
|
||||
)
|
||||
self.db_filtered_production_id_selector.pack(fill=tk.X)
|
||||
|
||||
# 共享 Production ID 显示标签
|
||||
self.shared_ids_info_label = ttk.Label(
|
||||
self.db_filtered_frame,
|
||||
text="",
|
||||
foreground="blue"
|
||||
)
|
||||
# 初始时不显示
|
||||
|
||||
# 输出文件(对于所有用户都可见)
|
||||
self.output_file_selector = FileSelector(
|
||||
@@ -396,6 +457,56 @@ class MaterialValidationTab(ttk.Frame):
|
||||
elif mode == "database_filtered":
|
||||
self.db_filtered_frame.pack(fill=tk.X, pady=(0, 5))
|
||||
|
||||
def _on_production_id_source_changed(self):
|
||||
"""Production ID 数据源切换回调(仅 Admin 模式)"""
|
||||
# User 模式下没有文件选择器,直接返回
|
||||
if not self.session_manager.is_admin():
|
||||
return
|
||||
|
||||
source = self.production_id_source_var.get()
|
||||
if source == "shared":
|
||||
# 使用共享 Production ID
|
||||
self.db_filtered_production_id_selector.pack_forget()
|
||||
self.shared_ids_info_label.pack(anchor="w", pady=(0, 5))
|
||||
else:
|
||||
# 使用文件
|
||||
self.shared_ids_info_label.pack_forget()
|
||||
self.db_filtered_production_id_selector.pack(fill=tk.X)
|
||||
|
||||
def on_production_ids_updated(self, production_ids: list):
|
||||
"""当数据提取页面的 Production ID 更新时调用"""
|
||||
self.shared_production_ids = production_ids
|
||||
|
||||
is_user_only = not self.session_manager.is_admin()
|
||||
|
||||
# User 模式:静默更新,不显示任何提示
|
||||
if is_user_only:
|
||||
return
|
||||
|
||||
# Admin 模式:显示提示信息
|
||||
if production_ids:
|
||||
count = len(production_ids)
|
||||
preview = ", ".join(production_ids[:3])
|
||||
if count > 3:
|
||||
preview += f" ... (共 {count} 个)"
|
||||
self.shared_ids_info_label.config(text=f"📋 {preview}")
|
||||
# 显示提示标签
|
||||
if hasattr(self, 'shared_ids_label') and not self.shared_ids_label.winfo_ismapped():
|
||||
self.shared_ids_label.pack(anchor="w", pady=(0, 5))
|
||||
else:
|
||||
self.shared_ids_info_label.config(text="")
|
||||
# 隐藏提示标签
|
||||
if hasattr(self, 'shared_ids_label'):
|
||||
if self.shared_ids_label.winfo_ismapped():
|
||||
self.shared_ids_label.pack_forget()
|
||||
|
||||
def reload_config(self):
|
||||
"""配置更新后重新加载"""
|
||||
# 从主窗口获取最新的 Production ID
|
||||
if self.main_window:
|
||||
production_ids = self.main_window.get_shared_production_ids()
|
||||
self.on_production_ids_updated(production_ids)
|
||||
|
||||
def _create_manager_filter_area(self, parent):
|
||||
"""创建负责人筛选区域"""
|
||||
# PERMISSION CHECK: 非管理员用户不显示筛选区域
|
||||
@@ -716,19 +827,41 @@ class MaterialValidationTab(ttk.Frame):
|
||||
mode = self.source_mode.get()
|
||||
input_file = None
|
||||
production_id_file = None
|
||||
production_ids_list = None # 新增:用于传递共享的 Production ID 列表
|
||||
|
||||
is_user_only = not self.session_manager.is_admin()
|
||||
|
||||
# 根据模式验证输入文件
|
||||
if mode == "database_full":
|
||||
# 无需输入文件
|
||||
pass
|
||||
elif mode == "database_filtered":
|
||||
production_id_file = self.db_filtered_production_id_selector.get()
|
||||
if not production_id_file:
|
||||
messagebox.showerror("错误", "请选择 ProductionID 文件")
|
||||
return
|
||||
if not os.path.exists(production_id_file):
|
||||
messagebox.showerror("错误", f"文件不存在:{production_id_file}")
|
||||
return
|
||||
if is_user_only:
|
||||
# User 模式:自动使用共享的 Production ID
|
||||
production_ids_list = self.shared_production_ids
|
||||
if not production_ids_list:
|
||||
messagebox.showerror("错误", "没有可用的共享 Production ID\n请在数据提取页面输入 Production ID 后再试")
|
||||
return
|
||||
self.log_text.info(f"使用数据提取页面的 Production ID({len(production_ids_list)} 个)")
|
||||
else:
|
||||
# Admin 模式:检查数据源选择
|
||||
source = self.production_id_source_var.get()
|
||||
if source == "shared":
|
||||
# 使用共享的 Production ID
|
||||
production_ids_list = self.shared_production_ids
|
||||
if not production_ids_list:
|
||||
messagebox.showerror("错误", "没有可用的共享 Production ID\n请在数据提取页面输入 Production ID 后再试")
|
||||
return
|
||||
self.log_text.info(f"使用共享的 Production ID({len(production_ids_list)} 个)")
|
||||
else:
|
||||
# 使用文件
|
||||
production_id_file = self.db_filtered_production_id_selector.get()
|
||||
if not production_id_file:
|
||||
messagebox.showerror("错误", "请选择 ProductionID 文件")
|
||||
return
|
||||
if not os.path.exists(production_id_file):
|
||||
messagebox.showerror("错误", f"文件不存在:{production_id_file}")
|
||||
return
|
||||
else:
|
||||
messagebox.showerror("错误", f"未知的校验模式: {mode}")
|
||||
return
|
||||
@@ -758,16 +891,27 @@ class MaterialValidationTab(ttk.Frame):
|
||||
# 在后台线程中执行校验
|
||||
validation_thread = threading.Thread(
|
||||
target=self._validation_worker_enhanced,
|
||||
args=(mode, input_file, production_id_file, output_file),
|
||||
args=(mode, input_file, production_id_file, output_file, production_ids_list),
|
||||
daemon=True,
|
||||
)
|
||||
validation_thread.start()
|
||||
|
||||
def _validation_worker_enhanced(
|
||||
self, mode: str, input_file: str, production_id_file: str, output_file: str
|
||||
self, mode: str, input_file: str, production_id_file: str, output_file: str, production_ids_list: list = None
|
||||
):
|
||||
"""增强的校验工作线程(使用完整记录模式)"""
|
||||
import tempfile
|
||||
temp_production_id_file = None
|
||||
|
||||
try:
|
||||
# 如果提供了共享的 Production ID 列表,创建临时文件
|
||||
if production_ids_list:
|
||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.txt', delete=False, encoding='utf-8') as f:
|
||||
temp_production_id_file = f.name
|
||||
f.write('\n'.join(production_ids_list))
|
||||
production_id_file = temp_production_id_file
|
||||
self._update_log(f"使用共享的 Production ID 列表({len(production_ids_list)} 个)", "INFO")
|
||||
|
||||
# 导入校验器
|
||||
from utils.material_status_validator import MaterialStatusValidator
|
||||
|
||||
@@ -820,6 +964,12 @@ class MaterialValidationTab(ttk.Frame):
|
||||
import traceback
|
||||
self._update_log(traceback.format_exc(), "ERROR")
|
||||
finally:
|
||||
# 清理临时文件
|
||||
if temp_production_id_file and os.path.exists(temp_production_id_file):
|
||||
try:
|
||||
os.unlink(temp_production_id_file)
|
||||
except:
|
||||
pass
|
||||
# 更新 UI 状态
|
||||
self.after(0, self._validation_complete)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user