Browse Source

专家数据采集页面

chengxr 1 year ago
parent
commit
89fae23fe7

+ 295 - 0
ExpertClient/EXDataTableView.cpp

@@ -0,0 +1,295 @@
+#include "EXDataTableView.h"
+
+#include "EXDataViewDelegate.h"
+
+#include "ProjectManager.h"
+#include <dbService/CNodeDataService.h>
+#include <dbService/ClassSet.h>
+
+#include <Widgets/Button.h>
+#include <QBoxLayout>
+
+#include <QTabWidget>
+#include <QLabel>
+#include <QTableView>
+#include <QTableWidget>
+#include <QAbstractItemModel>
+#include <QStandardItemModel>
+#include <QStandardItem>
+#include <QHeaderView>
+#include <QApplication>
+#include <QDesktopWidget>
+
+#include <QDebug>
+
+EXDataTableView::EXDataTableView(SchemePlanManager::SchemeProcessInfo process, QWidget *parent)
+    : QWidget(parent), m_process(process)
+{
+    m_mind1 = new CMind(this);
+    m_mind2 = new CMind(this);
+    initWidget();
+    initLayout();
+    connectSignalsAndSlots();
+}
+
+SchemePlanManager::SchemeProcessInfo EXDataTableView::process() const
+{
+    return m_process;
+}
+
+void EXDataTableView::initWidget()
+{
+    m_dataTab = new QTabWidget(this);
+    m_dataTab->setTabPosition(QTabWidget::South);
+    m_pageLab  = new QLabel(this);
+    m_previous = new PushButton("上一级指标", this);
+    m_next     = new PushButton("下一级指标", this);
+}
+
+void EXDataTableView::initLayout()
+{
+    m_layout = new QVBoxLayout(this);
+    m_layout->addWidget(m_dataTab);
+    m_pageLayout = new QHBoxLayout();
+    m_layout->addLayout(m_pageLayout);
+
+    m_pageLayout->setSpacing(10);
+    m_pageLayout->addStretch();
+    m_pageLayout->addWidget(m_previous);
+    m_pageLayout->addWidget(m_pageLab);
+    m_pageLayout->addWidget(m_next);
+    m_pageLayout->addStretch();
+}
+
+void EXDataTableView::connectSignalsAndSlots()
+{
+    connect(m_previous, &PushButton::clicked, this, &EXDataTableView::slotPrevious);
+    connect(m_next, &PushButton::clicked, this, &EXDataTableView::slotNext);
+    connect(m_dataTab, &QTabWidget::currentChanged, this, &EXDataTableView::slotTabCurrentChanged);
+}
+
+void EXDataTableView::setupTabWidget()
+{
+    /// 创建 tableView 并添加进 tabWidget
+    /// 这个过程中会触发 tabWidget 的 currentChanged,
+    /// 所以使用 m_isSettingTable 标记此过程, 以采取必要措施来规避一些异常操作
+    m_isSettingTable = true;
+    m_dataTab->clear();
+    for (CNodeData n : m_mind1->nodesInLevel(m_currentPage)) {
+        QTableView *t = new QTableView(m_dataTab);
+        t->setAlternatingRowColors(m_mind2->nodeList().count() > 0);
+        t->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
+        t->horizontalHeader()->setStyleSheet("QHeaderView::section{background:rgb(244,244,244);color: black;}");
+        t->verticalHeader()->setStyleSheet("QHeaderView::section{background:rgb(244,244,244);color: black;}");
+        t->verticalHeader()->setDefaultAlignment(Qt::AlignCenter);
+        t->setSelectionMode(QAbstractItemView::SingleSelection);
+
+        m_dataTab->addTab(t, n.name);
+
+        QStandardItemModel *model = new QStandardItemModel(t);
+        t->setModel(model);
+        connect(t, &QTableView::clicked, this, &EXDataTableView::itemClicked);
+    }
+    m_isSettingTable = false;
+}
+
+int EXDataTableView::currentPage() const
+{
+    return m_currentPage;
+}
+
+void EXDataTableView::setCurrentPage(int p)
+{
+    if (p < 1 || p >= m_mind1->levels()) {
+        return;
+    }
+    m_currentPage = p;
+    m_previous->setEnabled(p > 1);
+    m_next->setEnabled(p < m_mind1->levels() - 1);
+    setupTabWidget();
+
+    m_pageLab->setText(QString("共 %1 页, 当前第 %2 页").arg(m_mind1->levels() - 1).arg(p));
+    updateCurrentTable();
+}
+
+void EXDataTableView::updateCurrentTable()
+{
+    int c             = m_dataTab->currentIndex();
+    QTableView *table = (QTableView *)m_dataTab->widget(c);
+
+    if (table == nullptr || table->model() == nullptr || m_isSettingTable) {
+        return;
+    }
+
+    m_hNodes.clear();
+    m_vNodes.clear();
+    m_values.clear();
+
+    QStandardItemModel *model = (QStandardItemModel *)table->model();
+
+    /// 设置顶部水平方向标题
+    int hIndex         = -1;
+    int dimensionIndex = -1;  // 量纲所在列
+    int typeIndex      = -1;  // 指标类型所在列
+
+    // 指标
+    CNodeData n            = m_mind1->nodesInLevel(m_currentPage)[c];
+    QList<CNodeData> hList = m_mind1->subNodes(n);
+    // 以下情况需要显示指标
+    // 导入权重分析数据
+    // 导入需求分析评估的评估数据
+    if (m_process.type == SchemePlanManager::ImportWeightData || m_process.indexType == ProjectManager::TechIndex) {
+        for (CNodeData node : hList) {
+            QStandardItem *item = new QStandardItem(node.name);
+            item->setToolTip(node.remark);
+            model->setHorizontalHeaderItem(++hIndex, item);
+            m_hNodes.append(node.name);
+        }
+    }
+    // 导入方案优选评估或效能评估的评估数据时, 需要显示量纲
+    if (m_process.type == SchemePlanManager::ImportEvalData
+        && (m_process.indexType == ProjectManager::OptimalIndex
+            || m_process.indexType == ProjectManager::EfficiencyIndex)) {
+        QStandardItem *item = new QStandardItem("指标量纲");
+        model->setHorizontalHeaderItem(++hIndex, item);
+        dimensionIndex = hIndex;
+    }
+    // 导入方案优选评估的评估数据时, 需要显示指标类型
+    if (m_process.type == SchemePlanManager::ImportEvalData && m_process.indexType == ProjectManager::OptimalIndex) {
+        QStandardItem *item = new QStandardItem("指标类型");
+        model->setHorizontalHeaderItem(++hIndex, item);
+        typeIndex = hIndex;
+    }
+
+    /// 设置左侧垂直方向标题
+    QList<CNodeData> vList;
+    // 导入权重分析的专家数据时, 显示指标
+    if (m_process.type == SchemePlanManager::ImportWeightData) {
+        vList = hList;
+    }
+    // 导入评估数据时, 显示最后一级指标
+    if (m_process.type == SchemePlanManager::ImportEvalData) {
+        if (m_process.indexType == ProjectManager::TechIndex) {
+            vList = m_mind2->leaves();
+        } else {
+            vList = m_mind1->leaves();
+        }
+    }
+    for (int i = 0; i < vList.count(); i++) {
+        CNodeData node      = vList[i];
+        QStandardItem *item = new QStandardItem(QString("  %1  ").arg(node.name));
+        item->setToolTip(node.remark);
+        model->setVerticalHeaderItem(i, item);
+        table->setRowHeight(i, 35);
+        m_vNodes.append(node.name);
+    }
+
+    /// 填充量纲和指标类型
+    for (int i = 0; i < vList.count(); i++) {
+        CNodeData vNode  = vList[i];
+        QStandardItem *d = new QStandardItem();
+        d->setData(Qt::AlignCenter, Qt::TextAlignmentRole);  // 单元格文字居中
+        if (dimensionIndex >= 0) {
+            d->setText(vNode.dimension);
+            model->setItem(i, dimensionIndex, d);
+        }
+
+        if (typeIndex >= 0) {
+            model->setItem(i, typeIndex, d);
+        }
+        table->setRowHeight(i, 35);
+    }
+
+    if (m_process.type == SchemePlanManager::ImportWeightData) {
+        for (int i = 0; i < vList.count(); i++) {
+            CNodeData vNode = vList[i];
+            for (int j = 0; j < hList.count(); j++) {
+                CNodeData hNode     = hList[i];
+                QStandardItem *item = new QStandardItem();
+                item->setData(Qt::AlignCenter, Qt::TextAlignmentRole);
+                item->setEditable(false);
+                if (i == j) {
+                    item->setText("1");
+                }
+                if (i >= j) {
+                    item->setBackground(QBrush(QColor("lightgray")));
+                }
+                model->setItem(i, j, item);
+            }
+        }
+    }
+}
+
+CMind *EXDataTableView::mind1() const
+{
+    return m_mind1;
+}
+
+CMind *EXDataTableView::mind2() const
+{
+    return m_mind2;
+}
+
+void EXDataTableView::editItemData(const QModelIndex &index, const QString &val)
+{
+    int c                     = m_dataTab->currentIndex();
+    QTableView *table         = (QTableView *)m_dataTab->widget(c);
+    QStandardItemModel *model = (QStandardItemModel *)table->model();
+    model->itemFromIndex(index)->setText(val);
+    QString symmetry;
+    if (val.startsWith("1/")) {
+        symmetry = val.split("/")[1];
+    } else {
+        if (val == "0" || val == "1") {
+            symmetry = val;
+        } else {
+            symmetry = "1/" + val;
+        }
+    }
+    model->item(index.column(), index.row())->setText(symmetry);
+}
+
+void EXDataTableView::slotPrevious()
+{
+    setCurrentPage(m_currentPage - 1);
+}
+
+void EXDataTableView::slotNext()
+{
+    setCurrentPage(m_currentPage + 1);
+}
+
+void EXDataTableView::slotTabCurrentChanged(int c)
+{
+    Q_UNUSED(c)
+    updateCurrentTable();
+}
+
+void EXDataTableView::itemClicked(const QModelIndex &index)
+{
+    if (index.row() >= index.column()) {
+        return;
+    }
+
+    if (m_process.type != SchemePlanManager::ImportWeightData) {
+        return;
+    }
+
+    QStringList l = { "1/9", "1/7", "1/5", "1/3", "1", "3", "5", "7", "9" };
+
+    QTableView *table         = (QTableView *)sender();
+    QStandardItemModel *model = (QStandardItemModel *)table->model();
+    SchemeBar *scheme =
+            new SchemeBar(model->item(index.row(), 0)->text(), model->horizontalHeaderItem(index.column())->text(), l);
+    scheme->setModal(true);
+    scheme->setAttribute(Qt::WA_DeleteOnClose);
+
+    connect(scheme, &SchemeBar::setValue, [=](QString val) { editItemData(index, val); });
+
+    scheme->show();
+    QPoint p = QCursor::pos();
+    if (p.x() + scheme->width() + 10 >= QApplication::desktop()->width()) {
+        p.setX(QApplication::desktop()->width() - 10 - scheme->width());
+    }
+    scheme->move(p);
+}

