""" 字段编辑对话框 用于创建和编辑字段 """ 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}")