323 lines
11 KiB
Python
323 lines
11 KiB
Python
|
|
"""
|
||
|
|
字段树形控件
|
||
|
|
用于显示和管理字段的树形结构
|
||
|
|
"""
|
||
|
|
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()
|