+ 87 - 0
ExpertClient/EXDataTableView.h

@@ -0,0 +1,87 @@
+#ifndef EXDATATABLEVIEW_H
+#define EXDATATABLEVIEW_H
+
+#include <QWidget>
+
+#include "SchemePlanManager.h"
+
+#include <dbService/ClassSet.h>
+
+class CMind;
+class ProjectInfo;
+class PushButton;
+
+class QLabel;
+class QTabWidget;
+
+class QVBoxLayout;
+class QHBoxLayout;
+
+/**
+ * @brief The DataTableWidget class
+ * 数据表, 整体上是包含多个 QTableView 的 QTabWidget
+ * 显示以下数据:
+ * 能力重要度评估, 技术措施重要度, 方案优选评估: 权重分析数据和评估数据
+ * 综合效能评估: 权重分析数据
+ * 综合效能评估-物元分析法: 评估数据
+ */
+class EXDataTableView : public QWidget
+{
+    Q_OBJECT
+public:
+    explicit EXDataTableView(SchemePlanManager::SchemeProcessInfo process, QWidget *parent = nullptr);
+
+    SchemePlanManager::SchemeProcessInfo process() const;
+
+    void initWidget();
+
+    void initLayout();
+
+    void connectSignalsAndSlots();
+
+    void setupTabWidget();
+
+    int currentPage() const;
+
+    void setCurrentPage(int p);
+
+    void updateCurrentTable();
+
+    CMind *mind1() const;
+    CMind *mind2() const;
+
+private:
+    void editItemData(const QModelIndex &index, const QString &val);
+
+private slots:
+    void slotPrevious();
+    void slotNext();
+    void slotTabCurrentChanged(int c);
+    void itemClicked(const QModelIndex &index);
+
+private:
+    SchemePlanManager::SchemeProcessInfo m_process;
+
+    int m_currentPage = 0;
+
+    CMind *m_mind1 = nullptr;
+    CMind *m_mind2 = nullptr;
+
+    QTabWidget *m_dataTab = nullptr;
+
+    QLabel *m_pageLab      = nullptr;  // 共4页, 当前第2页
+    PushButton *m_previous = nullptr;  // 上一级指标
+    PushButton *m_next     = nullptr;  // 下一级指标
+
+    QVBoxLayout *m_layout     = nullptr;
+    QHBoxLayout *m_pageLayout = nullptr;
+
+    bool m_isSettingTable = false;
+
+    QStringList m_hNodes;
+    QStringList m_vNodes;
+
+    QVector<qreal> m_values;
+};
+
+#endif  // EXDATATABLEVIEW_H

+ 113 - 0
ExpertClient/EXDataView.cpp

