1036 lines
43 KiB
Python
1036 lines
43 KiB
Python
|
|
'''对话交互模块'''
|
|||
|
|
import os
|
|||
|
|
import json
|
|||
|
|
import datetime
|
|||
|
|
from pathlib import Path
|
|||
|
|
from PyQt6.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QLabel,
|
|||
|
|
QLineEdit, QCheckBox, QPushButton, QTextEdit,
|
|||
|
|
QTableWidget, QTableWidgetItem, QHeaderView,
|
|||
|
|
QDialogButtonBox, QGroupBox, QSplitter, QListWidget,
|
|||
|
|
QListWidgetItem, QComboBox, QInputDialog, QFileDialog,
|
|||
|
|
QMessageBox, QSpinBox, QDoubleSpinBox, QFrame,QWidget)
|
|||
|
|
from PyQt6.QtCore import Qt
|
|||
|
|
from PyQt6.QtGui import QBrush, QColor, QFont
|
|||
|
|
|
|||
|
|
from utils import get_validation_tooltip, ensure_rule_structure
|
|||
|
|
|
|||
|
|
class ConfigSettingsDialog(QDialog):
|
|||
|
|
"""配置文件设置对话框"""
|
|||
|
|
def __init__(self, current_path="", parent=None):
|
|||
|
|
super().__init__(parent)
|
|||
|
|
self.setWindowTitle("配置文件设置")
|
|||
|
|
self.setModal(True)
|
|||
|
|
self.setMinimumSize(500, 200)
|
|||
|
|
self.current_path = current_path
|
|||
|
|
|
|||
|
|
self.init_ui()
|
|||
|
|
|
|||
|
|
def init_ui(self):
|
|||
|
|
layout = QVBoxLayout(self)
|
|||
|
|
layout.setSpacing(12)
|
|||
|
|
|
|||
|
|
# 文件路径设置
|
|||
|
|
file_group = QGroupBox("配置文件路径")
|
|||
|
|
file_layout = QVBoxLayout(file_group)
|
|||
|
|
|
|||
|
|
path_layout = QHBoxLayout()
|
|||
|
|
path_layout.addWidget(QLabel("配置文件:"))
|
|||
|
|
|
|||
|
|
self.path_edit = QLineEdit()
|
|||
|
|
self.path_edit.setText(self.current_path)
|
|||
|
|
path_layout.addWidget(self.path_edit)
|
|||
|
|
|
|||
|
|
self.browse_btn = QPushButton("浏览...")
|
|||
|
|
self.browse_btn.clicked.connect(self.browse_file)
|
|||
|
|
path_layout.addWidget(self.browse_btn)
|
|||
|
|
|
|||
|
|
file_layout.addLayout(path_layout)
|
|||
|
|
|
|||
|
|
# 提示信息
|
|||
|
|
tip_label = QLabel("提示:请选择或输入要编辑的配置文件路径")
|
|||
|
|
tip_label.setStyleSheet("color: #666666; font-size: 11px;")
|
|||
|
|
file_layout.addWidget(tip_label)
|
|||
|
|
|
|||
|
|
layout.addWidget(file_group)
|
|||
|
|
|
|||
|
|
# 使用相对路径选项
|
|||
|
|
self.use_relative_checkbox = QCheckBox("使用相对路径(相对于程序所在目录)")
|
|||
|
|
self.use_relative_checkbox.setChecked(True)
|
|||
|
|
layout.addWidget(self.use_relative_checkbox)
|
|||
|
|
|
|||
|
|
layout.addStretch()
|
|||
|
|
|
|||
|
|
# 按钮
|
|||
|
|
button_box = QDialogButtonBox(
|
|||
|
|
QDialogButtonBox.StandardButton.Ok |
|
|||
|
|
QDialogButtonBox.StandardButton.Cancel
|
|||
|
|
)
|
|||
|
|
button_box.accepted.connect(self.accept)
|
|||
|
|
button_box.rejected.connect(self.reject)
|
|||
|
|
layout.addWidget(button_box)
|
|||
|
|
|
|||
|
|
def browse_file(self):
|
|||
|
|
"""浏览文件"""
|
|||
|
|
file_path, _ = QFileDialog.getOpenFileName(
|
|||
|
|
self, "选择配置文件",
|
|||
|
|
self.path_edit.text() or str(Path.home()),
|
|||
|
|
"Python Files (*.py);;All Files (*)"
|
|||
|
|
)
|
|||
|
|
if file_path:
|
|||
|
|
self.path_edit.setText(file_path)
|
|||
|
|
|
|||
|
|
def get_settings(self):
|
|||
|
|
"""获取设置"""
|
|||
|
|
config_path = self.path_edit.text().strip()
|
|||
|
|
|
|||
|
|
# 如果使用相对路径,则转换为相对于程序目录的路径
|
|||
|
|
if self.use_relative_checkbox.isChecked() and config_path:
|
|||
|
|
if not os.path.isabs(config_path):
|
|||
|
|
# 如果已经是相对路径,相对于程序所在目录
|
|||
|
|
program_dir = Path(__file__).parent.absolute()
|
|||
|
|
config_path = str(program_dir / config_path)
|
|||
|
|
|
|||
|
|
return {
|
|||
|
|
"config_file_path": config_path,
|
|||
|
|
"use_relative_path": self.use_relative_checkbox.isChecked()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
class ConfigManagementWindow(QDialog):
|
|||
|
|
def __init__(self, rules, config_fields, parent=None):
|
|||
|
|
super().__init__(parent)
|
|||
|
|
self.rules = rules
|
|||
|
|
self.config_fields = config_fields
|
|||
|
|
self.all_fields = list(config_fields.keys()) # 保存所有字段名用于搜索
|
|||
|
|
self.setWindowTitle("规则管理")
|
|||
|
|
self.setModal(True)
|
|||
|
|
self.setMinimumSize(700, 600)
|
|||
|
|
self.resize(700, 600)
|
|||
|
|
|
|||
|
|
self.init_ui()
|
|||
|
|
self.load_data()
|
|||
|
|
|
|||
|
|
def init_ui(self):
|
|||
|
|
layout = QVBoxLayout(self)
|
|||
|
|
layout.setContentsMargins(8, 8, 8, 8)
|
|||
|
|
layout.setSpacing(8)
|
|||
|
|
|
|||
|
|
# 分割窗口
|
|||
|
|
splitter = QSplitter(Qt.Orientation.Horizontal)
|
|||
|
|
layout.addWidget(splitter)
|
|||
|
|
|
|||
|
|
# 左侧:配置项列表
|
|||
|
|
left_widget = QWidget()
|
|||
|
|
left_layout = QVBoxLayout(left_widget)
|
|||
|
|
left_layout.setContentsMargins(4, 4, 4, 4)
|
|||
|
|
|
|||
|
|
# 搜索框 - 添加搜索类型选择
|
|||
|
|
search_layout = QHBoxLayout()
|
|||
|
|
search_layout.addWidget(QLabel("搜索:"))
|
|||
|
|
|
|||
|
|
# 搜索类型选择
|
|||
|
|
self.search_type_combo = QComboBox()
|
|||
|
|
self.search_type_combo.addItems(["变量名", "显示名称"])
|
|||
|
|
self.search_type_combo.setMaximumWidth(80)
|
|||
|
|
search_layout.addWidget(self.search_type_combo)
|
|||
|
|
|
|||
|
|
self.search_edit = QLineEdit()
|
|||
|
|
self.search_edit.setPlaceholderText("输入关键词进行搜索...")
|
|||
|
|
self.search_edit.textChanged.connect(self.filter_fields)
|
|||
|
|
search_layout.addWidget(self.search_edit)
|
|||
|
|
|
|||
|
|
# 清除搜索按钮
|
|||
|
|
self.clear_search_btn = QPushButton("清除")
|
|||
|
|
self.clear_search_btn.clicked.connect(self.clear_search)
|
|||
|
|
self.clear_search_btn.setMaximumWidth(60)
|
|||
|
|
search_layout.addWidget(self.clear_search_btn)
|
|||
|
|
|
|||
|
|
left_layout.addLayout(search_layout)
|
|||
|
|
|
|||
|
|
# 修改:添加分组显示说明
|
|||
|
|
list_label = QLabel("配置项列表(上方为未隐藏项,下方为隐藏项):")
|
|||
|
|
list_label.setStyleSheet("color: #333333; font-weight: bold;")
|
|||
|
|
left_layout.addWidget(list_label)
|
|||
|
|
|
|||
|
|
self.fields_list = QListWidget()
|
|||
|
|
self.fields_list.currentItemChanged.connect(self.on_field_selected)
|
|||
|
|
left_layout.addWidget(self.fields_list)
|
|||
|
|
|
|||
|
|
# 添加统计信息
|
|||
|
|
self.fields_count_label = QLabel("")
|
|||
|
|
left_layout.addWidget(self.fields_count_label)
|
|||
|
|
|
|||
|
|
splitter.addWidget(left_widget)
|
|||
|
|
|
|||
|
|
# 右侧:编辑区域
|
|||
|
|
right_widget = QWidget()
|
|||
|
|
right_layout = QVBoxLayout(right_widget)
|
|||
|
|
right_layout.setContentsMargins(4, 4, 4, 4)
|
|||
|
|
|
|||
|
|
# 基本信息
|
|||
|
|
info_group = QGroupBox("字段属性")
|
|||
|
|
form_layout = QVBoxLayout(info_group)
|
|||
|
|
form_layout.setContentsMargins(8, 8, 8, 8)
|
|||
|
|
|
|||
|
|
# 变量名
|
|||
|
|
self.name_label = QLabel()
|
|||
|
|
self.create_form_row(form_layout, "变量名:", self.name_label)
|
|||
|
|
|
|||
|
|
# 显示名称
|
|||
|
|
self.display_name_edit = QLineEdit()
|
|||
|
|
self.create_form_row(form_layout, "显示名称:", self.display_name_edit)
|
|||
|
|
|
|||
|
|
# 分组和类型
|
|||
|
|
category_layout = QHBoxLayout()
|
|||
|
|
category_layout.addWidget(QLabel("分组:"))
|
|||
|
|
self.category_combo = QComboBox()
|
|||
|
|
self.category_combo.setEditable(True)
|
|||
|
|
category_layout.addWidget(self.category_combo)
|
|||
|
|
category_layout.addStretch()
|
|||
|
|
|
|||
|
|
category_layout.addWidget(QLabel("类型:"))
|
|||
|
|
self.type_combo = QComboBox()
|
|||
|
|
self.type_combo.addItems(["auto", "str", "int", "float", "bool", "json"])
|
|||
|
|
self.type_combo.currentTextChanged.connect(self.on_type_changed)
|
|||
|
|
category_layout.addWidget(self.type_combo)
|
|||
|
|
|
|||
|
|
form_layout.addLayout(category_layout)
|
|||
|
|
|
|||
|
|
# 小数位数设置(仅当类型为float或int时显示)
|
|||
|
|
self.decimal_row = QHBoxLayout()
|
|||
|
|
self.decimal_label = QLabel("小数位数:")
|
|||
|
|
self.decimal_spinbox = QSpinBox()
|
|||
|
|
self.decimal_spinbox.setRange(0, 10)
|
|||
|
|
self.decimal_spinbox.setValue(2) # 默认值
|
|||
|
|
self.decimal_spinbox.setEnabled(False) # 默认禁用,只有float类型才启用
|
|||
|
|
self.decimal_row.addWidget(self.decimal_label)
|
|||
|
|
self.decimal_row.addWidget(self.decimal_spinbox)
|
|||
|
|
self.decimal_row.addStretch()
|
|||
|
|
form_layout.addLayout(self.decimal_row)
|
|||
|
|
|
|||
|
|
# 隐藏状态
|
|||
|
|
self.hidden_checkbox = QCheckBox("隐藏此配置项(不在主界面显示)")
|
|||
|
|
self.hidden_checkbox.stateChanged.connect(self.on_hidden_changed)
|
|||
|
|
form_layout.addWidget(self.hidden_checkbox)
|
|||
|
|
|
|||
|
|
# 加密配置项选项
|
|||
|
|
self.encrypted_checkbox = QCheckBox("加密配置项(修改时需要验证二级密码)")
|
|||
|
|
self.encrypted_checkbox.stateChanged.connect(self.on_encrypted_changed)
|
|||
|
|
form_layout.addWidget(self.encrypted_checkbox)
|
|||
|
|
|
|||
|
|
# 提示信息
|
|||
|
|
form_layout.addWidget(QLabel("提示信息:"))
|
|||
|
|
self.tooltip_edit = QTextEdit()
|
|||
|
|
self.tooltip_edit.setMaximumHeight(80)
|
|||
|
|
form_layout.addWidget(self.tooltip_edit)
|
|||
|
|
|
|||
|
|
right_layout.addWidget(info_group)
|
|||
|
|
|
|||
|
|
# 校验规则
|
|||
|
|
validation_group = QGroupBox("校验规则")
|
|||
|
|
validation_layout = QVBoxLayout(validation_group)
|
|||
|
|
validation_layout.setContentsMargins(8, 8, 8, 8)
|
|||
|
|
|
|||
|
|
# 最小值
|
|||
|
|
min_layout = QHBoxLayout()
|
|||
|
|
min_layout.addWidget(QLabel("最小值:"))
|
|||
|
|
self.min_edit = QLineEdit()
|
|||
|
|
self.min_edit.setPlaceholderText("对于数字类型有效")
|
|||
|
|
min_layout.addWidget(self.min_edit)
|
|||
|
|
min_layout.addStretch()
|
|||
|
|
validation_layout.addLayout(min_layout)
|
|||
|
|
|
|||
|
|
# 最大值
|
|||
|
|
max_layout = QHBoxLayout()
|
|||
|
|
max_layout.addWidget(QLabel("最大值:"))
|
|||
|
|
self.max_edit = QLineEdit()
|
|||
|
|
self.max_edit.setPlaceholderText("对于数字类型有效")
|
|||
|
|
max_layout.addWidget(self.max_edit)
|
|||
|
|
max_layout.addStretch()
|
|||
|
|
validation_layout.addLayout(max_layout)
|
|||
|
|
|
|||
|
|
# 正则表达式
|
|||
|
|
validation_layout.addWidget(QLabel("正则表达式:"))
|
|||
|
|
self.regex_edit = QLineEdit()
|
|||
|
|
self.regex_edit.setPlaceholderText("对于字符串类型有效,如: ^[A-Za-z0-9_]+$")
|
|||
|
|
validation_layout.addWidget(self.regex_edit)
|
|||
|
|
|
|||
|
|
# 必填项
|
|||
|
|
self.required_checkbox = QCheckBox("必填项")
|
|||
|
|
validation_layout.addWidget(self.required_checkbox)
|
|||
|
|
|
|||
|
|
right_layout.addWidget(validation_group)
|
|||
|
|
|
|||
|
|
# 分组管理
|
|||
|
|
group_group = QGroupBox("分组管理")
|
|||
|
|
group_layout = QVBoxLayout(group_group)
|
|||
|
|
|
|||
|
|
group_btn_layout = QHBoxLayout()
|
|||
|
|
self.add_group_btn = QPushButton("添加分组")
|
|||
|
|
self.remove_group_btn = QPushButton("删除分组")
|
|||
|
|
|
|||
|
|
self.add_group_btn.clicked.connect(self.add_group)
|
|||
|
|
self.remove_group_btn.clicked.connect(self.remove_group)
|
|||
|
|
|
|||
|
|
group_btn_layout.addWidget(self.add_group_btn)
|
|||
|
|
group_btn_layout.addWidget(self.remove_group_btn)
|
|||
|
|
group_btn_layout.addStretch()
|
|||
|
|
|
|||
|
|
group_layout.addLayout(group_btn_layout)
|
|||
|
|
right_layout.addWidget(group_group)
|
|||
|
|
|
|||
|
|
# 批量操作
|
|||
|
|
batch_group = QGroupBox("批量操作")
|
|||
|
|
batch_layout = QVBoxLayout(batch_group)
|
|||
|
|
batch_layout.setContentsMargins(8, 8, 8, 8)
|
|||
|
|
|
|||
|
|
batch_btn_layout = QHBoxLayout()
|
|||
|
|
self.show_all_btn = QPushButton("全部显示")
|
|||
|
|
self.show_all_btn.clicked.connect(self.show_all_fields)
|
|||
|
|
self.hide_all_btn = QPushButton("全部隐藏")
|
|||
|
|
self.hide_all_btn.clicked.connect(self.hide_all_fields)
|
|||
|
|
|
|||
|
|
batch_btn_layout.addWidget(self.show_all_btn)
|
|||
|
|
batch_btn_layout.addWidget(self.hide_all_btn)
|
|||
|
|
batch_btn_layout.addStretch()
|
|||
|
|
|
|||
|
|
batch_layout.addLayout(batch_btn_layout)
|
|||
|
|
|
|||
|
|
# 导入导出功能
|
|||
|
|
import_export_group = QGroupBox("导入导出规则文件")
|
|||
|
|
import_export_layout = QVBoxLayout(import_export_group)
|
|||
|
|
import_export_layout.setContentsMargins(8, 8, 8, 8)
|
|||
|
|
|
|||
|
|
import_export_btn_layout = QHBoxLayout()
|
|||
|
|
self.import_rules_btn = QPushButton("导入规则文件")
|
|||
|
|
self.import_rules_btn.clicked.connect(self.import_rules_file)
|
|||
|
|
self.export_rules_btn = QPushButton("导出规则文件")
|
|||
|
|
self.export_rules_btn.clicked.connect(self.export_rules_file)
|
|||
|
|
|
|||
|
|
import_export_btn_layout.addWidget(self.import_rules_btn)
|
|||
|
|
import_export_btn_layout.addWidget(self.export_rules_btn)
|
|||
|
|
import_export_btn_layout.addStretch()
|
|||
|
|
|
|||
|
|
import_export_layout.addLayout(import_export_btn_layout)
|
|||
|
|
right_layout.addWidget(import_export_group)
|
|||
|
|
|
|||
|
|
right_layout.addStretch()
|
|||
|
|
splitter.addWidget(right_widget)
|
|||
|
|
|
|||
|
|
# 设置分割比例
|
|||
|
|
splitter.setSizes([250, 420])
|
|||
|
|
|
|||
|
|
# 按钮
|
|||
|
|
button_box = QDialogButtonBox(QDialogButtonBox.StandardButton.Ok |
|
|||
|
|
QDialogButtonBox.StandardButton.Cancel)
|
|||
|
|
button_box.accepted.connect(self.accept)
|
|||
|
|
button_box.rejected.connect(self.reject)
|
|||
|
|
layout.addWidget(button_box)
|
|||
|
|
|
|||
|
|
def create_form_row(self, layout, label_text, widget):
|
|||
|
|
"""创建舒适的表单行"""
|
|||
|
|
row_layout = QHBoxLayout()
|
|||
|
|
label = QLabel(label_text)
|
|||
|
|
label.setMinimumWidth(80)
|
|||
|
|
row_layout.addWidget(label)
|
|||
|
|
row_layout.addWidget(widget)
|
|||
|
|
layout.addLayout(row_layout)
|
|||
|
|
|
|||
|
|
def load_data(self):
|
|||
|
|
"""加载数据到界面(按隐藏状态分组显示)"""
|
|||
|
|
self.fields_list.clear()
|
|||
|
|
|
|||
|
|
# 获取隐藏字段列表
|
|||
|
|
hidden_fields = self.rules.get("hidden", [])
|
|||
|
|
|
|||
|
|
# 获取加密字段列表
|
|||
|
|
encrypted_fields = self.rules.get("encrypted_fields", [])
|
|||
|
|
|
|||
|
|
# 分离未隐藏和已隐藏的字段
|
|||
|
|
visible_fields = []
|
|||
|
|
hidden_fields_list = []
|
|||
|
|
|
|||
|
|
for field_name in sorted(self.config_fields.keys()):
|
|||
|
|
if field_name in hidden_fields:
|
|||
|
|
hidden_fields_list.append(field_name)
|
|||
|
|
else:
|
|||
|
|
visible_fields.append(field_name)
|
|||
|
|
|
|||
|
|
# 添加分隔项
|
|||
|
|
if visible_fields and hidden_fields_list:
|
|||
|
|
# 添加分隔符(未隐藏部分)
|
|||
|
|
self.create_separator_item("未隐藏的配置项")
|
|||
|
|
|
|||
|
|
# 添加未隐藏的字段(高亮显示)
|
|||
|
|
for field_name in visible_fields:
|
|||
|
|
self.create_field_item(field_name, is_hidden=False, is_encrypted=(field_name in encrypted_fields))
|
|||
|
|
|
|||
|
|
# 添加分隔项
|
|||
|
|
if visible_fields and hidden_fields_list:
|
|||
|
|
# 添加分隔符(隐藏部分)
|
|||
|
|
self.create_separator_item("已隐藏的配置项")
|
|||
|
|
|
|||
|
|
# 添加已隐藏的字段(灰色显示)
|
|||
|
|
for field_name in hidden_fields_list:
|
|||
|
|
self.create_field_item(field_name, is_hidden=True, is_encrypted=(field_name in encrypted_fields))
|
|||
|
|
|
|||
|
|
# 如果没有配置项,显示提示
|
|||
|
|
if not visible_fields and not hidden_fields_list:
|
|||
|
|
item = QListWidgetItem("没有配置项")
|
|||
|
|
self.fields_list.addItem(item)
|
|||
|
|
item.setFlags(Qt.ItemFlag.NoItemFlags) # 不可选
|
|||
|
|
item.setForeground(QBrush(QColor("#999999")))
|
|||
|
|
|
|||
|
|
self.update_fields_count()
|
|||
|
|
|
|||
|
|
# 加载分组
|
|||
|
|
self.category_combo.clear()
|
|||
|
|
categories = list(self.rules.get("categories", {}).keys())
|
|||
|
|
# 确保"未分类"分组始终存在
|
|||
|
|
if "未分类" not in categories:
|
|||
|
|
categories.append("未分类")
|
|||
|
|
if categories:
|
|||
|
|
for category in sorted(categories):
|
|||
|
|
self.category_combo.addItem(category)
|
|||
|
|
|
|||
|
|
def create_separator_item(self, text):
|
|||
|
|
"""创建分隔项"""
|
|||
|
|
item = QListWidgetItem(f"--- {text} ---")
|
|||
|
|
self.fields_list.addItem(item)
|
|||
|
|
item.setFlags(Qt.ItemFlag.NoItemFlags) # 不可选
|
|||
|
|
item.setForeground(QBrush(QColor("#666666")))
|
|||
|
|
font = item.font()
|
|||
|
|
font.setItalic(True)
|
|||
|
|
font.setBold(True)
|
|||
|
|
item.setFont(font)
|
|||
|
|
|
|||
|
|
# 设置背景色
|
|||
|
|
item.setBackground(QBrush(QColor("#f0f0f0")))
|
|||
|
|
return item
|
|||
|
|
|
|||
|
|
def create_field_item(self, field_name, is_hidden=False, is_encrypted=False):
|
|||
|
|
"""创建字段项"""
|
|||
|
|
item = QListWidgetItem(field_name)
|
|||
|
|
self.fields_list.addItem(item)
|
|||
|
|
|
|||
|
|
# 获取显示名称用于搜索
|
|||
|
|
display_name = self.rules.get("display_names", {}).get(field_name, field_name)
|
|||
|
|
|
|||
|
|
# 设置自定义数据:变量名和显示名称
|
|||
|
|
item.setData(Qt.ItemDataRole.UserRole, field_name) # 变量名
|
|||
|
|
item.setData(Qt.ItemDataRole.UserRole + 1, display_name) # 显示名称
|
|||
|
|
|
|||
|
|
# 设置显示文本(包含加密标识)
|
|||
|
|
display_text = field_name
|
|||
|
|
if is_encrypted:
|
|||
|
|
display_text += " 🔒"
|
|||
|
|
|
|||
|
|
item.setText(display_text)
|
|||
|
|
|
|||
|
|
if is_hidden:
|
|||
|
|
# 隐藏字段:灰色,斜体
|
|||
|
|
item.setForeground(QBrush(QColor("#999999")))
|
|||
|
|
font = item.font()
|
|||
|
|
font.setItalic(True)
|
|||
|
|
item.setFont(font)
|
|||
|
|
item.setToolTip(f"已隐藏: {field_name} ({display_name})" + (" [加密]" if is_encrypted else ""))
|
|||
|
|
else:
|
|||
|
|
# 未隐藏字段:高亮显示
|
|||
|
|
if is_encrypted:
|
|||
|
|
# 加密字段:橙色高亮
|
|||
|
|
item.setForeground(QBrush(QColor("#ff5722"))) # 橙色
|
|||
|
|
font = item.font()
|
|||
|
|
font.setBold(True)
|
|||
|
|
item.setFont(font)
|
|||
|
|
else:
|
|||
|
|
# 普通字段:默认颜色
|
|||
|
|
item.setBackground(QBrush(QColor("#e8f5e9"))) # 浅绿色背景
|
|||
|
|
font = item.font()
|
|||
|
|
font.setBold(True)
|
|||
|
|
item.setFont(font)
|
|||
|
|
item.setToolTip(f"未隐藏: {field_name} ({display_name})" + (" [加密]" if is_encrypted else ""))
|
|||
|
|
|
|||
|
|
return item
|
|||
|
|
|
|||
|
|
def filter_fields(self, search_text):
|
|||
|
|
"""根据搜索文本过滤配置项列表(保持分组),支持变量名和显示名称搜索"""
|
|||
|
|
search_text = search_text.strip().lower()
|
|||
|
|
search_type = self.search_type_combo.currentText() # 获取搜索类型
|
|||
|
|
|
|||
|
|
if not search_text:
|
|||
|
|
# 如果搜索文本为空,显示所有项
|
|||
|
|
for i in range(self.fields_list.count()):
|
|||
|
|
item = self.fields_list.item(i)
|
|||
|
|
# 跳过分隔符项
|
|||
|
|
if not self.is_separator_item(item):
|
|||
|
|
item.setHidden(False)
|
|||
|
|
else:
|
|||
|
|
# 模糊匹配:根据搜索类型进行匹配
|
|||
|
|
for i in range(self.fields_list.count()):
|
|||
|
|
item = self.fields_list.item(i)
|
|||
|
|
# 跳过分隔符项
|
|||
|
|
if not self.is_separator_item(item):
|
|||
|
|
# 根据搜索类型获取要匹配的文本
|
|||
|
|
if search_type == "变量名":
|
|||
|
|
# 搜索变量名
|
|||
|
|
field_name = item.data(Qt.ItemDataRole.UserRole)
|
|||
|
|
match_text = field_name.lower() if field_name else ""
|
|||
|
|
else: # 显示名称
|
|||
|
|
# 搜索显示名称
|
|||
|
|
display_name = item.data(Qt.ItemDataRole.UserRole + 1)
|
|||
|
|
match_text = display_name.lower() if display_name else ""
|
|||
|
|
|
|||
|
|
# 模糊匹配:检查搜索文本是否在匹配文本中
|
|||
|
|
if search_text in match_text:
|
|||
|
|
item.setHidden(False)
|
|||
|
|
else:
|
|||
|
|
item.setHidden(True)
|
|||
|
|
|
|||
|
|
self.update_fields_count()
|
|||
|
|
|
|||
|
|
def is_separator_item(self, item):
|
|||
|
|
"""检查是否是分隔符项"""
|
|||
|
|
text = item.text()
|
|||
|
|
return text.startswith("---") and text.endswith("---")
|
|||
|
|
|
|||
|
|
def clear_search(self):
|
|||
|
|
"""清除搜索框内容"""
|
|||
|
|
self.search_edit.clear()
|
|||
|
|
|
|||
|
|
def update_fields_count(self):
|
|||
|
|
"""更新配置项计数显示"""
|
|||
|
|
total_count = 0
|
|||
|
|
visible_count = 0
|
|||
|
|
hidden_count = 0
|
|||
|
|
encrypted_count = 0
|
|||
|
|
|
|||
|
|
for i in range(self.fields_list.count()):
|
|||
|
|
item = self.fields_list.item(i)
|
|||
|
|
# 跳过分隔符项
|
|||
|
|
if not self.is_separator_item(item):
|
|||
|
|
total_count += 1
|
|||
|
|
if not item.isHidden():
|
|||
|
|
visible_count += 1
|
|||
|
|
|
|||
|
|
# 统计隐藏状态
|
|||
|
|
if "已隐藏" in item.toolTip():
|
|||
|
|
hidden_count += 1
|
|||
|
|
|
|||
|
|
# 统计加密状态
|
|||
|
|
if "加密" in item.toolTip():
|
|||
|
|
encrypted_count += 1
|
|||
|
|
|
|||
|
|
visible_unhidden = visible_count - hidden_count
|
|||
|
|
visible_hidden = hidden_count
|
|||
|
|
|
|||
|
|
# 显示搜索类型信息
|
|||
|
|
search_type = self.search_type_combo.currentText()
|
|||
|
|
self.fields_count_label.setText(f"总计: {total_count} 个配置项 (显示: {visible_unhidden} 未隐藏, {visible_hidden} 隐藏, {encrypted_count} 加密) - 搜索类型: {search_type}")
|
|||
|
|
|
|||
|
|
def on_type_changed(self, field_type):
|
|||
|
|
"""当类型改变时,控制小数位数设置的显示"""
|
|||
|
|
# 只有float类型才显示小数位数设置
|
|||
|
|
if field_type == "float":
|
|||
|
|
self.decimal_label.setVisible(True)
|
|||
|
|
self.decimal_spinbox.setVisible(True)
|
|||
|
|
self.decimal_spinbox.setEnabled(True)
|
|||
|
|
else:
|
|||
|
|
self.decimal_label.setVisible(False)
|
|||
|
|
self.decimal_spinbox.setVisible(False)
|
|||
|
|
self.decimal_spinbox.setEnabled(False)
|
|||
|
|
|
|||
|
|
def on_hidden_changed(self, state):
|
|||
|
|
"""当隐藏状态改变时,更新左侧列表项的外观和位置"""
|
|||
|
|
current_item = self.fields_list.currentItem()
|
|||
|
|
if not current_item or self.is_separator_item(current_item):
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
field_name = current_item.data(Qt.ItemDataRole.UserRole)
|
|||
|
|
display_name = current_item.data(Qt.ItemDataRole.UserRole + 1)
|
|||
|
|
is_hidden = (state == Qt.CheckState.Checked.value)
|
|||
|
|
|
|||
|
|
# 更新工具提示
|
|||
|
|
if is_hidden:
|
|||
|
|
current_item.setToolTip(f"已隐藏: {field_name} ({display_name})")
|
|||
|
|
else:
|
|||
|
|
current_item.setToolTip(f"未隐藏: {field_name} ({display_name})")
|
|||
|
|
|
|||
|
|
# 更新外观
|
|||
|
|
if is_hidden:
|
|||
|
|
# 改为隐藏样式
|
|||
|
|
current_item.setForeground(QBrush(QColor("#999999")))
|
|||
|
|
font = current_item.font()
|
|||
|
|
font.setItalic(True)
|
|||
|
|
font.setBold(False)
|
|||
|
|
current_item.setFont(font)
|
|||
|
|
current_item.setBackground(QBrush()) # 清除背景色
|
|||
|
|
else:
|
|||
|
|
# 改为未隐藏样式
|
|||
|
|
current_item.setForeground(QBrush()) # 恢复默认前景色
|
|||
|
|
font = current_item.font()
|
|||
|
|
font.setItalic(False)
|
|||
|
|
font.setBold(True)
|
|||
|
|
current_item.setFont(font)
|
|||
|
|
current_item.setBackground(QBrush(QColor("#e8f5e9"))) # 浅绿色背景
|
|||
|
|
|
|||
|
|
def on_encrypted_changed(self, state):
|
|||
|
|
"""当加密状态改变时,更新左侧列表项的外观"""
|
|||
|
|
current_item = self.fields_list.currentItem()
|
|||
|
|
if not current_item or self.is_separator_item(current_item):
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
field_name = current_item.data(Qt.ItemDataRole.UserRole)
|
|||
|
|
display_name = current_item.data(Qt.ItemDataRole.UserRole + 1)
|
|||
|
|
is_encrypted = (state == Qt.CheckState.Checked.value)
|
|||
|
|
|
|||
|
|
# 更新工具提示
|
|||
|
|
current_tooltip = current_item.toolTip()
|
|||
|
|
if "加密" not in current_tooltip and is_encrypted:
|
|||
|
|
current_item.setToolTip(current_tooltip + " [加密]")
|
|||
|
|
elif "加密" in current_tooltip and not is_encrypted:
|
|||
|
|
current_item.setToolTip(current_tooltip.replace(" [加密]", ""))
|
|||
|
|
|
|||
|
|
# 更新显示文本
|
|||
|
|
current_text = current_item.text()
|
|||
|
|
if "🔒" not in current_text and is_encrypted:
|
|||
|
|
current_item.setText(current_text + " 🔒")
|
|||
|
|
elif "🔒" in current_text and not is_encrypted:
|
|||
|
|
current_item.setText(current_text.replace(" 🔒", ""))
|
|||
|
|
|
|||
|
|
# 更新外观
|
|||
|
|
if is_encrypted:
|
|||
|
|
# 加密字段:橙色高亮
|
|||
|
|
current_item.setForeground(QBrush(QColor("#ff5722"))) # 橙色
|
|||
|
|
font = current_item.font()
|
|||
|
|
font.setBold(True)
|
|||
|
|
current_item.setFont(font)
|
|||
|
|
else:
|
|||
|
|
# 恢复默认外观
|
|||
|
|
current_item.setForeground(QBrush()) # 恢复默认前景色
|
|||
|
|
font = current_item.font()
|
|||
|
|
font.setBold(True)
|
|||
|
|
current_item.setFont(font)
|
|||
|
|
|
|||
|
|
def on_field_selected(self, current, previous):
|
|||
|
|
# 保存前一个字段的更改
|
|||
|
|
if previous is not None and not self.is_separator_item(previous):
|
|||
|
|
previous_field_name = previous.data(Qt.ItemDataRole.UserRole)
|
|||
|
|
self.save_current_field_changes(previous_field_name)
|
|||
|
|
|
|||
|
|
if not current or self.is_separator_item(current):
|
|||
|
|
# 清空右侧面板
|
|||
|
|
self.name_label.setText("")
|
|||
|
|
self.display_name_edit.clear()
|
|||
|
|
self.category_combo.setCurrentText("")
|
|||
|
|
self.type_combo.setCurrentText("auto")
|
|||
|
|
self.decimal_spinbox.setValue(2)
|
|||
|
|
self.hidden_checkbox.setChecked(False)
|
|||
|
|
self.encrypted_checkbox.setChecked(False)
|
|||
|
|
self.tooltip_edit.clear()
|
|||
|
|
self.min_edit.clear()
|
|||
|
|
self.max_edit.clear()
|
|||
|
|
self.regex_edit.clear()
|
|||
|
|
self.required_checkbox.setChecked(False)
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
field_name = current.data(Qt.ItemDataRole.UserRole)
|
|||
|
|
display_name = current.data(Qt.ItemDataRole.UserRole + 1)
|
|||
|
|
|
|||
|
|
self.name_label.setText(field_name)
|
|||
|
|
|
|||
|
|
# 从规则中获取显示名称,如果规则中没有则使用当前存储的显示名称
|
|||
|
|
rule_display_name = self.rules.get("display_names", {}).get(field_name, display_name)
|
|||
|
|
self.display_name_edit.setText(rule_display_name)
|
|||
|
|
|
|||
|
|
# 查找配置项所属的分组
|
|||
|
|
category = "未分类"
|
|||
|
|
for cat, fields in self.rules.get("categories", {}).items():
|
|||
|
|
if field_name in fields:
|
|||
|
|
category = cat
|
|||
|
|
break
|
|||
|
|
|
|||
|
|
# 设置分组
|
|||
|
|
index = self.category_combo.findText(category)
|
|||
|
|
if index >= 0:
|
|||
|
|
self.category_combo.setCurrentIndex(index)
|
|||
|
|
else:
|
|||
|
|
self.category_combo.setCurrentText(category)
|
|||
|
|
|
|||
|
|
field_type = self.rules.get("field_types", {}).get(field_name, "auto")
|
|||
|
|
index = self.type_combo.findText(field_type)
|
|||
|
|
if index >= 0:
|
|||
|
|
self.type_combo.setCurrentIndex(index)
|
|||
|
|
|
|||
|
|
# 设置小数位数
|
|||
|
|
decimals = self.rules.get("field_decimals", {}).get(field_name, 6) # 默认6位小数
|
|||
|
|
self.decimal_spinbox.setValue(decimals)
|
|||
|
|
|
|||
|
|
# 根据类型显示/隐藏小数位数设置
|
|||
|
|
self.on_type_changed(field_type)
|
|||
|
|
|
|||
|
|
# 隐藏状态
|
|||
|
|
hidden = field_name in self.rules.get("hidden", [])
|
|||
|
|
self.hidden_checkbox.setChecked(hidden)
|
|||
|
|
|
|||
|
|
# 加密状态
|
|||
|
|
encrypted = field_name in self.rules.get("encrypted_fields", [])
|
|||
|
|
self.encrypted_checkbox.setChecked(encrypted)
|
|||
|
|
|
|||
|
|
tooltip = self.rules.get("tooltips", {}).get(field_name, "")
|
|||
|
|
self.tooltip_edit.setPlainText(tooltip)
|
|||
|
|
|
|||
|
|
# 校验规则
|
|||
|
|
validation = self.rules.get("validations", {}).get(field_name, {})
|
|||
|
|
self.min_edit.setText(validation.get("min", ""))
|
|||
|
|
self.max_edit.setText(validation.get("max", ""))
|
|||
|
|
self.regex_edit.setText(validation.get("regex", ""))
|
|||
|
|
self.required_checkbox.setChecked(validation.get("required", False))
|
|||
|
|
|
|||
|
|
def save_current_field_changes(self, field_name):
|
|||
|
|
"""保存当前字段的更改到规则字典"""
|
|||
|
|
if not field_name:
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
# 更新显示名称
|
|||
|
|
display_name = self.display_name_edit.text().strip()
|
|||
|
|
if display_name:
|
|||
|
|
self.rules.setdefault("display_names", {})[field_name] = display_name
|
|||
|
|
elif field_name in self.rules.get("display_names", {}):
|
|||
|
|
del self.rules["display_names"][field_name]
|
|||
|
|
|
|||
|
|
# 更新分组
|
|||
|
|
category = self.category_combo.currentText().strip()
|
|||
|
|
if category == "未分类":
|
|||
|
|
# 从所有分组中移除该字段
|
|||
|
|
for cat, fields in self.rules.setdefault("categories", {}).items():
|
|||
|
|
if cat != "未分类" and field_name in fields:
|
|||
|
|
fields.remove(field_name)
|
|||
|
|
# 确保在"未分类"分组中
|
|||
|
|
if "未分类" not in self.rules["categories"]:
|
|||
|
|
self.rules["categories"]["未分类"] = []
|
|||
|
|
if field_name not in self.rules["categories"]["未分类"]:
|
|||
|
|
self.rules["categories"]["未分类"].append(field_name)
|
|||
|
|
elif category: # 其他分组
|
|||
|
|
# 从所有分组中移除该字段
|
|||
|
|
for cat, fields in self.rules.setdefault("categories", {}).items():
|
|||
|
|
if field_name in fields:
|
|||
|
|
fields.remove(field_name)
|
|||
|
|
|
|||
|
|
# 确保"未分类"分组存在
|
|||
|
|
if "未分类" not in self.rules["categories"]:
|
|||
|
|
self.rules["categories"]["未分类"] = []
|
|||
|
|
|
|||
|
|
# 添加到新分组
|
|||
|
|
if category not in self.rules["categories"]:
|
|||
|
|
self.rules["categories"][category] = []
|
|||
|
|
if field_name not in self.rules["categories"][category]:
|
|||
|
|
self.rules["categories"][category].append(field_name)
|
|||
|
|
|
|||
|
|
# 更新字段类型
|
|||
|
|
field_type = self.type_combo.currentText()
|
|||
|
|
if field_type != "auto":
|
|||
|
|
self.rules.setdefault("field_types", {})[field_name] = field_type
|
|||
|
|
|
|||
|
|
# 更新小数位数(仅当类型为float时保存)
|
|||
|
|
if field_type == "float":
|
|||
|
|
decimals = self.decimal_spinbox.value()
|
|||
|
|
self.rules.setdefault("field_decimals", {})[field_name] = decimals
|
|||
|
|
elif field_name in self.rules.get("field_decimals", {}):
|
|||
|
|
# 如果类型不是float但存在小数位数设置,删除它
|
|||
|
|
del self.rules["field_decimals"][field_name]
|
|||
|
|
elif field_name in self.rules.get("field_types", {}):
|
|||
|
|
del self.rules["field_types"][field_name]
|
|||
|
|
# 如果字段类型被删除,也删除小数位数设置
|
|||
|
|
if field_name in self.rules.get("field_decimals", {}):
|
|||
|
|
del self.rules["field_decimals"][field_name]
|
|||
|
|
|
|||
|
|
# 更新提示信息
|
|||
|
|
tooltip = self.tooltip_edit.toPlainText().strip()
|
|||
|
|
if tooltip:
|
|||
|
|
self.rules.setdefault("tooltips", {})[field_name] = tooltip
|
|||
|
|
elif field_name in self.rules.get("tooltips", {}):
|
|||
|
|
del self.rules["tooltips"][field_name]
|
|||
|
|
|
|||
|
|
# 更新隐藏状态
|
|||
|
|
hidden = self.hidden_checkbox.isChecked()
|
|||
|
|
hidden_list = self.rules.setdefault("hidden", [])
|
|||
|
|
if hidden and field_name not in hidden_list:
|
|||
|
|
hidden_list.append(field_name)
|
|||
|
|
elif not hidden and field_name in hidden_list:
|
|||
|
|
hidden_list.remove(field_name)
|
|||
|
|
|
|||
|
|
# 更新加密状态
|
|||
|
|
encrypted = self.encrypted_checkbox.isChecked()
|
|||
|
|
encrypted_list = self.rules.setdefault("encrypted_fields", [])
|
|||
|
|
if encrypted and field_name not in encrypted_list:
|
|||
|
|
encrypted_list.append(field_name)
|
|||
|
|
elif not encrypted and field_name in encrypted_list:
|
|||
|
|
encrypted_list.remove(field_name)
|
|||
|
|
|
|||
|
|
# 更新校验规则
|
|||
|
|
validation = {}
|
|||
|
|
min_val = self.min_edit.text().strip()
|
|||
|
|
if min_val:
|
|||
|
|
validation["min"] = min_val
|
|||
|
|
|
|||
|
|
max_val = self.max_edit.text().strip()
|
|||
|
|
if max_val:
|
|||
|
|
validation["max"] = max_val
|
|||
|
|
|
|||
|
|
regex_val = self.regex_edit.text().strip()
|
|||
|
|
if regex_val:
|
|||
|
|
validation["regex"] = regex_val
|
|||
|
|
|
|||
|
|
validation["required"] = self.required_checkbox.isChecked()
|
|||
|
|
|
|||
|
|
if validation:
|
|||
|
|
self.rules.setdefault("validations", {})[field_name] = validation
|
|||
|
|
elif field_name in self.rules.get("validations", {}):
|
|||
|
|
del self.rules["validations"][field_name]
|
|||
|
|
|
|||
|
|
def accept(self):
|
|||
|
|
"""重写accept方法,确保保存当前字段的更改"""
|
|||
|
|
current_item = self.fields_list.currentItem()
|
|||
|
|
if current_item and not self.is_separator_item(current_item):
|
|||
|
|
field_name = current_item.data(Qt.ItemDataRole.UserRole)
|
|||
|
|
self.save_current_field_changes(field_name)
|
|||
|
|
|
|||
|
|
super().accept()
|
|||
|
|
|
|||
|
|
def add_group(self):
|
|||
|
|
new_group, ok = QInputDialog.getText(self, "添加分组", "请输入新分组名称:")
|
|||
|
|
if ok and new_group.strip():
|
|||
|
|
if new_group not in self.rules.setdefault("categories", {}):
|
|||
|
|
self.rules["categories"][new_group] = []
|
|||
|
|
self.category_combo.addItem(new_group)
|
|||
|
|
|
|||
|
|
def remove_group(self):
|
|||
|
|
current_group = self.category_combo.currentText()
|
|||
|
|
if current_group and current_group != "未分类":
|
|||
|
|
reply = QMessageBox.question(self, "确认删除",
|
|||
|
|
f"确定要删除分组 '{current_group}' 吗?\n该分组中的所有配置项将被移到'未分类'分组。")
|
|||
|
|
if reply == QMessageBox.StandardButton.Yes:
|
|||
|
|
if current_group in self.rules.get("categories", {}):
|
|||
|
|
# 将该分组中的配置项移到"未分类"分组
|
|||
|
|
fields_to_move = self.rules["categories"][current_group]
|
|||
|
|
if "未分类" not in self.rules["categories"]:
|
|||
|
|
self.rules["categories"]["未分类"] = []
|
|||
|
|
|
|||
|
|
for field_name in fields_to_move:
|
|||
|
|
if field_name not in self.rules["categories"]["未分类"]:
|
|||
|
|
self.rules["categories"]["未分类"].append(field_name)
|
|||
|
|
|
|||
|
|
# 删除分组
|
|||
|
|
del self.rules["categories"][current_group]
|
|||
|
|
|
|||
|
|
# 从下拉框中移除
|
|||
|
|
index = self.category_combo.findText(current_group)
|
|||
|
|
if index >= 0:
|
|||
|
|
self.category_combo.removeItem(index)
|
|||
|
|
# 设置当前选中的分组为"未分类"
|
|||
|
|
self.category_combo.setCurrentText("未分类")
|
|||
|
|
|
|||
|
|
def show_all_fields(self):
|
|||
|
|
"""批量显示所有配置项(取消所有隐藏)"""
|
|||
|
|
reply = QMessageBox.question(self, "确认",
|
|||
|
|
"确定要取消所有配置项的隐藏状态吗?",
|
|||
|
|
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
|
|||
|
|
if reply == QMessageBox.StandardButton.Yes:
|
|||
|
|
# 清空隐藏列表
|
|||
|
|
self.rules["hidden"] = []
|
|||
|
|
|
|||
|
|
# 重新加载数据以更新显示
|
|||
|
|
self.load_data()
|
|||
|
|
|
|||
|
|
QMessageBox.information(self, "完成", "已取消所有配置项的隐藏状态")
|
|||
|
|
|
|||
|
|
def hide_all_fields(self):
|
|||
|
|
"""批量隐藏所有配置项"""
|
|||
|
|
reply = QMessageBox.question(self, "确认",
|
|||
|
|
"确定要隐藏所有配置项吗?",
|
|||
|
|
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No)
|
|||
|
|
if reply == QMessageBox.StandardButton.Yes:
|
|||
|
|
# 获取所有配置项的名称
|
|||
|
|
all_fields = list(self.config_fields.keys())
|
|||
|
|
|
|||
|
|
# 将所有配置项添加到隐藏列表
|
|||
|
|
self.rules["hidden"] = all_fields
|
|||
|
|
|
|||
|
|
# 重新加载数据以更新显示
|
|||
|
|
self.load_data()
|
|||
|
|
|
|||
|
|
QMessageBox.information(self, "完成", "已隐藏所有配置项")
|
|||
|
|
|
|||
|
|
# 导入规则文件功能
|
|||
|
|
def import_rules_file(self):
|
|||
|
|
"""导入规则文件"""
|
|||
|
|
# 确认保存当前更改
|
|||
|
|
current_item = self.fields_list.currentItem()
|
|||
|
|
if current_item and not self.is_separator_item(current_item):
|
|||
|
|
field_name = current_item.data(Qt.ItemDataRole.UserRole)
|
|||
|
|
self.save_current_field_changes(field_name)
|
|||
|
|
|
|||
|
|
# 选择要导入的规则文件
|
|||
|
|
file_path, _ = QFileDialog.getOpenFileName(
|
|||
|
|
self, "选择规则文件",
|
|||
|
|
str(Path.home()),
|
|||
|
|
"JSON规则文件 (*.json);;所有文件 (*)"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if not file_path:
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
# 读取规则文件
|
|||
|
|
with open(file_path, 'r', encoding='utf-8') as f:
|
|||
|
|
imported_rules = json.load(f)
|
|||
|
|
|
|||
|
|
# 验证规则文件格式
|
|||
|
|
if not isinstance(imported_rules, dict):
|
|||
|
|
QMessageBox.warning(self, "导入失败", "规则文件格式不正确")
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
# 显示导入确认对话框
|
|||
|
|
reply = QMessageBox.question(
|
|||
|
|
self, "确认导入",
|
|||
|
|
f"确定要导入规则文件吗?\n\n文件: {os.path.basename(file_path)}\n\n"
|
|||
|
|
f"这将覆盖当前的规则设置。",
|
|||
|
|
QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No,
|
|||
|
|
QMessageBox.StandardButton.No
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if reply != QMessageBox.StandardButton.Yes:
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
# 完全替换现有规则
|
|||
|
|
self.rules.clear()
|
|||
|
|
self.rules.update(imported_rules)
|
|||
|
|
|
|||
|
|
# 确保规则结构完整
|
|||
|
|
ensure_rule_structure(self.rules)
|
|||
|
|
|
|||
|
|
# 重新加载数据
|
|||
|
|
self.load_data()
|
|||
|
|
|
|||
|
|
QMessageBox.information(
|
|||
|
|
self, "导入成功",
|
|||
|
|
f"规则文件导入成功!\n\n"
|
|||
|
|
f"已导入 {len(self.rules.get('display_names', {}))} 个显示名称设置\n"
|
|||
|
|
f"已导入 {len(self.rules.get('categories', {}))} 个分组设置\n"
|
|||
|
|
f"已导入 {len(self.rules.get('hidden', []))} 个隐藏设置"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
except json.JSONDecodeError:
|
|||
|
|
QMessageBox.warning(self, "导入失败", "规则文件格式不正确,不是有效的JSON文件")
|
|||
|
|
except Exception as e:
|
|||
|
|
QMessageBox.critical(self, "导入失败", f"导入规则文件时发生错误:{str(e)}")
|
|||
|
|
|
|||
|
|
# 导出规则文件功能
|
|||
|
|
def export_rules_file(self):
|
|||
|
|
"""导出规则文件"""
|
|||
|
|
# 确认保存当前更改
|
|||
|
|
current_item = self.fields_list.currentItem()
|
|||
|
|
if current_item and not self.is_separator_item(current_item):
|
|||
|
|
field_name = current_item.data(Qt.ItemDataRole.UserRole)
|
|||
|
|
self.save_current_field_changes(field_name)
|
|||
|
|
|
|||
|
|
# 选择导出位置和文件名
|
|||
|
|
default_name = f"config_editor_rules_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
|
|||
|
|
file_path, _ = QFileDialog.getSaveFileName(
|
|||
|
|
self, "导出规则文件",
|
|||
|
|
default_name,
|
|||
|
|
"JSON规则文件 (*.json);;所有文件 (*)"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if not file_path:
|
|||
|
|
return
|
|||
|
|
|
|||
|
|
# 确保文件扩展名是.json
|
|||
|
|
if not file_path.lower().endswith('.json'):
|
|||
|
|
file_path += '.json'
|
|||
|
|
|
|||
|
|
try:
|
|||
|
|
# 准备导出的规则数据
|
|||
|
|
export_data = self.rules.copy()
|
|||
|
|
|
|||
|
|
# 添加导出信息
|
|||
|
|
export_data["_export_info"] = {
|
|||
|
|
"export_time": datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
|||
|
|
"export_version": "1.0",
|
|||
|
|
"note": "配置文件编辑器规则文件"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# 添加统计信息
|
|||
|
|
export_data["_statistics"] = {
|
|||
|
|
"total_fields": len(self.all_fields),
|
|||
|
|
"display_names_count": len(self.rules.get("display_names", {})),
|
|||
|
|
"categories_count": len(self.rules.get("categories", {})),
|
|||
|
|
"hidden_fields_count": len(self.rules.get("hidden", [])),
|
|||
|
|
"encrypted_fields_count": len(self.rules.get("encrypted_fields", [])),
|
|||
|
|
"field_types_count": len(self.rules.get("field_types", {})),
|
|||
|
|
"validations_count": len(self.rules.get("validations", {}))
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# 写入文件
|
|||
|
|
with open(file_path, 'w', encoding='utf-8') as f:
|
|||
|
|
json.dump(export_data, f, indent=2, ensure_ascii=False)
|
|||
|
|
|
|||
|
|
QMessageBox.information(
|
|||
|
|
self, "导出成功",
|
|||
|
|
f"规则文件已成功导出到:\n{file_path}\n\n"
|
|||
|
|
f"导出时间:{datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n"
|
|||
|
|
f"规则数量:{len(export_data) - 2}" # 减去_export_info和_statistics
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
except Exception as e:
|
|||
|
|
QMessageBox.critical(self, "导出失败", f"导出规则文件时发生错误:{str(e)}")
|
|||
|
|
|
|||
|
|
def get_updated_rules(self):
|
|||
|
|
"""获取更新后的规则"""
|
|||
|
|
return self.rules
|
|||
|
|
|
|||
|
|
class ChangeConfirmDialog(QDialog):
|
|||
|
|
"""变更确认对话框"""
|
|||
|
|
def __init__(self, changes, parent=None):
|
|||
|
|
super().__init__(parent)
|
|||
|
|
self.setWindowTitle("确认配置变更")
|
|||
|
|
self.setModal(True)
|
|||
|
|
self.setMinimumSize(600, 400)
|
|||
|
|
self.resize(600, 400)
|
|||
|
|
|
|||
|
|
# 设置窗口标志,避免重影问题
|
|||
|
|
self.setWindowFlags(Qt.WindowType.Dialog | Qt.WindowType.WindowTitleHint |
|
|||
|
|
Qt.WindowType.WindowCloseButtonHint | Qt.WindowType.WindowStaysOnTopHint)
|
|||
|
|
|
|||
|
|
self.init_ui(changes)
|
|||
|
|
|
|||
|
|
def init_ui(self, changes):
|
|||
|
|
layout = QVBoxLayout(self)
|
|||
|
|
|
|||
|
|
# 标题
|
|||
|
|
title_label = QLabel(f"以下 {len(changes)} 个配置项将被修改:")
|
|||
|
|
title_label.setStyleSheet("font-weight: bold; font-size: 14px;")
|
|||
|
|
layout.addWidget(title_label)
|
|||
|
|
|
|||
|
|
# 变更列表
|
|||
|
|
table = QTableWidget()
|
|||
|
|
table.setColumnCount(3)
|
|||
|
|
table.setHorizontalHeaderLabels(["配置项", "原值", "新值"])
|
|||
|
|
table.setRowCount(len(changes))
|
|||
|
|
|
|||
|
|
for row, (field_name, (old_value, new_value)) in enumerate(changes.items()):
|
|||
|
|
table.setItem(row, 0, QTableWidgetItem(field_name))
|
|||
|
|
table.setItem(row, 1, QTableWidgetItem(str(old_value)))
|
|||
|
|
table.setItem(row, 2, QTableWidgetItem(str(new_value)))
|
|||
|
|
|
|||
|
|
table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeMode.Stretch)
|
|||
|
|
table.setSelectionBehavior(QTableWidget.SelectionBehavior.SelectRows)
|
|||
|
|
layout.addWidget(table)
|
|||
|
|
|
|||
|
|
# 按钮
|
|||
|
|
button_box = QDialogButtonBox(
|
|||
|
|
QDialogButtonBox.StandardButton.Ok |
|
|||
|
|
QDialogButtonBox.StandardButton.Cancel
|
|||
|
|
)
|
|||
|
|
button_box.accepted.connect(self.accept)
|
|||
|
|
button_box.rejected.connect(self.reject)
|
|||
|
|
layout.addWidget(button_box)
|