feat: implement user type-based UI access control
Restrict settings interface and material validation features based on user type: - SettingsTab: User type only sees "Test ERP Connection" and "Test Database Connection" buttons; Admin sees full configuration interface - MaterialValidationTab: Hide manager filter and data source options from non-admin users; default to database_filtered mode - test_db_connection(): Read from config directly to support User type without UI variables - Add backward compatibility: no session_manager defaults to full interface Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -83,8 +83,8 @@ class MainWindow:
|
||||
self.query_tab = DataQueryTab(self.notebook, self.config)
|
||||
self.notebook.add(self.query_tab, text="数据查询")
|
||||
|
||||
# 设置标签页
|
||||
self.settings_tab = SettingsTab(self.notebook, self.config)
|
||||
# 设置标签页(传入 session_manager)
|
||||
self.settings_tab = SettingsTab(self.notebook, self.config, self.session_manager)
|
||||
self.notebook.add(self.settings_tab, text="设置")
|
||||
|
||||
def create_status_bar(self):
|
||||
|
||||
@@ -171,13 +171,18 @@ class MaterialValidationTab(ttk.Frame):
|
||||
control_frame = ttk.Frame(main_container)
|
||||
control_frame.pack(fill=tk.X, pady=(0, 10))
|
||||
|
||||
# 负责人筛选区域
|
||||
manager_filter_frame = ttk.LabelFrame(
|
||||
main_container,
|
||||
text="筛选(按负责人)",
|
||||
padding=10
|
||||
)
|
||||
manager_filter_frame.pack(fill=tk.X, pady=(0, 10))
|
||||
# 负责人筛选区域 - 仅管理员可见
|
||||
# PERMISSION CHECK: 非管理员用户不显示筛选区域
|
||||
if self.session_manager.is_admin():
|
||||
manager_filter_frame = ttk.LabelFrame(
|
||||
main_container,
|
||||
text="筛选(按负责人)",
|
||||
padding=10
|
||||
)
|
||||
manager_filter_frame.pack(fill=tk.X, pady=(0, 10))
|
||||
else:
|
||||
# 普通用户:创建一个空容器,避免后续代码出错
|
||||
manager_filter_frame = ttk.Frame(main_container)
|
||||
|
||||
# 中部:结果表格
|
||||
result_frame = ttk.LabelFrame(main_container, text="校验结果", padding=5)
|
||||
@@ -197,28 +202,33 @@ class MaterialValidationTab(ttk.Frame):
|
||||
|
||||
def _create_control_panel(self, parent):
|
||||
"""创建控制面板"""
|
||||
# 数据来源选择
|
||||
source_group = ttk.LabelFrame(parent, text="数据来源", padding=10)
|
||||
source_group.pack(fill=tk.X, pady=5)
|
||||
# 数据来源选择 - 仅管理员可见
|
||||
# PERMISSION CHECK: 非管理员用户不显示数据来源选项
|
||||
if self.session_manager.is_admin():
|
||||
source_group = ttk.LabelFrame(parent, text="数据来源", padding=10)
|
||||
source_group.pack(fill=tk.X, pady=5)
|
||||
|
||||
self.source_mode = tk.StringVar(value="database_full")
|
||||
self.source_mode = tk.StringVar(value="database_full")
|
||||
|
||||
# 数据库模式
|
||||
ttk.Radiobutton(
|
||||
source_group,
|
||||
text="数据库 - 全表校验",
|
||||
variable=self.source_mode,
|
||||
value="database_full",
|
||||
command=self._on_source_mode_change,
|
||||
).grid(row=0, column=0, sticky="w", padx=5)
|
||||
# 数据库模式
|
||||
ttk.Radiobutton(
|
||||
source_group,
|
||||
text="数据库 - 全表校验",
|
||||
variable=self.source_mode,
|
||||
value="database_full",
|
||||
command=self._on_source_mode_change,
|
||||
).grid(row=0, column=0, sticky="w", padx=5)
|
||||
|
||||
ttk.Radiobutton(
|
||||
source_group,
|
||||
text="数据库 - ProductionID 过滤",
|
||||
variable=self.source_mode,
|
||||
value="database_filtered",
|
||||
command=self._on_source_mode_change,
|
||||
).grid(row=0, column=1, sticky="w", padx=5)
|
||||
ttk.Radiobutton(
|
||||
source_group,
|
||||
text="数据库 - ProductionID 过滤",
|
||||
variable=self.source_mode,
|
||||
value="database_filtered",
|
||||
command=self._on_source_mode_change,
|
||||
).grid(row=0, column=1, sticky="w", padx=5)
|
||||
else:
|
||||
# 普通用户:默认使用 database_filtered 模式
|
||||
self.source_mode = tk.StringVar(value="database_filtered")
|
||||
|
||||
# 文件选择
|
||||
file_group = ttk.LabelFrame(parent, text="文件选择", padding=10)
|
||||
@@ -243,18 +253,15 @@ class MaterialValidationTab(ttk.Frame):
|
||||
)
|
||||
self.db_filtered_production_id_selector.pack(fill=tk.X)
|
||||
|
||||
# 输出文件
|
||||
output_frame = ttk.Frame(file_group)
|
||||
output_frame.grid(row=1, column=0, columnspan=2, sticky="ew", pady=(10, 0))
|
||||
|
||||
# 输出文件(对于所有用户都可见)
|
||||
self.output_file_selector = FileSelector(
|
||||
output_frame,
|
||||
file_group,
|
||||
label_text="输出文件:",
|
||||
file_type="file",
|
||||
file_types=[("Excel 文件", "*.xlsx"), ("所有文件", "*.*")],
|
||||
initial_dir=self.config.get("paths.data_dir", "data/"),
|
||||
)
|
||||
self.output_file_selector.pack(fill=tk.X)
|
||||
self.output_file_selector.pack(fill=tk.X, pady=(10, 0))
|
||||
|
||||
# 设置默认输出
|
||||
default_output = os.path.join(
|
||||
@@ -370,20 +377,31 @@ class MaterialValidationTab(ttk.Frame):
|
||||
|
||||
def _on_source_mode_change(self):
|
||||
"""数据源模式切换"""
|
||||
# PERMISSION CHECK: 普通用户固定为 database_filtered 模式
|
||||
if not self.session_manager.is_admin():
|
||||
# 对于普通用户,pack 数据库过滤模式框架
|
||||
self.db_full_frame.pack_forget()
|
||||
self.db_filtered_frame.pack(fill=tk.X, pady=(0, 5))
|
||||
return
|
||||
|
||||
mode = self.source_mode.get()
|
||||
|
||||
# 隐藏所有文件选择框架
|
||||
self.db_full_frame.grid_remove()
|
||||
self.db_filtered_frame.grid_remove()
|
||||
self.db_full_frame.pack_forget()
|
||||
self.db_filtered_frame.pack_forget()
|
||||
|
||||
# 根据模式显示对应的文件选择器
|
||||
if mode == "database_full":
|
||||
self.db_full_frame.grid(row=0, column=0, columnspan=2, sticky="ew")
|
||||
self.db_full_frame.pack(fill=tk.X, pady=(0, 5))
|
||||
elif mode == "database_filtered":
|
||||
self.db_filtered_frame.grid(row=0, column=0, columnspan=2, sticky="ew")
|
||||
self.db_filtered_frame.pack(fill=tk.X, pady=(0, 5))
|
||||
|
||||
def _create_manager_filter_area(self, parent):
|
||||
"""创建负责人筛选区域"""
|
||||
# PERMISSION CHECK: 非管理员用户不显示筛选区域
|
||||
if not self.session_manager.is_admin():
|
||||
return
|
||||
|
||||
# 创建复选框容器
|
||||
canvas_container = ttk.Frame(parent)
|
||||
canvas_container.pack(fill=tk.BOTH, expand=True)
|
||||
|
||||
@@ -16,21 +16,26 @@ from config.schema import DatabaseType
|
||||
class SettingsTab(ttk.Frame):
|
||||
"""设置标签页"""
|
||||
|
||||
def __init__(self, parent, config: ConfigManager):
|
||||
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.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)
|
||||
@@ -43,40 +48,42 @@ class SettingsTab(ttk.Frame):
|
||||
canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
|
||||
canvas.configure(yscrollcommand=scrollbar.set)
|
||||
|
||||
# ERP 配置组
|
||||
self._create_erp_group(scrollable_frame)
|
||||
# 根据用户类型创建配置组
|
||||
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_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)
|
||||
|
||||
# 按钮区域
|
||||
# 按钮区域 - 根据用户类型显示不同按钮
|
||||
button_frame = ttk.Frame(scrollable_frame)
|
||||
button_frame.grid(row=6, column=0, columnspan=2, pady=20, sticky="ew")
|
||||
|
||||
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
|
||||
)
|
||||
if is_user_only:
|
||||
# 普通用户只显示测试按钮
|
||||
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)
|
||||
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")
|
||||
@@ -326,6 +333,15 @@ class SettingsTab(ttk.Frame):
|
||||
|
||||
def load_settings(self):
|
||||
"""从配置加载设置到界面"""
|
||||
# 判断是否为仅测试用户模式
|
||||
is_user_only = self.session_manager and self.session_manager.get_user_type() == 'User'
|
||||
|
||||
if is_user_only:
|
||||
# 普通用户模式 - 只需要加载测试连接所需的配置
|
||||
# 不需要加载设置到界面,因为界面没有配置输入框
|
||||
return
|
||||
|
||||
# 管理员模式 - 加载所有配置
|
||||
# ERP 设置
|
||||
self.erp_url_var.set(self.config.get("erp.url", ""))
|
||||
self.erp_username_var.set(self.config.get("erp.username", ""))
|
||||
@@ -431,7 +447,8 @@ class SettingsTab(ttk.Frame):
|
||||
|
||||
def test_db_connection(self):
|
||||
"""测试数据库连接"""
|
||||
db_type = self.db_type_var.get()
|
||||
# 从配置读取而不是从 UI 变量(支持 User 类型用户)
|
||||
db_type = self.config.get("database.db_type", "sqlserver")
|
||||
|
||||
try:
|
||||
if db_type == "mysql":
|
||||
@@ -439,11 +456,11 @@ class SettingsTab(ttk.Frame):
|
||||
from mysql.connector import Error
|
||||
|
||||
conn = mysql.connector.connect(
|
||||
host=self.mysql_host_var.get(),
|
||||
port=self.mysql_port_var.get(),
|
||||
database=self.db_name_var.get(),
|
||||
user=self.db_username_var.get(),
|
||||
password=self.db_password_var.get(),
|
||||
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()
|
||||
@@ -451,10 +468,10 @@ class SettingsTab(ttk.Frame):
|
||||
else:
|
||||
conn_str = (
|
||||
f"DRIVER={{ODBC Driver 18 for SQL Server}};"
|
||||
f"SERVER={self.db_server_var.get()};"
|
||||
f"DATABASE={self.db_name_var.get()};"
|
||||
f"UID={self.db_username_var.get()};"
|
||||
f"PWD={self.db_password_var.get()};"
|
||||
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)
|
||||
|
||||
Reference in New Issue
Block a user