698 lines
23 KiB
C++
698 lines
23 KiB
C++
#include "procedureplayerwidget.h"
|
|
#include "overlaydialogswidget.h"
|
|
#include "../procedure/proceduremanager.h"
|
|
#include <QHeaderView>
|
|
#include <QScrollBar>
|
|
|
|
// ============== StepItemWidget ==============
|
|
|
|
StepItemWidget::StepItemWidget(const StepData &step, QWidget *parent)
|
|
: QWidget(parent), m_step(step), m_typeBtn(nullptr), m_confirmCheckbox(nullptr), m_tableBtn(nullptr)
|
|
{
|
|
setupUI();
|
|
updateStatusDisplay();
|
|
}
|
|
|
|
void StepItemWidget::setupUI()
|
|
{
|
|
auto *mainLayout = new QVBoxLayout(this);
|
|
mainLayout->setContentsMargins(0, 0, 0, 0);
|
|
mainLayout->setSpacing(0);
|
|
|
|
// 主容器
|
|
m_container = new QWidget(this);
|
|
m_container->setObjectName("stepContainer");
|
|
|
|
auto *containerLayout = new QHBoxLayout(m_container);
|
|
containerLayout->setContentsMargins(12, 10, 12, 10);
|
|
containerLayout->setSpacing(12);
|
|
|
|
// 左侧区域:类型按钮 + 状态标签
|
|
auto *leftArea = new QVBoxLayout();
|
|
leftArea->setAlignment(Qt::AlignTop);
|
|
leftArea->setSpacing(6);
|
|
|
|
// 类型切换按钮 - Material Design 风格,可点击切换 - 触摸屏优化
|
|
m_typeBtn = new QPushButton(this);
|
|
updateTypeButton();
|
|
m_typeBtn->setFixedSize(78, 36);
|
|
m_typeBtn->setCursor(Qt::PointingHandCursor);
|
|
connect(m_typeBtn, &QPushButton::clicked, this, &StepItemWidget::onTypeBtnClicked);
|
|
leftArea->addWidget(m_typeBtn);
|
|
|
|
// 状态标签 - 放在类型按钮下方,大小一致
|
|
m_statusLabel = new QLabel(this);
|
|
m_statusLabel->setFixedSize(78, 36);
|
|
m_statusLabel->setAlignment(Qt::AlignCenter);
|
|
leftArea->addWidget(m_statusLabel);
|
|
|
|
containerLayout->addLayout(leftArea);
|
|
|
|
// 内容区域
|
|
auto *contentArea = new QVBoxLayout();
|
|
contentArea->setSpacing(6);
|
|
|
|
m_contentLabel = new QLabel(m_step.content, this);
|
|
m_contentLabel->setWordWrap(true);
|
|
m_contentLabel->setTextFormat(Qt::PlainText);
|
|
m_contentLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
|
|
m_contentLabel->setMinimumWidth(0);
|
|
m_contentLabel->setStyleSheet("font-size: 14px; color: #37474F; line-height: 1.6;");
|
|
contentArea->addWidget(m_contentLabel);
|
|
|
|
containerLayout->addLayout(contentArea, 1);
|
|
|
|
// 右侧区域:仅确认复选框(如果是手动步骤)
|
|
if (m_step.type == StepType::Manual)
|
|
{
|
|
auto *rightArea = new QVBoxLayout();
|
|
rightArea->setAlignment(Qt::AlignTop | Qt::AlignRight);
|
|
rightArea->setSpacing(10);
|
|
|
|
m_confirmCheckbox = new QCheckBox("确认", this);
|
|
m_confirmCheckbox->setStyleSheet(
|
|
"QCheckBox { font-size: 14px; color: #455A64; spacing: 8px; }"
|
|
"QCheckBox::indicator { width: 24px; height: 24px; border: 2px solid #90A4AE; border-radius: 4px; background: white; }"
|
|
"QCheckBox::indicator:checked { background: #43A047; border-color: #43A047; image: url(:/icons/check-white.png); }"
|
|
"QCheckBox::indicator:hover { border-color: #1976D2; }");
|
|
m_confirmCheckbox->setChecked(m_step.status == StepStatus::Confirmed);
|
|
connect(m_confirmCheckbox, &QCheckBox::toggled, this, &StepItemWidget::onCheckboxToggled);
|
|
rightArea->addWidget(m_confirmCheckbox);
|
|
|
|
containerLayout->addLayout(rightArea);
|
|
}
|
|
|
|
mainLayout->addWidget(m_container);
|
|
|
|
// 底部表格按钮 - banner 样式
|
|
if (!m_step.tableRefs.isEmpty())
|
|
{
|
|
m_tableBtn = new QPushButton("📋 查看关联表格", this);
|
|
m_tableBtn->setFixedHeight(40);
|
|
m_tableBtn->setCursor(Qt::PointingHandCursor);
|
|
m_tableBtn->setStyleSheet(
|
|
"QPushButton { "
|
|
" background: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #E3F2FD, stop:1 #BBDEFB); "
|
|
" color: #1565C0; "
|
|
" border: none; "
|
|
" border-top: 1px solid #E0E0E0; "
|
|
" border-bottom-left-radius: 8px; "
|
|
" border-bottom-right-radius: 8px; "
|
|
" font-size: 13px; "
|
|
" font-weight: 500; "
|
|
" padding: 0px 16px; "
|
|
"}"
|
|
"QPushButton:hover { background: qlineargradient(x1:0, y1:0, x2:1, y2:0, stop:0 #BBDEFB, stop:1 #90CAF9); }"
|
|
"QPushButton:pressed { background: #90CAF9; }");
|
|
connect(m_tableBtn, &QPushButton::clicked, this, [this]()
|
|
{
|
|
if (!m_step.tableRefs.isEmpty()) emit tableExpandRequested(m_step.tableRefs.first()); });
|
|
mainLayout->addWidget(m_tableBtn);
|
|
|
|
// 有表格按钮时,主容器底部不要圆角
|
|
m_container->setStyleSheet(
|
|
"QWidget#stepContainer { background: white; border: 1px solid #E0E0E0; border-bottom: none; "
|
|
"border-top-left-radius: 8px; border-top-right-radius: 8px; "
|
|
"border-bottom-left-radius: 0px; border-bottom-right-radius: 0px; }");
|
|
}
|
|
else
|
|
{
|
|
m_container->setStyleSheet(
|
|
"QWidget#stepContainer { background: white; border: 1px solid #E0E0E0; border-radius: 8px; }");
|
|
}
|
|
}
|
|
|
|
void StepItemWidget::setHighlighted(bool highlighted)
|
|
{
|
|
m_container->setStyleSheet(highlighted
|
|
? "QWidget#stepContainer { background: #FFF8E1; border: 2px solid #FFA000; border-radius: 8px; }"
|
|
: "QWidget#stepContainer { background: white; border: 1px solid #E0E0E0; border-radius: 8px; }");
|
|
}
|
|
|
|
void StepItemWidget::updateStatus(StepStatus status)
|
|
{
|
|
m_step.status = status;
|
|
updateStatusDisplay();
|
|
if (m_confirmCheckbox)
|
|
{
|
|
m_confirmCheckbox->blockSignals(true);
|
|
m_confirmCheckbox->setChecked(status == StepStatus::Confirmed);
|
|
m_confirmCheckbox->blockSignals(false);
|
|
}
|
|
}
|
|
|
|
void StepItemWidget::updateStatusDisplay()
|
|
{
|
|
QString statusText, bgColor, textColor = "white";
|
|
switch (m_step.status)
|
|
{
|
|
case StepStatus::Pending:
|
|
statusText = "待执行";
|
|
bgColor = "#78909C";
|
|
break;
|
|
case StepStatus::InProgress:
|
|
statusText = "执行中";
|
|
bgColor = "#1976D2";
|
|
break;
|
|
case StepStatus::Passed:
|
|
case StepStatus::Confirmed:
|
|
statusText = "✓ 完成";
|
|
bgColor = "#388E3C";
|
|
break;
|
|
case StepStatus::Failed:
|
|
statusText = "✗ 失败";
|
|
bgColor = "#D32F2F";
|
|
break;
|
|
case StepStatus::Skipped:
|
|
statusText = "跳过";
|
|
bgColor = "#FFA000";
|
|
textColor = "#333";
|
|
break;
|
|
}
|
|
m_statusLabel->setText(statusText);
|
|
m_statusLabel->setStyleSheet(QString(
|
|
"QLabel { background: %1; color: %2; border-radius: 6px; padding: 6px 12px; font-size: 13px; font-weight: bold; }")
|
|
.arg(bgColor)
|
|
.arg(textColor));
|
|
}
|
|
|
|
void StepItemWidget::onCheckboxToggled(bool checked)
|
|
{
|
|
if (checked)
|
|
{
|
|
OverlayDialog::question(this, "确认步骤",
|
|
QString("确定已完成此步骤?\n\n%1").arg(m_step.content.left(100)),
|
|
[this](bool accepted)
|
|
{
|
|
if (accepted)
|
|
{
|
|
emit confirmRequested(m_step.id);
|
|
}
|
|
else
|
|
{
|
|
m_confirmCheckbox->blockSignals(true);
|
|
m_confirmCheckbox->setChecked(false);
|
|
m_confirmCheckbox->blockSignals(false);
|
|
}
|
|
});
|
|
}
|
|
else
|
|
{
|
|
OverlayDialog::input(this, "取消确认", "请输入取消原因:",
|
|
[this](bool ok, const QString &reason)
|
|
{
|
|
if (ok && !reason.isEmpty())
|
|
{
|
|
emit confirmCancelled(m_step.id, reason);
|
|
}
|
|
else
|
|
{
|
|
m_confirmCheckbox->blockSignals(true);
|
|
m_confirmCheckbox->setChecked(true);
|
|
m_confirmCheckbox->blockSignals(false);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
void StepItemWidget::onTypeBtnClicked()
|
|
{
|
|
// 切换类型
|
|
m_step.type = (m_step.type == StepType::Automatic) ? StepType::Manual : StepType::Automatic;
|
|
updateTypeButton();
|
|
|
|
// 如果切换为手动模式,显示确认复选框
|
|
if (m_step.type == StepType::Manual && !m_confirmCheckbox)
|
|
{
|
|
m_confirmCheckbox = new QCheckBox("确认", this);
|
|
m_confirmCheckbox->setStyleSheet(
|
|
"QCheckBox { font-size: 15px; color: #455A64; spacing: 10px; }"
|
|
"QCheckBox::indicator { width: 28px; height: 28px; border: 2px solid #90A4AE; border-radius: 5px; background: white; }"
|
|
"QCheckBox::indicator:checked { background: #43A047; border-color: #43A047; }"
|
|
"QCheckBox::indicator:hover { border-color: #1976D2; }");
|
|
connect(m_confirmCheckbox, &QCheckBox::toggled, this, &StepItemWidget::onCheckboxToggled);
|
|
// 找到右侧布局并添加
|
|
if (auto *rightLayout = qobject_cast<QVBoxLayout *>(m_container->layout()->itemAt(m_container->layout()->count() - 1)->layout()))
|
|
{
|
|
rightLayout->addWidget(m_confirmCheckbox);
|
|
}
|
|
}
|
|
else if (m_step.type == StepType::Automatic && m_confirmCheckbox)
|
|
{
|
|
m_confirmCheckbox->hide();
|
|
m_confirmCheckbox->deleteLater();
|
|
m_confirmCheckbox = nullptr;
|
|
}
|
|
|
|
emit stepTypeChanged(m_step.id, m_step.type);
|
|
}
|
|
|
|
void StepItemWidget::updateTypeButton()
|
|
{
|
|
QString typeText = (m_step.type == StepType::Automatic) ? "自动" : "手动";
|
|
QString typeBg = (m_step.type == StepType::Automatic) ? "#0288D1" : "#F57C00";
|
|
QString hoverBg = (m_step.type == StepType::Automatic) ? "#0277BD" : "#EF6C00";
|
|
m_typeBtn->setText(typeText);
|
|
m_typeBtn->setStyleSheet(QString(
|
|
"QPushButton { background: %1; color: white; border: none; border-radius: 6px; "
|
|
"font-size: 13px; font-weight: bold; padding: 0px; margin: 0px; }"
|
|
"QPushButton:hover { background: %2; }"
|
|
"QPushButton:pressed { background: %2; }")
|
|
.arg(typeBg)
|
|
.arg(hoverBg));
|
|
}
|
|
|
|
// ============== TableViewWidget ==============
|
|
|
|
TableViewWidget::TableViewWidget(const TableData &table, QWidget *parent)
|
|
: QWidget(parent), m_table(table), m_expanded(true)
|
|
{
|
|
setupUI();
|
|
}
|
|
|
|
void TableViewWidget::setupUI()
|
|
{
|
|
auto *mainLayout = new QVBoxLayout(this);
|
|
mainLayout->setContentsMargins(0, 0, 0, 0);
|
|
mainLayout->setSpacing(0);
|
|
|
|
// 直接显示表格,不需要头部和展开/收起按钮
|
|
m_tableContainer = new QWidget(this);
|
|
auto *tableLayout = new QVBoxLayout(m_tableContainer);
|
|
tableLayout->setContentsMargins(8, 8, 8, 8);
|
|
|
|
m_tableWidget = new QTableWidget(this);
|
|
m_tableWidget->setStyleSheet(
|
|
"QTableWidget { border: 1px solid #dcdcdc; gridline-color: #E0E0E0; background: white; }"
|
|
"QTableWidget::item { padding: 8px; }"
|
|
"QHeaderView::section { background: #ECEFF1; padding: 8px; border: none; border-bottom: 1px solid #CFD8DC; font-weight: bold; }");
|
|
m_tableWidget->horizontalHeader()->setStretchLastSection(true);
|
|
m_tableWidget->verticalHeader()->setVisible(false);
|
|
m_tableWidget->setAlternatingRowColors(true);
|
|
|
|
tableLayout->addWidget(m_tableWidget);
|
|
m_tableContainer->setVisible(true);
|
|
mainLayout->addWidget(m_tableContainer);
|
|
|
|
updateTable();
|
|
}
|
|
|
|
void TableViewWidget::setHighlightedFields(const QStringList &fields)
|
|
{
|
|
m_highlightedFields = fields;
|
|
updateTable();
|
|
}
|
|
|
|
void TableViewWidget::toggleExpanded()
|
|
{
|
|
m_expanded = !m_expanded;
|
|
m_tableContainer->setVisible(m_expanded);
|
|
m_toggleBtn->setText(m_expanded ? "收起 ▲" : "展开 ▼");
|
|
}
|
|
|
|
void TableViewWidget::updateTable()
|
|
{
|
|
if (m_table.columns.isEmpty())
|
|
{
|
|
m_tableWidget->setColumnCount(3);
|
|
m_tableWidget->setHorizontalHeaderLabels({"字段", "数值", "单位"});
|
|
m_tableWidget->setRowCount(1);
|
|
m_tableWidget->setItem(0, 0, new QTableWidgetItem("暂无数据"));
|
|
return;
|
|
}
|
|
|
|
m_tableWidget->setColumnCount(m_table.columns.size());
|
|
QStringList headers;
|
|
for (const auto &col : m_table.columns)
|
|
headers << col.name;
|
|
m_tableWidget->setHorizontalHeaderLabels(headers);
|
|
|
|
int rowCount = m_table.rows.isEmpty() ? 1 : m_table.rows.size();
|
|
m_tableWidget->setRowCount(rowCount);
|
|
|
|
for (int col = 0; col < m_table.columns.size(); ++col)
|
|
{
|
|
auto *item = new QTableWidgetItem("-");
|
|
item->setTextAlignment(Qt::AlignCenter);
|
|
if (m_highlightedFields.contains(m_table.columns[col].id))
|
|
{
|
|
item->setBackground(QColor("#FFF8E1"));
|
|
}
|
|
m_tableWidget->setItem(0, col, item);
|
|
}
|
|
}
|
|
|
|
// ============== ProcedurePlayerWidget ==============
|
|
|
|
ProcedurePlayerWidget::ProcedurePlayerWidget(ProcedureManager *manager, QWidget *parent)
|
|
: QWidget(parent), m_manager(manager)
|
|
{
|
|
setupUI();
|
|
connect(m_manager, &ProcedureManager::stepStatusChanged, this, &ProcedurePlayerWidget::onStepStatusChanged);
|
|
}
|
|
|
|
ProcedurePlayerWidget::~ProcedurePlayerWidget() {}
|
|
|
|
void ProcedurePlayerWidget::setupUI()
|
|
{
|
|
auto *mainLayout = new QVBoxLayout(this);
|
|
mainLayout->setContentsMargins(0, 0, 0, 0);
|
|
mainLayout->setSpacing(0);
|
|
|
|
// 顶部工具栏 - Material Design 深色
|
|
auto *headerWidget = new QWidget(this);
|
|
headerWidget->setStyleSheet("background: #263238;");
|
|
headerWidget->setFixedHeight(56);
|
|
|
|
auto *headerLayout = new QHBoxLayout(headerWidget);
|
|
headerLayout->setContentsMargins(8, 0, 16, 0);
|
|
headerLayout->setSpacing(12);
|
|
|
|
m_backBtn = new QPushButton("返回列表", this);
|
|
m_backBtn->setFixedHeight(40);
|
|
m_backBtn->setStyleSheet(
|
|
"QPushButton { background: #455A64; color: white; border: none; border-radius: 4px; padding: 0 16px; font-size: 14px; }"
|
|
"QPushButton:hover { background: #546E7A; }"
|
|
"QPushButton:pressed { background: #37474F; }");
|
|
connect(m_backBtn, &QPushButton::clicked, this, &ProcedurePlayerWidget::onBackClicked);
|
|
headerLayout->addWidget(m_backBtn);
|
|
|
|
m_procedureIdLabel = new QLabel(this);
|
|
m_procedureIdLabel->setStyleSheet("background: #1976D2; color: white; border-radius: 4px; padding: 8px 16px; font-size: 14px; font-weight: bold;");
|
|
headerLayout->addWidget(m_procedureIdLabel);
|
|
|
|
m_procedureNameLabel = new QLabel(this);
|
|
m_procedureNameLabel->setStyleSheet("color: white; font-size: 16px; font-weight: bold; margin-left: 8px;");
|
|
headerLayout->addWidget(m_procedureNameLabel, 1);
|
|
|
|
mainLayout->addWidget(headerWidget);
|
|
|
|
// 内容区域
|
|
auto *contentSplitter = new QSplitter(Qt::Horizontal, this);
|
|
|
|
// 左侧导航
|
|
auto *navWidget = new QWidget(this);
|
|
navWidget->setFixedWidth(280);
|
|
navWidget->setStyleSheet("background: white; border-right: 1px solid #E0E0E0;");
|
|
|
|
auto *navLayout = new QVBoxLayout(navWidget);
|
|
navLayout->setContentsMargins(0, 0, 0, 0);
|
|
|
|
auto *navTitle = new QLabel("步骤导航", navWidget);
|
|
navTitle->setStyleSheet(
|
|
"font-size: 15px; font-weight: bold; color: #263238; padding: 16px; "
|
|
"background: #FAFAFA; border-bottom: 1px solid #E0E0E0;");
|
|
navLayout->addWidget(navTitle);
|
|
|
|
m_navigationTree = new QTreeWidget(navWidget);
|
|
m_navigationTree->setHeaderHidden(true);
|
|
m_navigationTree->setStyleSheet(
|
|
"QTreeWidget { border: none; background: white; font-size: 13px; }"
|
|
"QTreeWidget::item { padding: 10px 16px; border-bottom: 1px solid #F5F5F5; }"
|
|
"QTreeWidget::item:selected { background: #E3F2FD; color: #1976D2; border-left: 3px solid #1976D2; }"
|
|
"QTreeWidget::item:hover { background: #F5F5F5; }");
|
|
connect(m_navigationTree, &QTreeWidget::itemClicked, this, &ProcedurePlayerWidget::onNavigationItemClicked);
|
|
navLayout->addWidget(m_navigationTree, 1);
|
|
|
|
contentSplitter->addWidget(navWidget);
|
|
|
|
// 右侧内容区
|
|
auto *rightContainer = new QWidget(this);
|
|
auto *rightLayout = new QVBoxLayout(rightContainer);
|
|
rightLayout->setContentsMargins(0, 0, 0, 0);
|
|
rightLayout->setSpacing(0);
|
|
|
|
// 步骤标题区
|
|
m_stepTitleLabel = new QLabel("请选择一个步骤", rightContainer);
|
|
m_stepTitleLabel->setWordWrap(true);
|
|
m_stepTitleLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Preferred);
|
|
m_stepTitleLabel->setMinimumWidth(0);
|
|
m_stepTitleLabel->setMinimumHeight(50);
|
|
m_stepTitleLabel->setStyleSheet(
|
|
"font-size: 15px; font-weight: bold; color: #263238; padding: 16px 24px; "
|
|
"background: white; border-bottom: 1px solid #E0E0E0;");
|
|
rightLayout->addWidget(m_stepTitleLabel);
|
|
|
|
m_contentArea = new QScrollArea(rightContainer);
|
|
m_contentArea->setWidgetResizable(true);
|
|
m_contentArea->setStyleSheet("QScrollArea { border: none; background: #FAFAFA; }");
|
|
|
|
m_contentWidget = new QWidget();
|
|
m_contentWidget->setStyleSheet("background: #FAFAFA;");
|
|
m_contentLayout = new QVBoxLayout(m_contentWidget);
|
|
m_contentLayout->setContentsMargins(24, 24, 24, 24);
|
|
m_contentLayout->setSpacing(16);
|
|
m_contentLayout->setAlignment(Qt::AlignTop);
|
|
|
|
m_contentArea->setWidget(m_contentWidget);
|
|
rightLayout->addWidget(m_contentArea, 1);
|
|
|
|
contentSplitter->addWidget(rightContainer);
|
|
|
|
contentSplitter->setStretchFactor(0, 0);
|
|
contentSplitter->setStretchFactor(1, 1);
|
|
|
|
mainLayout->addWidget(contentSplitter, 1);
|
|
|
|
// 创建表格浮层(初始隐藏)
|
|
m_tableOverlay = new TableOverlayWidget(this);
|
|
m_tableOverlay->setGeometry(0, 0, width(), height());
|
|
m_tableOverlay->hide();
|
|
}
|
|
|
|
void ProcedurePlayerWidget::loadProcedure(const QString &procedureId)
|
|
{
|
|
m_currentProcedure = m_manager->loadProcedure(procedureId);
|
|
m_procedureIdLabel->setText(m_currentProcedure.id);
|
|
m_procedureNameLabel->setText(m_currentProcedure.name);
|
|
populateNavigation();
|
|
populateStepContent();
|
|
}
|
|
|
|
void ProcedurePlayerWidget::populateNavigation()
|
|
{
|
|
m_navigationTree->clear();
|
|
m_navItems.clear();
|
|
|
|
for (const auto &group : m_currentProcedure.taskGroups)
|
|
{
|
|
auto *groupItem = new QTreeWidgetItem(m_navigationTree);
|
|
groupItem->setText(0, group.name);
|
|
groupItem->setData(0, Qt::UserRole, group.id);
|
|
groupItem->setExpanded(true);
|
|
QFont font = groupItem->font(0);
|
|
font.setBold(true);
|
|
groupItem->setFont(0, font);
|
|
|
|
for (const auto &step : group.steps)
|
|
{
|
|
auto *stepItem = new QTreeWidgetItem(groupItem);
|
|
QString typeIcon = (step.type == StepType::Automatic) ? "⚡" : "✋";
|
|
QString statusIcon;
|
|
switch (step.status)
|
|
{
|
|
case StepStatus::Passed:
|
|
case StepStatus::Confirmed:
|
|
statusIcon = "✅";
|
|
break;
|
|
case StepStatus::Failed:
|
|
statusIcon = "❌";
|
|
break;
|
|
case StepStatus::InProgress:
|
|
statusIcon = "🔄";
|
|
break;
|
|
default:
|
|
statusIcon = "○";
|
|
break;
|
|
}
|
|
stepItem->setText(0, QString("%1 %2 %3").arg(statusIcon).arg(typeIcon).arg(step.content.left(20) + "..."));
|
|
stepItem->setData(0, Qt::UserRole, step.id);
|
|
|
|
if (step.status == StepStatus::Passed || step.status == StepStatus::Confirmed)
|
|
{
|
|
stepItem->setForeground(0, QColor("#388E3C"));
|
|
}
|
|
else if (step.status == StepStatus::Failed)
|
|
{
|
|
stepItem->setForeground(0, QColor("#D32F2F"));
|
|
}
|
|
m_navItems[step.id] = stepItem;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ProcedurePlayerWidget::populateStepContent()
|
|
{
|
|
m_stepWidgets.clear();
|
|
m_tableWidgets.clear();
|
|
m_allSteps.clear();
|
|
|
|
// 清空浮层中的表格
|
|
m_tableOverlay->clearTables();
|
|
|
|
// 清空当前内容
|
|
QLayoutItem *item;
|
|
while ((item = m_contentLayout->takeAt(0)) != nullptr)
|
|
{
|
|
if (item->widget())
|
|
item->widget()->deleteLater();
|
|
delete item;
|
|
}
|
|
|
|
// 收集所有表格定义,添加到浮层
|
|
for (const auto &table : m_currentProcedure.tables)
|
|
{
|
|
auto *tableWidget = new TableViewWidget(table, nullptr);
|
|
m_tableOverlay->addTable(table.id, table.name, tableWidget);
|
|
}
|
|
|
|
// 预存所有步骤数据
|
|
for (const auto &group : m_currentProcedure.taskGroups)
|
|
{
|
|
for (const auto &step : group.steps)
|
|
{
|
|
m_allSteps[step.id] = step;
|
|
}
|
|
}
|
|
|
|
// 默认显示第一个组的所有步骤
|
|
if (!m_currentProcedure.taskGroups.isEmpty())
|
|
{
|
|
showGroupSteps(m_currentProcedure.taskGroups.first().id);
|
|
}
|
|
}
|
|
|
|
void ProcedurePlayerWidget::showStep(const QString &stepId)
|
|
{
|
|
if (!m_allSteps.contains(stepId))
|
|
return;
|
|
|
|
// 清空当前内容
|
|
QLayoutItem *item;
|
|
while ((item = m_contentLayout->takeAt(0)) != nullptr)
|
|
{
|
|
if (item->widget())
|
|
item->widget()->deleteLater();
|
|
delete item;
|
|
}
|
|
m_stepWidgets.clear();
|
|
|
|
const StepData &step = m_allSteps[stepId];
|
|
m_currentStepId = stepId;
|
|
|
|
// 更新标题
|
|
m_stepTitleLabel->setText(step.content.left(50) + (step.content.length() > 50 ? "..." : ""));
|
|
|
|
// 创建步骤组件
|
|
auto *stepWidget = new StepItemWidget(step, m_contentWidget);
|
|
connect(stepWidget, &StepItemWidget::confirmRequested, this, &ProcedurePlayerWidget::onStepConfirmRequested);
|
|
connect(stepWidget, &StepItemWidget::confirmCancelled, this, &ProcedurePlayerWidget::onStepConfirmCancelled);
|
|
connect(stepWidget, &StepItemWidget::tableExpandRequested, this, &ProcedurePlayerWidget::onTableExpandRequested);
|
|
m_stepWidgets[stepId] = stepWidget;
|
|
m_contentLayout->addWidget(stepWidget);
|
|
|
|
// 高亮导航项
|
|
if (m_navItems.contains(stepId))
|
|
{
|
|
m_navigationTree->setCurrentItem(m_navItems[stepId]);
|
|
}
|
|
}
|
|
|
|
void ProcedurePlayerWidget::showGroupSteps(const QString &groupId)
|
|
{
|
|
// 清空当前内容
|
|
QLayoutItem *item;
|
|
while ((item = m_contentLayout->takeAt(0)) != nullptr)
|
|
{
|
|
if (item->widget())
|
|
item->widget()->deleteLater();
|
|
delete item;
|
|
}
|
|
m_stepWidgets.clear();
|
|
m_currentGroupId = groupId;
|
|
|
|
// 查找对应的组
|
|
for (const auto &group : m_currentProcedure.taskGroups)
|
|
{
|
|
if (group.id == groupId)
|
|
{
|
|
// 更新标题
|
|
m_stepTitleLabel->setText(group.name);
|
|
|
|
// 显示该组下的所有步骤
|
|
for (const auto &step : group.steps)
|
|
{
|
|
auto *stepWidget = new StepItemWidget(step, m_contentWidget);
|
|
connect(stepWidget, &StepItemWidget::confirmRequested, this, &ProcedurePlayerWidget::onStepConfirmRequested);
|
|
connect(stepWidget, &StepItemWidget::confirmCancelled, this, &ProcedurePlayerWidget::onStepConfirmCancelled);
|
|
connect(stepWidget, &StepItemWidget::tableExpandRequested, this, &ProcedurePlayerWidget::onTableExpandRequested);
|
|
m_stepWidgets[step.id] = stepWidget;
|
|
m_contentLayout->addWidget(stepWidget);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ProcedurePlayerWidget::onBackClicked() { emit backToListRequested(); }
|
|
|
|
void ProcedurePlayerWidget::onNavigationItemClicked(QTreeWidgetItem *item, int)
|
|
{
|
|
QString id = item->data(0, Qt::UserRole).toString();
|
|
|
|
// 检查是否是组(没有父节点的是组)
|
|
if (item->parent() == nullptr)
|
|
{
|
|
// 点击的是组,显示该组的所有步骤
|
|
showGroupSteps(id);
|
|
}
|
|
else
|
|
{
|
|
// 点击的是步骤,显示单个步骤
|
|
if (m_allSteps.contains(id))
|
|
{
|
|
showStep(id);
|
|
}
|
|
}
|
|
}
|
|
|
|
void ProcedurePlayerWidget::onStepConfirmRequested(const QString &stepId)
|
|
{
|
|
m_manager->confirmStep(m_currentProcedure.id, stepId);
|
|
}
|
|
|
|
void ProcedurePlayerWidget::onStepConfirmCancelled(const QString &stepId, const QString &reason)
|
|
{
|
|
m_manager->cancelStepConfirm(m_currentProcedure.id, stepId, reason);
|
|
}
|
|
|
|
void ProcedurePlayerWidget::onTableExpandRequested(const QString &tableId)
|
|
{
|
|
// 显示浮层并切换到指定表格
|
|
m_tableOverlay->showTable(tableId);
|
|
m_tableOverlay->showOverlay();
|
|
|
|
// 调整浮层位置和大小 - 覆盖整个页面
|
|
int overlayWidth = width();
|
|
int overlayHeight = height();
|
|
int overlayX = 0;
|
|
int overlayY = 0;
|
|
|
|
m_tableOverlay->setGeometry(overlayX, overlayY, overlayWidth, overlayHeight);
|
|
m_tableOverlay->raise();
|
|
}
|
|
|
|
void ProcedurePlayerWidget::onStepStatusChanged(const QString &stepId, StepStatus status)
|
|
{
|
|
if (m_stepWidgets.contains(stepId))
|
|
{
|
|
m_stepWidgets[stepId]->updateStatus(status);
|
|
}
|
|
updateNavigationStatus();
|
|
}
|
|
|
|
void ProcedurePlayerWidget::updateNavigationStatus()
|
|
{
|
|
populateNavigation();
|
|
}
|