@@ -0,0 +1,113 @@
+#include "EXDataView.h"
+
+#include "EXDataTableView.h"
+
+#include "dbService/SchemeProcessService.h"
+#include "dbService/ClassSet.h"
+#include "dbService/CNodeDataService.h"
+
+#include <CNode.h>
+
+#include <Widgets/Button.h>
+
+#include <QTabWidget>
+#include <QBoxLayout>
+#include <QMap>
+
+#include <QDebug>
+
+EXDataView::EXDataView(ProjectInfo *proj, QWidget *parent) : EXEvalView(proj, parent)
+{
+    setTitle("数据采集");
+
+    connect(m_tab, &QTabWidget::currentChanged, this, &EXDataView::slotTabCurrentChanged);
+}
+
+void EXDataView::setType(int type)
+{
+    EXEvalView::setType(type);
+    setupTabWidget();
+}
+
+///
+/// \brief EXDataView::setupTabWidget
+/// 根据评估类型, 评估方案, 评估算法, 加载合适的数据导入界面
+void EXDataView::setupTabWidget()
+{
+    m_tab->clear();
+
+    QMap<int, QList<CNodeData>> nodeListMap;
+    for (int i : indexList()) {
+        // 获取指标体系数据
+        QList<CNodeData> nodeList;
+        bool ret = CNodeDataService().QueryAll(nodeList, m_proj->id, i);
+        if (ret) {
+            nodeListMap[i] = nodeList;
+        } else {
+            return;
+        }
+
+        // 获取方案规划数据
+        QList<SchemePlanManager::SchemeProcessInfo> processList;
+        ret = SchemeProcessService().QueryAllByProjectIdAndIndexType(processList, m_proj->id, i);
+        if (ret == false) {
+            return;
+        }
+
+        ProjectManager::IndexType t = (ProjectManager::IndexType)i;
+        QString indexName           = ProjectManager::nameOfIndexType(t);
+
+        // 效能评估方案中导入评估数据的步骤, 选择物元分析法时使用
+        SchemePlanManager::SchemeProcessInfo importEffiEvalDataProcess;
+
+        for (SchemePlanManager::SchemeProcessInfo process : processList) {
+            //            // 综合效能评估 - 灰色聚类法: 效能等级配置页面, 导入评估数据页面
+            //            if (process.algorithm == SchemePlanManager::GCE && process.indexType ==
+            //            ProjectManager::EfficiencyIndex) {
+            //                GreyClusteringConfigWidget *gc = new GreyClusteringConfigWidget(process.efficiencyGrades);
+            //                m_tab->addTab(gc, indexName + " - " + "灰色聚类法效能等级配置");
+
+            //                QVector<GreyClusteringItem> items;
+            //                GreyClusteringSampleTable *gs = new GreyClusteringSampleTable(items, 2, 10);
+            //                m_tab->addTab(gs, indexName + " - " + "收集效能评估数据");
+            //            }
+
+            //            // 综合效能评估 - 物元分析法: 效能等级配置页面, 导入评估数据页面
+            //            if (process.algorithm == SchemePlanManager::MEA && process.indexType ==
+            //            ProjectManager::EfficiencyIndex) {
+            //                QList<MEConfigItem> items;
+            //                CMind *mind = new CMind();
+            //                mind->setNodeList(nodeListMap[i]);
+            //                MatterElementConfigWidget *mec = new MatterElementConfigWidget(mind,
+            //                process.efficiencyGrades); m_tab->addTab(mec, indexName + "-" + "物元分析法效能等级配置");
+
+            //                DataTableWidget *table = new DataTableWidget(importEffiEvalDataProcess, this);
+            //                table->mind1()->setNodeList(nodeListMap[i]);
+            //                table->setCurrentPage(1);
+            //                m_tab->addTab(table, indexName + " - " + "收集效能评估数据");
+            //            }
+
+            // 导入效能评估的权重分析数据
+            // 导入其他评估的权重分析数据和评估数据
+            if (process.dSource == SchemePlanManager::FromExpert) {
+                if (process.type == SchemePlanManager::ImportEvalData
+                    && process.indexType == ProjectManager::EfficiencyIndex) {
+                    importEffiEvalDataProcess = process;
+                    continue;
+                }
+
+                EXDataTableView *table = new EXDataTableView(process, this);
+                table->mind1()->setNodeList(nodeListMap[i]);
+                if (i == ProjectManager::TechIndex) {
+                    table->mind2()->setNodeList(nodeListMap[ProjectManager::AbilityIndex]);
+                }
+                table->setCurrentPage(1);
+
+                QString processName = SchemePlanManager::processName(process);
+                m_tab->addTab(table, indexName + " - " + processName);
+            }
+        }
+    }
+}
+
+void EXDataView::slotTabCurrentChanged(int index) { }

+ 34 - 0
ExpertClient/EXDataView.h

@@ -0,0 +1,34 @@
+#ifndef EXDATAVIEW_H
+#define EXDATAVIEW_H
+
+#include "EXEvalView.h"
+
+class PushButton;
+
+class QTabWidget;
+class QHBoxLayout;
+
+/**
+ * @brief The EXDataView class
+ * 数据采集
+ */
+class EXDataView : public EXEvalView
+{
+    Q_OBJECT
+public:
+    explicit EXDataView(ProjectInfo *proj, QWidget *parent);
+
+    void setType(int type) override;
+
+private:
+    void setupTabWidget();
+
+private slots:
+    void slotTabCurrentChanged(int index);
+
+signals:
+
+private:
+};
+
+#endif  // EXDATAVIEW_H

+ 46 - 0
ExpertClient/EXDataViewDelegate.cpp

@@ -0,0 +1,46 @@
+#include "EXDataViewDelegate.h"
+
+#include <QHBoxLayout>
+#include <QLabel>
+#include <QPushButton>
+#include <QButtonGroup>
+#include <QDebug>
+
+SchemeBar::SchemeBar(const QString &lLabel, const QString &rLabel, const QStringList &vlist, QWidget *parent)
+    : QDialog(parent), leftLabel(lLabel), rightLabel(rLabel), barValueList(vlist)
+{
+    setWindowFlags(Qt::CustomizeWindowHint | Qt::WindowCloseButtonHint);
+    QHBoxLayout *hlay = new QHBoxLayout;
+    QLabel *l         = new QLabel(leftLabel);
+    QLabel *r         = new QLabel(rightLabel);
+
+    QHBoxLayout *centerLay = new QHBoxLayout;
+    QButtonGroup *btnGroup = new QButtonGroup(this);
+
+    for (QString &v : barValueList) {
+        QPushButton *btn = new QPushButton(v);
+        centerLay->addWidget(btn);
+        btnGroup->addButton(btn);
+        if (v.startsWith("1/")) {
+            btn->setText(v.split("/")[1]);
+        } else {
+            btn->setText(v);
+        }
+        btn->setProperty("number", v);
+    }
+
+    connect(btnGroup, static_cast<void (QButtonGroup::*)(QAbstractButton *)>(&QButtonGroup::buttonClicked), this,
+            &SchemeBar::barClicked);
+
+    hlay->addWidget(l);
+    hlay->addLayout(centerLay);
+    hlay->addWidget(r);
+
+    setLayout(hlay);
+}
+
+void SchemeBar::barClicked(QAbstractButton *btn)
+{
+    emit setValue(btn->property("number").toString());
+    this->close();
+}

+ 25 - 0
ExpertClient/EXDataViewDelegate.h

@@ -0,0 +1,25 @@
+#ifndef EXDATAVIEWDELEGATE_H
+#define EXDATAVIEWDELEGATE_H
+
+#include <QDialog>
+
+class QAbstractButton;
+class SchemeBar : public QDialog
+{
+    Q_OBJECT
+public:
+    SchemeBar(const QString &lLabel, const QString &rLabel, const QStringList &vlist, QWidget *parent = nullptr);
+
+private slots:
+    void barClicked(QAbstractButton *btn);
+
+signals:
+    void setValue(QString val);
+
+private:
+    QString leftLabel;
+    QString rightLabel;
+    QStringList barValueList;
+};
+
+#endif  // EXDATAVIEWDELEGATE_H

+ 79 - 0
ExpertClient/EXEvalView.cpp

