""" 字段树形控件 用于显示和管理字段的树形结构 """ from PyQt5.QtWidgets import ( QWidget, QVBoxLayout, QHBoxLayout, QTreeWidget, QTreeWidgetItem, QPushButton, QLineEdit, QLabel, QComboBox, QMessageBox, QSplitter, QGroupBox, QFormLayout, QTextEdit ) from PyQt5.QtCore import Qt, pyqtSignal from controllers.field_controller import FieldController from models.field import Field from config import FieldType from utils.logger import get_logger logger = get_logger(__name__) class FieldManagementWidget(QWidget): """字段管理控件""" field_selected = pyqtSignal(Field) def __init__(self, controller: FieldController): super().__init__() self.controller = controller self.current_field = None self.init_ui() self.load_fields() def init_ui(self): """初始化UI""" # 主布局 main_layout = QHBoxLayout(self) # 创建分割器 splitter = QSplitter(Qt.Horizontal) main_layout.addWidget(splitter) # 左侧:树形结构和搜索 left_widget = QWidget() left_layout = QVBoxLayout(left_widget) # 搜索区域 search_group = QGroupBox("搜索") search_layout = QVBoxLayout(search_group) search_form = QFormLayout() self.domain_input = QLineEdit() self.domain_input.setPlaceholderText("输入域名...") search_form.addRow("域名:", self.domain_input) self.name_input = QLineEdit() self.name_input.setPlaceholderText("输入字段名...") search_form.addRow("字段名:", self.name_input) self.type_combo = QComboBox() self.type_combo.addItem("全部", None) for field_type in FieldType: self.type_combo.addItem(field_type.value, field_type) search_form.addRow("类型:", self.type_combo) search_layout.addLayout(search_form) search_btn_layout = QHBoxLayout() self.search_btn = QPushButton("搜索") self.search_btn.clicked.connect(self.on_search) self.clear_btn = QPushButton("清除") self.clear_btn.clicked.connect(self.on_clear_search) search_btn_layout.addWidget(self.search_btn) search_btn_layout.addWidget(self.clear_btn) search_layout.addLayout(search_btn_layout) left_layout.addWidget(search_group) # 字段树 self.field_tree = QTreeWidget() self.field_tree.setHeaderLabels(["名称", "类型", "描述"]) self.field_tree.setColumnWidth(0, 200) self.field_tree.itemClicked.connect(self.on_field_selected) left_layout.addWidget(self.field_tree) # 操作按钮 btn_layout = QHBoxLayout() self.add_btn = QPushButton("新建") self.add_btn.clicked.connect(self.on_add_field) self.delete_btn = QPushButton("删除") self.delete_btn.clicked.connect(self.on_delete_field) self.refresh_btn = QPushButton("刷新") self.refresh_btn.clicked.connect(self.load_fields) btn_layout.addWidget(self.add_btn) btn_layout.addWidget(self.delete_btn) btn_layout.addWidget(self.refresh_btn) btn_layout.addStretch() left_layout.addLayout(btn_layout) # 右侧:字段详情编辑 right_widget = QWidget() right_layout = QVBoxLayout(right_widget) detail_group = QGroupBox("字段详情") detail_layout = QFormLayout(detail_group) self.full_name_edit = QLineEdit() detail_layout.addRow("完整名称:", self.full_name_edit) self.domain_edit = QLineEdit() detail_layout.addRow("域:", self.domain_edit) self.name_edit = QLineEdit() detail_layout.addRow("字段名:", self.name_edit) self.type_edit = QComboBox() for field_type in FieldType: self.type_edit.addItem(field_type.value, field_type) detail_layout.addRow("类型:", self.type_edit) self.range_min_edit = QLineEdit() detail_layout.addRow("最小值:", self.range_min_edit) self.range_max_edit = QLineEdit() detail_layout.addRow("最大值:", self.range_max_edit) self.default_value_edit = QLineEdit() detail_layout.addRow("默认值:", self.default_value_edit) self.unit_edit = QLineEdit() detail_layout.addRow("单位:", self.unit_edit) self.description_edit = QTextEdit() self.description_edit.setMaximumHeight(100) detail_layout.addRow("描述:", self.description_edit) right_layout.addWidget(detail_group) # 保存按钮 save_btn_layout = QHBoxLayout() self.save_btn = QPushButton("保存") self.save_btn.clicked.connect(self.on_save_field) self.cancel_btn = QPushButton("取消") self.cancel_btn.clicked.connect(self.on_cancel_edit) save_btn_layout.addWidget(self.save_btn) save_btn_layout.addWidget(self.cancel_btn) save_btn_layout.addStretch() right_layout.addLayout(save_btn_layout) right_layout.addStretch() # 添加到分割器 splitter.addWidget(left_widget) splitter.addWidget(right_widget) splitter.setStretchFactor(0, 1) splitter.setStretchFactor(1, 1) def load_fields(self): """加载字段列表""" self.field_tree.clear() fields, total = self.controller.get_all_fields(page=1, page_size=1000) # 按域分组 domain_items = {} for field in fields: # 获取或创建域节点 if field.domain not in domain_items: domain_item = QTreeWidgetItem(self.field_tree) domain_item.setText(0, field.domain) domain_item.setData(0, Qt.UserRole, None) domain_items[field.domain] = domain_item # 创建字段节点 field_item = QTreeWidgetItem(domain_items[field.domain]) field_item.setText(0, field.full_name) field_item.setText(1, field.type.value) field_item.setText(2, field.description or "") field_item.setData(0, Qt.UserRole, field) self.field_tree.expandAll() logger.info(f"Loaded {total} fields") def on_field_selected(self, item: QTreeWidgetItem, column: int): """字段被选中""" field = item.data(0, Qt.UserRole) if field: self.current_field = field self.display_field_details(field) self.field_selected.emit(field) def display_field_details(self, field: Field): """显示字段详情""" self.full_name_edit.setText(field.full_name) self.domain_edit.setText(field.domain) self.name_edit.setText(field.name) # 设置类型 index = self.type_edit.findData(field.type) if index >= 0: self.type_edit.setCurrentIndex(index) self.range_min_edit.setText(field.range_min or "") self.range_max_edit.setText(field.range_max or "") self.default_value_edit.setText(field.default_value or "") self.unit_edit.setText(field.unit or "") self.description_edit.setText(field.description or "") def on_add_field(self): """新建字段""" self.current_field = None self.clear_field_details() def on_delete_field(self): """删除字段""" if not self.current_field: QMessageBox.warning(self, "警告", "请先选择要删除的字段") return reply = QMessageBox.question( self, "确认删除", f"确定要删除字段 '{self.current_field.full_name}' 吗?", QMessageBox.Yes | QMessageBox.No ) if reply == QMessageBox.Yes: success, msg = self.controller.delete_field(self.current_field.id) if success: QMessageBox.information(self, "成功", msg) self.load_fields() self.clear_field_details() else: QMessageBox.warning(self, "失败", msg) def on_save_field(self): """保存字段""" # 收集字段数据 field_data = { 'full_name': self.full_name_edit.text(), 'domain': self.domain_edit.text(), 'name': self.name_edit.text(), 'type': self.type_edit.currentData().value, 'range_min': self.range_min_edit.text() or None, 'range_max': self.range_max_edit.text() or None, 'default_value': self.default_value_edit.text() or None, 'unit': self.unit_edit.text() or None, 'description': self.description_edit.toPlainText() or None, } if self.current_field: # 更新 field_data['id'] = self.current_field.id success, msg = self.controller.update_field(field_data) else: # 创建 success, msg, field_id = self.controller.create_field(field_data) if success: QMessageBox.information(self, "成功", msg) self.load_fields() else: QMessageBox.warning(self, "失败", msg) def on_cancel_edit(self): """取消编辑""" if self.current_field: self.display_field_details(self.current_field) else: self.clear_field_details() def clear_field_details(self): """清空字段详情""" self.full_name_edit.clear() self.domain_edit.clear() self.name_edit.clear() self.type_edit.setCurrentIndex(0) self.range_min_edit.clear() self.range_max_edit.clear() self.default_value_edit.clear() self.unit_edit.clear() self.description_edit.clear() def on_search(self): """搜索字段""" search_params = { 'domain': self.domain_input.text() or None, 'name': self.name_input.text() or None, 'type': self.type_combo.currentData(), 'page': 1, 'page_size': 1000 } fields, total = self.controller.search_fields(search_params) # 更新树形显示 self.field_tree.clear() domain_items = {} for field in fields: if field.domain not in domain_items: domain_item = QTreeWidgetItem(self.field_tree) domain_item.setText(0, field.domain) domain_items[field.domain] = domain_item field_item = QTreeWidgetItem(domain_items[field.domain]) field_item.setText(0, field.full_name) field_item.setText(1, field.type.value) field_item.setText(2, field.description or "") field_item.setData(0, Qt.UserRole, field) self.field_tree.expandAll() def on_clear_search(self): """清除搜索""" self.domain_input.clear() self.name_input.clear() self.type_combo.setCurrentIndex(0) self.load_fields()