- Add EXECUTION_DRYRUN to save_to_env() to fix dryrun setting not persisting after restart - Add headless mode checkbox in User mode settings for browser visibility control Co-Authored-By: Claude (glm-5) <noreply@anthropic.com>
788 lines
32 KiB
Python
788 lines
32 KiB
Python
#!/usr/bin/env python
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
设置标签页
|
||
|
||
管理系统配置(ERP、数据库、浏览器、路径等)。
|
||
"""
|
||
|
||
import tkinter as tk
|
||
from tkinter import ttk, messagebox
|
||
import pyodbc
|
||
from gui.config_manager import ConfigManager
|
||
from config.schema import DatabaseType
|
||
|
||
|
||
class SettingsTab(ttk.Frame):
|
||
"""设置标签页"""
|
||
|
||
def __init__(self, parent, config: ConfigManager, session_manager=None):
|
||
"""
|
||
初始化设置标签页
|
||
|
||
Args:
|
||
parent: 父容器
|
||
config: 配置管理器
|
||
session_manager: 会话管理器,用于获取用户类型
|
||
"""
|
||
super().__init__(parent)
|
||
self.config = config
|
||
self.session_manager = session_manager
|
||
|
||
# 初始化物料校验输出文件名变量
|
||
self.validation_output_filename_var = tk.StringVar()
|
||
|
||
self.create_widgets()
|
||
self.load_settings()
|
||
|
||
def create_widgets(self):
|
||
"""创建界面组件"""
|
||
# 判断用户类型
|
||
is_user_only = (
|
||
self.session_manager and self.session_manager.get_user_type() == "User"
|
||
)
|
||
|
||
# 创建主容器,带滚动条
|
||
canvas = tk.Canvas(self)
|
||
scrollbar = ttk.Scrollbar(self, orient="vertical", command=canvas.yview)
|
||
scrollable_frame = ttk.Frame(canvas)
|
||
|
||
scrollable_frame.bind(
|
||
"<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all"))
|
||
)
|
||
|
||
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
|
||
canvas.configure(yscrollcommand=scrollbar.set)
|
||
|
||
# 根据用户类型创建配置组
|
||
if not is_user_only:
|
||
# 管理员显示所有配置组
|
||
self._create_erp_group(scrollable_frame)
|
||
self._create_database_group(scrollable_frame)
|
||
self._create_browser_group(scrollable_frame)
|
||
self._create_paths_group(scrollable_frame)
|
||
self._create_extraction_group(scrollable_frame)
|
||
self._create_validation_group(scrollable_frame)
|
||
self._create_ui_group(scrollable_frame)
|
||
else:
|
||
# User 用户:显示 ERP 凭据、路径配置和执行设置
|
||
self._create_user_erp_group(scrollable_frame)
|
||
self._create_paths_group(scrollable_frame)
|
||
self._create_user_execution_group(scrollable_frame)
|
||
|
||
# 按钮区域 - 根据用户类型显示不同按钮
|
||
button_frame = ttk.Frame(scrollable_frame)
|
||
if is_user_only:
|
||
# User 用户:显示测试按钮和保存设置按钮(row=3 因为有 ERP 凭据组、路径设置组、执行设置组)
|
||
button_frame.grid(row=3, column=0, columnspan=2, pady=20, sticky="ew")
|
||
else:
|
||
# 管理员显示所有按钮
|
||
button_frame.grid(row=7, column=0, columnspan=2, pady=20, sticky="ew")
|
||
|
||
if is_user_only:
|
||
# User 用户:显示测试按钮和保存设置按钮
|
||
ttk.Button(
|
||
button_frame, text="测试 ERP 连接", command=self.test_erp_connection
|
||
).pack(side="left", padx=5)
|
||
ttk.Button(
|
||
button_frame, text="测试数据库连接", command=self.test_db_connection
|
||
).pack(side="left", padx=5)
|
||
ttk.Button(button_frame, text="保存设置", command=self.save_settings).pack(
|
||
side="left", padx=5
|
||
)
|
||
else:
|
||
# 管理员显示所有按钮
|
||
ttk.Button(
|
||
button_frame, text="测试 ERP 连接", command=self.test_erp_connection
|
||
).pack(side="left", padx=5)
|
||
ttk.Button(
|
||
button_frame, text="测试数据库连接", command=self.test_db_connection
|
||
).pack(side="left", padx=5)
|
||
ttk.Button(button_frame, text="保存设置", command=self.save_settings).pack(
|
||
side="left", padx=5
|
||
)
|
||
ttk.Button(button_frame, text="恢复默认", command=self.reset_defaults).pack(
|
||
side="left", padx=5
|
||
)
|
||
|
||
# 布局
|
||
canvas.grid(row=0, column=0, sticky="nsew")
|
||
scrollbar.grid(row=0, column=1, sticky="ns")
|
||
|
||
self.rowconfigure(0, weight=1)
|
||
self.columnconfigure(0, weight=1)
|
||
|
||
def _create_erp_group(self, parent):
|
||
"""创建 ERP 配置组"""
|
||
group = ttk.LabelFrame(parent, text="ERP 系统配置", padding=10)
|
||
group.grid(row=0, column=0, columnspan=2, pady=10, padx=10, sticky="ew")
|
||
|
||
# URL
|
||
ttk.Label(group, text="ERP URL:").grid(row=0, column=0, sticky="w", pady=5)
|
||
self.erp_url_var = tk.StringVar()
|
||
ttk.Entry(group, textvariable=self.erp_url_var, width=50).grid(
|
||
row=0, column=1, pady=5, sticky="ew"
|
||
)
|
||
|
||
# 用户名
|
||
ttk.Label(group, text="用户名:").grid(row=1, column=0, sticky="w", pady=5)
|
||
self.erp_username_var = tk.StringVar()
|
||
ttk.Entry(group, textvariable=self.erp_username_var, width=50).grid(
|
||
row=1, column=1, pady=5, sticky="ew"
|
||
)
|
||
|
||
# 密码
|
||
ttk.Label(group, text="密码:").grid(row=2, column=0, sticky="w", pady=5)
|
||
self.erp_password_var = tk.StringVar()
|
||
entry = ttk.Entry(group, textvariable=self.erp_password_var, width=50, show="*")
|
||
entry.grid(row=2, column=1, pady=5, sticky="ew")
|
||
|
||
group.columnconfigure(1, weight=1)
|
||
|
||
def _create_user_erp_group(self, parent):
|
||
"""创建 User 用户的 ERP 凭据配置组"""
|
||
group = ttk.LabelFrame(parent, text="ERP 登录凭据", padding=10)
|
||
group.grid(row=0, column=0, columnspan=2, pady=10, padx=10, sticky="ew")
|
||
|
||
# 用户名
|
||
ttk.Label(group, text="ERP 用户名:").grid(row=0, column=0, sticky="w", pady=5)
|
||
self.user_erp_username_var = tk.StringVar()
|
||
ttk.Entry(group, textvariable=self.user_erp_username_var, width=40).grid(
|
||
row=0, column=1, pady=5, sticky="ew"
|
||
)
|
||
|
||
# 密码(带显示/隐藏切换)
|
||
ttk.Label(group, text="ERP 密码:").grid(row=1, column=0, sticky="w", pady=5)
|
||
self.user_erp_password_var = tk.StringVar()
|
||
password_entry = ttk.Entry(group, textvariable=self.user_erp_password_var, width=40, show="*")
|
||
password_entry.grid(row=1, column=1, pady=5, sticky="ew")
|
||
|
||
# 显示/隐藏密码按钮
|
||
self.password_visible = tk.BooleanVar(value=False)
|
||
ttk.Checkbutton(
|
||
group, text="显示密码", variable=self.password_visible,
|
||
command=lambda: password_entry.configure(show="" if self.password_visible.get() else "*")
|
||
).grid(row=2, column=1, sticky="w", pady=2)
|
||
|
||
# 提示文字
|
||
hint_label = ttk.Label(
|
||
group,
|
||
text="提示:此凭据为共享配置,修改后所有用户将使用新的 ERP 账号。",
|
||
foreground="gray",
|
||
)
|
||
hint_label.grid(row=3, column=0, columnspan=2, sticky="w", pady=(5, 0))
|
||
|
||
group.columnconfigure(1, weight=1)
|
||
|
||
def _create_database_group(self, parent):
|
||
"""创建数据库配置组"""
|
||
group = ttk.LabelFrame(parent, text="数据库配置", padding=10)
|
||
group.grid(row=1, column=0, columnspan=2, pady=10, padx=10, sticky="ew")
|
||
|
||
# 数据库类型选择
|
||
ttk.Label(group, text="数据库类型:").grid(row=0, column=0, sticky="w", pady=5)
|
||
self.db_type_var = tk.StringVar()
|
||
db_type_combo = ttk.Combobox(
|
||
group,
|
||
textvariable=self.db_type_var,
|
||
values=["sqlserver", "mysql"],
|
||
state="readonly",
|
||
width=30,
|
||
)
|
||
db_type_combo.grid(row=0, column=1, sticky="w", pady=5)
|
||
db_type_combo.bind("<<ComboboxSelected>>", self._on_db_type_changed)
|
||
|
||
# SQL Server 配置
|
||
self.sqlserver_frame = ttk.Frame(group)
|
||
self.sqlserver_frame.grid(row=1, column=0, columnspan=2, sticky="ew", pady=5)
|
||
|
||
ttk.Label(self.sqlserver_frame, text="服务器:").grid(
|
||
row=0, column=0, sticky="w", pady=5
|
||
)
|
||
self.db_server_var = tk.StringVar()
|
||
ttk.Entry(self.sqlserver_frame, textvariable=self.db_server_var, width=50).grid(
|
||
row=0, column=1, pady=5, sticky="ew"
|
||
)
|
||
|
||
# MySQL 配置
|
||
self.mysql_frame = ttk.Frame(group)
|
||
|
||
ttk.Label(self.mysql_frame, text="主机:").grid(
|
||
row=0, column=0, sticky="w", pady=5
|
||
)
|
||
self.mysql_host_var = tk.StringVar()
|
||
ttk.Entry(self.mysql_frame, textvariable=self.mysql_host_var, width=50).grid(
|
||
row=0, column=1, pady=5, sticky="ew"
|
||
)
|
||
|
||
ttk.Label(self.mysql_frame, text="端口:").grid(
|
||
row=1, column=0, sticky="w", pady=5
|
||
)
|
||
self.mysql_port_var = tk.IntVar(value=3306)
|
||
ttk.Spinbox(
|
||
self.mysql_frame,
|
||
from_=1,
|
||
to=65535,
|
||
textvariable=self.mysql_port_var,
|
||
width=10,
|
||
).grid(row=1, column=1, sticky="w", pady=5)
|
||
|
||
# 通用配置(两种数据库都需要)
|
||
ttk.Label(group, text="数据库:").grid(row=2, column=0, sticky="w", pady=5)
|
||
self.db_name_var = tk.StringVar()
|
||
ttk.Entry(group, textvariable=self.db_name_var, width=50).grid(
|
||
row=2, column=1, pady=5, sticky="ew"
|
||
)
|
||
|
||
ttk.Label(group, text="用户名:").grid(row=3, column=0, sticky="w", pady=5)
|
||
self.db_username_var = tk.StringVar()
|
||
ttk.Entry(group, textvariable=self.db_username_var, width=50).grid(
|
||
row=3, column=1, pady=5, sticky="ew"
|
||
)
|
||
|
||
ttk.Label(group, text="密码:").grid(row=4, column=0, sticky="w", pady=5)
|
||
self.db_password_var = tk.StringVar()
|
||
entry = ttk.Entry(group, textvariable=self.db_password_var, width=50, show="*")
|
||
entry.grid(row=4, column=1, pady=5, sticky="ew")
|
||
|
||
group.columnconfigure(1, weight=1)
|
||
|
||
def _on_db_type_changed(self, event=None):
|
||
"""数据库类型改变时的回调"""
|
||
db_type = self.db_type_var.get()
|
||
if db_type == "mysql":
|
||
self.sqlserver_frame.grid_forget()
|
||
self.mysql_frame.grid(row=1, column=0, columnspan=2, sticky="ew", pady=5)
|
||
else:
|
||
self.mysql_frame.grid_forget()
|
||
self.sqlserver_frame.grid(
|
||
row=1, column=0, columnspan=2, sticky="ew", pady=5
|
||
)
|
||
|
||
def _create_browser_group(self, parent):
|
||
"""创建浏览器配置组"""
|
||
group = ttk.LabelFrame(parent, text="浏览器设置", padding=10)
|
||
group.grid(row=2, column=0, pady=10, padx=10, sticky="ew")
|
||
|
||
self.browser_headless_var = tk.BooleanVar()
|
||
ttk.Checkbutton(
|
||
group, text="无头模式 (不显示浏览器)", variable=self.browser_headless_var
|
||
).grid(row=0, column=0, sticky="w", pady=5)
|
||
|
||
self.browser_ignore_https_var = tk.BooleanVar()
|
||
ttk.Checkbutton(
|
||
group, text="忽略 HTTPS 错误", variable=self.browser_ignore_https_var
|
||
).grid(row=1, column=0, sticky="w", pady=5)
|
||
|
||
self.browser_auto_close_var = tk.BooleanVar()
|
||
ttk.Checkbutton(
|
||
group, text="操作完成后自动关闭浏览器", variable=self.browser_auto_close_var
|
||
).grid(row=2, column=0, sticky="w", pady=5)
|
||
|
||
def _create_paths_group(self, parent):
|
||
"""创建路径配置组"""
|
||
# 根据用户类型调整 grid 位置
|
||
is_user_only = (
|
||
self.session_manager and self.session_manager.get_user_type() == "User"
|
||
)
|
||
|
||
group = ttk.LabelFrame(parent, text="路径设置", padding=10)
|
||
if is_user_only:
|
||
# User 用户:ERP 凭据在 row=0,路径设置在 row=1
|
||
group.grid(row=1, column=0, columnspan=2, pady=10, padx=10, sticky="ew")
|
||
else:
|
||
group.grid(row=2, column=1, pady=10, padx=10, sticky="nsew")
|
||
|
||
from gui.widgets import FileSelector
|
||
|
||
# 数据目录
|
||
ttk.Label(group, text="数据目录:").grid(row=0, column=0, sticky="w", pady=5)
|
||
self.data_dir_selector = FileSelector(
|
||
group,
|
||
label_text="",
|
||
file_type="directory",
|
||
initial_dir="D:/python/playwrite/data/",
|
||
)
|
||
self.data_dir_selector.grid(row=1, column=0, columnspan=2, sticky="ew", pady=5)
|
||
|
||
# 默认输出文件
|
||
ttk.Label(group, text="默认输出文件:").grid(row=2, column=0, sticky="w", pady=5)
|
||
self.default_output_var = tk.StringVar()
|
||
ttk.Entry(group, textvariable=self.default_output_var, width=40).grid(
|
||
row=2, column=1, columnspan=2, sticky="ew", pady=5
|
||
)
|
||
|
||
# 校验输出文件
|
||
ttk.Label(group, text="校验输出文件:").grid(row=3, column=0, sticky="w", pady=5)
|
||
ttk.Entry(
|
||
group, textvariable=self.validation_output_filename_var, width=40
|
||
).grid(row=3, column=1, columnspan=2, sticky="ew", pady=5)
|
||
|
||
group.columnconfigure(0, weight=1)
|
||
|
||
def _create_user_execution_group(self, parent):
|
||
"""创建 User 用户的执行设置组"""
|
||
group = ttk.LabelFrame(parent, text="执行设置", padding=10)
|
||
# User 用户:ERP 凭据在 row=0,路径设置在 row=1,执行设置在 row=2
|
||
group.grid(row=2, column=0, columnspan=2, pady=10, padx=10, sticky="ew")
|
||
|
||
# dryrun 模式设置
|
||
self.user_dryrun_var = tk.BooleanVar(value=False)
|
||
ttk.Checkbutton(
|
||
group, text="预览模式 (执行删除时不保存更改)", variable=self.user_dryrun_var
|
||
).grid(row=0, column=0, sticky="w", pady=5)
|
||
|
||
# 说明文字
|
||
dryrun_hint = ttk.Label(
|
||
group,
|
||
text="提示:勾选后,执行删除操作时将只预览不实际保存,用于测试流程。",
|
||
foreground="gray",
|
||
)
|
||
dryrun_hint.grid(row=1, column=0, sticky="w", pady=(0, 5))
|
||
|
||
# 无头模式设置
|
||
self.user_headless_var = tk.BooleanVar(value=True)
|
||
ttk.Checkbutton(
|
||
group, text="无头模式 (不显示浏览器)", variable=self.user_headless_var
|
||
).grid(row=2, column=0, sticky="w", pady=5)
|
||
|
||
# 说明文字
|
||
headless_hint = ttk.Label(
|
||
group,
|
||
text="提示:勾选后浏览器将在后台运行,不显示窗口。",
|
||
foreground="gray",
|
||
)
|
||
headless_hint.grid(row=3, column=0, sticky="w", pady=(0, 5))
|
||
|
||
def _create_extraction_group(self, parent):
|
||
"""创建处理配置组"""
|
||
group = ttk.LabelFrame(parent, text="数据提取设置", padding=10)
|
||
group.grid(row=3, column=0, columnspan=2, pady=10, padx=10, sticky="ew")
|
||
|
||
# 批次大小
|
||
ttk.Label(group, text="批次大小:").grid(row=0, column=0, sticky="w", pady=5)
|
||
self.batch_size_var = tk.IntVar(value=100)
|
||
ttk.Spinbox(
|
||
group, from_=10, to=500, textvariable=self.batch_size_var, width=10
|
||
).grid(row=0, column=1, sticky="w", pady=5)
|
||
|
||
# 详细日志
|
||
self.verbose_var = tk.BooleanVar()
|
||
ttk.Checkbutton(group, text="启用详细日志", variable=self.verbose_var).grid(
|
||
row=1, column=0, columnspan=2, sticky="w", pady=5
|
||
)
|
||
|
||
# 自动转换
|
||
self.auto_convert_var = tk.BooleanVar()
|
||
ttk.Checkbutton(
|
||
group, text="自动转换 Excel 格式", variable=self.auto_convert_var
|
||
).grid(row=2, column=0, columnspan=2, sticky="w", pady=5)
|
||
|
||
# 合并批次
|
||
self.merge_batches_var = tk.BooleanVar()
|
||
ttk.Checkbutton(
|
||
group, text="自动合并批次数据", variable=self.merge_batches_var
|
||
).grid(row=3, column=0, columnspan=2, sticky="w", pady=5)
|
||
|
||
# 数据库持久化
|
||
self.enable_db_persistence_var = tk.BooleanVar()
|
||
ttk.Checkbutton(
|
||
group, text="保存到数据库", variable=self.enable_db_persistence_var
|
||
).grid(row=4, column=0, columnspan=2, sticky="w", pady=5)
|
||
|
||
def _create_validation_group(self, parent):
|
||
"""创建物料校验配置组"""
|
||
group = ttk.LabelFrame(parent, text="物料校验设置", padding=10)
|
||
group.grid(row=4, column=0, columnspan=2, pady=10, padx=10, sticky="ew")
|
||
|
||
# 数据源选择
|
||
ttk.Label(group, text="默认数据源:").grid(row=0, column=0, sticky="w", pady=5)
|
||
self.validation_data_source_var = tk.StringVar()
|
||
data_source_combo = ttk.Combobox(
|
||
group,
|
||
textvariable=self.validation_data_source_var,
|
||
values=[
|
||
"database_full",
|
||
"database_filtered",
|
||
"excel_existing",
|
||
"excel_full",
|
||
],
|
||
state="readonly",
|
||
width=30,
|
||
)
|
||
data_source_combo.grid(row=0, column=1, sticky="w", pady=5)
|
||
|
||
# 使用数据库
|
||
self.validation_use_database_var = tk.BooleanVar()
|
||
ttk.Checkbutton(
|
||
group,
|
||
text="使用数据库作为数据源",
|
||
variable=self.validation_use_database_var,
|
||
).grid(row=1, column=0, columnspan=2, sticky="w", pady=5)
|
||
|
||
# 输出文件名
|
||
ttk.Label(group, text="输出文件名:").grid(row=2, column=0, sticky="w", pady=5)
|
||
ttk.Entry(
|
||
group, textvariable=self.validation_output_filename_var, width=30
|
||
).grid(row=2, column=1, sticky="w", pady=5)
|
||
|
||
# 批次大小
|
||
ttk.Label(group, text="数据库批次大小:").grid(
|
||
row=3, column=0, sticky="w", pady=5
|
||
)
|
||
self.validation_batch_size_var = tk.IntVar(value=2000)
|
||
ttk.Spinbox(
|
||
group,
|
||
from_=100,
|
||
to=2000,
|
||
textvariable=self.validation_batch_size_var,
|
||
width=10,
|
||
).grid(row=3, column=1, sticky="w", pady=5)
|
||
|
||
# 匹配模式
|
||
ttk.Label(group, text="匹配模式:").grid(row=4, column=0, sticky="w", pady=5)
|
||
self.validation_match_mode_var = tk.StringVar()
|
||
match_mode_combo = ttk.Combobox(
|
||
group,
|
||
textvariable=self.validation_match_mode_var,
|
||
values=["substring", "exact"],
|
||
state="readonly",
|
||
width=30,
|
||
)
|
||
match_mode_combo.grid(row=4, column=1, sticky="w", pady=5)
|
||
|
||
# CRUD 操作
|
||
self.validation_enable_crud_var = tk.BooleanVar()
|
||
ttk.Checkbutton(
|
||
group,
|
||
text="启用 CRUD 操作(管理待删除物料)",
|
||
variable=self.validation_enable_crud_var,
|
||
).grid(row=5, column=0, columnspan=2, sticky="w", pady=5)
|
||
|
||
# 默认负责人
|
||
ttk.Label(group, text="默认负责人:").grid(row=6, column=0, sticky="w", pady=5)
|
||
self.validation_default_manager_var = tk.StringVar()
|
||
ttk.Entry(
|
||
group, textvariable=self.validation_default_manager_var, width=30
|
||
).grid(row=6, column=1, sticky="w", pady=5)
|
||
|
||
group.columnconfigure(1, weight=1)
|
||
|
||
def _create_ui_group(self, parent):
|
||
"""创建 UI 配置组"""
|
||
group = ttk.LabelFrame(parent, text="界面设置", padding=10)
|
||
group.grid(row=5, column=0, columnspan=2, pady=10, padx=10, sticky="ew")
|
||
|
||
# 字体选择
|
||
ttk.Label(group, text="字体:").grid(row=0, column=0, sticky="w", pady=5)
|
||
self.ui_font_family_var = tk.StringVar()
|
||
font_combo = ttk.Combobox(
|
||
group,
|
||
textvariable=self.ui_font_family_var,
|
||
values=[
|
||
"Microsoft YaHei UI",
|
||
"SimSun",
|
||
"KaiTi",
|
||
"FangSong",
|
||
"Arial",
|
||
"Segoe UI",
|
||
],
|
||
state="readonly",
|
||
width=30,
|
||
)
|
||
font_combo.grid(row=0, column=1, sticky="w", pady=5)
|
||
|
||
# 字号选择
|
||
ttk.Label(group, text="字号:").grid(row=1, column=0, sticky="w", pady=5)
|
||
self.ui_font_size_var = tk.IntVar(value=10)
|
||
ttk.Spinbox(
|
||
group, from_=8, to=24, textvariable=self.ui_font_size_var, width=10
|
||
).grid(row=1, column=1, sticky="w", pady=5)
|
||
|
||
# Production ID 输入框宽度
|
||
ttk.Label(group, text="输入框宽度(字符):").grid(
|
||
row=2, column=0, sticky="w", pady=5
|
||
)
|
||
self.ui_input_width_var = tk.IntVar(value=20)
|
||
ttk.Spinbox(
|
||
group, from_=10, to=100, textvariable=self.ui_input_width_var, width=10
|
||
).grid(row=2, column=1, sticky="w", pady=5)
|
||
|
||
group.columnconfigure(1, weight=1)
|
||
|
||
def load_settings(self):
|
||
"""从配置加载设置到界面"""
|
||
# 判断是否为仅测试用户模式
|
||
is_user_only = (
|
||
self.session_manager and self.session_manager.get_user_type() == "User"
|
||
)
|
||
|
||
if is_user_only:
|
||
# User 用户模式 - 只需要加载 ERP 凭据、路径设置到界面
|
||
# ERP 凭据
|
||
self.user_erp_username_var.set(self.config.get("erp.username", ""))
|
||
self.user_erp_password_var.set(self.config.get("erp.password", ""))
|
||
# 路径设置
|
||
self.data_dir_selector.set(self.config.get("paths.data_dir", ""))
|
||
self.default_output_var.set(self.config.get("paths.default_output", ""))
|
||
self.validation_output_filename_var.set(
|
||
self.config.get("paths.validation_output", "")
|
||
)
|
||
# 执行设置
|
||
self.user_dryrun_var.set(self.config.get("execution.dryrun", False))
|
||
# 无头模式
|
||
self.user_headless_var.set(self.config.get("erp.headless", True))
|
||
return
|
||
|
||
# 管理员模式 - 加载所有配置
|
||
# ERP 设置
|
||
self.erp_url_var.set(self.config.get("erp.url", ""))
|
||
self.erp_username_var.set(self.config.get("erp.username", ""))
|
||
self.erp_password_var.set(self.config.get("erp.password", ""))
|
||
|
||
# 数据库设置
|
||
db_type = self.config.get("database.db_type", "sqlserver")
|
||
self.db_type_var.set(db_type)
|
||
|
||
if db_type == "mysql":
|
||
self.db_server_var.set(self.config.get("database.server", ""))
|
||
self.mysql_host_var.set(self.config.get("database.mysql.host", ""))
|
||
self.mysql_port_var.set(self.config.get("database.mysql.port", 3306))
|
||
else:
|
||
self.db_server_var.set(self.config.get("database.server", ""))
|
||
|
||
self.db_name_var.set(self.config.get("database.database", ""))
|
||
self.db_username_var.set(self.config.get("database.username", ""))
|
||
self.db_password_var.set(self.config.get("database.password", ""))
|
||
|
||
# 更新界面显示
|
||
self._on_db_type_changed()
|
||
|
||
# 浏览器设置(已合并到 ERP 配置中)
|
||
self.browser_headless_var.set(self.config.get("erp.headless", True))
|
||
self.browser_ignore_https_var.set(
|
||
self.config.get("erp.ignore_https_errors", True)
|
||
)
|
||
self.browser_auto_close_var.set(self.config.get("erp.auto_close_browser", True))
|
||
|
||
# 路径设置
|
||
self.data_dir_selector.set(self.config.get("paths.data_dir", ""))
|
||
self.default_output_var.set(self.config.get("paths.default_output", ""))
|
||
|
||
# 处理设置
|
||
self.batch_size_var.set(self.config.get("extraction.batch_size", 100))
|
||
self.verbose_var.set(self.config.get("extraction.verbose", True))
|
||
self.auto_convert_var.set(self.config.get("extraction.auto_convert", True))
|
||
self.merge_batches_var.set(self.config.get("extraction.merge_batches", True))
|
||
self.enable_db_persistence_var.set(
|
||
self.config.get("extraction.enable_db_persistence", False)
|
||
)
|
||
|
||
# 校验设置
|
||
self.validation_data_source_var.set(
|
||
self.config.get("validation.data_source", "database_full")
|
||
)
|
||
self.validation_use_database_var.set(
|
||
self.config.get("validation.use_database", True)
|
||
)
|
||
self.validation_output_filename_var.set(
|
||
self.config.get("paths.validation_output", "物料状态校验结果.xlsx")
|
||
)
|
||
self.validation_batch_size_var.set(
|
||
self.config.get("validation.batch_size", 2000)
|
||
)
|
||
self.validation_match_mode_var.set(
|
||
self.config.get("validation.match_mode", "substring")
|
||
)
|
||
self.validation_enable_crud_var.set(
|
||
self.config.get("validation.enable_crud_operations", False)
|
||
)
|
||
self.validation_default_manager_var.set(
|
||
self.config.get("validation.default_manager", "")
|
||
)
|
||
|
||
# UI 设置
|
||
self.ui_font_family_var.set(
|
||
self.config.get("ui.font_family", "Microsoft YaHei UI")
|
||
)
|
||
self.ui_font_size_var.set(self.config.get("ui.font_size", 10))
|
||
self.ui_input_width_var.set(self.config.get("ui.production_id_input_width", 20))
|
||
|
||
def save_settings(self):
|
||
"""保存界面设置到配置"""
|
||
# 判断是否为仅测试用户模式
|
||
is_user_only = (
|
||
self.session_manager and self.session_manager.get_user_type() == "User"
|
||
)
|
||
|
||
if is_user_only:
|
||
# User 用户模式 - 只保存 ERP 凭据、路径设置和执行设置
|
||
# ERP 凭据
|
||
self.config.set("erp.username", self.user_erp_username_var.get())
|
||
self.config.set("erp.password", self.user_erp_password_var.get())
|
||
# 路径设置
|
||
self.config.set("paths.data_dir", self.data_dir_selector.get())
|
||
self.config.set("paths.default_output", self.default_output_var.get())
|
||
self.config.set(
|
||
"paths.validation_output", self.validation_output_filename_var.get()
|
||
)
|
||
# 保存执行设置
|
||
self.config.set("execution.dryrun", self.user_dryrun_var.get())
|
||
# 无头模式
|
||
self.config.set("erp.headless", self.user_headless_var.get())
|
||
|
||
# 保存到文件
|
||
if self.config.save():
|
||
messagebox.showinfo("成功", "设置已保存")
|
||
# 通知其他标签页重新加载配置
|
||
self._notify_config_reload()
|
||
else:
|
||
messagebox.showerror("错误", "保存设置失败")
|
||
return
|
||
|
||
# 管理员模式 - 加载所有配置
|
||
# ERP 设置
|
||
self.config.set("erp.url", self.erp_url_var.get())
|
||
self.config.set("erp.username", self.erp_username_var.get())
|
||
self.config.set("erp.password", self.erp_password_var.get())
|
||
|
||
# 数据库设置
|
||
db_type = self.db_type_var.get()
|
||
self.config.set("database.db_type", db_type)
|
||
|
||
if db_type == "mysql":
|
||
# MySQL: 使用 host 字段
|
||
self.config.set("database.server", self.mysql_host_var.get())
|
||
self.config.set("database.mysql.host", self.mysql_host_var.get())
|
||
self.config.set("database.mysql.port", self.mysql_port_var.get())
|
||
else:
|
||
# SQL Server: 使用 server 字段
|
||
self.config.set("database.server", self.db_server_var.get())
|
||
|
||
self.config.set("database.database", self.db_name_var.get())
|
||
self.config.set("database.username", self.db_username_var.get())
|
||
self.config.set("database.password", self.db_password_var.get())
|
||
|
||
# 浏览器设置(已合并到 ERP 配置中)
|
||
self.config.set("erp.headless", self.browser_headless_var.get())
|
||
self.config.set("erp.ignore_https_errors", self.browser_ignore_https_var.get())
|
||
self.config.set("erp.auto_close_browser", self.browser_auto_close_var.get())
|
||
|
||
# 路径设置
|
||
self.config.set("paths.data_dir", self.data_dir_selector.get())
|
||
self.config.set("paths.default_output", self.default_output_var.get())
|
||
|
||
# 处理设置
|
||
self.config.set("extraction.batch_size", self.batch_size_var.get())
|
||
self.config.set("extraction.verbose", self.verbose_var.get())
|
||
self.config.set("extraction.auto_convert", self.auto_convert_var.get())
|
||
self.config.set("extraction.merge_batches", self.merge_batches_var.get())
|
||
self.config.set(
|
||
"extraction.enable_db_persistence", self.enable_db_persistence_var.get()
|
||
)
|
||
|
||
# 校验设置
|
||
self.config.set("validation.data_source", self.validation_data_source_var.get())
|
||
self.config.set(
|
||
"validation.use_database", self.validation_use_database_var.get()
|
||
)
|
||
self.config.set(
|
||
"paths.validation_output", self.validation_output_filename_var.get()
|
||
)
|
||
self.config.set("validation.batch_size", self.validation_batch_size_var.get())
|
||
self.config.set("validation.match_mode", self.validation_match_mode_var.get())
|
||
self.config.set(
|
||
"validation.enable_crud_operations", self.validation_enable_crud_var.get()
|
||
)
|
||
self.config.set(
|
||
"validation.default_manager", self.validation_default_manager_var.get()
|
||
)
|
||
|
||
# UI 设置
|
||
self.config.set("ui.font_family", self.ui_font_family_var.get())
|
||
self.config.set("ui.font_size", self.ui_font_size_var.get())
|
||
self.config.set("ui.production_id_input_width", self.ui_input_width_var.get())
|
||
|
||
# 保存到文件
|
||
if self.config.save():
|
||
messagebox.showinfo("成功", "设置已保存")
|
||
# 通知其他标签页重新加载配置
|
||
self._notify_config_reload()
|
||
else:
|
||
messagebox.showerror("错误", "保存设置失败")
|
||
|
||
def _notify_config_reload(self):
|
||
"""通知其他标签页配置已更新"""
|
||
# 尝试通知主窗口重新加载配置
|
||
try:
|
||
# 获取主窗口
|
||
main_window = self.winfo_toplevel()
|
||
# 调用主窗口的 reload_config 方法(如果存在)
|
||
if hasattr(main_window, "reload_config"):
|
||
main_window.reload_config()
|
||
except Exception:
|
||
pass
|
||
|
||
def test_db_connection(self):
|
||
"""测试数据库连接"""
|
||
# 从配置读取而不是从 UI 变量(支持 User 类型用户)
|
||
db_type = self.config.get("database.db_type", "sqlserver")
|
||
|
||
try:
|
||
if db_type == "mysql":
|
||
import mysql.connector
|
||
from mysql.connector import Error
|
||
|
||
conn = mysql.connector.connect(
|
||
host=self.config.get("database.mysql.host", ""),
|
||
port=self.config.get("database.mysql.port", 3306),
|
||
database=self.config.get("database.database", ""),
|
||
user=self.config.get("database.username", ""),
|
||
password=self.config.get("database.password", ""),
|
||
connection_timeout=5,
|
||
)
|
||
conn.close()
|
||
messagebox.showinfo("成功", "MySQL 数据库连接测试成功!")
|
||
else:
|
||
conn_str = (
|
||
f"DRIVER={{ODBC Driver 18 for SQL Server}};"
|
||
f"SERVER={self.config.get('database.server', '')};"
|
||
f"DATABASE={self.config.get('database.database', '')};"
|
||
f"UID={self.config.get('database.username', '')};"
|
||
f"PWD={self.config.get('database.password', '')};"
|
||
f"TrustServerCertificate=yes;"
|
||
)
|
||
conn = pyodbc.connect(conn_str, timeout=5)
|
||
conn.close()
|
||
messagebox.showinfo("成功", "SQL Server 数据库连接测试成功!")
|
||
|
||
except ImportError:
|
||
if db_type == "mysql":
|
||
messagebox.showerror(
|
||
"错误",
|
||
"未安装 mysql-connector-python,请运行:\npip install mysql-connector-python",
|
||
)
|
||
else:
|
||
messagebox.showerror(
|
||
"错误", "未安装 pyodbc,请运行:\npip install pyodbc"
|
||
)
|
||
except Exception as e:
|
||
messagebox.showerror("错误", f"数据库连接失败:\n{str(e)}")
|
||
|
||
def test_erp_connection(self):
|
||
"""测试 ERP 连接"""
|
||
# ERP 连接测试需要实际启动浏览器
|
||
messagebox.showinfo("提示", "ERP 连接测试将在数据提取功能中自动验证")
|
||
|
||
def reset_defaults(self):
|
||
"""恢复默认设置"""
|
||
if messagebox.askyesno(
|
||
"确认", "确定要恢复默认设置吗?这将覆盖 .env 文件中的所有配置。"
|
||
):
|
||
from config.schema import AppConfig
|
||
|
||
self.config.config = AppConfig.from_env() # 重新加载默认配置
|
||
self.config.save()
|
||
self.load_settings()
|
||
messagebox.showinfo("成功", "已恢复默认设置")
|