@@ -0,0 +1,79 @@
+#include "EXEvalView.h"
+
+#include "dbService/ClassSet.h"
+
+#include <Widgets/Button.h>
+
+#include <QLabel>
+#include <QLayout>
+#include <QTabWidget>
+
+#include <QDebug>
+
+EXEvalView::EXEvalView(ProjectInfo *proj, QWidget *parent) : QWidget(parent), m_proj(proj)
+{
+    initWidgets();
+    initLayout();
+}
+
+ProjectInfo *EXEvalView::proj() const
+{
+    return m_proj;
+}
+
+void EXEvalView::setProject(ProjectInfo *proj)
+{
+    m_proj = proj;
+}
+
+int EXEvalView::type() const
+{
+    return m_type;
+}
+
+void EXEvalView::setType(int type)
+{
+    m_type = type;
+}
+
+QList<ProjectManager::IndexType> EXEvalView::indexList() const
+{
+    ProjectManager::EvalTypes t = (ProjectManager::EvalTypes)m_type;
+    return ProjectManager::indexListOfEvalTypes(t);
+}
+
+void EXEvalView::setTitle(const QString title)
+{
+    m_title->setText(title);
+}
+
+void EXEvalView::initWidgets()
+{
+    m_title = new QLabel(this);
+    QFont ft("Microsoft YaHei", 12);
+    m_title->setFont(ft);
+
+    // 分割线
+    m_seperator = new QWidget(this);
+    m_seperator->setFixedHeight(1);
+    QPalette pal(m_seperator->palette());
+    pal.setColor(QPalette::Background, QColor("#aaaaaa"));
+    m_seperator->setAutoFillBackground(true);
+    m_seperator->setPalette(pal);
+
+    m_tab = new QTabWidget(this);
+    m_tab->setTabShape(QTabWidget::Rounded);
+}
+
+void EXEvalView::initLayout()
+{
+    m_layout    = new QVBoxLayout(this);
+    m_topLayout = new QHBoxLayout();
+    m_layout->addLayout(m_topLayout);
+    m_layout->addWidget(m_seperator);
+    m_contentLayout = new QHBoxLayout();
+    m_layout->addLayout(m_contentLayout);
+
+    m_topLayout->addWidget(m_title);
+    m_contentLayout->addWidget(m_tab);
+}

+ 51 - 0
ExpertClient/EXEvalView.h

@@ -0,0 +1,51 @@
+#ifndef EXEVALVIEW_H
+#define EXEVALVIEW_H
+
+#include <QWidget>
+
+#include "ProjectManager.h"
+
+class ProjectInfo;
+class PushButton;
+
+class QVBoxLayout;
+class QHBoxLayout;
+class QLabel;
+class QTabWidget;
+
+class EXEvalView : public QWidget
+{
+    Q_OBJECT
+public:
+    explicit EXEvalView(ProjectInfo *proj, QWidget *parent = nullptr);
+
+    ProjectInfo *proj() const;
+    void setProject(ProjectInfo *proj);
+
+    int type() const;
+    virtual void setType(int type);
+
+    // 包含的指标体系类型
+    QList<ProjectManager::IndexType> indexList() const;
+
+    void setTitle(const QString title);
+
+    void initWidgets();
+
+    void initLayout();
+
+protected:
+    ProjectInfo *m_proj = nullptr;
+    int m_type          = 0;  // 评估类型
+
+    QTabWidget *m_tab = nullptr;
+
+    QVBoxLayout *m_layout        = nullptr;
+    QHBoxLayout *m_topLayout     = nullptr;
+    QHBoxLayout *m_contentLayout = nullptr;
+
+    QLabel *m_title      = nullptr;
+    QWidget *m_seperator = nullptr;
+};
+
+#endif  // EXEVALVIEW_H

+ 37 - 0
ExpertClient/EXIndexView.cpp

@@ -0,0 +1,37 @@
+#include "EXIndexView.h"
+
+#include "dbService/ClassSet.h"
+#include "dbService/CNodeDataService.h"
+
+#include <CMindView.h>
+
+#include <QTabWidget>
+
+EXIndexView::EXIndexView(ProjectInfo *proj, QWidget *parent) : EXEvalView(proj, parent)
+{
+    setTitle("指标体系");
+}
+
+void EXIndexView::setType(int type)
+{
+    EXEvalView::setType(type);
+    setupTabWidget();
+}
+
+void EXIndexView::setupTabWidget()
+{
+    m_tab->clear();
+    for (int i : indexList()) {
+        CMindView *m = new CMindView(this);
+        m->setAllowEdit(false);
+        ProjectManager::IndexType t = (ProjectManager::IndexType)i;
+        QString s                   = ProjectManager::nameOfIndexType(t);
+        m_tab->addTab(m, s);
+
+        QList<CNodeData> list;
+        bool ret = CNodeDataService().QueryAll(list, proj()->id, t);
+        if (ret) {
+            m->setNodeList(list);
+        }
+    }
+}

+ 18 - 0
ExpertClient/EXIndexView.h

@@ -0,0 +1,18 @@
+#ifndef EXINDEXVIEW_H
+#define EXINDEXVIEW_H
+
+#include "EXEvalView.h"
+
+class EXIndexView : public EXEvalView
+{
+    Q_OBJECT
+public:
+    EXIndexView(ProjectInfo *proj, QWidget *parent = nullptr);
+
+    void setType(int type) override;
+
+private:
+    void setupTabWidget();
+};
+
+#endif  // EXINDEXVIEW_H

+ 140 - 0
ExpertClient/EXProjectView.cpp

