|
|
|
|
import sys
|
|
|
|
|
import os
|
|
|
|
|
from PyQt5.QtCore import Qt, QTimer, QAbstractTableModel, QModelIndex, QPoint, QSize, pyqtSignal, QFile, QTextStream
|
|
|
|
|
from PyQt5.QtGui import QBrush, QColor, QFont, QStandardItemModel, QStandardItem
|
|
|
|
|
from PyQt5.QtWidgets import (QApplication, QMainWindow, QTableView,
|
|
|
|
|
QPushButton, QVBoxLayout, QWidget, QHBoxLayout,
|
|
|
|
|
QLabel, QCheckBox, QSpinBox, QMenu, QFileDialog,
|
|
|
|
|
QTabWidget, QTabBar, QListWidget, QListWidgetItem, QDialog,
|
|
|
|
|
QLineEdit, QFormLayout, QDialogButtonBox, QMessageBox,
|
|
|
|
|
QHeaderView, QToolBar, QAction, QStatusBar, QComboBox, QSplitter, QAbstractItemView)
|
|
|
|
|
from docx import Document
|
|
|
|
|
from docx.shared import Pt, RGBColor
|
|
|
|
|
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
|
|
|
|
|
import qtawesome as qta
|
|
|
|
|
|
|
|
|
|
from datetime import datetime
|
|
|
|
|
|
|
|
|
|
# 导入其他模块
|
|
|
|
|
from model.ProcedureModel.ProcedureProcessor import ExcelParser, StepTableModel
|
|
|
|
|
from utils.DBModels.ProcedureModel import DatabaseManager
|
|
|
|
|
from UI.ProcedureManager.StepExecutor import StepExecutor # 修改导入路径
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class HistoryViewerWidget(QDialog):
|
|
|
|
|
def __init__(self, dbManager, parent=None):
|
|
|
|
|
super().__init__(parent)
|
|
|
|
|
self.dbManager = dbManager
|
|
|
|
|
self.setWindowTitle("历史记录查看器")
|
|
|
|
|
self.setGeometry(200, 200, 1000, 800)
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
# 将搜索栏布局添加到主布局
|
|
|
|
|
layout.addLayout(searchLayout) # 修复:在此处添加搜索栏
|
|
|
|
|
|
|
|
|
|
# 历史记录表格
|
|
|
|
|
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)
|
|
|
|
|
|
|
|
|
|
# 新增:设置表格列宽自适应内容
|
|
|
|
|
self.table.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
|
|
|
|
|
|
|
|
|
|
# 步骤详情表格
|
|
|
|
|
self.stepTable = QTableView()
|
|
|
|
|
self.stepTable.setEditTriggers(QTableView.NoEditTriggers) # 新增:禁用编辑
|
|
|
|
|
self.stepModel = QStandardItemModel()
|
|
|
|
|
self.stepModel.setHorizontalHeaderLabels(["步骤ID", "步骤描述", "执行时间", "执行结果"])
|
|
|
|
|
self.stepTable.setModel(self.stepModel)
|
|
|
|
|
|
|
|
|
|
# 新增:设置表格列宽自适应内容
|
|
|
|
|
self.stepTable.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
|
|
|
|
|
|
|
|
|
|
# 分割窗口
|
|
|
|
|
splitter = QSplitter(Qt.Vertical)
|
|
|
|
|
splitter.addWidget(self.table)
|
|
|
|
|
splitter.addWidget(self.stepTable)
|
|
|
|
|
splitter.setSizes([400, 400]) # 初始分配高度
|
|
|
|
|
|
|
|
|
|
layout.addWidget(splitter)
|
|
|
|
|
# 新增:操作按钮布局
|
|
|
|
|
button_layout = QHBoxLayout()
|
|
|
|
|
self.deleteButton = QPushButton("删除历史记录")
|
|
|
|
|
self.deleteButton.setIcon(qta.icon('fa5s.trash', color='red'))
|
|
|
|
|
self.deleteButton.clicked.connect(self.deleteSelectedHistory)
|
|
|
|
|
button_layout.addWidget(self.deleteButton)
|
|
|
|
|
|
|
|
|
|
# 新增:导出报告按钮
|
|
|
|
|
self.exportButton = QPushButton("导出报告")
|
|
|
|
|
self.exportButton.setIcon(qta.icon('fa5s.file-export', color='green'))
|
|
|
|
|
self.exportButton.clicked.connect(self.exportReport)
|
|
|
|
|
button_layout.addWidget(self.exportButton)
|
|
|
|
|
|
|
|
|
|
button_layout.addStretch()
|
|
|
|
|
layout.addLayout(button_layout)
|
|
|
|
|
|
|
|
|
|
# 按钮
|
|
|
|
|
buttonBox = QDialogButtonBox(QDialogButtonBox.Ok)
|
|
|
|
|
buttonBox.accepted.connect(self.accept)
|
|
|
|
|
layout.addWidget(buttonBox)
|
|
|
|
|
|
|
|
|
|
self.setLayout(layout)
|
|
|
|
|
|
|
|
|
|
def loadHistory(self):
|
|
|
|
|
self.model.removeRows(0, self.model.rowCount())
|
|
|
|
|
filterText = self.searchEdit.text().strip()
|
|
|
|
|
history = self.dbManager.getExecutionHistory(filterText)
|
|
|
|
|
|
|
|
|
|
for record in history:
|
|
|
|
|
# 修改:使用正确的列索引(0-4)
|
|
|
|
|
row = [
|
|
|
|
|
QStandardItem(str(record[0])), # ID
|
|
|
|
|
QStandardItem(record[1]), # 规程全名(名称+编号)
|
|
|
|
|
QStandardItem(record[2]), # 类型
|
|
|
|
|
QStandardItem(record[3]), # 执行时间
|
|
|
|
|
QStandardItem(record[4]) # 报告路径
|
|
|
|
|
]
|
|
|
|
|
self.model.appendRow(row)
|
|
|
|
|
|
|
|
|
|
self.table.resizeColumnsToContents()
|
|
|
|
|
|
|
|
|
|
def openReportOrDetails(self, index):
|
|
|
|
|
# 修改:将 get_step_results 改为 getStepResults
|
|
|
|
|
execution_id = self.model.item(index.row(), 0).text()
|
|
|
|
|
step_results = self.dbManager.getStepResults(execution_id) # 修正方法名
|
|
|
|
|
self.showStepDetails(step_results)
|
|
|
|
|
# 保存操作历史
|
|
|
|
|
self.saveOperationHistory(execution_id, "查看步骤详情", "")
|
|
|
|
|
|
|
|
|
|
def saveOperationHistory(self, executionId, operationType, operationDetail):
|
|
|
|
|
"""保存操作历史记录"""
|
|
|
|
|
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:
|
|
|
|
|
row = [
|
|
|
|
|
QStandardItem(step['step_id']),
|
|
|
|
|
QStandardItem(step['step_description']),
|
|
|
|
|
QStandardItem(step['execution_time']),
|
|
|
|
|
QStandardItem('成功' if step['result'] else '失败')
|
|
|
|
|
]
|
|
|
|
|
self.stepModel.appendRow(row)
|
|
|
|
|
|
|
|
|
|
# 新增删除历史记录的方法
|
|
|
|
|
def deleteSelectedHistory(self):
|
|
|
|
|
"""删除选中的历史记录"""
|
|
|
|
|
selected_indexes = self.table.selectionModel().selectedRows()
|
|
|
|
|
if not selected_indexes:
|
|
|
|
|
QMessageBox.warning(self, "未选择", "请先选择要删除的历史记录")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 获取选中的执行ID
|
|
|
|
|
execution_ids = []
|
|
|
|
|
for index in selected_indexes:
|
|
|
|
|
execution_id = self.model.item(index.row(), 0).text()
|
|
|
|
|
execution_ids.append(execution_id)
|
|
|
|
|
|
|
|
|
|
# 确认删除
|
|
|
|
|
reply = QMessageBox.question(
|
|
|
|
|
self,
|
|
|
|
|
"确认删除",
|
|
|
|
|
f"确定要删除选中的 {len(execution_ids)} 条历史记录吗?\n此操作不可恢复!",
|
|
|
|
|
QMessageBox.Yes | QMessageBox.No
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if reply == QMessageBox.Yes:
|
|
|
|
|
# 修改:将 delete_execution_history 改为 deleteExecutionHistory
|
|
|
|
|
success = self.dbManager.deleteExecutionHistory(execution_ids) # 修正方法名
|
|
|
|
|
if success:
|
|
|
|
|
QMessageBox.information(self, "删除成功", "已成功删除选中的历史记录")
|
|
|
|
|
self.loadHistory() # 重新加载历史记录
|
|
|
|
|
else:
|
|
|
|
|
QMessageBox.warning(self, "删除失败", "删除历史记录时发生错误")
|
|
|
|
|
|
|
|
|
|
def exportReport(self):
|
|
|
|
|
"""导出选中历史记录的报告"""
|
|
|
|
|
selected_indexes = self.table.selectionModel().selectedRows()
|
|
|
|
|
if not selected_indexes:
|
|
|
|
|
QMessageBox.warning(self, "未选择", "请先选择要导出的历史记录")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
if len(selected_indexes) > 1:
|
|
|
|
|
QMessageBox.warning(self, "选择过多", "一次只能导出一个历史记录的报告")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
index = selected_indexes[0]
|
|
|
|
|
execution_id = self.model.item(index.row(), 0).text()
|
|
|
|
|
# 修改:报告路径现在是第5列(索引4)
|
|
|
|
|
reportPath = self.model.item(index.row(), 4).text()
|
|
|
|
|
|
|
|
|
|
# 获取执行详情数据
|
|
|
|
|
# 修改:将 get_execution_details 改为 getExecutionDetails
|
|
|
|
|
execution_data = self.dbManager.getExecutionDetails(execution_id) # 修正方法名
|
|
|
|
|
if not execution_data:
|
|
|
|
|
QMessageBox.warning(self, "数据错误", "无法获取执行详情数据")
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
# 生成报告
|
|
|
|
|
try:
|
|
|
|
|
# 创建临时StepExecutor实例用于生成报告
|
|
|
|
|
executor = StepExecutor(execution_data['procedure_content'],
|
|
|
|
|
execution_data['procedure_id'],
|
|
|
|
|
self.dbManager)
|
|
|
|
|
executor.stepResults = execution_data['step_results']
|
|
|
|
|
|
|
|
|
|
# 设置步骤数据
|
|
|
|
|
for step in executor.tableModel.stepData:
|
|
|
|
|
step_result = next((s for s in execution_data['step_results']
|
|
|
|
|
if s['step_id'] == step['stepId']), None)
|
|
|
|
|
if step_result:
|
|
|
|
|
step['executed'] = True
|
|
|
|
|
step['result'] = step_result['result']
|
|
|
|
|
step['time'] = datetime.strptime(step_result['execution_time'], "%Y-%m-%d %H:%M:%S")
|
|
|
|
|
|
|
|
|
|
# 生成报告文件
|
|
|
|
|
default_name = f"{execution_data['procedure_name']}_历史报告_{execution_id}.docx"
|
|
|
|
|
# file_path, _ = QFileDialog.getSaveFileName(
|
|
|
|
|
# self, "保存报告", default_name, "Word文档 (*.docx)"
|
|
|
|
|
# )
|
|
|
|
|
|
|
|
|
|
# if file_path:
|
|
|
|
|
res = executor.generateReport()
|
|
|
|
|
# QMessageBox.information(self, "导出成功", f"报告已保存到:\n{res}")
|
|
|
|
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
QMessageBox.critical(self, "导出错误", f"生成报告时出错:\n{str(e)}")
|
|
|
|
|
|
|
|
|
|
# 新增:历史记录表格的右键菜单
|
|
|
|
|
def showHistoryContextMenu(self, pos):
|
|
|
|
|
"""显示历史记录的右键菜单"""
|
|
|
|
|
index = self.table.indexAt(pos)
|
|
|
|
|
if index.isValid():
|
|
|
|
|
menu = QMenu()
|
|
|
|
|
deleteAction = menu.addAction("删除历史记录")
|
|
|
|
|
deleteAction.triggered.connect(self.deleteSelectedHistory)
|
|
|
|
|
menu.exec_(self.table.viewport().mapToGlobal(pos))
|