259 lines
8.3 KiB
Python
259 lines
8.3 KiB
Python
|
|
"""
|
||
|
|
字段编辑对话框
|
||
|
|
用于创建和编辑字段
|
||
|
|
"""
|
||
|
|
from PyQt5.QtWidgets import (
|
||
|
|
QDialog, QVBoxLayout, QHBoxLayout, QFormLayout,
|
||
|
|
QLineEdit, QComboBox, QTextEdit, QPushButton,
|
||
|
|
QLabel, QMessageBox, QGroupBox
|
||
|
|
)
|
||
|
|
from PyQt5.QtCore import Qt, pyqtSignal
|
||
|
|
|
||
|
|
from models.field import Field
|
||
|
|
from config import FieldType
|
||
|
|
from utils.validator import Validator
|
||
|
|
from utils.logger import get_logger
|
||
|
|
|
||
|
|
logger = get_logger(__name__)
|
||
|
|
|
||
|
|
|
||
|
|
class FieldDialog(QDialog):
|
||
|
|
"""字段编辑对话框"""
|
||
|
|
|
||
|
|
field_saved = pyqtSignal(Field)
|
||
|
|
|
||
|
|
def __init__(self, parent=None, field: Field = None):
|
||
|
|
super().__init__(parent)
|
||
|
|
|
||
|
|
self.field = field
|
||
|
|
self.validator = Validator()
|
||
|
|
|
||
|
|
self.init_ui()
|
||
|
|
|
||
|
|
if field:
|
||
|
|
self.load_field_data(field)
|
||
|
|
self.setWindowTitle("编辑字段")
|
||
|
|
else:
|
||
|
|
self.setWindowTitle("新建字段")
|
||
|
|
|
||
|
|
def init_ui(self):
|
||
|
|
"""初始化UI"""
|
||
|
|
self.setModal(True)
|
||
|
|
self.setMinimumWidth(500)
|
||
|
|
|
||
|
|
# 主布局
|
||
|
|
main_layout = QVBoxLayout(self)
|
||
|
|
|
||
|
|
# 基本信息组
|
||
|
|
basic_group = QGroupBox("基本信息")
|
||
|
|
basic_layout = QFormLayout(basic_group)
|
||
|
|
|
||
|
|
# 完整名称
|
||
|
|
self.full_name_edit = QLineEdit()
|
||
|
|
self.full_name_edit.setPlaceholderText("例如: gis.map.location")
|
||
|
|
basic_layout.addRow("完整名称*:", self.full_name_edit)
|
||
|
|
|
||
|
|
# 域
|
||
|
|
self.domain_edit = QLineEdit()
|
||
|
|
self.domain_edit.setPlaceholderText("例如: gis")
|
||
|
|
basic_layout.addRow("域*:", self.domain_edit)
|
||
|
|
|
||
|
|
# 子域
|
||
|
|
self.sub_domains_edit = QLineEdit()
|
||
|
|
self.sub_domains_edit.setPlaceholderText("例如: map (多个用点号分隔)")
|
||
|
|
basic_layout.addRow("子域:", self.sub_domains_edit)
|
||
|
|
|
||
|
|
# 字段名
|
||
|
|
self.name_edit = QLineEdit()
|
||
|
|
self.name_edit.setPlaceholderText("例如: location")
|
||
|
|
basic_layout.addRow("字段名*:", self.name_edit)
|
||
|
|
|
||
|
|
# 类型
|
||
|
|
self.type_combo = QComboBox()
|
||
|
|
for field_type in FieldType:
|
||
|
|
self.type_combo.addItem(field_type.value, field_type)
|
||
|
|
basic_layout.addRow("类型*:", self.type_combo)
|
||
|
|
|
||
|
|
main_layout.addWidget(basic_group)
|
||
|
|
|
||
|
|
# 约束信息组
|
||
|
|
constraint_group = QGroupBox("约束信息")
|
||
|
|
constraint_layout = QFormLayout(constraint_group)
|
||
|
|
|
||
|
|
# 最小值
|
||
|
|
self.range_min_edit = QLineEdit()
|
||
|
|
self.range_min_edit.setPlaceholderText("可选")
|
||
|
|
constraint_layout.addRow("最小值:", self.range_min_edit)
|
||
|
|
|
||
|
|
# 最大值
|
||
|
|
self.range_max_edit = QLineEdit()
|
||
|
|
self.range_max_edit.setPlaceholderText("可选")
|
||
|
|
constraint_layout.addRow("最大值:", self.range_max_edit)
|
||
|
|
|
||
|
|
# 默认值
|
||
|
|
self.default_value_edit = QLineEdit()
|
||
|
|
self.default_value_edit.setPlaceholderText("可选")
|
||
|
|
constraint_layout.addRow("默认值:", self.default_value_edit)
|
||
|
|
|
||
|
|
# 单位
|
||
|
|
self.unit_edit = QLineEdit()
|
||
|
|
self.unit_edit.setPlaceholderText("例如: m, km/h")
|
||
|
|
constraint_layout.addRow("单位:", self.unit_edit)
|
||
|
|
|
||
|
|
main_layout.addWidget(constraint_group)
|
||
|
|
|
||
|
|
# 描述
|
||
|
|
desc_group = QGroupBox("描述")
|
||
|
|
desc_layout = QVBoxLayout(desc_group)
|
||
|
|
|
||
|
|
self.description_edit = QTextEdit()
|
||
|
|
self.description_edit.setPlaceholderText("字段的详细描述...")
|
||
|
|
self.description_edit.setMaximumHeight(100)
|
||
|
|
desc_layout.addWidget(self.description_edit)
|
||
|
|
|
||
|
|
main_layout.addWidget(desc_group)
|
||
|
|
|
||
|
|
# 提示信息
|
||
|
|
hint_label = QLabel("* 标记为必填项")
|
||
|
|
hint_label.setStyleSheet("color: #666666; font-size: 11px;")
|
||
|
|
main_layout.addWidget(hint_label)
|
||
|
|
|
||
|
|
# 按钮
|
||
|
|
button_layout = QHBoxLayout()
|
||
|
|
|
||
|
|
self.save_btn = QPushButton("保存")
|
||
|
|
self.save_btn.clicked.connect(self.on_save)
|
||
|
|
self.save_btn.setDefault(True)
|
||
|
|
|
||
|
|
self.cancel_btn = QPushButton("取消")
|
||
|
|
self.cancel_btn.clicked.connect(self.reject)
|
||
|
|
|
||
|
|
button_layout.addStretch()
|
||
|
|
button_layout.addWidget(self.save_btn)
|
||
|
|
button_layout.addWidget(self.cancel_btn)
|
||
|
|
|
||
|
|
main_layout.addLayout(button_layout)
|
||
|
|
|
||
|
|
# 连接信号:自动生成完整名称
|
||
|
|
self.domain_edit.textChanged.connect(self.update_full_name)
|
||
|
|
self.sub_domains_edit.textChanged.connect(self.update_full_name)
|
||
|
|
self.name_edit.textChanged.connect(self.update_full_name)
|
||
|
|
|
||
|
|
def update_full_name(self):
|
||
|
|
"""自动更新完整名称"""
|
||
|
|
domain = self.domain_edit.text().strip()
|
||
|
|
sub_domains = self.sub_domains_edit.text().strip()
|
||
|
|
name = self.name_edit.text().strip()
|
||
|
|
|
||
|
|
if domain and name:
|
||
|
|
parts = [domain]
|
||
|
|
if sub_domains:
|
||
|
|
parts.extend(sub_domains.split('.'))
|
||
|
|
parts.append(name)
|
||
|
|
full_name = '.'.join(parts)
|
||
|
|
self.full_name_edit.setText(full_name)
|
||
|
|
|
||
|
|
def load_field_data(self, field: Field):
|
||
|
|
"""加载字段数据"""
|
||
|
|
self.full_name_edit.setText(field.full_name)
|
||
|
|
self.domain_edit.setText(field.domain)
|
||
|
|
|
||
|
|
if field.sub_domains:
|
||
|
|
self.sub_domains_edit.setText('.'.join(field.sub_domains))
|
||
|
|
|
||
|
|
self.name_edit.setText(field.name)
|
||
|
|
|
||
|
|
# 设置类型
|
||
|
|
index = self.type_combo.findData(field.type)
|
||
|
|
if index >= 0:
|
||
|
|
self.type_combo.setCurrentIndex(index)
|
||
|
|
|
||
|
|
if field.range_min:
|
||
|
|
self.range_min_edit.setText(field.range_min)
|
||
|
|
|
||
|
|
if field.range_max:
|
||
|
|
self.range_max_edit.setText(field.range_max)
|
||
|
|
|
||
|
|
if field.default_value:
|
||
|
|
self.default_value_edit.setText(field.default_value)
|
||
|
|
|
||
|
|
if field.unit:
|
||
|
|
self.unit_edit.setText(field.unit)
|
||
|
|
|
||
|
|
if field.description:
|
||
|
|
self.description_edit.setText(field.description)
|
||
|
|
|
||
|
|
def validate_input(self) -> tuple[bool, str]:
|
||
|
|
"""验证输入"""
|
||
|
|
# 必填项检查
|
||
|
|
if not self.full_name_edit.text().strip():
|
||
|
|
return False, "完整名称不能为空"
|
||
|
|
|
||
|
|
if not self.domain_edit.text().strip():
|
||
|
|
return False, "域不能为空"
|
||
|
|
|
||
|
|
if not self.name_edit.text().strip():
|
||
|
|
return False, "字段名不能为空"
|
||
|
|
|
||
|
|
# 验证字段名格式
|
||
|
|
is_valid, error_msg = self.validator.validate_field_name(
|
||
|
|
self.full_name_edit.text().strip()
|
||
|
|
)
|
||
|
|
if not is_valid:
|
||
|
|
return False, error_msg
|
||
|
|
|
||
|
|
# 验证域名格式
|
||
|
|
is_valid, error_msg = self.validator.validate_domain(
|
||
|
|
self.domain_edit.text().strip()
|
||
|
|
)
|
||
|
|
if not is_valid:
|
||
|
|
return False, error_msg
|
||
|
|
|
||
|
|
# 验证范围值
|
||
|
|
field_type = self.type_combo.currentData()
|
||
|
|
is_valid, error_msg = self.validator.validate_range(
|
||
|
|
self.range_min_edit.text().strip() or None,
|
||
|
|
self.range_max_edit.text().strip() or None,
|
||
|
|
field_type
|
||
|
|
)
|
||
|
|
if not is_valid:
|
||
|
|
return False, error_msg
|
||
|
|
|
||
|
|
return True, ""
|
||
|
|
|
||
|
|
def on_save(self):
|
||
|
|
"""保存字段"""
|
||
|
|
# 验证输入
|
||
|
|
is_valid, error_msg = self.validate_input()
|
||
|
|
if not is_valid:
|
||
|
|
QMessageBox.warning(self, "验证失败", error_msg)
|
||
|
|
return
|
||
|
|
|
||
|
|
# 构建字段对象
|
||
|
|
sub_domains_str = self.sub_domains_edit.text().strip()
|
||
|
|
sub_domains = sub_domains_str.split('.') if sub_domains_str else []
|
||
|
|
|
||
|
|
field_data = {
|
||
|
|
'full_name': self.full_name_edit.text().strip(),
|
||
|
|
'domain': self.domain_edit.text().strip(),
|
||
|
|
'sub_domains': sub_domains,
|
||
|
|
'name': self.name_edit.text().strip(),
|
||
|
|
'type': self.type_combo.currentData().value,
|
||
|
|
'range_min': self.range_min_edit.text().strip() or None,
|
||
|
|
'range_max': self.range_max_edit.text().strip() or None,
|
||
|
|
'default_value': self.default_value_edit.text().strip() or None,
|
||
|
|
'unit': self.unit_edit.text().strip() or None,
|
||
|
|
'description': self.description_edit.toPlainText().strip() or None,
|
||
|
|
}
|
||
|
|
|
||
|
|
if self.field:
|
||
|
|
field_data['id'] = self.field.id
|
||
|
|
|
||
|
|
try:
|
||
|
|
field = Field.from_dict(field_data)
|
||
|
|
self.field_saved.emit(field)
|
||
|
|
self.accept()
|
||
|
|
except Exception as e:
|
||
|
|
QMessageBox.critical(self, "错误", f"创建字段对象失败: {str(e)}")
|
||
|
|
logger.error(f"Failed to create field object: {e}")
|