@@ -0,0 +1,140 @@
+#include "EXProjectView.h"
+
+#include "EXIndexView.h"
+#include "EXDataView.h"
+
+#include "ProjectManager.h"
+
+#include <dbService/ClassSet.h>
+
+#include <Widgets/Button.h>
+#include <Widgets/LineEdit.h>
+#include <Widgets/TreeView.h>
+
+#include <QBoxLayout>
+#include <QLabel>
+#include <QStackedWidget>
+
+#include <QDebug>
+
+EXProjectView::EXProjectView(ProjectInfo *proj, QWidget *parent) : QWidget(parent), m_proj(proj)
+{
+    initWidgets();
+    initLayout();
+    connectSigalsAndSlots();
+    if (m_tree->topLevelItemCount() > 0) {
+        itemClicked(m_tree->topLevelItem(0), 0);
+    }
+}
+
+void EXProjectView::showEvent(QShowEvent *event)
+{
+    QWidget::showEvent(event);
+}
+
+void EXProjectView::hideEvent(QHideEvent *event)
+{
+    QWidget::hideEvent(event);
+}
+
+ProjectInfo *EXProjectView::proj() const
+{
+    return m_proj;
+}
+
+void EXProjectView::showEvalWidget(int eval, int task)
+{
+    m_stack->setCurrentIndex(task);
+    EXEvalView *w = (EXEvalView *)m_stack->widget(task);
+    w->setType(eval);
+
+    ProjectManager::EvalType type = (ProjectManager::EvalType)eval;
+
+    qDebug() << __FUNCTION__ << __LINE__ << type;
+}
+
+void EXProjectView::initWidgets()
+{
+    m_title = new QLabel(this);
+    m_title->setText(m_proj->projectName);
+    QFont ft("Microsoft YaHei", 14);
+    m_title->setFont(ft);
+    m_tree = new TreeWidget(this);
+
+    // 根据评估类型添加列表条目
+    QList<ProjectManager::EvalType> types = ProjectManager::evalTypeList(*m_proj);
+    for (int i = 0; i < types.count(); i++) {
+        QString typeName      = ProjectManager::nameOfEvalType(types[i]);
+        QTreeWidgetItem *item = new QTreeWidgetItem({ typeName });
+        Qt::ItemFlags f       = item->flags() ^ Qt::ItemIsSelectable;
+        item->setFlags(f);  // 设置 topLevelItem 不可选中
+
+        item->addChildren({ new QTreeWidgetItem({ "指标体系" }), new QTreeWidgetItem({ "数据采集" }) });
+        m_tree->addTopLevelItem(item);
+    }
+
+    m_tree->expandAll();
+    m_tree->setHeaderHidden(true);
+    m_tree->setFixedWidth(200);
+
+    // 分割线
+    m_seperator = new QWidget(this);
+    m_seperator->setFixedWidth(1);
+    QPalette pal(m_seperator->palette());
+    pal.setColor(QPalette::Background, QColor("#aaaaaa"));
+    m_seperator->setAutoFillBackground(true);
+    m_seperator->setPalette(pal);
+
+    m_stack = new QStackedWidget(this);
+
+    m_indexSystem    = new EXIndexView(m_proj, m_stack);
+    m_dataCollection = new EXDataView(m_proj, m_stack);
+
+    m_stack->addWidget(m_indexSystem);
+    m_stack->addWidget(m_dataCollection);
+}
+
+void EXProjectView::initLayout()
+{
+    m_layout       = new QVBoxLayout(this);
+    m_headerLayout = new QHBoxLayout();
+    m_layout->addLayout(m_headerLayout);
+    m_contentLayout = new QHBoxLayout();
+    m_layout->addLayout(m_contentLayout);
+
+    m_headerLayout->addWidget(m_title);
+    m_headerLayout->addSpacing(20);
+    m_headerLayout->addStretch();
+
+    m_contentLayout->addWidget(m_tree);
+    m_contentLayout->addWidget(m_seperator);
+    m_contentLayout->addWidget(m_stack);
+}
+
+void EXProjectView::connectSigalsAndSlots()
+{
+    connect(m_tree, &TreeWidget::itemClicked, this, &EXProjectView::itemClicked);
+}
+
+void EXProjectView::itemClicked(QTreeWidgetItem *item, int column)
+{
+    Q_UNUSED(column)
+
+    int typeIndex = m_tree->indexOfTopLevelItem(item);  // 评估类型, 一级条目
+    int taskIndex = -1;                                 // 评估事项, 二级条目
+    if (typeIndex < 0) {
+        typeIndex = m_tree->indexOfTopLevelItem(item->parent());
+        taskIndex = item->parent()->indexOfChild(item);
+    }
+
+    // 点击一级条目时, 选中其首个子条目
+    if (taskIndex < 0) {
+        taskIndex = 0;
+        item->child(taskIndex)->setSelected(true);
+    }
+
+    ProjectManager::EvalType eval = ProjectManager::evalTypeList(*m_proj)[typeIndex];
+    QString typeName              = ProjectManager::nameOfEvalType(eval);
+
+    showEvalWidget(eval, taskIndex);
+}

+ 57 - 0
ExpertClient/EXProjectView.h

@@ -0,0 +1,57 @@
+#ifndef EXPROJECTVIEW_H
+#define EXPROJECTVIEW_H
+
+#include <QWidget>
+
+class ProjectInfo;
+
+class EXIndexView;
+class EXDataView;
+
+class PushButton;
+class TreeWidget;
+
+class QVBoxLayout;
+class QHBoxLayout;
+class QLabel;
+class QTreeWidgetItem;
+class QStackedWidget;
+
+class EXProjectView : public QWidget
+{
+    Q_OBJECT
+public:
+    explicit EXProjectView(ProjectInfo *proj, QWidget *parent = nullptr);
+
+    void showEvent(QShowEvent *event) override;
+    void hideEvent(QHideEvent *event) override;
+
+    ProjectInfo *proj() const;
+
+    void showEvalWidget(int eval, int task);
+
+private:
+    void initWidgets();
+    void initLayout();
+    void connectSigalsAndSlots();
+
+private slots:
+    void itemClicked(QTreeWidgetItem *item, int column);
+
+private:
+    ProjectInfo *m_proj = nullptr;
+
+    QVBoxLayout *m_layout        = nullptr;
+    QHBoxLayout *m_headerLayout  = nullptr;
+    QHBoxLayout *m_contentLayout = nullptr;
+
+    QLabel *m_title      = nullptr;
+    TreeWidget *m_tree   = nullptr;
+    QWidget *m_seperator = nullptr;
+
+    QStackedWidget *m_stack      = nullptr;
+    EXIndexView *m_indexSystem   = nullptr;
+    EXDataView *m_dataCollection = nullptr;
+};
+
+#endif  // EXPROJECTVIEW_H

+ 17 - 1
ExpertClient/ExpertClient.pro

@@ -60,14 +60,30 @@ VERSION = "2.1.2.1"
 
 
 SOURCES += \
+    EXDataTableView.cpp \
+    EXDataView.cpp \
+    EXDataViewDelegate.cpp \
+    EXEvalView.cpp \
+    EXIndexView.cpp \
+    EXProjectView.cpp \
     MainWindow.cpp \
+    ProjectManager.cpp \
+    SchemePlanManager.cpp \
     main.cpp
 
 FORMS += \
     MainWindow.ui
 
 HEADERS += \
-    MainWindow.h
+    EXDataTableView.h \
+    EXDataView.h \
+    EXDataViewDelegate.h \
+    EXEvalView.h \
+    EXIndexView.h \
+    EXProjectView.h \
+    MainWindow.h \
+    ProjectManager.h \
+    SchemePlanManager.h
 
 RESOURCES += \
     $$PWD/../QFD/resource.qrc

+ 19 - 4
ExpertClient/MainWindow.cpp

@@ -1,11 +1,26 @@
-#include "MainWindow.h"
+#include "MainWindow.h"
 #include "ui_MainWindow.h"
 
-MainWindow::MainWindow(QWidget *parent) :
-    QMainWindow(parent),
-    ui(new Ui::MainWindow)
+#include "EXProjectView.h"
+
+#include <dbService/ClassSet.h>
+#include <dbService/ProjectService.h>
+
+#include <QDebug>
+
+MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
 {
     ui->setupUi(this);
+    setWindowTitle("");
+
+    ProjectInfo *proj = new ProjectInfo();
+    ProjectService().QueryProjectById(proj, 110);
+    if (proj->id < 0) {
+        return;
+    }
+
+    EXProjectView *projView = new EXProjectView(proj, this);
+    setCentralWidget(projView);
 }
 
 MainWindow::~MainWindow()

+ 154 - 0
ExpertClient/ProjectManager.cpp

