|
|
#!/usr/bin/env python3
|
|
|
# -*- coding: utf-8 -*-
|
|
|
"""
|
|
|
规程编辑器
|
|
|
支持编辑规程内容并保存到数据库
|
|
|
"""
|
|
|
|
|
|
import json
|
|
|
from datetime import datetime
|
|
|
from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
|
|
QLineEdit, QTextEdit, QPushButton, QTableWidget,
|
|
|
QTableWidgetItem, QHeaderView, QMessageBox,
|
|
|
QDialog, QFormLayout, QSpinBox, QComboBox,
|
|
|
QDialogButtonBox, QAbstractItemView, QMenu)
|
|
|
from PyQt5.QtCore import Qt, pyqtSignal
|
|
|
from PyQt5.QtGui import QFont, QBrush, QColor
|
|
|
import qtawesome as qta
|
|
|
|
|
|
class NoClearDelegate(QAbstractItemView):
|
|
|
"""自定义委托,防止双击时清除内容"""
|
|
|
def createEditor(self, parent, option, index):
|
|
|
editor = super().createEditor(parent, option, index)
|
|
|
if hasattr(editor, 'setText'):
|
|
|
# 保持原有内容
|
|
|
current_text = index.data()
|
|
|
if current_text:
|
|
|
editor.setText(current_text)
|
|
|
return editor
|
|
|
|
|
|
class StepEditDialog(QDialog):
|
|
|
"""步骤编辑对话框"""
|
|
|
|
|
|
def __init__(self, step_data=None, parent=None):
|
|
|
super().__init__(parent)
|
|
|
self.setWindowTitle("编辑步骤")
|
|
|
self.setModal(True)
|
|
|
self.setMinimumWidth(500)
|
|
|
|
|
|
self.step_data = step_data or {}
|
|
|
self.initUI()
|
|
|
|
|
|
def initUI(self):
|
|
|
layout = QVBoxLayout()
|
|
|
self.setLayout(layout)
|
|
|
|
|
|
# 表单布局
|
|
|
form_layout = QFormLayout()
|
|
|
|
|
|
# 步骤ID
|
|
|
self.stepIdEdit = QLineEdit()
|
|
|
self.stepIdEdit.setText(self.step_data.get('stepId', ''))
|
|
|
form_layout.addRow("步骤ID:", self.stepIdEdit)
|
|
|
|
|
|
# 操作(合并关键词和步骤描述)
|
|
|
self.operationEdit = QTextEdit()
|
|
|
self.operationEdit.setMaximumHeight(80)
|
|
|
self.operationEdit.setPlainText(self.step_data.get('operation', ''))
|
|
|
form_layout.addRow("操作:", self.operationEdit)
|
|
|
|
|
|
# 操作类型(直接输入)
|
|
|
self.typeEdit = QLineEdit()
|
|
|
self.typeEdit.setText(self.step_data.get('type', 'SET'))
|
|
|
form_layout.addRow("操作类型:", self.typeEdit)
|
|
|
|
|
|
# 预期值
|
|
|
self.expectedValueEdit = QLineEdit()
|
|
|
self.expectedValueEdit.setText(str(self.step_data.get('expectedValue', '')))
|
|
|
form_layout.addRow("预期值:", self.expectedValueEdit)
|
|
|
|
|
|
# 备注
|
|
|
self.remarkEdit = QTextEdit()
|
|
|
self.remarkEdit.setMaximumHeight(60)
|
|
|
self.remarkEdit.setPlainText(self.step_data.get('remark', ''))
|
|
|
form_layout.addRow("备注:", self.remarkEdit)
|
|
|
|
|
|
# 序号
|
|
|
self.orderSpin = QSpinBox()
|
|
|
self.orderSpin.setRange(1, 999)
|
|
|
self.orderSpin.setValue(self.step_data.get('order', 1))
|
|
|
form_layout.addRow("序号:", self.orderSpin)
|
|
|
|
|
|
layout.addLayout(form_layout)
|
|
|
|
|
|
# 按钮
|
|
|
button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
|
|
button_box.accepted.connect(self.accept)
|
|
|
button_box.rejected.connect(self.reject)
|
|
|
layout.addWidget(button_box)
|
|
|
|
|
|
def getStepData(self):
|
|
|
"""获取步骤数据"""
|
|
|
return {
|
|
|
'stepId': self.stepIdEdit.text(),
|
|
|
'operation': self.operationEdit.toPlainText(),
|
|
|
'type': self.typeEdit.text(),
|
|
|
'expectedValue': self.expectedValueEdit.text(),
|
|
|
'remark': self.remarkEdit.toPlainText(),
|
|
|
'order': self.orderSpin.value()
|
|
|
}
|
|
|
|
|
|
class ProcedureEditor(QWidget):
|
|
|
"""规程编辑器"""
|
|
|
|
|
|
procedureSaved = pyqtSignal(int) # 规程保存信号
|
|
|
|
|
|
def __init__(self, procedureData, procedureId, dbManager, parent=None):
|
|
|
super().__init__(parent)
|
|
|
self.procedureData = procedureData
|
|
|
self.procedureId = procedureId
|
|
|
self.dbManager = dbManager
|
|
|
self.parent = parent
|
|
|
|
|
|
self.initUI()
|
|
|
self.loadProcedureData()
|
|
|
|
|
|
def initUI(self):
|
|
|
"""初始化界面"""
|
|
|
layout = QVBoxLayout()
|
|
|
self.setLayout(layout)
|
|
|
|
|
|
# 标题
|
|
|
title_label = QLabel("规程编辑器")
|
|
|
title_label.setObjectName("procedureEditorTitle") # 设置对象名称以便QSS选择器使用
|
|
|
layout.addWidget(title_label)
|
|
|
|
|
|
# 基本信息区域
|
|
|
self.createBasicInfoSection(layout)
|
|
|
|
|
|
# 步骤表格区域
|
|
|
self.createStepsSection(layout)
|
|
|
|
|
|
# 按钮区域
|
|
|
self.createButtonSection(layout)
|
|
|
|
|
|
def createBasicInfoSection(self, layout):
|
|
|
"""创建基本信息区域 - 优化为紧凑布局"""
|
|
|
info_group = QWidget()
|
|
|
info_group.setObjectName("basicInfoGroup")
|
|
|
info_group.setMaximumHeight(80) # 限制基本信息区域的最大高度
|
|
|
info_group.setMinimumHeight(80)
|
|
|
layout.addWidget(info_group)
|
|
|
|
|
|
info_layout = QVBoxLayout()
|
|
|
info_layout.setContentsMargins(8, 4, 8, 4) # 减小边距
|
|
|
info_layout.setSpacing(4) # 减小间距
|
|
|
info_group.setLayout(info_layout)
|
|
|
|
|
|
# 标题
|
|
|
info_title = QLabel("基本信息")
|
|
|
info_title.setObjectName("basicInfoTitle")
|
|
|
info_layout.addWidget(info_title)
|
|
|
|
|
|
# 使用网格布局实现紧凑的两行布局
|
|
|
grid_layout = QHBoxLayout()
|
|
|
grid_layout.setSpacing(16)
|
|
|
|
|
|
# 第一列
|
|
|
col1_layout = QHBoxLayout()
|
|
|
col1_layout.setSpacing(8)
|
|
|
|
|
|
# 规程名称
|
|
|
name_label = QLabel("规程名称:")
|
|
|
name_label.setFixedWidth(60)
|
|
|
name_label.setProperty("fieldLabel", True)
|
|
|
self.nameEdit = QLineEdit()
|
|
|
self.nameEdit.setPlaceholderText("输入规程名称")
|
|
|
self.nameEdit.setObjectName("procedureNameEdit")
|
|
|
self.nameEdit.setMaximumHeight(32)
|
|
|
col1_layout.addWidget(name_label)
|
|
|
col1_layout.addWidget(self.nameEdit)
|
|
|
|
|
|
# 规程编号
|
|
|
number_label = QLabel("规程编号:")
|
|
|
number_label.setFixedWidth(60)
|
|
|
number_label.setProperty("fieldLabel", True)
|
|
|
self.numberEdit = QLineEdit()
|
|
|
self.numberEdit.setPlaceholderText("输入规程编号")
|
|
|
self.numberEdit.setObjectName("procedureNumberEdit")
|
|
|
self.numberEdit.setMaximumHeight(32)
|
|
|
col1_layout.addWidget(number_label)
|
|
|
col1_layout.addWidget(self.numberEdit)
|
|
|
|
|
|
grid_layout.addLayout(col1_layout)
|
|
|
|
|
|
# 第二列
|
|
|
col2_layout = QHBoxLayout()
|
|
|
col2_layout.setSpacing(8)
|
|
|
|
|
|
# 规程类型
|
|
|
type_label = QLabel("规程类型:")
|
|
|
type_label.setFixedWidth(60)
|
|
|
type_label.setProperty("fieldLabel", True)
|
|
|
self.typeEdit = QLineEdit()
|
|
|
self.typeEdit.setPlaceholderText("输入规程类型")
|
|
|
self.typeEdit.setObjectName("procedureTypeEdit")
|
|
|
self.typeEdit.setMaximumHeight(32)
|
|
|
col2_layout.addWidget(type_label)
|
|
|
col2_layout.addWidget(self.typeEdit)
|
|
|
|
|
|
# 规程描述(简化为单行输入)
|
|
|
desc_label = QLabel("规程描述:")
|
|
|
desc_label.setFixedWidth(60)
|
|
|
desc_label.setProperty("fieldLabel", True)
|
|
|
self.descriptionEdit = QLineEdit() # 改为单行输入
|
|
|
self.descriptionEdit.setPlaceholderText("输入规程描述")
|
|
|
self.descriptionEdit.setObjectName("procedureDescriptionEdit")
|
|
|
self.descriptionEdit.setMaximumHeight(32)
|
|
|
col2_layout.addWidget(desc_label)
|
|
|
col2_layout.addWidget(self.descriptionEdit)
|
|
|
|
|
|
grid_layout.addLayout(col2_layout)
|
|
|
grid_layout.addStretch() # 添加弹性空间
|
|
|
|
|
|
info_layout.addLayout(grid_layout)
|
|
|
|
|
|
def createStepsSection(self, layout):
|
|
|
"""创建步骤表格区域 - 优化为最大化表格显示"""
|
|
|
steps_group = QWidget()
|
|
|
steps_group.setObjectName("stepsGroup")
|
|
|
layout.addWidget(steps_group)
|
|
|
|
|
|
steps_layout = QVBoxLayout()
|
|
|
steps_layout.setContentsMargins(8, 4, 8, 4) # 减小边距
|
|
|
steps_layout.setSpacing(4) # 减小间距
|
|
|
steps_group.setLayout(steps_layout)
|
|
|
|
|
|
# 紧凑的标题和按钮行
|
|
|
header_layout = QHBoxLayout()
|
|
|
header_layout.setSpacing(8)
|
|
|
|
|
|
# 标题
|
|
|
steps_title = QLabel("步骤列表")
|
|
|
steps_title.setObjectName("stepsTitle")
|
|
|
header_layout.addWidget(steps_title)
|
|
|
|
|
|
# 添加步骤按钮(紧凑样式)
|
|
|
add_step_btn = QPushButton("添加步骤")
|
|
|
add_step_btn.setIcon(qta.icon('fa5s.plus', color='white'))
|
|
|
add_step_btn.setObjectName("addStepButton")
|
|
|
add_step_btn.setMaximumHeight(28) # 减小按钮高度
|
|
|
add_step_btn.clicked.connect(self.addStep)
|
|
|
header_layout.addWidget(add_step_btn)
|
|
|
|
|
|
# 删除步骤按钮(紧凑样式)
|
|
|
delete_step_btn = QPushButton("删除步骤")
|
|
|
delete_step_btn.setIcon(qta.icon('fa5s.trash', color='white'))
|
|
|
delete_step_btn.setObjectName("deleteStepButton")
|
|
|
delete_step_btn.setMaximumHeight(28) # 减小按钮高度
|
|
|
delete_step_btn.clicked.connect(self.deleteStep)
|
|
|
header_layout.addWidget(delete_step_btn)
|
|
|
|
|
|
header_layout.addStretch()
|
|
|
steps_layout.addLayout(header_layout)
|
|
|
|
|
|
# 步骤表格(最大化显示)
|
|
|
self.stepsTable = QTableWidget()
|
|
|
self.stepsTable.setObjectName("stepsTable")
|
|
|
self.stepsTable.setColumnCount(6)
|
|
|
self.stepsTable.setHorizontalHeaderLabels(['序号', '步骤ID', '操作', '类型', '预期值', '备注'])
|
|
|
|
|
|
# 设置表格属性
|
|
|
self.stepsTable.setSelectionBehavior(QAbstractItemView.SelectRows)
|
|
|
self.stepsTable.setEditTriggers(QAbstractItemView.AllEditTriggers)
|
|
|
self.stepsTable.setAlternatingRowColors(True) # 启用交替行颜色
|
|
|
self.stepsTable.setShowGrid(True) # 显示网格线
|
|
|
|
|
|
# 优化表格样式
|
|
|
|
|
|
# 设置列宽
|
|
|
header = self.stepsTable.horizontalHeader()
|
|
|
if header:
|
|
|
header.setSectionResizeMode(0, QHeaderView.ResizeToContents) # 序号
|
|
|
header.setSectionResizeMode(1, QHeaderView.ResizeToContents) # 步骤ID
|
|
|
header.setSectionResizeMode(2, QHeaderView.Stretch) # 操作
|
|
|
header.setSectionResizeMode(3, QHeaderView.ResizeToContents) # 类型
|
|
|
header.setSectionResizeMode(4, QHeaderView.ResizeToContents) # 预期值
|
|
|
header.setSectionResizeMode(5, QHeaderView.ResizeToContents) # 备注
|
|
|
|
|
|
# 设置最小列宽
|
|
|
self.stepsTable.setColumnWidth(0, 50) # 序号
|
|
|
self.stepsTable.setColumnWidth(1, 100) # 步骤ID
|
|
|
self.stepsTable.setColumnWidth(3, 70) # 类型
|
|
|
self.stepsTable.setColumnWidth(4, 80) # 预期值
|
|
|
self.stepsTable.setColumnWidth(5, 100) # 备注
|
|
|
|
|
|
# 添加右键菜单
|
|
|
self.stepsTable.setContextMenuPolicy(Qt.CustomContextMenu)
|
|
|
self.stepsTable.customContextMenuRequested.connect(self.showContextMenu)
|
|
|
|
|
|
steps_layout.addWidget(self.stepsTable)
|
|
|
|
|
|
def createButtonSection(self, layout):
|
|
|
"""创建紧凑的按钮区域"""
|
|
|
button_widget = QWidget()
|
|
|
button_widget.setMaximumHeight(40) # 限制按钮区域高度
|
|
|
button_widget.setMinimumHeight(40)
|
|
|
|
|
|
button_layout = QHBoxLayout()
|
|
|
button_layout.setContentsMargins(8, 4, 8, 4) # 减小边距
|
|
|
button_layout.setSpacing(8) # 减小按钮间距
|
|
|
button_widget.setLayout(button_layout)
|
|
|
|
|
|
# 保存按钮(紧凑样式)
|
|
|
save_btn = QPushButton("保存规程")
|
|
|
save_btn.setIcon(qta.icon('fa5s.save', color='white'))
|
|
|
save_btn.setObjectName("saveProcedureButton")
|
|
|
save_btn.setMaximumHeight(32) # 减小按钮高度
|
|
|
save_btn.clicked.connect(self.saveProcedure)
|
|
|
button_layout.addWidget(save_btn)
|
|
|
|
|
|
# 取消按钮(紧凑样式)
|
|
|
cancel_btn = QPushButton("取消")
|
|
|
cancel_btn.setIcon(qta.icon('fa5s.times', color='red'))
|
|
|
cancel_btn.setObjectName("cancelEditButton")
|
|
|
cancel_btn.setMaximumHeight(32) # 减小按钮高度
|
|
|
cancel_btn.clicked.connect(self.cancelEdit)
|
|
|
button_layout.addWidget(cancel_btn)
|
|
|
|
|
|
button_layout.addStretch() # 将按钮推到左侧
|
|
|
layout.addWidget(button_widget)
|
|
|
|
|
|
def loadProcedureData(self):
|
|
|
"""加载规程数据"""
|
|
|
if not self.procedureData:
|
|
|
print("警告:规程数据为空")
|
|
|
return
|
|
|
|
|
|
print(f"加载规程数据: {self.procedureData.keys()}")
|
|
|
|
|
|
# 加载基本信息
|
|
|
procedure_info = self.procedureData.get('规程信息', {})
|
|
|
self.nameEdit.setText(procedure_info.get('规程名称', ''))
|
|
|
self.numberEdit.setText(procedure_info.get('规程编号', ''))
|
|
|
self.typeEdit.setText(procedure_info.get('规程类型', ''))
|
|
|
|
|
|
# 加载描述(可能在不同位置)
|
|
|
description = self.procedureData.get('description', '')
|
|
|
if not description:
|
|
|
# 尝试从测试用例信息中获取描述
|
|
|
test_case_info = self.procedureData.get('测试用例信息', {})
|
|
|
description = test_case_info.get('工况描述', '')
|
|
|
self.descriptionEdit.setText(description) # 修改为setText,因为现在是QLineEdit
|
|
|
#!/usr/bin/env python3
|
|
|
# -*- coding: utf-8 -*-
|
|
|
"""
|
|
|
规程编辑器
|
|
|
支持编辑规程内容并保存到数据库
|
|
|
"""
|
|
|
|
|
|
import json
|
|
|
from datetime import datetime
|
|
|
from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
|
|
QLineEdit, QTextEdit, QPushButton, QTableWidget,
|
|
|
QTableWidgetItem, QHeaderView, QMessageBox,
|
|
|
QDialog, QFormLayout, QSpinBox, QComboBox,
|
|
|
QDialogButtonBox, QAbstractItemView, QMenu)
|
|
|
from PyQt5.QtCore import Qt, pyqtSignal
|
|
|
from PyQt5.QtGui import QFont, QBrush, QColor
|
|
|
import qtawesome as qta
|
|
|
|
|
|
class NoClearDelegate(QAbstractItemView):
|
|
|
"""自定义委托,防止双击时清除内容"""
|
|
|
def createEditor(self, parent, option, index):
|
|
|
editor = super().createEditor(parent, option, index)
|
|
|
if hasattr(editor, 'setText'):
|
|
|
# 保持原有内容
|
|
|
current_text = index.data()
|
|
|
if current_text:
|
|
|
editor.setText(current_text)
|
|
|
return editor
|
|
|
|
|
|
class StepEditDialog(QDialog):
|
|
|
"""步骤编辑对话框"""
|
|
|
|
|
|
def __init__(self, step_data=None, parent=None):
|
|
|
super().__init__(parent)
|
|
|
self.setWindowTitle("编辑步骤")
|
|
|
self.setModal(True)
|
|
|
self.setMinimumWidth(500)
|
|
|
|
|
|
self.step_data = step_data or {}
|
|
|
self.initUI()
|
|
|
|
|
|
def initUI(self):
|
|
|
layout = QVBoxLayout()
|
|
|
self.setLayout(layout)
|
|
|
|
|
|
# 表单布局
|
|
|
form_layout = QFormLayout()
|
|
|
|
|
|
# 步骤ID
|
|
|
self.stepIdEdit = QLineEdit()
|
|
|
self.stepIdEdit.setText(self.step_data.get('stepId', ''))
|
|
|
form_layout.addRow("步骤ID:", self.stepIdEdit)
|
|
|
|
|
|
# 操作(合并关键词和步骤描述)
|
|
|
self.operationEdit = QTextEdit()
|
|
|
self.operationEdit.setMaximumHeight(80)
|
|
|
self.operationEdit.setPlainText(self.step_data.get('operation', ''))
|
|
|
form_layout.addRow("操作:", self.operationEdit)
|
|
|
|
|
|
# 操作类型(直接输入)
|
|
|
self.typeEdit = QLineEdit()
|
|
|
self.typeEdit.setText(self.step_data.get('type', 'SET'))
|
|
|
form_layout.addRow("操作类型:", self.typeEdit)
|
|
|
|
|
|
# 预期值
|
|
|
self.expectedValueEdit = QLineEdit()
|
|
|
self.expectedValueEdit.setText(str(self.step_data.get('expectedValue', '')))
|
|
|
form_layout.addRow("预期值:", self.expectedValueEdit)
|
|
|
|
|
|
# 备注
|
|
|
self.remarkEdit = QTextEdit()
|
|
|
self.remarkEdit.setMaximumHeight(60)
|
|
|
self.remarkEdit.setPlainText(self.step_data.get('remark', ''))
|
|
|
form_layout.addRow("备注:", self.remarkEdit)
|
|
|
|
|
|
# 序号
|
|
|
self.orderSpin = QSpinBox()
|
|
|
self.orderSpin.setRange(1, 999)
|
|
|
self.orderSpin.setValue(self.step_data.get('order', 1))
|
|
|
form_layout.addRow("序号:", self.orderSpin)
|
|
|
|
|
|
layout.addLayout(form_layout)
|
|
|
|
|
|
# 按钮
|
|
|
button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
|
|
button_box.accepted.connect(self.accept)
|
|
|
button_box.rejected.connect(self.reject)
|
|
|
layout.addWidget(button_box)
|
|
|
|
|
|
def getStepData(self):
|
|
|
"""获取步骤数据"""
|
|
|
return {
|
|
|
'stepId': self.stepIdEdit.text(),
|
|
|
'operation': self.operationEdit.toPlainText(),
|
|
|
'type': self.typeEdit.text(),
|
|
|
'expectedValue': self.expectedValueEdit.text(),
|
|
|
'remark': self.remarkEdit.toPlainText(),
|
|
|
'order': self.orderSpin.value()
|
|
|
}
|
|
|
|
|
|
class ProcedureEditor(QWidget):
|
|
|
"""规程编辑器"""
|
|
|
|
|
|
procedureSaved = pyqtSignal(int) # 规程保存信号
|
|
|
|
|
|
def __init__(self, procedureData, procedureId, dbManager, parent=None):
|
|
|
super().__init__(parent)
|
|
|
self.procedureData = procedureData
|
|
|
self.procedureId = procedureId
|
|
|
self.dbManager = dbManager
|
|
|
self.parent = parent
|
|
|
|
|
|
self.initUI()
|
|
|
self.loadProcedureData()
|
|
|
|
|
|
def initUI(self):
|
|
|
"""初始化界面"""
|
|
|
layout = QVBoxLayout()
|
|
|
self.setLayout(layout)
|
|
|
|
|
|
# 标题
|
|
|
title_label = QLabel("规程编辑器")
|
|
|
title_label.setObjectName("procedureEditorTitle") # 设置对象名称以便QSS选择器使用
|
|
|
layout.addWidget(title_label)
|
|
|
|
|
|
# 基本信息区域
|
|
|
self.createBasicInfoSection(layout)
|
|
|
|
|
|
# 步骤表格区域
|
|
|
self.createStepsSection(layout)
|
|
|
|
|
|
# 按钮区域
|
|
|
self.createButtonSection(layout)
|
|
|
|
|
|
def createBasicInfoSection(self, layout):
|
|
|
"""创建基本信息区域 - 优化为紧凑布局"""
|
|
|
info_group = QWidget()
|
|
|
info_group.setObjectName("basicInfoGroup")
|
|
|
info_group.setMaximumHeight(80) # 限制基本信息区域的最大高度
|
|
|
info_group.setMinimumHeight(80)
|
|
|
layout.addWidget(info_group)
|
|
|
|
|
|
info_layout = QVBoxLayout()
|
|
|
info_layout.setContentsMargins(8, 4, 8, 4) # 减小边距
|
|
|
info_layout.setSpacing(4) # 减小间距
|
|
|
info_group.setLayout(info_layout)
|
|
|
|
|
|
# 标题
|
|
|
info_title = QLabel("基本信息")
|
|
|
info_title.setObjectName("basicInfoTitle")
|
|
|
info_layout.addWidget(info_title)
|
|
|
|
|
|
# 使用网格布局实现紧凑的两行布局
|
|
|
grid_layout = QHBoxLayout()
|
|
|
grid_layout.setSpacing(16)
|
|
|
|
|
|
# 第一列
|
|
|
col1_layout = QHBoxLayout()
|
|
|
col1_layout.setSpacing(8)
|
|
|
|
|
|
# 规程名称
|
|
|
name_label = QLabel("规程名称:")
|
|
|
name_label.setFixedWidth(60)
|
|
|
name_label.setProperty("fieldLabel", True)
|
|
|
self.nameEdit = QLineEdit()
|
|
|
self.nameEdit.setPlaceholderText("输入规程名称")
|
|
|
self.nameEdit.setObjectName("procedureNameEdit")
|
|
|
self.nameEdit.setMaximumHeight(32)
|
|
|
col1_layout.addWidget(name_label)
|
|
|
col1_layout.addWidget(self.nameEdit)
|
|
|
|
|
|
# 规程编号
|
|
|
number_label = QLabel("规程编号:")
|
|
|
number_label.setFixedWidth(60)
|
|
|
number_label.setProperty("fieldLabel", True)
|
|
|
self.numberEdit = QLineEdit()
|
|
|
self.numberEdit.setPlaceholderText("输入规程编号")
|
|
|
self.numberEdit.setObjectName("procedureNumberEdit")
|
|
|
self.numberEdit.setMaximumHeight(32)
|
|
|
col1_layout.addWidget(number_label)
|
|
|
col1_layout.addWidget(self.numberEdit)
|
|
|
|
|
|
grid_layout.addLayout(col1_layout)
|
|
|
|
|
|
# 第二列
|
|
|
col2_layout = QHBoxLayout()
|
|
|
col2_layout.setSpacing(8)
|
|
|
|
|
|
# 规程类型
|
|
|
type_label = QLabel("规程类型:")
|
|
|
type_label.setFixedWidth(60)
|
|
|
type_label.setProperty("fieldLabel", True)
|
|
|
self.typeEdit = QLineEdit()
|
|
|
self.typeEdit.setPlaceholderText("输入规程类型")
|
|
|
self.typeEdit.setObjectName("procedureTypeEdit")
|
|
|
self.typeEdit.setMaximumHeight(32)
|
|
|
col2_layout.addWidget(type_label)
|
|
|
col2_layout.addWidget(self.typeEdit)
|
|
|
|
|
|
# 规程描述(简化为单行输入)
|
|
|
desc_label = QLabel("规程描述:")
|
|
|
desc_label.setFixedWidth(60)
|
|
|
desc_label.setProperty("fieldLabel", True)
|
|
|
self.descriptionEdit = QLineEdit() # 改为单行输入
|
|
|
self.descriptionEdit.setPlaceholderText("输入规程描述")
|
|
|
self.descriptionEdit.setObjectName("procedureDescriptionEdit")
|
|
|
self.descriptionEdit.setMaximumHeight(32)
|
|
|
col2_layout.addWidget(desc_label)
|
|
|
col2_layout.addWidget(self.descriptionEdit)
|
|
|
|
|
|
grid_layout.addLayout(col2_layout)
|
|
|
grid_layout.addStretch() # 添加弹性空间
|
|
|
|
|
|
info_layout.addLayout(grid_layout)
|
|
|
|
|
|
def createStepsSection(self, layout):
|
|
|
"""创建步骤表格区域 - 优化为最大化表格显示"""
|
|
|
steps_group = QWidget()
|
|
|
steps_group.setObjectName("stepsGroup")
|
|
|
layout.addWidget(steps_group)
|
|
|
|
|
|
steps_layout = QVBoxLayout()
|
|
|
steps_layout.setContentsMargins(8, 4, 8, 4) # 减小边距
|
|
|
steps_layout.setSpacing(4) # 减小间距
|
|
|
steps_group.setLayout(steps_layout)
|
|
|
|
|
|
# 紧凑的标题和按钮行
|
|
|
header_layout = QHBoxLayout()
|
|
|
header_layout.setSpacing(8)
|
|
|
|
|
|
# 标题
|
|
|
steps_title = QLabel("步骤列表")
|
|
|
steps_title.setObjectName("stepsTitle")
|
|
|
header_layout.addWidget(steps_title)
|
|
|
|
|
|
# 添加步骤按钮(紧凑样式)
|
|
|
add_step_btn = QPushButton("添加步骤")
|
|
|
add_step_btn.setIcon(qta.icon('fa5s.plus', color='white'))
|
|
|
add_step_btn.setObjectName("addStepButton")
|
|
|
add_step_btn.setMaximumHeight(28) # 减小按钮高度
|
|
|
add_step_btn.clicked.connect(self.addStep)
|
|
|
header_layout.addWidget(add_step_btn)
|
|
|
|
|
|
# 删除步骤按钮(紧凑样式)
|
|
|
delete_step_btn = QPushButton("删除步骤")
|
|
|
delete_step_btn.setIcon(qta.icon('fa5s.trash', color='white'))
|
|
|
delete_step_btn.setObjectName("deleteStepButton")
|
|
|
delete_step_btn.setMaximumHeight(28) # 减小按钮高度
|
|
|
delete_step_btn.clicked.connect(self.deleteStep)
|
|
|
header_layout.addWidget(delete_step_btn)
|
|
|
|
|
|
header_layout.addStretch()
|
|
|
steps_layout.addLayout(header_layout)
|
|
|
|
|
|
# 步骤表格(最大化显示)
|
|
|
self.stepsTable = QTableWidget()
|
|
|
self.stepsTable.setObjectName("stepsTable")
|
|
|
self.stepsTable.setColumnCount(6)
|
|
|
self.stepsTable.setHorizontalHeaderLabels(['序号', '步骤ID', '操作', '类型', '预期值', '备注'])
|
|
|
|
|
|
# 设置表格属性
|
|
|
self.stepsTable.setSelectionBehavior(QAbstractItemView.SelectRows)
|
|
|
self.stepsTable.setEditTriggers(QAbstractItemView.AllEditTriggers)
|
|
|
self.stepsTable.setAlternatingRowColors(True) # 启用交替行颜色
|
|
|
self.stepsTable.setShowGrid(True) # 显示网格线
|
|
|
|
|
|
# 优化表格样式
|
|
|
|
|
|
# 设置列宽
|
|
|
header = self.stepsTable.horizontalHeader()
|
|
|
if header:
|
|
|
header.setSectionResizeMode(0, QHeaderView.ResizeToContents) # 序号
|
|
|
header.setSectionResizeMode(1, QHeaderView.ResizeToContents) # 步骤ID
|
|
|
header.setSectionResizeMode(2, QHeaderView.Stretch) # 操作
|
|
|
header.setSectionResizeMode(3, QHeaderView.ResizeToContents) # 类型
|
|
|
header.setSectionResizeMode(4, QHeaderView.ResizeToContents) # 预期值
|
|
|
header.setSectionResizeMode(5, QHeaderView.ResizeToContents) # 备注
|
|
|
|
|
|
# 设置最小列宽
|
|
|
self.stepsTable.setColumnWidth(0, 50) # 序号
|
|
|
self.stepsTable.setColumnWidth(1, 100) # 步骤ID
|
|
|
self.stepsTable.setColumnWidth(3, 70) # 类型
|
|
|
self.stepsTable.setColumnWidth(4, 80) # 预期值
|
|
|
self.stepsTable.setColumnWidth(5, 100) # 备注
|
|
|
|
|
|
# 添加右键菜单
|
|
|
self.stepsTable.setContextMenuPolicy(Qt.CustomContextMenu)
|
|
|
self.stepsTable.customContextMenuRequested.connect(self.showContextMenu)
|
|
|
|
|
|
steps_layout.addWidget(self.stepsTable)
|
|
|
|
|
|
def createButtonSection(self, layout):
|
|
|
"""创建紧凑的按钮区域"""
|
|
|
button_widget = QWidget()
|
|
|
button_widget.setMaximumHeight(40) # 限制按钮区域高度
|
|
|
button_widget.setMinimumHeight(40)
|
|
|
|
|
|
button_layout = QHBoxLayout()
|
|
|
button_layout.setContentsMargins(8, 4, 8, 4) # 减小边距
|
|
|
button_layout.setSpacing(8) # 减小按钮间距
|
|
|
button_widget.setLayout(button_layout)
|
|
|
|
|
|
# 保存按钮(紧凑样式)
|
|
|
save_btn = QPushButton("保存规程")
|
|
|
save_btn.setIcon(qta.icon('fa5s.save', color='white'))
|
|
|
save_btn.setObjectName("saveProcedureButton")
|
|
|
save_btn.setMaximumHeight(32) # 减小按钮高度
|
|
|
save_btn.clicked.connect(self.saveProcedure)
|
|
|
button_layout.addWidget(save_btn)
|
|
|
|
|
|
# 取消按钮(紧凑样式)
|
|
|
cancel_btn = QPushButton("取消")
|
|
|
cancel_btn.setIcon(qta.icon('fa5s.times', color='red'))
|
|
|
cancel_btn.setObjectName("cancelEditButton")
|
|
|
cancel_btn.setMaximumHeight(32) # 减小按钮高度
|
|
|
cancel_btn.clicked.connect(self.cancelEdit)
|
|
|
button_layout.addWidget(cancel_btn)
|
|
|
|
|
|
button_layout.addStretch() # 将按钮推到左侧
|
|
|
layout.addWidget(button_widget)
|
|
|
|
|
|
def loadProcedureData(self):
|
|
|
"""加载规程数据"""
|
|
|
if not self.procedureData:
|
|
|
print("警告:规程数据为空")
|
|
|
return
|
|
|
|
|
|
print(f"加载规程数据: {self.procedureData.keys()}")
|
|
|
|
|
|
# 加载基本信息
|
|
|
procedure_info = self.procedureData.get('规程信息', {})
|
|
|
self.nameEdit.setText(procedure_info.get('规程名称', ''))
|
|
|
self.numberEdit.setText(procedure_info.get('规程编号', ''))
|
|
|
self.typeEdit.setText(procedure_info.get('规程类型', ''))
|
|
|
|
|
|
#!/usr/bin/env python3
|
|
|
# -*- coding: utf-8 -*-
|
|
|
"""
|
|
|
规程编辑器
|
|
|
支持编辑规程内容并保存到数据库
|
|
|
"""
|
|
|
|
|
|
import json
|
|
|
from datetime import datetime
|
|
|
from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel,
|
|
|
QLineEdit, QTextEdit, QPushButton, QTableWidget,
|
|
|
QTableWidgetItem, QHeaderView, QMessageBox,
|
|
|
QDialog, QFormLayout, QSpinBox, QComboBox,
|
|
|
QDialogButtonBox, QAbstractItemView, QMenu)
|
|
|
from PyQt5.QtCore import Qt, pyqtSignal
|
|
|
from PyQt5.QtGui import QFont, QBrush, QColor
|
|
|
import qtawesome as qta
|
|
|
|
|
|
class NoClearDelegate(QAbstractItemView):
|
|
|
"""自定义委托,防止双击时清除内容"""
|
|
|
def createEditor(self, parent, option, index):
|
|
|
editor = super().createEditor(parent, option, index)
|
|
|
if hasattr(editor, 'setText'):
|
|
|
# 保持原有内容
|
|
|
current_text = index.data()
|
|
|
if current_text:
|
|
|
editor.setText(current_text)
|
|
|
return editor
|
|
|
|
|
|
class StepEditDialog(QDialog):
|
|
|
"""步骤编辑对话框"""
|
|
|
|
|
|
def __init__(self, step_data=None, parent=None):
|
|
|
super().__init__(parent)
|
|
|
self.setWindowTitle("编辑步骤")
|
|
|
self.setModal(True)
|
|
|
self.setMinimumWidth(500)
|
|
|
|
|
|
self.step_data = step_data or {}
|
|
|
self.initUI()
|
|
|
|
|
|
def initUI(self):
|
|
|
layout = QVBoxLayout()
|
|
|
self.setLayout(layout)
|
|
|
|
|
|
# 表单布局
|
|
|
form_layout = QFormLayout()
|
|
|
|
|
|
# 步骤ID
|
|
|
self.stepIdEdit = QLineEdit()
|
|
|
self.stepIdEdit.setText(self.step_data.get('stepId', ''))
|
|
|
form_layout.addRow("步骤ID:", self.stepIdEdit)
|
|
|
|
|
|
# 操作(合并关键词和步骤描述)
|
|
|
self.operationEdit = QTextEdit()
|
|
|
self.operationEdit.setMaximumHeight(80)
|
|
|
self.operationEdit.setPlainText(self.step_data.get('operation', ''))
|
|
|
form_layout.addRow("操作:", self.operationEdit)
|
|
|
|
|
|
# 操作类型(直接输入)
|
|
|
self.typeEdit = QLineEdit()
|
|
|
self.typeEdit.setText(self.step_data.get('type', 'SET'))
|
|
|
form_layout.addRow("操作类型:", self.typeEdit)
|
|
|
|
|
|
# 预期值
|
|
|
self.expectedValueEdit = QLineEdit()
|
|
|
self.expectedValueEdit.setText(str(self.step_data.get('expectedValue', '')))
|
|
|
form_layout.addRow("预期值:", self.expectedValueEdit)
|
|
|
|
|
|
# 备注
|
|
|
self.remarkEdit = QTextEdit()
|
|
|
self.remarkEdit.setMaximumHeight(60)
|
|
|
self.remarkEdit.setPlainText(self.step_data.get('remark', ''))
|
|
|
form_layout.addRow("备注:", self.remarkEdit)
|
|
|
|
|
|
# 序号
|
|
|
self.orderSpin = QSpinBox()
|
|
|
self.orderSpin.setRange(1, 999)
|
|
|
self.orderSpin.setValue(self.step_data.get('order', 1))
|
|
|
form_layout.addRow("序号:", self.orderSpin)
|
|
|
|
|
|
layout.addLayout(form_layout)
|
|
|
|
|
|
# 按钮
|
|
|
button_box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
|
|
|
button_box.accepted.connect(self.accept)
|
|
|
button_box.rejected.connect(self.reject)
|
|
|
layout.addWidget(button_box)
|
|
|
|
|
|
def getStepData(self):
|
|
|
"""获取步骤数据"""
|
|
|
return {
|
|
|
'stepId': self.stepIdEdit.text(),
|
|
|
'operation': self.operationEdit.toPlainText(),
|
|
|
'type': self.typeEdit.text(),
|
|
|
'expectedValue': self.expectedValueEdit.text(),
|
|
|
'remark': self.remarkEdit.toPlainText(),
|
|
|
'order': self.orderSpin.value()
|
|
|
}
|
|
|
|
|
|
class ProcedureEditor(QWidget):
|
|
|
"""规程编辑器"""
|
|
|
|
|
|
procedureSaved = pyqtSignal(int) # 规程保存信号
|
|
|
|
|
|
def __init__(self, procedureData, procedureId, dbManager, parent=None):
|
|
|
super().__init__(parent)
|
|
|
self.procedureData = procedureData
|
|
|
self.procedureId = procedureId
|
|
|
self.dbManager = dbManager
|
|
|
self.parent = parent
|
|
|
|
|
|
self.initUI()
|
|
|
self.loadProcedureData()
|
|
|
|
|
|
def initUI(self):
|
|
|
"""初始化界面"""
|
|
|
layout = QVBoxLayout()
|
|
|
self.setLayout(layout)
|
|
|
|
|
|
# 标题
|
|
|
title_label = QLabel("规程编辑器")
|
|
|
title_label.setObjectName("procedureEditorTitle") # 设置对象名称以便QSS选择器使用
|
|
|
layout.addWidget(title_label)
|
|
|
|
|
|
# 基本信息区域
|
|
|
self.createBasicInfoSection(layout)
|
|
|
|
|
|
# 步骤表格区域
|
|
|
self.createStepsSection(layout)
|
|
|
|
|
|
# 按钮区域
|
|
|
self.createButtonSection(layout)
|
|
|
|
|
|
def createBasicInfoSection(self, layout):
|
|
|
"""创建基本信息区域 - 优化为紧凑布局"""
|
|
|
info_group = QWidget()
|
|
|
info_group.setObjectName("basicInfoGroup")
|
|
|
info_group.setMaximumHeight(80) # 限制基本信息区域的最大高度
|
|
|
info_group.setMinimumHeight(80)
|
|
|
layout.addWidget(info_group)
|
|
|
|
|
|
info_layout = QVBoxLayout()
|
|
|
info_layout.setContentsMargins(8, 4, 8, 4) # 减小边距
|
|
|
info_layout.setSpacing(4) # 减小间距
|
|
|
info_group.setLayout(info_layout)
|
|
|
|
|
|
# 标题
|
|
|
info_title = QLabel("基本信息")
|
|
|
info_title.setObjectName("basicInfoTitle")
|
|
|
info_layout.addWidget(info_title)
|
|
|
|
|
|
# 使用网格布局实现紧凑的两行布局
|
|
|
grid_layout = QHBoxLayout()
|
|
|
grid_layout.setSpacing(16)
|
|
|
|
|
|
# 第一列
|
|
|
col1_layout = QHBoxLayout()
|
|
|
col1_layout.setSpacing(8)
|
|
|
|
|
|
# 规程名称
|
|
|
name_label = QLabel("规程名称:")
|
|
|
name_label.setFixedWidth(60)
|
|
|
name_label.setProperty("fieldLabel", True)
|
|
|
self.nameEdit = QLineEdit()
|
|
|
self.nameEdit.setPlaceholderText("输入规程名称")
|
|
|
self.nameEdit.setObjectName("procedureNameEdit")
|
|
|
self.nameEdit.setMaximumHeight(32)
|
|
|
col1_layout.addWidget(name_label)
|
|
|
col1_layout.addWidget(self.nameEdit)
|
|
|
|
|
|
# 规程编号
|
|
|
number_label = QLabel("规程编号:")
|
|
|
number_label.setFixedWidth(60)
|
|
|
number_label.setProperty("fieldLabel", True)
|
|
|
self.numberEdit = QLineEdit()
|
|
|
self.numberEdit.setPlaceholderText("输入规程编号")
|
|
|
self.numberEdit.setObjectName("procedureNumberEdit")
|
|
|
self.numberEdit.setMaximumHeight(32)
|
|
|
col1_layout.addWidget(number_label)
|
|
|
col1_layout.addWidget(self.numberEdit)
|
|
|
|
|
|
grid_layout.addLayout(col1_layout)
|
|
|
|
|
|
# 第二列
|
|
|
col2_layout = QHBoxLayout()
|
|
|
col2_layout.setSpacing(8)
|
|
|
|
|
|
# 规程类型
|
|
|
type_label = QLabel("规程类型:")
|
|
|
type_label.setFixedWidth(60)
|
|
|
type_label.setProperty("fieldLabel", True)
|
|
|
self.typeEdit = QLineEdit()
|
|
|
self.typeEdit.setPlaceholderText("输入规程类型")
|
|
|
self.typeEdit.setObjectName("procedureTypeEdit")
|
|
|
self.typeEdit.setMaximumHeight(32)
|
|
|
col2_layout.addWidget(type_label)
|
|
|
col2_layout.addWidget(self.typeEdit)
|
|
|
|
|
|
# 规程描述(简化为单行输入)
|
|
|
desc_label = QLabel("规程描述:")
|
|
|
desc_label.setFixedWidth(60)
|
|
|
desc_label.setProperty("fieldLabel", True)
|
|
|
self.descriptionEdit = QLineEdit() # 改为单行输入
|
|
|
self.descriptionEdit.setPlaceholderText("输入规程描述")
|
|
|
self.descriptionEdit.setObjectName("procedureDescriptionEdit")
|
|
|
self.descriptionEdit.setMaximumHeight(32)
|
|
|
col2_layout.addWidget(desc_label)
|
|
|
col2_layout.addWidget(self.descriptionEdit)
|
|
|
|
|
|
grid_layout.addLayout(col2_layout)
|
|
|
grid_layout.addStretch() # 添加弹性空间
|
|
|
|
|
|
info_layout.addLayout(grid_layout)
|
|
|
|
|
|
def createStepsSection(self, layout):
|
|
|
"""创建步骤表格区域 - 优化为最大化表格显示"""
|
|
|
steps_group = QWidget()
|
|
|
steps_group.setObjectName("stepsGroup")
|
|
|
layout.addWidget(steps_group)
|
|
|
|
|
|
steps_layout = QVBoxLayout()
|
|
|
steps_layout.setContentsMargins(8, 4, 8, 4) # 减小边距
|
|
|
steps_layout.setSpacing(4) # 减小间距
|
|
|
steps_group.setLayout(steps_layout)
|
|
|
|
|
|
# 紧凑的标题和按钮行
|
|
|
header_layout = QHBoxLayout()
|
|
|
header_layout.setSpacing(8)
|
|
|
|
|
|
# 标题
|
|
|
steps_title = QLabel("步骤列表")
|
|
|
steps_title.setObjectName("stepsTitle")
|
|
|
header_layout.addWidget(steps_title)
|
|
|
|
|
|
# 添加步骤按钮(紧凑样式)
|
|
|
add_step_btn = QPushButton("添加步骤")
|
|
|
add_step_btn.setIcon(qta.icon('fa5s.plus', color='white'))
|
|
|
add_step_btn.setObjectName("addStepButton")
|
|
|
add_step_btn.setMaximumHeight(28) # 减小按钮高度
|
|
|
add_step_btn.clicked.connect(self.addStep)
|
|
|
header_layout.addWidget(add_step_btn)
|
|
|
|
|
|
# 删除步骤按钮(紧凑样式)
|
|
|
delete_step_btn = QPushButton("删除步骤")
|
|
|
delete_step_btn.setIcon(qta.icon('fa5s.trash', color='white'))
|
|
|
delete_step_btn.setObjectName("deleteStepButton")
|
|
|
delete_step_btn.setMaximumHeight(28) # 减小按钮高度
|
|
|
delete_step_btn.clicked.connect(self.deleteStep)
|
|
|
header_layout.addWidget(delete_step_btn)
|
|
|
|
|
|
header_layout.addStretch()
|
|
|
steps_layout.addLayout(header_layout)
|
|
|
|
|
|
# 步骤表格(最大化显示)
|
|
|
self.stepsTable = QTableWidget()
|
|
|
self.stepsTable.setObjectName("stepsTable")
|
|
|
self.stepsTable.setColumnCount(6)
|
|
|
self.stepsTable.setHorizontalHeaderLabels(['序号', '步骤ID', '操作', '类型', '预期值', '备注'])
|
|
|
|
|
|
# 设置表格属性
|
|
|
self.stepsTable.setSelectionBehavior(QAbstractItemView.SelectRows)
|
|
|
self.stepsTable.setEditTriggers(QAbstractItemView.AllEditTriggers)
|
|
|
self.stepsTable.setAlternatingRowColors(True) # 启用交替行颜色
|
|
|
self.stepsTable.setShowGrid(True) # 显示网格线
|
|
|
|
|
|
# 优化表格样式
|
|
|
|
|
|
# 设置列宽
|
|
|
header = self.stepsTable.horizontalHeader()
|
|
|
if header:
|
|
|
header.setSectionResizeMode(0, QHeaderView.ResizeToContents) # 序号
|
|
|
header.setSectionResizeMode(1, QHeaderView.ResizeToContents) # 步骤ID
|
|
|
header.setSectionResizeMode(2, QHeaderView.Stretch) # 操作
|
|
|
header.setSectionResizeMode(3, QHeaderView.ResizeToContents) # 类型
|
|
|
header.setSectionResizeMode(4, QHeaderView.ResizeToContents) # 预期值
|
|
|
header.setSectionResizeMode(5, QHeaderView.ResizeToContents) # 备注
|
|
|
|
|
|
# 设置最小列宽
|
|
|
self.stepsTable.setColumnWidth(0, 50) # 序号
|
|
|
self.stepsTable.setColumnWidth(1, 100) # 步骤ID
|
|
|
self.stepsTable.setColumnWidth(3, 70) # 类型
|
|
|
self.stepsTable.setColumnWidth(4, 80) # 预期值
|
|
|
self.stepsTable.setColumnWidth(5, 100) # 备注
|
|
|
|
|
|
# 添加右键菜单
|
|
|
self.stepsTable.setContextMenuPolicy(Qt.CustomContextMenu)
|
|
|
self.stepsTable.customContextMenuRequested.connect(self.showContextMenu)
|
|
|
|
|
|
steps_layout.addWidget(self.stepsTable)
|
|
|
|
|
|
def createButtonSection(self, layout):
|
|
|
"""创建紧凑的按钮区域"""
|
|
|
button_widget = QWidget()
|
|
|
button_widget.setMaximumHeight(40) # 限制按钮区域高度
|
|
|
button_widget.setMinimumHeight(40)
|
|
|
|
|
|
button_layout = QHBoxLayout()
|
|
|
button_layout.setContentsMargins(8, 4, 8, 4) # 减小边距
|
|
|
button_layout.setSpacing(8) # 减小按钮间距
|
|
|
button_widget.setLayout(button_layout)
|
|
|
|
|
|
# 保存按钮(紧凑样式)
|
|
|
save_btn = QPushButton("保存规程")
|
|
|
save_btn.setIcon(qta.icon('fa5s.save', color='white'))
|
|
|
save_btn.setObjectName("saveProcedureButton")
|
|
|
save_btn.setMaximumHeight(32) # 减小按钮高度
|
|
|
save_btn.clicked.connect(self.saveProcedure)
|
|
|
button_layout.addWidget(save_btn)
|
|
|
|
|
|
# 取消按钮(紧凑样式)
|
|
|
cancel_btn = QPushButton("取消")
|
|
|
cancel_btn.setIcon(qta.icon('fa5s.times', color='red'))
|
|
|
cancel_btn.setObjectName("cancelEditButton")
|
|
|
cancel_btn.setMaximumHeight(32) # 减小按钮高度
|
|
|
cancel_btn.clicked.connect(self.cancelEdit)
|
|
|
button_layout.addWidget(cancel_btn)
|
|
|
|
|
|
button_layout.addStretch() # 将按钮推到左侧
|
|
|
layout.addWidget(button_widget)
|
|
|
|
|
|
def loadProcedureData(self):
|
|
|
"""加载规程数据"""
|
|
|
if not self.procedureData:
|
|
|
print("警告:规程数据为空")
|
|
|
return
|
|
|
|
|
|
print(f"加载规程数据: {self.procedureData.keys()}")
|
|
|
|
|
|
# 加载基本信息
|
|
|
procedure_info = self.procedureData.get('规程信息', {})
|
|
|
self.nameEdit.setText(procedure_info.get('规程名称', ''))
|
|
|
self.numberEdit.setText(procedure_info.get('规程编号', ''))
|
|
|
self.typeEdit.setText(procedure_info.get('规程类型', ''))
|
|
|
|
|
|
# 加载描述(可能在不同位置)
|
|
|
description = self.procedureData.get('description', '')
|
|
|
if not description:
|
|
|
# 尝试从测试用例信息中获取描述
|
|
|
test_case_info = self.procedureData.get('测试用例信息', {})
|
|
|
description = test_case_info.get('工况描述', '')
|
|
|
self.descriptionEdit.setText(description)
|
|
|
|
|
|
# 加载步骤数据
|
|
|
self.loadStepsData()
|
|
|
|
|
|
def loadStepsData(self):
|
|
|
"""加载步骤数据到表格(主子步骤都显示,字段驼峰)"""
|
|
|
steps = []
|
|
|
# 兼容老数据
|
|
|
if 'testSteps' in self.procedureData:
|
|
|
steps = self.procedureData['testSteps']
|
|
|
elif '测试步骤' in self.procedureData:
|
|
|
steps = self.procedureData['测试步骤']
|
|
|
if not steps:
|
|
|
self.stepsTable.setRowCount(0)
|
|
|
return
|
|
|
# 计算总行数
|
|
|
totalRows = 0
|
|
|
for step in steps:
|
|
|
totalRows += 1
|
|
|
totalRows += len(step.get('subSteps', []) or step.get('子步骤', []))
|
|
|
self.stepsTable.setRowCount(totalRows)
|
|
|
currentRow = 0
|
|
|
for mainStepIndex, mainStep in enumerate(steps):
|
|
|
# 主步骤
|
|
|
orderItem = QTableWidgetItem(str(mainStepIndex + 1))
|
|
|
orderItem.setFlags(orderItem.flags() & ~Qt.ItemIsEditable)
|
|
|
self.stepsTable.setItem(currentRow, 0, orderItem)
|
|
|
stepId = mainStep.get('stepId') or mainStep.get('步骤ID', '') or ''
|
|
|
self.stepsTable.setItem(currentRow, 1, QTableWidgetItem(stepId))
|
|
|
operation = mainStep.get('operation') or mainStep.get('步骤描述', '') or ''
|
|
|
self.stepsTable.setItem(currentRow, 2, QTableWidgetItem(operation))
|
|
|
stepType = mainStep.get('stepType') or mainStep.get('操作类型', '') or ''
|
|
|
self.stepsTable.setItem(currentRow, 3, QTableWidgetItem(stepType))
|
|
|
expectedValue = mainStep.get('expectedValue') or mainStep.get('预期结果', '') or ''
|
|
|
self.stepsTable.setItem(currentRow, 4, QTableWidgetItem(str(expectedValue)))
|
|
|
remark = mainStep.get('remark') or mainStep.get('备注', '') or ''
|
|
|
self.stepsTable.setItem(currentRow, 5, QTableWidgetItem(remark))
|
|
|
# 主步骤样式
|
|
|
for col in range(6):
|
|
|
item = self.stepsTable.item(currentRow, col)
|
|
|
if item:
|
|
|
item.setBackground(QBrush(QColor(220, 220, 220)))
|
|
|
font = item.font()
|
|
|
font.setBold(True)
|
|
|
item.setFont(font)
|
|
|
currentRow += 1
|
|
|
# 子步骤
|
|
|
subSteps = mainStep.get('subSteps') or mainStep.get('子步骤', [])
|
|
|
for subIndex, subStep in enumerate(subSteps):
|
|
|
orderItem = QTableWidgetItem(str(subStep.get('order', subStep.get('序号', subIndex + 1))))
|
|
|
orderItem.setFlags(orderItem.flags() & ~Qt.ItemIsEditable)
|
|
|
self.stepsTable.setItem(currentRow, 0, orderItem)
|
|
|
subStepId = subStep.get('stepId') or f"{stepId}{subStep.get('order', subStep.get('序号', subIndex + 1))}" or ''
|
|
|
self.stepsTable.setItem(currentRow, 1, QTableWidgetItem(subStepId))
|
|
|
subOperation = subStep.get('operation') or subStep.get('操作', '') or ''
|
|
|
self.stepsTable.setItem(currentRow, 2, QTableWidgetItem(subOperation))
|
|
|
subType = subStep.get('stepType') or subStep.get('操作类型', '') or ''
|
|
|
self.stepsTable.setItem(currentRow, 3, QTableWidgetItem(subType))
|
|
|
subExpected = subStep.get('expectedValue') or subStep.get('预期结果', '') or ''
|
|
|
self.stepsTable.setItem(currentRow, 4, QTableWidgetItem(str(subExpected)))
|
|
|
subRemark = subStep.get('remark') or subStep.get('备注', '') or ''
|
|
|
self.stepsTable.setItem(currentRow, 5, QTableWidgetItem(subRemark))
|
|
|
currentRow += 1
|
|
|
self.stepsTable.resizeRowsToContents()
|
|
|
|
|
|
def addStep(self):
|
|
|
"""添加步骤(默认为子步骤)"""
|
|
|
dialog = StepEditDialog(parent=self)
|
|
|
if dialog.exec_() == QDialog.Accepted:
|
|
|
step_data = dialog.getStepData()
|
|
|
|
|
|
# 添加到表格
|
|
|
row = self.stepsTable.rowCount()
|
|
|
self.stepsTable.insertRow(row)
|
|
|
|
|
|
# 设置序号
|
|
|
order_item = QTableWidgetItem(str(row + 1))
|
|
|
order_item.setFlags(order_item.flags() & ~Qt.ItemIsEditable)
|
|
|
self.stepsTable.setItem(row, 0, order_item)
|
|
|
|
|
|
# 设置其他字段
|
|
|
self.stepsTable.setItem(row, 1, QTableWidgetItem(step_data['stepId']))
|
|
|
self.stepsTable.setItem(row, 2, QTableWidgetItem(step_data['operation']))
|
|
|
self.stepsTable.setItem(row, 3, QTableWidgetItem(step_data['type']))
|
|
|
self.stepsTable.setItem(row, 4, QTableWidgetItem(step_data['expectedValue']))
|
|
|
self.stepsTable.setItem(row, 5, QTableWidgetItem(step_data['remark']))
|
|
|
|
|
|
# 不设置主步骤样式,默认为子步骤
|
|
|
|
|
|
def deleteStep(self):
|
|
|
"""删除选中的步骤"""
|
|
|
current_row = self.stepsTable.currentRow()
|
|
|
if current_row < 0:
|
|
|
QMessageBox.warning(self, "未选择步骤", "请先选择要删除的步骤")
|
|
|
return
|
|
|
|
|
|
# 检查是否是最后一个步骤
|
|
|
total_rows = self.stepsTable.rowCount()
|
|
|
is_last_step = (total_rows == 1)
|
|
|
|
|
|
if is_last_step:
|
|
|
reply = QMessageBox.question(
|
|
|
self, "确认删除",
|
|
|
"这是最后一个步骤,删除后规程将为空。确定要删除吗?",
|
|
|
QMessageBox.Yes | QMessageBox.No
|
|
|
)
|
|
|
else:
|
|
|
reply = QMessageBox.question(
|
|
|
self, "确认删除",
|
|
|
f"确定要删除第 {current_row + 1} 个步骤吗?",
|
|
|
QMessageBox.Yes | QMessageBox.No
|
|
|
)
|
|
|
|
|
|
if reply == QMessageBox.Yes:
|
|
|
self.stepsTable.removeRow(current_row)
|
|
|
# 重新编号
|
|
|
self.renumberSteps()
|
|
|
|
|
|
# 如果删除后没有步骤了,给出提示
|
|
|
if self.stepsTable.rowCount() == 0:
|
|
|
QMessageBox.information(
|
|
|
self, "步骤已清空",
|
|
|
"所有步骤已删除,规程现在为空。您可以添加新步骤或保存空规程。"
|
|
|
)
|
|
|
|
|
|
def editStep(self, row, column):
|
|
|
"""编辑步骤"""
|
|
|
# 获取当前步骤数据
|
|
|
stepIdItem = self.stepsTable.item(row, 1)
|
|
|
operationItem = self.stepsTable.item(row, 2)
|
|
|
typeItem = self.stepsTable.item(row, 3)
|
|
|
expectedValueItem = self.stepsTable.item(row, 4)
|
|
|
remarkItem = self.stepsTable.item(row, 5)
|
|
|
|
|
|
step_data = {
|
|
|
'stepId': stepIdItem.text() if stepIdItem else '',
|
|
|
'operation': operationItem.text() if operationItem else '',
|
|
|
'type': typeItem.text() if typeItem else 'SET',
|
|
|
'expectedValue': expectedValueItem.text() if expectedValueItem else '',
|
|
|
'remark': remarkItem.text() if remarkItem else '',
|
|
|
'order': row + 1
|
|
|
}
|
|
|
|
|
|
dialog = StepEditDialog(step_data, self)
|
|
|
if dialog.exec_() == QDialog.Accepted:
|
|
|
updated_data = dialog.getStepData()
|
|
|
|
|
|
# 更新表格
|
|
|
self.stepsTable.setItem(row, 1, QTableWidgetItem(updated_data['stepId']))
|
|
|
self.stepsTable.setItem(row, 2, QTableWidgetItem(updated_data['operation']))
|
|
|
self.stepsTable.setItem(row, 3, QTableWidgetItem(updated_data['type']))
|
|
|
self.stepsTable.setItem(row, 4, QTableWidgetItem(updated_data['expectedValue']))
|
|
|
self.stepsTable.setItem(row, 5, QTableWidgetItem(updated_data['remark']))
|
|
|
|
|
|
def renumberSteps(self):
|
|
|
"""重新编号步骤"""
|
|
|
for row in range(self.stepsTable.rowCount()):
|
|
|
order_item = QTableWidgetItem(str(row + 1))
|
|
|
order_item.setFlags(order_item.flags() & ~Qt.ItemIsEditable)
|
|
|
|
|
|
# 检查这一行是否原本是主步骤(通过检查其他列的背景色)
|
|
|
is_main_step = False
|
|
|
for col in range(1, 6): # 检查步骤ID、操作、类型、预期值、备注列
|
|
|
item = self.stepsTable.item(row, col)
|
|
|
if item and hasattr(item, 'background') and item.background().color() == QColor(220, 220, 220):
|
|
|
is_main_step = True
|
|
|
break
|
|
|
|
|
|
# 如果是主步骤,设置背景色和字体
|
|
|
if is_main_step:
|
|
|
order_item.setBackground(QBrush(QColor(220, 220, 220)))
|
|
|
font = order_item.font()
|
|
|
font.setBold(True)
|
|
|
order_item.setFont(font)
|
|
|
|
|
|
self.stepsTable.setItem(row, 0, order_item)
|
|
|
|
|
|
def getStepsData(self):
|
|
|
"""获取表格中的步骤数据"""
|
|
|
steps = []
|
|
|
for row in range(self.stepsTable.rowCount()):
|
|
|
stepIdItem = self.stepsTable.item(row, 1)
|
|
|
operationItem = self.stepsTable.item(row, 2)
|
|
|
typeItem = self.stepsTable.item(row, 3)
|
|
|
expectedValueItem = self.stepsTable.item(row, 4)
|
|
|
remarkItem = self.stepsTable.item(row, 5)
|
|
|
|
|
|
step = {
|
|
|
'order': row + 1,
|
|
|
'stepId': stepIdItem.text() if stepIdItem else '',
|
|
|
'operation': operationItem.text() if operationItem else '',
|
|
|
'type': typeItem.text() if typeItem else 'SET',
|
|
|
'expectedValue': expectedValueItem.text() if expectedValueItem else '',
|
|
|
'remark': remarkItem.text() if remarkItem else ''
|
|
|
}
|
|
|
steps.append(step)
|
|
|
return steps
|
|
|
|
|
|
def saveProcedure(self):
|
|
|
"""保存规程到数据库(字段驼峰,主子步骤分组)"""
|
|
|
try:
|
|
|
procedureInfo = {
|
|
|
'procedureName': self.nameEdit.text().strip(),
|
|
|
'procedureNumber': self.numberEdit.text().strip(),
|
|
|
'procedureType': self.typeEdit.text().strip()
|
|
|
}
|
|
|
|
|
|
# 数据验证和默认值处理
|
|
|
if not procedureInfo['procedureName']:
|
|
|
QMessageBox.warning(self, "验证失败", "规程名称不能为空")
|
|
|
return
|
|
|
if not procedureInfo['procedureNumber']:
|
|
|
QMessageBox.warning(self, "验证失败", "规程编号不能为空")
|
|
|
return
|
|
|
|
|
|
# 如果规程类型为空,设置默认值
|
|
|
if not procedureInfo['procedureType']:
|
|
|
procedureInfo['procedureType'] = '标准规程'
|
|
|
|
|
|
testSteps = []
|
|
|
currentMainStep = None
|
|
|
print(f"开始处理步骤,总行数: {self.stepsTable.rowCount()}")
|
|
|
for row in range(self.stepsTable.rowCount()):
|
|
|
try:
|
|
|
stepIdItem = self.stepsTable.item(row, 1)
|
|
|
operationItem = self.stepsTable.item(row, 2)
|
|
|
stepTypeItem = self.stepsTable.item(row, 3)
|
|
|
expectedValueItem = self.stepsTable.item(row, 4)
|
|
|
remarkItem = self.stepsTable.item(row, 5)
|
|
|
|
|
|
stepId = stepIdItem.text() if stepIdItem else ''
|
|
|
operation = operationItem.text() if operationItem else ''
|
|
|
stepType = stepTypeItem.text() if stepTypeItem else ''
|
|
|
expectedValue = expectedValueItem.text() if expectedValueItem else ''
|
|
|
remark = remarkItem.text() if remarkItem else ''
|
|
|
|
|
|
print(f"处理第{row+1}行: stepId='{stepId}', operation='{operation}', stepType='{stepType}', expectedValue='{expectedValue}', remark='{remark}'")
|
|
|
|
|
|
# 简化验证:只要步骤ID或操作描述不为空就认为是有效步骤
|
|
|
if not (stepId.strip() or operation.strip()):
|
|
|
print(f" 跳过第{row+1}行:步骤ID和操作描述都为空")
|
|
|
continue
|
|
|
|
|
|
# 判断是否为主步骤(通过背景色判断)
|
|
|
isMainStep = False
|
|
|
item = self.stepsTable.item(row, 0)
|
|
|
if item and hasattr(item, 'background') and item.background().color() == QColor(220, 220, 220):
|
|
|
isMainStep = True
|
|
|
|
|
|
print(f" 第{row+1}行是主步骤: {isMainStep}")
|
|
|
|
|
|
if isMainStep:
|
|
|
# 如果有前一个主步骤,先添加到结果中
|
|
|
if currentMainStep is not None:
|
|
|
testSteps.append(currentMainStep)
|
|
|
print(f" 添加主步骤到testSteps,当前testSteps长度: {len(testSteps)}")
|
|
|
# 创建新的主步骤
|
|
|
currentMainStep = {
|
|
|
'步骤ID': stepId,
|
|
|
'步骤描述': operation,
|
|
|
'操作类型': stepType,
|
|
|
'预期结果': expectedValue,
|
|
|
'备注': remark,
|
|
|
'子步骤': []
|
|
|
}
|
|
|
print(f" 创建新主步骤: {stepId}")
|
|
|
else:
|
|
|
# 子步骤
|
|
|
if currentMainStep is not None:
|
|
|
orderItem = self.stepsTable.item(row, 0)
|
|
|
orderText = orderItem.text() if orderItem else '1'
|
|
|
try:
|
|
|
orderNum = int(orderText)
|
|
|
except ValueError:
|
|
|
orderNum = 1
|
|
|
subStep = {
|
|
|
'序号': orderNum,
|
|
|
'操作': operation,
|
|
|
'操作类型': stepType,
|
|
|
'预期结果': expectedValue,
|
|
|
'实际结果': '',
|
|
|
'一致性': '是',
|
|
|
'测试时间': '',
|
|
|
'备注': remark
|
|
|
}
|
|
|
currentMainStep['子步骤'].append(subStep)
|
|
|
print(f" 添加子步骤到当前主步骤,子步骤数量: {len(currentMainStep['子步骤'])}")
|
|
|
except Exception as rowError:
|
|
|
print(f"处理第 {row + 1} 行时出错: {str(rowError)}")
|
|
|
continue
|
|
|
|
|
|
# 添加最后一个主步骤
|
|
|
if currentMainStep is not None:
|
|
|
testSteps.append(currentMainStep)
|
|
|
print(f"添加最后一个主步骤,最终testSteps长度: {len(testSteps)}")
|
|
|
else:
|
|
|
print("没有找到任何主步骤")
|
|
|
|
|
|
# 修改验证逻辑:允许保存空规程,但给出提示
|
|
|
if not testSteps:
|
|
|
reply = QMessageBox.question(
|
|
|
self, "确认保存",
|
|
|
"当前规程没有步骤,确定要保存空规程吗?",
|
|
|
QMessageBox.Yes | QMessageBox.No
|
|
|
)
|
|
|
if reply == QMessageBox.No:
|
|
|
return
|
|
|
updatedProcedure = {
|
|
|
'规程信息': {
|
|
|
'规程名称': procedureInfo['procedureName'],
|
|
|
'规程编号': procedureInfo['procedureNumber'],
|
|
|
'规程类型': procedureInfo['procedureType']
|
|
|
},
|
|
|
'测试用例信息': {
|
|
|
'测试用例': procedureInfo['procedureName'],
|
|
|
'用例编号': procedureInfo['procedureNumber'],
|
|
|
'工况描述': self.descriptionEdit.text() # 修改为text(),因为现在是QLineEdit
|
|
|
},
|
|
|
'测试步骤': testSteps,
|
|
|
'updatedAt': datetime.now().isoformat()
|
|
|
}
|
|
|
print(f"准备保存规程: {procedureInfo['procedureName']}")
|
|
|
print(f"步骤数量: {len(testSteps)}")
|
|
|
success = self.dbManager.updateProcedure(self.procedureId, updatedProcedure)
|
|
|
if success:
|
|
|
QMessageBox.information(self, "保存成功", "规程已成功保存到数据库")
|
|
|
self.procedureSaved.emit(self.procedureId)
|
|
|
if self.parent:
|
|
|
currentIndex = self.parent.tabs.currentIndex()
|
|
|
if currentIndex > 0:
|
|
|
self.parent.tabs.removeTab(currentIndex)
|
|
|
else:
|
|
|
QMessageBox.critical(self, "保存失败", "保存规程到数据库时发生错误")
|
|
|
except Exception as e:
|
|
|
print(f"保存规程时发生错误: {str(e)}")
|
|
|
import traceback
|
|
|
traceback.print_exc()
|
|
|
QMessageBox.critical(self, "保存失败", f"保存规程时发生错误:{str(e)}")
|
|
|
|
|
|
def cancelEdit(self):
|
|
|
"""取消编辑"""
|
|
|
reply = QMessageBox.question(
|
|
|
self, "确认取消",
|
|
|
"确定要取消编辑吗?未保存的更改将丢失。",
|
|
|
QMessageBox.Yes | QMessageBox.No
|
|
|
)
|
|
|
|
|
|
if reply == QMessageBox.Yes:
|
|
|
# 关闭编辑器标签页
|
|
|
if self.parent:
|
|
|
current_index = self.parent.tabs.currentIndex()
|
|
|
if current_index > 0: # 不是规程管理标签页
|
|
|
self.parent.tabs.removeTab(current_index)
|
|
|
|
|
|
def showContextMenu(self, pos):
|
|
|
"""显示右键菜单"""
|
|
|
menu = QMenu(self)
|
|
|
|
|
|
# 获取当前行
|
|
|
current_row = self.stepsTable.rowAt(pos.y())
|
|
|
|
|
|
# 添加步骤
|
|
|
add_action = menu.addAction("添加步骤")
|
|
|
add_action.triggered.connect(self.addStep)
|
|
|
|
|
|
# 插入步骤(在选中行之前)
|
|
|
if current_row >= 0:
|
|
|
insert_action = menu.addAction(f"在此行前插入步骤")
|
|
|
insert_action.triggered.connect(lambda: self.insertStep(current_row))
|
|
|
|
|
|
# 编辑步骤
|
|
|
if current_row >= 0:
|
|
|
edit_action = menu.addAction("编辑步骤")
|
|
|
edit_action.triggered.connect(lambda: self.editStep(current_row, 0))
|
|
|
|
|
|
# 删除步骤
|
|
|
if current_row >= 0:
|
|
|
delete_action = menu.addAction("删除步骤")
|
|
|
delete_action.triggered.connect(self.deleteStep)
|
|
|
|
|
|
# 显示菜单
|
|
|
menu.exec_(self.stepsTable.mapToGlobal(pos))
|
|
|
|
|
|
def insertStep(self, position):
|
|
|
"""在指定位置插入新步骤(默认为子步骤)"""
|
|
|
dialog = StepEditDialog(parent=self)
|
|
|
if dialog.exec_() == QDialog.Accepted:
|
|
|
step_data = dialog.getStepData()
|
|
|
|
|
|
# 在指定位置插入行
|
|
|
self.stepsTable.insertRow(position)
|
|
|
|
|
|
# 设置序号
|
|
|
order_item = QTableWidgetItem(str(position + 1))
|
|
|
order_item.setFlags(order_item.flags() & ~Qt.ItemIsEditable)
|
|
|
self.stepsTable.setItem(position, 0, order_item)
|
|
|
|
|
|
# 设置其他字段
|
|
|
self.stepsTable.setItem(position, 1, QTableWidgetItem(step_data['stepId']))
|
|
|
self.stepsTable.setItem(position, 2, QTableWidgetItem(step_data['operation']))
|
|
|
self.stepsTable.setItem(position, 3, QTableWidgetItem(step_data['type']))
|
|
|
self.stepsTable.setItem(position, 4, QTableWidgetItem(step_data['expectedValue']))
|
|
|
self.stepsTable.setItem(position, 5, QTableWidgetItem(step_data['remark']))
|
|
|
|
|
|
# 不设置主步骤样式,默认为子步骤
|
|
|
# 重新编号所有步骤
|
|
|
self.renumberSteps() |