You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

347 lines
14 KiB
Python

4 months ago
import sys
import os
4 months ago
from PyQt5.QtCore import Qt, QTimer, QModelIndex, QSize, pyqtSignal, QFile, QTextStream
from PyQt5.QtGui import QBrush, QColor, QStandardItemModel, QStandardItem
from PyQt5.QtWidgets import (QTableView, QPushButton, QVBoxLayout, QWidget, QHBoxLayout,
QLabel, QMenu, QFileDialog, QDialog, QLineEdit,
QDialogButtonBox, QMessageBox, QHeaderView, QSplitter)
4 months ago
import qtawesome as qta
from datetime import datetime
from utils.DBModels.ProcedureModel import DatabaseManager
4 months ago
from UI.ProcedureManager.StepExecutor import StepExecutor
4 months ago
4 months ago
class HistoryViewerWidget(QWidget):
4 months ago
def __init__(self, dbManager, parent=None):
super().__init__(parent)
self.dbManager = dbManager
self.initUi()
self.loadHistory()
def initUi(self):
layout = QVBoxLayout()
# 搜索栏
searchLayout = QHBoxLayout()
self.searchEdit = QLineEdit()
self.searchEdit.setPlaceholderText("搜索规程...")
self.searchEdit.textChanged.connect(self.loadHistory)
searchLayout.addWidget(QLabel("搜索:"))
searchLayout.addWidget(self.searchEdit)
4 months ago
layout.addLayout(searchLayout)
4 months ago
# 历史记录表格
self.table = QTableView()
self.table.setEditTriggers(QTableView.NoEditTriggers)
self.model = QStandardItemModel()
self.model.setHorizontalHeaderLabels(["ID", "规程全名", "类型", "执行时间", "报告路径"])
self.table.setModel(self.model)
self.table.doubleClicked.connect(self.openReportOrDetails)
self.table.setSelectionBehavior(QTableView.SelectRows)
self.table.setContextMenuPolicy(Qt.CustomContextMenu)
self.table.customContextMenuRequested.connect(self.showHistoryContextMenu)
4 months ago
self.setupTableDisplay(self.table)
4 months ago
# 步骤详情表格
self.stepTable = QTableView()
4 months ago
self.stepTable.setEditTriggers(QTableView.NoEditTriggers)
4 months ago
self.stepModel = QStandardItemModel()
self.stepModel.setHorizontalHeaderLabels(["步骤ID", "步骤描述", "执行时间", "执行结果"])
self.stepTable.setModel(self.stepModel)
4 months ago
self.setupTableDisplay(self.stepTable)
4 months ago
# 分割窗口
splitter = QSplitter(Qt.Vertical)
splitter.addWidget(self.table)
splitter.addWidget(self.stepTable)
4 months ago
splitter.setSizes([400, 400])
4 months ago
layout.addWidget(splitter)
4 months ago
# 操作按钮
buttonLayout = QHBoxLayout()
separator = QWidget()
separator.setFixedWidth(20)
buttonLayout.addWidget(separator)
4 months ago
self.deleteButton = QPushButton("删除历史记录")
self.deleteButton.setIcon(qta.icon('fa5s.trash', color='red'))
self.deleteButton.clicked.connect(self.deleteSelectedHistory)
4 months ago
buttonLayout.addWidget(self.deleteButton)
4 months ago
self.exportButton = QPushButton("导出报告")
self.exportButton.setIcon(qta.icon('fa5s.file-export', color='green'))
self.exportButton.clicked.connect(self.exportReport)
4 months ago
buttonLayout.addWidget(self.exportButton)
4 months ago
4 months ago
buttonLayout.addStretch()
layout.addLayout(buttonLayout)
4 months ago
self.setLayout(layout)
4 months ago
def setupTableDisplay(self, table):
"""设置表格显示属性"""
table.setWordWrap(True)
table.setAlternatingRowColors(True)
table.setSortingEnabled(True)
header = table.horizontalHeader()
if header:
header.setStretchLastSection(True)
header.setSectionResizeMode(QHeaderView.Interactive)
header.setDefaultAlignment(Qt.AlignLeft)
verticalHeader = table.verticalHeader()
if verticalHeader:
verticalHeader.setSectionResizeMode(QHeaderView.ResizeToContents)
verticalHeader.setDefaultAlignment(Qt.AlignCenter)
if table == self.table:
table.setSelectionMode(QTableView.ExtendedSelection)
else:
table.setSelectionMode(QTableView.SingleSelection)
table.setSelectionBehavior(QTableView.SelectRows)
table.setStyleSheet("""
QTableView {
gridline-color: #d0d0d0;
background-color: white;
alternate-background-color: #f5f5f5;
selection-background-color: #0078d4;
selection-color: white;
}
QTableView::item {
padding: 5px;
border: none;
}
QTableView::item:selected {
background-color: #0078d4;
color: white;
}
QHeaderView::section {
background-color: #f0f0f0;
padding: 5px;
border: 1px solid #d0d0d0;
font-weight: bold;
}
""")
4 months ago
def loadHistory(self):
4 months ago
"""加载历史记录"""
4 months ago
self.model.removeRows(0, self.model.rowCount())
filterText = self.searchEdit.text().strip()
history = self.dbManager.getExecutionHistory(filterText)
for record in history:
4 months ago
rowItems = []
for i, value in enumerate(record):
item = QStandardItem(str(value) if value is not None else "")
item.setTextAlignment(Qt.AlignTop | Qt.AlignLeft)
if i in [1, 4]: # 规程全名和报告路径列
item.setToolTip(str(value) if value is not None else "")
rowItems.append(item)
self.model.appendRow(rowItems)
self.adjustTableColumns(self.table, self.model)
4 months ago
def openReportOrDetails(self, index):
4 months ago
"""打开报告或详情"""
executionId = self.model.item(index.row(), 0).text()
stepResults = self.dbManager.getStepResults(executionId)
self.showStepDetails(stepResults)
self.saveOperationHistory(executionId, "查看步骤详情", "")
4 months ago
def saveOperationHistory(self, executionId, operationType, operationDetail):
4 months ago
"""保存操作历史"""
4 months ago
self.dbManager.saveOperationHistory(
executionId,
operationType,
operationDetail,
datetime.now().strftime("%Y-%m-%d %H:%M:%S")
)
def showStepDetails(self, stepResults):
"""显示步骤详情"""
self.stepModel.removeRows(0, self.stepModel.rowCount())
if not stepResults:
return
for step in stepResults:
4 months ago
rowItems = []
# 步骤ID
stepIdItem = QStandardItem(step.get('step_id', ''))
stepIdItem.setTextAlignment(Qt.AlignCenter)
rowItems.append(stepIdItem)
# 步骤描述
descItem = QStandardItem(step.get('step_description', ''))
descItem.setTextAlignment(Qt.AlignTop | Qt.AlignLeft)
descItem.setToolTip(step.get('step_description', ''))
rowItems.append(descItem)
# 执行时间
timeItem = QStandardItem(step.get('execution_time', ''))
timeItem.setTextAlignment(Qt.AlignCenter)
rowItems.append(timeItem)
# 执行结果
resultText = '成功' if step.get('result', False) else '失败'
resultItem = QStandardItem(resultText)
resultItem.setTextAlignment(Qt.AlignCenter)
if step.get('result', False):
resultItem.setBackground(QBrush(QColor(200, 255, 200)))
else:
resultItem.setBackground(QBrush(QColor(255, 200, 200)))
rowItems.append(resultItem)
self.stepModel.appendRow(rowItems)
self.adjustTableColumns(self.stepTable, self.stepModel)
def adjustTableColumns(self, table, model):
"""调整表格列宽"""
header = table.horizontalHeader()
if not header:
return
for col in range(model.columnCount()):
if col == 0: # ID列
header.setSectionResizeMode(col, QHeaderView.ResizeToContents)
elif col == 1: # 描述列
header.setSectionResizeMode(col, QHeaderView.Stretch)
elif col == 2: # 类型列
header.setSectionResizeMode(col, QHeaderView.ResizeToContents)
elif col == 3: # 时间列
header.setSectionResizeMode(col, QHeaderView.ResizeToContents)
elif col == 4: # 报告路径或结果列
if model == self.model:
header.setSectionResizeMode(col, QHeaderView.Stretch)
else:
header.setSectionResizeMode(col, QHeaderView.ResizeToContents)
table.resizeRowsToContents()
4 months ago
def deleteSelectedHistory(self):
"""删除选中的历史记录"""
4 months ago
selectedIndexes = self.table.selectionModel().selectedRows()
if not selectedIndexes:
4 months ago
QMessageBox.warning(self, "未选择", "请先选择要删除的历史记录")
return
4 months ago
executionIds = []
for index in selectedIndexes:
executionId = self.model.item(index.row(), 0).text()
executionIds.append(executionId)
4 months ago
reply = QMessageBox.question(
self,
"确认删除",
4 months ago
f"确定要删除选中的 {len(executionIds)} 条历史记录吗?\n此操作不可恢复!",
4 months ago
QMessageBox.Yes | QMessageBox.No
)
if reply == QMessageBox.Yes:
4 months ago
success = self.dbManager.deleteExecutionHistory(executionIds)
4 months ago
if success:
QMessageBox.information(self, "删除成功", "已成功删除选中的历史记录")
4 months ago
self.loadHistory()
4 months ago
else:
QMessageBox.warning(self, "删除失败", "删除历史记录时发生错误")
def exportReport(self):
4 months ago
"""导出报告"""
selectedIndexes = self.table.selectionModel().selectedRows()
if not selectedIndexes:
4 months ago
QMessageBox.warning(self, "未选择", "请先选择要导出的历史记录")
return
4 months ago
if len(selectedIndexes) > 1:
4 months ago
QMessageBox.warning(self, "选择过多", "一次只能导出一个历史记录的报告")
return
4 months ago
index = selectedIndexes[0]
executionId = self.model.item(index.row(), 0).text()
4 months ago
4 months ago
executionData = self.dbManager.getExecutionDetails(executionId)
if not executionData:
4 months ago
QMessageBox.warning(self, "数据错误", "无法获取执行详情数据")
return
try:
4 months ago
executor = StepExecutor(executionData['procedure_content'],
executionData['procedure_id'],
4 months ago
self.dbManager)
4 months ago
executor.stepResults = executionData['step_results']
4 months ago
for step in executor.tableModel.stepData:
4 months ago
stepResult = next((s for s in executionData['step_results']
4 months ago
if s['step_id'] == step['stepId']), None)
4 months ago
if stepResult:
4 months ago
step['executed'] = True
4 months ago
step['result'] = stepResult['result']
step['time'] = datetime.strptime(stepResult['execution_time'], "%Y-%m-%d %H:%M:%S")
resultPath = executor.generateReport()
if resultPath:
return
else:
QMessageBox.information(self, "导出取消", "用户取消了报告导出")
4 months ago
except Exception as e:
QMessageBox.critical(self, "导出错误", f"生成报告时出错:\n{str(e)}")
4 months ago
import traceback
print(f"导出报告详细错误: {traceback.format_exc()}")
4 months ago
def showHistoryContextMenu(self, pos):
4 months ago
"""显示右键菜单"""
4 months ago
index = self.table.indexAt(pos)
4 months ago
menu = QMenu()
4 months ago
if index.isValid():
4 months ago
deleteAction = menu.addAction(qta.icon('fa5s.trash', color='red'), "删除历史记录")
4 months ago
deleteAction.triggered.connect(self.deleteSelectedHistory)
4 months ago
menu.addSeparator()
exportAction = menu.addAction(qta.icon('fa5s.file-export', color='green'), "导出报告")
exportAction.triggered.connect(lambda: self.exportReportFromContextMenu(index))
menu.addSeparator()
selectAllAction = menu.addAction("全选")
selectAllAction.triggered.connect(self.selectAllHistory)
deselectAllAction = menu.addAction("取消全选")
deselectAllAction.triggered.connect(self.deselectAllHistory)
if index.isValid():
menu.exec_(self.table.viewport().mapToGlobal(pos))
else:
menu.exec_(self.table.mapToGlobal(pos))
def showEvent(self, event):
"""窗口显示事件"""
super().showEvent(event)
QTimer.singleShot(100, self.adjustTablesAfterShow)
def adjustTablesAfterShow(self):
"""窗口显示后调整表格"""
self.adjustTableColumns(self.table, self.model)
self.adjustTableColumns(self.stepTable, self.stepModel)
def selectAllHistory(self):
"""全选历史记录"""
self.table.selectAll()
def deselectAllHistory(self):
"""取消全选历史记录"""
self.table.clearSelection()
def exportReportFromContextMenu(self, index):
"""从右键菜单导出报告"""
self.table.selectRow(index.row())
self.exportReport()