@@ -0,0 +1,154 @@
+#include "ProjectManager.h"
+
+#include <dbService/DBServiceSet.h>
+#include <dbService/UserConfigService.h>
+#include <dbService/ProjectService.h>
+
+#include <QMetaEnum>
+#include <QDebug>
+
+QString ProjectManager::nameOfIndexType(ProjectManager::IndexType t)
+{
+    switch (t) {
+    case AbilityIndex:
+        return "能力重要度评估";
+    case TechIndex:
+        return "技术措施重要度评估";
+    case OptimalIndex:
+        return "方案优选评估";
+    case EfficiencyIndex:
+        return "综合效能评估";
+    }
+}
+
+QString ProjectManager::nameOfEvalType(ProjectManager::EvalType t)
+{
+    switch (t) {
+    case None:
+        return "无";
+    case DemandEval:
+        return "需求分析评估";
+    case OptimalEval:
+        return "方案优选评估";
+    case EfficiencyEval:
+        return "综合效能评估";
+    }
+}
+
+QList<ProjectManager::IndexType> ProjectManager::indexListOfEvalTypes(EvalTypes types)
+{
+    QList<IndexType> list;
+
+    QMetaEnum metaEnum = QMetaEnum::fromType<IndexType>();
+
+    for (int i = 0; i < metaEnum.keyCount(); i++) {
+        IndexType t = IndexType(metaEnum.value(i));
+        if ((types & t) == t) {
+            list.append(t);
+        }
+    }
+
+    return list;
+}
+
+ProjectManager::EvalTypes ProjectManager::evalTypes(ProjectInfo proj)
+{
+    int t           = proj.estimateType.toInt();
+    EvalTypes types = EvalTypes(t);
+    return types;
+}
+
+QList<ProjectManager::EvalType> ProjectManager::evalTypeList(EvalTypes types)
+{
+    QList<ProjectManager::EvalType> list;
+
+    QMetaEnum metaEnum = QMetaEnum::fromType<EvalType>();
+
+    for (int i = 0; i < metaEnum.keyCount(); i++) {
+        EvalType t = EvalType(metaEnum.value(i));
+        if (t != None && (types & t) == t) {
+            list.append(t);
+        }
+    }
+
+    return list;
+}
+
+QList<ProjectManager::EvalType> ProjectManager::evalTypeList(ProjectInfo proj)
+{
+    EvalTypes types                      = evalTypes(proj);
+    QList<ProjectManager::EvalType> list = evalTypeList(types);
+    return list;
+}
+
+QList<ProjectManager::IndexType> ProjectManager::indexList(ProjectInfo proj)
+{
+    return indexListOfEvalTypes(evalTypes(proj));
+}
+
+int ProjectManager::queryProjects(QList<ProjectInfo *> *projList)
+{
+    bool ret = ProjectService().QueryAll(projList);
+    return ret ? QF_CODE_SUCCEEDED : QF_CODE_DATA_ERROR;
+}
+
+int ProjectManager::queryProjects(QList<ProjectInfo *> *list, int &total, int page, int pageSize, QString name)
+{
+    qDebug() << __FUNCTION__ << __LINE__ << "page:" << page << "pagesize:" << pageSize << "name:" << name << endl;
+    bool ret = ProjectService().SelectAllByPage(list, total, page, pageSize, name);
+    return ret ? QF_CODE_SUCCEEDED : QF_CODE_DATA_ERROR;
+}
+
+int ProjectManager::verifyProjectInfo(ProjectInfo proj)
+{
+    if (proj.taskName.isEmpty() || proj.estimateObjective.isEmpty() || proj.estimateDept.isEmpty()
+        || proj.estimatePerson.isEmpty() || proj.positionalTitles.isEmpty() || proj.remark.isEmpty()) {
+        return QF_CODE_NEED_PROJ_SUMMARY;
+    }
+
+    if (proj.projectName.isEmpty()) {
+        return QF_CODE_NEED_PROJ_NAME;
+    }
+
+    if (proj.estimateType.isEmpty()) {
+        return QF_CODE_NEED_PROJ_TYPE;
+    }
+
+    return QF_CODE_SUCCEEDED;
+}
+
+int ProjectManager::insertProject(ProjectInfo &proj)
+{
+    int code = verifyProjectInfo(proj);
+    if (code == QF_CODE_SUCCEEDED) {
+        int id = ProjectService().AddProjectInfo(proj);
+        qDebug() << __FUNCTION__ << __LINE__ << "id:" << id;
+        if (id == -1) {
+            code = QF_CODE_PROJ_CREATE_FALIED;
+        }
+    }
+    return code;
+}
+
+int ProjectManager::updateProject(ProjectInfo &proj)
+{
+    if (proj.estimateType.isEmpty()) {
+        return QF_CODE_PROJ_NOT_EDITABLE;
+    }
+    int code = verifyProjectInfo(proj);
+    if (code == QF_CODE_SUCCEEDED) {
+        bool ret = ProjectService().UpdateProjectInfo(proj);
+        qDebug() << __FUNCTION__ << __LINE__ << "ret:" << ret;
+        if (ret == false) {
+            code = QF_CODE_PROJ_UPDATE_FALIED;
+        }
+    }
+    return code;
+}
+
+int ProjectManager::deleteProject(int id)
+{
+    bool ret = ProjectService().DeleteById(id);
+    qDebug() << __FUNCTION__ << __LINE__ << "id:" << id << "ret:" << ret;
+    return ret ? QF_CODE_SUCCEEDED : QF_CODE_PROJ_DELETE_FALIED;
+}

+ 62 - 0
ExpertClient/ProjectManager.h

@@ -0,0 +1,62 @@
+#ifndef PROJECTMANAGER_H
+#define PROJECTMANAGER_H
+
+#include <QObject>
+
+class ProjectInfo;
+
+class ProjectManager : QObject
+{
+    Q_OBJECT
+public:
+    /// 指标体系类型
+    /// @attention 枚举值与本地数据库关联,可以新增,但如无必要禁止修改,除非你知道自己在干什么
+    enum IndexType
+    {
+        AbilityIndex    = 0b1,       // 能力重要度评估指标体系
+        TechIndex       = 0b1 << 1,  // 技术措施重要度评估对象
+        OptimalIndex    = 0b1 << 2,  // 方案优选评估指标体系
+        EfficiencyIndex = 0b1 << 3,  // 综合效能评估指标体系
+    };
+
+    Q_ENUM(IndexType)
+
+    static QString nameOfIndexType(IndexType t);
+
+    /// 评估类型
+    /// 评估类型是根据指标类型体系定义的
+    /// 一个评估类型包含一个或多个指标体系类型
+    enum EvalType
+    {
+        None,
+        DemandEval     = AbilityIndex | TechIndex,  // 需求分析评估
+        OptimalEval    = OptimalIndex,              // 方案优选评估
+        EfficiencyEval = EfficiencyIndex,           // 综合效能评估
+    };
+
+    Q_ENUM(EvalType)
+    Q_DECLARE_FLAGS(EvalTypes, EvalType)
+
+    static QString nameOfEvalType(EvalType t);
+    static QList<IndexType> indexListOfEvalTypes(EvalTypes flags);
+
+    static EvalTypes evalTypes(ProjectInfo proj);
+    static QList<EvalType> evalTypeList(EvalTypes types);
+    static QList<EvalType> evalTypeList(ProjectInfo proj);
+    static QList<IndexType> indexList(ProjectInfo proj);
+
+    ///
+    /// \brief queryProjects 查询全部项目
+    /// \param projList 项目列表
+    /// \return 错误码
+    static int queryProjects(QList<ProjectInfo *> *projList);
+    static int queryProjects(QList<ProjectInfo *> *list, int &total, int page, int pageSize, QString name);
+
+    static int verifyProjectInfo(ProjectInfo proj);
+
+    static int insertProject(ProjectInfo &proj);
+    static int updateProject(ProjectInfo &proj);
+    static int deleteProject(int id);
+};
+
+#endif  // PROJECTMANAGER_H

+ 130 - 0
ExpertClient/SchemePlanManager.cpp

@@ -0,0 +1,130 @@
+#include "SchemePlanManager.h"
+
+#include "ProjectManager.h"
+
+#include <QMetaEnum>
+#include <QDebug>
+
+QString SchemePlanManager::stringFromDataSource(SchemePlanManager::SchemeDataSource src)
+{
+    switch (src) {
+    case NoData:
+        return "无";
+
+    case FromExpert:
+        return "导入专家数据";
+
+    case FromMeasurement:
+        return "导入实测数据";
+    }
+}
+
+QString SchemePlanManager::stringFromAlgorithm(SchemePlanManager::Algorithm alg)
+{
+    switch (alg) {
+    case NoAlg:
+        return "无";
+    case PrincipalComponents:
+        return "主成分分析法";
+    case Entropy:
+        return "熵值法";
+    case AHP:
+        return "层次分析法";
+    case HWM:
+        return "层次加权法";
+    case SPA:
+        return "集对分析法";
+    case MEA:
+        return "物元分析法";
+    case GCE:
+        return "灰色聚类评估法";
+    case WeightedSum:
+        return "加权求和法";
+    }
+}
+
+QString SchemePlanManager::processName(const SchemeProcessInfo &process)
+{
+    QList<QString> l1 = { "构建权重分析指标体系", "收集权重分析数据", "指标体系优化", "指标权重计算", "", "",
+                          "分析结果展示",         "生成分析评估报告" };
+    QList<QString> l2 = {
+        "构建技术措施指标体系", "", "", "", "收集评估数据", "评估计算", "评估结果展示", "生成评估报告"
+    };
+    QList<QString> l3 = { "构建方案优选指标体系", "收集方案优选权重分析数据",
+                          "指标体系优化",         "指标权重计算",
+                          "评估数据采集",         "方案优选计算",
+                          "方案优选结果展示",     "生成方案优选报告" };
+    QList<QString> l4 = { "构建效能评估指标体系", "收集效能评估权重分析数据", "指标体系优化",
+                          "效能评估权重计算",     "收集效能评估数据",         "效能评估计算",
+                          "效能评估结果展示",     "生成效能评估报告" };
+
+    QList<QList<QString>> names = { l1, l2, l3, l4 };
+
+    /// 获取指标类型枚举值 index
+    /// 由于枚举值不是连续自然数, 故不能作为下表来索引数组
+    /// 故须使用 index 替代
+    int index           = 0;
+    QMetaEnum indexEnum = QMetaEnum::fromType<ProjectManager::IndexType>();
+    for (int i = 0; i < indexEnum.keyCount(); i++) {
+        if (indexEnum.value(i) == process.indexType) {
+            index = i;
+            break;
+        }
+    }
+
+    if (index >= 0 && index < names.count() && process.type >= 0 && process.type < names[index].count()) {
+        return names[index][process.type];
+    }
+    return "";
+}
+
+QList<SchemePlanManager::SchemeDataSource>
+SchemePlanManager::processOptionalDataSource(const SchemeProcessInfo &process)
+{
+    if (process.type == ImportEvalData && process.indexType == ProjectManager::EfficiencyIndex) {
+        return { FromMeasurement };
+    }
+    if (process.type == ImportWeightData || process.type == ImportEvalData) {
+        return { FromExpert, FromMeasurement };
+    }
+
+    return {};
+}
+
+/**
+ * 根据指标体系类型和方案步骤类型, 返回可选算法
+ * date: 2023-11-03 by chengxr
+ */
+QList<SchemePlanManager::Algorithm> SchemePlanManager::processOptionalAlgorithms(const SchemeProcessInfo &process)
+{
+    switch (process.type) {
+    case OptimizeIndex:
+        return { PrincipalComponents };
+    case CalculateWeight: {
+        return { AHP, Entropy, PrincipalComponents };
+    }
+    case RunEvaluate: {
+        if (process.indexType == ProjectManager::TechIndex) {
+            return { WeightedSum };
+        } else if (process.indexType == ProjectManager::OptimalIndex) {
+            return { HWM, SPA };
+        } else if (process.indexType == ProjectManager::EfficiencyIndex) {
+            return { MEA, GCE };
+        }
+        return {};
+    }
+    case IndexSystem:
+    case ImportWeightData:
+    case ImportEvalData:
+    case ShowEvalResult:
+    case GenerateReport:
+        return {};
+    }
+}
+
+bool SchemePlanManager::processIsOptional(const SchemeProcessInfo &process)
+{
+    return (process.type == GenerateReport);
+}
+
+SchemePlanManager::SchemePlanManager(QObject *parent) : QObject(parent) { }

+ 123 - 0
ExpertClient/SchemePlanManager.h

@@ -0,0 +1,123 @@
+#ifndef SCHEMEPLANMANAGER_H
+#define SCHEMEPLANMANAGER_H
+
+#include <QObject>
+
+class SchemePlanManager : public QObject
+{
+    Q_OBJECT
+public:
+    /**
+     * @brief The SchemeProcessType enum
+     * 方案步骤类型
+     */
+    enum SchemeProcessType
+    {
+        IndexSystem,       // 构建指标体系
+        ImportWeightData,  // 导入权重数据
+        OptimizeIndex,     // 指标体系优化
+        CalculateWeight,   // 指标权重计算
+        ImportEvalData,    // 导入评估数据
+        RunEvaluate,       // 运行评估算法
+        ShowEvalResult,    // 显示评估结果
+        GenerateReport,    // 生成报告
+    };
+
+    /**
+     * @brief The SchemeDataSource enum
+     * 数据来源
+     */
+    enum SchemeDataSource
+    {
+        NoData = -1,
+        FromExpert,       // 来自专家评估
+        FromMeasurement,  // 来自实测
+    };
+
+    static QString stringFromDataSource(SchemeDataSource src);
+
+    /**
+     * @brief The Algorithm enum
+     * 算法
+     */
+    enum Algorithm
+    {
+        NoAlg = -1,
+        PrincipalComponents,  // principal components analysis 主成分分析法
+        Entropy,              // entropy methods 熵值法
+        AHP,                  // aanalytical hierarchy process 层次分析法
+        HWM,                  // hierarchical weighting method 层次加权法
+        SPA,                  // set pair analysis 集对分析法
+        MEA,                  // matter element analysis 物元分析法
+        GCE,                  // Grey clustering evaluation method 灰色聚类评估法
+        WeightedSum,          // Weighted Sum 加权求和算法
+    };
+
+    static QString stringFromAlgorithm(Algorithm alg);
+
+    /**
+     * @brief The AlgorithmCategory enum
+     * 算法分类
+     */
+    enum AlgorithmCategory
+    {
+        NoCategory = -1,
+    };
+
+    /**
+     * @brief The AlgorithmInfo struct
+     * 算法信息
+     */
+    struct AlgorithmInfo
+    {
+        Algorithm alg              = NoAlg;
+        AlgorithmCategory category = NoCategory;
+    };
+
+    /**
+     * @brief The SchemeProcessInfo struct
+     * 方案步骤详情
+     */
+    struct SchemeProcessInfo
+    {
+        int id        = -1;  // 对应数据库中主键
+        int projectId = -1;  // 所属项目id
+        int indexType;       // 指标体系类型
+
+        int step;                // 步骤编号
+        SchemeProcessType type;  // 步骤类型
+
+        /// 数据来源
+        /// 导入数据(权重数据或者评估数据)时有效
+        SchemeDataSource dSource = NoData;
+
+        /// 算法
+        /// 优化指标体系, 计算权重, 计算评估结果时, 需要用到的算法
+        Algorithm algorithm = NoAlg;
+
+        /// 效能分级, 选择灰色聚类算法计算效能评估时用到
+        int efficiencyGrades = 3;
+
+        bool isChecked = false;  // 是否选择了执行
+        QString createTime;      // 创建时间
+        QString updateTime;      // 更新时间
+    };
+
+    /// 方案步骤的名称
+    static QString processName(const SchemeProcessInfo &process);
+
+    /// 给定方案步骤中可选的数据来源
+    static QList<SchemeDataSource> processOptionalDataSource(const SchemeProcessInfo &process);
+
+    /// 给定方案步骤中可选的算法
+    static QList<Algorithm> processOptionalAlgorithms(const SchemeProcessInfo &process);
+
+    /// 给定方案步骤是否可以选择执行
+    static bool processIsOptional(const SchemeProcessInfo &process);
+
+    explicit SchemePlanManager(QObject *parent = nullptr);
+
+signals:
+};
+
+#endif  // SCHEMEPLANMANAGER_H

+ 8 - 0
QFD/CCanvas/CMindView.cpp

@@ -63,6 +63,8 @@ void CMindView::addNode(CNodeData n)
 {
     m_mind->addNode(n);
     CNodeItem *item = new CNodeItem(n);
+    item->textItem()->setAllowEdit(m_allowEdit);
+    item->rectItem()->setAllowEdit(m_allowEdit);
     connect(item, &CNodeItem::sigEditNode, this, &CMindView::slotEditNode);
     connect(item, &CNodeItem::sigAddSubItem, this, &CMindView::slotAddSubNode);
     connect(item, &CNodeItem::sigRemoveItem, this, &CMindView::slotRemoveNode);
@@ -75,6 +77,7 @@ void CMindView::addNode(CNodeData n)
         m_root->endEditing();
         m_root->addSubNode(item);
     }
+
     item->textItem()->beginEditing();
 
     refreshItems();
@@ -264,6 +267,11 @@ void CMindView::updateCursor(QPoint pos)
     }
 }
 
+void CMindView::setAllowEdit(bool allow)
+{
+    m_allowEdit = allow;
+}
+
 void CMindView::slotEditNode(CNodeData n)
 {
     emit sigEditNode(n);

+ 4 - 0
QFD/CCanvas/CMindView.h

@@ -50,6 +50,8 @@ public:
 
     void updateCursor(QPoint pos);
 
+    void setAllowEdit(bool allow);
+
 public slots:
 
     void slotEditNode(CNodeData n);
@@ -81,6 +83,8 @@ private:
 
     bool m_align        = true;
     bool m_isMovingItem = false;
+
+    bool m_allowEdit = true;
 };
 
 #endif  // CMINDVIEW_H

+ 8 - 0
QFD/CCanvas/CRectItem.cpp

@@ -69,6 +69,9 @@ void CRectItem::updatePath()
 
 void CRectItem::contextMenuEvent(QGraphicsSceneContextMenuEvent *event)
 {
+    if (!m_allowEdit) {
+        return;
+    }
     const QString txt = highlighted() ? "取消选中" : "选中";
     m_select->setText(txt);
     m_menu->exec(event->screenPos() + QPoint(-40, -20));
@@ -111,6 +114,11 @@ void CRectItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
     QGraphicsPathItem::paint(painter, option, widget);
 }
 
+void CRectItem::setAllowEdit(bool allow)
+{
+    m_allowEdit = allow;
+}
+
 void CRectItem::slotSelect()
 {
     qDebug() << __FUNCTION__ << __LINE__ << endl;

+ 4 - 0
QFD/CCanvas/CRectItem.h

@@ -42,6 +42,8 @@ public:
 
     void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget = nullptr) override;
 
+    void setAllowEdit(bool allow);
+
 private slots:
     void slotSelect();
 
@@ -55,6 +57,8 @@ private:
     QAction *m_subNode = nullptr;
     QAction *m_remove  = nullptr;
     QAction *m_edit    = nullptr;
+
+    bool m_allowEdit = true;
 };
 
 #endif  // CRECTITEM_H

+ 12 - 12
QFD/common/EvalDataManager.cpp

@@ -16,18 +16,18 @@ QList<NodeMatrixInfo> EvalDataManager::dataSample(SchemePlanManager::SchemeProce
     unsigned seed = 1;
     srand(seed);
 
-    //    for (int lev = 1; lev < mind1->levels(); lev++) {
-    //        QList<CNodeData> nodes = mind1->nodesInLevel(lev);
-    //        for (int i = 0; i < nodes.count(); i++) {
-    //            CNodeData node            = nodes[i];  // 父节点
-    //            QList<CNodeData> subNodes = mind1->subNodes(node);
-    //            for (int i = 0; i < subNodes.count(); i++) {
-    //                CNodeData subNode = subNodes[i];  // 子节点
-    //            }
-    //        }
-    //    }
-
-    //    return list;
+    for (int lev = 1; lev < mind1->levels(); lev++) {
+        QList<CNodeData> nodes = mind1->nodesInLevel(lev);
+        for (int i = 0; i < nodes.count(); i++) {
+            CNodeData node            = nodes[i];  // 父节点
+            QList<CNodeData> subNodes = mind1->subNodes(node);
+            for (int i = 0; i < subNodes.count(); i++) {
+                CNodeData subNode = subNodes[i];  // 子节点
+            }
+        }
+    }
+
+    return list;
 
     // 权重数据
     if (process.type == SchemePlanManager::ImportWeightData) {

+ 8 - 6
QFD/dbService/dbService.pri

@@ -10,6 +10,7 @@ HEADERS += \
     $$PWD/ProjectMindRelationService.h \
     $$PWD/ProjectService.h \
     $$PWD/ReturnMessage.h \   
+    $$PWD/SchemeProcessService.h \
     $$PWD/SqlDBHelper.h \
     $$PWD/UserConfigService.h \
     $$PWD/UserService.h
@@ -26,14 +27,15 @@ SOURCES += \
     $$PWD/ProjectMindRelationService.cpp \
     $$PWD/ProjectService.cpp \
     $$PWD/ReturnMessage.cpp \
+    $$PWD/SchemeProcessService.cpp \
     $$PWD/SqlDBHelper.cpp \
     $$PWD/UserConfigService.cpp \
     $$PWD/UserService.cpp
 
-contains(DEFINES, QFD2_APP) {
-HEADERS += \
-    $$PWD/SchemeProcessService.h \
+#contains(DEFINES, QFD2_APP) {
+#HEADERS += \
+#    $$PWD/SchemeProcessService.h \
 
-SOURCES += \
-    $$PWD/SchemeProcessService.cpp \
-}
+#SOURCES += \
+#    $$PWD/SchemeProcessService.cpp \
+#}

+ 1 - 1
QFD/widgets/EvalWidget.h

@@ -3,7 +3,7 @@
 
 #include <QWidget>
 
-#include "ProjectManager.h"
+#include <common/ProjectManager.h>
 
 class ProjectInfo;
 class PushButton;